Back to index

avfs  1.0.1
ugz.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     GUNZIP module
00009 */
00010 
00011 #include "zfile.h"
00012 #include "zipconst.h"
00013 #include "filecache.h"
00014 #include "cache.h"
00015 #include "oper.h"
00016 #include "version.h"
00017 
00018 struct gznode {
00019     avmutex lock;
00020     int ready;
00021     struct avstat sig;
00022     struct cacheobj *cache;
00023     avino_t ino;
00024     avoff_t size;
00025     avoff_t dataoff;
00026     avuint crc;
00027     avtime_t mtime;
00028 };
00029 
00030 struct gzfile {
00031     struct zfile *zfil;
00032     vfile *base;
00033     struct gznode *node;
00034     int validsize;
00035 };
00036 
00037 #define GZBUFSIZE 1024
00038 
00039 struct gzbuf {
00040     vfile *vf;
00041     unsigned char buf[GZBUFSIZE];
00042     unsigned char *next;
00043     unsigned int avail;
00044     unsigned int total;
00045 };
00046 
00047 #define GZHEADER_SIZE 10
00048 #define GZFOOTER_SIZE 8
00049 
00050 #define GZMAGIC1 0x1f
00051 #define GZMAGIC2 0x8b
00052 
00053 /* gzip flag byte */
00054 #define GZFL_ASCII        0x01 /* bit 0 set: file probably ascii text */
00055 #define GZFL_CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
00056 #define GZFL_EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
00057 #define GZFL_ORIG_NAME    0x08 /* bit 3 set: original file name present */
00058 #define GZFL_COMMENT      0x10 /* bit 4 set: file comment present */
00059 #define GZFL_RESERVED     0xE0 /* bits 5..7: reserved */
00060 
00061 
00062 #define BI(ptr, i)  ((avbyte) (ptr)[i])
00063 #define DBYTE(ptr) (BI(ptr,0) | (BI(ptr,1)<<8))
00064 #define QBYTE(ptr) ((avuint) (BI(ptr,0) | (BI(ptr,1)<<8) | \
00065                    (BI(ptr,2)<<16) | (BI(ptr,3)<<24)))
00066 
00067 static int gzbuf_getbyte(struct gzbuf *gb)
00068 {
00069     if(gb->avail == 0) {
00070         avssize_t res = av_read(gb->vf, (char*)( gb->buf ), GZBUFSIZE);
00071         if(res < 0)
00072             return res;
00073 
00074         gb->next = gb->buf;
00075         gb->avail = res;
00076     }
00077     
00078     if(gb->avail == 0) {
00079         av_log(AVLOG_ERROR, "UGZ: Premature end of file");
00080         return -EIO;
00081     }
00082     
00083     gb->avail --;
00084     gb->total ++;
00085     return *gb->next ++;
00086 }
00087 
00088 static int gzbuf_skip_string(struct gzbuf *gb)
00089 {
00090     int c;
00091 
00092     do {
00093         c = gzbuf_getbyte(gb);
00094         if(c < 0)
00095             return c;
00096     } while(c != '\0');
00097 
00098     return 0;
00099 }
00100 
00101 static int gzbuf_read(struct gzbuf *gb, char *buf, avsize_t nbyte)
00102 {
00103     for(; nbyte; nbyte--) {
00104         int c = gzbuf_getbyte(gb);
00105         if(c < 0)
00106             return c;
00107 
00108         *buf ++ = c;
00109     }
00110     return 0;
00111 }
00112 
00113 static int gz_read_header(vfile *vf, struct gznode *nod)
00114 {
00115     int res;
00116     avoff_t sres;
00117     struct gzbuf gb;
00118     unsigned char buf[GZHEADER_SIZE];
00119     int method, flags;
00120     
00121     gb.vf = vf;
00122     gb.avail = 0;
00123     gb.total = 0;
00124 
00125     res = gzbuf_read(&gb, (char*)buf, GZHEADER_SIZE);
00126     if(res < 0)
00127         return res;
00128 
00129     if(buf[0] != GZMAGIC1 || buf[1] != GZMAGIC2) {
00130         av_log(AVLOG_ERROR, "UGZ: File not in GZIP format");
00131         return -EIO;
00132     }
00133 
00134     method = buf[2];
00135     flags  = buf[3];
00136   
00137     if(method != METHOD_DEFLATE) {
00138         av_log(AVLOG_ERROR, "UGZ: File compression is not DEFLATE");
00139         return -EIO;
00140     }
00141 
00142     if(flags & GZFL_RESERVED) {
00143         av_log(AVLOG_ERROR, "UGZ: Unknown flags");
00144         return -EIO;
00145     }
00146 
00147     nod->mtime = QBYTE(buf + 4);
00148   
00149     /* Ignore bytes 8 and 9 */
00150 
00151     if((flags & GZFL_CONTINUATION) != 0) {
00152         res = gzbuf_read(&gb, (char*)buf, 2);
00153         if(res < 0)
00154             return res;
00155     }
00156 
00157     if((flags & GZFL_EXTRA_FIELD) != 0) {
00158         avsize_t len;
00159 
00160         res = gzbuf_read(&gb, (char*)buf, 2);
00161         if(res < 0)
00162             return res;
00163 
00164         for(len = DBYTE(buf); len; len--) {
00165             res = gzbuf_getbyte(&gb);
00166             if(res < 0)
00167                 return res;
00168         }
00169     }
00170 
00171     if((flags & GZFL_ORIG_NAME) != 0) {
00172         res = gzbuf_skip_string(&gb);
00173         if(res < 0)
00174             return res;
00175     }
00176 
00177     if((flags & GZFL_COMMENT) != 0) {
00178         res = gzbuf_skip_string(&gb);
00179         if(res < 0)
00180             return res;
00181     }
00182 
00183     nod->dataoff = gb.total;
00184 
00185     sres = av_lseek(vf, -8, AVSEEK_END);
00186     if(sres < 0)
00187         return sres;
00188 
00189     res = av_read_all(vf, (char*)buf, 8);
00190     if(res < 0)
00191         return res;
00192 
00193     nod->crc = QBYTE(buf);
00194     nod->size = QBYTE(buf + 4);
00195 
00196     nod->ready = 1;
00197     
00198     return 0;
00199 }
00200 
00201 static void gznode_destroy(struct gznode *nod)
00202 {
00203     av_unref_obj(nod->cache);
00204     AV_FREELOCK(nod->lock);
00205 }
00206 
00207 static struct gznode *gz_new_node(ventry *ve, struct avstat *stbuf)
00208 {
00209     struct gznode *nod;
00210 
00211     AV_NEW_OBJ(nod, gznode_destroy);
00212     AV_INITLOCK(nod->lock);
00213     nod->ready = 0;
00214     nod->sig = *stbuf;
00215     nod->cache = NULL;
00216     nod->ino = av_new_ino(ve->mnt->avfs);
00217     
00218     return nod;
00219 }
00220 
00221 static int gz_same(struct gznode *nod, struct avstat *stbuf)
00222 {
00223     if(nod->sig.ino == stbuf->ino &&
00224        nod->sig.dev == stbuf->dev &&
00225        nod->sig.size == stbuf->size &&
00226        AV_TIME_EQ(nod->sig.mtime, stbuf->mtime))
00227         return 1;
00228     else
00229         return 0;
00230 }
00231 
00232 static struct gznode *gz_findnode(ventry *ve, const char *key,
00233                                   struct avstat *stbuf)
00234 {
00235     struct gznode *nod;
00236     static AV_LOCK_DECL(lock);
00237 
00238     AV_LOCK(lock);
00239     nod = (struct gznode *) av_filecache_get(key);
00240     if(nod != NULL) {
00241         if(!gz_same(nod, stbuf)) {
00242             av_unref_obj(nod);
00243             nod = NULL;
00244         }
00245     }
00246     
00247     if(nod == NULL) {
00248         nod = gz_new_node(ve, stbuf);
00249         av_filecache_set(key, nod);
00250     }
00251     AV_UNLOCK(lock);
00252 
00253     return nod;
00254 }
00255 
00256 static int gz_getnode(ventry *ve, vfile *base, struct gznode **resp)
00257 {
00258     int res;
00259     struct avstat stbuf;
00260     const int attrmask = AVA_INO | AVA_DEV | AVA_SIZE | AVA_MTIME;
00261     struct gznode *nod;
00262     char *key;
00263 
00264     res = av_fgetattr(base, &stbuf, attrmask);
00265     if(res < 0)
00266         return res;
00267 
00268     res = av_filecache_getkey(ve, &key);
00269     if(res < 0)
00270         return res;
00271     
00272     nod = gz_findnode(ve, key, &stbuf);
00273 
00274     AV_LOCK(nod->lock);
00275     if(!nod->ready) {
00276         res = gz_read_header(base, nod);
00277         if(res < 0)
00278             av_filecache_set(key, NULL);
00279     }
00280     else
00281         res = 0;
00282     AV_UNLOCK(nod->lock);
00283     av_free(key);
00284 
00285     if(res < 0) {
00286         av_unref_obj(nod);
00287         return res;
00288     }
00289 
00290     *resp = nod;
00291     return 0;
00292 }
00293 
00294 static struct zcache *gz_getcache(ventry *base, struct gznode *nod)
00295 {
00296     struct zcache *cache;
00297     
00298     cache = (struct zcache *) av_cacheobj_get(nod->cache);
00299     if(cache == NULL) {
00300         int res;
00301         char *name;
00302 
00303         res = av_generate_path(base, &name);
00304         if(res < 0)
00305             name = NULL;
00306         else
00307             name = av_stradd(name, "(index)", NULL);
00308 
00309         cache = av_zcache_new();
00310         av_unref_obj(nod->cache);
00311 
00312         /* FIXME: the cacheobj should only be created when the zcache
00313            is nonempty */
00314         nod->cache = av_cacheobj_new(cache, name);
00315         av_free(name); 
00316     }
00317 
00318     return cache;
00319 }
00320 
00321 static int gz_lookup(ventry *ve, const char *name, void **newp)
00322 {
00323     char *path = (char *) ve->data;
00324     
00325     if(path == NULL) {
00326         if(name[0] != '\0')
00327             return -ENOENT;
00328        if(ve->mnt->opts[0] != '\0' && strcmp(ve->mnt->opts, "-s") != 0)
00329             return -ENOENT;
00330         path = av_strdup(name);
00331     }
00332     else if(name == NULL) {
00333         av_free(path);
00334         path = NULL;
00335     }
00336     else 
00337         return -ENOENT;
00338     
00339     *newp = path;
00340     return 0;
00341 }
00342 
00343 static int gz_access(ventry *ve, int amode)
00344 {
00345     return av_access(ve->mnt->base, amode);
00346 }
00347 
00348 static int gz_open(ventry *ve, int flags, avmode_t mode, void **resp)
00349 {
00350     int res;
00351     vfile *base;
00352     struct gznode *nod;
00353     struct gzfile *fil;
00354 
00355     if(flags & AVO_DIRECTORY)
00356         return -ENOTDIR;
00357 
00358     if(AV_ISWRITE(flags))
00359         return -EROFS;
00360 
00361     res = av_open(ve->mnt->base, AVO_RDONLY, 0, &base);
00362     if(res < 0)
00363         return res;
00364 
00365     res = gz_getnode(ve, base, &nod);
00366     if(res < 0) {
00367         av_close(base);
00368         return res;
00369     }
00370 
00371     AV_NEW(fil);
00372     if((flags & AVO_ACCMODE) != AVO_NOPERM)
00373         fil->zfil = av_zfile_new(base, nod->dataoff, nod->crc, 1);
00374     else
00375         fil->zfil = NULL;
00376 
00377     fil->base = base;
00378     fil->node = nod;
00379     if(strcmp(ve->mnt->opts, "-s") == 0)
00380         fil->validsize = 1;
00381     else
00382         fil->validsize = 0;
00383     
00384     *resp = fil;
00385     return 0;
00386 }
00387 
00388 static int gz_close(vfile *vf)
00389 {
00390     struct gzfile *fil = (struct gzfile *) vf->data;
00391 
00392     av_unref_obj(fil->zfil);
00393     av_unref_obj(fil->node);
00394     av_close(fil->base);
00395     av_free(fil);
00396 
00397     return 0;
00398 }
00399 
00400 static avssize_t gz_read(vfile *vf, char *buf, avsize_t nbyte)
00401 {
00402     avssize_t res;
00403     struct gzfile *fil = (struct gzfile *) vf->data;
00404     struct zcache *zc;
00405     struct cacheobj *cobj;
00406 
00407     AV_LOCK(fil->node->lock);
00408     zc = gz_getcache(vf->mnt->base, fil->node);
00409     cobj = fil->node->cache;
00410     av_ref_obj(cobj);
00411     AV_UNLOCK(fil->node->lock);
00412 
00413     res = av_zfile_pread(fil->zfil, zc, buf, nbyte, vf->ptr);
00414     if(res >= 0) {
00415         vf->ptr += res;
00416 
00417         /* FIXME: should only be set when changed, ugly, UGLY, etc... */
00418         av_cacheobj_setsize(cobj, av_zcache_size(zc));
00419     }
00420     else {
00421         AV_LOCK(fil->node->lock);
00422         av_unref_obj(fil->node->cache);
00423         fil->node->cache = NULL;
00424         AV_UNLOCK(fil->node->lock);
00425     }
00426 
00427     av_unref_obj(zc);
00428     av_unref_obj(cobj);
00429 
00430     return res;
00431 }
00432 
00433 static int gz_getsize(vfile *vf, struct avstat *buf)
00434 {
00435     int res;
00436     struct gzfile *fil = (struct gzfile *) vf->data;
00437     struct zcache *zc;
00438     struct cacheobj *cobj;
00439     avoff_t size;
00440 
00441     AV_LOCK(fil->node->lock);
00442     zc = gz_getcache(vf->mnt->base, fil->node);
00443     cobj = fil->node->cache;
00444     av_ref_obj(cobj);
00445     AV_UNLOCK(fil->node->lock);
00446 
00447     res = av_zfile_size(fil->zfil, zc, &size);
00448     if(res == 0 && size == -1) {
00449         fil->zfil = av_zfile_new(fil->base, fil->node->dataoff,
00450                                  fil->node->crc, 0);
00451         res = av_zfile_size(fil->zfil, zc, &size);
00452     }
00453     buf->size = size;
00454 
00455     av_unref_obj(zc);
00456     av_unref_obj(cobj);
00457 
00458     return res;
00459 }
00460 
00461 static int gz_getattr(vfile *vf, struct avstat *buf, int attrmask)
00462 {
00463     int res;
00464     struct gzfile *fil = (struct gzfile *) vf->data;
00465     struct gznode *nod = fil->node;
00466     const int basemask = AVA_MODE | AVA_UID | AVA_GID | AVA_ATIME | AVA_CTIME |
00467            AVA_BLKCNT;
00468 
00469     res = av_fgetattr(fil->base, buf, basemask);
00470     if(res < 0)
00471         return res;
00472 
00473     buf->size = nod->size;
00474     if(!fil->validsize && (attrmask & (AVA_SIZE | AVA_BLKCNT)) != 0) {
00475         res = gz_getsize(vf, buf);
00476         if(res < 0)
00477             return res;
00478     }
00479 
00480     buf->mode &= ~(07000);
00481     buf->blksize = 4096;
00482     buf->dev = vf->mnt->avfs->dev;
00483     buf->ino = nod->ino;
00484     buf->nlink = 1;
00485     buf->mtime.sec = nod->mtime;
00486     buf->mtime.nsec = 0;
00487     
00488     return 0;
00489 }
00490 
00491 extern int av_init_module_ugz(struct vmodule *module);
00492 
00493 int av_init_module_ugz(struct vmodule *module)
00494 {
00495     int res;
00496     struct avfs *avfs;
00497     struct ext_info ugz_exts[3];
00498 
00499     ugz_exts[0].from = ".gz",  ugz_exts[0].to = NULL;
00500     ugz_exts[1].from = ".tgz", ugz_exts[1].to = ".tar";
00501     ugz_exts[2].from = NULL;
00502 
00503     res = av_new_avfs("ugz", ugz_exts, AV_VER, AVF_NOLOCK, module, &avfs);
00504     if(res < 0)
00505         return res;
00506 
00507     avfs->lookup   = gz_lookup;
00508     avfs->access   = gz_access;
00509     avfs->open     = gz_open;
00510     avfs->close    = gz_close; 
00511     avfs->read     = gz_read;
00512     avfs->getattr  = gz_getattr;
00513 
00514     av_add_avfs(avfs);
00515 
00516     return 0;
00517 }
00518