Skip site navigation (1)Skip section navigation (2)
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>