text: /*
   * CDDL HEADER START
   *
   * The contents of this file are subject to the terms of the
   * Common Development and Distribution License (the "License").
   * You may not use this file except in compliance with the License.
   *
   * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   * or http://www.opensolaris.org/os/licensing.
   * See the License for the specific language governing permissions
   * and limitations under the License.
   *
   * When distributing Covered Code, include this CDDL HEADER in each
   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
   * If applicable, add the following below this CDDL HEADER, with the
   * fields enclosed by brackets "[]" replaced with your own identifying
   * information: Portions Copyright [yyyy] [name of copyright owner]
   *
   * CDDL HEADER END
   */
  /*
   * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
   * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
   */
  
  #include <smbsrv/smb_kproto.h>
  #include <smbsrv/smb_fsops.h>
  #include <smbsrv/smb_vops.h>
  
  int smb_open_dsize_check = 0;
  
  /*
   *  Client Request                     Description
   *  ================================== =================================
   *
   *  UCHAR WordCount;                   Count of parameter words = 15
   *  UCHAR AndXCommand;                 Secondary (X) command;  0xFF =
   *                                      none
   *  UCHAR AndXReserved;                Reserved (must be 0)
   *  USHORT AndXOffset;                 Offset to next command WordCount
   *  USHORT Flags;                      Additional information: bit set-
   *                                      0 - return additional info
   *                                      1 - exclusive oplock requested
   *                                      2 - batch oplock requested
   *  USHORT DesiredAccess;              File open mode
   *  USHORT SearchAttributes;
   *  USHORT FileAttributes;
   *  UTIME CreationTime;                Creation timestamp for file if it
   *                                      gets created
   *  USHORT OpenFunction;               Action to take if file exists
   *  ULONG AllocationSize;              Bytes to reserve on create or
   *                                      truncate
   *  ULONG Reserved[2];                 Must be 0
   *  USHORT ByteCount;                  Count of data bytes;    min = 1
   *  UCHAR BufferFormat                 0x04
   *  STRING FileName;
   *
   *  Server Response                    Description
   *  ================================== =================================
   *
   *  UCHAR WordCount;                   Count of parameter words = 15
   *  UCHAR AndXCommand;                 Secondary (X) command;  0xFF =
   *                                      none
   *  UCHAR AndXReserved;                Reserved (must be 0)
   *  USHORT AndXOffset;                 Offset to next command WordCount
   *  USHORT Fid;                        File handle
   *  USHORT FileAttributes;
   *  UTIME LastWriteTime;
   *  ULONG DataSize;                    Current file size
   *  USHORT GrantedAccess;              Access permissions actually
   *                                      allowed
   *  USHORT FileType;                   Type of file opened
   *  USHORT DeviceState;                State of the named pipe
   *  USHORT Action;                     Action taken
   *  ULONG ServerFid;                   Server unique file id
   *  USHORT Reserved;                   Reserved (must be 0)
   *  USHORT ByteCount;                  Count of data bytes = 0
   *
   * DesiredAccess describes the access the client desires for the file (see
   * section 3.6 -  Access Mode Encoding).
   *
   * OpenFunction specifies the action to be taken depending on whether or
   * not the file exists (see section 3.8 -  Open Function Encoding).  Action
   *
   * in the response specifies the action as a result of the Open request
   * (see section 3.9 -  Open Action Encoding).
   *
   * SearchAttributes indicates the attributes that the file must have to be
   * found while searching to see if it exists.  The encoding of this field
   * is described in the "File Attribute Encoding" section elsewhere in this
   * document.  If SearchAttributes is zero then only normal files are
   * returned.  If the system file, hidden or directory attributes are
   * specified then the search is inclusive -- both the specified type(s) of
   * files and normal files are returned.
   *
   * FileType returns the kind of resource actually opened:
   *
   *  Name                       Value  Description
   *  ========================== ====== ==================================
   *
   *  FileTypeDisk               0      Disk file or directory as defined
   *                                     in the attribute field
   *  FileTypeByteModePipe       1      Named pipe in byte mode
   *  FileTypeMessageModePipe    2      Named pipe in message mode
   *  FileTypePrinter            3      Spooled printer
   *  FileTypeUnknown            0xFFFF Unrecognized resource type
   *
   * If bit0 of Flags is clear, the FileAttributes, LastWriteTime, DataSize,
   * FileType, and DeviceState have indeterminate values in the response.
   *
   * This SMB can request an oplock on the opened file.  Oplocks are fully
   * described in the "Oplocks" section elsewhere in this document, and there
   * is also discussion of oplocks in the SMB_COM_LOCKING_ANDX SMB
   * description.  Bit1 and bit2 of the Flags field are used to request
   * oplocks during open.
   *
   * The following SMBs may follow SMB_COM_OPEN_ANDX:
   *
   *    SMB_COM_READ    SMB_COM_READ_ANDX
   *    SMB_COM_IOCTL
   */
  
  /*
   * This message is sent to obtain a file handle for a data file.  This
   * returned Fid is used in subsequent client requests such as read, write,
   * close, etc.
   *
   * Client Request                     Description
   * ================================== =================================
   *
   * UCHAR WordCount;                   Count of parameter words = 2
   * USHORT DesiredAccess;              Mode - read/write/share
   * USHORT SearchAttributes;
   * USHORT ByteCount;                  Count of data bytes;    min = 2
   * UCHAR BufferFormat;                0x04
   * STRING FileName[];                 File name
   *
   * FileName is the fully qualified file name, relative to the root of the
   * share specified in the Tid field of the SMB header.  If Tid in the SMB
   * header refers to a print share, this SMB creates a new file which will
   * be spooled to the printer when closed.  In this case, FileName is
   * ignored.
   *
   * SearchAttributes specifies the type of file desired.  The encoding is
   * described in the "File Attribute Encoding" section.
   *
   * DesiredAccess controls the mode under which the file is opened, and the
   * file will be opened only if the client has the appropriate permissions.
   * The encoding of DesiredAccess is discussed in the section entitled
   * "Access Mode Encoding".
   *
   * Server Response                    Description
   * ================================== =================================
   *
   * UCHAR WordCount;                   Count of parameter words = 7
   * USHORT Fid;                        File handle
   * USHORT FileAttributes;             Attributes of opened file
   * UTIME LastWriteTime;               Time file was last written
   * ULONG DataSize;                    File size
   * USHORT GrantedAccess;              Access allowed
   * USHORT ByteCount;                  Count of data bytes = 0
   *
   * Fid is the handle value which should be used for subsequent file
   * operations.
   *
   * FileAttributes specifies the type of file obtained.  The encoding is
   * described in the "File Attribute Encoding" section.
   *
   * GrantedAccess indicates the access permissions actually allowed, and may
   * have one of the following values:
   *
   *    0  read-only
   *    1  write-only
   *    2 read/write
   *
   * File Handles (Fids) are scoped per client.  A Pid may reference any Fid
   * established by itself or any other Pid on the client (so far as the
   * server is concerned).  The actual accesses allowed through the Fid
   * depends on the open and deny modes specified when the file was opened
   * (see below).
   *
   * The MS-DOS compatibility mode of file open provides exclusion at the
   * client level.  A file open in compatibility mode may be opened (also in
   * compatibility mode) any number of times for any combination of reading
   * and writing (subject to the user's permissions) by any Pid on the same
   * client.  If the first client has the file open for writing, then the
   * file may not be opened in any way by any other client.  If the first
   * client has the file open only for reading, then other clients may open
   * the file, in compatibility mode, for reading..  The above
   * notwithstanding, if the filename has an extension of .EXE, .DLL, .SYM,
   * or .COM other clients are permitted to open the file regardless of
   * read/write open modes of other compatibility mode opens.  However, once
   * multiple clients have the file open for reading, no client is permitted
   * to open the file for writing and no other client may open the file in
   * any mode other than compatibility mode.
   *
   * The other file exclusion modes (Deny read/write, Deny write, Deny read,
   * Deny none) provide exclusion at the file level.  A file opened in any
   * "Deny" mode may be opened again only for the accesses allowed by the
   * Deny mode (subject to the user's permissions).  This is true regardless
   * of the identity of the second opener -a different client, a Pid from the
   * same client, or the Pid that already has the file open.  For example, if
   * a file is open in "Deny write" mode a second open may only obtain read
   * permission to the file.
   *
   * Although Fids are available to all Pids on a client, Pids other than the
   * owner may not have the full access rights specified in the open mode by
   * the Fid's creator.  If the open creating the Fid specified a deny mode,
   * then any Pid using the Fid, other than the creating Pid, will have only
   * those access rights determined by "anding" the open mode rights and the
   * deny mode rights, i.e., the deny mode is checked on all file accesses.
   * For example, if a file is opened for Read/Write in Deny write mode, then
   * other clients may only read the file and cannot write; if a file is
   * opened for Read in Deny read mode, then the other clients can neither
   * read nor write the file.
   */
  
  smb_sdrc_t
  smb_pre_open(smb_request_t *sr)
  {
  	struct open_param *op = &sr->arg.open;
  	int rc;
  
  	bzero(op, sizeof (sr->arg.open));
  
  	rc = smbsr_decode_vwv(sr, "ww", &op->omode, &op->fqi.fq_sattr);
  	if (rc == 0)
  		rc = smbsr_decode_data(sr, "%S", sr, &op->fqi.fq_path.pn_path);
  
  	DTRACE_SMB_START(op__Open, smb_request_t *, sr); /* arg.open */
  
  	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
  }
  
  void
  smb_post_open(smb_request_t *sr)
  {
  	DTRACE_SMB_DONE(op__Open, smb_request_t *, sr);
  }
  
  smb_sdrc_t
  smb_com_open(smb_request_t *sr)
  {
  	struct open_param *op = &sr->arg.open;
  	smb_ofile_t *of;
  	uint32_t mtime_sec;
  	uint32_t status;
  	uint16_t file_attr;
  	int rc;
  
  	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->crtime.tv_sec = op->crtime.tv_nsec = 0;
  	op->create_disposition = FILE_OPEN;
  	op->create_options = FILE_NON_DIRECTORY_FILE;
  	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_open_dsize_check && op->dsize > UINT_MAX) {
  		smbsr_error(sr, 0, ERRDOS, ERRbadaccess);
  		return (SDRC_ERROR);
  	}
  
  	/*
  	 * The real open call.   Note: this gets attributes into
  	 * op->fqi.fq_fattr (SMB_AT_ALL).  We need those below.
  	 */
  	status = smb_common_open(sr);
  	if (status != NT_STATUS_SUCCESS) {
  		smbsr_status(sr, status, 0, 0);
  		return (SDRC_ERROR);
  	}
  	if (op->op_oplock_level != SMB_OPLOCK_NONE) {
  		/* Oplock req. in op->op_oplock_level etc. */
  		smb1_oplock_acquire(sr, B_FALSE);
  	}
  
  	/*
  	 * 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;
  
  	if (op->op_oplock_level == SMB_OPLOCK_NONE) {
  		sr->smb_flg &=
  		    ~(SMB_FLAGS_OPLOCK | SMB_FLAGS_OPLOCK_NOTIFY_ANY);
  	}
  
  	file_attr = op->dattr & FILE_ATTRIBUTE_MASK;
  	mtime_sec = smb_time_gmt_to_local(sr,
  	    op->fqi.fq_fattr.sa_vattr.va_mtime.tv_sec);
  
  	rc = smbsr_encode_result(sr, 7, 0, "bwwllww",
  	    7,
  	    sr->smb_fid,
  	    file_attr,
  	    mtime_sec,
  	    (uint32_t)op->dsize,
  	    op->omode,
  	    (uint16_t)0);	/* bcc */
  
  	if (rc == 0)
  		return (SDRC_SUCCESS);
  
  	smb_ofile_close(of, 0);
  	return (SDRC_ERROR);
  }
  
  int smb_openx_enable_extended_response = 1;
  
  /*
   * smb_pre_open_andx
   * For compatibility with windows servers, the search attributes
   * specified in the request are ignored.
   */
  smb_sdrc_t
  smb_pre_open_andx(smb_request_t *sr)
  {
  	struct open_param *op = &sr->arg.open;
  	uint16_t openx_flags;
  	uint32_t alloc_size;
  	uint32_t creation_time;
  	uint16_t file_attr, sattr;
  	int rc;
  
  	bzero(op, sizeof (sr->arg.open));
  
  	rc = smbsr_decode_vwv(sr, "b.wwwwwlwll4.", &sr->andx_com,
  	    &sr->andx_off, &openx_flags, &op->omode, &sattr,
  	    &file_attr, &creation_time, &op->ofun, &alloc_size, &op->timeo);
  
  	if (rc == 0) {
  		rc = smbsr_decode_data(sr, "%u", sr, &op->fqi.fq_path.pn_path);
  
  		op->dattr = file_attr;
  		op->dsize = alloc_size;
  
  		/*
  		 * The openx_flags use some "extended" flags that
  		 * happen to match some of the NtCreateX flags.
  		 */
  		if (openx_flags & NT_CREATE_FLAG_REQUEST_OPBATCH)
  			op->op_oplock_level = SMB_OPLOCK_BATCH;
  		else if (openx_flags & NT_CREATE_FLAG_REQUEST_OPLOCK)
  			op->op_oplock_level = SMB_OPLOCK_EXCLUSIVE;
  		else
  			op->op_oplock_level = SMB_OPLOCK_NONE;
  		if (openx_flags & NT_CREATE_FLAG_EXTENDED_RESPONSE)
  			op->nt_flags |= NT_CREATE_FLAG_EXTENDED_RESPONSE;
  
  		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->create_disposition = smb_ofun_to_crdisposition(op->ofun);
  	}
  
  	DTRACE_SMB_START(op__OpenX, smb_request_t *, sr); /* arg.open */
  
  	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
  }
  
  void
  smb_post_open_andx(smb_request_t *sr)
  {
  	DTRACE_SMB_DONE(op__OpenX, smb_request_t *, sr);
  }
  
  smb_sdrc_t
  smb_com_open_andx(smb_request_t *sr)
  {
  	struct open_param	*op = &sr->arg.open;
  	smb_attr_t		*ap = &op->fqi.fq_fattr;
  	smb_ofile_t		*of;
  	uint32_t		status;
  	uint32_t		mtime_sec;
  	uint16_t		file_attr;
  	int rc;
  
  	op->desired_access = smb_omode_to_amask(op->omode);
  	op->share_access = smb_denymode_to_sharemode(op->omode,
  	    op->fqi.fq_path.pn_path);
  
  	if (op->create_disposition > FILE_MAXIMUM_DISPOSITION) {
  		smbsr_error(sr, 0, ERRDOS, ERRbadaccess);
  		return (SDRC_ERROR);
  	}
  
  	op->create_options = FILE_NON_DIRECTORY_FILE;
  	if (op->omode & SMB_DA_WRITE_THROUGH)
  		op->create_options |= FILE_WRITE_THROUGH;
  
  	if (smb_open_dsize_check && op->dsize > UINT_MAX) {
  		smbsr_error(sr, 0, ERRDOS, ERRbadaccess);
  		return (SDRC_ERROR);
  	}
  
  	status = smb_common_open(sr);
  	if (status != NT_STATUS_SUCCESS) {
  		smbsr_status(sr, status, 0, 0);
  		return (SDRC_ERROR);
  	}
  	if (op->op_oplock_level != SMB_OPLOCK_NONE) {
  		/* Oplock req. in op->op_oplock_level etc. */
  		smb1_oplock_acquire(sr, B_FALSE);
  	}
  
  	/*
  	 * 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;
  
  	if (op->op_oplock_level != SMB_OPLOCK_NONE)
  		op->action_taken |= SMB_OACT_OPLOCK;
  	else
  		op->action_taken &= ~SMB_OACT_OPLOCK;
  
  	file_attr = op->dattr & FILE_ATTRIBUTE_MASK;
  	mtime_sec = smb_time_gmt_to_local(sr, ap->sa_vattr.va_mtime.tv_sec);
  
  	switch (sr->tid_tree->t_res_type & STYPE_MASK) {
  	case STYPE_DISKTREE:
  	case STYPE_PRINTQ:
  		break;
  
  	case STYPE_IPC:
  		mtime_sec = 0;
  		break;
  
  	default:
  		smbsr_error(sr, NT_STATUS_INVALID_DEVICE_REQUEST,
  		    ERRDOS, ERROR_INVALID_FUNCTION);
  		goto errout;
  	}
  
  	if ((op->nt_flags & NT_CREATE_FLAG_EXTENDED_RESPONSE) != 0 &&
  	    smb_openx_enable_extended_response != 0) {
  		uint32_t MaxAccess = 0;
  		if (of->f_node != NULL) {
  			smb_fsop_eaccess(sr, of->f_cr, of->f_node, &MaxAccess);
  		}
  		MaxAccess |= of->f_granted_access;
  
  		rc = smbsr_encode_result(
  		    sr, 19, 0, "bb.wwwllwwwwl2.llw",
  		    19,		/* word count	   (b) */
  		    sr->andx_com,		/* (b.) */
  		    VAR_BCC,	/* andx offset	   (w) */
  		    sr->smb_fid,		/* (w) */
  		    file_attr,			/* (w) */
  		    mtime_sec,			/* (l) */
  		    (uint32_t)op->dsize,	/* (l) */
  		    op->omode,			/* (w) */
  		    op->ftype,			/* (w) */
  		    op->devstate,		/* (w) */
  		    op->action_taken,		/* (w) */
  		    0,		/* legacy fileid   (l) */
  		    /* reserved			  (2.) */
  		    MaxAccess,			/* (l) */
  		    0,		/* guest access	   (l) */
  		    0);		/* byte count	   (w) */
  
  	} else {
  		rc = smbsr_encode_result(
  		    sr, 15, 0, "bb.wwwllwwwwl2.w",
  		    15,		/* word count	   (b) */
  		    sr->andx_com,		/* (b.) */
  		    VAR_BCC,	/* andx offset	   (w) */
  		    sr->smb_fid,		/* (w) */
  		    file_attr,			/* (w) */
  		    mtime_sec,			/* (l) */
  		    (uint32_t)op->dsize,	/* (l) */
  		    op->omode,			/* (w) */
  		    op->ftype,			/* (w) */
  		    op->devstate,		/* (w) */
  		    op->action_taken,		/* (w) */
  		    0,		/* legacy fileid   (l) */
  		    /* reserved			  (2.) */
  		    0);		/* byte count	   (w) */
  	}
  
  	if (rc == 0)
  		return (SDRC_SUCCESS);
  
  errout:
  	smb_ofile_close(of, 0);
  	return (SDRC_ERROR);
  }
  
  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;
  	uint32_t	ea_list_size;
  	uint16_t	flags;
  	uint16_t	file_attr;
  	uint32_t	status;
  	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);
  
  	/*
  	 * The data part of this transaction may contain an EA list.
  	 * See: SMB_FEA_LIST ExtendedAttributeList
  	 *
  	 * If we find a non-empty EA list payload, return the special
  	 * error that tells the caller this FS does not suport EAs.
  	 *
  	 * Note: the first word is the size of the whole data segment,
  	 * INCLUDING the size of that length word.  That means if
  	 * the length word specifies a size less than four, it's
  	 * invalid (and probably a client trying something fishy).
  	 */
  	rc = smb_mbc_decodef(&xa->req_data_mb, "l", &ea_list_size);
  	if (rc == 0 && ea_list_size > 4) {
  		smbsr_status(sr, NT_STATUS_EAS_NOT_SUPPORTED, 0, 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;
  	}
  
  	status = smb_common_open(sr);
  	if (status != NT_STATUS_SUCCESS) {
  		smbsr_status(sr, status, 0, 0);
  		return (SDRC_ERROR);
  	}
  	if (op->op_oplock_level != SMB_OPLOCK_NONE) {
  		/* Oplock req. in op->op_oplock_level etc. */
  		smb1_oplock_acquire(sr, B_FALSE);
  	}
  
  	if (op->op_oplock_level != SMB_OPLOCK_NONE)
  		op->action_taken |= SMB_OACT_OPLOCK;
  	else
  		op->action_taken &= ~SMB_OACT_OPLOCK;
  
  	file_attr = op->dattr & FILE_ATTRIBUTE_MASK;
  
  	if (STYPE_ISIPC(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);
  }
  