]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/kern/vfs_init.c
vmware: Fix a typo in a source code comment
[FreeBSD/FreeBSD.git] / sys / kern / vfs_init.c
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1989, 1993
5  *      The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed
8  * to Berkeley by John Heidemann of the UCLA Ficus project.
9  *
10  * Source: * @(#)i405_init.c 2.10 92/04/27 UCLA Ficus project
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. 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  *      @(#)vfs_init.c  8.3 (Berkeley) 1/4/94
37  */
38
39 #include <sys/cdefs.h>
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/fnv_hash.h>
43 #include <sys/jail.h>
44 #include <sys/kernel.h>
45 #include <sys/linker.h>
46 #include <sys/mount.h>
47 #include <sys/proc.h>
48 #include <sys/sx.h>
49 #include <sys/syscallsubr.h>
50 #include <sys/sysctl.h>
51 #include <sys/vnode.h>
52 #include <sys/malloc.h>
53
54 static int      vfs_register(struct vfsconf *);
55 static int      vfs_unregister(struct vfsconf *);
56
57 MALLOC_DEFINE(M_VNODE, "vnodes", "Dynamically allocated vnodes");
58
59 /*
60  * The highest defined VFS number.
61  */
62 int maxvfsconf = VFS_GENERIC + 1;
63
64 /*
65  * Single-linked list of configured VFSes.
66  * New entries are added/deleted by vfs_register()/vfs_unregister()
67  */
68 struct vfsconfhead vfsconf = TAILQ_HEAD_INITIALIZER(vfsconf);
69 struct sx vfsconf_sx;
70 SX_SYSINIT(vfsconf, &vfsconf_sx, "vfsconf");
71
72 /*
73  * Loader.conf variable vfs.typenumhash enables setting vfc_typenum using a hash
74  * calculation on vfc_name, so that it doesn't change when file systems are
75  * loaded in a different order. This will avoid the NFS server file handles from
76  * changing for file systems that use vfc_typenum in their fsid.
77  */
78 static int      vfs_typenumhash = 1;
79 SYSCTL_INT(_vfs, OID_AUTO, typenumhash, CTLFLAG_RDTUN, &vfs_typenumhash, 0,
80     "Set vfc_typenum using a hash calculation on vfc_name, so that it does not"
81     "change when file systems are loaded in a different order.");
82
83 /*
84  * A Zen vnode attribute structure.
85  *
86  * Initialized when the first filesystem registers by vfs_register().
87  */
88 struct vattr va_null;
89
90 /*
91  * vfs_init.c
92  *
93  * Allocate and fill in operations vectors.
94  *
95  * An undocumented feature of this approach to defining operations is that
96  * there can be multiple entries in vfs_opv_descs for the same operations
97  * vector. This allows third parties to extend the set of operations
98  * supported by another layer in a binary compatibile way. For example,
99  * assume that NFS needed to be modified to support Ficus. NFS has an entry
100  * (probably nfs_vnopdeop_decls) declaring all the operations NFS supports by
101  * default. Ficus could add another entry (ficus_nfs_vnodeop_decl_entensions)
102  * listing those new operations Ficus adds to NFS, all without modifying the
103  * NFS code. (Of couse, the OTW NFS protocol still needs to be munged, but
104  * that is a(whole)nother story.) This is a feature.
105  */
106
107 /*
108  * Routines having to do with the management of the vnode table.
109  */
110
111 static struct vfsconf *
112 vfs_byname_locked(const char *name)
113 {
114         struct vfsconf *vfsp;
115
116         sx_assert(&vfsconf_sx, SA_LOCKED);
117         if (!strcmp(name, "ffs"))
118                 name = "ufs";
119         TAILQ_FOREACH(vfsp, &vfsconf, vfc_list) {
120                 if (!strcmp(name, vfsp->vfc_name))
121                         return (vfsp);
122         }
123         return (NULL);
124 }
125
126 struct vfsconf *
127 vfs_byname(const char *name)
128 {
129         struct vfsconf *vfsp;
130
131         vfsconf_slock();
132         vfsp = vfs_byname_locked(name);
133         vfsconf_sunlock();
134         return (vfsp);
135 }
136
137 struct vfsconf *
138 vfs_byname_kld(const char *fstype, struct thread *td, int *error)
139 {
140         struct vfsconf *vfsp;
141         int fileid, loaded;
142
143         vfsp = vfs_byname(fstype);
144         if (vfsp != NULL)
145                 return (vfsp);
146
147         /* Try to load the respective module. */
148         *error = kern_kldload(td, fstype, &fileid);
149         loaded = (*error == 0);
150         if (*error == EEXIST)
151                 *error = 0;
152         if (*error)
153                 return (NULL);
154
155         /* Look up again to see if the VFS was loaded. */
156         vfsp = vfs_byname(fstype);
157         if (vfsp == NULL) {
158                 if (loaded)
159                         (void)kern_kldunload(td, fileid, LINKER_UNLOAD_FORCE);
160                 *error = ENODEV;
161                 return (NULL);
162         }
163         return (vfsp);
164 }
165
166 static int
167 vfs_mount_sigdefer(struct mount *mp)
168 {
169         int prev_stops, rc;
170
171         TSRAW(curthread, TS_ENTER, "VFS_MOUNT", mp->mnt_vfc->vfc_name);
172         prev_stops = sigdeferstop(SIGDEFERSTOP_SILENT);
173         rc = (*mp->mnt_vfc->vfc_vfsops_sd->vfs_mount)(mp);
174         sigallowstop(prev_stops);
175         TSRAW(curthread, TS_EXIT, "VFS_MOUNT", mp->mnt_vfc->vfc_name);
176         return (rc);
177 }
178
179 static int
180 vfs_unmount_sigdefer(struct mount *mp, int mntflags)
181 {
182         int prev_stops, rc;
183
184         prev_stops = sigdeferstop(SIGDEFERSTOP_SILENT);
185         rc = (*mp->mnt_vfc->vfc_vfsops_sd->vfs_unmount)(mp, mntflags);
186         sigallowstop(prev_stops);
187         return (rc);
188 }
189
190 static int
191 vfs_root_sigdefer(struct mount *mp, int flags, struct vnode **vpp)
192 {
193         int prev_stops, rc;
194
195         prev_stops = sigdeferstop(SIGDEFERSTOP_SILENT);
196         rc = (*mp->mnt_vfc->vfc_vfsops_sd->vfs_root)(mp, flags, vpp);
197         sigallowstop(prev_stops);
198         return (rc);
199 }
200
201 static int
202 vfs_cachedroot_sigdefer(struct mount *mp, int flags, struct vnode **vpp)
203 {
204         int prev_stops, rc;
205
206         prev_stops = sigdeferstop(SIGDEFERSTOP_SILENT);
207         rc = (*mp->mnt_vfc->vfc_vfsops_sd->vfs_cachedroot)(mp, flags, vpp);
208         sigallowstop(prev_stops);
209         return (rc);
210 }
211
212 static int
213 vfs_quotactl_sigdefer(struct mount *mp, int cmd, uid_t uid, void *arg)
214 {
215         int prev_stops, rc;
216
217         prev_stops = sigdeferstop(SIGDEFERSTOP_SILENT);
218         rc = (*mp->mnt_vfc->vfc_vfsops_sd->vfs_quotactl)(mp, cmd, uid, arg);
219         sigallowstop(prev_stops);
220         return (rc);
221 }
222
223 static int
224 vfs_statfs_sigdefer(struct mount *mp, struct statfs *sbp)
225 {
226         int prev_stops, rc;
227
228         prev_stops = sigdeferstop(SIGDEFERSTOP_SILENT);
229         rc = (*mp->mnt_vfc->vfc_vfsops_sd->vfs_statfs)(mp, sbp);
230         sigallowstop(prev_stops);
231         return (rc);
232 }
233
234 static int
235 vfs_sync_sigdefer(struct mount *mp, int waitfor)
236 {
237         int prev_stops, rc;
238
239         prev_stops = sigdeferstop(SIGDEFERSTOP_SILENT);
240         rc = (*mp->mnt_vfc->vfc_vfsops_sd->vfs_sync)(mp, waitfor);
241         sigallowstop(prev_stops);
242         return (rc);
243 }
244
245 static int
246 vfs_vget_sigdefer(struct mount *mp, ino_t ino, int flags, struct vnode **vpp)
247 {
248         int prev_stops, rc;
249
250         prev_stops = sigdeferstop(SIGDEFERSTOP_SILENT);
251         rc = (*mp->mnt_vfc->vfc_vfsops_sd->vfs_vget)(mp, ino, flags, vpp);
252         sigallowstop(prev_stops);
253         return (rc);
254 }
255
256 static int
257 vfs_fhtovp_sigdefer(struct mount *mp, struct fid *fidp, int flags,
258     struct vnode **vpp)
259 {
260         int prev_stops, rc;
261
262         prev_stops = sigdeferstop(SIGDEFERSTOP_SILENT);
263         rc = (*mp->mnt_vfc->vfc_vfsops_sd->vfs_fhtovp)(mp, fidp, flags, vpp);
264         sigallowstop(prev_stops);
265         return (rc);
266 }
267
268 static int
269 vfs_checkexp_sigdefer(struct mount *mp, struct sockaddr *nam, uint64_t *exflg,
270     struct ucred **credp, int *numsecflavors, int *secflavors)
271 {
272         int prev_stops, rc;
273
274         prev_stops = sigdeferstop(SIGDEFERSTOP_SILENT);
275         rc = (*mp->mnt_vfc->vfc_vfsops_sd->vfs_checkexp)(mp, nam, exflg, credp,
276             numsecflavors, secflavors);
277         sigallowstop(prev_stops);
278         return (rc);
279 }
280
281 static int
282 vfs_extattrctl_sigdefer(struct mount *mp, int cmd, struct vnode *filename_vp,
283     int attrnamespace, const char *attrname)
284 {
285         int prev_stops, rc;
286
287         prev_stops = sigdeferstop(SIGDEFERSTOP_SILENT);
288         rc = (*mp->mnt_vfc->vfc_vfsops_sd->vfs_extattrctl)(mp, cmd,
289             filename_vp, attrnamespace, attrname);
290         sigallowstop(prev_stops);
291         return (rc);
292 }
293
294 static int
295 vfs_sysctl_sigdefer(struct mount *mp, fsctlop_t op, struct sysctl_req *req)
296 {
297         int prev_stops, rc;
298
299         prev_stops = sigdeferstop(SIGDEFERSTOP_SILENT);
300         rc = (*mp->mnt_vfc->vfc_vfsops_sd->vfs_sysctl)(mp, op, req);
301         sigallowstop(prev_stops);
302         return (rc);
303 }
304
305 static void
306 vfs_susp_clean_sigdefer(struct mount *mp)
307 {
308         int prev_stops;
309
310         if (*mp->mnt_vfc->vfc_vfsops_sd->vfs_susp_clean == NULL)
311                 return;
312         prev_stops = sigdeferstop(SIGDEFERSTOP_SILENT);
313         (*mp->mnt_vfc->vfc_vfsops_sd->vfs_susp_clean)(mp);
314         sigallowstop(prev_stops);
315 }
316
317 static void
318 vfs_reclaim_lowervp_sigdefer(struct mount *mp, struct vnode *vp)
319 {
320         int prev_stops;
321
322         if (*mp->mnt_vfc->vfc_vfsops_sd->vfs_reclaim_lowervp == NULL)
323                 return;
324         prev_stops = sigdeferstop(SIGDEFERSTOP_SILENT);
325         (*mp->mnt_vfc->vfc_vfsops_sd->vfs_reclaim_lowervp)(mp, vp);
326         sigallowstop(prev_stops);
327 }
328
329 static void
330 vfs_unlink_lowervp_sigdefer(struct mount *mp, struct vnode *vp)
331 {
332         int prev_stops;
333
334         if (*mp->mnt_vfc->vfc_vfsops_sd->vfs_unlink_lowervp == NULL)
335                 return;
336         prev_stops = sigdeferstop(SIGDEFERSTOP_SILENT);
337         (*(mp)->mnt_vfc->vfc_vfsops_sd->vfs_unlink_lowervp)(mp, vp);
338         sigallowstop(prev_stops);
339 }
340
341 static void
342 vfs_purge_sigdefer(struct mount *mp)
343 {
344         int prev_stops;
345
346         prev_stops = sigdeferstop(SIGDEFERSTOP_SILENT);
347         (*mp->mnt_vfc->vfc_vfsops_sd->vfs_purge)(mp);
348         sigallowstop(prev_stops);
349 }
350
351 static int
352 vfs_report_lockf_sigdefer(struct mount *mp, struct sbuf *sb)
353 {
354         int prev_stops, rc;
355
356         prev_stops = sigdeferstop(SIGDEFERSTOP_SILENT);
357         rc = (*mp->mnt_vfc->vfc_vfsops_sd->vfs_report_lockf)(mp, sb);
358         sigallowstop(prev_stops);
359         return (rc);
360 }
361
362 static struct vfsops vfsops_sigdefer = {
363         .vfs_mount =            vfs_mount_sigdefer,
364         .vfs_unmount =          vfs_unmount_sigdefer,
365         .vfs_root =             vfs_root_sigdefer,
366         .vfs_cachedroot =       vfs_cachedroot_sigdefer,
367         .vfs_quotactl =         vfs_quotactl_sigdefer,
368         .vfs_statfs =           vfs_statfs_sigdefer,
369         .vfs_sync =             vfs_sync_sigdefer,
370         .vfs_vget =             vfs_vget_sigdefer,
371         .vfs_fhtovp =           vfs_fhtovp_sigdefer,
372         .vfs_checkexp =         vfs_checkexp_sigdefer,
373         .vfs_extattrctl =       vfs_extattrctl_sigdefer,
374         .vfs_sysctl =           vfs_sysctl_sigdefer,
375         .vfs_susp_clean =       vfs_susp_clean_sigdefer,
376         .vfs_reclaim_lowervp =  vfs_reclaim_lowervp_sigdefer,
377         .vfs_unlink_lowervp =   vfs_unlink_lowervp_sigdefer,
378         .vfs_purge =            vfs_purge_sigdefer,
379         .vfs_report_lockf =     vfs_report_lockf_sigdefer,
380 };
381
382 /* Register a new filesystem type in the global table */
383 static int
384 vfs_register(struct vfsconf *vfc)
385 {
386         struct sysctl_oid *oidp;
387         struct vfsops *vfsops;
388         static int once;
389         struct vfsconf *tvfc;
390         uint32_t hashval;
391         int secondpass;
392
393         if (!once) {
394                 vattr_null(&va_null);
395                 once = 1;
396         }
397
398         if (vfc->vfc_version != VFS_VERSION) {
399                 printf("ERROR: filesystem %s, unsupported ABI version %x\n",
400                     vfc->vfc_name, vfc->vfc_version);
401                 return (EINVAL);
402         }
403         vfsconf_lock();
404         if (vfs_byname_locked(vfc->vfc_name) != NULL) {
405                 vfsconf_unlock();
406                 return (EEXIST);
407         }
408
409         if (vfs_typenumhash != 0) {
410                 /*
411                  * Calculate a hash on vfc_name to use for vfc_typenum. Unless
412                  * all of 1<->255 are assigned, it is limited to 8bits since
413                  * that is what ZFS uses from vfc_typenum and is also the
414                  * preferred range for vfs_getnewfsid().
415                  */
416                 hashval = fnv_32_str(vfc->vfc_name, FNV1_32_INIT);
417                 hashval &= 0xff;
418                 secondpass = 0;
419                 do {
420                         /* Look for and fix any collision. */
421                         TAILQ_FOREACH(tvfc, &vfsconf, vfc_list) {
422                                 if (hashval == tvfc->vfc_typenum) {
423                                         if (hashval == 255 && secondpass == 0) {
424                                                 hashval = 1;
425                                                 secondpass = 1;
426                                         } else
427                                                 hashval++;
428                                         break;
429                                 }
430                         }
431                 } while (tvfc != NULL);
432                 vfc->vfc_typenum = hashval;
433                 if (vfc->vfc_typenum >= maxvfsconf)
434                         maxvfsconf = vfc->vfc_typenum + 1;
435         } else
436                 vfc->vfc_typenum = maxvfsconf++;
437         TAILQ_INSERT_TAIL(&vfsconf, vfc, vfc_list);
438
439         /*
440          * Initialise unused ``struct vfsops'' fields, to use
441          * the vfs_std*() functions.  Note, we need the mount
442          * and unmount operations, at the least.  The check
443          * for vfsops available is just a debugging aid.
444          */
445         KASSERT(vfc->vfc_vfsops != NULL,
446             ("Filesystem %s has no vfsops", vfc->vfc_name));
447         /*
448          * Check the mount and unmount operations.
449          */
450         vfsops = vfc->vfc_vfsops;
451         KASSERT(vfsops->vfs_mount != NULL,
452             ("Filesystem %s has no mount op", vfc->vfc_name));
453         KASSERT(vfsops->vfs_unmount != NULL,
454             ("Filesystem %s has no unmount op", vfc->vfc_name));
455
456         if (vfsops->vfs_root == NULL)
457                 /* return file system's root vnode */
458                 vfsops->vfs_root =      vfs_stdroot;
459         if (vfsops->vfs_quotactl == NULL)
460                 /* quota control */
461                 vfsops->vfs_quotactl =  vfs_stdquotactl;
462         if (vfsops->vfs_statfs == NULL)
463                 /* return file system's status */
464                 vfsops->vfs_statfs =    vfs_stdstatfs;
465         if (vfsops->vfs_sync == NULL)
466                 /*
467                  * flush unwritten data (nosync)
468                  * file systems can use vfs_stdsync
469                  * explicitly by setting it in the
470                  * vfsop vector.
471                  */
472                 vfsops->vfs_sync =      vfs_stdnosync;
473         if (vfsops->vfs_vget == NULL)
474                 /* convert an inode number to a vnode */
475                 vfsops->vfs_vget =      vfs_stdvget;
476         if (vfsops->vfs_fhtovp == NULL)
477                 /* turn an NFS file handle into a vnode */
478                 vfsops->vfs_fhtovp =    vfs_stdfhtovp;
479         if (vfsops->vfs_checkexp == NULL)
480                 /* check if file system is exported */
481                 vfsops->vfs_checkexp =  vfs_stdcheckexp;
482         if (vfsops->vfs_init == NULL)
483                 /* file system specific initialisation */
484                 vfsops->vfs_init =      vfs_stdinit;
485         if (vfsops->vfs_uninit == NULL)
486                 /* file system specific uninitialisation */
487                 vfsops->vfs_uninit =    vfs_stduninit;
488         if (vfsops->vfs_extattrctl == NULL)
489                 /* extended attribute control */
490                 vfsops->vfs_extattrctl = vfs_stdextattrctl;
491         if (vfsops->vfs_sysctl == NULL)
492                 vfsops->vfs_sysctl = vfs_stdsysctl;
493         if (vfsops->vfs_report_lockf == NULL)
494                 vfsops->vfs_report_lockf = vfs_report_lockf;
495
496         if ((vfc->vfc_flags & VFCF_SBDRY) != 0) {
497                 vfc->vfc_vfsops_sd = vfc->vfc_vfsops;
498                 vfc->vfc_vfsops = &vfsops_sigdefer;
499         }
500
501         if (vfc->vfc_flags & VFCF_JAIL)
502                 prison_add_vfs(vfc);
503
504         /*
505          * Call init function for this VFS...
506          */
507         if ((vfc->vfc_flags & VFCF_SBDRY) != 0)
508                 vfc->vfc_vfsops_sd->vfs_init(vfc);
509         else
510                 vfc->vfc_vfsops->vfs_init(vfc);
511         vfsconf_unlock();
512
513         /*
514          * If this filesystem has a sysctl node under vfs
515          * (i.e. vfs.xxfs), then change the oid number of that node to
516          * match the filesystem's type number.  This allows user code
517          * which uses the type number to read sysctl variables defined
518          * by the filesystem to continue working. Since the oids are
519          * in a sorted list, we need to make sure the order is
520          * preserved by re-registering the oid after modifying its
521          * number.
522          */
523         sysctl_wlock();
524         SLIST_FOREACH(oidp, SYSCTL_CHILDREN(&sysctl___vfs), oid_link) {
525                 if (strcmp(oidp->oid_name, vfc->vfc_name) == 0) {
526                         sysctl_unregister_oid(oidp);
527                         oidp->oid_number = vfc->vfc_typenum;
528                         sysctl_register_oid(oidp);
529                         break;
530                 }
531         }
532         sysctl_wunlock();
533
534         return (0);
535 }
536
537 /* Remove registration of a filesystem type */
538 static int
539 vfs_unregister(struct vfsconf *vfc)
540 {
541         struct vfsconf *vfsp;
542         int error, maxtypenum;
543
544         vfsconf_lock();
545         vfsp = vfs_byname_locked(vfc->vfc_name);
546         if (vfsp == NULL) {
547                 vfsconf_unlock();
548                 return (EINVAL);
549         }
550         if (vfsp->vfc_refcount != 0) {
551                 vfsconf_unlock();
552                 return (EBUSY);
553         }
554         error = 0;
555         if ((vfc->vfc_flags & VFCF_SBDRY) != 0) {
556                 if (vfc->vfc_vfsops_sd->vfs_uninit != NULL)
557                         error = vfc->vfc_vfsops_sd->vfs_uninit(vfsp);
558         } else {
559                 if (vfc->vfc_vfsops->vfs_uninit != NULL)
560                         error = vfc->vfc_vfsops->vfs_uninit(vfsp);
561         }
562         if (error != 0) {
563                 vfsconf_unlock();
564                 return (error);
565         }
566         TAILQ_REMOVE(&vfsconf, vfsp, vfc_list);
567         maxtypenum = VFS_GENERIC;
568         TAILQ_FOREACH(vfsp, &vfsconf, vfc_list)
569                 if (maxtypenum < vfsp->vfc_typenum)
570                         maxtypenum = vfsp->vfc_typenum;
571         maxvfsconf = maxtypenum + 1;
572         vfsconf_unlock();
573         return (0);
574 }
575
576 /*
577  * Standard kernel module handling code for filesystem modules.
578  * Referenced from VFS_SET().
579  */
580 int
581 vfs_modevent(module_t mod, int type, void *data)
582 {
583         struct vfsconf *vfc;
584         int error = 0;
585
586         vfc = (struct vfsconf *)data;
587
588         switch (type) {
589         case MOD_LOAD:
590                 if (vfc)
591                         error = vfs_register(vfc);
592                 break;
593
594         case MOD_UNLOAD:
595                 if (vfc)
596                         error = vfs_unregister(vfc);
597                 break;
598         default:
599                 error = EOPNOTSUPP;
600                 break;
601         }
602         return (error);
603 }