NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

kern/45425: how to restore traditional unix behaviour for slashes on the end of pathnames

>Number:         45425
>Category:       kern
>Synopsis:       how to restore traditional unix behaviour for slashes on the 
>end of pathnames
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Tue Oct 04 19:55:00 +0000 2011
>Originator:     Greg A. Woods
>Release:        NetBSD-current 2011/10/04
Planix, Inc.; Kelowna, BC; Canada
System: NetBSD
Architecture: all
Machine: all

        Traditional Unix, from at least V5 onwards and right through to
        the end (V10) and perhaps beyond (plan 9?), always treated
        slashes (in groups of one or more) in pathnames purely as
        separators between components, and thus trailing slashes were
        always effectively ignored (treated as NULs).

        At some point BSD changed this interpretation of trailing
        slashes.  Apparently they forgot that appending "/." was the
        correct and obvious way to specify that the final component of a
        pathname must be a directory.

        There has been discussion in tech-userlevel over the past decade
        about this issue because the BSD behaviour has had "unexpected"
        fallout causing real bugs and other odd problems.  I.e. the BSD
        behaviour has proven to be problemetic and at the same time has
        not really added anything new or novel which has value
        significant enough to justify the problems, and which could not
        already be obtained with existing well known techniques
        (i.e. the appending of "/." instead of just "/").  The saving of
        one character which imparts even more concrete meaning is not
        worth all the problems it caused.

        Long ago I think I promised to post a patch which restores the
        original behaviour after I had tested it, and I think 8 years of
        testing in releases since NetBSD-1.6, and with a wide-spread
        user-base beyond just myself, is sufficient.  :-)

        The only change of behaviour this "fix" causes which might
        trouble some people, but which I find to be a feature for my own
        use since it gives me extra information without me having to do
        anything extra of different, is that "ls" of a symlink to a
        directory, even when a trailing slash is appeneded to the link
        name, still shows just the link, not the contents of the target
        directory.  Adding the trailing "/."  will of course show the
        contents of the target directory.  The only time this shows up
        is of course when one uses filename completion in an interactive
        shell and the shell shows just a trailing slash -- the proper
        fix would be for the shell to show a trailing "/." when the
        previous component is a symlink and the user would then erase
        the "." and continue with completing another pathname component
        if they didn't want to see just inside the directory itself.

        I plan to fix "ksh" and "sh" so that they offer "foo/." instead
        of "foo/" IFF the name "foo" is a symlink (and automatically
        remove the trailing "." if the user hits <tab> again
        immediately).  This would give the user the hint during
        interactive pathname completion that a name is a symlink without
        changing the final behaviour if the target directory is the
        object of interest.



        Because of how the BSD behaviour has snuck into the most recent
        modern unix standards (in final revisions before publication,
        and in direct opposition to previous standards, and with faulty
        rationale to boot), some people will still no doubt prefer if
        this change is wrapped in a conditional protected by a
        conditional of some sort, perhaps a sysctl, or at least an

        I leave the choice of conditional mechanism to the person who
        might choose to tackle this issue.  Showing the basic change
        without a conditional also shows more clearly just how simple
        and elegant the change is without any unnecessary clutter.

Index: sys/kern/vfs_lookup.c
RCS file: /cvs/master/m-NetBSD/main/src/sys/kern/vfs_lookup.c,v
retrieving revision 1.192
diff -u -r1.192 vfs_lookup.c
--- sys/kern/vfs_lookup.c       27 Sep 2011 02:10:55 -0000      1.192
+++ sys/kern/vfs_lookup.c       4 Oct 2011 18:12:05 -0000
@@ -825,9 +825,10 @@
        ndp->ni_pathlen -= cnp->cn_namelen;
        ndp->ni_next = cp;
-        * If this component is followed by a slash, then move the pointer to
-        * the next component forward, and remember that this component must be
-        * a directory.
+        * If this component is followed by one or more slashes then move the
+        * pointer to the next component forward.  If there is another
+        * component following the slash(es) then remember that this component
+        * must be a directory.
        if (*cp == '/') {
                do {
@@ -837,6 +838,8 @@
                ndp->ni_pathlen -= state->slashes;
                ndp->ni_next = cp;
                cnp->cn_flags |= REQUIREDIR;
+               if (*cp != '\0')
+                       cnp->cn_flags |= REQUIREDIR;
        } else {
                state->slashes = 0;
                cnp->cn_flags &= ~REQUIREDIR;
@@ -851,6 +854,7 @@
                        cnp->cn_flags &= ~MAKEENTRY;
                cnp->cn_flags |= ISLASTCN;
+               cnp->cn_flags &= ~REQUIREDIR;
        } else {
                cnp->cn_flags |= MAKEENTRY;
                cnp->cn_flags &= ~ISLASTCN;
@@ -984,9 +988,8 @@
                        goto done;
-                * If this was not the last component, or there were trailing
-                * slashes, and we are not going to create a directory,
-                * then the name must exist.
+                * If this was not the last component, then the name must
+                * exist (and of course it must be a directory).
                if ((cnp->cn_flags & (REQUIREDIR | CREATEDIR)) == REQUIREDIR) {
                        error = ENOENT;

Home | Main Index | Thread Index | Old Index