diff --git a/include/uv-private/uv-unix.h b/include/uv-private/uv-unix.h index 729082e0..61f5d18b 100644 --- a/include/uv-private/uv-unix.h +++ b/include/uv-private/uv-unix.h @@ -121,7 +121,6 @@ typedef struct { typedef int uv_file; typedef int uv_os_sock_t; -typedef struct stat uv_statbuf_t; #define UV_ONCE_INIT PTHREAD_ONCE_INIT diff --git a/include/uv-private/uv-win.h b/include/uv-private/uv-win.h index c9ec38ef..b1c4179c 100644 --- a/include/uv-private/uv-win.h +++ b/include/uv-private/uv-win.h @@ -207,8 +207,6 @@ typedef struct uv_buf_t { typedef int uv_file; -typedef struct _stati64 uv_statbuf_t; - typedef SOCKET uv_os_sock_t; typedef HANDLE uv_thread_t; diff --git a/include/uv.h b/include/uv.h index 6b87b52a..3986ec85 100644 --- a/include/uv.h +++ b/include/uv.h @@ -350,6 +350,29 @@ typedef void (*uv_getaddrinfo_cb)(uv_getaddrinfo_t* req, int status, struct addrinfo* res); +typedef struct { + long tv_sec; + long tv_nsec; +} uv_timespec_t; + + +typedef struct { + uint64_t st_dev; + uint64_t st_mode; + uint64_t st_nlink; + uint64_t st_uid; + uint64_t st_gid; + uint64_t st_rdev; + uint64_t st_ino; + uint64_t st_size; + uint64_t st_blksize; + uint64_t st_blocks; + uv_timespec_t st_atim; + uv_timespec_t st_mtim; + uv_timespec_t st_ctim; +} uv_stat_t; + + /* * This will be called repeatedly after the uv_fs_event_t is initialized. * If uv_fs_event_t was initialized with a directory the filename parameter @@ -361,8 +384,8 @@ typedef void (*uv_fs_event_cb)(uv_fs_event_t* handle, const char* filename, typedef void (*uv_fs_poll_cb)(uv_fs_poll_t* handle, int status, - const uv_statbuf_t* prev, - const uv_statbuf_t* curr); + const uv_stat_t* prev, + const uv_stat_t* curr); typedef void (*uv_signal_cb)(uv_signal_t* handle, int signum); @@ -1519,7 +1542,7 @@ struct uv_fs_s { void* ptr; const char* path; uv_err_code errorno; - uv_statbuf_t statbuf; /* Stores the result of uv_fs_stat and uv_fs_fstat. */ + uv_stat_t statbuf; /* Stores the result of uv_fs_stat and uv_fs_fstat. */ UV_FS_PRIVATE_FIELDS }; @@ -1646,7 +1669,7 @@ UV_EXTERN int uv_fs_poll_init(uv_loop_t* loop, uv_fs_poll_t* handle); * or the error reason changes). * * When `status == 0`, your callback receives pointers to the old and new - * `uv_statbuf_t` structs. They are valid for the duration of the callback + * `uv_stat_t` structs. They are valid for the duration of the callback * only! * * For maximum portability, use multi-second intervals. Sub-second intervals diff --git a/src/fs-poll.c b/src/fs-poll.c index ad27f184..9b2d03ee 100644 --- a/src/fs-poll.c +++ b/src/fs-poll.c @@ -35,16 +35,16 @@ struct poll_ctx { uv_fs_poll_cb poll_cb; uv_timer_t timer_handle; uv_fs_t fs_req; /* TODO(bnoordhuis) mark fs_req internal */ - uv_statbuf_t statbuf; + uv_stat_t statbuf; char path[1]; /* variable length */ }; -static int statbuf_eq(const uv_statbuf_t* a, const uv_statbuf_t* b); +static int statbuf_eq(const uv_stat_t* a, const uv_stat_t* b); static void poll_cb(uv_fs_t* req); static void timer_cb(uv_timer_t* timer, int status); static void timer_close_cb(uv_handle_t* handle); -static uv_statbuf_t zero_statbuf; +static uv_stat_t zero_statbuf; int uv_fs_poll_init(uv_loop_t* loop, uv_fs_poll_t* handle) { @@ -137,7 +137,7 @@ static void timer_cb(uv_timer_t* timer, int status) { static void poll_cb(uv_fs_t* req) { - uv_statbuf_t* statbuf; + uv_stat_t* statbuf; struct poll_ctx* ctx; uint64_t interval; @@ -189,48 +189,17 @@ static void timer_close_cb(uv_handle_t* handle) { } -static int statbuf_eq(const uv_statbuf_t* a, const uv_statbuf_t* b) { -#if defined(_WIN32) - return a->st_mtime == b->st_mtime - && a->st_size == b->st_size - && a->st_mode == b->st_mode; -#else - - /* Jump through a few hoops to get sub-second granularity on Linux. */ -# if defined(__linux__) -# if defined(__USE_MISC) /* _BSD_SOURCE || _SVID_SOURCE */ - if (a->st_ctim.tv_nsec != b->st_ctim.tv_nsec) return 0; - if (a->st_mtim.tv_nsec != b->st_mtim.tv_nsec) return 0; -# else - if (a->st_ctimensec != b->st_ctimensec) return 0; - if (a->st_mtimensec != b->st_mtimensec) return 0; -# endif -# endif - - /* Jump through different hoops on OS X. */ -# if defined(__APPLE__) -# if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE) - if (a->st_ctimespec.tv_nsec != b->st_ctimespec.tv_nsec) return 0; - if (a->st_mtimespec.tv_nsec != b->st_mtimespec.tv_nsec) return 0; -# else - if (a->st_ctimensec != b->st_ctimensec) return 0; - if (a->st_mtimensec != b->st_mtimensec) return 0; -# endif -# endif - - /* TODO(bnoordhuis) Other Unices have st_ctim and friends too, provided - * the stars and compiler flags are right... - */ - - return a->st_ctime == b->st_ctime - && a->st_mtime == b->st_mtime +static int statbuf_eq(const uv_stat_t* a, const uv_stat_t* b) { + return a->st_ctim.tv_nsec == b->st_ctim.tv_nsec + && a->st_mtim.tv_nsec == b->st_mtim.tv_nsec + && a->st_ctim.tv_sec == b->st_ctim.tv_sec + && a->st_mtim.tv_sec == b->st_mtim.tv_sec && a->st_size == b->st_size && a->st_mode == b->st_mode && a->st_uid == b->st_uid && a->st_gid == b->st_gid && a->st_ino == b->st_ino && a->st_dev == b->st_dev; -#endif } diff --git a/src/unix/fs.c b/src/unix/fs.c index 53f46ce7..fb8cdb25 100644 --- a/src/unix/fs.c +++ b/src/unix/fs.c @@ -497,6 +497,69 @@ static ssize_t uv__fs_write(uv_fs_t* req) { return r; } +static inline void uv__to_stat(struct stat* src, uv_stat_t* dst) { + dst->st_dev = src->st_dev; + dst->st_mode = src->st_mode; + dst->st_nlink = src->st_nlink; + dst->st_uid = src->st_uid; + dst->st_gid = src->st_gid; + dst->st_rdev = src->st_rdev; + dst->st_ino = src->st_ino; + dst->st_size = src->st_size; + dst->st_blksize = src->st_blksize; + dst->st_blocks = src->st_blocks; + +#if defined(__APPLE__) + dst->st_atim.tv_sec = src->st_atimespec.tv_sec; + dst->st_atim.tv_nsec = src->st_atimespec.tv_nsec; + dst->st_mtim.tv_sec = src->st_mtimespec.tv_sec; + dst->st_mtim.tv_nsec = src->st_mtimespec.tv_nsec; + dst->st_ctim.tv_sec = src->st_ctimespec.tv_sec; + dst->st_ctim.tv_nsec = src->st_ctimespec.tv_nsec; +#elif defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_XOPEN_SOURCE) + dst->st_atim.tv_sec = src->st_atim.tv_sec; + dst->st_atim.tv_nsec = src->st_atim.tv_nsec; + dst->st_mtim.tv_sec = src->st_mtim.tv_sec; + dst->st_mtim.tv_nsec = src->st_mtim.tv_nsec; + dst->st_ctim.tv_sec = src->st_ctim.tv_sec; + dst->st_ctim.tv_nsec = src->st_ctim.tv_nsec; +#else + dst->st_atim.tv_sec = src->st_atime; + dst->st_atim.tv_nsec = 0; + dst->st_mtim.tv_sec = src->st_mtime; + dst->st_mtim.tv_nsec = 0; + dst->st_ctim.tv_sec = src->st_ctime; + dst->st_ctim.tv_nsec = 0; +#endif +} + + +static int uv__fs_stat(const char *path, uv_stat_t *buf) { + struct stat pbuf; + int ret; + ret = stat(path, &pbuf); + uv__to_stat(&pbuf, buf); + return ret; +} + + +static int uv__fs_lstat(const char *path, uv_stat_t *buf) { + struct stat pbuf; + int ret; + ret = lstat(path, &pbuf); + uv__to_stat(&pbuf, buf); + return ret; +} + + +static int uv__fs_fstat(int fd, uv_stat_t *buf) { + struct stat pbuf; + int ret; + ret = fstat(fd, &pbuf); + uv__to_stat(&pbuf, buf); + return ret; +} + static void uv__fs_work(struct uv__work* w) { int retry_on_eintr; @@ -521,11 +584,11 @@ static void uv__fs_work(struct uv__work* w) { X(FCHMOD, fchmod(req->file, req->mode)); X(FCHOWN, fchown(req->file, req->uid, req->gid)); X(FDATASYNC, uv__fs_fdatasync(req)); - X(FSTAT, fstat(req->file, &req->statbuf)); + X(FSTAT, uv__fs_fstat(req->file, &req->statbuf)); X(FSYNC, fsync(req->file)); X(FTRUNCATE, ftruncate(req->file, req->off)); X(FUTIME, uv__fs_futime(req)); - X(LSTAT, lstat(req->path, &req->statbuf)); + X(LSTAT, uv__fs_lstat(req->path, &req->statbuf)); X(LINK, link(req->path, req->new_path)); X(MKDIR, mkdir(req->path, req->mode)); X(OPEN, open(req->path, req->flags, req->mode)); @@ -535,7 +598,7 @@ static void uv__fs_work(struct uv__work* w) { X(RENAME, rename(req->path, req->new_path)); X(RMDIR, rmdir(req->path)); X(SENDFILE, uv__fs_sendfile(req)); - X(STAT, stat(req->path, &req->statbuf)); + X(STAT, uv__fs_stat(req->path, &req->statbuf)); X(SYMLINK, symlink(req->path, req->new_path)); X(UNLINK, unlink(req->path)); X(UTIME, uv__fs_utime(req)); diff --git a/src/win/fs.c b/src/win/fs.c index 60e67a41..c32b84bf 100644 --- a/src/win/fs.c +++ b/src/win/fs.c @@ -77,8 +77,20 @@ return; \ } +#define FILETIME_TO_UINT(filetime) \ + (*((uint64_t*) &(filetime)) - 116444736000000000ULL) + #define FILETIME_TO_TIME_T(filetime) \ - ((*((uint64_t*) &(filetime)) - 116444736000000000ULL) / 10000000ULL); + (FILETIME_TO_UINT(filetime) / 10000000ULL); + +#define FILETIME_TO_TIME_NS(filetime, secs) \ + ((FILETIME_TO_UINT(filetime) - (secs * 10000000ULL)) * 100); + +#define FILETIME_TO_TIMESPEC(ts, filetime) \ + do { \ + (ts).tv_sec = FILETIME_TO_TIME_T(filetime); \ + (ts).tv_nsec = FILETIME_TO_TIME_NS(filetime, (ts).tv_sec); \ + } while(0) #define TIME_T_TO_FILETIME(time, filetime_ptr) \ do { \ @@ -86,7 +98,6 @@ 116444736000000000ULL; \ } while(0) - #define IS_SLASH(c) ((c) == L'\\' || (c) == L'/') #define IS_LETTER(c) (((c) >= L'a' && (c) <= L'z') || \ ((c) >= L'A' && (c) <= L'Z')) @@ -808,7 +819,7 @@ void fs__readdir(uv_fs_t* req) { } -INLINE static int fs__stat_handle(HANDLE handle, uv_statbuf_t* statbuf) { +INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf) { BY_HANDLE_FILE_INFORMATION info; if (!GetFileInformationByHandle(handle, &info)) { @@ -825,6 +836,9 @@ INLINE static int fs__stat_handle(HANDLE handle, uv_statbuf_t* statbuf) { statbuf->st_mode = 0; + statbuf->st_blksize = 0; + statbuf->st_blocks = 0; + if (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { if (fs__readlink_handle(handle, NULL, &statbuf->st_size) != 0) { return -1; @@ -846,9 +860,9 @@ INLINE static int fs__stat_handle(HANDLE handle, uv_statbuf_t* statbuf) { ((_S_IREAD|_S_IWRITE) >> 6)); } - statbuf->st_mtime = FILETIME_TO_TIME_T(info.ftLastWriteTime); - statbuf->st_atime = FILETIME_TO_TIME_T(info.ftLastAccessTime); - statbuf->st_ctime = FILETIME_TO_TIME_T(info.ftCreationTime); + FILETIME_TO_TIMESPEC(statbuf->st_mtim, info.ftLastWriteTime); + FILETIME_TO_TIMESPEC(statbuf->st_atim, info.ftLastAccessTime); + FILETIME_TO_TIMESPEC(statbuf->st_ctim, info.ftCreationTime); statbuf->st_nlink = (info.nNumberOfLinks <= SHRT_MAX) ? (short) info.nNumberOfLinks : SHRT_MAX; diff --git a/test/test-fs-poll.c b/test/test-fs-poll.c index f312d87f..87884181 100644 --- a/test/test-fs-poll.c +++ b/test/test-fs-poll.c @@ -30,8 +30,8 @@ static void timer_cb(uv_timer_t* handle, int status); static void close_cb(uv_handle_t* handle); static void poll_cb(uv_fs_poll_t* handle, int status, - const uv_statbuf_t* prev, - const uv_statbuf_t* curr); + const uv_stat_t* prev, + const uv_stat_t* curr); static uv_fs_poll_t poll_handle; static uv_timer_t timer_handle; @@ -74,9 +74,9 @@ static void timer_cb(uv_timer_t* handle, int status) { static void poll_cb(uv_fs_poll_t* handle, int status, - const uv_statbuf_t* prev, - const uv_statbuf_t* curr) { - uv_statbuf_t zero_statbuf; + const uv_stat_t* prev, + const uv_stat_t* curr) { + uv_stat_t zero_statbuf; memset(&zero_statbuf, 0, sizeof(zero_statbuf)); diff --git a/test/test-fs.c b/test/test-fs.c index 0016b3be..6459d125 100644 --- a/test/test-fs.c +++ b/test/test-fs.c @@ -107,7 +107,7 @@ static char test_buf[] = "test-buffer\n"; static void check_permission(const char* filename, int mode) { int r; uv_fs_t req; - uv_statbuf_t* s; + uv_stat_t* s; r = uv_fs_stat(uv_default_loop(), &req, filename, NULL); ASSERT(r == 0); @@ -213,7 +213,7 @@ static void unlink_cb(uv_fs_t* req) { } static void fstat_cb(uv_fs_t* req) { - struct stat* s = req->ptr; + uv_stat_t* s = req->ptr; ASSERT(req->fs_type == UV_FS_FSTAT); ASSERT(req->result == 0); ASSERT(s->st_size == sizeof(test_buf)); @@ -543,7 +543,7 @@ TEST_IMPL(fs_file_loop) { } static void check_utime(const char* path, double atime, double mtime) { - uv_statbuf_t* s; + uv_stat_t* s; uv_fs_t req; int r; @@ -553,16 +553,8 @@ static void check_utime(const char* path, double atime, double mtime) { ASSERT(req.result == 0); s = &req.statbuf; -#if defined(_WIN32) || defined(_AIX) - ASSERT(s->st_atime == atime); - ASSERT(s->st_mtime == mtime); -#elif !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE) - ASSERT(s->st_atimespec.tv_sec == atime); - ASSERT(s->st_mtimespec.tv_sec == mtime); -#else ASSERT(s->st_atim.tv_sec == atime); ASSERT(s->st_mtim.tv_sec == mtime); -#endif uv_fs_req_cleanup(&req); } @@ -906,7 +898,10 @@ TEST_IMPL(fs_fstat) { int r; uv_fs_t req; uv_file file; - struct stat* s; + uv_stat_t* s; +#ifndef _WIN32 + struct stat t; +#endif /* Setup. */ unlink("test_file"); @@ -930,6 +925,45 @@ TEST_IMPL(fs_fstat) { ASSERT(req.result == 0); s = req.ptr; ASSERT(s->st_size == sizeof(test_buf)); + +#ifndef _WIN32 + r = fstat(file, &t); + ASSERT(r == 0); + + ASSERT(s->st_dev == t.st_dev); + ASSERT(s->st_mode == t.st_mode); + ASSERT(s->st_nlink == t.st_nlink); + ASSERT(s->st_uid == t.st_uid); + ASSERT(s->st_gid == t.st_gid); + ASSERT(s->st_rdev == t.st_rdev); + ASSERT(s->st_ino == t.st_ino); + ASSERT(s->st_size == t.st_size); + ASSERT(s->st_blksize == t.st_blksize); + ASSERT(s->st_blocks == t.st_blocks); +#if defined(__APPLE__) + ASSERT(s->st_atim.tv_sec == t.st_atimespec.tv_sec); + ASSERT(s->st_atim.tv_nsec == t.st_atimespec.tv_nsec); + ASSERT(s->st_mtim.tv_sec == t.st_mtimespec.tv_sec); + ASSERT(s->st_mtim.tv_nsec == t.st_mtimespec.tv_nsec); + ASSERT(s->st_ctim.tv_sec == t.st_ctimespec.tv_sec); + ASSERT(s->st_ctim.tv_nsec == t.st_ctimespec.tv_nsec); +#elif defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_XOPEN_SOURCE) + ASSERT(s->st_atim.tv_sec == t.st_atim.tv_sec); + ASSERT(s->st_atim.tv_nsec == t.st_atim.tv_nsec); + ASSERT(s->st_mtim.tv_sec == t.st_mtim.tv_sec); + ASSERT(s->st_mtim.tv_nsec == t.st_mtim.tv_nsec); + ASSERT(s->st_ctim.tv_sec == t.st_ctim.tv_sec); + ASSERT(s->st_ctim.tv_nsec == t.st_ctim.tv_nsec); +#else + ASSERT(s->st_atim.tv_sec == t.st_atime); + ASSERT(s->st_atim.tv_nsec == 0); + ASSERT(s->st_mtim.tv_sec == t.st_mtime); + ASSERT(s->st_mtim.tv_nsec == 0); + ASSERT(s->st_ctim.tv_sec == t.st_ctime); + ASSERT(s->st_ctim.tv_nsec == 0); +#endif +#endif + uv_fs_req_cleanup(&req); /* Now do the uv_fs_fstat call asynchronously */ @@ -1382,16 +1416,16 @@ TEST_IMPL(fs_symlink_dir) { r = uv_fs_stat(loop, &req, "test_dir_symlink", NULL); ASSERT(r == 0); - ASSERT(((struct stat*)req.ptr)->st_mode & S_IFDIR); + ASSERT(((uv_stat_t*)req.ptr)->st_mode & S_IFDIR); uv_fs_req_cleanup(&req); r = uv_fs_lstat(loop, &req, "test_dir_symlink", NULL); ASSERT(r == 0); - ASSERT(((struct stat*)req.ptr)->st_mode & S_IFLNK); + ASSERT(((uv_stat_t*)req.ptr)->st_mode & S_IFLNK); #ifdef _WIN32 - ASSERT(((struct stat*)req.ptr)->st_size == strlen(test_dir + 4)); + ASSERT(((uv_stat_t*)req.ptr)->st_size == strlen(test_dir + 4)); #else - ASSERT(((struct stat*)req.ptr)->st_size == strlen(test_dir)); + ASSERT(((uv_stat_t*)req.ptr)->st_size == strlen(test_dir)); #endif uv_fs_req_cleanup(&req);