Print this page
3744 zfs shouldn't ignore errors unmounting snapshots
Submitted by:   Will Andrews <willa@spectralogic.com>
Reviewed by:    Matthew Ahrens <mahrens@delphix.com>

Split Close
Expand all
Collapse all
          --- old/usr/src/uts/common/fs/zfs/zfs_ioctl.c
          +++ new/usr/src/uts/common/fs/zfs/zfs_ioctl.c
↓ open down ↓ 3360 lines elided ↑ open up ↑
3361 3361          spa_close(spa, FTAG);
3362 3362          return (error);
3363 3363  }
3364 3364  
3365 3365  /*
3366 3366   * The dp_config_rwlock must not be held when calling this, because the
3367 3367   * unmount may need to write out data.
3368 3368   *
3369 3369   * This function is best-effort.  Callers must deal gracefully if it
3370 3370   * remains mounted (or is remounted after this call).
     3371 + *
     3372 + * Returns 0 if the argument is not a snapshot, or it is not currently a
     3373 + * filesystem, or we were able to unmount it.  Returns error code otherwise.
3371 3374   */
3372      -void
     3375 +int
3373 3376  zfs_unmount_snap(const char *snapname)
3374 3377  {
3375 3378          vfs_t *vfsp;
3376 3379          zfsvfs_t *zfsvfs;
     3380 +        int err;
3377 3381  
3378 3382          if (strchr(snapname, '@') == NULL)
3379      -                return;
     3383 +                return (0);
3380 3384  
3381 3385          vfsp = zfs_get_vfs(snapname);
3382 3386          if (vfsp == NULL)
3383      -                return;
     3387 +                return (0);
3384 3388  
3385 3389          zfsvfs = vfsp->vfs_data;
3386 3390          ASSERT(!dsl_pool_config_held(dmu_objset_pool(zfsvfs->z_os)));
3387 3391  
3388      -        if (vn_vfswlock(vfsp->vfs_vnodecovered) != 0) {
3389      -                VFS_RELE(vfsp);
3390      -                return;
3391      -        }
     3392 +        err = vn_vfswlock(vfsp->vfs_vnodecovered);
3392 3393          VFS_RELE(vfsp);
     3394 +        if (err != 0)
     3395 +                return (err);
3393 3396  
3394 3397          /*
3395 3398           * Always force the unmount for snapshots.
3396 3399           */
3397 3400          (void) dounmount(vfsp, MS_FORCE, kcred);
     3401 +        return (0);
3398 3402  }
3399 3403  
3400 3404  /* ARGSUSED */
3401 3405  static int
3402 3406  zfs_unmount_snap_cb(const char *snapname, void *arg)
3403 3407  {
3404      -        zfs_unmount_snap(snapname);
3405      -        return (0);
     3408 +        return (zfs_unmount_snap(snapname));
3406 3409  }
3407 3410  
3408 3411  /*
3409 3412   * When a clone is destroyed, its origin may also need to be destroyed,
3410 3413   * in which case it must be unmounted.  This routine will do that unmount
3411 3414   * if necessary.
3412 3415   */
3413 3416  void
3414 3417  zfs_destroy_unmount_origin(const char *fsname)
3415 3418  {
↓ open down ↓ 2 lines elided ↑ open up ↑
3418 3421          dsl_dataset_t *ds;
3419 3422  
3420 3423          error = dmu_objset_hold(fsname, FTAG, &os);
3421 3424          if (error != 0)
3422 3425                  return;
3423 3426          ds = dmu_objset_ds(os);
3424 3427          if (dsl_dir_is_clone(ds->ds_dir) && DS_IS_DEFER_DESTROY(ds->ds_prev)) {
3425 3428                  char originname[MAXNAMELEN];
3426 3429                  dsl_dataset_name(ds->ds_prev, originname);
3427 3430                  dmu_objset_rele(os, FTAG);
3428      -                zfs_unmount_snap(originname);
     3431 +                (void) zfs_unmount_snap(originname);
3429 3432          } else {
3430 3433                  dmu_objset_rele(os, FTAG);
3431 3434          }
3432 3435  }
3433 3436  
3434 3437  /*
3435 3438   * innvl: {
3436 3439   *     "snaps" -> { snapshot1, snapshot2 }
3437 3440   *     (optional boolean) "defer"
3438 3441   * }
3439 3442   *
3440 3443   * outnvl: snapshot -> error code (int32)
3441 3444   *
3442 3445   */
3443 3446  static int
3444 3447  zfs_ioc_destroy_snaps(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl)
3445 3448  {
3446      -        int poollen;
     3449 +        int error, poollen;
3447 3450          nvlist_t *snaps;
3448 3451          nvpair_t *pair;
3449 3452          boolean_t defer;
3450 3453  
3451 3454          if (nvlist_lookup_nvlist(innvl, "snaps", &snaps) != 0)
3452 3455                  return (SET_ERROR(EINVAL));
3453 3456          defer = nvlist_exists(innvl, "defer");
3454 3457  
3455 3458          poollen = strlen(poolname);
3456 3459          for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL;
3457 3460              pair = nvlist_next_nvpair(snaps, pair)) {
3458 3461                  const char *name = nvpair_name(pair);
3459 3462  
3460 3463                  /*
3461 3464                   * The snap must be in the specified pool.
3462 3465                   */
3463 3466                  if (strncmp(name, poolname, poollen) != 0 ||
3464 3467                      (name[poollen] != '/' && name[poollen] != '@'))
3465 3468                          return (SET_ERROR(EXDEV));
3466 3469  
3467      -                zfs_unmount_snap(name);
     3470 +                error = zfs_unmount_snap(name);
     3471 +                if (error)
     3472 +                        return (SET_ERROR(error));
3468 3473          }
3469 3474  
3470 3475          return (dsl_destroy_snapshots_nvl(snaps, defer, outnvl));
3471 3476  }
3472 3477  
3473 3478  /*
3474 3479   * inputs:
3475 3480   * zc_name              name of dataset to destroy
3476 3481   * zc_objset_type       type of objset
3477 3482   * zc_defer_destroy     mark for deferred destroy
3478 3483   *
3479 3484   * outputs:             none
3480 3485   */
3481 3486  static int
3482 3487  zfs_ioc_destroy(zfs_cmd_t *zc)
3483 3488  {
3484 3489          int err;
3485      -        if (strchr(zc->zc_name, '@') && zc->zc_objset_type == DMU_OST_ZFS)
3486      -                zfs_unmount_snap(zc->zc_name);
     3490 +
     3491 +        if (zc->zc_objset_type == DMU_OST_ZFS) {
     3492 +                err = zfs_unmount_snap(zc->zc_name);
     3493 +                if (err)
     3494 +                        return (err);
     3495 +        }
3487 3496  
3488 3497          if (strchr(zc->zc_name, '@'))
3489 3498                  err = dsl_destroy_snapshot(zc->zc_name, zc->zc_defer_destroy);
3490 3499          else
3491 3500                  err = dsl_destroy_head(zc->zc_name);
3492 3501          if (zc->zc_objset_type == DMU_OST_ZVOL && err == 0)
3493 3502                  (void) zvol_remove_minor(zc->zc_name);
3494 3503          return (err);
3495 3504  }
3496 3505  
↓ open down ↓ 25 lines elided ↑ open up ↑
3522 3531          return (error);
3523 3532  }
3524 3533  
3525 3534  static int
3526 3535  recursive_unmount(const char *fsname, void *arg)
3527 3536  {
3528 3537          const char *snapname = arg;
3529 3538          char fullname[MAXNAMELEN];
3530 3539  
3531 3540          (void) snprintf(fullname, sizeof (fullname), "%s@%s", fsname, snapname);
3532      -        zfs_unmount_snap(fullname);
3533      -        return (0);
     3541 +        return (zfs_unmount_snap(fullname));
3534 3542  }
3535 3543  
3536 3544  /*
3537 3545   * inputs:
3538 3546   * zc_name      old name of dataset
3539 3547   * zc_value     new name of dataset
3540 3548   * zc_cookie    recursive flag (only valid for snapshots)
3541 3549   *
3542 3550   * outputs:     none
3543 3551   */
↓ open down ↓ 1438 lines elided ↑ open up ↑
4982 4990   * outnvl: {
4983 4991   *     snapname -> error value (int32)
4984 4992   *     ...
4985 4993   * }
4986 4994   */
4987 4995  /* ARGSUSED */
4988 4996  static int
4989 4997  zfs_ioc_release(const char *pool, nvlist_t *holds, nvlist_t *errlist)
4990 4998  {
4991 4999          nvpair_t *pair;
     5000 +        int err;
4992 5001  
4993 5002          /*
4994 5003           * The release may cause the snapshot to be destroyed; make sure it
4995 5004           * is not mounted.
4996 5005           */
4997 5006          for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL;
4998      -            pair = nvlist_next_nvpair(holds, pair))
4999      -                zfs_unmount_snap(nvpair_name(pair));
     5007 +            pair = nvlist_next_nvpair(holds, pair)) {
     5008 +                err = zfs_unmount_snap(nvpair_name(pair));
     5009 +                if (err)
     5010 +                        return (err);
     5011 +        }
5000 5012  
5001 5013          return (dsl_dataset_user_release(holds, errlist));
5002 5014  }
5003 5015  
5004 5016  /*
5005 5017   * inputs:
5006 5018   * zc_name              name of new filesystem or snapshot
5007 5019   * zc_value             full name of old snapshot
5008 5020   *
5009 5021   * outputs:
↓ open down ↓ 917 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX