extern struct file_list *the_file_list;
extern struct filter_list_struct server_filter_list;
+int ignore_perishable = 0;
+int non_perishable_cnt = 0;
+
static int deletion_count = 0; /* used to implement --max-delete */
+static FILE *delete_delay_fp = NULL;
-/* For calling delete_item() */
+/* For calling delete_item() and delete_dir_contents(). */
#define DEL_RECURSE (1<<1) /* recurse */
+#define DEL_DIR_IS_EMPTY (1<<2) /* internal delete_FUNCTIONS use only */
enum nonregtype {
TYPE_DIR, TYPE_SPECIAL, TYPE_DEVICE, TYPE_SYMLINK
};
enum delret {
- DR_SUCCESS = 0, DR_FAILURE, DR_AT_LIMIT, DR_PINNED, DR_NOT_EMPTY
+ DR_SUCCESS = 0, DR_FAILURE, DR_AT_LIMIT, DR_NOT_EMPTY
};
/* Forward declaration for delete_item(). */
/* Delete a file or directory. If DEL_RECURSE is set in the flags, this will
* delete recursively.
*
- * Note that fname must point to a MAXPATHLEN buffer if the mode indicates it's
+ * Note that fbuf must point to a MAXPATHLEN buffer if the mode indicates it's
* a directory! (The buffer is used for recursion, but returned unchanged.)
*/
-static enum delret delete_item(char *fname, int mode, char *replace, int flags)
+static enum delret delete_item(char *fbuf, int mode, char *replace, int flags)
{
enum delret ret;
char *what;
if (verbose > 2) {
rprintf(FINFO, "delete_item(%s) mode=%o flags=%d\n",
- fname, mode, flags);
+ fbuf, mode, flags);
}
- if (S_ISDIR(mode) && flags & DEL_RECURSE) {
- ret = delete_dir_contents(fname, flags);
- if (ret == DR_PINNED || ret == DR_NOT_EMPTY
- || ret == DR_AT_LIMIT)
+ if (S_ISDIR(mode) && !(flags & DEL_DIR_IS_EMPTY)) {
+ ignore_perishable = 1;
+ /* If DEL_RECURSE is not set, this just reports emptiness. */
+ ret = delete_dir_contents(fbuf, flags);
+ ignore_perishable = 0;
+ if (ret == DR_NOT_EMPTY || ret == DR_AT_LIMIT)
goto check_ret;
/* OK: try to delete the directory. */
}
if (S_ISDIR(mode)) {
what = "rmdir";
- ok = do_rmdir(fname) == 0;
- } else if (make_backups && (backup_dir || !is_backup_file(fname))) {
+ ok = do_rmdir(fbuf) == 0;
+ } else if (make_backups && (backup_dir || !is_backup_file(fbuf))) {
what = "make_backup";
- ok = make_backup(fname);
+ ok = make_backup(fbuf);
} else {
what = "unlink";
- ok = robust_unlink(fname) == 0;
+ ok = robust_unlink(fbuf) == 0;
}
if (ok) {
if (!replace)
- log_delete(fname, mode);
+ log_delete(fbuf, mode);
ret = DR_SUCCESS;
} else {
if (S_ISDIR(mode) && errno == ENOTEMPTY) {
- rprintf(FINFO, "non-empty directory, %s, not deleted\n",
- fname);
+ rprintf(FINFO, "cannot delete non-empty directory: %s\n",
+ fbuf);
ret = DR_NOT_EMPTY;
} else if (errno != ENOENT) {
rsyserr(FERROR, errno, "delete_file: %s(%s) failed",
- what, full_fname(fname));
+ what, fbuf);
ret = DR_FAILURE;
} else {
deletion_count--;
check_ret:
if (replace && ret != DR_SUCCESS) {
rprintf(FERROR, "could not make way for new %s: %s\n",
- replace, fname);
+ replace, fbuf);
}
return ret;
}
-/* The directory is to be deleted, so delete all its contents. Note
- * that fname must point to a MAXPATHLEN buffer! (The buffer is used
- * for recursion, but returned unchanged.)
+/* The directory is about to be deleted: if DEL_RECURSE is given, delete all
+ * its contents, otherwise just checks for content. Returns DR_SUCCESS or
+ * DR_NOT_EMPTY. Note that fname must point to a MAXPATHLEN buffer! (The
+ * buffer is used for recursion, but returned unchanged.)
*/
static enum delret delete_dir_contents(char *fname, int flags)
{
struct file_list *dirlist;
- enum delret ret, result;
+ enum delret ret;
unsigned remainder;
void *save_filters;
int j, dlen;
dlen = strlen(fname);
save_filters = push_local_filters(fname, dlen);
+ non_perishable_cnt = 0;
dirlist = get_dirlist(fname, dlen, 0);
+ ret = non_perishable_cnt ? DR_NOT_EMPTY : DR_SUCCESS;
+
+ if (!dirlist->count)
+ goto done;
+
+ if (!(flags & DEL_RECURSE)) {
+ ret = DR_NOT_EMPTY;
+ goto done;
+ }
p = fname + dlen;
if (dlen != 1 || *fname != '/')
remainder = MAXPATHLEN - (p - fname);
/* We do our own recursion, so make delete_item() non-recursive. */
- flags &= ~DEL_RECURSE;
- ret = DR_SUCCESS;
+ flags = (flags & ~DEL_RECURSE) | DEL_DIR_IS_EMPTY;
for (j = dirlist->count; j--; ) {
struct file_struct *fp = dirlist->files[j];
"mount point, %s, pins parent directory\n",
f_name(fp, NULL));
}
- ret = DR_PINNED;
+ ret = DR_NOT_EMPTY;
continue;
}
strlcpy(p, fp->basename, remainder);
- if (S_ISDIR(fp->mode)) {
- /* Save stack by recursing to ourself directly. */
- result = delete_dir_contents(fname, flags);
- if (result == DR_PINNED)
- ret = result;
- else if (result != DR_SUCCESS && ret == DR_SUCCESS)
- ret = DR_NOT_EMPTY;
- }
- result = delete_item(fname, fp->mode, NULL, flags);
- if (result == DR_PINNED)
- ret = result;
- else if (result != DR_SUCCESS && ret == DR_SUCCESS)
+ /* Save stack by recursing to ourself directly. */
+ if (S_ISDIR(fp->mode)
+ && delete_dir_contents(fname, flags | DEL_RECURSE) != DR_SUCCESS)
+ ret = DR_NOT_EMPTY;
+ if (delete_item(fname, fp->mode, NULL, flags) != DR_SUCCESS)
ret = DR_NOT_EMPTY;
}
fname[dlen] = '\0';
- pop_local_filters(save_filters);
+ done:
flist_free(dirlist);
+ pop_local_filters(save_filters);
+ if (ret == DR_NOT_EMPTY) {
+ rprintf(FINFO, "cannot delete non-empty directory: %s\n",
+ fname);
+ }
return ret;
}
+static void start_delete_temp(void)
+{
+ char fnametmp[MAXPATHLEN];
+ int fd, save_dry_run = dry_run;
+
+ dry_run = 0;
+ if (!get_tmpname(fnametmp, "deldelay")
+ || (fd = do_mkstemp(fnametmp, 0600)) < 0
+ || !(delete_delay_fp = fdopen(fd, "w+"))) {
+ rprintf(FERROR, "Unable to create delete-delay temp file.\n");
+ exit_cleanup(RERR_FILEIO);
+ }
+ dry_run = save_dry_run;
+ unlink(fnametmp);
+}
+
+static int read_delay_line(FILE *fp, char *buf, int bsize)
+{
+ int ch, mode = 0;
+
+ if ((ch = fgetc(fp)) == EOF)
+ return -1;
+
+ while (1) {
+ if (ch == ' ')
+ break;
+ if (ch > '7' || ch < '0') {
+ rprintf(FERROR, "invalid data in delete-delay file.\n");
+ exit_cleanup(RERR_FILEIO);
+ }
+ mode = mode*8 + ch - '0';
+ if ((ch = fgetc(fp)) == EOF) {
+ unexpected_eof:
+ rprintf(FERROR, "unexpected EOF in delete-delay file.\n");
+ exit_cleanup(RERR_FILEIO);
+ }
+ }
+
+ while (1) {
+ if ((ch = fgetc(fp)) == EOF)
+ goto unexpected_eof;
+ if (bsize-- <= 0) {
+ rprintf(FERROR, "filename too long in delete-delay file.\n");
+ exit_cleanup(RERR_FILEIO);
+ }
+ *buf++ = (char)ch;
+ if (ch == '\0')
+ break;
+ }
+
+ return mode;
+}
+
+static void delayed_deletions(char *delbuf)
+{
+ int mode;
+
+ fseek(delete_delay_fp, 0, 0);
+ while ((mode = read_delay_line(delete_delay_fp, delbuf, MAXPATHLEN)) >= 0)
+ delete_item(delbuf, mode, NULL, DEL_RECURSE);
+ fclose(delete_delay_fp);
+}
/* 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
continue;
if (fp->flags & FLAG_MOUNT_POINT) {
if (verbose > 1)
- rprintf(FINFO, "mount point %s not deleted\n",
+ rprintf(FINFO, "cannot delete mount point: %s\n",
f_name(fp, NULL));
continue;
}
if (flist_find(flist, fp) < 0) {
f_name(fp, delbuf);
- delete_item(delbuf, fp->mode, NULL, DEL_RECURSE);
+ if (delete_delay_fp)
+ fprintf(delete_delay_fp, "%o %s%c", (short)fp->mode, delbuf, '\0');
+ else
+ delete_item(delbuf, fp->mode, NULL, DEL_RECURSE);
}
}
}
void itemize(struct file_struct *file, int ndx, int statret, STRUCT_STAT *st,
- int32 iflags, uchar fnamecmp_type, char *xname)
+ int32 iflags, uchar fnamecmp_type, const char *xname)
{
if (statret >= 0) { /* A from-dest-dir statret can == 1! */
int keep_time = !preserve_times ? 0
sum->flength = len;
sum->blength = blength;
sum->s2length = s2length;
- sum->remainder = len % blength;
- sum->count = len / blength + (sum->remainder != 0);
+ sum->remainder = (int32)(len % blength);
+ sum->count = (int32)(len / blength) + (sum->remainder != 0);
if (sum->count && verbose > 2) {
rprintf(FINFO,
enum logcode code, int f_out)
{
static int missing_below = -1, excluded_below = -1;
- static char *parent_dirname = "";
+ static const char *parent_dirname = "";
static struct file_list *fuzzy_dirlist = NULL;
static int need_fuzzy_dirlist = 0;
struct file_struct *fuzzy_file = NULL;
statret = -1;
stat_errno = ENOENT;
} else {
- char *dn = file->dirname ? file->dirname : ".";
+ const char *dn = file->dirname ? file->dirname : ".";
if (parent_dirname != dn && strcmp(parent_dirname, dn) != 0) {
if (relative_paths && !implied_dirs
&& do_stat(dn, &st) < 0
parent_dirname = dn;
if (need_fuzzy_dirlist && S_ISREG(file->mode)) {
- fuzzy_dirlist = get_dirlist(dn, -1, 1);
+ strlcpy(fnamecmpbuf, dn, sizeof fnamecmpbuf);
+ fuzzy_dirlist = get_dirlist(fnamecmpbuf, -1, 1);
need_fuzzy_dirlist = 0;
}
if (delete_before && !local_name && flist->count > 0)
do_delete_pass(flist);
+ if (delete_during == 2)
+ start_delete_temp();
do_progress = 0;
if (append_mode || whole_file < 0)
}
do_progress = save_do_progress;
+ if (delete_delay_fp)
+ delayed_deletions(fbuf);
if (delete_after && !local_name && flist->count > 0)
do_delete_pass(flist);