The .tar filesystem's io_read() is the standard one that we've seen in the RAM disk—it decides if the request is for a file or a directory, and calls the appropriate function.
The .tar filesystem's tarfs_io_read_dir() is the exact same thing as the RAM disk version—after all, the directory entry structures in the extended attributes structure are identical.
The only function that's different is the tarfs_io_read_file() function to read the data from the .tar file on disk.
int
tarfs_io_read_file (resmgr_context_t *ctp, io_read_t *msg,
iofunc_ocb_t *ocb)
{
int nbytes;
int nleft;
iov_t *iovs;
int niovs;
int i;
int pool_flag;
gzFile fd;
// we don't do any xtypes here...
if ((msg -> i.xtype & _IO_XTYPE_MASK) != _IO_XTYPE_NONE) {
return (ENOSYS);
}
// figure out how many bytes are left
nleft = ocb -> attr -> attr.nbytes - ocb -> offset;
// and how many we can return to the client
nbytes = min (nleft, msg -> i.nbytes);
if (nbytes) {
// 1) open the on-disk .tar file
if ((fd = gzopen (ocb -> attr -> type.vfile.name, "r")) == NULL) {
return (errno);
}
// 2) calculate number of IOVs required for transfer
niovs = (nbytes + BLOCKSIZE - 1) / BLOCKSIZE;
if (niovs <= 8) {
iovs = mpool_malloc (mpool_iov8);
pool_flag = 1;
} else {
iovs = malloc (sizeof (iov_t) * niovs);
pool_flag = 0;
}
if (iovs == NULL) {
gzclose (fd);
return (ENOMEM);
}
// 3) allocate blocks for the transfer
for (i = 0; i < niovs; i++) {
SETIOV (&iovs [i], cfs_block_alloc (ocb -> attr), BLOCKSIZE);
if (iovs [i].iov_base == NULL) {
for (--i ; i >= 0; i--) {
cfs_block_free (ocb -> attr, iovs [i].iov_base);
}
gzclose (fd);
return (ENOMEM);
}
}
// 4) trim last block to correctly read last entry in a .tar file
if (nbytes & BLOCKSIZE) {
iovs [niovs - 1].iov_len = nbytes & BLOCKSIZE;
}
// 5) get the data
gzseek (fd, ocb -> attr -> type.vfile.off + ocb -> offset, SEEK_SET);
for (i = 0; i < niovs; i++) {
gzread (fd, iovs [i].iov_base, iovs [i].iov_len);
}
gzclose (fd);
// return it to the client
MsgReplyv (ctp -> rcvid, nbytes, iovs, i);
// update flags and offset
ocb -> attr -> attr.flags |= IOFUNC_ATTR_ATIME
| IOFUNC_ATTR_DIRTY_TIME;
ocb -> offset += nbytes;
for (i = 0; i < niovs; i++) {
cfs_block_free (ocb -> attr, iovs [i].iov_base);
}
if (pool_flag) {
mpool_free (mpool_iov8, iovs);
} else {
free (iovs);
}
} else {
// nothing to return, indicate End Of File
MsgReply (ctp -> rcvid, EOK, NULL, 0);
}
// already done the reply ourselves
return (_RESMGR_NOREPLY);
}
Many of the steps here are common with the RAM disk version, so only steps 1 through 5 are documented here:
The rest of the code is standard; return the buffer to the client via MsgReplyv(), update the access flags and offset, free the blocks and IOVs, etc.