f923e4ef93da6917c576abca3d9cde9feabbef4b
[openafs.git] / src / afs / SOLARIS / osi_vfsops.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /*
11  * osi_vfsops.c for SOLARIS
12  */
13 #include <afsconfig.h>
14 #include "afs/param.h"
15
16
17 #include "afs/sysincludes.h"    /* Standard vendor system headers */
18 #include "afsincludes.h"        /* Afs-based standard headers */
19 #include "afs/afs_stats.h"      /* statistics stuff */
20 #include "h/modctl.h"
21 #include "h/syscall.h"
22 #if defined(AFS_SUN511_ENV)
23 #include <sys/vfs_opreg.h>
24 #endif
25 #include <sys/kobj.h>
26
27 #ifdef AFS_SUN58_ENV
28 # include <sys/mount.h>
29 #endif
30
31 struct vfs *afs_globalVFS = 0;
32 struct vcache *afs_globalVp = 0;
33
34 #if defined(AFS_SUN57_64BIT_ENV)
35 extern struct sysent sysent32[];
36 #endif
37
38 int afsfstype = 0;
39
40 int
41 afs_mount(struct vfs *afsp, struct vnode *amvp, struct mounta *uap,
42           afs_ucred_t *credp)
43 {
44
45     AFS_GLOCK();
46
47     AFS_STATCNT(afs_mount);
48
49 #if defined(AFS_SUN510_ENV)
50     if (secpolicy_fs_mount(credp, amvp, afsp) != 0) {
51 #else
52     if (!afs_osi_suser(credp)) {
53 #endif
54         AFS_GUNLOCK();
55         return (EPERM);
56     }
57     afsp->vfs_fstype = afsfstype;
58
59     if (afs_globalVFS) {        /* Don't allow remounts. */
60         AFS_GUNLOCK();
61         return (EBUSY);
62     }
63
64     afs_globalVFS = afsp;
65     afsp->vfs_bsize = 8192;
66     afsp->vfs_fsid.val[0] = AFS_VFSMAGIC;       /* magic */
67     afsp->vfs_fsid.val[1] = AFS_VFSFSID;
68     afsp->vfs_dev = AFS_VFSMAGIC;
69
70     AFS_GUNLOCK();
71     return 0;
72 }
73
74 static void
75 afs_freevfs(void)
76 {
77     int i;
78     struct vcache *vc, *nvc;
79     extern struct vcache *afs_vhashT[VCSIZE];
80
81     afs_globalVFS = 0;
82
83     /* free mappings for all vcaches */
84     for (i = 0; i < VCSIZE; i++) {
85         for (vc = afs_vhashT[i]; vc; vc = nvc) {
86             int fv_slept;
87             nvc = vc->hnext;
88             if (afs_FlushVCache(vc, &fv_slept)) {
89                 afs_warn("afs_FlushVCache failed on 0x%llx\n",
90                          (unsigned long long)vc);
91             }
92         }
93     }
94
95     afs_shutdown();
96 }
97
98 #if defined(AFS_SUN58_ENV)
99 int
100 afs_unmount(struct vfs *afsp, int flag, afs_ucred_t *credp)
101 #else
102 int
103 afs_unmount(struct vfs *afsp, afs_ucred_t *credp)
104 #endif
105 {
106     struct vcache *rootvp = NULL;
107
108     AFS_GLOCK();
109     AFS_STATCNT(afs_unmount);
110
111 #if defined(AFS_SUN510_ENV)
112     if (secpolicy_fs_unmount(credp, afsp) != 0) {
113 #else
114     if (!afs_osi_suser(credp)) {
115 #endif
116         AFS_GUNLOCK();
117         return (EPERM);
118     }
119
120 #ifdef AFS_SUN58_ENV
121     if (flag & MS_FORCE) {
122         AFS_GUNLOCK();
123         return ENOTSUP;
124     }
125
126     /* We should have one reference from the caller, and one reference for the
127      * root vnode; any more and someone is still referencing something */
128     if (afsp->vfs_count > 2) {
129         AFS_GUNLOCK();
130         return EBUSY;
131     }
132
133     /* The root vnode should have one ref for the mount; any more, and someone
134      * else is using the root vnode */
135     if (afs_globalVp && VREFCOUNT_GT(afs_globalVp, 1)) {
136         AFS_GUNLOCK();
137         return EBUSY;
138     }
139
140     afsp->vfs_flag |= VFS_UNMOUNTED;
141 #endif /* AFS_SUN58_ENV */
142
143     /* release the root vnode, which should be the last reference to us
144      * besides the caller of afs_unmount */
145     rootvp = afs_globalVp;
146     afs_globalVp = NULL;
147     AFS_RELE(rootvp);
148
149 #ifndef AFS_SUN58_ENV
150     /* shutdown now, since gafs_freevfs() will not be called */
151     afs_freevfs();
152 #endif /* !AFS_SUN58_ENV */
153
154     AFS_GUNLOCK();
155     return 0;
156 }
157
158 #ifdef AFS_SUN58_ENV
159 void
160 gafs_freevfs(struct vfs *afsp)
161 {
162     AFS_GLOCK();
163
164     afs_freevfs();
165
166     AFS_GUNLOCK();
167 }
168 #endif /* AFS_SUN58_ENV */
169
170 int
171 afs_root(struct vfs *afsp, struct vnode **avpp)
172 {
173     afs_int32 code = 0;
174     struct vrequest treq;
175     struct vcache *tvp = 0;
176     struct vcache *gvp;
177     struct proc *proc = ttoproc(curthread);
178     struct vnode *vp = afsp->vfs_vnodecovered;
179     int locked = 0;
180
181     /* Potential deadlock:
182      * afs_root is called with the Vnode's v_lock locked. Set VVFSLOCK
183      * and drop the v_lock if we need to make an RPC to complete this
184      * request. There used to be a deadlock on the global lock until
185      * we stopped calling iget while holding the global lock.
186      */
187
188     AFS_GLOCK();
189
190     AFS_STATCNT(afs_root);
191
192 again:
193     if (afs_globalVp && (afs_globalVp->f.states & CStatd)) {
194         tvp = afs_globalVp;
195     } else {
196         if (MUTEX_HELD(&vp->v_lock)) {
197             vp->v_flag |= VVFSLOCK;
198             locked = 1;
199             mutex_exit(&vp->v_lock);
200         }
201
202         if (afs_globalVp) {
203             gvp = afs_globalVp;
204             afs_globalVp = NULL;
205             afs_PutVCache(gvp);
206         }
207
208         if (!(code = afs_InitReq(&treq, proc->p_cred))
209             && !(code = afs_CheckInit())) {
210             tvp = afs_GetVCache(&afs_rootFid, &treq, NULL, NULL);
211             /* we really want this to stay around */
212             if (tvp) {
213                 if (afs_globalVp) {
214                     /* someone else got there before us! */
215                     afs_PutVCache(tvp);
216                     tvp = 0;
217                     goto again;
218                 }
219                 afs_globalVp = tvp;
220             } else
221                 code = ENOENT;
222         }
223     }
224     if (tvp) {
225         AFS_FAST_HOLD(tvp);
226         mutex_enter(&AFSTOV(tvp)->v_lock);
227         AFSTOV(tvp)->v_flag |= VROOT;
228         mutex_exit(&AFSTOV(tvp)->v_lock);
229
230         afs_globalVFS = afsp;
231         *avpp = AFSTOV(tvp);
232     }
233
234     afs_Trace2(afs_iclSetp, CM_TRACE_VFSROOT, ICL_TYPE_POINTER, *avpp,
235                ICL_TYPE_INT32, code);
236
237     AFS_GUNLOCK();
238     if (locked) {
239         mutex_enter(&vp->v_lock);
240         vp->v_flag &= ~VVFSLOCK;
241         if (vp->v_flag & VVFSWAIT) {
242             vp->v_flag &= ~VVFSWAIT;
243             cv_broadcast(&vp->v_cv);
244         }
245     }
246
247     return code;
248 }
249
250 #ifdef AFS_SUN56_ENV
251 int
252 afs_statvfs(struct vfs *afsp, struct statvfs64 *abp)
253 #else
254 int
255 afs_statvfs(struct vfs *afsp, struct statvfs *abp)
256 #endif
257 {
258     AFS_GLOCK();
259
260     AFS_STATCNT(afs_statfs);
261
262     abp->f_frsize = 1024;
263     abp->f_favail = 9000000;
264     abp->f_bsize = afsp->vfs_bsize;
265     abp->f_blocks = abp->f_bfree = abp->f_bavail = abp->f_files =
266         abp->f_ffree = 9000000;
267     abp->f_fsid = (AFS_VFSMAGIC << 16) || AFS_VFSFSID;
268
269     AFS_GUNLOCK();
270     return 0;
271 }
272
273 int
274 afs_sync(struct vfs *afsp, short flags, afs_ucred_t *credp)
275 {
276     return 0;
277 }
278
279 int
280 afs_vget(struct vfs *afsp, struct vnode **avcp, struct fid *fidp)
281 {
282     cred_t *credp = CRED();
283     struct vrequest treq;
284     int code;
285
286     AFS_GLOCK();
287
288     AFS_STATCNT(afs_vget);
289
290     *avcp = NULL;
291     if (!(code = afs_InitReq(&treq, credp))) {
292         code = afs_osi_vget((struct vcache **)avcp, fidp, &treq);
293     }
294
295     afs_Trace3(afs_iclSetp, CM_TRACE_VGET, ICL_TYPE_POINTER, *avcp,
296                ICL_TYPE_INT32, treq.uid, ICL_TYPE_FID, fidp);
297     code = afs_CheckCode(code, &treq, 42);
298
299     AFS_GUNLOCK();
300     return code;
301 }
302
303 /* This is only called by vfs_mount when afs is going to be mounted as root.
304  * Since we don't support diskless clients we shouldn't come here.
305  */
306 int afsmountroot = 0;
307 afs_mountroot(struct vfs *afsp, whymountroot_t why)
308 {
309     AFS_GLOCK();
310     AFS_STATCNT(afs_mountroot);
311     afsmountroot++;
312     AFS_GUNLOCK();
313     return EINVAL;
314 }
315
316 /* afs_swapvp is called to setup swapping over the net for diskless clients.
317  * Again not for us.
318  */
319 int afsswapvp = 0;
320 afs_swapvp(struct vfs *afsp, struct vnode **avpp, char *nm)
321 {
322     AFS_GLOCK();
323     AFS_STATCNT(afs_swapvp);
324     afsswapvp++;
325     AFS_GUNLOCK();
326     return EINVAL;
327 }
328
329
330 #if defined(AFS_SUN511_ENV)
331 /* The following list must always be NULL-terminated */
332 static const fs_operation_def_t afs_vfsops_template[] = {
333     VFSNAME_MOUNT,              { .vfs_mount = afs_mount },
334     VFSNAME_UNMOUNT,            { .vfs_unmount = afs_unmount },
335     VFSNAME_ROOT,               { .vfs_root = afs_root },
336     VFSNAME_STATVFS,            { .vfs_statvfs = afs_statvfs },
337     VFSNAME_SYNC,               { .vfs_sync = afs_sync },
338     VFSNAME_VGET,               { .vfs_vget = afs_vget },
339     VFSNAME_MOUNTROOT,          { .vfs_mountroot = afs_mountroot },
340     VFSNAME_FREEVFS,            { .vfs_freevfs = gafs_freevfs },
341     NULL,                       NULL
342 };
343 struct vfsops *afs_vfsopsp;
344 #elif defined(AFS_SUN510_ENV)
345 /* The following list must always be NULL-terminated */
346 const fs_operation_def_t afs_vfsops_template[] = {
347     VFSNAME_MOUNT,              afs_mount,
348     VFSNAME_UNMOUNT,            afs_unmount,
349     VFSNAME_ROOT,               afs_root,
350     VFSNAME_STATVFS,            afs_statvfs,
351     VFSNAME_SYNC,               afs_sync,
352     VFSNAME_VGET,               afs_vget,
353     VFSNAME_MOUNTROOT,          afs_mountroot,
354     VFSNAME_FREEVFS,            gafs_freevfs,
355     NULL,                       NULL
356 };
357 struct vfsops *afs_vfsopsp;
358 #else
359 struct vfsops Afs_vfsops = {
360     afs_mount,
361     afs_unmount,
362     afs_root,
363     afs_statvfs,
364     afs_sync,
365     afs_vget,
366     afs_mountroot,
367     afs_swapvp,
368 #if defined(AFS_SUN58_ENV)
369     gafs_freevfs,
370 #endif
371 };
372 #endif
373
374
375 /*
376  * afsinit - intialize VFS
377  */
378 int (*ufs_iallocp) ();
379 void (*ufs_iupdatp) ();
380 int (*ufs_igetp) ();
381 void (*ufs_itimes_nolockp) ();
382
383 int (*afs_orig_ioctl) (), (*afs_orig_ioctl32) ();
384 int (*afs_orig_setgroups) (), (*afs_orig_setgroups32) ();
385
386 #ifndef AFS_SUN510_ENV
387 struct ill_s *ill_g_headp = 0;
388 #endif
389
390 int afs_sinited = 0;
391
392 extern struct fs_operation_def afs_vnodeops_template[];
393
394 #if     !defined(AFS_NONFSTRANS)
395 int (*nfs_rfsdisptab_v2) ();
396 int (*nfs_rfsdisptab_v3) ();
397 int (*nfs_acldisptab_v2) ();
398 int (*nfs_acldisptab_v3) ();
399
400 int (*nfs_checkauth) ();
401 #endif
402
403 extern Afs_syscall();
404
405 static void *
406 do_mod_lookup(const char * mod, const char * sym)
407 {
408     void * ptr;
409
410     ptr = modlookup(mod, sym);
411     if (ptr == NULL) {
412         afs_warn("modlookup failed for symbol '%s' in module '%s'\n",
413                  sym, mod);
414     }
415
416     return ptr;
417 }
418
419 #ifdef AFS_SUN510_ENV
420 afsinit(int fstype, char *dummy)
421 #else
422 afsinit(struct vfssw *vfsswp, int fstype)
423 #endif
424 {
425     extern int afs_xioctl();
426     extern int afs_xsetgroups();
427
428     AFS_STATCNT(afsinit);
429
430     afs_orig_setgroups = sysent[SYS_setgroups].sy_callc;
431     afs_orig_ioctl = sysent[SYS_ioctl].sy_call;
432     sysent[SYS_setgroups].sy_callc = afs_xsetgroups;
433     sysent[SYS_ioctl].sy_call = afs_xioctl;
434
435 #if defined(AFS_SUN57_64BIT_ENV)
436     afs_orig_setgroups32 = sysent32[SYS_setgroups].sy_callc;
437     afs_orig_ioctl32 = sysent32[SYS_ioctl].sy_call;
438     sysent32[SYS_setgroups].sy_callc = afs_xsetgroups;
439     sysent32[SYS_ioctl].sy_call = afs_xioctl;
440 #endif /* AFS_SUN57_64BIT_ENV */
441
442 #ifdef AFS_SUN510_ENV
443     vfs_setfsops(fstype, afs_vfsops_template, &afs_vfsopsp);
444     afsfstype = fstype;
445     vn_make_ops("afs", afs_vnodeops_template, &afs_ops);
446 #else /* !AFS_SUN510_ENV */
447     vfsswp->vsw_vfsops = &Afs_vfsops;
448     afsfstype = fstype;
449 #endif /* !AFS_SUN510_ENV */
450
451
452 #if !defined(AFS_NONFSTRANS)
453     nfs_rfsdisptab_v2 = (int (*)()) do_mod_lookup("nfssrv", "rfsdisptab_v2");
454     if (nfs_rfsdisptab_v2 != NULL) {
455         nfs_acldisptab_v2 = (int (*)()) do_mod_lookup("nfssrv", "acldisptab_v2");
456         if (nfs_acldisptab_v2 != NULL) {
457             afs_xlatorinit_v2(nfs_rfsdisptab_v2, nfs_acldisptab_v2);
458         }
459     }
460     nfs_rfsdisptab_v3 = (int (*)()) do_mod_lookup("nfssrv", "rfsdisptab_v3");
461     if (nfs_rfsdisptab_v3 != NULL) {
462         nfs_acldisptab_v3 = (int (*)()) do_mod_lookup("nfssrv", "acldisptab_v3");
463         if (nfs_acldisptab_v3 != NULL) {
464             afs_xlatorinit_v3(nfs_rfsdisptab_v3, nfs_acldisptab_v3);
465         }
466     }
467
468     nfs_checkauth = (int (*)()) do_mod_lookup("nfssrv", "checkauth");
469 #endif /* !AFS_NONFSTRANS */
470
471     ufs_iallocp = (int (*)()) do_mod_lookup("ufs", "ufs_ialloc");
472     ufs_iupdatp = (void (*)()) do_mod_lookup("ufs", "ufs_iupdat");
473     ufs_igetp = (int (*)()) do_mod_lookup("ufs", "ufs_iget");
474     ufs_itimes_nolockp = (void (*)()) do_mod_lookup("ufs", "ufs_itimes_nolock");
475
476     if (!ufs_iallocp || !ufs_iupdatp || !ufs_itimes_nolockp || !ufs_igetp) {
477         afs_warn("AFS to UFS mapping cannot be fully initialised\n");
478     }
479
480 #if !defined(AFS_SUN510_ENV)
481     ill_g_headp = (struct ill_s *) do_mod_lookup("ip", "ill_g_head");
482 #endif /* !AFS_SUN510_ENV */
483
484     afs_sinited = 1;
485     return 0;
486
487 }
488
489 #ifdef AFS_SUN510_ENV
490 #ifdef AFS_SUN511_ENV
491 static vfsdef_t afs_vfsdef = {
492     VFSDEF_VERSION,
493     "afs",
494     afsinit,
495     0,
496     NULL
497 };
498 #else
499 static struct vfsdef_v3 afs_vfsdef = {
500     VFSDEF_VERSION,
501     "afs",
502     afsinit,
503     0
504 };
505 #endif
506 #else
507 static struct vfssw afs_vfw = {
508     "afs",
509     afsinit,
510     &Afs_vfsops,
511     0
512 };
513 #endif
514
515 #ifndef AFS_SUN511_ENV
516 static struct sysent afssysent = {
517     6,
518     0,
519     Afs_syscall
520 };
521 #endif /* AFS_SUN511_ENV */
522
523 /* inter-module dependencies */
524 char _depends_on[] = "drv/ip drv/udp strmod/rpcmod";
525
526 /*
527  * Info/Structs to link the afs module into the kernel
528  */
529 extern struct mod_ops mod_fsops;
530
531 static struct modlfs afsmodlfs = {
532     &mod_fsops,
533     "afs filesystem",
534 #ifdef AFS_SUN510_ENV
535     &afs_vfsdef
536 #else
537     &afs_vfw
538 #endif
539 };
540
541 #ifdef AFS_SUN511_ENV
542
543 extern struct modldrv afs_modldrv;
544
545 #else /* AFS_SUN511_ENV */
546
547 extern struct mod_ops mod_syscallops;
548 static struct modlsys afsmodlsys = {
549     &mod_syscallops,
550     "afs syscall interface",
551     &afssysent
552 };
553
554 /** The two structures afssysent32 and afsmodlsys32 are being added
555   * for supporting 32 bit syscalls. In Solaris 7 there are two system
556   * tables viz. sysent ans sysent32. 32 bit applications use sysent32.
557   * Since most of our user space binaries are going to be 32 bit
558   * we need to attach to sysent32 also. Note that the entry into AFS
559   * land still happens through Afs_syscall irrespective of whether we
560   * land here from sysent or sysent32
561   */
562
563 # if defined(AFS_SUN57_64BIT_ENV)
564 extern struct mod_ops mod_syscallops32;
565
566 static struct modlsys afsmodlsys32 = {
567     &mod_syscallops32,
568     "afs syscall interface(32 bit)",
569     &afssysent
570 };
571 # endif
572 #endif /* !AFS_SUN511_ENV */
573
574
575 static struct modlinkage afs_modlinkage = {
576     MODREV_1,
577     (void *)&afsmodlfs,
578 #ifdef AFS_SUN511_ENV
579     (void *)&afs_modldrv,
580 #else
581     (void *)&afsmodlsys,
582 # ifdef AFS_SUN57_64BIT_ENV
583     (void *)&afsmodlsys32,
584 # endif
585 #endif /* !AFS_SUN511_ENV */
586     NULL
587 };
588
589 /** This is the function that modload calls when loading the afs kernel
590   * extensions. The solaris modload program searches for the _init
591   * function in a module and calls it when modloading
592   */
593
594 _init()
595 {
596     char *sysn, *mod_getsysname();
597     int code;
598     extern char *sysbind;
599     extern struct bind *sb_hashtab[];
600     struct modctl *mp = 0;
601
602     if (afs_sinited)
603         return EBUSY;
604
605     if ((!(mp = mod_find_by_filename("fs", "ufs"))
606          && !(mp = mod_find_by_filename(NULL, "/kernel/fs/ufs"))
607          && !(mp = mod_find_by_filename(NULL, "sys/ufs"))) || (mp
608                                                                && !mp->
609                                                                mod_installed))
610     {
611         printf
612             ("ufs module must be loaded before loading afs; use modload /kernel/fs/ufs\n");
613         return (ENOSYS);
614     }
615 #ifndef AFS_NONFSTRANS
616 #if     defined(AFS_SUN55_ENV)
617     if ((!(mp = mod_find_by_filename("misc", "nfssrv"))
618          && !(mp = mod_find_by_filename(NULL, NFSSRV))
619          && !(mp = mod_find_by_filename(NULL, NFSSRV_V9))
620          && !(mp = mod_find_by_filename(NULL, NFSSRV_AMD64))) || (mp
621                                                                && !mp->
622                                                                mod_installed))
623     {
624         printf
625             ("misc/nfssrv module must be loaded before loading afs with nfs-xlator\n");
626         return (ENOSYS);
627     }
628 #else /* !AFS_SUN55_ENV */
629 #if     defined(AFS_SUN52_ENV)
630     if ((!(mp = mod_find_by_filename("fs", "nfs"))
631          && !(mp = mod_find_by_filename(NULL, "/kernel/fs/nfs"))
632          && !(mp = mod_find_by_filename(NULL, "sys/nfs"))) || (mp
633                                                                && !mp->
634                                                                mod_installed))
635     {
636         printf
637             ("fs/nfs module must be loaded before loading afs with nfs-xlator\n");
638         return (ENOSYS);
639     }
640 #endif /* AFS_SUN52_ENV */
641 #endif /* AFS_SUN55_ENV */
642 #endif /* !AFS_NONFSTRANS */
643
644 #ifndef AFS_SUN511_ENV
645     /* syscall initialization stff */
646
647 # if !defined(AFS_SUN58_ENV)
648     /* 
649      * Re-read the /etc/name_to_sysnum file to make sure afs isn't added after
650      * reboot.  Ideally we would like to call modctl_read_sysbinding_file() but
651      * unfortunately in Solaris 2.2 it became a local function so we have to do
652      * the read_binding_file() direct call with the appropriate text file and
653      * system call hashtable.  make_syscallname actually copies "afs" to the
654      * proper slot entry and we also actually have to properly initialize the
655      * global sysent[AFS_SYSCALL] entry!
656      */
657 #  ifdef        AFS_SUN53_ENV
658 #   ifndef      SYSBINDFILE
659 #    define     SYSBINDFILE     "/etc/name_to_sysnum"
660 #   endif /* SYSBINDFILE */
661     read_binding_file(SYSBINDFILE, sb_hashtab);
662 #  else /* !AFS_SUN53_ENV */
663     read_binding_file(sysbind, sb_hashtab);
664 #  endif /* AFS_SUN53_ENV */
665     make_syscallname("afs", AFS_SYSCALL);
666
667     if (sysent[AFS_SYSCALL].sy_call == nosys) {
668         if ((sysn = mod_getsysname(AFS_SYSCALL)) != NULL) {
669             sysent[AFS_SYSCALL].sy_lock =
670                 (krwlock_t *) kobj_zalloc(sizeof(krwlock_t), KM_SLEEP);
671             rw_init(sysent[AFS_SYSCALL].sy_lock, "afs_syscall",
672 #  ifdef AFS_SUN57_ENV
673                     RW_DEFAULT, NULL);
674 #  else /* !AFS_SUN57_ENV */
675                     RW_DEFAULT, DEFAULT_WT);
676 #  endif /* AFS_SUN57_ENV */
677         }
678     }
679 # endif /* !AFS_SUN58_ENV */
680 #endif /* !AFS_SUN511_ENV */
681
682     osi_Init();                 /* initialize global lock, etc */
683
684     code = mod_install(&afs_modlinkage);
685     return code;
686 }
687
688 _info(modp)
689      struct modinfo *modp;
690 {
691     int code;
692
693     code = mod_info(&afs_modlinkage, modp);
694     return code;
695 }
696
697 _fini()
698 {
699     int code;
700
701     if (afs_globalVFS)
702         return EBUSY;
703
704     if (afs_sinited) {
705         sysent[SYS_setgroups].sy_callc = afs_orig_setgroups;
706         sysent[SYS_ioctl].sy_call = afs_orig_ioctl;
707 #if defined(AFS_SUN57_64BIT_ENV)
708         sysent32[SYS_setgroups].sy_callc = afs_orig_setgroups32;
709         sysent32[SYS_ioctl].sy_call = afs_orig_ioctl32;
710 #endif
711     }
712     code = mod_remove(&afs_modlinkage);
713     return code;
714 }