2008-11-20 20:01:55 +00:00
|
|
|
/*
|
|
|
|
* CDDL HEADER START
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the terms of the
|
|
|
|
* Common Development and Distribution License (the "License").
|
|
|
|
* You may not use this file except in compliance with the License.
|
|
|
|
*
|
|
|
|
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
|
|
|
* or http://www.opensolaris.org/os/licensing.
|
|
|
|
* See the License for the specific language governing permissions
|
|
|
|
* and limitations under the License.
|
|
|
|
*
|
|
|
|
* When distributing Covered Code, include this CDDL HEADER in each
|
|
|
|
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
|
|
|
* If applicable, add the following below this CDDL HEADER, with the
|
|
|
|
* fields enclosed by brackets "[]" replaced with your own identifying
|
|
|
|
* information: Portions Copyright [yyyy] [name of copyright owner]
|
|
|
|
*
|
|
|
|
* CDDL HEADER END
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2010-05-28 20:45:14 +00:00
|
|
|
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
2016-01-06 21:22:48 +00:00
|
|
|
* Copyright (c) 2011, 2015 by Delphix. All rights reserved.
|
2012-05-09 22:05:14 +00:00
|
|
|
* Copyright (c) 2012, Joyent, Inc. All rights reserved.
|
2013-05-25 02:06:23 +00:00
|
|
|
* Copyright (c) 2012 Pawel Jakub Dawidek <pawel@dawidek.net>.
|
2012-05-29 17:50:50 +00:00
|
|
|
* All rights reserved
|
2013-05-25 02:06:23 +00:00
|
|
|
* Copyright (c) 2013 Steven Hartland. All rights reserved.
|
2016-06-09 19:24:29 +00:00
|
|
|
* Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved.
|
2017-02-07 22:02:27 +00:00
|
|
|
* Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
|
2019-02-08 23:44:15 +00:00
|
|
|
* Copyright (c) 2018, loli10K <ezomori.nozomu@gmail.com>. All rights reserved.
|
2019-03-12 20:13:22 +00:00
|
|
|
* Copyright (c) 2019 Datto Inc.
|
2008-11-20 20:01:55 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <libintl.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <strings.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <sys/mount.h>
|
2010-08-26 18:57:29 +00:00
|
|
|
#include <sys/mntent.h>
|
|
|
|
#include <sys/mnttab.h>
|
|
|
|
#include <sys/avl.h>
|
|
|
|
#include <sys/debug.h>
|
2015-03-11 18:24:46 +00:00
|
|
|
#include <sys/stat.h>
|
2010-08-26 18:57:29 +00:00
|
|
|
#include <stddef.h>
|
2010-05-28 20:45:14 +00:00
|
|
|
#include <pthread.h>
|
|
|
|
#include <umem.h>
|
2012-05-09 22:05:14 +00:00
|
|
|
#include <time.h>
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
#include <libzfs.h>
|
2014-06-05 21:19:08 +00:00
|
|
|
#include <libzfs_core.h>
|
2018-11-05 19:22:33 +00:00
|
|
|
#include <libzutil.h>
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
#include "zfs_namecheck.h"
|
|
|
|
#include "zfs_prop.h"
|
2010-05-28 20:45:14 +00:00
|
|
|
#include "zfs_fletcher.h"
|
2008-11-20 20:01:55 +00:00
|
|
|
#include "libzfs_impl.h"
|
2016-01-06 21:22:48 +00:00
|
|
|
#include <zlib.h>
|
2010-05-28 20:45:14 +00:00
|
|
|
#include <sys/zio_checksum.h>
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
#include <sys/dsl_crypt.h>
|
2010-05-28 20:45:14 +00:00
|
|
|
#include <sys/ddt.h>
|
2011-06-09 20:41:55 +00:00
|
|
|
#include <sys/socket.h>
|
2016-06-15 22:47:05 +00:00
|
|
|
#include <sys/sha2.h>
|
2008-11-20 20:01:55 +00:00
|
|
|
|
2010-05-28 20:45:14 +00:00
|
|
|
/* in libzfs_dataset.c */
|
|
|
|
extern void zfs_setprop_error(libzfs_handle_t *, zfs_prop_t, int, char *);
|
2008-11-20 20:01:55 +00:00
|
|
|
|
2015-12-22 01:31:57 +00:00
|
|
|
static int zfs_receive_impl(libzfs_handle_t *, const char *, const char *,
|
|
|
|
recvflags_t *, int, const char *, nvlist_t *, avl_tree_t *, char **, int,
|
2017-05-09 23:21:09 +00:00
|
|
|
uint64_t *, const char *, nvlist_t *);
|
2016-01-06 21:22:48 +00:00
|
|
|
static int guid_to_name(libzfs_handle_t *, const char *,
|
|
|
|
uint64_t, boolean_t, char *);
|
2010-05-28 20:45:14 +00:00
|
|
|
|
2010-08-26 16:52:41 +00:00
|
|
|
static const zio_cksum_t zero_cksum = { { 0 } };
|
2010-05-28 20:45:14 +00:00
|
|
|
|
|
|
|
typedef struct dedup_arg {
|
|
|
|
int inputfd;
|
|
|
|
int outputfd;
|
|
|
|
libzfs_handle_t *dedup_hdl;
|
|
|
|
} dedup_arg_t;
|
|
|
|
|
2012-05-09 22:05:14 +00:00
|
|
|
typedef struct progress_arg {
|
|
|
|
zfs_handle_t *pa_zhp;
|
|
|
|
int pa_fd;
|
|
|
|
boolean_t pa_parsable;
|
|
|
|
} progress_arg_t;
|
|
|
|
|
2010-05-28 20:45:14 +00:00
|
|
|
typedef struct dataref {
|
|
|
|
uint64_t ref_guid;
|
|
|
|
uint64_t ref_object;
|
|
|
|
uint64_t ref_offset;
|
|
|
|
} dataref_t;
|
|
|
|
|
|
|
|
typedef struct dedup_entry {
|
|
|
|
struct dedup_entry *dde_next;
|
|
|
|
zio_cksum_t dde_chksum;
|
|
|
|
uint64_t dde_prop;
|
|
|
|
dataref_t dde_ref;
|
|
|
|
} dedup_entry_t;
|
|
|
|
|
|
|
|
#define MAX_DDT_PHYSMEM_PERCENT 20
|
|
|
|
#define SMALLEST_POSSIBLE_MAX_DDT_MB 128
|
|
|
|
|
|
|
|
typedef struct dedup_table {
|
|
|
|
dedup_entry_t **dedup_hash_array;
|
|
|
|
umem_cache_t *ddecache;
|
|
|
|
uint64_t max_ddt_size; /* max dedup table size in bytes */
|
|
|
|
uint64_t cur_ddt_size; /* current dedup table size in bytes */
|
|
|
|
uint64_t ddt_count;
|
|
|
|
int numhashbits;
|
|
|
|
boolean_t ddt_full;
|
|
|
|
} dedup_table_t;
|
|
|
|
|
|
|
|
static int
|
|
|
|
high_order_bit(uint64_t n)
|
|
|
|
{
|
|
|
|
int count;
|
|
|
|
|
|
|
|
for (count = 0; n != 0; count++)
|
|
|
|
n >>= 1;
|
|
|
|
return (count);
|
|
|
|
}
|
|
|
|
|
|
|
|
static size_t
|
|
|
|
ssread(void *buf, size_t len, FILE *stream)
|
|
|
|
{
|
|
|
|
size_t outlen;
|
|
|
|
|
|
|
|
if ((outlen = fread(buf, len, 1, stream)) == 0)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
return (outlen);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
ddt_hash_append(libzfs_handle_t *hdl, dedup_table_t *ddt, dedup_entry_t **ddepp,
|
|
|
|
zio_cksum_t *cs, uint64_t prop, dataref_t *dr)
|
|
|
|
{
|
|
|
|
dedup_entry_t *dde;
|
|
|
|
|
|
|
|
if (ddt->cur_ddt_size >= ddt->max_ddt_size) {
|
|
|
|
if (ddt->ddt_full == B_FALSE) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"Dedup table full. Deduplication will continue "
|
|
|
|
"with existing table entries"));
|
|
|
|
ddt->ddt_full = B_TRUE;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((dde = umem_cache_alloc(ddt->ddecache, UMEM_DEFAULT))
|
|
|
|
!= NULL) {
|
|
|
|
assert(*ddepp == NULL);
|
|
|
|
dde->dde_next = NULL;
|
|
|
|
dde->dde_chksum = *cs;
|
|
|
|
dde->dde_prop = prop;
|
|
|
|
dde->dde_ref = *dr;
|
|
|
|
*ddepp = dde;
|
|
|
|
ddt->cur_ddt_size += sizeof (dedup_entry_t);
|
|
|
|
ddt->ddt_count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Using the specified dedup table, do a lookup for an entry with
|
|
|
|
* the checksum cs. If found, return the block's reference info
|
|
|
|
* in *dr. Otherwise, insert a new entry in the dedup table, using
|
|
|
|
* the reference information specified by *dr.
|
|
|
|
*
|
|
|
|
* return value: true - entry was found
|
|
|
|
* false - entry was not found
|
|
|
|
*/
|
|
|
|
static boolean_t
|
|
|
|
ddt_update(libzfs_handle_t *hdl, dedup_table_t *ddt, zio_cksum_t *cs,
|
|
|
|
uint64_t prop, dataref_t *dr)
|
|
|
|
{
|
|
|
|
uint32_t hashcode;
|
|
|
|
dedup_entry_t **ddepp;
|
|
|
|
|
|
|
|
hashcode = BF64_GET(cs->zc_word[0], 0, ddt->numhashbits);
|
|
|
|
|
|
|
|
for (ddepp = &(ddt->dedup_hash_array[hashcode]); *ddepp != NULL;
|
|
|
|
ddepp = &((*ddepp)->dde_next)) {
|
|
|
|
if (ZIO_CHECKSUM_EQUAL(((*ddepp)->dde_chksum), *cs) &&
|
|
|
|
(*ddepp)->dde_prop == prop) {
|
|
|
|
*dr = (*ddepp)->dde_ref;
|
|
|
|
return (B_TRUE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ddt_hash_append(hdl, ddt, ddepp, cs, prop, dr);
|
|
|
|
return (B_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2015-07-06 03:20:31 +00:00
|
|
|
dump_record(dmu_replay_record_t *drr, void *payload, int payload_len,
|
|
|
|
zio_cksum_t *zc, int outfd)
|
2010-05-28 20:45:14 +00:00
|
|
|
{
|
2015-07-06 03:20:31 +00:00
|
|
|
ASSERT3U(offsetof(dmu_replay_record_t, drr_u.drr_checksum.drr_checksum),
|
|
|
|
==, sizeof (dmu_replay_record_t) - sizeof (zio_cksum_t));
|
|
|
|
fletcher_4_incremental_native(drr,
|
|
|
|
offsetof(dmu_replay_record_t, drr_u.drr_checksum.drr_checksum), zc);
|
|
|
|
if (drr->drr_type != DRR_BEGIN) {
|
|
|
|
ASSERT(ZIO_CHECKSUM_IS_ZERO(&drr->drr_u.
|
|
|
|
drr_checksum.drr_checksum));
|
|
|
|
drr->drr_u.drr_checksum.drr_checksum = *zc;
|
|
|
|
}
|
|
|
|
fletcher_4_incremental_native(&drr->drr_u.drr_checksum.drr_checksum,
|
|
|
|
sizeof (zio_cksum_t), zc);
|
|
|
|
if (write(outfd, drr, sizeof (*drr)) == -1)
|
|
|
|
return (errno);
|
|
|
|
if (payload_len != 0) {
|
|
|
|
fletcher_4_incremental_native(payload, payload_len, zc);
|
|
|
|
if (write(outfd, payload, payload_len) == -1)
|
|
|
|
return (errno);
|
|
|
|
}
|
|
|
|
return (0);
|
2010-05-28 20:45:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This function is started in a separate thread when the dedup option
|
|
|
|
* has been requested. The main send thread determines the list of
|
|
|
|
* snapshots to be included in the send stream and makes the ioctl calls
|
|
|
|
* for each one. But instead of having the ioctl send the output to the
|
|
|
|
* the output fd specified by the caller of zfs_send()), the
|
|
|
|
* ioctl is told to direct the output to a pipe, which is read by the
|
|
|
|
* alternate thread running THIS function. This function does the
|
|
|
|
* dedup'ing by:
|
|
|
|
* 1. building a dedup table (the DDT)
|
|
|
|
* 2. doing checksums on each data block and inserting a record in the DDT
|
|
|
|
* 3. looking for matching checksums, and
|
|
|
|
* 4. sending a DRR_WRITE_BYREF record instead of a write record whenever
|
|
|
|
* a duplicate block is found.
|
|
|
|
* The output of this function then goes to the output fd requested
|
|
|
|
* by the caller of zfs_send().
|
|
|
|
*/
|
|
|
|
static void *
|
|
|
|
cksummer(void *arg)
|
|
|
|
{
|
|
|
|
dedup_arg_t *dda = arg;
|
2014-11-03 20:15:08 +00:00
|
|
|
char *buf = zfs_alloc(dda->dedup_hdl, SPA_MAXBLOCKSIZE);
|
2016-01-06 21:22:48 +00:00
|
|
|
dmu_replay_record_t thedrr = { 0 };
|
2010-05-28 20:45:14 +00:00
|
|
|
dmu_replay_record_t *drr = &thedrr;
|
|
|
|
FILE *ofp;
|
|
|
|
int outfd;
|
|
|
|
dedup_table_t ddt;
|
|
|
|
zio_cksum_t stream_cksum;
|
|
|
|
uint64_t numbuckets;
|
|
|
|
|
2017-02-04 17:10:24 +00:00
|
|
|
#ifdef _ILP32
|
|
|
|
ddt.max_ddt_size = SMALLEST_POSSIBLE_MAX_DDT_MB << 20;
|
|
|
|
#else
|
|
|
|
uint64_t physmem = sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE);
|
2010-05-28 20:45:14 +00:00
|
|
|
ddt.max_ddt_size =
|
2015-07-06 03:20:31 +00:00
|
|
|
MAX((physmem * MAX_DDT_PHYSMEM_PERCENT) / 100,
|
|
|
|
SMALLEST_POSSIBLE_MAX_DDT_MB << 20);
|
2017-02-04 17:10:24 +00:00
|
|
|
#endif
|
2010-05-28 20:45:14 +00:00
|
|
|
|
2015-07-06 03:20:31 +00:00
|
|
|
numbuckets = ddt.max_ddt_size / (sizeof (dedup_entry_t));
|
2010-05-28 20:45:14 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* numbuckets must be a power of 2. Increase number to
|
|
|
|
* a power of 2 if necessary.
|
|
|
|
*/
|
|
|
|
if (!ISP2(numbuckets))
|
2016-11-07 22:54:32 +00:00
|
|
|
numbuckets = 1ULL << high_order_bit(numbuckets);
|
2010-05-28 20:45:14 +00:00
|
|
|
|
|
|
|
ddt.dedup_hash_array = calloc(numbuckets, sizeof (dedup_entry_t *));
|
|
|
|
ddt.ddecache = umem_cache_create("dde", sizeof (dedup_entry_t), 0,
|
|
|
|
NULL, NULL, NULL, NULL, NULL, 0);
|
|
|
|
ddt.cur_ddt_size = numbuckets * sizeof (dedup_entry_t *);
|
|
|
|
ddt.numhashbits = high_order_bit(numbuckets) - 1;
|
|
|
|
ddt.ddt_full = B_FALSE;
|
|
|
|
|
|
|
|
outfd = dda->outputfd;
|
|
|
|
ofp = fdopen(dda->inputfd, "r");
|
2015-07-06 03:20:31 +00:00
|
|
|
while (ssread(drr, sizeof (*drr), ofp) != 0) {
|
2010-05-28 20:45:14 +00:00
|
|
|
|
OpenZFS 7745 - print error if lzc_* is called before libzfs_core_init
The problem is that consumers of `libZFS_Core` that forget to call
`libzfs_core_init()` before calling any other function of the library
are having a hard time realizing their mistake. The library's internal
file descriptor is declared as global static, which is ok, but it is not
initialized explicitly; therefore, it defaults to 0, which is a valid
file descriptor. If `libzfs_core_init()`, which explicitly initializes
the correct fd, is skipped, the ioctl functions return errors that do
not have anything to do with `libZFS_Core`, where the problem is
actually located.
Even though assertions for that existed within `libZFS_Core` for debug
builds, they were never enabled because the `-DDEBUG` flag was missing
from the compiler flags.
This patch applies the following changes:
1. It adds `-DDEBUG` for debug builds of `libZFS_Core` and `libzfs`,
to enable their assertions on debug builds.
2. It corrects an assertion within `libzfs`, where a function had
been spelled incorrectly (`zpool_prop_unsupported()`) and nobody
knew because the `-DDEBUG` flag was missing, and the preprocessor
was taking that part of the code away.
3. The library's internal fd is initialized to `-1` and `VERIFY`
assertions have been placed to check that the fd is not equal to
`-1` before issuing any ioctl. It is important here to note, that
the `VERIFY` assertions exist in both debug and non-debug builds.
4. In `libzfs_core_fini` we make sure to never increment the
refcount of our fd below 0, and also reset the fd to `-1` when no
one refers to it. The reason for this, is for the rare case that
the consumer closes all references but then calls one of the
library's functions without using `libzfs_core_init()` first, and
in the mean time, a previous call to `open()` decided to reuse
our previous fd. This scenario would have passed our assertion in
non-debug builds.
5. Once the `ASSERTION` macros were enabled again, two tests from
the test suite were failing in `libzfs_sendrecv.c` at a
`ZIO_CHECKSUM_IS_ZERO` check within `dump_record()`. We now zero
the kernel filled checksums in all `dmu_replay_record`s that we
read in `cksummer()`, except the ones that are of type
`DRR_BEGIN`.
I considered making all assertions available for both debug and
non-debug builds, but I figured that it would not be appropriate if, for
example, an outside consumer of `libZFS_Core` suddenly triggers an
assertion failure because they happened to call `libzfs_core_fini()`,
even if previously the reference counter was `0`. Therefore, all the
reference counter related assertions are only enabled for debug builds,
and fd related assertions are enabled for debug and non-debug builds.
Porting notes:
- `ASSERT3S(g_refcount, >, 0);` added to `recv_impl` in
lib/libzfs_core/libzfs_core.c .
Authored by: Serapheim Dimitropoulos <serapheim@delphix.com>
Reviewed by: Pavel Zakharov <pavel.zakharov@delphix.com>
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Ported-by: George Melikov <mail@gmelikov.ru>
OpenZFS-issue: https://www.illumos.org/issues/7745
OpenZFS-commit: https://github.com/openzfs/openzfs/commit/7e3139a
Closes #5698
2017-01-31 18:48:45 +00:00
|
|
|
/*
|
|
|
|
* kernel filled in checksum, we are going to write same
|
|
|
|
* record, but need to regenerate checksum.
|
|
|
|
*/
|
|
|
|
if (drr->drr_type != DRR_BEGIN) {
|
|
|
|
bzero(&drr->drr_u.drr_checksum.drr_checksum,
|
|
|
|
sizeof (drr->drr_u.drr_checksum.drr_checksum));
|
|
|
|
}
|
|
|
|
|
2010-05-28 20:45:14 +00:00
|
|
|
switch (drr->drr_type) {
|
|
|
|
case DRR_BEGIN:
|
|
|
|
{
|
2015-07-06 03:20:31 +00:00
|
|
|
struct drr_begin *drrb = &drr->drr_u.drr_begin;
|
|
|
|
int fflags;
|
|
|
|
int sz = 0;
|
2010-05-28 20:45:14 +00:00
|
|
|
ZIO_SET_CHECKSUM(&stream_cksum, 0, 0, 0, 0);
|
|
|
|
|
2015-07-06 03:20:31 +00:00
|
|
|
ASSERT3U(drrb->drr_magic, ==, DMU_BACKUP_MAGIC);
|
|
|
|
|
2010-05-28 20:45:14 +00:00
|
|
|
/* set the DEDUP feature flag for this stream */
|
|
|
|
fflags = DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo);
|
|
|
|
fflags |= (DMU_BACKUP_FEATURE_DEDUP |
|
|
|
|
DMU_BACKUP_FEATURE_DEDUPPROPS);
|
|
|
|
DMU_SET_FEATUREFLAGS(drrb->drr_versioninfo, fflags);
|
|
|
|
|
2016-01-06 21:22:48 +00:00
|
|
|
if (drr->drr_payloadlen != 0) {
|
2015-07-06 03:20:31 +00:00
|
|
|
sz = drr->drr_payloadlen;
|
2010-05-28 20:45:14 +00:00
|
|
|
|
2014-11-03 20:15:08 +00:00
|
|
|
if (sz > SPA_MAXBLOCKSIZE) {
|
|
|
|
buf = zfs_realloc(dda->dedup_hdl, buf,
|
|
|
|
SPA_MAXBLOCKSIZE, sz);
|
2010-05-28 20:45:14 +00:00
|
|
|
}
|
|
|
|
(void) ssread(buf, sz, ofp);
|
|
|
|
if (ferror(stdin))
|
|
|
|
perror("fread");
|
|
|
|
}
|
2015-07-06 03:20:31 +00:00
|
|
|
if (dump_record(drr, buf, sz, &stream_cksum,
|
|
|
|
outfd) != 0)
|
|
|
|
goto out;
|
2010-05-28 20:45:14 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DRR_END:
|
|
|
|
{
|
2015-07-06 03:20:31 +00:00
|
|
|
struct drr_end *drre = &drr->drr_u.drr_end;
|
2010-05-28 20:45:14 +00:00
|
|
|
/* use the recalculated checksum */
|
2015-07-06 03:20:31 +00:00
|
|
|
drre->drr_checksum = stream_cksum;
|
|
|
|
if (dump_record(drr, NULL, 0, &stream_cksum,
|
|
|
|
outfd) != 0)
|
2010-05-28 20:45:14 +00:00
|
|
|
goto out;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DRR_OBJECT:
|
|
|
|
{
|
2015-07-06 03:20:31 +00:00
|
|
|
struct drr_object *drro = &drr->drr_u.drr_object;
|
2010-05-28 20:45:14 +00:00
|
|
|
if (drro->drr_bonuslen > 0) {
|
|
|
|
(void) ssread(buf,
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
DRR_OBJECT_PAYLOAD_SIZE(drro), ofp);
|
2010-05-28 20:45:14 +00:00
|
|
|
}
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
if (dump_record(drr, buf, DRR_OBJECT_PAYLOAD_SIZE(drro),
|
2015-07-06 03:20:31 +00:00
|
|
|
&stream_cksum, outfd) != 0)
|
|
|
|
goto out;
|
2010-05-28 20:45:14 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DRR_SPILL:
|
|
|
|
{
|
2015-07-06 03:20:31 +00:00
|
|
|
struct drr_spill *drrs = &drr->drr_u.drr_spill;
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
(void) ssread(buf, DRR_SPILL_PAYLOAD_SIZE(drrs), ofp);
|
|
|
|
if (dump_record(drr, buf, DRR_SPILL_PAYLOAD_SIZE(drrs),
|
2015-07-06 03:20:31 +00:00
|
|
|
&stream_cksum, outfd) != 0)
|
2010-05-28 20:45:14 +00:00
|
|
|
goto out;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DRR_FREEOBJECTS:
|
|
|
|
{
|
2015-07-06 03:20:31 +00:00
|
|
|
if (dump_record(drr, NULL, 0, &stream_cksum,
|
|
|
|
outfd) != 0)
|
2010-05-28 20:45:14 +00:00
|
|
|
goto out;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DRR_WRITE:
|
|
|
|
{
|
2015-07-06 03:20:31 +00:00
|
|
|
struct drr_write *drrw = &drr->drr_u.drr_write;
|
2010-05-28 20:45:14 +00:00
|
|
|
dataref_t dataref;
|
2016-07-11 17:45:52 +00:00
|
|
|
uint64_t payload_size;
|
2010-05-28 20:45:14 +00:00
|
|
|
|
2016-07-11 17:45:52 +00:00
|
|
|
payload_size = DRR_WRITE_PAYLOAD_SIZE(drrw);
|
|
|
|
(void) ssread(buf, payload_size, ofp);
|
2010-05-28 20:45:14 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Use the existing checksum if it's dedup-capable,
|
|
|
|
* else calculate a SHA256 checksum for it.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (ZIO_CHECKSUM_EQUAL(drrw->drr_key.ddk_cksum,
|
|
|
|
zero_cksum) ||
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
!DRR_IS_DEDUP_CAPABLE(drrw->drr_flags)) {
|
2016-07-22 15:52:49 +00:00
|
|
|
SHA2_CTX ctx;
|
2010-08-26 18:52:05 +00:00
|
|
|
zio_cksum_t tmpsha256;
|
|
|
|
|
2016-07-22 15:52:49 +00:00
|
|
|
SHA2Init(SHA256, &ctx);
|
|
|
|
SHA2Update(&ctx, buf, payload_size);
|
|
|
|
SHA2Final(&tmpsha256, &ctx);
|
2010-05-28 20:45:14 +00:00
|
|
|
|
|
|
|
drrw->drr_key.ddk_cksum.zc_word[0] =
|
|
|
|
BE_64(tmpsha256.zc_word[0]);
|
|
|
|
drrw->drr_key.ddk_cksum.zc_word[1] =
|
|
|
|
BE_64(tmpsha256.zc_word[1]);
|
|
|
|
drrw->drr_key.ddk_cksum.zc_word[2] =
|
|
|
|
BE_64(tmpsha256.zc_word[2]);
|
|
|
|
drrw->drr_key.ddk_cksum.zc_word[3] =
|
|
|
|
BE_64(tmpsha256.zc_word[3]);
|
|
|
|
drrw->drr_checksumtype = ZIO_CHECKSUM_SHA256;
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
drrw->drr_flags |= DRR_CHECKSUM_DEDUP;
|
2010-05-28 20:45:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
dataref.ref_guid = drrw->drr_toguid;
|
|
|
|
dataref.ref_object = drrw->drr_object;
|
|
|
|
dataref.ref_offset = drrw->drr_offset;
|
|
|
|
|
|
|
|
if (ddt_update(dda->dedup_hdl, &ddt,
|
|
|
|
&drrw->drr_key.ddk_cksum, drrw->drr_key.ddk_prop,
|
|
|
|
&dataref)) {
|
2015-07-06 03:20:31 +00:00
|
|
|
dmu_replay_record_t wbr_drr = {0};
|
|
|
|
struct drr_write_byref *wbr_drrr =
|
|
|
|
&wbr_drr.drr_u.drr_write_byref;
|
|
|
|
|
2010-05-28 20:45:14 +00:00
|
|
|
/* block already present in stream */
|
2015-07-06 03:20:31 +00:00
|
|
|
wbr_drr.drr_type = DRR_WRITE_BYREF;
|
|
|
|
|
2010-05-28 20:45:14 +00:00
|
|
|
wbr_drrr->drr_object = drrw->drr_object;
|
|
|
|
wbr_drrr->drr_offset = drrw->drr_offset;
|
2016-07-11 17:45:52 +00:00
|
|
|
wbr_drrr->drr_length = drrw->drr_logical_size;
|
2010-05-28 20:45:14 +00:00
|
|
|
wbr_drrr->drr_toguid = drrw->drr_toguid;
|
|
|
|
wbr_drrr->drr_refguid = dataref.ref_guid;
|
|
|
|
wbr_drrr->drr_refobject =
|
|
|
|
dataref.ref_object;
|
|
|
|
wbr_drrr->drr_refoffset =
|
|
|
|
dataref.ref_offset;
|
|
|
|
|
|
|
|
wbr_drrr->drr_checksumtype =
|
|
|
|
drrw->drr_checksumtype;
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
wbr_drrr->drr_flags = drrw->drr_flags;
|
2010-05-28 20:45:14 +00:00
|
|
|
wbr_drrr->drr_key.ddk_cksum =
|
|
|
|
drrw->drr_key.ddk_cksum;
|
|
|
|
wbr_drrr->drr_key.ddk_prop =
|
|
|
|
drrw->drr_key.ddk_prop;
|
|
|
|
|
2015-07-06 03:20:31 +00:00
|
|
|
if (dump_record(&wbr_drr, NULL, 0,
|
|
|
|
&stream_cksum, outfd) != 0)
|
2010-05-28 20:45:14 +00:00
|
|
|
goto out;
|
|
|
|
} else {
|
|
|
|
/* block not previously seen */
|
2016-07-11 17:45:52 +00:00
|
|
|
if (dump_record(drr, buf, payload_size,
|
2015-07-06 03:20:31 +00:00
|
|
|
&stream_cksum, outfd) != 0)
|
2010-05-28 20:45:14 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-06-05 21:19:08 +00:00
|
|
|
case DRR_WRITE_EMBEDDED:
|
|
|
|
{
|
2015-07-06 03:20:31 +00:00
|
|
|
struct drr_write_embedded *drrwe =
|
|
|
|
&drr->drr_u.drr_write_embedded;
|
2014-06-05 21:19:08 +00:00
|
|
|
(void) ssread(buf,
|
|
|
|
P2ROUNDUP((uint64_t)drrwe->drr_psize, 8), ofp);
|
2015-07-06 03:20:31 +00:00
|
|
|
if (dump_record(drr, buf,
|
2014-06-05 21:19:08 +00:00
|
|
|
P2ROUNDUP((uint64_t)drrwe->drr_psize, 8),
|
2015-07-06 03:20:31 +00:00
|
|
|
&stream_cksum, outfd) != 0)
|
2014-06-05 21:19:08 +00:00
|
|
|
goto out;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2010-05-28 20:45:14 +00:00
|
|
|
case DRR_FREE:
|
|
|
|
{
|
2015-07-06 03:20:31 +00:00
|
|
|
if (dump_record(drr, NULL, 0, &stream_cksum,
|
|
|
|
outfd) != 0)
|
2010-05-28 20:45:14 +00:00
|
|
|
goto out;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
case DRR_OBJECT_RANGE:
|
|
|
|
{
|
|
|
|
if (dump_record(drr, NULL, 0, &stream_cksum,
|
|
|
|
outfd) != 0)
|
|
|
|
goto out;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2010-05-28 20:45:14 +00:00
|
|
|
default:
|
2015-07-06 03:20:31 +00:00
|
|
|
(void) fprintf(stderr, "INVALID record type 0x%x\n",
|
2010-05-28 20:45:14 +00:00
|
|
|
drr->drr_type);
|
|
|
|
/* should never happen, so assert */
|
|
|
|
assert(B_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
umem_cache_destroy(ddt.ddecache);
|
|
|
|
free(ddt.dedup_hash_array);
|
|
|
|
free(buf);
|
|
|
|
(void) fclose(ofp);
|
|
|
|
|
|
|
|
return (NULL);
|
|
|
|
}
|
2008-12-03 20:09:06 +00:00
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
/*
|
|
|
|
* Routines for dealing with the AVL tree of fs-nvlists
|
|
|
|
*/
|
|
|
|
typedef struct fsavl_node {
|
|
|
|
avl_node_t fn_node;
|
|
|
|
nvlist_t *fn_nvfs;
|
|
|
|
char *fn_snapname;
|
|
|
|
uint64_t fn_guid;
|
|
|
|
} fsavl_node_t;
|
|
|
|
|
|
|
|
static int
|
|
|
|
fsavl_compare(const void *arg1, const void *arg2)
|
|
|
|
{
|
2016-08-27 18:12:53 +00:00
|
|
|
const fsavl_node_t *fn1 = (const fsavl_node_t *)arg1;
|
|
|
|
const fsavl_node_t *fn2 = (const fsavl_node_t *)arg2;
|
|
|
|
|
|
|
|
return (AVL_CMP(fn1->fn_guid, fn2->fn_guid));
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Given the GUID of a snapshot, find its containing filesystem and
|
|
|
|
* (optionally) name.
|
|
|
|
*/
|
|
|
|
static nvlist_t *
|
|
|
|
fsavl_find(avl_tree_t *avl, uint64_t snapguid, char **snapname)
|
|
|
|
{
|
|
|
|
fsavl_node_t fn_find;
|
|
|
|
fsavl_node_t *fn;
|
|
|
|
|
|
|
|
fn_find.fn_guid = snapguid;
|
|
|
|
|
|
|
|
fn = avl_find(avl, &fn_find, NULL);
|
|
|
|
if (fn) {
|
|
|
|
if (snapname)
|
|
|
|
*snapname = fn->fn_snapname;
|
|
|
|
return (fn->fn_nvfs);
|
|
|
|
}
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
fsavl_destroy(avl_tree_t *avl)
|
|
|
|
{
|
|
|
|
fsavl_node_t *fn;
|
|
|
|
void *cookie;
|
|
|
|
|
|
|
|
if (avl == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
cookie = NULL;
|
|
|
|
while ((fn = avl_destroy_nodes(avl, &cookie)) != NULL)
|
|
|
|
free(fn);
|
|
|
|
avl_destroy(avl);
|
|
|
|
free(avl);
|
|
|
|
}
|
|
|
|
|
2009-08-18 18:43:27 +00:00
|
|
|
/*
|
|
|
|
* Given an nvlist, produce an avl tree of snapshots, ordered by guid
|
|
|
|
*/
|
2008-11-20 20:01:55 +00:00
|
|
|
static avl_tree_t *
|
|
|
|
fsavl_create(nvlist_t *fss)
|
|
|
|
{
|
|
|
|
avl_tree_t *fsavl;
|
|
|
|
nvpair_t *fselem = NULL;
|
|
|
|
|
|
|
|
if ((fsavl = malloc(sizeof (avl_tree_t))) == NULL)
|
|
|
|
return (NULL);
|
|
|
|
|
|
|
|
avl_create(fsavl, fsavl_compare, sizeof (fsavl_node_t),
|
|
|
|
offsetof(fsavl_node_t, fn_node));
|
|
|
|
|
|
|
|
while ((fselem = nvlist_next_nvpair(fss, fselem)) != NULL) {
|
|
|
|
nvlist_t *nvfs, *snaps;
|
|
|
|
nvpair_t *snapelem = NULL;
|
|
|
|
|
|
|
|
VERIFY(0 == nvpair_value_nvlist(fselem, &nvfs));
|
|
|
|
VERIFY(0 == nvlist_lookup_nvlist(nvfs, "snaps", &snaps));
|
|
|
|
|
|
|
|
while ((snapelem =
|
|
|
|
nvlist_next_nvpair(snaps, snapelem)) != NULL) {
|
|
|
|
fsavl_node_t *fn;
|
|
|
|
uint64_t guid;
|
|
|
|
|
|
|
|
VERIFY(0 == nvpair_value_uint64(snapelem, &guid));
|
|
|
|
if ((fn = malloc(sizeof (fsavl_node_t))) == NULL) {
|
|
|
|
fsavl_destroy(fsavl);
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
fn->fn_nvfs = nvfs;
|
|
|
|
fn->fn_snapname = nvpair_name(snapelem);
|
|
|
|
fn->fn_guid = guid;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Note: if there are multiple snaps with the
|
|
|
|
* same GUID, we ignore all but one.
|
|
|
|
*/
|
|
|
|
if (avl_find(fsavl, fn, NULL) == NULL)
|
|
|
|
avl_add(fsavl, fn);
|
|
|
|
else
|
|
|
|
free(fn);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (fsavl);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Routines for dealing with the giant nvlist of fs-nvlists, etc.
|
|
|
|
*/
|
|
|
|
typedef struct send_data {
|
2016-09-23 00:22:37 +00:00
|
|
|
/*
|
|
|
|
* assigned inside every recursive call,
|
|
|
|
* restored from *_save on return:
|
|
|
|
*
|
|
|
|
* guid of fromsnap snapshot in parent dataset
|
|
|
|
* txg of fromsnap snapshot in current dataset
|
|
|
|
* txg of tosnap snapshot in current dataset
|
|
|
|
*/
|
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
uint64_t parent_fromsnap_guid;
|
2016-09-23 00:22:37 +00:00
|
|
|
uint64_t fromsnap_txg;
|
|
|
|
uint64_t tosnap_txg;
|
|
|
|
|
|
|
|
/* the nvlists get accumulated during depth-first traversal */
|
2008-11-20 20:01:55 +00:00
|
|
|
nvlist_t *parent_snaps;
|
|
|
|
nvlist_t *fss;
|
2008-12-03 20:09:06 +00:00
|
|
|
nvlist_t *snapprops;
|
2019-02-15 20:41:38 +00:00
|
|
|
nvlist_t *snapholds; /* user holds */
|
2016-09-23 00:22:37 +00:00
|
|
|
|
|
|
|
/* send-receive configuration, does not change during traversal */
|
|
|
|
const char *fsname;
|
2008-11-20 20:01:55 +00:00
|
|
|
const char *fromsnap;
|
|
|
|
const char *tosnap;
|
2010-05-28 20:45:14 +00:00
|
|
|
boolean_t recursive;
|
2019-03-12 20:13:22 +00:00
|
|
|
boolean_t raw;
|
|
|
|
boolean_t replicate;
|
2016-09-23 00:22:37 +00:00
|
|
|
boolean_t verbose;
|
2019-03-12 20:13:22 +00:00
|
|
|
boolean_t backup;
|
2014-05-15 08:42:19 +00:00
|
|
|
boolean_t seenfrom;
|
|
|
|
boolean_t seento;
|
2019-02-15 20:41:38 +00:00
|
|
|
boolean_t holds; /* were holds requested with send -h */
|
|
|
|
boolean_t props;
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The header nvlist is of the following format:
|
|
|
|
* {
|
|
|
|
* "tosnap" -> string
|
|
|
|
* "fromsnap" -> string (if incremental)
|
|
|
|
* "fss" -> {
|
|
|
|
* id -> {
|
|
|
|
*
|
|
|
|
* "name" -> string (full name; for debugging)
|
|
|
|
* "parentfromsnap" -> number (guid of fromsnap in parent)
|
|
|
|
*
|
|
|
|
* "props" -> { name -> value (only if set here) }
|
|
|
|
* "snaps" -> { name (lastname) -> number (guid) }
|
2008-12-03 20:09:06 +00:00
|
|
|
* "snapprops" -> { name (lastname) -> { name -> value } }
|
2019-02-15 20:41:38 +00:00
|
|
|
* "snapholds" -> { name (lastname) -> { holdname -> crtime } }
|
2008-11-20 20:01:55 +00:00
|
|
|
*
|
|
|
|
* "origin" -> number (guid) (if clone)
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
* "is_encroot" -> boolean
|
2008-11-20 20:01:55 +00:00
|
|
|
* "sent" -> boolean (not on-disk)
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
} send_data_t;
|
|
|
|
|
2018-02-21 20:32:06 +00:00
|
|
|
static void
|
|
|
|
send_iterate_prop(zfs_handle_t *zhp, boolean_t received_only, nvlist_t *nv);
|
2008-12-03 20:09:06 +00:00
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
static int
|
|
|
|
send_iterate_snap(zfs_handle_t *zhp, void *arg)
|
|
|
|
{
|
|
|
|
send_data_t *sd = arg;
|
|
|
|
uint64_t guid = zhp->zfs_dmustats.dds_guid;
|
2016-09-23 00:22:37 +00:00
|
|
|
uint64_t txg = zhp->zfs_dmustats.dds_creation_txg;
|
2008-11-20 20:01:55 +00:00
|
|
|
char *snapname;
|
2008-12-03 20:09:06 +00:00
|
|
|
nvlist_t *nv;
|
2014-11-17 04:27:58 +00:00
|
|
|
boolean_t isfromsnap, istosnap, istosnapwithnofrom;
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
snapname = strrchr(zhp->zfs_name, '@')+1;
|
2014-05-15 08:42:19 +00:00
|
|
|
isfromsnap = (sd->fromsnap != NULL &&
|
|
|
|
strcmp(sd->fromsnap, snapname) == 0);
|
|
|
|
istosnap = (sd->tosnap != NULL && (strcmp(sd->tosnap, snapname) == 0));
|
2014-11-17 04:27:58 +00:00
|
|
|
istosnapwithnofrom = (istosnap && sd->fromsnap == NULL);
|
2008-11-20 20:01:55 +00:00
|
|
|
|
2016-09-23 00:22:37 +00:00
|
|
|
if (sd->tosnap_txg != 0 && txg > sd->tosnap_txg) {
|
|
|
|
if (sd->verbose) {
|
|
|
|
(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
|
|
|
|
"skipping snapshot %s because it was created "
|
|
|
|
"after the destination snapshot (%s)\n"),
|
|
|
|
zhp->zfs_name, sd->tosnap);
|
|
|
|
}
|
|
|
|
zfs_close(zhp);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2014-11-17 04:27:58 +00:00
|
|
|
VERIFY(0 == nvlist_add_uint64(sd->parent_snaps, snapname, guid));
|
2008-11-20 20:01:55 +00:00
|
|
|
/*
|
|
|
|
* NB: if there is no fromsnap here (it's a newly created fs in
|
|
|
|
* an incremental replication), we will substitute the tosnap.
|
|
|
|
*/
|
2014-05-15 08:42:19 +00:00
|
|
|
if (isfromsnap || (sd->parent_fromsnap_guid == 0 && istosnap)) {
|
2008-11-20 20:01:55 +00:00
|
|
|
sd->parent_fromsnap_guid = guid;
|
|
|
|
}
|
|
|
|
|
2014-05-15 08:42:19 +00:00
|
|
|
if (!sd->recursive) {
|
|
|
|
if (!sd->seenfrom && isfromsnap) {
|
|
|
|
sd->seenfrom = B_TRUE;
|
|
|
|
zfs_close(zhp);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2014-11-17 04:27:58 +00:00
|
|
|
if ((sd->seento || !sd->seenfrom) && !istosnapwithnofrom) {
|
2014-05-15 08:42:19 +00:00
|
|
|
zfs_close(zhp);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (istosnap)
|
|
|
|
sd->seento = B_TRUE;
|
|
|
|
}
|
|
|
|
|
2008-12-03 20:09:06 +00:00
|
|
|
VERIFY(0 == nvlist_alloc(&nv, NV_UNIQUE_NAME, 0));
|
2018-02-21 20:32:06 +00:00
|
|
|
send_iterate_prop(zhp, sd->backup, nv);
|
2008-12-03 20:09:06 +00:00
|
|
|
VERIFY(0 == nvlist_add_nvlist(sd->snapprops, snapname, nv));
|
|
|
|
nvlist_free(nv);
|
2019-02-15 20:41:38 +00:00
|
|
|
if (sd->holds) {
|
|
|
|
nvlist_t *holds = fnvlist_alloc();
|
|
|
|
int err = lzc_get_holds(zhp->zfs_name, &holds);
|
|
|
|
if (err == 0) {
|
|
|
|
VERIFY(0 == nvlist_add_nvlist(sd->snapholds,
|
|
|
|
snapname, holds));
|
|
|
|
}
|
|
|
|
fnvlist_free(holds);
|
|
|
|
}
|
2008-12-03 20:09:06 +00:00
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
zfs_close(zhp);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2018-02-21 20:32:06 +00:00
|
|
|
send_iterate_prop(zfs_handle_t *zhp, boolean_t received_only, nvlist_t *nv)
|
2008-11-20 20:01:55 +00:00
|
|
|
{
|
2018-02-21 20:32:06 +00:00
|
|
|
nvlist_t *props = NULL;
|
2008-11-20 20:01:55 +00:00
|
|
|
nvpair_t *elem = NULL;
|
|
|
|
|
2018-02-21 20:32:06 +00:00
|
|
|
if (received_only)
|
|
|
|
props = zfs_get_recvd_props(zhp);
|
|
|
|
else
|
|
|
|
props = zhp->zfs_props;
|
|
|
|
|
|
|
|
while ((elem = nvlist_next_nvpair(props, elem)) != NULL) {
|
2008-11-20 20:01:55 +00:00
|
|
|
char *propname = nvpair_name(elem);
|
|
|
|
zfs_prop_t prop = zfs_name_to_prop(propname);
|
|
|
|
nvlist_t *propnv;
|
|
|
|
|
2010-05-28 20:45:14 +00:00
|
|
|
if (!zfs_prop_user(propname)) {
|
|
|
|
/*
|
|
|
|
* Realistically, this should never happen. However,
|
|
|
|
* we want the ability to add DSL properties without
|
|
|
|
* needing to make incompatible version changes. We
|
|
|
|
* need to ignore unknown properties to allow older
|
|
|
|
* software to still send datasets containing these
|
|
|
|
* properties, with the unknown properties elided.
|
|
|
|
*/
|
|
|
|
if (prop == ZPROP_INVAL)
|
|
|
|
continue;
|
2009-07-02 22:44:48 +00:00
|
|
|
|
2010-05-28 20:45:14 +00:00
|
|
|
if (zfs_prop_readonly(prop))
|
|
|
|
continue;
|
|
|
|
}
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
verify(nvpair_value_nvlist(elem, &propnv) == 0);
|
2009-08-18 18:43:27 +00:00
|
|
|
if (prop == ZFS_PROP_QUOTA || prop == ZFS_PROP_RESERVATION ||
|
|
|
|
prop == ZFS_PROP_REFQUOTA ||
|
|
|
|
prop == ZFS_PROP_REFRESERVATION) {
|
2010-05-28 20:45:14 +00:00
|
|
|
char *source;
|
2008-11-20 20:01:55 +00:00
|
|
|
uint64_t value;
|
|
|
|
verify(nvlist_lookup_uint64(propnv,
|
|
|
|
ZPROP_VALUE, &value) == 0);
|
2008-12-03 20:09:06 +00:00
|
|
|
if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT)
|
|
|
|
continue;
|
2010-05-28 20:45:14 +00:00
|
|
|
/*
|
|
|
|
* May have no source before SPA_VERSION_RECVD_PROPS,
|
|
|
|
* but is still modifiable.
|
|
|
|
*/
|
|
|
|
if (nvlist_lookup_string(propnv,
|
|
|
|
ZPROP_SOURCE, &source) == 0) {
|
|
|
|
if ((strcmp(source, zhp->zfs_name) != 0) &&
|
|
|
|
(strcmp(source,
|
|
|
|
ZPROP_SOURCE_VAL_RECVD) != 0))
|
|
|
|
continue;
|
|
|
|
}
|
2008-11-20 20:01:55 +00:00
|
|
|
} else {
|
|
|
|
char *source;
|
|
|
|
if (nvlist_lookup_string(propnv,
|
|
|
|
ZPROP_SOURCE, &source) != 0)
|
|
|
|
continue;
|
2010-05-28 20:45:14 +00:00
|
|
|
if ((strcmp(source, zhp->zfs_name) != 0) &&
|
|
|
|
(strcmp(source, ZPROP_SOURCE_VAL_RECVD) != 0))
|
2008-11-20 20:01:55 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (zfs_prop_user(propname) ||
|
|
|
|
zfs_prop_get_type(prop) == PROP_TYPE_STRING) {
|
|
|
|
char *value;
|
|
|
|
verify(nvlist_lookup_string(propnv,
|
|
|
|
ZPROP_VALUE, &value) == 0);
|
|
|
|
VERIFY(0 == nvlist_add_string(nv, propname, value));
|
|
|
|
} else {
|
|
|
|
uint64_t value;
|
|
|
|
verify(nvlist_lookup_uint64(propnv,
|
|
|
|
ZPROP_VALUE, &value) == 0);
|
|
|
|
VERIFY(0 == nvlist_add_uint64(nv, propname, value));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-23 00:22:37 +00:00
|
|
|
/*
|
|
|
|
* returns snapshot creation txg
|
|
|
|
* and returns 0 if the snapshot does not exist
|
|
|
|
*/
|
|
|
|
static uint64_t
|
|
|
|
get_snap_txg(libzfs_handle_t *hdl, const char *fs, const char *snap)
|
|
|
|
{
|
|
|
|
char name[ZFS_MAX_DATASET_NAME_LEN];
|
|
|
|
uint64_t txg = 0;
|
|
|
|
|
|
|
|
if (fs == NULL || fs[0] == '\0' || snap == NULL || snap[0] == '\0')
|
|
|
|
return (txg);
|
|
|
|
|
|
|
|
(void) snprintf(name, sizeof (name), "%s@%s", fs, snap);
|
|
|
|
if (zfs_dataset_exists(hdl, name, ZFS_TYPE_SNAPSHOT)) {
|
|
|
|
zfs_handle_t *zhp = zfs_open(hdl, name, ZFS_TYPE_SNAPSHOT);
|
|
|
|
if (zhp != NULL) {
|
|
|
|
txg = zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG);
|
|
|
|
zfs_close(zhp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (txg);
|
|
|
|
}
|
|
|
|
|
2009-08-18 18:43:27 +00:00
|
|
|
/*
|
|
|
|
* recursively generate nvlists describing datasets. See comment
|
|
|
|
* for the data structure send_data_t above for description of contents
|
|
|
|
* of the nvlist.
|
|
|
|
*/
|
2008-11-20 20:01:55 +00:00
|
|
|
static int
|
|
|
|
send_iterate_fs(zfs_handle_t *zhp, void *arg)
|
|
|
|
{
|
|
|
|
send_data_t *sd = arg;
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
nvlist_t *nvfs = NULL, *nv = NULL;
|
2010-05-28 20:45:14 +00:00
|
|
|
int rv = 0;
|
2019-03-12 20:13:22 +00:00
|
|
|
uint64_t min_txg = 0, max_txg = 0;
|
2008-11-20 20:01:55 +00:00
|
|
|
uint64_t parent_fromsnap_guid_save = sd->parent_fromsnap_guid;
|
2016-09-23 00:22:37 +00:00
|
|
|
uint64_t fromsnap_txg_save = sd->fromsnap_txg;
|
|
|
|
uint64_t tosnap_txg_save = sd->tosnap_txg;
|
|
|
|
uint64_t txg = zhp->zfs_dmustats.dds_creation_txg;
|
2008-11-20 20:01:55 +00:00
|
|
|
uint64_t guid = zhp->zfs_dmustats.dds_guid;
|
2016-09-23 00:22:37 +00:00
|
|
|
uint64_t fromsnap_txg, tosnap_txg;
|
2008-11-20 20:01:55 +00:00
|
|
|
char guidstring[64];
|
|
|
|
|
2016-09-23 00:22:37 +00:00
|
|
|
fromsnap_txg = get_snap_txg(zhp->zfs_hdl, zhp->zfs_name, sd->fromsnap);
|
|
|
|
if (fromsnap_txg != 0)
|
|
|
|
sd->fromsnap_txg = fromsnap_txg;
|
|
|
|
|
|
|
|
tosnap_txg = get_snap_txg(zhp->zfs_hdl, zhp->zfs_name, sd->tosnap);
|
|
|
|
if (tosnap_txg != 0)
|
|
|
|
sd->tosnap_txg = tosnap_txg;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* on the send side, if the current dataset does not have tosnap,
|
|
|
|
* perform two additional checks:
|
|
|
|
*
|
|
|
|
* - skip sending the current dataset if it was created later than
|
|
|
|
* the parent tosnap
|
|
|
|
* - return error if the current dataset was created earlier than
|
|
|
|
* the parent tosnap
|
|
|
|
*/
|
|
|
|
if (sd->tosnap != NULL && tosnap_txg == 0) {
|
|
|
|
if (sd->tosnap_txg != 0 && txg > sd->tosnap_txg) {
|
|
|
|
if (sd->verbose) {
|
|
|
|
(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
|
|
|
|
"skipping dataset %s: snapshot %s does "
|
|
|
|
"not exist\n"), zhp->zfs_name, sd->tosnap);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
|
|
|
|
"cannot send %s@%s%s: snapshot %s@%s does not "
|
|
|
|
"exist\n"), sd->fsname, sd->tosnap, sd->recursive ?
|
|
|
|
dgettext(TEXT_DOMAIN, " recursively") : "",
|
|
|
|
zhp->zfs_name, sd->tosnap);
|
|
|
|
rv = -1;
|
|
|
|
}
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2019-03-12 20:13:22 +00:00
|
|
|
nvfs = fnvlist_alloc();
|
|
|
|
fnvlist_add_string(nvfs, "name", zhp->zfs_name);
|
|
|
|
fnvlist_add_uint64(nvfs, "parentfromsnap",
|
|
|
|
sd->parent_fromsnap_guid);
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
if (zhp->zfs_dmustats.dds_origin[0]) {
|
|
|
|
zfs_handle_t *origin = zfs_open(zhp->zfs_hdl,
|
|
|
|
zhp->zfs_dmustats.dds_origin, ZFS_TYPE_SNAPSHOT);
|
2016-09-23 00:22:37 +00:00
|
|
|
if (origin == NULL) {
|
|
|
|
rv = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
2019-03-12 20:13:22 +00:00
|
|
|
fnvlist_add_uint64(nvfs, "origin",
|
|
|
|
origin->zfs_dmustats.dds_guid);
|
2017-04-19 23:36:32 +00:00
|
|
|
|
|
|
|
zfs_close(origin);
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* iterate over props */
|
2019-02-15 20:41:38 +00:00
|
|
|
if (sd->props || sd->backup || sd->recursive) {
|
2019-03-12 20:13:22 +00:00
|
|
|
nv = fnvlist_alloc();
|
2019-02-15 20:41:38 +00:00
|
|
|
send_iterate_prop(zhp, sd->backup, nv);
|
|
|
|
}
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
if (zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION) != ZIO_CRYPT_OFF) {
|
|
|
|
boolean_t encroot;
|
|
|
|
|
|
|
|
/* determine if this dataset is an encryption root */
|
|
|
|
if (zfs_crypto_get_encryption_root(zhp, &encroot, NULL) != 0) {
|
|
|
|
rv = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (encroot)
|
2019-03-12 20:13:22 +00:00
|
|
|
fnvlist_add_boolean(nvfs, "is_encroot");
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Encrypted datasets can only be sent with properties if
|
|
|
|
* the raw flag is specified because the receive side doesn't
|
|
|
|
* currently have a mechanism for recursively asking the user
|
|
|
|
* for new encryption parameters.
|
|
|
|
*/
|
|
|
|
if (!sd->raw) {
|
|
|
|
(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
|
|
|
|
"cannot send %s@%s: encrypted dataset %s may not "
|
|
|
|
"be sent with properties without the raw flag\n"),
|
|
|
|
sd->fsname, sd->tosnap, zhp->zfs_name);
|
|
|
|
rv = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-02-15 20:41:38 +00:00
|
|
|
if (nv != NULL)
|
2019-03-12 20:13:22 +00:00
|
|
|
fnvlist_add_nvlist(nvfs, "props", nv);
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
/* iterate over snaps, and set sd->parent_fromsnap_guid */
|
|
|
|
sd->parent_fromsnap_guid = 0;
|
2019-03-12 20:13:22 +00:00
|
|
|
sd->parent_snaps = fnvlist_alloc();
|
|
|
|
sd->snapprops = fnvlist_alloc();
|
|
|
|
if (!sd->replicate && fromsnap_txg != 0)
|
|
|
|
min_txg = fromsnap_txg;
|
|
|
|
if (!sd->replicate && tosnap_txg != 0)
|
|
|
|
max_txg = tosnap_txg;
|
2019-02-15 20:41:38 +00:00
|
|
|
if (sd->holds)
|
|
|
|
VERIFY(0 == nvlist_alloc(&sd->snapholds, NV_UNIQUE_NAME, 0));
|
2019-03-12 20:13:22 +00:00
|
|
|
(void) zfs_iter_snapshots_sorted(zhp, send_iterate_snap, sd,
|
|
|
|
min_txg, max_txg);
|
|
|
|
fnvlist_add_nvlist(nvfs, "snaps", sd->parent_snaps);
|
|
|
|
fnvlist_add_nvlist(nvfs, "snapprops", sd->snapprops);
|
2019-02-15 20:41:38 +00:00
|
|
|
if (sd->holds)
|
2019-03-12 20:13:22 +00:00
|
|
|
fnvlist_add_nvlist(nvfs, "snapholds", sd->snapholds);
|
|
|
|
fnvlist_free(sd->parent_snaps);
|
|
|
|
fnvlist_free(sd->snapprops);
|
|
|
|
fnvlist_free(sd->snapholds);
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
/* add this fs to nvlist */
|
|
|
|
(void) snprintf(guidstring, sizeof (guidstring),
|
|
|
|
"0x%llx", (longlong_t)guid);
|
2019-03-12 20:13:22 +00:00
|
|
|
fnvlist_add_nvlist(sd->fss, guidstring, nvfs);
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
/* iterate over children */
|
2010-05-28 20:45:14 +00:00
|
|
|
if (sd->recursive)
|
|
|
|
rv = zfs_iter_filesystems(zhp, send_iterate_fs, sd);
|
2008-11-20 20:01:55 +00:00
|
|
|
|
2016-09-23 00:22:37 +00:00
|
|
|
out:
|
2008-11-20 20:01:55 +00:00
|
|
|
sd->parent_fromsnap_guid = parent_fromsnap_guid_save;
|
2016-09-23 00:22:37 +00:00
|
|
|
sd->fromsnap_txg = fromsnap_txg_save;
|
|
|
|
sd->tosnap_txg = tosnap_txg_save;
|
2019-03-12 20:13:22 +00:00
|
|
|
fnvlist_free(nv);
|
|
|
|
fnvlist_free(nvfs);
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
zfs_close(zhp);
|
|
|
|
return (rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
gather_nvlist(libzfs_handle_t *hdl, const char *fsname, const char *fromsnap,
|
2019-03-12 20:13:22 +00:00
|
|
|
const char *tosnap, boolean_t recursive, boolean_t raw, boolean_t replicate,
|
|
|
|
boolean_t verbose, boolean_t backup, boolean_t holds, boolean_t props,
|
|
|
|
nvlist_t **nvlp,
|
2019-02-15 20:41:38 +00:00
|
|
|
avl_tree_t **avlp)
|
2008-11-20 20:01:55 +00:00
|
|
|
{
|
|
|
|
zfs_handle_t *zhp;
|
|
|
|
send_data_t sd = { 0 };
|
|
|
|
int error;
|
|
|
|
|
|
|
|
zhp = zfs_open(hdl, fsname, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
|
|
|
|
if (zhp == NULL)
|
|
|
|
return (EZFS_BADTYPE);
|
|
|
|
|
|
|
|
VERIFY(0 == nvlist_alloc(&sd.fss, NV_UNIQUE_NAME, 0));
|
2016-09-23 00:22:37 +00:00
|
|
|
sd.fsname = fsname;
|
2008-11-20 20:01:55 +00:00
|
|
|
sd.fromsnap = fromsnap;
|
|
|
|
sd.tosnap = tosnap;
|
2010-05-28 20:45:14 +00:00
|
|
|
sd.recursive = recursive;
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
sd.raw = raw;
|
2019-03-12 20:13:22 +00:00
|
|
|
sd.replicate = replicate;
|
2016-09-23 00:22:37 +00:00
|
|
|
sd.verbose = verbose;
|
2018-02-21 20:32:06 +00:00
|
|
|
sd.backup = backup;
|
2019-02-15 20:41:38 +00:00
|
|
|
sd.holds = holds;
|
|
|
|
sd.props = props;
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
if ((error = send_iterate_fs(zhp, &sd)) != 0) {
|
|
|
|
nvlist_free(sd.fss);
|
|
|
|
if (avlp != NULL)
|
|
|
|
*avlp = NULL;
|
|
|
|
*nvlp = NULL;
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (avlp != NULL && (*avlp = fsavl_create(sd.fss)) == NULL) {
|
|
|
|
nvlist_free(sd.fss);
|
|
|
|
*nvlp = NULL;
|
|
|
|
return (EZFS_NOMEM);
|
|
|
|
}
|
|
|
|
|
|
|
|
*nvlp = sd.fss;
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Routines specific to "zfs send"
|
|
|
|
*/
|
|
|
|
typedef struct send_dump_data {
|
|
|
|
/* these are all just the short snapname (the part after the @) */
|
|
|
|
const char *fromsnap;
|
|
|
|
const char *tosnap;
|
2016-06-15 21:28:36 +00:00
|
|
|
char prevsnap[ZFS_MAX_DATASET_NAME_LEN];
|
2010-08-26 21:24:34 +00:00
|
|
|
uint64_t prevsnap_obj;
|
2008-11-20 20:01:55 +00:00
|
|
|
boolean_t seenfrom, seento, replicate, doall, fromorigin;
|
2015-07-10 23:54:52 +00:00
|
|
|
boolean_t verbose, dryrun, parsable, progress, embed_data, std_out;
|
2019-02-15 20:41:38 +00:00
|
|
|
boolean_t large_block, compress, raw, holds;
|
2008-11-20 20:01:55 +00:00
|
|
|
int outfd;
|
|
|
|
boolean_t err;
|
|
|
|
nvlist_t *fss;
|
2013-05-25 02:06:23 +00:00
|
|
|
nvlist_t *snapholds;
|
2008-11-20 20:01:55 +00:00
|
|
|
avl_tree_t *fsavl;
|
2010-05-28 20:45:14 +00:00
|
|
|
snapfilter_cb_t *filter_cb;
|
|
|
|
void *filter_cb_arg;
|
|
|
|
nvlist_t *debugnv;
|
2016-06-15 21:28:36 +00:00
|
|
|
char holdtag[ZFS_MAX_DATASET_NAME_LEN];
|
2010-08-26 21:24:34 +00:00
|
|
|
int cleanup_fd;
|
2011-11-17 18:14:36 +00:00
|
|
|
uint64_t size;
|
2008-11-20 20:01:55 +00:00
|
|
|
} send_dump_data_t;
|
|
|
|
|
2011-11-17 18:14:36 +00:00
|
|
|
static int
|
2017-08-31 16:00:35 +00:00
|
|
|
zfs_send_space(zfs_handle_t *zhp, const char *snapname, const char *from,
|
|
|
|
enum lzc_send_flags flags, uint64_t *spacep)
|
2011-11-17 18:14:36 +00:00
|
|
|
{
|
|
|
|
libzfs_handle_t *hdl = zhp->zfs_hdl;
|
2017-08-31 16:00:35 +00:00
|
|
|
int error;
|
2011-11-17 18:14:36 +00:00
|
|
|
|
2017-08-31 16:00:35 +00:00
|
|
|
assert(snapname != NULL);
|
|
|
|
error = lzc_send_space(snapname, from, flags, spacep);
|
2011-11-17 18:14:36 +00:00
|
|
|
|
2017-08-31 16:00:35 +00:00
|
|
|
if (error != 0) {
|
2011-11-17 18:14:36 +00:00
|
|
|
char errbuf[1024];
|
|
|
|
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
|
2017-08-31 16:00:35 +00:00
|
|
|
"warning: cannot estimate space for '%s'"), snapname);
|
2011-11-17 18:14:36 +00:00
|
|
|
|
2017-08-31 16:00:35 +00:00
|
|
|
switch (error) {
|
2011-11-17 18:14:36 +00:00
|
|
|
case EXDEV:
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"not an earlier snapshot from the same fs"));
|
|
|
|
return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
|
|
|
|
|
|
|
|
case ENOENT:
|
2017-08-31 16:00:35 +00:00
|
|
|
if (zfs_dataset_exists(hdl, snapname,
|
2011-11-17 18:14:36 +00:00
|
|
|
ZFS_TYPE_SNAPSHOT)) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
2017-08-31 16:00:35 +00:00
|
|
|
"incremental source (%s) does not exist"),
|
|
|
|
snapname);
|
2011-11-17 18:14:36 +00:00
|
|
|
}
|
|
|
|
return (zfs_error(hdl, EZFS_NOENT, errbuf));
|
|
|
|
|
|
|
|
case EDQUOT:
|
|
|
|
case EFBIG:
|
|
|
|
case EIO:
|
|
|
|
case ENOLINK:
|
|
|
|
case ENOSPC:
|
|
|
|
case ENOSTR:
|
|
|
|
case ENXIO:
|
|
|
|
case EPIPE:
|
|
|
|
case ERANGE:
|
|
|
|
case EFAULT:
|
|
|
|
case EROFS:
|
2017-08-31 16:00:35 +00:00
|
|
|
case EINVAL:
|
|
|
|
zfs_error_aux(hdl, strerror(error));
|
2011-11-17 18:14:36 +00:00
|
|
|
return (zfs_error(hdl, EZFS_BADBACKUP, errbuf));
|
|
|
|
|
|
|
|
default:
|
2017-08-31 16:00:35 +00:00
|
|
|
return (zfs_standard_error(hdl, error, errbuf));
|
2011-11-17 18:14:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
/*
|
|
|
|
* Dumps a backup of the given snapshot (incremental from fromsnap if it's not
|
|
|
|
* NULL) to the file descriptor specified by outfd.
|
|
|
|
*/
|
|
|
|
static int
|
2010-08-26 21:24:34 +00:00
|
|
|
dump_ioctl(zfs_handle_t *zhp, const char *fromsnap, uint64_t fromsnap_obj,
|
2014-06-05 21:19:08 +00:00
|
|
|
boolean_t fromorigin, int outfd, enum lzc_send_flags flags,
|
|
|
|
nvlist_t *debugnv)
|
2008-11-20 20:01:55 +00:00
|
|
|
{
|
2013-09-04 12:00:57 +00:00
|
|
|
zfs_cmd_t zc = {"\0"};
|
2008-11-20 20:01:55 +00:00
|
|
|
libzfs_handle_t *hdl = zhp->zfs_hdl;
|
2010-05-28 20:45:14 +00:00
|
|
|
nvlist_t *thisdbg;
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
|
2010-08-26 21:24:34 +00:00
|
|
|
assert(fromsnap_obj == 0 || !fromorigin);
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
|
|
|
|
zc.zc_cookie = outfd;
|
|
|
|
zc.zc_obj = fromorigin;
|
2010-08-26 21:24:34 +00:00
|
|
|
zc.zc_sendobj = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID);
|
|
|
|
zc.zc_fromobj = fromsnap_obj;
|
2014-06-05 21:19:08 +00:00
|
|
|
zc.zc_flags = flags;
|
2010-05-28 20:45:14 +00:00
|
|
|
|
|
|
|
VERIFY(0 == nvlist_alloc(&thisdbg, NV_UNIQUE_NAME, 0));
|
|
|
|
if (fromsnap && fromsnap[0] != '\0') {
|
|
|
|
VERIFY(0 == nvlist_add_string(thisdbg,
|
|
|
|
"fromsnap", fromsnap));
|
|
|
|
}
|
|
|
|
|
2011-11-17 18:14:36 +00:00
|
|
|
if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SEND, &zc) != 0) {
|
2008-11-20 20:01:55 +00:00
|
|
|
char errbuf[1024];
|
|
|
|
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
|
|
|
|
"warning: cannot send '%s'"), zhp->zfs_name);
|
|
|
|
|
2010-05-28 20:45:14 +00:00
|
|
|
VERIFY(0 == nvlist_add_uint64(thisdbg, "error", errno));
|
|
|
|
if (debugnv) {
|
|
|
|
VERIFY(0 == nvlist_add_nvlist(debugnv,
|
|
|
|
zhp->zfs_name, thisdbg));
|
|
|
|
}
|
|
|
|
nvlist_free(thisdbg);
|
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
switch (errno) {
|
|
|
|
case EXDEV:
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"not an earlier snapshot from the same fs"));
|
|
|
|
return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
|
|
|
|
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
case EACCES:
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"source key must be loaded"));
|
|
|
|
return (zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf));
|
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
case ENOENT:
|
|
|
|
if (zfs_dataset_exists(hdl, zc.zc_name,
|
|
|
|
ZFS_TYPE_SNAPSHOT)) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"incremental source (@%s) does not exist"),
|
|
|
|
zc.zc_value);
|
|
|
|
}
|
|
|
|
return (zfs_error(hdl, EZFS_NOENT, errbuf));
|
|
|
|
|
|
|
|
case EDQUOT:
|
|
|
|
case EFBIG:
|
|
|
|
case EIO:
|
|
|
|
case ENOLINK:
|
|
|
|
case ENOSPC:
|
|
|
|
case ENOSTR:
|
|
|
|
case ENXIO:
|
|
|
|
case EPIPE:
|
|
|
|
case ERANGE:
|
|
|
|
case EFAULT:
|
|
|
|
case EROFS:
|
|
|
|
zfs_error_aux(hdl, strerror(errno));
|
|
|
|
return (zfs_error(hdl, EZFS_BADBACKUP, errbuf));
|
|
|
|
|
|
|
|
default:
|
|
|
|
return (zfs_standard_error(hdl, errno, errbuf));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-28 20:45:14 +00:00
|
|
|
if (debugnv)
|
|
|
|
VERIFY(0 == nvlist_add_nvlist(debugnv, zhp->zfs_name, thisdbg));
|
|
|
|
nvlist_free(thisdbg);
|
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2013-05-25 02:06:23 +00:00
|
|
|
static void
|
|
|
|
gather_holds(zfs_handle_t *zhp, send_dump_data_t *sdd)
|
2010-08-26 21:24:34 +00:00
|
|
|
{
|
|
|
|
assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
|
|
|
|
|
|
|
|
/*
|
2013-05-25 02:06:23 +00:00
|
|
|
* zfs_send() only sets snapholds for sends that need them,
|
2010-08-26 21:24:34 +00:00
|
|
|
* e.g. replication and doall.
|
|
|
|
*/
|
2013-05-25 02:06:23 +00:00
|
|
|
if (sdd->snapholds == NULL)
|
|
|
|
return;
|
2010-08-26 21:24:34 +00:00
|
|
|
|
2013-05-25 02:06:23 +00:00
|
|
|
fnvlist_add_string(sdd->snapholds, zhp->zfs_name, sdd->holdtag);
|
2010-08-26 21:24:34 +00:00
|
|
|
}
|
|
|
|
|
2012-05-09 22:05:14 +00:00
|
|
|
static void *
|
|
|
|
send_progress_thread(void *arg)
|
|
|
|
{
|
|
|
|
progress_arg_t *pa = arg;
|
2013-09-04 12:00:57 +00:00
|
|
|
zfs_cmd_t zc = {"\0"};
|
2012-05-09 22:05:14 +00:00
|
|
|
zfs_handle_t *zhp = pa->pa_zhp;
|
|
|
|
libzfs_handle_t *hdl = zhp->zfs_hdl;
|
|
|
|
unsigned long long bytes;
|
|
|
|
char buf[16];
|
|
|
|
time_t t;
|
|
|
|
struct tm *tm;
|
|
|
|
|
|
|
|
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
|
|
|
|
|
|
|
|
if (!pa->pa_parsable)
|
|
|
|
(void) fprintf(stderr, "TIME SENT SNAPSHOT\n");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Print the progress from ZFS_IOC_SEND_PROGRESS every second.
|
|
|
|
*/
|
|
|
|
for (;;) {
|
|
|
|
(void) sleep(1);
|
|
|
|
|
|
|
|
zc.zc_cookie = pa->pa_fd;
|
|
|
|
if (zfs_ioctl(hdl, ZFS_IOC_SEND_PROGRESS, &zc) != 0)
|
|
|
|
return ((void *)-1);
|
|
|
|
|
|
|
|
(void) time(&t);
|
|
|
|
tm = localtime(&t);
|
|
|
|
bytes = zc.zc_cookie;
|
|
|
|
|
|
|
|
if (pa->pa_parsable) {
|
|
|
|
(void) fprintf(stderr, "%02d:%02d:%02d\t%llu\t%s\n",
|
|
|
|
tm->tm_hour, tm->tm_min, tm->tm_sec,
|
|
|
|
bytes, zhp->zfs_name);
|
|
|
|
} else {
|
2017-05-02 20:43:53 +00:00
|
|
|
zfs_nicebytes(bytes, buf, sizeof (buf));
|
2012-05-09 22:05:14 +00:00
|
|
|
(void) fprintf(stderr, "%02d:%02d:%02d %5s %s\n",
|
|
|
|
tm->tm_hour, tm->tm_min, tm->tm_sec,
|
|
|
|
buf, zhp->zfs_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-06 21:22:48 +00:00
|
|
|
static void
|
|
|
|
send_print_verbose(FILE *fout, const char *tosnap, const char *fromsnap,
|
|
|
|
uint64_t size, boolean_t parsable)
|
|
|
|
{
|
|
|
|
if (parsable) {
|
|
|
|
if (fromsnap != NULL) {
|
|
|
|
(void) fprintf(fout, "incremental\t%s\t%s",
|
|
|
|
fromsnap, tosnap);
|
|
|
|
} else {
|
|
|
|
(void) fprintf(fout, "full\t%s",
|
|
|
|
tosnap);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (fromsnap != NULL) {
|
|
|
|
if (strchr(fromsnap, '@') == NULL &&
|
|
|
|
strchr(fromsnap, '#') == NULL) {
|
|
|
|
(void) fprintf(fout, dgettext(TEXT_DOMAIN,
|
|
|
|
"send from @%s to %s"),
|
|
|
|
fromsnap, tosnap);
|
|
|
|
} else {
|
|
|
|
(void) fprintf(fout, dgettext(TEXT_DOMAIN,
|
|
|
|
"send from %s to %s"),
|
|
|
|
fromsnap, tosnap);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
(void) fprintf(fout, dgettext(TEXT_DOMAIN,
|
|
|
|
"full send of %s"),
|
|
|
|
tosnap);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-08 22:24:31 +00:00
|
|
|
if (parsable) {
|
|
|
|
(void) fprintf(fout, "\t%llu",
|
|
|
|
(longlong_t)size);
|
|
|
|
} else if (size != 0) {
|
|
|
|
char buf[16];
|
|
|
|
zfs_nicebytes(size, buf, sizeof (buf));
|
|
|
|
(void) fprintf(fout, dgettext(TEXT_DOMAIN,
|
|
|
|
" estimated size is %s"), buf);
|
2016-01-06 21:22:48 +00:00
|
|
|
}
|
|
|
|
(void) fprintf(fout, "\n");
|
|
|
|
}
|
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
static int
|
|
|
|
dump_snapshot(zfs_handle_t *zhp, void *arg)
|
|
|
|
{
|
|
|
|
send_dump_data_t *sdd = arg;
|
2012-05-09 22:05:14 +00:00
|
|
|
progress_arg_t pa = { 0 };
|
|
|
|
pthread_t tid;
|
2010-08-26 21:24:34 +00:00
|
|
|
char *thissnap;
|
2016-07-11 17:45:52 +00:00
|
|
|
enum lzc_send_flags flags = 0;
|
2008-11-20 20:01:55 +00:00
|
|
|
int err;
|
2011-11-17 18:14:36 +00:00
|
|
|
boolean_t isfromsnap, istosnap, fromorigin;
|
2010-05-28 20:45:14 +00:00
|
|
|
boolean_t exclude = B_FALSE;
|
2015-07-10 23:54:52 +00:00
|
|
|
FILE *fout = sdd->std_out ? stdout : stderr;
|
2008-11-20 20:01:55 +00:00
|
|
|
|
2013-05-25 02:06:23 +00:00
|
|
|
err = 0;
|
2008-11-20 20:01:55 +00:00
|
|
|
thissnap = strchr(zhp->zfs_name, '@') + 1;
|
2010-05-28 20:45:14 +00:00
|
|
|
isfromsnap = (sdd->fromsnap != NULL &&
|
|
|
|
strcmp(sdd->fromsnap, thissnap) == 0);
|
2008-11-20 20:01:55 +00:00
|
|
|
|
2010-05-28 20:45:14 +00:00
|
|
|
if (!sdd->seenfrom && isfromsnap) {
|
2013-05-25 02:06:23 +00:00
|
|
|
gather_holds(zhp, sdd);
|
|
|
|
sdd->seenfrom = B_TRUE;
|
2016-09-29 19:06:14 +00:00
|
|
|
(void) strlcpy(sdd->prevsnap, thissnap,
|
|
|
|
sizeof (sdd->prevsnap));
|
2013-05-25 02:06:23 +00:00
|
|
|
sdd->prevsnap_obj = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID);
|
2008-11-20 20:01:55 +00:00
|
|
|
zfs_close(zhp);
|
2013-05-25 02:06:23 +00:00
|
|
|
return (0);
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (sdd->seento || !sdd->seenfrom) {
|
|
|
|
zfs_close(zhp);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2010-05-28 20:45:14 +00:00
|
|
|
istosnap = (strcmp(sdd->tosnap, thissnap) == 0);
|
|
|
|
if (istosnap)
|
|
|
|
sdd->seento = B_TRUE;
|
|
|
|
|
2016-07-11 17:45:52 +00:00
|
|
|
if (sdd->large_block)
|
|
|
|
flags |= LZC_SEND_FLAG_LARGE_BLOCK;
|
|
|
|
if (sdd->embed_data)
|
|
|
|
flags |= LZC_SEND_FLAG_EMBED_DATA;
|
|
|
|
if (sdd->compress)
|
|
|
|
flags |= LZC_SEND_FLAG_COMPRESS;
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
if (sdd->raw)
|
|
|
|
flags |= LZC_SEND_FLAG_RAW;
|
2016-07-11 17:45:52 +00:00
|
|
|
|
2010-05-28 20:45:14 +00:00
|
|
|
if (!sdd->doall && !isfromsnap && !istosnap) {
|
|
|
|
if (sdd->replicate) {
|
|
|
|
char *snapname;
|
|
|
|
nvlist_t *snapprops;
|
|
|
|
/*
|
|
|
|
* Filter out all intermediate snapshots except origin
|
|
|
|
* snapshots needed to replicate clones.
|
|
|
|
*/
|
|
|
|
nvlist_t *nvfs = fsavl_find(sdd->fsavl,
|
|
|
|
zhp->zfs_dmustats.dds_guid, &snapname);
|
|
|
|
|
|
|
|
VERIFY(0 == nvlist_lookup_nvlist(nvfs,
|
|
|
|
"snapprops", &snapprops));
|
|
|
|
VERIFY(0 == nvlist_lookup_nvlist(snapprops,
|
|
|
|
thissnap, &snapprops));
|
|
|
|
exclude = !nvlist_exists(snapprops, "is_clone_origin");
|
|
|
|
} else {
|
|
|
|
exclude = B_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If a filter function exists, call it to determine whether
|
|
|
|
* this snapshot will be sent.
|
|
|
|
*/
|
|
|
|
if (exclude || (sdd->filter_cb != NULL &&
|
|
|
|
sdd->filter_cb(zhp, sdd->filter_cb_arg) == B_FALSE)) {
|
|
|
|
/*
|
|
|
|
* This snapshot is filtered out. Don't send it, and don't
|
2010-08-26 21:24:34 +00:00
|
|
|
* set prevsnap_obj, so it will be as if this snapshot didn't
|
2010-05-28 20:45:14 +00:00
|
|
|
* exist, and the next accepted snapshot will be sent as
|
|
|
|
* an incremental from the last accepted one, or as the
|
|
|
|
* first (and full) snapshot in the case of a replication,
|
|
|
|
* non-incremental send.
|
|
|
|
*/
|
|
|
|
zfs_close(zhp);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2013-05-25 02:06:23 +00:00
|
|
|
gather_holds(zhp, sdd);
|
2011-11-17 18:14:36 +00:00
|
|
|
fromorigin = sdd->prevsnap[0] == '\0' &&
|
|
|
|
(sdd->fromorigin || sdd->replicate);
|
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
if (sdd->verbose) {
|
2016-01-06 21:22:48 +00:00
|
|
|
uint64_t size = 0;
|
2017-08-31 16:00:35 +00:00
|
|
|
char fromds[ZFS_MAX_DATASET_NAME_LEN];
|
2011-11-17 18:14:36 +00:00
|
|
|
|
2017-08-31 16:00:35 +00:00
|
|
|
if (sdd->prevsnap[0] != '\0') {
|
|
|
|
(void) strlcpy(fromds, zhp->zfs_name, sizeof (fromds));
|
|
|
|
*(strchr(fromds, '@') + 1) = '\0';
|
|
|
|
(void) strlcat(fromds, sdd->prevsnap, sizeof (fromds));
|
|
|
|
}
|
|
|
|
if (zfs_send_space(zhp, zhp->zfs_name,
|
|
|
|
sdd->prevsnap[0] ? fromds : NULL, flags, &size) != 0) {
|
|
|
|
size = 0; /* cannot estimate send space */
|
|
|
|
} else {
|
|
|
|
send_print_verbose(fout, zhp->zfs_name,
|
|
|
|
sdd->prevsnap[0] ? sdd->prevsnap : NULL,
|
|
|
|
size, sdd->parsable);
|
|
|
|
}
|
2016-01-06 21:22:48 +00:00
|
|
|
sdd->size += size;
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
2011-11-17 18:14:36 +00:00
|
|
|
if (!sdd->dryrun) {
|
2012-05-09 22:05:14 +00:00
|
|
|
/*
|
|
|
|
* If progress reporting is requested, spawn a new thread to
|
|
|
|
* poll ZFS_IOC_SEND_PROGRESS at a regular interval.
|
|
|
|
*/
|
|
|
|
if (sdd->progress) {
|
|
|
|
pa.pa_zhp = zhp;
|
|
|
|
pa.pa_fd = sdd->outfd;
|
|
|
|
pa.pa_parsable = sdd->parsable;
|
|
|
|
|
|
|
|
if ((err = pthread_create(&tid, NULL,
|
2017-02-07 22:02:27 +00:00
|
|
|
send_progress_thread, &pa)) != 0) {
|
2012-05-09 22:05:14 +00:00
|
|
|
zfs_close(zhp);
|
|
|
|
return (err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-11-17 18:14:36 +00:00
|
|
|
err = dump_ioctl(zhp, sdd->prevsnap, sdd->prevsnap_obj,
|
2014-06-05 21:19:08 +00:00
|
|
|
fromorigin, sdd->outfd, flags, sdd->debugnv);
|
2012-05-09 22:05:14 +00:00
|
|
|
|
|
|
|
if (sdd->progress) {
|
|
|
|
(void) pthread_cancel(tid);
|
|
|
|
(void) pthread_join(tid, NULL);
|
|
|
|
}
|
2011-11-17 18:14:36 +00:00
|
|
|
}
|
2008-11-20 20:01:55 +00:00
|
|
|
|
2010-08-26 21:24:34 +00:00
|
|
|
(void) strcpy(sdd->prevsnap, thissnap);
|
|
|
|
sdd->prevsnap_obj = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID);
|
2008-11-20 20:01:55 +00:00
|
|
|
zfs_close(zhp);
|
|
|
|
return (err);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
dump_filesystem(zfs_handle_t *zhp, void *arg)
|
|
|
|
{
|
|
|
|
int rv = 0;
|
|
|
|
send_dump_data_t *sdd = arg;
|
|
|
|
boolean_t missingfrom = B_FALSE;
|
2013-09-04 12:00:57 +00:00
|
|
|
zfs_cmd_t zc = {"\0"};
|
2019-03-12 20:13:22 +00:00
|
|
|
uint64_t min_txg = 0, max_txg = 0;
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
(void) snprintf(zc.zc_name, sizeof (zc.zc_name), "%s@%s",
|
|
|
|
zhp->zfs_name, sdd->tosnap);
|
|
|
|
if (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0) {
|
2011-11-17 18:14:36 +00:00
|
|
|
(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
|
|
|
|
"WARNING: could not send %s@%s: does not exist\n"),
|
2008-11-20 20:01:55 +00:00
|
|
|
zhp->zfs_name, sdd->tosnap);
|
|
|
|
sdd->err = B_TRUE;
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sdd->replicate && sdd->fromsnap) {
|
|
|
|
/*
|
|
|
|
* If this fs does not have fromsnap, and we're doing
|
|
|
|
* recursive, we need to send a full stream from the
|
|
|
|
* beginning (or an incremental from the origin if this
|
|
|
|
* is a clone). If we're doing non-recursive, then let
|
|
|
|
* them get the error.
|
|
|
|
*/
|
|
|
|
(void) snprintf(zc.zc_name, sizeof (zc.zc_name), "%s@%s",
|
|
|
|
zhp->zfs_name, sdd->fromsnap);
|
|
|
|
if (ioctl(zhp->zfs_hdl->libzfs_fd,
|
|
|
|
ZFS_IOC_OBJSET_STATS, &zc) != 0) {
|
|
|
|
missingfrom = B_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-28 20:45:14 +00:00
|
|
|
sdd->seenfrom = sdd->seento = sdd->prevsnap[0] = 0;
|
2010-08-26 21:24:34 +00:00
|
|
|
sdd->prevsnap_obj = 0;
|
2010-05-28 20:45:14 +00:00
|
|
|
if (sdd->fromsnap == NULL || missingfrom)
|
|
|
|
sdd->seenfrom = B_TRUE;
|
2008-11-20 20:01:55 +00:00
|
|
|
|
2019-03-12 20:13:22 +00:00
|
|
|
if (!sdd->replicate && sdd->fromsnap != NULL)
|
|
|
|
min_txg = get_snap_txg(zhp->zfs_hdl, zhp->zfs_name,
|
|
|
|
sdd->fromsnap);
|
|
|
|
if (!sdd->replicate && sdd->tosnap != NULL)
|
|
|
|
max_txg = get_snap_txg(zhp->zfs_hdl, zhp->zfs_name,
|
|
|
|
sdd->tosnap);
|
|
|
|
|
|
|
|
rv = zfs_iter_snapshots_sorted(zhp, dump_snapshot, arg,
|
|
|
|
min_txg, max_txg);
|
2010-05-28 20:45:14 +00:00
|
|
|
if (!sdd->seenfrom) {
|
2011-11-17 18:14:36 +00:00
|
|
|
(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
|
2010-05-28 20:45:14 +00:00
|
|
|
"WARNING: could not send %s@%s:\n"
|
2011-11-17 18:14:36 +00:00
|
|
|
"incremental source (%s@%s) does not exist\n"),
|
2010-05-28 20:45:14 +00:00
|
|
|
zhp->zfs_name, sdd->tosnap,
|
|
|
|
zhp->zfs_name, sdd->fromsnap);
|
|
|
|
sdd->err = B_TRUE;
|
|
|
|
} else if (!sdd->seento) {
|
|
|
|
if (sdd->fromsnap) {
|
2011-11-17 18:14:36 +00:00
|
|
|
(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
|
2008-11-20 20:01:55 +00:00
|
|
|
"WARNING: could not send %s@%s:\n"
|
2010-05-28 20:45:14 +00:00
|
|
|
"incremental source (%s@%s) "
|
2011-11-17 18:14:36 +00:00
|
|
|
"is not earlier than it\n"),
|
2008-11-20 20:01:55 +00:00
|
|
|
zhp->zfs_name, sdd->tosnap,
|
|
|
|
zhp->zfs_name, sdd->fromsnap);
|
|
|
|
} else {
|
2011-11-17 18:14:36 +00:00
|
|
|
(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
|
|
|
|
"WARNING: "
|
|
|
|
"could not send %s@%s: does not exist\n"),
|
2010-05-28 20:45:14 +00:00
|
|
|
zhp->zfs_name, sdd->tosnap);
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
2010-05-28 20:45:14 +00:00
|
|
|
sdd->err = B_TRUE;
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return (rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
dump_filesystems(zfs_handle_t *rzhp, void *arg)
|
|
|
|
{
|
|
|
|
send_dump_data_t *sdd = arg;
|
|
|
|
nvpair_t *fspair;
|
|
|
|
boolean_t needagain, progress;
|
|
|
|
|
|
|
|
if (!sdd->replicate)
|
|
|
|
return (dump_filesystem(rzhp, sdd));
|
|
|
|
|
2010-05-28 20:45:14 +00:00
|
|
|
/* Mark the clone origin snapshots. */
|
|
|
|
for (fspair = nvlist_next_nvpair(sdd->fss, NULL); fspair;
|
|
|
|
fspair = nvlist_next_nvpair(sdd->fss, fspair)) {
|
|
|
|
nvlist_t *nvfs;
|
|
|
|
uint64_t origin_guid = 0;
|
|
|
|
|
|
|
|
VERIFY(0 == nvpair_value_nvlist(fspair, &nvfs));
|
|
|
|
(void) nvlist_lookup_uint64(nvfs, "origin", &origin_guid);
|
|
|
|
if (origin_guid != 0) {
|
|
|
|
char *snapname;
|
|
|
|
nvlist_t *origin_nv = fsavl_find(sdd->fsavl,
|
|
|
|
origin_guid, &snapname);
|
|
|
|
if (origin_nv != NULL) {
|
|
|
|
nvlist_t *snapprops;
|
|
|
|
VERIFY(0 == nvlist_lookup_nvlist(origin_nv,
|
|
|
|
"snapprops", &snapprops));
|
|
|
|
VERIFY(0 == nvlist_lookup_nvlist(snapprops,
|
|
|
|
snapname, &snapprops));
|
|
|
|
VERIFY(0 == nvlist_add_boolean(
|
|
|
|
snapprops, "is_clone_origin"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-11-20 20:01:55 +00:00
|
|
|
again:
|
|
|
|
needagain = progress = B_FALSE;
|
|
|
|
for (fspair = nvlist_next_nvpair(sdd->fss, NULL); fspair;
|
|
|
|
fspair = nvlist_next_nvpair(sdd->fss, fspair)) {
|
2011-11-17 18:14:36 +00:00
|
|
|
nvlist_t *fslist, *parent_nv;
|
2008-11-20 20:01:55 +00:00
|
|
|
char *fsname;
|
|
|
|
zfs_handle_t *zhp;
|
|
|
|
int err;
|
|
|
|
uint64_t origin_guid = 0;
|
2011-11-17 18:14:36 +00:00
|
|
|
uint64_t parent_guid = 0;
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
VERIFY(nvpair_value_nvlist(fspair, &fslist) == 0);
|
|
|
|
if (nvlist_lookup_boolean(fslist, "sent") == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
VERIFY(nvlist_lookup_string(fslist, "name", &fsname) == 0);
|
|
|
|
(void) nvlist_lookup_uint64(fslist, "origin", &origin_guid);
|
2011-11-17 18:14:36 +00:00
|
|
|
(void) nvlist_lookup_uint64(fslist, "parentfromsnap",
|
|
|
|
&parent_guid);
|
|
|
|
|
|
|
|
if (parent_guid != 0) {
|
|
|
|
parent_nv = fsavl_find(sdd->fsavl, parent_guid, NULL);
|
|
|
|
if (!nvlist_exists(parent_nv, "sent")) {
|
|
|
|
/* parent has not been sent; skip this one */
|
|
|
|
needagain = B_TRUE;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2008-11-20 20:01:55 +00:00
|
|
|
|
2010-05-28 20:45:14 +00:00
|
|
|
if (origin_guid != 0) {
|
|
|
|
nvlist_t *origin_nv = fsavl_find(sdd->fsavl,
|
|
|
|
origin_guid, NULL);
|
|
|
|
if (origin_nv != NULL &&
|
2011-11-17 18:14:36 +00:00
|
|
|
!nvlist_exists(origin_nv, "sent")) {
|
2010-05-28 20:45:14 +00:00
|
|
|
/*
|
|
|
|
* origin has not been sent yet;
|
|
|
|
* skip this clone.
|
|
|
|
*/
|
|
|
|
needagain = B_TRUE;
|
|
|
|
continue;
|
|
|
|
}
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
zhp = zfs_open(rzhp->zfs_hdl, fsname, ZFS_TYPE_DATASET);
|
|
|
|
if (zhp == NULL)
|
|
|
|
return (-1);
|
|
|
|
err = dump_filesystem(zhp, sdd);
|
|
|
|
VERIFY(nvlist_add_boolean(fslist, "sent") == 0);
|
|
|
|
progress = B_TRUE;
|
|
|
|
zfs_close(zhp);
|
|
|
|
if (err)
|
|
|
|
return (err);
|
|
|
|
}
|
|
|
|
if (needagain) {
|
|
|
|
assert(progress);
|
|
|
|
goto again;
|
|
|
|
}
|
2011-11-17 18:14:36 +00:00
|
|
|
|
|
|
|
/* clean out the sent flags in case we reuse this fss */
|
|
|
|
for (fspair = nvlist_next_nvpair(sdd->fss, NULL); fspair;
|
|
|
|
fspair = nvlist_next_nvpair(sdd->fss, fspair)) {
|
|
|
|
nvlist_t *fslist;
|
|
|
|
|
|
|
|
VERIFY(nvpair_value_nvlist(fspair, &fslist) == 0);
|
|
|
|
(void) nvlist_remove_all(fslist, "sent");
|
|
|
|
}
|
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2016-01-06 21:22:48 +00:00
|
|
|
nvlist_t *
|
|
|
|
zfs_send_resume_token_to_nvlist(libzfs_handle_t *hdl, const char *token)
|
|
|
|
{
|
|
|
|
unsigned int version;
|
|
|
|
int nread, i;
|
|
|
|
unsigned long long checksum, packed_len;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Decode token header, which is:
|
|
|
|
* <token version>-<checksum of payload>-<uncompressed payload length>
|
|
|
|
* Note that the only supported token version is 1.
|
|
|
|
*/
|
|
|
|
nread = sscanf(token, "%u-%llx-%llx-",
|
|
|
|
&version, &checksum, &packed_len);
|
|
|
|
if (nread != 3) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"resume token is corrupt (invalid format)"));
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (version != ZFS_SEND_RESUME_TOKEN_VERSION) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"resume token is corrupt (invalid version %u)"),
|
|
|
|
version);
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* convert hexadecimal representation to binary */
|
|
|
|
token = strrchr(token, '-') + 1;
|
|
|
|
int len = strlen(token) / 2;
|
|
|
|
unsigned char *compressed = zfs_alloc(hdl, len);
|
|
|
|
for (i = 0; i < len; i++) {
|
|
|
|
nread = sscanf(token + i * 2, "%2hhx", compressed + i);
|
|
|
|
if (nread != 1) {
|
|
|
|
free(compressed);
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"resume token is corrupt "
|
|
|
|
"(payload is not hex-encoded)"));
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* verify checksum */
|
|
|
|
zio_cksum_t cksum;
|
2016-07-12 15:50:54 +00:00
|
|
|
fletcher_4_native_varsize(compressed, len, &cksum);
|
2016-01-06 21:22:48 +00:00
|
|
|
if (cksum.zc_word[0] != checksum) {
|
|
|
|
free(compressed);
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"resume token is corrupt (incorrect checksum)"));
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* uncompress */
|
|
|
|
void *packed = zfs_alloc(hdl, packed_len);
|
|
|
|
uLongf packed_len_long = packed_len;
|
|
|
|
if (uncompress(packed, &packed_len_long, compressed, len) != Z_OK ||
|
|
|
|
packed_len_long != packed_len) {
|
|
|
|
free(packed);
|
|
|
|
free(compressed);
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"resume token is corrupt (decompression failed)"));
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* unpack nvlist */
|
|
|
|
nvlist_t *nv;
|
|
|
|
int error = nvlist_unpack(packed, packed_len, &nv, KM_SLEEP);
|
|
|
|
free(packed);
|
|
|
|
free(compressed);
|
|
|
|
if (error != 0) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"resume token is corrupt (nvlist_unpack failed)"));
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
return (nv);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
zfs_send_resume(libzfs_handle_t *hdl, sendflags_t *flags, int outfd,
|
|
|
|
const char *resume_token)
|
|
|
|
{
|
|
|
|
char errbuf[1024];
|
|
|
|
char *toname;
|
|
|
|
char *fromname = NULL;
|
|
|
|
uint64_t resumeobj, resumeoff, toguid, fromguid, bytes;
|
|
|
|
zfs_handle_t *zhp;
|
|
|
|
int error = 0;
|
2016-06-15 21:28:36 +00:00
|
|
|
char name[ZFS_MAX_DATASET_NAME_LEN];
|
2016-01-06 21:22:48 +00:00
|
|
|
enum lzc_send_flags lzc_flags = 0;
|
2017-10-10 22:22:05 +00:00
|
|
|
FILE *fout = (flags->verbose && flags->dryrun) ? stdout : stderr;
|
2016-01-06 21:22:48 +00:00
|
|
|
|
|
|
|
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
|
|
|
|
"cannot resume send"));
|
|
|
|
|
|
|
|
nvlist_t *resume_nvl =
|
|
|
|
zfs_send_resume_token_to_nvlist(hdl, resume_token);
|
|
|
|
if (resume_nvl == NULL) {
|
|
|
|
/*
|
|
|
|
* zfs_error_aux has already been set by
|
|
|
|
* zfs_send_resume_token_to_nvlist
|
|
|
|
*/
|
|
|
|
return (zfs_error(hdl, EZFS_FAULT, errbuf));
|
|
|
|
}
|
|
|
|
if (flags->verbose) {
|
2017-10-10 22:22:05 +00:00
|
|
|
(void) fprintf(fout, dgettext(TEXT_DOMAIN,
|
2016-01-06 21:22:48 +00:00
|
|
|
"resume token contents:\n"));
|
2017-10-10 22:22:05 +00:00
|
|
|
nvlist_print(fout, resume_nvl);
|
2016-01-06 21:22:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (nvlist_lookup_string(resume_nvl, "toname", &toname) != 0 ||
|
|
|
|
nvlist_lookup_uint64(resume_nvl, "object", &resumeobj) != 0 ||
|
|
|
|
nvlist_lookup_uint64(resume_nvl, "offset", &resumeoff) != 0 ||
|
|
|
|
nvlist_lookup_uint64(resume_nvl, "bytes", &bytes) != 0 ||
|
|
|
|
nvlist_lookup_uint64(resume_nvl, "toguid", &toguid) != 0) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"resume token is corrupt"));
|
|
|
|
return (zfs_error(hdl, EZFS_FAULT, errbuf));
|
|
|
|
}
|
|
|
|
fromguid = 0;
|
|
|
|
(void) nvlist_lookup_uint64(resume_nvl, "fromguid", &fromguid);
|
|
|
|
|
2016-07-11 17:45:52 +00:00
|
|
|
if (flags->largeblock || nvlist_exists(resume_nvl, "largeblockok"))
|
|
|
|
lzc_flags |= LZC_SEND_FLAG_LARGE_BLOCK;
|
2016-01-06 21:22:48 +00:00
|
|
|
if (flags->embed_data || nvlist_exists(resume_nvl, "embedok"))
|
|
|
|
lzc_flags |= LZC_SEND_FLAG_EMBED_DATA;
|
2016-07-11 17:45:52 +00:00
|
|
|
if (flags->compress || nvlist_exists(resume_nvl, "compressok"))
|
|
|
|
lzc_flags |= LZC_SEND_FLAG_COMPRESS;
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
if (flags->raw || nvlist_exists(resume_nvl, "rawok"))
|
|
|
|
lzc_flags |= LZC_SEND_FLAG_RAW;
|
2016-01-06 21:22:48 +00:00
|
|
|
|
|
|
|
if (guid_to_name(hdl, toname, toguid, B_FALSE, name) != 0) {
|
|
|
|
if (zfs_dataset_exists(hdl, toname, ZFS_TYPE_DATASET)) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"'%s' is no longer the same snapshot used in "
|
|
|
|
"the initial send"), toname);
|
|
|
|
} else {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"'%s' used in the initial send no longer exists"),
|
|
|
|
toname);
|
|
|
|
}
|
|
|
|
return (zfs_error(hdl, EZFS_BADPATH, errbuf));
|
|
|
|
}
|
|
|
|
zhp = zfs_open(hdl, name, ZFS_TYPE_DATASET);
|
|
|
|
if (zhp == NULL) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"unable to access '%s'"), name);
|
|
|
|
return (zfs_error(hdl, EZFS_BADPATH, errbuf));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fromguid != 0) {
|
|
|
|
if (guid_to_name(hdl, toname, fromguid, B_TRUE, name) != 0) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"incremental source %#llx no longer exists"),
|
|
|
|
(longlong_t)fromguid);
|
|
|
|
return (zfs_error(hdl, EZFS_BADPATH, errbuf));
|
|
|
|
}
|
|
|
|
fromname = name;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags->verbose) {
|
|
|
|
uint64_t size = 0;
|
2016-07-11 17:45:52 +00:00
|
|
|
error = lzc_send_space(zhp->zfs_name, fromname,
|
|
|
|
lzc_flags, &size);
|
2016-01-06 21:22:48 +00:00
|
|
|
if (error == 0)
|
|
|
|
size = MAX(0, (int64_t)(size - bytes));
|
2017-10-10 22:22:05 +00:00
|
|
|
send_print_verbose(fout, zhp->zfs_name, fromname,
|
2016-01-06 21:22:48 +00:00
|
|
|
size, flags->parsable);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!flags->dryrun) {
|
|
|
|
progress_arg_t pa = { 0 };
|
|
|
|
pthread_t tid;
|
|
|
|
/*
|
|
|
|
* If progress reporting is requested, spawn a new thread to
|
|
|
|
* poll ZFS_IOC_SEND_PROGRESS at a regular interval.
|
|
|
|
*/
|
|
|
|
if (flags->progress) {
|
|
|
|
pa.pa_zhp = zhp;
|
|
|
|
pa.pa_fd = outfd;
|
|
|
|
pa.pa_parsable = flags->parsable;
|
|
|
|
|
|
|
|
error = pthread_create(&tid, NULL,
|
|
|
|
send_progress_thread, &pa);
|
|
|
|
if (error != 0) {
|
|
|
|
zfs_close(zhp);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
error = lzc_send_resume(zhp->zfs_name, fromname, outfd,
|
|
|
|
lzc_flags, resumeobj, resumeoff);
|
|
|
|
|
|
|
|
if (flags->progress) {
|
|
|
|
(void) pthread_cancel(tid);
|
|
|
|
(void) pthread_join(tid, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
char errbuf[1024];
|
|
|
|
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
|
|
|
|
"warning: cannot send '%s'"), zhp->zfs_name);
|
|
|
|
|
|
|
|
zfs_close(zhp);
|
|
|
|
|
|
|
|
switch (error) {
|
|
|
|
case 0:
|
|
|
|
return (0);
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
case EACCES:
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"source key must be loaded"));
|
|
|
|
return (zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf));
|
|
|
|
|
2016-01-06 21:22:48 +00:00
|
|
|
case EXDEV:
|
|
|
|
case ENOENT:
|
|
|
|
case EDQUOT:
|
|
|
|
case EFBIG:
|
|
|
|
case EIO:
|
|
|
|
case ENOLINK:
|
|
|
|
case ENOSPC:
|
|
|
|
case ENOSTR:
|
|
|
|
case ENXIO:
|
|
|
|
case EPIPE:
|
|
|
|
case ERANGE:
|
|
|
|
case EFAULT:
|
|
|
|
case EROFS:
|
|
|
|
zfs_error_aux(hdl, strerror(errno));
|
|
|
|
return (zfs_error(hdl, EZFS_BADBACKUP, errbuf));
|
|
|
|
|
|
|
|
default:
|
|
|
|
return (zfs_standard_error(hdl, errno, errbuf));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
zfs_close(zhp);
|
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
/*
|
2009-08-18 18:43:27 +00:00
|
|
|
* Generate a send stream for the dataset identified by the argument zhp.
|
|
|
|
*
|
|
|
|
* The content of the send stream is the snapshot identified by
|
|
|
|
* 'tosnap'. Incremental streams are requested in two ways:
|
|
|
|
* - from the snapshot identified by "fromsnap" (if non-null) or
|
|
|
|
* - from the origin of the dataset identified by zhp, which must
|
|
|
|
* be a clone. In this case, "fromsnap" is null and "fromorigin"
|
|
|
|
* is TRUE.
|
|
|
|
*
|
|
|
|
* The send stream is recursive (i.e. dumps a hierarchy of snapshots) and
|
2010-05-28 20:45:14 +00:00
|
|
|
* uses a special header (with a hdrtype field of DMU_COMPOUNDSTREAM)
|
2009-08-18 18:43:27 +00:00
|
|
|
* if "replicate" is set. If "doall" is set, dump all the intermediate
|
2010-05-28 20:45:14 +00:00
|
|
|
* snapshots. The DMU_COMPOUNDSTREAM header is used in the "doall"
|
|
|
|
* case too. If "props" is set, send properties.
|
2008-11-20 20:01:55 +00:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
|
2011-11-17 18:14:36 +00:00
|
|
|
sendflags_t *flags, int outfd, snapfilter_cb_t filter_func,
|
2010-05-28 20:45:14 +00:00
|
|
|
void *cb_arg, nvlist_t **debugnvp)
|
2008-11-20 20:01:55 +00:00
|
|
|
{
|
|
|
|
char errbuf[1024];
|
|
|
|
send_dump_data_t sdd = { 0 };
|
2011-11-17 18:14:36 +00:00
|
|
|
int err = 0;
|
2008-11-20 20:01:55 +00:00
|
|
|
nvlist_t *fss = NULL;
|
|
|
|
avl_tree_t *fsavl = NULL;
|
2010-05-28 20:45:14 +00:00
|
|
|
static uint64_t holdseq;
|
|
|
|
int spa_version;
|
2013-05-25 02:06:23 +00:00
|
|
|
pthread_t tid = 0;
|
2010-05-28 20:45:14 +00:00
|
|
|
int pipefd[2];
|
|
|
|
dedup_arg_t dda = { 0 };
|
|
|
|
int featureflags = 0;
|
2015-07-10 23:54:52 +00:00
|
|
|
FILE *fout;
|
2010-05-28 20:45:14 +00:00
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
|
|
|
|
"cannot send '%s'"), zhp->zfs_name);
|
|
|
|
|
|
|
|
if (fromsnap && fromsnap[0] == '\0') {
|
|
|
|
zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"zero-length incremental source"));
|
|
|
|
return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf));
|
|
|
|
}
|
|
|
|
|
2010-08-26 21:24:34 +00:00
|
|
|
if (zhp->zfs_type == ZFS_TYPE_FILESYSTEM) {
|
|
|
|
uint64_t version;
|
|
|
|
version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
|
|
|
|
if (version >= ZPL_VERSION_SA) {
|
|
|
|
featureflags |= DMU_BACKUP_FEATURE_SA_SPILL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-15 20:41:38 +00:00
|
|
|
if (flags->holds)
|
|
|
|
featureflags |= DMU_BACKUP_FEATURE_HOLDS;
|
|
|
|
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
/*
|
|
|
|
* Start the dedup thread if this is a dedup stream. We do not bother
|
|
|
|
* doing this if this a raw send of an encrypted dataset with dedup off
|
|
|
|
* because normal encrypted blocks won't dedup.
|
|
|
|
*/
|
|
|
|
if (flags->dedup && !flags->dryrun && !(flags->raw &&
|
|
|
|
zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION) != ZIO_CRYPT_OFF &&
|
|
|
|
zfs_prop_get_int(zhp, ZFS_PROP_DEDUP) == ZIO_CHECKSUM_OFF)) {
|
2010-05-28 20:45:14 +00:00
|
|
|
featureflags |= (DMU_BACKUP_FEATURE_DEDUP |
|
|
|
|
DMU_BACKUP_FEATURE_DEDUPPROPS);
|
2017-02-07 22:02:27 +00:00
|
|
|
if ((err = socketpair(AF_UNIX, SOCK_STREAM, 0, pipefd)) != 0) {
|
2010-05-28 20:45:14 +00:00
|
|
|
zfs_error_aux(zhp->zfs_hdl, strerror(errno));
|
|
|
|
return (zfs_error(zhp->zfs_hdl, EZFS_PIPEFAILED,
|
|
|
|
errbuf));
|
|
|
|
}
|
|
|
|
dda.outputfd = outfd;
|
|
|
|
dda.inputfd = pipefd[1];
|
|
|
|
dda.dedup_hdl = zhp->zfs_hdl;
|
2017-02-07 22:02:27 +00:00
|
|
|
if ((err = pthread_create(&tid, NULL, cksummer, &dda)) != 0) {
|
2010-05-28 20:45:14 +00:00
|
|
|
(void) close(pipefd[0]);
|
|
|
|
(void) close(pipefd[1]);
|
|
|
|
zfs_error_aux(zhp->zfs_hdl, strerror(errno));
|
|
|
|
return (zfs_error(zhp->zfs_hdl,
|
|
|
|
EZFS_THREADCREATEFAILED, errbuf));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-15 20:41:38 +00:00
|
|
|
if (flags->replicate || flags->doall || flags->props ||
|
|
|
|
flags->holds || flags->backup) {
|
2008-11-20 20:01:55 +00:00
|
|
|
dmu_replay_record_t drr = { 0 };
|
|
|
|
char *packbuf = NULL;
|
|
|
|
size_t buflen = 0;
|
2016-01-06 21:22:48 +00:00
|
|
|
zio_cksum_t zc;
|
|
|
|
|
|
|
|
ZIO_SET_CHECKSUM(&zc, 0, 0, 0, 0);
|
2008-11-20 20:01:55 +00:00
|
|
|
|
2019-02-15 20:41:38 +00:00
|
|
|
if (flags->replicate || flags->props || flags->backup ||
|
|
|
|
flags->holds) {
|
2008-11-20 20:01:55 +00:00
|
|
|
nvlist_t *hdrnv;
|
|
|
|
|
|
|
|
VERIFY(0 == nvlist_alloc(&hdrnv, NV_UNIQUE_NAME, 0));
|
|
|
|
if (fromsnap) {
|
|
|
|
VERIFY(0 == nvlist_add_string(hdrnv,
|
|
|
|
"fromsnap", fromsnap));
|
|
|
|
}
|
|
|
|
VERIFY(0 == nvlist_add_string(hdrnv, "tosnap", tosnap));
|
2011-11-17 18:14:36 +00:00
|
|
|
if (!flags->replicate) {
|
2010-05-28 20:45:14 +00:00
|
|
|
VERIFY(0 == nvlist_add_boolean(hdrnv,
|
|
|
|
"not_recursive"));
|
|
|
|
}
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
if (flags->raw) {
|
|
|
|
VERIFY(0 == nvlist_add_boolean(hdrnv, "raw"));
|
|
|
|
}
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
err = gather_nvlist(zhp->zfs_hdl, zhp->zfs_name,
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
fromsnap, tosnap, flags->replicate, flags->raw,
|
2019-03-12 20:13:22 +00:00
|
|
|
flags->replicate, flags->verbose, flags->backup,
|
|
|
|
flags->holds, flags->props, &fss, &fsavl);
|
2010-08-26 21:24:34 +00:00
|
|
|
if (err)
|
2010-05-28 20:45:14 +00:00
|
|
|
goto err_out;
|
2008-11-20 20:01:55 +00:00
|
|
|
VERIFY(0 == nvlist_add_nvlist(hdrnv, "fss", fss));
|
|
|
|
err = nvlist_pack(hdrnv, &packbuf, &buflen,
|
|
|
|
NV_ENCODE_XDR, 0);
|
2010-05-28 20:45:14 +00:00
|
|
|
if (debugnvp)
|
|
|
|
*debugnvp = hdrnv;
|
|
|
|
else
|
|
|
|
nvlist_free(hdrnv);
|
2013-05-25 02:06:23 +00:00
|
|
|
if (err)
|
2010-05-28 20:45:14 +00:00
|
|
|
goto stderr_out;
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
2011-11-17 18:14:36 +00:00
|
|
|
if (!flags->dryrun) {
|
|
|
|
/* write first begin record */
|
|
|
|
drr.drr_type = DRR_BEGIN;
|
|
|
|
drr.drr_u.drr_begin.drr_magic = DMU_BACKUP_MAGIC;
|
|
|
|
DMU_SET_STREAM_HDRTYPE(drr.drr_u.drr_begin.
|
|
|
|
drr_versioninfo, DMU_COMPOUNDSTREAM);
|
|
|
|
DMU_SET_FEATUREFLAGS(drr.drr_u.drr_begin.
|
|
|
|
drr_versioninfo, featureflags);
|
2017-06-28 17:05:16 +00:00
|
|
|
if (snprintf(drr.drr_u.drr_begin.drr_toname,
|
2011-11-17 18:14:36 +00:00
|
|
|
sizeof (drr.drr_u.drr_begin.drr_toname),
|
2017-06-28 17:05:16 +00:00
|
|
|
"%s@%s", zhp->zfs_name, tosnap) >=
|
|
|
|
sizeof (drr.drr_u.drr_begin.drr_toname)) {
|
|
|
|
err = EINVAL;
|
|
|
|
goto stderr_out;
|
|
|
|
}
|
2011-11-17 18:14:36 +00:00
|
|
|
drr.drr_payloadlen = buflen;
|
|
|
|
|
2015-07-06 03:20:31 +00:00
|
|
|
err = dump_record(&drr, packbuf, buflen, &zc, outfd);
|
2011-11-17 18:14:36 +00:00
|
|
|
free(packbuf);
|
2015-07-06 03:20:31 +00:00
|
|
|
if (err != 0)
|
2011-11-17 18:14:36 +00:00
|
|
|
goto stderr_out;
|
2008-11-20 20:01:55 +00:00
|
|
|
|
2011-11-17 18:14:36 +00:00
|
|
|
/* write end record */
|
2008-11-20 20:01:55 +00:00
|
|
|
bzero(&drr, sizeof (drr));
|
|
|
|
drr.drr_type = DRR_END;
|
|
|
|
drr.drr_u.drr_end.drr_checksum = zc;
|
|
|
|
err = write(outfd, &drr, sizeof (drr));
|
|
|
|
if (err == -1) {
|
2010-05-28 20:45:14 +00:00
|
|
|
err = errno;
|
|
|
|
goto stderr_out;
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
2011-11-17 18:14:36 +00:00
|
|
|
|
|
|
|
err = 0;
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* dump each stream */
|
|
|
|
sdd.fromsnap = fromsnap;
|
|
|
|
sdd.tosnap = tosnap;
|
2013-05-25 02:06:23 +00:00
|
|
|
if (tid != 0)
|
2010-05-28 20:45:14 +00:00
|
|
|
sdd.outfd = pipefd[0];
|
|
|
|
else
|
|
|
|
sdd.outfd = outfd;
|
2011-11-17 18:14:36 +00:00
|
|
|
sdd.replicate = flags->replicate;
|
|
|
|
sdd.doall = flags->doall;
|
|
|
|
sdd.fromorigin = flags->fromorigin;
|
2008-11-20 20:01:55 +00:00
|
|
|
sdd.fss = fss;
|
|
|
|
sdd.fsavl = fsavl;
|
2011-11-17 18:14:36 +00:00
|
|
|
sdd.verbose = flags->verbose;
|
|
|
|
sdd.parsable = flags->parsable;
|
2012-05-09 22:05:14 +00:00
|
|
|
sdd.progress = flags->progress;
|
2011-11-17 18:14:36 +00:00
|
|
|
sdd.dryrun = flags->dryrun;
|
2014-11-03 20:15:08 +00:00
|
|
|
sdd.large_block = flags->largeblock;
|
2014-06-05 21:19:08 +00:00
|
|
|
sdd.embed_data = flags->embed_data;
|
2016-07-11 17:45:52 +00:00
|
|
|
sdd.compress = flags->compress;
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
sdd.raw = flags->raw;
|
2019-02-15 20:41:38 +00:00
|
|
|
sdd.holds = flags->holds;
|
2010-05-28 20:45:14 +00:00
|
|
|
sdd.filter_cb = filter_func;
|
|
|
|
sdd.filter_cb_arg = cb_arg;
|
|
|
|
if (debugnvp)
|
|
|
|
sdd.debugnv = *debugnvp;
|
2015-07-10 23:54:52 +00:00
|
|
|
if (sdd.verbose && sdd.dryrun)
|
|
|
|
sdd.std_out = B_TRUE;
|
|
|
|
fout = sdd.std_out ? stdout : stderr;
|
2012-07-12 12:32:45 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Some flags require that we place user holds on the datasets that are
|
|
|
|
* being sent so they don't get destroyed during the send. We can skip
|
|
|
|
* this step if the pool is imported read-only since the datasets cannot
|
|
|
|
* be destroyed.
|
|
|
|
*/
|
|
|
|
if (!flags->dryrun && !zpool_get_prop_int(zfs_get_pool_handle(zhp),
|
|
|
|
ZPOOL_PROP_READONLY, NULL) &&
|
|
|
|
zfs_spa_version(zhp, &spa_version) == 0 &&
|
|
|
|
spa_version >= SPA_VERSION_USERREFS &&
|
|
|
|
(flags->doall || flags->replicate)) {
|
2010-08-26 21:24:34 +00:00
|
|
|
++holdseq;
|
|
|
|
(void) snprintf(sdd.holdtag, sizeof (sdd.holdtag),
|
|
|
|
".send-%d-%llu", getpid(), (u_longlong_t)holdseq);
|
2010-08-26 18:44:39 +00:00
|
|
|
sdd.cleanup_fd = open(ZFS_DEV, O_RDWR);
|
2010-08-26 21:24:34 +00:00
|
|
|
if (sdd.cleanup_fd < 0) {
|
|
|
|
err = errno;
|
|
|
|
goto stderr_out;
|
|
|
|
}
|
2013-05-25 02:06:23 +00:00
|
|
|
sdd.snapholds = fnvlist_alloc();
|
2010-08-26 21:24:34 +00:00
|
|
|
} else {
|
|
|
|
sdd.cleanup_fd = -1;
|
2013-05-25 02:06:23 +00:00
|
|
|
sdd.snapholds = NULL;
|
2010-08-26 21:24:34 +00:00
|
|
|
}
|
2019-02-15 20:41:38 +00:00
|
|
|
|
2013-05-25 02:06:23 +00:00
|
|
|
if (flags->verbose || sdd.snapholds != NULL) {
|
2011-11-17 18:14:36 +00:00
|
|
|
/*
|
|
|
|
* Do a verbose no-op dry run to get all the verbose output
|
2013-05-25 02:06:23 +00:00
|
|
|
* or to gather snapshot hold's before generating any data,
|
|
|
|
* then do a non-verbose real run to generate the streams.
|
2011-11-17 18:14:36 +00:00
|
|
|
*/
|
|
|
|
sdd.dryrun = B_TRUE;
|
|
|
|
err = dump_filesystems(zhp, &sdd);
|
2013-05-25 02:06:23 +00:00
|
|
|
|
|
|
|
if (err != 0)
|
|
|
|
goto stderr_out;
|
|
|
|
|
|
|
|
if (flags->verbose) {
|
|
|
|
if (flags->parsable) {
|
2015-07-10 23:54:52 +00:00
|
|
|
(void) fprintf(fout, "size\t%llu\n",
|
2013-05-25 02:06:23 +00:00
|
|
|
(longlong_t)sdd.size);
|
|
|
|
} else {
|
|
|
|
char buf[16];
|
2017-05-02 20:43:53 +00:00
|
|
|
zfs_nicebytes(sdd.size, buf, sizeof (buf));
|
2015-07-10 23:54:52 +00:00
|
|
|
(void) fprintf(fout, dgettext(TEXT_DOMAIN,
|
2013-05-25 02:06:23 +00:00
|
|
|
"total estimated size is %s\n"), buf);
|
|
|
|
}
|
2011-11-17 18:14:36 +00:00
|
|
|
}
|
2013-05-25 02:06:23 +00:00
|
|
|
|
|
|
|
/* Ensure no snaps found is treated as an error. */
|
|
|
|
if (!sdd.seento) {
|
|
|
|
err = ENOENT;
|
|
|
|
goto err_out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Skip the second run if dryrun was requested. */
|
|
|
|
if (flags->dryrun)
|
|
|
|
goto err_out;
|
|
|
|
|
|
|
|
if (sdd.snapholds != NULL) {
|
|
|
|
err = zfs_hold_nvl(zhp, sdd.cleanup_fd, sdd.snapholds);
|
|
|
|
if (err != 0)
|
|
|
|
goto stderr_out;
|
|
|
|
|
|
|
|
fnvlist_free(sdd.snapholds);
|
|
|
|
sdd.snapholds = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
sdd.dryrun = B_FALSE;
|
|
|
|
sdd.verbose = B_FALSE;
|
2011-11-17 18:14:36 +00:00
|
|
|
}
|
2013-05-25 02:06:23 +00:00
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
err = dump_filesystems(zhp, &sdd);
|
|
|
|
fsavl_destroy(fsavl);
|
|
|
|
nvlist_free(fss);
|
|
|
|
|
2013-05-25 02:06:23 +00:00
|
|
|
/* Ensure no snaps found is treated as an error. */
|
|
|
|
if (err == 0 && !sdd.seento)
|
|
|
|
err = ENOENT;
|
|
|
|
|
|
|
|
if (tid != 0) {
|
|
|
|
if (err != 0)
|
|
|
|
(void) pthread_cancel(tid);
|
|
|
|
(void) close(pipefd[0]);
|
2013-07-29 19:36:31 +00:00
|
|
|
(void) pthread_join(tid, NULL);
|
2010-05-28 20:45:14 +00:00
|
|
|
}
|
|
|
|
|
2010-08-26 21:24:34 +00:00
|
|
|
if (sdd.cleanup_fd != -1) {
|
|
|
|
VERIFY(0 == close(sdd.cleanup_fd));
|
|
|
|
sdd.cleanup_fd = -1;
|
|
|
|
}
|
|
|
|
|
2011-11-17 18:14:36 +00:00
|
|
|
if (!flags->dryrun && (flags->replicate || flags->doall ||
|
2019-02-15 20:41:38 +00:00
|
|
|
flags->props || flags->backup || flags->holds)) {
|
2008-11-20 20:01:55 +00:00
|
|
|
/*
|
|
|
|
* write final end record. NB: want to do this even if
|
|
|
|
* there was some error, because it might not be totally
|
|
|
|
* failed.
|
|
|
|
*/
|
|
|
|
dmu_replay_record_t drr = { 0 };
|
|
|
|
drr.drr_type = DRR_END;
|
|
|
|
if (write(outfd, &drr, sizeof (drr)) == -1) {
|
|
|
|
return (zfs_standard_error(zhp->zfs_hdl,
|
|
|
|
errno, errbuf));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (err || sdd.err);
|
2010-05-28 20:45:14 +00:00
|
|
|
|
|
|
|
stderr_out:
|
|
|
|
err = zfs_standard_error(zhp->zfs_hdl, err, errbuf);
|
|
|
|
err_out:
|
2013-05-25 02:06:23 +00:00
|
|
|
fsavl_destroy(fsavl);
|
|
|
|
nvlist_free(fss);
|
|
|
|
fnvlist_free(sdd.snapholds);
|
|
|
|
|
2010-08-26 21:24:34 +00:00
|
|
|
if (sdd.cleanup_fd != -1)
|
|
|
|
VERIFY(0 == close(sdd.cleanup_fd));
|
2013-05-25 02:06:23 +00:00
|
|
|
if (tid != 0) {
|
2010-05-28 20:45:14 +00:00
|
|
|
(void) pthread_cancel(tid);
|
|
|
|
(void) close(pipefd[0]);
|
2013-07-29 19:36:31 +00:00
|
|
|
(void) pthread_join(tid, NULL);
|
2010-05-28 20:45:14 +00:00
|
|
|
}
|
|
|
|
return (err);
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
2013-12-11 22:33:41 +00:00
|
|
|
int
|
2017-09-08 22:24:31 +00:00
|
|
|
zfs_send_one(zfs_handle_t *zhp, const char *from, int fd, sendflags_t flags)
|
2013-12-11 22:33:41 +00:00
|
|
|
{
|
2017-09-08 22:24:31 +00:00
|
|
|
int err = 0;
|
2013-12-11 22:33:41 +00:00
|
|
|
libzfs_handle_t *hdl = zhp->zfs_hdl;
|
2017-09-08 22:24:31 +00:00
|
|
|
enum lzc_send_flags lzc_flags = 0;
|
|
|
|
FILE *fout = (flags.verbose && flags.dryrun) ? stdout : stderr;
|
2013-12-11 22:33:41 +00:00
|
|
|
char errbuf[1024];
|
2017-09-08 22:24:31 +00:00
|
|
|
|
|
|
|
if (flags.largeblock)
|
|
|
|
lzc_flags |= LZC_SEND_FLAG_LARGE_BLOCK;
|
|
|
|
if (flags.embed_data)
|
|
|
|
lzc_flags |= LZC_SEND_FLAG_EMBED_DATA;
|
|
|
|
if (flags.compress)
|
|
|
|
lzc_flags |= LZC_SEND_FLAG_COMPRESS;
|
|
|
|
if (flags.raw)
|
|
|
|
lzc_flags |= LZC_SEND_FLAG_RAW;
|
|
|
|
|
|
|
|
if (flags.verbose) {
|
|
|
|
uint64_t size = 0;
|
|
|
|
err = lzc_send_space(zhp->zfs_name, from, lzc_flags, &size);
|
|
|
|
if (err == 0) {
|
|
|
|
send_print_verbose(fout, zhp->zfs_name, from, size,
|
|
|
|
flags.parsable);
|
|
|
|
} else {
|
|
|
|
(void) fprintf(stderr, "Cannot estimate send size: "
|
|
|
|
"%s\n", strerror(errno));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags.dryrun)
|
|
|
|
return (err);
|
|
|
|
|
2013-12-11 22:33:41 +00:00
|
|
|
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
|
|
|
|
"warning: cannot send '%s'"), zhp->zfs_name);
|
|
|
|
|
2017-09-08 22:24:31 +00:00
|
|
|
err = lzc_send(zhp->zfs_name, from, fd, lzc_flags);
|
2013-12-11 22:33:41 +00:00
|
|
|
if (err != 0) {
|
|
|
|
switch (errno) {
|
|
|
|
case EXDEV:
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"not an earlier snapshot from the same fs"));
|
|
|
|
return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
|
|
|
|
|
|
|
|
case ENOENT:
|
|
|
|
case ESRCH:
|
|
|
|
if (lzc_exists(zhp->zfs_name)) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"incremental source (%s) does not exist"),
|
|
|
|
from);
|
|
|
|
}
|
|
|
|
return (zfs_error(hdl, EZFS_NOENT, errbuf));
|
|
|
|
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
case EACCES:
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"dataset key must be loaded"));
|
|
|
|
return (zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf));
|
|
|
|
|
2013-12-11 22:33:41 +00:00
|
|
|
case EBUSY:
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"target is busy; if a filesystem, "
|
|
|
|
"it must not be mounted"));
|
|
|
|
return (zfs_error(hdl, EZFS_BUSY, errbuf));
|
|
|
|
|
|
|
|
case EDQUOT:
|
|
|
|
case EFBIG:
|
|
|
|
case EIO:
|
|
|
|
case ENOLINK:
|
|
|
|
case ENOSPC:
|
|
|
|
case ENOSTR:
|
|
|
|
case ENXIO:
|
|
|
|
case EPIPE:
|
|
|
|
case ERANGE:
|
|
|
|
case EFAULT:
|
|
|
|
case EROFS:
|
|
|
|
zfs_error_aux(hdl, strerror(errno));
|
|
|
|
return (zfs_error(hdl, EZFS_BADBACKUP, errbuf));
|
|
|
|
|
|
|
|
default:
|
|
|
|
return (zfs_standard_error(hdl, errno, errbuf));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (err != 0);
|
|
|
|
}
|
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
/*
|
|
|
|
* Routines specific to "zfs recv"
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int
|
|
|
|
recv_read(libzfs_handle_t *hdl, int fd, void *buf, int ilen,
|
|
|
|
boolean_t byteswap, zio_cksum_t *zc)
|
|
|
|
{
|
|
|
|
char *cp = buf;
|
|
|
|
int rv;
|
|
|
|
int len = ilen;
|
|
|
|
|
2015-07-06 03:20:31 +00:00
|
|
|
assert(ilen <= SPA_MAXBLOCKSIZE);
|
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
do {
|
|
|
|
rv = read(fd, cp, len);
|
|
|
|
cp += rv;
|
|
|
|
len -= rv;
|
|
|
|
} while (rv > 0);
|
|
|
|
|
|
|
|
if (rv < 0 || len != 0) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"failed to read from stream"));
|
|
|
|
return (zfs_error(hdl, EZFS_BADSTREAM, dgettext(TEXT_DOMAIN,
|
|
|
|
"cannot receive")));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (zc) {
|
|
|
|
if (byteswap)
|
|
|
|
fletcher_4_incremental_byteswap(buf, ilen, zc);
|
|
|
|
else
|
|
|
|
fletcher_4_incremental_native(buf, ilen, zc);
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
recv_read_nvlist(libzfs_handle_t *hdl, int fd, int len, nvlist_t **nvp,
|
|
|
|
boolean_t byteswap, zio_cksum_t *zc)
|
|
|
|
{
|
|
|
|
char *buf;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
buf = zfs_alloc(hdl, len);
|
|
|
|
if (buf == NULL)
|
|
|
|
return (ENOMEM);
|
|
|
|
|
|
|
|
err = recv_read(hdl, fd, buf, len, byteswap, zc);
|
|
|
|
if (err != 0) {
|
|
|
|
free(buf);
|
|
|
|
return (err);
|
|
|
|
}
|
|
|
|
|
|
|
|
err = nvlist_unpack(buf, len, nvp, 0);
|
|
|
|
free(buf);
|
|
|
|
if (err != 0) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
|
|
|
|
"stream (malformed nvlist)"));
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
/*
|
|
|
|
* Returns the grand origin (origin of origin of origin...) of a given handle.
|
|
|
|
* If this dataset is not a clone, it simply returns a copy of the original
|
|
|
|
* handle.
|
|
|
|
*/
|
|
|
|
static zfs_handle_t *
|
|
|
|
recv_open_grand_origin(zfs_handle_t *zhp)
|
|
|
|
{
|
|
|
|
char origin[ZFS_MAX_DATASET_NAME_LEN];
|
|
|
|
zprop_source_t src;
|
|
|
|
zfs_handle_t *ozhp = zfs_handle_dup(zhp);
|
|
|
|
|
|
|
|
while (ozhp != NULL) {
|
|
|
|
if (zfs_prop_get(ozhp, ZFS_PROP_ORIGIN, origin,
|
|
|
|
sizeof (origin), &src, NULL, 0, B_FALSE) != 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
(void) zfs_close(ozhp);
|
|
|
|
ozhp = zfs_open(zhp->zfs_hdl, origin, ZFS_TYPE_FILESYSTEM);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (ozhp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2018-06-27 21:37:54 +00:00
|
|
|
recv_rename_impl(zfs_handle_t *zhp, const char *name, const char *newname)
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
{
|
|
|
|
int err;
|
|
|
|
zfs_handle_t *ozhp = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Attempt to rename the dataset. If it fails with EACCES we have
|
|
|
|
* attempted to rename the dataset outside of its encryption root.
|
|
|
|
* Force the dataset to become an encryption root and try again.
|
|
|
|
*/
|
2018-06-27 21:37:54 +00:00
|
|
|
err = lzc_rename(name, newname);
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
if (err == EACCES) {
|
|
|
|
ozhp = recv_open_grand_origin(zhp);
|
|
|
|
if (ozhp == NULL) {
|
|
|
|
err = ENOENT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = lzc_change_key(ozhp->zfs_name, DCP_CMD_FORCE_NEW_KEY,
|
|
|
|
NULL, NULL, 0);
|
|
|
|
if (err != 0)
|
|
|
|
goto out;
|
|
|
|
|
2018-06-27 21:37:54 +00:00
|
|
|
err = lzc_rename(name, newname);
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
if (ozhp != NULL)
|
|
|
|
zfs_close(ozhp);
|
|
|
|
return (err);
|
|
|
|
}
|
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
static int
|
|
|
|
recv_rename(libzfs_handle_t *hdl, const char *name, const char *tryname,
|
2011-11-17 18:14:36 +00:00
|
|
|
int baselen, char *newname, recvflags_t *flags)
|
2008-11-20 20:01:55 +00:00
|
|
|
{
|
|
|
|
static int seq;
|
|
|
|
int err;
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
prop_changelist_t *clp = NULL;
|
|
|
|
zfs_handle_t *zhp = NULL;
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
zhp = zfs_open(hdl, name, ZFS_TYPE_DATASET);
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
if (zhp == NULL) {
|
|
|
|
err = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
2008-12-03 20:09:06 +00:00
|
|
|
clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
|
2011-11-17 18:14:36 +00:00
|
|
|
flags->force ? MS_FORCE : 0);
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
if (clp == NULL) {
|
|
|
|
err = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
2008-11-20 20:01:55 +00:00
|
|
|
err = changelist_prefix(clp);
|
|
|
|
if (err)
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
goto out;
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
if (tryname) {
|
|
|
|
(void) strcpy(newname, tryname);
|
2011-11-17 18:14:36 +00:00
|
|
|
if (flags->verbose) {
|
2008-11-20 20:01:55 +00:00
|
|
|
(void) printf("attempting rename %s to %s\n",
|
2018-06-27 21:37:54 +00:00
|
|
|
name, newname);
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
2018-06-27 21:37:54 +00:00
|
|
|
err = recv_rename_impl(zhp, name, newname);
|
2008-11-20 20:01:55 +00:00
|
|
|
if (err == 0)
|
|
|
|
changelist_rename(clp, name, tryname);
|
|
|
|
} else {
|
|
|
|
err = ENOENT;
|
|
|
|
}
|
|
|
|
|
2013-09-04 12:00:57 +00:00
|
|
|
if (err != 0 && strncmp(name + baselen, "recv-", 5) != 0) {
|
2008-11-20 20:01:55 +00:00
|
|
|
seq++;
|
|
|
|
|
2016-06-15 21:28:36 +00:00
|
|
|
(void) snprintf(newname, ZFS_MAX_DATASET_NAME_LEN,
|
|
|
|
"%.*srecv-%u-%u", baselen, name, getpid(), seq);
|
2008-11-20 20:01:55 +00:00
|
|
|
|
2011-11-17 18:14:36 +00:00
|
|
|
if (flags->verbose) {
|
2008-11-20 20:01:55 +00:00
|
|
|
(void) printf("failed - trying rename %s to %s\n",
|
2018-06-27 21:37:54 +00:00
|
|
|
name, newname);
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
2018-06-27 21:37:54 +00:00
|
|
|
err = recv_rename_impl(zhp, name, newname);
|
2008-11-20 20:01:55 +00:00
|
|
|
if (err == 0)
|
|
|
|
changelist_rename(clp, name, newname);
|
2011-11-17 18:14:36 +00:00
|
|
|
if (err && flags->verbose) {
|
2008-11-20 20:01:55 +00:00
|
|
|
(void) printf("failed (%u) - "
|
|
|
|
"will try again on next pass\n", errno);
|
|
|
|
}
|
|
|
|
err = EAGAIN;
|
2011-11-17 18:14:36 +00:00
|
|
|
} else if (flags->verbose) {
|
2008-11-20 20:01:55 +00:00
|
|
|
if (err == 0)
|
|
|
|
(void) printf("success\n");
|
|
|
|
else
|
|
|
|
(void) printf("failed (%u)\n", errno);
|
|
|
|
}
|
|
|
|
|
|
|
|
(void) changelist_postfix(clp);
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
|
|
|
|
out:
|
|
|
|
if (clp != NULL)
|
|
|
|
changelist_free(clp);
|
|
|
|
if (zhp != NULL)
|
|
|
|
zfs_close(zhp);
|
|
|
|
|
|
|
|
return (err);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
recv_promote(libzfs_handle_t *hdl, const char *fsname,
|
|
|
|
const char *origin_fsname, recvflags_t *flags)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
zfs_cmd_t zc = {"\0"};
|
|
|
|
zfs_handle_t *zhp = NULL, *ozhp = NULL;
|
|
|
|
|
|
|
|
if (flags->verbose)
|
|
|
|
(void) printf("promoting %s\n", fsname);
|
|
|
|
|
|
|
|
(void) strlcpy(zc.zc_value, origin_fsname, sizeof (zc.zc_value));
|
|
|
|
(void) strlcpy(zc.zc_name, fsname, sizeof (zc.zc_name));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Attempt to promote the dataset. If it fails with EACCES the
|
|
|
|
* promotion would cause this dataset to leave its encryption root.
|
|
|
|
* Force the origin to become an encryption root and try again.
|
|
|
|
*/
|
|
|
|
err = zfs_ioctl(hdl, ZFS_IOC_PROMOTE, &zc);
|
|
|
|
if (err == EACCES) {
|
|
|
|
zhp = zfs_open(hdl, fsname, ZFS_TYPE_DATASET);
|
|
|
|
if (zhp == NULL) {
|
|
|
|
err = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ozhp = recv_open_grand_origin(zhp);
|
|
|
|
if (ozhp == NULL) {
|
|
|
|
err = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = lzc_change_key(ozhp->zfs_name, DCP_CMD_FORCE_NEW_KEY,
|
|
|
|
NULL, NULL, 0);
|
|
|
|
if (err != 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
err = zfs_ioctl(hdl, ZFS_IOC_PROMOTE, &zc);
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
if (zhp != NULL)
|
|
|
|
zfs_close(zhp);
|
|
|
|
if (ozhp != NULL)
|
|
|
|
zfs_close(ozhp);
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
return (err);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
recv_destroy(libzfs_handle_t *hdl, const char *name, int baselen,
|
2011-11-17 18:14:36 +00:00
|
|
|
char *newname, recvflags_t *flags)
|
2008-11-20 20:01:55 +00:00
|
|
|
{
|
|
|
|
int err = 0;
|
|
|
|
prop_changelist_t *clp;
|
|
|
|
zfs_handle_t *zhp;
|
2009-08-18 18:43:27 +00:00
|
|
|
boolean_t defer = B_FALSE;
|
|
|
|
int spa_version;
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
zhp = zfs_open(hdl, name, ZFS_TYPE_DATASET);
|
|
|
|
if (zhp == NULL)
|
|
|
|
return (-1);
|
2008-12-03 20:09:06 +00:00
|
|
|
clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
|
2011-11-17 18:14:36 +00:00
|
|
|
flags->force ? MS_FORCE : 0);
|
2009-08-18 18:43:27 +00:00
|
|
|
if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT &&
|
|
|
|
zfs_spa_version(zhp, &spa_version) == 0 &&
|
|
|
|
spa_version >= SPA_VERSION_USERREFS)
|
|
|
|
defer = B_TRUE;
|
2008-11-20 20:01:55 +00:00
|
|
|
zfs_close(zhp);
|
|
|
|
if (clp == NULL)
|
|
|
|
return (-1);
|
|
|
|
err = changelist_prefix(clp);
|
|
|
|
if (err)
|
|
|
|
return (err);
|
|
|
|
|
2011-11-17 18:14:36 +00:00
|
|
|
if (flags->verbose)
|
2018-06-27 21:37:54 +00:00
|
|
|
(void) printf("attempting destroy %s\n", name);
|
|
|
|
if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
|
|
|
|
nvlist_t *nv = fnvlist_alloc();
|
|
|
|
fnvlist_add_boolean(nv, name);
|
|
|
|
err = lzc_destroy_snaps(nv, defer, NULL);
|
|
|
|
fnvlist_free(nv);
|
|
|
|
} else {
|
|
|
|
err = lzc_destroy(name);
|
|
|
|
}
|
2008-11-20 20:01:55 +00:00
|
|
|
if (err == 0) {
|
2011-11-17 18:14:36 +00:00
|
|
|
if (flags->verbose)
|
2008-11-20 20:01:55 +00:00
|
|
|
(void) printf("success\n");
|
2018-06-27 21:37:54 +00:00
|
|
|
changelist_remove(clp, name);
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
(void) changelist_postfix(clp);
|
|
|
|
changelist_free(clp);
|
|
|
|
|
2009-08-18 18:43:27 +00:00
|
|
|
/*
|
2010-05-28 20:45:14 +00:00
|
|
|
* Deferred destroy might destroy the snapshot or only mark it to be
|
|
|
|
* destroyed later, and it returns success in either case.
|
2009-08-18 18:43:27 +00:00
|
|
|
*/
|
2010-05-28 20:45:14 +00:00
|
|
|
if (err != 0 || (defer && zfs_dataset_exists(hdl, name,
|
|
|
|
ZFS_TYPE_SNAPSHOT))) {
|
2008-11-20 20:01:55 +00:00
|
|
|
err = recv_rename(hdl, name, NULL, baselen, newname, flags);
|
2010-05-28 20:45:14 +00:00
|
|
|
}
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
return (err);
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct guid_to_name_data {
|
|
|
|
uint64_t guid;
|
2016-01-06 21:22:48 +00:00
|
|
|
boolean_t bookmark_ok;
|
2008-11-20 20:01:55 +00:00
|
|
|
char *name;
|
2011-11-17 18:14:36 +00:00
|
|
|
char *skip;
|
2008-11-20 20:01:55 +00:00
|
|
|
} guid_to_name_data_t;
|
|
|
|
|
|
|
|
static int
|
|
|
|
guid_to_name_cb(zfs_handle_t *zhp, void *arg)
|
|
|
|
{
|
|
|
|
guid_to_name_data_t *gtnd = arg;
|
2016-01-06 21:22:48 +00:00
|
|
|
const char *slash;
|
2008-11-20 20:01:55 +00:00
|
|
|
int err;
|
|
|
|
|
2011-11-17 18:14:36 +00:00
|
|
|
if (gtnd->skip != NULL &&
|
2016-01-06 21:22:48 +00:00
|
|
|
(slash = strrchr(zhp->zfs_name, '/')) != NULL &&
|
|
|
|
strcmp(slash + 1, gtnd->skip) == 0) {
|
|
|
|
zfs_close(zhp);
|
2011-11-17 18:14:36 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2016-01-06 21:22:48 +00:00
|
|
|
if (zfs_prop_get_int(zhp, ZFS_PROP_GUID) == gtnd->guid) {
|
2008-11-20 20:01:55 +00:00
|
|
|
(void) strcpy(gtnd->name, zhp->zfs_name);
|
2010-05-28 20:45:14 +00:00
|
|
|
zfs_close(zhp);
|
2008-11-20 20:01:55 +00:00
|
|
|
return (EEXIST);
|
|
|
|
}
|
2011-11-17 18:14:36 +00:00
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
err = zfs_iter_children(zhp, guid_to_name_cb, gtnd);
|
2016-01-06 21:22:48 +00:00
|
|
|
if (err != EEXIST && gtnd->bookmark_ok)
|
|
|
|
err = zfs_iter_bookmarks(zhp, guid_to_name_cb, gtnd);
|
2008-11-20 20:01:55 +00:00
|
|
|
zfs_close(zhp);
|
|
|
|
return (err);
|
|
|
|
}
|
|
|
|
|
2011-11-17 18:14:36 +00:00
|
|
|
/*
|
|
|
|
* Attempt to find the local dataset associated with this guid. In the case of
|
|
|
|
* multiple matches, we attempt to find the "best" match by searching
|
|
|
|
* progressively larger portions of the hierarchy. This allows one to send a
|
|
|
|
* tree of datasets individually and guarantee that we will find the source
|
|
|
|
* guid within that hierarchy, even if there are multiple matches elsewhere.
|
|
|
|
*/
|
2008-11-20 20:01:55 +00:00
|
|
|
static int
|
|
|
|
guid_to_name(libzfs_handle_t *hdl, const char *parent, uint64_t guid,
|
2016-01-06 21:22:48 +00:00
|
|
|
boolean_t bookmark_ok, char *name)
|
2008-11-20 20:01:55 +00:00
|
|
|
{
|
2016-06-15 21:28:36 +00:00
|
|
|
char pname[ZFS_MAX_DATASET_NAME_LEN];
|
2008-11-20 20:01:55 +00:00
|
|
|
guid_to_name_data_t gtnd;
|
|
|
|
|
|
|
|
gtnd.guid = guid;
|
2016-01-06 21:22:48 +00:00
|
|
|
gtnd.bookmark_ok = bookmark_ok;
|
2008-11-20 20:01:55 +00:00
|
|
|
gtnd.name = name;
|
2011-11-17 18:14:36 +00:00
|
|
|
gtnd.skip = NULL;
|
2008-11-20 20:01:55 +00:00
|
|
|
|
2011-11-17 18:14:36 +00:00
|
|
|
/*
|
2016-01-06 21:22:48 +00:00
|
|
|
* Search progressively larger portions of the hierarchy, starting
|
|
|
|
* with the filesystem specified by 'parent'. This will
|
2011-11-17 18:14:36 +00:00
|
|
|
* select the "most local" version of the origin snapshot in the case
|
|
|
|
* that there are multiple matching snapshots in the system.
|
|
|
|
*/
|
2016-01-06 21:22:48 +00:00
|
|
|
(void) strlcpy(pname, parent, sizeof (pname));
|
|
|
|
char *cp = strrchr(pname, '@');
|
|
|
|
if (cp == NULL)
|
|
|
|
cp = strchr(pname, '\0');
|
|
|
|
for (; cp != NULL; cp = strrchr(pname, '/')) {
|
2011-11-17 18:14:36 +00:00
|
|
|
/* Chop off the last component and open the parent */
|
2008-11-20 20:01:55 +00:00
|
|
|
*cp = '\0';
|
2016-01-06 21:22:48 +00:00
|
|
|
zfs_handle_t *zhp = make_dataset_handle(hdl, pname);
|
2011-11-17 18:14:36 +00:00
|
|
|
|
|
|
|
if (zhp == NULL)
|
|
|
|
continue;
|
2016-01-06 21:22:48 +00:00
|
|
|
int err = guid_to_name_cb(zfs_handle_dup(zhp), >nd);
|
|
|
|
if (err != EEXIST)
|
|
|
|
err = zfs_iter_children(zhp, guid_to_name_cb, >nd);
|
|
|
|
if (err != EEXIST && bookmark_ok)
|
|
|
|
err = zfs_iter_bookmarks(zhp, guid_to_name_cb, >nd);
|
2008-11-20 20:01:55 +00:00
|
|
|
zfs_close(zhp);
|
2011-11-17 18:14:36 +00:00
|
|
|
if (err == EEXIST)
|
|
|
|
return (0);
|
2008-11-20 20:01:55 +00:00
|
|
|
|
2011-11-17 18:14:36 +00:00
|
|
|
/*
|
2016-01-06 21:22:48 +00:00
|
|
|
* Remember the last portion of the dataset so we skip it next
|
|
|
|
* time through (as we've already searched that portion of the
|
|
|
|
* hierarchy).
|
2011-11-17 18:14:36 +00:00
|
|
|
*/
|
2016-01-06 21:22:48 +00:00
|
|
|
gtnd.skip = strrchr(pname, '/') + 1;
|
2011-11-17 18:14:36 +00:00
|
|
|
}
|
2008-11-20 20:01:55 +00:00
|
|
|
|
2011-11-17 18:14:36 +00:00
|
|
|
return (ENOENT);
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2011-11-17 18:14:36 +00:00
|
|
|
* Return +1 if guid1 is before guid2, 0 if they are the same, and -1 if
|
|
|
|
* guid1 is after guid2.
|
2008-11-20 20:01:55 +00:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
created_before(libzfs_handle_t *hdl, avl_tree_t *avl,
|
|
|
|
uint64_t guid1, uint64_t guid2)
|
|
|
|
{
|
|
|
|
nvlist_t *nvfs;
|
2015-11-09 20:10:02 +00:00
|
|
|
char *fsname = NULL, *snapname = NULL;
|
2016-06-15 21:28:36 +00:00
|
|
|
char buf[ZFS_MAX_DATASET_NAME_LEN];
|
2008-11-20 20:01:55 +00:00
|
|
|
int rv;
|
2011-11-17 18:14:36 +00:00
|
|
|
zfs_handle_t *guid1hdl, *guid2hdl;
|
|
|
|
uint64_t create1, create2;
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
if (guid2 == 0)
|
|
|
|
return (0);
|
|
|
|
if (guid1 == 0)
|
|
|
|
return (1);
|
|
|
|
|
|
|
|
nvfs = fsavl_find(avl, guid1, &snapname);
|
|
|
|
VERIFY(0 == nvlist_lookup_string(nvfs, "name", &fsname));
|
|
|
|
(void) snprintf(buf, sizeof (buf), "%s@%s", fsname, snapname);
|
2011-11-17 18:14:36 +00:00
|
|
|
guid1hdl = zfs_open(hdl, buf, ZFS_TYPE_SNAPSHOT);
|
|
|
|
if (guid1hdl == NULL)
|
2008-11-20 20:01:55 +00:00
|
|
|
return (-1);
|
|
|
|
|
|
|
|
nvfs = fsavl_find(avl, guid2, &snapname);
|
|
|
|
VERIFY(0 == nvlist_lookup_string(nvfs, "name", &fsname));
|
|
|
|
(void) snprintf(buf, sizeof (buf), "%s@%s", fsname, snapname);
|
2011-11-17 18:14:36 +00:00
|
|
|
guid2hdl = zfs_open(hdl, buf, ZFS_TYPE_SNAPSHOT);
|
|
|
|
if (guid2hdl == NULL) {
|
|
|
|
zfs_close(guid1hdl);
|
2008-11-20 20:01:55 +00:00
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
2011-11-17 18:14:36 +00:00
|
|
|
create1 = zfs_prop_get_int(guid1hdl, ZFS_PROP_CREATETXG);
|
|
|
|
create2 = zfs_prop_get_int(guid2hdl, ZFS_PROP_CREATETXG);
|
2008-11-20 20:01:55 +00:00
|
|
|
|
2011-11-17 18:14:36 +00:00
|
|
|
if (create1 < create2)
|
|
|
|
rv = -1;
|
|
|
|
else if (create1 > create2)
|
|
|
|
rv = +1;
|
|
|
|
else
|
|
|
|
rv = 0;
|
|
|
|
|
|
|
|
zfs_close(guid1hdl);
|
|
|
|
zfs_close(guid2hdl);
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
return (rv);
|
|
|
|
}
|
|
|
|
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
/*
|
|
|
|
* This function reestablishes the heirarchy of encryption roots after a
|
|
|
|
* recursive incremental receive has completed. This must be done after the
|
|
|
|
* second call to recv_incremental_replication() has renamed and promoted all
|
|
|
|
* sent datasets to their final locations in the dataset heriarchy.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
recv_fix_encryption_heirarchy(libzfs_handle_t *hdl, const char *destname,
|
|
|
|
nvlist_t *stream_nv, avl_tree_t *stream_avl)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
nvpair_t *fselem = NULL;
|
|
|
|
nvlist_t *stream_fss;
|
2017-09-12 20:15:11 +00:00
|
|
|
char *cp;
|
|
|
|
char top_zfs[ZFS_MAX_DATASET_NAME_LEN];
|
|
|
|
|
|
|
|
(void) strcpy(top_zfs, destname);
|
|
|
|
cp = strrchr(top_zfs, '@');
|
|
|
|
if (cp != NULL)
|
|
|
|
*cp = '\0';
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
|
|
|
|
VERIFY(0 == nvlist_lookup_nvlist(stream_nv, "fss", &stream_fss));
|
|
|
|
|
|
|
|
while ((fselem = nvlist_next_nvpair(stream_fss, fselem)) != NULL) {
|
|
|
|
zfs_handle_t *zhp = NULL;
|
|
|
|
uint64_t crypt;
|
|
|
|
nvlist_t *snaps, *props, *stream_nvfs = NULL;
|
|
|
|
nvpair_t *snapel = NULL;
|
|
|
|
boolean_t is_encroot, is_clone, stream_encroot;
|
|
|
|
char *cp;
|
|
|
|
char *stream_keylocation = NULL;
|
|
|
|
char keylocation[MAXNAMELEN];
|
|
|
|
char fsname[ZFS_MAX_DATASET_NAME_LEN];
|
|
|
|
|
|
|
|
keylocation[0] = '\0';
|
|
|
|
VERIFY(0 == nvpair_value_nvlist(fselem, &stream_nvfs));
|
|
|
|
VERIFY(0 == nvlist_lookup_nvlist(stream_nvfs, "snaps", &snaps));
|
|
|
|
VERIFY(0 == nvlist_lookup_nvlist(stream_nvfs, "props", &props));
|
|
|
|
stream_encroot = nvlist_exists(stream_nvfs, "is_encroot");
|
|
|
|
|
|
|
|
/* find a snapshot from the stream that exists locally */
|
|
|
|
err = ENOENT;
|
|
|
|
while ((snapel = nvlist_next_nvpair(snaps, snapel)) != NULL) {
|
|
|
|
uint64_t guid;
|
|
|
|
|
|
|
|
VERIFY(0 == nvpair_value_uint64(snapel, &guid));
|
|
|
|
err = guid_to_name(hdl, destname, guid, B_FALSE,
|
|
|
|
fsname);
|
|
|
|
if (err == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err != 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
cp = strchr(fsname, '@');
|
|
|
|
if (cp != NULL)
|
|
|
|
*cp = '\0';
|
|
|
|
|
|
|
|
zhp = zfs_open(hdl, fsname, ZFS_TYPE_DATASET);
|
|
|
|
if (zhp == NULL) {
|
|
|
|
err = ENOENT;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
crypt = zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION);
|
|
|
|
is_clone = zhp->zfs_dmustats.dds_origin[0] != '\0';
|
|
|
|
(void) zfs_crypto_get_encryption_root(zhp, &is_encroot, NULL);
|
|
|
|
|
|
|
|
/* we don't need to do anything for unencrypted filesystems */
|
|
|
|
if (crypt == ZIO_CRYPT_OFF) {
|
|
|
|
zfs_close(zhp);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the dataset is flagged as an encryption root, was not
|
|
|
|
* received as a clone and is not currently an encryption root,
|
|
|
|
* force it to become one. Fixup the keylocation if necessary.
|
|
|
|
*/
|
|
|
|
if (stream_encroot) {
|
|
|
|
if (!is_clone && !is_encroot) {
|
|
|
|
err = lzc_change_key(fsname,
|
|
|
|
DCP_CMD_FORCE_NEW_KEY, NULL, NULL, 0);
|
|
|
|
if (err != 0) {
|
|
|
|
zfs_close(zhp);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VERIFY(0 == nvlist_lookup_string(props,
|
|
|
|
zfs_prop_to_name(ZFS_PROP_KEYLOCATION),
|
|
|
|
&stream_keylocation));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Refresh the properties in case the call to
|
|
|
|
* lzc_change_key() changed the value.
|
|
|
|
*/
|
|
|
|
zfs_refresh_properties(zhp);
|
|
|
|
err = zfs_prop_get(zhp, ZFS_PROP_KEYLOCATION,
|
|
|
|
keylocation, sizeof (keylocation), NULL, NULL,
|
|
|
|
0, B_TRUE);
|
|
|
|
if (err != 0) {
|
|
|
|
zfs_close(zhp);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcmp(keylocation, stream_keylocation) != 0) {
|
|
|
|
err = zfs_prop_set(zhp,
|
|
|
|
zfs_prop_to_name(ZFS_PROP_KEYLOCATION),
|
|
|
|
stream_keylocation);
|
|
|
|
if (err != 0) {
|
|
|
|
zfs_close(zhp);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the dataset is not flagged as an encryption root and is
|
|
|
|
* currently an encryption root, force it to inherit from its
|
2017-09-12 20:15:11 +00:00
|
|
|
* parent. The root of a raw send should never be
|
|
|
|
* force-inherited.
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
*/
|
2017-09-12 20:15:11 +00:00
|
|
|
if (!stream_encroot && is_encroot &&
|
|
|
|
strcmp(top_zfs, fsname) != 0) {
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
err = lzc_change_key(fsname, DCP_CMD_FORCE_INHERIT,
|
|
|
|
NULL, NULL, 0);
|
|
|
|
if (err != 0) {
|
|
|
|
zfs_close(zhp);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
zfs_close(zhp);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
error:
|
|
|
|
return (err);
|
|
|
|
}
|
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
static int
|
|
|
|
recv_incremental_replication(libzfs_handle_t *hdl, const char *tofs,
|
2011-11-17 18:14:36 +00:00
|
|
|
recvflags_t *flags, nvlist_t *stream_nv, avl_tree_t *stream_avl,
|
2010-05-28 20:45:14 +00:00
|
|
|
nvlist_t *renamed)
|
2008-11-20 20:01:55 +00:00
|
|
|
{
|
2012-12-13 22:03:07 +00:00
|
|
|
nvlist_t *local_nv, *deleted = NULL;
|
2008-11-20 20:01:55 +00:00
|
|
|
avl_tree_t *local_avl;
|
|
|
|
nvpair_t *fselem, *nextfselem;
|
2010-05-28 20:45:14 +00:00
|
|
|
char *fromsnap;
|
2016-06-15 21:28:36 +00:00
|
|
|
char newname[ZFS_MAX_DATASET_NAME_LEN];
|
2012-12-13 22:03:07 +00:00
|
|
|
char guidname[32];
|
2008-11-20 20:01:55 +00:00
|
|
|
int error;
|
2010-05-28 20:45:14 +00:00
|
|
|
boolean_t needagain, progress, recursive;
|
|
|
|
char *s1, *s2;
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
VERIFY(0 == nvlist_lookup_string(stream_nv, "fromsnap", &fromsnap));
|
2010-05-28 20:45:14 +00:00
|
|
|
|
|
|
|
recursive = (nvlist_lookup_boolean(stream_nv, "not_recursive") ==
|
|
|
|
ENOENT);
|
2008-11-20 20:01:55 +00:00
|
|
|
|
2011-11-17 18:14:36 +00:00
|
|
|
if (flags->dryrun)
|
2008-11-20 20:01:55 +00:00
|
|
|
return (0);
|
|
|
|
|
|
|
|
again:
|
|
|
|
needagain = progress = B_FALSE;
|
|
|
|
|
2012-12-13 22:03:07 +00:00
|
|
|
VERIFY(0 == nvlist_alloc(&deleted, NV_UNIQUE_NAME, 0));
|
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
if ((error = gather_nvlist(hdl, tofs, fromsnap, NULL,
|
2019-03-12 20:13:22 +00:00
|
|
|
recursive, B_TRUE, recursive, B_FALSE, B_FALSE, B_FALSE, B_TRUE,
|
|
|
|
&local_nv, &local_avl)) != 0)
|
2008-11-20 20:01:55 +00:00
|
|
|
return (error);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Process deletes and renames
|
|
|
|
*/
|
|
|
|
for (fselem = nvlist_next_nvpair(local_nv, NULL);
|
|
|
|
fselem; fselem = nextfselem) {
|
|
|
|
nvlist_t *nvfs, *snaps;
|
|
|
|
nvlist_t *stream_nvfs = NULL;
|
|
|
|
nvpair_t *snapelem, *nextsnapelem;
|
|
|
|
uint64_t fromguid = 0;
|
|
|
|
uint64_t originguid = 0;
|
|
|
|
uint64_t stream_originguid = 0;
|
|
|
|
uint64_t parent_fromsnap_guid, stream_parent_fromsnap_guid;
|
|
|
|
char *fsname, *stream_fsname;
|
|
|
|
|
|
|
|
nextfselem = nvlist_next_nvpair(local_nv, fselem);
|
|
|
|
|
|
|
|
VERIFY(0 == nvpair_value_nvlist(fselem, &nvfs));
|
|
|
|
VERIFY(0 == nvlist_lookup_nvlist(nvfs, "snaps", &snaps));
|
|
|
|
VERIFY(0 == nvlist_lookup_string(nvfs, "name", &fsname));
|
|
|
|
VERIFY(0 == nvlist_lookup_uint64(nvfs, "parentfromsnap",
|
|
|
|
&parent_fromsnap_guid));
|
|
|
|
(void) nvlist_lookup_uint64(nvfs, "origin", &originguid);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* First find the stream's fs, so we can check for
|
|
|
|
* a different origin (due to "zfs promote")
|
|
|
|
*/
|
|
|
|
for (snapelem = nvlist_next_nvpair(snaps, NULL);
|
|
|
|
snapelem; snapelem = nvlist_next_nvpair(snaps, snapelem)) {
|
|
|
|
uint64_t thisguid;
|
|
|
|
|
|
|
|
VERIFY(0 == nvpair_value_uint64(snapelem, &thisguid));
|
|
|
|
stream_nvfs = fsavl_find(stream_avl, thisguid, NULL);
|
|
|
|
|
|
|
|
if (stream_nvfs != NULL)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check for promote */
|
|
|
|
(void) nvlist_lookup_uint64(stream_nvfs, "origin",
|
|
|
|
&stream_originguid);
|
|
|
|
if (stream_nvfs && originguid != stream_originguid) {
|
|
|
|
switch (created_before(hdl, local_avl,
|
|
|
|
stream_originguid, originguid)) {
|
|
|
|
case 1: {
|
|
|
|
/* promote it! */
|
|
|
|
nvlist_t *origin_nvfs;
|
|
|
|
char *origin_fsname;
|
|
|
|
|
|
|
|
origin_nvfs = fsavl_find(local_avl, originguid,
|
|
|
|
NULL);
|
|
|
|
VERIFY(0 == nvlist_lookup_string(origin_nvfs,
|
|
|
|
"name", &origin_fsname));
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
error = recv_promote(hdl, fsname, origin_fsname,
|
|
|
|
flags);
|
2008-11-20 20:01:55 +00:00
|
|
|
if (error == 0)
|
|
|
|
progress = B_TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
case -1:
|
|
|
|
fsavl_destroy(local_avl);
|
|
|
|
nvlist_free(local_nv);
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* We had/have the wrong origin, therefore our
|
|
|
|
* list of snapshots is wrong. Need to handle
|
|
|
|
* them on the next pass.
|
|
|
|
*/
|
|
|
|
needagain = B_TRUE;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (snapelem = nvlist_next_nvpair(snaps, NULL);
|
|
|
|
snapelem; snapelem = nextsnapelem) {
|
|
|
|
uint64_t thisguid;
|
|
|
|
char *stream_snapname;
|
2008-12-03 20:09:06 +00:00
|
|
|
nvlist_t *found, *props;
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
nextsnapelem = nvlist_next_nvpair(snaps, snapelem);
|
|
|
|
|
|
|
|
VERIFY(0 == nvpair_value_uint64(snapelem, &thisguid));
|
|
|
|
found = fsavl_find(stream_avl, thisguid,
|
|
|
|
&stream_snapname);
|
|
|
|
|
|
|
|
/* check for delete */
|
|
|
|
if (found == NULL) {
|
2016-06-15 21:28:36 +00:00
|
|
|
char name[ZFS_MAX_DATASET_NAME_LEN];
|
2008-11-20 20:01:55 +00:00
|
|
|
|
2011-11-17 18:14:36 +00:00
|
|
|
if (!flags->force)
|
2008-11-20 20:01:55 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
(void) snprintf(name, sizeof (name), "%s@%s",
|
|
|
|
fsname, nvpair_name(snapelem));
|
|
|
|
|
|
|
|
error = recv_destroy(hdl, name,
|
|
|
|
strlen(fsname)+1, newname, flags);
|
|
|
|
if (error)
|
|
|
|
needagain = B_TRUE;
|
|
|
|
else
|
|
|
|
progress = B_TRUE;
|
2015-05-11 19:05:05 +00:00
|
|
|
sprintf(guidname, "%llu",
|
|
|
|
(u_longlong_t)thisguid);
|
2012-12-13 22:03:07 +00:00
|
|
|
nvlist_add_boolean(deleted, guidname);
|
2008-11-20 20:01:55 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
stream_nvfs = found;
|
|
|
|
|
2008-12-03 20:09:06 +00:00
|
|
|
if (0 == nvlist_lookup_nvlist(stream_nvfs, "snapprops",
|
|
|
|
&props) && 0 == nvlist_lookup_nvlist(props,
|
|
|
|
stream_snapname, &props)) {
|
2013-09-04 12:00:57 +00:00
|
|
|
zfs_cmd_t zc = {"\0"};
|
2008-12-03 20:09:06 +00:00
|
|
|
|
2010-05-28 20:45:14 +00:00
|
|
|
zc.zc_cookie = B_TRUE; /* received */
|
2008-12-03 20:09:06 +00:00
|
|
|
(void) snprintf(zc.zc_name, sizeof (zc.zc_name),
|
|
|
|
"%s@%s", fsname, nvpair_name(snapelem));
|
|
|
|
if (zcmd_write_src_nvlist(hdl, &zc,
|
|
|
|
props) == 0) {
|
|
|
|
(void) zfs_ioctl(hdl,
|
|
|
|
ZFS_IOC_SET_PROP, &zc);
|
|
|
|
zcmd_free_nvlists(&zc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
/* check for different snapname */
|
|
|
|
if (strcmp(nvpair_name(snapelem),
|
|
|
|
stream_snapname) != 0) {
|
2016-06-15 21:28:36 +00:00
|
|
|
char name[ZFS_MAX_DATASET_NAME_LEN];
|
|
|
|
char tryname[ZFS_MAX_DATASET_NAME_LEN];
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
(void) snprintf(name, sizeof (name), "%s@%s",
|
|
|
|
fsname, nvpair_name(snapelem));
|
|
|
|
(void) snprintf(tryname, sizeof (name), "%s@%s",
|
|
|
|
fsname, stream_snapname);
|
|
|
|
|
|
|
|
error = recv_rename(hdl, name, tryname,
|
|
|
|
strlen(fsname)+1, newname, flags);
|
|
|
|
if (error)
|
|
|
|
needagain = B_TRUE;
|
|
|
|
else
|
|
|
|
progress = B_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcmp(stream_snapname, fromsnap) == 0)
|
|
|
|
fromguid = thisguid;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check for delete */
|
|
|
|
if (stream_nvfs == NULL) {
|
2011-11-17 18:14:36 +00:00
|
|
|
if (!flags->force)
|
2008-11-20 20:01:55 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
error = recv_destroy(hdl, fsname, strlen(tofs)+1,
|
|
|
|
newname, flags);
|
|
|
|
if (error)
|
|
|
|
needagain = B_TRUE;
|
|
|
|
else
|
|
|
|
progress = B_TRUE;
|
2015-05-11 19:05:05 +00:00
|
|
|
sprintf(guidname, "%llu",
|
2016-12-12 18:46:26 +00:00
|
|
|
(u_longlong_t)parent_fromsnap_guid);
|
2012-12-13 22:03:07 +00:00
|
|
|
nvlist_add_boolean(deleted, guidname);
|
2008-11-20 20:01:55 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2010-05-28 20:45:14 +00:00
|
|
|
if (fromguid == 0) {
|
2011-11-17 18:14:36 +00:00
|
|
|
if (flags->verbose) {
|
2010-05-28 20:45:14 +00:00
|
|
|
(void) printf("local fs %s does not have "
|
|
|
|
"fromsnap (%s in stream); must have "
|
|
|
|
"been deleted locally; ignoring\n",
|
|
|
|
fsname, fromsnap);
|
|
|
|
}
|
2008-11-20 20:01:55 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
VERIFY(0 == nvlist_lookup_string(stream_nvfs,
|
|
|
|
"name", &stream_fsname));
|
|
|
|
VERIFY(0 == nvlist_lookup_uint64(stream_nvfs,
|
|
|
|
"parentfromsnap", &stream_parent_fromsnap_guid));
|
|
|
|
|
2010-05-28 20:45:14 +00:00
|
|
|
s1 = strrchr(fsname, '/');
|
|
|
|
s2 = strrchr(stream_fsname, '/');
|
|
|
|
|
2012-12-13 22:03:07 +00:00
|
|
|
/*
|
|
|
|
* Check if we're going to rename based on parent guid change
|
|
|
|
* and the current parent guid was also deleted. If it was then
|
|
|
|
* rename will fail and is likely unneeded, so avoid this and
|
|
|
|
* force an early retry to determine the new
|
|
|
|
* parent_fromsnap_guid.
|
|
|
|
*/
|
|
|
|
if (stream_parent_fromsnap_guid != 0 &&
|
|
|
|
parent_fromsnap_guid != 0 &&
|
|
|
|
stream_parent_fromsnap_guid != parent_fromsnap_guid) {
|
2015-05-11 19:05:05 +00:00
|
|
|
sprintf(guidname, "%llu",
|
2016-12-12 18:46:26 +00:00
|
|
|
(u_longlong_t)parent_fromsnap_guid);
|
2012-12-13 22:03:07 +00:00
|
|
|
if (nvlist_exists(deleted, guidname)) {
|
|
|
|
progress = B_TRUE;
|
|
|
|
needagain = B_TRUE;
|
|
|
|
goto doagain;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-28 20:45:14 +00:00
|
|
|
/*
|
|
|
|
* Check for rename. If the exact receive path is specified, it
|
|
|
|
* does not count as a rename, but we still need to check the
|
|
|
|
* datasets beneath it.
|
|
|
|
*/
|
2008-11-20 20:01:55 +00:00
|
|
|
if ((stream_parent_fromsnap_guid != 0 &&
|
2010-05-28 20:45:14 +00:00
|
|
|
parent_fromsnap_guid != 0 &&
|
2008-11-20 20:01:55 +00:00
|
|
|
stream_parent_fromsnap_guid != parent_fromsnap_guid) ||
|
2011-11-17 18:14:36 +00:00
|
|
|
((flags->isprefix || strcmp(tofs, fsname) != 0) &&
|
2010-05-28 20:45:14 +00:00
|
|
|
(s1 != NULL) && (s2 != NULL) && strcmp(s1, s2) != 0)) {
|
2008-11-20 20:01:55 +00:00
|
|
|
nvlist_t *parent;
|
2016-06-15 21:28:36 +00:00
|
|
|
char tryname[ZFS_MAX_DATASET_NAME_LEN];
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
parent = fsavl_find(local_avl,
|
|
|
|
stream_parent_fromsnap_guid, NULL);
|
|
|
|
/*
|
|
|
|
* NB: parent might not be found if we used the
|
|
|
|
* tosnap for stream_parent_fromsnap_guid,
|
|
|
|
* because the parent is a newly-created fs;
|
|
|
|
* we'll be able to rename it after we recv the
|
|
|
|
* new fs.
|
|
|
|
*/
|
|
|
|
if (parent != NULL) {
|
|
|
|
char *pname;
|
|
|
|
|
|
|
|
VERIFY(0 == nvlist_lookup_string(parent, "name",
|
|
|
|
&pname));
|
|
|
|
(void) snprintf(tryname, sizeof (tryname),
|
|
|
|
"%s%s", pname, strrchr(stream_fsname, '/'));
|
|
|
|
} else {
|
|
|
|
tryname[0] = '\0';
|
2011-11-17 18:14:36 +00:00
|
|
|
if (flags->verbose) {
|
2008-11-20 20:01:55 +00:00
|
|
|
(void) printf("local fs %s new parent "
|
|
|
|
"not found\n", fsname);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-28 20:45:14 +00:00
|
|
|
newname[0] = '\0';
|
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
error = recv_rename(hdl, fsname, tryname,
|
|
|
|
strlen(tofs)+1, newname, flags);
|
2010-05-28 20:45:14 +00:00
|
|
|
|
|
|
|
if (renamed != NULL && newname[0] != '\0') {
|
|
|
|
VERIFY(0 == nvlist_add_boolean(renamed,
|
|
|
|
newname));
|
|
|
|
}
|
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
if (error)
|
|
|
|
needagain = B_TRUE;
|
|
|
|
else
|
|
|
|
progress = B_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-13 22:03:07 +00:00
|
|
|
doagain:
|
2008-11-20 20:01:55 +00:00
|
|
|
fsavl_destroy(local_avl);
|
|
|
|
nvlist_free(local_nv);
|
2012-12-13 22:03:07 +00:00
|
|
|
nvlist_free(deleted);
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
if (needagain && progress) {
|
|
|
|
/* do another pass to fix up temporary names */
|
2011-11-17 18:14:36 +00:00
|
|
|
if (flags->verbose)
|
2008-11-20 20:01:55 +00:00
|
|
|
(void) printf("another pass:\n");
|
|
|
|
goto again;
|
|
|
|
}
|
|
|
|
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
return (needagain || error != 0);
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname,
|
2011-11-17 18:14:36 +00:00
|
|
|
recvflags_t *flags, dmu_replay_record_t *drr, zio_cksum_t *zc,
|
2017-05-09 23:21:09 +00:00
|
|
|
char **top_zfs, int cleanup_fd, uint64_t *action_handlep,
|
|
|
|
nvlist_t *cmdprops)
|
2008-11-20 20:01:55 +00:00
|
|
|
{
|
|
|
|
nvlist_t *stream_nv = NULL;
|
|
|
|
avl_tree_t *stream_avl = NULL;
|
|
|
|
char *fromsnap = NULL;
|
2016-06-09 19:24:29 +00:00
|
|
|
char *sendsnap = NULL;
|
2010-05-28 20:45:14 +00:00
|
|
|
char *cp;
|
2016-06-15 21:28:36 +00:00
|
|
|
char tofs[ZFS_MAX_DATASET_NAME_LEN];
|
|
|
|
char sendfs[ZFS_MAX_DATASET_NAME_LEN];
|
2008-11-20 20:01:55 +00:00
|
|
|
char errbuf[1024];
|
|
|
|
dmu_replay_record_t drre;
|
|
|
|
int error;
|
|
|
|
boolean_t anyerr = B_FALSE;
|
|
|
|
boolean_t softerr = B_FALSE;
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
boolean_t recursive, raw;
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
|
|
|
|
"cannot receive"));
|
|
|
|
|
|
|
|
assert(drr->drr_type == DRR_BEGIN);
|
|
|
|
assert(drr->drr_u.drr_begin.drr_magic == DMU_BACKUP_MAGIC);
|
2010-05-28 20:45:14 +00:00
|
|
|
assert(DMU_GET_STREAM_HDRTYPE(drr->drr_u.drr_begin.drr_versioninfo) ==
|
|
|
|
DMU_COMPOUNDSTREAM);
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Read in the nvlist from the stream.
|
|
|
|
*/
|
|
|
|
if (drr->drr_payloadlen != 0) {
|
|
|
|
error = recv_read_nvlist(hdl, fd, drr->drr_payloadlen,
|
2011-11-17 18:14:36 +00:00
|
|
|
&stream_nv, flags->byteswap, zc);
|
2008-11-20 20:01:55 +00:00
|
|
|
if (error) {
|
|
|
|
error = zfs_error(hdl, EZFS_BADSTREAM, errbuf);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-28 20:45:14 +00:00
|
|
|
recursive = (nvlist_lookup_boolean(stream_nv, "not_recursive") ==
|
|
|
|
ENOENT);
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
raw = (nvlist_lookup_boolean(stream_nv, "raw") == 0);
|
2010-05-28 20:45:14 +00:00
|
|
|
|
|
|
|
if (recursive && strchr(destname, '@')) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"cannot specify snapshot name for multi-snapshot stream"));
|
|
|
|
error = zfs_error(hdl, EZFS_BADSTREAM, errbuf);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
/*
|
|
|
|
* Read in the end record and verify checksum.
|
|
|
|
*/
|
|
|
|
if (0 != (error = recv_read(hdl, fd, &drre, sizeof (drre),
|
2011-11-17 18:14:36 +00:00
|
|
|
flags->byteswap, NULL)))
|
2008-11-20 20:01:55 +00:00
|
|
|
goto out;
|
2011-11-17 18:14:36 +00:00
|
|
|
if (flags->byteswap) {
|
2008-11-20 20:01:55 +00:00
|
|
|
drre.drr_type = BSWAP_32(drre.drr_type);
|
|
|
|
drre.drr_u.drr_end.drr_checksum.zc_word[0] =
|
|
|
|
BSWAP_64(drre.drr_u.drr_end.drr_checksum.zc_word[0]);
|
|
|
|
drre.drr_u.drr_end.drr_checksum.zc_word[1] =
|
|
|
|
BSWAP_64(drre.drr_u.drr_end.drr_checksum.zc_word[1]);
|
|
|
|
drre.drr_u.drr_end.drr_checksum.zc_word[2] =
|
|
|
|
BSWAP_64(drre.drr_u.drr_end.drr_checksum.zc_word[2]);
|
|
|
|
drre.drr_u.drr_end.drr_checksum.zc_word[3] =
|
|
|
|
BSWAP_64(drre.drr_u.drr_end.drr_checksum.zc_word[3]);
|
|
|
|
}
|
|
|
|
if (drre.drr_type != DRR_END) {
|
|
|
|
error = zfs_error(hdl, EZFS_BADSTREAM, errbuf);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (!ZIO_CHECKSUM_EQUAL(drre.drr_u.drr_end.drr_checksum, *zc)) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"incorrect header checksum"));
|
|
|
|
error = zfs_error(hdl, EZFS_BADSTREAM, errbuf);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
(void) nvlist_lookup_string(stream_nv, "fromsnap", &fromsnap);
|
|
|
|
|
|
|
|
if (drr->drr_payloadlen != 0) {
|
|
|
|
nvlist_t *stream_fss;
|
|
|
|
|
|
|
|
VERIFY(0 == nvlist_lookup_nvlist(stream_nv, "fss",
|
|
|
|
&stream_fss));
|
|
|
|
if ((stream_avl = fsavl_create(stream_fss)) == NULL) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"couldn't allocate avl tree"));
|
|
|
|
error = zfs_error(hdl, EZFS_NOMEM, errbuf);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2017-04-07 20:54:29 +00:00
|
|
|
if (fromsnap != NULL && recursive) {
|
2010-05-28 20:45:14 +00:00
|
|
|
nvlist_t *renamed = NULL;
|
|
|
|
nvpair_t *pair = NULL;
|
|
|
|
|
2016-06-15 21:28:36 +00:00
|
|
|
(void) strlcpy(tofs, destname, sizeof (tofs));
|
2011-11-17 18:14:36 +00:00
|
|
|
if (flags->isprefix) {
|
2010-05-28 20:45:14 +00:00
|
|
|
struct drr_begin *drrb = &drr->drr_u.drr_begin;
|
|
|
|
int i;
|
|
|
|
|
2011-11-17 18:14:36 +00:00
|
|
|
if (flags->istail) {
|
2010-05-28 20:45:14 +00:00
|
|
|
cp = strrchr(drrb->drr_toname, '/');
|
|
|
|
if (cp == NULL) {
|
|
|
|
(void) strlcat(tofs, "/",
|
2016-06-15 21:28:36 +00:00
|
|
|
sizeof (tofs));
|
2010-05-28 20:45:14 +00:00
|
|
|
i = 0;
|
|
|
|
} else {
|
|
|
|
i = (cp - drrb->drr_toname);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
i = strcspn(drrb->drr_toname, "/@");
|
|
|
|
}
|
2008-11-20 20:01:55 +00:00
|
|
|
/* zfs_receive_one() will create_parents() */
|
2010-05-28 20:45:14 +00:00
|
|
|
(void) strlcat(tofs, &drrb->drr_toname[i],
|
2016-06-15 21:28:36 +00:00
|
|
|
sizeof (tofs));
|
2008-11-20 20:01:55 +00:00
|
|
|
*strchr(tofs, '@') = '\0';
|
|
|
|
}
|
2010-05-28 20:45:14 +00:00
|
|
|
|
2017-04-07 20:54:29 +00:00
|
|
|
if (!flags->dryrun && !flags->nomount) {
|
2010-05-28 20:45:14 +00:00
|
|
|
VERIFY(0 == nvlist_alloc(&renamed,
|
|
|
|
NV_UNIQUE_NAME, 0));
|
|
|
|
}
|
|
|
|
|
|
|
|
softerr = recv_incremental_replication(hdl, tofs, flags,
|
|
|
|
stream_nv, stream_avl, renamed);
|
|
|
|
|
|
|
|
/* Unmount renamed filesystems before receiving. */
|
|
|
|
while ((pair = nvlist_next_nvpair(renamed,
|
|
|
|
pair)) != NULL) {
|
|
|
|
zfs_handle_t *zhp;
|
|
|
|
prop_changelist_t *clp = NULL;
|
|
|
|
|
|
|
|
zhp = zfs_open(hdl, nvpair_name(pair),
|
|
|
|
ZFS_TYPE_FILESYSTEM);
|
|
|
|
if (zhp != NULL) {
|
|
|
|
clp = changelist_gather(zhp,
|
|
|
|
ZFS_PROP_MOUNTPOINT, 0, 0);
|
|
|
|
zfs_close(zhp);
|
|
|
|
if (clp != NULL) {
|
|
|
|
softerr |=
|
|
|
|
changelist_prefix(clp);
|
|
|
|
changelist_free(clp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nvlist_free(renamed);
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-28 20:45:14 +00:00
|
|
|
/*
|
|
|
|
* Get the fs specified by the first path in the stream (the top level
|
|
|
|
* specified by 'zfs send') and pass it to each invocation of
|
|
|
|
* zfs_receive_one().
|
|
|
|
*/
|
|
|
|
(void) strlcpy(sendfs, drr->drr_u.drr_begin.drr_toname,
|
2016-06-15 21:28:36 +00:00
|
|
|
sizeof (sendfs));
|
2016-06-09 19:24:29 +00:00
|
|
|
if ((cp = strchr(sendfs, '@')) != NULL) {
|
2010-05-28 20:45:14 +00:00
|
|
|
*cp = '\0';
|
2016-06-09 19:24:29 +00:00
|
|
|
/*
|
|
|
|
* Find the "sendsnap", the final snapshot in a replication
|
|
|
|
* stream. zfs_receive_one() handles certain errors
|
|
|
|
* differently, depending on if the contained stream is the
|
|
|
|
* last one or not.
|
|
|
|
*/
|
|
|
|
sendsnap = (cp + 1);
|
|
|
|
}
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
/* Finally, receive each contained stream */
|
|
|
|
do {
|
|
|
|
/*
|
|
|
|
* we should figure out if it has a recoverable
|
|
|
|
* error, in which case do a recv_skip() and drive on.
|
|
|
|
* Note, if we fail due to already having this guid,
|
|
|
|
* zfs_receive_one() will take care of it (ie,
|
|
|
|
* recv_skip() and return 0).
|
|
|
|
*/
|
2015-12-22 01:31:57 +00:00
|
|
|
error = zfs_receive_impl(hdl, destname, NULL, flags, fd,
|
2010-08-26 21:24:34 +00:00
|
|
|
sendfs, stream_nv, stream_avl, top_zfs, cleanup_fd,
|
2017-05-09 23:21:09 +00:00
|
|
|
action_handlep, sendsnap, cmdprops);
|
2008-11-20 20:01:55 +00:00
|
|
|
if (error == ENODATA) {
|
|
|
|
error = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
anyerr |= error;
|
|
|
|
} while (error == 0);
|
|
|
|
|
2017-04-07 20:54:29 +00:00
|
|
|
if (drr->drr_payloadlen != 0 && recursive && fromsnap != NULL) {
|
2008-11-20 20:01:55 +00:00
|
|
|
/*
|
|
|
|
* Now that we have the fs's they sent us, try the
|
|
|
|
* renames again.
|
|
|
|
*/
|
|
|
|
softerr = recv_incremental_replication(hdl, tofs, flags,
|
2010-05-28 20:45:14 +00:00
|
|
|
stream_nv, stream_avl, NULL);
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
if (raw && softerr == 0) {
|
|
|
|
softerr = recv_fix_encryption_heirarchy(hdl, destname,
|
|
|
|
stream_nv, stream_avl);
|
|
|
|
}
|
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
out:
|
|
|
|
fsavl_destroy(stream_avl);
|
2016-04-01 03:54:07 +00:00
|
|
|
nvlist_free(stream_nv);
|
2008-11-20 20:01:55 +00:00
|
|
|
if (softerr)
|
|
|
|
error = -2;
|
|
|
|
if (anyerr)
|
|
|
|
error = -1;
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
2010-05-28 20:45:14 +00:00
|
|
|
static void
|
|
|
|
trunc_prop_errs(int truncated)
|
|
|
|
{
|
|
|
|
ASSERT(truncated != 0);
|
|
|
|
|
|
|
|
if (truncated == 1)
|
|
|
|
(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
|
|
|
|
"1 more property could not be set\n"));
|
|
|
|
else
|
|
|
|
(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
|
|
|
|
"%d more properties could not be set\n"), truncated);
|
|
|
|
}
|
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
static int
|
|
|
|
recv_skip(libzfs_handle_t *hdl, int fd, boolean_t byteswap)
|
|
|
|
{
|
|
|
|
dmu_replay_record_t *drr;
|
2014-11-03 20:15:08 +00:00
|
|
|
void *buf = zfs_alloc(hdl, SPA_MAXBLOCKSIZE);
|
2010-05-28 20:45:14 +00:00
|
|
|
char errbuf[1024];
|
|
|
|
|
|
|
|
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
|
|
|
|
"cannot receive:"));
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
/* XXX would be great to use lseek if possible... */
|
|
|
|
drr = buf;
|
|
|
|
|
|
|
|
while (recv_read(hdl, fd, drr, sizeof (dmu_replay_record_t),
|
|
|
|
byteswap, NULL) == 0) {
|
|
|
|
if (byteswap)
|
|
|
|
drr->drr_type = BSWAP_32(drr->drr_type);
|
|
|
|
|
|
|
|
switch (drr->drr_type) {
|
|
|
|
case DRR_BEGIN:
|
2010-05-28 20:45:14 +00:00
|
|
|
if (drr->drr_payloadlen != 0) {
|
2016-01-06 21:22:48 +00:00
|
|
|
(void) recv_read(hdl, fd, buf,
|
|
|
|
drr->drr_payloadlen, B_FALSE, NULL);
|
2010-05-28 20:45:14 +00:00
|
|
|
}
|
2008-11-20 20:01:55 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case DRR_END:
|
|
|
|
free(buf);
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
case DRR_OBJECT:
|
|
|
|
if (byteswap) {
|
|
|
|
drr->drr_u.drr_object.drr_bonuslen =
|
|
|
|
BSWAP_32(drr->drr_u.drr_object.
|
|
|
|
drr_bonuslen);
|
|
|
|
}
|
|
|
|
(void) recv_read(hdl, fd, buf,
|
|
|
|
P2ROUNDUP(drr->drr_u.drr_object.drr_bonuslen, 8),
|
|
|
|
B_FALSE, NULL);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DRR_WRITE:
|
|
|
|
if (byteswap) {
|
2016-07-11 17:45:52 +00:00
|
|
|
drr->drr_u.drr_write.drr_logical_size =
|
|
|
|
BSWAP_64(
|
|
|
|
drr->drr_u.drr_write.drr_logical_size);
|
|
|
|
drr->drr_u.drr_write.drr_compressed_size =
|
|
|
|
BSWAP_64(
|
|
|
|
drr->drr_u.drr_write.drr_compressed_size);
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
2016-07-11 17:45:52 +00:00
|
|
|
uint64_t payload_size =
|
|
|
|
DRR_WRITE_PAYLOAD_SIZE(&drr->drr_u.drr_write);
|
2008-11-20 20:01:55 +00:00
|
|
|
(void) recv_read(hdl, fd, buf,
|
2016-07-11 17:45:52 +00:00
|
|
|
payload_size, B_FALSE, NULL);
|
2008-11-20 20:01:55 +00:00
|
|
|
break;
|
2010-05-28 20:45:14 +00:00
|
|
|
case DRR_SPILL:
|
|
|
|
if (byteswap) {
|
2016-05-09 18:22:00 +00:00
|
|
|
drr->drr_u.drr_spill.drr_length =
|
2010-05-28 20:45:14 +00:00
|
|
|
BSWAP_64(drr->drr_u.drr_spill.drr_length);
|
|
|
|
}
|
|
|
|
(void) recv_read(hdl, fd, buf,
|
|
|
|
drr->drr_u.drr_spill.drr_length, B_FALSE, NULL);
|
|
|
|
break;
|
2014-06-05 21:19:08 +00:00
|
|
|
case DRR_WRITE_EMBEDDED:
|
|
|
|
if (byteswap) {
|
|
|
|
drr->drr_u.drr_write_embedded.drr_psize =
|
|
|
|
BSWAP_32(drr->drr_u.drr_write_embedded.
|
|
|
|
drr_psize);
|
|
|
|
}
|
|
|
|
(void) recv_read(hdl, fd, buf,
|
|
|
|
P2ROUNDUP(drr->drr_u.drr_write_embedded.drr_psize,
|
|
|
|
8), B_FALSE, NULL);
|
|
|
|
break;
|
2010-05-28 20:45:14 +00:00
|
|
|
case DRR_WRITE_BYREF:
|
2008-11-20 20:01:55 +00:00
|
|
|
case DRR_FREEOBJECTS:
|
|
|
|
case DRR_FREE:
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2010-05-28 20:45:14 +00:00
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"invalid record type"));
|
2016-10-11 17:24:18 +00:00
|
|
|
free(buf);
|
2010-05-28 20:45:14 +00:00
|
|
|
return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free(buf);
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
2016-01-06 21:22:48 +00:00
|
|
|
static void
|
|
|
|
recv_ecksum_set_aux(libzfs_handle_t *hdl, const char *target_snap,
|
|
|
|
boolean_t resumable)
|
|
|
|
{
|
2016-06-15 21:28:36 +00:00
|
|
|
char target_fs[ZFS_MAX_DATASET_NAME_LEN];
|
2016-01-06 21:22:48 +00:00
|
|
|
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"checksum mismatch or incomplete stream"));
|
|
|
|
|
|
|
|
if (!resumable)
|
|
|
|
return;
|
|
|
|
(void) strlcpy(target_fs, target_snap, sizeof (target_fs));
|
|
|
|
*strchr(target_fs, '@') = '\0';
|
|
|
|
zfs_handle_t *zhp = zfs_open(hdl, target_fs,
|
|
|
|
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
|
|
|
|
if (zhp == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
char token_buf[ZFS_MAXPROPLEN];
|
|
|
|
int error = zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN,
|
|
|
|
token_buf, sizeof (token_buf),
|
|
|
|
NULL, NULL, 0, B_TRUE);
|
|
|
|
if (error == 0) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"checksum mismatch or incomplete stream.\n"
|
|
|
|
"Partially received snapshot is saved.\n"
|
|
|
|
"A resuming stream can be generated on the sending "
|
|
|
|
"system by running:\n"
|
|
|
|
" zfs send -t %s"),
|
|
|
|
token_buf);
|
|
|
|
}
|
|
|
|
zfs_close(zhp);
|
|
|
|
}
|
|
|
|
|
2017-05-09 23:21:09 +00:00
|
|
|
/*
|
|
|
|
* Prepare a new nvlist of properties that are to override (-o) or be excluded
|
|
|
|
* (-x) from the received dataset
|
|
|
|
* recvprops: received properties from the send stream
|
|
|
|
* cmdprops: raw input properties from command line
|
|
|
|
* origprops: properties, both locally-set and received, currently set on the
|
|
|
|
* target dataset if it exists, NULL otherwise.
|
|
|
|
* oxprops: valid output override (-o) and excluded (-x) properties
|
|
|
|
*/
|
|
|
|
static int
|
2017-10-13 17:09:04 +00:00
|
|
|
zfs_setup_cmdline_props(libzfs_handle_t *hdl, zfs_type_t type,
|
|
|
|
char *fsname, boolean_t zoned, boolean_t recursive, boolean_t newfs,
|
|
|
|
boolean_t raw, boolean_t toplevel, nvlist_t *recvprops, nvlist_t *cmdprops,
|
|
|
|
nvlist_t *origprops, nvlist_t **oxprops, uint8_t **wkeydata_out,
|
|
|
|
uint_t *wkeylen_out, const char *errbuf)
|
2017-05-09 23:21:09 +00:00
|
|
|
{
|
|
|
|
nvpair_t *nvp;
|
|
|
|
nvlist_t *oprops, *voprops;
|
|
|
|
zfs_handle_t *zhp = NULL;
|
|
|
|
zpool_handle_t *zpool_hdl = NULL;
|
2017-10-13 17:09:04 +00:00
|
|
|
char *cp;
|
2017-05-09 23:21:09 +00:00
|
|
|
int ret = 0;
|
2017-10-13 17:09:04 +00:00
|
|
|
char namebuf[ZFS_MAX_DATASET_NAME_LEN];
|
2017-05-09 23:21:09 +00:00
|
|
|
|
|
|
|
if (nvlist_empty(cmdprops))
|
|
|
|
return (0); /* No properties to override or exclude */
|
|
|
|
|
|
|
|
*oxprops = fnvlist_alloc();
|
|
|
|
oprops = fnvlist_alloc();
|
|
|
|
|
2017-10-13 17:09:04 +00:00
|
|
|
strlcpy(namebuf, fsname, ZFS_MAX_DATASET_NAME_LEN);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get our dataset handle. The target dataset may not exist yet.
|
|
|
|
*/
|
|
|
|
if (zfs_dataset_exists(hdl, namebuf, ZFS_TYPE_DATASET)) {
|
|
|
|
zhp = zfs_open(hdl, namebuf, ZFS_TYPE_DATASET);
|
|
|
|
if (zhp == NULL) {
|
|
|
|
ret = -1;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* open the zpool handle */
|
|
|
|
cp = strchr(namebuf, '/');
|
|
|
|
if (cp != NULL)
|
|
|
|
*cp = '\0';
|
|
|
|
zpool_hdl = zpool_open(hdl, namebuf);
|
|
|
|
if (zpool_hdl == NULL) {
|
|
|
|
ret = -1;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* restore namebuf to match fsname for later use */
|
|
|
|
if (cp != NULL)
|
|
|
|
*cp = '/';
|
|
|
|
|
2017-05-09 23:21:09 +00:00
|
|
|
/*
|
|
|
|
* first iteration: process excluded (-x) properties now and gather
|
|
|
|
* added (-o) properties to be later processed by zfs_valid_proplist()
|
|
|
|
*/
|
|
|
|
nvp = NULL;
|
|
|
|
while ((nvp = nvlist_next_nvpair(cmdprops, nvp)) != NULL) {
|
|
|
|
const char *name = nvpair_name(nvp);
|
|
|
|
zfs_prop_t prop = zfs_name_to_prop(name);
|
|
|
|
|
|
|
|
/* "origin" is processed separately, don't handle it here */
|
|
|
|
if (prop == ZFS_PROP_ORIGIN)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* we're trying to override or exclude a property that does not
|
|
|
|
* make sense for this type of dataset, but we don't want to
|
|
|
|
* fail if the receive is recursive: this comes in handy when
|
|
|
|
* the send stream contains, for instance, a child ZVOL and
|
|
|
|
* we're trying to receive it with "-o atime=on"
|
|
|
|
*/
|
|
|
|
if (!zfs_prop_valid_for_type(prop, type, B_FALSE) &&
|
|
|
|
!zfs_prop_user(name)) {
|
|
|
|
if (recursive)
|
|
|
|
continue;
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"property '%s' does not apply to datasets of this "
|
|
|
|
"type"), name);
|
|
|
|
ret = zfs_error(hdl, EZFS_BADPROP, errbuf);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2017-10-13 17:09:04 +00:00
|
|
|
/* raw streams can't override encryption properties */
|
|
|
|
if ((zfs_prop_encryption_key_param(prop) ||
|
|
|
|
prop == ZFS_PROP_ENCRYPTION) && (raw || !newfs)) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"encryption property '%s' cannot "
|
|
|
|
"be set or excluded for raw or incremental "
|
|
|
|
"streams."), name);
|
|
|
|
ret = zfs_error(hdl, EZFS_BADPROP, errbuf);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2017-05-09 23:21:09 +00:00
|
|
|
switch (nvpair_type(nvp)) {
|
|
|
|
case DATA_TYPE_BOOLEAN: /* -x property */
|
|
|
|
/*
|
|
|
|
* DATA_TYPE_BOOLEAN is the way we're asked to "exclude"
|
|
|
|
* a property: this is done by forcing an explicit
|
|
|
|
* inherit on the destination so the effective value is
|
|
|
|
* not the one we received from the send stream.
|
|
|
|
* We do this only if the property is not already
|
|
|
|
* locally-set, in which case its value will take
|
|
|
|
* priority over the received anyway.
|
|
|
|
*/
|
|
|
|
if (nvlist_exists(origprops, name)) {
|
|
|
|
nvlist_t *attrs;
|
|
|
|
|
|
|
|
attrs = fnvlist_lookup_nvlist(origprops, name);
|
|
|
|
if (strcmp(fnvlist_lookup_string(attrs,
|
|
|
|
ZPROP_SOURCE), ZPROP_SOURCE_VAL_RECVD) != 0)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* We can't force an explicit inherit on non-inheritable
|
|
|
|
* properties: if we're asked to exclude this kind of
|
|
|
|
* values we remove them from "recvprops" input nvlist.
|
|
|
|
*/
|
|
|
|
if (!zfs_prop_inheritable(prop) &&
|
|
|
|
!zfs_prop_user(name) && /* can be inherited too */
|
|
|
|
nvlist_exists(recvprops, name))
|
|
|
|
fnvlist_remove(recvprops, name);
|
|
|
|
else
|
|
|
|
fnvlist_add_nvpair(*oxprops, nvp);
|
|
|
|
break;
|
|
|
|
case DATA_TYPE_STRING: /* -o property=value */
|
|
|
|
fnvlist_add_nvpair(oprops, nvp);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"property '%s' must be a string or boolean"), name);
|
|
|
|
ret = zfs_error(hdl, EZFS_BADPROP, errbuf);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (toplevel) {
|
|
|
|
/* convert override strings properties to native */
|
|
|
|
if ((voprops = zfs_valid_proplist(hdl, ZFS_TYPE_DATASET,
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
oprops, zoned, zhp, zpool_hdl, B_FALSE, errbuf)) == NULL) {
|
2017-05-09 23:21:09 +00:00
|
|
|
ret = zfs_error(hdl, EZFS_BADPROP, errbuf);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2017-10-13 17:09:04 +00:00
|
|
|
/*
|
|
|
|
* zfs_crypto_create() requires the parent name. Get it
|
|
|
|
* by truncating the fsname copy stored in namebuf.
|
|
|
|
*/
|
|
|
|
cp = strrchr(namebuf, '/');
|
|
|
|
if (cp != NULL)
|
|
|
|
*cp = '\0';
|
|
|
|
|
|
|
|
if (!raw && zfs_crypto_create(hdl, namebuf, voprops, NULL,
|
|
|
|
B_FALSE, wkeydata_out, wkeylen_out) != 0) {
|
|
|
|
fnvlist_free(voprops);
|
|
|
|
ret = zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2017-05-09 23:21:09 +00:00
|
|
|
/* second pass: process "-o" properties */
|
|
|
|
fnvlist_merge(*oxprops, voprops);
|
|
|
|
fnvlist_free(voprops);
|
|
|
|
} else {
|
|
|
|
/* override props on child dataset are inherited */
|
|
|
|
nvp = NULL;
|
|
|
|
while ((nvp = nvlist_next_nvpair(oprops, nvp)) != NULL) {
|
|
|
|
const char *name = nvpair_name(nvp);
|
|
|
|
fnvlist_add_boolean(*oxprops, name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
error:
|
2017-10-13 17:09:04 +00:00
|
|
|
if (zhp != NULL)
|
|
|
|
zfs_close(zhp);
|
|
|
|
if (zpool_hdl != NULL)
|
|
|
|
zpool_close(zpool_hdl);
|
2017-05-09 23:21:09 +00:00
|
|
|
fnvlist_free(oprops);
|
|
|
|
return (ret);
|
|
|
|
}
|
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
/*
|
|
|
|
* Restores a backup of tosnap from the file descriptor specified by infd.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
|
2015-12-22 01:31:57 +00:00
|
|
|
const char *originsnap, recvflags_t *flags, dmu_replay_record_t *drr,
|
|
|
|
dmu_replay_record_t *drr_noswap, const char *sendfs, nvlist_t *stream_nv,
|
|
|
|
avl_tree_t *stream_avl, char **top_zfs, int cleanup_fd,
|
2017-05-09 23:21:09 +00:00
|
|
|
uint64_t *action_handlep, const char *finalsnap, nvlist_t *cmdprops)
|
2008-11-20 20:01:55 +00:00
|
|
|
{
|
|
|
|
time_t begin_time;
|
2010-05-28 20:45:14 +00:00
|
|
|
int ioctl_err, ioctl_errno, err;
|
2008-11-20 20:01:55 +00:00
|
|
|
char *cp;
|
|
|
|
struct drr_begin *drrb = &drr->drr_u.drr_begin;
|
|
|
|
char errbuf[1024];
|
2010-05-28 20:45:14 +00:00
|
|
|
const char *chopprefix;
|
2008-11-20 20:01:55 +00:00
|
|
|
boolean_t newfs = B_FALSE;
|
|
|
|
boolean_t stream_wantsnewfs;
|
2016-06-10 00:04:12 +00:00
|
|
|
boolean_t newprops = B_FALSE;
|
|
|
|
uint64_t read_bytes = 0;
|
|
|
|
uint64_t errflags = 0;
|
2008-11-20 20:01:55 +00:00
|
|
|
uint64_t parent_snapguid = 0;
|
|
|
|
prop_changelist_t *clp = NULL;
|
2008-12-03 20:09:06 +00:00
|
|
|
nvlist_t *snapprops_nvlist = NULL;
|
2019-02-15 20:41:38 +00:00
|
|
|
nvlist_t *snapholds_nvlist = NULL;
|
2010-05-28 20:45:14 +00:00
|
|
|
zprop_errflags_t prop_errflags;
|
2016-06-10 00:04:12 +00:00
|
|
|
nvlist_t *prop_errors = NULL;
|
2010-05-28 20:45:14 +00:00
|
|
|
boolean_t recursive;
|
2016-06-09 19:24:29 +00:00
|
|
|
char *snapname = NULL;
|
2016-06-10 00:04:12 +00:00
|
|
|
char destsnap[MAXPATHLEN * 2];
|
|
|
|
char origin[MAXNAMELEN];
|
|
|
|
char name[MAXPATHLEN];
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
char tmp_keylocation[MAXNAMELEN];
|
2017-05-09 23:21:09 +00:00
|
|
|
nvlist_t *rcvprops = NULL; /* props received from the send stream */
|
|
|
|
nvlist_t *oxprops = NULL; /* override (-o) and exclude (-x) props */
|
|
|
|
nvlist_t *origprops = NULL; /* original props (if destination exists) */
|
|
|
|
zfs_type_t type;
|
2018-01-30 23:54:33 +00:00
|
|
|
boolean_t toplevel = B_FALSE;
|
2017-05-09 23:21:09 +00:00
|
|
|
boolean_t zoned = B_FALSE;
|
2018-02-12 20:28:59 +00:00
|
|
|
boolean_t hastoken = B_FALSE;
|
2017-10-13 17:09:04 +00:00
|
|
|
uint8_t *wkeydata = NULL;
|
|
|
|
uint_t wkeylen = 0;
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
begin_time = time(NULL);
|
2016-06-10 00:04:12 +00:00
|
|
|
bzero(origin, MAXNAMELEN);
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
bzero(tmp_keylocation, MAXNAMELEN);
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
|
|
|
|
"cannot receive"));
|
|
|
|
|
2010-05-28 20:45:14 +00:00
|
|
|
recursive = (nvlist_lookup_boolean(stream_nv, "not_recursive") ==
|
|
|
|
ENOENT);
|
|
|
|
|
2019-02-15 20:41:38 +00:00
|
|
|
/* Did the user request holds be skipped via zfs recv -k? */
|
|
|
|
boolean_t holds = flags->holds && !flags->skipholds;
|
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
if (stream_avl != NULL) {
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
char *keylocation = NULL;
|
2016-10-07 17:05:06 +00:00
|
|
|
nvlist_t *lookup = NULL;
|
2008-12-03 20:09:06 +00:00
|
|
|
nvlist_t *fs = fsavl_find(stream_avl, drrb->drr_toguid,
|
|
|
|
&snapname);
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
(void) nvlist_lookup_uint64(fs, "parentfromsnap",
|
|
|
|
&parent_snapguid);
|
2017-05-09 23:21:09 +00:00
|
|
|
err = nvlist_lookup_nvlist(fs, "props", &rcvprops);
|
2016-06-10 00:04:12 +00:00
|
|
|
if (err) {
|
2017-05-09 23:21:09 +00:00
|
|
|
VERIFY(0 == nvlist_alloc(&rcvprops, NV_UNIQUE_NAME, 0));
|
2016-06-10 00:04:12 +00:00
|
|
|
newprops = B_TRUE;
|
|
|
|
}
|
2008-11-20 20:01:55 +00:00
|
|
|
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
/*
|
|
|
|
* The keylocation property may only be set on encryption roots,
|
|
|
|
* but this dataset might not become an encryption root until
|
|
|
|
* recv_fix_encryption_heirarchy() is called. That function
|
|
|
|
* will fixup the keylocation anyway, so we temporarily unset
|
|
|
|
* the keylocation for now to avoid any errors from the receive
|
|
|
|
* ioctl.
|
|
|
|
*/
|
|
|
|
err = nvlist_lookup_string(rcvprops,
|
|
|
|
zfs_prop_to_name(ZFS_PROP_KEYLOCATION), &keylocation);
|
|
|
|
if (err == 0) {
|
|
|
|
strcpy(tmp_keylocation, keylocation);
|
|
|
|
(void) nvlist_remove_all(rcvprops,
|
|
|
|
zfs_prop_to_name(ZFS_PROP_KEYLOCATION));
|
|
|
|
}
|
|
|
|
|
2011-11-17 18:14:36 +00:00
|
|
|
if (flags->canmountoff) {
|
2017-05-09 23:21:09 +00:00
|
|
|
VERIFY(0 == nvlist_add_uint64(rcvprops,
|
2008-11-20 20:01:55 +00:00
|
|
|
zfs_prop_to_name(ZFS_PROP_CANMOUNT), 0));
|
2019-02-15 20:41:38 +00:00
|
|
|
} else if (newprops) { /* nothing in rcvprops, eliminate it */
|
|
|
|
nvlist_free(rcvprops);
|
|
|
|
rcvprops = NULL;
|
|
|
|
newprops = B_FALSE;
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
2016-10-07 17:05:06 +00:00
|
|
|
if (0 == nvlist_lookup_nvlist(fs, "snapprops", &lookup)) {
|
|
|
|
VERIFY(0 == nvlist_lookup_nvlist(lookup,
|
|
|
|
snapname, &snapprops_nvlist));
|
|
|
|
}
|
2019-02-15 20:41:38 +00:00
|
|
|
if (holds) {
|
|
|
|
if (0 == nvlist_lookup_nvlist(fs, "snapholds",
|
|
|
|
&lookup)) {
|
|
|
|
VERIFY(0 == nvlist_lookup_nvlist(lookup,
|
|
|
|
snapname, &snapholds_nvlist));
|
|
|
|
}
|
|
|
|
}
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
2010-05-28 20:45:14 +00:00
|
|
|
cp = NULL;
|
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
/*
|
|
|
|
* Determine how much of the snapshot name stored in the stream
|
|
|
|
* we are going to tack on to the name they specified on the
|
|
|
|
* command line, and how much we are going to chop off.
|
|
|
|
*
|
|
|
|
* If they specified a snapshot, chop the entire name stored in
|
|
|
|
* the stream.
|
|
|
|
*/
|
2011-11-17 18:14:36 +00:00
|
|
|
if (flags->istail) {
|
2010-05-28 20:45:14 +00:00
|
|
|
/*
|
|
|
|
* A filesystem was specified with -e. We want to tack on only
|
|
|
|
* the tail of the sent snapshot path.
|
|
|
|
*/
|
|
|
|
if (strchr(tosnap, '@')) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
|
|
|
|
"argument - snapshot not allowed with -e"));
|
2016-06-10 00:04:12 +00:00
|
|
|
err = zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
|
|
|
|
goto out;
|
2010-05-28 20:45:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
chopprefix = strrchr(sendfs, '/');
|
|
|
|
|
|
|
|
if (chopprefix == NULL) {
|
|
|
|
/*
|
|
|
|
* The tail is the poolname, so we need to
|
|
|
|
* prepend a path separator.
|
|
|
|
*/
|
|
|
|
int len = strlen(drrb->drr_toname);
|
|
|
|
cp = malloc(len + 2);
|
|
|
|
cp[0] = '/';
|
|
|
|
(void) strcpy(&cp[1], drrb->drr_toname);
|
|
|
|
chopprefix = cp;
|
|
|
|
} else {
|
|
|
|
chopprefix = drrb->drr_toname + (chopprefix - sendfs);
|
|
|
|
}
|
2011-11-17 18:14:36 +00:00
|
|
|
} else if (flags->isprefix) {
|
2008-11-20 20:01:55 +00:00
|
|
|
/*
|
2010-05-28 20:45:14 +00:00
|
|
|
* A filesystem was specified with -d. We want to tack on
|
|
|
|
* everything but the first element of the sent snapshot path
|
|
|
|
* (all but the pool name).
|
2008-11-20 20:01:55 +00:00
|
|
|
*/
|
|
|
|
if (strchr(tosnap, '@')) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
|
|
|
|
"argument - snapshot not allowed with -d"));
|
2016-06-10 00:04:12 +00:00
|
|
|
err = zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
|
|
|
|
goto out;
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
2010-05-28 20:45:14 +00:00
|
|
|
|
|
|
|
chopprefix = strchr(drrb->drr_toname, '/');
|
|
|
|
if (chopprefix == NULL)
|
|
|
|
chopprefix = strchr(drrb->drr_toname, '@');
|
2008-11-20 20:01:55 +00:00
|
|
|
} else if (strchr(tosnap, '@') == NULL) {
|
|
|
|
/*
|
2010-05-28 20:45:14 +00:00
|
|
|
* If a filesystem was specified without -d or -e, we want to
|
|
|
|
* tack on everything after the fs specified by 'zfs send'.
|
2008-11-20 20:01:55 +00:00
|
|
|
*/
|
2010-05-28 20:45:14 +00:00
|
|
|
chopprefix = drrb->drr_toname + strlen(sendfs);
|
|
|
|
} else {
|
|
|
|
/* A snapshot was specified as an exact path (no -d or -e). */
|
|
|
|
if (recursive) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"cannot specify snapshot name for multi-snapshot "
|
|
|
|
"stream"));
|
2016-06-10 00:04:12 +00:00
|
|
|
err = zfs_error(hdl, EZFS_BADSTREAM, errbuf);
|
|
|
|
goto out;
|
2010-05-28 20:45:14 +00:00
|
|
|
}
|
|
|
|
chopprefix = drrb->drr_toname + strlen(drrb->drr_toname);
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
2010-05-28 20:45:14 +00:00
|
|
|
|
|
|
|
ASSERT(strstr(drrb->drr_toname, sendfs) == drrb->drr_toname);
|
2018-12-04 17:38:55 +00:00
|
|
|
ASSERT(chopprefix > drrb->drr_toname || strchr(sendfs, '/') == NULL);
|
|
|
|
ASSERT(chopprefix <= drrb->drr_toname + strlen(drrb->drr_toname) ||
|
|
|
|
strchr(sendfs, '/') == NULL);
|
2010-05-28 20:45:14 +00:00
|
|
|
ASSERT(chopprefix[0] == '/' || chopprefix[0] == '@' ||
|
|
|
|
chopprefix[0] == '\0');
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
/*
|
2016-06-10 00:04:12 +00:00
|
|
|
* Determine name of destination snapshot.
|
2008-11-20 20:01:55 +00:00
|
|
|
*/
|
2016-10-13 21:02:07 +00:00
|
|
|
(void) strlcpy(destsnap, tosnap, sizeof (destsnap));
|
2016-06-10 00:04:12 +00:00
|
|
|
(void) strlcat(destsnap, chopprefix, sizeof (destsnap));
|
2010-05-28 20:45:14 +00:00
|
|
|
free(cp);
|
2016-06-10 00:04:12 +00:00
|
|
|
if (!zfs_name_valid(destsnap, ZFS_TYPE_SNAPSHOT)) {
|
|
|
|
err = zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
|
|
|
|
goto out;
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2016-06-10 00:04:12 +00:00
|
|
|
* Determine the name of the origin snapshot.
|
2008-11-20 20:01:55 +00:00
|
|
|
*/
|
2017-01-27 19:47:54 +00:00
|
|
|
if (originsnap) {
|
2018-04-04 17:16:47 +00:00
|
|
|
(void) strlcpy(origin, originsnap, sizeof (origin));
|
2017-01-27 19:47:54 +00:00
|
|
|
if (flags->verbose)
|
|
|
|
(void) printf("using provided clone origin %s\n",
|
|
|
|
origin);
|
|
|
|
} else if (drrb->drr_flags & DRR_FLAG_CLONE) {
|
2016-06-10 00:04:12 +00:00
|
|
|
if (guid_to_name(hdl, destsnap,
|
|
|
|
drrb->drr_fromguid, B_FALSE, origin) != 0) {
|
2008-11-20 20:01:55 +00:00
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"local origin for clone %s does not exist"),
|
2016-06-10 00:04:12 +00:00
|
|
|
destsnap);
|
|
|
|
err = zfs_error(hdl, EZFS_NOENT, errbuf);
|
|
|
|
goto out;
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
2011-11-17 18:14:36 +00:00
|
|
|
if (flags->verbose)
|
2016-06-10 00:04:12 +00:00
|
|
|
(void) printf("found clone origin %s\n", origin);
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
2016-01-06 21:22:48 +00:00
|
|
|
boolean_t resuming = DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo) &
|
|
|
|
DMU_BACKUP_FEATURE_RESUMING;
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
boolean_t raw = DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo) &
|
|
|
|
DMU_BACKUP_FEATURE_RAW;
|
2017-08-23 23:54:24 +00:00
|
|
|
boolean_t embedded = DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo) &
|
|
|
|
DMU_BACKUP_FEATURE_EMBED_DATA;
|
2010-08-26 16:52:39 +00:00
|
|
|
stream_wantsnewfs = (drrb->drr_fromguid == 0 ||
|
2016-01-06 21:22:48 +00:00
|
|
|
(drrb->drr_flags & DRR_FLAG_CLONE) || originsnap) && !resuming;
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
if (stream_wantsnewfs) {
|
|
|
|
/*
|
|
|
|
* if the parent fs does not exist, look for it based on
|
|
|
|
* the parent snap GUID
|
|
|
|
*/
|
|
|
|
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
|
|
|
|
"cannot receive new filesystem stream"));
|
|
|
|
|
2016-06-10 00:04:12 +00:00
|
|
|
(void) strcpy(name, destsnap);
|
|
|
|
cp = strrchr(name, '/');
|
2008-11-20 20:01:55 +00:00
|
|
|
if (cp)
|
|
|
|
*cp = '\0';
|
|
|
|
if (cp &&
|
2016-06-10 00:04:12 +00:00
|
|
|
!zfs_dataset_exists(hdl, name, ZFS_TYPE_DATASET)) {
|
2016-06-15 21:28:36 +00:00
|
|
|
char suffix[ZFS_MAX_DATASET_NAME_LEN];
|
2016-06-10 00:04:12 +00:00
|
|
|
(void) strcpy(suffix, strrchr(destsnap, '/'));
|
|
|
|
if (guid_to_name(hdl, name, parent_snapguid,
|
|
|
|
B_FALSE, destsnap) == 0) {
|
|
|
|
*strchr(destsnap, '@') = '\0';
|
|
|
|
(void) strcat(destsnap, suffix);
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* if the fs does not exist, look for it based on the
|
|
|
|
* fromsnap GUID
|
|
|
|
*/
|
|
|
|
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
|
|
|
|
"cannot receive incremental stream"));
|
|
|
|
|
2016-06-10 00:04:12 +00:00
|
|
|
(void) strcpy(name, destsnap);
|
|
|
|
*strchr(name, '@') = '\0';
|
2008-11-20 20:01:55 +00:00
|
|
|
|
2010-05-28 20:45:14 +00:00
|
|
|
/*
|
|
|
|
* If the exact receive path was specified and this is the
|
|
|
|
* topmost path in the stream, then if the fs does not exist we
|
|
|
|
* should look no further.
|
|
|
|
*/
|
2011-11-17 18:14:36 +00:00
|
|
|
if ((flags->isprefix || (*(chopprefix = drrb->drr_toname +
|
2010-05-28 20:45:14 +00:00
|
|
|
strlen(sendfs)) != '\0' && *chopprefix != '@')) &&
|
2016-06-10 00:04:12 +00:00
|
|
|
!zfs_dataset_exists(hdl, name, ZFS_TYPE_DATASET)) {
|
2016-06-15 21:28:36 +00:00
|
|
|
char snap[ZFS_MAX_DATASET_NAME_LEN];
|
2016-06-10 00:04:12 +00:00
|
|
|
(void) strcpy(snap, strchr(destsnap, '@'));
|
|
|
|
if (guid_to_name(hdl, name, drrb->drr_fromguid,
|
|
|
|
B_FALSE, destsnap) == 0) {
|
|
|
|
*strchr(destsnap, '@') = '\0';
|
|
|
|
(void) strcat(destsnap, snap);
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-10 00:04:12 +00:00
|
|
|
(void) strcpy(name, destsnap);
|
|
|
|
*strchr(name, '@') = '\0';
|
2008-11-20 20:01:55 +00:00
|
|
|
|
2016-06-10 00:04:12 +00:00
|
|
|
if (zfs_dataset_exists(hdl, name, ZFS_TYPE_DATASET)) {
|
|
|
|
zfs_cmd_t zc = {"\0"};
|
2008-11-20 20:01:55 +00:00
|
|
|
zfs_handle_t *zhp;
|
2018-02-21 20:30:11 +00:00
|
|
|
boolean_t encrypted;
|
2010-05-28 20:45:14 +00:00
|
|
|
|
2016-06-10 00:04:12 +00:00
|
|
|
(void) strcpy(zc.zc_name, name);
|
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
/*
|
2016-01-06 21:22:48 +00:00
|
|
|
* Destination fs exists. It must be one of these cases:
|
|
|
|
* - an incremental send stream
|
|
|
|
* - the stream specifies a new fs (full stream or clone)
|
|
|
|
* and they want us to blow away the existing fs (and
|
|
|
|
* have therefore specified -F and removed any snapshots)
|
|
|
|
* - we are resuming a failed receive.
|
2008-11-20 20:01:55 +00:00
|
|
|
*/
|
|
|
|
if (stream_wantsnewfs) {
|
2019-02-08 23:44:15 +00:00
|
|
|
boolean_t is_volume = drrb->drr_type == DMU_OST_ZVOL;
|
2011-11-17 18:14:36 +00:00
|
|
|
if (!flags->force) {
|
2008-11-20 20:01:55 +00:00
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"destination '%s' exists\n"
|
2016-06-10 00:04:12 +00:00
|
|
|
"must specify -F to overwrite it"), name);
|
|
|
|
err = zfs_error(hdl, EZFS_EXISTS, errbuf);
|
|
|
|
goto out;
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
if (ioctl(hdl->libzfs_fd, ZFS_IOC_SNAPSHOT_LIST_NEXT,
|
|
|
|
&zc) == 0) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"destination has snapshots (eg. %s)\n"
|
|
|
|
"must destroy them to overwrite it"),
|
2018-12-05 17:33:52 +00:00
|
|
|
zc.zc_name);
|
2016-06-10 00:04:12 +00:00
|
|
|
err = zfs_error(hdl, EZFS_EXISTS, errbuf);
|
|
|
|
goto out;
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
2019-02-08 23:44:15 +00:00
|
|
|
if (is_volume && strrchr(name, '/') == NULL) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"destination %s is the root dataset\n"
|
|
|
|
"cannot overwrite with a ZVOL"),
|
|
|
|
name);
|
|
|
|
err = zfs_error(hdl, EZFS_EXISTS, errbuf);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (is_volume &&
|
|
|
|
ioctl(hdl->libzfs_fd, ZFS_IOC_DATASET_LIST_NEXT,
|
|
|
|
&zc) == 0) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"destination has children (eg. %s)\n"
|
|
|
|
"cannot overwrite with a ZVOL"),
|
|
|
|
zc.zc_name);
|
|
|
|
err = zfs_error(hdl, EZFS_WRONG_PARENT, errbuf);
|
|
|
|
goto out;
|
|
|
|
}
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
2016-06-10 00:04:12 +00:00
|
|
|
if ((zhp = zfs_open(hdl, name,
|
2008-11-20 20:01:55 +00:00
|
|
|
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)) == NULL) {
|
2016-06-10 00:04:12 +00:00
|
|
|
err = -1;
|
|
|
|
goto out;
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (stream_wantsnewfs &&
|
|
|
|
zhp->zfs_dmustats.dds_origin[0]) {
|
|
|
|
zfs_close(zhp);
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"destination '%s' is a clone\n"
|
2016-06-10 00:04:12 +00:00
|
|
|
"must destroy it to overwrite it"), name);
|
|
|
|
err = zfs_error(hdl, EZFS_EXISTS, errbuf);
|
|
|
|
goto out;
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
/*
|
2018-02-21 20:30:11 +00:00
|
|
|
* Raw sends can not be performed as an incremental on top
|
|
|
|
* of existing unencryppted datasets. zfs recv -F cant be
|
|
|
|
* used to blow away an existing encrypted filesystem. This
|
|
|
|
* is because it would require the dsl dir to point to the
|
|
|
|
* new key (or lack of a key) and the old key at the same
|
|
|
|
* time. The -F flag may still be used for deleting
|
|
|
|
* intermediate snapshots that would otherwise prevent the
|
|
|
|
* receive from working.
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
*/
|
2018-02-21 20:30:11 +00:00
|
|
|
encrypted = zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION) !=
|
|
|
|
ZIO_CRYPT_OFF;
|
|
|
|
if (!stream_wantsnewfs && !encrypted && raw) {
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
zfs_close(zhp);
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
2018-02-21 20:30:11 +00:00
|
|
|
"cannot perform raw receive on top of "
|
|
|
|
"existing unencrypted dataset"));
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
err = zfs_error(hdl, EZFS_BADRESTORE, errbuf);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2018-02-21 20:30:11 +00:00
|
|
|
if (stream_wantsnewfs && flags->force &&
|
|
|
|
((raw && !encrypted) || encrypted)) {
|
|
|
|
zfs_close(zhp);
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"zfs receive -F cannot be used to destroy an "
|
|
|
|
"encrypted filesystem or overwrite an "
|
|
|
|
"unencrypted one with an encrypted one"));
|
|
|
|
err = zfs_error(hdl, EZFS_BADRESTORE, errbuf);
|
|
|
|
goto out;
|
|
|
|
}
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
|
2011-11-17 18:14:36 +00:00
|
|
|
if (!flags->dryrun && zhp->zfs_type == ZFS_TYPE_FILESYSTEM &&
|
2008-11-20 20:01:55 +00:00
|
|
|
stream_wantsnewfs) {
|
|
|
|
/* We can't do online recv in this case */
|
2008-12-03 20:09:06 +00:00
|
|
|
clp = changelist_gather(zhp, ZFS_PROP_NAME, 0, 0);
|
2008-11-20 20:01:55 +00:00
|
|
|
if (clp == NULL) {
|
2009-08-18 18:43:27 +00:00
|
|
|
zfs_close(zhp);
|
2016-06-10 00:04:12 +00:00
|
|
|
err = -1;
|
|
|
|
goto out;
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
if (changelist_prefix(clp) != 0) {
|
|
|
|
changelist_free(clp);
|
2009-08-18 18:43:27 +00:00
|
|
|
zfs_close(zhp);
|
2016-06-10 00:04:12 +00:00
|
|
|
err = -1;
|
|
|
|
goto out;
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
}
|
2016-01-06 21:22:48 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If we are resuming a newfs, set newfs here so that we will
|
|
|
|
* mount it if the recv succeeds this time. We can tell
|
|
|
|
* that it was a newfs on the first recv because the fs
|
|
|
|
* itself will be inconsistent (if the fs existed when we
|
|
|
|
* did the first recv, we would have received it into
|
|
|
|
* .../%recv).
|
|
|
|
*/
|
|
|
|
if (resuming && zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT))
|
|
|
|
newfs = B_TRUE;
|
|
|
|
|
2017-05-09 23:21:09 +00:00
|
|
|
/* we want to know if we're zoned when validating -o|-x props */
|
|
|
|
zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
|
|
|
|
|
2018-02-12 20:28:59 +00:00
|
|
|
/* may need this info later, get it now we have zhp around */
|
|
|
|
if (zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN, NULL, 0,
|
|
|
|
NULL, NULL, 0, B_TRUE) == 0)
|
|
|
|
hastoken = B_TRUE;
|
|
|
|
|
2017-05-09 23:21:09 +00:00
|
|
|
/* gather existing properties on destination */
|
|
|
|
origprops = fnvlist_alloc();
|
|
|
|
fnvlist_merge(origprops, zhp->zfs_props);
|
|
|
|
fnvlist_merge(origprops, zhp->zfs_user_props);
|
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
zfs_close(zhp);
|
|
|
|
} else {
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
zfs_handle_t *zhp;
|
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
/*
|
|
|
|
* Destination filesystem does not exist. Therefore we better
|
|
|
|
* be creating a new filesystem (either from a full backup, or
|
|
|
|
* a clone). It would therefore be invalid if the user
|
|
|
|
* specified only the pool name (i.e. if the destination name
|
|
|
|
* contained no slash character).
|
|
|
|
*/
|
2016-07-19 16:24:24 +00:00
|
|
|
cp = strrchr(name, '/');
|
|
|
|
|
|
|
|
if (!stream_wantsnewfs || cp == NULL) {
|
2008-11-20 20:01:55 +00:00
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
2016-06-10 00:04:12 +00:00
|
|
|
"destination '%s' does not exist"), name);
|
|
|
|
err = zfs_error(hdl, EZFS_NOENT, errbuf);
|
|
|
|
goto out;
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Trim off the final dataset component so we perform the
|
|
|
|
* recvbackup ioctl to the filesystems's parent.
|
|
|
|
*/
|
|
|
|
*cp = '\0';
|
|
|
|
|
2011-11-17 18:14:36 +00:00
|
|
|
if (flags->isprefix && !flags->istail && !flags->dryrun &&
|
2016-06-10 00:04:12 +00:00
|
|
|
create_parents(hdl, destsnap, strlen(tosnap)) != 0) {
|
|
|
|
err = zfs_error(hdl, EZFS_BADRESTORE, errbuf);
|
|
|
|
goto out;
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
2019-02-08 23:44:15 +00:00
|
|
|
/* validate parent */
|
|
|
|
zhp = zfs_open(hdl, name, ZFS_TYPE_DATASET);
|
|
|
|
if (zhp == NULL) {
|
|
|
|
err = zfs_error(hdl, EZFS_BADRESTORE, errbuf);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"parent '%s' is not a filesystem"), name);
|
|
|
|
err = zfs_error(hdl, EZFS_WRONG_PARENT, errbuf);
|
|
|
|
zfs_close(zhp);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
/*
|
|
|
|
* It is invalid to receive a properties stream that was
|
|
|
|
* unencrypted on the send side as a child of an encrypted
|
|
|
|
* parent. Technically there is nothing preventing this, but
|
|
|
|
* it would mean that the encryption=off property which is
|
|
|
|
* locally set on the send side would not be received correctly.
|
|
|
|
* We can infer encryption=off if the stream is not raw and
|
|
|
|
* properties were included since the send side will only ever
|
2017-10-13 17:09:04 +00:00
|
|
|
* send the encryption property in a raw nvlist header. This
|
|
|
|
* check will be avoided if the user specifically overrides
|
|
|
|
* the encryption property on the command line.
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
*/
|
2017-10-13 17:09:04 +00:00
|
|
|
if (!raw && rcvprops != NULL &&
|
|
|
|
!nvlist_exists(cmdprops,
|
|
|
|
zfs_prop_to_name(ZFS_PROP_ENCRYPTION))) {
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
uint64_t crypt;
|
|
|
|
|
|
|
|
crypt = zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION);
|
|
|
|
|
|
|
|
if (crypt != ZIO_CRYPT_OFF) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"parent '%s' must not be encrypted to "
|
|
|
|
"receive unenecrypted property"), name);
|
|
|
|
err = zfs_error(hdl, EZFS_BADPROP, errbuf);
|
2019-02-08 23:44:15 +00:00
|
|
|
zfs_close(zhp);
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
2019-02-08 23:44:15 +00:00
|
|
|
zfs_close(zhp);
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
newfs = B_TRUE;
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
*cp = '/';
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
2011-11-17 18:14:36 +00:00
|
|
|
if (flags->verbose) {
|
2008-11-20 20:01:55 +00:00
|
|
|
(void) printf("%s %s stream of %s into %s\n",
|
2011-11-17 18:14:36 +00:00
|
|
|
flags->dryrun ? "would receive" : "receiving",
|
2008-11-20 20:01:55 +00:00
|
|
|
drrb->drr_fromguid ? "incremental" : "full",
|
2016-06-10 00:04:12 +00:00
|
|
|
drrb->drr_toname, destsnap);
|
2008-11-20 20:01:55 +00:00
|
|
|
(void) fflush(stdout);
|
|
|
|
}
|
|
|
|
|
2011-11-17 18:14:36 +00:00
|
|
|
if (flags->dryrun) {
|
2016-06-10 00:04:12 +00:00
|
|
|
err = recv_skip(hdl, infd, flags->byteswap);
|
|
|
|
goto out;
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
2018-05-01 03:58:29 +00:00
|
|
|
if (top_zfs && (*top_zfs == NULL || strcmp(*top_zfs, name) == 0))
|
2018-01-30 23:54:33 +00:00
|
|
|
toplevel = B_TRUE;
|
2017-05-09 23:21:09 +00:00
|
|
|
if (drrb->drr_type == DMU_OST_ZVOL) {
|
|
|
|
type = ZFS_TYPE_VOLUME;
|
|
|
|
} else if (drrb->drr_type == DMU_OST_ZFS) {
|
|
|
|
type = ZFS_TYPE_FILESYSTEM;
|
|
|
|
} else {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"invalid record type: 0x%d"), drrb->drr_type);
|
|
|
|
err = zfs_error(hdl, EZFS_BADSTREAM, errbuf);
|
|
|
|
goto out;
|
|
|
|
}
|
2017-10-13 17:09:04 +00:00
|
|
|
if ((err = zfs_setup_cmdline_props(hdl, type, name, zoned, recursive,
|
|
|
|
stream_wantsnewfs, raw, toplevel, rcvprops, cmdprops, origprops,
|
|
|
|
&oxprops, &wkeydata, &wkeylen, errbuf)) != 0)
|
2017-05-09 23:21:09 +00:00
|
|
|
goto out;
|
|
|
|
|
2017-10-13 17:09:04 +00:00
|
|
|
err = ioctl_err = lzc_receive_with_cmdprops(destsnap, rcvprops,
|
|
|
|
oxprops, wkeydata, wkeylen, origin, flags->force, flags->resumable,
|
|
|
|
raw, infd, drr_noswap, cleanup_fd, &read_bytes, &errflags,
|
|
|
|
action_handlep, &prop_errors);
|
2016-06-10 00:04:12 +00:00
|
|
|
ioctl_errno = ioctl_err;
|
|
|
|
prop_errflags = errflags;
|
2010-05-28 20:45:14 +00:00
|
|
|
|
|
|
|
if (err == 0) {
|
|
|
|
nvpair_t *prop_err = NULL;
|
|
|
|
|
|
|
|
while ((prop_err = nvlist_next_nvpair(prop_errors,
|
|
|
|
prop_err)) != NULL) {
|
|
|
|
char tbuf[1024];
|
|
|
|
zfs_prop_t prop;
|
|
|
|
int intval;
|
|
|
|
|
|
|
|
prop = zfs_name_to_prop(nvpair_name(prop_err));
|
|
|
|
(void) nvpair_value_int32(prop_err, &intval);
|
|
|
|
if (strcmp(nvpair_name(prop_err),
|
|
|
|
ZPROP_N_MORE_ERRORS) == 0) {
|
|
|
|
trunc_prop_errs(intval);
|
|
|
|
break;
|
2016-06-09 19:24:29 +00:00
|
|
|
} else if (snapname == NULL || finalsnap == NULL ||
|
|
|
|
strcmp(finalsnap, snapname) == 0 ||
|
|
|
|
strcmp(nvpair_name(prop_err),
|
|
|
|
zfs_prop_to_name(ZFS_PROP_REFQUOTA)) != 0) {
|
|
|
|
/*
|
|
|
|
* Skip the special case of, for example,
|
|
|
|
* "refquota", errors on intermediate
|
|
|
|
* snapshots leading up to a final one.
|
|
|
|
* That's why we have all of the checks above.
|
|
|
|
*
|
|
|
|
* See zfs_ioctl.c's extract_delay_props() for
|
|
|
|
* a list of props which can fail on
|
|
|
|
* intermediate snapshots, but shouldn't
|
|
|
|
* affect the overall receive.
|
|
|
|
*/
|
2010-05-28 20:45:14 +00:00
|
|
|
(void) snprintf(tbuf, sizeof (tbuf),
|
|
|
|
dgettext(TEXT_DOMAIN,
|
|
|
|
"cannot receive %s property on %s"),
|
2016-06-10 00:04:12 +00:00
|
|
|
nvpair_name(prop_err), name);
|
2010-05-28 20:45:14 +00:00
|
|
|
zfs_setprop_error(hdl, prop, intval, tbuf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-12-03 20:09:06 +00:00
|
|
|
if (err == 0 && snapprops_nvlist) {
|
2016-06-10 00:04:12 +00:00
|
|
|
zfs_cmd_t zc = {"\0"};
|
2008-12-03 20:09:06 +00:00
|
|
|
|
2016-06-10 00:04:12 +00:00
|
|
|
(void) strcpy(zc.zc_name, destsnap);
|
|
|
|
zc.zc_cookie = B_TRUE; /* received */
|
|
|
|
if (zcmd_write_src_nvlist(hdl, &zc, snapprops_nvlist) == 0) {
|
|
|
|
(void) zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc);
|
|
|
|
zcmd_free_nvlists(&zc);
|
2008-12-03 20:09:06 +00:00
|
|
|
}
|
|
|
|
}
|
2019-02-15 20:41:38 +00:00
|
|
|
if (err == 0 && snapholds_nvlist) {
|
|
|
|
nvpair_t *pair;
|
|
|
|
nvlist_t *holds, *errors = NULL;
|
|
|
|
int cleanup_fd = -1;
|
|
|
|
|
|
|
|
VERIFY(0 == nvlist_alloc(&holds, 0, KM_SLEEP));
|
|
|
|
for (pair = nvlist_next_nvpair(snapholds_nvlist, NULL);
|
|
|
|
pair != NULL;
|
|
|
|
pair = nvlist_next_nvpair(snapholds_nvlist, pair)) {
|
|
|
|
VERIFY(0 == nvlist_add_string(holds, destsnap,
|
|
|
|
nvpair_name(pair)));
|
|
|
|
}
|
|
|
|
(void) lzc_hold(holds, cleanup_fd, &errors);
|
|
|
|
nvlist_free(snapholds_nvlist);
|
|
|
|
nvlist_free(holds);
|
|
|
|
}
|
2008-12-03 20:09:06 +00:00
|
|
|
|
2010-05-28 20:45:14 +00:00
|
|
|
if (err && (ioctl_errno == ENOENT || ioctl_errno == EEXIST)) {
|
2008-11-20 20:01:55 +00:00
|
|
|
/*
|
|
|
|
* It may be that this snapshot already exists,
|
|
|
|
* in which case we want to consume & ignore it
|
|
|
|
* rather than failing.
|
|
|
|
*/
|
|
|
|
avl_tree_t *local_avl;
|
|
|
|
nvlist_t *local_nv, *fs;
|
2016-06-10 00:04:12 +00:00
|
|
|
cp = strchr(destsnap, '@');
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX Do this faster by just iterating over snaps in
|
|
|
|
* this fs. Also if zc_value does not exist, we will
|
|
|
|
* get a strange "does not exist" error message.
|
|
|
|
*/
|
|
|
|
*cp = '\0';
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
if (gather_nvlist(hdl, destsnap, NULL, NULL, B_FALSE, B_TRUE,
|
2019-03-12 20:13:22 +00:00
|
|
|
B_FALSE, B_FALSE, B_FALSE, B_FALSE, B_TRUE, &local_nv,
|
|
|
|
&local_avl) == 0) {
|
2008-11-20 20:01:55 +00:00
|
|
|
*cp = '@';
|
|
|
|
fs = fsavl_find(local_avl, drrb->drr_toguid, NULL);
|
|
|
|
fsavl_destroy(local_avl);
|
|
|
|
nvlist_free(local_nv);
|
|
|
|
|
|
|
|
if (fs != NULL) {
|
2011-11-17 18:14:36 +00:00
|
|
|
if (flags->verbose) {
|
2008-11-20 20:01:55 +00:00
|
|
|
(void) printf("snap %s already exists; "
|
2016-06-10 00:04:12 +00:00
|
|
|
"ignoring\n", destsnap);
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
2010-05-28 20:45:14 +00:00
|
|
|
err = ioctl_err = recv_skip(hdl, infd,
|
2011-11-17 18:14:36 +00:00
|
|
|
flags->byteswap);
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
*cp = '@';
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ioctl_err != 0) {
|
|
|
|
switch (ioctl_errno) {
|
|
|
|
case ENODEV:
|
2016-06-10 00:04:12 +00:00
|
|
|
cp = strchr(destsnap, '@');
|
2008-11-20 20:01:55 +00:00
|
|
|
*cp = '\0';
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"most recent snapshot of %s does not\n"
|
2016-06-10 00:04:12 +00:00
|
|
|
"match incremental source"), destsnap);
|
2008-11-20 20:01:55 +00:00
|
|
|
(void) zfs_error(hdl, EZFS_BADRESTORE, errbuf);
|
|
|
|
*cp = '@';
|
|
|
|
break;
|
|
|
|
case ETXTBSY:
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"destination %s has been modified\n"
|
2016-06-10 00:04:12 +00:00
|
|
|
"since most recent snapshot"), name);
|
2008-11-20 20:01:55 +00:00
|
|
|
(void) zfs_error(hdl, EZFS_BADRESTORE, errbuf);
|
|
|
|
break;
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
case EACCES:
|
|
|
|
if (raw && stream_wantsnewfs) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"failed to create encryption key"));
|
|
|
|
} else if (raw && !stream_wantsnewfs) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"encryption key does not match "
|
|
|
|
"existing key"));
|
|
|
|
} else {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"inherited key must be loaded"));
|
|
|
|
}
|
|
|
|
(void) zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf);
|
|
|
|
break;
|
2008-11-20 20:01:55 +00:00
|
|
|
case EEXIST:
|
2016-06-10 00:04:12 +00:00
|
|
|
cp = strchr(destsnap, '@');
|
2008-11-20 20:01:55 +00:00
|
|
|
if (newfs) {
|
|
|
|
/* it's the containing fs that exists */
|
|
|
|
*cp = '\0';
|
|
|
|
}
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"destination already exists"));
|
|
|
|
(void) zfs_error_fmt(hdl, EZFS_EXISTS,
|
|
|
|
dgettext(TEXT_DOMAIN, "cannot restore to %s"),
|
2016-06-10 00:04:12 +00:00
|
|
|
destsnap);
|
2008-11-20 20:01:55 +00:00
|
|
|
*cp = '@';
|
|
|
|
break;
|
|
|
|
case EINVAL:
|
2016-06-10 00:04:12 +00:00
|
|
|
if (flags->resumable)
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"kernel modules must be upgraded to "
|
|
|
|
"receive this stream."));
|
2017-08-23 23:54:24 +00:00
|
|
|
if (embedded && !raw)
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"incompatible embedded data stream "
|
|
|
|
"feature with encrypted receive."));
|
2008-11-20 20:01:55 +00:00
|
|
|
(void) zfs_error(hdl, EZFS_BADSTREAM, errbuf);
|
|
|
|
break;
|
|
|
|
case ECKSUM:
|
2016-06-10 00:04:12 +00:00
|
|
|
recv_ecksum_set_aux(hdl, destsnap, flags->resumable);
|
2008-11-20 20:01:55 +00:00
|
|
|
(void) zfs_error(hdl, EZFS_BADSTREAM, errbuf);
|
|
|
|
break;
|
2010-05-28 20:45:14 +00:00
|
|
|
case ENOTSUP:
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"pool must be upgraded to receive this stream."));
|
|
|
|
(void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
|
|
|
|
break;
|
|
|
|
case EDQUOT:
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
2018-02-12 20:28:59 +00:00
|
|
|
"destination %s space quota exceeded."), name);
|
2011-11-17 18:14:36 +00:00
|
|
|
(void) zfs_error(hdl, EZFS_NOSPC, errbuf);
|
2010-05-28 20:45:14 +00:00
|
|
|
break;
|
2019-02-04 19:24:55 +00:00
|
|
|
case ZFS_ERR_FROM_IVSET_GUID_MISSING:
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"IV set guid missing. See errata %u at"
|
|
|
|
"http://zfsonlinux.org/msg/ZFS-8000-ER"),
|
|
|
|
ZPOOL_ERRATA_ZOL_8308_ENCRYPTION);
|
|
|
|
(void) zfs_error(hdl, EZFS_BADSTREAM, errbuf);
|
|
|
|
break;
|
|
|
|
case ZFS_ERR_FROM_IVSET_GUID_MISMATCH:
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"IV set guid mismatch. See the 'zfs receive' "
|
|
|
|
"man page section\n discussing the limitations "
|
|
|
|
"of raw encrypted send streams."));
|
|
|
|
(void) zfs_error(hdl, EZFS_BADSTREAM, errbuf);
|
|
|
|
break;
|
2018-02-12 20:28:59 +00:00
|
|
|
case EBUSY:
|
|
|
|
if (hastoken) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"destination %s contains "
|
|
|
|
"partially-complete state from "
|
|
|
|
"\"zfs receive -s\"."), name);
|
|
|
|
(void) zfs_error(hdl, EZFS_BUSY, errbuf);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* fallthru */
|
2008-11-20 20:01:55 +00:00
|
|
|
default:
|
|
|
|
(void) zfs_standard_error(hdl, ioctl_errno, errbuf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2010-05-28 20:45:14 +00:00
|
|
|
* Mount the target filesystem (if created). Also mount any
|
|
|
|
* children of the target filesystem if we did a replication
|
|
|
|
* receive (indicated by stream_avl being non-NULL).
|
2008-11-20 20:01:55 +00:00
|
|
|
*/
|
2016-06-10 00:04:12 +00:00
|
|
|
cp = strchr(destsnap, '@');
|
2008-11-20 20:01:55 +00:00
|
|
|
if (cp && (ioctl_err == 0 || !newfs)) {
|
|
|
|
zfs_handle_t *h;
|
|
|
|
|
|
|
|
*cp = '\0';
|
2016-06-10 00:04:12 +00:00
|
|
|
h = zfs_open(hdl, destsnap,
|
2008-11-20 20:01:55 +00:00
|
|
|
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
|
|
|
|
if (h != NULL) {
|
|
|
|
if (h->zfs_type == ZFS_TYPE_VOLUME) {
|
2008-12-03 20:09:06 +00:00
|
|
|
*cp = '@';
|
2009-08-18 18:43:27 +00:00
|
|
|
} else if (newfs || stream_avl) {
|
2008-12-03 20:09:06 +00:00
|
|
|
/*
|
|
|
|
* Track the first/top of hierarchy fs,
|
|
|
|
* for mounting and sharing later.
|
|
|
|
*/
|
|
|
|
if (top_zfs && *top_zfs == NULL)
|
2016-06-10 00:04:12 +00:00
|
|
|
*top_zfs = zfs_strdup(hdl, destsnap);
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
zfs_close(h);
|
|
|
|
}
|
2008-12-03 20:09:06 +00:00
|
|
|
*cp = '@';
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (clp) {
|
2017-01-26 20:42:48 +00:00
|
|
|
if (!flags->nomount)
|
|
|
|
err |= changelist_postfix(clp);
|
2008-11-20 20:01:55 +00:00
|
|
|
changelist_free(clp);
|
|
|
|
}
|
|
|
|
|
2010-05-28 20:45:14 +00:00
|
|
|
if (prop_errflags & ZPROP_ERR_NOCLEAR) {
|
|
|
|
(void) fprintf(stderr, dgettext(TEXT_DOMAIN, "Warning: "
|
2016-06-10 00:04:12 +00:00
|
|
|
"failed to clear unreceived properties on %s"), name);
|
2010-05-28 20:45:14 +00:00
|
|
|
(void) fprintf(stderr, "\n");
|
|
|
|
}
|
|
|
|
if (prop_errflags & ZPROP_ERR_NORESTORE) {
|
|
|
|
(void) fprintf(stderr, dgettext(TEXT_DOMAIN, "Warning: "
|
2016-06-10 00:04:12 +00:00
|
|
|
"failed to restore original properties on %s"), name);
|
2010-05-28 20:45:14 +00:00
|
|
|
(void) fprintf(stderr, "\n");
|
|
|
|
}
|
|
|
|
|
2016-06-10 00:04:12 +00:00
|
|
|
if (err || ioctl_err) {
|
|
|
|
err = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
2010-08-26 21:24:34 +00:00
|
|
|
|
2011-11-17 18:14:36 +00:00
|
|
|
if (flags->verbose) {
|
2008-11-20 20:01:55 +00:00
|
|
|
char buf1[64];
|
|
|
|
char buf2[64];
|
2016-06-10 00:04:12 +00:00
|
|
|
uint64_t bytes = read_bytes;
|
2008-11-20 20:01:55 +00:00
|
|
|
time_t delta = time(NULL) - begin_time;
|
|
|
|
if (delta == 0)
|
|
|
|
delta = 1;
|
2017-05-02 20:43:53 +00:00
|
|
|
zfs_nicebytes(bytes, buf1, sizeof (buf1));
|
|
|
|
zfs_nicebytes(bytes/delta, buf2, sizeof (buf1));
|
2008-11-20 20:01:55 +00:00
|
|
|
|
2017-05-02 20:43:53 +00:00
|
|
|
(void) printf("received %s stream in %lu seconds (%s/sec)\n",
|
2008-11-20 20:01:55 +00:00
|
|
|
buf1, delta, buf2);
|
|
|
|
}
|
|
|
|
|
2016-06-10 00:04:12 +00:00
|
|
|
err = 0;
|
|
|
|
out:
|
|
|
|
if (prop_errors != NULL)
|
|
|
|
nvlist_free(prop_errors);
|
|
|
|
|
Native Encryption for ZFS on Linux
This change incorporates three major pieces:
The first change is a keystore that manages wrapping
and encryption keys for encrypted datasets. These
commands mostly involve manipulating the new
DSL Crypto Key ZAP Objects that live in the MOS. Each
encrypted dataset has its own DSL Crypto Key that is
protected with a user's key. This level of indirection
allows users to change their keys without re-encrypting
their entire datasets. The change implements the new
subcommands "zfs load-key", "zfs unload-key" and
"zfs change-key" which allow the user to manage their
encryption keys and settings. In addition, several new
flags and properties have been added to allow dataset
creation and to make mounting and unmounting more
convenient.
The second piece of this patch provides the ability to
encrypt, decyrpt, and authenticate protected datasets.
Each object set maintains a Merkel tree of Message
Authentication Codes that protect the lower layers,
similarly to how checksums are maintained. This part
impacts the zio layer, which handles the actual
encryption and generation of MACs, as well as the ARC
and DMU, which need to be able to handle encrypted
buffers and protected data.
The last addition is the ability to do raw, encrypted
sends and receives. The idea here is to send raw
encrypted and compressed data and receive it exactly
as is on a backup system. This means that the dataset
on the receiving system is protected using the same
user key that is in use on the sending side. By doing
so, datasets can be efficiently backed up to an
untrusted system without fear of data being
compromised.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #494
Closes #5769
2017-08-14 17:36:48 +00:00
|
|
|
if (tmp_keylocation[0] != '\0') {
|
|
|
|
VERIFY(0 == nvlist_add_string(rcvprops,
|
|
|
|
zfs_prop_to_name(ZFS_PROP_KEYLOCATION), tmp_keylocation));
|
|
|
|
}
|
|
|
|
|
2016-06-10 00:04:12 +00:00
|
|
|
if (newprops)
|
2017-05-09 23:21:09 +00:00
|
|
|
nvlist_free(rcvprops);
|
|
|
|
|
|
|
|
nvlist_free(oxprops);
|
|
|
|
nvlist_free(origprops);
|
2016-06-10 00:04:12 +00:00
|
|
|
|
|
|
|
return (err);
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
2017-05-09 23:21:09 +00:00
|
|
|
/*
|
|
|
|
* Check properties we were asked to override (both -o|-x)
|
|
|
|
*/
|
|
|
|
static boolean_t
|
|
|
|
zfs_receive_checkprops(libzfs_handle_t *hdl, nvlist_t *props,
|
|
|
|
const char *errbuf)
|
|
|
|
{
|
|
|
|
nvpair_t *nvp;
|
|
|
|
zfs_prop_t prop;
|
|
|
|
const char *name;
|
|
|
|
|
|
|
|
nvp = NULL;
|
|
|
|
while ((nvp = nvlist_next_nvpair(props, nvp)) != NULL) {
|
|
|
|
name = nvpair_name(nvp);
|
|
|
|
prop = zfs_name_to_prop(name);
|
|
|
|
|
|
|
|
if (prop == ZPROP_INVAL) {
|
|
|
|
if (!zfs_prop_user(name)) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"invalid property '%s'"), name);
|
|
|
|
return (B_FALSE);
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* "origin" is readonly but is used to receive datasets as
|
|
|
|
* clones so we don't raise an error here
|
|
|
|
*/
|
|
|
|
if (prop == ZFS_PROP_ORIGIN)
|
|
|
|
continue;
|
|
|
|
|
2017-10-13 17:09:04 +00:00
|
|
|
/* encryption params have their own verification later */
|
|
|
|
if (prop == ZFS_PROP_ENCRYPTION ||
|
|
|
|
zfs_prop_encryption_key_param(prop))
|
|
|
|
continue;
|
|
|
|
|
2017-05-09 23:21:09 +00:00
|
|
|
/*
|
|
|
|
* cannot override readonly, set-once and other specific
|
|
|
|
* settable properties
|
|
|
|
*/
|
|
|
|
if (zfs_prop_readonly(prop) || prop == ZFS_PROP_VERSION ||
|
|
|
|
prop == ZFS_PROP_VOLSIZE) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"invalid property '%s'"), name);
|
|
|
|
return (B_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (B_TRUE);
|
|
|
|
}
|
|
|
|
|
2008-12-03 20:09:06 +00:00
|
|
|
static int
|
2015-12-22 01:31:57 +00:00
|
|
|
zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap,
|
|
|
|
const char *originsnap, recvflags_t *flags, int infd, const char *sendfs,
|
|
|
|
nvlist_t *stream_nv, avl_tree_t *stream_avl, char **top_zfs, int cleanup_fd,
|
2017-05-09 23:21:09 +00:00
|
|
|
uint64_t *action_handlep, const char *finalsnap, nvlist_t *cmdprops)
|
2008-11-20 20:01:55 +00:00
|
|
|
{
|
|
|
|
int err;
|
|
|
|
dmu_replay_record_t drr, drr_noswap;
|
|
|
|
struct drr_begin *drrb = &drr.drr_u.drr_begin;
|
|
|
|
char errbuf[1024];
|
2010-08-26 16:52:41 +00:00
|
|
|
zio_cksum_t zcksum = { { 0 } };
|
2010-05-28 20:45:14 +00:00
|
|
|
uint64_t featureflags;
|
|
|
|
int hdrtype;
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
|
|
|
|
"cannot receive"));
|
|
|
|
|
2017-05-09 23:21:09 +00:00
|
|
|
/* check cmdline props, raise an error if they cannot be received */
|
|
|
|
if (!zfs_receive_checkprops(hdl, cmdprops, errbuf)) {
|
|
|
|
return (zfs_error(hdl, EZFS_BADPROP, errbuf));
|
|
|
|
}
|
|
|
|
|
2011-11-17 18:14:36 +00:00
|
|
|
if (flags->isprefix &&
|
2008-11-20 20:01:55 +00:00
|
|
|
!zfs_dataset_exists(hdl, tosnap, ZFS_TYPE_DATASET)) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "specified fs "
|
|
|
|
"(%s) does not exist"), tosnap);
|
|
|
|
return (zfs_error(hdl, EZFS_NOENT, errbuf));
|
|
|
|
}
|
2015-12-22 01:31:57 +00:00
|
|
|
if (originsnap &&
|
|
|
|
!zfs_dataset_exists(hdl, originsnap, ZFS_TYPE_DATASET)) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "specified origin fs "
|
|
|
|
"(%s) does not exist"), originsnap);
|
|
|
|
return (zfs_error(hdl, EZFS_NOENT, errbuf));
|
|
|
|
}
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
/* read in the BEGIN record */
|
|
|
|
if (0 != (err = recv_read(hdl, infd, &drr, sizeof (drr), B_FALSE,
|
|
|
|
&zcksum)))
|
|
|
|
return (err);
|
|
|
|
|
|
|
|
if (drr.drr_type == DRR_END || drr.drr_type == BSWAP_32(DRR_END)) {
|
|
|
|
/* It's the double end record at the end of a package */
|
|
|
|
return (ENODATA);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* the kernel needs the non-byteswapped begin record */
|
|
|
|
drr_noswap = drr;
|
|
|
|
|
2011-11-17 18:14:36 +00:00
|
|
|
flags->byteswap = B_FALSE;
|
2008-11-20 20:01:55 +00:00
|
|
|
if (drrb->drr_magic == BSWAP_64(DMU_BACKUP_MAGIC)) {
|
|
|
|
/*
|
|
|
|
* We computed the checksum in the wrong byteorder in
|
|
|
|
* recv_read() above; do it again correctly.
|
|
|
|
*/
|
|
|
|
bzero(&zcksum, sizeof (zio_cksum_t));
|
|
|
|
fletcher_4_incremental_byteswap(&drr, sizeof (drr), &zcksum);
|
2011-11-17 18:14:36 +00:00
|
|
|
flags->byteswap = B_TRUE;
|
2008-11-20 20:01:55 +00:00
|
|
|
|
|
|
|
drr.drr_type = BSWAP_32(drr.drr_type);
|
|
|
|
drr.drr_payloadlen = BSWAP_32(drr.drr_payloadlen);
|
|
|
|
drrb->drr_magic = BSWAP_64(drrb->drr_magic);
|
2010-05-28 20:45:14 +00:00
|
|
|
drrb->drr_versioninfo = BSWAP_64(drrb->drr_versioninfo);
|
2008-11-20 20:01:55 +00:00
|
|
|
drrb->drr_creation_time = BSWAP_64(drrb->drr_creation_time);
|
|
|
|
drrb->drr_type = BSWAP_32(drrb->drr_type);
|
|
|
|
drrb->drr_flags = BSWAP_32(drrb->drr_flags);
|
|
|
|
drrb->drr_toguid = BSWAP_64(drrb->drr_toguid);
|
|
|
|
drrb->drr_fromguid = BSWAP_64(drrb->drr_fromguid);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (drrb->drr_magic != DMU_BACKUP_MAGIC || drr.drr_type != DRR_BEGIN) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
|
|
|
|
"stream (bad magic number)"));
|
|
|
|
return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
|
|
|
|
}
|
|
|
|
|
2010-05-28 20:45:14 +00:00
|
|
|
featureflags = DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo);
|
|
|
|
hdrtype = DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo);
|
|
|
|
|
|
|
|
if (!DMU_STREAM_SUPPORTED(featureflags) ||
|
|
|
|
(hdrtype != DMU_SUBSTREAM && hdrtype != DMU_COMPOUNDSTREAM)) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
|
|
|
"stream has unsupported feature, feature flags = %lx"),
|
|
|
|
featureflags);
|
|
|
|
return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
|
|
|
|
}
|
|
|
|
|
2019-02-15 20:41:38 +00:00
|
|
|
/* Holds feature is set once in the compound stream header. */
|
|
|
|
boolean_t holds = (DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo) &
|
|
|
|
DMU_BACKUP_FEATURE_HOLDS);
|
|
|
|
if (holds)
|
|
|
|
flags->holds = B_TRUE;
|
|
|
|
|
2008-11-20 20:01:55 +00:00
|
|
|
if (strchr(drrb->drr_toname, '@') == NULL) {
|
|
|
|
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
|
|
|
|
"stream (bad snapshot name)"));
|
|
|
|
return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
|
|
|
|
}
|
|
|
|
|
2010-05-28 20:45:14 +00:00
|
|
|
if (DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo) == DMU_SUBSTREAM) {
|
2016-06-15 21:28:36 +00:00
|
|
|
char nonpackage_sendfs[ZFS_MAX_DATASET_NAME_LEN];
|
2010-05-28 20:45:14 +00:00
|
|
|
if (sendfs == NULL) {
|
|
|
|
/*
|
|
|
|
* We were not called from zfs_receive_package(). Get
|
|
|
|
* the fs specified by 'zfs send'.
|
|
|
|
*/
|
|
|
|
char *cp;
|
|
|
|
(void) strlcpy(nonpackage_sendfs,
|
2016-06-15 21:28:36 +00:00
|
|
|
drr.drr_u.drr_begin.drr_toname,
|
|
|
|
sizeof (nonpackage_sendfs));
|
2010-05-28 20:45:14 +00:00
|
|
|
if ((cp = strchr(nonpackage_sendfs, '@')) != NULL)
|
|
|
|
*cp = '\0';
|
|
|
|
sendfs = nonpackage_sendfs;
|
2016-06-09 19:24:29 +00:00
|
|
|
VERIFY(finalsnap == NULL);
|
2010-05-28 20:45:14 +00:00
|
|
|
}
|
2015-12-22 01:31:57 +00:00
|
|
|
return (zfs_receive_one(hdl, infd, tosnap, originsnap, flags,
|
|
|
|
&drr, &drr_noswap, sendfs, stream_nv, stream_avl, top_zfs,
|
2017-05-09 23:21:09 +00:00
|
|
|
cleanup_fd, action_handlep, finalsnap, cmdprops));
|
2010-05-28 20:45:14 +00:00
|
|
|
} else {
|
|
|
|
assert(DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo) ==
|
|
|
|
DMU_COMPOUNDSTREAM);
|
2015-12-22 01:31:57 +00:00
|
|
|
return (zfs_receive_package(hdl, infd, tosnap, flags, &drr,
|
2017-05-09 23:21:09 +00:00
|
|
|
&zcksum, top_zfs, cleanup_fd, action_handlep, cmdprops));
|
2008-11-20 20:01:55 +00:00
|
|
|
}
|
|
|
|
}
|
2008-12-03 20:09:06 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Restores a backup of tosnap from the file descriptor specified by infd.
|
|
|
|
* Return 0 on total success, -2 if some things couldn't be
|
|
|
|
* destroyed/renamed/promoted, -1 if some things couldn't be received.
|
2016-01-06 21:22:48 +00:00
|
|
|
* (-1 will override -2, if -1 and the resumable flag was specified the
|
|
|
|
* transfer can be resumed if the sending side supports it).
|
2008-12-03 20:09:06 +00:00
|
|
|
*/
|
|
|
|
int
|
2015-12-22 01:31:57 +00:00
|
|
|
zfs_receive(libzfs_handle_t *hdl, const char *tosnap, nvlist_t *props,
|
|
|
|
recvflags_t *flags, int infd, avl_tree_t *stream_avl)
|
2008-12-03 20:09:06 +00:00
|
|
|
{
|
|
|
|
char *top_zfs = NULL;
|
|
|
|
int err;
|
2010-08-26 21:24:34 +00:00
|
|
|
int cleanup_fd;
|
|
|
|
uint64_t action_handle = 0;
|
2015-03-11 18:24:46 +00:00
|
|
|
struct stat sb;
|
2015-12-22 01:31:57 +00:00
|
|
|
char *originsnap = NULL;
|
2015-03-11 18:24:46 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The only way fstat can fail is if we do not have a valid file
|
|
|
|
* descriptor.
|
|
|
|
*/
|
|
|
|
if (fstat(infd, &sb) == -1) {
|
|
|
|
perror("fstat");
|
|
|
|
return (-2);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef __linux__
|
|
|
|
#ifndef F_SETPIPE_SZ
|
|
|
|
#define F_SETPIPE_SZ (F_SETLEASE + 7)
|
|
|
|
#endif /* F_SETPIPE_SZ */
|
|
|
|
|
|
|
|
#ifndef F_GETPIPE_SZ
|
|
|
|
#define F_GETPIPE_SZ (F_GETLEASE + 7)
|
|
|
|
#endif /* F_GETPIPE_SZ */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* It is not uncommon for gigabytes to be processed in zfs receive.
|
|
|
|
* Speculatively increase the buffer size via Linux-specific fcntl()
|
|
|
|
* call.
|
|
|
|
*/
|
|
|
|
if (S_ISFIFO(sb.st_mode)) {
|
|
|
|
FILE *procf = fopen("/proc/sys/fs/pipe-max-size", "r");
|
|
|
|
|
|
|
|
if (procf != NULL) {
|
|
|
|
unsigned long max_psize;
|
|
|
|
long cur_psize;
|
|
|
|
if (fscanf(procf, "%lu", &max_psize) > 0) {
|
|
|
|
cur_psize = fcntl(infd, F_GETPIPE_SZ);
|
|
|
|
if (cur_psize > 0 &&
|
|
|
|
max_psize > (unsigned long) cur_psize)
|
|
|
|
(void) fcntl(infd, F_SETPIPE_SZ,
|
|
|
|
max_psize);
|
|
|
|
}
|
|
|
|
fclose(procf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* __linux__ */
|
2010-08-26 21:24:34 +00:00
|
|
|
|
2015-12-22 01:31:57 +00:00
|
|
|
if (props) {
|
|
|
|
err = nvlist_lookup_string(props, "origin", &originsnap);
|
|
|
|
if (err && err != ENOENT)
|
|
|
|
return (err);
|
|
|
|
}
|
|
|
|
|
2010-08-26 18:44:39 +00:00
|
|
|
cleanup_fd = open(ZFS_DEV, O_RDWR);
|
2010-08-26 21:24:34 +00:00
|
|
|
VERIFY(cleanup_fd >= 0);
|
2008-12-03 20:09:06 +00:00
|
|
|
|
2015-12-22 01:31:57 +00:00
|
|
|
err = zfs_receive_impl(hdl, tosnap, originsnap, flags, infd, NULL, NULL,
|
2017-05-09 23:21:09 +00:00
|
|
|
stream_avl, &top_zfs, cleanup_fd, &action_handle, NULL, props);
|
2010-08-26 21:24:34 +00:00
|
|
|
|
|
|
|
VERIFY(0 == close(cleanup_fd));
|
2008-12-03 20:09:06 +00:00
|
|
|
|
2011-11-17 18:14:36 +00:00
|
|
|
if (err == 0 && !flags->nomount && top_zfs) {
|
2016-07-26 19:08:51 +00:00
|
|
|
zfs_handle_t *zhp = NULL;
|
|
|
|
prop_changelist_t *clp = NULL;
|
2008-12-03 20:09:06 +00:00
|
|
|
|
|
|
|
zhp = zfs_open(hdl, top_zfs, ZFS_TYPE_FILESYSTEM);
|
|
|
|
if (zhp != NULL) {
|
|
|
|
clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT,
|
|
|
|
CL_GATHER_MOUNT_ALWAYS, 0);
|
|
|
|
zfs_close(zhp);
|
|
|
|
if (clp != NULL) {
|
|
|
|
/* mount and share received datasets */
|
|
|
|
err = changelist_postfix(clp);
|
|
|
|
changelist_free(clp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (zhp == NULL || clp == NULL || err)
|
|
|
|
err = -1;
|
|
|
|
}
|
|
|
|
if (top_zfs)
|
|
|
|
free(top_zfs);
|
|
|
|
|
|
|
|
return (err);
|
|
|
|
}
|