Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 11 Feb 2001 23:09:44 +0100 (CET)
From:      mkamm@gmx.net
To:        FreeBSD-gnats-submit@freebsd.org
Subject:   bin/25013: mv(1) cannot move unresolvable symlinks across devices
Message-ID:  <200102112209.f1BM9iB02125@homebox.kammerhofer.org>

next in thread | raw e-mail | index | archive | help

>Number:         25013
>Category:       bin
>Synopsis:       mv(1) cannot move unresolvable symlinks across devices
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Sun Feb 11 14:50:01 PST 2001
>Closed-Date:
>Last-Modified:
>Originator:     Martin Kammerhofer
>Release:        FreeBSD 4.2-STABLE i386
>Organization:
Universität Graz
>Environment:
>Description:

Unresolvable symlinks cannot be moved across devices with mv(1).

>How-To-Repeat:

I assume that /tmp and $HOME are on different devices:
$ ln -sf /GENERIC /tmp/generic
$ mv /tmp/generic ~ # this does work

Now I try to move a broken symlink
$ ln -sf /NOSUCH /tmp/nosuch
$ mv /tmp/nosuch ~ # this does work too

Finally I trigger the bug:
$ ln -sf /NODIR/NOFILE /tmp/nofile
$ mv /tmp/nofile ~
mv: cannot resolve /tmp/nofile: /NODIR

Even option -f doesn't help here.

>Fix:

The problem was introduced with a code snippet that protects against
moving mountpoints. Moving mountpoints is bad, because that would
trigger "cp -pRP /mountpoint newname".  My patch invokes this code
snippet only if a directory is to be moved and bypasses it otherwise.
(My patch also tries to avoid redundant lstat(2) calls.)

Index: mv.c
===================================================================
RCS file: /home/ncvs/src/bin/mv/mv.c,v
retrieving revision 1.27
diff -u -r1.27 mv.c
--- mv.c	2000/07/20 18:30:00	1.27
+++ mv.c	2001/02/10 03:51:23
@@ -152,10 +152,11 @@
 do_move(from, to)
 	char *from, *to;
 {
-	struct stat sb;
-	int ask, ch, first;
+	struct stat sb_from, sb;
+	int ask, ch, first, got_from;
 	char modep[15];
 
+	got_from = 0; /* lstat of "from" not done yet */
 	/*
 	 * Check access.  If interactive and file exists, ask user if it
 	 * should be replaced.  Otherwise if file exists but isn't writable
@@ -164,10 +165,11 @@
 	if (!fflg && !access(to, F_OK)) {
 
 		/* prompt only if source exist */
-	        if (lstat(from, &sb) == -1) {
+	        if (lstat(from, &sb_from) == -1) {
 			warn("%s", from);
 			return (1);
 		}
+		got_from = 1;
 
 #define YESNO "(y/n [n]) "
 		ask = 0;
@@ -202,6 +204,14 @@
 		struct statfs sfs;
 		char path[MAXPATHLEN];
 
+		/* if from isn't a directory it can't be a mount point */
+	        if (!got_from && lstat(from, &sb_from) == -1) {
+			warn("%s", from);
+			return (1);
+		}
+		if (!S_ISDIR(sb_from.st_mode))
+		    goto copy_it;
+		    
 		/* Can't mv(1) a mount point. */
 		if (realpath(from, path) == NULL) {
 			warnx("cannot resolve %s: %s", from, path);
@@ -221,12 +231,13 @@
 	 * it's a regular file, do the copy internally; otherwise, use
 	 * cp and rm.
 	 */
-	if (lstat(from, &sb)) {
+	if (!got_from && lstat(from, &sb_from)) {
 		warn("%s", from);
 		return (1);
 	}
-	return (S_ISREG(sb.st_mode) ?
-	    fastcopy(from, to, &sb) : copy(from, to));
+ copy_it:
+	return (S_ISREG(sb_from.st_mode) ?
+	    fastcopy(from, to, &sb_from) : copy(from, to));
 }
 
 int

>Release-Note:
>Audit-Trail:
>Unformatted:


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message




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