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:
Chunwei Chen 2021-03-19 22:53:31 -07:00 committed by Tony Hutter
parent ab2110e3e2
commit e68e938f54
1 changed files with 13 additions and 1 deletions

View File

@ -739,7 +739,8 @@ static void zfs_get_done(zgd_t *zgd, int error);
* Get data to generate a TX_WRITE intent log record. * Get data to generate a TX_WRITE intent log record.
*/ */
int 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; zfsvfs_t *zfsvfs = arg;
objset_t *os = zfsvfs->z_os; objset_t *os = zfsvfs->z_os;
@ -750,6 +751,7 @@ zfs_get_data(void *arg, lr_write_t *lr, char *buf, struct lwb *lwb, zio_t *zio)
dmu_buf_t *db; dmu_buf_t *db;
zgd_t *zgd; zgd_t *zgd;
int error = 0; int error = 0;
uint64_t zp_gen;
ASSERT3P(lwb, !=, NULL); ASSERT3P(lwb, !=, NULL);
ASSERT3P(zio, !=, NULL); ASSERT3P(zio, !=, NULL);
@ -768,6 +770,16 @@ zfs_get_data(void *arg, lr_write_t *lr, char *buf, struct lwb *lwb, zio_t *zio)
zfs_zrele_async(zp); zfs_zrele_async(zp);
return (SET_ERROR(ENOENT)); 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 = (zgd_t *)kmem_zalloc(sizeof (zgd_t), KM_SLEEP); zgd = (zgd_t *)kmem_zalloc(sizeof (zgd_t), KM_SLEEP);
zgd->zgd_lwb = lwb; zgd->zgd_lwb = lwb;