xref: /netbsd-src/sys/miscfs/umapfs/umap_subr.c (revision 2a399c6883d870daece976daec6ffa7bb7f934ce)
1 /*	$NetBSD: umap_subr.c,v 1.11 1997/09/10 13:44:28 christos Exp $	*/
2 
3 /*
4  * Copyright (c) 1992, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software donated to Berkeley by
8  * Jan-Simon Pendry.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  *
38  *	from: Id: lofs_subr.c, v 1.11 1992/05/30 10:05:43 jsp Exp
39  *	@(#)umap_subr.c	8.6 (Berkeley) 1/26/94
40  */
41 
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/time.h>
45 #include <sys/types.h>
46 #include <sys/vnode.h>
47 #include <sys/mount.h>
48 #include <sys/namei.h>
49 #include <sys/malloc.h>
50 #include <miscfs/specfs/specdev.h>
51 #include <miscfs/umapfs/umap.h>
52 
53 #define LOG2_SIZEVNODE 7		/* log2(sizeof struct vnode) */
54 #define	NUMAPNODECACHE 16
55 
56 /*
57  * Null layer cache:
58  * Each cache entry holds a reference to the target vnode
59  * along with a pointer to the alias vnode.  When an
60  * entry is added the target vnode is VREF'd.  When the
61  * alias is removed the target vnode is vrele'd.
62  */
63 
64 #define	UMAP_NHASH(vp) \
65 	(&umap_node_hashtbl[(((u_long)vp)>>LOG2_SIZEVNODE) & umap_node_hash])
66 LIST_HEAD(umap_node_hashhead, umap_node) *umap_node_hashtbl;
67 u_long umap_node_hash;
68 
69 static u_long umap_findid __P((u_long, u_long [][2], int));
70 static struct vnode *umap_node_find __P((struct mount *, struct vnode *));
71 static int umap_node_alloc __P((struct mount *, struct vnode *,
72 				struct vnode **));
73 
74 /*
75  * Initialise cache headers
76  */
77 void
78 umapfs_init()
79 {
80 
81 #ifdef UMAPFS_DIAGNOSTIC
82 	printf("umapfs_init\n");		/* printed during system boot */
83 #endif
84 	umap_node_hashtbl = hashinit(NUMAPNODECACHE, M_CACHE, &umap_node_hash);
85 }
86 
87 /*
88  * umap_findid is called by various routines in umap_vnodeops.c to
89  * find a user or group id in a map.
90  */
91 static u_long
92 umap_findid(id, map, nentries)
93 	u_long id;
94 	u_long map[][2];
95 	int nentries;
96 {
97 	int i;
98 
99 	/* Find uid entry in map */
100 	i = 0;
101 	while ((i<nentries) && ((map[i][0]) != id))
102 		i++;
103 
104 	if (i < nentries)
105 		return (map[i][1]);
106 	else
107 		return (-1);
108 
109 }
110 
111 /*
112  * umap_reverse_findid is called by umap_getattr() in umap_vnodeops.c to
113  * find a user or group id in a map, in reverse.
114  */
115 u_long
116 umap_reverse_findid(id, map, nentries)
117 	u_long id;
118 	u_long map[][2];
119 	int nentries;
120 {
121 	int i;
122 
123 	/* Find uid entry in map */
124 	i = 0;
125 	while ((i<nentries) && ((map[i][1]) != id))
126 		i++;
127 
128 	if (i < nentries)
129 		return (map[i][0]);
130 	else
131 		return (-1);
132 
133 }
134 
135 /*
136  * Return alias for target vnode if already exists, else 0.
137  */
138 static struct vnode *
139 umap_node_find(mp, targetvp)
140 	struct mount *mp;
141 	struct vnode *targetvp;
142 {
143 	struct umap_node_hashhead *hd;
144 	struct umap_node *a;
145 	struct vnode *vp;
146 
147 #ifdef UMAPFS_DIAGNOSTIC
148 	printf("umap_node_find(mp = %p, target = %p)\n", mp, targetvp);
149 #endif
150 
151 	/*
152 	 * Find hash base, and then search the (two-way) linked
153 	 * list looking for a umap_node structure which is referencing
154 	 * the target vnode.  If found, the increment the umap_node
155 	 * reference count (but NOT the target vnode's VREF counter).
156 	 */
157 	hd = UMAP_NHASH(targetvp);
158 loop:
159 	for (a = hd->lh_first; a != 0; a = a->umap_hash.le_next) {
160 		if (a->umap_lowervp == targetvp &&
161 		    a->umap_vnode->v_mount == mp) {
162 			vp = UMAPTOV(a);
163 			/*
164 			 * We need vget for the VXLOCK
165 			 * stuff, but we don't want to lock
166 			 * the lower node.
167 			 */
168 			if (vget(vp, 0)) {
169 #ifdef UMAPFS_DIAGNOSTIC
170 				printf ("umap_node_find: vget failed.\n");
171 #endif
172 				goto loop;
173 			}
174 			return (vp);
175 		}
176 	}
177 
178 #ifdef UMAPFS_DIAGNOSTIC
179 	printf("umap_node_find(%p, %p): NOT found\n", mp, targetvp);
180 #endif
181 
182 	return (0);
183 }
184 
185 /*
186  * Make a new umap_node node.
187  * Vp is the alias vnode, lowervp is the target vnode.
188  * Maintain a reference to lowervp.
189  */
190 static int
191 umap_node_alloc(mp, lowervp, vpp)
192 	struct mount *mp;
193 	struct vnode *lowervp;
194 	struct vnode **vpp;
195 {
196 	struct umap_node_hashhead *hd;
197 	struct umap_node *xp;
198 	struct vnode *vp, *nvp;
199 	int error;
200 	extern int (**dead_vnodeop_p) __P((void *));
201 
202 	if ((error = getnewvnode(VT_UMAP, mp, umap_vnodeop_p, &vp)) != 0)
203 		return (error);
204 	vp->v_type = lowervp->v_type;
205 
206 	MALLOC(xp, struct umap_node *, sizeof(struct umap_node), M_TEMP,
207 	    M_WAITOK);
208 	if (vp->v_type == VBLK || vp->v_type == VCHR) {
209 		MALLOC(vp->v_specinfo, struct specinfo *,
210 		    sizeof(struct specinfo), M_VNODE, M_WAITOK);
211 		vp->v_rdev = lowervp->v_rdev;
212 	}
213 
214 	vp->v_data = xp;
215 	xp->umap_vnode = vp;
216 	xp->umap_lowervp = lowervp;
217 	/*
218 	 * Before we insert our new node onto the hash chains,
219 	 * check to see if someone else has beaten us to it.
220 	 * (We could have slept in MALLOC.)
221 	 */
222 	if ((nvp = umap_node_find(mp, lowervp)) != NULL) {
223 		*vpp = nvp;
224 
225 		/* free the substructures we've allocated. */
226 		FREE(xp, M_TEMP);
227 		if (vp->v_type == VBLK || vp->v_type == VCHR)
228 			FREE(vp->v_specinfo, M_VNODE);
229 
230 		vp->v_type = VBAD;		/* node is discarded */
231 		vp->v_op = dead_vnodeop_p;	/* so ops will still work */
232 		vrele(vp);			/* get rid of it. */
233 		return (0);
234 	}
235 
236 	/*
237 	 * XXX if it's a device node, it needs to be checkalias()ed.
238 	 * however, for locking reasons, that's just not possible.
239 	 * so we have to do most of the dirty work inline.  Note that
240 	 * this is a limited case; we know that there's going to be
241 	 * an alias, and we know that that alias will be a "real"
242 	 * device node, i.e. not tagged VT_NON.
243 	 */
244 	if (vp->v_type == VBLK || vp->v_type == VCHR) {
245 		struct vnode *cvp, **cvpp;
246 
247 		cvpp = &speclisth[SPECHASH(vp->v_rdev)];
248 loop:
249 		for (cvp = *cvpp; cvp; cvp = cvp->v_specnext) {
250 			if (vp->v_rdev != cvp->v_rdev ||
251 			    vp->v_type != cvp->v_type)
252 				continue;
253 
254 			/*
255 			 * Alias, but not in use, so flush it out.
256 			 */
257 			if (cvp->v_usecount == 0) {
258 				vgone(cvp);
259 				goto loop;
260 			}
261 			if (vget(cvp, 0))	/* can't lock; will die! */
262 				goto loop;
263 			break;
264 		}
265 
266 		vp->v_hashchain = cvpp;
267 		vp->v_specnext = *cvpp;
268 		vp->v_specflags = 0;
269 		*cvpp = vp;
270 #ifdef DIAGNOSTIC
271 		if (cvp == NULLVP)
272 			panic("umap_node_alloc: no alias for device");
273 #endif
274 		vp->v_flag |= VALIASED;
275 		cvp->v_flag |= VALIASED;
276 		vrele(cvp);
277 	}
278 	/* XXX end of transmogrified checkalias() */
279 
280 	*vpp = vp;
281 	VREF(lowervp);	/* Extra VREF will be vrele'd in umap_node_create */
282 	hd = UMAP_NHASH(lowervp);
283 	LIST_INSERT_HEAD(hd, xp, umap_hash);
284 	return (0);
285 }
286 
287 
288 /*
289  * Try to find an existing umap_node vnode refering
290  * to it, otherwise make a new umap_node vnode which
291  * contains a reference to the target vnode.
292  */
293 int
294 umap_node_create(mp, targetvp, newvpp)
295 	struct mount *mp;
296 	struct vnode *targetvp;
297 	struct vnode **newvpp;
298 {
299 	struct vnode *aliasvp;
300 
301 	if ((aliasvp = umap_node_find(mp, targetvp)) != NULL) {
302 		/*
303 		 * Take another reference to the alias vnode
304 		 */
305 #ifdef UMAPFS_DIAGNOSTIC
306 		vprint("umap_node_create: exists", aliasvp);
307 #endif
308 		/* VREF(aliasvp); */
309 	} else {
310 		int error;
311 
312 		/*
313 		 * Get new vnode.
314 		 */
315 #ifdef UMAPFS_DIAGNOSTIC
316 		printf("umap_node_create: create new alias vnode\n");
317 #endif
318 		/*
319 		 * Make new vnode reference the umap_node.
320 		 */
321 		if ((error = umap_node_alloc(mp, targetvp, &aliasvp)) != 0)
322 			return (error);
323 
324 		/*
325 		 * aliasvp is already VREF'd by getnewvnode()
326 		 */
327 	}
328 
329 	vrele(targetvp);
330 
331 #ifdef UMAPFS_DIAGNOSTIC
332 	vprint("umap_node_create: alias", aliasvp);
333 	vprint("umap_node_create: target", targetvp);
334 #endif
335 
336 	*newvpp = aliasvp;
337 	return (0);
338 }
339 
340 #ifdef UMAPFS_DIAGNOSTIC
341 int umap_checkvp_barrier = 1;
342 struct vnode *
343 umap_checkvp(vp, fil, lno)
344 	struct vnode *vp;
345 	char *fil;
346 	int lno;
347 {
348 	struct umap_node *a = VTOUMAP(vp);
349 #if 0
350 	/*
351 	 * Can't do this check because vop_reclaim runs
352 	 * with funny vop vector.
353 	 */
354 	if (vp->v_op != umap_vnodeop_p) {
355 		printf("umap_checkvp: on non-umap-node\n");
356 		while (umap_checkvp_barrier) /*WAIT*/ ;
357 		panic("umap_checkvp");
358 	}
359 #endif
360 	if (a->umap_lowervp == NULL) {
361 		/* Should never happen */
362 		int i; u_long *p;
363 		printf("vp = %p, ZERO ptr\n", vp);
364 		for (p = (u_long *) a, i = 0; i < 8; i++)
365 			printf(" %lx", p[i]);
366 		printf("\n");
367 		/* wait for debugger */
368 		while (umap_checkvp_barrier) /*WAIT*/ ;
369 		panic("umap_checkvp");
370 	}
371 	if (a->umap_lowervp->v_usecount < 1) {
372 		int i; u_long *p;
373 		printf("vp = %p, unref'ed lowervp\n", vp);
374 		for (p = (u_long *) a, i = 0; i < 8; i++)
375 			printf(" %lx", p[i]);
376 		printf("\n");
377 		/* wait for debugger */
378 		while (umap_checkvp_barrier) /*WAIT*/ ;
379 		panic ("umap with unref'ed lowervp");
380 	}
381 #if 0
382 	printf("umap %p/%d -> %p/%d [%s, %d]\n",
383 	        a->umap_vnode, a->umap_vnode->v_usecount,
384 		a->umap_lowervp, a->umap_lowervp->v_usecount,
385 		fil, lno);
386 #endif
387 	return (a->umap_lowervp);
388 }
389 #endif
390 
391 /* umap_mapids maps all of the ids in a credential, both user and group. */
392 
393 void
394 umap_mapids(v_mount, credp)
395 	struct mount *v_mount;
396 	struct ucred *credp;
397 {
398 	int i, unentries, gnentries;
399 	uid_t uid;
400 	gid_t gid;
401 	u_long (*usermap)[2], (*groupmap)[2];
402 
403 	if (credp == NOCRED)
404 		return;
405 
406 	unentries =  MOUNTTOUMAPMOUNT(v_mount)->info_nentries;
407 	usermap =  MOUNTTOUMAPMOUNT(v_mount)->info_mapdata;
408 	gnentries =  MOUNTTOUMAPMOUNT(v_mount)->info_gnentries;
409 	groupmap =  MOUNTTOUMAPMOUNT(v_mount)->info_gmapdata;
410 
411 	/* Find uid entry in map */
412 
413 	uid = (uid_t) umap_findid(credp->cr_uid, usermap, unentries);
414 
415 	if (uid != -1)
416 		credp->cr_uid = uid;
417 	else
418 		credp->cr_uid = (uid_t) NOBODY;
419 
420 #if 1
421 	/* cr_gid is the same as cr_groups[0] in 4BSD, but not in NetBSD */
422 
423 	/* Find gid entry in map */
424 
425 	gid = (gid_t) umap_findid(credp->cr_gid, groupmap, gnentries);
426 
427 	if (gid != -1)
428 		credp->cr_gid = gid;
429 	else
430 		credp->cr_gid = NULLGROUP;
431 #endif
432 
433 	/* Now we must map each of the set of groups in the cr_groups
434 		structure. */
435 
436 	i = 0;
437 	while (credp->cr_groups[i] != 0) {
438 		gid = (gid_t) umap_findid(credp->cr_groups[i],
439 					  groupmap, gnentries);
440 
441 		if (gid != -1)
442 			credp->cr_groups[i++] = gid;
443 		else
444 			credp->cr_groups[i++] = NULLGROUP;
445 	}
446 }
447