Back to index

avfs  1.0.1
Classes | Defines | Functions
utar.c File Reference
#include "gtar.h"
#include "archive.h"
#include "oper.h"
#include "ugid.h"
#include "version.h"

Go to the source code of this file.

Classes

struct  tar_entinfo
struct  sp_array
struct  tarnode

Defines

#define COPYBUFSIZE   16384
#define BIGBLOCKSIZE   (20 * BLOCKSIZE)
#define NAME_FIELD_SIZE   100
#define PREFIX_FIELD_SIZE   155
#define UNAME_FIELD_SIZE   32
#define ISSPACE(x)   isspace(x)
#define ISODIGIT(x)   ((x) >= '0' && (x) < '8')

Functions

static long long from_oct (int digs, char *where)
static int find_next_block (vfile *vf, union block *blk)
static int get_next_block (vfile *vf, union block *blk)
static enum archive_format get_header_format (union block *header)
static int read_entry (vfile *vf, struct tar_entinfo *tinf)
static void decode_header (union block *header, struct avstat *stat_info, enum archive_format *format_pointer, struct ugidcache *cache)
static int check_existing (struct entry *ent, struct avstat *tarstat)
static void fill_link (struct archive *arch, struct entry *ent, const char *linkname)
static void tarnode_delete (struct tarnode *tn)
static void fill_node (struct archive *arch, struct entry *ent, struct tar_entinfo *tinf, struct avstat *tarstat)
static void fill_tarentry (struct archive *arch, struct entry *ent, struct tar_entinfo *tinf, struct avstat *tarstat)
static void insert_tarentry (struct archive *arch, struct tar_entinfo *tinf, struct avstat *tarstat)
static int read_tarfile (vfile *vf, struct archive *arch, struct ugidcache *cache)
static int parse_tarfile (void *data, ventry *ve, struct archive *arch)
static void tar_release (struct archive *arch, struct archnode *nod)
static int read_sparsearray (struct archfile *fil)
static avssize_t read_sparse (vfile *vf, char *buf, avsize_t nbyte)
static avssize_t tar_read (vfile *vf, char *buf, avsize_t nbyte)
int av_init_module_utar (struct vmodule *module)

Class Documentation

struct tar_entinfo

Definition at line 31 of file utar.c.

Class Members
avoff_t datastart
char * linkname
char * name
avoff_t size
struct sp_array

Definition at line 40 of file utar.c.

Class Members
int numbytes
avoff_t offset
struct tarnode

Definition at line 46 of file utar.c.

Collaboration diagram for tarnode:
Class Members
avgid_t gid
char gname
avoff_t headeroff
int sp_array_len
struct sp_array * sparsearray
int type
avuid_t uid
char uname

Define Documentation

#define BIGBLOCKSIZE   (20 * BLOCKSIZE)

Definition at line 21 of file utar.c.

#define COPYBUFSIZE   16384

Definition at line 20 of file utar.c.

#define ISODIGIT (   x)    ((x) >= '0' && (x) < '8')

Definition at line 59 of file utar.c.

#define ISSPACE (   x)    isspace(x)

Definition at line 58 of file utar.c.

#define NAME_FIELD_SIZE   100

Definition at line 24 of file utar.c.

#define PREFIX_FIELD_SIZE   155

Definition at line 25 of file utar.c.

#define UNAME_FIELD_SIZE   32

Definition at line 26 of file utar.c.


Function Documentation

int av_init_module_utar ( struct vmodule module)

Definition at line 1209 of file utar.c.

{
    int res;
    struct avfs *avfs;
    struct ext_info tarexts[2];
    struct archparams *ap;
    
    tarexts[0].from = ".tar",   tarexts[0].to = NULL;
    tarexts[1].from = NULL;

    res = av_archive_init("utar", tarexts, AV_VER, module, &avfs);
    if(res < 0)
        return res;

    ap = (struct archparams *) avfs->data;
    ap->parse = parse_tarfile;
    ap->read = tar_read;
    ap->release = tar_release;

    av_add_avfs(avfs);

    return 0;
}

Here is the call graph for this function:

static int check_existing ( struct entry ent,
struct avstat tarstat 
) [static]

Definition at line 411 of file utar.c.

{
    struct archnode *nod;
    
    nod = (struct archnode *) av_namespace_get(ent);

    if(AV_ISDIR(nod->st.mode)) {
        if(AV_ISDIR(tarstat->mode)) {
            nod->st.mode = tarstat->mode;
            nod->st.uid = tarstat->uid;
            nod->st.gid = tarstat->gid;
            nod->st.mtime = tarstat->mtime;
#if 0
            /* FIXME */
            nod->origst = nod->st;
#endif
            return 0;
        }
        else {
            av_log(AVLOG_WARNING, "TAR: Overwriting directory with file");
            return 0;
        }
    }
    
    av_arch_del_node(ent);

    return 1;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void decode_header ( union block header,
struct avstat stat_info,
enum archive_format format_pointer,
struct ugidcache cache 
) [static]

Definition at line 321 of file utar.c.

{
    enum archive_format format;
    char ugname[UNAME_FIELD_SIZE+1];

    if (strcmp (header->header.magic, TMAGIC) == 0)
        format = POSIX_FORMAT;
    else if (strcmp (header->header.magic, OLDGNU_MAGIC) == 0)
        format = OLDGNU_FORMAT;
    else
        format = V7_FORMAT;
    *format_pointer = format;

    stat_info->mode = from_oct (8, header->header.mode);
    stat_info->mode &= 07777;
    stat_info->mtime.sec = from_oct (1 + 12, header->header.mtime);
    stat_info->mtime.nsec = 0;

    if(header->header.typeflag == GNUTYPE_SPARSE) 
        stat_info->size = 
            from_oct (1 + 12, header->oldgnu_header.realsize);
    else stat_info->size = from_oct (1 + 12, header->header.size);
  
    switch(header->header.typeflag) {
    case GNUTYPE_SPARSE:
    case REGTYPE:
    case AREGTYPE:
    case LNKTYPE:
    case CONTTYPE:
        stat_info->mode |= AV_IFREG;
        break;

    case GNUTYPE_DUMPDIR:
    case DIRTYPE:
        stat_info->mode |= AV_IFDIR;
        break;

    case SYMTYPE:
        stat_info->mode |= AV_IFLNK;
        break;
    
    case BLKTYPE:
        stat_info->mode |= AV_IFBLK;
        break;

    case CHRTYPE:
        stat_info->mode |= AV_IFCHR;
        break;

    case FIFOTYPE:
        stat_info->mode |= AV_IFIFO;
        break;
    }

    if (format == V7_FORMAT)
    {
        stat_info->uid = from_oct (8, header->header.uid);
        stat_info->gid = from_oct (8, header->header.gid);
        stat_info->rdev = 0;
    }
    else
    {
        ugname[UNAME_FIELD_SIZE] = '\0';

        strncpy(ugname, header->header.uname, UNAME_FIELD_SIZE);
        stat_info->uid =
            av_finduid(cache, ugname, from_oct (8, header->header.uid));

        strncpy(ugname, header->header.gname, UNAME_FIELD_SIZE);
        stat_info->gid =
            av_findgid(cache, ugname, from_oct (8, header->header.gid));

        switch (header->header.typeflag)
       {
       case BLKTYPE:
       case CHRTYPE:
            stat_info->rdev = 
                av_mkdev (from_oct (8, header->header.devmajor),
                          from_oct (8, header->header.devminor));
            break;

       default:
            stat_info->rdev = 0;
       }
    }
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void fill_link ( struct archive arch,
struct entry ent,
const char *  linkname 
) [static]

Definition at line 440 of file utar.c.

{
    struct entry *link;
    struct archnode *nod = NULL;

    link = av_arch_resolve(arch, linkname, 0, 0);
    if(link != NULL)
        nod = (struct archnode *) av_namespace_get(link);

    if(nod == NULL || AV_ISDIR(nod->st.mode))
        av_log(AVLOG_WARNING, "utar: Illegal hard link");
    else {
        nod->st.nlink ++;
        av_namespace_set(ent, nod);
        av_ref_obj(ent);
        av_ref_obj(nod);
    }

    av_unref_obj(link);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void fill_node ( struct archive arch,
struct entry ent,
struct tar_entinfo tinf,
struct avstat tarstat 
) [static]

Definition at line 467 of file utar.c.

{
    struct archnode *nod;
    struct tarnode *tn;
    union block *header = &tinf->header;

    nod = av_arch_new_node(arch, ent, AV_ISDIR(tarstat->mode));

    /* keep dev, ino, nlink */
    nod->st.mode = tarstat->mode;
    nod->st.uid = tarstat->uid;
    nod->st.gid = tarstat->gid;
    nod->st.rdev = tarstat->rdev;
    nod->st.size = tarstat->size;
    nod->st.blksize = BLOCKSIZE;
    nod->st.blocks = AV_BLOCKS(tinf->size);
    nod->st.atime = tarstat->mtime; /* FIXME */
    nod->st.mtime = tarstat->mtime;
    nod->st.ctime = tarstat->mtime;

    nod->offset = tinf->datastart;
    nod->realsize = tinf->size;

    AV_NEW_OBJ(tn, tarnode_delete);
    nod->data = tn;

    tn->sparsearray = NULL;
    tn->headeroff = tinf->datastart - BLOCKSIZE;
    tn->uid = from_oct (8, header->header.uid);
    tn->gid = from_oct (8, header->header.gid);
    strncpy(tn->uname, header->header.uname, UNAME_FIELD_SIZE);
    strncpy(tn->gname, header->header.gname, UNAME_FIELD_SIZE);
    tn->type =  header->header.typeflag;

    if(tn->type == SYMTYPE) {
        nod->linkname = tinf->linkname;
        nod->st.size = strlen(nod->linkname);
        tinf->linkname = NULL;
    }
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void fill_tarentry ( struct archive arch,
struct entry ent,
struct tar_entinfo tinf,
struct avstat tarstat 
) [static]

Definition at line 509 of file utar.c.

{
    int res;
    union block *header = &tinf->header;
    struct archnode *nod;

    nod = (struct archnode *) av_namespace_get(ent);
    if(nod != NULL) {
        res = check_existing(ent, tarstat);
        if(res != 1)
            return;
    }

    if(header->header.typeflag == LNKTYPE) 
        fill_link(arch, ent, tinf->linkname);
    else
        fill_node(arch, ent, tinf, tarstat);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int find_next_block ( vfile *  vf,
union block blk 
) [static]

Definition at line 115 of file utar.c.

{
    int res;

    res = av_read(vf, blk->buffer, BLOCKSIZE);
    if(res <= 0)
        return res;
    if(res < BLOCKSIZE) {
        av_log(AVLOG_WARNING, "TAR: Broken archive");
        return -EIO;
    }

    return 1;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static long long from_oct ( int  digs,
char *  where 
) [static]

Definition at line 66 of file utar.c.

{
    long long value;

    while (ISSPACE ((int) *where))
    {                       /* skip spaces */
        where++;
        if (--digs <= 0)
            return -1;             /* all blank field */
    }
    value = 0;
    while (digs > 0 && ISODIGIT (*where))
    {
        /* Scan til nonoctal.  */

        value = (value << 3) | (*where++ - '0');
        --digs;
    }

    if (digs > 0 && *where && !ISSPACE ((int) *where))
        return -1;                 /* ended on non-space/nul */

    return value;
}

Here is the caller graph for this function:

static enum archive_format get_header_format ( union block header) [static]

Definition at line 145 of file utar.c.

{
    enum archive_format f = V7_FORMAT;
    
    if ( strncmp( header->header.magic, TMAGIC, TMAGLEN ) == 0 &&
         strncmp( header->header.version, TVERSION, TVERSLEN ) == 0 ) {
        /* POSIX header is magic == "ustar\0" and version == "00" */
        f = POSIX_FORMAT;
    } else if ( strcmp( header->header.magic, OLDGNU_MAGIC) == 0 ) {
        /* Old GNU header is magic == "ustar " and version == "0\0" */
        /* it could be actually a pre-POSIX header */
        f = OLDGNU_FORMAT;
    }

    return f;
}

Here is the caller graph for this function:

static int get_next_block ( vfile *  vf,
union block blk 
) [static]

Definition at line 130 of file utar.c.

{
    int res;
  
    res = find_next_block(vf, blk);
    if(res < 0)
        return res;
    if(res == 0) {
         av_log(AVLOG_WARNING, "TAR: Broken archive");
         return -EIO;
    }
  
    return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void insert_tarentry ( struct archive arch,
struct tar_entinfo tinf,
struct avstat tarstat 
) [static]

Definition at line 529 of file utar.c.

{
    struct entry *ent;

    if(tinf->header.header.typeflag == GNUTYPE_SPARSE) {
#if 0 /* FIXME */
        arch->flags |= ARCHF_RDONLY;
        if(arch->readonly_reason == NULL)
            arch->readonly_reason =
                av_strdup("TAR: Cannot modify archive containing sparsefiles");
#endif
    }

    /* Appears to be a file.  But BSD tar uses the convention that a
       slash suffix means a directory.  */
    if(AV_ISREG(tarstat->mode) && tinf->name[strlen(tinf->name)-1] == '/') 
        tarstat->mode = (tarstat->mode & 07777) | AV_IFDIR;
    
    ent = av_arch_resolve(arch, tinf->name, 1, 0);
    if(ent == NULL)
        return;

    if(av_arch_isroot(arch, ent)) {
        /* that is not so unusual, archives created with
           "tar -cf test.tar ." have a ./ entry so
           the warning is disabled */
        /* av_log(AVLOG_WARNING, "TAR: Empty filename");*/
    } else
        fill_tarentry(arch, ent, tinf, tarstat);

    av_unref_obj(ent);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int parse_tarfile ( void *  data,
ventry *  ve,
struct archive arch 
) [static]

Definition at line 601 of file utar.c.

{
    int res;
    vfile *vf;
    struct ugidcache *cache;

    res = av_open(ve->mnt->base, AVO_RDONLY, 0, &vf);
    if(res < 0)
        return res;

    cache = av_new_ugidcache();
    res = read_tarfile(vf, arch, cache);
    av_unref_obj(cache);

    av_close(vf);
    
    return res;  
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int read_entry ( vfile *  vf,
struct tar_entinfo tinf 
) [static]

Definition at line 163 of file utar.c.

{
    int i;
    long unsigned_sum;             /* the POSIX one :-) */
    long signed_sum;        /* the Sun one :-( */
    long recorded_sum;
    char *p;
    char **longp;
    char *bp;
    union block data_block;
    int size, written;
    int res;
    avoff_t sres;
    char *next_long_name = NULL, *next_long_link = NULL;
    union block *header = &tinf->header;

    while (1)
    {
        res = find_next_block(vf, header);
        if(res <= 0) break; /* HEADER_END_OF_FILE */

        recorded_sum
            = from_oct (sizeof header->header.chksum, header->header.chksum);

        unsigned_sum = 0;
        signed_sum = 0;
        p = header->buffer;
        for (i = sizeof (*header); --i >= 0;)
       {
            /* We can't use unsigned char here because of old compilers,
               e.g. V7.  */

            unsigned_sum += 0xFF & *p;
            signed_sum += *p++;
       }

        /* Adjust checksum to count the "chksum" field as blanks.  */

        for (i = sizeof (header->header.chksum); --i >= 0;)
       {
            unsigned_sum -= 0xFF & header->header.chksum[i];
            signed_sum -= header->header.chksum[i];
       }
        unsigned_sum += ' ' * sizeof header->header.chksum;
        signed_sum += ' ' * sizeof header->header.chksum;

        if (unsigned_sum == sizeof header->header.chksum * ' ')
       {
            /* This is a zeroed block...whole block is 0's except for the
               blanks we faked for the checksum field.  */

            res = 0;
            break; /* HEADER_ZERO_BLOCK */
       }

        if (unsigned_sum != recorded_sum && signed_sum != recorded_sum) {
            res = 1;
            av_log(AVLOG_WARNING, "TAR: Bad header");
            break; /* HEADER_FAILURE */
        }

        /* Good block.  Decode file size and return.  */

        if (header->header.typeflag == LNKTYPE)
            tinf->size  = 0;       /* links 0 size on tape */
        else
            tinf->size = from_oct (1 + 12, header->header.size);

        if (header->header.typeflag == GNUTYPE_LONGNAME
            || header->header.typeflag == GNUTYPE_LONGLINK)
       {
            longp = ((header->header.typeflag == GNUTYPE_LONGNAME)
                     ? &next_long_name
                     : &next_long_link);

            if (*longp) av_free (*longp);
            bp = *longp = (char *) av_malloc ((avsize_t) tinf->size);

            for (size = tinf->size; size > 0; size -= written)
           {
                res = get_next_block (vf, &data_block);
                if (res < 0) break;
                written = BLOCKSIZE;
                if (written > size)
                    written = size;

                memcpy (bp, data_block.buffer, (avsize_t) written);
                bp += written;
           }
            if(res < 0) break;

            /* Loop!  */

       }
        else
       {
            tinf->datastart = vf->ptr;

            if (header->oldgnu_header.isextended) {
                do {
                    res = get_next_block (vf, &data_block);
                    if(res < 0) break;
                }
                while(data_block.sparse_header.isextended);
            }
            if(res < 0) break;

            sres = av_lseek(vf, AV_DIV(tinf->size, BLOCKSIZE) * BLOCKSIZE, 
                            AVSEEK_CUR);
            if(sres < 0)
                break;

            if (header->header.typeflag == 'g')
                continue;

            if ( get_header_format( header ) == POSIX_FORMAT ) {
                /* POSIX ustar format uses prefix for long file names
                   the actual name is prefix/name
                */
                if ( header->header.prefix[0] != '\0' ) {
                    char *tmp_prefix, *tmp_name;

                    tmp_prefix = av_strndup( header->header.prefix, PREFIX_FIELD_SIZE );
                    tmp_name = av_strndup( header->header.name, NAME_FIELD_SIZE );
                    
                    if ( next_long_name )
                        av_free(next_long_name);
                    
                    next_long_name = av_stradd( NULL, tmp_prefix, "/", tmp_name, NULL );
                    
                    av_free( tmp_prefix );
                    av_free( tmp_name );
                }
            }

            /* NOTE: header->header.name is not necessarily null-terminated */
            if ( next_long_name ) {
                tinf->name = av_strdup (next_long_name);
            } else {
                tinf->name = av_strndup( header->header.name, NAME_FIELD_SIZE );
            }

            if ( next_long_link ) {
                tinf->linkname = av_strdup (next_long_link);
            } else {
                tinf->linkname = av_strndup( header->header.linkname, NAME_FIELD_SIZE );
            }
            res = 2;
            break; /* HEADER_SUCCESS */
       }
    }

    av_free(next_long_name);
    av_free(next_long_link);
    return res;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static avssize_t read_sparse ( vfile *  vf,
char *  buf,
avsize_t  nbyte 
) [static]

Definition at line 1129 of file utar.c.

{
    struct archfile *fil = arch_vfile_file(vf);
    struct tarnode *tn = (struct tarnode *) fil->nod->data;
    avoff_t offset;
    avoff_t size     = fil->nod->st.size;
    avoff_t realsize = fil->nod->realsize;
    struct sp_array *sparses;
    avoff_t realoff;
    int ctr;
    avsize_t nact;
    avoff_t start, end;
    avoff_t spstart, spend;
    avoff_t cmstart, cmend;
    int res;

    if(AV_ISDIR(fil->nod->st.mode))
        return -EISDIR;

    if(vf->ptr >= size) 
        return 0;
  
    if(tn->sparsearray == NULL) {
        res = read_sparsearray(fil);
        if(res < 0)
            return res;
    }
    sparses = tn->sparsearray;
    offset = fil->nod->offset;

    nact = AV_MIN(nbyte, (avsize_t) (size - vf->ptr));
    start = vf->ptr;
    end = start + nact;

    memset(buf, 0, nact);
  
    realoff = 0;
    ctr = 0; 
    while(ctr < tn->sp_array_len && realoff < realsize) {
        spstart = sparses[ctr].offset;
        spend = spstart + sparses[ctr].numbytes;

        if(spstart < end && spend > start) {
            avoff_t rdoffset;
            cmstart = AV_MAX(spstart, start);
            cmend   = AV_MIN(spend,   end);

            rdoffset = realoff + offset + (cmstart - spstart);
            res = av_pread(fil->basefile, buf + (cmstart - start), 
                           cmend - cmstart, rdoffset);
            if(res < 0)
                return res;
            if(res != (cmend - cmstart)) {
                av_log(AVLOG_WARNING, "TAR: Broken archive");
                return -EIO;
            }
        }
    
        realoff += ((spend - spstart - 1) / BLOCKSIZE + 1) * BLOCKSIZE;
        ctr++;
    }
  
    vf->ptr += nact;
    return nact;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int read_sparsearray ( struct archfile fil) [static]

Definition at line 1057 of file utar.c.

{
    int res;
    union block header;
    int counter;
    struct sp_array *sparses;
    struct tarnode *tn = (struct tarnode *) fil->nod->data;
    int size, len;
  
    av_lseek(fil->basefile, tn->headeroff, AVSEEK_SET);
    res = get_next_block(fil->basefile, &header);
    if(res < 0)
        return res;

    size = 10;
    len = 0;
    sparses = (struct sp_array *) av_malloc(size * sizeof(struct sp_array));

    for (counter = 0; counter < SPARSES_IN_OLDGNU_HEADER; counter++) {
        sparses[len].offset = 
            from_oct (1 + 12, header.oldgnu_header.sp[counter].offset);
        sparses[len].numbytes =
            from_oct (1 + 12, header.oldgnu_header.sp[counter].numbytes);

        if (!sparses[counter].numbytes) break;

        len++;
    }

    if (header.oldgnu_header.isextended)  {
        /* Read in the list of extended headers and translate them into
           the sparsearray as before.  */

        while (1) {
            res = get_next_block(fil->basefile, &header);
            if(res < 0) {
                av_free(sparses);
                return res;
            }
      
            for (counter = 0; counter < SPARSES_IN_SPARSE_HEADER; counter++) {
                if (counter + len > size - 1) {
                    /* Realloc the scratch area since we've run out of
                       room.  */

                    size *= 2;
                    sparses = (struct sp_array *)
                        av_realloc (sparses, size * sizeof (struct sp_array));
                }

                if (header.sparse_header.sp[counter].numbytes[0] == 0)
                    break;
       
                sparses[len].offset =
                    from_oct (1 + 12, header.sparse_header.sp[counter].offset);
                sparses[len].numbytes =
                    from_oct (1 + 12, header.sparse_header.sp[counter].numbytes);

                len++;
            }
            if (!header.sparse_header.isextended)
                break;
        }
    }
  
    tn->sparsearray = sparses;
    tn->sp_array_len = len;
    fil->nod->offset = fil->basefile->ptr; /* the correct offset */

    return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static int read_tarfile ( vfile *  vf,
struct archive arch,
struct ugidcache cache 
) [static]

Definition at line 563 of file utar.c.

{
    struct tar_entinfo tinf;
    enum archive_format format;
    struct avstat tarstat;
    int res;

    while(1) {
        res = read_entry(vf, &tinf);
        if(res < 0)
            return res;
        else if(res == 1) {
#if 0  /* FIXME */
            arch->flags |= ARCHF_RDONLY; /* Broken archive */

            if(arch->readonly_reason == NULL) 
                arch->readonly_reason = 
                    av_strdup("TAR: Cannot modify archive with errors");

#endif
            continue;
        }
        else if(res == 0)
            break;

        av_default_stat(&tarstat);
        decode_header(&tinf.header, &tarstat, &format, cache);

        insert_tarentry(arch, &tinf, &tarstat);
        av_free(tinf.name);
        av_free(tinf.linkname);
    }

    return 0;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static avssize_t tar_read ( vfile *  vf,
char *  buf,
avsize_t  nbyte 
) [static]

Definition at line 1196 of file utar.c.

{
    struct archfile *fil = arch_vfile_file(vf);
    struct tarnode *tn = (struct tarnode *) fil->nod->data;

    if(tn->type == GNUTYPE_SPARSE) 
        return read_sparse(vf, buf, nbyte);
    else
        return av_arch_read(vf, buf, nbyte);
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void tar_release ( struct archive arch,
struct archnode nod 
) [static]

Definition at line 1046 of file utar.c.

{
    struct tarnode *tn = (struct tarnode *) nod->data;

    if(tn != NULL) {
       av_free(tn->sparsearray);
       tn->sparsearray = NULL;
    }
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void tarnode_delete ( struct tarnode tn) [static]

Definition at line 462 of file utar.c.

{
    av_free(tn->sparsearray);
}

Here is the call graph for this function:

Here is the caller graph for this function: