diff --git a/src/unix/fs.c b/src/unix/fs.c index 8987fd0cb9..264226bcfb 100644 --- a/src/unix/fs.c +++ b/src/unix/fs.c @@ -81,28 +81,36 @@ static int uv__fs_after(eio_req* eio) { req->result = req->eio->result; req->errorno = req->eio->errorno; - if (req->fs_type == UV_FS_READDIR) { - /* - * XXX This is pretty bad. - * We alloc and copy the large null termiated string list from libeio. - * This is done because libeio is going to free eio->ptr2 after this - * callback. We must keep it until uv_fs_req_cleanup. If we get rid of - * libeio this can be avoided. - */ - buflen = 0; - name = req->eio->ptr2; - for (i = 0; i < req->result; i++) { - namelen = strlen(name); - buflen += namelen + 1; - /* TODO check ENOMEM */ - name += namelen; - assert(*name == '\0'); - name++; - } - req->ptr = malloc(buflen); - memcpy(req->ptr, req->eio->ptr2, buflen); - } else if (req->fs_type == UV_FS_STAT || req->fs_type == UV_FS_LSTAT) { - req->ptr = req->eio->ptr2; + switch (req->fs_type) { + case UV_FS_READDIR: + /* + * XXX This is pretty bad. + * We alloc and copy the large null termiated string list from libeio. + * This is done because libeio is going to free eio->ptr2 after this + * callback. We must keep it until uv_fs_req_cleanup. If we get rid of + * libeio this can be avoided. + */ + buflen = 0; + name = req->eio->ptr2; + for (i = 0; i < req->result; i++) { + namelen = strlen(name); + buflen += namelen + 1; + /* TODO check ENOMEM */ + name += namelen; + assert(*name == '\0'); + name++; + } + req->ptr = malloc(buflen); + memcpy(req->ptr, req->eio->ptr2, buflen); + break; + case UV_FS_STAT: + case UV_FS_LSTAT: + case UV_FS_FSTAT: + req->ptr = req->eio->ptr2; + break; + + default: + break; } uv_unref(req->loop); @@ -398,8 +406,31 @@ int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { - assert(0 && "implement me"); - return -1; + uv_fs_req_init(loop, req, UV_FS_FSTAT, cb); + + if (cb) { + /* async */ + uv_ref(loop); + req->eio = eio_fstat(file, EIO_PRI_DEFAULT, uv__fs_after, req); + + if (!req->eio) { + uv_err_new(loop, ENOMEM); + return -1; + } + + } else { + /* sync */ + req->result = fstat(file, &req->statbuf); + + if (req->result < 0) { + uv_err_new(loop, errno); + return -1; + } + + req->ptr = &req->statbuf; + } + + return 0; } diff --git a/test/test-fs.c b/test/test-fs.c index 44cf6db7d4..d8b85efcfe 100644 --- a/test/test-fs.c +++ b/test/test-fs.c @@ -57,6 +57,7 @@ static int fsync_cb_count; static int fdatasync_cb_count; static int ftruncate_cb_count; static int sendfile_cb_count; +static int fstat_cb_count; static uv_loop_t* loop; @@ -88,6 +89,15 @@ static void unlink_cb(uv_fs_t* req) { uv_fs_req_cleanup(req); } +static void fstat_cb(uv_fs_t* req) { + struct stat* s = req->ptr; + ASSERT(req->fs_type == UV_FS_FSTAT); + ASSERT(req->result == 0); + ASSERT(s->st_size == sizeof(test_buf)); + uv_fs_req_cleanup(req); + fstat_cb_count++; +} + static void close_cb(uv_fs_t* req) { int r; @@ -546,3 +556,58 @@ TEST_IMPL(fs_async_sendfile) { return 0; } + + +TEST_IMPL(fs_fstat) { + int r; + uv_fs_t req; + uv_file file; + + /* Setup. */ + unlink("test_file"); + + uv_init(); + + loop = uv_default_loop(); + + r = uv_fs_open(loop, &req, "test_file", O_RDWR | O_CREAT, 0, NULL); + ASSERT(r == 0); + ASSERT(req.result != -1); + file = req.result; + uv_fs_req_cleanup(&req); + + r = uv_fs_write(loop, &req, file, test_buf, sizeof(test_buf), -1, NULL); + ASSERT(r == 0); + ASSERT(req.result == sizeof(test_buf)); + uv_fs_req_cleanup(&req); + + r = uv_fs_fstat(loop, &req, file, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + struct stat* s = req.ptr; + ASSERT(s->st_size == sizeof(test_buf)); + uv_fs_req_cleanup(&req); + + /* Now do the uv_fs_fstat call asynchronously */ + r = uv_fs_fstat(loop, &req, file, fstat_cb); + ASSERT(r == 0); + uv_run(loop); + ASSERT(fstat_cb_count == 1); + + + r = uv_fs_close(loop, &req, file, NULL); + ASSERT(r == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + /* + * Run the loop just to check we don't have make any extranious uv_ref() + * calls. This should drop out immediately. + */ + uv_run(loop); + + /* Cleanup. */ + unlink("test_file"); + + return 0; +} diff --git a/test/test-list.h b/test/test-list.h index a162551991..22bc12e844 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -76,6 +76,7 @@ TEST_DECLARE (fs_file_async) TEST_DECLARE (fs_file_sync) TEST_DECLARE (fs_async_dir) TEST_DECLARE (fs_async_sendfile) +TEST_DECLARE (fs_fstat) TEST_DECLARE (threadpool_queue_work_simple) #ifdef _WIN32 TEST_DECLARE (spawn_detect_pipe_name_collisions_on_windows) @@ -179,6 +180,7 @@ TASK_LIST_START TEST_ENTRY (fs_file_sync) TEST_ENTRY (fs_async_dir) TEST_ENTRY (fs_async_sendfile) + TEST_ENTRY (fs_fstat) TEST_ENTRY (threadpool_queue_work_simple)