From 3cb6f5d6cbe2b1fead0453ffb31488c2ffc2def2 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 25 Mar 1998 06:05:47 +0000 Subject: [PATCH] added a --force option. This options tells rsync to delete directories even if they are not empty. This applies to both the --delete option and to cases where rsync tries to copy a normal file but the destination contains a directory of the same name. Normally rsync will refuse to do a recursive directory deletion in such cases, by using --force the recursive deletion will be done. --- main.c | 12 ++++++++- rsync.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 83 insertions(+), 12 deletions(-) diff --git a/main.c b/main.c index ec408185..d476e21a 100644 --- a/main.c +++ b/main.c @@ -54,6 +54,7 @@ int am_root=0; int orig_umask=0; int relative_paths=0; int numeric_ids = 0; +int force_delete = 0; extern int csum_length; @@ -175,6 +176,9 @@ static void server_options(char **args,int *argc) if (delete_mode) args[ac++] = "--delete"; + if (force_delete) + args[ac++] = "--force"; + if (numeric_ids) args[ac++] = "--numeric-ids"; @@ -439,6 +443,7 @@ static void usage(FILE *f) fprintf(f," --rsync-path PATH specify path to rsync on the remote machine\n"); fprintf(f,"-C, --cvs-exclude auto ignore files in the same way CVS does\n"); fprintf(f," --delete delete files that don't exist on the sending side\n"); + fprintf(f," --force force deletion of directories even if not empty\n"); fprintf(f," --numeric-ids don't map uid/gid values by user/group name\n"); fprintf(f,"-I, --ignore-times don't exclude files that match length and time\n"); fprintf(f,"-T --temp-dir DIR create temporary files in directory DIR\n"); @@ -454,7 +459,7 @@ static void usage(FILE *f) } enum {OPT_VERSION,OPT_SUFFIX,OPT_SENDER,OPT_SERVER,OPT_EXCLUDE, - OPT_EXCLUDE_FROM,OPT_DELETE,OPT_NUMERIC_IDS,OPT_RSYNC_PATH}; + OPT_EXCLUDE_FROM,OPT_DELETE,OPT_NUMERIC_IDS,OPT_RSYNC_PATH,OPT_FORCE}; static char *short_options = "oblLWHpguDCtcahvrRIxnSe:B:T:z"; @@ -463,6 +468,7 @@ static struct option long_options[] = { {"server", 0, 0, OPT_SERVER}, {"sender", 0, 0, OPT_SENDER}, {"delete", 0, 0, OPT_DELETE}, + {"force", 0, 0, OPT_FORCE}, {"numeric-ids", 0, 0, OPT_NUMERIC_IDS}, {"exclude", 1, 0, OPT_EXCLUDE}, {"exclude-from",1, 0, OPT_EXCLUDE_FROM}, @@ -553,6 +559,10 @@ int main(int argc,char *argv[]) delete_mode = 1; break; + case OPT_FORCE: + force_delete = 1; + break; + case OPT_NUMERIC_IDS: numeric_ids = 1; break; diff --git a/rsync.c b/rsync.c index 16f80ed5..34c85f49 100644 --- a/rsync.c +++ b/rsync.c @@ -60,6 +60,75 @@ static void free_sums(struct sum_struct *s) } +/* + * delete a file or directory. If force_delet is set then delete + * recursively + */ +static int delete_file(char *fname) +{ + DIR *d; + struct dirent *di; + char buf[MAXPATHLEN]; + extern int force_delete; + struct stat st; + int ret; + + if (do_unlink(fname) == 0 || errno == ENOENT) return 0; + +#if SUPPORT_LINKS + ret = lstat(fname, &st); +#else + ret = stat(fname, &st); +#endif + if (ret) { + fprintf(FERROR,"stat(%s) : %s\n", fname, strerror(errno)); + return -1; + } + + if (!S_ISDIR(st.st_mode)) { + fprintf(FERROR,"unlink(%s) : %s\n", fname, strerror(errno)); + return -1; + } + + if (do_rmdir(fname) == 0 || errno == ENOENT) return 0; + if (!force_delete || errno != ENOTEMPTY) { + fprintf(FERROR,"rmdir(%s) : %s\n", fname, strerror(errno)); + return -1; + } + + /* now we do a recsursive delete on the directory ... */ + d = opendir(fname); + if (!d) { + fprintf(FERROR,"opendir(%s): %s\n", + fname,strerror(errno)); + return -1; + } + + for (di=readdir(d); di; di=readdir(d)) { + if (strcmp(di->d_name,".")==0 || + strcmp(di->d_name,"..")==0) + continue; + strncpy(buf, fname, (MAXPATHLEN-strlen(di->d_name))-2); + strcat(buf, "/"); + strcat(buf, di->d_name); + buf[MAXPATHLEN-1] = 0; + if (verbose > 0) + fprintf(FINFO,"deleting %s\n", buf); + if (delete_file(buf) != 0) { + closedir(d); + return -1; + } + } + + closedir(d); + + if (do_rmdir(fname) != 0) { + fprintf(FERROR,"rmdir(%s) : %s\n", fname, strerror(errno)); + return -1; + } + + return 0; +} /* send a sums struct down a fd @@ -337,7 +406,7 @@ void recv_generator(char *fname,struct file_list *flist,int i,int f_out) } } } - do_unlink(fname); + delete_file(fname); if (do_symlink(file->link,fname) != 0) { fprintf(FERROR,"link %s -> %s : %s\n", fname,file->link,strerror(errno)); @@ -356,7 +425,7 @@ void recv_generator(char *fname,struct file_list *flist,int i,int f_out) if (statret != 0 || st.st_mode != file->mode || st.st_rdev != file->rdev) { - do_unlink(fname); + delete_file(fname); if (verbose > 2) fprintf(FERROR,"mknod(%s,0%o,0x%x)\n", fname,(int)file->mode,(int)file->rdev); @@ -397,15 +466,7 @@ void recv_generator(char *fname,struct file_list *flist,int i,int f_out) } if (!S_ISREG(st.st_mode)) { - /* its not a regular file on the receiving end, but it is on the - sending end. If its a directory then skip it (too dangerous to - do a recursive deletion??) otherwise try to unlink it */ - if (S_ISDIR(st.st_mode)) { - fprintf(FERROR,"ERROR: %s is a directory\n",fname); - return; - } - if (do_unlink(fname) != 0) { - fprintf(FERROR,"%s : not a regular file (generator)\n",fname); + if (delete_file(fname) != 0) { return; } -- 2.34.1