From: David Dykstra Date: Thu, 11 Mar 1999 22:17:42 +0000 (+0000) Subject: When a file cannot be deleted because of ETXTBSY (in particular, when an X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/commitdiff_plain/c7c11a0d4c315a052dddf97cc6d3d63a54b4a831 When a file cannot be deleted because of ETXTBSY (in particular, when an executable is busy on HPUX), rename it instead to .rsyncNNN. Most of the code was submitted by Ketil Kristiansen --- diff --git a/generator.c b/generator.c index 1be47176..8b0eeab1 100644 --- a/generator.c +++ b/generator.c @@ -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 76b2fb6e..561ea736 100644 --- 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", diff --git a/receiver.c b/receiver.c index 5882e605..0533fffe 100644 --- a/receiver.c +++ b/receiver.c @@ -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 d7f2d3ce..489afcf1 100644 --- 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 16d8f6cc..86ee3f0c 100644 --- 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 /.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) {