From: David Mulder Date: Mon, 21 Mar 2022 18:29:26 +0000 (-0600) Subject: smbd: Move reply_trans2 to smb1_trans2.c X-Git-Tag: tevent-0.12.0~61 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=aab698e5266ed4abba01274ec7b9ef135af98efd;p=thirdparty%2Fsamba.git smbd: Move reply_trans2 to smb1_trans2.c Signed-off-by: David Mulder Reviewed-by: Jeremy Allison --- diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h index e63e72e5ffe..230ef07e169 100644 --- a/source3/smbd/proto.h +++ b/source3/smbd/proto.h @@ -1110,7 +1110,6 @@ NTSTATUS smb_set_file_time(connection_struct *conn, bool setting_write_time); void reply_findclose(struct smb_request *req); void reply_findnclose(struct smb_request *req); -void reply_trans2(struct smb_request *req); void reply_transs2(struct smb_request *req); enum perm_type { @@ -1125,6 +1124,13 @@ NTSTATUS unix_perms_from_wire(connection_struct *conn, uint32_t perms, enum perm_type ptype, mode_t *ret_perms); +struct ea_list *read_ea_list(TALLOC_CTX *ctx, const char *pdata, + size_t data_size); +unsigned int estimate_ea_size(files_struct *fsp); +NTSTATUS smb_set_fsquota(connection_struct *conn, + struct smb_request *req, + files_struct *fsp, + const DATA_BLOB *qdata); /* The following definitions come from smbd/uid.c */ diff --git a/source3/smbd/smb1_trans2.c b/source3/smbd/smb1_trans2.c index d63a239e2fd..bd14f592e89 100644 --- a/source3/smbd/smb1_trans2.c +++ b/source3/smbd/smb1_trans2.c @@ -47,6 +47,8 @@ #include "source3/lib/substitute.h" #include "source3/lib/adouble.h" +#define DIR_ENTRY_SAFETY_MARGIN 4096 + /**************************************************************************** Send the required number of replies back. We assume all fields other than the data fields are @@ -438,3 +440,2890 @@ static void smb_set_posix_lock_done(struct tevent_req *subreq) 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", 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,mtime=0; + SMB_INO_T inode = 0; + int smb_action = 0; + 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; + 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 = ¶ms[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)); + + status = filename_convert(ctx, + conn, + fname, + ucf_flags, + 0, + &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 */ + 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); + mtime = convert_timespec_to_time_t(smb_fname->st.st_ex_mtime); + 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(params,4, 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, + bool *got_exact_match, + 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, + got_exact_match, + 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 = lp_smbd_search_ask_sharemode(SNUM(conn)); + 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; + 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) { + /* Always use filesystem for UNIX mtime query. */ + ask_sharemode = false; + } + + 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 = filename_convert_smb1_search_path(talloc_tos(), + conn, + directory, + ucf_flags, + &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) { + DEBUG(4,("call_trans2findfirst: 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); + 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 */ + 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, + True, + req->smbpid, + 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'tbe 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; + + for (i=0;(i 0) { + out_of_space = True; + finished = False; + } else { + 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, + &got_exact_match, + &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++; + + /* + * As an optimisation if we know we aren't looking + * for a wildcard name (ie. the name matches the wildcard exactly) + * then we can finish on any (first) match. + * This speeds up large directory searches. JRA. + */ + + if(got_exact_match) + finished = True; + + /* 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 (get_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; +} + +/**************************************************************************** + 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 = lp_smbd_search_ask_sharemode(SNUM(conn)); + 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; + } + } + } + + DEBUG(3,("call_trans2findnext: dirhandle = %d, max_data_bytes = %d, 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: + /* Always use filesystem for UNIX mtime query. */ + ask_sharemode = false; + 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) { + DEBUG(4,("call_trans2findnext: 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(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,%ld) " + "backup_priv = %d\n", + dptr_num, mask, dirtype, + (long)fsp->dptr, + dptr_TellDir(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) { + SMB_STRUCT_STAT st; + bool posix_open = (fsp->posix_flags & FSP_POSIX_FLAGS_OPEN); + + long current_pos = 0; + /* + * 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. + */ + + finished = !dptr_SearchDir(fsp->dptr, resume_name, ¤t_pos, &st); + } /* end if resume_name && !continue_bit */ + + for (i=0;(i<(int)maxentries) && !finished && !out_of_space ;i++) { + bool got_exact_match = False; + + /* this is a heuristic to avoid seeking the fsp->dptr except when + absolutely necessary. It allows for a filename of about 40 chars */ + if (space_remaining < DIRLEN_GUESS && numentries > 0) { + out_of_space = True; + finished = False; + } else { + 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, + &got_exact_match, + &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++; + + /* + * As an optimisation if we know we aren't looking + * for a wildcard name (ie. the name matches the wildcard exactly) + * then we can finish on any (first) match. + * This speeds up large directory searches. JRA. + */ + + if(got_exact_match) + finished = True; + + 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)) { + DEBUG(5,("call_trans2findnext: 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, + 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. */ + DEBUG(10, ("call_trans2setfsinfo: set unix_info info. " + "major = %u, minor = %u cap_low = 0x%x, " + "cap_high = 0x%xn", + (unsigned int)xconn-> + smb1.unix_info.client_major, + (unsigned int)xconn-> + smb1.unix_info.client_minor, + (unsigned int)xconn-> + smb1.unix_info.client_cap_low, + (unsigned int)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, + ¶m_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_outbuf(req, 10, 0); +} + +/**************************************************************************** + Reply to a TRANSACT2_QFILEINFO on a PIPE ! +****************************************************************************/ + +static void call_trans2qpipeinfo(connection_struct *conn, + struct smb_request *req, + 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; + uint16_t info_level; + files_struct *fsp; + + if (!params) { + 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)); + if (!fsp_is_np(fsp)) { + reply_nterror(req, NT_STATUS_INVALID_HANDLE); + return; + } + + info_level = SVAL(params,2); + + *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); + + return; +} + +/**************************************************************************** + 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, + char **pparams, int total_params, + char **ppdata, int total_data, + unsigned int max_data_bytes) +{ + char *params = *pparams; + char *pdata = *ppdata; + uint16_t info_level; + unsigned int data_size = 0; + unsigned int param_size = 2; + struct smb_filename *smb_fname = NULL; + bool delete_pending = False; + struct timespec write_time_ts; + files_struct *fsp = NULL; + struct file_id fileid; + struct ea_list *ea_list = NULL; + int lock_data_count = 0; + char *lock_data = NULL; + size_t fixed_portion; + NTSTATUS status = NT_STATUS_OK; + int ret; + + if (!params) { + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + return; + } + + ZERO_STRUCT(write_time_ts); + + if (tran_call == TRANSACT2_QFILEINFO) { + if (total_params < 4) { + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + return; + } + + if (IS_IPC(conn)) { + call_trans2qpipeinfo(conn, req, tran_call, + pparams, total_params, + ppdata, total_data, + max_data_bytes); + return; + } + + fsp = file_fsp(req, SVAL(params,0)); + info_level = SVAL(params,2); + + DEBUG(3,("call_trans2qfilepathinfo: 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 (lp_smbd_getinfo_ask_sharemode(SNUM(conn))) { + 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 (lp_smbd_getinfo_ask_sharemode(SNUM(conn))) { + fileid = vfs_file_id_from_sbuf( + conn, &smb_fname->st); + get_file_infos(fileid, fsp->name_hash, + &delete_pending, + &write_time_ts); + } + } + + } else { + uint32_t name_hash; + char *fname = NULL; + uint32_t ucf_flags = ucf_flags_from_smb_request(req); + + /* qpathinfo */ + if (total_params < 7) { + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + return; + } + + info_level = SVAL(params,0); + + DEBUG(3,("call_trans2qfilepathinfo: 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, + ¶ms[6], + total_params - 6, + STR_TERMINATE, + &status); + } else { + srvstr_get_path(req, + params, + req->flags2, + &fname, + ¶ms[6], + total_params - 6, + STR_TERMINATE, + &status); + } + if (!NT_STATUS_IS_OK(status)) { + reply_nterror(req, status); + return; + } + + status = filename_convert(req, + conn, + fname, + ucf_flags, + 0, + &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() 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 (lp_smbd_getinfo_ask_sharemode(SNUM(conn))) { + 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; + } + } + + 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; + } + + case SMB_QUERY_POSIX_LOCK: + { + if (fsp == NULL || + fsp->fsp_flags.is_pathref || + fsp_get_io_fd(fsp) == -1) + { + reply_nterror(req, NT_STATUS_INVALID_HANDLE); + return; + } + + if (total_data != POSIX_LOCK_DATA_SIZE) { + reply_nterror( + req, NT_STATUS_INVALID_PARAMETER); + return; + } + + /* Copy the lock range data. */ + lock_data = (char *)talloc_memdup( + req, pdata, total_data); + if (!lock_data) { + reply_nterror(req, NT_STATUS_NO_MEMORY); + return; + } + lock_data_count = total_data; + 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); + + /* + * 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 ((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, + lock_data_count, lock_data, + req->flags2, max_data_bytes, + &fixed_portion, + ppdata, &data_size); + 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, *ppdata, data_size, + max_data_bytes); + + return; +} + +/**************************************************************************** + Reply to a TRANS2_SETFILEINFO (set file info by fileid or pathname). +****************************************************************************/ + +static void call_trans2setfilepathinfo(connection_struct *conn, + struct smb_request *req, + 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; + uint16_t info_level; + struct smb_filename *smb_fname = NULL; + files_struct *fsp = NULL; + NTSTATUS status = NT_STATUS_OK; + int data_return_size = 0; + int ret; + + if (!params) { + reply_nterror(req, NT_STATUS_INVALID_PARAMETER); + return; + } + + if (tran_call == TRANSACT2_SETFILEINFO) { + 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; + + 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)) { + uint32_t new_private_options = + fh_get_private_options(fsp->fh); + new_private_options |= + NTCREATEX_FLAG_DELETE_ON_CLOSE; + fh_set_private_options(fsp->fh, + new_private_options); + + DEBUG(3,("call_trans2setfilepathinfo: " + "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)) { + DEBUG(3,("call_trans2setfilepathinfo: fstat " + "of %s failed (%s)\n", fsp_fnum_dbg(fsp), + nt_errstr(status))); + reply_nterror(req, status); + return; + } + } + } else { + char *fname = NULL; + uint32_t ucf_flags = ucf_flags_from_smb_request(req); + bool require_existing_object = true; + + /* 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, + ¶ms[6], + total_params - 6, + STR_TERMINATE, + &status); + } else { + srvstr_get_path(req, + params, + req->flags2, + &fname, + ¶ms[6], + total_params - 6, + STR_TERMINATE, + &status); + } + if (!NT_STATUS_IS_OK(status)) { + reply_nterror(req, status); + return; + } + + status = filename_convert(req, conn, + fname, + ucf_flags, + 0, + &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; + } + + /* + * 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; + + /* + * There are 4 info levels which can + * create a new object in the filesystem. + * They are: + * SMB_SET_FILE_UNIX_LINK -> creates POSIX symlink. + * SMB_POSIX_PATH_OPEN -> creates POSIX file or directory. + * SMB_SET_FILE_UNIX_BASIC: + * SMB_SET_FILE_UNIX_INFO2: can create a POSIX special file. + * + * These info levels do not require an existing object. + */ + switch (info_level) { + case SMB_SET_FILE_UNIX_LINK: + case SMB_POSIX_PATH_OPEN: + case SMB_SET_FILE_UNIX_BASIC: + case SMB_SET_FILE_UNIX_INFO2: + require_existing_object = false; + break; + default: + break; + } + + if (!VALID_STAT(smb_fname->st) && require_existing_object) { + reply_nterror(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); + } + } + + DEBUG(3,("call_trans2setfilepathinfo(%d) %s (%s) info_level=%d " + "totdata=%d\n", tran_call, smb_fname_str_dbg(smb_fname), + fsp_fnum_dbg(fsp), + info_level,total_data)); + + /* Realloc the parameter size */ + *pparams = (char *)SMB_REALLOC(*pparams,2); + if (*pparams == NULL) { + reply_nterror(req, NT_STATUS_NO_MEMORY); + return; + } + params = *pparams; + + SSVAL(params,0,0); + + status = smbd_do_setfilepathinfo(conn, req, req, + info_level, + fsp, + smb_fname, + ppdata, total_data, + &data_return_size); + 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; + } + } + 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; + } + + /* + * Invalid EA name needs to return 2 param bytes, + * not a zero-length error packet. + */ + if (NT_STATUS_EQUAL(status, STATUS_INVALID_EA_NAME)) { + send_trans2_replies(conn, req, status, params, 2, NULL, 0, + max_data_bytes); + } else { + reply_nterror(req, status); + } + return; + } + + send_trans2_replies(conn, req, NT_STATUS_OK, params, 2, *ppdata, data_return_size, + max_data_bytes); + + return; +} + +/**************************************************************************** + 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 *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); + 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, + ¶ms[4], + total_params - 4, + STR_TERMINATE, + &status); + } else { + srvstr_get_path(ctx, + params, + req->flags2, + &directory, + ¶ms[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)); + + status = filename_convert(ctx, + conn, + directory, + ucf_flags, + 0, + &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 */ + 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); + return; +} + +/**************************************************************************** + 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); + + return; +} + +/**************************************************************************** + 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); + + return; +} + +/**************************************************************************** + Reply to a TRANS2_GET_DFS_REFERRAL - Shirish Kalele . +****************************************************************************/ + +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 (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, ¶ms[2], + total_params - 2, STR_TERMINATE); + if (!pathname) { + reply_nterror(req, NT_STATUS_NOT_FOUND); + return; + } + if((reply_size = setup_dfs_referral(conn, pathname, max_referral_level, + ppdata,&status)) < 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); + + return; +} + +#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); +} + +void handle_trans2(connection_struct *conn, struct smb_request *req, + struct trans_state *state) +{ + if (get_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; + } + } + + SMB_PERFCOUNT_SET_SUBOP(&req->pcd, state->call); + + /* 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: + case TRANSACT2_QFILEINFO: + { + START_PROFILE(Trans2_qpathinfo); + call_trans2qfilepathinfo(conn, req, state->call, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); + END_PROFILE(Trans2_qpathinfo); + break; + } + + case TRANSACT2_SETPATHINFO: + case TRANSACT2_SETFILEINFO: + { + START_PROFILE(Trans2_setpathinfo); + call_trans2setfilepathinfo(conn, req, state->call, + &state->param, state->total_param, + &state->data, state->total_data, + state->max_data_return); + END_PROFILE(Trans2_setpathinfo); + 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_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); +} diff --git a/source3/smbd/smb1_trans2.h b/source3/smbd/smb1_trans2.h index b462dc55541..2df8468ccaf 100644 --- a/source3/smbd/smb1_trans2.h +++ b/source3/smbd/smb1_trans2.h @@ -36,3 +36,6 @@ NTSTATUS smb_set_posix_lock(connection_struct *conn, const char *pdata, int total_data, files_struct *fsp); +void reply_trans2(struct smb_request *req); +void handle_trans2(connection_struct *conn, struct smb_request *req, + struct trans_state *state); diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c index 4d91eab8ee9..2a379218d46 100644 --- a/source3/smbd/trans2.c +++ b/source3/smbd/trans2.c @@ -648,7 +648,7 @@ static NTSTATUS fill_ea_chained_buffer(TALLOC_CTX *mem_ctx, return NT_STATUS_OK; } -static unsigned int estimate_ea_size(files_struct *fsp) +unsigned int estimate_ea_size(files_struct *fsp) { size_t total_ea_len = 0; TALLOC_CTX *mem_ctx; @@ -821,53 +821,12 @@ NTSTATUS set_ea(connection_struct *conn, files_struct *fsp, } return NT_STATUS_OK; } -/**************************************************************************** - 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", 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; -} /**************************************************************************** Read a list of EA names and data from an incoming data buffer. Create an ea_list with them. ****************************************************************************/ -static struct ea_list *read_ea_list(TALLOC_CTX *ctx, const char *pdata, size_t data_size) +struct ea_list *read_ea_list(TALLOC_CTX *ctx, const char *pdata, size_t data_size) { struct ea_list *ea_list_head = NULL; size_t offset = 0; @@ -941,278 +900,6 @@ static struct ea_list *ea_list_union(struct ea_list *name_list, struct ea_list * return name_list; } -/**************************************************************************** - 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,mtime=0; - SMB_INO_T inode = 0; - int smb_action = 0; - 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; - 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 = ¶ms[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)); - - status = filename_convert(ctx, - conn, - fname, - ucf_flags, - 0, - &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 */ - 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); - mtime = convert_timespec_to_time_t(smb_fname->st.st_ex_mtime); - 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(params,4, 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); -} - /********************************************************* Routine to check if a given string matches exactly. as a special case a mask of "." does NOT match. That @@ -2363,971 +2050,291 @@ NTSTATUS smbd_dirptr_lanman2_entry(TALLOC_CTX *ctx, return NT_STATUS_OK; } -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, - bool *got_exact_match, - int *last_entry_off, - struct ea_list *name_list) +unsigned char *create_volume_objectid(connection_struct *conn, unsigned char objid[16]) { - 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, - got_exact_match, - last_entry_off, name_list, NULL); + const struct loadparm_substitution *lp_sub = + loadparm_s3_global_substitution(); + + E_md4hash(lp_servicename(talloc_tos(), lp_sub, SNUM(conn)),objid); + return objid; } -/**************************************************************************** - Reply to a TRANS2_FINDFIRST. -****************************************************************************/ +static void samba_extended_info_version(struct smb_extended_info *extended_info) +{ + SMB_ASSERT(extended_info != NULL); -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) + extended_info->samba_magic = SAMBA_EXTENDED_INFO_MAGIC; + extended_info->samba_version = ((SAMBA_VERSION_MAJOR & 0xff) << 24) + | ((SAMBA_VERSION_MINOR & 0xff) << 16) + | ((SAMBA_VERSION_RELEASE & 0xff) << 8); +#ifdef SAMBA_VERSION_REVISION + extended_info->samba_version |= (tolower(*SAMBA_VERSION_REVISION) - 'a' + 1) & 0xff; +#endif + extended_info->samba_subversion = 0; +#ifdef SAMBA_VERSION_RC_RELEASE + extended_info->samba_subversion |= (SAMBA_VERSION_RC_RELEASE & 0xff) << 24; +#else +#ifdef SAMBA_VERSION_PRE_RELEASE + extended_info->samba_subversion |= (SAMBA_VERSION_PRE_RELEASE & 0xff) << 16; +#endif +#endif +#ifdef SAMBA_VERSION_VENDOR_PATCH + extended_info->samba_subversion |= (SAMBA_VERSION_VENDOR_PATCH & 0xffff); +#endif + extended_info->samba_gitcommitdate = 0; +#ifdef SAMBA_VERSION_COMMIT_TIME + unix_to_nt_time(&extended_info->samba_gitcommitdate, SAMBA_VERSION_COMMIT_TIME); +#endif + + memset(extended_info->samba_version_string, 0, + sizeof(extended_info->samba_version_string)); + + snprintf (extended_info->samba_version_string, + sizeof(extended_info->samba_version_string), + "%s", samba_version_string()); +} + +NTSTATUS smbd_do_qfsinfo(struct smbXsrv_connection *xconn, + connection_struct *conn, + TALLOC_CTX *mem_ctx, + uint16_t info_level, + uint16_t flags2, + unsigned int max_data_bytes, + size_t *fixed_portion, + struct smb_filename *fname, + char **ppdata, + int *ret_data_len) { - /* 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 = lp_smbd_search_ask_sharemode(SNUM(conn)); - 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; const struct loadparm_substitution *lp_sub = loadparm_s3_global_substitution(); + char *pdata, *end_data; + int data_len = 0; + size_t len = 0; + const char *vname = volume_label(talloc_tos(), SNUM(conn)); + int snum = SNUM(conn); + const char *fstype = lp_fstype(SNUM(conn)); + const char *filename = NULL; + const uint64_t bytes_per_sector = 512; + uint32_t additional_flags = 0; + struct smb_filename smb_fname; + SMB_STRUCT_STAT st; + NTSTATUS status = NT_STATUS_OK; + uint64_t df_ret; - if (total_params < 13) { - reply_nterror(req, NT_STATUS_INVALID_PARAMETER); - goto out; + if (fname == NULL || fname->base_name == NULL) { + filename = "."; + } else { + filename = fname->base_name; } - 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; + if (IS_IPC(conn)) { + if (info_level != SMB_QUERY_CIFS_UNIX_INFO) { + DEBUG(0,("smbd_do_qfsinfo: not an allowed " + "info level (0x%x) on IPC$.\n", + (unsigned int)info_level)); + return NT_STATUS_ACCESS_DENIED; + } } - 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; - } + DEBUG(3,("smbd_do_qfsinfo: level = %d\n", info_level)); - if (req->posix_pathnames) { - /* Always use filesystem for UNIX mtime query. */ - ask_sharemode = false; - } + smb_fname = (struct smb_filename) { + .base_name = discard_const_p(char, filename), + .flags = fname ? fname->flags : 0, + .twrp = fname ? fname->twrp : 0, + }; - 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 = filename_convert_smb1_search_path(talloc_tos(), - conn, - directory, - ucf_flags, - &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; + if(info_level != SMB_FS_QUOTA_INFORMATION + && SMB_VFS_STAT(conn, &smb_fname) != 0) { + DEBUG(2,("stat of . failed (%s)\n", strerror(errno))); + return map_nt_error_from_unix(errno); } - 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) { - DEBUG(4,("call_trans2findfirst: 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); - 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; - } - } + st = smb_fname.st; if (max_data_bytes + DIR_ENTRY_SAFETY_MARGIN < max_data_bytes) { - reply_nterror(req, NT_STATUS_INVALID_PARAMETER); - goto out; + return NT_STATUS_INVALID_PARAMETER; } *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 */ - 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, - True, - req->smbpid, - 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'tbe 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); + if (*ppdata == NULL) { + return NT_STATUS_NO_MEMORY; } - 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; - } + pdata = *ppdata; + memset((char *)pdata,'\0',max_data_bytes + DIR_ENTRY_SAFETY_MARGIN); + end_data = pdata + max_data_bytes + DIR_ENTRY_SAFETY_MARGIN - 1; - p = pdata; - space_remaining = max_data_bytes; - out_of_space = False; + *fixed_portion = 0; - for (i=0;(i 0) { - out_of_space = True; - finished = False; - } else { - 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, - &got_exact_match, - &last_entry_off, ea_list); - if (NT_STATUS_EQUAL(ntstatus, - NT_STATUS_ILLEGAL_CHARACTER)) { - /* - * Bad character conversion on name. Ignore this - * entry. - */ - continue; + block_size = lp_block_size(snum); + if (bsize < block_size) { + uint64_t factor = block_size/bsize; + bsize = block_size; + dsize /= factor; + dfree /= factor; } - if (NT_STATUS_EQUAL(ntstatus, STATUS_MORE_ENTRIES)) { - out_of_space = true; - } else { - finished = !NT_STATUS_IS_OK(ntstatus); + if (bsize > block_size) { + uint64_t factor = bsize/block_size; + bsize = block_size; + dsize *= factor; + dfree *= factor; } - } - - if (!finished && !out_of_space) - numentries++; - - /* - * As an optimisation if we know we aren't looking - * for a wildcard name (ie. the name matches the wildcard exactly) - * then we can finish on any (first) match. - * This speeds up large directory searches. JRA. - */ - - if(got_exact_match) - finished = True; - - /* 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 (get_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. */ + sectors_per_unit = bsize/bytes_per_sector; - /* 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); + DEBUG(5,("smbd_do_qfsinfo : SMB_INFO_ALLOCATION id=%x, bsize=%u, cSectorUnit=%u, \ +cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)st.st_ex_dev, (unsigned int)bsize, (unsigned int)sectors_per_unit, + (unsigned int)bytes_per_sector, (unsigned int)dsize, (unsigned int)dfree)); - send_trans2_replies(conn, req, NT_STATUS_OK, params, 10, pdata, PTR_DIFF(p,pdata), - max_data_bytes); + /* + * For large drives, return max values and not modulo. + */ + dsize = MIN(dsize, UINT32_MAX); + dfree = MIN(dfree, UINT32_MAX); - 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); + SIVAL(pdata,l1_idFileSystem,st.st_ex_dev); + SIVAL(pdata,l1_cSectorUnit,sectors_per_unit); + SIVAL(pdata,l1_cUnit,dsize); + SIVAL(pdata,l1_cUnitAvail,dfree); + SSVAL(pdata,l1_cbSector,bytes_per_sector); + break; } - } - - 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; -} -/**************************************************************************** - Reply to a TRANS2_FINDNEXT. -****************************************************************************/ + case SMB_INFO_VOLUME: + /* Return volume name */ + /* + * Add volume serial number - hash of a combination of + * the called hostname and the service name. + */ + SIVAL(pdata,0,str_checksum(lp_servicename(talloc_tos(), lp_sub, snum)) ^ (str_checksum(get_local_machine_name())<<16) ); + /* + * Win2k3 and previous mess this up by sending a name length + * one byte short. I believe only older clients (OS/2 Win9x) use + * this call so try fixing this by adding a terminating null to + * the pushed string. The change here was adding the STR_TERMINATE. JRA. + */ + status = srvstr_push( + pdata, flags2, + pdata+l2_vol_szVolLabel, vname, + PTR_DIFF(end_data, pdata+l2_vol_szVolLabel), + STR_NOALIGN|STR_TERMINATE, &len); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + SCVAL(pdata,l2_vol_cch,len); + data_len = l2_vol_szVolLabel + len; + DEBUG(5,("smbd_do_qfsinfo : time = %x, namelen = %u, name = %s\n", + (unsigned)convert_timespec_to_time_t(st.st_ex_ctime), + (unsigned)len, vname)); + break; -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 = lp_smbd_search_ask_sharemode(SNUM(conn)); - 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(); + case SMB_QUERY_FS_ATTRIBUTE_INFO: + case SMB_FS_ATTRIBUTE_INFORMATION: - if (total_params < 13) { - reply_nterror(req, NT_STATUS_INVALID_PARAMETER); - return; - } + additional_flags = 0; +#if defined(HAVE_SYS_QUOTAS) + additional_flags |= FILE_VOLUME_QUOTAS; +#endif - 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; + if(lp_nt_acl_support(SNUM(conn))) { + additional_flags |= FILE_PERSISTENT_ACLS; } - } - } - DEBUG(3,("call_trans2findnext: dirhandle = %d, max_data_bytes = %d, 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)); + /* Capabilities are filled in at connection time through STATVFS call */ + additional_flags |= conn->fs_capabilities; + additional_flags |= lp_parm_int(conn->params->service, + "share", "fake_fscaps", + 0); - if (!maxentries) { - /* W2K3 seems to treat zero as 1. */ - maxentries = 1; - } + SIVAL(pdata,0,FILE_CASE_PRESERVED_NAMES|FILE_CASE_SENSITIVE_SEARCH| + FILE_SUPPORTS_OBJECT_IDS|FILE_UNICODE_ON_DISK| + additional_flags); /* FS ATTRIBUTES */ - 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: - /* Always use filesystem for UNIX mtime query. */ - ask_sharemode = false; - if (!lp_smb1_unix_extensions()) { - reply_nterror(req, NT_STATUS_INVALID_LEVEL); - return; + SIVAL(pdata,4,255); /* Max filename component length */ + /* NOTE! the fstype must *not* be null terminated or win98 won't recognise it + and will think we can't do long filenames */ + status = srvstr_push(pdata, flags2, pdata+12, fstype, + PTR_DIFF(end_data, pdata+12), + STR_UNICODE, &len); + if (!NT_STATUS_IS_OK(status)) { + return status; } - if (!req->posix_pathnames) { - reply_nterror(req, NT_STATUS_INVALID_LEVEL); - return; + SIVAL(pdata,8,len); + data_len = 12 + len; + if (max_data_bytes >= 16 && data_len > max_data_bytes) { + /* the client only requested a portion of the + file system name */ + data_len = max_data_bytes; + status = STATUS_BUFFER_OVERFLOW; } + *fixed_portion = 16; 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) { - DEBUG(4,("call_trans2findnext: 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; - } + case SMB_QUERY_FS_LABEL_INFO: + case SMB_FS_LABEL_INFORMATION: + status = srvstr_push(pdata, flags2, pdata+4, vname, + PTR_DIFF(end_data, pdata+4), 0, &len); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + data_len = 4 + len; + SIVAL(pdata,0,len); + break; - if (!lp_ea_support(SNUM(conn))) { - reply_nterror(req, NT_STATUS_EAS_NOT_SUPPORTED); - return; - } + case SMB_QUERY_FS_VOLUME_INFO: + case SMB_FS_VOLUME_INFORMATION: - /* 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,%ld) " - "backup_priv = %d\n", - dptr_num, mask, dirtype, - (long)fsp->dptr, - dptr_TellDir(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) { - SMB_STRUCT_STAT st; - bool posix_open = (fsp->posix_flags & FSP_POSIX_FLAGS_OPEN); - - long current_pos = 0; - /* - * 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. - */ + /* + * Add volume serial number - hash of a combination of + * the called hostname and the service name. + */ + SIVAL(pdata,8,str_checksum(lp_servicename(talloc_tos(), lp_sub, snum)) ^ + (str_checksum(get_local_machine_name())<<16)); - 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; + /* Max label len is 32 characters. */ + status = srvstr_push(pdata, flags2, pdata+18, vname, + PTR_DIFF(end_data, pdata+18), + STR_UNICODE, &len); + if (!NT_STATUS_IS_OK(status)) { + return status; } - } - - /* - * 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. - */ - - finished = !dptr_SearchDir(fsp->dptr, resume_name, ¤t_pos, &st); - } /* end if resume_name && !continue_bit */ - - for (i=0;(i<(int)maxentries) && !finished && !out_of_space ;i++) { - bool got_exact_match = False; + SIVAL(pdata,12,len); + data_len = 18+len; - /* this is a heuristic to avoid seeking the fsp->dptr except when - absolutely necessary. It allows for a filename of about 40 chars */ - if (space_remaining < DIRLEN_GUESS && numentries > 0) { - out_of_space = True; - finished = False; - } else { - 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, - &got_exact_match, - &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); + DEBUG(5,("smbd_do_qfsinfo : SMB_QUERY_FS_VOLUME_INFO namelen = %d, vol=%s serv=%s\n", + (int)strlen(vname),vname, + lp_servicename(talloc_tos(), lp_sub, snum))); + if (max_data_bytes >= 24 && data_len > max_data_bytes) { + /* the client only requested a portion of the + volume label */ + data_len = max_data_bytes; + status = STATUS_BUFFER_OVERFLOW; } - } - - if (!finished && !out_of_space) - numentries++; - - /* - * As an optimisation if we know we aren't looking - * for a wildcard name (ie. the name matches the wildcard exactly) - * then we can finish on any (first) match. - * This speeds up large directory searches. JRA. - */ - - if(got_exact_match) - finished = True; - - 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)) { - DEBUG(5,("call_trans2findnext: 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; -} - -unsigned char *create_volume_objectid(connection_struct *conn, unsigned char objid[16]) -{ - const struct loadparm_substitution *lp_sub = - loadparm_s3_global_substitution(); - - E_md4hash(lp_servicename(talloc_tos(), lp_sub, SNUM(conn)),objid); - return objid; -} - -static void samba_extended_info_version(struct smb_extended_info *extended_info) -{ - SMB_ASSERT(extended_info != NULL); - - extended_info->samba_magic = SAMBA_EXTENDED_INFO_MAGIC; - extended_info->samba_version = ((SAMBA_VERSION_MAJOR & 0xff) << 24) - | ((SAMBA_VERSION_MINOR & 0xff) << 16) - | ((SAMBA_VERSION_RELEASE & 0xff) << 8); -#ifdef SAMBA_VERSION_REVISION - extended_info->samba_version |= (tolower(*SAMBA_VERSION_REVISION) - 'a' + 1) & 0xff; -#endif - extended_info->samba_subversion = 0; -#ifdef SAMBA_VERSION_RC_RELEASE - extended_info->samba_subversion |= (SAMBA_VERSION_RC_RELEASE & 0xff) << 24; -#else -#ifdef SAMBA_VERSION_PRE_RELEASE - extended_info->samba_subversion |= (SAMBA_VERSION_PRE_RELEASE & 0xff) << 16; -#endif -#endif -#ifdef SAMBA_VERSION_VENDOR_PATCH - extended_info->samba_subversion |= (SAMBA_VERSION_VENDOR_PATCH & 0xffff); -#endif - extended_info->samba_gitcommitdate = 0; -#ifdef SAMBA_VERSION_COMMIT_TIME - unix_to_nt_time(&extended_info->samba_gitcommitdate, SAMBA_VERSION_COMMIT_TIME); -#endif - - memset(extended_info->samba_version_string, 0, - sizeof(extended_info->samba_version_string)); - - snprintf (extended_info->samba_version_string, - sizeof(extended_info->samba_version_string), - "%s", samba_version_string()); -} - -NTSTATUS smbd_do_qfsinfo(struct smbXsrv_connection *xconn, - connection_struct *conn, - TALLOC_CTX *mem_ctx, - uint16_t info_level, - uint16_t flags2, - unsigned int max_data_bytes, - size_t *fixed_portion, - struct smb_filename *fname, - char **ppdata, - int *ret_data_len) -{ - const struct loadparm_substitution *lp_sub = - loadparm_s3_global_substitution(); - char *pdata, *end_data; - int data_len = 0; - size_t len = 0; - const char *vname = volume_label(talloc_tos(), SNUM(conn)); - int snum = SNUM(conn); - const char *fstype = lp_fstype(SNUM(conn)); - const char *filename = NULL; - const uint64_t bytes_per_sector = 512; - uint32_t additional_flags = 0; - struct smb_filename smb_fname; - SMB_STRUCT_STAT st; - NTSTATUS status = NT_STATUS_OK; - uint64_t df_ret; - - if (fname == NULL || fname->base_name == NULL) { - filename = "."; - } else { - filename = fname->base_name; - } - - if (IS_IPC(conn)) { - if (info_level != SMB_QUERY_CIFS_UNIX_INFO) { - DEBUG(0,("smbd_do_qfsinfo: not an allowed " - "info level (0x%x) on IPC$.\n", - (unsigned int)info_level)); - return NT_STATUS_ACCESS_DENIED; - } - } - - DEBUG(3,("smbd_do_qfsinfo: level = %d\n", info_level)); - - smb_fname = (struct smb_filename) { - .base_name = discard_const_p(char, filename), - .flags = fname ? fname->flags : 0, - .twrp = fname ? fname->twrp : 0, - }; - - if(info_level != SMB_FS_QUOTA_INFORMATION - && SMB_VFS_STAT(conn, &smb_fname) != 0) { - DEBUG(2,("stat of . failed (%s)\n", strerror(errno))); - return map_nt_error_from_unix(errno); - } - - st = smb_fname.st; - - if (max_data_bytes + DIR_ENTRY_SAFETY_MARGIN < max_data_bytes) { - return NT_STATUS_INVALID_PARAMETER; - } - - *ppdata = (char *)SMB_REALLOC( - *ppdata, max_data_bytes + DIR_ENTRY_SAFETY_MARGIN); - if (*ppdata == NULL) { - return NT_STATUS_NO_MEMORY; - } - - pdata = *ppdata; - memset((char *)pdata,'\0',max_data_bytes + DIR_ENTRY_SAFETY_MARGIN); - end_data = pdata + max_data_bytes + DIR_ENTRY_SAFETY_MARGIN - 1; - - *fixed_portion = 0; + *fixed_portion = 24; + break; - switch (info_level) { - case SMB_INFO_ALLOCATION: + case SMB_QUERY_FS_SIZE_INFO: + case SMB_FS_SIZE_INFORMATION: { uint64_t dfree,dsize,bsize,block_size,sectors_per_unit; - data_len = 18; + data_len = 24; df_ret = get_dfree_info(conn, &smb_fname, &bsize, &dfree, &dsize); if (df_ret == (uint64_t)-1) { return map_nt_error_from_unix(errno); } - block_size = lp_block_size(snum); if (bsize < block_size) { uint64_t factor = block_size/bsize; @@ -3342,170 +2349,14 @@ NTSTATUS smbd_do_qfsinfo(struct smbXsrv_connection *xconn, dfree *= factor; } sectors_per_unit = bsize/bytes_per_sector; - - DEBUG(5,("smbd_do_qfsinfo : SMB_INFO_ALLOCATION id=%x, bsize=%u, cSectorUnit=%u, \ -cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)st.st_ex_dev, (unsigned int)bsize, (unsigned int)sectors_per_unit, + DEBUG(5,("smbd_do_qfsinfo : SMB_QUERY_FS_SIZE_INFO bsize=%u, cSectorUnit=%u, \ +cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned int)sectors_per_unit, (unsigned int)bytes_per_sector, (unsigned int)dsize, (unsigned int)dfree)); - - /* - * For large drives, return max values and not modulo. - */ - dsize = MIN(dsize, UINT32_MAX); - dfree = MIN(dfree, UINT32_MAX); - - SIVAL(pdata,l1_idFileSystem,st.st_ex_dev); - SIVAL(pdata,l1_cSectorUnit,sectors_per_unit); - SIVAL(pdata,l1_cUnit,dsize); - SIVAL(pdata,l1_cUnitAvail,dfree); - SSVAL(pdata,l1_cbSector,bytes_per_sector); - break; - } - - case SMB_INFO_VOLUME: - /* Return volume name */ - /* - * Add volume serial number - hash of a combination of - * the called hostname and the service name. - */ - SIVAL(pdata,0,str_checksum(lp_servicename(talloc_tos(), lp_sub, snum)) ^ (str_checksum(get_local_machine_name())<<16) ); - /* - * Win2k3 and previous mess this up by sending a name length - * one byte short. I believe only older clients (OS/2 Win9x) use - * this call so try fixing this by adding a terminating null to - * the pushed string. The change here was adding the STR_TERMINATE. JRA. - */ - status = srvstr_push( - pdata, flags2, - pdata+l2_vol_szVolLabel, vname, - PTR_DIFF(end_data, pdata+l2_vol_szVolLabel), - STR_NOALIGN|STR_TERMINATE, &len); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - SCVAL(pdata,l2_vol_cch,len); - data_len = l2_vol_szVolLabel + len; - DEBUG(5,("smbd_do_qfsinfo : time = %x, namelen = %u, name = %s\n", - (unsigned)convert_timespec_to_time_t(st.st_ex_ctime), - (unsigned)len, vname)); - break; - - case SMB_QUERY_FS_ATTRIBUTE_INFO: - case SMB_FS_ATTRIBUTE_INFORMATION: - - additional_flags = 0; -#if defined(HAVE_SYS_QUOTAS) - additional_flags |= FILE_VOLUME_QUOTAS; -#endif - - if(lp_nt_acl_support(SNUM(conn))) { - additional_flags |= FILE_PERSISTENT_ACLS; - } - - /* Capabilities are filled in at connection time through STATVFS call */ - additional_flags |= conn->fs_capabilities; - additional_flags |= lp_parm_int(conn->params->service, - "share", "fake_fscaps", - 0); - - SIVAL(pdata,0,FILE_CASE_PRESERVED_NAMES|FILE_CASE_SENSITIVE_SEARCH| - FILE_SUPPORTS_OBJECT_IDS|FILE_UNICODE_ON_DISK| - additional_flags); /* FS ATTRIBUTES */ - - SIVAL(pdata,4,255); /* Max filename component length */ - /* NOTE! the fstype must *not* be null terminated or win98 won't recognise it - and will think we can't do long filenames */ - status = srvstr_push(pdata, flags2, pdata+12, fstype, - PTR_DIFF(end_data, pdata+12), - STR_UNICODE, &len); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - SIVAL(pdata,8,len); - data_len = 12 + len; - if (max_data_bytes >= 16 && data_len > max_data_bytes) { - /* the client only requested a portion of the - file system name */ - data_len = max_data_bytes; - status = STATUS_BUFFER_OVERFLOW; - } - *fixed_portion = 16; - break; - - case SMB_QUERY_FS_LABEL_INFO: - case SMB_FS_LABEL_INFORMATION: - status = srvstr_push(pdata, flags2, pdata+4, vname, - PTR_DIFF(end_data, pdata+4), 0, &len); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - data_len = 4 + len; - SIVAL(pdata,0,len); - break; - - case SMB_QUERY_FS_VOLUME_INFO: - case SMB_FS_VOLUME_INFORMATION: - - /* - * Add volume serial number - hash of a combination of - * the called hostname and the service name. - */ - SIVAL(pdata,8,str_checksum(lp_servicename(talloc_tos(), lp_sub, snum)) ^ - (str_checksum(get_local_machine_name())<<16)); - - /* Max label len is 32 characters. */ - status = srvstr_push(pdata, flags2, pdata+18, vname, - PTR_DIFF(end_data, pdata+18), - STR_UNICODE, &len); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - SIVAL(pdata,12,len); - data_len = 18+len; - - DEBUG(5,("smbd_do_qfsinfo : SMB_QUERY_FS_VOLUME_INFO namelen = %d, vol=%s serv=%s\n", - (int)strlen(vname),vname, - lp_servicename(talloc_tos(), lp_sub, snum))); - if (max_data_bytes >= 24 && data_len > max_data_bytes) { - /* the client only requested a portion of the - volume label */ - data_len = max_data_bytes; - status = STATUS_BUFFER_OVERFLOW; - } - *fixed_portion = 24; - break; - - case SMB_QUERY_FS_SIZE_INFO: - case SMB_FS_SIZE_INFORMATION: - { - uint64_t dfree,dsize,bsize,block_size,sectors_per_unit; - data_len = 24; - df_ret = get_dfree_info(conn, &smb_fname, &bsize, - &dfree, &dsize); - if (df_ret == (uint64_t)-1) { - return map_nt_error_from_unix(errno); - } - block_size = lp_block_size(snum); - if (bsize < block_size) { - uint64_t factor = block_size/bsize; - bsize = block_size; - dsize /= factor; - dfree /= factor; - } - if (bsize > block_size) { - uint64_t factor = bsize/block_size; - bsize = block_size; - dsize *= factor; - dfree *= factor; - } - sectors_per_unit = bsize/bytes_per_sector; - DEBUG(5,("smbd_do_qfsinfo : SMB_QUERY_FS_SIZE_INFO bsize=%u, cSectorUnit=%u, \ -cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned int)sectors_per_unit, - (unsigned int)bytes_per_sector, (unsigned int)dsize, (unsigned int)dfree)); - SBIG_UINT(pdata,0,dsize); - SBIG_UINT(pdata,8,dfree); - SIVAL(pdata,16,sectors_per_unit); - SIVAL(pdata,20,bytes_per_sector); - *fixed_portion = 24; + SBIG_UINT(pdata,0,dsize); + SBIG_UINT(pdata,8,dfree); + SIVAL(pdata,16,sectors_per_unit); + SIVAL(pdata,20,bytes_per_sector); + *fixed_portion = 24; break; } @@ -3884,10 +2735,10 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned return status; } -static NTSTATUS smb_set_fsquota(connection_struct *conn, - struct smb_request *req, - files_struct *fsp, - const DATA_BLOB *qdata) +NTSTATUS smb_set_fsquota(connection_struct *conn, + struct smb_request *req, + files_struct *fsp, + const DATA_BLOB *qdata) { const struct loadparm_substitution *lp_sub = loadparm_s3_global_substitution(); @@ -3966,271 +2817,6 @@ NTSTATUS smbd_do_setfsinfo(connection_struct *conn, return NT_STATUS_INVALID_LEVEL; } -/**************************************************************************** - 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, - 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. */ - DEBUG(10, ("call_trans2setfsinfo: set unix_info info. " - "major = %u, minor = %u cap_low = 0x%x, " - "cap_high = 0x%xn", - (unsigned int)xconn-> - smb1.unix_info.client_major, - (unsigned int)xconn-> - smb1.unix_info.client_minor, - (unsigned int)xconn-> - smb1.unix_info.client_cap_low, - (unsigned int)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, - ¶m_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_outbuf(req, 10, 0); -} - #if defined(HAVE_POSIX_ACLS) /**************************************************************************** Utility function to count the number of entries in a POSIX acl. @@ -4822,81 +3408,6 @@ static NTSTATUS smb_query_posix_acl(connection_struct *conn, } #endif -/**************************************************************************** - Reply to a TRANSACT2_QFILEINFO on a PIPE ! -****************************************************************************/ - -static void call_trans2qpipeinfo(connection_struct *conn, - struct smb_request *req, - 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; - uint16_t info_level; - files_struct *fsp; - - if (!params) { - 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)); - if (!fsp_is_np(fsp)) { - reply_nterror(req, NT_STATUS_INVALID_HANDLE); - return; - } - - info_level = SVAL(params,2); - - *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); - - return; -} - NTSTATUS smbd_do_qfilepathinfo(connection_struct *conn, TALLOC_CTX *mem_ctx, struct smb_request *req, @@ -5707,423 +4218,6 @@ NTSTATUS smbd_do_qfilepathinfo(connection_struct *conn, return NT_STATUS_OK; } -/**************************************************************************** - 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, - char **pparams, int total_params, - char **ppdata, int total_data, - unsigned int max_data_bytes) -{ - char *params = *pparams; - char *pdata = *ppdata; - uint16_t info_level; - unsigned int data_size = 0; - unsigned int param_size = 2; - struct smb_filename *smb_fname = NULL; - bool delete_pending = False; - struct timespec write_time_ts; - files_struct *fsp = NULL; - struct file_id fileid; - struct ea_list *ea_list = NULL; - int lock_data_count = 0; - char *lock_data = NULL; - size_t fixed_portion; - NTSTATUS status = NT_STATUS_OK; - int ret; - - if (!params) { - reply_nterror(req, NT_STATUS_INVALID_PARAMETER); - return; - } - - ZERO_STRUCT(write_time_ts); - - if (tran_call == TRANSACT2_QFILEINFO) { - if (total_params < 4) { - reply_nterror(req, NT_STATUS_INVALID_PARAMETER); - return; - } - - if (IS_IPC(conn)) { - call_trans2qpipeinfo(conn, req, tran_call, - pparams, total_params, - ppdata, total_data, - max_data_bytes); - return; - } - - fsp = file_fsp(req, SVAL(params,0)); - info_level = SVAL(params,2); - - DEBUG(3,("call_trans2qfilepathinfo: 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 (lp_smbd_getinfo_ask_sharemode(SNUM(conn))) { - 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 (lp_smbd_getinfo_ask_sharemode(SNUM(conn))) { - fileid = vfs_file_id_from_sbuf( - conn, &smb_fname->st); - get_file_infos(fileid, fsp->name_hash, - &delete_pending, - &write_time_ts); - } - } - - } else { - uint32_t name_hash; - char *fname = NULL; - uint32_t ucf_flags = ucf_flags_from_smb_request(req); - - /* qpathinfo */ - if (total_params < 7) { - reply_nterror(req, NT_STATUS_INVALID_PARAMETER); - return; - } - - info_level = SVAL(params,0); - - DEBUG(3,("call_trans2qfilepathinfo: 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, - ¶ms[6], - total_params - 6, - STR_TERMINATE, - &status); - } else { - srvstr_get_path(req, - params, - req->flags2, - &fname, - ¶ms[6], - total_params - 6, - STR_TERMINATE, - &status); - } - if (!NT_STATUS_IS_OK(status)) { - reply_nterror(req, status); - return; - } - - status = filename_convert(req, - conn, - fname, - ucf_flags, - 0, - &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() 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 (lp_smbd_getinfo_ask_sharemode(SNUM(conn))) { - 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; - } - } - - 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; - } - - case SMB_QUERY_POSIX_LOCK: - { - if (fsp == NULL || - fsp->fsp_flags.is_pathref || - fsp_get_io_fd(fsp) == -1) - { - reply_nterror(req, NT_STATUS_INVALID_HANDLE); - return; - } - - if (total_data != POSIX_LOCK_DATA_SIZE) { - reply_nterror( - req, NT_STATUS_INVALID_PARAMETER); - return; - } - - /* Copy the lock range data. */ - lock_data = (char *)talloc_memdup( - req, pdata, total_data); - if (!lock_data) { - reply_nterror(req, NT_STATUS_NO_MEMORY); - return; - } - lock_data_count = total_data; - 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); - - /* - * 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 ((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, - lock_data_count, lock_data, - req->flags2, max_data_bytes, - &fixed_portion, - ppdata, &data_size); - 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, *ppdata, data_size, - max_data_bytes); - - return; -} - /**************************************************************************** Set a hard link (called by UNIX extensions and by NT rename with HARD link code. @@ -8255,991 +6349,207 @@ static NTSTATUS smb_posix_open(connection_struct *conn, 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) { - 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 */ - 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. -****************************************************************************/ - -static NTSTATUS smb_posix_unlink(connection_struct *conn, - struct smb_request *req, - const char *pdata, - int total_data, - struct smb_filename *smb_fname) -{ - NTSTATUS status = NT_STATUS_OK; - files_struct *fsp = NULL; - uint16_t flags = 0; - char del = 1; - int info = 0; - int create_options = 0; - struct share_mode_lock *lck = NULL; - bool other_nonposix_opens; - struct smb2_create_blobs *posx = NULL; - - 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 (VALID_STAT_OF_DIR(smb_fname->st)) { - 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 */ - 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. - */ - - lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id); - if (lck == NULL) { - DEBUG(0, ("smb_posix_unlink: Could not get share mode " - "lock for file %s\n", fsp_str_dbg(fsp))); - close_file_free(req, &fsp, NORMAL_CLOSE); - return NT_STATUS_INVALID_PARAMETER; - } - - other_nonposix_opens = has_other_nonposix_opens(lck, fsp); - if (other_nonposix_opens) { - /* Fail with sharing violation. */ - TALLOC_FREE(lck); - close_file_free(req, &fsp, NORMAL_CLOSE); - return NT_STATUS_SHARING_VIOLATION; - } - - /* - * Set the delete on close. - */ - status = smb_set_file_disposition_info(conn, - &del, - 1, - fsp, - smb_fname); - - TALLOC_FREE(lck); - - if (!NT_STATUS_IS_OK(status)) { - close_file_free(req, &fsp, NORMAL_CLOSE); - return status; - } - return close_file_free(req, &fsp, NORMAL_CLOSE); -} - -static NTSTATUS smbd_do_posix_setfilepathinfo(struct connection_struct *conn, - struct smb_request *req, - TALLOC_CTX *mem_ctx, - uint16_t info_level, - struct smb_filename *smb_fname, - files_struct *fsp, - char **ppdata, - int total_data, - int *ret_data_size) -{ - char *pdata = *ppdata; - NTSTATUS status = NT_STATUS_OK; - int data_return_size = 0; - - *ret_data_size = 0; - - if (!CAN_WRITE(conn)) { - /* Allow POSIX opens. The open path will deny - * any non-readonly opens. */ - if (info_level != SMB_POSIX_PATH_OPEN) { - return NT_STATUS_DOS(ERRSRV, ERRaccess); - } - } - - DBG_DEBUG("file=%s (%s) info_level=%d totdata=%d\n", - smb_fname_str_dbg(smb_fname), - fsp_fnum_dbg(fsp), - info_level, - total_data); - - switch (info_level) { - case SMB_SET_FILE_UNIX_BASIC: - { - status = smb_set_file_unix_basic(conn, req, - pdata, - total_data, - fsp, - smb_fname); - break; - } - - case SMB_SET_FILE_UNIX_INFO2: - { - status = smb_set_file_unix_info2(conn, req, - pdata, - total_data, - fsp, - smb_fname); - break; - } - - case SMB_SET_FILE_UNIX_LINK: - { - if (smb_fname == NULL) { - /* We must have a pathname for this. */ - return NT_STATUS_INVALID_LEVEL; - } - status = smb_set_file_unix_link(conn, req, pdata, - total_data, smb_fname); - break; - } - - case SMB_SET_FILE_UNIX_HLINK: - { - if (smb_fname == NULL) { - /* We must have a pathname for this. */ - return NT_STATUS_INVALID_LEVEL; - } - status = smb_set_file_unix_hlink(conn, req, - pdata, total_data, - smb_fname); - break; - } - -#if defined(HAVE_POSIX_ACLS) - case SMB_SET_POSIX_ACL: - { - status = smb_set_posix_acl(conn, - req, - pdata, - total_data, - fsp, - smb_fname); - break; - } -#endif - - case SMB_SET_POSIX_LOCK: - { - if (fsp == NULL) { - return NT_STATUS_INVALID_LEVEL; - } - status = smb_set_posix_lock(conn, req, - pdata, total_data, fsp); - break; - } - - case SMB_POSIX_PATH_OPEN: - { - if (smb_fname == NULL) { - /* We must have a pathname for this. */ - return NT_STATUS_INVALID_LEVEL; - } - - status = smb_posix_open(conn, req, - ppdata, - total_data, - smb_fname, - &data_return_size); - break; - } - - case SMB_POSIX_PATH_UNLINK: - { - if (smb_fname == NULL) { - /* We must have a pathname for this. */ - return NT_STATUS_INVALID_LEVEL; - } - - status = smb_posix_unlink(conn, req, - pdata, - total_data, - smb_fname); - break; - } - - default: - return NT_STATUS_INVALID_LEVEL; - } - - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - *ret_data_size = data_return_size; - return NT_STATUS_OK; -} - -NTSTATUS smbd_do_setfilepathinfo(connection_struct *conn, - struct smb_request *req, - TALLOC_CTX *mem_ctx, - uint16_t info_level, - files_struct *fsp, - struct smb_filename *smb_fname, - char **ppdata, int total_data, - int *ret_data_size) -{ - char *pdata = *ppdata; - NTSTATUS status = NT_STATUS_OK; - int data_return_size = 0; - - if (INFO_LEVEL_IS_UNIX(info_level)) { - if (!lp_smb1_unix_extensions()) { - return NT_STATUS_INVALID_LEVEL; - } - if (!req->posix_pathnames) { - return NT_STATUS_INVALID_LEVEL; - } - status = smbd_do_posix_setfilepathinfo(conn, - req, - req, - info_level, - smb_fname, - fsp, - ppdata, - total_data, - &data_return_size); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - *ret_data_size = data_return_size; - return NT_STATUS_OK; - } - - *ret_data_size = 0; - - DEBUG(3,("smbd_do_setfilepathinfo: %s (%s) info_level=%d " - "totdata=%d\n", smb_fname_str_dbg(smb_fname), - fsp_fnum_dbg(fsp), - info_level, total_data)); - - switch (info_level) { - - case SMB_INFO_STANDARD: - { - status = smb_set_info_standard(conn, - pdata, - total_data, - fsp, - smb_fname); - break; - } - - case SMB_INFO_SET_EA: - { - status = smb_info_set_ea(conn, - pdata, - total_data, - fsp, - smb_fname); - break; - } - - case SMB_SET_FILE_BASIC_INFO: - case SMB_FILE_BASIC_INFORMATION: - { - status = smb_set_file_basic_info(conn, - pdata, - total_data, - fsp, - smb_fname); - break; - } - - case SMB_FILE_ALLOCATION_INFORMATION: - case SMB_SET_FILE_ALLOCATION_INFO: - { - status = smb_set_file_allocation_info(conn, req, - pdata, - total_data, - fsp, - smb_fname); - break; - } - - case SMB_FILE_END_OF_FILE_INFORMATION: - case SMB_SET_FILE_END_OF_FILE_INFO: - { - /* - * XP/Win7 both fail after the createfile with - * SMB_SET_FILE_END_OF_FILE_INFO but not - * SMB_FILE_END_OF_FILE_INFORMATION (pass-through). - * The level is known here, so pass it down - * appropriately. - */ - bool should_fail = - (info_level == SMB_SET_FILE_END_OF_FILE_INFO); - - status = smb_set_file_end_of_file_info(conn, req, - pdata, - total_data, - fsp, - smb_fname, - should_fail); - break; - } - - case SMB_FILE_DISPOSITION_INFORMATION: - case SMB_SET_FILE_DISPOSITION_INFO: /* Set delete on close for open file. */ - { -#if 0 - /* JRA - We used to just ignore this on a path ? - * Shouldn't this be invalid level on a pathname - * based call ? - */ - if (tran_call != TRANSACT2_SETFILEINFO) { - return ERROR_NT(NT_STATUS_INVALID_LEVEL); - } -#endif - status = smb_set_file_disposition_info(conn, - pdata, - total_data, - fsp, - smb_fname); - break; - } - - case SMB_FILE_POSITION_INFORMATION: - { - status = smb_file_position_information(conn, - pdata, - total_data, - fsp); - break; - } - - case SMB_FILE_FULL_EA_INFORMATION: - { - status = smb_set_file_full_ea_info(conn, - pdata, - total_data, - fsp); - break; - } - - /* From tridge Samba4 : - * MODE_INFORMATION in setfileinfo (I have no - * idea what "mode information" on a file is - it takes a value of 0, - * 2, 4 or 6. What could it be?). - */ - - case SMB_FILE_MODE_INFORMATION: - { - status = smb_file_mode_information(conn, - pdata, - total_data); - break; - } - - /* [MS-SMB2] 3.3.5.21.1 states we MUST fail with STATUS_NOT_SUPPORTED. */ - case SMB_FILE_VALID_DATA_LENGTH_INFORMATION: - case SMB_FILE_SHORT_NAME_INFORMATION: - return NT_STATUS_NOT_SUPPORTED; - - case SMB_FILE_RENAME_INFORMATION: - { - status = smb_file_rename_information(conn, req, - pdata, total_data, - fsp, smb_fname); - break; - } - - case SMB2_FILE_RENAME_INFORMATION_INTERNAL: - { - /* SMB2 rename information. */ - status = smb2_file_rename_information(conn, req, - pdata, total_data, - fsp, smb_fname); - break; - } - - case SMB_FILE_LINK_INFORMATION: - { - status = smb_file_link_information(conn, req, - pdata, total_data, - fsp, smb_fname); - break; - } - - default: - return NT_STATUS_INVALID_LEVEL; - } - - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - *ret_data_size = data_return_size; - return NT_STATUS_OK; -} - -/**************************************************************************** - Reply to a TRANS2_SETFILEINFO (set file info by fileid or pathname). -****************************************************************************/ - -static void call_trans2setfilepathinfo(connection_struct *conn, - struct smb_request *req, - 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; - uint16_t info_level; - struct smb_filename *smb_fname = NULL; - files_struct *fsp = NULL; - NTSTATUS status = NT_STATUS_OK; - int data_return_size = 0; - int ret; - - if (!params) { - reply_nterror(req, NT_STATUS_INVALID_PARAMETER); - return; - } - - if (tran_call == TRANSACT2_SETFILEINFO) { - 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; - - 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)) { - uint32_t new_private_options = - fh_get_private_options(fsp->fh); - new_private_options |= - NTCREATEX_FLAG_DELETE_ON_CLOSE; - fh_set_private_options(fsp->fh, - new_private_options); - - DEBUG(3,("call_trans2setfilepathinfo: " - "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)) { - DEBUG(3,("call_trans2setfilepathinfo: fstat " - "of %s failed (%s)\n", fsp_fnum_dbg(fsp), - nt_errstr(status))); - reply_nterror(req, status); - return; - } + /* Cannot get here. */ + smb_panic("smb_posix_open: logic error"); + return NT_STATUS_INVALID_PARAMETER; } - } else { - char *fname = NULL; - uint32_t ucf_flags = ucf_flags_from_smb_request(req); - bool require_existing_object = true; + } - /* set path info */ - if (total_params < 7) { - reply_nterror(req, NT_STATUS_INVALID_PARAMETER); - return; - } + raw_unixmode = IVAL(pdata,8); + /* Next 4 bytes are not yet defined. */ - info_level = SVAL(params,0); + status = unix_perms_from_wire(conn, &smb_fname->st, raw_unixmode, + (VALID_STAT(smb_fname->st) ? + PERM_EXISTING_FILE : PERM_NEW_FILE), + &unixmode); - 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 (!NT_STATUS_IS_OK(status)) { + return status; + } - if (req->posix_pathnames) { - srvstr_get_path_posix(req, - params, - req->flags2, - &fname, - ¶ms[6], - total_params - 6, - STR_TERMINATE, - &status); - } else { - srvstr_get_path(req, - params, - req->flags2, - &fname, - ¶ms[6], - total_params - 6, - STR_TERMINATE, - &status); - } - if (!NT_STATUS_IS_OK(status)) { - reply_nterror(req, status); - return; - } + 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; + } - status = filename_convert(req, conn, - fname, - ucf_flags, - 0, - &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; + 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) { + 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; + } - /* - * 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; + 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 )); - /* - * There are 4 info levels which can - * create a new object in the filesystem. - * They are: - * SMB_SET_FILE_UNIX_LINK -> creates POSIX symlink. - * SMB_POSIX_PATH_OPEN -> creates POSIX file or directory. - * SMB_SET_FILE_UNIX_BASIC: - * SMB_SET_FILE_UNIX_INFO2: can create a POSIX special file. - * - * These info levels do not require an existing object. - */ - switch (info_level) { - case SMB_SET_FILE_UNIX_LINK: - case SMB_POSIX_PATH_OPEN: - case SMB_SET_FILE_UNIX_BASIC: - case SMB_SET_FILE_UNIX_INFO2: - require_existing_object = false; - break; - default: - break; - } + status = SMB_VFS_CREATE_FILE( + conn, /* conn */ + req, /* req */ + 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 */ - if (!VALID_STAT(smb_fname->st) && require_existing_object) { - reply_nterror(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); - } + TALLOC_FREE(posx); + + if (!NT_STATUS_IS_OK(status)) { + return status; } - DEBUG(3,("call_trans2setfilepathinfo(%d) %s (%s) info_level=%d " - "totdata=%d\n", tran_call, smb_fname_str_dbg(smb_fname), - fsp_fnum_dbg(fsp), - info_level,total_data)); + if (oplock_request && lp_fake_oplocks(SNUM(conn))) { + extended_oplock_granted = True; + } - /* Realloc the parameter size */ - *pparams = (char *)SMB_REALLOC(*pparams,2); - if (*pparams == NULL) { - reply_nterror(req, NT_STATUS_NO_MEMORY); - return; + if(oplock_request && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { + extended_oplock_granted = True; } - params = *pparams; - SSVAL(params,0,0); + info_level_return = SVAL(pdata,16); + + /* Allocate the correct return size. */ - status = smbd_do_setfilepathinfo(conn, req, req, - info_level, - fsp, - smb_fname, - ppdata, total_data, - &data_return_size); - 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; - } - } - 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 (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; + } - /* - * Invalid EA name needs to return 2 param bytes, - * not a zero-length error packet. - */ - if (NT_STATUS_EQUAL(status, STATUS_INVALID_EA_NAME)) { - send_trans2_replies(conn, req, status, params, 2, NULL, 0, - max_data_bytes); + /* 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 { - reply_nterror(req, status); + SSVAL(pdata,0, EXCLUSIVE_OPLOCK_RETURN); } - return; + } else if (fsp->oplock_type == LEVEL_II_OPLOCK) { + SSVAL(pdata,0, LEVEL_II_OPLOCK_RETURN); + } else { + SSVAL(pdata,0,NO_OPLOCK_RETURN); } - send_trans2_replies(conn, req, NT_STATUS_OK, params, 2, *ppdata, data_return_size, - max_data_bytes); + SSVAL(pdata,2,fsp->fnum); + SIVAL(pdata,4,info); /* Was file created etc. */ - return; + 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; } /**************************************************************************** - Reply to a TRANS2_MKDIR (make directory with extended attributes). + Delete a file with POSIX semantics. ****************************************************************************/ -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) +static NTSTATUS smb_posix_unlink(connection_struct *conn, + struct smb_request *req, + const char *pdata, + int total_data, + struct smb_filename *smb_fname) { - 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); - TALLOC_CTX *ctx = talloc_tos(); + files_struct *fsp = NULL; + uint16_t flags = 0; + char del = 1; + int info = 0; + int create_options = 0; + struct share_mode_lock *lck = NULL; + bool other_nonposix_opens; + struct smb2_create_blobs *posx = NULL; - if (!CAN_WRITE(conn)) { - reply_nterror(req, NT_STATUS_ACCESS_DENIED); - return; + if (total_data < 2) { + return NT_STATUS_INVALID_PARAMETER; } - if (total_params < 5) { - reply_nterror(req, NT_STATUS_INVALID_PARAMETER); - return; - } + flags = SVAL(pdata,0); - if (req->posix_pathnames) { - srvstr_get_path_posix(ctx, - params, - req->flags2, - &directory, - ¶ms[4], - total_params - 4, - STR_TERMINATE, - &status); - } else { - srvstr_get_path(ctx, - params, - req->flags2, - &directory, - ¶ms[4], - total_params - 4, - STR_TERMINATE, - &status); + if (!VALID_STAT(smb_fname->st)) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; } - if (!NT_STATUS_IS_OK(status)) { - reply_nterror(req, status); - return; + + if ((flags == SMB_POSIX_UNLINK_DIRECTORY_TARGET) && + !VALID_STAT_OF_DIR(smb_fname->st)) { + return NT_STATUS_NOT_A_DIRECTORY; } - DEBUG(3,("call_trans2mkdir : name = %s\n", directory)); + DEBUG(10,("smb_posix_unlink: %s %s\n", + (flags == SMB_POSIX_UNLINK_DIRECTORY_TARGET) ? "directory" : "file", + smb_fname_str_dbg(smb_fname))); - status = filename_convert(ctx, - conn, - directory, - ucf_flags, - 0, - &smb_dname); + if (VALID_STAT_OF_DIR(smb_fname->st)) { + create_options |= FILE_DIRECTORY_FILE; + } + status = make_smb2_posix_create_ctx(talloc_tos(), &posx, 0777); 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; - } + DBG_WARNING("make_smb2_posix_create_ctx failed: %s\n", + nt_errstr(status)); + return status; } - /* 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( + status = SMB_VFS_CREATE_FILE( conn, /* conn */ req, /* req */ - 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 */ + 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 */ @@ -9247,239 +6557,394 @@ static void call_trans2mkdir(connection_struct *conn, struct smb_request *req, NULL, /* sd */ NULL, /* ea_list */ &fsp, /* result */ - NULL, /* pinfo */ - NULL, NULL); /* create context */ + &info, /* pinfo */ + posx, /* in_context_blobs */ + NULL); /* out_context_blobs */ + + TALLOC_FREE(posx); + if (!NT_STATUS_IS_OK(status)) { - reply_nterror(req, status); - goto out; + return status; } - /* 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; - } + /* + * Don't lie to client. If we can't really delete due to + * non-POSIX opens return SHARING_VIOLATION. + */ + + lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id); + if (lck == NULL) { + DEBUG(0, ("smb_posix_unlink: Could not get share mode " + "lock for file %s\n", fsp_str_dbg(fsp))); + close_file_free(req, &fsp, NORMAL_CLOSE); + return NT_STATUS_INVALID_PARAMETER; } - /* Realloc the parameter and data sizes */ - *pparams = (char *)SMB_REALLOC(*pparams,2); - if(*pparams == NULL) { - reply_nterror(req, NT_STATUS_NO_MEMORY); - goto out; + other_nonposix_opens = has_other_nonposix_opens(lck, fsp); + if (other_nonposix_opens) { + /* Fail with sharing violation. */ + TALLOC_FREE(lck); + close_file_free(req, &fsp, NORMAL_CLOSE); + return NT_STATUS_SHARING_VIOLATION; } - params = *pparams; - SSVAL(params,0,0); + /* + * Set the delete on close. + */ + status = smb_set_file_disposition_info(conn, + &del, + 1, + fsp, + smb_fname); - send_trans2_replies(conn, req, NT_STATUS_OK, params, 2, *ppdata, 0, max_data_bytes); + TALLOC_FREE(lck); - out: - if (fsp != NULL) { - close_file_free(NULL, &fsp, NORMAL_CLOSE); + if (!NT_STATUS_IS_OK(status)) { + close_file_free(req, &fsp, NORMAL_CLOSE); + return status; } - TALLOC_FREE(smb_dname); - return; + return close_file_free(req, &fsp, NORMAL_CLOSE); } -/**************************************************************************** - 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) +static NTSTATUS smbd_do_posix_setfilepathinfo(struct connection_struct *conn, + struct smb_request *req, + TALLOC_CTX *mem_ctx, + uint16_t info_level, + struct smb_filename *smb_fname, + files_struct *fsp, + char **ppdata, + int total_data, + int *ret_data_size) { - char *params = *pparams; - uint16_t info_level; + char *pdata = *ppdata; + NTSTATUS status = NT_STATUS_OK; + int data_return_size = 0; - if (total_params < 6) { - reply_nterror(req, NT_STATUS_INVALID_PARAMETER); - return; + *ret_data_size = 0; + + if (!CAN_WRITE(conn)) { + /* Allow POSIX opens. The open path will deny + * any non-readonly opens. */ + if (info_level != SMB_POSIX_PATH_OPEN) { + return NT_STATUS_DOS(ERRSRV, ERRaccess); + } } - info_level = SVAL(params,4); - DEBUG(3,("call_trans2findnotifyfirst - info_level %d\n", info_level)); + DBG_DEBUG("file=%s (%s) info_level=%d totdata=%d\n", + smb_fname_str_dbg(smb_fname), + fsp_fnum_dbg(fsp), + info_level, + total_data); switch (info_level) { - case 1: - case 2: + case SMB_SET_FILE_UNIX_BASIC: + { + status = smb_set_file_unix_basic(conn, req, + pdata, + total_data, + fsp, + smb_fname); 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; + case SMB_SET_FILE_UNIX_INFO2: + { + status = smb_set_file_unix_info2(conn, req, + pdata, + total_data, + fsp, + smb_fname); + break; + } - SSVAL(params,0,fnf_handle); - SSVAL(params,2,0); /* No changes */ - SSVAL(params,4,0); /* No EA errors */ + case SMB_SET_FILE_UNIX_LINK: + { + if (smb_fname == NULL) { + /* We must have a pathname for this. */ + return NT_STATUS_INVALID_LEVEL; + } + status = smb_set_file_unix_link(conn, req, pdata, + total_data, smb_fname); + break; + } - fnf_handle++; + case SMB_SET_FILE_UNIX_HLINK: + { + if (smb_fname == NULL) { + /* We must have a pathname for this. */ + return NT_STATUS_INVALID_LEVEL; + } + status = smb_set_file_unix_hlink(conn, req, + pdata, total_data, + smb_fname); + break; + } - if(fnf_handle == 0) - fnf_handle = 257; +#if defined(HAVE_POSIX_ACLS) + case SMB_SET_POSIX_ACL: + { + status = smb_set_posix_acl(conn, + req, + pdata, + total_data, + fsp, + smb_fname); + break; + } +#endif - send_trans2_replies(conn, req, NT_STATUS_OK, params, 6, *ppdata, 0, max_data_bytes); + case SMB_SET_POSIX_LOCK: + { + if (fsp == NULL) { + return NT_STATUS_INVALID_LEVEL; + } + status = smb_set_posix_lock(conn, req, + pdata, total_data, fsp); + break; + } - return; -} + case SMB_POSIX_PATH_OPEN: + { + if (smb_fname == NULL) { + /* We must have a pathname for this. */ + return NT_STATUS_INVALID_LEVEL; + } -/**************************************************************************** - Reply to a TRANS2_FINDNOTIFYNEXT (continue monitoring a directory for - changes). Currently this does nothing. -****************************************************************************/ + status = smb_posix_open(conn, req, + ppdata, + total_data, + smb_fname, + &data_return_size); + break; + } -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; + case SMB_POSIX_PATH_UNLINK: + { + if (smb_fname == NULL) { + /* We must have a pathname for this. */ + return NT_STATUS_INVALID_LEVEL; + } - DEBUG(3,("call_trans2findnotifynext\n")); + status = smb_posix_unlink(conn, req, + pdata, + total_data, + smb_fname); + break; + } - /* Realloc the parameter and data sizes */ - *pparams = (char *)SMB_REALLOC(*pparams,4); - if (*pparams == NULL) { - reply_nterror(req, NT_STATUS_NO_MEMORY); - return; + default: + return NT_STATUS_INVALID_LEVEL; } - 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); + if (!NT_STATUS_IS_OK(status)) { + return status; + } - return; + *ret_data_size = data_return_size; + return NT_STATUS_OK; } -/**************************************************************************** - Reply to a TRANS2_GET_DFS_REFERRAL - Shirish Kalele . -****************************************************************************/ - -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) +NTSTATUS smbd_do_setfilepathinfo(connection_struct *conn, + struct smb_request *req, + TALLOC_CTX *mem_ctx, + uint16_t info_level, + files_struct *fsp, + struct smb_filename *smb_fname, + char **ppdata, int total_data, + int *ret_data_size) { - char *params = *pparams; - char *pathname = NULL; - int reply_size = 0; - int max_referral_level; + char *pdata = *ppdata; NTSTATUS status = NT_STATUS_OK; - TALLOC_CTX *ctx = talloc_tos(); - - DEBUG(10,("call_trans2getdfsreferral\n")); + int data_return_size = 0; - if (total_params < 3) { - reply_nterror(req, NT_STATUS_INVALID_PARAMETER); - return; + if (INFO_LEVEL_IS_UNIX(info_level)) { + if (!lp_smb1_unix_extensions()) { + return NT_STATUS_INVALID_LEVEL; + } + if (!req->posix_pathnames) { + return NT_STATUS_INVALID_LEVEL; + } + status = smbd_do_posix_setfilepathinfo(conn, + req, + req, + info_level, + smb_fname, + fsp, + ppdata, + total_data, + &data_return_size); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + *ret_data_size = data_return_size; + return NT_STATUS_OK; } - max_referral_level = SVAL(params,0); + *ret_data_size = 0; - if(!lp_host_msdfs()) { - reply_nterror(req, NT_STATUS_NOT_IMPLEMENTED); - return; - } + DEBUG(3,("smbd_do_setfilepathinfo: %s (%s) info_level=%d " + "totdata=%d\n", smb_fname_str_dbg(smb_fname), + fsp_fnum_dbg(fsp), + info_level, total_data)); - srvstr_pull_talloc(ctx, params, req->flags2, &pathname, ¶ms[2], - total_params - 2, STR_TERMINATE); - if (!pathname) { - reply_nterror(req, NT_STATUS_NOT_FOUND); - return; - } - if((reply_size = setup_dfs_referral(conn, pathname, max_referral_level, - ppdata,&status)) < 0) { - reply_nterror(req, status); - return; - } + switch (info_level) { + + case SMB_INFO_STANDARD: + { + status = smb_set_info_standard(conn, + pdata, + total_data, + fsp, + smb_fname); + break; + } + + case SMB_INFO_SET_EA: + { + status = smb_info_set_ea(conn, + pdata, + total_data, + fsp, + smb_fname); + break; + } + + case SMB_SET_FILE_BASIC_INFO: + case SMB_FILE_BASIC_INFORMATION: + { + status = smb_set_file_basic_info(conn, + pdata, + total_data, + fsp, + smb_fname); + break; + } - 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); + case SMB_FILE_ALLOCATION_INFORMATION: + case SMB_SET_FILE_ALLOCATION_INFO: + { + status = smb_set_file_allocation_info(conn, req, + pdata, + total_data, + fsp, + smb_fname); + break; + } - return; -} + case SMB_FILE_END_OF_FILE_INFORMATION: + case SMB_SET_FILE_END_OF_FILE_INFO: + { + /* + * XP/Win7 both fail after the createfile with + * SMB_SET_FILE_END_OF_FILE_INFO but not + * SMB_FILE_END_OF_FILE_INFORMATION (pass-through). + * The level is known here, so pass it down + * appropriately. + */ + bool should_fail = + (info_level == SMB_SET_FILE_END_OF_FILE_INFO); -#define LMCAT_SPL 0x53 -#define LMFUNC_GETJOBID 0x60 + status = smb_set_file_end_of_file_info(conn, req, + pdata, + total_data, + fsp, + smb_fname, + should_fail); + break; + } -/**************************************************************************** - Reply to a TRANS2_IOCTL - used for OS/2 printing. -****************************************************************************/ + case SMB_FILE_DISPOSITION_INFORMATION: + case SMB_SET_FILE_DISPOSITION_INFO: /* Set delete on close for open file. */ + { +#if 0 + /* JRA - We used to just ignore this on a path ? + * Shouldn't this be invalid level on a pathname + * based call ? + */ + if (tran_call != TRANSACT2_SETFILEINFO) { + return ERROR_NT(NT_STATUS_INVALID_LEVEL); + } +#endif + status = smb_set_file_disposition_info(conn, + pdata, + total_data, + fsp, + smb_fname); + break; + } -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; + case SMB_FILE_POSITION_INFORMATION: + { + status = smb_file_position_information(conn, + pdata, + total_data, + fsp); + break; + } - /* check for an invalid fid before proceeding */ + case SMB_FILE_FULL_EA_INFORMATION: + { + status = smb_set_file_full_ea_info(conn, + pdata, + total_data, + fsp); + break; + } - if (!fsp) { - reply_nterror(req, NT_STATUS_INVALID_HANDLE); - return; - } + /* From tridge Samba4 : + * MODE_INFORMATION in setfileinfo (I have no + * idea what "mode information" on a file is - it takes a value of 0, + * 2, 4 or 6. What could it be?). + */ - 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; + case SMB_FILE_MODE_INFORMATION: + { + status = smb_file_mode_information(conn, + pdata, + total_data); + break; } - pdata = *ppdata; - /* NOTE - THIS IS ASCII ONLY AT THE MOMENT - NOT SURE IF OS/2 - CAN ACCEPT THIS IN UNICODE. JRA. */ + /* [MS-SMB2] 3.3.5.21.1 states we MUST fail with STATUS_NOT_SUPPORTED. */ + case SMB_FILE_VALID_DATA_LENGTH_INFORMATION: + case SMB_FILE_SHORT_NAME_INFORMATION: + return NT_STATUS_NOT_SUPPORTED; - /* Job number */ - SSVAL(pdata, 0, print_spool_rap_jobid(fsp->print_file)); + case SMB_FILE_RENAME_INFORMATION: + { + status = smb_file_rename_information(conn, req, + pdata, total_data, + fsp, smb_fname); + break; + } - 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; + case SMB2_FILE_RENAME_INFORMATION_INTERNAL: + { + /* SMB2 rename information. */ + status = smb2_file_rename_information(conn, req, + pdata, total_data, + fsp, smb_fname); + break; } - 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; + + case SMB_FILE_LINK_INFORMATION: + { + status = smb_file_link_information(conn, req, + pdata, total_data, + fsp, smb_fname); + break; } - send_trans2_replies(conn, req, NT_STATUS_OK, *pparams, 0, *ppdata, 32, - max_data_bytes); - return; + + default: + return NT_STATUS_INVALID_LEVEL; + } + + if (!NT_STATUS_IS_OK(status)) { + return status; } - DEBUG(2,("Unknown TRANS2_IOCTL\n")); - reply_nterror(req, NT_STATUS_NOT_IMPLEMENTED); + *ret_data_size = data_return_size; + return NT_STATUS_OK; } /**************************************************************************** @@ -9558,358 +7023,6 @@ void reply_findnclose(struct smb_request *req) return; } -static void handle_trans2(connection_struct *conn, struct smb_request *req, - struct trans_state *state) -{ - if (get_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; - } - } - - SMB_PERFCOUNT_SET_SUBOP(&req->pcd, state->call); - - /* 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: - case TRANSACT2_QFILEINFO: - { - START_PROFILE(Trans2_qpathinfo); - call_trans2qfilepathinfo(conn, req, state->call, - &state->param, state->total_param, - &state->data, state->total_data, - state->max_data_return); - END_PROFILE(Trans2_qpathinfo); - break; - } - - case TRANSACT2_SETPATHINFO: - case TRANSACT2_SETFILEINFO: - { - START_PROFILE(Trans2_setpathinfo); - call_trans2setfilepathinfo(conn, req, state->call, - &state->param, state->total_param, - &state->data, state->total_data, - state->max_data_return); - END_PROFILE(Trans2_setpathinfo); - 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_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 ****************************************************************************/