Subject: Re: lib/30943: realpath() behaviour changed with nonexistant relative
To: None <lib-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: Elad Efrat <elad@NetBSD.org>
List: netbsd-bugs
Date: 08/13/2005 00:14:01
The following reply was made to PR lib/30943; it has been noted by GNATS.

From: Elad Efrat <elad@NetBSD.org>
To: gnats-bugs@netbsd.org
Cc: mark@mcs.vuw.ac.nz
Subject: Re: lib/30943: realpath() behaviour changed with nonexistant relative
 path
Date: Sat, 13 Aug 2005 03:04:20 +0300

 Hi,
 
 Looking at several revisions of realpath(3):
 
   1.35: 6 months, 2 weeks ago
   1.36: 6 months, 1 week ago (imported FreeBSD code)
   1.38: 5 weeks, 3 days ago (bug fix)
   1.39: not yet commited
 
 We'll try realpath(3) on a path with a non-existant last element
 and see how it behaves. My current working directory is
 
 	/home/elad/work/realpath/realpath
 
    [ first let's see it works the same for an existing path ]
 
 phyre:realpath {33} ls
 realpath*   realpath.c
 phyre:realpath {34} mkdir foo
 phyre:realpath {35} ./realpath foo
 Revision 1.35:
 buf=[/home/elad/work/realpath/realpath/foo]
 ret=[/home/elad/work/realpath/realpath/foo]
 errno=[0]
 
 Revision 1.36:
 buf=[/home/elad/work/realpath/realpath/foo]
 ret=[/home/elad/work/realpath/realpath/foo]
 errno=[0]
 
 Revision 1.38:
 buf=[/home/elad/work/realpath/realpath/foo]
 ret=[/home/elad/work/realpath/realpath/foo]
 errno=[0]
 
 (to be) Revision 1.39:
 buf=[/home/elad/work/realpath/realpath/foo]
 ret=[/home/elad/work/realpath/realpath/foo]
 errno=[0]
 
    [ so far so good. ]
 
 phyre:realpath {36} ./realpath foo/bar/
 Revision 1.35:
 buf=[foo/bar]
 ret=[(null)]
 errno=[2]
 
 Revision 1.36:
 buf=[/home/elad/work/realpath/realpath/foo/bar]
 ret=[(null)]
 errno=[2]
 
 Revision 1.38:
 buf=[/home/elad/work/realpath/realpath/foo/bar]
 ret=[/home/elad/work/realpath/realpath/foo/bar]
 errno=[2]
 
 (to be) Revision 1.39:
 buf=[/home/elad/work/realpath/realpath/foo/bar]
 ret=[(null)]
 errno=[2]
 
    [ revision 1.35 acts like solaris, broken. revision 1.36 acts
      like you mentioned it should. revision 1.38 *supposedly*
      breaks behavior. ]
 
 phyre:realpath {37} ./realpath foo/bar
 Revision 1.35:
 buf=[/home/elad/work/realpath/realpath/foo/bar]
 ret=[/home/elad/work/realpath/realpath/foo/bar]
 errno=[2]
 
 Revision 1.36:
 buf=[/home/elad/work/realpath/realpath/foo/bar]
 ret=[/home/elad/work/realpath/realpath/foo/bar]
 errno=[2]
 
 Revision 1.38:
 buf=[/home/elad/work/realpath/realpath/foo/bar]
 ret=[/home/elad/work/realpath/realpath/foo/bar]
 errno=[2]
 
 (to be) Revision 1.39:
 buf=[/home/elad/work/realpath/realpath/foo/bar]
 ret=[(null)]
 errno=[2]
 
    [ look how removing the trailing slash makes revision 1.36 behave
       ``wrong''. revision 1.38 didn't introduce bad behavior - it
       fixed a bug in revision 1.36.]
 
 Conclusions:
   1. Don't rely on an inconsistent API. :)
   2. Don't use realpath(3) when you really want stat(2).
 
 If you like what ``revision 1.39'' is doing, use this patch:
 
 Index: getcwd.c
 ===================================================================
 RCS file: /cvsroot/src/lib/libc/gen/getcwd.c,v
 retrieving revision 1.38
 diff -u -p -r1.38 getcwd.c
 --- getcwd.c    5 Jul 2005 02:56:12 -0000       1.38
 +++ getcwd.c    13 Aug 2005 00:05:26 -0000
 @@ -160,12 +160,6 @@ loop:
          */
         if (lstat(resolved, &sb) == -1) {
                 /* Allow nonexistent component if this is the last one. */
 -               while (*q == '/')
 -                       q++;
 -               if (*q == 0 && errno == ENOENT) {
 -                       errno = serrno;
 -                       return (resolved);
 -               }
                 return (NULL);
         }
         if (S_ISLNK(sb.st_mode)) {
 
 Thanks,
 
 -e.
 
 -- 
 Elad Efrat
 PGP Key ID: 0x666EB914