2019-12-11 23:12:08 +03: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
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
|
|
|
* Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>.
|
|
|
|
* All rights reserved.
|
|
|
|
* Copyright (c) 2012, 2015, 2018 by Delphix. All rights reserved.
|
|
|
|
* Copyright (c) 2014 Integros [integros.com]
|
|
|
|
* Copyright 2016 Nexenta Systems, Inc. All rights reserved.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Portions Copyright 2010 Robert Milkowski */
|
|
|
|
|
|
|
|
#include <sys/avl.h>
|
|
|
|
#include <sys/dmu_objset.h>
|
|
|
|
#include <sys/sa.h>
|
|
|
|
#include <sys/sa_impl.h>
|
|
|
|
#include <sys/zap.h>
|
|
|
|
#include <sys/zfs_project.h>
|
|
|
|
#include <sys/zfs_quota.h>
|
|
|
|
#include <sys/zfs_znode.h>
|
|
|
|
|
|
|
|
int
|
File incorrectly zeroed when receiving incremental stream that toggles -L
Background:
By increasing the recordsize property above the default of 128KB, a
filesystem may have "large" blocks. By default, a send stream of such a
filesystem does not contain large WRITE records, instead it decreases
objects' block sizes to 128KB and splits the large blocks into 128KB
blocks, allowing the large-block filesystem to be received by a system
that does not support the `large_blocks` feature. A send stream
generated by `zfs send -L` (or `--large-block`) preserves the large
block size on the receiving system, by using large WRITE records.
When receiving an incremental send stream for a filesystem with large
blocks, if the send stream's -L flag was toggled, a bug is encountered
in which the file's contents are incorrectly zeroed out. The contents
of any blocks that were not modified by this send stream will be lost.
"Toggled" means that the previous send used `-L`, but this incremental
does not use `-L` (-L to no-L); or that the previous send did not use
`-L`, but this incremental does use `-L` (no-L to -L).
Changes:
This commit addresses the problem with several changes to the semantics
of zfs send/receive:
1. "-L to no-L" incrementals are rejected. If the previous send used
`-L`, but this incremental does not use `-L`, the `zfs receive` will
fail with this error message:
incremental send stream requires -L (--large-block), to match
previous receive.
2. "no-L to -L" incrementals are handled correctly, preserving the
smaller (128KB) block size of any already-received files that used large
blocks on the sending system but were split by `zfs send` without the
`-L` flag.
3. A new send stream format flag is added, `SWITCH_TO_LARGE_BLOCKS`.
This feature indicates that we can correctly handle "no-L to -L"
incrementals. This flag is currently not set on any send streams. In
the future, we intend for incremental send streams of snapshots that
have large blocks to use `-L` by default, and these streams will also
have the `SWITCH_TO_LARGE_BLOCKS` feature set. This ensures that streams
from the default use of `zfs send` won't encounter the bug mentioned
above, because they can't be received by software with the bug.
Implementation notes:
To facilitate accessing the ZPL's generation number,
`zfs_space_delta_cb()` has been renamed to `zpl_get_file_info()` and
restructured to fill in a struct with ZPL-specific info including owner
and generation.
In the "no-L to -L" case, if this is a compressed send stream (from
`zfs send -cL`), large WRITE records that are being written to small
(128KB) blocksize files need to be decompressed so that they can be
written split up into multiple blocks. The zio pipeline will recompress
each smaller block individually.
A new test case, `send-L_toggle`, is added, which tests the "no-L to -L"
case and verifies that we get an error for the "-L to no-L" case.
Reviewed-by: Paul Dagnelie <pcd@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Matthew Ahrens <mahrens@delphix.com>
Closes #6224
Closes #10383
2020-06-09 20:41:01 +03:00
|
|
|
zpl_get_file_info(dmu_object_type_t bonustype, const void *data,
|
|
|
|
zfs_file_info_t *zoi)
|
2019-12-11 23:12:08 +03:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Is it a valid type of object to track?
|
|
|
|
*/
|
|
|
|
if (bonustype != DMU_OT_ZNODE && bonustype != DMU_OT_SA)
|
|
|
|
return (SET_ERROR(ENOENT));
|
|
|
|
|
File incorrectly zeroed when receiving incremental stream that toggles -L
Background:
By increasing the recordsize property above the default of 128KB, a
filesystem may have "large" blocks. By default, a send stream of such a
filesystem does not contain large WRITE records, instead it decreases
objects' block sizes to 128KB and splits the large blocks into 128KB
blocks, allowing the large-block filesystem to be received by a system
that does not support the `large_blocks` feature. A send stream
generated by `zfs send -L` (or `--large-block`) preserves the large
block size on the receiving system, by using large WRITE records.
When receiving an incremental send stream for a filesystem with large
blocks, if the send stream's -L flag was toggled, a bug is encountered
in which the file's contents are incorrectly zeroed out. The contents
of any blocks that were not modified by this send stream will be lost.
"Toggled" means that the previous send used `-L`, but this incremental
does not use `-L` (-L to no-L); or that the previous send did not use
`-L`, but this incremental does use `-L` (no-L to -L).
Changes:
This commit addresses the problem with several changes to the semantics
of zfs send/receive:
1. "-L to no-L" incrementals are rejected. If the previous send used
`-L`, but this incremental does not use `-L`, the `zfs receive` will
fail with this error message:
incremental send stream requires -L (--large-block), to match
previous receive.
2. "no-L to -L" incrementals are handled correctly, preserving the
smaller (128KB) block size of any already-received files that used large
blocks on the sending system but were split by `zfs send` without the
`-L` flag.
3. A new send stream format flag is added, `SWITCH_TO_LARGE_BLOCKS`.
This feature indicates that we can correctly handle "no-L to -L"
incrementals. This flag is currently not set on any send streams. In
the future, we intend for incremental send streams of snapshots that
have large blocks to use `-L` by default, and these streams will also
have the `SWITCH_TO_LARGE_BLOCKS` feature set. This ensures that streams
from the default use of `zfs send` won't encounter the bug mentioned
above, because they can't be received by software with the bug.
Implementation notes:
To facilitate accessing the ZPL's generation number,
`zfs_space_delta_cb()` has been renamed to `zpl_get_file_info()` and
restructured to fill in a struct with ZPL-specific info including owner
and generation.
In the "no-L to -L" case, if this is a compressed send stream (from
`zfs send -cL`), large WRITE records that are being written to small
(128KB) blocksize files need to be decompressed so that they can be
written split up into multiple blocks. The zio pipeline will recompress
each smaller block individually.
A new test case, `send-L_toggle`, is added, which tests the "no-L to -L"
case and verifies that we get an error for the "-L to no-L" case.
Reviewed-by: Paul Dagnelie <pcd@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Matthew Ahrens <mahrens@delphix.com>
Closes #6224
Closes #10383
2020-06-09 20:41:01 +03:00
|
|
|
zoi->zfi_project = ZFS_DEFAULT_PROJID;
|
|
|
|
|
2019-12-11 23:12:08 +03:00
|
|
|
/*
|
|
|
|
* If we have a NULL data pointer
|
|
|
|
* then assume the id's aren't changing and
|
|
|
|
* return EEXIST to the dmu to let it know to
|
|
|
|
* use the same ids
|
|
|
|
*/
|
|
|
|
if (data == NULL)
|
|
|
|
return (SET_ERROR(EEXIST));
|
|
|
|
|
|
|
|
if (bonustype == DMU_OT_ZNODE) {
|
File incorrectly zeroed when receiving incremental stream that toggles -L
Background:
By increasing the recordsize property above the default of 128KB, a
filesystem may have "large" blocks. By default, a send stream of such a
filesystem does not contain large WRITE records, instead it decreases
objects' block sizes to 128KB and splits the large blocks into 128KB
blocks, allowing the large-block filesystem to be received by a system
that does not support the `large_blocks` feature. A send stream
generated by `zfs send -L` (or `--large-block`) preserves the large
block size on the receiving system, by using large WRITE records.
When receiving an incremental send stream for a filesystem with large
blocks, if the send stream's -L flag was toggled, a bug is encountered
in which the file's contents are incorrectly zeroed out. The contents
of any blocks that were not modified by this send stream will be lost.
"Toggled" means that the previous send used `-L`, but this incremental
does not use `-L` (-L to no-L); or that the previous send did not use
`-L`, but this incremental does use `-L` (no-L to -L).
Changes:
This commit addresses the problem with several changes to the semantics
of zfs send/receive:
1. "-L to no-L" incrementals are rejected. If the previous send used
`-L`, but this incremental does not use `-L`, the `zfs receive` will
fail with this error message:
incremental send stream requires -L (--large-block), to match
previous receive.
2. "no-L to -L" incrementals are handled correctly, preserving the
smaller (128KB) block size of any already-received files that used large
blocks on the sending system but were split by `zfs send` without the
`-L` flag.
3. A new send stream format flag is added, `SWITCH_TO_LARGE_BLOCKS`.
This feature indicates that we can correctly handle "no-L to -L"
incrementals. This flag is currently not set on any send streams. In
the future, we intend for incremental send streams of snapshots that
have large blocks to use `-L` by default, and these streams will also
have the `SWITCH_TO_LARGE_BLOCKS` feature set. This ensures that streams
from the default use of `zfs send` won't encounter the bug mentioned
above, because they can't be received by software with the bug.
Implementation notes:
To facilitate accessing the ZPL's generation number,
`zfs_space_delta_cb()` has been renamed to `zpl_get_file_info()` and
restructured to fill in a struct with ZPL-specific info including owner
and generation.
In the "no-L to -L" case, if this is a compressed send stream (from
`zfs send -cL`), large WRITE records that are being written to small
(128KB) blocksize files need to be decompressed so that they can be
written split up into multiple blocks. The zio pipeline will recompress
each smaller block individually.
A new test case, `send-L_toggle`, is added, which tests the "no-L to -L"
case and verifies that we get an error for the "-L to no-L" case.
Reviewed-by: Paul Dagnelie <pcd@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Matthew Ahrens <mahrens@delphix.com>
Closes #6224
Closes #10383
2020-06-09 20:41:01 +03:00
|
|
|
const znode_phys_t *znp = data;
|
|
|
|
zoi->zfi_user = znp->zp_uid;
|
|
|
|
zoi->zfi_group = znp->zp_gid;
|
|
|
|
zoi->zfi_generation = znp->zp_gen;
|
2019-12-11 23:12:08 +03:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
File incorrectly zeroed when receiving incremental stream that toggles -L
Background:
By increasing the recordsize property above the default of 128KB, a
filesystem may have "large" blocks. By default, a send stream of such a
filesystem does not contain large WRITE records, instead it decreases
objects' block sizes to 128KB and splits the large blocks into 128KB
blocks, allowing the large-block filesystem to be received by a system
that does not support the `large_blocks` feature. A send stream
generated by `zfs send -L` (or `--large-block`) preserves the large
block size on the receiving system, by using large WRITE records.
When receiving an incremental send stream for a filesystem with large
blocks, if the send stream's -L flag was toggled, a bug is encountered
in which the file's contents are incorrectly zeroed out. The contents
of any blocks that were not modified by this send stream will be lost.
"Toggled" means that the previous send used `-L`, but this incremental
does not use `-L` (-L to no-L); or that the previous send did not use
`-L`, but this incremental does use `-L` (no-L to -L).
Changes:
This commit addresses the problem with several changes to the semantics
of zfs send/receive:
1. "-L to no-L" incrementals are rejected. If the previous send used
`-L`, but this incremental does not use `-L`, the `zfs receive` will
fail with this error message:
incremental send stream requires -L (--large-block), to match
previous receive.
2. "no-L to -L" incrementals are handled correctly, preserving the
smaller (128KB) block size of any already-received files that used large
blocks on the sending system but were split by `zfs send` without the
`-L` flag.
3. A new send stream format flag is added, `SWITCH_TO_LARGE_BLOCKS`.
This feature indicates that we can correctly handle "no-L to -L"
incrementals. This flag is currently not set on any send streams. In
the future, we intend for incremental send streams of snapshots that
have large blocks to use `-L` by default, and these streams will also
have the `SWITCH_TO_LARGE_BLOCKS` feature set. This ensures that streams
from the default use of `zfs send` won't encounter the bug mentioned
above, because they can't be received by software with the bug.
Implementation notes:
To facilitate accessing the ZPL's generation number,
`zfs_space_delta_cb()` has been renamed to `zpl_get_file_info()` and
restructured to fill in a struct with ZPL-specific info including owner
and generation.
In the "no-L to -L" case, if this is a compressed send stream (from
`zfs send -cL`), large WRITE records that are being written to small
(128KB) blocksize files need to be decompressed so that they can be
written split up into multiple blocks. The zio pipeline will recompress
each smaller block individually.
A new test case, `send-L_toggle`, is added, which tests the "no-L to -L"
case and verifies that we get an error for the "-L to no-L" case.
Reviewed-by: Paul Dagnelie <pcd@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Matthew Ahrens <mahrens@delphix.com>
Closes #6224
Closes #10383
2020-06-09 20:41:01 +03:00
|
|
|
const sa_hdr_phys_t *sap = data;
|
2019-12-11 23:12:08 +03:00
|
|
|
if (sap->sa_magic == 0) {
|
|
|
|
/*
|
|
|
|
* This should only happen for newly created files
|
|
|
|
* that haven't had the znode data filled in yet.
|
|
|
|
*/
|
File incorrectly zeroed when receiving incremental stream that toggles -L
Background:
By increasing the recordsize property above the default of 128KB, a
filesystem may have "large" blocks. By default, a send stream of such a
filesystem does not contain large WRITE records, instead it decreases
objects' block sizes to 128KB and splits the large blocks into 128KB
blocks, allowing the large-block filesystem to be received by a system
that does not support the `large_blocks` feature. A send stream
generated by `zfs send -L` (or `--large-block`) preserves the large
block size on the receiving system, by using large WRITE records.
When receiving an incremental send stream for a filesystem with large
blocks, if the send stream's -L flag was toggled, a bug is encountered
in which the file's contents are incorrectly zeroed out. The contents
of any blocks that were not modified by this send stream will be lost.
"Toggled" means that the previous send used `-L`, but this incremental
does not use `-L` (-L to no-L); or that the previous send did not use
`-L`, but this incremental does use `-L` (no-L to -L).
Changes:
This commit addresses the problem with several changes to the semantics
of zfs send/receive:
1. "-L to no-L" incrementals are rejected. If the previous send used
`-L`, but this incremental does not use `-L`, the `zfs receive` will
fail with this error message:
incremental send stream requires -L (--large-block), to match
previous receive.
2. "no-L to -L" incrementals are handled correctly, preserving the
smaller (128KB) block size of any already-received files that used large
blocks on the sending system but were split by `zfs send` without the
`-L` flag.
3. A new send stream format flag is added, `SWITCH_TO_LARGE_BLOCKS`.
This feature indicates that we can correctly handle "no-L to -L"
incrementals. This flag is currently not set on any send streams. In
the future, we intend for incremental send streams of snapshots that
have large blocks to use `-L` by default, and these streams will also
have the `SWITCH_TO_LARGE_BLOCKS` feature set. This ensures that streams
from the default use of `zfs send` won't encounter the bug mentioned
above, because they can't be received by software with the bug.
Implementation notes:
To facilitate accessing the ZPL's generation number,
`zfs_space_delta_cb()` has been renamed to `zpl_get_file_info()` and
restructured to fill in a struct with ZPL-specific info including owner
and generation.
In the "no-L to -L" case, if this is a compressed send stream (from
`zfs send -cL`), large WRITE records that are being written to small
(128KB) blocksize files need to be decompressed so that they can be
written split up into multiple blocks. The zio pipeline will recompress
each smaller block individually.
A new test case, `send-L_toggle`, is added, which tests the "no-L to -L"
case and verifies that we get an error for the "-L to no-L" case.
Reviewed-by: Paul Dagnelie <pcd@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Matthew Ahrens <mahrens@delphix.com>
Closes #6224
Closes #10383
2020-06-09 20:41:01 +03:00
|
|
|
zoi->zfi_user = 0;
|
|
|
|
zoi->zfi_group = 0;
|
|
|
|
zoi->zfi_generation = 0;
|
2019-12-11 23:12:08 +03:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
File incorrectly zeroed when receiving incremental stream that toggles -L
Background:
By increasing the recordsize property above the default of 128KB, a
filesystem may have "large" blocks. By default, a send stream of such a
filesystem does not contain large WRITE records, instead it decreases
objects' block sizes to 128KB and splits the large blocks into 128KB
blocks, allowing the large-block filesystem to be received by a system
that does not support the `large_blocks` feature. A send stream
generated by `zfs send -L` (or `--large-block`) preserves the large
block size on the receiving system, by using large WRITE records.
When receiving an incremental send stream for a filesystem with large
blocks, if the send stream's -L flag was toggled, a bug is encountered
in which the file's contents are incorrectly zeroed out. The contents
of any blocks that were not modified by this send stream will be lost.
"Toggled" means that the previous send used `-L`, but this incremental
does not use `-L` (-L to no-L); or that the previous send did not use
`-L`, but this incremental does use `-L` (no-L to -L).
Changes:
This commit addresses the problem with several changes to the semantics
of zfs send/receive:
1. "-L to no-L" incrementals are rejected. If the previous send used
`-L`, but this incremental does not use `-L`, the `zfs receive` will
fail with this error message:
incremental send stream requires -L (--large-block), to match
previous receive.
2. "no-L to -L" incrementals are handled correctly, preserving the
smaller (128KB) block size of any already-received files that used large
blocks on the sending system but were split by `zfs send` without the
`-L` flag.
3. A new send stream format flag is added, `SWITCH_TO_LARGE_BLOCKS`.
This feature indicates that we can correctly handle "no-L to -L"
incrementals. This flag is currently not set on any send streams. In
the future, we intend for incremental send streams of snapshots that
have large blocks to use `-L` by default, and these streams will also
have the `SWITCH_TO_LARGE_BLOCKS` feature set. This ensures that streams
from the default use of `zfs send` won't encounter the bug mentioned
above, because they can't be received by software with the bug.
Implementation notes:
To facilitate accessing the ZPL's generation number,
`zfs_space_delta_cb()` has been renamed to `zpl_get_file_info()` and
restructured to fill in a struct with ZPL-specific info including owner
and generation.
In the "no-L to -L" case, if this is a compressed send stream (from
`zfs send -cL`), large WRITE records that are being written to small
(128KB) blocksize files need to be decompressed so that they can be
written split up into multiple blocks. The zio pipeline will recompress
each smaller block individually.
A new test case, `send-L_toggle`, is added, which tests the "no-L to -L"
case and verifies that we get an error for the "-L to no-L" case.
Reviewed-by: Paul Dagnelie <pcd@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Matthew Ahrens <mahrens@delphix.com>
Closes #6224
Closes #10383
2020-06-09 20:41:01 +03:00
|
|
|
sa_hdr_phys_t sa = *sap;
|
|
|
|
boolean_t swap = B_FALSE;
|
2019-12-11 23:12:08 +03:00
|
|
|
if (sa.sa_magic == BSWAP_32(SA_MAGIC)) {
|
|
|
|
sa.sa_magic = SA_MAGIC;
|
|
|
|
sa.sa_layout_info = BSWAP_16(sa.sa_layout_info);
|
|
|
|
swap = B_TRUE;
|
|
|
|
}
|
File incorrectly zeroed when receiving incremental stream that toggles -L
Background:
By increasing the recordsize property above the default of 128KB, a
filesystem may have "large" blocks. By default, a send stream of such a
filesystem does not contain large WRITE records, instead it decreases
objects' block sizes to 128KB and splits the large blocks into 128KB
blocks, allowing the large-block filesystem to be received by a system
that does not support the `large_blocks` feature. A send stream
generated by `zfs send -L` (or `--large-block`) preserves the large
block size on the receiving system, by using large WRITE records.
When receiving an incremental send stream for a filesystem with large
blocks, if the send stream's -L flag was toggled, a bug is encountered
in which the file's contents are incorrectly zeroed out. The contents
of any blocks that were not modified by this send stream will be lost.
"Toggled" means that the previous send used `-L`, but this incremental
does not use `-L` (-L to no-L); or that the previous send did not use
`-L`, but this incremental does use `-L` (no-L to -L).
Changes:
This commit addresses the problem with several changes to the semantics
of zfs send/receive:
1. "-L to no-L" incrementals are rejected. If the previous send used
`-L`, but this incremental does not use `-L`, the `zfs receive` will
fail with this error message:
incremental send stream requires -L (--large-block), to match
previous receive.
2. "no-L to -L" incrementals are handled correctly, preserving the
smaller (128KB) block size of any already-received files that used large
blocks on the sending system but were split by `zfs send` without the
`-L` flag.
3. A new send stream format flag is added, `SWITCH_TO_LARGE_BLOCKS`.
This feature indicates that we can correctly handle "no-L to -L"
incrementals. This flag is currently not set on any send streams. In
the future, we intend for incremental send streams of snapshots that
have large blocks to use `-L` by default, and these streams will also
have the `SWITCH_TO_LARGE_BLOCKS` feature set. This ensures that streams
from the default use of `zfs send` won't encounter the bug mentioned
above, because they can't be received by software with the bug.
Implementation notes:
To facilitate accessing the ZPL's generation number,
`zfs_space_delta_cb()` has been renamed to `zpl_get_file_info()` and
restructured to fill in a struct with ZPL-specific info including owner
and generation.
In the "no-L to -L" case, if this is a compressed send stream (from
`zfs send -cL`), large WRITE records that are being written to small
(128KB) blocksize files need to be decompressed so that they can be
written split up into multiple blocks. The zio pipeline will recompress
each smaller block individually.
A new test case, `send-L_toggle`, is added, which tests the "no-L to -L"
case and verifies that we get an error for the "-L to no-L" case.
Reviewed-by: Paul Dagnelie <pcd@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Matthew Ahrens <mahrens@delphix.com>
Closes #6224
Closes #10383
2020-06-09 20:41:01 +03:00
|
|
|
VERIFY3U(sa.sa_magic, ==, SA_MAGIC);
|
2019-12-11 23:12:08 +03:00
|
|
|
|
File incorrectly zeroed when receiving incremental stream that toggles -L
Background:
By increasing the recordsize property above the default of 128KB, a
filesystem may have "large" blocks. By default, a send stream of such a
filesystem does not contain large WRITE records, instead it decreases
objects' block sizes to 128KB and splits the large blocks into 128KB
blocks, allowing the large-block filesystem to be received by a system
that does not support the `large_blocks` feature. A send stream
generated by `zfs send -L` (or `--large-block`) preserves the large
block size on the receiving system, by using large WRITE records.
When receiving an incremental send stream for a filesystem with large
blocks, if the send stream's -L flag was toggled, a bug is encountered
in which the file's contents are incorrectly zeroed out. The contents
of any blocks that were not modified by this send stream will be lost.
"Toggled" means that the previous send used `-L`, but this incremental
does not use `-L` (-L to no-L); or that the previous send did not use
`-L`, but this incremental does use `-L` (no-L to -L).
Changes:
This commit addresses the problem with several changes to the semantics
of zfs send/receive:
1. "-L to no-L" incrementals are rejected. If the previous send used
`-L`, but this incremental does not use `-L`, the `zfs receive` will
fail with this error message:
incremental send stream requires -L (--large-block), to match
previous receive.
2. "no-L to -L" incrementals are handled correctly, preserving the
smaller (128KB) block size of any already-received files that used large
blocks on the sending system but were split by `zfs send` without the
`-L` flag.
3. A new send stream format flag is added, `SWITCH_TO_LARGE_BLOCKS`.
This feature indicates that we can correctly handle "no-L to -L"
incrementals. This flag is currently not set on any send streams. In
the future, we intend for incremental send streams of snapshots that
have large blocks to use `-L` by default, and these streams will also
have the `SWITCH_TO_LARGE_BLOCKS` feature set. This ensures that streams
from the default use of `zfs send` won't encounter the bug mentioned
above, because they can't be received by software with the bug.
Implementation notes:
To facilitate accessing the ZPL's generation number,
`zfs_space_delta_cb()` has been renamed to `zpl_get_file_info()` and
restructured to fill in a struct with ZPL-specific info including owner
and generation.
In the "no-L to -L" case, if this is a compressed send stream (from
`zfs send -cL`), large WRITE records that are being written to small
(128KB) blocksize files need to be decompressed so that they can be
written split up into multiple blocks. The zio pipeline will recompress
each smaller block individually.
A new test case, `send-L_toggle`, is added, which tests the "no-L to -L"
case and verifies that we get an error for the "-L to no-L" case.
Reviewed-by: Paul Dagnelie <pcd@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Matthew Ahrens <mahrens@delphix.com>
Closes #6224
Closes #10383
2020-06-09 20:41:01 +03:00
|
|
|
int hdrsize = sa_hdrsize(&sa);
|
2019-12-11 23:12:08 +03:00
|
|
|
VERIFY3U(hdrsize, >=, sizeof (sa_hdr_phys_t));
|
|
|
|
|
File incorrectly zeroed when receiving incremental stream that toggles -L
Background:
By increasing the recordsize property above the default of 128KB, a
filesystem may have "large" blocks. By default, a send stream of such a
filesystem does not contain large WRITE records, instead it decreases
objects' block sizes to 128KB and splits the large blocks into 128KB
blocks, allowing the large-block filesystem to be received by a system
that does not support the `large_blocks` feature. A send stream
generated by `zfs send -L` (or `--large-block`) preserves the large
block size on the receiving system, by using large WRITE records.
When receiving an incremental send stream for a filesystem with large
blocks, if the send stream's -L flag was toggled, a bug is encountered
in which the file's contents are incorrectly zeroed out. The contents
of any blocks that were not modified by this send stream will be lost.
"Toggled" means that the previous send used `-L`, but this incremental
does not use `-L` (-L to no-L); or that the previous send did not use
`-L`, but this incremental does use `-L` (no-L to -L).
Changes:
This commit addresses the problem with several changes to the semantics
of zfs send/receive:
1. "-L to no-L" incrementals are rejected. If the previous send used
`-L`, but this incremental does not use `-L`, the `zfs receive` will
fail with this error message:
incremental send stream requires -L (--large-block), to match
previous receive.
2. "no-L to -L" incrementals are handled correctly, preserving the
smaller (128KB) block size of any already-received files that used large
blocks on the sending system but were split by `zfs send` without the
`-L` flag.
3. A new send stream format flag is added, `SWITCH_TO_LARGE_BLOCKS`.
This feature indicates that we can correctly handle "no-L to -L"
incrementals. This flag is currently not set on any send streams. In
the future, we intend for incremental send streams of snapshots that
have large blocks to use `-L` by default, and these streams will also
have the `SWITCH_TO_LARGE_BLOCKS` feature set. This ensures that streams
from the default use of `zfs send` won't encounter the bug mentioned
above, because they can't be received by software with the bug.
Implementation notes:
To facilitate accessing the ZPL's generation number,
`zfs_space_delta_cb()` has been renamed to `zpl_get_file_info()` and
restructured to fill in a struct with ZPL-specific info including owner
and generation.
In the "no-L to -L" case, if this is a compressed send stream (from
`zfs send -cL`), large WRITE records that are being written to small
(128KB) blocksize files need to be decompressed so that they can be
written split up into multiple blocks. The zio pipeline will recompress
each smaller block individually.
A new test case, `send-L_toggle`, is added, which tests the "no-L to -L"
case and verifies that we get an error for the "-L to no-L" case.
Reviewed-by: Paul Dagnelie <pcd@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Matthew Ahrens <mahrens@delphix.com>
Closes #6224
Closes #10383
2020-06-09 20:41:01 +03:00
|
|
|
uintptr_t data_after_hdr = (uintptr_t)data + hdrsize;
|
|
|
|
zoi->zfi_user = *((uint64_t *)(data_after_hdr + SA_UID_OFFSET));
|
|
|
|
zoi->zfi_group = *((uint64_t *)(data_after_hdr + SA_GID_OFFSET));
|
|
|
|
zoi->zfi_generation = *((uint64_t *)(data_after_hdr + SA_GEN_OFFSET));
|
|
|
|
uint64_t flags = *((uint64_t *)(data_after_hdr + SA_FLAGS_OFFSET));
|
2019-12-11 23:12:08 +03:00
|
|
|
if (swap)
|
|
|
|
flags = BSWAP_64(flags);
|
|
|
|
|
File incorrectly zeroed when receiving incremental stream that toggles -L
Background:
By increasing the recordsize property above the default of 128KB, a
filesystem may have "large" blocks. By default, a send stream of such a
filesystem does not contain large WRITE records, instead it decreases
objects' block sizes to 128KB and splits the large blocks into 128KB
blocks, allowing the large-block filesystem to be received by a system
that does not support the `large_blocks` feature. A send stream
generated by `zfs send -L` (or `--large-block`) preserves the large
block size on the receiving system, by using large WRITE records.
When receiving an incremental send stream for a filesystem with large
blocks, if the send stream's -L flag was toggled, a bug is encountered
in which the file's contents are incorrectly zeroed out. The contents
of any blocks that were not modified by this send stream will be lost.
"Toggled" means that the previous send used `-L`, but this incremental
does not use `-L` (-L to no-L); or that the previous send did not use
`-L`, but this incremental does use `-L` (no-L to -L).
Changes:
This commit addresses the problem with several changes to the semantics
of zfs send/receive:
1. "-L to no-L" incrementals are rejected. If the previous send used
`-L`, but this incremental does not use `-L`, the `zfs receive` will
fail with this error message:
incremental send stream requires -L (--large-block), to match
previous receive.
2. "no-L to -L" incrementals are handled correctly, preserving the
smaller (128KB) block size of any already-received files that used large
blocks on the sending system but were split by `zfs send` without the
`-L` flag.
3. A new send stream format flag is added, `SWITCH_TO_LARGE_BLOCKS`.
This feature indicates that we can correctly handle "no-L to -L"
incrementals. This flag is currently not set on any send streams. In
the future, we intend for incremental send streams of snapshots that
have large blocks to use `-L` by default, and these streams will also
have the `SWITCH_TO_LARGE_BLOCKS` feature set. This ensures that streams
from the default use of `zfs send` won't encounter the bug mentioned
above, because they can't be received by software with the bug.
Implementation notes:
To facilitate accessing the ZPL's generation number,
`zfs_space_delta_cb()` has been renamed to `zpl_get_file_info()` and
restructured to fill in a struct with ZPL-specific info including owner
and generation.
In the "no-L to -L" case, if this is a compressed send stream (from
`zfs send -cL`), large WRITE records that are being written to small
(128KB) blocksize files need to be decompressed so that they can be
written split up into multiple blocks. The zio pipeline will recompress
each smaller block individually.
A new test case, `send-L_toggle`, is added, which tests the "no-L to -L"
case and verifies that we get an error for the "-L to no-L" case.
Reviewed-by: Paul Dagnelie <pcd@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Matthew Ahrens <mahrens@delphix.com>
Closes #6224
Closes #10383
2020-06-09 20:41:01 +03:00
|
|
|
if (flags & ZFS_PROJID) {
|
|
|
|
zoi->zfi_project =
|
|
|
|
*((uint64_t *)(data_after_hdr + SA_PROJID_OFFSET));
|
|
|
|
}
|
2019-12-11 23:12:08 +03:00
|
|
|
|
|
|
|
if (swap) {
|
File incorrectly zeroed when receiving incremental stream that toggles -L
Background:
By increasing the recordsize property above the default of 128KB, a
filesystem may have "large" blocks. By default, a send stream of such a
filesystem does not contain large WRITE records, instead it decreases
objects' block sizes to 128KB and splits the large blocks into 128KB
blocks, allowing the large-block filesystem to be received by a system
that does not support the `large_blocks` feature. A send stream
generated by `zfs send -L` (or `--large-block`) preserves the large
block size on the receiving system, by using large WRITE records.
When receiving an incremental send stream for a filesystem with large
blocks, if the send stream's -L flag was toggled, a bug is encountered
in which the file's contents are incorrectly zeroed out. The contents
of any blocks that were not modified by this send stream will be lost.
"Toggled" means that the previous send used `-L`, but this incremental
does not use `-L` (-L to no-L); or that the previous send did not use
`-L`, but this incremental does use `-L` (no-L to -L).
Changes:
This commit addresses the problem with several changes to the semantics
of zfs send/receive:
1. "-L to no-L" incrementals are rejected. If the previous send used
`-L`, but this incremental does not use `-L`, the `zfs receive` will
fail with this error message:
incremental send stream requires -L (--large-block), to match
previous receive.
2. "no-L to -L" incrementals are handled correctly, preserving the
smaller (128KB) block size of any already-received files that used large
blocks on the sending system but were split by `zfs send` without the
`-L` flag.
3. A new send stream format flag is added, `SWITCH_TO_LARGE_BLOCKS`.
This feature indicates that we can correctly handle "no-L to -L"
incrementals. This flag is currently not set on any send streams. In
the future, we intend for incremental send streams of snapshots that
have large blocks to use `-L` by default, and these streams will also
have the `SWITCH_TO_LARGE_BLOCKS` feature set. This ensures that streams
from the default use of `zfs send` won't encounter the bug mentioned
above, because they can't be received by software with the bug.
Implementation notes:
To facilitate accessing the ZPL's generation number,
`zfs_space_delta_cb()` has been renamed to `zpl_get_file_info()` and
restructured to fill in a struct with ZPL-specific info including owner
and generation.
In the "no-L to -L" case, if this is a compressed send stream (from
`zfs send -cL`), large WRITE records that are being written to small
(128KB) blocksize files need to be decompressed so that they can be
written split up into multiple blocks. The zio pipeline will recompress
each smaller block individually.
A new test case, `send-L_toggle`, is added, which tests the "no-L to -L"
case and verifies that we get an error for the "-L to no-L" case.
Reviewed-by: Paul Dagnelie <pcd@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Matthew Ahrens <mahrens@delphix.com>
Closes #6224
Closes #10383
2020-06-09 20:41:01 +03:00
|
|
|
zoi->zfi_user = BSWAP_64(zoi->zfi_user);
|
|
|
|
zoi->zfi_group = BSWAP_64(zoi->zfi_group);
|
|
|
|
zoi->zfi_project = BSWAP_64(zoi->zfi_project);
|
|
|
|
zoi->zfi_generation = BSWAP_64(zoi->zfi_generation);
|
2019-12-11 23:12:08 +03:00
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
fuidstr_to_sid(zfsvfs_t *zfsvfs, const char *fuidstr,
|
|
|
|
char *domainbuf, int buflen, uid_t *ridp)
|
|
|
|
{
|
|
|
|
uint64_t fuid;
|
|
|
|
const char *domain;
|
|
|
|
|
|
|
|
fuid = zfs_strtonum(fuidstr, NULL);
|
|
|
|
|
|
|
|
domain = zfs_fuid_find_by_idx(zfsvfs, FUID_INDEX(fuid));
|
|
|
|
if (domain)
|
|
|
|
(void) strlcpy(domainbuf, domain, buflen);
|
|
|
|
else
|
|
|
|
domainbuf[0] = '\0';
|
|
|
|
*ridp = FUID_RID(fuid);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint64_t
|
|
|
|
zfs_userquota_prop_to_obj(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type)
|
|
|
|
{
|
|
|
|
switch (type) {
|
|
|
|
case ZFS_PROP_USERUSED:
|
|
|
|
case ZFS_PROP_USEROBJUSED:
|
|
|
|
return (DMU_USERUSED_OBJECT);
|
|
|
|
case ZFS_PROP_GROUPUSED:
|
|
|
|
case ZFS_PROP_GROUPOBJUSED:
|
|
|
|
return (DMU_GROUPUSED_OBJECT);
|
|
|
|
case ZFS_PROP_PROJECTUSED:
|
|
|
|
case ZFS_PROP_PROJECTOBJUSED:
|
|
|
|
return (DMU_PROJECTUSED_OBJECT);
|
|
|
|
case ZFS_PROP_USERQUOTA:
|
|
|
|
return (zfsvfs->z_userquota_obj);
|
|
|
|
case ZFS_PROP_GROUPQUOTA:
|
|
|
|
return (zfsvfs->z_groupquota_obj);
|
|
|
|
case ZFS_PROP_USEROBJQUOTA:
|
|
|
|
return (zfsvfs->z_userobjquota_obj);
|
|
|
|
case ZFS_PROP_GROUPOBJQUOTA:
|
|
|
|
return (zfsvfs->z_groupobjquota_obj);
|
|
|
|
case ZFS_PROP_PROJECTQUOTA:
|
|
|
|
return (zfsvfs->z_projectquota_obj);
|
|
|
|
case ZFS_PROP_PROJECTOBJQUOTA:
|
|
|
|
return (zfsvfs->z_projectobjquota_obj);
|
|
|
|
default:
|
|
|
|
return (ZFS_NO_OBJECT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
zfs_userspace_many(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type,
|
|
|
|
uint64_t *cookiep, void *vbuf, uint64_t *bufsizep)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
zap_cursor_t zc;
|
|
|
|
zap_attribute_t za;
|
|
|
|
zfs_useracct_t *buf = vbuf;
|
|
|
|
uint64_t obj;
|
|
|
|
int offset = 0;
|
|
|
|
|
|
|
|
if (!dmu_objset_userspace_present(zfsvfs->z_os))
|
|
|
|
return (SET_ERROR(ENOTSUP));
|
|
|
|
|
|
|
|
if ((type == ZFS_PROP_PROJECTQUOTA || type == ZFS_PROP_PROJECTUSED ||
|
|
|
|
type == ZFS_PROP_PROJECTOBJQUOTA ||
|
|
|
|
type == ZFS_PROP_PROJECTOBJUSED) &&
|
|
|
|
!dmu_objset_projectquota_present(zfsvfs->z_os))
|
|
|
|
return (SET_ERROR(ENOTSUP));
|
|
|
|
|
|
|
|
if ((type == ZFS_PROP_USEROBJUSED || type == ZFS_PROP_GROUPOBJUSED ||
|
|
|
|
type == ZFS_PROP_USEROBJQUOTA || type == ZFS_PROP_GROUPOBJQUOTA ||
|
|
|
|
type == ZFS_PROP_PROJECTOBJUSED ||
|
|
|
|
type == ZFS_PROP_PROJECTOBJQUOTA) &&
|
|
|
|
!dmu_objset_userobjspace_present(zfsvfs->z_os))
|
|
|
|
return (SET_ERROR(ENOTSUP));
|
|
|
|
|
|
|
|
obj = zfs_userquota_prop_to_obj(zfsvfs, type);
|
|
|
|
if (obj == ZFS_NO_OBJECT) {
|
|
|
|
*bufsizep = 0;
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type == ZFS_PROP_USEROBJUSED || type == ZFS_PROP_GROUPOBJUSED ||
|
|
|
|
type == ZFS_PROP_PROJECTOBJUSED)
|
|
|
|
offset = DMU_OBJACCT_PREFIX_LEN;
|
|
|
|
|
|
|
|
for (zap_cursor_init_serialized(&zc, zfsvfs->z_os, obj, *cookiep);
|
|
|
|
(error = zap_cursor_retrieve(&zc, &za)) == 0;
|
|
|
|
zap_cursor_advance(&zc)) {
|
|
|
|
if ((uintptr_t)buf - (uintptr_t)vbuf + sizeof (zfs_useracct_t) >
|
|
|
|
*bufsizep)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* skip object quota (with zap name prefix DMU_OBJACCT_PREFIX)
|
|
|
|
* when dealing with block quota and vice versa.
|
|
|
|
*/
|
|
|
|
if ((offset > 0) != (strncmp(za.za_name, DMU_OBJACCT_PREFIX,
|
|
|
|
DMU_OBJACCT_PREFIX_LEN) == 0))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
fuidstr_to_sid(zfsvfs, za.za_name + offset,
|
|
|
|
buf->zu_domain, sizeof (buf->zu_domain), &buf->zu_rid);
|
|
|
|
|
|
|
|
buf->zu_space = za.za_first_integer;
|
|
|
|
buf++;
|
|
|
|
}
|
|
|
|
if (error == ENOENT)
|
|
|
|
error = 0;
|
|
|
|
|
|
|
|
ASSERT3U((uintptr_t)buf - (uintptr_t)vbuf, <=, *bufsizep);
|
|
|
|
*bufsizep = (uintptr_t)buf - (uintptr_t)vbuf;
|
|
|
|
*cookiep = zap_cursor_serialize(&zc);
|
|
|
|
zap_cursor_fini(&zc);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
zfs_userspace_one(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type,
|
|
|
|
const char *domain, uint64_t rid, uint64_t *valp)
|
|
|
|
{
|
|
|
|
char buf[20 + DMU_OBJACCT_PREFIX_LEN];
|
|
|
|
int offset = 0;
|
|
|
|
int err;
|
|
|
|
uint64_t obj;
|
|
|
|
|
|
|
|
*valp = 0;
|
|
|
|
|
|
|
|
if (!dmu_objset_userspace_present(zfsvfs->z_os))
|
|
|
|
return (SET_ERROR(ENOTSUP));
|
|
|
|
|
|
|
|
if ((type == ZFS_PROP_USEROBJUSED || type == ZFS_PROP_GROUPOBJUSED ||
|
|
|
|
type == ZFS_PROP_USEROBJQUOTA || type == ZFS_PROP_GROUPOBJQUOTA ||
|
|
|
|
type == ZFS_PROP_PROJECTOBJUSED ||
|
|
|
|
type == ZFS_PROP_PROJECTOBJQUOTA) &&
|
|
|
|
!dmu_objset_userobjspace_present(zfsvfs->z_os))
|
|
|
|
return (SET_ERROR(ENOTSUP));
|
|
|
|
|
|
|
|
if (type == ZFS_PROP_PROJECTQUOTA || type == ZFS_PROP_PROJECTUSED ||
|
|
|
|
type == ZFS_PROP_PROJECTOBJQUOTA ||
|
|
|
|
type == ZFS_PROP_PROJECTOBJUSED) {
|
|
|
|
if (!dmu_objset_projectquota_present(zfsvfs->z_os))
|
|
|
|
return (SET_ERROR(ENOTSUP));
|
|
|
|
if (!zpl_is_valid_projid(rid))
|
|
|
|
return (SET_ERROR(EINVAL));
|
|
|
|
}
|
|
|
|
|
|
|
|
obj = zfs_userquota_prop_to_obj(zfsvfs, type);
|
|
|
|
if (obj == ZFS_NO_OBJECT)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
if (type == ZFS_PROP_USEROBJUSED || type == ZFS_PROP_GROUPOBJUSED ||
|
|
|
|
type == ZFS_PROP_PROJECTOBJUSED) {
|
|
|
|
strlcpy(buf, DMU_OBJACCT_PREFIX, DMU_OBJACCT_PREFIX_LEN + 1);
|
|
|
|
offset = DMU_OBJACCT_PREFIX_LEN;
|
|
|
|
}
|
|
|
|
|
2020-06-07 21:42:12 +03:00
|
|
|
err = zfs_id_to_fuidstr(zfsvfs, domain, rid, buf + offset,
|
|
|
|
sizeof (buf) - offset, B_FALSE);
|
2019-12-11 23:12:08 +03:00
|
|
|
if (err)
|
|
|
|
return (err);
|
|
|
|
|
|
|
|
err = zap_lookup(zfsvfs->z_os, obj, buf, 8, 1, valp);
|
|
|
|
if (err == ENOENT)
|
|
|
|
err = 0;
|
|
|
|
return (err);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
zfs_set_userquota(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type,
|
|
|
|
const char *domain, uint64_t rid, uint64_t quota)
|
|
|
|
{
|
|
|
|
char buf[32];
|
|
|
|
int err;
|
|
|
|
dmu_tx_t *tx;
|
|
|
|
uint64_t *objp;
|
|
|
|
boolean_t fuid_dirtied;
|
|
|
|
|
|
|
|
if (zfsvfs->z_version < ZPL_VERSION_USERSPACE)
|
|
|
|
return (SET_ERROR(ENOTSUP));
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case ZFS_PROP_USERQUOTA:
|
|
|
|
objp = &zfsvfs->z_userquota_obj;
|
|
|
|
break;
|
|
|
|
case ZFS_PROP_GROUPQUOTA:
|
|
|
|
objp = &zfsvfs->z_groupquota_obj;
|
|
|
|
break;
|
|
|
|
case ZFS_PROP_USEROBJQUOTA:
|
|
|
|
objp = &zfsvfs->z_userobjquota_obj;
|
|
|
|
break;
|
|
|
|
case ZFS_PROP_GROUPOBJQUOTA:
|
|
|
|
objp = &zfsvfs->z_groupobjquota_obj;
|
|
|
|
break;
|
|
|
|
case ZFS_PROP_PROJECTQUOTA:
|
|
|
|
if (!dmu_objset_projectquota_enabled(zfsvfs->z_os))
|
|
|
|
return (SET_ERROR(ENOTSUP));
|
|
|
|
if (!zpl_is_valid_projid(rid))
|
|
|
|
return (SET_ERROR(EINVAL));
|
|
|
|
|
|
|
|
objp = &zfsvfs->z_projectquota_obj;
|
|
|
|
break;
|
|
|
|
case ZFS_PROP_PROJECTOBJQUOTA:
|
|
|
|
if (!dmu_objset_projectquota_enabled(zfsvfs->z_os))
|
|
|
|
return (SET_ERROR(ENOTSUP));
|
|
|
|
if (!zpl_is_valid_projid(rid))
|
|
|
|
return (SET_ERROR(EINVAL));
|
|
|
|
|
|
|
|
objp = &zfsvfs->z_projectobjquota_obj;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return (SET_ERROR(EINVAL));
|
|
|
|
}
|
|
|
|
|
2020-06-07 21:42:12 +03:00
|
|
|
err = zfs_id_to_fuidstr(zfsvfs, domain, rid, buf, sizeof (buf), B_TRUE);
|
2019-12-11 23:12:08 +03:00
|
|
|
if (err)
|
|
|
|
return (err);
|
|
|
|
fuid_dirtied = zfsvfs->z_fuid_dirty;
|
|
|
|
|
|
|
|
tx = dmu_tx_create(zfsvfs->z_os);
|
|
|
|
dmu_tx_hold_zap(tx, *objp ? *objp : DMU_NEW_OBJECT, B_TRUE, NULL);
|
|
|
|
if (*objp == 0) {
|
|
|
|
dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, B_TRUE,
|
|
|
|
zfs_userquota_prop_prefixes[type]);
|
|
|
|
}
|
|
|
|
if (fuid_dirtied)
|
|
|
|
zfs_fuid_txhold(zfsvfs, tx);
|
|
|
|
err = dmu_tx_assign(tx, TXG_WAIT);
|
|
|
|
if (err) {
|
|
|
|
dmu_tx_abort(tx);
|
|
|
|
return (err);
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_enter(&zfsvfs->z_lock);
|
|
|
|
if (*objp == 0) {
|
|
|
|
*objp = zap_create(zfsvfs->z_os, DMU_OT_USERGROUP_QUOTA,
|
|
|
|
DMU_OT_NONE, 0, tx);
|
|
|
|
VERIFY(0 == zap_add(zfsvfs->z_os, MASTER_NODE_OBJ,
|
|
|
|
zfs_userquota_prop_prefixes[type], 8, 1, objp, tx));
|
|
|
|
}
|
|
|
|
mutex_exit(&zfsvfs->z_lock);
|
|
|
|
|
|
|
|
if (quota == 0) {
|
|
|
|
err = zap_remove(zfsvfs->z_os, *objp, buf, tx);
|
|
|
|
if (err == ENOENT)
|
|
|
|
err = 0;
|
|
|
|
} else {
|
|
|
|
err = zap_update(zfsvfs->z_os, *objp, buf, 8, 1, "a, tx);
|
|
|
|
}
|
|
|
|
ASSERT(err == 0);
|
|
|
|
if (fuid_dirtied)
|
|
|
|
zfs_fuid_sync(zfsvfs, tx);
|
|
|
|
dmu_tx_commit(tx);
|
|
|
|
return (err);
|
|
|
|
}
|
|
|
|
|
|
|
|
boolean_t
|
|
|
|
zfs_id_overobjquota(zfsvfs_t *zfsvfs, uint64_t usedobj, uint64_t id)
|
|
|
|
{
|
|
|
|
char buf[20 + DMU_OBJACCT_PREFIX_LEN];
|
|
|
|
uint64_t used, quota, quotaobj;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (!dmu_objset_userobjspace_present(zfsvfs->z_os)) {
|
|
|
|
if (dmu_objset_userobjspace_upgradable(zfsvfs->z_os)) {
|
|
|
|
dsl_pool_config_enter(
|
|
|
|
dmu_objset_pool(zfsvfs->z_os), FTAG);
|
|
|
|
dmu_objset_id_quota_upgrade(zfsvfs->z_os);
|
|
|
|
dsl_pool_config_exit(
|
|
|
|
dmu_objset_pool(zfsvfs->z_os), FTAG);
|
|
|
|
}
|
|
|
|
return (B_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (usedobj == DMU_PROJECTUSED_OBJECT) {
|
|
|
|
if (!dmu_objset_projectquota_present(zfsvfs->z_os)) {
|
|
|
|
if (dmu_objset_projectquota_upgradable(zfsvfs->z_os)) {
|
|
|
|
dsl_pool_config_enter(
|
|
|
|
dmu_objset_pool(zfsvfs->z_os), FTAG);
|
|
|
|
dmu_objset_id_quota_upgrade(zfsvfs->z_os);
|
|
|
|
dsl_pool_config_exit(
|
|
|
|
dmu_objset_pool(zfsvfs->z_os), FTAG);
|
|
|
|
}
|
|
|
|
return (B_FALSE);
|
|
|
|
}
|
|
|
|
quotaobj = zfsvfs->z_projectobjquota_obj;
|
|
|
|
} else if (usedobj == DMU_USERUSED_OBJECT) {
|
|
|
|
quotaobj = zfsvfs->z_userobjquota_obj;
|
|
|
|
} else if (usedobj == DMU_GROUPUSED_OBJECT) {
|
|
|
|
quotaobj = zfsvfs->z_groupobjquota_obj;
|
|
|
|
} else {
|
|
|
|
return (B_FALSE);
|
|
|
|
}
|
|
|
|
if (quotaobj == 0 || zfsvfs->z_replay)
|
|
|
|
return (B_FALSE);
|
|
|
|
|
2020-06-07 21:42:12 +03:00
|
|
|
(void) snprintf(buf, sizeof (buf), "%llx", (longlong_t)id);
|
2019-12-11 23:12:08 +03:00
|
|
|
err = zap_lookup(zfsvfs->z_os, quotaobj, buf, 8, 1, "a);
|
|
|
|
if (err != 0)
|
|
|
|
return (B_FALSE);
|
|
|
|
|
2020-06-07 21:42:12 +03:00
|
|
|
(void) snprintf(buf, sizeof (buf), DMU_OBJACCT_PREFIX "%llx",
|
|
|
|
(longlong_t)id);
|
2019-12-11 23:12:08 +03:00
|
|
|
err = zap_lookup(zfsvfs->z_os, usedobj, buf, 8, 1, &used);
|
|
|
|
if (err != 0)
|
|
|
|
return (B_FALSE);
|
|
|
|
return (used >= quota);
|
|
|
|
}
|
|
|
|
|
|
|
|
boolean_t
|
|
|
|
zfs_id_overblockquota(zfsvfs_t *zfsvfs, uint64_t usedobj, uint64_t id)
|
|
|
|
{
|
|
|
|
char buf[20];
|
|
|
|
uint64_t used, quota, quotaobj;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (usedobj == DMU_PROJECTUSED_OBJECT) {
|
|
|
|
if (!dmu_objset_projectquota_present(zfsvfs->z_os)) {
|
|
|
|
if (dmu_objset_projectquota_upgradable(zfsvfs->z_os)) {
|
|
|
|
dsl_pool_config_enter(
|
|
|
|
dmu_objset_pool(zfsvfs->z_os), FTAG);
|
|
|
|
dmu_objset_id_quota_upgrade(zfsvfs->z_os);
|
|
|
|
dsl_pool_config_exit(
|
|
|
|
dmu_objset_pool(zfsvfs->z_os), FTAG);
|
|
|
|
}
|
|
|
|
return (B_FALSE);
|
|
|
|
}
|
|
|
|
quotaobj = zfsvfs->z_projectquota_obj;
|
|
|
|
} else if (usedobj == DMU_USERUSED_OBJECT) {
|
|
|
|
quotaobj = zfsvfs->z_userquota_obj;
|
|
|
|
} else if (usedobj == DMU_GROUPUSED_OBJECT) {
|
|
|
|
quotaobj = zfsvfs->z_groupquota_obj;
|
|
|
|
} else {
|
|
|
|
return (B_FALSE);
|
|
|
|
}
|
|
|
|
if (quotaobj == 0 || zfsvfs->z_replay)
|
|
|
|
return (B_FALSE);
|
|
|
|
|
2020-06-07 21:42:12 +03:00
|
|
|
(void) snprintf(buf, sizeof (buf), "%llx", (longlong_t)id);
|
2019-12-11 23:12:08 +03:00
|
|
|
err = zap_lookup(zfsvfs->z_os, quotaobj, buf, 8, 1, "a);
|
|
|
|
if (err != 0)
|
|
|
|
return (B_FALSE);
|
|
|
|
|
|
|
|
err = zap_lookup(zfsvfs->z_os, usedobj, buf, 8, 1, &used);
|
|
|
|
if (err != 0)
|
|
|
|
return (B_FALSE);
|
|
|
|
return (used >= quota);
|
|
|
|
}
|
|
|
|
|
|
|
|
boolean_t
|
|
|
|
zfs_id_overquota(zfsvfs_t *zfsvfs, uint64_t usedobj, uint64_t id)
|
|
|
|
{
|
|
|
|
return (zfs_id_overblockquota(zfsvfs, usedobj, id) ||
|
|
|
|
zfs_id_overobjquota(zfsvfs, usedobj, id));
|
|
|
|
}
|
|
|
|
|
File incorrectly zeroed when receiving incremental stream that toggles -L
Background:
By increasing the recordsize property above the default of 128KB, a
filesystem may have "large" blocks. By default, a send stream of such a
filesystem does not contain large WRITE records, instead it decreases
objects' block sizes to 128KB and splits the large blocks into 128KB
blocks, allowing the large-block filesystem to be received by a system
that does not support the `large_blocks` feature. A send stream
generated by `zfs send -L` (or `--large-block`) preserves the large
block size on the receiving system, by using large WRITE records.
When receiving an incremental send stream for a filesystem with large
blocks, if the send stream's -L flag was toggled, a bug is encountered
in which the file's contents are incorrectly zeroed out. The contents
of any blocks that were not modified by this send stream will be lost.
"Toggled" means that the previous send used `-L`, but this incremental
does not use `-L` (-L to no-L); or that the previous send did not use
`-L`, but this incremental does use `-L` (no-L to -L).
Changes:
This commit addresses the problem with several changes to the semantics
of zfs send/receive:
1. "-L to no-L" incrementals are rejected. If the previous send used
`-L`, but this incremental does not use `-L`, the `zfs receive` will
fail with this error message:
incremental send stream requires -L (--large-block), to match
previous receive.
2. "no-L to -L" incrementals are handled correctly, preserving the
smaller (128KB) block size of any already-received files that used large
blocks on the sending system but were split by `zfs send` without the
`-L` flag.
3. A new send stream format flag is added, `SWITCH_TO_LARGE_BLOCKS`.
This feature indicates that we can correctly handle "no-L to -L"
incrementals. This flag is currently not set on any send streams. In
the future, we intend for incremental send streams of snapshots that
have large blocks to use `-L` by default, and these streams will also
have the `SWITCH_TO_LARGE_BLOCKS` feature set. This ensures that streams
from the default use of `zfs send` won't encounter the bug mentioned
above, because they can't be received by software with the bug.
Implementation notes:
To facilitate accessing the ZPL's generation number,
`zfs_space_delta_cb()` has been renamed to `zpl_get_file_info()` and
restructured to fill in a struct with ZPL-specific info including owner
and generation.
In the "no-L to -L" case, if this is a compressed send stream (from
`zfs send -cL`), large WRITE records that are being written to small
(128KB) blocksize files need to be decompressed so that they can be
written split up into multiple blocks. The zio pipeline will recompress
each smaller block individually.
A new test case, `send-L_toggle`, is added, which tests the "no-L to -L"
case and verifies that we get an error for the "-L to no-L" case.
Reviewed-by: Paul Dagnelie <pcd@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Matthew Ahrens <mahrens@delphix.com>
Closes #6224
Closes #10383
2020-06-09 20:41:01 +03:00
|
|
|
EXPORT_SYMBOL(zpl_get_file_info);
|
2019-12-11 23:12:08 +03:00
|
|
|
EXPORT_SYMBOL(zfs_userspace_one);
|
|
|
|
EXPORT_SYMBOL(zfs_userspace_many);
|
|
|
|
EXPORT_SYMBOL(zfs_set_userquota);
|
|
|
|
EXPORT_SYMBOL(zfs_id_overblockquota);
|
|
|
|
EXPORT_SYMBOL(zfs_id_overobjquota);
|
|
|
|
EXPORT_SYMBOL(zfs_id_overquota);
|