unix,win: add uv_fs_statfs()

Fixes: https://github.com/libuv/libuv/issues/2386
PR-URL: https://github.com/libuv/libuv/pull/2396
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Saúl Ibarra Corretgé <saghul@gmail.com>
Reviewed-By: Santiago Gimeno <santiago.gimeno@gmail.com>
Reviewed-By: Bartosz Sosnowski <bartosz@janeasystems.com>
This commit is contained in:
cjihrig 2019-07-29 07:48:05 -10:00
parent 79c2d74058
commit bf86d5fbaf
No known key found for this signature in database
GPG Key ID: 7434390BDBE9B9C5
6 changed files with 216 additions and 1 deletions

View File

@ -102,6 +102,24 @@ Data types
UV_FS_CLOSEDIR
} uv_fs_type;
.. c:type:: uv_statfs_t
Reduced cross platform equivalent of ``struct statfs``.
Used in :c:func:`uv_fs_statfs`.
::
typedef struct uv_statfs_s {
uint64_t f_type;
uint64_t f_bsize;
uint64_t f_blocks;
uint64_t f_bfree;
uint64_t f_bavail;
uint64_t f_files;
uint64_t f_ffree;
uint64_t f_spare[4];
} uv_statfs_t;
.. c:type:: uv_dirent_t
Cross platform (reduced) equivalent of ``struct dirent``.
@ -290,6 +308,17 @@ API
Equivalent to :man:`stat(2)`, :man:`fstat(2)` and :man:`lstat(2)` respectively.
.. c:function:: int uv_fs_statfs(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb)
Equivalent to :man:`statfs(2)`. On success, a `uv_statfs_t` is allocated
and returned via `req->ptr`. This memory is freed by `uv_fs_req_cleanup()`.
.. note::
Any fields in the resulting `uv_statfs_t` that are not supported by the
underlying operating system are set to zero.
.. versionadded:: 1.31.0
.. c:function:: int uv_fs_rename(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, uv_fs_cb cb)
Equivalent to :man:`rename(2)`.

View File

@ -236,6 +236,7 @@ typedef struct uv_interface_address_s uv_interface_address_t;
typedef struct uv_dirent_s uv_dirent_t;
typedef struct uv_passwd_s uv_passwd_t;
typedef struct uv_utsname_s uv_utsname_t;
typedef struct uv_statfs_s uv_statfs_t;
typedef enum {
UV_LOOP_BLOCK_SIGNAL
@ -1070,6 +1071,17 @@ struct uv_utsname_s {
to as meaningless in the docs. */
};
struct uv_statfs_s {
uint64_t f_type;
uint64_t f_bsize;
uint64_t f_blocks;
uint64_t f_bfree;
uint64_t f_bavail;
uint64_t f_files;
uint64_t f_ffree;
uint64_t f_spare[4];
};
typedef enum {
UV_DIRENT_UNKNOWN,
UV_DIRENT_FILE,
@ -1205,7 +1217,8 @@ typedef enum {
UV_FS_LCHOWN,
UV_FS_OPENDIR,
UV_FS_READDIR,
UV_FS_CLOSEDIR
UV_FS_CLOSEDIR,
UV_FS_STATFS
} uv_fs_type;
struct uv_dir_s {
@ -1433,6 +1446,10 @@ UV_EXTERN int uv_fs_lchown(uv_loop_t* loop,
uv_uid_t uid,
uv_gid_t gid,
uv_fs_cb cb);
UV_EXTERN int uv_fs_statfs(uv_loop_t* loop,
uv_fs_t* req,
const char* path,
uv_fs_cb cb);
enum uv_fs_event {

View File

@ -70,6 +70,20 @@
# include <utime.h>
#endif
#if defined(__APPLE__) || \
defined(__DragonFly__) || \
defined(__FreeBSD__) || \
defined(__FreeBSD_kernel__) || \
defined(__OpenBSD__) || \
defined(__NetBSD__)
# include <sys/param.h>
# include <sys/mount.h>
#elif defined(__sun) || defined(__MVS__)
# include <sys/statvfs.h>
#else
# include <sys/statfs.h>
#endif
#if defined(_AIX) && _XOPEN_SOURCE <= 600
extern char *mkdtemp(char *template); /* See issue #740 on AIX < 7 */
#endif
@ -519,6 +533,40 @@ static int uv__fs_closedir(uv_fs_t* req) {
return 0;
}
static int uv__fs_statfs(uv_fs_t* req) {
uv_statfs_t* stat_fs;
#if defined(__sun) || defined(__MVS__)
struct statvfs buf;
if (0 != statvfs(req->path, &buf))
#else
struct statfs buf;
if (0 != statfs(req->path, &buf))
#endif /* defined(__sun) */
return -1;
stat_fs = uv__malloc(sizeof(*stat_fs));
if (stat_fs == NULL) {
errno = ENOMEM;
return -1;
}
#if defined(__sun) || defined(__MVS__)
stat_fs->f_type = 0; /* f_type is not supported. */
#else
stat_fs->f_type = buf.f_type;
#endif
stat_fs->f_bsize = buf.f_bsize;
stat_fs->f_blocks = buf.f_blocks;
stat_fs->f_bfree = buf.f_bfree;
stat_fs->f_bavail = buf.f_bavail;
stat_fs->f_files = buf.f_files;
stat_fs->f_ffree = buf.f_ffree;
req->ptr = stat_fs;
return 0;
}
static ssize_t uv__fs_pathmax_size(const char* path) {
ssize_t pathmax;
@ -1386,6 +1434,7 @@ static void uv__fs_work(struct uv__work* w) {
X(RMDIR, rmdir(req->path));
X(SENDFILE, uv__fs_sendfile(req));
X(STAT, uv__fs_stat(req->path, &req->statbuf));
X(STATFS, uv__fs_statfs(req));
X(SYMLINK, symlink(req->path, req->new_path));
X(UNLINK, unlink(req->path));
X(UTIME, uv__fs_utime(req));
@ -1858,3 +1907,13 @@ int uv_fs_copyfile(uv_loop_t* loop,
req->flags = flags;
POST;
}
int uv_fs_statfs(uv_loop_t* loop,
uv_fs_t* req,
const char* path,
uv_fs_cb cb) {
INIT(STATFS);
PATH;
POST;
}

View File

@ -2548,6 +2548,41 @@ static void fs__lchown(uv_fs_t* req) {
req->result = 0;
}
static void fs__statfs(uv_fs_t* req) {
uv_statfs_t* stat_fs;
DWORD sectors_per_cluster;
DWORD bytes_per_sector;
DWORD free_clusters;
DWORD total_clusters;
if (0 == GetDiskFreeSpaceW(req->file.pathw,
&sectors_per_cluster,
&bytes_per_sector,
&free_clusters,
&total_clusters)) {
SET_REQ_WIN32_ERROR(req, GetLastError());
return;
}
stat_fs = uv__malloc(sizeof(*stat_fs));
if (stat_fs == NULL) {
SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
return;
}
stat_fs->f_type = 0;
stat_fs->f_bsize = bytes_per_sector * sectors_per_cluster;
stat_fs->f_blocks = total_clusters;
stat_fs->f_bfree = free_clusters;
stat_fs->f_bavail = free_clusters;
stat_fs->f_files = 0;
stat_fs->f_ffree = 0;
req->ptr = stat_fs;
SET_REQ_RESULT(req, 0);
}
static void uv__fs_work(struct uv__work* w) {
uv_fs_t* req;
@ -2589,6 +2624,7 @@ static void uv__fs_work(struct uv__work* w) {
XX(CHOWN, chown)
XX(FCHOWN, fchown)
XX(LCHOWN, lchown)
XX(STATFS, statfs)
default:
assert(!"bad uv_fs_type");
}
@ -3100,3 +3136,18 @@ int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file fd, double atime,
req->fs.time.mtime = mtime;
POST;
}
int uv_fs_statfs(uv_loop_t* loop,
uv_fs_t* req,
const char* path,
uv_fs_cb cb) {
int err;
INIT(UV_FS_STATFS);
err = fs__capture_path(req, path, NULL, cb != NULL);
if (err)
return uv_translate_sys_error(err);
POST;
}

View File

@ -94,6 +94,7 @@ static int readlink_cb_count;
static int realpath_cb_count;
static int utime_cb_count;
static int futime_cb_count;
static int statfs_cb_count;
static uv_loop_t* loop;
@ -330,6 +331,38 @@ static void fstat_cb(uv_fs_t* req) {
}
static void statfs_cb(uv_fs_t* req) {
uv_statfs_t* stats;
ASSERT(req->fs_type == UV_FS_STATFS);
ASSERT(req->result == 0);
ASSERT(req->ptr != NULL);
stats = req->ptr;
#if defined(_WIN32) || defined(__sun) || defined(_AIX) || defined(__MVS__)
ASSERT(stats->f_type == 0);
#else
ASSERT(stats->f_type > 0);
#endif
ASSERT(stats->f_bsize > 0);
ASSERT(stats->f_blocks > 0);
ASSERT(stats->f_bfree <= stats->f_blocks);
ASSERT(stats->f_bavail <= stats->f_bfree);
#ifdef _WIN32
ASSERT(stats->f_files == 0);
ASSERT(stats->f_ffree == 0);
#else
ASSERT(stats->f_files > 0);
ASSERT(stats->f_ffree <= stats->f_files);
#endif
uv_fs_req_cleanup(req);
ASSERT(req->ptr == NULL);
statfs_cb_count++;
}
static void close_cb(uv_fs_t* req) {
int r;
ASSERT(req == &close_req);
@ -3817,6 +3850,9 @@ TEST_IMPL(fs_null_req) {
r = uv_fs_futime(NULL, NULL, 0, 0.0, 0.0, NULL);
ASSERT(r == UV_EINVAL);
r = uv_fs_statfs(NULL, NULL, NULL, NULL);
ASSERT(r == UV_EINVAL);
/* This should be a no-op. */
uv_fs_req_cleanup(NULL);
@ -4073,3 +4109,24 @@ TEST_IMPL(fs_invalid_mkdir_name) {
return 0;
}
#endif
TEST_IMPL(fs_statfs) {
uv_fs_t req;
int r;
loop = uv_default_loop();
/* Test the synchronous version. */
r = uv_fs_statfs(NULL, &req, ".", NULL);
ASSERT(r == 0);
statfs_cb(&req);
ASSERT(statfs_cb_count == 1);
/* Test the asynchronous version. */
r = uv_fs_statfs(loop, &req, ".", statfs_cb);
ASSERT(r == 0);
uv_run(loop, UV_RUN_DEFAULT);
ASSERT(statfs_cb_count == 2);
return 0;
}

View File

@ -329,6 +329,7 @@ TEST_DECLARE (fs_fd_hash)
TEST_DECLARE (fs_utime)
TEST_DECLARE (fs_futime)
TEST_DECLARE (fs_file_open_append)
TEST_DECLARE (fs_statfs)
TEST_DECLARE (fs_stat_missing_path)
TEST_DECLARE (fs_read_bufs)
TEST_DECLARE (fs_read_file_eof)
@ -924,6 +925,7 @@ TASK_LIST_START
#if defined(_WIN32) && !defined(USING_UV_SHARED)
TEST_ENTRY (fs_fd_hash)
#endif
TEST_ENTRY (fs_statfs)
TEST_ENTRY (fs_stat_missing_path)
TEST_ENTRY (fs_read_bufs)
TEST_ENTRY (fs_read_file_eof)