bin/144411: mtree(8) doesn't reject non-regular files for -X

Garrett Cooper gcooper at
Tue Mar 2 04:40:03 UTC 2010

>Number:         144411
>Category:       bin
>Synopsis:       mtree(8) doesn't reject non-regular files for -X
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Tue Mar 02 04:40:02 UTC 2010
>Originator:     Garrett Cooper
>Release:        RELENG_8
Cisco Systems, Inc
FreeBSD 8.0-STABLE FreeBSD 8.0-STABLE #2: Wed Feb  3 16:57:07 PST 2010     garrcoop at  i386
Didn't fully read the manpage for mtree(8), and I assumed that -X accepted exclusion arguments like tar does.

Turns out that wasn't the case, and mtree hangs when given directories via -X:

[root at garrcoop-fbsd /usr/home/garrcoop/head/tools/regression/lib/libc/gen]# mtree -X /boot/

The patch attached does a stat(2) on the file first to ensure that the resolved file is in fact legitimate, then proceeds to poke at the file to make sure that it's a regular file.

This is a draft patch and can and will be revised if necessary..
mtree -X /boot
See proposed patch attached.

Patch attached with submission follows:

Index: excludes.c
--- excludes.c	(revision 204532)
+++ excludes.c	(working copy)
@@ -31,6 +31,7 @@
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <sys/time.h>		/* XXX for mtree.h */
 #include <sys/queue.h>
@@ -39,6 +40,7 @@
 #include <fts.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <errno.h>
 #include "mtree.h"		/* XXX for extern.h */
 #include "extern.h"
@@ -66,10 +68,23 @@
 	FILE *fp;
 	char *line, *str;
 	struct exclude *e;
+	struct stat exclude_stat;
 	size_t len;
+	/* Let's resolve the name via stat(2) so symlinks to files pass. */
+	if (stat(name, &exclude_stat) < 0) {
+		err(1, "%s", name);
+	}
+	/* Don't let certain files like directories, fifos, etc pass. */
+	if (!S_ISREG(exclude_stat.st_mode)) {
+		/* Make the error message make sense for the directory error
+		 * case. All other values can be EINVAL. */
+		errno = S_ISDIR(exclude_stat.st_mode) ? EISDIR : EINVAL;
+		err(1, "%s", name);
+	}
 	fp = fopen(name, "r");
-	if (fp == 0)
+	if (fp == NULL)
 		err(1, "%s", name);
 	while ((line = fgetln(fp, &len)) != 0) {


More information about the freebsd-bugs mailing list