Print this page
OS-1566 filesystem limits for ZFS datasets

*** 641,650 **** --- 641,665 ---- return (EINVAL); if (rbsa->origin->ds_phys->ds_guid != rbsa->fromguid) return (ENODEV); } + /* + * Check filesystem and snapshot limits before receiving. We'll recheck + * again at the end, but might as well abort before receiving if we're + * already over the limit. + */ + if (dd->dd_parent != NULL) { + err = dsl_dir_fscount_check(dd->dd_parent, 1, NULL); + if (err != 0) + return (err); + } + + err = dsl_snapcount_check(dd, 1, NULL); + if (err != 0) + return (err); + return (0); } static void recv_new_sync(void *arg1, void *arg2, dmu_tx_t *tx)
*** 723,732 **** --- 738,752 ---- } } else { /* if full, most recent snapshot must be $ORIGIN */ if (ds->ds_phys->ds_prev_snap_txg >= TXG_INITIAL) return (ENODEV); + + /* Check snapshot limit before receiving */ + err = dsl_snapcount_check(ds->ds_dir, 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; --- 1565,1610 ---- 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 filesystem limit now that recv is complete */ ! dsl_dir_t *dd; ! int err; ! ! dd = ds->ds_dir; ! if (dd->dd_parent != NULL) { ! err = dsl_dir_fscount_check(dd->dd_parent, 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 filesystem counts */ + dsl_dir_fscount_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;
*** 1622,1631 **** --- 1661,1671 ---- } 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 */
*** 1657,1666 **** --- 1697,1707 ---- 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 */