Back to index

avfs  1.0.1
extfs.c
Go to the documentation of this file.
00001 /*
00002     AVFS: A Virtual File System Library
00003     Copyright (C) 1998  Miklos Szeredi <miklos@szeredi.hu>
00004     Copyright (C) 2006  Ralf Hoffmann (ralf@boomerangsworld.de)
00005     
00006     This program can be distributed under the terms of the GNU GPL.
00007     See the file COPYING.
00008 
00009     EXTFS module
00010 
00011     This module is partly based on the 'extfs.c' module of 
00012     Midnight Commander VFS, by Jakub Jelinek and Pavel Machek.
00013 */
00014 
00015 #include "archive.h"
00016 #include "version.h"
00017 #include "filebuf.h"
00018 #include "parsels.h"
00019 #include "realfile.h"
00020 #include "runprog.h"
00021 #include "filecache.h"
00022 #include "cache.h"
00023 #include "exit.h"
00024 #include "tmpfile.h"
00025 
00026 #include <unistd.h>
00027 #include <fcntl.h>
00028 
00029 struct extfsdata {
00030     int needbase;
00031     char *progpath;
00032 };
00033 
00034 struct extfsnode {
00035     char *fullpath;
00036     avmutex lock;
00037 };
00038 
00039 struct extfscacheentry {
00040     char *tmpfile;
00041 };
00042 
00043 struct extfsfile {
00044     struct extfscacheentry *cent;
00045     int fd;
00046 };
00047 
00048 static void extfscacheentry_delete(struct extfscacheentry *cent)
00049 {
00050     if( cent->tmpfile != NULL ) {
00051         av_del_tmpfile(cent->tmpfile);
00052     }
00053 }
00054 
00055 static void fill_extfs_link(struct archive *arch, struct entry *ent,
00056                            char *linkname)
00057 {
00058     struct entry *link;
00059     struct archnode *nod = NULL;
00060 
00061     link = av_arch_resolve(arch, linkname, 0, 0);
00062     if(link != NULL)
00063         nod = (struct archnode *) av_namespace_get(link);
00064 
00065     if(nod == NULL || AV_ISDIR(nod->st.mode))
00066         av_log(AVLOG_WARNING, "EXTFS: Illegal hard link");
00067     else {
00068         nod->st.nlink ++;
00069         av_namespace_set(ent, nod);
00070         av_ref_obj(ent);
00071         av_ref_obj(nod);
00072     }
00073 
00074     av_unref_obj(link);
00075 }
00076 
00077 static void extfsnode_delete(struct extfsnode *enod)
00078 {
00079     av_free(enod->fullpath);
00080     AV_FREELOCK(enod->lock);
00081 }
00082 
00083 static void fill_extfs_node(struct archive *arch, struct entry *ent, 
00084                             struct avstat *stbuf, char *path, char *linkname)
00085 {
00086     struct archnode *nod;
00087     struct extfsnode *enod;
00088     char *s;
00089 
00090     nod = av_arch_new_node(arch, ent, AV_ISDIR(stbuf->mode));
00091         
00092     stbuf->dev = nod->st.dev;
00093     stbuf->ino = nod->st.ino;
00094     stbuf->nlink = nod->st.nlink;
00095 
00096     nod->st = *stbuf;
00097     nod->offset = 0;
00098     nod->realsize = 0;
00099 
00100     AV_NEW_OBJ(enod, extfsnode_delete);
00101 
00102     AV_INITLOCK(enod->lock);
00103 
00104     nod->data = enod;
00105 
00106     /* Fullpath should be without leading slashes */
00107     for(s = path; *s && *s == '/'; s++);
00108     enod->fullpath = av_strdup(s);
00109 
00110     if(AV_ISLNK(stbuf->mode))
00111         nod->linkname = av_strdup(linkname);
00112 }
00113 
00114 
00115 static void insert_extfs_entry(struct archive *arch, struct avstat *stbuf,
00116                            char *path, char *linkname)
00117 {
00118     struct entry *ent;
00119 
00120     if(!path[0])
00121         return;
00122 
00123     ent = av_arch_create(arch, path, 0);
00124     if(ent == NULL)
00125         return;
00126 
00127     /* if linkname is not null but mode is not a link
00128        then this should be a hard link */
00129     if(linkname != NULL && !AV_ISLNK(stbuf->mode)) 
00130         fill_extfs_link(arch, ent, linkname);
00131     else
00132         fill_extfs_node(arch, ent, stbuf, path, linkname);
00133 
00134     av_unref_obj(ent);
00135 }
00136 
00137 static void parse_extfs_line(struct lscache *lc, char *line,
00138                              struct archive *arch)
00139 {
00140     int res;
00141     char *filename;
00142     char *linkname;
00143     struct avstat stbuf;
00144     
00145     res = av_parse_ls(lc, line, &stbuf, &filename, &linkname);
00146     if(res != 1)
00147         return;
00148     
00149     insert_extfs_entry(arch, &stbuf, filename, linkname);
00150     av_free(filename);
00151     av_free(linkname);
00152 }
00153 
00154 static int read_extfs_list(struct program *pr, struct lscache *lc,
00155                            struct archive *arch)
00156 {
00157     int res;
00158 
00159     while(1) {
00160         char *line;
00161 
00162         res = av_program_getline(pr, &line, -1);
00163         if(res <= 0)
00164             return res;
00165         if(line == NULL)
00166             return 0;
00167         parse_extfs_line(lc, line, arch);
00168         av_free(line);
00169     }
00170 }
00171 
00172 static int extfs_list(void *data, ventry *ve, struct archive *arch)
00173 {
00174     int res;
00175     const char *prog[4];
00176     struct realfile *rf;
00177     struct program *pr;
00178     struct extfsdata *info = (struct extfsdata *) data;    
00179 
00180     if(info->needbase) {
00181         res = av_get_realfile(ve->mnt->base, &rf);
00182         if(res < 0)
00183             return res;
00184     }
00185     else
00186         rf = NULL;
00187     
00188     prog[0] = info->progpath;
00189     prog[1] = "list";
00190     prog[2] = rf == NULL ? NULL : rf->name;
00191     prog[3] = NULL;
00192 
00193     res = av_start_program(prog, &pr);
00194     if(res == 0) {
00195         struct lscache *lc = av_new_lscache();
00196         res = read_extfs_list(pr, lc, arch);
00197         av_unref_obj(lc);
00198         av_unref_obj(pr);
00199     }
00200     av_unref_obj(rf);
00201 
00202     return res;
00203 }
00204 
00205 static int get_key_for_node(ventry *ve, struct archfile *fil, char **resp)
00206 {
00207     struct extfsnode *enod = (struct extfsnode *) fil->nod->data;
00208     char *key;
00209     int res;
00210 
00211     if(enod == NULL) {
00212         return -EISDIR;
00213     }
00214 
00215     res = av_filecache_getkey(ve, &key);
00216     if(res < 0)
00217         return res;
00218 
00219     key = av_stradd(key, "/", enod->fullpath, NULL);
00220     *resp = key;
00221     return 0;
00222 }
00223 
00224 static int get_extfs_file(ventry *ve, struct archfile *fil,
00225                           const char *tmpfile)
00226 {
00227     int res;
00228     struct archparams *ap = (struct archparams *) ve->mnt->avfs->data;
00229     struct extfsdata *info = (struct extfsdata *) ap->data;
00230     struct extfsnode *enod = (struct extfsnode *) fil->nod->data;
00231     const char *prog[6];
00232     struct realfile *rf;
00233 
00234     if(enod == NULL) {
00235         /* no extfsnode means someone tries to access the extfs
00236           archive as a file (e.g. open( "test.lha#" ) )
00237           Although open on a directory is not forbidden we cannot
00238           create an appropriate tmpfile so we return EISDIR */
00239         return -EISDIR;
00240     }
00241   
00242     if(info->needbase) {
00243         res = av_get_realfile(ve->mnt->base, &rf);
00244         if(res < 0)
00245             return res;
00246     }
00247     else 
00248         rf = NULL;
00249   
00250     prog[0] = info->progpath;
00251     prog[1] = "copyout";
00252     prog[2] = rf == NULL ? "/" : rf->name;
00253     prog[3] = enod->fullpath;
00254     prog[4] = tmpfile;
00255     prog[5] = NULL;
00256   
00257     res = av_run_program(prog);
00258     av_unref_obj(rf);
00259 
00260     return res;
00261 }
00262 
00263 static struct ext_info *create_exts(char *line)
00264 {
00265     struct ext_info *exts;
00266     char *elist, *newelist;
00267     int i, n;
00268   
00269     while(*line && !isspace((unsigned char) *line)) line++;
00270     if(*line) *line++ = '\0';
00271     while(isspace((unsigned char) *line)) line++;
00272     elist = line;
00273 
00274     for(n = 0; *line && *line != '#'; n++) {
00275         while(*line && !isspace((unsigned char) *line)) line++;
00276         while(isspace((unsigned char) *line)) line++;
00277     }
00278     if(!n) return NULL;  /* No extensions */
00279   
00280     exts = av_malloc((n + 1) * sizeof(*exts) + strlen(elist) + 1);
00281 
00282     newelist = (char *) (&exts[n+1]);
00283     strcpy(newelist, elist);
00284   
00285     for(i = 0; i < n; i++) {
00286         exts[i].from = newelist;
00287         exts[i].to   = NULL;
00288         while(*newelist && !isspace((unsigned char) *newelist)) newelist++;
00289         if(*newelist) *newelist++ = '\0';
00290         while(isspace((unsigned char) *newelist)) newelist++;
00291 
00292     }
00293     exts[n].from = NULL;
00294     exts[n].to   = NULL;
00295 
00296     return exts;
00297 }
00298 
00299 static avssize_t extfs_read(vfile *vf, char *buf, avsize_t nbyte)
00300 {
00301     avssize_t res;
00302     struct archfile *fil = arch_vfile_file(vf);
00303     struct extfsfile *efil = (struct extfsfile *) fil->data;
00304 
00305     if(lseek(efil->fd, vf->ptr, SEEK_SET) == -1)
00306         return -errno;
00307 
00308     res = read(efil->fd, buf, nbyte);
00309     if(res == -1)
00310         return -errno;
00311 
00312     vf->ptr += res;
00313 
00314     return res;
00315 }
00316 
00317 static int extfs_open(ventry *ve, struct archfile *fil)
00318 {
00319     int res;
00320     struct extfsfile *efil;
00321     struct extfsnode *enod = (struct extfsnode *) fil->nod->data;
00322     int fd;
00323     char *key;
00324     struct extfscacheentry *cent;
00325     
00326     /* get key for extfscache */
00327     res = get_key_for_node(ve, fil, &key);
00328     if(res < 0)
00329         return res;
00330 
00331     AV_LOCK(enod->lock);
00332     cent = av_cache2_get(key);
00333     if (cent == NULL) {
00334         char *tmpfile;
00335         avoff_t tmpsize;
00336 
00337        /* no entry in cache so create a temporary file... */
00338         res = av_get_tmpfile(&tmpfile);
00339         if(res < 0) {
00340            av_free(key);
00341            AV_UNLOCK(enod->lock);
00342             return res;
00343        }
00344        res = get_extfs_file(ve, fil, tmpfile);
00345        if(res < 0) {
00346            av_free(key);
00347            av_del_tmpfile(tmpfile);
00348            AV_UNLOCK(enod->lock);
00349            return res;
00350        }
00351 
00352        /* ...create an object to store tmpfile */
00353        AV_NEW_OBJ(cent, extfscacheentry_delete);
00354        cent->tmpfile = tmpfile;
00355 
00356        /* put it in the extfscache */
00357        av_cache2_set(cent,key);
00358        AV_UNLOCK(enod->lock);
00359 
00360         tmpsize = av_tmpfile_blksize(tmpfile);
00361         if(tmpsize > 0)
00362             av_cache2_setsize(key, tmpsize);
00363     } else {
00364        AV_UNLOCK(enod->lock);
00365     }
00366     av_free(key);
00367 
00368     fd = open(cent->tmpfile, O_RDONLY);
00369     if(fd == -1) {
00370         res = -errno; 
00371         av_log(AVLOG_ERROR, "EXTFS: Could not open %s: %s", cent->tmpfile,
00372                strerror(errno));
00373        av_unref_obj(cent);
00374         return res;
00375     }
00376 
00377     AV_NEW(efil);
00378     efil->cent = cent;
00379     efil->fd = fd;
00380 
00381     fil->data = efil;
00382     
00383     return 0;
00384 }
00385 
00386 static int extfs_close(struct archfile *fil)
00387 {
00388     struct extfsfile *efil = (struct extfsfile *) fil->data;
00389 
00390     close(efil->fd);
00391 
00392     av_unref_obj(efil->cent);
00393     av_free(efil);
00394     
00395     return 0;
00396 }
00397 
00398 static void extfsdata_delete(struct extfsdata *info)
00399 {
00400     av_free(info->progpath);
00401 }
00402 
00403 static int create_extfs_handler(struct vmodule *module, const char *extfs_dir,
00404                                 char *name)
00405 {
00406     int res;
00407     struct avfs *avfs;
00408     struct archparams *ap;
00409     struct extfsdata *info;
00410     struct ext_info *extlist;
00411     int needbase;
00412     int end;
00413 
00414     /* Creates extension list, and strips name of the extensions */
00415     extlist = create_exts(name);
00416     end = strlen(name) - 1;
00417 
00418     if(name[end] == ':') {
00419         needbase = 0;
00420         name[end] = '\0';
00421     }
00422     else
00423         needbase = 1;
00424 
00425     res = av_archive_init(name, extlist, AV_VER, module, &avfs);
00426     av_free(extlist);
00427     if(res < 0)
00428         return res;
00429 
00430     ap = (struct archparams *) avfs->data;
00431 
00432     /* FIXME: If there is no basefile then cache the listing forever? */
00433     AV_NEW_OBJ(info, extfsdata_delete);
00434     ap->data = info;
00435     ap->parse = extfs_list;
00436     ap->read = extfs_read;
00437     ap->open = extfs_open;
00438     ap->close = extfs_close;
00439 
00440     if(!needbase)
00441         ap->flags |= ARF_NOBASE;
00442   
00443     info->progpath = av_stradd(NULL, extfs_dir, "/", name, NULL);
00444     info->needbase = needbase;
00445     
00446     av_add_avfs(avfs);
00447 
00448     return 0;
00449 }
00450 
00451 static int extfs_init(struct vmodule *module)
00452 {
00453     char *extfs_dir, *extfs_conf;
00454     struct filebuf *fb;
00455     int fd;
00456     int res;
00457     char *line;
00458     char *c;
00459 
00460     extfs_dir = av_get_config("moduledir");
00461     extfs_dir = av_stradd(extfs_dir, "/extfs", NULL);
00462     extfs_conf = av_stradd(NULL, extfs_dir, "/extfs.ini", NULL);
00463 
00464     fd = open(extfs_conf, O_RDONLY);
00465     if(fd == -1) {
00466         res = -errno;
00467         av_log(AVLOG_WARNING, "Could not open extfs config file %s: %s", 
00468                  extfs_conf, strerror(errno));
00469         av_free(extfs_conf);
00470         av_free(extfs_dir);
00471         return res;
00472     }
00473     av_free(extfs_conf);
00474   
00475     fb = av_filebuf_new(fd, 0);
00476 
00477     while(1) {
00478         res = av_filebuf_getline(fb, &line, -1);
00479         if(res < 0 || line == NULL)
00480             break;
00481 
00482         if (*line != '#') {
00483             c = line + strlen(line) - 1;
00484             if(*c == '\n') *c-- = '\0';
00485 
00486             if(*line) 
00487                 res = create_extfs_handler(module, extfs_dir, line);
00488         }
00489         av_free(line);
00490         if(res < 0) 
00491             break;
00492     }
00493     av_unref_obj(fb);
00494     av_free(extfs_dir);
00495     
00496     if(res < 0)
00497         return res;
00498 
00499     return 0;
00500 }
00501 
00502 extern int av_init_module_extfs(struct vmodule *module);
00503 
00504 int av_init_module_extfs(struct vmodule *module)
00505 {
00506     return extfs_init(module);
00507 }