From owner-freebsd-fs Mon Jul 22 21: 2:19 2002 Delivered-To: freebsd-fs@freebsd.org Received: from mx1.FreeBSD.org (mx1.FreeBSD.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id CB20837B400 for ; Mon, 22 Jul 2002 21:02:12 -0700 (PDT) Received: from mailman.zeta.org.au (mailman.zeta.org.au [203.26.10.16]) by mx1.FreeBSD.org (Postfix) with ESMTP id 9E4E943E5E for ; Mon, 22 Jul 2002 21:02:11 -0700 (PDT) (envelope-from bde@zeta.org.au) Received: from bde.zeta.org.au (bde.zeta.org.au [203.2.228.102]) by mailman.zeta.org.au (8.9.3/8.8.7) with ESMTP id OAA24101; Tue, 23 Jul 2002 14:02:06 +1000 Date: Tue, 23 Jul 2002 14:06:14 +1000 (EST) From: Bruce Evans X-X-Sender: bde@gamplex.bde.org To: Alfred Perlstein Cc: fs@FreeBSD.ORG Subject: Re: rename hardlinks "works" on FreeBSD, but no-op on others In-Reply-To: <20020722191522.GA77219@elvis.mu.org> Message-ID: <20020723133856.L28400-100000@gamplex.bde.org> MIME-Version: 1.0 Content-Type: TEXT/PLAIN; charset=US-ASCII Sender: owner-freebsd-fs@FreeBSD.ORG Precedence: bulk List-ID: List-Archive: (Web Archive) List-Help: (List Instructions) List-Subscribe: List-Unsubscribe: X-Loop: FreeBSD.org On Mon, 22 Jul 2002, Alfred Perlstein wrote: > A coworker recently asked me (slightly modified): > > ...it says that if you rename file A to file B where A and B are > hard links to each other, it does nothing. I thought this was > kind of odd so I tried the experiment on Linux and sure enough > it does the same there: no error and both A and B still exist > after the rename. > > I tried the same experiment on Solaris 8 and got the same effect. > > I tried the same experiment on FreeBSD and it did the obviously > correct thing and removed the old name. > > What gives? > > It seems that we do the right thing, however: > > 1) are we standards compliant? No. > 2) just for curiousity, why would others silently fail? Because they are standards compliant (even though the standard may be wrong here). I fixed this in Linux back in 1992 or 1993, and in my version of FreeBSD a few months later, but never got around to committing the fix. I know too much about this because Minix has or had test programs for it, and I ran these tests to find bugs in early versions of Linux, FreeBSD and in the tests themselves. This patch has been tested for 9-10 years :-). It is a bit incomplete -- it doesn't remove the broken code, and filesystems still do extra work to handle the fvp == tvp case. %%% Index: vfs_syscalls.c =================================================================== RCS file: /home/ncvs/src/sys/kern/vfs_syscalls.c,v retrieving revision 1.268 diff -u -2 -r1.268 vfs_syscalls.c --- vfs_syscalls.c 13 Jul 2002 04:07:12 -0000 1.268 +++ vfs_syscalls.c 13 Jul 2002 20:22:32 -0000 @@ -2519,4 +2516,5 @@ if (fvp == tdvp) error = EINVAL; +#if 0 /* * If source is the same as the destination (that is the @@ -2529,4 +2527,13 @@ fromnd.ni_cnd.cn_namelen)) error = -1; +#else + /* + * If the source is the same as the destination (that is, if they + * are links to the same vnode), then there is nothing to do. + * POSIX standard. + */ + if (fvp == tvp) + error = -1; +#endif out: if (!error) { %%% Here is the ufs code for the fvp == tvp case: % /* % * Check if just deleting a link name or if we've lost a race. % * If another process completes the same rename after we've looked % * up the source and have blocked looking up the target, then the % * source and target inodes may be identical now although the % * names were never linked. % */ % if (fvp == tvp) { % if (fvp->v_type == VDIR) { % /* % * Linked directories are impossible, so we must % * have lost the race. Pretend that the rename % * completed before the lookup. % */ % #ifdef UFS_RENAME_DEBUG % printf("ufs_rename: fvp == tvp for directories\n"); % #endif % error = ENOENT; % goto abortit; % } % % /* Release destination completely. */ % vput(tdvp); % vput(tvp); % % /* % * Delete source. There is another race now that everything % * is unlocked, but this doesn't cause any new complications. % * Relookup() may find a file that is unrelated to the % * original one, or it may fail. Too bad. % */ % vrele(fdvp); % vrele(fvp); % fcnp->cn_flags &= ~MODMASK; % fcnp->cn_flags |= LOCKPARENT | LOCKLEAF; % if ((fcnp->cn_flags & SAVESTART) == 0) % panic("ufs_rename: lost from startdir"); % fcnp->cn_nameiop = DELETE; % VREF(fdvp); % error = relookup(fdvp, &fvp, fcnp); % if (error == 0) % vrele(fdvp); % if (fvp == NULL) { % #ifdef UFS_RENAME_DEBUG % printf("ufs_rename: from name disappeared\n"); % #endif % return (ENOENT); % } % error = VOP_REMOVE(fdvp, fvp, fcnp); % if (fdvp == fvp) % vrele(fdvp); % else % vput(fdvp); % vput(fvp); % return (error); % } I don't understand the race case now, although ISTR fixing it. Races from using relookup() may also need to be considered. Bruce To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-fs" in the body of the message