Makefile: clean up http-walker.o dependency rules
[gitweb.git] / entry.c
diff --git a/entry.c b/entry.c
index 1f53588a11aee0debef472b6cce09c8798a3e3af..06d24f14c6ba9401637aebfb11659ae747796c06 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -35,9 +35,9 @@ static void create_directories(const char *path, int path_len,
                 */
                if (mkdir(buf, 0777)) {
                        if (errno == EEXIST && state->force &&
-                           !unlink(buf) && !mkdir(buf, 0777))
+                           !unlink_or_warn(buf) && !mkdir(buf, 0777))
                                continue;
-                       die("cannot create directory at %s", buf);
+                       die_errno("cannot create directory at '%s'", buf);
                }
        }
        free(buf);
@@ -51,7 +51,7 @@ static void remove_subtree(const char *path)
        char *name;
 
        if (!dir)
-               die("cannot opendir %s (%s)", path, strerror(errno));
+               die_errno("cannot opendir '%s'", path);
        strcpy(pathbuf, path);
        name = pathbuf + strlen(path);
        *name++ = '/';
@@ -61,15 +61,15 @@ static void remove_subtree(const char *path)
                        continue;
                strcpy(name, de->d_name);
                if (lstat(pathbuf, &st))
-                       die("cannot lstat %s (%s)", pathbuf, strerror(errno));
+                       die_errno("cannot lstat '%s'", pathbuf);
                if (S_ISDIR(st.st_mode))
                        remove_subtree(pathbuf);
                else if (unlink(pathbuf))
-                       die("cannot unlink %s (%s)", pathbuf, strerror(errno));
+                       die_errno("cannot unlink '%s'", pathbuf);
        }
        closedir(dir);
        if (rmdir(path))
-               die("cannot rmdir %s (%s)", path, strerror(errno));
+               die_errno("cannot rmdir '%s'", path);
 }
 
 static int create_file(const char *path, unsigned int mode)
@@ -94,11 +94,12 @@ static void *read_blob_entry(struct cache_entry *ce, unsigned long *size)
 static int write_entry(struct cache_entry *ce, char *path, const struct checkout *state, int to_tempfile)
 {
        unsigned int ce_mode_s_ifmt = ce->ce_mode & S_IFMT;
-       int fd, ret;
+       int fd, ret, fstat_done = 0;
        char *new;
        struct strbuf buf = STRBUF_INIT;
        unsigned long size;
        size_t wrote, newsize = 0;
+       struct stat st;
 
        switch (ce_mode_s_ifmt) {
        case S_IFREG:
@@ -145,6 +146,12 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
                }
 
                wrote = write_in_full(fd, new, size);
+               /* use fstat() only when path == ce->name */
+               if (fstat_is_reliable() &&
+                   state->refresh_cache && !to_tempfile && !state->base_dir_len) {
+                       fstat(fd, &st);
+                       fstat_done = 1;
+               }
                close(fd);
                free(new);
                if (wrote != size)
@@ -161,13 +168,30 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
        }
 
        if (state->refresh_cache) {
-               struct stat st;
-               lstat(ce->name, &st);
+               if (!fstat_done)
+                       lstat(ce->name, &st);
                fill_stat_cache_info(ce, &st);
        }
        return 0;
 }
 
+/*
+ * This is like 'lstat()', except it refuses to follow symlinks
+ * in the path, after skipping "skiplen".
+ */
+int check_path(const char *path, int len, struct stat *st, int skiplen)
+{
+       const char *slash = path + len;
+
+       while (path < slash && *slash != '/')
+               slash--;
+       if (!has_dirs_only_path(path, slash - path, skiplen)) {
+               errno = ENOENT;
+               return -1;
+       }
+       return lstat(path, st);
+}
+
 int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath)
 {
        static char path[PATH_MAX + 1];
@@ -181,7 +205,7 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t
        strcpy(path + len, ce->name);
        len += ce_namelen(ce);
 
-       if (!lstat(path, &st)) {
+       if (!check_path(path, len, &st, state->base_dir_len)) {
                unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID);
                if (!changed)
                        return 0;