union_subr.c revision 276500
1290001Sglebius/*-
2290001Sglebius * Copyright (c) 1994 Jan-Simon Pendry
3290001Sglebius * Copyright (c) 1994
4290001Sglebius *	The Regents of the University of California.  All rights reserved.
5290001Sglebius * Copyright (c) 2005, 2006, 2012 Masanori Ozawa <ozawa@ongs.co.jp>, ONGS Inc.
6290001Sglebius * Copyright (c) 2006, 2012 Daichi Goto <daichi@freebsd.org>
7290001Sglebius *
8290001Sglebius * This code is derived from software contributed to Berkeley by
9290001Sglebius * Jan-Simon Pendry.
10290001Sglebius *
11290001Sglebius * Redistribution and use in source and binary forms, with or without
12290001Sglebius * modification, are permitted provided that the following conditions
13290001Sglebius * are met:
14290001Sglebius * 1. Redistributions of source code must retain the above copyright
15290001Sglebius *    notice, this list of conditions and the following disclaimer.
16290001Sglebius * 2. Redistributions in binary form must reproduce the above copyright
17290001Sglebius *    notice, this list of conditions and the following disclaimer in the
18290001Sglebius *    documentation and/or other materials provided with the distribution.
19290001Sglebius * 4. Neither the name of the University nor the names of its contributors
20290001Sglebius *    may be used to endorse or promote products derived from this software
21290001Sglebius *    without specific prior written permission.
22290001Sglebius *
23290001Sglebius * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24290001Sglebius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25290001Sglebius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26290001Sglebius * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27290001Sglebius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28290001Sglebius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29290001Sglebius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30290001Sglebius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31290001Sglebius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32290001Sglebius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33290001Sglebius * SUCH DAMAGE.
34290001Sglebius *
35290001Sglebius *	@(#)union_subr.c	8.20 (Berkeley) 5/20/95
36290001Sglebius * $FreeBSD: stable/10/sys/fs/unionfs/union_subr.c 276500 2015-01-01 10:44:20Z kib $
37290001Sglebius */
38290001Sglebius
39290001Sglebius#include <sys/param.h>
40290001Sglebius#include <sys/systm.h>
41290001Sglebius#include <sys/kernel.h>
42290001Sglebius#include <sys/lock.h>
43290001Sglebius#include <sys/mutex.h>
44290001Sglebius#include <sys/malloc.h>
45290001Sglebius#include <sys/mount.h>
46290001Sglebius#include <sys/namei.h>
47290001Sglebius#include <sys/proc.h>
48290001Sglebius#include <sys/vnode.h>
49290001Sglebius#include <sys/dirent.h>
50290001Sglebius#include <sys/fcntl.h>
51290001Sglebius#include <sys/filedesc.h>
52290001Sglebius#include <sys/stat.h>
53290001Sglebius#include <sys/resourcevar.h>
54290001Sglebius
55290001Sglebius#include <security/mac/mac_framework.h>
56290001Sglebius
57290001Sglebius#include <vm/uma.h>
58290001Sglebius
59290001Sglebius#include <fs/unionfs/union.h>
60290001Sglebius
61290001Sglebius#define NUNIONFSNODECACHE 16
62290001Sglebius
63290001Sglebiusstatic MALLOC_DEFINE(M_UNIONFSHASH, "UNIONFS hash", "UNIONFS hash table");
64290001SglebiusMALLOC_DEFINE(M_UNIONFSNODE, "UNIONFS node", "UNIONFS vnode private part");
65290001SglebiusMALLOC_DEFINE(M_UNIONFSPATH, "UNIONFS path", "UNIONFS path private part");
66290001Sglebius
67290001Sglebius/*
68290001Sglebius * Initialize
69290001Sglebius */
70290001Sglebiusint
71290001Sglebiusunionfs_init(struct vfsconf *vfsp)
72290001Sglebius{
73290001Sglebius	UNIONFSDEBUG("unionfs_init\n");	/* printed during system boot */
74290001Sglebius	return (0);
75290001Sglebius}
76290001Sglebius
77290001Sglebius/*
78290001Sglebius * Uninitialize
79290001Sglebius */
80290001Sglebiusint
81290001Sglebiusunionfs_uninit(struct vfsconf *vfsp)
82290001Sglebius{
83290001Sglebius	return (0);
84290001Sglebius}
85290001Sglebius
86290001Sglebiusstatic struct unionfs_node_hashhead *
87290001Sglebiusunionfs_get_hashhead(struct vnode *dvp, char *path)
88290001Sglebius{
89290001Sglebius	int		count;
90290001Sglebius	char		hash;
91290001Sglebius	struct unionfs_node *unp;
92290001Sglebius
93290001Sglebius	hash = 0;
94290001Sglebius	unp = VTOUNIONFS(dvp);
95290001Sglebius	if (path != NULL) {
96290001Sglebius		for (count = 0; path[count]; count++)
97290001Sglebius			hash += path[count];
98290001Sglebius	}
99290001Sglebius
100290001Sglebius	return (&(unp->un_hashtbl[hash & (unp->un_hashmask)]));
101290001Sglebius}
102290001Sglebius
103290001Sglebius/*
104290001Sglebius * Get the cached vnode.
105290001Sglebius */
106290001Sglebiusstatic struct vnode *
107290001Sglebiusunionfs_get_cached_vnode(struct vnode *uvp, struct vnode *lvp,
108290001Sglebius			struct vnode *dvp, char *path)
109290001Sglebius{
110290001Sglebius	struct unionfs_node_hashhead *hd;
111290001Sglebius	struct unionfs_node *unp;
112290001Sglebius	struct vnode   *vp;
113290001Sglebius
114290001Sglebius	KASSERT((uvp == NULLVP || uvp->v_type == VDIR),
115290001Sglebius	    ("unionfs_get_cached_vnode: v_type != VDIR"));
116290001Sglebius	KASSERT((lvp == NULLVP || lvp->v_type == VDIR),
117290001Sglebius	    ("unionfs_get_cached_vnode: v_type != VDIR"));
118290001Sglebius
119290001Sglebius	VI_LOCK(dvp);
120290001Sglebius	hd = unionfs_get_hashhead(dvp, path);
121290001Sglebius	LIST_FOREACH(unp, hd, un_hash) {
122290001Sglebius		if (!strcmp(unp->un_path, path)) {
123290001Sglebius			vp = UNIONFSTOV(unp);
124290001Sglebius			VI_LOCK_FLAGS(vp, MTX_DUPOK);
125290001Sglebius			VI_UNLOCK(dvp);
126290001Sglebius			vp->v_iflag &= ~VI_OWEINACT;
127290001Sglebius			if ((vp->v_iflag & (VI_DOOMED | VI_DOINGINACT)) != 0) {
128290001Sglebius				VI_UNLOCK(vp);
129290001Sglebius				vp = NULLVP;
130290001Sglebius			} else
131290001Sglebius				VI_UNLOCK(vp);
132290001Sglebius			return (vp);
133290001Sglebius		}
134290001Sglebius	}
135290001Sglebius	VI_UNLOCK(dvp);
136290001Sglebius
137290001Sglebius	return (NULLVP);
138290001Sglebius}
139290001Sglebius
140290001Sglebius/*
141290001Sglebius * Add the new vnode into cache.
142290001Sglebius */
143290001Sglebiusstatic struct vnode *
144290001Sglebiusunionfs_ins_cached_vnode(struct unionfs_node *uncp,
145290001Sglebius			struct vnode *dvp, char *path)
146290001Sglebius{
147290001Sglebius	struct unionfs_node_hashhead *hd;
148290001Sglebius	struct unionfs_node *unp;
149290001Sglebius	struct vnode   *vp;
150290001Sglebius
151290001Sglebius	KASSERT((uncp->un_uppervp==NULLVP || uncp->un_uppervp->v_type==VDIR),
152290001Sglebius	    ("unionfs_ins_cached_vnode: v_type != VDIR"));
153290001Sglebius	KASSERT((uncp->un_lowervp==NULLVP || uncp->un_lowervp->v_type==VDIR),
154290001Sglebius	    ("unionfs_ins_cached_vnode: v_type != VDIR"));
155290001Sglebius
156290001Sglebius	VI_LOCK(dvp);
157290001Sglebius	hd = unionfs_get_hashhead(dvp, path);
158290001Sglebius	LIST_FOREACH(unp, hd, un_hash) {
159290001Sglebius		if (!strcmp(unp->un_path, path)) {
160290001Sglebius			vp = UNIONFSTOV(unp);
161290001Sglebius			VI_LOCK_FLAGS(vp, MTX_DUPOK);
162290001Sglebius			vp->v_iflag &= ~VI_OWEINACT;
163290001Sglebius			if ((vp->v_iflag & (VI_DOOMED | VI_DOINGINACT)) != 0) {
164290001Sglebius				LIST_INSERT_HEAD(hd, uncp, un_hash);
165290001Sglebius				VI_UNLOCK(vp);
166290001Sglebius				vp = NULLVP;
167290001Sglebius			} else
168290001Sglebius				VI_UNLOCK(vp);
169290001Sglebius			VI_UNLOCK(dvp);
170290001Sglebius			return (vp);
171290001Sglebius		}
172290001Sglebius	}
173290001Sglebius
174290001Sglebius	LIST_INSERT_HEAD(hd, uncp, un_hash);
175290001Sglebius	VI_UNLOCK(dvp);
176290001Sglebius
177290001Sglebius	return (NULLVP);
178290001Sglebius}
179290001Sglebius
180290001Sglebius/*
181290001Sglebius * Remove the vnode.
182290001Sglebius */
183290001Sglebiusstatic void
184290001Sglebiusunionfs_rem_cached_vnode(struct unionfs_node *unp, struct vnode *dvp)
185290001Sglebius{
186290001Sglebius	KASSERT((unp != NULL), ("unionfs_rem_cached_vnode: null node"));
187290001Sglebius	KASSERT((dvp != NULLVP),
188290001Sglebius	    ("unionfs_rem_cached_vnode: null parent vnode"));
189290001Sglebius	KASSERT((unp->un_hash.le_prev != NULL),
190290001Sglebius	    ("unionfs_rem_cached_vnode: null hash"));
191290001Sglebius
192290001Sglebius	VI_LOCK(dvp);
193290001Sglebius	LIST_REMOVE(unp, un_hash);
194290001Sglebius	unp->un_hash.le_next = NULL;
195290001Sglebius	unp->un_hash.le_prev = NULL;
196290001Sglebius	VI_UNLOCK(dvp);
197290001Sglebius}
198290001Sglebius
199290001Sglebius/*
200290001Sglebius * Make a new or get existing unionfs node.
201290001Sglebius *
202290001Sglebius * uppervp and lowervp should be unlocked. Because if new unionfs vnode is
203290001Sglebius * locked, uppervp or lowervp is locked too. In order to prevent dead lock,
204290001Sglebius * you should not lock plurality simultaneously.
205290001Sglebius */
206290001Sglebiusint
207290001Sglebiusunionfs_nodeget(struct mount *mp, struct vnode *uppervp,
208290001Sglebius		struct vnode *lowervp, struct vnode *dvp,
209290001Sglebius		struct vnode **vpp, struct componentname *cnp,
210290001Sglebius		struct thread *td)
211290001Sglebius{
212290001Sglebius	struct unionfs_mount *ump;
213290001Sglebius	struct unionfs_node *unp;
214290001Sglebius	struct vnode   *vp;
215290001Sglebius	int		error;
216290001Sglebius	int		lkflags;
217290001Sglebius	enum vtype	vt;
218290001Sglebius	char	       *path;
219290001Sglebius
220290001Sglebius	ump = MOUNTTOUNIONFSMOUNT(mp);
221290001Sglebius	lkflags = (cnp ? cnp->cn_lkflags : 0);
222290001Sglebius	path = (cnp ? cnp->cn_nameptr : NULL);
223290001Sglebius	*vpp = NULLVP;
224290001Sglebius
225290001Sglebius	if (uppervp == NULLVP && lowervp == NULLVP)
226290001Sglebius		panic("unionfs_nodeget: upper and lower is null");
227290001Sglebius
228290001Sglebius	vt = (uppervp != NULLVP ? uppervp->v_type : lowervp->v_type);
229290001Sglebius
230290001Sglebius	/* If it has no ISLASTCN flag, path check is skipped. */
231290001Sglebius	if (cnp && !(cnp->cn_flags & ISLASTCN))
232290001Sglebius		path = NULL;
233290001Sglebius
234290001Sglebius	/* check the cache */
235290001Sglebius	if (path != NULL && dvp != NULLVP && vt == VDIR) {
236290001Sglebius		vp = unionfs_get_cached_vnode(uppervp, lowervp, dvp, path);
237290001Sglebius		if (vp != NULLVP) {
238290001Sglebius			vref(vp);
239290001Sglebius			*vpp = vp;
240290001Sglebius			goto unionfs_nodeget_out;
241290001Sglebius		}
242290001Sglebius	}
243290001Sglebius
244290001Sglebius	if ((uppervp == NULLVP || ump->um_uppervp != uppervp) ||
245290001Sglebius	    (lowervp == NULLVP || ump->um_lowervp != lowervp)) {
246290001Sglebius		/* dvp will be NULLVP only in case of root vnode. */
247290001Sglebius		if (dvp == NULLVP)
248290001Sglebius			return (EINVAL);
249290001Sglebius	}
250290001Sglebius	unp = malloc(sizeof(struct unionfs_node),
251290001Sglebius	    M_UNIONFSNODE, M_WAITOK | M_ZERO);
252290001Sglebius
253290001Sglebius	error = getnewvnode("unionfs", mp, &unionfs_vnodeops, &vp);
254290001Sglebius	if (error != 0) {
255290001Sglebius		free(unp, M_UNIONFSNODE);
256290001Sglebius		return (error);
257290001Sglebius	}
258290001Sglebius	error = insmntque(vp, mp);	/* XXX: Too early for mpsafe fs */
259290001Sglebius	if (error != 0) {
260290001Sglebius		free(unp, M_UNIONFSNODE);
261290001Sglebius		return (error);
262290001Sglebius	}
263290001Sglebius	if (dvp != NULLVP)
264290001Sglebius		vref(dvp);
265290001Sglebius	if (uppervp != NULLVP)
266290001Sglebius		vref(uppervp);
267290001Sglebius	if (lowervp != NULLVP)
268290001Sglebius		vref(lowervp);
269290001Sglebius
270290001Sglebius	if (vt == VDIR)
271290001Sglebius		unp->un_hashtbl = hashinit(NUNIONFSNODECACHE, M_UNIONFSHASH,
272290001Sglebius		    &(unp->un_hashmask));
273290001Sglebius
274290001Sglebius	unp->un_vnode = vp;
275290001Sglebius	unp->un_uppervp = uppervp;
276290001Sglebius	unp->un_lowervp = lowervp;
277290001Sglebius	unp->un_dvp = dvp;
278290001Sglebius	if (uppervp != NULLVP)
279290001Sglebius		vp->v_vnlock = uppervp->v_vnlock;
280290001Sglebius	else
281290001Sglebius		vp->v_vnlock = lowervp->v_vnlock;
282290001Sglebius
283290001Sglebius	if (path != NULL) {
284290001Sglebius		unp->un_path = (char *)
285290001Sglebius		    malloc(cnp->cn_namelen +1, M_UNIONFSPATH, M_WAITOK|M_ZERO);
286290001Sglebius		bcopy(cnp->cn_nameptr, unp->un_path, cnp->cn_namelen);
287290001Sglebius		unp->un_path[cnp->cn_namelen] = '\0';
288290001Sglebius	}
289290001Sglebius	vp->v_type = vt;
290290001Sglebius	vp->v_data = unp;
291290001Sglebius
292290001Sglebius	if ((uppervp != NULLVP && ump->um_uppervp == uppervp) &&
293290001Sglebius	    (lowervp != NULLVP && ump->um_lowervp == lowervp))
294290001Sglebius		vp->v_vflag |= VV_ROOT;
295290001Sglebius
296290001Sglebius	if (path != NULL && dvp != NULLVP && vt == VDIR)
297290001Sglebius		*vpp = unionfs_ins_cached_vnode(unp, dvp, path);
298290001Sglebius	if ((*vpp) != NULLVP) {
299290001Sglebius		if (dvp != NULLVP)
300290001Sglebius			vrele(dvp);
301290001Sglebius		if (uppervp != NULLVP)
302290001Sglebius			vrele(uppervp);
303290001Sglebius		if (lowervp != NULLVP)
304290001Sglebius			vrele(lowervp);
305290001Sglebius
306290001Sglebius		unp->un_uppervp = NULLVP;
307290001Sglebius		unp->un_lowervp = NULLVP;
308290001Sglebius		unp->un_dvp = NULLVP;
309290001Sglebius		vrele(vp);
310290001Sglebius		vp = *vpp;
311290001Sglebius		vref(vp);
312290001Sglebius	} else
313290001Sglebius		*vpp = vp;
314290001Sglebius
315290001Sglebiusunionfs_nodeget_out:
316290001Sglebius	if (lkflags & LK_TYPE_MASK)
317290001Sglebius		vn_lock(vp, lkflags | LK_RETRY);
318290001Sglebius
319290001Sglebius	return (0);
320290001Sglebius}
321290001Sglebius
322290001Sglebius/*
323290001Sglebius * Clean up the unionfs node.
324290001Sglebius */
325290001Sglebiusvoid
326290001Sglebiusunionfs_noderem(struct vnode *vp, struct thread *td)
327290001Sglebius{
328290001Sglebius	int		count;
329290001Sglebius	struct unionfs_node *unp, *unp_t1, *unp_t2;
330290001Sglebius	struct unionfs_node_hashhead *hd;
331290001Sglebius	struct unionfs_node_status *unsp, *unsp_tmp;
332290001Sglebius	struct vnode   *lvp;
333290001Sglebius	struct vnode   *uvp;
334290001Sglebius	struct vnode   *dvp;
335290001Sglebius
336290001Sglebius	/*
337290001Sglebius	 * Use the interlock to protect the clearing of v_data to
338290001Sglebius	 * prevent faults in unionfs_lock().
339290001Sglebius	 */
340290001Sglebius	VI_LOCK(vp);
341290001Sglebius	unp = VTOUNIONFS(vp);
342290001Sglebius	lvp = unp->un_lowervp;
343290001Sglebius	uvp = unp->un_uppervp;
344290001Sglebius	dvp = unp->un_dvp;
345290001Sglebius	unp->un_lowervp = unp->un_uppervp = NULLVP;
346290001Sglebius	vp->v_vnlock = &(vp->v_lock);
347290001Sglebius	vp->v_data = NULL;
348290001Sglebius	vp->v_object = NULL;
349290001Sglebius	VI_UNLOCK(vp);
350290001Sglebius
351290001Sglebius	if (lvp != NULLVP)
352290001Sglebius		VOP_UNLOCK(lvp, LK_RELEASE);
353290001Sglebius	if (uvp != NULLVP)
354290001Sglebius		VOP_UNLOCK(uvp, LK_RELEASE);
355290001Sglebius
356290001Sglebius	if (dvp != NULLVP && unp->un_hash.le_prev != NULL)
357290001Sglebius		unionfs_rem_cached_vnode(unp, dvp);
358290001Sglebius
359290001Sglebius	if (lockmgr(vp->v_vnlock, LK_EXCLUSIVE, VI_MTX(vp)) != 0)
360290001Sglebius		panic("the lock for deletion is unacquirable.");
361290001Sglebius
362290001Sglebius	if (lvp != NULLVP)
363290001Sglebius		vrele(lvp);
364290001Sglebius	if (uvp != NULLVP)
365290001Sglebius		vrele(uvp);
366290001Sglebius	if (dvp != NULLVP) {
367290001Sglebius		vrele(dvp);
368290001Sglebius		unp->un_dvp = NULLVP;
369290001Sglebius	}
370290001Sglebius	if (unp->un_path != NULL) {
371290001Sglebius		free(unp->un_path, M_UNIONFSPATH);
372290001Sglebius		unp->un_path = NULL;
373290001Sglebius	}
374290001Sglebius
375290001Sglebius	if (unp->un_hashtbl != NULL) {
376290001Sglebius		for (count = 0; count <= unp->un_hashmask; count++) {
377290001Sglebius			hd = unp->un_hashtbl + count;
378290001Sglebius			LIST_FOREACH_SAFE(unp_t1, hd, un_hash, unp_t2) {
379290001Sglebius				LIST_REMOVE(unp_t1, un_hash);
380290001Sglebius				unp_t1->un_hash.le_next = NULL;
381290001Sglebius				unp_t1->un_hash.le_prev = NULL;
382290001Sglebius			}
383290001Sglebius		}
384290001Sglebius		hashdestroy(unp->un_hashtbl, M_UNIONFSHASH, unp->un_hashmask);
385290001Sglebius	}
386290001Sglebius
387290001Sglebius	LIST_FOREACH_SAFE(unsp, &(unp->un_unshead), uns_list, unsp_tmp) {
388290001Sglebius		LIST_REMOVE(unsp, uns_list);
389290001Sglebius		free(unsp, M_TEMP);
390290001Sglebius	}
391290001Sglebius	free(unp, M_UNIONFSNODE);
392290001Sglebius}
393290001Sglebius
394290001Sglebius/*
395290001Sglebius * Get the unionfs node status.
396290001Sglebius * You need exclusive lock this vnode.
397290001Sglebius */
398290001Sglebiusvoid
399290001Sglebiusunionfs_get_node_status(struct unionfs_node *unp, struct thread *td,
400290001Sglebius			struct unionfs_node_status **unspp)
401290001Sglebius{
402290001Sglebius	struct unionfs_node_status *unsp;
403290001Sglebius	pid_t pid = td->td_proc->p_pid;
404290001Sglebius
405290001Sglebius	KASSERT(NULL != unspp, ("null pointer"));
406290001Sglebius	ASSERT_VOP_ELOCKED(UNIONFSTOV(unp), "unionfs_get_node_status");
407290001Sglebius
408290001Sglebius	LIST_FOREACH(unsp, &(unp->un_unshead), uns_list) {
409290001Sglebius		if (unsp->uns_pid == pid) {
410290001Sglebius			*unspp = unsp;
411290001Sglebius			return;
412290001Sglebius		}
413290001Sglebius	}
414290001Sglebius
415290001Sglebius	/* create a new unionfs node status */
416290001Sglebius	unsp = malloc(sizeof(struct unionfs_node_status),
417290001Sglebius	    M_TEMP, M_WAITOK | M_ZERO);
418290001Sglebius
419290001Sglebius	unsp->uns_pid = pid;
420290001Sglebius	LIST_INSERT_HEAD(&(unp->un_unshead), unsp, uns_list);
421290001Sglebius
422290001Sglebius	*unspp = unsp;
423290001Sglebius}
424290001Sglebius
425290001Sglebius/*
426290001Sglebius * Remove the unionfs node status, if you can.
427290001Sglebius * You need exclusive lock this vnode.
428290001Sglebius */
429290001Sglebiusvoid
430290001Sglebiusunionfs_tryrem_node_status(struct unionfs_node *unp,
431290001Sglebius			   struct unionfs_node_status *unsp)
432290001Sglebius{
433290001Sglebius	KASSERT(NULL != unsp, ("null pointer"));
434290001Sglebius	ASSERT_VOP_ELOCKED(UNIONFSTOV(unp), "unionfs_get_node_status");
435290001Sglebius
436290001Sglebius	if (0 < unsp->uns_lower_opencnt || 0 < unsp->uns_upper_opencnt)
437290001Sglebius		return;
438290001Sglebius
439290001Sglebius	LIST_REMOVE(unsp, uns_list);
440290001Sglebius	free(unsp, M_TEMP);
441290001Sglebius}
442290001Sglebius
443290001Sglebius/*
444290001Sglebius * Create upper node attr.
445290001Sglebius */
446290001Sglebiusvoid
447290001Sglebiusunionfs_create_uppervattr_core(struct unionfs_mount *ump,
448290001Sglebius			       struct vattr *lva,
449290001Sglebius			       struct vattr *uva,
450290001Sglebius			       struct thread *td)
451290001Sglebius{
452290001Sglebius	VATTR_NULL(uva);
453290001Sglebius	uva->va_type = lva->va_type;
454290001Sglebius	uva->va_atime = lva->va_atime;
455290001Sglebius	uva->va_mtime = lva->va_mtime;
456290001Sglebius	uva->va_ctime = lva->va_ctime;
457290001Sglebius
458290001Sglebius	switch (ump->um_copymode) {
459290001Sglebius	case UNIONFS_TRANSPARENT:
460290001Sglebius		uva->va_mode = lva->va_mode;
461290001Sglebius		uva->va_uid = lva->va_uid;
462290001Sglebius		uva->va_gid = lva->va_gid;
463290001Sglebius		break;
464290001Sglebius	case UNIONFS_MASQUERADE:
465290001Sglebius		if (ump->um_uid == lva->va_uid) {
466290001Sglebius			uva->va_mode = lva->va_mode & 077077;
467290001Sglebius			uva->va_mode |= (lva->va_type == VDIR ? ump->um_udir : ump->um_ufile) & 0700;
468290001Sglebius			uva->va_uid = lva->va_uid;
469290001Sglebius			uva->va_gid = lva->va_gid;
470290001Sglebius		} else {
471290001Sglebius			uva->va_mode = (lva->va_type == VDIR ? ump->um_udir : ump->um_ufile);
472290001Sglebius			uva->va_uid = ump->um_uid;
473290001Sglebius			uva->va_gid = ump->um_gid;
474290001Sglebius		}
475290001Sglebius		break;
476290001Sglebius	default:		/* UNIONFS_TRADITIONAL */
477290001Sglebius		uva->va_mode = 0777 & ~td->td_proc->p_fd->fd_cmask;
478290001Sglebius		uva->va_uid = ump->um_uid;
479290001Sglebius		uva->va_gid = ump->um_gid;
480290001Sglebius		break;
481290001Sglebius	}
482290001Sglebius}
483290001Sglebius
484290001Sglebius/*
485290001Sglebius * Create upper node attr.
486290001Sglebius */
487290001Sglebiusint
488290001Sglebiusunionfs_create_uppervattr(struct unionfs_mount *ump,
489290001Sglebius			  struct vnode *lvp,
490290001Sglebius			  struct vattr *uva,
491290001Sglebius			  struct ucred *cred,
492290001Sglebius			  struct thread *td)
493290001Sglebius{
494290001Sglebius	int		error;
495290001Sglebius	struct vattr	lva;
496290001Sglebius
497290001Sglebius	if ((error = VOP_GETATTR(lvp, &lva, cred)))
498290001Sglebius		return (error);
499290001Sglebius
500290001Sglebius	unionfs_create_uppervattr_core(ump, &lva, uva, td);
501290001Sglebius
502290001Sglebius	return (error);
503290001Sglebius}
504290001Sglebius
505290001Sglebius/*
506290001Sglebius * relookup
507290001Sglebius *
508290001Sglebius * dvp should be locked on entry and will be locked on return.
509290001Sglebius *
510290001Sglebius * If an error is returned, *vpp will be invalid, otherwise it will hold a
511290001Sglebius * locked, referenced vnode. If *vpp == dvp then remember that only one
512290001Sglebius * LK_EXCLUSIVE lock is held.
513290001Sglebius */
514290001Sglebiusint
515290001Sglebiusunionfs_relookup(struct vnode *dvp, struct vnode **vpp,
516290001Sglebius		 struct componentname *cnp, struct componentname *cn,
517290001Sglebius		 struct thread *td, char *path, int pathlen, u_long nameiop)
518290001Sglebius{
519290001Sglebius	int	error;
520290001Sglebius
521290001Sglebius	cn->cn_namelen = pathlen;
522290001Sglebius	cn->cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK);
523290001Sglebius	bcopy(path, cn->cn_pnbuf, pathlen);
524290001Sglebius	cn->cn_pnbuf[pathlen] = '\0';
525290001Sglebius
526290001Sglebius	cn->cn_nameiop = nameiop;
527290001Sglebius	cn->cn_flags = (LOCKPARENT | LOCKLEAF | HASBUF | SAVENAME | ISLASTCN);
528290001Sglebius	cn->cn_lkflags = LK_EXCLUSIVE;
529290001Sglebius	cn->cn_thread = td;
530290001Sglebius	cn->cn_cred = cnp->cn_cred;
531290001Sglebius
532290001Sglebius	cn->cn_nameptr = cn->cn_pnbuf;
533290001Sglebius	cn->cn_consume = cnp->cn_consume;
534290001Sglebius
535290001Sglebius	if (nameiop == DELETE)
536290001Sglebius		cn->cn_flags |= (cnp->cn_flags & (DOWHITEOUT | SAVESTART));
537290001Sglebius	else if (RENAME == nameiop)
538290001Sglebius		cn->cn_flags |= (cnp->cn_flags & SAVESTART);
539290001Sglebius	else if (nameiop == CREATE)
540290001Sglebius		cn->cn_flags |= NOCACHE;
541290001Sglebius
542290001Sglebius	vref(dvp);
543290001Sglebius	VOP_UNLOCK(dvp, LK_RELEASE);
544290001Sglebius
545290001Sglebius	if ((error = relookup(dvp, vpp, cn))) {
546290001Sglebius		uma_zfree(namei_zone, cn->cn_pnbuf);
547290001Sglebius		cn->cn_flags &= ~HASBUF;
548290001Sglebius		vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
549290001Sglebius	} else
550290001Sglebius		vrele(dvp);
551290001Sglebius
552290001Sglebius	return (error);
553290001Sglebius}
554290001Sglebius
555290001Sglebius/*
556290001Sglebius * relookup for CREATE namei operation.
557290001Sglebius *
558290001Sglebius * dvp is unionfs vnode. dvp should be locked.
559290001Sglebius *
560290001Sglebius * If it called 'unionfs_copyfile' function by unionfs_link etc,
561290001Sglebius * VOP_LOOKUP information is broken.
562290001Sglebius * So it need relookup in order to create link etc.
563290001Sglebius */
564290001Sglebiusint
565290001Sglebiusunionfs_relookup_for_create(struct vnode *dvp, struct componentname *cnp,
566290001Sglebius			    struct thread *td)
567290001Sglebius{
568290001Sglebius	int	error;
569290001Sglebius	struct vnode *udvp;
570290001Sglebius	struct vnode *vp;
571290001Sglebius	struct componentname cn;
572290001Sglebius
573290001Sglebius	udvp = UNIONFSVPTOUPPERVP(dvp);
574290001Sglebius	vp = NULLVP;
575290001Sglebius
576290001Sglebius	error = unionfs_relookup(udvp, &vp, cnp, &cn, td, cnp->cn_nameptr,
577290001Sglebius	    strlen(cnp->cn_nameptr), CREATE);
578290001Sglebius	if (error)
579290001Sglebius		return (error);
580290001Sglebius
581290001Sglebius	if (vp != NULLVP) {
582290001Sglebius		if (udvp == vp)
583290001Sglebius			vrele(vp);
584290001Sglebius		else
585290001Sglebius			vput(vp);
586290001Sglebius
587290001Sglebius		error = EEXIST;
588290001Sglebius	}
589290001Sglebius
590290001Sglebius	if (cn.cn_flags & HASBUF) {
591290001Sglebius		uma_zfree(namei_zone, cn.cn_pnbuf);
592290001Sglebius		cn.cn_flags &= ~HASBUF;
593290001Sglebius	}
594290001Sglebius
595290001Sglebius	if (!error) {
596290001Sglebius		cn.cn_flags |= (cnp->cn_flags & HASBUF);
597290001Sglebius		cnp->cn_flags = cn.cn_flags;
598290001Sglebius	}
599290001Sglebius
600290001Sglebius	return (error);
601290001Sglebius}
602290001Sglebius
603290001Sglebius/*
604290001Sglebius * relookup for DELETE namei operation.
605290001Sglebius *
606290001Sglebius * dvp is unionfs vnode. dvp should be locked.
607290001Sglebius */
608290001Sglebiusint
609290001Sglebiusunionfs_relookup_for_delete(struct vnode *dvp, struct componentname *cnp,
610290001Sglebius			    struct thread *td)
611290001Sglebius{
612290001Sglebius	int	error;
613290001Sglebius	struct vnode *udvp;
614290001Sglebius	struct vnode *vp;
615290001Sglebius	struct componentname cn;
616290001Sglebius
617290001Sglebius	udvp = UNIONFSVPTOUPPERVP(dvp);
618290001Sglebius	vp = NULLVP;
619290001Sglebius
620290001Sglebius	error = unionfs_relookup(udvp, &vp, cnp, &cn, td, cnp->cn_nameptr,
621290001Sglebius	    strlen(cnp->cn_nameptr), DELETE);
622290001Sglebius	if (error)
623290001Sglebius		return (error);
624290001Sglebius
625290001Sglebius	if (vp == NULLVP)
626290001Sglebius		error = ENOENT;
627290001Sglebius	else {
628290001Sglebius		if (udvp == vp)
629290001Sglebius			vrele(vp);
630290001Sglebius		else
631290001Sglebius			vput(vp);
632290001Sglebius	}
633290001Sglebius
634290001Sglebius	if (cn.cn_flags & HASBUF) {
635290001Sglebius		uma_zfree(namei_zone, cn.cn_pnbuf);
636290001Sglebius		cn.cn_flags &= ~HASBUF;
637290001Sglebius	}
638290001Sglebius
639290001Sglebius	if (!error) {
640290001Sglebius		cn.cn_flags |= (cnp->cn_flags & HASBUF);
641290001Sglebius		cnp->cn_flags = cn.cn_flags;
642290001Sglebius	}
643290001Sglebius
644290001Sglebius	return (error);
645290001Sglebius}
646290001Sglebius
647290001Sglebius/*
648290001Sglebius * relookup for RENAME namei operation.
649290001Sglebius *
650290001Sglebius * dvp is unionfs vnode. dvp should be locked.
651290001Sglebius */
652290001Sglebiusint
653290001Sglebiusunionfs_relookup_for_rename(struct vnode *dvp, struct componentname *cnp,
654290001Sglebius			    struct thread *td)
655290001Sglebius{
656290001Sglebius	int error;
657290001Sglebius	struct vnode *udvp;
658290001Sglebius	struct vnode *vp;
659290001Sglebius	struct componentname cn;
660290001Sglebius
661290001Sglebius	udvp = UNIONFSVPTOUPPERVP(dvp);
662290001Sglebius	vp = NULLVP;
663290001Sglebius
664290001Sglebius	error = unionfs_relookup(udvp, &vp, cnp, &cn, td, cnp->cn_nameptr,
665290001Sglebius	    strlen(cnp->cn_nameptr), RENAME);
666290001Sglebius	if (error)
667290001Sglebius		return (error);
668290001Sglebius
669290001Sglebius	if (vp != NULLVP) {
670290001Sglebius		if (udvp == vp)
671290001Sglebius			vrele(vp);
672290001Sglebius		else
673290001Sglebius			vput(vp);
674290001Sglebius	}
675290001Sglebius
676290001Sglebius	if (cn.cn_flags & HASBUF) {
677290001Sglebius		uma_zfree(namei_zone, cn.cn_pnbuf);
678290001Sglebius		cn.cn_flags &= ~HASBUF;
679290001Sglebius	}
680290001Sglebius
681290001Sglebius	if (!error) {
682290001Sglebius		cn.cn_flags |= (cnp->cn_flags & HASBUF);
683290001Sglebius		cnp->cn_flags = cn.cn_flags;
684290001Sglebius	}
685290001Sglebius
686290001Sglebius	return (error);
687290001Sglebius
688290001Sglebius}
689290001Sglebius
690290001Sglebius/*
691290001Sglebius * Update the unionfs_node.
692290001Sglebius *
693290001Sglebius * uvp is new locked upper vnode. unionfs vnode's lock will be exchanged to the
694290001Sglebius * uvp's lock and lower's lock will be unlocked.
695290001Sglebius */
696290001Sglebiusstatic void
697290001Sglebiusunionfs_node_update(struct unionfs_node *unp, struct vnode *uvp,
698290001Sglebius		    struct thread *td)
699290001Sglebius{
700290001Sglebius	unsigned	count, lockrec;
701290001Sglebius	struct vnode   *vp;
702290001Sglebius	struct vnode   *lvp;
703290001Sglebius	struct vnode   *dvp;
704290001Sglebius
705290001Sglebius	vp = UNIONFSTOV(unp);
706290001Sglebius	lvp = unp->un_lowervp;
707290001Sglebius	ASSERT_VOP_ELOCKED(lvp, "unionfs_node_update");
708290001Sglebius	dvp = unp->un_dvp;
709290001Sglebius
710290001Sglebius	/*
711290001Sglebius	 * lock update
712290001Sglebius	 */
713290001Sglebius	VI_LOCK(vp);
714290001Sglebius	unp->un_uppervp = uvp;
715290001Sglebius	vp->v_vnlock = uvp->v_vnlock;
716290001Sglebius	VI_UNLOCK(vp);
717290001Sglebius	lockrec = lvp->v_vnlock->lk_recurse;
718290001Sglebius	for (count = 0; count < lockrec; count++)
719290001Sglebius		vn_lock(uvp, LK_EXCLUSIVE | LK_CANRECURSE | LK_RETRY);
720290001Sglebius
721290001Sglebius	/*
722290001Sglebius	 * cache update
723290001Sglebius	 */
724290001Sglebius	if (unp->un_path != NULL && dvp != NULLVP && vp->v_type == VDIR) {
725290001Sglebius		static struct unionfs_node_hashhead *hd;
726290001Sglebius
727290001Sglebius		VI_LOCK(dvp);
728290001Sglebius		hd = unionfs_get_hashhead(dvp, unp->un_path);
729290001Sglebius		LIST_REMOVE(unp, un_hash);
730290001Sglebius		LIST_INSERT_HEAD(hd, unp, un_hash);
731290001Sglebius		VI_UNLOCK(dvp);
732290001Sglebius	}
733290001Sglebius}
734290001Sglebius
735290001Sglebius/*
736290001Sglebius * Create a new shadow dir.
737290001Sglebius *
738290001Sglebius * udvp should be locked on entry and will be locked on return.
739290001Sglebius *
740290001Sglebius * If no error returned, unp will be updated.
741290001Sglebius */
742290001Sglebiusint
743290001Sglebiusunionfs_mkshadowdir(struct unionfs_mount *ump, struct vnode *udvp,
744290001Sglebius		    struct unionfs_node *unp, struct componentname *cnp,
745290001Sglebius		    struct thread *td)
746290001Sglebius{
747290001Sglebius	int		error;
748290001Sglebius	struct vnode   *lvp;
749290001Sglebius	struct vnode   *uvp;
750290001Sglebius	struct vattr	va;
751290001Sglebius	struct vattr	lva;
752290001Sglebius	struct componentname cn;
753290001Sglebius	struct mount   *mp;
754290001Sglebius	struct ucred   *cred;
755290001Sglebius	struct ucred   *credbk;
756290001Sglebius	struct uidinfo *rootinfo;
757290001Sglebius
758290001Sglebius	if (unp->un_uppervp != NULLVP)
759290001Sglebius		return (EEXIST);
760290001Sglebius
761290001Sglebius	lvp = unp->un_lowervp;
762290001Sglebius	uvp = NULLVP;
763290001Sglebius	credbk = cnp->cn_cred;
764290001Sglebius
765290001Sglebius	/* Authority change to root */
766290001Sglebius	rootinfo = uifind((uid_t)0);
767290001Sglebius	cred = crdup(cnp->cn_cred);
768290001Sglebius	/*
769290001Sglebius	 * The calls to chgproccnt() are needed to compensate for change_ruid()
770290001Sglebius	 * calling chgproccnt().
771290001Sglebius	 */
772290001Sglebius	chgproccnt(cred->cr_ruidinfo, 1, 0);
773290001Sglebius	change_euid(cred, rootinfo);
774290001Sglebius	change_ruid(cred, rootinfo);
775290001Sglebius	change_svuid(cred, (uid_t)0);
776290001Sglebius	uifree(rootinfo);
777290001Sglebius	cnp->cn_cred = cred;
778290001Sglebius
779290001Sglebius	memset(&cn, 0, sizeof(cn));
780290001Sglebius
781290001Sglebius	if ((error = VOP_GETATTR(lvp, &lva, cnp->cn_cred)))
782290001Sglebius		goto unionfs_mkshadowdir_abort;
783290001Sglebius
784290001Sglebius	if ((error = unionfs_relookup(udvp, &uvp, cnp, &cn, td, cnp->cn_nameptr, cnp->cn_namelen, CREATE)))
785290001Sglebius		goto unionfs_mkshadowdir_abort;
786290001Sglebius	if (uvp != NULLVP) {
787290001Sglebius		if (udvp == uvp)
788290001Sglebius			vrele(uvp);
789290001Sglebius		else
790290001Sglebius			vput(uvp);
791290001Sglebius
792290001Sglebius		error = EEXIST;
793290001Sglebius		goto unionfs_mkshadowdir_free_out;
794290001Sglebius	}
795290001Sglebius
796290001Sglebius	if ((error = vn_start_write(udvp, &mp, V_WAIT | PCATCH)))
797290001Sglebius		goto unionfs_mkshadowdir_free_out;
798290001Sglebius	unionfs_create_uppervattr_core(ump, &lva, &va, td);
799290001Sglebius
800290001Sglebius	error = VOP_MKDIR(udvp, &uvp, &cn, &va);
801290001Sglebius
802290001Sglebius	if (!error) {
803290001Sglebius		unionfs_node_update(unp, uvp, td);
804290001Sglebius
805290001Sglebius		/*
806290001Sglebius		 * XXX The bug which cannot set uid/gid was corrected.
807290001Sglebius		 * Ignore errors.
808290001Sglebius		 */
809290001Sglebius		va.va_type = VNON;
810290001Sglebius		VOP_SETATTR(uvp, &va, cn.cn_cred);
811290001Sglebius	}
812290001Sglebius	vn_finished_write(mp);
813290001Sglebius
814290001Sglebiusunionfs_mkshadowdir_free_out:
815290001Sglebius	if (cn.cn_flags & HASBUF) {
816290001Sglebius		uma_zfree(namei_zone, cn.cn_pnbuf);
817290001Sglebius		cn.cn_flags &= ~HASBUF;
818290001Sglebius	}
819290001Sglebius
820290001Sglebiusunionfs_mkshadowdir_abort:
821290001Sglebius	cnp->cn_cred = credbk;
822290001Sglebius	chgproccnt(cred->cr_ruidinfo, -1, 0);
823290001Sglebius	crfree(cred);
824290001Sglebius
825290001Sglebius	return (error);
826290001Sglebius}
827290001Sglebius
828290001Sglebius/*
829290001Sglebius * Create a new whiteout.
830290001Sglebius *
831290001Sglebius * dvp should be locked on entry and will be locked on return.
832290001Sglebius */
833290001Sglebiusint
834290001Sglebiusunionfs_mkwhiteout(struct vnode *dvp, struct componentname *cnp,
835290001Sglebius		   struct thread *td, char *path)
836290001Sglebius{
837290001Sglebius	int		error;
838290001Sglebius	struct vnode   *wvp;
839290001Sglebius	struct componentname cn;
840290001Sglebius	struct mount   *mp;
841290001Sglebius
842290001Sglebius	if (path == NULL)
843290001Sglebius		path = cnp->cn_nameptr;
844290001Sglebius
845290001Sglebius	wvp = NULLVP;
846290001Sglebius	if ((error = unionfs_relookup(dvp, &wvp, cnp, &cn, td, path, strlen(path), CREATE)))
847290001Sglebius		return (error);
848290001Sglebius	if (wvp != NULLVP) {
849290001Sglebius		if (cn.cn_flags & HASBUF) {
850290001Sglebius			uma_zfree(namei_zone, cn.cn_pnbuf);
851290001Sglebius			cn.cn_flags &= ~HASBUF;
852290001Sglebius		}
853290001Sglebius		if (dvp == wvp)
854290001Sglebius			vrele(wvp);
855290001Sglebius		else
856290001Sglebius			vput(wvp);
857290001Sglebius
858290001Sglebius		return (EEXIST);
859290001Sglebius	}
860290001Sglebius
861290001Sglebius	if ((error = vn_start_write(dvp, &mp, V_WAIT | PCATCH)))
862290001Sglebius		goto unionfs_mkwhiteout_free_out;
863290001Sglebius	error = VOP_WHITEOUT(dvp, &cn, CREATE);
864290001Sglebius
865290001Sglebius	vn_finished_write(mp);
866290001Sglebius
867290001Sglebiusunionfs_mkwhiteout_free_out:
868290001Sglebius	if (cn.cn_flags & HASBUF) {
869290001Sglebius		uma_zfree(namei_zone, cn.cn_pnbuf);
870290001Sglebius		cn.cn_flags &= ~HASBUF;
871290001Sglebius	}
872290001Sglebius
873290001Sglebius	return (error);
874290001Sglebius}
875290001Sglebius
876290001Sglebius/*
877290001Sglebius * Create a new vnode for create a new shadow file.
878290001Sglebius *
879290001Sglebius * If an error is returned, *vpp will be invalid, otherwise it will hold a
880290001Sglebius * locked, referenced and opened vnode.
881290001Sglebius *
882290001Sglebius * unp is never updated.
883290001Sglebius */
884290001Sglebiusstatic int
885290001Sglebiusunionfs_vn_create_on_upper(struct vnode **vpp, struct vnode *udvp,
886290001Sglebius			   struct unionfs_node *unp, struct vattr *uvap,
887290001Sglebius			   struct thread *td)
888290001Sglebius{
889290001Sglebius	struct unionfs_mount *ump;
890290001Sglebius	struct vnode   *vp;
891290001Sglebius	struct vnode   *lvp;
892290001Sglebius	struct ucred   *cred;
893290001Sglebius	struct vattr	lva;
894290001Sglebius	int		fmode;
895290001Sglebius	int		error;
896290001Sglebius	struct componentname cn;
897290001Sglebius
898290001Sglebius	ump = MOUNTTOUNIONFSMOUNT(UNIONFSTOV(unp)->v_mount);
899290001Sglebius	vp = NULLVP;
900290001Sglebius	lvp = unp->un_lowervp;
901290001Sglebius	cred = td->td_ucred;
902290001Sglebius	fmode = FFLAGS(O_WRONLY | O_CREAT | O_TRUNC | O_EXCL);
903290001Sglebius	error = 0;
904290001Sglebius
905290001Sglebius	if ((error = VOP_GETATTR(lvp, &lva, cred)) != 0)
906290001Sglebius		return (error);
907290001Sglebius	unionfs_create_uppervattr_core(ump, &lva, uvap, td);
908290001Sglebius
909290001Sglebius	if (unp->un_path == NULL)
910290001Sglebius		panic("unionfs: un_path is null");
911290001Sglebius
912290001Sglebius	cn.cn_namelen = strlen(unp->un_path);
913290001Sglebius	cn.cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK);
914290001Sglebius	bcopy(unp->un_path, cn.cn_pnbuf, cn.cn_namelen + 1);
915290001Sglebius	cn.cn_nameiop = CREATE;
916290001Sglebius	cn.cn_flags = (LOCKPARENT | LOCKLEAF | HASBUF | SAVENAME | ISLASTCN);
917290001Sglebius	cn.cn_lkflags = LK_EXCLUSIVE;
918290001Sglebius	cn.cn_thread = td;
919290001Sglebius	cn.cn_cred = cred;
920290001Sglebius	cn.cn_nameptr = cn.cn_pnbuf;
921290001Sglebius	cn.cn_consume = 0;
922290001Sglebius
923290001Sglebius	vref(udvp);
924290001Sglebius	if ((error = relookup(udvp, &vp, &cn)) != 0)
925290001Sglebius		goto unionfs_vn_create_on_upper_free_out2;
926290001Sglebius	vrele(udvp);
927290001Sglebius
928290001Sglebius	if (vp != NULLVP) {
929290001Sglebius		if (vp == udvp)
930290001Sglebius			vrele(vp);
931290001Sglebius		else
932290001Sglebius			vput(vp);
933290001Sglebius		error = EEXIST;
934290001Sglebius		goto unionfs_vn_create_on_upper_free_out1;
935290001Sglebius	}
936290001Sglebius
937290001Sglebius	if ((error = VOP_CREATE(udvp, &vp, &cn, uvap)) != 0)
938290001Sglebius		goto unionfs_vn_create_on_upper_free_out1;
939290001Sglebius
940290001Sglebius	if ((error = VOP_OPEN(vp, fmode, cred, td, NULL)) != 0) {
941290001Sglebius		vput(vp);
942290001Sglebius		goto unionfs_vn_create_on_upper_free_out1;
943290001Sglebius	}
944290001Sglebius	VOP_ADD_WRITECOUNT(vp, 1);
945290001Sglebius	CTR3(KTR_VFS, "%s: vp %p v_writecount increased to %d",  __func__, vp,
946290001Sglebius	    vp->v_writecount);
947290001Sglebius	*vpp = vp;
948290001Sglebius
949290001Sglebiusunionfs_vn_create_on_upper_free_out1:
950290001Sglebius	VOP_UNLOCK(udvp, LK_RELEASE);
951290001Sglebius
952290001Sglebiusunionfs_vn_create_on_upper_free_out2:
953290001Sglebius	if (cn.cn_flags & HASBUF) {
954290001Sglebius		uma_zfree(namei_zone, cn.cn_pnbuf);
955290001Sglebius		cn.cn_flags &= ~HASBUF;
956290001Sglebius	}
957290001Sglebius
958290001Sglebius	return (error);
959290001Sglebius}
960290001Sglebius
961290001Sglebius/*
962290001Sglebius * Copy from lvp to uvp.
963290001Sglebius *
964290001Sglebius * lvp and uvp should be locked and opened on entry and will be locked and
965290001Sglebius * opened on return.
966290001Sglebius */
967290001Sglebiusstatic int
968290001Sglebiusunionfs_copyfile_core(struct vnode *lvp, struct vnode *uvp,
969290001Sglebius		      struct ucred *cred, struct thread *td)
970290001Sglebius{
971290001Sglebius	int		error;
972290001Sglebius	off_t		offset;
973290001Sglebius	int		count;
974290001Sglebius	int		bufoffset;
975290001Sglebius	char           *buf;
976290001Sglebius	struct uio	uio;
977290001Sglebius	struct iovec	iov;
978290001Sglebius
979290001Sglebius	error = 0;
980290001Sglebius	memset(&uio, 0, sizeof(uio));
981290001Sglebius
982290001Sglebius	uio.uio_td = td;
983290001Sglebius	uio.uio_segflg = UIO_SYSSPACE;
984290001Sglebius	uio.uio_offset = 0;
985290001Sglebius
986290001Sglebius	buf = malloc(MAXBSIZE, M_TEMP, M_WAITOK);
987290001Sglebius
988290001Sglebius	while (error == 0) {
989290001Sglebius		offset = uio.uio_offset;
990290001Sglebius
991290001Sglebius		uio.uio_iov = &iov;
992290001Sglebius		uio.uio_iovcnt = 1;
993290001Sglebius		iov.iov_base = buf;
994290001Sglebius		iov.iov_len = MAXBSIZE;
995290001Sglebius		uio.uio_resid = iov.iov_len;
996290001Sglebius		uio.uio_rw = UIO_READ;
997290001Sglebius
998290001Sglebius		if ((error = VOP_READ(lvp, &uio, 0, cred)) != 0)
999290001Sglebius			break;
1000290001Sglebius		if ((count = MAXBSIZE - uio.uio_resid) == 0)
1001290001Sglebius			break;
1002290001Sglebius
1003290001Sglebius		bufoffset = 0;
1004290001Sglebius		while (bufoffset < count) {
1005290001Sglebius			uio.uio_iov = &iov;
1006290001Sglebius			uio.uio_iovcnt = 1;
1007290001Sglebius			iov.iov_base = buf + bufoffset;
1008290001Sglebius			iov.iov_len = count - bufoffset;
1009290001Sglebius			uio.uio_offset = offset + bufoffset;
1010290001Sglebius			uio.uio_resid = iov.iov_len;
1011290001Sglebius			uio.uio_rw = UIO_WRITE;
1012290001Sglebius
1013290001Sglebius			if ((error = VOP_WRITE(uvp, &uio, 0, cred)) != 0)
1014290001Sglebius				break;
1015290001Sglebius
1016290001Sglebius			bufoffset += (count - bufoffset) - uio.uio_resid;
1017290001Sglebius		}
1018290001Sglebius
1019290001Sglebius		uio.uio_offset = offset + bufoffset;
1020290001Sglebius	}
1021290001Sglebius
1022290001Sglebius	free(buf, M_TEMP);
1023290001Sglebius
1024290001Sglebius	return (error);
1025290001Sglebius}
1026290001Sglebius
1027290001Sglebius/*
1028290001Sglebius * Copy file from lower to upper.
1029290001Sglebius *
1030290001Sglebius * If you need copy of the contents, set 1 to docopy. Otherwise, set 0 to
1031290001Sglebius * docopy.
1032290001Sglebius *
1033290001Sglebius * If no error returned, unp will be updated.
1034290001Sglebius */
1035290001Sglebiusint
1036290001Sglebiusunionfs_copyfile(struct unionfs_node *unp, int docopy, struct ucred *cred,
1037290001Sglebius		 struct thread *td)
1038290001Sglebius{
1039290001Sglebius	int		error;
1040290001Sglebius	struct mount   *mp;
1041290001Sglebius	struct vnode   *udvp;
1042290001Sglebius	struct vnode   *lvp;
1043290001Sglebius	struct vnode   *uvp;
1044290001Sglebius	struct vattr	uva;
1045290001Sglebius
1046290001Sglebius	lvp = unp->un_lowervp;
1047290001Sglebius	uvp = NULLVP;
1048290001Sglebius
1049290001Sglebius	if ((UNIONFSTOV(unp)->v_mount->mnt_flag & MNT_RDONLY))
1050290001Sglebius		return (EROFS);
1051290001Sglebius	if (unp->un_dvp == NULLVP)
1052290001Sglebius		return (EINVAL);
1053290001Sglebius	if (unp->un_uppervp != NULLVP)
1054290001Sglebius		return (EEXIST);
1055290001Sglebius	udvp = VTOUNIONFS(unp->un_dvp)->un_uppervp;
1056290001Sglebius	if (udvp == NULLVP)
1057290001Sglebius		return (EROFS);
1058290001Sglebius	if ((udvp->v_mount->mnt_flag & MNT_RDONLY))
1059290001Sglebius		return (EROFS);
1060290001Sglebius
1061290001Sglebius	error = VOP_ACCESS(lvp, VREAD, cred, td);
1062290001Sglebius	if (error != 0)
1063290001Sglebius		return (error);
1064290001Sglebius
1065290001Sglebius	if ((error = vn_start_write(udvp, &mp, V_WAIT | PCATCH)) != 0)
1066290001Sglebius		return (error);
1067290001Sglebius	error = unionfs_vn_create_on_upper(&uvp, udvp, unp, &uva, td);
1068290001Sglebius	if (error != 0) {
1069290001Sglebius		vn_finished_write(mp);
1070290001Sglebius		return (error);
1071290001Sglebius	}
1072290001Sglebius
1073290001Sglebius	if (docopy != 0) {
1074290001Sglebius		error = VOP_OPEN(lvp, FREAD, cred, td, NULL);
1075290001Sglebius		if (error == 0) {
1076290001Sglebius			error = unionfs_copyfile_core(lvp, uvp, cred, td);
1077290001Sglebius			VOP_CLOSE(lvp, FREAD, cred, td);
1078290001Sglebius		}
1079290001Sglebius	}
1080290001Sglebius	VOP_CLOSE(uvp, FWRITE, cred, td);
1081290001Sglebius	VOP_ADD_WRITECOUNT(uvp, -1);
1082290001Sglebius	CTR3(KTR_VFS, "%s: vp %p v_writecount decreased to %d", __func__, uvp,
1083290001Sglebius	    uvp->v_writecount);
1084290001Sglebius
1085290001Sglebius	vn_finished_write(mp);
1086290001Sglebius
1087290001Sglebius	if (error == 0) {
1088290001Sglebius		/* Reset the attributes. Ignore errors. */
1089290001Sglebius		uva.va_type = VNON;
1090290001Sglebius		VOP_SETATTR(uvp, &uva, cred);
1091290001Sglebius	}
1092290001Sglebius
1093290001Sglebius	unionfs_node_update(unp, uvp, td);
1094290001Sglebius
1095290001Sglebius	return (error);
1096290001Sglebius}
1097290001Sglebius
1098290001Sglebius/*
1099290001Sglebius * It checks whether vp can rmdir. (check empty)
1100290001Sglebius *
1101290001Sglebius * vp is unionfs vnode.
1102290001Sglebius * vp should be locked.
1103290001Sglebius */
1104290001Sglebiusint
1105290001Sglebiusunionfs_check_rmdir(struct vnode *vp, struct ucred *cred, struct thread *td)
1106290001Sglebius{
1107290001Sglebius	int		error;
1108290001Sglebius	int		eofflag;
1109290001Sglebius	int		lookuperr;
1110290001Sglebius	struct vnode   *uvp;
1111290001Sglebius	struct vnode   *lvp;
1112290001Sglebius	struct vnode   *tvp;
1113290001Sglebius	struct vattr	va;
1114290001Sglebius	struct componentname cn;
1115290001Sglebius	/*
1116290001Sglebius	 * The size of buf needs to be larger than DIRBLKSIZ.
1117290001Sglebius	 */
1118290001Sglebius	char		buf[256 * 6];
1119290001Sglebius	struct dirent  *dp;
1120290001Sglebius	struct dirent  *edp;
1121290001Sglebius	struct uio	uio;
1122290001Sglebius	struct iovec	iov;
1123290001Sglebius
1124290001Sglebius	ASSERT_VOP_ELOCKED(vp, "unionfs_check_rmdir");
1125290001Sglebius
1126290001Sglebius	eofflag = 0;
1127290001Sglebius	uvp = UNIONFSVPTOUPPERVP(vp);
1128290001Sglebius	lvp = UNIONFSVPTOLOWERVP(vp);
1129290001Sglebius
1130290001Sglebius	/* check opaque */
1131290001Sglebius	if ((error = VOP_GETATTR(uvp, &va, cred)) != 0)
1132290001Sglebius		return (error);
1133290001Sglebius	if (va.va_flags & OPAQUE)
1134290001Sglebius		return (0);
1135290001Sglebius
1136290001Sglebius	/* open vnode */
1137290001Sglebius#ifdef MAC
1138290001Sglebius	if ((error = mac_vnode_check_open(cred, vp, VEXEC|VREAD)) != 0)
1139290001Sglebius		return (error);
1140290001Sglebius#endif
1141290001Sglebius	if ((error = VOP_ACCESS(vp, VEXEC|VREAD, cred, td)) != 0)
1142290001Sglebius		return (error);
1143290001Sglebius	if ((error = VOP_OPEN(vp, FREAD, cred, td, NULL)) != 0)
1144290001Sglebius		return (error);
1145290001Sglebius
1146290001Sglebius	uio.uio_rw = UIO_READ;
1147290001Sglebius	uio.uio_segflg = UIO_SYSSPACE;
1148290001Sglebius	uio.uio_td = td;
1149290001Sglebius	uio.uio_offset = 0;
1150290001Sglebius
1151290001Sglebius#ifdef MAC
1152290001Sglebius	error = mac_vnode_check_readdir(td->td_ucred, lvp);
1153290001Sglebius#endif
1154290001Sglebius	while (!error && !eofflag) {
1155290001Sglebius		iov.iov_base = buf;
1156290001Sglebius		iov.iov_len = sizeof(buf);
1157290001Sglebius		uio.uio_iov = &iov;
1158290001Sglebius		uio.uio_iovcnt = 1;
1159290001Sglebius		uio.uio_resid = iov.iov_len;
1160290001Sglebius
1161290001Sglebius		error = VOP_READDIR(lvp, &uio, cred, &eofflag, NULL, NULL);
1162290001Sglebius		if (error != 0)
1163290001Sglebius			break;
1164290001Sglebius		if (eofflag == 0 && uio.uio_resid == sizeof(buf)) {
1165290001Sglebius#ifdef DIAGNOSTIC
1166290001Sglebius			panic("bad readdir response from lower FS.");
1167290001Sglebius#endif
1168290001Sglebius			break;
1169290001Sglebius		}
1170290001Sglebius
1171290001Sglebius		edp = (struct dirent*)&buf[sizeof(buf) - uio.uio_resid];
1172290001Sglebius		for (dp = (struct dirent*)buf; !error && dp < edp;
1173290001Sglebius		     dp = (struct dirent*)((caddr_t)dp + dp->d_reclen)) {
1174290001Sglebius			if (dp->d_type == DT_WHT || dp->d_fileno == 0 ||
1175290001Sglebius			    (dp->d_namlen == 1 && dp->d_name[0] == '.') ||
1176290001Sglebius			    (dp->d_namlen == 2 && !bcmp(dp->d_name, "..", 2)))
1177290001Sglebius				continue;
1178290001Sglebius
1179290001Sglebius			cn.cn_namelen = dp->d_namlen;
1180290001Sglebius			cn.cn_pnbuf = NULL;
1181290001Sglebius			cn.cn_nameptr = dp->d_name;
1182290001Sglebius			cn.cn_nameiop = LOOKUP;
1183290001Sglebius			cn.cn_flags = (LOCKPARENT | LOCKLEAF | SAVENAME | RDONLY | ISLASTCN);
1184290001Sglebius			cn.cn_lkflags = LK_EXCLUSIVE;
1185290001Sglebius			cn.cn_thread = td;
1186290001Sglebius			cn.cn_cred = cred;
1187290001Sglebius			cn.cn_consume = 0;
1188290001Sglebius
1189290001Sglebius			/*
1190290001Sglebius			 * check entry in lower.
1191290001Sglebius			 * Sometimes, readdir function returns
1192290001Sglebius			 * wrong entry.
1193290001Sglebius			 */
1194290001Sglebius			lookuperr = VOP_LOOKUP(lvp, &tvp, &cn);
1195290001Sglebius
1196290001Sglebius			if (!lookuperr)
1197290001Sglebius				vput(tvp);
1198290001Sglebius			else
1199290001Sglebius				continue; /* skip entry */
1200290001Sglebius
1201290001Sglebius			/*
1202290001Sglebius			 * check entry
1203290001Sglebius			 * If it has no exist/whiteout entry in upper,
1204290001Sglebius			 * directory is not empty.
1205290001Sglebius			 */
1206290001Sglebius			cn.cn_flags = (LOCKPARENT | LOCKLEAF | SAVENAME | RDONLY | ISLASTCN);
1207290001Sglebius			lookuperr = VOP_LOOKUP(uvp, &tvp, &cn);
1208290001Sglebius
1209290001Sglebius			if (!lookuperr)
1210290001Sglebius				vput(tvp);
1211290001Sglebius
1212290001Sglebius			/* ignore exist or whiteout entry */
1213290001Sglebius			if (!lookuperr ||
1214290001Sglebius			    (lookuperr == ENOENT && (cn.cn_flags & ISWHITEOUT)))
1215290001Sglebius				continue;
1216290001Sglebius
1217290001Sglebius			error = ENOTEMPTY;
1218290001Sglebius		}
1219290001Sglebius	}
1220290001Sglebius
1221290001Sglebius	/* close vnode */
1222290001Sglebius	VOP_CLOSE(vp, FREAD, cred, td);
1223290001Sglebius
1224290001Sglebius	return (error);
1225290001Sglebius}
1226290001Sglebius
1227290001Sglebius#ifdef DIAGNOSTIC
1228290001Sglebius
1229290001Sglebiusstruct vnode   *
1230290001Sglebiusunionfs_checkuppervp(struct vnode *vp, char *fil, int lno)
1231290001Sglebius{
1232290001Sglebius	struct unionfs_node *unp;
1233290001Sglebius
1234290001Sglebius	unp = VTOUNIONFS(vp);
1235290001Sglebius
1236290001Sglebius#ifdef notyet
1237290001Sglebius	if (vp->v_op != unionfs_vnodeop_p) {
1238290001Sglebius		printf("unionfs_checkuppervp: on non-unionfs-node.\n");
1239290001Sglebius#ifdef KDB
1240290001Sglebius		kdb_enter(KDB_WHY_UNIONFS,
1241290001Sglebius		    "unionfs_checkuppervp: on non-unionfs-node.\n");
1242290001Sglebius#endif
1243290001Sglebius		panic("unionfs_checkuppervp");
1244290001Sglebius	};
1245290001Sglebius#endif
1246290001Sglebius	return (unp->un_uppervp);
1247290001Sglebius}
1248290001Sglebius
1249290001Sglebiusstruct vnode   *
1250290001Sglebiusunionfs_checklowervp(struct vnode *vp, char *fil, int lno)
1251290001Sglebius{
1252290001Sglebius	struct unionfs_node *unp;
1253290001Sglebius
1254290001Sglebius	unp = VTOUNIONFS(vp);
1255290001Sglebius
1256290001Sglebius#ifdef notyet
1257290001Sglebius	if (vp->v_op != unionfs_vnodeop_p) {
1258290001Sglebius		printf("unionfs_checklowervp: on non-unionfs-node.\n");
1259290001Sglebius#ifdef KDB
1260290001Sglebius		kdb_enter(KDB_WHY_UNIONFS,
1261290001Sglebius		    "unionfs_checklowervp: on non-unionfs-node.\n");
1262290001Sglebius#endif
1263290001Sglebius		panic("unionfs_checklowervp");
1264290001Sglebius	};
1265290001Sglebius#endif
1266290001Sglebius	return (unp->un_lowervp);
1267290001Sglebius}
1268290001Sglebius#endif
1269290001Sglebius