diff --git a/include/sys/zfs_ioctl.h b/include/sys/zfs_ioctl.h index 8838322a9a51..74890fad7f54 100644 --- a/include/sys/zfs_ioctl.h +++ b/include/sys/zfs_ioctl.h @@ -351,7 +351,7 @@ extern int zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr); extern int zfs_secpolicy_rename_perms(const char *from, const char *to, cred_t *cr); extern int zfs_secpolicy_destroy_perms(const char *name, cred_t *cr); -extern void zfs_unmount_snap(const char *); +extern int zfs_unmount_snap(const char *); extern void zfs_destroy_unmount_origin(const char *); enum zfsdev_state_type { diff --git a/module/zfs/dsl_userhold.c b/module/zfs/dsl_userhold.c index 50adfabb02a8..cda4081f33f4 100644 --- a/module/zfs/dsl_userhold.c +++ b/module/zfs/dsl_userhold.c @@ -434,7 +434,7 @@ dsl_dataset_user_release_tmp(dsl_pool_t *dp, uint64_t dsobj, const char *htag) dsl_dataset_name(ds, name); dsl_dataset_rele(ds, FTAG); dsl_pool_config_exit(dp, FTAG); - zfs_unmount_snap(name); + (void) zfs_unmount_snap(name); } else { dsl_pool_config_exit(dp, FTAG); } diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c index de9b63e488f6..78f9332dd1bb 100644 --- a/module/zfs/zfs_ioctl.c +++ b/module/zfs/zfs_ioctl.c @@ -3348,40 +3348,55 @@ zfs_ioc_log_history(const char *unused, nvlist_t *innvl, nvlist_t *outnvl) * * This function is best-effort. Callers must deal gracefully if it * remains mounted (or is remounted after this call). + * + * Returns 0 if the argument is not a snapshot, or it is not currently a + * filesystem, or we were able to unmount it. Returns error code otherwise. */ -void +int zfs_unmount_snap(const char *snapname) { zfs_sb_t *zsb = NULL; char *dsname; char *fullname; char *ptr; + int err = 0; if ((ptr = strchr(snapname, '@')) == NULL) - return; + return (0); dsname = strdup(snapname); dsname[ptr - snapname] = '\0'; snapname = strdup(ptr + 1); fullname = kmem_asprintf("%s@%s", dsname, snapname); - if (zfs_sb_hold(dsname, FTAG, &zsb, B_FALSE) == 0) { - ASSERT(!dsl_pool_config_held(dmu_objset_pool(zsb->z_os))); - (void) zfsctl_unmount_snapshot(zsb, fullname, MNT_FORCE); - zfs_sb_rele(zsb, FTAG); - } + err = zfs_sb_hold(dsname, FTAG, &zsb, B_FALSE); + if (err != 0) + goto out1; + + ASSERT(!dsl_pool_config_held(dmu_objset_pool(zsb->z_os))); + + err = zfsctl_unmount_snapshot(zsb, fullname, MNT_FORCE); + if (err != 0) + goto out2; + + err = zvol_remove_minor(snapname); + if (err == ENXIO) + err = 0; + +out2: + zfs_sb_rele(zsb, FTAG); +out1: strfree(dsname); strfree(fullname); - return; + return (err); } /* ARGSUSED */ static int zfs_unmount_snap_cb(const char *snapname, void *arg) { - zfs_unmount_snap(snapname); - return (0); + return (zfs_unmount_snap(snapname)); } /* @@ -3404,7 +3419,7 @@ zfs_destroy_unmount_origin(const char *fsname) char originname[MAXNAMELEN]; dsl_dataset_name(ds->ds_prev, originname); dmu_objset_rele(os, FTAG); - zfs_unmount_snap(originname); + (void) zfs_unmount_snap(originname); } else { dmu_objset_rele(os, FTAG); } @@ -3421,7 +3436,7 @@ zfs_destroy_unmount_origin(const char *fsname) static int zfs_ioc_destroy_snaps(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) { - int poollen; + int error, poollen; nvlist_t *snaps; nvpair_t *pair; boolean_t defer; @@ -3442,8 +3457,9 @@ zfs_ioc_destroy_snaps(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) (name[poollen] != '/' && name[poollen] != '@')) return (SET_ERROR(EXDEV)); - zfs_unmount_snap(name); - (void) zvol_remove_minor(name); + error = zfs_unmount_snap(name); + if (error != 0) + return (error); } return (dsl_destroy_snapshots_nvl(snaps, defer, outnvl)); @@ -3461,8 +3477,12 @@ static int zfs_ioc_destroy(zfs_cmd_t *zc) { int err; - if (strchr(zc->zc_name, '@') && zc->zc_objset_type == DMU_OST_ZFS) - zfs_unmount_snap(zc->zc_name); + + if (zc->zc_objset_type == DMU_OST_ZFS) { + err = zfs_unmount_snap(zc->zc_name); + if (err != 0) + return (err); + } if (strchr(zc->zc_name, '@')) err = dsl_destroy_snapshot(zc->zc_name, zc->zc_defer_destroy); @@ -3510,7 +3530,7 @@ recursive_unmount(const char *fsname, void *arg) fullname = kmem_asprintf("%s@%s", fsname, snapname); zfs_unmount_snap(fullname); strfree(fullname); - return (0); + return (zfs_unmount_snap(fullname)); } /* @@ -4855,14 +4875,18 @@ static int zfs_ioc_release(const char *pool, nvlist_t *holds, nvlist_t *errlist) { nvpair_t *pair; + int err; /* * The release may cause the snapshot to be destroyed; make sure it * is not mounted. */ for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; - pair = nvlist_next_nvpair(holds, pair)) - zfs_unmount_snap(nvpair_name(pair)); + pair = nvlist_next_nvpair(holds, pair)) { + err = zfs_unmount_snap(nvpair_name(pair)); + if (err != 0) + return (err); + } return (dsl_dataset_user_release(holds, errlist)); }