Index: nse_fs.cc =================================================================== --- nse_fs.cc (revision 28365) +++ nse_fs.cc (working copy) @@ -1,7 +1,38 @@ +/* +** LuaFileSystem +** Copyright Kepler Project 2003 (http://www.keplerproject.org/luafilesystem) +** +** File system manipulation library. +** This library offers these functions: +** lfs.attributes (filepath [, attributename]) +** lfs.chdir (path) +** lfs.currentdir () +** lfs.dir (path) +** lfs.lock (fh, mode) +** lfs.lock_dir (path) +** lfs.mkdir (path) +** lfs.rmdir (path) +** lfs.setmode (filepath, mode) +** lfs.symlinkattributes (filepath [, attributename]) -- thanks to Sam Roberts +** lfs.touch (filepath [, atime [, mtime]]) +** lfs.unlock (fh) +** +** $Id: lfs.c,v 1.61 2009/07/04 02:10:16 mascarenhas Exp $ +*/ +#ifndef _WIN32 +#ifndef _AIX +#define _FILE_OFFSET_BITS 64 /* Linux, Solaris and HP-UX */ +#else +#define _LARGE_FILES 1 /* AIX */ +#endif +#endif + +#define _LARGEFILE64_SOURCE + extern "C" { + #include "lauxlib.h" #include "lua.h" - #include "lauxlib.h" } #include "nmap.h" @@ -9,6 +40,7 @@ #include "nmap_error.h" #include "NmapOps.h" +#include #include #include @@ -18,243 +50,784 @@ #include "dirent.h" #endif -#define DIR_METATABLE "dir" - -#ifndef MAXPATHLEN -# define MAXPATHLEN 2048 +/* Define 'strerror' for systems that do not implement it */ +#ifdef NO_STRERROR +#define strerror(_) "System unable to describe the error" #endif -#ifndef MAX_DIR_LENGTH -# define MAX_DIR_LENGTH 1024 +/* 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) #endif +#define DIR_METATABLE "directory metatable" typedef struct dir_data { - int closed; -#ifdef WIN32 - long hFile; - char pattern[MAX_DIR_LENGTH+1]; + int closed; +#ifdef _WIN32 + long hFile; + char pattern[MAX_PATH+1]; #else - DIR *dir; + DIR *dir; #endif } dir_data; -extern NmapOps o; +#define LOCK_METATABLE "lock metatable" -static bool filename_is_absolute(const char *file) { - if (file[0] == '/') - return true; -#ifdef WIN32 - if ((file[0] != '\0' && file[1] == ':') || file[0] == '\\') - return true; +#ifdef _WIN32 + #ifdef __BORLANDC__ + #define lfs_setmode(L,file,m) ((void)L, setmode(_fileno(file), m)) + #define STAT_STRUCT struct stati64 + #else + #define lfs_setmode(L,file,m) ((void)L, _setmode(_fileno(file), m)) + #define STAT_STRUCT struct _stati64 + #endif +#define STAT_FUNC _stati64 +#define LSTAT_FUNC STAT_FUNC +#else +#define _O_TEXT 0 +#define _O_BINARY 0 +#define lfs_setmode(L,file,m) 0 +#define STAT_STRUCT struct stat +#define STAT_FUNC stat +#define LSTAT_FUNC lstat #endif - return false; + +/* +** Utility functions +*/ +static int pusherror(lua_State *L, const char *info) +{ + lua_pushboolean(L, false); + if (info==NULL) + lua_pushstring(L, strerror(errno)); + else + lua_pushfstring(L, "%s: %s", info, strerror(errno)); + lua_pushinteger(L, errno); + return 3; } -/* This is a modification of nmap_fetchfile specialized to look for files - * in the scripts subdirectory. If the path is absolute, it is always tried - * verbatim. Otherwise, the file is looked for under scripts/, and then finally - * in the current directory. - */ -static int nse_fetchscript(char *path, size_t path_len, const char *file) { - std::string scripts_path = std::string(SCRIPT_ENGINE_LUA_DIR) + std::string(file); - int type; +static int pushresult(lua_State *L, int i, const char *info) +{ + if (i==-1) + return pusherror(L, info); + lua_pushboolean(L, true); + return 1; +} - if (filename_is_absolute(file)) { - if (o.debugging > 1) - log_write(LOG_STDOUT, "%s: Trying absolute path %s\n", SCRIPT_ENGINE, file); - Strncpy(path, file, path_len); - return nmap_fileexistsandisreadable(file); - } - // lets look in /scripts - type = nmap_fetchfile(path, path_len, scripts_path.c_str()); +/* +** This function changes the working (current) directory +*/ +static int change_dir (lua_State *L) { + const char *path = luaL_checkstring(L, 1); + if (chdir(path)) { + lua_pushboolean (L, false); + lua_pushfstring (L,"Unable to change working directory to '%s'\n%s\n", + path, chdir_error); + return 2; + } else { + lua_pushboolean (L, 1); + return 1; + } +} - if (type == 0) { - // current directory - Strncpy(path, file, path_len); - return nmap_fileexistsandisreadable(file); +/* +** This function returns the current directory +** If unable to get the current directory, it returns nil +** and a string describing the error +*/ +static int get_dir (lua_State *L) { + char *path; + if ((path = getcwd(NULL, 0)) == NULL) { + lua_pushboolean(L, false); + lua_pushstring(L, getcwd_error); + return 2; } + else { + lua_pushboolean(L, true); + lua_pushstring(L, path); + free(path); + return 2; + } +} - return type; +/* +** Check if the given element on the stack is a file and returns it. +*/ +static FILE *check_file (lua_State *L, int idx, const char *funcname) { + FILE **fh = (FILE **)luaL_checkudata (L, idx, "FILE*"); + if (fh == NULL) { + luaL_error (L, "%s: not a file", funcname); + return 0; + } else if (*fh == NULL) { + luaL_error (L, "%s: closed file", funcname); + return 0; + } else + return *fh; } -/* This is a modification of nmap_fetchfile that first looks for an - * absolute file name. - */ -static int nse_fetchfile_absolute(char *path, size_t path_len, const char *file) { - if (filename_is_absolute(file)) { - if (o.debugging > 1) - log_write(LOG_STDOUT, "%s: Trying absolute path %s\n", SCRIPT_ENGINE, file); - Strncpy(path, file, path_len); - return nmap_fileexistsandisreadable(file); - } - return nmap_fetchfile(path, path_len, file); +/* +** +*/ +static int _file_lock (lua_State *L, FILE *fh, const char *mode, const long start, long len, const char *funcname) { + int code; +#ifdef _WIN32 + /* lkmode valid values are: + LK_LOCK Locks the specified bytes. If the bytes cannot be locked, the program immediately tries again after 1 second. If, after 10 attempts, the bytes cannot be locked, the constant returns an error. + LK_NBLCK Locks the specified bytes. If the bytes cannot be locked, the constant returns an error. + LK_NBRLCK Same as _LK_NBLCK. + LK_RLCK Same as _LK_LOCK. + LK_UNLCK Unlocks the specified bytes, which must have been previously locked. + + Regions should be locked only briefly and should be unlocked before closing a file or exiting the program. + + http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_crt__locking.asp + */ + int lkmode; + switch (*mode) { + case 'r': lkmode = LK_NBLCK; break; + case 'w': lkmode = LK_NBLCK; break; + case 'u': lkmode = LK_UNLCK; break; + default : return luaL_error (L, "%s: invalid mode", funcname); + } + if (!len) { + fseek (fh, 0L, SEEK_END); + len = ftell (fh); + } + fseek (fh, start, SEEK_SET); +#ifdef __BORLANDC__ + code = locking (fileno(fh), lkmode, len); +#else + code = _locking (fileno(fh), lkmode, len); +#endif +#else + struct flock f; + switch (*mode) { + case 'w': f.l_type = F_WRLCK; break; + case 'r': f.l_type = F_RDLCK; break; + case 'u': f.l_type = F_UNLCK; break; + default : return luaL_error (L, "%s: invalid mode", funcname); + } + f.l_whence = SEEK_SET; + f.l_start = (off_t)start; + f.l_len = (off_t)len; + code = fcntl (fileno(fh), F_SETLK, &f); +#endif + return (code != -1); } -static int nse_fetch (lua_State *L, int (*fetch)(char *, size_t, const char *)) -{ - char path[MAXPATHLEN]; - switch (fetch(path, sizeof(path), luaL_checkstring(L, 1))) - { - case 0: // no such path - lua_pushnil(L); - lua_pushfstring(L, "no path to file/directory: %s", lua_tostring(L, 1)); - break; - case 1: // file returned - lua_pushliteral(L, "file"); - lua_pushstring(L, path); - break; - case 2: // directory returned - lua_pushliteral(L, "directory"); - lua_pushstring(L, path); - break; - default: - return luaL_error(L, "nse_fetch returned bad code"); +#ifdef _WIN32 +typedef struct lfs_Lock { + HANDLE fd; +} lfs_Lock; +static int lfs_lock_dir(lua_State *L) { + size_t pathl; HANDLE fd; + lfs_Lock *lock; + char *ln; + const char *lockfile = "/lockfile.lfs"; + const char *path = luaL_checklstring(L, 1, &pathl); + ln = (char*)malloc(pathl + strlen(lockfile) + 1); + if(!ln) { + lua_pushboolean(L, false); lua_pushstring(L, strerror(errno)); return 2; } - return 2; + strcpy(ln, path); strcat(ln, lockfile); + if((fd = CreateFile(ln, GENERIC_WRITE, 0, NULL, CREATE_NEW, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL)) == INVALID_HANDLE_VALUE) { + int en = GetLastError(); + free(ln); lua_pushboolean(L, false); + if(en == ERROR_FILE_EXISTS || en == ERROR_SHARING_VIOLATION) + lua_pushstring(L, "File exists"); + else + lua_pushstring(L, strerror(en)); + return 2; + } + free(ln); + lock = (lfs_Lock*)lua_newuserdata(L, sizeof(lfs_Lock)); + lock->fd = fd; + luaL_getmetatable (L, LOCK_METATABLE); + lua_setmetatable (L, -2); + return 1; } +static int lfs_unlock_dir(lua_State *L) { + lfs_Lock *lock = luaL_checkudata(L, 1, LOCK_METATABLE); + CloseHandle(lock->fd); + return 0; +} +#else +typedef struct lfs_Lock { + char *ln; +} lfs_Lock; +static int lfs_lock_dir(lua_State *L) { + lfs_Lock *lock; + size_t pathl; + char *ln; + const char *lockfile = "/lockfile.lfs"; + const char *path = luaL_checklstring(L, 1, &pathl); + lock = (lfs_Lock*)lua_newuserdata(L, sizeof(lfs_Lock)); + ln = (char*)malloc(pathl + strlen(lockfile) + 1); + if(!ln) { + lua_pushboolean(L, false); lua_pushstring(L, strerror(errno)); return 2; + } + strcpy(ln, path); strcat(ln, lockfile); + if(symlink("lock", ln) == -1) { + free(ln); lua_pushboolean(L, false); + lua_pushstring(L, strerror(errno)); return 2; + } + lock->ln = ln; + luaL_getmetatable (L, LOCK_METATABLE); + lua_setmetatable (L, -2); + return 1; +} +static int lfs_unlock_dir(lua_State *L) { + lfs_Lock *lock = (lfs_Lock *)luaL_checkudata(L, 1, LOCK_METATABLE); + if(lock->ln) { + unlink(lock->ln); + free(lock->ln); + lock->ln = NULL; + } + return 0; +} +#endif -int fetchscript (lua_State *L) -{ - return nse_fetch(L, nse_fetchscript); +static int lfs_g_setmode (lua_State *L, FILE *f, int arg) { + static const int mode[] = {_O_BINARY, _O_TEXT}; + static const char *const modenames[] = {"binary", "text", NULL}; + int op = luaL_checkoption(L, arg, NULL, modenames); + int res = lfs_setmode(L, f, mode[op]); + if (res != -1) { + int i; + lua_pushboolean(L, 1); + for (i = 0; modenames[i] != NULL; i++) { + if (mode[i] == res) { + lua_pushstring(L, modenames[i]); + goto exit; + } + } + lua_pushnil(L); + exit: + return 2; + } else { + int en = errno; + lua_pushboolean(L, false); + lua_pushfstring(L, "%s", strerror(en)); + lua_pushinteger(L, en); + return 3; + } } -int fetchfile_absolute (lua_State *L) +static int lfs_f_setmode(lua_State *L) { + return lfs_g_setmode(L, check_file(L, 1, "setmode"), 2); +} + +/* +** Locks a file. +** @param #1 File handle. +** @param #2 String with lock mode ('w'rite, 'r'ead). +** @param #3 Number with start position (optional). +** @param #4 Number with length (optional). +*/ +static int file_lock (lua_State *L) { + FILE *fh = check_file (L, 1, "lock"); + const char *mode = luaL_checkstring (L, 2); + const long start = luaL_optlong (L, 3, 0); + long len = luaL_optlong (L, 4, 0); + if (_file_lock (L, fh, mode, start, len, "lock")) { + lua_pushboolean (L, 1); + return 1; + } else { + lua_pushboolean (L, false); + lua_pushfstring (L, "%s", strerror(errno)); + return 2; + } +} + + +/* +** Unlocks a file. +** @param #1 File handle. +** @param #2 Number with start position (optional). +** @param #3 Number with length (optional). +*/ +static int file_unlock (lua_State *L) { + FILE *fh = check_file (L, 1, "unlock"); + const long start = luaL_optlong (L, 2, 0); + long len = luaL_optlong (L, 3, 0); + if (_file_lock (L, fh, "u", start, len, "unlock")) { + lua_pushboolean (L, 1); + return 1; + } else { + lua_pushboolean (L, false); + lua_pushfstring (L, "%s", strerror(errno)); + return 2; + } +} + + +/* +** Creates a link. +** @param #1 Object to link to. +** @param #2 Name of link. +** @param #3 True if link is symbolic (optional). +*/ +static int make_link(lua_State *L) { - return nse_fetch(L, nse_fetchfile_absolute); +#ifndef _WIN32 + const char *oldpath = luaL_checkstring(L, 1); + const char *newpath = luaL_checkstring(L, 2); + return pushresult(L, + (lua_toboolean(L,3) ? symlink : link)(oldpath, newpath), NULL); +#else + pusherror(L, "make_link is not supported on Windows"); +#endif } -/* LuaFileSystem directory iterator port. - * - * LuaFileSystem library: - * by Roberto Ierusalimschy, Andre Carregal and Tomas Guisasola - * as part of the Kepler Project. - * LuaFileSystem is currently maintained by Fabio Mascarenhas. - * - * LuaFileSystem is a Lua library developed to complement the set - * of functions related to file systems offered by the standard - * Lua distribution. - * LuaFileSystem offers a portable way to access the underlying - * directory structure and file attributes. - * - * LuaFileSystem is free software and uses the same license as Lua 5.1. - * - * the most recent copy can be found at - * http://www.keplerproject.org/luafilesystem/ - * - * Note: this is a port of the LuaFileSystem directory iterator for the - * Nmap project http://nmap.org - **/ +/* +** Creates a directory. +** @param #1 Directory path. +*/ +static int make_dir (lua_State *L) { + const char *path = luaL_checkstring (L, 1); + int fail; +#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_pushboolean (L, false); + lua_pushfstring (L, "%s", strerror(errno)); + return 2; + } + lua_pushboolean (L, 1); + return 1; +} /* +** Removes a directory. +** @param #1 Directory path. +*/ +static int remove_dir (lua_State *L) { + const char *path = luaL_checkstring (L, 1); + int fail; + + fail = rmdir (path); + + if (fail) { + lua_pushboolean (L, false); + lua_pushfstring (L, "%s", strerror(errno)); + return 2; + } + lua_pushboolean (L, 1); + return 1; +} + +/* ** Directory iterator */ static int dir_iter (lua_State *L) { -#ifdef WIN32 - struct _finddata_t c_file; +#ifdef _WIN32 + struct _finddata_t c_file; #else - struct dirent *entry; + struct dirent *entry; #endif - dir_data *d = (dir_data *)luaL_checkudata(L, 1, DIR_METATABLE); - luaL_argcheck(L, !d->closed, 1, "closed directory"); -#ifdef WIN32 - if (d->hFile == 0L) { /* first entry */ - if ((d->hFile = _findfirst(d->pattern, &c_file)) == -1L) { - lua_pushnil(L); - lua_pushstring(L, strerror (errno)); - return 2; - } else { - lua_pushstring(L, c_file.name); - return 1; - } - } else { /* next entry */ - if (_findnext(d->hFile, &c_file) == -1L) { - /* no more entries => close directory */ - _findclose(d->hFile); - d->closed = 1; - return 0; - } else { - lua_pushstring(L, c_file.name); - return 1; - } - } + dir_data *d = (dir_data *)luaL_checkudata (L, 1, DIR_METATABLE); + luaL_argcheck (L, d->closed == 0, 1, "closed directory"); +#ifdef _WIN32 + if (d->hFile == 0L) { /* first entry */ + if ((d->hFile = _findfirst (d->pattern, &c_file)) == -1L) { + lua_pushboolean (L, false); + lua_pushstring (L, strerror (errno)); + d->closed = 1; + return 2; + } else { + lua_pushstring (L, c_file.name); + return 1; + } + } else { /* next entry */ + if (_findnext (d->hFile, &c_file) == -1L) { + /* no more entries => close directory */ + _findclose (d->hFile); + d->closed = 1; + return 0; + } else { + lua_pushstring (L, c_file.name); + return 1; + } + } #else - if ((entry = readdir(d->dir)) != NULL) { - lua_pushstring(L, entry->d_name); - return 1; - } else { - /* no more entries => close directory */ - closedir(d->dir); - d->closed = 1; - return 0; - } + if ((entry = readdir (d->dir)) != NULL) { + lua_pushstring (L, entry->d_name); + return 1; + } else { + /* no more entries => close directory */ + closedir (d->dir); + d->closed = 1; + return 0; + } #endif } + /* ** Closes directory iterators */ static int dir_close (lua_State *L) { - dir_data *d = (dir_data *)lua_touserdata(L, 1); -#ifdef WIN32 - if (!d->closed && d->hFile) { - _findclose(d->hFile); - d->closed = 1; - } + dir_data *d = (dir_data *)lua_touserdata (L, 1); +#ifdef _WIN32 + if (!d->closed && d->hFile) { + _findclose (d->hFile); + } #else - if (!d->closed && d->dir) { - closedir(d->dir); - d->closed = 1; - } + if (!d->closed && d->dir) { + closedir (d->dir); + } #endif - return 0; + d->closed = 1; + return 0; } + /* ** Factory of directory iterators */ -int nse_readdir (lua_State *L) { - const char *dirname = luaL_checkstring(L, 1); - dir_data *d; - lua_pushcfunction(L, dir_iter); - d = (dir_data *)lua_newuserdata(L, sizeof(dir_data)); - d->closed = 0; -#ifdef WIN32 - d->hFile = 0L; - luaL_getmetatable(L, DIR_METATABLE); - lua_setmetatable(L, -2); - if (strlen(dirname) > MAX_DIR_LENGTH) - luaL_error(L, "%s: Path too long '%s'.", SCRIPT_ENGINE, dirname); - else - Snprintf(d->pattern, MAX_DIR_LENGTH, "%s/*", dirname); +static int dir_iter_factory (lua_State *L) { + const char *path = luaL_checkstring (L, 1); + dir_data *d; + lua_pushcfunction (L, dir_iter); + d = (dir_data *) lua_newuserdata (L, sizeof(dir_data)); + luaL_getmetatable (L, DIR_METATABLE); + lua_setmetatable (L, -2); + d->closed = 0; +#ifdef _WIN32 + d->hFile = 0L; + if (strlen(path) > MAX_PATH-2) + luaL_error (L, "path too long: %s", path); + else + sprintf (d->pattern, "%s/*", path); #else - luaL_getmetatable(L, DIR_METATABLE); - lua_setmetatable(L, -2); - d->dir = opendir(dirname); - if (d->dir == NULL) - luaL_error(L, "%s: Could not open directory '%s'.", SCRIPT_ENGINE, dirname); + d->dir = opendir (path); + if (d->dir == NULL) + luaL_error (L, "cannot open %s: %s", path, strerror (errno)); #endif - return 2; + return 2; } -int luaopen_fs(lua_State *L) + +/* +** Creates directory metatable. +*/ +static int dir_create_meta (lua_State *L) { + luaL_newmetatable (L, DIR_METATABLE); + + /* Method table */ + lua_newtable(L); + lua_pushcfunction (L, dir_iter); + lua_setfield(L, -2, "next"); + lua_pushcfunction (L, dir_close); + lua_setfield(L, -2, "close"); + + /* Metamethods */ + lua_setfield(L, -2, "__index"); + lua_pushcfunction (L, dir_close); + lua_setfield (L, -2, "__gc"); + return 1; +} + +/* +** Creates lock metatable. +*/ +static int lock_create_meta (lua_State *L) { + luaL_newmetatable (L, LOCK_METATABLE); + + /* Method table */ + lua_newtable(L); + lua_pushcfunction(L, lfs_unlock_dir); + lua_setfield(L, -2, "free"); + + /* Metamethods */ + lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, lfs_unlock_dir); + lua_setfield(L, -2, "__gc"); + return 1; +} + + +#ifdef _WIN32 + #ifndef S_ISDIR + #define S_ISDIR(mode) (mode&_S_IFDIR) + #endif + #ifndef S_ISREG + #define S_ISREG(mode) (mode&_S_IFREG) + #endif + #ifndef S_ISLNK + #define S_ISLNK(mode) (0) + #endif + #ifndef S_ISSOCK + #define S_ISSOCK(mode) (0) + #endif + #ifndef S_ISFIFO + #define S_ISFIFO(mode) (0) + #endif + #ifndef S_ISCHR + #define S_ISCHR(mode) (mode&_S_IFCHR) + #endif + #ifndef S_ISBLK + #define S_ISBLK(mode) (0) + #endif +#endif +/* +** Convert the inode protection mode to a string. +*/ +#ifdef _WIN32 +static const char *mode2string (unsigned short mode) { +#else +static const char *mode2string (mode_t mode) { +#endif + if ( S_ISREG(mode) ) + return "file"; + else if ( S_ISDIR(mode) ) + return "directory"; + else if ( S_ISLNK(mode) ) + return "link"; + else if ( S_ISSOCK(mode) ) + return "socket"; + else if ( S_ISFIFO(mode) ) + return "named pipe"; + else if ( S_ISCHR(mode) ) + return "char device"; + else if ( S_ISBLK(mode) ) + return "block device"; + else + return "other"; +} + + +/* +** Set access time and modification values for file +*/ +static int file_utime (lua_State *L) { + const char *file = luaL_checkstring (L, 1); + struct utimbuf utb, *buf; + + if (lua_gettop (L) == 1) /* set to current date/time */ + buf = NULL; + else { + utb.actime = (time_t)luaL_optnumber (L, 2, 0); + utb.modtime = (time_t)luaL_optnumber (L, 3, utb.actime); + buf = &utb; + } + if (utime (file, buf)) { + lua_pushboolean (L, false); + lua_pushfstring (L, "%s", strerror (errno)); + return 2; + } + lua_pushboolean (L, 1); + return 1; +} + + +/* inode protection mode */ +static void push_st_mode (lua_State *L, STAT_STRUCT *info) { + lua_pushstring (L, mode2string (info->st_mode)); +} +/* device inode resides on */ +static void push_st_dev (lua_State *L, STAT_STRUCT *info) { + lua_pushnumber (L, (lua_Number)info->st_dev); +} +/* inode's number */ +static void push_st_ino (lua_State *L, STAT_STRUCT *info) { + lua_pushnumber (L, (lua_Number)info->st_ino); +} +/* number of hard links to the file */ +static void push_st_nlink (lua_State *L, STAT_STRUCT *info) { + lua_pushnumber (L, (lua_Number)info->st_nlink); +} +/* user-id of owner */ +static void push_st_uid (lua_State *L, STAT_STRUCT *info) { + lua_pushnumber (L, (lua_Number)info->st_uid); +} +/* group-id of owner */ +static void push_st_gid (lua_State *L, STAT_STRUCT *info) { + lua_pushnumber (L, (lua_Number)info->st_gid); +} +/* device type, for special file inode */ +static void push_st_rdev (lua_State *L, STAT_STRUCT *info) { + lua_pushnumber (L, (lua_Number)info->st_rdev); +} +/* time of last access */ +static void push_st_atime (lua_State *L, STAT_STRUCT *info) { + lua_pushnumber (L, info->st_atime); +} +/* time of last data modification */ +static void push_st_mtime (lua_State *L, STAT_STRUCT *info) { + lua_pushnumber (L, info->st_mtime); +} +/* time of last file status change */ +static void push_st_ctime (lua_State *L, STAT_STRUCT *info) { + lua_pushnumber (L, info->st_ctime); +} +/* file size, in bytes */ +static void push_st_size (lua_State *L, STAT_STRUCT *info) { + lua_pushnumber (L, (lua_Number)info->st_size); +} +#ifndef _WIN32 +/* blocks allocated for file */ +static void push_st_blocks (lua_State *L, STAT_STRUCT *info) { + lua_pushnumber (L, (lua_Number)info->st_blocks); +} +/* optimal file system I/O blocksize */ +static void push_st_blksize (lua_State *L, STAT_STRUCT *info) { + lua_pushnumber (L, (lua_Number)info->st_blksize); +} +#endif +static void push_invalid (lua_State *L, STAT_STRUCT *info) { + luaL_error(L, "invalid attribute name"); +#ifndef _WIN32 + info->st_blksize = 0; /* never reached */ +#endif +} + +typedef void (*_push_function) (lua_State *L, STAT_STRUCT *info); + +struct _stat_members { + const char *name; + _push_function push; +}; + +struct _stat_members members[] = { + { "mode", push_st_mode }, + { "dev", push_st_dev }, + { "ino", push_st_ino }, + { "nlink", push_st_nlink }, + { "uid", push_st_uid }, + { "gid", push_st_gid }, + { "rdev", push_st_rdev }, + { "access", push_st_atime }, + { "modification", push_st_mtime }, + { "change", push_st_ctime }, + { "size", push_st_size }, +#ifndef _WIN32 + { "blocks", push_st_blocks }, + { "blksize", push_st_blksize }, +#endif + { NULL, push_invalid } +}; + +/* +** Get file or symbolic link information +*/ +static int _file_info_ (lua_State *L, int (*st)(const char*, STAT_STRUCT*)) { + int i; + STAT_STRUCT info; + const char *file = luaL_checkstring (L, 1); + + if (st(file, &info)) { + lua_pushboolean (L, false); + lua_pushfstring (L, "cannot obtain information from file `%s'", file); + return 2; + } + if (lua_isstring (L, 2)) { + int v; + const char *member = lua_tostring (L, 2); + if (strcmp (member, "mode") == 0) v = 0; +#ifndef _WIN32 + else if (strcmp (member, "blocks") == 0) v = 11; + else if (strcmp (member, "blksize") == 0) v = 12; +#endif + else /* look for member */ + for (v = 1; members[v].name; v++) + if (*members[v].name == *member) + break; + /* push member value and return */ + lua_pushboolean(L, true); + members[v].push (L, &info); + return 2; + } + + lua_pushboolean(L, true); + if (!lua_istable (L, 3)) + /* creates a table if none is given */ + lua_newtable (L); + /* stores all members in table on top of the stack */ + for (i = 0; members[i].name; i++) { + lua_pushstring (L, members[i].name); + members[i].push (L, &info); + lua_rawset (L, -3); + } + return 2; +} + + +/* +** Get file information using stat. +*/ +static int file_info (lua_State *L) { + return _file_info_ (L, STAT_FUNC); +} + + +/* +** Get symbolic link information using lstat. +*/ +static int link_info (lua_State *L) { + return _file_info_ (L, LSTAT_FUNC); +} + + +/* +** Assumes the table is on top of the stack. +*/ +static void set_info (lua_State *L) { + lua_pushliteral (L, "_COPYRIGHT"); + lua_pushliteral (L, "Copyright (C) 2003-2009 Kepler Project"); + lua_settable (L, -3); + lua_pushliteral (L, "_DESCRIPTION"); + lua_pushliteral (L, "LuaFileSystem is a Lua library developed to complement the set of functions related to file systems offered by the standard Lua distribution"); + lua_settable (L, -3); + lua_pushliteral (L, "_VERSION"); + lua_pushliteral (L, "LuaFileSystem 1.5.0"); + lua_settable (L, -3); +} + +static int get_path_separator(lua_State *L) { - /* create the dir metatable */ - luaL_newmetatable(L, DIR_METATABLE); - lua_pushstring(L, "__index"); - lua_newtable(L); - lua_pushstring(L, "next"); - lua_pushcfunction(L, dir_iter); - lua_settable(L, -3); - lua_pushstring (L, "close"); - lua_pushcfunction (L, dir_close); - lua_settable(L, -3); - lua_settable(L, -3); - lua_pushstring(L, "__gc"); - lua_pushcfunction (L, dir_close); - lua_settable(L, -3); - lua_pop(L, 1); - return 0; +#ifdef WIN32 + lua_pushstring(L, "\\"); +#else + lua_pushstring(L, "/"); +#endif + return 1; } + + +static const struct luaL_Reg fslib[] = { + {"attributes", file_info}, + {"chdir", change_dir}, + {"currentdir", get_dir}, + {"dir", dir_iter_factory}, + {"link", make_link}, + {"lock", file_lock}, + {"mkdir", make_dir}, + {"rmdir", remove_dir}, + {"symlinkattributes", link_info}, + {"setmode", lfs_f_setmode}, + {"touch", file_utime}, + {"unlock", file_unlock}, + {"lock_dir", lfs_lock_dir}, + {"get_path_separator", get_path_separator}, + {NULL, NULL}, +}; + +LUALIB_API int luaopen_fs(lua_State *L) { + dir_create_meta (L); + lock_create_meta (L); + luaL_register (L, "fs", fslib); + set_info (L); + return 1; +} \ No newline at end of file Index: nse_main.cc =================================================================== --- nse_main.cc (revision 28365) +++ nse_main.cc (working copy) @@ -254,12 +254,96 @@ return 0; } +static int nse_fetch (lua_State *L, int (*fetch)(char *, size_t, const char *)) +{ + char path[MAXPATHLEN]; + switch (fetch(path, sizeof(path), luaL_checkstring(L, 1))) + { + case 0: // no such path + lua_pushnil(L); + lua_pushfstring(L, "no path to file/directory: %s", lua_tostring(L, 1)); + break; + case 1: // file returned + lua_pushliteral(L, "file"); + lua_pushstring(L, path); + break; + case 2: // directory returned + lua_pushliteral(L, "directory"); + lua_pushstring(L, path); + break; + default: + return luaL_error(L, "nse_fetch returned bad code"); + } + return 2; +} + +static bool filename_is_absolute(const char *file) { + if (file[0] == '/') + return true; +#ifdef WIN32 + if ((file[0] != '\0' && file[1] == ':') || file[0] == '\\') + return true; +#endif + return false; +} + +/* This is a modification of nmap_fetchfile that first looks for an + * absolute file name. + */ +static int nse_fetchfile_absolute(char *path, size_t path_len, const char *file) { + if (filename_is_absolute(file)) { + if (o.debugging > 1) + log_write(LOG_STDOUT, "%s: Trying absolute path %s\n", SCRIPT_ENGINE, file); + Strncpy(path, file, path_len); + return nmap_fileexistsandisreadable(file); + } + + return nmap_fetchfile(path, path_len, file); +} + +/* This is a modification of nmap_fetchfile specialized to look for files + * in the scripts subdirectory. If the path is absolute, it is always tried + * verbatim. Otherwise, the file is looked for under scripts/, and then finally + * in the current directory. + */ +static int nse_fetchscript(char *path, size_t path_len, const char *file) { + std::string scripts_path = std::string(SCRIPT_ENGINE_LUA_DIR) + std::string(file); + int type; + + if (filename_is_absolute(file)) { + if (o.debugging > 1) + log_write(LOG_STDOUT, "%s: Trying absolute path %s\n", SCRIPT_ENGINE, file); + Strncpy(path, file, path_len); + return nmap_fileexistsandisreadable(file); + } + + // lets look in /scripts + type = nmap_fetchfile(path, path_len, scripts_path.c_str()); + + if (type == 0) { + // current directory + Strncpy(path, file, path_len); + return nmap_fileexistsandisreadable(file); + } + + return type; +} + +static int fetchscript (lua_State *L) +{ + return nse_fetch(L, nse_fetchscript); +} + +static int fetchfile_absolute (lua_State *L) +{ + return nse_fetch(L, nse_fetchfile_absolute); +} + static void open_cnse (lua_State *L) { static const luaL_Reg nse[] = { {"fetchfile_absolute", fetchfile_absolute}, {"fetchscript", fetchscript}, - {"dir", nse_readdir}, {"nsock_loop", nsock_loop}, {"key_was_pressed", key_was_pressed}, {"scan_progress_meter", scan_progress_meter}, @@ -276,11 +360,8 @@ {"xml_write_escaped", l_xml_write_escaped}, {"xml_newline", l_xml_newline}, {NULL, NULL} - }; + }; - /* create dir metatable */ - luaopen_fs(L); - lua_newtable(L); luaL_register(L, NULL, nse); /* Add some other fields */ @@ -337,6 +418,7 @@ {NSE_NMAPLIBNAME, luaopen_nmap}, // nmap bindings {NSE_BINLIBNAME, luaopen_binlib}, {BITLIBNAME, luaopen_bit}, // bit library + {FSLIBNAME, luaopen_fs}, // fs library #ifdef HAVE_OPENSSL {OPENSSLLIBNAME, luaopen_openssl}, // openssl bindings #endif Index: nselib/fs.luadoc =================================================================== --- nselib/fs.luadoc (revision 0) +++ nselib/fs.luadoc (revision 0) @@ -0,0 +1,178 @@ +--- +-- The fs module provides Nmap with a portable interface to file +-- system functions that are missing in the standard Lua libraries. The module +-- is a port of the LuaFileSystem module which was written as part of the +-- Kepler Project. +-- + +module "fs" + +--- Returns a table with the file attributes +-- If the second optional argument is given, then only the value of the named +-- attribute is returned (this use is equivalent to +-- attributes(filepath).aname, but the table is not created and +-- only one attribute is retrieved from the OS). +-- This function uses stat internally thus if the given filepath is a symbolic +-- link, it is followed (if it points to another link the chain is followed +-- recursively) and the information is about the file it refers to. To obtain +-- information about the link itself, see function +-- symlinkattributes. The attributes are described as follows; +-- attribute mode is a string, all the others are numbers, and the +-- time related attributes use the same time reference of os.time: +-- +-- dev - on Unix systems, this represents the device that +-- the inode resides on. On Windows systems, +-- represents the drive number of the disk +-- containing the file. +-- ino - on Unix systems, this represents the inode +-- number. On Windows systems this has no meaning +-- mode - string representing the associated protection +-- mode (the values could be file, +-- directory, link, +-- socket, named pipe, +-- char device, +-- block device or other) +-- nlink - number of hard links to the file. +-- uid - user-id of owner (Unix only, always 0 on +-- Windows) +-- gid - group-id of owner (Unix only, always 0 on +-- Windows) +-- rdev - on Unix systems, represents the device type, +-- for special file inodes. On Windows systems +-- represents the same as dev. +-- access - time of last access +-- modification - time of last data modification +-- change - time of last file status change +-- size - file size, in bytes +-- blocks - block allocated for file; (Unix only) +-- blksize - optimal file system I/O blocksize; (Unix only) +-- +-- @param filepath string containing the file path +-- @param aname [optional] attribute to retrieve +-- @return Status (true or false) +-- @return string or table of attributes, depending on aname +function attributes(filepath, aname) + +--- Changes the current working directory to the given path. +-- +-- @param path string containing the path +-- @return Status (true or false) +-- @return Error string (if status is false). +function chdir(path) + +--- Returns a string with the current working directory +-- +-- @return Status (true or false) +-- @return Directory (if status is true) or error string (if status is false). +function currentdir() + +--- Returns a directory iterator listing the contents of the given path +-- Each time the iterator is called with dir_obj it returns a directory entry's +-- name as a string, or nil if there are no more entries. +-- +-- @param path string containing the directory to list +-- @return iterator function returning the next file or nil when done +-- @usage for f in fs.dir("/tmp") do print("file:", f) end +function dir(path) + +--- Creates a hard or symbolic link to a file +-- +-- @param filepath string containing the object to link to +-- @param linkname string containing the name of the link +-- @param symbolic [optional] boolean true if link is symbolic +-- @return Status (true or false) +-- @return Error string (if status is false). +function link(filepath, linkname, symbolic) + +--- Locks a file or a part of it. +-- This function works on open files; the file handle should be specified as +-- the first argument. The string mode could be either r (for a read/shared +-- lock) or w (for a write/exclusive lock). The optional arguments start and +-- length can be used to specify a starting point and its length; both should +-- be numbers. +-- +-- @param filehandle containing the file handle of an already opened file +-- @param mode string specifying whether to use a write w +-- or read r lock. +-- @param start [optional] number specifying the starting point +-- @param length [optional] number specifying the length +-- @return Status (true or false) +-- @return Error string (if status is false). +function lock(filehandle, mode, start, length) + +--- Creates a new directory. +-- The parent directory has to exist otherwise the operation will fail +-- +-- @param path string containing the directory name to create +-- @return Status (true or false) +-- @return Error string (if status is false). +function mkdir(path) + + +--- Removes an existing directory. +-- The directory has to be empty, otherwise the operation will fail +-- +-- @param path string containing the directory name to remove +-- @return Status (true or false) +-- @return Error string (if status is false). +function rmdir(path) + +--- Identical to attributes except that it obtains information +-- about the link itself (not the file it refers to). +-- This function is not available in Windows so you may want to make sure +-- that symlinkattributes exists before using it. +-- +-- @see attributes +-- @param filepath string containing the file path +-- @param aname [optional] attribute to retrieve +-- @return Status (true or false) +-- @return string or table of attributes, depending on aname or error string +-- (if status is false). +function symlinkattributes(filepath, aname) + + +--- Sets the writing mode for a file. +-- The mode string can be either binary or text. +-- This function is only available in Windows, so you may want to make sure +-- that setmode exists before using it. +-- +-- @param file file handle containing the file handle of an already opened file +-- @param mode string containing the mode which is either binary +-- or text +-- @return string containing previous mode string for the file +function setmode(file, mode) + +--- Set access and modification times of a file. +-- This function is a bind to utime function. Times are provided in seconds +-- (which should be generated with Lua standard function os.time). If the +-- modification time is omitted, the access time provided is used; if both +-- times are omitted, the current time is used. +-- +-- @param filepath string containing the file path +-- @return Status (true or false) +-- @return Error string (if status is false). +function touch(filepath) + +--- Unlocks a file or a part of it. +-- +-- @param filehandle containing the file handle of an already opened file +-- @param start [optional] number specifying the starting point +-- @param length [optional] number specifying the length +-- @return Status (true or false) +-- @return Error string (if status is false). +function unlock(filehandle, start, length) + +--- Creates a lockfile (called lockfile.lfs) in path if it does not exist +-- and returns the lock. To free the the lock call lock:free(). +-- +-- @param path string containing the directory name +-- @return Status (true or false) +-- @return Error string (if status is false). +function lock_dir(path) + +--- Returns the OS specific directory path separator. +-- +-- @return sep string containing the path separator +function get_path_separator() + + Index: nse_fs.h =================================================================== --- nse_fs.h (revision 28365) +++ nse_fs.h (working copy) @@ -1,9 +1,15 @@ #ifndef NSE_FS #define NSE_FS +#define FSLIBNAME "fs" -int fetchscript (lua_State *L); +/* Define 'chdir' for systems that do not implement it */ +#ifdef NO_CHDIR +#define chdir(p) (-1) +#define chdir_error "Function 'chdir' not provided by system" +#else +#define chdir_error strerror(errno) +#endif -int fetchfile_absolute (lua_State *L); int nse_readdir (lua_State *L); Index: nse_main.lua =================================================================== --- nse_main.lua (revision 28365) +++ nse_main.lua (working copy) @@ -114,6 +114,7 @@ local sort = table.sort; local nmap = require "nmap"; +local fs = require "fs"; local cnse, rules = ...; -- The NSE C library and Script Rules @@ -676,7 +677,7 @@ chosen_scripts[#chosen_scripts+1] = script; files_loaded[path] = true; elseif t == "directory" then - for f in cnse.dir(path) do + for f in fs.dir(path) do local file = path .."/".. f if find(f, "%.nse$") and not files_loaded[file] then script_params.selection = "directory"; @@ -1079,7 +1080,7 @@ script_database_path = path.."script.db"; local db = assert(open(script_database_path, 'w')); local scripts = {}; - for f in cnse.dir(path) do + for f in fs.dir(path) do if match(f, '%.nse$') then scripts[#scripts+1] = path.."/"..f; end