From 295372322d4e6bcca81f10a015b41a5ecacad671 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Sat, 13 Dec 1997 16:22:58 +0000 Subject: (do_move): If rename fails for any reason (not just when errno == EXDEV), then revert to trying copy-then-unlink. This is necessary to allow moving files within certain types of Linux NFS mounted filesystems. Reported by Marty Leisner. --- src/mv.c | 61 ++++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/src/mv.c b/src/mv.c index 13afc8fe0..9b08bf50f 100644 --- a/src/mv.c +++ b/src/mv.c @@ -239,7 +239,7 @@ copy_reg (const char *source, const char *dest, const struct stat *source_stats) /* Move SOURCE onto DEST. Handles cross-filesystem moves. If SOURCE is a directory, DEST must not exist. - Return 0 if successful, 1 if an error occurred. */ + Return 0 if successful, non-zero if an error occurred. */ static int do_move (const char *source, const char *dest) @@ -247,6 +247,7 @@ do_move (const char *source, const char *dest) char *dest_backup = NULL; struct stat source_stats; struct stat dest_stats; + int fail; if (lstat (source, &source_stats) != 0) { @@ -319,37 +320,47 @@ do_move (const char *source, const char *dest) if (verbose) printf ("%s -> %s\n", source, dest); - if (rename (source, dest) == 0) - { - return 0; - } + /* Always try rename first. */ + fail = rename (source, dest); - if (errno != EXDEV) + if (fail) { - error (0, errno, _("cannot move `%s' to `%s'"), source, dest); - goto un_backup; - } + /* This may mean SOURCE and DEST are on different devices. + It may also (conceivably) mean that even though they are + on the same device, rename isn't implemented for that device. - /* rename failed on cross-filesystem link. Copy the file instead. */ + E.g., (from Joel N. Weber), + [...] there might someday be cases where you can't rename but you + can copy where the device name is the same, especially on Hurd. + Consider an ftpfs with a primitive ftp server that supports + uploading, downloading and deleting, but not renaming. - if (copy_reg (source, dest, &source_stats)) - goto un_backup; + Also, note that comparing device numbers is not a reliable check + for `can-rename'. Some systems can be set up so that files from + many different physical devices all have the same st_dev field. + This is a feature of some NFS mounting configurations. - if (unlink (source)) - { - error (0, errno, _("cannot remove `%s'"), source); - return 1; - } + Try copying-then-removing SOURCE instead. - return 0; + This function used to resort to copying only when rename failed + and set errno to EXDEV. */ - un_backup: - if (dest_backup) - { - if (rename (dest_backup, dest)) - error (0, errno, _("cannot un-backup `%s'"), dest); + fail = copy_reg (source, dest, &source_stats); + if (fail) + { + /* Restore original destination file DEST if made a backup. */ + if (dest_backup && rename (dest_backup, dest)) + error (0, errno, _("cannot un-backup `%s'"), dest); + } + else + { + fail = unlink (source); + if (fail) + error (0, errno, _("cannot remove `%s'"), source); + } } - return 1; + + return fail; } static int @@ -367,7 +378,7 @@ strip_trailing_slashes_2 (char *path) /* Move file SOURCE onto DEST. Handles the case when DEST is a directory. DEST_IS_DIR must be nonzero when DEST is a directory or a symlink to a directory and zero otherwise. - Return 0 if successful, 1 if an error occurred. */ + Return 0 if successful, non-zero if an error occurred. */ static int movefile (char *source, char *dest, int dest_is_dir) -- cgit v1.2.3-70-g09d2