Attempt to avoid a problem with --copy-unsafe-links where a symlink was
[rsync/rsync.git] / util.c
diff --git a/util.c b/util.c
index bad2fae..4e745e5 100644 (file)
--- a/util.c
+++ b/util.c
@@ -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);
@@ -534,24 +542,32 @@ void kill_all(int sig)
        }
 }
 
-/** Turn a user name into a uid */
-int name_to_uid(const char *name, uid_t *uid_p)
+/* Parse a user name or (optionally) a number into a uid */
+int user_to_uid(const char *name, uid_t *uid_p, BOOL num_ok)
 {
        struct passwd *pass;
        if (!name || !*name)
                return 0;
+       if (num_ok && name[strspn(name, "0123456789")] == '\0') {
+               *uid_p = atol(name);
+               return 1;
+       }
        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)
+/* Parse a group name or (optionally) a number into a gid */
+int group_to_gid(const char *name, gid_t *gid_p, BOOL num_ok)
 {
        struct group *grp;
        if (!name || !*name)
                return 0;
+       if (num_ok && name[strspn(name, "0123456789")] == '\0') {
+               *gid_p = atol(name);
+               return 1;
+       }
        if (!(grp = getgrnam(name)))
                return 0;
        *gid_p = grp->gr_gid;
@@ -1208,6 +1224,31 @@ int handle_partial_dir(const char *fname, int create)
        return 1;
 }
 
+/* Computes the depth of the given path, excluding any basename.  This
+ * assumes that the path does not walk through any symlinks.
+ * 
+ * For use with unsafe_symlink. */
+int path_depth(const char *path)
+{
+       const char *name, *slash;
+       int depth = 0;
+
+       for (name = path; (slash = strchr(name, '/')) != 0; name = slash+1) {
+               /* ".." segment starts the count over.  "." segment is ignored. */
+               if (strncmp(name, "./", 2) == 0)
+                       ;
+               else if (strncmp(name, "../", 3) == 0)
+                       depth = 0;
+               else
+                       depth++;
+               while (slash[1] == '/') slash++; /* just in case path isn't clean */
+       }
+       if (strcmp(name, "..") == 0)
+               depth = 0;
+
+       return depth;
+}
+
 /* 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
@@ -1224,47 +1265,35 @@ int handle_partial_dir(const char *fname, int create)
  *
  * "dest" is the target of the symlink in question.
  *
- * "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)
+ * "depth" is the depth of the symlink inside the current directory
+ * tree.  It can be computed with "path_depth". */
+int unsafe_symlink(const char *dest, int depth)
 {
        const char *name, *slash;
-       int depth = 0;
 
        /* all absolute and null symlinks are unsafe */
        if (!dest || !*dest || *dest == '/')
                return 1;
 
-       /* find out what our safety margin is */
-       for (name = src; (slash = strchr(name, '/')) != 0; name = slash+1) {
-               /* ".." 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 (*name == '.' && name[1] == '.' && name[2] == '\0')
-               depth = 0;
-
        for (name = dest; (slash = strchr(name, '/')) != 0; name = slash+1) {
-               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;
-                       }
+               if (strncmp(name, "./", 2) == 0)
+                       ;
+               else 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
+                       /* XXX: We could be walking through another symlink! */
                        depth++;
                while (slash[1] == '/') slash++;
        }
-       if (*name == '.' && name[1] == '.' && name[2] == '\0')
-               depth--;
+       if (strcmp(name, "..") == 0) {
+               if (--depth < 0)
+                       return 1;
+       }
 
-       return depth < 0;
+       return 0;
 }
 
 /* Return the date and time as a string.  Some callers tweak returned buf. */