1
#include "cache.h"
2
3
-struct pathname {
4
- int len;
3
+static struct cache_def {
4
char path[PATH_MAX];
6
-};
5
+ int len;
6
+ int flags;
7
+} cache;
8
8
-/* Return matching pathname prefix length, or zero if not matching */
9
-static inline int match_pathname(int len, const char *name, struct pathname *match)
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
+ */
13
+static inline int longest_match_lstat_cache(int len, const char *name)
14
{
11
- int match_len = match->len;
12
- return (len > match_len &&
13
- name[match_len] == '/' &&
14
- !memcmp(name, match->path, match_len)) ? match_len : 0;
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
17
-static inline void set_pathname(int len, const char *name, struct pathname *match)
33
+static inline void reset_lstat_cache(void)
34
{
19
- if (len < PATH_MAX) {
20
- match->len = len;
21
- memcpy(match->path, name, len);
22
- match->path[len] = 0;
23
- }
35
+ cache.path[0] = '\0';
36
+ cache.len = 0;
37
+ cache.flags = 0;
38
}
39
26
-int has_symlink_leading_path(int len, const char *name)
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
+ */
52
+static int lstat_cache(int len, const char *name,
53
+ int track_flags)
54
{
28
- static struct pathname link, nonlink;
29
- char path[PATH_MAX];
55
+ int match_len, last_slash, last_slash_dir;
56
+ int match_flags, ret_flags, save_flags, max_len;
57
struct stat st;
31
- char *sp;
32
- int known_dir;
58
59
/*
35
- * See if the last known symlink cache matches.
60
+ * Check to see if we have a match from the cache for the
61
+ * symlink path type.
62
*/
37
- if (match_pathname(len, name, &link))
38
- return 1;
39
-
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
/*
41
- * Get rid of the last known directory part
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
*/
43
- known_dir = match_pathname(len, name, &nonlink);
75
+ match_flags = track_flags & FL_DIR;
76
+ if (match_flags && len == match_len)
77
+ return match_flags;
78
45
- while ((sp = strchr(name + known_dir + 1, '/')) != NULL) {
46
- int thislen = sp - name ;
47
- memcpy(path, name, thislen);
48
- path[thislen] = 0;
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
50
- if (lstat(path, &st))
51
- return 0;
52
- if (S_ISDIR(st.st_mode)) {
53
- set_pathname(thislen, path, &nonlink);
54
- known_dir = thislen;
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;
56
- }
57
- if (S_ISLNK(st.st_mode)) {
58
- set_pathname(thislen, path, &link);
59
- return 1;
101
+ } else if (S_ISLNK(st.st_mode)) {
102
+ ret_flags = FL_SYMLINK;
103
+ } else {
104
+ ret_flags = FL_ERR;
105
}
106
break;
107
}
63
- return 0;
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
+ */
144
+int 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
}