Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 30 Jul 1995 19:45:24 +1000
From:      Bruce Evans <bde@zeta.org.au>
To:        bde@zeta.org.au, markd@grizzly.com, terry@cs.weber.edu
Cc:        current@freebsd.org
Subject:   Re: kern/169: Errors from mkdir & mv when directories paths end with "/"
Message-ID:  <199507300945.TAA02305@godzilla.zeta.org.au>

next in thread | raw e-mail | index | archive | help
I have finally got my 1.1.5 fixes for trailing slashes working in 2.x.
They make FreeBSD work the same as SunOS for normal cases and better
(most like hpux) for symlinks.

Mark Diekhans <markd@grizzly.com> wrote a long time ago (24 Jan 1995):

>>>>>> "Bruce" == Bruce Evans <bde@zeta.org.au> writes:

>    Bruce> [mv foo baz/]
>    >> (2) Moved to baz/foo

>    Bruce> I should have asked for this to be done with a syscall - mv(1)
>Yes, much more useful, enclosed..

>>>>>> "Terry" == Terry Lambert <terry@cs.weber.edu> writes:

>    Terry> [ ... tables of sco, hpux, and sun behaviour ... ] Would it be too
>    Terry> much trouble to put a BSD line in these tables?  8-).
>I though you guys had this all memorized :-)  No problem, enclosed..

>"mkdir foo/" and "mv foo/ baz" (command and system call:

>              | mkdir        | mv       | mkdir        | rename
>              | cmd          | cmd      | call         | call
>      ------------------------------------------------------------
>          sco | ok           | ok       | ok           | ok
>         hpux | ok           | ok       | ok           | fails *
>          sun | ok           | ok       | ok           | ok
>      FreeBSD | No such file | Is a dir | No such file | Is a dir
         Linux | No such file | ok       | No such file | Op not permitted
   new-FreeBSD | ok           | ok       | ok           | ok

>     * HPUX returns "not owner" on a rename call of a directory regardless
>       of the trailing /.

>rename ("foo", "baz/"); (foo is a directory):

>         baz: | does not     | existing  | empty      | nonempty
>              | exist        | file      | dir        | directory
>      ---------------------------------------------------------------
>          sco | RENAMED      | Not a dir | REPLACED   | File exists
>         hpux | RENAMED      | Not a dir | REPLACED   | File exists
>          sun | RENAMED      | Not a dir | REPLACED   | Dir not empty
>      FreeBSD | No such file | Is a dir  | Is a dir   | Is a dir
         Linux | No such file | Not a dir | Op not perm| Op not perm
   new-FreeBSD | RENAMED      | Not a dir | REPLACED   | Dir not empty

>      baz is a symlink to:
>              | does not     | existing  | empty      | nonempty
>              | exist        | file      | dir        | directory
>      ---------------------------------------------------------------
>          sco | Not a dir    | Not a dir | Not a dir  | Not a dir
>         hpux | RENAMED      | Not a dir | RENAMED    | File exists
>          sun | Not a dir    | Not a dir | Not a dir  | Not a dir
>      FreeBSD | Is a dir     | Is a dir  | Is a dir   | Is a dir
         Linux | No such file | Not a dir | Op not perm| Op not perm
   new-FreeBSD | RENAMED      | Not a dir | RENAMED    | Dir not empty

                ----- rmdir("baz/"); ----- ----- unlink("baz/"); -----
          baz: | does not     | existing  | does not     | existing
               | exist        | file      | exist        | file
       ---------------------------------------------------------------
       FreeBSD | No such file | Is a dir  | No such file | Is a dir
         Linux | No such file | Not a dir | No such file | Not a dir
   new-FreeBSD | No such file | Not a dir | No such file | Not a dir

Other new (and maybe old) behaviour:

	rename("existing-file", "non-existing-dir/"): ENOENT
	rename("existing-file/", "non-existing-dir"): ENOTDIR
	rmdir("symlink-to-existing-dir/"): succeeds

Bruce

diff -c2 src/sys/kern/vfs_lookup.c~ src/sys/kern/vfs_lookup.c
*** src/sys/kern/vfs_lookup.c~	Wed May 31 20:55:21 1995
--- src/sys/kern/vfs_lookup.c	Sun Jul 30 16:18:03 1995
***************
*** 258,261 ****
--- 271,275 ----
  	int wantparent;			/* 1 => wantparent or lockparent flag */
  	int rdonly;			/* lookup read-only flag bit */
+ 	int trailing_slash;
  	int error = 0;
  	struct componentname *cnp = &ndp->ni_cnd;
***************
*** 303,306 ****
--- 317,339 ----
  	ndp->ni_pathlen -= cnp->cn_namelen;
  	ndp->ni_next = cp;
+ 
+ 	/*
+ 	 * Replace multiple slashes by a single slash and trailing slashes
+ 	 * by a null.  This must be done before VOP_LOOKUP() because some
+ 	 * fs's don't know about trailing slashes.  Remember if there were
+ 	 * trailing slashes to handle symlinks, existing non-directories
+ 	 * and non-existing files that won't be directories specially later.
+ 	 */
+ 	trailing_slash = 0;
+ 	while (*cp == '/' && (cp[1] == '/' || cp[1] == '\0')) {
+ 		cp++;
+ 		ndp->ni_pathlen--;
+ 		if (*cp == '\0') {
+ 			trailing_slash = 1;
+ 			*ndp->ni_next = '\0';	/* XXX for direnter() ... */
+ 		}
+ 	}
+ 	ndp->ni_next = cp;
+ 
  	cnp->cn_flags |= MAKEENTRY;
  	if (*cp == '\0' && docache == 0)
***************
*** 407,410 ****
--- 440,448 ----
  			goto bad;
  		}
+ 		if (*cp == '\0' && trailing_slash &&
+ 		     !(cnp->cn_flags & WILLBEDIR)) {
+ 			error = ENOENT;
+ 			goto bad;
+ 		}
  		/*
  		 * We return with ni_vp NULL to indicate that the entry
***************
*** 438,444 ****
  	 */
  	if ((dp->v_type == VLNK) &&
! 	    ((cnp->cn_flags & FOLLOW) || *ndp->ni_next == '/')) {
  		cnp->cn_flags |= ISSYMLINK;
  		return (0);
  	}
  
--- 476,491 ----
  	 */
  	if ((dp->v_type == VLNK) &&
! 	    ((cnp->cn_flags & FOLLOW) || trailing_slash ||
! 	     *ndp->ni_next == '/')) {
  		cnp->cn_flags |= ISSYMLINK;
  		return (0);
+ 	}
+ 
+ 	/*
+ 	 * Check for bogus trailing slashes.
+ 	 */
+ 	if (trailing_slash && dp->v_type != VDIR) {
+ 		error = ENOTDIR;
+ 		goto bad2;
  	}
  
diff -c2 src/sys/kern/vfs_syscalls.c~ src/sys/kern/vfs_syscalls.c
*** src/sys/kern/vfs_syscalls.c~	Fri Jul 14 02:05:22 1995
--- src/sys/kern/vfs_syscalls.c	Fri Jul 14 02:06:18 1995
***************
*** 835,838 ****
--- 835,840 ----
  		nd.ni_cnd.cn_nameiop = CREATE;
  		nd.ni_cnd.cn_flags = LOCKPARENT;
+ 		if (vp->v_type == VDIR)
+ 			nd.ni_cnd.cn_flags |= WILLBEDIR;
  		nd.ni_dirp = uap->link;
  		error = namei(&nd);
***************
*** 1820,1823 ****
--- 1822,1827 ----
  	NDINIT(&tond, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART,
  		UIO_USERSPACE, uap->to, p);
+ 	if (fromnd.ni_vp->v_type == VDIR)
+ 		tond.ni_cnd.cn_flags |= WILLBEDIR;
  	error = namei(&tond);
  	if (error) {
***************
*** 1905,1908 ****
--- 1919,1923 ----
  
  	NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, uap->path, p);
+ 	nd.ni_cnd.cn_flags |= WILLBEDIR;
  	error = namei(&nd);
  	if (error)
diff -c2 src/sys/sys/namei.h~ src/sys/sys/namei.h
*** src/sys/sys/namei.h~	Sun Jul 30 00:45:36 1995
--- src/sys/sys/namei.h	Sun Jul 30 00:46:05 1995
***************
*** 135,138 ****
--- 135,139 ----
  #define ISLASTCN	0x08000	/* this is last component of pathname */
  #define ISSYMLINK	0x10000	/* symlink needs interpretation */
+ #define	WILLBEDIR	0x20000	/* new files will be dirs; allow trailing / */
  #define PARAMASK	0xfff00	/* mask of parameter descriptors */
  /*



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