Subject: proposal: FTS_NOPATH
To: None <tech-userlevel@netbsd.org>
From: YAMAMOTO Takashi <yamt@mwd.biglobe.ne.jp>
List: tech-userlevel
Date: 09/28/2002 19:14:07
--NextPart-20020928191241-2411900
Content-Type: Text/Plain; charset=us-ascii

hi.

i've noticed that "rm -r" can't remove
too deep directories. (ie. pathlen > USHRT_MAX)

it's due to limit of FTSENTRY::fts_pathlen.
but some applications (including rm(1), imo) don't really need
fts_pathlen (and fts_path).

so i plan to add a new flag, FTS_NOPATH, to fts_open.
if it's specified, fts_path{,len} isn't updated and
fts_read can dig into such deep directories.

patch is attached.
is it ok?

--
YAMAMOTO Takashi

--NextPart-20020928191241-2411900
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="fts.diff"

Index: include/fts.h
===================================================================
RCS file: /cvs/NetBSD/basesrc/include/fts.h,v
retrieving revision 1.9
diff -u -p -r1.9 fts.h
--- include/fts.h	1998/11/06 19:44:52	1.9
+++ include/fts.h	2002/09/28 10:01:27
@@ -58,10 +58,11 @@ typedef struct {
 #define	FTS_SEEDOT	0x020		/* return dot and dot-dot */
 #define	FTS_XDEV	0x040		/* don't cross devices */
 #define	FTS_WHITEOUT	0x080		/* return whiteout information */
-#define	FTS_OPTIONMASK	0x0ff		/* valid user option mask */
+#define	FTS_NOPATH	0x100		/* don't need path */
+#define	FTS_OPTIONMASK	0x1ff		/* valid user option mask */
 
-#define	FTS_NAMEONLY	0x100		/* (private) child names only */
-#define	FTS_STOP	0x200		/* (private) unrecoverable error */
+#define	FTS_NAMEONLY	0x200		/* (private) child names only */
+#define	FTS_STOP	0x400		/* (private) unrecoverable error */
 	int fts_options;		/* fts_open options, global flags */
 } FTS;
 
Index: lib/libc/gen/__fts13.c
===================================================================
RCS file: /cvs/NetBSD/basesrc/lib/libc/gen/__fts13.c,v
retrieving revision 1.40
diff -u -p -r1.40 __fts13.c
--- lib/libc/gen/__fts13.c	2002/09/16 04:10:36	1.40
+++ lib/libc/gen/__fts13.c	2002/09/28 10:01:27
@@ -155,12 +155,14 @@ fts_open(argv, options, compar)
 	if (ISSET(FTS_LOGICAL))
 		SET(FTS_NOCHDIR);
 
-	/*
-	 * Start out with 1K of path space, and enough, in any case,
-	 * to hold the user's paths.
-	 */
-	if (fts_palloc(sp, MAX(fts_maxarglen(argv), MAXPATHLEN)))
-		goto mem1;
+	if (!ISSET(FTS_NOPATH)) {
+		/*
+		 * Start out with 1K of path space, and enough, in any case,
+		 * to hold the user's paths.
+		 */
+		if (fts_palloc(sp, MAX(fts_maxarglen(argv), MAXPATHLEN)))
+			goto mem1;
+	}
 
 	/* Allocate/initialize root's parent. */
 	if ((parent = fts_alloc(sp, "", 0)) == NULL)
@@ -226,6 +228,11 @@ fts_open(argv, options, compar)
 	if (!ISSET(FTS_NOCHDIR) && (sp->fts_rfd = open(".", O_RDONLY, 0)) < 0)
 		SET(FTS_NOCHDIR);
 
+	if (ISSET(FTS_NOPATH) && ISSET(FTS_NOCHDIR)) {
+		errno = EINVAL;
+		goto mem3; /*XXX*/
+	}
+
 	return (sp);
 
 mem3:	fts_lfree(root);
@@ -253,14 +260,20 @@ fts_load(sp, p)
 	 * place and the user can access the first node.  From fts_open it's
 	 * known that the path will fit.
 	 */
-	len = p->fts_pathlen = p->fts_namelen;
-	memmove(sp->fts_path, p->fts_name, len + 1);
-	if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) {
-		len = strlen(++cp);
-		memmove(p->fts_name, cp, len + 1);
-		p->fts_namelen = len;
+	if (ISSET(FTS_NOPATH)) {
+		p->fts_accpath = p->fts_name;
 	}
-	p->fts_accpath = p->fts_path = sp->fts_path;
+	else {
+		len = p->fts_pathlen = p->fts_namelen;
+		memmove(sp->fts_path, p->fts_name, len + 1);
+		if ((cp = strrchr(p->fts_name, '/')) &&
+		    (cp != p->fts_name || cp[1])) {
+			len = strlen(++cp);
+			memmove(p->fts_name, cp, len + 1);
+			p->fts_namelen = len;
+		}
+		p->fts_accpath = p->fts_path = sp->fts_path;
+	}
 	sp->fts_dev = p->fts_dev;
 }
 
@@ -458,9 +471,12 @@ next:	tmp = p;
 			p->fts_instr = FTS_NOINSTR;
 		}
 
-name:		t = sp->fts_path + NAPPEND(p->fts_parent);
-		*t++ = '/';
-		memmove(t, p->fts_name, (size_t)(p->fts_namelen + 1));
+name:
+		if (!ISSET(FTS_NOPATH)) {
+			t = sp->fts_path + NAPPEND(p->fts_parent);
+			*t++ = '/';
+			memmove(t, p->fts_name, (size_t)(p->fts_namelen + 1));
+		}
 		return (sp->fts_cur = p);
 	}
 
@@ -478,8 +494,10 @@ name:		t = sp->fts_path + NAPPEND(p->fts
 		return (sp->fts_cur = NULL);
 	}
 
-	/* Nul terminate the pathname. */
-	sp->fts_path[p->fts_pathlen] = '\0';
+	if (!ISSET(FTS_NOPATH)) {
+		/* Nul terminate the pathname. */
+		sp->fts_path[p->fts_pathlen] = '\0';
+	}
 
 	/*
 	 * Return to the parent directory.  If at a root node or came through
@@ -722,13 +740,15 @@ fts_build(sp, type)
 	 * If not changing directories set a pointer so that can just append
 	 * each new name into the path.
 	 */
-	len = NAPPEND(cur);
-	if (ISSET(FTS_NOCHDIR)) {
-		cp = sp->fts_path + len;
-		*cp++ = '/';
+	if (!ISSET(FTS_NOPATH)) {
+		len = NAPPEND(cur);
+		if (ISSET(FTS_NOCHDIR)) {
+			cp = sp->fts_path + len;
+			*cp++ = '/';
+		}
+		len++;
+		maxlen = sp->fts_pathlen - len;
 	}
-	len++;
-	maxlen = sp->fts_pathlen - len;
 
 	level = cur->fts_level + 1;
 
@@ -747,30 +767,33 @@ fts_build(sp, type)
 #endif
 		if ((p = fts_alloc(sp, dp->d_name, dlen)) == NULL)
 			goto mem1;
-		if (dlen >= maxlen) {	/* include space for NUL */
-			if (fts_palloc(sp, len + dlen + 1)) {
-				/*
-				 * No more memory for path or structures.  Save
-				 * errno, free up the current structure and the
-				 * structures already allocated.
-				 */
-mem1:				saved_errno = errno;
-				if (p)
-					free(p);
-				fts_lfree(head);
-				(void)closedir(dirp);
-				errno = saved_errno;
-				cur->fts_info = FTS_ERR;
-				SET(FTS_STOP);
-				return (NULL);
+		if (!ISSET(FTS_NOPATH)) {
+			if (dlen >= maxlen) {	/* include space for NUL */
+				if (fts_palloc(sp, len + dlen + 1)) {
+					/*
+					 * No more memory for path or
+					 * structures.  Save errno, free up the
+					 * current structure and the structures
+					 * already allocated.
+					 */
+mem1:					saved_errno = errno;
+					if (p)
+						free(p);
+					fts_lfree(head);
+					(void)closedir(dirp);
+					errno = saved_errno;
+					cur->fts_info = FTS_ERR;
+					SET(FTS_STOP);
+					return (NULL);
+				}
+				adjust = 1;
+				if (ISSET(FTS_NOCHDIR))
+					cp = sp->fts_path + len;
+				maxlen = sp->fts_pathlen - len;
 			}
-			adjust = 1;
-			if (ISSET(FTS_NOCHDIR))
-				cp = sp->fts_path + len;
-			maxlen = sp->fts_pathlen - len;
-		}
 
-		p->fts_pathlen = len + dlen;
+			p->fts_pathlen = len + dlen;
+		}
 		p->fts_parent = sp->fts_cur;
 		p->fts_level = level;
 
@@ -1106,7 +1129,7 @@ fts_palloc(sp, size)
 #if 1
 	/* Protect against fts_pathlen overflow. */
 	if (size > USHRT_MAX + 1) {
-		errno = ENOMEM;
+		errno = ENAMETOOLONG;
 		return (1);
 	}
 #endif

--NextPart-20020928191241-2411900
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="rm.diff"

Index: bin/rm/rm.c
===================================================================
RCS file: /cvs/NetBSD/basesrc/bin/rm/rm.c,v
retrieving revision 1.30
diff -u -p -r1.30 rm.c
--- bin/rm/rm.c	2002/05/02 13:25:09	1.30
+++ bin/rm/rm.c	2002/09/28 10:12:28
@@ -62,7 +62,7 @@ __RCSID("$NetBSD: rm.c,v 1.30 2002/05/02
 #include <string.h>
 #include <unistd.h>
 
-int dflag, eval, fflag, iflag, Pflag ,stdin_ok, Wflag;
+int Cflag, dflag, eval, fflag, iflag, Pflag ,stdin_ok, Wflag;
 
 int	check(char *, char *, struct stat *);
 void	checkdot(char **);
@@ -79,6 +79,7 @@ int	main(int, char *[]);
  */
 #define NONEXISTENT(x) \
     ((x) == ENOENT || (x) == ENAMETOOLONG || (x) == ENOTDIR)
+#define	PATH(x)	((Cflag) ? (x)->fts_path : (x)->fts_accpath)
 
 /*
  * rm --
@@ -96,8 +97,11 @@ main(int argc, char *argv[])
 	(void)setlocale(LC_ALL, "");
 
 	Pflag = rflag = 0;
-	while ((ch = getopt(argc, argv, "dfiPRrW")) != -1)
+	while ((ch = getopt(argc, argv, "CdfiPRrW")) != -1)
 		switch (ch) {
+		case 'C':
+			Cflag = 1;
+			break;
 		case 'd':
 			dflag = 1;
 			break;
@@ -164,6 +168,8 @@ rm_tree(char **argv)
 #define	SKIPPED	1
 
 	flags = FTS_PHYSICAL;
+	if (Cflag)
+		flags |= FTS_NOPATH;
 	if (!needstat)
 		flags |= FTS_NOSTAT;
 	if (Wflag)
@@ -176,12 +182,12 @@ rm_tree(char **argv)
 		case FTS_DNR:
 			if (!fflag || p->fts_errno != ENOENT) {
 				warnx("%s: %s",
-				    p->fts_path, strerror(p->fts_errno));
+				    PATH(p), strerror(p->fts_errno));
 				eval = 1;
 			}
 			continue;
 		case FTS_ERR:
-			errx(1, "%s: %s", p->fts_path, strerror(p->fts_errno));
+			errx(1, "%s: %s", PATH(p), strerror(p->fts_errno));
 			/* NOTREACHED */
 		case FTS_NS:
 			/*
@@ -192,14 +198,14 @@ rm_tree(char **argv)
 				continue;
 			if (needstat) {
 				warnx("%s: %s",
-				    p->fts_path, strerror(p->fts_errno));
+				    PATH(p), strerror(p->fts_errno));
 				eval = 1;
 				continue;
 			}
 			break;
 		case FTS_D:
 			/* Pre-order: give user chance to skip. */
-			if (!fflag && !check(p->fts_path, p->fts_accpath,
+			if (!fflag && !check(PATH(p), p->fts_accpath,
 			    p->fts_statp)) {
 				(void)fts_set(fts, p, FTS_SKIP);
 				p->fts_number = SKIPPED;
@@ -212,7 +218,7 @@ rm_tree(char **argv)
 			break;
 		default:
 			if (!fflag &&
-			    !check(p->fts_path, p->fts_accpath, p->fts_statp))
+			    !check(PATH(p), p->fts_accpath, p->fts_statp))
 				continue;
 		}
 
@@ -242,7 +248,7 @@ rm_tree(char **argv)
 			    (fflag && NONEXISTENT(errno)))
 				continue;
 		}
-		warn("%s", p->fts_path);
+		warn("%s", PATH(p));
 		eval = 1;
 	}
 	if (errno)
@@ -430,7 +436,7 @@ void
 usage(void)
 {
 
-	(void)fprintf(stderr, "usage: %s [-f|-i] [-dPRrW] file ...\n",
+	(void)fprintf(stderr, "usage: %s [-f|-i] [-CdPRrW] file ...\n",
 	    getprogname());
 	exit(1);
 	/* NOTREACHED */

--NextPart-20020928191241-2411900--