When a file cannot be deleted because of ETXTBSY (in particular, when an
authorDavid Dykstra <dwd@samba.org>
Thu, 11 Mar 1999 22:17:42 +0000 (22:17 +0000)
committerDavid Dykstra <dwd@samba.org>
Thu, 11 Mar 1999 22:17:42 +0000 (22:17 +0000)
executable is busy on HPUX), rename it instead to .rsyncNNN.  Most of
the code was submitted by Ketil Kristiansen <ketil-k@osc.no>

generator.c
hlink.c
receiver.c
rsync.c
util.c

index 1be4717..8b0eeab 100644 (file)
@@ -182,7 +182,7 @@ void recv_generator(char *fname,struct file_list *flist,int i,int f_out)
        if (S_ISDIR(file->mode)) {
                if (dry_run) return;
                if (statret == 0 && !S_ISDIR(st.st_mode)) {
-                       if (do_unlink(fname) != 0) {
+                       if (robust_unlink(fname) != 0) {
                                rprintf(FERROR,"unlink %s : %s\n",fname,strerror(errno));
                                return;
                        }
diff --git a/hlink.c b/hlink.c
index 76b2fb6..561ea73 100644 (file)
--- a/hlink.c
+++ b/hlink.c
@@ -120,7 +120,7 @@ static void hard_link_one(int i)
        } else {
                if (st2.st_dev == st1.st_dev && st2.st_ino == st1.st_ino) return;
                
-               if (do_unlink(f_name(&hlink_list[i])) != 0 ||
+               if (robust_unlink(f_name(&hlink_list[i])) != 0 ||
                    do_link(f_name(&hlink_list[i-1]),f_name(&hlink_list[i])) != 0) {
                        if (verbose > 0)
                                rprintf(FINFO,"link %s => %s : %s\n",
index 5882e60..0533fff 100644 (file)
@@ -83,7 +83,7 @@ static void add_delete_entry(struct file_struct *file)
 static void delete_one(struct file_struct *f)
 {
        if (!S_ISDIR(f->mode)) {
-               if (do_unlink(f_name(f)) != 0) {
+               if (robust_unlink(f_name(f)) != 0) {
                        rprintf(FERROR,"unlink %s : %s\n",f_name(f),strerror(errno));
                } else if (verbose) {
                        rprintf(FINFO,"deleting %s\n",f_name(f));
diff --git a/rsync.c b/rsync.c
index d7f2d3c..489afcf 100644 (file)
--- a/rsync.c
+++ b/rsync.c
@@ -57,7 +57,7 @@ int delete_file(char *fname)
        int ret;
        extern int recurse;
 
-       if (do_unlink(fname) == 0 || errno == ENOENT) return 0;
+       if (robust_unlink(fname) == 0 || errno == ENOENT) return 0;
 
 #if SUPPORT_LINKS
        ret = do_lstat(fname, &st);
@@ -262,7 +262,7 @@ void finish_transfer(char *fname, char *fnametmp, struct file_struct *file)
                return;
 
        /* move tmp file over real file */
-       if (do_rename(fnametmp,fname) != 0) {
+       if (robust_rename(fnametmp,fname) != 0) {
                if (errno == EXDEV) {
                        /* rename failed on cross-filesystem link.  
                           Copy the file instead. */
@@ -272,12 +272,11 @@ void finish_transfer(char *fname, char *fnametmp, struct file_struct *file)
                        } else {
                                set_perms(fname,file,NULL,0);
                        }
-                       do_unlink(fnametmp);
                } else {
                        rprintf(FERROR,"rename %s -> %s : %s\n",
                                fnametmp,fname,strerror(errno));
-                       do_unlink(fnametmp);
                }
+               do_unlink(fnametmp);
        } else {
                set_perms(fname,file,NULL,0);
        }
diff --git a/util.c b/util.c
index 16d8f6c..86ee3f0 100644 (file)
--- a/util.c
+++ b/util.c
@@ -24,6 +24,8 @@
   */
 #include "rsync.h"
 
+extern int verbose;
+
 /****************************************************************************
 Set a fd into nonblocking mode. Uses POSIX O_NONBLOCK if available,
 else
@@ -287,7 +289,7 @@ int copy_file(char *source, char *dest, mode_t mode)
                return -1;
        }
 
-       if (do_unlink(dest) && errno != ENOENT) {
+       if (robust_unlink(dest) && errno != ENOENT) {
                rprintf(FERROR,"unlink %s: %s\n",
                        dest,strerror(errno));
                return -1;
@@ -323,6 +325,81 @@ int copy_file(char *source, char *dest, mode_t mode)
        return 0;
 }
 
+/*
+  Robust unlink: some OS'es (HPUX) refuse to unlink busy files, so
+  rename to <path>/.rsyncNNN instead. Note that successive rsync runs
+  will shuffle the filenames around a bit as long as the file is still
+  busy; this is because this function does not know if the unlink call
+  is due to a new file coming in, or --delete trying to remove old
+  .rsyncNNN files, hence it renames it each time.
+*/
+/* MAX_RENAMES should be 10**MAX_RENAMES_DIGITS */
+#define MAX_RENAMES_DIGITS 3
+#define MAX_RENAMES 1000
+
+int robust_unlink(char *fname)
+{
+#ifndef ETXTBSY
+       return do_unlink(fname);
+#else
+       static int counter = 1;
+       int rc, pos, start;
+       char path[MAXPATHLEN];
+
+       rc = do_unlink(fname);
+       if ((rc == 0) || (errno != ETXTBSY))
+               return rc;
+
+       strlcpy(path, fname, MAXPATHLEN);
+
+       pos = strlen(path);
+       while((path[--pos] != '/') && (pos >= 0))
+               ;
+       ++pos;
+       strlcpy(&path[pos], ".rsync", MAXPATHLEN-pos);
+       pos += sizeof(".rsync")-1;
+
+       if (pos > (MAXPATHLEN-MAX_RENAMES_DIGITS-1)) {
+               errno = ETXTBSY;
+               return -1;
+       }
+
+       /* start where the last one left off to reduce chance of clashes */
+       start = counter;
+       do {
+               sprintf(&path[pos], "%03d", counter);
+               if (++counter >= MAX_RENAMES)
+                       counter = 1;
+       } while (((rc = access(path, 0)) == 0) && (counter != start));
+
+       if (verbose > 0)
+               rprintf(FINFO,"renaming %s to %s because of text busy\n",
+                                           fname, path);
+
+       /* maybe we should return rename()'s exit status? Nah. */
+       if (do_rename(fname, path) != 0) {
+               errno = ETXTBSY;
+               return -1;
+       }
+       return 0;
+#endif
+}
+
+int robust_rename(char *from, char *to)
+{
+#ifndef ETXTBSY
+       return do_rename(from, to);
+#else
+       int rc = do_rename(from, to);
+       if ((rc == 0) || (errno != ETXTBSY))
+               return rc;
+       if (robust_unlink(to) != 0)
+               return -1;
+       return do_rename(from, to);
+#endif
+    }
+
+
 /* sleep for a while via select */
 void u_sleep(int usec)
 {