/* 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 . */ /* 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 "smb2cli.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 "lib/util_ea.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. Synchronous only. ***************************************************************/ 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 smb2_create_returns *cr) { NTSTATUS status; struct smb2_hnd h; 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; } if (cli->backup_intent) { create_options |= FILE_OPEN_FOR_BACKUP_INTENT; } /* SMB2 is pickier about pathnames. Ensure it doesn't start in a '\' */ if (*fname == '\\') { fname++; } status = smb2cli_create(cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon, fname, flags_to_smb2_oplock(create_flags), SMB2_IMPERSONATION_IMPERSONATION, desired_access, file_attributes, share_access, create_disposition, create_options, NULL, &h.fid_persistent, &h.fid_volatile, cr); if (NT_STATUS_IS_OK(status)) { status = map_smb2_handle_to_fnum(cli, &h, pfid); } return status; } /*************************************************************** Small wrapper that allows SMB2 close to use a uint16_t fnum. Synchronous only. ***************************************************************/ NTSTATUS cli_smb2_close_fnum(struct cli_state *cli, uint16_t fnum) { struct smb2_hnd *ph = NULL; NTSTATUS status; if (smbXcli_conn_has_async_calls(cli->conn)) { /* * Can't use sync call while an async call is in flight */ return NT_STATUS_INVALID_PARAMETER; } if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { return NT_STATUS_INVALID_PARAMETER; } status = map_fnum_to_smb2_handle(cli, fnum, &ph); if (!NT_STATUS_IS_OK(status)) { return status; } status = smb2cli_close(cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon, 0, ph->fid_persistent, ph->fid_volatile); /* Delete the fnum -> handle mapping. */ if (NT_STATUS_IS_OK(status)) { status = delete_smb2_handle_mapping(cli, &ph, fnum); } return status; } /*************************************************************** Small wrapper that allows SMB2 to create a directory Synchronous only. ***************************************************************/ NTSTATUS cli_smb2_mkdir(struct cli_state *cli, const char *dname) { NTSTATUS status; uint16_t fnum; if (smbXcli_conn_has_async_calls(cli->conn)) { /* * Can't use sync call while an async call is in flight */ return NT_STATUS_INVALID_PARAMETER; } if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { return NT_STATUS_INVALID_PARAMETER; } status = cli_smb2_create_fnum(cli, dname, 0, /* create_flags */ FILE_READ_ATTRIBUTES, /* desired_access */ FILE_ATTRIBUTE_DIRECTORY, /* file attributes */ FILE_SHARE_READ|FILE_SHARE_WRITE, /* share_access */ FILE_CREATE, /* create_disposition */ FILE_DIRECTORY_FILE, /* create_options */ &fnum, NULL); if (!NT_STATUS_IS_OK(status)) { return status; } return cli_smb2_close_fnum(cli, fnum); } /*************************************************************** Small wrapper that allows SMB2 to delete a directory Synchronous only. ***************************************************************/ NTSTATUS cli_smb2_rmdir(struct cli_state *cli, const char *dname) { NTSTATUS status; uint16_t fnum; if (smbXcli_conn_has_async_calls(cli->conn)) { /* * Can't use sync call while an async call is in flight */ return NT_STATUS_INVALID_PARAMETER; } if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { return NT_STATUS_INVALID_PARAMETER; } status = cli_smb2_create_fnum(cli, dname, 0, /* create_flags */ DELETE_ACCESS, /* desired_access */ FILE_ATTRIBUTE_DIRECTORY, /* file attributes */ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */ FILE_OPEN, /* create_disposition */ FILE_DIRECTORY_FILE|FILE_DELETE_ON_CLOSE, /* create_options */ &fnum, NULL); if (!NT_STATUS_IS_OK(status)) { return status; } return cli_smb2_close_fnum(cli, fnum); } /*************************************************************** Small wrapper that allows SMB2 to unlink a pathname. Synchronous only. ***************************************************************/ NTSTATUS cli_smb2_unlink(struct cli_state *cli, const char *fname) { NTSTATUS status; uint16_t fnum; if (smbXcli_conn_has_async_calls(cli->conn)) { /* * Can't use sync call while an async call is in flight */ return NT_STATUS_INVALID_PARAMETER; } if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { return NT_STATUS_INVALID_PARAMETER; } status = cli_smb2_create_fnum(cli, fname, 0, /* create_flags */ DELETE_ACCESS, /* desired_access */ FILE_ATTRIBUTE_NORMAL, /* file attributes */ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */ FILE_OPEN, /* create_disposition */ FILE_DELETE_ON_CLOSE, /* create_options */ &fnum, NULL); if (!NT_STATUS_IS_OK(status)) { return status; } return cli_smb2_close_fnum(cli, fnum); } /*************************************************************** Utility function to parse a SMB2_FIND_ID_BOTH_DIRECTORY_INFO reply. ***************************************************************/ static NTSTATUS parse_finfo_id_both_directory_info(uint8_t *dir_data, uint32_t dir_data_length, struct file_info *finfo, uint32_t *next_offset) { size_t namelen = 0; size_t slen = 0; size_t ret = 0; if (dir_data_length < 4) { return NT_STATUS_INFO_LENGTH_MISMATCH; } *next_offset = IVAL(dir_data, 0); if (*next_offset > dir_data_length) { return NT_STATUS_INFO_LENGTH_MISMATCH; } if (*next_offset != 0) { /* Ensure we only read what in this record. */ dir_data_length = *next_offset; } if (dir_data_length < 105) { return NT_STATUS_INFO_LENGTH_MISMATCH; } finfo->atime_ts = interpret_long_date((const char *)dir_data + 16); finfo->mtime_ts = interpret_long_date((const char *)dir_data + 24); finfo->ctime_ts = interpret_long_date((const char *)dir_data + 32); finfo->size = IVAL2_TO_SMB_BIG_UINT(dir_data + 40, 0); finfo->mode = CVAL(dir_data + 56, 0); namelen = IVAL(dir_data + 60,0); if (namelen > (dir_data_length - 104)) { return NT_STATUS_INFO_LENGTH_MISMATCH; } slen = CVAL(dir_data + 68, 0); if (slen > 24) { return NT_STATUS_INFO_LENGTH_MISMATCH; } ret = pull_string_talloc(finfo, dir_data, FLAGS2_UNICODE_STRINGS, &finfo->short_name, dir_data + 70, slen, STR_UNICODE); if (ret == (size_t)-1) { /* Bad conversion. */ return NT_STATUS_INVALID_NETWORK_RESPONSE; } ret = pull_string_talloc(finfo, dir_data, FLAGS2_UNICODE_STRINGS, &finfo->name, dir_data + 104, namelen, STR_UNICODE); if (ret == (size_t)-1) { /* Bad conversion. */ return NT_STATUS_INVALID_NETWORK_RESPONSE; } return NT_STATUS_OK; } /******************************************************************* Given a filename - get its directory name ********************************************************************/ static bool windows_parent_dirname(TALLOC_CTX *mem_ctx, const char *dir, char **parent, const char **name) { char *p; ptrdiff_t len; p = strrchr_m(dir, '\\'); /* Find final '\\', if any */ if (p == NULL) { if (!(*parent = talloc_strdup(mem_ctx, "\\"))) { return false; } if (name) { *name = dir; } return true; } len = p-dir; if (!(*parent = (char *)talloc_memdup(mem_ctx, dir, len+1))) { return false; } (*parent)[len] = '\0'; if (name) { *name = p+1; } return true; } /*************************************************************** Wrapper that allows SMB2 to list a directory. Synchronous only. ***************************************************************/ NTSTATUS cli_smb2_list(struct cli_state *cli, const char *pathname, NTSTATUS (*fn)(const char *, struct file_info *, const char *, void *), void *state) { NTSTATUS status; uint16_t fnum = 0xffff; char *parent_dir = NULL; const char *mask = NULL; struct smb2_hnd *ph = NULL; TALLOC_CTX *frame = talloc_stackframe(); TALLOC_CTX *subframe = 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; } /* Get the directory name. */ if (!windows_parent_dirname(frame, pathname, &parent_dir, &mask)) { status = NT_STATUS_NO_MEMORY; goto fail; } status = cli_smb2_create_fnum(cli, parent_dir, 0, /* create_flags */ SEC_DIR_LIST|SEC_DIR_READ_ATTRIBUTE,/* desired_access */ FILE_ATTRIBUTE_DIRECTORY, /* file attributes */ FILE_SHARE_READ|FILE_SHARE_WRITE, /* share_access */ FILE_OPEN, /* create_disposition */ FILE_DIRECTORY_FILE, /* create_options */ &fnum, NULL); if (!NT_STATUS_IS_OK(status)) { goto fail; } status = map_fnum_to_smb2_handle(cli, fnum, &ph); if (!NT_STATUS_IS_OK(status)) { goto fail; } do { uint8_t *dir_data = NULL; uint32_t dir_data_length = 0; uint32_t next_offset = 0; subframe = talloc_stackframe(); status = smb2cli_query_directory(cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon, SMB2_FIND_ID_BOTH_DIRECTORY_INFO, 0, /* flags */ 0, /* file_index */ ph->fid_persistent, ph->fid_volatile, mask, 0xffff, subframe, &dir_data, &dir_data_length); if (!NT_STATUS_IS_OK(status)) { if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) { break; } goto fail; } do { struct file_info *finfo = talloc_zero(subframe, struct file_info); if (finfo == NULL) { status = NT_STATUS_NO_MEMORY; goto fail; } status = parse_finfo_id_both_directory_info(dir_data, dir_data_length, finfo, &next_offset); if (!NT_STATUS_IS_OK(status)) { goto fail; } status = fn(cli->dfs_mountpoint, finfo, pathname, state); if (!NT_STATUS_IS_OK(status)) { break; } TALLOC_FREE(finfo); /* Move to next entry. */ if (next_offset) { dir_data += next_offset; dir_data_length -= next_offset; } } while (next_offset != 0); TALLOC_FREE(subframe); } while (NT_STATUS_IS_OK(status)); if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) { status = NT_STATUS_OK; } fail: if (fnum != 0xffff) { cli_smb2_close_fnum(cli, fnum); } TALLOC_FREE(subframe); TALLOC_FREE(frame); return status; } /*************************************************************** Wrapper that allows SMB2 to query a path info (basic level). Synchronous only. ***************************************************************/ NTSTATUS cli_smb2_qpathinfo_basic(struct cli_state *cli, const char *name, SMB_STRUCT_STAT *sbuf, uint32_t *attributes) { NTSTATUS status; struct smb2_create_returns cr; uint16_t fnum = 0xffff; size_t namelen = strlen(name); if (smbXcli_conn_has_async_calls(cli->conn)) { /* * Can't use sync call while an async call is in flight */ return NT_STATUS_INVALID_PARAMETER; } if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { return NT_STATUS_INVALID_PARAMETER; } /* SMB2 is pickier about pathnames. Ensure it doesn't end in a '\' */ if (namelen > 0 && name[namelen-1] == '\\') { char *modname = talloc_strdup(talloc_tos(), name); modname[namelen-1] = '\0'; name = modname; } /* This is commonly used as a 'cd'. Try qpathinfo on a directory handle first. */ status = cli_smb2_create_fnum(cli, name, 0, /* create_flags */ FILE_READ_ATTRIBUTES, /* desired_access */ FILE_ATTRIBUTE_DIRECTORY, /* file attributes */ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */ FILE_OPEN, /* create_disposition */ FILE_DIRECTORY_FILE, /* create_options */ &fnum, &cr); if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_DIRECTORY)) { /* Maybe a file ? */ status = cli_smb2_create_fnum(cli, name, 0, /* create_flags */ FILE_READ_ATTRIBUTES, /* desired_access */ 0, /* file attributes */ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */ FILE_OPEN, /* create_disposition */ 0, /* create_options */ &fnum, &cr); } if (!NT_STATUS_IS_OK(status)) { return status; } 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 NT_STATUS_OK; } /*************************************************************** Helper function for pathname operations. ***************************************************************/ static NTSTATUS get_fnum_from_path(struct cli_state *cli, const char *name, uint32_t desired_access, uint16_t *pfnum) { NTSTATUS status; size_t namelen = strlen(name); TALLOC_CTX *frame = talloc_stackframe(); /* SMB2 is pickier about pathnames. Ensure it doesn't end in a '\' */ if (namelen > 0 && name[namelen-1] == '\\') { char *modname = talloc_strdup(frame, name); if (modname == NULL) { status = NT_STATUS_NO_MEMORY; goto fail; } modname[namelen-1] = '\0'; name = modname; } /* Try to open a file handle first. */ status = cli_smb2_create_fnum(cli, name, 0, /* create_flags */ desired_access, 0, /* file attributes */ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */ FILE_OPEN, /* create_disposition */ 0, /* create_options */ pfnum, NULL); if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) { status = cli_smb2_create_fnum(cli, name, 0, /* create_flags */ desired_access, FILE_ATTRIBUTE_DIRECTORY, /* file attributes */ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */ FILE_OPEN, /* create_disposition */ FILE_DIRECTORY_FILE, /* create_options */ pfnum, NULL); } fail: TALLOC_FREE(frame); return status; } /*************************************************************** Wrapper that allows SMB2 to query a path info (ALTNAME level). Synchronous only. ***************************************************************/ NTSTATUS cli_smb2_qpathinfo_alt_name(struct cli_state *cli, const char *name, fstring alt_name) { NTSTATUS status; DATA_BLOB outbuf = data_blob_null; uint16_t fnum = 0xffff; struct smb2_hnd *ph = NULL; uint32_t altnamelen = 0; TALLOC_CTX *frame = talloc_stackframe(); if (smbXcli_conn_has_async_calls(cli->conn)) { /* * Can't use sync call while an async call is in flight */ status = NT_STATUS_INVALID_PARAMETER; goto fail; } if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { status = NT_STATUS_INVALID_PARAMETER; goto fail; } status = get_fnum_from_path(cli, name, FILE_READ_ATTRIBUTES, &fnum); if (!NT_STATUS_IS_OK(status)) { goto fail; } status = map_fnum_to_smb2_handle(cli, fnum, &ph); if (!NT_STATUS_IS_OK(status)) { goto fail; } /* getinfo on the handle with info_type SMB2_GETINFO_FILE (1), level SMB_FILE_ALTERNATE_NAME_INFORMATION (1021) == SMB2 21 */ status = smb2cli_query_info(cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon, 1, /* in_info_type */ (SMB_FILE_ALTERNATE_NAME_INFORMATION - 1000), /* in_file_info_class */ 0xFFFF, /* in_max_output_length */ NULL, /* in_input_buffer */ 0, /* in_additional_info */ 0, /* in_flags */ ph->fid_persistent, ph->fid_volatile, frame, &outbuf); if (!NT_STATUS_IS_OK(status)) { goto fail; } /* Parse the reply. */ if (outbuf.length < 4) { status = NT_STATUS_INVALID_NETWORK_RESPONSE; goto fail; } altnamelen = IVAL(outbuf.data, 0); if (altnamelen > outbuf.length - 4) { status = NT_STATUS_INVALID_NETWORK_RESPONSE; goto fail; } if (altnamelen > 0) { size_t ret = 0; char *short_name = NULL; ret = pull_string_talloc(frame, outbuf.data, FLAGS2_UNICODE_STRINGS, &short_name, outbuf.data + 4, altnamelen, STR_UNICODE); if (ret == (size_t)-1) { /* Bad conversion. */ status = NT_STATUS_INVALID_NETWORK_RESPONSE; goto fail; } fstrcpy(alt_name, short_name); } else { alt_name[0] = '\0'; } status = NT_STATUS_OK; fail: if (fnum != 0xffff) { cli_smb2_close_fnum(cli, fnum); } TALLOC_FREE(frame); return status; } /*************************************************************** Wrapper that allows SMB2 to query a fnum info (basic level). Synchronous only. ***************************************************************/ NTSTATUS cli_smb2_qfileinfo_basic(struct cli_state *cli, uint16_t fnum, uint16_t *mode, off_t *size, struct timespec *create_time, struct timespec *access_time, struct timespec *write_time, struct timespec *change_time, SMB_INO_T *ino) { NTSTATUS status; DATA_BLOB outbuf = data_blob_null; struct smb2_hnd *ph = NULL; TALLOC_CTX *frame = talloc_stackframe(); if (smbXcli_conn_has_async_calls(cli->conn)) { /* * Can't use sync call while an async call is in flight */ status = NT_STATUS_INVALID_PARAMETER; goto fail; } if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { status = NT_STATUS_INVALID_PARAMETER; goto fail; } status = map_fnum_to_smb2_handle(cli, fnum, &ph); if (!NT_STATUS_IS_OK(status)) { goto fail; } /* getinfo on the handle with info_type SMB2_GETINFO_FILE (1), level 0x12 (SMB2_FILE_ALL_INFORMATION). */ status = smb2cli_query_info(cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon, 1, /* in_info_type */ (SMB_FILE_ALL_INFORMATION - 1000), /* in_file_info_class */ 0xFFFF, /* in_max_output_length */ NULL, /* in_input_buffer */ 0, /* in_additional_info */ 0, /* in_flags */ ph->fid_persistent, ph->fid_volatile, frame, &outbuf); if (!NT_STATUS_IS_OK(status)) { goto fail; } /* Parse the reply. */ if (outbuf.length < 0x60) { status = NT_STATUS_INVALID_NETWORK_RESPONSE; goto fail; } if (create_time) { *create_time = interpret_long_date((const char *)outbuf.data + 0x0); } if (access_time) { *access_time = interpret_long_date((const char *)outbuf.data + 0x8); } if (write_time) { *write_time = interpret_long_date((const char *)outbuf.data + 0x10); } if (change_time) { *change_time = interpret_long_date((const char *)outbuf.data + 0x18); } if (mode) { uint32_t attr = IVAL(outbuf.data, 0x20); *mode = (uint16_t)attr; } if (size) { uint64_t file_size = BVAL(outbuf.data, 0x30); *size = (off_t)file_size; } if (ino) { uint64_t file_index = BVAL(outbuf.data, 0x40); *ino = (SMB_INO_T)file_index; } fail: TALLOC_FREE(frame); return status; } /*************************************************************** Wrapper that allows SMB2 to query an fnum. Implement on top of cli_smb2_qfileinfo_basic(). Synchronous only. ***************************************************************/ NTSTATUS cli_smb2_getattrE(struct cli_state *cli, uint16_t fnum, uint16_t *attr, off_t *size, time_t *change_time, time_t *access_time, time_t *write_time) { struct timespec access_time_ts; struct timespec write_time_ts; struct timespec change_time_ts; NTSTATUS status = cli_smb2_qfileinfo_basic(cli, fnum, attr, size, NULL, &access_time_ts, &write_time_ts, &change_time_ts, NULL); if (!NT_STATUS_IS_OK(status)) { return status; } if (change_time) { *change_time = change_time_ts.tv_sec; } if (access_time) { *access_time = access_time_ts.tv_sec; } if (write_time) { *write_time = write_time_ts.tv_sec; } return NT_STATUS_OK; } /*************************************************************** Wrapper that allows SMB2 to get pathname attributes. Synchronous only. ***************************************************************/ NTSTATUS cli_smb2_getatr(struct cli_state *cli, const char *name, uint16_t *attr, off_t *size, time_t *write_time) { NTSTATUS status; uint16_t fnum = 0xffff; struct smb2_hnd *ph = NULL; TALLOC_CTX *frame = talloc_stackframe(); if (smbXcli_conn_has_async_calls(cli->conn)) { /* * Can't use sync call while an async call is in flight */ status = NT_STATUS_INVALID_PARAMETER; goto fail; } if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { status = NT_STATUS_INVALID_PARAMETER; goto fail; } status = get_fnum_from_path(cli, name, FILE_READ_ATTRIBUTES, &fnum); if (!NT_STATUS_IS_OK(status)) { goto fail; } status = map_fnum_to_smb2_handle(cli, fnum, &ph); if (!NT_STATUS_IS_OK(status)) { goto fail; } status = cli_smb2_getattrE(cli, fnum, attr, size, NULL, NULL, write_time); if (!NT_STATUS_IS_OK(status)) { goto fail; } fail: if (fnum != 0xffff) { cli_smb2_close_fnum(cli, fnum); } TALLOC_FREE(frame); return status; } /*************************************************************** Wrapper that allows SMB2 to query a pathname info (basic level). Implement on top of cli_smb2_qfileinfo_basic(). Synchronous only. ***************************************************************/ NTSTATUS cli_smb2_qpathinfo2(struct cli_state *cli, const char *name, struct timespec *create_time, struct timespec *access_time, struct timespec *write_time, struct timespec *change_time, off_t *size, uint16_t *mode, SMB_INO_T *ino) { NTSTATUS status; struct smb2_hnd *ph = NULL; uint16_t fnum = 0xffff; TALLOC_CTX *frame = talloc_stackframe(); if (smbXcli_conn_has_async_calls(cli->conn)) { /* * Can't use sync call while an async call is in flight */ status = NT_STATUS_INVALID_PARAMETER; goto fail; } if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { status = NT_STATUS_INVALID_PARAMETER; goto fail; } status = get_fnum_from_path(cli, name, FILE_READ_ATTRIBUTES, &fnum); if (!NT_STATUS_IS_OK(status)) { goto fail; } status = map_fnum_to_smb2_handle(cli, fnum, &ph); if (!NT_STATUS_IS_OK(status)) { goto fail; } status = cli_smb2_qfileinfo_basic(cli, fnum, mode, size, create_time, access_time, write_time, change_time, ino); fail: if (fnum != 0xffff) { cli_smb2_close_fnum(cli, fnum); } TALLOC_FREE(frame); return status; } /*************************************************************** Wrapper that allows SMB2 to query pathname streams. Synchronous only. ***************************************************************/ NTSTATUS cli_smb2_qpathinfo_streams(struct cli_state *cli, const char *name, TALLOC_CTX *mem_ctx, unsigned int *pnum_streams, struct stream_struct **pstreams) { NTSTATUS status; struct smb2_hnd *ph = NULL; uint16_t fnum = 0xffff; DATA_BLOB outbuf = data_blob_null; TALLOC_CTX *frame = talloc_stackframe(); if (smbXcli_conn_has_async_calls(cli->conn)) { /* * Can't use sync call while an async call is in flight */ status = NT_STATUS_INVALID_PARAMETER; goto fail; } if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { status = NT_STATUS_INVALID_PARAMETER; goto fail; } status = get_fnum_from_path(cli, name, FILE_READ_ATTRIBUTES, &fnum); if (!NT_STATUS_IS_OK(status)) { goto fail; } status = map_fnum_to_smb2_handle(cli, fnum, &ph); if (!NT_STATUS_IS_OK(status)) { goto fail; } /* getinfo on the handle with info_type SMB2_GETINFO_FILE (1), level 22 (SMB2_FILE_STREAM_INFORMATION). */ status = smb2cli_query_info(cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon, 1, /* in_info_type */ (SMB_FILE_STREAM_INFORMATION - 1000), /* in_file_info_class */ 0xFFFF, /* in_max_output_length */ NULL, /* in_input_buffer */ 0, /* in_additional_info */ 0, /* in_flags */ ph->fid_persistent, ph->fid_volatile, frame, &outbuf); if (!NT_STATUS_IS_OK(status)) { goto fail; } /* Parse the reply. */ if (!parse_streams_blob(mem_ctx, outbuf.data, outbuf.length, pnum_streams, pstreams)) { status = NT_STATUS_INVALID_NETWORK_RESPONSE; goto fail; } fail: if (fnum != 0xffff) { cli_smb2_close_fnum(cli, fnum); } 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) { 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)) { /* * Can't use sync call while an async call is in flight */ status = NT_STATUS_INVALID_PARAMETER; goto fail; } if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { status = NT_STATUS_INVALID_PARAMETER; goto fail; } status = get_fnum_from_path(cli, name, FILE_WRITE_ATTRIBUTES, &fnum); if (!NT_STATUS_IS_OK(status)) { goto fail; } status = map_fnum_to_smb2_handle(cli, fnum, &ph); if (!NT_STATUS_IS_OK(status)) { goto fail; } /* 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); 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); status = smb2cli_set_info(cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon, 1, /* in_info_type */ SMB_FILE_BASIC_INFORMATION - 1000, /* in_file_info_class */ &inbuf, /* in_input_buffer */ 0, /* in_additional_info */ ph->fid_persistent, ph->fid_volatile); fail: if (fnum != 0xffff) { cli_smb2_close_fnum(cli, fnum); } TALLOC_FREE(frame); return status; } /*************************************************************** Wrapper that allows SMB2 to set file handle times. Synchronous only. ***************************************************************/ NTSTATUS cli_smb2_setattrE(struct cli_state *cli, uint16_t fnum, time_t change_time, time_t access_time, time_t write_time) { NTSTATUS status; struct smb2_hnd *ph = NULL; uint8_t inbuf_store[40]; DATA_BLOB inbuf = data_blob_null; if (smbXcli_conn_has_async_calls(cli->conn)) { /* * Can't use sync call while an async call is in flight */ return NT_STATUS_INVALID_PARAMETER; } if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { return NT_STATUS_INVALID_PARAMETER; } status = map_fnum_to_smb2_handle(cli, fnum, &ph); if (!NT_STATUS_IS_OK(status)) { return status; } /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1), level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */ inbuf.data = inbuf_store; inbuf.length = sizeof(inbuf_store); data_blob_clear(&inbuf); SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL); if (change_time != 0) { put_long_date((char *)inbuf.data + 24, change_time); } if (access_time != 0) { put_long_date((char *)inbuf.data + 8, access_time); } if (write_time != 0) { put_long_date((char *)inbuf.data + 16, write_time); } return smb2cli_set_info(cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon, 1, /* in_info_type */ SMB_FILE_BASIC_INFORMATION - 1000, /* in_file_info_class */ &inbuf, /* in_input_buffer */ 0, /* in_additional_info */ ph->fid_persistent, ph->fid_volatile); } /*************************************************************** Wrapper that allows SMB2 to query disk attributes (size). Synchronous only. ***************************************************************/ NTSTATUS cli_smb2_dskattr(struct cli_state *cli, int *bsize, int *total, int *avail) { NTSTATUS status; uint16_t fnum = 0xffff; DATA_BLOB outbuf = data_blob_null; struct smb2_hnd *ph = NULL; uint32_t sectors_per_unit = 0; uint32_t bytes_per_sector = 0; uint64_t total_size = 0; uint64_t size_free = 0; TALLOC_CTX *frame = talloc_stackframe(); if (smbXcli_conn_has_async_calls(cli->conn)) { /* * Can't use sync call while an async call is in flight */ status = NT_STATUS_INVALID_PARAMETER; goto fail; } if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { status = NT_STATUS_INVALID_PARAMETER; goto fail; } /* First open the top level directory. */ status = cli_smb2_create_fnum(cli, "", 0, /* create_flags */ FILE_READ_ATTRIBUTES, /* desired_access */ FILE_ATTRIBUTE_DIRECTORY, /* file attributes */ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */ FILE_OPEN, /* create_disposition */ FILE_DIRECTORY_FILE, /* create_options */ &fnum, NULL); if (!NT_STATUS_IS_OK(status)) { goto fail; } status = map_fnum_to_smb2_handle(cli, fnum, &ph); if (!NT_STATUS_IS_OK(status)) { goto fail; } /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2), level 3 (SMB_FS_SIZE_INFORMATION). */ status = smb2cli_query_info(cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon, 2, /* in_info_type */ 3, /* in_file_info_class */ 0xFFFF, /* in_max_output_length */ NULL, /* in_input_buffer */ 0, /* in_additional_info */ 0, /* in_flags */ ph->fid_persistent, ph->fid_volatile, frame, &outbuf); if (!NT_STATUS_IS_OK(status)) { goto fail; } /* Parse the reply. */ if (outbuf.length != 24) { status = NT_STATUS_INVALID_NETWORK_RESPONSE; goto fail; } total_size = BVAL(outbuf.data, 0); size_free = BVAL(outbuf.data, 8); sectors_per_unit = IVAL(outbuf.data, 16); bytes_per_sector = IVAL(outbuf.data, 20); if (bsize) { *bsize = (int)(sectors_per_unit * bytes_per_sector); } if (total) { *total = (int)total_size; } if (avail) { *avail = (int)size_free; } status = NT_STATUS_OK; fail: if (fnum != 0xffff) { cli_smb2_close_fnum(cli, fnum); } 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: 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: 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) { NTSTATUS status; DATA_BLOB inbuf = data_blob_null; uint16_t fnum = 0xffff; struct smb2_hnd *ph = NULL; smb_ucs2_t *converted_str = NULL; size_t converted_size_bytes = 0; size_t namelen = 0; TALLOC_CTX *frame = talloc_stackframe(); if (smbXcli_conn_has_async_calls(cli->conn)) { /* * Can't use sync call while an async call is in flight */ status = NT_STATUS_INVALID_PARAMETER; goto fail; } if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { status = NT_STATUS_INVALID_PARAMETER; goto fail; } status = get_fnum_from_path(cli, fname_src, DELETE_ACCESS, &fnum); if (!NT_STATUS_IS_OK(status)) { goto fail; } status = map_fnum_to_smb2_handle(cli, fnum, &ph); if (!NT_STATUS_IS_OK(status)) { goto fail; } /* SMB2 is pickier about pathnames. Ensure it doesn't start in a '\' */ if (*fname_dst == '\\') { fname_dst++; } /* SMB2 is pickier about pathnames. Ensure it doesn't end in a '\' */ namelen = strlen(fname_dst); if (namelen > 0 && fname_dst[namelen-1] == '\\') { char *modname = talloc_strdup(frame, fname_dst); modname[namelen-1] = '\0'; fname_dst = modname; } if (!push_ucs2_talloc(frame, &converted_str, fname_dst, &converted_size_bytes)) { status = NT_STATUS_INVALID_PARAMETER; goto fail; } /* W2K8 insists the dest name is not null terminated. Remove the last 2 zero bytes and reduce the name length. */ if (converted_size_bytes < 2) { status = NT_STATUS_INVALID_PARAMETER; goto fail; } converted_size_bytes -= 2; inbuf = data_blob_talloc_zero(frame, 20 + converted_size_bytes); if (inbuf.data == NULL) { status = NT_STATUS_NO_MEMORY; goto fail; } SIVAL(inbuf.data, 16, converted_size_bytes); memcpy(inbuf.data + 20, converted_str, converted_size_bytes); /* setinfo on the returned handle with info_type SMB2_GETINFO_FILE (1), level SMB2_FILE_RENAME_INFORMATION (SMB_FILE_RENAME_INFORMATION - 1000) */ status = smb2cli_set_info(cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon, 1, /* in_info_type */ SMB_FILE_RENAME_INFORMATION - 1000, /* in_file_info_class */ &inbuf, /* in_input_buffer */ 0, /* in_additional_info */ ph->fid_persistent, ph->fid_volatile); fail: if (fnum != 0xffff) { cli_smb2_close_fnum(cli, fnum); } TALLOC_FREE(frame); return status; } /*************************************************************** Wrapper that allows SMB2 to set an EA on a fnum. Synchronous only. ***************************************************************/ NTSTATUS cli_smb2_set_ea_fnum(struct cli_state *cli, uint16_t fnum, const char *ea_name, const char *ea_val, size_t ea_len) { NTSTATUS status; DATA_BLOB inbuf = data_blob_null; size_t bloblen = 0; char *ea_name_ascii = NULL; size_t namelen = 0; struct smb2_hnd *ph = NULL; TALLOC_CTX *frame = talloc_stackframe(); if (smbXcli_conn_has_async_calls(cli->conn)) { /* * Can't use sync call while an async call is in flight */ status = NT_STATUS_INVALID_PARAMETER; goto fail; } if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { status = NT_STATUS_INVALID_PARAMETER; goto fail; } status = map_fnum_to_smb2_handle(cli, fnum, &ph); if (!NT_STATUS_IS_OK(status)) { goto fail; } /* Marshall the SMB2 EA data. */ if (ea_len > 0xFFFF) { status = NT_STATUS_INVALID_PARAMETER; goto fail; } if (!push_ascii_talloc(frame, &ea_name_ascii, ea_name, &namelen)) { status = NT_STATUS_INVALID_PARAMETER; goto fail; } if (namelen < 2 || namelen > 0xFF) { status = NT_STATUS_INVALID_PARAMETER; goto fail; } bloblen = 8 + ea_len + namelen; /* Round up to a 4 byte boundary. */ bloblen = ((bloblen + 3)&~3); inbuf = data_blob_talloc_zero(frame, bloblen); if (inbuf.data == NULL) { status = NT_STATUS_NO_MEMORY; goto fail; } /* namelen doesn't include the NULL byte. */ SCVAL(inbuf.data, 5, namelen - 1); SSVAL(inbuf.data, 6, ea_len); memcpy(inbuf.data + 8, ea_name_ascii, namelen); memcpy(inbuf.data + 8 + namelen, ea_val, ea_len); /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1), level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */ status = smb2cli_set_info(cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon, 1, /* in_info_type */ SMB_FILE_FULL_EA_INFORMATION - 1000, /* in_file_info_class */ &inbuf, /* in_input_buffer */ 0, /* in_additional_info */ ph->fid_persistent, ph->fid_volatile); fail: 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); } return status; } /*************************************************************** Wrapper that allows SMB2 to get an EA list on a pathname. Synchronous only. ***************************************************************/ NTSTATUS cli_smb2_get_ea_list_path(struct cli_state *cli, const char *name, TALLOC_CTX *ctx, size_t *pnum_eas, struct ea_struct **pea_array) { NTSTATUS status; uint16_t fnum = 0xffff; DATA_BLOB outbuf = data_blob_null; struct smb2_hnd *ph = NULL; struct ea_list *ea_list = NULL; struct ea_list *eal = NULL; size_t ea_count = 0; TALLOC_CTX *frame = talloc_stackframe(); *pnum_eas = 0; *pea_array = NULL; if (smbXcli_conn_has_async_calls(cli->conn)) { /* * Can't use sync call while an async call is in flight */ status = NT_STATUS_INVALID_PARAMETER; goto fail; } if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { status = NT_STATUS_INVALID_PARAMETER; goto fail; } status = get_fnum_from_path(cli, name, FILE_READ_EA, &fnum); if (!NT_STATUS_IS_OK(status)) { goto fail; } status = map_fnum_to_smb2_handle(cli, fnum, &ph); if (!NT_STATUS_IS_OK(status)) { goto fail; } /* getinfo on the handle with info_type SMB2_GETINFO_FILE (1), level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */ status = smb2cli_query_info(cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon, 1, /* in_info_type */ SMB_FILE_FULL_EA_INFORMATION - 1000, /* in_file_info_class */ 0xFFFF, /* in_max_output_length */ NULL, /* in_input_buffer */ 0, /* in_additional_info */ 0, /* in_flags */ ph->fid_persistent, ph->fid_volatile, frame, &outbuf); if (!NT_STATUS_IS_OK(status)) { goto fail; } /* Parse the reply. */ ea_list = read_nttrans_ea_list(ctx, (const char *)outbuf.data, outbuf.length); if (ea_list == NULL) { status = NT_STATUS_INVALID_NETWORK_RESPONSE; goto fail; } /* Convert to an array. */ for (eal = ea_list; eal; eal = eal->next) { ea_count++; } if (ea_count) { *pea_array = talloc_array(ctx, struct ea_struct, ea_count); if (*pea_array == NULL) { status = NT_STATUS_NO_MEMORY; goto fail; } ea_count = 0; for (eal = ea_list; eal; eal = eal->next) { (*pea_array)[ea_count++] = ea_list->ea; } *pnum_eas = ea_count; } fail: if (fnum != 0xffff) { cli_smb2_close_fnum(cli, fnum); } 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)) { 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; 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)) { tevent_req_received(req); return status; } if (pwritten != NULL) { *pwritten = (size_t)state->written; } 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)) { return status; } if (pwritten != NULL) { *pwritten = (size_t)state->written; } return NT_STATUS_OK; }