897 if (err) |
913 if (err) |
898 return (err); |
914 return (err); |
899 da.dstg = dsl_sync_task_group_create(spa_get_dsl(spa)); |
915 da.dstg = dsl_sync_task_group_create(spa_get_dsl(spa)); |
900 da.snapname = snapname; |
916 da.snapname = snapname; |
901 da.failed = fsname; |
917 da.failed = fsname; |
|
918 da.defer = defer; |
902 |
919 |
903 err = dmu_objset_find(fsname, |
920 err = dmu_objset_find(fsname, |
904 dsl_snapshot_destroy_one, &da, DS_FIND_CHILDREN); |
921 dsl_snapshot_destroy_one, &da, DS_FIND_CHILDREN); |
905 |
922 |
906 if (err == 0) |
923 if (err == 0) |
907 err = dsl_sync_task_group_wait(da.dstg); |
924 err = dsl_sync_task_group_wait(da.dstg); |
908 |
925 |
909 for (dst = list_head(&da.dstg->dstg_tasks); dst; |
926 for (dst = list_head(&da.dstg->dstg_tasks); dst; |
910 dst = list_next(&da.dstg->dstg_tasks, dst)) { |
927 dst = list_next(&da.dstg->dstg_tasks, dst)) { |
911 dsl_dataset_t *ds = dst->dst_arg1; |
928 struct dsl_ds_destroyarg *dsda = dst->dst_arg1; |
|
929 dsl_dataset_t *ds = dsda->ds; |
|
930 |
912 /* |
931 /* |
913 * Return the file system name that triggered the error |
932 * Return the file system name that triggered the error |
914 */ |
933 */ |
915 if (dst->dst_err) { |
934 if (dst->dst_err) { |
916 dsl_dataset_name(ds, fsname); |
935 dsl_dataset_name(ds, fsname); |
917 *strchr(fsname, '@') = '\0'; |
936 *strchr(fsname, '@') = '\0'; |
918 } |
937 } |
|
938 ASSERT3P(dsda->rm_origin, ==, NULL); |
919 dsl_dataset_disown(ds, da.dstg); |
939 dsl_dataset_disown(ds, da.dstg); |
|
940 kmem_free(dsda, sizeof (struct dsl_ds_destroyarg)); |
920 } |
941 } |
921 |
942 |
922 dsl_sync_task_group_destroy(da.dstg); |
943 dsl_sync_task_group_destroy(da.dstg); |
923 spa_close(spa, FTAG); |
944 spa_close(spa, FTAG); |
924 return (err); |
945 return (err); |
|
946 } |
|
947 |
|
948 static boolean_t |
|
949 dsl_dataset_might_destroy_origin(dsl_dataset_t *ds) |
|
950 { |
|
951 boolean_t might_destroy = B_FALSE; |
|
952 |
|
953 mutex_enter(&ds->ds_lock); |
|
954 if (ds->ds_phys->ds_num_children == 2 && ds->ds_userrefs == 0 && |
|
955 DS_IS_DEFER_DESTROY(ds)) |
|
956 might_destroy = B_TRUE; |
|
957 mutex_exit(&ds->ds_lock); |
|
958 |
|
959 return (might_destroy); |
|
960 } |
|
961 |
|
962 #ifdef _KERNEL |
|
963 static int |
|
964 dsl_dataset_zvol_cleanup(dsl_dataset_t *ds, const char *name) |
|
965 { |
|
966 int error; |
|
967 objset_t *os; |
|
968 |
|
969 error = dmu_objset_open_ds(ds, DMU_OST_ANY, &os); |
|
970 if (error) |
|
971 return (error); |
|
972 |
|
973 if (dmu_objset_type(os) == DMU_OST_ZVOL) |
|
974 error = zvol_remove_minor(name); |
|
975 dmu_objset_close(os); |
|
976 |
|
977 return (error); |
|
978 } |
|
979 #endif |
|
980 |
|
981 /* |
|
982 * If we're removing a clone, and these three conditions are true: |
|
983 * 1) the clone's origin has no other children |
|
984 * 2) the clone's origin has no user references |
|
985 * 3) the clone's origin has been marked for deferred destruction |
|
986 * Then, prepare to remove the origin as part of this sync task group. |
|
987 */ |
|
988 static int |
|
989 dsl_dataset_origin_rm_prep(struct dsl_ds_destroyarg *dsda, void *tag) |
|
990 { |
|
991 dsl_dataset_t *ds = dsda->ds; |
|
992 dsl_dataset_t *origin = ds->ds_prev; |
|
993 |
|
994 if (dsl_dataset_might_destroy_origin(origin)) { |
|
995 char *name; |
|
996 int namelen; |
|
997 int error; |
|
998 |
|
999 namelen = dsl_dataset_namelen(origin) + 1; |
|
1000 name = kmem_alloc(namelen, KM_SLEEP); |
|
1001 dsl_dataset_name(origin, name); |
|
1002 #ifdef _KERNEL |
|
1003 error = zfs_unmount_snap(name, NULL); |
|
1004 if (error) { |
|
1005 kmem_free(name, namelen); |
|
1006 return (error); |
|
1007 } |
|
1008 error = dsl_dataset_zvol_cleanup(origin, name); |
|
1009 if (error) { |
|
1010 kmem_free(name, namelen); |
|
1011 return (error); |
|
1012 } |
|
1013 #endif |
|
1014 error = dsl_dataset_own(name, |
|
1015 DS_MODE_READONLY | DS_MODE_INCONSISTENT, |
|
1016 tag, &origin); |
|
1017 kmem_free(name, namelen); |
|
1018 if (error) |
|
1019 return (error); |
|
1020 dsda->rm_origin = origin; |
|
1021 dsl_dataset_make_exclusive(origin, tag); |
|
1022 } |
|
1023 |
|
1024 return (0); |
925 } |
1025 } |
926 |
1026 |
927 /* |
1027 /* |
928 * ds must be opened as OWNER. On return (whether successful or not), |
1028 * ds must be opened as OWNER. On return (whether successful or not), |
929 * ds will be closed and caller can no longer dereference it. |
1029 * ds will be closed and caller can no longer dereference it. |
930 */ |
1030 */ |
931 int |
1031 int |
932 dsl_dataset_destroy(dsl_dataset_t *ds, void *tag) |
1032 dsl_dataset_destroy(dsl_dataset_t *ds, void *tag, boolean_t defer) |
933 { |
1033 { |
934 int err; |
1034 int err; |
935 dsl_sync_task_group_t *dstg; |
1035 dsl_sync_task_group_t *dstg; |
936 objset_t *os; |
1036 objset_t *os; |
937 dsl_dir_t *dd; |
1037 dsl_dir_t *dd; |
938 uint64_t obj; |
1038 uint64_t obj; |
|
1039 struct dsl_ds_destroyarg dsda = {0}; |
|
1040 |
|
1041 dsda.ds = ds; |
939 |
1042 |
940 if (dsl_dataset_is_snapshot(ds)) { |
1043 if (dsl_dataset_is_snapshot(ds)) { |
941 /* Destroying a snapshot is simpler */ |
1044 /* Destroying a snapshot is simpler */ |
942 dsl_dataset_make_exclusive(ds, tag); |
1045 dsl_dataset_make_exclusive(ds, tag); |
943 |
1046 |
944 if (ds->ds_user_ptr) { |
1047 if (ds->ds_user_ptr) { |
945 ds->ds_user_evict_func(ds, ds->ds_user_ptr); |
1048 ds->ds_user_evict_func(ds, ds->ds_user_ptr); |
946 ds->ds_user_ptr = NULL; |
1049 ds->ds_user_ptr = NULL; |
947 } |
1050 } |
|
1051 /* NOTE: defer is always B_FALSE for non-snapshots */ |
|
1052 dsda.defer = defer; |
948 err = dsl_sync_task_do(ds->ds_dir->dd_pool, |
1053 err = dsl_sync_task_do(ds->ds_dir->dd_pool, |
949 dsl_dataset_destroy_check, dsl_dataset_destroy_sync, |
1054 dsl_dataset_destroy_check, dsl_dataset_destroy_sync, |
950 ds, tag, 0); |
1055 &dsda, tag, 0); |
|
1056 ASSERT3P(dsda.rm_origin, ==, NULL); |
951 goto out; |
1057 goto out; |
952 } |
1058 } |
953 |
1059 |
954 dd = ds->ds_dir; |
1060 dd = ds->ds_dir; |
955 |
1061 |
1026 dsl_dataset_make_exclusive(ds, tag); |
1132 dsl_dataset_make_exclusive(ds, tag); |
1027 if (ds->ds_user_ptr) { |
1133 if (ds->ds_user_ptr) { |
1028 ds->ds_user_evict_func(ds, ds->ds_user_ptr); |
1134 ds->ds_user_evict_func(ds, ds->ds_user_ptr); |
1029 ds->ds_user_ptr = NULL; |
1135 ds->ds_user_ptr = NULL; |
1030 } |
1136 } |
1031 dstg = dsl_sync_task_group_create(ds->ds_dir->dd_pool); |
1137 |
1032 dsl_sync_task_create(dstg, dsl_dataset_destroy_check, |
1138 /* |
1033 dsl_dataset_destroy_sync, ds, tag, 0); |
1139 * If we're removing a clone, we might also need to remove its |
1034 dsl_sync_task_create(dstg, dsl_dir_destroy_check, |
1140 * origin. |
1035 dsl_dir_destroy_sync, dd, FTAG, 0); |
1141 */ |
1036 err = dsl_sync_task_group_wait(dstg); |
1142 do { |
1037 dsl_sync_task_group_destroy(dstg); |
1143 dsda.need_prep = B_FALSE; |
|
1144 if (dsl_dir_is_clone(dd)) { |
|
1145 err = dsl_dataset_origin_rm_prep(&dsda, tag); |
|
1146 if (err) { |
|
1147 dsl_dir_close(dd, FTAG); |
|
1148 goto out; |
|
1149 } |
|
1150 } |
|
1151 |
|
1152 dstg = dsl_sync_task_group_create(ds->ds_dir->dd_pool); |
|
1153 dsl_sync_task_create(dstg, dsl_dataset_destroy_check, |
|
1154 dsl_dataset_destroy_sync, &dsda, tag, 0); |
|
1155 dsl_sync_task_create(dstg, dsl_dir_destroy_check, |
|
1156 dsl_dir_destroy_sync, dd, FTAG, 0); |
|
1157 err = dsl_sync_task_group_wait(dstg); |
|
1158 dsl_sync_task_group_destroy(dstg); |
|
1159 |
|
1160 /* |
|
1161 * We could be racing against 'zfs release' or 'zfs destroy -d' |
|
1162 * on the origin snap, in which case we can get EBUSY if we |
|
1163 * needed to destroy the origin snap but were not ready to |
|
1164 * do so. |
|
1165 */ |
|
1166 if (dsda.need_prep) { |
|
1167 ASSERT(err == EBUSY); |
|
1168 ASSERT(dsl_dir_is_clone(dd)); |
|
1169 ASSERT(dsda.rm_origin == NULL); |
|
1170 } |
|
1171 } while (dsda.need_prep); |
|
1172 |
|
1173 if (dsda.rm_origin != NULL) |
|
1174 dsl_dataset_disown(dsda.rm_origin, tag); |
|
1175 |
1038 /* if it is successful, dsl_dir_destroy_sync will close the dd */ |
1176 /* if it is successful, dsl_dir_destroy_sync will close the dd */ |
1039 if (err) |
1177 if (err) |
1040 dsl_dir_close(dd, FTAG); |
1178 dsl_dir_close(dd, FTAG); |
1041 out: |
1179 out: |
1042 dsl_dataset_disown(ds, tag); |
1180 dsl_dataset_disown(ds, tag); |
3142 dsl_dataset_set_reservation_check, |
3399 dsl_dataset_set_reservation_check, |
3143 dsl_dataset_set_reservation_sync, ds, &reservation, 0); |
3400 dsl_dataset_set_reservation_sync, ds, &reservation, 0); |
3144 dsl_dataset_rele(ds, FTAG); |
3401 dsl_dataset_rele(ds, FTAG); |
3145 return (err); |
3402 return (err); |
3146 } |
3403 } |
|
3404 |
|
3405 static int |
|
3406 dsl_dataset_user_hold_check(void *arg1, void *arg2, dmu_tx_t *tx) |
|
3407 { |
|
3408 dsl_dataset_t *ds = arg1; |
|
3409 char *htag = arg2; |
|
3410 objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; |
|
3411 int error = 0; |
|
3412 |
|
3413 if (spa_version(ds->ds_dir->dd_pool->dp_spa) < SPA_VERSION_USERREFS) |
|
3414 return (ENOTSUP); |
|
3415 |
|
3416 if (!dsl_dataset_is_snapshot(ds)) |
|
3417 return (EINVAL); |
|
3418 |
|
3419 if (strlen(htag) >= ZAP_MAXNAMELEN) |
|
3420 return (ENAMETOOLONG); |
|
3421 |
|
3422 /* tags must be unique */ |
|
3423 mutex_enter(&ds->ds_lock); |
|
3424 if (ds->ds_phys->ds_userrefs_obj) { |
|
3425 error = zap_lookup(mos, ds->ds_phys->ds_userrefs_obj, htag, |
|
3426 8, 1, tx); |
|
3427 if (error == 0) |
|
3428 error = EEXIST; |
|
3429 else if (error == ENOENT) |
|
3430 error = 0; |
|
3431 } |
|
3432 mutex_exit(&ds->ds_lock); |
|
3433 |
|
3434 return (error); |
|
3435 } |
|
3436 |
|
3437 static void |
|
3438 dsl_dataset_user_hold_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx) |
|
3439 { |
|
3440 dsl_dataset_t *ds = arg1; |
|
3441 char *htag = arg2; |
|
3442 objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; |
|
3443 time_t now = gethrestime_sec(); |
|
3444 uint64_t zapobj; |
|
3445 |
|
3446 mutex_enter(&ds->ds_lock); |
|
3447 if (ds->ds_phys->ds_userrefs_obj == 0) { |
|
3448 /* |
|
3449 * This is the first user hold for this dataset. Create |
|
3450 * the userrefs zap object. |
|
3451 */ |
|
3452 dmu_buf_will_dirty(ds->ds_dbuf, tx); |
|
3453 zapobj = ds->ds_phys->ds_userrefs_obj = |
|
3454 zap_create(mos, DMU_OT_USERREFS, DMU_OT_NONE, 0, tx); |
|
3455 } else { |
|
3456 zapobj = ds->ds_phys->ds_userrefs_obj; |
|
3457 } |
|
3458 ds->ds_userrefs++; |
|
3459 mutex_exit(&ds->ds_lock); |
|
3460 |
|
3461 VERIFY(0 == zap_add(mos, zapobj, htag, 8, 1, &now, tx)); |
|
3462 |
|
3463 spa_history_internal_log(LOG_DS_USER_HOLD, |
|
3464 ds->ds_dir->dd_pool->dp_spa, tx, cr, "<%s> dataset = %llu", |
|
3465 htag, ds->ds_object); |
|
3466 } |
|
3467 |
|
3468 struct dsl_ds_holdarg { |
|
3469 dsl_sync_task_group_t *dstg; |
|
3470 char *htag; |
|
3471 char *snapname; |
|
3472 boolean_t recursive; |
|
3473 char failed[MAXPATHLEN]; |
|
3474 }; |
|
3475 |
|
3476 static int |
|
3477 dsl_dataset_user_hold_one(char *dsname, void *arg) |
|
3478 { |
|
3479 struct dsl_ds_holdarg *ha = arg; |
|
3480 dsl_dataset_t *ds; |
|
3481 int error; |
|
3482 char *name; |
|
3483 size_t buflen; |
|
3484 |
|
3485 /* alloc a buffer to hold dsname@snapname plus terminating NULL */ |
|
3486 buflen = strlen(dsname) + strlen(ha->snapname) + 2; |
|
3487 name = kmem_alloc(buflen, KM_SLEEP); |
|
3488 (void) snprintf(name, buflen, "%s@%s", dsname, ha->snapname); |
|
3489 error = dsl_dataset_hold(name, ha->dstg, &ds); |
|
3490 kmem_free(name, buflen); |
|
3491 if (error == 0) { |
|
3492 dsl_sync_task_create(ha->dstg, dsl_dataset_user_hold_check, |
|
3493 dsl_dataset_user_hold_sync, ds, ha->htag, 0); |
|
3494 } else if (error == ENOENT && ha->recursive) { |
|
3495 error = 0; |
|
3496 } else { |
|
3497 (void) strcpy(ha->failed, dsname); |
|
3498 } |
|
3499 return (error); |
|
3500 } |
|
3501 |
|
3502 int |
|
3503 dsl_dataset_user_hold(char *dsname, char *snapname, char *htag, |
|
3504 boolean_t recursive) |
|
3505 { |
|
3506 struct dsl_ds_holdarg *ha; |
|
3507 dsl_sync_task_t *dst; |
|
3508 spa_t *spa; |
|
3509 int error; |
|
3510 |
|
3511 ha = kmem_zalloc(sizeof (struct dsl_ds_holdarg), KM_SLEEP); |
|
3512 |
|
3513 (void) strlcpy(ha->failed, dsname, sizeof (ha->failed)); |
|
3514 |
|
3515 error = spa_open(dsname, &spa, FTAG); |
|
3516 if (error) { |
|
3517 kmem_free(ha, sizeof (struct dsl_ds_holdarg)); |
|
3518 return (error); |
|
3519 } |
|
3520 |
|
3521 ha->dstg = dsl_sync_task_group_create(spa_get_dsl(spa)); |
|
3522 ha->htag = htag; |
|
3523 ha->snapname = snapname; |
|
3524 ha->recursive = recursive; |
|
3525 if (recursive) { |
|
3526 error = dmu_objset_find(dsname, dsl_dataset_user_hold_one, |
|
3527 ha, DS_FIND_CHILDREN); |
|
3528 } else { |
|
3529 error = dsl_dataset_user_hold_one(dsname, ha); |
|
3530 } |
|
3531 if (error == 0) |
|
3532 error = dsl_sync_task_group_wait(ha->dstg); |
|
3533 |
|
3534 for (dst = list_head(&ha->dstg->dstg_tasks); dst; |
|
3535 dst = list_next(&ha->dstg->dstg_tasks, dst)) { |
|
3536 dsl_dataset_t *ds = dst->dst_arg1; |
|
3537 |
|
3538 if (dst->dst_err) { |
|
3539 dsl_dataset_name(ds, ha->failed); |
|
3540 *strchr(ha->failed, '@') = '\0'; |
|
3541 } |
|
3542 dsl_dataset_rele(ds, ha->dstg); |
|
3543 } |
|
3544 |
|
3545 if (error) |
|
3546 (void) strcpy(dsname, ha->failed); |
|
3547 |
|
3548 dsl_sync_task_group_destroy(ha->dstg); |
|
3549 kmem_free(ha, sizeof (struct dsl_ds_holdarg)); |
|
3550 spa_close(spa, FTAG); |
|
3551 return (error); |
|
3552 } |
|
3553 |
|
3554 struct dsl_ds_releasearg { |
|
3555 dsl_dataset_t *ds; |
|
3556 const char *htag; |
|
3557 boolean_t own; /* do we own or just hold ds? */ |
|
3558 }; |
|
3559 |
|
3560 static int |
|
3561 dsl_dataset_release_might_destroy(dsl_dataset_t *ds, const char *htag, |
|
3562 boolean_t *might_destroy) |
|
3563 { |
|
3564 objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; |
|
3565 uint64_t zapobj; |
|
3566 uint64_t tmp; |
|
3567 int error; |
|
3568 |
|
3569 *might_destroy = B_FALSE; |
|
3570 |
|
3571 mutex_enter(&ds->ds_lock); |
|
3572 zapobj = ds->ds_phys->ds_userrefs_obj; |
|
3573 if (zapobj == 0) { |
|
3574 /* The tag can't possibly exist */ |
|
3575 mutex_exit(&ds->ds_lock); |
|
3576 return (ESRCH); |
|
3577 } |
|
3578 |
|
3579 /* Make sure the tag exists */ |
|
3580 error = zap_lookup(mos, zapobj, htag, 8, 1, &tmp); |
|
3581 if (error) { |
|
3582 mutex_exit(&ds->ds_lock); |
|
3583 if (error == ENOENT) |
|
3584 error = ESRCH; |
|
3585 return (error); |
|
3586 } |
|
3587 |
|
3588 if (ds->ds_userrefs == 1 && ds->ds_phys->ds_num_children == 1 && |
|
3589 DS_IS_DEFER_DESTROY(ds)) |
|
3590 *might_destroy = B_TRUE; |
|
3591 |
|
3592 mutex_exit(&ds->ds_lock); |
|
3593 return (0); |
|
3594 } |
|
3595 |
|
3596 static int |
|
3597 dsl_dataset_user_release_check(void *arg1, void *tag, dmu_tx_t *tx) |
|
3598 { |
|
3599 struct dsl_ds_releasearg *ra = arg1; |
|
3600 dsl_dataset_t *ds = ra->ds; |
|
3601 boolean_t might_destroy; |
|
3602 int error; |
|
3603 |
|
3604 if (spa_version(ds->ds_dir->dd_pool->dp_spa) < SPA_VERSION_USERREFS) |
|
3605 return (ENOTSUP); |
|
3606 |
|
3607 error = dsl_dataset_release_might_destroy(ds, ra->htag, &might_destroy); |
|
3608 if (error) |
|
3609 return (error); |
|
3610 |
|
3611 if (might_destroy) { |
|
3612 struct dsl_ds_destroyarg dsda = {0}; |
|
3613 |
|
3614 if (dmu_tx_is_syncing(tx)) { |
|
3615 /* |
|
3616 * If we're not prepared to remove the snapshot, |
|
3617 * we can't allow the release to happen right now. |
|
3618 */ |
|
3619 if (!ra->own) |
|
3620 return (EBUSY); |
|
3621 if (ds->ds_user_ptr) { |
|
3622 ds->ds_user_evict_func(ds, ds->ds_user_ptr); |
|
3623 ds->ds_user_ptr = NULL; |
|
3624 } |
|
3625 } |
|
3626 dsda.ds = ds; |
|
3627 dsda.releasing = B_TRUE; |
|
3628 return (dsl_dataset_destroy_check(&dsda, tag, tx)); |
|
3629 } |
|
3630 |
|
3631 return (0); |
|
3632 } |
|
3633 |
|
3634 static void |
|
3635 dsl_dataset_user_release_sync(void *arg1, void *tag, cred_t *cr, dmu_tx_t *tx) |
|
3636 { |
|
3637 struct dsl_ds_releasearg *ra = arg1; |
|
3638 dsl_dataset_t *ds = ra->ds; |
|
3639 spa_t *spa = ds->ds_dir->dd_pool->dp_spa; |
|
3640 objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; |
|
3641 uint64_t zapobj; |
|
3642 uint64_t dsobj = ds->ds_object; |
|
3643 uint64_t refs; |
|
3644 |
|
3645 mutex_enter(&ds->ds_lock); |
|
3646 ds->ds_userrefs--; |
|
3647 refs = ds->ds_userrefs; |
|
3648 mutex_exit(&ds->ds_lock); |
|
3649 zapobj = ds->ds_phys->ds_userrefs_obj; |
|
3650 VERIFY(0 == zap_remove(mos, zapobj, ra->htag, tx)); |
|
3651 if (ds->ds_userrefs == 0 && ds->ds_phys->ds_num_children == 1 && |
|
3652 DS_IS_DEFER_DESTROY(ds)) { |
|
3653 struct dsl_ds_destroyarg dsda = {0}; |
|
3654 |
|
3655 ASSERT(ra->own); |
|
3656 dsda.ds = ds; |
|
3657 dsda.releasing = B_TRUE; |
|
3658 /* We already did the destroy_check */ |
|
3659 dsl_dataset_destroy_sync(&dsda, tag, cr, tx); |
|
3660 } |
|
3661 |
|
3662 spa_history_internal_log(LOG_DS_USER_RELEASE, |
|
3663 spa, tx, cr, "<%s> %lld dataset = %llu", |
|
3664 ra->htag, (longlong_t)refs, dsobj); |
|
3665 } |
|
3666 |
|
3667 static int |
|
3668 dsl_dataset_user_release_one(char *dsname, void *arg) |
|
3669 { |
|
3670 struct dsl_ds_holdarg *ha = arg; |
|
3671 struct dsl_ds_releasearg *ra; |
|
3672 dsl_dataset_t *ds; |
|
3673 int error; |
|
3674 void *dtag = ha->dstg; |
|
3675 char *name; |
|
3676 size_t buflen; |
|
3677 boolean_t own = B_FALSE; |
|
3678 boolean_t might_destroy; |
|
3679 |
|
3680 if (strlen(ha->htag) >= ZAP_MAXNAMELEN) |
|
3681 return (ENAMETOOLONG); |
|
3682 |
|
3683 /* alloc a buffer to hold dsname@snapname, plus the terminating NULL */ |
|
3684 buflen = strlen(dsname) + strlen(ha->snapname) + 2; |
|
3685 name = kmem_alloc(buflen, KM_SLEEP); |
|
3686 (void) snprintf(name, buflen, "%s@%s", dsname, ha->snapname); |
|
3687 error = dsl_dataset_hold(name, dtag, &ds); |
|
3688 kmem_free(name, buflen); |
|
3689 if (error == ENOENT && ha->recursive) |
|
3690 return (0); |
|
3691 (void) strcpy(ha->failed, dsname); |
|
3692 if (error) |
|
3693 return (error); |
|
3694 |
|
3695 ASSERT(dsl_dataset_is_snapshot(ds)); |
|
3696 |
|
3697 error = dsl_dataset_release_might_destroy(ds, ha->htag, &might_destroy); |
|
3698 if (error) { |
|
3699 dsl_dataset_rele(ds, dtag); |
|
3700 return (error); |
|
3701 } |
|
3702 |
|
3703 if (might_destroy) { |
|
3704 #ifdef _KERNEL |
|
3705 error = zfs_unmount_snap(name, NULL); |
|
3706 if (error) { |
|
3707 dsl_dataset_rele(ds, dtag); |
|
3708 return (error); |
|
3709 } |
|
3710 error = dsl_dataset_zvol_cleanup(ds, name); |
|
3711 if (error) { |
|
3712 dsl_dataset_rele(ds, dtag); |
|
3713 return (error); |
|
3714 } |
|
3715 #endif |
|
3716 if (!dsl_dataset_tryown(ds, |
|
3717 DS_MODE_READONLY | DS_MODE_INCONSISTENT, dtag)) { |
|
3718 dsl_dataset_rele(ds, dtag); |
|
3719 return (EBUSY); |
|
3720 } else { |
|
3721 own = B_TRUE; |
|
3722 dsl_dataset_make_exclusive(ds, dtag); |
|
3723 } |
|
3724 } |
|
3725 |
|
3726 ra = kmem_alloc(sizeof (struct dsl_ds_releasearg), KM_SLEEP); |
|
3727 ra->ds = ds; |
|
3728 ra->htag = ha->htag; |
|
3729 ra->own = own; |
|
3730 dsl_sync_task_create(ha->dstg, dsl_dataset_user_release_check, |
|
3731 dsl_dataset_user_release_sync, ra, dtag, 0); |
|
3732 |
|
3733 return (0); |
|
3734 } |
|
3735 |
|
3736 int |
|
3737 dsl_dataset_user_release(char *dsname, char *snapname, char *htag, |
|
3738 boolean_t recursive) |
|
3739 { |
|
3740 struct dsl_ds_holdarg *ha; |
|
3741 dsl_sync_task_t *dst; |
|
3742 spa_t *spa; |
|
3743 int error; |
|
3744 |
|
3745 ha = kmem_zalloc(sizeof (struct dsl_ds_holdarg), KM_SLEEP); |
|
3746 |
|
3747 (void) strlcpy(ha->failed, dsname, sizeof (ha->failed)); |
|
3748 |
|
3749 error = spa_open(dsname, &spa, FTAG); |
|
3750 if (error) { |
|
3751 kmem_free(ha, sizeof (struct dsl_ds_holdarg)); |
|
3752 return (error); |
|
3753 } |
|
3754 |
|
3755 ha->dstg = dsl_sync_task_group_create(spa_get_dsl(spa)); |
|
3756 ha->htag = htag; |
|
3757 ha->snapname = snapname; |
|
3758 ha->recursive = recursive; |
|
3759 if (recursive) { |
|
3760 error = dmu_objset_find(dsname, dsl_dataset_user_release_one, |
|
3761 ha, DS_FIND_CHILDREN); |
|
3762 } else { |
|
3763 error = dsl_dataset_user_release_one(dsname, ha); |
|
3764 } |
|
3765 if (error == 0) |
|
3766 error = dsl_sync_task_group_wait(ha->dstg); |
|
3767 |
|
3768 for (dst = list_head(&ha->dstg->dstg_tasks); dst; |
|
3769 dst = list_next(&ha->dstg->dstg_tasks, dst)) { |
|
3770 struct dsl_ds_releasearg *ra = dst->dst_arg1; |
|
3771 dsl_dataset_t *ds = ra->ds; |
|
3772 |
|
3773 if (dst->dst_err) |
|
3774 dsl_dataset_name(ds, ha->failed); |
|
3775 |
|
3776 if (ra->own) |
|
3777 dsl_dataset_disown(ds, ha->dstg); |
|
3778 else |
|
3779 dsl_dataset_rele(ds, ha->dstg); |
|
3780 |
|
3781 kmem_free(ra, sizeof (struct dsl_ds_releasearg)); |
|
3782 } |
|
3783 |
|
3784 if (error) |
|
3785 (void) strcpy(dsname, ha->failed); |
|
3786 |
|
3787 dsl_sync_task_group_destroy(ha->dstg); |
|
3788 kmem_free(ha, sizeof (struct dsl_ds_holdarg)); |
|
3789 spa_close(spa, FTAG); |
|
3790 return (error); |
|
3791 } |
|
3792 |
|
3793 int |
|
3794 dsl_dataset_get_holds(const char *dsname, nvlist_t **nvp) |
|
3795 { |
|
3796 dsl_dataset_t *ds; |
|
3797 int err; |
|
3798 |
|
3799 err = dsl_dataset_hold(dsname, FTAG, &ds); |
|
3800 if (err) |
|
3801 return (err); |
|
3802 |
|
3803 VERIFY(0 == nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP)); |
|
3804 if (ds->ds_phys->ds_userrefs_obj != 0) { |
|
3805 zap_attribute_t *za; |
|
3806 zap_cursor_t zc; |
|
3807 |
|
3808 za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP); |
|
3809 for (zap_cursor_init(&zc, ds->ds_dir->dd_pool->dp_meta_objset, |
|
3810 ds->ds_phys->ds_userrefs_obj); |
|
3811 zap_cursor_retrieve(&zc, za) == 0; |
|
3812 zap_cursor_advance(&zc)) { |
|
3813 VERIFY(0 == nvlist_add_uint64(*nvp, za->za_name, |
|
3814 za->za_first_integer)); |
|
3815 } |
|
3816 zap_cursor_fini(&zc); |
|
3817 kmem_free(za, sizeof (zap_attribute_t)); |
|
3818 } |
|
3819 dsl_dataset_rele(ds, FTAG); |
|
3820 return (0); |
|
3821 } |