mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-22 02:27:36 +03:00
Fix zfs_get_data access to files with wrong generation
If TX_WRITE is create on a file, and the file is later deleted and a new directory is created on the same object id, it is possible that when zil_commit happens, zfs_get_data will be called on the new directory. This may result in panic as it tries to do range lock. This patch fixes this issue by record the generation number during zfs_log_write, so zfs_get_data can check if the object is valid. Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Signed-off-by: Chunwei Chen <david.chen@nutanix.com> Closes #10593 Closes #11682
This commit is contained in:
committed by
Tony Hutter
parent
ecb1b1a31d
commit
bfb2928490
@@ -521,7 +521,8 @@ static int zil_fault_io = 0;
|
||||
* Get data to generate a TX_WRITE intent log record.
|
||||
*/
|
||||
int
|
||||
zfs_get_data(void *arg, lr_write_t *lr, char *buf, struct lwb *lwb, zio_t *zio)
|
||||
zfs_get_data(void *arg, uint64_t gen, lr_write_t *lr, char *buf,
|
||||
struct lwb *lwb, zio_t *zio)
|
||||
{
|
||||
zfsvfs_t *zfsvfs = arg;
|
||||
objset_t *os = zfsvfs->z_os;
|
||||
@@ -532,6 +533,7 @@ zfs_get_data(void *arg, lr_write_t *lr, char *buf, struct lwb *lwb, zio_t *zio)
|
||||
dmu_buf_t *db;
|
||||
zgd_t *zgd;
|
||||
int error = 0;
|
||||
uint64_t zp_gen;
|
||||
|
||||
ASSERT3P(lwb, !=, NULL);
|
||||
ASSERT3P(zio, !=, NULL);
|
||||
@@ -550,6 +552,16 @@ zfs_get_data(void *arg, lr_write_t *lr, char *buf, struct lwb *lwb, zio_t *zio)
|
||||
zfs_zrele_async(zp);
|
||||
return (SET_ERROR(ENOENT));
|
||||
}
|
||||
/* check if generation number matches */
|
||||
if (sa_lookup(zp->z_sa_hdl, SA_ZPL_GEN(zfsvfs), &zp_gen,
|
||||
sizeof (zp_gen)) != 0) {
|
||||
zfs_zrele_async(zp);
|
||||
return (SET_ERROR(EIO));
|
||||
}
|
||||
if (zp_gen != gen) {
|
||||
zfs_zrele_async(zp);
|
||||
return (SET_ERROR(ENOENT));
|
||||
}
|
||||
|
||||
zgd = kmem_zalloc(sizeof (zgd_t), KM_SLEEP);
|
||||
zgd->zgd_lwb = lwb;
|
||||
|
||||
@@ -540,6 +540,7 @@ zfs_log_write(zilog_t *zilog, dmu_tx_t *tx, int txtype,
|
||||
uint32_t blocksize = zp->z_blksz;
|
||||
itx_wr_state_t write_state;
|
||||
uintptr_t fsync_cnt;
|
||||
uint64_t gen = 0;
|
||||
|
||||
if (zil_replaying(zilog, tx) || zp->z_unlinked ||
|
||||
zfs_xattr_owner_unlinked(zp)) {
|
||||
@@ -562,6 +563,9 @@ zfs_log_write(zilog_t *zilog, dmu_tx_t *tx, int txtype,
|
||||
(void) tsd_set(zfs_fsyncer_key, (void *)(fsync_cnt - 1));
|
||||
}
|
||||
|
||||
(void) sa_lookup(zp->z_sa_hdl, SA_ZPL_GEN(ZTOZSB(zp)), &gen,
|
||||
sizeof (gen));
|
||||
|
||||
while (resid) {
|
||||
itx_t *itx;
|
||||
lr_write_t *lr;
|
||||
@@ -609,6 +613,7 @@ zfs_log_write(zilog_t *zilog, dmu_tx_t *tx, int txtype,
|
||||
BP_ZERO(&lr->lr_blkptr);
|
||||
|
||||
itx->itx_private = ZTOZSB(zp);
|
||||
itx->itx_gen = gen;
|
||||
|
||||
if (!(ioflag & (O_SYNC | O_DSYNC)) && (zp->z_sync_cnt == 0) &&
|
||||
(fsync_cnt == 0))
|
||||
|
||||
+2
-1
@@ -1744,7 +1744,8 @@ cont:
|
||||
* completed after "lwb_write_zio" completed.
|
||||
*/
|
||||
error = zilog->zl_get_data(itx->itx_private,
|
||||
lrwb, dbuf, lwb, lwb->lwb_write_zio);
|
||||
itx->itx_gen, lrwb, dbuf, lwb,
|
||||
lwb->lwb_write_zio);
|
||||
|
||||
if (error == EIO) {
|
||||
txg_wait_synced(zilog->zl_dmu_pool, txg);
|
||||
|
||||
+2
-1
@@ -673,7 +673,8 @@ zvol_get_done(zgd_t *zgd, int error)
|
||||
* Get data to generate a TX_WRITE intent log record.
|
||||
*/
|
||||
int
|
||||
zvol_get_data(void *arg, lr_write_t *lr, char *buf, struct lwb *lwb, zio_t *zio)
|
||||
zvol_get_data(void *arg, uint64_t arg2, lr_write_t *lr, char *buf,
|
||||
struct lwb *lwb, zio_t *zio)
|
||||
{
|
||||
zvol_state_t *zv = arg;
|
||||
uint64_t offset = lr->lr_offset;
|
||||
|
||||
Reference in New Issue
Block a user