include $(TOPDIR)/include/builddefs
LTCOMMAND = mkfs.xfs
+XFS_PROTOFILE = xfs_protofile
HFILES =
CFILES = proto.c xfs_mkfs.c
$(LIBUUID) $(LIBINIH) $(LIBURCU) $(LIBPTHREAD)
LTDEPENDENCIES += $(LIBXFS) $(LIBXCMD) $(LIBFROG)
LLDFLAGS = -static-libtool-libs
+DIRT = $(XFS_PROTOFILE)
-default: depend $(LTCOMMAND) $(CFGFILES)
+default: depend $(LTCOMMAND) $(CFGFILES) $(XFS_PROTOFILE)
include $(BUILDRULES)
+$(XFS_PROTOFILE): $(XFS_PROTOFILE).in
+ @echo " [SED] $@"
+ $(Q)$(SED) -e "s|@pkg_version@|$(PKG_VERSION)|g" < $< > $@
+ $(Q)chmod a+x $@
+
install: default
$(INSTALL) -m 755 -d $(PKG_SBIN_DIR)
$(LTINSTALL) -m 755 $(LTCOMMAND) $(PKG_SBIN_DIR)
+ $(INSTALL) -m 755 $(XFS_PROTOFILE) $(PKG_SBIN_DIR)
$(INSTALL) -m 755 -d $(MKFS_CFG_DIR)
$(INSTALL) -m 644 $(CFGFILES) $(MKFS_CFG_DIR)
--- /dev/null
+#!/usr/bin/python3
+
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2018-2024 Oracle. All rights reserved.
+#
+# Author: Darrick J. Wong <djwong@kernel.org>
+
+# Walk a filesystem tree to generate a protofile for mkfs.
+
+import os
+import argparse
+import sys
+import stat
+
+def emit_proto_header():
+ '''Emit the protofile header.'''
+ print('/')
+ print('0 0')
+
+def stat_to_str(statbuf):
+ '''Convert a stat buffer to a proto string.'''
+
+ if stat.S_ISREG(statbuf.st_mode):
+ type = '-'
+ elif stat.S_ISCHR(statbuf.st_mode):
+ type = 'c'
+ elif stat.S_ISBLK(statbuf.st_mode):
+ type = 'b'
+ elif stat.S_ISFIFO(statbuf.st_mode):
+ type = 'p'
+ elif stat.S_ISDIR(statbuf.st_mode):
+ type = 'd'
+ elif stat.S_ISLNK(statbuf.st_mode):
+ type = 'l'
+
+ if statbuf.st_mode & stat.S_ISUID:
+ suid = 'u'
+ else:
+ suid = '-'
+
+ if statbuf.st_mode & stat.S_ISGID:
+ sgid = 'g'
+ else:
+ sgid = '-'
+
+ perms = stat.S_IMODE(statbuf.st_mode)
+
+ return '%s%s%s%o %d %d' % (type, suid, sgid, perms, statbuf.st_uid, \
+ statbuf.st_gid)
+
+def stat_to_extra(statbuf, fullpath):
+ '''Compute the extras column for a protofile.'''
+
+ if stat.S_ISREG(statbuf.st_mode):
+ return ' %s' % fullpath
+ elif stat.S_ISCHR(statbuf.st_mode) or stat.S_ISBLK(statbuf.st_mode):
+ return ' %d %d' % (statbuf.st_rdev, statbuf.st_rdev)
+ elif stat.S_ISLNK(statbuf.st_mode):
+ return ' %s' % os.readlink(fullpath)
+ return ''
+
+def max_fname_len(s1):
+ '''Return the length of the longest string in s1.'''
+ ret = 0
+ for s in s1:
+ if len(s) > ret:
+ ret = len(s)
+ return ret
+
+def walk_tree(path, depth):
+ '''Walk the directory tree rooted by path.'''
+ dirs = []
+ files = []
+
+ for fname in os.listdir(path):
+ fullpath = os.path.join(path, fname)
+ sb = os.lstat(fullpath)
+
+ if stat.S_ISDIR(sb.st_mode):
+ dirs.append(fname)
+ continue
+ elif stat.S_ISSOCK(sb.st_mode):
+ continue
+ else:
+ files.append(fname)
+
+ for fname in files:
+ if ' ' in fname:
+ raise ValueError( \
+ f'{fname}: Spaces not allowed in file names.')
+ for fname in dirs:
+ if ' ' in fname:
+ raise Exception( \
+ f'{fname}: Spaces not allowed in file names.')
+
+ fname_width = max_fname_len(files)
+ for fname in files:
+ fullpath = os.path.join(path, fname)
+ sb = os.lstat(fullpath)
+ extra = stat_to_extra(sb, fullpath)
+ print('%*s%-*s %s%s' % (depth, ' ', fname_width, fname, \
+ stat_to_str(sb), extra))
+
+ for fname in dirs:
+ fullpath = os.path.join(path, fname)
+ sb = os.lstat(fullpath)
+ extra = stat_to_extra(sb, fullpath)
+ print('%*s%s %s' % (depth, ' ', fname, \
+ stat_to_str(sb)))
+ walk_tree(fullpath, depth + 1)
+
+ if depth > 1:
+ print('%*s$' % (depth - 1, ' '))
+
+def main():
+ parser = argparse.ArgumentParser( \
+ description = "Generate mkfs.xfs protofile for a directory tree.")
+ parser.add_argument('paths', metavar = 'paths', type = str, \
+ nargs = '*', help = 'Directory paths to walk.')
+ parser.add_argument("-V", help = "Report version and exit.", \
+ action = "store_true")
+ args = parser.parse_args()
+
+ if args.V:
+ print("xfs_protofile version @pkg_version@")
+ sys.exit(0)
+
+ emit_proto_header()
+ if len(args.paths) == 0:
+ print('d--755 0 0')
+ print('$')
+ else:
+ # Copy the first argument's stat to the rootdir
+ statbuf = os.stat(args.paths[0])
+ if not stat.S_ISDIR(statbuf.st_mode):
+ raise NotADirectoryError(path)
+ print(stat_to_str(statbuf))
+
+ # All files under each path go in the root dir, recursively
+ for path in args.paths:
+ print(': Descending path %s' % path)
+ try:
+ walk_tree(path, 1)
+ except Exception as e:
+ print(e, file = sys.stderr)
+ return 1
+
+ print('$')
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main())