summaryrefslogtreecommitdiff
path: root/src/mv.c
diff options
context:
space:
mode:
authorJim Meyering <jim@meyering.net>1997-12-13 16:22:58 +0000
committerJim Meyering <jim@meyering.net>1997-12-13 16:22:58 +0000
commit295372322d4e6bcca81f10a015b41a5ecacad671 (patch)
treec63d9fabe97792bd29784b4eecec167ca838f51b /src/mv.c
parentb2959537e681c3b747080f8c1f08c5db2c7f07d6 (diff)
downloadcoreutils-295372322d4e6bcca81f10a015b41a5ecacad671.tar.xz
(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.
Diffstat (limited to 'src/mv.c')
-rw-r--r--src/mv.c61
1 files 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)