+ if ((flags & FWRITE) && (vn->sc_flags & VNF_READONLY)) {
+ return EACCES;
+ }
+
+ return 0;
+}
+
+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;
+ off_t this_block_number;
+ size_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 = 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,
+ off_t file_block, off_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, 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, 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);