/*
* 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 2014 Nexenta Systems, Inc. All rights reserved.
*/
#include <smbsrv/smb_kproto.h>
#include <smbsrv/smb_fsops.h>
/*
* SMB_TRANS2_SET_FILE/PATH_INFO (RENAME_INFORMATION level) flag
*/
static int smb_rename_lookup_src(smb_request_t *);
static void smb_rename_release_src(smb_request_t *);
static uint32_t smb_rename_errno2status(int);
/*
* smb_setinfo_rename
*
* Implements SMB_FILE_RENAME_INFORMATION level of Trans2_Set_FileInfo
* and Trans2_Set_PathInfo and SMB2 set_info, FileRenameInformation.
* If the new filename (dst_fqi) already exists it may be overwritten
* if flags == 1.
*
* The passed path is a full path relative to the share root.
*
* Returns NT status codes.
*
* Similar to smb_setinfo_link(), below.
*/
{
/* validate the dst pathname */
return (NT_STATUS_OBJECT_NAME_INVALID);
return (status);
}
/*
* smb_common_rename
*
* Common code for renaming a file.
*
* If the source and destination are identical, we go through all
* the checks but we don't actually do the rename. If the source
* and destination files differ only in case, we do a case-sensitive
* rename. Otherwise, we do a full case-insensitive rename.
*
* Returns NT status values.
*
* Similar to smb_make_link(), below.
*/
{
/* Check if attempting to rename a stream - not yet supported */
if (rc != 0)
return (smb_rename_errno2status(rc));
/*
* The source node may already have been provided,
* Not provided by smb_com_rename, smb_com_nt_rename.
*/
} else {
/* lookup and validate src node */
if (rc != 0)
return (smb_rename_errno2status(rc));
}
/*
* Find the destination dnode and last component.
* May already be provided, i.e. when called via
* SMB1 trans2 setinfo.
*/
/* called via smb_set_rename_info */
} else {
/* called via smb2_setf_rename, smb_com_rename, etc. */
if (rc != 0) {
return (smb_rename_errno2status(rc));
}
}
/* If exact name match in same directory, we're done */
return (0);
}
/* Lookup destination node */
/* If the destination node doesn't already exist, validate new_name. */
if (smb_is_invalid_filename(new_name)) {
return (NT_STATUS_OBJECT_NAME_INVALID);
}
}
/*
* Handle case where changing case of the same directory entry.
*
* If we found the dst node in the same directory as the src node,
* and their names differ only in case:
*
* If the tree is case sensitive (or mixed):
* Do case sensitive lookup to see if exact match exists.
* If the exact match is the same node as src_node we're done.
*
* If the tree is case insensitive:
* There is currently no way to tell if the case is different
* or not, so do the rename (unless the specified new name was
* mangled).
*/
if ((rc == 0) &&
dst_fqi->fq_last_comp, 0) != 0) {
return (0);
}
} else {
if ((rc == 0) &&
return (0);
}
}
}
return (smb_rename_errno2status(rc));
}
/*
* Destination already exists. Do delete checks.
*/
return (NT_STATUS_OBJECT_NAME_COLLISION);
}
/*
* Wait (a little) for the oplock break to be
* responded to by clients closing handles.
* Hold node->n_lock as reader to keep new
* ofiles from showing up after we check.
*/
if (status != NT_STATUS_SHARING_VIOLATION)
break;
}
if (status != NT_STATUS_SUCCESS) {
return (NT_STATUS_ACCESS_DENIED);
}
/*
* Note, the combination of these two:
* smb_node_rdlock(node);
* nbl_start_crit(node->vp, RW_READER);
* is equivalent to this call:
* smb_node_start_crit(node, RW_READER)
*
* Cleanup after this point should use:
* smb_node_end_crit(dst_fnode)
*/
/*
* This checks nbl_share_conflict, nbl_lock_conflict
*/
if (status != NT_STATUS_SUCCESS) {
return (NT_STATUS_ACCESS_DENIED);
}
}
if (rc == 0) {
/*
* Note that renames in the same directory are normally
* delivered in {old,new} pairs, and clients expect them
* in that order, if both events are delivered.
*/
} else {
}
}
}
return (smb_rename_errno2status(rc));
}
/*
* smb_rename_check_stream
*
* For a stream rename the dst path must begin with ':', or "\\:".
* We don't yet support stream rename, Return EACCES.
*
* If not a stream rename, in accordance with the above rule,
* it is not valid for either the src or dst to be a stream.
* Return EINVAL.
*/
static int
{
/* We do not yet support named stream rename - ACCESS DENIED */
if ((dst_path[0] == ':') ||
return (EACCES);
}
/*
* If not stream rename (above) neither src or dst can be
* a named stream.
*/
if (smb_is_stream_name(dst_path))
return (EINVAL);
if (SMB_IS_STREAM(src_fnode))
return (EINVAL);
} else {
if (smb_is_stream_name(src_path))
return (EINVAL);
}
return (0);
}
/*
* smb_setinfo_link
*
* Implements FileRenameInformation for SMB1 Trans2 setinfo, SMB2 setinfo.
* If the new filename (dst_fqi) already exists it may be overwritten
* if flags == 1.
*
* The passed path is a full path relative to the share root.
*
* Returns NT status codes.
*
* Similar to smb_setinfo_rename(), above.
*/
{
/* validate the dst pathname */
return (NT_STATUS_OBJECT_NAME_INVALID);
return (status);
}
/*
* smb_make_link
*
* Creating a hard link (adding an additional name) for a file.
*
* If the source and destination are identical, we go through all
* the checks but we don't create a link.
*
* If the file is a symlink we create the hardlink on the target
* of the symlink (i.e. use SMB_FOLLOW_LINKS when looking up src).
* If the target of the symlink does not exist we fail with ENOENT.
*
* Returns NT status values.
*
* Similar to smb_common_rename() above.
*/
{
char *path;
int rc;
/* Cannnot create link on named stream */
return (NT_STATUS_INVALID_PARAMETER);
}
/* The source node may already have been provided */
} else {
/* lookup and validate src node */
if (rc != 0)
return (smb_rename_errno2status(rc));
}
/* Not valid to create hardlink for directory */
return (NT_STATUS_FILE_IS_A_DIRECTORY);
}
/*
* Find the destination dnode and last component.
* May already be provided, i.e. when called via
* SMB1 trans2 setinfo.
*/
} else {
if (rc != 0) {
return (smb_rename_errno2status(rc));
}
}
/* If CI name match in same directory, we're done */
dst_fqi->fq_last_comp, 0) == 0)) {
return (0);
}
return (NT_STATUS_OBJECT_NAME_INVALID);
}
/* Lookup the destination node. It MUST NOT exist. */
if (rc == 0) {
}
return (smb_rename_errno2status(rc));
}
if (rc == 0) {
}
return (smb_rename_errno2status(rc));
}
/*
* smb_rename_lookup_src
*
* Lookup the src node, checking for sharing violations and
* breaking any existing BATCH oplock.
* Populate sr->arg.dirop.fqi
*
* Upon success, the dnode and fnode will have holds and the
* fnode will be in a critical section. These should be
* released using smb_rename_release_src().
*
* Returns errno values.
*/
static int
{
int rc;
int count;
char *path;
return (EINVAL);
/* Lookup the source node */
if (rc != 0)
return (rc);
if (rc != 0) {
return (rc);
}
if (rc != 0) {
return (rc);
}
/*
* Break BATCH oplock before ofile checks. If a client
* has a file open, this will force a flush or close,
* which may affect the outcome of any share checking.
*/
/*
* Wait (a little) for the oplock break to be
* responded to by clients closing handles.
* Hold node->n_lock as reader to keep new
* ofiles from showing up after we check.
*/
if (status != NT_STATUS_SHARING_VIOLATION)
break;
}
if (status != NT_STATUS_SUCCESS) {
return (EPIPE); /* = ERRbadshare */
}
/*
* Note, the combination of these two:
* smb_node_rdlock(node);
* nbl_start_crit(node->vp, RW_READER);
* is equivalent to this call:
* smb_node_start_crit(node, RW_READER)
*
* Cleanup after this point should use:
* smb_node_end_crit(src_node)
*/
/*
* This checks nbl_share_conflict, nbl_lock_conflict
*/
if (status != NT_STATUS_SUCCESS) {
if (status == NT_STATUS_SHARING_VIOLATION)
return (EPIPE); /* = ERRbadshare */
return (EACCES);
}
/* NB: Caller expects holds on src_fqi fnode, dnode */
return (0);
}
/*
* smb_rename_release_src
*/
static void
{
}
static int
{
return (EACCES);
!(SMB_SEARCH_HIDDEN(sattr)))
return (ESRCH);
!(SMB_SEARCH_SYSTEM(sattr)))
return (ESRCH);
return (0);
}
/*
* The following values are based on observed WFWG, Windows 9x, Windows NT
* and Windows 2000 behaviour.
*
* ERROR_FILE_EXISTS doesn't work for Windows 98 clients.
*
* Windows 95 clients don't see the problem because the target is deleted
* before the rename request.
*/
static uint32_t
{
static struct {
int errnum;
} rc_map[] = {
{ ESRCH, NT_STATUS_NO_SUCH_FILE },
};
int i;
if (errnum == 0)
return (0);
}
}
return (smb_errno2status(errnum));
}