NetBSD Problem Report #59838

From www@netbsd.org  Mon Dec 15 05:15:37 2025
Return-Path: <www@netbsd.org>
Received: from mail.netbsd.org (mail.netbsd.org [199.233.217.200])
	(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
	 key-exchange X25519 server-signature RSA-PSS (2048 bits)
	 client-signature RSA-PSS (2048 bits))
	(Client CN "mail.NetBSD.org", Issuer "mail.NetBSD.org CA" (not verified))
	by mollari.NetBSD.org (Postfix) with ESMTPS id 46D9C1A9239
	for <gnats-bugs@gnats.NetBSD.org>; Mon, 15 Dec 2025 05:15:37 +0000 (UTC)
Message-Id: <20251215051535.DFDD71A923A@mollari.NetBSD.org>
Date: Mon, 15 Dec 2025 05:15:35 +0000 (UTC)
From: jlduran@gmail.com
Reply-To: jlduran@gmail.com
To: gnats-bugs@NetBSD.org
Subject: mtree: Fix parsing 'K', 'k', with 'R' flags
X-Send-Pr-Version: www-1.0

>Number:         59838
>Category:       bin
>Synopsis:       mtree: Fix parsing 'K', 'k', with 'R' flags
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    bin-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Mon Dec 15 05:20:00 +0000 2025
>Last-Modified:  Thu Jan 08 06:15:00 +0000 2026
>Originator:     Jose Luis Duran
>Release:        trunk
>Organization:
FreeBSD
>Environment:
>Description:
It was reported on
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=219467
that an mtree command with -k and -R flags do not work as expected.

In mtree, the -k flag uses the type keyword plus the specified keywords. If the type keyword is not desired, it can be suppressed with -R type.

But issuing:
# mtree -cn -R type -k flags -p /usr/bin/ -x | grep type=

Doesn't seem to work as expected.
>How-To-Repeat:
# mtree -cn -R type -k flags -p /usr/bin/ -x | grep type=

>Fix:
A very straightforward solution is offered:

Move the parsing of the keywords for the 'K', 'k', and 'R' flags outside the getopt loop, so that the order of the flags does not matter.

Also, check for the presence of the F_TYPE flag before printing the type keyword.
---
 contrib/mtree/create.c |  3 ++-
 contrib/mtree/mtree.c  | 30 +++++++++++++++++++++---------
 2 files changed, 23 insertions(+), 10 deletions(-)

diff --git a/contrib/mtree/create.c b/contrib/mtree/create.c
index e23004851f39..245b9efa1c35 100644
--- a/contrib/mtree/create.c
+++ b/contrib/mtree/create.c
@@ -236,7 +236,8 @@ statf(FILE *fp, int indent, FTSENT *p)
 		offset += fprintf(fp, "%*s",
 		    (INDENTNAMELEN + indent) - offset, "");

-	if (!S_ISREG(p->fts_statp->st_mode) && (flavor == F_NETBSD6 || !dflag))
+	if (keys & F_TYPE && !S_ISREG(p->fts_statp->st_mode) &&
+	    (flavor == F_NETBSD6 || !dflag))
 		output(fp, indent, &offset, "type=%s",
 		    inotype(p->fts_statp->st_mode));
 	if (keys & (F_UID | F_UNAME) && p->fts_statp->st_uid != uid) {
diff --git a/contrib/mtree/mtree.c b/contrib/mtree/mtree.c
index 28f09fa32210..198db961e305 100644
--- a/contrib/mtree/mtree.c
+++ b/contrib/mtree/mtree.c
@@ -80,12 +80,14 @@ main(int argc, char **argv)
 	int	ch, status;
 	unsigned int	i;
 	int	cflag, Cflag, Dflag, Uflag, wflag;
+	char    *koptarg, *Koptarg, *Roptarg;
 	char	*dir, *p;
 	FILE	*spec1, *spec2;

 	setprogname(argv[0]);

 	cflag = Cflag = Dflag = Uflag = wflag = 0;
+	koptarg = Koptarg = Roptarg = NULL;
 	dir = NULL;
 	init_excludes();
 	spec1 = stdin;
@@ -150,14 +152,10 @@ main(int argc, char **argv)
 			break;
 		case 'k':
 			keys = F_TYPE;
-			while ((p = strsep(&optarg, " \t,")) != NULL)
-				if (*p != '\0')
-					keys |= parsekey(p, NULL);
+			koptarg = optarg;
 			break;
 		case 'K':
-			while ((p = strsep(&optarg, " \t,")) != NULL)
-				if (*p != '\0')
-					keys |= parsekey(p, NULL);
+			Koptarg = optarg;
 			break;
 		case 'l':
 			lflag = 1;
@@ -198,9 +196,7 @@ main(int argc, char **argv)
 			rflag++;
 			break;
 		case 'R':
-			while ((p = strsep(&optarg, " \t,")) != NULL)
-				if (*p != '\0')
-					keys &= ~parsekey(p, NULL);
+			Roptarg = optarg;
 			break;
 		case 's':
 			sflag = 1;
@@ -243,6 +239,22 @@ main(int argc, char **argv)
 	if (argc)
 		usage();

+	if (koptarg != NULL) {
+		while ((p = strsep(&koptarg, " \t,")) != NULL)
+			if (*p != '\0')
+				keys |= parsekey(p, NULL);
+	}
+	if (Koptarg != NULL) {
+		while ((p = strsep(&Koptarg, " \t,")) != NULL)
+			if (*p != '\0')
+				keys |= parsekey(p, NULL);
+	}
+	if (Roptarg != NULL) {
+		while ((p = strsep(&Roptarg, " \t,")) != NULL)
+			if (*p != '\0')
+				keys &= ~parsekey(p, NULL);
+	}
+
 	switch (flavor) {
 	case F_FREEBSD9:
 		if (cflag && iflag) {

>Audit-Trail:
From: "Christos Zoulas" <christos@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/59838 CVS commit: src/usr.sbin/mtree
Date: Thu, 18 Dec 2025 13:16:48 -0500

 Module Name:	src
 Committed By:	christos
 Date:		Thu Dec 18 18:16:48 UTC 2025

 Modified Files:
 	src/usr.sbin/mtree: create.c

 Log Message:
 PR/59838: Jose Louis Duran: Check for F_TYPE before printing the type


 To generate a diff of this commit:
 cvs rdiff -u -r1.79 -r1.80 src/usr.sbin/mtree/create.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

From: "Christos Zoulas" <christos@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/59838 CVS commit: src/usr.sbin/mtree
Date: Thu, 18 Dec 2025 13:17:26 -0500

 Module Name:	src
 Committed By:	christos
 Date:		Thu Dec 18 18:17:26 UTC 2025

 Modified Files:
 	src/usr.sbin/mtree: mtree.c

 Log Message:
 PR/59838: Jose Louis Duran: Save the R mask and apply it at the end so the
 order of -k -K -R does not matter.


 To generate a diff of this commit:
 cvs rdiff -u -r1.51 -r1.52 src/usr.sbin/mtree/mtree.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

From: Jose Luis Duran <jlduran@gmail.com>
To: gnats-bugs@netbsd.org
Cc: 
Subject: Re: PR/59838 CVS commit: src/usr.sbin/mtree
Date: Thu, 18 Dec 2025 18:26:14 -0300

 >  Log Message:
 >  PR/59838: Jose Louis Duran: Save the R mask and apply it at the end so the
 >  order of -k -K -R does not matter.

 Thank you, beautiful implementation!

 I also had to:

 --- a/usr.sbin/mtree/mtree.c
 +++ b/usr.sbin/mtree/mtree.c
 @@ -72,6 +72,7 @@ static struct {
         {F_NETBSD6, "netbsd6"},
  };

 +static int      parsekeys(char **);
  __dead static  void    usage(void);

  int

 And I have a question, if this is also needed?:

 --- a/usr.sbin/mtree/create.c
 +++ b/usr.sbin/mtree/create.c
 @@ -402,10 +402,13 @@ statd(FILE *fp, FTS *t, FTSENT *parent, uid_t
 *puid, gid_t *pgid, mode_t *pmode,
             ((keys & F_FLAGS) && (*pflags != saveflags)) ||
             first) {
                 first = 0;
 -               if (flavor != F_NETBSD6 && dflag)
 -                       fprintf(fp, "/set type=dir");
 -               else
 -                       fprintf(fp, "/set type=file");
 +               fprintf(fp, "/set");
 +               if (keys & F_TYPE) {
 +                       if (flavor != F_NETBSD6 && dflag)
 +                               fprintf(fp, " type=dir");
 +                       else
 +                               fprintf(fp, " type=file");
 +               }
                 if (keys & (F_UID | F_UNAME)) {
                         if (keys & F_UNAME &&
                             (name = user_from_uid(saveuid, 1)) != NULL)

 I'm worried that: `mtree -cn -k flags -R flags,type -p /usr/bin/ -x`
 would result in a bad specification.

 Thank you!

From: Jose Luis Duran <jlduran@gmail.com>
To: gnats-bugs@netbsd.org
Cc: 
Subject: Re: PR/59838 CVS commit: src/usr.sbin/mtree
Date: Fri, 19 Dec 2025 11:12:40 -0300

 >  I also had to:
 >
 >  --- a/usr.sbin/mtree/mtree.c
 >  +++ b/usr.sbin/mtree/mtree.c
 >  @@ -72,6 +72,7 @@ static struct {
 >          {F_NETBSD6, "netbsd6"},
 >   };
 >
 >  +static int      parsekeys(char **);
 >   __dead static  void    usage(void);
 >
 >   int

 It is not needed, the function prototype is there, I just didn't see it.

 >  And I have a question, if this is also needed?:
 >
 >  --- a/usr.sbin/mtree/create.c
 >  +++ b/usr.sbin/mtree/create.c
 >  @@ -402,10 +402,13 @@ statd(FILE *fp, FTS *t, FTSENT *parent, uid_t
 >  *puid, gid_t *pgid, mode_t *pmode,
 >              ((keys & F_FLAGS) && (*pflags != saveflags)) ||
 >              first) {
 >                  first = 0;
 >  -               if (flavor != F_NETBSD6 && dflag)
 >  -                       fprintf(fp, "/set type=dir");
 >  -               else
 >  -                       fprintf(fp, "/set type=file");
 >  +               fprintf(fp, "/set");
 >  +               if (keys & F_TYPE) {
 >  +                       if (flavor != F_NETBSD6 && dflag)
 >  +                               fprintf(fp, " type=dir");
 >  +                       else
 >  +                               fprintf(fp, " type=file");
 >  +               }
 >                  if (keys & (F_UID | F_UNAME)) {
 >                          if (keys & F_UNAME &&
 >                              (name = user_from_uid(saveuid, 1)) != NULL)
 >
 >  I'm worried that: `mtree -cn -k flags -R flags,type -p /usr/bin/ -x`
 >  would result in a bad specification.

 I believe it should also check for the presence of F_TYPE before
 printing "/set":

 --- a/usr.sbin/mtree/create.c
 +++ b/usr.sbin/mtree/create.c
 @@ -396,16 +396,20 @@ statd(FILE *fp, FTS *t, FTSENT *parent, uid_t
 *puid, gid_t *pgid, mode_t *pmode,
          * output a new one.  So first we check to see if anything changed.
          * Note that we always output a /set record for the first directory.
          */
 -       if (((keys & (F_UNAME | F_UID)) && (*puid != saveuid)) ||
 +       if ((keys & F_TYPE) ||
 +           ((keys & (F_UNAME | F_UID)) && (*puid != saveuid)) ||
             ((keys & (F_GNAME | F_GID)) && (*pgid != savegid)) ||
 -           ((keys & F_MODE) && (*pmode != savemode)) ||
 +           ((keys & F_MODE) && (*pmode != savemode)) ||
             ((keys & F_FLAGS) && (*pflags != saveflags)) ||
             first) {
                 first = 0;
 -               if (flavor != F_NETBSD6 && dflag)
 -                       fprintf(fp, "/set type=dir");
 -               else
 -                       fprintf(fp, "/set type=file");
 +               fprintf(fp, "/set");
 +               if (keys & F_TYPE) {
 +                       if (flavor != F_NETBSD6 && dflag)
 +                               fprintf(fp, " type=dir");
 +                       else
 +                               fprintf(fp, " type=file");
 +               }
                 if (keys & (F_UID | F_UNAME)) {
                         if (keys & F_UNAME &&
                             (name = user_from_uid(saveuid, 1)) != NULL)

 Thank you!

 -- 
 Jose Luis Duran

From: Christos Zoulas <christos@zoulas.com>
To: gnats-bugs@netbsd.org
Cc: gnats-admin@netbsd.org,
 netbsd-bugs@netbsd.org,
 jlduran@gmail.com
Subject: Re: PR/59838 CVS commit: src/usr.sbin/mtree
Date: Fri, 19 Dec 2025 09:22:13 -0500

 --Apple-Mail=_18CAC5AA-D2D7-4EAB-94BC-CBDF68E67A1E
 Content-Transfer-Encoding: quoted-printable
 Content-Type: text/plain;
 	charset=utf-8



 > On Dec 19, 2025, at 9:15=E2=80=AFAM, Jose Luis Duran via gnats =
 <gnats-admin@netbsd.org> wrote:
 >=20
 > The following reply was made to PR bin/59838; it has been noted by =
 GNATS.
 >=20

 [stuff deleted]

 > I believe it should also check for the presence of F_TYPE before
 > printing "/set":
 >=20
 > --- a/usr.sbin/mtree/create.c
 > +++ b/usr.sbin/mtree/create.c
 > @@ -396,16 +396,20 @@ statd(FILE *fp, FTS *t, FTSENT *parent, uid_t
 > *puid, gid_t *pgid, mode_t *pmode,
 >          * output a new one.  So first we check to see if anything =
 changed.
 >          * Note that we always output a /set record for the first =
 directory.
 >          */
 > -       if (((keys & (F_UNAME | F_UID)) && (*puid !=3D saveuid)) ||
 > +       if ((keys & F_TYPE) ||
 > +           ((keys & (F_UNAME | F_UID)) && (*puid !=3D saveuid)) ||
 >             ((keys & (F_GNAME | F_GID)) && (*pgid !=3D savegid)) ||
 > -           ((keys & F_MODE) && (*pmode !=3D savemode)) ||
 > +           ((keys & F_MODE) && (*pmode !=3D savemode)) ||
 >             ((keys & F_FLAGS) && (*pflags !=3D saveflags)) ||
 >             first) {
 >                 first =3D 0;
 > -               if (flavor !=3D F_NETBSD6 && dflag)
 > -                       fprintf(fp, "/set type=3Ddir");
 > -               else
 > -                       fprintf(fp, "/set type=3Dfile");
 > +               fprintf(fp, "/set");
 > +               if (keys & F_TYPE) {
 > +                       if (flavor !=3D F_NETBSD6 && dflag)
 > +                               fprintf(fp, " type=3Ddir");
 > +                       else
 > +                               fprintf(fp, " type=3Dfile");
 > +               }
 >                 if (keys & (F_UID | F_UNAME)) {
 >                         if (keys & F_UNAME &&
 >                             (name =3D user_from_uid(saveuid, 1)) !=3D =
 NULL)
 >=20

 We do we need to check F_TYPE twice? It is in the outer if...


 christos


 --Apple-Mail=_18CAC5AA-D2D7-4EAB-94BC-CBDF68E67A1E
 Content-Transfer-Encoding: 7bit
 Content-Disposition: attachment;
 	filename=signature.asc
 Content-Type: application/pgp-signature;
 	name=signature.asc
 Content-Description: Message signed with OpenPGP

 -----BEGIN PGP SIGNATURE-----
 Comment: GPGTools - http://gpgtools.org

 iF0EARECAB0WIQS+BJlbqPkO0MDBdsRxESqxbLM7OgUCaUVflQAKCRBxESqxbLM7
 OtaqAJ4x8GwpuM9jKMnL0DIKHfS0oOv25ACdGZvRn7fDPcwBeI8IvHQYpHroBoI=
 =sihb
 -----END PGP SIGNATURE-----

 --Apple-Mail=_18CAC5AA-D2D7-4EAB-94BC-CBDF68E67A1E--

From: Jose Luis Duran <jlduran@gmail.com>
To: Christos Zoulas <christos@zoulas.com>
Cc: gnats-bugs@netbsd.org, gnats-admin@netbsd.org, netbsd-bugs@netbsd.org
Subject: Re: PR/59838 CVS commit: src/usr.sbin/mtree
Date: Fri, 19 Dec 2025 11:34:45 -0300

 On Fri, Dec 19, 2025 at 11:22=E2=80=AFAM Christos Zoulas <christos@zoulas.c=
 om> wrote:
 >
 >
 >
 > > On Dec 19, 2025, at 9:15=E2=80=AFAM, Jose Luis Duran via gnats <gnats-a=
 dmin@netbsd.org> wrote:
 > >
 > > The following reply was made to PR bin/59838; it has been noted by GNAT=
 S.
 > >
 >
 > [stuff deleted]
 >
 > > I believe it should also check for the presence of F_TYPE before
 > > printing "/set":
 > >
 > > --- a/usr.sbin/mtree/create.c
 > > +++ b/usr.sbin/mtree/create.c
 > > @@ -396,16 +396,20 @@ statd(FILE *fp, FTS *t, FTSENT *parent, uid_t
 > > *puid, gid_t *pgid, mode_t *pmode,
 > >          * output a new one.  So first we check to see if anything chan=
 ged.
 > >          * Note that we always output a /set record for the first direc=
 tory.
 > >          */
 > > -       if (((keys & (F_UNAME | F_UID)) && (*puid !=3D saveuid)) ||
 > > +       if ((keys & F_TYPE) ||
 > > +           ((keys & (F_UNAME | F_UID)) && (*puid !=3D saveuid)) ||
 > >             ((keys & (F_GNAME | F_GID)) && (*pgid !=3D savegid)) ||
 > > -           ((keys & F_MODE) && (*pmode !=3D savemode)) ||
 > > +           ((keys & F_MODE) && (*pmode !=3D savemode)) ||
 > >             ((keys & F_FLAGS) && (*pflags !=3D saveflags)) ||
 > >             first) {
 > >                 first =3D 0;
 > > -               if (flavor !=3D F_NETBSD6 && dflag)
 > > -                       fprintf(fp, "/set type=3Ddir");
 > > -               else
 > > -                       fprintf(fp, "/set type=3Dfile");
 > > +               fprintf(fp, "/set");
 > > +               if (keys & F_TYPE) {
 > > +                       if (flavor !=3D F_NETBSD6 && dflag)
 > > +                               fprintf(fp, " type=3Ddir");
 > > +                       else
 > > +                               fprintf(fp, " type=3Dfile");
 > > +               }
 > >                 if (keys & (F_UID | F_UNAME)) {
 > >                         if (keys & F_UNAME &&
 > >                             (name =3D user_from_uid(saveuid, 1)) !=3D N=
 ULL)
 > >
 >
 > We do we need to check F_TYPE twice? It is in the outer if...

 The outer check is for the case where "/set" should not be printed at
 all, without any key/value pair, for example:
     $ mtree -cn -k flags -R flags,type -p /usr/bin/ -x
 In that corner case, no "/set" line should be printed.

 The inner check is to print "type=3D<dir/file>" conditionally, for example:
     $ mtree -cn -k uid -R type -p /usr/bin/ -x
 In this case, "/set uid=3D0" should be printed, notice that type is
 indeed excluded from the line.

 > christos
 >

From: Jose Luis Duran <jlduran@gmail.com>
To: gnats-bugs@netbsd.org
Cc: gnats-admin@netbsd.org, netbsd-bugs@netbsd.org
Subject: Re: PR/59838 CVS commit: src/usr.sbin/mtree
Date: Fri, 19 Dec 2025 11:46:29 -0300

 >  > We do we need to check F_TYPE twice? It is in the outer if...
 >
 >  The outer check is for the case where "/set" should not be printed at
 >  all, without any key/value pair, for example:
 >      $ mtree -cn -k flags -R flags,type -p /usr/bin/ -x
 >  In that corner case, no "/set" line should be printed.

 Forgot to mention my doubt:
 Reading the comment: "Note that we always output a /set record for the
 first directory.", my fear is that it could produce an invalid
 specification, because there would be no "/set" record, or, in the
 previous case, just an empty "/set" record with no key/value pairs.

From: jlduran@gmail.com
To: gnats-bugs@NetBSD.org
Cc: 
Subject: mtree: Partially revert PR/59838 mtree's type keyword is mandatory
Date: Thu,  8 Jan 2026 06:11:05 +0000 (UTC)

 >Submitter-Id:	net
 >Originator:	Jose Luis Duran
 >Organization:	FreeBSD
 >Confidential:	no
 >Synopsis:	mtree: Partially revert PR/59838 mtree's type keyword is mandatory
 >Severity:	non-critical
 >Priority:	low
 >Category:	bin
 >Class:		doc-bug
 >Release:	trunk
 >Environment:	
 >Description:
 It was reported on the FreeBSD current mailing list that the "type" keyword has been historically mandatory, and should not be removed by the -R flag.

 Partially revert PR/59838 (create.c,v 1.80) and fix the manual page to clarify this intention, effectively also reverting mtree.8,v 1.44.

 >How-To-Repeat:
 $ mtree -c -R all | mtree -C

 "-R all" should *not* remove the "type" keyword.
 >Fix:
 diff --git usr.sbin/mtree/create.c usr.sbin/mtree/create.c
 index 1a4f006bcfea..3ad592ba1173 100644
 --- usr.sbin/mtree/create.c
 +++ usr.sbin/mtree/create.c
 @@ -236,8 +236,7 @@ statf(FILE *fp, int indent, FTSENT *p)
  		offset += fprintf(fp, "%*s",
  		    (INDENTNAMELEN + indent) - offset, "");

 -	if (keys & F_TYPE &&
 -	    !S_ISREG(p->fts_statp->st_mode) && (flavor == F_NETBSD6 || !dflag))
 +	if (!S_ISREG(p->fts_statp->st_mode) && (flavor == F_NETBSD6 || !dflag))
  		output(fp, indent, &offset, "type=%s",
  		    inotype(p->fts_statp->st_mode));
  	if (keys & (F_UID | F_UNAME) && p->fts_statp->st_uid != uid) {
 diff --git usr.sbin/mtree/mtree.8 usr.sbin/mtree/mtree.8
 index 99e3199de943..a975403a6d20 100644
 --- usr.sbin/mtree/mtree.8
 +++ usr.sbin/mtree/mtree.8
 @@ -56,7 +56,7 @@
  .\"
  .\"     @(#)mtree.8	8.2 (Berkeley) 12/11/93
  .\"
 -.Dd December 2, 2023
 +.Dd January 8, 2026
  .Dt MTREE 8
  .Os
  .Sh NAME
 @@ -242,18 +242,14 @@ If
  is specified, add all of the other keywords.
  .
  .It Fl k Ar keywords
 -Use the
 +Use the mandatory
  .Sy type
  keyword plus the specified (whitespace or comma separated)
  .Ar keywords
 -instead of the current set of keywords.
 +to replace the current set of keywords.
  If
  .Ql all
 -is specified, use all of the other keywords.
 -If the
 -.Sy type
 -keyword is not desired, suppress it with
 -.Fl R Cm type .
 +is specified, use all of the available keywords.
  .
  .It Fl L
  Follow all symbolic links in the file hierarchy.
 @@ -338,9 +334,13 @@ This occurs when the directory is a symbolic link.
  .It Fl R Ar keywords
  Remove the specified (whitespace or comma separated) keywords from the current
  set of keywords.
 +The
 +.Sy type
 +keyword is mandatory and is always retained.
  If
  .Ql all
 -is specified, remove all of the other keywords.
 +is specified, remove all keywords except
 +.Sy type .
  .
  .It Fl r
  Remove any files in the file hierarchy that are not described in the

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.47 2022/09/11 19:34:41 kim Exp $
$NetBSD: gnats_config.sh,v 1.9 2014/08/02 14:16:04 spz Exp $
Copyright © 1994-2026 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.