/* * smb_com_trans2_query_file_information */ smb_sdrc_t smb_com_trans2_query_file_information(struct smb_request *sr, struct smb_xa *xa) { uint16_t infolev; if (smb_mbc_decodef(&xa->req_param_mb, "ww", &sr->smb_fid, &infolev) != 0) return (SDRC_ERROR); if (smb_query_by_fid(sr, xa, infolev) != 0) return (SDRC_ERROR); return (SDRC_SUCCESS); }
smb_sdrc_t smb_com_echo(struct smb_request *sr) { unsigned short necho; unsigned short nbytes; unsigned short i; struct mbuf_chain reply; char *data; if (smbsr_decode_vwv(sr, "w", &necho) != 0) return (SDRC_ERROR); nbytes = sr->smb_bcc; data = smb_srm_zalloc(sr, nbytes); if (smb_mbc_decodef(&sr->smb_data, "#c", nbytes, data)) return (SDRC_ERROR); for (i = 1; i <= necho; ++i) { MBC_INIT(&reply, SMB_HEADER_ED_LEN + 10 + nbytes); (void) smb_mbc_encodef(&reply, SMB_HEADER_ED_FMT, sr->first_smb_com, sr->smb_rcls, sr->smb_reh, sr->smb_err, sr->smb_flg | SMB_FLAGS_REPLY, sr->smb_flg2, sr->smb_pid_high, sr->smb_sig, sr->smb_tid, sr->smb_pid, sr->smb_uid, sr->smb_mid); (void) smb_mbc_encodef(&reply, "bww#c", 1, i, nbytes, nbytes, data); if (sr->session->signing.flags & SMB_SIGNING_ENABLED) smb_sign_reply(sr, &reply); (void) smb_session_send(sr->session, 0, &reply); } return (SDRC_NO_REPLY); }
int smb2_decode_header(smb_request_t *sr) { uint64_t ssnid; uint32_t pid, tid; uint16_t hdr_len; int rc; rc = smb_mbc_decodef( &sr->command, "Nwww..wwllqllq16c", &hdr_len, /* w */ &sr->smb2_credit_charge, /* w */ &sr->smb2_chan_seq, /* w */ /* reserved .. */ &sr->smb2_cmd_code, /* w */ &sr->smb2_credit_request, /* w */ &sr->smb2_hdr_flags, /* l */ &sr->smb2_next_command, /* l */ &sr->smb2_messageid, /* q */ &pid, /* l */ &tid, /* l */ &ssnid, /* q */ sr->smb2_sig); /* 16c */ if (rc) return (rc); if (hdr_len != SMB2_HDR_SIZE) return (-1); sr->smb_uid = (uint16_t)ssnid; /* XXX wide UIDs */ if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) { sr->smb2_async_id = pid | ((uint64_t)sr->smb_tid) << 32; } else { sr->smb_pid = pid; sr->smb_tid = (uint16_t)tid; /* XXX wide TIDs */ } return (rc); }
/* * smb_com_trans2_query_path_information */ smb_sdrc_t smb_com_trans2_query_path_information(smb_request_t *sr, smb_xa_t *xa) { uint16_t infolev; char *path; if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { smbsr_error(sr, NT_STATUS_INVALID_DEVICE_REQUEST, ERRDOS, ERROR_INVALID_FUNCTION); return (SDRC_ERROR); } if (smb_mbc_decodef(&xa->req_param_mb, "%w4.u", sr, &infolev, &path) != 0) return (SDRC_ERROR); if (smb_query_by_path(sr, xa, infolev, path) != 0) return (SDRC_ERROR); return (SDRC_SUCCESS); }
/* * smb_com_trans2_query_path_information */ smb_sdrc_t smb_com_trans2_query_path_information(smb_request_t *sr, smb_xa_t *xa) { uint16_t infolev; smb_fqi_t *fqi = &sr->arg.dirop.fqi; if (STYPE_ISIPC(sr->tid_tree->t_res_type)) { smbsr_error(sr, NT_STATUS_INVALID_DEVICE_REQUEST, ERRDOS, ERROR_INVALID_FUNCTION); return (SDRC_ERROR); } if (smb_mbc_decodef(&xa->req_param_mb, "%w4.u", sr, &infolev, &fqi->fq_path.pn_path) != 0) return (SDRC_ERROR); if (smb_query_by_path(sr, xa, infolev) != 0) return (SDRC_ERROR); return (SDRC_SUCCESS); }
/* * smb_nt_transact_notify_change * * Handle and SMB NT transact NOTIFY CHANGE request. * Basically, wait until "something has changed", and either * return information about what changed, or return a special * error telling the client "many things changed". * * The implementation uses a per-node list of waiting notify * requests like this one, each with a blocked worker thead. * Later, FEM and/or smbsrv events wake these threads, which * then send the reply to the client. */ smb_sdrc_t smb_nt_transact_notify_change(smb_request_t *sr, struct smb_xa *xa) { uint32_t CompletionFilter; unsigned char WatchTree; uint32_t status; hrtime_t t1, t2; if (smb_mbc_decodef(&xa->req_setup_mb, "lwb", &CompletionFilter, &sr->smb_fid, &WatchTree) != 0) { smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, 0, 0); return (SDRC_ERROR); } CompletionFilter &= FILE_NOTIFY_VALID_MASK; if (WatchTree) CompletionFilter |= NODE_FLAGS_WATCH_TREE; smbsr_lookup_file(sr); t1 = gethrtime(); status = smb_notify_common(sr, &xa->rep_data_mb, CompletionFilter); t2 = gethrtime(); /* * We don't want to include the (indefinite) wait time of the * smb_notify_common() call in the SMB1 transact latency. * The easiest way to do that, without adding special case * logic to the common SMB1 dispatch handler is to adjust the * start time of this request to effectively subtract out the * time we were blocked in smb_notify_common(). */ sr->sr_time_start += (t2 - t1); if (status != 0) smbsr_error(sr, status, 0, 0); return (SDRC_SUCCESS); }
/* * smb_com_trans2_find_first2 * * Client Request Value * ============================ ================================== * * UCHAR WordCount 15 * UCHAR TotalDataCount Total size of extended attribute list * UCHAR SetupCount 1 * UCHAR Setup[0] TRANS2_FIND_FIRST2 * * Parameter Block Encoding Description * ============================ ================================== * USHORT SearchAttributes; * USHORT SearchCount; Maximum number of entries to return * USHORT Flags; Additional information: * Bit 0 - close search after this request * Bit 1 - close search if end of search * reached * Bit 2 - return resume keys for each * entry found * Bit 3 - continue search from previous * ending place * Bit 4 - find with backup intent * USHORT InformationLevel; See below * ULONG SearchStorageType; * STRING FileName; Pattern for the search * UCHAR Data[ TotalDataCount ] FEAList if InformationLevel is * QUERY_EAS_FROM_LIST * * Response Parameter Block Description * ============================ ================================== * * USHORT Sid; Search handle * USHORT SearchCount; Number of entries returned * USHORT EndOfSearch; Was last entry returned? * USHORT EaErrorOffset; Offset into EA list if EA error * USHORT LastNameOffset; Offset into data to file name of last * entry, if server needs it to resume * search; else 0 * UCHAR Data[ TotalDataCount ] Level dependent info about the matches * found in the search */ smb_sdrc_t smb_com_trans2_find_first2(smb_request_t *sr, smb_xa_t *xa) { int count; uint16_t sattr, odid; smb_pathname_t *pn; smb_odir_t *od; smb_find_args_t args; uint32_t odir_flags = 0; bzero(&args, sizeof (smb_find_args_t)); if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERROR_ACCESS_DENIED); return (SDRC_ERROR); } pn = &sr->arg.dirop.fqi.fq_path; if (smb_mbc_decodef(&xa->req_param_mb, "%wwww4.u", sr, &sattr, &args.fa_maxcount, &args.fa_fflag, &args.fa_infolev, &pn->pn_path) != 0) { return (SDRC_ERROR); } smb_pathname_init(sr, pn, pn->pn_path); if (!smb_pathname_validate(sr, pn)) return (-1); if (smb_is_stream_name(pn->pn_path)) { smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID, ERRDOS, ERROR_INVALID_NAME); return (SDRC_ERROR); } if (args.fa_fflag & SMB_FIND_WITH_BACKUP_INTENT) { sr->user_cr = smb_user_getprivcred(sr->uid_user); odir_flags = SMB_ODIR_OPENF_BACKUP_INTENT; } args.fa_maxdata = smb_trans2_find_get_maxdata(sr, args.fa_infolev, args.fa_fflag); if (args.fa_maxdata == 0) return (SDRC_ERROR); odid = smb_odir_open(sr, pn->pn_path, sattr, odir_flags); if (odid == 0) { if (sr->smb_error.status == NT_STATUS_OBJECT_PATH_NOT_FOUND) { smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND, ERRDOS, ERROR_FILE_NOT_FOUND); } return (SDRC_ERROR); } od = smb_tree_lookup_odir(sr->tid_tree, odid); if (od == NULL) return (SDRC_ERROR); count = smb_trans2_find_entries(sr, xa, od, &args); if (count == -1) { smb_odir_close(od); smb_odir_release(od); return (SDRC_ERROR); } if (count == 0) { smb_odir_close(od); smb_odir_release(od); smbsr_errno(sr, ENOENT); return (SDRC_ERROR); } if ((args.fa_fflag & SMB_FIND_CLOSE_AFTER_REQUEST) || (args.fa_eos && (args.fa_fflag & SMB_FIND_CLOSE_AT_EOS))) { smb_odir_close(od); } /* else leave odir open for trans2_find_next2 */ smb_odir_release(od); (void) smb_mbc_encodef(&xa->rep_param_mb, "wwwww", odid, /* Search ID */ count, /* Search Count */ args.fa_eos, /* End Of Search */ 0, /* EA Error Offset */ args.fa_lno); /* Last Name Offset */ return (SDRC_SUCCESS); }
smb_sdrc_t smb_com_trans2_open2(smb_request_t *sr, smb_xa_t *xa) { struct open_param *op = &sr->arg.open; uint32_t creation_time; uint32_t alloc_size; uint16_t flags; uint16_t file_attr; int rc; bzero(op, sizeof (sr->arg.open)); rc = smb_mbc_decodef(&xa->req_param_mb, "%wwwwlwl10.u", sr, &flags, &op->omode, &op->fqi.fq_sattr, &file_attr, &creation_time, &op->ofun, &alloc_size, &op->fqi.fq_path.pn_path); if (rc != 0) return (SDRC_ERROR); if ((creation_time != 0) && (creation_time != UINT_MAX)) op->crtime.tv_sec = smb_time_local_to_gmt(sr, creation_time); op->crtime.tv_nsec = 0; op->dattr = file_attr; op->dsize = alloc_size; op->create_options = FILE_NON_DIRECTORY_FILE; op->desired_access = smb_omode_to_amask(op->omode); op->share_access = smb_denymode_to_sharemode(op->omode, op->fqi.fq_path.pn_path); op->create_disposition = smb_ofun_to_crdisposition(op->ofun); if (op->create_disposition > FILE_MAXIMUM_DISPOSITION) op->create_disposition = FILE_CREATE; if (op->omode & SMB_DA_WRITE_THROUGH) op->create_options |= FILE_WRITE_THROUGH; if (sr->smb_flg & SMB_FLAGS_OPLOCK) { if (sr->smb_flg & SMB_FLAGS_OPLOCK_NOTIFY_ANY) op->op_oplock_level = SMB_OPLOCK_BATCH; else op->op_oplock_level = SMB_OPLOCK_EXCLUSIVE; } else { op->op_oplock_level = SMB_OPLOCK_NONE; } if (smb_common_open(sr) != NT_STATUS_SUCCESS) return (SDRC_ERROR); if (op->op_oplock_level != SMB_OPLOCK_NONE) op->action_taken |= SMB_OACT_LOCK; else op->action_taken &= ~SMB_OACT_LOCK; file_attr = op->dattr & FILE_ATTRIBUTE_MASK; if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) op->dsize = 0; (void) smb_mbc_encodef(&xa->rep_param_mb, "wwllwwwwlwl", sr->smb_fid, file_attr, (uint32_t)0, /* creation time */ (uint32_t)op->dsize, op->omode, op->ftype, op->devstate, op->action_taken, op->fileid, (uint16_t)0, /* EA error offset */ (uint32_t)0); /* EA list length */ return (SDRC_SUCCESS); }
smb_sdrc_t smb2_session_setup(smb_request_t *sr) { smb_arg_sessionsetup_t *sinfo; uint16_t StructureSize; uint8_t Flags; uint8_t SecurityMode; uint32_t Capabilities; /* ignored - see above */ uint32_t Channel; uint16_t SecBufOffset; uint16_t SecBufLength; uint64_t PrevSessionId; uint16_t SessionFlags; uint32_t status; int skip; int rc = 0; sinfo = smb_srm_zalloc(sr, sizeof (smb_arg_sessionsetup_t)); sr->sr_ssetup = sinfo; rc = smb_mbc_decodef( &sr->smb_data, "wbbllwwq", &StructureSize, /* w */ &Flags, /* b */ &SecurityMode, /* b */ &Capabilities, /* l */ &Channel, /* l */ &SecBufOffset, /* w */ &SecBufLength, /* w */ &PrevSessionId); /* q */ if (rc) return (SDRC_ERROR); /* * We're normally positioned at the security buffer now, * but there could be some padding before it. */ skip = (SecBufOffset + sr->smb2_cmd_hdr) - sr->smb_data.chain_offset; if (skip < 0) return (SDRC_ERROR); if (skip > 0) (void) smb_mbc_decodef(&sr->smb_data, "#.", skip); /* * Get the security buffer */ sinfo->ssi_iseclen = SecBufLength; sinfo->ssi_isecblob = smb_srm_zalloc(sr, sinfo->ssi_iseclen); rc = smb_mbc_decodef(&sr->smb_data, "#c", sinfo->ssi_iseclen, sinfo->ssi_isecblob); if (rc) return (SDRC_ERROR); /* * The real auth. work happens in here. */ status = smb_authenticate_ext(sr); SecBufOffset = SMB2_HDR_SIZE + 8; SecBufLength = sinfo->ssi_oseclen; SessionFlags = 0; switch (status) { case NT_STATUS_SUCCESS: /* Authenticated */ if (sr->uid_user->u_flags & SMB_USER_FLAG_GUEST) SessionFlags |= SMB2_SESSION_FLAG_IS_GUEST; if (sr->uid_user->u_flags & SMB_USER_FLAG_ANON) SessionFlags |= SMB2_SESSION_FLAG_IS_NULL; smb2_ss_adjust_credits(sr); break; /* * This is not really an error, but tells the client * it should send another session setup request. * Not smb2_put_error because we send a payload. */ case NT_STATUS_MORE_PROCESSING_REQUIRED: sr->smb2_status = status; break; default: SecBufLength = 0; sr->smb2_status = status; break; } /* * SMB2 Session Setup reply */ rc = smb_mbc_encodef( &sr->reply, "wwww#c", 9, /* StructSize */ /* w */ SessionFlags, /* w */ SecBufOffset, /* w */ SecBufLength, /* w */ SecBufLength, /* # */ sinfo->ssi_osecblob); /* c */ if (rc) return (SDRC_ERROR); return (SDRC_SUCCESS); }
/* * SMB Negotiate gets special handling. This is called directly by * the reader thread (see smbsr_newrq_initial) with what _should_ be * an SMB1 Negotiate. Only the "\ffSMB" header has been checked * when this is called, so this needs to check the SMB command, * if it's Negotiate execute it, then send the reply, etc. * * Since this is called directly from the reader thread, we * know this is the only thread currently using this session. * This has to duplicate some of what smb1sr_work does as a * result of bypassing the normal dispatch mechanism. * * The caller always frees this request. */ int smb1_newrq_negotiate(smb_request_t *sr) { smb_sdrc_t sdrc; uint16_t pid_hi, pid_lo; /* * Decode the header */ if (smb_mbc_decodef(&sr->command, SMB_HEADER_ED_FMT, &sr->smb_com, &sr->smb_rcls, &sr->smb_reh, &sr->smb_err, &sr->smb_flg, &sr->smb_flg2, &pid_hi, sr->smb_sig, &sr->smb_tid, &pid_lo, &sr->smb_uid, &sr->smb_mid) != 0) return (-1); if (sr->smb_com != SMB_COM_NEGOTIATE) return (-1); sr->smb_pid = (pid_hi << 16) | pid_lo; /* * Reserve space for the reply header. */ (void) smb_mbc_encodef(&sr->reply, "#.", SMB_HEADER_LEN); sr->first_smb_com = sr->smb_com; if (smb_mbc_decodef(&sr->command, "b", &sr->smb_wct) != 0) return (-1); (void) MBC_SHADOW_CHAIN(&sr->smb_vwv, &sr->command, sr->command.chain_offset, sr->smb_wct * 2); if (smb_mbc_decodef(&sr->command, "#.w", sr->smb_wct*2, &sr->smb_bcc)) return (-1); (void) MBC_SHADOW_CHAIN(&sr->smb_data, &sr->command, sr->command.chain_offset, sr->smb_bcc); sr->command.chain_offset += sr->smb_bcc; if (sr->command.chain_offset > sr->command.max_bytes) return (-1); /* Store pointers for later */ sr->cur_reply_offset = sr->reply.chain_offset; sdrc = smb_pre_negotiate(sr); if (sdrc == SDRC_SUCCESS) sdrc = smb_com_negotiate(sr); smb_post_negotiate(sr); if (sdrc != SDRC_NO_REPLY) smbsr_send_reply(sr); if (sdrc == SDRC_DROP_VC) return (-1); return (0); }
/* * See [MS-DFSC] for details about this command */ uint32_t smb_dfs_get_referrals(smb_request_t *sr, smb_fsctl_t *fsctl) { dfs_info_t *referrals; dfs_referral_response_t refrsp; dfs_reftype_t reftype; char *path; uint16_t maxver; uint32_t status; int rc; /* * The caller checks this, because the error reporting method * varies across SMB versions. */ ASSERT(STYPE_ISIPC(sr->tid_tree->t_res_type)); /* * XXX Instead of decoding the referral request and encoding * the response here (in-kernel) we could pass the given * request buffer in our door call, and let that return the * response buffer ready to stuff into out_mbc. That would * allow all this decoding/encoding to happen at user-level. * (and most of this file would go away. :-) */ switch (fsctl->CtlCode) { case FSCTL_DFS_GET_REFERRALS: /* * Input data is (w) MaxReferralLevel, (U) path */ rc = smb_mbc_decodef(fsctl->in_mbc, "%wu", sr, &maxver, &path); if (rc != 0) return (NT_STATUS_INVALID_PARAMETER); break; case FSCTL_DFS_GET_REFERRALS_EX: /* XXX - todo */ default: return (NT_STATUS_NOT_SUPPORTED); } reftype = smb_dfs_get_reftype((const char *)path); switch (reftype) { case DFS_REFERRAL_INVALID: /* Need to check the error for this case */ return (NT_STATUS_INVALID_PARAMETER); case DFS_REFERRAL_DOMAIN: case DFS_REFERRAL_DC: /* MS-DFSC: this error is returned by non-DC root */ return (NT_STATUS_INVALID_PARAMETER); case DFS_REFERRAL_SYSVOL: /* MS-DFSC: this error is returned by non-DC root */ return (NT_STATUS_NO_SUCH_DEVICE); default: break; } status = smb_dfs_referrals_get(sr, path, reftype, &refrsp); if (status != NT_STATUS_SUCCESS) return (status); referrals = &refrsp.rp_referrals; smb_dfs_encode_hdr(fsctl->out_mbc, referrals); /* * Server may respond with any referral version at or below * the maximum specified in the request. */ switch (maxver) { case DFS_REFERRAL_V1: status = smb_dfs_encode_refv1(sr, fsctl->out_mbc, referrals); break; case DFS_REFERRAL_V2: status = smb_dfs_encode_refv2(sr, fsctl->out_mbc, referrals); break; case DFS_REFERRAL_V3: status = smb_dfs_encode_refv3x(sr, fsctl->out_mbc, referrals, DFS_REFERRAL_V3); break; case DFS_REFERRAL_V4: default: status = smb_dfs_encode_refv3x(sr, fsctl->out_mbc, referrals, DFS_REFERRAL_V4); break; } smb_dfs_referrals_free(&refrsp); return (status); }
smb_sdrc_t smb2_read(smb_request_t *sr) { smb_ofile_t *of = NULL; smb_vdb_t *vdb = NULL; struct mbuf *m = NULL; uint16_t StructSize; uint8_t Padding; uint8_t DataOff; uint32_t Length; uint64_t Offset; smb2fid_t smb2fid; uint32_t MinCount; uint32_t Channel; uint32_t Remaining; uint16_t ChanInfoOffset; uint16_t ChanInfoLength; uint32_t XferCount; uint32_t status; int rc = 0; /* * SMB2 Read request */ rc = smb_mbc_decodef( &sr->smb_data, "wb.lqqqlllww", &StructSize, /* w */ &Padding, /* b. */ &Length, /* l */ &Offset, /* q */ &smb2fid.persistent, /* q */ &smb2fid.temporal, /* q */ &MinCount, /* l */ &Channel, /* l */ &Remaining, /* l */ &ChanInfoOffset, /* w */ &ChanInfoLength); /* w */ if (rc) return (SDRC_ERROR); if (StructSize != 49) return (SDRC_ERROR); status = smb2sr_lookup_fid(sr, &smb2fid); if (status) { smb2sr_put_error(sr, status); return (SDRC_SUCCESS); } of = sr->fid_ofile; if (Length > smb2_max_rwsize) { smb2sr_put_error(sr, NT_STATUS_INVALID_PARAMETER); return (SDRC_SUCCESS); } if (MinCount > Length) MinCount = Length; /* This is automatically free'd. */ vdb = smb_srm_zalloc(sr, sizeof (*vdb)); vdb->vdb_tag = 0; vdb->vdb_uio.uio_iov = &vdb->vdb_iovec[0]; vdb->vdb_uio.uio_iovcnt = MAX_IOVEC; vdb->vdb_uio.uio_resid = Length; vdb->vdb_uio.uio_loffset = (offset_t)Offset; vdb->vdb_uio.uio_segflg = UIO_SYSSPACE; vdb->vdb_uio.uio_extflg = UIO_COPY_DEFAULT; sr->raw_data.max_bytes = Length; m = smb_mbuf_allocate(&vdb->vdb_uio); switch (of->f_tree->t_res_type & STYPE_MASK) { case STYPE_DISKTREE: if (!smb_node_is_dir(of->f_node)) { /* Check for conflicting locks. */ rc = smb_lock_range_access(sr, of->f_node, Offset, Length, B_FALSE); if (rc) { rc = ERANGE; break; } } rc = smb_fsop_read(sr, of->f_cr, of->f_node, &vdb->vdb_uio); break; case STYPE_IPC: rc = smb_opipe_read(sr, &vdb->vdb_uio); break; default: case STYPE_PRINTQ: rc = EACCES; break; } /* How much data we moved. */ XferCount = Length - vdb->vdb_uio.uio_resid; sr->raw_data.max_bytes = XferCount; smb_mbuf_trim(m, XferCount); MBC_ATTACH_MBUF(&sr->raw_data, m); /* * Checking the error return _after_ dealing with * the returned data so that if m was allocated, * it will be free'd via sr->raw_data cleanup. */ if (rc) { smb2sr_put_errno(sr, rc); return (SDRC_SUCCESS); } /* * SMB2 Read reply */ DataOff = SMB2_HDR_SIZE + 16; rc = smb_mbc_encodef( &sr->reply, "wb.lllC", 17, /* StructSize */ /* w */ DataOff, /* b. */ XferCount, /* l */ 0, /* DataRemaining */ /* l */ 0, /* reserved */ /* l */ &sr->raw_data); /* C */ if (rc) return (SDRC_ERROR); mutex_enter(&of->f_mutex); of->f_seek_pos = Offset + XferCount; mutex_exit(&of->f_mutex); return (SDRC_SUCCESS); }
smb_sdrc_t smb_com_locking_andx(smb_request_t *sr) { unsigned short i; unsigned char lock_type; /* See lock_type table above */ unsigned char oplock_level; /* The new oplock level */ uint32_t timeout; /* Milliseconds to wait for lock */ unsigned short unlock_num; /* # unlock range structs */ unsigned short lock_num; /* # lock range structs */ uint32_t save_pid; /* Process Id of owner */ uint32_t offset32, length32; uint64_t offset64; uint64_t length64; DWORD result; int rc; uint32_t ltype; smb_ofile_t *ofile; uint16_t tmp_pid; /* locking uses 16-bit pids */ uint8_t brk; rc = smbsr_decode_vwv(sr, "4.wbblww", &sr->smb_fid, &lock_type, &oplock_level, &timeout, &unlock_num, &lock_num); if (rc != 0) return (SDRC_ERROR); smbsr_lookup_file(sr); if (sr->fid_ofile == NULL) { smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid); return (SDRC_ERROR); } ofile = sr->fid_ofile; if (lock_type & LOCKING_ANDX_SHARED_LOCK) ltype = SMB_LOCK_TYPE_READONLY; else ltype = SMB_LOCK_TYPE_READWRITE; save_pid = sr->smb_pid; /* Save the original pid */ if (lock_type & LOCKING_ANDX_OPLOCK_RELEASE) { if (oplock_level == 0) brk = SMB_OPLOCK_BREAK_TO_NONE; else brk = SMB_OPLOCK_BREAK_TO_LEVEL_II; smb_oplock_ack(ofile->f_node, ofile, brk); if (unlock_num == 0 && lock_num == 0) return (SDRC_NO_REPLY); } /* * No support for changing locktype (although we could probably * implement this) */ if (lock_type & LOCKING_ANDX_CHANGE_LOCK_TYPE) { smbsr_error(sr, 0, ERRDOS, ERROR_ATOMIC_LOCKS_NOT_SUPPORTED); return (SDRC_ERROR); } /* * No support for cancel lock (smbtorture expects this) */ if (lock_type & LOCKING_ANDX_CANCEL_LOCK) { smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, ERRDOS, ERROR_INVALID_PARAMETER); return (SDRC_ERROR); } if (lock_type & LOCKING_ANDX_LARGE_FILES) { /* * negotiated protocol should be NT LM 0.12 or later */ if (sr->session->dialect < NT_LM_0_12) { smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, ERRDOS, ERROR_INVALID_PARAMETER); return (SDRC_ERROR); } for (i = 0; i < unlock_num; i++) { rc = smb_mbc_decodef(&sr->smb_data, "w2.QQ", &tmp_pid, &offset64, &length64); if (rc) { /* * This is the error returned by Windows 2000 * even when STATUS32 has been negotiated. */ smbsr_error(sr, 0, ERRSRV, ERRerror); return (SDRC_ERROR); } sr->smb_pid = tmp_pid; /* NB: 16-bit */ result = smb_unlock_range(sr, sr->fid_ofile->f_node, offset64, length64); if (result != NT_STATUS_SUCCESS) { smbsr_error(sr, NT_STATUS_RANGE_NOT_LOCKED, ERRDOS, ERROR_NOT_LOCKED); return (SDRC_ERROR); } } for (i = 0; i < lock_num; i++) { rc = smb_mbc_decodef(&sr->smb_data, "w2.QQ", &tmp_pid, &offset64, &length64); if (rc) { smbsr_error(sr, 0, ERRSRV, ERRerror); return (SDRC_ERROR); } sr->smb_pid = tmp_pid; /* NB: 16-bit */ result = smb_lock_range(sr, offset64, length64, timeout, ltype); if (result != NT_STATUS_SUCCESS) { smb_lock_range_error(sr, result); return (SDRC_ERROR); } } } else { for (i = 0; i < unlock_num; i++) { rc = smb_mbc_decodef(&sr->smb_data, "wll", &tmp_pid, &offset32, &length32); if (rc) { smbsr_error(sr, 0, ERRSRV, ERRerror); return (SDRC_ERROR); } sr->smb_pid = tmp_pid; /* NB: 16-bit */ result = smb_unlock_range(sr, sr->fid_ofile->f_node, (uint64_t)offset32, (uint64_t)length32); if (result != NT_STATUS_SUCCESS) { smbsr_error(sr, NT_STATUS_RANGE_NOT_LOCKED, ERRDOS, ERROR_NOT_LOCKED); return (SDRC_ERROR); } } for (i = 0; i < lock_num; i++) { rc = smb_mbc_decodef(&sr->smb_data, "wll", &tmp_pid, &offset32, &length32); if (rc) { smbsr_error(sr, 0, ERRSRV, ERRerror); return (SDRC_ERROR); } sr->smb_pid = tmp_pid; /* NB: 16-bit */ result = smb_lock_range(sr, (uint64_t)offset32, (uint64_t)length32, timeout, ltype); if (result != NT_STATUS_SUCCESS) { smb_lock_range_error(sr, result); return (SDRC_ERROR); } } } sr->smb_pid = save_pid; if (smbsr_encode_result(sr, 2, 0, "bb.ww", 2, sr->andx_com, 7, 0)) return (SDRC_ERROR); return (SDRC_SUCCESS); }
/* * smb_com_trans2_find_next2 * * Client Request Value * ================================== ================================= * * WordCount 15 * SetupCount 1 * Setup[0] TRANS2_FIND_NEXT2 * * Parameter Block Encoding Description * ================================== ================================= * * USHORT Sid; Search handle * USHORT SearchCount; Maximum number of entries to * return * USHORT InformationLevel; Levels described in * TRANS2_FIND_FIRST2 request * ULONG ResumeKey; Value returned by previous find2 * call * USHORT Flags; Additional information: bit set- * 0 - close search after this * request * 1 - close search if end of search * reached * 2 - return resume keys for each * entry found * 3 - resume/continue from previous * ending place * 4 - find with backup intent * STRING FileName; Resume file name * * Sid is the value returned by a previous successful TRANS2_FIND_FIRST2 * call. If Bit3 of Flags is set, then FileName may be the NULL string, * since the search is continued from the previous TRANS2_FIND request. * Otherwise, FileName must not be more than 256 characters long. * * Response Field Description * ================================== ================================= * * USHORT SearchCount; Number of entries returned * USHORT EndOfSearch; Was last entry returned? * USHORT EaErrorOffset; Offset into EA list if EA error * USHORT LastNameOffset; Offset into data to file name of * last entry, if server needs it to * resume search; else 0 * UCHAR Data[TotalDataCount] Level dependent info about the * matches found in the search * * * The last parameter in the request is a filename, which is a * null-terminated unicode string. * * smb_mbc_decodef(&xa->req_param_mb, "%www lwu", sr, * &odid, &fa_maxcount, &fa_infolev, &cookie, &fa_fflag, &fname) * * The filename parameter is not currently decoded because we * expect a 2-byte null but Mac OS 10 clients send a 1-byte null, * which leads to a decode error. * Thus, we do not support resume by filename. We treat a request * to resume by filename as SMB_FIND_CONTINUE_FROM_LAST. */ smb_sdrc_t smb_com_trans2_find_next2(smb_request_t *sr, smb_xa_t *xa) { int count; uint16_t odid; uint32_t cookie; smb_odir_t *od; smb_find_args_t args; boolean_t eos; smb_odir_resume_t odir_resume; bzero(&args, sizeof (smb_find_args_t)); if (smb_mbc_decodef(&xa->req_param_mb, "%wwwlw", sr, &odid, &args.fa_maxcount, &args.fa_infolev, &cookie, &args.fa_fflag) != 0) { return (SDRC_ERROR); } /* continuation by filename not supported */ if ((args.fa_fflag & SMB_FIND_CONTINUE_FROM_LAST) || (cookie == 0)) { odir_resume.or_type = SMB_ODIR_RESUME_IDX; odir_resume.or_idx = 0; } else { odir_resume.or_type = SMB_ODIR_RESUME_COOKIE; odir_resume.or_cookie = cookie; } if (args.fa_fflag & SMB_FIND_WITH_BACKUP_INTENT) sr->user_cr = smb_user_getprivcred(sr->uid_user); args.fa_maxdata = smb_trans2_find_get_maxdata(sr, args.fa_infolev, args.fa_fflag); if (args.fa_maxdata == 0) return (SDRC_ERROR); od = smb_tree_lookup_odir(sr->tid_tree, odid); if (od == NULL) { smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERROR_INVALID_HANDLE); return (SDRC_ERROR); } smb_odir_resume_at(od, &odir_resume); count = smb_trans2_find_entries(sr, xa, od, &args, &eos); if (count == -1) { smb_odir_close(od); smb_odir_release(od); return (SDRC_ERROR); } if ((args.fa_fflag & SMB_FIND_CLOSE_AFTER_REQUEST) || (eos && (args.fa_fflag & SMB_FIND_CLOSE_AT_EOS))) { smb_odir_close(od); } /* else leave odir open for trans2_find_next2 */ smb_odir_release(od); (void) smb_mbc_encodef(&xa->rep_param_mb, "wwww", count, (eos) ? 1 : 0, 0, 0); return (SDRC_SUCCESS); }
/* * smb_com_trans2_find_first2 * * Client Request Value * ============================ ================================== * * UCHAR WordCount 15 * UCHAR TotalDataCount Total size of extended attribute list * UCHAR SetupCount 1 * UCHAR Setup[0] TRANS2_FIND_FIRST2 * * Parameter Block Encoding Description * ============================ ================================== * USHORT SearchAttributes; * USHORT SearchCount; Maximum number of entries to return * USHORT Flags; Additional information: * Bit 0 - close search after this request * Bit 1 - close search if end of search * reached * Bit 2 - return resume keys for each * entry found * Bit 3 - continue search from previous * ending place * Bit 4 - find with backup intent * USHORT InformationLevel; See below * ULONG SearchStorageType; * STRING FileName; Pattern for the search * UCHAR Data[ TotalDataCount ] FEAList if InformationLevel is * QUERY_EAS_FROM_LIST * * Response Parameter Block Description * ============================ ================================== * * USHORT Sid; Search handle * USHORT SearchCount; Number of entries returned * USHORT EndOfSearch; Was last entry returned? * USHORT EaErrorOffset; Offset into EA list if EA error * USHORT LastNameOffset; Offset into data to file name of last * entry, if server needs it to resume * search; else 0 * UCHAR Data[ TotalDataCount ] Level dependent info about the matches * found in the search */ smb_sdrc_t smb_com_trans2_find_first2(smb_request_t *sr, smb_xa_t *xa) { int count; uint16_t sattr, odid; char *path; smb_odir_t *od; smb_find_args_t args; boolean_t eos; uint32_t odir_flags = 0; bzero(&args, sizeof (smb_find_args_t)); if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERROR_ACCESS_DENIED); return (SDRC_ERROR); } if (smb_mbc_decodef(&xa->req_param_mb, "%wwww4.u", sr, &sattr, &args.fa_maxcount, &args.fa_fflag, &args.fa_infolev, &path) != 0) { return (SDRC_ERROR); } if (smb_is_stream_name(path)) { smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID, ERRDOS, ERROR_INVALID_NAME); return (SDRC_ERROR); } if (args.fa_fflag & SMB_FIND_WITH_BACKUP_INTENT) { sr->user_cr = smb_user_getprivcred(sr->uid_user); odir_flags = SMB_ODIR_OPENF_BACKUP_INTENT; } args.fa_maxdata = smb_trans2_find_get_maxdata(sr, args.fa_infolev, args.fa_fflag); if (args.fa_maxdata == 0) return (SDRC_ERROR); if (sr->smb_flg2 & SMB_FLAGS2_UNICODE) (void) smb_convert_wildcards(path); odid = smb_odir_open(sr, path, sattr, odir_flags); if (odid == 0) return (SDRC_ERROR); od = smb_tree_lookup_odir(sr->tid_tree, odid); if (od == NULL) return (SDRC_ERROR); count = smb_trans2_find_entries(sr, xa, od, &args, &eos); if (count == -1) { smb_odir_close(od); smb_odir_release(od); return (SDRC_ERROR); } if (count == 0) { smb_odir_close(od); smb_odir_release(od); smbsr_errno(sr, ENOENT); return (SDRC_ERROR); } if ((args.fa_fflag & SMB_FIND_CLOSE_AFTER_REQUEST) || (eos && (args.fa_fflag & SMB_FIND_CLOSE_AT_EOS))) { smb_odir_close(od); } /* else leave odir open for trans2_find_next2 */ smb_odir_release(od); (void) smb_mbc_encodef(&xa->rep_param_mb, "wwwww", odid, count, (eos) ? 1 : 0, 0, 0); return (SDRC_SUCCESS); }
/* * smb_com_trans2_find_next2 * * Client Request Value * ================================== ================================= * * WordCount 15 * SetupCount 1 * Setup[0] TRANS2_FIND_NEXT2 * * Parameter Block Encoding Description * ================================== ================================= * * USHORT Sid; Search handle * USHORT SearchCount; Maximum number of entries to * return * USHORT InformationLevel; Levels described in * TRANS2_FIND_FIRST2 request * ULONG ResumeKey; Value returned by previous find2 * call * USHORT Flags; Additional information: bit set- * 0 - close search after this * request * 1 - close search if end of search * reached * 2 - return resume keys for each * entry found * 3 - resume/continue from previous * ending place * 4 - find with backup intent * STRING FileName; Resume file name * * Sid is the value returned by a previous successful TRANS2_FIND_FIRST2 * call. If Bit3 of Flags is set, then FileName may be the NULL string, * since the search is continued from the previous TRANS2_FIND request. * Otherwise, FileName must not be more than 256 characters long. * * Response Field Description * ================================== ================================= * * USHORT SearchCount; Number of entries returned * USHORT EndOfSearch; Was last entry returned? * USHORT EaErrorOffset; Offset into EA list if EA error * USHORT LastNameOffset; Offset into data to file name of * last entry, if server needs it to * resume search; else 0 * UCHAR Data[TotalDataCount] Level dependent info about the * matches found in the search * * * The last parameter in the request is a filename, which is a * null-terminated unicode string. * * smb_mbc_decodef(&xa->req_param_mb, "%www lwu", sr, * &odid, &fa_maxcount, &fa_infolev, &cookie, &fa_fflag, &fname) * * The filename parameter is not currently decoded because we * expect a 2-byte null but Mac OS 10 clients send a 1-byte null, * which leads to a decode error. * Thus, we do not support resume by filename. We treat a request * to resume by filename as SMB_FIND_CONTINUE_FROM_LAST. */ smb_sdrc_t smb_com_trans2_find_next2(smb_request_t *sr, smb_xa_t *xa) { int count; uint16_t odid; smb_odir_t *od; smb_find_args_t args; smb_odir_resume_t odir_resume; bzero(&args, sizeof (args)); bzero(&odir_resume, sizeof (odir_resume)); if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERROR_ACCESS_DENIED); return (SDRC_ERROR); } if (smb_mbc_decodef(&xa->req_param_mb, "%wwwlwu", sr, &odid, &args.fa_maxcount, &args.fa_infolev, &odir_resume.or_cookie, &args.fa_fflag, &odir_resume.or_fname) != 0) { return (SDRC_ERROR); } if (args.fa_fflag & SMB_FIND_WITH_BACKUP_INTENT) sr->user_cr = smb_user_getprivcred(sr->uid_user); args.fa_maxdata = smb_trans2_find_get_maxdata(sr, args.fa_infolev, args.fa_fflag); if (args.fa_maxdata == 0) return (SDRC_ERROR); od = smb_tree_lookup_odir(sr->tid_tree, odid); if (od == NULL) { smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERROR_INVALID_HANDLE); return (SDRC_ERROR); } /* * Set the correct position in the directory. * * "Continue from last" is easy, but due to a history of * buggy server implementations, most clients don't use * that method. The most widely used (and reliable) is * resume by file name. Unfortunately, that can't really * be fully supported unless your file system stores all * directory entries in some sorted order (like NTFS). * We can partially support resume by name, where the only * name we're ever asked to resume on is the same as the * most recent we returned. That's always what the client * gives us as the resume name, so we can simply remember * the last name/offset pair and use that to position on * the following FindNext call. In the unlikely event * that the client asks to resume somewhere else, we'll * use the numeric resume key, and hope the client gives * correctly uses one of the resume keys we provided. */ if (args.fa_fflag & SMB_FIND_CONTINUE_FROM_LAST) { odir_resume.or_type = SMB_ODIR_RESUME_CONT; } else { odir_resume.or_type = SMB_ODIR_RESUME_FNAME; } smb_odir_resume_at(od, &odir_resume); count = smb_trans2_find_entries(sr, xa, od, &args); if (count == -1) { smb_odir_close(od); smb_odir_release(od); return (SDRC_ERROR); } if ((args.fa_fflag & SMB_FIND_CLOSE_AFTER_REQUEST) || (args.fa_eos && (args.fa_fflag & SMB_FIND_CLOSE_AT_EOS))) { smb_odir_close(od); } /* else leave odir open for trans2_find_next2 */ smb_odir_release(od); (void) smb_mbc_encodef(&xa->rep_param_mb, "wwww", count, /* Search Count */ args.fa_eos, /* End Of Search */ 0, /* EA Error Offset */ args.fa_lno); /* Last Name Offset */ return (SDRC_SUCCESS); }
/* * smb_nt_transact_notify_change * * This function is responsible for processing NOTIFY CHANGE requests. * Requests are stored in a global queue. This queue is processed when * a monitored directory is changed or client cancels one of its already * sent requests. */ smb_sdrc_t smb_nt_transact_notify_change(struct smb_request *sr, struct smb_xa *xa) { uint32_t CompletionFilter; unsigned char WatchTree; smb_node_t *node; if (smb_mbc_decodef(&xa->req_setup_mb, "lwb", &CompletionFilter, &sr->smb_fid, &WatchTree) != 0) return (SDRC_NOT_IMPLEMENTED); smbsr_lookup_file(sr); if (sr->fid_ofile == NULL) { smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid); return (SDRC_ERROR); } node = sr->fid_ofile->f_node; if (!smb_node_is_dir(node)) { /* * Notify change requests are only valid on directories. */ smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY, 0, 0); return (SDRC_ERROR); } mutex_enter(&sr->sr_mutex); switch (sr->sr_state) { case SMB_REQ_STATE_ACTIVE: node->waiting_event++; node->flags |= NODE_FLAGS_NOTIFY_CHANGE; if ((node->flags & NODE_FLAGS_CHANGED) == 0) { sr->sr_ncr.nc_node = node; sr->sr_ncr.nc_flags = CompletionFilter; if (WatchTree) sr->sr_ncr.nc_flags |= NODE_FLAGS_WATCH_TREE; sr->sr_keep = B_TRUE; sr->sr_state = SMB_REQ_STATE_WAITING_EVENT; smb_slist_insert_tail(&smb_ncr_list, sr); /* * Monitor events system-wide. * * XXX: smb_node_ref() and smb_node_release() * take &node->n_lock. May need alternate forms * of these routines if node->n_lock is taken * around calls to smb_fem_fcn_install() and * smb_fem_fcn_uninstall(). */ smb_fem_fcn_install(node); mutex_exit(&sr->sr_mutex); return (SDRC_SR_KEPT); } else { /* node already changed, reply immediately */ if (--node->waiting_event == 0) node->flags &= ~(NODE_FLAGS_NOTIFY_CHANGE | NODE_FLAGS_CHANGED); mutex_exit(&sr->sr_mutex); return (SDRC_SUCCESS); } case SMB_REQ_STATE_CANCELED: mutex_exit(&sr->sr_mutex); smbsr_error(sr, NT_STATUS_CANCELLED, 0, 0); return (SDRC_ERROR); default: ASSERT(0); mutex_exit(&sr->sr_mutex); return (SDRC_SUCCESS); } }
/* * smb_nt_transact_create * * This command is used to create or open a file or directory, when EAs * or an SD must be applied to the file. The request parameter block * encoding, data block encoding and output parameter block encoding are * described in CIFS section 4.2.2. * * The format of the command is SmbNtTransact but it is basically the same * as SmbNtCreateAndx with the option to supply extended attributes or a * security descriptor. For information not defined in CIFS section 4.2.2 * see section 4.2.1 (NT_CREATE_ANDX). */ smb_sdrc_t smb_pre_nt_transact_create(smb_request_t *sr, smb_xa_t *xa) { struct open_param *op = &sr->arg.open; uint8_t SecurityFlags; uint32_t EaLength; uint32_t ImpersonationLevel; uint32_t NameLength; uint32_t sd_len; uint32_t status; smb_sd_t sd; int rc; bzero(op, sizeof (sr->arg.open)); rc = smb_mbc_decodef(&xa->req_param_mb, "%lllqllllllllb", sr, &op->nt_flags, &op->rootdirfid, &op->desired_access, &op->dsize, &op->dattr, &op->share_access, &op->create_disposition, &op->create_options, &sd_len, &EaLength, &NameLength, &ImpersonationLevel, &SecurityFlags); if (rc == 0) { if (NameLength == 0) { op->fqi.fq_path.pn_path = "\\"; } else if (NameLength >= MAXPATHLEN) { smbsr_error(sr, NT_STATUS_OBJECT_PATH_NOT_FOUND, ERRDOS, ERROR_PATH_NOT_FOUND); rc = -1; } else { rc = smb_mbc_decodef(&xa->req_param_mb, "%#u", sr, NameLength, &op->fqi.fq_path.pn_path); } } op->op_oplock_level = SMB_OPLOCK_NONE; if (op->nt_flags & NT_CREATE_FLAG_REQUEST_OPLOCK) { if (op->nt_flags & NT_CREATE_FLAG_REQUEST_OPBATCH) op->op_oplock_level = SMB_OPLOCK_BATCH; else op->op_oplock_level = SMB_OPLOCK_EXCLUSIVE; } if (sd_len) { status = smb_decode_sd(xa, &sd); if (status != NT_STATUS_SUCCESS) { smbsr_error(sr, status, 0, 0); return (SDRC_ERROR); } op->sd = kmem_alloc(sizeof (smb_sd_t), KM_SLEEP); *op->sd = sd; } else { op->sd = NULL; } DTRACE_SMB_2(op__NtTransactCreate__start, smb_request_t *, sr, struct open_param *, op); return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); }
smb_sdrc_t smb_com_echo(struct smb_request *sr) { unsigned short necho; unsigned short nbytes; unsigned short i; struct mbuf_chain reply; char *data; uint16_t pid_hi, pid_lo; pid_hi = sr->smb_pid >> 16; pid_lo = (uint16_t)sr->smb_pid; if (smbsr_decode_vwv(sr, "w", &necho) != 0) return (SDRC_ERROR); /* * Don't let the client fool us into doing * more work than is "reasonable". */ if (necho > smb_max_echo) necho = smb_max_echo; nbytes = sr->smb_bcc; data = smb_srm_zalloc(sr, nbytes); if (smb_mbc_decodef(&sr->smb_data, "#c", nbytes, data)) return (SDRC_ERROR); for (i = 1; i <= necho; ++i) { /* * According to [MS-CIFS] 3.3.5.32 echo is * subject to cancellation. */ if (sr->sr_state != SMB_REQ_STATE_ACTIVE) break; MBC_INIT(&reply, SMB_HEADER_ED_LEN + 10 + nbytes); (void) smb_mbc_encodef(&reply, SMB_HEADER_ED_FMT, sr->first_smb_com, sr->smb_rcls, sr->smb_reh, sr->smb_err, sr->smb_flg | SMB_FLAGS_REPLY, sr->smb_flg2, pid_hi, sr->smb_sig, sr->smb_tid, pid_lo, sr->smb_uid, sr->smb_mid); (void) smb_mbc_encodef(&reply, "bww#c", 1, i, nbytes, nbytes, data); if (sr->session->signing.flags & SMB_SIGNING_ENABLED) smb_sign_reply(sr, &reply); (void) smb_session_send(sr->session, 0, &reply); delay(MSEC_TO_TICK(100)); } return (SDRC_NO_REPLY); }
smb_sdrc_t smb2_close(smb_request_t *sr) { smb_attr_t attr; smb_ofile_t *of; uint16_t StructSize; uint16_t Flags; uint32_t reserved; smb2fid_t smb2fid; uint32_t status; int rc = 0; /* * SMB2 Close request */ rc = smb_mbc_decodef( &sr->smb_data, "wwlqq", &StructSize, /* w */ &Flags, /* w */ &reserved, /* l */ &smb2fid.persistent, /* q */ &smb2fid.temporal); /* q */ if (rc) return (SDRC_ERROR); if (StructSize != 24) return (SDRC_ERROR); status = smb2sr_lookup_fid(sr, &smb2fid); if (status) { smb2sr_put_error(sr, status); return (SDRC_SUCCESS); } of = sr->fid_ofile; bzero(&attr, sizeof (attr)); if (Flags & SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB) { attr.sa_mask = SMB_AT_ALL; status = smb2_ofile_getattr(sr, of, &attr); if (status) { /* * We could not stat the open file. * Let's not fail the close call, * but just turn off the flag. */ Flags = 0; } } smb_ofile_close(of, 0); /* * SMB2 Close reply */ (void) smb_mbc_encodef( &sr->reply, "wwlTTTTqql", 60, /* StructSize */ /* w */ Flags, /* w */ 0, /* reserved */ /* l */ &attr.sa_crtime, /* T */ &attr.sa_vattr.va_atime, /* T */ &attr.sa_vattr.va_mtime, /* T */ &attr.sa_vattr.va_ctime, /* T */ attr.sa_allocsz, /* q */ attr.sa_vattr.va_size, /* q */ attr.sa_dosattr); /* l */ return (SDRC_SUCCESS); }