symlinks.con commit lstat_cache(): small cleanup and optimisation (60b458b)
   1#include "cache.h"
   2
   3static 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;
  10
  11/*
  12 * Returns the length (on a path component basis) of the longest
  13 * common prefix match of 'name' and the cached path string.
  14 */
  15static inline int longest_match_lstat_cache(int len, const char *name,
  16                                            int *previous_slash)
  17{
  18        int max_len, match_len = 0, match_len_prev = 0, i = 0;
  19
  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;
  25                }
  26                i++;
  27        }
  28        /*
  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?
  32         */
  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;
  38        }
  39        *previous_slash = match_len_prev;
  40        return match_len;
  41}
  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 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)
  60
  61/*
  62 * Check if name 'name' of length 'len' has a symlink leading
  63 * component, or if the directory exists and is real, or not.
  64 *
  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.
  68 *
  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.
  72 */
  73static int lstat_cache(int len, const char *name,
  74                       int track_flags, int prefix_len_stat_func)
  75{
  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;
  79
  80        if (cache.track_flags != track_flags ||
  81            cache.prefix_len_stat_func != prefix_len_stat_func) {
  82                /*
  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.
  86                 */
  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 {
  92                /*
  93                 * Check to see if we have a match from the cache for
  94                 * the 2 "excluding" path types.
  95                 */
  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;
 101                /*
 102                 * If we now have match_len > 0, we would know that
 103                 * the matched part will always be a directory.
 104                 *
 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.
 108                 */
 109                match_flags = track_flags & FL_DIR;
 110                if (match_flags && len == match_len)
 111                        return match_flags;
 112        }
 113
 114        /*
 115         * Okay, no match from the cache so far, so now we have to
 116         * check the rest of the path components.
 117         */
 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';
 130
 131                if (last_slash <= prefix_len_stat_func)
 132                        ret = stat(cache.path, &st);
 133                else
 134                        ret = lstat(cache.path, &st);
 135
 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;
 147                }
 148                break;
 149        }
 150
 151        /*
 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!
 155         */
 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) {
 163                /*
 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.
 169                 *
 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).
 173                 */
 174                cache.path[last_slash_dir] = '\0';
 175                cache.len = last_slash_dir;
 176                cache.flags = FL_DIR;
 177        } else {
 178                reset_lstat_cache();
 179        }
 180        return ret_flags;
 181}
 182
 183/*
 184 * Invalidate the given 'name' from the cache, if 'name' matches
 185 * completely with the cache.
 186 */
 187void invalidate_lstat_cache(int len, const char *name)
 188{
 189        int match_len, previous_slash;
 190
 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();
 199        }
 200}
 201
 202/*
 203 * Completely clear the contents of the cache
 204 */
 205void clear_lstat_cache(void)
 206{
 207        reset_lstat_cache();
 208}
 209
 210#define USE_ONLY_LSTAT  0
 211
 212/*
 213 * Return non-zero if path 'name' has a leading symlink component
 214 */
 215int has_symlink_leading_path(int len, const char *name)
 216{
 217        return lstat_cache(len, name,
 218                           FL_SYMLINK|FL_DIR, USE_ONLY_LSTAT) &
 219                FL_SYMLINK;
 220}
 221
 222/*
 223 * Return non-zero if path 'name' has a leading symlink component or
 224 * if some leading path component does not exists.
 225 */
 226int has_symlink_or_noent_leading_path(int len, const char *name)
 227{
 228        return lstat_cache(len, name,
 229                           FL_SYMLINK|FL_NOENT|FL_DIR, USE_ONLY_LSTAT) &
 230                (FL_SYMLINK|FL_NOENT);
 231}
 232
 233/*
 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.
 239 */
 240int has_dirs_only_path(int len, const char *name, int prefix_len)
 241{
 242        return lstat_cache(len, name,
 243                           FL_DIR|FL_FULLPATH, prefix_len) &
 244                FL_DIR;
 245}