http://prdownloads.sourceforge.net/lufs/lufs-0.9.6.tar.gz?download
[lufs.git] / lufsd / dircache.c
1 /*
2  * dircache.c
3  * Copyright (C) 2002 Florin Malita <mali@go.ro>
4  *
5  * This file is part of LUFS, a free userspace filesystem implementation.
6  * See http://lufs.sourceforge.net/ for updates.
7  *
8  * LUFS is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * LUFS is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include <unistd.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <pthread.h>
27
28 #include <sys/stat.h>
29
30 #include <lufs/proto.h>
31 #include <lufs/fs.h>
32
33 #include "list.h"
34 #include "dircache.h"
35
36 static char root_dir[]="/";
37 static char current_dir[]=".";
38
39 static unsigned long
40 hash(char *name){
41     unsigned long res = 0;
42     int i;
43
44     for(i = 0; i < strlen(name); i++)
45         if(name[i] != '/')
46             res = 0x21413 * (res + name[i]);
47     
48     return res % NBUCKETS;
49 }
50
51 static void
52 delete_dir(struct directory *d){
53     struct list_head *p, *tmp;
54     struct direntry *de;
55
56     TRACE("in");
57     list_for_each_safe(p, tmp, &d->d_entries){
58         de = list_entry(p, struct direntry, e_list);
59         list_del(&de->e_list);
60         free(de->e_name);
61         if(de->e_link)
62             free(de->e_link);
63         free(de);
64     }
65
66     list_del(&d->d_list);
67     free(d->d_name);
68     free(d);
69
70     TRACE("out");
71 }
72
73 struct dir_cache*
74 lu_cache_create(struct list_head *cfg){
75     struct dir_cache *cache;
76     int i;
77     const char *c;
78
79     TRACE("creating dir cache...");
80
81     if(!(cache = malloc(sizeof(struct dir_cache))))
82         return NULL;
83
84     memset(cache, 0, sizeof(struct dir_cache));
85
86     for(i = 0; i < NBUCKETS; i++)
87         INIT_LIST_HEAD(&cache->buckets[i]);
88
89     pthread_mutex_init(&cache->lock, NULL);
90     
91     cache->ttl = DEF_TTL;
92     if((c = lu_opt_getchar(cfg, "LUFSD", "DirCacheTTL")) && atoi(c))
93         cache->ttl = atoi(c);
94     if((c = lu_opt_getchar(cfg, "MOUNT", "dir_cache_ttl")) && atoi(c))
95         cache->ttl = atoi(c);
96     
97     cache->entries = DEF_NENTRIES;
98     if((c = lu_opt_getchar(cfg, "LUFSD", "DirCacheEntries")) && atoi(c))
99         cache->entries = atoi(c);
100     if((c = lu_opt_getchar(cfg, "MOUNT", "dir_cache_entries")) && atoi(c))
101         cache->entries = atoi(c);
102
103     TRACE("entries: %d, ttl: %d", cache->entries, cache->ttl);
104
105     return cache;
106 }
107
108 void
109 lu_cache_destroy(struct dir_cache *cache){
110     struct list_head *p, *tmp;
111     int i;
112     
113     for(i = 0; i < NBUCKETS; i++){
114         list_for_each_safe(p, tmp, &cache->buckets[i]){
115             delete_dir(list_entry(p, struct directory, d_list));
116         }
117     }
118
119     free(cache);
120 }
121
122 static struct directory*
123 search(struct dir_cache *cache, char *dir){
124     struct list_head *p, *tmp;
125     struct directory *d;
126     int hsh;
127
128     hsh = hash(dir);
129
130     TRACE("search %s in bucket %u, size=%u", dir, hsh, cache->lengths[hsh]);
131
132     list_for_each_safe(p, tmp, &cache->buckets[hsh]){
133         d = list_entry(p, struct directory, d_list);
134         
135         if(time(NULL) - d->d_stamp >= cache->ttl){
136             TRACE("%s expired...", d->d_name);
137             delete_dir(d);
138             cache->lengths[hsh]--;
139             TRACE("directory deleted");
140         }else if(!strcmp(dir, d->d_name)){
141             TRACE("%s found", dir);
142             d->d_stamp = time(NULL);
143             return d;
144         }
145     }
146
147     TRACE("dir not found");
148     return NULL;
149 }
150
151 int
152 lu_cache_lookup(struct dir_cache *cache, char *dir, char *file, struct lufs_fattr *fattr, char *link, int buflen){
153     struct directory *d;
154     struct direntry *de;
155     struct list_head *p;
156     int res = -1;
157
158     TRACE("looking up %s in dir %s", file, dir);
159
160     pthread_mutex_lock(&cache->lock);
161
162     if(!(d = search(cache, dir)))
163         goto out;
164
165     list_for_each(p, &d->d_entries){
166         de = list_entry(p, struct direntry, e_list);
167         if(!strcmp(file, de->e_name)){
168             TRACE("file found");
169
170             memcpy(fattr, &de->e_attr, sizeof(struct lufs_fattr));
171             if(link){
172                 if(de->e_link){
173                     if(snprintf(link, buflen, "%s", de->e_link) >= buflen){
174                         WARN("link too long!");
175                         link[buflen - 1] =0;
176                     }
177                 }else{
178                     link[0] = 0;
179                 }
180             }
181                     
182             res = 0;
183             goto out;
184         }
185     }
186
187     TRACE("file not found!");
188
189   out:
190     pthread_mutex_unlock(&cache->lock);
191     return res;
192 }
193
194 static void
195 shrink(struct dir_cache *cache, int hsh){
196     struct directory *dir;
197
198     TRACE("shrinking bucket %u, len=%u", hsh, cache->lengths[hsh]);
199
200     if(list_empty(&cache->buckets[hsh]))
201         return;
202
203     dir = list_entry(cache->buckets[hsh].prev, struct directory, d_list);
204
205     TRACE("deleting dir %s", dir->d_name);
206     
207     delete_dir(dir);
208     cache->lengths[hsh]--;
209 }
210
211 static void
212 check_dir(struct directory *d){
213     struct list_head *p, *tmp;
214     struct direntry *e;
215     struct lufs_fattr dummy;
216     int dot = 0, dotdot = 0;
217
218     memset(&dummy, 0, sizeof(struct lufs_fattr));
219     dummy.f_nlink = 1;
220     dummy.f_uid = dummy.f_gid = 1;
221     dummy.f_mode = S_IFDIR | S_IRWXU | S_IRGRP | S_IXGRP;
222     dummy.f_mtime = dummy.f_atime = dummy.f_ctime = time(NULL);
223     dummy.f_size = 512;
224
225     do{
226         list_for_each_safe(p, tmp, &d->d_entries){
227             e = list_entry(p, struct direntry, e_list);
228             
229             if(!strcmp(e->e_name, ".")){
230                 TRACE("'.' entry found");
231                 list_del(&e->e_list);
232                 list_add(&e->e_list, &d->d_entries);
233                 dot = 1;
234                 continue;
235             }
236             
237             if(!strcmp(e->e_name, "..")){
238                 TRACE("'..' entry found");
239                 list_del(&e->e_list);
240                 if(!dot)
241                     list_add(&e->e_list, &d->d_entries);
242                 else
243                     list_add(&e->e_list, d->d_entries.next);
244                 
245                 dotdot = 1;
246             }
247         }
248         
249         if(!dot)
250             lu_cache_add2dir(d, ".", NULL, &dummy);
251
252         if(!dotdot)
253             lu_cache_add2dir(d, "..", NULL, &dummy);
254
255     }while((!dot) || (!dotdot));
256
257 }
258
259 void
260 lu_cache_add_dir(struct dir_cache *cache, struct directory *d){
261     struct directory *dir;
262     int hsh;
263
264     hsh = hash(d->d_name);
265
266     TRACE("adding dir %s to bucket %i", d->d_name, hsh);
267     
268     check_dir(d);
269
270     pthread_mutex_lock(&cache->lock);
271
272     if((dir = search(cache, d->d_name))){
273         TRACE("directory already in cache, deleting...");
274         delete_dir(dir);
275         cache->lengths[hsh]--;
276     }
277
278     d->d_stamp = time(NULL);
279
280     list_add(&d->d_list, &cache->buckets[hsh]);
281     cache->lengths[hsh]++;
282
283     while(cache->lengths[hsh] > cache->entries)
284         shrink(cache, hsh);
285
286     pthread_mutex_unlock(&cache->lock);
287     
288     TRACE("out");
289 }
290
291 int
292 lu_cache_readdir(struct dir_cache *cache, char *dir, int offset, char *buf, int buflen){
293     struct directory *d;
294     struct direntry *de;
295     struct list_head *p;
296     int slen, res = -1;
297     unsigned len = 0, off = 0;
298
299     TRACE("reading directory %s", dir);
300
301     pthread_mutex_lock(&cache->lock);
302
303     if(!(d = search(cache, dir)))
304         goto out;
305     
306     buf[0] = 0;
307
308     list_for_each(p, &d->d_entries){
309         if(off >= offset){
310             de = list_entry(p, struct direntry, e_list);
311             slen = strlen(de->e_name);
312
313             if(len + slen + 2 >= buflen){
314                 TRACE("buffer filled up");
315                 break;
316             }
317
318             strcat(buf, de->e_name);
319             strcat(buf, "\n");
320
321             len += slen + 1;
322         }
323
324         off++;
325     }
326
327     d->d_stamp = time(NULL);
328
329     res = len;
330
331   out:
332     pthread_mutex_unlock(&cache->lock);
333     TRACE("out");
334     return res;
335 }
336
337 int
338 lu_cache_lookup_file(struct dir_cache *cache, char *file, struct lufs_fattr *fattr, char *link, int buflen){
339     int res;
340
341     char *sep, *dir;
342     
343     if(!(sep = strrchr(file, '/'))){
344         WARN("separator not present!");
345         return -1;
346     }
347
348     *sep = 0;
349
350     if(sep == file)
351         dir = root_dir;
352     else
353         dir = file;
354     
355     if(*(sep+1))
356         file = sep + 1;
357     else
358         file = current_dir;
359
360     TRACE("dir: %s, file: %s", dir, file);
361
362     res = lu_cache_lookup(cache, dir, file, fattr, link, buflen);
363     *sep = '/';
364
365     return res;
366 }
367
368 int
369 lu_cache_invalidate(struct dir_cache *cache, char *file){
370     struct directory *d;
371     char *sep, *dir;
372
373     if(!(sep = strrchr(file, '/'))){
374         WARN("separator not present!");
375         return -1;
376     }
377
378     *sep = 0;
379
380     if(sep == file)
381         dir = root_dir;
382     else
383         dir = file;
384     
385     TRACE("invalidating dir %s", dir);
386
387     pthread_mutex_lock(&cache->lock);
388
389     if(!(d = search(cache, dir))){
390         *sep = '/';
391         pthread_mutex_unlock(&cache->lock);
392         return -1;
393     }
394
395     d->d_stamp = 0;
396
397     pthread_mutex_unlock(&cache->lock);
398     *sep = '/';
399     
400     return 0;
401 }
402
403 struct directory*
404 lu_cache_mkdir(char *dir){
405     struct directory *res;
406
407     TRACE("create dir %s", dir);
408
409     if(!(res = malloc(sizeof(struct directory)))){
410         WARN("out of mem!");
411         return NULL;
412     }
413
414     memset(res, 0, sizeof(struct directory));
415
416     if(!(res->d_name = malloc(strlen(dir) + 1))){
417         WARN("out of mem!");
418         free(res);
419         return NULL;
420     }
421
422     INIT_LIST_HEAD(&res->d_entries);
423     res->d_stamp = time(NULL);
424     strcpy(res->d_name, dir);
425     
426     return res;
427 }
428
429 int
430 lu_cache_add2dir(struct directory *d, char *fname, char *link, struct lufs_fattr *fattr){
431     struct direntry *de;
432
433     TRACE("adding %s->%s to %s", fname, link, d->d_name);
434
435     if(!(de = malloc(sizeof(struct direntry))))
436         goto fail;
437
438
439     if(!(de->e_name = malloc(strlen(fname) + 1)))
440         goto fail_de;
441
442
443     if(link)
444         de->e_link = malloc(strlen(link) + 1);
445     else
446         de->e_link = malloc(2);
447     
448     if(!de->e_link)
449         goto fail_ename;
450
451     memcpy(&de->e_attr, fattr, sizeof(struct lufs_fattr));
452     strcpy(de->e_name, fname);
453     if(link)
454         strcpy(de->e_link, link);
455     else
456         strcpy(de->e_link, "");
457
458     list_add_tail(&de->e_list, &d->d_entries);    
459
460     return 0;
461
462   fail_ename:
463     free(de->e_name);
464   fail_de:
465     free(de);
466   fail:
467     WARN("out of mem!");
468     return -1;
469 }
470
471 void
472 lu_cache_killdir(struct directory *d){
473     struct list_head *p, *tmp;
474     struct direntry *de;
475
476     TRACE("in");
477
478     list_for_each_safe(p, tmp, &d->d_entries){
479         de = list_entry(p, struct direntry, e_list);
480         list_del(&de->e_list);
481         free(de->e_name);
482         if(de->e_link)
483             free(de->e_link);
484         free(de);
485     }
486
487     free(d->d_name);
488     free(d);
489
490 }