summaryrefslogtreecommitdiffstats
path: root/source3/smbd/smb1_trans2.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/smbd/smb1_trans2.c')
-rw-r--r--source3/smbd/smb1_trans2.c5703
1 files changed, 5703 insertions, 0 deletions
diff --git a/source3/smbd/smb1_trans2.c b/source3/smbd/smb1_trans2.c
new file mode 100644
index 0000000..16dff34
--- /dev/null
+++ b/source3/smbd/smb1_trans2.c
@@ -0,0 +1,5703 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB transaction2 handling
+ Copyright (C) Jeremy Allison 1994-2007
+ Copyright (C) Stefan (metze) Metzmacher 2003
+ Copyright (C) Volker Lendecke 2005-2007
+ Copyright (C) Steve French 2005
+ Copyright (C) James Peach 2006-2007
+
+ Extensively modified by Andrew Tridgell, 1995
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "ntioctl.h"
+#include "system/filesys.h"
+#include "lib/util/time_basic.h"
+#include "version.h"
+#include "smbd/smbd.h"
+#include "smbd/globals.h"
+#include "../libcli/auth/libcli_auth.h"
+#include "../librpc/gen_ndr/xattr.h"
+#include "../librpc/gen_ndr/ndr_security.h"
+#include "libcli/security/security.h"
+#include "trans2.h"
+#include "auth.h"
+#include "smbprofile.h"
+#include "rpc_server/srv_pipe_hnd.h"
+#include "printing.h"
+#include "lib/util_ea.h"
+#include "lib/readdir_attr.h"
+#include "messages.h"
+#include "libcli/smb/smb2_posix.h"
+#include "lib/util/string_wrappers.h"
+#include "source3/lib/substitute.h"
+#include "source3/lib/adouble.h"
+#include "source3/smbd/dir.h"
+
+#define DIR_ENTRY_SAFETY_MARGIN 4096
+
+/****************************************************************************
+ Send the required number of replies back.
+ We assume all fields other than the data fields are
+ set correctly for the type of call.
+ HACK ! Always assumes smb_setup field is zero.
+****************************************************************************/
+
+static void send_trans2_replies(connection_struct *conn,
+ struct smb_request *req,
+ NTSTATUS status,
+ const char *params,
+ int paramsize,
+ const char *pdata,
+ int datasize,
+ int max_data_bytes)
+{
+ /* As we are using a protocol > LANMAN1 then the max_send
+ variable must have been set in the sessetupX call.
+ This takes precedence over the max_xmit field in the
+ global struct. These different max_xmit variables should
+ be merged as this is now too confusing */
+
+ int data_to_send = datasize;
+ int params_to_send = paramsize;
+ int useable_space;
+ const char *pp = params;
+ const char *pd = pdata;
+ int params_sent_thistime, data_sent_thistime, total_sent_thistime;
+ int alignment_offset = 1; /* JRA. This used to be 3. Set to 1 to make netmon parse ok. */
+ int data_alignment_offset = 0;
+ bool overflow = False;
+ struct smbXsrv_connection *xconn = req->xconn;
+ int max_send = xconn->smb1.sessions.max_send;
+
+ /* Modify the data_to_send and datasize and set the error if
+ we're trying to send more than max_data_bytes. We still send
+ the part of the packet(s) that fit. Strange, but needed
+ for OS/2. */
+
+ if (max_data_bytes > 0 && datasize > max_data_bytes) {
+ DEBUG(5,("send_trans2_replies: max_data_bytes %d exceeded by data %d\n",
+ max_data_bytes, datasize ));
+ datasize = data_to_send = max_data_bytes;
+ overflow = True;
+ }
+
+ /* If there genuinely are no parameters or data to send just send the empty packet */
+
+ if(params_to_send == 0 && data_to_send == 0) {
+ reply_smb1_outbuf(req, 10, 0);
+ if (NT_STATUS_V(status)) {
+ uint8_t eclass;
+ uint32_t ecode;
+ ntstatus_to_dos(status, &eclass, &ecode);
+ error_packet_set((char *)req->outbuf,
+ eclass, ecode, status,
+ __LINE__,__FILE__);
+ }
+ show_msg((char *)req->outbuf);
+ if (!smb1_srv_send(xconn,
+ (char *)req->outbuf,
+ true,
+ req->seqnum + 1,
+ IS_CONN_ENCRYPTED(conn))) {
+ exit_server_cleanly("send_trans2_replies: smb1_srv_send failed.");
+ }
+ TALLOC_FREE(req->outbuf);
+ return;
+ }
+
+ /* When sending params and data ensure that both are nicely aligned */
+ /* Only do this alignment when there is also data to send - else
+ can cause NT redirector problems. */
+
+ if (((params_to_send % 4) != 0) && (data_to_send != 0))
+ data_alignment_offset = 4 - (params_to_send % 4);
+
+ /* Space is bufsize minus Netbios over TCP header minus SMB header */
+ /* The alignment_offset is to align the param bytes on an even byte
+ boundary. NT 4.0 Beta needs this to work correctly. */
+
+ useable_space = max_send - (smb_size
+ + 2 * 10 /* wct */
+ + alignment_offset
+ + data_alignment_offset);
+
+ if (useable_space < 0) {
+ DEBUG(0, ("send_trans2_replies failed sanity useable_space "
+ "= %d!!!\n", useable_space));
+ exit_server_cleanly("send_trans2_replies: Not enough space");
+ }
+
+ while (params_to_send || data_to_send) {
+ /* Calculate whether we will totally or partially fill this packet */
+
+ total_sent_thistime = params_to_send + data_to_send;
+
+ /* We can never send more than useable_space */
+ /*
+ * Note that 'useable_space' does not include the alignment offsets,
+ * but we must include the alignment offsets in the calculation of
+ * the length of the data we send over the wire, as the alignment offsets
+ * are sent here. Fix from Marc_Jacobsen@hp.com.
+ */
+
+ total_sent_thistime = MIN(total_sent_thistime, useable_space);
+
+ reply_smb1_outbuf(req, 10, total_sent_thistime + alignment_offset
+ + data_alignment_offset);
+
+ /* Set total params and data to be sent */
+ SSVAL(req->outbuf,smb_tprcnt,paramsize);
+ SSVAL(req->outbuf,smb_tdrcnt,datasize);
+
+ /* Calculate how many parameters and data we can fit into
+ * this packet. Parameters get precedence
+ */
+
+ params_sent_thistime = MIN(params_to_send,useable_space);
+ data_sent_thistime = useable_space - params_sent_thistime;
+ data_sent_thistime = MIN(data_sent_thistime,data_to_send);
+
+ SSVAL(req->outbuf,smb_prcnt, params_sent_thistime);
+
+ /* smb_proff is the offset from the start of the SMB header to the
+ parameter bytes, however the first 4 bytes of outbuf are
+ the Netbios over TCP header. Thus use smb_base() to subtract
+ them from the calculation */
+
+ SSVAL(req->outbuf,smb_proff,
+ ((smb_buf(req->outbuf)+alignment_offset)
+ - smb_base(req->outbuf)));
+
+ if(params_sent_thistime == 0)
+ SSVAL(req->outbuf,smb_prdisp,0);
+ else
+ /* Absolute displacement of param bytes sent in this packet */
+ SSVAL(req->outbuf,smb_prdisp,pp - params);
+
+ SSVAL(req->outbuf,smb_drcnt, data_sent_thistime);
+ if(data_sent_thistime == 0) {
+ SSVAL(req->outbuf,smb_droff,0);
+ SSVAL(req->outbuf,smb_drdisp, 0);
+ } else {
+ /* The offset of the data bytes is the offset of the
+ parameter bytes plus the number of parameters being sent this time */
+ SSVAL(req->outbuf, smb_droff,
+ ((smb_buf(req->outbuf)+alignment_offset)
+ - smb_base(req->outbuf))
+ + params_sent_thistime + data_alignment_offset);
+ SSVAL(req->outbuf,smb_drdisp, pd - pdata);
+ }
+
+ /* Initialize the padding for alignment */
+
+ if (alignment_offset != 0) {
+ memset(smb_buf(req->outbuf), 0, alignment_offset);
+ }
+
+ /* Copy the param bytes into the packet */
+
+ if(params_sent_thistime) {
+ memcpy((smb_buf(req->outbuf)+alignment_offset), pp,
+ params_sent_thistime);
+ }
+
+ /* Copy in the data bytes */
+ if(data_sent_thistime) {
+ if (data_alignment_offset != 0) {
+ memset((smb_buf(req->outbuf)+alignment_offset+
+ params_sent_thistime), 0,
+ data_alignment_offset);
+ }
+ memcpy(smb_buf(req->outbuf)+alignment_offset
+ +params_sent_thistime+data_alignment_offset,
+ pd,data_sent_thistime);
+ }
+
+ DEBUG(9,("t2_rep: params_sent_thistime = %d, data_sent_thistime = %d, useable_space = %d\n",
+ params_sent_thistime, data_sent_thistime, useable_space));
+ DEBUG(9,("t2_rep: params_to_send = %d, data_to_send = %d, paramsize = %d, datasize = %d\n",
+ params_to_send, data_to_send, paramsize, datasize));
+
+ if (overflow) {
+ error_packet_set((char *)req->outbuf,
+ ERRDOS,ERRbufferoverflow,
+ STATUS_BUFFER_OVERFLOW,
+ __LINE__,__FILE__);
+ } else if (NT_STATUS_V(status)) {
+ uint8_t eclass;
+ uint32_t ecode;
+ ntstatus_to_dos(status, &eclass, &ecode);
+ error_packet_set((char *)req->outbuf,
+ eclass, ecode, status,
+ __LINE__,__FILE__);
+ }
+
+ /* Send the packet */
+ show_msg((char *)req->outbuf);
+ if (!smb1_srv_send(xconn,
+ (char *)req->outbuf,
+ true,
+ req->seqnum + 1,
+ IS_CONN_ENCRYPTED(conn))) {
+ exit_server_cleanly("send_trans2_replies: smb1_srv_send failed.");
+ }
+
+ TALLOC_FREE(req->outbuf);
+
+ pp += params_sent_thistime;
+ pd += data_sent_thistime;
+
+ params_to_send -= params_sent_thistime;
+ data_to_send -= data_sent_thistime;
+
+ /* Sanity check */
+ if(params_to_send < 0 || data_to_send < 0) {
+ DEBUG(0,("send_trans2_replies failed sanity check pts = %d, dts = %d\n!!!",
+ params_to_send, data_to_send));
+ return;
+ }
+ }
+
+ return;
+}
+
+/****************************************************************************
+ Deal with SMB_SET_POSIX_LOCK.
+****************************************************************************/
+
+static void smb_set_posix_lock_done(struct tevent_req *subreq);
+
+static NTSTATUS smb_set_posix_lock(connection_struct *conn,
+ struct smb_request *req,
+ const char *pdata,
+ int total_data,
+ files_struct *fsp)
+{
+ struct tevent_req *subreq = NULL;
+ struct smbd_lock_element *lck = NULL;
+ uint64_t count;
+ uint64_t offset;
+ uint64_t smblctx;
+ bool blocking_lock = False;
+ enum brl_type lock_type;
+
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (!CAN_WRITE(conn)) {
+ return NT_STATUS_DOS(ERRSRV, ERRaccess);
+ }
+
+ if (fsp == NULL ||
+ fsp->fsp_flags.is_pathref ||
+ fsp_get_io_fd(fsp) == -1)
+ {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (total_data != POSIX_LOCK_DATA_SIZE) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ switch (SVAL(pdata, POSIX_LOCK_TYPE_OFFSET)) {
+ case POSIX_LOCK_TYPE_READ:
+ lock_type = READ_LOCK;
+ break;
+ case POSIX_LOCK_TYPE_WRITE:
+ /* Return the right POSIX-mappable error code for files opened read-only. */
+ if (!fsp->fsp_flags.can_write) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+ lock_type = WRITE_LOCK;
+ break;
+ case POSIX_LOCK_TYPE_UNLOCK:
+ lock_type = UNLOCK_LOCK;
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ switch (SVAL(pdata, POSIX_LOCK_FLAGS_OFFSET)) {
+ case POSIX_LOCK_FLAG_NOWAIT:
+ blocking_lock = false;
+ break;
+ case POSIX_LOCK_FLAG_WAIT:
+ blocking_lock = true;
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!lp_blocking_locks(SNUM(conn))) {
+ blocking_lock = False;
+ }
+
+ smblctx = (uint64_t)IVAL(pdata, POSIX_LOCK_PID_OFFSET);
+ offset = (((uint64_t) IVAL(pdata,(POSIX_LOCK_START_OFFSET+4))) << 32) |
+ ((uint64_t) IVAL(pdata,POSIX_LOCK_START_OFFSET));
+ count = (((uint64_t) IVAL(pdata,(POSIX_LOCK_LEN_OFFSET+4))) << 32) |
+ ((uint64_t) IVAL(pdata,POSIX_LOCK_LEN_OFFSET));
+
+ DBG_DEBUG("file %s, lock_type = %u, smblctx = %"PRIu64", "
+ "count = %"PRIu64", offset = %"PRIu64"\n",
+ fsp_str_dbg(fsp),
+ (unsigned int)lock_type,
+ smblctx,
+ count,
+ offset);
+
+ if (lock_type == UNLOCK_LOCK) {
+ struct smbd_lock_element l = {
+ .req_guid = smbd_request_guid(req, 0),
+ .smblctx = smblctx,
+ .brltype = UNLOCK_LOCK,
+ .lock_flav = POSIX_LOCK,
+ .offset = offset,
+ .count = count,
+ };
+ status = smbd_do_unlocking(req, fsp, 1, &l);
+ return status;
+ }
+
+ lck = talloc(req, struct smbd_lock_element);
+ if (lck == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *lck = (struct smbd_lock_element) {
+ .req_guid = smbd_request_guid(req, 0),
+ .smblctx = smblctx,
+ .brltype = lock_type,
+ .lock_flav = POSIX_LOCK,
+ .count = count,
+ .offset = offset,
+ };
+
+ subreq = smbd_smb1_do_locks_send(
+ fsp,
+ req->sconn->ev_ctx,
+ &req,
+ fsp,
+ blocking_lock ? UINT32_MAX : 0,
+ true, /* large_offset */
+ 1,
+ lck);
+ if (subreq == NULL) {
+ TALLOC_FREE(lck);
+ return NT_STATUS_NO_MEMORY;
+ }
+ tevent_req_set_callback(subreq, smb_set_posix_lock_done, req);
+ return NT_STATUS_EVENT_PENDING;
+}
+
+static void smb_set_posix_lock_done(struct tevent_req *subreq)
+{
+ struct smb_request *req = NULL;
+ NTSTATUS status;
+ bool ok;
+
+ ok = smbd_smb1_do_locks_extract_smbreq(subreq, talloc_tos(), &req);
+ SMB_ASSERT(ok);
+
+ status = smbd_smb1_do_locks_recv(subreq);
+ TALLOC_FREE(subreq);
+
+ if (NT_STATUS_IS_OK(status)) {
+ char params[2] = {0};
+ /* Fake up max_data_bytes here - we know it fits. */
+ send_trans2_replies(
+ req->conn,
+ req,
+ NT_STATUS_OK,
+ params,
+ 2,
+ NULL,
+ 0,
+ 0xffff);
+ } else {
+ reply_nterror(req, status);
+ ok = smb1_srv_send(req->xconn,
+ (char *)req->outbuf,
+ true,
+ req->seqnum + 1,
+ IS_CONN_ENCRYPTED(req->conn));
+ if (!ok) {
+ exit_server_cleanly("smb_set_posix_lock_done: "
+ "smb1_srv_send failed.");
+ }
+ }
+
+ TALLOC_FREE(req);
+ return;
+}
+
+/****************************************************************************
+ Read a list of EA names from an incoming data buffer. Create an ea_list with them.
+****************************************************************************/
+
+static struct ea_list *read_ea_name_list(TALLOC_CTX *ctx, const char *pdata, size_t data_size)
+{
+ struct ea_list *ea_list_head = NULL;
+ size_t converted_size, offset = 0;
+
+ while (offset + 2 < data_size) {
+ struct ea_list *eal = talloc_zero(ctx, struct ea_list);
+ unsigned int namelen = CVAL(pdata,offset);
+
+ offset++; /* Go past the namelen byte. */
+
+ /* integer wrap paranioa. */
+ if ((offset + namelen < offset) || (offset + namelen < namelen) ||
+ (offset > data_size) || (namelen > data_size) ||
+ (offset + namelen >= data_size)) {
+ break;
+ }
+ /* Ensure the name is null terminated. */
+ if (pdata[offset + namelen] != '\0') {
+ return NULL;
+ }
+ if (!pull_ascii_talloc(ctx, &eal->ea.name, &pdata[offset],
+ &converted_size)) {
+ DEBUG(0,("read_ea_name_list: pull_ascii_talloc "
+ "failed: %s\n", strerror(errno)));
+ }
+ if (!eal->ea.name) {
+ return NULL;
+ }
+
+ offset += (namelen + 1); /* Go past the name + terminating zero. */
+ DLIST_ADD_END(ea_list_head, eal);
+ DEBUG(10,("read_ea_name_list: read ea name %s\n", eal->ea.name));
+ }
+
+ return ea_list_head;
+}
+
+/****************************************************************************
+ Reply to a TRANSACT2_OPEN.
+****************************************************************************/
+
+static void call_trans2open(connection_struct *conn,
+ struct smb_request *req,
+ char **pparams, int total_params,
+ char **ppdata, int total_data,
+ unsigned int max_data_bytes)
+{
+ struct smb_filename *smb_fname = NULL;
+ char *params = *pparams;
+ char *pdata = *ppdata;
+ int deny_mode;
+ int32_t open_attr;
+ bool oplock_request;
+#if 0
+ bool return_additional_info;
+ int16 open_sattr;
+ time_t open_time;
+#endif
+ int open_ofun;
+ uint32_t open_size;
+ char *pname;
+ char *fname = NULL;
+ off_t size=0;
+ int fattr = 0;
+ SMB_INO_T inode = 0;
+ int smb_action = 0;
+ struct files_struct *dirfsp = NULL;
+ files_struct *fsp;
+ struct ea_list *ea_list = NULL;
+ uint16_t flags = 0;
+ NTSTATUS status;
+ uint32_t access_mask;
+ uint32_t share_mode;
+ uint32_t create_disposition;
+ uint32_t create_options = 0;
+ uint32_t private_flags = 0;
+ NTTIME twrp = 0;
+ uint32_t ucf_flags = ucf_flags_from_smb_request(req);
+ TALLOC_CTX *ctx = talloc_tos();
+
+ /*
+ * Ensure we have enough parameters to perform the operation.
+ */
+
+ if (total_params < 29) {
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ goto out;
+ }
+
+ flags = SVAL(params, 0);
+ deny_mode = SVAL(params, 2);
+ open_attr = SVAL(params,6);
+ oplock_request = (flags & REQUEST_OPLOCK) ? EXCLUSIVE_OPLOCK : 0;
+ if (oplock_request) {
+ oplock_request |= (flags & REQUEST_BATCH_OPLOCK) ? BATCH_OPLOCK : 0;
+ }
+
+#if 0
+ return_additional_info = BITSETW(params,0);
+ open_sattr = SVAL(params, 4);
+ open_time = make_unix_date3(params+8);
+#endif
+ open_ofun = SVAL(params,12);
+ open_size = IVAL(params,14);
+ pname = &params[28];
+
+ if (IS_IPC(conn)) {
+ reply_nterror(req, NT_STATUS_NETWORK_ACCESS_DENIED);
+ goto out;
+ }
+
+ if (req->posix_pathnames) {
+ srvstr_get_path_posix(ctx,
+ params,
+ req->flags2,
+ &fname,
+ pname,
+ total_params - 28,
+ STR_TERMINATE,
+ &status);
+ } else {
+ srvstr_get_path(ctx,
+ params,
+ req->flags2,
+ &fname,
+ pname,
+ total_params - 28,
+ STR_TERMINATE,
+ &status);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ reply_nterror(req, status);
+ goto out;
+ }
+
+ DEBUG(3,("call_trans2open %s deny_mode=0x%x attr=%d ofun=0x%x size=%d\n",
+ fname, (unsigned int)deny_mode, (unsigned int)open_attr,
+ (unsigned int)open_ofun, open_size));
+
+ if (ucf_flags & UCF_GMT_PATHNAME) {
+ extract_snapshot_token(fname, &twrp);
+ }
+ status = smb1_strip_dfs_path(ctx, &ucf_flags, &fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ reply_nterror(req, status);
+ goto out;
+ }
+ status = filename_convert_dirfsp(ctx,
+ conn,
+ fname,
+ ucf_flags,
+ twrp,
+ &dirfsp,
+ &smb_fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) {
+ reply_botherror(req,
+ NT_STATUS_PATH_NOT_COVERED,
+ ERRSRV, ERRbadpath);
+ goto out;
+ }
+ reply_nterror(req, status);
+ goto out;
+ }
+
+ if (open_ofun == 0) {
+ reply_nterror(req, NT_STATUS_OBJECT_NAME_COLLISION);
+ goto out;
+ }
+
+ if (!map_open_params_to_ntcreate(smb_fname->base_name, deny_mode,
+ open_ofun,
+ &access_mask, &share_mode,
+ &create_disposition,
+ &create_options,
+ &private_flags)) {
+ reply_nterror(req, NT_STATUS_ACCESS_DENIED);
+ goto out;
+ }
+
+ /* Any data in this call is an EA list. */
+ if (total_data && (total_data != 4)) {
+ if (total_data < 10) {
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ goto out;
+ }
+
+ if (IVAL(pdata,0) > total_data) {
+ DEBUG(10,("call_trans2open: bad total data size (%u) > %u\n",
+ IVAL(pdata,0), (unsigned int)total_data));
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ goto out;
+ }
+
+ ea_list = read_ea_list(talloc_tos(), pdata + 4,
+ total_data - 4);
+ if (!ea_list) {
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ goto out;
+ }
+
+ if (!lp_ea_support(SNUM(conn))) {
+ reply_nterror(req, NT_STATUS_EAS_NOT_SUPPORTED);
+ goto out;
+ }
+
+ if (!req->posix_pathnames &&
+ ea_list_has_invalid_name(ea_list)) {
+ int param_len = 30;
+ *pparams = (char *)SMB_REALLOC(*pparams, param_len);
+ if(*pparams == NULL ) {
+ reply_nterror(req, NT_STATUS_NO_MEMORY);
+ goto out;
+ }
+ params = *pparams;
+ memset(params, '\0', param_len);
+ send_trans2_replies(conn, req, STATUS_INVALID_EA_NAME,
+ params, param_len, NULL, 0, max_data_bytes);
+ goto out;
+ }
+ }
+
+ status = SMB_VFS_CREATE_FILE(
+ conn, /* conn */
+ req, /* req */
+ dirfsp, /* dirfsp */
+ smb_fname, /* fname */
+ access_mask, /* access_mask */
+ share_mode, /* share_access */
+ create_disposition, /* create_disposition*/
+ create_options, /* create_options */
+ open_attr, /* file_attributes */
+ oplock_request, /* oplock_request */
+ NULL, /* lease */
+ open_size, /* allocation_size */
+ private_flags,
+ NULL, /* sd */
+ ea_list, /* ea_list */
+ &fsp, /* result */
+ &smb_action, /* psbuf */
+ NULL, NULL); /* create context */
+
+ if (!NT_STATUS_IS_OK(status)) {
+ if (open_was_deferred(req->xconn, req->mid)) {
+ /* We have re-scheduled this call. */
+ goto out;
+ }
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
+ reply_openerror(req, status);
+ goto out;
+ }
+
+ fsp = fcb_or_dos_open(
+ req,
+ smb_fname,
+ access_mask,
+ create_options,
+ private_flags);
+ if (fsp == NULL) {
+ bool ok = defer_smb1_sharing_violation(req);
+ if (ok) {
+ goto out;
+ }
+ reply_openerror(req, status);
+ goto out;
+ }
+
+ smb_action = FILE_WAS_OPENED;
+ }
+
+ size = get_file_size_stat(&smb_fname->st);
+ fattr = fdos_mode(fsp);
+ inode = smb_fname->st.st_ex_ino;
+ if (fattr & FILE_ATTRIBUTE_DIRECTORY) {
+ close_file_free(req, &fsp, ERROR_CLOSE);
+ reply_nterror(req, NT_STATUS_ACCESS_DENIED);
+ goto out;
+ }
+
+ /* Realloc the size of parameters and data we will return */
+ *pparams = (char *)SMB_REALLOC(*pparams, 30);
+ if(*pparams == NULL ) {
+ reply_nterror(req, NT_STATUS_NO_MEMORY);
+ goto out;
+ }
+ params = *pparams;
+
+ SSVAL(params,0,fsp->fnum);
+ SSVAL(params,2,fattr);
+ srv_put_dos_date2_ts(params, 4, smb_fname->st.st_ex_mtime);
+ SIVAL(params,8, (uint32_t)size);
+ SSVAL(params,12,deny_mode);
+ SSVAL(params,14,0); /* open_type - file or directory. */
+ SSVAL(params,16,0); /* open_state - only valid for IPC device. */
+
+ if (oplock_request && lp_fake_oplocks(SNUM(conn))) {
+ smb_action |= EXTENDED_OPLOCK_GRANTED;
+ }
+
+ SSVAL(params,18,smb_action);
+
+ /*
+ * WARNING - this may need to be changed if SMB_INO_T <> 4 bytes.
+ */
+ SIVAL(params,20,inode);
+ SSVAL(params,24,0); /* Padding. */
+ if (flags & 8) {
+ uint32_t ea_size = estimate_ea_size(smb_fname->fsp);
+ SIVAL(params, 26, ea_size);
+ } else {
+ SIVAL(params, 26, 0);
+ }
+
+ /* Send the required number of replies */
+ send_trans2_replies(conn, req, NT_STATUS_OK, params, 30, *ppdata, 0, max_data_bytes);
+ out:
+ TALLOC_FREE(smb_fname);
+}
+
+static NTSTATUS get_lanman2_dir_entry(TALLOC_CTX *ctx,
+ connection_struct *conn,
+ struct dptr_struct *dirptr,
+ uint16_t flags2,
+ const char *path_mask,
+ uint32_t dirtype,
+ int info_level,
+ bool requires_resume_key,
+ bool dont_descend,
+ bool ask_sharemode,
+ char **ppdata,
+ char *base_data,
+ char *end_data,
+ int space_remaining,
+ int *last_entry_off,
+ struct ea_list *name_list)
+{
+ uint8_t align = 4;
+ const bool do_pad = true;
+
+ if (info_level >= 1 && info_level <= 3) {
+ /* No alignment on earlier info levels. */
+ align = 1;
+ }
+
+ return smbd_dirptr_lanman2_entry(ctx, conn, dirptr, flags2,
+ path_mask, dirtype, info_level,
+ requires_resume_key, dont_descend, ask_sharemode,
+ true, align, do_pad,
+ ppdata, base_data, end_data,
+ space_remaining,
+ NULL,
+ last_entry_off, name_list, NULL);
+}
+
+/****************************************************************************
+ Reply to a TRANS2_FINDFIRST.
+****************************************************************************/
+
+static void call_trans2findfirst(connection_struct *conn,
+ struct smb_request *req,
+ char **pparams, int total_params,
+ char **ppdata, int total_data,
+ unsigned int max_data_bytes)
+{
+ /* We must be careful here that we don't return more than the
+ allowed number of data bytes. If this means returning fewer than
+ maxentries then so be it. We assume that the redirector has
+ enough room for the fixed number of parameter bytes it has
+ requested. */
+ struct smb_filename *smb_dname = NULL;
+ char *params = *pparams;
+ char *pdata = *ppdata;
+ char *data_end;
+ uint32_t dirtype;
+ int maxentries;
+ uint16_t findfirst_flags;
+ bool close_after_first;
+ bool close_if_end;
+ bool requires_resume_key;
+ int info_level;
+ char *directory = NULL;
+ char *mask = NULL;
+ char *p;
+ int last_entry_off=0;
+ int dptr_num = -1;
+ int numentries = 0;
+ int i;
+ bool finished = False;
+ bool dont_descend = False;
+ bool out_of_space = False;
+ int space_remaining;
+ struct ea_list *ea_list = NULL;
+ NTSTATUS ntstatus = NT_STATUS_OK;
+ bool ask_sharemode;
+ struct smbXsrv_connection *xconn = req->xconn;
+ struct smbd_server_connection *sconn = req->sconn;
+ uint32_t ucf_flags = ucf_flags_from_smb_request(req);
+ bool backup_priv = false;
+ bool as_root = false;
+ files_struct *fsp = NULL;
+ struct files_struct *dirfsp = NULL;
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+
+ if (total_params < 13) {
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ goto out;
+ }
+
+ dirtype = SVAL(params,0);
+ maxentries = SVAL(params,2);
+ findfirst_flags = SVAL(params,4);
+ close_after_first = (findfirst_flags & FLAG_TRANS2_FIND_CLOSE);
+ close_if_end = (findfirst_flags & FLAG_TRANS2_FIND_CLOSE_IF_END);
+ requires_resume_key = (findfirst_flags & FLAG_TRANS2_FIND_REQUIRE_RESUME);
+ backup_priv = ((findfirst_flags & FLAG_TRANS2_FIND_BACKUP_INTENT) &&
+ security_token_has_privilege(get_current_nttok(conn),
+ SEC_PRIV_BACKUP));
+
+ info_level = SVAL(params,6);
+
+ DBG_NOTICE("dirtype = %"PRIx32", maxentries = %d, "
+ "close_after_first=%d, close_if_end = %d "
+ "requires_resume_key = %d backup_priv = %d level = 0x%x, "
+ "max_data_bytes = %d\n",
+ dirtype,
+ maxentries,
+ close_after_first,
+ close_if_end,
+ requires_resume_key,
+ backup_priv,
+ info_level,
+ max_data_bytes);
+
+ if (!maxentries) {
+ /* W2K3 seems to treat zero as 1. */
+ maxentries = 1;
+ }
+
+ switch (info_level) {
+ case SMB_FIND_INFO_STANDARD:
+ case SMB_FIND_EA_SIZE:
+ case SMB_FIND_EA_LIST:
+ case SMB_FIND_FILE_DIRECTORY_INFO:
+ case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
+ case SMB_FIND_FILE_NAMES_INFO:
+ case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
+ case SMB_FIND_ID_FULL_DIRECTORY_INFO:
+ case SMB_FIND_ID_BOTH_DIRECTORY_INFO:
+ break;
+ case SMB_FIND_FILE_UNIX:
+ case SMB_FIND_FILE_UNIX_INFO2:
+ if (!lp_smb1_unix_extensions()) {
+ reply_nterror(req, NT_STATUS_INVALID_LEVEL);
+ goto out;
+ }
+ if (!req->posix_pathnames) {
+ reply_nterror(req, NT_STATUS_INVALID_LEVEL);
+ goto out;
+ }
+ break;
+ default:
+ reply_nterror(req, NT_STATUS_INVALID_LEVEL);
+ goto out;
+ }
+
+ if (req->posix_pathnames) {
+ srvstr_get_path_posix(talloc_tos(),
+ params,
+ req->flags2,
+ &directory,
+ params+12,
+ total_params - 12,
+ STR_TERMINATE,
+ &ntstatus);
+ } else {
+ srvstr_get_path(talloc_tos(),
+ params,
+ req->flags2,
+ &directory,
+ params+12,
+ total_params - 12,
+ STR_TERMINATE,
+ &ntstatus);
+ }
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ reply_nterror(req, ntstatus);
+ goto out;
+ }
+
+ if (backup_priv) {
+ become_root();
+ as_root = true;
+ }
+ ntstatus = smb1_strip_dfs_path(talloc_tos(), &ucf_flags, &directory);
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ reply_nterror(req, ntstatus);
+ goto out;
+ }
+
+ ntstatus = filename_convert_smb1_search_path(talloc_tos(),
+ conn,
+ directory,
+ ucf_flags,
+ &dirfsp,
+ &smb_dname,
+ &mask);
+
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ if (NT_STATUS_EQUAL(ntstatus,NT_STATUS_PATH_NOT_COVERED)) {
+ reply_botherror(req, NT_STATUS_PATH_NOT_COVERED,
+ ERRSRV, ERRbadpath);
+ goto out;
+ }
+ reply_nterror(req, ntstatus);
+ goto out;
+ }
+
+ TALLOC_FREE(directory);
+ directory = smb_dname->base_name;
+
+ DEBUG(5,("dir=%s, mask = %s\n",directory, mask));
+
+ if (info_level == SMB_FIND_EA_LIST) {
+ uint32_t ea_size;
+
+ if (total_data < 4) {
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ goto out;
+ }
+
+ ea_size = IVAL(pdata,0);
+ if (ea_size != total_data) {
+ DBG_NOTICE("Rejecting EA request with incorrect "
+ "total_data=%d (should be %" PRIu32 ")\n",
+ total_data,
+ ea_size);
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ goto out;
+ }
+
+ if (!lp_ea_support(SNUM(conn))) {
+ reply_nterror(req, NT_STATUS_EAS_NOT_SUPPORTED);
+ goto out;
+ }
+
+ /* Pull out the list of names. */
+ ea_list = read_ea_name_list(talloc_tos(), pdata + 4, ea_size - 4);
+ if (!ea_list) {
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ goto out;
+ }
+ }
+
+ if (max_data_bytes + DIR_ENTRY_SAFETY_MARGIN < max_data_bytes) {
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ goto out;
+ }
+
+ *ppdata = (char *)SMB_REALLOC(
+ *ppdata, max_data_bytes + DIR_ENTRY_SAFETY_MARGIN);
+ if(*ppdata == NULL ) {
+ reply_nterror(req, NT_STATUS_NO_MEMORY);
+ goto out;
+ }
+ pdata = *ppdata;
+ data_end = pdata + max_data_bytes + DIR_ENTRY_SAFETY_MARGIN - 1;
+ /*
+ * squash valgrind "writev(vector[...]) points to uninitialised byte(s)"
+ * error.
+ */
+ memset(pdata + total_data, 0, ((max_data_bytes + DIR_ENTRY_SAFETY_MARGIN) - total_data));
+ /* Realloc the params space */
+ *pparams = (char *)SMB_REALLOC(*pparams, 10);
+ if (*pparams == NULL) {
+ reply_nterror(req, NT_STATUS_NO_MEMORY);
+ goto out;
+ }
+ params = *pparams;
+
+ /*
+ * Open an fsp on this directory for the dptr.
+ */
+ ntstatus = SMB_VFS_CREATE_FILE(
+ conn, /* conn */
+ req, /* req */
+ dirfsp, /* dirfsp */
+ smb_dname, /* dname */
+ FILE_LIST_DIRECTORY, /* access_mask */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE, /* share_access */
+ FILE_OPEN, /* create_disposition*/
+ FILE_DIRECTORY_FILE, /* create_options */
+ FILE_ATTRIBUTE_DIRECTORY,/* file_attributes */
+ NO_OPLOCK, /* oplock_request */
+ NULL, /* lease */
+ 0, /* allocation_size */
+ 0, /* private_flags */
+ NULL, /* sd */
+ NULL, /* ea_list */
+ &fsp, /* result */
+ NULL, /* pinfo */
+ NULL, /* in_context */
+ NULL);/* out_context */
+
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ DBG_ERR("failed to open directory %s\n",
+ smb_fname_str_dbg(smb_dname));
+ reply_nterror(req, ntstatus);
+ goto out;
+ }
+
+ /* Save the wildcard match and attribs we are using on this directory -
+ needed as lanman2 assumes these are being saved between calls */
+
+ ntstatus = dptr_create(conn,
+ req,
+ fsp, /* fsp */
+ False,
+ mask,
+ dirtype,
+ &fsp->dptr);
+
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ /*
+ * Use NULL here for the first parameter (req)
+ * as this is not a client visible handle so
+ * can't be part of an SMB1 chain.
+ */
+ close_file_free(NULL, &fsp, NORMAL_CLOSE);
+ reply_nterror(req, ntstatus);
+ goto out;
+ }
+
+ if (backup_priv) {
+ /* Remember this in case we have
+ to do a findnext. */
+ dptr_set_priv(fsp->dptr);
+ }
+
+ dptr_num = dptr_dnum(fsp->dptr);
+ DEBUG(4,("dptr_num is %d, wcard = %s, attr = %d\n", dptr_num, mask, dirtype));
+
+ /* We don't need to check for VOL here as this is returned by
+ a different TRANS2 call. */
+
+ DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n",
+ directory,lp_dont_descend(talloc_tos(), lp_sub, SNUM(conn))));
+ if (in_list(directory,
+ lp_dont_descend(talloc_tos(), lp_sub, SNUM(conn)),
+ dptr_case_sensitive(fsp->dptr))) {
+ dont_descend = True;
+ }
+
+ p = pdata;
+ space_remaining = max_data_bytes;
+ out_of_space = False;
+
+ ask_sharemode = fsp_search_ask_sharemode(fsp);
+
+ for (i=0;(i<maxentries) && !finished && !out_of_space;i++) {
+
+ ntstatus = get_lanman2_dir_entry(talloc_tos(),
+ conn,
+ fsp->dptr,
+ req->flags2,
+ mask,
+ dirtype,
+ info_level,
+ requires_resume_key,
+ dont_descend,
+ ask_sharemode,
+ &p,
+ pdata,
+ data_end,
+ space_remaining,
+ &last_entry_off,
+ ea_list);
+ if (NT_STATUS_EQUAL(ntstatus, NT_STATUS_ILLEGAL_CHARACTER)) {
+ /*
+ * Bad character conversion on name. Ignore
+ * this entry.
+ */
+ continue;
+ }
+ if (NT_STATUS_EQUAL(ntstatus, STATUS_MORE_ENTRIES)) {
+ out_of_space = true;
+ } else {
+ finished = !NT_STATUS_IS_OK(ntstatus);
+ }
+
+ if (!finished && !out_of_space) {
+ numentries++;
+ }
+
+ /* Ensure space_remaining never goes -ve. */
+ if (PTR_DIFF(p,pdata) > max_data_bytes) {
+ space_remaining = 0;
+ out_of_space = true;
+ } else {
+ space_remaining = max_data_bytes - PTR_DIFF(p,pdata);
+ }
+ }
+
+ /* Check if we can close the dirptr */
+ if(close_after_first || (finished && close_if_end)) {
+ DEBUG(5,("call_trans2findfirst - (2) closing dptr_num %d\n", dptr_num));
+ dptr_num = -1;
+ close_file_free(NULL, &fsp, NORMAL_CLOSE);
+ }
+
+ /*
+ * If there are no matching entries we must return ERRDOS/ERRbadfile -
+ * from observation of NT. NB. This changes to ERRDOS,ERRnofiles if
+ * the protocol level is less than NT1. Tested with smbclient. JRA.
+ * This should fix the OS/2 client bug #2335.
+ */
+
+ if(numentries == 0) {
+ dptr_num = -1;
+ /*
+ * We may have already closed the file in the
+ * close_after_first or finished case above.
+ */
+ if (fsp != NULL) {
+ close_file_free(NULL, &fsp, NORMAL_CLOSE);
+ }
+ if (xconn->protocol < PROTOCOL_NT1) {
+ reply_force_doserror(req, ERRDOS, ERRnofiles);
+ goto out;
+ } else {
+ reply_botherror(req, NT_STATUS_NO_SUCH_FILE,
+ ERRDOS, ERRbadfile);
+ goto out;
+ }
+ }
+
+ /* At this point pdata points to numentries directory entries. */
+
+ /* Set up the return parameter block */
+ SSVAL(params,0,dptr_num);
+ SSVAL(params,2,numentries);
+ SSVAL(params,4,finished);
+ SSVAL(params,6,0); /* Never an EA error */
+ SSVAL(params,8,last_entry_off);
+
+ send_trans2_replies(conn, req, NT_STATUS_OK, params, 10, pdata, PTR_DIFF(p,pdata),
+ max_data_bytes);
+
+ if ((! *directory) && dptr_path(sconn, dptr_num)) {
+ directory = talloc_strdup(talloc_tos(),dptr_path(sconn, dptr_num));
+ if (!directory) {
+ reply_nterror(req, NT_STATUS_NO_MEMORY);
+ }
+ }
+
+ DEBUG( 4, ( "%s mask=%s directory=%s dirtype=%d numentries=%d\n",
+ smb_fn_name(req->cmd),
+ mask, directory, dirtype, numentries ) );
+
+ /*
+ * Force a name mangle here to ensure that the
+ * mask as an 8.3 name is top of the mangled cache.
+ * The reasons for this are subtle. Don't remove
+ * this code unless you know what you are doing
+ * (see PR#13758). JRA.
+ */
+
+ if(!mangle_is_8_3_wildcards( mask, False, conn->params)) {
+ char mangled_name[13];
+ name_to_8_3(mask, mangled_name, True, conn->params);
+ }
+ out:
+
+ if (as_root) {
+ unbecome_root();
+ }
+
+ TALLOC_FREE(smb_dname);
+ return;
+}
+
+static bool smbd_dptr_name_equal(struct dptr_struct *dptr,
+ const char *name1,
+ const char *name2)
+{
+ bool equal;
+
+ if (dptr_case_sensitive(dptr)) {
+ equal = (strcmp(name1, name2) == 0);
+ } else {
+ equal = strequal(name1, name2);
+ }
+
+ return equal;
+}
+
+/****************************************************************************
+ Reply to a TRANS2_FINDNEXT.
+****************************************************************************/
+
+static void call_trans2findnext(connection_struct *conn,
+ struct smb_request *req,
+ char **pparams, int total_params,
+ char **ppdata, int total_data,
+ unsigned int max_data_bytes)
+{
+ /* We must be careful here that we don't return more than the
+ allowed number of data bytes. If this means returning fewer than
+ maxentries then so be it. We assume that the redirector has
+ enough room for the fixed number of parameter bytes it has
+ requested. */
+ char *params = *pparams;
+ char *pdata = *ppdata;
+ char *data_end;
+ int dptr_num;
+ int maxentries;
+ uint16_t info_level;
+ uint32_t resume_key;
+ uint16_t findnext_flags;
+ bool close_after_request;
+ bool close_if_end;
+ bool requires_resume_key;
+ bool continue_bit;
+ char *resume_name = NULL;
+ const char *mask = NULL;
+ const char *directory = NULL;
+ char *p = NULL;
+ uint16_t dirtype;
+ int numentries = 0;
+ int i, last_entry_off=0;
+ bool finished = False;
+ bool dont_descend = False;
+ bool out_of_space = False;
+ int space_remaining;
+ struct ea_list *ea_list = NULL;
+ NTSTATUS ntstatus = NT_STATUS_OK;
+ bool ask_sharemode;
+ TALLOC_CTX *ctx = talloc_tos();
+ struct smbd_server_connection *sconn = req->sconn;
+ bool backup_priv = false;
+ bool as_root = false;
+ files_struct *fsp = NULL;
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+
+ if (total_params < 13) {
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ dptr_num = SVAL(params,0);
+ maxentries = SVAL(params,2);
+ info_level = SVAL(params,4);
+ resume_key = IVAL(params,6);
+ findnext_flags = SVAL(params,10);
+ close_after_request = (findnext_flags & FLAG_TRANS2_FIND_CLOSE);
+ close_if_end = (findnext_flags & FLAG_TRANS2_FIND_CLOSE_IF_END);
+ requires_resume_key = (findnext_flags & FLAG_TRANS2_FIND_REQUIRE_RESUME);
+ continue_bit = (findnext_flags & FLAG_TRANS2_FIND_CONTINUE);
+
+ if (!continue_bit) {
+ /* We only need resume_name if continue_bit is zero. */
+ if (req->posix_pathnames) {
+ srvstr_get_path_posix(ctx,
+ params,
+ req->flags2,
+ &resume_name,
+ params+12,
+ total_params - 12,
+ STR_TERMINATE,
+ &ntstatus);
+ } else {
+ srvstr_get_path(ctx,
+ params,
+ req->flags2,
+ &resume_name,
+ params+12,
+ total_params - 12,
+ STR_TERMINATE,
+ &ntstatus);
+ }
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ /* Win9x or OS/2 can send a resume name of ".." or ".". This will cause the parser to
+ complain (it thinks we're asking for the directory above the shared
+ path or an invalid name). Catch this as the resume name is only compared, never used in
+ a file access. JRA. */
+ srvstr_pull_talloc(ctx, params, req->flags2,
+ &resume_name, params+12,
+ total_params - 12,
+ STR_TERMINATE);
+
+ if (!resume_name || !(ISDOT(resume_name) || ISDOTDOT(resume_name))) {
+ reply_nterror(req, ntstatus);
+ return;
+ }
+ }
+ }
+
+ DBG_NOTICE("dirhandle = %d, max_data_bytes = %u, maxentries = %d, "
+ "close_after_request=%d, close_if_end = %d "
+ "requires_resume_key = %d resume_key = %d "
+ "resume name = %s continue=%d level = %d\n",
+ dptr_num,
+ max_data_bytes,
+ maxentries,
+ close_after_request,
+ close_if_end,
+ requires_resume_key,
+ resume_key,
+ resume_name ? resume_name : "(NULL)",
+ continue_bit,
+ info_level);
+
+ if (!maxentries) {
+ /* W2K3 seems to treat zero as 1. */
+ maxentries = 1;
+ }
+
+ switch (info_level) {
+ case SMB_FIND_INFO_STANDARD:
+ case SMB_FIND_EA_SIZE:
+ case SMB_FIND_EA_LIST:
+ case SMB_FIND_FILE_DIRECTORY_INFO:
+ case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
+ case SMB_FIND_FILE_NAMES_INFO:
+ case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
+ case SMB_FIND_ID_FULL_DIRECTORY_INFO:
+ case SMB_FIND_ID_BOTH_DIRECTORY_INFO:
+ break;
+ case SMB_FIND_FILE_UNIX:
+ case SMB_FIND_FILE_UNIX_INFO2:
+ if (!lp_smb1_unix_extensions()) {
+ reply_nterror(req, NT_STATUS_INVALID_LEVEL);
+ return;
+ }
+ if (!req->posix_pathnames) {
+ reply_nterror(req, NT_STATUS_INVALID_LEVEL);
+ return;
+ }
+ break;
+ default:
+ reply_nterror(req, NT_STATUS_INVALID_LEVEL);
+ return;
+ }
+
+ if (info_level == SMB_FIND_EA_LIST) {
+ uint32_t ea_size;
+
+ if (total_data < 4) {
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ ea_size = IVAL(pdata,0);
+ if (ea_size != total_data) {
+ DBG_NOTICE("Rejecting EA request with incorrect "
+ "total_data=%d (should be %" PRIu32 ")\n",
+ total_data,
+ ea_size);
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ if (!lp_ea_support(SNUM(conn))) {
+ reply_nterror(req, NT_STATUS_EAS_NOT_SUPPORTED);
+ return;
+ }
+
+ /* Pull out the list of names. */
+ ea_list = read_ea_name_list(ctx, pdata + 4, ea_size - 4);
+ if (!ea_list) {
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ }
+
+ if (max_data_bytes + DIR_ENTRY_SAFETY_MARGIN < max_data_bytes) {
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ *ppdata = (char *)SMB_REALLOC(
+ *ppdata, max_data_bytes + DIR_ENTRY_SAFETY_MARGIN);
+ if(*ppdata == NULL) {
+ reply_nterror(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+
+ pdata = *ppdata;
+ data_end = pdata + max_data_bytes + DIR_ENTRY_SAFETY_MARGIN - 1;
+
+ /*
+ * squash valgrind "writev(vector[...]) points to uninitialised byte(s)"
+ * error.
+ */
+ memset(pdata + total_data, 0, (max_data_bytes + DIR_ENTRY_SAFETY_MARGIN) - total_data);
+ /* Realloc the params space */
+ *pparams = (char *)SMB_REALLOC(*pparams, 6*SIZEOFWORD);
+ if(*pparams == NULL ) {
+ reply_nterror(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+
+ params = *pparams;
+
+ /* Check that the dptr is valid */
+ fsp = dptr_fetch_lanman2_fsp(sconn, dptr_num);
+ if (fsp == NULL) {
+ reply_nterror(req, STATUS_NO_MORE_FILES);
+ return;
+ }
+
+ directory = dptr_path(sconn, dptr_num);
+
+ /* Get the wildcard mask from the dptr */
+ if((mask = dptr_wcard(sconn, dptr_num))== NULL) {
+ DEBUG(2,("dptr_num %d has no wildcard\n", dptr_num));
+ reply_nterror(req, STATUS_NO_MORE_FILES);
+ return;
+ }
+
+ /* Get the attr mask from the dptr */
+ dirtype = dptr_attr(sconn, dptr_num);
+
+ backup_priv = dptr_get_priv(fsp->dptr);
+
+ DEBUG(3,("dptr_num is %d, mask = %s, attr = %x, dirptr=(0x%lX) "
+ "backup_priv = %d\n",
+ dptr_num, mask, dirtype,
+ (long)fsp->dptr,
+ (int)backup_priv));
+
+ /* We don't need to check for VOL here as this is returned by
+ a different TRANS2 call. */
+
+ DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n",
+ directory,lp_dont_descend(ctx, lp_sub, SNUM(conn))));
+ if (in_list(directory,lp_dont_descend(ctx, lp_sub, SNUM(conn)),
+ dptr_case_sensitive(fsp->dptr)))
+ dont_descend = True;
+
+ p = pdata;
+ space_remaining = max_data_bytes;
+ out_of_space = False;
+
+ if (backup_priv) {
+ become_root();
+ as_root = true;
+ }
+
+ /*
+ * Seek to the correct position. We no longer use the resume key but
+ * depend on the last file name instead.
+ */
+
+ if(!continue_bit && resume_name && *resume_name) {
+ bool posix_open = (fsp->posix_flags & FSP_POSIX_FLAGS_OPEN);
+ char *last_name_sent = NULL;
+ bool sequential;
+
+ /*
+ * Remember, name_to_8_3 is called by
+ * get_lanman2_dir_entry(), so the resume name
+ * could be mangled. Ensure we check the unmangled name.
+ */
+
+ if (!posix_open &&
+ mangle_is_mangled(resume_name, conn->params)) {
+ char *new_resume_name = NULL;
+ mangle_lookup_name_from_8_3(ctx,
+ resume_name,
+ &new_resume_name,
+ conn->params);
+ if (new_resume_name) {
+ resume_name = new_resume_name;
+ }
+ }
+
+ /*
+ * Fix for NT redirector problem triggered by resume key indexes
+ * changing between directory scans. We now return a resume key of 0
+ * and instead look for the filename to continue from (also given
+ * to us by NT/95/smbfs/smbclient). If no other scans have been done between the
+ * findfirst/findnext (as is usual) then the directory pointer
+ * should already be at the correct place.
+ */
+
+ last_name_sent = smbd_dirptr_get_last_name_sent(fsp->dptr);
+ sequential = smbd_dptr_name_equal(fsp->dptr,
+ resume_name,
+ last_name_sent);
+ if (!sequential) {
+ char *name = NULL;
+ bool found = false;
+
+ dptr_RewindDir(fsp->dptr);
+
+ while ((name = dptr_ReadDirName(talloc_tos(),
+ fsp->dptr)) != NULL) {
+ found = smbd_dptr_name_equal(fsp->dptr,
+ resume_name,
+ name);
+ TALLOC_FREE(name);
+ if (found) {
+ break;
+ }
+ }
+
+ if (!found) {
+ /*
+ * We got a name that used to exist
+ * but does not anymore. Just start
+ * from the beginning. Shown by the
+ * "raw.search.os2 delete" smbtorture
+ * test.
+ */
+ dptr_RewindDir(fsp->dptr);
+ }
+ }
+ } /* end if resume_name && !continue_bit */
+
+ ask_sharemode = fsp_search_ask_sharemode(fsp);
+
+ for (i=0;(i<(int)maxentries) && !finished && !out_of_space ;i++) {
+
+ ntstatus = get_lanman2_dir_entry(ctx,
+ conn,
+ fsp->dptr,
+ req->flags2,
+ mask,
+ dirtype,
+ info_level,
+ requires_resume_key,
+ dont_descend,
+ ask_sharemode,
+ &p,
+ pdata,
+ data_end,
+ space_remaining,
+ &last_entry_off,
+ ea_list);
+ if (NT_STATUS_EQUAL(ntstatus, NT_STATUS_ILLEGAL_CHARACTER)) {
+ /*
+ * Bad character conversion on name. Ignore
+ * this entry.
+ */
+ continue;
+ }
+ if (NT_STATUS_EQUAL(ntstatus, STATUS_MORE_ENTRIES)) {
+ out_of_space = true;
+ } else {
+ finished = !NT_STATUS_IS_OK(ntstatus);
+ }
+
+ if (!finished && !out_of_space) {
+ numentries++;
+ }
+
+ space_remaining = max_data_bytes - PTR_DIFF(p,pdata);
+ }
+
+ DEBUG( 3, ( "%s mask=%s directory=%s dirtype=%d numentries=%d\n",
+ smb_fn_name(req->cmd),
+ mask, directory, dirtype, numentries ) );
+
+ /* Check if we can close the fsp->dptr */
+ if(close_after_request || (finished && close_if_end)) {
+ DBG_INFO("closing dptr_num = %d\n", dptr_num);
+ dptr_num = -1;
+ close_file_free(NULL, &fsp, NORMAL_CLOSE);
+ }
+
+ if (as_root) {
+ unbecome_root();
+ }
+
+ /* Set up the return parameter block */
+ SSVAL(params,0,numentries);
+ SSVAL(params,2,finished);
+ SSVAL(params,4,0); /* Never an EA error */
+ SSVAL(params,6,last_entry_off);
+
+ send_trans2_replies(conn, req, NT_STATUS_OK, params, 8, pdata, PTR_DIFF(p,pdata),
+ max_data_bytes);
+
+ return;
+}
+
+/****************************************************************************
+ Reply to a TRANS2_QFSINFO (query filesystem info).
+****************************************************************************/
+
+static void call_trans2qfsinfo(connection_struct *conn,
+ struct smb_request *req,
+ char **pparams, int total_params,
+ char **ppdata, int total_data,
+ unsigned int max_data_bytes)
+{
+ char *params = *pparams;
+ uint16_t info_level;
+ int data_len = 0;
+ size_t fixed_portion;
+ NTSTATUS status;
+
+ if (total_params < 2) {
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ info_level = SVAL(params,0);
+
+ if (ENCRYPTION_REQUIRED(conn) && !req->encrypted) {
+ if (info_level != SMB_QUERY_CIFS_UNIX_INFO) {
+ DEBUG(0,("call_trans2qfsinfo: encryption required "
+ "and info level 0x%x sent.\n",
+ (unsigned int)info_level));
+ reply_nterror(req, NT_STATUS_ACCESS_DENIED);
+ return;
+ }
+ }
+
+ DEBUG(3,("call_trans2qfsinfo: level = %d\n", info_level));
+
+ status = smbd_do_qfsinfo(req->xconn, conn, req,
+ info_level,
+ req->flags2,
+ max_data_bytes,
+ &fixed_portion,
+ NULL,
+ NULL,
+ ppdata, &data_len);
+ if (!NT_STATUS_IS_OK(status)) {
+ reply_nterror(req, status);
+ return;
+ }
+
+ send_trans2_replies(conn, req, NT_STATUS_OK, params, 0, *ppdata, data_len,
+ max_data_bytes);
+
+ DEBUG( 4, ( "%s info_level = %d\n",
+ smb_fn_name(req->cmd), info_level) );
+
+ return;
+}
+
+/****************************************************************************
+ Reply to a TRANS2_SETFSINFO (set filesystem info).
+****************************************************************************/
+
+static void call_trans2setfsinfo(connection_struct *conn,
+ struct smb_request *req,
+ char **pparams, int total_params,
+ char **ppdata, int total_data,
+ unsigned int max_data_bytes)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ struct smbXsrv_connection *xconn = req->xconn;
+ char *pdata = *ppdata;
+ char *params = *pparams;
+ uint16_t info_level;
+
+ DEBUG(10,("call_trans2setfsinfo: for service [%s]\n",
+ lp_servicename(talloc_tos(), lp_sub, SNUM(conn))));
+
+ /* */
+ if (total_params < 4) {
+ DEBUG(0,("call_trans2setfsinfo: requires total_params(%d) >= 4 bytes!\n",
+ total_params));
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ info_level = SVAL(params,2);
+
+ if (IS_IPC(conn)) {
+ if (info_level != SMB_REQUEST_TRANSPORT_ENCRYPTION &&
+ info_level != SMB_SET_CIFS_UNIX_INFO) {
+ DEBUG(0,("call_trans2setfsinfo: not an allowed "
+ "info level (0x%x) on IPC$.\n",
+ (unsigned int)info_level));
+ reply_nterror(req, NT_STATUS_ACCESS_DENIED);
+ return;
+ }
+ }
+
+ if (ENCRYPTION_REQUIRED(conn) && !req->encrypted) {
+ if (info_level != SMB_REQUEST_TRANSPORT_ENCRYPTION) {
+ DEBUG(0,("call_trans2setfsinfo: encryption required "
+ "and info level 0x%x sent.\n",
+ (unsigned int)info_level));
+ reply_nterror(req, NT_STATUS_ACCESS_DENIED);
+ return;
+ }
+ }
+
+ switch(info_level) {
+ case SMB_SET_CIFS_UNIX_INFO:
+ if (!lp_smb1_unix_extensions()) {
+ DEBUG(2,("call_trans2setfsinfo: "
+ "SMB_SET_CIFS_UNIX_INFO is invalid with "
+ "unix extensions off\n"));
+ reply_nterror(req,
+ NT_STATUS_INVALID_LEVEL);
+ return;
+ }
+
+ /* There should be 12 bytes of capabilities set. */
+ if (total_data < 12) {
+ reply_nterror(
+ req,
+ NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ xconn->smb1.unix_info.client_major = SVAL(pdata,0);
+ xconn->smb1.unix_info.client_minor = SVAL(pdata,2);
+ xconn->smb1.unix_info.client_cap_low = IVAL(pdata,4);
+ xconn->smb1.unix_info.client_cap_high = IVAL(pdata,8);
+
+ /* Just print these values for now. */
+ DBG_DEBUG("set unix_info info. "
+ "major = %"PRIu16", minor = %"PRIu16
+ "cap_low = 0x%"PRIx32", "
+ "cap_high = 0x%"PRIx32"\n",
+ xconn->smb1.unix_info.client_major,
+ xconn->smb1.unix_info.client_minor,
+ xconn->smb1.unix_info.client_cap_low,
+ xconn->smb1.unix_info.client_cap_high);
+
+ /*
+ * Here is where we must switch to posix
+ * pathname processing...
+ */
+ if (xconn->smb1.unix_info.client_cap_low &
+ CIFS_UNIX_POSIX_PATHNAMES_CAP)
+ {
+ lp_set_posix_pathnames();
+ mangle_change_to_posix();
+ }
+
+ if ((xconn->smb1.unix_info.client_cap_low &
+ CIFS_UNIX_FCNTL_LOCKS_CAP) &&
+ !(xconn->smb1.unix_info.client_cap_low &
+ CIFS_UNIX_POSIX_PATH_OPERATIONS_CAP))
+ {
+ /* Client that knows how to do posix locks,
+ * but not posix open/mkdir operations. Set a
+ * default type for read/write checks. */
+
+ lp_set_posix_default_cifsx_readwrite_locktype(
+ POSIX_LOCK);
+
+ }
+ break;
+
+ case SMB_REQUEST_TRANSPORT_ENCRYPTION:
+ {
+ NTSTATUS status;
+ size_t param_len = 0;
+ size_t data_len = total_data;
+
+ if (!lp_smb1_unix_extensions()) {
+ reply_nterror(
+ req,
+ NT_STATUS_INVALID_LEVEL);
+ return;
+ }
+
+ if (lp_server_smb_encrypt(SNUM(conn)) ==
+ SMB_ENCRYPTION_OFF) {
+ reply_nterror(
+ req,
+ NT_STATUS_NOT_SUPPORTED);
+ return;
+ }
+
+ if (xconn->smb1.echo_handler.trusted_fde) {
+ DEBUG( 2,("call_trans2setfsinfo: "
+ "request transport encryption disabled"
+ "with 'fork echo handler = yes'\n"));
+ reply_nterror(
+ req,
+ NT_STATUS_NOT_SUPPORTED);
+ return;
+ }
+
+ DEBUG( 4,("call_trans2setfsinfo: "
+ "request transport encryption.\n"));
+
+ status = srv_request_encryption_setup(conn,
+ (unsigned char **)ppdata,
+ &data_len,
+ (unsigned char **)pparams,
+ &param_len);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) &&
+ !NT_STATUS_IS_OK(status)) {
+ reply_nterror(req, status);
+ return;
+ }
+
+ send_trans2_replies(conn, req,
+ NT_STATUS_OK,
+ *pparams,
+ param_len,
+ *ppdata,
+ data_len,
+ max_data_bytes);
+
+ if (NT_STATUS_IS_OK(status)) {
+ /* Server-side transport
+ * encryption is now *on*. */
+ status = srv_encryption_start(conn);
+ if (!NT_STATUS_IS_OK(status)) {
+ char *reason = talloc_asprintf(talloc_tos(),
+ "Failure in setting "
+ "up encrypted transport: %s",
+ nt_errstr(status));
+ exit_server_cleanly(reason);
+ }
+ }
+ return;
+ }
+
+ case SMB_FS_QUOTA_INFORMATION:
+ {
+ NTSTATUS status;
+ DATA_BLOB qdata = {
+ .data = (uint8_t *)pdata,
+ .length = total_data
+ };
+ files_struct *fsp = NULL;
+ fsp = file_fsp(req, SVAL(params,0));
+
+ status = smb_set_fsquota(conn,
+ req,
+ fsp,
+ &qdata);
+ if (!NT_STATUS_IS_OK(status)) {
+ reply_nterror(req, status);
+ return;
+ }
+ break;
+ }
+ default:
+ DEBUG(3,("call_trans2setfsinfo: unknown level (0x%X) not implemented yet.\n",
+ info_level));
+ reply_nterror(req, NT_STATUS_INVALID_LEVEL);
+ return;
+ break;
+ }
+
+ /*
+ * sending this reply works fine,
+ * but I'm not sure it's the same
+ * like windows do...
+ * --metze
+ */
+ reply_smb1_outbuf(req, 10, 0);
+}
+
+/****************************************************************************
+ Reply to a TRANSACT2_QFILEINFO on a PIPE !
+****************************************************************************/
+
+static void call_trans2qpipeinfo(connection_struct *conn,
+ struct smb_request *req,
+ files_struct *fsp,
+ uint16_t info_level,
+ unsigned int tran_call,
+ char **pparams, int total_params,
+ char **ppdata, int total_data,
+ unsigned int max_data_bytes)
+{
+ char *params = *pparams;
+ char *pdata = *ppdata;
+ unsigned int data_size = 0;
+ unsigned int param_size = 2;
+
+ if (!fsp_is_np(fsp)) {
+ reply_nterror(req, NT_STATUS_INVALID_HANDLE);
+ return;
+ }
+
+ *pparams = (char *)SMB_REALLOC(*pparams,2);
+ if (*pparams == NULL) {
+ reply_nterror(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ params = *pparams;
+ SSVAL(params,0,0);
+ if (max_data_bytes + DIR_ENTRY_SAFETY_MARGIN < max_data_bytes) {
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ data_size = max_data_bytes + DIR_ENTRY_SAFETY_MARGIN;
+ *ppdata = (char *)SMB_REALLOC(*ppdata, data_size);
+ if (*ppdata == NULL ) {
+ reply_nterror(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ pdata = *ppdata;
+
+ switch (info_level) {
+ case SMB_FILE_STANDARD_INFORMATION:
+ memset(pdata,0,24);
+ SOFF_T(pdata,0,4096LL);
+ SIVAL(pdata,16,1);
+ SIVAL(pdata,20,1);
+ data_size = 24;
+ break;
+
+ default:
+ reply_nterror(req, NT_STATUS_INVALID_LEVEL);
+ return;
+ }
+
+ send_trans2_replies(conn, req, NT_STATUS_OK, params, param_size, *ppdata, data_size,
+ max_data_bytes);
+}
+
+static void handle_trans2qfilepathinfo_result(
+ connection_struct *conn,
+ struct smb_request *req,
+ uint16_t info_level,
+ NTSTATUS status,
+ char *pdata,
+ int data_return_size,
+ size_t fixed_portion,
+ unsigned int max_data_bytes)
+{
+ char params[2] = { 0, 0, };
+ int param_size = 2;
+
+ /*
+ * draft-leach-cifs-v1-spec-02.txt
+ * 4.2.14 TRANS2_QUERY_PATH_INFORMATION: Get File Attributes given Path
+ * says:
+ *
+ * The requested information is placed in the Data portion of the
+ * transaction response. For the information levels greater than 0x100,
+ * the transaction response has 1 parameter word which should be
+ * ignored by the client.
+ *
+ * However Windows only follows this rule for the IS_NAME_VALID call.
+ */
+ switch (info_level) {
+ case SMB_INFO_IS_NAME_VALID:
+ param_size = 0;
+ break;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ if (open_was_deferred(req->xconn, req->mid)) {
+ /* We have re-scheduled this call. */
+ return;
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
+ bool ok = defer_smb1_sharing_violation(req);
+ if (ok) {
+ return;
+ }
+ }
+ reply_nterror(req, status);
+ return;
+ }
+
+ if (fixed_portion > max_data_bytes) {
+ reply_nterror(req, NT_STATUS_INFO_LENGTH_MISMATCH);
+ return;
+ }
+
+ send_trans2_replies(
+ conn,
+ req,
+ NT_STATUS_OK,
+ params,
+ param_size,
+ pdata,
+ data_return_size,
+ max_data_bytes);
+}
+
+/****************************************************************************
+ Reply to a TRANS2_QFILEPATHINFO or TRANSACT2_QFILEINFO (query file info by
+ file name or file id).
+****************************************************************************/
+
+static void call_trans2qfilepathinfo(connection_struct *conn,
+ struct smb_request *req,
+ unsigned int tran_call,
+ uint16_t info_level,
+ struct smb_filename *smb_fname,
+ struct files_struct *fsp,
+ bool delete_pending,
+ struct timespec write_time_ts,
+ char **pparams, int total_params,
+ char **ppdata, int total_data,
+ unsigned int max_data_bytes)
+{
+ char *params = *pparams;
+ char *pdata = *ppdata;
+ unsigned int data_size = 0;
+ struct ea_list *ea_list = NULL;
+ size_t fixed_portion;
+ NTSTATUS status = NT_STATUS_OK;
+
+ DEBUG(3,("call_trans2qfilepathinfo %s (%s) level=%d call=%d "
+ "total_data=%d\n", smb_fname_str_dbg(smb_fname),
+ fsp_fnum_dbg(fsp),
+ info_level,tran_call,total_data));
+
+ /* Pull out any data sent here before we realloc. */
+ switch (info_level) {
+ case SMB_INFO_QUERY_EAS_FROM_LIST:
+ {
+ /* Pull any EA list from the data portion. */
+ uint32_t ea_size;
+
+ if (total_data < 4) {
+ reply_nterror(
+ req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ ea_size = IVAL(pdata,0);
+
+ if (total_data > 0 && ea_size != total_data) {
+ DEBUG(4,("call_trans2qfilepathinfo: Rejecting EA request with incorrect \
+total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pdata,0) ));
+ reply_nterror(
+ req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ if (!lp_ea_support(SNUM(conn))) {
+ reply_nterror(req, NT_STATUS_EAS_NOT_SUPPORTED);
+ return;
+ }
+
+ /* Pull out the list of names. */
+ ea_list = read_ea_name_list(req, pdata + 4, ea_size - 4);
+ if (!ea_list) {
+ reply_nterror(
+ req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ *pparams = (char *)SMB_REALLOC(*pparams,2);
+ if (*pparams == NULL) {
+ reply_nterror(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ params = *pparams;
+ SSVAL(params,0,0);
+
+ if ((info_level & SMB2_INFO_SPECIAL) == SMB2_INFO_SPECIAL) {
+ /*
+ * We use levels that start with 0xFF00
+ * internally to represent SMB2 specific levels
+ */
+ reply_nterror(req, NT_STATUS_INVALID_LEVEL);
+ return;
+ }
+
+ status = smbd_do_qfilepathinfo(conn, req, req, info_level,
+ fsp, smb_fname,
+ delete_pending, write_time_ts,
+ ea_list,
+ req->flags2, max_data_bytes,
+ &fixed_portion,
+ ppdata, &data_size);
+
+ handle_trans2qfilepathinfo_result(
+ conn,
+ req,
+ info_level,
+ status,
+ *ppdata,
+ data_size,
+ fixed_portion,
+ max_data_bytes);
+}
+
+static NTSTATUS smb_q_unix_basic(
+ struct connection_struct *conn,
+ struct smb_request *req,
+ struct smb_filename *smb_fname,
+ struct files_struct *fsp,
+ char **ppdata,
+ int *ptotal_data)
+{
+ const int total_data = 100;
+
+ *ppdata = SMB_REALLOC(*ppdata, total_data);
+ if (*ppdata == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ store_file_unix_basic(conn, *ppdata, fsp, &smb_fname->st);
+
+ *ptotal_data = total_data;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS smb_q_unix_info2(
+ struct connection_struct *conn,
+ struct smb_request *req,
+ struct smb_filename *smb_fname,
+ struct files_struct *fsp,
+ char **ppdata,
+ int *ptotal_data)
+{
+ const int total_data = 116;
+
+ *ppdata = SMB_REALLOC(*ppdata, total_data);
+ if (*ppdata == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ store_file_unix_basic_info2(conn, *ppdata, fsp, &smb_fname->st);
+
+ *ptotal_data = total_data;
+
+ return NT_STATUS_OK;
+}
+
+#if defined(HAVE_POSIX_ACLS)
+/****************************************************************************
+ Utility function to open a fsp for a POSIX handle operation.
+****************************************************************************/
+
+static NTSTATUS get_posix_fsp(connection_struct *conn,
+ struct smb_request *req,
+ struct smb_filename *smb_fname,
+ uint32_t access_mask,
+ files_struct **ret_fsp)
+{
+ NTSTATUS status;
+ uint32_t create_disposition = FILE_OPEN;
+ uint32_t share_access = FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE;
+ struct smb2_create_blobs *posx = NULL;
+
+ /*
+ * Only FILE_FLAG_POSIX_SEMANTICS matters on existing files,
+ * but set reasonable defaults.
+ */
+ uint32_t file_attributes = 0664;
+ uint32_t oplock = NO_OPLOCK;
+ uint32_t create_options = FILE_NON_DIRECTORY_FILE;
+
+ /* File or directory must exist. */
+ if (!VALID_STAT(smb_fname->st)) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ /* Cannot be a symlink. */
+ if (S_ISLNK(smb_fname->st.st_ex_mode)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ /* Set options correctly for directory open. */
+ if (S_ISDIR(smb_fname->st.st_ex_mode)) {
+ /*
+ * Only FILE_FLAG_POSIX_SEMANTICS matters on existing
+ * directories, but set reasonable defaults.
+ */
+ file_attributes = 0775;
+ create_options = FILE_DIRECTORY_FILE;
+ }
+
+ status = make_smb2_posix_create_ctx(
+ talloc_tos(), &posx, file_attributes);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_WARNING("make_smb2_posix_create_ctx failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = SMB_VFS_CREATE_FILE(
+ conn, /* conn */
+ req, /* req */
+ NULL, /* dirfsp */
+ smb_fname, /* fname */
+ access_mask, /* access_mask */
+ share_access, /* share_access */
+ create_disposition,/* create_disposition*/
+ create_options, /* create_options */
+ file_attributes,/* file_attributes */
+ oplock, /* oplock_request */
+ NULL, /* lease */
+ 0, /* allocation_size */
+ 0, /* private_flags */
+ NULL, /* sd */
+ NULL, /* ea_list */
+ ret_fsp, /* result */
+ NULL, /* pinfo */
+ posx, /* in_context */
+ NULL); /* out_context */
+
+done:
+ TALLOC_FREE(posx);
+ return status;
+}
+
+/****************************************************************************
+ Utility function to count the number of entries in a POSIX acl.
+****************************************************************************/
+
+static unsigned int count_acl_entries(connection_struct *conn, SMB_ACL_T posix_acl)
+{
+ unsigned int ace_count = 0;
+ int entry_id = SMB_ACL_FIRST_ENTRY;
+ SMB_ACL_ENTRY_T entry;
+
+ while ( posix_acl && (sys_acl_get_entry(posix_acl, entry_id, &entry) == 1)) {
+ entry_id = SMB_ACL_NEXT_ENTRY;
+ ace_count++;
+ }
+ return ace_count;
+}
+
+/****************************************************************************
+ Utility function to marshall a POSIX acl into wire format.
+****************************************************************************/
+
+static bool marshall_posix_acl(connection_struct *conn, char *pdata, SMB_STRUCT_STAT *pst, SMB_ACL_T posix_acl)
+{
+ int entry_id = SMB_ACL_FIRST_ENTRY;
+ SMB_ACL_ENTRY_T entry;
+
+ while ( posix_acl && (sys_acl_get_entry(posix_acl, entry_id, &entry) == 1)) {
+ SMB_ACL_TAG_T tagtype;
+ SMB_ACL_PERMSET_T permset;
+ unsigned char perms = 0;
+ unsigned int own_grp;
+
+ entry_id = SMB_ACL_NEXT_ENTRY;
+
+ if (sys_acl_get_tag_type(entry, &tagtype) == -1) {
+ DEBUG(0,("marshall_posix_acl: SMB_VFS_SYS_ACL_GET_TAG_TYPE failed.\n"));
+ return False;
+ }
+
+ if (sys_acl_get_permset(entry, &permset) == -1) {
+ DEBUG(0,("marshall_posix_acl: SMB_VFS_SYS_ACL_GET_PERMSET failed.\n"));
+ return False;
+ }
+
+ perms |= (sys_acl_get_perm(permset, SMB_ACL_READ) ? SMB_POSIX_ACL_READ : 0);
+ perms |= (sys_acl_get_perm(permset, SMB_ACL_WRITE) ? SMB_POSIX_ACL_WRITE : 0);
+ perms |= (sys_acl_get_perm(permset, SMB_ACL_EXECUTE) ? SMB_POSIX_ACL_EXECUTE : 0);
+
+ SCVAL(pdata,1,perms);
+
+ switch (tagtype) {
+ case SMB_ACL_USER_OBJ:
+ SCVAL(pdata,0,SMB_POSIX_ACL_USER_OBJ);
+ own_grp = (unsigned int)pst->st_ex_uid;
+ SIVAL(pdata,2,own_grp);
+ SIVAL(pdata,6,0);
+ break;
+ case SMB_ACL_USER:
+ {
+ uid_t *puid = (uid_t *)sys_acl_get_qualifier(entry);
+ if (!puid) {
+ DEBUG(0,("marshall_posix_acl: SMB_VFS_SYS_ACL_GET_QUALIFIER failed.\n"));
+ return False;
+ }
+ own_grp = (unsigned int)*puid;
+ SCVAL(pdata,0,SMB_POSIX_ACL_USER);
+ SIVAL(pdata,2,own_grp);
+ SIVAL(pdata,6,0);
+ break;
+ }
+ case SMB_ACL_GROUP_OBJ:
+ SCVAL(pdata,0,SMB_POSIX_ACL_GROUP_OBJ);
+ own_grp = (unsigned int)pst->st_ex_gid;
+ SIVAL(pdata,2,own_grp);
+ SIVAL(pdata,6,0);
+ break;
+ case SMB_ACL_GROUP:
+ {
+ gid_t *pgid= (gid_t *)sys_acl_get_qualifier(entry);
+ if (!pgid) {
+ DEBUG(0,("marshall_posix_acl: SMB_VFS_SYS_ACL_GET_QUALIFIER failed.\n"));
+ return False;
+ }
+ own_grp = (unsigned int)*pgid;
+ SCVAL(pdata,0,SMB_POSIX_ACL_GROUP);
+ SIVAL(pdata,2,own_grp);
+ SIVAL(pdata,6,0);
+ break;
+ }
+ case SMB_ACL_MASK:
+ SCVAL(pdata,0,SMB_POSIX_ACL_MASK);
+ SIVAL(pdata,2,0xFFFFFFFF);
+ SIVAL(pdata,6,0xFFFFFFFF);
+ break;
+ case SMB_ACL_OTHER:
+ SCVAL(pdata,0,SMB_POSIX_ACL_OTHER);
+ SIVAL(pdata,2,0xFFFFFFFF);
+ SIVAL(pdata,6,0xFFFFFFFF);
+ break;
+ default:
+ DEBUG(0,("marshall_posix_acl: unknown tagtype.\n"));
+ return False;
+ }
+ pdata += SMB_POSIX_ACL_ENTRY_SIZE;
+ }
+
+ return True;
+}
+#endif
+
+static NTSTATUS smb_q_posix_acl(
+ struct connection_struct *conn,
+ struct smb_request *req,
+ struct smb_filename *smb_fname,
+ struct files_struct *fsp,
+ char **ppdata,
+ int *ptotal_data)
+{
+#if !defined(HAVE_POSIX_ACLS)
+ return NT_STATUS_INVALID_LEVEL;
+#else
+ char *pdata = NULL;
+ SMB_ACL_T file_acl = NULL;
+ SMB_ACL_T def_acl = NULL;
+ uint16_t num_file_acls = 0;
+ uint16_t num_def_acls = 0;
+ unsigned int size_needed = 0;
+ NTSTATUS status;
+ bool ok;
+ bool close_fsp = false;
+
+ /*
+ * Ensure we always operate on a file descriptor, not just
+ * the filename.
+ */
+ if (fsp == NULL || !fsp->fsp_flags.is_fsa) {
+ uint32_t access_mask = SEC_STD_READ_CONTROL|
+ FILE_READ_ATTRIBUTES|
+ FILE_WRITE_ATTRIBUTES;
+
+ status = get_posix_fsp(conn,
+ req,
+ smb_fname,
+ access_mask,
+ &fsp);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+ close_fsp = true;
+ }
+
+ SMB_ASSERT(fsp != NULL);
+
+ status = refuse_symlink_fsp(fsp);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ file_acl = SMB_VFS_SYS_ACL_GET_FD(fsp, SMB_ACL_TYPE_ACCESS,
+ talloc_tos());
+
+ if (file_acl == NULL && no_acl_syscall_error(errno)) {
+ DBG_INFO("ACLs not implemented on "
+ "filesystem containing %s\n",
+ fsp_str_dbg(fsp));
+ status = NT_STATUS_NOT_IMPLEMENTED;
+ goto out;
+ }
+
+ if (S_ISDIR(fsp->fsp_name->st.st_ex_mode)) {
+ /*
+ * We can only have default POSIX ACLs on
+ * directories.
+ */
+ if (!fsp->fsp_flags.is_directory) {
+ DBG_INFO("Non-directory open %s\n",
+ fsp_str_dbg(fsp));
+ status = NT_STATUS_INVALID_HANDLE;
+ goto out;
+ }
+ def_acl = SMB_VFS_SYS_ACL_GET_FD(fsp,
+ SMB_ACL_TYPE_DEFAULT,
+ talloc_tos());
+ def_acl = free_empty_sys_acl(conn, def_acl);
+ }
+
+ num_file_acls = count_acl_entries(conn, file_acl);
+ num_def_acls = count_acl_entries(conn, def_acl);
+
+ /* Wrap checks. */
+ if (num_file_acls + num_def_acls < num_file_acls) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto out;
+ }
+
+ size_needed = num_file_acls + num_def_acls;
+
+ /*
+ * (size_needed * SMB_POSIX_ACL_ENTRY_SIZE) must be less
+ * than UINT_MAX, so check by division.
+ */
+ if (size_needed > (UINT_MAX/SMB_POSIX_ACL_ENTRY_SIZE)) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto out;
+ }
+
+ size_needed = size_needed*SMB_POSIX_ACL_ENTRY_SIZE;
+ if (size_needed + SMB_POSIX_ACL_HEADER_SIZE < size_needed) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto out;
+ }
+ size_needed += SMB_POSIX_ACL_HEADER_SIZE;
+
+ *ppdata = SMB_REALLOC(*ppdata, size_needed);
+ if (*ppdata == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ pdata = *ppdata;
+
+ SSVAL(pdata,0,SMB_POSIX_ACL_VERSION);
+ SSVAL(pdata,2,num_file_acls);
+ SSVAL(pdata,4,num_def_acls);
+ pdata += SMB_POSIX_ACL_HEADER_SIZE;
+
+ ok = marshall_posix_acl(conn,
+ pdata,
+ &fsp->fsp_name->st,
+ file_acl);
+ if (!ok) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto out;
+ }
+ pdata += (num_file_acls*SMB_POSIX_ACL_ENTRY_SIZE);
+
+ ok = marshall_posix_acl(conn,
+ pdata,
+ &fsp->fsp_name->st,
+ def_acl);
+ if (!ok) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto out;
+ }
+
+ *ptotal_data = size_needed;
+ status = NT_STATUS_OK;
+
+ out:
+
+ if (close_fsp) {
+ /*
+ * Ensure the stat struct in smb_fname is up to
+ * date. Structure copy.
+ */
+ smb_fname->st = fsp->fsp_name->st;
+ (void)close_file_free(req, &fsp, NORMAL_CLOSE);
+ }
+
+ TALLOC_FREE(file_acl);
+ TALLOC_FREE(def_acl);
+ return status;
+#endif
+}
+
+static NTSTATUS smb_q_posix_symlink(
+ struct connection_struct *conn,
+ struct smb_request *req,
+ struct smb_filename *smb_fname,
+ char **ppdata,
+ int *ptotal_data)
+{
+ char buffer[PATH_MAX+1];
+ size_t needed, len;
+ int link_len;
+ char *pdata = NULL;
+ struct smb_filename *parent_fname = NULL;
+ struct smb_filename *base_name = NULL;
+ NTSTATUS status;
+
+ DBG_DEBUG("SMB_QUERY_FILE_UNIX_LINK for file %s\n",
+ smb_fname_str_dbg(smb_fname));
+
+ if (!S_ISLNK(smb_fname->st.st_ex_mode)) {
+ return NT_STATUS_DOS(ERRSRV, ERRbadlink);
+ }
+
+ status = parent_pathref(
+ talloc_tos(),
+ conn->cwd_fsp,
+ smb_fname,
+ &parent_fname,
+ &base_name);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("parent_pathref failed: %s\n", nt_errstr(status));
+ return status;
+ }
+
+ link_len = SMB_VFS_READLINKAT(
+ conn,
+ parent_fname->fsp,
+ base_name,
+ buffer,
+ sizeof(buffer)-1);
+ TALLOC_FREE(parent_fname);
+
+ if (link_len == -1) {
+ status = map_nt_error_from_unix(errno);
+ DBG_DEBUG("READLINKAT failed: %s\n", nt_errstr(status));
+ return status;
+ }
+ if (link_len >= sizeof(buffer)) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ buffer[link_len] = 0;
+
+ needed = (link_len+1)*2;
+
+ *ppdata = SMB_REALLOC(*ppdata, needed);
+ if (*ppdata == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ pdata = *ppdata;
+
+ status = srvstr_push(
+ pdata,
+ req->flags2,
+ pdata,
+ buffer,
+ needed,
+ STR_TERMINATE,
+ &len);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ *ptotal_data = len;
+
+ return NT_STATUS_OK;
+}
+
+static void call_trans2qpathinfo(
+ connection_struct *conn,
+ struct smb_request *req,
+ char **pparams,
+ int total_params,
+ char **ppdata,
+ int total_data,
+ unsigned int max_data_bytes)
+{
+ char *params = *pparams;
+ uint16_t info_level;
+ struct smb_filename *smb_fname = NULL;
+ bool delete_pending = False;
+ struct timespec write_time_ts = { .tv_sec = 0, };
+ struct files_struct *dirfsp = NULL;
+ files_struct *fsp = NULL;
+ struct file_id fileid;
+ uint32_t name_hash;
+ char *fname = NULL;
+ uint32_t ucf_flags = ucf_flags_from_smb_request(req);
+ NTTIME twrp = 0;
+ bool info_level_handled;
+ NTSTATUS status = NT_STATUS_OK;
+ int ret;
+
+ if (!params) {
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+
+ /* qpathinfo */
+ if (total_params < 7) {
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ info_level = SVAL(params,0);
+
+ DBG_NOTICE("TRANSACT2_QPATHINFO: level = %d\n", info_level);
+
+ if (INFO_LEVEL_IS_UNIX(info_level)) {
+ if (!lp_smb1_unix_extensions()) {
+ reply_nterror(req, NT_STATUS_INVALID_LEVEL);
+ return;
+ }
+ if (!req->posix_pathnames) {
+ reply_nterror(req, NT_STATUS_INVALID_LEVEL);
+ return;
+ }
+ }
+
+ if (req->posix_pathnames) {
+ srvstr_get_path_posix(req,
+ params,
+ req->flags2,
+ &fname,
+ &params[6],
+ total_params - 6,
+ STR_TERMINATE,
+ &status);
+ } else {
+ srvstr_get_path(req,
+ params,
+ req->flags2,
+ &fname,
+ &params[6],
+ total_params - 6,
+ STR_TERMINATE,
+ &status);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ reply_nterror(req, status);
+ return;
+ }
+
+ if (ucf_flags & UCF_GMT_PATHNAME) {
+ extract_snapshot_token(fname, &twrp);
+ }
+ status = smb1_strip_dfs_path(req, &ucf_flags, &fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ reply_nterror(req, status);
+ return;
+ }
+ status = filename_convert_dirfsp(req,
+ conn,
+ fname,
+ ucf_flags,
+ twrp,
+ &dirfsp,
+ &smb_fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) {
+ reply_botherror(req,
+ NT_STATUS_PATH_NOT_COVERED,
+ ERRSRV, ERRbadpath);
+ return;
+ }
+ reply_nterror(req, status);
+ return;
+ }
+
+ /*
+ * qpathinfo must operate on an existing file, so we
+ * can exit early if filename_convert_dirfsp() returned the
+ * "new file" NT_STATUS_OK, !VALID_STAT case.
+ */
+
+ if (!VALID_STAT(smb_fname->st)) {
+ reply_nterror(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return;
+ }
+
+ /*
+ * smb_fname->fsp may be NULL if smb_fname points at a symlink
+ * and we're in POSIX context, so be careful when using fsp
+ * below, it can still be NULL.
+ */
+ fsp = smb_fname->fsp;
+
+ /* If this is a stream, check if there is a delete_pending. */
+ if ((conn->fs_capabilities & FILE_NAMED_STREAMS)
+ && is_ntfs_stream_smb_fname(smb_fname)) {
+ struct smb_filename *smb_fname_base;
+
+ /* Create an smb_filename with stream_name == NULL. */
+ smb_fname_base = synthetic_smb_fname(
+ talloc_tos(),
+ smb_fname->base_name,
+ NULL,
+ NULL,
+ smb_fname->twrp,
+ smb_fname->flags);
+ if (smb_fname_base == NULL) {
+ reply_nterror(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+
+ ret = vfs_stat(conn, smb_fname_base);
+ if (ret != 0) {
+ DBG_NOTICE("vfs_stat of %s failed "
+ "(%s)\n",
+ smb_fname_str_dbg(smb_fname_base),
+ strerror(errno));
+ TALLOC_FREE(smb_fname_base);
+ reply_nterror(req,
+ map_nt_error_from_unix(errno));
+ return;
+ }
+
+ status = file_name_hash(conn,
+ smb_fname_str_dbg(smb_fname_base),
+ &name_hash);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(smb_fname_base);
+ reply_nterror(req, status);
+ return;
+ }
+
+ fileid = vfs_file_id_from_sbuf(conn,
+ &smb_fname_base->st);
+ TALLOC_FREE(smb_fname_base);
+ get_file_infos(fileid, name_hash, &delete_pending, NULL);
+ if (delete_pending) {
+ reply_nterror(req, NT_STATUS_DELETE_PENDING);
+ return;
+ }
+ }
+
+ status = file_name_hash(conn,
+ smb_fname_str_dbg(smb_fname),
+ &name_hash);
+ if (!NT_STATUS_IS_OK(status)) {
+ reply_nterror(req, status);
+ return;
+ }
+
+ if (fsp_getinfo_ask_sharemode(fsp)) {
+ fileid = vfs_file_id_from_sbuf(conn, &smb_fname->st);
+ get_file_infos(fileid, name_hash, &delete_pending,
+ &write_time_ts);
+ }
+
+ if (delete_pending) {
+ reply_nterror(req, NT_STATUS_DELETE_PENDING);
+ return;
+ }
+
+ info_level_handled = true; /* Untouched in switch cases below */
+
+ switch (info_level) {
+
+ default:
+ info_level_handled = false;
+ break;
+
+ case SMB_QUERY_FILE_UNIX_BASIC:
+ status = smb_q_unix_basic(
+ conn,
+ req,
+ smb_fname,
+ smb_fname->fsp,
+ ppdata,
+ &total_data);
+ break;
+
+ case SMB_QUERY_FILE_UNIX_INFO2:
+ status = smb_q_unix_info2(
+ conn,
+ req,
+ smb_fname,
+ smb_fname->fsp,
+ ppdata,
+ &total_data);
+ break;
+
+ case SMB_QUERY_POSIX_ACL:
+ status = smb_q_posix_acl(
+ conn,
+ req,
+ smb_fname,
+ smb_fname->fsp,
+ ppdata,
+ &total_data);
+ break;
+
+ case SMB_QUERY_FILE_UNIX_LINK:
+ status = smb_q_posix_symlink(
+ conn,
+ req,
+ smb_fname,
+ ppdata,
+ &total_data);
+ break;
+ }
+
+ if (info_level_handled) {
+ handle_trans2qfilepathinfo_result(
+ conn,
+ req,
+ info_level,
+ status,
+ *ppdata,
+ total_data,
+ total_data,
+ max_data_bytes);
+ return;
+ }
+
+ call_trans2qfilepathinfo(
+ conn,
+ req,
+ TRANSACT2_QPATHINFO,
+ info_level,
+ smb_fname,
+ fsp,
+ false,
+ write_time_ts,
+ pparams,
+ total_params,
+ ppdata,
+ total_data,
+ max_data_bytes);
+}
+
+static NTSTATUS smb_q_posix_lock(
+ struct connection_struct *conn,
+ struct smb_request *req,
+ struct files_struct *fsp,
+ char **ppdata,
+ int *ptotal_data)
+{
+ char *pdata = *ppdata;
+ int total_data = *ptotal_data;
+ uint64_t count;
+ uint64_t offset;
+ uint64_t smblctx;
+ enum brl_type lock_type;
+ NTSTATUS status;
+
+ if (fsp->fsp_flags.is_pathref || (fsp_get_io_fd(fsp) == -1)) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (total_data != POSIX_LOCK_DATA_SIZE) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ switch (SVAL(pdata, POSIX_LOCK_TYPE_OFFSET)) {
+ case POSIX_LOCK_TYPE_READ:
+ lock_type = READ_LOCK;
+ break;
+ case POSIX_LOCK_TYPE_WRITE:
+ lock_type = WRITE_LOCK;
+ break;
+ case POSIX_LOCK_TYPE_UNLOCK:
+ default:
+ /* There's no point in asking for an unlock... */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ smblctx = (uint64_t)IVAL(pdata, POSIX_LOCK_PID_OFFSET);
+ offset = BVAL(pdata,POSIX_LOCK_START_OFFSET);
+ count = BVAL(pdata,POSIX_LOCK_LEN_OFFSET);
+
+ status = query_lock(
+ fsp,
+ &smblctx,
+ &count,
+ &offset,
+ &lock_type,
+ POSIX_LOCK);
+
+ if (NT_STATUS_IS_OK(status)) {
+ /*
+ * For success we just return a copy of what we sent
+ * with the lock type set to POSIX_LOCK_TYPE_UNLOCK.
+ */
+ SSVAL(pdata, POSIX_LOCK_TYPE_OFFSET, POSIX_LOCK_TYPE_UNLOCK);
+ return NT_STATUS_OK;
+ }
+
+ if (!ERROR_WAS_LOCK_DENIED(status)) {
+ DBG_DEBUG("query_lock() failed: %s\n", nt_errstr(status));
+ return status;
+ }
+
+ /*
+ * Here we need to report who has it locked.
+ */
+
+ SSVAL(pdata, POSIX_LOCK_TYPE_OFFSET, lock_type);
+ SSVAL(pdata, POSIX_LOCK_FLAGS_OFFSET, 0);
+ SIVAL(pdata, POSIX_LOCK_PID_OFFSET, (uint32_t)smblctx);
+ SBVAL(pdata, POSIX_LOCK_START_OFFSET, offset);
+ SBVAL(pdata, POSIX_LOCK_LEN_OFFSET, count);
+
+ return NT_STATUS_OK;
+}
+
+static void call_trans2qfileinfo(
+ connection_struct *conn,
+ struct smb_request *req,
+ char **pparams,
+ int total_params,
+ char **ppdata,
+ int total_data,
+ unsigned int max_data_bytes)
+{
+ char *params = *pparams;
+ uint16_t info_level;
+ struct smb_filename *smb_fname = NULL;
+ bool delete_pending = False;
+ struct timespec write_time_ts = { .tv_sec = 0, };
+ files_struct *fsp = NULL;
+ struct file_id fileid;
+ bool info_level_handled;
+ NTSTATUS status = NT_STATUS_OK;
+ int ret;
+
+ if (params == NULL) {
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ if (total_params < 4) {
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ fsp = file_fsp(req, SVAL(params,0));
+ info_level = SVAL(params,2);
+
+ if (IS_IPC(conn)) {
+ call_trans2qpipeinfo(
+ conn,
+ req,
+ fsp,
+ info_level,
+ TRANSACT2_QFILEINFO,
+ pparams,
+ total_params,
+ ppdata,
+ total_data,
+ max_data_bytes);
+ return;
+ }
+
+ DBG_NOTICE("TRANSACT2_QFILEINFO: level = %d\n", info_level);
+
+ if (INFO_LEVEL_IS_UNIX(info_level)) {
+ if (!lp_smb1_unix_extensions()) {
+ reply_nterror(req, NT_STATUS_INVALID_LEVEL);
+ return;
+ }
+ if (!req->posix_pathnames) {
+ reply_nterror(req, NT_STATUS_INVALID_LEVEL);
+ return;
+ }
+ }
+
+ /* Initial check for valid fsp ptr. */
+ if (!check_fsp_open(conn, req, fsp)) {
+ return;
+ }
+
+ smb_fname = fsp->fsp_name;
+
+ if(fsp->fake_file_handle) {
+ /*
+ * This is actually for the QUOTA_FAKE_FILE --metze
+ */
+
+ /* We know this name is ok, it's already passed the checks. */
+
+ } else if(fsp_get_pathref_fd(fsp) == -1) {
+ /*
+ * This is actually a QFILEINFO on a directory
+ * handle (returned from an NT SMB). NT5.0 seems
+ * to do this call. JRA.
+ */
+ ret = vfs_stat(conn, smb_fname);
+ if (ret != 0) {
+ DBG_NOTICE("vfs_stat of %s failed (%s)\n",
+ smb_fname_str_dbg(smb_fname),
+ strerror(errno));
+ reply_nterror(req,
+ map_nt_error_from_unix(errno));
+ return;
+ }
+
+ if (fsp_getinfo_ask_sharemode(fsp)) {
+ fileid = vfs_file_id_from_sbuf(
+ conn, &smb_fname->st);
+ get_file_infos(fileid, fsp->name_hash,
+ &delete_pending,
+ &write_time_ts);
+ }
+ } else {
+ /*
+ * Original code - this is an open file.
+ */
+ status = vfs_stat_fsp(fsp);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(3, ("fstat of %s failed (%s)\n",
+ fsp_fnum_dbg(fsp), nt_errstr(status)));
+ reply_nterror(req, status);
+ return;
+ }
+ if (fsp_getinfo_ask_sharemode(fsp)) {
+ fileid = vfs_file_id_from_sbuf(
+ conn, &smb_fname->st);
+ get_file_infos(fileid, fsp->name_hash,
+ &delete_pending,
+ &write_time_ts);
+ }
+ }
+
+ info_level_handled = true; /* Untouched in switch cases below */
+
+ switch (info_level) {
+
+ default:
+ info_level_handled = false;
+ break;
+
+ case SMB_QUERY_POSIX_LOCK:
+ status = smb_q_posix_lock(conn, req, fsp, ppdata, &total_data);
+ break;
+
+ case SMB_QUERY_FILE_UNIX_BASIC:
+ status = smb_q_unix_basic(
+ conn, req, fsp->fsp_name, fsp, ppdata, &total_data);
+ break;
+
+ case SMB_QUERY_FILE_UNIX_INFO2:
+ status = smb_q_unix_info2(
+ conn, req, fsp->fsp_name, fsp, ppdata, &total_data);
+ break;
+
+ case SMB_QUERY_POSIX_ACL:
+ status = smb_q_posix_acl(
+ conn, req, fsp->fsp_name, fsp, ppdata, &total_data);
+ break;
+ }
+
+ if (info_level_handled) {
+ handle_trans2qfilepathinfo_result(
+ conn,
+ req,
+ info_level,
+ status,
+ *ppdata,
+ total_data,
+ total_data,
+ max_data_bytes);
+ return;
+ }
+
+ call_trans2qfilepathinfo(
+ conn,
+ req,
+ TRANSACT2_QFILEINFO,
+ info_level,
+ smb_fname,
+ fsp,
+ delete_pending,
+ write_time_ts,
+ pparams,
+ total_params,
+ ppdata,
+ total_data,
+ max_data_bytes);
+}
+
+static void handle_trans2setfilepathinfo_result(
+ connection_struct *conn,
+ struct smb_request *req,
+ uint16_t info_level,
+ NTSTATUS status,
+ char *pdata,
+ int data_return_size,
+ unsigned int max_data_bytes)
+{
+ char params[2] = { 0, 0, };
+
+ if (NT_STATUS_IS_OK(status)) {
+ send_trans2_replies(
+ conn,
+ req,
+ NT_STATUS_OK,
+ params,
+ 2,
+ pdata,
+ data_return_size,
+ max_data_bytes);
+ return;
+ }
+
+ if (open_was_deferred(req->xconn, req->mid)) {
+ /* We have re-scheduled this call. */
+ return;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
+ bool ok = defer_smb1_sharing_violation(req);
+ if (ok) {
+ return;
+ }
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_EVENT_PENDING)) {
+ /* We have re-scheduled this call. */
+ return;
+ }
+
+ if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) {
+ reply_botherror(
+ req,
+ NT_STATUS_PATH_NOT_COVERED,
+ ERRSRV,
+ ERRbadpath);
+ return;
+ }
+
+ if (info_level == SMB_POSIX_PATH_OPEN) {
+ reply_openerror(req, status);
+ return;
+ }
+
+ if (NT_STATUS_EQUAL(status, STATUS_INVALID_EA_NAME)) {
+ /*
+ * Invalid EA name needs to return 2 param bytes,
+ * not a zero-length error packet.
+ */
+
+ send_trans2_replies(
+ conn,
+ req,
+ status,
+ params,
+ 2,
+ NULL,
+ 0,
+ max_data_bytes);
+ return;
+ }
+
+ reply_nterror(req, status);
+}
+
+/****************************************************************************
+ Create a directory with POSIX semantics.
+****************************************************************************/
+
+static NTSTATUS smb_posix_mkdir(connection_struct *conn,
+ struct smb_request *req,
+ char **ppdata,
+ int total_data,
+ struct smb_filename *smb_fname,
+ int *pdata_return_size)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ uint32_t raw_unixmode = 0;
+ mode_t unixmode = (mode_t)0;
+ files_struct *fsp = NULL;
+ uint16_t info_level_return = 0;
+ int info;
+ char *pdata = *ppdata;
+ struct smb2_create_blobs *posx = NULL;
+
+ if (total_data < 18) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ raw_unixmode = IVAL(pdata,8);
+ /* Next 4 bytes are not yet defined. */
+
+ status = unix_perms_from_wire(conn, &smb_fname->st, raw_unixmode,
+ PERM_NEW_DIR, &unixmode);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = make_smb2_posix_create_ctx(talloc_tos(), &posx, unixmode);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_WARNING("make_smb2_posix_create_ctx failed: %s\n",
+ nt_errstr(status));
+ return status;
+ }
+
+ DEBUG(10,("smb_posix_mkdir: file %s, mode 0%o\n",
+ smb_fname_str_dbg(smb_fname), (unsigned int)unixmode));
+
+ status = SMB_VFS_CREATE_FILE(
+ conn, /* conn */
+ req, /* req */
+ NULL, /* dirfsp */
+ smb_fname, /* fname */
+ FILE_READ_ATTRIBUTES, /* access_mask */
+ FILE_SHARE_NONE, /* share_access */
+ FILE_CREATE, /* create_disposition*/
+ FILE_DIRECTORY_FILE, /* create_options */
+ 0, /* file_attributes */
+ 0, /* oplock_request */
+ NULL, /* lease */
+ 0, /* allocation_size */
+ 0, /* private_flags */
+ NULL, /* sd */
+ NULL, /* ea_list */
+ &fsp, /* result */
+ &info, /* pinfo */
+ posx, /* in_context_blobs */
+ NULL); /* out_context_blobs */
+
+ TALLOC_FREE(posx);
+
+ if (NT_STATUS_IS_OK(status)) {
+ close_file_free(req, &fsp, NORMAL_CLOSE);
+ }
+
+ info_level_return = SVAL(pdata,16);
+
+ if (info_level_return == SMB_QUERY_FILE_UNIX_BASIC) {
+ *pdata_return_size = 12 + SMB_FILE_UNIX_BASIC_SIZE;
+ } else if (info_level_return == SMB_QUERY_FILE_UNIX_INFO2) {
+ *pdata_return_size = 12 + SMB_FILE_UNIX_INFO2_SIZE;
+ } else {
+ *pdata_return_size = 12;
+ }
+
+ /* Realloc the data size */
+ *ppdata = (char *)SMB_REALLOC(*ppdata,*pdata_return_size);
+ if (*ppdata == NULL) {
+ *pdata_return_size = 0;
+ return NT_STATUS_NO_MEMORY;
+ }
+ pdata = *ppdata;
+
+ SSVAL(pdata,0,NO_OPLOCK_RETURN);
+ SSVAL(pdata,2,0); /* No fnum. */
+ SIVAL(pdata,4,info); /* Was directory created. */
+
+ switch (info_level_return) {
+ case SMB_QUERY_FILE_UNIX_BASIC:
+ SSVAL(pdata,8,SMB_QUERY_FILE_UNIX_BASIC);
+ SSVAL(pdata,10,0); /* Padding. */
+ store_file_unix_basic(conn, pdata + 12, fsp,
+ &smb_fname->st);
+ break;
+ case SMB_QUERY_FILE_UNIX_INFO2:
+ SSVAL(pdata,8,SMB_QUERY_FILE_UNIX_INFO2);
+ SSVAL(pdata,10,0); /* Padding. */
+ store_file_unix_basic_info2(conn, pdata + 12, fsp,
+ &smb_fname->st);
+ break;
+ default:
+ SSVAL(pdata,8,SMB_NO_INFO_LEVEL_RETURNED);
+ SSVAL(pdata,10,0); /* Padding. */
+ break;
+ }
+
+ return status;
+}
+
+/****************************************************************************
+ Open/Create a file with POSIX semantics.
+****************************************************************************/
+
+#define SMB_O_RDONLY_MAPPING (FILE_READ_DATA|FILE_READ_ATTRIBUTES|FILE_READ_EA)
+#define SMB_O_WRONLY_MAPPING (FILE_WRITE_DATA|FILE_WRITE_ATTRIBUTES|FILE_WRITE_EA)
+
+static NTSTATUS smb_posix_open(connection_struct *conn,
+ struct smb_request *req,
+ char **ppdata,
+ int total_data,
+ struct files_struct *dirfsp,
+ struct smb_filename *smb_fname,
+ int *pdata_return_size)
+{
+ bool extended_oplock_granted = False;
+ char *pdata = *ppdata;
+ uint32_t flags = 0;
+ uint32_t wire_open_mode = 0;
+ uint32_t raw_unixmode = 0;
+ uint32_t attributes = 0;
+ uint32_t create_disp = 0;
+ uint32_t access_mask = 0;
+ uint32_t create_options = FILE_NON_DIRECTORY_FILE;
+ NTSTATUS status = NT_STATUS_OK;
+ mode_t unixmode = (mode_t)0;
+ files_struct *fsp = NULL;
+ int oplock_request = 0;
+ int info = 0;
+ uint16_t info_level_return = 0;
+ struct smb2_create_blobs *posx = NULL;
+
+ if (total_data < 18) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ flags = IVAL(pdata,0);
+ oplock_request = (flags & REQUEST_OPLOCK) ? EXCLUSIVE_OPLOCK : 0;
+ if (oplock_request) {
+ oplock_request |= (flags & REQUEST_BATCH_OPLOCK) ? BATCH_OPLOCK : 0;
+ }
+
+ wire_open_mode = IVAL(pdata,4);
+
+ if (wire_open_mode == (SMB_O_CREAT|SMB_O_DIRECTORY)) {
+ return smb_posix_mkdir(conn, req,
+ ppdata,
+ total_data,
+ smb_fname,
+ pdata_return_size);
+ }
+
+ switch (wire_open_mode & SMB_ACCMODE) {
+ case SMB_O_RDONLY:
+ access_mask = SMB_O_RDONLY_MAPPING;
+ break;
+ case SMB_O_WRONLY:
+ access_mask = SMB_O_WRONLY_MAPPING;
+ break;
+ case SMB_O_RDWR:
+ access_mask = (SMB_O_RDONLY_MAPPING|
+ SMB_O_WRONLY_MAPPING);
+ break;
+ default:
+ DEBUG(5,("smb_posix_open: invalid open mode 0x%x\n",
+ (unsigned int)wire_open_mode ));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ wire_open_mode &= ~SMB_ACCMODE;
+
+ /* First take care of O_CREAT|O_EXCL interactions. */
+ switch (wire_open_mode & (SMB_O_CREAT | SMB_O_EXCL)) {
+ case (SMB_O_CREAT | SMB_O_EXCL):
+ /* File exists fail. File not exist create. */
+ create_disp = FILE_CREATE;
+ break;
+ case SMB_O_CREAT:
+ /* File exists open. File not exist create. */
+ create_disp = FILE_OPEN_IF;
+ break;
+ case SMB_O_EXCL:
+ /* O_EXCL on its own without O_CREAT is undefined.
+ We deliberately ignore it as some versions of
+ Linux CIFSFS can send a bare O_EXCL on the
+ wire which other filesystems in the kernel
+ ignore. See bug 9519 for details. */
+
+ /* Fallthrough. */
+
+ case 0:
+ /* File exists open. File not exist fail. */
+ create_disp = FILE_OPEN;
+ break;
+ default:
+ DEBUG(5,("smb_posix_open: invalid create mode 0x%x\n",
+ (unsigned int)wire_open_mode ));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* Next factor in the effects of O_TRUNC. */
+ wire_open_mode &= ~(SMB_O_CREAT | SMB_O_EXCL);
+
+ if (wire_open_mode & SMB_O_TRUNC) {
+ switch (create_disp) {
+ case FILE_CREATE:
+ /* (SMB_O_CREAT | SMB_O_EXCL | O_TRUNC) */
+ /* Leave create_disp alone as
+ (O_CREAT|O_EXCL|O_TRUNC) == (O_CREAT|O_EXCL)
+ */
+ /* File exists fail. File not exist create. */
+ break;
+ case FILE_OPEN_IF:
+ /* SMB_O_CREAT | SMB_O_TRUNC */
+ /* File exists overwrite. File not exist create. */
+ create_disp = FILE_OVERWRITE_IF;
+ break;
+ case FILE_OPEN:
+ /* SMB_O_TRUNC */
+ /* File exists overwrite. File not exist fail. */
+ create_disp = FILE_OVERWRITE;
+ break;
+ default:
+ /* Cannot get here. */
+ smb_panic("smb_posix_open: logic error");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ raw_unixmode = IVAL(pdata,8);
+ /* Next 4 bytes are not yet defined. */
+
+ status = unix_perms_from_wire(conn, &smb_fname->st, raw_unixmode,
+ (VALID_STAT(smb_fname->st) ?
+ PERM_EXISTING_FILE : PERM_NEW_FILE),
+ &unixmode);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = make_smb2_posix_create_ctx(talloc_tos(), &posx, unixmode);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_WARNING("make_smb2_posix_create_ctx failed: %s\n",
+ nt_errstr(status));
+ return status;
+ }
+
+ if (wire_open_mode & SMB_O_SYNC) {
+ create_options |= FILE_WRITE_THROUGH;
+ }
+ if (wire_open_mode & SMB_O_APPEND) {
+ access_mask |= FILE_APPEND_DATA;
+ }
+ if (wire_open_mode & SMB_O_DIRECT) {
+ /*
+ * BUG: this doesn't work anymore since
+ * e0814dc5082dd4ecca8a155e0ce24b073158fd92. But since
+ * FILE_FLAG_NO_BUFFERING isn't used at all in the IO codepath,
+ * it doesn't really matter.
+ */
+ attributes |= FILE_FLAG_NO_BUFFERING;
+ }
+
+ if ((wire_open_mode & SMB_O_DIRECTORY) ||
+ VALID_STAT_OF_DIR(smb_fname->st)) {
+ if (access_mask != SMB_O_RDONLY_MAPPING) {
+ return NT_STATUS_FILE_IS_A_DIRECTORY;
+ }
+ create_options &= ~FILE_NON_DIRECTORY_FILE;
+ create_options |= FILE_DIRECTORY_FILE;
+ }
+
+ DEBUG(10,("smb_posix_open: file %s, smb_posix_flags = %u, mode 0%o\n",
+ smb_fname_str_dbg(smb_fname),
+ (unsigned int)wire_open_mode,
+ (unsigned int)unixmode ));
+
+ status = SMB_VFS_CREATE_FILE(
+ conn, /* conn */
+ req, /* req */
+ dirfsp, /* dirfsp */
+ smb_fname, /* fname */
+ access_mask, /* access_mask */
+ (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
+ FILE_SHARE_DELETE),
+ create_disp, /* create_disposition*/
+ create_options, /* create_options */
+ attributes, /* file_attributes */
+ oplock_request, /* oplock_request */
+ NULL, /* lease */
+ 0, /* allocation_size */
+ 0, /* private_flags */
+ NULL, /* sd */
+ NULL, /* ea_list */
+ &fsp, /* result */
+ &info, /* pinfo */
+ posx, /* in_context_blobs */
+ NULL); /* out_context_blobs */
+
+ TALLOC_FREE(posx);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (oplock_request && lp_fake_oplocks(SNUM(conn))) {
+ extended_oplock_granted = True;
+ }
+
+ if(oplock_request && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
+ extended_oplock_granted = True;
+ }
+
+ info_level_return = SVAL(pdata,16);
+
+ /* Allocate the correct return size. */
+
+ if (info_level_return == SMB_QUERY_FILE_UNIX_BASIC) {
+ *pdata_return_size = 12 + SMB_FILE_UNIX_BASIC_SIZE;
+ } else if (info_level_return == SMB_QUERY_FILE_UNIX_INFO2) {
+ *pdata_return_size = 12 + SMB_FILE_UNIX_INFO2_SIZE;
+ } else {
+ *pdata_return_size = 12;
+ }
+
+ /* Realloc the data size */
+ *ppdata = (char *)SMB_REALLOC(*ppdata,*pdata_return_size);
+ if (*ppdata == NULL) {
+ close_file_free(req, &fsp, ERROR_CLOSE);
+ *pdata_return_size = 0;
+ return NT_STATUS_NO_MEMORY;
+ }
+ pdata = *ppdata;
+
+ if (extended_oplock_granted) {
+ if (flags & REQUEST_BATCH_OPLOCK) {
+ SSVAL(pdata,0, BATCH_OPLOCK_RETURN);
+ } else {
+ SSVAL(pdata,0, EXCLUSIVE_OPLOCK_RETURN);
+ }
+ } else if (fsp->oplock_type == LEVEL_II_OPLOCK) {
+ SSVAL(pdata,0, LEVEL_II_OPLOCK_RETURN);
+ } else {
+ SSVAL(pdata,0,NO_OPLOCK_RETURN);
+ }
+
+ SSVAL(pdata,2,fsp->fnum);
+ SIVAL(pdata,4,info); /* Was file created etc. */
+
+ switch (info_level_return) {
+ case SMB_QUERY_FILE_UNIX_BASIC:
+ SSVAL(pdata,8,SMB_QUERY_FILE_UNIX_BASIC);
+ SSVAL(pdata,10,0); /* padding. */
+ store_file_unix_basic(conn, pdata + 12, fsp,
+ &smb_fname->st);
+ break;
+ case SMB_QUERY_FILE_UNIX_INFO2:
+ SSVAL(pdata,8,SMB_QUERY_FILE_UNIX_INFO2);
+ SSVAL(pdata,10,0); /* padding. */
+ store_file_unix_basic_info2(conn, pdata + 12, fsp,
+ &smb_fname->st);
+ break;
+ default:
+ SSVAL(pdata,8,SMB_NO_INFO_LEVEL_RETURNED);
+ SSVAL(pdata,10,0); /* padding. */
+ break;
+ }
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Delete a file with POSIX semantics.
+****************************************************************************/
+
+struct smb_posix_unlink_state {
+ struct smb_filename *smb_fname;
+ struct files_struct *fsp;
+ NTSTATUS status;
+};
+
+static void smb_posix_unlink_locked(struct share_mode_lock *lck,
+ void *private_data)
+{
+ struct smb_posix_unlink_state *state = private_data;
+ char del = 1;
+ bool other_nonposix_opens;
+
+ other_nonposix_opens = has_other_nonposix_opens(lck, state->fsp);
+ if (other_nonposix_opens) {
+ /* Fail with sharing violation. */
+ state->status = NT_STATUS_SHARING_VIOLATION;
+ return;
+ }
+
+ /*
+ * Set the delete on close.
+ */
+ state->status = smb_set_file_disposition_info(state->fsp->conn,
+ &del,
+ 1,
+ state->fsp,
+ state->smb_fname);
+}
+
+static NTSTATUS smb_posix_unlink(connection_struct *conn,
+ struct smb_request *req,
+ const char *pdata,
+ int total_data,
+ struct files_struct *dirfsp,
+ struct smb_filename *smb_fname)
+{
+ struct smb_posix_unlink_state state = {};
+ NTSTATUS status = NT_STATUS_OK;
+ files_struct *fsp = NULL;
+ uint16_t flags = 0;
+ int info = 0;
+ int create_options = FILE_OPEN_REPARSE_POINT;
+ struct smb2_create_blobs *posx = NULL;
+
+ if (!CAN_WRITE(conn)) {
+ return NT_STATUS_DOS(ERRSRV, ERRaccess);
+ }
+
+ if (total_data < 2) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ flags = SVAL(pdata,0);
+
+ if (!VALID_STAT(smb_fname->st)) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if ((flags == SMB_POSIX_UNLINK_DIRECTORY_TARGET) &&
+ !VALID_STAT_OF_DIR(smb_fname->st)) {
+ return NT_STATUS_NOT_A_DIRECTORY;
+ }
+
+ DEBUG(10,("smb_posix_unlink: %s %s\n",
+ (flags == SMB_POSIX_UNLINK_DIRECTORY_TARGET) ? "directory" : "file",
+ smb_fname_str_dbg(smb_fname)));
+
+ if (S_ISDIR(smb_fname->st.st_ex_mode)) {
+ create_options |= FILE_DIRECTORY_FILE;
+ }
+
+ status = make_smb2_posix_create_ctx(talloc_tos(), &posx, 0777);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_WARNING("make_smb2_posix_create_ctx failed: %s\n",
+ nt_errstr(status));
+ return status;
+ }
+
+ status = SMB_VFS_CREATE_FILE(
+ conn, /* conn */
+ req, /* req */
+ dirfsp, /* dirfsp */
+ smb_fname, /* fname */
+ DELETE_ACCESS, /* access_mask */
+ (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
+ FILE_SHARE_DELETE),
+ FILE_OPEN, /* create_disposition*/
+ create_options, /* create_options */
+ 0, /* file_attributes */
+ 0, /* oplock_request */
+ NULL, /* lease */
+ 0, /* allocation_size */
+ 0, /* private_flags */
+ NULL, /* sd */
+ NULL, /* ea_list */
+ &fsp, /* result */
+ &info, /* pinfo */
+ posx, /* in_context_blobs */
+ NULL); /* out_context_blobs */
+
+ TALLOC_FREE(posx);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /*
+ * Don't lie to client. If we can't really delete due to
+ * non-POSIX opens return SHARING_VIOLATION.
+ */
+
+ state = (struct smb_posix_unlink_state) {
+ .smb_fname = smb_fname,
+ .fsp = fsp,
+ };
+
+ status = share_mode_do_locked_vfs_allowed(fsp->file_id,
+ smb_posix_unlink_locked,
+ &state);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("share_mode_do_locked_vfs_allowed(%s) failed - %s\n",
+ fsp_str_dbg(fsp), nt_errstr(status));
+ close_file_free(req, &fsp, NORMAL_CLOSE);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = state.status;
+ if (!NT_STATUS_IS_OK(status)) {
+ close_file_free(req, &fsp, NORMAL_CLOSE);
+ return status;
+ }
+ return close_file_free(req, &fsp, NORMAL_CLOSE);
+}
+
+/****************************************************************************
+ Deal with SMB_SET_FILE_UNIX_LINK (create a UNIX symlink).
+****************************************************************************/
+
+static NTSTATUS smb_set_file_unix_link(connection_struct *conn,
+ struct smb_request *req,
+ const char *pdata,
+ int total_data,
+ struct smb_filename *new_smb_fname)
+{
+ char *link_target = NULL;
+ struct smb_filename target_fname;
+ TALLOC_CTX *ctx = talloc_tos();
+ NTSTATUS status;
+ int ret;
+ struct smb_filename *parent_fname = NULL;
+ struct smb_filename *base_name = NULL;
+
+ if (!CAN_WRITE(conn)) {
+ return NT_STATUS_DOS(ERRSRV, ERRaccess);
+ }
+
+ /* Set a symbolic link. */
+ /* Don't allow this if follow links is false. */
+
+ if (total_data == 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!lp_follow_symlinks(SNUM(conn))) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ srvstr_pull_talloc(ctx, pdata, req->flags2, &link_target, pdata,
+ total_data, STR_TERMINATE);
+
+ if (!link_target) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ target_fname = (struct smb_filename) {
+ .base_name = link_target,
+ };
+
+ /* Removes @GMT tokens if any */
+ status = canonicalize_snapshot_path(&target_fname, UCF_GMT_PATHNAME, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DEBUG(10,("smb_set_file_unix_link: SMB_SET_FILE_UNIX_LINK doing symlink %s -> %s\n",
+ new_smb_fname->base_name, link_target ));
+
+ status = parent_pathref(talloc_tos(),
+ conn->cwd_fsp,
+ new_smb_fname,
+ &parent_fname,
+ &base_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ ret = SMB_VFS_SYMLINKAT(conn,
+ &target_fname,
+ parent_fname->fsp,
+ base_name);
+ if (ret != 0) {
+ TALLOC_FREE(parent_fname);
+ return map_nt_error_from_unix(errno);
+ }
+
+ TALLOC_FREE(parent_fname);
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Deal with SMB_SET_FILE_UNIX_HLINK (create a UNIX hard link).
+****************************************************************************/
+
+static NTSTATUS smb_set_file_unix_hlink(connection_struct *conn,
+ struct smb_request *req,
+ const char *pdata, int total_data,
+ struct smb_filename *smb_fname_new)
+{
+ char *oldname = NULL;
+ struct files_struct *src_dirfsp = NULL;
+ struct smb_filename *smb_fname_old = NULL;
+ uint32_t ucf_flags = ucf_flags_from_smb_request(req);
+ NTTIME old_twrp = 0;
+ TALLOC_CTX *ctx = talloc_tos();
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (!CAN_WRITE(conn)) {
+ return NT_STATUS_DOS(ERRSRV, ERRaccess);
+ }
+
+ /* Set a hard link. */
+ if (total_data == 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (req->posix_pathnames) {
+ srvstr_get_path_posix(ctx,
+ pdata,
+ req->flags2,
+ &oldname,
+ pdata,
+ total_data,
+ STR_TERMINATE,
+ &status);
+ } else {
+ srvstr_get_path(ctx,
+ pdata,
+ req->flags2,
+ &oldname,
+ pdata,
+ total_data,
+ STR_TERMINATE,
+ &status);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DEBUG(10,("smb_set_file_unix_hlink: SMB_SET_FILE_UNIX_LINK doing hard link %s -> %s\n",
+ smb_fname_str_dbg(smb_fname_new), oldname));
+
+ if (ucf_flags & UCF_GMT_PATHNAME) {
+ extract_snapshot_token(oldname, &old_twrp);
+ }
+ status = smb1_strip_dfs_path(ctx, &ucf_flags, &oldname);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ status = filename_convert_dirfsp(ctx,
+ conn,
+ oldname,
+ ucf_flags,
+ old_twrp,
+ &src_dirfsp,
+ &smb_fname_old);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return hardlink_internals(ctx,
+ conn,
+ req,
+ false,
+ smb_fname_old,
+ smb_fname_new);
+}
+
+/****************************************************************************
+ Allow a UNIX info mknod.
+****************************************************************************/
+
+static NTSTATUS smb_unix_mknod(connection_struct *conn,
+ const char *pdata,
+ int total_data,
+ struct files_struct *dirfsp,
+ const struct smb_filename *smb_fname)
+{
+ uint32_t file_type = IVAL(pdata,56);
+#if defined(HAVE_MAKEDEV)
+ uint32_t dev_major = IVAL(pdata,60);
+ uint32_t dev_minor = IVAL(pdata,68);
+#endif
+ SMB_DEV_T dev = (SMB_DEV_T)0;
+ uint32_t raw_unixmode = IVAL(pdata,84);
+ NTSTATUS status;
+ mode_t unixmode;
+ int ret;
+ struct smb_filename *parent_fname = NULL;
+ struct smb_filename *atname = NULL;
+
+ if (total_data < 100) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = unix_perms_from_wire(conn, &smb_fname->st, raw_unixmode,
+ PERM_NEW_FILE, &unixmode);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+#if defined(HAVE_MAKEDEV)
+ dev = makedev(dev_major, dev_minor);
+#endif
+
+ switch (file_type) {
+ /* We can't create other objects here. */
+ case UNIX_TYPE_FILE:
+ case UNIX_TYPE_DIR:
+ case UNIX_TYPE_SYMLINK:
+ return NT_STATUS_ACCESS_DENIED;
+#if defined(S_IFIFO)
+ case UNIX_TYPE_FIFO:
+ unixmode |= S_IFIFO;
+ break;
+#endif
+#if defined(S_IFSOCK)
+ case UNIX_TYPE_SOCKET:
+ unixmode |= S_IFSOCK;
+ break;
+#endif
+#if defined(S_IFCHR)
+ case UNIX_TYPE_CHARDEV:
+ /* This is only allowed for root. */
+ if (get_current_uid(conn) != sec_initial_uid()) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ unixmode |= S_IFCHR;
+ break;
+#endif
+#if defined(S_IFBLK)
+ case UNIX_TYPE_BLKDEV:
+ if (get_current_uid(conn) != sec_initial_uid()) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ unixmode |= S_IFBLK;
+ break;
+#endif
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DEBUG(10,("smb_unix_mknod: SMB_SET_FILE_UNIX_BASIC doing mknod dev "
+ "%.0f mode 0%o for file %s\n", (double)dev,
+ (unsigned int)unixmode, smb_fname_str_dbg(smb_fname)));
+
+ status = SMB_VFS_PARENT_PATHNAME(dirfsp->conn,
+ talloc_tos(),
+ smb_fname,
+ &parent_fname,
+ &atname);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* Ok - do the mknod. */
+ ret = SMB_VFS_MKNODAT(conn,
+ dirfsp,
+ atname,
+ unixmode,
+ dev);
+
+ if (ret != 0) {
+ TALLOC_FREE(parent_fname);
+ return map_nt_error_from_unix(errno);
+ }
+
+ /* If any of the other "set" calls fail we
+ * don't want to end up with a half-constructed mknod.
+ */
+
+ if (lp_inherit_permissions(SNUM(conn))) {
+ inherit_access_posix_acl(conn,
+ dirfsp,
+ smb_fname,
+ unixmode);
+ }
+ TALLOC_FREE(parent_fname);
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Deal with SMB_SET_FILE_UNIX_BASIC.
+****************************************************************************/
+
+static NTSTATUS smb_set_file_unix_basic(connection_struct *conn,
+ struct smb_request *req,
+ const char *pdata,
+ int total_data,
+ struct files_struct *dirfsp,
+ files_struct *fsp,
+ struct smb_filename *smb_fname)
+{
+ struct smb_file_time ft;
+ uint32_t raw_unixmode;
+ mode_t unixmode;
+ off_t size = 0;
+ uid_t set_owner = (uid_t)SMB_UID_NO_CHANGE;
+ gid_t set_grp = (uid_t)SMB_GID_NO_CHANGE;
+ NTSTATUS status = NT_STATUS_OK;
+ enum perm_type ptype;
+ files_struct *all_fsps = NULL;
+ bool modify_mtime = true;
+ struct file_id id;
+ SMB_STRUCT_STAT sbuf;
+
+ if (!CAN_WRITE(conn)) {
+ return NT_STATUS_DOS(ERRSRV, ERRaccess);
+ }
+
+ init_smb_file_time(&ft);
+
+ if (total_data < 100) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if(IVAL(pdata, 0) != SMB_SIZE_NO_CHANGE_LO &&
+ IVAL(pdata, 4) != SMB_SIZE_NO_CHANGE_HI) {
+ size=IVAL(pdata,0); /* first 8 Bytes are size */
+ size |= (((off_t)IVAL(pdata,4)) << 32);
+ }
+
+ ft.atime = pull_long_date_full_timespec(pdata+24); /* access_time */
+ ft.mtime = pull_long_date_full_timespec(pdata+32); /* modification_time */
+ set_owner = (uid_t)IVAL(pdata,40);
+ set_grp = (gid_t)IVAL(pdata,48);
+ raw_unixmode = IVAL(pdata,84);
+
+ if (VALID_STAT(smb_fname->st)) {
+ if (S_ISDIR(smb_fname->st.st_ex_mode)) {
+ ptype = PERM_EXISTING_DIR;
+ } else {
+ ptype = PERM_EXISTING_FILE;
+ }
+ } else {
+ ptype = PERM_NEW_FILE;
+ }
+
+ status = unix_perms_from_wire(conn, &smb_fname->st, raw_unixmode,
+ ptype, &unixmode);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DEBUG(10,("smb_set_file_unix_basic: SMB_SET_FILE_UNIX_BASIC: name = "
+ "%s size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n",
+ smb_fname_str_dbg(smb_fname), (double)size,
+ (unsigned int)set_owner, (unsigned int)set_grp,
+ (int)raw_unixmode));
+
+ sbuf = smb_fname->st;
+
+ if (!VALID_STAT(sbuf)) {
+ /*
+ * The only valid use of this is to create character and block
+ * devices, and named pipes. This is deprecated (IMHO) and
+ * a new info level should be used for mknod. JRA.
+ */
+
+ if (dirfsp == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return smb_unix_mknod(conn,
+ pdata,
+ total_data,
+ dirfsp,
+ smb_fname);
+ }
+
+#if 1
+ /* Horrible backwards compatibility hack as an old server bug
+ * allowed a CIFS client bug to remain unnoticed :-(. JRA.
+ * */
+
+ if (!size) {
+ size = get_file_size_stat(&sbuf);
+ }
+#endif
+
+ /*
+ * Deal with the UNIX specific mode set.
+ */
+
+ if (raw_unixmode != SMB_MODE_NO_CHANGE) {
+ int ret;
+
+ if (fsp == NULL || S_ISLNK(smb_fname->st.st_ex_mode)) {
+ DBG_WARNING("Can't set mode on symlink %s\n",
+ smb_fname_str_dbg(smb_fname));
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ DEBUG(10,("smb_set_file_unix_basic: SMB_SET_FILE_UNIX_BASIC "
+ "setting mode 0%o for file %s\n",
+ (unsigned int)unixmode,
+ smb_fname_str_dbg(smb_fname)));
+ ret = SMB_VFS_FCHMOD(fsp, unixmode);
+ if (ret != 0) {
+ return map_nt_error_from_unix(errno);
+ }
+ }
+
+ /*
+ * Deal with the UNIX specific uid set.
+ */
+
+ if ((set_owner != (uid_t)SMB_UID_NO_CHANGE) &&
+ (sbuf.st_ex_uid != set_owner)) {
+ int ret;
+
+ DEBUG(10,("smb_set_file_unix_basic: SMB_SET_FILE_UNIX_BASIC "
+ "changing owner %u for path %s\n",
+ (unsigned int)set_owner,
+ smb_fname_str_dbg(smb_fname)));
+
+ if (fsp &&
+ !fsp->fsp_flags.is_pathref &&
+ fsp_get_io_fd(fsp) != -1)
+ {
+ ret = SMB_VFS_FCHOWN(fsp, set_owner, (gid_t)-1);
+ } else {
+ /*
+ * UNIX extensions calls must always operate
+ * on symlinks.
+ */
+ ret = SMB_VFS_LCHOWN(conn, smb_fname,
+ set_owner, (gid_t)-1);
+ }
+
+ if (ret != 0) {
+ status = map_nt_error_from_unix(errno);
+ return status;
+ }
+ }
+
+ /*
+ * Deal with the UNIX specific gid set.
+ */
+
+ if ((set_grp != (uid_t)SMB_GID_NO_CHANGE) &&
+ (sbuf.st_ex_gid != set_grp)) {
+ int ret;
+
+ DEBUG(10,("smb_set_file_unix_basic: SMB_SET_FILE_UNIX_BASIC "
+ "changing group %u for file %s\n",
+ (unsigned int)set_grp,
+ smb_fname_str_dbg(smb_fname)));
+ if (fsp &&
+ !fsp->fsp_flags.is_pathref &&
+ fsp_get_io_fd(fsp) != -1)
+ {
+ ret = SMB_VFS_FCHOWN(fsp, (uid_t)-1, set_grp);
+ } else {
+ /*
+ * UNIX extensions calls must always operate
+ * on symlinks.
+ */
+ ret = SMB_VFS_LCHOWN(conn, smb_fname, (uid_t)-1,
+ set_grp);
+ }
+ if (ret != 0) {
+ status = map_nt_error_from_unix(errno);
+ return status;
+ }
+ }
+
+ /* Deal with any size changes. */
+
+ if (S_ISREG(sbuf.st_ex_mode)) {
+ status = smb_set_file_size(conn, req,
+ fsp,
+ smb_fname,
+ &sbuf,
+ size,
+ false);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ /* Deal with any time changes. */
+ if (is_omit_timespec(&ft.mtime) && is_omit_timespec(&ft.atime)) {
+ /* No change, don't cancel anything. */
+ return status;
+ }
+
+ id = vfs_file_id_from_sbuf(conn, &sbuf);
+ for(all_fsps = file_find_di_first(conn->sconn, id, true); all_fsps;
+ all_fsps = file_find_di_next(all_fsps, true)) {
+ /*
+ * We're setting the time explicitly for UNIX.
+ * Cancel any pending changes over all handles.
+ */
+ all_fsps->fsp_flags.update_write_time_on_close = false;
+ TALLOC_FREE(all_fsps->update_write_time_event);
+ }
+
+ /*
+ * Override the "setting_write_time"
+ * parameter here as it almost does what
+ * we need. Just remember if we modified
+ * mtime and send the notify ourselves.
+ */
+ if (is_omit_timespec(&ft.mtime)) {
+ modify_mtime = false;
+ }
+
+ status = smb_set_file_time(conn,
+ fsp,
+ smb_fname,
+ &ft,
+ false);
+ if (modify_mtime) {
+ notify_fname(conn, NOTIFY_ACTION_MODIFIED,
+ FILE_NOTIFY_CHANGE_LAST_WRITE, smb_fname->base_name);
+ }
+ return status;
+}
+
+/****************************************************************************
+ Deal with SMB_SET_FILE_UNIX_INFO2.
+****************************************************************************/
+
+static NTSTATUS smb_set_file_unix_info2(connection_struct *conn,
+ struct smb_request *req,
+ const char *pdata,
+ int total_data,
+ struct files_struct *dirfsp,
+ files_struct *fsp,
+ struct smb_filename *smb_fname)
+{
+ NTSTATUS status;
+ uint32_t smb_fflags;
+ uint32_t smb_fmask;
+
+ if (!CAN_WRITE(conn)) {
+ return NT_STATUS_DOS(ERRSRV, ERRaccess);
+ }
+
+ if (total_data < 116) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* Start by setting all the fields that are common between UNIX_BASIC
+ * and UNIX_INFO2.
+ */
+ status = smb_set_file_unix_basic(conn,
+ req,
+ pdata,
+ total_data,
+ dirfsp,
+ fsp,
+ smb_fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ smb_fflags = IVAL(pdata, 108);
+ smb_fmask = IVAL(pdata, 112);
+
+ /* NB: We should only attempt to alter the file flags if the client
+ * sends a non-zero mask.
+ */
+ if (smb_fmask != 0) {
+ int stat_fflags = 0;
+
+ if (!map_info2_flags_to_sbuf(&smb_fname->st, smb_fflags,
+ smb_fmask, &stat_fflags)) {
+ /* Client asked to alter a flag we don't understand. */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (fsp == NULL || S_ISLNK(smb_fname->st.st_ex_mode)) {
+ DBG_WARNING("Can't change flags on symlink %s\n",
+ smb_fname_str_dbg(smb_fname));
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ if (SMB_VFS_FCHFLAGS(fsp, stat_fflags) != 0) {
+ return map_nt_error_from_unix(errno);
+ }
+ }
+
+ /* XXX: need to add support for changing the create_time here. You
+ * can do this for paths on Darwin with setattrlist(2). The right way
+ * to hook this up is probably by extending the VFS utimes interface.
+ */
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Deal with SMB_SET_POSIX_ACL.
+****************************************************************************/
+
+static NTSTATUS smb_set_posix_acl(connection_struct *conn,
+ struct smb_request *req,
+ const char *pdata,
+ int total_data_in,
+ files_struct *fsp,
+ struct smb_filename *smb_fname)
+{
+#if !defined(HAVE_POSIX_ACLS)
+ return NT_STATUS_INVALID_LEVEL;
+#else
+ uint16_t posix_acl_version;
+ uint16_t num_file_acls;
+ uint16_t num_def_acls;
+ bool valid_file_acls = true;
+ bool valid_def_acls = true;
+ NTSTATUS status;
+ unsigned int size_needed;
+ unsigned int total_data;
+ bool close_fsp = false;
+
+ if (total_data_in < 0) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto out;
+ }
+
+ total_data = total_data_in;
+
+ if (total_data < SMB_POSIX_ACL_HEADER_SIZE) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto out;
+ }
+ posix_acl_version = SVAL(pdata,0);
+ num_file_acls = SVAL(pdata,2);
+ num_def_acls = SVAL(pdata,4);
+
+ if (num_file_acls == SMB_POSIX_IGNORE_ACE_ENTRIES) {
+ valid_file_acls = false;
+ num_file_acls = 0;
+ }
+
+ if (num_def_acls == SMB_POSIX_IGNORE_ACE_ENTRIES) {
+ valid_def_acls = false;
+ num_def_acls = 0;
+ }
+
+ if (posix_acl_version != SMB_POSIX_ACL_VERSION) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /* Wrap checks. */
+ if (num_file_acls + num_def_acls < num_file_acls) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto out;
+ }
+
+ size_needed = num_file_acls + num_def_acls;
+
+ /*
+ * (size_needed * SMB_POSIX_ACL_ENTRY_SIZE) must be less
+ * than UINT_MAX, so check by division.
+ */
+ if (size_needed > (UINT_MAX/SMB_POSIX_ACL_ENTRY_SIZE)) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto out;
+ }
+
+ size_needed = size_needed*SMB_POSIX_ACL_ENTRY_SIZE;
+ if (size_needed + SMB_POSIX_ACL_HEADER_SIZE < size_needed) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto out;
+ }
+ size_needed += SMB_POSIX_ACL_HEADER_SIZE;
+
+ if (total_data < size_needed) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto out;
+ }
+
+ /*
+ * Ensure we always operate on a file descriptor, not just
+ * the filename.
+ */
+ if (fsp == NULL || !fsp->fsp_flags.is_fsa) {
+ uint32_t access_mask = SEC_STD_WRITE_OWNER|
+ SEC_STD_WRITE_DAC|
+ SEC_STD_READ_CONTROL|
+ FILE_READ_ATTRIBUTES|
+ FILE_WRITE_ATTRIBUTES;
+
+ status = get_posix_fsp(conn,
+ req,
+ smb_fname,
+ access_mask,
+ &fsp);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+ close_fsp = true;
+ }
+
+ /* Here we know fsp != NULL */
+ SMB_ASSERT(fsp != NULL);
+
+ status = refuse_symlink_fsp(fsp);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ /* If we have a default acl, this *must* be a directory. */
+ if (valid_def_acls && !fsp->fsp_flags.is_directory) {
+ DBG_INFO("Can't set default acls on "
+ "non-directory %s\n",
+ fsp_str_dbg(fsp));
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ DBG_DEBUG("file %s num_file_acls = %"PRIu16", "
+ "num_def_acls = %"PRIu16"\n",
+ fsp_str_dbg(fsp),
+ num_file_acls,
+ num_def_acls);
+
+ /* Move pdata to the start of the file ACL entries. */
+ pdata += SMB_POSIX_ACL_HEADER_SIZE;
+
+ if (valid_file_acls) {
+ status = set_unix_posix_acl(conn,
+ fsp,
+ num_file_acls,
+ pdata);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+ }
+
+ /* Move pdata to the start of the default ACL entries. */
+ pdata += (num_file_acls*SMB_POSIX_ACL_ENTRY_SIZE);
+
+ if (valid_def_acls) {
+ status = set_unix_posix_default_acl(conn,
+ fsp,
+ num_def_acls,
+ pdata);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+ }
+
+ status = NT_STATUS_OK;
+
+ out:
+
+ if (close_fsp) {
+ (void)close_file_free(req, &fsp, NORMAL_CLOSE);
+ }
+ return status;
+#endif
+}
+
+static void call_trans2setpathinfo(
+ connection_struct *conn,
+ struct smb_request *req,
+ char **pparams,
+ int total_params,
+ char **ppdata,
+ int total_data,
+ unsigned int max_data_bytes)
+{
+ uint16_t info_level;
+ struct smb_filename *smb_fname = NULL;
+ struct files_struct *dirfsp = NULL;
+ struct files_struct *fsp = NULL;
+ char *params = *pparams;
+ uint32_t ucf_flags = ucf_flags_from_smb_request(req);
+ NTTIME twrp = 0;
+ char *fname = NULL;
+ bool info_level_handled;
+ int data_return_size = 0;
+ NTSTATUS status;
+
+ if (params == NULL) {
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ /* set path info */
+ if (total_params < 7) {
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ info_level = SVAL(params,0);
+
+ if (INFO_LEVEL_IS_UNIX(info_level)) {
+ if (!lp_smb1_unix_extensions()) {
+ reply_nterror(req, NT_STATUS_INVALID_LEVEL);
+ return;
+ }
+ if (!req->posix_pathnames) {
+ reply_nterror(req, NT_STATUS_INVALID_LEVEL);
+ return;
+ }
+ }
+
+ if (req->posix_pathnames) {
+ srvstr_get_path_posix(req,
+ params,
+ req->flags2,
+ &fname,
+ &params[6],
+ total_params - 6,
+ STR_TERMINATE,
+ &status);
+ } else {
+ srvstr_get_path(req,
+ params,
+ req->flags2,
+ &fname,
+ &params[6],
+ total_params - 6,
+ STR_TERMINATE,
+ &status);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ reply_nterror(req, status);
+ return;
+ }
+
+ DBG_NOTICE("fname=%s info_level=%d totdata=%d\n",
+ fname,
+ info_level,
+ total_data);
+
+ if (ucf_flags & UCF_GMT_PATHNAME) {
+ extract_snapshot_token(fname, &twrp);
+ }
+ status = smb1_strip_dfs_path(req, &ucf_flags, &fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ reply_nterror(req, status);
+ return;
+ }
+ status = filename_convert_dirfsp(req,
+ conn,
+ fname,
+ ucf_flags,
+ twrp,
+ &dirfsp,
+ &smb_fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) {
+ reply_botherror(req,
+ NT_STATUS_PATH_NOT_COVERED,
+ ERRSRV, ERRbadpath);
+ return;
+ }
+ reply_nterror(req, status);
+ return;
+ }
+
+ info_level_handled = true; /* Untouched in switch cases below */
+
+ switch (info_level) {
+
+ default:
+ info_level_handled = false;
+ break;
+
+ case SMB_POSIX_PATH_OPEN:
+ status = smb_posix_open(conn,
+ req,
+ ppdata,
+ total_data,
+ dirfsp,
+ smb_fname,
+ &data_return_size);
+ break;
+
+ case SMB_POSIX_PATH_UNLINK:
+ status = smb_posix_unlink(conn,
+ req,
+ *ppdata,
+ total_data,
+ dirfsp,
+ smb_fname);
+ break;
+
+ case SMB_SET_FILE_UNIX_LINK:
+ status = smb_set_file_unix_link(
+ conn, req, *ppdata, total_data, smb_fname);
+ break;
+
+ case SMB_SET_FILE_UNIX_HLINK:
+ status = smb_set_file_unix_hlink(
+ conn, req, *ppdata, total_data, smb_fname);
+ break;
+
+ case SMB_SET_FILE_UNIX_BASIC:
+ status = smb_set_file_unix_basic(conn,
+ req,
+ *ppdata,
+ total_data,
+ dirfsp,
+ smb_fname->fsp,
+ smb_fname);
+ break;
+
+ case SMB_SET_FILE_UNIX_INFO2:
+ status = smb_set_file_unix_info2(conn,
+ req,
+ *ppdata,
+ total_data,
+ dirfsp,
+ smb_fname->fsp,
+ smb_fname);
+ break;
+ case SMB_SET_POSIX_ACL:
+ status = smb_set_posix_acl(
+ conn, req, *ppdata, total_data, NULL, smb_fname);
+ break;
+ }
+
+ if (info_level_handled) {
+ handle_trans2setfilepathinfo_result(
+ conn,
+ req,
+ info_level,
+ status,
+ *ppdata,
+ data_return_size,
+ max_data_bytes);
+ return;
+ }
+
+ /*
+ * smb_fname->fsp may be NULL if smb_fname points at a symlink
+ * and we're in POSIX context, so be careful when using fsp
+ * below, it can still be NULL.
+ */
+ fsp = smb_fname->fsp;
+
+ status = smbd_do_setfilepathinfo(
+ conn,
+ req,
+ req,
+ info_level,
+ fsp,
+ smb_fname,
+ ppdata,
+ total_data,
+ &data_return_size);
+
+ handle_trans2setfilepathinfo_result(
+ conn,
+ req,
+ info_level,
+ status,
+ *ppdata,
+ data_return_size,
+ max_data_bytes);
+}
+
+static void call_trans2setfileinfo(
+ connection_struct *conn,
+ struct smb_request *req,
+ char **pparams,
+ int total_params,
+ char **ppdata,
+ int total_data,
+ unsigned int max_data_bytes)
+{
+ char *pdata = *ppdata;
+ uint16_t info_level;
+ struct smb_filename *smb_fname = NULL;
+ struct files_struct *fsp = NULL;
+ char *params = *pparams;
+ int data_return_size = 0;
+ bool info_level_handled;
+ NTSTATUS status;
+ int ret;
+
+ if (params == NULL) {
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ if (total_params < 4) {
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ fsp = file_fsp(req, SVAL(params,0));
+ /* Basic check for non-null fsp. */
+ if (!check_fsp_open(conn, req, fsp)) {
+ return;
+ }
+ info_level = SVAL(params,2);
+
+ if (INFO_LEVEL_IS_UNIX(info_level)) {
+ if (!lp_smb1_unix_extensions()) {
+ reply_nterror(req, NT_STATUS_INVALID_LEVEL);
+ return;
+ }
+ if (!req->posix_pathnames) {
+ reply_nterror(req, NT_STATUS_INVALID_LEVEL);
+ return;
+ }
+ }
+
+ smb_fname = fsp->fsp_name;
+
+ DBG_NOTICE("fnum=%s fname=%s info_level=%d totdata=%d\n",
+ fsp_fnum_dbg(fsp),
+ fsp_str_dbg(fsp),
+ info_level,
+ total_data);
+
+ if (fsp_get_pathref_fd(fsp) == -1) {
+ /*
+ * This is actually a SETFILEINFO on a directory
+ * handle (returned from an NT SMB). NT5.0 seems
+ * to do this call. JRA.
+ */
+ ret = vfs_stat(conn, smb_fname);
+ if (ret != 0) {
+ DBG_NOTICE("vfs_stat of %s failed (%s)\n",
+ smb_fname_str_dbg(smb_fname),
+ strerror(errno));
+ reply_nterror(req, map_nt_error_from_unix(errno));
+ return;
+ }
+ } else if (fsp->print_file) {
+ /*
+ * Doing a DELETE_ON_CLOSE should cancel a print job.
+ */
+ if ((info_level == SMB_SET_FILE_DISPOSITION_INFO) &&
+ CVAL(pdata,0)) {
+
+ fsp->fsp_flags.delete_on_close = true;
+
+ DBG_NOTICE("Cancelling print job (%s)\n",
+ fsp_str_dbg(fsp));
+
+ SSVAL(params,0,0);
+ send_trans2_replies(
+ conn,
+ req,
+ NT_STATUS_OK,
+ params,
+ 2,
+ *ppdata, 0,
+ max_data_bytes);
+ return;
+ } else {
+ reply_nterror(req, NT_STATUS_OBJECT_PATH_NOT_FOUND);
+ return;
+ }
+ } else {
+ /*
+ * Original code - this is an open file.
+ */
+ status = vfs_stat_fsp(fsp);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_NOTICE("fstat of %s failed (%s)\n",
+ fsp_fnum_dbg(fsp),
+ nt_errstr(status));
+ reply_nterror(req, status);
+ return;
+ }
+ }
+
+ info_level_handled = true; /* Untouched in switch cases below */
+
+ switch (info_level) {
+
+ default:
+ info_level_handled = false;
+ break;
+
+ case SMB_SET_FILE_UNIX_BASIC:
+ status = smb_set_file_unix_basic(conn,
+ req,
+ pdata,
+ total_data,
+ NULL,
+ fsp,
+ smb_fname);
+ break;
+
+ case SMB_SET_FILE_UNIX_INFO2:
+ status = smb_set_file_unix_info2(conn,
+ req,
+ pdata,
+ total_data,
+ NULL,
+ fsp,
+ smb_fname);
+ break;
+
+ case SMB_SET_POSIX_LOCK:
+ status = smb_set_posix_lock(
+ conn, req, *ppdata, total_data, fsp);
+ break;
+ }
+
+ if (info_level_handled) {
+ handle_trans2setfilepathinfo_result(
+ conn,
+ req,
+ info_level,
+ status,
+ *ppdata,
+ data_return_size,
+ max_data_bytes);
+ return;
+ }
+
+ status = smbd_do_setfilepathinfo(
+ conn,
+ req,
+ req,
+ info_level,
+ fsp,
+ smb_fname,
+ ppdata,
+ total_data,
+ &data_return_size);
+
+ handle_trans2setfilepathinfo_result(
+ conn,
+ req,
+ info_level,
+ status,
+ *ppdata,
+ data_return_size,
+ max_data_bytes);
+}
+
+/****************************************************************************
+ Reply to a TRANS2_MKDIR (make directory with extended attributes).
+****************************************************************************/
+
+static void call_trans2mkdir(connection_struct *conn, struct smb_request *req,
+ char **pparams, int total_params,
+ char **ppdata, int total_data,
+ unsigned int max_data_bytes)
+{
+ struct files_struct *dirfsp = NULL;
+ struct files_struct *fsp = NULL;
+ struct smb_filename *smb_dname = NULL;
+ char *params = *pparams;
+ char *pdata = *ppdata;
+ char *directory = NULL;
+ NTSTATUS status = NT_STATUS_OK;
+ struct ea_list *ea_list = NULL;
+ uint32_t ucf_flags = ucf_flags_from_smb_request(req);
+ NTTIME twrp = 0;
+ TALLOC_CTX *ctx = talloc_tos();
+
+ if (!CAN_WRITE(conn)) {
+ reply_nterror(req, NT_STATUS_ACCESS_DENIED);
+ return;
+ }
+
+ if (total_params < 5) {
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ if (req->posix_pathnames) {
+ srvstr_get_path_posix(ctx,
+ params,
+ req->flags2,
+ &directory,
+ &params[4],
+ total_params - 4,
+ STR_TERMINATE,
+ &status);
+ } else {
+ srvstr_get_path(ctx,
+ params,
+ req->flags2,
+ &directory,
+ &params[4],
+ total_params - 4,
+ STR_TERMINATE,
+ &status);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ reply_nterror(req, status);
+ return;
+ }
+
+ DEBUG(3,("call_trans2mkdir : name = %s\n", directory));
+
+ if (ucf_flags & UCF_GMT_PATHNAME) {
+ extract_snapshot_token(directory, &twrp);
+ }
+ status = smb1_strip_dfs_path(ctx, &ucf_flags, &directory);
+ if (!NT_STATUS_IS_OK(status)) {
+ reply_nterror(req, status);
+ goto out;
+ }
+ status = filename_convert_dirfsp(ctx,
+ conn,
+ directory,
+ ucf_flags,
+ twrp,
+ &dirfsp,
+ &smb_dname);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status,NT_STATUS_PATH_NOT_COVERED)) {
+ reply_botherror(req,
+ NT_STATUS_PATH_NOT_COVERED,
+ ERRSRV, ERRbadpath);
+ return;
+ }
+ reply_nterror(req, status);
+ return;
+ }
+
+ /*
+ * OS/2 workplace shell seems to send SET_EA requests of "null"
+ * length (4 bytes containing IVAL 4).
+ * They seem to have no effect. Bug #3212. JRA.
+ */
+
+ if (total_data && (total_data != 4)) {
+ /* Any data in this call is an EA list. */
+ if (total_data < 10) {
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ goto out;
+ }
+
+ if (IVAL(pdata,0) > total_data) {
+ DEBUG(10,("call_trans2mkdir: bad total data size (%u) > %u\n",
+ IVAL(pdata,0), (unsigned int)total_data));
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ goto out;
+ }
+
+ ea_list = read_ea_list(talloc_tos(), pdata + 4,
+ total_data - 4);
+ if (!ea_list) {
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ goto out;
+ }
+
+ if (!lp_ea_support(SNUM(conn))) {
+ reply_nterror(req, NT_STATUS_EAS_NOT_SUPPORTED);
+ goto out;
+ }
+ }
+ /* If total_data == 4 Windows doesn't care what values
+ * are placed in that field, it just ignores them.
+ * The System i QNTC IBM SMB client puts bad values here,
+ * so ignore them. */
+
+ status = SMB_VFS_CREATE_FILE(
+ conn, /* conn */
+ req, /* req */
+ dirfsp, /* dirfsp */
+ smb_dname, /* fname */
+ MAXIMUM_ALLOWED_ACCESS, /* access_mask */
+ FILE_SHARE_NONE, /* share_access */
+ FILE_CREATE, /* create_disposition*/
+ FILE_DIRECTORY_FILE, /* create_options */
+ FILE_ATTRIBUTE_DIRECTORY, /* file_attributes */
+ 0, /* oplock_request */
+ NULL, /* lease */
+ 0, /* allocation_size */
+ 0, /* private_flags */
+ NULL, /* sd */
+ NULL, /* ea_list */
+ &fsp, /* result */
+ NULL, /* pinfo */
+ NULL, NULL); /* create context */
+ if (!NT_STATUS_IS_OK(status)) {
+ reply_nterror(req, status);
+ goto out;
+ }
+
+ /* Try and set any given EA. */
+ if (ea_list) {
+ status = set_ea(conn, fsp, ea_list);
+ if (!NT_STATUS_IS_OK(status)) {
+ reply_nterror(req, status);
+ goto out;
+ }
+ }
+
+ /* Realloc the parameter and data sizes */
+ *pparams = (char *)SMB_REALLOC(*pparams,2);
+ if(*pparams == NULL) {
+ reply_nterror(req, NT_STATUS_NO_MEMORY);
+ goto out;
+ }
+ params = *pparams;
+
+ SSVAL(params,0,0);
+
+ send_trans2_replies(conn, req, NT_STATUS_OK, params, 2, *ppdata, 0, max_data_bytes);
+
+ out:
+ if (fsp != NULL) {
+ close_file_free(NULL, &fsp, NORMAL_CLOSE);
+ }
+ TALLOC_FREE(smb_dname);
+}
+
+/****************************************************************************
+ Reply to a TRANS2_FINDNOTIFYFIRST (start monitoring a directory for changes).
+ We don't actually do this - we just send a null response.
+****************************************************************************/
+
+static void call_trans2findnotifyfirst(connection_struct *conn,
+ struct smb_request *req,
+ char **pparams, int total_params,
+ char **ppdata, int total_data,
+ unsigned int max_data_bytes)
+{
+ char *params = *pparams;
+ uint16_t info_level;
+
+ if (total_params < 6) {
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ info_level = SVAL(params,4);
+ DEBUG(3,("call_trans2findnotifyfirst - info_level %d\n", info_level));
+
+ switch (info_level) {
+ case 1:
+ case 2:
+ break;
+ default:
+ reply_nterror(req, NT_STATUS_INVALID_LEVEL);
+ return;
+ }
+
+ /* Realloc the parameter and data sizes */
+ *pparams = (char *)SMB_REALLOC(*pparams,6);
+ if (*pparams == NULL) {
+ reply_nterror(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ params = *pparams;
+
+ SSVAL(params,0,fnf_handle);
+ SSVAL(params,2,0); /* No changes */
+ SSVAL(params,4,0); /* No EA errors */
+
+ fnf_handle++;
+
+ if(fnf_handle == 0)
+ fnf_handle = 257;
+
+ send_trans2_replies(conn, req, NT_STATUS_OK, params, 6, *ppdata, 0, max_data_bytes);
+}
+
+/****************************************************************************
+ Reply to a TRANS2_FINDNOTIFYNEXT (continue monitoring a directory for
+ changes). Currently this does nothing.
+****************************************************************************/
+
+static void call_trans2findnotifynext(connection_struct *conn,
+ struct smb_request *req,
+ char **pparams, int total_params,
+ char **ppdata, int total_data,
+ unsigned int max_data_bytes)
+{
+ char *params = *pparams;
+
+ DEBUG(3,("call_trans2findnotifynext\n"));
+
+ /* Realloc the parameter and data sizes */
+ *pparams = (char *)SMB_REALLOC(*pparams,4);
+ if (*pparams == NULL) {
+ reply_nterror(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ params = *pparams;
+
+ SSVAL(params,0,0); /* No changes */
+ SSVAL(params,2,0); /* No EA errors */
+
+ send_trans2_replies(conn, req, NT_STATUS_OK, params, 4, *ppdata, 0, max_data_bytes);
+}
+
+/****************************************************************************
+ Reply to a TRANS2_GET_DFS_REFERRAL - Shirish Kalele <kalele@veritas.com>.
+****************************************************************************/
+
+static void call_trans2getdfsreferral(connection_struct *conn,
+ struct smb_request *req,
+ char **pparams, int total_params,
+ char **ppdata, int total_data,
+ unsigned int max_data_bytes)
+{
+ char *params = *pparams;
+ char *pathname = NULL;
+ int reply_size = 0;
+ int max_referral_level;
+ NTSTATUS status = NT_STATUS_OK;
+ TALLOC_CTX *ctx = talloc_tos();
+
+ DEBUG(10,("call_trans2getdfsreferral\n"));
+
+ if (!IS_IPC(conn)) {
+ reply_nterror(req, NT_STATUS_ACCESS_DENIED);
+ return;
+ }
+
+ if (total_params < 3) {
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ max_referral_level = SVAL(params,0);
+
+ if(!lp_host_msdfs()) {
+ reply_nterror(req, NT_STATUS_NOT_IMPLEMENTED);
+ return;
+ }
+
+ srvstr_pull_talloc(ctx, params, req->flags2, &pathname, &params[2],
+ total_params - 2, STR_TERMINATE);
+ if (!pathname) {
+ reply_nterror(req, NT_STATUS_NOT_FOUND);
+ return;
+ }
+ reply_size = setup_dfs_referral(
+ conn, pathname, max_referral_level, ppdata, &status);
+ if (reply_size < 0) {
+ reply_nterror(req, status);
+ return;
+ }
+
+ SSVAL((discard_const_p(uint8_t, req->inbuf)), smb_flg2,
+ SVAL(req->inbuf,smb_flg2) | FLAGS2_DFS_PATHNAMES);
+ send_trans2_replies(conn, req, NT_STATUS_OK, 0,0,*ppdata,reply_size, max_data_bytes);
+}
+
+#define LMCAT_SPL 0x53
+#define LMFUNC_GETJOBID 0x60
+
+/****************************************************************************
+ Reply to a TRANS2_IOCTL - used for OS/2 printing.
+****************************************************************************/
+
+static void call_trans2ioctl(connection_struct *conn,
+ struct smb_request *req,
+ char **pparams, int total_params,
+ char **ppdata, int total_data,
+ unsigned int max_data_bytes)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ char *pdata = *ppdata;
+ files_struct *fsp = file_fsp(req, SVAL(req->vwv+15, 0));
+ NTSTATUS status;
+ size_t len = 0;
+
+ /* check for an invalid fid before proceeding */
+
+ if (!fsp) {
+ reply_nterror(req, NT_STATUS_INVALID_HANDLE);
+ return;
+ }
+
+ if ((SVAL(req->vwv+16, 0) == LMCAT_SPL)
+ && (SVAL(req->vwv+17, 0) == LMFUNC_GETJOBID)) {
+ *ppdata = (char *)SMB_REALLOC(*ppdata, 32);
+ if (*ppdata == NULL) {
+ reply_nterror(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ pdata = *ppdata;
+
+ /* NOTE - THIS IS ASCII ONLY AT THE MOMENT - NOT SURE IF OS/2
+ CAN ACCEPT THIS IN UNICODE. JRA. */
+
+ /* Job number */
+ SSVAL(pdata, 0, print_spool_rap_jobid(fsp->print_file));
+
+ status = srvstr_push(pdata, req->flags2, pdata + 2,
+ lp_netbios_name(), 15,
+ STR_ASCII|STR_TERMINATE, &len); /* Our NetBIOS name */
+ if (!NT_STATUS_IS_OK(status)) {
+ reply_nterror(req, status);
+ return;
+ }
+ status = srvstr_push(pdata, req->flags2, pdata+18,
+ lp_servicename(talloc_tos(), lp_sub, SNUM(conn)), 13,
+ STR_ASCII|STR_TERMINATE, &len); /* Service name */
+ if (!NT_STATUS_IS_OK(status)) {
+ reply_nterror(req, status);
+ return;
+ }
+ send_trans2_replies(conn, req, NT_STATUS_OK, *pparams, 0, *ppdata, 32,
+ max_data_bytes);
+ return;
+ }
+
+ DEBUG(2,("Unknown TRANS2_IOCTL\n"));
+ reply_nterror(req, NT_STATUS_NOT_IMPLEMENTED);
+}
+
+static void handle_trans2(connection_struct *conn, struct smb_request *req,
+ struct trans_state *state)
+{
+ struct smbXsrv_connection *xconn = req->xconn;
+
+ if (xconn->protocol >= PROTOCOL_NT1) {
+ req->flags2 |= 0x40; /* IS_LONG_NAME */
+ SSVAL((discard_const_p(uint8_t, req->inbuf)),smb_flg2,req->flags2);
+ }
+
+ if (ENCRYPTION_REQUIRED(conn) && !req->encrypted) {
+ if (state->call != TRANSACT2_QFSINFO &&
+ state->call != TRANSACT2_SETFSINFO) {
+ DEBUG(0,("handle_trans2: encryption required "
+ "with call 0x%x\n",
+ (unsigned int)state->call));
+ reply_nterror(req, NT_STATUS_ACCESS_DENIED);
+ return;
+ }
+ }
+
+ /* Now we must call the relevant TRANS2 function */
+ switch(state->call) {
+ case TRANSACT2_OPEN:
+ {
+ START_PROFILE(Trans2_open);
+ call_trans2open(conn, req,
+ &state->param, state->total_param,
+ &state->data, state->total_data,
+ state->max_data_return);
+ END_PROFILE(Trans2_open);
+ break;
+ }
+
+ case TRANSACT2_FINDFIRST:
+ {
+ START_PROFILE(Trans2_findfirst);
+ call_trans2findfirst(conn, req,
+ &state->param, state->total_param,
+ &state->data, state->total_data,
+ state->max_data_return);
+ END_PROFILE(Trans2_findfirst);
+ break;
+ }
+
+ case TRANSACT2_FINDNEXT:
+ {
+ START_PROFILE(Trans2_findnext);
+ call_trans2findnext(conn, req,
+ &state->param, state->total_param,
+ &state->data, state->total_data,
+ state->max_data_return);
+ END_PROFILE(Trans2_findnext);
+ break;
+ }
+
+ case TRANSACT2_QFSINFO:
+ {
+ START_PROFILE(Trans2_qfsinfo);
+ call_trans2qfsinfo(conn, req,
+ &state->param, state->total_param,
+ &state->data, state->total_data,
+ state->max_data_return);
+ END_PROFILE(Trans2_qfsinfo);
+ break;
+ }
+
+ case TRANSACT2_SETFSINFO:
+ {
+ START_PROFILE(Trans2_setfsinfo);
+ call_trans2setfsinfo(conn, req,
+ &state->param, state->total_param,
+ &state->data, state->total_data,
+ state->max_data_return);
+ END_PROFILE(Trans2_setfsinfo);
+ break;
+ }
+
+ case TRANSACT2_QPATHINFO:
+ {
+ START_PROFILE(Trans2_qpathinfo);
+ call_trans2qpathinfo(
+ conn,
+ req,
+ &state->param,
+ state->total_param,
+ &state->data,
+ state->total_data,
+ state->max_data_return);
+ END_PROFILE(Trans2_qpathinfo);
+ break;
+ }
+
+ case TRANSACT2_QFILEINFO:
+ {
+ START_PROFILE(Trans2_qfileinfo);
+ call_trans2qfileinfo(
+ conn,
+ req,
+ &state->param,
+ state->total_param,
+ &state->data,
+ state->total_data,
+ state->max_data_return);
+ END_PROFILE(Trans2_qfileinfo);
+ break;
+ }
+
+ case TRANSACT2_SETPATHINFO:
+ {
+ START_PROFILE(Trans2_setpathinfo);
+ call_trans2setpathinfo(
+ conn,
+ req,
+ &state->param,
+ state->total_param,
+ &state->data,
+ state->total_data,
+ state->max_data_return);
+ END_PROFILE(Trans2_setpathinfo);
+ break;
+ }
+
+ case TRANSACT2_SETFILEINFO:
+ {
+ START_PROFILE(Trans2_setfileinfo);
+ call_trans2setfileinfo(
+ conn,
+ req,
+ &state->param,
+ state->total_param,
+ &state->data,
+ state->total_data,
+ state->max_data_return);
+ END_PROFILE(Trans2_setfileinfo);
+ break;
+ }
+
+ case TRANSACT2_FINDNOTIFYFIRST:
+ {
+ START_PROFILE(Trans2_findnotifyfirst);
+ call_trans2findnotifyfirst(conn, req,
+ &state->param, state->total_param,
+ &state->data, state->total_data,
+ state->max_data_return);
+ END_PROFILE(Trans2_findnotifyfirst);
+ break;
+ }
+
+ case TRANSACT2_FINDNOTIFYNEXT:
+ {
+ START_PROFILE(Trans2_findnotifynext);
+ call_trans2findnotifynext(conn, req,
+ &state->param, state->total_param,
+ &state->data, state->total_data,
+ state->max_data_return);
+ END_PROFILE(Trans2_findnotifynext);
+ break;
+ }
+
+ case TRANSACT2_MKDIR:
+ {
+ START_PROFILE(Trans2_mkdir);
+ call_trans2mkdir(conn, req,
+ &state->param, state->total_param,
+ &state->data, state->total_data,
+ state->max_data_return);
+ END_PROFILE(Trans2_mkdir);
+ break;
+ }
+
+ case TRANSACT2_GET_DFS_REFERRAL:
+ {
+ START_PROFILE(Trans2_get_dfs_referral);
+ call_trans2getdfsreferral(conn, req,
+ &state->param, state->total_param,
+ &state->data, state->total_data,
+ state->max_data_return);
+ END_PROFILE(Trans2_get_dfs_referral);
+ break;
+ }
+
+ case TRANSACT2_IOCTL:
+ {
+ START_PROFILE(Trans2_ioctl);
+ call_trans2ioctl(conn, req,
+ &state->param, state->total_param,
+ &state->data, state->total_data,
+ state->max_data_return);
+ END_PROFILE(Trans2_ioctl);
+ break;
+ }
+
+ default:
+ /* Error in request */
+ DEBUG(2,("Unknown request %d in trans2 call\n", state->call));
+ reply_nterror(req, NT_STATUS_NOT_IMPLEMENTED);
+ }
+}
+
+/****************************************************************************
+ Reply to a SMBtrans2.
+ ****************************************************************************/
+
+void reply_trans2(struct smb_request *req)
+{
+ connection_struct *conn = req->conn;
+ unsigned int dsoff;
+ unsigned int dscnt;
+ unsigned int psoff;
+ unsigned int pscnt;
+ unsigned int tran_call;
+ struct trans_state *state;
+ NTSTATUS result;
+
+ START_PROFILE(SMBtrans2);
+
+ if (req->wct < 14) {
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ END_PROFILE(SMBtrans2);
+ return;
+ }
+
+ dsoff = SVAL(req->vwv+12, 0);
+ dscnt = SVAL(req->vwv+11, 0);
+ psoff = SVAL(req->vwv+10, 0);
+ pscnt = SVAL(req->vwv+9, 0);
+ tran_call = SVAL(req->vwv+14, 0);
+
+ result = allow_new_trans(conn->pending_trans, req->mid);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(2, ("Got invalid trans2 request: %s\n",
+ nt_errstr(result)));
+ reply_nterror(req, result);
+ END_PROFILE(SMBtrans2);
+ return;
+ }
+
+ if (IS_IPC(conn)) {
+ switch (tran_call) {
+ /* List the allowed trans2 calls on IPC$ */
+ case TRANSACT2_OPEN:
+ case TRANSACT2_GET_DFS_REFERRAL:
+ case TRANSACT2_QFILEINFO:
+ case TRANSACT2_QFSINFO:
+ case TRANSACT2_SETFSINFO:
+ break;
+ default:
+ reply_nterror(req, NT_STATUS_ACCESS_DENIED);
+ END_PROFILE(SMBtrans2);
+ return;
+ }
+ }
+
+ if ((state = talloc(conn, struct trans_state)) == NULL) {
+ DEBUG(0, ("talloc failed\n"));
+ reply_nterror(req, NT_STATUS_NO_MEMORY);
+ END_PROFILE(SMBtrans2);
+ return;
+ }
+
+ state->cmd = SMBtrans2;
+
+ state->mid = req->mid;
+ state->vuid = req->vuid;
+ state->setup_count = SVAL(req->vwv+13, 0);
+ state->setup = NULL;
+ state->total_param = SVAL(req->vwv+0, 0);
+ state->param = NULL;
+ state->total_data = SVAL(req->vwv+1, 0);
+ state->data = NULL;
+ state->max_param_return = SVAL(req->vwv+2, 0);
+ state->max_data_return = SVAL(req->vwv+3, 0);
+ state->max_setup_return = SVAL(req->vwv+4, 0);
+ state->close_on_completion = BITSETW(req->vwv+5, 0);
+ state->one_way = BITSETW(req->vwv+5, 1);
+
+ state->call = tran_call;
+
+ /* All trans2 messages we handle have smb_sucnt == 1 - ensure this
+ is so as a sanity check */
+ if (state->setup_count != 1) {
+ /*
+ * Need to have rc=0 for ioctl to get job id for OS/2.
+ * Network printing will fail if function is not successful.
+ * Similar function in reply.c will be used if protocol
+ * is LANMAN1.0 instead of LM1.2X002.
+ * Until DosPrintSetJobInfo with PRJINFO3 is supported,
+ * outbuf doesn't have to be set(only job id is used).
+ */
+ if ( (state->setup_count == 4)
+ && (tran_call == TRANSACT2_IOCTL)
+ && (SVAL(req->vwv+16, 0) == LMCAT_SPL)
+ && (SVAL(req->vwv+17, 0) == LMFUNC_GETJOBID)) {
+ DEBUG(2,("Got Trans2 DevIOctl jobid\n"));
+ } else {
+ DEBUG(2,("Invalid smb_sucnt in trans2 call(%u)\n",state->setup_count));
+ DEBUG(2,("Transaction is %d\n",tran_call));
+ TALLOC_FREE(state);
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ END_PROFILE(SMBtrans2);
+ return;
+ }
+ }
+
+ if ((dscnt > state->total_data) || (pscnt > state->total_param))
+ goto bad_param;
+
+ if (state->total_data) {
+
+ if (smb_buffer_oob(state->total_data, 0, dscnt)
+ || smb_buffer_oob(smb_len(req->inbuf), dsoff, dscnt)) {
+ goto bad_param;
+ }
+
+ /* Can't use talloc here, the core routines do realloc on the
+ * params and data. */
+ state->data = (char *)SMB_MALLOC(state->total_data);
+ if (state->data == NULL) {
+ DEBUG(0,("reply_trans2: data malloc fail for %u "
+ "bytes !\n", (unsigned int)state->total_data));
+ TALLOC_FREE(state);
+ reply_nterror(req, NT_STATUS_NO_MEMORY);
+ END_PROFILE(SMBtrans2);
+ return;
+ }
+
+ memcpy(state->data,smb_base(req->inbuf)+dsoff,dscnt);
+ }
+
+ if (state->total_param) {
+
+ if (smb_buffer_oob(state->total_param, 0, pscnt)
+ || smb_buffer_oob(smb_len(req->inbuf), psoff, pscnt)) {
+ goto bad_param;
+ }
+
+ /* Can't use talloc here, the core routines do realloc on the
+ * params and data. */
+ state->param = (char *)SMB_MALLOC(state->total_param);
+ if (state->param == NULL) {
+ DEBUG(0,("reply_trans: param malloc fail for %u "
+ "bytes !\n", (unsigned int)state->total_param));
+ SAFE_FREE(state->data);
+ TALLOC_FREE(state);
+ reply_nterror(req, NT_STATUS_NO_MEMORY);
+ END_PROFILE(SMBtrans2);
+ return;
+ }
+
+ memcpy(state->param,smb_base(req->inbuf)+psoff,pscnt);
+ }
+
+ state->received_data = dscnt;
+ state->received_param = pscnt;
+
+ if ((state->received_param == state->total_param) &&
+ (state->received_data == state->total_data)) {
+
+ handle_trans2(conn, req, state);
+
+ SAFE_FREE(state->data);
+ SAFE_FREE(state->param);
+ TALLOC_FREE(state);
+ END_PROFILE(SMBtrans2);
+ return;
+ }
+
+ DLIST_ADD(conn->pending_trans, state);
+
+ /* We need to send an interim response then receive the rest
+ of the parameter/data bytes */
+ reply_smb1_outbuf(req, 0, 0);
+ show_msg((char *)req->outbuf);
+ END_PROFILE(SMBtrans2);
+ return;
+
+ bad_param:
+
+ DEBUG(0,("reply_trans2: invalid trans parameters\n"));
+ SAFE_FREE(state->data);
+ SAFE_FREE(state->param);
+ TALLOC_FREE(state);
+ END_PROFILE(SMBtrans2);
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+}
+
+/****************************************************************************
+ Reply to a SMBtranss2
+ ****************************************************************************/
+
+void reply_transs2(struct smb_request *req)
+{
+ connection_struct *conn = req->conn;
+ unsigned int pcnt,poff,dcnt,doff,pdisp,ddisp;
+ struct trans_state *state;
+
+ START_PROFILE(SMBtranss2);
+
+ show_msg((const char *)req->inbuf);
+
+ /* Windows clients expect all replies to
+ a transact secondary (SMBtranss2 0x33)
+ to have a command code of transact
+ (SMBtrans2 0x32). See bug #8989
+ and also [MS-CIFS] section 2.2.4.47.2
+ for details.
+ */
+ req->cmd = SMBtrans2;
+
+ if (req->wct < 8) {
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ END_PROFILE(SMBtranss2);
+ return;
+ }
+
+ for (state = conn->pending_trans; state != NULL;
+ state = state->next) {
+ if (state->mid == req->mid) {
+ break;
+ }
+ }
+
+ if ((state == NULL) || (state->cmd != SMBtrans2)) {
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ END_PROFILE(SMBtranss2);
+ return;
+ }
+
+ /* Revise state->total_param and state->total_data in case they have
+ changed downwards */
+
+ if (SVAL(req->vwv+0, 0) < state->total_param)
+ state->total_param = SVAL(req->vwv+0, 0);
+ if (SVAL(req->vwv+1, 0) < state->total_data)
+ state->total_data = SVAL(req->vwv+1, 0);
+
+ pcnt = SVAL(req->vwv+2, 0);
+ poff = SVAL(req->vwv+3, 0);
+ pdisp = SVAL(req->vwv+4, 0);
+
+ dcnt = SVAL(req->vwv+5, 0);
+ doff = SVAL(req->vwv+6, 0);
+ ddisp = SVAL(req->vwv+7, 0);
+
+ state->received_param += pcnt;
+ state->received_data += dcnt;
+
+ if ((state->received_data > state->total_data) ||
+ (state->received_param > state->total_param))
+ goto bad_param;
+
+ if (pcnt) {
+ if (smb_buffer_oob(state->total_param, pdisp, pcnt)
+ || smb_buffer_oob(smb_len(req->inbuf), poff, pcnt)) {
+ goto bad_param;
+ }
+ memcpy(state->param+pdisp,smb_base(req->inbuf)+poff,pcnt);
+ }
+
+ if (dcnt) {
+ if (smb_buffer_oob(state->total_data, ddisp, dcnt)
+ || smb_buffer_oob(smb_len(req->inbuf), doff, dcnt)) {
+ goto bad_param;
+ }
+ memcpy(state->data+ddisp, smb_base(req->inbuf)+doff,dcnt);
+ }
+
+ if ((state->received_param < state->total_param) ||
+ (state->received_data < state->total_data)) {
+ END_PROFILE(SMBtranss2);
+ return;
+ }
+
+ handle_trans2(conn, req, state);
+
+ DLIST_REMOVE(conn->pending_trans, state);
+ SAFE_FREE(state->data);
+ SAFE_FREE(state->param);
+ TALLOC_FREE(state);
+
+ END_PROFILE(SMBtranss2);
+ return;
+
+ bad_param:
+
+ DEBUG(0,("reply_transs2: invalid trans parameters\n"));
+ DLIST_REMOVE(conn->pending_trans, state);
+ SAFE_FREE(state->data);
+ SAFE_FREE(state->param);
+ TALLOC_FREE(state);
+ reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ END_PROFILE(SMBtranss2);
+}