Back to index

avfs  1.0.1
archive.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 "archint.h"
00010 #include "namespace.h"
00011 #include "filecache.h"
00012 #include "internal.h"
00013 #include "oper.h"
00014 
00015 static struct archent *arch_ventry_entry(ventry *ve)
00016 {
00017     return (struct archent *) ve->data;
00018 }
00019 
00020 static void arch_free_tree(struct entry *parent)
00021 {
00022     struct entry *ent;
00023     struct archnode *nod;
00024 
00025     ent = av_namespace_subdir(NULL, parent);
00026     while(ent != NULL) {
00027         struct entry *next;
00028         
00029         arch_free_tree(ent);
00030         next = av_namespace_next(ent);
00031         av_unref_obj(ent);
00032         ent = next;
00033     }
00034     
00035     nod = (struct archnode *) av_namespace_get(parent);
00036     av_unref_obj(nod);
00037     av_unref_obj(parent);
00038 }
00039 
00040 static void arch_delete(struct archive *arch)
00041 {
00042     struct entry *root;
00043 
00044     if(arch->ns != NULL) {
00045         root = av_namespace_subdir(arch->ns, NULL);
00046         arch_free_tree(root);
00047         av_unref_obj(root);
00048         av_unref_obj(arch->ns);
00049     }
00050 
00051     AV_FREELOCK(arch->lock);
00052 }
00053 
00054 static int arch_same(struct archive *arch, struct avstat *stbuf)
00055 {
00056     if(arch->st.ino == stbuf->ino &&
00057        arch->st.dev == stbuf->dev &&
00058        arch->st.size == stbuf->size &&
00059        AV_TIME_EQ(arch->st.mtime, stbuf->mtime))
00060         return 1;
00061     else
00062         return 0;
00063 }
00064 
00065 static int new_archive(ventry *ve, struct archive *arch)
00066 {
00067     int res;
00068     struct archparams *ap = (struct archparams *) ve->mnt->avfs->data;
00069     struct entry *root;
00070     struct avstat stbuf;
00071 
00072     arch->avfs = ve->mnt->avfs;
00073 
00074     if(!(ap->flags & ARF_NOBASE)) {
00075         res = av_getattr(ve->mnt->base, &arch->st, AVA_ALL & ~AVA_SIZE, 0);
00076         if(res < 0)
00077             return res;
00078     }
00079     
00080     arch->ns = av_namespace_new();
00081     root = av_namespace_lookup(arch->ns, NULL, "");
00082     av_arch_default_dir(arch, root);
00083     av_unref_obj(root);
00084 
00085     res = ap->parse(ap->data, ve, arch);
00086     if(res < 0)
00087         return res;
00088 
00089     if(!(ap->flags & ARF_NOBASE)) {
00090         /* The size is only requested _after_ the parse, so bzip2 &
00091            al. won't suffer. */
00092         res = av_getattr(ve->mnt->base, &stbuf, AVA_SIZE, 0);
00093         if(res < 0)
00094             return res;
00095     }
00096 
00097     arch->st.size = stbuf.size;
00098 
00099     arch->flags |= ARCHF_READY;
00100 
00101     return 0;
00102 }
00103 
00104 static int check_archive(ventry *ve, struct archive *arch, int *neednew)
00105 {
00106     int res;
00107     struct archparams *ap = (struct archparams *) ve->mnt->avfs->data;
00108     struct avstat stbuf;
00109     int attrmask = AVA_INO | AVA_DEV | AVA_SIZE | AVA_MTIME;
00110     
00111     if((ap->flags & ARF_NOBASE) != 0)
00112         return 0;
00113 
00114     res = av_getattr(ve->mnt->base, &stbuf, attrmask, 0);
00115     if(res < 0)
00116         return res;
00117 
00118     if(!arch_same(arch, &stbuf))
00119         *neednew = 1;
00120 
00121     return 0;
00122 }
00123 
00124 static struct archive *find_archive(const char *key)
00125 {
00126     struct archive *arch;
00127     static AV_LOCK_DECL(lock);
00128 
00129     AV_LOCK(lock);
00130     arch = (struct archive *) av_filecache_get(key);
00131     if(arch == NULL) {
00132         AV_NEW_OBJ(arch, arch_delete);
00133         AV_INITLOCK(arch->lock);
00134         arch->flags = 0;
00135         arch->ns = NULL;
00136         arch->numread = 0;
00137         av_filecache_set(key, arch);
00138     }
00139     AV_UNLOCK(lock);
00140 
00141     return arch;
00142 }
00143 
00144 static int get_archive(ventry *ve, struct archive **archp)
00145 {
00146     int res;
00147     char *key;
00148     struct archive *arch = NULL;
00149     int neednew;
00150     int tries;
00151 
00152     res = av_filecache_getkey(ve, &key);
00153     if(res < 0)
00154         return res;
00155 
00156     tries = 0;
00157     do {
00158         if(tries > 5) {
00159             av_log(AVLOG_ERROR, "ARCH: Giving up trying to create archive");
00160             res = -EIO;
00161             break;
00162         }
00163         arch = find_archive(key);
00164 
00165         neednew = 0;
00166         AV_LOCK(arch->lock);
00167         if(!(arch->flags & ARCHF_READY))
00168             res = new_archive(ve, arch);
00169         else
00170             res = check_archive(ve, arch, &neednew);
00171         if(res < 0 || neednew) {
00172             AV_UNLOCK(arch->lock);
00173             av_unref_obj(arch);
00174             av_filecache_set(key, NULL);
00175         }
00176         tries ++;
00177     } while(neednew);
00178 
00179     av_free(key);
00180     if(res < 0)
00181         return res;
00182 
00183     *archp = arch;
00184     return 0;
00185 }
00186 
00187 static int lookup_check_node(struct entry *ent, const char *name)
00188 {
00189     struct archnode *nod = (struct archnode *) av_namespace_get(ent);
00190     
00191     if(nod == NULL)
00192         return -ENOENT;
00193     
00194     if(name != NULL && !AV_ISDIR(nod->st.mode))
00195         return -ENOTDIR;
00196 
00197     return 0;
00198 }
00199 
00200 static int arch_lookup(ventry *ve, const char *name, void **newp)
00201 {
00202     int res;
00203     int type;
00204     struct archent *ae = arch_ventry_entry(ve);
00205     struct entry *ent;
00206     struct archive *arch;
00207  
00208     if(ae == NULL) {
00209         if(name[0] != '\0')
00210             return -ENOENT;
00211 
00212         AV_NEW(ae);
00213         ae->ent = NULL;
00214         res = get_archive(ve, &arch);
00215         if(res < 0) {
00216             av_free(ae);
00217             return res;
00218         }
00219         ae->arch = arch;
00220     }
00221     else {
00222         arch = ae->arch;
00223         AV_LOCK(arch->lock);
00224         res = lookup_check_node(ae->ent, name);
00225         if(res < 0) {
00226             AV_UNLOCK(arch->lock);
00227             return res;
00228         }
00229     }
00230 
00231     ent = av_namespace_lookup_all(arch->ns, ae->ent, name);
00232     av_unref_obj(ae->ent);
00233     if(ent == NULL) {
00234         av_unref_obj(ae->arch);
00235         av_free(ae);
00236         ae = NULL;
00237         type = 0;
00238     }
00239     else {
00240         struct archnode *nod = (struct archnode *) av_namespace_get(ent);
00241 
00242         if(nod != NULL)
00243             type = AV_TYPE(nod->st.mode);
00244         else
00245             type = 0;
00246 
00247         ae->ent = ent;        
00248     }
00249     AV_UNLOCK(arch->lock);
00250 
00251     *newp = ae;
00252     return type;
00253 }
00254 
00255 static void arch_putent(ventry *ve)
00256 {
00257     struct archent *ae = arch_ventry_entry(ve);
00258 
00259     av_unref_obj(ae->ent);
00260     av_unref_obj(ae->arch);
00261 
00262     av_free(ae);
00263 }
00264 
00265 static int arch_copyent(ventry *ve, void **resp)
00266 {
00267     struct archent *ae = arch_ventry_entry(ve);
00268     struct archent *nae;
00269 
00270     AV_NEW(nae);
00271     nae->ent = ae->ent;
00272     nae->arch = ae->arch;
00273     
00274     av_ref_obj(nae->ent);
00275     av_ref_obj(nae->arch);
00276 
00277     *resp = nae;
00278     return 0;
00279 }
00280 
00281 static int arch_getpath(ventry *ve, char **resp)
00282 {
00283     struct archent *ae = arch_ventry_entry(ve);
00284     
00285     *resp = av_namespace_getpath(ae->ent);
00286 
00287     return 0;
00288 }
00289 static int arch_real_open(int flags)
00290 {
00291     if((flags & AVO_DIRECTORY) == 0 && (flags & AVO_ACCMODE) != AVO_NOPERM)
00292         return 1;
00293     else
00294         return 0;
00295 }
00296 
00297 static void arch_do_close(struct archfile *fil, int realopen)
00298 {
00299     struct archive *arch = fil->arch;
00300     struct archparams *ap = (struct archparams *) arch->avfs->data;
00301 
00302     if(realopen) {
00303         if(fil->basefile != NULL) {
00304             arch->numread --;
00305             if(arch->numread == 0) {
00306                 av_close(arch->basefile);
00307                 arch->basefile = NULL;
00308             }
00309         }
00310 
00311         fil->nod->numopen --;
00312         if(fil->nod->numopen == 0 && ap->release != NULL)
00313             ap->release(arch, fil->nod);
00314     }
00315 
00316     av_unref_obj(fil->arch);
00317     av_unref_obj(fil->nod);
00318     av_unref_obj(fil->ent);
00319     av_unref_obj(fil->curr);
00320     av_free(fil);
00321 }
00322 
00323 static int arch_do_open(ventry *ve, int flags, avmode_t mode, void **resp)
00324 {
00325     int res;
00326     struct archent *ae = arch_ventry_entry(ve);
00327     struct archfile *fil;
00328     struct archnode *nod = (struct archnode *) av_namespace_get(ae->ent);
00329     struct archive *arch = ae->arch;
00330     struct archparams *ap = (struct archparams *) ve->mnt->avfs->data;
00331     vfile *basefile = NULL;
00332     int realopen;
00333    
00334     if(nod == NULL)
00335         return -ENOENT;
00336 
00337     if(AV_ISWRITE(flags))
00338         return -EROFS;
00339 
00340     if((flags & AVO_DIRECTORY) != 0 && !AV_ISDIR(nod->st.mode))
00341         return -ENOTDIR;
00342     
00343     realopen = arch_real_open(flags);
00344     if(realopen) {
00345         if(!(ap->flags & ARF_NOBASE)) {
00346             if(arch->basefile == NULL) {
00347                 res = av_open(ve->mnt->base, AVO_RDONLY, 0, &arch->basefile);
00348                 if(res < 0)
00349                     return res;
00350             }
00351 
00352             arch->numread ++;
00353             basefile = arch->basefile;
00354         }
00355 
00356         nod->numopen ++;
00357     }
00358     
00359     AV_NEW(fil);
00360     fil->basefile = basefile;
00361     fil->arch = arch;
00362     fil->nod = nod;
00363     fil->data = NULL;
00364     
00365     if((flags & AVO_DIRECTORY))
00366         fil->ent = ae->ent;
00367     else
00368         fil->ent = NULL;
00369 
00370     fil->curr = NULL;
00371     fil->currn = -1;
00372 
00373     av_ref_obj(fil->arch);
00374     av_ref_obj(fil->nod);
00375     av_ref_obj(fil->ent);
00376 
00377     if(realopen && ap->open != NULL) {
00378         res = ap->open(ve, fil);
00379         if(res < 0) {
00380             arch_do_close(fil, realopen);
00381             return res;
00382         }
00383     }
00384 
00385     *resp = fil;
00386     return 0;
00387 }
00388 
00389 
00390 static int arch_open(ventry *ve, int flags, avmode_t mode, void **resp)
00391 {
00392     int res;
00393     struct archent *ae = arch_ventry_entry(ve);
00394     struct archive *arch = ae->arch;
00395  
00396     AV_LOCK(arch->lock);
00397     res = arch_do_open(ve, flags, mode, resp);
00398     AV_UNLOCK(arch->lock);
00399     
00400     return res;
00401 }
00402 
00403 
00404 static int arch_close(vfile *vf)
00405 {
00406     int res;
00407     struct archfile *fil = arch_vfile_file(vf);
00408     struct archive *arch = fil->arch;
00409     struct archparams *ap = (struct archparams *) vf->mnt->avfs->data;
00410     int realopen = arch_real_open(vf->flags);
00411 
00412     AV_LOCK(arch->lock);
00413     if(realopen && ap->close != NULL)
00414         res = ap->close(fil);
00415     else
00416         res = 0;
00417     arch_do_close(fil, realopen);
00418     AV_UNLOCK(arch->lock);
00419 
00420     return res;
00421 }
00422 
00423 avssize_t av_arch_read(vfile *vf, char *buf, avsize_t nbyte)
00424 {
00425     int res;
00426     avoff_t realoff;
00427     struct archfile *fil = arch_vfile_file(vf);
00428     struct archnode *nod = fil->nod;
00429     avsize_t nact;
00430 
00431     if(AV_ISDIR(nod->st.mode))
00432         return -EISDIR;
00433 
00434     if(nbyte == 0 || vf->ptr >= nod->realsize)
00435         return 0;
00436 
00437     realoff = vf->ptr + nod->offset;
00438     nact = AV_MIN(nbyte, (avsize_t) (nod->realsize - vf->ptr));
00439 
00440     res = av_pread(fil->basefile, buf, nact, realoff);
00441     if(res > 0)
00442         vf->ptr += res;
00443 
00444     return res;
00445 }
00446 
00447 static avssize_t arch_read(vfile *vf, char *buf, avsize_t nbyte)
00448 {
00449     avssize_t res;
00450     struct archfile *fil = arch_vfile_file(vf);
00451     struct archive *arch = fil->arch;
00452     struct archparams *ap = (struct archparams *) vf->mnt->avfs->data;
00453     
00454     AV_LOCK(arch->lock);
00455     if(AV_ISDIR(fil->nod->st.mode))
00456        res = -EISDIR;
00457     else
00458        res =  ap->read(vf, buf, nbyte);
00459     AV_UNLOCK(arch->lock);
00460 
00461     return res;
00462 }
00463 
00464 static struct archnode *arch_special_entry(int n, struct entry *ent,
00465                                            char **namep)
00466 {
00467     struct archnode *nod;
00468 
00469     if(n == 0) {
00470         *namep = av_strdup(".");
00471         nod = (struct archnode *) av_namespace_get(ent);
00472         return nod;
00473     }
00474     else {
00475         struct entry *parent;
00476 
00477         *namep = av_strdup("..");
00478         parent = av_namespace_parent(ent);
00479         if(parent != NULL)
00480             nod = (struct archnode *) av_namespace_get(parent);
00481         else
00482             nod = (struct archnode *) av_namespace_get(ent);
00483 
00484         av_unref_obj(parent);
00485         return nod;
00486     }
00487 }
00488 
00489 static struct archnode *arch_nth_entry(int n, struct archfile *fil,
00490                                    char **namep)
00491 {
00492     struct entry *ent;
00493     struct archnode *nod;
00494 
00495     if(n  < 2)
00496         return arch_special_entry(n, fil->ent, namep);
00497     
00498     n -= 2;
00499     if(n == 0 || fil->currn != n - 1)
00500        ent = av_namespace_nth(NULL, fil->ent, n);
00501     else
00502        ent = av_namespace_next(fil->curr);
00503 
00504     av_unref_obj(fil->curr);
00505     fil->curr = ent;
00506     fil->currn = n;
00507 
00508     if(ent == NULL)
00509         return NULL;
00510 
00511     *namep = av_namespace_name(ent);
00512     nod = (struct archnode *) av_namespace_get(ent);
00513 
00514     return nod;
00515 }
00516 
00517 static int arch_readdir(vfile *vf, struct avdirent *buf)
00518 {
00519     int res;
00520     struct archfile *fil = arch_vfile_file(vf);
00521     struct archive *arch = fil->arch;
00522     struct archnode *nod;
00523     char *name;
00524 
00525     AV_LOCK(arch->lock);
00526     nod = arch_nth_entry(vf->ptr, fil, &name);
00527     if(nod == NULL)
00528         res = 0;
00529     else {
00530         buf->name = name;
00531         buf->ino = nod->st.ino;
00532         buf->type = AV_TYPE(nod->st.mode);
00533         
00534         vf->ptr ++;
00535         res = 1;
00536     }
00537     AV_UNLOCK(arch->lock);
00538 
00539     return res;
00540 }
00541 
00542 static int arch_getattr(vfile *vf, struct avstat *buf, int attrmask)
00543 {
00544      struct archfile *fil = arch_vfile_file(vf);
00545      struct archnode *nod = fil->nod;
00546      struct archive *arch = fil->arch;
00547     
00548      AV_LOCK(arch->lock);
00549      *buf = nod->st;
00550      AV_UNLOCK(arch->lock);
00551 
00552      return 0;
00553 }
00554 
00555 static int arch_access(ventry *ve, int amode)
00556 {
00557     if((amode & AVW_OK) != 0)
00558         return -EACCES;
00559 
00560     return 0;
00561 }
00562 
00563 static int arch_readlink(ventry *ve, char **bufp)
00564 {
00565     int res;
00566     struct archent *ae = arch_ventry_entry(ve);
00567     struct archnode *nod;
00568     struct archive *arch = ae->arch;
00569 
00570     AV_LOCK(arch->lock);
00571     nod = (struct archnode *) av_namespace_get(ae->ent);
00572     if(nod == NULL)
00573         res = -ENOENT;
00574     else if(!AV_ISLNK(nod->st.mode))
00575         res = -EINVAL;
00576     else if(nod->linkname == NULL) {
00577        av_log(AVLOG_ERROR, "ARCH: linkname is NULL");
00578        res = -EIO;
00579     }
00580     else {
00581         *bufp = av_strdup(nod->linkname);
00582         res = 0;
00583     }
00584     AV_UNLOCK(arch->lock);    
00585 
00586     return res;
00587 }
00588 
00589 static void arch_destroy(struct avfs *avfs)
00590 {
00591     struct archparams *ap = (struct archparams *) avfs->data;
00592 
00593     av_unref_obj(ap->data);
00594     av_free(ap);
00595 }
00596 
00597 
00598 int av_archive_init(const char *name, struct ext_info *exts, int version,
00599                     struct vmodule *module, struct avfs **avfsp)
00600 {
00601     int res;
00602     struct avfs *avfs;
00603     struct archparams *ap;
00604 
00605     res = av_new_avfs(name, exts, version, AVF_NOLOCK, module, &avfs);
00606     if(res < 0)
00607         return res;
00608 
00609     avfs->lookup    = arch_lookup;
00610     avfs->putent    = arch_putent;
00611     avfs->copyent   = arch_copyent;
00612     avfs->getpath   = arch_getpath;
00613     avfs->open      = arch_open;
00614     avfs->close     = arch_close;
00615     avfs->read      = arch_read;
00616     avfs->readdir   = arch_readdir;
00617     avfs->getattr   = arch_getattr;
00618     avfs->access    = arch_access;
00619     avfs->readlink  = arch_readlink;
00620     avfs->destroy   = arch_destroy;
00621 
00622     AV_NEW(ap);
00623     ap->data = NULL;
00624     ap->flags = 0;
00625     ap->parse = NULL;
00626     ap->open = NULL;
00627     ap->close = NULL;
00628     ap->read = av_arch_read;
00629     ap->release = NULL;
00630 
00631     avfs->data = ap;
00632 
00633     *avfsp = avfs;
00634     
00635     return 0;
00636 }
00637