version 1.1.1.2, 2013/10/14 07:51:14
|
version 1.1.1.4, 2021/03/17 00:32:36
|
Line 4
|
Line 4
|
* Copyright (C) 1996-2000 Andrew Tridgell |
* Copyright (C) 1996-2000 Andrew Tridgell |
* Copyright (C) 1996 Paul Mackerras |
* Copyright (C) 1996 Paul Mackerras |
* Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org> |
* Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org> |
* Copyright (C) 2003-2013 Wayne Davison | * Copyright (C) 2003-2020 Wayne Davison |
* |
* |
* This program is free software; you can redistribute it and/or modify |
* 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 |
* it under the terms of the GNU General Public License as published by |
Line 25
|
Line 25
|
#include "itypes.h" |
#include "itypes.h" |
#include "inums.h" |
#include "inums.h" |
|
|
|
extern int dry_run; |
extern int module_id; |
extern int module_id; |
|
extern int do_fsync; |
extern int protect_args; |
extern int protect_args; |
extern int modify_window; |
extern int modify_window; |
extern int relative_paths; |
extern int relative_paths; |
extern int preserve_times; |
extern int preserve_times; |
extern int preserve_xattrs; |
extern int preserve_xattrs; |
extern int preallocate_files; |
extern int preallocate_files; |
|
extern int force_change; |
extern char *module_dir; |
extern char *module_dir; |
extern unsigned int module_dirlen; |
extern unsigned int module_dirlen; |
extern char *partial_dir; |
extern char *partial_dir; |
Line 114 void print_child_argv(const char *prefix, char **cmd)
|
Line 117 void print_child_argv(const char *prefix, char **cmd)
|
rprintf(FCLIENT, " (%d args)\n", cnt); |
rprintf(FCLIENT, " (%d args)\n", cnt); |
} |
} |
|
|
|
#ifdef SUPPORT_FORCE_CHANGE |
|
static int try_a_force_change(const char *fname, STRUCT_STAT *stp) |
|
{ |
|
uint32 fileflags = ST_FLAGS(*stp); |
|
if (fileflags == NO_FFLAGS) { |
|
STRUCT_STAT st; |
|
if (x_lstat(fname, &st, NULL) == 0) |
|
fileflags = st.st_flags; |
|
} |
|
if (fileflags != NO_FFLAGS && make_mutable(fname, stp->st_mode, fileflags, force_change) > 0) { |
|
int ret, save_force_change = force_change; |
|
|
|
force_change = 0; /* Make certain we can't come back here. */ |
|
ret = set_times(fname, stp); |
|
force_change = save_force_change; |
|
|
|
undo_make_mutable(fname, fileflags); |
|
|
|
return ret; |
|
} |
|
|
|
errno = EPERM; |
|
|
|
return -1; |
|
} |
|
#endif |
|
|
/* This returns 0 for success, 1 for a symlink if symlink time-setting |
/* This returns 0 for success, 1 for a symlink if symlink time-setting |
* is not possible, or -1 for any other error. */ |
* is not possible, or -1 for any other error. */ |
int set_modtime(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode) | int set_times(const char *fname, STRUCT_STAT *stp) |
{ |
{ |
static int switch_step = 0; |
static int switch_step = 0; |
|
|
if (DEBUG_GTE(TIME, 1)) { |
if (DEBUG_GTE(TIME, 1)) { |
rprintf(FINFO, "set modtime of %s to (%ld) %s", | rprintf(FINFO, |
fname, (long)modtime, | "set modtime, atime of %s to (%ld) %s, (%ld) %s\n", |
asctime(localtime(&modtime))); | fname, (long)stp->st_mtime, |
| timestring(stp->st_mtime), (long)stp->st_atime, timestring(stp->st_atime)); |
} |
} |
|
|
switch (switch_step) { |
switch (switch_step) { |
|
#ifdef HAVE_SETATTRLIST |
|
#include "case_N.h" |
|
if (do_setattrlist_times(fname, stp) == 0) |
|
break; |
|
if (errno != ENOSYS) |
|
return -1; |
|
switch_step++; |
|
#endif |
|
|
#ifdef HAVE_UTIMENSAT |
#ifdef HAVE_UTIMENSAT |
#include "case_N.h" |
#include "case_N.h" |
if (do_utimensat(fname, modtime, mod_nsec) == 0) | if (do_utimensat(fname, stp) == 0) |
break; |
break; |
|
#ifdef SUPPORT_FORCE_CHANGE |
|
if (force_change && errno == EPERM && try_a_force_change(fname, stp) == 0) |
|
break; |
|
#endif |
if (errno != ENOSYS) |
if (errno != ENOSYS) |
return -1; |
return -1; |
switch_step++; |
switch_step++; |
/* FALLTHROUGH */ |
|
#endif |
#endif |
|
|
#ifdef HAVE_LUTIMES |
#ifdef HAVE_LUTIMES |
#include "case_N.h" |
#include "case_N.h" |
if (do_lutimes(fname, modtime, mod_nsec) == 0) | if (do_lutimes(fname, stp) == 0) |
break; |
break; |
|
#ifdef SUPPORT_FORCE_CHANGE |
|
if (force_change && errno == EPERM && try_a_force_change(fname, stp) == 0) |
|
break; |
|
#endif |
if (errno != ENOSYS) |
if (errno != ENOSYS) |
return -1; |
return -1; |
switch_step++; |
switch_step++; |
/* FALLTHROUGH */ |
|
#endif |
#endif |
|
|
#include "case_N.h" |
#include "case_N.h" |
switch_step++; |
switch_step++; |
if (preserve_times & PRESERVE_LINK_TIMES) { |
if (preserve_times & PRESERVE_LINK_TIMES) { |
preserve_times &= ~PRESERVE_LINK_TIMES; |
preserve_times &= ~PRESERVE_LINK_TIMES; |
if (S_ISLNK(mode)) | if (S_ISLNK(stp->st_mode)) |
return 1; |
return 1; |
} |
} |
/* FALLTHROUGH */ |
|
|
|
#include "case_N.h" |
#include "case_N.h" |
#ifdef HAVE_UTIMES |
#ifdef HAVE_UTIMES |
if (do_utimes(fname, modtime, mod_nsec) == 0) | if (do_utimes(fname, stp) == 0) |
break; |
break; |
#else |
#else |
if (do_utime(fname, modtime, mod_nsec) == 0) | if (do_utime(fname, stp) == 0) |
break; |
break; |
#endif |
#endif |
|
#ifdef SUPPORT_FORCE_CHANGE |
|
if (force_change && errno == EPERM && try_a_force_change(fname, stp) == 0) |
|
break; |
|
#endif |
|
|
return -1; |
return -1; |
} |
} |
Line 174 int set_modtime(const char *fname, time_t modtime, uin
|
Line 223 int set_modtime(const char *fname, time_t modtime, uin
|
/* Create any necessary directories in fname. Any missing directories are |
/* Create any necessary directories in fname. Any missing directories are |
* created with default permissions. Returns < 0 on error, or the number |
* created with default permissions. Returns < 0 on error, or the number |
* of directories created. */ |
* of directories created. */ |
int make_path(char *fname, int flags) | int make_path(char *fname, mode_t mode, int flags) |
{ |
{ |
char *end, *p; |
char *end, *p; |
int ret = 0; |
int ret = 0; |
Line 189 int make_path(char *fname, int flags)
|
Line 238 int make_path(char *fname, int flags)
|
|
|
if (flags & MKP_DROP_NAME) { |
if (flags & MKP_DROP_NAME) { |
end = strrchr(fname, '/'); |
end = strrchr(fname, '/'); |
if (!end) | if (!end || end == fname) |
return 0; |
return 0; |
*end = '\0'; |
*end = '\0'; |
} else |
} else |
Line 197 int make_path(char *fname, int flags)
|
Line 246 int make_path(char *fname, int flags)
|
|
|
/* Try to find an existing dir, starting from the deepest dir. */ |
/* Try to find an existing dir, starting from the deepest dir. */ |
for (p = end; ; ) { |
for (p = end; ; ) { |
if (do_mkdir(fname, ACCESSPERMS) == 0) { | if (dry_run) { |
| STRUCT_STAT st; |
| if (do_stat(fname, &st) == 0) { |
| if (S_ISDIR(st.st_mode)) |
| errno = EEXIST; |
| else |
| errno = ENOTDIR; |
| } |
| } else if (do_mkdir(fname, mode) == 0) { |
ret++; |
ret++; |
break; |
break; |
} |
} |
|
|
if (errno != ENOENT) { |
if (errno != ENOENT) { |
if (errno != EEXIST) | STRUCT_STAT st; |
| if (errno != EEXIST || (do_stat(fname, &st) == 0 && !S_ISDIR(st.st_mode))) |
ret = -ret - 1; |
ret = -ret - 1; |
break; |
break; |
} |
} |
while (1) { |
while (1) { |
if (p == fname) { |
if (p == fname) { |
ret = -ret - 1; | /* We got a relative path that doesn't exist, so assume that '.' |
| * is there and just break out and create the whole thing. */ |
| p = NULL; |
goto double_break; |
goto double_break; |
} |
} |
if (*--p == '/') { |
if (*--p == '/') { |
if (p == fname) { |
if (p == fname) { |
ret = -ret - 1; /* impossible... */ | /* We reached the "/" dir, which we assume is there. */ |
goto double_break; |
goto double_break; |
} |
} |
*p = '\0'; |
*p = '\0'; |
Line 225 int make_path(char *fname, int flags)
|
Line 286 int make_path(char *fname, int flags)
|
|
|
/* Make all the dirs that we didn't find on the way here. */ |
/* Make all the dirs that we didn't find on the way here. */ |
while (p != end) { |
while (p != end) { |
*p = '/'; | if (p) |
| *p = '/'; |
| else |
| p = fname; |
p += strlen(p); |
p += strlen(p); |
if (ret < 0) /* Skip mkdir on error, but keep restoring the path. */ |
if (ret < 0) /* Skip mkdir on error, but keep restoring the path. */ |
continue; |
continue; |
if (do_mkdir(fname, ACCESSPERMS) < 0) | if (do_mkdir(fname, mode) < 0) |
ret = -ret - 1; |
ret = -ret - 1; |
else |
else |
ret++; |
ret++; |
Line 307 int copy_file(const char *source, const char *dest, in
|
Line 371 int copy_file(const char *source, const char *dest, in
|
int ifd; |
int ifd; |
char buf[1024 * 8]; |
char buf[1024 * 8]; |
int len; /* Number of bytes read into `buf'. */ |
int len; /* Number of bytes read into `buf'. */ |
#ifdef PREALLOCATE_NEEDS_TRUNCATE | OFF_T prealloc_len = 0, offset = 0; |
OFF_T preallocated_len = 0, offset = 0; | |
#endif | |
|
|
if ((ifd = do_open(source, O_RDONLY, 0)) < 0) { |
if ((ifd = do_open(source, O_RDONLY, 0)) < 0) { |
int save_errno = errno; |
int save_errno = errno; |
Line 322 int copy_file(const char *source, const char *dest, in
|
Line 384 int copy_file(const char *source, const char *dest, in
|
if (robust_unlink(dest) && errno != ENOENT) { |
if (robust_unlink(dest) && errno != ENOENT) { |
int save_errno = errno; |
int save_errno = errno; |
rsyserr(FERROR_XFER, errno, "unlink %s", full_fname(dest)); |
rsyserr(FERROR_XFER, errno, "unlink %s", full_fname(dest)); |
|
close(ifd); |
errno = save_errno; |
errno = save_errno; |
return -1; |
return -1; |
} |
} |
Line 349 int copy_file(const char *source, const char *dest, in
|
Line 412 int copy_file(const char *source, const char *dest, in
|
if (do_fstat(ifd, &srcst) < 0) |
if (do_fstat(ifd, &srcst) < 0) |
rsyserr(FWARNING, errno, "fstat %s", full_fname(source)); |
rsyserr(FWARNING, errno, "fstat %s", full_fname(source)); |
else if (srcst.st_size > 0) { |
else if (srcst.st_size > 0) { |
if (do_fallocate(ofd, 0, srcst.st_size) == 0) { | prealloc_len = do_fallocate(ofd, 0, srcst.st_size); |
#ifdef PREALLOCATE_NEEDS_TRUNCATE | if (prealloc_len < 0) |
preallocated_len = srcst.st_size; | |
#endif | |
} else | |
rsyserr(FWARNING, errno, "do_fallocate %s", full_fname(dest)); |
rsyserr(FWARNING, errno, "do_fallocate %s", full_fname(dest)); |
} |
} |
} |
} |
Line 368 int copy_file(const char *source, const char *dest, in
|
Line 428 int copy_file(const char *source, const char *dest, in
|
errno = save_errno; |
errno = save_errno; |
return -1; |
return -1; |
} |
} |
#ifdef PREALLOCATE_NEEDS_TRUNCATE |
|
offset += len; |
offset += len; |
#endif |
|
} |
} |
|
|
if (len < 0) { |
if (len < 0) { |
Line 387 int copy_file(const char *source, const char *dest, in
|
Line 445 int copy_file(const char *source, const char *dest, in
|
full_fname(source)); |
full_fname(source)); |
} |
} |
|
|
#ifdef PREALLOCATE_NEEDS_TRUNCATE |
|
/* Source file might have shrunk since we fstatted it. |
/* Source file might have shrunk since we fstatted it. |
* Cut off any extra preallocated zeros from dest file. */ |
* Cut off any extra preallocated zeros from dest file. */ |
if (offset < preallocated_len && do_ftruncate(ofd, offset) < 0) { | if (offset < prealloc_len && do_ftruncate(ofd, offset) < 0) { |
/* If we fail to truncate, the dest file may be wrong, so we |
/* If we fail to truncate, the dest file may be wrong, so we |
* must trigger the "partial transfer" error. */ |
* must trigger the "partial transfer" error. */ |
rsyserr(FERROR_XFER, errno, "ftruncate %s", full_fname(dest)); |
rsyserr(FERROR_XFER, errno, "ftruncate %s", full_fname(dest)); |
} |
} |
#endif |
|
|
|
|
if (do_fsync && fsync(ofd) < 0) { |
|
rsyserr(FERROR, errno, "fsync failed on %s", |
|
full_fname(dest)); |
|
close(ofd); |
|
return -1; |
|
} |
|
|
if (close(ofd) < 0) { |
if (close(ofd) < 0) { |
int save_errno = errno; |
int save_errno = errno; |
rsyserr(FERROR_XFER, errno, "close failed on %s", |
rsyserr(FERROR_XFER, errno, "close failed on %s", |
Line 483 int robust_rename(const char *from, const char *to, co
|
Line 546 int robust_rename(const char *from, const char *to, co
|
{ |
{ |
int tries = 4; |
int tries = 4; |
|
|
|
/* A resumed in-place partial-dir transfer might call us with from and |
|
* to pointing to the same buf if the transfer failed yet again. */ |
|
if (from == to) |
|
return 0; |
|
|
while (tries--) { |
while (tries--) { |
if (do_rename(from, to) == 0) |
if (do_rename(from, to) == 0) |
return 0; |
return 0; |
Line 572 int lock_range(int fd, int offset, int len)
|
Line 640 int lock_range(int fd, int offset, int len)
|
} |
} |
|
|
#define ENSURE_MEMSPACE(buf, type, sz, req) \ |
#define ENSURE_MEMSPACE(buf, type, sz, req) \ |
if ((req) > sz && !(buf = realloc_array(buf, type, sz = MAX(sz * 2, req)))) \ | do { if ((req) > sz) buf = realloc_array(buf, type, sz = MAX(sz * 2, req)); } while(0) |
out_of_memory("glob_expand") | |
|
|
static inline void call_glob_match(const char *name, int len, int from_glob, |
static inline void call_glob_match(const char *name, int len, int from_glob, |
char *arg, int abpos, int fbpos); |
char *arg, int abpos, int fbpos); |
Line 675 static inline void call_glob_match(const char *name, i
|
Line 742 static inline void call_glob_match(const char *name, i
|
glob_match(arg, abpos, fbpos); |
glob_match(arg, abpos, fbpos); |
} else { |
} else { |
ENSURE_MEMSPACE(glob.argv, char *, glob.maxargs, glob.argc + 1); |
ENSURE_MEMSPACE(glob.argv, char *, glob.maxargs, glob.argc + 1); |
if (!(glob.argv[glob.argc++] = strdup(glob.arg_buf))) | glob.argv[glob.argc++] = strdup(glob.arg_buf); |
out_of_memory("glob_match"); | |
} |
} |
} |
} |
|
|
Line 700 int glob_expand(const char *arg, char ***argv_p, int *
|
Line 766 int glob_expand(const char *arg, char ***argv_p, int *
|
s = sanitize_path(NULL, arg, "", 0, SP_KEEP_DOT_DIRS); |
s = sanitize_path(NULL, arg, "", 0, SP_KEEP_DOT_DIRS); |
else { |
else { |
s = strdup(arg); |
s = strdup(arg); |
if (!s) | clean_fname(s, CFN_KEEP_DOT_DIRS | CFN_KEEP_TRAILING_SLASH | CFN_COLLAPSE_DOT_DOT_DIRS); |
out_of_memory("glob_expand"); | |
clean_fname(s, CFN_KEEP_DOT_DIRS | |
| CFN_KEEP_TRAILING_SLASH | |
| CFN_COLLAPSE_DOT_DOT_DIRS); | |
} |
} |
|
|
ENSURE_MEMSPACE(glob.arg_buf, char, glob.absize, MAXPATHLEN); |
ENSURE_MEMSPACE(glob.arg_buf, char, glob.absize, MAXPATHLEN); |
Line 753 void glob_expand_module(char *base1, char *arg, char *
|
Line 815 void glob_expand_module(char *base1, char *arg, char *
|
return; |
return; |
} |
} |
|
|
if (!(arg = strdup(arg))) | arg = strdup(arg); |
out_of_memory("glob_expand_module"); | |
|
|
if (asprintf(&base," %s/", base1) < 0) |
if (asprintf(&base," %s/", base1) < 0) |
out_of_memory("glob_expand_module"); |
out_of_memory("glob_expand_module"); |
Line 784 void strlower(char *s)
|
Line 845 void strlower(char *s)
|
} |
} |
} |
} |
|
|
|
/** |
|
* Split a string into tokens based (usually) on whitespace & commas. If the |
|
* string starts with a comma (after skipping any leading whitespace), then |
|
* splitting is done only on commas. No empty tokens are ever returned. */ |
|
char *conf_strtok(char *str) |
|
{ |
|
static int commas_only = 0; |
|
|
|
if (str) { |
|
while (isSpace(str)) str++; |
|
if (*str == ',') { |
|
commas_only = 1; |
|
str++; |
|
} else |
|
commas_only = 0; |
|
} |
|
|
|
while (commas_only) { |
|
char *end, *tok = strtok(str, ","); |
|
if (!tok) |
|
return NULL; |
|
/* Trim just leading and trailing whitespace. */ |
|
while (isSpace(tok)) |
|
tok++; |
|
end = tok + strlen(tok); |
|
while (end > tok && isSpace(end-1)) |
|
*--end = '\0'; |
|
if (*tok) |
|
return tok; |
|
str = NULL; |
|
} |
|
|
|
return strtok(str, " ,\t\r\n"); |
|
} |
|
|
/* Join strings p1 & p2 into "dest" with a guaranteed '/' between them. (If |
/* Join strings p1 & p2 into "dest" with a guaranteed '/' between them. (If |
* p1 ends with a '/', no extra '/' is inserted.) Returns the length of both |
* p1 ends with a '/', no extra '/' is inserted.) Returns the length of both |
* strings + 1 (if '/' was inserted), regardless of whether the null-terminated |
* strings + 1 (if '/' was inserted), regardless of whether the null-terminated |
Line 836 size_t stringjoin(char *dest, size_t destsize, ...)
|
Line 932 size_t stringjoin(char *dest, size_t destsize, ...)
|
return ret; |
return ret; |
} |
} |
|
|
|
/* Append formatted text at *dest_ptr up to a maximum of sz (like snprintf). |
|
* On success, advance *dest_ptr and return True; on overflow, return False. */ |
|
BOOL snappendf(char **dest_ptr, size_t sz, const char *format, ...) |
|
{ |
|
va_list ap; |
|
size_t len; |
|
|
|
va_start(ap, format); |
|
len = vsnprintf(*dest_ptr, sz, format, ap); |
|
va_end(ap); |
|
|
|
if (len >= sz) |
|
return False; |
|
else { |
|
*dest_ptr += len; |
|
return True; |
|
} |
|
} |
|
|
int count_dir_elements(const char *p) |
int count_dir_elements(const char *p) |
{ |
{ |
int cnt = 0, new_component = 1; |
int cnt = 0, new_component = 1; |
Line 858 int count_dir_elements(const char *p)
|
Line 973 int count_dir_elements(const char *p)
|
* CFN_KEEP_TRAILING_SLASH is flagged, and will also collapse ".." elements |
* CFN_KEEP_TRAILING_SLASH is flagged, and will also collapse ".." elements |
* (except at the start) if CFN_COLLAPSE_DOT_DOT_DIRS is flagged. If the |
* (except at the start) if CFN_COLLAPSE_DOT_DOT_DIRS is flagged. If the |
* resulting name would be empty, returns ".". */ |
* resulting name would be empty, returns ".". */ |
unsigned int clean_fname(char *name, int flags) | int clean_fname(char *name, int flags) |
{ |
{ |
char *limit = name - 1, *t = name, *f = name; |
char *limit = name - 1, *t = name, *f = name; |
int anchored; |
int anchored; |
Line 866 unsigned int clean_fname(char *name, int flags)
|
Line 981 unsigned int clean_fname(char *name, int flags)
|
if (!name) |
if (!name) |
return 0; |
return 0; |
|
|
|
#define DOT_IS_DOT_DOT_DIR(bp) (bp[1] == '.' && (bp[2] == '/' || !bp[2])) |
|
|
if ((anchored = *f == '/') != 0) { |
if ((anchored = *f == '/') != 0) { |
*t++ = *f++; |
*t++ = *f++; |
#ifdef __CYGWIN__ |
#ifdef __CYGWIN__ |
Line 878 unsigned int clean_fname(char *name, int flags)
|
Line 995 unsigned int clean_fname(char *name, int flags)
|
} else if (flags & CFN_KEEP_DOT_DIRS && *f == '.' && f[1] == '/') { |
} else if (flags & CFN_KEEP_DOT_DIRS && *f == '.' && f[1] == '/') { |
*t++ = *f++; |
*t++ = *f++; |
*t++ = *f++; |
*t++ = *f++; |
} | } else if (flags & CFN_REFUSE_DOT_DOT_DIRS && *f == '.' && DOT_IS_DOT_DOT_DIR(f)) |
| return -1; |
while (*f) { |
while (*f) { |
/* discard extra slashes */ |
/* discard extra slashes */ |
if (*f == '/') { |
if (*f == '/') { |
Line 894 unsigned int clean_fname(char *name, int flags)
|
Line 1012 unsigned int clean_fname(char *name, int flags)
|
if (f[1] == '\0' && flags & CFN_DROP_TRAILING_DOT_DIR) |
if (f[1] == '\0' && flags & CFN_DROP_TRAILING_DOT_DIR) |
break; |
break; |
/* collapse ".." dirs */ |
/* collapse ".." dirs */ |
if (flags & CFN_COLLAPSE_DOT_DOT_DIRS | if (flags & (CFN_COLLAPSE_DOT_DOT_DIRS|CFN_REFUSE_DOT_DOT_DIRS) && DOT_IS_DOT_DOT_DIR(f)) { |
&& f[1] == '.' && (f[2] == '/' || !f[2])) { | |
char *s = t - 1; |
char *s = t - 1; |
|
if (flags & CFN_REFUSE_DOT_DOT_DIRS) |
|
return -1; |
if (s == name && anchored) { |
if (s == name && anchored) { |
f += 2; |
f += 2; |
continue; |
continue; |
Line 919 unsigned int clean_fname(char *name, int flags)
|
Line 1038 unsigned int clean_fname(char *name, int flags)
|
*t++ = '.'; |
*t++ = '.'; |
*t = '\0'; |
*t = '\0'; |
|
|
|
#undef DOT_IS_DOT_DOT_DIR |
|
|
return t - name; |
return t - name; |
} |
} |
|
|
Line 944 unsigned int clean_fname(char *name, int flags)
|
Line 1065 unsigned int clean_fname(char *name, int flags)
|
* ALWAYS collapses ".." elements (except for those at the start of the |
* ALWAYS collapses ".." elements (except for those at the start of the |
* string up to "depth" deep). If the resulting name would be empty, |
* string up to "depth" deep). If the resulting name would be empty, |
* change it into a ".". */ |
* change it into a ".". */ |
char *sanitize_path(char *dest, const char *p, const char *rootdir, int depth, | char *sanitize_path(char *dest, const char *p, const char *rootdir, int depth, int flags) |
int flags) | |
{ |
{ |
char *start, *sanp; |
char *start, *sanp; |
int rlen = 0, drop_dot_dirs = !relative_paths || !(flags & SP_KEEP_DOT_DIRS); |
int rlen = 0, drop_dot_dirs = !relative_paths || !(flags & SP_KEEP_DOT_DIRS); |
|
|
if (dest != p) { |
if (dest != p) { |
int plen = strlen(p); | int plen = strlen(p); /* the path len INCLUDING any separating slash */ |
if (*p == '/') { |
if (*p == '/') { |
if (!rootdir) |
if (!rootdir) |
rootdir = module_dir; |
rootdir = module_dir; |
Line 959 char *sanitize_path(char *dest, const char *p, const c
|
Line 1079 char *sanitize_path(char *dest, const char *p, const c
|
depth = 0; |
depth = 0; |
p++; |
p++; |
} |
} |
if (dest) { | if (!dest) |
if (rlen + plen + 1 >= MAXPATHLEN) | dest = new_array(char, MAX(rlen + plen + 1, 2)); |
return NULL; | else if (rlen + plen + 1 >= MAXPATHLEN) |
} else if (!(dest = new_array(char, rlen + plen + 1))) | return NULL; |
out_of_memory("sanitize_path"); | if (rlen) { /* only true if p previously started with a slash */ |
if (rlen) { | |
memcpy(dest, rootdir, rlen); |
memcpy(dest, rootdir, rlen); |
if (rlen > 1) | if (rlen > 1) /* a rootdir of len 1 is "/", so this avoids a 2nd slash */ |
dest[rlen++] = '/'; |
dest[rlen++] = '/'; |
} |
} |
} |
} |
Line 1055 int change_dir(const char *dir, int set_path_only)
|
Line 1174 int change_dir(const char *dir, int set_path_only)
|
skipped_chdir = set_path_only; |
skipped_chdir = set_path_only; |
memcpy(curr_dir, dir, len + 1); |
memcpy(curr_dir, dir, len + 1); |
} else { |
} else { |
|
unsigned int save_dir_len = curr_dir_len; |
if (curr_dir_len + 1 + len >= sizeof curr_dir) { |
if (curr_dir_len + 1 + len >= sizeof curr_dir) { |
errno = ENAMETOOLONG; |
errno = ENAMETOOLONG; |
return 0; |
return 0; |
Line 1064 int change_dir(const char *dir, int set_path_only)
|
Line 1184 int change_dir(const char *dir, int set_path_only)
|
memcpy(curr_dir + curr_dir_len, dir, len + 1); |
memcpy(curr_dir + curr_dir_len, dir, len + 1); |
|
|
if (!set_path_only && chdir(curr_dir)) { |
if (!set_path_only && chdir(curr_dir)) { |
|
curr_dir_len = save_dir_len; |
curr_dir[curr_dir_len] = '\0'; |
curr_dir[curr_dir_len] = '\0'; |
return 0; |
return 0; |
} |
} |
Line 1095 char *normalize_path(char *path, BOOL force_newbuf, un
|
Line 1216 char *normalize_path(char *path, BOOL force_newbuf, un
|
return NULL; |
return NULL; |
curr_dir[curr_dir_len] = '/'; |
curr_dir[curr_dir_len] = '/'; |
memcpy(curr_dir + curr_dir_len + 1, path, len + 1); |
memcpy(curr_dir + curr_dir_len + 1, path, len + 1); |
if (!(path = strdup(curr_dir))) | path = strdup(curr_dir); |
out_of_memory("normalize_path"); | |
curr_dir[curr_dir_len] = '\0'; |
curr_dir[curr_dir_len] = '\0'; |
} else if (force_newbuf) { | } else if (force_newbuf) |
if (!(path = strdup(path))) | path = strdup(path); |
out_of_memory("normalize_path"); | |
} | |
|
|
len = clean_fname(path, CFN_COLLAPSE_DOT_DOT_DIRS | CFN_DROP_TRAILING_DOT_DIR); |
len = clean_fname(path, CFN_COLLAPSE_DOT_DOT_DIRS | CFN_DROP_TRAILING_DOT_DIR); |
|
|
Line 1111 char *normalize_path(char *path, BOOL force_newbuf, un
|
Line 1229 char *normalize_path(char *path, BOOL force_newbuf, un
|
return path; |
return path; |
} |
} |
|
|
|
/* We need to supply our own strcmp function for file list comparisons |
|
* to ensure that signed/unsigned usage is consistent between machines. */ |
|
int u_strcmp(const char *p1, const char *p2) |
|
{ |
|
for ( ; *p1; p1++, p2++) { |
|
if (*p1 != *p2) |
|
break; |
|
} |
|
|
|
return (int)*(uchar*)p1 - (int)*(uchar*)p2; |
|
} |
|
|
|
/* We need a memcmp function compares unsigned-byte values. */ |
|
int u_memcmp(const void *p1, const void *p2, size_t len) |
|
{ |
|
const uchar *u1 = p1; |
|
const uchar *u2 = p2; |
|
|
|
while (len--) { |
|
if (*u1 != *u2) |
|
return (int)*u1 - (int)*u2; |
|
} |
|
|
|
return 0; |
|
} |
|
|
/** |
/** |
* Return a quoted string with the full pathname of the indicated filename. |
* Return a quoted string with the full pathname of the indicated filename. |
* The string " (in MODNAME)" may also be appended. The returned pointer |
* The string " (in MODNAME)" may also be appended. The returned pointer |
Line 1204 int handle_partial_dir(const char *fname, int create)
|
Line 1348 int handle_partial_dir(const char *fname, int create)
|
} |
} |
statret = -1; |
statret = -1; |
} |
} |
if (statret < 0 && do_mkdir(dir, 0700) < 0) { | if (statret < 0 && make_path(dir, 0700, 0) < 0) { |
*fn = '/'; |
*fn = '/'; |
return 0; |
return 0; |
} |
} |
Line 1277 int unsafe_symlink(const char *dest, const char *src)
|
Line 1421 int unsafe_symlink(const char *dest, const char *src)
|
/* Return the date and time as a string. Some callers tweak returned buf. */ |
/* Return the date and time as a string. Some callers tweak returned buf. */ |
char *timestring(time_t t) |
char *timestring(time_t t) |
{ |
{ |
static char TimeBuf[200]; | static int ndx = 0; |
| static char buffers[4][20]; /* We support 4 simultaneous timestring results. */ |
| char *TimeBuf = buffers[ndx = (ndx + 1) % 4]; |
struct tm *tm = localtime(&t); |
struct tm *tm = localtime(&t); |
char *p; | int len = snprintf(TimeBuf, sizeof buffers[0], "%4d/%02d/%02d %02d:%02d:%02d", |
| (int)tm->tm_year + 1900, (int)tm->tm_mon + 1, (int)tm->tm_mday, |
| (int)tm->tm_hour, (int)tm->tm_min, (int)tm->tm_sec); |
| assert(len > 0); /* Silence gcc warning */ |
|
|
#ifdef HAVE_STRFTIME |
|
strftime(TimeBuf, sizeof TimeBuf - 1, "%Y/%m/%d %H:%M:%S", tm); |
|
#else |
|
strlcpy(TimeBuf, asctime(tm), sizeof TimeBuf); |
|
#endif |
|
|
|
if ((p = strchr(TimeBuf, '\n')) != NULL) |
|
*p = '\0'; |
|
|
|
return TimeBuf; |
return TimeBuf; |
} |
} |
|
|
/* Determine if two time_t values are equivalent (either exact, or in |
/* Determine if two time_t values are equivalent (either exact, or in |
* the modification timestamp window established by --modify-window). |
* the modification timestamp window established by --modify-window). |
* | * Returns 1 if the times the "same", or 0 if they are different. */ |
* @retval 0 if the times should be treated as the same | int same_time(time_t f1_sec, unsigned long f1_nsec, time_t f2_sec, unsigned long f2_nsec) |
* | |
* @retval +1 if the first is later | |
* | |
* @retval -1 if the 2nd is later | |
**/ | |
int cmp_time(time_t file1, time_t file2) | |
{ |
{ |
if (file2 > file1) { | if (modify_window == 0) |
if (file2 - file1 <= modify_window) | return f1_sec == f2_sec; |
return 0; | if (modify_window < 0) |
return -1; | return f1_sec == f2_sec && f1_nsec == f2_nsec; |
} | /* The nano seconds doesn't figure into these checks -- time windows don't care about that. */ |
if (file1 - file2 <= modify_window) | if (f2_sec > f1_sec) |
return 0; | return f2_sec - f1_sec <= modify_window; |
return 1; | return f1_sec - f2_sec <= modify_window; |
} |
} |
|
|
|
|
#ifdef __INSURE__XX |
#ifdef __INSURE__XX |
#include <dlfcn.h> |
#include <dlfcn.h> |
|
|
Line 1386 const char *find_filename_suffix(const char *fn, int f
|
Line 1519 const char *find_filename_suffix(const char *fn, int f
|
} else if (s_len == 5) { |
} else if (s_len == 5) { |
if (strcmp(s+1, "orig") == 0) |
if (strcmp(s+1, "orig") == 0) |
continue; |
continue; |
} else if (s_len > 2 && had_tilde | } else if (s_len > 2 && had_tilde && s[1] == '~' && isDigit(s + 2)) |
&& s[1] == '~' && isDigit(s + 2)) | |
continue; |
continue; |
*len_ptr = s_len; |
*len_ptr = s_len; |
suf = s; |
suf = s; |
Line 1398 const char *find_filename_suffix(const char *fn, int f
|
Line 1530 const char *find_filename_suffix(const char *fn, int f
|
if (!isDigit(s)) |
if (!isDigit(s)) |
return suf; |
return suf; |
} |
} |
/* An all-digit suffix may not be that signficant. */ | /* An all-digit suffix may not be that significant. */ |
s = suf; |
s = suf; |
} |
} |
|
|
Line 1462 uint32 fuzzy_distance(const char *s1, unsigned len1, c
|
Line 1594 uint32 fuzzy_distance(const char *s1, unsigned len1, c
|
#define BB_PER_SLOT_INTS (BB_SLOT_SIZE / 4) /* Number of int32s per slot */ |
#define BB_PER_SLOT_INTS (BB_SLOT_SIZE / 4) /* Number of int32s per slot */ |
|
|
struct bitbag { |
struct bitbag { |
uint32 **bits; | uint32 **bits; |
int slot_cnt; | int slot_cnt; |
}; |
}; |
|
|
struct bitbag *bitbag_create(int max_ndx) |
struct bitbag *bitbag_create(int max_ndx) |
Line 1471 struct bitbag *bitbag_create(int max_ndx)
|
Line 1603 struct bitbag *bitbag_create(int max_ndx)
|
struct bitbag *bb = new(struct bitbag); |
struct bitbag *bb = new(struct bitbag); |
bb->slot_cnt = (max_ndx + BB_PER_SLOT_BITS - 1) / BB_PER_SLOT_BITS; |
bb->slot_cnt = (max_ndx + BB_PER_SLOT_BITS - 1) / BB_PER_SLOT_BITS; |
|
|
if (!(bb->bits = (uint32**)calloc(bb->slot_cnt, sizeof (uint32*)))) | bb->bits = new_array0(uint32*, bb->slot_cnt); |
out_of_memory("bitbag_create"); | |
|
|
return bb; |
return bb; |
} |
} |
Line 1482 void bitbag_set_bit(struct bitbag *bb, int ndx)
|
Line 1613 void bitbag_set_bit(struct bitbag *bb, int ndx)
|
int slot = ndx / BB_PER_SLOT_BITS; |
int slot = ndx / BB_PER_SLOT_BITS; |
ndx %= BB_PER_SLOT_BITS; |
ndx %= BB_PER_SLOT_BITS; |
|
|
if (!bb->bits[slot]) { | if (!bb->bits[slot]) |
if (!(bb->bits[slot] = (uint32*)calloc(BB_PER_SLOT_INTS, 4))) | bb->bits[slot] = new_array0(uint32, BB_PER_SLOT_INTS); |
out_of_memory("bitbag_set_bit"); | |
} | |
|
|
bb->bits[slot][ndx/32] |= 1u << (ndx % 32); |
bb->bits[slot][ndx/32] |= 1u << (ndx % 32); |
} |
} |
Line 1553 void flist_ndx_push(flist_ndx_list *lp, int ndx)
|
Line 1682 void flist_ndx_push(flist_ndx_list *lp, int ndx)
|
{ |
{ |
struct flist_ndx_item *item; |
struct flist_ndx_item *item; |
|
|
if (!(item = new(struct flist_ndx_item))) | item = new(struct flist_ndx_item); |
out_of_memory("flist_ndx_push"); | |
item->next = NULL; |
item->next = NULL; |
item->ndx = ndx; |
item->ndx = ndx; |
if (lp->tail) |
if (lp->tail) |
Line 1582 int flist_ndx_pop(flist_ndx_list *lp)
|
Line 1710 int flist_ndx_pop(flist_ndx_list *lp)
|
return ndx; |
return ndx; |
} |
} |
|
|
void *expand_item_list(item_list *lp, size_t item_size, | /* Make sure there is room for one more item in the item list. If there |
const char *desc, int incr) | * is not, expand the list as indicated by the value of "incr": |
| * - if incr < 0 then increase the malloced size by -1 * incr |
| * - if incr >= 0 then either make the malloced size equal to "incr" |
| * or (if that's not large enough) double the malloced size |
| * After the size check, the list's count is incremented by 1 and a pointer |
| * to the "new" list item is returned. |
| */ |
| void *expand_item_list(item_list *lp, size_t item_size, const char *desc, int incr) |
{ |
{ |
/* First time through, 0 <= 0, so list is expanded. */ |
/* First time through, 0 <= 0, so list is expanded. */ |
if (lp->malloced <= lp->count) { |
if (lp->malloced <= lp->count) { |
void *new_ptr; |
void *new_ptr; |
size_t new_size = lp->malloced; | size_t expand_size; |
if (incr < 0) |
if (incr < 0) |
new_size += -incr; /* increase slowly */ | expand_size = -incr; /* increase slowly */ |
else if (new_size < (size_t)incr) | else if (lp->malloced < (size_t)incr) |
new_size += incr; | expand_size = incr - lp->malloced; |
| else if (lp->malloced) |
| expand_size = lp->malloced; /* double in size */ |
else |
else |
new_size *= 2; | expand_size = 1; |
if (new_size < lp->malloced) | if (SIZE_MAX/item_size - expand_size < lp->malloced) |
overflow_exit("expand_item_list"); |
overflow_exit("expand_item_list"); |
/* Using _realloc_array() lets us pass the size, not a type. */ | expand_size += lp->malloced; |
new_ptr = _realloc_array(lp->items, item_size, new_size); | new_ptr = realloc_buf(lp->items, expand_size * item_size); |
if (DEBUG_GTE(FLIST, 3)) { |
if (DEBUG_GTE(FLIST, 3)) { |
rprintf(FINFO, "[%s] expand %s to %s bytes, did%s move\n", |
rprintf(FINFO, "[%s] expand %s to %s bytes, did%s move\n", |
who_am_i(), desc, big_num(new_size * item_size), | who_am_i(), desc, big_num(expand_size * item_size), |
new_ptr == lp->items ? " not" : ""); |
new_ptr == lp->items ? " not" : ""); |
} |
} |
if (!new_ptr) |
|
out_of_memory("expand_item_list"); |
|
|
|
lp->items = new_ptr; |
lp->items = new_ptr; |
lp->malloced = new_size; | lp->malloced = expand_size; |
} |
} |
return (char*)lp->items + (lp->count++ * item_size); |
return (char*)lp->items + (lp->count++ * item_size); |
|
} |
|
|
|
/* This zeroing of memory won't be optimized away by the compiler. */ |
|
void force_memzero(void *buf, size_t len) |
|
{ |
|
volatile uchar *z = buf; |
|
while (len-- > 0) |
|
*z++ = '\0'; |
} |
} |