1/* $NetBSD: smbfs_vfsops.c,v 1.95 2011/10/07 09:35:05 hannken Exp $ */ 2 3/* 4 * Copyright (c) 2000-2001, Boris Popov 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Boris Popov. 18 * 4. Neither the name of the author nor the names of any co-contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * FreeBSD: src/sys/fs/smbfs/smbfs_vfsops.c,v 1.5 2001/12/13 13:08:34 sheldonh Exp 35 */ 36 37#include <sys/cdefs.h> 38__KERNEL_RCSID(0, "$NetBSD: smbfs_vfsops.c,v 1.95 2011/10/07 09:35:05 hannken Exp $"); 39 40#include <sys/param.h> 41#include <sys/systm.h> 42#include <sys/proc.h> 43#include <sys/buf.h> 44#include <sys/kernel.h> 45#include <sys/dirent.h> 46#include <sys/sysctl.h> 47#include <sys/vnode.h> 48#include <sys/mount.h> 49#include <sys/stat.h> 50#include <sys/malloc.h> 51#include <sys/kauth.h> 52#include <sys/module.h> 53#include <miscfs/genfs/genfs.h> 54 55 56#include <netsmb/smb.h> 57#include <netsmb/smb_conn.h> 58#include <netsmb/smb_subr.h> 59#include <netsmb/smb_dev.h> 60 61#include <fs/smbfs/smbfs.h> 62#include <fs/smbfs/smbfs_node.h> 63#include <fs/smbfs/smbfs_subr.h> 64 65MODULE(MODULE_CLASS_VFS, smbfs, NULL); 66 67VFS_PROTOS(smbfs); 68 69static struct sysctllog *smbfs_sysctl_log; 70 71static int smbfs_setroot(struct mount *); 72 73extern struct vnodeopv_desc smbfs_vnodeop_opv_desc; 74 75static const struct vnodeopv_desc *smbfs_vnodeopv_descs[] = { 76 &smbfs_vnodeop_opv_desc, 77 NULL, 78}; 79 80struct vfsops smbfs_vfsops = { 81 MOUNT_SMBFS, 82 sizeof (struct smbfs_args), 83 smbfs_mount, 84 smbfs_start, 85 smbfs_unmount, 86 smbfs_root, 87 (void *)eopnotsupp, /* vfs_quotactl */ 88 smbfs_statvfs, 89 smbfs_sync, 90 smbfs_vget, 91 (void *)eopnotsupp, /* vfs_fhtovp */ 92 (void *)eopnotsupp, /* vfs_vptofh */ 93 smbfs_init, 94 smbfs_reinit, 95 smbfs_done, 96 (int (*) (void)) eopnotsupp, /* mountroot */ 97 (int (*)(struct mount *, struct vnode *, struct timespec *)) eopnotsupp, 98 vfs_stdextattrctl, 99 (void *)eopnotsupp, /* vfs_suspendctl */ 100 genfs_renamelock_enter, 101 genfs_renamelock_exit, 102 (void *)eopnotsupp, 103 smbfs_vnodeopv_descs, 104 0, /* vfs_refcount */ 105 { NULL, NULL }, 106}; 107 108static int 109smbfs_modcmd(modcmd_t cmd, void *arg) 110{ 111 const struct sysctlnode *smb = NULL; 112 int error; 113 114 switch (cmd) { 115 case MODULE_CMD_INIT: 116 error = vfs_attach(&smbfs_vfsops); 117 if (error != 0) 118 break; 119 sysctl_createv(&smbfs_sysctl_log, 0, NULL, NULL, 120 CTLFLAG_PERMANENT, 121 CTLTYPE_NODE, "vfs", NULL, 122 NULL, 0, NULL, 0, 123 CTL_VFS, CTL_EOL); 124 sysctl_createv(&smbfs_sysctl_log, 0, NULL, &smb, 125 CTLFLAG_PERMANENT, 126 CTLTYPE_NODE, "samba", 127 SYSCTL_DESCR("SMB/CIFS remote file system"), 128 NULL, 0, NULL, 0, 129 CTL_VFS, CTL_CREATE, CTL_EOL); 130 131 if (smb != NULL) { 132 sysctl_createv(&smbfs_sysctl_log, 0, &smb, NULL, 133 CTLFLAG_PERMANENT|CTLFLAG_IMMEDIATE, 134 CTLTYPE_INT, "version", 135 SYSCTL_DESCR("smbfs version"), 136 NULL, SMBFS_VERSION, NULL, 0, 137 CTL_CREATE, CTL_EOL); 138 } 139 break; 140 case MODULE_CMD_FINI: 141 error = vfs_detach(&smbfs_vfsops); 142 if (error != 0) 143 break; 144 sysctl_teardown(&smbfs_sysctl_log); 145 break; 146 default: 147 error = ENOTTY; 148 break; 149 } 150 151 return (error); 152} 153 154int 155smbfs_mount(struct mount *mp, const char *path, void *data, size_t *data_len) 156{ 157 struct lwp *l = curlwp; 158 struct smbfs_args *args = data; /* holds data from mount request */ 159 struct smbmount *smp = NULL; 160 struct smb_vc *vcp; 161 struct smb_share *ssp = NULL; 162 struct smb_cred scred; 163 struct proc *p; 164 char *fromname; 165 int error; 166 167 if (args == NULL) 168 return EINVAL; 169 if (*data_len < sizeof *args) 170 return EINVAL; 171 172 p = l->l_proc; 173 if (mp->mnt_flag & MNT_GETARGS) { 174 smp = VFSTOSMBFS(mp); 175 if (smp == NULL) 176 return EIO; 177 *args = smp->sm_args; 178 *data_len = sizeof *args; 179 return 0; 180 } 181 182 if (mp->mnt_flag & MNT_UPDATE) 183 return EOPNOTSUPP; 184 185 if (args->version != SMBFS_VERSION) { 186 SMBVDEBUG("mount version mismatch: kernel=%d, mount=%d\n", 187 SMBFS_VERSION, args->version); 188 return EINVAL; 189 } 190 191 smb_makescred(&scred, l, l->l_cred); 192 error = smb_dev2share(args->dev_fd, SMBM_EXEC, &scred, &ssp); 193 if (error) 194 return error; 195 smb_share_unlock(ssp); /* keep ref, but unlock */ 196 vcp = SSTOVC(ssp); 197 198 fromname = kmem_zalloc(MNAMELEN, KM_SLEEP); 199 snprintf(fromname, MNAMELEN, 200 "//%s@%s/%s", vcp->vc_username, vcp->vc_srvname, ssp->ss_name); 201 error = set_statvfs_info(path, UIO_USERSPACE, fromname, UIO_SYSSPACE, 202 mp->mnt_op->vfs_name, mp, l); 203 kmem_free(fromname, MNAMELEN); 204 if (error) { 205 smb_share_lock(ssp); 206 smb_share_put(ssp, &scred); 207 return error; 208 } 209 210 mp->mnt_stat.f_iosize = vcp->vc_txmax; 211 mp->mnt_stat.f_namemax = 212 (vcp->vc_hflags2 & SMB_FLAGS2_KNOWS_LONG_NAMES) ? 255 : 12; 213 214 smp = malloc(sizeof(*smp), M_SMBFSDATA, M_WAITOK|M_ZERO); 215 mp->mnt_data = smp; 216 217 smp->sm_hash = hashinit(desiredvnodes, HASH_LIST, true, 218 &smp->sm_hashlen); 219 220 mutex_init(&smp->sm_hashlock, MUTEX_DEFAULT, IPL_NONE); 221 smp->sm_share = ssp; 222 smp->sm_root = NULL; 223 smp->sm_args = *args; 224 smp->sm_caseopt = args->caseopt; 225 smp->sm_args.file_mode = (smp->sm_args.file_mode & 226 (S_IRWXU|S_IRWXG|S_IRWXO)) | S_IFREG; 227 smp->sm_args.dir_mode = (smp->sm_args.dir_mode & 228 (S_IRWXU|S_IRWXG|S_IRWXO)) | S_IFDIR; 229 230 vfs_getnewfsid(mp); 231 return (0); 232} 233 234/* Unmount the filesystem described by mp. */ 235int 236smbfs_unmount(struct mount *mp, int mntflags) 237{ 238 struct lwp *l = curlwp; 239 struct smbmount *smp = VFSTOSMBFS(mp); 240 struct smb_cred scred; 241 struct vnode *smbfs_rootvp = SMBTOV(smp->sm_root); 242 int error, flags; 243 244 SMBVDEBUG("smbfs_unmount: flags=%04x\n", mntflags); 245 flags = 0; 246 if (mntflags & MNT_FORCE) 247 flags |= FORCECLOSE; 248 249 if (smbfs_rootvp->v_usecount > 1 && (mntflags & MNT_FORCE) == 0) 250 return EBUSY; 251 252 /* Flush all vnodes. 253 * Keep trying to flush the vnode list for the mount while 254 * some are still busy and we are making progress towards 255 * making them not busy. This is needed because smbfs vnodes 256 * reference their parent directory but may appear after their 257 * parent in the list; one pass over the vnode list is not 258 * sufficient in this case. */ 259 do { 260 smp->sm_didrele = 0; 261 error = vflush(mp, smbfs_rootvp, flags); 262 } while (error == EBUSY && smp->sm_didrele != 0); 263 if (error) 264 return error; 265 266 vgone(smbfs_rootvp); 267 268 smb_makescred(&scred, l, l->l_cred); 269 smb_share_lock(smp->sm_share); 270 smb_share_put(smp->sm_share, &scred); 271 mp->mnt_data = NULL; 272 273 hashdone(smp->sm_hash, HASH_LIST, smp->sm_hashlen); 274 mutex_destroy(&smp->sm_hashlock); 275 free(smp, M_SMBFSDATA); 276 return 0; 277} 278 279/* 280 * Get root vnode of the smbfs filesystem, and store it in sm_root. 281 */ 282static int 283smbfs_setroot(struct mount *mp) 284{ 285 struct smbmount *smp = VFSTOSMBFS(mp); 286 struct vnode *vp; 287 struct smbfattr fattr; 288 struct lwp *l = curlwp; 289 kauth_cred_t cred = l->l_cred; 290 struct smb_cred scred; 291 int error; 292 293 KASSERT(smp->sm_root == NULL); 294 295 smb_makescred(&scred, l, cred); 296 error = smbfs_smb_lookup(NULL, NULL, 0, &fattr, &scred); 297 if (error) 298 return error; 299 error = smbfs_nget(mp, NULL, "TheRooT", 7, &fattr, &vp); 300 if (error) 301 return error; 302 303 /* 304 * Someone might have already set sm_root while we slept 305 * in smb_lookup or vnode allocation. 306 */ 307 if (smp->sm_root) 308 vput(vp); 309 else { 310 vp->v_vflag |= VV_ROOT; 311 smp->sm_root = VTOSMB(vp); 312 313 /* Keep reference, but unlock */ 314 VOP_UNLOCK(vp); 315 } 316 317 return (0); 318} 319 320/* 321 * Return locked root vnode of a filesystem. 322 */ 323int 324smbfs_root(struct mount *mp, struct vnode **vpp) 325{ 326 struct smbmount *smp = VFSTOSMBFS(mp); 327 int error; 328 329 if (__predict_false(!smp->sm_root)) { 330 error = smbfs_setroot(mp); 331 if (error) 332 return (error); 333 /* fallthrough */ 334 } 335 336 KASSERT(smp->sm_root != NULL && SMBTOV(smp->sm_root) != NULL); 337 *vpp = SMBTOV(smp->sm_root); 338 vref(*vpp); 339 error = vn_lock(*vpp, LK_EXCLUSIVE | LK_RETRY); 340 if (error) 341 vrele(*vpp); 342 return error; 343} 344 345/* 346 * Make a filesystem operational. 347 * Nothing to do at the moment. 348 */ 349/* ARGSUSED */ 350int 351smbfs_start(struct mount *mp, int flags) 352{ 353 SMBVDEBUG("flags=%04x\n", flags); 354 return 0; 355} 356 357void 358smbfs_init(void) 359{ 360 361 malloc_type_attach(M_SMBNODENAME); 362 malloc_type_attach(M_SMBFSDATA); 363 pool_init(&smbfs_node_pool, sizeof(struct smbnode), 0, 0, 0, 364 "smbfsnopl", &pool_allocator_nointr, IPL_NONE); 365 366 SMBVDEBUG0("init.\n"); 367} 368 369void 370smbfs_reinit(void) 371{ 372 373 SMBVDEBUG0("reinit.\n"); 374} 375 376void 377smbfs_done(void) 378{ 379 380 pool_destroy(&smbfs_node_pool); 381 malloc_type_detach(M_SMBNODENAME); 382 malloc_type_detach(M_SMBFSDATA); 383 384 SMBVDEBUG0("done.\n"); 385} 386 387/* 388 * smbfs_statvfs call 389 */ 390int 391smbfs_statvfs(struct mount *mp, struct statvfs *sbp) 392{ 393 struct lwp *l = curlwp; 394 struct smbmount *smp = VFSTOSMBFS(mp); 395 struct smb_share *ssp = smp->sm_share; 396 struct smb_cred scred; 397 int error = 0; 398 399 sbp->f_iosize = SSTOVC(ssp)->vc_txmax; /* optimal transfer block size */ 400 smb_makescred(&scred, l, l->l_cred); 401 402 error = smbfs_smb_statvfs(ssp, sbp, &scred); 403 if (error) 404 return error; 405 406 sbp->f_flag = 0; /* copy of mount exported flags */ 407 sbp->f_owner = mp->mnt_stat.f_owner; /* user that mounted the filesystem */ 408 copy_statvfs_info(sbp, mp); 409 return 0; 410} 411 412/* 413 * Flush out the buffer cache 414 */ 415int 416smbfs_sync(struct mount *mp, int waitfor, kauth_cred_t cred) 417{ 418 struct vnode *vp, *mvp; 419 struct smbnode *np; 420 int error, allerror = 0; 421 422 /* Allocate a marker vnode. */ 423 mvp = vnalloc(mp); 424 /* 425 * Force stale buffer cache information to be flushed. 426 */ 427 mutex_enter(&mntvnode_lock); 428loop: 429 for (vp = TAILQ_FIRST(&mp->mnt_vnodelist); vp; vp = vunmark(mvp)) { 430 vmark(mvp, vp); 431 /* 432 * If the vnode that we are about to sync is no longer 433 * associated with this mount point, start over. 434 */ 435 if (vp->v_mount != mp || vismarker(vp)) 436 continue; 437 mutex_enter(vp->v_interlock); 438 np = VTOSMB(vp); 439 if (np == NULL) { 440 mutex_exit(vp->v_interlock); 441 continue; 442 } 443 if ((vp->v_type == VNON || (np->n_flag & NMODIFIED) == 0) && 444 LIST_EMPTY(&vp->v_dirtyblkhd) && 445 vp->v_uobj.uo_npages == 0) { 446 mutex_exit(vp->v_interlock); 447 continue; 448 } 449 mutex_exit(&mntvnode_lock); 450 error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT); 451 if (error) { 452 mutex_enter(&mntvnode_lock); 453 if (error == ENOENT) { 454 (void)vunmark(mvp); 455 goto loop; 456 } 457 continue; 458 } 459 error = VOP_FSYNC(vp, cred, 460 waitfor == MNT_WAIT ? FSYNC_WAIT : 0, 0, 0); 461 if (error) 462 allerror = error; 463 vput(vp); 464 mutex_enter(&mntvnode_lock); 465 } 466 mutex_exit(&mntvnode_lock); 467 vnfree(mvp); 468 return (allerror); 469} 470 471#if __FreeBSD_version < 400009 472/* 473 * smbfs flat namespace lookup. Unsupported. 474 */ 475/* ARGSUSED */ 476int smbfs_vget(struct mount *mp, ino_t ino, 477 struct vnode **vpp) 478{ 479 return (EOPNOTSUPP); 480} 481 482#endif /* __FreeBSD_version < 400009 */ 483