fs: introduce uv_readdir_next() and report types

Introduce:

    int uv_fs_readdir_next(uv_fs_t* req, uv_dirent_t* ent);

`uv_fs_readdir()` is not returning a file names list in `req->ptr`
anymore, the proper way to gather them is to call `uv_fs_readdir_next()`
in a callback.
This commit is contained in:
Fedor Indutny 2014-08-13 17:48:08 +04:00
parent 7bdcf3dc7e
commit ab2c4425a5
8 changed files with 173 additions and 91 deletions

View File

@ -25,6 +25,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <sys/socket.h>
#include <netinet/in.h>
@ -155,6 +156,10 @@ typedef pthread_barrier_t uv_barrier_t;
typedef gid_t uv_gid_t;
typedef uid_t uv_uid_t;
typedef struct dirent uv__dirent_t;
#define UV__DT_DIR DT_DIR
/* Platform-specific definitions for uv_dlopen support. */
#define UV_DYNAMIC /* empty */

View File

@ -289,6 +289,14 @@ typedef struct uv_once_s {
typedef unsigned char uv_uid_t;
typedef unsigned char uv_gid_t;
typedef struct uv__dirent_s {
int d_type;
char d_name[1];
} uv__dirent_t;
#define UV__DT_DIR UV_DIRENT_DIR
#define UV__DT_FILE UV_DIRENT_FILE
/* Platform-specific definitions for uv_dlopen support. */
#define UV_DYNAMIC FAR WINAPI
typedef struct {

View File

@ -227,6 +227,7 @@ typedef struct uv_work_s uv_work_t;
/* None of the above. */
typedef struct uv_cpu_info_s uv_cpu_info_t;
typedef struct uv_interface_address_s uv_interface_address_t;
typedef struct uv_dirent_s uv_dirent_t;
typedef enum {
@ -1786,6 +1787,16 @@ struct uv_interface_address_s {
} netmask;
};
typedef enum {
UV_DIRENT_FILE,
UV_DIRENT_DIR
} uv_dirent_type_t;
struct uv_dirent_s {
const char* name;
uv_dirent_type_t type;
};
UV_EXTERN char** uv_setup_args(int argc, char** argv);
UV_EXTERN int uv_get_process_title(char* buffer, size_t size);
UV_EXTERN int uv_set_process_title(const char* title);
@ -1931,6 +1942,15 @@ UV_EXTERN int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path,
UV_EXTERN int uv_fs_readdir(uv_loop_t* loop, uv_fs_t* req,
const char* path, int flags, uv_fs_cb cb);
/*
* Call this after `uv_fs_readdir` cb's invocation, this function should be
* called until it returns `UV_EOF`.
*
* The data that is put into `ent` is managed by libuv and should not be
* deallocated by the user.
*/
UV_EXTERN int uv_fs_readdir_next(uv_fs_t* req, uv_dirent_t* ent);
UV_EXTERN int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path,
uv_fs_cb cb);

View File

@ -38,7 +38,6 @@
#include <sys/stat.h>
#include <sys/time.h>
#include <pthread.h>
#include <dirent.h>
#include <unistd.h>
#include <fcntl.h>
#include <utime.h>
@ -296,9 +295,9 @@ done:
#if defined(__OpenBSD__) || (defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_8))
static int uv__fs_readdir_filter(struct dirent* dent) {
static int uv__fs_readdir_filter(uv__dirent_t* dent) {
#else
static int uv__fs_readdir_filter(const struct dirent* dent) {
static int uv__fs_readdir_filter(const uv__dirent_t* dent) {
#endif
return strcmp(dent->d_name, ".") != 0 && strcmp(dent->d_name, "..") != 0;
}
@ -306,12 +305,8 @@ static int uv__fs_readdir_filter(const struct dirent* dent) {
/* This should have been called uv__fs_scandir(). */
static ssize_t uv__fs_readdir(uv_fs_t* req) {
struct dirent **dents;
uv__dirent_t **dents;
int saved_errno;
size_t off;
size_t len;
char *buf;
int i;
int n;
dents = NULL;
@ -322,32 +317,17 @@ static ssize_t uv__fs_readdir(uv_fs_t* req) {
else if (n == -1)
return n;
len = 0;
/* NOTE: We will use nbufs as an index field */
req->ptr = dents;
req->nbufs = 0;
for (i = 0; i < n; i++)
len += strlen(dents[i]->d_name) + 1;
buf = malloc(len);
if (buf == NULL) {
errno = ENOMEM;
n = -1;
goto out;
}
off = 0;
for (i = 0; i < n; i++) {
len = strlen(dents[i]->d_name) + 1;
memcpy(buf + off, dents[i]->d_name, len);
off += len;
}
req->ptr = buf;
return n;
out:
saved_errno = errno;
if (dents != NULL) {
int i;
for (i = 0; i < n; i++)
free(dents[i]);
free(dents);
@ -1184,6 +1164,9 @@ void uv_fs_req_cleanup(uv_fs_t* req) {
req->path = NULL;
req->new_path = NULL;
if (req->fs_type == UV_FS_READDIR && req->ptr != NULL)
uv__fs_readdir_cleanup(req);
if (req->ptr != &req->statbuf)
free(req->ptr);
req->ptr = NULL;

View File

@ -449,3 +449,43 @@ int uv_fs_event_getpath(uv_fs_event_t* handle, char* buf, size_t* len) {
return 0;
}
void uv__fs_readdir_cleanup(uv_fs_t* req) {
uv__dirent_t** dents;
dents = req->ptr;
if (req->nbufs > 0 && req->nbufs != (unsigned int) req->result)
req->nbufs--;
for (; req->nbufs < (unsigned int) req->result; req->nbufs++)
free(dents[req->nbufs]);
}
int uv_fs_readdir_next(uv_fs_t* req, uv_dirent_t* ent) {
uv__dirent_t** dents;
uv__dirent_t* dent;
dents = req->ptr;
/* Free previous entity */
if (req->nbufs > 0)
free(dents[req->nbufs - 1]);
/* End was already reached */
if (req->nbufs == (unsigned int) req->result) {
free(dents);
req->ptr = NULL;
return UV_EOF;
}
dent = dents[req->nbufs++];
ent->name = dent->d_name;
if (dent->d_type == UV__DT_DIR)
ent->type = UV_DIRENT_DIR;
else
ent->type = UV_DIRENT_FILE;
return 0;
}

View File

@ -109,6 +109,8 @@ size_t uv__count_bufs(const uv_buf_t bufs[], unsigned int nbufs);
int uv__socket_sockopt(uv_handle_t* handle, int optname, int* value);
void uv__fs_readdir_cleanup(uv_fs_t* req);
#define uv__has_active_reqs(loop) \
(QUEUE_EMPTY(&(loop)->active_reqs) == 0)

View File

@ -43,6 +43,8 @@
#define UV_FS_FREE_PTR 0x0008
#define UV_FS_CLEANEDUP 0x0010
static const int uv__fs_dirent_slide = 0x20;
#define QUEUE_FS_TP_JOB(loop, req) \
do { \
@ -784,13 +786,14 @@ void fs__mkdtemp(uv_fs_t* req) {
void fs__readdir(uv_fs_t* req) {
WCHAR* pathw = req->pathw;
size_t len = wcslen(pathw);
int result, size;
WCHAR* buf = NULL, *ptr, *name;
int result;
WCHAR* name;
HANDLE dir;
WIN32_FIND_DATAW ent = { 0 };
size_t buf_char_len = 4096;
WCHAR* path2;
const WCHAR* fmt;
uv__dirent_t** dents;
int dent_size;
if (len == 0) {
fmt = L"./*";
@ -809,7 +812,8 @@ void fs__readdir(uv_fs_t* req) {
path2 = (WCHAR*)malloc(sizeof(WCHAR) * (len + 4));
if (!path2) {
uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
return;
}
_snwprintf(path2, len + 3, fmt, pathw);
@ -822,71 +826,79 @@ void fs__readdir(uv_fs_t* req) {
}
result = 0;
dents = NULL;
dent_size = 0;
do {
uv__dirent_t* dent;
int utf8_len;
name = ent.cFileName;
if (name[0] != L'.' || (name[1] && (name[1] != L'.' || name[2]))) {
len = wcslen(name);
if (!(name[0] != L'.' || (name[1] && (name[1] != L'.' || name[2]))))
continue;
if (!buf) {
buf = (WCHAR*)malloc(buf_char_len * sizeof(WCHAR));
if (!buf) {
uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
}
/* Grow dents buffer, if needed */
if (result >= dent_size) {
uv__dirent_t** tmp;
ptr = buf;
dent_size += uv__fs_dirent_slide;
tmp = realloc(dents, dent_size * sizeof(*dents));
if (tmp == NULL) {
SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
goto fatal;
}
while ((ptr - buf) + len + 1 > buf_char_len) {
buf_char_len *= 2;
path2 = buf;
buf = (WCHAR*)realloc(buf, buf_char_len * sizeof(WCHAR));
if (!buf) {
uv_fatal_error(ERROR_OUTOFMEMORY, "realloc");
}
ptr = buf + (ptr - path2);
}
wcscpy(ptr, name);
ptr += len + 1;
result++;
dents = tmp;
}
/* Allocate enough space to fit utf8 encoding of file name */
len = wcslen(name);
utf8_len = uv_utf16_to_utf8(name, len, NULL, 0);
if (!utf8_len) {
SET_REQ_WIN32_ERROR(req, GetLastError());
goto fatal;
}
dent = malloc(sizeof(*dent) + utf8_len + 1);
if (dent == NULL) {
SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
goto fatal;
}
/* Copy file name */
utf8_len = uv_utf16_to_utf8(name, len, dent->d_name, utf8_len);
if (!utf8_len) {
free(dent);
SET_REQ_WIN32_ERROR(req, GetLastError());
goto fatal;
}
dent->d_name[utf8_len] = '\0';
/* Copy file type */
if ((ent.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
dent->d_type = UV__DT_DIR;
else
dent->d_type = UV__DT_FILE;
dents[result++] = dent;
} while(FindNextFileW(dir, &ent));
FindClose(dir);
if (buf) {
/* Convert result to UTF8. */
size = uv_utf16_to_utf8(buf, buf_char_len, NULL, 0);
if (!size) {
SET_REQ_WIN32_ERROR(req, GetLastError());
return;
}
req->ptr = (char*)malloc(size + 1);
if (!req->ptr) {
uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
}
size = uv_utf16_to_utf8(buf, buf_char_len, (char*)req->ptr, size);
if (!size) {
free(buf);
free(req->ptr);
req->ptr = NULL;
SET_REQ_WIN32_ERROR(req, GetLastError());
return;
}
free(buf);
((char*)req->ptr)[size] = '\0';
if (dents != NULL)
req->flags |= UV_FS_FREE_PTR;
} else {
req->ptr = NULL;
}
/* NOTE: nbufs will be used as index */
req->nbufs = 0;
req->ptr = dents;
SET_REQ_RESULT(req, result);
return;
fatal:
/* Deallocate dents */
for (result--; result >= 0; result--)
free(dents[result]);
free(dents);
}

View File

@ -417,12 +417,16 @@ static void rmdir_cb(uv_fs_t* req) {
static void readdir_cb(uv_fs_t* req) {
uv_dirent_t dent;
ASSERT(req == &readdir_req);
ASSERT(req->fs_type == UV_FS_READDIR);
ASSERT(req->result == 2);
ASSERT(req->ptr);
ASSERT(memcmp(req->ptr, "file1\0file2\0", 12) == 0
|| memcmp(req->ptr, "file2\0file1\0", 12) == 0);
while (UV_EOF != uv_fs_readdir_next(req, &dent)) {
ASSERT(strcmp(dent.name, "file1") == 0 || strcmp(dent.name, "file2") == 0);
ASSERT(dent.type == UV_DIRENT_FILE);
}
readdir_cb_count++;
ASSERT(req->path);
ASSERT(memcmp(req->path, "test_dir\0", 9) == 0);
@ -802,6 +806,7 @@ TEST_IMPL(fs_file_write_null_buffer) {
TEST_IMPL(fs_async_dir) {
int r;
uv_dirent_t dent;
/* Setup */
unlink("test_dir/file1");
@ -844,8 +849,10 @@ TEST_IMPL(fs_async_dir) {
ASSERT(r == 2);
ASSERT(readdir_req.result == 2);
ASSERT(readdir_req.ptr);
ASSERT(memcmp(readdir_req.ptr, "file1\0file2\0", 12) == 0
|| memcmp(readdir_req.ptr, "file2\0file1\0", 12) == 0);
while (UV_EOF != uv_fs_readdir_next(&readdir_req, &dent)) {
ASSERT(strcmp(dent.name, "file1") == 0 || strcmp(dent.name, "file2") == 0);
ASSERT(dent.type == UV_DIRENT_FILE);
}
uv_fs_req_cleanup(&readdir_req);
ASSERT(!readdir_req.ptr);
@ -1521,6 +1528,7 @@ TEST_IMPL(fs_symlink_dir) {
uv_fs_t req;
int r;
char* test_dir;
uv_dirent_t dent;
/* set-up */
unlink("test_dir/file1");
@ -1597,8 +1605,10 @@ TEST_IMPL(fs_symlink_dir) {
ASSERT(r == 2);
ASSERT(readdir_req.result == 2);
ASSERT(readdir_req.ptr);
ASSERT(memcmp(readdir_req.ptr, "file1\0file2\0", 12) == 0
|| memcmp(readdir_req.ptr, "file2\0file1\0", 12) == 0);
while (UV_EOF != uv_fs_readdir_next(&readdir_req, &dent)) {
ASSERT(strcmp(dent.name, "file1") == 0 || strcmp(dent.name, "file2") == 0);
ASSERT(dent.type == UV_DIRENT_FILE);
}
uv_fs_req_cleanup(&readdir_req);
ASSERT(!readdir_req.ptr);
@ -1615,8 +1625,10 @@ TEST_IMPL(fs_symlink_dir) {
ASSERT(r == 2);
ASSERT(readdir_req.result == 2);
ASSERT(readdir_req.ptr);
ASSERT(memcmp(readdir_req.ptr, "file1\0file2\0", 12) == 0
|| memcmp(readdir_req.ptr, "file2\0file1\0", 12) == 0);
while (UV_EOF != uv_fs_readdir_next(&readdir_req, &dent)) {
ASSERT(strcmp(dent.name, "file1") == 0 || strcmp(dent.name, "file2") == 0);
ASSERT(dent.type == UV_DIRENT_FILE);
}
uv_fs_req_cleanup(&readdir_req);
ASSERT(!readdir_req.ptr);