xref: /386bsd/usr/src/kernel/kern/fs/vfs.c (revision a2142627)
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, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * $Id: vfs.c,v 1.1 94/10/19 17:09:24 bill Exp Locker: bill $
34  *
35  * Virtual filesystem interface routines and implementation.
36  */
37 
38 #include "sys/param.h"
39 #include "sys/mount.h"
40 #include "sys/time.h"
41 #include "sys/file.h"
42 #include "sys/errno.h"
43 #include "sys/kinfo.h"
44 
45 #include "proc.h"
46 #include "uio.h"
47 #include "specdev.h"
48 #include "buf.h"
49 #include "malloc.h"
50 #include "modconfig.h"
51 
52 #include "vnode.h"
53 #include "namei.h"
54 
55 #include "prototypes.h"
56 
57 /*static*/ void insmntque(struct vnode *vp, struct mount *mp);
58 
59 /*
60  * Remove a mount point from the list of mounted filesystems.
61  * Unmount of the root is illegal.
62  */
63 void
64 vfs_remove(struct mount *mp)
65 {
66 
67 	if (mp == rootfs)
68 		panic("vfs_remove: unmounting root");
69 	mp->mnt_prev->mnt_next = mp->mnt_next;
70 	mp->mnt_next->mnt_prev = mp->mnt_prev;
71 	mp->mnt_vnodecovered->v_mountedhere = (struct mount *)0;
72 	vfs_unlock(mp);
73 }
74 
75 /*
76  * Lock a filesystem.
77  * Used to prevent access to it while mounting and unmounting.
78  */
79 int
80 vfs_lock(struct mount *mp)
81 {
82 
83 	while(mp->mnt_flag & MNT_MLOCK) {
84 		mp->mnt_flag |= MNT_MWAIT;
85 		tsleep((caddr_t)mp, PVFS, "vfslock", 0);
86 	}
87 	mp->mnt_flag |= MNT_MLOCK;
88 	return (0);
89 }
90 
91 /*
92  * Unlock a locked filesystem.
93  * Panic if filesystem is not locked.
94  */
95 void
96 vfs_unlock(struct mount *mp)
97 {
98 
99 	if ((mp->mnt_flag & MNT_MLOCK) == 0)
100 		panic("vfs_unlock: not locked");
101 	mp->mnt_flag &= ~MNT_MLOCK;
102 	if (mp->mnt_flag & MNT_MWAIT) {
103 		mp->mnt_flag &= ~MNT_MWAIT;
104 		wakeup((caddr_t)mp);
105 	}
106 }
107 
108 /*
109  * Mark a mount point as busy.
110  * Used to synchronize access and to delay unmounting.
111  */
112 int
113 vfs_busy(struct mount *mp)
114 {
115 
116 	while(mp->mnt_flag & MNT_MPBUSY) {
117 		mp->mnt_flag |= MNT_MPWANT;
118 		tsleep((caddr_t)&mp->mnt_flag, PVFS, "vfsbusy", 0);
119 	}
120 	if (mp->mnt_flag & MNT_UNMOUNT)
121 		return (1);
122 	mp->mnt_flag |= MNT_MPBUSY;
123 	return (0);
124 }
125 
126 /*
127  * Free a busy filesystem.
128  * Panic if filesystem is not busy.
129  */
130 void
131 vfs_unbusy(struct mount *mp)
132 {
133 
134 	if ((mp->mnt_flag & MNT_MPBUSY) == 0)
135 		panic("vfs_unbusy: not busy");
136 	mp->mnt_flag &= ~MNT_MPBUSY;
137 	if (mp->mnt_flag & MNT_MPWANT) {
138 		mp->mnt_flag &= ~MNT_MPWANT;
139 		wakeup((caddr_t)&mp->mnt_flag);
140 	}
141 }
142 
143 /*
144  * Lookup a mount point by filesystem identifier.
145  */
146 struct mount *
147 getvfs(fsid_t *fsid)
148 {
149 	struct mount *mp;
150 
151 	mp = rootfs;
152 	do {
153 		if (mp->mnt_stat.f_fsid.val[0] == fsid->val[0] &&
154 		    mp->mnt_stat.f_fsid.val[1] == fsid->val[1]) {
155 			return (mp);
156 		}
157 		mp = mp->mnt_next;
158 	} while (mp != rootfs);
159 	return ((struct mount *)0);
160 }
161 
162 /* add a filesystem type to the system. */
163 void
164 addvfs(struct vfsops *vfsp) {
165 	struct vfsops *ovfsp = vfs;
166 
167 	vfsp->vfs_next = ovfsp;
168 	vfs = vfsp;
169 }
170 
171 /* find a filesystem type in the system. */
172 struct vfsops *
173 findvfs(int type) {
174 	struct vfsops *vfsp;
175 
176 	/* search list */
177 	for(vfsp = vfs; vfsp ; vfsp = vfsp->vfs_next)
178 		if (vfsp->vfs_type == type)
179 			return (vfsp);
180 	return (0);
181 }
182 
183 /* compose a partially implemented filesystem with parts from another */
184 void
185 incorporatefs(struct vfsops *srcfs, struct vfsops *dstfs) {
186 
187 	if (dstfs->vfs_mount == 0)
188 		dstfs->vfs_mount = srcfs->vfs_mount;
189 	if (dstfs->vfs_start == 0)
190 		dstfs->vfs_start = srcfs->vfs_start;
191 	if (dstfs->vfs_unmount == 0)
192 		dstfs->vfs_unmount = srcfs->vfs_unmount;
193 	if (dstfs->vfs_root == 0)
194 		dstfs->vfs_root = srcfs->vfs_root;
195 	if (dstfs->vfs_quotactl == 0)
196 		dstfs->vfs_quotactl = srcfs->vfs_quotactl;
197 	if (dstfs->vfs_statfs == 0)
198 		dstfs->vfs_statfs = srcfs->vfs_statfs;
199 	if (dstfs->vfs_sync == 0)
200 		dstfs->vfs_sync = srcfs->vfs_sync;
201 	if (dstfs->vfs_fhtovp == 0)
202 		dstfs->vfs_fhtovp = srcfs->vfs_fhtovp;
203 	if (dstfs->vfs_vptofh == 0)
204 		dstfs->vfs_vptofh = srcfs->vfs_vptofh;
205 	if (dstfs->vfs_init == 0)
206 		dstfs->vfs_init = srcfs->vfs_init;
207 	if (dstfs->vfs_mountroot == 0)
208 		dstfs->vfs_mountroot = srcfs->vfs_mountroot;
209 }
210 
211 /*
212  * Initialize the vnode structures and initialize each file system type.
213  */
214 void
215 vfsinit(void)
216 {
217 	struct vfsops *vfsp;
218 
219 	/* initialize the name cache */
220 	nchinit();
221 
222 	/* initalize the vnode layer */
223 	vattr_null(&va_null);
224 
225 	/* initialize each filesystem */
226 	for (vfsp = vfs; vfsp ; vfsp = vfsp->vfs_next)
227 		(*vfsp->vfs_init)();
228 }
229 
230 /*
231  * Move a vnode from one mount queue to another.
232  */
233 /*static*/ void
234 insmntque(struct vnode *vp, struct mount *mp)
235 {
236 	struct vnode *vq;
237 
238 	/*
239 	 * Delete from old mount point vnode list, if on one.
240 	 */
241 	if (vp->v_mountb) {
242 		if (vq = vp->v_mountf)
243 			vq->v_mountb = vp->v_mountb;
244 		*vp->v_mountb = vq;
245 	}
246 	/*
247 	 * Insert into list of vnodes for the new mount point, if available.
248 	 */
249 	vp->v_mount = mp;
250 	if (mp == NULL) {
251 		vp->v_mountf = NULL;
252 		vp->v_mountb = NULL;
253 		return;
254 	}
255 	if (vq = mp->mnt_mounth)
256 		vq->v_mountb = &vp->v_mountf;
257 	vp->v_mountf = vq;
258 	vp->v_mountb = &mp->mnt_mounth;
259 	mp->mnt_mounth = vp;
260 }
261 
262 /*
263  * Make sure all write-behind blocks associated
264  * with mount point are flushed out (from sync).
265  */
266 void
267 mntflushbuf(struct mount *mountp, int flags)
268 {
269 	struct vnode *vp;
270 
271 	if ((mountp->mnt_flag & MNT_MPBUSY) == 0)
272 		panic("mntflushbuf: not busy");
273 loop:
274 	for (vp = mountp->mnt_mounth; vp; vp = vp->v_mountf) {
275 		if (VOP_ISLOCKED(vp))
276 			continue;
277 		if (vget(vp))
278 			goto loop;
279 		vflushbuf(vp, flags, 0);
280 		vput(vp);
281 		if (vp->v_mount != mountp)
282 			goto loop;
283 	}
284 }
285 
286 /*
287  * Invalidate in core blocks belonging to closed or unmounted filesystem
288  *
289  * Go through the list of vnodes associated with the file system;
290  * for each vnode invalidate any buffers that it holds. Normally
291  * this routine is preceeded by a bflush call, so that on a quiescent
292  * filesystem there will be no dirty buffers when we are done. Binval
293  * returns the count of dirty buffers when it is finished.
294  */
295 int
296 mntinvalbuf(struct mount *mountp)
297 {
298 	register struct vnode *vp;
299 	int dirty = 0;
300 
301 	if ((mountp->mnt_flag & MNT_MPBUSY) == 0)
302 		panic("mntinvalbuf: not busy");
303 loop:
304 	for (vp = mountp->mnt_mounth; vp; vp = vp->v_mountf) {
305 		if (vget(vp))
306 			goto loop;
307 		dirty += vinvalbuf(vp, 1);
308 		vput(vp);
309 		if (vp->v_mount != mountp)
310 			goto loop;
311 	}
312 	return (dirty);
313 }
314 
315 /*
316  * Remove any vnodes in the vnode table belonging to mount point mp.
317  *
318  * If MNT_NOFORCE is specified, there should not be any active ones,
319  * return error if any are found (nb: this is a user error, not a
320  * system error). If MNT_FORCE is specified, detach any active vnodes
321  * that are found.
322  */
323 #ifdef	DEBUG
324 int busyprt = 0;	/* patch to print out busy vnodes */
325 #endif
326 
327 int
328 vflush(struct mount *mp, struct vnode *skipvp, int flags)
329 {
330 	register struct vnode *vp, *nvp;
331 	int busy = 0;
332 
333 	if ((mp->mnt_flag & MNT_MPBUSY) == 0)
334 		panic("vflush: not busy");
335 loop:
336 	for (vp = mp->mnt_mounth; vp; vp = nvp) {
337 		if (vp->v_mount != mp)
338 			goto loop;
339 		nvp = vp->v_mountf;
340 		/*
341 		 * Skip over a selected vnode.
342 		 */
343 		if (vp == skipvp)
344 			continue;
345 		/*
346 		 * Skip over a vnodes marked VSYSTEM.
347 		 */
348 		if ((flags & SKIPSYSTEM) && (vp->v_flag & VSYSTEM))
349 			continue;
350 		/*
351 		 * With v_usecount == 0, all we need to do is clear
352 		 * out the vnode data structures and we are done.
353 		 */
354 		if (vp->v_usecount == 0) {
355 			vgone(vp);
356 			continue;
357 		}
358 		/*
359 		 * For block or character devices, revert to an
360 		 * anonymous device. For all other files, just kill them.
361 		 */
362 		if (flags & FORCECLOSE) {
363 			if (vp->v_type != VBLK && vp->v_type != VCHR) {
364 				vgone(vp);
365 			} else {
366 				spec_anonymous(vp);
367 				/*vclean(vp, 0);
368 				vp->v_op = &spec_vnodeops;
369 				insmntque(vp, (struct mount *)0);*/
370 			}
371 			continue;
372 		}
373 #ifdef DEBUG
374 		if (busyprt)
375 			vprint("vflush: busy vnode", vp);
376 #endif
377 		busy++;
378 	}
379 	if (busy)
380 		return (EBUSY);
381 	return (0);
382 }
383