Print this page
NEX-14666 Need to provide SMB 2.1 Client
NEX-17187 panic in smbfs_acl_store
NEX-17231 smbfs create xattr files finds wrong file
NEX-17224 smbfs lookup EINVAL should be ENOENT
NEX-17260 SMB1 client fails to list directory after NEX-14666
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
Reviewed by: Joyce McIntosh <joyce.mcintosh@nexenta.com>
and: (cleanup)
NEX-16818 Add fksmbcl development tool
NEX-17264 SMB client test tp_smbutil_013 fails after NEX-14666
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
and: (fix ref leaks)
NEX-16783 Panic in smbfs_delmap_callback (fix leak)
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Dan Fields <dan.fields@nexenta.com>
NEX-16783 Panic in smbfs_delmap_callback
Reviewed by: Jean McCormack <jean.mccormack@nexenta.com>
Reviewed by: Dan Fields <dan.fields@nexenta.com>
5404 smbfs needs mmap support
Portions contributed by: Gordon Ross <gordon.w.ross@gmail.com>
Reviewed by: C Fraire <cfraire@me.com>
Reviewed by: Toomas Soome <tsoome@me.com>
Reviewed by: Jason King <jason.brian.king@gmail.com>
Reviewed by: Andrew Stormont <andyjstormont@gmail.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
2552 smbfs: add support for NFS-like remove
Portions contributed by: Gordon Ross <gordon.w.ross@gmail.com>
Reviewed by: Yuri Pankov <yuripv@yuripv.net>
Reviewed by: Jason King <jason.king@joyent.com>
Reviewed by: C Fraire <cfraire@me.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
*** 32,43 ****
--- 32,51 ----
* $Id: smbfs_vnops.c,v 1.128.36.1 2005/05/27 02:35:28 lindak Exp $
*/
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
*/
+ /*
+ * Vnode operations
+ *
+ * This file is similar to nfs3_vnops.c
+ */
+
+ #include <sys/param.h>
#include <sys/systm.h>
#include <sys/cred.h>
#include <sys/vnode.h>
#include <sys/vfs.h>
#include <sys/filio.h>
*** 48,58 ****
--- 56,81 ----
#include <sys/sysmacros.h>
#include <sys/kmem.h>
#include <sys/cmn_err.h>
#include <sys/vfs_opreg.h>
#include <sys/policy.h>
+ #include <sys/sdt.h>
+ #include <sys/taskq_impl.h>
+ #include <sys/zone.h>
+ #ifdef _KERNEL
+ #include <sys/vmsystm.h> // for desfree
+ #include <vm/hat.h>
+ #include <vm/as.h>
+ #include <vm/page.h>
+ #include <vm/pvn.h>
+ #include <vm/seg.h>
+ #include <vm/seg_map.h>
+ #include <vm/seg_kpm.h>
+ #include <vm/seg_vn.h>
+ #endif // _KERNEL
+
#include <netsmb/smb_osdep.h>
#include <netsmb/smb.h>
#include <netsmb/smb_conn.h>
#include <netsmb/smb_subr.h>
*** 61,70 ****
--- 84,97 ----
#include <smbfs/smbfs_subr.h>
#include <sys/fs/smbfs_ioctl.h>
#include <fs/fs_subr.h>
+ #ifndef MAXOFF32_T
+ #define MAXOFF32_T 0x7fffffff
+ #endif
+
/*
* 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
*** 99,124 ****
* during directory listings, normally avoiding a second
* OtW attribute fetch just after a readdir.
*/
int smbfs_fastlookup = 1;
/* local static function defines */
static int smbfslookup_cache(vnode_t *, char *, int, vnode_t **,
cred_t *);
static int smbfslookup(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr,
int cache_ok, caller_context_t *);
! static int smbfsrename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm,
! cred_t *cr, caller_context_t *);
static int smbfssetattr(vnode_t *, struct vattr *, int, cred_t *);
static int smbfs_accessx(void *, int, cred_t *);
static int smbfs_readvdir(vnode_t *vp, uio_t *uio, cred_t *cr, int *eofp,
caller_context_t *);
static void smbfs_rele_fid(smbnode_t *, struct smb_cred *);
static uint32_t xvattr_to_dosattr(smbnode_t *, struct vattr *);
/*
* 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.
*
--- 126,184 ----
* during directory listings, normally avoiding a second
* OtW attribute fetch just after a readdir.
*/
int smbfs_fastlookup = 1;
+ struct vnodeops *smbfs_vnodeops = NULL;
+
/* local static function defines */
static int smbfslookup_cache(vnode_t *, char *, int, vnode_t **,
cred_t *);
static int smbfslookup(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr,
int cache_ok, caller_context_t *);
! static int smbfsremove(vnode_t *dvp, vnode_t *vp, struct smb_cred *scred,
! int flags);
! static int smbfsrename(vnode_t *odvp, vnode_t *ovp, vnode_t *ndvp,
! char *nnm, struct smb_cred *scred, int flags);
static int smbfssetattr(vnode_t *, struct vattr *, int, cred_t *);
static int smbfs_accessx(void *, int, cred_t *);
static int smbfs_readvdir(vnode_t *vp, uio_t *uio, cred_t *cr, int *eofp,
caller_context_t *);
+ static int smbfsflush(smbnode_t *, struct smb_cred *);
static void smbfs_rele_fid(smbnode_t *, struct smb_cred *);
static uint32_t xvattr_to_dosattr(smbnode_t *, struct vattr *);
+ static int smbfs_fsync(vnode_t *, int, cred_t *, caller_context_t *);
+
+ static int smbfs_putpage(vnode_t *, offset_t, size_t, int, cred_t *,
+ caller_context_t *);
+ #ifdef _KERNEL
+ static int smbfs_getapage(vnode_t *, u_offset_t, size_t, uint_t *,
+ page_t *[], size_t, struct seg *, caddr_t,
+ enum seg_rw, cred_t *);
+ static int smbfs_putapage(vnode_t *, page_t *, u_offset_t *, size_t *,
+ int, cred_t *);
+ static void smbfs_delmap_async(void *);
+
+ static int smbfs_rdwrlbn(vnode_t *, page_t *, u_offset_t, size_t, int,
+ cred_t *);
+ static int smbfs_bio(struct buf *, int, cred_t *);
+ static int smbfs_writenp(smbnode_t *np, caddr_t base, int tcount,
+ struct uio *uiop, int pgcreated);
+ #endif // _KERNEL
+
/*
+ * Error flags used to pass information about certain special errors
+ * which need to be handled specially.
+ */
+ #define SMBFS_EOF -98
+
+ /* When implementing OtW locks, make this a real function. */
+ #define smbfs_lm_has_sleep(vp) 0
+
+ /*
* 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.
*
*** 126,238 ****
* we purge the directory cache relative to that vnode. This way, the
* user won't get burned by the cache repeatedly. See <smbfs/smbnode.h> for
* more details on smbnode locking.
*/
- static int smbfs_open(vnode_t **, int, cred_t *, caller_context_t *);
- static int smbfs_close(vnode_t *, int, int, offset_t, cred_t *,
- caller_context_t *);
- static int smbfs_read(vnode_t *, struct uio *, int, cred_t *,
- caller_context_t *);
- static int smbfs_write(vnode_t *, struct uio *, int, cred_t *,
- caller_context_t *);
- static int smbfs_ioctl(vnode_t *, int, intptr_t, int, cred_t *, int *,
- caller_context_t *);
- static int smbfs_getattr(vnode_t *, struct vattr *, int, cred_t *,
- caller_context_t *);
- static int smbfs_setattr(vnode_t *, struct vattr *, int, cred_t *,
- caller_context_t *);
- static int smbfs_access(vnode_t *, int, int, cred_t *, caller_context_t *);
- static int smbfs_fsync(vnode_t *, int, cred_t *, caller_context_t *);
- static void smbfs_inactive(vnode_t *, cred_t *, caller_context_t *);
- static int smbfs_lookup(vnode_t *, char *, vnode_t **, struct pathname *,
- int, vnode_t *, cred_t *, caller_context_t *,
- int *, pathname_t *);
- static int smbfs_create(vnode_t *, char *, struct vattr *, enum vcexcl,
- int, vnode_t **, cred_t *, int, caller_context_t *,
- vsecattr_t *);
- static int smbfs_remove(vnode_t *, char *, cred_t *, caller_context_t *,
- int);
- static int smbfs_rename(vnode_t *, char *, vnode_t *, char *, cred_t *,
- caller_context_t *, int);
- static int smbfs_mkdir(vnode_t *, char *, struct vattr *, vnode_t **,
- cred_t *, caller_context_t *, int, vsecattr_t *);
- static int smbfs_rmdir(vnode_t *, char *, vnode_t *, cred_t *,
- caller_context_t *, int);
- static int smbfs_readdir(vnode_t *, struct uio *, cred_t *, int *,
- caller_context_t *, int);
- static int smbfs_rwlock(vnode_t *, int, caller_context_t *);
- static void smbfs_rwunlock(vnode_t *, int, caller_context_t *);
- static int smbfs_seek(vnode_t *, offset_t, offset_t *, caller_context_t *);
- static int smbfs_frlock(vnode_t *, int, struct flock64 *, int, offset_t,
- struct flk_callback *, cred_t *, caller_context_t *);
- static int smbfs_space(vnode_t *, int, struct flock64 *, int, offset_t,
- cred_t *, caller_context_t *);
- static int smbfs_pathconf(vnode_t *, int, ulong_t *, cred_t *,
- caller_context_t *);
- static int smbfs_setsecattr(vnode_t *, vsecattr_t *, int, cred_t *,
- caller_context_t *);
- static int smbfs_getsecattr(vnode_t *, vsecattr_t *, int, cred_t *,
- caller_context_t *);
- static int smbfs_shrlock(vnode_t *, int, struct shrlock *, int, cred_t *,
- caller_context_t *);
- /* Dummy function to use until correct function is ported in */
- int noop_vnodeop() {
- return (0);
- }
-
- struct vnodeops *smbfs_vnodeops = NULL;
-
/*
- * 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[] = {
- { VOPNAME_OPEN, { .vop_open = smbfs_open } },
- { VOPNAME_CLOSE, { .vop_close = smbfs_close } },
- { VOPNAME_READ, { .vop_read = smbfs_read } },
- { VOPNAME_WRITE, { .vop_write = smbfs_write } },
- { VOPNAME_IOCTL, { .vop_ioctl = smbfs_ioctl } },
- { VOPNAME_GETATTR, { .vop_getattr = smbfs_getattr } },
- { VOPNAME_SETATTR, { .vop_setattr = smbfs_setattr } },
- { VOPNAME_ACCESS, { .vop_access = smbfs_access } },
- { VOPNAME_LOOKUP, { .vop_lookup = smbfs_lookup } },
- { VOPNAME_CREATE, { .vop_create = smbfs_create } },
- { VOPNAME_REMOVE, { .vop_remove = smbfs_remove } },
- { VOPNAME_LINK, { .error = fs_nosys } }, /* smbfs_link, */
- { VOPNAME_RENAME, { .vop_rename = smbfs_rename } },
- { VOPNAME_MKDIR, { .vop_mkdir = smbfs_mkdir } },
- { VOPNAME_RMDIR, { .vop_rmdir = smbfs_rmdir } },
- { VOPNAME_READDIR, { .vop_readdir = smbfs_readdir } },
- { VOPNAME_SYMLINK, { .error = fs_nosys } }, /* smbfs_symlink, */
- { VOPNAME_READLINK, { .error = fs_nosys } }, /* smbfs_readlink, */
- { VOPNAME_FSYNC, { .vop_fsync = smbfs_fsync } },
- { VOPNAME_INACTIVE, { .vop_inactive = smbfs_inactive } },
- { VOPNAME_FID, { .error = fs_nosys } }, /* smbfs_fid, */
- { VOPNAME_RWLOCK, { .vop_rwlock = smbfs_rwlock } },
- { VOPNAME_RWUNLOCK, { .vop_rwunlock = smbfs_rwunlock } },
- { VOPNAME_SEEK, { .vop_seek = smbfs_seek } },
- { VOPNAME_FRLOCK, { .vop_frlock = smbfs_frlock } },
- { VOPNAME_SPACE, { .vop_space = smbfs_space } },
- { VOPNAME_REALVP, { .error = fs_nosys } }, /* smbfs_realvp, */
- { VOPNAME_GETPAGE, { .error = fs_nosys } }, /* smbfs_getpage, */
- { VOPNAME_PUTPAGE, { .error = fs_nosys } }, /* smbfs_putpage, */
- { VOPNAME_MAP, { .error = fs_nosys } }, /* smbfs_map, */
- { VOPNAME_ADDMAP, { .error = fs_nosys } }, /* smbfs_addmap, */
- { VOPNAME_DELMAP, { .error = fs_nosys } }, /* smbfs_delmap, */
- { VOPNAME_DUMP, { .error = fs_nosys } }, /* smbfs_dump, */
- { VOPNAME_PATHCONF, { .vop_pathconf = smbfs_pathconf } },
- { VOPNAME_PAGEIO, { .error = fs_nosys } }, /* smbfs_pageio, */
- { VOPNAME_SETSECATTR, { .vop_setsecattr = smbfs_setsecattr } },
- { VOPNAME_GETSECATTR, { .vop_getsecattr = smbfs_getsecattr } },
- { VOPNAME_SHRLOCK, { .vop_shrlock = smbfs_shrlock } },
- { NULL, NULL }
- };
-
- /*
* 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.
*/
--- 186,197 ----
*** 241,258 ****
smbfs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
{
smbnode_t *np;
vnode_t *vp;
smbfattr_t fa;
! u_int32_t rights, rightsrcvd;
! u_int16_t fid, oldfid;
! int oldgenid;
struct smb_cred scred;
smbmntinfo_t *smi;
smb_share_t *ssp;
cred_t *oldcr;
- int tmperror;
int error = 0;
vp = *vpp;
np = VTOSMB(vp);
smi = VTOSMI(vp);
--- 200,216 ----
smbfs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
{
smbnode_t *np;
vnode_t *vp;
smbfattr_t fa;
! smb_fh_t *fid = NULL;
! smb_fh_t *oldfid;
! uint32_t rights;
struct smb_cred scred;
smbmntinfo_t *smi;
smb_share_t *ssp;
cred_t *oldcr;
int error = 0;
vp = *vpp;
np = VTOSMB(vp);
smi = VTOSMI(vp);
*** 280,290 ****
/*
* 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.
*/
if (np->n_ovtype == VNON) {
ASSERT(np->n_dirrefs == 0);
ASSERT(np->n_fidrefs == 0);
} else if (np->n_ovtype != vp->v_type) {
--- 238,247 ----
*** 321,348 ****
/*
* If we already have it open, and the FID is still valid,
* check whether the rights are sufficient for FID reuse.
*/
if (np->n_fidrefs > 0 &&
! np->n_vcgenid == ssp->ss_vcgenid) {
int upgrade = 0;
if ((flag & FWRITE) &&
! !(np->n_rights & SA_RIGHT_FILE_WRITE_DATA))
upgrade = 1;
if ((flag & FREAD) &&
! !(np->n_rights & SA_RIGHT_FILE_READ_DATA))
upgrade = 1;
if (!upgrade) {
/*
* the existing open is good enough
*/
np->n_fidrefs++;
goto have_fid;
}
}
! rights = np->n_fidrefs ? np->n_rights : 0;
/*
* we always ask for READ_CONTROL so we can always get the
* owner/group IDs to satisfy a stat. Ditto attributes.
*/
--- 278,307 ----
/*
* If we already have it open, and the FID is still valid,
* check whether the rights are sufficient for FID reuse.
*/
if (np->n_fidrefs > 0 &&
! (fid = np->n_fid) != NULL &&
! fid->fh_vcgenid == ssp->ss_vcgenid) {
int upgrade = 0;
if ((flag & FWRITE) &&
! !(fid->fh_rights & SA_RIGHT_FILE_WRITE_DATA))
upgrade = 1;
if ((flag & FREAD) &&
! !(fid->fh_rights & SA_RIGHT_FILE_READ_DATA))
upgrade = 1;
if (!upgrade) {
/*
* the existing open is good enough
*/
np->n_fidrefs++;
goto have_fid;
}
+ fid = NULL;
}
! rights = (fid != NULL) ? fid->fh_rights : 0;
/*
* we always ask for READ_CONTROL so we can always get the
* owner/group IDs to satisfy a stat. Ditto attributes.
*/
*** 357,393 ****
bzero(&fa, sizeof (fa));
error = smbfs_smb_open(np,
NULL, 0, 0, /* name nmlen xattr */
rights, &scred,
! &fid, &rightsrcvd, &fa);
if (error)
goto out;
smbfs_attrcache_fa(vp, &fa);
/*
* We have a new FID and access rights.
*/
oldfid = np->n_fid;
- oldgenid = np->n_vcgenid;
np->n_fid = fid;
- np->n_vcgenid = ssp->ss_vcgenid;
- np->n_rights = rightsrcvd;
np->n_fidrefs++;
! if (np->n_fidrefs > 1 &&
! oldgenid == ssp->ss_vcgenid) {
! /*
! * We already had it open (presumably because
! * it was open with insufficient rights.)
! * Close old wire-open.
! */
! tmperror = smbfs_smb_close(ssp,
! oldfid, NULL, &scred);
! if (tmperror)
! SMBVDEBUG("error %d closing %s\n",
! tmperror, np->n_rpath);
! }
/*
* This thread did the open.
* Save our credentials too.
*/
--- 316,338 ----
bzero(&fa, sizeof (fa));
error = smbfs_smb_open(np,
NULL, 0, 0, /* name nmlen xattr */
rights, &scred,
! &fid, &fa);
if (error)
goto out;
smbfs_attrcache_fa(vp, &fa);
/*
* We have a new FID and access rights.
*/
oldfid = np->n_fid;
np->n_fid = fid;
np->n_fidrefs++;
! if (oldfid != NULL)
! smb_fh_rele(oldfid);
/*
* This thread did the open.
* Save our credentials too.
*/
*** 419,428 ****
--- 364,374 ----
caller_context_t *ct)
{
smbnode_t *np;
smbmntinfo_t *smi;
struct smb_cred scred;
+ int error = 0;
np = VTOSMB(vp);
smi = VTOSMI(vp);
/*
*** 466,485 ****
if (smi->smi_flags & SMI_LLOCK) {
pid_t pid = ddi_get_pid();
cleanlocks(vp, pid, 0);
cleanshares(vp, pid);
}
/*
* 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.
--- 412,457 ----
if (smi->smi_flags & SMI_LLOCK) {
pid_t pid = ddi_get_pid();
cleanlocks(vp, pid, 0);
cleanshares(vp, pid);
}
+ /*
+ * else doing OtW locking. SMB servers drop all locks
+ * on the file ID we close here, so no _lockrelease()
+ */
/*
* This (passed in) count is the ref. count from the
* user's file_t before the closef call (fio.c).
! * The rest happens only on last close.
*/
if (count > 1)
return (0);
+ /* NFS has DNLC purge here. */
+
/*
+ * If the file was open for write and there are pages,
+ * then make sure dirty pages written back.
+ *
+ * NFS does this async when "close-to-open" is off
+ * (MI_NOCTO flag is set) to avoid blocking the caller.
+ * For now, always do this synchronously (no B_ASYNC).
+ */
+ if ((flag & FWRITE) && vn_has_cached_data(vp)) {
+ error = smbfs_putpage(vp, (offset_t)0, 0, 0, cr, ct);
+ if (error == EAGAIN)
+ error = 0;
+ }
+ if (error == 0) {
+ mutex_enter(&np->r_statelock);
+ np->r_flags &= ~RSTALE;
+ np->r_error = 0;
+ mutex_exit(&np->r_statelock);
+ }
+
+ /*
* 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.
*** 502,518 ****
* Also called in smbfs_inactive (defensive cleanup).
*/
static void
smbfs_rele_fid(smbnode_t *np, struct smb_cred *scred)
{
- smb_share_t *ssp;
cred_t *oldcr;
struct smbfs_fctx *fctx;
int error;
! uint16_t ofid;
- ssp = np->n_mount->smi_share;
error = 0;
/* Make sure we serialize for n_dirseq use. */
ASSERT(smbfs_rw_lock_held(&np->r_lkserlock, RW_WRITER));
--- 474,488 ----
* Also called in smbfs_inactive (defensive cleanup).
*/
static void
smbfs_rele_fid(smbnode_t *np, struct smb_cred *scred)
{
cred_t *oldcr;
struct smbfs_fctx *fctx;
int error;
! smb_fh_t *ofid;
error = 0;
/* Make sure we serialize for n_dirseq use. */
ASSERT(smbfs_rw_lock_held(&np->r_lkserlock, RW_WRITER));
*** 537,554 ****
case VREG:
ASSERT(np->n_fidrefs > 0);
if (--np->n_fidrefs)
return;
! if ((ofid = np->n_fid) != SMB_FID_UNUSED) {
! np->n_fid = SMB_FID_UNUSED;
! /* After reconnect, n_fid is invalid */
! if (np->n_vcgenid == ssp->ss_vcgenid) {
! error = smbfs_smb_close(
! ssp, ofid, NULL, scred);
}
- }
break;
default:
SMBVDEBUG("bad n_ovtype %d\n", np->n_ovtype);
break;
--- 507,520 ----
case VREG:
ASSERT(np->n_fidrefs > 0);
if (--np->n_fidrefs)
return;
! if ((ofid = np->n_fid) != NULL) {
! np->n_fid = NULL;
! smb_fh_rele(ofid);
}
break;
default:
SMBVDEBUG("bad n_ovtype %d\n", np->n_ovtype);
break;
*** 581,598 ****
{
struct smb_cred scred;
struct vattr va;
smbnode_t *np;
smbmntinfo_t *smi;
- smb_share_t *ssp;
offset_t endoff;
ssize_t past_eof;
int error;
np = VTOSMB(vp);
smi = VTOSMI(vp);
- ssp = smi->smi_share;
if (curproc->p_zone != smi->smi_zone_ref.zref_zone)
return (EIO);
if (smi->smi_flags & SMI_DEAD || vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
--- 547,562 ----
*** 635,663 ****
past_eof = (ssize_t)(endoff - va.va_size);
uiop->uio_resid -= past_eof;
} else
past_eof = 0;
/* Shared lock for n_fid use in smb_rwuio */
if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp)))
return (EINTR);
smb_credinit(&scred, cr);
! /* After reconnect, n_fid is invalid */
! if (np->n_vcgenid != ssp->ss_vcgenid)
! error = ESTALE;
! else
! error = smb_rwuio(ssp, np->n_fid, UIO_READ,
uiop, &scred, smb_timo_read);
smb_credrele(&scred);
smbfs_rw_exit(&np->r_lkserlock);
/* undo adjustment of resid */
uiop->uio_resid += past_eof;
return (error);
}
/* ARGSUSED */
static int
--- 599,700 ----
past_eof = (ssize_t)(endoff - va.va_size);
uiop->uio_resid -= past_eof;
} else
past_eof = 0;
+ /*
+ * Bypass VM if caching has been disabled (e.g., locking) or if
+ * using client-side direct I/O and the file is not mmap'd and
+ * there are no cached pages.
+ */
+ if ((vp->v_flag & VNOCACHE) ||
+ (((np->r_flags & RDIRECTIO) || (smi->smi_flags & SMI_DIRECTIO)) &&
+ np->r_mapcnt == 0 && np->r_inmap == 0 &&
+ !vn_has_cached_data(vp))) {
+
/* Shared lock for n_fid use in smb_rwuio */
if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp)))
return (EINTR);
smb_credinit(&scred, cr);
! error = smb_rwuio(np->n_fid, UIO_READ,
uiop, &scred, smb_timo_read);
smb_credrele(&scred);
smbfs_rw_exit(&np->r_lkserlock);
/* undo adjustment of resid */
uiop->uio_resid += past_eof;
return (error);
+ }
+
+ #ifdef _KERNEL
+ /* (else) Do I/O through segmap. */
+ do {
+ caddr_t base;
+ u_offset_t off;
+ size_t n;
+ int on;
+ uint_t flags;
+
+ off = uiop->uio_loffset & MAXBMASK; /* mapping offset */
+ on = uiop->uio_loffset & MAXBOFFSET; /* Relative offset */
+ n = MIN(MAXBSIZE - on, uiop->uio_resid);
+
+ error = smbfs_validate_caches(vp, cr);
+ if (error)
+ break;
+
+ /* NFS waits for RINCACHEPURGE here. */
+
+ if (vpm_enable) {
+ /*
+ * Copy data.
+ */
+ error = vpm_data_copy(vp, off + on, n, uiop,
+ 1, NULL, 0, S_READ);
+ } else {
+ base = segmap_getmapflt(segkmap, vp, off + on, n, 1,
+ S_READ);
+
+ error = uiomove(base + on, n, UIO_READ, uiop);
+ }
+
+ if (!error) {
+ /*
+ * If read a whole block or read to eof,
+ * won't need this buffer again soon.
+ */
+ mutex_enter(&np->r_statelock);
+ if (n + on == MAXBSIZE ||
+ uiop->uio_loffset == np->r_size)
+ flags = SM_DONTNEED;
+ else
+ flags = 0;
+ mutex_exit(&np->r_statelock);
+ if (vpm_enable) {
+ error = vpm_sync_pages(vp, off, n, flags);
+ } else {
+ error = segmap_release(segkmap, base, flags);
+ }
+ } else {
+ if (vpm_enable) {
+ (void) vpm_sync_pages(vp, off, n, 0);
+ } else {
+ (void) segmap_release(segkmap, base, 0);
+ }
+ }
+ } while (!error && uiop->uio_resid > 0);
+ #else // _KERNEL
+ error = ENOSYS;
+ #endif // _KERNEL
+
+ /* undo adjustment of resid */
+ uiop->uio_resid += past_eof;
+
+ return (error);
}
/* ARGSUSED */
static int
*** 666,683 ****
{
struct smb_cred scred;
struct vattr va;
smbnode_t *np;
smbmntinfo_t *smi;
- smb_share_t *ssp;
offset_t endoff, limit;
ssize_t past_limit;
int error, timo;
np = VTOSMB(vp);
smi = VTOSMI(vp);
- ssp = smi->smi_share;
if (curproc->p_zone != smi->smi_zone_ref.zref_zone)
return (EIO);
if (smi->smi_flags & SMI_DEAD || vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
--- 703,723 ----
{
struct smb_cred scred;
struct vattr va;
smbnode_t *np;
smbmntinfo_t *smi;
offset_t endoff, limit;
ssize_t past_limit;
int error, timo;
+ u_offset_t last_off;
+ size_t last_resid;
+ #ifdef _KERNEL
+ uint_t bsize;
+ #endif
np = VTOSMB(vp);
smi = VTOSMI(vp);
if (curproc->p_zone != smi->smi_zone_ref.zref_zone)
return (EIO);
if (smi->smi_flags & SMI_DEAD || vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
*** 695,710 ****
* Handle ioflag bits: (FAPPEND|FSYNC|FDSYNC)
*/
if (ioflag & (FAPPEND | FSYNC)) {
if (np->n_flag & NMODIFIED) {
smbfs_attrcache_remove(np);
- /* XXX: smbfs_vinvalbuf? */
}
}
if (ioflag & FAPPEND) {
/*
* File size can be changed by another client
*/
va.va_mask = AT_SIZE;
if (error = smbfsgetattr(vp, &va, cr))
return (error);
uiop->uio_loffset = va.va_size;
--- 735,752 ----
* Handle ioflag bits: (FAPPEND|FSYNC|FDSYNC)
*/
if (ioflag & (FAPPEND | FSYNC)) {
if (np->n_flag & NMODIFIED) {
smbfs_attrcache_remove(np);
}
}
if (ioflag & FAPPEND) {
/*
* File size can be changed by another client
+ *
+ * Todo: Consider redesigning this to use a
+ * handle opened for append instead.
*/
va.va_mask = AT_SIZE;
if (error = smbfsgetattr(vp, &va, cr))
return (error);
uiop->uio_loffset = va.va_size;
*** 724,746 ****
* 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 = uiop->uio_llimit;
if (limit == RLIM64_INFINITY || limit > MAXOFFSET_T)
limit = MAXOFFSET_T;
! if (uiop->uio_loffset >= limit)
return (EFBIG);
if (endoff > limit) {
past_limit = (ssize_t)(endoff - limit);
uiop->uio_resid -= past_limit;
} else
past_limit = 0;
/* Timeout: longer for append. */
timo = smb_timo_write;
if (endoff > np->r_size)
timo = smb_timo_append;
--- 766,823 ----
* 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
! * that is after the limit.
*/
limit = uiop->uio_llimit;
if (limit == RLIM64_INFINITY || limit > MAXOFFSET_T)
limit = MAXOFFSET_T;
! if (uiop->uio_loffset >= limit) {
! #ifdef _KERNEL
! proc_t *p = ttoproc(curthread);
!
! mutex_enter(&p->p_lock);
! (void) rctl_action(rctlproc_legacy[RLIMIT_FSIZE],
! p->p_rctls, p, RCA_UNSAFE_SIGINFO);
! mutex_exit(&p->p_lock);
! #endif // _KERNEL
return (EFBIG);
+ }
if (endoff > limit) {
past_limit = (ssize_t)(endoff - limit);
uiop->uio_resid -= past_limit;
} else
past_limit = 0;
+ /*
+ * Bypass VM if caching has been disabled (e.g., locking) or if
+ * using client-side direct I/O and the file is not mmap'd and
+ * there are no cached pages.
+ */
+ if ((vp->v_flag & VNOCACHE) ||
+ (((np->r_flags & RDIRECTIO) || (smi->smi_flags & SMI_DIRECTIO)) &&
+ np->r_mapcnt == 0 && np->r_inmap == 0 &&
+ !vn_has_cached_data(vp))) {
+
+ #ifdef _KERNEL
+ smbfs_fwrite:
+ #endif // _KERNEL
+ if (np->r_flags & RSTALE) {
+ last_resid = uiop->uio_resid;
+ last_off = uiop->uio_loffset;
+ error = np->r_error;
+ /*
+ * A close may have cleared r_error, if so,
+ * propagate ESTALE error return properly
+ */
+ if (error == 0)
+ error = ESTALE;
+ goto bottom;
+ }
+
/* Timeout: longer for append. */
timo = smb_timo_write;
if (endoff > np->r_size)
timo = smb_timo_append;
*** 747,772 ****
/* Shared lock for n_fid use in smb_rwuio */
if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp)))
return (EINTR);
smb_credinit(&scred, cr);
! /* After reconnect, n_fid is invalid */
! if (np->n_vcgenid != ssp->ss_vcgenid)
! error = ESTALE;
! else
! error = smb_rwuio(ssp, np->n_fid, UIO_WRITE,
uiop, &scred, timo);
if (error == 0) {
mutex_enter(&np->r_statelock);
np->n_flag |= (NFLUSHWIRE | NATTRCHANGED);
if (uiop->uio_loffset > (offset_t)np->r_size)
np->r_size = (len_t)uiop->uio_loffset;
mutex_exit(&np->r_statelock);
! if (ioflag & (FSYNC|FDSYNC)) {
/* Don't error the I/O if this fails. */
! (void) smbfs_smb_flush(np, &scred);
}
}
smb_credrele(&scred);
smbfs_rw_exit(&np->r_lkserlock);
--- 824,845 ----
/* Shared lock for n_fid use in smb_rwuio */
if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp)))
return (EINTR);
smb_credinit(&scred, cr);
! error = smb_rwuio(np->n_fid, UIO_WRITE,
uiop, &scred, timo);
if (error == 0) {
mutex_enter(&np->r_statelock);
np->n_flag |= (NFLUSHWIRE | NATTRCHANGED);
if (uiop->uio_loffset > (offset_t)np->r_size)
np->r_size = (len_t)uiop->uio_loffset;
mutex_exit(&np->r_statelock);
! if (ioflag & (FSYNC | FDSYNC)) {
/* Don't error the I/O if this fails. */
! (void) smbfsflush(np, &scred);
}
}
smb_credrele(&scred);
smbfs_rw_exit(&np->r_lkserlock);
*** 773,785 ****
--- 846,1365 ----
/* undo adjustment of resid */
uiop->uio_resid += past_limit;
return (error);
+ }
+
+ #ifdef _KERNEL
+ /* (else) Do I/O through segmap. */
+ bsize = vp->v_vfsp->vfs_bsize;
+
+ do {
+ caddr_t base;
+ u_offset_t off;
+ size_t n;
+ int on;
+ uint_t flags;
+
+ off = uiop->uio_loffset & MAXBMASK; /* mapping offset */
+ on = uiop->uio_loffset & MAXBOFFSET; /* Relative offset */
+ n = MIN(MAXBSIZE - on, uiop->uio_resid);
+
+ last_resid = uiop->uio_resid;
+ last_off = uiop->uio_loffset;
+
+ if (np->r_flags & RSTALE) {
+ error = np->r_error;
+ /*
+ * A close may have cleared r_error, if so,
+ * propagate ESTALE error return properly
+ */
+ if (error == 0)
+ error = ESTALE;
+ break;
+ }
+
+ /*
+ * From NFS: Don't create dirty pages faster than they
+ * can be cleaned.
+ *
+ * Here NFS also checks for async writes (np->r_awcount)
+ */
+ mutex_enter(&np->r_statelock);
+ while (np->r_gcount > 0) {
+ if (SMBINTR(vp)) {
+ klwp_t *lwp = ttolwp(curthread);
+
+ if (lwp != NULL)
+ lwp->lwp_nostop++;
+ if (!cv_wait_sig(&np->r_cv, &np->r_statelock)) {
+ mutex_exit(&np->r_statelock);
+ if (lwp != NULL)
+ lwp->lwp_nostop--;
+ error = EINTR;
+ goto bottom;
+ }
+ if (lwp != NULL)
+ lwp->lwp_nostop--;
+ } else
+ cv_wait(&np->r_cv, &np->r_statelock);
+ }
+ mutex_exit(&np->r_statelock);
+
+ /*
+ * Touch the page and fault it in if it is not in core
+ * before segmap_getmapflt or vpm_data_copy can lock it.
+ * This is to avoid the deadlock if the buffer is mapped
+ * to the same file through mmap which we want to write.
+ */
+ uio_prefaultpages((long)n, uiop);
+
+ if (vpm_enable) {
+ /*
+ * It will use kpm mappings, so no need to
+ * pass an address.
+ */
+ error = smbfs_writenp(np, NULL, n, uiop, 0);
+ } else {
+ if (segmap_kpm) {
+ int pon = uiop->uio_loffset & PAGEOFFSET;
+ size_t pn = MIN(PAGESIZE - pon,
+ uiop->uio_resid);
+ int pagecreate;
+
+ mutex_enter(&np->r_statelock);
+ pagecreate = (pon == 0) && (pn == PAGESIZE ||
+ uiop->uio_loffset + pn >= np->r_size);
+ mutex_exit(&np->r_statelock);
+
+ base = segmap_getmapflt(segkmap, vp, off + on,
+ pn, !pagecreate, S_WRITE);
+
+ error = smbfs_writenp(np, base + pon, n, uiop,
+ pagecreate);
+
+ } else {
+ base = segmap_getmapflt(segkmap, vp, off + on,
+ n, 0, S_READ);
+ error = smbfs_writenp(np, base + on, n, uiop, 0);
+ }
+ }
+
+ if (!error) {
+ if (smi->smi_flags & SMI_NOAC)
+ flags = SM_WRITE;
+ else if ((uiop->uio_loffset % bsize) == 0 ||
+ IS_SWAPVP(vp)) {
+ /*
+ * Have written a whole block.
+ * Start an asynchronous write
+ * and mark the buffer to
+ * indicate that it won't be
+ * needed again soon.
+ */
+ flags = SM_WRITE | SM_ASYNC | SM_DONTNEED;
+ } else
+ flags = 0;
+ if ((ioflag & (FSYNC|FDSYNC)) ||
+ (np->r_flags & ROUTOFSPACE)) {
+ flags &= ~SM_ASYNC;
+ flags |= SM_WRITE;
+ }
+ if (vpm_enable) {
+ error = vpm_sync_pages(vp, off, n, flags);
+ } else {
+ error = segmap_release(segkmap, base, flags);
+ }
+ } else {
+ if (vpm_enable) {
+ (void) vpm_sync_pages(vp, off, n, 0);
+ } else {
+ (void) segmap_release(segkmap, base, 0);
+ }
+ /*
+ * In the event that we got an access error while
+ * faulting in a page for a write-only file just
+ * force a write.
+ */
+ if (error == EACCES)
+ goto smbfs_fwrite;
+ }
+ } while (!error && uiop->uio_resid > 0);
+ #else // _KERNEL
+ last_resid = uiop->uio_resid;
+ last_off = uiop->uio_loffset;
+ error = ENOSYS;
+ #endif // _KERNEL
+
+ bottom:
+ /* undo adjustment of resid */
+ if (error) {
+ uiop->uio_resid = last_resid + past_limit;
+ uiop->uio_loffset = last_off;
+ } else {
+ uiop->uio_resid += past_limit;
+ }
+
+ return (error);
}
+ #ifdef _KERNEL
+ /*
+ * Like nfs_client.c: writerp()
+ *
+ * Write by creating pages and uiomove data onto them.
+ */
+
+ int
+ smbfs_writenp(smbnode_t *np, caddr_t base, int tcount, struct uio *uio,
+ int pgcreated)
+ {
+ int pagecreate;
+ int n;
+ int saved_n;
+ caddr_t saved_base;
+ u_offset_t offset;
+ int error;
+ int sm_error;
+ vnode_t *vp = SMBTOV(np);
+
+ ASSERT(tcount <= MAXBSIZE && tcount <= uio->uio_resid);
+ ASSERT(smbfs_rw_lock_held(&np->r_rwlock, RW_WRITER));
+ if (!vpm_enable) {
+ ASSERT(((uintptr_t)base & MAXBOFFSET) + tcount <= MAXBSIZE);
+ }
+
+ /*
+ * Move bytes in at most PAGESIZE chunks. We must avoid
+ * spanning pages in uiomove() because page faults may cause
+ * the cache to be invalidated out from under us. The r_size is not
+ * updated until after the uiomove. If we push the last page of a
+ * file before r_size is correct, we will lose the data written past
+ * the current (and invalid) r_size.
+ */
+ do {
+ offset = uio->uio_loffset;
+ pagecreate = 0;
+
+ /*
+ * n is the number of bytes required to satisfy the request
+ * or the number of bytes to fill out the page.
+ */
+ n = (int)MIN((PAGESIZE - (offset & PAGEOFFSET)), tcount);
+
+ /*
+ * Check to see if we can skip reading in the page
+ * and just allocate the memory. We can do this
+ * if we are going to rewrite the entire mapping
+ * or if we are going to write to or beyond the current
+ * end of file from the beginning of the mapping.
+ *
+ * The read of r_size is now protected by r_statelock.
+ */
+ mutex_enter(&np->r_statelock);
+ /*
+ * When pgcreated is nonzero the caller has already done
+ * a segmap_getmapflt with forcefault 0 and S_WRITE. With
+ * segkpm this means we already have at least one page
+ * created and mapped at base.
+ */
+ pagecreate = pgcreated ||
+ ((offset & PAGEOFFSET) == 0 &&
+ (n == PAGESIZE || ((offset + n) >= np->r_size)));
+
+ mutex_exit(&np->r_statelock);
+ if (!vpm_enable && pagecreate) {
+ /*
+ * The last argument tells segmap_pagecreate() to
+ * always lock the page, as opposed to sometimes
+ * returning with the page locked. This way we avoid a
+ * fault on the ensuing uiomove(), but also
+ * more importantly (to fix bug 1094402) we can
+ * call segmap_fault() to unlock the page in all
+ * cases. An alternative would be to modify
+ * segmap_pagecreate() to tell us when it is
+ * locking a page, but that's a fairly major
+ * interface change.
+ */
+ if (pgcreated == 0)
+ (void) segmap_pagecreate(segkmap, base,
+ (uint_t)n, 1);
+ saved_base = base;
+ saved_n = n;
+ }
+
+ /*
+ * The number of bytes of data in the last page can not
+ * be accurately be determined while page is being
+ * uiomove'd to and the size of the file being updated.
+ * Thus, inform threads which need to know accurately
+ * how much data is in the last page of the file. They
+ * will not do the i/o immediately, but will arrange for
+ * the i/o to happen later when this modify operation
+ * will have finished.
+ */
+ ASSERT(!(np->r_flags & RMODINPROGRESS));
+ mutex_enter(&np->r_statelock);
+ np->r_flags |= RMODINPROGRESS;
+ np->r_modaddr = (offset & MAXBMASK);
+ mutex_exit(&np->r_statelock);
+
+ if (vpm_enable) {
+ /*
+ * Copy data. If new pages are created, part of
+ * the page that is not written will be initizliazed
+ * with zeros.
+ */
+ error = vpm_data_copy(vp, offset, n, uio,
+ !pagecreate, NULL, 0, S_WRITE);
+ } else {
+ error = uiomove(base, n, UIO_WRITE, uio);
+ }
+
+ /*
+ * r_size is the maximum number of
+ * bytes known to be in the file.
+ * Make sure it is at least as high as the
+ * first unwritten byte pointed to by uio_loffset.
+ */
+ mutex_enter(&np->r_statelock);
+ if (np->r_size < uio->uio_loffset)
+ np->r_size = uio->uio_loffset;
+ np->r_flags &= ~RMODINPROGRESS;
+ np->r_flags |= RDIRTY;
+ mutex_exit(&np->r_statelock);
+
+ /* n = # of bytes written */
+ n = (int)(uio->uio_loffset - offset);
+
+ if (!vpm_enable) {
+ base += n;
+ }
+ tcount -= n;
+ /*
+ * If we created pages w/o initializing them completely,
+ * we need to zero the part that wasn't set up.
+ * This happens on a most EOF write cases and if
+ * we had some sort of error during the uiomove.
+ */
+ if (!vpm_enable && pagecreate) {
+ if ((uio->uio_loffset & PAGEOFFSET) || n == 0)
+ (void) kzero(base, PAGESIZE - n);
+
+ if (pgcreated) {
+ /*
+ * Caller is responsible for this page,
+ * it was not created in this loop.
+ */
+ pgcreated = 0;
+ } else {
+ /*
+ * For bug 1094402: segmap_pagecreate locks
+ * page. Unlock it. This also unlocks the
+ * pages allocated by page_create_va() in
+ * segmap_pagecreate().
+ */
+ sm_error = segmap_fault(kas.a_hat, segkmap,
+ saved_base, saved_n,
+ F_SOFTUNLOCK, S_WRITE);
+ if (error == 0)
+ error = sm_error;
+ }
+ }
+ } while (tcount > 0 && error == 0);
+
+ return (error);
+ }
+
+ /*
+ * Flags are composed of {B_ASYNC, B_INVAL, B_FREE, B_DONTNEED}
+ * Like nfs3_rdwrlbn()
+ */
+ static int
+ smbfs_rdwrlbn(vnode_t *vp, page_t *pp, u_offset_t off, size_t len,
+ int flags, cred_t *cr)
+ {
+ smbmntinfo_t *smi = VTOSMI(vp);
+ struct buf *bp;
+ int error;
+ int sync;
+
+ if (curproc->p_zone != smi->smi_zone_ref.zref_zone)
+ return (EIO);
+
+ if (smi->smi_flags & SMI_DEAD || vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
+ bp = pageio_setup(pp, len, vp, flags);
+ ASSERT(bp != NULL);
+
+ /*
+ * pageio_setup should have set b_addr to 0. This
+ * is correct since we want to do I/O on a page
+ * boundary. bp_mapin will use this addr to calculate
+ * an offset, and then set b_addr to the kernel virtual
+ * address it allocated for us.
+ */
+ ASSERT(bp->b_un.b_addr == 0);
+
+ bp->b_edev = 0;
+ bp->b_dev = 0;
+ bp->b_lblkno = lbtodb(off);
+ bp->b_file = vp;
+ bp->b_offset = (offset_t)off;
+ bp_mapin(bp);
+
+ /*
+ * Calculate the desired level of stability to write data.
+ */
+ if ((flags & (B_WRITE|B_ASYNC)) == (B_WRITE|B_ASYNC) &&
+ freemem > desfree) {
+ sync = 0;
+ } else {
+ sync = 1;
+ }
+
+ error = smbfs_bio(bp, sync, cr);
+
+ bp_mapout(bp);
+ pageio_done(bp);
+
+ return (error);
+ }
+
+
+ /*
+ * Corresponds to nfs3_vnopc.c : nfs3_bio(), though the NFS code
+ * uses nfs3read()/nfs3write() where we use smb_rwuio(). Also,
+ * NFS has this later in the file. Move it up here closer to
+ * the one call site just above.
+ */
+
+ static int
+ smbfs_bio(struct buf *bp, int sync, cred_t *cr)
+ {
+ struct iovec aiov[1];
+ struct uio auio;
+ struct smb_cred scred;
+ smbnode_t *np = VTOSMB(bp->b_vp);
+ smbmntinfo_t *smi = np->n_mount;
+ offset_t offset;
+ offset_t endoff;
+ size_t count;
+ size_t past_eof;
+ int error;
+
+ ASSERT(curproc->p_zone == smi->smi_zone_ref.zref_zone);
+
+ offset = ldbtob(bp->b_lblkno);
+ count = bp->b_bcount;
+ endoff = offset + count;
+ if (offset < 0 || endoff < 0)
+ return (EINVAL);
+
+ /*
+ * Limit file I/O to the remaining file size, but see
+ * the notes in smbfs_getpage about SMBFS_EOF.
+ */
+ mutex_enter(&np->r_statelock);
+ if (offset >= np->r_size) {
+ mutex_exit(&np->r_statelock);
+ if (bp->b_flags & B_READ) {
+ return (SMBFS_EOF);
+ } else {
+ return (EINVAL);
+ }
+ }
+ if (endoff > np->r_size) {
+ past_eof = (size_t)(endoff - np->r_size);
+ count -= past_eof;
+ } else
+ past_eof = 0;
+ mutex_exit(&np->r_statelock);
+ ASSERT(count > 0);
+
+ /* Caller did bpmapin(). Mapped address is... */
+ aiov[0].iov_base = bp->b_un.b_addr;
+ aiov[0].iov_len = count;
+ auio.uio_iov = aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_loffset = offset;
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_fmode = 0;
+ auio.uio_resid = count;
+
+ /* Shared lock for n_fid use in smb_rwuio */
+ if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER,
+ smi->smi_flags & SMI_INT))
+ return (EINTR);
+ smb_credinit(&scred, cr);
+
+ DTRACE_IO1(start, struct buf *, bp);
+
+ if (bp->b_flags & B_READ) {
+
+ error = smb_rwuio(np->n_fid, UIO_READ,
+ &auio, &scred, smb_timo_read);
+
+ /* Like NFS, only set b_error here. */
+ bp->b_error = error;
+ bp->b_resid = auio.uio_resid;
+
+ if (!error && auio.uio_resid != 0)
+ error = EIO;
+ if (!error && past_eof != 0) {
+ /* Zero the memory beyond EOF. */
+ bzero(bp->b_un.b_addr + count, past_eof);
+ }
+ } else {
+
+ error = smb_rwuio(np->n_fid, UIO_WRITE,
+ &auio, &scred, smb_timo_write);
+
+ /* Like NFS, only set b_error here. */
+ bp->b_error = error;
+ bp->b_resid = auio.uio_resid;
+
+ if (!error && auio.uio_resid != 0)
+ error = EIO;
+ if (!error && sync) {
+ (void) smbfsflush(np, &scred);
+ }
+ }
+
+ /*
+ * This comes from nfs3_commit()
+ */
+ if (error != 0) {
+ mutex_enter(&np->r_statelock);
+ if (error == ESTALE)
+ np->r_flags |= RSTALE;
+ if (!np->r_error)
+ np->r_error = error;
+ mutex_exit(&np->r_statelock);
+ bp->b_flags |= B_ERROR;
+ }
+
+ DTRACE_IO1(done, struct buf *, bp);
+
+ smb_credrele(&scred);
+ smbfs_rw_exit(&np->r_lkserlock);
+
+ if (error == ESTALE)
+ smbfs_attrcache_remove(np);
+
+ return (error);
+ }
+ #endif // _KERNEL
+
+ /*
+ * Here NFS has: nfs3write, nfs3read
+ * We use smb_rwuio instead.
+ */
+
/* ARGSUSED */
static int
smbfs_ioctl(vnode_t *vp, int cmd, intptr_t arg, int flag,
cred_t *cr, int *rvalp, caller_context_t *ct)
{
*** 793,803 ****
if (smi->smi_flags & SMI_DEAD || vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
return (EIO);
switch (cmd) {
- /* First three from ZFS. XXX - need these? */
case _FIOFFS:
error = smbfs_fsync(vp, 0, cr, ct);
break;
--- 1373,1382 ----
*** 808,821 ****
case _FIOGDIO:
case _FIOSDIO:
error = 0;
break;
! #ifdef NOT_YET /* XXX - from the NFS code. */
case _FIODIRECTIO:
error = smbfs_directio(vp, (int)arg, cr);
! #endif
/*
* Allow get/set with "raw" security descriptor (SD) data.
* Useful for testing, diagnosing idmap problems, etc.
*/
--- 1387,1404 ----
case _FIOGDIO:
case _FIOSDIO:
error = 0;
break;
! #if 0 /* Todo - SMB ioctl query regions */
! case _FIO_SEEK_DATA:
! case _FIO_SEEK_HOLE:
! #endif
!
case _FIODIRECTIO:
error = smbfs_directio(vp, (int)arg, cr);
! break;
/*
* Allow get/set with "raw" security descriptor (SD) data.
* Useful for testing, diagnosing idmap problems, etc.
*/
*** 845,854 ****
--- 1428,1438 ----
smbfs_getattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr,
caller_context_t *ct)
{
smbnode_t *np;
smbmntinfo_t *smi;
+ int error;
smi = VTOSMI(vp);
if (curproc->p_zone != smi->smi_zone_ref.zref_zone)
return (EIO);
*** 879,888 ****
--- 1463,1496 ----
mutex_exit(&np->r_statelock);
return (0);
}
}
+ /*
+ * Only need to flush pages if asking for the mtime
+ * and if there any dirty pages.
+ *
+ * Here NFS also checks for async writes (np->r_awcount)
+ */
+ if (vap->va_mask & AT_MTIME) {
+ if (vn_has_cached_data(vp) &&
+ ((np->r_flags & RDIRTY) != 0)) {
+ mutex_enter(&np->r_statelock);
+ np->r_gcount++;
+ mutex_exit(&np->r_statelock);
+ error = smbfs_putpage(vp, (offset_t)0, 0, 0, cr, ct);
+ mutex_enter(&np->r_statelock);
+ if (error && (error == ENOSPC || error == EDQUOT)) {
+ if (!np->r_error)
+ np->r_error = error;
+ }
+ if (--np->r_gcount == 0)
+ cv_broadcast(&np->r_cv);
+ mutex_exit(&np->r_statelock);
+ }
+ }
+
return (smbfsgetattr(vp, vap, cr));
}
/* smbfsgetattr() in smbfs_client.c */
*** 949,959 ****
* the rest of the setattr work.
*/
}
}
! return (smbfssetattr(vp, vap, flags, cr));
}
/*
* Mostly from Darwin smbfs_setattr()
* but then modified a lot.
--- 1557,1574 ----
* the rest of the setattr work.
*/
}
}
! error = smbfssetattr(vp, vap, flags, cr);
!
! #ifdef SMBFS_VNEVENT
! if (error == 0 && (vap->va_mask & AT_SIZE) && vap->va_size == 0)
! vnevent_truncate(vp, ct);
! #endif
!
! return (error);
}
/*
* Mostly from Darwin smbfs_setattr()
* but then modified a lot.
*** 962,977 ****
static int
smbfssetattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr)
{
int error = 0;
smbnode_t *np = VTOSMB(vp);
uint_t mask = vap->va_mask;
struct timespec *mtime, *atime;
struct smb_cred scred;
! int cerror, modified = 0;
! unsigned short fid;
! int have_fid = 0;
uint32_t rights = 0;
uint32_t dosattr = 0;
ASSERT(curproc->p_zone == VTOSMI(vp)->smi_zone_ref.zref_zone);
--- 1577,1592 ----
static int
smbfssetattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr)
{
int error = 0;
smbnode_t *np = VTOSMB(vp);
+ smbmntinfo_t *smi = np->n_mount;
uint_t mask = vap->va_mask;
struct timespec *mtime, *atime;
struct smb_cred scred;
! int modified = 0;
! smb_fh_t *fid = NULL;
uint32_t rights = 0;
uint32_t dosattr = 0;
ASSERT(curproc->p_zone == VTOSMI(vp)->smi_zone_ref.zref_zone);
*** 987,1006 ****
SMBVDEBUG("ignore set time on xattr\n");
mask &= AT_SIZE;
}
/*
* 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. */
- if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp)))
- return (EINTR);
smb_credinit(&scred, cr);
/*
* If the caller has provided extensible attributes,
* map those into DOS attributes supported by SMB.
--- 1602,1643 ----
SMBVDEBUG("ignore set time on xattr\n");
mask &= AT_SIZE;
}
/*
+ * Only need to flush pages if there are any pages and
+ * if the file is marked as dirty in some fashion. The
+ * file must be flushed so that we can accurately
+ * determine the size of the file and the cached data
+ * after the SETATTR returns. A file is considered to
+ * be dirty if it is either marked with RDIRTY, has
+ * outstanding i/o's active, or is mmap'd. In this
+ * last case, we can't tell whether there are dirty
+ * pages, so we flush just to be sure.
+ */
+ if (vn_has_cached_data(vp) &&
+ ((np->r_flags & RDIRTY) ||
+ np->r_count > 0 ||
+ np->r_mapcnt > 0)) {
+ ASSERT(vp->v_type != VCHR);
+ error = smbfs_putpage(vp, (offset_t)0, 0, 0, cr, NULL);
+ if (error && (error == ENOSPC || error == EDQUOT)) {
+ mutex_enter(&np->r_statelock);
+ if (!np->r_error)
+ np->r_error = error;
+ mutex_exit(&np->r_statelock);
+ }
+ }
+
+ /*
* 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.
*/
smb_credinit(&scred, cr);
/*
* If the caller has provided extensible attributes,
* map those into DOS attributes supported by SMB.
*** 1034,1044 ****
if (error) {
SMBVDEBUG("error %d opening %s\n",
error, np->n_rpath);
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.
--- 1671,1681 ----
if (error) {
SMBVDEBUG("error %d opening %s\n",
error, np->n_rpath);
goto out;
}
! ASSERT(fid != NULL);
}
/*
* If the server supports the UNIX extensions, right here is where
* we'd support changes to uid, gid, mode, and possibly va_flags.
*** 1048,1099 ****
if (mask & AT_SIZE) {
/*
* 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.
*/
! ASSERT(have_fid);
! error = smbfs_smb_setfsize(np, fid, vap->va_size, &scred);
if (error) {
SMBVDEBUG("setsize error %d file %s\n",
error, np->n_rpath);
} 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.
*/
mutex_enter(&np->r_statelock);
np->r_size = vap->va_size;
mutex_exit(&np->r_statelock);
modified = 1;
}
}
/*
! * XXX: When Solaris has create_time, set that too.
! * Note: create_time is different from ctime.
*/
mtime = ((mask & AT_MTIME) ? &vap->va_mtime : 0);
atime = ((mask & AT_ATIME) ? &vap->va_atime : 0);
if (dosattr || mtime || atime) {
/*
* Always use the handle-based set attr call now.
*/
! ASSERT(have_fid);
! error = smbfs_smb_setfattr(np, fid,
dosattr, mtime, atime, &scred);
if (error) {
SMBVDEBUG("set times error %d file %s\n",
error, np->n_rpath);
} else {
--- 1685,1732 ----
if (mask & AT_SIZE) {
/*
* 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.
*/
/*
* Set the file size to vap->va_size.
*/
! ASSERT(fid != NULL);
! error = smbfs_smb_setfsize(smi->smi_share, fid,
! vap->va_size, &scred);
if (error) {
SMBVDEBUG("setsize error %d file %s\n",
error, np->n_rpath);
} else {
/*
* Darwin had code here to zero-extend.
* Tests indicate the server will zero-fill,
! * so looks like we don't need to do that.
*/
mutex_enter(&np->r_statelock);
np->r_size = vap->va_size;
+ np->n_flag |= (NFLUSHWIRE | NATTRCHANGED);
mutex_exit(&np->r_statelock);
modified = 1;
}
}
/*
! * Todo: Implement setting create_time (which is
! * different from ctime).
*/
mtime = ((mask & AT_MTIME) ? &vap->va_mtime : 0);
atime = ((mask & AT_ATIME) ? &vap->va_atime : 0);
if (dosattr || mtime || atime) {
/*
* Always use the handle-based set attr call now.
*/
! ASSERT(fid != NULL);
! error = smbfs_smb_setfattr(smi->smi_share, fid,
dosattr, mtime, atime, &scred);
if (error) {
SMBVDEBUG("set times error %d file %s\n",
error, np->n_rpath);
} else {
*** 1100,1127 ****
modified = 1;
}
}
out:
if (modified) {
/*
* Invalidate attribute cache in case the server
* doesn't set exactly the attributes we asked.
*/
smbfs_attrcache_remove(np);
- }
! if (have_fid) {
! cerror = smbfs_smb_tmpclose(np, fid, &scred);
! if (cerror)
! SMBVDEBUG("error %d closing %s\n",
! cerror, np->n_rpath);
}
- smb_credrele(&scred);
- smbfs_rw_exit(&np->r_lkserlock);
-
return (error);
}
/*
* Helper function for extensible system attributes (PSARC 2007/315)
--- 1733,1772 ----
modified = 1;
}
}
out:
+ if (fid != NULL)
+ smbfs_smb_tmpclose(np, fid);
+
+ smb_credrele(&scred);
+
if (modified) {
/*
* Invalidate attribute cache in case the server
* doesn't set exactly the attributes we asked.
*/
smbfs_attrcache_remove(np);
! /*
! * If changing the size of the file, invalidate
! * any local cached data which is no longer part
! * of the file. We also possibly invalidate the
! * last page in the file. We could use
! * pvn_vpzero(), but this would mark the page as
! * modified and require it to be written back to
! * the server for no particularly good reason.
! * This way, if we access it, then we bring it
! * back in. A read should be cheaper than a
! * write.
! */
! if (mask & AT_SIZE) {
! smbfs_invalidate_pages(vp,
! (vap->va_size & PAGEMASK), cr);
}
+ }
return (error);
}
/*
* Helper function for extensible system attributes (PSARC 2007/315)
*** 1204,1217 ****
*
* 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
smbfs_access_rwx(vfs_t *vfsp, int vtype, int mode, cred_t *cr)
{
/* See the secpolicy call below. */
--- 1849,1858 ----
*** 1222,1233 ****
struct smbmntinfo *smi = VFTOSMI(vfsp);
int shift = 0;
/*
* Build our (fabricated) vnode attributes.
- * XXX: Could make these templates in the
- * per-mount struct and use them here.
*/
bzero(&va, sizeof (va));
va.va_mask = AT_TYPE | AT_MODE | AT_UID | AT_GID;
va.va_type = vtype;
va.va_mode = (vtype == VDIR) ?
--- 1863,1872 ----
*** 1248,1258 ****
return (EROFS);
/*
* Disallow attempts to access mandatory lock files.
* Similarly, expand MANDLOCK here.
- * XXX: not sure we need this.
*/
if ((mode & (VWRITE | VREAD | VEXEC)) &&
va.va_type == VREG && MANDMODE(va.va_mode))
return (EACCES);
--- 1887,1896 ----
*** 1318,1327 ****
--- 1956,1974 ----
return (smbfs_access_rwx(vfsp, vp->v_type, mode, cr));
}
+ /* ARGSUSED */
+ static int
+ smbfs_readlink(vnode_t *vp, struct uio *uiop, cred_t *cr, caller_context_t *ct)
+ {
+ /* Not yet... */
+ return (ENOSYS);
+ }
+
+
/*
* 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
*** 1349,1398 ****
return (0);
if ((syncflag & (FSYNC|FDSYNC)) == 0)
return (0);
/* Shared lock for n_fid use in _flush */
if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp)))
return (EINTR);
smb_credinit(&scred, cr);
! error = smbfs_smb_flush(np, &scred);
smb_credrele(&scred);
smbfs_rw_exit(&np->r_lkserlock);
return (error);
}
/*
* Last reference to vnode went away.
*/
/* ARGSUSED */
static void
smbfs_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
{
- smbnode_t *np;
struct smb_cred scred;
/*
* Don't "bail out" for VFS_UNMOUNTED here,
* as we want to do cleanup, etc.
* See also pcfs_inactive
*/
- np = VTOSMB(vp);
-
/*
* 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.
--- 1996,2112 ----
return (0);
if ((syncflag & (FSYNC|FDSYNC)) == 0)
return (0);
+ error = smbfs_putpage(vp, (offset_t)0, 0, 0, cr, ct);
+ if (error)
+ return (error);
+
/* Shared lock for n_fid use in _flush */
if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp)))
return (EINTR);
smb_credinit(&scred, cr);
! error = smbfsflush(np, &scred);
smb_credrele(&scred);
smbfs_rw_exit(&np->r_lkserlock);
return (error);
}
+ static int
+ smbfsflush(smbnode_t *np, struct smb_cred *scrp)
+ {
+ struct smb_share *ssp = np->n_mount->smi_share;
+ smb_fh_t *fhp;
+ int error;
+
+ /* Shared lock for n_fid use below. */
+ ASSERT(smbfs_rw_lock_held(&np->r_lkserlock, RW_READER));
+
+ if (!(np->n_flag & NFLUSHWIRE))
+ return (0);
+ if (np->n_fidrefs == 0)
+ return (0); /* not open */
+ if ((fhp = np->n_fid) == NULL)
+ return (0);
+
+ /* After reconnect, n_fid is invalid */
+ if (fhp->fh_vcgenid != ssp->ss_vcgenid)
+ return (ESTALE);
+
+ error = smbfs_smb_flush(ssp, fhp, scrp);
+
+ if (!error) {
+ mutex_enter(&np->r_statelock);
+ np->n_flag &= ~NFLUSHWIRE;
+ mutex_exit(&np->r_statelock);
+ }
+ return (error);
+ }
+
/*
* Last reference to vnode went away.
*/
/* ARGSUSED */
static void
smbfs_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
{
struct smb_cred scred;
+ smbnode_t *np = VTOSMB(vp);
+ int error;
/*
* 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.
*/
/*
+ * From NFS:rinactive()
+ *
+ * Before freeing anything, wait until all asynchronous
+ * activity is done on this rnode. This will allow all
+ * asynchronous read ahead and write behind i/o's to
+ * finish.
+ */
+ mutex_enter(&np->r_statelock);
+ while (np->r_count > 0)
+ cv_wait(&np->r_cv, &np->r_statelock);
+ mutex_exit(&np->r_statelock);
+
+ /*
+ * Flush and invalidate all pages associated with the vnode.
+ */
+ if (vn_has_cached_data(vp)) {
+ if ((np->r_flags & RDIRTY) && !np->r_error) {
+ error = smbfs_putpage(vp, (u_offset_t)0, 0, 0, cr, ct);
+ if (error && (error == ENOSPC || error == EDQUOT)) {
+ mutex_enter(&np->r_statelock);
+ if (!np->r_error)
+ np->r_error = error;
+ mutex_exit(&np->r_statelock);
+ }
+ }
+ smbfs_invalidate_pages(vp, (u_offset_t)0, cr);
+ }
+ /*
+ * This vnode should have lost all cached data.
+ */
+ ASSERT(vn_has_cached_data(vp) == 0);
+
+ /*
* 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.
*** 1419,1430 ****
break;
case VREG:
if (np->n_fidrefs == 0)
break;
! SMBVDEBUG("open file: refs %d id 0x%x path %s\n",
! np->n_fidrefs, np->n_fid, np->n_rpath);
/* Force last close. */
np->n_fidrefs = 1;
smbfs_rele_fid(np, &scred);
break;
--- 2133,2144 ----
break;
case VREG:
if (np->n_fidrefs == 0)
break;
! SMBVDEBUG("open file: refs %d path %s\n",
! np->n_fidrefs, np->n_rpath);
/* Force last close. */
np->n_fidrefs = 1;
smbfs_rele_fid(np, &scred);
break;
*** 1435,1444 ****
--- 2149,2169 ----
}
smb_credrele(&scred);
smbfs_rw_exit(&np->r_lkserlock);
+ /*
+ * XATTR directories (and the files under them) have
+ * little value for reclaim, so just remove them from
+ * the "hash" (AVL) as soon as they go inactive.
+ * Note that the node may already have been removed
+ * from the hash by smbfsremove.
+ */
+ if ((np->n_flag & N_XATTR) != 0 &&
+ (np->r_flags & RHASHED) != 0)
+ smbfs_rmhash(np);
+
smbfs_addfree(np);
}
/*
* Remote file system operations having to do with directory manipulation.
*** 1487,1496 ****
--- 2212,2229 ----
error = smbfslookup(dvp, nm, vpp, cr, 1, ct);
smbfs_rw_exit(&dnp->r_rwlock);
+ /*
+ * If the caller passes an invalid name here, we'll have
+ * error == EINVAL but want to return ENOENT. This is
+ * common with things like "ls foo*" with no matches.
+ */
+ if (error == EINVAL)
+ error = ENOENT;
+
return (error);
}
/* ARGSUSED */
static int
*** 1514,1536 ****
smi = VTOSMI(dvp);
dnp = VTOSMB(dvp);
ASSERT(curproc->p_zone == smi->smi_zone_ref.zref_zone);
- #ifdef NOT_YET
- vcp = SSTOVC(smi->smi_share);
-
- /* XXX: Should compute this once and store it in smbmntinfo_t */
- supplen = (SMB_DIALECT(vcp) >= SMB_DIALECT_LANMAN2_0) ? 255 : 12;
- #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?
*/
ASSERT(dnp->r_rwlock.count != 0);
/*
* If lookup is for "", just return dvp.
--- 2247,2260 ----
*** 1573,1583 ****
return (ENAMETOOLONG);
/*
* Avoid surprises with characters that are
* illegal in Windows file names.
! * Todo: CATIA mappings XXX
*/
ill = illegal_chars;
if (dnp->n_flag & N_XATTR)
ill++; /* allow colon */
if (strpbrk(nm, ill))
--- 2297,2307 ----
return (ENAMETOOLONG);
/*
* Avoid surprises with characters that are
* illegal in Windows file names.
! * Todo: CATIA mappings?
*/
ill = illegal_chars;
if (dnp->n_flag & N_XATTR)
ill++; /* allow colon */
if (strpbrk(nm, ill))
*** 1794,1803 ****
--- 2518,2528 ----
#endif
*vpp = vp;
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.
*
*** 1809,1833 ****
smbfs_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive,
int mode, vnode_t **vpp, cred_t *cr, int lfaware, caller_context_t *ct,
vsecattr_t *vsecp)
{
int error;
- int cerror;
vfs_t *vfsp;
vnode_t *vp;
- #ifdef NOT_YET
smbnode_t *np;
- #endif
smbnode_t *dnp;
smbmntinfo_t *smi;
struct vattr vattr;
struct smbfattr fattr;
struct smb_cred scred;
const char *name = (const char *)nm;
int nmlen = strlen(nm);
uint32_t disp;
! uint16_t fid;
int xattr;
vfsp = dvp->v_vfsp;
smi = VFTOSMI(vfsp);
dnp = VTOSMB(dvp);
--- 2534,2555 ----
smbfs_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive,
int mode, vnode_t **vpp, cred_t *cr, int lfaware, caller_context_t *ct,
vsecattr_t *vsecp)
{
int error;
vfs_t *vfsp;
vnode_t *vp;
smbnode_t *np;
smbnode_t *dnp;
smbmntinfo_t *smi;
struct vattr vattr;
struct smbfattr fattr;
struct smb_cred scred;
const char *name = (const char *)nm;
int nmlen = strlen(nm);
uint32_t disp;
! smb_fh_t *fid = NULL;
int xattr;
vfsp = dvp->v_vfsp;
smi = VFTOSMI(vfsp);
dnp = VTOSMB(dvp);
*** 1840,1850 ****
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.
*/
if (va->va_type != VREG)
return (EINVAL);
/*
--- 2562,2572 ----
return (EIO);
/*
* Note: this may break mknod(2) calls to create a directory,
* but that's obscure use. Some other filesystems do this.
! * Todo: redirect VDIR type here to _mkdir.
*/
if (va->va_type != VREG)
return (EINVAL);
/*
*** 1871,1885 ****
if (smbfs_rw_enter_sig(&dnp->r_rwlock, RW_WRITER, SMBINTR(dvp)))
return (EINTR);
smb_credinit(&scred, cr);
/*
- * 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
--- 2593,2602 ----
*** 1910,1931 ****
}
/*
* Truncate (if requested).
*/
! if ((vattr.va_mask & AT_SIZE) && vattr.va_size == 0) {
vattr.va_mask = AT_SIZE;
error = smbfssetattr(vp, &vattr, 0, cr);
if (error) {
VN_RELE(vp);
goto out;
}
! }
! /* Success! */
! #ifdef NOT_YET
vnevent_create(vp, ct);
#endif
*vpp = vp;
goto out;
}
/*
--- 2627,2665 ----
}
/*
* Truncate (if requested).
*/
! if ((vattr.va_mask & AT_SIZE) && vp->v_type == VREG) {
! np = VTOSMB(vp);
! /*
! * Check here for large file truncation by
! * LF-unaware process, like ufs_create().
! */
! if (!(lfaware & FOFFMAX)) {
! mutex_enter(&np->r_statelock);
! if (np->r_size > MAXOFF32_T)
! error = EOVERFLOW;
! mutex_exit(&np->r_statelock);
! }
! if (error) {
! VN_RELE(vp);
! goto out;
! }
vattr.va_mask = AT_SIZE;
error = smbfssetattr(vp, &vattr, 0, cr);
if (error) {
VN_RELE(vp);
goto out;
}
! #ifdef SMBFS_VNEVENT
! /* Existing file was truncated */
vnevent_create(vp, ct);
#endif
+ /* invalidate pages done in smbfssetattr() */
+ }
+ /* Success! */
*vpp = vp;
goto out;
}
/*
*** 1970,2018 ****
disp, &scred, &fid);
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 */
- if ((vattr.va_mask & AT_SIZE) &&
- vp->v_type == VREG) {
- np = VTOSMB(vp);
- /*
- * Check here for large file handled
- * by LF-unaware process (as
- * ufs_create() does)
- */
- if (!(lfaware & FOFFMAX)) {
- mutex_enter(&np->r_statelock);
- if (np->r_size > MAXOFF32_T)
- error = EOVERFLOW;
- mutex_exit(&np->r_statelock);
- }
- if (!error) {
- vattr.va_mask = AT_SIZE;
- error = smbfssetattr(vp,
- &vattr, 0, cr);
- }
- }
- #endif /* XXX */
- /*
* Should use the fid to get/set the size
* while we have it opened here. See above.
*/
- cerror = smbfs_smb_close(smi->smi_share, fid, NULL, &scred);
- if (cerror)
- SMBVDEBUG("error %d closing %s\\%s\n",
- cerror, dnp->n_rpath, name);
-
/*
* 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.
*
--- 2704,2718 ----
disp, &scred, &fid);
if (error)
goto out;
/*
* Should use the fid to get/set the size
* while we have it opened here. See above.
*/
+ smbfs_smb_close(fid);
/*
* 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.
*
*** 2029,2040 ****
error = smbfs_nget(dvp, name, nmlen, &fattr, &vp);
if (error)
goto out;
- /* XXX invalidate pages if we truncated? */
-
/* Success! */
*vpp = vp;
error = 0;
out:
--- 2729,2738 ----
*** 2053,2154 ****
/* ARGSUSED */
static int
smbfs_remove(vnode_t *dvp, char *nm, cred_t *cr, caller_context_t *ct,
int flags)
{
- int error;
- vnode_t *vp;
- smbnode_t *np;
- smbnode_t *dnp;
struct smb_cred scred;
! /* enum smbfsstat status; */
! smbmntinfo_t *smi;
- smi = VTOSMI(dvp);
-
if (curproc->p_zone != smi->smi_zone_ref.zref_zone)
return (EPERM);
if (smi->smi_flags & SMI_DEAD || dvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
return (EIO);
- dnp = VTOSMB(dvp);
- if (smbfs_rw_enter_sig(&dnp->r_rwlock, RW_WRITER, SMBINTR(dvp)))
- return (EINTR);
- smb_credinit(&scred, cr);
-
/*
* Verify access to the dirctory.
*/
error = smbfs_access(dvp, VWRITE|VEXEC, 0, cr, ct);
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.
! */
error = smbfslookup(dvp, nm, &vp, cr, 0, ct);
! if (error)
goto out;
! /* Never allow link/unlink directories on CIFS. */
if (vp->v_type == VDIR) {
- VN_RELE(vp);
error = EPERM;
goto out;
}
/*
! * Now we have the real reference count on the vnode
! * Do we have the file open?
*/
! np = VTOSMB(vp);
! mutex_enter(&np->r_statelock);
! if ((vp->v_count > 1) && (np->n_fidrefs > 0)) {
! /*
! * 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.
*/
mutex_exit(&np->r_statelock);
! error = EBUSY;
! } else {
! smbfs_attrcache_rm_locked(np);
! mutex_exit(&np->r_statelock);
! error = smbfs_smb_delete(np, &scred, NULL, 0, 0);
/*
! * If the file should no longer exist, discard
! * any cached attributes under this node.
*/
! switch (error) {
! case 0:
! case ENOENT:
! case ENOTDIR:
! smbfs_attrcache_prune(np);
! break;
}
}
! VN_RELE(vp);
out:
! smb_credrele(&scred);
! smbfs_rw_exit(&dnp->r_rwlock);
return (error);
}
/*
* XXX
* This op should support the new FIGNORECASE flag for case-insensitive
* lookups, per PSARC 2007/244.
*/
--- 2751,2963 ----
/* ARGSUSED */
static int
smbfs_remove(vnode_t *dvp, char *nm, cred_t *cr, caller_context_t *ct,
int flags)
{
struct smb_cred scred;
! vnode_t *vp = NULL;
! smbnode_t *dnp = VTOSMB(dvp);
! smbmntinfo_t *smi = VTOSMI(dvp);
! int error;
if (curproc->p_zone != smi->smi_zone_ref.zref_zone)
return (EPERM);
if (smi->smi_flags & SMI_DEAD || dvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
return (EIO);
/*
* Verify access to the dirctory.
*/
error = smbfs_access(dvp, VWRITE|VEXEC, 0, cr, ct);
if (error)
! return (error);
! if (smbfs_rw_enter_sig(&dnp->r_rwlock, RW_WRITER, SMBINTR(dvp)))
! return (EINTR);
! smb_credinit(&scred, cr);
!
! /* Lookup the file to remove. */
error = smbfslookup(dvp, nm, &vp, cr, 0, ct);
! if (error != 0)
goto out;
! /* Don't allow unlink of a directory. */
if (vp->v_type == VDIR) {
error = EPERM;
goto out;
}
/*
! * Do the real remove work
*/
! error = smbfsremove(dvp, vp, &scred, flags);
! if (error != 0)
! goto out;
!
! #ifdef SMBFS_VNEVENT
! vnevent_remove(vp, dvp, nm, ct);
! #endif
!
! out:
! if (vp != NULL)
! VN_RELE(vp);
!
! smb_credrele(&scred);
! smbfs_rw_exit(&dnp->r_rwlock);
!
! return (error);
! }
!
! /*
! * smbfsremove does the real work of removing in SMBFS
! * Caller has done dir access checks etc.
*
! * The normal way to delete a file over SMB is open it (with DELETE access),
! * set the "delete-on-close" flag, and close the file. The problem for Unix
! * applications is that they expect the file name to be gone once the unlink
! * completes, and the SMB server does not actually delete the file until ALL
! * opens of that file are closed. We can't assume our open handles are the
! * only open handles on a file we're deleting, so to be safe we'll try to
! * rename the file to a temporary name and then set delete-on-close. If we
! * fail to set delete-on-close (i.e. because other opens prevent it) then
! * undo the changes we made and give up with EBUSY. Note that we might have
! * permission to delete a file but lack permission to rename, so we want to
! * continue in cases where rename fails. As an optimization, only do the
! * rename when we have the file open.
! *
! * This is similar to what NFS does when deleting a file that has local opens,
! * but thanks to SMB delete-on-close, we don't need to keep track of when the
! * last local open goes away and send a delete. The server does that for us.
*/
+ /* ARGSUSED */
+ static int
+ smbfsremove(vnode_t *dvp, vnode_t *vp, struct smb_cred *scred,
+ int flags)
+ {
+ smbnode_t *dnp = VTOSMB(dvp);
+ smbnode_t *np = VTOSMB(vp);
+ smbmntinfo_t *smi = np->n_mount;
+ char *tmpname = NULL;
+ int tnlen;
+ int error;
+ smb_fh_t *fid = NULL;
+ boolean_t renamed = B_FALSE;
+
+ /*
+ * The dvp RWlock must be held as writer.
+ */
+ ASSERT(dnp->r_rwlock.owner == curthread);
+
+ /*
+ * We need to flush any dirty pages which happen to
+ * be hanging around before removing the file. This
+ * shouldn't happen very often and mostly on file
+ * systems mounted "nocto".
+ */
+ if (vn_has_cached_data(vp) &&
+ ((np->r_flags & RDIRTY) || np->r_count > 0)) {
+ error = smbfs_putpage(vp, (offset_t)0, 0, 0,
+ scred->scr_cred, NULL);
+ if (error && (error == ENOSPC || error == EDQUOT)) {
+ mutex_enter(&np->r_statelock);
+ if (!np->r_error)
+ np->r_error = error;
mutex_exit(&np->r_statelock);
! }
! }
! /*
! * Get a file handle with delete access.
! * Close this FID before return.
! */
! error = smbfs_smb_tmpopen(np, STD_RIGHT_DELETE_ACCESS,
! scred, &fid);
! if (error) {
! SMBVDEBUG("error %d opening %s\n",
! error, np->n_rpath);
! goto out;
! }
! ASSERT(fid != NULL);
/*
! * If we have the file open, try to rename it to a temporary name.
! * If we can't rename, continue on and try setting DoC anyway.
! * Unnecessary for directories.
*/
! if (vp->v_type != VDIR && vp->v_count > 1 && np->n_fidrefs > 0) {
! tmpname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
! tnlen = smbfs_newname(tmpname, MAXNAMELEN);
! error = smbfs_smb_rename(dnp, np, dnp, tmpname, tnlen,
! fid, scred);
! if (error != 0) {
! SMBVDEBUG("error %d renaming %s -> %s\n",
! error, np->n_rpath, tmpname);
! /* Keep going without the rename. */
! } else {
! renamed = B_TRUE;
}
}
! /*
! * Mark the file as delete-on-close. If we can't,
! * undo what we did and err out.
! */
! error = smbfs_smb_setdisp(smi->smi_share, fid, 1, scred);
! if (error != 0) {
! SMBVDEBUG("error %d setting DoC on %s\n",
! error, np->n_rpath);
! /*
! * Failed to set DoC. If we renamed, undo that.
! * Need np->n_rpath relative to parent (dnp).
! * Use parent path name length plus one for
! * the separator ('/' or ':')
! */
! if (renamed) {
! char *oldname;
! int oldnlen;
! int err2;
+ oldname = np->n_rpath + (dnp->n_rplen + 1);
+ oldnlen = np->n_rplen - (dnp->n_rplen + 1);
+ err2 = smbfs_smb_rename(dnp, np, dnp, oldname, oldnlen,
+ fid, scred);
+ SMBVDEBUG("error %d un-renaming %s -> %s\n",
+ err2, tmpname, np->n_rpath);
+ }
+ error = EBUSY;
+ goto out;
+ }
+ /* Done! */
+ smbfs_attrcache_remove(np);
+ smbfs_attrcache_prune(np);
+
out:
! if (tmpname != NULL)
! kmem_free(tmpname, MAXNAMELEN);
! if (fid != NULL)
! smbfs_smb_tmpclose(np, fid);
+ if (error == 0) {
+ /* Keep lookup from finding this node anymore. */
+ smbfs_rmhash(np);
+ }
+
return (error);
}
+ /* ARGSUSED */
+ static int
+ smbfs_link(vnode_t *tdvp, vnode_t *svp, char *tnm, cred_t *cr,
+ caller_context_t *ct, int flags)
+ {
+ /* Not yet... */
+ return (ENOSYS);
+ }
+
+
/*
* XXX
* This op should support the new FIGNORECASE flag for case-insensitive
* lookups, per PSARC 2007/244.
*/
*** 2155,2165 ****
/* ARGSUSED */
static int
smbfs_rename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr,
caller_context_t *ct, int flags)
{
! /* vnode_t *realvp; */
if (curproc->p_zone != VTOSMI(odvp)->smi_zone_ref.zref_zone ||
curproc->p_zone != VTOSMI(ndvp)->smi_zone_ref.zref_zone)
return (EPERM);
--- 2964,2978 ----
/* ARGSUSED */
static int
smbfs_rename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr,
caller_context_t *ct, int flags)
{
! struct smb_cred scred;
! smbnode_t *odnp = VTOSMB(odvp);
! smbnode_t *ndnp = VTOSMB(ndvp);
! vnode_t *ovp;
! int error;
if (curproc->p_zone != VTOSMI(odvp)->smi_zone_ref.zref_zone ||
curproc->p_zone != VTOSMI(ndvp)->smi_zone_ref.zref_zone)
return (EPERM);
*** 2167,2200 ****
VTOSMI(ndvp)->smi_flags & SMI_DEAD ||
odvp->v_vfsp->vfs_flag & VFS_UNMOUNTED ||
ndvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
return (EIO);
- return (smbfsrename(odvp, onm, ndvp, nnm, cr, ct));
- }
-
- /*
- * smbfsrename does the real work of renaming in SMBFS
- */
- /* ARGSUSED */
- static int
- smbfsrename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr,
- caller_context_t *ct)
- {
- int error;
- int nvp_locked = 0;
- vnode_t *nvp = NULL;
- vnode_t *ovp = NULL;
- smbnode_t *onp;
- smbnode_t *nnp;
- smbnode_t *odnp;
- smbnode_t *ndnp;
- struct smb_cred scred;
- /* enum smbfsstat status; */
-
- ASSERT(curproc->p_zone == VTOSMI(odvp)->smi_zone_ref.zref_zone);
-
if (strcmp(onm, ".") == 0 || strcmp(onm, "..") == 0 ||
strcmp(nnm, ".") == 0 || strcmp(nnm, "..") == 0)
return (EINVAL);
/*
--- 2980,2989 ----
*** 2203,2216 ****
* fill those in correctly, check here too.
*/
if (odvp->v_vfsp != ndvp->v_vfsp)
return (EXDEV);
! odnp = VTOSMB(odvp);
! ndnp = VTOSMB(ndvp);
/*
* 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.
*/
if (odnp < ndnp) {
--- 2992,3017 ----
* fill those in correctly, check here too.
*/
if (odvp->v_vfsp != ndvp->v_vfsp)
return (EXDEV);
! /*
! * Need write access on source and target.
! * Server takes care of most checks.
! */
! error = smbfs_access(odvp, VWRITE|VEXEC, 0, cr, ct);
! if (error)
! return (error);
! if (odvp != ndvp) {
! error = smbfs_access(ndvp, VWRITE, 0, cr, ct);
! if (error)
! return (error);
! }
/*
+ * Need to lock both old/new dirs as writer.
+ *
* 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.
*/
if (odnp < ndnp) {
*** 2231,2270 ****
smbfs_rw_exit(&ndnp->r_rwlock);
return (EINTR);
}
}
smb_credinit(&scred, cr);
- /*
- * No returns after this point (goto out)
- */
/*
! * Need write access on source and target.
! * Server takes care of most checks.
*/
! error = smbfs_access(odvp, VWRITE|VEXEC, 0, cr, ct);
! if (error)
! goto out;
! if (odvp != ndvp) {
! error = smbfs_access(ndvp, VWRITE, 0, cr, ct);
! if (error)
! goto out;
}
! /*
! * Lookup the source name. Must already exist.
*/
! error = smbfslookup(odvp, onm, &ovp, cr, 0, ct);
! 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).
*/
! error = smbfslookup(ndvp, nnm, &nvp, cr, 0, ct);
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
--- 3032,3088 ----
smbfs_rw_exit(&ndnp->r_rwlock);
return (EINTR);
}
}
smb_credinit(&scred, cr);
+ /* Lookup the "old" name */
+ error = smbfslookup(odvp, onm, &ovp, cr, 0, ct);
+ if (error == 0) {
/*
! * Do the real rename work
*/
! error = smbfsrename(odvp, ovp, ndvp, nnm, &scred, flags);
! VN_RELE(ovp);
}
! smb_credrele(&scred);
! smbfs_rw_exit(&odnp->r_rwlock);
! smbfs_rw_exit(&ndnp->r_rwlock);
!
! return (error);
! }
!
! /*
! * smbfsrename does the real work of renaming in SMBFS
! * Caller has done dir access checks etc.
*/
! /* ARGSUSED */
! static int
! smbfsrename(vnode_t *odvp, vnode_t *ovp, vnode_t *ndvp, char *nnm,
! struct smb_cred *scred, int flags)
! {
! smbnode_t *odnp = VTOSMB(odvp);
! smbnode_t *onp = VTOSMB(ovp);
! smbnode_t *ndnp = VTOSMB(ndvp);
! vnode_t *nvp = NULL;
! int error;
! int nvp_locked = 0;
! smb_fh_t *fid = NULL;
+ /* Things our caller should have checked. */
+ ASSERT(curproc->p_zone == VTOSMI(odvp)->smi_zone_ref.zref_zone);
+ ASSERT(odvp->v_vfsp == ndvp->v_vfsp);
+ ASSERT(odnp->r_rwlock.owner == curthread);
+ ASSERT(ndnp->r_rwlock.owner == curthread);
+
/*
* 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).
*/
! error = smbfslookup(ndvp, nnm, &nvp, scred->scr_cred, 0, NULL);
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
*** 2305,2401 ****
error = EBUSY;
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.)
*/
if (nvp->v_type == VDIR) {
error = EEXIST;
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.
! */
! nnp = VTOSMB(nvp);
! mutex_enter(&nnp->r_statelock);
! if ((nvp->v_count > 2) && (nnp->n_fidrefs > 0)) {
! /*
! * 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.
! */
! mutex_exit(&nnp->r_statelock);
! error = EBUSY;
goto out;
- }
/*
- * Target file is not active. Try to remove it.
- */
- smbfs_attrcache_rm_locked(nnp);
- mutex_exit(&nnp->r_statelock);
-
- error = smbfs_smb_delete(nnp, &scred, NULL, 0, 0);
-
- /*
- * Similar to smbfs_remove
- */
- switch (error) {
- case 0:
- case ENOENT:
- case ENOTDIR:
- smbfs_attrcache_prune(nnp);
- break;
- }
-
- if (error)
- goto out;
- /*
* OK, removed the target file. Continue as if
* lookup target had failed (nvp == NULL).
*/
vn_vfsunlock(nvp);
nvp_locked = 0;
VN_RELE(nvp);
nvp = NULL;
} /* nvp */
! onp = VTOSMB(ovp);
smbfs_attrcache_remove(onp);
! error = smbfs_smb_rename(onp, ndnp, nnm, strlen(nnm), &scred);
/*
* If the old name should no longer exist,
* discard any cached attributes under it.
*/
! if (error == 0)
smbfs_attrcache_prune(onp);
out:
if (nvp) {
if (nvp_locked)
vn_vfsunlock(nvp);
VN_RELE(nvp);
}
- if (ovp)
- VN_RELE(ovp);
- smb_credrele(&scred);
- smbfs_rw_exit(&odnp->r_rwlock);
- smbfs_rw_exit(&ndnp->r_rwlock);
-
return (error);
}
/*
* XXX
--- 3123,3189 ----
error = EBUSY;
goto out;
}
/*
! * CIFS may give a SHARING_VIOLATION error when
* trying to rename onto an exising object,
* so try to remove the target first.
* (Only for files, not directories.)
*/
if (nvp->v_type == VDIR) {
error = EEXIST;
goto out;
}
! error = smbfsremove(ndvp, nvp, scred, flags);
! if (error != 0)
goto out;
/*
* OK, removed the target file. Continue as if
* lookup target had failed (nvp == NULL).
*/
vn_vfsunlock(nvp);
nvp_locked = 0;
VN_RELE(nvp);
nvp = NULL;
} /* nvp */
! /*
! * Get a file handle with delete access.
! * Close this FID before return.
! */
! error = smbfs_smb_tmpopen(onp, STD_RIGHT_DELETE_ACCESS,
! scred, &fid);
! if (error) {
! SMBVDEBUG("error %d opening %s\n",
! error, onp->n_rpath);
! goto out;
! }
!
smbfs_attrcache_remove(onp);
+ error = smbfs_smb_rename(odnp, onp, ndnp, nnm, strlen(nnm),
+ fid, scred);
! smbfs_smb_tmpclose(onp, fid);
/*
* If the old name should no longer exist,
* discard any cached attributes under it.
*/
! if (error == 0) {
smbfs_attrcache_prune(onp);
+ /* SMBFS_VNEVENT... */
+ }
out:
if (nvp) {
if (nvp_locked)
vn_vfsunlock(nvp);
VN_RELE(nvp);
}
return (error);
}
/*
* XXX
*** 2415,2425 ****
struct smbmntinfo *smi = VTOSMI(dvp);
struct smb_cred scred;
struct smbfattr fattr;
const char *name = (const char *) nm;
int nmlen = strlen(name);
! int error, hiderr;
if (curproc->p_zone != smi->smi_zone_ref.zref_zone)
return (EPERM);
if (smi->smi_flags & SMI_DEAD || dvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
--- 3203,3213 ----
struct smbmntinfo *smi = VTOSMI(dvp);
struct smb_cred scred;
struct smbfattr fattr;
const char *name = (const char *) nm;
int nmlen = strlen(name);
! int error;
if (curproc->p_zone != smi->smi_zone_ref.zref_zone)
return (EPERM);
if (smi->smi_flags & SMI_DEAD || dvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
*** 2436,2450 ****
if (smbfs_rw_enter_sig(&dnp->r_rwlock, RW_WRITER, SMBINTR(dvp)))
return (EINTR);
smb_credinit(&scred, cr);
/*
- * XXX: Do we need r_lkserlock too?
- * No use of any shared fid or fctx...
- */
-
- /*
* Require write access in the containing directory.
*/
error = smbfs_access(dvp, VWRITE, 0, cr, ct);
if (error)
goto out;
--- 3224,3233 ----
*** 2461,2474 ****
error = smbfs_nget(dvp, name, nmlen, &fattr, &vp);
if (error)
goto out;
- if (name[0] == '.')
- if ((hiderr = smbfs_smb_hideit(VTOSMB(vp), NULL, 0, &scred)))
- SMBVDEBUG("hide failure %d\n", hiderr);
-
/* Success! */
*vpp = vp;
error = 0;
out:
smb_credrele(&scred);
--- 3244,3253 ----
*** 2488,2524 ****
/* ARGSUSED */
static int
smbfs_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr,
caller_context_t *ct, int flags)
{
vnode_t *vp = NULL;
int vp_locked = 0;
struct smbmntinfo *smi = VTOSMI(dvp);
struct smbnode *dnp = VTOSMB(dvp);
struct smbnode *np;
- struct smb_cred scred;
int error;
if (curproc->p_zone != smi->smi_zone_ref.zref_zone)
return (EPERM);
if (smi->smi_flags & SMI_DEAD || dvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
return (EIO);
if (smbfs_rw_enter_sig(&dnp->r_rwlock, RW_WRITER, SMBINTR(dvp)))
return (EINTR);
smb_credinit(&scred, cr);
/*
- * Require w/x access in the containing directory.
- * Server handles all other access checks.
- */
- error = smbfs_access(dvp, VEXEC|VWRITE, 0, cr, ct);
- if (error)
- goto out;
-
- /*
* First lookup the entry to be removed.
*/
error = smbfslookup(dvp, nm, &vp, cr, 0, ct);
if (error)
goto out;
--- 3267,3302 ----
/* ARGSUSED */
static int
smbfs_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr,
caller_context_t *ct, int flags)
{
+ struct smb_cred scred;
vnode_t *vp = NULL;
int vp_locked = 0;
struct smbmntinfo *smi = VTOSMI(dvp);
struct smbnode *dnp = VTOSMB(dvp);
struct smbnode *np;
int error;
if (curproc->p_zone != smi->smi_zone_ref.zref_zone)
return (EPERM);
if (smi->smi_flags & SMI_DEAD || dvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
return (EIO);
+ /*
+ * Verify access to the dirctory.
+ */
+ error = smbfs_access(dvp, VWRITE|VEXEC, 0, cr, ct);
+ if (error)
+ return (error);
+
if (smbfs_rw_enter_sig(&dnp->r_rwlock, RW_WRITER, SMBINTR(dvp)))
return (EINTR);
smb_credinit(&scred, cr);
/*
* First lookup the entry to be removed.
*/
error = smbfslookup(dvp, nm, &vp, cr, 0, ct);
if (error)
goto out;
*** 2545,2571 ****
if (vn_mountedvfs(vp) != NULL) {
error = EBUSY;
goto out;
}
- smbfs_attrcache_remove(np);
- error = smbfs_smb_rmdir(np, &scred);
-
/*
! * Similar to smbfs_remove
*/
! switch (error) {
! case 0:
! case ENOENT:
! case ENOTDIR:
! smbfs_attrcache_prune(np);
! break;
! }
!
if (error)
goto out;
mutex_enter(&np->r_statelock);
dnp->n_flag |= NMODIFIED;
mutex_exit(&np->r_statelock);
smbfs_attr_touchdir(dnp);
smbfs_rmhash(np);
--- 3323,3343 ----
if (vn_mountedvfs(vp) != NULL) {
error = EBUSY;
goto out;
}
/*
! * Do the real rmdir work
*/
! error = smbfsremove(dvp, vp, &scred, flags);
if (error)
goto out;
+ #ifdef SMBFS_VNEVENT
+ vnevent_rmdir(vp, dvp, nm, ct);
+ #endif
+
mutex_enter(&np->r_statelock);
dnp->n_flag |= NMODIFIED;
mutex_exit(&np->r_statelock);
smbfs_attr_touchdir(dnp);
smbfs_rmhash(np);
*** 2583,2592 ****
--- 3355,3374 ----
}
/* ARGSUSED */
static int
+ smbfs_symlink(vnode_t *dvp, char *lnm, struct vattr *tva, char *tnm, cred_t *cr,
+ caller_context_t *ct, int flags)
+ {
+ /* Not yet... */
+ return (ENOSYS);
+ }
+
+
+ /* ARGSUSED */
+ static int
smbfs_readdir(vnode_t *vp, struct uio *uiop, cred_t *cr, int *eofp,
caller_context_t *ct, int flags)
{
struct smbnode *np = VTOSMB(vp);
int error = 0;
*** 2608,2619 ****
return (error);
ASSERT(smbfs_rw_lock_held(&np->r_rwlock, RW_READER));
/*
! * 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.
--- 3390,3400 ----
return (error);
ASSERT(smbfs_rw_lock_held(&np->r_rwlock, RW_READER));
/*
! * Todo readdir cache here
*
* 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.
*** 2843,2853 ****
--- 3624,3646 ----
kmem_free(dp, dbufsiz);
smb_credrele(&scred);
return (error);
}
+ /*
+ * Here NFS has: nfs3_bio
+ * See smbfs_bio above.
+ */
+ /* ARGSUSED */
+ static int
+ smbfs_fid(vnode_t *vp, fid_t *fidp, caller_context_t *ct)
+ {
+ return (ENOSYS);
+ }
+
+
/*
* The pair of functions VOP_RWLOCK, VOP_RWUNLOCK
* are optional functions that are called by:
* getdents, before/after VOP_READDIR
* pread, before/after ... VOP_READ
*** 2917,2928 ****
--- 3710,4712 ----
return (EINVAL);
return (0);
}
+ /* mmap support ******************************************************** */
+ #ifdef _KERNEL
+
+ #ifdef DEBUG
+ static int smbfs_lostpage = 0; /* number of times we lost original page */
+ #endif
+
/*
+ * Return all the pages from [off..off+len) in file
+ * Like nfs3_getpage
+ */
+ /* ARGSUSED */
+ static int
+ smbfs_getpage(vnode_t *vp, offset_t off, size_t len, uint_t *protp,
+ page_t *pl[], size_t plsz, struct seg *seg, caddr_t addr,
+ enum seg_rw rw, cred_t *cr, caller_context_t *ct)
+ {
+ smbnode_t *np;
+ smbmntinfo_t *smi;
+ int error;
+
+ np = VTOSMB(vp);
+ smi = VTOSMI(vp);
+
+ if (curproc->p_zone != smi->smi_zone_ref.zref_zone)
+ return (EIO);
+
+ if (smi->smi_flags & SMI_DEAD || vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
+ if (vp->v_flag & VNOMAP)
+ return (ENOSYS);
+
+ if (protp != NULL)
+ *protp = PROT_ALL;
+
+ /*
+ * Now valididate that the caches are up to date.
+ */
+ error = smbfs_validate_caches(vp, cr);
+ if (error)
+ return (error);
+
+ retry:
+ mutex_enter(&np->r_statelock);
+
+ /*
+ * Don't create dirty pages faster than they
+ * can be cleaned ... (etc. see nfs)
+ *
+ * Here NFS also tests:
+ * (mi->mi_max_threads != 0 &&
+ * rp->r_awcount > 2 * mi->mi_max_threads)
+ */
+ if (rw == S_CREATE) {
+ while (np->r_gcount > 0)
+ cv_wait(&np->r_cv, &np->r_statelock);
+ }
+
+ /*
+ * If we are getting called as a side effect of a write
+ * operation the local file size might not be extended yet.
+ * In this case we want to be able to return pages of zeroes.
+ */
+ if (off + len > np->r_size + PAGEOFFSET && seg != segkmap) {
+ mutex_exit(&np->r_statelock);
+ return (EFAULT); /* beyond EOF */
+ }
+
+ mutex_exit(&np->r_statelock);
+
+ error = pvn_getpages(smbfs_getapage, vp, off, len, protp,
+ pl, plsz, seg, addr, rw, cr);
+
+ switch (error) {
+ case SMBFS_EOF:
+ smbfs_purge_caches(vp, cr);
+ goto retry;
+ case ESTALE:
+ /*
+ * Here NFS has: PURGE_STALE_FH(error, vp, cr);
+ * In-line here as we only use it once.
+ */
+ mutex_enter(&np->r_statelock);
+ np->r_flags |= RSTALE;
+ if (!np->r_error)
+ np->r_error = (error);
+ mutex_exit(&np->r_statelock);
+ if (vn_has_cached_data(vp))
+ smbfs_invalidate_pages(vp, (u_offset_t)0, cr);
+ smbfs_purge_caches(vp, cr);
+ break;
+ default:
+ break;
+ }
+
+ return (error);
+ }
+
+ /*
+ * Called from pvn_getpages to get a particular page.
+ * Like nfs3_getapage
+ */
+ /* ARGSUSED */
+ static int
+ smbfs_getapage(vnode_t *vp, u_offset_t off, size_t len, uint_t *protp,
+ page_t *pl[], size_t plsz, struct seg *seg, caddr_t addr,
+ enum seg_rw rw, cred_t *cr)
+ {
+ smbnode_t *np;
+ smbmntinfo_t *smi;
+
+ uint_t bsize;
+ struct buf *bp;
+ page_t *pp;
+ u_offset_t lbn;
+ u_offset_t io_off;
+ u_offset_t blkoff;
+ size_t io_len;
+ uint_t blksize;
+ int error;
+ /* int readahead; */
+ int readahead_issued = 0;
+ /* int ra_window; * readahead window */
+ page_t *pagefound;
+
+ np = VTOSMB(vp);
+ smi = VTOSMI(vp);
+
+ if (curproc->p_zone != smi->smi_zone_ref.zref_zone)
+ return (EIO);
+
+ if (smi->smi_flags & SMI_DEAD || vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
+ bsize = MAX(vp->v_vfsp->vfs_bsize, PAGESIZE);
+
+ reread:
+ bp = NULL;
+ pp = NULL;
+ pagefound = NULL;
+
+ if (pl != NULL)
+ pl[0] = NULL;
+
+ error = 0;
+ lbn = off / bsize;
+ blkoff = lbn * bsize;
+
+ /*
+ * NFS queues up readahead work here.
+ */
+
+ again:
+ if ((pagefound = page_exists(vp, off)) == NULL) {
+ if (pl == NULL) {
+ (void) 0; /* Todo: smbfs_async_readahead(); */
+ } else if (rw == S_CREATE) {
+ /*
+ * Block for this page is not allocated, or the offset
+ * is beyond the current allocation size, or we're
+ * allocating a swap slot and the page was not found,
+ * so allocate it and return a zero page.
+ */
+ if ((pp = page_create_va(vp, off,
+ PAGESIZE, PG_WAIT, seg, addr)) == NULL)
+ cmn_err(CE_PANIC, "smbfs_getapage: page_create");
+ io_len = PAGESIZE;
+ mutex_enter(&np->r_statelock);
+ np->r_nextr = off + PAGESIZE;
+ mutex_exit(&np->r_statelock);
+ } else {
+ /*
+ * Need to go to server to get a BLOCK, exception to
+ * that being while reading at offset = 0 or doing
+ * random i/o, in that case read only a PAGE.
+ */
+ mutex_enter(&np->r_statelock);
+ if (blkoff < np->r_size &&
+ blkoff + bsize >= np->r_size) {
+ /*
+ * If only a block or less is left in
+ * the file, read all that is remaining.
+ */
+ if (np->r_size <= off) {
+ /*
+ * Trying to access beyond EOF,
+ * set up to get at least one page.
+ */
+ blksize = off + PAGESIZE - blkoff;
+ } else
+ blksize = np->r_size - blkoff;
+ } else if ((off == 0) ||
+ (off != np->r_nextr && !readahead_issued)) {
+ blksize = PAGESIZE;
+ blkoff = off; /* block = page here */
+ } else
+ blksize = bsize;
+ mutex_exit(&np->r_statelock);
+
+ pp = pvn_read_kluster(vp, off, seg, addr, &io_off,
+ &io_len, blkoff, blksize, 0);
+
+ /*
+ * Some other thread has entered the page,
+ * so just use it.
+ */
+ if (pp == NULL)
+ goto again;
+
+ /*
+ * Now round the request size up to page boundaries.
+ * This ensures that the entire page will be
+ * initialized to zeroes if EOF is encountered.
+ */
+ io_len = ptob(btopr(io_len));
+
+ bp = pageio_setup(pp, io_len, vp, B_READ);
+ ASSERT(bp != NULL);
+
+ /*
+ * pageio_setup should have set b_addr to 0. This
+ * is correct since we want to do I/O on a page
+ * boundary. bp_mapin will use this addr to calculate
+ * an offset, and then set b_addr to the kernel virtual
+ * address it allocated for us.
+ */
+ ASSERT(bp->b_un.b_addr == 0);
+
+ bp->b_edev = 0;
+ bp->b_dev = 0;
+ bp->b_lblkno = lbtodb(io_off);
+ bp->b_file = vp;
+ bp->b_offset = (offset_t)off;
+ bp_mapin(bp);
+
+ /*
+ * If doing a write beyond what we believe is EOF,
+ * don't bother trying to read the pages from the
+ * server, we'll just zero the pages here. We
+ * don't check that the rw flag is S_WRITE here
+ * because some implementations may attempt a
+ * read access to the buffer before copying data.
+ */
+ mutex_enter(&np->r_statelock);
+ if (io_off >= np->r_size && seg == segkmap) {
+ mutex_exit(&np->r_statelock);
+ bzero(bp->b_un.b_addr, io_len);
+ } else {
+ mutex_exit(&np->r_statelock);
+ error = smbfs_bio(bp, 0, cr);
+ }
+
+ /*
+ * Unmap the buffer before freeing it.
+ */
+ bp_mapout(bp);
+ pageio_done(bp);
+
+ /* Here NFS3 updates all pp->p_fsdata */
+
+ if (error == SMBFS_EOF) {
+ /*
+ * If doing a write system call just return
+ * zeroed pages, else user tried to get pages
+ * beyond EOF, return error. We don't check
+ * that the rw flag is S_WRITE here because
+ * some implementations may attempt a read
+ * access to the buffer before copying data.
+ */
+ if (seg == segkmap)
+ error = 0;
+ else
+ error = EFAULT;
+ }
+
+ if (!readahead_issued && !error) {
+ mutex_enter(&np->r_statelock);
+ np->r_nextr = io_off + io_len;
+ mutex_exit(&np->r_statelock);
+ }
+ }
+ }
+
+ if (pl == NULL)
+ return (error);
+
+ if (error) {
+ if (pp != NULL)
+ pvn_read_done(pp, B_ERROR);
+ return (error);
+ }
+
+ if (pagefound) {
+ se_t se = (rw == S_CREATE ? SE_EXCL : SE_SHARED);
+
+ /*
+ * Page exists in the cache, acquire the appropriate lock.
+ * If this fails, start all over again.
+ */
+ if ((pp = page_lookup(vp, off, se)) == NULL) {
+ #ifdef DEBUG
+ smbfs_lostpage++;
+ #endif
+ goto reread;
+ }
+ pl[0] = pp;
+ pl[1] = NULL;
+ return (0);
+ }
+
+ if (pp != NULL)
+ pvn_plist_init(pp, pl, plsz, off, io_len, rw);
+
+ return (error);
+ }
+
+ /*
+ * Here NFS has: nfs3_readahead
+ * No read-ahead in smbfs yet.
+ */
+
+ #endif // _KERNEL
+
+ /*
+ * Flags are composed of {B_INVAL, B_FREE, B_DONTNEED, B_FORCE}
+ * If len == 0, do from off to EOF.
+ *
+ * The normal cases should be len == 0 && off == 0 (entire vp list),
+ * len == MAXBSIZE (from segmap_release actions), and len == PAGESIZE
+ * (from pageout).
+ *
+ * Like nfs3_putpage + nfs_putpages
+ */
+ /* ARGSUSED */
+ static int
+ smbfs_putpage(vnode_t *vp, offset_t off, size_t len, int flags, cred_t *cr,
+ caller_context_t *ct)
+ {
+ #ifdef _KERNEL
+ smbnode_t *np;
+ smbmntinfo_t *smi;
+ page_t *pp;
+ u_offset_t eoff;
+ u_offset_t io_off;
+ size_t io_len;
+ int error;
+ int rdirty;
+ int err;
+
+ np = VTOSMB(vp);
+ smi = VTOSMI(vp);
+
+ if (curproc->p_zone != smi->smi_zone_ref.zref_zone)
+ return (EIO);
+
+ if (smi->smi_flags & SMI_DEAD || vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
+ if (vp->v_flag & VNOMAP)
+ return (ENOSYS);
+
+ /* Here NFS does rp->r_count (++/--) stuff. */
+
+ /* Beginning of code from nfs_putpages. */
+
+ if (!vn_has_cached_data(vp))
+ return (0);
+
+ /*
+ * If ROUTOFSPACE is set, then all writes turn into B_INVAL
+ * writes. B_FORCE is set to force the VM system to actually
+ * invalidate the pages, even if the i/o failed. The pages
+ * need to get invalidated because they can't be written out
+ * because there isn't any space left on either the server's
+ * file system or in the user's disk quota. The B_FREE bit
+ * is cleared to avoid confusion as to whether this is a
+ * request to place the page on the freelist or to destroy
+ * it.
+ */
+ if ((np->r_flags & ROUTOFSPACE) ||
+ (vp->v_vfsp->vfs_flag & VFS_UNMOUNTED))
+ flags = (flags & ~B_FREE) | B_INVAL | B_FORCE;
+
+ if (len == 0) {
+ /*
+ * If doing a full file synchronous operation, then clear
+ * the RDIRTY bit. If a page gets dirtied while the flush
+ * is happening, then RDIRTY will get set again. The
+ * RDIRTY bit must get cleared before the flush so that
+ * we don't lose this information.
+ *
+ * NFS has B_ASYNC vs sync stuff here.
+ */
+ if (off == (u_offset_t)0 &&
+ (np->r_flags & RDIRTY)) {
+ mutex_enter(&np->r_statelock);
+ rdirty = (np->r_flags & RDIRTY);
+ np->r_flags &= ~RDIRTY;
+ mutex_exit(&np->r_statelock);
+ } else
+ rdirty = 0;
+
+ /*
+ * Search the entire vp list for pages >= off, and flush
+ * the dirty pages.
+ */
+ error = pvn_vplist_dirty(vp, off, smbfs_putapage,
+ flags, cr);
+
+ /*
+ * If an error occurred and the file was marked as dirty
+ * before and we aren't forcibly invalidating pages, then
+ * reset the RDIRTY flag.
+ */
+ if (error && rdirty &&
+ (flags & (B_INVAL | B_FORCE)) != (B_INVAL | B_FORCE)) {
+ mutex_enter(&np->r_statelock);
+ np->r_flags |= RDIRTY;
+ mutex_exit(&np->r_statelock);
+ }
+ } else {
+ /*
+ * Do a range from [off...off + len) looking for pages
+ * to deal with.
+ */
+ error = 0;
+ io_len = 1; /* quiet warnings */
+ eoff = off + len;
+
+ for (io_off = off; io_off < eoff; io_off += io_len) {
+ mutex_enter(&np->r_statelock);
+ if (io_off >= np->r_size) {
+ mutex_exit(&np->r_statelock);
+ break;
+ }
+ mutex_exit(&np->r_statelock);
+ /*
+ * If we are not invalidating, synchronously
+ * freeing or writing pages use the routine
+ * page_lookup_nowait() to prevent reclaiming
+ * them from the free list.
+ */
+ if ((flags & B_INVAL) || !(flags & B_ASYNC)) {
+ pp = page_lookup(vp, io_off,
+ (flags & (B_INVAL | B_FREE)) ?
+ SE_EXCL : SE_SHARED);
+ } else {
+ pp = page_lookup_nowait(vp, io_off,
+ (flags & B_FREE) ? SE_EXCL : SE_SHARED);
+ }
+
+ if (pp == NULL || !pvn_getdirty(pp, flags))
+ io_len = PAGESIZE;
+ else {
+ err = smbfs_putapage(vp, pp, &io_off,
+ &io_len, flags, cr);
+ if (!error)
+ error = err;
+ /*
+ * "io_off" and "io_len" are returned as
+ * the range of pages we actually wrote.
+ * This allows us to skip ahead more quickly
+ * since several pages may've been dealt
+ * with by this iteration of the loop.
+ */
+ }
+ }
+ }
+
+ return (error);
+
+ #else // _KERNEL
+ return (ENOSYS);
+ #endif // _KERNEL
+ }
+
+ #ifdef _KERNEL
+
+ /*
+ * Write out a single page, possibly klustering adjacent dirty pages.
+ *
+ * Like nfs3_putapage / nfs3_sync_putapage
+ */
+ static int
+ smbfs_putapage(vnode_t *vp, page_t *pp, u_offset_t *offp, size_t *lenp,
+ int flags, cred_t *cr)
+ {
+ smbnode_t *np;
+ u_offset_t io_off;
+ u_offset_t lbn_off;
+ u_offset_t lbn;
+ size_t io_len;
+ uint_t bsize;
+ int error;
+
+ np = VTOSMB(vp);
+
+ ASSERT(!vn_is_readonly(vp));
+
+ bsize = MAX(vp->v_vfsp->vfs_bsize, PAGESIZE);
+ lbn = pp->p_offset / bsize;
+ lbn_off = lbn * bsize;
+
+ /*
+ * Find a kluster that fits in one block, or in
+ * one page if pages are bigger than blocks. If
+ * there is less file space allocated than a whole
+ * page, we'll shorten the i/o request below.
+ */
+ pp = pvn_write_kluster(vp, pp, &io_off, &io_len, lbn_off,
+ roundup(bsize, PAGESIZE), flags);
+
+ /*
+ * pvn_write_kluster shouldn't have returned a page with offset
+ * behind the original page we were given. Verify that.
+ */
+ ASSERT((pp->p_offset / bsize) >= lbn);
+
+ /*
+ * Now pp will have the list of kept dirty pages marked for
+ * write back. It will also handle invalidation and freeing
+ * of pages that are not dirty. Check for page length rounding
+ * problems.
+ */
+ if (io_off + io_len > lbn_off + bsize) {
+ ASSERT((io_off + io_len) - (lbn_off + bsize) < PAGESIZE);
+ io_len = lbn_off + bsize - io_off;
+ }
+ /*
+ * The RMODINPROGRESS flag makes sure that smbfs_bio() sees a
+ * consistent value of r_size. RMODINPROGRESS is set in writerp().
+ * When RMODINPROGRESS is set it indicates that a uiomove() is in
+ * progress and the r_size has not been made consistent with the
+ * new size of the file. When the uiomove() completes the r_size is
+ * updated and the RMODINPROGRESS flag is cleared.
+ *
+ * The RMODINPROGRESS flag makes sure that smbfs_bio() sees a
+ * consistent value of r_size. Without this handshaking, it is
+ * possible that smbfs_bio() picks up the old value of r_size
+ * before the uiomove() in writerp() completes. This will result
+ * in the write through smbfs_bio() being dropped.
+ *
+ * More precisely, there is a window between the time the uiomove()
+ * completes and the time the r_size is updated. If a VOP_PUTPAGE()
+ * operation intervenes in this window, the page will be picked up,
+ * because it is dirty (it will be unlocked, unless it was
+ * pagecreate'd). When the page is picked up as dirty, the dirty
+ * bit is reset (pvn_getdirty()). In smbfs_write(), r_size is
+ * checked. This will still be the old size. Therefore the page will
+ * not be written out. When segmap_release() calls VOP_PUTPAGE(),
+ * the page will be found to be clean and the write will be dropped.
+ */
+ if (np->r_flags & RMODINPROGRESS) {
+ mutex_enter(&np->r_statelock);
+ if ((np->r_flags & RMODINPROGRESS) &&
+ np->r_modaddr + MAXBSIZE > io_off &&
+ np->r_modaddr < io_off + io_len) {
+ page_t *plist;
+ /*
+ * A write is in progress for this region of the file.
+ * If we did not detect RMODINPROGRESS here then this
+ * path through smbfs_putapage() would eventually go to
+ * smbfs_bio() and may not write out all of the data
+ * in the pages. We end up losing data. So we decide
+ * to set the modified bit on each page in the page
+ * list and mark the rnode with RDIRTY. This write
+ * will be restarted at some later time.
+ */
+ plist = pp;
+ while (plist != NULL) {
+ pp = plist;
+ page_sub(&plist, pp);
+ hat_setmod(pp);
+ page_io_unlock(pp);
+ page_unlock(pp);
+ }
+ np->r_flags |= RDIRTY;
+ mutex_exit(&np->r_statelock);
+ if (offp)
+ *offp = io_off;
+ if (lenp)
+ *lenp = io_len;
+ return (0);
+ }
+ mutex_exit(&np->r_statelock);
+ }
+
+ /*
+ * NFS handles (flags & B_ASYNC) here...
+ * (See nfs_async_putapage())
+ *
+ * This code section from: nfs3_sync_putapage()
+ */
+
+ flags |= B_WRITE;
+
+ error = smbfs_rdwrlbn(vp, pp, io_off, io_len, flags, cr);
+
+ if ((error == ENOSPC || error == EDQUOT || error == EFBIG ||
+ error == EACCES) &&
+ (flags & (B_INVAL|B_FORCE)) != (B_INVAL|B_FORCE)) {
+ if (!(np->r_flags & ROUTOFSPACE)) {
+ mutex_enter(&np->r_statelock);
+ np->r_flags |= ROUTOFSPACE;
+ mutex_exit(&np->r_statelock);
+ }
+ flags |= B_ERROR;
+ pvn_write_done(pp, flags);
+ /*
+ * If this was not an async thread, then try again to
+ * write out the pages, but this time, also destroy
+ * them whether or not the write is successful. This
+ * will prevent memory from filling up with these
+ * pages and destroying them is the only alternative
+ * if they can't be written out.
+ *
+ * Don't do this if this is an async thread because
+ * when the pages are unlocked in pvn_write_done,
+ * some other thread could have come along, locked
+ * them, and queued for an async thread. It would be
+ * possible for all of the async threads to be tied
+ * up waiting to lock the pages again and they would
+ * all already be locked and waiting for an async
+ * thread to handle them. Deadlock.
+ */
+ if (!(flags & B_ASYNC)) {
+ error = smbfs_putpage(vp, io_off, io_len,
+ B_INVAL | B_FORCE, cr, NULL);
+ }
+ } else {
+ if (error)
+ flags |= B_ERROR;
+ else if (np->r_flags & ROUTOFSPACE) {
+ mutex_enter(&np->r_statelock);
+ np->r_flags &= ~ROUTOFSPACE;
+ mutex_exit(&np->r_statelock);
+ }
+ pvn_write_done(pp, flags);
+ }
+
+ /* Now more code from: nfs3_putapage */
+
+ if (offp)
+ *offp = io_off;
+ if (lenp)
+ *lenp = io_len;
+
+ return (error);
+ }
+
+ #endif // _KERNEL
+
+
+ /*
+ * NFS has this in nfs_client.c (shared by v2,v3,...)
+ * We have it here so smbfs_putapage can be file scope.
+ */
+ void
+ smbfs_invalidate_pages(vnode_t *vp, u_offset_t off, cred_t *cr)
+ {
+ smbnode_t *np;
+
+ np = VTOSMB(vp);
+
+ mutex_enter(&np->r_statelock);
+ while (np->r_flags & RTRUNCATE)
+ cv_wait(&np->r_cv, &np->r_statelock);
+ np->r_flags |= RTRUNCATE;
+
+ if (off == (u_offset_t)0) {
+ np->r_flags &= ~RDIRTY;
+ if (!(np->r_flags & RSTALE))
+ np->r_error = 0;
+ }
+ /* Here NFSv3 has np->r_truncaddr = off; */
+ mutex_exit(&np->r_statelock);
+
+ #ifdef _KERNEL
+ (void) pvn_vplist_dirty(vp, off, smbfs_putapage,
+ B_INVAL | B_TRUNC, cr);
+ #endif // _KERNEL
+
+ mutex_enter(&np->r_statelock);
+ np->r_flags &= ~RTRUNCATE;
+ cv_broadcast(&np->r_cv);
+ mutex_exit(&np->r_statelock);
+ }
+
+ #ifdef _KERNEL
+
+ /* Like nfs3_map */
+
+ /* ARGSUSED */
+ static int
+ smbfs_map(vnode_t *vp, offset_t off, struct as *as, caddr_t *addrp,
+ size_t len, uchar_t prot, uchar_t maxprot, uint_t flags,
+ cred_t *cr, caller_context_t *ct)
+ {
+ segvn_crargs_t vn_a;
+ struct vattr va;
+ smbnode_t *np;
+ smbmntinfo_t *smi;
+ int error;
+
+ np = VTOSMB(vp);
+ smi = VTOSMI(vp);
+
+ if (curproc->p_zone != smi->smi_zone_ref.zref_zone)
+ return (EIO);
+
+ if (smi->smi_flags & SMI_DEAD || vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
+ if (vp->v_flag & VNOMAP)
+ return (ENOSYS);
+
+ if (off < 0 || off + (ssize_t)len < 0)
+ return (ENXIO);
+
+ if (vp->v_type != VREG)
+ return (ENODEV);
+
+ /*
+ * NFS does close-to-open consistency stuff here.
+ * Just get (possibly cached) attributes.
+ */
+ va.va_mask = AT_ALL;
+ if ((error = smbfsgetattr(vp, &va, cr)) != 0)
+ return (error);
+
+ /*
+ * Check to see if the vnode is currently marked as not cachable.
+ * This means portions of the file are locked (through VOP_FRLOCK).
+ * In this case the map request must be refused. We use
+ * rp->r_lkserlock to avoid a race with concurrent lock requests.
+ */
+ /*
+ * Atomically increment r_inmap after acquiring r_rwlock. The
+ * idea here is to acquire r_rwlock to block read/write and
+ * not to protect r_inmap. r_inmap will inform smbfs_read/write()
+ * that we are in smbfs_map(). Now, r_rwlock is acquired in order
+ * and we can prevent the deadlock that would have occurred
+ * when smbfs_addmap() would have acquired it out of order.
+ *
+ * Since we are not protecting r_inmap by any lock, we do not
+ * hold any lock when we decrement it. We atomically decrement
+ * r_inmap after we release r_lkserlock. Note that rwlock is
+ * re-entered as writer in smbfs_addmap (called via as_map).
+ */
+
+ if (smbfs_rw_enter_sig(&np->r_rwlock, RW_WRITER, SMBINTR(vp)))
+ return (EINTR);
+ atomic_inc_uint(&np->r_inmap);
+ smbfs_rw_exit(&np->r_rwlock);
+
+ if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_WRITER, SMBINTR(vp))) {
+ atomic_dec_uint(&np->r_inmap);
+ return (EINTR);
+ }
+
+ if (vp->v_flag & VNOCACHE) {
+ error = EAGAIN;
+ goto done;
+ }
+
+ /*
+ * Don't allow concurrent locks and mapping if mandatory locking is
+ * enabled.
+ */
+ if ((flk_has_remote_locks(vp) || smbfs_lm_has_sleep(vp)) &&
+ MANDLOCK(vp, va.va_mode)) {
+ error = EAGAIN;
+ goto done;
+ }
+
+ as_rangelock(as);
+ error = choose_addr(as, addrp, len, off, ADDR_VACALIGN, flags);
+ if (error != 0) {
+ as_rangeunlock(as);
+ goto done;
+ }
+
+ vn_a.vp = vp;
+ vn_a.offset = off;
+ vn_a.type = (flags & MAP_TYPE);
+ vn_a.prot = (uchar_t)prot;
+ vn_a.maxprot = (uchar_t)maxprot;
+ vn_a.flags = (flags & ~MAP_TYPE);
+ vn_a.cred = cr;
+ vn_a.amp = NULL;
+ vn_a.szc = 0;
+ vn_a.lgrp_mem_policy_flags = 0;
+
+ error = as_map(as, *addrp, len, segvn_create, &vn_a);
+ as_rangeunlock(as);
+
+ done:
+ smbfs_rw_exit(&np->r_lkserlock);
+ atomic_dec_uint(&np->r_inmap);
+ return (error);
+ }
+
+ /*
+ * This uses addmap/delmap functions to hold the SMB FID open as long as
+ * there are pages mapped in this as/seg. Increment the FID refs. when
+ * the maping count goes from zero to non-zero, and release the FID ref
+ * when the maping count goes from non-zero to zero.
+ */
+
+ /* ARGSUSED */
+ static int
+ smbfs_addmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr,
+ size_t len, uchar_t prot, uchar_t maxprot, uint_t flags,
+ cred_t *cr, caller_context_t *ct)
+ {
+ smbnode_t *np = VTOSMB(vp);
+ boolean_t inc_fidrefs = B_FALSE;
+
+ /*
+ * When r_mapcnt goes from zero to non-zero,
+ * increment n_fidrefs
+ */
+ mutex_enter(&np->r_statelock);
+ if (np->r_mapcnt == 0)
+ inc_fidrefs = B_TRUE;
+ np->r_mapcnt += btopr(len);
+ mutex_exit(&np->r_statelock);
+
+ if (inc_fidrefs) {
+ (void) smbfs_rw_enter_sig(&np->r_lkserlock, RW_WRITER, 0);
+ np->n_fidrefs++;
+ smbfs_rw_exit(&np->r_lkserlock);
+ }
+
+ return (0);
+ }
+
+ /*
+ * Args passed to smbfs_delmap_async
+ */
+ typedef struct smbfs_delmap_args {
+ taskq_ent_t dm_tqent;
+ cred_t *dm_cr;
+ vnode_t *dm_vp;
+ offset_t dm_off;
+ caddr_t dm_addr;
+ size_t dm_len;
+ uint_t dm_prot;
+ uint_t dm_maxprot;
+ uint_t dm_flags;
+ boolean_t dm_rele_fid;
+ } smbfs_delmap_args_t;
+
+ /*
+ * Using delmap not only to release the SMB FID (as described above)
+ * but to flush dirty pages as needed. Both of those do the actual
+ * work in an async taskq job to avoid interfering with locks held
+ * in the VM layer when this is called.
+ */
+
+ /* ARGSUSED */
+ static int
+ smbfs_delmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr,
+ size_t len, uint_t prot, uint_t maxprot, uint_t flags,
+ cred_t *cr, caller_context_t *ct)
+ {
+ smbnode_t *np = VTOSMB(vp);
+ smbmntinfo_t *smi = VTOSMI(vp);
+ smbfs_delmap_args_t *dmapp;
+
+ dmapp = kmem_zalloc(sizeof (*dmapp), KM_SLEEP);
+
+ /*
+ * The VM layer may segvn_free the seg holding this vnode
+ * before our callback has a chance run, so take a hold on
+ * the vnode here and release it in the callback.
+ * (same for the cred)
+ */
+ crhold(cr);
+ VN_HOLD(vp);
+
+ dmapp->dm_vp = vp;
+ dmapp->dm_cr = cr;
+ dmapp->dm_off = off;
+ dmapp->dm_addr = addr;
+ dmapp->dm_len = len;
+ dmapp->dm_prot = prot;
+ dmapp->dm_maxprot = maxprot;
+ dmapp->dm_flags = flags;
+ dmapp->dm_rele_fid = B_FALSE;
+
+ /*
+ * Go ahead and decrement r_mapcount now, which is
+ * the primary purpose of this function.
+ *
+ * When r_mapcnt goes to zero, we need to call
+ * smbfs_rele_fid, but can't do that here, so
+ * set a flag telling the async task to do it.
+ */
+ mutex_enter(&np->r_statelock);
+ np->r_mapcnt -= btopr(len);
+ ASSERT(np->r_mapcnt >= 0);
+ if (np->r_mapcnt == 0)
+ dmapp->dm_rele_fid = B_TRUE;
+ mutex_exit(&np->r_statelock);
+
+ taskq_dispatch_ent(smi->smi_taskq, smbfs_delmap_async, dmapp, 0,
+ &dmapp->dm_tqent);
+
+ return (0);
+ }
+
+ /*
+ * Remove some pages from an mmap'd vnode. Flush any
+ * dirty pages in the unmapped range.
+ */
+ /* ARGSUSED */
+ static void
+ smbfs_delmap_async(void *varg)
+ {
+ smbfs_delmap_args_t *dmapp = varg;
+ cred_t *cr;
+ vnode_t *vp;
+ smbnode_t *np;
+ smbmntinfo_t *smi;
+
+ cr = dmapp->dm_cr;
+ vp = dmapp->dm_vp;
+ np = VTOSMB(vp);
+ smi = VTOSMI(vp);
+
+ /* Decremented r_mapcnt in smbfs_delmap */
+
+ /*
+ * Initiate a page flush and potential commit if there are
+ * pages, the file system was not mounted readonly, the segment
+ * was mapped shared, and the pages themselves were writeable.
+ *
+ * mark RDIRTY here, will be used to check if a file is dirty when
+ * unmount smbfs
+ */
+ if (vn_has_cached_data(vp) && !vn_is_readonly(vp) &&
+ dmapp->dm_flags == MAP_SHARED &&
+ (dmapp->dm_maxprot & PROT_WRITE) != 0) {
+ mutex_enter(&np->r_statelock);
+ np->r_flags |= RDIRTY;
+ mutex_exit(&np->r_statelock);
+
+ /*
+ * Need to finish the putpage before we
+ * close the OtW FID needed for I/O.
+ */
+ (void) smbfs_putpage(vp, dmapp->dm_off, dmapp->dm_len, 0,
+ dmapp->dm_cr, NULL);
+ }
+
+ if ((np->r_flags & RDIRECTIO) || (smi->smi_flags & SMI_DIRECTIO))
+ (void) smbfs_putpage(vp, dmapp->dm_off, dmapp->dm_len,
+ B_INVAL, dmapp->dm_cr, NULL);
+
+ /*
+ * If r_mapcnt went to zero, drop our FID ref now.
+ * On the last fidref, this does an OtW close.
+ */
+ if (dmapp->dm_rele_fid) {
+ struct smb_cred scred;
+
+ (void) smbfs_rw_enter_sig(&np->r_lkserlock, RW_WRITER, 0);
+ smb_credinit(&scred, dmapp->dm_cr);
+
+ smbfs_rele_fid(np, &scred);
+
+ smb_credrele(&scred);
+ smbfs_rw_exit(&np->r_lkserlock);
+ }
+
+ /* Release holds taken in smbfs_delmap */
+ VN_RELE(vp);
+ crfree(cr);
+
+ kmem_free(dmapp, sizeof (*dmapp));
+ }
+
+ /* No smbfs_pageio() or smbfs_dispose() ops. */
+
+ #endif // _KERNEL
+
+ /* misc. ******************************************************** */
+
+
+ /*
* XXX
* This op may need to support PSARC 2007/440, nbmand changes for CIFS Service.
*/
static int
smbfs_frlock(vnode_t *vp, int cmd, struct flock64 *bfp, int flag,
*** 2988,3006 ****
--- 4772,4801 ----
if (error || va.va_size == bfp->l_start)
return (error);
va.va_mask = AT_SIZE;
va.va_size = bfp->l_start;
error = smbfssetattr(vp, &va, 0, cr);
+ /* SMBFS_VNEVENT... */
} else
error = EINVAL;
}
return (error);
}
+
/* ARGSUSED */
static int
+ smbfs_realvp(vnode_t *vp, vnode_t **vpp, caller_context_t *ct)
+ {
+
+ return (ENOSYS);
+ }
+
+
+ /* ARGSUSED */
+ static int
smbfs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr,
caller_context_t *ct)
{
vfs_t *vfs;
smbmntinfo_t *smi;
*** 3171,3175 ****
--- 4966,5021 ----
if (VTOSMI(vp)->smi_flags & SMI_LLOCK)
return (fs_shrlock(vp, cmd, shr, flag, cr, ct));
else
return (ENOSYS);
}
+
+
+ /*
+ * 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[] = {
+ VOPNAME_OPEN, { .vop_open = smbfs_open },
+ VOPNAME_CLOSE, { .vop_close = smbfs_close },
+ VOPNAME_READ, { .vop_read = smbfs_read },
+ VOPNAME_WRITE, { .vop_write = smbfs_write },
+ VOPNAME_IOCTL, { .vop_ioctl = smbfs_ioctl },
+ VOPNAME_GETATTR, { .vop_getattr = smbfs_getattr },
+ VOPNAME_SETATTR, { .vop_setattr = smbfs_setattr },
+ VOPNAME_ACCESS, { .vop_access = smbfs_access },
+ VOPNAME_LOOKUP, { .vop_lookup = smbfs_lookup },
+ VOPNAME_CREATE, { .vop_create = smbfs_create },
+ VOPNAME_REMOVE, { .vop_remove = smbfs_remove },
+ VOPNAME_LINK, { .vop_link = smbfs_link },
+ VOPNAME_RENAME, { .vop_rename = smbfs_rename },
+ VOPNAME_MKDIR, { .vop_mkdir = smbfs_mkdir },
+ VOPNAME_RMDIR, { .vop_rmdir = smbfs_rmdir },
+ VOPNAME_READDIR, { .vop_readdir = smbfs_readdir },
+ VOPNAME_SYMLINK, { .vop_symlink = smbfs_symlink },
+ VOPNAME_READLINK, { .vop_readlink = smbfs_readlink },
+ VOPNAME_FSYNC, { .vop_fsync = smbfs_fsync },
+ VOPNAME_INACTIVE, { .vop_inactive = smbfs_inactive },
+ VOPNAME_FID, { .vop_fid = smbfs_fid },
+ VOPNAME_RWLOCK, { .vop_rwlock = smbfs_rwlock },
+ VOPNAME_RWUNLOCK, { .vop_rwunlock = smbfs_rwunlock },
+ VOPNAME_SEEK, { .vop_seek = smbfs_seek },
+ VOPNAME_FRLOCK, { .vop_frlock = smbfs_frlock },
+ VOPNAME_SPACE, { .vop_space = smbfs_space },
+ VOPNAME_REALVP, { .vop_realvp = smbfs_realvp },
+ #ifdef _KERNEL
+ VOPNAME_GETPAGE, { .vop_getpage = smbfs_getpage },
+ VOPNAME_PUTPAGE, { .vop_putpage = smbfs_putpage },
+ VOPNAME_MAP, { .vop_map = smbfs_map },
+ VOPNAME_ADDMAP, { .vop_addmap = smbfs_addmap },
+ VOPNAME_DELMAP, { .vop_delmap = smbfs_delmap },
+ #endif // _KERNEL
+ VOPNAME_PATHCONF, { .vop_pathconf = smbfs_pathconf },
+ VOPNAME_SETSECATTR, { .vop_setsecattr = smbfs_setsecattr },
+ VOPNAME_GETSECATTR, { .vop_getsecattr = smbfs_getsecattr },
+ VOPNAME_SHRLOCK, { .vop_shrlock = smbfs_shrlock },
+ #ifdef SMBFS_VNEVENT
+ VOPNAME_VNEVENT, { .vop_vnevent = fs_vnevent_support },
+ #endif
+ { NULL, NULL }
+ };