Skip to content

Commit

Permalink
unix,win: add uv_fs_{open,read,close}dir()
Browse files Browse the repository at this point in the history
Co-authored-by: Julien Gilli <jgilli@nodejs.org>
Co-authored-by: Jeremy Whitlock <jwhitlock@apache.org>
Refs: joyent/libuv#1430
Refs: joyent/libuv#1521
Refs: joyent/libuv#1574
Refs: libuv#175
Refs: nodejs/node#583
Refs: libuv#416
Refs: libuv#170
  • Loading branch information
3 people committed Mar 26, 2019
1 parent 575d414 commit d9ec47a
Show file tree
Hide file tree
Showing 10 changed files with 923 additions and 12 deletions.
1 change: 1 addition & 0 deletions Makefile.am
Expand Up @@ -191,6 +191,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-fork.c \
test/test-getters-setters.c \
test/test-get-currentexe.c \
Expand Down
27 changes: 26 additions & 1 deletion include/uv.h
Expand Up @@ -202,6 +202,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 @@ -1196,9 +1197,16 @@ typedef enum {
UV_FS_FCHOWN,
UV_FS_REALPATH,
UV_FS_COPYFILE,
UV_FS_LCHOWN
UV_FS_LCHOWN,
UV_FS_OPENDIR,
UV_FS_READDIR,
UV_FS_CLOSEDIR
} uv_fs_type;

struct uv_dir_s {
UV_DIR_PRIVATE_FIELDS
};

/* uv_fs_t is a subclass of uv_req_t. */
struct uv_fs_s {
UV_REQ_FIELDS
Expand All @@ -1209,6 +1217,9 @@ 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; /* Stores the result of uv_fs_opendir() */
uv_dirent_t* dirents;
size_t nentries;
UV_FS_PRIVATE_FIELDS
};

Expand Down Expand Up @@ -1291,6 +1302,20 @@ 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);
UV_EXTERN int uv_fs_opendir(uv_loop_t* loop,
uv_fs_t* req,
const char* path,
uv_fs_cb cb);
UV_EXTERN int uv_fs_readdir(uv_loop_t* loop,
uv_fs_t* req,
uv_dir_t* dir,
uv_dirent_t dirents[],
size_t ndirents,
uv_fs_cb cb);
UV_EXTERN int uv_fs_closedir(uv_loop_t* loop,
uv_fs_t* req,
uv_dir_t* dir,
uv_fs_cb cb);
UV_EXTERN int uv_fs_stat(uv_loop_t* loop,
uv_fs_t* req,
const char* path,
Expand Down
7 changes: 7 additions & 0 deletions include/uv/unix.h
Expand Up @@ -165,6 +165,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
23 changes: 23 additions & 0 deletions include/uv/win.h
Expand Up @@ -301,6 +301,29 @@ typedef struct uv__dirent_s {
char d_name[1];
} uv__dirent_t;

/*
* "handle" is the actual directory handle that is used to perform calls
* to FindFirstFile/FindNextFile.
*
* "find_data" is needed to store the result of the FindFirstFile call that
* happened in uv_fs_opendir so that it can be used in the subsequent
* uv_fs_readdir call.
*
* "need_find_call" is a boolean determining if the next call to uv_fs_readdir
* must call FindNextFile. In uv_fs_opendir, FindFirstFile reads the first
* entry of the directory, and thus the subsequent call to uv_fs_readdir must
* not call FindNextFile, or otherwise it would miss the first directory entry.
* The next uv_fs_readdir calls after the first one will use FindNextFile.
*
* "dirent" is used to hold a buffer large enough for any dirent in the
* directory being read. It avoids allocating for each directory entry.
*/
#define UV_DIR_PRIVATE_FIELDS \
HANDLE dir_handle; \
WIN32_FIND_DATAW find_data; \
BOOL need_find_call; \
uv__dirent_t* dirent;

#define HAVE_DIRENT_TYPES
#define UV__DT_DIR UV_DIRENT_DIR
#define UV__DT_FILE UV_DIRENT_FILE
Expand Down
105 changes: 104 additions & 1 deletion src/unix/fs.c
Expand Up @@ -351,7 +351,7 @@ static int uv__fs_scandir_sort(UV_CONST_DIRENT** a, UV_CONST_DIRENT** b) {


static ssize_t uv__fs_scandir(uv_fs_t* req) {
uv__dirent_t **dents;
uv__dirent_t** dents;
int n;

dents = NULL;
Expand All @@ -375,6 +375,72 @@ static ssize_t uv__fs_scandir(uv_fs_t* req) {
return n;
}

static int uv__fs_opendir(uv_fs_t* req) {
req->dir = uv__malloc(sizeof(uv_dir_t));

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

req->dir->dir = opendir(req->path);

if (req->dir->dir == NULL) {
uv__free(req->dir);
req->dir = NULL;
return -1;
}

return 0;
}

static int uv__fs_readdir(uv_fs_t* req) {
uv_dirent_t* dirent;
struct dirent* res;
unsigned int dirent_idx;

assert(req->dir != NULL);
assert(req->dir->dir != NULL);
assert(req->dirents != NULL);

for (dirent_idx = 0; dirent_idx < req->nentries; ++dirent_idx) {
/* readdir() returns NULL on end of directory, as well as on error. errno
is used to differentiate between the two conditions. */
errno = 0;
res = readdir(req->dir->dir);

if (errno != 0) {
return -1;
}

if (res == NULL) {
break;
}

dirent = &req->dirents[dirent_idx];
dirent->name = uv__strdup(res->d_name);

if (dirent->name == NULL) {
return -1;
}

dirent->type = uv__fs_get_dirent_type(res);
}

return dirent_idx;
}

static int uv__fs_closedir(uv_fs_t* req) {
assert(req->dir != NULL);

if (req->dir->dir != NULL) {
closedir(req->dir->dir);
req->dir->dir = NULL;
}

uv__free(req->dir);
req->dir = NULL;
return 0;
}

#if defined(_POSIX_PATH_MAX)
# define UV__FS_PATH_MAX _POSIX_PATH_MAX
#elif defined(PATH_MAX)
Expand Down Expand Up @@ -1266,6 +1332,9 @@ static void uv__fs_work(struct uv__work* w) {
X(OPEN, uv__fs_open(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(CLOSEDIR, uv__fs_closedir(req));
X(READLINK, uv__fs_readlink(req));
X(REALPATH, uv__fs_realpath(req));
X(RENAME, rename(req->path, req->new_path));
Expand Down Expand Up @@ -1536,6 +1605,36 @@ int uv_fs_scandir(uv_loop_t* loop,
POST;
}

int uv_fs_opendir(uv_loop_t* loop,
uv_fs_t* req,
const char* path,
uv_fs_cb cb) {
INIT(OPENDIR);
PATH;
POST;
}

int uv_fs_readdir(uv_loop_t* loop,
uv_fs_t* req,
uv_dir_t* dir,
uv_dirent_t dirents[],
size_t ndirents,
uv_fs_cb cb) {
INIT(READDIR);
req->dir = dir;
req->dirents = dirents;
req->nentries = ndirents;
POST;
}

int uv_fs_closedir(uv_loop_t* loop,
uv_fs_t* req,
uv_dir_t* dir,
uv_fs_cb cb) {
INIT(CLOSEDIR);
req->dir = dir;
POST;
}

int uv_fs_readlink(uv_loop_t* loop,
uv_fs_t* req,
Expand Down Expand Up @@ -1679,12 +1778,16 @@ 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->fs_type == UV_FS_READDIR && req->ptr != NULL)
uv__fs_readdir_cleanup(req);

if (req->bufs != req->bufsml)
uv__free(req->bufs);
req->bufs = NULL;

if (req->ptr != &req->statbuf)
uv__free(req->ptr);

req->ptr = NULL;
}

Expand Down
42 changes: 32 additions & 10 deletions src/uv-common.c
Expand Up @@ -631,37 +631,59 @@ int uv_fs_scandir_next(uv_fs_t* req, uv_dirent_t* ent) {
dent = dents[(*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) {
uv_dirent_t* dirents;
unsigned int dirent_idx;

if (req != NULL && req->dirents != NULL) {
dirents = req->dirents;
for (dirent_idx = 0; dirent_idx < req->nentries; ++dirent_idx) {
if (dirents[dirent_idx].name != NULL)
free((char*)dirents[dirent_idx].name);
dirents[dirent_idx].name = NULL;
}
}
}


Expand Down
2 changes: 2 additions & 0 deletions src/uv-common.h
Expand Up @@ -193,6 +193,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);

int uv__next_timeout(const uv_loop_t* loop);
void uv__run_timers(uv_loop_t* loop);
Expand Down

0 comments on commit d9ec47a

Please sign in to comment.