> 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