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