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 6019fcc..4e745e5 100644 (file)
--- a/util.c
+++ b/util.c
@@ -1224,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
@@ -1240,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. */