Back to index

avfs  1.0.1
urar.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     
00005     This program can be distributed under the terms of the GNU GPL.
00006     See the file COPYING.
00007 
00008     RAR module
00009     Copyright (C) 1998 David Hanak (dhanak@inf.bme.hu)
00010 */
00011 
00012 #include "archive.h"
00013 #include "realfile.h"
00014 #include "prog.h"
00015 #include "oper.h"
00016 #include "version.h"
00017 
00018 #include <fcntl.h>
00019 #include <unistd.h>
00020 
00021 #define DOS_DIR_SEP_CHAR  '\\'
00022 
00023 static avbyte good_marker_head[] = 
00024 { 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00 };
00025 
00026 #define LONG_HEAD_SIZE      11
00027 #define SHORT_HEAD_SIZE      7
00028 #define FILE_HEAD_SIZE      21
00029 #define MARKER_HEAD_SIZE (sizeof(good_marker_head)/sizeof(avbyte))
00030 
00031 typedef avbyte block_header[LONG_HEAD_SIZE];
00032 typedef avbyte file_header[FILE_HEAD_SIZE];
00033 
00034 #define BI(ptr, i)  ((avbyte) (ptr)[i])
00035 #define BYTE(ptr)  (BI(ptr,0))
00036 #define DBYTE(ptr) ((avushort)(BI(ptr,0) | (BI(ptr,1)<<8)))
00037 #define QBYTE(ptr) ((avuint)(BI(ptr,0) | (BI(ptr,1)<<8) | \
00038                    (BI(ptr,2)<<16) | (BI(ptr,3)<<24)))
00039 
00040 #define bh_CRC(bh)      DBYTE(bh     )
00041 #define bh_type(bh)     BYTE (bh +  2)
00042 #define bh_flags(bh)    DBYTE(bh +  3)
00043 #define bh_headsize(bh) DBYTE(bh +  5)
00044 #define bh_addsize(bh)  QBYTE(bh +  7)
00045 #define bh_size(bh)     (bh_headsize(bh) + bh_addsize(bh))
00046 
00047 #define fh_origsize(fh) QBYTE(fh     )
00048 #define fh_hostos(fh)   BYTE (fh +  4)
00049 #define fh_CRC(fh)      QBYTE(fh +  5)
00050 #define fh_time(fh)     QBYTE(fh +  9)
00051 #define fh_version(fh)  BYTE (fh + 13)
00052 #define fh_method(fh)   BYTE (fh + 14)
00053 #define fh_namelen(fh)  DBYTE(fh + 15)
00054 #define fh_attr(fh)     QBYTE(fh + 17)
00055 
00056 #define dos_ftsec(ft)   (int)( 2 * ((ft >>  0) & 0x1F))
00057 #define dos_ftmin(ft)   (int)(     ((ft >>  5) & 0x3F))
00058 #define dos_fthour(ft)  (int)(     ((ft >> 11) & 0x1F))
00059 #define dos_ftday(ft)   (int)(     ((ft >> 16) & 0x1F))
00060 #define dos_ftmonth(ft) (int)(-1 + ((ft >> 21) & 0x0F))
00061 #define dos_ftyear(ft)  (int)(80 + ((ft >> 25) & 0x7F))
00062 
00063 /* Block types */
00064 #define B_MARKER             0x72
00065 #define B_MAIN               0x73
00066 #define B_FILE               0x74
00067 #define B_COMMENT            0x75
00068 #define B_EXTRA_INFO         0x76
00069 #define B_SUB                0x77
00070 #define B_RECOVERY           0x78
00071 
00072 /* Block flags */
00073 #define FB_OUTDATED        0x4000
00074 #define FB_WITH_BODY       0x8000
00075 
00076 /* Archive flags */
00077 #define FA_IS_VOLUME         0x01
00078 #define FA_WITH_COMMENT      0x02
00079 #define FA_IS_SOLID          0x04
00080 #define FA_WITH_AUTHENTICITY 0x20
00081 
00082 /* File block flags */
00083 #define FF_CONT_FROM_PREV    0x01
00084 #define FF_CONT_IN_NEXT      0x02
00085 #define FF_WITH_PASSWORD     0x04
00086 #define FF_WITH_COMMENT      0x08
00087 #define FF_IS_SOLID          0x10
00088 
00089 /* Compression methods */
00090 #define M_STORE              0x30
00091 #define M_FASTEST            0x31
00092 #define M_FAST               0x32
00093 #define M_NORMAL             0x33
00094 #define M_GOOD               0x34
00095 #define M_BEST               0x35
00096 
00097 /* Archiving OS */
00098 #define OS_MSDOS                0
00099 #define OS_OS2                  1
00100 #define OS_WIN32                2
00101 #define OS_UNIX                 3
00102 
00103 #define CRC_START     0xFFFFFFFFUL
00104 #define CRC_INIT      0xEDB88320UL
00105 
00106 #define CRC_TABLESIZE 256
00107 static avuint CRC_table[CRC_TABLESIZE];
00108 
00109 struct rarnode {
00110     avushort flags;
00111     avbyte hostos;
00112     avbyte packer_version;
00113     avbyte method;
00114     char *path;
00115 };
00116 
00117 struct rar_entinfo {
00118     char *name;
00119     char *linkname;
00120     avoff_t datastart;
00121     block_header bh;
00122     file_header fh;
00123 };
00124 
00125 struct rarfile {
00126     char *tmpfile;
00127     int fd;
00128 };
00129 
00130 static void initCRC(void)
00131 {
00132     int i, j;
00133     avuint c;
00134   
00135     for (i = 0; i < CRC_TABLESIZE; i++)
00136     {
00137         for (c = i, j = 0; j < 8; j++)
00138             c = (c & 1) ? (c >> 1) ^ CRC_INIT : (c >> 1);
00139         CRC_table[i] = c;
00140     }
00141 }
00142 
00143 static avuint CRC_byte(avuint crc, avbyte byte)
00144 {
00145     return CRC_table[(avbyte)crc ^ byte] ^ (crc >> 8);
00146 }
00147 
00148 static avuint CRC_string(avuint crc, avbyte *buf, long size)
00149 {
00150     long i;
00151 
00152     for (i = 0; i < size; i++)
00153         crc = CRC_byte(crc, buf[i]);
00154     return crc;
00155 }
00156 
00157 static int read_block_header(vfile *vf, block_header bh, int all)
00158 {
00159     int res;
00160     int size = SHORT_HEAD_SIZE;
00161     int i;
00162 
00163     for(i = SHORT_HEAD_SIZE; i < LONG_HEAD_SIZE; i++) bh[i] = 0;
00164 
00165     if(all)
00166         res = av_read_all(vf, (char*)bh, SHORT_HEAD_SIZE);
00167     else
00168         res = av_read(vf, (char*)bh, SHORT_HEAD_SIZE);
00169     if(res < 0)
00170         return res;
00171     if(res < SHORT_HEAD_SIZE)
00172         return 0;
00173 
00174     if ((bh_flags(bh) & FB_WITH_BODY) != 0) {
00175         res = av_read_all(vf, (char*) ( bh+SHORT_HEAD_SIZE ), 4);
00176         if(res < 0)
00177             return res;
00178 
00179         size += 4;
00180     }
00181 
00182     return size;
00183 }
00184 
00185 static int read_marker_block(vfile *vf)
00186 {
00187     int res;
00188     avbyte buf[MARKER_HEAD_SIZE], *pos = buf;
00189     int readsize = MARKER_HEAD_SIZE;
00190 
00191     /* An SFX module starts with the extraction header. Skip that part by
00192        searching for the marker head. */
00193     while(1) {
00194         res = av_read_all(vf, (char*)( buf + MARKER_HEAD_SIZE - readsize ), readsize);
00195         if(res < 0)
00196             return res;
00197 
00198         if (memcmp(buf, good_marker_head, MARKER_HEAD_SIZE) == 0) return 0;
00199 
00200         pos = memchr(buf + 1, good_marker_head[0], MARKER_HEAD_SIZE-1);
00201         if (pos == NULL) readsize = MARKER_HEAD_SIZE;
00202         else {
00203             readsize = pos - buf;
00204             memmove(buf, pos, MARKER_HEAD_SIZE - readsize);
00205         }
00206     }
00207     return 0; /* Just to avoid warnings. Never reaches this line. */
00208 }
00209 
00210 static int read_archive_header(vfile *vf)
00211 {
00212     int res;
00213     block_header main_head;
00214     avuint crc;
00215     avbyte tmpbuf[6];
00216     int headlen;
00217 
00218     headlen = read_block_header(vf, main_head, 1);
00219     if(headlen < 0)
00220         return headlen;
00221 
00222     if (bh_type(main_head) != B_MAIN) {
00223         av_log(AVLOG_ERROR, "URAR: Bad archive header");
00224         return -EIO;
00225     }
00226 
00227     crc = CRC_string(CRC_START, main_head + 2, headlen - 2);
00228 
00229     /* Read reserved bytes. */
00230     res = av_read_all(vf, (char*)tmpbuf, 6);
00231     if(res < 0)
00232         return res;
00233     crc = CRC_string(crc, tmpbuf, 6);
00234 
00235     if ((avushort)~crc != bh_CRC(main_head)) {
00236         av_log(AVLOG_ERROR, "URAR: Bad archive header CRC");
00237         return -EIO;
00238     }
00239 
00240     av_lseek(vf, bh_size(main_head) - headlen - 6, AVSEEK_CUR);
00241 
00242     return 0;
00243 }
00244 
00245 static void conv_tolower(char *s)
00246 {
00247     for(; *s; s++) *s = tolower((int) *s);
00248 }
00249 
00250 
00251 static void dos2unix_path(char *path)
00252 {
00253     char *pos = path;
00254 
00255     while((pos = strchr(pos, DOS_DIR_SEP_CHAR)) != NULL)
00256         *pos = '/';
00257 }
00258 
00259 static avtime_t dos2unix_time(avuint dt)
00260 {
00261     struct avtm ut;
00262 
00263     ut.sec = dos_ftsec(dt);
00264     ut.min = dos_ftmin(dt);
00265     ut.hour = dos_fthour(dt);
00266     ut.day = dos_ftday(dt);
00267     ut.mon = dos_ftmonth(dt);
00268     ut.year = dos_ftyear(dt);
00269 
00270     return av_mktime(&ut);
00271 }
00272 
00273 static avmode_t dos2unix_attr(avuint da, avmode_t archmode)
00274 {
00275     avmode_t mode = (archmode & 0666);
00276     if (da & 0x01) mode = mode & ~0222;
00277     if (da & 0x10) mode = mode | ((mode & 0444) >> 2) | AV_IFDIR;
00278     else mode |= AV_IFREG;
00279 
00280     return mode;
00281 }
00282 
00283 static void rarnode_delete(struct rarnode *info)
00284 {
00285     av_free(info->path);
00286 }
00287 
00288 static avmode_t rar_get_mode(struct rar_entinfo *ei, avmode_t origmode)
00289 {
00290     if (bh_flags(ei->bh) & FF_WITH_PASSWORD)
00291         return AV_IFREG; /* FIXME */
00292     else {
00293         if (fh_hostos(ei->fh) == OS_UNIX)
00294             return fh_attr(ei->fh);
00295         else 
00296             return dos2unix_attr(fh_attr(ei->fh), origmode);
00297     }
00298 }
00299 
00300 static void fill_rarentry(struct archive *arch, struct entry *ent,
00301                          struct rar_entinfo *ei)
00302 {
00303     struct rarnode *info;
00304     struct archnode *nod;
00305     int isdir = AV_ISDIR(rar_get_mode(ei, 0));
00306 
00307     nod = av_arch_new_node(arch, ent, isdir);
00308 
00309     nod->st.mode = rar_get_mode(ei, nod->st.mode);
00310     nod->st.mtime.sec = dos2unix_time(fh_time(ei->fh));
00311     nod->st.mtime.nsec = 0;
00312     nod->st.atime = nod->st.mtime;
00313     nod->st.ctime = nod->st.mtime;
00314     nod->st.size = fh_origsize(ei->fh);
00315     nod->st.blocks = AV_BLOCKS(nod->st.size);
00316     nod->st.blksize = 4096;
00317 
00318     nod->offset = ei->datastart;
00319     if(fh_method(ei->fh) == M_STORE)
00320         nod->realsize = fh_origsize(ei->fh);
00321     else
00322         nod->realsize = 0;
00323 
00324     nod->linkname = av_strdup(ei->linkname);
00325 
00326     AV_NEW_OBJ(info, rarnode_delete);
00327     nod->data = info;
00328 
00329     info->flags = bh_flags(ei->bh);
00330     info->hostos = fh_hostos(ei->fh);
00331     info->packer_version = fh_version(ei->fh);
00332     info->method = fh_method(ei->fh);
00333     info->path = av_strdup(ei->name);
00334 }
00335 
00336 static void insert_rarentry(struct archive *arch, struct rar_entinfo *ei)
00337 {
00338     struct entry *ent;
00339     int entflags = 0;
00340     char *path = ei->name;
00341 
00342     dos2unix_path(path);
00343 
00344     if(fh_hostos(ei->fh) == OS_MSDOS) {
00345         conv_tolower(path);
00346         entflags |= NSF_NOCASE;
00347     }
00348 
00349     ent = av_arch_create(arch, path, entflags);
00350     if(ent == NULL)
00351         return;
00352 
00353     fill_rarentry(arch, ent, ei);
00354     av_unref_obj(ent);
00355 }
00356 
00357 static int crc_additional_header(vfile *vf, struct rar_entinfo *ei, int bytes_crcd, avuint *crc)
00358 {
00359     /* In the header there are some optional entries (e.g. salt, exttime; see arcread.ccp::ReadHeader
00360      * from unrar package). We need to use these bytes for the CRC.
00361      * Currently this optional stuff is not supported and I don't want to
00362      * add additional code to handle these information so I just read the
00363      * remaining bytes up to bh_headsize
00364      *
00365      * TODO: The salt is needed for crypted files which are not supported right now
00366      *       so this is not a problem.
00367      *       But perhaps it is a good idea to support the additional time information.
00368      */
00369     int res, tlen = bytes_crcd;
00370     avbyte *tempbuf;
00371     
00372     tlen = bh_headsize(ei->bh) - 2 - tlen;
00373     
00374     if(tlen > 0) {
00375         tempbuf = av_malloc(tlen);
00376        res = av_read_all(vf, (char*)tempbuf, tlen);
00377        if(res < 0) {
00378            av_free(tempbuf);
00379            return res;
00380        }
00381        *crc = CRC_string(*crc, tempbuf, tlen);
00382        av_free(tempbuf);
00383     } else if(tlen < 0) {
00384         return -EIO;
00385     }
00386     return 0;
00387 }
00388 
00389 static int read_rarentry(vfile *vf, struct rar_entinfo *ei)
00390 {
00391     int res;
00392     block_header ch;
00393     avuint crc;
00394 
00395     if (bh_size(ei->bh) < LONG_HEAD_SIZE + FILE_HEAD_SIZE) {
00396         av_log(AVLOG_ERROR, "URAR: bad header");
00397         return -EIO;
00398     }
00399             
00400     res = av_read_all(vf, (char*)( ei->fh ), FILE_HEAD_SIZE);
00401     if(res < 0)
00402         return res;
00403 
00404     ei->name = av_malloc(fh_namelen(ei->fh)+1);
00405     res = av_read_all(vf, ei->name, fh_namelen(ei->fh));
00406     if(res < 0)
00407         return res;
00408     ei->name[fh_namelen(ei->fh)] = '\0';
00409     
00410     crc = CRC_string(CRC_START, ei->bh + 2, LONG_HEAD_SIZE - 2);
00411     crc = CRC_string(crc, ei->fh, FILE_HEAD_SIZE);
00412     crc = CRC_string(crc, (avbyte*)( ei->name ), fh_namelen(ei->fh));
00413 
00414     if(crc_additional_header(vf, ei,
00415                           LONG_HEAD_SIZE - 2 + FILE_HEAD_SIZE + fh_namelen(ei->fh),
00416                           &crc) != 0) {
00417         av_log(AVLOG_ERROR, "URAR: bad header");
00418         return -EIO;
00419     }
00420     
00421     if ((avushort)~crc != bh_CRC(ei->bh)) {
00422         av_log(AVLOG_ERROR, "URAR: bad CRC");
00423         return -EIO;
00424     }
00425     
00426     if ((bh_flags(ei->bh) & FF_WITH_COMMENT) != 0) {
00427         res = read_block_header(vf, ch, 1);
00428         if(res < 0)
00429             return res;
00430 
00431         av_lseek(vf, bh_size(ch) - res, AVSEEK_CUR);
00432     }
00433     
00434     if(fh_hostos(ei->fh) == OS_UNIX && AV_ISLNK(fh_attr(ei->fh))) {
00435         ei->linkname = av_malloc(fh_origsize(ei->fh) + 1);
00436         res = av_read_all(vf, ei->linkname, fh_origsize(ei->fh));
00437         if(res < 0)
00438             return res;
00439 
00440         ei->linkname[fh_origsize(ei->fh)] = '\0';
00441     }
00442     
00443     return 0;
00444 }
00445 
00446 static int read_rarfile(vfile *vf, struct archive *arch)
00447 {
00448     avoff_t headstart;
00449     int res;
00450 
00451     res = read_marker_block(vf);
00452     if(res < 0)
00453         return res;
00454 
00455     res = read_archive_header(vf);
00456     if(res < 0)
00457         return res;
00458 
00459     headstart = vf->ptr;
00460     while(1) {
00461         struct rar_entinfo ei;
00462     
00463         res = read_block_header(vf, ei.bh, 0);
00464         if(res < 0)
00465             return res;
00466         if(res == 0)
00467             break;
00468 
00469         if (bh_type(ei.bh) == B_FILE) {
00470             ei.name = NULL;
00471             ei.linkname = NULL;
00472 
00473             res = read_rarentry(vf, &ei);
00474             if(res < 0) {
00475                 av_free(ei.name);
00476                 av_free(ei.linkname);
00477                 return res;
00478             }
00479             ei.datastart = vf->ptr;
00480 
00481             insert_rarentry(arch, &ei);
00482             av_free(ei.name);
00483             av_free(ei.linkname);
00484         }
00485         av_lseek(vf, headstart + bh_size(ei.bh), AVSEEK_SET);
00486         headstart = vf->ptr;
00487     }
00488 
00489     return 0;
00490 }
00491 
00492 static int parse_rarfile(void *data, ventry *ve, struct archive *arch)
00493 {
00494     int res;
00495     vfile *vf;
00496 
00497     res = av_open(ve->mnt->base, AVO_RDONLY, 0, &vf);
00498     if(res < 0)
00499         return res;
00500 
00501     res = read_rarfile(vf, arch);
00502 
00503     av_close(vf);
00504     
00505     return res;  
00506 }
00507 
00508 /* FIXME: Because we use the 'rar' program to extract the contents of
00509    each file individually , we get _VERY_ poor performance */
00510 
00511 static int get_rar_file(ventry *ve, struct archfile *fil, int fd)
00512 {
00513     int res;
00514     struct rarnode *info = (struct rarnode *) fil->nod->data;
00515     struct realfile *rf;
00516     const char *prog[7];
00517     struct proginfo pri;
00518     static int rar_available = 1;
00519 
00520     res = av_get_realfile(ve->mnt->base, &rf);
00521     if(res < 0)
00522         return res;
00523 
00524     /* prepare arguments */
00525     prog[0] = "rar";
00526     prog[1] = "p";
00527     prog[2] = "-c-";
00528     prog[3] = "-ierr";
00529     prog[4] = rf->name;
00530     prog[5] = info->path;
00531     prog[6] = NULL;
00532  
00533     if(rar_available) {
00534         av_init_proginfo(&pri);
00535         pri.prog = prog;
00536         pri.ifd = open("/dev/null", O_RDONLY);
00537         pri.ofd = fd;
00538         pri.efd = pri.ifd;
00539         
00540         res = av_start_prog(&pri);
00541         close(pri.ifd);
00542         
00543         if(res == 0)
00544             res = av_wait_prog(&pri, 0, 0);
00545     } else {
00546         /* force unrar execution */
00547         res = -EIO;
00548     }
00549     
00550     if(res == -EIO)
00551     {
00552         /* rar failed or unavailable, try unrar */
00553         rar_available = 0;
00554         
00555         prog[0] = "unrar";
00556         av_init_proginfo(&pri);
00557         pri.prog = prog;
00558         pri.ifd = open("/dev/null", O_RDONLY);
00559         pri.ofd = fd;
00560         pri.efd = pri.ifd;
00561         
00562         res = av_start_prog(&pri);
00563         close(pri.ifd);
00564         
00565         if(res == 0)
00566             res = av_wait_prog(&pri, 0, 0);
00567 
00568         if(res == -EIO) {
00569             /* unrar failed too so reset rar_available */
00570             rar_available = 1;
00571         }
00572     }
00573 
00574     av_unref_obj(rf);
00575 
00576     return res;
00577 }
00578 
00579 static int do_unrar(ventry *ve, struct archfile *fil)
00580 {
00581     int res;
00582     struct rarfile *rfil;
00583     char *tmpfile;
00584     int fd;
00585 
00586     res = av_get_tmpfile(&tmpfile);
00587     if(res < 0)
00588         return res;
00589 
00590     fd = open(tmpfile, O_RDWR | O_CREAT | O_TRUNC, 0644);
00591     if(fd == -1) {
00592         res = -errno; 
00593         av_log(AVLOG_ERROR, "RAR: Could not open %s: %s", tmpfile,
00594                strerror(errno));
00595         av_del_tmpfile(tmpfile);
00596         return res;
00597     }
00598 
00599     res = get_rar_file(ve, fil, fd);
00600     if(res < 0) {
00601         close(fd);
00602         av_del_tmpfile(tmpfile);
00603         return res;
00604     }
00605 
00606     AV_NEW(rfil);
00607     rfil->tmpfile = tmpfile;
00608     rfil->fd = fd;
00609 
00610     fil->data = rfil;
00611 
00612     return 0;
00613 }
00614 
00615 
00616 static int rar_open(ventry *ve, struct archfile *fil)
00617 {
00618     struct rarnode *info = (struct rarnode *) fil->nod->data;
00619 
00620     if(info == NULL) {
00621         /* access to base rar directory */
00622         return -EISDIR;
00623     }
00624     
00625     if(info->flags & FF_WITH_PASSWORD) {
00626         av_log(AVLOG_WARNING, "URAR: File password protected, sorry...");
00627         return -EACCES;
00628     }
00629 
00630     if(info->method != M_STORE)
00631         return do_unrar(ve, fil);
00632     
00633     return 0;
00634 }
00635 
00636 static int rar_close(struct archfile *fil)
00637 {
00638     struct rarfile *rfil = (struct rarfile *) fil->data;
00639 
00640     if(rfil != NULL) {
00641         close(rfil->fd);
00642         av_del_tmpfile(rfil->tmpfile);
00643         av_free(rfil);
00644     }
00645     
00646     return 0;
00647 }
00648 
00649 static avssize_t rar_read(vfile *vf, char *buf, avsize_t nbyte)
00650 {
00651     avssize_t res;
00652     struct archfile *fil = arch_vfile_file(vf);
00653     struct rarfile *rfil = (struct rarfile *) fil->data;
00654 
00655     if(rfil == NULL)
00656         return av_arch_read(vf, buf, nbyte);
00657 
00658     if(lseek(rfil->fd, vf->ptr, SEEK_SET) == -1)
00659         return -errno;
00660 
00661     res = read(rfil->fd, buf, nbyte);
00662     if(res == -1)
00663         return -errno;
00664 
00665     vf->ptr += res;
00666 
00667     return res;
00668 }
00669 
00670 extern int av_init_module_urar(struct vmodule *module);
00671 
00672 int av_init_module_urar(struct vmodule *module)
00673 {
00674     int res;
00675     struct avfs *avfs;
00676     struct ext_info rarexts[3];
00677     struct archparams *ap;
00678 
00679     rarexts[0].from = ".rar",  rarexts[0].to = NULL;
00680     rarexts[1].from = ".sfx",  rarexts[1].to = NULL;
00681     rarexts[2].from = NULL;
00682 
00683     res = av_archive_init("urar", rarexts, AV_VER, module, &avfs);
00684     if(res < 0)
00685         return res;
00686     
00687     ap = (struct archparams *) avfs->data;
00688     ap->parse = parse_rarfile;
00689     ap->open = rar_open;
00690     ap->close = rar_close;
00691     ap->read = rar_read;
00692 
00693     initCRC();
00694 
00695     av_add_avfs(avfs);
00696 
00697     return 0;
00698 }