Version increased to unfs3_0.9.22+dfsg-1maemo3
[unfs3] / unfs3 / fh_cache.c
1
2 /*
3  * UNFS3 filehandle cache
4  * (C) 2004
5  * see file LICENSE for license details
6  */
7
8 #include "config.h"
9
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #include <rpc/rpc.h>
13 #include <limits.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <unistd.h>
18 #include <time.h>
19
20 #include "nfs.h"
21 #include "fh.h"
22 #include "locate.h"
23 #include "fh_cache.h"
24 #include "mount.h"
25 #include "daemon.h"
26 #include "Config/exports.h"
27 #include "readdir.h"
28 #include "backend.h"
29
30 /* number of entries in fh cache */
31 #define CACHE_ENTRIES   4096
32
33 typedef struct {
34     uint32 dev;                 /* device */
35     uint64 ino;                 /* inode */
36     char path[NFS_MAXPATHLEN];  /* pathname */
37     unsigned int use;           /* last use */
38 } unfs3_cache_t;
39
40 static unfs3_cache_t fh_cache[CACHE_ENTRIES];
41
42 /* statistics */
43 int fh_cache_max = 0;
44 int fh_cache_use = 0;
45 int fh_cache_hit = 0;
46
47 /* counter for LRU */
48 static unsigned int fh_cache_time = 0;
49
50 /*
51  * last returned entry
52  *
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
56  *
57  * this is needed since fh_cache_time can roll around to 0, thus
58  * making the entry evictable
59  */
60 static int fh_last_entry = -1;
61
62 /*
63  * return next pseudo-time value for LRU counter
64  */
65 static unsigned int fh_cache_next(void)
66 {
67     return ++fh_cache_time;
68 }
69
70 /*
71  * initialize cache
72  */
73 void fh_cache_init(void)
74 {
75     memset(fh_cache, 0, sizeof(unfs3_cache_t) * CACHE_ENTRIES);
76 }
77
78 /*
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
82  */
83 static int fh_cache_lru(void)
84 {
85     unsigned int best = UINT_MAX;
86     int best_idx = 0;
87     int i;
88
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++;
92
93     for (i = 0; i < CACHE_ENTRIES; i++) {
94         if (i == fh_last_entry)
95             continue;
96         if (fh_cache[i].use == 0)
97             return i;
98         if (fh_cache[i].use < best) {
99             best = fh_cache[i].use;
100             best_idx = i;
101         }
102     }
103
104     /* avoid stomping over last returned entry */
105     if (best_idx == 0 && fh_last_entry == 0)
106         best_idx = 1;
107
108     return best_idx;
109 }
110
111 /*
112  * invalidate (clear) a cache entry
113  */
114 static void fh_cache_inval(int idx)
115 {
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;
120 }
121
122 /*
123  * find index given device and inode number
124  */
125 static int fh_cache_index(uint32 dev, uint64 ino)
126 {
127     int i, res = -1;
128
129     for (i = 0; i < fh_cache_max + 1; i++)
130         if (fh_cache[i].dev == dev && fh_cache[i].ino == ino) {
131             res = i;
132             break;
133         }
134
135     return res;
136 }
137
138 /*
139  * add an entry to the filehandle cache
140  */
141 char *fh_cache_add(uint32 dev, uint64 ino, const char *path)
142 {
143     int idx;
144
145     /* if we already have a matching entry, overwrite that */
146     idx = fh_cache_index(dev, ino);
147
148     /* otherwise overwrite least recently used entry */
149     if (idx == -1)
150         idx = fh_cache_lru();
151
152     fh_cache[idx].dev = dev;
153     fh_cache[idx].ino = ino;
154     fh_cache[idx].use = fh_cache_next();
155
156     strcpy(fh_cache[idx].path, path);
157
158     return fh_cache[idx].path;
159 }
160
161 /*
162  * lookup an entry in the cache given a device, inode, and generation number
163  */
164 static char *fh_cache_lookup(uint32 dev, uint64 ino)
165 {
166     int i, res;
167     backend_statstruct buf;
168
169     i = fh_cache_index(dev, ino);
170
171     if (i != -1) {
172         /* check whether path to <dev,ino> relation still holds */
173         res = backend_lstat(fh_cache[i].path, &buf);
174         if (res == -1) {
175             /* object does not exist any more */
176             fh_cache_inval(i);
177             return NULL;
178         }
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();
182
183             /* update stat cache */
184             st_cache_valid = TRUE;
185             st_cache = buf;
186
187             /* prevent next fh_cache_add from overwriting entry */
188             fh_last_entry = i;
189
190             return fh_cache[i].path;
191         } else {
192             /* path to <dev,ino> relation has changed */
193             fh_cache_inval(i);
194             return NULL;
195         }
196     }
197
198     return NULL;
199 }
200
201 /*
202  * resolve a filename into a path
203  * cache-using wrapper for fh_decomp_raw
204  */
205 char *fh_decomp(nfs_fh3 fh)
206 {
207     char *result;
208     unfs3_fh_t *obj = (void *) fh.data.data_val;
209     time_t *last_mtime;
210     uint32 *dir_hash, new_dir_hash;
211
212     if (!nfh_valid(fh)) {
213         st_cache_valid = FALSE;
214         return NULL;
215     }
216
217     /* Does the fsid match some static fsid? */
218     if ((result =
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;
224
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;
231                 st_cache.st_uid = 0;
232                 st_cache.st_gid = 0;
233                 st_cache.st_rdev = 0;
234                 st_cache.st_size = 4096;
235                 st_cache.st_blksize = 512;
236                 st_cache.st_blocks = 8;
237             } else {
238                 /* Stat was OK, but make sure the values are sane. Supermount 
239                    returns insane values when no media is inserted, for
240                    example. */
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;
249             }
250
251             st_cache.st_dev = obj->dev;
252             st_cache.st_ino = 0x1;
253
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
259                anyway.
260
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.
265
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 */
271
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
277                    time. */
278                 st_cache.st_mtime = time(NULL);
279                 *last_mtime = st_cache.st_mtime;
280                 *dir_hash = new_dir_hash;
281             } else {
282                 /* Hash unchanged. Returned stored mtime. */
283                 st_cache.st_mtime = *last_mtime;
284             }
285
286             return result;
287         }
288     }
289
290     /* try lookup in cache, increase cache usage counter */
291     result = fh_cache_lookup(obj->dev, obj->ino);
292     fh_cache_use++;
293
294     if (!result) {
295         /* not found, resolve the hard way */
296         result = fh_decomp_raw(obj);
297
298         /* if still not found, do full recursive search) */
299         if (!result)
300             result = backend_locate_file(obj->dev, obj->ino);
301
302         if (result)
303             /* add to cache for later use if resolution ok */
304             result = fh_cache_add(obj->dev, obj->ino, result);
305         else
306             /* could not resolve in any way */
307             st_cache_valid = FALSE;
308     } else
309         /* found, update cache hit statistic */
310         fh_cache_hit++;
311
312     return result;
313 }
314
315 /*
316  * compose a filehandle for a path
317  * cache-using wrapper for fh_comp_raw
318  * exports_options must be called before
319  */
320 unfs3_fh_t fh_comp(const char *path, struct svc_req * rqstp, int need_dir)
321 {
322     unfs3_fh_t res;
323
324     res = fh_comp_raw(path, rqstp, need_dir);
325     if (fh_valid(res))
326         /* add to cache for later use */
327         fh_cache_add(res.dev, res.ino, path);
328
329     res.pwhash = export_password_hash;
330     return res;
331 }
332
333 /*
334  * return pointer to composed filehandle
335  * wrapper for fh_comp
336  */
337 unfs3_fh_t *fh_comp_ptr(const char *path, struct svc_req * rqstp,
338                         int need_dir)
339 {
340     static unfs3_fh_t res;
341
342     res = fh_comp(path, rqstp, need_dir);
343     if (fh_valid(res))
344         return &res;
345     else
346         return NULL;
347 }