3 * UNFS3 filehandle cache
5 * see file LICENSE for license details
10 #include <sys/types.h>
26 #include "Config/exports.h"
30 /* number of entries in fh cache */
31 #define CACHE_ENTRIES 4096
34 uint32 dev; /* device */
35 uint64 ino; /* inode */
36 char path[NFS_MAXPATHLEN]; /* pathname */
37 unsigned int use; /* last use */
40 static unfs3_cache_t fh_cache[CACHE_ENTRIES];
48 static unsigned int fh_cache_time = 0;
53 * this entry must not be overwritten before the next lookup, because
54 * operations such as CREATE may still be needing the path inside the
55 * entry for getting directory attributes
57 * this is needed since fh_cache_time can roll around to 0, thus
58 * making the entry evictable
60 static int fh_last_entry = -1;
63 * return next pseudo-time value for LRU counter
65 static unsigned int fh_cache_next(void)
67 return ++fh_cache_time;
73 void fh_cache_init(void)
75 memset(fh_cache, 0, sizeof(unfs3_cache_t) * CACHE_ENTRIES);
79 * find cache index to use for new entry
80 * returns either an empty slot or the least recently used slot if no
81 * empty slot is present
83 static int fh_cache_lru(void)
85 unsigned int best = UINT_MAX;
89 /* if cache is not full, we simply hand out the next slot */
90 if (fh_cache_max < CACHE_ENTRIES - 1)
91 return fh_cache_max++;
93 for (i = 0; i < CACHE_ENTRIES; i++) {
94 if (i == fh_last_entry)
96 if (fh_cache[i].use == 0)
98 if (fh_cache[i].use < best) {
99 best = fh_cache[i].use;
104 /* avoid stomping over last returned entry */
105 if (best_idx == 0 && fh_last_entry == 0)
112 * invalidate (clear) a cache entry
114 static void fh_cache_inval(int idx)
116 fh_cache[idx].dev = 0;
117 fh_cache[idx].ino = 0;
118 fh_cache[idx].use = 0;
119 fh_cache[idx].path[0] = 0;
123 * find index given device and inode number
125 static int fh_cache_index(uint32 dev, uint64 ino)
129 for (i = 0; i < fh_cache_max + 1; i++)
130 if (fh_cache[i].dev == dev && fh_cache[i].ino == ino) {
139 * add an entry to the filehandle cache
141 char *fh_cache_add(uint32 dev, uint64 ino, const char *path)
145 /* if we already have a matching entry, overwrite that */
146 idx = fh_cache_index(dev, ino);
148 /* otherwise overwrite least recently used entry */
150 idx = fh_cache_lru();
152 fh_cache[idx].dev = dev;
153 fh_cache[idx].ino = ino;
154 fh_cache[idx].use = fh_cache_next();
156 strcpy(fh_cache[idx].path, path);
158 return fh_cache[idx].path;
162 * lookup an entry in the cache given a device, inode, and generation number
164 static char *fh_cache_lookup(uint32 dev, uint64 ino)
167 backend_statstruct buf;
169 i = fh_cache_index(dev, ino);
172 /* check whether path to <dev,ino> relation still holds */
173 res = backend_lstat(fh_cache[i].path, &buf);
175 /* object does not exist any more */
179 if (buf.st_dev == dev && buf.st_ino == ino) {
180 /* cache hit, update time on cache entry */
181 fh_cache[i].use = fh_cache_next();
183 /* update stat cache */
184 st_cache_valid = TRUE;
187 /* prevent next fh_cache_add from overwriting entry */
190 return fh_cache[i].path;
192 /* path to <dev,ino> relation has changed */
202 * resolve a filename into a path
203 * cache-using wrapper for fh_decomp_raw
205 char *fh_decomp(nfs_fh3 fh)
208 unfs3_fh_t *obj = (void *) fh.data.data_val;
210 uint32 *dir_hash, new_dir_hash;
212 if (!nfh_valid(fh)) {
213 st_cache_valid = FALSE;
217 /* Does the fsid match some static fsid? */
219 export_point_from_fsid(obj->dev, &last_mtime, &dir_hash)) != NULL) {
220 if (obj->ino == 0x1) {
221 /* This FH refers to the export point itself */
222 /* Need to fill stat cache */
223 st_cache_valid = TRUE;
225 if (backend_lstat(result, &st_cache) == -1) {
226 /* export point does not exist. This probably means that we
227 are using autofs and no media is inserted. Fill stat cache
228 with dummy information */
229 st_cache.st_mode = S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO;
230 st_cache.st_nlink = 2;
233 st_cache.st_rdev = 0;
234 st_cache.st_size = 4096;
235 st_cache.st_blksize = 512;
236 st_cache.st_blocks = 8;
238 /* Stat was OK, but make sure the values are sane. Supermount
239 returns insane values when no media is inserted, for
241 if (st_cache.st_nlink == 0)
242 st_cache.st_nlink = 1;
243 if (st_cache.st_size == 0)
244 st_cache.st_size = 4096;
245 if (st_cache.st_blksize == 0)
246 st_cache.st_blksize = 512;
247 if (st_cache.st_blocks == 0)
248 st_cache.st_blocks = 8;
251 st_cache.st_dev = obj->dev;
252 st_cache.st_ino = 0x1;
254 /* It's very important that we get mtime correct, since it's used
255 as verifier in READDIR. The generation of mtime is tricky,
256 because with some filesystems, such as the Linux 2.4 FAT fs,
257 the mtime value for the mount point is set to *zero* on each
258 mount. I consider this a bug, but we need to work around it
261 We store the last mtime returned. When stat returns a smaller
262 value than this, we double-check by doing a hash of the names
263 in the directory. If this hash is different from what we had
264 earlier, return current time.
266 Note: Since dir_hash is stored in memory, we have introduced a
267 little statefulness here. This means that if unfsd is
268 restarted during two READDIR calls, NFS3ERR_BAD_COOKIE will be
269 returned, and the client has to retry the READDIR operation
270 with a zero cookie */
272 if (st_cache.st_mtime > *last_mtime) {
273 /* stat says our directory has changed */
274 *last_mtime = st_cache.st_mtime;
275 } else if (*dir_hash != (new_dir_hash = directory_hash(result))) {
276 /* The names in the directory has changed. Return current
278 st_cache.st_mtime = time(NULL);
279 *last_mtime = st_cache.st_mtime;
280 *dir_hash = new_dir_hash;
282 /* Hash unchanged. Returned stored mtime. */
283 st_cache.st_mtime = *last_mtime;
290 /* try lookup in cache, increase cache usage counter */
291 result = fh_cache_lookup(obj->dev, obj->ino);
295 /* not found, resolve the hard way */
296 result = fh_decomp_raw(obj);
298 /* if still not found, do full recursive search) */
300 result = backend_locate_file(obj->dev, obj->ino);
303 /* add to cache for later use if resolution ok */
304 result = fh_cache_add(obj->dev, obj->ino, result);
306 /* could not resolve in any way */
307 st_cache_valid = FALSE;
309 /* found, update cache hit statistic */
316 * compose a filehandle for a path
317 * cache-using wrapper for fh_comp_raw
318 * exports_options must be called before
320 unfs3_fh_t fh_comp(const char *path, struct svc_req * rqstp, int need_dir)
324 res = fh_comp_raw(path, rqstp, need_dir);
326 /* add to cache for later use */
327 fh_cache_add(res.dev, res.ino, path);
329 res.pwhash = export_password_hash;
334 * return pointer to composed filehandle
335 * wrapper for fh_comp
337 unfs3_fh_t *fh_comp_ptr(const char *path, struct svc_req * rqstp,
340 static unfs3_fh_t res;
342 res = fh_comp(path, rqstp, need_dir);