X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/a71b436858ba82aa171bd4b9f7deccd69a7541b1..refs/heads/wip/copy-unsafe-links-double:/util.c diff --git a/util.c b/util.c index 6019fccb..4e745e5a 100644 --- 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. */