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>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
PR-URL: libuv#2057
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 authored and njlr committed Apr 5, 2019
1 parent 851348e commit 3f76d58
Show file tree
Hide file tree
Showing 13 changed files with 902 additions and 13 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Expand Up @@ -53,6 +53,7 @@ set(uv_test_sources
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
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
58 changes: 58 additions & 0 deletions docs/src/fs.rst
Expand Up @@ -122,6 +122,21 @@ Data types
uv_dirent_type_t type;
} uv_dirent_t;

.. c:type:: uv_dir_t
Data type used for streaming directory iteration.
Used by :c:func:`uv_fs_opendir()`, :c:func:`uv_fs_readdir()`, and
:c:func:`uv_fs_closedir()`. `dirents` represents a user provided array of
`uv_dirent_t`s used to hold results. `nentries` is the user provided maximum
array size of `dirents`.

::

typedef struct uv_dir_s {
uv_dirent_t* dirents;
size_t nentries;
} uv_dir_t;


Public members
^^^^^^^^^^^^^^
Expand Down Expand Up @@ -208,6 +223,49 @@ API
Equivalent to :man:`rmdir(2)`.
.. c:function:: int uv_fs_opendir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb)
Opens `path` as a directory stream. On success, a `uv_dir_t` is allocated
and returned via `req->ptr`. This memory is not freed by
`uv_fs_req_cleanup()`, although `req->ptr` is set to `NULL`. The allocated
memory must be freed by calling `uv_fs_closedir()`. On failure, no memory
is allocated.
The contents of the directory can be iterated over by passing the resulting
`uv_dir_t` to `uv_fs_readdir()`.
.. versionadded:: 1.28.0
.. c:function:: int uv_fs_closedir(uv_loop_t* loop, uv_fs_t* req, uv_dir_t* dir, uv_fs_cb cb)
Closes the directory stream represented by `dir` and frees the memory
allocated by `uv_fs_opendir()`.
.. versionadded:: 1.28.0
.. c:function:: int uv_fs_readdir(uv_loop_t* loop, uv_fs_t* req, uv_dir_t* dir, uv_fs_cb cb)
Iterates over the directory stream, `dir`, returned by a successful
`uv_fs_opendir()` call. Prior to invoking `uv_fs_readdir()`, the caller
must set `dir->dirents` and `dir->nentries`, representing the array of
:c:type:`uv_dirent_t` elements used to hold the read directory entries and
its size.
On success, the result is an integer >= 0 representing the number of entries
read from the stream.
.. versionadded:: 1.28.0
.. warning::
`uv_fs_readdir()` is not thread safe.
.. note::
This function does not return the "." and ".." entries.
.. note::
On success this function allocates memory that must be freed using
`uv_fs_req_cleanup()`.
.. c:function:: int uv_fs_scandir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, uv_fs_cb cb)
.. c:function:: int uv_fs_scandir_next(uv_fs_t* req, uv_dirent_t* ent)
Expand Down
25 changes: 24 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,19 @@ 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_dirent_t* dirents;
size_t nentries;
void* reserved[4];
UV_DIR_PRIVATE_FIELDS
};

/* uv_fs_t is a subclass of uv_req_t. */
struct uv_fs_s {
UV_REQ_FIELDS
Expand Down Expand Up @@ -1291,6 +1302,18 @@ 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_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
3 changes: 3 additions & 0 deletions include/uv/unix.h
Expand Up @@ -166,6 +166,9 @@ typedef uid_t uv_uid_t;

typedef struct dirent uv__dirent_t;

#define UV_DIR_PRIVATE_FIELDS \
DIR* dir;

#if defined(DT_UNKNOWN)
# define HAVE_DIRENT_TYPES
# if defined(DT_REG)
Expand Down
5 changes: 5 additions & 0 deletions include/uv/win.h
Expand Up @@ -301,6 +301,11 @@ typedef struct uv__dirent_s {
char d_name[1];
} uv__dirent_t;

#define UV_DIR_PRIVATE_FIELDS \
HANDLE dir_handle; \
WIN32_FIND_DATAW find_data; \
BOOL need_find_call;

#define HAVE_DIRENT_TYPES
#define UV__DT_DIR UV_DIRENT_DIR
#define UV__DT_FILE UV_DIRENT_FILE
Expand Down
125 changes: 123 additions & 2 deletions 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,87 @@ static ssize_t uv__fs_scandir(uv_fs_t* req) {
return n;
}

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

dir = uv__malloc(sizeof(*dir));
if (dir == NULL)
goto error;

dir->dir = opendir(req->path);
if (dir->dir == NULL)
goto error;

req->ptr = dir;
return 0;

error:
uv__free(dir);
req->ptr = NULL;
return -1;
}

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

dir = req->ptr;
dirent_idx = 0;

while (dirent_idx < dir->nentries) {
/* 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(dir->dir);

if (res == NULL) {
if (errno != 0)
goto error;
break;
}

if (strcmp(res->d_name, ".") == 0 || strcmp(res->d_name, "..") == 0)
continue;

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

if (dirent->name == NULL)
goto error;

dirent->type = uv__fs_get_dirent_type(res);
++dirent_idx;
}

return dirent_idx;

error:
for (i = 0; i < dirent_idx; ++i) {
uv__free((char*) dir->dirents[i].name);
dir->dirents[i].name = NULL;
}

return -1;
}

static int uv__fs_closedir(uv_fs_t* req) {
uv_dir_t* dir;

dir = req->ptr;

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

uv__free(req->ptr);
req->ptr = 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 +1347,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 +1620,40 @@ 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_fs_cb cb) {
INIT(READDIR);

if (dir == NULL || dir->dir == NULL || dir->dirents == NULL)
return UV_EINVAL;

req->ptr = dir;
POST;
}

int uv_fs_closedir(uv_loop_t* loop,
uv_fs_t* req,
uv_dir_t* dir,
uv_fs_cb cb) {
INIT(CLOSEDIR);

if (dir == NULL)
return UV_EINVAL;

req->ptr = dir;
POST;
}

int uv_fs_readlink(uv_loop_t* loop,
uv_fs_t* req,
Expand Down Expand Up @@ -1676,14 +1794,17 @@ 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->fs_type == UV_FS_SCANDIR && req->ptr != NULL)
uv__fs_scandir_cleanup(req);

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

if (req->ptr != &req->statbuf)
if (req->fs_type != UV_FS_OPENDIR && req->ptr != &req->statbuf)
uv__free(req->ptr);
req->ptr = NULL;
}
Expand Down

0 comments on commit 3f76d58

Please sign in to comment.