X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/e0c572c5c691da243a3187f440daab739c0b3412..9270e88d76d3ea9e94d48977fbea57b997545577:/util.c diff --git a/util.c b/util.c index 6b075d0a..b01e64d8 100644 --- a/util.c +++ b/util.c @@ -4,7 +4,7 @@ * Copyright (C) 1996-2000 Andrew Tridgell * Copyright (C) 1996 Paul Mackerras * Copyright (C) 2001, 2002 Martin Pool - * 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 @@ -34,7 +34,7 @@ extern char *module_dir; extern unsigned int module_dirlen; extern mode_t orig_umask; extern char *partial_dir; -extern struct filter_list_struct daemon_filter_list; +extern filter_rule_list daemon_filter_list; int sanitize_paths = 0; @@ -123,9 +123,9 @@ NORETURN void overflow_exit(const char *str) exit_cleanup(RERR_MALLOC); } -int set_modtime(const char *fname, time_t modtime, mode_t mode) +int set_modtime(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode) { -#if !defined HAVE_LUTIMES || !defined HAVE_UTIMES +#ifndef CAN_SET_SYMLINK_TIMES if (S_ISLNK(mode)) return 1; #endif @@ -140,20 +140,28 @@ int set_modtime(const char *fname, time_t modtime, mode_t mode) return 0; { -#ifdef HAVE_UTIMES +#ifdef HAVE_UTIMENSAT + struct timespec t[2]; + t[0].tv_sec = 0; + t[0].tv_nsec = UTIME_NOW; + t[1].tv_sec = modtime; + t[1].tv_nsec = mod_nsec; + if (utimensat(AT_FDCWD, fname, t, AT_SYMLINK_NOFOLLOW) < 0) + return S_ISLNK(mode) && errno == ENOSYS ? 1 : -1; + return 0; +#elif defined HAVE_UTIMES || defined HAVE_LUTIMES struct timeval t[2]; t[0].tv_sec = time(NULL); t[0].tv_usec = 0; t[1].tv_sec = modtime; - t[1].tv_usec = 0; + t[1].tv_usec = mod_nsec / 1000; # ifdef HAVE_LUTIMES - if (S_ISLNK(mode)) { - if (lutimes(fname, t) < 0) - return errno == ENOSYS ? 1 : -1; - return 0; - } -# endif + if (lutimes(fname, t) < 0) + return S_ISLNK(mode) && errno == ENOSYS ? 1 : -1; + return 0; +# else return utimes(fname, t); +# endif #elif defined HAVE_STRUCT_UTIMBUF struct utimbuf tbuf; tbuf.actime = time(NULL); @@ -185,27 +193,76 @@ int mkdir_defmode(char *fname) } /* Create any necessary directories in fname. Any missing directories are - * created with default permissions. */ -int create_directory_path(char *fname) + * created with default permissions. Returns < 0 on error, or the number + * of directories created. */ +int make_path(char *fname, int flags) { - char *p; + char *end, *p; int ret = 0; - while (*fname == '/') - fname++; - while (strncmp(fname, "./", 2) == 0) + if (flags & MKP_SKIP_SLASH) { + while (*fname == '/') + fname++; + } + + while (*fname == '.' && fname[1] == '/') fname += 2; - umask(orig_umask); - p = fname; - while ((p = strchr(p,'/')) != NULL) { - *p = '\0'; - if (do_mkdir(fname, ACCESSPERMS) < 0 && errno != EEXIST) - ret = -1; - *p++ = '/'; + if (flags & MKP_DROP_NAME) { + end = strrchr(fname, '/'); + if (!end) + return 0; + *end = '\0'; + } else + end = fname + strlen(fname); + + umask(orig_umask); /* NOTE: don't return before setting this back to 0! */ + + /* Try to find an existing dir, starting from the deepest dir. */ + for (p = end; ; ) { + if (do_mkdir(fname, ACCESSPERMS) == 0) { + ret++; + break; + } + if (errno != ENOENT) { + if (errno != EEXIST) + ret = -ret - 1; + break; + } + while (1) { + if (p == fname) { + ret = -ret - 1; + goto double_break; + } + if (*--p == '/') { + if (p == fname) { + ret = -ret - 1; /* impossible... */ + goto double_break; + } + *p = '\0'; + break; + } + } + } + double_break: + + /* Make all the dirs that we didn't find on the way here. */ + while (p != end) { + *p = '/'; + p += strlen(p); + if (ret < 0) /* Skip mkdir on error, but keep restoring the path. */ + continue; + if (do_mkdir(fname, ACCESSPERMS) < 0) + ret = -ret - 1; + else + ret++; } + umask(0); + if (flags & MKP_DROP_NAME) + *end = '/'; + return ret; } @@ -270,8 +327,7 @@ static int safe_read(int desc, char *ptr, size_t len) * * This is used in conjunction with the --temp-dir, --backup, and * --copy-dest options. */ -int copy_file(const char *source, const char *dest, int ofd, - mode_t mode, int create_bak_dir) +int copy_file(const char *source, const char *dest, int ofd, mode_t mode) { int ifd; char buf[1024 * 8]; @@ -293,19 +349,11 @@ int copy_file(const char *source, const char *dest, int ofd, } 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; - } + int save_errno = errno; + rsyserr(FERROR_XFER, save_errno, "open %s", full_fname(dest)); + close(ifd); + errno = save_errno; + return -1; } } @@ -440,7 +488,7 @@ int robust_rename(const char *from, const char *to, const char *partialptr, return -2; to = partialptr; } - if (copy_file(from, to, -1, mode, 0) != 0) + if (copy_file(from, to, -1, mode) != 0) return -2; do_unlink(from); return 1; @@ -494,30 +542,6 @@ void kill_all(int sig) } } -/** Turn a user name into a uid */ -int name_to_uid(const char *name, uid_t *uid_p) -{ - struct passwd *pass; - if (!name || !*name) - return 0; - if (!(pass = getpwnam(name))) - return 0; - *uid_p = pass->pw_uid; - return 1; -} - -/** Turn a group name into a gid */ -int name_to_gid(const char *name, gid_t *gid_p) -{ - struct group *grp; - if (!name || !*name) - return 0; - if (!(grp = getgrnam(name))) - return 0; - *gid_p = grp->gr_gid; - return 1; -} - /** Lock a byte range in a open file */ int lock_range(int fd, int offset, int len) { @@ -806,7 +830,8 @@ int count_dir_elements(const char *p) 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 @@ -821,9 +846,16 @@ unsigned int clean_fname(char *name, int flags) 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++; } @@ -979,7 +1011,10 @@ int change_dir(const char *dir, int set_path_only) 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); } @@ -1157,12 +1192,13 @@ int handle_partial_dir(const char *fname, int create) return 1; } -/** - * Determine if a symlink points outside the current directory tree. +/* Determine if a symlink points outside the current directory tree. * This is considered "unsafe" because e.g. when mirroring somebody * else's machine it might allow them to establish a symlink to * /etc/passwd, and then read it through a web server. * + * Returns 1 if unsafe, 0 if safe. + * * Null symlinks and absolute symlinks are always unsafe. * * Basically here we are concerned with symlinks whose target contains @@ -1170,17 +1206,11 @@ int handle_partial_dir(const char *fname, int create) * transferred directory. We are not allowed to go back up and * reenter. * - * @param dest Target of the symlink in question. - * - * @param src Top source directory currently applicable. Basically this - * is the first parameter to rsync in a simple invocation, but it's - * modified by flist.c in slightly complex ways. + * "dest" is the target of the symlink in question. * - * @retval True if unsafe - * @retval False is unsafe - * - * @sa t_unsafe.c - **/ + * "src" is the top source directory currently applicable at the level + * of the referenced symlink. This is usually the symlink's full path + * (including its name), as referenced from the root of the transfer. */ int unsafe_symlink(const char *dest, const char *src) { const char *name, *slash; @@ -1192,33 +1222,33 @@ int unsafe_symlink(const char *dest, const char *src) /* find out what our safety margin is */ for (name = src; (slash = strchr(name, '/')) != 0; name = slash+1) { - if (strncmp(name, "../", 3) == 0) { - depth = 0; - } else if (strncmp(name, "./", 2) == 0) { - /* nothing */ - } else { + /* ".." segment starts the count over. "." segment is ignored. */ + if (*name == '.' && (name[1] == '/' || (name[1] == '.' && name[2] == '/'))) { + if (name[1] == '.') + depth = 0; + } else depth++; - } + while (slash[1] == '/') slash++; /* just in case src isn't clean */ } - if (strcmp(name, "..") == 0) + if (*name == '.' && name[1] == '.' && name[2] == '\0') depth = 0; for (name = dest; (slash = strchr(name, '/')) != 0; name = slash+1) { - if (strncmp(name, "../", 3) == 0) { - /* if at any point we go outside the current directory - then stop - it is unsafe */ - if (--depth < 0) - return 1; - } else if (strncmp(name, "./", 2) == 0) { - /* nothing */ - } else { + if (*name == '.' && (name[1] == '/' || (name[1] == '.' && name[2] == '/'))) { + if (name[1] == '.') { + /* if at any point we go outside the current directory + then stop - it is unsafe */ + if (--depth < 0) + return 1; + } + } else depth++; - } + while (slash[1] == '/') slash++; } - if (strcmp(name, "..") == 0) + if (*name == '.' && name[1] == '.' && name[2] == '\0') depth--; - return (depth < 0); + return depth < 0; } /* Return the date and time as a string. Some callers tweak returned buf. */