lint
[unix-history] / usr / src / sys / kern / vfs_subr.c
CommitLineData
3c4390e8
KM
1/*
2 * Copyright (c) 1989 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms are permitted
6 * provided that the above copyright notice and this paragraph are
7 * duplicated in all such forms and that any documentation,
8 * advertising materials, and other materials related to such
9 * distribution and use acknowledge that the software was developed
10 * by the University of California, Berkeley. The name of the
11 * University may not be used to endorse or promote products derived
12 * from this software without specific prior written permission.
13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16 *
2bae1875 17 * @(#)vfs_subr.c 7.11 (Berkeley) %G%
3c4390e8
KM
18 */
19
20/*
21 * External virtual filesystem routines
22 */
23
24#include "param.h"
25#include "mount.h"
26#include "time.h"
27#include "vnode.h"
c60798ca
KM
28#include "namei.h"
29#include "ucred.h"
3c4390e8 30#include "errno.h"
ef24f6dd 31#include "malloc.h"
3c4390e8 32
3c4390e8
KM
33/*
34 * Remove a mount point from the list of mounted filesystems.
35 * Unmount of the root is illegal.
36 */
37void
38vfs_remove(mp)
39 register struct mount *mp;
40{
41
42 if (mp == rootfs)
43 panic("vfs_remove: unmounting root");
44 mp->m_prev->m_next = mp->m_next;
45 mp->m_next->m_prev = mp->m_prev;
46 mp->m_vnodecovered->v_mountedhere = (struct mount *)0;
47 vfs_unlock(mp);
48}
49
50/*
51 * Lock a filesystem.
52 * Used to prevent access to it while mounting and unmounting.
53 */
54vfs_lock(mp)
55 register struct mount *mp;
56{
57
594501df
KM
58 while(mp->m_flag & M_MLOCK) {
59 mp->m_flag |= M_MWAIT;
60 sleep((caddr_t)mp, PVFS);
61 }
3c4390e8
KM
62 mp->m_flag |= M_MLOCK;
63 return (0);
64}
65
66/*
67 * Unlock a locked filesystem.
68 * Panic if filesystem is not locked.
69 */
70void
71vfs_unlock(mp)
72 register struct mount *mp;
73{
74
75 if ((mp->m_flag & M_MLOCK) == 0)
76 panic("vfs_unlock: locked fs");
77 mp->m_flag &= ~M_MLOCK;
78 if (mp->m_flag & M_MWAIT) {
79 mp->m_flag &= ~M_MWAIT;
80 wakeup((caddr_t)mp);
81 }
82}
83
84/*
85 * Lookup a mount point by filesystem identifier.
86 */
87struct mount *
88getvfs(fsid)
89 fsid_t *fsid;
90{
91 register struct mount *mp;
92
d713f801
KM
93 mp = rootfs;
94 do {
3c4390e8
KM
95 if (mp->m_fsid.val[0] == fsid->val[0] &&
96 mp->m_fsid.val[1] == fsid->val[1]) {
d713f801 97 return (mp);
3c4390e8 98 }
d713f801
KM
99 mp = mp->m_next;
100 } while (mp != rootfs);
101 return ((struct mount *)0);
3c4390e8
KM
102}
103
104/*
105 * Set vnode attributes to VNOVAL
106 */
107void vattr_null(vap)
108 register struct vattr *vap;
109{
110
111 vap->va_type = VNON;
112 vap->va_mode = vap->va_nlink = vap->va_uid = vap->va_gid =
113 vap->va_fsid = vap->va_fileid = vap->va_size =
114 vap->va_size1 = vap->va_blocksize = vap->va_rdev =
115 vap->va_bytes = vap->va_bytes1 =
116 vap->va_atime.tv_sec = vap->va_atime.tv_usec =
117 vap->va_mtime.tv_sec = vap->va_mtime.tv_usec =
8cf4d4fb
KM
118 vap->va_ctime.tv_sec = vap->va_ctime.tv_usec =
119 vap->va_flags = vap->va_gen = VNOVAL;
3c4390e8 120}
c60798ca
KM
121
122/*
123 * Initialize a nameidata structure
124 */
125ndinit(ndp)
126 register struct nameidata *ndp;
127{
128
129 bzero((caddr_t)ndp, sizeof(struct nameidata));
130 ndp->ni_iov = &ndp->ni_nd.nd_iovec;
131 ndp->ni_iovcnt = 1;
132 ndp->ni_base = (caddr_t)&ndp->ni_dent;
133 ndp->ni_rw = UIO_WRITE;
134 ndp->ni_segflg = UIO_SYSSPACE;
135}
136
137/*
138 * Duplicate a nameidata structure
139 */
140nddup(ndp, newndp)
141 register struct nameidata *ndp, *newndp;
142{
143
144 ndinit(newndp);
145 newndp->ni_cdir = ndp->ni_cdir;
8fe1c702 146 VREF(newndp->ni_cdir);
c60798ca
KM
147 newndp->ni_rdir = ndp->ni_rdir;
148 if (newndp->ni_rdir)
8fe1c702 149 VREF(newndp->ni_rdir);
c60798ca
KM
150 newndp->ni_cred = ndp->ni_cred;
151 crhold(newndp->ni_cred);
152}
153
154/*
155 * Release a nameidata structure
156 */
157ndrele(ndp)
158 register struct nameidata *ndp;
159{
160
161 vrele(ndp->ni_cdir);
162 if (ndp->ni_rdir)
163 vrele(ndp->ni_rdir);
164 crfree(ndp->ni_cred);
165}
36d09cb1
KM
166
167/*
168 * Routines having to do with the management of the vnode table.
169 */
170struct vnode *vfreeh, **vfreet;
4ef5d036 171extern struct vnodeops dead_vnodeops, spec_vnodeops;
ef24f6dd
KM
172struct speclist *speclisth;
173struct speclist {
174 struct speclist *sl_next;
175 struct vnode *sl_vp;
176};
36d09cb1
KM
177
178/*
ef24f6dd 179 * Initialize the vnode structures and initialize each file system type.
36d09cb1 180 */
ef24f6dd 181vfsinit()
36d09cb1
KM
182{
183 register struct vnode *vp = vnode;
ef24f6dd 184 struct vfsops **vfsp;
36d09cb1 185
ef24f6dd
KM
186 /*
187 * Build vnode free list.
188 */
36d09cb1
KM
189 vfreeh = vp;
190 vfreet = &vp->v_freef;
191 vp->v_freeb = &vfreeh;
192 vp->v_op = &dead_vnodeops;
193 for (vp++; vp < vnodeNVNODE; vp++) {
194 *vfreet = vp;
195 vp->v_freeb = vfreet;
196 vfreet = &vp->v_freef;
197 vp->v_op = &dead_vnodeops;
198 }
199 vp--;
200 vp->v_freef = NULL;
ef24f6dd
KM
201 /*
202 * Initialize the vnode name cache
203 */
204 nchinit();
205 /*
206 * Initialize each file system type.
207 */
208 for (vfsp = &vfssw[0]; vfsp <= &vfssw[MOUNT_MAXTYPE]; vfsp++) {
209 if (*vfsp == NULL)
210 continue;
211 (*(*vfsp)->vfs_init)();
212 }
36d09cb1
KM
213}
214
215/*
216 * Return the next vnode from the free list.
217 */
218getnewvnode(tag, mp, vops, vpp)
219 enum vtagtype tag;
220 struct mount *mp;
221 struct vnodeops *vops;
222 struct vnode **vpp;
223{
224 register struct vnode *vp, *vq;
225
226 if ((vp = vfreeh) == NULL) {
227 tablefull("vnode");
228 *vpp = 0;
229 return (ENFILE);
230 }
ef24f6dd 231 if (vp->v_count)
36d09cb1
KM
232 panic("free vnode isn't");
233 if (vq = vp->v_freef)
234 vq->v_freeb = &vfreeh;
235 vfreeh = vq;
236 vp->v_freef = NULL;
237 vp->v_freeb = NULL;
ef24f6dd
KM
238 if (vp->v_type != VNON)
239 vgone(vp);
36d09cb1
KM
240 vp->v_flag = 0;
241 vp->v_shlockc = 0;
242 vp->v_exlockc = 0;
243 vp->v_socket = 0;
36d09cb1
KM
244 cache_purge(vp);
245 vp->v_tag = tag;
ef24f6dd 246 vp->v_op = vops;
36d09cb1
KM
247 vp->v_mount = mp;
248 insmntque(vp, mp);
249 VREF(vp);
250 *vpp = vp;
251 return (0);
252}
253
254/*
255 * Move a vnode from one mount queue to another.
256 */
257insmntque(vp, mp)
258 register struct vnode *vp;
259 register struct mount *mp;
260{
261 struct vnode *vq;
262
263 /*
264 * Delete from old mount point vnode list, if on one.
265 */
266 if (vp->v_mountb) {
267 if (vq = vp->v_mountf)
268 vq->v_mountb = vp->v_mountb;
269 *vp->v_mountb = vq;
270 }
271 /*
272 * Insert into list of vnodes for the new mount point, if available.
273 */
274 if (mp == NULL) {
275 vp->v_mountf = NULL;
276 vp->v_mountb = NULL;
277 return;
278 }
279 if (mp->m_mounth) {
280 vp->v_mountf = mp->m_mounth;
281 vp->v_mountb = &mp->m_mounth;
282 mp->m_mounth->v_mountb = &vp->v_mountf;
283 mp->m_mounth = vp;
284 } else {
285 mp->m_mounth = vp;
286 vp->v_mountb = &mp->m_mounth;
287 vp->v_mountf = NULL;
288 }
289}
290
291/*
ef24f6dd
KM
292 * Create a vnode for a block device.
293 * Used for root filesystem, argdev, and swap areas.
294 * Also used for memory file system special devices.
295 */
296bdevvp(dev, vpp)
297 dev_t dev;
298 struct vnode **vpp;
299{
300 register struct inode *ip;
301 register struct vnode *vp;
302 struct vnode *nvp;
303 int error;
304
4ef5d036 305 error = getnewvnode(VT_NON, (struct mount *)0, &spec_vnodeops, &nvp);
ef24f6dd
KM
306 if (error) {
307 *vpp = 0;
308 return (error);
309 }
310 vp = nvp;
311 vp->v_type = VBLK;
312 vp->v_rdev = dev;
313 if (nvp = checkalias(vp, (struct mount *)0)) {
314 vput(vp);
315 vp = nvp;
316 }
317 *vpp = vp;
318 return (0);
319}
320
321/*
322 * Check to see if the new vnode represents a special device
323 * for which we already have a vnode (either because of
324 * bdevvp() or because of a different vnode representing
325 * the same block device). If such an alias exists, deallocate
326 * the existing contents and return the aliased inode. The
327 * caller is responsible for filling it with its new contents.
328 */
329struct vnode *
330checkalias(nvp, mp)
331 register struct vnode *nvp;
332 struct mount *mp;
333{
334 register struct vnode *vp;
335 register struct speclist *slp;
336
337 if (nvp->v_type != VBLK && nvp->v_type != VCHR)
338 return ((struct vnode *)0);
339loop:
340 for (slp = speclisth; slp; slp = slp->sl_next) {
341 vp = slp->sl_vp;
342 if (nvp->v_rdev != vp->v_rdev ||
343 nvp->v_type != vp->v_type)
344 continue;
345 if (vget(vp))
346 goto loop;
347 break;
348 }
349 if (slp == NULL) {
350 MALLOC(slp, struct speclist *, sizeof(*slp), M_VNODE, M_WAITOK);
351 slp->sl_vp = nvp;
352 slp->sl_next = speclisth;
353 speclisth = slp;
354 return ((struct vnode *)0);
355 }
2bae1875
KM
356 VOP_UNLOCK(vp);
357 vclean(vp, 0);
ef24f6dd
KM
358 vp->v_op = nvp->v_op;
359 vp->v_tag = nvp->v_tag;
360 nvp->v_type = VNON;
361 insmntque(vp, mp);
362 return (vp);
363}
364
365/*
366 * Grab a particular vnode from the free list, increment its
367 * reference count and lock it. The vnode lock bit is set the
368 * vnode is being eliminated in vgone. The process is awakened
369 * when the transition is completed, and an error returned to
370 * indicate that the vnode is no longer usable (possibly having
371 * been changed to a new file system type).
36d09cb1
KM
372 */
373vget(vp)
374 register struct vnode *vp;
375{
376 register struct vnode *vq;
377
ef24f6dd
KM
378 if (vp->v_flag & VXLOCK) {
379 vp->v_flag |= VXWANT;
380 sleep((caddr_t)vp, PINOD);
381 return (1);
382 }
383 if (vp->v_count == 0) {
384 if (vq = vp->v_freef)
385 vq->v_freeb = vp->v_freeb;
386 else
387 vfreet = vp->v_freeb;
388 *vp->v_freeb = vq;
389 vp->v_freef = NULL;
390 vp->v_freeb = NULL;
391 }
36d09cb1 392 VREF(vp);
ef24f6dd
KM
393 VOP_LOCK(vp);
394 return (0);
36d09cb1
KM
395}
396
397/*
398 * Vnode reference, just increment the count
399 */
400void vref(vp)
401 struct vnode *vp;
402{
403
404 vp->v_count++;
405}
406
407/*
408 * vput(), just unlock and vrele()
409 */
410void vput(vp)
411 register struct vnode *vp;
412{
413 VOP_UNLOCK(vp);
414 vrele(vp);
415}
416
417/*
418 * Vnode release.
419 * If count drops to zero, call inactive routine and return to freelist.
420 */
421void vrele(vp)
422 register struct vnode *vp;
423{
424
425 if (vp == NULL)
ef24f6dd 426 panic("vrele: null vp");
36d09cb1
KM
427 vp->v_count--;
428 if (vp->v_count < 0)
429 printf("vnode bad ref count %d, type %d, tag %d\n",
430 vp->v_count, vp->v_type, vp->v_tag);
431 if (vp->v_count > 0)
432 return;
36d09cb1
KM
433 if (vfreeh == (struct vnode *)0) {
434 /*
435 * insert into empty list
436 */
437 vfreeh = vp;
438 vp->v_freeb = &vfreeh;
36d09cb1
KM
439 } else {
440 /*
441 * insert at tail of list
442 */
443 *vfreet = vp;
444 vp->v_freeb = vfreet;
36d09cb1 445 }
ef24f6dd
KM
446 vp->v_freef = NULL;
447 vfreet = &vp->v_freef;
448 VOP_INACTIVE(vp);
449}
450
451/*
452 * Disassociate the underlying file system from a vnode.
ef24f6dd 453 */
2bae1875 454void vclean(vp, doclose)
ef24f6dd 455 register struct vnode *vp;
2bae1875 456 long doclose;
ef24f6dd
KM
457{
458 struct vnodeops *origops;
2bae1875 459 int active;
ef24f6dd 460
2bae1875
KM
461 /*
462 * Check to see if the vnode is in use.
463 * If so we have to lock it before we clean it out.
464 */
465 if (active = vp->v_count) {
466 VREF(vp);
467 VOP_LOCK(vp);
468 }
469 /*
470 * Prevent the vnode from being recycled or
471 * brought into use while we clean it out.
472 */
ef24f6dd
KM
473 while (vp->v_flag & VXLOCK) {
474 vp->v_flag |= VXWANT;
475 sleep((caddr_t)vp, PINOD);
476 }
477 vp->v_flag |= VXLOCK;
478 /*
479 * Prevent any further operations on the vnode from
480 * being passed through to the old file system.
481 */
482 origops = vp->v_op;
483 vp->v_op = &dead_vnodeops;
484 vp->v_tag = VT_NON;
485 /*
2bae1875
KM
486 * If purging an active vnode, it must be unlocked, closed,
487 * and deactivated before being reclaimed.
ef24f6dd 488 */
2bae1875 489 if (active) {
ef24f6dd 490 (*(origops->vn_unlock))(vp);
2bae1875
KM
491 if (doclose)
492 (*(origops->vn_close))(vp, 0, NOCRED);
ef24f6dd
KM
493 (*(origops->vn_inactive))(vp);
494 }
495 /*
496 * Reclaim the vnode.
497 */
498 if ((*(origops->vn_reclaim))(vp))
499 panic("vclean: cannot reclaim");
2bae1875
KM
500 if (active)
501 vrele(vp);
ef24f6dd
KM
502 /*
503 * Done with purge, notify sleepers in vget of the grim news.
504 */
505 vp->v_flag &= ~VXLOCK;
506 if (vp->v_flag & VXWANT) {
507 vp->v_flag &= ~VXWANT;
508 wakeup((caddr_t)vp);
509 }
510}
511
512/*
513 * Eliminate all activity associated with a vnode
514 * in preparation for reuse.
515 */
516void vgone(vp)
517 register struct vnode *vp;
518{
519 register struct speclist *slp;
520 struct speclist *pslp;
521 register struct vnode *vq;
522
ef24f6dd
KM
523 /*
524 * Clean out the filesystem specific data.
525 */
2bae1875 526 vclean(vp, 1);
ef24f6dd
KM
527 /*
528 * Delete from old mount point vnode list, if on one.
529 */
530 if (vp->v_mountb) {
531 if (vq = vp->v_mountf)
532 vq->v_mountb = vp->v_mountb;
533 *vp->v_mountb = vq;
534 vp->v_mountf = NULL;
535 vp->v_mountb = NULL;
536 }
537 /*
538 * If special device, remove it from special device alias list.
539 */
540 if (vp->v_type == VBLK || vp->v_type == VCHR) {
541 if (speclisth->sl_vp == vp) {
542 slp = speclisth;
543 speclisth = slp->sl_next;
544 } else {
545 for (slp = speclisth; slp;
546 pslp = slp, slp = slp->sl_next) {
547 if (slp->sl_vp != vp)
548 continue;
549 pslp->sl_next = slp->sl_next;
550 break;
551 }
552 if (slp == NULL)
553 panic("missing bdev");
554 }
555 FREE(slp, M_VNODE);
556 }
557 /*
558 * If it is on the freelist, move it to the head of the list.
559 */
560 if (vp->v_freeb) {
561 if (vq = vp->v_freef)
562 vq->v_freeb = vp->v_freeb;
563 else
564 vfreet = vp->v_freeb;
565 *vp->v_freeb = vq;
566 vp->v_freef = vfreeh;
567 vp->v_freeb = &vfreeh;
568 vfreeh->v_freeb = &vp->v_freef;
569 vfreeh = vp;
570 }
2bae1875 571 vp->v_type = VBAD;
36d09cb1 572}