diff --git a/extra/dist.lua b/extra/dist.lua
new file mode 100644
index 0000000000000000000000000000000000000000..851f32fc69b5bc4230f7a33d1f3f5a862039be38
--- /dev/null
+++ b/extra/dist.lua
@@ -0,0 +1,29 @@
+#!env tarantool
+
+local ffi  = require 'ffi'
+local yaml = require 'yaml'
+
+ffi.cdef[[
+    int access(const char *path, int amode);
+]]
+
+local function file_exists(name)
+    
+
+end
+
+
+
+local dist_cfg = {
+    snap_dir = '/usr/lib/tarantool',
+    xlog_dir = '/usr/lib/tarantool',
+    log_dir  = '/var/log/tarantool',
+    pid_dir  = '/var/run/tarantool',
+    username = 'tarantool'
+}
+
+
+
+
+
+print(yaml.encode({arg, dist_cfg }))
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 71c82e68f54c8024669db10032d5fb11a2091c80..85f5254a4db824ff6e5590a51bc3b0849469cc0f 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -27,6 +27,7 @@ lua_source(lua_sources lua/log.lua)
 lua_source(lua_sources lua/box_net_box.lua)
 lua_source(lua_sources lua/help.lua)
 lua_source(lua_sources lua/tap.lua)
+lua_source(lua_sources lua/fio.lua)
 file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/third_party/luafun)
 lua_source(lua_sources ../third_party/luafun/fun.lua)
 
@@ -75,6 +76,7 @@ set (common_sources
      tt_uuid.c
      ffisyms.cc
      uri.cc
+     disk_fiber_io.cc
      lua/init.cc
      lua/fiber.cc
      lua/trigger.cc
@@ -87,6 +89,7 @@ set (common_sources
      lua/errno.c
      lua/bsdsocket.cc
      lua/pickle.cc
+     lua/fio.cc
      ${lua_sources}
 )
 
diff --git a/src/disk_fiber_io.cc b/src/disk_fiber_io.cc
new file mode 100644
index 0000000000000000000000000000000000000000..cd064e628b1ee30d474493a8c5fc2c007541dccb
--- /dev/null
+++ b/src/disk_fiber_io.cc
@@ -0,0 +1,98 @@
+/*
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <disk_fiber_io.h>
+#include <coeio.h>
+#include <fiber.h>
+
+struct fiber_eio {
+	ssize_t result;
+	int errorno;
+	struct fiber *fiber;
+	bool done;
+};
+
+
+int
+dfio_complete(eio_req *req)
+{
+	struct fiber_eio *fio = (struct fiber_eio *)req->data;
+
+	fio->result = req->result;
+	fio->errorno = req->errorno;
+	fio->done = true;
+
+	fiber_wakeup(fio->fiber);
+	return 0;
+}
+
+static ssize_t
+dfio_wait_done(eio_req *req, struct fiber_eio *eio)
+{
+	if (!req) {
+		errno = ENOMEM;
+		return -1;
+	}
+
+	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 };
+
+	eio_req *req = eio_open(path, flags, mode, 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);
+	eio_req *req = eio_write(fd,
+		(void *)buf, count, offset, 0, dfio_complete, &eio);
+	return dfio_wait_done(req, &eio);
+}
+
+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);
+	eio_req *req = eio_read(fd, buf, count,
+		offset, 0, dfio_complete, &eio);
+	return dfio_wait_done(req, &eio);
+}
diff --git a/src/disk_fiber_io.h b/src/disk_fiber_io.h
new file mode 100644
index 0000000000000000000000000000000000000000..74121ae6ade5de0f41cec5f7cf5e370caaa4ea8c
--- /dev/null
+++ b/src/disk_fiber_io.h
@@ -0,0 +1,40 @@
+#ifndef INCLUDES_TARANTOOL_LUA_DISK_FIBER_IO_H
+#define INCLUDES_TARANTOOL_LUA_DISK_FIBER_IO_H
+/*
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+
+int dfio_open(const char *path, int flags, mode_t mode);
+
+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);
+
+
+#endif /* INCLUDES_TARANTOOL_LUA_DISK_FIBER_IO_H */
diff --git a/src/lua/fio.cc b/src/lua/fio.cc
new file mode 100644
index 0000000000000000000000000000000000000000..631b748c6eca23588091c31dd4f81a8a6c5f09ef
--- /dev/null
+++ b/src/lua/fio.cc
@@ -0,0 +1,346 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <glob.h>
+#include <time.h>
+#include "lua/fio.h"
+#include <coeio.h>
+#include <fiber.h>
+
+extern "C" {
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+}
+#include "lua/utils.h"
+
+#include <disk_fiber_io.h>
+
+
+
+static int
+fio_eio_open(struct lua_State *L)
+{
+	const char *path = lua_tostring(L, 1);
+	int flags = lua_tointeger(L, 2);
+	int mode = lua_tointeger(L, 3);
+
+	int fh = dfio_open(path, flags, mode);
+	lua_pushinteger(L, fh);
+	return 1;
+}
+
+
+static int
+fio_eio_pwrite(struct lua_State *L)
+{
+	int fh = lua_tointeger(L, 1);
+	const char *buf = lua_tostring(L, 2);
+	size_t len = lua_tonumber(L, 3);
+	size_t offset = lua_tonumber(L, 4);
+
+	int res = dfio_pwrite(fh, buf, len, offset);
+	lua_pushinteger(L, res);
+	return 1;
+}
+
+
+static int
+fio_eio_pread(struct lua_State *L)
+{
+	int fh = lua_tointeger(L, 1);
+	size_t len = lua_tonumber(L, 2);
+	size_t offset = lua_tonumber(L, 3);
+
+	if (!len) {
+		lua_pushliteral(L, "");
+		return 1;
+	}
+
+	/* allocate buffer at lua stack */
+	void *buf = lua_newuserdata(L, len);
+	if (!buf) {
+		errno = ENOMEM;
+		lua_pushnil(L);
+		return 1;
+	}
+
+
+	int res = dfio_pread(fh, buf, len, offset);
+
+	if (res < 0) {
+		lua_pop(L, 1);
+		lua_pushnil(L);
+		return 1;
+	}
+	lua_pushlstring(L, (char *)buf, res);
+	lua_remove(L, -2);
+	return 1;
+}
+
+
+
+#define ADD_CONST(c)	{			\
+		lua_pushliteral(L, # c);	\
+		lua_pushinteger(L, c);		\
+		lua_settable(L, -3);		\
+	}
+
+
+static int
+fio_lua_glob(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);
+
+	glob_t globbuf;
+	switch (glob(p, 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
+lua_pushtimespec(struct lua_State *L, const struct timespec *ts)
+{
+	double nsec = ts->tv_nsec;
+	nsec /= 1000000000;
+	lua_pushnumber(L, ts->tv_sec + nsec);
+	return 1;
+}
+
+static int
+lua_pushstat(struct lua_State *L, const struct stat *stat)
+{
+	lua_newtable(L);
+
+	lua_pushliteral(L, "dev");
+	lua_pushinteger(L, stat->st_dev);
+	lua_settable(L, -3);
+
+	lua_pushliteral(L, "inode");
+	lua_pushinteger(L, stat->st_ino);
+	lua_settable(L, -3);
+
+	lua_pushliteral(L, "mode");
+	lua_pushinteger(L, stat->st_mode);
+	lua_settable(L, -3);
+
+	lua_pushliteral(L, "nlink");
+	lua_pushinteger(L, stat->st_nlink);
+	lua_settable(L, -3);
+
+	lua_pushliteral(L, "uid");
+	lua_pushinteger(L, stat->st_uid);
+	lua_settable(L, -3);
+
+	lua_pushliteral(L, "gid");
+	lua_pushinteger(L, stat->st_gid);
+	lua_settable(L, -3);
+
+	lua_pushliteral(L, "rdev");
+	lua_pushinteger(L, stat->st_rdev);
+	lua_settable(L, -3);
+
+	lua_pushliteral(L, "size");
+	lua_pushinteger(L, stat->st_size);
+	lua_settable(L, -3);
+
+	lua_pushliteral(L, "blksize");
+	lua_pushinteger(L, stat->st_blksize);
+	lua_settable(L, -3);
+
+	lua_pushliteral(L, "blocks");
+	lua_pushinteger(L, stat->st_blocks);
+	lua_settable(L, -3);
+
+
+	lua_pushliteral(L, "ctime");
+	lua_pushtimespec(L, &stat->st_ctim);
+	lua_settable(L, -3);
+
+	lua_pushliteral(L, "mtime");
+	lua_pushtimespec(L, &stat->st_mtim);
+	lua_settable(L, -3);
+
+	lua_pushliteral(L, "atime");
+	lua_pushtimespec(L, &stat->st_atim);
+	lua_settable(L, -3);
+
+	return 1;
+}
+
+static int
+fio_lua_lstat(struct lua_State *L)
+{
+	if (lua_gettop(L) < 1 || !lua_isstring(L, 1))
+		luaL_error(L, "Usage: fio.stat(pathname)");
+	struct stat stat;
+	if (lstat(lua_tostring(L, 1), &stat) < 0) {
+		lua_pushnil(L);
+		return 1;
+	}
+	return lua_pushstat(L, &stat);
+}
+
+
+static int
+fio_lua_fstat(struct lua_State *L)
+{
+	if (lua_gettop(L) < 1 || !lua_isnumber(L, 1))
+		luaL_error(L, "Usage: fio.fstat(fd)");
+	struct stat stat;
+	if (fstat(lua_tointeger(L, 1), &stat) < 0) {
+		lua_pushnil(L);
+		return 1;
+	}
+	return lua_pushstat(L, &stat);
+}
+
+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			},
+		{ NULL,			NULL				}
+	};
+
+	luaL_register_module(L, "fio", fio_methods);
+
+
+
+	/* internal table */
+	lua_pushliteral(L, "internal");
+	lua_newtable(L);
+	static const struct luaL_Reg internal_methods[] = {
+		{ "open",		fio_eio_open			},
+		{ "pwrite",		fio_eio_pwrite			},
+		{ "pread",		fio_eio_pread			},
+		{ NULL,			NULL				}
+	};
+	luaL_register(L, NULL, internal_methods);
+	lua_settable(L, -3);
+
+
+	lua_pushliteral(L, "c");
+	lua_newtable(L);
+
+
+	lua_pushliteral(L, "flag");
+	lua_newtable(L);
+	#ifdef O_APPEND
+		ADD_CONST(O_APPEND)
+	#endif
+	#ifdef O_ASYNC
+		ADD_CONST(O_ASYNC)
+	#endif
+	#ifdef O_CLOEXEC
+		ADD_CONST(O_CLOEXEC)
+	#endif
+	#ifdef O_CREAT
+		ADD_CONST(O_CREAT)
+	#endif
+	#ifdef O_DIRECT
+		ADD_CONST(O_DIRECT)
+	#endif
+	#ifdef O_DIRECTORY
+		ADD_CONST(O_DIRECTORY)
+	#endif
+	#ifdef O_EXCL
+		ADD_CONST(O_EXCL)
+	#endif
+	#ifdef O_LARGEFILE
+		ADD_CONST(O_LARGEFILE)
+	#endif
+	#ifdef O_NOATIME
+		ADD_CONST(O_NOATIME)
+	#endif
+	#ifdef O_NOCTTY
+		ADD_CONST(O_NOCTTY)
+	#endif
+	#ifdef O_NOFOLLOW
+		ADD_CONST(O_NOFOLLOW)
+	#endif
+	#ifdef O_NONBLOCK
+		ADD_CONST(O_NONBLOCK)
+	#endif
+	#ifdef O_NDELAY
+		ADD_CONST(O_NDELAY)
+	#endif
+	#ifdef O_PATH
+		ADD_CONST(O_PATH)
+	#endif
+	#ifdef O_SYNC
+		ADD_CONST(O_SYNC)
+	#endif
+	#ifdef O_TMPFILE
+		ADD_CONST(O_TMPFILE)
+	#endif
+	#ifdef O_TRUNC
+		ADD_CONST(O_TRUNC)
+	#endif
+	ADD_CONST(O_RDONLY);
+	ADD_CONST(O_WRONLY);
+	ADD_CONST(O_RDWR);
+	lua_settable(L, -3);
+
+	lua_pushliteral(L, "mode");
+	lua_newtable(L);
+	ADD_CONST(S_IRWXU);
+	ADD_CONST(S_IRUSR);
+	ADD_CONST(S_IWUSR);
+	ADD_CONST(S_IXUSR);
+	ADD_CONST(S_IRWXG);
+	ADD_CONST(S_IRGRP);
+	ADD_CONST(S_IWGRP);
+	ADD_CONST(S_IXGRP);
+	ADD_CONST(S_IRWXO);
+	ADD_CONST(S_IROTH);
+	ADD_CONST(S_IWOTH);
+	ADD_CONST(S_IXOTH);
+	lua_settable(L, -3);
+
+
+	lua_pushliteral(L, "seek");
+	lua_newtable(L);
+	ADD_CONST(SEEK_SET);
+	ADD_CONST(SEEK_CUR);
+	ADD_CONST(SEEK_END);
+	#ifdef SEEK_DATA
+		ADD_CONST(SEEK_DATA);
+	#endif
+	#ifdef SEEK_HOLE
+		ADD_CONST(SEEK_HOLE);
+	#endif
+	lua_settable(L, -3);
+
+
+	lua_settable(L, -3);
+	lua_pop(L, 1);
+}
diff --git a/src/lua/fio.h b/src/lua/fio.h
new file mode 100644
index 0000000000000000000000000000000000000000..e1053551916a4de92b7c23f63dc644ddcdbe4c64
--- /dev/null
+++ b/src/lua/fio.h
@@ -0,0 +1,35 @@
+#ifndef INCLUDES_TARANTOOL_LUA_FIO_H
+#define INCLUDES_TARANTOOL_LUA_FIO_H
+/*
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+struct lua_State;
+void fio_lua_init(struct lua_State *L);
+
+#endif /* INCLUDES_TARANTOOL_LUA_FIO_H */
diff --git a/src/lua/fio.lua b/src/lua/fio.lua
new file mode 100644
index 0000000000000000000000000000000000000000..51b240461a7c99dd9be69d191eed7712f956deb5
--- /dev/null
+++ b/src/lua/fio.lua
@@ -0,0 +1,247 @@
+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
+    end
+    return string.format(fmt, ...)
+end
+
+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
+    end
+    return ffi.string(b, res)
+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)
+    return res >= 0
+end
+
+fio_methods.pwrite = function(self, data, len, offset)
+    data = tostring(data)
+    if len == nil then
+        len = #data
+    end
+    if offset == nil then
+        offset = 0
+    else
+        offset = tonumber(offset)
+    end
+
+    local res = fio.internal.pwrite(self.fh, data, len, offset)
+    return res >= 0
+end
+
+fio_methods.pread = function(self, len, offset)
+    if len == nil then
+        return ''
+    end
+    if offset == nil then
+        offset = 0
+    end
+
+    return fio.internal.pread(self.fh, tonumber(len), tonumber(offset))
+end
+
+
+fio_methods.truncate = function(self, length)
+    local res = ffi.C.ftruncate(self.fh, length)
+    if res < 0 then
+        return nil
+    end
+    return true
+end
+
+fio_methods.seek = function(self, offset, whence)
+    if whence == nil then
+        whence = 'SEEK_SET'
+    end
+    if type(whence) == 'string' then
+        if fio.c.seek[whence] == nil then
+            error(sprintf("Unknown whence: %s", whence))
+        end
+        whence = fio.c.seek[whence]
+    else
+        whence = tonumber(whence)
+    end
+
+    local res = ffi.C.lseek(self.fh, tonumber(offset), whence)
+
+    if res < 0 then
+        return nil
+    end
+    return tonumber(res)
+end
+
+fio_methods.close = function(self)
+    local res = ffi.C.close(self.fh)
+    if res < 0 then
+        return false
+    end
+    return true
+end
+
+fio_methods.fsync = function(self)
+    local res = ffi.C.fsync(self.fh)
+
+    if res < 0 then
+        return false
+    end
+    return true
+end
+
+fio_methods.fdatasync = function(self)
+    local res = ffi.C.fdatasync(self.fh)
+
+    if res < 0 then
+        return false
+    end
+    return true
+end
+
+
+fio_methods.stat = function(self)
+    return fio.fstat(self.fh)
+end
+
+
+local fio_mt = { __index = fio_methods }
+
+fio.open = function(path, flags, mode)
+    local iflag = 0
+    local imode = 0
+
+    if type(flags) ~= 'table' then
+        flags = { flags }
+    end
+    if type(mode) ~= 'table' then
+        mode = { mode }
+    end
+
+
+    for _, flag in pairs(flags) do
+        if type(flag) == 'number' then
+            iflag = bit.bor(iflag, flag)
+        else
+            if fio.c.flag[ flag ] == nil then
+                error(sprintf("Unknown flag: %s", flag))
+            end
+            iflag = bit.bor(iflag, fio.c.flag[ flag ])
+        end
+    end
+
+    for _, m in pairs(mode) do
+        if type(m) == 'string' then
+            if fio.c.mode[m] == nil then
+                error(sprintf("Unknown mode: %s", m))
+            end
+            imode = bit.bor(imode, fio.c.mode[m])
+        else
+            imode = bit.bor(imode, tonumber(m))
+        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
+    end
+
+    fh = { fh = fh }
+    setmetatable(fh, fio_mt)
+    return fh
+end
+
+fio.readlink = function(path)
+    local res = ffi.C.readlink(tostring(path), buffer, bsize)
+    if res < 0 then
+        return nil
+    end
+    return ffi.string(buffer, res)
+end
+
+fio.symlink = function(path, linkpath)
+    local res = ffi.C.symlink(tostring(path), tostring(linkpath))
+    if res < 0 then
+        return false
+    end
+    return true
+end
+
+fio.link = function(path, newpath)
+    local res = ffi.C.link(tostring(path), tostring(linkpath))
+    if res < 0 then
+        return false
+    end
+    return true
+end
+
+fio.unlink = function(path)
+    local res = ffi.C.unlink(tostring(path))
+    if res == 0 then
+        return true
+    end
+    return false
+end
+
+fio.rename = function(path, newpath)
+    local res = ffi.C.rename(path, newpath)
+    if res < 0 then
+        return false
+    end
+    return true
+end
+
+
+fio.truncate = function(path, length)
+    local res = ffi.C.truncate(tostring(path), tonumber(length))
+    if res < 0 then
+        return false
+    end
+    return true
+end
+
+return fio
diff --git a/src/lua/init.cc b/src/lua/init.cc
index 048575cb5b800a01eb9ba3b54974aad49919740f..0a857552238882f57928f4fa88e296e43ace202a 100644
--- a/src/lua/init.cc
+++ b/src/lua/init.cc
@@ -55,6 +55,7 @@ extern "C" {
 #include "lua/yaml.h"
 #include "lua/msgpack.h"
 #include "lua/pickle.h"
+#include "lua/fio.h"
 
 #include <ctype.h>
 #include "small/region.h"
@@ -75,7 +76,8 @@ extern char uuid_lua[],
 	console_lua[],
 	box_net_box_lua[],
 	help_lua[],
-	tap_lua[];
+	tap_lua[],
+	fio_lua[];
 
 static const char *lua_sources[] = {
 	init_lua,
@@ -92,6 +94,7 @@ static const char *lua_modules[] = {
 	"net.box", box_net_box_lua,
 	"console", console_lua,
 	"tap", tap_lua,
+	"fio", fio_lua,
 	NULL
 };
 
@@ -313,6 +316,7 @@ tarantool_lua_init(const char *tarantool_bin, int argc, char **argv)
 	}
 
 	box_lua_init(L);
+	fio_lua_init(L);
 
 
 	lua_newtable(L);
diff --git a/test/box/fio.result b/test/box/fio.result
new file mode 100644
index 0000000000000000000000000000000000000000..ecea3b6d6da3a66095dc52cb1d4f9aa36f9ac54e
--- /dev/null
+++ b/test/box/fio.result
@@ -0,0 +1,146 @@
+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' })
+---
+...
+type(fh1)
+---
+- table
+...
+type(fh2)
+---
+- table
+...
+fh1:seek(123)
+---
+- 123
+...
+fh1:write('Hello, world')
+---
+- true
+...
+fh1:fdatasync()
+---
+- true
+...
+fh1:fsync()
+---
+- true
+...
+fio.stat("/tmp/tarantool-test.fio.1").size
+---
+- 135
+...
+fh1:seek(123)
+---
+- 123
+...
+fh1:read(500)
+---
+- Hello, world
+...
+fh1:truncate(128)
+---
+- true
+...
+fh1:seek(123)
+---
+- 123
+...
+fh1:read(3)
+---
+- Hel
+...
+fh1:read(500)
+---
+- lo
+...
+fh1:seek(123)
+---
+- 123
+...
+fio.truncate("/tmp/tarantool-test.fio.1", 127)
+---
+- true
+...
+fio.stat("/tmp/tarantool-test.fio.1").size
+---
+- 127
+...
+fh1:stat().size
+---
+- 127
+...
+fh1:read(500)
+---
+- Hell
+...
+fh1:close()
+---
+- true
+...
+fh1:close()
+---
+- false
+...
+fh2:close()
+---
+- true
+...
+fio.symlink("/tmp/tarantool-test.fio.1", "/tmp/tarantool-test.fio.3")
+---
+- true
+...
+fio.readlink("/tmp/tarantool-test.fio.3")
+---
+- /tmp/tarantool-test.fio.1
+...
+fio.symlink("/tmp/tarantool-test.fio.1", "/tmp/tarantool-test.fio.3")
+---
+- false
+...
+errno.strerror(errno())
+---
+- File exists
+...
+fio.rename("/tmp/tarantool-test.fio.3", "/tmp/tarantool-test.fio.4")
+---
+- true
+...
+fio.glob("/tmp/tarantool-test.fio.[1-4]")
+---
+- - /tmp/tarantool-test.fio.1
+  - /tmp/tarantool-test.fio.2
+  - /tmp/tarantool-test.fio.4
+...
+fio.unlink("/tmp/tarantool-test.fio.1")
+---
+- true
+...
+fio.unlink("/tmp/tarantool-test.fio.2")
+---
+- true
+...
+fio.unlink("/tmp/tarantool-test.fio.3")
+---
+- false
+...
+fio.unlink("/tmp/tarantool-test.fio.4")
+---
+- true
+...
+fio.stat("/tmp/tarantool-test.fio.1")
+---
+- null
+...
+fio.glob("/tmp/tarantool-test.fio.[12]")
+---
+- []
+...
diff --git a/test/box/fio.test.lua b/test/box/fio.test.lua
new file mode 100644
index 0000000000000000000000000000000000000000..09c625603cacb429835f26237877ab6287a54863
--- /dev/null
+++ b/test/box/fio.test.lua
@@ -0,0 +1,56 @@
+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' })
+
+type(fh1)
+type(fh2)
+
+fh1:seek(123)
+fh1:write('Hello, world')
+
+fh1:fdatasync()
+fh1:fsync()
+
+fio.stat("/tmp/tarantool-test.fio.1").size
+
+fh1:seek(123)
+fh1:read(500)
+
+fh1:truncate(128)
+fh1:seek(123)
+fh1:read(3)
+fh1:read(500)
+
+fh1:seek(123)
+fio.truncate("/tmp/tarantool-test.fio.1", 127)
+fio.stat("/tmp/tarantool-test.fio.1").size
+fh1:stat().size
+fh1:read(500)
+
+
+
+fh1:close()
+fh1:close()
+fh2:close()
+
+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.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")
+
+fio.stat("/tmp/tarantool-test.fio.1")
+
+fio.glob("/tmp/tarantool-test.fio.[12]")
+
+
+fio