> I tried to copy a 8GB Xen domU disk image from a zvol device
> to an image file on an ufs filesystem, and was surprised that
> reading from the zvol character device doesn't detect "EOF".
> 
> On snv_66 (sparc) and snv_73 (x86) I can reproduce it, like this:
> 
> # zfs create -V 1440k tank/floppy-img
> 
> # dd if=/dev/zvol/dsk/tank/floppy-img of=/dev/null
> bs=1k count=2000
> 1440+0 records in
> 1440+0 records out
> (no problem on block device, we detect eof after
> reading 1440k)
> 
> 
> # dd if=/dev/zvol/rdsk/tank/floppy-img of=/dev/null
> bs=1k count=2000
> 2000+0 records in
> 2000+0 records out
> 
> (Oops!  No eof detected on zvol raw device after
> reading 1440k?)

After looking at the code in usr/src/uts/common/fs/zfs/zvol.c
it seems that neither zvol_read() nor zvol_write() cares about
the zvol's "zv_volsize".

I think we need something like this:

diff -r 26be3efbd346 usr/src/uts/common/fs/zfs/zvol.c
--- a/usr/src/uts/common/fs/zfs/zvol.c  Thu Aug 23 00:53:10 2007 -0700
+++ b/usr/src/uts/common/fs/zfs/zvol.c  Thu Aug 23 16:30:41 2007 +0200
@@ -904,6 +904,7 @@ zvol_read(dev_t dev, uio_t *uio, cred_t 
 {
        minor_t minor = getminor(dev);
        zvol_state_t *zv;
+       uint64_t volsize;
        rl_t *rl;
        int error = 0;
 
@@ -914,10 +915,16 @@ zvol_read(dev_t dev, uio_t *uio, cred_t 
        if (zv == NULL)
                return (ENXIO);
 
+       volsize = zv->zv_volsize;
+
        rl = zfs_range_lock(&zv->zv_znode, uio->uio_loffset, uio->uio_resid,
            RL_READER);
-       while (uio->uio_resid > 0) {
+       while (uio->uio_resid > 0 && uio->uio_loffset < volsize) {
                uint64_t bytes = MIN(uio->uio_resid, DMU_MAX_ACCESS >> 1);
+
+               /* don't read past the end */
+               if (bytes > volsize - uio->uio_loffset)
+                       bytes = volsize - uio->uio_loffset;
 
                error =  dmu_read_uio(zv->zv_objset, ZVOL_OBJ, uio, bytes);
                if (error)
@@ -933,6 +940,7 @@ zvol_write(dev_t dev, uio_t *uio, cred_t
 {
        minor_t minor = getminor(dev);
        zvol_state_t *zv;
+       uint64_t volsize;
        rl_t *rl;
        int error = 0;
 
@@ -943,13 +951,19 @@ zvol_write(dev_t dev, uio_t *uio, cred_t
        if (zv == NULL)
                return (ENXIO);
 
+       volsize = zv->zv_volsize;
+
        rl = zfs_range_lock(&zv->zv_znode, uio->uio_loffset, uio->uio_resid,
            RL_WRITER);
-       while (uio->uio_resid > 0) {
+       while (uio->uio_resid > 0 && uio->uio_loffset < volsize) {
                uint64_t bytes = MIN(uio->uio_resid, DMU_MAX_ACCESS >> 1);
                uint64_t off = uio->uio_loffset;
-
-               dmu_tx_t *tx = dmu_tx_create(zv->zv_objset);
+               dmu_tx_t *tx;
+
+               if (bytes > volsize - off)      /* don't write past the end */
+                       bytes = volsize - off;
+
+               tx = dmu_tx_create(zv->zv_objset);
                dmu_tx_hold_write(tx, ZVOL_OBJ, off, bytes);
                error = dmu_tx_assign(tx, TXG_WAIT);
                if (error) {
--
This messages posted from opensolaris.org

Reply via email to