smb_common_open.c revision eb1d736b1c19f6abeee90c921a9320b67fedd016
/*
* 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
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* This module provides the common open functionality to the various
* open and create SMB interface functions.
*/
#include <smbsrv/smb_incl.h>
#include <smbsrv/smb_fsops.h>
#include <smbsrv/ntstatus.h>
extern uint32_t smb_is_executable(char *);
static void smb_delete_new_object(smb_request_t *);
static char *smb_pathname_strdup(smb_request_t *, const char *);
static char *smb_pathname_strcat(char *, const char *);
/*
* smb_access_generic_to_file
*
* Search MSDN for IoCreateFile to see following mapping.
*
* GENERIC_READ STANDARD_RIGHTS_READ, FILE_READ_DATA,
* FILE_READ_ATTRIBUTES and FILE_READ_EA
*
* GENERIC_WRITE STANDARD_RIGHTS_WRITE, FILE_WRITE_DATA,
* FILE_WRITE_ATTRIBUTES, FILE_WRITE_EA, and FILE_APPEND_DATA
*
* GENERIC_EXECUTE STANDARD_RIGHTS_EXECUTE, SYNCHRONIZE, and FILE_EXECUTE.
*/
{
if (desired_access & GENERIC_ALL)
return (FILE_ALL_ACCESS & ~SYNCHRONIZE);
if (desired_access & GENERIC_EXECUTE) {
}
if (desired_access & GENERIC_WRITE) {
}
if (desired_access & GENERIC_READ) {
}
return (access | desired_access);
}
/*
* smb_omode_to_amask
*
* This function converts open modes used by Open and Open AndX
* commands to desired access bits used by NT Create AndX command.
*/
{
switch (desired_access & SMB_DA_ACCESS_MASK) {
case SMB_DA_ACCESS_READ:
return (FILE_GENERIC_READ);
case SMB_DA_ACCESS_WRITE:
return (FILE_GENERIC_WRITE);
case SMB_DA_ACCESS_READ_WRITE:
return (FILE_GENERIC_READ | FILE_GENERIC_WRITE);
case SMB_DA_ACCESS_EXECUTE:
return (FILE_GENERIC_EXECUTE);
default:
return (FILE_GENERIC_ALL);
}
}
/*
* smb_denymode_to_sharemode
*
* This function converts deny modes used by Open and Open AndX
* commands to share access bits used by NT Create AndX command.
*/
{
switch (desired_access & SMB_DA_SHARE_MASK) {
if (smb_is_executable(fname))
return (FILE_SHARE_READ | FILE_SHARE_WRITE);
return (FILE_SHARE_ALL);
case SMB_DA_SHARE_EXCLUSIVE:
return (FILE_SHARE_NONE);
case SMB_DA_SHARE_DENY_WRITE:
return (FILE_SHARE_READ);
case SMB_DA_SHARE_DENY_READ:
return (FILE_SHARE_WRITE);
case SMB_DA_SHARE_DENY_NONE:
default:
return (FILE_SHARE_READ | FILE_SHARE_WRITE);
}
}
/*
* smb_ofun_to_crdisposition
*
* This function converts open function values used by Open and Open AndX
* commands to create disposition values used by NT Create AndX command.
*/
{
{
{ -1, FILE_CREATE },
{ FILE_OPEN, FILE_OPEN_IF },
};
if (row == 3)
return (FILE_MAXIMUM_DISPOSITION + 1);
}
/*
* Retry opens to avoid spurious sharing violations, due to timing
* issues between closes and opens. The client that already has the
* file open may be in the process of closing it.
*/
{
int count;
if (count)
if (status != NT_STATUS_SHARING_VIOLATION)
break;
}
if (status == NT_STATUS_SHARING_VIOLATION) {
}
if (status == NT_STATUS_NO_SUCH_FILE) {
}
return (status);
}
/*
* smb_open_subr
*
* Notes on write-through behaviour. It looks like pre-LM0.12 versions
* of the protocol specify the write-through mode when a file is opened,
* (SmbOpen, SmbOpenAndX) so the write calls (SmbWrite, SmbWriteAndClose,
* SmbWriteAndUnlock) don't need to contain a write-through flag.
*
* With LM0.12, the open calls (SmbCreateAndX, SmbNtTransactCreate)
* don't indicate which write-through mode to use. Instead the write
* calls (SmbWriteAndX, SmbWriteRaw) specify the mode on a per call
* basis.
*
* We don't care which open call was used to get us here, we just need
* to ensure that the write-through mode flag is copied from the open
* parameters to the node. We test the omode write-through flag in all
* write functions.
*
* This function will return NT status codes but it also raises errors,
* in which case it won't return to the caller. Be careful how you
* handle things in here.
*
* The following rules apply when processing a file open request:
*
* - Oplocks must be broken prior to share checking to prevent open
* starvation due to batch oplocks. Checking share reservations first
* could potentially result in unnecessary open failures due to
*
* - Share checks must take place prior to access checks for correct
* Windows semantics and to prevent unnecessary NFS delegation recalls.
*
* - Oplocks must be acquired after open to ensure the correct
* synchronization with NFS delegation and FEM installation.
*
*
* DOS readonly bit rules
*
* using the original create fid, even though the file will appear as readonly
* to all other fids and via a CIFS getattr call.
*
* readonly will be successful regardless of whether a creator of a readonly
* file has an open fid (and has the special privilege mentioned in #1,
* above). I.e., the creator of a readonly fid holding that fid will no longer
* have a special privilege.
*
* 3. The DOS readonly bit affects only data and some metadata.
* The following metadata can be changed regardless of the readonly bit:
* - security descriptors
* - DOS attributes
* - timestamps
*
* In the current implementation, the file size cannot be changed (except for
* the exceptions in #1 and #2, above).
*
*
* DOS attribute rules
*
* These rules are specific to creating / opening files and directories.
* How the attribute value (specifically ZERO or FILE_ATTRIBUTE_NORMAL)
* should be interpreted may differ in other requests.
*
* - An attribute value equal to ZERO or FILE_ATTRIBUTE_NORMAL means that the
* file's attributes should be cleared.
* - If FILE_ATTRIBUTE_NORMAL is specified with any other attributes,
* FILE_ATTRIBUTE_NORMAL is ignored.
*
* 1. Creating a new file
* - The request attributes + FILE_ATTRIBUTE_ARCHIVE are applied to the file.
*
* 2. Creating a new directory
* - The request attributes + FILE_ATTRIBUTE_DIRECTORY are applied to the file.
* - FILE_ATTRIBUTE_ARCHIVE does not get set.
*
* 3. Overwriting an existing file
* - the request attributes are used as search attributes. If the existing
* file does not meet the search criteria access is denied.
* - otherwise, applies attributes + FILE_ATTRIBUTE_ARCHIVE.
*
* 4. Opening an existing file or directory
* The request attributes are ignored.
*/
static uint32_t
{
int rc;
int pathlen;
int max_requested = 0;
int is_dir;
int lookup_flags = SMB_FOLLOW_LINKS;
if (is_dir) {
/*
* The object being created or opened is a directory,
* and the Disposition parameter must be one of
* FILE_CREATE, FILE_OPEN, or FILE_OPEN_IF
*/
return (NT_STATUS_INVALID_PARAMETER);
}
}
max_requested = 1;
}
return (NT_STATUS_TOO_MANY_OPENED_FILES);
}
/* This must be NULL at this point */
case STYPE_DISKTREE:
break;
case STYPE_IPC:
/*
* No further processing for IPC, we need to either
* raise an exception or return success here.
*/
return (status);
default:
return (NT_STATUS_BAD_DEVICE_TYPE);
}
return (NT_STATUS_NAME_TOO_LONG);
}
/*
* Some clients pass null file names; NT interprets this as "\".
*/
if (pathlen == 0) {
pathlen = 1;
}
if (is_dir)
else
if (status != NT_STATUS_SUCCESS) {
return (status);
}
/*
* if no path or filename are specified the stream should be
* created on cur_node
*/
/* can't currently create a stream on the tree root */
return (NT_STATUS_ACCESS_DENIED);
}
} else {
}
}
/*
* If the access mask has only DELETE set (ignore
* FILE_READ_ATTRIBUTES), then assume that this
* is a request to delete the link (if a link)
* and do not follow links. Otherwise, follow
* the link to the target.
*/
if (rc == 0) {
rc = 0;
} else {
}
/*
* The uniq_fid is a CIFS-server-wide unique identifier for an ofile
* which is used to uniquely identify open instances for the
* VFS share reservation and POSIX locks.
*/
uniq_fid = SMB_UNIQ_FID();
if (last_comp_found) {
return (NT_STATUS_ACCESS_DENIED);
}
/*
* Reject this request if either:
* - the target IS a directory and the client requires that
* it must NOT be (required by Lotus Notes)
* - the target is NOT a directory and client requires that
* it MUST be.
*/
return (NT_STATUS_FILE_IS_A_DIRECTORY);
}
} else {
return (NT_STATUS_NOT_A_DIRECTORY);
}
}
/*
* No more open should be accepted when "Delete on close"
* flag is set.
*/
return (NT_STATUS_DELETE_PENDING);
}
/*
* Specified file already exists so the operation should fail.
*/
return (NT_STATUS_OBJECT_NAME_COLLISION);
}
/*
* Windows seems to check read-only access before file
* sharing check.
*
* Check to see if the file is currently readonly (irrespective
* of whether this open will make it readonly).
*/
/* Files data only */
FILE_APPEND_DATA)) {
return (NT_STATUS_ACCESS_DENIED);
}
}
}
if ((!(op->desired_access &
FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA))) ||
return (NT_STATUS_ACCESS_DENIED);
}
}
if (status == NT_STATUS_SHARING_VIOLATION) {
return (status);
}
op->desired_access);
if (status != NT_STATUS_SUCCESS) {
if (status == NT_STATUS_PRIVILEGE_NOT_HELD) {
return (status);
} else {
return (NT_STATUS_ACCESS_DENIED);
}
}
switch (op->create_disposition) {
case FILE_SUPERSEDE:
case FILE_OVERWRITE_IF:
case FILE_OVERWRITE:
return (NT_STATUS_ACCESS_DENIED);
}
if (rc) {
uniq_fid);
}
}
}
/*
* If file is being replaced,
* we should remove existing streams
*/
if (SMB_IS_STREAM(node) == 0) {
node) != 0) {
uniq_fid);
}
}
break;
default:
/*
* FILE_OPEN or FILE_OPEN_IF.
*/
break;
}
} else {
/* Last component was not found. */
if (is_dir == 0)
return (NT_STATUS_OBJECT_NAME_NOT_FOUND);
}
return (NT_STATUS_OBJECT_NAME_INVALID);
}
/*
* lock the parent dir node in case another create
* request to the same parent directory comes in.
*/
/*
* A file created with the readonly bit should not
* stop the creator writing to the file until it is
* closed. Although the readonly bit will not be set
* on the file until it is closed, it will be accounted
* for on other fids and on queries based on the node
* state.
*/
}
if (is_dir == 0) {
}
if (rc != 0) {
}
if (status == NT_STATUS_SHARING_VIOLATION) {
if (created)
return (status);
}
} else {
if (rc != 0) {
}
}
}
}
if (max_requested) {
}
/*
* if last_write time was in request and is not 0 or -1,
* use it as file's mtime
*/
}
if (created)
if (created)
}
smb_ofile_close(of, 0);
if (created)
if (created)
return (NT_STATUS_UNSUCCESSFUL);
}
/*
* Propagate the write-through mode from the open params
* to the node: see the notes in the function header.
*/
/*
* Set up the file type in open_param for the response
*/
if (created)
} else { /* VDIR or VLNK */
}
return (NT_STATUS_SUCCESS);
}
/*
* smb_validate_object_name
*
* Very basic file name validation.
* For filenames, we check for names of the form "AAAn:". Names that
* contain three characters, a single digit and a colon (:) are reserved
* as DOS device names, i.e. "COM1:".
* Stream name validation is handed off to smb_validate_stream_name
*
* Returns NT status codes.
*/
{
return (NT_STATUS_OBJECT_NAME_INVALID);
}
return (smb_validate_stream_name(pn));
return (NT_STATUS_SUCCESS);
}
/*
* This function is used to delete a newly created object (file or
* directory) if an error occurs after creation of the object.
*/
static void
{
flags |= SMB_IGNORE_CASE;
if (SMB_TREE_SUPPORTS_CATIA(sr))
else
}
/*
* smb_pathname_setup
*
* Elements of the smb_pathname_t structure are allocated using
* smbsr_malloc and will thus be free'd when the sr is destroyed.
*
* Eliminate duplicate slashes in pn->pn_path.
* Populate pn structure elements with the individual elements
* of pn->pn_path. pn->pn_sname will contain the whole stream name
* including the stream type and preceding colon: :sname:%DATA
* pn_stype will point to the stream type within pn_sname.
*
* If any element is missing the pointer in pn will be NULL.
*/
void
{
int len;
if (fname) {
else {
*fname = '\0';
*fname = '\\';
}
++fname;
} else {
}
if (!smb_is_stream_name(fname)) {
return;
}
/* sname can't be NULL smb_is_stream_name checks this */
else {
*sname = '\0';
*sname = ':';
}
} else {
}
}
/*
* smb_pathname_strdup
*
* Duplicate NULL terminated string s.
* The new string buffer is allocated using smbsr_malloc and
* will thus be free'd when the sr is destroyed.
*/
static char *
{
char *s2;
size_t n;
n = strlen(s) + 1;
return (s2);
}
/*
* smb_pathname_strcat
*
* Reallocate NULL terminated string s1 to accommodate
* concatenating NULL terminated string s2.
* Append s2 and return resulting NULL terminated string.
*
* The string buffer is reallocated using smbsr_realloc
* and will thus be free'd when the sr is destroyed.
*/
static char *
{
size_t n;
return (s1);
}