symlinks.con commit lstat_cache(): print a warning if doing ping-pong between cache types (7734f04)
   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}