1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * $FreeBSD$ 29 */ 30 31 #include <sys/param.h> 32 #include <sys/systm.h> 33 #include <sys/proc.h> 34 #include <sys/bio.h> 35 #include <sys/buf.h> 36 #include <sys/kernel.h> 37 #include <sys/sysctl.h> 38 #include <sys/vnode.h> 39 #include <sys/mount.h> 40 #include <sys/stat.h> 41 #include <sys/malloc.h> 42 #include <sys/module.h> 43 #include <sys/sx.h> 44 45 #include <netsmb/smb.h> 46 #include <netsmb/smb_conn.h> 47 #include <netsmb/smb_subr.h> 48 #include <netsmb/smb_dev.h> 49 50 #include <fs/smbfs/smbfs.h> 51 #include <fs/smbfs/smbfs_node.h> 52 #include <fs/smbfs/smbfs_subr.h> 53 54 static int smbfs_debuglevel = 0; 55 56 static int smbfs_version = SMBFS_VERSION; 57 58 SYSCTL_NODE(_vfs, OID_AUTO, smbfs, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 59 "SMB/CIFS filesystem"); 60 SYSCTL_INT(_vfs_smbfs, OID_AUTO, version, CTLFLAG_RD, &smbfs_version, 0, ""); 61 SYSCTL_INT(_vfs_smbfs, OID_AUTO, debuglevel, CTLFLAG_RW, &smbfs_debuglevel, 0, ""); 62 63 static vfs_init_t smbfs_init; 64 static vfs_uninit_t smbfs_uninit; 65 static vfs_cmount_t smbfs_cmount; 66 static vfs_mount_t smbfs_mount; 67 static vfs_root_t smbfs_root; 68 static vfs_quotactl_t smbfs_quotactl; 69 static vfs_statfs_t smbfs_statfs; 70 static vfs_unmount_t smbfs_unmount; 71 72 static struct vfsops smbfs_vfsops = { 73 .vfs_init = smbfs_init, 74 .vfs_cmount = smbfs_cmount, 75 .vfs_mount = smbfs_mount, 76 .vfs_quotactl = smbfs_quotactl, 77 .vfs_root = smbfs_root, 78 .vfs_statfs = smbfs_statfs, 79 .vfs_sync = vfs_stdsync, 80 .vfs_uninit = smbfs_uninit, 81 .vfs_unmount = smbfs_unmount, 82 }; 83 84 VFS_SET(smbfs_vfsops, smbfs, VFCF_NETWORK); 85 86 MODULE_DEPEND(smbfs, netsmb, NSMB_VERSION, NSMB_VERSION, NSMB_VERSION); 87 MODULE_DEPEND(smbfs, libiconv, 1, 1, 2); 88 MODULE_DEPEND(smbfs, libmchain, 1, 1, 1); 89 90 uma_zone_t smbfs_pbuf_zone; 91 92 static int 93 smbfs_cmount(struct mntarg *ma, void * data, uint64_t flags) 94 { 95 struct smbfs_args args; 96 int error; 97 98 error = copyin(data, &args, sizeof(struct smbfs_args)); 99 if (error) 100 return error; 101 102 if (args.version != SMBFS_VERSION) { 103 printf("mount version mismatch: kernel=%d, mount=%d\n", 104 SMBFS_VERSION, args.version); 105 return EINVAL; 106 } 107 ma = mount_argf(ma, "dev", "%d", args.dev); 108 ma = mount_argb(ma, args.flags & SMBFS_MOUNT_SOFT, "nosoft"); 109 ma = mount_argb(ma, args.flags & SMBFS_MOUNT_INTR, "nointr"); 110 ma = mount_argb(ma, args.flags & SMBFS_MOUNT_STRONG, "nostrong"); 111 ma = mount_argb(ma, args.flags & SMBFS_MOUNT_HAVE_NLS, "nohave_nls"); 112 ma = mount_argb(ma, !(args.flags & SMBFS_MOUNT_NO_LONG), "nolong"); 113 ma = mount_arg(ma, "rootpath", args.root_path, -1); 114 ma = mount_argf(ma, "uid", "%d", args.uid); 115 ma = mount_argf(ma, "gid", "%d", args.gid); 116 ma = mount_argf(ma, "file_mode", "%d", args.file_mode); 117 ma = mount_argf(ma, "dir_mode", "%d", args.dir_mode); 118 ma = mount_argf(ma, "caseopt", "%d", args.caseopt); 119 120 error = kernel_mount(ma, flags); 121 122 return (error); 123 } 124 125 static const char *smbfs_opts[] = { 126 "fd", "soft", "intr", "strong", "have_nls", "long", 127 "mountpoint", "rootpath", "uid", "gid", "file_mode", "dir_mode", 128 "caseopt", "errmsg", NULL 129 }; 130 131 static int 132 smbfs_mount(struct mount *mp) 133 { 134 struct smbmount *smp = NULL; 135 struct smb_vc *vcp; 136 struct smb_share *ssp = NULL; 137 struct vnode *vp; 138 struct thread *td; 139 struct smb_dev *dev; 140 struct smb_cred *scred; 141 int error, v; 142 char *pc, *pe; 143 144 dev = NULL; 145 td = curthread; 146 if (mp->mnt_flag & (MNT_UPDATE | MNT_ROOTFS)) 147 return EOPNOTSUPP; 148 149 if (vfs_filteropt(mp->mnt_optnew, smbfs_opts)) { 150 vfs_mount_error(mp, "%s", "Invalid option"); 151 return (EINVAL); 152 } 153 154 scred = smbfs_malloc_scred(); 155 smb_makescred(scred, td, td->td_ucred); 156 157 /* Ask userspace of `fd`, the file descriptor of this session */ 158 if (1 != vfs_scanopt(mp->mnt_optnew, "fd", "%d", &v)) { 159 vfs_mount_error(mp, "No fd option"); 160 smbfs_free_scred(scred); 161 return (EINVAL); 162 } 163 error = smb_dev2share(v, SMBM_EXEC, scred, &ssp, &dev); 164 smp = malloc(sizeof(*smp), M_SMBFSDATA, M_WAITOK | M_ZERO); 165 if (error) { 166 printf("invalid device handle %d (%d)\n", v, error); 167 vfs_mount_error(mp, "invalid device handle %d %d\n", v, error); 168 smbfs_free_scred(scred); 169 free(smp, M_SMBFSDATA); 170 return error; 171 } 172 vcp = SSTOVC(ssp); 173 smb_share_unlock(ssp); 174 mp->mnt_stat.f_iosize = SSTOVC(ssp)->vc_txmax; 175 mp->mnt_data = smp; 176 smp->sm_share = ssp; 177 smp->sm_root = NULL; 178 smp->sm_dev = dev; 179 if (1 != vfs_scanopt(mp->mnt_optnew, 180 "caseopt", "%d", &smp->sm_caseopt)) { 181 vfs_mount_error(mp, "Invalid caseopt"); 182 error = EINVAL; 183 goto bad; 184 } 185 if (1 != vfs_scanopt(mp->mnt_optnew, "uid", "%d", &v)) { 186 vfs_mount_error(mp, "Invalid uid"); 187 error = EINVAL; 188 goto bad; 189 } 190 smp->sm_uid = v; 191 192 if (1 != vfs_scanopt(mp->mnt_optnew, "gid", "%d", &v)) { 193 vfs_mount_error(mp, "Invalid gid"); 194 error = EINVAL; 195 goto bad; 196 } 197 smp->sm_gid = v; 198 199 if (1 != vfs_scanopt(mp->mnt_optnew, "file_mode", "%d", &v)) { 200 vfs_mount_error(mp, "Invalid file_mode"); 201 error = EINVAL; 202 goto bad; 203 } 204 smp->sm_file_mode = (v & (S_IRWXU|S_IRWXG|S_IRWXO)) | S_IFREG; 205 206 if (1 != vfs_scanopt(mp->mnt_optnew, "dir_mode", "%d", &v)) { 207 vfs_mount_error(mp, "Invalid dir_mode"); 208 error = EINVAL; 209 goto bad; 210 } 211 smp->sm_dir_mode = (v & (S_IRWXU|S_IRWXG|S_IRWXO)) | S_IFDIR; 212 213 vfs_flagopt(mp->mnt_optnew, 214 "nolong", &smp->sm_flags, SMBFS_MOUNT_NO_LONG); 215 216 pc = mp->mnt_stat.f_mntfromname; 217 pe = pc + sizeof(mp->mnt_stat.f_mntfromname); 218 bzero(pc, MNAMELEN); 219 *pc++ = '/'; 220 *pc++ = '/'; 221 pc = strchr(strncpy(pc, vcp->vc_username, pe - pc - 2), 0); 222 if (pc < pe-1) { 223 *(pc++) = '@'; 224 pc = strchr(strncpy(pc, vcp->vc_srvname, pe - pc - 2), 0); 225 if (pc < pe - 1) { 226 *(pc++) = '/'; 227 strncpy(pc, ssp->ss_name, pe - pc - 2); 228 } 229 } 230 vfs_getnewfsid(mp); 231 error = smbfs_root(mp, LK_EXCLUSIVE, &vp); 232 if (error) { 233 vfs_mount_error(mp, "smbfs_root error: %d", error); 234 goto bad; 235 } 236 VOP_UNLOCK(vp); 237 SMBVDEBUG("root.v_usecount = %d\n", vrefcnt(vp)); 238 239 #ifdef DIAGNOSTIC 240 SMBERROR("mp=%p\n", mp); 241 #endif 242 smbfs_free_scred(scred); 243 return error; 244 bad: 245 if (ssp) 246 smb_share_put(ssp, scred); 247 smbfs_free_scred(scred); 248 SMB_LOCK(); 249 if (error && smp->sm_dev == dev) { 250 smp->sm_dev = NULL; 251 sdp_trydestroy(dev); 252 } 253 SMB_UNLOCK(); 254 free(smp, M_SMBFSDATA); 255 return error; 256 } 257 258 /* Unmount the filesystem described by mp. */ 259 static int 260 smbfs_unmount(struct mount *mp, int mntflags) 261 { 262 struct thread *td; 263 struct smbmount *smp = VFSTOSMBFS(mp); 264 struct smb_cred *scred; 265 struct smb_dev *dev; 266 int error, flags; 267 268 SMBVDEBUG("smbfs_unmount: flags=%04x\n", mntflags); 269 td = curthread; 270 flags = 0; 271 if (mntflags & MNT_FORCE) 272 flags |= FORCECLOSE; 273 /* 274 * Keep trying to flush the vnode list for the mount while 275 * some are still busy and we are making progress towards 276 * making them not busy. This is needed because smbfs vnodes 277 * reference their parent directory but may appear after their 278 * parent in the list; one pass over the vnode list is not 279 * sufficient in this case. 280 */ 281 do { 282 smp->sm_didrele = 0; 283 /* There is 1 extra root vnode reference from smbfs_mount(). */ 284 error = vflush(mp, 1, flags, td); 285 } while (error == EBUSY && smp->sm_didrele != 0); 286 if (error) 287 return error; 288 scred = smbfs_malloc_scred(); 289 smb_makescred(scred, td, td->td_ucred); 290 error = smb_share_lock(smp->sm_share); 291 if (error) 292 goto out; 293 smb_share_put(smp->sm_share, scred); 294 SMB_LOCK(); 295 dev = smp->sm_dev; 296 if (!dev) 297 panic("No private data for mount point"); 298 sdp_trydestroy(dev); 299 mp->mnt_data = NULL; 300 SMB_UNLOCK(); 301 free(smp, M_SMBFSDATA); 302 out: 303 smbfs_free_scred(scred); 304 return error; 305 } 306 307 /* 308 * Return locked root vnode of a filesystem 309 */ 310 static int 311 smbfs_root(struct mount *mp, int flags, struct vnode **vpp) 312 { 313 struct smbmount *smp = VFSTOSMBFS(mp); 314 struct vnode *vp; 315 struct smbnode *np; 316 struct smbfattr fattr; 317 struct thread *td; 318 struct ucred *cred; 319 struct smb_cred *scred; 320 int error; 321 322 td = curthread; 323 cred = td->td_ucred; 324 325 if (smp->sm_root) { 326 *vpp = SMBTOV(smp->sm_root); 327 return vget(*vpp, LK_EXCLUSIVE | LK_RETRY); 328 } 329 scred = smbfs_malloc_scred(); 330 smb_makescred(scred, td, cred); 331 error = smbfs_smb_lookup(NULL, NULL, 0, &fattr, scred); 332 if (error) 333 goto out; 334 error = smbfs_nget(mp, NULL, NULL, 0, &fattr, &vp); 335 if (error) 336 goto out; 337 ASSERT_VOP_LOCKED(vp, "smbfs_root"); 338 vp->v_vflag |= VV_ROOT; 339 np = VTOSMB(vp); 340 smp->sm_root = np; 341 *vpp = vp; 342 out: 343 smbfs_free_scred(scred); 344 return error; 345 } 346 347 /* 348 * Do operations associated with quotas, not supported 349 */ 350 /* ARGSUSED */ 351 static int 352 smbfs_quotactl(struct mount *mp, int cmd, uid_t uid, void *arg, bool *mp_busy) 353 { 354 SMBVDEBUG("return EOPNOTSUPP\n"); 355 return EOPNOTSUPP; 356 } 357 358 /*ARGSUSED*/ 359 int 360 smbfs_init(struct vfsconf *vfsp) 361 { 362 363 smbfs_pbuf_zone = pbuf_zsecond_create("smbpbuf", nswbuf / 2); 364 SMBVDEBUG("done.\n"); 365 return 0; 366 } 367 368 /*ARGSUSED*/ 369 int 370 smbfs_uninit(struct vfsconf *vfsp) 371 { 372 373 uma_zdestroy(smbfs_pbuf_zone); 374 SMBVDEBUG("done.\n"); 375 return 0; 376 } 377 378 /* 379 * smbfs_statfs call 380 */ 381 int 382 smbfs_statfs(struct mount *mp, struct statfs *sbp) 383 { 384 struct thread *td = curthread; 385 struct smbmount *smp = VFSTOSMBFS(mp); 386 struct smbnode *np = smp->sm_root; 387 struct smb_share *ssp = smp->sm_share; 388 struct smb_cred *scred; 389 int error; 390 391 if (np == NULL) { 392 vfs_mount_error(mp, "np == NULL"); 393 return EINVAL; 394 } 395 396 sbp->f_iosize = SSTOVC(ssp)->vc_txmax; /* optimal transfer block size */ 397 scred = smbfs_malloc_scred(); 398 smb_makescred(scred, td, td->td_ucred); 399 error = smbfs_smb_statfs(ssp, sbp, scred); 400 smbfs_free_scred(scred); 401 return (error); 402 } 403