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.