Skip to content

Commit

Permalink
unix: use oldest ctime,atime,mtime as birthtime
Browse files Browse the repository at this point in the history
When the operating system does not support birthtime for stat,
previously behaviour was to use the ctime (last changed) time instead.

This change instead enforces that uv_fs_stat will use the oldest
available time (of ctime, atime, and mtime) rather than arbitrarily
picking ctime.

Fixes: nodejs/node#2222
  • Loading branch information
brendanashworth committed Aug 16, 2015
1 parent 939ea06 commit c1b0d4b
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 4 deletions.
39 changes: 35 additions & 4 deletions src/unix/fs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) && ( \
Expand All @@ -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
Expand Down
60 changes: 60 additions & 0 deletions test/test-fs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
2 changes: 2 additions & 0 deletions test/test-list.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit c1b0d4b

Please sign in to comment.