+ zap_dir = (flags & DEL_FORCE_RECURSE || (force_delete && recurse))
+ && !(flags & DEL_NO_RECURSE);
+ if (dry_run && zap_dir) {
+ ok = 0;
+ errno = ENOTEMPTY;
+ } else if (make_backups && !backup_dir && !is_backup_file(fname)
+ && !(flags & DEL_FORCE_RECURSE))
+ ok = make_backup(fname);
+ else
+ ok = do_rmdir(fname) == 0;
+ if (ok) {
+ if (!(flags & DEL_TERSE))
+ log_delete(fname, mode);
+ deletion_count++;
+ return 0;
+ }
+ if (errno == ENOENT)
+ return 0;
+ if (!zap_dir || (errno != ENOTEMPTY && errno != EEXIST)) {
+ rsyserr(FERROR, errno, "delete_file: rmdir %s failed",
+ full_fname(fname));
+ return -1;
+ }
+ flags |= DEL_FORCE_RECURSE; /* mark subdir dels as not "in the way" */
+
+ dlen = strlcpy(buf, fname, MAXPATHLEN);
+ save_filters = push_local_filters(buf, dlen);
+
+ dirlist = get_dirlist(buf, dlen, 0);
+ for (j = dirlist->count; j--; ) {
+ struct file_struct *fp = dirlist->files[j];
+
+ if (fp->flags & FLAG_MOUNT_POINT)
+ continue;
+
+ f_name_to(fp, buf);
+ if (delete_item(buf, fp->mode, flags & ~DEL_TERSE) != 0) {
+ flist_free(dirlist);
+ return -1;
+ }
+ }
+ flist_free(dirlist);
+
+ pop_local_filters(save_filters);
+
+ if (max_delete && deletion_count >= max_delete)
+ return -1;
+
+ if (do_rmdir(fname) == 0) {
+ if (!(flags & DEL_TERSE))
+ log_delete(fname, mode);
+ deletion_count++;
+ } else if (errno != ENOTEMPTY && errno != ENOENT) {
+ rsyserr(FERROR, errno, "delete_file: rmdir %s failed",
+ full_fname(fname));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/* This function is used to implement per-directory deletion, and is used by
+ * all the --delete-WHEN options. Note that the fbuf pointer must point to a
+ * MAXPATHLEN buffer with the name of the directory in it (the functions we
+ * call will append names onto the end, but the old dir value will be restored
+ * on exit). */
+static void delete_in_dir(struct file_list *flist, char *fbuf,
+ struct file_struct *file, int allowed_lull)
+{
+ static int min_depth = MAXPATHLEN, cur_depth = -1;
+ static void *filt_array[MAXPATHLEN/2+1];
+ struct file_list *dirlist;
+ char delbuf[MAXPATHLEN];
+ STRUCT_STAT st;
+ int dlen, i;
+
+ if (!flist) {
+ while (cur_depth >= min_depth)
+ pop_local_filters(filt_array[cur_depth--]);
+ min_depth = MAXPATHLEN;
+ cur_depth = -1;
+ return;
+ }
+
+ if (verbose > 2)
+ rprintf(FINFO, "delete_in_dir(%s)\n", safe_fname(fbuf));
+
+ if (allowed_lull)
+ maybe_send_keepalive(allowed_lull, flist->count);
+
+ if (file->dir.depth >= MAXPATHLEN/2+1)
+ return; /* Impossible... */
+
+ if (max_delete && deletion_count >= max_delete)
+ return;
+
+ if (io_error && !(lp_ignore_errors(module_id) || ignore_errors)) {
+ rprintf(FINFO,
+ "IO error encountered -- skipping file deletion\n");
+ max_delete = -1; /* avoid duplicating the above warning */
+ return;
+ }
+
+ while (cur_depth >= file->dir.depth && cur_depth >= min_depth)
+ pop_local_filters(filt_array[cur_depth--]);
+ cur_depth = file->dir.depth;
+ if (min_depth > cur_depth)
+ min_depth = cur_depth;
+ dlen = strlen(fbuf);
+ filt_array[cur_depth] = push_local_filters(fbuf, dlen);
+
+ if (link_stat(fbuf, &st, keep_dirlinks) < 0)
+ return;
+
+ if (one_file_system && file->flags & FLAG_TOP_DIR)
+ filesystem_dev = st.st_dev;
+
+ dirlist = get_dirlist(fbuf, dlen, 0);
+
+ /* If an item in dirlist is not found in flist, delete it
+ * from the filesystem. */
+ for (i = dirlist->count; i--; ) {
+ if (!dirlist->files[i]->basename)
+ continue;
+ if (flist_find(flist, dirlist->files[i]) < 0) {
+ int mode = dirlist->files[i]->mode;
+ f_name_to(dirlist->files[i], delbuf);
+ if (delete_item(delbuf, mode, DEL_FORCE_RECURSE) < 0)
+ break;
+ }
+ }
+
+ flist_free(dirlist);
+}
+
+/* This deletes any files on the receiving side that are not present on the
+ * sending side. This is used by --delete-before and --delete-after. */
+static void do_delete_pass(struct file_list *flist, int allowed_lull)
+{
+ char fbuf[MAXPATHLEN];
+ int j;
+
+ for (j = 0; j < flist->count; j++) {
+ struct file_struct *file = flist->files[j];
+
+ if (!(file->flags & FLAG_DEL_HERE))
+ continue;
+
+ f_name_to(file, fbuf);
+ if (verbose > 1 && file->flags & FLAG_TOP_DIR)
+ rprintf(FINFO, "deleting in %s\n", safe_fname(fbuf));
+
+ delete_in_dir(flist, fbuf, file, allowed_lull);
+ }
+}
+
+static int unchanged_attrs(struct file_struct *file, STRUCT_STAT *st)
+{
+ if (preserve_perms
+ && (st->st_mode & CHMOD_BITS) != (file->mode & CHMOD_BITS))
+ return 0;
+
+ if (am_root && preserve_uid && st->st_uid != file->uid)
+ return 0;
+
+ if (preserve_gid && file->gid != GID_NONE && st->st_gid != file->gid)
+ return 0;
+
+ return 1;
+}
+
+
+static void itemize(struct file_struct *file, int statret, STRUCT_STAT *st,
+ int32 iflags, int f_out, int ndx)
+{
+ if (statret >= 0) {
+ if (S_ISREG(file->mode) && file->length != st->st_size)
+ iflags |= ITEM_REPORT_SIZE;
+ if (!(iflags & ITEM_NO_DEST_AND_NO_UPDATE)) {
+ int keep_time = !preserve_times ? 0
+ : S_ISDIR(file->mode) ? !omit_dir_times
+ : !S_ISLNK(file->mode);
+
+ if ((iflags & ITEM_UPDATING && !keep_time)
+ || (keep_time && file->modtime != st->st_mtime))
+ iflags |= ITEM_REPORT_TIME;
+ if (preserve_perms && file->mode != st->st_mode)
+ iflags |= ITEM_REPORT_PERMS;
+ if (preserve_uid && am_root && file->uid != st->st_uid)
+ iflags |= ITEM_REPORT_OWNER;
+ if (preserve_gid && file->gid != GID_NONE
+ && st->st_gid != file->gid)
+ iflags |= ITEM_REPORT_GROUP;
+ }
+ } else
+ iflags |= ITEM_IS_NEW | ITEM_UPDATING;
+
+ iflags &= 0xffff;
+ if ((iflags || verbose > 1) && !read_batch) {
+ if (protocol_version >= 29) {
+ if (ndx >= 0)
+ write_int(f_out, ndx);
+ write_shortint(f_out, iflags);
+ } else if (ndx >= 0)
+ log_recv(file, &stats, iflags);
+ }
+}
+
+
+/* Perform our quick-check heuristic for determining if a file is unchanged. */
+static int unchanged_file(char *fn, struct file_struct *file, STRUCT_STAT *st)
+{
+ if (st->st_size != file->length)
+ return 0;
+