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 14, 2014
1 parent 471e844 commit 18cb045
Show file tree
Hide file tree
Showing 12 changed files with 828 additions and 22 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
7 changes: 7 additions & 0 deletions include/uv-unix.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,13 @@ typedef gid_t uv_gid_t;
typedef uid_t uv_uid_t;

typedef struct dirent uv__dirent_t;
/*
* "dirent" is used to hold a buffer large enough for any dirent in the
* directory being read. Avoids allocating for each directory entry.
*/
#define UV_DIR_PRIVATE_FIELDS \
uv__dirent_t* dirent; \
DIR* dir;

#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
37 changes: 36 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,21 @@ 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
int dir_flags;
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 +1059,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 +1113,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 +1426,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
168 changes: 157 additions & 11 deletions src/unix/fs.c
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,40 @@ static ssize_t uv__fs_fdatasync(uv_fs_t* req) {
}


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

return len;
}

static ssize_t uv__fs_max_path_len_from_fd(int fd) {
ssize_t len;

assert(fd >= 0);

len = fpathconf(fd, _PC_PATH_MAX);
len = uv__fs_clamp_max_path_len(len);

return len;
}

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

assert(path);

len = pathconf(path, _PC_PATH_MAX);
len = uv__fs_clamp_max_path_len(len);

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 +373,78 @@ static ssize_t uv__fs_scandir(uv_fs_t* req) {
return n;
}

static int uv__fs_opendir(uv_fs_t* req) {
DIR *dir;
ssize_t max_path_len;
size_t len;

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

len = pathconf(req->path, _PC_PATH_MAX);
dir = opendir(req->path);
if (dir == NULL)
return -1;

if (len == -1) {
#if defined(PATH_MAX)
len = PATH_MAX;
#else
len = 4096;
#endif
/*
* Compute max path len from the file descriptor of the open directory
* instead of from the path to avoid race conditions.
*/
max_path_len = uv__fs_max_path_len_from_fd(dirfd(dir));
len = max_path_len + offsetof(struct dirent, d_name);

/*
* Allocate the space for each directory entry just once instead of
* once per directory entry.
*/
req->dir_handle->dirent = malloc(len + 1);
if (req->dir_handle->dirent == NULL) {
errno = ENOMEM;
return -1;
}

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

return 0;
}

static int uv__fs_readdir(uv_fs_t* req) {
struct dirent *de;
int r;

assert(req);
assert(req->dir_handle);
assert(req->dir_handle->dirent);
assert(req->dir_entry);

req->ptr = req->dir_entry;

de = NULL;

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

if (r == 0) {
if (de != NULL) {
req->dir_entry->name = strdup(de->d_name);
if (req->dir_entry->name != NULL) {
req->dir_entry->type = uv__fs_get_dirent_type(de);
r = 1;
} else {
errno = ENOMEM;
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 +870,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 +1143,54 @@ 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);
uv__handle_start(dirh);

dirh->dir_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);
dirh->dir = NULL;
}

if (dirh->dirent) {
free(dirh->dirent);
dirh->dirent = NULL;
}
}

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 +1310,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
Loading

0 comments on commit 18cb045

Please sign in to comment.