From ecca76f1ba74f265e1e081366037fbe560ae64a8 Mon Sep 17 00:00:00 2001 From: Tobias Ulmer Date: Fri, 27 Sep 2013 16:49:05 +0200 Subject: [PATCH] Add new mkdir(), mkdir_recursive and parse_mode() functions Replace all calls to the external mkdir tool with our own mkdir code. Signed-off-by: Tobias Ulmer --- generic/e2lib.lua | 111 ++++++++++++++++++------ generic/generic_git.lua | 2 +- generic/le2lib.c | 163 +++++++++++++++++++++++++++++++++++ generic/transport.lua | 29 ++++--- global/e2-create-project.lua | 4 +- local/e2-cf.lua | 4 +- local/e2build.lua | 24 +++--- local/e2tool.lua | 2 +- plugins/cvs.lua | 4 +- plugins/files.lua | 16 ++-- plugins/git.lua | 11 ++- plugins/svn.lua | 4 +- 12 files changed, 301 insertions(+), 73 deletions(-) diff --git a/generic/e2lib.lua b/generic/e2lib.lua index 4acf196..9a616ef 100644 --- a/generic/e2lib.lua +++ b/generic/e2lib.lua @@ -50,6 +50,7 @@ package.loaded["e2lib"] = e2lib local buildconfig = require("buildconfig") local lock = require("lock") local err = require("err") +local errno = require("errno") local plugin = require("plugin") local tools = require("tools") local cache = require("cache") @@ -1761,20 +1762,76 @@ function e2lib.rmdir(dir) return true end ---- call the mkdir command --- @param dir string: the directory name --- @param flags string: flags to pass to mkdir --- @return bool --- @return the last line ouf captured output -function e2lib.mkdir(dir, flags) - flags = flags or "" - assert(type(dir) == "string") - assert(string.len(dir) > 0) - assert(type(flags) == "string") +--- Parse a mode string in the form ugo+rwx etc. +-- @param modestring Mode string. +-- @return Numeric mode or false on error. +-- @return Error object on failure. +function e2lib.parse_mode(modestring) + local rc, errstring = le2lib.parse_mode(modestring) + + if not rc then + return false, err.new("cannot parse mode string '%s': %s", modestring, + errstring) + end - -- TODO: quote flags as well - local args = string.format("%s %s", flags, e2lib.shquote(dir)) - return e2lib.call_tool("mkdir", args) + return rc +end + +--- Create a single directory. +-- @param dir Directory name (string). +-- @param mode Numeric mode for directory creation (umask restrictions apply). +-- @return True on success, false on error. +-- @return Error object on failure. +-- @return Errno (number) on failure. +function e2lib.mkdir(dir, mode) + local re + + if mode == nil then + mode, re = e2lib.parse_mode("a+rwx") + if not mode then + return false, re + end + end + + local rc, errstring, errnum = le2lib.mkdir(dir, mode) + + if not rc then + return false, err.new("cannot create directory %q: %s", dir, + errstring), errnum + end + + return true +end + +--- Create zero or more directories making up a path. Some or all directories +-- in the path may already exist. +-- @param path Path name (string). +-- @param mode Numeric mode for directory creation (umask restrictions apply). +-- @return True on success, false on error. +-- @return Error object on failure. +function e2lib.mkdir_recursive(path, mode) + local dirs = e2lib.parentdirs(path) + local rc, re, errnum, eexist + + if mode == nil then + mode, re = e2lib.parse_mode("ugo+rwx") + if not mode then + return false, re + end + end + + eexist = errno.def2errnum("EEXIST") + + for _,dir in ipairs(dirs) do + rc, re, errnum = e2lib.mkdir(dir, mode) + if not rc then + if errnum ~= eexist then + return false, re + end + end + end + + return true end --- call the patch command @@ -2137,26 +2194,24 @@ function e2lib.uname_machine() return machine end ---- return a table of parent directories +--- Return a table of parent directories going deeper one directory at a time. +-- Example: "/foo/", "/foo/bar", ... -- @param path string: path -- @return a table of parent directories, including path. function e2lib.parentdirs(path) - local i = 2 + local start = 2 + local stop local t = {} - local stop = false - while true do - local px - local p = path:find("/", i) - if not p then - p = #path - stop = true - end - px = path:sub(1, p) - table.insert(t, px) - i = p + 1 - if stop then - break + local parent + + while stop ~= path:len() do + stop = path:find("/", start) + if not stop then + stop = path:len() end + start = stop + 1 + parent = path:sub(1, stop) + table.insert(t, parent) end return t end diff --git a/generic/generic_git.lua b/generic/generic_git.lua index e189eed..fed8980 100644 --- a/generic/generic_git.lua +++ b/generic/generic_git.lua @@ -650,7 +650,7 @@ function generic_git.new_repository(c, lserver, llocation, rserver, rlocation, f if not lurl then return false, e:cat(re) end - local rc = e2lib.mkdir(string.format("/%s", lurl.path), "-p") + local rc = e2lib.mkdir_recursive(e2lib.join("/", lurl.path)) if not rc then return false, e:cat("can't create path to local git repository") end diff --git a/generic/le2lib.c b/generic/le2lib.c index e12a0a0..46c0fec 100644 --- a/generic/le2lib.c +++ b/generic/le2lib.c @@ -526,6 +526,167 @@ do_rmdir(lua_State *lua) return 1; } +static unsigned calc_mode(unsigned, int, int, unsigned); +enum { OWN_USER = 0x1, OWN_GROUP = 0x2, OWN_OTHER = 0x4 }; + +static int +do_parse_mode(lua_State *lua) +{ + const char *modestr = luaL_checkstring(lua, 1); + size_t len = strlen(modestr); + size_t pos; + enum { PARSE_OWNERS, PARSE_OP, PARSE_PERMS, PARSE_COMMA }; + + int state = PARSE_OWNERS; + int owners = 0; + char op = 0; + + mode_t protection = 0; + mode_t mode = 0; + + + for (pos = 0; pos <= len; pos++) { + switch (state) { + case PARSE_OWNERS: + switch (modestr[pos]) { + case 'u': owners |= OWN_USER; break; + case 'g': owners |= OWN_GROUP; break; + case 'o': owners |= OWN_OTHER; break; + case 'a': owners |= (OWN_USER|OWN_GROUP|OWN_OTHER); + break; + case 0: + lua_pushboolean(lua, 0); + lua_pushstring(lua, "unexpected end of mode string"); + return 2; + default: + state = PARSE_OP; + pos--; + break; + } + + if (owners == 0) + owners |= (OWN_USER|OWN_GROUP|OWN_OTHER); + + break; + case PARSE_OP: + switch (modestr[pos]) { + case '+': + case '-': + case '=': + op = modestr[pos]; + state = PARSE_PERMS; + break; + case 0: + lua_pushboolean(lua, 0); + lua_pushstring(lua, "unexpected end of mode string"); + return 2; + default: + lua_pushboolean(lua, 0); + lua_pushstring(lua, "unknown operator"); + + return 2; + } + + break; + case PARSE_PERMS: + switch (modestr[pos]) { + case 'r': protection |= S_IRUSR; break; + case 'w': protection |= S_IWUSR; break; + case 'x': + case 'X': + protection |= S_IXUSR; break; + /*case 's': break; + case 't': break; + case 'u': break; + case 'g': break; + case 'o': break;*/ + case ',': + state = PARSE_COMMA; + pos--; + break; + case 0: + /* end of string, end of parse */ + break; + default: + lua_pushboolean(lua, 0); + lua_pushstring(lua, "unknown protection mode"); + + return 2; + } + + break; + case PARSE_COMMA: + mode = calc_mode(mode, owners, op, protection); + state = PARSE_OWNERS; + owners = op = protection = 0; + + break; + } + } + + mode = calc_mode(mode, owners, op, protection); + + lua_pushinteger(lua, mode); + + return 1; +} + +static mode_t +calc_mode(mode_t mode, int owners, int op, mode_t protection) +{ + int i, shift; + + /* Loop over all possible owners and calc shift */ + for (i = OWN_USER; i <= OWN_OTHER ; i <<= 1) { + if (owners & i) { + switch (i) { + case OWN_USER: shift = 0; break; + case OWN_GROUP: shift = 3; break; + case OWN_OTHER: shift = 6; break; + } + } else { + continue; + } + + switch (op) { + case '+': + mode |= (protection>>shift); + break; + case '-': + mode &= ~(protection>>shift); + break; + case '=': + /* reset protection, leaving suid/sguid alone */ + mode = mode & ~0777; + mode |= (protection>>shift); + break; + } + } + + return mode; +} + +static int +do_mkdir(lua_State *lua) +{ + const char *pathname = luaL_checkstring(lua, 1); + mode_t mode = 0777; + + if (lua_gettop(lua) > 1) + mode = luaL_checkinteger(lua, 2); + + if (mkdir(pathname, mode) != 0) { + lua_pushboolean(lua, 0); + lua_pushstring(lua, strerror(errno)); + lua_pushinteger(lua, errno); + + return 3; + } + + lua_pushboolean(lua, 1); + return 1; +} + static int do_kill(lua_State *lua) { @@ -610,6 +771,8 @@ static luaL_Reg lib[] = { { "getpid", do_getpid }, { "hardlink", do_hardlink }, { "kill", do_kill }, + { "mkdir", do_mkdir }, + { "parse_mode", do_parse_mode }, { "poll", poll_fd }, { "rmdir", do_rmdir }, { "setenv", do_setenv }, diff --git a/generic/transport.lua b/generic/transport.lua index 31febbb..7fd5a07 100644 --- a/generic/transport.lua +++ b/generic/transport.lua @@ -175,7 +175,7 @@ function transport.fetch_file(surl, location, destdir, destname) return false, e:cat(re) end -- create the destination directory - rc, re = e2lib.mkdir(destdir, "-p") + rc, re = e2lib.mkdir_recursive(destdir) if not rc then return false, e:cat(re) end @@ -281,24 +281,25 @@ function transport.push_file(sourcefile, durl, location, push_permissions, try_h if u.transport == "file" then local destdir = string.format("/%s", e2lib.dirname(u.path)) local destname = e2lib.basename(u.path) - -- split directories, to apply permissions to all newly - -- created parent directories, too. - local dirs = e2lib.parentdirs(destdir) - local mkdir_perm = "" + local mode = nil + + if push_permissions then + mode, re = e2lib.parse_mode(push_permissions) + if not mode then + return false, e:cat(re) + end + end + + rc, re = e2lib.mkdir_recursive(destdir, mode) + if not rc then + return false, e:cat(re) + end + local rsync_argv = {} if push_permissions then - mkdir_perm = string.format("--mode \"%s\"", push_permissions) table.insert(rsync_argv, "--perms") table.insert(rsync_argv, "--chmod") table.insert(rsync_argv, push_permissions) - - end - for _,d in ipairs(dirs) do - local mkdir_flags = string.format("-p %s", mkdir_perm) - rc, re = e2lib.mkdir(d, mkdir_flags) - if not rc then - return false, e:cat(re) - end end local done = false local dst = e2lib.join(destdir, destname) diff --git a/global/e2-create-project.lua b/global/e2-create-project.lua index c399769..6487b1c 100644 --- a/global/e2-create-project.lua +++ b/global/e2-create-project.lua @@ -121,7 +121,7 @@ local function e2_create_project(arg) } for _,f in ipairs(files) do local dir = e2lib.dirname(f.filename) - rc, re = e2lib.mkdir(dir, "-p") + rc, re = e2lib.mkdir_recursive(dir) if not rc then return false, e:cat(re) end @@ -209,7 +209,7 @@ local function e2_create_project(arg) } for _,f in ipairs(files) do local dir = e2lib.dirname(f.filename) - rc, re = e2lib.mkdir(dir, "-p") + rc, re = e2lib.mkdir_recursive(dir) if not rc then return false, e:cat(re) end diff --git a/local/e2-cf.lua b/local/e2-cf.lua index d00f6e8..07eb787 100644 --- a/local/e2-cf.lua +++ b/local/e2-cf.lua @@ -165,7 +165,7 @@ local function newsource(info, ...) return false, e:cat(re) end - local rc, re = e2lib.mkdir(cfdir, "-p") + local rc, re = e2lib.mkdir_recursive(cfdir) if not rc then return false, e:cat(re) end @@ -253,7 +253,7 @@ local function newresult(info, ...) return false, e:cat(re) end - local rc, re = e2lib.mkdir(cfdir, "-p") + local rc, re = e2lib.mkdir_recursive(cfdir) if not rc then return false, e:cat(re) end diff --git a/local/e2build.lua b/local/e2build.lua index 3f65fb1..006a56c 100644 --- a/local/e2build.lua +++ b/local/e2build.lua @@ -68,7 +68,7 @@ local function linklast(info, r, return_flags) if not lnk then return false, e:cat(re) end - rc, re = e2lib.mkdir(e2lib.dirname(lnk), "-p") -- create the directory + rc, re = e2lib.mkdir_recursive(e2lib.dirname(lnk)) if not rc then return false, e:cat(re) end @@ -256,7 +256,7 @@ local function chroot_lock(info, r, return_flags) local res = info.results[r] local rc, re local e = err.new("error locking chroot") - rc, re = e2lib.mkdir(res.build_config.c, "-p") + rc, re = e2lib.mkdir_recursive(res.build_config.c) if not rc then return false, e:cat(re) end @@ -284,7 +284,7 @@ local function setup_chroot(info, r, return_flags) local e = err.new("error setting up chroot") -- create the chroot path and create the chroot marker file without root -- permissions. That makes sure we have write permissions here. - rc, re = e2lib.mkdir(res.build_config.c, "-p") + rc, re = e2lib.mkdir_recursive(res.build_config.c) if not rc then return false, e:cat(re) end @@ -550,7 +550,7 @@ function e2build.unpack_result(info, r, dep, destdir) if not rc then return false, e:cat(re) end - rc, re = e2lib.mkdir(destdir, "-p") + rc, re = e2lib.mkdir_recursive(destdir) if not rc then return false, e:cat(re) end @@ -676,7 +676,7 @@ local function sources(info, r, return_flags) local dirs = {"out", "init", "script", "build", "root", "env", "dep"} for _, v in pairs(dirs) do local d = e2lib.join(res.build_config.T, v) - local rc, re = e2lib.mkdir(d, "-p") + local rc, re = e2lib.mkdir_recursive(d) if not rc then return false, e:cat(re) end @@ -908,7 +908,7 @@ local function store_result(info, r, return_flags) if not rc then return false, e:cat(re) end - rc, re = e2lib.mkdir("result/files", "-p") + rc, re = e2lib.mkdir_recursive("result/files") if not rc then return false, e:cat(re) end @@ -1079,7 +1079,7 @@ local function collect_project(info, r, return_flags) local e = err.new("providing project data to this build failed") -- project/proj/init/ local destdir = e2lib.join(res.build_config.T, "project/proj/init") - rc, re = e2lib.mkdir(destdir, "-p") + rc, re = e2lib.mkdir_recursive(destdir) if not rc then return false, e:cat(re) end @@ -1117,7 +1117,7 @@ local function collect_project(info, r, return_flags) f:close() -- files from the project local destdir = e2lib.join(res.build_config.T, "project/.e2/bin") - rc, re = e2lib.mkdir(destdir, "-p") + rc, re = e2lib.mkdir_recursive(destdir) if not rc then return false, e:cat(re) end @@ -1128,7 +1128,7 @@ local function collect_project(info, r, return_flags) e2lib.logf(3, "chroot group: %s", g) local grp = info.chroot.groups_byname[g] local destdir = e2lib.join( res.build_config.T, "project/chroot", g) - rc, re = e2lib.mkdir(destdir, "-p") + rc, re = e2lib.mkdir_recursive(destdir) if not rc then return false, e:cat(re) end @@ -1175,7 +1175,7 @@ local function collect_project(info, r, return_flags) e2lib.logf(3, "licence: %s", l) local lic = info.licences[l] local destdir = e2lib.join(res.build_config.T, "project/licences", l) - rc, re = e2lib.mkdir(destdir, "-p") + rc, re = e2lib.mkdir_recursive(destdir) if not rc then return false, e:cat(re) end @@ -1207,7 +1207,7 @@ local function collect_project(info, r, return_flags) end local destdir = e2lib.join(res.build_config.T, "project", e2tool.resultdir(n)) - rc, re = e2lib.mkdir(destdir, "-p") + rc, re = e2lib.mkdir_recursive(destdir) if not rc then return false, e:cat(re) end @@ -1261,7 +1261,7 @@ local function collect_project(info, r, return_flags) e2lib.logf(3, "source: %s", s) local destdir = e2lib.join(res.build_config.T, "project", e2tool.sourcedir(s)) - rc, re = e2lib.mkdir(destdir, "-p") + rc, re = e2lib.mkdir_recursive(destdir) if not rc then return false, e:cat(re) end diff --git a/local/e2tool.lua b/local/e2tool.lua index 3869de9..ac84479 100644 --- a/local/e2tool.lua +++ b/local/e2tool.lua @@ -248,7 +248,7 @@ end --- Open debug logfile. local function opendebuglogfile(info) - local rc, re = e2lib.mkdir(info.root .. "/log", "-p") + local rc, re = e2lib.mkdir_recursive(e2lib.join(info.root, "log")) if not rc then local e = err.new("error making log directory") return false, e:cat(re) diff --git a/plugins/cvs.lua b/plugins/cvs.lua index 89c89f5..c2cb0e3 100644 --- a/plugins/cvs.lua +++ b/plugins/cvs.lua @@ -406,7 +406,7 @@ function cvs.toresult(info, sourcename, sourceset, directory) local sourcedir = string.format("%s/%s", directory, source) local archive = string.format("%s.tar.gz", sourcename) local fname = string.format("%s/%s", directory, makefile) - rc, re = e2lib.mkdir(sourcedir, "-p") + rc, re = e2lib.mkdir_recursive(sourcedir) if not rc then return false, e:cat(re) end @@ -441,7 +441,7 @@ function cvs.toresult(info, sourcename, sourceset, directory) local destdir = string.format("%s/licences", directory) local fname = string.format("%s/%s.licences", destdir, archive) local licence_list = table.concat(src.licences, "\n") .. "\n" - rc, re = e2lib.mkdir(destdir, "-p") + rc, re = e2lib.mkdir_recursive(destdir) if not rc then return false, e:cat(re) end diff --git a/plugins/files.lua b/plugins/files.lua index 9f5a3a4..59f77d3 100644 --- a/plugins/files.lua +++ b/plugins/files.lua @@ -374,7 +374,7 @@ function files.prepare_source(info, sourcename, sourceset, buildpath) else if not symlink then symlink = buildpath .. "/" .. sourcename - local rc, re = e2lib.mkdir(symlink, "-p") + rc, re = e2lib.mkdir_recursive(symlink) if not rc then return false, e:cat(re) end @@ -403,7 +403,7 @@ function files.prepare_source(info, sourcename, sourceset, buildpath) destdir, destname = gen_dest_dir_name(buildpath, sourcename, file.copy, file.location) - rc, re = e2lib.mkdir(destdir, "-p") + rc, re = e2lib.mkdir_recursive(destdir) if not rc then re = err.new("creating directory failed: %s", re) return false, e:cat(re) @@ -532,9 +532,13 @@ function files.toresult(info, sourcename, sourceset, directory) e2lib.logf(4, "export file: %s", file.location) local destdir = string.format("%s/%s", directory, source) local destname = nil - e2lib.mkdir(destdir, "-p") - local rc, re = info.cache:fetch_file(file.server, - file.location, destdir, destname, {}) + + rc, re = e2lib.mkdir_recursive(destdir) + if not rc then + return false, e:cat(re) + end + rc, re = info.cache:fetch_file(file.server, file.location, destdir, + destname, {}) if not rc then return false, e:cat(re) end @@ -621,7 +625,7 @@ function files.toresult(info, sourcename, sourceset, directory) local fname = string.format("%s/%s.licences", destdir, e2lib.basename(file.location)) local licence_list = table.concat(file.licences, "\n") .. "\n" - rc, re = e2lib.mkdir(destdir, "-p") + rc, re = e2lib.mkdir_recursive(destdir) if not rc then return false, e:cat(re) end diff --git a/plugins/git.lua b/plugins/git.lua index a915489..3236a58 100644 --- a/plugins/git.lua +++ b/plugins/git.lua @@ -343,7 +343,7 @@ function git.prepare_source(info, sourcename, sourceset, buildpath) e2lib.warnf("WOTHER", "working copy seems empty") end local dir = string.format("%s/%s", buildpath, sourcename) - local rc, re = e2lib.mkdir(dir, "-p") + local rc, re = e2lib.mkdir_recursive(dir) if not rc then return false, re end @@ -523,9 +523,14 @@ function git.toresult(info, sourcename, sourceset, directory) local makefile = "makefile" local source = "source" local sourcedir = string.format("%s/%s", directory, source) - e2lib.mkdir(sourcedir, "-p") local archive = string.format("%s.tar.gz", src.name) local cmd = nil + + rc, re = e2lib.mkdir_recursive(sourcedir) + if not rc then + return false, e:cat(re) + end + if sourceset == "tag" or sourceset == "branch" then local ref = generic_git.sourceset2ref(sourceset, src.branch, src.tag) -- git archive --format=tar | gzip > @@ -566,7 +571,7 @@ function git.toresult(info, sourcename, sourceset, directory) local destdir = string.format("%s/licences", directory) local fname = string.format("%s/%s.licences", destdir, archive) local licence_list = table.concat(src.licences, "\n") .. "\n" - rc, re = e2lib.mkdir(destdir, "-p") + rc, re = e2lib.mkdir_recursive(destdir) if not rc then return false, e:cat(re) end diff --git a/plugins/svn.lua b/plugins/svn.lua index f2001e8..ac4385a 100644 --- a/plugins/svn.lua +++ b/plugins/svn.lua @@ -319,7 +319,7 @@ function svn.toresult(info, sourcename, sourceset, directory) local sourcedir = e2lib.join(directory, source) local archive = string.format("%s.tar.gz", sourcename) local fname = e2lib.join(directory, makefile) - rc, re = e2lib.mkdir(sourcedir, "-p") + rc, re = e2lib.mkdir_recursive(sourcedir) if not rc then return false, e:cat(re) end @@ -354,7 +354,7 @@ function svn.toresult(info, sourcename, sourceset, directory) local destdir = e2lib.join(directory, "licences") local fname = string.format("%s/%s.licences", destdir, archive) local licence_list = table.concat(src.licences, "\n") .. "\n" - rc, re = e2lib.mkdir(destdir, "-p") + rc, re = e2lib.mkdir_recursive(destdir) if not rc then return false, e:cat(re) end -- 2.39.5