Back to index

avfs  1.0.1
utar.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) 2007  Ralf Hoffmann (ralf@boomerangsworld.de)
00005 
00006     Based on the GNU tar sources (C) Free Software Foundation
00007     
00008     This file can be distributed under the GNU GPL. 
00009     See the file COPYING. 
00010 
00011     TAR module
00012 */
00013 
00014 #include "gtar.h"
00015 #include "archive.h"
00016 #include "oper.h"
00017 #include "ugid.h"
00018 #include "version.h"
00019 
00020 #define COPYBUFSIZE 16384
00021 #define BIGBLOCKSIZE (20 * BLOCKSIZE)
00022 
00023 /* Some constants from POSIX are given names.  */
00024 #define NAME_FIELD_SIZE   100
00025 #define PREFIX_FIELD_SIZE 155
00026 #define UNAME_FIELD_SIZE   32
00027 
00028 /* FIXME: Not any more: inode udata is used for saving filenames temporarily at archive creation */
00029 
00030 
00031 struct tar_entinfo {
00032     char *name;
00033     char *linkname;
00034     avoff_t size;
00035     avoff_t datastart;
00036 
00037     union block header;
00038 };
00039 
00040 struct sp_array
00041 {
00042     avoff_t offset;
00043     int numbytes;
00044 };
00045 
00046 struct tarnode {
00047     int type;
00048     struct sp_array *sparsearray;
00049     int sp_array_len;
00050     avoff_t headeroff;
00051     avuid_t uid;
00052     avgid_t gid;
00053     char uname[UNAME_FIELD_SIZE];
00054     char gname[UNAME_FIELD_SIZE];
00055 };
00056 
00057 
00058 #define ISSPACE(x) isspace(x)
00059 #define ISODIGIT(x) ((x) >= '0' && (x) < '8')
00060 
00061 /*------------------------------------------------------------------------.
00062 | Quick and dirty octal conversion.  Result is -1 if the field is invalid |
00063 | (all blank, or nonoctal).                                      |
00064 `------------------------------------------------------------------------*/
00065 
00066 static long long from_oct(int digs, char *where)
00067 {
00068     long long value;
00069 
00070     while (ISSPACE ((int) *where))
00071     {                       /* skip spaces */
00072         where++;
00073         if (--digs <= 0)
00074             return -1;             /* all blank field */
00075     }
00076     value = 0;
00077     while (digs > 0 && ISODIGIT (*where))
00078     {
00079         /* Scan til nonoctal.  */
00080 
00081         value = (value << 3) | (*where++ - '0');
00082         --digs;
00083     }
00084 
00085     if (digs > 0 && *where && !ISSPACE ((int) *where))
00086         return -1;                 /* ended on non-space/nul */
00087 
00088     return value;
00089 }
00090 
00091 /*------------------------------------------------------------------------.
00092 | Converts long VALUE into a DIGS-digit field at WHERE, including a       |
00093 | trailing space and room for a NUL.  For example, 3 for DIGS 3 means one |
00094 | digit, a space, and room for a NUL.                                     |
00095 |                                                                         |
00096 | We assume the trailing NUL is already there and don't fill it in.  This |
00097 | fact is used by start_header and finish_header, so don't change it!     |
00098 `------------------------------------------------------------------------*/
00099 
00100 #if 0
00101 static void to_oct (long value, int digs, char *where)
00102 {
00103     --digs;                 /* Trailing null slot is left alone */
00104 
00105     do
00106     {
00107         where[--digs] = '0' + (char) (value & 7);       /* one octal digit */
00108         value >>= 3;
00109     }
00110     while (digs > 0);
00111 }
00112 
00113 #endif
00114 
00115 static int find_next_block(vfile *vf, union block *blk)
00116 {
00117     int res;
00118 
00119     res = av_read(vf, blk->buffer, BLOCKSIZE);
00120     if(res <= 0)
00121         return res;
00122     if(res < BLOCKSIZE) {
00123         av_log(AVLOG_WARNING, "TAR: Broken archive");
00124         return -EIO;
00125     }
00126 
00127     return 1;
00128 }
00129 
00130 static int get_next_block(vfile *vf, union block *blk)
00131 {
00132     int res;
00133   
00134     res = find_next_block(vf, blk);
00135     if(res < 0)
00136         return res;
00137     if(res == 0) {
00138          av_log(AVLOG_WARNING, "TAR: Broken archive");
00139          return -EIO;
00140     }
00141   
00142     return 0;
00143 }
00144 
00145 static enum archive_format get_header_format( union block *header )
00146 {
00147     enum archive_format f = V7_FORMAT;
00148     
00149     if ( strncmp( header->header.magic, TMAGIC, TMAGLEN ) == 0 &&
00150          strncmp( header->header.version, TVERSION, TVERSLEN ) == 0 ) {
00151         /* POSIX header is magic == "ustar\0" and version == "00" */
00152         f = POSIX_FORMAT;
00153     } else if ( strcmp( header->header.magic, OLDGNU_MAGIC) == 0 ) {
00154         /* Old GNU header is magic == "ustar " and version == "0\0" */
00155         /* it could be actually a pre-POSIX header */
00156         f = OLDGNU_FORMAT;
00157     }
00158 
00159     return f;
00160 }
00161 
00162 /* return values: < 0: fatal, 0 eof, 1 bad header, 2 OK */
00163 static int read_entry(vfile *vf, struct tar_entinfo *tinf)
00164 {
00165     int i;
00166     long unsigned_sum;             /* the POSIX one :-) */
00167     long signed_sum;        /* the Sun one :-( */
00168     long recorded_sum;
00169     char *p;
00170     char **longp;
00171     char *bp;
00172     union block data_block;
00173     int size, written;
00174     int res;
00175     avoff_t sres;
00176     char *next_long_name = NULL, *next_long_link = NULL;
00177     union block *header = &tinf->header;
00178 
00179     while (1)
00180     {
00181         res = find_next_block(vf, header);
00182         if(res <= 0) break; /* HEADER_END_OF_FILE */
00183 
00184         recorded_sum
00185             = from_oct (sizeof header->header.chksum, header->header.chksum);
00186 
00187         unsigned_sum = 0;
00188         signed_sum = 0;
00189         p = header->buffer;
00190         for (i = sizeof (*header); --i >= 0;)
00191        {
00192             /* We can't use unsigned char here because of old compilers,
00193                e.g. V7.  */
00194 
00195             unsigned_sum += 0xFF & *p;
00196             signed_sum += *p++;
00197        }
00198 
00199         /* Adjust checksum to count the "chksum" field as blanks.  */
00200 
00201         for (i = sizeof (header->header.chksum); --i >= 0;)
00202        {
00203             unsigned_sum -= 0xFF & header->header.chksum[i];
00204             signed_sum -= header->header.chksum[i];
00205        }
00206         unsigned_sum += ' ' * sizeof header->header.chksum;
00207         signed_sum += ' ' * sizeof header->header.chksum;
00208 
00209         if (unsigned_sum == sizeof header->header.chksum * ' ')
00210        {
00211             /* This is a zeroed block...whole block is 0's except for the
00212                blanks we faked for the checksum field.  */
00213 
00214             res = 0;
00215             break; /* HEADER_ZERO_BLOCK */
00216        }
00217 
00218         if (unsigned_sum != recorded_sum && signed_sum != recorded_sum) {
00219             res = 1;
00220             av_log(AVLOG_WARNING, "TAR: Bad header");
00221             break; /* HEADER_FAILURE */
00222         }
00223 
00224         /* Good block.  Decode file size and return.  */
00225 
00226         if (header->header.typeflag == LNKTYPE)
00227             tinf->size  = 0;       /* links 0 size on tape */
00228         else
00229             tinf->size = from_oct (1 + 12, header->header.size);
00230 
00231         if (header->header.typeflag == GNUTYPE_LONGNAME
00232             || header->header.typeflag == GNUTYPE_LONGLINK)
00233        {
00234             longp = ((header->header.typeflag == GNUTYPE_LONGNAME)
00235                      ? &next_long_name
00236                      : &next_long_link);
00237 
00238             if (*longp) av_free (*longp);
00239             bp = *longp = (char *) av_malloc ((avsize_t) tinf->size);
00240 
00241             for (size = tinf->size; size > 0; size -= written)
00242            {
00243                 res = get_next_block (vf, &data_block);
00244                 if (res < 0) break;
00245                 written = BLOCKSIZE;
00246                 if (written > size)
00247                     written = size;
00248 
00249                 memcpy (bp, data_block.buffer, (avsize_t) written);
00250                 bp += written;
00251            }
00252             if(res < 0) break;
00253 
00254             /* Loop!  */
00255 
00256        }
00257         else
00258        {
00259             tinf->datastart = vf->ptr;
00260 
00261             if (header->oldgnu_header.isextended) {
00262                 do {
00263                     res = get_next_block (vf, &data_block);
00264                     if(res < 0) break;
00265                 }
00266                 while(data_block.sparse_header.isextended);
00267             }
00268             if(res < 0) break;
00269 
00270             sres = av_lseek(vf, AV_DIV(tinf->size, BLOCKSIZE) * BLOCKSIZE, 
00271                             AVSEEK_CUR);
00272             if(sres < 0)
00273                 break;
00274 
00275             if (header->header.typeflag == 'g')
00276                 continue;
00277 
00278             if ( get_header_format( header ) == POSIX_FORMAT ) {
00279                 /* POSIX ustar format uses prefix for long file names
00280                    the actual name is prefix/name
00281                 */
00282                 if ( header->header.prefix[0] != '\0' ) {
00283                     char *tmp_prefix, *tmp_name;
00284 
00285                     tmp_prefix = av_strndup( header->header.prefix, PREFIX_FIELD_SIZE );
00286                     tmp_name = av_strndup( header->header.name, NAME_FIELD_SIZE );
00287                     
00288                     if ( next_long_name )
00289                         av_free(next_long_name);
00290                     
00291                     next_long_name = av_stradd( NULL, tmp_prefix, "/", tmp_name, NULL );
00292                     
00293                     av_free( tmp_prefix );
00294                     av_free( tmp_name );
00295                 }
00296             }
00297 
00298             /* NOTE: header->header.name is not necessarily null-terminated */
00299             if ( next_long_name ) {
00300                 tinf->name = av_strdup (next_long_name);
00301             } else {
00302                 tinf->name = av_strndup( header->header.name, NAME_FIELD_SIZE );
00303             }
00304 
00305             if ( next_long_link ) {
00306                 tinf->linkname = av_strdup (next_long_link);
00307             } else {
00308                 tinf->linkname = av_strndup( header->header.linkname, NAME_FIELD_SIZE );
00309             }
00310             res = 2;
00311             break; /* HEADER_SUCCESS */
00312        }
00313     }
00314 
00315     av_free(next_long_name);
00316     av_free(next_long_link);
00317     return res;
00318 }
00319 
00320 
00321 static void decode_header (union block *header, struct avstat *stat_info,
00322                         enum archive_format *format_pointer, 
00323                         struct ugidcache *cache)
00324 {
00325     enum archive_format format;
00326     char ugname[UNAME_FIELD_SIZE+1];
00327 
00328     if (strcmp (header->header.magic, TMAGIC) == 0)
00329         format = POSIX_FORMAT;
00330     else if (strcmp (header->header.magic, OLDGNU_MAGIC) == 0)
00331         format = OLDGNU_FORMAT;
00332     else
00333         format = V7_FORMAT;
00334     *format_pointer = format;
00335 
00336     stat_info->mode = from_oct (8, header->header.mode);
00337     stat_info->mode &= 07777;
00338     stat_info->mtime.sec = from_oct (1 + 12, header->header.mtime);
00339     stat_info->mtime.nsec = 0;
00340 
00341     if(header->header.typeflag == GNUTYPE_SPARSE) 
00342         stat_info->size = 
00343             from_oct (1 + 12, header->oldgnu_header.realsize);
00344     else stat_info->size = from_oct (1 + 12, header->header.size);
00345   
00346     switch(header->header.typeflag) {
00347     case GNUTYPE_SPARSE:
00348     case REGTYPE:
00349     case AREGTYPE:
00350     case LNKTYPE:
00351     case CONTTYPE:
00352         stat_info->mode |= AV_IFREG;
00353         break;
00354 
00355     case GNUTYPE_DUMPDIR:
00356     case DIRTYPE:
00357         stat_info->mode |= AV_IFDIR;
00358         break;
00359 
00360     case SYMTYPE:
00361         stat_info->mode |= AV_IFLNK;
00362         break;
00363     
00364     case BLKTYPE:
00365         stat_info->mode |= AV_IFBLK;
00366         break;
00367 
00368     case CHRTYPE:
00369         stat_info->mode |= AV_IFCHR;
00370         break;
00371 
00372     case FIFOTYPE:
00373         stat_info->mode |= AV_IFIFO;
00374         break;
00375     }
00376 
00377     if (format == V7_FORMAT)
00378     {
00379         stat_info->uid = from_oct (8, header->header.uid);
00380         stat_info->gid = from_oct (8, header->header.gid);
00381         stat_info->rdev = 0;
00382     }
00383     else
00384     {
00385         ugname[UNAME_FIELD_SIZE] = '\0';
00386 
00387         strncpy(ugname, header->header.uname, UNAME_FIELD_SIZE);
00388         stat_info->uid =
00389             av_finduid(cache, ugname, from_oct (8, header->header.uid));
00390 
00391         strncpy(ugname, header->header.gname, UNAME_FIELD_SIZE);
00392         stat_info->gid =
00393             av_findgid(cache, ugname, from_oct (8, header->header.gid));
00394 
00395         switch (header->header.typeflag)
00396        {
00397        case BLKTYPE:
00398        case CHRTYPE:
00399             stat_info->rdev = 
00400                 av_mkdev (from_oct (8, header->header.devmajor),
00401                           from_oct (8, header->header.devminor));
00402             break;
00403 
00404        default:
00405             stat_info->rdev = 0;
00406        }
00407     }
00408 }
00409 
00410 
00411 static int check_existing(struct entry *ent, struct avstat *tarstat)
00412 {
00413     struct archnode *nod;
00414     
00415     nod = (struct archnode *) av_namespace_get(ent);
00416 
00417     if(AV_ISDIR(nod->st.mode)) {
00418         if(AV_ISDIR(tarstat->mode)) {
00419             nod->st.mode = tarstat->mode;
00420             nod->st.uid = tarstat->uid;
00421             nod->st.gid = tarstat->gid;
00422             nod->st.mtime = tarstat->mtime;
00423 #if 0
00424             /* FIXME */
00425             nod->origst = nod->st;
00426 #endif
00427             return 0;
00428         }
00429         else {
00430             av_log(AVLOG_WARNING, "TAR: Overwriting directory with file");
00431             return 0;
00432         }
00433     }
00434     
00435     av_arch_del_node(ent);
00436 
00437     return 1;
00438 }
00439 
00440 static void fill_link(struct archive *arch, struct entry *ent,
00441                       const char *linkname)
00442 {
00443     struct entry *link;
00444     struct archnode *nod = NULL;
00445 
00446     link = av_arch_resolve(arch, linkname, 0, 0);
00447     if(link != NULL)
00448         nod = (struct archnode *) av_namespace_get(link);
00449 
00450     if(nod == NULL || AV_ISDIR(nod->st.mode))
00451         av_log(AVLOG_WARNING, "utar: Illegal hard link");
00452     else {
00453         nod->st.nlink ++;
00454         av_namespace_set(ent, nod);
00455         av_ref_obj(ent);
00456         av_ref_obj(nod);
00457     }
00458 
00459     av_unref_obj(link);
00460 }
00461 
00462 static void tarnode_delete(struct tarnode *tn)
00463 {
00464     av_free(tn->sparsearray);
00465 }
00466 
00467 static void fill_node(struct archive *arch, struct entry *ent,
00468                       struct tar_entinfo *tinf, struct avstat *tarstat)
00469 {
00470     struct archnode *nod;
00471     struct tarnode *tn;
00472     union block *header = &tinf->header;
00473 
00474     nod = av_arch_new_node(arch, ent, AV_ISDIR(tarstat->mode));
00475 
00476     /* keep dev, ino, nlink */
00477     nod->st.mode = tarstat->mode;
00478     nod->st.uid = tarstat->uid;
00479     nod->st.gid = tarstat->gid;
00480     nod->st.rdev = tarstat->rdev;
00481     nod->st.size = tarstat->size;
00482     nod->st.blksize = BLOCKSIZE;
00483     nod->st.blocks = AV_BLOCKS(tinf->size);
00484     nod->st.atime = tarstat->mtime; /* FIXME */
00485     nod->st.mtime = tarstat->mtime;
00486     nod->st.ctime = tarstat->mtime;
00487 
00488     nod->offset = tinf->datastart;
00489     nod->realsize = tinf->size;
00490 
00491     AV_NEW_OBJ(tn, tarnode_delete);
00492     nod->data = tn;
00493 
00494     tn->sparsearray = NULL;
00495     tn->headeroff = tinf->datastart - BLOCKSIZE;
00496     tn->uid = from_oct (8, header->header.uid);
00497     tn->gid = from_oct (8, header->header.gid);
00498     strncpy(tn->uname, header->header.uname, UNAME_FIELD_SIZE);
00499     strncpy(tn->gname, header->header.gname, UNAME_FIELD_SIZE);
00500     tn->type =  header->header.typeflag;
00501 
00502     if(tn->type == SYMTYPE) {
00503         nod->linkname = tinf->linkname;
00504         nod->st.size = strlen(nod->linkname);
00505         tinf->linkname = NULL;
00506     }
00507 }
00508 
00509 static void fill_tarentry(struct archive *arch, struct entry *ent,
00510                           struct tar_entinfo *tinf, struct avstat *tarstat)
00511 {
00512     int res;
00513     union block *header = &tinf->header;
00514     struct archnode *nod;
00515 
00516     nod = (struct archnode *) av_namespace_get(ent);
00517     if(nod != NULL) {
00518         res = check_existing(ent, tarstat);
00519         if(res != 1)
00520             return;
00521     }
00522 
00523     if(header->header.typeflag == LNKTYPE) 
00524         fill_link(arch, ent, tinf->linkname);
00525     else
00526         fill_node(arch, ent, tinf, tarstat);
00527 }
00528 
00529 static void insert_tarentry(struct archive *arch, struct tar_entinfo *tinf,
00530                             struct avstat *tarstat)
00531 {
00532     struct entry *ent;
00533 
00534     if(tinf->header.header.typeflag == GNUTYPE_SPARSE) {
00535 #if 0 /* FIXME */
00536         arch->flags |= ARCHF_RDONLY;
00537         if(arch->readonly_reason == NULL)
00538             arch->readonly_reason =
00539                 av_strdup("TAR: Cannot modify archive containing sparsefiles");
00540 #endif
00541     }
00542 
00543     /* Appears to be a file.  But BSD tar uses the convention that a
00544        slash suffix means a directory.  */
00545     if(AV_ISREG(tarstat->mode) && tinf->name[strlen(tinf->name)-1] == '/') 
00546         tarstat->mode = (tarstat->mode & 07777) | AV_IFDIR;
00547     
00548     ent = av_arch_resolve(arch, tinf->name, 1, 0);
00549     if(ent == NULL)
00550         return;
00551 
00552     if(av_arch_isroot(arch, ent)) {
00553         /* that is not so unusual, archives created with
00554            "tar -cf test.tar ." have a ./ entry so
00555            the warning is disabled */
00556         /* av_log(AVLOG_WARNING, "TAR: Empty filename");*/
00557     } else
00558         fill_tarentry(arch, ent, tinf, tarstat);
00559 
00560     av_unref_obj(ent);
00561 }
00562 
00563 static int read_tarfile(vfile *vf, struct archive *arch,
00564                         struct ugidcache *cache)
00565 {
00566     struct tar_entinfo tinf;
00567     enum archive_format format;
00568     struct avstat tarstat;
00569     int res;
00570 
00571     while(1) {
00572         res = read_entry(vf, &tinf);
00573         if(res < 0)
00574             return res;
00575         else if(res == 1) {
00576 #if 0  /* FIXME */
00577             arch->flags |= ARCHF_RDONLY; /* Broken archive */
00578 
00579             if(arch->readonly_reason == NULL) 
00580                 arch->readonly_reason = 
00581                     av_strdup("TAR: Cannot modify archive with errors");
00582 
00583 #endif
00584             continue;
00585         }
00586         else if(res == 0)
00587             break;
00588 
00589         av_default_stat(&tarstat);
00590         decode_header(&tinf.header, &tarstat, &format, cache);
00591 
00592         insert_tarentry(arch, &tinf, &tarstat);
00593         av_free(tinf.name);
00594         av_free(tinf.linkname);
00595     }
00596 
00597     return 0;
00598 }
00599 
00600 
00601 static int parse_tarfile(void *data, ventry *ve, struct archive *arch)
00602 {
00603     int res;
00604     vfile *vf;
00605     struct ugidcache *cache;
00606 
00607     res = av_open(ve->mnt->base, AVO_RDONLY, 0, &vf);
00608     if(res < 0)
00609         return res;
00610 
00611     cache = av_new_ugidcache();
00612     res = read_tarfile(vf, arch, cache);
00613     av_unref_obj(cache);
00614 
00615     av_close(vf);
00616     
00617     return res;  
00618 }
00619 
00620 #if 0
00621 static int write_out(ave *v, int outfd, arch_file *file, avsize_t size)
00622 {
00623     avssize_t rres, wres, len;
00624     char buf[COPYBUFSIZE];
00625     avsize_t at;
00626 
00627     for(at = 0; at < size;) {
00628         rres = av_read(v, file->fh, buf, AV_MIN(COPYBUFSIZE, size-at));
00629         if(rres == -1) return -1;
00630         at += rres;
00631         file->ptr += rres;
00632 
00633         if(rres != COPYBUFSIZE && at != size) {
00634             v->errn = EIO;
00635             return -1;
00636         }
00637     
00638         if(rres < COPYBUFSIZE) {
00639             len = AV_DIV(rres, BLOCKSIZE) * BLOCKSIZE;
00640             if(len > rres) av_memset(buf + rres, 0, len - rres);
00641         }
00642         else len = COPYBUFSIZE;
00643     
00644         wres = av_write(v, outfd, buf, len);
00645         if(wres == -1) return -1;
00646     }
00647 
00648     return 0;
00649 }
00650 
00651 static void finish_header(union block *blk)
00652 {
00653     int i, sum;
00654     char *p;
00655 
00656     av_memset(blk->header.chksum, ' ', 8);
00657 
00658     /* Fill in the checksum field.  It's formatted differently from the
00659        other fields: it has [6] digits, a null, then a space -- rather than
00660        digits, a space, then a null.  We use to_oct then write the null in
00661        over to_oct's space.  The final space is already there, from
00662        checksumming, and to_oct doesn't modify it.
00663     */
00664 
00665     sum = 0;
00666     p = blk->buffer;
00667     for (i = BLOCKSIZE; --i >= 0; )
00668         /* We can't use unsigned char here because of old compilers, e.g. V7.  */
00669         sum += 0xFF & *p++;
00670 
00671     to_oct ((long) sum, 7, blk->header.chksum);
00672     blk->header.chksum[6] = '\0';  /* zap the space */
00673 
00674 }
00675 
00676 static int long_name(ave *v, int outfd, const char *name, int type)
00677 {
00678     union block blk;
00679     int size;
00680     int at;
00681 
00682     size = av_strlen(name) + 1;
00683 
00684     av_memset(blk.buffer, 0, BLOCKSIZE);
00685 
00686     av_strcpy(blk.header.name, "././@LongLink");
00687     to_oct ((long) 0, 8, blk.header.mode);
00688     to_oct ((long) 0, 8, blk.header.uid);
00689     to_oct ((long) 0, 8, blk.header.gid);
00690     to_oct ((long) 0, 12, blk.header.mtime);
00691     av_strcpy(blk.header.uname, "root");
00692     av_strcpy(blk.header.gname, "root");
00693     av_strcpy(blk.header.magic, OLDGNU_MAGIC);
00694     blk.header.typeflag = type;
00695     to_oct ((long) size, 12, blk.header.size);
00696 
00697     finish_header(&blk);
00698 
00699     if(av_write(v, outfd, blk.buffer, BLOCKSIZE) == -1) return -1;
00700   
00701     for(at = 0; at < size; at += BLOCKSIZE) {
00702         av_memset(blk.buffer, 0, BLOCKSIZE);
00703         av_strncpy(blk.buffer, name + at, BLOCKSIZE);
00704 
00705         if(av_write(v, outfd, blk.buffer, BLOCKSIZE) == -1) return -1;    
00706     }
00707   
00708     return 0;
00709 }
00710 
00711 static int create_entry(ave *v, arch_entry *ent, const char *path, 
00712                      arch_file *file, int outfd, struct ugidcache *cache)
00713 {
00714     union block blk;
00715     arch_inode *ino = ent->ino;
00716     struct tar_entdat *ted = (struct tar_entdat *) ent->udata;
00717     char *name;
00718     int type;
00719     avsize_t size;
00720     int res;
00721     char ugname[AV_TUNMLEN];
00722 
00723     av_memset(blk.buffer, 0, BLOCKSIZE);
00724 
00725     to_oct ((long) ino->st.mode, 8, blk.header.mode);
00726     to_oct ((long) ino->st.mtime, 12, blk.header.mtime);
00727 
00728     if(!(ino->flags & INOF_CREATED) &&  ted != NULL && 
00729        ino->st.uid == ino->origst.uid && ino->st.gid == ino->origst.gid) {
00730     
00731         to_oct ((long) ted->uid, 8, blk.header.uid);
00732         to_oct ((long) ted->gid, 8, blk.header.gid);
00733         av_strncpy(blk.header.uname, ted->uname, UNAME_FIELD_SIZE);
00734         av_strncpy(blk.header.gname, ted->gname, UNAME_FIELD_SIZE);
00735     }
00736     else {
00737         to_oct ((long) ino->st.uid, 8, blk.header.uid);
00738         to_oct ((long) ino->st.gid, 8, blk.header.gid);
00739     
00740         av_finduname(ugname, ino->st.uid, cache);
00741         av_strncpy(blk.header.uname, ugname, UNAME_FIELD_SIZE);
00742     
00743         av_findgname(ugname, ino->st.gid, cache);
00744         av_strncpy(blk.header.gname, ugname, UNAME_FIELD_SIZE);
00745     }
00746 
00747     /* We only do OLDGNU for the moment */
00748     av_strcpy(blk.header.magic, OLDGNU_MAGIC);
00749 
00750     if(AV_ISDIR(ino->st.mode)) 
00751         type = DIRTYPE;
00752     else if(AV_ISLNK(ino->st.mode))
00753         type = SYMTYPE;
00754     else if(AV_ISCHR(ino->st.mode)) 
00755         type = CHRTYPE;
00756     else if(AV_ISBLK(ino->st.mode))
00757         type = BLKTYPE;
00758     else if(AV_ISFIFO(ino->st.mode) || AV_ISSOCK(ino->st.mode))
00759         type = FIFOTYPE;
00760     else 
00761         type = REGTYPE;
00762 
00763     if(ino->udata != NULL)
00764         type = LNKTYPE;
00765 
00766     blk.header.typeflag = type;
00767 
00768     if(type == REGTYPE) size = ino->st.size;
00769     else size = 0;
00770 
00771     to_oct ((long) size, 12, blk.header.size);
00772 
00773     if(type == CHRTYPE || type == BLKTYPE) {
00774         int major, minor;
00775 
00776         av_splitdev(ino->st.rdev, &major, &minor);
00777         to_oct ((long) major, 8, blk.header.devmajor);
00778         to_oct ((long) minor, 8, blk.header.devminor);
00779     }
00780   
00781     if(type == LNKTYPE || type == SYMTYPE) {
00782         char *linkname;
00783 
00784         if(type == LNKTYPE)
00785             linkname = (char *) ino->udata;
00786         else
00787             linkname = ino->syml;
00788 
00789 
00790         if(av_strlen(linkname) >= NAME_FIELD_SIZE && 
00791            long_name(v, outfd, linkname, GNUTYPE_LONGLINK) == -1) return -1;
00792     
00793         av_strncpy(blk.header.linkname, linkname, NAME_FIELD_SIZE);
00794         blk.header.linkname[NAME_FIELD_SIZE-1] = '\0';
00795     }
00796 
00797     if(!AV_ISDIR(ino->st.mode)) 
00798         name = av_strconcat(v, path, ent->name, NULL);
00799     else
00800         name = av_strconcat(v, path, ent->name, "/", NULL);
00801 
00802     if(name == NULL) return -1;
00803 
00804     if(av_strlen(name) >= NAME_FIELD_SIZE && 
00805        long_name(v, outfd, name, GNUTYPE_LONGNAME) == -1) return -1;
00806 
00807     av_strncpy(blk.header.name, name, NAME_FIELD_SIZE);
00808     blk.header.name[NAME_FIELD_SIZE-1] = '\0';
00809     av_free(name);
00810 
00811 
00812     finish_header(&blk);
00813 
00814     if(av_write(v, outfd, blk.buffer, BLOCKSIZE) == -1) return -1;
00815 
00816     /* FIXME: sparse files */
00817     if(ino->typeflag == GNUTYPE_SPARSE) {
00818         v->errn = EFAULT;
00819         return -1;
00820     }
00821   
00822     if(size != 0) {
00823         if(ino->tmpfile != NULL) {
00824             arch_file f;
00825       
00826             f.ptr = 0;
00827             f.fh = av_localopen(v, ino->tmpfile, AVO_RDONLY, 0);
00828             if(f.fh == -1) return -1;
00829 
00830             res = write_out(v, outfd, &f, size);
00831             av_localclose(DUMMYV, f.fh);
00832 
00833             if(res == -1) return -1;
00834         }
00835         else {
00836             file->ptr = av_lseek(v, file->fh, ino->offset, AVSEEK_SET);
00837             if(file->ptr == -1) return -1;
00838 
00839             res = write_out(v, outfd, file, size);
00840             if(res == -1) return -1;
00841         }
00842     }
00843 
00844     return 0;
00845 }
00846 
00847 static int write_tardir(ave *v, arch_file *file, arch_inode *dir, int outfd, 
00848                      const char *path, int pathchanged, 
00849                      struct ugidcache *cache)
00850 {
00851     arch_entry *ent;
00852     arch_inode *ino;
00853     struct tar_entdat *ted;
00854     int res;
00855 
00856     for(ent = dir->subdir; ent != NULL; ent = ent->next) {
00857         ted = (struct tar_entdat *) ent->udata;
00858         ino = ent->ino;
00859 
00860         if(!(ino->flags & INOF_AUTODIR)) {
00861             if(create_entry(v, ent, path, file, outfd, cache) == -1) return -1;
00862       
00863             if(!AV_ISDIR(ino->st.mode) && ino->st.nlink > 1 && 
00864                ino->udata == NULL) {
00865                 ino->udata = av_strconcat(v, path, ent->name, NULL);
00866                 if(ino->udata == NULL) return -1;
00867             }
00868         }
00869 
00870         if(AV_ISDIR(ino->st.mode)) {
00871             int dirchanged;
00872             char *newpath;
00873 
00874             if(ted == NULL) dirchanged = 1; /* Renamed directory */
00875             else dirchanged = 0;
00876 
00877             newpath = av_strconcat(v, path, ent->name, "/", NULL);
00878             if(newpath == NULL) return -1;
00879 
00880             res = write_tardir(v, file, ino, outfd, newpath, dirchanged,
00881                                cache);
00882             av_free(newpath);
00883       
00884             if(res == -1) return -1;
00885         }
00886     }
00887 
00888     return 0;
00889 }
00890 
00891 static void clear_filenames(arch_inode *dir)
00892 {
00893     arch_entry *ent;
00894 
00895     for(ent = dir->subdir; ent != NULL; ent = ent->next) {
00896         av_free(ent->ino->udata);
00897         ent->ino->udata = NULL;
00898         if(AV_ISDIR(ent->ino->st.mode)) clear_filenames(ent->ino);
00899     }
00900 }
00901 
00902 static int need_origarch(arch_inode *dir)
00903 {
00904     arch_entry *ent;
00905 
00906     for(ent = dir->subdir; ent != NULL; ent = ent->next) {
00907         if(ent->ino->tmpfile == NULL && AV_ISREG(ent->ino->st.mode) &&
00908            ent->ino->st.size != 0) 
00909             return 1;
00910 
00911         if(AV_ISDIR(ent->ino->st.mode) && need_origarch(ent->ino)) return 1;
00912     }
00913     return 0;
00914 }
00915 
00916 static int zero_block(ave *v, int outfd)
00917 {
00918     union block blk;
00919   
00920     av_memset(blk.buffer, 0, BLOCKSIZE);
00921     if(av_write(v, outfd, blk.buffer, BLOCKSIZE) == -1) return -1;
00922   
00923     return 0;
00924 }
00925 
00926 static int flush_tarfile(ave *v, vpath *path, archive *arch)
00927 {
00928     arch_file file;
00929     rep_file *rf;
00930     int res;
00931     struct ugidcache cache;
00932 
00933     av_init_ugidcache(&cache);
00934 
00935     rf = av_get_replacement(v, BASE(path), need_origarch(arch->root->ino));
00936     if(rf == NULL) return -1;
00937 
00938     file.fh = av_open(v, BASE(path), AVO_RDONLY, 0);
00939     if(file.fh == -1) {
00940         av_del_replacement(rf);
00941         return -1;
00942     }
00943     file.ptr = 0;
00944 
00945     res = write_tardir(v, &file, arch->root->ino, rf->outfd, "", 0, &cache);
00946     clear_filenames(arch->root->ino);
00947 
00948     if(res != -1) {
00949         avoff_t currsize, esize;
00950 
00951         /* This pads the size to 10 blocks */
00952         /* FIXME: Do it nicer. Maybe with buffering all the writes */
00953 
00954         currsize = av_lseek(v, rf->outfd, 0, AVSEEK_CUR);
00955         if(currsize == -1) res = -1;
00956         else {
00957             esize = AV_DIV(currsize + BLOCKSIZE, BIGBLOCKSIZE) * BIGBLOCKSIZE;
00958             while(currsize < esize) {
00959                 res = zero_block(v, rf->outfd);
00960                 if(res == -1) break;
00961                 currsize += BLOCKSIZE;
00962             }
00963         }
00964     }
00965 
00966     av_close(DUMMYV, file.fh);
00967 
00968     if(res == -1) {
00969         av_log(AVLOG_ERROR, "utar: Flush failed, errno: %i", v->errn);
00970         av_del_replacement(rf);
00971         return -1;
00972     }
00973   
00974     res = av_replace_file(v, rf);
00975     if(res == -1) {
00976         av_log(AVLOG_ERROR, "utar: Replace file failed, errno: %i", v->errn);
00977     }
00978   
00979     return res;
00980 }
00981 
00982 static int copy_file(ave *v, arch_fdi *di)
00983 {
00984     arch_inode *ino = di->ino;
00985     avoff_t currpos;
00986     char buf[COPYBUFSIZE];
00987     avssize_t rres, wres;
00988     int fd;
00989 
00990     ino->tmpfile = av_get_tmpfile(v);
00991     if(ino->tmpfile == NULL) return -1;
00992 
00993     fd = av_localopen(v, ino->tmpfile, AVO_RDWR | AVO_CREAT | AVO_EXCL,
00994                         0600);
00995     if(fd == -1) goto error;
00996   
00997     currpos = di->ptr;
00998     di->ptr = 0;
00999   
01000     while(di->ptr < di->size) {
01001         rres = tar_read(v, (void *) di, buf, COPYBUFSIZE);
01002         if(rres == -1) goto error;
01003         if(rres == 0) {
01004             v->errn = EIO;
01005             goto error;
01006         }
01007 
01008         wres = av_localwrite(v, fd, buf, rres);
01009 
01010         if(wres == -1) goto error;
01011     }
01012 
01013     av_close(DUMMYV, di->file.fh);
01014     di->file.fh = fd;
01015     di->file.ptr = di->size;
01016     di->offset = 0;
01017     ino->offset = 0;
01018   
01019     di->ptr = currpos;
01020 
01021     ino->flags |= INOF_DIRTY;
01022 
01023     return 0;
01024 
01025   error:
01026     if(fd != -1) av_localclose(DUMMYV, fd);
01027     av_del_tmpfile(ino->tmpfile);
01028     ino->tmpfile = NULL;
01029     return -1;
01030 }
01031 
01032 static avssize_t tar_write(ave *v, void *devinfo, const char *buf, 
01033                         avsize_t nbyte)
01034 {
01035     arch_fdi *di = (arch_fdi *) devinfo;
01036 
01037     if(nbyte == 0) return 0;
01038   
01039     if(di->ino->tmpfile == NULL && copy_file(v, di) == -1) return -1;
01040 
01041     return (*di->vdev->write) (v, devinfo, buf, nbyte);
01042 }
01043 
01044 #endif
01045 
01046 static void tar_release(struct archive *arch, struct archnode *nod)
01047 {
01048     struct tarnode *tn = (struct tarnode *) nod->data;
01049 
01050     if(tn != NULL) {
01051        av_free(tn->sparsearray);
01052        tn->sparsearray = NULL;
01053     }
01054 }
01055 
01056 
01057 static int read_sparsearray(struct archfile *fil)
01058 {
01059     int res;
01060     union block header;
01061     int counter;
01062     struct sp_array *sparses;
01063     struct tarnode *tn = (struct tarnode *) fil->nod->data;
01064     int size, len;
01065   
01066     av_lseek(fil->basefile, tn->headeroff, AVSEEK_SET);
01067     res = get_next_block(fil->basefile, &header);
01068     if(res < 0)
01069         return res;
01070 
01071     size = 10;
01072     len = 0;
01073     sparses = (struct sp_array *) av_malloc(size * sizeof(struct sp_array));
01074 
01075     for (counter = 0; counter < SPARSES_IN_OLDGNU_HEADER; counter++) {
01076         sparses[len].offset = 
01077             from_oct (1 + 12, header.oldgnu_header.sp[counter].offset);
01078         sparses[len].numbytes =
01079             from_oct (1 + 12, header.oldgnu_header.sp[counter].numbytes);
01080 
01081         if (!sparses[counter].numbytes) break;
01082 
01083         len++;
01084     }
01085 
01086     if (header.oldgnu_header.isextended)  {
01087         /* Read in the list of extended headers and translate them into
01088            the sparsearray as before.  */
01089 
01090         while (1) {
01091             res = get_next_block(fil->basefile, &header);
01092             if(res < 0) {
01093                 av_free(sparses);
01094                 return res;
01095             }
01096       
01097             for (counter = 0; counter < SPARSES_IN_SPARSE_HEADER; counter++) {
01098                 if (counter + len > size - 1) {
01099                     /* Realloc the scratch area since we've run out of
01100                        room.  */
01101 
01102                     size *= 2;
01103                     sparses = (struct sp_array *)
01104                         av_realloc (sparses, size * sizeof (struct sp_array));
01105                 }
01106 
01107                 if (header.sparse_header.sp[counter].numbytes[0] == 0)
01108                     break;
01109        
01110                 sparses[len].offset =
01111                     from_oct (1 + 12, header.sparse_header.sp[counter].offset);
01112                 sparses[len].numbytes =
01113                     from_oct (1 + 12, header.sparse_header.sp[counter].numbytes);
01114 
01115                 len++;
01116             }
01117             if (!header.sparse_header.isextended)
01118                 break;
01119         }
01120     }
01121   
01122     tn->sparsearray = sparses;
01123     tn->sp_array_len = len;
01124     fil->nod->offset = fil->basefile->ptr; /* the correct offset */
01125 
01126     return 0;
01127 }
01128 
01129 static avssize_t read_sparse(vfile *vf, char *buf, avsize_t nbyte)
01130 {
01131     struct archfile *fil = arch_vfile_file(vf);
01132     struct tarnode *tn = (struct tarnode *) fil->nod->data;
01133     avoff_t offset;
01134     avoff_t size     = fil->nod->st.size;
01135     avoff_t realsize = fil->nod->realsize;
01136     struct sp_array *sparses;
01137     avoff_t realoff;
01138     int ctr;
01139     avsize_t nact;
01140     avoff_t start, end;
01141     avoff_t spstart, spend;
01142     avoff_t cmstart, cmend;
01143     int res;
01144 
01145     if(AV_ISDIR(fil->nod->st.mode))
01146         return -EISDIR;
01147 
01148     if(vf->ptr >= size) 
01149         return 0;
01150   
01151     if(tn->sparsearray == NULL) {
01152         res = read_sparsearray(fil);
01153         if(res < 0)
01154             return res;
01155     }
01156     sparses = tn->sparsearray;
01157     offset = fil->nod->offset;
01158 
01159     nact = AV_MIN(nbyte, (avsize_t) (size - vf->ptr));
01160     start = vf->ptr;
01161     end = start + nact;
01162 
01163     memset(buf, 0, nact);
01164   
01165     realoff = 0;
01166     ctr = 0; 
01167     while(ctr < tn->sp_array_len && realoff < realsize) {
01168         spstart = sparses[ctr].offset;
01169         spend = spstart + sparses[ctr].numbytes;
01170 
01171         if(spstart < end && spend > start) {
01172             avoff_t rdoffset;
01173             cmstart = AV_MAX(spstart, start);
01174             cmend   = AV_MIN(spend,   end);
01175 
01176             rdoffset = realoff + offset + (cmstart - spstart);
01177             res = av_pread(fil->basefile, buf + (cmstart - start), 
01178                            cmend - cmstart, rdoffset);
01179             if(res < 0)
01180                 return res;
01181             if(res != (cmend - cmstart)) {
01182                 av_log(AVLOG_WARNING, "TAR: Broken archive");
01183                 return -EIO;
01184             }
01185         }
01186     
01187         realoff += ((spend - spstart - 1) / BLOCKSIZE + 1) * BLOCKSIZE;
01188         ctr++;
01189     }
01190   
01191     vf->ptr += nact;
01192     return nact;
01193 }
01194 
01195 
01196 static avssize_t tar_read(vfile *vf, char *buf, avsize_t nbyte)
01197 {
01198     struct archfile *fil = arch_vfile_file(vf);
01199     struct tarnode *tn = (struct tarnode *) fil->nod->data;
01200 
01201     if(tn->type == GNUTYPE_SPARSE) 
01202         return read_sparse(vf, buf, nbyte);
01203     else
01204         return av_arch_read(vf, buf, nbyte);
01205 }
01206 
01207 int av_init_module_utar(struct vmodule *module);
01208 
01209 int av_init_module_utar(struct vmodule *module)
01210 {
01211     int res;
01212     struct avfs *avfs;
01213     struct ext_info tarexts[2];
01214     struct archparams *ap;
01215     
01216     tarexts[0].from = ".tar",   tarexts[0].to = NULL;
01217     tarexts[1].from = NULL;
01218 
01219     res = av_archive_init("utar", tarexts, AV_VER, module, &avfs);
01220     if(res < 0)
01221         return res;
01222 
01223     ap = (struct archparams *) avfs->data;
01224     ap->parse = parse_tarfile;
01225     ap->read = tar_read;
01226     ap->release = tar_release;
01227 
01228     av_add_avfs(avfs);
01229 
01230     return 0;
01231 }