Page MenuHomeFreeBSD

D50833.id156966.diff
No OneTemporary

D50833.id156966.diff

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

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)

Event Timeline