The structures

We still use an extended attributes structure in the .tar filesystem, because we still need to store additional information (like the contents of directories and symlinks). The main modification is the way we store files—we don't. We store only a reference to the file, indicating where in the .tar file the actual file begins, and its name. (The base attributes structure still stores the usual stuff: permissions, file times, etc., and most importantly, the size.)

typedef struct fes_s
    char                *name;
    off_t               off;
}   fes_t;

typedef struct cfs_attr_s
    iofunc_attr_t       attr;

    int                 nels;
    int                 nalloc;
    union {
        struct des_s    *dirblocks;
//      iov_t           *fileblocks;
        fes_t           vfile;
        char            *symlinkdata;
    } type;
} cfs_attr_t;

As you can see, the cfs_attr_t is almost identical to the RAM-disk structure. In fact, I left the fileblocks member commented-out to show the evolution from one source base to the next.

So our data for the file is now stored in a fes_t data type. All that the fes_t data type contains is the name of the .tar file (where the data is actually stored), and the offset into the .tar — we don't need to store the size of the file, because that's already stored normally in the plain (not extended) attributes structure.

The other difference from the RAM-disk filesystem is that we store the fes_t directly, rather than a pointer to it (as we did with the fileblocks IOV array). That's because the fes_t doesn't ever grow, and storing a 4-byte pointer to a tiny, fixed-size malloc()'d data region will take more space than just storing the 8-byte item directly.