Bug 19514

Summary: patch to prevent tail'ing directories
Product: Base System Reporter: kbyanc <kbyanc>
Component: binAssignee: freebsd-bugs (Nobody) <bugs>
Status: Closed FIXED    
Severity: Affects Only Me    
Priority: Normal    
Version: 4.0-STABLE   
Hardware: Any   
OS: Any   
Attachments:
Description Flags
file.diff none

Description kbyanc 2000-06-26 09:40:00 UTC
	tail(1) does not prevent the user from doing nonsensical things
	like tail'ing directories. Other commands, i.e. more(1) do. This
	patch alerts the user when they try to tail a directory. In
	addition, it also adds checks for reading symlink contents and
	whiteout entries, however I can't imagine how either of these
	scenarios would occur.

How-To-Repeat: 
	tail .
Comment 1 kbyanc 2000-06-27 06:43:10 UTC
  Attached is a more correct patch, please apply it instead. Thanks,

  Kelly

Index: usr.bin/tail/tail.c
===================================================================
RCS file: /home/cvs/src/usr.bin/tail/tail.c,v
retrieving revision 1.6
diff -u -r1.6 tail.c
--- usr.bin/tail/tail.c	1999/07/04 17:26:03	1.6
+++ usr.bin/tail/tail.c	2000/06/27 05:37:31
@@ -171,6 +171,16 @@
 				ierr();
 				continue;
 			}
+			if (S_ISDIR(sb.st_mode))
+				errx(1, "%s is a directory", fname);
+			if (S_ISLNK(sb.st_mode))
+				/* This should transparently be resolved and
+				 * thus never happen.
+				 */
+				errx(1, "%s is a symlink", fname);
+			if (S_ISWHT(sb.st_mode))
+				/* This should never happen. */
+				errx(1, "%s is a whiteout entry", fname);
 			if (argc > 1) {
 				(void)printf("%s==> %s <==\n",
 				    first ? "" : "\n", fname);
Comment 2 Bruce Evans 2000-06-27 10:22:55 UTC
On Mon, 26 Jun 2000 kbyanc@posi.net wrote:

> >Description:
> 
> 	tail(1) does not prevent the user from doing nonsensical things
> 	like tail'ing directories.

It is a feature of Unix utilities that they do what they are told to do.

>	Other commands, i.e. more(1) do. This

This is a misfeature of more(1) :-).

POSIX.2 (in the old draft that I have) specifically requires "tail -c"
to accept arbitrary data.  tail(1) without -c is only required to handle
text files.  head(1) is only required to handle text files.  cat(1) is
required to handle all types of files.

> 	patch alerts the user when they try to tail a directory. In
> 	addition, it also adds checks for reading symlink contents and
> 	whiteout entries, however I can't imagine how either of these
> 	scenarios would occur.

At least the symlink case can't happen, so don't obfuscate the code
checking for it.

> --- usr.bin/tail/tail.c.orig	Mon Jun 26 01:30:01 2000
> +++ usr.bin/tail/tail.c	Mon Jun 26 01:38:38 2000
> @@ -171,6 +171,16 @@
>  				ierr();
>  				continue;
>  			}
> +			if (sb.st_mode & S_IFDIR)
> +				errx(1, "%s is a directory", fname);

Exiting for a non-error is a bug.  The current tail(1) doesn't even exit
for errors on individual files.

Bruce
Comment 3 kbyanc 2000-06-28 22:01:36 UTC
On Tue, 27 Jun 2000, Bruce Evans wrote:

> POSIX.2 (in the old draft that I have) specifically requires "tail -c"
> to accept arbitrary data.  tail(1) without -c is only required to handle
> text files.  head(1) is only required to handle text files.  cat(1) is
> required to handle all types of files.

  Hmm. Sounds like a misfeature of POSIX :( What sense can
cat/head/tailing a directory possibly make.

> 
> > 	patch alerts the user when they try to tail a directory. In
> > 	addition, it also adds checks for reading symlink contents and
> > 	whiteout entries, however I can't imagine how either of these
> > 	scenarios would occur.
> 
> At least the symlink case can't happen, so don't obfuscate the code
> checking for it.

  Noted. Personally, I like leaving tidbits like that in my source code so
I can remember why I did (or did not do) things.

> 
> > --- usr.bin/tail/tail.c.orig	Mon Jun 26 01:30:01 2000
> > +++ usr.bin/tail/tail.c	Mon Jun 26 01:38:38 2000
> > @@ -171,6 +171,16 @@
> >  				ierr();
> >  				continue;
> >  			}
> > +			if (sb.st_mode & S_IFDIR)
> > +				errx(1, "%s is a directory", fname);
> 
> Exiting for a non-error is a bug.  The current tail(1) doesn't even exit
> for errors on individual files.
> 
> Bruce

  Also noted. If the POSIX hadn't ruled the current (in my opinion,
broken) behaviour to be the law, then I would fix the patch.

  As it stands, I regretfully have to ask for the PR be closed and declare
this bug a feature. :(

  Kelly

--
Kelly Yancey  -  kbyanc@posi.net  -  Belmont, CA
System Administrator, eGroups.com                  http://www.egroups.com/
Maintainer, BSD Driver Database       http://www.posi.net/freebsd/drivers/
Coordinator, Team FreeBSD        http://www.posi.net/freebsd/Team-FreeBSD/
Comment 4 kbyanc 2000-06-29 00:32:53 UTC
On Tue, 27 Jun 2000, Bruce Evans wrote:

> POSIX.2 (in the old draft that I have) specifically requires "tail -c"
> to accept arbitrary data.  tail(1) without -c is only required to handle
> text files.  head(1) is only required to handle text files.  cat(1) is
> required to handle all types of files.
> 

  I just researched this in the SUS and found the same thing. So contrary
to my previous post, do not close this PR yet. Instead, please apply the
following patch instead. It keeps us SUS and POSIX compliant in this
regards, but is still 'user-friendly'.

Index: usr.bin/tail/tail.c
===================================================================
RCS file: /home/cvs/src/usr.bin/tail/tail.c,v
retrieving revision 1.6
diff -u -r1.6 tail.c
--- usr.bin/tail/tail.c 1999/07/04 17:26:03     1.6
+++ usr.bin/tail/tail.c 2000/06/28 23:23:24
@@ -69,7 +69,7 @@
	FILE *fp;
	long off;
	enum STYLE style;
-	int ch, first;
+	int ch, first, num;
	char *p;
 
	/*
@@ -164,6 +164,7 @@
		}
	}
 
+	num = 0;
	if (*argv)
		for (first = 1; fname = *argv++;) {
			if ((fp = fopen(fname, "r")) == NULL ||
@@ -171,7 +172,25 @@
				ierr();
				continue;
			}
-			if (argc > 1) {
+				if (style == FLINES || style == RLINES) {
+				/*
+				 * SUS says that `line' mode is only required
+				 * to support text files. We support everything
+				 * that `makes sense'.
+				 */
+				if (S_ISDIR(sb.st_mode)) {
+					warnx("%s is a directory", fname);
+					continue;
+				} else if (S_ISLNK(sb.st_mode)) {
+					warnx("%s is a symlink", fname);
+					continue;
+				} else if (S_ISWHT(sb.st_mode)) {
+					warnx("%s is a whiteout entry", fname);
+					continue;
+				}
+			}
+			num++;
+			if (num > 1) {
				(void)printf("%s==> %s <==\n",
				    first ? "" : "\n", fname);
				first = 0;


  Thanks,

  Kelly

--
Kelly Yancey  -  kbyanc@posi.net  -  Belmont, CA
System Administrator, eGroups.com                  http://www.egroups.com/
Maintainer, BSD Driver Database       http://www.posi.net/freebsd/drivers/
Coordinator, Team FreeBSD        http://www.posi.net/freebsd/Team-FreeBSD/
Comment 5 Jens Schweikhardt freebsd_committer freebsd_triage 2001-06-14 14:07:46 UTC
State Changed
From-To: open->closed

We think that it's better to stick to the WYGIWYAF principle 
(what you get is what you asked for). As bde points out, 
why should "head /" not do the same thing as "cat / | head"?