xref: /csrg-svn/sys/miscfs/union/union_subr.c (revision 65965)
1 /*
2  * Copyright (c) 1994 Jan-Simon Pendry
3  * Copyright (c) 1994
4  *	The Regents of the University of California.  All rights reserved.
5  *
6  * This code is derived from software contributed to Berkeley by
7  * Jan-Simon Pendry.
8  *
9  * %sccs.include.redist.c%
10  *
11  *	@(#)union_subr.c	1.3 (Berkeley) 02/01/94
12  */
13 
14 #include <sys/param.h>
15 #include <sys/systm.h>
16 #include <sys/time.h>
17 #include <sys/kernel.h>
18 #include <sys/vnode.h>
19 #include <sys/namei.h>
20 #include <sys/malloc.h>
21 #include "union.h" /*<miscfs/union/union.h>*/
22 
23 static struct union_node *unhead;
24 static int unvplock;
25 
26 int
27 union_init()
28 {
29 
30 	unhead = 0;
31 	unvplock = 0;
32 }
33 
34 /*
35  * allocate a union_node/vnode pair.  the vnode is
36  * referenced and locked.  the new vnode is returned
37  * via (vpp).  (mp) is the mountpoint of the union filesystem,
38  * (dvp) is the parent directory where the upper layer object
39  * should exist (but doesn't) and (cnp) is the componentname
40  * information which is partially copied to allow the upper
41  * layer object to be created at a later time.  (uppervp)
42  * and (lowervp) reference the upper and lower layer objects
43  * being mapped.  either, but not both, can be nil.
44  *
45  * all union_nodes are maintained on a singly-linked
46  * list.  new nodes are only allocated when they cannot
47  * be found on this list.  entries on the list are
48  * removed when the vfs reclaim entry is called.
49  *
50  * a single lock is kept for the entire list.  this is
51  * needed because the getnewvnode() function can block
52  * waiting for a vnode to become free, in which case there
53  * may be more than one process trying to get the same
54  * vnode.  this lock is only taken if we are going to
55  * call getnewvnode, since the kernel itself is single-threaded.
56  *
57  * if an entry is found on the list, then call vget() to
58  * take a reference.  this is done because there may be
59  * zero references to it and so it needs to removed from
60  * the vnode free list.
61  */
62 int
63 union_allocvp(vpp, mp, dvp, cnp, uppervp, lowervp)
64 	struct vnode **vpp;
65 	struct mount *mp;
66 	struct vnode *dvp;		/* may be null */
67 	struct componentname *cnp;	/* may be null */
68 	struct vnode *uppervp;		/* may be null */
69 	struct vnode *lowervp;		/* may be null */
70 {
71 	int error;
72 	struct union_node *un;
73 	struct union_node **pp;
74 	struct vnode *xlowervp = 0;
75 
76 	if (uppervp == 0 && lowervp == 0)
77 		panic("union: unidentifiable allocation");
78 
79 	if (uppervp && lowervp && (uppervp->v_type != lowervp->v_type)) {
80 		xlowervp = lowervp;
81 		lowervp = 0;
82 	}
83 
84 loop:
85 	for (un = unhead; un != 0; un = un->un_next) {
86 		if ((un->un_lowervp == lowervp ||
87 		     un->un_lowervp == 0) &&
88 		    (un->un_uppervp == uppervp ||
89 		     un->un_uppervp == 0) &&
90 		    (UNIONTOV(un)->v_mount == mp)) {
91 			if (vget(un->un_vnode, 1))
92 				goto loop;
93 			un->un_uppervp = uppervp;
94 			if ((lowervp == 0) && un->un_lowervp)
95 				vrele(un->un_lowervp);
96 			un->un_lowervp = lowervp;
97 			*vpp = un->un_vnode;
98 			return (0);
99 		}
100 	}
101 
102 	/*
103 	 * otherwise lock the vp list while we call getnewvnode
104 	 * since that can block.
105 	 */
106 	if (unvplock & UN_LOCKED) {
107 		unvplock |= UN_WANT;
108 		sleep((caddr_t) &unvplock, PINOD);
109 		goto loop;
110 	}
111 	unvplock |= UN_LOCKED;
112 
113 	error = getnewvnode(VT_UNION, mp, union_vnodeop_p, vpp);
114 	if (error)
115 		goto out;
116 
117 	MALLOC((*vpp)->v_data, void *, sizeof(struct union_node),
118 		M_TEMP, M_WAITOK);
119 
120 	if (uppervp)
121 		(*vpp)->v_type = uppervp->v_type;
122 	else
123 		(*vpp)->v_type = lowervp->v_type;
124 	un = VTOUNION(*vpp);
125 	un->un_next = 0;
126 	un->un_uppervp = uppervp;
127 	un->un_lowervp = lowervp;
128 	un->un_vnode = *vpp;
129 	un->un_flags = 0;
130 	if (uppervp == 0 && cnp) {
131 		un->un_path = malloc(cnp->cn_namelen+1, M_TEMP, M_WAITOK);
132 		bcopy(cnp->cn_nameptr, un->un_path, cnp->cn_namelen);
133 		un->un_path[cnp->cn_namelen] = '\0';
134 		VREF(dvp);
135 		un->un_dirvp = dvp;
136 	} else {
137 		un->un_path = 0;
138 		un->un_dirvp = 0;
139 	}
140 
141 #ifdef DIAGNOSTIC
142 	un->un_pid = 0;
143 #endif
144 
145 	/* add to union vnode list */
146 	for (pp = &unhead; *pp; pp = &(*pp)->un_next)
147 		continue;
148 	*pp = un;
149 
150 	if (un)
151 		un->un_flags |= UN_LOCKED;
152 
153 	if (xlowervp)
154 		vrele(xlowervp);
155 
156 out:
157 	unvplock &= ~UN_LOCKED;
158 
159 	if (unvplock & UN_WANT) {
160 		unvplock &= ~UN_WANT;
161 		wakeup((caddr_t) &unvplock);
162 	}
163 
164 	return (error);
165 }
166 
167 int
168 union_freevp(vp)
169 	struct vnode *vp;
170 {
171 	struct union_node **unpp;
172 	struct union_node *un = VTOUNION(vp);
173 
174 	for (unpp = &unhead; *unpp != 0; unpp = &(*unpp)->un_next) {
175 		if (*unpp == un) {
176 			*unpp = un->un_next;
177 			break;
178 		}
179 	}
180 
181 	if (un->un_path)
182 		FREE(un->un_path, M_TEMP);
183 
184 	FREE(vp->v_data, M_TEMP);
185 	vp->v_data = 0;
186 	return (0);
187 }
188