Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 5 Jan 2007 00:00:36 GMT
From:      Bruce Evans <bde@zeta.org.au>
To:        freebsd-bugs@FreeBSD.org
Subject:   Re: bin/107515: /bin/ls bug
Message-ID:  <200701050000.l0500aoD097019@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help
The following reply was made to PR bin/107515; it has been noted by GNATS.

From: Bruce Evans <bde@zeta.org.au>
To: David Taylor <davidt@yadt.co.uk>
Cc: freebsd-gnats-submit@freebsd.org, yar@freebsd.org
Subject: Re: bin/107515: /bin/ls bug
Date: Fri, 5 Jan 2007 10:50:12 +1100 (EST)

 On Thu, 4 Jan 2007, David Taylor wrote:
 
 > On Thu, 04 Jan 2007, Remko Lodder wrote:
 > > I am sorry but this is not a bug, the behaviour is to list the contents
 > > of the directory if you request the leaf ( /tmp/a/ ) and to show the
 > > directory itself if you request that (/tmp/a). This is desired
 > > functionality and should not change imo.
 >
 > Unfortunately that is not the behaviour exhibited by ls.  Further,
 > that was not the problem being reported, either.
 >
 > Both "ls /tmp/a/" and "ls /tmp/a" attempt to list the contents of the
 > directory (as shown by the "Permission Denied" error.  The directory
 > itself can by listed using "ls -d /tmp/a/" (or "ls -d /tmp/a").
 >
 > The actual bug being reported is a different matter, however:
 >
 > $ ls /tmp/a/
 > ls: : Permission denied
 >   ^^^
 >
 > The name should not be blank here, it should be something like:
 >
 > $ ls /tmp/a
 > ls: a: Permission denied
 >   ^^^^
 
 Both of these behaviours are bugs.  The pathname with the problem is
 /tmp/a or /tmp/a/.  ls should be reporting that.  Instead, ls apparently
 does extra work to mess up the pathname, and gets it completely wrong
 for the one with the trailing slash.  Truss output:
 
 ls /tmp/a:
 %%%
 ...
 stat("/tmp/a",0xbfbfe230)			 = 0 (0x0)
 open(".",0x0,00)				 = 3 (0x3)
 fchdir(0x3)					 = 0 (0x0)
 stat("/tmp/a",0xbfbfe1e0)			 = 0 (0x0)
 open("/tmp/a",0x4,00)				 ERR#13 'Permission denied'
 stat("/tmp/a",0xbfbfe1e0)			 = 0 (0x0)
 open("/tmp/a",0x4,00)				 ERR#13 'Permission denied'
 ls: write(2,0xbfbfdb70,4)				 = 4 (0x4)
 a: Permission deniedwrite(2,0xbfbfdb90,20)				 = 20 (0x14)
 %%%
 
 ls /tmp/a/
 %%%
 ...
 stat("/tmp/a/",0xbfbfe230)			 = 0 (0x0)
 open(".",0x0,00)				 = 3 (0x3)
 fchdir(0x3)					 = 0 (0x0)
 stat("/tmp/a/",0xbfbfe1e0)			 = 0 (0x0)
 open("/tmp/a/",0x4,00)				 ERR#13 'Permission denied'
 stat("/tmp/a/",0xbfbfe1e0)			 = 0 (0x0)
 open("/tmp/a/",0x4,00)				 ERR#13 'Permission denied'
 ls: write(2,0xbfbfdb70,4)				 = 4 (0x4)
 : Permission deniedwrite(2,0xbfbfdb90,19)				 = 19 (0x13)
 %%%
 
 It works with the full pathname throughout, then apparently tries to
 print the basename.  basename(1) and presumably basename(3) give a
 basename of "a" for both, but ls gets the trailing slash stripping
 wrong and prints "" for /tmp/a/.
 
 It may be wrong for basename(3) to strip the slash too (since stripping
 the slash is wrong if the path is a symlink), but this is the historical
 behaviour and POSIX requries it.  Thus utilities should be careful about
 using basename(3) if there might be a symlink involved.  ls's bug with
 a symlinked variant of the unreadable directory is a little different:
 
 Setup:
 %%%
 d---------  2 bde  wheel  512 Jan  5 09:41 a
 lrwxr-xr-x  1 bde  wheel    1 Jan  5 10:08 b -> a
 %%%
 
 ls /tmp/b:
 %%%
 ...
 open("/tmp/b",0x4,00)				 ERR#13 'Permission denied'
 ls: write(2,0xbfbfdb70,4)				 = 4 (0x4)
 b: Permission deniedwrite(2,0xbfbfdb90,20)				 = 20 (0x14)
 %%%
 
 Now "b" is perfectly readable, but what it points to isn't.  It is hard for
 ls to print anything better than /tmp/b in the error message, since it
 only tried to access the latter.
 
 ls /tmp/b/:
 %%%
 ...
 open("/tmp/b/",0x4,00)				 ERR#13 'Permission denied'
 ls: write(2,0xbfbfdb70,4)				 = 4 (0x4)
 : Permission deniedwrite(2,0xbfbfdb90,19)				 = 19 (0x13)
 %%%
 
 Same bug as for ls /tmp/b/.  Even more confusing now.
 
 The case of ls -R broken in a slightly different way:
 
 ls -R /tmp (where /tmp contains "a" and "b" as above):
 %%%
 ...
 open("a",0x4,027757761130)			 ERR#13 'Permission denied'
 ls: write(2,0xbfbfdb70,4)				 = 4 (0x4)
 a: Permission deniedwrite(2,0xbfbfdb90,20)				 = 20 (0x14)
 %%%
 
 As usual, ls cannot open "a", and it prints only "a" in the error message,
 but the full path is much more needed for recursive listings since it might
 be anywhere in the tree.
 
 Incomplete fix (ls isn't actually doing extra work to get this wrong):
 %%%
 Index: ls.c
 ===================================================================
 RCS file: /home/ncvs/src/bin/ls/ls.c,v
 retrieving revision 1.78
 diff -u -2 -r1.78 ls.c
 --- ls.c	8 Jun 2004 09:30:10 -0000	1.78
 +++ ls.c	4 Jan 2007 23:30:52 -0000
 @@ -477,5 +477,5 @@
   		case FTS_DNR:
   		case FTS_ERR:
 -			warnx("%s: %s", p->fts_name, strerror(p->fts_errno));
 +			warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
   			rval = 1;
   			break;
 %%%
 
 This prints the full path in the error message for the unreadable directory
 (FTS_DNR) case and in some other cases.
 
 Other things to fix:
 - ls prints only fts_name in many other messages.  I think this is only
    sometimes correct.  E.g., it is normal the output format for ls -R.
    (However I rarely use ls -R since this format is hard to unimprove on.)
 - I think it is a bug in fts(3) for fts_path to be empty.  The empty
    pathname is never valid, and the error is for the directory.  Apparently,
    fts(3) reduces /tmp/a/ to the invalid basename "", but does the right
    thing for /tmp/a.
 
 Bruce



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200701050000.l0500aoD097019>