Print this page
OS-1566 dataset quota for ZFS datasets
*** 49,58 ****
--- 49,59 ----
/* Set this tunable to TRUE to replace corrupt data with 0x2f5baddb10c */
int zfs_send_corrupt_data = B_FALSE;
static char *dmu_recv_tag = "dmu_recv_tag";
+ char *tmp_dmu_recv_tag = "tmp_dmu_recv_tag";
static int
dump_bytes(dmu_sendarg_t *dsp, void *buf, int len)
{
dsl_dataset_t *ds = dsp->dsa_os->os_dsl_dataset;
*** 641,650 ****
--- 642,667 ----
return (EINVAL);
if (rbsa->origin->ds_phys->ds_guid != rbsa->fromguid)
return (ENODEV);
}
+ /*
+ * Check dataset and snapshot quotas before receiving. We'll recheck
+ * again at the end, but might as well abort before receiving if we're
+ * already over quota.
+ */
+ if (dd->dd_parent != NULL) {
+ err = dsl_dir_dscount_check(dd->dd_parent, NULL, 1, NULL);
+ if (err != 0)
+ return (err);
+ }
+
+ err = dsl_snapcount_check(dd, tx, 1, NULL);
+ if (err != 0)
+ return (err);
+
+
return (0);
}
static void
recv_new_sync(void *arg1, void *arg2, dmu_tx_t *tx)
*** 723,732 ****
--- 740,754 ----
}
} else {
/* if full, most recent snapshot must be $ORIGIN */
if (ds->ds_phys->ds_prev_snap_txg >= TXG_INITIAL)
return (ENODEV);
+
+ /* Check snapshot quota before receiving */
+ err = dsl_snapcount_check(ds->ds_dir, tx, 1, NULL);
+ if (err != 0)
+ return (err);
}
/* temporary clone name must not exist */
err = zap_lookup(ds->ds_dir->dd_pool->dp_meta_objset,
ds->ds_dir->dd_phys->dd_child_dir_zapobj,
*** 1545,1571 ****
struct recvendsyncarg {
char *tosnap;
uint64_t creation_time;
uint64_t toguid;
};
static int
recv_end_check(void *arg1, void *arg2, dmu_tx_t *tx)
{
dsl_dataset_t *ds = arg1;
struct recvendsyncarg *resa = arg2;
! return (dsl_dataset_snapshot_check(ds, resa->tosnap, tx));
}
static void
recv_end_sync(void *arg1, void *arg2, dmu_tx_t *tx)
{
dsl_dataset_t *ds = arg1;
struct recvendsyncarg *resa = arg2;
dsl_dataset_snapshot_sync(ds, resa->tosnap, tx);
/* set snapshot's creation time and guid */
dmu_buf_will_dirty(ds->ds_prev->ds_dbuf, tx);
ds->ds_prev->ds_phys->ds_creation_time = resa->creation_time;
--- 1567,1613 ----
struct recvendsyncarg {
char *tosnap;
uint64_t creation_time;
uint64_t toguid;
+ boolean_t is_new;
};
static int
recv_end_check(void *arg1, void *arg2, dmu_tx_t *tx)
{
dsl_dataset_t *ds = arg1;
struct recvendsyncarg *resa = arg2;
! if (resa->is_new) {
! /* re-check the dataset quota now that recv is complete */
! dsl_dir_t *dd;
! int err;
!
! dd = ds->ds_dir;
! if (dd->dd_parent != NULL) {
! err = dsl_dir_dscount_check(dd->dd_parent, NULL, 1,
! NULL);
! if (err != 0)
! return (err);
! }
! }
!
! return (dsl_dataset_snapshot_check(ds, resa->tosnap, 1, tx));
}
static void
recv_end_sync(void *arg1, void *arg2, dmu_tx_t *tx)
{
dsl_dataset_t *ds = arg1;
struct recvendsyncarg *resa = arg2;
+ if (resa->is_new)
+ /* update the dataset counts */
+ dsl_dir_dscount_adjust(ds->ds_dir->dd_parent, tx, 1, B_FALSE,
+ B_TRUE);
+
dsl_dataset_snapshot_sync(ds, resa->tosnap, tx);
/* set snapshot's creation time and guid */
dmu_buf_will_dirty(ds->ds_prev->ds_dbuf, tx);
ds->ds_prev->ds_phys->ds_creation_time = resa->creation_time;
*** 1614,1631 ****
if (err)
goto out;
} else {
mutex_exit(&ds->ds_recvlock);
dsl_dataset_rele(ds, dmu_recv_tag);
! (void) dsl_dataset_destroy(drc->drc_real_ds, dmu_recv_tag,
B_FALSE);
return (EBUSY);
}
resa.creation_time = drc->drc_drrb->drr_creation_time;
resa.toguid = drc->drc_drrb->drr_toguid;
resa.tosnap = drc->drc_tosnap;
err = dsl_sync_task_do(ds->ds_dir->dd_pool,
recv_end_check, recv_end_sync, ds, &resa, 3);
if (err) {
/* swap back */
--- 1656,1675 ----
if (err)
goto out;
} else {
mutex_exit(&ds->ds_recvlock);
dsl_dataset_rele(ds, dmu_recv_tag);
! /* tag indicates temporary ds to dsl_dir_destroy_sync */
! (void) dsl_dataset_destroy(drc->drc_real_ds, tmp_dmu_recv_tag,
B_FALSE);
return (EBUSY);
}
resa.creation_time = drc->drc_drrb->drr_creation_time;
resa.toguid = drc->drc_drrb->drr_toguid;
resa.tosnap = drc->drc_tosnap;
+ resa.is_new = B_FALSE;
err = dsl_sync_task_do(ds->ds_dir->dd_pool,
recv_end_check, recv_end_sync, ds, &resa, 3);
if (err) {
/* swap back */
*** 1635,1645 ****
out:
mutex_exit(&ds->ds_recvlock);
if (err == 0 && drc->drc_guid_to_ds_map != NULL)
(void) add_ds_to_guidmap(drc->drc_guid_to_ds_map, ds);
dsl_dataset_disown(ds, dmu_recv_tag);
! myerr = dsl_dataset_destroy(drc->drc_real_ds, dmu_recv_tag, B_FALSE);
ASSERT0(myerr);
return (err);
}
static int
--- 1679,1691 ----
out:
mutex_exit(&ds->ds_recvlock);
if (err == 0 && drc->drc_guid_to_ds_map != NULL)
(void) add_ds_to_guidmap(drc->drc_guid_to_ds_map, ds);
dsl_dataset_disown(ds, dmu_recv_tag);
! /* tag indicates temporary ds to dsl_dir_destroy_sync */
! myerr = dsl_dataset_destroy(drc->drc_real_ds, tmp_dmu_recv_tag,
! B_FALSE);
ASSERT0(myerr);
return (err);
}
static int
*** 1657,1666 ****
--- 1703,1713 ----
txg_wait_synced(ds->ds_dir->dd_pool, 0);
resa.creation_time = drc->drc_drrb->drr_creation_time;
resa.toguid = drc->drc_drrb->drr_toguid;
resa.tosnap = drc->drc_tosnap;
+ resa.is_new = B_TRUE;
err = dsl_sync_task_do(ds->ds_dir->dd_pool,
recv_end_check, recv_end_sync, ds, &resa, 3);
if (err) {
/* clean up the fs we just recv'd into */