mirror of
https://github.com/samba-team/samba.git
synced 2024-12-24 21:34:56 +03:00
6123bd25ad
Found by covscan. Signed-off-by: Andreas Schneider <asn@samba.org> Reviewed-by: Andrew Bartlett <abartlet@samba.org>
5267 lines
129 KiB
C
5267 lines
129 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"
|
|
#include "librpc/gen_ndr/ndr_quota.h"
|
|
#include "lib/util/string_wrappers.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 smb2_create_blobs in_cblobs;
|
|
struct smb2_create_blobs out_cblobs;
|
|
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 impersonation_level,
|
|
uint32_t desired_access,
|
|
uint32_t file_attributes,
|
|
uint32_t share_access,
|
|
uint32_t create_disposition,
|
|
uint32_t create_options,
|
|
const struct smb2_create_blobs *in_cblobs)
|
|
{
|
|
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;
|
|
NTSTATUS status;
|
|
|
|
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;
|
|
|
|
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);
|
|
|
|
status = smb2_create_blob_add(
|
|
state,
|
|
&state->in_cblobs,
|
|
SMB2_CREATE_TAG_TWRP,
|
|
twrp_blob);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
tevent_req_nterror(req, status);
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
}
|
|
|
|
if (in_cblobs != NULL) {
|
|
uint32_t i;
|
|
for (i=0; i<in_cblobs->num_blobs; i++) {
|
|
struct smb2_create_blob *b = &in_cblobs->blobs[i];
|
|
status = smb2_create_blob_add(
|
|
state, &state->in_cblobs, b->tag, b->data);
|
|
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),
|
|
impersonation_level,
|
|
desired_access,
|
|
file_attributes,
|
|
share_access,
|
|
create_disposition,
|
|
create_options,
|
|
&state->in_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,
|
|
state,
|
|
&state->out_cblobs);
|
|
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,
|
|
TALLOC_CTX *mem_ctx,
|
|
struct smb2_create_blobs *out_cblobs)
|
|
{
|
|
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;
|
|
}
|
|
if (out_cblobs != NULL) {
|
|
*out_cblobs = (struct smb2_create_blobs) {
|
|
.num_blobs = state->out_cblobs.num_blobs,
|
|
.blobs = talloc_move(
|
|
mem_ctx, &state->out_cblobs.blobs),
|
|
};
|
|
}
|
|
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 impersonation_level,
|
|
uint32_t desired_access,
|
|
uint32_t file_attributes,
|
|
uint32_t share_access,
|
|
uint32_t create_disposition,
|
|
uint32_t create_options,
|
|
const struct smb2_create_blobs *in_cblobs,
|
|
uint16_t *pfid,
|
|
struct smb_create_returns *cr,
|
|
TALLOC_CTX *mem_ctx,
|
|
struct smb2_create_blobs *out_cblobs)
|
|
{
|
|
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,
|
|
impersonation_level,
|
|
desired_access,
|
|
file_attributes,
|
|
share_access,
|
|
create_disposition,
|
|
create_options,
|
|
in_cblobs);
|
|
if (req == NULL) {
|
|
goto fail;
|
|
}
|
|
if (!tevent_req_poll_ntstatus(req, ev, &status)) {
|
|
goto fail;
|
|
}
|
|
status = cli_smb2_create_fnum_recv(req, pfid, cr, mem_ctx, out_cblobs);
|
|
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 = 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)
|
|
{
|
|
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_set_info_fnum_state {
|
|
uint8_t dummy;
|
|
};
|
|
|
|
static void cli_smb2_set_info_fnum_done(struct tevent_req *subreq);
|
|
|
|
struct tevent_req *cli_smb2_set_info_fnum_send(
|
|
TALLOC_CTX *mem_ctx,
|
|
struct tevent_context *ev,
|
|
struct cli_state *cli,
|
|
uint16_t fnum,
|
|
uint8_t in_info_type,
|
|
uint8_t in_info_class,
|
|
const DATA_BLOB *in_input_buffer,
|
|
uint32_t in_additional_info)
|
|
{
|
|
struct tevent_req *req = NULL, *subreq = NULL;
|
|
struct cli_smb2_set_info_fnum_state *state = NULL;
|
|
struct smb2_hnd *ph = NULL;
|
|
NTSTATUS status;
|
|
|
|
req = tevent_req_create(
|
|
mem_ctx, &state, struct cli_smb2_set_info_fnum_state);
|
|
if (req == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
status = map_fnum_to_smb2_handle(cli, fnum, &ph);
|
|
if (tevent_req_nterror(req, status)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
|
|
subreq = smb2cli_set_info_send(
|
|
state,
|
|
ev,
|
|
cli->conn,
|
|
cli->timeout,
|
|
cli->smb2.session,
|
|
cli->smb2.tcon,
|
|
in_info_type,
|
|
in_info_class,
|
|
in_input_buffer,
|
|
in_additional_info,
|
|
ph->fid_persistent,
|
|
ph->fid_volatile);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
tevent_req_set_callback(subreq, cli_smb2_set_info_fnum_done, req);
|
|
return req;
|
|
}
|
|
|
|
static void cli_smb2_set_info_fnum_done(struct tevent_req *subreq)
|
|
{
|
|
NTSTATUS status = smb2cli_set_info_recv(subreq);
|
|
tevent_req_simple_finish_ntstatus(subreq, status);
|
|
}
|
|
|
|
NTSTATUS cli_smb2_set_info_fnum_recv(struct tevent_req *req)
|
|
{
|
|
return tevent_req_simple_recv_ntstatus(req);
|
|
}
|
|
|
|
NTSTATUS cli_smb2_set_info_fnum(
|
|
struct cli_state *cli,
|
|
uint16_t fnum,
|
|
uint8_t in_info_type,
|
|
uint8_t in_info_class,
|
|
const DATA_BLOB *in_input_buffer,
|
|
uint32_t in_additional_info)
|
|
{
|
|
TALLOC_CTX *frame = talloc_stackframe();
|
|
struct tevent_context *ev = NULL;
|
|
struct tevent_req *req = NULL;
|
|
NTSTATUS status = NT_STATUS_NO_MEMORY;
|
|
bool ok;
|
|
|
|
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_set_info_fnum_send(
|
|
frame,
|
|
ev,
|
|
cli,
|
|
fnum,
|
|
in_info_type,
|
|
in_info_class,
|
|
in_input_buffer,
|
|
in_additional_info);
|
|
if (req == NULL) {
|
|
goto fail;
|
|
}
|
|
ok = tevent_req_poll_ntstatus(req, ev, &status);
|
|
if (!ok) {
|
|
goto fail;
|
|
}
|
|
status = cli_smb2_set_info_fnum_recv(req);
|
|
fail:
|
|
TALLOC_FREE(frame);
|
|
return status;
|
|
}
|
|
|
|
struct cli_smb2_delete_on_close_state {
|
|
struct cli_state *cli;
|
|
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;
|
|
|
|
req = tevent_req_create(mem_ctx, &state,
|
|
struct cli_smb2_delete_on_close_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);
|
|
}
|
|
|
|
/*
|
|
* 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 = cli_smb2_set_info_fnum_send(
|
|
state,
|
|
ev,
|
|
cli,
|
|
fnum,
|
|
in_info_type,
|
|
in_file_info_class,
|
|
&state->inbuf,
|
|
0);
|
|
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 = cli_smb2_set_info_fnum_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;
|
|
}
|
|
|
|
struct cli_smb2_mkdir_state {
|
|
struct tevent_context *ev;
|
|
struct cli_state *cli;
|
|
};
|
|
|
|
static void cli_smb2_mkdir_opened(struct tevent_req *subreq);
|
|
static void cli_smb2_mkdir_closed(struct tevent_req *subreq);
|
|
|
|
struct tevent_req *cli_smb2_mkdir_send(
|
|
TALLOC_CTX *mem_ctx,
|
|
struct tevent_context *ev,
|
|
struct cli_state *cli,
|
|
const char *dname)
|
|
{
|
|
struct tevent_req *req = NULL, *subreq = NULL;
|
|
struct cli_smb2_mkdir_state *state = NULL;
|
|
|
|
req = tevent_req_create(
|
|
mem_ctx, &state, struct cli_smb2_mkdir_state);
|
|
if (req == NULL) {
|
|
return NULL;
|
|
}
|
|
state->ev = ev;
|
|
state->cli = cli;
|
|
|
|
/* Ensure this is a directory. */
|
|
subreq = cli_smb2_create_fnum_send(
|
|
state, /* mem_ctx */
|
|
ev, /* ev */
|
|
cli, /* cli */
|
|
dname, /* fname */
|
|
0, /* create_flags */
|
|
SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
|
|
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 */
|
|
NULL); /* in_cblobs */
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
tevent_req_set_callback(subreq, cli_smb2_mkdir_opened, req);
|
|
return req;
|
|
}
|
|
|
|
static void cli_smb2_mkdir_opened(struct tevent_req *subreq)
|
|
{
|
|
struct tevent_req *req = tevent_req_callback_data(
|
|
subreq, struct tevent_req);
|
|
struct cli_smb2_mkdir_state *state = tevent_req_data(
|
|
req, struct cli_smb2_mkdir_state);
|
|
NTSTATUS status;
|
|
uint16_t fnum = 0xffff;
|
|
|
|
status = cli_smb2_create_fnum_recv(subreq, &fnum, NULL, NULL, NULL);
|
|
TALLOC_FREE(subreq);
|
|
if (tevent_req_nterror(req, status)) {
|
|
return;
|
|
}
|
|
|
|
subreq = cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return;
|
|
}
|
|
tevent_req_set_callback(subreq, cli_smb2_mkdir_closed, req);
|
|
}
|
|
|
|
static void cli_smb2_mkdir_closed(struct tevent_req *subreq)
|
|
{
|
|
NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
|
|
tevent_req_simple_finish_ntstatus(subreq, status);
|
|
}
|
|
|
|
NTSTATUS cli_smb2_mkdir_recv(struct tevent_req *req)
|
|
{
|
|
return tevent_req_simple_recv_ntstatus(req);
|
|
}
|
|
|
|
struct cli_smb2_rmdir_state {
|
|
struct tevent_context *ev;
|
|
struct cli_state *cli;
|
|
const char *dname;
|
|
const struct smb2_create_blobs *in_cblobs;
|
|
uint16_t fnum;
|
|
NTSTATUS status;
|
|
};
|
|
|
|
static void cli_smb2_rmdir_opened1(struct tevent_req *subreq);
|
|
static void cli_smb2_rmdir_opened2(struct tevent_req *subreq);
|
|
static void cli_smb2_rmdir_disp_set(struct tevent_req *subreq);
|
|
static void cli_smb2_rmdir_closed(struct tevent_req *subreq);
|
|
|
|
struct tevent_req *cli_smb2_rmdir_send(
|
|
TALLOC_CTX *mem_ctx,
|
|
struct tevent_context *ev,
|
|
struct cli_state *cli,
|
|
const char *dname,
|
|
const struct smb2_create_blobs *in_cblobs)
|
|
{
|
|
struct tevent_req *req = NULL, *subreq = NULL;
|
|
struct cli_smb2_rmdir_state *state = NULL;
|
|
|
|
req = tevent_req_create(mem_ctx, &state, struct cli_smb2_rmdir_state);
|
|
if (req == NULL) {
|
|
return NULL;
|
|
}
|
|
state->ev = ev;
|
|
state->cli = cli;
|
|
state->dname = dname;
|
|
state->in_cblobs = in_cblobs;
|
|
|
|
if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
|
|
tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
|
|
subreq = cli_smb2_create_fnum_send(
|
|
state,
|
|
state->ev,
|
|
state->cli,
|
|
state->dname,
|
|
0, /* create_flags */
|
|
SMB2_IMPERSONATION_IMPERSONATION,
|
|
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, /* create_options */
|
|
state->in_cblobs); /* in_cblobs */
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
tevent_req_set_callback(subreq, cli_smb2_rmdir_opened1, req);
|
|
return req;
|
|
}
|
|
|
|
static void cli_smb2_rmdir_opened1(struct tevent_req *subreq)
|
|
{
|
|
struct tevent_req *req = tevent_req_callback_data(
|
|
subreq, struct tevent_req);
|
|
struct cli_smb2_rmdir_state *state = tevent_req_data(
|
|
req, struct cli_smb2_rmdir_state);
|
|
NTSTATUS status;
|
|
|
|
status = cli_smb2_create_fnum_recv(
|
|
subreq, &state->fnum, NULL, NULL, NULL);
|
|
TALLOC_FREE(subreq);
|
|
|
|
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.
|
|
*/
|
|
subreq = cli_smb2_create_fnum_send(
|
|
state,
|
|
state->ev,
|
|
state->cli,
|
|
state->dname,
|
|
0, /* create_flags */
|
|
SMB2_IMPERSONATION_IMPERSONATION,
|
|
DELETE_ACCESS, /* desired_access */
|
|
FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
|
|
FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
|
|
FILE_OPEN, /* create_disposition */
|
|
FILE_DIRECTORY_FILE|
|
|
FILE_DELETE_ON_CLOSE|
|
|
FILE_OPEN_REPARSE_POINT, /* create_options */
|
|
state->in_cblobs); /* in_cblobs */
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return;
|
|
}
|
|
tevent_req_set_callback(subreq, cli_smb2_rmdir_opened2, req);
|
|
return;
|
|
}
|
|
|
|
if (tevent_req_nterror(req, status)) {
|
|
return;
|
|
}
|
|
|
|
subreq = cli_smb2_delete_on_close_send(
|
|
state, state->ev, state->cli, state->fnum, true);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return;
|
|
}
|
|
tevent_req_set_callback(subreq, cli_smb2_rmdir_disp_set, req);
|
|
}
|
|
|
|
static void cli_smb2_rmdir_opened2(struct tevent_req *subreq)
|
|
{
|
|
struct tevent_req *req = tevent_req_callback_data(
|
|
subreq, struct tevent_req);
|
|
struct cli_smb2_rmdir_state *state = tevent_req_data(
|
|
req, struct cli_smb2_rmdir_state);
|
|
NTSTATUS status;
|
|
|
|
status = cli_smb2_create_fnum_recv(
|
|
subreq, &state->fnum, NULL, NULL, NULL);
|
|
TALLOC_FREE(subreq);
|
|
if (tevent_req_nterror(req, status)) {
|
|
return;
|
|
}
|
|
|
|
subreq = cli_smb2_delete_on_close_send(
|
|
state, state->ev, state->cli, state->fnum, true);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return;
|
|
}
|
|
tevent_req_set_callback(subreq, cli_smb2_rmdir_disp_set, req);
|
|
}
|
|
|
|
static void cli_smb2_rmdir_disp_set(struct tevent_req *subreq)
|
|
{
|
|
struct tevent_req *req = tevent_req_callback_data(
|
|
subreq, struct tevent_req);
|
|
struct cli_smb2_rmdir_state *state = tevent_req_data(
|
|
req, struct cli_smb2_rmdir_state);
|
|
|
|
state->status = cli_smb2_delete_on_close_recv(subreq);
|
|
TALLOC_FREE(subreq);
|
|
|
|
/*
|
|
* Close the fd even if the set_disp failed
|
|
*/
|
|
|
|
subreq = cli_smb2_close_fnum_send(
|
|
state, state->ev, state->cli, state->fnum);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return;
|
|
}
|
|
tevent_req_set_callback(subreq, cli_smb2_rmdir_closed, req);
|
|
}
|
|
|
|
static void cli_smb2_rmdir_closed(struct tevent_req *subreq)
|
|
{
|
|
NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
|
|
tevent_req_simple_finish_ntstatus(subreq, status);
|
|
}
|
|
|
|
NTSTATUS cli_smb2_rmdir_recv(struct tevent_req *req)
|
|
{
|
|
struct cli_smb2_rmdir_state *state = tevent_req_data(
|
|
req, struct cli_smb2_rmdir_state);
|
|
NTSTATUS status;
|
|
|
|
if (tevent_req_is_nterror(req, &status)) {
|
|
return status;
|
|
}
|
|
return state->status;
|
|
}
|
|
|
|
/***************************************************************
|
|
Small wrapper that allows SMB2 to unlink a pathname.
|
|
***************************************************************/
|
|
|
|
struct cli_smb2_unlink_state {
|
|
struct tevent_context *ev;
|
|
struct cli_state *cli;
|
|
const char *fname;
|
|
const struct smb2_create_blobs *in_cblobs;
|
|
};
|
|
|
|
static void cli_smb2_unlink_opened1(struct tevent_req *subreq);
|
|
static void cli_smb2_unlink_opened2(struct tevent_req *subreq);
|
|
static void cli_smb2_unlink_closed(struct tevent_req *subreq);
|
|
|
|
struct tevent_req *cli_smb2_unlink_send(
|
|
TALLOC_CTX *mem_ctx,
|
|
struct tevent_context *ev,
|
|
struct cli_state *cli,
|
|
const char *fname,
|
|
const struct smb2_create_blobs *in_cblobs)
|
|
{
|
|
struct tevent_req *req = NULL, *subreq = NULL;
|
|
struct cli_smb2_unlink_state *state = NULL;
|
|
|
|
req = tevent_req_create(mem_ctx, &state, struct cli_smb2_unlink_state);
|
|
if (req == NULL) {
|
|
return NULL;
|
|
}
|
|
state->ev = ev;
|
|
state->cli = cli;
|
|
state->fname = fname;
|
|
state->in_cblobs = in_cblobs;
|
|
|
|
if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
|
|
tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
|
|
subreq = cli_smb2_create_fnum_send(
|
|
state, /* mem_ctx */
|
|
state->ev, /* tevent_context */
|
|
state->cli, /* cli_struct */
|
|
state->fname, /* filename */
|
|
0, /* create_flags */
|
|
SMB2_IMPERSONATION_IMPERSONATION,
|
|
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 */
|
|
state->in_cblobs); /* in_cblobs */
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
tevent_req_set_callback(subreq, cli_smb2_unlink_opened1, req);
|
|
return req;
|
|
}
|
|
|
|
static void cli_smb2_unlink_opened1(struct tevent_req *subreq)
|
|
{
|
|
struct tevent_req *req = tevent_req_callback_data(
|
|
subreq, struct tevent_req);
|
|
struct cli_smb2_unlink_state *state = tevent_req_data(
|
|
req, struct cli_smb2_unlink_state);
|
|
uint16_t fnum = 0xffff;
|
|
NTSTATUS status;
|
|
|
|
status = cli_smb2_create_fnum_recv(subreq, &fnum, NULL, NULL, NULL);
|
|
TALLOC_FREE(subreq);
|
|
|
|
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.
|
|
*/
|
|
subreq = cli_smb2_create_fnum_send(
|
|
state, /* mem_ctx */
|
|
state->ev, /* tevent_context */
|
|
state->cli, /* cli_struct */
|
|
state->fname, /* filename */
|
|
0, /* create_flags */
|
|
SMB2_IMPERSONATION_IMPERSONATION,
|
|
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 */
|
|
state->in_cblobs); /* in_cblobs */
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return;
|
|
}
|
|
tevent_req_set_callback(subreq, cli_smb2_unlink_opened2, req);
|
|
return;
|
|
}
|
|
|
|
if (tevent_req_nterror(req, status)) {
|
|
return;
|
|
}
|
|
|
|
subreq = cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return;
|
|
}
|
|
tevent_req_set_callback(subreq, cli_smb2_unlink_closed, req);
|
|
}
|
|
|
|
static void cli_smb2_unlink_opened2(struct tevent_req *subreq)
|
|
{
|
|
struct tevent_req *req = tevent_req_callback_data(
|
|
subreq, struct tevent_req);
|
|
struct cli_smb2_unlink_state *state = tevent_req_data(
|
|
req, struct cli_smb2_unlink_state);
|
|
uint16_t fnum = 0xffff;
|
|
NTSTATUS status;
|
|
|
|
status = cli_smb2_create_fnum_recv(subreq, &fnum, NULL, NULL, NULL);
|
|
TALLOC_FREE(subreq);
|
|
if (tevent_req_nterror(req, status)) {
|
|
return;
|
|
}
|
|
|
|
subreq = cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return;
|
|
}
|
|
tevent_req_set_callback(subreq, cli_smb2_unlink_closed, req);
|
|
}
|
|
|
|
static void cli_smb2_unlink_closed(struct tevent_req *subreq)
|
|
{
|
|
NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
|
|
tevent_req_simple_finish_ntstatus(subreq, status);
|
|
}
|
|
|
|
NTSTATUS cli_smb2_unlink_recv(struct tevent_req *req)
|
|
{
|
|
return tevent_req_simple_recv_ntstatus(req);
|
|
}
|
|
|
|
/***************************************************************
|
|
Utility function to parse a SMB2_FIND_ID_BOTH_DIRECTORY_INFO reply.
|
|
***************************************************************/
|
|
|
|
static NTSTATUS parse_finfo_id_both_directory_info(const 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->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);
|
|
finfo->size = IVAL2_TO_SMB_BIG_UINT(dir_data + 40, 0);
|
|
finfo->allocated_size = IVAL2_TO_SMB_BIG_UINT(dir_data + 48, 0);
|
|
finfo->attr = IVAL(dir_data + 56, 0);
|
|
finfo->ino = IVAL2_TO_SMB_BIG_UINT(dir_data + 96, 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;
|
|
}
|
|
|
|
if (finfo->name == NULL) {
|
|
/* 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;
|
|
}
|
|
|
|
struct cli_smb2_list_dir_data {
|
|
uint8_t *data;
|
|
uint32_t length;
|
|
};
|
|
|
|
struct cli_smb2_list_state {
|
|
struct tevent_context *ev;
|
|
struct cli_state *cli;
|
|
const char *mask;
|
|
|
|
uint16_t fnum;
|
|
|
|
NTSTATUS status;
|
|
struct cli_smb2_list_dir_data *response;
|
|
uint32_t offset;
|
|
};
|
|
|
|
static void cli_smb2_list_opened(struct tevent_req *subreq);
|
|
static void cli_smb2_list_done(struct tevent_req *subreq);
|
|
static void cli_smb2_list_closed(struct tevent_req *subreq);
|
|
|
|
struct tevent_req *cli_smb2_list_send(
|
|
TALLOC_CTX *mem_ctx,
|
|
struct tevent_context *ev,
|
|
struct cli_state *cli,
|
|
const char *pathname)
|
|
{
|
|
struct tevent_req *req = NULL, *subreq = NULL;
|
|
struct cli_smb2_list_state *state = NULL;
|
|
char *parent = NULL;
|
|
bool ok;
|
|
|
|
req = tevent_req_create(mem_ctx, &state, struct cli_smb2_list_state);
|
|
if (req == NULL) {
|
|
return NULL;
|
|
}
|
|
state->ev = ev;
|
|
state->cli = cli;
|
|
state->status = NT_STATUS_OK;
|
|
|
|
if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
|
|
tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
|
|
ok = windows_parent_dirname(state, pathname, &parent, &state->mask);
|
|
if (!ok) {
|
|
tevent_req_oom(req);
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
|
|
subreq = cli_smb2_create_fnum_send(
|
|
state, /* mem_ctx */
|
|
ev, /* ev */
|
|
cli, /* cli */
|
|
parent, /* fname */
|
|
0, /* create_flags */
|
|
SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
|
|
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 */
|
|
NULL); /* in_cblobs */
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
tevent_req_set_callback(subreq, cli_smb2_list_opened, req);
|
|
return req;
|
|
}
|
|
|
|
static void cli_smb2_list_opened(struct tevent_req *subreq)
|
|
{
|
|
struct tevent_req *req = tevent_req_callback_data(
|
|
subreq, struct tevent_req);
|
|
struct cli_smb2_list_state *state = tevent_req_data(
|
|
req, struct cli_smb2_list_state);
|
|
NTSTATUS status;
|
|
|
|
status = cli_smb2_create_fnum_recv(
|
|
subreq, &state->fnum, NULL, NULL, NULL);
|
|
TALLOC_FREE(subreq);
|
|
if (tevent_req_nterror(req, status)) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Make our caller get back to us via cli_smb2_list_recv(),
|
|
* triggering the smb2_query_directory_send()
|
|
*/
|
|
tevent_req_defer_callback(req, state->ev);
|
|
tevent_req_notify_callback(req);
|
|
}
|
|
|
|
static void cli_smb2_list_done(struct tevent_req *subreq)
|
|
{
|
|
struct tevent_req *req = tevent_req_callback_data(
|
|
subreq, struct tevent_req);
|
|
struct cli_smb2_list_state *state = tevent_req_data(
|
|
req, struct cli_smb2_list_state);
|
|
struct cli_smb2_list_dir_data *response = NULL;
|
|
|
|
response = talloc(state, struct cli_smb2_list_dir_data);
|
|
if (tevent_req_nomem(response, req)) {
|
|
return;
|
|
}
|
|
|
|
state->status = smb2cli_query_directory_recv(
|
|
subreq, response, &response->data, &response->length);
|
|
TALLOC_FREE(subreq);
|
|
|
|
if (NT_STATUS_IS_OK(state->status)) {
|
|
state->response = response;
|
|
state->offset = 0;
|
|
|
|
tevent_req_defer_callback(req, state->ev);
|
|
tevent_req_notify_callback(req);
|
|
return;
|
|
}
|
|
|
|
TALLOC_FREE(response);
|
|
|
|
subreq = cli_smb2_close_fnum_send(
|
|
state, state->ev, state->cli, state->fnum);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return;
|
|
}
|
|
tevent_req_set_callback(subreq, cli_smb2_list_closed, req);
|
|
}
|
|
|
|
static void cli_smb2_list_closed(struct tevent_req *subreq)
|
|
{
|
|
NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
|
|
tevent_req_simple_finish_ntstatus(subreq, status);
|
|
}
|
|
|
|
/*
|
|
* Return the next finfo directory.
|
|
*
|
|
* This parses the blob returned from QUERY_DIRECTORY step by step. If
|
|
* the blob ends, this triggers a fresh QUERY_DIRECTORY and returns
|
|
* NT_STATUS_RETRY, which will then trigger the caller again when the
|
|
* QUERY_DIRECTORY has returned with another buffer. This way we
|
|
* guarantee that no asynchronous request is open after this call
|
|
* returns an entry, so that other synchronous requests can be issued
|
|
* on the same connection while the directoy listing proceeds.
|
|
*/
|
|
NTSTATUS cli_smb2_list_recv(
|
|
struct tevent_req *req,
|
|
TALLOC_CTX *mem_ctx,
|
|
struct file_info **pfinfo)
|
|
{
|
|
struct cli_smb2_list_state *state = tevent_req_data(
|
|
req, struct cli_smb2_list_state);
|
|
struct cli_smb2_list_dir_data *response = NULL;
|
|
struct file_info *finfo = NULL;
|
|
NTSTATUS status;
|
|
uint32_t next_offset = 0;
|
|
bool in_progress;
|
|
|
|
in_progress = tevent_req_is_in_progress(req);
|
|
|
|
if (!in_progress) {
|
|
if (!tevent_req_is_nterror(req, &status)) {
|
|
status = NT_STATUS_NO_MORE_FILES;
|
|
}
|
|
goto fail;
|
|
}
|
|
|
|
response = state->response;
|
|
if (response == NULL) {
|
|
struct tevent_req *subreq = NULL;
|
|
struct cli_state *cli = state->cli;
|
|
struct smb2_hnd *ph = NULL;
|
|
uint32_t max_trans, max_avail_len;
|
|
bool ok;
|
|
|
|
if (!NT_STATUS_IS_OK(state->status)) {
|
|
status = state->status;
|
|
goto fail;
|
|
}
|
|
|
|
status = map_fnum_to_smb2_handle(cli, state->fnum, &ph);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
goto fail;
|
|
}
|
|
|
|
max_trans = smb2cli_conn_max_trans_size(cli->conn);
|
|
ok = smb2cli_conn_req_possible(cli->conn, &max_avail_len);
|
|
if (ok) {
|
|
max_trans = MIN(max_trans, max_avail_len);
|
|
}
|
|
|
|
subreq = smb2cli_query_directory_send(
|
|
state, /* mem_ctx */
|
|
state->ev, /* ev */
|
|
cli->conn, /* conn */
|
|
cli->timeout, /* timeout_msec */
|
|
cli->smb2.session, /* session */
|
|
cli->smb2.tcon, /* tcon */
|
|
SMB2_FIND_ID_BOTH_DIRECTORY_INFO, /* level */
|
|
0, /* flags */
|
|
0, /* file_index */
|
|
ph->fid_persistent, /* fid_persistent */
|
|
ph->fid_volatile, /* fid_volatile */
|
|
state->mask, /* mask */
|
|
max_trans); /* outbuf_len */
|
|
if (subreq == NULL) {
|
|
status = NT_STATUS_NO_MEMORY;
|
|
goto fail;
|
|
}
|
|
tevent_req_set_callback(subreq, cli_smb2_list_done, req);
|
|
return NT_STATUS_RETRY;
|
|
}
|
|
|
|
SMB_ASSERT(response->length > state->offset);
|
|
|
|
finfo = talloc_zero(mem_ctx, struct file_info);
|
|
if (finfo == NULL) {
|
|
status = NT_STATUS_NO_MEMORY;
|
|
goto fail;
|
|
}
|
|
|
|
status = parse_finfo_id_both_directory_info(
|
|
response->data + state->offset,
|
|
response->length - state->offset,
|
|
finfo,
|
|
&next_offset);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
goto fail;
|
|
}
|
|
|
|
status = is_bad_finfo_name(state->cli, finfo);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
goto fail;
|
|
}
|
|
|
|
/*
|
|
* parse_finfo_id_both_directory_info() checks for overflow,
|
|
* no need to check again here.
|
|
*/
|
|
state->offset += next_offset;
|
|
|
|
if (next_offset == 0) {
|
|
TALLOC_FREE(state->response);
|
|
}
|
|
|
|
tevent_req_defer_callback(req, state->ev);
|
|
tevent_req_notify_callback(req);
|
|
|
|
*pfinfo = finfo;
|
|
return NT_STATUS_OK;
|
|
|
|
fail:
|
|
TALLOC_FREE(finfo);
|
|
tevent_req_received(req);
|
|
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_strndup(talloc_tos(), name, namelen-1);
|
|
if (modname == NULL) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
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 */
|
|
SMB2_IMPERSONATION_IMPERSONATION,
|
|
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 */
|
|
NULL,
|
|
&fnum,
|
|
&cr,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_DIRECTORY)) {
|
|
/* Maybe a file ? */
|
|
status = cli_smb2_create_fnum(cli,
|
|
name,
|
|
0, /* create_flags */
|
|
SMB2_IMPERSONATION_IMPERSONATION,
|
|
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 */
|
|
NULL,
|
|
&fnum,
|
|
&cr,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
struct cli_smb2_chkpath_state {
|
|
struct tevent_context *ev;
|
|
struct cli_state *cli;
|
|
};
|
|
|
|
static void cli_smb2_chkpath_opened(struct tevent_req *subreq);
|
|
static void cli_smb2_chkpath_closed(struct tevent_req *subreq);
|
|
|
|
struct tevent_req *cli_smb2_chkpath_send(
|
|
TALLOC_CTX *mem_ctx,
|
|
struct tevent_context *ev,
|
|
struct cli_state *cli,
|
|
const char *name)
|
|
{
|
|
struct tevent_req *req = NULL, *subreq = NULL;
|
|
struct cli_smb2_chkpath_state *state = NULL;
|
|
|
|
req = tevent_req_create(
|
|
mem_ctx, &state, struct cli_smb2_chkpath_state);
|
|
if (req == NULL) {
|
|
return NULL;
|
|
}
|
|
state->ev = ev;
|
|
state->cli = cli;
|
|
|
|
/* Ensure this is a directory. */
|
|
subreq = cli_smb2_create_fnum_send(
|
|
state, /* mem_ctx */
|
|
ev, /* ev */
|
|
cli, /* cli */
|
|
name, /* fname */
|
|
0, /* create_flags */
|
|
SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
|
|
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 */
|
|
NULL); /* in_cblobs */
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
tevent_req_set_callback(subreq, cli_smb2_chkpath_opened, req);
|
|
return req;
|
|
}
|
|
|
|
static void cli_smb2_chkpath_opened(struct tevent_req *subreq)
|
|
{
|
|
struct tevent_req *req = tevent_req_callback_data(
|
|
subreq, struct tevent_req);
|
|
struct cli_smb2_chkpath_state *state = tevent_req_data(
|
|
req, struct cli_smb2_chkpath_state);
|
|
NTSTATUS status;
|
|
uint16_t fnum = 0xffff;
|
|
|
|
status = cli_smb2_create_fnum_recv(subreq, &fnum, NULL, NULL, NULL);
|
|
TALLOC_FREE(subreq);
|
|
if (tevent_req_nterror(req, status)) {
|
|
return;
|
|
}
|
|
|
|
subreq = cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return;
|
|
}
|
|
tevent_req_set_callback(subreq, cli_smb2_chkpath_closed, req);
|
|
}
|
|
|
|
static void cli_smb2_chkpath_closed(struct tevent_req *subreq)
|
|
{
|
|
NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
|
|
tevent_req_simple_finish_ntstatus(subreq, status);
|
|
}
|
|
|
|
NTSTATUS cli_smb2_chkpath_recv(struct tevent_req *req)
|
|
{
|
|
return tevent_req_simple_recv_ntstatus(req);
|
|
}
|
|
|
|
struct cli_smb2_query_info_fnum_state {
|
|
DATA_BLOB outbuf;
|
|
};
|
|
|
|
static void cli_smb2_query_info_fnum_done(struct tevent_req *subreq);
|
|
|
|
struct tevent_req *cli_smb2_query_info_fnum_send(
|
|
TALLOC_CTX *mem_ctx,
|
|
struct tevent_context *ev,
|
|
struct cli_state *cli,
|
|
uint16_t fnum,
|
|
uint8_t in_info_type,
|
|
uint8_t in_info_class,
|
|
uint32_t in_max_output_length,
|
|
const DATA_BLOB *in_input_buffer,
|
|
uint32_t in_additional_info,
|
|
uint32_t in_flags)
|
|
{
|
|
struct tevent_req *req = NULL, *subreq = NULL;
|
|
struct cli_smb2_query_info_fnum_state *state = NULL;
|
|
struct smb2_hnd *ph = NULL;
|
|
NTSTATUS status;
|
|
|
|
req = tevent_req_create(
|
|
mem_ctx, &state, struct cli_smb2_query_info_fnum_state);
|
|
if (req == NULL) {
|
|
return req;
|
|
}
|
|
|
|
status = map_fnum_to_smb2_handle(cli, fnum, &ph);
|
|
if (tevent_req_nterror(req, status)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
|
|
subreq = smb2cli_query_info_send(
|
|
state,
|
|
ev,
|
|
cli->conn,
|
|
cli->timeout,
|
|
cli->smb2.session,
|
|
cli->smb2.tcon,
|
|
in_info_type,
|
|
in_info_class,
|
|
in_max_output_length,
|
|
in_input_buffer,
|
|
in_additional_info,
|
|
in_flags,
|
|
ph->fid_persistent,
|
|
ph->fid_volatile);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
tevent_req_set_callback(subreq, cli_smb2_query_info_fnum_done, req);
|
|
return req;
|
|
}
|
|
|
|
static void cli_smb2_query_info_fnum_done(struct tevent_req *subreq)
|
|
{
|
|
struct tevent_req *req = tevent_req_callback_data(
|
|
subreq, struct tevent_req);
|
|
struct cli_smb2_query_info_fnum_state *state = tevent_req_data(
|
|
req, struct cli_smb2_query_info_fnum_state);
|
|
DATA_BLOB outbuf;
|
|
NTSTATUS status;
|
|
|
|
status = smb2cli_query_info_recv(subreq, state, &outbuf);
|
|
TALLOC_FREE(subreq);
|
|
if (tevent_req_nterror(req, status)) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* We have to dup the memory here because outbuf.data is not
|
|
* returned as a talloc object by smb2cli_query_info_recv.
|
|
* It's a pointer into the received buffer.
|
|
*/
|
|
state->outbuf = data_blob_dup_talloc(state, outbuf);
|
|
|
|
if ((outbuf.length != 0) &&
|
|
tevent_req_nomem(state->outbuf.data, req)) {
|
|
return;
|
|
}
|
|
tevent_req_done(req);
|
|
}
|
|
|
|
NTSTATUS cli_smb2_query_info_fnum_recv(
|
|
struct tevent_req *req, TALLOC_CTX *mem_ctx, DATA_BLOB *outbuf)
|
|
{
|
|
struct cli_smb2_query_info_fnum_state *state = tevent_req_data(
|
|
req, struct cli_smb2_query_info_fnum_state);
|
|
NTSTATUS status;
|
|
|
|
if (tevent_req_is_nterror(req, &status)) {
|
|
return status;
|
|
}
|
|
*outbuf = (DATA_BLOB) {
|
|
.data = talloc_move(mem_ctx, &state->outbuf.data),
|
|
.length = state->outbuf.length,
|
|
};
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
NTSTATUS cli_smb2_query_info_fnum(
|
|
struct cli_state *cli,
|
|
uint16_t fnum,
|
|
uint8_t in_info_type,
|
|
uint8_t in_info_class,
|
|
uint32_t in_max_output_length,
|
|
const DATA_BLOB *in_input_buffer,
|
|
uint32_t in_additional_info,
|
|
uint32_t in_flags,
|
|
TALLOC_CTX *mem_ctx,
|
|
DATA_BLOB *outbuf)
|
|
{
|
|
TALLOC_CTX *frame = talloc_stackframe();
|
|
struct tevent_context *ev = NULL;
|
|
struct tevent_req *req = NULL;
|
|
NTSTATUS status = NT_STATUS_NO_MEMORY;
|
|
bool ok;
|
|
|
|
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_query_info_fnum_send(
|
|
frame,
|
|
ev,
|
|
cli,
|
|
fnum,
|
|
in_info_type,
|
|
in_info_class,
|
|
in_max_output_length,
|
|
in_input_buffer,
|
|
in_additional_info,
|
|
in_flags);
|
|
if (req == NULL) {
|
|
goto fail;
|
|
}
|
|
ok = tevent_req_poll_ntstatus(req, ev, &status);
|
|
if (!ok) {
|
|
goto fail;
|
|
}
|
|
status = cli_smb2_query_info_fnum_recv(req, mem_ctx, outbuf);
|
|
fail:
|
|
TALLOC_FREE(frame);
|
|
return status;
|
|
}
|
|
|
|
/***************************************************************
|
|
Helper function for pathname operations.
|
|
***************************************************************/
|
|
|
|
struct get_fnum_from_path_state {
|
|
struct tevent_context *ev;
|
|
struct cli_state *cli;
|
|
const char *name;
|
|
uint32_t desired_access;
|
|
uint16_t fnum;
|
|
};
|
|
|
|
static void get_fnum_from_path_opened_file(struct tevent_req *subreq);
|
|
static void get_fnum_from_path_opened_reparse(struct tevent_req *subreq);
|
|
static void get_fnum_from_path_opened_dir(struct tevent_req *subreq);
|
|
|
|
static struct tevent_req *get_fnum_from_path_send(
|
|
TALLOC_CTX *mem_ctx,
|
|
struct tevent_context *ev,
|
|
struct cli_state *cli,
|
|
const char *name,
|
|
uint32_t desired_access)
|
|
{
|
|
struct tevent_req *req = NULL, *subreq = NULL;
|
|
struct get_fnum_from_path_state *state = NULL;
|
|
size_t namelen = strlen(name);
|
|
|
|
req = tevent_req_create(
|
|
mem_ctx, &state, struct get_fnum_from_path_state);
|
|
if (req == NULL) {
|
|
return NULL;
|
|
}
|
|
state->ev = ev;
|
|
state->cli = cli;
|
|
state->name = name;
|
|
state->desired_access = desired_access;
|
|
|
|
/*
|
|
* SMB2 is pickier about pathnames. Ensure it doesn't end in a
|
|
* '\'
|
|
*/
|
|
if (namelen > 0 && name[namelen-1] == '\\') {
|
|
state->name = talloc_strndup(state, name, namelen-1);
|
|
if (tevent_req_nomem(state->name, req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
}
|
|
|
|
subreq = cli_smb2_create_fnum_send(
|
|
state, /* mem_ctx, */
|
|
ev, /* ev */
|
|
cli, /* cli */
|
|
state->name, /* fname */
|
|
0, /* create_flags */
|
|
SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
|
|
desired_access, /* desired_access */
|
|
0, /* file_attributes */
|
|
FILE_SHARE_READ|
|
|
FILE_SHARE_WRITE|
|
|
FILE_SHARE_DELETE, /* share_access */
|
|
FILE_OPEN, /* create_disposition */
|
|
0, /* create_options */
|
|
NULL); /* in_cblobs */
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
tevent_req_set_callback(subreq, get_fnum_from_path_opened_file, req);
|
|
return req;
|
|
}
|
|
|
|
static void get_fnum_from_path_opened_file(struct tevent_req *subreq)
|
|
{
|
|
struct tevent_req *req = tevent_req_callback_data(
|
|
subreq, struct tevent_req);
|
|
struct get_fnum_from_path_state *state = tevent_req_data(
|
|
req, struct get_fnum_from_path_state);
|
|
NTSTATUS status;
|
|
|
|
status = cli_smb2_create_fnum_recv(
|
|
subreq, &state->fnum, NULL, NULL, NULL);
|
|
TALLOC_FREE(subreq);
|
|
|
|
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.
|
|
*/
|
|
subreq = cli_smb2_create_fnum_send(
|
|
state, /* mem_ctx, */
|
|
state->ev, /* ev */
|
|
state->cli, /* cli */
|
|
state->name, /* fname */
|
|
0, /* create_flags */
|
|
SMB2_IMPERSONATION_IMPERSONATION, /* impersonation */
|
|
state->desired_access, /* desired_access */
|
|
0, /* file_attributes */
|
|
FILE_SHARE_READ|
|
|
FILE_SHARE_WRITE|
|
|
FILE_SHARE_DELETE, /* share_access */
|
|
FILE_OPEN, /* create_disposition */
|
|
FILE_OPEN_REPARSE_POINT, /* create_options */
|
|
NULL); /* in_cblobs */
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return;
|
|
}
|
|
tevent_req_set_callback(
|
|
subreq, get_fnum_from_path_opened_reparse, req);
|
|
return;
|
|
}
|
|
|
|
if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
|
|
subreq = cli_smb2_create_fnum_send(
|
|
state, /* mem_ctx, */
|
|
state->ev, /* ev */
|
|
state->cli, /* cli */
|
|
state->name, /* fname */
|
|
0, /* create_flags */
|
|
SMB2_IMPERSONATION_IMPERSONATION, /* impersonation */
|
|
state->desired_access, /* desired_access */
|
|
0, /* file_attributes */
|
|
FILE_SHARE_READ|
|
|
FILE_SHARE_WRITE|
|
|
FILE_SHARE_DELETE, /* share_access */
|
|
FILE_OPEN, /* create_disposition */
|
|
FILE_DIRECTORY_FILE, /* create_options */
|
|
NULL); /* in_cblobs */
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return;
|
|
}
|
|
tevent_req_set_callback(
|
|
subreq, get_fnum_from_path_opened_dir, req);
|
|
return;
|
|
}
|
|
|
|
if (tevent_req_nterror(req, status)) {
|
|
return;
|
|
}
|
|
tevent_req_done(req);
|
|
}
|
|
|
|
static void get_fnum_from_path_opened_reparse(struct tevent_req *subreq)
|
|
{
|
|
struct tevent_req *req = tevent_req_callback_data(
|
|
subreq, struct tevent_req);
|
|
struct get_fnum_from_path_state *state = tevent_req_data(
|
|
req, struct get_fnum_from_path_state);
|
|
NTSTATUS status = cli_smb2_create_fnum_recv(
|
|
subreq, &state->fnum, NULL, NULL, NULL);
|
|
tevent_req_simple_finish_ntstatus(subreq, status);
|
|
}
|
|
|
|
static void get_fnum_from_path_opened_dir(struct tevent_req *subreq)
|
|
{
|
|
/* Abstraction violation, but these two are just the same... */
|
|
get_fnum_from_path_opened_reparse(subreq);
|
|
}
|
|
|
|
static NTSTATUS get_fnum_from_path_recv(
|
|
struct tevent_req *req, uint16_t *pfnum)
|
|
{
|
|
struct get_fnum_from_path_state *state = tevent_req_data(
|
|
req, struct get_fnum_from_path_state);
|
|
NTSTATUS status = NT_STATUS_OK;
|
|
|
|
if (!tevent_req_is_nterror(req, &status)) {
|
|
*pfnum = state->fnum;
|
|
}
|
|
tevent_req_received(req);
|
|
return status;
|
|
}
|
|
|
|
static NTSTATUS get_fnum_from_path(struct cli_state *cli,
|
|
const char *name,
|
|
uint32_t desired_access,
|
|
uint16_t *pfnum)
|
|
{
|
|
TALLOC_CTX *frame = talloc_stackframe();
|
|
struct tevent_context *ev = NULL;
|
|
struct tevent_req *req = NULL;
|
|
NTSTATUS status = NT_STATUS_NO_MEMORY;
|
|
|
|
if (smbXcli_conn_has_async_calls(cli->conn)) {
|
|
status = NT_STATUS_INVALID_PARAMETER;
|
|
goto fail;
|
|
}
|
|
ev = samba_tevent_context_init(frame);
|
|
if (ev == NULL) {
|
|
goto fail;
|
|
}
|
|
req = get_fnum_from_path_send(frame, ev, cli, name, desired_access);
|
|
if (req == NULL) {
|
|
goto fail;
|
|
}
|
|
if (!tevent_req_poll_ntstatus(req, ev, &status)) {
|
|
goto fail;
|
|
}
|
|
status = get_fnum_from_path_recv(req, pfnum);
|
|
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;
|
|
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 = cli_smb2_query_info_fnum(
|
|
cli,
|
|
fnum,
|
|
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 */
|
|
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 get pathname attributes.
|
|
Synchronous only.
|
|
***************************************************************/
|
|
|
|
NTSTATUS cli_smb2_getatr(struct cli_state *cli,
|
|
const char *name,
|
|
uint32_t *pattr,
|
|
off_t *size,
|
|
time_t *write_time)
|
|
{
|
|
NTSTATUS status;
|
|
uint16_t fnum = 0xffff;
|
|
struct smb2_hnd *ph = NULL;
|
|
struct timespec write_time_ts;
|
|
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_qfileinfo_basic(
|
|
cli,
|
|
fnum,
|
|
pattr,
|
|
size,
|
|
NULL, /* create_time */
|
|
NULL, /* access_time */
|
|
&write_time_ts,
|
|
NULL, /* change_time */
|
|
NULL); /* ino */
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
goto fail;
|
|
}
|
|
if (write_time != NULL) {
|
|
*write_time = write_time_ts.tv_sec;
|
|
}
|
|
|
|
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_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,
|
|
uint32_t *pattr,
|
|
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_qfileinfo_basic(
|
|
cli,
|
|
fnum,
|
|
pattr,
|
|
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;
|
|
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;
|
|
}
|
|
|
|
/* getinfo on the handle with info_type SMB2_GETINFO_FILE (1),
|
|
level 22 (SMB2_FILE_STREAM_INFORMATION). */
|
|
|
|
status = cli_smb2_query_info_fnum(
|
|
cli,
|
|
fnum,
|
|
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 */
|
|
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;
|
|
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 = cli_smb2_set_info_fnum(
|
|
cli,
|
|
fnum,
|
|
in_info_type,
|
|
in_file_info_class,
|
|
p_in_data, /* in_input_buffer */
|
|
0); /* in_additional_info */
|
|
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,
|
|
uint32_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;
|
|
}
|
|
|
|
SIVAL(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)
|
|
{
|
|
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;
|
|
}
|
|
|
|
/* 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 = cli_smb2_set_info_fnum(
|
|
cli,
|
|
fnum,
|
|
1, /* in_info_type */
|
|
SMB_FILE_BASIC_INFORMATION - 1000, /* in_file_info_class */
|
|
&inbuf, /* in_input_buffer */
|
|
0); /* in_additional_info */
|
|
|
|
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;
|
|
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 */
|
|
SMB2_IMPERSONATION_IMPERSONATION,
|
|
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 */
|
|
NULL,
|
|
&fnum,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
|
|
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 = cli_smb2_query_info_fnum(
|
|
cli,
|
|
fnum,
|
|
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 */
|
|
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 sizes.
|
|
Synchronous only.
|
|
***************************************************************/
|
|
|
|
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;
|
|
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 */
|
|
SMB2_IMPERSONATION_IMPERSONATION,
|
|
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 */
|
|
NULL,
|
|
&fnum,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
|
|
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 = cli_smb2_query_info_fnum(
|
|
cli,
|
|
fnum,
|
|
SMB2_0_INFO_FILESYSTEM, /* in_info_type */
|
|
SMB_FS_FULL_SIZE_INFORMATION - 1000, /* in_file_info_class */
|
|
0xFFFF, /* in_max_output_length */
|
|
NULL, /* in_input_buffer */
|
|
0, /* in_additional_info */
|
|
0, /* in_flags */
|
|
frame,
|
|
&outbuf);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
goto fail;
|
|
}
|
|
|
|
if (outbuf.length < 32) {
|
|
status = NT_STATUS_INVALID_NETWORK_RESPONSE;
|
|
goto fail;
|
|
}
|
|
|
|
*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);
|
|
}
|
|
|
|
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;
|
|
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 */
|
|
SMB2_IMPERSONATION_IMPERSONATION,
|
|
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 */
|
|
NULL,
|
|
&fnum,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
goto fail;
|
|
}
|
|
|
|
status = cli_smb2_query_info_fnum(
|
|
cli,
|
|
fnum,
|
|
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 */
|
|
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 file system volume info.
|
|
Synchronous only.
|
|
***************************************************************/
|
|
|
|
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;
|
|
uint16_t fnum = 0xffff;
|
|
DATA_BLOB outbuf = data_blob_null;
|
|
uint32_t nlen;
|
|
char *volume_name = 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 */
|
|
SMB2_IMPERSONATION_IMPERSONATION,
|
|
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 */
|
|
NULL,
|
|
&fnum,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
goto fail;
|
|
}
|
|
|
|
/* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
|
|
level 1 (SMB_FS_VOLUME_INFORMATION). */
|
|
|
|
status = cli_smb2_query_info_fnum(
|
|
cli,
|
|
fnum,
|
|
SMB2_0_INFO_FILESYSTEM, /* 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 */
|
|
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;
|
|
}
|
|
|
|
pull_string_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;
|
|
}
|
|
|
|
struct cli_smb2_mxac_state {
|
|
struct tevent_context *ev;
|
|
struct cli_state *cli;
|
|
const char *fname;
|
|
struct smb2_create_blobs in_cblobs;
|
|
uint16_t fnum;
|
|
NTSTATUS status;
|
|
uint32_t mxac;
|
|
};
|
|
|
|
static void cli_smb2_mxac_opened(struct tevent_req *subreq);
|
|
static void cli_smb2_mxac_closed(struct tevent_req *subreq);
|
|
|
|
struct tevent_req *cli_smb2_query_mxac_send(TALLOC_CTX *mem_ctx,
|
|
struct tevent_context *ev,
|
|
struct cli_state *cli,
|
|
const char *fname)
|
|
{
|
|
struct tevent_req *req = NULL, *subreq = NULL;
|
|
struct cli_smb2_mxac_state *state = NULL;
|
|
NTSTATUS status;
|
|
|
|
req = tevent_req_create(mem_ctx, &state, struct cli_smb2_mxac_state);
|
|
if (req == NULL) {
|
|
return NULL;
|
|
}
|
|
*state = (struct cli_smb2_mxac_state) {
|
|
.ev = ev,
|
|
.cli = cli,
|
|
.fname = fname,
|
|
};
|
|
|
|
if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
|
|
tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
|
|
status = smb2_create_blob_add(state,
|
|
&state->in_cblobs,
|
|
SMB2_CREATE_TAG_MXAC,
|
|
data_blob(NULL, 0));
|
|
if (tevent_req_nterror(req, status)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
|
|
subreq = cli_smb2_create_fnum_send(
|
|
state,
|
|
state->ev,
|
|
state->cli,
|
|
state->fname,
|
|
0, /* create_flags */
|
|
SMB2_IMPERSONATION_IMPERSONATION,
|
|
FILE_READ_ATTRIBUTES,
|
|
0, /* file attributes */
|
|
FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
|
|
FILE_OPEN,
|
|
0, /* create_options */
|
|
&state->in_cblobs);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
tevent_req_set_callback(subreq, cli_smb2_mxac_opened, req);
|
|
return req;
|
|
}
|
|
|
|
static void cli_smb2_mxac_opened(struct tevent_req *subreq)
|
|
{
|
|
struct tevent_req *req = tevent_req_callback_data(
|
|
subreq, struct tevent_req);
|
|
struct cli_smb2_mxac_state *state = tevent_req_data(
|
|
req, struct cli_smb2_mxac_state);
|
|
struct smb2_create_blobs out_cblobs = {0};
|
|
struct smb2_create_blob *mxac_blob = NULL;
|
|
NTSTATUS status;
|
|
|
|
status = cli_smb2_create_fnum_recv(
|
|
subreq, &state->fnum, NULL, state, &out_cblobs);
|
|
TALLOC_FREE(subreq);
|
|
|
|
if (tevent_req_nterror(req, status)) {
|
|
return;
|
|
}
|
|
|
|
mxac_blob = smb2_create_blob_find(&out_cblobs, SMB2_CREATE_TAG_MXAC);
|
|
if (mxac_blob == NULL) {
|
|
state->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
|
|
goto close;
|
|
}
|
|
if (mxac_blob->data.length != 8) {
|
|
state->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
|
|
goto close;
|
|
}
|
|
|
|
state->status = NT_STATUS(IVAL(mxac_blob->data.data, 0));
|
|
state->mxac = IVAL(mxac_blob->data.data, 4);
|
|
|
|
close:
|
|
subreq = cli_smb2_close_fnum_send(
|
|
state, state->ev, state->cli, state->fnum);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return;
|
|
}
|
|
tevent_req_set_callback(subreq, cli_smb2_mxac_closed, req);
|
|
|
|
return;
|
|
}
|
|
|
|
static void cli_smb2_mxac_closed(struct tevent_req *subreq)
|
|
{
|
|
struct tevent_req *req = tevent_req_callback_data(
|
|
subreq, struct tevent_req);
|
|
NTSTATUS status;
|
|
|
|
status = cli_smb2_close_fnum_recv(subreq);
|
|
if (tevent_req_nterror(req, status)) {
|
|
return;
|
|
}
|
|
|
|
tevent_req_done(req);
|
|
}
|
|
|
|
NTSTATUS cli_smb2_query_mxac_recv(struct tevent_req *req, uint32_t *mxac)
|
|
{
|
|
struct cli_smb2_mxac_state *state = tevent_req_data(
|
|
req, struct cli_smb2_mxac_state);
|
|
NTSTATUS status;
|
|
|
|
if (tevent_req_is_nterror(req, &status)) {
|
|
return status;
|
|
}
|
|
|
|
if (!NT_STATUS_IS_OK(state->status)) {
|
|
return state->status;
|
|
}
|
|
|
|
*mxac = state->mxac;
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
NTSTATUS cli_smb2_query_mxac(struct cli_state *cli,
|
|
const char *fname,
|
|
uint32_t *_mxac)
|
|
{
|
|
TALLOC_CTX *frame = talloc_stackframe();
|
|
struct tevent_context *ev = NULL;
|
|
struct tevent_req *req = NULL;
|
|
NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
|
|
bool ok;
|
|
|
|
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_query_mxac_send(frame, ev, cli, fname);
|
|
if (req == NULL) {
|
|
goto fail;
|
|
}
|
|
ok = tevent_req_poll_ntstatus(req, ev, &status);
|
|
if (!ok) {
|
|
goto fail;
|
|
}
|
|
status = cli_smb2_query_mxac_recv(req, _mxac);
|
|
|
|
fail:
|
|
cli->raw_status = status;
|
|
TALLOC_FREE(frame);
|
|
return status;
|
|
}
|
|
|
|
struct cli_smb2_rename_fnum_state {
|
|
DATA_BLOB inbuf;
|
|
};
|
|
|
|
static void cli_smb2_rename_fnum_done(struct tevent_req *subreq);
|
|
|
|
static struct tevent_req *cli_smb2_rename_fnum_send(
|
|
TALLOC_CTX *mem_ctx,
|
|
struct tevent_context *ev,
|
|
struct cli_state *cli,
|
|
uint16_t fnum,
|
|
const char *fname_dst,
|
|
bool replace)
|
|
{
|
|
struct tevent_req *req = NULL, *subreq = NULL;
|
|
struct cli_smb2_rename_fnum_state *state = NULL;
|
|
size_t namelen = strlen(fname_dst);
|
|
smb_ucs2_t *converted_str = NULL;
|
|
size_t converted_size_bytes = 0;
|
|
size_t inbuf_size;
|
|
bool ok;
|
|
|
|
req = tevent_req_create(
|
|
mem_ctx, &state, struct cli_smb2_rename_fnum_state);
|
|
if (req == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* 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
|
|
* '\'
|
|
*/
|
|
if (namelen > 0 && fname_dst[namelen-1] == '\\') {
|
|
fname_dst = talloc_strndup(state, fname_dst, namelen-1);
|
|
if (tevent_req_nomem(fname_dst, req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
}
|
|
|
|
ok = push_ucs2_talloc(
|
|
state, &converted_str, fname_dst, &converted_size_bytes);
|
|
if (!ok) {
|
|
tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
|
|
/*
|
|
* 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) {
|
|
tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
converted_size_bytes -= 2;
|
|
|
|
inbuf_size = 20 + converted_size_bytes;
|
|
if (inbuf_size < 20) {
|
|
/* Integer wrap check. */
|
|
tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
|
|
/*
|
|
* The Windows 10 SMB2 server has a minimum length
|
|
* for a SMB2_FILE_RENAME_INFORMATION buffer of
|
|
* 24 bytes. It returns NT_STATUS_INFO_LENGTH_MISMATCH
|
|
* if the length is less. This isn't an alignment
|
|
* issue as Windows client happily 2-byte align
|
|
* for larget target name sizes. Also the Windows 10
|
|
* SMB1 server doesn't have this restriction.
|
|
*
|
|
* BUG: https://bugzilla.samba.org/show_bug.cgi?id=14403
|
|
*/
|
|
inbuf_size = MAX(inbuf_size, 24);
|
|
|
|
state->inbuf = data_blob_talloc_zero(state, inbuf_size);
|
|
if (tevent_req_nomem(state->inbuf.data, req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
|
|
if (replace) {
|
|
SCVAL(state->inbuf.data, 0, 1);
|
|
}
|
|
|
|
SIVAL(state->inbuf.data, 16, converted_size_bytes);
|
|
memcpy(state->inbuf.data + 20, converted_str, converted_size_bytes);
|
|
|
|
TALLOC_FREE(converted_str);
|
|
|
|
/* setinfo on the returned handle with info_type SMB2_GETINFO_FILE (1),
|
|
level SMB2_FILE_RENAME_INFORMATION (SMB_FILE_RENAME_INFORMATION - 1000) */
|
|
|
|
subreq = cli_smb2_set_info_fnum_send(
|
|
state, /* mem_ctx */
|
|
ev, /* ev */
|
|
cli, /* cli */
|
|
fnum, /* fnum */
|
|
1, /* in_info_type */
|
|
SMB_FILE_RENAME_INFORMATION - 1000, /* in_file_info_class */
|
|
&state->inbuf, /* in_input_buffer */
|
|
0); /* in_additional_info */
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
tevent_req_set_callback(subreq, cli_smb2_rename_fnum_done, req);
|
|
return req;
|
|
}
|
|
|
|
static void cli_smb2_rename_fnum_done(struct tevent_req *subreq)
|
|
{
|
|
NTSTATUS status = cli_smb2_set_info_fnum_recv(subreq);
|
|
tevent_req_simple_finish_ntstatus(subreq, status);
|
|
}
|
|
|
|
static NTSTATUS cli_smb2_rename_fnum_recv(struct tevent_req *req)
|
|
{
|
|
return tevent_req_simple_recv_ntstatus(req);
|
|
}
|
|
|
|
/***************************************************************
|
|
Wrapper that allows SMB2 to rename a file.
|
|
***************************************************************/
|
|
|
|
struct cli_smb2_rename_state {
|
|
struct tevent_context *ev;
|
|
struct cli_state *cli;
|
|
const char *fname_dst;
|
|
bool replace;
|
|
uint16_t fnum;
|
|
|
|
NTSTATUS rename_status;
|
|
};
|
|
|
|
static void cli_smb2_rename_opened(struct tevent_req *subreq);
|
|
static void cli_smb2_rename_renamed(struct tevent_req *subreq);
|
|
static void cli_smb2_rename_closed(struct tevent_req *subreq);
|
|
|
|
struct tevent_req *cli_smb2_rename_send(
|
|
TALLOC_CTX *mem_ctx,
|
|
struct tevent_context *ev,
|
|
struct cli_state *cli,
|
|
const char *fname_src,
|
|
const char *fname_dst,
|
|
bool replace)
|
|
{
|
|
struct tevent_req *req = NULL, *subreq = NULL;
|
|
struct cli_smb2_rename_state *state = NULL;
|
|
|
|
req = tevent_req_create(
|
|
mem_ctx, &state, struct cli_smb2_rename_state);
|
|
if (req == NULL) {
|
|
return NULL;
|
|
}
|
|
state->ev = ev;
|
|
state->cli = cli;
|
|
state->fname_dst = fname_dst;
|
|
state->replace = replace;
|
|
|
|
subreq = get_fnum_from_path_send(
|
|
state, ev, cli, fname_src, DELETE_ACCESS);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
tevent_req_set_callback(subreq, cli_smb2_rename_opened, req);
|
|
return req;
|
|
}
|
|
|
|
static void cli_smb2_rename_opened(struct tevent_req *subreq)
|
|
{
|
|
struct tevent_req *req = tevent_req_callback_data(
|
|
subreq, struct tevent_req);
|
|
struct cli_smb2_rename_state *state = tevent_req_data(
|
|
req, struct cli_smb2_rename_state);
|
|
NTSTATUS status;
|
|
|
|
status = get_fnum_from_path_recv(subreq, &state->fnum);
|
|
TALLOC_FREE(subreq);
|
|
if (tevent_req_nterror(req, status)) {
|
|
return;
|
|
}
|
|
|
|
subreq = cli_smb2_rename_fnum_send(
|
|
state,
|
|
state->ev,
|
|
state->cli,
|
|
state->fnum,
|
|
state->fname_dst,
|
|
state->replace);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return;
|
|
}
|
|
tevent_req_set_callback(subreq, cli_smb2_rename_renamed, req);
|
|
}
|
|
|
|
static void cli_smb2_rename_renamed(struct tevent_req *subreq)
|
|
{
|
|
struct tevent_req *req = tevent_req_callback_data(
|
|
subreq, struct tevent_req);
|
|
struct cli_smb2_rename_state *state = tevent_req_data(
|
|
req, struct cli_smb2_rename_state);
|
|
|
|
state->rename_status = cli_smb2_rename_fnum_recv(subreq);
|
|
TALLOC_FREE(subreq);
|
|
|
|
subreq = cli_smb2_close_fnum_send(
|
|
state, state->ev, state->cli, state->fnum);
|
|
if (tevent_req_nomem(subreq, req)) {
|
|
return;
|
|
}
|
|
tevent_req_set_callback(subreq, cli_smb2_rename_closed, req);
|
|
}
|
|
|
|
static void cli_smb2_rename_closed(struct tevent_req *subreq)
|
|
{
|
|
NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
|
|
tevent_req_simple_finish_ntstatus(subreq, status);
|
|
}
|
|
|
|
NTSTATUS cli_smb2_rename_recv(struct tevent_req *req)
|
|
{
|
|
struct cli_smb2_rename_state *state = tevent_req_data(
|
|
req, struct cli_smb2_rename_state);
|
|
NTSTATUS status = NT_STATUS_OK;
|
|
|
|
if (!tevent_req_is_nterror(req, &status)) {
|
|
status = state->rename_status;
|
|
}
|
|
tevent_req_received(req);
|
|
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;
|
|
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;
|
|
}
|
|
|
|
/* 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 = cli_smb2_set_info_fnum(
|
|
cli,
|
|
fnum,
|
|
1, /* in_info_type */
|
|
SMB_FILE_FULL_EA_INFORMATION - 1000, /* in_file_info_class */
|
|
&inbuf, /* in_input_buffer */
|
|
0); /* in_additional_info */
|
|
|
|
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 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;
|
|
}
|
|
|
|
/* getinfo on the handle with info_type SMB2_GETINFO_FILE (1),
|
|
level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */
|
|
|
|
status = cli_smb2_query_info_fnum(
|
|
cli,
|
|
fnum,
|
|
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 */
|
|
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 info_blob = data_blob_null;
|
|
DATA_BLOB outbuf = data_blob_null;
|
|
TALLOC_CTX *frame = talloc_stackframe();
|
|
unsigned sid_len;
|
|
unsigned int offset;
|
|
struct smb2_query_quota_info query = {0};
|
|
struct file_get_quota_info info = {0};
|
|
enum ndr_err_code err;
|
|
struct ndr_push *ndr_push = 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;
|
|
}
|
|
|
|
sid_len = ndr_size_dom_sid(&pqt->sid, 0);
|
|
|
|
query.return_single = 1;
|
|
|
|
info.next_entry_offset = 0;
|
|
info.sid_length = sid_len;
|
|
info.sid = pqt->sid;
|
|
|
|
err = ndr_push_struct_blob(
|
|
&info_blob,
|
|
frame,
|
|
&info,
|
|
(ndr_push_flags_fn_t)ndr_push_file_get_quota_info);
|
|
|
|
if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
|
|
status = NT_STATUS_INTERNAL_ERROR;
|
|
goto fail;
|
|
}
|
|
|
|
query.sid_list_length = info_blob.length;
|
|
ndr_push = ndr_push_init_ctx(frame);
|
|
if (!ndr_push) {
|
|
status = NT_STATUS_NO_MEMORY;
|
|
goto fail;
|
|
}
|
|
|
|
err = ndr_push_smb2_query_quota_info(ndr_push,
|
|
NDR_SCALARS | NDR_BUFFERS,
|
|
&query);
|
|
|
|
if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
|
|
status = NT_STATUS_INTERNAL_ERROR;
|
|
goto fail;
|
|
}
|
|
|
|
err = ndr_push_array_uint8(ndr_push, NDR_SCALARS, info_blob.data,
|
|
info_blob.length);
|
|
|
|
if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
|
|
status = NT_STATUS_INTERNAL_ERROR;
|
|
goto fail;
|
|
}
|
|
inbuf.data = ndr_push->data;
|
|
inbuf.length = ndr_push->offset;
|
|
|
|
status = cli_smb2_query_info_fnum(
|
|
cli,
|
|
quota_fnum,
|
|
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 */
|
|
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;
|
|
TALLOC_CTX *frame = talloc_stackframe();
|
|
struct smb2_query_quota_info info = {0};
|
|
enum ndr_err_code err;
|
|
|
|
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;
|
|
}
|
|
|
|
info.restart_scan = first ? 1 : 0;
|
|
|
|
err = ndr_push_struct_blob(
|
|
&inbuf,
|
|
frame,
|
|
&info,
|
|
(ndr_push_flags_fn_t)ndr_push_smb2_query_quota_info);
|
|
|
|
if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
|
|
status = NT_STATUS_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
|
|
status = cli_smb2_query_info_fnum(
|
|
cli,
|
|
quota_fnum,
|
|
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 */
|
|
frame,
|
|
&outbuf);
|
|
|
|
/*
|
|
* safeguard against panic from calling parse_user_quota_list with
|
|
* NULL buffer
|
|
*/
|
|
if (NT_STATUS_IS_OK(status) && outbuf.length == 0) {
|
|
status = NT_STATUS_NO_MORE_ENTRIES;
|
|
}
|
|
|
|
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;
|
|
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 = cli_smb2_query_info_fnum(
|
|
cli,
|
|
quota_fnum,
|
|
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 */
|
|
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;
|
|
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 = build_user_quota_buffer(qtl, 0, talloc_tos(), &inbuf, NULL);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
status = cli_smb2_set_info_fnum(
|
|
cli,
|
|
quota_fnum,
|
|
4, /* in_info_type */
|
|
0, /* in_file_info_class */
|
|
&inbuf, /* in_input_buffer */
|
|
0); /* in_additional_info */
|
|
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;
|
|
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 = build_fs_quota_buffer(talloc_tos(), pqt, &inbuf, 0);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
status = cli_smb2_set_info_fnum(
|
|
cli,
|
|
quota_fnum,
|
|
2, /* in_info_type */
|
|
SMB_FS_QUOTA_INFORMATION - 1000, /* in_file_info_class */
|
|
&inbuf, /* in_input_buffer */
|
|
0); /* in_additional_info */
|
|
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);
|
|
tevent_req_simple_finish_ntstatus(subreq, status);
|
|
}
|
|
|
|
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;
|
|
uint8_t buf[8] = {0};
|
|
DATA_BLOB inbuf = { .data = buf, .length = sizeof(buf) };
|
|
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;
|
|
}
|
|
|
|
SBVAL(buf, 0, newsize);
|
|
|
|
/* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
|
|
level 20 (SMB_FILE_END_OF_FILE_INFORMATION - 1000). */
|
|
|
|
status = cli_smb2_set_info_fnum(
|
|
cli,
|
|
fnum,
|
|
1, /* in_info_type */
|
|
SMB_FILE_END_OF_FILE_INFORMATION-1000, /* in_file_info_class */
|
|
&inbuf, /* in_input_buffer */
|
|
0);
|
|
|
|
fail:
|
|
|
|
cli->raw_status = status;
|
|
|
|
TALLOC_FREE(frame);
|
|
return status;
|
|
}
|
|
|
|
struct cli_smb2_notify_state {
|
|
struct tevent_req *subreq;
|
|
struct notify_change *changes;
|
|
size_t num_changes;
|
|
};
|
|
|
|
static void cli_smb2_notify_done(struct tevent_req *subreq);
|
|
static bool cli_smb2_notify_cancel(struct tevent_req *req);
|
|
|
|
struct tevent_req *cli_smb2_notify_send(
|
|
TALLOC_CTX *mem_ctx,
|
|
struct tevent_context *ev,
|
|
struct cli_state *cli,
|
|
uint16_t fnum,
|
|
uint32_t buffer_size,
|
|
uint32_t completion_filter,
|
|
bool recursive)
|
|
{
|
|
struct tevent_req *req = NULL;
|
|
struct cli_smb2_notify_state *state = NULL;
|
|
struct smb2_hnd *ph = NULL;
|
|
NTSTATUS status;
|
|
|
|
req = tevent_req_create(mem_ctx, &state,
|
|
struct cli_smb2_notify_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);
|
|
}
|
|
|
|
status = map_fnum_to_smb2_handle(cli, fnum, &ph);
|
|
if (tevent_req_nterror(req, status)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
|
|
state->subreq = smb2cli_notify_send(
|
|
state,
|
|
ev,
|
|
cli->conn,
|
|
cli->timeout,
|
|
cli->smb2.session,
|
|
cli->smb2.tcon,
|
|
buffer_size,
|
|
ph->fid_persistent,
|
|
ph->fid_volatile,
|
|
completion_filter,
|
|
recursive);
|
|
if (tevent_req_nomem(state->subreq, req)) {
|
|
return tevent_req_post(req, ev);
|
|
}
|
|
tevent_req_set_callback(state->subreq, cli_smb2_notify_done, req);
|
|
tevent_req_set_cancel_fn(req, cli_smb2_notify_cancel);
|
|
return req;
|
|
}
|
|
|
|
static bool cli_smb2_notify_cancel(struct tevent_req *req)
|
|
{
|
|
struct cli_smb2_notify_state *state = tevent_req_data(
|
|
req, struct cli_smb2_notify_state);
|
|
bool ok;
|
|
|
|
ok = tevent_req_cancel(state->subreq);
|
|
return ok;
|
|
}
|
|
|
|
static void cli_smb2_notify_done(struct tevent_req *subreq)
|
|
{
|
|
struct tevent_req *req = tevent_req_callback_data(
|
|
subreq, struct tevent_req);
|
|
struct cli_smb2_notify_state *state = tevent_req_data(
|
|
req, struct cli_smb2_notify_state);
|
|
uint8_t *base;
|
|
uint32_t len;
|
|
uint32_t ofs;
|
|
NTSTATUS status;
|
|
|
|
status = smb2cli_notify_recv(subreq, state, &base, &len);
|
|
TALLOC_FREE(subreq);
|
|
|
|
if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
|
|
tevent_req_done(req);
|
|
return;
|
|
}
|
|
if (tevent_req_nterror(req, status)) {
|
|
return;
|
|
}
|
|
|
|
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(
|
|
state,
|
|
state->changes,
|
|
struct notify_change,
|
|
state->num_changes + 1);
|
|
if (tevent_req_nomem(tmp, req)) {
|
|
return;
|
|
}
|
|
state->changes = tmp;
|
|
c = &state->changes[state->num_changes];
|
|
state->num_changes += 1;
|
|
|
|
if (smb_buffer_oob(len, ofs, next_ofs) ||
|
|
smb_buffer_oob(len, ofs+12, file_name_length)) {
|
|
tevent_req_nterror(
|
|
req, NT_STATUS_INVALID_NETWORK_RESPONSE);
|
|
return;
|
|
}
|
|
|
|
c->action = IVAL(base, ofs+4);
|
|
|
|
ok = convert_string_talloc(
|
|
state->changes,
|
|
CH_UTF16LE,
|
|
CH_UNIX,
|
|
base + ofs + 12,
|
|
file_name_length,
|
|
&c->name,
|
|
&namelen);
|
|
if (!ok) {
|
|
tevent_req_nterror(
|
|
req, NT_STATUS_INVALID_NETWORK_RESPONSE);
|
|
return;
|
|
}
|
|
|
|
if (next_ofs == 0) {
|
|
break;
|
|
}
|
|
ofs += next_ofs;
|
|
}
|
|
|
|
tevent_req_done(req);
|
|
}
|
|
|
|
NTSTATUS cli_smb2_notify_recv(struct tevent_req *req,
|
|
TALLOC_CTX *mem_ctx,
|
|
struct notify_change **pchanges,
|
|
uint32_t *pnum_changes)
|
|
{
|
|
struct cli_smb2_notify_state *state = tevent_req_data(
|
|
req, struct cli_smb2_notify_state);
|
|
NTSTATUS status;
|
|
|
|
if (tevent_req_is_nterror(req, &status)) {
|
|
return status;
|
|
}
|
|
*pchanges = talloc_move(mem_ctx, &state->changes);
|
|
*pnum_changes = state->num_changes;
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
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)
|
|
{
|
|
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_notify_send(
|
|
frame,
|
|
ev,
|
|
cli,
|
|
fnum,
|
|
buffer_size,
|
|
completion_filter,
|
|
recursive);
|
|
if (req == NULL) {
|
|
goto fail;
|
|
}
|
|
if (!tevent_req_poll_ntstatus(req, ev, &status)) {
|
|
goto fail;
|
|
}
|
|
status = cli_smb2_notify_recv(req, mem_ctx, pchanges, pnum_changes);
|
|
fail:
|
|
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_get_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);
|
|
struct cli_state *cli = state->cli;
|
|
NTSTATUS status;
|
|
|
|
status = smb2cli_ioctl_recv(subreq, state,
|
|
NULL,
|
|
&state->output_buffer);
|
|
TALLOC_FREE(subreq);
|
|
if (tevent_req_nterror(req, status)) {
|
|
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)) {
|
|
NTSTATUS status = state->cli->raw_status;
|
|
tevent_req_received(req);
|
|
return 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;
|
|
}
|