#include "client.h"
#include "async_smb.h"
#include "../libcli/smb/smbXcli_base.h"
-#include "smb2cli.h"
#include "cli_smb2_fnum.h"
#include "trans2.h"
#include "clirap.h"
#include "libsmb/proto.h"
#include "lib/util/tevent_ntstatus.h"
#include "../libcli/security/security.h"
+#include "../librpc/gen_ndr/ndr_security.h"
#include "lib/util_ea.h"
+#include "librpc/gen_ndr/ndr_ioctl.h"
+#include "ntioctl.h"
struct smb2_hnd {
uint64_t fid_persistent;
/***************************************************************
Small wrapper that allows SMB2 create to return a uint16_t fnum.
- Synchronous only.
***************************************************************/
+struct cli_smb2_create_fnum_state {
+ struct cli_state *cli;
+ struct smb_create_returns cr;
+ uint16_t fnum;
+ struct tevent_req *subreq;
+};
+
+static void cli_smb2_create_fnum_done(struct tevent_req *subreq);
+static bool cli_smb2_create_fnum_cancel(struct tevent_req *req);
+
+struct tevent_req *cli_smb2_create_fnum_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ uint32_t create_flags,
+ uint32_t desired_access,
+ uint32_t file_attributes,
+ uint32_t share_access,
+ uint32_t create_disposition,
+ uint32_t create_options)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_smb2_create_fnum_state *state;
+ size_t fname_len = 0;
+ const char *startp = NULL;
+ const char *endp = NULL;
+ time_t tstamp = (time_t)0;
+ struct smb2_create_blobs *cblobs = NULL;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_smb2_create_fnum_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->cli = cli;
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ if (cli->backup_intent) {
+ create_options |= FILE_OPEN_FOR_BACKUP_INTENT;
+ }
+
+ /* Check for @GMT- paths. Remove the @GMT and turn into TWrp if so. */
+ fname_len = strlen(fname);
+ if (clistr_is_previous_version_path(fname, &startp, &endp, &tstamp)) {
+ size_t len_before_gmt = startp - fname;
+ size_t len_after_gmt = fname + fname_len - endp;
+ DATA_BLOB twrp_blob;
+ NTTIME ntt;
+ NTSTATUS status;
+
+ char *new_fname = talloc_array(state, char,
+ len_before_gmt + len_after_gmt + 1);
+
+ if (tevent_req_nomem(new_fname, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ memcpy(new_fname, fname, len_before_gmt);
+ memcpy(new_fname + len_before_gmt, endp, len_after_gmt + 1);
+ fname = new_fname;
+ fname_len = len_before_gmt + len_after_gmt;
+
+ unix_to_nt_time(&ntt, tstamp);
+ twrp_blob = data_blob_const((const void *)&ntt, 8);
+
+ cblobs = talloc_zero(state, struct smb2_create_blobs);
+ if (tevent_req_nomem(cblobs, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ status = smb2_create_blob_add(state, cblobs,
+ SMB2_CREATE_TAG_TWRP, twrp_blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ /* SMB2 is pickier about pathnames. Ensure it doesn't
+ start in a '\' */
+ if (*fname == '\\') {
+ fname++;
+ fname_len--;
+ }
+
+ /* Or end in a '\' */
+ if (fname_len > 0 && fname[fname_len-1] == '\\') {
+ char *new_fname = talloc_strdup(state, fname);
+ if (tevent_req_nomem(new_fname, req)) {
+ return tevent_req_post(req, ev);
+ }
+ new_fname[fname_len-1] = '\0';
+ fname = new_fname;
+ }
+
+ subreq = smb2cli_create_send(state, ev,
+ cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ fname,
+ flags_to_smb2_oplock(create_flags),
+ SMB2_IMPERSONATION_IMPERSONATION,
+ desired_access,
+ file_attributes,
+ share_access,
+ create_disposition,
+ create_options,
+ cblobs);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_smb2_create_fnum_done, req);
+
+ state->subreq = subreq;
+ tevent_req_set_cancel_fn(req, cli_smb2_create_fnum_cancel);
+
+ return req;
+}
+
+static void cli_smb2_create_fnum_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_create_fnum_state *state = tevent_req_data(
+ req, struct cli_smb2_create_fnum_state);
+ struct smb2_hnd h;
+ NTSTATUS status;
+
+ status = smb2cli_create_recv(subreq, &h.fid_persistent,
+ &h.fid_volatile, &state->cr, NULL, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ status = map_smb2_handle_to_fnum(state->cli, &h, &state->fnum);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static bool cli_smb2_create_fnum_cancel(struct tevent_req *req)
+{
+ struct cli_smb2_create_fnum_state *state = tevent_req_data(
+ req, struct cli_smb2_create_fnum_state);
+ return tevent_req_cancel(state->subreq);
+}
+
+NTSTATUS cli_smb2_create_fnum_recv(struct tevent_req *req, uint16_t *pfnum,
+ struct smb_create_returns *cr)
+{
+ struct cli_smb2_create_fnum_state *state = tevent_req_data(
+ req, struct cli_smb2_create_fnum_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ state->cli->raw_status = status;
+ return status;
+ }
+ if (pfnum != NULL) {
+ *pfnum = state->fnum;
+ }
+ if (cr != NULL) {
+ *cr = state->cr;
+ }
+ state->cli->raw_status = NT_STATUS_OK;
+ return NT_STATUS_OK;
+}
+
NTSTATUS cli_smb2_create_fnum(struct cli_state *cli,
const char *fname,
uint32_t create_flags,
uint32_t create_disposition,
uint32_t create_options,
uint16_t *pfid,
- struct smb2_create_returns *cr)
+ struct smb_create_returns *cr)
{
- NTSTATUS status;
- struct smb2_hnd h;
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
if (smbXcli_conn_has_async_calls(cli->conn)) {
/*
* Can't use sync call while an async call is in flight
*/
- return NT_STATUS_INVALID_PARAMETER;
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_smb2_create_fnum_send(frame, ev, cli, fname, create_flags,
+ desired_access, file_attributes,
+ share_access, create_disposition,
+ create_options);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_smb2_create_fnum_recv(req, pfid, cr);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/***************************************************************
+ Small wrapper that allows SMB2 close to use a uint16_t fnum.
+***************************************************************/
+
+struct cli_smb2_close_fnum_state {
+ struct cli_state *cli;
+ uint16_t fnum;
+ struct smb2_hnd *ph;
+};
+
+static void cli_smb2_close_fnum_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_smb2_close_fnum_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_smb2_close_fnum_state *state;
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_smb2_close_fnum_state);
+ if (req == NULL) {
+ return NULL;
}
+ state->cli = cli;
+ state->fnum = fnum;
if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
- return NT_STATUS_INVALID_PARAMETER;
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
}
- if (cli->backup_intent) {
- create_options |= FILE_OPEN_FOR_BACKUP_INTENT;
+ status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
}
- /* SMB2 is pickier about pathnames. Ensure it doesn't
- start in a '\' */
- if (*fname == '\\') {
- fname++;
+ subreq = smb2cli_close_send(state, ev, cli->conn, cli->timeout,
+ cli->smb2.session, cli->smb2.tcon,
+ 0, state->ph->fid_persistent,
+ state->ph->fid_volatile);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
}
+ tevent_req_set_callback(subreq, cli_smb2_close_fnum_done, req);
+ return req;
+}
- status = smb2cli_create(cli->conn,
- cli->timeout,
- cli->smb2.session,
- cli->smb2.tcon,
- fname,
- flags_to_smb2_oplock(create_flags),
- SMB2_IMPERSONATION_IMPERSONATION,
- desired_access,
- file_attributes,
- share_access,
- create_disposition,
- create_options,
- NULL,
- &h.fid_persistent,
- &h.fid_volatile,
- cr);
+static void cli_smb2_close_fnum_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_close_fnum_state *state = tevent_req_data(
+ req, struct cli_smb2_close_fnum_state);
+ NTSTATUS status;
- if (NT_STATUS_IS_OK(status)) {
- status = map_smb2_handle_to_fnum(cli, &h, pfid);
+ status = smb2cli_close_recv(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
}
- return status;
+ /* Delete the fnum -> handle mapping. */
+ status = delete_smb2_handle_mapping(state->cli, &state->ph,
+ state->fnum);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
}
-/***************************************************************
- Small wrapper that allows SMB2 close to use a uint16_t fnum.
- Synchronous only.
-***************************************************************/
+NTSTATUS cli_smb2_close_fnum_recv(struct tevent_req *req)
+{
+ struct cli_smb2_close_fnum_state *state = tevent_req_data(
+ req, struct cli_smb2_close_fnum_state);
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ state->cli->raw_status = status;
+ }
+ tevent_req_received(req);
+ return status;
+}
NTSTATUS cli_smb2_close_fnum(struct cli_state *cli, uint16_t fnum)
{
- struct smb2_hnd *ph = NULL;
- NTSTATUS status;
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
if (smbXcli_conn_has_async_calls(cli->conn)) {
/*
* Can't use sync call while an async call is in flight
*/
- return NT_STATUS_INVALID_PARAMETER;
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_smb2_close_fnum_send(frame, ev, cli, fnum);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_smb2_close_fnum_recv(req);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct cli_smb2_delete_on_close_state {
+ struct cli_state *cli;
+ uint16_t fnum;
+ struct smb2_hnd *ph;
+ uint8_t data[1];
+ DATA_BLOB inbuf;
+};
+
+static void cli_smb2_delete_on_close_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_smb2_delete_on_close_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ bool flag)
+{
+ struct tevent_req *req = NULL;
+ struct cli_smb2_delete_on_close_state *state = NULL;
+ struct tevent_req *subreq = NULL;
+ uint8_t in_info_type;
+ uint8_t in_file_info_class;
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_smb2_delete_on_close_state);
+ if (req == NULL) {
+ return NULL;
}
+ state->cli = cli;
+ state->fnum = fnum;
if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
- return NT_STATUS_INVALID_PARAMETER;
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
}
- status = map_fnum_to_smb2_handle(cli,
- fnum,
- &ph);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
+ status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
}
- status = smb2cli_close(cli->conn,
- cli->timeout,
- cli->smb2.session,
- cli->smb2.tcon,
- 0,
- ph->fid_persistent,
- ph->fid_volatile);
+ /*
+ * setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
+ * level 13 (SMB_FILE_DISPOSITION_INFORMATION - 1000).
+ */
+ in_info_type = 1;
+ in_file_info_class = SMB_FILE_DISPOSITION_INFORMATION - 1000;
+ /* Setup data array. */
+ SCVAL(&state->data[0], 0, flag ? 1 : 0);
+ state->inbuf.data = &state->data[0];
+ state->inbuf.length = 1;
+
+ subreq = smb2cli_set_info_send(state, ev,
+ cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ in_info_type,
+ in_file_info_class,
+ &state->inbuf, /* in_input_buffer */
+ 0, /* in_additional_info */
+ state->ph->fid_persistent,
+ state->ph->fid_volatile);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq,
+ cli_smb2_delete_on_close_done,
+ req);
+ return req;
+}
- /* Delete the fnum -> handle mapping. */
- if (NT_STATUS_IS_OK(status)) {
- status = delete_smb2_handle_mapping(cli, &ph, fnum);
+static void cli_smb2_delete_on_close_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = smb2cli_set_info_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_smb2_delete_on_close_recv(struct tevent_req *req)
+{
+ struct cli_smb2_delete_on_close_state *state =
+ tevent_req_data(req,
+ struct cli_smb2_delete_on_close_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ state->cli->raw_status = status;
+ tevent_req_received(req);
+ return status;
}
+ state->cli->raw_status = NT_STATUS_OK;
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_smb2_delete_on_close(struct cli_state *cli, uint16_t fnum, bool flag)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_smb2_delete_on_close_send(frame, ev, cli, fnum, flag);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_smb2_delete_on_close_recv(req);
+ fail:
+ TALLOC_FREE(frame);
return status;
}
&fnum,
NULL);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
+ /*
+ * Naive option to match our SMB1 code. Assume the
+ * symlink path that tripped us up was the last
+ * component and try again. Eventually we will have to
+ * deal with the returned path unprocessed component. JRA.
+ */
+ status = cli_smb2_create_fnum(cli,
+ dname,
+ 0, /* create_flags */
+ DELETE_ACCESS, /* desired_access */
+ FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
+ FILE_OPEN, /* create_disposition */
+ FILE_DIRECTORY_FILE|
+ FILE_DELETE_ON_CLOSE|
+ FILE_OPEN_REPARSE_POINT, /* create_options */
+ &fnum,
+ NULL);
+ }
+
if (!NT_STATUS_IS_OK(status)) {
return status;
}
&fnum,
NULL);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
+ /*
+ * Naive option to match our SMB1 code. Assume the
+ * symlink path that tripped us up was the last
+ * component and try again. Eventually we will have to
+ * deal with the returned path unprocessed component. JRA.
+ */
+ status = cli_smb2_create_fnum(cli,
+ fname,
+ 0, /* create_flags */
+ DELETE_ACCESS, /* desired_access */
+ FILE_ATTRIBUTE_NORMAL, /* file attributes */
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
+ FILE_OPEN, /* create_disposition */
+ FILE_DELETE_ON_CLOSE|
+ FILE_OPEN_REPARSE_POINT, /* create_options */
+ &fnum,
+ NULL);
+ }
+
if (!NT_STATUS_IS_OK(status)) {
return status;
}
return NT_STATUS_INFO_LENGTH_MISMATCH;
}
+ finfo->btime_ts = interpret_long_date((const char *)dir_data + 8);
finfo->atime_ts = interpret_long_date((const char *)dir_data + 16);
finfo->mtime_ts = interpret_long_date((const char *)dir_data + 24);
finfo->ctime_ts = interpret_long_date((const char *)dir_data + 32);
NTSTATUS cli_smb2_list(struct cli_state *cli,
const char *pathname,
+ uint16_t attribute,
NTSTATUS (*fn)(const char *,
struct file_info *,
const char *,
char *parent_dir = NULL;
const char *mask = NULL;
struct smb2_hnd *ph = NULL;
+ bool processed_file = false;
TALLOC_CTX *frame = talloc_stackframe();
TALLOC_CTX *subframe = NULL;
+ bool mask_has_wild;
if (smbXcli_conn_has_async_calls(cli->conn)) {
/*
goto fail;
}
+ mask_has_wild = ms_has_wild(mask);
+
status = cli_smb2_create_fnum(cli,
parent_dir,
0, /* create_flags */
goto fail;
}
- status = fn(cli->dfs_mountpoint,
+ if (dir_check_ftype((uint32_t)finfo->mode,
+ (uint32_t)attribute)) {
+ /*
+ * Only process if attributes match.
+ * On SMB1 server does this, so on
+ * SMB2 we need to emulate in the
+ * client.
+ *
+ * https://bugzilla.samba.org/show_bug.cgi?id=10260
+ */
+ processed_file = true;
+
+ status = fn(cli->dfs_mountpoint,
finfo,
pathname,
state);
- if (!NT_STATUS_IS_OK(status)) {
- break;
+ if (!NT_STATUS_IS_OK(status)) {
+ break;
+ }
}
TALLOC_FREE(finfo);
TALLOC_FREE(subframe);
+ if (!mask_has_wild) {
+ /*
+ * MacOSX 10 doesn't set STATUS_NO_MORE_FILES
+ * when handed a non-wildcard path. Do it
+ * for the server (with a non-wildcard path
+ * there should only ever be one file returned.
+ */
+ status = STATUS_NO_MORE_FILES;
+ break;
+ }
+
} while (NT_STATUS_IS_OK(status));
if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
status = NT_STATUS_OK;
}
+ if (NT_STATUS_IS_OK(status) && !processed_file) {
+ /*
+ * In SMB1 findfirst returns NT_STATUS_NO_SUCH_FILE
+ * if no files match. Emulate this in the client.
+ */
+ status = NT_STATUS_NO_SUCH_FILE;
+ }
+
fail:
if (fnum != 0xffff) {
cli_smb2_close_fnum(cli, fnum);
}
+
+ cli->raw_status = status;
+
TALLOC_FREE(subframe);
TALLOC_FREE(frame);
return status;
uint32_t *attributes)
{
NTSTATUS status;
- struct smb2_create_returns cr;
+ struct smb_create_returns cr;
uint16_t fnum = 0xffff;
size_t namelen = strlen(name);
return status;
}
- cli_smb2_close_fnum(cli, fnum);
+ status = cli_smb2_close_fnum(cli, fnum);
ZERO_STRUCTP(sbuf);
- sbuf->st_ex_atime = nt_time_to_unix_timespec(&cr.last_access_time);
- sbuf->st_ex_mtime = nt_time_to_unix_timespec(&cr.last_write_time);
- sbuf->st_ex_ctime = nt_time_to_unix_timespec(&cr.change_time);
+ sbuf->st_ex_atime = nt_time_to_unix_timespec(cr.last_access_time);
+ sbuf->st_ex_mtime = nt_time_to_unix_timespec(cr.last_write_time);
+ sbuf->st_ex_ctime = nt_time_to_unix_timespec(cr.change_time);
sbuf->st_ex_size = cr.end_of_file;
*attributes = cr.file_attributes;
- return NT_STATUS_OK;
+ return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to check if a path is a directory.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_chkpath(struct cli_state *cli,
+ const char *name)
+{
+ NTSTATUS status;
+ uint16_t fnum = 0xffff;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* Ensure this is a directory. */
+ status = cli_smb2_create_fnum(cli,
+ name,
+ 0, /* create_flags */
+ FILE_READ_ATTRIBUTES, /* desired_access */
+ FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
+ FILE_OPEN, /* create_disposition */
+ FILE_DIRECTORY_FILE, /* create_options */
+ &fnum,
+ NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return cli_smb2_close_fnum(cli, fnum);
}
/***************************************************************
NTSTATUS status;
size_t namelen = strlen(name);
TALLOC_CTX *frame = talloc_stackframe();
+ uint32_t create_options = 0;
/* SMB2 is pickier about pathnames. Ensure it doesn't
end in a '\' */
0, /* file attributes */
FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
FILE_OPEN, /* create_disposition */
- 0, /* create_options */
+ create_options,
pfnum,
NULL);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
+ /*
+ * Naive option to match our SMB1 code. Assume the
+ * symlink path that tripped us up was the last
+ * component and try again. Eventually we will have to
+ * deal with the returned path unprocessed component. JRA.
+ */
+ create_options |= FILE_OPEN_REPARSE_POINT;
+ status = cli_smb2_create_fnum(cli,
+ name,
+ 0, /* create_flags */
+ desired_access,
+ 0, /* file attributes */
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
+ FILE_OPEN, /* create_disposition */
+ create_options,
+ pfnum,
+ NULL);
+ }
+
if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
+ create_options |= FILE_DIRECTORY_FILE;
status = cli_smb2_create_fnum(cli,
name,
0, /* create_flags */
if (fnum != 0xffff) {
cli_smb2_close_fnum(cli, fnum);
}
+
+ cli->raw_status = status;
+
TALLOC_FREE(frame);
return status;
}
fail:
+ cli->raw_status = status;
+
TALLOC_FREE(frame);
return status;
}
&change_time_ts,
NULL);
+ cli->raw_status = status;
+
if (!NT_STATUS_IS_OK(status)) {
return status;
}
cli_smb2_close_fnum(cli, fnum);
}
+ cli->raw_status = status;
+
TALLOC_FREE(frame);
return status;
}
cli_smb2_close_fnum(cli, fnum);
}
+ cli->raw_status = status;
+
TALLOC_FREE(frame);
return status;
}
cli_smb2_close_fnum(cli, fnum);
}
+ cli->raw_status = status;
+
TALLOC_FREE(frame);
return status;
}
/***************************************************************
- Wrapper that allows SMB2 to set pathname attributes.
+ Wrapper that allows SMB2 to set SMB_FILE_BASIC_INFORMATION on
+ a pathname.
Synchronous only.
***************************************************************/
-NTSTATUS cli_smb2_setatr(struct cli_state *cli,
+NTSTATUS cli_smb2_setpathinfo(struct cli_state *cli,
const char *name,
- uint16_t attr,
- time_t mtime)
+ uint8_t in_info_type,
+ uint8_t in_file_info_class,
+ const DATA_BLOB *p_in_data)
{
NTSTATUS status;
uint16_t fnum = 0xffff;
struct smb2_hnd *ph = NULL;
- uint8_t inbuf_store[40];
- DATA_BLOB inbuf = data_blob_null;
TALLOC_CTX *frame = talloc_stackframe();
if (smbXcli_conn_has_async_calls(cli->conn)) {
goto fail;
}
+ status = smb2cli_set_info(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ in_info_type,
+ in_file_info_class,
+ p_in_data, /* in_input_buffer */
+ 0, /* in_additional_info */
+ ph->fid_persistent,
+ ph->fid_volatile);
+ fail:
+
+ if (fnum != 0xffff) {
+ cli_smb2_close_fnum(cli, fnum);
+ }
+
+ cli->raw_status = status;
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+
+/***************************************************************
+ Wrapper that allows SMB2 to set pathname attributes.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_setatr(struct cli_state *cli,
+ const char *name,
+ uint16_t attr,
+ time_t mtime)
+{
+ uint8_t inbuf_store[40];
+ DATA_BLOB inbuf = data_blob_null;
+
/* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
inbuf.length = sizeof(inbuf_store);
data_blob_clear(&inbuf);
- SIVAL(inbuf.data,32,attr);
+ /*
+ * SMB1 uses attr == 0 to clear all attributes
+ * on a file (end up with FILE_ATTRIBUTE_NORMAL),
+ * and attr == FILE_ATTRIBUTE_NORMAL to mean ignore
+ * request attribute change.
+ *
+ * SMB2 uses exactly the reverse. Unfortunately as the
+ * cli_setatr() ABI is exposed inside libsmbclient,
+ * we must make the SMB2 cli_smb2_setatr() call
+ * export the same ABI as the SMB1 cli_setatr()
+ * which calls it. This means reversing the sense
+ * of the requested attr argument if it's zero
+ * or FILE_ATTRIBUTE_NORMAL.
+ *
+ * See BUG: https://bugzilla.samba.org/show_bug.cgi?id=12899
+ */
+
+ if (attr == 0) {
+ attr = FILE_ATTRIBUTE_NORMAL;
+ } else if (attr == FILE_ATTRIBUTE_NORMAL) {
+ attr = 0;
+ }
+
+ SSVAL(inbuf.data, 32, attr);
if (mtime != 0) {
put_long_date((char *)inbuf.data + 16,mtime);
}
SBVAL(inbuf.data, 8, 0xFFFFFFFFFFFFFFFFLL);
SBVAL(inbuf.data, 24, 0xFFFFFFFFFFFFFFFFLL);
- status = smb2cli_set_info(cli->conn,
- cli->timeout,
- cli->smb2.session,
- cli->smb2.tcon,
+ return cli_smb2_setpathinfo(cli,
+ name,
1, /* in_info_type */
- SMB_FILE_BASIC_INFORMATION - 1000, /* in_file_info_class */
- &inbuf, /* in_input_buffer */
- 0, /* in_additional_info */
- ph->fid_persistent,
- ph->fid_volatile);
- fail:
-
- if (fnum != 0xffff) {
- cli_smb2_close_fnum(cli, fnum);
- }
-
- TALLOC_FREE(frame);
- return status;
+ /* in_file_info_class */
+ SMB_FILE_BASIC_INFORMATION - 1000,
+ &inbuf);
}
+
/***************************************************************
Wrapper that allows SMB2 to set file handle times.
Synchronous only.
put_long_date((char *)inbuf.data + 16, write_time);
}
- return smb2cli_set_info(cli->conn,
+ cli->raw_status = smb2cli_set_info(cli->conn,
cli->timeout,
cli->smb2.session,
cli->smb2.tcon,
0, /* in_additional_info */
ph->fid_persistent,
ph->fid_volatile);
+
+ return cli->raw_status;
}
/***************************************************************
Synchronous only.
***************************************************************/
-NTSTATUS cli_smb2_dskattr(struct cli_state *cli, int *bsize, int *total, int *avail)
+NTSTATUS cli_smb2_dskattr(struct cli_state *cli, const char *path,
+ uint64_t *bsize, uint64_t *total, uint64_t *avail)
{
NTSTATUS status;
uint16_t fnum = 0xffff;
/* First open the top level directory. */
status = cli_smb2_create_fnum(cli,
- "",
+ path,
0, /* create_flags */
FILE_READ_ATTRIBUTES, /* desired_access */
FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
bytes_per_sector = IVAL(outbuf.data, 20);
if (bsize) {
- *bsize = (int)(sectors_per_unit * bytes_per_sector);
+ *bsize = (uint64_t)sectors_per_unit * (uint64_t)bytes_per_sector;
}
if (total) {
- *total = (int)total_size;
+ *total = total_size;
}
if (avail) {
- *avail = (int)size_free;
+ *avail = size_free;
}
status = NT_STATUS_OK;
cli_smb2_close_fnum(cli, fnum);
}
+ cli->raw_status = status;
+
TALLOC_FREE(frame);
return status;
}
/***************************************************************
- Wrapper that allows SMB2 to query a security descriptor.
+ Wrapper that allows SMB2 to query file system sizes.
Synchronous only.
***************************************************************/
-NTSTATUS cli_smb2_query_security_descriptor(struct cli_state *cli,
- uint16_t fnum,
- uint32_t sec_info,
- TALLOC_CTX *mem_ctx,
- struct security_descriptor **ppsd)
+NTSTATUS cli_smb2_get_fs_full_size_info(struct cli_state *cli,
+ uint64_t *total_allocation_units,
+ uint64_t *caller_allocation_units,
+ uint64_t *actual_allocation_units,
+ uint64_t *sectors_per_allocation_unit,
+ uint64_t *bytes_per_sector)
{
NTSTATUS status;
+ uint16_t fnum = 0xffff;
DATA_BLOB outbuf = data_blob_null;
struct smb2_hnd *ph = NULL;
- struct security_descriptor *lsd = NULL;
TALLOC_CTX *frame = talloc_stackframe();
if (smbXcli_conn_has_async_calls(cli->conn)) {
goto fail;
}
- status = map_fnum_to_smb2_handle(cli,
- fnum,
- &ph);
+ /* First open the top level directory. */
+ status =
+ cli_smb2_create_fnum(cli, "", 0, /* create_flags */
+ FILE_READ_ATTRIBUTES, /* desired_access */
+ FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
+ FILE_SHARE_READ | FILE_SHARE_WRITE |
+ FILE_SHARE_DELETE, /* share_access */
+ FILE_OPEN, /* create_disposition */
+ FILE_DIRECTORY_FILE, /* create_options */
+ &fnum,
+ NULL);
+
if (!NT_STATUS_IS_OK(status)) {
goto fail;
}
- /* getinfo on the returned handle with info_type SMB2_GETINFO_SEC (3) */
+ status = map_fnum_to_smb2_handle(cli, fnum, &ph);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
+ level 7 (SMB_FS_FULL_SIZE_INFORMATION). */
status = smb2cli_query_info(cli->conn,
cli->timeout,
cli->smb2.session,
cli->smb2.tcon,
- 3, /* in_info_type */
- 0, /* in_file_info_class */
+ SMB2_GETINFO_FS, /* in_info_type */
+ /* in_file_info_class */
+ SMB_FS_FULL_SIZE_INFORMATION - 1000,
0xFFFF, /* in_max_output_length */
NULL, /* in_input_buffer */
- sec_info, /* in_additional_info */
+ 0, /* in_additional_info */
0, /* in_flags */
ph->fid_persistent,
ph->fid_volatile,
frame,
&outbuf);
-
if (!NT_STATUS_IS_OK(status)) {
goto fail;
}
- /* Parse the reply. */
- status = unmarshall_sec_desc(mem_ctx,
- outbuf.data,
- outbuf.length,
- &lsd);
-
- if (!NT_STATUS_IS_OK(status)) {
+ if (outbuf.length < 32) {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
goto fail;
}
- if (ppsd != NULL) {
- *ppsd = lsd;
- } else {
- TALLOC_FREE(lsd);
+ *total_allocation_units = BIG_UINT(outbuf.data, 0);
+ *caller_allocation_units = BIG_UINT(outbuf.data, 8);
+ *actual_allocation_units = BIG_UINT(outbuf.data, 16);
+ *sectors_per_allocation_unit = (uint64_t)IVAL(outbuf.data, 24);
+ *bytes_per_sector = (uint64_t)IVAL(outbuf.data, 28);
+
+fail:
+
+ if (fnum != 0xffff) {
+ cli_smb2_close_fnum(cli, fnum);
}
- fail:
+ cli->raw_status = status;
TALLOC_FREE(frame);
return status;
}
/***************************************************************
- Wrapper that allows SMB2 to set a security descriptor.
+ Wrapper that allows SMB2 to query file system attributes.
Synchronous only.
***************************************************************/
-NTSTATUS cli_smb2_set_security_descriptor(struct cli_state *cli,
- uint16_t fnum,
- uint32_t sec_info,
- const struct security_descriptor *sd)
+NTSTATUS cli_smb2_get_fs_attr_info(struct cli_state *cli, uint32_t *fs_attr)
{
NTSTATUS status;
- DATA_BLOB inbuf = data_blob_null;
+ uint16_t fnum = 0xffff;
+ DATA_BLOB outbuf = data_blob_null;
struct smb2_hnd *ph = NULL;
TALLOC_CTX *frame = talloc_stackframe();
goto fail;
}
- status = map_fnum_to_smb2_handle(cli,
- fnum,
- &ph);
+ /* First open the top level directory. */
+ status =
+ cli_smb2_create_fnum(cli, "", 0, /* create_flags */
+ FILE_READ_ATTRIBUTES, /* desired_access */
+ FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
+ FILE_SHARE_READ | FILE_SHARE_WRITE |
+ FILE_SHARE_DELETE, /* share_access */
+ FILE_OPEN, /* create_disposition */
+ FILE_DIRECTORY_FILE, /* create_options */
+ &fnum,
+ NULL);
+
if (!NT_STATUS_IS_OK(status)) {
goto fail;
}
- status = marshall_sec_desc(frame,
- sd,
- &inbuf.data,
- &inbuf.length);
+ status = map_fnum_to_smb2_handle(cli, fnum, &ph);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
- if (!NT_STATUS_IS_OK(status)) {
+ status = smb2cli_query_info(cli->conn, cli->timeout, cli->smb2.session,
+ cli->smb2.tcon, 2, /* in_info_type */
+ 5, /* in_file_info_class */
+ 0xFFFF, /* in_max_output_length */
+ NULL, /* in_input_buffer */
+ 0, /* in_additional_info */
+ 0, /* in_flags */
+ ph->fid_persistent, ph->fid_volatile, frame,
+ &outbuf);
+ if (!NT_STATUS_IS_OK(status)) {
goto fail;
- }
+ }
- /* setinfo on the returned handle with info_type SMB2_SETINFO_SEC (3) */
+ if (outbuf.length < 12) {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto fail;
+ }
- status = smb2cli_set_info(cli->conn,
- cli->timeout,
- cli->smb2.session,
- cli->smb2.tcon,
- 3, /* in_info_type */
- 0, /* in_file_info_class */
- &inbuf, /* in_input_buffer */
- sec_info, /* in_additional_info */
- ph->fid_persistent,
- ph->fid_volatile);
+ *fs_attr = IVAL(outbuf.data, 0);
- fail:
+fail:
+
+ if (fnum != 0xffff) {
+ cli_smb2_close_fnum(cli, fnum);
+ }
+
+ cli->raw_status = status;
TALLOC_FREE(frame);
return status;
}
/***************************************************************
- Wrapper that allows SMB2 to rename a file.
+ Wrapper that allows SMB2 to query file system volume info.
Synchronous only.
***************************************************************/
-NTSTATUS cli_smb2_rename(struct cli_state *cli,
- const char *fname_src,
- const char *fname_dst)
+NTSTATUS cli_smb2_get_fs_volume_info(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ char **_volume_name,
+ uint32_t *pserial_number,
+ time_t *pdate)
{
NTSTATUS status;
- DATA_BLOB inbuf = data_blob_null;
uint16_t fnum = 0xffff;
+ DATA_BLOB outbuf = data_blob_null;
struct smb2_hnd *ph = NULL;
- smb_ucs2_t *converted_str = NULL;
- size_t converted_size_bytes = 0;
- size_t namelen = 0;
+ uint32_t nlen;
+ char *volume_name = NULL;
TALLOC_CTX *frame = talloc_stackframe();
if (smbXcli_conn_has_async_calls(cli->conn)) {
goto fail;
}
- status = get_fnum_from_path(cli,
- fname_src,
- DELETE_ACCESS,
- &fnum);
+ /* First open the top level directory. */
+ status =
+ cli_smb2_create_fnum(cli, "", 0, /* create_flags */
+ FILE_READ_ATTRIBUTES, /* desired_access */
+ FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
+ FILE_SHARE_READ | FILE_SHARE_WRITE |
+ FILE_SHARE_DELETE, /* share_access */
+ FILE_OPEN, /* create_disposition */
+ FILE_DIRECTORY_FILE, /* create_options */
+ &fnum,
+ NULL);
if (!NT_STATUS_IS_OK(status)) {
goto fail;
}
- status = map_fnum_to_smb2_handle(cli,
- fnum,
- &ph);
+ status = map_fnum_to_smb2_handle(cli, fnum, &ph);
if (!NT_STATUS_IS_OK(status)) {
goto fail;
}
- /* SMB2 is pickier about pathnames. Ensure it doesn't
- start in a '\' */
- if (*fname_dst == '\\') {
- fname_dst++;
- }
+ /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
+ level 1 (SMB_FS_VOLUME_INFORMATION). */
- /* SMB2 is pickier about pathnames. Ensure it doesn't
- end in a '\' */
+ status = smb2cli_query_info(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ SMB2_GETINFO_FS, /* in_info_type */
+ /* in_file_info_class */
+ SMB_FS_VOLUME_INFORMATION - 1000,
+ 0xFFFF, /* in_max_output_length */
+ NULL, /* in_input_buffer */
+ 0, /* in_additional_info */
+ 0, /* in_flags */
+ ph->fid_persistent,
+ ph->fid_volatile,
+ frame,
+ &outbuf);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ if (outbuf.length < 24) {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto fail;
+ }
+
+ if (pdate) {
+ struct timespec ts;
+ ts = interpret_long_date((char *)outbuf.data);
+ *pdate = ts.tv_sec;
+ }
+ if (pserial_number) {
+ *pserial_number = IVAL(outbuf.data,8);
+ }
+ nlen = IVAL(outbuf.data,12);
+ if (nlen + 18 < 18) {
+ /* Integer wrap. */
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto fail;
+ }
+ /*
+ * The next check is safe as we know outbuf.length >= 24
+ * from above.
+ */
+ if (nlen > (outbuf.length - 18)) {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto fail;
+ }
+
+ clistr_pull_talloc(mem_ctx,
+ (const char *)outbuf.data,
+ 0,
+ &volume_name,
+ outbuf.data + 18,
+ nlen,
+ STR_UNICODE);
+ if (volume_name == NULL) {
+ status = map_nt_error_from_unix(errno);
+ goto fail;
+ }
+
+ *_volume_name = volume_name;
+
+fail:
+
+ if (fnum != 0xffff) {
+ cli_smb2_close_fnum(cli, fnum);
+ }
+
+ cli->raw_status = status;
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+
+/***************************************************************
+ Wrapper that allows SMB2 to query a security descriptor.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_query_security_descriptor(struct cli_state *cli,
+ uint16_t fnum,
+ uint32_t sec_info,
+ TALLOC_CTX *mem_ctx,
+ struct security_descriptor **ppsd)
+{
+ NTSTATUS status;
+ DATA_BLOB outbuf = data_blob_null;
+ struct smb2_hnd *ph = NULL;
+ struct security_descriptor *lsd = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ status = map_fnum_to_smb2_handle(cli,
+ fnum,
+ &ph);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ /* getinfo on the returned handle with info_type SMB2_GETINFO_SEC (3) */
+
+ status = smb2cli_query_info(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ 3, /* in_info_type */
+ 0, /* in_file_info_class */
+ 0xFFFF, /* in_max_output_length */
+ NULL, /* in_input_buffer */
+ sec_info, /* in_additional_info */
+ 0, /* in_flags */
+ ph->fid_persistent,
+ ph->fid_volatile,
+ frame,
+ &outbuf);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ /* Parse the reply. */
+ status = unmarshall_sec_desc(mem_ctx,
+ outbuf.data,
+ outbuf.length,
+ &lsd);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ if (ppsd != NULL) {
+ *ppsd = lsd;
+ } else {
+ TALLOC_FREE(lsd);
+ }
+
+ fail:
+
+ cli->raw_status = status;
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to set a security descriptor.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_set_security_descriptor(struct cli_state *cli,
+ uint16_t fnum,
+ uint32_t sec_info,
+ const struct security_descriptor *sd)
+{
+ NTSTATUS status;
+ DATA_BLOB inbuf = data_blob_null;
+ struct smb2_hnd *ph = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ status = map_fnum_to_smb2_handle(cli,
+ fnum,
+ &ph);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = marshall_sec_desc(frame,
+ sd,
+ &inbuf.data,
+ &inbuf.length);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ /* setinfo on the returned handle with info_type SMB2_SETINFO_SEC (3) */
+
+ status = smb2cli_set_info(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ 3, /* in_info_type */
+ 0, /* in_file_info_class */
+ &inbuf, /* in_input_buffer */
+ sec_info, /* in_additional_info */
+ ph->fid_persistent,
+ ph->fid_volatile);
+
+ fail:
+
+ cli->raw_status = status;
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to rename a file.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_rename(struct cli_state *cli,
+ const char *fname_src,
+ const char *fname_dst,
+ bool replace)
+{
+ NTSTATUS status;
+ DATA_BLOB inbuf = data_blob_null;
+ uint16_t fnum = 0xffff;
+ struct smb2_hnd *ph = NULL;
+ smb_ucs2_t *converted_str = NULL;
+ size_t converted_size_bytes = 0;
+ size_t namelen = 0;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ status = get_fnum_from_path(cli,
+ fname_src,
+ DELETE_ACCESS,
+ &fnum);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = map_fnum_to_smb2_handle(cli,
+ fnum,
+ &ph);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ /* SMB2 is pickier about pathnames. Ensure it doesn't
+ start in a '\' */
+ if (*fname_dst == '\\') {
+ fname_dst++;
+ }
+
+ /* SMB2 is pickier about pathnames. Ensure it doesn't
+ end in a '\' */
namelen = strlen(fname_dst);
if (namelen > 0 && fname_dst[namelen-1] == '\\') {
char *modname = talloc_strdup(frame, fname_dst);
goto fail;
}
+ if (replace) {
+ SCVAL(inbuf.data, 0, 1);
+ }
+
SIVAL(inbuf.data, 16, converted_size_bytes);
memcpy(inbuf.data + 20, converted_str, converted_size_bytes);
cli_smb2_close_fnum(cli, fnum);
}
+ cli->raw_status = status;
+
TALLOC_FREE(frame);
return status;
}
fail:
+ cli->raw_status = status;
+
TALLOC_FREE(frame);
return status;
}
cli_smb2_close_fnum(cli, fnum);
}
+ cli->raw_status = status;
+
return status;
}
}
ea_count = 0;
for (eal = ea_list; eal; eal = eal->next) {
- (*pea_array)[ea_count++] = ea_list->ea;
+ (*pea_array)[ea_count++] = eal->ea;
}
*pnum_eas = ea_count;
}
cli_smb2_close_fnum(cli, fnum);
}
+ cli->raw_status = status;
+
TALLOC_FREE(frame);
return status;
}
-struct cli_smb2_read_state {
- struct tevent_context *ev;
- struct cli_state *cli;
- struct smb2_hnd *ph;
- uint64_t start_offset;
- uint32_t size;
+/***************************************************************
+ Wrapper that allows SMB2 to get user quota.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_get_user_quota(struct cli_state *cli,
+ int quota_fnum,
+ SMB_NTQUOTA_STRUCT *pqt)
+{
+ NTSTATUS status;
+ DATA_BLOB inbuf = data_blob_null;
+ DATA_BLOB outbuf = data_blob_null;
+ struct smb2_hnd *ph = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+ unsigned sid_len;
+ unsigned int offset;
+ uint8_t *buf;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ status = map_fnum_to_smb2_handle(cli, quota_fnum, &ph);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ sid_len = ndr_size_dom_sid(&pqt->sid, 0);
+
+ inbuf = data_blob_talloc_zero(frame, 24 + sid_len);
+ if (inbuf.data == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ buf = inbuf.data;
+
+ SCVAL(buf, 0, 1); /* ReturnSingle */
+ SCVAL(buf, 1, 0); /* RestartScan */
+ SSVAL(buf, 2, 0); /* Reserved */
+ if (8 + sid_len < 8) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ SIVAL(buf, 4, 8 + sid_len); /* SidListLength */
+ SIVAL(buf, 8, 0); /* StartSidLength */
+ SIVAL(buf, 12, 0); /* StartSidOffset */
+ SIVAL(buf, 16, 0); /* NextEntryOffset */
+ SIVAL(buf, 20, sid_len); /* SidLength */
+ sid_linearize(buf + 24, sid_len, &pqt->sid);
+
+ status = smb2cli_query_info(cli->conn, cli->timeout, cli->smb2.session,
+ cli->smb2.tcon, 4, /* in_info_type */
+ 0, /* in_file_info_class */
+ 0xFFFF, /* in_max_output_length */
+ &inbuf, /* in_input_buffer */
+ 0, /* in_additional_info */
+ 0, /* in_flags */
+ ph->fid_persistent, ph->fid_volatile, frame,
+ &outbuf);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ if (!parse_user_quota_record(outbuf.data, outbuf.length, &offset,
+ pqt)) {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ DEBUG(0, ("Got invalid FILE_QUOTA_INFORMATION in reply.\n"));
+ }
+
+fail:
+ cli->raw_status = status;
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to list user quota.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_list_user_quota_step(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int quota_fnum,
+ SMB_NTQUOTA_LIST **pqt_list,
+ bool first)
+{
+ NTSTATUS status;
+ DATA_BLOB inbuf = data_blob_null;
+ DATA_BLOB outbuf = data_blob_null;
+ struct smb2_hnd *ph = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+ uint8_t *buf;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto cleanup;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto cleanup;
+ }
+
+ status = map_fnum_to_smb2_handle(cli, quota_fnum, &ph);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup;
+ }
+
+ inbuf = data_blob_talloc_zero(frame, 16);
+ if (inbuf.data == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto cleanup;
+ }
+
+ buf = inbuf.data;
+
+ SCVAL(buf, 0, 0); /* ReturnSingle */
+ SCVAL(buf, 1, first ? 1 : 0); /* RestartScan */
+ SSVAL(buf, 2, 0); /* Reserved */
+ SIVAL(buf, 4, 0); /* SidListLength */
+ SIVAL(buf, 8, 0); /* StartSidLength */
+ SIVAL(buf, 12, 0); /* StartSidOffset */
+
+ status = smb2cli_query_info(cli->conn, cli->timeout, cli->smb2.session,
+ cli->smb2.tcon, 4, /* in_info_type */
+ 0, /* in_file_info_class */
+ 0xFFFF, /* in_max_output_length */
+ &inbuf, /* in_input_buffer */
+ 0, /* in_additional_info */
+ 0, /* in_flags */
+ ph->fid_persistent, ph->fid_volatile, frame,
+ &outbuf);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup;
+ }
+
+ status = parse_user_quota_list(outbuf.data, outbuf.length, mem_ctx,
+ pqt_list);
+
+cleanup:
+ cli->raw_status = status;
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to get file system quota.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_get_fs_quota_info(struct cli_state *cli,
+ int quota_fnum,
+ SMB_NTQUOTA_STRUCT *pqt)
+{
+ NTSTATUS status;
+ DATA_BLOB outbuf = data_blob_null;
+ struct smb2_hnd *ph = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto cleanup;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto cleanup;
+ }
+
+ status = map_fnum_to_smb2_handle(cli, quota_fnum, &ph);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup;
+ }
+
+ status = smb2cli_query_info(
+ cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon,
+ 2, /* in_info_type */
+ SMB_FS_QUOTA_INFORMATION - 1000, /* in_file_info_class */
+ 0xFFFF, /* in_max_output_length */
+ NULL, /* in_input_buffer */
+ 0, /* in_additional_info */
+ 0, /* in_flags */
+ ph->fid_persistent, ph->fid_volatile, frame, &outbuf);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup;
+ }
+
+ status = parse_fs_quota_buffer(outbuf.data, outbuf.length, pqt);
+
+cleanup:
+ cli->raw_status = status;
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to set user quota.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_set_user_quota(struct cli_state *cli,
+ int quota_fnum,
+ SMB_NTQUOTA_LIST *qtl)
+{
+ NTSTATUS status;
+ DATA_BLOB inbuf = data_blob_null;
+ struct smb2_hnd *ph = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto cleanup;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto cleanup;
+ }
+
+ status = map_fnum_to_smb2_handle(cli, quota_fnum, &ph);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup;
+ }
+
+ status = build_user_quota_buffer(qtl, 0, talloc_tos(), &inbuf, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup;
+ }
+
+ status = smb2cli_set_info(cli->conn, cli->timeout, cli->smb2.session,
+ cli->smb2.tcon, 4, /* in_info_type */
+ 0, /* in_file_info_class */
+ &inbuf, /* in_input_buffer */
+ 0, /* in_additional_info */
+ ph->fid_persistent, ph->fid_volatile);
+cleanup:
+
+ cli->raw_status = status;
+
+ TALLOC_FREE(frame);
+
+ return status;
+}
+
+NTSTATUS cli_smb2_set_fs_quota_info(struct cli_state *cli,
+ int quota_fnum,
+ SMB_NTQUOTA_STRUCT *pqt)
+{
+ NTSTATUS status;
+ DATA_BLOB inbuf = data_blob_null;
+ struct smb2_hnd *ph = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto cleanup;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto cleanup;
+ }
+
+ status = map_fnum_to_smb2_handle(cli, quota_fnum, &ph);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup;
+ }
+
+ status = build_fs_quota_buffer(talloc_tos(), pqt, &inbuf, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup;
+ }
+
+ status = smb2cli_set_info(
+ cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon,
+ 2, /* in_info_type */
+ SMB_FS_QUOTA_INFORMATION - 1000, /* in_file_info_class */
+ &inbuf, /* in_input_buffer */
+ 0, /* in_additional_info */
+ ph->fid_persistent, ph->fid_volatile);
+cleanup:
+ cli->raw_status = status;
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct cli_smb2_read_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ struct smb2_hnd *ph;
+ uint64_t start_offset;
+ uint32_t size;
uint32_t received;
uint8_t *buf;
};
req, struct cli_smb2_read_state);
if (tevent_req_is_nterror(req, &status)) {
+ state->cli->raw_status = status;
return status;
}
/*
*/
*received = (ssize_t)state->received;
*rcvbuf = state->buf;
+ state->cli->raw_status = NT_STATUS_OK;
return NT_STATUS_OK;
}
NTSTATUS status;
if (tevent_req_is_nterror(req, &status)) {
+ state->cli->raw_status = status;
tevent_req_received(req);
return status;
}
if (pwritten != NULL) {
*pwritten = (size_t)state->written;
}
+ state->cli->raw_status = NT_STATUS_OK;
tevent_req_received(req);
return NT_STATUS_OK;
}
NTSTATUS status;
if (tevent_req_is_nterror(req, &status)) {
+ state->cli->raw_status = status;
return status;
}
if (pwritten != NULL) {
*pwritten = (size_t)state->written;
}
+ state->cli->raw_status = NT_STATUS_OK;
+ return NT_STATUS_OK;
+}
+
+struct cli_smb2_splice_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ struct smb2_hnd *src_ph;
+ struct smb2_hnd *dst_ph;
+ int (*splice_cb)(off_t n, void *priv);
+ void *priv;
+ off_t written;
+ off_t size;
+ off_t src_offset;
+ off_t dst_offset;
+ bool resized;
+ struct req_resume_key_rsp resume_rsp;
+ struct srv_copychunk_copy cc_copy;
+};
+
+static void cli_splice_copychunk_send(struct cli_smb2_splice_state *state,
+ struct tevent_req *req);
+
+static void cli_splice_copychunk_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_splice_state *state =
+ tevent_req_data(req,
+ struct cli_smb2_splice_state);
+ struct smbXcli_conn *conn = state->cli->conn;
+ DATA_BLOB out_input_buffer = data_blob_null;
+ DATA_BLOB out_output_buffer = data_blob_null;
+ struct srv_copychunk_rsp cc_copy_rsp;
+ enum ndr_err_code ndr_ret;
+ NTSTATUS status;
+
+ status = smb2cli_ioctl_recv(subreq, state,
+ &out_input_buffer,
+ &out_output_buffer);
+ TALLOC_FREE(subreq);
+ if ((!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER) ||
+ state->resized) && tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ ndr_ret = ndr_pull_struct_blob(&out_output_buffer, state, &cc_copy_rsp,
+ (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
+ if (ndr_ret != NDR_ERR_SUCCESS) {
+ DEBUG(0, ("failed to unmarshall copy chunk rsp\n"));
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ uint32_t max_chunks = MIN(cc_copy_rsp.chunks_written,
+ cc_copy_rsp.total_bytes_written / cc_copy_rsp.chunk_bytes_written);
+ if ((cc_copy_rsp.chunk_bytes_written > smb2cli_conn_cc_chunk_len(conn) ||
+ max_chunks > smb2cli_conn_cc_max_chunks(conn)) &&
+ tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ state->resized = true;
+ smb2cli_conn_set_cc_chunk_len(conn, cc_copy_rsp.chunk_bytes_written);
+ smb2cli_conn_set_cc_max_chunks(conn, max_chunks);
+ } else {
+ if ((state->src_offset > INT64_MAX - cc_copy_rsp.total_bytes_written) ||
+ (state->dst_offset > INT64_MAX - cc_copy_rsp.total_bytes_written) ||
+ (state->written > INT64_MAX - cc_copy_rsp.total_bytes_written)) {
+ tevent_req_nterror(req, NT_STATUS_FILE_TOO_LARGE);
+ return;
+ }
+ state->src_offset += cc_copy_rsp.total_bytes_written;
+ state->dst_offset += cc_copy_rsp.total_bytes_written;
+ state->written += cc_copy_rsp.total_bytes_written;
+ if (!state->splice_cb(state->written, state->priv)) {
+ tevent_req_nterror(req, NT_STATUS_CANCELLED);
+ return;
+ }
+ }
+
+ cli_splice_copychunk_send(state, req);
+}
+
+static void cli_splice_copychunk_send(struct cli_smb2_splice_state *state,
+ struct tevent_req *req)
+{
+ struct tevent_req *subreq;
+ enum ndr_err_code ndr_ret;
+ struct smbXcli_conn *conn = state->cli->conn;
+ struct srv_copychunk_copy *cc_copy = &state->cc_copy;
+ off_t src_offset = state->src_offset;
+ off_t dst_offset = state->dst_offset;
+ uint32_t req_len = MIN(smb2cli_conn_cc_chunk_len(conn) * smb2cli_conn_cc_max_chunks(conn),
+ state->size - state->written);
+ DATA_BLOB in_input_buffer = data_blob_null;
+ DATA_BLOB in_output_buffer = data_blob_null;
+
+ if (state->size - state->written == 0) {
+ tevent_req_done(req);
+ return;
+ }
+
+ cc_copy->chunk_count = 0;
+ while (req_len) {
+ cc_copy->chunks[cc_copy->chunk_count].source_off = src_offset;
+ cc_copy->chunks[cc_copy->chunk_count].target_off = dst_offset;
+ cc_copy->chunks[cc_copy->chunk_count].length = MIN(req_len,
+ smb2cli_conn_cc_chunk_len(conn));
+ if (req_len < cc_copy->chunks[cc_copy->chunk_count].length) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+ req_len -= cc_copy->chunks[cc_copy->chunk_count].length;
+ if ((src_offset > INT64_MAX - cc_copy->chunks[cc_copy->chunk_count].length) ||
+ (dst_offset > INT64_MAX - cc_copy->chunks[cc_copy->chunk_count].length)) {
+ tevent_req_nterror(req, NT_STATUS_FILE_TOO_LARGE);
+ return;
+ }
+ src_offset += cc_copy->chunks[cc_copy->chunk_count].length;
+ dst_offset += cc_copy->chunks[cc_copy->chunk_count].length;
+ cc_copy->chunk_count++;
+ }
+
+ ndr_ret = ndr_push_struct_blob(&in_input_buffer, state, cc_copy,
+ (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
+ if (ndr_ret != NDR_ERR_SUCCESS) {
+ DEBUG(0, ("failed to marshall copy chunk req\n"));
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ subreq = smb2cli_ioctl_send(state, state->ev, state->cli->conn,
+ state->cli->timeout,
+ state->cli->smb2.session,
+ state->cli->smb2.tcon,
+ state->dst_ph->fid_persistent, /* in_fid_persistent */
+ state->dst_ph->fid_volatile, /* in_fid_volatile */
+ FSCTL_SRV_COPYCHUNK_WRITE,
+ 0, /* in_max_input_length */
+ &in_input_buffer,
+ 12, /* in_max_output_length */
+ &in_output_buffer,
+ SMB2_IOCTL_FLAG_IS_FSCTL);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ cli_splice_copychunk_done,
+ req);
+}
+
+static void cli_splice_key_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_splice_state *state =
+ tevent_req_data(req,
+ struct cli_smb2_splice_state);
+ enum ndr_err_code ndr_ret;
+ NTSTATUS status;
+
+ DATA_BLOB out_input_buffer = data_blob_null;
+ DATA_BLOB out_output_buffer = data_blob_null;
+
+ status = smb2cli_ioctl_recv(subreq, state,
+ &out_input_buffer,
+ &out_output_buffer);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ ndr_ret = ndr_pull_struct_blob(&out_output_buffer,
+ state, &state->resume_rsp,
+ (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
+ if (ndr_ret != NDR_ERR_SUCCESS) {
+ DEBUG(0, ("failed to unmarshall resume key rsp\n"));
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ memcpy(&state->cc_copy.source_key,
+ &state->resume_rsp.resume_key,
+ sizeof state->resume_rsp.resume_key);
+
+ cli_splice_copychunk_send(state, req);
+}
+
+struct tevent_req *cli_smb2_splice_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t src_fnum, uint16_t dst_fnum,
+ off_t size, off_t src_offset, off_t dst_offset,
+ int (*splice_cb)(off_t n, void *priv),
+ void *priv)
+{
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ struct cli_smb2_splice_state *state;
+ NTSTATUS status;
+ DATA_BLOB in_input_buffer = data_blob_null;
+ DATA_BLOB in_output_buffer = data_blob_null;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_smb2_splice_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->cli = cli;
+ state->ev = ev;
+ state->splice_cb = splice_cb;
+ state->priv = priv;
+ state->size = size;
+ state->written = 0;
+ state->src_offset = src_offset;
+ state->dst_offset = dst_offset;
+ state->cc_copy.chunks = talloc_array(state,
+ struct srv_copychunk,
+ smb2cli_conn_cc_max_chunks(cli->conn));
+ if (state->cc_copy.chunks == NULL) {
+ return NULL;
+ }
+
+ status = map_fnum_to_smb2_handle(cli, src_fnum, &state->src_ph);
+ if (tevent_req_nterror(req, status))
+ return tevent_req_post(req, ev);
+
+ status = map_fnum_to_smb2_handle(cli, dst_fnum, &state->dst_ph);
+ if (tevent_req_nterror(req, status))
+ return tevent_req_post(req, ev);
+
+ subreq = smb2cli_ioctl_send(state, ev, cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ state->src_ph->fid_persistent, /* in_fid_persistent */
+ state->src_ph->fid_volatile, /* in_fid_volatile */
+ FSCTL_SRV_REQUEST_RESUME_KEY,
+ 0, /* in_max_input_length */
+ &in_input_buffer,
+ 32, /* in_max_output_length */
+ &in_output_buffer,
+ SMB2_IOCTL_FLAG_IS_FSCTL);
+ if (tevent_req_nomem(subreq, req)) {
+ return NULL;
+ }
+ tevent_req_set_callback(subreq,
+ cli_splice_key_done,
+ req);
+
+ return req;
+}
+
+NTSTATUS cli_smb2_splice_recv(struct tevent_req *req, off_t *written)
+{
+ struct cli_smb2_splice_state *state = tevent_req_data(
+ req, struct cli_smb2_splice_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ state->cli->raw_status = status;
+ tevent_req_received(req);
+ return status;
+ }
+ if (written != NULL) {
+ *written = state->written;
+ }
+ state->cli->raw_status = NT_STATUS_OK;
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+/***************************************************************
+ SMB2 enum shadow copy data.
+***************************************************************/
+
+struct cli_smb2_shadow_copy_data_fnum_state {
+ struct cli_state *cli;
+ uint16_t fnum;
+ struct smb2_hnd *ph;
+ DATA_BLOB out_input_buffer;
+ DATA_BLOB out_output_buffer;
+};
+
+static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_smb2_shadow_copy_data_fnum_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ bool get_names)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_smb2_shadow_copy_data_fnum_state *state;
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_smb2_shadow_copy_data_fnum_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ state->cli = cli;
+ state->fnum = fnum;
+
+ status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * TODO. Under SMB2 we should send a zero max_output_length
+ * ioctl to get the required size, then send another ioctl
+ * to get the data, but the current SMB1 implementation just
+ * does one roundtrip with a 64K buffer size. Do the same
+ * for now. JRA.
+ */
+
+ subreq = smb2cli_ioctl_send(state, ev, state->cli->conn,
+ state->cli->timeout,
+ state->cli->smb2.session,
+ state->cli->smb2.tcon,
+ state->ph->fid_persistent, /* in_fid_persistent */
+ state->ph->fid_volatile, /* in_fid_volatile */
+ FSCTL_GET_SHADOW_COPY_DATA,
+ 0, /* in_max_input_length */
+ NULL, /* in_input_buffer */
+ get_names ?
+ CLI_BUFFER_SIZE : 16, /* in_max_output_length */
+ NULL, /* in_output_buffer */
+ SMB2_IOCTL_FLAG_IS_FSCTL);
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq,
+ cli_smb2_shadow_copy_data_fnum_done,
+ req);
+
+ return req;
+}
+
+static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_shadow_copy_data_fnum_state *state = tevent_req_data(
+ req, struct cli_smb2_shadow_copy_data_fnum_state);
+ NTSTATUS status;
+
+ status = smb2cli_ioctl_recv(subreq, state,
+ &state->out_input_buffer,
+ &state->out_output_buffer);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static NTSTATUS cli_smb2_shadow_copy_data_fnum_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ bool get_names,
+ char ***pnames,
+ int *pnum_names)
+{
+ struct cli_smb2_shadow_copy_data_fnum_state *state = tevent_req_data(
+ req, struct cli_smb2_shadow_copy_data_fnum_state);
+ char **names = NULL;
+ uint32_t num_names = 0;
+ uint32_t num_names_returned = 0;
+ uint32_t dlength = 0;
+ uint32_t i;
+ uint8_t *endp = NULL;
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+
+ if (state->out_output_buffer.length < 16) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ num_names = IVAL(state->out_output_buffer.data, 0);
+ num_names_returned = IVAL(state->out_output_buffer.data, 4);
+ dlength = IVAL(state->out_output_buffer.data, 8);
+
+ if (num_names > 0x7FFFFFFF) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ if (get_names == false) {
+ *pnum_names = (int)num_names;
+ return NT_STATUS_OK;
+ }
+ if (num_names != num_names_returned) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ if (dlength + 12 < 12) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ /*
+ * NB. The below is an allowable return if there are
+ * more snapshots than the buffer size we told the
+ * server we can receive. We currently don't support
+ * this.
+ */
+ if (dlength + 12 > state->out_output_buffer.length) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ if (state->out_output_buffer.length +
+ (2 * sizeof(SHADOW_COPY_LABEL)) <
+ state->out_output_buffer.length) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ names = talloc_array(mem_ctx, char *, num_names_returned);
+ if (names == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ endp = state->out_output_buffer.data +
+ state->out_output_buffer.length;
+
+ for (i=0; i<num_names_returned; i++) {
+ bool ret;
+ uint8_t *src;
+ size_t converted_size;
+
+ src = state->out_output_buffer.data + 12 +
+ (i * 2 * sizeof(SHADOW_COPY_LABEL));
+
+ if (src + (2 * sizeof(SHADOW_COPY_LABEL)) > endp) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ ret = convert_string_talloc(
+ names, CH_UTF16LE, CH_UNIX,
+ src, 2 * sizeof(SHADOW_COPY_LABEL),
+ &names[i], &converted_size);
+ if (!ret) {
+ TALLOC_FREE(names);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ }
+ *pnum_names = num_names;
+ *pnames = names;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_smb2_shadow_copy_data(TALLOC_CTX *mem_ctx,
+ struct cli_state *cli,
+ uint16_t fnum,
+ bool get_names,
+ char ***pnames,
+ int *pnum_names)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_smb2_shadow_copy_data_fnum_send(frame,
+ ev,
+ cli,
+ fnum,
+ get_names);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_smb2_shadow_copy_data_fnum_recv(req,
+ mem_ctx,
+ get_names,
+ pnames,
+ pnum_names);
+ fail:
+ cli->raw_status = status;
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to truncate a file.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_ftruncate(struct cli_state *cli,
+ uint16_t fnum,
+ uint64_t newsize)
+{
+ NTSTATUS status;
+ DATA_BLOB inbuf = data_blob_null;
+ struct smb2_hnd *ph = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ status = map_fnum_to_smb2_handle(cli,
+ fnum,
+ &ph);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ inbuf = data_blob_talloc_zero(frame, 8);
+ if (inbuf.data == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ SBVAL(inbuf.data, 0, newsize);
+
+ /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
+ level 20 (SMB_FILE_END_OF_FILE_INFORMATION - 1000). */
+
+ status = smb2cli_set_info(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ 1, /* in_info_type */
+ /* in_file_info_class */
+ SMB_FILE_END_OF_FILE_INFORMATION - 1000,
+ &inbuf, /* in_input_buffer */
+ 0, /* in_additional_info */
+ ph->fid_persistent,
+ ph->fid_volatile);
+
+ fail:
+
+ cli->raw_status = status;
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+NTSTATUS cli_smb2_notify(struct cli_state *cli, uint16_t fnum,
+ uint32_t buffer_size, uint32_t completion_filter,
+ bool recursive, TALLOC_CTX *mem_ctx,
+ struct notify_change **pchanges,
+ uint32_t *pnum_changes)
+{
+ NTSTATUS status;
+ struct smb2_hnd *ph = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+ uint8_t *base;
+ uint32_t len, ofs;
+ struct notify_change *changes = NULL;
+ size_t num_changes = 0;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ status = map_fnum_to_smb2_handle(cli, fnum, &ph);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = smb2cli_notify(cli->conn, cli->timeout,
+ cli->smb2.session, cli->smb2.tcon,
+ buffer_size,
+ ph->fid_persistent, ph->fid_volatile,
+ completion_filter, recursive,
+ frame, &base, &len);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ len = 0;
+ status = NT_STATUS_OK;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ ofs = 0;
+
+ while (len - ofs >= 12) {
+ struct notify_change *tmp;
+ struct notify_change *c;
+ uint32_t next_ofs = IVAL(base, ofs);
+ uint32_t file_name_length = IVAL(base, ofs+8);
+ size_t namelen;
+ bool ok;
+
+ tmp = talloc_realloc(frame, changes, struct notify_change,
+ num_changes + 1);
+ if (tmp == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ changes = tmp;
+ c = &changes[num_changes];
+ num_changes += 1;
+
+ if (smb_buffer_oob(len, ofs, next_ofs) ||
+ smb_buffer_oob(len, ofs+12, file_name_length)) {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto fail;
+ }
+
+ c->action = IVAL(base, ofs+4);
+
+ ok = convert_string_talloc(changes, CH_UTF16LE, CH_UNIX,
+ base + ofs + 12, file_name_length,
+ &c->name, &namelen);
+ if (!ok) {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto fail;
+ }
+
+ if (next_ofs == 0) {
+ break;
+ }
+ ofs += next_ofs;
+ }
+
+ *pchanges = talloc_move(mem_ctx, &changes);
+ *pnum_changes = num_changes;
+ status = NT_STATUS_OK;
+
+fail:
+ cli->raw_status = status;
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct cli_smb2_set_reparse_point_fnum_state {
+ struct cli_state *cli;
+ uint16_t fnum;
+ struct smb2_hnd *ph;
+ DATA_BLOB input_buffer;
+};
+
+static void cli_smb2_set_reparse_point_fnum_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_smb2_set_reparse_point_fnum_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ DATA_BLOB in_buf)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_smb2_set_reparse_point_fnum_state *state = NULL;
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_smb2_set_reparse_point_fnum_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ state->cli = cli;
+ state->fnum = fnum;
+
+ status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->input_buffer = data_blob_talloc(state,
+ in_buf.data,
+ in_buf.length);
+ if (state->input_buffer.data == NULL) {
+ tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = smb2cli_ioctl_send(state, ev, state->cli->conn,
+ state->cli->timeout,
+ state->cli->smb2.session,
+ state->cli->smb2.tcon,
+ state->ph->fid_persistent, /* in_fid_persistent */
+ state->ph->fid_volatile, /* in_fid_volatile */
+ FSCTL_SET_REPARSE_POINT,
+ 0, /* in_max_input_length */
+ &state->input_buffer ,
+ 0,
+ NULL,
+ SMB2_IOCTL_FLAG_IS_FSCTL);
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq,
+ cli_smb2_set_reparse_point_fnum_done,
+ req);
+
+ return req;
+}
+
+static void cli_smb2_set_reparse_point_fnum_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_set_reparse_point_fnum_state *state = tevent_req_data(
+ req, struct cli_smb2_set_reparse_point_fnum_state);
+ NTSTATUS status;
+
+ status = smb2cli_ioctl_recv(subreq, state,
+ NULL,
+ NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_smb2_set_reparse_point_fnum_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+struct cli_smb2_get_reparse_point_fnum_state {
+ struct cli_state *cli;
+ uint16_t fnum;
+ struct smb2_hnd *ph;
+ DATA_BLOB output_buffer;
+};
+
+static void cli_smb2_get_reparse_point_fnum_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_smb2_get_reparse_point_fnum_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_smb2_set_reparse_point_fnum_state *state = NULL;
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_smb2_get_reparse_point_fnum_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ state->cli = cli;
+ state->fnum = fnum;
+
+ status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = smb2cli_ioctl_send(state, ev, state->cli->conn,
+ state->cli->timeout,
+ state->cli->smb2.session,
+ state->cli->smb2.tcon,
+ state->ph->fid_persistent, /* in_fid_persistent */
+ state->ph->fid_volatile, /* in_fid_volatile */
+ FSCTL_GET_REPARSE_POINT,
+ 0, /* in_max_input_length */
+ NULL,
+ 64*1024,
+ NULL,
+ SMB2_IOCTL_FLAG_IS_FSCTL);
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq,
+ cli_smb2_get_reparse_point_fnum_done,
+ req);
+
+ return req;
+}
+
+static void cli_smb2_get_reparse_point_fnum_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_get_reparse_point_fnum_state *state = tevent_req_data(
+ req, struct cli_smb2_get_reparse_point_fnum_state);
+ NTSTATUS status;
+
+ status = smb2cli_ioctl_recv(subreq, state,
+ NULL,
+ &state->output_buffer);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ state->cli->raw_status = status;
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_smb2_get_reparse_point_fnum_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *output)
+{
+ struct cli_smb2_get_reparse_point_fnum_state *state = tevent_req_data(
+ req, struct cli_smb2_get_reparse_point_fnum_state);
+
+ if (tevent_req_is_nterror(req, &state->cli->raw_status)) {
+ tevent_req_received(req);
+ return state->cli->raw_status;
+ }
+ *output = data_blob_dup_talloc(mem_ctx, state->output_buffer);
+ if (output->data == NULL) {
+ tevent_req_received(req);
+ return NT_STATUS_NO_MEMORY;
+ }
+ tevent_req_received(req);
return NT_STATUS_OK;
}