Skip to content

Commit

Permalink
fs: introduce uv_readdir_next() and report types
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
indutny committed Aug 14, 2014
1 parent 7bdcf3d commit ab2c442
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 86 deletions.
5 changes: 5 additions & 0 deletions include/uv-unix.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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 */

Expand Down
8 changes: 8 additions & 0 deletions include/uv-win.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
20 changes: 20 additions & 0 deletions include/uv.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down
41 changes: 12 additions & 29 deletions src/unix/fs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -296,22 +295,18 @@ static ssize_t uv__fs_read(uv_fs_t* req) {


#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;
}


/* 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;
Expand All @@ -322,32 +317,17 @@ static ssize_t uv__fs_readdir(uv_fs_t* req) {
else if (n == -1)
return n;

len = 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;
}
/* NOTE: We will use nbufs as an index field */
req->ptr = dents;
req->nbufs = 0;

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);
Expand Down Expand Up @@ -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;
Expand Down
40 changes: 40 additions & 0 deletions src/uv-common.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
2 changes: 2 additions & 0 deletions src/uv-common.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
110 changes: 61 additions & 49 deletions src/win/fs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 { \
Expand Down Expand Up @@ -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"./*";
Expand All @@ -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);
Expand All @@ -822,71 +826,79 @@ void fs__readdir(uv_fs_t* req) {
}

result = 0;
dents = NULL;
dent_size = 0;

do {
name = ent.cFileName;

if (name[0] != L'.' || (name[1] && (name[1] != L'.' || name[2]))) {
len = wcslen(name);
uv__dirent_t* dent;
int utf8_len;

if (!buf) {
buf = (WCHAR*)malloc(buf_char_len * sizeof(WCHAR));
if (!buf) {
uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
}
name = ent.cFileName;

ptr = buf;
}
if (!(name[0] != L'.' || (name[1] && (name[1] != L'.' || name[2]))))
continue;

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");
}
/* Grow dents buffer, if needed */
if (result >= dent_size) {
uv__dirent_t** tmp;

ptr = buf + (ptr - path2);
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;
}

wcscpy(ptr, name);
ptr += len + 1;
result++;
dents = tmp;
}
} 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) {
/* 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());
return;
goto fatal;
}

req->ptr = (char*)malloc(size + 1);
if (!req->ptr) {
uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
dent = malloc(sizeof(*dent) + utf8_len + 1);
if (dent == NULL) {
SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
goto fatal;
}

size = uv_utf16_to_utf8(buf, buf_char_len, (char*)req->ptr, size);
if (!size) {
free(buf);
free(req->ptr);
req->ptr = NULL;
/* 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());
return;
goto fatal;
}
free(buf);
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));

((char*)req->ptr)[size] = '\0';
FindClose(dir);

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);
}


Expand Down
Loading

0 comments on commit ab2c442

Please sign in to comment.