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 intlongest_path_match(const char*name_a,int len_a, 8const char*name_b,int len_b, 9int*previous_slash) 10{ 11int max_len, match_len =0, match_len_prev =0, i =0; 12 13 max_len = len_a < len_b ? len_a : len_b; 14while(i < max_len && name_a[i] == name_b[i]) { 15if(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 */ 25if(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; 32return match_len; 33} 34 35static struct cache_def { 36char path[PATH_MAX +1]; 37int len; 38int flags; 39int track_flags; 40int prefix_len_stat_func; 41} cache; 42 43staticinlinevoidreset_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 intlstat_cache(const char*name,int len, 79int track_flags,int prefix_len_stat_func) 80{ 81int match_len, last_slash, last_slash_dir, previous_slash; 82int match_flags, ret_flags, save_flags, max_len, ret; 83struct stat st; 84 85 total_calls++; 86if(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 */ 93reset_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++; 98if(cache_switches > SWITCHES_BEFORE_WARNING) { 99if(number_of_warnings <10|| number_of_warnings %1000==0) 100printf("warning from%s:%dcache_switches:%u>%u"\ 101"(current:%ulast:%utotal:%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 = 114longest_path_match(name, len, cache.path, cache.len, 115&previous_slash); 116 match_flags = cache.flags & track_flags & (FL_NOENT|FL_SYMLINK); 117if(match_flags && match_len == cache.len) 118return 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; 128if(match_flags && len == match_len) 129return 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; 139while(match_len < max_len) { 140do{ 141 cache.path[match_len] = name[match_len]; 142 match_len++; 143}while(match_len < max_len && name[match_len] !='/'); 144if(match_len >= max_len && !(track_flags & FL_FULLPATH)) 145break; 146 last_slash = match_len; 147 cache.path[last_slash] ='\0'; 148 149if(last_slash <= prefix_len_stat_func) 150 ret =stat(cache.path, &st); 151else 152 ret =lstat(cache.path, &st); 153 154if(ret) { 155 ret_flags = FL_LSTATERR; 156if(errno == ENOENT) 157 ret_flags |= FL_NOENT; 158}else if(S_ISDIR(st.st_mode)) { 159 last_slash_dir = last_slash; 160continue; 161}else if(S_ISLNK(st.st_mode)) { 162 ret_flags = FL_SYMLINK; 163}else{ 164 ret_flags = FL_ERR; 165} 166break; 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); 175if(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{ 196reset_lstat_cache(); 197} 198return ret_flags; 199} 200 201/* 202 * Invalidate the given 'name' from the cache, if 'name' matches 203 * completely with the cache. 204 */ 205voidinvalidate_lstat_cache(const char*name,int len) 206{ 207int match_len, previous_slash; 208 209 match_len =longest_path_match(name, len, cache.path, cache.len, 210&previous_slash); 211if(len == match_len) { 212if((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 217reset_lstat_cache(); 218} 219} 220 221/* 222 * Completely clear the contents of the cache 223 */ 224voidclear_lstat_cache(void) 225{ 226reset_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 */ 234inthas_symlink_leading_path(const char*name,int len) 235{ 236 last_cache_func = current_cache_func; 237 current_cache_func =1; 238returnlstat_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 */ 247inthas_symlink_or_noent_leading_path(const char*name,int len) 248{ 249 last_cache_func = current_cache_func; 250 current_cache_func =2; 251returnlstat_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 */ 263inthas_dirs_only_path(const char*name,int len,int prefix_len) 264{ 265 last_cache_func = current_cache_func; 266 current_cache_func =3; 267returnlstat_cache(name, len, 268 FL_DIR|FL_FULLPATH, prefix_len) & 269 FL_DIR; 270} 271 272static struct removal_def { 273char path[PATH_MAX]; 274int len; 275} removal; 276 277static voiddo_remove_scheduled_dirs(int new_len) 278{ 279while(removal.len > new_len) { 280 removal.path[removal.len] ='\0'; 281if(rmdir(removal.path)) 282break; 283do{ 284 removal.len--; 285}while(removal.len > new_len && 286 removal.path[removal.len] !='/'); 287} 288 removal.len = new_len; 289return; 290} 291 292voidschedule_dir_for_removal(const char*name,int len) 293{ 294int match_len, last_slash, i, previous_slash; 295 296 match_len = last_slash = i = 297longest_path_match(name, len, removal.path, removal.len, 298&previous_slash); 299/* Find last slash inside 'name' */ 300while(i < len) { 301if(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 */ 311if(match_len < last_slash && match_len < removal.len) 312do_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 */ 317if(match_len < last_slash) { 318memcpy(&removal.path[match_len], &name[match_len], 319 last_slash - match_len); 320 removal.len = last_slash; 321} 322return; 323} 324 325voidremove_scheduled_dirs(void) 326{ 327do_remove_scheduled_dirs(0); 328return; 329}