diff --git a/src/disk_fiber_io.cc b/src/disk_fiber_io.cc index cd064e628b1ee30d474493a8c5fc2c007541dccb..2be8176c0f27eb9105404ca4e08ee608be1444c0 100644 --- a/src/disk_fiber_io.cc +++ b/src/disk_fiber_io.cc @@ -30,25 +30,82 @@ #include <disk_fiber_io.h> #include <coeio.h> #include <fiber.h> +#include <say.h> +#include <stdio.h> +#include <stdlib.h> + struct fiber_eio { ssize_t result; int errorno; struct fiber *fiber; bool done; + + union { + struct { + int fd; + struct stat *buf; + } fstat; + + struct { + struct stat *buf; + const char *pathname; + } lstat; + + struct { + const char *pattern; + int flags; + int (*errfunc) (const char *epath, int eerrno); + glob_t *pglob; + } glob; + + struct { + int fd; + off_t offset; + int whence; + } lseek; + + struct { + int fd; + const void *buf; + size_t count; + } write; + + struct { + int fd; + void *buf; + size_t count; + } read; + + struct { + const char *pathname; + char *buf; + size_t bufsize; + } readlink; + + struct { + char *tpl; + char *res; + } tempdir; + }; }; +#define INIT_EIO(name) \ + struct fiber_eio name; \ + memset(&name, 0, sizeof(name)); \ + name.fiber = fiber(); \ -int + +static int dfio_complete(eio_req *req) { - struct fiber_eio *fio = (struct fiber_eio *)req->data; + struct fiber_eio *eio = (struct fiber_eio *)req->data; - fio->result = req->result; - fio->errorno = req->errorno; - fio->done = true; + eio->errorno = req->errorno; + eio->done = true; + eio->result = req->result; - fiber_wakeup(fio->fiber); + fiber_wakeup(eio->fiber); return 0; } @@ -62,26 +119,32 @@ dfio_wait_done(eio_req *req, struct fiber_eio *eio) while (!eio->done) fiber_yield(); + errno = eio->errorno; - say_info("Done evio operation"); return eio->result; } int dfio_open(const char *path, int flags, mode_t mode) { - struct fiber_eio eio = { 0, 0, fiber(), false }; - + INIT_EIO(eio); eio_req *req = eio_open(path, flags, mode, 0, dfio_complete, &eio); return dfio_wait_done(req, &eio); } +int +dfio_close(int fd) +{ + INIT_EIO(eio); + eio_req *req = eio_close(fd, 0, dfio_complete, &eio); + return dfio_wait_done(req, &eio); +} + ssize_t dfio_pwrite(int fd, const void *buf, size_t count, off_t offset) { - struct fiber_eio eio = { 0, 0, fiber(), false }; - say_info("Write %s (%zu bytes)", (const char *)buf, count); + INIT_EIO(eio); eio_req *req = eio_write(fd, (void *)buf, count, offset, 0, dfio_complete, &eio); return dfio_wait_done(req, &eio); @@ -90,9 +153,302 @@ dfio_pwrite(int fd, const void *buf, size_t count, off_t offset) ssize_t dfio_pread(int fd, void *buf, size_t count, off_t offset) { - struct fiber_eio eio = { 0, 0, fiber(), false }; - say_info("Read %s (%zu bytes)", (const char *)buf, count); + INIT_EIO(eio); eio_req *req = eio_read(fd, buf, count, offset, 0, dfio_complete, &eio); return dfio_wait_done(req, &eio); } + +static void +dfio_do_write(eio_req *req) +{ + struct fiber_eio *eio = (struct fiber_eio *)req->data; + req->result = write(eio->write.fd, eio->write.buf, eio->write.count); + eio->errorno = errno; +} + +ssize_t +dfio_write(int fd, const void *buf, size_t count) +{ + INIT_EIO(eio); + eio.write.buf = buf; + eio.write.count = count; + eio.write.fd = fd; + eio_req *req = eio_custom(dfio_do_write, 0, dfio_complete, &eio); + return dfio_wait_done(req, &eio); +} + +static void +dfio_do_read(eio_req *req) +{ + struct fiber_eio *eio = (struct fiber_eio *)req->data; + req->result = read(eio->read.fd, eio->read.buf, eio->read.count); + req->errorno = errno; +} + +ssize_t +dfio_read(int fd, void *buf, size_t count) +{ + INIT_EIO(eio); + eio.read.buf = buf; + eio.read.count = count; + eio.read.fd = fd; + eio_req *req = eio_custom(dfio_do_read, 0, dfio_complete, &eio); + return dfio_wait_done(req, &eio); +} + + +static void +dfio_do_lseek(eio_req *req) +{ + struct fiber_eio *eio = (struct fiber_eio *)req->data; + req->result = + lseek(eio->lseek.fd, eio->lseek.offset, eio->lseek.whence); + req->errorno = errno; +} + +off_t +dfio_lseek(int fd, off_t offset, int whence) +{ + INIT_EIO(eio); + + eio.lseek.whence = whence; + eio.lseek.offset = offset; + eio.lseek.fd = fd; + + eio_req *req = eio_custom(dfio_do_lseek, 0, dfio_complete, &eio); + return dfio_wait_done(req, &eio); +} + +static void +dfio_do_lstat(eio_req *req) +{ + struct fiber_eio *eio = (struct fiber_eio *)req->data; + req->result = lstat(eio->lstat.pathname, eio->lstat.buf); + req->errorno = errno; +} + + +int +dfio_lstat(const char *pathname, struct stat *buf) +{ + INIT_EIO(eio); + eio.lstat.pathname = pathname; + eio.lstat.buf = buf; + eio_req *req = eio_custom(dfio_do_lstat, 0, dfio_complete, &eio); + return dfio_wait_done(req, &eio); +} + +static void +dfio_do_stat(eio_req *req) +{ + struct fiber_eio *eio = (struct fiber_eio *)req->data; + req->result = stat(eio->lstat.pathname, eio->lstat.buf); + req->errorno = errno; +} + +int +dfio_stat(const char *pathname, struct stat *buf) +{ + INIT_EIO(eio); + eio.lstat.pathname = pathname; + eio.lstat.buf = buf; + eio_req *req = eio_custom(dfio_do_stat, 0, dfio_complete, &eio); + return dfio_wait_done(req, &eio); +} + +static void +dfio_do_fstat(eio_req *req) +{ + struct fiber_eio *eio = (struct fiber_eio *)req->data; + req->result = fstat(eio->fstat.fd, eio->fstat.buf); + req->errorno = errno; +} + +int +dfio_fstat(int fd, struct stat *stat) +{ + INIT_EIO(eio); + eio.fstat.fd = fd; + eio.fstat.buf = stat; + + eio_req *req = eio_custom(dfio_do_fstat, 0, dfio_complete, &eio); + return dfio_wait_done(req, &eio); +} + +int +dfio_rename(const char *oldpath, const char *newpath) +{ + INIT_EIO(eio); + eio_req *req = eio_rename(oldpath, newpath, 0, dfio_complete, &eio); + return dfio_wait_done(req, &eio); + +} + +int +dfio_unlink(const char *pathname) +{ + INIT_EIO(eio); + eio_req *req = eio_unlink(pathname, 0, dfio_complete, &eio); + return dfio_wait_done(req, &eio); +} + +int +dfio_ftruncate(int fd, off_t length) +{ + INIT_EIO(eio); + eio_req *req = eio_ftruncate(fd, length, 0, dfio_complete, &eio); + return dfio_wait_done(req, &eio); +} + +int +dfio_truncate(const char *path, off_t length) +{ + INIT_EIO(eio); + eio_req *req = eio_truncate(path, length, 0, dfio_complete, &eio); + return dfio_wait_done(req, &eio); +} + +static void +dfio_do_glob(eio_req *req) +{ + struct fiber_eio *eio = (struct fiber_eio *)req->data; + req->result = glob(eio->glob.pattern, + eio->glob.flags, eio->glob.errfunc, eio->glob.pglob); + req->errorno = errno; +} + +int +dfio_glob(const char *pattern, int flags, + int (*errfunc) (const char *epath, int eerrno), + glob_t *pglob) +{ + INIT_EIO(eio); + eio.glob.pattern = pattern; + eio.glob.flags = flags; + eio.glob.errfunc = errfunc; + eio.glob.pglob = pglob; + eio_req *req = eio_custom(dfio_do_glob, 0, dfio_complete, &eio); + return dfio_wait_done(req, &eio); +} + +int +dfio_chown(const char *path, uid_t owner, gid_t group) +{ + INIT_EIO(eio); + eio_req *req = eio_chown(path, owner, group, 0, dfio_complete, &eio); + return dfio_wait_done(req, &eio); +} + +int +dfio_chmod(const char *path, mode_t mode) +{ + INIT_EIO(eio); + eio_req *req = eio_chmod(path, mode, 0, dfio_complete, &eio); + return dfio_wait_done(req, &eio); +} + +int +dfio_mkdir(const char *pathname, mode_t mode) +{ + INIT_EIO(eio); + eio_req *req = eio_mkdir(pathname, mode, 0, dfio_complete, &eio); + return dfio_wait_done(req, &eio); +} + +int +dfio_rmdir(const char *pathname) +{ + INIT_EIO(eio); + eio_req *req = eio_rmdir(pathname, 0, dfio_complete, &eio); + return dfio_wait_done(req, &eio); +} + +int +dfio_link(const char *oldpath, const char *newpath) +{ + INIT_EIO(eio); + eio_req *req = eio_link(oldpath, newpath, 0, dfio_complete, &eio); + return dfio_wait_done(req, &eio); +} + +int +dfio_symlink(const char *target, const char *linkpath) +{ + INIT_EIO(eio); + eio_req *req = eio_symlink(target, linkpath, 0, dfio_complete, &eio); + return dfio_wait_done(req, &eio); +} + +static void +dfio_do_readlink(eio_req *req) +{ + struct fiber_eio *eio = (struct fiber_eio *)req->data; + req->result = readlink(eio->readlink.pathname, + eio->readlink.buf, eio->readlink.bufsize); + req->errorno = errno; +} + +int +dfio_readlink(const char *pathname, char *buf, size_t bufsize) +{ + INIT_EIO(eio); + eio.readlink.pathname = pathname; + eio.readlink.buf = buf; + eio.readlink.bufsize = bufsize; + eio_req *req = eio_custom(dfio_do_readlink, 0, dfio_complete, &eio); + return dfio_wait_done(req, &eio); +} + +static void +dfio_do_tempdir(eio_req *req) +{ + struct fiber_eio *eio = (struct fiber_eio *)req->data; + char *res = mkdtemp(eio->tempdir.tpl); + req->errorno = errno; + if (res == NULL) { + req->result = -1; + } else { + req->result = 0; + eio->tempdir.res = res; + } +} + +const char * +dfio_tempdir() +{ + static __thread char path[PATH_MAX]; + INIT_EIO(eio); + + snprintf(path, PATH_MAX, "/tmp/XXXXXX"); + + eio.tempdir.tpl = path; + eio_req *req = eio_custom(dfio_do_tempdir, 0, dfio_complete, &eio); + if (dfio_wait_done(req, &eio) == 0) + return eio.tempdir.res; + return NULL; +} + +int +dfio_sync() +{ + INIT_EIO(eio); + eio_req *req = eio_sync(0, dfio_complete, &eio); + return dfio_wait_done(req, &eio); +} + +int +dfio_fsync(int fd) +{ + INIT_EIO(eio); + eio_req *req = eio_fsync(fd, 0, dfio_complete, &eio); + return dfio_wait_done(req, &eio); +} + +int +dfio_fdatasync(int fd) +{ + INIT_EIO(eio); + eio_req *req = eio_fdatasync(fd, 0, dfio_complete, &eio); + return dfio_wait_done(req, &eio); +} diff --git a/src/disk_fiber_io.h b/src/disk_fiber_io.h index 74121ae6ade5de0f41cec5f7cf5e370caaa4ea8c..5410aea2d5b20b6d4071bf7d4e155fd2f7f79c3d 100644 --- a/src/disk_fiber_io.h +++ b/src/disk_fiber_io.h @@ -30,11 +30,41 @@ */ #include <sys/types.h> +#include <glob.h> -int dfio_open(const char *path, int flags, mode_t mode); +int dfio_open(const char *path, int flags, mode_t mode); +int dfio_close(int fd); ssize_t dfio_pwrite(int fd, const void *buf, size_t count, off_t offset); ssize_t dfio_pread(int fd, void *buf, size_t count, off_t offset); +ssize_t dfio_read(int fd, void *buf, size_t count); +ssize_t dfio_write(int fd, const void *buf, size_t count); +off_t dfio_lseek(int fd, off_t offset, int whence); +int dfio_stat(const char *pathname, struct stat *buf); +int dfio_lstat(const char *pathname, struct stat *buf); +int dfio_fstat(int fd, struct stat *buf); +int dfio_rename(const char *oldpath, const char *newpath); +int dfio_unlink(const char *pathname); +int dfio_mkdir(const char *pathname, mode_t mode); +int dfio_rmdir(const char *pathname); +int dfio_ftruncate(int fd, off_t length); +int dfio_truncate(const char *path, off_t length); +int dfio_glob(const char *pattern, int flags, + int (*errfunc) (const char *epath, int eerrno), + glob_t *pglob); +int dfio_chown(const char *path, uid_t owner, gid_t group); +int dfio_chmod(const char *path, mode_t mode); + +int dfio_link(const char *oldpath, const char *newpath); +int dfio_symlink(const char *target, const char *linkpath); +int dfio_readlink(const char *pathname, char *buf, size_t bufsiz); + +int dfio_sync(); +int dfio_fsync(int fd); +int dfio_fdatasync(int fd); + + +const char *dfio_tempdir(); #endif /* INCLUDES_TARANTOOL_LUA_DISK_FIBER_IO_H */ diff --git a/src/lua/fio.cc b/src/lua/fio.cc index 631b748c6eca23588091c31dd4f81a8a6c5f09ef..f1ac449c084638d2d1f162c1ad916013c4f158ef 100644 --- a/src/lua/fio.cc +++ b/src/lua/fio.cc @@ -1,5 +1,7 @@ #include <sys/types.h> #include <sys/stat.h> +#include <pwd.h> +#include <grp.h> #include <fcntl.h> #include <unistd.h> #include <glob.h> @@ -69,6 +71,7 @@ fio_eio_pread(struct lua_State *L) int res = dfio_pread(fh, buf, len, offset); + if (res < 0) { lua_pop(L, 1); lua_pushnil(L); @@ -80,47 +83,163 @@ fio_eio_pread(struct lua_State *L) } +static int +fio_eio_rename(struct lua_State *L) +{ + if (lua_gettop(L) < 2) + luaL_error(L, "Usage: fio.rename(oldpath, newpath)"); + const char *oldpath = lua_tostring(L, 1); + const char *newpath = lua_tostring(L, 2); + int res = dfio_rename(oldpath, newpath); + lua_pushboolean(L, res == 0); + return 1; +} -#define ADD_CONST(c) { \ - lua_pushliteral(L, # c); \ - lua_pushinteger(L, c); \ - lua_settable(L, -3); \ +static int +fio_eio_unlink(struct lua_State *L) +{ + if (lua_gettop(L) < 1) + luaL_error(L, "Usage: fio.unlink(pathname)"); + const char *pathname = lua_tostring(L, 1); + if (!pathname) { + errno = EINVAL; + lua_pushboolean(L, 0); + return 1; } + int res = dfio_unlink(pathname); + lua_pushboolean(L, res == 0); + return 1; +} +static int +fio_eio_ftruncate(struct lua_State *L) +{ + int fd = lua_tointeger(L, 1); + off_t length = lua_tonumber(L, 2); + int res = dfio_ftruncate(fd, length); + lua_pushboolean(L, res == 0); + return 1; +} static int -fio_lua_glob(struct lua_State *L) +fio_eio_truncate(struct lua_State *L) { - if (lua_gettop(L) < 1) - luaL_error(L, "Usage: fio.glob('[pattern].*'"); - if (!lua_isstring(L, 1)) - luaL_error(L, "pattern must be string"); - const char *p = lua_tostring(L, 1); + int top = lua_gettop(L); + if (top < 1) + luaL_error(L, "Usage: fio.truncate(pathname[, newlen])"); + const char *path = lua_tostring(L, 1); + off_t length; + if (top >= 2) + length = lua_tonumber(L, 2); + else + length = 0; + int res = dfio_truncate(path, length); + + lua_pushboolean(L, res == 0); + return 1; +} - glob_t globbuf; - switch (glob(p, GLOB_NOESCAPE, NULL, &globbuf)) { - case 0: - break; - case GLOB_NOMATCH: - lua_newtable(L); - return 1; +static int +fio_eio_write(struct lua_State *L) +{ + int fh = lua_tointeger(L, 1); + const char *buf = lua_tostring(L, 2); + size_t len = lua_tonumber(L, 3); + int res = dfio_write(fh, buf, len); + lua_pushinteger(L, res); + return 1; +} - default: - case GLOB_NOSPACE: - errno = ENOMEM; +static int +fio_eio_chown(struct lua_State *L) +{ + if (lua_gettop(L) < 3) + luaL_error(L, "Usage: fio.chown(pathname, owner, group)"); + const char *path = lua_tostring(L, 1); + uid_t owner; + if (lua_isnumber(L, 2)) { + owner = lua_tointeger(L, 2); + } else { + const char *username = lua_tostring(L, 2); + struct passwd *entry = getpwnam(username); + if (!entry) { + errno = EINVAL; lua_pushnil(L); return 1; + } + owner = entry->pw_uid; } + gid_t group; + + if (lua_isnumber(L, 3)) { + group = lua_tointeger(L, 3); + } else { + const char *groupname = lua_tostring(L, 3); + struct group *entry = getgrnam(groupname); + if (!entry) { + errno = EINVAL; + lua_pushnil(L); + return 1; + } + group = entry->gr_gid; + } + int res = dfio_chown(path, owner, group); + lua_pushboolean(L, res == 0); + return 1; +} - lua_newtable(L); +static int +fio_eio_chmod(struct lua_State *L) +{ + if (lua_gettop(L) < 2) + luaL_error(L, "Usage: fio.chmod(pathname, mode)"); + const char *path = lua_tostring(L, 1); + mode_t mode = lua_tointeger(L, 2); + lua_pushboolean(L, dfio_chmod(path, mode) == 0); + return 1; +} - for (size_t i = 0; i < globbuf.gl_pathc; i++) { - lua_pushinteger(L, i + 1); - lua_pushstring(L, globbuf.gl_pathv[i]); - lua_settable(L, -3); +static int +fio_eio_read(struct lua_State *L) +{ + int fh = lua_tointeger(L, 1); + size_t len = lua_tonumber(L, 2); + + if (!len) { + lua_pushliteral(L, ""); + return 1; } - globfree(&globbuf); + /* allocate buffer at lua stack */ + void *buf = lua_newuserdata(L, len); + if (!buf) { + errno = ENOMEM; + lua_pushnil(L); + return 1; + } + + + int res = dfio_read(fh, buf, len); + + if (res < 0) { + lua_pop(L, 1); + lua_pushnil(L); + return 1; + } + lua_pushlstring(L, (char *)buf, res); + lua_remove(L, -2); + return 1; +} + + +static int +fio_eio_lseek(struct lua_State *L) +{ + int fh = lua_tointeger(L, 1); + off_t offset = lua_tonumber(L, 2); + int whence = lua_tointeger(L, 3); + off_t res = dfio_lseek(fh, offset, whence); + lua_pushnumber(L, res); return 1; } @@ -194,40 +313,231 @@ lua_pushstat(struct lua_State *L, const struct stat *stat) return 1; } + static int -fio_lua_lstat(struct lua_State *L) +fio_eio_lstat(struct lua_State *L) { - if (lua_gettop(L) < 1 || !lua_isstring(L, 1)) - luaL_error(L, "Usage: fio.stat(pathname)"); + if (lua_gettop(L) < 1) + luaL_error(L, "pathname is absent"); + const char *pathname = lua_tostring(L, 1); struct stat stat; - if (lstat(lua_tostring(L, 1), &stat) < 0) { + + int res = dfio_lstat(pathname, &stat); + if (res < 0) { lua_pushnil(L); return 1; } return lua_pushstat(L, &stat); } +static int +fio_eio_stat(struct lua_State *L) +{ + if (lua_gettop(L) < 1) + luaL_error(L, "pathname is absent"); + const char *pathname = lua_tostring(L, 1); + struct stat stat; + + int res = dfio_stat(pathname, &stat); + if (res < 0) { + lua_pushnil(L); + return 1; + } + return lua_pushstat(L, &stat); +} static int -fio_lua_fstat(struct lua_State *L) +fio_eio_fstat(struct lua_State *L) { - if (lua_gettop(L) < 1 || !lua_isnumber(L, 1)) - luaL_error(L, "Usage: fio.fstat(fd)"); + int fd = lua_tointeger(L, 1); struct stat stat; - if (fstat(lua_tointeger(L, 1), &stat) < 0) { + int res = dfio_fstat(fd, &stat); + if (res < 0) { lua_pushnil(L); return 1; } return lua_pushstat(L, &stat); } + +static int +fio_eio_mkdir(struct lua_State *L) +{ + int top = lua_gettop(L); + if (top < 1) + luaL_error(L, "usage fio.mkdir(pathname[, mode])"); + + const char *pathname = lua_tostring(L, 1); + + mode_t mode; + + if (top >= 2) + mode = lua_tointeger(L, 2); + else + mode = 0; + lua_pushboolean(L, dfio_mkdir(pathname, mode) == 0); + return 1; +} + +static int +fio_eio_rmdir(struct lua_State *L) +{ + if (lua_gettop(L) < 1) + luaL_error(L, "usage: fio.rmdir(pathname)"); + + const char *pathname = lua_tostring(L, 1); + lua_pushboolean(L, dfio_rmdir(pathname) == 0); + return 1; +} + +static int +fio_eio_glob(struct lua_State *L) +{ + if (lua_gettop(L) < 1) + luaL_error(L, "Usage: fio.glob(pattern)"); + + const char *pattern = lua_tostring(L, 1); + + glob_t globbuf; + switch (glob(pattern, GLOB_NOESCAPE, NULL, &globbuf)) { + case 0: + break; + case GLOB_NOMATCH: + lua_newtable(L); + return 1; + + default: + case GLOB_NOSPACE: + errno = ENOMEM; + lua_pushnil(L); + return 1; + } + + lua_newtable(L); + + for (size_t i = 0; i < globbuf.gl_pathc; i++) { + lua_pushinteger(L, i + 1); + lua_pushstring(L, globbuf.gl_pathv[i]); + lua_settable(L, -3); + } + + globfree(&globbuf); + return 1; +} + +static int +fio_eio_link(struct lua_State *L) +{ + if (lua_gettop(L) < 2) + luaL_error(L, "Usage: fio.link(target, linkpath)"); + const char *target = lua_tostring(L, 1); + const char *linkpath = lua_tostring(L, 2); + lua_pushboolean(L, dfio_link(target, linkpath) == 0); + return 1; +} + +static int +fio_eio_symlink(struct lua_State *L) +{ + if (lua_gettop(L) < 2) + luaL_error(L, "Usage: fio.symlink(target, linkpath)"); + const char *target = lua_tostring(L, 1); + const char *linkpath = lua_tostring(L, 2); + lua_pushboolean(L, dfio_symlink(target, linkpath) == 0); + return 1; +} + +static int +fio_eio_readlink(struct lua_State *L) +{ + if (lua_gettop(L) < 1) + luaL_error(L, "Usage: fio.readlink(pathname)"); + static __thread char path[PATH_MAX]; + const char *pathname = lua_tostring(L, 1); + int res = dfio_readlink(pathname, path, sizeof(path)); + if (res < 0) { + lua_pushnil(L); + return 1; + } + lua_pushlstring(L, path, res); + return 1; +} + + +static int +fio_eio_tempdir(struct lua_State *L) +{ + const char *path = dfio_tempdir(); + if (path) + lua_pushstring(L, path); + else + lua_pushnil(L); + return 1; +} + + +static int +fio_eio_fsync(struct lua_State *L) +{ + int fd = lua_tointeger(L, 1); + lua_pushboolean(L, dfio_fsync(fd) == 0); + return 1; +} + +static int +fio_eio_fdatasync(struct lua_State *L) +{ + int fd = lua_tointeger(L, 1); + lua_pushboolean(L, dfio_fdatasync(fd) == 0); + return 1; +} + +static int +fio_eio_sync(struct lua_State *L) +{ + lua_pushboolean(L, dfio_sync() == 0); + return 1; +} + + +static int +fio_eio_close(struct lua_State *L) +{ + int fd = lua_tointeger(L, 1); + lua_pushboolean(L, dfio_close(fd) == 0); + return 1; +} + +#define ADD_CONST(c) { \ + lua_pushliteral(L, # c); \ + lua_pushinteger(L, c); \ + lua_settable(L, -3); \ + } + + + + + void fio_lua_init(struct lua_State *L) { static const struct luaL_Reg fio_methods[] = { - { "stat", fio_lua_lstat }, - { "fstat", fio_lua_fstat }, - { "glob", fio_lua_glob }, + { "lstat", fio_eio_lstat }, + { "stat", fio_eio_stat }, + { "mkdir", fio_eio_mkdir }, + { "rmdir", fio_eio_rmdir }, + { "glob", fio_eio_glob }, + { "link", fio_eio_link }, + { "symlink", fio_eio_symlink }, + { "readlink", fio_eio_readlink }, + { "unlink", fio_eio_unlink }, + { "rename", fio_eio_rename }, + { "chown", fio_eio_chown }, + { "chmod", fio_eio_chmod }, + { "truncate", fio_eio_truncate }, + { "tempdir", fio_eio_tempdir }, + { "sync", fio_eio_sync }, + { NULL, NULL } }; @@ -240,8 +550,18 @@ fio_lua_init(struct lua_State *L) lua_newtable(L); static const struct luaL_Reg internal_methods[] = { { "open", fio_eio_open }, + { "close", fio_eio_close }, { "pwrite", fio_eio_pwrite }, { "pread", fio_eio_pread }, + { "read", fio_eio_read }, + { "write", fio_eio_write }, + { "lseek", fio_eio_lseek }, + { "ftruncate", fio_eio_ftruncate }, + { "fsync", fio_eio_fsync }, + { "fdatasync", fio_eio_fdatasync }, + + { "fstat", fio_eio_fstat }, + { NULL, NULL } }; luaL_register(L, NULL, internal_methods); diff --git a/src/lua/fio.lua b/src/lua/fio.lua index 51b240461a7c99dd9be69d191eed7712f956deb5..6224dc04e5718dfe5f600d906f76464d8392076a 100644 --- a/src/lua/fio.lua +++ b/src/lua/fio.lua @@ -1,34 +1,5 @@ -local ffi = require 'ffi' -local log = require 'log' - local fio = {} -ffi.cdef[[ - - typedef ssize_t off_t; - - int open(const char *pathname, int flags, int mode); - int unlink(const char *pathname); - int close(int fd); - - int fsync(int fd); - int fdatasync(int fd); - ssize_t readlink(const char *pathname, char *buf, size_t bufsiz); - int symlink(const char *target, const char *linkpath); - int link(const char *oldpath, const char *newpath); - int rename(const char *oldpath, const char *newpath); - off_t lseek(int fd, off_t offset, int whence); - - ssize_t read(int fd, void *buf, size_t count); - ssize_t write(int fd, const void *buf, size_t count); - - int truncate(const char *path, off_t length); - int ftruncate(int fd, off_t length); -]] - -local bsize = 4096 -local buffer = ffi.new('char[?]', bsize) - local function sprintf(fmt, ...) if select('#', ...) == 0 then return fmt @@ -40,29 +11,26 @@ local fio_methods = {} fio_methods.read = function(self, size) - local b = ffi.new('char[?]', size) - if b == nil then - return nil - end - local res = ffi.C.read(self.fh, b, size) - if res < 0 then - return nil + if size == nil then + return '' end - return ffi.string(b, res) + + return fio.internal.read(self.fh, tonumber(size)) end fio_methods.write = function(self, data) data = tostring(data) - local res = ffi.C.write(self.fh, data, #data) --- local res = fio.internal.write(self.fh, data, #data) + local res = fio.internal.write(self.fh, data, #data) return res >= 0 end -fio_methods.pwrite = function(self, data, len, offset) +fio_methods.pwrite = function(self, data, offset) data = tostring(data) - if len == nil then - len = #data + local len = #data + if len == 0 then + return true end + if offset == nil then offset = 0 else @@ -86,11 +54,10 @@ end fio_methods.truncate = function(self, length) - local res = ffi.C.ftruncate(self.fh, length) - if res < 0 then - return nil + if length == nil then + length = 0 end - return true + return fio.internal.ftruncate(self.fh, length) end fio_methods.seek = function(self, offset, whence) @@ -106,7 +73,7 @@ fio_methods.seek = function(self, offset, whence) whence = tonumber(whence) end - local res = ffi.C.lseek(self.fh, tonumber(offset), whence) + local res = fio.internal.lseek(self.fh, tonumber(offset), whence) if res < 0 then return nil @@ -115,34 +82,20 @@ fio_methods.seek = function(self, offset, whence) end fio_methods.close = function(self) - local res = ffi.C.close(self.fh) - if res < 0 then - return false - end - return true + return fio.internal.close(self.fh) end fio_methods.fsync = function(self) - local res = ffi.C.fsync(self.fh) - - if res < 0 then - return false - end - return true + return fio.internal.fsync(self.fh) end fio_methods.fdatasync = function(self) - local res = ffi.C.fdatasync(self.fh) - - if res < 0 then - return false - end - return true + return fio.internal.fdatasync(self.fh) end fio_methods.stat = function(self) - return fio.fstat(self.fh) + return fio.internal.fstat(self.fh) end @@ -182,9 +135,6 @@ fio.open = function(path, flags, mode) end end - log.info("File: %s flag: %s, mode: %s", path, iflag, imode) - - local fh = fio.internal.open(tostring(path), iflag, imode) if fh < 0 then return nil @@ -195,53 +145,59 @@ fio.open = function(path, flags, mode) return fh end -fio.readlink = function(path) - local res = ffi.C.readlink(tostring(path), buffer, bsize) - if res < 0 then - return nil +fio.pathjoin = function(path, ...) + path = tostring(path) + if path == nil or path == '' then + error("Empty path part") end - return ffi.string(buffer, res) -end + for i = 1, select('#', ...) do + if string.match(path, '/$') ~= nil then + path = string.gsub(path, '/$', '') + end -fio.symlink = function(path, linkpath) - local res = ffi.C.symlink(tostring(path), tostring(linkpath)) - if res < 0 then - return false + local sp = select(i, ...) + if sp == nil then + error("Undefined path part") + end + if sp == '' or sp == '/' then + error("Empty path part") + end + if string.match(sp, '^/') ~= nil then + sp = string.gsub(sp, '^/', '') + end + if sp ~= '' then + path = path .. '/' .. sp + end end - return true -end - -fio.link = function(path, newpath) - local res = ffi.C.link(tostring(path), tostring(linkpath)) - if res < 0 then - return false + if string.match(path, '/$') ~= nil and #path > 1 then + path = string.gsub(path, '/$', '') end - return true -end -fio.unlink = function(path) - local res = ffi.C.unlink(tostring(path)) - if res == 0 then - return true - end - return false + return path end -fio.rename = function(path, newpath) - local res = ffi.C.rename(path, newpath) - if res < 0 then - return false + +fio.basename = function(path, suffix) + if path == nil then + return nil end - return true -end + path = tostring(path) + path = string.gsub(path, '.*/', '') -fio.truncate = function(path, length) - local res = ffi.C.truncate(tostring(path), tonumber(length)) - if res < 0 then - return false + if suffix ~= nil then + suffix = tostring(suffix) + if #suffix > 0 then + suffix = string.gsub(suffix, '(.)', '[%1]') + path = string.gsub(path, suffix, '') + end end - return true + + return path end + + + + return fio diff --git a/test/box/fio.result b/test/box/fio.result index ecea3b6d6da3a66095dc52cb1d4f9aa36f9ac54e..3618943e97f6a6c1aec9d9093f5d8e19f00b34a9 100644 --- a/test/box/fio.result +++ b/test/box/fio.result @@ -4,143 +4,276 @@ fio = require 'fio' errno = require 'errno' --- ... -fh1 = fio.open("/tmp/tarantool-test.fio.1", { 'O_RDWR', 'O_TRUNC', 'O_CREAT' }, { 'S_IRUSR', 'S_IWUSR' }) +-- pathjoin +fio.pathjoin('abc', 'cde') --- +- abc/cde ... -fh2 = fio.open("/tmp/tarantool-test.fio.2", { 'O_RDWR', 'O_TRUNC', 'O_CREAT' }, { 'S_IRUSR', 'S_IWUSR' }) +fio.pathjoin('/', 'abc') --- +- /abc ... -type(fh1) +fio.pathjoin('abc/', '/cde') --- -- table +- abc/cde ... -type(fh2) +fio.pathjoin('/', '/cde') --- -- table +- /cde ... -fh1:seek(123) +-- basename +fio.basename('/') --- -- 123 +- ... -fh1:write('Hello, world') +fio.basename('abc') +--- +- abc +... +fio.basename('abc.cde', '.cde') +--- +- abc +... +fio.basename('abc^cde', '.cde') +--- +- abc^cde +... +fio.basename('/path/to/file.cde', '.cde') +--- +- file +... +-- other tests +tmpdir = fio.tempdir() +--- +... +file1 = fio.pathjoin(tmpdir, 'file.1') +--- +... +file2 = fio.pathjoin(tmpdir, 'file.2') +--- +... +file3 = fio.pathjoin(tmpdir, 'file.3') +--- +... +file4 = fio.pathjoin(tmpdir, 'file.4') +--- +... +fh1 = fio.open(file1, { 'O_RDWR', 'O_TRUNC', 'O_CREAT' }, 0777) +--- +... +fh1 ~= nil --- - true ... -fh1:fdatasync() +fh1:stat().size +--- +- 0 +... +fh1:seek(121) +--- +- 121 +... +fh1:stat().size +--- +- 0 +... +fh1:write("Hello, world") --- - true ... +fh1:stat().size +--- +- 133 +... fh1:fsync() --- - true ... -fio.stat("/tmp/tarantool-test.fio.1").size +fh1:fdatasync() --- -- 135 +- true ... -fh1:seek(123) +fio.sync() --- -- 123 +- true ... -fh1:read(500) +fh1:pread(512, 121) --- - Hello, world ... -fh1:truncate(128) +fh1:pread(5, 121) +--- +- Hello +... +fh1:write("; Ehllo, again") --- - true ... -fh1:seek(123) +fh1:seek(121) --- -- 123 +- 121 ... -fh1:read(3) +fh1:read(13) --- -- Hel +- Hello, world; ... -fh1:read(500) +fh1:read(512) --- -- lo +- ' Ehllo, again' ... -fh1:seek(123) +fh1:pread(512, 14 + 121) --- -- 123 +- Ehllo, again ... -fio.truncate("/tmp/tarantool-test.fio.1", 127) +fh1:pwrite("He", 14 + 121) --- - true ... -fio.stat("/tmp/tarantool-test.fio.1").size +fh1:pread(512, 14 + 121) --- -- 127 +- Hello, again ... -fh1:stat().size +{ fh1:stat().size, fio.stat(file1).size } +--- +- - 147 + - 147 +... +fh1:seek(121) --- -- 127 +- 121 ... -fh1:read(500) +fh1:read(512) --- -- Hell +- Hello, world; Hello, again ... -fh1:close() +fio.link(file1, file2) --- - true ... -fh1:close() +glob = fio.glob(fio.pathjoin(tmpdir, '*')) --- -- false ... -fh2:close() +#glob +--- +- 2 +... +{ string.match(glob[1], '^.*/(.*)'), string.match(glob[2], '^.*/(.*)') } +--- +- - file.1 + - file.2 +... +fio.stat(file1).inode == fio.stat(file2).inode --- - true ... -fio.symlink("/tmp/tarantool-test.fio.1", "/tmp/tarantool-test.fio.3") +fh3 = fio.open(file3, { 'O_RDWR', 'O_TRUNC', 'O_CREAT' }, 0x1FD) +--- +... +fh1:stat().inode ~= fh3:stat().inode --- - true ... -fio.readlink("/tmp/tarantool-test.fio.3") +0775 --- -- /tmp/tarantool-test.fio.1 +- 775 ... -fio.symlink("/tmp/tarantool-test.fio.1", "/tmp/tarantool-test.fio.3") +bit.band(fh3:stat().mode, 0x1FF) == 0x1FD --- -- false +- true ... -errno.strerror(errno()) +fh3:write("abc") --- -- File exists +- true +... +fio.rename(file3, file4) +--- +- true ... -fio.rename("/tmp/tarantool-test.fio.3", "/tmp/tarantool-test.fio.4") +fio.symlink(file4, file3) --- - true ... -fio.glob("/tmp/tarantool-test.fio.[1-4]") +fio.stat(file3).size --- -- - /tmp/tarantool-test.fio.1 - - /tmp/tarantool-test.fio.2 - - /tmp/tarantool-test.fio.4 +- 3 ... -fio.unlink("/tmp/tarantool-test.fio.1") +fio.lstat(file3).size ~= fio.stat(file3).size --- - true ... -fio.unlink("/tmp/tarantool-test.fio.2") +fio.lstat(file3).mode ~= fio.stat(file3).mode --- - true ... -fio.unlink("/tmp/tarantool-test.fio.3") +fio.basename(fio.readlink(file3)) --- -- false +- file.4 +... +bit.band(fio.stat(file4).mode, 0x1FF) == 0x1FD +--- +- true +... +fio.chmod(file4, 0x1F8) -- 0x770 +--- +- true ... -fio.unlink("/tmp/tarantool-test.fio.4") +bit.band(fh3:stat().mode, 0x1FF) == 0x1F8 --- - true ... -fio.stat("/tmp/tarantool-test.fio.1") +bit.band(fio.stat(file4).mode, 0x1FF) == 0x1F8 --- -- null +- true ... -fio.glob("/tmp/tarantool-test.fio.[12]") +fio.mkdir(fio.pathjoin(tmpdir, "dir")) --- -- [] +- true +... +-- cleanup directories +{ fh1:close(), fh3:close() } +--- +- - true + - true +... +{ fh1:close(), errno.strerror(), fh3:close(), errno.strerror() } +--- +- - false + - Bad file descriptor + - false + - Bad file descriptor +... +fio.rmdir(fio.pathjoin(tmpdir, "dir")) +--- +- true +... +{ fio.unlink(file1), fio.unlink(file2), fio.unlink(file3), fio.unlink(file4) } +--- +- - true + - true + - true + - true +... +{ fio.unlink(file1), fio.unlink(file2), fio.unlink(file3), fio.unlink(file4) } +--- +- - false + - false + - false + - false +... +fio.rmdir(tmpdir) +--- +- true +... +{ fio.rmdir(tmpdir), errno.strerror() } +--- +- - false + - No such file or directory +... +fio.unlink() +--- +- error: 'Usage: fio.unlink(pathname)' +... +fio.unlink(nil) +--- +- false ... diff --git a/test/box/fio.test.lua b/test/box/fio.test.lua index 09c625603cacb429835f26237877ab6287a54863..71ce13ef0cf91d489c9d31c67d0816e4032862f3 100644 --- a/test/box/fio.test.lua +++ b/test/box/fio.test.lua @@ -1,56 +1,98 @@ fio = require 'fio' errno = require 'errno' -fh1 = fio.open("/tmp/tarantool-test.fio.1", { 'O_RDWR', 'O_TRUNC', 'O_CREAT' }, { 'S_IRUSR', 'S_IWUSR' }) -fh2 = fio.open("/tmp/tarantool-test.fio.2", { 'O_RDWR', 'O_TRUNC', 'O_CREAT' }, { 'S_IRUSR', 'S_IWUSR' }) +-- pathjoin -type(fh1) -type(fh2) +fio.pathjoin('abc', 'cde') +fio.pathjoin('/', 'abc') +fio.pathjoin('abc/', '/cde') +fio.pathjoin('/', '/cde') + +-- basename + +fio.basename('/') +fio.basename('abc') +fio.basename('abc.cde', '.cde') +fio.basename('abc^cde', '.cde') +fio.basename('/path/to/file.cde', '.cde') -fh1:seek(123) -fh1:write('Hello, world') -fh1:fdatasync() -fh1:fsync() -fio.stat("/tmp/tarantool-test.fio.1").size +-- other tests -fh1:seek(123) -fh1:read(500) +tmpdir = fio.tempdir() -fh1:truncate(128) -fh1:seek(123) -fh1:read(3) -fh1:read(500) +file1 = fio.pathjoin(tmpdir, 'file.1') +file2 = fio.pathjoin(tmpdir, 'file.2') +file3 = fio.pathjoin(tmpdir, 'file.3') +file4 = fio.pathjoin(tmpdir, 'file.4') -fh1:seek(123) -fio.truncate("/tmp/tarantool-test.fio.1", 127) -fio.stat("/tmp/tarantool-test.fio.1").size + +fh1 = fio.open(file1, { 'O_RDWR', 'O_TRUNC', 'O_CREAT' }, 0777) +fh1 ~= nil +fh1:stat().size +fh1:seek(121) +fh1:stat().size +fh1:write("Hello, world") fh1:stat().size -fh1:read(500) +fh1:fsync() +fh1:fdatasync() +fio.sync() +fh1:pread(512, 121) +fh1:pread(5, 121) + +fh1:write("; Ehllo, again") +fh1:seek(121) +fh1:read(13) +fh1:read(512) +fh1:pread(512, 14 + 121) +fh1:pwrite("He", 14 + 121) +fh1:pread(512, 14 + 121) + +{ fh1:stat().size, fio.stat(file1).size } +fh1:seek(121) +fh1:read(512) + +fio.link(file1, file2) + +glob = fio.glob(fio.pathjoin(tmpdir, '*')) +#glob +{ string.match(glob[1], '^.*/(.*)'), string.match(glob[2], '^.*/(.*)') } +fio.stat(file1).inode == fio.stat(file2).inode + +fh3 = fio.open(file3, { 'O_RDWR', 'O_TRUNC', 'O_CREAT' }, 0x1FD) +fh1:stat().inode ~= fh3:stat().inode +0775 +bit.band(fh3:stat().mode, 0x1FF) == 0x1FD +fh3:write("abc") +fio.rename(file3, file4) +fio.symlink(file4, file3) +fio.stat(file3).size +fio.lstat(file3).size ~= fio.stat(file3).size +fio.lstat(file3).mode ~= fio.stat(file3).mode +fio.basename(fio.readlink(file3)) -fh1:close() -fh1:close() -fh2:close() +bit.band(fio.stat(file4).mode, 0x1FF) == 0x1FD +fio.chmod(file4, 0x1F8) -- 0x770 +bit.band(fh3:stat().mode, 0x1FF) == 0x1F8 +bit.band(fio.stat(file4).mode, 0x1FF) == 0x1F8 -fio.symlink("/tmp/tarantool-test.fio.1", "/tmp/tarantool-test.fio.3") -fio.readlink("/tmp/tarantool-test.fio.3") -fio.symlink("/tmp/tarantool-test.fio.1", "/tmp/tarantool-test.fio.3") -errno.strerror(errno()) -fio.rename("/tmp/tarantool-test.fio.3", "/tmp/tarantool-test.fio.4") -fio.glob("/tmp/tarantool-test.fio.[1-4]") +fio.mkdir(fio.pathjoin(tmpdir, "dir")) -fio.unlink("/tmp/tarantool-test.fio.1") -fio.unlink("/tmp/tarantool-test.fio.2") -fio.unlink("/tmp/tarantool-test.fio.3") -fio.unlink("/tmp/tarantool-test.fio.4") +-- cleanup directories -fio.stat("/tmp/tarantool-test.fio.1") +{ fh1:close(), fh3:close() } +{ fh1:close(), errno.strerror(), fh3:close(), errno.strerror() } -fio.glob("/tmp/tarantool-test.fio.[12]") +fio.rmdir(fio.pathjoin(tmpdir, "dir")) +{ fio.unlink(file1), fio.unlink(file2), fio.unlink(file3), fio.unlink(file4) } +{ fio.unlink(file1), fio.unlink(file2), fio.unlink(file3), fio.unlink(file4) } +fio.rmdir(tmpdir) +{ fio.rmdir(tmpdir), errno.strerror() } -fio +fio.unlink() +fio.unlink(nil)