bug-coreutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

mv fails to link all moved files


From: Jim Meyering
Subject: mv fails to link all moved files
Date: Fri, 12 Mar 2004 12:52:58 +0100

IIDA Yosiaki <address@hidden> wrote:
> It seems for me that mv in coreutils 5.1.3 on sparc-sun-solaris2.8
> platform fails to move files in a certain condition.

Thank you for reporting that and for the detailed test case!
This is indeed a bug.  It also affects the latest: coreutils-5.2.0.
This bug appears to pre-date fileutils-3.16 (Jan 1997) -- when I
factored copy.c out of cp.c.  I didn't look further than that.

Here's a small test case, assuming that `.' and /var/tmp are
on different partitions.

dst=/var/tmp
> a; ln a b; mv a b $dst; set - `ls -iC $dst/[ab]`; test $1 = $3 || echo fail

I've included a patch below.
Note that I could have simply added `|| x->move_mode'
to the existing list of disjuncts, but that would have
made `mv' incur the excessive overhead of recording a
dev/ino => dst_path triple for every file being moved.

As usual, I'll add tests, too.

2004-03-12  Jim Meyering  <address@hidden>

        Sometimes, when source and destination partition are different,
        mv mistakenly fails to preserve a hard link.  Reported by IIDA Yosiaki.

        * src/copy.c: When moving a set of N hard-linked files between
        partitions, via two or more command line arguments where the
        command line argument containing the Nth link contains no other
        link to that same file, mv would mistakenly copy the file, rather
        than hard-linking it to the other(s).  That happens because when the
        final link is processed, its link count has been reduced to 1 since
        the other links have been `copied' to the destination partition
        and the source links have been removed.
        (copy_internal): When in move mode, use the source dev/inode
        pair to look up destination name even when st_nlink == 1.
        * src/cp-hash.c (src_to_dest_lookup): New function.
        * src/cp-hash.h (remember_created): Add prototype.

Index: src/copy.c
===================================================================
RCS file: /fetish/cu/src/copy.c,v
retrieving revision 1.158
diff -u -p -u -p -r1.158 copy.c
--- src/copy.c  6 Mar 2004 17:40:56 -0000       1.158
+++ src/copy.c  12 Mar 2004 11:48:59 -0000
@@ -1099,6 +1099,15 @@ copy_internal (const char *src_path, con
 
      Sometimes, when preserving links, we have to record dev/ino even
      though st_nlink == 1:
+     - when in move_mode, since we may be moving a group of N hard-linked
+       files (via two or more command line arguments) to a different
+       partition; the links may be distributed among the command line
+       arguments (possibly hierarchies) so that the link count of
+       the final, once-linked source file is reduced to 1 when it is
+       considered below.  But in this case (for mv) we don't need to
+       incur the expense of recording the dev/ino => name mapping; all we
+       really need is a lookup, to see if the dev/ino pair has already
+       been copied.
      - when using -H and processing a command line argument;
        that command line argument could be a symlink pointing to another
        command line argument.  With `cp -H --preserve=link', we hard-link
@@ -1114,12 +1123,16 @@ copy_internal (const char *src_path, con
      command line args.  Using the same hash table to preserve hard
      links means that it may not be cleared.  */
 
-  if ((x->preserve_links
-       && (1 < src_sb.st_nlink
-          || (command_line_arg
-              && x->dereference == DEREF_COMMAND_LINE_ARGUMENTS)
-          || x->dereference == DEREF_ALWAYS))
-      || (x->recursive && S_ISDIR (src_type)))
+  if (x->move_mode && src_sb.st_nlink == 1)
+    {
+       earlier_file = src_to_dest_lookup (src_sb.st_ino, src_sb.st_dev);
+    }
+  else if ((x->preserve_links
+           && (1 < src_sb.st_nlink
+               || (command_line_arg
+                   && x->dereference == DEREF_COMMAND_LINE_ARGUMENTS)
+               || x->dereference == DEREF_ALWAYS))
+          || (x->recursive && S_ISDIR (src_type)))
     {
       earlier_file = remember_copied (dst_path, src_sb.st_ino, src_sb.st_dev);
     }
Index: src/cp-hash.c
===================================================================
RCS file: /fetish/cu/src/cp-hash.c,v
retrieving revision 1.30
diff -u -p -u -p -r1.30 cp-hash.c
--- src/cp-hash.c       25 Oct 2003 15:33:43 -0000      1.30
+++ src/cp-hash.c       12 Mar 2004 08:52:15 -0000
@@ -1,5 +1,5 @@
 /* cp-hash.c  -- file copying (hash search routines)
-   Copyright (C) 89, 90, 91, 1995-2003 Free Software Foundation.
+   Copyright (C) 89, 90, 91, 1995-2004 Free Software Foundation.
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -116,6 +116,20 @@ remember_created (const char *path)
   return 0;
 }
 
+/* If INO/DEV correspond to an already-copied source file, return the
+   name of the corresponding destination file.  Otherwise, return NULL.  */
+
+char *
+src_to_dest_lookup (ino_t ino, dev_t dev)
+{
+  struct Src_to_dest ent;
+  struct Src_to_dest const *e;
+  ent.st_ino = ino;
+  ent.st_dev = dev;
+  e = hash_lookup (src_to_dest, &ent);
+  return e ? e->name : NULL;
+}
+
 /* Add path NAME, copied from inode number INO and device number DEV,
    to the list of files we have copied.
    Return NULL if inserted, otherwise non-NULL. */
Index: src/cp-hash.h
===================================================================
RCS file: /fetish/cu/src/cp-hash.h,v
retrieving revision 1.6
diff -u -p -u -p -r1.6 cp-hash.h
--- src/cp-hash.h       15 Dec 2002 20:54:29 -0000      1.6
+++ src/cp-hash.h       12 Mar 2004 08:37:59 -0000
@@ -3,3 +3,4 @@ void forget_all (void);
 void forget_created (ino_t ino, dev_t dev);
 char *remember_copied (const char *node, ino_t ino, dev_t dev);
 int remember_created (const char *path);
+char *src_to_dest_lookup (ino_t ino, dev_t dev);




reply via email to

[Prev in Thread] Current Thread [Next in Thread]