diff --git a/src/unix/fs.c b/src/unix/fs.c index 3ec6bf8956c..a001f41562e 100644 --- a/src/unix/fs.c +++ b/src/unix/fs.c @@ -701,8 +701,24 @@ static void uv__to_stat(struct stat* src, uv_stat_t* dst) { dst->st_mtim.tv_nsec = src->st_mtime_nsec; dst->st_ctim.tv_sec = src->st_ctime; dst->st_ctim.tv_nsec = src->st_ctime_nsec; - dst->st_birthtim.tv_sec = src->st_ctime; - dst->st_birthtim.tv_nsec = src->st_ctime_nsec; + + /* On systems that do not support birthtime, resolve the value to the least + * time available (oldest of atime, mtime, ctime), disregarding nanoseconds. + */ + if (src->st_atime < src->st_mtime && src->st_atime < src->st_ctime) { + /* Use last accessed time. */ + dst->st_birthtim.tv_sec = src->st_atime; + dst->st_birthtim.tv_nsec = src->st_atime_nsec; + } else if (src->st_mtime < src->st_ctime) { + /* Use last modified time. */ + dst->st_birthtim.tv_sec = src->st_mtime; + dst->st_birthtim.tv_nsec = src->st_mtime_nsec; + } else { + /* Use last change time. */ + dst->st_birthtim.tv_sec = src->st_ctime; + dst->st_birthtim.tv_nsec = src->st_ctime_nsec; + } + dst->st_flags = 0; dst->st_gen = 0; #elif !defined(_AIX) && ( \ @@ -725,8 +741,23 @@ static void uv__to_stat(struct stat* src, uv_stat_t* dst) { dst->st_flags = src->st_flags; dst->st_gen = src->st_gen; # else - dst->st_birthtim.tv_sec = src->st_ctim.tv_sec; - dst->st_birthtim.tv_nsec = src->st_ctim.tv_nsec; + /* On systems that do not support birthtime, resolve the value to the least + * time available (oldest of atime, mtime, ctime), disregarding nanoseconds. + */ + if (src->st_atime < src->st_mtime && src->st_atime < src->st_ctime) { + /* Use last accessed time. */ + dst->st_birthtim.tv_sec = src->st_atim.tv_sec; + dst->st_birthtim.tv_nsec = src->st_atim.tv_nsec; + } else if (src->st_mtime < src->st_ctime) { + /* Use last modified time. */ + dst->st_birthtim.tv_sec = src->st_mtim.tv_sec; + dst->st_birthtim.tv_nsec = src->st_mtim.tv_nsec; + } else { + /* Use last change time. */ + dst->st_birthtim.tv_sec = src->st_ctim.tv_sec; + dst->st_birthtim.tv_nsec = src->st_ctim.tv_nsec; + } + dst->st_flags = 0; dst->st_gen = 0; # endif diff --git a/test/test-fs.c b/test/test-fs.c index a96f4325fd7..531194fb0f7 100644 --- a/test/test-fs.c +++ b/test/test-fs.c @@ -2535,3 +2535,63 @@ TEST_IMPL(fs_read_write_null_arguments) { return 0; } + + +TEST_IMPL(fs_stat_small_birthtime) { + int r; + long birthtime; + uv_buf_t* iovs; + + unlink("test_file"); + + loop = uv_default_loop(); + + /* First open the file, then write and read, in that order. uv_fs_stat used + * to give preference to ctime, which would in this case give the smaller + * time. In this case, atime will be the returned birthtime, for systems that + * do not support birthtime. + */ + r = uv_fs_open(loop, + &open_req1, + "test_file", + O_RDWR | O_CREAT, + S_IWUSR | S_IRUSR, + NULL); + ASSERT(r >= 0); + uv_fs_req_cleanup(&open_req1); + + iovs = malloc(sizeof(*iovs) * 1); + iovs[0] = uv_buf_init(test_buf, sizeof(test_buf)); + + r = uv_fs_write(loop, &write_req, open_req1.result, iovs, 1, 0, NULL); + ASSERT(r >= 0); + uv_fs_req_cleanup(&write_req); + + r = uv_fs_read(loop, &read_req, open_req1.result, iovs, 1, 0, NULL); + ASSERT(r >= 0); + uv_fs_req_cleanup(&read_req); + + /* And now, we test. */ + + r = uv_fs_stat(loop, &stat_req, "test_file", NULL); + ASSERT(r >= 0); + ASSERT(stat_req.result >= 0); + + /* Birthtime must be less than or equal to all of the other stat time fields: + * ctime, atime, and mtime. + */ + birthtime = stat_req.statbuf.st_birthtim.tv_sec; + + ASSERT(birthtime <= stat_req.statbuf.st_ctim.tv_sec); + ASSERT(birthtime <= stat_req.statbuf.st_atim.tv_sec); + ASSERT(birthtime <= stat_req.statbuf.st_mtim.tv_sec); + + /* Cleanup. */ + uv_fs_req_cleanup(&stat_req); + unlink("test_file"); + free(iovs); + + MAKE_VALGRIND_HAPPY(); + + return 0; +} diff --git a/test/test-list.h b/test/test-list.h index c6f1d3c3327..5d4e825a88f 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -276,6 +276,7 @@ TEST_DECLARE (fs_open_dir) TEST_DECLARE (fs_rename_to_existing_file) TEST_DECLARE (fs_write_multiple_bufs) TEST_DECLARE (fs_read_write_null_arguments) +TEST_DECLARE (fs_stat_small_birthtime) TEST_DECLARE (fs_write_alotof_bufs) TEST_DECLARE (fs_write_alotof_bufs_with_offset) TEST_DECLARE (threadpool_queue_work_simple) @@ -692,6 +693,7 @@ TASK_LIST_START TEST_ENTRY (fs_write_alotof_bufs) TEST_ENTRY (fs_write_alotof_bufs_with_offset) TEST_ENTRY (fs_read_write_null_arguments) + TEST_ENTRY (fs_stat_small_birthtime) TEST_ENTRY (threadpool_queue_work_simple) TEST_ENTRY (threadpool_queue_work_einval) TEST_ENTRY (threadpool_multiple_event_loops)