1 /*
2  * Copyright (c) 1992, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  * All rights reserved.
5  *
6  * This code is derived from software donated to Berkeley by
7  * Jan-Simon Pendry.
8  *
9  * %sccs.include.redist.c%
10  *
11  *	@(#)lofs_subr.c	8.4 (Berkeley) 01/05/94
12  *
13  * $Id: lofs_subr.c,v 1.11 1992/05/30 10:05:43 jsp Exp jsp $
14  */
15 
16 #include <sys/param.h>
17 #include <sys/systm.h>
18 #include <sys/time.h>
19 #include <sys/types.h>
20 #include <sys/vnode.h>
21 #include <sys/mount.h>
22 #include <sys/namei.h>
23 #include <sys/malloc.h>
24 #include <miscfs/lofs/lofs.h>
25 
26 #define LOG2_SIZEVNODE 7		/* log2(sizeof struct vnode) */
27 #define	NLOFSCACHE 16
28 #define	LOFS_NHASH(vp) ((((u_long)vp)>>LOG2_SIZEVNODE) & (NLOFSCACHE-1))
29 
30 /*
31  * Loopback cache:
32  * Each cache entry holds a reference to the target vnode
33  * along with a pointer to the alias vnode.  When an
34  * entry is added the target vnode is VREF'd.  When the
35  * alias is removed the target vnode is vrele'd.
36  */
37 
38 /*
39  * Cache head
40  */
41 struct lofscache {
42 	struct lofsnode	*ac_forw;
43 	struct lofsnode	*ac_back;
44 };
45 
46 static struct lofscache lofscache[NLOFSCACHE];
47 
48 /*
49  * Initialise cache headers
50  */
51 int
52 lofs_init()
53 {
54 	struct lofscache *ac;
55 
56 	for (ac = lofscache; ac < lofscache + NLOFSCACHE; ac++)
57 		ac->ac_forw = ac->ac_back = (struct lofsnode *) ac;
58 
59 	return (0);
60 }
61 
62 /*
63  * Compute hash list for given target vnode
64  */
65 static struct lofscache *
66 lofs_hash(targetvp)
67 struct vnode *targetvp;
68 {
69 
70 	return (&lofscache[LOFS_NHASH(targetvp)]);
71 }
72 
73 /*
74  * Make a new lofsnode node.
75  * Vp is the alias vnode, lofsvp is the target vnode.
76  * Maintain a reference to (targetvp).
77  */
78 static void
79 lofs_alloc(vp, targetvp)
80 	struct vnode *vp;
81 	struct vnode *targetvp;
82 {
83 	struct lofscache *hd;
84 	struct lofsnode *a;
85 
86 	MALLOC(a, struct lofsnode *, sizeof(struct lofsnode), M_TEMP, M_WAITOK);
87 	a->a_vnode = vp;
88 	vp->v_data = a;
89 	VREF(targetvp);
90 	a->a_lofsvp = targetvp;
91 	hd = lofs_hash(targetvp);
92 	insque(a, hd);
93 
94 }
95 
96 
97 /*
98  * Return alias for target vnode if already exists, else 0.
99  */
100 static struct lofsnode *
101 lofs_find(mp, targetvp)
102 	struct mount *mp;
103 	struct vnode *targetvp;
104 {
105 	struct lofscache *hd;
106 	struct lofsnode *a;
107 
108 	/*
109 	 * Find hash base, and then search the (two-way) linked
110 	 * list looking for a lofsnode structure which is referencing
111 	 * the target vnode.  If found, the increment the lofsnode
112 	 * reference count (but NOT the target vnode's VREF counter).
113 	 */
114 	hd = lofs_hash(targetvp);
115 
116 	for (a = hd->ac_forw; a != (struct lofsnode *) hd; a = a->a_forw) {
117 		if (a->a_lofsvp == targetvp && a->a_vnode->v_mount == mp) {
118 			return (a);
119 		}
120 	}
121 
122 	return (0);
123 }
124 
125 static int
126 lofs_alias(mp, targetvp, newvpp)
127 	struct mount *mp;
128 	struct vnode *targetvp;
129 	struct vnode **newvpp;
130 {
131 	struct lofsnode *ap;
132 	struct vnode *aliasvp;
133 
134 	if (targetvp->v_type != VDIR || targetvp->v_op == lofs_vnodeop_p) {
135 		*newvpp = targetvp;
136 		return (0);
137 	}
138 
139 	ap = lofs_find(mp, targetvp);
140 
141 	if (ap) {
142 		/*
143 		 * Take another reference to the alias vnode
144 		 */
145 		aliasvp = ap->a_vnode;
146 		VREF(aliasvp);
147 	} else {
148 		int error;
149 
150 		/*
151 		 * Get new vnode.
152 		 */
153 		if (error = getnewvnode(VT_LOFS, mp, lofs_vnodeop_p, &aliasvp))
154 			return (error);
155 
156 		/*
157 		 * Must be a directory
158 		 */
159 		aliasvp->v_type = VDIR;
160 
161 		/*
162 		 * Make new vnode reference the lofsnode.
163 		 */
164 		lofs_alloc(aliasvp, targetvp);
165 
166 		/*
167 		 * aliasvp is already VREF'd by getnewvnode()
168 		 */
169 	}
170 
171 	vrele(targetvp);
172 
173 	*newvpp = aliasvp;
174 	return (0);
175 }
176 
177 /*
178  * Try to find an existing lofsnode vnode refering
179  * to it, otherwise make a new lofsnode vnode which
180  * contains a reference to the target vnode.
181  */
182 int
183 make_lofs(mp, vpp)
184 	struct mount *mp;
185 	struct vnode **vpp;
186 {
187 	struct vnode *targetvp;
188 
189 	/*
190 	 * (targetvp) is locked at this point.
191 	 */
192 	targetvp = *vpp;
193 
194 	/*
195 	 * Try to find an existing reference to the target vnodes.
196 	 */
197 	return (lofs_alias(mp, targetvp, vpp));
198 }
199 
200