smb_vops.c revision 62ab33e74bc127994db89903ba3f8fe82b68eec7
/*
* 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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/pathname.h>
#include <sys/extdirent.h>
#include <smbsrv/smb_vops.h>
#include <smbsrv/smbtrans.h>
#include <smbsrv/smb_fsops.h>
#include <smbsrv/smb_kproto.h>
#include <smbsrv/smb_incl.h>
void
static int
static int
static int
extern int
static void
extern sysid_t lm_alloc_sysidt();
#define SMB_AT_MAX 16
0,
};
/*
* The smb_ct will be used primarily for range locking.
* Since the CIFS server is mapping its locks to POSIX locks,
* only one pid is used for operations originating from the
* CIFS server (to represent CIFS in the VOP_FRLOCK routines).
*/
/*
* smb_vop_start()
*
* Initialize the smb caller context. This function must be called
* before any other smb_vop calls.
*/
void
smb_vop_start(void)
{
if (!initialized) {
}
}
int
{
}
int
{
}
/*
* The smb_vop_* functions have minimal knowledge of CIFS semantics and
* serve as an interface to the VFS layer.
*
* Only smb_fsop_* layer functions should call smb_vop_* layer functions.
* (Higher-level CIFS service code should never skip the smb_fsop_* layer
* to call smb_vop_* layer functions directly.)
*/
/*
* XXX - Extended attributes support in the file system assumed.
* This is needed for full NT Streams functionality.
*/
int
{
int error;
return (error);
}
int
{
int error;
int ioflag = 0;
if (*flag == FSSTAB_FILE_SYNC)
return (error);
}
/*
* smb_vop_getattr()
*
* smb_fsop_getattr()/smb_vop_getattr() should always be called from the CIFS
* service (instead of calling VOP_GETATTR directly) to retrieve attributes
* due to special processing needed for streams files.
*
* All attributes are retrieved.
*
* A named stream's attributes (as far as CIFS is concerned) are those of the
* unnamed (i.e. data) stream (minus the size attribute), and the size of the
* named stream. Though the file system may store attributes other than size
* with the named stream, these should not be used by CIFS for any purpose.
*
* When vp denotes a named stream, then unnamed_vp should be passed in (denoting
* the corresponding unnamed stream).
*/
int
{
int error;
if (unnamed_vp)
use_vp = unnamed_vp;
else
return (error);
/*
* Copy special attributes to ret_attr parameter
*/
ret_attr->sa_dosattr = 0;
if (xoap->xoa_readonly)
}
if (xoap->xoa_hidden)
}
if (xoap->xoa_system)
}
if (xoap->xoa_archive)
}
/*
* Retrieve stream size attribute into temporary
* structure, in case the underlying file system
* returns attributes other than the size (we do not
* want to have ret_attr's other fields get
* overwritten).
*
* Note that vp is used here, and not use_vp.
* Also, only AT_SIZE is needed.
*/
return (error);
}
}
return (error);
}
/*
* Support for file systems without VFSFT_XVATTR
*/
if (error != 0)
return (error);
/*
* "Fake" DOS attributes and create time, filesystem doesn't support
* them.
*/
ret_attr->sa_dosattr = 0;
/*
* Retrieve stream size attribute into temporary structure,
* in case the underlying file system returns attributes
* other than the size (we do not want to have ret_attr's
* other fields get overwritten).
*
* Note that vp is used here, and not use_vp.
* Also, only AT_SIZE is needed.
*/
if (error != 0)
return (error);
}
}
return (error);
}
/*
* smb_vop_setattr()
*
* smb_fsop_setattr()/smb_vop_setattr() should always be called from the CIFS
* service to set attributes due to special processing for streams files.
*
* When smb_vop_setattr() is called on a named stream file, all indicated
* attributes except the size are set on the unnamed stream file. The size
* (if indicated) is set on the named stream file.
*/
int
{
int error = 0;
int at_size = 0;
if (unnamed_vp) {
use_vp = unnamed_vp;
at_size = 1;
}
} else {
}
/*
* The caller should not be setting sa_vattr.va_mask,
* but rather sa_mask.
*/
} else {
}
return (error);
/*
* If the size of the stream needs to be set, set it on
* the stream file directly. (All other indicated attributes
* are set on the stream's unnamed stream, above.)
*/
if (at_size) {
/*
* set_attr->sa_vattr.va_size already contains the
* size as set by the caller
*
* Note that vp is used here, and not use_vp.
* Also, only AT_SIZE is needed.
*/
&smb_ct);
}
return (error);
}
/*
* smb_vop_access
*
* This is a wrapper round VOP_ACCESS. VOP_ACCESS checks the given mode
* against file's ACL or Unix permissions. CIFS on the other hand needs to
* know if the requested operation can succeed for the given object, this
* requires more checks in case of DELETE bit since permissions on the parent
* directory are important as well. Based on Windows rules if parent's ACL
* grant FILE_DELETE_CHILD a file can be delete regardless of the file's
* permissions.
*/
int
{
int error = 0;
if (mode == 0)
return (0);
if (dir_vp) {
if (error == 0)
mode &= ~ACE_DELETE;
}
}
if (mode) {
}
return (error);
}
/*
* smb_vop_lookup
*
* dvp: directory vnode (in)
* name: name of file to be looked up (in)
* vpp: looked-up vnode (out)
* od_name: on-disk name of file (out).
* This parameter is optional. If a pointer is passed in, it
* must be allocated with MAXNAMELEN bytes
* rootvp: vnode of the tree root (in)
* This parameter is always passed in non-NULL except at the time
* of share set up.
*/
int
{
int error = 0;
int option_flags = 0;
if (*name == '\0')
return (EINVAL);
return (0);
}
/*
* Set dvp and check for races with forced unmount
* (see lookuppnvp())
*/
return (EIO);
}
}
}
if (flags & SMB_IGNORE_CASE)
if (option_flags == FIGNORECASE)
else
}
return (error);
}
int
{
int error;
int option_flags = 0;
if (flags & SMB_IGNORE_CASE)
} else {
}
return (error);
}
int
{
int error;
int option_flags = 0;
if (flags & SMB_IGNORE_CASE)
return (error);
}
/*
* smb_vop_rename()
*
* The rename is for files in the same tree (identical TID) only.
*/
int
{
int error;
int option_flags = 0;
if (flags & SMB_IGNORE_CASE)
&smb_ct, option_flags);
return (error);
}
int
{
int error;
int option_flags = 0;
if (flags & SMB_IGNORE_CASE)
option_flags, vsap);
return (error);
}
/*
* smb_vop_rmdir()
*
* Only simple rmdir supported, consistent with NT semantics
* (can only remove an empty directory).
*
*/
int
{
int error;
int option_flags = 0;
if (flags & SMB_IGNORE_CASE)
/*
* Comments adapted from rfs_rmdir().
*
* VOP_RMDIR now takes a new third argument (the current
* directory of the process). That's because rmdir
* wants to return EINVAL if one tries to remove ".".
* Of course, SMB servers do not know what their
* clients' current directories are. We fake it by
* supplying a vnode known to exist and illegal to
* remove.
*/
return (error);
}
int
{
}
void
{
/*
* Initialize xvattr, including bzero
*/
/*
* Copy caller-specified classic attributes to xvattr.
* First save xvattr's mask (set in xva_init()), which
* contains AT_XVATTR. This is |'d in later if needed.
*/
/*
* Do not set ctime (only the file system can do it)
*/
/*
* "|" in the original xva_mask, which contains
* AT_XVATTR
*/
/*
* smb_attr->sa_dosattr: If a given bit is not set,
* that indicates that the corresponding field needs
* to be updated with a "0" value. This is done
* implicitly as the xoap->xoa_* fields were bzero'd.
*/
}
/*
* "|" in the original xva_mask, which contains
* AT_XVATTR
*/
}
}
/*
* smb_vop_readdir()
*
* Upon return, the "name" field will contain either the on-disk name or, if
* it needs mangling or has a case-insensitive collision, the mangled
* "shortname."
*
* vpp is an optional parameter. If non-NULL, it will contain a pointer to
* the vnode for the name that is looked up (the vnode will be returned held).
*
* od_name is an optional parameter (NULL can be passed if the on-disk name
* is not needed by the caller).
*/
int
{
int num_bytes;
int error = 0;
*namelen = 0;
return (ENOTDIR);
}
if (vpp)
/*
* The goal is to retrieve the first valid entry from *cookiep
* forward. smb_vop_readdir_readpage() collects an
* SMB_MINLEN_RDDIR_BUF-size "page" of directory entry information.
* smb_vop_readdir_entry() attempts to find the first valid entry
* in that page.
*/
if (num_bytes <= 0)
break;
name[0] = '\0';
if (error)
break;
if (*name)
break;
}
if (error) {
*namelen = 0;
return (error);
}
if (num_bytes == 0) { /* EOF */
*namelen = 0;
return (0);
}
return (0);
}
/*
* smb_vop_readdir_readpage()
*
* Collects an SMB_MINLEN_RDDIR_BUF "page" of directory entries. (The
* directory entries are returned in an fs-independent format by the
* underlying file system. That is, the "page" of information returned is
* not literally stored on-disk in the format returned.)
*
* Much of the following is borrowed from getdents64()
*
* MAXGETDENTS_SIZE is defined in getdents.c
*/
static int
{
int error = 0;
int rdirent_flags = 0;
int sink;
return (ENOTDIR);
/* entflags not working for streams so don't try to use them */
if (!(flags & SMB_STREAM_RDDIR) &&
/*
* Setting V_RDDIR_ENTFLAGS will cause the buffer to
* be filled with edirent_t structures (instead of
* dirent64_t structures).
*/
return (EINVAL);
} else {
if (*count < sizeof (dirent64_t))
return (EINVAL);
}
if (*count > MAXGETDENTS_SIZE)
if (error) {
/* Fake EOF if offset is bad due to dropping of lock */
*count = 0;
return (0);
} else {
return (error);
}
}
/*
* Windows cannot handle an offset > SMB_EOF.
* Pretend we are at EOF.
*/
*count = 0;
return (0);
}
return (0);
}
/*
* smb_vop_readdir_entry()
*
* This function retrieves the first valid entry from the
* SMB_MINLEN_RDDIR_BUF-sized buffer returned by smb_vop_readdir_readpage()
* to smb_vop_readdir().
*
* Both dirent64_t and edirent_t structures need to be handled. The former is
* needed for file systems that do not support VFSFT_DIRENTFLAGS. The latter
* is required for proper handling of case collisions on file systems that
* support case-insensitivity. edirent_t structures are also used for
* case-sensitive file systems if VFSFT_DIRENTFLAGS is supported.
*/
static int
{
int ebufsize;
int error = 0;
int len;
int rc;
char shortname[MANGLE_NAMELEN];
char name83[MANGLE_NAMELEN];
/*
* Use edirent_t structure for both
* entflags not working for streams so don't try to use them
*/
if (!(flags & SMB_STREAM_RDDIR) &&
/*LINTED E_BAD_PTR_CAST_ALIGN*/
} else {
/*LINTED E_BAD_PTR_CAST_ALIGN*/
/*LINTED E_BAD_PTR_CAST_ALIGN*/
}
while (edp) {
if (dp)
*cookiep = next_cookie;
if (dp) {
/*LINTED E_BAD_PTR_CAST_ALIGN*/
} else {
/*LINTED E_BAD_PTR_CAST_ALIGN*/
}
continue;
}
*namelen = 0;
if (ebuf)
return (EOVERFLOW);
}
/*
* Do not pass SMB_IGNORE_CASE to smb_vop_lookup
*/
if (error) {
if (dp) {
/*LINTED E_BAD_PTR_CAST_ALIGN*/
} else {
/*LINTED E_BAD_PTR_CAST_ALIGN*/
}
continue;
}
*namelen = 0;
if (ebuf)
return (error);
}
} else {
*namelen + 1);
}
} else {
}
if (inop)
break;
}
if (ebuf)
return (error);
}
/*
* smb_sa_to_va_mask
*
* Set va_mask by running through the SMB_AT_* #define's and
* setting those bits that correspond to the SMB_AT_* bits
* set in sa_mask.
*/
void
{
int i;
if (smask & 1)
*(va_maskp) |= smb_attrmap[i];
smask >>= 1;
}
}
/*
* smb_vop_getdents()
*
* Upon success, the smb_node corresponding to each entry returned will
* have a reference taken on it. These will be released in
* smb_trans2_find_get_dents().
*
* If an error is returned from this routine, a list of already processed
* entries will be returned. The smb_nodes corresponding to these entries
* will be referenced, and will be released in smb_trans2_find_get_dents().
*
* The returned dp->d_name field will contain either the on-disk name or, if
* it needs mangling or has a case-insensitive collision, the mangled
* "shortname." In this case, the on-disk name can be retrieved from the
* smb_node's od_name (the smb_node is passed to smb_gather_dents_info()).
*/
int /*ARGSUSED*/
char *arg,
char *pattern,
{
int error = 0;
int maxentries;
int num_bytes;
int resid;
/*LINTED E_BAD_PTR_CAST_ALIGN*/
while (maxentries) {
break;
pattern);
if (error)
goto out;
}
if (num_bytes < 0) {
error = -1;
} else if (num_bytes == 0) {
error = 0;
} else {
error = 0;
}
out:
if (dirbuf)
return (error);
}
/*
* smb_vop_getdents_entries()
*
* This function retrieves names from the SMB_MINLEN_RDDIR_BUF-sized buffer
* returned by smb_vop_readdir_readpage() to smb_vop_getdents().
*
* Both dirent64_t and edirent_t structures need to be handled. The former is
* needed for file systems that do not support VFSFT_DIRENTFLAGS. The latter
* is required for properly handling case collisions on file systems that
* support case-insensitivity. edirent_t is also used on case-sensitive
* file systems where VFSFT_DIRENTFLAGS is available.
*/
static int
char *arg,
struct smb_request *sr,
char *dirbuf,
int *maxentries,
int num_bytes,
char *pattern)
{
int ebufsize;
char *tmp_name;
int error;
int rc;
char shortname[MANGLE_NAMELEN];
char name83[MANGLE_NAMELEN];
/*LINTED E_BAD_PTR_CAST_ALIGN*/
} else {
/*LINTED E_BAD_PTR_CAST_ALIGN*/
/*LINTED E_BAD_PTR_CAST_ALIGN*/
}
while (edp) {
if (dp)
if (*maxentries == 0)
break;
*cookiep = next_cookie;
if (dp) {
/*LINTED E_BAD_PTR_CAST_ALIGN*/
} else {
/*LINTED E_BAD_PTR_CAST_ALIGN*/
}
continue;
}
if (error) {
*cookiep = next_cookie;
if (dp) {
/*LINTED E_BAD_PTR_CAST_ALIGN*/
} else {
/*LINTED E_BAD_PTR_CAST_ALIGN*/
}
continue;
}
if (ebuf)
return (error);
}
if (ebuf)
return (ENOMEM);
}
if ((flags & SMB_IGNORE_CASE) &&
ED_CASE_CONFLICTS(edp)) {
if (rc == 1)
} else {
}
if (rc != 1) {
}
if (error > 0) {
if (ebuf)
return (error);
}
/*
* Treat errors from smb_gather_dents_info() that are
* < 0 the same as EOF.
*/
if (error < 0) {
if (ebuf)
*maxentries = 0;
return (0);
}
(*maxentries)--;
} else {
}
*cookiep = next_cookie;
if (dp) {
/*LINTED E_BAD_PTR_CAST_ALIGN*/
} else {
/*LINTED E_BAD_PTR_CAST_ALIGN*/
}
}
if (ebuf)
return (0);
}
/*
* smb_vop_stream_lookup()
*
* The name returned in od_name is the on-disk name of the stream with the
* SMB_STREAM_PREFIX stripped off. od_name should be allocated to MAXNAMELEN
* by the caller.
*/
int
{
char *solaris_stream_name;
char *name;
int error;
return (error);
/*
* Prepend SMB_STREAM_PREFIX to stream name
*/
/*
* "name" will hold the on-disk name returned from smb_vop_lookup
* for the stream, including the SMB_STREAM_PREFIX.
*/
} else {
}
return (error);
}
int
{
char *solaris_stream_name;
int error;
return (error);
/*
* Prepend SMB_STREAM_PREFIX to stream name
*/
return (error);
}
int
{
char *solaris_stream_name;
int error;
!= 0)
return (error);
/*
* Prepend SMB_STREAM_PREFIX to stream name
*/
/* XXX might have to use kcred */
return (error);
}
/*
* smb_vop_stream_readdir()
*
* Note: stream_info.size is not filled in in this routine.
* It needs to be filled in by the caller due to the parameters for getattr.
*
* stream_info.name is set to the on-disk stream name with the SMB_STREAM_PREFIX
* removed.
*/
int
{
int error = 0;
char *tmp_name;
cr)) != 0)
return (error);
stream_info->size = 0;
for (;;) {
break;
continue;
}
sizeof (stream_info->name));
break;
}
if (vpp)
else
if (xattrdirvpp)
else
} else {
}
return (error);
}
int
{
int error;
return (error);
}
/*
* smb_vop_traverse_check()
*
* This function checks to see if the passed-in vnode has a file system
* mounted on it. If it does, the mount point is "traversed" and the
* vnode for the root of the file system is returned.
*/
int
{
int error;
if (vn_mountedvfs(*vpp) == 0)
return (0);
/*
* traverse() may return a different held vnode, even in the error case.
* If it returns a different vnode, it will have released the original.
*/
return (error);
}
int /*ARGSUSED*/
{
int error;
return (error);
}
/*
* smb_vop_acl_read
*
* Reads the ACL of the specified file into 'aclp'.
* acl_type is the type of ACL which the filesystem supports.
*
* Caller has to free the allocated memory for aclp by calling
* acl_free().
*/
int
{
int error;
switch (acl_type) {
case ACLENT_T:
break;
case ACE_T:
break;
default:
return (EINVAL);
}
return (error);
return (0);
}
/*
* smb_vop_acl_write
*
* Writes the given ACL in aclp for the specified file.
*/
int
{
int error;
int aclbsize;
if (error == 0) {
}
return (error);
}
/*
* smb_vop_acl_type
*
* Determines the ACL type for the given vnode.
* ACLENT_T is a Posix ACL and ACE_T is a ZFS ACL.
*/
{
int error;
if (error != 0) {
/*
* If we got an error, then the filesystem
* likely does not understand the _PC_ACL_ENABLED
* pathconf. In this case, we fall back to trying
* POSIX-draft (aka UFS-style) ACLs.
*/
}
/*
* If the file system supports neither ACE nor
* ACLENT ACLs we will fall back to UFS-style ACLs
* like we did above if there was an error upon
* calling VOP_PATHCONF.
*
* ACE and ACLENT type ACLs are the only interfaces
* supported thus far. If any other bits are set on
* 'whichacl' upon return from VOP_PATHCONF, we will
* ignore them.
*/
}
if (whichacl == _ACL_ACLENT_ENABLED)
return (ACLENT_T);
return (ACE_T);
}
static int zfs_perms[] = {
};
/*
* smb_vop_eaccess
*
* Returns the effective permission of the given credential for the
* specified object.
*
*/
void
{
int error, i;
int pnum;
*mode = 0;
if (flags == V_ACE_MASK) {
for (i = 0; i < pnum; i++) {
if (error == 0)
}
} else {
pnum = sizeof (unix_perms) / sizeof (int);
for (i = 0; i < pnum; i++) {
if (error == 0)
*mode |= unix_perms[i];
}
}
}
/*
* smb_vop_shrlock()
*
* See comments for smb_fsop_shrlock()
*/
int
{
struct shr_locowner shr_own;
short new_access = 0;
short deny = 0;
int flag = 0;
int cmd;
/*
* Check if this is a metadata access
*/
if ((desired_access & FILE_DATA_ALL) == 0) {
new_access |= F_MDACC;
} else {
new_access |= F_RDACC;
}
ACE_ADD_FILE)) {
new_access |= F_WRACC;
}
if (SMB_DENY_READ(share_access)) {
}
if (SMB_DENY_WRITE(share_access)) {
}
if (cmd == F_SHARE_NBMAND) {
if (desired_access & ACE_DELETE)
new_access |= F_RMACC;
if (SMB_DENY_DELETE(share_access)) {
}
}
}
}
int
{
struct shr_locowner shr_own;
/*
* For s_access and s_deny, we do not need to pass in the original
* values.
*/
}