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 * %sccs.include.redist.c% 9 * 10 * @(#)nfs_vfsops.c 7.36 (Berkeley) 01/14/92 11 */ 12 13 #include "param.h" 14 #include "conf.h" 15 #include "ioctl.h" 16 #include "signal.h" 17 #include "proc.h" 18 #include "namei.h" 19 #include "vnode.h" 20 #include "kernel.h" 21 #include "mount.h" 22 #include "buf.h" 23 #include "mbuf.h" 24 #include "socket.h" 25 #include "systm.h" 26 27 #include "net/if.h" 28 #include "net/route.h" 29 #include "netinet/in.h" 30 31 #include "rpcv2.h" 32 #include "nfsv2.h" 33 #include "nfsnode.h" 34 #include "nfsmount.h" 35 #include "nfs.h" 36 #include "xdr_subs.h" 37 #include "nfsm_subs.h" 38 #include "nfsdiskless.h" 39 #include "nqnfs.h" 40 41 /* 42 * nfs vfs operations. 43 */ 44 struct vfsops nfs_vfsops = { 45 nfs_mount, 46 nfs_start, 47 nfs_unmount, 48 nfs_root, 49 nfs_quotactl, 50 nfs_statfs, 51 nfs_sync, 52 nfs_fhtovp, 53 nfs_vptofh, 54 nfs_init, 55 }; 56 57 /* 58 * This structure must be filled in by a primary bootstrap or bootstrap 59 * server for a diskless/dataless machine. It is initialized below just 60 * to ensure that it is allocated to initialized data (.data not .bss). 61 */ 62 struct nfs_diskless nfs_diskless = { 0 }; 63 64 static u_char nfs_mntid; 65 extern u_long nfs_procids[NFS_NPROCS]; 66 extern u_long nfs_prog, nfs_vers; 67 void nfs_disconnect(), nfsargs_ntoh(); 68 69 #define TRUE 1 70 #define FALSE 0 71 72 /* 73 * nfs statfs call 74 */ 75 nfs_statfs(mp, sbp, p) 76 struct mount *mp; 77 register struct statfs *sbp; 78 struct proc *p; 79 { 80 register struct vnode *vp; 81 register struct nfsv2_statfs *sfp; 82 register caddr_t cp; 83 register long t1; 84 caddr_t bpos, dpos, cp2; 85 u_long xid; 86 int error = 0; 87 struct mbuf *mreq, *mrep, *md, *mb, *mb2; 88 struct nfsmount *nmp; 89 struct ucred *cred; 90 struct nfsnode *np; 91 92 nmp = VFSTONFS(mp); 93 if (error = nfs_nget(mp, &nmp->nm_fh, &np)) 94 return (error); 95 vp = NFSTOV(np); 96 nfsstats.rpccnt[NFSPROC_STATFS]++; 97 cred = crget(); 98 cred->cr_ngroups = 1; 99 nfsm_reqhead(vp, NFSPROC_STATFS, NFSX_FH); 100 nfsm_fhtom(vp); 101 nfsm_request(vp, NFSPROC_STATFS, p, cred); 102 nfsm_dissect(sfp, struct nfsv2_statfs *, NFSX_STATFS); 103 sbp->f_type = MOUNT_NFS; 104 sbp->f_flags = nmp->nm_flag; 105 sbp->f_iosize = NFS_MAXDGRAMDATA; 106 sbp->f_bsize = fxdr_unsigned(long, sfp->sf_bsize); 107 sbp->f_blocks = fxdr_unsigned(long, sfp->sf_blocks); 108 sbp->f_bfree = fxdr_unsigned(long, sfp->sf_bfree); 109 sbp->f_bavail = fxdr_unsigned(long, sfp->sf_bavail); 110 sbp->f_files = 0; 111 sbp->f_ffree = 0; 112 if (sbp != &mp->mnt_stat) { 113 bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); 114 bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); 115 } 116 nfsm_reqdone; 117 vrele(vp); 118 crfree(cred); 119 return (error); 120 } 121 122 /* 123 * Mount a remote root fs via. nfs. This depends on the info in the 124 * nfs_diskless structure that has been filled in properly by some primary 125 * bootstrap. 126 * It goes something like this: 127 * - do enough of "ifconfig" by calling ifioctl() so that the system 128 * can talk to the server 129 * - If nfs_diskless.mygateway is filled in, use that address as 130 * a default gateway. 131 * - hand craft the swap nfs vnode hanging off a fake mount point 132 * if swdevt[0].sw_dev == NODEV 133 * - build the rootfs mount point and call mountnfs() to do the rest. 134 */ 135 nfs_mountroot() 136 { 137 register struct mount *mp; 138 register struct mbuf *m; 139 struct socket *so; 140 struct vnode *vp; 141 int error; 142 143 /* 144 * Do enough of ifconfig(8) so that the critical net interface can 145 * talk to the server. 146 */ 147 if (socreate(nfs_diskless.myif.ifra_addr.sa_family, &so, SOCK_DGRAM, 0)) 148 panic("nfs ifconf"); 149 if (ifioctl(so, SIOCAIFADDR, &nfs_diskless.myif)) 150 panic("nfs ifconf2"); 151 soclose(so); 152 153 /* 154 * If the gateway field is filled in, set it as the default route. 155 */ 156 if (nfs_diskless.mygateway.sin_len != 0) { 157 struct sockaddr_in sin; 158 extern struct sockaddr_in icmpmask; 159 160 sin.sin_len = sizeof (struct sockaddr_in); 161 sin.sin_family = AF_INET; 162 sin.sin_addr.s_addr = 0; /* default */ 163 in_sockmaskof(sin.sin_addr, &icmpmask); 164 if (rtrequest(RTM_ADD, (struct sockaddr *)&sin, 165 (struct sockaddr *)&nfs_diskless.mygateway, 166 (struct sockaddr *)&icmpmask, 167 RTF_UP | RTF_GATEWAY, (struct rtentry **)0)) 168 panic("nfs root route"); 169 } 170 171 /* 172 * If swapping to an nfs node (indicated by swdevt[0].sw_dev == NODEV): 173 * Create a fake mount point just for the swap vnode so that the 174 * swap file can be on a different server from the rootfs. 175 */ 176 if (swdevt[0].sw_dev == NODEV) { 177 mp = (struct mount *)malloc((u_long)sizeof(struct mount), 178 M_MOUNT, M_NOWAIT); 179 if (mp == NULL) 180 panic("nfs root mount"); 181 mp->mnt_op = &nfs_vfsops; 182 mp->mnt_flag = 0; 183 mp->mnt_mounth = NULLVP; 184 185 /* 186 * Set up the diskless nfs_args for the swap mount point 187 * and then call mountnfs() to mount it. 188 * Since the swap file is not the root dir of a file system, 189 * hack it to a regular file. 190 */ 191 nfs_diskless.swap_args.fh = (nfsv2fh_t *)nfs_diskless.swap_fh; 192 MGET(m, MT_SONAME, M_DONTWAIT); 193 if (m == NULL) 194 panic("nfs root mbuf"); 195 bcopy((caddr_t)&nfs_diskless.swap_saddr, mtod(m, caddr_t), 196 nfs_diskless.swap_saddr.sin_len); 197 m->m_len = (int)nfs_diskless.swap_saddr.sin_len; 198 nfsargs_ntoh(&nfs_diskless.swap_args); 199 if (mountnfs(&nfs_diskless.swap_args, mp, m, "/swap", 200 nfs_diskless.swap_hostnam, &vp)) 201 panic("nfs swap"); 202 vp->v_type = VREG; 203 vp->v_flag = 0; 204 swapdev_vp = vp; 205 VREF(vp); 206 swdevt[0].sw_vp = vp; 207 swdevt[0].sw_nblks = ntohl(nfs_diskless.swap_nblks); 208 } 209 210 /* 211 * Create the rootfs mount point. 212 */ 213 mp = (struct mount *)malloc((u_long)sizeof(struct mount), 214 M_MOUNT, M_NOWAIT); 215 if (mp == NULL) 216 panic("nfs root mount2"); 217 mp->mnt_op = &nfs_vfsops; 218 mp->mnt_flag = MNT_RDONLY; 219 mp->mnt_mounth = NULLVP; 220 221 /* 222 * Set up the root fs args and call mountnfs() to do the rest. 223 */ 224 nfs_diskless.root_args.fh = (nfsv2fh_t *)nfs_diskless.root_fh; 225 MGET(m, MT_SONAME, M_DONTWAIT); 226 if (m == NULL) 227 panic("nfs root mbuf2"); 228 bcopy((caddr_t)&nfs_diskless.root_saddr, mtod(m, caddr_t), 229 nfs_diskless.root_saddr.sin_len); 230 m->m_len = (int)nfs_diskless.root_saddr.sin_len; 231 nfsargs_ntoh(&nfs_diskless.root_args); 232 if (mountnfs(&nfs_diskless.root_args, mp, m, "/", 233 nfs_diskless.root_hostnam, &vp)) 234 panic("nfs root"); 235 if (vfs_lock(mp)) 236 panic("nfs root2"); 237 rootfs = mp; 238 mp->mnt_next = mp; 239 mp->mnt_prev = mp; 240 mp->mnt_vnodecovered = NULLVP; 241 vfs_unlock(mp); 242 rootvp = vp; 243 inittodr((time_t)0); /* There is no time in the nfs fsstat so ?? */ 244 return (0); 245 } 246 247 /* 248 * Convert the integer fields of the nfs_args structure from net byte order 249 * to host byte order. Called by nfs_mountroot() above. 250 */ 251 void 252 nfsargs_ntoh(nfsp) 253 register struct nfs_args *nfsp; 254 { 255 256 NTOHL(nfsp->sotype); 257 NTOHL(nfsp->proto); 258 NTOHL(nfsp->flags); 259 NTOHL(nfsp->wsize); 260 NTOHL(nfsp->rsize); 261 NTOHL(nfsp->timeo); 262 NTOHL(nfsp->retrans); 263 NTOHL(nfsp->maxgrouplist); 264 NTOHL(nfsp->readahead); 265 NTOHL(nfsp->leaseterm); 266 NTOHL(nfsp->deadthresh); 267 } 268 269 /* 270 * VFS Operations. 271 * 272 * mount system call 273 * It seems a bit dumb to copyinstr() the host and path here and then 274 * bcopy() them in mountnfs(), but I wanted to detect errors before 275 * doing the sockargs() call because sockargs() allocates an mbuf and 276 * an error after that means that I have to release the mbuf. 277 */ 278 /* ARGSUSED */ 279 nfs_mount(mp, path, data, ndp, p) 280 struct mount *mp; 281 char *path; 282 caddr_t data; 283 struct nameidata *ndp; 284 struct proc *p; 285 { 286 int error; 287 struct nfs_args args; 288 struct mbuf *nam; 289 struct vnode *vp; 290 char pth[MNAMELEN], hst[MNAMELEN]; 291 u_int len; 292 nfsv2fh_t nfh; 293 294 if (mp->mnt_flag & MNT_UPDATE) 295 return (0); 296 if (error = copyin(data, (caddr_t)&args, sizeof (struct nfs_args))) 297 return (error); 298 if (error = copyin((caddr_t)args.fh, (caddr_t)&nfh, sizeof (nfsv2fh_t))) 299 return (error); 300 if (error = copyinstr(path, pth, MNAMELEN-1, &len)) 301 return (error); 302 bzero(&pth[len], MNAMELEN - len); 303 if (error = copyinstr(args.hostname, hst, MNAMELEN-1, &len)) 304 return (error); 305 bzero(&hst[len], MNAMELEN - len); 306 /* sockargs() call must be after above copyin() calls */ 307 if (error = sockargs(&nam, (caddr_t)args.addr, 308 args.addrlen, MT_SONAME)) 309 return (error); 310 args.fh = &nfh; 311 error = mountnfs(&args, mp, nam, pth, hst, &vp); 312 return (error); 313 } 314 315 /* 316 * Common code for mount and mountroot 317 */ 318 mountnfs(argp, mp, nam, pth, hst, vpp) 319 register struct nfs_args *argp; 320 register struct mount *mp; 321 struct mbuf *nam; 322 char *pth, *hst; 323 struct vnode **vpp; 324 { 325 register struct nfsmount *nmp; 326 struct nfsnode *np; 327 int error; 328 fsid_t tfsid; 329 330 MALLOC(nmp, struct nfsmount *, sizeof (struct nfsmount), M_NFSMNT, 331 M_WAITOK); 332 bzero((caddr_t)nmp, sizeof (struct nfsmount)); 333 mp->mnt_data = (qaddr_t)nmp; 334 /* 335 * Generate a unique nfs mount id. The problem is that a dev number 336 * is not unique across multiple systems. The techique is as follows: 337 * 1) Set to nblkdev,0 which will never be used otherwise 338 * 2) Generate a first guess as nblkdev,nfs_mntid where nfs_mntid is 339 * NOT 0 340 * 3) Loop searching the mount list for another one with same id 341 * If a match, increment val[0] and try again 342 * NB: I increment val[0] { a long } instead of nfs_mntid { a u_char } 343 * so that nfs is not limited to 255 mount points 344 * Incrementing the high order bits does no real harm, since it 345 * simply makes the major dev number tick up. The upper bound is 346 * set to major dev 127 to avoid any sign extention problems 347 */ 348 mp->mnt_stat.f_fsid.val[0] = makedev(nblkdev, 0); 349 mp->mnt_stat.f_fsid.val[1] = MOUNT_NFS; 350 if (++nfs_mntid == 0) 351 ++nfs_mntid; 352 tfsid.val[0] = makedev(nblkdev, nfs_mntid); 353 tfsid.val[1] = MOUNT_NFS; 354 while (rootfs && getvfs(&tfsid)) { 355 tfsid.val[0]++; 356 nfs_mntid++; 357 } 358 if (major(tfsid.val[0]) > 127) { 359 error = ENOENT; 360 goto bad; 361 } 362 mp->mnt_stat.f_fsid.val[0] = tfsid.val[0]; 363 nmp->nm_mountp = mp; 364 nmp->nm_flag = argp->flags; 365 if ((nmp->nm_flag & (NFSMNT_NQNFS | NFSMNT_MYWRITE)) == 366 (NFSMNT_NQNFS | NFSMNT_MYWRITE)) { 367 error = EPERM; 368 goto bad; 369 } 370 if ((nmp->nm_flag & (NFSMNT_RDIRALOOK | NFSMNT_LEASETERM)) && 371 (nmp->nm_flag & NFSMNT_NQNFS) == 0) { 372 error = EPERM; 373 goto bad; 374 } 375 nmp->nm_timeo = NFS_TIMEO; 376 nmp->nm_retry = NFS_RETRANS; 377 nmp->nm_wsize = NFS_WSIZE; 378 nmp->nm_rsize = NFS_RSIZE; 379 nmp->nm_numgrps = NFS_MAXGRPS; 380 nmp->nm_readahead = NFS_DEFRAHEAD; 381 nmp->nm_leaseterm = NQ_DEFLEASE; 382 nmp->nm_deadthresh = NQ_DEADTHRESH; 383 nmp->nm_tnext = (struct nfsnode *)nmp; 384 nmp->nm_tprev = (struct nfsnode *)nmp; 385 nmp->nm_inprog = NULLVP; 386 bcopy((caddr_t)argp->fh, (caddr_t)&nmp->nm_fh, sizeof(nfsv2fh_t)); 387 mp->mnt_stat.f_type = MOUNT_NFS; 388 bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN); 389 bcopy(pth, mp->mnt_stat.f_mntonname, MNAMELEN); 390 nmp->nm_nam = nam; 391 392 if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) { 393 nmp->nm_timeo = (argp->timeo * NFS_HZ + 5) / 10; 394 if (nmp->nm_timeo < NFS_MINTIMEO) 395 nmp->nm_timeo = NFS_MINTIMEO; 396 else if (nmp->nm_timeo > NFS_MAXTIMEO) 397 nmp->nm_timeo = NFS_MAXTIMEO; 398 } 399 400 if ((argp->flags & NFSMNT_RETRANS) && argp->retrans > 1) { 401 nmp->nm_retry = argp->retrans; 402 if (nmp->nm_retry > NFS_MAXREXMIT) 403 nmp->nm_retry = NFS_MAXREXMIT; 404 } 405 406 if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) { 407 nmp->nm_wsize = argp->wsize; 408 /* Round down to multiple of blocksize */ 409 nmp->nm_wsize &= ~0x1ff; 410 if (nmp->nm_wsize <= 0) 411 nmp->nm_wsize = 512; 412 else if (nmp->nm_wsize > NFS_MAXDATA) 413 nmp->nm_wsize = NFS_MAXDATA; 414 } 415 if (nmp->nm_wsize > MAXBSIZE) 416 nmp->nm_wsize = MAXBSIZE; 417 418 if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) { 419 nmp->nm_rsize = argp->rsize; 420 /* Round down to multiple of blocksize */ 421 nmp->nm_rsize &= ~0x1ff; 422 if (nmp->nm_rsize <= 0) 423 nmp->nm_rsize = 512; 424 else if (nmp->nm_rsize > NFS_MAXDATA) 425 nmp->nm_rsize = NFS_MAXDATA; 426 } 427 if (nmp->nm_rsize > MAXBSIZE) 428 nmp->nm_rsize = MAXBSIZE; 429 if ((argp->flags & NFSMNT_MAXGRPS) && argp->maxgrouplist >= 0 && 430 argp->maxgrouplist <= NFS_MAXGRPS) 431 nmp->nm_numgrps = argp->maxgrouplist; 432 if ((argp->flags & NFSMNT_READAHEAD) && argp->readahead >= 0 && 433 argp->readahead <= NFS_MAXRAHEAD) 434 nmp->nm_readahead = argp->readahead; 435 if ((argp->flags & NFSMNT_LEASETERM) && argp->leaseterm >= 2 && 436 argp->leaseterm <= NQ_MAXLEASE) 437 nmp->nm_leaseterm = argp->leaseterm; 438 if ((argp->flags & NFSMNT_DEADTHRESH) && argp->deadthresh >= 1 && 439 argp->deadthresh <= NQ_NEVERDEAD) 440 nmp->nm_deadthresh = argp->deadthresh; 441 /* Set up the sockets and per-host congestion */ 442 nmp->nm_sotype = argp->sotype; 443 nmp->nm_soproto = argp->proto; 444 445 /* 446 * For Connection based sockets (TCP,...) defer the connect until 447 * the first request, in case the server is not responding. 448 */ 449 if (nmp->nm_sotype == SOCK_DGRAM && 450 (error = nfs_connect(nmp, (struct nfsreq *)0))) 451 goto bad; 452 453 /* 454 * This is silly, but it has to be set so that vinifod() works. 455 * We do not want to do an nfs_statfs() here since we can get 456 * stuck on a dead server and we are holding a lock on the mount 457 * point. 458 */ 459 mp->mnt_stat.f_iosize = NFS_MAXDGRAMDATA; 460 /* 461 * A reference count is needed on the nfsnode representing the 462 * remote root. If this object is not persistent, then backward 463 * traversals of the mount point (i.e. "..") will not work if 464 * the nfsnode gets flushed out of the cache. Ufs does not have 465 * this problem, because one can identify root inodes by their 466 * number == ROOTINO (2). 467 */ 468 if (error = nfs_nget(mp, &nmp->nm_fh, &np)) 469 goto bad; 470 *vpp = NFSTOV(np); 471 472 return (0); 473 bad: 474 nfs_disconnect(nmp); 475 free((caddr_t)nmp, M_NFSMNT); 476 m_freem(nam); 477 return (error); 478 } 479 480 /* 481 * unmount system call 482 */ 483 nfs_unmount(mp, mntflags, p) 484 struct mount *mp; 485 int mntflags; 486 struct proc *p; 487 { 488 register struct nfsmount *nmp; 489 struct nfsnode *np; 490 struct vnode *vp; 491 int error, flags = 0; 492 extern int doforce; 493 494 if (mntflags & MNT_FORCE) { 495 if (!doforce || mp == rootfs) 496 return (EINVAL); 497 flags |= FORCECLOSE; 498 } 499 nmp = VFSTONFS(mp); 500 /* 501 * Clear out the buffer cache 502 */ 503 mntflushbuf(mp, 0); 504 if (mntinvalbuf(mp)) 505 return (EBUSY); 506 /* 507 * Goes something like this.. 508 * - Check for activity on the root vnode (other than ourselves). 509 * - Call vflush() to clear out vnodes for this file system, 510 * except for the root vnode. 511 * - Decrement reference on the vnode representing remote root. 512 * - Close the socket 513 * - Free up the data structures 514 */ 515 /* 516 * We need to decrement the ref. count on the nfsnode representing 517 * the remote root. See comment in mountnfs(). The VFS unmount() 518 * has done vput on this vnode, otherwise we would get deadlock! 519 */ 520 if (error = nfs_nget(mp, &nmp->nm_fh, &np)) 521 return(error); 522 vp = NFSTOV(np); 523 if (vp->v_usecount > 2) { 524 vput(vp); 525 return (EBUSY); 526 } 527 528 /* 529 * Must handshake with nqnfs_clientd() if it is active. 530 */ 531 nmp->nm_flag |= NFSMNT_DISMINPROG; 532 while (nmp->nm_inprog != NULLVP) 533 (void) tsleep((caddr_t)&lbolt, PSOCK, "nfsdism", 0); 534 if (error = vflush(mp, vp, flags)) { 535 vput(vp); 536 nmp->nm_flag &= ~NFSMNT_DISMINPROG; 537 return (error); 538 } 539 540 /* 541 * We are now committed to the unmount. 542 * For NQNFS, let the server daemon free the nfsmount structure. 543 */ 544 if (nmp->nm_flag & (NFSMNT_NQNFS | NFSMNT_KERB)) 545 nmp->nm_flag |= NFSMNT_DISMNT; 546 547 /* 548 * There are two reference counts to get rid of here. 549 */ 550 vrele(vp); 551 vrele(vp); 552 nfs_disconnect(nmp); 553 m_freem(nmp->nm_nam); 554 555 if ((nmp->nm_flag & (NFSMNT_NQNFS | NFSMNT_KERB)) == 0) 556 free((caddr_t)nmp, M_NFSMNT); 557 return (0); 558 } 559 560 /* 561 * Return root of a filesystem 562 */ 563 nfs_root(mp, vpp) 564 struct mount *mp; 565 struct vnode **vpp; 566 { 567 register struct vnode *vp; 568 struct nfsmount *nmp; 569 struct nfsnode *np; 570 int error; 571 572 nmp = VFSTONFS(mp); 573 if (error = nfs_nget(mp, &nmp->nm_fh, &np)) 574 return (error); 575 vp = NFSTOV(np); 576 vp->v_type = VDIR; 577 vp->v_flag = VROOT; 578 *vpp = vp; 579 return (0); 580 } 581 582 extern int syncprt; 583 584 /* 585 * Flush out the buffer cache 586 */ 587 /* ARGSUSED */ 588 nfs_sync(mp, waitfor) 589 struct mount *mp; 590 int waitfor; 591 { 592 if (syncprt) 593 ufs_bufstats(); 594 /* 595 * Force stale buffer cache information to be flushed. 596 */ 597 mntflushbuf(mp, waitfor == MNT_WAIT ? B_SYNC : 0); 598 return (0); 599 } 600 601 /* 602 * At this point, this should never happen 603 */ 604 /* ARGSUSED */ 605 nfs_fhtovp(mp, fhp, setgen, vpp) 606 struct mount *mp; 607 struct fid *fhp; 608 int setgen; 609 struct vnode **vpp; 610 { 611 612 return (EINVAL); 613 } 614 615 /* 616 * Vnode pointer to File handle, should never happen either 617 */ 618 /* ARGSUSED */ 619 nfs_vptofh(vp, fhp) 620 struct vnode *vp; 621 struct fid *fhp; 622 { 623 624 return (EINVAL); 625 } 626 627 /* 628 * Vfs start routine, a no-op. 629 */ 630 /* ARGSUSED */ 631 nfs_start(mp, flags, p) 632 struct mount *mp; 633 int flags; 634 struct proc *p; 635 { 636 637 return (0); 638 } 639 640 /* 641 * Do operations associated with quotas, not supported 642 */ 643 /* ARGSUSED */ 644 nfs_quotactl(mp, cmd, uid, arg, p) 645 struct mount *mp; 646 int cmd; 647 u_int uid; 648 caddr_t arg; 649 struct proc *p; 650 { 651 652 return (EOPNOTSUPP); 653 } 654