Skip to content

Commit

Permalink
3744 zfs shouldn't ignore errors unmounting snapshots
Browse files Browse the repository at this point in the history
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Approved by: Christopher Siden <christopher.siden@delphix.com>
Ported-by: Richard Yao <ryao@gentoo.org>

Porting notes:

zfs_unmount_snap() significantly differs from Illumos, so the changes to this
function had to be done by hand. In addition, the single line addition in
openzfs#908 (openzfs/zfs@4ca9a43)
added a fairly big merge conflict. It was moved into zfs_unmount_snap(), which
is a more natural place for it.
  • Loading branch information
wca authored and ryao committed Oct 7, 2013
1 parent 248d1ab commit f7e3731
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 21 deletions.
2 changes: 1 addition & 1 deletion include/sys/zfs_ioctl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion module/zfs/dsl_userhold.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
62 changes: 43 additions & 19 deletions module/zfs/zfs_ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}

/*
Expand All @@ -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);
}
Expand All @@ -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;
Expand All @@ -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));
Expand All @@ -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);
Expand Down Expand Up @@ -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));
}

/*
Expand Down Expand Up @@ -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));
}
Expand Down

0 comments on commit f7e3731

Please sign in to comment.