#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
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<maxentries) && !finished && !out_of_space;i++) {
+ bool got_exact_match = False;
+
+ /* this is a heuristic to avoid seeking the dirptr 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(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 <kalele@veritas.com>.
+****************************************************************************/
+
+static void call_trans2getdfsreferral(connection_struct *conn,
+ struct smb_request *req,
+ char **pparams, int total_params,
+ char **ppdata, int total_data,
+ unsigned int max_data_bytes)
+{
+ char *params = *pparams;
+ char *pathname = NULL;
+ int reply_size = 0;
+ int max_referral_level;
+ NTSTATUS status = NT_STATUS_OK;
+ TALLOC_CTX *ctx = talloc_tos();
+
+ DEBUG(10,("call_trans2getdfsreferral\n"));
+
+ if (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);
+}
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;
}
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;
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
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<maxentries) && !finished && !out_of_space;i++) {
- bool got_exact_match = False;
+ switch (info_level) {
+ case SMB_INFO_ALLOCATION:
+ {
+ uint64_t dfree,dsize,bsize,block_size,sectors_per_unit;
+ data_len = 18;
+ df_ret = get_dfree_info(conn, &smb_fname, &bsize,
+ &dfree, &dsize);
+ if (df_ret == (uint64_t)-1) {
+ return map_nt_error_from_unix(errno);
+ }
- /* this is a heuristic to avoid seeking the dirptr 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(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;
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;
}
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();
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.
}
#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,
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.
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 */
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 <kalele@veritas.com>.
-****************************************************************************/
-
-static void call_trans2getdfsreferral(connection_struct *conn,
- struct smb_request *req,
- char **pparams, int total_params,
- char **ppdata, int total_data,
- unsigned int max_data_bytes)
+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;
}
/****************************************************************************
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
****************************************************************************/