git: ce878284318e - main - makefs: Handle special file types when creating a zpool

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Tue, 07 Jan 2025 14:32:31 UTC
The branch main has been updated by markj:

URL: https://cgit.FreeBSD.org/src/commit/?id=ce878284318e71217d8d8f43f7d590b6c338d3aa

commit ce878284318e71217d8d8f43f7d590b6c338d3aa
Author:     Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2025-01-07 14:31:02 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2025-01-07 14:32:20 +0000

    makefs: Handle special file types when creating a zpool
    
    Previously, anything other than a regular file, directory or symlink
    would cause makefs to exit with an assertion failure.  Make it a bit
    more resilient to user error: print a warning and skip the file.  Add a
    regression test wherein we create an image from a devfs mount.
    
    PR:             283583
    MFC after:      2 weeks
---
 usr.sbin/makefs/tests/makefs_zfs_tests.sh | 22 +++++++++++++++++
 usr.sbin/makefs/zfs/fs.c                  | 39 +++++++++++++++++++++++++------
 2 files changed, 54 insertions(+), 7 deletions(-)

diff --git a/usr.sbin/makefs/tests/makefs_zfs_tests.sh b/usr.sbin/makefs/tests/makefs_zfs_tests.sh
index aeda889d9a5c..3d5819439a73 100644
--- a/usr.sbin/makefs/tests/makefs_zfs_tests.sh
+++ b/usr.sbin/makefs/tests/makefs_zfs_tests.sh
@@ -148,6 +148,27 @@ dataset_removal_cleanup()
 	common_cleanup
 }
 
+#
+# Make sure that we can handle some special file types.  Anything other than
+# regular files, symlinks and directories are ignored.
+#
+atf_test_case devfs cleanup
+devfs_body()
+{
+	atf_check mkdir dev
+	atf_check mount -t devfs none ./dev
+
+	atf_check -e match:"skipping unhandled" $MAKEFS -s 1g -o rootpath=/ \
+	    -o poolname=$ZFS_POOL_NAME $TEST_IMAGE ./dev
+
+	import_image
+}
+devfs_cleanup()
+{
+	common_cleanup
+	umount -f ./dev
+}
+
 #
 # Make sure that we can create and remove an empty directory.
 #
@@ -842,6 +863,7 @@ atf_init_test_cases()
 	atf_add_test_case autoexpand
 	atf_add_test_case basic
 	atf_add_test_case dataset_removal
+	atf_add_test_case devfs
 	atf_add_test_case empty_dir
 	atf_add_test_case empty_fs
 	atf_add_test_case file_extend
diff --git a/usr.sbin/makefs/zfs/fs.c b/usr.sbin/makefs/zfs/fs.c
index 9413241da0c7..073dce3ce697 100644
--- a/usr.sbin/makefs/zfs/fs.c
+++ b/usr.sbin/makefs/zfs/fs.c
@@ -177,6 +177,13 @@ fsnode_isroot(const fsnode *cur)
 	return (strcmp(cur->name, ".") == 0);
 }
 
+static bool
+fsnode_valid(const fsnode *cur)
+{
+	return (cur->type == S_IFREG || cur->type == S_IFDIR ||
+	    cur->type == S_IFLNK);
+}
+
 /*
  * Visit each node in a directory hierarchy, in pre-order depth-first order.
  */
@@ -186,9 +193,11 @@ fsnode_foreach(fsnode *root, int (*cb)(fsnode *, void *), void *arg)
 	assert(root->type == S_IFDIR);
 
 	for (fsnode *cur = root; cur != NULL; cur = cur->next) {
-		assert(cur->type == S_IFREG || cur->type == S_IFDIR ||
-		    cur->type == S_IFLNK);
-
+		if (!fsnode_valid(cur)) {
+			warnx("skipping unhandled %s %s/%s",
+			    inode_type(cur->type), cur->path, cur->name);
+			continue;
+		}
 		if (cb(cur, arg) == 0)
 			continue;
 		if (cur->type == S_IFDIR && cur->child != NULL)
@@ -381,9 +390,15 @@ fs_populate_sattrs(struct fs_populate_arg *arg, const fsnode *cur,
 		 */
 		for (fsnode *c = fsnode_isroot(cur) ? cur->next : cur->child;
 		    c != NULL; c = c->next) {
-			if (c->type == S_IFDIR)
+			switch (c->type) {
+			case S_IFDIR:
 				links++;
-			objsize++;
+				/* FALLTHROUGH */
+			case S_IFREG:
+			case S_IFLNK:
+				objsize++;
+				break;
+			}
 		}
 
 		/* The root directory is its own parent. */
@@ -652,6 +667,16 @@ fs_populate_symlink(fsnode *cur, struct fs_populate_arg *arg)
 	fs_populate_sattrs(arg, cur, dnode);
 }
 
+static fsnode *
+fsnode_next(fsnode *cur)
+{
+	for (cur = cur->next; cur != NULL; cur = cur->next) {
+		if (fsnode_valid(cur))
+			return (cur);
+	}
+	return (NULL);
+}
+
 static int
 fs_foreach_populate(fsnode *cur, void *_arg)
 {
@@ -678,7 +703,7 @@ fs_foreach_populate(fsnode *cur, void *_arg)
 
 	ret = (cur->inode->flags & FI_ROOT) != 0 ? 0 : 1;
 
-	if (cur->next == NULL &&
+	if (fsnode_next(cur) == NULL &&
 	    (cur->child == NULL || (cur->inode->flags & FI_ROOT) != 0)) {
 		/*
 		 * We reached a terminal node in a subtree.  Walk back up and
@@ -694,7 +719,7 @@ fs_foreach_populate(fsnode *cur, void *_arg)
 				eclose(dir->dirfd);
 			free(dir);
 			cur = cur->parent;
-		} while (cur != NULL && cur->next == NULL &&
+		} while (cur != NULL && fsnode_next(cur) == NULL &&
 		    (cur->inode->flags & FI_ROOT) == 0);
 	}