traverse_trees(): handle D/F conflict case sanely
[gitweb.git] / path.c
diff --git a/path.c b/path.c
index 2470f78d3971acdf6a716e6d743e4fdaa1fc8a55..2ec950b27f1c3e4919ce7d1696360c5a49abb724 100644 (file)
--- a/path.c
+++ b/path.c
@@ -235,10 +235,15 @@ char *expand_user_path(const char *path)
        if (path[0] == '~') {
                const char *username = path + 1;
                size_t username_len = first_slash - username;
-               struct passwd *pw = getpw_str(username, username_len);
-               if (!pw)
-                       goto return_null;
-               strbuf_add(&user_path, pw->pw_dir, strlen(pw->pw_dir));
+               if (username_len == 0) {
+                       const char *home = getenv("HOME");
+                       strbuf_add(&user_path, home, strlen(home));
+               } else {
+                       struct passwd *pw = getpw_str(username, username_len);
+                       if (!pw)
+                               goto return_null;
+                       strbuf_add(&user_path, pw->pw_dir, strlen(pw->pw_dir));
+               }
                to_copy = first_slash;
        }
        strbuf_add(&user_path, to_copy, strlen(to_copy));
@@ -576,3 +581,50 @@ char *strip_path_suffix(const char *path, const char *suffix)
                return NULL;
        return xstrndup(path, chomp_trailing_dir_sep(path, path_len));
 }
+
+int daemon_avoid_alias(const char *p)
+{
+       int sl, ndot;
+
+       /*
+        * This resurrects the belts and suspenders paranoia check by HPA
+        * done in <435560F7.4080006@zytor.com> thread, now enter_repo()
+        * does not do getcwd() based path canonicalizations.
+        *
+        * sl becomes true immediately after seeing '/' and continues to
+        * be true as long as dots continue after that without intervening
+        * non-dot character.
+        */
+       if (!p || (*p != '/' && *p != '~'))
+               return -1;
+       sl = 1; ndot = 0;
+       p++;
+
+       while (1) {
+               char ch = *p++;
+               if (sl) {
+                       if (ch == '.')
+                               ndot++;
+                       else if (ch == '/') {
+                               if (ndot < 3)
+                                       /* reject //, /./ and /../ */
+                                       return -1;
+                               ndot = 0;
+                       }
+                       else if (ch == 0) {
+                               if (0 < ndot && ndot < 3)
+                                       /* reject /.$ and /..$ */
+                                       return -1;
+                               return 0;
+                       }
+                       else
+                               sl = ndot = 0;
+               }
+               else if (ch == 0)
+                       return 0;
+               else if (ch == '/') {
+                       sl = 1;
+                       ndot = 0;
+               }
+       }
+}