summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source3/smbd/smb2_ioctl.c121
1 files changed, 121 insertions, 0 deletions
diff --git a/source3/smbd/smb2_ioctl.c b/source3/smbd/smb2_ioctl.c
index 40276c5772..48487428de 100644
--- a/source3/smbd/smb2_ioctl.c
+++ b/source3/smbd/smb2_ioctl.c
@@ -24,6 +24,7 @@
#include "../libcli/smb/smb_common.h"
#include "../lib/util/tevent_ntstatus.h"
#include "rpc_server/srv_pipe_hnd.h"
+#include "include/ntioctl.h"
static struct tevent_req *smbd_smb2_ioctl_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
@@ -378,6 +379,126 @@ static struct tevent_req *smbd_smb2_ioctl_send(TALLOC_CTX *mem_ctx,
req);
return req;
+ case 0x00144064: /* FSCTL_SRV_ENUMERATE_SNAPSHOTS */
+ {
+ /*
+ * This is called to retrieve the number of Shadow Copies (a.k.a. snapshots)
+ * and return their volume names. If max_data_count is 16, then it is just
+ * asking for the number of volumes and length of the combined names.
+ *
+ * pdata is the data allocated by our caller, but that uses
+ * total_data_count (which is 0 in our case) rather than max_data_count.
+ * Allocate the correct amount and return the pointer to let
+ * it be deallocated when we return.
+ */
+ struct shadow_copy_data *shadow_data = NULL;
+ bool labels = False;
+ uint32_t labels_data_count = 0;
+ uint32_t data_count;
+ uint32_t i;
+ char *pdata;
+ NTSTATUS status;
+
+ if (in_max_output < 16) {
+ DEBUG(0,("FSCTL_GET_SHADOW_COPY_DATA: "
+ "in_max_output(%u) < 16 is invalid!\n",
+ in_max_output));
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ if (in_max_output > 16) {
+ labels = True;
+ }
+
+ shadow_data = TALLOC_ZERO_P(talloc_tos(),
+ struct shadow_copy_data);
+ if (tevent_req_nomem(shadow_data, req)) {
+ DEBUG(0,("TALLOC_ZERO() failed!\n"));
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * Call the VFS routine to actually do the work.
+ */
+ if (SMB_VFS_GET_SHADOW_COPY_DATA(fsp, shadow_data, labels)
+ != 0) {
+ if (errno == ENOSYS) {
+ DEBUG(5, ("FSCTL_GET_SHADOW_COPY_DATA: "
+ "connectpath %s, not supported.\n",
+ smbreq->conn->connectpath));
+ status = NT_STATUS_NOT_SUPPORTED;
+ } else {
+ DEBUG(0,("FSCTL_GET_SHADOW_COPY_DATA: "
+ "connectpath %s, failed.\n",
+ smbreq->conn->connectpath));
+ status = map_nt_error_from_unix(errno);
+ }
+ TALLOC_FREE(shadow_data);
+ tevent_req_nterror(req, status);
+ return tevent_req_post(req, ev);
+ }
+
+ labels_data_count =
+ (shadow_data->num_volumes*2*sizeof(SHADOW_COPY_LABEL))
+ + 2;
+
+ if (labels) {
+ data_count = 12+labels_data_count+4;
+ } else {
+ data_count = 16;
+ }
+
+ if (labels && (in_max_output < data_count)) {
+ DEBUG(0, ("FSCTL_GET_SHADOW_COPY_DATA: "
+ "in_max_output(%u) too small (%u) bytes "
+ "needed!\n", in_max_output, data_count));
+ TALLOC_FREE(shadow_data);
+ tevent_req_nterror(req, NT_STATUS_BUFFER_TOO_SMALL);
+ return tevent_req_post(req, ev);
+ }
+
+ state->out_output = data_blob_talloc(state, NULL, data_count);
+ if (tevent_req_nomem(state->out_output.data, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ pdata = (char *)state->out_output.data;
+
+ /* num_volumes 4 bytes */
+ SIVAL(pdata, 0, shadow_data->num_volumes);
+
+ if (labels) {
+ /* num_labels 4 bytes */
+ SIVAL(pdata, 4, shadow_data->num_volumes);
+ }
+
+ /* needed_data_count 4 bytes */
+ SIVAL(pdata, 8, labels_data_count+4);
+
+ pdata += 12;
+
+ DEBUG(10,("FSCTL_GET_SHADOW_COPY_DATA: %u volumes for "
+ "path[%s].\n",
+ shadow_data->num_volumes, fsp_str_dbg(fsp)));
+ if (labels && shadow_data->labels) {
+ for (i=0; i<shadow_data->num_volumes; i++) {
+ srvstr_push(pdata, smbreq->flags2,
+ pdata, shadow_data->labels[i],
+ 2*sizeof(SHADOW_COPY_LABEL),
+ STR_UNICODE|STR_TERMINATE);
+ pdata += 2*sizeof(SHADOW_COPY_LABEL);
+ DEBUGADD(10, ("Label[%u]: '%s'\n", i,
+ shadow_data->labels[i]));
+ }
+ }
+
+ TALLOC_FREE(shadow_data);
+
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
default:
if (IS_IPC(smbreq->conn)) {
tevent_req_nterror(req, NT_STATUS_FS_DRIVER_REQUIRED);