c69556acf8d41cf0ccf728de732356105e7df0fa
   1#include "cache.h"
   2
   3static struct cache_def {
   4        char path[PATH_MAX];
   5        int len;
   6        int flags;
   7        int track_flags;
   8} cache;
   9
  10/*
  11 * Returns the length (on a path component basis) of the longest
  12 * common prefix match of 'name' and the cached path string.
  13 */
  14static inline int longest_match_lstat_cache(int len, const char *name)
  15{
  16        int max_len, match_len = 0, i = 0;
  17
  18        max_len = len < cache.len ? len : cache.len;
  19        while (i < max_len && name[i] == cache.path[i]) {
  20                if (name[i] == '/')
  21                        match_len = i;
  22                i++;
  23        }
  24        /* Is the cached path string a substring of 'name'? */
  25        if (i == cache.len && cache.len < len && name[cache.len] == '/')
  26                match_len = cache.len;
  27        /* Is 'name' a substring of the cached path string? */
  28        else if ((i == len && len < cache.len && cache.path[len] == '/') ||
  29                 (i == len && len == cache.len))
  30                match_len = len;
  31        return match_len;
  32}
  33
  34static inline void reset_lstat_cache(int track_flags)
  35{
  36        cache.path[0] = '\0';
  37        cache.len = 0;
  38        cache.flags = 0;
  39        cache.track_flags = track_flags;
  40}
  41
  42#define FL_DIR      (1 << 0)
  43#define FL_NOENT    (1 << 1)
  44#define FL_SYMLINK  (1 << 2)
  45#define FL_LSTATERR (1 << 3)
  46#define FL_ERR      (1 << 4)
  47
  48/*
  49 * Check if name 'name' of length 'len' has a symlink leading
  50 * component, or if the directory exists and is real, or not.
  51 *
  52 * To speed up the check, some information is allowed to be cached.
  53 * This can be indicated by the 'track_flags' argument.
  54 */
  55static int lstat_cache(int len, const char *name,
  56                       int track_flags)
  57{
  58        int match_len, last_slash, last_slash_dir;
  59        int match_flags, ret_flags, save_flags, max_len;
  60        struct stat st;
  61
  62        if (cache.track_flags != track_flags) {
  63                /*
  64                 * As a safeguard we clear the cache if the value of
  65                 * track_flags does not match with the last supplied
  66                 * value.
  67                 */
  68                reset_lstat_cache(track_flags);
  69                match_len = last_slash = 0;
  70        } else {
  71                /*
  72                 * Check to see if we have a match from the cache for
  73                 * the 2 "excluding" path types.
  74                 */
  75                match_len = last_slash = longest_match_lstat_cache(len, name);
  76                match_flags = cache.flags & track_flags & (FL_NOENT|FL_SYMLINK);
  77                if (match_flags && match_len == cache.len)
  78                        return match_flags;
  79                /*
  80                 * If we now have match_len > 0, we would know that
  81                 * the matched part will always be a directory.
  82                 *
  83                 * Also, if we are tracking directories and 'name' is
  84                 * a substring of the cache on a path component basis,
  85                 * we can return immediately.
  86                 */
  87                match_flags = track_flags & FL_DIR;
  88                if (match_flags && len == match_len)
  89                        return match_flags;
  90        }
  91
  92        /*
  93         * Okay, no match from the cache so far, so now we have to
  94         * check the rest of the path components.
  95         */
  96        ret_flags = FL_DIR;
  97        last_slash_dir = last_slash;
  98        max_len = len < PATH_MAX ? len : PATH_MAX;
  99        while (match_len < max_len) {
 100                do {
 101                        cache.path[match_len] = name[match_len];
 102                        match_len++;
 103                } while (match_len < max_len && name[match_len] != '/');
 104                if (match_len >= max_len)
 105                        break;
 106                last_slash = match_len;
 107                cache.path[last_slash] = '\0';
 108
 109                if (lstat(cache.path, &st)) {
 110                        ret_flags = FL_LSTATERR;
 111                        if (errno == ENOENT)
 112                                ret_flags |= FL_NOENT;
 113                } else if (S_ISDIR(st.st_mode)) {
 114                        last_slash_dir = last_slash;
 115                        continue;
 116                } else if (S_ISLNK(st.st_mode)) {
 117                        ret_flags = FL_SYMLINK;
 118                } else {
 119                        ret_flags = FL_ERR;
 120                }
 121                break;
 122        }
 123
 124        /*
 125         * At the end update the cache.  Note that max 3 different
 126         * path types, FL_NOENT, FL_SYMLINK and FL_DIR, can be cached
 127         * for the moment!
 128         */
 129        save_flags = ret_flags & track_flags & (FL_NOENT|FL_SYMLINK);
 130        if (save_flags && last_slash > 0 && last_slash < PATH_MAX) {
 131                cache.path[last_slash] = '\0';
 132                cache.len = last_slash;
 133                cache.flags = save_flags;
 134        } else if (track_flags & FL_DIR &&
 135                   last_slash_dir > 0 && last_slash_dir < PATH_MAX) {
 136                /*
 137                 * We have a separate test for the directory case,
 138                 * since it could be that we have found a symlink or a
 139                 * non-existing directory and the track_flags says
 140                 * that we cannot cache this fact, so the cache would
 141                 * then have been left empty in this case.
 142                 *
 143                 * But if we are allowed to track real directories, we
 144                 * can still cache the path components before the last
 145                 * one (the found symlink or non-existing component).
 146                 */
 147                cache.path[last_slash_dir] = '\0';
 148                cache.len = last_slash_dir;
 149                cache.flags = FL_DIR;
 150        } else {
 151                reset_lstat_cache(track_flags);
 152        }
 153        return ret_flags;
 154}
 155
 156/*
 157 * Return non-zero if path 'name' has a leading symlink component
 158 */
 159int has_symlink_leading_path(int len, const char *name)
 160{
 161        return lstat_cache(len, name,
 162                           FL_SYMLINK|FL_DIR) &
 163                FL_SYMLINK;
 164}
 165
 166/*
 167 * Return non-zero if path 'name' has a leading symlink component or
 168 * if some leading path component does not exists.
 169 */
 170int has_symlink_or_noent_leading_path(int len, const char *name)
 171{
 172        return lstat_cache(len, name,
 173                           FL_SYMLINK|FL_NOENT|FL_DIR) &
 174                (FL_SYMLINK|FL_NOENT);
 175}