1#include "cache.h" 2 3/* 4 * Returns the length (on a path component basis) of the longest 5 * common prefix match of 'name_a' and 'name_b'. 6 */ 7static int longest_path_match(const char *name_a, int len_a, 8 const char *name_b, int len_b, 9 int *previous_slash) 10{ 11 int max_len, match_len = 0, match_len_prev = 0, i = 0; 12 13 max_len = len_a < len_b ? len_a : len_b; 14 while (i < max_len && name_a[i] == name_b[i]) { 15 if (name_a[i] == '/') { 16 match_len_prev = match_len; 17 match_len = i; 18 } 19 i++; 20 } 21 /* 22 * Is 'name_b' a substring of 'name_a', the other way around, 23 * or is 'name_a' and 'name_b' the exact same string? 24 */ 25 if (i >= max_len && ((len_a > len_b && name_a[len_b] == '/') || 26 (len_a < len_b && name_b[len_a] == '/') || 27 (len_a == len_b))) { 28 match_len_prev = match_len; 29 match_len = i; 30 } 31 *previous_slash = match_len_prev; 32 return match_len; 33} 34 35static struct cache_def { 36 char path[PATH_MAX + 1]; 37 int len; 38 int flags; 39 int track_flags; 40 int prefix_len_stat_func; 41} cache; 42 43static inline void reset_lstat_cache(void) 44{ 45 cache.path[0] = '\0'; 46 cache.len = 0; 47 cache.flags = 0; 48 /* 49 * The track_flags and prefix_len_stat_func members is only 50 * set by the safeguard rule inside lstat_cache() 51 */ 52} 53 54#define SWITCHES_BEFORE_WARNING 10 55static unsigned int cache_switches, number_of_warnings; 56static unsigned int current_cache_func, last_cache_func; 57static unsigned int total_calls; 58 59#define FL_DIR (1 << 0) 60#define FL_NOENT (1 << 1) 61#define FL_SYMLINK (1 << 2) 62#define FL_LSTATERR (1 << 3) 63#define FL_ERR (1 << 4) 64#define FL_FULLPATH (1 << 5) 65 66/* 67 * Check if name 'name' of length 'len' has a symlink leading 68 * component, or if the directory exists and is real, or not. 69 * 70 * To speed up the check, some information is allowed to be cached. 71 * This can be indicated by the 'track_flags' argument, which also can 72 * be used to indicate that we should check the full path. 73 * 74 * The 'prefix_len_stat_func' parameter can be used to set the length 75 * of the prefix, where the cache should use the stat() function 76 * instead of the lstat() function to test each path component. 77 */ 78static int lstat_cache(const char *name, int len, 79 int track_flags, int prefix_len_stat_func) 80{ 81 int match_len, last_slash, last_slash_dir, previous_slash; 82 int match_flags, ret_flags, save_flags, max_len, ret; 83 struct stat st; 84 85 total_calls++; 86 if (cache.track_flags != track_flags || 87 cache.prefix_len_stat_func != prefix_len_stat_func) { 88 /* 89 * As a safeguard rule we clear the cache if the 90 * values of track_flags and/or prefix_len_stat_func 91 * does not match with the last supplied values. 92 */ 93 reset_lstat_cache(); 94 cache.track_flags = track_flags; 95 cache.prefix_len_stat_func = prefix_len_stat_func; 96 match_len = last_slash = 0; 97 cache_switches++; 98 if (cache_switches > SWITCHES_BEFORE_WARNING) { 99 if (number_of_warnings < 10 || number_of_warnings % 1000 == 0) 100 printf("warning from %s:%d cache_switches:%u > %u "\ 101 "(current:%u last:%u total:%u)\n", 102 __FILE__, __LINE__, 103 cache_switches, SWITCHES_BEFORE_WARNING, 104 current_cache_func, last_cache_func, 105 total_calls); 106 number_of_warnings++; 107 } 108 } else { 109 /* 110 * Check to see if we have a match from the cache for 111 * the 2 "excluding" path types. 112 */ 113 match_len = last_slash = 114 longest_path_match(name, len, cache.path, cache.len, 115 &previous_slash); 116 match_flags = cache.flags & track_flags & (FL_NOENT|FL_SYMLINK); 117 if (match_flags && match_len == cache.len) 118 return match_flags; 119 /* 120 * If we now have match_len > 0, we would know that 121 * the matched part will always be a directory. 122 * 123 * Also, if we are tracking directories and 'name' is 124 * a substring of the cache on a path component basis, 125 * we can return immediately. 126 */ 127 match_flags = track_flags & FL_DIR; 128 if (match_flags && len == match_len) 129 return match_flags; 130 } 131 132 /* 133 * Okay, no match from the cache so far, so now we have to 134 * check the rest of the path components. 135 */ 136 ret_flags = FL_DIR; 137 last_slash_dir = last_slash; 138 max_len = len < PATH_MAX ? len : PATH_MAX; 139 while (match_len < max_len) { 140 do { 141 cache.path[match_len] = name[match_len]; 142 match_len++; 143 } while (match_len < max_len && name[match_len] != '/'); 144 if (match_len >= max_len && !(track_flags & FL_FULLPATH)) 145 break; 146 last_slash = match_len; 147 cache.path[last_slash] = '\0'; 148 149 if (last_slash <= prefix_len_stat_func) 150 ret = stat(cache.path, &st); 151 else 152 ret = lstat(cache.path, &st); 153 154 if (ret) { 155 ret_flags = FL_LSTATERR; 156 if (errno == ENOENT) 157 ret_flags |= FL_NOENT; 158 } else if (S_ISDIR(st.st_mode)) { 159 last_slash_dir = last_slash; 160 continue; 161 } else if (S_ISLNK(st.st_mode)) { 162 ret_flags = FL_SYMLINK; 163 } else { 164 ret_flags = FL_ERR; 165 } 166 break; 167 } 168 169 /* 170 * At the end update the cache. Note that max 3 different 171 * path types, FL_NOENT, FL_SYMLINK and FL_DIR, can be cached 172 * for the moment! 173 */ 174 save_flags = ret_flags & track_flags & (FL_NOENT|FL_SYMLINK); 175 if (save_flags && last_slash > 0 && last_slash <= PATH_MAX) { 176 cache.path[last_slash] = '\0'; 177 cache.len = last_slash; 178 cache.flags = save_flags; 179 } else if ((track_flags & FL_DIR) && 180 last_slash_dir > 0 && last_slash_dir <= PATH_MAX) { 181 /* 182 * We have a separate test for the directory case, 183 * since it could be that we have found a symlink or a 184 * non-existing directory and the track_flags says 185 * that we cannot cache this fact, so the cache would 186 * then have been left empty in this case. 187 * 188 * But if we are allowed to track real directories, we 189 * can still cache the path components before the last 190 * one (the found symlink or non-existing component). 191 */ 192 cache.path[last_slash_dir] = '\0'; 193 cache.len = last_slash_dir; 194 cache.flags = FL_DIR; 195 } else { 196 reset_lstat_cache(); 197 } 198 return ret_flags; 199} 200 201/* 202 * Invalidate the given 'name' from the cache, if 'name' matches 203 * completely with the cache. 204 */ 205void invalidate_lstat_cache(const char *name, int len) 206{ 207 int match_len, previous_slash; 208 209 match_len = longest_path_match(name, len, cache.path, cache.len, 210 &previous_slash); 211 if (len == match_len) { 212 if ((cache.track_flags & FL_DIR) && previous_slash > 0) { 213 cache.path[previous_slash] = '\0'; 214 cache.len = previous_slash; 215 cache.flags = FL_DIR; 216 } else 217 reset_lstat_cache(); 218 } 219} 220 221/* 222 * Completely clear the contents of the cache 223 */ 224void clear_lstat_cache(void) 225{ 226 reset_lstat_cache(); 227} 228 229#define USE_ONLY_LSTAT 0 230 231/* 232 * Return non-zero if path 'name' has a leading symlink component 233 */ 234int has_symlink_leading_path(const char *name, int len) 235{ 236 last_cache_func = current_cache_func; 237 current_cache_func = 1; 238 return lstat_cache(name, len, 239 FL_SYMLINK|FL_DIR, USE_ONLY_LSTAT) & 240 FL_SYMLINK; 241} 242 243/* 244 * Return non-zero if path 'name' has a leading symlink component or 245 * if some leading path component does not exists. 246 */ 247int has_symlink_or_noent_leading_path(const char *name, int len) 248{ 249 last_cache_func = current_cache_func; 250 current_cache_func = 2; 251 return lstat_cache(name, len, 252 FL_SYMLINK|FL_NOENT|FL_DIR, USE_ONLY_LSTAT) & 253 (FL_SYMLINK|FL_NOENT); 254} 255 256/* 257 * Return non-zero if all path components of 'name' exists as a 258 * directory. If prefix_len > 0, we will test with the stat() 259 * function instead of the lstat() function for a prefix length of 260 * 'prefix_len', thus we then allow for symlinks in the prefix part as 261 * long as those points to real existing directories. 262 */ 263int has_dirs_only_path(const char *name, int len, int prefix_len) 264{ 265 last_cache_func = current_cache_func; 266 current_cache_func = 3; 267 return lstat_cache(name, len, 268 FL_DIR|FL_FULLPATH, prefix_len) & 269 FL_DIR; 270} 271 272static struct removal_def { 273 char path[PATH_MAX]; 274 int len; 275} removal; 276 277static void do_remove_scheduled_dirs(int new_len) 278{ 279 while (removal.len > new_len) { 280 removal.path[removal.len] = '\0'; 281 if (rmdir(removal.path)) 282 break; 283 do { 284 removal.len--; 285 } while (removal.len > new_len && 286 removal.path[removal.len] != '/'); 287 } 288 removal.len = new_len; 289 return; 290} 291 292void schedule_dir_for_removal(const char *name, int len) 293{ 294 int match_len, last_slash, i, previous_slash; 295 296 match_len = last_slash = i = 297 longest_path_match(name, len, removal.path, removal.len, 298 &previous_slash); 299 /* Find last slash inside 'name' */ 300 while (i < len) { 301 if (name[i] == '/') 302 last_slash = i; 303 i++; 304 } 305 306 /* 307 * If we are about to go down the directory tree, we check if 308 * we must first go upwards the tree, such that we then can 309 * remove possible empty directories as we go upwards. 310 */ 311 if (match_len < last_slash && match_len < removal.len) 312 do_remove_scheduled_dirs(match_len); 313 /* 314 * If we go deeper down the directory tree, we only need to 315 * save the new path components as we go down. 316 */ 317 if (match_len < last_slash) { 318 memcpy(&removal.path[match_len], &name[match_len], 319 last_slash - match_len); 320 removal.len = last_slash; 321 } 322 return; 323} 324 325void remove_scheduled_dirs(void) 326{ 327 do_remove_scheduled_dirs(0); 328 return; 329}