Back to index

avfs  1.0.1
filter.c
Go to the documentation of this file.
00001 /*  
00002     AVFS: A Virtual File System Library
00003     Copyright (C) 1998-2001  Miklos Szeredi <miklos@szeredi.hu>
00004     
00005     This program can be distributed under the terms of the GNU GPL.
00006     See the file COPYING.
00007 */
00008 
00009 #include "filter.h"
00010 #include "version.h"
00011 #include "filtprog.h"
00012 #include "filecache.h"
00013 #include "cache.h"
00014 #include "internal.h"
00015 #include "oper.h"
00016 
00017 /* FIXME: If there was an error in read, this shouldn't be kept in cache */
00018 
00019 struct filtid {
00020     avino_t ino;
00021     avdev_t dev;
00022 };
00023 
00024 struct filtmod {
00025     avoff_t size;
00026     avtimestruc_t mtime;
00027 };
00028 
00029 struct filtnode {
00030     avmutex lock;
00031     vfile *vf;
00032     struct sfile *sf;
00033     struct filtid id;
00034     struct filtmod mod;
00035     avino_t ino;
00036     unsigned int writers;
00037 };
00038 
00039 struct filtfile {
00040     struct filtnode *nod;
00041     struct cacheobj *cobj;
00042     int iswrite;
00043 };
00044 
00045 static char **filt_copy_prog(const char *prog[])
00046 {
00047     int num;
00048     const char **curr;
00049     char **copyprog;
00050     int i;
00051 
00052     if(prog == NULL)
00053         return NULL;
00054 
00055     for(num = 0, curr = prog; *curr != NULL; curr++, num++);
00056     
00057     copyprog = (char **) av_malloc(sizeof(char *) * (num + 1));
00058 
00059     for(i = 0; i < num; i++)
00060         copyprog[i] = av_strdup(prog[i]);
00061 
00062     copyprog[i] = NULL;
00063 
00064     return copyprog;
00065 }
00066 
00067 static void filt_free_prog(char **prog)
00068 {
00069     if(prog != NULL) {
00070         char **curr;
00071 
00072         for(curr = prog; *curr != NULL; curr++)
00073             av_free(*curr);
00074         
00075         av_free(prog);
00076     }
00077 }
00078 
00079 static int filt_lookup(ventry *ve, const char *name, void **newp)
00080 {
00081     char *path = (char *) ve->data;
00082     
00083     if(path == NULL) {
00084         if(name[0] != '\0')
00085             return -ENOENT;
00086        if(ve->mnt->opts[0] != '\0')
00087             return -ENOENT;
00088         path = av_strdup(name);
00089     }
00090     else if(name == NULL) {
00091         av_free(path);
00092         path = NULL;
00093     }
00094     else 
00095         return -ENOENT;
00096     
00097     *newp = path;
00098 
00099     return 0;
00100 }
00101 
00102 static void filtnode_free(struct filtnode *nod)
00103 {
00104     av_unref_obj(nod->sf);
00105     av_close(nod->vf);
00106     AV_FREELOCK(nod->lock)
00107 }
00108 
00109 
00110 static int filt_same_file(struct filtid *id, struct avstat *stbuf)
00111 {
00112     if(id->ino == stbuf->ino && id->dev == stbuf->dev)
00113         return 1;
00114     else
00115         return 0;
00116 }
00117 
00118 static int filt_unmodif_file(struct filtmod *mod, struct avstat *stbuf)
00119 {
00120     if(mod->size == stbuf->size && mod->mtime.sec == stbuf->mtime.sec &&
00121        mod->mtime.nsec == stbuf->mtime.nsec)
00122         return 1;
00123     else
00124         return 0;
00125 }
00126 
00127 static void filt_id_set(struct filtid *id, struct avstat *stbuf)
00128 {
00129     id->ino = stbuf->ino;
00130     id->dev = stbuf->dev;
00131 }
00132 
00133 static void filt_mod_set(struct filtmod *mod, struct avstat *stbuf)
00134 {
00135     mod->size = stbuf->size;
00136     mod->mtime = stbuf->mtime;
00137 }
00138 
00139 
00140 static void filt_newnode(struct filtfile *ff, ventry *ve, vfile *vf,
00141                          const char *key, struct avstat *buf)
00142 {
00143     struct filtnode *nod;
00144     struct filtdata *filtdat = (struct filtdata *) ve->mnt->avfs->data;
00145 
00146     AV_NEW_OBJ(nod, filtnode_free);
00147     AV_INITLOCK(nod->lock);
00148     nod->vf = vf;
00149     nod->sf = av_filtprog_new(vf, filtdat);
00150     filt_id_set(&nod->id, buf);
00151     filt_mod_set(&nod->mod, buf);
00152     nod->ino = av_new_ino(ve->mnt->avfs);
00153     nod->writers = 0;
00154 
00155     AV_LOCK(nod->lock);
00156 
00157     ff->cobj = av_cacheobj_new(nod, key);
00158     ff->nod = nod;
00159     av_filecache_set(key, ff->cobj);
00160 }
00161 
00162 static int filt_validate_file(struct filtnode *nod, ventry *ve, vfile *vf,
00163                               struct avstat *buf, int iswrite)
00164 {
00165     if(nod->writers == 0 && !filt_unmodif_file(&nod->mod, buf)) {
00166         struct filtdata *filtdat = (struct filtdata *) ve->mnt->avfs->data;
00167         
00168         av_unref_obj(nod->sf);
00169         av_close(nod->vf);
00170         nod->sf = av_filtprog_new(vf, filtdat);
00171         nod->vf = vf;
00172         filt_mod_set(&nod->mod, buf);
00173     }
00174     else if(nod->writers == 0 && iswrite) {
00175         avoff_t pos = av_lseek(nod->vf, 0, AVSEEK_CUR);
00176 
00177         if(pos > 0)
00178             pos = av_lseek(vf, pos, AVSEEK_SET);
00179         
00180         if(pos < 0)
00181             return pos;
00182         
00183         av_filtprog_change(nod->sf, vf);
00184         av_close(nod->vf);
00185         nod->vf = vf;
00186     }
00187     else
00188         av_close(vf);
00189 
00190     return 0;
00191 }
00192 
00193 static int filt_getfile(struct filtfile *ff, ventry *ve, vfile *vf,
00194                         const char *key)
00195 {
00196     int res;
00197     struct avstat buf;
00198     int attrmask = AVA_INO | AVA_DEV | AVA_SIZE | AVA_MTIME;
00199 
00200     res = av_fgetattr(vf, &buf, attrmask);
00201     if(res < 0)
00202         return res;
00203 
00204     ff->cobj = (struct cacheobj *) av_filecache_get(key);
00205     if(ff->cobj != NULL)
00206         ff->nod = (struct filtnode *) av_cacheobj_get(ff->cobj);
00207 
00208     if(ff->nod == NULL || !filt_same_file(&ff->nod->id, &buf)) {
00209         av_unref_obj(ff->nod);
00210         av_unref_obj(ff->cobj);
00211         filt_newnode(ff, ve, vf, key, &buf);
00212         return 0;
00213     }
00214 
00215     AV_LOCK(ff->nod->lock);
00216     res = filt_validate_file(ff->nod, ve, vf, &buf, ff->iswrite);
00217     if(res < 0) {
00218         AV_UNLOCK(ff->nod->lock);
00219         av_unref_obj(ff->nod);
00220         av_unref_obj(ff->cobj);
00221         return res;
00222     }
00223 
00224     return 0;
00225 }
00226 
00227 static int filt_get_baseflags(int flags, int *maybecrp)
00228 {
00229     int baseflags;
00230     int maybecreat = 0;
00231 
00232     if(AV_ISWRITE(flags))
00233         baseflags = AVO_RDWR;
00234     else
00235         baseflags = AVO_RDONLY;
00236     
00237     if((flags & AVO_TRUNC) != 0)
00238         baseflags |= AVO_TRUNC;
00239 
00240     if((flags & AVO_CREAT) != 0) {
00241         if(flags & AVO_EXCL)
00242             baseflags = AVO_RDWR | AVO_CREAT | AVO_EXCL | AVO_TRUNC;
00243         else if(flags & AVO_TRUNC)
00244             baseflags = AVO_RDWR | AVO_CREAT | AVO_TRUNC;
00245         else
00246             maybecreat = 1;
00247     }
00248 
00249     *maybecrp = maybecreat;
00250     return baseflags;
00251 }
00252 
00253 static int filt_open_base(ventry *ve, int flags, avmode_t mode, vfile **vfp,
00254                           int *bfresp)
00255 {
00256     int res;
00257     int maybecreat;
00258     int baseflags;
00259 
00260     baseflags = filt_get_baseflags(flags, &maybecreat);
00261 
00262     res = av_open(ve->mnt->base, baseflags, mode, vfp);
00263     if(res == -ENOENT && maybecreat) {
00264         baseflags = AVO_RDWR | AVO_CREAT | AVO_TRUNC;
00265         res = av_open(ve->mnt->base, baseflags, mode, vfp);
00266     }
00267 
00268     *bfresp = baseflags;
00269     return res;
00270 }
00271 
00272 static int filt_open_file(struct filtfile *ff, ventry *ve, int flags,
00273                           avmode_t mode)
00274 {
00275     int res;
00276     vfile *vf;
00277     char *key;
00278     int baseflags;
00279     
00280     res = filt_open_base(ve, flags, mode, &vf, &baseflags);
00281     if(res < 0)
00282         return res;
00283 
00284     res = av_filecache_getkey(ve, &key);
00285     if(res == 0) {
00286         if((baseflags & AVO_ACCMODE) == AVO_RDWR)
00287             ff->iswrite = 1;
00288 
00289         res = filt_getfile(ff, ve, vf, key);
00290         if(res == 0) {
00291             if((baseflags & AVO_TRUNC) != 0)
00292                 av_sfile_truncate(ff->nod->sf, 0);
00293 
00294             if(ff->iswrite)
00295                 ff->nod->writers ++;
00296 
00297             AV_UNLOCK(ff->nod->lock);
00298         }
00299 
00300         av_free(key);
00301     }
00302     if(res < 0) {
00303         av_close(vf);
00304         return res;
00305     }
00306 
00307     return 0;
00308 }
00309 
00310 static int filt_open(ventry *ve, int flags, avmode_t mode, void **resp)
00311 {
00312     int res;
00313     struct filtfile *ff;
00314     
00315     if(flags & AVO_DIRECTORY)
00316         return -ENOTDIR;
00317 
00318     AV_NEW(ff);
00319     ff->nod = NULL;
00320     ff->cobj = NULL;
00321     ff->iswrite = 0;
00322 
00323     res = filt_open_file(ff, ve, flags, mode);
00324     if(res < 0) {
00325         av_free(ff);
00326         return res;
00327     }
00328 
00329     *resp = ff;
00330 
00331     return 0;
00332 }
00333 
00334 static avssize_t filt_read(vfile *vf, char *buf, avsize_t nbyte)
00335 {
00336     avssize_t res;
00337     struct filtfile *ff = (struct filtfile *) vf->data;
00338     struct filtnode *nod = ff->nod;
00339     
00340     AV_LOCK(nod->lock);
00341     res = av_sfile_pread(nod->sf, buf, nbyte, vf->ptr);
00342     AV_UNLOCK(nod->lock);
00343 
00344     if(res > 0)
00345         vf->ptr += res;
00346 
00347     return res;
00348 }
00349 
00350 static avssize_t filt_write(vfile *vf, const char *buf, avsize_t nbyte)
00351 {
00352     avssize_t res;
00353     struct filtfile *ff = (struct filtfile *) vf->data;
00354     struct filtnode *nod = ff->nod;
00355     
00356     AV_LOCK(nod->lock);
00357     if((vf->flags & AVO_APPEND) != 0) {
00358         avoff_t pos;
00359 
00360         pos = av_sfile_size(nod->sf);
00361         if(pos < 0) {
00362             AV_UNLOCK(nod->lock);
00363             return pos;
00364         }
00365         
00366         vf->ptr = pos;
00367     }
00368     res = av_sfile_pwrite(nod->sf, buf, nbyte, vf->ptr);
00369     if(res >= 0)
00370         av_curr_time(&nod->mod.mtime);
00371     AV_UNLOCK(nod->lock);
00372         
00373     if(res > 0)
00374         vf->ptr += res;
00375 
00376     return res;
00377 }
00378 
00379 static int filt_truncate(vfile *vf, avoff_t length)
00380 {
00381     int res;
00382     struct filtfile *ff = (struct filtfile *) vf->data;
00383     struct filtnode *nod = ff->nod;
00384     
00385     AV_LOCK(nod->lock);
00386     res = av_sfile_truncate(nod->sf, length);
00387     AV_UNLOCK(nod->lock);
00388 
00389     return res;
00390 }
00391 
00392 static void filt_afterflush(vfile *vf, struct filtnode *nod)
00393 {
00394     int res;
00395     struct avstat stbuf;
00396     int attrmask = AVA_INO | AVA_DEV | AVA_SIZE | AVA_MTIME;
00397     avoff_t size = -1;
00398     vfile *bvf;
00399 
00400     res = av_fgetattr(nod->vf, &stbuf, AVA_SIZE);
00401     if(res == 0)
00402         size = stbuf.size;
00403 
00404     av_close(nod->vf);
00405     nod->vf = NULL;
00406     nod->mod.size = -1;
00407 
00408     res = av_open(vf->mnt->base, AVO_NOPERM, 0, &bvf);
00409     if(res < 0)
00410         return;
00411 
00412     res = av_fgetattr(bvf, &stbuf, attrmask);
00413     if(res == 0 && filt_same_file(&nod->id, &stbuf) && stbuf.size == size)
00414         filt_mod_set(&nod->mod, &stbuf);
00415         
00416     av_close(bvf);
00417 }
00418 
00419 static int filt_close(vfile *vf)
00420 {
00421     int res = 0;
00422     struct filtfile *ff = (struct filtfile *) vf->data;
00423     struct filtnode *nod = ff->nod;
00424     avoff_t du;
00425 
00426     AV_LOCK(nod->lock);
00427     if(ff->iswrite) {
00428         nod->writers --;
00429 
00430         if(nod->writers == 0) {
00431             res = av_sfile_flush(nod->sf);
00432             filt_afterflush(vf, nod);
00433         }
00434     }
00435     du = av_sfile_diskusage(nod->sf);
00436     if(du >= 0)
00437         av_cacheobj_setsize(ff->cobj, du);
00438     AV_UNLOCK(nod->lock);
00439 
00440     av_unref_obj(nod);
00441     av_unref_obj(ff->cobj);
00442     av_free(ff);
00443 
00444     return res;
00445 }
00446 
00447 static int filt_getattr(vfile *vf, struct avstat *buf, int attrmask)
00448 {
00449     struct filtfile *ff = (struct filtfile *) vf->data;
00450     struct filtnode *nod = ff->nod;
00451     struct avstat origbuf;
00452     int res;
00453     avoff_t size = -1;
00454     avino_t ino;
00455     avtimestruc_t mtime;
00456 
00457     AV_LOCK(nod->lock);
00458     ino = nod->ino;
00459     res = av_fgetattr(nod->vf, &origbuf, AVA_ALL & ~AVA_SIZE);
00460     if(res == 0) { 
00461         size = av_sfile_size(nod->sf);
00462         if(size < 0)
00463             res = size;
00464     }
00465     if(nod->writers != 0)
00466         mtime = nod->mod.mtime;
00467     else
00468         mtime = origbuf.mtime;
00469     AV_UNLOCK(nod->lock);
00470 
00471     if(res < 0)
00472         return res;
00473 
00474     *buf = origbuf;
00475     buf->mode &= ~(07000);
00476     buf->blksize = 4096;
00477     buf->dev = vf->mnt->avfs->dev;
00478     buf->ino = ino;
00479     buf->size = size;
00480     buf->blocks = AV_BLOCKS(size);
00481     buf->mtime = mtime;
00482     
00483     return 0;
00484 }
00485 
00486 static int filt_setattr(vfile *vf, struct avstat *buf, int attrmask)
00487 {
00488     int res;
00489     struct filtfile *ff = (struct filtfile *) vf->data;
00490     struct filtnode *nod = ff->nod;
00491 
00492     AV_LOCK(nod->lock);
00493     res = av_fsetattr(nod->vf, buf, attrmask);
00494     AV_UNLOCK(nod->lock);
00495 
00496     return res;    
00497 }
00498 
00499 static int filt_access(ventry *ve, int amode)
00500 {
00501     return av_access(ve->mnt->base, amode);
00502 }
00503 
00504 static int filt_rename(ventry *ve, ventry *newve)
00505 {
00506     return -EXDEV;
00507 }
00508 
00509 static int filt_unlink(ventry *ve)
00510 {
00511     return av_unlink(ve->mnt->base);
00512 }
00513 
00514 static void filt_destroy(struct avfs *avfs)
00515 {
00516     struct filtdata *filtdat = (struct filtdata *) avfs->data;
00517 
00518     filt_free_prog(filtdat->prog);
00519     filt_free_prog(filtdat->revprog);
00520     av_free(filtdat);
00521 }
00522 
00523 int av_init_filt(struct vmodule *module, int version, const char *name,
00524                  const char *prog[], const char *revprog[],
00525                  struct ext_info *exts, struct avfs **resp)
00526 {
00527     int res;
00528     struct avfs *avfs;
00529     struct filtdata *filtdat;
00530     
00531     res = av_new_avfs(name, exts, version, AVF_NOLOCK, module, &avfs);
00532     if(res < 0)
00533         return res;
00534 
00535     AV_NEW(filtdat);
00536     filtdat->prog = filt_copy_prog(prog);
00537     filtdat->revprog = filt_copy_prog(revprog);
00538 
00539     avfs->data = filtdat;
00540 
00541     avfs->destroy  = filt_destroy;
00542     avfs->lookup   = filt_lookup;
00543     avfs->access   = filt_access;
00544     avfs->unlink   = filt_unlink;
00545     avfs->rename   = filt_rename;  
00546     avfs->open     = filt_open;
00547     avfs->close    = filt_close; 
00548     avfs->read     = filt_read;
00549     avfs->write    = filt_write;
00550     avfs->getattr  = filt_getattr;
00551     avfs->setattr  = filt_setattr;
00552     avfs->truncate = filt_truncate;
00553 
00554     av_add_avfs(avfs);
00555     
00556     *resp = avfs;
00557 
00558     return 0;
00559 }