3744 zfs shouldn't ignore errors unmounting snapshots
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Approved by: Christopher Siden <christopher.siden@delphix.com>

References:
  https://www.illumos.org/issues/3744
  illumos/illumos-gate@fc7a6e3fef

Ported-by: Richard Yao <ryao@gentoo.org>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Issue #1775

Porting notes:

1. There is no clear way to distinguish between a failure when we
   tried to unmount the snapdir of a zvol (which does not exist)
   and the failure when we try to unmount a snapdir of a dataset,
   so the changes to zfs_unmount_snap() were dropped in favor of
   an altered Linux function that unconditionally returns 0.
This commit is contained in:
Will Andrews 2013-06-11 09:13:43 -08:00 committed by Brian Behlendorf
parent 3a84951d7d
commit d09f25dc66
3 changed files with 32 additions and 15 deletions

View File

@ -356,7 +356,7 @@ extern int zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr);
extern int zfs_secpolicy_rename_perms(const char *from, extern int zfs_secpolicy_rename_perms(const char *from,
const char *to, cred_t *cr); const char *to, cred_t *cr);
extern int zfs_secpolicy_destroy_perms(const char *name, 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 *); extern void zfs_destroy_unmount_origin(const char *);
enum zfsdev_state_type { enum zfsdev_state_type {

View File

@ -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_name(ds, name);
dsl_dataset_rele(ds, FTAG); dsl_dataset_rele(ds, FTAG);
dsl_pool_config_exit(dp, FTAG); dsl_pool_config_exit(dp, FTAG);
zfs_unmount_snap(name); (void) zfs_unmount_snap(name);
} else { } else {
dsl_pool_config_exit(dp, FTAG); dsl_pool_config_exit(dp, FTAG);
} }

View File

@ -3348,8 +3348,16 @@ zfs_ioc_log_history(const char *unused, nvlist_t *innvl, nvlist_t *outnvl)
* *
* This function is best-effort. Callers must deal gracefully if it * This function is best-effort. Callers must deal gracefully if it
* remains mounted (or is remounted after this call). * remains mounted (or is remounted after this call).
*
* XXX: This function should detect a failure to unmount a snapdir of a dataset
* and return the appropriate error code when it is mounted. Its Illumos and
* FreeBSD counterparts do this. We do not do this on Linux because there is no
* clear way to access the mount information that FreeBSD and Illumos use to
* distinguish between things with mounted snapshot directories, and things
* without mounted snapshot directories, which include zvols. Returning a
* failure for the latter causes `zfs destroy` to fail on zvol snapshots.
*/ */
void int
zfs_unmount_snap(const char *snapname) zfs_unmount_snap(const char *snapname)
{ {
zfs_sb_t *zsb = NULL; zfs_sb_t *zsb = NULL;
@ -3358,7 +3366,7 @@ zfs_unmount_snap(const char *snapname)
char *ptr; char *ptr;
if ((ptr = strchr(snapname, '@')) == NULL) if ((ptr = strchr(snapname, '@')) == NULL)
return; return (0);
dsname = kmem_alloc(ptr - snapname + 1, KM_SLEEP); dsname = kmem_alloc(ptr - snapname + 1, KM_SLEEP);
strlcpy(dsname, snapname, ptr - snapname + 1); strlcpy(dsname, snapname, ptr - snapname + 1);
@ -3373,15 +3381,14 @@ zfs_unmount_snap(const char *snapname)
kmem_free(dsname, ptr - snapname + 1); kmem_free(dsname, ptr - snapname + 1);
strfree(fullname); strfree(fullname);
return; return (0);
} }
/* ARGSUSED */ /* ARGSUSED */
static int static int
zfs_unmount_snap_cb(const char *snapname, void *arg) zfs_unmount_snap_cb(const char *snapname, void *arg)
{ {
zfs_unmount_snap(snapname); return (zfs_unmount_snap(snapname));
return (0);
} }
/* /*
@ -3404,7 +3411,7 @@ zfs_destroy_unmount_origin(const char *fsname)
char originname[MAXNAMELEN]; char originname[MAXNAMELEN];
dsl_dataset_name(ds->ds_prev, originname); dsl_dataset_name(ds->ds_prev, originname);
dmu_objset_rele(os, FTAG); dmu_objset_rele(os, FTAG);
zfs_unmount_snap(originname); (void) zfs_unmount_snap(originname);
} else { } else {
dmu_objset_rele(os, FTAG); dmu_objset_rele(os, FTAG);
} }
@ -3421,7 +3428,7 @@ zfs_destroy_unmount_origin(const char *fsname)
static int static int
zfs_ioc_destroy_snaps(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) zfs_ioc_destroy_snaps(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl)
{ {
int poollen; int error, poollen;
nvlist_t *snaps; nvlist_t *snaps;
nvpair_t *pair; nvpair_t *pair;
boolean_t defer; boolean_t defer;
@ -3442,8 +3449,10 @@ zfs_ioc_destroy_snaps(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl)
(name[poollen] != '/' && name[poollen] != '@')) (name[poollen] != '/' && name[poollen] != '@'))
return (SET_ERROR(EXDEV)); return (SET_ERROR(EXDEV));
zfs_unmount_snap(name);
(void) zvol_remove_minor(name); (void) zvol_remove_minor(name);
error = zfs_unmount_snap(name);
if (error != 0)
return (error);
} }
return (dsl_destroy_snapshots_nvl(snaps, defer, outnvl)); return (dsl_destroy_snapshots_nvl(snaps, defer, outnvl));
@ -3461,8 +3470,12 @@ static int
zfs_ioc_destroy(zfs_cmd_t *zc) zfs_ioc_destroy(zfs_cmd_t *zc)
{ {
int err; 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, '@')) if (strchr(zc->zc_name, '@'))
err = dsl_destroy_snapshot(zc->zc_name, zc->zc_defer_destroy); err = dsl_destroy_snapshot(zc->zc_name, zc->zc_defer_destroy);
@ -3510,7 +3523,7 @@ recursive_unmount(const char *fsname, void *arg)
fullname = kmem_asprintf("%s@%s", fsname, snapname); fullname = kmem_asprintf("%s@%s", fsname, snapname);
zfs_unmount_snap(fullname); zfs_unmount_snap(fullname);
strfree(fullname); strfree(fullname);
return (0); return (zfs_unmount_snap(fullname));
} }
/* /*
@ -4855,14 +4868,18 @@ static int
zfs_ioc_release(const char *pool, nvlist_t *holds, nvlist_t *errlist) zfs_ioc_release(const char *pool, nvlist_t *holds, nvlist_t *errlist)
{ {
nvpair_t *pair; nvpair_t *pair;
int err;
/* /*
* The release may cause the snapshot to be destroyed; make sure it * The release may cause the snapshot to be destroyed; make sure it
* is not mounted. * is not mounted.
*/ */
for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL;
pair = nvlist_next_nvpair(holds, pair)) pair = nvlist_next_nvpair(holds, pair)) {
zfs_unmount_snap(nvpair_name(pair)); err = zfs_unmount_snap(nvpair_name(pair));
if (err != 0)
return (err);
}
return (dsl_dataset_user_release(holds, errlist)); return (dsl_dataset_user_release(holds, errlist));
} }