X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=symlinks.c;h=290036744bcfd390f91e75db49bf620bb12c12ea;hb=fdec2eb8ebbf995a77e2ab9971565c792b52944a;hp=1d6b35b858020300f502e2a9341b82d4fa8a61fd;hpb=77f143bf3e218857ec8e5244d7e862e8e0c1a041;p=git.git diff --git a/symlinks.c b/symlinks.c index 1d6b35b85..290036744 100644 --- a/symlinks.c +++ b/symlinks.c @@ -32,19 +32,13 @@ static int longest_path_match(const char *name_a, int len_a, return match_len; } -static struct cache_def { - char path[PATH_MAX + 1]; - int len; - int flags; - int track_flags; - int prefix_len_stat_func; -} cache; +static struct cache_def default_cache; -static inline void reset_lstat_cache(void) +static inline void reset_lstat_cache(struct cache_def *cache) { - cache.path[0] = '\0'; - cache.len = 0; - cache.flags = 0; + cache->path[0] = '\0'; + cache->len = 0; + cache->flags = 0; /* * The track_flags and prefix_len_stat_func members is only * set by the safeguard rule inside lstat_cache() @@ -70,23 +64,25 @@ static inline void reset_lstat_cache(void) * of the prefix, where the cache should use the stat() function * instead of the lstat() function to test each path component. */ -static int lstat_cache(const char *name, int len, - int track_flags, int prefix_len_stat_func) +static int lstat_cache_matchlen(struct cache_def *cache, + const char *name, int len, + int *ret_flags, int track_flags, + int prefix_len_stat_func) { int match_len, last_slash, last_slash_dir, previous_slash; - int match_flags, ret_flags, save_flags, max_len, ret; + int save_flags, max_len, ret; struct stat st; - if (cache.track_flags != track_flags || - cache.prefix_len_stat_func != prefix_len_stat_func) { + if (cache->track_flags != track_flags || + cache->prefix_len_stat_func != prefix_len_stat_func) { /* * As a safeguard rule we clear the cache if the * values of track_flags and/or prefix_len_stat_func * does not match with the last supplied values. */ - reset_lstat_cache(); - cache.track_flags = track_flags; - cache.prefix_len_stat_func = prefix_len_stat_func; + reset_lstat_cache(cache); + cache->track_flags = track_flags; + cache->prefix_len_stat_func = prefix_len_stat_func; match_len = last_slash = 0; } else { /* @@ -94,11 +90,15 @@ static int lstat_cache(const char *name, int len, * the 2 "excluding" path types. */ match_len = last_slash = - longest_path_match(name, len, cache.path, cache.len, + longest_path_match(name, len, cache->path, cache->len, &previous_slash); - match_flags = cache.flags & track_flags & (FL_NOENT|FL_SYMLINK); - if (match_flags && match_len == cache.len) - return match_flags; + *ret_flags = cache->flags & track_flags & (FL_NOENT|FL_SYMLINK); + + if (!(track_flags & FL_FULLPATH) && match_len == len) + match_len = last_slash = previous_slash; + + if (*ret_flags && match_len == cache->len) + return match_len; /* * If we now have match_len > 0, we would know that * the matched part will always be a directory. @@ -107,44 +107,44 @@ static int lstat_cache(const char *name, int len, * a substring of the cache on a path component basis, * we can return immediately. */ - match_flags = track_flags & FL_DIR; - if (match_flags && len == match_len) - return match_flags; + *ret_flags = track_flags & FL_DIR; + if (*ret_flags && len == match_len) + return match_len; } /* * Okay, no match from the cache so far, so now we have to * check the rest of the path components. */ - ret_flags = FL_DIR; + *ret_flags = FL_DIR; last_slash_dir = last_slash; max_len = len < PATH_MAX ? len : PATH_MAX; while (match_len < max_len) { do { - cache.path[match_len] = name[match_len]; + cache->path[match_len] = name[match_len]; match_len++; } while (match_len < max_len && name[match_len] != '/'); if (match_len >= max_len && !(track_flags & FL_FULLPATH)) break; last_slash = match_len; - cache.path[last_slash] = '\0'; + cache->path[last_slash] = '\0'; if (last_slash <= prefix_len_stat_func) - ret = stat(cache.path, &st); + ret = stat(cache->path, &st); else - ret = lstat(cache.path, &st); + ret = lstat(cache->path, &st); if (ret) { - ret_flags = FL_LSTATERR; + *ret_flags = FL_LSTATERR; if (errno == ENOENT) - ret_flags |= FL_NOENT; + *ret_flags |= FL_NOENT; } else if (S_ISDIR(st.st_mode)) { last_slash_dir = last_slash; continue; } else if (S_ISLNK(st.st_mode)) { - ret_flags = FL_SYMLINK; + *ret_flags = FL_SYMLINK; } else { - ret_flags = FL_ERR; + *ret_flags = FL_ERR; } break; } @@ -154,11 +154,11 @@ static int lstat_cache(const char *name, int len, * path types, FL_NOENT, FL_SYMLINK and FL_DIR, can be cached * for the moment! */ - save_flags = ret_flags & track_flags & (FL_NOENT|FL_SYMLINK); + save_flags = *ret_flags & track_flags & (FL_NOENT|FL_SYMLINK); if (save_flags && last_slash > 0 && last_slash <= PATH_MAX) { - cache.path[last_slash] = '\0'; - cache.len = last_slash; - cache.flags = save_flags; + cache->path[last_slash] = '\0'; + cache->len = last_slash; + cache->flags = save_flags; } else if ((track_flags & FL_DIR) && last_slash_dir > 0 && last_slash_dir <= PATH_MAX) { /* @@ -172,64 +172,76 @@ static int lstat_cache(const char *name, int len, * can still cache the path components before the last * one (the found symlink or non-existing component). */ - cache.path[last_slash_dir] = '\0'; - cache.len = last_slash_dir; - cache.flags = FL_DIR; + cache->path[last_slash_dir] = '\0'; + cache->len = last_slash_dir; + cache->flags = FL_DIR; } else { - reset_lstat_cache(); + reset_lstat_cache(cache); } - return ret_flags; + return match_len; } -/* - * Invalidate the given 'name' from the cache, if 'name' matches - * completely with the cache. - */ -void invalidate_lstat_cache(const char *name, int len) +static int lstat_cache(struct cache_def *cache, const char *name, int len, + int track_flags, int prefix_len_stat_func) { - int match_len, previous_slash; - - match_len = longest_path_match(name, len, cache.path, cache.len, - &previous_slash); - if (len == match_len) { - if ((cache.track_flags & FL_DIR) && previous_slash > 0) { - cache.path[previous_slash] = '\0'; - cache.len = previous_slash; - cache.flags = FL_DIR; - } else - reset_lstat_cache(); - } + int flags; + (void)lstat_cache_matchlen(cache, name, len, &flags, track_flags, + prefix_len_stat_func); + return flags; } +#define USE_ONLY_LSTAT 0 + /* - * Completely clear the contents of the cache + * Return non-zero if path 'name' has a leading symlink component */ -void clear_lstat_cache(void) +int threaded_has_symlink_leading_path(struct cache_def *cache, const char *name, int len) { - reset_lstat_cache(); + return lstat_cache(cache, name, len, FL_SYMLINK|FL_DIR, USE_ONLY_LSTAT) & FL_SYMLINK; } -#define USE_ONLY_LSTAT 0 - /* * Return non-zero if path 'name' has a leading symlink component */ int has_symlink_leading_path(const char *name, int len) { - return lstat_cache(name, len, - FL_SYMLINK|FL_DIR, USE_ONLY_LSTAT) & - FL_SYMLINK; + return threaded_has_symlink_leading_path(&default_cache, name, len); +} + +/* + * Return zero if path 'name' has a leading symlink component or + * if some leading path component does not exists. + * + * Return -1 if leading path exists and is a directory. + * + * Return path length if leading path exists and is neither a + * directory nor a symlink. + */ +int check_leading_path(const char *name, int len) +{ + return threaded_check_leading_path(&default_cache, name, len); } /* - * Return non-zero if path 'name' has a leading symlink component or + * Return zero if path 'name' has a leading symlink component or * if some leading path component does not exists. + * + * Return -1 if leading path exists and is a directory. + * + * Return path length if leading path exists and is neither a + * directory nor a symlink. */ -int has_symlink_or_noent_leading_path(const char *name, int len) +int threaded_check_leading_path(struct cache_def *cache, const char *name, int len) { - return lstat_cache(name, len, - FL_SYMLINK|FL_NOENT|FL_DIR, USE_ONLY_LSTAT) & - (FL_SYMLINK|FL_NOENT); + int flags; + int match_len = lstat_cache_matchlen(cache, name, len, &flags, + FL_SYMLINK|FL_NOENT|FL_DIR, USE_ONLY_LSTAT); + if (flags & FL_NOENT) + return 0; + else if (flags & FL_DIR) + return -1; + else + return match_len; } /* @@ -241,7 +253,19 @@ int has_symlink_or_noent_leading_path(const char *name, int len) */ int has_dirs_only_path(const char *name, int len, int prefix_len) { - return lstat_cache(name, len, + return threaded_has_dirs_only_path(&default_cache, name, len, prefix_len); +} + +/* + * Return non-zero if all path components of 'name' exists as a + * directory. If prefix_len > 0, we will test with the stat() + * function instead of the lstat() function for a prefix length of + * 'prefix_len', thus we then allow for symlinks in the prefix part as + * long as those points to real existing directories. + */ +int threaded_has_dirs_only_path(struct cache_def *cache, const char *name, int len, int prefix_len) +{ + return lstat_cache(cache, name, len, FL_DIR|FL_FULLPATH, prefix_len) & FL_DIR; } @@ -263,7 +287,6 @@ static void do_remove_scheduled_dirs(int new_len) removal.path[removal.len] != '/'); } removal.len = new_len; - return; } void schedule_dir_for_removal(const char *name, int len) @@ -296,11 +319,9 @@ void schedule_dir_for_removal(const char *name, int len) last_slash - match_len); removal.len = last_slash; } - return; } void remove_scheduled_dirs(void) { do_remove_scheduled_dirs(0); - return; }