/* * smb_odir_open * * Create an odir representing the directory specified in pathname. * * Returns: * odid - Unique identifier of newly created odir. * 0 - error, error details set in sr. */ uint16_t smb_odir_open(smb_request_t *sr, char *path, uint16_t sattr, uint32_t flags) { int rc; smb_tree_t *tree; smb_node_t *dnode; char pattern[MAXNAMELEN]; uint16_t odid; cred_t *cr; ASSERT(sr); ASSERT(sr->sr_magic == SMB_REQ_MAGIC); ASSERT(sr->tid_tree); ASSERT(sr->tid_tree->t_magic == SMB_TREE_MAGIC); tree = sr->tid_tree; if (sr->session->dialect < NT_LM_0_12) smb_convert_wildcards(path); rc = smb_pathname_reduce(sr, sr->user_cr, path, tree->t_snode, tree->t_snode, &dnode, pattern); if (rc != 0) { smbsr_errno(sr, rc); return (0); } if (!smb_node_is_dir(dnode)) { smbsr_error(sr, NT_STATUS_OBJECT_PATH_NOT_FOUND, ERRDOS, ERROR_PATH_NOT_FOUND); smb_node_release(dnode); return (0); } if (smb_fsop_access(sr, sr->user_cr, dnode, FILE_LIST_DIRECTORY) != 0) { smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERROR_ACCESS_DENIED); smb_node_release(dnode); return (0); } if (flags & SMB_ODIR_OPENF_BACKUP_INTENT) cr = smb_user_getprivcred(sr->uid_user); else cr = sr->uid_user->u_cred; odid = smb_odir_create(sr, dnode, pattern, sattr, cr); smb_node_release(dnode); return (odid); }
/* * smb_ofile_open */ smb_ofile_t * smb_ofile_open( smb_tree_t *tree, smb_node_t *node, uint16_t pid, struct open_param *op, uint16_t ftype, uint32_t uniqid, smb_error_t *err) { smb_ofile_t *of; uint16_t fid; smb_attr_t attr; int rc; if (smb_idpool_alloc(&tree->t_fid_pool, &fid)) { err->status = NT_STATUS_TOO_MANY_OPENED_FILES; err->errcls = ERRDOS; err->errcode = ERROR_TOO_MANY_OPEN_FILES; return (NULL); } of = kmem_cache_alloc(tree->t_server->si_cache_ofile, KM_SLEEP); bzero(of, sizeof (smb_ofile_t)); of->f_magic = SMB_OFILE_MAGIC; of->f_refcnt = 1; of->f_fid = fid; of->f_uniqid = uniqid; of->f_opened_by_pid = pid; of->f_granted_access = op->desired_access; of->f_share_access = op->share_access; of->f_create_options = op->create_options; of->f_cr = (op->create_options & FILE_OPEN_FOR_BACKUP_INTENT) ? smb_user_getprivcred(tree->t_user) : tree->t_user->u_cred; crhold(of->f_cr); of->f_ftype = ftype; of->f_server = tree->t_server; of->f_session = tree->t_user->u_session; of->f_user = tree->t_user; of->f_tree = tree; of->f_node = node; mutex_init(&of->f_mutex, NULL, MUTEX_DEFAULT, NULL); of->f_state = SMB_OFILE_STATE_OPEN; if (ftype == SMB_FTYPE_MESG_PIPE) { of->f_pipe = smb_opipe_alloc(tree->t_server); smb_server_inc_pipes(of->f_server); } else { ASSERT(ftype == SMB_FTYPE_DISK); /* Regular file, not a pipe */ ASSERT(node); if (of->f_granted_access == FILE_EXECUTE) of->f_flags |= SMB_OFLAGS_EXECONLY; bzero(&attr, sizeof (smb_attr_t)); attr.sa_mask = SMB_AT_UID | SMB_AT_DOSATTR; rc = smb_node_getattr(NULL, node, of->f_cr, NULL, &attr); if (rc != 0) { of->f_magic = 0; mutex_destroy(&of->f_mutex); crfree(of->f_cr); smb_idpool_free(&tree->t_fid_pool, of->f_fid); kmem_cache_free(tree->t_server->si_cache_ofile, of); err->status = NT_STATUS_INTERNAL_ERROR; err->errcls = ERRDOS; err->errcode = ERROR_INTERNAL_ERROR; return (NULL); } if (crgetuid(of->f_cr) == attr.sa_vattr.va_uid) { /* * Add this bit for the file's owner even if it's not * specified in the request (Windows behavior). */ of->f_granted_access |= FILE_READ_ATTRIBUTES; } if (smb_node_is_file(node)) { of->f_mode = smb_fsop_amask_to_omode(of->f_granted_access); if (smb_fsop_open(node, of->f_mode, of->f_cr) != 0) { of->f_magic = 0; mutex_destroy(&of->f_mutex); crfree(of->f_cr); smb_idpool_free(&tree->t_fid_pool, of->f_fid); kmem_cache_free(tree->t_server->si_cache_ofile, of); err->status = NT_STATUS_ACCESS_DENIED; err->errcls = ERRDOS; err->errcode = ERROR_ACCESS_DENIED; return (NULL); } } if (tree->t_flags & SMB_TREE_READONLY) of->f_flags |= SMB_OFLAGS_READONLY; /* * Note that if we created_readonly, that * will _not_ yet show in attr.sa_dosattr * so creating a readonly file gives the * caller a writable handle as it should. */ if (attr.sa_dosattr & FILE_ATTRIBUTE_READONLY) of->f_flags |= SMB_OFLAGS_READONLY; smb_node_inc_open_ofiles(node); smb_node_add_ofile(node, of); smb_node_ref(node); smb_server_inc_files(of->f_server); } smb_llist_enter(&tree->t_ofile_list, RW_WRITER); smb_llist_insert_tail(&tree->t_ofile_list, of); smb_llist_exit(&tree->t_ofile_list); atomic_inc_32(&tree->t_open_files); atomic_inc_32(&of->f_session->s_file_cnt); return (of); }
smb_sdrc_t smb_com_nt_create_andx(struct smb_request *sr) { struct open_param *op = &sr->arg.open; unsigned char DirFlag; smb_attr_t attr; smb_node_t *node; int rc; if ((op->create_options & FILE_DELETE_ON_CLOSE) && !(op->desired_access & DELETE)) { smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, ERRDOS, ERRbadaccess); return (SDRC_ERROR); } if (op->create_disposition > FILE_MAXIMUM_DISPOSITION) { smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, ERRDOS, ERRbadaccess); return (SDRC_ERROR); } if (op->dattr & FILE_FLAG_WRITE_THROUGH) op->create_options |= FILE_WRITE_THROUGH; if (op->dattr & FILE_FLAG_DELETE_ON_CLOSE) op->create_options |= FILE_DELETE_ON_CLOSE; if (op->dattr & FILE_FLAG_BACKUP_SEMANTICS) op->create_options |= FILE_OPEN_FOR_BACKUP_INTENT; if (op->create_options & FILE_OPEN_FOR_BACKUP_INTENT) sr->user_cr = smb_user_getprivcred(sr->uid_user); if (op->rootdirfid == 0) { op->fqi.fq_dnode = sr->tid_tree->t_snode; } else { op->dir = smb_ofile_lookup_by_fid(sr->tid_tree, (uint16_t)op->rootdirfid); if (op->dir == NULL) { smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid); return (SDRC_ERROR); } op->fqi.fq_dnode = op->dir->f_node; } op->op_oplock_levelII = B_TRUE; if (smb_common_open(sr) != NT_STATUS_SUCCESS) return (SDRC_ERROR); switch (sr->tid_tree->t_res_type & STYPE_MASK) { case STYPE_DISKTREE: case STYPE_PRINTQ: if (op->create_options & FILE_DELETE_ON_CLOSE) smb_ofile_set_delete_on_close(sr->fid_ofile); node = sr->fid_ofile->f_node; DirFlag = smb_node_is_dir(node) ? 1 : 0; if (smb_node_getattr(sr, node, &attr) != 0) { smbsr_error(sr, NT_STATUS_INTERNAL_ERROR, ERRDOS, ERROR_INTERNAL_ERROR); return (SDRC_ERROR); } rc = smbsr_encode_result(sr, 34, 0, "bb.wbwlTTTTlqqwwbw", 34, sr->andx_com, 0x67, op->op_oplock_level, sr->smb_fid, op->action_taken, &attr.sa_crtime, &attr.sa_vattr.va_atime, &attr.sa_vattr.va_mtime, &attr.sa_vattr.va_ctime, op->dattr & FILE_ATTRIBUTE_MASK, attr.sa_allocsz, attr.sa_vattr.va_size, op->ftype, op->devstate, DirFlag, 0); break; case STYPE_IPC: rc = smbsr_encode_result(sr, 34, 0, "bb.wbwlqqqqlqqwwbw", 34, sr->andx_com, 0x67, 0, sr->smb_fid, op->action_taken, 0LL, 0LL, 0LL, 0LL, FILE_ATTRIBUTE_NORMAL, 0x1000LL, 0LL, op->ftype, op->devstate, 0, 0); break; default: smbsr_error(sr, NT_STATUS_INVALID_DEVICE_REQUEST, ERRDOS, ERROR_INVALID_FUNCTION); return (SDRC_ERROR); } return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); }
/* * 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_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_ofile_open */ smb_ofile_t * smb_ofile_open( smb_request_t *sr, smb_node_t *node, struct open_param *op, uint16_t ftype, uint32_t uniqid, smb_error_t *err) { smb_tree_t *tree = sr->tid_tree; smb_ofile_t *of; uint16_t fid; smb_attr_t attr; int rc; enum errstates { EMPTY, FIDALLOC, CRHELD, MUTEXINIT }; enum errstates state = EMPTY; if (smb_idpool_alloc(&tree->t_fid_pool, &fid)) { err->status = NT_STATUS_TOO_MANY_OPENED_FILES; err->errcls = ERRDOS; err->errcode = ERROR_TOO_MANY_OPEN_FILES; return (NULL); } state = FIDALLOC; of = kmem_cache_alloc(smb_cache_ofile, KM_SLEEP); bzero(of, sizeof (smb_ofile_t)); of->f_magic = SMB_OFILE_MAGIC; of->f_refcnt = 1; of->f_fid = fid; of->f_uniqid = uniqid; of->f_opened_by_pid = sr->smb_pid; of->f_granted_access = op->desired_access; of->f_share_access = op->share_access; of->f_create_options = op->create_options; of->f_cr = (op->create_options & FILE_OPEN_FOR_BACKUP_INTENT) ? smb_user_getprivcred(sr->uid_user) : sr->uid_user->u_cred; crhold(of->f_cr); state = CRHELD; of->f_ftype = ftype; of->f_server = tree->t_server; of->f_session = tree->t_session; /* * grab a ref for of->f_user * released in smb_ofile_delete() */ smb_user_hold_internal(sr->uid_user); of->f_user = sr->uid_user; of->f_tree = tree; of->f_node = node; mutex_init(&of->f_mutex, NULL, MUTEX_DEFAULT, NULL); state = MUTEXINIT; of->f_state = SMB_OFILE_STATE_OPEN; if (ftype == SMB_FTYPE_MESG_PIPE) { /* See smb_opipe_open. */ of->f_pipe = op->pipe; smb_server_inc_pipes(of->f_server); } else { ASSERT(ftype == SMB_FTYPE_DISK); /* Regular file, not a pipe */ ASSERT(node); /* * Note that the common open path often adds bits like * READ_CONTROL, so the logic "is this open exec-only" * needs to look at only the FILE_DATA_ALL bits. */ if ((of->f_granted_access & FILE_DATA_ALL) == FILE_EXECUTE) of->f_flags |= SMB_OFLAGS_EXECONLY; bzero(&attr, sizeof (smb_attr_t)); attr.sa_mask = SMB_AT_UID | SMB_AT_DOSATTR; rc = smb_node_getattr(NULL, node, of->f_cr, NULL, &attr); if (rc != 0) { err->status = NT_STATUS_INTERNAL_ERROR; err->errcls = ERRDOS; err->errcode = ERROR_INTERNAL_ERROR; goto errout; } if (crgetuid(of->f_cr) == attr.sa_vattr.va_uid) { /* * Add this bit for the file's owner even if it's not * specified in the request (Windows behavior). */ of->f_granted_access |= FILE_READ_ATTRIBUTES; } if (smb_node_is_file(node)) { of->f_mode = smb_fsop_amask_to_omode(of->f_granted_access); if (smb_fsop_open(node, of->f_mode, of->f_cr) != 0) { err->status = NT_STATUS_ACCESS_DENIED; err->errcls = ERRDOS; err->errcode = ERROR_ACCESS_DENIED; goto errout; } } if (tree->t_flags & SMB_TREE_READONLY) of->f_flags |= SMB_OFLAGS_READONLY; /* * Note that if we created_readonly, that * will _not_ yet show in attr.sa_dosattr * so creating a readonly file gives the * caller a writable handle as it should. */ if (attr.sa_dosattr & FILE_ATTRIBUTE_READONLY) of->f_flags |= SMB_OFLAGS_READONLY; smb_node_inc_open_ofiles(node); smb_node_add_ofile(node, of); smb_node_ref(node); smb_server_inc_files(of->f_server); } smb_llist_enter(&tree->t_ofile_list, RW_WRITER); smb_llist_insert_tail(&tree->t_ofile_list, of); smb_llist_exit(&tree->t_ofile_list); atomic_inc_32(&tree->t_open_files); atomic_inc_32(&of->f_session->s_file_cnt); return (of); errout: switch (state) { case MUTEXINIT: mutex_destroy(&of->f_mutex); smb_user_release(of->f_user); /*FALLTHROUGH*/ case CRHELD: crfree(of->f_cr); of->f_magic = 0; kmem_cache_free(smb_cache_ofile, of); /*FALLTHROUGH*/ case FIDALLOC: smb_idpool_free(&tree->t_fid_pool, fid); /*FALLTHROUGH*/ case EMPTY: break; } return (NULL); }
smb_sdrc_t smb_nt_transact_create(smb_request_t *sr, smb_xa_t *xa) { struct open_param *op = &sr->arg.open; uint8_t DirFlag; smb_attr_t attr; smb_ofile_t *of; int rc; if ((op->create_options & FILE_DELETE_ON_CLOSE) && !(op->desired_access & DELETE)) { smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, ERRDOS, ERRbadaccess); return (SDRC_ERROR); } if (op->create_disposition > FILE_MAXIMUM_DISPOSITION) { smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, ERRDOS, ERRbadaccess); return (SDRC_ERROR); } if (op->dattr & FILE_FLAG_WRITE_THROUGH) op->create_options |= FILE_WRITE_THROUGH; if (op->dattr & FILE_FLAG_DELETE_ON_CLOSE) op->create_options |= FILE_DELETE_ON_CLOSE; if (op->dattr & FILE_FLAG_BACKUP_SEMANTICS) op->create_options |= FILE_OPEN_FOR_BACKUP_INTENT; if (op->create_options & FILE_OPEN_FOR_BACKUP_INTENT) sr->user_cr = smb_user_getprivcred(sr->uid_user); if (op->rootdirfid == 0) { op->fqi.fq_dnode = sr->tid_tree->t_snode; } else { op->dir = smb_ofile_lookup_by_fid(sr, (uint16_t)op->rootdirfid); if (op->dir == NULL) { smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid); return (SDRC_ERROR); } op->fqi.fq_dnode = op->dir->f_node; } op->op_oplock_levelII = B_TRUE; if (smb_common_open(sr) != NT_STATUS_SUCCESS) return (SDRC_ERROR); /* * NB: after the above smb_common_open() success, * we have a handle allocated (sr->fid_ofile). * If we don't return success, we must close it. */ of = sr->fid_ofile; switch (sr->tid_tree->t_res_type & STYPE_MASK) { case STYPE_DISKTREE: case STYPE_PRINTQ: if (op->create_options & FILE_DELETE_ON_CLOSE) smb_ofile_set_delete_on_close(of); DirFlag = smb_node_is_dir(of->f_node) ? 1 : 0; bzero(&attr, sizeof (attr)); attr.sa_mask = SMB_AT_ALL; rc = smb_node_getattr(sr, of->f_node, of->f_cr, of, &attr); if (rc != 0) { smbsr_errno(sr, rc); goto errout; } (void) smb_mbc_encodef(&xa->rep_param_mb, "b.wllTTTTlqqwwb", op->op_oplock_level, sr->smb_fid, op->action_taken, 0, /* EaErrorOffset */ &attr.sa_crtime, &attr.sa_vattr.va_atime, &attr.sa_vattr.va_mtime, &attr.sa_vattr.va_ctime, op->dattr & FILE_ATTRIBUTE_MASK, attr.sa_allocsz, attr.sa_vattr.va_size, op->ftype, op->devstate, DirFlag); break; case STYPE_IPC: bzero(&attr, sizeof (smb_attr_t)); (void) smb_mbc_encodef(&xa->rep_param_mb, "b.wllTTTTlqqwwb", 0, sr->smb_fid, op->action_taken, 0, /* EaErrorOffset */ &attr.sa_crtime, &attr.sa_vattr.va_atime, &attr.sa_vattr.va_mtime, &attr.sa_vattr.va_ctime, op->dattr, 0x1000LL, 0LL, op->ftype, op->devstate, 0); break; default: smbsr_error(sr, NT_STATUS_INVALID_DEVICE_REQUEST, ERRDOS, ERROR_INVALID_FUNCTION); goto errout; } return (SDRC_SUCCESS); errout: smb_ofile_close(of, 0); return (SDRC_ERROR); }
/* * 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); }