Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F144391042
D50833.id156966.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
17 KB
Referenced Files
None
Subscribers
None
D50833.id156966.diff
View Options
diff --git a/etc/mtree/BSD.usr.dist b/etc/mtree/BSD.usr.dist
--- a/etc/mtree/BSD.usr.dist
+++ b/etc/mtree/BSD.usr.dist
@@ -376,6 +376,8 @@
firmware
..
flua
+ mtree
+ ..
..
games
fortune
diff --git a/libexec/flua/share/Makefile b/libexec/flua/share/Makefile
new file mode 100644
--- /dev/null
+++ b/libexec/flua/share/Makefile
@@ -0,0 +1,8 @@
+
+BINDIR?= ${SHAREDIR}
+
+SUBDIR+= examples
+SUBDIR+= mtree
+
+.include "Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/libexec/flua/share/Makefile.inc b/libexec/flua/share/Makefile.inc
new file mode 100644
--- /dev/null
+++ b/libexec/flua/share/Makefile.inc
@@ -0,0 +1 @@
+SHAREDIR?= /usr/share/flua
diff --git a/libexec/flua/share/examples/METALOG b/libexec/flua/share/examples/METALOG
new file mode 100644
--- /dev/null
+++ b/libexec/flua/share/examples/METALOG
@@ -0,0 +1,71 @@
+#mtree 2.0
+. type=dir uname=root gname=wheel mode=0755
+./bin type=dir uname=root gname=wheel mode=0755
+./boot type=dir uname=root gname=wheel mode=0755
+./boot/defaults type=dir uname=root gname=wheel mode=0755
+./boot/dtb type=dir uname=root gname=wheel mode=0755
+./boot/dtb/allwinner type=dir uname=root gname=wheel mode=0755 tags=package=runtime
+./boot/dtb/overlays type=dir uname=root gname=wheel mode=0755 tags=package=runtime
+./boot/dtb/rockchip type=dir uname=root gname=wheel mode=0755 tags=package=runtime
+./boot/efi type=dir uname=root gname=wheel mode=0755
+./boot/firmware type=dir uname=root gname=wheel mode=0755
+./boot/fonts type=dir uname=root gname=wheel mode=0755
+./boot/images type=dir uname=root gname=wheel mode=0755
+./boot/kernel type=dir uname=root gname=wheel mode=0755
+./boot/loader.conf.d type=dir uname=root gname=wheel mode=0755 tags=package=bootloader
+./boot/lua type=dir uname=root gname=wheel mode=0755
+./boot/modules type=dir uname=root gname=wheel mode=0755
+./boot/uboot type=dir uname=root gname=wheel mode=0755
+./boot/zfs type=dir uname=root gname=wheel mode=0755
+./dev type=dir uname=root gname=wheel mode=0555
+./etc type=dir uname=root gname=wheel mode=0755
+./etc/X11 type=dir uname=root gname=wheel mode=0755
+./etc/authpf type=dir uname=root gname=wheel mode=0755
+./etc/autofs type=dir uname=root gname=wheel mode=0755
+./etc/bluetooth type=dir uname=root gname=wheel mode=0755
+./etc/cron.d type=dir uname=root gname=wheel mode=0755
+./etc/defaults type=dir uname=root gname=wheel mode=0755
+./etc/devd type=dir uname=root gname=wheel mode=0755
+./etc/dma type=dir uname=root gname=wheel mode=0755
+./etc/gss type=dir uname=root gname=wheel mode=0755
+./etc/jail.conf.d type=dir uname=root gname=wheel mode=0755
+./etc/kyua type=dir uname=root gname=wheel mode=0755 tags=package=tests
+./etc/mail type=dir uname=root gname=wheel mode=0755
+./etc/mtree type=dir uname=root gname=wheel mode=0755
+./etc/newsyslog.conf.d type=dir uname=root gname=wheel mode=0755
+./etc/ntp type=dir uname=root gname=wheel mode=0700
+./etc/pam.d type=dir uname=root gname=wheel mode=0755
+./etc/periodic type=dir uname=root gname=wheel mode=0755
+./etc/periodic/daily type=dir uname=root gname=wheel mode=0755
+./etc/periodic/monthly type=dir uname=root gname=wheel mode=0755
+./etc/periodic/security type=dir uname=root gname=wheel mode=0755
+./etc/periodic/weekly type=dir uname=root gname=wheel mode=0755
+./etc/pkg type=dir uname=root gname=wheel mode=0755
+./etc/ppp type=dir uname=root gname=wheel mode=0755
+./etc/profile.d type=dir uname=root gname=wheel mode=0755
+./etc/rc.conf.d type=dir uname=root gname=wheel mode=0755
+./etc/rc.d type=dir uname=root gname=wheel mode=0755
+./etc/security type=dir uname=root gname=wheel mode=0755
+./etc/ssh type=dir uname=root gname=wheel mode=0755
+./etc/ssl type=dir uname=root gname=wheel mode=0755
+./etc/ssl/certs type=dir uname=root gname=wheel mode=0755
+./etc/ssl/untrusted type=dir uname=root gname=wheel mode=0755
+./etc/sysctl.kld.d type=dir uname=root gname=wheel mode=0755
+./etc/syslog.d type=dir uname=root gname=wheel mode=0755
+./etc/zfs type=dir uname=root gname=wheel mode=0755
+./etc/zfs/compatibility.d type=dir uname=root gname=wheel mode=0755
+./etc/gss type=dir uname=root gname=wheel mode=0755 tags=package=utilities
+./etc/gss/mech type=file uname=root gname=wheel mode=0444 size=239 tags=package=utilities
+./etc/gss/qop type=file uname=root gname=wheel mode=0444 size=89 tags=package=utilities
+./etc/mtree type=dir uname=root gname=wheel mode=0755 tags=package=mtree
+./etc/mtree/BSD.debug.dist type=file uname=root gname=wheel mode=0444 size=1435 tags=package=mtree
+./etc/mtree/BSD.root.dist type=file uname=root gname=wheel mode=0444 size=2121 tags=package=mtree
+./etc/mtree/BSD.include.dist type=file uname=root gname=wheel mode=0444 size=5351 tags=package=mtree
+./etc/mtree/BSD.lib32.dist type=file uname=root gname=wheel mode=0444 size=377 tags=package=mtree
+./etc/mtree/BSD.tests.dist type=file uname=root gname=wheel mode=0444 size=24891 tags=package=mtree
+./etc/mtree/BSD.var.dist type=file uname=root gname=wheel mode=0444 size=2357 tags=package=mtree
+./etc/mtree/BSD.usr.dist type=file uname=root gname=wheel mode=0444 size=17353 tags=package=mtree
+./etc/termcap type=link uname=root gname=wheel mode=0755 link=/usr/share/misc/termcap tags=package=runtime
+./etc/rmt type=link uname=root gname=wheel mode=0755 link=../usr/sbin/rmt tags=package=utilities
+./etc/os-release type=link uname=root gname=wheel mode=0755 link=../var/run/os-release tags=package=runtime
+./etc/unbound type=link uname=root gname=wheel mode=0755 link=../var/unbound tags=package=unbound
diff --git a/libexec/flua/share/examples/Makefile b/libexec/flua/share/examples/Makefile
new file mode 100644
--- /dev/null
+++ b/libexec/flua/share/examples/Makefile
@@ -0,0 +1,10 @@
+
+PACKAGE?= examples
+BINDIR?= /usr/share/examples/flua
+
+FILES+= libjail.lua
+FILES+= mtree-read.lua
+FILES+= mtree-alter.lua
+FILES+= METALOG
+
+.include <bsd.prog.mk>
diff --git a/share/examples/flua/libjail.lua b/libexec/flua/share/examples/libjail.lua
rename from share/examples/flua/libjail.lua
rename to libexec/flua/share/examples/libjail.lua
diff --git a/libexec/flua/share/examples/mtree-alter.lua b/libexec/flua/share/examples/mtree-alter.lua
new file mode 100644
--- /dev/null
+++ b/libexec/flua/share/examples/mtree-alter.lua
@@ -0,0 +1,37 @@
+#!/usr/libexec/flua
+
+local mtree = require('mtree')
+
+local path = arg[0]
+-- Turn path into something useful for us
+local dirlen = #path - (string.find(path:reverse(), "/") or #path)
+path = path:sub(1, dirlen)
+if #path == 0 then
+ path = "."
+end
+
+local tree = assert(mtree.load(path .. "/METALOG"))
+
+-- waffles gets tagged as a dir automatically when we start adding entries
+-- beneath it. We set the mode to demonstrate that that type-setting doesn't
+-- override our dir mode with the auto-deduced mode.
+local leaf = tree:append("/etc/waffles")
+leaf:keyword("mode", "01777")
+leaf:keyword("tags", "package=breakfast")
+
+-- Adding new entries by absolute path may be the most convenient; append()
+-- will return the leaf node that we just added.
+leaf = tree:append("/etc/waffles/chocolate")
+leaf:keyword("type", "file")
+-- Note that any slash makes it an absolute path, so ./ is not from "pwd".
+leaf = tree:append("./etc/waffles/strawberry")
+leaf:keyword("type", "device")
+
+-- We can also chdir() to the path if we're going to be doing a lot of additions
+-- in a row to it.
+tree:chdir("/etc/waffles")
+leaf = tree:append("cherry")
+leaf:keyword("type", "link")
+leaf:keyword("link", "/nonexistent")
+
+print(tree:dump(true))
diff --git a/libexec/flua/share/examples/mtree-read.lua b/libexec/flua/share/examples/mtree-read.lua
new file mode 100644
--- /dev/null
+++ b/libexec/flua/share/examples/mtree-read.lua
@@ -0,0 +1,12 @@
+#!/usr/libexec/flua
+
+local filename = arg[1]
+local mtree = require('mtree')
+
+if not filename then
+ io.stderr:write("usage: " .. arg[0] .. " [file]\n")
+ os.exit(1)
+end
+
+local tree = assert(mtree.load(filename))
+print(tree:dump(true))
diff --git a/libexec/flua/share/mtree/Makefile b/libexec/flua/share/mtree/Makefile
new file mode 100644
--- /dev/null
+++ b/libexec/flua/share/mtree/Makefile
@@ -0,0 +1,7 @@
+BINDIR?= ${SHAREDIR}/mtree
+
+FILES+= init.lua
+FILES+= parser.lua
+FILES+= tree.lua
+
+.include <bsd.prog.mk>
diff --git a/libexec/flua/share/mtree/init.lua b/libexec/flua/share/mtree/init.lua
new file mode 100644
--- /dev/null
+++ b/libexec/flua/share/mtree/init.lua
@@ -0,0 +1,20 @@
+--
+-- Copyright (c) 2025 Kyle Evans <kevans@FreeBSD.org>
+--
+-- SPDX-License-Identifier: BSD-2-Clause
+--
+
+local mtree = {}
+local parser = require('mtree.parser')
+
+mtree.parse = parser.parse
+function mtree.load(filename)
+ local file, err = io.open(filename, "r")
+ if not file then
+ return nil, err
+ end
+ local contents = file:read("a")
+ return mtree.parse(contents)
+end
+
+return mtree
diff --git a/libexec/flua/share/mtree/parser.lua b/libexec/flua/share/mtree/parser.lua
new file mode 100644
--- /dev/null
+++ b/libexec/flua/share/mtree/parser.lua
@@ -0,0 +1,127 @@
+--
+-- Copyright (c) 2025 Kyle Evans <kevans@FreeBSD.org>
+--
+-- SPDX-License-Identifier: BSD-2-Clause
+--
+
+local tree = require('mtree.tree')
+local parser = {}
+
+function parser.parse(mtree)
+ local count = 0
+ local defaults = {}
+ local parsed_tree = tree:new()
+
+ -- Returns key, value pair
+ local function parse_keyword(keyword)
+ local key = keyword:match("^[^=]+")
+ local value = keyword:sub(#key + 2)
+
+ if not key then
+ return nil, "Malformed keyword: " .. keyword
+ end
+
+ return key, value
+ end
+
+ -- Processes an entry, returns the directory name part of it so that the
+ -- parent can chdir() into it.
+ local function process_entry(line)
+ local dir = line:match("[^%s]+")
+ local dirent, err = parsed_tree:append(dir)
+ local kwords_set = {}
+
+ if not dirent then
+ return nil, err
+ end
+
+ line = line:sub(#dir + 1)
+
+ for kword in line:gmatch("[^%s]+") do
+ local k, v = parse_keyword(kword)
+ if not k then
+ return nil, v
+ end
+
+ kwords_set[k] = 1
+ dirent:keyword(k, v)
+ end
+
+ for k, v in pairs(defaults) do
+ if not kwords_set[k] then
+ dirent:keyword(k, v)
+ end
+ end
+
+ return dir
+ end
+
+ for line in mtree:gmatch("[^\n]+") do
+ local dir, err, indent, ok
+
+ count = count + 1
+
+ -- First line may be a version spec.
+ if count == 1 and line:match("^#mtree") then
+ tree:version(line:match("mtree v([%d]+%.[%d]+)"))
+ goto next
+ end
+
+ -- Strip off any comments and any leading whitespace. Internal
+ -- whitespace is significant, so we'll leave that alone.
+ line = line:match("^[^#]+") or ""
+ indent = line:match("^[%s]+") or ""
+ line = line:sub(#indent + 1)
+
+ -- Ignore empty lines
+ if #line == 0 then
+ goto next
+ end
+
+ if line:match("^/set") or line:match("^/unset") then
+ local command = line:match("^/u?n?set")
+ local is_set = command == "/set"
+
+ line = line:sub(#command + 1)
+
+ for kword in line:gmatch("[^%s]+") do
+ if not is_set then
+ if kword:match("=") then
+ return nil, "Malformed /unset"
+ end
+
+ defaults[kword] = nil
+ else
+ local k, v = parse_keyword(kword, true)
+ if not k then
+ return nil, v
+ end
+
+ defaults[k] = v
+ end
+ end
+
+ goto next
+ end
+
+ dir = line
+ if line ~= ".." then
+ dir, err = process_entry(line)
+ if not dir then
+ return nil, err
+ end
+ end
+
+ ok, err = parsed_tree:chdir(dir)
+ if not ok then
+ return nil, err
+ end
+
+ ::next::
+ end
+
+-- io.stderr:write(tostring(parsed_tree) .. "\n")
+ return parsed_tree
+end
+
+return parser
diff --git a/libexec/flua/share/mtree/tree.lua b/libexec/flua/share/mtree/tree.lua
new file mode 100644
--- /dev/null
+++ b/libexec/flua/share/mtree/tree.lua
@@ -0,0 +1,284 @@
+--
+-- Copyright (c) 2025 Kyle Evans <kevans@FreeBSD.org>
+--
+-- SPDX-License-Identifier: BSD-2-Clause
+--
+
+local inherited = {
+ -- Almost certainly we want the same ownership as the parent,
+ -- the caller can override that.
+ "uname", "gname", "uid", "gid",
+
+ -- We use tags mostly for the package, but also to signify
+ -- config files. If we're a leaf entry, the parent should only
+ -- really have package tags if anything, and it doesn't hurt to
+ -- inherit those.
+ "tags",
+}
+
+local function encoded_name(name)
+ local function needescape(c)
+ -- Printable ASCII do not need escaping, with exception to a
+ -- backslash.
+ return not (c ~= 0x5c and c >= 0x20 and c < 0x7f)
+ end
+
+ local encoded = ""
+ for i = 1, #name do
+ local c = name:byte(i)
+
+ if not needescape(c) then
+ encoded = encoded .. string.char(c)
+ else
+ encoded = encoded .. string.format("\\%.3o", c)
+ end
+ end
+
+ return encoded
+end
+local function dirent_name(name)
+ for oct in name:gmatch("\\[0-7][0-7][0-7]") do
+ local rep = tonumber(oct:sub(2), 8)
+
+ name = name:gsub(oct, string.char(rep))
+ end
+
+ return name
+end
+
+local dirent = {}
+-- XXX Note that new dirents inherit the parent uname/gname/tags by default.
+function dirent:new(tree, parent, name)
+ local obj = setmetatable({}, self)
+ self.__index = self
+
+ if name:match("/") then
+ return nil, "Illegal filename: " .. name
+ end
+ obj.name = dirent_name(name)
+ obj.mtree_name = encoded_name(obj.name)
+ obj.parent = parent
+ obj.tree = tree
+ obj.children = {}
+ obj._keywords = {}
+
+ if parent then
+ for _, kword in ipairs(inherited) do
+ obj:keyword(kword, parent:keyword(kword))
+ end
+
+ local ptype = parent:keyword("type")
+ if not ptype then
+ parent:keyword("type", "dir")
+ elseif ptype ~= "dir" then
+ return nil, "Entry added to non-directory type"
+ end
+ end
+
+ return obj
+end
+function dirent:add(de)
+ self.children[de.name] = de
+end
+function dirent:child(path)
+ return self.children[path]
+end
+function dirent:keyword(key, ...)
+ local args = { ... }
+
+ -- A bit of a hack: dirent:keyword("foo") should return the value of
+ -- foo, but dirent:keyword("foo", nil) should unset it.
+ if #args == 0 then
+ return self._keywords[key]
+ end
+
+ local value = args[1]
+ self._keywords[key] = value
+
+ -- Set a mode if they're setting a type, but let's not override an
+ -- existing mode. These are just reasonable defaults.
+ if key == "type" and not self._keywords["mode"] then
+ if value == "dir" then
+ self._keywords["mode"] = "0755"
+ else
+ self._keywords["mode"] = "0644"
+ end
+ end
+ return true
+end
+function dirent:dump(absolute_path, pretty)
+ local strv = self.mtree_name
+
+ if absolute_path and self.parent ~= self then
+ local parent = self
+ local cmp
+
+ repeat
+ parent = parent.parent
+ cmp = parent.mtree_name
+ strv = cmp .. "/" .. strv
+ until cmp == "."
+ end
+ if self._keywords then
+ local count = 0
+ local pathsep = " "
+ local kwdelim = (pretty and ", ") or " "
+
+ if not absolute_path then
+ pathsep = string.rep(" ", 8)
+ end
+ for k, v in pairs(self._keywords) do
+ if count == 0 then
+ if not pretty then
+ strv = strv .. pathsep
+ else
+ strv = strv .. "["
+ end
+ else
+ strv = strv .. kwdelim
+ end
+
+ strv = strv .. k .. "=" .. v
+ count = count + 1
+ end
+
+ if count > 0 and pretty then
+ strv = strv .. "]"
+ end
+ end
+
+ return strv
+end
+function dirent:__tostring()
+ return self:dump(false, true)
+end
+
+local tree = {}
+function tree:new()
+ local obj = setmetatable({}, self)
+ self.__index = self
+
+ local root = assert(dirent:new(obj, nil, "."))
+ root.parent = root
+
+ obj._version = nil
+ obj.root = root
+ obj.pwd = root
+
+ return obj
+end
+local function traverse(self, path, create_missing)
+ local last_created
+ local is_abs = path:match("/")
+ local pwd = (is_abs and self.root) or self.pwd
+
+ for cmp in path:gmatch("[^/]+") do
+ local next
+
+ if cmp == ".." then
+ next = pwd.parent
+ elseif cmp == "." then
+ next = pwd
+ else
+ next = pwd:child(cmp)
+ end
+
+ -- Just construct the missing entry
+ if not next and create_missing then
+ local de, err = dirent:new(self, pwd, cmp)
+
+ if not de then
+ return nil, err
+ end
+
+ -- We now know that the last dirent we created was not
+ -- a leaf, so we can assume it's a directory and set the
+ -- type appropriately.
+ if last_created and not last_created:keyword("type") then
+ last_created:keyword("type", "dir")
+ end
+
+ pwd:add(de)
+ last_created = de
+ next = de
+ elseif not next then
+ return nil, "Missing component"
+ end
+
+ pwd = next
+ end
+
+ return pwd
+end
+
+function tree:append(path)
+ return traverse(self, path, true)
+end
+function tree:chdir(path)
+ local new_pwd, err = traverse(self, path)
+ if not new_pwd then
+ return nil, err
+ end
+
+ self.pwd = new_pwd
+ return true
+end
+function tree:get(path)
+ local getdir, err = traverse(self, path)
+ if not getdir then
+ return nil, err
+ end
+ return getdir
+end
+function tree:version(newvers)
+ if newvers then
+ self._version = newvers
+ end
+
+ return self._version or "2.0"
+end
+function tree:dump(pretty)
+ local pwd = self.root
+ local version = self:version()
+ local strv
+
+ if not pretty then
+ strv = "#mtree " .. version .. "\n"
+ else
+ strv = ""
+ end
+
+ local major_version = tonumber(version:match("^([0-9]+)"))
+ local absolute_path = not pretty and major_version >= 2
+ local function walk(node, indentlvl)
+ local sorted = {}
+ local indentation = ""
+
+ if not absolute_path then
+ indentation = string.rep("\t", indentlvl)
+ end
+
+ for name in pairs(node.children) do
+ sorted[#sorted + 1] = name
+ end
+
+ table.sort(sorted)
+ strv = strv .. indentation ..
+ node:dump(absolute_path, pretty) .. "\n"
+ for _, name in ipairs(sorted) do
+ local de = node.children[name]
+ walk(de, indentlvl + 1)
+ end
+ if not pretty and not absolute_path then
+ strv = strv .. indentation .. "..\n"
+ end
+ end
+
+ walk(pwd, 0)
+ return strv
+end
+function tree:__tostring()
+ return self:dump(false)
+end
+
+return tree
diff --git a/share/examples/Makefile b/share/examples/Makefile
--- a/share/examples/Makefile
+++ b/share/examples/Makefile
@@ -13,7 +13,6 @@
drivers \
etc \
find_interface \
- flua \
indent \
ipfw \
jails \
@@ -91,9 +90,6 @@
README \
find_interface.c
-SE_DIRS+= flua
-SE_FLUA= libjail.lua
-
SE_DIRS+= indent
SE_INDENT= indent.pro
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Feb 9, 5:03 AM (17 h, 33 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28540191
Default Alt Text
D50833.id156966.diff (17 KB)
Attached To
Mode
D50833: Initial version of an mtree module
Attached
Detach File
Event Timeline
Log In to Comment