+static int
+file_io(struct vnode * vp, vfs_context_t ctx,
+ enum uio_rw op, char * base, off_t offset, user_ssize_t count,
+ user_ssize_t * resid)
+{
+ uio_t auio;
+ int error;
+ char uio_buf[UIO_SIZEOF(1)];
+
+ auio = uio_createwithbuffer(1, offset, UIO_SYSSPACE, op,
+ &uio_buf[0], sizeof(uio_buf));
+ uio_addiov(auio, CAST_USER_ADDR_T(base), count);
+ if (op == UIO_READ)
+ error = VNOP_READ(vp, auio, IO_SYNC, ctx);
+ else
+ error = VNOP_WRITE(vp, auio, IO_SYNC, ctx);
+
+ if (resid != NULL) {
+ *resid = uio_resid(auio);
+ }
+ return (error);
+}
+
+static __inline__ off_t
+block_round(off_t o, int blocksize)
+{
+ return ((o + blocksize - 1) / blocksize);
+}
+
+static __inline__ off_t
+block_truncate(off_t o, int blocksize)
+{
+ return (o / blocksize);
+}
+
+static __inline__ int
+block_remainder(off_t o, int blocksize)
+{
+ return (o % blocksize);
+}
+
+static int
+vnread_shadow(struct vn_softc * vn, struct uio *uio, int ioflag,
+ vfs_context_t ctx)
+{
+ u_int32_t blocksize = vn->sc_secsize;
+ int error = 0;
+ off_t offset;
+ user_ssize_t resid;
+ off_t orig_offset;
+ user_ssize_t orig_resid;
+
+ orig_resid = resid = uio_resid(uio);
+ orig_offset = offset = uio_offset(uio);
+
+ while (resid > 0) {
+ u_int32_t remainder;
+ u_int32_t this_block_number;
+ u_int32_t this_block_count;
+ off_t this_offset;
+ user_ssize_t this_resid;
+ struct vnode * vp;
+
+ /* figure out which blocks to read */
+ remainder = block_remainder(offset, blocksize);
+ if (shadow_map_read(vn->sc_shadow_map,
+ block_truncate(offset, blocksize),
+ block_round(resid + remainder, blocksize),
+ &this_block_number, &this_block_count)) {
+ vp = vn->sc_shadow_vp;
+ }
+ else {
+ vp = vn->sc_vp;
+ }
+
+ /* read the blocks (or parts thereof) */
+ this_offset = (off_t)this_block_number * blocksize + remainder;
+ uio_setoffset(uio, this_offset);
+ this_resid = this_block_count * blocksize - remainder;
+ if (this_resid > resid) {
+ this_resid = resid;
+ }
+ uio_setresid(uio, this_resid);
+ error = VNOP_READ(vp, uio, ioflag, ctx);
+ if (error) {
+ break;
+ }
+
+ /* figure out how much we actually read */
+ this_resid -= uio_resid(uio);
+ if (this_resid == 0) {
+ printf("vn device: vnread_shadow zero length read\n");
+ break;
+ }
+ resid -= this_resid;
+ offset += this_resid;
+ }
+ uio_setresid(uio, resid);
+ uio_setoffset(uio, offset);
+ return (error);
+}
+
+static int
+vncopy_block_to_shadow(struct vn_softc * vn, vfs_context_t ctx,
+ u_int32_t file_block, u_int32_t shadow_block)
+{
+ int error;
+ char * tmpbuf;
+
+ tmpbuf = _MALLOC(vn->sc_secsize, M_TEMP, M_WAITOK);
+ if (tmpbuf == NULL) {
+ return (ENOMEM);
+ }
+ /* read one block from file at file_block offset */
+ error = file_io(vn->sc_vp, ctx, UIO_READ,
+ tmpbuf, (off_t)file_block * vn->sc_secsize,
+ vn->sc_secsize, NULL);
+ if (error) {
+ goto done;
+ }
+ /* write one block to shadow file at shadow_block offset */
+ error = file_io(vn->sc_shadow_vp, ctx, UIO_WRITE,
+ tmpbuf, (off_t)shadow_block * vn->sc_secsize,
+ vn->sc_secsize, NULL);
+ done:
+ FREE(tmpbuf, M_TEMP);
+ return (error);
+}
+
+enum {
+ FLAGS_FIRST_BLOCK_PARTIAL = 0x1,
+ FLAGS_LAST_BLOCK_PARTIAL = 0x2
+};
+
+static int
+vnwrite_shadow(struct vn_softc * vn, struct uio *uio, int ioflag,
+ vfs_context_t ctx)
+{
+ u_int32_t blocksize = vn->sc_secsize;
+ int error = 0;
+ user_ssize_t resid;
+ off_t offset;
+
+ resid = uio_resid(uio);
+ offset = uio_offset(uio);
+
+ while (resid > 0) {
+ int flags = 0;
+ u_int32_t offset_block_number;
+ u_int32_t remainder;
+ u_int32_t resid_block_count;
+ u_int32_t shadow_block_count;
+ u_int32_t shadow_block_number;
+ user_ssize_t this_resid;
+
+ /* figure out which blocks to write */
+ offset_block_number = block_truncate(offset, blocksize);
+ remainder = block_remainder(offset, blocksize);
+ resid_block_count = block_round(resid + remainder, blocksize);
+ /* figure out if the first or last blocks are partial writes */
+ if (remainder > 0
+ && !shadow_map_is_written(vn->sc_shadow_map,
+ offset_block_number)) {
+ /* the first block is a partial write */
+ flags |= FLAGS_FIRST_BLOCK_PARTIAL;
+ }
+ if (resid_block_count > 1
+ && !shadow_map_is_written(vn->sc_shadow_map,
+ offset_block_number
+ + resid_block_count - 1)
+ && block_remainder(offset + resid, blocksize) > 0) {
+ /* the last block is a partial write */
+ flags |= FLAGS_LAST_BLOCK_PARTIAL;
+ }
+ if (shadow_map_write(vn->sc_shadow_map,
+ offset_block_number, resid_block_count,
+ &shadow_block_number,
+ &shadow_block_count)) {
+ /* shadow file is growing */
+#if 0
+ /* truncate the file to its new length before write */
+ off_t size;
+ size = (off_t)shadow_map_shadow_size(vn->sc_shadow_map)
+ * vn->sc_secsize;
+ vnode_setsize(vn->sc_shadow_vp, size, IO_SYNC, ctx);
+#endif
+ }
+ /* write the blocks (or parts thereof) */
+ uio_setoffset(uio, (off_t)
+ shadow_block_number * blocksize + remainder);
+ this_resid = (off_t)shadow_block_count * blocksize - remainder;
+ if (this_resid >= resid) {
+ this_resid = resid;
+ if ((flags & FLAGS_LAST_BLOCK_PARTIAL) != 0) {
+ /* copy the last block to the shadow */
+ u_int32_t d;
+ u_int32_t s;
+
+ s = offset_block_number
+ + resid_block_count - 1;
+ d = shadow_block_number
+ + shadow_block_count - 1;
+ error = vncopy_block_to_shadow(vn, ctx, s, d);
+ if (error) {
+ printf("vnwrite_shadow: failed to copy"
+ " block %u to shadow block %u\n",
+ s, d);
+ break;
+ }
+ }
+ }
+ uio_setresid(uio, this_resid);
+ if ((flags & FLAGS_FIRST_BLOCK_PARTIAL) != 0) {
+ /* copy the first block to the shadow */
+ error = vncopy_block_to_shadow(vn, ctx,
+ offset_block_number,
+ shadow_block_number);
+ if (error) {
+ printf("vnwrite_shadow: failed to"
+ " copy block %u to shadow block %u\n",
+ offset_block_number,
+ shadow_block_number);
+ break;
+ }
+ }
+ error = VNOP_WRITE(vn->sc_shadow_vp, uio, ioflag, ctx);
+ if (error) {
+ break;
+ }
+ /* figure out how much we actually wrote */
+ this_resid -= uio_resid(uio);
+ if (this_resid == 0) {
+ printf("vn device: vnwrite_shadow zero length write\n");
+ break;
+ }
+ resid -= this_resid;
+ offset += this_resid;
+ }
+ uio_setresid(uio, resid);
+ uio_setoffset(uio, offset);
+ return (error);
+}
+