smbfs_vnops.c revision 28162916a3f5a19f85a16b70e708bbe9235fb7c0
/*
* Copyright (c) 2000-2001 Boris Popov
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Boris Popov.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: smbfs_vnops.c,v 1.128.36.1 2005/05/27 02:35:28 lindak Exp $
*/
/*
*/
#include <sys/sysmacros.h>
#include <sys/vfs_opreg.h>
#include <netsmb/smb_osdep.h>
#include <netsmb/smb_conn.h>
#include <netsmb/smb_subr.h>
#include <smbfs/smbfs_node.h>
#include <smbfs/smbfs_subr.h>
/*
* We assign directory offsets like the NFS client, where the
* offset increments by _one_ after each directory entry.
* Further, the entries "." and ".." are always at offsets
* zero and one (respectively) and the "real" entries from
* the server appear at offsets starting with two. This
* macro is used to initialize the n_dirofs field after
* setting n_dirseq with a _findopen call.
*/
#define FIRST_DIROFS 2
/*
* These characters are illegal in NTFS file names.
*
* Careful! The check in the XATTR case skips the
* first character to allow colon in XATTR names.
*/
static const char illegal_chars[] = {
':', /* colon - keep this first! */
'\\', /* back slash */
'/', /* slash */
'*', /* asterisk */
'?', /* question mark */
'"', /* double quote */
'<', /* less than sign */
'>', /* greater than sign */
'|', /* vertical bar */
0
};
/*
* Turning this on causes nodes to be created in the cache
* during directory listings, normally avoiding a second
* OtW attribute fetch just after a readdir.
*/
int smbfs_fastlookup = 1;
/* local static function defines */
cred_t *);
int cache_ok, caller_context_t *);
static int smbfs_accessx(void *, int, cred_t *);
caller_context_t *);
/*
* These are the vnode ops routines which implement the vnode interface to
* the networked file system. These routines just take their parameters,
* make them look networkish by putting the right info into interface structs,
* and then calling the appropriate remote routine(s) to do the work.
*
* Note on directory name lookup cacheing: If we detect a stale fhandle,
* we purge the directory cache relative to that vnode. This way, the
* more details on smbnode locking.
*/
caller_context_t *);
caller_context_t *);
caller_context_t *);
caller_context_t *);
caller_context_t *);
caller_context_t *);
int *, pathname_t *);
vsecattr_t *);
int);
caller_context_t *, int);
caller_context_t *, int);
caller_context_t *, int);
cred_t *, caller_context_t *);
caller_context_t *);
caller_context_t *);
caller_context_t *);
caller_context_t *);
/* Dummy function to use until correct function is ported in */
int noop_vnodeop() {
return (0);
}
/*
* Most unimplemented ops will return ENOSYS because of fs_nosys().
* The only ops where that won't work are ACCESS (due to open(2)
* failures) and ... (anything else left?)
*/
const fs_operation_def_t smbfs_vnodeops_template[] = {
};
/*
* XXX
* When new and relevant functionality is enabled, we should be
* calling vfs_set_feature() to inform callers that pieces of
* functionality are available, per PSARC 2007/227.
*/
/* ARGSUSED */
static int
{
int oldgenid;
int tmperror;
int error = 0;
return (EIO);
return (EIO);
return (EACCES);
}
/*
* Get exclusive access to n_fid and related stuff.
* No returns after this until out.
*/
return (EINTR);
/*
* Keep track of the vnode type at first open.
* It may change later, and we need close to do
* cleanup for the type we opened. Also deny
* open of new types until old type is closed.
* XXX: Per-open instance nodes whould help.
*/
SMBVDEBUG("open n_ovtype=%d v_type=%d\n",
goto out;
}
/*
* Directory open. See smbfs_readvdir()
*/
/* first open */
if (error != 0)
goto out;
}
goto have_fid;
}
/*
* FWRITE (to drive successful setattr(size=0) after open)
*/
/*
* If we already have it open, and the FID is still valid,
* check whether the rights are sufficient for FID reuse.
*/
int upgrade = 0;
upgrade = 1;
upgrade = 1;
if (!upgrade) {
/*
* the existing open is good enough
*/
goto have_fid;
}
}
/*
* we always ask for READ_CONTROL so we can always get the
*/
NULL, 0, 0, /* name nmlen xattr */
if (error)
goto out;
/*
* We have a new FID and access rights.
*/
/*
* We already had it open (presumably because
* it was open with insufficient rights.)
* Close old wire-open.
*/
if (tmperror)
SMBVDEBUG("error %d closing %s\n",
}
/*
* This thread did the open.
* Save our credentials too.
*/
if (oldcr)
/*
* Keep track of the vnode type at first open.
* (see comments above)
*/
out:
return (error);
}
/*ARGSUSED*/
static int
{
/*
* Don't "bail out" for VFS_UNMOUNTED here,
* as we want to do cleanup, etc.
*/
/*
* zone_enter(2) prevents processes from changing zones with SMBFS files
* open; if we happen to get here from the wrong zone we can't do
* anything over the wire.
*/
/*
* We could attempt to clean up locks, except we're sure
* that the current process didn't acquire any locks on
* the file: any attempt to lock a file belong to another zone
* will fail, and one can't lock an SMBFS file and then change
* zones, as that fails too.
*
* Returning an error here is the sane thing to do. A
* subsequent call to VN_RELE() which translates to a
* smbfs_inactive() will clean up state: if the zone of the
* vnode's origin is still alive and kicking, an async worker
* thread will handle the request (from the correct zone), and
* everything (minus the final smbfs_getattr_otw() call) should
* be OK. If the zone is going away smbfs_async_inactive() will
* throw away cached pages inline.
*/
return (EIO);
}
/*
* If we are using local locking for this filesystem, then
* release all of the SYSV style record locks. Otherwise,
* we are doing network locking and we need to release all
* of the network locks. All of the locks held by this
* process on this file are released no matter what the
* incoming reference count is.
*/
}
/*
* This (passed in) count is the ref. count from the
* user's file_t before the closef call (fio.c).
* We only care when the reference goes away.
*/
if (count > 1)
return (0);
/*
* Decrement the reference count for the FID
* and possibly do the OtW close.
*
* Exclusive lock for modifying n_fid stuff.
* Don't want this one ever interruptible.
*/
return (0);
}
/*
* Helper for smbfs_close. Decrement the reference count
* for an SMB-level file or directory ID, and when the last
* reference for the fid goes away, do the OtW close.
* Also called in smbfs_inactive (defensive cleanup).
*/
static void
{
struct smbfs_fctx *fctx;
int error;
error = 0;
/* Make sure we serialize for n_dirseq use. */
/*
* Note that vp->v_type may change if a remote node
* is deleted and recreated as a different type, and
* our getattr may change v_type accordingly.
* Now use n_ovtype to keep track of the v_type
* we had during open (see comments above).
*/
case VDIR:
return;
}
break;
case VREG:
return;
/* After reconnect, n_fid is invalid */
}
}
break;
default:
break;
}
if (error) {
SMBVDEBUG("error %d closing %s\n",
}
/* Allow next open to use any v_type. */
/*
* Other "last close" stuff.
*/
}
/* ARGSUSED */
static int
{
int error;
return (EIO);
return (EIO);
return (EISDIR);
return (0);
/*
* Like NFS3, just check for 63-bit overflow.
* Our SMB layer takes care to return EFBIG
* when it has to fallback to a 32-bit call.
*/
return (EINVAL);
/* get vnode attributes from server */
return (error);
/* Update mtime with mtime from server here? */
/* if offset is beyond EOF, read nothing */
return (0);
/*
* Limit the read to the remaining file size.
* Do this by temporarily reducing uio_resid
* by the amount the lies beyoned the EOF.
*/
} else
past_eof = 0;
/* Shared lock for n_fid use in smb_rwuio */
return (EINTR);
/* After reconnect, n_fid is invalid */
else
/* undo adjustment of resid */
return (error);
}
/* ARGSUSED */
static int
{
return (EIO);
return (EIO);
return (EISDIR);
return (0);
/*
* Handle ioflag bits: (FAPPEND|FSYNC|FDSYNC)
*/
/* XXX: smbfs_vinvalbuf? */
}
}
/*
* File size can be changed by another client
*/
return (error);
}
/*
* Like NFS3, just check for 63-bit overflow.
*/
return (EINVAL);
/*
* Check to make sure that the process will not exceed
* its limit on file size. It is okay to write up to
* the limit, but not beyond. Thus, the write which
* reaches the limit will be short and the next write
* will return an error.
*
* So if we're starting at or beyond the limit, EFBIG.
* Otherwise, temporarily reduce resid to the amount
* the falls after the limit.
*/
limit = MAXOFFSET_T;
return (EFBIG);
} else
past_limit = 0;
/* Timeout: longer for append. */
/* Shared lock for n_fid use in smb_rwuio */
return (EINTR);
/* After reconnect, n_fid is invalid */
else
if (error == 0) {
/* Don't error the I/O if this fails. */
}
}
/* undo adjustment of resid */
return (error);
}
/* ARGSUSED */
static int
{
int error;
return (EIO);
return (EIO);
switch (cmd) {
/* First three from ZFS. XXX - need these? */
case _FIOFFS:
break;
/*
* The following two ioctls are used by bfu.
* Silently ignore to avoid bfu errors.
*/
case _FIOGDIO:
case _FIOSDIO:
error = 0;
break;
#ifdef NOT_YET /* XXX - from the NFS code. */
case _FIODIRECTIO:
#endif
/*
* Useful for testing, diagnosing idmap problems, etc.
*/
case SMBFSIO_GETSD:
break;
case SMBFSIO_SETSD:
break;
default:
break;
}
return (error);
}
/*
* Return either cached or remote attributes. If get remote attr
* use them to check and invalidate caches, then cache the new attributes.
*/
/* ARGSUSED */
static int
{
return (EIO);
return (EIO);
/*
* If it has been specified that the return value will
* just be used as a hint, and we are only being asked
* for size, fsid or rdevid, then return the client's
* notion of these values without checking to make sure
* that the attribute cache is up to date.
* The whole point is to avoid an over the wire GETATTR
* call.
*/
return (0);
}
}
}
/* smbfsgetattr() in smbfs_client.c */
/*ARGSUSED4*/
static int
{
int error;
return (EIO);
return (EIO);
return (EINVAL);
return (EROFS);
/*
* This is a _local_ access check so that only the owner of
* this mount can set attributes. With ACLs enabled, the
* file owner can be different from the mount owner, and we
* need to check the _mount_ owner here. See _access_rwx
*/
if (error)
return (error);
smbfs_accessx, vp);
if (error)
return (error);
else
if (error != 0) {
/*
* It might be more correct to return the
* error here, but that causes complaints
* when root extracts a cpio archive, etc.
* So ignore this error, and go ahead with
* the rest of the setattr work.
*/
}
}
}
/*
* Mostly from Darwin smbfs_setattr()
* but then modified a lot.
*/
/* ARGSUSED */
static int
{
int error = 0;
unsigned short fid;
int have_fid = 0;
/*
* There are no settable attributes on the XATTR dir,
* so just silently ignore these. On XATTR files,
* you can set the size but nothing else.
*/
return (0);
SMBVDEBUG("ignore set time on xattr\n");
}
/*
* If our caller is trying to set multiple attributes, they
* can make no assumption about what order they are done in.
* Here we try to do them in order of decreasing likelihood
* of failure, just to minimize the chance we'll wind up
* with a partially complete request.
*/
/* Shared lock for (possible) n_fid use. */
return (EINTR);
/*
* If the caller has provided extensible attributes,
* map those into DOS attributes supported by SMB.
* Note: zero means "no change".
*/
/*
* Will we need an open handle for this setattr?
* If so, what rights will we need?
*/
rights |=
}
rights |=
}
/*
* Only SIZE really requires a handle, but it's
* simpler and more reliable to set via a handle.
* Some servers like NT4 won't set times by path.
* Also, we're usually setting everything anyway.
*/
if (rights != 0) {
if (error) {
SMBVDEBUG("error %d opening %s\n",
goto out;
}
have_fid = 1;
}
/*
* If the server supports the UNIX extensions, right here is where
* we'd support changes to uid, gid, mode, and possibly va_flags.
* For now we claim to have made any such changes.
*/
/*
* If the new file size is less than what the client sees as
* the file size, then just change the size and invalidate
* the pages.
* I am commenting this code at present because the function
* smbfs_putapage() is not yet implemented.
*/
/*
* Set the file size to vap->va_size.
*/
if (error) {
SMBVDEBUG("setsize error %d file %s\n",
} else {
/*
* Darwin had code here to zero-extend.
* Tests indicate the server will zero-fill,
* so looks like we don't need to do this.
* Good thing, as this could take forever.
*
* XXX: Reportedly, writing one byte of zero
* at the end offset avoids problems here.
*/
modified = 1;
}
}
/*
* XXX: When Solaris has create_time, set that too.
* Note: create_time is different from ctime.
*/
/*
* Always use the handle-based set attr call now.
*/
if (error) {
SMBVDEBUG("set times error %d file %s\n",
} else {
modified = 1;
}
}
out:
if (modified) {
/*
* Invalidate attribute cache in case the server
* doesn't set exactly the attributes we asked.
*/
}
if (have_fid) {
if (cerror)
SMBVDEBUG("error %d closing %s\n",
}
return (error);
}
/*
* Helper function for extensible system attributes (PSARC 2007/315)
* Compute the DOS attribute word to pass to _setfattr (see above).
* This returns zero IFF no change is being made to attributes.
* Otherwise return the new attributes or SMB_EFA_NORMAL.
*/
static uint32_t
{
return (0);
if (xoap->xoa_archive)
attr |= SMB_FA_ARCHIVE;
else
attr &= ~SMB_FA_ARCHIVE;
}
if (xoap->xoa_system)
attr |= SMB_FA_SYSTEM;
else
attr &= ~SMB_FA_SYSTEM;
}
if (xoap->xoa_readonly)
attr |= SMB_FA_RDONLY;
else
attr &= ~SMB_FA_RDONLY;
}
if (xoap->xoa_hidden)
attr |= SMB_FA_HIDDEN;
else
attr &= ~SMB_FA_HIDDEN;
}
return (0); /* no change */
if (attr == 0)
return (attr);
}
/*
* smbfs_access_rwx()
* Common function for smbfs_access, etc.
*
* The security model implemented by the FS is unusual
* due to the current "single user mounts" restriction:
* All access under a given mount point uses the CIFS
* credentials established by the owner of the mount.
*
* Most access checking is handled by the CIFS server,
* but we need sufficient Unix access checks here to
* prevent other local Unix users from having access
* settings in the mount would not allow.
*
* With this model, there is a case where we need the
* ability to do an access check before we have the
* vnode for an object. This function takes advantage
* avoids the need for a vnode.
*
* We still (sort of) need a vnode when we call
* secpolicy_vnode_access, but that only uses
* the vtype field, so we can use a pair of fake
* vnodes that have only v_type filled in.
*
* XXX: Later, add a new secpolicy_vtype_access()
* that takes the vtype instead of a vnode, and
* get rid of the tmpl_vxxx fake vnodes below.
*/
static int
{
/* See the secpolicy call below. */
int shift = 0;
/*
* Build our (fabricated) vnode attributes.
* XXX: Could make these templates in the
* per-mount struct and use them here.
*/
/*
* Disallow write attempts on read-only file systems,
* unless the file is a device or fifo node. Note:
* Inline vn_is_readonly and IS_DEVVP here because
* we may not have a vnode ptr. Original expr. was:
* (mode & VWRITE) && vn_is_readonly(vp) && !IS_DEVVP(vp))
*/
return (EROFS);
/*
* Disallow attempts to access mandatory lock files.
* Similarly, expand MANDLOCK here.
* XXX: not sure we need this.
*/
return (EACCES);
/*
* Access check is based on only
* one of owner, group, public.
* If not owner, then check group.
* If not a member of the group,
* then check public access.
*/
shift += 3;
shift += 3;
}
/*
* We need a vnode for secpolicy_vnode_access,
* but the only thing it looks at is v_type,
* so pass one of the templates above.
*/
}
/*
* See smbfs_setattr
*/
static int
{
/*
* Note: The caller has checked the current zone,
* the SMI_DEAD and VFS_UNMOUNTED flags, etc.
*/
}
/*
* XXX
* This op should support PSARC 2007/403, Modified Access Checks for CIFS
*/
/* ARGSUSED */
static int
{
return (EIO);
return (EIO);
}
/*
* Flush local dirty pages to stable storage on the server.
*
* If FNODSYNC is specified, then there is nothing to do because
* metadata changes are not cached on the client before being
* sent to the server.
*/
/* ARGSUSED */
static int
{
int error = 0;
return (EIO);
return (EIO);
return (0);
return (0);
/* Shared lock for n_fid use in _flush */
return (EINTR);
return (error);
}
/*
* Last reference to vnode went away.
*/
/* ARGSUSED */
static void
{
/*
* Don't "bail out" for VFS_UNMOUNTED here,
* as we want to do cleanup, etc.
* See also pcfs_inactive
*/
/*
* If this is coming from the wrong zone, we let someone in the right
* zone take care of it asynchronously. We can get here due to
* VN_RELE() being called from pageout() or fsflush(). This call may
* potentially turn into an expensive no-op if, for instance, v_count
* gets incremented in the meantime, but it's still correct.
*/
/*
* Defend against the possibility that higher-level callers
* might not correctly balance open and close calls. If we
* get here with open references remaining, it means there
* was a missing VOP_CLOSE somewhere. If that happens, do
* the close here so we don't "leak" FIDs on the server.
*
* Exclusive lock for modifying n_fid stuff.
* Don't want this one ever interruptible.
*/
case VNON:
/* not open (OK) */
break;
case VDIR:
break;
SMBVDEBUG("open dir: refs %d path %s\n",
/* Force last close. */
break;
case VREG:
break;
SMBVDEBUG("open file: refs %d id 0x%x path %s\n",
/* Force last close. */
break;
default:
break;
}
}
/*
* Remote file system operations having to do with directory manipulation.
*/
/* ARGSUSED */
static int
{
int error;
return (EPERM);
return (EIO);
/*
* Are we looking up extended attributes? If so, "dvp" is
* the file or directory for which we want attributes, and
* we need a lookup of the (faked up) attribute directory
* before we lookup the rest of the path.
*/
if (flags & LOOKUP_XATTR) {
/*
* Require the xattr mount option.
*/
return (EINVAL);
return (error);
}
return (EINTR);
return (error);
}
/* ARGSUSED */
static int
{
int error;
int supplen; /* supported length */
/* struct smb_vc *vcp; */
const char *ill;
int rplen;
#ifdef NOT_YET
/* XXX: Should compute this once and store it in smbmntinfo_t */
#else
supplen = 255;
#endif
/*
* RWlock must be held, either reader or writer.
* XXX: Can we check without looking directly
* inside the struct smbfs_rwlock_t?
*/
/*
* If lookup is for "", just return dvp.
* No need to perform any access checks.
*/
if (nmlen == 0) {
return (0);
}
/*
* Can't do lookups in non-directories.
*/
return (ENOTDIR);
/*
* Need search permission in the directory.
*/
if (error)
return (error);
/*
* If lookup is for ".", just return dvp.
* Access check was done above.
*/
return (0);
}
/*
* Now some sanity checks on the name.
* First check the length.
*/
return (ENAMETOOLONG);
/*
* Avoid surprises with characters that are
* illegal in Windows file names.
* Todo: CATIA mappings XXX
*/
ill = illegal_chars;
ill++; /* allow colon */
return (EINVAL);
/*
* Special handling for lookup of ".."
*
* We keep full pathnames (as seen on the server)
* so we can just trim off the last component to
* get the full pathname of the parent. Note:
* We don't actually copy and modify, but just
* compute the trimmed length and pass that with
* the current dir path (not null terminated).
*
* We don't go over-the-wire to get attributes
* for ".." because we know it's a directory,
* and we can just leave the rest "stale"
* until someone does a getattr.
*/
/*
* Already at the root. This can happen
* with directory listings at the root,
* which lookup "." and ".." to get the
* inode numbers. Let ".." be the same
* as "." in the FS root.
*/
return (0);
}
/*
* Special case for XATTR directory
*/
return (error);
}
/*
* Find the parent path length.
*/
while (--rplen >= 0) {
break;
}
if (rplen <= 0) {
/* Found our way to the root. */
return (0);
}
&smbfs_fattr0); /* force create */
/* Success! */
return (0);
}
/*
* Normal lookup of a name under this directory.
* Note we handled "", ".", ".." above.
*/
if (cache_ok) {
/*
* The caller indicated that it's OK to use a
* cached result for this lookup, so try to
* reclaim a node from the smbfs node cache.
*/
if (error)
return (error);
/* hold taken in lookup_cache */
return (0);
}
}
/*
* OK, go over-the-wire to get the attributes,
* then create the node.
*/
/* Note: this can allocate a new "name" */
/*
* Lookup failed because this directory was
* removed or renamed by another client.
* Remove any cached attributes under it.
*/
}
if (error)
goto out;
if (error)
goto out;
/* Success! */
out:
/* smbfs_smb_lookup may have allocated name. */
return (error);
}
/*
* smbfslookup_cache
*
* Try to reclaim a node from the smbfs node cache.
* Some statistics for DEBUG.
*
* This mechanism lets us avoid many of the five (or more)
* OtW lookup calls per file seen with "ls -l" if we search
* the smbfs node cache for recently inactive(ated) nodes.
*/
#ifdef DEBUG
int smbfs_lookup_cache_calls = 0;
int smbfs_lookup_cache_error = 0;
int smbfs_lookup_cache_miss = 0;
int smbfs_lookup_cache_stale = 0;
int smbfs_lookup_cache_hits = 0;
#endif /* DEBUG */
/* ARGSUSED */
static int
{
int error;
char sep;
#ifdef DEBUG
#endif
/*
* First make sure we can get attributes for the
* directory. Cached attributes are OK here.
* If we removed or renamed the directory, this
* will return ENOENT. If someone else removed
* this directory or file, we'll find out when we
* try to open or get attributes.
*/
if (error) {
#ifdef DEBUG
#endif
return (error);
}
/*
* Passing NULL smbfattr here so we will
* just look, not create.
*/
#ifdef DEBUG
#endif
return (0);
}
/*
* Found it. Attributes still valid?
*/
/* stale */
#ifdef DEBUG
#endif
return (0);
}
/*
* Success!
* Caller gets hold from smbfs_node_findcreate
*/
#ifdef DEBUG
#endif
return (0);
}
/*
* XXX
* vsecattr_t is new to build 77, and we need to eventually support
* it in order to create an ACL when an object is created.
*
* This op should support the new FIGNORECASE flag for case-insensitive
* lookups, per PSARC 2007/244.
*/
/* ARGSUSED */
static int
{
int error;
int cerror;
#ifdef NOT_YET
#endif
int xattr;
return (EPERM);
return (EIO);
/*
* Note: this may break mknod(2) calls to create a directory,
* but that's obscure use. Some other filesystems do this.
* XXX: Later, redirect VDIR type here to _mkdir.
*/
return (EINVAL);
/*
* If the pathname is "", just use dvp, no checks.
* Do this outside of the rwlock (like zfs).
*/
if (nmlen == 0) {
return (0);
}
/* Don't allow "." or ".." through here. */
return (EISDIR);
/*
* We make a copy of the attributes because the caller does not
* expect us to change what va points to.
*/
return (EINTR);
/*
* XXX: Do we need r_lkserlock too?
* No use of any shared fid or fctx...
*/
/*
* NFS needs to go over the wire, just to be sure whether the
* file exists or not. Using a cached result is dangerous in
* this case when making a decision regarding existence.
*
* The SMB protocol does NOT really need to go OTW here
* thanks to the expressive NTCREATE disposition values.
* Unfortunately, to do Unix access checks correctly,
* we need to know if the object already exists.
* When the object does not exist, we need VWRITE on
* the directory. Note: smbfslookup() checks VEXEC.
*/
if (error == 0) {
/*
* The file already exists. Error?
* NB: have a hold from smbfslookup
*/
goto out;
}
/*
* Verify requested access.
*/
if (error) {
goto out;
}
/*
* Truncate (if requested).
*/
if (error) {
goto out;
}
}
/* Success! */
#ifdef NOT_YET
#endif
goto out;
}
/*
* The file did not exist. Need VWRITE in the directory.
*/
if (error)
goto out;
/*
* Now things get tricky. We also need to check the
* requested open mode against the file we may create.
* See comments at smbfs_access_rwx
*/
if (error)
goto out;
/*
* Now the code derived from Darwin,
* but with greater use of NT_CREATE
* disposition options. Much changed.
*
* Create (or open) a new child node.
* Note we handled "." and ".." above.
*/
else {
/* Truncate regular files if requested. */
else
}
if (error)
goto out;
/*
* XXX: Missing some code here to deal with
* the case where we opened an existing file,
* it's size is larger than 32-bits, and we're
* setting the size from a process that's not
* aware of large file offsets. i.e.
* from the NFS3 code:
*/
#if NOT_YET /* XXX */
/*
* Check here for large file handled
* by LF-unaware process (as
* ufs_create() does)
*/
}
if (!error) {
}
}
#endif /* XXX */
/*
* while we have it opened here. See above.
*/
if (cerror)
SMBVDEBUG("error %d closing %s\\%s\n",
/*
* In the open case, the name may differ a little
* from what we passed to create (case, etc.)
* so call lookup to get the (opened) name.
*
* XXX: Could avoid this extra lookup if the
* "createact" result from NT_CREATE says we
* created the object.
*/
if (error)
goto out;
/* update attr and directory cache */
if (error)
goto out;
/* XXX invalidate pages if we truncated? */
/* Success! */
error = 0;
out:
return (error);
}
/*
* XXX
* This op should support the new FIGNORECASE flag for case-insensitive
* lookups, per PSARC 2007/244.
*/
/* ARGSUSED */
static int
int flags)
{
int error;
/* enum smbfsstat status; */
return (EPERM);
return (EIO);
return (EINTR);
/*
* Verify access to the dirctory.
*/
if (error)
goto out;
/*
* NOTE: the darwin code gets the "vp" passed in so it looks
* like the "vp" has probably been "lookup"ed by the VFS layer.
* It looks like we will need to lookup the vp to check the
* caches and check if the object being deleted is a directory.
*/
if (error)
goto out;
goto out;
}
/*
* Now we have the real reference count on the vnode
* Do we have the file open?
*/
/*
* NFS does a rename on remove here.
* Probably not applicable for SMB.
* Like Darwin, just return EBUSY.
*
* XXX: Todo - Use Trans2rename, and
* if that fails, ask the server to
* set the delete-on-close flag.
*/
} else {
/*
* If the file should no longer exist, discard
* any cached attributes under this node.
*/
switch (error) {
case 0:
case ENOENT:
case ENOTDIR:
break;
}
}
out:
return (error);
}
/*
* XXX
* This op should support the new FIGNORECASE flag for case-insensitive
* lookups, per PSARC 2007/244.
*/
/* ARGSUSED */
static int
{
/* vnode_t *realvp; */
return (EPERM);
return (EIO);
}
/*
* smbfsrename does the real work of renaming in SMBFS
*/
/* ARGSUSED */
static int
{
int error;
int nvp_locked = 0;
/* enum smbfsstat status; */
return (EINVAL);
/*
* Check that everything is on the same filesystem.
* vn_rename checks the fsid's, but in case we don't
* fill those in correctly, check here too.
*/
return (EXDEV);
/*
* Avoid deadlock here on old vs new directory nodes
* by always taking the locks in order of address.
* The order is arbitrary, but must be consistent.
*/
return (EINTR);
return (EINTR);
}
} else {
return (EINTR);
return (EINTR);
}
}
/*
* No returns after this point (goto out)
*/
/*
* Need write access on source and target.
* Server takes care of most checks.
*/
if (error)
goto out;
if (error)
goto out;
}
/*
* Lookup the source name. Must already exist.
*/
if (error)
goto out;
/*
* Lookup the target file. If it exists, it needs to be
* checked to see whether it is a mount point and whether
* it is active (open).
*/
if (!error) {
/*
* Target (nvp) already exists. Check that it
* has the same type as the source. The server
* will check this also, (and more reliably) but
* this lets us return the correct error codes.
*/
goto out;
}
} else {
goto out;
}
}
/*
* POSIX dictates that when the source and target
* entries refer to the same file object, rename
* must do nothing and exit without error.
*/
error = 0;
goto out;
}
/*
* Also must ensure the target is not a mount point,
*/
if (vn_vfsrlock(nvp)) {
goto out;
}
nvp_locked = 1;
goto out;
}
/*
* CIFS gives a SHARING_VIOLATION error when
* trying to rename onto an exising object,
* so try to remove the target first.
* (Only for files, not directories.)
*/
goto out;
}
/*
* Nodes that are "not active" here have v_count=2
* because vn_renameat (our caller) did a lookup on
* both the source and target before this call.
* Otherwise this similar to smbfs_remove.
*/
/*
* The target file exists, is not the same as
* the source file, and is active. Other FS
* implementations unlink the target here.
* For SMB, we don't assume we can remove an
* open file. Return an error instead.
*/
goto out;
}
/*
* Target file is not active. Try to remove it.
*/
/*
* Similar to smbfs_remove
*/
switch (error) {
case 0:
case ENOENT:
case ENOTDIR:
break;
}
if (error)
goto out;
/*
* OK, removed the target file. Continue as if
* lookup target had failed (nvp == NULL).
*/
nvp_locked = 0;
} /* nvp */
/*
* If the old name should no longer exist,
* discard any cached attributes under it.
*/
if (error == 0)
out:
if (nvp) {
if (nvp_locked)
}
if (ovp)
return (error);
}
/*
* XXX
* vsecattr_t is new to build 77, and we need to eventually support
* it in order to create an ACL when an object is created.
*
* This op should support the new FIGNORECASE flag for case-insensitive
* lookups, per PSARC 2007/244.
*/
/* ARGSUSED */
static int
{
return (EPERM);
return (EIO);
return (EEXIST);
/* Only plain files are allowed in V_XATTRDIR. */
return (EINVAL);
return (EINTR);
/*
* XXX: Do we need r_lkserlock too?
* No use of any shared fid or fctx...
*/
/*
* Require write access in the containing directory.
*/
if (error)
goto out;
if (error)
goto out;
if (error)
goto out;
if (error)
goto out;
if (name[0] == '.')
/* Success! */
error = 0;
out:
return (error);
}
/*
* XXX
* This op should support the new FIGNORECASE flag for case-insensitive
* lookups, per PSARC 2007/244.
*/
/* ARGSUSED */
static int
{
int vp_locked = 0;
int error;
return (EPERM);
return (EIO);
return (EINTR);
/*
* Require w/x access in the containing directory.
* Server handles all other access checks.
*/
if (error)
goto out;
/*
* First lookup the entry to be removed.
*/
if (error)
goto out;
/*
* Disallow rmdir of "." or current dir, or the FS root.
* Also make sure it's a directory, not a mount point,
*/
goto out;
}
goto out;
}
if (vn_vfsrlock(vp)) {
goto out;
}
vp_locked = 1;
goto out;
}
/*
* Similar to smbfs_remove
*/
switch (error) {
case 0:
case ENOENT:
case ENOTDIR:
break;
}
if (error)
goto out;
out:
if (vp) {
if (vp_locked)
}
return (error);
}
/* ARGSUSED */
static int
{
int error = 0;
return (EIO);
return (EIO);
/*
* Require read access in the directory.
*/
if (error)
return (error);
/*
* XXX: Todo readdir cache here
* Note: NFS code is just below this.
*
* I am serializing the entire readdir opreation
* now since we have not yet implemented readdir
* cache. This fix needs to be revisited once
* we implement readdir cache.
*/
return (EINTR);
return (error);
}
/* ARGSUSED */
static int
{
/*
* functions how many directory entries to request in
* each OtW call. It needs to be large enough so that
* we don't make lots of tiny OtW requests, but there's
* no point making it larger than the maximum number of
* OtW entries that would fit in a maximum sized trans2
* response (64k / 48). Beyond that, it's just tuning.
* WinNT used 512, Win2k used 1366. We use 1000.
*/
static const int limit = 1000;
/* Largest possible dirent size. */
struct smbfs_fctx *ctx;
int offset; /* yes, 32 bits */
/* Make sure we serialize for n_dirseq use. */
/*
* Make sure smbfs_open filled in n_dirseq
*/
return (EBADF);
/* Check for overflow of (32-bit) directory offset. */
return (EINVAL);
/* Require space for at least one dirent. */
return (EINVAL);
SMBVDEBUG("in: offset=%d, resid=%d\n",
error = 0;
/*
* Generate the "." and ".." entries here so we can
* (1) make sure they appear (but only once), and
* (2) deal with getting their I numbers which the
* findnext below does only for normal names.
*/
while (offset < FIRST_DIROFS) {
/*
* Tricky bit filling in the first two:
* offset 0 is ".", offset 1 is ".."
* so strlen of these is offset+1.
*/
goto out;
/*
* Want the real I-numbers for the "." and ".."
* entries. For these two names, we know that
* smbfslookup can get the nodes efficiently.
*/
if (error) {
} else {
}
/*
* Note: d_off is the offset that a user-level program
* should seek to for reading the NEXT directory entry.
* See libc: readdir, telldir, seekdir
*/
if (error)
goto out;
/*
* Note: uiomove updates uio->uio_offset,
* but we want it to be our "cookie" value,
* which just counts dirents ignoring size.
*/
}
/*
* If there was a backward seek, we have to reopen.
*/
SMBVDEBUG("Reopening search %d:%d\n",
if (error) {
goto out;
}
/* free the old one */
/* save the new one */
} else {
}
/*
* Skip entries before the requested offset.
*/
if (error != 0)
goto out;
}
/*
* While there's room in the caller's buffer:
* get a directory entry from SMB,
* convert to a dirent, copyout.
* We stop when there is no longer room for a
* maximum sized dirent because we must decide
* before we know anything about the next entry.
*/
if (error != 0)
goto out;
/* Sanity check the name length. */
if (nmlen > SMB_MAXFNAMELEN) {
}
if (smbfs_fastlookup) {
/* See comment at smbfs_fastlookup above. */
}
if (error)
goto out;
/* See comment re. uio_offset above. */
}
out:
/*
* When we come to the end of a directory, the
* SMB-level functions return ENOENT, but the
* caller is not expecting an error return.
*
* Also note that we must delay the call to
* smbfs_smb_findclose(np->n_dirseq, ...)
* until smbfs_close so that all reads at the
* end of the directory will return no data.
*/
error = 0;
if (eofp)
*eofp = 1;
}
/*
* If we encountered an error (i.e. "access denied")
* from the FindFirst call, we will have copied out
* the "." and ".." entries leaving offset == 2.
* so the caller gets no data with the error.
*/
}
SMBVDEBUG("out: offset=%d, resid=%d\n",
return (error);
}
/*
* The pair of functions VOP_RWLOCK, VOP_RWUNLOCK
* are optional functions that are called by:
* (other places)
*
* Careful here: None of the above check for any
* error returns from VOP_RWLOCK / VOP_RWUNLOCK!
* In fact, the return value from _rwlock is NOT
* an error code, but V_WRITELOCK_TRUE / _FALSE.
*
* Therefore, it's up to _this_ code to make sure
* the lock state remains balanced, which means
* we can't "bail out" on interrupts, etc.
*/
/* ARGSUSED2 */
static int
{
if (!write_lock) {
return (V_WRITELOCK_FALSE);
}
return (V_WRITELOCK_TRUE);
}
/* ARGSUSED */
static void
{
}
/* ARGSUSED */
static int
{
return (EPERM);
return (EIO);
/*
* Because we stuff the readdir cookie into the offset field
* someone may attempt to do an lseek with the cookie which
* we want to succeed.
*/
return (0);
/* Like NFS3, just check for 63-bit overflow. */
if (*noffp < 0)
return (EINVAL);
return (0);
}
/*
* XXX
* This op may need to support PSARC 2007/440, nbmand changes for CIFS Service.
*/
static int
{
return (EIO);
else
return (ENOSYS);
}
/*
* Free storage space associated with the specified vnode. The portion
* to be freed is specified by bfp->l_start and bfp->l_len (already
* normalized to a "whence" of 0).
*
* Called by fcntl(fd, F_FREESP, lkp) for libc:ftruncate, etc.
*/
/* ARGSUSED */
static int
{
int error;
return (EIO);
return (EIO);
/* Caller (fcntl) has checked v_type */
return (EINVAL);
/*
* Like NFS3, no 32-bit offset checks here.
* Our SMB layer takes care to return EFBIG
* when it has to fallback to a 32-bit call.
*/
if (!error) {
/*
* ftruncate should not change the ctime and
* mtime if we truncate the file to its
* previous size.
*/
return (error);
} else
}
return (error);
}
/* ARGSUSED */
static int
{
return (EIO);
return (EIO);
switch (cmd) {
case _PC_FILESIZEBITS:
*valp = 64;
else
*valp = 32;
break;
case _PC_LINK_MAX:
/* We only ever report one link to an object */
*valp = 1;
break;
case _PC_ACL_ENABLED:
/*
* Always indicate that ACLs are enabled and
* that we support ACE_T format, otherwise
* libsec will ask for ACLENT_T format data
* which we don't support.
*/
*valp = _ACL_ACE_ENABLED;
break;
case _PC_SYMLINK_MAX: /* No symlinks until we do Unix extensions */
*valp = 0;
break;
case _PC_XATTR_EXISTS:
break;
}
return (EINVAL);
case _PC_SATTR_ENABLED:
case _PC_SATTR_EXISTS:
*valp = 1;
break;
case _PC_TIMESTAMP_RESOLUTION:
/*
* Windows times are tenths of microseconds
* (multiples of 100 nanoseconds).
*/
*valp = 100L;
break;
default:
}
return (0);
}
/* ARGSUSED */
static int
{
int error;
return (EIO);
return (EIO);
/*
* Our _pathconf indicates _ACL_ACE_ENABLED,
* so we should only see VSA_ACE, etc here.
* Note: vn_create asks for VSA_DFACLCNT,
* and it expects ENOSYS and empty data.
*/
if (mask == 0)
return (ENOSYS);
else
return (error);
}
/* ARGSUSED */
static int
{
int error;
return (EIO);
return (EIO);
/*
* Our _pathconf indicates _ACL_ACE_ENABLED,
* so we should only see VSA_ACE, etc here.
*/
if (mask == 0)
return (ENOSYS);
return (EROFS);
/*
* Allow only the mount owner to do this.
* See comments at smbfs_access_rwx.
*/
if (error != 0)
return (error);
else
return (error);
}
/*
* XXX
* This op should eventually support PSARC 2007/268.
*/
static int
{
return (EIO);
else
return (ENOSYS);
}