Date: Tue, 23 Jul 2002 14:06:14 +1000 (EST) From: Bruce Evans <bde@zeta.org.au> To: Alfred Perlstein <bright@mu.org> Cc: fs@FreeBSD.ORG Subject: Re: rename hardlinks "works" on FreeBSD, but no-op on others Message-ID: <20020723133856.L28400-100000@gamplex.bde.org> In-Reply-To: <20020722191522.GA77219@elvis.mu.org>
next in thread | previous in thread | raw e-mail | index | archive | help
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
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20020723133856.L28400-100000>
