15 Commits

Author SHA1 Message Date
Hisham
b1f0f80141 Merge branch 'master' into 2.0 2016-10-19 01:36:11 -04:00
NiteHawk
3c4e563d9c Dynamically size getcwd() buffer in get_dir function (#84)
* Dynamically size getcwd() buffer in get_dir function

This should fix issue 42.

* Fixup: Properly respect NO_GETCWD

* Fixup: Get rid of getcwd_error, handle NO_GETCWD in a single place
2016-10-14 15:46:54 -07:00
Peter Melnichenko
50919ed69f Fix lfs.attributes and lfs.symlinkattributes extra argument handling
When the second argument is not a string, _file_info() wants to
ensure that there is a table on top of the stack: the second argument
or a new table. If a new table is pushed it's created on top immediately,
but if a table is passed as the second argument it can be followed
by extra arguments, with the last one ending up being used as a table,
causing a crash. The fix is to remove any potential extra arguments
using `lua_settop(L, 2)`.

Also added a few tests for this case. Ref #80.
2016-08-25 18:31:09 +03:00
Hisham Muhammad
4cf702d850 Merge pull request #78 from n1tehawk/20160716_symlink-target
Add a 'target' field for symlinkattributes.
2016-07-17 22:10:31 -03:00
NiteHawk
32b355a990 Add a 'target' field for symlinkattributes.
It returns the resolved path of the symlink.

Original version by Hisham <hisham@gobolinux.org>, modified to
use a different strategy for sizing the readlink() buffer.
2016-07-16 15:35:27 +02:00
Peter Melnichenko
080f74f785 Fix anchor names in documentation 2016-06-30 21:17:54 +03:00
Peter Melnichenko
aa18f3e127 Don't declare global 'lfs' 2016-05-05 15:24:29 +03:00
Peter Melnichenko
6d6fd631ba Add a test for success return value of lfs.link 2016-05-05 15:10:56 +03:00
Peter Melnichenko
46bbddc5a2 Return true instead of 0 on lfs.link success 2016-05-05 12:46:54 +03:00
Peter Melnichenko
81e5b165bf Update docs for new return values 2016-05-05 12:39:17 +03:00
Peter Melnichenko
8d5f9661e9 Test what lfs.attributes returns on error 2016-05-05 12:39:17 +03:00
Peter Melnichenko
b37e88b3d6 Return errno from lfs.attributes on error 2016-05-05 12:39:17 +03:00
Peter Melnichenko
8f167ef1de Return errno from lfs.touch on error 2016-05-05 12:39:17 +03:00
Peter Melnichenko
8b85d257a6 Return errno from lfs.rmdir on error 2016-05-05 12:39:17 +03:00
Peter Melnichenko
d186dda4d7 Return errno from lfs.mkdir on error
Change pushresult() to return true on success.
Change make_link to keep returning 0.
2016-05-05 12:38:28 +03:00
3 changed files with 168 additions and 116 deletions

View File

@@ -104,7 +104,7 @@ LuaFileSystem offers the following functions:
<dl class="reference"> <dl class="reference">
<dt><a name="attributes"></a><strong><code>lfs.attributes (filepath [, aname | atable])</code></strong></dt> <dt><a name="attributes"></a><strong><code>lfs.attributes (filepath [, aname | atable])</code></strong></dt>
<dd>Returns a table with the file attributes corresponding to <dd>Returns a table with the file attributes corresponding to
<code>filepath</code> (or <code>nil</code> followed by an error message <code>filepath</code> (or <code>nil</code> followed by an error message and a system-dependent error code
in case of error). in case of error).
If the second optional argument is given and is a string, then only the value of the If the second optional argument is given and is a string, then only the value of the
named attribute is returned (this use is equivalent to named attribute is returned (this use is equivalent to
@@ -177,7 +177,7 @@ LuaFileSystem offers the following functions:
Returns <code>true</code> in case of success or <code>nil</code> plus an Returns <code>true</code> in case of success or <code>nil</code> plus an
error string.</dd> error string.</dd>
<dt><a name="chdir"></a><strong><code>lfs.lock_dir(path, [seconds_stale])</code></strong></dt> <dt><a name="lock_dir"></a><strong><code>lfs.lock_dir(path, [seconds_stale])</code></strong></dt>
<dd>Creates a lockfile (called lockfile.lfs) in <code>path</code> if it does not <dd>Creates a lockfile (called lockfile.lfs) in <code>path</code> if it does not
exist and returns the lock. If the lock already exists checks if exist and returns the lock. If the lock already exists checks if
it's stale, using the second parameter (default for the second it's stale, using the second parameter (default for the second
@@ -187,7 +187,7 @@ LuaFileSystem offers the following functions:
particular, if the lock exists and is not stale it returns the particular, if the lock exists and is not stale it returns the
"File exists" message.</dd> "File exists" message.</dd>
<dt><a name="getcwd"></a><strong><code>lfs.currentdir ()</code></strong></dt> <dt><a name="currentdir"></a><strong><code>lfs.currentdir ()</code></strong></dt>
<dd>Returns a string with the current working directory or <code>nil</code> <dd>Returns a string with the current working directory or <code>nil</code>
plus an error string.</dd> plus an error string.</dd>
@@ -222,14 +222,14 @@ LuaFileSystem offers the following functions:
<dt><a name="mkdir"></a><strong><code>lfs.mkdir (dirname)</code></strong></dt> <dt><a name="mkdir"></a><strong><code>lfs.mkdir (dirname)</code></strong></dt>
<dd>Creates a new directory. The argument is the name of the new <dd>Creates a new directory. The argument is the name of the new
directory.<br /> directory.<br />
Returns <code>true</code> if the operation was successful; Returns <code>true</code> in case of success or <code>nil</code>, an error message and
in case of error, it returns <code>nil</code> plus an error string. a system-dependent error code in case of error.
</dd> </dd>
<dt><a name="rmdir"></a><strong><code>lfs.rmdir (dirname)</code></strong></dt> <dt><a name="rmdir"></a><strong><code>lfs.rmdir (dirname)</code></strong></dt>
<dd>Removes an existing directory. The argument is the name of the directory.<br /> <dd>Removes an existing directory. The argument is the name of the directory.<br />
Returns <code>true</code> if the operation was successful; Returns <code>true</code> in case of success or <code>nil</code>, an error message and
in case of error, it returns <code>nil</code> plus an error string.</dd> a system-dependent error code in case of error.
<dt><a name="setmode"></a><strong><code>lfs.setmode (file, mode)</code></strong></dt> <dt><a name="setmode"></a><strong><code>lfs.setmode (file, mode)</code></strong></dt>
<dd>Sets the writing mode for a file. The mode string can be either <code>"binary"</code> or <code>"text"</code>. <dd>Sets the writing mode for a file. The mode string can be either <code>"binary"</code> or <code>"text"</code>.
@@ -242,6 +242,8 @@ LuaFileSystem offers the following functions:
<dt><a name="symlinkattributes"></a><strong><code>lfs.symlinkattributes (filepath [, aname])</code></strong></dt> <dt><a name="symlinkattributes"></a><strong><code>lfs.symlinkattributes (filepath [, aname])</code></strong></dt>
<dd>Identical to <a href="#attributes">lfs.attributes</a> except that <dd>Identical to <a href="#attributes">lfs.attributes</a> except that
it obtains information about the link itself (not the file it refers to). it obtains information about the link itself (not the file it refers to).
It also adds a <strong><code>target</code></strong> field, containing
the file name that the symlink points to.
On Windows this function does not yet support links, and is identical to On Windows this function does not yet support links, and is identical to
<code>lfs.attributes</code>. <code>lfs.attributes</code>.
</dd> </dd>
@@ -255,8 +257,8 @@ LuaFileSystem offers the following functions:
Lua standard function <code>os.time</code>). Lua standard function <code>os.time</code>).
If the modification time is omitted, the access time provided is used; If the modification time is omitted, the access time provided is used;
if both times are omitted, the current time is used.<br /> if both times are omitted, the current time is used.<br />
Returns <code>true</code> if the operation was successful; Returns <code>true</code> in case of success or <code>nil</code>, an error message and
in case of error, it returns <code>nil</code> plus an error string. a system-dependent error code in case of error.
</dd> </dd>
<dt><a name="unlock"></a><strong><code>lfs.unlock (filehandle[, start[, length]])</code></strong></dt> <dt><a name="unlock"></a><strong><code>lfs.unlock (filehandle[, start[, length]])</code></strong></dt>

242
src/lfs.c
View File

@@ -41,22 +41,26 @@
#include <sys/stat.h> #include <sys/stat.h>
#ifdef _WIN32 #ifdef _WIN32
#include <direct.h> #include <direct.h>
#include <windows.h> #include <windows.h>
#include <io.h> #include <io.h>
#include <sys/locking.h> #include <sys/locking.h>
#ifdef __BORLANDC__ #ifdef __BORLANDC__
#include <utime.h> #include <utime.h>
#else
#include <sys/utime.h>
#endif
#include <fcntl.h>
/* MAX_PATH seems to be 260. Seems kind of small. Is there a better one? */
#define LFS_MAXPATHLEN MAX_PATH
#else #else
#include <sys/utime.h> #include <unistd.h>
#endif #include <dirent.h>
#include <fcntl.h> #include <fcntl.h>
#else #include <sys/types.h>
#include <unistd.h> #include <utime.h>
#include <dirent.h> #include <sys/param.h> /* for MAXPATHLEN */
#include <fcntl.h> #define LFS_MAXPATHLEN MAXPATHLEN
#include <sys/types.h>
#include <utime.h>
#endif #endif
#include <lua.h> #include <lua.h>
@@ -66,7 +70,6 @@
#include "lfs.h" #include "lfs.h"
#define LFS_VERSION "1.6.3" #define LFS_VERSION "1.6.3"
#define LFS_LIBNAME "lfs"
#if LUA_VERSION_NUM >= 503 /* Lua 5.3 */ #if LUA_VERSION_NUM >= 503 /* Lua 5.3 */
@@ -85,22 +88,6 @@
#define strerror(_) "System unable to describe the error" #define strerror(_) "System unable to describe the error"
#endif #endif
/* Define 'getcwd' for systems that do not implement it */
#ifdef NO_GETCWD
#define getcwd(p,s) NULL
#define getcwd_error "Function 'getcwd' not provided by system"
#else
#define getcwd_error strerror(errno)
#ifdef _WIN32
/* MAX_PATH seems to be 260. Seems kind of small. Is there a better one? */
#define LFS_MAXPATHLEN MAX_PATH
#else
/* For MAXPATHLEN: */
#include <sys/param.h>
#define LFS_MAXPATHLEN MAXPATHLEN
#endif
#endif
#define DIR_METATABLE "directory metatable" #define DIR_METATABLE "directory metatable"
typedef struct dir_data { typedef struct dir_data {
int closed; int closed;
@@ -133,6 +120,13 @@ typedef struct dir_data {
#define LSTAT_FUNC lstat #define LSTAT_FUNC lstat
#endif #endif
#ifdef _WIN32
#define lfs_mkdir _mkdir
#else
#define lfs_mkdir(path) (mkdir((path), \
S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH))
#endif
/* /*
** Utility functions ** Utility functions
*/ */
@@ -147,12 +141,13 @@ static int pusherror(lua_State *L, const char *info)
return 3; return 3;
} }
static int pushresult(lua_State *L, int i, const char *info) static int pushresult(lua_State *L, int res, const char *info) {
{ if (res == -1) {
if (i==-1) return pusherror(L, info);
return pusherror(L, info); } else {
lua_pushinteger(L, i); lua_pushboolean(L, 1);
return 1; return 1;
}
} }
@@ -178,18 +173,35 @@ static int change_dir (lua_State *L) {
** and a string describing the error ** and a string describing the error
*/ */
static int get_dir (lua_State *L) { static int get_dir (lua_State *L) {
char *path; #ifdef NO_GETCWD
/* Passing (NULL, 0) is not guaranteed to work. Use a temp buffer and size instead. */
char buf[LFS_MAXPATHLEN];
if ((path = getcwd(buf, LFS_MAXPATHLEN)) == NULL) {
lua_pushnil(L); lua_pushnil(L);
lua_pushstring(L, getcwd_error); lua_pushstring(L, "Function 'getcwd' not provided by system");
return 2; return 2;
} #else
else { char *path = NULL;
lua_pushstring(L, path); /* Passing (NULL, 0) is not guaranteed to work. Use a temp buffer and size instead. */
return 1; size_t size = LFS_MAXPATHLEN; /* initial buffer size */
} int result;
while (1) {
path = realloc(path, size);
if (!path) /* failed to allocate */
return pusherror(L, "get_dir realloc() failed");
if (getcwd(path, size) != NULL) {
/* success, push the path to the Lua stack */
lua_pushstring(L, path);
result = 1;
break;
}
if (errno != ERANGE) { /* unexpected error */
result = pusherror(L, "get_dir getcwd() failed");
break;
}
/* ERANGE = insufficient buffer capacity, double size and retry */
size *= 2;
}
free(path);
return result;
#endif
} }
/* /*
@@ -417,16 +429,14 @@ static int file_unlock (lua_State *L) {
** @param #2 Name of link. ** @param #2 Name of link.
** @param #3 True if link is symbolic (optional). ** @param #3 True if link is symbolic (optional).
*/ */
static int make_link(lua_State *L) static int make_link (lua_State *L) {
{
#ifndef _WIN32 #ifndef _WIN32
const char *oldpath = luaL_checkstring(L, 1); const char *oldpath = luaL_checkstring(L, 1);
const char *newpath = luaL_checkstring(L, 2); const char *newpath = luaL_checkstring(L, 2);
return pushresult(L, return pushresult(L, (lua_toboolean(L, 3) ? symlink : link)(oldpath, newpath), NULL);
(lua_toboolean(L,3) ? symlink : link)(oldpath, newpath), NULL);
#else #else
errno = ENOSYS; /* = "Function not implemented" */ errno = ENOSYS; /* = "Function not implemented" */
return pushresult(L, -1, "make_link is not supported on Windows"); return pushresult(L, -1, "make_link is not supported on Windows");
#endif #endif
} }
@@ -436,21 +446,8 @@ static int make_link(lua_State *L)
** @param #1 Directory path. ** @param #1 Directory path.
*/ */
static int make_dir (lua_State *L) { static int make_dir (lua_State *L) {
const char *path = luaL_checkstring (L, 1); const char *path = luaL_checkstring(L, 1);
int fail; return pushresult(L, lfs_mkdir(path), NULL);
#ifdef _WIN32
fail = _mkdir (path);
#else
fail = mkdir (path, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP |
S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH );
#endif
if (fail) {
lua_pushnil (L);
lua_pushfstring (L, "%s", strerror(errno));
return 2;
}
lua_pushboolean (L, 1);
return 1;
} }
@@ -459,18 +456,8 @@ static int make_dir (lua_State *L) {
** @param #1 Directory path. ** @param #1 Directory path.
*/ */
static int remove_dir (lua_State *L) { static int remove_dir (lua_State *L) {
const char *path = luaL_checkstring (L, 1); const char *path = luaL_checkstring(L, 1);
int fail; return pushresult(L, rmdir(path), NULL);
fail = rmdir (path);
if (fail) {
lua_pushnil (L);
lua_pushfstring (L, "%s", strerror(errno));
return 2;
}
lua_pushboolean (L, 1);
return 1;
} }
@@ -657,26 +644,24 @@ static const char *mode2string (mode_t mode) {
/* /*
** Set access time and modification values for file ** Set access time and modification values for a file.
** @param #1 File path.
** @param #2 Access time in seconds, current time is used if missing.
** @param #3 Modification time in seconds, access time is used if missing.
*/ */
static int file_utime (lua_State *L) { static int file_utime (lua_State *L) {
const char *file = luaL_checkstring (L, 1); const char *file = luaL_checkstring(L, 1);
struct utimbuf utb, *buf; struct utimbuf utb, *buf;
if (lua_gettop (L) == 1) /* set to current date/time */ if (lua_gettop (L) == 1) /* set to current date/time */
buf = NULL; buf = NULL;
else { else {
utb.actime = (time_t)luaL_optnumber (L, 2, 0); utb.actime = (time_t) luaL_optnumber(L, 2, 0);
utb.modtime = (time_t) luaL_optinteger (L, 3, utb.actime); utb.modtime = (time_t) luaL_optinteger(L, 3, utb.actime);
buf = &utb; buf = &utb;
} }
if (utime (file, buf)) {
lua_pushnil (L); return pushresult(L, utime(file, buf), NULL);
lua_pushfstring (L, "%s", strerror (errno));
return 2;
}
lua_pushboolean (L, 1);
return 1;
} }
@@ -813,7 +798,8 @@ static int _file_info_ (lua_State *L, int (*st)(const char*, STAT_STRUCT*)) {
if (st(file, &info)) { if (st(file, &info)) {
lua_pushnil(L); lua_pushnil(L);
lua_pushfstring(L, "cannot obtain information from file '%s': %s", file, strerror(errno)); lua_pushfstring(L, "cannot obtain information from file '%s': %s", file, strerror(errno));
return 2; lua_pushinteger(L, errno);
return 3;
} }
if (lua_isstring (L, 2)) { if (lua_isstring (L, 2)) {
const char *member = lua_tostring (L, 2); const char *member = lua_tostring (L, 2);
@@ -827,7 +813,8 @@ static int _file_info_ (lua_State *L, int (*st)(const char*, STAT_STRUCT*)) {
/* member not found */ /* member not found */
return luaL_error(L, "invalid attribute name '%s'", member); return luaL_error(L, "invalid attribute name '%s'", member);
} }
/* creates a table if none is given */ /* creates a table if none is given, removes extra arguments */
lua_settop(L, 2);
if (!lua_istable (L, 2)) { if (!lua_istable (L, 2)) {
lua_newtable (L); lua_newtable (L);
} }
@@ -849,11 +836,58 @@ static int file_info (lua_State *L) {
} }
/*
** Push the symlink target to the top of the stack.
** Assumes the file name is at position 1 of the stack.
** Returns 1 if successful (with the target on top of the stack),
** 0 on failure (with stack unchanged, and errno set).
*/
static int push_link_target(lua_State *L) {
#ifdef _WIN32
errno = ENOSYS;
return 0;
#else
const char *file = luaL_checkstring(L, 1);
char *target = NULL;
int tsize, size = 256; /* size = initial buffer capacity */
while (1) {
target = realloc(target, size);
if (!target) /* failed to allocate */
return 0;
tsize = readlink(file, target, size);
if (tsize < 0) { /* a readlink() error occurred */
free(target);
return 0;
}
if (tsize < size)
break;
/* possibly truncated readlink() result, double size and retry */
size *= 2;
}
target[tsize] = '\0';
lua_pushlstring(L, target, tsize);
free(target);
return 1;
#endif
}
/* /*
** Get symbolic link information using lstat. ** Get symbolic link information using lstat.
*/ */
static int link_info (lua_State *L) { static int link_info (lua_State *L) {
return _file_info_ (L, LSTAT_FUNC); int ret;
if (lua_isstring (L, 2) && (strcmp(lua_tostring(L, 2), "target") == 0)) {
int ok = push_link_target(L);
return ok ? 1 : pusherror(L, "could not obtain link target");
}
ret = _file_info_ (L, LSTAT_FUNC);
if (ret == 1 && lua_type(L, -1) == LUA_TTABLE) {
int ok = push_link_target(L);
if (ok) {
lua_setfield(L, -2, "target");
}
}
return ret;
} }
@@ -891,8 +925,6 @@ LFS_EXPORT int luaopen_lfs (lua_State *L) {
dir_create_meta (L); dir_create_meta (L);
lock_create_meta (L); lock_create_meta (L);
luaL_newlib (L, fslib); luaL_newlib (L, fslib);
lua_pushvalue(L, -1);
lua_setglobal(L, LFS_LIBNAME);
set_info (L); set_info (L);
return 1; return 1;
} }

View File

@@ -88,9 +88,13 @@ io.write(".")
io.flush() io.flush()
-- Checking link (does not work on Windows) -- Checking link (does not work on Windows)
if lfs.link (tmpfile, "_a_link_for_test_", true) then local link_ok = lfs.link (tmpfile, "_a_link_for_test_", true)
if link_ok then
assert (link_ok == true, "successful lfs.link did not return true")
assert (lfs.attributes"_a_link_for_test_".mode == "file") assert (lfs.attributes"_a_link_for_test_".mode == "file")
assert (lfs.symlinkattributes"_a_link_for_test_".mode == "link") assert (lfs.symlinkattributes"_a_link_for_test_".mode == "link")
assert (lfs.symlinkattributes"_a_link_for_test_".target == tmpfile)
assert (lfs.symlinkattributes("_a_link_for_test_", "target") == tmpfile)
assert (lfs.link (tmpfile, "_a_hard_link_for_test_")) assert (lfs.link (tmpfile, "_a_hard_link_for_test_"))
assert (lfs.attributes (tmpfile, "nlink") == 2) assert (lfs.attributes (tmpfile, "nlink") == 2)
assert (os.remove"_a_link_for_test_") assert (os.remove"_a_link_for_test_")
@@ -130,6 +134,17 @@ for key, value in pairs(attr) do
"lfs.attributes values not consistent") "lfs.attributes values not consistent")
end end
-- Check that lfs.attributes accepts a table as second argument
local attr2 = {}
lfs.attributes(tmpfile, attr2)
for key, value in pairs(attr2) do
assert (value == lfs.attributes (tmpfile, key),
"lfs.attributes values with table argument not consistent")
end
-- Check that extra arguments are ignored
lfs.attributes(tmpfile, attr2, nil)
-- Remove new file and directory -- Remove new file and directory
assert (os.remove (tmpfile), "could not remove new file") assert (os.remove (tmpfile), "could not remove new file")
assert (lfs.rmdir (tmpdir), "could not remove new directory") assert (lfs.rmdir (tmpdir), "could not remove new directory")
@@ -139,7 +154,10 @@ io.write(".")
io.flush() io.flush()
-- Trying to get attributes of a non-existent file -- Trying to get attributes of a non-existent file
assert (lfs.attributes ("this couldn't be an actual file") == nil, "could get attributes of a non-existent file") local attr_ok, err, errno = lfs.attributes("this couldn't be an actual file")
assert(attr_ok == nil, "could get attributes of a non-existent file")
assert(type(err) == "string", "failed lfs.attributes did not return an error message")
assert(type(errno) == "number", "failed lfs.attributes did not return error code")
assert (type(lfs.attributes (upper)) == "table", "couldn't get attributes of upper directory") assert (type(lfs.attributes (upper)) == "table", "couldn't get attributes of upper directory")
io.write(".") io.write(".")