lstat_cache(): small cleanup and optimisation
[git/spearce.git] / symlinks.c
blobae57e5603bb3e07f3d4c28204850913a9ba2b0ba
1 #include "cache.h"
3 static struct cache_def {
4 char path[PATH_MAX + 1];
5 int len;
6 int flags;
7 int track_flags;
8 int prefix_len_stat_func;
9 } cache;
12 * Returns the length (on a path component basis) of the longest
13 * common prefix match of 'name' and the cached path string.
15 static inline int longest_match_lstat_cache(int len, const char *name,
16 int *previous_slash)
18 int max_len, match_len = 0, match_len_prev = 0, i = 0;
20 max_len = len < cache.len ? len : cache.len;
21 while (i < max_len && name[i] == cache.path[i]) {
22 if (name[i] == '/') {
23 match_len_prev = match_len;
24 match_len = i;
26 i++;
29 * Is the cached path string a substring of 'name', is 'name'
30 * a substring of the cached path string, or is 'name' and the
31 * cached path string the exact same string?
33 if (i >= max_len && ((len > cache.len && name[cache.len] == '/') ||
34 (len < cache.len && cache.path[len] == '/') ||
35 (len == cache.len))) {
36 match_len_prev = match_len;
37 match_len = i;
39 *previous_slash = match_len_prev;
40 return match_len;
43 static inline void reset_lstat_cache(void)
45 cache.path[0] = '\0';
46 cache.len = 0;
47 cache.flags = 0;
49 * The track_flags and prefix_len_stat_func members is only
50 * set by the safeguard rule inside lstat_cache()
54 #define FL_DIR (1 << 0)
55 #define FL_NOENT (1 << 1)
56 #define FL_SYMLINK (1 << 2)
57 #define FL_LSTATERR (1 << 3)
58 #define FL_ERR (1 << 4)
59 #define FL_FULLPATH (1 << 5)
62 * Check if name 'name' of length 'len' has a symlink leading
63 * component, or if the directory exists and is real, or not.
65 * To speed up the check, some information is allowed to be cached.
66 * This can be indicated by the 'track_flags' argument, which also can
67 * be used to indicate that we should check the full path.
69 * The 'prefix_len_stat_func' parameter can be used to set the length
70 * of the prefix, where the cache should use the stat() function
71 * instead of the lstat() function to test each path component.
73 static int lstat_cache(int len, const char *name,
74 int track_flags, int prefix_len_stat_func)
76 int match_len, last_slash, last_slash_dir, previous_slash;
77 int match_flags, ret_flags, save_flags, max_len, ret;
78 struct stat st;
80 if (cache.track_flags != track_flags ||
81 cache.prefix_len_stat_func != prefix_len_stat_func) {
83 * As a safeguard rule we clear the cache if the
84 * values of track_flags and/or prefix_len_stat_func
85 * does not match with the last supplied values.
87 reset_lstat_cache();
88 cache.track_flags = track_flags;
89 cache.prefix_len_stat_func = prefix_len_stat_func;
90 match_len = last_slash = 0;
91 } else {
93 * Check to see if we have a match from the cache for
94 * the 2 "excluding" path types.
96 match_len = last_slash =
97 longest_match_lstat_cache(len, name, &previous_slash);
98 match_flags = cache.flags & track_flags & (FL_NOENT|FL_SYMLINK);
99 if (match_flags && match_len == cache.len)
100 return match_flags;
102 * If we now have match_len > 0, we would know that
103 * the matched part will always be a directory.
105 * Also, if we are tracking directories and 'name' is
106 * a substring of the cache on a path component basis,
107 * we can return immediately.
109 match_flags = track_flags & FL_DIR;
110 if (match_flags && len == match_len)
111 return match_flags;
115 * Okay, no match from the cache so far, so now we have to
116 * check the rest of the path components.
118 ret_flags = FL_DIR;
119 last_slash_dir = last_slash;
120 max_len = len < PATH_MAX ? len : PATH_MAX;
121 while (match_len < max_len) {
122 do {
123 cache.path[match_len] = name[match_len];
124 match_len++;
125 } while (match_len < max_len && name[match_len] != '/');
126 if (match_len >= max_len && !(track_flags & FL_FULLPATH))
127 break;
128 last_slash = match_len;
129 cache.path[last_slash] = '\0';
131 if (last_slash <= prefix_len_stat_func)
132 ret = stat(cache.path, &st);
133 else
134 ret = lstat(cache.path, &st);
136 if (ret) {
137 ret_flags = FL_LSTATERR;
138 if (errno == ENOENT)
139 ret_flags |= FL_NOENT;
140 } else if (S_ISDIR(st.st_mode)) {
141 last_slash_dir = last_slash;
142 continue;
143 } else if (S_ISLNK(st.st_mode)) {
144 ret_flags = FL_SYMLINK;
145 } else {
146 ret_flags = FL_ERR;
148 break;
152 * At the end update the cache. Note that max 3 different
153 * path types, FL_NOENT, FL_SYMLINK and FL_DIR, can be cached
154 * for the moment!
156 save_flags = ret_flags & track_flags & (FL_NOENT|FL_SYMLINK);
157 if (save_flags && last_slash > 0 && last_slash <= PATH_MAX) {
158 cache.path[last_slash] = '\0';
159 cache.len = last_slash;
160 cache.flags = save_flags;
161 } else if ((track_flags & FL_DIR) &&
162 last_slash_dir > 0 && last_slash_dir <= PATH_MAX) {
164 * We have a separate test for the directory case,
165 * since it could be that we have found a symlink or a
166 * non-existing directory and the track_flags says
167 * that we cannot cache this fact, so the cache would
168 * then have been left empty in this case.
170 * But if we are allowed to track real directories, we
171 * can still cache the path components before the last
172 * one (the found symlink or non-existing component).
174 cache.path[last_slash_dir] = '\0';
175 cache.len = last_slash_dir;
176 cache.flags = FL_DIR;
177 } else {
178 reset_lstat_cache();
180 return ret_flags;
184 * Invalidate the given 'name' from the cache, if 'name' matches
185 * completely with the cache.
187 void invalidate_lstat_cache(int len, const char *name)
189 int match_len, previous_slash;
191 match_len = longest_match_lstat_cache(len, name, &previous_slash);
192 if (len == match_len) {
193 if ((cache.track_flags & FL_DIR) && previous_slash > 0) {
194 cache.path[previous_slash] = '\0';
195 cache.len = previous_slash;
196 cache.flags = FL_DIR;
197 } else
198 reset_lstat_cache();
203 * Completely clear the contents of the cache
205 void clear_lstat_cache(void)
207 reset_lstat_cache();
210 #define USE_ONLY_LSTAT 0
213 * Return non-zero if path 'name' has a leading symlink component
215 int has_symlink_leading_path(int len, const char *name)
217 return lstat_cache(len, name,
218 FL_SYMLINK|FL_DIR, USE_ONLY_LSTAT) &
219 FL_SYMLINK;
223 * Return non-zero if path 'name' has a leading symlink component or
224 * if some leading path component does not exists.
226 int has_symlink_or_noent_leading_path(int len, const char *name)
228 return lstat_cache(len, name,
229 FL_SYMLINK|FL_NOENT|FL_DIR, USE_ONLY_LSTAT) &
230 (FL_SYMLINK|FL_NOENT);
234 * Return non-zero if all path components of 'name' exists as a
235 * directory. If prefix_len > 0, we will test with the stat()
236 * function instead of the lstat() function for a prefix length of
237 * 'prefix_len', thus we then allow for symlinks in the prefix part as
238 * long as those points to real existing directories.
240 int has_dirs_only_path(int len, const char *name, int prefix_len)
242 return lstat_cache(len, name,
243 FL_DIR|FL_FULLPATH, prefix_len) &
244 FL_DIR;