diff options
author | David Disseldorp <ddiss@samba.org> | 2015-02-11 00:24:16 +0100 |
---|---|---|
committer | Jeremy Allison <jra@samba.org> | 2015-03-09 21:27:07 +0100 |
commit | 1359e859328567090bf80911083605e17fbd4519 (patch) | |
tree | d4fca3226a1bf6b13e64bbea26de10c4bfc7f862 | |
parent | 47f15b14ae4f3a9d83ed6f7c9ad31e74978e9606 (diff) | |
download | samba-1359e859328567090bf80911083605e17fbd4519.tar.gz samba-1359e859328567090bf80911083605e17fbd4519.tar.xz samba-1359e859328567090bf80911083605e17fbd4519.zip |
smbd/ioctl: add FSCTL_SET_ZERO_DATA support
FSCTL_SET_ZERO_DATA can be used in two ways.
- When requested against a file marked as sparse, it provides a
mechanism for requesting that the server deallocate the underlying
disk space for the corresponding zeroed range.
- When requested against a non-sparse file, it indicates that the server
should allocate and zero the corresponding range.
Both use cases can be handled in Samba using fallocate(). The Linux
specific FALLOC_FL_PUNCH_HOLE flag can be used to deallocate the
underlying disk space. After doing so, a normal fallocate() call can
be used to ensure that the zeroed range is allocated on non-sparse
files.
FSCTL_SET_ZERO_DATA requests must not result in a change to the file
size. The FSCTL_SET_ZERO_DATA handler always calls fallocate() with the
KEEP_SIZE flag set, ensuring that Samba meets this requirement.
Signed-off-by: David Disseldorp <ddiss@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
-rw-r--r-- | source3/smbd/smb2_ioctl_filesys.c | 112 |
1 files changed, 111 insertions, 1 deletions
diff --git a/source3/smbd/smb2_ioctl_filesys.c b/source3/smbd/smb2_ioctl_filesys.c index 92bf63a9e2..63ec19661e 100644 --- a/source3/smbd/smb2_ioctl_filesys.c +++ b/source3/smbd/smb2_ioctl_filesys.c @@ -3,7 +3,7 @@ Core SMB2 server Copyright (C) Stefan Metzmacher 2009 - Copyright (C) David Disseldorp 2013 + Copyright (C) David Disseldorp 2013-2015 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -123,6 +123,108 @@ static NTSTATUS fsctl_set_cmprn(TALLOC_CTX *mem_ctx, return NT_STATUS_OK; } +static NTSTATUS fsctl_zero_data(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct files_struct *fsp, + DATA_BLOB *in_input) +{ + struct file_zero_data_info zdata_info; + enum ndr_err_code ndr_ret; + struct lock_struct lck; + int mode; + uint64_t len; + int ret; + NTSTATUS status; + + if (fsp == NULL) { + return NT_STATUS_FILE_CLOSED; + } + + /* WRITE_DATA permission is required */ + status = check_access(fsp->conn, fsp, NULL, FILE_WRITE_DATA); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* allow regardless of whether FS supports sparse or not */ + + ndr_ret = ndr_pull_struct_blob(in_input, mem_ctx, &zdata_info, + (ndr_pull_flags_fn_t)ndr_pull_file_zero_data_info); + if (ndr_ret != NDR_ERR_SUCCESS) { + DEBUG(0, ("failed to unmarshall zero data request\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + if (zdata_info.beyond_final_zero < zdata_info.file_off) { + DEBUG(0, ("invalid zero data params: off %lu, bfz, %lu\n", + (unsigned long)zdata_info.file_off, + (unsigned long)zdata_info.beyond_final_zero)); + return NT_STATUS_INVALID_PARAMETER; + } + + /* convert strange "beyond final zero" param into length */ + len = zdata_info.beyond_final_zero - zdata_info.file_off; + + if (len == 0) { + DEBUG(2, ("zero data called with zero length range\n")); + return NT_STATUS_OK; + } + + init_strict_lock_struct(fsp, + fsp->op->global->open_persistent_id, + zdata_info.file_off, + len, + WRITE_LOCK, + &lck); + + if (!SMB_VFS_STRICT_LOCK(fsp->conn, fsp, &lck)) { + DEBUG(2, ("failed to lock range for zero-data\n")); + return NT_STATUS_FILE_LOCK_CONFLICT; + } + + /* + * MS-FSCC <58> Section 2.3.65 + * This FSCTL sets the range of bytes to zero (0) without extending the + * file size. + * + * The VFS_FALLOCATE_FL_KEEP_SIZE flag is used to satisfy this + * constraint. + */ + + mode = VFS_FALLOCATE_FL_PUNCH_HOLE | VFS_FALLOCATE_FL_KEEP_SIZE; + ret = SMB_VFS_FALLOCATE(fsp, mode, zdata_info.file_off, len); + if (ret == -1) { + status = map_nt_error_from_unix_common(errno); + DEBUG(2, ("zero-data fallocate(0x%x) failed: %s\n", mode, + strerror(errno))); + SMB_VFS_STRICT_UNLOCK(fsp->conn, fsp, &lck); + return status; + } + + if (!fsp->is_sparse && lp_strict_allocate(SNUM(fsp->conn))) { + /* + * File marked non-sparse and "strict allocate" is enabled - + * allocate the range that we just punched out. + * In future FALLOC_FL_ZERO_RANGE could be used exclusively for + * this, but it's currently only supported on XFS and ext4. + * + * The newly allocated range still won't be found by SEEK_DATA + * for QAR, but stat.st_blocks will reflect it. + */ + ret = SMB_VFS_FALLOCATE(fsp, VFS_FALLOCATE_FL_KEEP_SIZE, + zdata_info.file_off, len); + if (ret == -1) { + status = map_nt_error_from_unix_common(errno); + DEBUG(0, ("fallocate failed: %s\n", strerror(errno))); + SMB_VFS_STRICT_UNLOCK(fsp->conn, fsp, &lck); + return status; + } + } + + SMB_VFS_STRICT_UNLOCK(fsp->conn, fsp, &lck); + return NT_STATUS_OK; +} + struct tevent_req *smb2_ioctl_filesys(uint32_t ctl_code, struct tevent_context *ev, struct tevent_req *req, @@ -148,6 +250,14 @@ struct tevent_req *smb2_ioctl_filesys(uint32_t ctl_code, } return tevent_req_post(req, ev); break; + case FSCTL_SET_ZERO_DATA: + status = fsctl_zero_data(state, ev, state->fsp, + &state->in_input); + if (!tevent_req_nterror(req, status)) { + tevent_req_done(req); + } + return tevent_req_post(req, ev); + break; default: { uint8_t *out_data = NULL; uint32_t out_data_len = 0; |