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