summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Disseldorp <ddiss@samba.org>2015-02-11 00:24:16 +0100
committerJeremy Allison <jra@samba.org>2015-03-09 21:27:07 +0100
commit1359e859328567090bf80911083605e17fbd4519 (patch)
treed4fca3226a1bf6b13e64bbea26de10c4bfc7f862
parent47f15b14ae4f3a9d83ed6f7c9ad31e74978e9606 (diff)
downloadsamba-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.c112
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;