Restored write_ndx_and_attrs() and made it public.
[rsync/rsync.git] / generator.c
index 67baed2..dacb6c7 100644 (file)
@@ -93,17 +93,22 @@ extern int backup_suffix_len;
 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(). */
@@ -119,10 +124,10 @@ static int is_backup_file(char *fn)
 /* 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;
@@ -130,13 +135,15 @@ static enum delret delete_item(char *fname, int mode, char *replace, int flags)
 
        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. */
        }
@@ -146,27 +153,27 @@ static enum delret delete_item(char *fname, int mode, char *replace, int flags)
 
        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--;
@@ -177,35 +184,52 @@ static enum delret delete_item(char *fname, int mode, char *replace, int flags)
   check_ret:
        if (replace && ret != DR_SUCCESS) {
                rprintf(FERROR, "could not make way for new %s: %s\n",
-                       replace, fname);
+                       replace, fbuf);
        }
        return ret;
 }
 
-/* Prep 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;
        char *p;
 
+       if (verbose > 3) {
+               rprintf(FINFO, "delete_dir_contents(%s) flags=%d\n",
+                       fname, flags);
+       }
+
        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 != '/')
                *p++ = '/';
        remainder = MAXPATHLEN - (p - fname);
 
-       ret = DR_SUCCESS;
+       /* We do our own recursion, so make delete_item() non-recursive. */
+       flags = (flags & ~DEL_RECURSE) | DEL_DIR_IS_EMPTY;
 
        for (j = dirlist->count; j--; ) {
                struct file_struct *fp = dirlist->files[j];
@@ -216,24 +240,94 @@ static enum delret delete_dir_contents(char *fname, int flags)
                                    "mount point, %s, pins parent directory\n",
                                    f_name(fp, NULL));
                        }
-                       ret = DR_PINNED;
+                       ret = DR_NOT_EMPTY;
                        continue;
                }
 
                strlcpy(p, fp->basename, remainder);
-               result = delete_item(fname, fp->mode, NULL, flags);
-               if (result != DR_SUCCESS && ret == DR_SUCCESS)
-                       ret = result == DR_PINNED ? result : DR_NOT_EMPTY;
+               /* 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
@@ -301,13 +395,16 @@ static void delete_in_dir(struct file_list *flist, char *fbuf,
                        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);
                }
        }
 
@@ -364,7 +461,7 @@ int unchanged_attrs(struct file_struct *file, STRUCT_STAT *st)
 }
 
 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
@@ -495,8 +592,8 @@ static void sum_sizes_sqroot(struct sum_struct *sum, int64 len)
        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,
@@ -888,7 +985,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                           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;
@@ -953,7 +1050,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                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
@@ -972,7 +1069,8 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                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;
                }
 
@@ -1498,6 +1596,8 @@ void generate_files(int f_out, struct file_list *flist, char *local_name)
 
        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)
@@ -1610,6 +1710,8 @@ void generate_files(int f_out, struct file_list *flist, char *local_name)
        }
 
        do_progress = save_do_progress;
+       if (delete_delay_fp)
+               delayed_deletions(fbuf);
        if (delete_after && !local_name && flist->count > 0)
                do_delete_pass(flist);