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 } + };