1 /* 2 * Copyright (c) 1989 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Rick Macklem at The University of Guelph. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * from: @(#)nfs_vfsops.c 7.31 (Berkeley) 5/6/91 37 * $Id: nfs_vfsops.c,v 1.4 1993/07/13 10:04:29 cgd Exp $ 38 */ 39 40 #include "param.h" 41 #include "conf.h" 42 #include "ioctl.h" 43 #include "signal.h" 44 #include "proc.h" 45 #include "namei.h" 46 #include "vnode.h" 47 #include "mount.h" 48 #include "buf.h" 49 #include "mbuf.h" 50 #include "socket.h" 51 #include "systm.h" 52 53 #include "../net/if.h" 54 #include "../net/route.h" 55 #include "../netinet/in.h" 56 57 #include "nfsv2.h" 58 #include "nfsnode.h" 59 #include "nfsmount.h" 60 #include "nfs.h" 61 #include "xdr_subs.h" 62 #include "nfsm_subs.h" 63 #include "nfsdiskless.h" 64 65 /* 66 * nfs vfs operations. 67 */ 68 struct vfsops nfs_vfsops = { 69 nfs_mount, 70 nfs_start, 71 nfs_unmount, 72 nfs_root, 73 nfs_quotactl, 74 nfs_statfs, 75 nfs_sync, 76 nfs_fhtovp, 77 nfs_vptofh, 78 nfs_init, 79 }; 80 81 static u_char nfs_mntid; 82 extern u_long nfs_procids[NFS_NPROCS]; 83 extern u_long nfs_prog, nfs_vers; 84 struct nfs_diskless nfs_diskless; 85 void nfs_disconnect(); 86 87 #define TRUE 1 88 #define FALSE 0 89 90 /* 91 * nfs statfs call 92 */ 93 nfs_statfs(mp, sbp, p) 94 struct mount *mp; 95 register struct statfs *sbp; 96 struct proc *p; 97 { 98 register struct vnode *vp; 99 register struct nfsv2_statfs *sfp; 100 register caddr_t cp; 101 register long t1; 102 caddr_t bpos, dpos, cp2; 103 u_long xid; 104 int error = 0; 105 struct mbuf *mreq, *mrep, *md, *mb, *mb2; 106 struct nfsmount *nmp; 107 struct ucred *cred; 108 struct nfsnode *np; 109 110 nmp = VFSTONFS(mp); 111 if (error = nfs_nget(mp, &nmp->nm_fh, &np)) 112 return (error); 113 vp = NFSTOV(np); 114 nfsstats.rpccnt[NFSPROC_STATFS]++; 115 cred = crget(); 116 cred->cr_ngroups = 1; 117 nfsm_reqhead(nfs_procids[NFSPROC_STATFS], cred, NFSX_FH); 118 nfsm_fhtom(vp); 119 nfsm_request(vp, NFSPROC_STATFS, p, 0); 120 nfsm_disect(sfp, struct nfsv2_statfs *, NFSX_STATFS); 121 sbp->f_type = MOUNT_NFS; 122 sbp->f_flags = nmp->nm_flag; 123 sbp->f_bsize = fxdr_unsigned(long, sfp->sf_tsize); 124 sbp->f_fsize = fxdr_unsigned(long, sfp->sf_bsize); 125 sbp->f_blocks = fxdr_unsigned(long, sfp->sf_blocks); 126 sbp->f_bfree = fxdr_unsigned(long, sfp->sf_bfree); 127 sbp->f_bavail = fxdr_unsigned(long, sfp->sf_bavail); 128 sbp->f_files = 0; 129 sbp->f_ffree = 0; 130 if (sbp != &mp->mnt_stat) { 131 bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); 132 bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); 133 } 134 nfsm_reqdone; 135 nfs_nput(vp); 136 crfree(cred); 137 return (error); 138 } 139 140 /* 141 * Mount a remote root fs via. nfs. This depends on the info in the 142 * nfs_diskless structure that has been filled in properly by some primary 143 * bootstrap. 144 * It goes something like this: 145 * - do enough of "ifconfig" by calling ifioctl() so that the system 146 * can talk to the server 147 * - If nfs_diskless.mygateway is filled in, use that address as 148 * a default gateway. 149 * (This is done the 4.3 way with rtioctl() and should be changed) 150 * - hand craft the swap nfs vnode hanging off a fake mount point 151 * - build the rootfs mount point and call mountnfs() to do the rest. 152 */ 153 nfs_mountroot() 154 { 155 register struct mount *mp; 156 register struct mbuf *m; 157 struct socket *so; 158 struct vnode *vp; 159 int error; 160 161 /* 162 * Do enough of ifconfig(8) so that critical net interface can 163 * talk to the server. 164 */ 165 if (socreate(nfs_diskless.myif.ifra_addr.sa_family, &so, SOCK_DGRAM, 0)) 166 panic("nfs ifconf"); 167 if (ifioctl(so, SIOCAIFADDR, &nfs_diskless.myif)) 168 panic("nfs ifconf2"); 169 soclose(so); 170 171 /* 172 * If the gateway field is filled in, set it as the default route. 173 */ 174 #ifdef COMPAT_43 175 if (nfs_diskless.mygateway.sa_family == AF_INET) { 176 struct ortentry rt; 177 struct sockaddr_in *sin; 178 179 sin = (struct sockaddr_in *) &rt.rt_dst; 180 sin->sin_len = sizeof (struct sockaddr_in); 181 sin->sin_family = AF_INET; 182 sin->sin_addr.s_addr = 0; /* default */ 183 bcopy((caddr_t)&nfs_diskless.mygateway, (caddr_t)&rt.rt_gateway, 184 sizeof (struct sockaddr_in)); 185 rt.rt_flags = (RTF_UP | RTF_GATEWAY); 186 if (rtioctl(SIOCADDRT, (caddr_t)&rt, curproc)) 187 panic("nfs root route"); 188 } 189 #endif /* COMPAT_43 */ 190 191 /* 192 * If swapping to an nfs node (indicated by swdevt[0].sw_dev == NODEV): 193 * Create a fake mount point just for the swap vnode so that the 194 * swap file can be on a different server from the rootfs. 195 */ 196 if (swdevt[0].sw_dev == NODEV) { 197 mp = (struct mount *)malloc((u_long)sizeof(struct mount), 198 M_MOUNT, M_NOWAIT); 199 if (mp == NULL) 200 panic("nfs root mount"); 201 mp->mnt_op = &nfs_vfsops; 202 mp->mnt_flag = 0; 203 mp->mnt_exroot = 0; 204 mp->mnt_mounth = NULLVP; 205 206 /* 207 * Set up the diskless nfs_args for the swap mount point 208 * and then call mountnfs() to mount it. 209 * Since the swap file is not the root dir of a file system, 210 * hack it to a regular file. 211 */ 212 nfs_diskless.swap_args.fh = (nfsv2fh_t *)nfs_diskless.swap_fh; 213 MGET(m, MT_SONAME, M_DONTWAIT); 214 if (m == NULL) 215 panic("nfs root mbuf"); 216 bcopy((caddr_t)&nfs_diskless.swap_saddr, mtod(m, caddr_t), 217 nfs_diskless.swap_saddr.sa_len); 218 m->m_len = nfs_diskless.swap_saddr.sa_len; 219 if (mountnfs(&nfs_diskless.swap_args, mp, m, "/swap", 220 nfs_diskless.swap_hostnam, &vp)) 221 panic("nfs swap"); 222 vp->v_type = VREG; 223 vp->v_flag = 0; 224 swapdev_vp = vp; 225 VREF(vp); 226 swdevt[0].sw_vp = vp; 227 { 228 struct vattr attr; 229 230 if (nfs_dogetattr(vp,&attr,NOCRED,0,0)) { 231 panic("nfs swap"); 232 } 233 swdevt[0].sw_nblks = attr.va_size / DEV_BSIZE; 234 } 235 } 236 237 /* 238 * Create the rootfs mount point. 239 */ 240 mp = (struct mount *)malloc((u_long)sizeof(struct mount), 241 M_MOUNT, M_NOWAIT); 242 if (mp == NULL) 243 panic("nfs root mount2"); 244 mp->mnt_op = &nfs_vfsops; 245 mp->mnt_flag = MNT_RDONLY; 246 mp->mnt_exroot = 0; 247 mp->mnt_mounth = NULLVP; 248 249 /* 250 * Set up the root fs args and call mountnfs() to do the rest. 251 */ 252 nfs_diskless.root_args.fh = (nfsv2fh_t *)nfs_diskless.root_fh; 253 MGET(m, MT_SONAME, M_DONTWAIT); 254 if (m == NULL) 255 panic("nfs root mbuf2"); 256 bcopy((caddr_t)&nfs_diskless.root_saddr, mtod(m, caddr_t), 257 nfs_diskless.root_saddr.sa_len); 258 m->m_len = nfs_diskless.root_saddr.sa_len; 259 if (mountnfs(&nfs_diskless.root_args, mp, m, "/", 260 nfs_diskless.root_hostnam, &vp)) 261 panic("nfs root"); 262 if (vfs_lock(mp)) 263 panic("nfs root2"); 264 rootfs = mp; 265 mp->mnt_next = mp; 266 mp->mnt_prev = mp; 267 mp->mnt_vnodecovered = NULLVP; 268 vfs_unlock(mp); 269 rootvp = vp; 270 inittodr((time_t)0); /* There is no time in the nfs fsstat so ?? */ 271 return (0); 272 } 273 274 /* 275 * VFS Operations. 276 * 277 * mount system call 278 * It seems a bit dumb to copyinstr() the host and path here and then 279 * bcopy() them in mountnfs(), but I wanted to detect errors before 280 * doing the sockargs() call because sockargs() allocates an mbuf and 281 * an error after that means that I have to release the mbuf. 282 */ 283 /* ARGSUSED */ 284 nfs_mount(mp, path, data, ndp, p) 285 struct mount *mp; 286 char *path; 287 caddr_t data; 288 struct nameidata *ndp; 289 struct proc *p; 290 { 291 int error; 292 struct nfs_args args; 293 struct mbuf *nam; 294 struct vnode *vp; 295 char pth[MNAMELEN], hst[MNAMELEN]; 296 u_int len; 297 nfsv2fh_t nfh; 298 299 if (mp->mnt_flag & MNT_UPDATE) 300 return (0); 301 if (error = copyin(data, (caddr_t)&args, sizeof (struct nfs_args))) 302 return (error); 303 if (error = copyin((caddr_t)args.fh, (caddr_t)&nfh, sizeof (nfsv2fh_t))) 304 return (error); 305 if (error = copyinstr(path, pth, MNAMELEN-1, &len)) 306 return (error); 307 bzero(&pth[len], MNAMELEN - len); 308 if (error = copyinstr(args.hostname, hst, MNAMELEN-1, &len)) 309 return (error); 310 bzero(&hst[len], MNAMELEN - len); 311 /* sockargs() call must be after above copyin() calls */ 312 if (error = sockargs(&nam, (caddr_t)args.addr, 313 sizeof (struct sockaddr), MT_SONAME)) 314 return (error); 315 args.fh = &nfh; 316 error = mountnfs(&args, mp, nam, pth, hst, &vp); 317 return (error); 318 } 319 320 /* 321 * Common code for mount and mountroot 322 */ 323 mountnfs(argp, mp, nam, pth, hst, vpp) 324 register struct nfs_args *argp; 325 register struct mount *mp; 326 struct mbuf *nam; 327 char *pth, *hst; 328 struct vnode **vpp; 329 { 330 register struct nfsmount *nmp; 331 struct proc *p = curproc; /* XXX */ 332 struct nfsnode *np; 333 int error; 334 fsid_t tfsid; 335 336 MALLOC(nmp, struct nfsmount *, sizeof *nmp, M_NFSMNT, M_WAITOK); 337 bzero((caddr_t)nmp, sizeof *nmp); 338 mp->mnt_data = (qaddr_t)nmp; 339 /* 340 * Generate a unique nfs mount id. The problem is that a dev number 341 * is not unique across multiple systems. The techique is as follows: 342 * 1) Set to nblkdev,0 which will never be used otherwise 343 * 2) Generate a first guess as nblkdev,nfs_mntid where nfs_mntid is 344 * NOT 0 345 * 3) Loop searching the mount list for another one with same id 346 * If a match, increment val[0] and try again 347 * NB: I increment val[0] { a long } instead of nfs_mntid { a u_char } 348 * so that nfs is not limited to 255 mount points 349 * Incrementing the high order bits does no real harm, since it 350 * simply makes the major dev number tick up. The upper bound is 351 * set to major dev 127 to avoid any sign extention problems 352 */ 353 mp->mnt_stat.f_fsid.val[0] = makedev(nblkdev, 0); 354 mp->mnt_stat.f_fsid.val[1] = MOUNT_NFS; 355 if (++nfs_mntid == 0) 356 ++nfs_mntid; 357 tfsid.val[0] = makedev(nblkdev, nfs_mntid); 358 tfsid.val[1] = MOUNT_NFS; 359 while (rootfs && getvfs(&tfsid)) { 360 tfsid.val[0]++; 361 nfs_mntid++; 362 } 363 if (major(tfsid.val[0]) > 127) { 364 error = ENOENT; 365 goto bad; 366 } 367 mp->mnt_stat.f_fsid.val[0] = tfsid.val[0]; 368 nmp->nm_mountp = mp; 369 nmp->nm_flag = argp->flags; 370 nmp->nm_rto = NFS_TIMEO; 371 nmp->nm_rtt = -1; 372 nmp->nm_rttvar = nmp->nm_rto << 1; 373 nmp->nm_retry = NFS_RETRANS; 374 nmp->nm_wsize = NFS_WSIZE; 375 nmp->nm_rsize = NFS_RSIZE; 376 bcopy((caddr_t)argp->fh, (caddr_t)&nmp->nm_fh, sizeof(nfsv2fh_t)); 377 bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN); 378 bcopy(pth, mp->mnt_stat.f_mntonname, MNAMELEN); 379 nmp->nm_nam = nam; 380 381 if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) { 382 nmp->nm_rto = argp->timeo; 383 /* NFS timeouts are specified in 1/10 sec. */ 384 nmp->nm_rto = (nmp->nm_rto * 10) / NFS_HZ; 385 if (nmp->nm_rto < NFS_MINTIMEO) 386 nmp->nm_rto = NFS_MINTIMEO; 387 else if (nmp->nm_rto > NFS_MAXTIMEO) 388 nmp->nm_rto = NFS_MAXTIMEO; 389 nmp->nm_rttvar = nmp->nm_rto << 1; 390 } 391 392 if ((argp->flags & NFSMNT_RETRANS) && argp->retrans > 1) { 393 nmp->nm_retry = argp->retrans; 394 if (nmp->nm_retry > NFS_MAXREXMIT) 395 nmp->nm_retry = NFS_MAXREXMIT; 396 } 397 398 if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) { 399 nmp->nm_wsize = argp->wsize; 400 /* Round down to multiple of blocksize */ 401 nmp->nm_wsize &= ~0x1ff; 402 if (nmp->nm_wsize <= 0) 403 nmp->nm_wsize = 512; 404 else if (nmp->nm_wsize > NFS_MAXDATA) 405 nmp->nm_wsize = NFS_MAXDATA; 406 } 407 if (nmp->nm_wsize > MAXBSIZE) 408 nmp->nm_wsize = MAXBSIZE; 409 410 if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) { 411 nmp->nm_rsize = argp->rsize; 412 /* Round down to multiple of blocksize */ 413 nmp->nm_rsize &= ~0x1ff; 414 if (nmp->nm_rsize <= 0) 415 nmp->nm_rsize = 512; 416 else if (nmp->nm_rsize > NFS_MAXDATA) 417 nmp->nm_rsize = NFS_MAXDATA; 418 } 419 if (nmp->nm_rsize > MAXBSIZE) 420 nmp->nm_rsize = MAXBSIZE; 421 /* Set up the sockets and per-host congestion */ 422 nmp->nm_sotype = argp->sotype; 423 nmp->nm_soproto = argp->proto; 424 if (error = nfs_connect(nmp)) 425 goto bad; 426 427 if (error = nfs_statfs(mp, &mp->mnt_stat, p)) 428 goto bad; 429 /* 430 * A reference count is needed on the nfsnode representing the 431 * remote root. If this object is not persistent, then backward 432 * traversals of the mount point (i.e. "..") will not work if 433 * the nfsnode gets flushed out of the cache. Ufs does not have 434 * this problem, because one can identify root inodes by their 435 * number == ROOTINO (2). 436 */ 437 if (error = nfs_nget(mp, &nmp->nm_fh, &np)) 438 goto bad; 439 /* 440 * Unlock it, but keep the reference count. 441 */ 442 nfs_unlock(NFSTOV(np)); 443 *vpp = NFSTOV(np); 444 445 return (0); 446 bad: 447 nfs_disconnect(nmp); 448 FREE(nmp, M_NFSMNT); 449 m_freem(nam); 450 return (error); 451 } 452 453 /* 454 * unmount system call 455 */ 456 nfs_unmount(mp, mntflags, p) 457 struct mount *mp; 458 int mntflags; 459 struct proc *p; 460 { 461 register struct nfsmount *nmp; 462 struct nfsnode *np; 463 struct vnode *vp; 464 int error, flags = 0; 465 extern int doforce; 466 467 if (mntflags & MNT_FORCE) { 468 if (!doforce || mp == rootfs) 469 return (EINVAL); 470 flags |= FORCECLOSE; 471 } 472 nmp = VFSTONFS(mp); 473 /* 474 * Clear out the buffer cache 475 */ 476 mntflushbuf(mp, 0); 477 if (mntinvalbuf(mp)) 478 return (EBUSY); 479 /* 480 * Goes something like this.. 481 * - Check for activity on the root vnode (other than ourselves). 482 * - Call vflush() to clear out vnodes for this file system, 483 * except for the root vnode. 484 * - Decrement reference on the vnode representing remote root. 485 * - Close the socket 486 * - Free up the data structures 487 */ 488 /* 489 * We need to decrement the ref. count on the nfsnode representing 490 * the remote root. See comment in mountnfs(). The VFS unmount() 491 * has done vput on this vnode, otherwise we would get deadlock! 492 */ 493 if (error = nfs_nget(mp, &nmp->nm_fh, &np)) 494 return(error); 495 vp = NFSTOV(np); 496 if (vp->v_usecount > 2) { 497 vput(vp); 498 return (EBUSY); 499 } 500 if (error = vflush(mp, vp, flags)) { 501 vput(vp); 502 return (error); 503 } 504 /* 505 * Get rid of two reference counts, and unlock it on the second. 506 */ 507 vrele(vp); 508 vput(vp); 509 nfs_disconnect(nmp); 510 m_freem(nmp->nm_nam); 511 free((caddr_t)nmp, M_NFSMNT); 512 return (0); 513 } 514 515 /* 516 * Return root of a filesystem 517 */ 518 nfs_root(mp, vpp) 519 struct mount *mp; 520 struct vnode **vpp; 521 { 522 register struct vnode *vp; 523 struct nfsmount *nmp; 524 struct nfsnode *np; 525 int error; 526 527 nmp = VFSTONFS(mp); 528 if (error = nfs_nget(mp, &nmp->nm_fh, &np)) 529 return (error); 530 vp = NFSTOV(np); 531 vp->v_type = VDIR; 532 vp->v_flag = VROOT; 533 *vpp = vp; 534 return (0); 535 } 536 537 extern int syncprt; 538 539 /* 540 * Flush out the buffer cache 541 */ 542 /* ARGSUSED */ 543 nfs_sync(mp, waitfor) 544 struct mount *mp; 545 int waitfor; 546 { 547 if (syncprt) 548 bufstats(); 549 /* 550 * Force stale buffer cache information to be flushed. 551 */ 552 mntflushbuf(mp, waitfor == MNT_WAIT ? B_SYNC : 0); 553 return (0); 554 } 555 556 /* 557 * At this point, this should never happen 558 */ 559 /* ARGSUSED */ 560 nfs_fhtovp(mp, fhp, vpp) 561 struct mount *mp; 562 struct fid *fhp; 563 struct vnode **vpp; 564 { 565 566 return (EINVAL); 567 } 568 569 /* 570 * Vnode pointer to File handle, should never happen either 571 */ 572 /* ARGSUSED */ 573 nfs_vptofh(vp, fhp) 574 struct vnode *vp; 575 struct fid *fhp; 576 { 577 578 return (EINVAL); 579 } 580 581 /* 582 * Vfs start routine, a no-op. 583 */ 584 /* ARGSUSED */ 585 nfs_start(mp, flags, p) 586 struct mount *mp; 587 int flags; 588 struct proc *p; 589 { 590 591 return (0); 592 } 593 594 /* 595 * Do operations associated with quotas, not supported 596 */ 597 nfs_quotactl(mp, cmd, uid, arg, p) 598 struct mount *mp; 599 int cmd; 600 uid_t uid; 601 caddr_t arg; 602 struct proc *p; 603 { 604 #ifdef lint 605 mp = mp; cmd = cmd; uid = uid; arg = arg; 606 #endif /* lint */ 607 return (EOPNOTSUPP); 608 } 609