* Copyright (C) 1996-2000 Andrew Tridgell
* Copyright (C) 1996 Paul Mackerras
* Copyright (C) 2001, 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 modify_window;
extern int relative_paths;
extern int human_readable;
+extern int preserve_xattrs;
extern char *module_dir;
extern unsigned int module_dirlen;
extern mode_t orig_umask;
/* Copy a file. If ofd < 0, copy_file unlinks and opens the "dest" file.
* Otherwise, it just writes to and closes the provided file descriptor.
+ * In either case, if --xattrs are being preserved, the dest file will
+ * have its xattrs set from the source file.
*
* This is used in conjunction with the --temp-dir, --backup, and
* --copy-dest options. */
int len; /* Number of bytes read into `buf'. */
if ((ifd = do_open(source, O_RDONLY, 0)) < 0) {
+ int save_errno = errno;
rsyserr(FERROR_XFER, errno, "open %s", full_fname(source));
+ errno = save_errno;
return -1;
}
if (ofd < 0) {
if (robust_unlink(dest) && errno != ENOENT) {
+ int save_errno = errno;
rsyserr(FERROR_XFER, errno, "unlink %s", full_fname(dest));
+ errno = save_errno;
return -1;
}
- if ((ofd = do_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode)) < 0
- && (!create_bak_dir || errno != ENOENT || make_bak_dir(dest) < 0
- || (ofd = do_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode)) < 0)) {
- rsyserr(FERROR_XFER, errno, "open %s", full_fname(dest));
- close(ifd);
- return -1;
+ if ((ofd = do_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode)) < 0) {
+ int save_errno = errno ? errno : EINVAL; /* 0 paranoia */
+ if (create_bak_dir && errno == ENOENT && make_bak_dir(dest) == 0) {
+ if ((ofd = do_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode)) < 0)
+ save_errno = errno ? errno : save_errno;
+ else
+ save_errno = 0;
+ }
+ if (save_errno) {
+ rsyserr(FERROR_XFER, save_errno, "open %s", full_fname(dest));
+ close(ifd);
+ errno = save_errno;
+ return -1;
+ }
}
}
while ((len = safe_read(ifd, buf, sizeof buf)) > 0) {
if (full_write(ofd, buf, len) < 0) {
+ int save_errno = errno;
rsyserr(FERROR_XFER, errno, "write %s", full_fname(dest));
close(ifd);
close(ofd);
+ errno = save_errno;
return -1;
}
}
if (len < 0) {
+ int save_errno = errno;
rsyserr(FERROR_XFER, errno, "read %s", full_fname(source));
close(ifd);
close(ofd);
+ errno = save_errno;
return -1;
}
}
if (close(ofd) < 0) {
+ int save_errno = errno;
rsyserr(FERROR_XFER, errno, "close failed on %s",
full_fname(dest));
+ errno = save_errno;
return -1;
}
+#ifdef SUPPORT_XATTRS
+ if (preserve_xattrs)
+ copy_xattrs(source, dest);
+#endif
+
return 0;
}
switch (errno) {
#ifdef ETXTBSY
case ETXTBSY:
- if (robust_unlink(to) != 0)
+ if (robust_unlink(to) != 0) {
+ errno = ETXTBSY;
return -1;
+ }
+ errno = ETXTBSY;
break;
#endif
case EXDEV:
if (partialptr) {
if (!handle_partial_dir(partialptr,PDIR_CREATE))
- return -1;
+ return -2;
to = partialptr;
}
if (copy_file(from, to, -1, mode, 0) != 0)
}
#define ENSURE_MEMSPACE(buf, type, sz, req) \
- if ((req) >= sz && !(buf = realloc_array(buf, type, sz *= 2))) \
- out_of_memory("ENSURE_MEMSPACE")
+ if ((req) > sz && !(buf = realloc_array(buf, type, sz = MAX(sz * 2, req)))) \
+ out_of_memory("glob_expand")
static inline void call_glob_match(const char *name, int len, int from_glob,
char *arg, int abpos, int fbpos);
while (*arg == '.' && arg[1] == '/') {
if (fbpos < 0) {
- if (glob.fbsize < glob.absize) {
- glob.filt_buf = realloc_array(glob.filt_buf,
- char, glob.fbsize = glob.absize);
- }
+ ENSURE_MEMSPACE(glob.filt_buf, char, glob.fbsize, glob.absize);
memcpy(glob.filt_buf, glob.arg_buf, abpos + 1);
fbpos = abpos;
}
- ENSURE_MEMSPACE(glob.arg_buf, char, glob.absize, abpos + 2);
+ ENSURE_MEMSPACE(glob.arg_buf, char, glob.absize, abpos + 3);
glob.arg_buf[abpos++] = *arg++;
glob.arg_buf[abpos++] = *arg++;
glob.arg_buf[abpos] = '\0';
} else
use_buf = glob.arg_buf;
- if (from_glob || arg) {
+ if (from_glob || (arg && len)) {
STRUCT_STAT st;
int is_dir;
- if (do_stat(glob.arg_buf, &st) != 0) {
- if (from_glob)
- return;
- is_dir = 0;
- } else {
- is_dir = S_ISDIR(st.st_mode) != 0;
- if (arg && !is_dir)
- return;
- }
+ if (do_stat(glob.arg_buf, &st) != 0)
+ return;
+ is_dir = S_ISDIR(st.st_mode) != 0;
+ if (arg && !is_dir)
+ return;
if (daemon_filter_list.head
- && check_filter(&daemon_filter_list, use_buf, is_dir) < 0) {
- if (from_glob)
- return;
- arg = NULL;
- }
+ && check_filter(&daemon_filter_list, FLOG, use_buf, is_dir) < 0)
+ return;
}
if (arg) {
| CFN_COLLAPSE_DOT_DOT_DIRS);
}
- if (glob.absize < MAXPATHLEN
- && !(glob.arg_buf = realloc_array(glob.arg_buf, char, glob.absize = MAXPATHLEN)))
- out_of_memory("glob_expand");
+ ENSURE_MEMSPACE(glob.arg_buf, char, glob.absize, MAXPATHLEN);
*glob.arg_buf = '\0';
glob.argc = save_argc = *argc_p;
glob.argv = *argv_p;
glob.maxargs = *maxargs_p;
- if (glob.maxargs < 100
- && !(glob.argv = realloc_array(glob.argv, char *, glob.maxargs = 100)))
- out_of_memory("glob_expand");
+ ENSURE_MEMSPACE(glob.argv, char *, glob.maxargs, 100);
glob_match(s, 0, -1);
return cnt;
}
-/* Turns multiple adjacent slashes into a single slash, drops all leading or
+/* Turns multiple adjacent slashes into a single slash (possible exception:
+ * the preserving of two leading slashes at the start), drops all leading or
* interior "." elements unless CFN_KEEP_DOT_DIRS is flagged. Will also drop
* a trailing '.' after a '/' if CFN_DROP_TRAILING_DOT_DIR is flagged, removes
* a trailing slash (perhaps after removing the aforementioned dot) unless
if (!name)
return 0;
- if ((anchored = *f == '/') != 0)
+ if ((anchored = *f == '/') != 0) {
*t++ = *f++;
- else if (flags & CFN_KEEP_DOT_DIRS && *f == '.' && f[1] == '/') {
+#ifdef __CYGWIN__
+ /* If there are exactly 2 slashes at the start, preserve
+ * them. Would break daemon excludes unless the paths are
+ * really treated differently, so used this sparingly. */
+ if (*f == '/' && f[1] != '/')
+ *t++ = *f++;
+#endif
+ } else if (flags & CFN_KEEP_DOT_DIRS && *f == '.' && f[1] == '/') {
*t++ = *f++;
*t++ = *f++;
}
/* Like chdir(), but it keeps track of the current directory (in the
* global "curr_dir"), and ensures that the path size doesn't overflow.
* Also cleans the path using the clean_fname() function. */
-int push_dir(const char *dir, int set_path_only)
+int change_dir(const char *dir, int set_path_only)
{
static int initialised;
unsigned int len;
if (!initialised) {
initialised = 1;
- getcwd(curr_dir, sizeof curr_dir - 1);
+ if (getcwd(curr_dir, sizeof curr_dir - 1) == NULL) {
+ rsyserr(FERROR, errno, "getcwd()");
+ exit_cleanup(RERR_FILESELECT);
+ }
curr_dir_len = strlen(curr_dir);
}
if (len == 1 && *dir == '.')
return 1;
- if ((*dir == '/' ? len : curr_dir_len + 1 + len) >= sizeof curr_dir) {
- errno = ENAMETOOLONG;
- return 0;
- }
-
- if (!set_path_only && chdir(dir))
- return 0;
-
if (*dir == '/') {
+ if (len >= sizeof curr_dir) {
+ errno = ENAMETOOLONG;
+ return 0;
+ }
+ if (!set_path_only && chdir(dir))
+ return 0;
memcpy(curr_dir, dir, len + 1);
- curr_dir_len = len;
} else {
- curr_dir[curr_dir_len++] = '/';
- memcpy(curr_dir + curr_dir_len, dir, len + 1);
- curr_dir_len += len;
+ if (curr_dir_len + 1 + len >= sizeof curr_dir) {
+ errno = ENAMETOOLONG;
+ return 0;
+ }
+ curr_dir[curr_dir_len] = '/';
+ memcpy(curr_dir + curr_dir_len + 1, dir, len + 1);
+
+ if (!set_path_only && chdir(curr_dir)) {
+ curr_dir[curr_dir_len] = '\0';
+ return 0;
+ }
}
curr_dir_len = clean_fname(curr_dir, CFN_COLLAPSE_DOT_DOT_DIRS);
}
if (verbose >= 5 && !set_path_only)
- rprintf(FINFO, "[%s] push_dir(%s)\n", who_am_i(), curr_dir);
+ rprintf(FINFO, "[%s] change_dir(%s)\n", who_am_i(), curr_dir);
return 1;
}
-/**
- * Reverse a push_dir() call. You must pass in an absolute path
- * that was copied from a prior value of "curr_dir".
- **/
-int pop_dir(const char *dir)
+/* This will make a relative path absolute and clean it up via clean_fname().
+ * Returns the string, which might be newly allocated, or NULL on error. */
+char *normalize_path(char *path, BOOL force_newbuf, unsigned int *len_ptr)
{
- if (chdir(dir))
- return 0;
+ unsigned int len;
- curr_dir_len = strlcpy(curr_dir, dir, sizeof curr_dir);
- if (curr_dir_len >= sizeof curr_dir)
- curr_dir_len = sizeof curr_dir - 1;
- if (sanitize_paths)
- curr_dir_depth = count_dir_elements(curr_dir + module_dirlen);
+ if (*path != '/') { /* Make path absolute. */
+ int len = strlen(path);
+ if (curr_dir_len + 1 + len >= sizeof curr_dir)
+ return NULL;
+ curr_dir[curr_dir_len] = '/';
+ memcpy(curr_dir + curr_dir_len + 1, path, len + 1);
+ if (!(path = strdup(curr_dir)))
+ out_of_memory("normalize_path");
+ curr_dir[curr_dir_len] = '\0';
+ } else if (force_newbuf) {
+ if (!(path = strdup(path)))
+ out_of_memory("normalize_path");
+ }
- if (verbose >= 5)
- rprintf(FINFO, "[%s] pop_dir(%s)\n", who_am_i(), curr_dir);
+ len = clean_fname(path, CFN_COLLAPSE_DOT_DOT_DIRS | CFN_DROP_TRAILING_DOT_DIR);
- return 1;
+ if (len_ptr)
+ *len_ptr = len;
+
+ return path;
}
/**
if (daemon_filter_list.head) {
t = strrchr(partial_fname, '/');
*t = '\0';
- if (check_filter(&daemon_filter_list, partial_fname, 1) < 0)
+ if (check_filter(&daemon_filter_list, FLOG, partial_fname, 1) < 0)
return NULL;
*t = '/';
- if (check_filter(&daemon_filter_list, partial_fname, 0) < 0)
+ if (check_filter(&daemon_filter_list, FLOG, partial_fname, 0) < 0)
return NULL;
}
STRUCT_STAT st;
int statret = do_lstat(dir, &st);
if (statret == 0 && !S_ISDIR(st.st_mode)) {
- if (do_unlink(dir) < 0)
+ if (do_unlink(dir) < 0) {
+ *fn = '/';
return 0;
+ }
statret = -1;
}
- if (statret < 0 && do_mkdir(dir, 0700) < 0)
+ if (statret < 0 && do_mkdir(dir, 0700) < 0) {
+ *fn = '/';
return 0;
+ }
} else
do_rmdir(dir);
*fn = '/';
return (depth < 0);
}
+#define HUMANIFY(mult) \
+ do { \
+ if (num >= mult || num <= -mult) { \
+ double dnum = (double)num / mult; \
+ char units; \
+ if (num < 0) \
+ dnum = -dnum; \
+ if (dnum < mult) \
+ units = 'K'; \
+ else if ((dnum /= mult) < mult) \
+ units = 'M'; \
+ else { \
+ dnum /= mult; \
+ units = 'G'; \
+ } \
+ if (num < 0) \
+ dnum = -dnum; \
+ snprintf(bufs[n], sizeof bufs[0], "%.2f%c", dnum, units); \
+ return bufs[n]; \
+ } \
+ } while (0)
+
/* Return the int64 number as a string. If the --human-readable option was
* specified, we may output the number in K, M, or G units. We can return
* up to 4 buffers at a time. */
static char bufs[4][128]; /* more than enough room */
static unsigned int n;
char *s;
+ int negated;
n = (n + 1) % (sizeof bufs / sizeof bufs[0]);
if (human_readable) {
- char units = '\0';
- int mult = human_readable == 1 ? 1000 : 1024;
- double dnum = 0;
- if (num > mult*mult*mult) {
- dnum = (double)num / (mult*mult*mult);
- units = 'G';
- } else if (num > mult*mult) {
- dnum = (double)num / (mult*mult);
- units = 'M';
- } else if (num > mult) {
- dnum = (double)num / mult;
- units = 'K';
- }
- if (units) {
- snprintf(bufs[n], sizeof bufs[0], "%.2f%c", dnum, units);
- return bufs[n];
- }
+ if (human_readable == 1)
+ HUMANIFY(1000);
+ else
+ HUMANIFY(1024);
}
s = bufs[n] + sizeof bufs[0] - 1;
if (!num)
*--s = '0';
+ if (num < 0) {
+ /* A maximum-size negated number can't fit as a positive,
+ * so do one digit in negated form to start us off. */
+ *--s = (char)(-(num % 10)) + '0';
+ num = -(num / 10);
+ negated = 1;
+ } else
+ negated = 0;
+
while (num) {
*--s = (char)(num % 10) + '0';
num /= 10;
}
+
+ if (negated)
+ *--s = '-';
+
return s;
}
int len = strlen(buf);
if (isDigit(buf + len - 1)) {
/* There's extra room in buf prior to the start of the num. */
- buf -= decimal_digits + 1;
- snprintf(buf, len + decimal_digits + 2, "%.*f", decimal_digits, dnum);
+ buf -= decimal_digits + 2;
+ snprintf(buf, len + decimal_digits + 3, "%.*f", decimal_digits, dnum);
}
return buf;
}
return use_calloc ? calloc(num, size) : malloc(num * size);
}
-void *_realloc_array(void *ptr, unsigned int size, unsigned long num)
+void *_realloc_array(void *ptr, unsigned int size, size_t num)
{
if (num >= MALLOC_MAX/size)
return NULL;
return -1;
}
+void flist_ndx_push(flist_ndx_list *lp, int ndx)
+{
+ struct flist_ndx_item *item;
+
+ if (!(item = new(struct flist_ndx_item)))
+ out_of_memory("flist_ndx_push");
+ item->next = NULL;
+ item->ndx = ndx;
+ if (lp->tail)
+ lp->tail->next = item;
+ else
+ lp->head = item;
+ lp->tail = item;
+}
+
+int flist_ndx_pop(flist_ndx_list *lp)
+{
+ struct flist_ndx_item *next;
+ int ndx;
+
+ if (!lp->head)
+ return -1;
+
+ ndx = lp->head->ndx;
+ next = lp->head->next;
+ free(lp->head);
+ lp->head = next;
+ if (!next)
+ lp->tail = NULL;
+
+ return ndx;
+}
+
void *expand_item_list(item_list *lp, size_t item_size,
const char *desc, int incr)
{
new_size += incr;
else
new_size *= 2;
- new_ptr = realloc_array(lp->items, char, new_size * item_size);
+ if (new_size < lp->malloced)
+ overflow_exit("expand_item_list");
+ /* Using _realloc_array() lets us pass the size, not a type. */
+ new_ptr = _realloc_array(lp->items, item_size, new_size);
if (verbose >= 4) {
rprintf(FINFO, "[%s] expand %s to %.0f bytes, did%s move\n",
who_am_i(), desc, (double)new_size * item_size,