/*
 * smb_query_by_fid
 *
 * Common code for querying file information by open file (or pipe) id.
 * Use the id to identify the node / pipe object and request the
 * smb_queryinfo_t data for that object.
 */
static int
smb_query_by_fid(smb_request_t *sr, smb_xa_t *xa, uint16_t infolev)
{
	int		rc;
	smb_queryinfo_t	*qinfo;
	smb_node_t	*node;
	smb_opipe_t	*opipe;

	smbsr_lookup_file(sr);

	if (sr->fid_ofile == NULL) {
		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
		return (-1);
	}

	if (infolev == SMB_INFO_IS_NAME_VALID) {
		smbsr_error(sr, 0, ERRDOS, ERROR_INVALID_LEVEL);
		smbsr_release_file(sr);
		return (-1);
	}

	if ((sr->fid_ofile->f_ftype == SMB_FTYPE_MESG_PIPE) &&
	    (!smb_query_pipe_valid_infolev(sr, infolev))) {
		smbsr_release_file(sr);
		return (-1);
	}

	sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
	qinfo = kmem_alloc(sizeof (smb_queryinfo_t), KM_SLEEP);

	switch (sr->fid_ofile->f_ftype) {
	case SMB_FTYPE_DISK:
		node = sr->fid_ofile->f_node;
		rc = smb_query_fileinfo(sr, node, infolev, qinfo);
		break;
	case SMB_FTYPE_MESG_PIPE:
		opipe = sr->fid_ofile->f_pipe;
		rc = smb_query_pipeinfo(sr, opipe, infolev, qinfo);
		break;
	default:
		smbsr_error(sr, 0, ERRDOS, ERRbadfile);
		rc = -1;
		break;
	}

	if (rc == 0)
		rc = smb_query_encode_response(sr, xa, infolev, qinfo);

	kmem_free(qinfo, sizeof (smb_queryinfo_t));
	smbsr_release_file(sr);
	return (rc);
}
/*
 * smb_query_by_path
 *
 * Common code for querying file information by file name.
 * Use the file name to identify the node object and request the
 * smb_queryinfo_t data for that node.
 *
 * Path should be set in sr->arg.dirop.fqi.fq_path prior to
 * calling smb_query_by_path.
 *
 * Querying attributes on a named pipe by name is an error and
 * is handled in the calling functions so that they can return
 * the appropriate error status code (which differs by caller).
 */
static int
smb_query_by_path(smb_request_t *sr, smb_xa_t *xa, uint16_t infolev)
{
	smb_queryinfo_t	*qinfo;
	smb_node_t	*node, *dnode;
	smb_pathname_t	*pn;
	int		rc;

	/*
	 * The function smb_query_fileinfo is used here and in
	 * smb_query_by_fid.  That common function needs this
	 * one to call it with a NULL fid_ofile, so check here.
	 * Note: smb_query_by_fid enforces the opposite.
	 *
	 * In theory we could ASSERT this, but whether we have
	 * fid_ofile set here depends on what sequence of SMB
	 * commands the client has sent in this message, so
	 * let's be cautious and handle it as an error.
	 */
	if (sr->fid_ofile != NULL)
		return (-1);


	/* VALID, but not yet supported */
	if (infolev == SMB_FILE_ACCESS_INFORMATION) {
		smbsr_error(sr, 0, ERRDOS, ERROR_INVALID_LEVEL);
		return (-1);
	}

	pn = &sr->arg.dirop.fqi.fq_path;
	smb_pathname_init(sr, pn, pn->pn_path);
	if (!smb_pathname_validate(sr, pn))
		return (-1);

	qinfo = kmem_alloc(sizeof (smb_queryinfo_t), KM_SLEEP);

	rc = smb_pathname_reduce(sr, sr->user_cr, pn->pn_path,
	    sr->tid_tree->t_snode, sr->tid_tree->t_snode, &dnode,
	    qinfo->qi_name);

	if (rc == 0) {
		rc = smb_fsop_lookup_name(sr, sr->user_cr, SMB_FOLLOW_LINKS,
		    sr->tid_tree->t_snode, dnode, qinfo->qi_name, &node);
		smb_node_release(dnode);
	}

	if (rc != 0) {
		if (rc == ENOENT)
			smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND,
			    ERRDOS, ERROR_FILE_NOT_FOUND);
		else
			smbsr_errno(sr, rc);

		kmem_free(qinfo, sizeof (smb_queryinfo_t));
		return (-1);
	}

	if ((sr->smb_flg2 & SMB_FLAGS2_DFS) && smb_node_is_dfslink(node)) {
		smbsr_error(sr, NT_STATUS_PATH_NOT_COVERED, ERRSRV, ERRbadpath);
		kmem_free(qinfo, sizeof (smb_queryinfo_t));
		smb_node_release(node);
		return (-1);
	}

	rc = smb_query_fileinfo(sr, node, infolev, qinfo);
	if (rc != 0) {
		kmem_free(qinfo, sizeof (smb_queryinfo_t));
		smb_node_release(node);
		return (rc);
	}

	/* If delete_on_close - NT_STATUS_DELETE_PENDING */
	if (qinfo->qi_delete_on_close) {
		smbsr_error(sr, NT_STATUS_DELETE_PENDING,
		    ERRDOS, ERROR_ACCESS_DENIED);
		kmem_free(qinfo, sizeof (smb_queryinfo_t));
		smb_node_release(node);
		return (-1);
	}

	rc = smb_query_encode_response(sr, xa, infolev, qinfo);
	kmem_free(qinfo, sizeof (smb_queryinfo_t));
	smb_node_release(node);
	return (rc);
}
/*
 * smb_query_by_path
 *
 * Common code for querying file information by file name.
 * Use the file name to identify the node object and request the
 * smb_queryinfo_t data for that node.
 *
 * Querying attributes on a named pipe by name is an error and
 * is handled in the calling functions so that they can return
 * the appropriate error status code (which differs by caller).
 */
static int
smb_query_by_path(smb_request_t *sr, smb_xa_t *xa, uint16_t infolev, char *path)
{
	smb_queryinfo_t	*qinfo;
	smb_node_t	*node, *dnode;
	int		rc;
	int		len;

	/* VALID, but not yet supported */
	if (infolev == SMB_FILE_ACCESS_INFORMATION) {
		smbsr_error(sr, 0, ERRDOS, ERRunknownlevel);
		return (-1);
	}

	/*
	 * Some MS clients pass NULL file names. NT interprets this as "\".
	 * Otherwise, if path is not "\\", remove the terminating slash.
	 */
	if ((len = strlen(path)) == 0)
		path = "\\";
	else {
		if ((len > 1) && (path[len - 1] == '\\')) {
			path[len - 1] = 0;
		}
	}

	qinfo = kmem_alloc(sizeof (smb_queryinfo_t), KM_SLEEP);

	rc = smb_pathname_reduce(sr, sr->user_cr, path, sr->tid_tree->t_snode,
	    sr->tid_tree->t_snode, &dnode, qinfo->qi_name);
	if (rc == 0) {
		rc = smb_fsop_lookup_name(sr, sr->user_cr, SMB_FOLLOW_LINKS,
		    sr->tid_tree->t_snode, dnode, qinfo->qi_name, &node);
		smb_node_release(dnode);
	}

	if (rc != 0) {
		if (rc == ENOENT)
			smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND,
			    ERRDOS, ERROR_FILE_NOT_FOUND);
		else
			smbsr_errno(sr, rc);

		kmem_free(qinfo, sizeof (smb_queryinfo_t));
		return (-1);
	}

	rc = smb_query_fileinfo(sr, node, infolev, qinfo);
	if (rc != 0) {
		kmem_free(qinfo, sizeof (smb_queryinfo_t));
		smb_node_release(node);
		return (rc);
	}

	/* If delete_on_close - NT_STATUS_DELETE_PENDING */
	if (qinfo->qi_delete_on_close) {
		smbsr_error(sr, NT_STATUS_DELETE_PENDING,
		    ERRDOS, ERROR_ACCESS_DENIED);
		kmem_free(qinfo, sizeof (smb_queryinfo_t));
		smb_node_release(node);
		return (-1);
	}

	rc = smb_query_encode_response(sr, xa, infolev, qinfo);
	kmem_free(qinfo, sizeof (smb_queryinfo_t));
	smb_node_release(node);
	return (rc);
}