-/*
+/*
Copyright (C) Andrew Tridgell 1996
Copyright (C) Paul Mackerras 1996
Copyright (C) 2001, 2002 by Martin Pool <mbp@samba.org>
-
+
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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
-
+
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
+
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
extern struct stats stats;
extern int verbose;
+extern int do_progress;
extern int am_server;
extern int always_checksum;
extern int cvs_exclude;
extern int recurse;
+extern char *files_from;
+extern int filesfrom_fd;
extern int one_file_system;
extern int make_backups;
extern int preserve_gid;
extern int preserve_times;
extern int relative_paths;
+extern int implied_dirs;
extern int copy_links;
extern int copy_unsafe_links;
-extern int remote_version;
-extern int io_error;
+extern int protocol_version;
extern int sanitize_paths;
extern int read_batch;
extern int write_batch;
-static char topsrcname[MAXPATHLEN];
+extern struct exclude_struct **exclude_list;
+extern struct exclude_struct **server_exclude_list;
+extern struct exclude_struct **local_exclude_list;
-static struct exclude_struct **local_exclude_list;
+int io_error;
static struct file_struct null_file;
-static void clean_flist(struct file_list *flist, int strip_root);
+static void clean_flist(struct file_list *flist, int strip_root, int no_dups);
-static int show_build_progress_p(void)
+static int show_filelist_p(void)
{
- extern int do_progress;
+ return verbose && (recurse || files_from) && !am_server;
+}
- return do_progress && verbose && recurse && !am_server;
+static void start_filelist_progress(char *kind)
+{
+ rprintf(FINFO, "%s ... ", kind);
+ if ((verbose > 1) || do_progress)
+ rprintf(FINFO, "\n");
+ rflush(FINFO);
}
-/**
- * True if we're local, etc, and should emit progress emssages.
- **/
-static void emit_build_progress(const struct file_list *flist)
+
+static void emit_filelist_progress(const struct file_list *flist)
{
rprintf(FINFO, " %d files...\r", flist->count);
}
-static void finish_build_progress(const struct file_list *flist)
+static void maybe_emit_filelist_progress(const struct file_list *flist)
{
- if (verbose && recurse && !am_server) {
- /* This overwrites the progress line, if any. */
- rprintf(FINFO, RSYNC_NAME ": %d files to consider.\n",
- flist->count);
- }
+ if (do_progress && show_filelist_p() && ((flist->count % 100) == 0))
+ emit_filelist_progress(flist);
}
+static void finish_filelist_progress(const struct file_list *flist)
+{
+ if (do_progress) {
+ /* This overwrites the progress line */
+ rprintf(FINFO, "%d file%sto consider\n",
+ flist->count, flist->count == 1 ? " " : "s ");
+ } else {
+ rprintf(FINFO, "done\n");
+ }
+}
+
void show_flist_stats(void)
{
/* Nothing yet */
if (size <= 0)
size = ARENA_SIZE;
- a = malloc(sizeof(*a));
+ a = new(struct string_area);
if (!a)
out_of_memory("string_area_new");
- a->current = a->base = malloc(size);
+ a->current = a->base = new_array(char, size);
if (!a->current)
out_of_memory("string_area_new buffer");
a->end = a->base + size;
}
-int readlink_stat(const char *Path, STRUCT_STAT * Buffer, char *Linkbuf)
+/**
+ * Stat either a symlink or its referent, depending on the settings of
+ * copy_links, copy_unsafe_links, etc.
+ *
+ * @retval -1 on error
+ *
+ * @retval 0 for success
+ *
+ * @post If @p path is a symlink, then @p linkbuf (of size @c
+ * MAXPATHLEN) contains the symlink target.
+ *
+ * @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)
{
#if SUPPORT_LINKS
if (copy_links) {
- return do_stat(Path, Buffer);
+ return do_stat(path, buffer);
}
- if (do_lstat(Path, Buffer) == -1) {
+ if (do_lstat(path, buffer) == -1) {
return -1;
}
- if (S_ISLNK(Buffer->st_mode)) {
+ if (S_ISLNK(buffer->st_mode)) {
int l;
- if ((l =
- readlink((char *) Path, Linkbuf,
- MAXPATHLEN - 1)) == -1) {
+ l = readlink((char *) path, linkbuf, MAXPATHLEN - 1);
+ if (l == -1)
return -1;
- }
- Linkbuf[l] = 0;
- if (copy_unsafe_links && (topsrcname[0] != '\0') &&
- unsafe_symlink(Linkbuf, topsrcname)) {
- return do_stat(Path, Buffer);
+ linkbuf[l] = 0;
+ if (copy_unsafe_links && unsafe_symlink(linkbuf, path)) {
+ if (verbose > 1) {
+ rprintf(FINFO,"copying unsafe symlink \"%s\" -> \"%s\"\n",
+ path, linkbuf);
+ }
+ return do_stat(path, buffer);
}
}
return 0;
#else
- return do_stat(Path, Buffer);
+ return do_stat(path, buffer);
#endif
}
-int link_stat(const char *Path, STRUCT_STAT * Buffer)
+int link_stat(const char *path, STRUCT_STAT * buffer)
{
#if SUPPORT_LINKS
if (copy_links) {
- return do_stat(Path, Buffer);
+ return do_stat(path, buffer);
} else {
- return do_lstat(Path, Buffer);
+ return do_lstat(path, buffer);
}
#else
- return do_stat(Path, Buffer);
+ return do_stat(path, buffer);
#endif
}
/*
- This function is used to check if a file should be included/excluded
- from the list of files based on its name and type etc
+ * This function is used to check if a file should be included/excluded
+ * from the list of files based on its name and type etc. The value of
+ * exclude_level is set to either SERVER_EXCLUDES or ALL_EXCLUDES.
*/
-static int check_exclude_file(int f, char *fname, STRUCT_STAT * st)
+static int check_exclude_file(char *fname, int is_dir, int exclude_level)
{
- extern int delete_excluded;
-
- /* f is set to -1 when calculating deletion file list */
- if ((f == -1) && delete_excluded) {
+#if 0 /* This currently never happens, so avoid a useless compare. */
+ if (exclude_level == NO_EXCLUDES)
return 0;
+#endif
+ if (fname) {
+ /* never exclude '.', even if somebody does --exclude '*' */
+ if (fname[0] == '.' && !fname[1])
+ return 0;
+ /* Handle the -R version of the '.' dir. */
+ if (fname[0] == '/') {
+ int len = strlen(fname);
+ if (fname[len-1] == '.' && fname[len-2] == '/')
+ return 0;
+ }
}
- if (check_exclude(fname, local_exclude_list, st)) {
+ if (server_exclude_list
+ && check_exclude(server_exclude_list, fname, is_dir))
+ return 1;
+ if (exclude_level != ALL_EXCLUDES)
+ return 0;
+ if (exclude_list && check_exclude(exclude_list, fname, is_dir))
+ return 1;
+ if (local_exclude_list
+ && check_exclude(local_exclude_list, fname, is_dir))
return 1;
- }
return 0;
}
static void flist_expand(struct file_list *flist)
{
if (flist->count >= flist->malloced) {
+ void *new_ptr;
+
if (flist->malloced < 1000)
flist->malloced += 1000;
else
flist->malloced *= 2;
- flist->files = (struct file_struct **)
- realloc(flist->files,
- sizeof(flist->files[0]) * flist->malloced);
+
+ if (flist->files) {
+ new_ptr = realloc_array(flist->files,
+ struct file_struct *,
+ flist->malloced);
+ } else {
+ new_ptr = new_array(struct file_struct *,
+ flist->malloced);
+ }
+
+ if (verbose >= 2) {
+ rprintf(FINFO, "expand file_list to %.0f bytes, did%s move\n",
+ (double)sizeof(flist->files[0])
+ * flist->malloced,
+ (new_ptr == flist->files) ? " not" : "");
+ }
+
+ flist->files = (struct file_struct **) new_ptr;
+
if (!flist->files)
out_of_memory("flist_expand");
}
unsigned char flags;
static time_t last_time;
static mode_t last_mode;
- static dev_t last_rdev;
+ static DEV64_T last_rdev;
static uid_t last_uid;
static gid_t last_gid;
static char lastname[MAXPATHLEN];
return;
}
+ io_write_phase = "send_file_entry";
+
fname = f_name(file);
flags = base_flags;
for (l1 = 0;
lastname[l1] && (fname[l1] == lastname[l1]) && (l1 < 255);
- l1++);
+ l1++) {}
l2 = strlen(fname) - l1;
if (l1 > 0)
#if SUPPORT_HARD_LINKS
if (preserve_hard_links && S_ISREG(file->mode)) {
- if (remote_version < 26) {
+ if (protocol_version < 26) {
/* 32-bit dev_t and ino_t */
write_int(f, (int) file->dev);
write_int(f, (int) file->inode);
#endif
if (always_checksum) {
- if (remote_version < 21) {
+ if (protocol_version < 21) {
write_buf(f, file->sum, 2);
} else {
write_buf(f, file->sum, MD4_SUM_LENGTH);
strlcpy(lastname, fname, MAXPATHLEN);
lastname[MAXPATHLEN - 1] = 0;
+
+ io_write_phase = "unknown";
}
{
static time_t last_time;
static mode_t last_mode;
- static dev_t last_rdev;
+ static DEV64_T last_rdev;
static uid_t last_uid;
static gid_t last_gid;
static char lastname[MAXPATHLEN];
else
l2 = read_byte(f);
- file = (struct file_struct *) malloc(sizeof(*file));
+ file = new(struct file_struct);
if (!file)
out_of_memory("receive_file_entry");
memset((char *) file, 0, sizeof(*file));
(flags & SAME_GID) ? last_gid : (gid_t) read_int(f);
if (preserve_devices && IS_DEVICE(file->mode))
file->rdev =
- (flags & SAME_RDEV) ? last_rdev : (dev_t) read_int(f);
+ (flags & SAME_RDEV) ? last_rdev : (DEV64_T) read_int(f);
if (preserve_links && S_ISLNK(file->mode)) {
int l = read_int(f);
rprintf(FERROR, "overflow: l=%d\n", l);
overflow("receive_file_entry");
}
- file->link = (char *) malloc(l + 1);
+ file->link = new_array(char, l + 1);
if (!file->link)
out_of_memory("receive_file_entry 2");
read_sbuf(f, file->link, l);
}
#if SUPPORT_HARD_LINKS
if (preserve_hard_links && S_ISREG(file->mode)) {
- if (remote_version < 26) {
+ if (protocol_version < 26) {
file->dev = read_int(f);
file->inode = read_int(f);
} else {
#endif
if (always_checksum) {
- file->sum = (char *) malloc(MD4_SUM_LENGTH);
+ file->sum = new_array(char, MD4_SUM_LENGTH);
if (!file->sum)
out_of_memory("md4 sum");
- if (remote_version < 21) {
+ if (protocol_version < 21) {
read_buf(f, file->sum, 2);
} else {
read_buf(f, file->sum, MD4_SUM_LENGTH);
/* IRIX cc cares that the operands to the ternary have the same type. */
#define MALLOC(ap, i) (ap ? (void*) string_area_malloc(ap, i) : malloc(i))
-/* create a file_struct for a named file */
-struct file_struct *make_file(int f, char *fname, struct string_area **ap,
- int noexcludes)
+/**
+ * Create a file_struct for a named file by reading its stat()
+ * information and performing extensive checks against global
+ * options.
+ *
+ * @return the new file, or NULL if there was an error or this file
+ * should be excluded.
+ *
+ * @todo There is a small optimization opportunity here to avoid
+ * stat()ing the file in some circumstances, which has a certain cost.
+ * We are called immediately after doing readdir(), and so we may
+ * already know the d_type of the file. We could for example avoid
+ * 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 string_area **ap,
+ int exclude_level)
{
struct file_struct *file;
STRUCT_STAT st;
if (readlink_stat(fname, &st, linkbuf) != 0) {
int save_errno = errno;
- if ((errno == ENOENT) && copy_links && !noexcludes) {
- /* symlink pointing nowhere, see if excluded */
- memset((char *) &st, 0, sizeof(st));
- if (check_exclude_file(f, fname, &st)) {
+ if (errno == ENOENT && exclude_level != NO_EXCLUDES) {
+ /* either symlink pointing nowhere or file that
+ * was removed during rsync run; see if excluded
+ * before reporting an error */
+ if (check_exclude_file(fname, 0, exclude_level)) {
/* file is excluded anyway, ignore silently */
return NULL;
}
}
- io_error = 1;
- rprintf(FERROR, "readlink %s: %s\n",
- fname, strerror(save_errno));
+ io_error |= IOERR_GENERAL;
+ rprintf(FERROR, "readlink %s failed: %s\n",
+ full_fname(fname), strerror(save_errno));
return NULL;
}
- /* we use noexcludes from backup.c */
- if (noexcludes)
+ /* backup.c calls us with exclude_level set to NO_EXCLUDES. */
+ if (exclude_level == NO_EXCLUDES)
goto skip_excludes;
- if (S_ISDIR(st.st_mode) && !recurse) {
+ if (S_ISDIR(st.st_mode) && !recurse && !files_from) {
rprintf(FINFO, "skipping directory %s\n", fname);
return NULL;
}
return NULL;
}
- if (check_exclude_file(f, fname, &st))
+ if (check_exclude_file(fname, S_ISDIR(st.st_mode) != 0, exclude_level))
return NULL;
-
if (lp_ignore_nonreadable(module_id) && access(fname, R_OK) != 0)
return NULL;
skip_excludes:
if (verbose > 2)
- rprintf(FINFO, "make_file(%d,%s)\n", f, fname);
+ rprintf(FINFO, "make_file(%s,*,%d)\n", fname, exclude_level);
- file = (struct file_struct *) malloc(sizeof(*file));
+ file = new(struct file_struct);
if (!file)
out_of_memory("make_file");
memset((char *) file, 0, sizeof(*file));
file->gid = st.st_gid;
file->dev = st.st_dev;
file->inode = st.st_ino;
-#ifdef HAVE_ST_RDEV
+#ifdef HAVE_STRUCT_STAT_ST_RDEV
file->rdev = st.st_rdev;
#endif
int recursive, unsigned base_flags)
{
struct file_struct *file;
+ extern int delete_excluded;
- file = make_file(f, fname, &flist->string_area, 0);
+ /* f is set to -1 when calculating deletion file list */
+ file = make_file(fname, &flist->string_area,
+ f == -1 && delete_excluded? SERVER_EXCLUDES
+ : ALL_EXCLUDES);
if (!file)
return;
- if (show_build_progress_p() & !(flist->count % 100))
- emit_build_progress(flist);
+ maybe_emit_filelist_progress(flist);
flist_expand(flist);
- if (write_batch) /* dw */
+ if (write_batch)
file->flags = FLAG_DELETE;
- if (strcmp(file->basename, "")) {
+ if (file->basename[0]) {
flist->files[flist->count++] = file;
send_file_entry(file, f, base_flags);
}
d = opendir(dir);
if (!d) {
- io_error = 1;
- rprintf(FERROR, "opendir(%s): %s\n", dir, strerror(errno));
+ io_error |= IOERR_GENERAL;
+ rprintf(FERROR, "opendir %s failed: %s\n",
+ full_fname(dir), strerror(errno));
return;
}
l = strlen(fname);
if (fname[l - 1] != '/') {
if (l == MAXPATHLEN - 1) {
- io_error = 1;
- rprintf(FERROR,
- "skipping long-named directory %s\n",
- fname);
+ io_error |= IOERR_GENERAL;
+ rprintf(FERROR, "skipping long-named directory: %s\n",
+ full_fname(fname));
closedir(d);
return;
}
if (cvs_exclude) {
if (strlen(fname) + strlen(".cvsignore") <= MAXPATHLEN - 1) {
strcpy(p, ".cvsignore");
- local_exclude_list =
- make_exclude_list(fname, NULL, 0, 0);
+ add_exclude_file(&exclude_list,fname,MISSING_OK,ADD_EXCLUDE);
} else {
- io_error = 1;
+ io_error |= IOERR_GENERAL;
rprintf(FINFO,
"cannot cvs-exclude in long-named directory %s\n",
- fname);
+ full_fname(fname));
}
}
- for (di = readdir(d); di; di = readdir(d)) {
+ for (errno = 0, di = readdir(d); di; errno = 0, di = readdir(d)) {
char *dname = d_name(di);
- if (strcmp(dname, ".") == 0 || strcmp(dname, "..") == 0)
+ if (dname[0] == '.' && (dname[1] == '\0'
+ || (dname[1] == '.' && dname[2] == '\0')))
continue;
strlcpy(p, dname, MAXPATHLEN - l);
send_file_name(f, flist, fname, recurse, 0);
}
-
- if (local_exclude_list) {
- add_exclude_list("!", &local_exclude_list, 0);
+ if (errno) {
+ io_error |= IOERR_GENERAL;
+ rprintf(FERROR, "readdir(%s): (%d) %s\n",
+ dir, errno, strerror(errno));
}
+ if (local_exclude_list)
+ free_exclude_list(&local_exclude_list); /* Zeros pointer too */
+
closedir(d);
}
-/*
- *
- * I *think* f==-1 means that the list should just be built in memory
- * and not transmitted. But who can tell? -- mbp
- */
+/**
+ * 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
+ * is set (so that the delete list includes user-excluded files).
+ **/
struct file_list *send_file_list(int f, int argc, char *argv[])
{
- int i, l;
+ int l;
STRUCT_STAT st;
char *p, *dir, *olddir;
char lastpath[MAXPATHLEN] = "";
struct file_list *flist;
int64 start_write;
+ int use_ff_fd = 0;
- if (verbose && recurse && !am_server && f != -1) {
- rprintf(FINFO, RSYNC_NAME ": building file list...\n");
- if (verbose > 1)
- rprintf(FINFO, "\n");
- rflush(FINFO);
- }
+ if (show_filelist_p() && f != -1)
+ start_filelist_progress("building file list");
start_write = stats.total_written;
if (f != -1) {
io_start_buffering(f);
+ if (filesfrom_fd >= 0) {
+ if (argv[0] && !push_dir(argv[0], 0)) {
+ rprintf(FERROR, "push_dir %s failed: %s\n",
+ full_fname(argv[0]), strerror(errno));
+ exit_cleanup(RERR_FILESELECT);
+ }
+ use_ff_fd = 1;
+ }
}
- for (i = 0; i < argc; i++) {
- char *fname = topsrcname;
+ while (1) {
+ char fname2[MAXPATHLEN];
+ char *fname = fname2;
- strlcpy(fname, argv[i], MAXPATHLEN);
+ if (use_ff_fd) {
+ if (read_filesfrom_line(filesfrom_fd, fname) == 0)
+ break;
+ sanitize_path(fname, NULL);
+ } else {
+ if (argc-- == 0)
+ break;
+ strlcpy(fname, *argv++, MAXPATHLEN);
+ if (sanitize_paths)
+ sanitize_path(fname, NULL);
+ }
l = strlen(fname);
- if (l != 1 && fname[l - 1] == '/') {
- if ((l == 2) && (fname[0] == '.')) {
- /* Turn ./ into just . rather than ./.
- This was put in to avoid a problem with
- rsync -aR --delete from ./
- The send_file_name() below of ./ was
- mysteriously preventing deletes */
- fname[1] = 0;
+ if (fname[l - 1] == '/') {
+ if (l == 2 && fname[0] == '.') {
+ /* Turn "./" into just "." rather than "./." */
+ fname[1] = '\0';
} else {
strlcat(fname, ".", MAXPATHLEN);
}
if (link_stat(fname, &st) != 0) {
if (f != -1) {
- io_error = 1;
- rprintf(FERROR, "link_stat %s : %s\n",
- fname, strerror(errno));
+ io_error |= IOERR_GENERAL;
+ rprintf(FERROR, "link_stat %s failed: %s\n",
+ full_fname(fname), strerror(errno));
}
continue;
}
- if (S_ISDIR(st.st_mode) && !recurse) {
+ if (S_ISDIR(st.st_mode) && !recurse && !files_from) {
rprintf(FINFO, "skipping directory %s\n", fname);
continue;
}
dir = fname;
fname = p + 1;
}
- } else if (f != -1 && (p = strrchr(fname, '/'))) {
+ } else if (f != -1 && implied_dirs && (p=strrchr(fname,'/')) && p != fname) {
/* this ensures we send the intermediate directories,
thus getting their permissions right */
+ char *lp = lastpath, *fn = fname, *slash = fname;
*p = 0;
- if (strcmp(lastpath, fname)) {
- strlcpy(lastpath, fname, sizeof(lastpath));
- *p = '/';
- for (p = fname + 1; (p = strchr(p, '/'));
- p++) {
- int copy_links_saved = copy_links;
- int recurse_saved = recurse;
- *p = 0;
- copy_links = copy_unsafe_links;
- /* set recurse to 1 to prevent make_file
- from ignoring directory, but still
- turn off the recursive parameter to
- send_file_name */
- recurse = 1;
- send_file_name(f, flist, fname, 0,
- 0);
- copy_links = copy_links_saved;
- recurse = recurse_saved;
- *p = '/';
+ /* Skip any initial directories in our path that we
+ * have in common with lastpath. */
+ while (*fn && *lp == *fn) {
+ if (*fn == '/')
+ slash = fn;
+ lp++, fn++;
+ }
+ *p = '/';
+ if (fn != p || (*lp && *lp != '/')) {
+ int copy_links_saved = copy_links;
+ int recurse_saved = recurse;
+ copy_links = copy_unsafe_links;
+ /* set recurse to 1 to prevent make_file
+ * from ignoring directory, but still
+ * turn off the recursive parameter to
+ * send_file_name */
+ recurse = 1;
+ while ((slash = strchr(slash+1, '/')) != 0) {
+ *slash = 0;
+ send_file_name(f, flist, fname, 0, 0);
+ *slash = '/';
}
- } else {
+ copy_links = copy_links_saved;
+ recurse = recurse_saved;
+ *p = 0;
+ strlcpy(lastpath, fname, sizeof lastpath);
*p = '/';
}
}
olddir = push_dir(dir, 1);
if (!olddir) {
- io_error = 1;
- rprintf(FERROR, "push_dir %s : %s\n",
- dir, strerror(errno));
+ io_error |= IOERR_GENERAL;
+ rprintf(FERROR, "push_dir %s failed: %s\n",
+ full_fname(dir), strerror(errno));
continue;
}
if (olddir != NULL) {
flist_dir = NULL;
if (pop_dir(olddir) != 0) {
- rprintf(FERROR, "pop_dir %s : %s\n",
- dir, strerror(errno));
+ rprintf(FERROR, "pop_dir %s failed: %s\n",
+ full_fname(dir), strerror(errno));
exit_cleanup(RERR_FILESELECT);
}
}
}
- topsrcname[0] = '\0';
-
if (f != -1) {
send_file_entry(NULL, f, 0);
}
- finish_build_progress(flist);
+ if (show_filelist_p() && f != -1) {
+ finish_filelist_progress(flist);
+ }
- clean_flist(flist, 0);
+ clean_flist(flist, 0, 0);
/* now send the uid/gid list. This was introduced in protocol
version 15 */
- if (f != -1 && remote_version >= 15) {
+ if (f != -1) {
send_uid_list(f);
}
- /* if protocol version is >= 17 then send the io_error flag */
- if (f != -1 && remote_version >= 17) {
+ /* send the io_error flag */
+ if (f != -1) {
extern int module_id;
write_int(f, lp_ignore_errors(module_id) ? 0 : io_error);
}
if (f != -1) {
- io_end_buffering(f);
+ io_end_buffering();
stats.flist_size = stats.total_written - start_write;
stats.num_files = flist->count;
- if (write_batch) /* dw */
+ if (write_batch)
write_batch_flist_info(flist->count, flist->files);
}
int64 start_read;
extern int list_only;
- if (verbose && recurse && !am_server) {
- rprintf(FINFO, "receiving file list ... ");
- rflush(FINFO);
- }
+ if (show_filelist_p())
+ start_filelist_progress("receiving file list");
start_read = stats.total_read;
- flist = (struct file_list *) malloc(sizeof(flist[0]));
+ flist = new(struct file_list);
if (!flist)
goto oom;
flist->count = 0;
flist->malloced = 1000;
- flist->files =
- (struct file_struct **) malloc(sizeof(flist->files[0]) *
- flist->malloced);
+ flist->files = new_array(struct file_struct *, flist->malloced);
if (!flist->files)
goto oom;
for (flags = read_byte(f); flags; flags = read_byte(f)) {
+ int i = flist->count;
+
flist_expand(flist);
receive_file_entry(&flist->files[i], flags, f);
flist->count++;
+ maybe_emit_filelist_progress(flist);
+
if (verbose > 2)
rprintf(FINFO, "recv_file_name(%s)\n",
f_name(flist->files[i]));
if (verbose > 2)
rprintf(FINFO, "received %d names\n", flist->count);
- clean_flist(flist, relative_paths);
+ clean_flist(flist, relative_paths, 1);
- if (verbose && recurse && !am_server) {
- rprintf(FINFO, "done\n");
+ if (show_filelist_p()) {
+ finish_filelist_progress(flist);
}
/* now recv the uid/gid list. This was introduced in protocol version 15 */
- if (f != -1 && remote_version >= 15) {
+ if (f != -1) {
recv_uid_list(f, flist);
}
- /* if protocol version is >= 17 then recv the io_error flag */
- if (f != -1 && remote_version >= 17 && !read_batch) { /* dw-added readbatch */
+ /* recv the io_error flag */
+ if (f != -1 && !read_batch) { /* dw-added readbatch */
extern int module_id;
extern int ignore_errors;
if (lp_ignore_errors(module_id) || ignore_errors) {
{
int low = 0, high = flist->count - 1;
- if (flist->count <= 0)
+ while (high >= 0 && !flist->files[high]->basename) high--;
+
+ if (high < 0)
return -1;
while (low != high) {
/*
* allocate a new file list
*/
-struct file_list *flist_new()
+struct file_list *flist_new(void)
{
struct file_list *flist;
- flist = (struct file_list *) malloc(sizeof(flist[0]));
+ flist = new(struct file_list);
if (!flist)
out_of_memory("send_file_list");
/*
* This routine ensures we don't have any duplicate names in our file list.
- * duplicate names can cause corruption because of the pipelining
+ * duplicate names can cause corruption because of the pipelining
*/
-static void clean_flist(struct file_list *flist, int strip_root)
+static void clean_flist(struct file_list *flist, int strip_root, int no_dups)
{
- int i;
+ int i, prev_i = 0;
+ char *name, *prev_name = NULL;
if (!flist || flist->count == 0)
return;
qsort(flist->files, flist->count,
sizeof(flist->files[0]), (int (*)()) file_compare);
- for (i = 1; i < flist->count; i++) {
- if (flist->files[i]->basename &&
- flist->files[i - 1]->basename &&
- strcmp(f_name(flist->files[i]),
- f_name(flist->files[i - 1])) == 0) {
- if (verbose > 1 && !am_server)
+ for (i = no_dups? 0 : flist->count; i < flist->count; i++) {
+ if (flist->files[i]->basename) {
+ prev_i = i;
+ prev_name = f_name(flist->files[i]);
+ break;
+ }
+ }
+ while (++i < flist->count) {
+ if (!flist->files[i]->basename)
+ continue;
+ name = f_name(flist->files[i]);
+ if (strcmp(name, prev_name) == 0) {
+ if (verbose > 1 && !am_server) {
rprintf(FINFO,
"removing duplicate name %s from file list %d\n",
- f_name(flist->files[i - 1]),
- i - 1);
- /* it's not great that the flist knows the semantics of the
- * file memory usage, but i'd rather not add a flag byte
- * to that struct. XXX can i use a bit in the flags field? */
+ name, i);
+ }
+ /* Make sure that if we unduplicate '.', that we don't
+ * lose track of a user-specified starting point (or
+ * else deletions will mysteriously fail with -R). */
+ if (flist->files[i]->flags & FLAG_DELETE)
+ flist->files[prev_i]->flags |= FLAG_DELETE;
+ /* it's not great that the flist knows the semantics of
+ * the file memory usage, but i'd rather not add a flag
+ * byte to that struct.
+ * XXX can i use a bit in the flags field? */
if (flist->string_area)
flist->files[i][0] = null_file;
else
free_file(flist->files[i]);
}
+ else
+ prev_i = i;
+ /* We set prev_name every iteration to avoid it becoming
+ * invalid when names[][] in f_name() wraps around. */
+ prev_name = name;
}
- /* FIXME: There is a bug here when filenames are repeated more
- * than once, because we don't handle freed files when doing
- * the comparison. */
-
if (strip_root) {
/* we need to strip off the root directory in the case
of relative paths, but this must be done _after_
}
}
-
if (verbose <= 3)
return;