extern int am_root;
extern int am_server;
extern int am_daemon;
+extern int am_sender;
extern int always_checksum;
extern int module_id;
extern int ignore_errors;
extern int filesfrom_fd;
extern int one_file_system;
+extern int keep_dirlinks;
extern int preserve_links;
extern int preserve_hard_links;
extern int preserve_perms;
extern int copy_unsafe_links;
extern int protocol_version;
extern int sanitize_paths;
-
-extern int read_batch;
-extern int write_batch;
+extern int delete_excluded;
+extern int orig_umask;
+extern int list_only;
extern struct exclude_list_struct exclude_list;
extern struct exclude_list_struct server_exclude_list;
static char empty_sum[MD4_SUM_LENGTH];
static unsigned int file_struct_len;
+static struct file_list *received_flist;
static void clean_flist(struct file_list *flist, int strip_root, int no_dups);
static void output_flist(struct file_list *flist);
static void start_filelist_progress(char *kind)
{
rprintf(FINFO, "%s ... ", kind);
- if ((verbose > 1) || do_progress)
+ if (verbose > 1 || do_progress)
rprintf(FINFO, "\n");
rflush(FINFO);
}
static void maybe_emit_filelist_progress(const struct file_list *flist)
{
- if (do_progress && show_filelist_p() && ((flist->count % 100) == 0))
+ if (do_progress && show_filelist_p() && (flist->count % 100) == 0)
emit_filelist_progress(flist);
}
{
char perms[11];
- if (!f->basename)
+ if (!f->basename) {
/* this can happen if duplicate names were removed */
return;
+ }
permstring(perms, f->mode);
f_name(f), f->u.link);
} else
#endif
+ {
rprintf(FINFO, "%s %11.0f %s %s\n",
perms,
(double)f->length, timestring(f->modtime),
f_name(f));
+ }
}
* @post @p buffer contains information about the link or the
* referrent as appropriate, if they exist.
**/
-int readlink_stat(const char *path, STRUCT_STAT *buffer, char *linkbuf)
+static int readlink_stat(const char *path, STRUCT_STAT *buffer, char *linkbuf)
{
#if SUPPORT_LINKS
if (copy_links)
return do_stat(path, buffer);
- if (do_lstat(path, buffer) == -1)
+ if (link_stat(path, buffer, 0) < 0)
return -1;
if (S_ISLNK(buffer->st_mode)) {
int l = readlink((char *)path, linkbuf, MAXPATHLEN - 1);
#endif
}
-int link_stat(const char *path, STRUCT_STAT *buffer)
+int link_stat(const char *path, STRUCT_STAT *buffer, int follow_dirlinks)
{
#if SUPPORT_LINKS
if (copy_links)
return do_stat(path, buffer);
- return do_lstat(path, buffer);
+ if (do_lstat(path, buffer) < 0)
+ return -1;
+ if (follow_dirlinks && S_ISLNK(buffer->st_mode)) {
+ STRUCT_STAT st;
+ if (do_stat(path, &st) == 0 && S_ISDIR(st.st_mode))
+ *buffer = st;
+ }
+ return 0;
#else
return do_stat(path, buffer);
#endif
static void set_filesystem(char *fname)
{
STRUCT_STAT st;
- if (link_stat(fname, &st) != 0)
+ if (do_stat(fname, &st) != 0)
return;
filesystem_dev = st.st_dev;
}
void receive_file_entry(struct file_struct **fptr, unsigned short flags,
- struct file_list *flist, int f)
+ struct file_list *flist, int f)
{
static time_t modtime;
static mode_t mode;
static uid_t uid;
static gid_t gid;
static char lastname[MAXPATHLEN], *lastdir;
- static int lastdir_len = -1;
+ static int lastdir_depth, lastdir_len = -1;
char thisname[MAXPATHLEN];
unsigned int l1 = 0, l2 = 0;
int alloc_len, basename_len, dirname_len, linkname_len, sum_len;
strlcpy(lastname, thisname, MAXPATHLEN);
- clean_fname(thisname);
+ clean_fname(thisname, 0);
if (sanitize_paths)
- sanitize_path(thisname, NULL);
+ sanitize_path(thisname, thisname, "", 0);
if ((basename = strrchr(thisname, '/')) != NULL) {
dirname_len = ++basename - thisname; /* counts future '\0' */
memcpy(bp, dirname, dirname_len - 1);
bp += dirname_len;
bp[-1] = '\0';
+ if (sanitize_paths)
+ lastdir_depth = count_dir_elements(lastdir);
} else if (dirname)
file->dirname = dirname;
file->u.link = bp;
read_sbuf(f, bp, linkname_len - 1);
if (sanitize_paths)
- sanitize_path(bp, lastdir);
+ sanitize_path(bp, bp, "", lastdir_depth);
bp += linkname_len;
}
#endif
}
if (!preserve_perms) {
- extern int orig_umask;
/* set an appropriate set of permissions based on original
* permissions and umask. This emulates what GNU cp does */
file->mode &= ~orig_umask;
* statting directories if we're not recursing, but this is not a very
* important case. Some systems may not have d_type.
**/
-struct file_struct *make_file(char *fname,
- struct file_list *flist, int exclude_level)
+struct file_struct *make_file(char *fname, struct file_list *flist,
+ int exclude_level)
{
static char *lastdir;
static int lastdir_len = -1;
rprintf(FINFO, "skipping overly long name: %s\n", fname);
return NULL;
}
- clean_fname(thisname);
+ clean_fname(thisname, 0);
if (sanitize_paths)
- sanitize_path(thisname, NULL);
+ sanitize_path(thisname, thisname, "", 0);
memset(sum, 0, SUM_LENGTH);
&& check_exclude_file(thisname, 0, exclude_level))
return NULL;
if (save_errno == ENOENT) {
+#if SUPPORT_LINKS
/* Avoid "vanished" error if symlink points nowhere. */
if (copy_links && do_lstat(thisname, &st) == 0
&& S_ISLNK(st.st_mode)) {
io_error |= IOERR_GENERAL;
rprintf(FERROR, "symlink has no referent: %s\n",
full_fname(thisname));
- } else {
+ } else
+#endif
+ {
enum logcode c = am_daemon && protocol_version < 28
? FERROR : FINFO;
io_error |= IOERR_VANISHED;
if (check_exclude_file(thisname, S_ISDIR(st.st_mode) != 0, exclude_level))
return NULL;
- if (lp_ignore_nonreadable(module_id) && access(thisname, R_OK) != 0)
- return NULL;
+ if (lp_ignore_nonreadable(module_id)) {
+#if SUPPORT_LINKS
+ if (!S_ISLNK(st.st_mode))
+#endif
+ if (access(thisname, R_OK) != 0)
+ return NULL;
+ }
skip_excludes:
file->basedir = flist_dir;
+ /* This code is only used by the receiver when it is building
+ * a list of files for a delete pass. */
+ if (keep_dirlinks && linkname_len && flist) {
+ STRUCT_STAT st2;
+ int i = flist_find(received_flist, file);
+ if (i >= 0 && S_ISDIR(received_flist->files[i]->mode)
+ && do_stat(thisname, &st2) == 0 && S_ISDIR(st2.st_mode)) {
+ file->modtime = st2.st_mtime;
+ file->length = st2.st_size;
+ file->mode = st2.st_mode;
+ file->uid = st2.st_uid;
+ file->gid = st2.st_gid;
+ file->u.link = NULL;
+ if (file->link_u.idev) {
+ pool_free(flist->hlink_pool, 0, file->link_u.idev);
+ file->link_u.idev = NULL;
+ }
+ }
+ }
+
if (!S_ISDIR(st.st_mode))
stats.total_size += st.st_size;
{
struct file_struct *file;
char fbuf[MAXPATHLEN];
- extern int delete_excluded;
/* f is set to -1 when calculating deletion file list */
file = make_file(fname, flist,
flist_expand(flist);
- if (write_batch)
- file->flags |= FLAG_TOP_DIR;
-
if (file->basename[0]) {
flist->files[flist->count++] = file;
send_file_entry(file, f, base_flags);
/**
+ * This function is normally called by the sender, but the receiver also
+ * uses it to construct its own file list if --delete has been specified.
* The delete_files() function in receiver.c sets f to -1 so that we just
* construct the file list in memory without sending it over the wire. It
* also has the side-effect of ignoring user-excludes if delete_excluded
"send_file_list");
if (f != -1) {
- io_start_buffering_out(f);
+ io_start_buffering_out();
if (filesfrom_fd >= 0) {
if (argv[0] && !push_dir(argv[0])) {
rsyserr(FERROR, errno, "push_dir %s failed",
if (use_ff_fd) {
if (read_filesfrom_line(filesfrom_fd, fname) == 0)
break;
- sanitize_path(fname, NULL);
+ sanitize_path(fname, fname, "", 0);
} else {
if (argc-- == 0)
break;
strlcpy(fname, *argv++, MAXPATHLEN);
if (sanitize_paths)
- sanitize_path(fname, NULL);
+ sanitize_path(fname, fname, "", 0);
}
l = strlen(fname);
}
}
- if (link_stat(fname, &st) != 0) {
+ if (link_stat(fname, &st, keep_dirlinks) != 0) {
if (f != -1) {
io_error |= IOERR_GENERAL;
rsyserr(FERROR, errno, "link_stat %s failed",
io_end_buffering();
stats.flist_size = stats.total_written - start_write;
stats.num_files = flist->count;
- if (write_batch)
- write_batch_flist_info(flist->count, flist->files);
}
if (verbose > 3)
struct file_list *flist;
unsigned short flags;
int64 start_read;
- extern int list_only;
if (show_filelist_p())
start_filelist_progress("receiving file list");
start_read = stats.total_read;
flist = flist_new(WITH_HLINK, "recv_file_list");
+ received_flist = flist;
flist->count = 0;
flist->malloced = 1000;
* protocol version 15 */
recv_uid_list(f, flist);
- if (!read_batch) {
- /* Recv the io_error flag */
- if (lp_ignore_errors(module_id) || ignore_errors)
- read_int(f);
- else
- io_error |= read_int(f);
- }
+ /* Recv the io_error flag */
+ if (lp_ignore_errors(module_id) || ignore_errors)
+ read_int(f);
+ else
+ io_error |= read_int(f);
}
if (verbose > 3)
for (i = 0; i < flist->count; i++) {
file = flist->files[i];
- if (am_root && preserve_uid)
+ if ((am_root || am_sender) && preserve_uid)
sprintf(uidbuf, " uid=%ld", (long)file->uid);
else
*uidbuf = '\0';