示例#1
0
/*
 * 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);
}
示例#2
0
/*
 * 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);
}
示例#3
0
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);
}
示例#4
0
/*
 * 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);
}
示例#5
0
/*
 * 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);
}
示例#6
0
/*
 * 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);
}