Print this page
4986 receiving replication stream fails if any snapshot exceeds refquota

Split Close
Expand all
Collapse all
          --- old/usr/src/lib/libzfs/common/libzfs_sendrecv.c
          +++ new/usr/src/lib/libzfs/common/libzfs_sendrecv.c
↓ open down ↓ 16 lines elided ↑ open up ↑
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  
  22   22  /*
  23   23   * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  24   24   * Copyright (c) 2011, 2015 by Delphix. All rights reserved.
  25   25   * Copyright (c) 2012, Joyent, Inc. All rights reserved.
  26   26   * Copyright (c) 2013 Steven Hartland. All rights reserved.
       27 + * Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved.
  27   28   */
  28   29  
  29   30  #include <assert.h>
  30   31  #include <ctype.h>
  31   32  #include <errno.h>
  32   33  #include <libintl.h>
  33   34  #include <stdio.h>
  34   35  #include <stdlib.h>
  35   36  #include <strings.h>
  36   37  #include <unistd.h>
↓ open down ↓ 14 lines elided ↑ open up ↑
  51   52  #include <zlib.h>
  52   53  #include <sha2.h>
  53   54  #include <sys/zio_checksum.h>
  54   55  #include <sys/ddt.h>
  55   56  
  56   57  /* in libzfs_dataset.c */
  57   58  extern void zfs_setprop_error(libzfs_handle_t *, zfs_prop_t, int, char *);
  58   59  
  59   60  static int zfs_receive_impl(libzfs_handle_t *, const char *, const char *,
  60   61      recvflags_t *, int, const char *, nvlist_t *, avl_tree_t *, char **, int,
  61      -    uint64_t *);
       62 +    uint64_t *, const char *);
  62   63  static int guid_to_name(libzfs_handle_t *, const char *,
  63   64      uint64_t, boolean_t, char *);
  64   65  
  65   66  static const zio_cksum_t zero_cksum = { 0 };
  66   67  
  67   68  typedef struct dedup_arg {
  68   69          int     inputfd;
  69   70          int     outputfd;
  70   71          libzfs_handle_t  *dedup_hdl;
  71   72  } dedup_arg_t;
↓ open down ↓ 2482 lines elided ↑ open up ↑
2554 2555  }
2555 2556  
2556 2557  static int
2557 2558  zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname,
2558 2559      recvflags_t *flags, dmu_replay_record_t *drr, zio_cksum_t *zc,
2559 2560      char **top_zfs, int cleanup_fd, uint64_t *action_handlep)
2560 2561  {
2561 2562          nvlist_t *stream_nv = NULL;
2562 2563          avl_tree_t *stream_avl = NULL;
2563 2564          char *fromsnap = NULL;
     2565 +        char *sendsnap = NULL;
2564 2566          char *cp;
2565 2567          char tofs[ZFS_MAXNAMELEN];
2566 2568          char sendfs[ZFS_MAXNAMELEN];
2567 2569          char errbuf[1024];
2568 2570          dmu_replay_record_t drre;
2569 2571          int error;
2570 2572          boolean_t anyerr = B_FALSE;
2571 2573          boolean_t softerr = B_FALSE;
2572 2574          boolean_t recursive;
2573 2575  
↓ open down ↓ 128 lines elided ↑ open up ↑
2702 2704                  }
2703 2705          }
2704 2706  
2705 2707          /*
2706 2708           * Get the fs specified by the first path in the stream (the top level
2707 2709           * specified by 'zfs send') and pass it to each invocation of
2708 2710           * zfs_receive_one().
2709 2711           */
2710 2712          (void) strlcpy(sendfs, drr->drr_u.drr_begin.drr_toname,
2711 2713              ZFS_MAXNAMELEN);
2712      -        if ((cp = strchr(sendfs, '@')) != NULL)
     2714 +        if ((cp = strchr(sendfs, '@')) != NULL) {
2713 2715                  *cp = '\0';
     2716 +                /*
     2717 +                 * Find the "sendsnap", the final snapshot in a replication
     2718 +                 * stream, so zfs_receive_one() can set filesystem properties
     2719 +                 * ONLY when receiving that final snapshot.
     2720 +                 */
     2721 +                sendsnap = (cp + 1);
     2722 +        }
2714 2723  
2715 2724          /* Finally, receive each contained stream */
2716 2725          do {
2717 2726                  /*
2718 2727                   * we should figure out if it has a recoverable
2719 2728                   * error, in which case do a recv_skip() and drive on.
2720 2729                   * Note, if we fail due to already having this guid,
2721 2730                   * zfs_receive_one() will take care of it (ie,
2722 2731                   * recv_skip() and return 0).
2723 2732                   */
2724 2733                  error = zfs_receive_impl(hdl, destname, NULL, flags, fd,
2725 2734                      sendfs, stream_nv, stream_avl, top_zfs, cleanup_fd,
2726      -                    action_handlep);
     2735 +                    action_handlep, sendsnap);
2727 2736                  if (error == ENODATA) {
2728 2737                          error = 0;
2729 2738                          break;
2730 2739                  }
2731 2740                  anyerr |= error;
2732 2741          } while (error == 0);
2733 2742  
2734 2743          if (drr->drr_payloadlen != 0 && fromsnap != NULL) {
2735 2744                  /*
2736 2745                   * Now that we have the fs's they sent us, try the
↓ open down ↓ 143 lines elided ↑ open up ↑
2880 2889          }
2881 2890          zfs_close(zhp);
2882 2891  }
2883 2892  
2884 2893  /*
2885 2894   * Restores a backup of tosnap from the file descriptor specified by infd.
2886 2895   */
2887 2896  static int
2888 2897  zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
2889 2898      const char *originsnap, recvflags_t *flags, dmu_replay_record_t *drr,
2890      -    dmu_replay_record_t *drr_noswap, const char *sendfs, nvlist_t *stream_nv,
     2899 +    dmu_replay_record_t *drr_noswap, const char *sendfs,
2891 2900      avl_tree_t *stream_avl, char **top_zfs, int cleanup_fd,
2892      -    uint64_t *action_handlep)
     2901 +    uint64_t *action_handlep, const char *finalsnap)
2893 2902  {
2894 2903          zfs_cmd_t zc = { 0 };
2895 2904          time_t begin_time;
2896 2905          int ioctl_err, ioctl_errno, err;
2897 2906          char *cp;
2898 2907          struct drr_begin *drrb = &drr->drr_u.drr_begin;
2899 2908          char errbuf[1024];
2900 2909          char prop_errbuf[1024];
2901 2910          const char *chopprefix;
2902 2911          boolean_t newfs = B_FALSE;
2903 2912          boolean_t stream_wantsnewfs;
2904 2913          uint64_t parent_snapguid = 0;
2905 2914          prop_changelist_t *clp = NULL;
2906 2915          nvlist_t *snapprops_nvlist = NULL;
2907 2916          zprop_errflags_t prop_errflags;
2908      -        boolean_t recursive;
2909 2917  
2910 2918          begin_time = time(NULL);
2911 2919  
2912 2920          (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
2913 2921              "cannot receive"));
2914 2922  
2915      -        recursive = (nvlist_lookup_boolean(stream_nv, "not_recursive") ==
2916      -            ENOENT);
2917      -
2918 2923          if (stream_avl != NULL) {
2919 2924                  char *snapname;
2920 2925                  nvlist_t *fs = fsavl_find(stream_avl, drrb->drr_toguid,
2921 2926                      &snapname);
2922 2927                  nvlist_t *props;
2923 2928                  int ret;
     2929 +                boolean_t is_finalsnap;
2924 2930  
     2931 +                VERIFY(fs != NULL);
2925 2932                  (void) nvlist_lookup_uint64(fs, "parentfromsnap",
2926 2933                      &parent_snapguid);
2927      -                err = nvlist_lookup_nvlist(fs, "props", &props);
2928      -                if (err)
     2934 +                /*
     2935 +                 * Can safely use strcmp because at least "snapname" has been
     2936 +                 * verified.
     2937 +                 */
     2938 +                is_finalsnap = (strcmp(snapname, finalsnap) == 0);
     2939 +
     2940 +                if (is_finalsnap)
     2941 +                        err = nvlist_lookup_nvlist(fs, "props", &props);
     2942 +                if (!is_finalsnap || err)
2929 2943                          VERIFY(0 == nvlist_alloc(&props, NV_UNIQUE_NAME, 0));
2930 2944  
2931 2945                  if (flags->canmountoff) {
2932 2946                          VERIFY(0 == nvlist_add_uint64(props,
2933 2947                              zfs_prop_to_name(ZFS_PROP_CANMOUNT), 0));
2934 2948                  }
2935 2949                  ret = zcmd_write_src_nvlist(hdl, &zc, props);
2936      -                if (err)
     2950 +                if (!is_finalsnap || err)
2937 2951                          nvlist_free(props);
2938 2952  
2939 2953                  if (0 == nvlist_lookup_nvlist(fs, "snapprops", &props)) {
2940 2954                          VERIFY(0 == nvlist_lookup_nvlist(props,
2941 2955                              snapname, &snapprops_nvlist));
2942 2956                  }
2943 2957  
2944 2958                  if (ret != 0)
2945 2959                          return (-1);
2946 2960          }
↓ open down ↓ 50 lines elided ↑ open up ↑
2997 3011                  if (chopprefix == NULL)
2998 3012                          chopprefix = strchr(drrb->drr_toname, '@');
2999 3013          } else if (strchr(tosnap, '@') == NULL) {
3000 3014                  /*
3001 3015                   * If a filesystem was specified without -d or -e, we want to
3002 3016                   * tack on everything after the fs specified by 'zfs send'.
3003 3017                   */
3004 3018                  chopprefix = drrb->drr_toname + strlen(sendfs);
3005 3019          } else {
3006 3020                  /* A snapshot was specified as an exact path (no -d or -e). */
3007      -                if (recursive) {
     3021 +                if (flags->recursive) {
3008 3022                          zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3009 3023                              "cannot specify snapshot name for multi-snapshot "
3010 3024                              "stream"));
3011 3025                          return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
3012 3026                  }
3013 3027                  chopprefix = drrb->drr_toname + strlen(drrb->drr_toname);
3014 3028          }
3015 3029  
3016 3030          ASSERT(strstr(drrb->drr_toname, sendfs) == drrb->drr_toname);
3017 3031          ASSERT(chopprefix > drrb->drr_toname);
↓ open down ↓ 423 lines elided ↑ open up ↑
3441 3455                      buf1, delta, buf2);
3442 3456          }
3443 3457  
3444 3458          return (0);
3445 3459  }
3446 3460  
3447 3461  static int
3448 3462  zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap,
3449 3463      const char *originsnap, recvflags_t *flags, int infd, const char *sendfs,
3450 3464      nvlist_t *stream_nv, avl_tree_t *stream_avl, char **top_zfs, int cleanup_fd,
3451      -    uint64_t *action_handlep)
     3465 +    uint64_t *action_handlep, const char *finalsnap)
3452 3466  {
3453 3467          int err;
3454 3468          dmu_replay_record_t drr, drr_noswap;
3455 3469          struct drr_begin *drrb = &drr.drr_u.drr_begin;
3456 3470          char errbuf[1024];
3457 3471          zio_cksum_t zcksum = { 0 };
3458 3472          uint64_t featureflags;
3459 3473          int hdrtype;
3460 3474  
3461 3475          (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
↓ open down ↓ 75 lines elided ↑ open up ↑
3537 3551                          /*
3538 3552                           * We were not called from zfs_receive_package(). Get
3539 3553                           * the fs specified by 'zfs send'.
3540 3554                           */
3541 3555                          char *cp;
3542 3556                          (void) strlcpy(nonpackage_sendfs,
3543 3557                              drr.drr_u.drr_begin.drr_toname, ZFS_MAXNAMELEN);
3544 3558                          if ((cp = strchr(nonpackage_sendfs, '@')) != NULL)
3545 3559                                  *cp = '\0';
3546 3560                          sendfs = nonpackage_sendfs;
     3561 +                        VERIFY(finalsnap == NULL);
3547 3562                  }
3548      -                return (zfs_receive_one(hdl, infd, tosnap, originsnap, flags,
3549      -                    &drr, &drr_noswap, sendfs, stream_nv, stream_avl, top_zfs,
3550      -                    cleanup_fd, action_handlep));
     3563 +                flags->recursive =
     3564 +                    (nvlist_lookup_boolean(stream_nv, "not_recursive") ==
     3565 +                    ENOENT);
     3566 +                err = zfs_receive_one(hdl, infd, tosnap, originsnap, flags,
     3567 +                    &drr, &drr_noswap, sendfs, stream_avl, top_zfs,
     3568 +                    cleanup_fd, action_handlep, finalsnap);
     3569 +                /* Clear out internal-only flags. */
     3570 +                flags->recursive = B_FALSE;
     3571 +                return (err);
3551 3572          } else {
3552 3573                  assert(DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo) ==
3553 3574                      DMU_COMPOUNDSTREAM);
3554 3575                  return (zfs_receive_package(hdl, infd, tosnap, flags, &drr,
3555 3576                      &zcksum, top_zfs, cleanup_fd, action_handlep));
3556 3577          }
3557 3578  }
3558 3579  
3559 3580  /*
3560 3581   * Restores a backup of tosnap from the file descriptor specified by infd.
↓ open down ↓ 14 lines elided ↑ open up ↑
3575 3596          if (props) {
3576 3597                  err = nvlist_lookup_string(props, "origin", &originsnap);
3577 3598                  if (err && err != ENOENT)
3578 3599                          return (err);
3579 3600          }
3580 3601  
3581 3602          cleanup_fd = open(ZFS_DEV, O_RDWR|O_EXCL);
3582 3603          VERIFY(cleanup_fd >= 0);
3583 3604  
3584 3605          err = zfs_receive_impl(hdl, tosnap, originsnap, flags, infd, NULL, NULL,
3585      -            stream_avl, &top_zfs, cleanup_fd, &action_handle);
     3606 +            stream_avl, &top_zfs, cleanup_fd, &action_handle, NULL);
3586 3607  
3587 3608          VERIFY(0 == close(cleanup_fd));
3588 3609  
3589 3610          if (err == 0 && !flags->nomount && top_zfs) {
3590 3611                  zfs_handle_t *zhp;
3591 3612                  prop_changelist_t *clp;
3592 3613  
3593 3614                  zhp = zfs_open(hdl, top_zfs, ZFS_TYPE_FILESYSTEM);
3594 3615                  if (zhp != NULL) {
3595 3616                          clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT,
↓ open down ↓ 16 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX