1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-25 23:21:54 +03:00
samba-mirror/source3/libsmb/cli_smb2_fnum.c
Volker Lendecke 18011343d8 libsmb: Add cli_smb2_notify
We have to do the parsing manually. Looking at librpc/gen_ndr/ndr_notify.c we
have the following code snippet:

  size_FileName1_0 = strlen_m(r->FileName1);
  NDR_CHECK(ndr_pull_charset(ndr, NDR_SCALARS, &r->FileName1,
                             size_FileName1_0, sizeof(uint16_t),
                             CH_UTF16));

which means that we take strlen_m(r->FileName1) before we pull
it off the wire. Not sure how to fix this, but that is clearly
broken pidl output. Once that is fixed, we can convert this
to ndr_pull_struct.

Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
2017-07-25 21:36:12 +02:00

3912 lines
94 KiB
C

/*
Unix SMB/CIFS implementation.
smb2 lib
Copyright (C) Jeremy Allison 2013
Copyright (C) Volker Lendecke 2013
Copyright (C) Stefan Metzmacher 2013
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
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
This code is a thin wrapper around the existing
cli_smb2_XXXX() functions in libcli/smb/smb2cli_XXXXX.c,
but allows the handles to be mapped to uint16_t fnums,
which are easier for smbclient to use.
*/
#include "includes.h"
#include "client.h"
#include "async_smb.h"
#include "../libcli/smb/smbXcli_base.h"
#include "cli_smb2_fnum.h"
#include "trans2.h"
#include "clirap.h"
#include "../libcli/smb/smb2_create_blob.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;
uint64_t fid_volatile;
};
/*
* Handle mapping code.
*/
/***************************************************************
Allocate a new fnum between 1 and 0xFFFE from an smb2_hnd.
Ensures handle is owned by cli struct.
***************************************************************/
static NTSTATUS map_smb2_handle_to_fnum(struct cli_state *cli,
const struct smb2_hnd *ph, /* In */
uint16_t *pfnum) /* Out */
{
int ret;
struct idr_context *idp = cli->smb2.open_handles;
struct smb2_hnd *owned_h = talloc_memdup(cli,
ph,
sizeof(struct smb2_hnd));
if (owned_h == NULL) {
return NT_STATUS_NO_MEMORY;
}
if (idp == NULL) {
/* Lazy init */
cli->smb2.open_handles = idr_init(cli);
if (cli->smb2.open_handles == NULL) {
TALLOC_FREE(owned_h);
return NT_STATUS_NO_MEMORY;
}
idp = cli->smb2.open_handles;
}
ret = idr_get_new_above(idp, owned_h, 1, 0xFFFE);
if (ret == -1) {
TALLOC_FREE(owned_h);
return NT_STATUS_NO_MEMORY;
}
*pfnum = (uint16_t)ret;
return NT_STATUS_OK;
}
/***************************************************************
Return the smb2_hnd pointer associated with the given fnum.
***************************************************************/
static NTSTATUS map_fnum_to_smb2_handle(struct cli_state *cli,
uint16_t fnum, /* In */
struct smb2_hnd **pph) /* Out */
{
struct idr_context *idp = cli->smb2.open_handles;
if (idp == NULL) {
return NT_STATUS_INVALID_PARAMETER;
}
*pph = (struct smb2_hnd *)idr_find(idp, fnum);
if (*pph == NULL) {
return NT_STATUS_INVALID_HANDLE;
}
return NT_STATUS_OK;
}
/***************************************************************
Delete the fnum to smb2_hnd mapping. Zeros out handle on
successful return.
***************************************************************/
static NTSTATUS delete_smb2_handle_mapping(struct cli_state *cli,
struct smb2_hnd **pph, /* In */
uint16_t fnum) /* In */
{
struct idr_context *idp = cli->smb2.open_handles;
struct smb2_hnd *ph;
if (idp == NULL) {
return NT_STATUS_INVALID_PARAMETER;
}
ph = (struct smb2_hnd *)idr_find(idp, fnum);
if (ph != *pph) {
return NT_STATUS_INVALID_PARAMETER;
}
idr_remove(idp, fnum);
TALLOC_FREE(*pph);
return NT_STATUS_OK;
}
/***************************************************************
Oplock mapping code.
***************************************************************/
static uint8_t flags_to_smb2_oplock(uint32_t create_flags)
{
if (create_flags & REQUEST_BATCH_OPLOCK) {
return SMB2_OPLOCK_LEVEL_BATCH;
} else if (create_flags & REQUEST_OPLOCK) {
return SMB2_OPLOCK_LEVEL_EXCLUSIVE;
}
/* create_flags doesn't do a level2 request. */
return SMB2_OPLOCK_LEVEL_NONE;
}
/***************************************************************
Small wrapper that allows SMB2 create to return a uint16_t fnum.
***************************************************************/
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 desired_access,
uint32_t file_attributes,
uint32_t share_access,
uint32_t create_disposition,
uint32_t create_options,
uint16_t *pfid,
struct smb_create_returns *cr)
{
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_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) {
tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
return tevent_req_post(req, ev);
}
status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
if (tevent_req_nterror(req, status)) {
return tevent_req_post(req, ev);
}
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;
}
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;
status = smb2cli_close_recv(subreq);
if (tevent_req_nterror(req, status)) {
return;
}
/* 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);
}
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 = tevent_req_simple_recv_ntstatus(req);
state->cli->raw_status = status;
return status;
}
NTSTATUS cli_smb2_close_fnum(struct cli_state *cli, uint16_t fnum)
{
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_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) {
tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
return tevent_req_post(req, ev);
}
status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
if (tevent_req_nterror(req, status)) {
return tevent_req_post(req, ev);
}
/*
* 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;
}
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;
}
/***************************************************************
Small wrapper that allows SMB2 to create a directory
Synchronous only.
***************************************************************/
NTSTATUS cli_smb2_mkdir(struct cli_state *cli, const char *dname)
{
NTSTATUS status;
uint16_t fnum;
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;
}
status = cli_smb2_create_fnum(cli,
dname,
0, /* create_flags */
FILE_READ_ATTRIBUTES, /* desired_access */
FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
FILE_SHARE_READ|FILE_SHARE_WRITE, /* share_access */
FILE_CREATE, /* create_disposition */
FILE_DIRECTORY_FILE, /* create_options */
&fnum,
NULL);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
return cli_smb2_close_fnum(cli, fnum);
}
/***************************************************************
Small wrapper that allows SMB2 to delete a directory
Synchronous only.
***************************************************************/
NTSTATUS cli_smb2_rmdir(struct cli_state *cli, const char *dname)
{
NTSTATUS status;
uint16_t fnum;
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;
}
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, /* create_options */
&fnum,
NULL);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
return cli_smb2_close_fnum(cli, fnum);
}
/***************************************************************
Small wrapper that allows SMB2 to unlink a pathname.
Synchronous only.
***************************************************************/
NTSTATUS cli_smb2_unlink(struct cli_state *cli, const char *fname)
{
NTSTATUS status;
uint16_t fnum;
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;
}
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, /* create_options */
&fnum,
NULL);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
return cli_smb2_close_fnum(cli, fnum);
}
/***************************************************************
Utility function to parse a SMB2_FIND_ID_BOTH_DIRECTORY_INFO reply.
***************************************************************/
static NTSTATUS parse_finfo_id_both_directory_info(uint8_t *dir_data,
uint32_t dir_data_length,
struct file_info *finfo,
uint32_t *next_offset)
{
size_t namelen = 0;
size_t slen = 0;
size_t ret = 0;
if (dir_data_length < 4) {
return NT_STATUS_INFO_LENGTH_MISMATCH;
}
*next_offset = IVAL(dir_data, 0);
if (*next_offset > dir_data_length) {
return NT_STATUS_INFO_LENGTH_MISMATCH;
}
if (*next_offset != 0) {
/* Ensure we only read what in this record. */
dir_data_length = *next_offset;
}
if (dir_data_length < 105) {
return NT_STATUS_INFO_LENGTH_MISMATCH;
}
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);
finfo->size = IVAL2_TO_SMB_BIG_UINT(dir_data + 40, 0);
finfo->mode = CVAL(dir_data + 56, 0);
namelen = IVAL(dir_data + 60,0);
if (namelen > (dir_data_length - 104)) {
return NT_STATUS_INFO_LENGTH_MISMATCH;
}
slen = CVAL(dir_data + 68, 0);
if (slen > 24) {
return NT_STATUS_INFO_LENGTH_MISMATCH;
}
ret = pull_string_talloc(finfo,
dir_data,
FLAGS2_UNICODE_STRINGS,
&finfo->short_name,
dir_data + 70,
slen,
STR_UNICODE);
if (ret == (size_t)-1) {
/* Bad conversion. */
return NT_STATUS_INVALID_NETWORK_RESPONSE;
}
ret = pull_string_talloc(finfo,
dir_data,
FLAGS2_UNICODE_STRINGS,
&finfo->name,
dir_data + 104,
namelen,
STR_UNICODE);
if (ret == (size_t)-1) {
/* Bad conversion. */
return NT_STATUS_INVALID_NETWORK_RESPONSE;
}
return NT_STATUS_OK;
}
/*******************************************************************
Given a filename - get its directory name
********************************************************************/
static bool windows_parent_dirname(TALLOC_CTX *mem_ctx,
const char *dir,
char **parent,
const char **name)
{
char *p;
ptrdiff_t len;
p = strrchr_m(dir, '\\'); /* Find final '\\', if any */
if (p == NULL) {
if (!(*parent = talloc_strdup(mem_ctx, "\\"))) {
return false;
}
if (name) {
*name = dir;
}
return true;
}
len = p-dir;
if (!(*parent = (char *)talloc_memdup(mem_ctx, dir, len+1))) {
return false;
}
(*parent)[len] = '\0';
if (name) {
*name = p+1;
}
return true;
}
/***************************************************************
Wrapper that allows SMB2 to list a directory.
Synchronous only.
***************************************************************/
NTSTATUS cli_smb2_list(struct cli_state *cli,
const char *pathname,
uint16_t attribute,
NTSTATUS (*fn)(const char *,
struct file_info *,
const char *,
void *),
void *state)
{
NTSTATUS status;
uint16_t fnum = 0xffff;
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)) {
/*
* 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;
}
/* Get the directory name. */
if (!windows_parent_dirname(frame,
pathname,
&parent_dir,
&mask)) {
status = NT_STATUS_NO_MEMORY;
goto fail;
}
mask_has_wild = ms_has_wild(mask);
status = cli_smb2_create_fnum(cli,
parent_dir,
0, /* create_flags */
SEC_DIR_LIST|SEC_DIR_READ_ATTRIBUTE,/* desired_access */
FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
FILE_SHARE_READ|FILE_SHARE_WRITE, /* 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);
if (!NT_STATUS_IS_OK(status)) {
goto fail;
}
do {
uint8_t *dir_data = NULL;
uint32_t dir_data_length = 0;
uint32_t next_offset = 0;
subframe = talloc_stackframe();
status = smb2cli_query_directory(cli->conn,
cli->timeout,
cli->smb2.session,
cli->smb2.tcon,
SMB2_FIND_ID_BOTH_DIRECTORY_INFO,
0, /* flags */
0, /* file_index */
ph->fid_persistent,
ph->fid_volatile,
mask,
0xffff,
subframe,
&dir_data,
&dir_data_length);
if (!NT_STATUS_IS_OK(status)) {
if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
break;
}
goto fail;
}
do {
struct file_info *finfo = talloc_zero(subframe,
struct file_info);
if (finfo == NULL) {
status = NT_STATUS_NO_MEMORY;
goto fail;
}
status = parse_finfo_id_both_directory_info(dir_data,
dir_data_length,
finfo,
&next_offset);
if (!NT_STATUS_IS_OK(status)) {
goto fail;
}
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;
}
}
TALLOC_FREE(finfo);
/* Move to next entry. */
if (next_offset) {
dir_data += next_offset;
dir_data_length -= next_offset;
}
} while (next_offset != 0);
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;
}
/***************************************************************
Wrapper that allows SMB2 to query a path info (basic level).
Synchronous only.
***************************************************************/
NTSTATUS cli_smb2_qpathinfo_basic(struct cli_state *cli,
const char *name,
SMB_STRUCT_STAT *sbuf,
uint32_t *attributes)
{
NTSTATUS status;
struct smb_create_returns cr;
uint16_t fnum = 0xffff;
size_t namelen = strlen(name);
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;
}
/* SMB2 is pickier about pathnames. Ensure it doesn't
end in a '\' */
if (namelen > 0 && name[namelen-1] == '\\') {
char *modname = talloc_strdup(talloc_tos(), name);
modname[namelen-1] = '\0';
name = modname;
}
/* This is commonly used as a 'cd'. Try qpathinfo on
a directory handle first. */
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,
&cr);
if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_DIRECTORY)) {
/* Maybe a file ? */
status = cli_smb2_create_fnum(cli,
name,
0, /* create_flags */
FILE_READ_ATTRIBUTES, /* desired_access */
0, /* file attributes */
FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
FILE_OPEN, /* create_disposition */
0, /* create_options */
&fnum,
&cr);
}
if (!NT_STATUS_IS_OK(status)) {
return status;
}
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_size = cr.end_of_file;
*attributes = cr.file_attributes;
return status;
}
/***************************************************************
Helper function for pathname operations.
***************************************************************/
static NTSTATUS get_fnum_from_path(struct cli_state *cli,
const char *name,
uint32_t desired_access,
uint16_t *pfnum)
{
NTSTATUS status;
size_t namelen = strlen(name);
TALLOC_CTX *frame = talloc_stackframe();
/* SMB2 is pickier about pathnames. Ensure it doesn't
end in a '\' */
if (namelen > 0 && name[namelen-1] == '\\') {
char *modname = talloc_strdup(frame, name);
if (modname == NULL) {
status = NT_STATUS_NO_MEMORY;
goto fail;
}
modname[namelen-1] = '\0';
name = modname;
}
/* Try to open a file handle first. */
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 */
0, /* create_options */
pfnum,
NULL);
if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
status = cli_smb2_create_fnum(cli,
name,
0, /* create_flags */
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 */
pfnum,
NULL);
}
fail:
TALLOC_FREE(frame);
return status;
}
/***************************************************************
Wrapper that allows SMB2 to query a path info (ALTNAME level).
Synchronous only.
***************************************************************/
NTSTATUS cli_smb2_qpathinfo_alt_name(struct cli_state *cli,
const char *name,
fstring alt_name)
{
NTSTATUS status;
DATA_BLOB outbuf = data_blob_null;
uint16_t fnum = 0xffff;
struct smb2_hnd *ph = NULL;
uint32_t altnamelen = 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,
name,
FILE_READ_ATTRIBUTES,
&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;
}
/* getinfo on the handle with info_type SMB2_GETINFO_FILE (1),
level SMB_FILE_ALTERNATE_NAME_INFORMATION (1021) == SMB2 21 */
status = smb2cli_query_info(cli->conn,
cli->timeout,
cli->smb2.session,
cli->smb2.tcon,
1, /* in_info_type */
(SMB_FILE_ALTERNATE_NAME_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 fail;
}
/* Parse the reply. */
if (outbuf.length < 4) {
status = NT_STATUS_INVALID_NETWORK_RESPONSE;
goto fail;
}
altnamelen = IVAL(outbuf.data, 0);
if (altnamelen > outbuf.length - 4) {
status = NT_STATUS_INVALID_NETWORK_RESPONSE;
goto fail;
}
if (altnamelen > 0) {
size_t ret = 0;
char *short_name = NULL;
ret = pull_string_talloc(frame,
outbuf.data,
FLAGS2_UNICODE_STRINGS,
&short_name,
outbuf.data + 4,
altnamelen,
STR_UNICODE);
if (ret == (size_t)-1) {
/* Bad conversion. */
status = NT_STATUS_INVALID_NETWORK_RESPONSE;
goto fail;
}
fstrcpy(alt_name, short_name);
} else {
alt_name[0] = '\0';
}
status = NT_STATUS_OK;
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 fnum info (basic level).
Synchronous only.
***************************************************************/
NTSTATUS cli_smb2_qfileinfo_basic(struct cli_state *cli,
uint16_t fnum,
uint16_t *mode,
off_t *size,
struct timespec *create_time,
struct timespec *access_time,
struct timespec *write_time,
struct timespec *change_time,
SMB_INO_T *ino)
{
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 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 handle with info_type SMB2_GETINFO_FILE (1),
level 0x12 (SMB2_FILE_ALL_INFORMATION). */
status = smb2cli_query_info(cli->conn,
cli->timeout,
cli->smb2.session,
cli->smb2.tcon,
1, /* in_info_type */
(SMB_FILE_ALL_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 fail;
}
/* Parse the reply. */
if (outbuf.length < 0x60) {
status = NT_STATUS_INVALID_NETWORK_RESPONSE;
goto fail;
}
if (create_time) {
*create_time = interpret_long_date((const char *)outbuf.data + 0x0);
}
if (access_time) {
*access_time = interpret_long_date((const char *)outbuf.data + 0x8);
}
if (write_time) {
*write_time = interpret_long_date((const char *)outbuf.data + 0x10);
}
if (change_time) {
*change_time = interpret_long_date((const char *)outbuf.data + 0x18);
}
if (mode) {
uint32_t attr = IVAL(outbuf.data, 0x20);
*mode = (uint16_t)attr;
}
if (size) {
uint64_t file_size = BVAL(outbuf.data, 0x30);
*size = (off_t)file_size;
}
if (ino) {
uint64_t file_index = BVAL(outbuf.data, 0x40);
*ino = (SMB_INO_T)file_index;
}
fail:
cli->raw_status = status;
TALLOC_FREE(frame);
return status;
}
/***************************************************************
Wrapper that allows SMB2 to query an fnum.
Implement on top of cli_smb2_qfileinfo_basic().
Synchronous only.
***************************************************************/
NTSTATUS cli_smb2_getattrE(struct cli_state *cli,
uint16_t fnum,
uint16_t *attr,
off_t *size,
time_t *change_time,
time_t *access_time,
time_t *write_time)
{
struct timespec access_time_ts;
struct timespec write_time_ts;
struct timespec change_time_ts;
NTSTATUS status = cli_smb2_qfileinfo_basic(cli,
fnum,
attr,
size,
NULL,
&access_time_ts,
&write_time_ts,
&change_time_ts,
NULL);
cli->raw_status = status;
if (!NT_STATUS_IS_OK(status)) {
return status;
}
if (change_time) {
*change_time = change_time_ts.tv_sec;
}
if (access_time) {
*access_time = access_time_ts.tv_sec;
}
if (write_time) {
*write_time = write_time_ts.tv_sec;
}
return NT_STATUS_OK;
}
/***************************************************************
Wrapper that allows SMB2 to get pathname attributes.
Synchronous only.
***************************************************************/
NTSTATUS cli_smb2_getatr(struct cli_state *cli,
const char *name,
uint16_t *attr,
off_t *size,
time_t *write_time)
{
NTSTATUS status;
uint16_t fnum = 0xffff;
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 = get_fnum_from_path(cli,
name,
FILE_READ_ATTRIBUTES,
&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;
}
status = cli_smb2_getattrE(cli,
fnum,
attr,
size,
NULL,
NULL,
write_time);
if (!NT_STATUS_IS_OK(status)) {
goto 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 query a pathname info (basic level).
Implement on top of cli_smb2_qfileinfo_basic().
Synchronous only.
***************************************************************/
NTSTATUS cli_smb2_qpathinfo2(struct cli_state *cli,
const char *name,
struct timespec *create_time,
struct timespec *access_time,
struct timespec *write_time,
struct timespec *change_time,
off_t *size,
uint16_t *mode,
SMB_INO_T *ino)
{
NTSTATUS status;
struct smb2_hnd *ph = NULL;
uint16_t fnum = 0xffff;
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,
name,
FILE_READ_ATTRIBUTES,
&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;
}
status = cli_smb2_qfileinfo_basic(cli,
fnum,
mode,
size,
create_time,
access_time,
write_time,
change_time,
ino);
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 pathname streams.
Synchronous only.
***************************************************************/
NTSTATUS cli_smb2_qpathinfo_streams(struct cli_state *cli,
const char *name,
TALLOC_CTX *mem_ctx,
unsigned int *pnum_streams,
struct stream_struct **pstreams)
{
NTSTATUS status;
struct smb2_hnd *ph = NULL;
uint16_t fnum = 0xffff;
DATA_BLOB outbuf = data_blob_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 = get_fnum_from_path(cli,
name,
FILE_READ_ATTRIBUTES,
&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;
}
/* getinfo on the handle with info_type SMB2_GETINFO_FILE (1),
level 22 (SMB2_FILE_STREAM_INFORMATION). */
status = smb2cli_query_info(cli->conn,
cli->timeout,
cli->smb2.session,
cli->smb2.tcon,
1, /* in_info_type */
(SMB_FILE_STREAM_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 fail;
}
/* Parse the reply. */
if (!parse_streams_blob(mem_ctx,
outbuf.data,
outbuf.length,
pnum_streams,
pstreams)) {
status = NT_STATUS_INVALID_NETWORK_RESPONSE;
goto 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 set SMB_FILE_BASIC_INFORMATION on
a pathname.
Synchronous only.
***************************************************************/
NTSTATUS cli_smb2_setpathinfo(struct cli_state *cli,
const char *name,
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;
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,
name,
FILE_WRITE_ATTRIBUTES,
&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;
}
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.data = inbuf_store;
inbuf.length = sizeof(inbuf_store);
data_blob_clear(&inbuf);
/*
* 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);
}
/* Set all the other times to -1. */
SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL);
SBVAL(inbuf.data, 8, 0xFFFFFFFFFFFFFFFFLL);
SBVAL(inbuf.data, 24, 0xFFFFFFFFFFFFFFFFLL);
return cli_smb2_setpathinfo(cli,
name,
1, /* in_info_type */
/* in_file_info_class */
SMB_FILE_BASIC_INFORMATION - 1000,
&inbuf);
}
/***************************************************************
Wrapper that allows SMB2 to set file handle times.
Synchronous only.
***************************************************************/
NTSTATUS cli_smb2_setattrE(struct cli_state *cli,
uint16_t fnum,
time_t change_time,
time_t access_time,
time_t write_time)
{
NTSTATUS status;
struct smb2_hnd *ph = NULL;
uint8_t inbuf_store[40];
DATA_BLOB inbuf = data_blob_null;
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;
}
status = map_fnum_to_smb2_handle(cli,
fnum,
&ph);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
/* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
inbuf.data = inbuf_store;
inbuf.length = sizeof(inbuf_store);
data_blob_clear(&inbuf);
SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL);
if (change_time != 0) {
put_long_date((char *)inbuf.data + 24, change_time);
}
if (access_time != 0) {
put_long_date((char *)inbuf.data + 8, access_time);
}
if (write_time != 0) {
put_long_date((char *)inbuf.data + 16, write_time);
}
cli->raw_status = smb2cli_set_info(cli->conn,
cli->timeout,
cli->smb2.session,
cli->smb2.tcon,
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);
return cli->raw_status;
}
/***************************************************************
Wrapper that allows SMB2 to query disk attributes (size).
Synchronous only.
***************************************************************/
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;
DATA_BLOB outbuf = data_blob_null;
struct smb2_hnd *ph = NULL;
uint32_t sectors_per_unit = 0;
uint32_t bytes_per_sector = 0;
uint64_t total_size = 0;
uint64_t size_free = 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;
}
/* 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 */
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);
if (!NT_STATUS_IS_OK(status)) {
goto fail;
}
/* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
level 3 (SMB_FS_SIZE_INFORMATION). */
status = smb2cli_query_info(cli->conn,
cli->timeout,
cli->smb2.session,
cli->smb2.tcon,
2, /* in_info_type */
3, /* 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;
}
/* Parse the reply. */
if (outbuf.length != 24) {
status = NT_STATUS_INVALID_NETWORK_RESPONSE;
goto fail;
}
total_size = BVAL(outbuf.data, 0);
size_free = BVAL(outbuf.data, 8);
sectors_per_unit = IVAL(outbuf.data, 16);
bytes_per_sector = IVAL(outbuf.data, 20);
if (bsize) {
*bsize = (uint64_t)sectors_per_unit * (uint64_t)bytes_per_sector;
}
if (total) {
*total = total_size;
}
if (avail) {
*avail = size_free;
}
status = NT_STATUS_OK;
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 file system attributes.
Synchronous only.
***************************************************************/
NTSTATUS cli_smb2_get_fs_attr_info(struct cli_state *cli, uint32_t *fs_attr)
{
NTSTATUS status;
uint16_t fnum = 0xffff;
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 fail;
}
if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
status = NT_STATUS_INVALID_PARAMETER;
goto fail;
}
/* 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);
if (!NT_STATUS_IS_OK(status)) {
goto fail;
}
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;
}
if (outbuf.length < 12) {
status = NT_STATUS_INVALID_NETWORK_RESPONSE;
goto fail;
}
*fs_attr = IVAL(outbuf.data, 0);
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);
modname[namelen-1] = '\0';
fname_dst = modname;
}
if (!push_ucs2_talloc(frame,
&converted_str,
fname_dst,
&converted_size_bytes)) {
status = NT_STATUS_INVALID_PARAMETER;
goto fail;
}
/* W2K8 insists the dest name is not null
terminated. Remove the last 2 zero bytes
and reduce the name length. */
if (converted_size_bytes < 2) {
status = NT_STATUS_INVALID_PARAMETER;
goto fail;
}
converted_size_bytes -= 2;
inbuf = data_blob_talloc_zero(frame,
20 + converted_size_bytes);
if (inbuf.data == NULL) {
status = NT_STATUS_NO_MEMORY;
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);
/* setinfo on the returned handle with info_type SMB2_GETINFO_FILE (1),
level SMB2_FILE_RENAME_INFORMATION (SMB_FILE_RENAME_INFORMATION - 1000) */
status = smb2cli_set_info(cli->conn,
cli->timeout,
cli->smb2.session,
cli->smb2.tcon,
1, /* in_info_type */
SMB_FILE_RENAME_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);
}
cli->raw_status = status;
TALLOC_FREE(frame);
return status;
}
/***************************************************************
Wrapper that allows SMB2 to set an EA on a fnum.
Synchronous only.
***************************************************************/
NTSTATUS cli_smb2_set_ea_fnum(struct cli_state *cli,
uint16_t fnum,
const char *ea_name,
const char *ea_val,
size_t ea_len)
{
NTSTATUS status;
DATA_BLOB inbuf = data_blob_null;
size_t bloblen = 0;
char *ea_name_ascii = NULL;
size_t namelen = 0;
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;
}
/* Marshall the SMB2 EA data. */
if (ea_len > 0xFFFF) {
status = NT_STATUS_INVALID_PARAMETER;
goto fail;
}
if (!push_ascii_talloc(frame,
&ea_name_ascii,
ea_name,
&namelen)) {
status = NT_STATUS_INVALID_PARAMETER;
goto fail;
}
if (namelen < 2 || namelen > 0xFF) {
status = NT_STATUS_INVALID_PARAMETER;
goto fail;
}
bloblen = 8 + ea_len + namelen;
/* Round up to a 4 byte boundary. */
bloblen = ((bloblen + 3)&~3);
inbuf = data_blob_talloc_zero(frame, bloblen);
if (inbuf.data == NULL) {
status = NT_STATUS_NO_MEMORY;
goto fail;
}
/* namelen doesn't include the NULL byte. */
SCVAL(inbuf.data, 5, namelen - 1);
SSVAL(inbuf.data, 6, ea_len);
memcpy(inbuf.data + 8, ea_name_ascii, namelen);
memcpy(inbuf.data + 8 + namelen, ea_val, ea_len);
/* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */
status = smb2cli_set_info(cli->conn,
cli->timeout,
cli->smb2.session,
cli->smb2.tcon,
1, /* in_info_type */
SMB_FILE_FULL_EA_INFORMATION - 1000, /* in_file_info_class */
&inbuf, /* in_input_buffer */
0, /* in_additional_info */
ph->fid_persistent,
ph->fid_volatile);
fail:
cli->raw_status = status;
TALLOC_FREE(frame);
return status;
}
/***************************************************************
Wrapper that allows SMB2 to set an EA on a pathname.
Synchronous only.
***************************************************************/
NTSTATUS cli_smb2_set_ea_path(struct cli_state *cli,
const char *name,
const char *ea_name,
const char *ea_val,
size_t ea_len)
{
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
*/
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,
name,
FILE_WRITE_EA,
&fnum);
if (!NT_STATUS_IS_OK(status)) {
goto fail;
}
status = cli_set_ea_fnum(cli,
fnum,
ea_name,
ea_val,
ea_len);
if (!NT_STATUS_IS_OK(status)) {
goto fail;
}
fail:
if (fnum != 0xffff) {
cli_smb2_close_fnum(cli, fnum);
}
cli->raw_status = status;
return status;
}
/***************************************************************
Wrapper that allows SMB2 to get an EA list on a pathname.
Synchronous only.
***************************************************************/
NTSTATUS cli_smb2_get_ea_list_path(struct cli_state *cli,
const char *name,
TALLOC_CTX *ctx,
size_t *pnum_eas,
struct ea_struct **pea_array)
{
NTSTATUS status;
uint16_t fnum = 0xffff;
DATA_BLOB outbuf = data_blob_null;
struct smb2_hnd *ph = NULL;
struct ea_list *ea_list = NULL;
struct ea_list *eal = NULL;
size_t ea_count = 0;
TALLOC_CTX *frame = talloc_stackframe();
*pnum_eas = 0;
*pea_array = NULL;
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,
name,
FILE_READ_EA,
&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;
}
/* getinfo on the handle with info_type SMB2_GETINFO_FILE (1),
level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */
status = smb2cli_query_info(cli->conn,
cli->timeout,
cli->smb2.session,
cli->smb2.tcon,
1, /* in_info_type */
SMB_FILE_FULL_EA_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 fail;
}
/* Parse the reply. */
ea_list = read_nttrans_ea_list(ctx,
(const char *)outbuf.data,
outbuf.length);
if (ea_list == NULL) {
status = NT_STATUS_INVALID_NETWORK_RESPONSE;
goto fail;
}
/* Convert to an array. */
for (eal = ea_list; eal; eal = eal->next) {
ea_count++;
}
if (ea_count) {
*pea_array = talloc_array(ctx, struct ea_struct, ea_count);
if (*pea_array == NULL) {
status = NT_STATUS_NO_MEMORY;
goto fail;
}
ea_count = 0;
for (eal = ea_list; eal; eal = eal->next) {
(*pea_array)[ea_count++] = eal->ea;
}
*pnum_eas = ea_count;
}
fail:
if (fnum != 0xffff) {
cli_smb2_close_fnum(cli, fnum);
}
cli->raw_status = status;
TALLOC_FREE(frame);
return status;
}
/***************************************************************
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;
};
static void cli_smb2_read_done(struct tevent_req *subreq);
struct tevent_req *cli_smb2_read_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct cli_state *cli,
uint16_t fnum,
off_t offset,
size_t size)
{
NTSTATUS status;
struct tevent_req *req, *subreq;
struct cli_smb2_read_state *state;
req = tevent_req_create(mem_ctx, &state, struct cli_smb2_read_state);
if (req == NULL) {
return NULL;
}
state->ev = ev;
state->cli = cli;
state->start_offset = (uint64_t)offset;
state->size = (uint32_t)size;
state->received = 0;
state->buf = NULL;
status = map_fnum_to_smb2_handle(cli,
fnum,
&state->ph);
if (tevent_req_nterror(req, status)) {
return tevent_req_post(req, ev);
}
subreq = smb2cli_read_send(state,
state->ev,
state->cli->conn,
state->cli->timeout,
state->cli->smb2.session,
state->cli->smb2.tcon,
state->size,
state->start_offset,
state->ph->fid_persistent,
state->ph->fid_volatile,
0, /* minimum_count */
0); /* remaining_bytes */
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
}
tevent_req_set_callback(subreq, cli_smb2_read_done, req);
return req;
}
static void cli_smb2_read_done(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
struct cli_smb2_read_state *state = tevent_req_data(
req, struct cli_smb2_read_state);
NTSTATUS status;
status = smb2cli_read_recv(subreq, state,
&state->buf, &state->received);
if (tevent_req_nterror(req, status)) {
return;
}
if (state->received > state->size) {
tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
return;
}
tevent_req_done(req);
}
NTSTATUS cli_smb2_read_recv(struct tevent_req *req,
ssize_t *received,
uint8_t **rcvbuf)
{
NTSTATUS status;
struct cli_smb2_read_state *state = tevent_req_data(
req, struct cli_smb2_read_state);
if (tevent_req_is_nterror(req, &status)) {
state->cli->raw_status = status;
return status;
}
/*
* As in cli_read_andx_recv() rcvbuf is talloced from the request, so
* better make sure that you copy it away before you talloc_free(req).
* "rcvbuf" is NOT a talloc_ctx of its own, so do not talloc_move it!
*/
*received = (ssize_t)state->received;
*rcvbuf = state->buf;
state->cli->raw_status = NT_STATUS_OK;
return NT_STATUS_OK;
}
struct cli_smb2_write_state {
struct tevent_context *ev;
struct cli_state *cli;
struct smb2_hnd *ph;
uint32_t flags;
const uint8_t *buf;
uint64_t offset;
uint32_t size;
uint32_t written;
};
static void cli_smb2_write_written(struct tevent_req *req);
struct tevent_req *cli_smb2_write_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct cli_state *cli,
uint16_t fnum,
uint16_t mode,
const uint8_t *buf,
off_t offset,
size_t size)
{
NTSTATUS status;
struct tevent_req *req, *subreq = NULL;
struct cli_smb2_write_state *state = NULL;
req = tevent_req_create(mem_ctx, &state, struct cli_smb2_write_state);
if (req == NULL) {
return NULL;
}
state->ev = ev;
state->cli = cli;
/* Both SMB1 and SMB2 use 1 in the following meaning write-through. */
state->flags = (uint32_t)mode;
state->buf = buf;
state->offset = (uint64_t)offset;
state->size = (uint32_t)size;
state->written = 0;
status = map_fnum_to_smb2_handle(cli,
fnum,
&state->ph);
if (tevent_req_nterror(req, status)) {
return tevent_req_post(req, ev);
}
subreq = smb2cli_write_send(state,
state->ev,
state->cli->conn,
state->cli->timeout,
state->cli->smb2.session,
state->cli->smb2.tcon,
state->size,
state->offset,
state->ph->fid_persistent,
state->ph->fid_volatile,
0, /* remaining_bytes */
state->flags, /* flags */
state->buf);
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
}
tevent_req_set_callback(subreq, cli_smb2_write_written, req);
return req;
}
static void cli_smb2_write_written(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
struct cli_smb2_write_state *state = tevent_req_data(
req, struct cli_smb2_write_state);
NTSTATUS status;
uint32_t written;
status = smb2cli_write_recv(subreq, &written);
TALLOC_FREE(subreq);
if (tevent_req_nterror(req, status)) {
return;
}
state->written = written;
tevent_req_done(req);
}
NTSTATUS cli_smb2_write_recv(struct tevent_req *req,
size_t *pwritten)
{
struct cli_smb2_write_state *state = tevent_req_data(
req, struct cli_smb2_write_state);
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;
}
/***************************************************************
Wrapper that allows SMB2 async write using an fnum.
This is mostly cut-and-paste from Volker's code inside
source3/libsmb/clireadwrite.c, adapted for SMB2.
Done this way so I can reuse all the logic inside cli_push()
for free :-).
***************************************************************/
struct cli_smb2_writeall_state {
struct tevent_context *ev;
struct cli_state *cli;
struct smb2_hnd *ph;
uint32_t flags;
const uint8_t *buf;
uint64_t offset;
uint32_t size;
uint32_t written;
};
static void cli_smb2_writeall_written(struct tevent_req *req);
struct tevent_req *cli_smb2_writeall_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct cli_state *cli,
uint16_t fnum,
uint16_t mode,
const uint8_t *buf,
off_t offset,
size_t size)
{
NTSTATUS status;
struct tevent_req *req, *subreq = NULL;
struct cli_smb2_writeall_state *state = NULL;
uint32_t to_write;
uint32_t max_size;
bool ok;
req = tevent_req_create(mem_ctx, &state, struct cli_smb2_writeall_state);
if (req == NULL) {
return NULL;
}
state->ev = ev;
state->cli = cli;
/* Both SMB1 and SMB2 use 1 in the following meaning write-through. */
state->flags = (uint32_t)mode;
state->buf = buf;
state->offset = (uint64_t)offset;
state->size = (uint32_t)size;
state->written = 0;
status = map_fnum_to_smb2_handle(cli,
fnum,
&state->ph);
if (tevent_req_nterror(req, status)) {
return tevent_req_post(req, ev);
}
to_write = state->size;
max_size = smb2cli_conn_max_write_size(state->cli->conn);
to_write = MIN(max_size, to_write);
ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
if (ok) {
to_write = MIN(max_size, to_write);
}
subreq = smb2cli_write_send(state,
state->ev,
state->cli->conn,
state->cli->timeout,
state->cli->smb2.session,
state->cli->smb2.tcon,
to_write,
state->offset,
state->ph->fid_persistent,
state->ph->fid_volatile,
0, /* remaining_bytes */
state->flags, /* flags */
state->buf + state->written);
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
}
tevent_req_set_callback(subreq, cli_smb2_writeall_written, req);
return req;
}
static void cli_smb2_writeall_written(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
struct cli_smb2_writeall_state *state = tevent_req_data(
req, struct cli_smb2_writeall_state);
NTSTATUS status;
uint32_t written, to_write;
uint32_t max_size;
bool ok;
status = smb2cli_write_recv(subreq, &written);
TALLOC_FREE(subreq);
if (tevent_req_nterror(req, status)) {
return;
}
state->written += written;
if (state->written > state->size) {
tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
return;
}
to_write = state->size - state->written;
if (to_write == 0) {
tevent_req_done(req);
return;
}
max_size = smb2cli_conn_max_write_size(state->cli->conn);
to_write = MIN(max_size, to_write);
ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
if (ok) {
to_write = MIN(max_size, to_write);
}
subreq = smb2cli_write_send(state,
state->ev,
state->cli->conn,
state->cli->timeout,
state->cli->smb2.session,
state->cli->smb2.tcon,
to_write,
state->offset + state->written,
state->ph->fid_persistent,
state->ph->fid_volatile,
0, /* remaining_bytes */
state->flags, /* flags */
state->buf + state->written);
if (tevent_req_nomem(subreq, req)) {
return;
}
tevent_req_set_callback(subreq, cli_smb2_writeall_written, req);
}
NTSTATUS cli_smb2_writeall_recv(struct tevent_req *req,
size_t *pwritten)
{
struct cli_smb2_writeall_state *state = tevent_req_data(
req, struct cli_smb2_writeall_state);
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);
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;
}