/* * Copyright (c) 2000-2001, Boris Popov * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Boris Popov. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: smbfs_vfsops.c,v 1.73 2005/03/09 16:52:00 lindak Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int smbfs_debuglevel = 0; static int smbfs_version = SMBFS_VERSION; #ifdef SMBFS_USEZONE #include #include #include vm_zone_t smbfsmount_zone; #endif #ifdef SYSCTL_DECL SYSCTL_DECL(_net_smb); #endif SYSCTL_NODE(_net_smb, OID_AUTO, fs, CTLFLAG_RW, 0, "SMB/CIFS file system"); SYSCTL_INT(_net_smb_fs, OID_AUTO, version, CTLFLAG_RD, &smbfs_version, 0, ""); SYSCTL_INT(_net_smb_fs, OID_AUTO, debuglevel, CTLFLAG_RW, &smbfs_debuglevel, 0, ""); extern struct sysctl_oid sysctl__net_smb; extern struct sysctl_oid sysctl__net_smb_fs_iconv; extern struct sysctl_oid sysctl__net_smb_fs_iconv_add; extern struct sysctl_oid sysctl__net_smb_fs_iconv_cslist; extern struct sysctl_oid sysctl__net_smb_fs_iconv_drvlist; MALLOC_DEFINE(M_SMBFSHASH, "SMBFS hash", "SMBFS hash table"); static int smbfs_mount(struct mount *, vnode_t, user_addr_t, vfs_context_t); static int smbfs_root(struct mount *, vnode_t *, vfs_context_t); static int smbfs_start(struct mount *, int, vfs_context_t); static int smbfs_vfs_getattr(struct mount *, struct vfs_attr *, vfs_context_t); static int smbfs_sync(struct mount *, int, vfs_context_t); static int smbfs_unmount(struct mount *, int, vfs_context_t); static int smbfs_init(struct vfsconf *vfsp); static int smbfs_sysctl(int *, u_int, user_addr_t, size_t *, user_addr_t, size_t, vfs_context_t); static int smbfs_vget(struct mount *, ino64_t , vnode_t *, vfs_context_t); static int smbfs_fhtovp(struct mount *, int, unsigned char *, vnode_t *, vfs_context_t); static int smbfs_vptofh(vnode_t, int *, unsigned char *, vfs_context_t); static struct vfsops smbfs_vfsops = { smbfs_mount, smbfs_start, smbfs_unmount, smbfs_root, NULL, /* quotactl */ smbfs_vfs_getattr, smbfs_sync, smbfs_vget, smbfs_fhtovp, smbfs_vptofh, smbfs_init, smbfs_sysctl }; VFS_SET(smbfs_vfsops, smbfs, VFCF_NETWORK); #ifdef MODULE_DEPEND MODULE_DEPEND(smbfs, netsmb, NSMB_VERSION, NSMB_VERSION, NSMB_VERSION); MODULE_DEPEND(smbfs, libiconv, 1, 1, 1); #endif int smbfs_pbuf_freecnt = -1; /* start out unlimited */ lck_grp_attr_t *co_grp_attr; lck_grp_t *co_lck_group; lck_attr_t *co_lck_attr; lck_grp_attr_t *vcst_grp_attr; lck_grp_t *vcst_lck_group; lck_attr_t *vcst_lck_attr; lck_grp_attr_t *ssst_grp_attr; lck_grp_t *ssst_lck_group; lck_attr_t *ssst_lck_attr; lck_grp_attr_t *iodflags_grp_attr; lck_grp_t *iodflags_lck_group; lck_attr_t *iodflags_lck_attr; lck_grp_attr_t *iodrq_grp_attr; lck_grp_t *iodrq_lck_group; lck_attr_t *iodrq_lck_attr; lck_grp_attr_t *iodev_grp_attr; lck_grp_t *iodev_lck_group; lck_attr_t *iodev_lck_attr; lck_grp_attr_t *srs_grp_attr; lck_grp_t *srs_lck_group; lck_attr_t *srs_lck_attr; lck_grp_attr_t *nbp_grp_attr; lck_grp_t *nbp_lck_group; lck_attr_t *nbp_lck_attr; lck_grp_attr_t * dev_lck_grp_attr; lck_grp_t * dev_lck_grp; lck_attr_t * dev_lck_attr; lck_mtx_t * dev_lck; lck_grp_attr_t * hash_lck_grp_attr; lck_grp_t * hash_lck_grp; lck_attr_t * hash_lck_attr; struct smbmnt_carg { vfs_context_t vfsctx; struct mount *mp; int found; }; static int smb_mnt_callback(struct mount *mp2, void *args) { struct smbmnt_carg *cargp; cargp = (struct smbmnt_carg *)args; if (vfs_statfs(mp2)->f_owner != kauth_cred_getuid(vfs_context_ucred(cargp->vfsctx))) return (VFS_RETURNED); if (strncmp(vfs_statfs(mp2)->f_mntfromname, vfs_statfs(cargp->mp)->f_mntfromname, MAXPATHLEN)) return (VFS_RETURNED); cargp->found = 1; return (VFS_RETURNED_DONE); } /* * These memberd constants should be in an memberd include file, obviously. * * memberd generates these UUIDs for well-known SIDs, and for translations * which Directory Services doesn't resolve. * * These temporary ids should probably go away altogether. A better approach * would be for the requests and replies between kernel and memberd to include * a scope or other indication of the server used to translate the identity. * So we might pass our file server's IP up and memberd/DS would use the * appropriate Active Directory or LDAP server, falling back to the file * server itself in the "ad hoc" scenario. */ #define IS_MEMBERD_TEMPUUID(uuidp) \ ((*(u_int64_t *)(uuidp) == 0xFFFFEEEEDDDDCCCCULL && \ *((u_int32_t *)(uuidp)+2) == 0xBBBBAAAA) || \ (*(u_int64_t *)(uuidp) == 0xAAAABBBBCCCCDDDDULL && \ *((u_int32_t *)(uuidp)+2) == 0xEEEEFFFF)) static int smbfs_aclsflunksniff(struct smbmount *smp, struct smb_cred *scrp) { struct smbnode *np = smp->sm_root; struct smb_share *ssp = smp->sm_share; int error, cerror; struct ntsecdesc *w_secp = NULL; /* Wire sec descriptor */ struct ntsid *usidp, *gsidp; ntsid_t kntsid; guid_t guid; u_int8_t ntauth[SIDAUTHSIZE] = { 0, 0, 0, 0, 0, 5 }; uid_t uid; u_int16_t fid = 0; /* * Does the server claim ACL support for this volume? */ if (!(smp->sm_flags & FILE_PERSISTENT_ACLS)) goto err; /* * Get a security descriptor from the share's root. */ error = smbfs_smb_tmpopen(np, STD_RIGHT_READ_CONTROL_ACCESS, scrp, &fid); if (error) { SMBERROR("smbfs_smb_tmpopen error %d\n", error); goto out; } error = smbfs_smb_getsec(ssp, fid, scrp, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION, &w_secp); cerror = smbfs_smb_tmpclose(np, fid, scrp); if (cerror) SMBERROR("error %d closing root fid %d\n", cerror, fid); if (error) { SMBERROR("smbfs_smb_getsec error %d\n", error); goto out; } /* * A null w_secp commonly means a FAT filesystem, but in that * case the FILE_PERSISTENT_ACL bit should not have been set. */ if (w_secp == NULL) { SMBERROR("null w_secp\n"); goto err; } usidp = sdowner(w_secp); if (!usidp) { SMBERROR("null owner\n"); goto err; } smb_sid_endianize(usidp); smb_sid2sid16(usidp, &kntsid); error = kauth_cred_ntsid2guid(&kntsid, &guid); if (error) { SMBERROR("kauth_cred_ntsid2guid error %d (owner)\n", error); goto out; } if (IS_MEMBERD_TEMPUUID(&guid)) { SMBERROR("user sid %s didnt map\n", smb_sid2str(usidp)); goto err; } gsidp = sdgroup(w_secp); if (!gsidp) { SMBERROR("null group\n"); goto err; } smb_sid_endianize(gsidp); smb_sid2sid16(gsidp, &kntsid); error = kauth_cred_ntsid2guid(&kntsid, &guid); if (error) { SMBERROR("kauth_cred_ntsid2guid error %d (group)\n", error); goto out; } if (IS_MEMBERD_TEMPUUID(&guid)) { SMBERROR("group sid %s didnt map\n", smb_sid2str(gsidp)); goto err; } /* * Can our DS subsystem give us a SID for the mounting user? */ error = kauth_cred_uid2ntsid(smp->sm_args.uid, &kntsid); if (error) { SMBERROR("sm_args.uid %d, error %d\n", smp->sm_args.uid, error); goto out; } /* * We accept SIDS of the form S-1-5-x-*, where x is not 32 * ("builtin" groups) and x > 20 (lower ones are "well-known") * Arguably a check could/should be done on the final subauthority * as those in the 500s are domain-specific well-knowns, while * those around 1000 are real users. */ if (kntsid.sid_kind != 1 || bcmp(kntsid.sid_authority, ntauth, sizeof(ntauth)) || kntsid.sid_authcount < 1 || kntsid.sid_authorities[0] <= 20 || kntsid.sid_authorities[0] == 32) { SMBERROR("sm_args.uid %d, sid %s\n", smp->sm_args.uid, smb_sid2str((struct ntsid *)&kntsid)); goto err; } error = kauth_cred_ntsid2uid(&kntsid, &uid); if (error) { SMBERROR("sid %s, error %d\n", smb_sid2str((struct ntsid *)&kntsid), error); goto out; } if (uid == smp->sm_args.uid) goto out; SMBERROR("sm_args.uid %d, uid %d\n", smp->sm_args.uid, uid); err: error = -1; out: if (w_secp) FREE(w_secp, M_TEMP); return (error); } static int smbfs_mount(struct mount *mp, vnode_t devvp, user_addr_t data, vfs_context_t vfsctx) { struct smbfs_args args; /* will hold data from mount request */ struct smbmount *smp = NULL; struct smb_vc *vcp; struct smb_share *ssp = NULL; vnode_t vp; struct smb_cred scred; int error; char *pc, *pe; struct smbmnt_carg carg; if (data == USER_ADDR_NULL) { printf("missing data argument\n"); return (EINVAL); } if (vfs_isupdate(mp)) { printf("MNT_UPDATE not implemented"); return (ENOTSUP); } error = copyin(data, (caddr_t)&args, sizeof(args)); if (error) return (error); if (args.version != SMBFS_VERSION) { printf("mount version mismatch: kernel=%d, mount=%d\n", SMBFS_VERSION, args.version); return (EINVAL); } error = smb_dev2share(args.dev, &ssp); if (error) { printf("invalid device handle %d (%d)\n", args.dev, error); return (error); } smb_scred_init(&scred, vfsctx); vcp = SSTOVC(ssp); smb_share_unlock(ssp, vfs_context_proc(vfsctx)); #ifdef SMBFS_USEZONE smp = zalloc(smbfsmount_zone); #else MALLOC(smp, struct smbmount*, sizeof(*smp), M_SMBFSDATA, M_USE_RESERVE); #endif if (smp == NULL) { printf("could not alloc smbmount\n"); error = ENOMEM; goto bad; } bzero(smp, sizeof(*smp)); smp->sm_mp = mp; vfs_setfsprivate(mp, (void *)smp); ssp->ss_mount = smp; smp->sm_hash = hashinit(desiredvnodes, M_SMBFSHASH, &smp->sm_hashlen); if (smp->sm_hash == NULL) goto bad; smp->sm_hashlock = lck_mtx_alloc_init(hash_lck_grp, hash_lck_attr); smp->sm_share = ssp; smp->sm_root = NULL; smp->sm_args = args; smp->sm_caseopt = args.caseopt; smp->sm_args.file_mode = smp->sm_args.file_mode & ACCESSPERMS; smp->sm_args.dir_mode = smp->sm_args.dir_mode & ACCESSPERMS; pc = vfs_statfs(mp)->f_mntfromname; pe = pc + sizeof(vfs_statfs(mp)->f_mntfromname); bzero(pc, MNAMELEN); *pc++ = '/'; *pc++ = '/'; pc=index(strncpy(pc, vcp->vc_username, pe - pc - 2), 0); if (pc < pe-1) { *(pc++) = '@'; pc = index(strncpy(pc, vcp->vc_srvname, pe - pc - 2), 0); if (pc < pe - 1) { *(pc++) = '/'; strncpy(pc, ssp->ss_name, pe - pc - 2); } } /* * XXX * This circumvents the "unique disk id" design flaw by disallowing * multiple mounts of the same filesystem as the same user. The flaw * is that URLMount, DiskArb, FileManager, Finder added new semantics * to f_mntfromname, requiring that field to be a unique id for a * given mount. (Another flaw added is to require that that field * have sufficient information to remount. That is not solved here.) * This is XXX because it cripples multiple mounting, a traditional * unix feature useful in multiuser and chroot-ed environments. This * limitation can often be (manually) avoided by altering the remote * login name - even a difference in case is sufficient, and * should authenticate as if there were no difference. * * Details: * "funnel" keeps us from having to lock mountlist during scan. * The scan could be more cpu-efficient, but mounts are not a hot spot. * Scanning mountlist is more robust than using smb_vclist. * * Changed for multisession: we now allow the multple mounts if being * done by a different user OR if not on the desktop. */ if (!(vfs_flags(mp) & MNT_DONTBROWSE)) { carg.vfsctx = vfsctx; carg.mp = mp; carg.found = 0; (void)vfs_iterate(0, smb_mnt_callback, &carg); if (carg.found) { error = EBUSY; goto bad; } } vfs_setauthopaque(mp); vfs_clearauthopaqueaccess(mp); /* protect against invalid mount points */ smp->sm_args.mount_point[sizeof(smp->sm_args.mount_point) - 1] = '\0'; vfs_getnewfsid(mp); error = smbfs_root(mp, &vp, vfsctx); if (error) goto bad; vfs_clearextendedsecurity(mp); error = smbfs_smb_qfsattr(ssp, &smp->sm_flags, &scred); if (error) { SMBERROR("smbfs_smb_qfsattr error %d\n", error); } else if (smbfs_aclsflunksniff(smp, &scred)) { smp->sm_flags &= ~FILE_PERSISTENT_ACLS; } else vfs_setextendedsecurity(mp); /* * Ensure cached info is set to reasonable values. * * XXX this call should be done from mount() in vfs layer. */ (void)vfs_update_vfsstat(mp, vfsctx); vnode_ref(vp); vnode_put(vp); return (error); bad: if (smp) { vfs_setfsprivate(mp, (void *)0); if (smp->sm_hash) free(smp->sm_hash, M_SMBFSHASH); lck_mtx_free(smp->sm_hashlock, hash_lck_grp); #ifdef SMBFS_USEZONE zfree(smbfsmount_zone, smp); #else free(smp, M_SMBFSDATA); #endif } if (ssp) smb_share_put(ssp, &scred); return (error); } /* Unmount the filesystem described by mp. */ static int smbfs_unmount(struct mount *mp, int mntflags, vfs_context_t vfsctx) { struct smbmount *smp = VFSTOSMBFS(mp); vnode_t vp; struct smb_cred scred; int error, flags; SMBVDEBUG("smbfs_unmount: flags=%04x\n", mntflags); flags = 0; if (mntflags & MNT_FORCE) { flags |= FORCECLOSE; wakeup(&smp->sm_status); /* sleeps are down in smb_rq.c */ } error = smbfs_root(mp, &vp, vfsctx); if (error) return (error); error = vflush(mp, vp, flags); if (error) { vnode_put(vp); return (error); } if (vnode_isinuse(vp, 1) && !(flags & FORCECLOSE)) { printf("smbfs_unmount: usecnt\n"); vnode_put(vp); return (EBUSY); } vnode_rele(vp); /* to drop ref taken by smbfs_mount */ vnode_put(vp); /* to drop ref taken by VFS_ROOT above */ (void)vflush(mp, NULLVP, FORCECLOSE); if (flags & FORCECLOSE) { /* * Shutdown all outstanding I/O requests on this share. */ smb_iod_shutdown_share(smp->sm_share); } smb_scred_init(&scred, vfsctx); smp->sm_share->ss_mount = NULL; smb_share_put(smp->sm_share, &scred); vfs_setfsprivate(mp, (void *)0); if (smp->sm_hash) { free(smp->sm_hash, M_SMBFSHASH); smp->sm_hash = (void *)0xDEAD5AB0; } lck_mtx_free(smp->sm_hashlock, hash_lck_grp); #ifdef SMBFS_USEZONE zfree(smbfsmount_zone, smp); #else free(smp, M_SMBFSDATA); #endif vfs_clearflags(mp, MNT_LOCAL); return (error); } /* * Return locked root vnode of a filesystem */ static int smbfs_root(struct mount *mp, vnode_t *vpp, vfs_context_t vfsctx) { struct smbmount *smp = VFSTOSMBFS(mp); vnode_t vp; struct smbnode *np; struct smbfattr fattr; struct smb_cred scred; int error; if (smp == NULL) { SMBERROR("smp == NULL (bug in umount)\n"); return (EINVAL); } if (smp->sm_root) { *vpp = SMBTOV(smp->sm_root); return (vnode_get(*vpp)); } smb_scred_init(&scred, vfsctx); error = smbfs_smb_lookup(NULL, NULL, NULL, &fattr, &scred); if (error) return (error); error = smbfs_nget(mp, NULL, "TheRooT", 7, &fattr, &vp, !MAKEENTRY, 0); if (error) return (error); np = VTOSMB(vp); smp->sm_root = np; *vpp = vp; return (0); } /* * Vfs start routine, a no-op. */ /* ARGSUSED */ static int smbfs_start(struct mount *mp, int flags, vfs_context_t vfsctx) { #pragma unused(mp, flags, vfsctx) return 0; } /*ARGSUSED*/ int smbfs_init(struct vfsconf *vfsp) { #pragma unused(vfsp) smb_checksmp(); #ifdef SMBFS_USEZONE smbfsmount_zone = zinit("SMBFSMOUNT", sizeof(struct smbmount), 0, 0, 1); #endif SMBVDEBUG("done.\n"); return 0; } /* * smbfs_statfs call */ int smbfs_vfs_getattr(struct mount *mp, struct vfs_attr *fsap, vfs_context_t vfsctx) { struct smbmount *smp = VFSTOSMBFS(mp); struct smbnode *np = smp->sm_root; struct smb_share *ssp = smp->sm_share; struct statfs *cachedstatfs = &smp->sm_statfsbuf; struct smb_cred scred; struct timespec ts; int error = 0; if (np == NULL) return (EINVAL); /* while there's a smbfs_smb_statfs request outstanding, sleep */ while (smp->sm_status & SM_STATUS_STATFS) { smp->sm_status |= SM_STATUS_STATFS_WANTED; (void) msleep((caddr_t)&smp->sm_status, 0, PRIBIO, "smbfs_vfs_getattr", 0); } /* we're making a request so grab the token */ smp->sm_status |= SM_STATUS_STATFS; /* if from-the-server data is wanted and our cache is stale... */ nanotime(&ts); if (ts.tv_sec - smp->sm_statfstime > SM_MAX_STATFSTIME && (VFSATTR_IS_ACTIVE(fsap, f_bsize) || VFSATTR_IS_ACTIVE(fsap, f_blocks) || VFSATTR_IS_ACTIVE(fsap, f_bfree) || VFSATTR_IS_ACTIVE(fsap, f_bavail) || VFSATTR_IS_ACTIVE(fsap, f_files) || VFSATTR_IS_ACTIVE(fsap, f_ffree))) { /* update cached from-the-server data */ smb_scred_init(&scred, vfsctx); if (SMB_DIALECT(SSTOVC(ssp)) >= SMB_DIALECT_LANMAN2_0) error = smbfs_smb_statfs2(ssp, cachedstatfs, &scred); else error = smbfs_smb_statfs(ssp, cachedstatfs, &scred); if (error) goto releasetoken; nanotime(&ts); smp->sm_statfstime = ts.tv_sec; } /* copy results from cached statfs */ VFSATTR_RETURN(fsap, f_bsize, cachedstatfs->f_bsize); VFSATTR_RETURN(fsap, f_blocks, cachedstatfs->f_blocks); VFSATTR_RETURN(fsap, f_bfree, cachedstatfs->f_bfree); VFSATTR_RETURN(fsap, f_bavail, cachedstatfs->f_bavail); VFSATTR_RETURN(fsap, f_files, cachedstatfs->f_files); VFSATTR_RETURN(fsap, f_ffree, cachedstatfs->f_ffree); releasetoken: /* we're done, so release the token */ smp->sm_status &= ~SM_STATUS_STATFS; /* if anyone else is waiting, wake them up */ if (smp->sm_status & SM_STATUS_STATFS_WANTED) { smp->sm_status &= ~SM_STATUS_STATFS_WANTED; wakeup((caddr_t)&smp->sm_status); } if (error) return (error); /* Give the Finder et al a better hint */ VFSATTR_RETURN(fsap, f_iosize, 128 * 1024); /* * ref 3984574. Returning null here keeps vfs from returning * f_mntonname, and causes CarbonCore (File Mgr) to use the * f_mntfromname, as it did (& still does) when an error is returned. */ if (VFSATTR_IS_ACTIVE(fsap, f_vol_name) && fsap->f_vol_name) { *fsap->f_vol_name = '\0'; VFSATTR_SET_SUPPORTED(fsap, f_vol_name); } if (VFSATTR_IS_ACTIVE(fsap, f_capabilities)) { vol_capabilities_attr_t *cap = &fsap->f_capabilities; cap->capabilities[VOL_CAPABILITIES_FORMAT] = VOL_CAP_FMT_SYMBOLICLINKS | VOL_CAP_FMT_NO_ROOT_TIMES | VOL_CAP_FMT_FAST_STATFS; cap->capabilities[VOL_CAPABILITIES_INTERFACES] = VOL_CAP_INT_FLOCK; cap->capabilities[VOL_CAPABILITIES_RESERVED1] = 0; cap->capabilities[VOL_CAPABILITIES_RESERVED2] = 0; /* * This file system is case sensitive and case preserving, but * servers vary, depending on the underlying volume. So rather * than providing a wrong yes or no answer we deny knowledge of * VOL_CAP_FMT_CASE_SENSITIVE and VOL_CAP_FMT_CASE_PRESERVING; */ cap->valid[VOL_CAPABILITIES_FORMAT] = VOL_CAP_FMT_PERSISTENTOBJECTIDS | VOL_CAP_FMT_SYMBOLICLINKS | VOL_CAP_FMT_HARDLINKS | VOL_CAP_FMT_JOURNAL | VOL_CAP_FMT_JOURNAL_ACTIVE | VOL_CAP_FMT_NO_ROOT_TIMES | VOL_CAP_FMT_SPARSE_FILES | VOL_CAP_FMT_ZERO_RUNS | VOL_CAP_FMT_FAST_STATFS; cap->valid[VOL_CAPABILITIES_INTERFACES] = VOL_CAP_INT_SEARCHFS | VOL_CAP_INT_ATTRLIST | VOL_CAP_INT_NFSEXPORT | VOL_CAP_INT_READDIRATTR | VOL_CAP_INT_EXCHANGEDATA | VOL_CAP_INT_COPYFILE | VOL_CAP_INT_ALLOCATE | VOL_CAP_INT_VOL_RENAME | VOL_CAP_INT_ADVLOCK | VOL_CAP_INT_FLOCK; cap->valid[VOL_CAPABILITIES_RESERVED1] = 0; cap->valid[VOL_CAPABILITIES_RESERVED2] = 0; VFSATTR_SET_SUPPORTED(fsap, f_capabilities); } return (error); } struct smbfs_sync_cargs { vfs_context_t vfsctx; int waitfor; int error; }; static int smbfs_sync_callback(vnode_t vp, void *args) { int error; struct smbfs_sync_cargs *cargs; cargs = (struct smbfs_sync_cargs *)args; if (vnode_hasdirtyblks(vp)) { error = VNOP_FSYNC(vp, cargs->waitfor, cargs->vfsctx); if (error) cargs->error = error; } return (VNODE_RETURNED); } /* * Flush out the buffer cache */ /* ARGSUSED */ static int smbfs_sync(struct mount *mp, int waitfor, vfs_context_t vfsctx) { struct smbfs_sync_cargs args; args.vfsctx = vfsctx; args.waitfor = waitfor; args.error = 0; /* * Force stale buffer cache information to be flushed. * * sbmfs_sync_callback will be called for each vnode * hung off of this mount point... the vnode will be * properly referenced and unreferenced around the callback */ vnode_iterate(mp, VNODE_ITERATE_ACTIVE, smbfs_sync_callback, (void *)&args); return (args.error); } /* * smbfs flat namespace lookup. Unsupported. */ /* ARGSUSED */ static int smbfs_vget(struct mount *mp, ino64_t ino, vnode_t *vpp, vfs_context_t vfsctx) { #pragma unused(mp, ino, vpp, vfsctx) return (ENOTSUP); } /* ARGSUSED */ static int smbfs_fhtovp(struct mount *mp, int fhlen, unsigned char *fhp, vnode_t *vpp, vfs_context_t vfsctx) { #pragma unused(mp, fhlen, fhp, vpp, vfsctx) return (EINVAL); } /* * Vnode pointer to File handle, should never happen either */ /* ARGSUSED */ static int smbfs_vptofh(vnode_t vp, int *fhlen, unsigned char *fhp, vfs_context_t vfsctx) { #pragma unused(vp, fhlen, fhp, vfsctx) return (EINVAL); } /* * smbfs_sysctl handles the VFS_CTL_QUERY request which tells interested * parties if the connection with the remote server is up or down. */ static int smbfs_sysctl(int * name, u_int namelen, user_addr_t oldp, size_t * oldlenp, user_addr_t newp, size_t newlen, vfs_context_t vfsctx) { #pragma unused(oldlenp, newp, newlen) int error; struct sysctl_req *req; struct vfsidctl vc; struct user_vfsidctl user_vc; struct mount *mp; struct smbmount *smp; struct vfsquery vq; /* * All names at this level are terminal. */ if (namelen > 1) return (ENOTDIR); /* overloaded */ switch (name[0]) { case VFS_CTL_QUERY: req = CAST_DOWN(struct sysctl_req *, oldp); /* we're new style vfs sysctl. */ if (vfs_context_is64bit(vfsctx)) { error = SYSCTL_IN(req, &user_vc, sizeof(user_vc)); if (error) break; mp = vfs_getvfs(&user_vc.vc_fsid); } else { error = SYSCTL_IN(req, &vc, sizeof(vc)); if (error) break; mp = vfs_getvfs(&vc.vc_fsid); } if (!mp) { error = ENOENT; } else { smp = VFSTOSMBFS(mp); bzero(&vq, sizeof(vq)); if (smp && (smp->sm_status & SM_STATUS_DEAD)) vq.vq_flags |= VQ_DEAD; else if (smp && (smp->sm_status & SM_STATUS_TIMEO)) vq.vq_flags |= VQ_NOTRESP; error = SYSCTL_OUT(req, &vq, sizeof(vq)); } break; default: error = ENOTSUP; break; } return (error); } static char smbfs_name[MFSNAMELEN] = "smbfs"; kmod_info_t *smbfs_kmod_infop; typedef int (*PFI)(); extern struct vnodeopv_desc smbfs_vnodeop_opv_desc; static struct vnodeopv_desc *smbfs_vnodeop_opv_desc_list[1] = { &smbfs_vnodeop_opv_desc }; extern int version_major; extern int version_minor; static vfstable_t smbfs_vfsconf; __private_extern__ int smbfs_module_start(kmod_info_t *ki, void *data) { #pragma unused(data) struct vfs_fsentry vfe; int error; boolean_t funnel_state; #if 0 /* instead of this just set breakpoint on kmod_start_or_stop */ Debugger("smb module start"); #endif funnel_state = thread_funnel_set(kernel_flock, TRUE); smbfs_kmod_infop = ki; vfe.vfe_vfsops = &smbfs_vfsops; vfe.vfe_vopcnt = 1; /* We just have vnode operations for regular files and directories */ vfe.vfe_opvdescs = smbfs_vnodeop_opv_desc_list; strncpy(vfe.vfe_fsname, smbfs_name, MFSNAMELEN); vfe.vfe_flags = VFS_TBLNOTYPENUM | VFS_TBL64BITREADY; /* 64bit mount & sysctl & ioctl */ vfe.vfe_reserv[0] = 0; vfe.vfe_reserv[1] = 0; error = vfs_fsadd(&vfe, &smbfs_vfsconf); if (!error) { co_grp_attr = lck_grp_attr_alloc_init(); lck_grp_attr_setstat(co_grp_attr); co_lck_group = lck_grp_alloc_init("smb-co", co_grp_attr); co_lck_attr = lck_attr_alloc_init(); lck_attr_setdebug(co_lck_attr); vcst_grp_attr = lck_grp_attr_alloc_init(); lck_grp_attr_setstat(vcst_grp_attr); vcst_lck_group = lck_grp_alloc_init("smb-vcst", vcst_grp_attr); vcst_lck_attr = lck_attr_alloc_init(); lck_attr_setdebug(vcst_lck_attr); ssst_grp_attr = lck_grp_attr_alloc_init(); lck_grp_attr_setstat(ssst_grp_attr); ssst_lck_group = lck_grp_alloc_init("smb-ssst", ssst_grp_attr); ssst_lck_attr = lck_attr_alloc_init(); lck_attr_setdebug(ssst_lck_attr); iodflags_grp_attr = lck_grp_attr_alloc_init(); lck_grp_attr_setstat(iodflags_grp_attr); iodflags_lck_group = lck_grp_alloc_init("smb-iodflags", iodflags_grp_attr); iodflags_lck_attr = lck_attr_alloc_init(); lck_attr_setdebug(iodflags_lck_attr); iodrq_grp_attr = lck_grp_attr_alloc_init(); lck_grp_attr_setstat(iodrq_grp_attr); iodrq_lck_group = lck_grp_alloc_init("smb-iodrq", iodrq_grp_attr); iodrq_lck_attr = lck_attr_alloc_init(); lck_attr_setdebug(iodrq_lck_attr); iodev_grp_attr = lck_grp_attr_alloc_init(); lck_grp_attr_setstat(iodev_grp_attr); iodev_lck_group = lck_grp_alloc_init("smb-iodev", iodev_grp_attr); iodev_lck_attr = lck_attr_alloc_init(); lck_attr_setdebug(iodev_lck_attr); srs_grp_attr = lck_grp_attr_alloc_init(); lck_grp_attr_setstat(srs_grp_attr); srs_lck_group = lck_grp_alloc_init("smb-srs", srs_grp_attr); srs_lck_attr = lck_attr_alloc_init(); lck_attr_setdebug(srs_lck_attr); nbp_grp_attr = lck_grp_attr_alloc_init(); lck_grp_attr_setstat(nbp_grp_attr); nbp_lck_group = lck_grp_alloc_init("smb-nbp", nbp_grp_attr); nbp_lck_attr = lck_attr_alloc_init(); lck_attr_setdebug(nbp_lck_attr); dev_lck_grp_attr = lck_grp_attr_alloc_init(); lck_grp_attr_setstat(dev_lck_grp_attr); dev_lck_grp = lck_grp_alloc_init("smb-dev", dev_lck_grp_attr); dev_lck_attr = lck_attr_alloc_init(); lck_attr_setdebug(dev_lck_attr); dev_lck = lck_mtx_alloc_init(dev_lck_grp, dev_lck_attr); hash_lck_grp_attr = lck_grp_attr_alloc_init(); lck_grp_attr_setstat(hash_lck_grp_attr); hash_lck_grp = lck_grp_alloc_init("smb-hash", hash_lck_grp_attr); hash_lck_attr = lck_attr_alloc_init(); lck_attr_setdebug(hash_lck_attr); SEND_EVENT(iconv, MOD_LOAD); SEND_EVENT(iconv_ces, MOD_LOAD); #ifdef XXX /* apparently unused */ SEND_EVENT(iconv_xlat_?, MOD_LOAD); /* iconv_xlatmod_handler */ SEND_EVENT(iconv_ces_table, MOD_LOAD); #endif SEND_EVENT(iconv_xlat, MOD_LOAD); /* Bring up UTF-8 converter */ SEND_EVENT(iconv_utf8, MOD_LOAD); iconv_add("utf8", "utf-8", "ucs-2"); iconv_add("utf8", "ucs-2", "utf-8"); /* Bring up default codepage converter */ SEND_EVENT(iconv_codepage, MOD_LOAD); iconv_add("codepage", "utf-8", "cp437"); iconv_add("codepage", "cp437", "utf-8"); SEND_EVENT(dev_netsmb, MOD_LOAD); sysctl_register_oid(&sysctl__net_smb); sysctl_register_oid(&sysctl__net_smb_fs); sysctl_register_oid(&sysctl__net_smb_fs_iconv); sysctl_register_oid(&sysctl__net_smb_fs_iconv_add); sysctl_register_oid(&sysctl__net_smb_fs_iconv_cslist); sysctl_register_oid(&sysctl__net_smb_fs_iconv_drvlist); smbfs_install_sleep_wake_notifier(); } (void) thread_funnel_set(kernel_flock, funnel_state); return (error ? KERN_FAILURE : KERN_SUCCESS); } __private_extern__ int smbfs_module_stop(kmod_info_t *ki, void *data) { #pragma unused(ki) #pragma unused(data) int error; boolean_t funnel_state; funnel_state = thread_funnel_set(kernel_flock, TRUE); sysctl_unregister_oid(&sysctl__net_smb_fs_iconv_drvlist); sysctl_unregister_oid(&sysctl__net_smb_fs_iconv_cslist); sysctl_unregister_oid(&sysctl__net_smb_fs_iconv_add); sysctl_unregister_oid(&sysctl__net_smb_fs_iconv); sysctl_unregister_oid(&sysctl__net_smb_fs); sysctl_unregister_oid(&sysctl__net_smb); error = vfs_fsremove(smbfs_vfsconf); SEND_EVENT(dev_netsmb, MOD_UNLOAD); SEND_EVENT(iconv_xlat, MOD_UNLOAD); SEND_EVENT(iconv_utf8, MOD_UNLOAD); #ifdef XXX /* apparently unused */ SEND_EVENT(iconv_ces_table, MOD_UNLOAD); SEND_EVENT(iconv_xlat_?, MOD_UNLOAD); /* iconv_xlatmod_handler */ #endif SEND_EVENT(iconv_ces, MOD_UNLOAD); SEND_EVENT(iconv, MOD_UNLOAD); smbfs_remove_sleep_wake_notifier(); lck_mtx_free(dev_lck, dev_lck_grp); (void) thread_funnel_set(kernel_flock, funnel_state); return (error ? KERN_FAILURE : KERN_SUCCESS); }