Skip to content
This repository has been archived by the owner on May 4, 2018. It is now read-only.

Commit

Permalink
fs: uv_fs_{open,read}_dir for unix
Browse files Browse the repository at this point in the history
Tested on Linux, MacOS X and SmartOS.

Fixes #1430
  • Loading branch information
Julien Gilli committed Oct 9, 2014
1 parent 471e844 commit c4e22d6
Show file tree
Hide file tree
Showing 12 changed files with 704 additions and 21 deletions.
1 change: 1 addition & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \
test/test-fs-event.c \
test/test-fs-poll.c \
test/test-fs.c \
test/test-fs-readdir.c \
test/test-get-currentexe.c \
test/test-get-loadavg.c \
test/test-get-memory.c \
Expand Down
3 changes: 3 additions & 0 deletions include/uv-unix.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ typedef gid_t uv_gid_t;
typedef uid_t uv_uid_t;

typedef struct dirent uv__dirent_t;
#define UV_DIR_PRIVATE_FIELDS \
DIR* dir; \
ssize_t max_path_len;

#if defined(DT_UNKNOWN)
# define HAVE_DIRENT_TYPES
Expand Down
2 changes: 2 additions & 0 deletions include/uv-win.h
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,8 @@ typedef struct uv__dirent_s {
#define UV__DT_CHAR UV_DIRENT_CHAR
#define UV__DT_BLOCK UV_DIRENT_BLOCK

#define UV_DIR_PRIVATE_FIELDS

/* Platform-specific definitions for uv_dlopen support. */
#define UV_DYNAMIC FAR WINAPI
typedef struct {
Expand Down
36 changes: 35 additions & 1 deletion include/uv.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ extern "C" {
XX(TTY, tty) \
XX(UDP, udp) \
XX(SIGNAL, signal) \
XX(DIR, dir) \

#define UV_REQ_TYPE_MAP(XX) \
XX(REQ, req) \
Expand Down Expand Up @@ -197,6 +198,7 @@ typedef enum {
/* Handle types. */
typedef struct uv_loop_s uv_loop_t;
typedef struct uv_handle_s uv_handle_t;
typedef struct uv_dir_s uv_dir_t;
typedef struct uv_stream_s uv_stream_t;
typedef struct uv_tcp_s uv_tcp_t;
typedef struct uv_udp_s uv_udp_t;
Expand Down Expand Up @@ -1032,13 +1034,20 @@ typedef enum {
UV_FS_MKDTEMP,
UV_FS_RENAME,
UV_FS_SCANDIR,
UV_FS_OPENDIR,
UV_FS_READDIR,
UV_FS_LINK,
UV_FS_SYMLINK,
UV_FS_READLINK,
UV_FS_CHOWN,
UV_FS_FCHOWN
} uv_fs_type;

struct uv_dir_s {
UV_HANDLE_FIELDS
UV_DIR_PRIVATE_FIELDS
};

/* uv_fs_t is a subclass of uv_req_t. */
struct uv_fs_s {
UV_REQ_FIELDS
Expand All @@ -1049,6 +1058,8 @@ struct uv_fs_s {
void* ptr;
const char* path;
uv_stat_t statbuf; /* Stores the result of uv_fs_stat() and uv_fs_fstat(). */
uv_dir_t* dir_handle; /* Stores the result of uv_fs_opendir() */
uv_dirent_t* dir_entry; /* Stores the result of uv_fs_readdir() */
UV_FS_PRIVATE_FIELDS
};

Expand Down Expand Up @@ -1101,6 +1112,30 @@ UV_EXTERN int uv_fs_scandir(uv_loop_t* loop,
uv_fs_cb cb);
UV_EXTERN int uv_fs_scandir_next(uv_fs_t* req,
uv_dirent_t* ent);

/*
* In the future, this flag could be used to specify a custom filter to be
* applied each time a directory entry is read. It removes the need for the
* user to iterate through all directory entries again to filter out the ones
* that they are interested in.
*/
#define UV_DIR_FLAGS_NONE 0

/* uv_fs_{open,readdir} are not yet implemented on Windows */
#ifndef _WIN32
UV_EXTERN int uv_fs_opendir(uv_loop_t* loop,
uv_fs_t* req,
uv_dir_t* dirh,
const char* path,
int flags,
uv_fs_cb cb);
UV_EXTERN int uv_fs_readdir(uv_loop_t* loop,
uv_fs_t* req,
uv_dir_t* dirh,
uv_dirent_t* dirent,
uv_fs_cb cb);
#endif /* _WIN32 */

UV_EXTERN int uv_fs_stat(uv_loop_t* loop,
uv_fs_t* req,
const char* path,
Expand Down Expand Up @@ -1390,7 +1425,6 @@ struct uv_loop_s {
UV_LOOP_PRIVATE_FIELDS
};


/* Don't export the private CPP symbols. */
#undef UV_HANDLE_TYPE_PRIVATE
#undef UV_REQ_TYPE_PRIVATE
Expand Down
5 changes: 5 additions & 0 deletions src/unix/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,10 @@ void uv_close(uv_handle_t* handle, uv_close_cb close_cb) {
/* itself close uv__make_close_pending whenever appropriate. */
return;

case UV_DIR:
uv__dir_close((uv_dir_t*)handle);
break;

default:
assert(0);
}
Expand Down Expand Up @@ -221,6 +225,7 @@ static void uv__finish_close(uv_handle_t* handle) {
case UV_FS_POLL:
case UV_POLL:
case UV_SIGNAL:
case UV_DIR:
break;

case UV_NAMED_PIPE:
Expand Down
133 changes: 123 additions & 10 deletions src/unix/fs.c
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,24 @@ static ssize_t uv__fs_fdatasync(uv_fs_t* req) {
}


static ssize_t uv__fs_max_path_len(const char* path) {
ssize_t len;

assert(path);

len = pathconf(path, _PC_PATH_MAX);

if (len == -1) {
#if defined(PATH_MAX)
len = PATH_MAX;
#else
len = 4096;
#endif
}

return len;
}

static ssize_t uv__fs_futime(uv_fs_t* req) {
#if defined(__linux__)
/* utimesat() has nanosecond resolution but we stick to microseconds
Expand Down Expand Up @@ -339,21 +357,69 @@ static ssize_t uv__fs_scandir(uv_fs_t* req) {
return n;
}

static int uv__fs_opendir(uv_fs_t* req) {
DIR* dir;

static ssize_t uv__fs_readlink(uv_fs_t* req) {
assert(req && req->dir_handle);

dir = opendir(req->path);
if (dir == NULL)
return -1;

req->dir_handle->max_path_len = uv__fs_max_path_len(req->path);

req->dir_handle->dir = dir;
req->ptr = req->dir_handle;

return 0;
}

static int uv__fs_readdir(uv_fs_t* req) {
ssize_t max_path_len;
ssize_t len;
char* buf;
struct dirent *buf, *de;
int r;

len = pathconf(req->path, _PC_PATH_MAX);
assert(req);
assert(req->dir_handle);
assert(req->dir_entry);

if (len == -1) {
#if defined(PATH_MAX)
len = PATH_MAX;
#else
len = 4096;
#endif
req->ptr = req->dir_entry;

buf = de = NULL;

max_path_len = req->dir_handle->max_path_len;
len = max_path_len + offsetof(struct dirent, d_name);

buf = malloc(len + 1);

if (buf == NULL) {
errno = ENOMEM;
return -1;
}

r = readdir_r(req->dir_handle->dir, buf, &de);

if (r == 0) {
if (de != NULL) {
req->dir_entry->name = strdup(de->d_name);
req->dir_entry->type = uv__fs_get_dirent_type(de);
free(de);
de = NULL;
r = 1;
} else {
r = UV_EOF;
}
}

return r;
}

static ssize_t uv__fs_readlink(uv_fs_t* req) {
ssize_t len;
char* buf;

len = uv__fs_max_path_len(req->path);
buf = malloc(len + 1);

if (buf == NULL) {
Expand Down Expand Up @@ -779,6 +845,8 @@ static void uv__fs_work(struct uv__work* w) {
X(MKDTEMP, uv__fs_mkdtemp(req));
X(READ, uv__fs_read(req));
X(SCANDIR, uv__fs_scandir(req));
X(OPENDIR, uv__fs_opendir(req));
X(READDIR, uv__fs_readdir(req));
X(READLINK, uv__fs_readlink(req));
X(RENAME, rename(req->path, req->new_path));
X(RMDIR, rmdir(req->path));
Expand Down Expand Up @@ -1050,6 +1118,46 @@ int uv_fs_scandir(uv_loop_t* loop,
POST;
}

int uv_fs_opendir(uv_loop_t* loop,
uv_fs_t* req,
uv_dir_t* dirh,
const char* path,
int flags,
uv_fs_cb cb) {

INIT(OPENDIR);
PATH;

uv__handle_init(loop, dirh, UV_DIR);

req->flags = flags;

dirh->dir = NULL;
req->dir_handle = dirh;

POST;
}

void uv__dir_close(uv_dir_t* dirh) {
if (!uv__is_active(dirh))
return;

if (dirh->dir)
closedir(dirh->dir);
}

int uv_fs_readdir(uv_loop_t* loop,
uv_fs_t* req,
uv_dir_t* dirh,
uv_dirent_t* dirent,
uv_fs_cb cb) {
INIT(READDIR);

req->dir_handle = dirh;
req->dir_entry = dirent;

POST;
}

int uv_fs_readlink(uv_loop_t* loop,
uv_fs_t* req,
Expand Down Expand Up @@ -1169,7 +1277,12 @@ void uv_fs_req_cleanup(uv_fs_t* req) {
if (req->fs_type == UV_FS_SCANDIR && req->ptr != NULL)
uv__fs_scandir_cleanup(req);

if (req->ptr != &req->statbuf)
if (req->fs_type == UV_FS_READDIR)
uv__fs_readdir_cleanup(req);

if (req->fs_type != UV_FS_READDIR &&
req->fs_type != UV_FS_OPENDIR &&
req->ptr != &req->statbuf)
free(req->ptr);
req->ptr = NULL;
}
1 change: 1 addition & 0 deletions src/unix/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ void uv__tcp_close(uv_tcp_t* handle);
void uv__timer_close(uv_timer_t* handle);
void uv__udp_close(uv_udp_t* handle);
void uv__udp_finish_close(uv_udp_t* handle);
void uv__dir_close(uv_dir_t* handle);
uv_handle_type uv__handle_type(int fd);

#if defined(__APPLE__)
Expand Down
37 changes: 27 additions & 10 deletions src/uv-common.c
Original file line number Diff line number Diff line change
Expand Up @@ -468,35 +468,52 @@ int uv_fs_scandir_next(uv_fs_t* req, uv_dirent_t* ent) {
dent = dents[req->nbufs++];

ent->name = dent->d_name;
ent->type = uv__fs_get_dirent_type(dent);

return 0;
}

uv_dirent_type_t uv__fs_get_dirent_type(uv__dirent_t* dent) {
uv_dirent_type_t type;

#ifdef HAVE_DIRENT_TYPES
switch (dent->d_type) {
case UV__DT_DIR:
ent->type = UV_DIRENT_DIR;
type = UV_DIRENT_DIR;
break;
case UV__DT_FILE:
ent->type = UV_DIRENT_FILE;
type = UV_DIRENT_FILE;
break;
case UV__DT_LINK:
ent->type = UV_DIRENT_LINK;
type = UV_DIRENT_LINK;
break;
case UV__DT_FIFO:
ent->type = UV_DIRENT_FIFO;
type = UV_DIRENT_FIFO;
break;
case UV__DT_SOCKET:
ent->type = UV_DIRENT_SOCKET;
type = UV_DIRENT_SOCKET;
break;
case UV__DT_CHAR:
ent->type = UV_DIRENT_CHAR;
type = UV_DIRENT_CHAR;
break;
case UV__DT_BLOCK:
ent->type = UV_DIRENT_BLOCK;
type = UV_DIRENT_BLOCK;
break;
default:
ent->type = UV_DIRENT_UNKNOWN;
type = UV_DIRENT_UNKNOWN;
}
#else
ent->type = UV_DIRENT_UNKNOWN;
type = UV_DIRENT_UNKNOWN;
#endif

return 0;
return type;
}

void uv__fs_readdir_cleanup(uv_fs_t* req) {
if (req && req->ptr) {
uv_dirent_t* dirent = req->ptr;
if (dirent->name)
free((char*)dirent->name);
dirent->name = NULL;
}
}
2 changes: 2 additions & 0 deletions src/uv-common.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,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_scandir_cleanup(uv_fs_t* req);
void uv__fs_readdir_cleanup(uv_fs_t* req);
uv_dirent_type_t uv__fs_get_dirent_type(uv__dirent_t* dent);

#define uv__has_active_reqs(loop) \
(QUEUE_EMPTY(&(loop)->active_reqs) == 0)
Expand Down
Loading

0 comments on commit c4e22d6

Please sign in to comment.