* Copyright (C) 1996-2000 Andrew Tridgell
* Copyright (C) 1996 Paul Mackerras
* Copyright (C) 2002 Martin Pool <mbp@samba.org>
- * Copyright (C) 2003-2008 Wayne Davison
+ * Copyright (C) 2003-2009 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
extern int preserve_executability;
extern int preserve_perms;
extern int preserve_times;
-extern int uid_ndx;
-extern int gid_ndx;
extern int delete_mode;
extern int delete_before;
extern int delete_during;
extern int always_checksum;
extern int checksum_len;
extern char *partial_dir;
-extern char *basis_dir[];
+extern char *basis_dir[MAX_BASIS_DIRS+1];
extern int compare_dest;
extern int copy_dest;
extern int link_dest;
static int deldelay_size = 0, deldelay_cnt = 0;
static char *deldelay_buf = NULL;
static int deldelay_fd = -1;
-static int lull_mod;
+static int loopchk_limit;
static int dir_tweaking;
static int symlink_timeset_failed_flags;
static int need_retouch_dir_times;
do_chmod(fbuf, mode | S_IWUSR);
if (S_ISDIR(mode) && !(flags & DEL_DIR_IS_EMPTY)) {
- int save_uid_ndx = uid_ndx;
/* This only happens on the first call to delete_item() since
* delete_dir_contents() always calls us w/DEL_DIR_IS_EMPTY. */
- if (!uid_ndx)
- uid_ndx = ++file_extra_cnt;
ignore_perishable = 1;
/* If DEL_RECURSE is not set, this just reports emptiness. */
ret = delete_dir_contents(fbuf, flags);
ignore_perishable = 0;
- if (!save_uid_ndx) {
- --file_extra_cnt;
- uid_ndx = 0;
- }
if (ret == DR_NOT_EMPTY || ret == DR_AT_LIMIT)
goto check_ret;
/* OK: try to delete the directory. */
}
strlcpy(p, fp->basename, remainder);
- if (!(fp->mode & S_IWUSR) && !am_root && (uid_t)F_OWNER(fp) == our_uid)
+ if (!(fp->mode & S_IWUSR) && !am_root && fp->flags & FLAG_OWNED_BY_US)
do_chmod(fname, fp->mode | S_IWUSR);
/* Save stack by recursing to ourself directly. */
if (S_ISDIR(fp->mode)) {
struct file_list *dirlist;
char delbuf[MAXPATHLEN];
int dlen, i;
- int save_uid_ndx = uid_ndx;
if (!fbuf) {
change_local_filter_dir(NULL, 0, 0);
return;
}
- if (!uid_ndx)
- uid_ndx = ++file_extra_cnt;
-
dirlist = get_dirlist(fbuf, dlen, 0);
/* If an item in dirlist is not found in flist, delete it
f_name(fp, NULL));
continue;
}
- if (flist_find(cur_flist, fp) < 0) {
+ /* Here we want to match regardless of file type. Replacement
+ * of a file with one of another type is handled separately by
+ * a delete_item call with a DEL_MAKE_ROOM flag. */
+ if (flist_find_ignore_dirness(cur_flist, fp) < 0) {
int flags = DEL_RECURSE;
- if (!(fp->mode & S_IWUSR) && !am_root && (uid_t)F_OWNER(fp) == our_uid)
+ if (!(fp->mode & S_IWUSR) && !am_root && fp->flags & FLAG_OWNED_BY_US)
flags |= DEL_NO_UID_WRITE;
f_name(fp, delbuf);
if (delete_during == 2) {
}
flist_free(dirlist);
-
- if (!save_uid_ndx) {
- --file_extra_cnt;
- uid_ndx = 0;
- }
}
/* This deletes any files on the receiving side that are not present on the
if (iflags & ITEM_XNAME_FOLLOWS)
write_vstring(sock_f_out, xname, strlen(xname));
#ifdef SUPPORT_XATTRS
- if (preserve_xattrs && !dry_run
+ if (preserve_xattrs && do_xfers
&& iflags & (ITEM_REPORT_XATTR|ITEM_TRANSFER)) {
send_xattr_request(NULL, file,
iflags & ITEM_REPORT_XATTR ? sock_f_out : -1);
int s2length;
int64 l;
+ if (len < 0) {
+ /* The file length overflowed our int64 var, so we can't process this file. */
+ sum->count = -1; /* indicate overflow error */
+ return;
+ }
+
if (block_size)
blength = block_size;
else if (len <= BLOCK_SIZE * BLOCK_SIZE)
blength = BLOCK_SIZE;
else {
+ int32 max_blength = protocol_version < 30 ? OLD_MAX_BLOCK_SIZE : MAX_BLOCK_SIZE;
int32 c;
int cnt;
for (c = 1, l = len, cnt = 0; l >>= 2; c <<= 1, cnt++) {}
- if (cnt >= 31 || c >= MAX_BLOCK_SIZE)
- blength = MAX_BLOCK_SIZE;
+ if (c < 0 || c >= max_blength)
+ blength = max_blength;
else {
blength = 0;
do {
}
switch (type) {
case TYPE_DIR:
- break;
case TYPE_SPECIAL:
+ break;
case TYPE_DEVICE:
devp = F_RDEV_P(file);
if (sxp->st.st_rdev != MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp)))
skip_dir = NULL;
}
+#ifdef SUPPORT_ACLS
+ sx.acc_acl = sx.def_acl = NULL;
+#endif
+#ifdef SUPPORT_XATTRS
+ sx.xattr = NULL;
+#endif
if (daemon_filter_list.head && (*fname != '.' || fname[1])) {
if (check_filter(&daemon_filter_list, FLOG, fname, is_dir) < 0) {
if (is_dir < 0)
}
}
-#ifdef SUPPORT_ACLS
- sx.acc_acl = sx.def_acl = NULL;
-#endif
-#ifdef SUPPORT_XATTRS
- sx.xattr = NULL;
-#endif
if (dry_run > 1 || (dry_missing_dir && is_below(file, dry_missing_dir))) {
parent_is_dry_missing:
if (fuzzy_dirlist) {
if (need_fuzzy_dirlist && S_ISREG(file->mode)) {
strlcpy(fnamecmpbuf, dn, sizeof fnamecmpbuf);
- fuzzy_dirlist = get_dirlist(fnamecmpbuf, -1, 1);
+ fuzzy_dirlist = get_dirlist(fnamecmpbuf, -1, GDL_IGNORE_FILTER_RULES);
need_fuzzy_dirlist = 0;
}
goto cleanup;
}
+ fnamecmp = fname;
+
if (is_dir) {
+ mode_t added_perms;
if (!implied_dirs && file->flags & FLAG_IMPLIED_DIR)
goto cleanup;
+ if (am_root < 0) {
+ /* For --fake-super, the dir must be useable by the copying
+ * user, just like it would be for root. */
+ added_perms = S_IRUSR|S_IWUSR|S_IXUSR;
+ } else
+ added_perms = 0;
if (is_dir < 0) {
/* In inc_recurse mode we want to make sure any missing
* directories get created while we're still processing
&& (S_ISDIR(sx.st.st_mode)
|| delete_item(fname, sx.st.st_mode, del_opts | DEL_FOR_DIR) != 0))
goto cleanup; /* Any errors get reported later. */
- if (do_mkdir(fname, file->mode & 0700) == 0)
+ if (do_mkdir(fname, (file->mode|added_perms) & 0700) == 0)
file->flags |= FLAG_DIR_CREATED;
goto cleanup;
}
if (j == -2) {
itemizing = 0;
code = FNONE;
- } else if (j >= 0)
statret = 1;
+ } else if (j >= 0) {
+ statret = 1;
+ fnamecmp = fnamecmpbuf;
+ }
}
if (itemizing && f_out != -1) {
- itemize(fname, file, ndx, statret, &sx,
+ itemize(fnamecmp, file, ndx, statret, &sx,
statret ? ITEM_LOCAL_CHANGE : 0, 0, NULL);
}
- if (real_ret != 0 && do_mkdir(fname,file->mode) < 0 && errno != EEXIST) {
+ if (real_ret != 0 && do_mkdir(fname,file->mode|added_perms) < 0 && errno != EEXIST) {
if (!relative_paths || errno != ENOENT
- || create_directory_path(fname) < 0
- || (do_mkdir(fname, file->mode) < 0 && errno != EEXIST)) {
+ || create_directory_path(fname) < 0
+ || (do_mkdir(fname, file->mode|added_perms) < 0 && errno != EEXIST)) {
rsyserr(FERROR_XFER, errno,
"recv_generator: mkdir %s failed",
full_fname(fname));
goto cleanup;
}
}
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs && statret == 1)
+ copy_xattrs(fnamecmpbuf, fname);
+#endif
if (set_file_attrs(fname, file, real_ret ? NULL : &real_sx, NULL, 0)
&& verbose && code != FNONE && f_out != -1)
rprintf(code, "%s/\n", fname);
const char *sl = F_SYMLINK(file);
if (safe_symlinks && unsafe_symlink(sl, fname)) {
if (verbose) {
- if (solo_file)
+ if (solo_file) {
+ /* fname contains the destination path, but we
+ * want to report the source path. */
fname = f_name(file, NULL);
+ }
rprintf(FINFO,
- "ignoring unsafe symlink %s -> \"%s\"\n",
- full_fname(fname), sl);
+ "ignoring unsafe symlink \"%s\" -> \"%s\"\n",
+ fname, sl);
}
return;
}
if ((am_root && preserve_devices && IS_DEVICE(file->mode))
|| (preserve_specials && IS_SPECIAL(file->mode))) {
- uint32 *devp = F_RDEV_P(file);
- dev_t rdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp));
+ dev_t rdev;
+ if (IS_DEVICE(file->mode)) {
+ uint32 *devp = F_RDEV_P(file);
+ rdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp));
+ } else
+ rdev = 0;
if (statret == 0) {
int del_for_flag;
if (IS_DEVICE(file->mode)) {
}
if (statret == 0
&& BITS_EQUAL(sx.st.st_mode, file->mode, _S_IFMT)
- && sx.st.st_rdev == rdev) {
+ && (IS_SPECIAL(sx.st.st_mode) || sx.st.st_rdev == rdev)) {
/* The device or special file is identical. */
set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT);
if (itemizing)
goto cleanup;
}
- fnamecmp = fname;
fnamecmp_type = FNAMECMP_FNAME;
if (statret == 0 && !S_ISREG(sx.st.st_mode)) {
if (read_batch)
goto cleanup;
- if (statret != 0 || whole_file || sx.st.st_size <= 0)
+ if (statret != 0 || whole_file)
write_sum_head(f_out, NULL);
- else {
+ else if (sx.st.st_size <= 0) {
+ write_sum_head(f_out, NULL);
+ close(fd);
+ } else {
if (generate_and_send_sums(fd, sx.st.st_size, f_out, f_copy) < 0) {
rprintf(FWARNING,
"WARNING: file is too large for checksum sending: %s\n",
static int counter = 0;
struct file_struct *file;
char *fname;
+ BOOL fix_dir_perms;
int i, start, end;
if (ndx < 0) {
rprintf(FINFO, "touch_up_dirs: %s (%d)\n",
NS(fname), i);
}
+ /* Be sure not to retouch permissions with --fake-super. */
+ fix_dir_perms = !am_root && !(file->mode & S_IWUSR);
if (!F_IS_ACTIVE(file) || file->flags & FLAG_MISSING_DIR
- || (!need_retouch_dir_times && file->mode & S_IWUSR))
+ || !(need_retouch_dir_times || fix_dir_perms))
continue;
fname = f_name(file, NULL);
- if (!(file->mode & S_IWUSR))
+ if (fix_dir_perms)
do_chmod(fname, file->mode);
if (need_retouch_dir_times) {
STRUCT_STAT st;
&& cmp_time(st.st_mtime, file->modtime) != 0)
set_modtime(fname, file->modtime, file->mode);
}
- if (allowed_lull && !(counter % lull_mod))
- maybe_send_keepalive();
- else if (!(counter & 0xFF))
- maybe_flush_socket(0);
+ if (counter >= loopchk_limit) {
+ if (allowed_lull)
+ maybe_send_keepalive();
+ else
+ maybe_flush_socket(0);
+ counter = 0;
+ }
}
}
while (1) {
#ifdef SUPPORT_HARD_LINKS
if (preserve_hard_links && (ndx = get_hlink_num()) != -1) {
- flist = flist_for_ndx(ndx);
- assert(flist != NULL);
+ flist = flist_for_ndx(ndx, "check_for_finished_files.1");
file = flist->files[ndx - flist->ndx_start];
assert(file->flags & FLAG_HLINKED);
finish_hard_link(file, f_name(file, fbuf), ndx, NULL, itemizing, code, -1);
ignore_times++;
flist = cur_flist;
- cur_flist = flist_for_ndx(ndx);
+ cur_flist = flist_for_ndx(ndx, "check_for_finished_files.2");
file = cur_flist->files[ndx - cur_flist->ndx_start];
if (solo_file)
if (first_flist->in_progress || first_flist->to_redo)
break;
- if (!read_batch) {
- write_ndx(sock_f_out, NDX_DONE);
+ write_ndx(sock_f_out, NDX_DONE);
+ if (!read_batch)
maybe_flush_socket(1);
- }
if (delete_during == 2 || !dir_tweaking) {
/* Skip directory touch-up. */
void generate_files(int f_out, const char *local_name)
{
- int i, ndx;
+ int i, ndx, next_loopchk = 0;
char fbuf[MAXPATHLEN];
int itemizing;
enum logcode code;
solo_file = local_name;
dir_tweaking = !(list_only || solo_file || dry_run);
need_retouch_dir_times = preserve_times > 1;
- lull_mod = allowed_lull * 5;
+ loopchk_limit = allowed_lull ? allowed_lull * 5 : 200;
symlink_timeset_failed_flags = ITEM_REPORT_TIME
| (protocol_version >= 30 || !am_server ? ITEM_REPORT_TIMEFAIL : 0);
implied_dirs_are_missing = relative_paths && !implied_dirs && protocol_version < 30;
/* Since we often fill up the outgoing socket and then just sit around
* waiting for the other 2 processes to do their thing, we don't want
* to exit on a timeout. If the data stops flowing, the receiver will
- * notice that and let us know via the redo pipe (or its closing). */
+ * notice that and let us know via the message pipe (or its closing). */
ignore_timeout = 1;
dflt_perms = (ACCESSPERMS & ~orig_umask);
if (inc_recurse && cur_flist->parent_ndx >= 0) {
struct file_struct *fp = dir_flist->files[cur_flist->parent_ndx];
- f_name(fp, fbuf);
+ if (solo_file)
+ strlcpy(fbuf, solo_file, sizeof fbuf);
+ else
+ f_name(fp, fbuf);
ndx = cur_flist->ndx_start - 1;
recv_generator(fbuf, fp, ndx, itemizing, code, f_out);
if (delete_during && dry_run < 2 && !list_only
check_for_finished_files(itemizing, code, 0);
- if (allowed_lull && !(i % lull_mod))
- maybe_send_keepalive();
- else if (!(i & 0xFF))
- maybe_flush_socket(0);
+ if (i + cur_flist->ndx_start >= next_loopchk) {
+ if (allowed_lull)
+ maybe_send_keepalive();
+ else
+ maybe_flush_socket(0);
+ next_loopchk += loopchk_limit;
+ }
}
if (!inc_recurse) {
rprintf(FINFO, "generate_files phase=%d\n", phase);
write_ndx(f_out, NDX_DONE);
+
/* Reduce round-trip lag-time for a useless delay-updates phase. */
if (protocol_version >= 29 && !delay_updates)
write_ndx(f_out, NDX_DONE);
touch_up_dirs(dir_flist, -1);
if (max_delete >= 0 && deletion_count > max_delete) {
- rprintf(FINFO,
+ rprintf(FWARNING,
"Deletions stopped due to --max-delete limit (%d skipped)\n",
deletion_count - max_delete);
io_error |= IOERR_DEL_LIMIT;