summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWayne Davison <wayned@samba.org>2008-03-16 18:06:47 -0700
committerWayne Davison <wayned@samba.org>2008-03-16 19:47:35 -0700
commitc9b62cf375f393de076d0fc7a3c2748d581c54a2 (patch)
treee645585c1ae9cdf9315fd86bc09618fb825c84ae
parent7bc595785ee6ac59fe514fd84ddc6d3a3c44e587 (diff)
downloadrsync-c9b62cf375f393de076d0fc7a3c2748d581c54a2.tar.gz
rsync-c9b62cf375f393de076d0fc7a3c2748d581c54a2.tar.bz2
rsync-c9b62cf375f393de076d0fc7a3c2748d581c54a2.zip
Fixed hard-linking when some of the files can get skipped. This adds
the FLAG_SKIP_HLINK flag, which gets set on any hard-linked file that the user wants to skip (e.g. via --ignore-existing, --append, etc.). The code in hlink.c now deals with the skipped files instead of triggering an assert() error.
-rw-r--r--generator.c55
-rw-r--r--hlink.c90
-rw-r--r--rsync.h3
3 files changed, 129 insertions, 19 deletions
diff --git a/generator.c b/generator.c
index 10500a99..125b3132 100644
--- a/generator.c
+++ b/generator.c
@@ -135,8 +135,12 @@ enum delret {
DR_SUCCESS = 0, DR_FAILURE, DR_AT_LIMIT, DR_NOT_EMPTY
};
-/* Forward declaration for delete_item(). */
+/* Forward declarations. */
static enum delret delete_dir_contents(char *fname, uint16 flags);
+#ifdef SUPPORT_HARD_LINKS
+static void handle_skipped_hlink(struct file_struct *file, int itemizing,
+ enum logcode code, int f_out);
+#endif
static int is_backup_file(char *fn)
{
@@ -1215,7 +1219,8 @@ static int dflt_perms;
* regular files that have changed, we try to find a basis file and then
* start sending checksums. The ndx is the file's unique index value.
*
- * When fname is non-null, it must point to a MAXPATHLEN buffer!
+ * The fname parameter must point to a MAXPATHLEN buffer! (e.g it gets
+ * passed to delete_item(), which can use it during a recursive delete.)
*
* Note that f_out is set to -1 when doing final directory-permission and
* modification-time repair. */
@@ -1269,6 +1274,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
excluded_dir = file;
}
skipping:
+#ifdef SUPPORT_HARD_LINKS
+ if (F_IS_HLINKED(file))
+ handle_skipped_hlink(file, itemizing, code, f_out);
+#endif
rprintf(FERROR_XFER,
"skipping daemon-excluded file \"%s\"\n",
fname);
@@ -1285,6 +1294,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
} else if (!dry_run) {
if (is_dir)
file->flags |= FLAG_MISSING_DIR;
+#ifdef SUPPORT_HARD_LINKS
+ if (F_IS_HLINKED(file))
+ handle_skipped_hlink(file, itemizing, code, f_out);
+#endif
return;
}
}
@@ -1347,6 +1360,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
}
file->flags |= FLAG_MISSING_DIR;
}
+#ifdef SUPPORT_HARD_LINKS
+ else if (F_IS_HLINKED(file))
+ handle_skipped_hlink(file, itemizing, code, f_out);
+#endif
if (verbose > 1) {
rprintf(FINFO, "not creating new %s \"%s\"\n",
is_dir ? "directory" : "file", fname);
@@ -1362,6 +1379,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
&& (!is_dir || !S_ISDIR(sx.st.st_mode))) {
if (verbose > 1 && is_dir >= 0)
rprintf(FINFO, "%s exists\n", fname);
+#ifdef SUPPORT_HARD_LINKS
+ if (F_IS_HLINKED(file))
+ handle_skipped_hlink(file, itemizing, code, f_out);
+#endif
goto cleanup;
}
@@ -1674,6 +1695,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
&& cmp_time(sx.st.st_mtime, file->modtime) > 0) {
if (verbose > 1)
rprintf(FINFO, "%s is newer\n", fname);
+#ifdef SUPPORT_HARD_LINKS
+ if (F_IS_HLINKED(file))
+ handle_skipped_hlink(file, itemizing, code, f_out);
+#endif
goto cleanup;
}
@@ -1768,6 +1793,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
}
if (append_mode > 0 && sx.st.st_size >= F_LENGTH(file)) {
+#ifdef SUPPORT_HARD_LINKS
+ if (F_IS_HLINKED(file))
+ handle_skipped_hlink(file, itemizing, code, f_out);
+#endif
goto cleanup;
}
@@ -1927,6 +1956,28 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
return;
}
+#ifdef SUPPORT_HARD_LINKS
+static void handle_skipped_hlink(struct file_struct *file, int itemizing,
+ enum logcode code, int f_out)
+{
+ char fbuf[MAXPATHLEN];
+ int new_last_ndx;
+ struct file_list *save_flist = cur_flist;
+
+ /* If we skip the last item in a chain of links and there was a
+ * prior non-skipped hard-link waiting to finish, finish it now. */
+ if ((new_last_ndx = skip_hard_link(file, &cur_flist)) < 0)
+ return;
+
+ file = cur_flist->files[new_last_ndx - cur_flist->ndx_start];
+ cur_flist->in_progress--; /* undo prior increment */
+ f_name(file, fbuf);
+ recv_generator(fbuf, file, new_last_ndx, itemizing, code, f_out);
+
+ cur_flist = save_flist;
+}
+#endif
+
static void touch_up_dirs(struct file_list *flist, int ndx)
{
static int counter = 0;
diff --git a/hlink.c b/hlink.c
index 2a8b77eb..536b571f 100644
--- a/hlink.c
+++ b/hlink.c
@@ -125,12 +125,16 @@ static void match_gnums(int32 *ndx_list, int ndx_count)
prev = -1;
} else if (CVAL(node->data, 0) == 0) {
struct file_list *flist;
- struct file_struct *fp;
prev = IVAL(node->data, 1);
flist = flist_for_ndx(prev);
- assert(flist != NULL);
- fp = flist->files[prev - flist->ndx_start];
- fp->flags &= ~FLAG_HLINK_LAST;
+ if (flist)
+ flist->files[prev - flist->ndx_start]->flags &= ~FLAG_HLINK_LAST;
+ else {
+ /* We skipped all prior files in this
+ * group, so mark this as a "first". */
+ file->flags |= FLAG_HLINK_FIRST;
+ prev = -1;
+ }
} else
prev = -1;
} else {
@@ -240,20 +244,45 @@ static int maybe_hard_link(struct file_struct *file, int ndx,
}
/* Figure out if a prior entry is still there or if we just have a
- * cached name for it. Never called with a FLAG_HLINK_FIRST entry. */
-static char *check_prior(int prev_ndx, int gnum, struct file_list **flist_p)
+ * cached name for it. */
+static char *check_prior(struct file_struct *file, int gnum,
+ int *prev_ndx_p, struct file_list **flist_p)
{
- struct file_list *flist = flist_for_ndx(prev_ndx);
+ struct file_struct *fp;
+ struct file_list *flist;
struct ht_int32_node *node;
+ int prev_ndx = F_HL_PREV(file);
- if (flist) {
- *flist_p = flist;
- return NULL;
+ while (1) {
+ if (prev_ndx < 0) {
+ *prev_ndx_p = prev_ndx;
+ *flist_p = NULL;
+ return NULL;
+ }
+ if ((flist = flist_for_ndx(prev_ndx)) == NULL)
+ break;
+ fp = flist->files[prev_ndx - flist->ndx_start];
+ if (!(fp->flags & FLAG_SKIP_HLINK)) {
+ *prev_ndx_p = prev_ndx;
+ *flist_p = flist;
+ return NULL;
+ }
+ F_HL_PREV(file) = prev_ndx = F_HL_PREV(fp);
}
node = hashtable_find(prior_hlinks, gnum, 0);
assert(node != NULL && node->data);
- assert(CVAL(node->data, 0) != 0);
+
+ if (CVAL(node->data, 0) == 0) {
+ /* The prior file must have been skipped. */
+ F_HL_PREV(file) = prev_ndx = -1;
+ *prev_ndx_p = prev_ndx;
+ *flist_p = NULL;
+ return NULL;
+ }
+
+ *prev_ndx_p = prev_ndx;
+ *flist_p = flist;
return node->data;
}
@@ -268,12 +297,20 @@ int hard_link_check(struct file_struct *file, int ndx, const char *fname,
char *realname, *prev_name;
struct file_list *flist;
int gnum = inc_recurse ? F_HL_GNUM(file) : -1;
- int prev_ndx = F_HL_PREV(file);
+ int prev_ndx;
- prev_name = realname = check_prior(prev_ndx, gnum, &flist);
+ prev_name = realname = check_prior(file, gnum, &prev_ndx, &flist);
if (!prev_name) {
- struct file_struct *prev_file = flist->files[prev_ndx - flist->ndx_start];
+ struct file_struct *prev_file;
+
+ if (!flist) {
+ /* The previous file was skipped, so this one is
+ * treated as if it were the first in its group. */
+ return 0;
+ }
+
+ prev_file = flist->files[prev_ndx - flist->ndx_start];
/* Is the previous link not complete yet? */
if (!(prev_file->flags & FLAG_HLINK_DONE)) {
@@ -294,8 +331,8 @@ int hard_link_check(struct file_struct *file, int ndx, const char *fname,
/* There is a finished file to link with! */
if (!(prev_file->flags & FLAG_HLINK_FIRST)) {
/* The previous previous is FIRST when prev is not. */
- prev_ndx = F_HL_PREV(prev_file);
- prev_name = realname = check_prior(prev_ndx, gnum, &flist);
+ prev_name = realname = check_prior(prev_file, gnum, &prev_ndx, &flist);
+ assert(prev_name != NULL || flist != NULL);
/* Update our previous pointer to point to the FIRST. */
F_HL_PREV(file) = prev_ndx;
}
@@ -474,4 +511,25 @@ void finish_hard_link(struct file_struct *file, const char *fname, int fin_ndx,
out_of_memory("finish_hard_link");
}
}
+
+int skip_hard_link(struct file_struct *file, struct file_list **flist_p)
+{
+ struct file_list *flist;
+ int prev_ndx;
+
+ file->flags |= FLAG_SKIP_HLINK;
+ if (!(file->flags & FLAG_HLINK_LAST))
+ return -1;
+
+ check_prior(file, F_HL_GNUM(file), &prev_ndx, &flist);
+ if (prev_ndx >= 0) {
+ file = flist->files[prev_ndx - flist->ndx_start];
+ if (file->flags & (FLAG_HLINK_DONE|FLAG_FILE_SENT))
+ return -1;
+ file->flags |= FLAG_HLINK_LAST;
+ *flist_p = flist;
+ }
+
+ return prev_ndx;
+}
#endif
diff --git a/rsync.h b/rsync.h
index c1225563..c6293d0f 100644
--- a/rsync.h
+++ b/rsync.h
@@ -67,7 +67,8 @@
#define FLAG_FILE_SENT (1<<1) /* sender/receiver/generator */
#define FLAG_DIR_CREATED (1<<1) /* generator */
#define FLAG_CONTENT_DIR (1<<2) /* sender/receiver/generator */
-#define FLAG_MOUNT_DIR (1<<3) /* sender/generator */
+#define FLAG_MOUNT_DIR (1<<3) /* sender/generator (dirs only) */
+#define FLAG_SKIP_HLINK (1<<3) /* receiver/generator (w/FLAG_HLINKED) */
#define FLAG_DUPLICATE (1<<4) /* sender */
#define FLAG_MISSING_DIR (1<<4) /* generator */
#define FLAG_HLINKED (1<<5) /* receiver/generator (checked on all types) */