smb_path_name_reduction.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.
*/
#include <smbsrv/smb_incl.h>
#include <smbsrv/smb_fsops.h>
#include <sys/pathname.h>
static char *smb_pathname_catia_v5tov4(smb_request_t *, char *, char *, int);
static char *smb_pathname_catia_v4tov5(smb_request_t *, char *, char *, int);
smb_is_executable(char *path)
{
char extension[5];
(void) utf8_strupr(extension);
return (NODE_FLAGS_EXECUTABLE);
return (NODE_FLAGS_EXECUTABLE);
return (NODE_FLAGS_EXECUTABLE);
return (NODE_FLAGS_EXECUTABLE);
}
return (0);
}
/*
* smbd_fs_query
*
* Upon success, the caller will need to call smb_node_release() on
* fqi.fq_fnode (if it isn't already set to NULL by this routine) and
* and fqi.fq_dnode. These pointers will not be used after the caller
* is done with them and should be released immediately. (The position
* of smb_fqi in a union in the smb_request structure makes it difficult
* to free these pointers at smb_request deallocation time.)
*
* If smbd_fs_query() returns error, no smb_nodes will need to be released
* by callers as a result of references taken in this routine, and
* fqi.fq_fnode and fqi.fq_dnode will be set to NULL.
*/
int
{
int rc;
fqi->fq_last_comp);
if (rc)
return (rc);
if (rc == 0) {
if (fqm == FQM_PATH_MUST_NOT_EXIST) {
return (EEXIST);
}
return (0);
}
if (fqm == FQM_PATH_MUST_EXIST) {
return (rc);
}
return (0);
}
return (rc);
}
/*
* smb_pathname_reduce
*
* smb_pathname_reduce() takes a path and returns the smb_node for the
* second-to-last component of the path. It also returns the name of the last
* component. Pointers for both of these fields must be supplied by the caller.
*
* Upon success, 0 is returned.
*
* Upon error, *dir_node will be set to 0.
*
* *sr (in)
* ---
* smb_request structure pointer
*
* *cred (in)
* -----
* credential
*
* *path (in)
* -----
* pathname to be looked up
*
* *share_root_node (in)
* ----------------
* File operations which are share-relative should pass sr->tid_tree->t_snode.
* If the call is not for a share-relative operation, this parameter must be 0
* (e.g. the call from smbsr_setup_share()). (Such callers will have path
* operations done using root_smb_node.) This parameter is used to determine
* whether mount points can be crossed.
*
* share_root_node should have at least one reference on it. This reference
* will stay intact throughout this routine.
*
* *cur_node (in)
* ---------
* The smb_node for the current directory (for relative paths).
* cur_node should have at least one reference on it.
* This reference will stay intact throughout this routine.
*
* **dir_node (out)
* ----------
* Directory for the penultimate component of the original path.
* (Note that this is not the same as the parent directory of the ultimate
* target in the case of a link.)
*
* The directory smb_node is returned held. The caller will need to release
* the hold or otherwise make sure it will get released (e.g. in a destroy
* routine if made part of a global structure).
*
* last_component (out)
* --------------
* The last component of the path. (This may be different from the name of any
* link target to which the last component may resolve.)
*
*
* ____________________________
*
* The CIFS server lookup path needs to have logic equivalent to that of
* smb_fsop_lookup(), smb_vop_lookup() and other smb_vop_*() routines in the
* following areas:
*
* - non-traversal of child mounts (handled by smb_pathname_reduce)
* - unmangling (handled in smb_pathname)
* - "chroot" behavior of share root (handled by lookuppnvp)
*
* In addition, it needs to replace backslashes with forward slashes. It also
* ensures that link processing is done correctly, and that directory
* information requested by the caller is correctly returned (i.e. for paths
* with a link in the last component, the directory information of the
* link and not the target needs to be returned).
*/
int
const char *path,
char *last_component)
{
char *usepath;
int lookup_flags = FOLLOW;
int trailing_slash = 0;
int err = 0;
int len;
*last_component = '\0';
vss_cur_node = NULL;
return (EACCES);
}
return (EINVAL);
if (*path == '\0')
return (ENOENT);
return (ENAMETOOLONG);
}
if (share_root_node)
else
if (err != 0) {
return (err);
}
}
trailing_slash = 1;
if (vss_cur_node != NULL)
(void) smb_node_release(vss_cur_node);
if (vss_root_node != NULL)
(void) smb_node_release(vss_root_node);
return (err);
}
/*
* If a path does not have a trailing slash, strip off the
* last component. (We only need to return an smb_node for
* the second to last component; a name is returned for the
* last component.)
*/
if (trailing_slash) {
} else {
(void) pn_setlast(&ppn);
}
} else {
}
/*
* Prevent access to anything outside of the share root, except
* when mapping a share because that may require traversal from
* / to a mounted file system. share_root_node is NULL when
* mapping a share.
*
* Note that we disregard whether the traversal of the path went
* outside of the file system and then came back (say via a link).
*/
if ((err == 0) && share_root_node) {
}
if (err) {
if (*dir_node) {
(void) smb_node_release(*dir_node);
}
*last_component = 0;
}
if (vss_cur_node != NULL)
(void) smb_node_release(vss_cur_node);
if (vss_root_node != NULL)
(void) smb_node_release(vss_root_node);
return (err);
}
/*
* smb_pathname()
* wrapper to lookuppnvp(). Handles name unmangling.
*
* *dir_node is the true directory of the target *node.
*
* If any component but the last in the path is not found, ENOTDIR instead of
* ENOENT will be returned.
*
* Path components are processed one at a time so that smb_nodes can be
* created for each component. This allows the dir_snode field in the
* smb_node to be properly populated.
*
* Because of the above, links are also processed in this routine
* (i.e., we do not pass the FOLLOW flag to lookuppnvp()). This
* will allow smb_nodes to be created for each component of a link.
*
* Mangle checking is per component. If a name is mangled, when the
* unmangled name is passed to smb_pathname_lookup() do not pass
* FIGNORECASE, since the unmangled name is the real on-disk name.
* Otherwise pass FIGNORECASE if it's set in flags. This will cause the
* file system to return "first match" in the event of a case collision.
*
* If CATIA character translation is enabled it is applied to each
* component before passing the component to smb_pathname_lookup().
* After smb_pathname_lookup() the reverse translation is applied.
*/
int
{
int err = 0;
int nlink = 0;
int local_flags;
char namebuf[MAXNAMELEN];
return (EINVAL);
if (dir_node)
return (err);
}
if (fnode) {
}
break;
break;
}
break;
if (err) {
if (smb_maybe_mangled_name(component) == 0)
break;
real_name, MAXNAMELEN)) != 0)
break;
break;
}
break;
local_flags = 0;
if (err)
break;
}
if (++nlink > MAXSYMLINKS) {
break;
}
if (err == 0) {
if (pn_pathleft(&link_pn) == 0)
}
if (err)
break;
if (upn.pn_pathlen == 0) {
break;
}
}
if (pn_fixslash(&upn))
} else {
if (flags & FIGNORECASE) {
pn_setlast(&rpn);
} else {
}
break;
}
}
upn.pn_pathlen--;
}
}
if (err) {
if (fnode)
if (dnode)
} else {
if (dir_node)
else
}
return (err);
}
/*
* Holds on dvp and rootvp (if not rootdir) are required by lookuppnvp()
* and will be released within lookuppnvp().
*/
static int
{
int err;
return (err);
}
/*
* CATIA Translation of a pathname component prior to passing it to lookuppnvp
*
* If the translated component name contains a '/' NULL is returned.
* The caller should treat this as error EILSEQ. It is not valid to
* have a directory name with a '/'.
*/
static char *
{
char *namep;
if (SMB_TREE_SUPPORTS_CATIA(sr)) {
return (NULL);
return (namep);
}
return (name);
}
/*
* CATIA translation of a pathname component after returning from lookuppnvp
*/
static char *
{
if (SMB_TREE_SUPPORTS_CATIA(sr)) {
return (namebuf);
}
return (name);
}
/*
* sr - needed to check for case sense
* path - non mangled path needed to be looked up from the startvp
* startvp - the vnode to start the lookup from
* rootvp - the vnode of the root of the filesystem
* returns the vnode found when starting at startvp and using the path
*
* Finds a vnode starting at startvp and parsing the non mangled path
*/
vnode_t *
{
int lookup_flags = FOLLOW;
/* lookuppnvp should release the holds */
return (NULL);
}
}
return (vp);
}