summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/luv/src/fs.c
diff options
context:
space:
mode:
author ImJezze <jezze@gmx.net>2016-02-21 11:48:45 +0100
committer ImJezze <jezze@gmx.net>2016-02-21 11:48:45 +0100
commitcc24a339d8c0517259084b5c178d784626ba965c (patch)
tree9868e9687b5802ae0a3733712a3bbeb3bc75c953 /3rdparty/luv/src/fs.c
parentb5daabda5495dea5c50e17961ecfed2ea8619d76 (diff)
Merge remote-tracking branch 'refs/remotes/mamedev/master'
Second attempt
Diffstat (limited to '3rdparty/luv/src/fs.c')
-rw-r--r--3rdparty/luv/src/fs.c614
1 files changed, 614 insertions, 0 deletions
diff --git a/3rdparty/luv/src/fs.c b/3rdparty/luv/src/fs.c
new file mode 100644
index 00000000000..bacf11ea647
--- /dev/null
+++ b/3rdparty/luv/src/fs.c
@@ -0,0 +1,614 @@
+/*
+ * Copyright 2014 The Luvit Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "luv.h"
+
+static uv_fs_t* luv_check_fs(lua_State* L, int index) {
+ uv_fs_t* req = luaL_checkudata(L, index, "uv_req");
+ luaL_argcheck(L, req->type = UV_FS && req->data, index, "Expected uv_fs_t");
+ return req;
+}
+
+static void luv_push_timespec_table(lua_State* L, const uv_timespec_t* t) {
+ lua_createtable(L, 0, 2);
+ lua_pushinteger(L, t->tv_sec);
+ lua_setfield(L, -2, "sec");
+ lua_pushinteger(L, t->tv_nsec);
+ lua_setfield(L, -2, "nsec");
+}
+
+static void luv_push_stats_table(lua_State* L, const uv_stat_t* s) {
+ const char* type = NULL;
+ lua_createtable(L, 0, 23);
+ lua_pushinteger(L, s->st_dev);
+ lua_setfield(L, -2, "dev");
+ lua_pushinteger(L, s->st_mode);
+ lua_setfield(L, -2, "mode");
+ lua_pushinteger(L, s->st_nlink);
+ lua_setfield(L, -2, "nlink");
+ lua_pushinteger(L, s->st_uid);
+ lua_setfield(L, -2, "uid");
+ lua_pushinteger(L, s->st_gid);
+ lua_setfield(L, -2, "gid");
+ lua_pushinteger(L, s->st_rdev);
+ lua_setfield(L, -2, "rdev");
+ lua_pushinteger(L, s->st_ino);
+ lua_setfield(L, -2, "ino");
+ lua_pushinteger(L, s->st_size);
+ lua_setfield(L, -2, "size");
+ lua_pushinteger(L, s->st_blksize);
+ lua_setfield(L, -2, "blksize");
+ lua_pushinteger(L, s->st_blocks);
+ lua_setfield(L, -2, "blocks");
+ lua_pushinteger(L, s->st_flags);
+ lua_setfield(L, -2, "flags");
+ lua_pushinteger(L, s->st_gen);
+ lua_setfield(L, -2, "gen");
+ luv_push_timespec_table(L, &s->st_atim);
+ lua_setfield(L, -2, "atime");
+ luv_push_timespec_table(L, &s->st_mtim);
+ lua_setfield(L, -2, "mtime");
+ luv_push_timespec_table(L, &s->st_ctim);
+ lua_setfield(L, -2, "ctime");
+ luv_push_timespec_table(L, &s->st_birthtim);
+ lua_setfield(L, -2, "birthtime");
+ if (S_ISREG(s->st_mode)) {
+ type = "file";
+ }
+ else if (S_ISDIR(s->st_mode)) {
+ type = "directory";
+ }
+ else if (S_ISLNK(s->st_mode)) {
+ type = "link";
+ }
+ else if (S_ISFIFO(s->st_mode)) {
+ type = "fifo";
+ }
+#ifdef S_ISSOCK
+ else if (S_ISSOCK(s->st_mode)) {
+ type = "socket";
+ }
+#endif
+ else if (S_ISCHR(s->st_mode)) {
+ type = "char";
+ }
+ else if (S_ISBLK(s->st_mode)) {
+ type = "block";
+ }
+ if (type) {
+ lua_pushstring(L, type);
+ lua_setfield(L, -2, "type");
+ }
+}
+
+static int luv_check_flags(lua_State* L, int index) {
+ const char* string;
+ if (lua_isnumber(L, index)) {
+ return lua_tointeger(L, index);
+ }
+ else if (!lua_isstring(L, index)) {
+ return luaL_argerror(L, index, "Expected string or integer for file open mode");
+ }
+ string = lua_tostring(L, index);
+
+ if (strcmp(string, "r") == 0) return O_RDONLY;
+#ifdef O_SYNC
+ if (strcmp(string, "rs") == 0 ||
+ strcmp(string, "sr") == 0) return O_RDONLY | O_SYNC;
+#endif
+ if (strcmp(string, "r+") == 0) return O_RDWR;
+#ifdef O_SYNC
+ if (strcmp(string, "rs+") == 0 ||
+ strcmp(string, "sr+") == 0) return O_RDWR | O_SYNC;
+#endif
+ if (strcmp(string, "w") == 0) return O_TRUNC | O_CREAT | O_WRONLY;
+ if (strcmp(string, "wx") == 0 ||
+ strcmp(string, "xw") == 0) return O_TRUNC | O_CREAT | O_WRONLY | O_EXCL;
+ if (strcmp(string, "w+") == 0) return O_TRUNC | O_CREAT | O_RDWR;
+ if (strcmp(string, "wx+") == 0 ||
+ strcmp(string, "xw+") == 0) return O_TRUNC | O_CREAT | O_RDWR | O_EXCL;
+ if (strcmp(string, "a") == 0) return O_APPEND | O_CREAT | O_WRONLY;
+ if (strcmp(string, "ax") == 0 ||
+ strcmp(string, "xa") == 0) return O_APPEND | O_CREAT | O_WRONLY | O_EXCL;
+ if (strcmp(string, "a+") == 0) return O_APPEND | O_CREAT | O_RDWR;
+ if (strcmp(string, "ax+") == 0 ||
+ strcmp(string, "xa+") == 0) return O_APPEND | O_CREAT | O_RDWR | O_EXCL;
+
+ return luaL_error(L, "Unknown file open flag '%s'", string);
+}
+
+static int luv_check_amode(lua_State* L, int index) {
+ size_t i;
+ int mode;
+ const char* string;
+ if (lua_isnumber(L, index)) {
+ return lua_tointeger(L, index);
+ }
+ else if (!lua_isstring(L, index)) {
+ return luaL_argerror(L, index, "Expected string or integer for file access mode check");
+ }
+ string = lua_tostring(L, index);
+ mode = 0;
+ for (i = 0; i < strlen(string); ++i) {
+ switch (string[i]) {
+ case 'r': case 'R':
+ mode |= R_OK;
+ break;
+ case 'w': case 'W':
+ mode |= W_OK;
+ break;
+ case 'x': case 'X':
+ mode |= X_OK;
+ break;
+ default:
+ return luaL_argerror(L, index, "Unknown character in access mode string");
+ }
+ }
+ return mode;
+}
+
+/* Processes a result and pushes the data onto the stack
+ returns the number of items pushed */
+static int push_fs_result(lua_State* L, uv_fs_t* req) {
+ luv_req_t* data = req->data;
+
+ if (req->fs_type == UV_FS_ACCESS) {
+ lua_pushboolean(L, req->result >= 0);
+ return 1;
+ }
+
+ if (req->result < 0) {
+ lua_pushnil(L);
+ if (req->path) {
+ lua_pushfstring(L, "%s: %s: %s", uv_err_name(req->result), uv_strerror(req->result), req->path);
+ }
+ else {
+ lua_pushfstring(L, "%s: %s", uv_err_name(req->result), uv_strerror(req->result));
+ }
+ return 2;
+ }
+
+ switch (req->fs_type) {
+ case UV_FS_CLOSE:
+ case UV_FS_RENAME:
+ case UV_FS_UNLINK:
+ case UV_FS_RMDIR:
+ case UV_FS_MKDIR:
+ case UV_FS_FTRUNCATE:
+ case UV_FS_FSYNC:
+ case UV_FS_FDATASYNC:
+ case UV_FS_LINK:
+ case UV_FS_SYMLINK:
+ case UV_FS_CHMOD:
+ case UV_FS_FCHMOD:
+ case UV_FS_CHOWN:
+ case UV_FS_FCHOWN:
+ case UV_FS_UTIME:
+ case UV_FS_FUTIME:
+ lua_pushboolean(L, 1);
+ return 1;
+
+ case UV_FS_OPEN:
+ case UV_FS_SENDFILE:
+ case UV_FS_WRITE:
+ lua_pushinteger(L, req->result);
+ return 1;
+
+ case UV_FS_STAT:
+ case UV_FS_LSTAT:
+ case UV_FS_FSTAT:
+ luv_push_stats_table(L, &req->statbuf);
+ return 1;
+
+ case UV_FS_MKDTEMP:
+ lua_pushstring(L, req->path);
+ return 1;
+
+ case UV_FS_READLINK:
+ case UV_FS_REALPATH:
+ lua_pushstring(L, (char*)req->ptr);
+ return 1;
+
+ case UV_FS_READ:
+ lua_pushlstring(L, data->data, req->result);
+ return 1;
+
+ case UV_FS_SCANDIR:
+ // Expose the userdata for the request.
+ lua_rawgeti(L, LUA_REGISTRYINDEX, data->req_ref);
+ return 1;
+
+ default:
+ lua_pushnil(L);
+ lua_pushfstring(L, "UNKNOWN FS TYPE %d\n", req->fs_type);
+ return 2;
+ }
+
+}
+
+static void luv_fs_cb(uv_fs_t* req) {
+ lua_State* L = luv_state(req->loop);
+
+ int nargs = push_fs_result(L, req);
+ if (nargs == 2 && lua_isnil(L, -nargs)) {
+ // If it was an error, convert to (err, value) format.
+ lua_remove(L, -nargs);
+ nargs--;
+ }
+ else {
+ // Otherwise insert a nil in front to convert to (err, value) format.
+ lua_pushnil(L);
+ lua_insert(L, -nargs - 1);
+ nargs++;
+ }
+ luv_fulfill_req(L, req->data, nargs);
+ if (req->fs_type != UV_FS_SCANDIR) {
+ luv_cleanup_req(L, req->data);
+ req->data = NULL;
+ uv_fs_req_cleanup(req);
+ }
+}
+
+#define FS_CALL(func, req, ...) { \
+ int ret, sync; \
+ luv_req_t* data = req->data; \
+ sync = data->callback_ref == LUA_NOREF; \
+ ret = uv_fs_##func(luv_loop(L), req, __VA_ARGS__, \
+ sync ? NULL : luv_fs_cb); \
+ if (req->fs_type != UV_FS_ACCESS && ret < 0) { \
+ lua_pushnil(L); \
+ if (req->path) { \
+ lua_pushfstring(L, "%s: %s: %s", uv_err_name(req->result), uv_strerror(req->result), req->path); \
+ } \
+ else { \
+ lua_pushfstring(L, "%s: %s", uv_err_name(req->result), uv_strerror(req->result)); \
+ } \
+ lua_pushstring(L, uv_err_name(req->result)); \
+ luv_cleanup_req(L, req->data); \
+ req->data = NULL; \
+ uv_fs_req_cleanup(req); \
+ return 3; \
+ } \
+ if (sync) { \
+ int nargs = push_fs_result(L, req); \
+ if (req->fs_type != UV_FS_SCANDIR) { \
+ luv_cleanup_req(L, req->data); \
+ req->data = NULL; \
+ uv_fs_req_cleanup(req); \
+ } \
+ return nargs; \
+ } \
+ lua_rawgeti(L, LUA_REGISTRYINDEX, data->req_ref); \
+ return 1; \
+}
+
+static int luv_fs_close(lua_State* L) {
+ uv_file file = luaL_checkinteger(L, 1);
+ int ref = luv_check_continuation(L, 2);
+ uv_fs_t* req = lua_newuserdata(L, sizeof(*req));
+ req->data = luv_setup_req(L, ref);
+ FS_CALL(close, req, file);
+}
+
+static int luv_fs_open(lua_State* L) {
+ const char* path = luaL_checkstring(L, 1);
+ int flags = luv_check_flags(L, 2);
+ int mode = luaL_checkinteger(L, 3);
+ int ref = luv_check_continuation(L, 4);
+ uv_fs_t* req = lua_newuserdata(L, sizeof(*req));
+ req->data = luv_setup_req(L, ref);
+ FS_CALL(open, req, path, flags, mode);
+}
+
+static int luv_fs_read(lua_State* L) {
+ uv_file file = luaL_checkinteger(L, 1);
+ int64_t len = luaL_checkinteger(L, 2);
+ int64_t offset = luaL_checkinteger(L, 3);
+ uv_buf_t buf;
+ int ref;
+ uv_fs_t* req;
+ char* data = malloc(len);
+ if (!data) return luaL_error(L, "Failure to allocate buffer");
+ buf = uv_buf_init(data, len);
+ ref = luv_check_continuation(L, 4);
+ req = lua_newuserdata(L, sizeof(*req));
+ req->data = luv_setup_req(L, ref);
+ // TODO: find out why we can't just use req->ptr for the base
+ ((luv_req_t*)req->data)->data = buf.base;
+ FS_CALL(read, req, file, &buf, 1, offset);
+}
+
+static int luv_fs_unlink(lua_State* L) {
+ const char* path = luaL_checkstring(L, 1);
+ int ref = luv_check_continuation(L, 2);
+ uv_fs_t* req = lua_newuserdata(L, sizeof(*req));
+ req->data = luv_setup_req(L, ref);
+ FS_CALL(unlink, req, path);
+}
+
+static int luv_fs_write(lua_State* L) {
+ uv_file file = luaL_checkinteger(L, 1);
+ uv_buf_t buf;
+ int64_t offset;
+ int ref;
+ uv_fs_t* req;
+ size_t count;
+ uv_buf_t *bufs = NULL;
+
+ if (lua_istable(L, 2)) {
+ bufs = luv_prep_bufs(L, 2, &count);
+ buf.base = NULL;
+ }
+ else if (lua_isstring(L, 2)) {
+ luv_check_buf(L, 2, &buf);
+ count = 1;
+ }
+ else {
+ return luaL_argerror(L, 2, "data must be string or table of strings");
+ }
+
+ offset = luaL_checkinteger(L, 3);
+ ref = luv_check_continuation(L, 4);
+ req = lua_newuserdata(L, sizeof(*req));
+ req->data = luv_setup_req(L, ref);
+ req->ptr = buf.base;
+ ((luv_req_t*)req->data)->data = bufs;
+ FS_CALL(write, req, file, bufs ? bufs : &buf, count, offset);
+}
+
+static int luv_fs_mkdir(lua_State* L) {
+ const char* path = luaL_checkstring(L, 1);
+ int mode = luaL_checkinteger(L, 2);
+ int ref = luv_check_continuation(L, 3);
+ uv_fs_t* req = lua_newuserdata(L, sizeof(*req));
+ req->data = luv_setup_req(L, ref);
+ FS_CALL(mkdir, req, path, mode);
+}
+
+static int luv_fs_mkdtemp(lua_State* L) {
+ const char* tpl = luaL_checkstring(L, 1);
+ int ref = luv_check_continuation(L, 2);
+ uv_fs_t* req = lua_newuserdata(L, sizeof(*req));
+ req->data = luv_setup_req(L, ref);
+ FS_CALL(mkdtemp, req, tpl);
+}
+
+static int luv_fs_rmdir(lua_State* L) {
+ const char* path = luaL_checkstring(L, 1);
+ int ref = luv_check_continuation(L, 2);
+ uv_fs_t* req = lua_newuserdata(L, sizeof(*req));
+ req->data = luv_setup_req(L, ref);
+ FS_CALL(rmdir, req, path);
+}
+
+static int luv_fs_scandir(lua_State* L) {
+ const char* path = luaL_checkstring(L, 1);
+ int flags = 0; // TODO: find out what these flags are.
+ int ref = luv_check_continuation(L, 2);
+ uv_fs_t* req = lua_newuserdata(L, sizeof(*req));
+ req->data = luv_setup_req(L, ref);
+ FS_CALL(scandir, req, path, flags);
+}
+
+static int luv_fs_scandir_next(lua_State* L) {
+ uv_fs_t* req = luv_check_fs(L, 1);
+ uv_dirent_t ent;
+ int ret = uv_fs_scandir_next(req, &ent);
+ const char* type;
+ if (ret == UV_EOF) {
+ luv_cleanup_req(L, req->data);
+ req->data = NULL;
+ uv_fs_req_cleanup(req);
+ return 0;
+ }
+ if (ret < 0) return luv_error(L, ret);
+ lua_pushstring(L, ent.name);
+ switch (ent.type) {
+ case UV_DIRENT_UNKNOWN: return 1;
+ case UV_DIRENT_FILE: type = "file"; break;
+ case UV_DIRENT_DIR: type = "directory"; break;
+ case UV_DIRENT_LINK: type = "link"; break;
+ case UV_DIRENT_FIFO: type = "fifo"; break;
+ case UV_DIRENT_SOCKET: type = "socket"; break;
+ case UV_DIRENT_CHAR: type = "char"; break;
+ case UV_DIRENT_BLOCK: type = "block"; break;
+ default: assert(0);
+ }
+ lua_pushstring(L, type);
+ return 2;
+}
+
+static int luv_fs_stat(lua_State* L) {
+ const char* path = luaL_checkstring(L, 1);
+ int ref = luv_check_continuation(L, 2);
+ uv_fs_t* req = lua_newuserdata(L, sizeof(*req));
+ req->data = luv_setup_req(L, ref);
+ FS_CALL(stat, req, path);
+}
+
+static int luv_fs_fstat(lua_State* L) {
+ uv_file file = luaL_checkinteger(L, 1);
+ int ref = luv_check_continuation(L, 2);
+ uv_fs_t* req = lua_newuserdata(L, sizeof(*req));
+ req->data = luv_setup_req(L, ref);
+ FS_CALL(fstat, req, file);
+}
+
+static int luv_fs_lstat(lua_State* L) {
+ const char* path = luaL_checkstring(L, 1);
+ int ref = luv_check_continuation(L, 2);
+ uv_fs_t* req = lua_newuserdata(L, sizeof(*req));
+ req->data = luv_setup_req(L, ref);
+ FS_CALL(lstat, req, path);
+}
+
+static int luv_fs_rename(lua_State* L) {
+ const char* path = luaL_checkstring(L, 1);
+ const char* new_path = luaL_checkstring(L, 2);
+ int ref = luv_check_continuation(L, 3);
+ uv_fs_t* req = lua_newuserdata(L, sizeof(*req));
+ req->data = luv_setup_req(L, ref);
+ FS_CALL(rename, req, path, new_path);
+}
+
+static int luv_fs_fsync(lua_State* L) {
+ uv_file file = luaL_checkinteger(L, 1);
+ int ref = luv_check_continuation(L, 2);
+ uv_fs_t* req = lua_newuserdata(L, sizeof(*req));
+ req->data = luv_setup_req(L, ref);
+ FS_CALL(fsync, req, file);
+}
+
+static int luv_fs_fdatasync(lua_State* L) {
+ uv_file file = luaL_checkinteger(L, 1);
+ int ref = luv_check_continuation(L, 2);
+ uv_fs_t* req = lua_newuserdata(L, sizeof(*req));
+ req->data = luv_setup_req(L, ref);
+ FS_CALL(fdatasync, req, file);
+}
+
+static int luv_fs_ftruncate(lua_State* L) {
+ uv_file file = luaL_checkinteger(L, 1);
+ int64_t offset = luaL_checkinteger(L, 2);
+ int ref = luv_check_continuation(L, 3);
+ uv_fs_t* req = lua_newuserdata(L, sizeof(*req));
+ req->data = luv_setup_req(L, ref);
+ FS_CALL(ftruncate, req, file, offset);
+}
+
+static int luv_fs_sendfile(lua_State* L) {
+ uv_file out_fd = luaL_checkinteger(L, 1);
+ uv_file in_fd = luaL_checkinteger(L, 2);
+ int64_t in_offset = luaL_checkinteger(L, 3);
+ size_t length = luaL_checkinteger(L, 4);
+ int ref = luv_check_continuation(L, 5);
+ uv_fs_t* req = lua_newuserdata(L, sizeof(*req));
+ req->data = luv_setup_req(L, ref);
+ FS_CALL(sendfile, req, out_fd, in_fd, in_offset, length);
+}
+
+static int luv_fs_access(lua_State* L) {
+ const char* path = luaL_checkstring(L, 1);
+ int amode = luv_check_amode(L, 2);
+ int ref = luv_check_continuation(L, 3);
+ uv_fs_t* req = lua_newuserdata(L, sizeof(*req));
+ req->data = luv_setup_req(L, ref);
+ FS_CALL(access, req, path, amode);
+}
+
+static int luv_fs_chmod(lua_State* L) {
+ const char* path = luaL_checkstring(L, 1);
+ int mode = luaL_checkinteger(L, 2);
+ int ref = luv_check_continuation(L, 3);
+ uv_fs_t* req = lua_newuserdata(L, sizeof(*req));
+ req->data = luv_setup_req(L, ref);
+ FS_CALL(chmod, req, path, mode);
+}
+
+static int luv_fs_fchmod(lua_State* L) {
+ uv_file file = luaL_checkinteger(L, 1);
+ int mode = luaL_checkinteger(L, 2);
+ int ref = luv_check_continuation(L, 3);
+ uv_fs_t* req = lua_newuserdata(L, sizeof(*req));
+ req->data = luv_setup_req(L, ref);
+ FS_CALL(fchmod, req, file, mode);
+}
+
+static int luv_fs_utime(lua_State* L) {
+ const char* path = luaL_checkstring(L, 1);
+ double atime = luaL_checknumber(L, 2);
+ double mtime = luaL_checknumber(L, 3);
+ int ref = luv_check_continuation(L, 4);
+ uv_fs_t* req = lua_newuserdata(L, sizeof(*req));
+ req->data = luv_setup_req(L, ref);
+ FS_CALL(utime, req, path, atime, mtime);
+}
+
+static int luv_fs_futime(lua_State* L) {
+ uv_file file = luaL_checkinteger(L, 1);
+ double atime = luaL_checknumber(L, 2);
+ double mtime = luaL_checknumber(L, 3);
+ int ref = luv_check_continuation(L, 4);
+ uv_fs_t* req = lua_newuserdata(L, sizeof(*req));
+ req->data = luv_setup_req(L, ref);
+ FS_CALL(futime, req, file, atime, mtime);
+}
+
+static int luv_fs_link(lua_State* L) {
+ const char* path = luaL_checkstring(L, 1);
+ const char* new_path = luaL_checkstring(L, 2);
+ int ref = luv_check_continuation(L, 3);
+ uv_fs_t* req = lua_newuserdata(L, sizeof(*req));
+ req->data = luv_setup_req(L, ref);
+ FS_CALL(link, req, path, new_path);
+}
+
+static int luv_fs_symlink(lua_State* L) {
+ const char* path = luaL_checkstring(L, 1);
+ const char* new_path = luaL_checkstring(L, 2);
+ int flags = 0, ref;
+ uv_fs_t* req;
+ if (lua_type(L, 3) == LUA_TTABLE) {
+ lua_getfield(L, 3, "dir");
+ if (lua_toboolean(L, -1)) flags |= UV_FS_SYMLINK_DIR;
+ lua_pop(L, 1);
+ lua_getfield(L, 3, "junction");
+ if (lua_toboolean(L, -1)) flags |= UV_FS_SYMLINK_JUNCTION;
+ lua_pop(L, 1);
+ }
+ ref = luv_check_continuation(L, 4);
+ req = lua_newuserdata(L, sizeof(*req));
+ req->data = luv_setup_req(L, ref);
+
+ FS_CALL(symlink, req, path, new_path, flags);
+}
+
+static int luv_fs_readlink(lua_State* L) {
+ const char* path = luaL_checkstring(L, 1);
+ int ref = luv_check_continuation(L, 2);
+ uv_fs_t* req = lua_newuserdata(L, sizeof(*req));
+ req->data = luv_setup_req(L, ref);
+ FS_CALL(readlink, req, path);
+}
+
+static int luv_fs_realpath(lua_State* L) {
+ const char* path = luaL_checkstring(L, 1);
+ int ref = luv_check_continuation(L, 2);
+ uv_fs_t* req = lua_newuserdata(L, sizeof(*req));
+ req->data = luv_setup_req(L, ref);
+ FS_CALL(realpath, req, path);
+}
+
+static int luv_fs_chown(lua_State* L) {
+ const char* path = luaL_checkstring(L, 1);
+ uv_uid_t uid = luaL_checkinteger(L, 2);
+ uv_uid_t gid = luaL_checkinteger(L, 3);
+ int ref = luv_check_continuation(L, 4);
+ uv_fs_t* req = lua_newuserdata(L, sizeof(*req));
+ req->data = luv_setup_req(L, ref);
+ FS_CALL(chown, req, path, uid, gid);
+}
+
+static int luv_fs_fchown(lua_State* L) {
+ uv_file file = luaL_checkinteger(L, 1);
+ uv_uid_t uid = luaL_checkinteger(L, 2);
+ uv_uid_t gid = luaL_checkinteger(L, 3);
+ int ref = luv_check_continuation(L, 4);
+ uv_fs_t* req = lua_newuserdata(L, sizeof(*req));
+ req->data = luv_setup_req(L, ref);
+ FS_CALL(fchown, req, file, uid, gid);
+}