X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source3%2Flibsmb%2Fcli_smb2_fnum.c;h=795dc557c45821d0a2d6feb8c5781e5d01b19b8d;hb=99ef0ba048ce3b1a1052418708f98ddd47e76162;hp=21c03408893b7ef99706e937d3ba2a7104061bf5;hpb=f73bcf4934be89f83e86459bc695b7d28348565c;p=samba.git diff --git a/source3/libsmb/cli_smb2_fnum.c b/source3/libsmb/cli_smb2_fnum.c index 21c03408893..795dc557c45 100644 --- a/source3/libsmb/cli_smb2_fnum.c +++ b/source3/libsmb/cli_smb2_fnum.c @@ -37,8 +37,11 @@ #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" struct smb2_hnd { uint64_t fid_persistent; @@ -155,6 +158,8 @@ static uint8_t flags_to_smb2_oplock(uint32_t create_flags) 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; @@ -163,19 +168,27 @@ struct cli_smb2_create_fnum_state { static void cli_smb2_create_fnum_done(struct tevent_req *subreq); static bool cli_smb2_create_fnum_cancel(struct tevent_req *req); -struct tevent_req *cli_smb2_create_fnum_send(TALLOC_CTX *mem_ctx, - struct tevent_context *ev, - struct cli_state *cli, - const char *fname, - uint32_t create_flags, - uint32_t desired_access, - uint32_t file_attributes, - uint32_t share_access, - uint32_t create_disposition, - uint32_t create_options) +struct tevent_req *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); @@ -193,10 +206,68 @@ struct tevent_req *cli_smb2_create_fnum_send(TALLOC_CTX *mem_ctx, 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; inum_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, @@ -206,13 +277,13 @@ struct tevent_req *cli_smb2_create_fnum_send(TALLOC_CTX *mem_ctx, cli->smb2.tcon, fname, flags_to_smb2_oplock(create_flags), - SMB2_IMPERSONATION_IMPERSONATION, + impersonation_level, desired_access, file_attributes, share_access, create_disposition, create_options, - NULL); + &state->in_cblobs); if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); } @@ -233,8 +304,12 @@ static void cli_smb2_create_fnum_done(struct tevent_req *subreq) struct smb2_hnd h; NTSTATUS status; - status = smb2cli_create_recv(subreq, &h.fid_persistent, - &h.fid_volatile, &state->cr, NULL, NULL); + 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; @@ -254,14 +329,19 @@ static bool cli_smb2_create_fnum_cancel(struct tevent_req *req) return tevent_req_cancel(state->subreq); } -NTSTATUS cli_smb2_create_fnum_recv(struct tevent_req *req, uint16_t *pfnum, - struct smb_create_returns *cr) +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) { @@ -270,19 +350,32 @@ NTSTATUS cli_smb2_create_fnum_recv(struct tevent_req *req, uint16_t *pfnum, 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 desired_access, - uint32_t file_attributes, - uint32_t share_access, - uint32_t create_disposition, - uint32_t create_options, - uint16_t *pfid, - struct smb_create_returns *cr) +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; @@ -300,17 +393,26 @@ NTSTATUS cli_smb2_create_fnum(struct cli_state *cli, if (ev == NULL) { goto fail; } - req = cli_smb2_create_fnum_send(frame, ev, cli, fname, create_flags, - desired_access, file_attributes, - share_access, create_disposition, - create_options); + 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); + status = cli_smb2_create_fnum_recv(req, pfid, cr, mem_ctx, out_cblobs); fail: TALLOC_FREE(frame); return status; @@ -390,7 +492,15 @@ static void cli_smb2_close_fnum_done(struct tevent_req *subreq) NTSTATUS cli_smb2_close_fnum_recv(struct tevent_req *req) { - return tevent_req_simple_recv_ntstatus(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) @@ -424,6 +534,133 @@ NTSTATUS cli_smb2_close_fnum(struct cli_state *cli, uint16_t fnum) return status; } +struct cli_smb2_delete_on_close_state { + struct cli_state *cli; + uint16_t fnum; + struct smb2_hnd *ph; + uint8_t data[1]; + DATA_BLOB inbuf; +}; + +static void cli_smb2_delete_on_close_done(struct tevent_req *subreq); + +struct tevent_req *cli_smb2_delete_on_close_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + bool flag) +{ + struct tevent_req *req = NULL; + struct cli_smb2_delete_on_close_state *state = NULL; + struct tevent_req *subreq = NULL; + uint8_t in_info_type; + uint8_t in_file_info_class; + NTSTATUS status; + + req = tevent_req_create(mem_ctx, &state, + struct cli_smb2_delete_on_close_state); + if (req == NULL) { + return NULL; + } + state->cli = cli; + state->fnum = fnum; + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + + status = map_fnum_to_smb2_handle(cli, fnum, &state->ph); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + /* + * setinfo on the handle with info_type SMB2_SETINFO_FILE (1), + * level 13 (SMB_FILE_DISPOSITION_INFORMATION - 1000). + */ + in_info_type = 1; + in_file_info_class = SMB_FILE_DISPOSITION_INFORMATION - 1000; + /* Setup data array. */ + SCVAL(&state->data[0], 0, flag ? 1 : 0); + state->inbuf.data = &state->data[0]; + state->inbuf.length = 1; + + subreq = smb2cli_set_info_send(state, ev, + cli->conn, + cli->timeout, + cli->smb2.session, + cli->smb2.tcon, + in_info_type, + in_file_info_class, + &state->inbuf, /* in_input_buffer */ + 0, /* in_additional_info */ + state->ph->fid_persistent, + state->ph->fid_volatile); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, + cli_smb2_delete_on_close_done, + req); + return req; +} + +static void cli_smb2_delete_on_close_done(struct tevent_req *subreq) +{ + NTSTATUS status = smb2cli_set_info_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +NTSTATUS cli_smb2_delete_on_close_recv(struct tevent_req *req) +{ + struct cli_smb2_delete_on_close_state *state = + tevent_req_data(req, + struct cli_smb2_delete_on_close_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + state->cli->raw_status = status; + tevent_req_received(req); + return status; + } + + state->cli->raw_status = NT_STATUS_OK; + tevent_req_received(req); + return NT_STATUS_OK; +} + +NTSTATUS cli_smb2_delete_on_close(struct cli_state *cli, uint16_t fnum, bool flag) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = cli_smb2_delete_on_close_send(frame, ev, cli, fnum, flag); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_smb2_delete_on_close_recv(req); + fail: + TALLOC_FREE(frame); + return status; +} + /*************************************************************** Small wrapper that allows SMB2 to create a directory Synchronous only. @@ -448,12 +685,16 @@ NTSTATUS cli_smb2_mkdir(struct cli_state *cli, const char *dname) status = cli_smb2_create_fnum(cli, dname, 0, /* create_flags */ + SMB2_IMPERSONATION_IMPERSONATION, 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, &fnum, + NULL, + NULL, NULL); if (!NT_STATUS_IS_OK(status)) { @@ -462,42 +703,215 @@ NTSTATUS cli_smb2_mkdir(struct cli_state *cli, const char *dname) return cli_smb2_close_fnum(cli, fnum); } -/*************************************************************** - Small wrapper that allows SMB2 to delete a directory - Synchronous only. -***************************************************************/ +struct cli_smb2_rmdir_state { + struct tevent_context *ev; + struct cli_state *cli; + const char *dname; + uint16_t fnum; + NTSTATUS status; +}; -NTSTATUS cli_smb2_rmdir(struct cli_state *cli, const char *dname) +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) { - NTSTATUS status; - uint16_t fnum; + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_smb2_rmdir_state *state = 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; + 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; if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { - return NT_STATUS_INVALID_PARAMETER; + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); } - status = cli_smb2_create_fnum(cli, - dname, + 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 */ + NULL); /* 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, /* share_access */ + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, FILE_OPEN, /* create_disposition */ - FILE_DIRECTORY_FILE|FILE_DELETE_ON_CLOSE, /* create_options */ - &fnum, - NULL); + FILE_DIRECTORY_FILE| + FILE_DELETE_ON_CLOSE| + FILE_OPEN_REPARSE_POINT, /* create_options */ + NULL); /* in_cblobs */ + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_smb2_rmdir_opened2, req); + return; + } - if (!NT_STATUS_IS_OK(status)) { + 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) +{ + 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_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 cli_smb2_close_fnum(cli, fnum); + return state->status; +} + +NTSTATUS cli_smb2_rmdir(struct cli_state *cli, const char *dname) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + 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_rmdir_send(frame, ev, cli, dname); + if (req == NULL) { + goto fail; + } + ok = tevent_req_poll_ntstatus(req, ev, &status); + if (!ok) { + goto fail; + } + status = cli_smb2_rmdir_recv(req); +fail: + cli->raw_status = status; + TALLOC_FREE(frame); + return status; } /*************************************************************** @@ -524,13 +938,41 @@ NTSTATUS cli_smb2_unlink(struct cli_state *cli, const char *fname) status = cli_smb2_create_fnum(cli, fname, 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 */ + NULL, + &fnum, + NULL, + NULL, + NULL); + + if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) { + /* + * Naive option to match our SMB1 code. Assume the + * symlink path that tripped us up was the last + * component and try again. Eventually we will have to + * deal with the returned path unprocessed component. JRA. + */ + status = cli_smb2_create_fnum(cli, + fname, + 0, /* create_flags */ + 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 */ + NULL, &fnum, + NULL, + NULL, NULL); + } if (!NT_STATUS_IS_OK(status)) { return status; @@ -570,11 +1012,14 @@ static NTSTATUS parse_finfo_id_both_directory_info(uint8_t *dir_data, 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->mode = CVAL(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; @@ -669,6 +1114,9 @@ NTSTATUS cli_smb2_list(struct cli_state *cli, TALLOC_CTX *frame = talloc_stackframe(); TALLOC_CTX *subframe = NULL; bool mask_has_wild; + uint32_t max_trans; + uint32_t max_avail_len; + bool ok; if (smbXcli_conn_has_async_calls(cli->conn)) { /* @@ -697,12 +1145,16 @@ NTSTATUS cli_smb2_list(struct cli_state *cli, status = cli_smb2_create_fnum(cli, parent_dir, 0, /* create_flags */ + SMB2_IMPERSONATION_IMPERSONATION, 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, &fnum, + NULL, + NULL, NULL); if (!NT_STATUS_IS_OK(status)) { @@ -716,7 +1168,17 @@ NTSTATUS cli_smb2_list(struct cli_state *cli, goto fail; } - do { + /* + * ideally, use the max transaction size, but don't send a request + * bigger than we have credits available for + */ + 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); + } + + do { uint8_t *dir_data = NULL; uint32_t dir_data_length = 0; uint32_t next_offset = 0; @@ -732,7 +1194,7 @@ NTSTATUS cli_smb2_list(struct cli_state *cli, ph->fid_persistent, ph->fid_volatile, mask, - 0xffff, + max_trans, subframe, &dir_data, &dir_data_length); @@ -825,6 +1287,9 @@ NTSTATUS cli_smb2_list(struct cli_state *cli, if (fnum != 0xffff) { cli_smb2_close_fnum(cli, fnum); } + + cli->raw_status = status; + TALLOC_FREE(subframe); TALLOC_FREE(frame); return status; @@ -870,33 +1335,41 @@ NTSTATUS cli_smb2_qpathinfo_basic(struct cli_state *cli, 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); + &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); + &cr, + NULL, + NULL); } if (!NT_STATUS_IS_OK(status)) { return status; } - cli_smb2_close_fnum(cli, fnum); + status = cli_smb2_close_fnum(cli, fnum); ZERO_STRUCTP(sbuf); @@ -906,7 +1379,52 @@ NTSTATUS cli_smb2_qpathinfo_basic(struct cli_state *cli, sbuf->st_ex_size = cr.end_of_file; *attributes = cr.file_attributes; - return NT_STATUS_OK; + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to check if a path is a directory. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_chkpath(struct cli_state *cli, + const char *name) +{ + NTSTATUS status; + uint16_t fnum = 0xffff; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + return NT_STATUS_INVALID_PARAMETER; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + return NT_STATUS_INVALID_PARAMETER; + } + + /* Ensure this is a directory. */ + status = cli_smb2_create_fnum(cli, + name, + 0, /* create_flags */ + 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)) { + return status; + } + + return cli_smb2_close_fnum(cli, fnum); } /*************************************************************** @@ -921,6 +1439,7 @@ static NTSTATUS get_fnum_from_path(struct cli_state *cli, NTSTATUS status; size_t namelen = strlen(name); TALLOC_CTX *frame = talloc_stackframe(); + uint32_t create_options = 0; /* SMB2 is pickier about pathnames. Ensure it doesn't end in a '\' */ @@ -938,24 +1457,57 @@ static NTSTATUS get_fnum_from_path(struct cli_state *cli, status = cli_smb2_create_fnum(cli, name, 0, /* create_flags */ + SMB2_IMPERSONATION_IMPERSONATION, desired_access, 0, /* file attributes */ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */ FILE_OPEN, /* create_disposition */ - 0, /* create_options */ + create_options, + NULL, + pfnum, + NULL, + NULL, + NULL); + + if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) { + /* + * Naive option to match our SMB1 code. Assume the + * symlink path that tripped us up was the last + * component and try again. Eventually we will have to + * deal with the returned path unprocessed component. JRA. + */ + create_options |= FILE_OPEN_REPARSE_POINT; + status = cli_smb2_create_fnum(cli, + name, + 0, /* create_flags */ + SMB2_IMPERSONATION_IMPERSONATION, + desired_access, + 0, /* file attributes */ + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */ + FILE_OPEN, /* create_disposition */ + create_options, + NULL, pfnum, + NULL, + NULL, NULL); + } if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) { + create_options |= FILE_DIRECTORY_FILE; status = cli_smb2_create_fnum(cli, name, 0, /* create_flags */ + SMB2_IMPERSONATION_IMPERSONATION, 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, pfnum, + NULL, + NULL, NULL); } @@ -1072,6 +1624,9 @@ NTSTATUS cli_smb2_qpathinfo_alt_name(struct cli_state *cli, if (fnum != 0xffff) { cli_smb2_close_fnum(cli, fnum); } + + cli->raw_status = status; + TALLOC_FREE(frame); return status; } @@ -1171,6 +1726,8 @@ NTSTATUS cli_smb2_qfileinfo_basic(struct cli_state *cli, fail: + cli->raw_status = status; + TALLOC_FREE(frame); return status; } @@ -1202,6 +1759,8 @@ NTSTATUS cli_smb2_getattrE(struct cli_state *cli, &change_time_ts, NULL); + cli->raw_status = status; + if (!NT_STATUS_IS_OK(status)) { return status; } @@ -1279,6 +1838,8 @@ NTSTATUS cli_smb2_getatr(struct cli_state *cli, cli_smb2_close_fnum(cli, fnum); } + cli->raw_status = status; + TALLOC_FREE(frame); return status; } @@ -1349,6 +1910,8 @@ NTSTATUS cli_smb2_qpathinfo2(struct cli_state *cli, cli_smb2_close_fnum(cli, fnum); } + cli->raw_status = status; + TALLOC_FREE(frame); return status; } @@ -1437,25 +2000,27 @@ NTSTATUS cli_smb2_qpathinfo_streams(struct cli_state *cli, cli_smb2_close_fnum(cli, fnum); } + cli->raw_status = status; + TALLOC_FREE(frame); return status; } /*************************************************************** - Wrapper that allows SMB2 to set pathname attributes. + Wrapper that allows SMB2 to set SMB_FILE_BASIC_INFORMATION on + a pathname. Synchronous only. ***************************************************************/ -NTSTATUS cli_smb2_setatr(struct cli_state *cli, +NTSTATUS cli_smb2_setpathinfo(struct cli_state *cli, const char *name, - uint16_t attr, - time_t mtime) + uint8_t in_info_type, + uint8_t in_file_info_class, + const DATA_BLOB *p_in_data) { NTSTATUS status; uint16_t fnum = 0xffff; struct smb2_hnd *ph = NULL; - uint8_t inbuf_store[40]; - DATA_BLOB inbuf = data_blob_null; TALLOC_CTX *frame = talloc_stackframe(); if (smbXcli_conn_has_async_calls(cli->conn)) { @@ -1487,6 +2052,42 @@ NTSTATUS cli_smb2_setatr(struct cli_state *cli, goto fail; } + status = smb2cli_set_info(cli->conn, + cli->timeout, + cli->smb2.session, + cli->smb2.tcon, + in_info_type, + in_file_info_class, + p_in_data, /* in_input_buffer */ + 0, /* in_additional_info */ + ph->fid_persistent, + ph->fid_volatile); + fail: + + if (fnum != 0xffff) { + cli_smb2_close_fnum(cli, fnum); + } + + cli->raw_status = status; + + TALLOC_FREE(frame); + return status; +} + + +/*************************************************************** + Wrapper that allows SMB2 to set pathname attributes. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_setatr(struct cli_state *cli, + const char *name, + uint16_t attr, + time_t mtime) +{ + uint8_t inbuf_store[40]; + DATA_BLOB inbuf = data_blob_null; + /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1), level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */ @@ -1494,6 +2095,29 @@ NTSTATUS cli_smb2_setatr(struct cli_state *cli, inbuf.length = sizeof(inbuf_store); data_blob_clear(&inbuf); + /* + * SMB1 uses attr == 0 to clear all attributes + * on a file (end up with FILE_ATTRIBUTE_NORMAL), + * and attr == FILE_ATTRIBUTE_NORMAL to mean ignore + * request attribute change. + * + * SMB2 uses exactly the reverse. Unfortunately as the + * cli_setatr() ABI is exposed inside libsmbclient, + * we must make the SMB2 cli_smb2_setatr() call + * export the same ABI as the SMB1 cli_setatr() + * which calls it. This means reversing the sense + * of the requested attr argument if it's zero + * or FILE_ATTRIBUTE_NORMAL. + * + * See BUG: https://bugzilla.samba.org/show_bug.cgi?id=12899 + */ + + if (attr == 0) { + attr = FILE_ATTRIBUTE_NORMAL; + } else if (attr == FILE_ATTRIBUTE_NORMAL) { + attr = 0; + } + SSVAL(inbuf.data, 32, attr); if (mtime != 0) { put_long_date((char *)inbuf.data + 16,mtime); @@ -1503,26 +2127,15 @@ NTSTATUS cli_smb2_setatr(struct cli_state *cli, SBVAL(inbuf.data, 8, 0xFFFFFFFFFFFFFFFFLL); SBVAL(inbuf.data, 24, 0xFFFFFFFFFFFFFFFFLL); - status = smb2cli_set_info(cli->conn, - cli->timeout, - cli->smb2.session, - cli->smb2.tcon, + return cli_smb2_setpathinfo(cli, + name, 1, /* in_info_type */ - SMB_FILE_BASIC_INFORMATION - 1000, /* in_file_info_class */ - &inbuf, /* in_input_buffer */ - 0, /* in_additional_info */ - ph->fid_persistent, - ph->fid_volatile); - fail: - - if (fnum != 0xffff) { - cli_smb2_close_fnum(cli, fnum); - } - - TALLOC_FREE(frame); - return status; + /* in_file_info_class */ + SMB_FILE_BASIC_INFORMATION - 1000, + &inbuf); } + /*************************************************************** Wrapper that allows SMB2 to set file handle times. Synchronous only. @@ -1575,7 +2188,7 @@ NTSTATUS cli_smb2_setattrE(struct cli_state *cli, put_long_date((char *)inbuf.data + 16, write_time); } - return smb2cli_set_info(cli->conn, + cli->raw_status = smb2cli_set_info(cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon, @@ -1585,6 +2198,8 @@ NTSTATUS cli_smb2_setattrE(struct cli_state *cli, 0, /* in_additional_info */ ph->fid_persistent, ph->fid_volatile); + + return cli->raw_status; } /*************************************************************** @@ -1592,7 +2207,8 @@ NTSTATUS cli_smb2_setattrE(struct cli_state *cli, Synchronous only. ***************************************************************/ -NTSTATUS cli_smb2_dskattr(struct cli_state *cli, uint64_t *bsize, uint64_t *total, uint64_t *avail) +NTSTATUS cli_smb2_dskattr(struct cli_state *cli, const char *path, + uint64_t *bsize, uint64_t *total, uint64_t *avail) { NTSTATUS status; uint16_t fnum = 0xffff; @@ -1619,14 +2235,18 @@ NTSTATUS cli_smb2_dskattr(struct cli_state *cli, uint64_t *bsize, uint64_t *tota /* 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)) { @@ -1690,25 +2310,28 @@ NTSTATUS cli_smb2_dskattr(struct cli_state *cli, uint64_t *bsize, uint64_t *tota cli_smb2_close_fnum(cli, fnum); } + cli->raw_status = status; + TALLOC_FREE(frame); return status; } /*************************************************************** - Wrapper that allows SMB2 to query a security descriptor. + Wrapper that allows SMB2 to query file system sizes. Synchronous only. ***************************************************************/ -NTSTATUS cli_smb2_query_security_descriptor(struct cli_state *cli, - uint16_t fnum, - uint32_t sec_info, - TALLOC_CTX *mem_ctx, - struct security_descriptor **ppsd) +NTSTATUS cli_smb2_get_fs_full_size_info(struct cli_state *cli, + uint64_t *total_allocation_units, + uint64_t *caller_allocation_units, + uint64_t *actual_allocation_units, + uint64_t *sectors_per_allocation_unit, + uint64_t *bytes_per_sector) { NTSTATUS status; + uint16_t fnum = 0xffff; DATA_BLOB outbuf = data_blob_null; struct smb2_hnd *ph = NULL; - struct security_descriptor *lsd = NULL; TALLOC_CTX *frame = talloc_stackframe(); if (smbXcli_conn_has_async_calls(cli->conn)) { @@ -1724,68 +2347,86 @@ NTSTATUS cli_smb2_query_security_descriptor(struct cli_state *cli, goto fail; } - status = map_fnum_to_smb2_handle(cli, - fnum, - &ph); + /* First open the top level directory. */ + status = + cli_smb2_create_fnum(cli, "", 0, /* create_flags */ + 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_SEC (3) */ + status = map_fnum_to_smb2_handle(cli, fnum, &ph); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2), + level 7 (SMB_FS_FULL_SIZE_INFORMATION). */ status = smb2cli_query_info(cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon, - 3, /* in_info_type */ - 0, /* in_file_info_class */ + SMB2_GETINFO_FS, /* in_info_type */ + /* in_file_info_class */ + SMB_FS_FULL_SIZE_INFORMATION - 1000, 0xFFFF, /* in_max_output_length */ NULL, /* in_input_buffer */ - sec_info, /* in_additional_info */ + 0, /* in_additional_info */ 0, /* in_flags */ ph->fid_persistent, ph->fid_volatile, frame, &outbuf); - if (!NT_STATUS_IS_OK(status)) { goto fail; } - /* Parse the reply. */ - status = unmarshall_sec_desc(mem_ctx, - outbuf.data, - outbuf.length, - &lsd); - - if (!NT_STATUS_IS_OK(status)) { + if (outbuf.length < 32) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; goto fail; } - if (ppsd != NULL) { - *ppsd = lsd; - } else { - TALLOC_FREE(lsd); + *total_allocation_units = BIG_UINT(outbuf.data, 0); + *caller_allocation_units = BIG_UINT(outbuf.data, 8); + *actual_allocation_units = BIG_UINT(outbuf.data, 16); + *sectors_per_allocation_unit = (uint64_t)IVAL(outbuf.data, 24); + *bytes_per_sector = (uint64_t)IVAL(outbuf.data, 28); + +fail: + + if (fnum != 0xffff) { + cli_smb2_close_fnum(cli, fnum); } - fail: + cli->raw_status = status; TALLOC_FREE(frame); return status; } /*************************************************************** - Wrapper that allows SMB2 to set a security descriptor. + Wrapper that allows SMB2 to query file system attributes. Synchronous only. ***************************************************************/ -NTSTATUS cli_smb2_set_security_descriptor(struct cli_state *cli, - uint16_t fnum, - uint32_t sec_info, - const struct security_descriptor *sd) +NTSTATUS cli_smb2_get_fs_attr_info(struct cli_state *cli, uint32_t *fs_attr) { NTSTATUS status; - DATA_BLOB inbuf = data_blob_null; + uint16_t fnum = 0xffff; + DATA_BLOB outbuf = data_blob_null; struct smb2_hnd *ph = NULL; TALLOC_CTX *frame = talloc_stackframe(); @@ -1802,57 +2443,80 @@ NTSTATUS cli_smb2_set_security_descriptor(struct cli_state *cli, goto fail; } - status = map_fnum_to_smb2_handle(cli, - fnum, - &ph); + /* First open the top level directory. */ + status = + cli_smb2_create_fnum(cli, "", 0, /* create_flags */ + 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 = marshall_sec_desc(frame, - sd, - &inbuf.data, - &inbuf.length); + status = map_fnum_to_smb2_handle(cli, fnum, &ph); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } - if (!NT_STATUS_IS_OK(status)) { + status = smb2cli_query_info(cli->conn, cli->timeout, cli->smb2.session, + cli->smb2.tcon, 2, /* in_info_type */ + 5, /* in_file_info_class */ + 0xFFFF, /* in_max_output_length */ + NULL, /* in_input_buffer */ + 0, /* in_additional_info */ + 0, /* in_flags */ + ph->fid_persistent, ph->fid_volatile, frame, + &outbuf); + if (!NT_STATUS_IS_OK(status)) { goto fail; - } + } - /* setinfo on the returned handle with info_type SMB2_SETINFO_SEC (3) */ + if (outbuf.length < 12) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto fail; + } - status = smb2cli_set_info(cli->conn, - cli->timeout, - cli->smb2.session, - cli->smb2.tcon, - 3, /* in_info_type */ - 0, /* in_file_info_class */ - &inbuf, /* in_input_buffer */ - sec_info, /* in_additional_info */ - ph->fid_persistent, - ph->fid_volatile); + *fs_attr = IVAL(outbuf.data, 0); - fail: +fail: + + if (fnum != 0xffff) { + cli_smb2_close_fnum(cli, fnum); + } + + cli->raw_status = status; TALLOC_FREE(frame); return status; } /*************************************************************** - Wrapper that allows SMB2 to rename a file. + Wrapper that allows SMB2 to query file system volume info. Synchronous only. ***************************************************************/ -NTSTATUS cli_smb2_rename(struct cli_state *cli, - const char *fname_src, - const char *fname_dst) +NTSTATUS cli_smb2_get_fs_volume_info(struct cli_state *cli, + TALLOC_CTX *mem_ctx, + char **_volume_name, + uint32_t *pserial_number, + time_t *pdate) { NTSTATUS status; - DATA_BLOB inbuf = data_blob_null; uint16_t fnum = 0xffff; + DATA_BLOB outbuf = data_blob_null; struct smb2_hnd *ph = NULL; - smb_ucs2_t *converted_str = NULL; - size_t converted_size_bytes = 0; - size_t namelen = 0; + uint32_t nlen; + char *volume_name = NULL; TALLOC_CTX *frame = talloc_stackframe(); if (smbXcli_conn_has_async_calls(cli->conn)) { @@ -1868,30 +2532,311 @@ NTSTATUS cli_smb2_rename(struct cli_state *cli, goto fail; } - status = get_fnum_from_path(cli, - fname_src, - DELETE_ACCESS, - &fnum); + /* First open the top level directory. */ + status = + cli_smb2_create_fnum(cli, "", 0, /* create_flags */ + 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 = map_fnum_to_smb2_handle(cli, - fnum, - &ph); + status = map_fnum_to_smb2_handle(cli, fnum, &ph); if (!NT_STATUS_IS_OK(status)) { goto fail; } - /* SMB2 is pickier about pathnames. Ensure it doesn't - start in a '\' */ - if (*fname_dst == '\\') { - fname_dst++; + /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2), + level 1 (SMB_FS_VOLUME_INFORMATION). */ + + status = smb2cli_query_info(cli->conn, + cli->timeout, + cli->smb2.session, + cli->smb2.tcon, + SMB2_GETINFO_FS, /* in_info_type */ + /* in_file_info_class */ + SMB_FS_VOLUME_INFORMATION - 1000, + 0xFFFF, /* in_max_output_length */ + NULL, /* in_input_buffer */ + 0, /* in_additional_info */ + 0, /* in_flags */ + ph->fid_persistent, + ph->fid_volatile, + frame, + &outbuf); + if (!NT_STATUS_IS_OK(status)) { + goto fail; } - /* SMB2 is pickier about pathnames. Ensure it doesn't - end in a '\' */ + if (outbuf.length < 24) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto fail; + } + + if (pdate) { + struct timespec ts; + ts = interpret_long_date((char *)outbuf.data); + *pdate = ts.tv_sec; + } + if (pserial_number) { + *pserial_number = IVAL(outbuf.data,8); + } + nlen = IVAL(outbuf.data,12); + if (nlen + 18 < 18) { + /* Integer wrap. */ + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto fail; + } + /* + * The next check is safe as we know outbuf.length >= 24 + * from above. + */ + if (nlen > (outbuf.length - 18)) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto fail; + } + + clistr_pull_talloc(mem_ctx, + (const char *)outbuf.data, + 0, + &volume_name, + outbuf.data + 18, + nlen, + STR_UNICODE); + if (volume_name == NULL) { + status = map_nt_error_from_unix(errno); + goto fail; + } + + *_volume_name = volume_name; + +fail: + + if (fnum != 0xffff) { + cli_smb2_close_fnum(cli, fnum); + } + + cli->raw_status = status; + + TALLOC_FREE(frame); + return status; +} + + +/*************************************************************** + Wrapper that allows SMB2 to query a security descriptor. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_query_security_descriptor(struct cli_state *cli, + uint16_t fnum, + uint32_t sec_info, + TALLOC_CTX *mem_ctx, + struct security_descriptor **ppsd) +{ + NTSTATUS status; + DATA_BLOB outbuf = data_blob_null; + struct smb2_hnd *ph = NULL; + struct security_descriptor *lsd = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + status = map_fnum_to_smb2_handle(cli, + fnum, + &ph); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + /* getinfo on the returned handle with info_type SMB2_GETINFO_SEC (3) */ + + status = smb2cli_query_info(cli->conn, + cli->timeout, + cli->smb2.session, + cli->smb2.tcon, + 3, /* in_info_type */ + 0, /* in_file_info_class */ + 0xFFFF, /* in_max_output_length */ + NULL, /* in_input_buffer */ + sec_info, /* in_additional_info */ + 0, /* in_flags */ + ph->fid_persistent, + ph->fid_volatile, + frame, + &outbuf); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + /* Parse the reply. */ + status = unmarshall_sec_desc(mem_ctx, + outbuf.data, + outbuf.length, + &lsd); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + if (ppsd != NULL) { + *ppsd = lsd; + } else { + TALLOC_FREE(lsd); + } + + fail: + + cli->raw_status = status; + + TALLOC_FREE(frame); + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to set a security descriptor. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_set_security_descriptor(struct cli_state *cli, + uint16_t fnum, + uint32_t sec_info, + const struct security_descriptor *sd) +{ + NTSTATUS status; + DATA_BLOB inbuf = data_blob_null; + struct smb2_hnd *ph = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + status = map_fnum_to_smb2_handle(cli, + fnum, + &ph); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + status = marshall_sec_desc(frame, + sd, + &inbuf.data, + &inbuf.length); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + /* setinfo on the returned handle with info_type SMB2_SETINFO_SEC (3) */ + + status = smb2cli_set_info(cli->conn, + cli->timeout, + cli->smb2.session, + cli->smb2.tcon, + 3, /* in_info_type */ + 0, /* in_file_info_class */ + &inbuf, /* in_input_buffer */ + sec_info, /* in_additional_info */ + ph->fid_persistent, + ph->fid_volatile); + + fail: + + cli->raw_status = status; + + TALLOC_FREE(frame); + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to rename a file. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_rename(struct cli_state *cli, + const char *fname_src, + const char *fname_dst, + bool replace) +{ + NTSTATUS status; + DATA_BLOB inbuf = data_blob_null; + uint16_t fnum = 0xffff; + struct smb2_hnd *ph = NULL; + smb_ucs2_t *converted_str = NULL; + size_t converted_size_bytes = 0; + size_t namelen = 0; + TALLOC_CTX *frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + status = get_fnum_from_path(cli, + fname_src, + DELETE_ACCESS, + &fnum); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + status = map_fnum_to_smb2_handle(cli, + fnum, + &ph); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + /* SMB2 is pickier about pathnames. Ensure it doesn't + start in a '\' */ + if (*fname_dst == '\\') { + fname_dst++; + } + + /* SMB2 is pickier about pathnames. Ensure it doesn't + end in a '\' */ namelen = strlen(fname_dst); if (namelen > 0 && fname_dst[namelen-1] == '\\') { char *modname = talloc_strdup(frame, fname_dst); @@ -1924,6 +2869,10 @@ NTSTATUS cli_smb2_rename(struct cli_state *cli, goto fail; } + if (replace) { + SCVAL(inbuf.data, 0, 1); + } + SIVAL(inbuf.data, 16, converted_size_bytes); memcpy(inbuf.data + 20, converted_str, converted_size_bytes); @@ -1947,6 +2896,8 @@ NTSTATUS cli_smb2_rename(struct cli_state *cli, cli_smb2_close_fnum(cli, fnum); } + cli->raw_status = status; + TALLOC_FREE(frame); return status; } @@ -2040,6 +2991,8 @@ NTSTATUS cli_smb2_set_ea_fnum(struct cli_state *cli, fail: + cli->raw_status = status; + TALLOC_FREE(frame); return status; } @@ -2095,6 +3048,8 @@ NTSTATUS cli_smb2_set_ea_path(struct cli_state *cli, cli_smb2_close_fnum(cli, fnum); } + cli->raw_status = status; + return status; } @@ -2194,7 +3149,7 @@ NTSTATUS cli_smb2_get_ea_list_path(struct cli_state *cli, } ea_count = 0; for (eal = ea_list; eal; eal = eal->next) { - (*pea_array)[ea_count++] = ea_list->ea; + (*pea_array)[ea_count++] = eal->ea; } *pnum_eas = ea_count; } @@ -2205,32 +3160,382 @@ NTSTATUS cli_smb2_get_ea_list_path(struct cli_state *cli, cli_smb2_close_fnum(cli, fnum); } + cli->raw_status = status; + TALLOC_FREE(frame); return status; } -struct cli_smb2_read_state { - struct tevent_context *ev; - struct cli_state *cli; - struct smb2_hnd *ph; - uint64_t start_offset; - uint32_t size; - uint32_t received; - uint8_t *buf; -}; - -static void cli_smb2_read_done(struct tevent_req *subreq); +/*************************************************************** + Wrapper that allows SMB2 to get user quota. + Synchronous only. +***************************************************************/ -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 cli_smb2_get_user_quota(struct cli_state *cli, + int quota_fnum, + SMB_NTQUOTA_STRUCT *pqt) { NTSTATUS status; - struct tevent_req *req, *subreq; - struct cli_smb2_read_state *state; + DATA_BLOB inbuf = data_blob_null; + DATA_BLOB info_blob = data_blob_null; + DATA_BLOB outbuf = data_blob_null; + struct smb2_hnd *ph = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + unsigned sid_len; + unsigned int offset; + 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; + } + + status = map_fnum_to_smb2_handle(cli, quota_fnum, &ph); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + sid_len = ndr_size_dom_sid(&pqt->sid, 0); + + 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 = smb2cli_query_info(cli->conn, cli->timeout, cli->smb2.session, + cli->smb2.tcon, 4, /* in_info_type */ + 0, /* in_file_info_class */ + 0xFFFF, /* in_max_output_length */ + &inbuf, /* in_input_buffer */ + 0, /* in_additional_info */ + 0, /* in_flags */ + ph->fid_persistent, ph->fid_volatile, frame, + &outbuf); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + if (!parse_user_quota_record(outbuf.data, outbuf.length, &offset, + pqt)) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + DEBUG(0, ("Got invalid FILE_QUOTA_INFORMATION in reply.\n")); + } + +fail: + cli->raw_status = status; + + TALLOC_FREE(frame); + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to list user quota. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_list_user_quota_step(struct cli_state *cli, + TALLOC_CTX *mem_ctx, + int quota_fnum, + SMB_NTQUOTA_LIST **pqt_list, + bool first) +{ + NTSTATUS status; + DATA_BLOB inbuf = data_blob_null; + DATA_BLOB outbuf = data_blob_null; + struct smb2_hnd *ph = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + 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; + } + + status = map_fnum_to_smb2_handle(cli, quota_fnum, &ph); + if (!NT_STATUS_IS_OK(status)) { + 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 = smb2cli_query_info(cli->conn, cli->timeout, cli->smb2.session, + cli->smb2.tcon, 4, /* in_info_type */ + 0, /* in_file_info_class */ + 0xFFFF, /* in_max_output_length */ + &inbuf, /* in_input_buffer */ + 0, /* in_additional_info */ + 0, /* in_flags */ + ph->fid_persistent, ph->fid_volatile, frame, + &outbuf); + + /* + * 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; + struct smb2_hnd *ph = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto cleanup; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto cleanup; + } + + status = map_fnum_to_smb2_handle(cli, quota_fnum, &ph); + if (!NT_STATUS_IS_OK(status)) { + goto cleanup; + } + + status = smb2cli_query_info( + cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon, + 2, /* in_info_type */ + SMB_FS_QUOTA_INFORMATION - 1000, /* in_file_info_class */ + 0xFFFF, /* in_max_output_length */ + NULL, /* in_input_buffer */ + 0, /* in_additional_info */ + 0, /* in_flags */ + ph->fid_persistent, ph->fid_volatile, frame, &outbuf); + + if (!NT_STATUS_IS_OK(status)) { + goto cleanup; + } + + status = parse_fs_quota_buffer(outbuf.data, outbuf.length, pqt); + +cleanup: + cli->raw_status = status; + + TALLOC_FREE(frame); + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to set user quota. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_set_user_quota(struct cli_state *cli, + int quota_fnum, + SMB_NTQUOTA_LIST *qtl) +{ + NTSTATUS status; + DATA_BLOB inbuf = data_blob_null; + struct smb2_hnd *ph = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto cleanup; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto cleanup; + } + + status = map_fnum_to_smb2_handle(cli, quota_fnum, &ph); + if (!NT_STATUS_IS_OK(status)) { + goto cleanup; + } + + status = build_user_quota_buffer(qtl, 0, talloc_tos(), &inbuf, NULL); + if (!NT_STATUS_IS_OK(status)) { + goto cleanup; + } + + status = smb2cli_set_info(cli->conn, cli->timeout, cli->smb2.session, + cli->smb2.tcon, 4, /* in_info_type */ + 0, /* in_file_info_class */ + &inbuf, /* in_input_buffer */ + 0, /* in_additional_info */ + ph->fid_persistent, ph->fid_volatile); +cleanup: + + cli->raw_status = status; + + TALLOC_FREE(frame); + + return status; +} + +NTSTATUS cli_smb2_set_fs_quota_info(struct cli_state *cli, + int quota_fnum, + SMB_NTQUOTA_STRUCT *pqt) +{ + NTSTATUS status; + DATA_BLOB inbuf = data_blob_null; + struct smb2_hnd *ph = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto cleanup; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto cleanup; + } + + status = map_fnum_to_smb2_handle(cli, quota_fnum, &ph); + if (!NT_STATUS_IS_OK(status)) { + goto cleanup; + } + + status = build_fs_quota_buffer(talloc_tos(), pqt, &inbuf, 0); + if (!NT_STATUS_IS_OK(status)) { + goto cleanup; + } + + status = smb2cli_set_info( + cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon, + 2, /* in_info_type */ + SMB_FS_QUOTA_INFORMATION - 1000, /* in_file_info_class */ + &inbuf, /* in_input_buffer */ + 0, /* in_additional_info */ + ph->fid_persistent, ph->fid_volatile); +cleanup: + cli->raw_status = status; + + TALLOC_FREE(frame); + return status; +} + +struct cli_smb2_read_state { + struct tevent_context *ev; + struct cli_state *cli; + struct smb2_hnd *ph; + uint64_t start_offset; + uint32_t size; + uint32_t received; + uint8_t *buf; +}; + +static void cli_smb2_read_done(struct tevent_req *subreq); + +struct tevent_req *cli_smb2_read_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + off_t offset, + size_t size) +{ + NTSTATUS status; + struct tevent_req *req, *subreq; + struct cli_smb2_read_state *state; req = tevent_req_create(mem_ctx, &state, struct cli_smb2_read_state); if (req == NULL) { @@ -2301,6 +3606,7 @@ NTSTATUS cli_smb2_read_recv(struct tevent_req *req, req, struct cli_smb2_read_state); if (tevent_req_is_nterror(req, &status)) { + state->cli->raw_status = status; return status; } /* @@ -2310,6 +3616,7 @@ NTSTATUS cli_smb2_read_recv(struct tevent_req *req, */ *received = (ssize_t)state->received; *rcvbuf = state->buf; + state->cli->raw_status = NT_STATUS_OK; return NT_STATUS_OK; } @@ -2408,6 +3715,7 @@ NTSTATUS cli_smb2_write_recv(struct tevent_req *req, NTSTATUS status; if (tevent_req_is_nterror(req, &status)) { + state->cli->raw_status = status; tevent_req_received(req); return status; } @@ -2415,6 +3723,7 @@ NTSTATUS cli_smb2_write_recv(struct tevent_req *req, if (pwritten != NULL) { *pwritten = (size_t)state->written; } + state->cli->raw_status = NT_STATUS_OK; tevent_req_received(req); return NT_STATUS_OK; } @@ -2572,11 +3881,13 @@ NTSTATUS cli_smb2_writeall_recv(struct tevent_req *req, 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; } @@ -2837,12 +4148,706 @@ NTSTATUS cli_smb2_splice_recv(struct tevent_req *req, off_t *written) NTSTATUS status; if (tevent_req_is_nterror(req, &status)) { + state->cli->raw_status = status; tevent_req_received(req); return status; } if (written != NULL) { *written = state->written; } + state->cli->raw_status = NT_STATUS_OK; + tevent_req_received(req); + return NT_STATUS_OK; +} + +/*************************************************************** + SMB2 enum shadow copy data. +***************************************************************/ + +struct cli_smb2_shadow_copy_data_fnum_state { + struct cli_state *cli; + uint16_t fnum; + struct smb2_hnd *ph; + DATA_BLOB out_input_buffer; + DATA_BLOB out_output_buffer; +}; + +static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq); + +static struct tevent_req *cli_smb2_shadow_copy_data_fnum_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + bool get_names) +{ + struct tevent_req *req, *subreq; + struct cli_smb2_shadow_copy_data_fnum_state *state; + NTSTATUS status; + + req = tevent_req_create(mem_ctx, &state, + struct cli_smb2_shadow_copy_data_fnum_state); + if (req == NULL) { + return NULL; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + + state->cli = cli; + state->fnum = fnum; + + status = map_fnum_to_smb2_handle(cli, fnum, &state->ph); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + /* + * TODO. Under SMB2 we should send a zero max_output_length + * ioctl to get the required size, then send another ioctl + * to get the data, but the current SMB1 implementation just + * does one roundtrip with a 64K buffer size. Do the same + * for now. JRA. + */ + + subreq = smb2cli_ioctl_send(state, ev, state->cli->conn, + state->cli->timeout, + state->cli->smb2.session, + state->cli->smb2.tcon, + state->ph->fid_persistent, /* in_fid_persistent */ + state->ph->fid_volatile, /* in_fid_volatile */ + FSCTL_GET_SHADOW_COPY_DATA, + 0, /* in_max_input_length */ + NULL, /* in_input_buffer */ + get_names ? + CLI_BUFFER_SIZE : 16, /* in_max_output_length */ + NULL, /* in_output_buffer */ + SMB2_IOCTL_FLAG_IS_FSCTL); + + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, + cli_smb2_shadow_copy_data_fnum_done, + req); + + return req; +} + +static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_smb2_shadow_copy_data_fnum_state *state = tevent_req_data( + req, struct cli_smb2_shadow_copy_data_fnum_state); + NTSTATUS status; + + status = smb2cli_ioctl_recv(subreq, state, + &state->out_input_buffer, + &state->out_output_buffer); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_done(req); +} + +static NTSTATUS cli_smb2_shadow_copy_data_fnum_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + bool get_names, + char ***pnames, + int *pnum_names) +{ + struct cli_smb2_shadow_copy_data_fnum_state *state = tevent_req_data( + req, struct cli_smb2_shadow_copy_data_fnum_state); + char **names = NULL; + uint32_t num_names = 0; + uint32_t num_names_returned = 0; + uint32_t dlength = 0; + uint32_t i; + uint8_t *endp = NULL; + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + + if (state->out_output_buffer.length < 16) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + num_names = IVAL(state->out_output_buffer.data, 0); + num_names_returned = IVAL(state->out_output_buffer.data, 4); + dlength = IVAL(state->out_output_buffer.data, 8); + + if (num_names > 0x7FFFFFFF) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + if (get_names == false) { + *pnum_names = (int)num_names; + return NT_STATUS_OK; + } + if (num_names != num_names_returned) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + if (dlength + 12 < 12) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + /* + * NB. The below is an allowable return if there are + * more snapshots than the buffer size we told the + * server we can receive. We currently don't support + * this. + */ + if (dlength + 12 > state->out_output_buffer.length) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + if (state->out_output_buffer.length + + (2 * sizeof(SHADOW_COPY_LABEL)) < + state->out_output_buffer.length) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + names = talloc_array(mem_ctx, char *, num_names_returned); + if (names == NULL) { + return NT_STATUS_NO_MEMORY; + } + + endp = state->out_output_buffer.data + + state->out_output_buffer.length; + + for (i=0; iout_output_buffer.data + 12 + + (i * 2 * sizeof(SHADOW_COPY_LABEL)); + + if (src + (2 * sizeof(SHADOW_COPY_LABEL)) > endp) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + ret = convert_string_talloc( + names, CH_UTF16LE, CH_UNIX, + src, 2 * sizeof(SHADOW_COPY_LABEL), + &names[i], &converted_size); + if (!ret) { + TALLOC_FREE(names); + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + } + *pnum_names = num_names; + *pnames = names; + return NT_STATUS_OK; +} + +NTSTATUS cli_smb2_shadow_copy_data(TALLOC_CTX *mem_ctx, + struct cli_state *cli, + uint16_t fnum, + bool get_names, + char ***pnames, + int *pnum_names) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = cli_smb2_shadow_copy_data_fnum_send(frame, + ev, + cli, + fnum, + get_names); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_smb2_shadow_copy_data_fnum_recv(req, + mem_ctx, + get_names, + pnames, + pnum_names); + fail: + cli->raw_status = status; + + TALLOC_FREE(frame); + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to truncate a file. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_ftruncate(struct cli_state *cli, + uint16_t fnum, + uint64_t newsize) +{ + NTSTATUS status; + DATA_BLOB inbuf = data_blob_null; + struct smb2_hnd *ph = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + status = map_fnum_to_smb2_handle(cli, + fnum, + &ph); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + inbuf = data_blob_talloc_zero(frame, 8); + if (inbuf.data == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + SBVAL(inbuf.data, 0, newsize); + + /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1), + level 20 (SMB_FILE_END_OF_FILE_INFORMATION - 1000). */ + + status = smb2cli_set_info(cli->conn, + cli->timeout, + cli->smb2.session, + cli->smb2.tcon, + 1, /* in_info_type */ + /* in_file_info_class */ + SMB_FILE_END_OF_FILE_INFORMATION - 1000, + &inbuf, /* in_input_buffer */ + 0, /* in_additional_info */ + ph->fid_persistent, + ph->fid_volatile); + + fail: + + cli->raw_status = status; + + TALLOC_FREE(frame); + return status; +} + +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_set_reparse_point_fnum_state *state = NULL; + NTSTATUS status; + + req = tevent_req_create(mem_ctx, &state, + struct cli_smb2_get_reparse_point_fnum_state); + if (req == NULL) { + return NULL; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + + state->cli = cli; + state->fnum = fnum; + + status = map_fnum_to_smb2_handle(cli, fnum, &state->ph); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + subreq = smb2cli_ioctl_send(state, ev, state->cli->conn, + state->cli->timeout, + state->cli->smb2.session, + state->cli->smb2.tcon, + state->ph->fid_persistent, /* in_fid_persistent */ + state->ph->fid_volatile, /* in_fid_volatile */ + FSCTL_GET_REPARSE_POINT, + 0, /* in_max_input_length */ + NULL, + 64*1024, + NULL, + SMB2_IOCTL_FLAG_IS_FSCTL); + + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, + cli_smb2_get_reparse_point_fnum_done, + req); + + return req; +} + +static void cli_smb2_get_reparse_point_fnum_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_smb2_get_reparse_point_fnum_state *state = tevent_req_data( + req, struct cli_smb2_get_reparse_point_fnum_state); + NTSTATUS status; + + status = smb2cli_ioctl_recv(subreq, state, + NULL, + &state->output_buffer); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + state->cli->raw_status = status; + return; + } + tevent_req_done(req); +} + +NTSTATUS cli_smb2_get_reparse_point_fnum_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + DATA_BLOB *output) +{ + struct cli_smb2_get_reparse_point_fnum_state *state = tevent_req_data( + req, struct cli_smb2_get_reparse_point_fnum_state); + + if (tevent_req_is_nterror(req, &state->cli->raw_status)) { + tevent_req_received(req); + return state->cli->raw_status; + } + *output = data_blob_dup_talloc(mem_ctx, state->output_buffer); + if (output->data == NULL) { + tevent_req_received(req); + return NT_STATUS_NO_MEMORY; + } tevent_req_received(req); return NT_STATUS_OK; }