xref: /freebsd/sys/fs/unionfs/union_subr.c (revision cb05b60a8982e5497cd30449710deb2f4be653d4)
1d167cf6fSWarner Losh /*-
2df8bae1dSRodney W. Grimes  * Copyright (c) 1994 Jan-Simon Pendry
3df8bae1dSRodney W. Grimes  * Copyright (c) 1994
4df8bae1dSRodney W. Grimes  *	The Regents of the University of California.  All rights reserved.
5d00947d8SCraig Rodrigues  * Copyright (c) 2005, 2006 Masanori Ozawa <ozawa@ongs.co.jp>, ONGS Inc.
6d00947d8SCraig Rodrigues  * Copyright (c) 2006 Daichi Goto <daichi@freebsd.org>
7df8bae1dSRodney W. Grimes  *
8df8bae1dSRodney W. Grimes  * This code is derived from software contributed to Berkeley by
9df8bae1dSRodney W. Grimes  * Jan-Simon Pendry.
10df8bae1dSRodney W. Grimes  *
11df8bae1dSRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
12df8bae1dSRodney W. Grimes  * modification, are permitted provided that the following conditions
13df8bae1dSRodney W. Grimes  * are met:
14df8bae1dSRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
15df8bae1dSRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
16df8bae1dSRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
17df8bae1dSRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
18df8bae1dSRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
19df8bae1dSRodney W. Grimes  * 4. Neither the name of the University nor the names of its contributors
20df8bae1dSRodney W. Grimes  *    may be used to endorse or promote products derived from this software
21df8bae1dSRodney W. Grimes  *    without specific prior written permission.
22df8bae1dSRodney W. Grimes  *
23df8bae1dSRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24df8bae1dSRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25df8bae1dSRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26df8bae1dSRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27df8bae1dSRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28df8bae1dSRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29df8bae1dSRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30df8bae1dSRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31df8bae1dSRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32df8bae1dSRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33df8bae1dSRodney W. Grimes  * SUCH DAMAGE.
34df8bae1dSRodney W. Grimes  *
35996c772fSJohn Dyson  *	@(#)union_subr.c	8.20 (Berkeley) 5/20/95
36c3aac50fSPeter Wemm  * $FreeBSD$
37df8bae1dSRodney W. Grimes  */
38df8bae1dSRodney W. Grimes 
39df8bae1dSRodney W. Grimes #include <sys/param.h>
40df8bae1dSRodney W. Grimes #include <sys/systm.h>
41fb919e4dSMark Murray #include <sys/kernel.h>
42fb919e4dSMark Murray #include <sys/lock.h>
432178ff8bSJohn Baldwin #include <sys/mutex.h>
44d00947d8SCraig Rodrigues #include <sys/malloc.h>
45d00947d8SCraig Rodrigues #include <sys/mount.h>
46fb919e4dSMark Murray #include <sys/namei.h>
47d00947d8SCraig Rodrigues #include <sys/proc.h>
48fb919e4dSMark Murray #include <sys/vnode.h>
49d00947d8SCraig Rodrigues #include <sys/dirent.h>
50d00947d8SCraig Rodrigues #include <sys/fcntl.h>
51d00947d8SCraig Rodrigues #include <sys/filedesc.h>
52d00947d8SCraig Rodrigues #include <sys/stat.h>
53d00947d8SCraig Rodrigues #include <sys/resourcevar.h>
54fb919e4dSMark Murray 
55d00947d8SCraig Rodrigues #ifdef MAC
56d00947d8SCraig Rodrigues #include <sys/mac.h>
57d00947d8SCraig Rodrigues #endif
58d00947d8SCraig Rodrigues 
598396dd9eSJeff Roberson #include <vm/uma.h>
60fb919e4dSMark Murray 
6199d300a1SRuslan Ermilov #include <fs/unionfs/union.h>
62df8bae1dSRodney W. Grimes 
63d00947d8SCraig Rodrigues MALLOC_DEFINE(M_UNIONFSNODE, "UNIONFS node", "UNIONFS vnode private part");
64d00947d8SCraig Rodrigues MALLOC_DEFINE(M_UNIONFSPATH, "UNIONFS path", "UNIONFS path private part");
65df8bae1dSRodney W. Grimes 
66d00947d8SCraig Rodrigues /*
67dc2dd185SDaichi GOTO  * Initialize
68d00947d8SCraig Rodrigues  */
69df8bae1dSRodney W. Grimes int
70d00947d8SCraig Rodrigues unionfs_init(struct vfsconf *vfsp)
71df8bae1dSRodney W. Grimes {
72d00947d8SCraig Rodrigues 	UNIONFSDEBUG("unionfs_init\n");	/* printed during system boot */
7326f9a767SRodney W. Grimes 	return (0);
74df8bae1dSRodney W. Grimes }
75df8bae1dSRodney W. Grimes 
76d00947d8SCraig Rodrigues /*
77dc2dd185SDaichi GOTO  * Uninitialize
78d00947d8SCraig Rodrigues  */
79d00947d8SCraig Rodrigues int
80d00947d8SCraig Rodrigues unionfs_uninit(struct vfsconf *vfsp)
81df8bae1dSRodney W. Grimes {
82df8bae1dSRodney W. Grimes 	return (0);
83df8bae1dSRodney W. Grimes }
84df8bae1dSRodney W. Grimes 
85d00947d8SCraig Rodrigues /*
86d00947d8SCraig Rodrigues  * Make a new or get existing unionfs node.
872a31267eSMatthew Dillon  *
88d00947d8SCraig Rodrigues  * uppervp and lowervp should be unlocked. Because if new unionfs vnode is
89d00947d8SCraig Rodrigues  * locked, uppervp or lowervp is locked too. In order to prevent dead lock,
90d00947d8SCraig Rodrigues  * you should not lock plurality simultaneously.
91df8bae1dSRodney W. Grimes  */
92d00947d8SCraig Rodrigues int
93d00947d8SCraig Rodrigues unionfs_nodeget(struct mount *mp, struct vnode *uppervp,
94d00947d8SCraig Rodrigues 		struct vnode *lowervp, struct vnode *dvp,
95d00947d8SCraig Rodrigues 		struct vnode **vpp, struct componentname *cnp,
96d00947d8SCraig Rodrigues 		struct thread *td)
97d00947d8SCraig Rodrigues {
98d00947d8SCraig Rodrigues 	struct unionfs_mount *ump;
99d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
100d00947d8SCraig Rodrigues 	struct vnode   *vp;
101d00947d8SCraig Rodrigues 	int		error;
102d00947d8SCraig Rodrigues 	int		lkflags;
103d00947d8SCraig Rodrigues 	char	       *path;
104df8bae1dSRodney W. Grimes 
105d00947d8SCraig Rodrigues 	ump = MOUNTTOUNIONFSMOUNT(mp);
106d00947d8SCraig Rodrigues 	lkflags = (cnp ? cnp->cn_lkflags : 0);
107dc2dd185SDaichi GOTO 	path = (cnp ? cnp->cn_nameptr : NULL);
1082a31267eSMatthew Dillon 
109d00947d8SCraig Rodrigues 	if (uppervp == NULLVP && lowervp == NULLVP)
110d00947d8SCraig Rodrigues 		panic("unionfs_nodeget: upper and lower is null");
111d00947d8SCraig Rodrigues 
112d00947d8SCraig Rodrigues 	/* If it has no ISLASTCN flag, path check is skipped. */
113dc2dd185SDaichi GOTO 	if (cnp && !(cnp->cn_flags & ISLASTCN))
114d00947d8SCraig Rodrigues 		path = NULL;
115d00947d8SCraig Rodrigues 
116d00947d8SCraig Rodrigues 	if ((uppervp == NULLVP || ump->um_uppervp != uppervp) ||
117d00947d8SCraig Rodrigues 	    (lowervp == NULLVP || ump->um_lowervp != lowervp)) {
118d00947d8SCraig Rodrigues 		if (dvp == NULLVP)
119d00947d8SCraig Rodrigues 			return (EINVAL);
120d00947d8SCraig Rodrigues 	}
121d00947d8SCraig Rodrigues 
122df8bae1dSRodney W. Grimes 	/*
123d00947d8SCraig Rodrigues 	 * Do the MALLOC before the getnewvnode since doing so afterward
124d00947d8SCraig Rodrigues 	 * might cause a bogus v_data pointer to get dereferenced elsewhere
125d00947d8SCraig Rodrigues 	 * if MALLOC should block.
126df8bae1dSRodney W. Grimes 	 */
127d00947d8SCraig Rodrigues 	MALLOC(unp, struct unionfs_node *, sizeof(struct unionfs_node),
128d00947d8SCraig Rodrigues 	    M_UNIONFSNODE, M_WAITOK | M_ZERO);
129d00947d8SCraig Rodrigues 
130d00947d8SCraig Rodrigues 	error = getnewvnode("unionfs", mp, &unionfs_vnodeops, &vp);
131dc2dd185SDaichi GOTO 	if (error != 0) {
132d00947d8SCraig Rodrigues 		FREE(unp, M_UNIONFSNODE);
133d00947d8SCraig Rodrigues 		return (error);
134d00947d8SCraig Rodrigues 	}
13561b9d89fSTor Egge 	error = insmntque(vp, mp);	/* XXX: Too early for mpsafe fs */
13661b9d89fSTor Egge 	if (error != 0) {
13761b9d89fSTor Egge 		FREE(unp, M_UNIONFSNODE);
13861b9d89fSTor Egge 		return (error);
13961b9d89fSTor Egge 	}
140d00947d8SCraig Rodrigues 	if (dvp != NULLVP)
141d00947d8SCraig Rodrigues 		vref(dvp);
142d00947d8SCraig Rodrigues 	if (uppervp != NULLVP)
143d00947d8SCraig Rodrigues 		vref(uppervp);
144d00947d8SCraig Rodrigues 	if (lowervp != NULLVP)
145d00947d8SCraig Rodrigues 		vref(lowervp);
146d00947d8SCraig Rodrigues 
147d00947d8SCraig Rodrigues 	unp->un_vnode = vp;
148d00947d8SCraig Rodrigues 	unp->un_uppervp = uppervp;
149d00947d8SCraig Rodrigues 	unp->un_lowervp = lowervp;
150d00947d8SCraig Rodrigues 	unp->un_dvp = dvp;
151d00947d8SCraig Rodrigues 	if (uppervp != NULLVP)
152d00947d8SCraig Rodrigues 		vp->v_vnlock = uppervp->v_vnlock;
153d00947d8SCraig Rodrigues 	else
154d00947d8SCraig Rodrigues 		vp->v_vnlock = lowervp->v_vnlock;
155d00947d8SCraig Rodrigues 
156dc2dd185SDaichi GOTO 	if (path != NULL) {
157d00947d8SCraig Rodrigues 		unp->un_path = (char *)
158d00947d8SCraig Rodrigues 		    malloc(cnp->cn_namelen +1, M_UNIONFSPATH, M_WAITOK|M_ZERO);
159d00947d8SCraig Rodrigues 		bcopy(cnp->cn_nameptr, unp->un_path, cnp->cn_namelen);
160d00947d8SCraig Rodrigues 		unp->un_path[cnp->cn_namelen] = '\0';
161d00947d8SCraig Rodrigues 	}
162d00947d8SCraig Rodrigues 	vp->v_type = (uppervp != NULLVP ? uppervp->v_type : lowervp->v_type);
163d00947d8SCraig Rodrigues 	vp->v_data = unp;
164d00947d8SCraig Rodrigues 
165d00947d8SCraig Rodrigues 	if ((uppervp != NULLVP && ump->um_uppervp == uppervp) &&
166d00947d8SCraig Rodrigues 	    (lowervp != NULLVP && ump->um_lowervp == lowervp))
167d00947d8SCraig Rodrigues 		vp->v_vflag |= VV_ROOT;
168d00947d8SCraig Rodrigues 
169d00947d8SCraig Rodrigues 	if (lkflags & LK_TYPE_MASK)
170cb05b60aSAttilio Rao 		vn_lock(vp, lkflags | LK_RETRY);
171df8bae1dSRodney W. Grimes 
172d00947d8SCraig Rodrigues 	*vpp = vp;
173d00947d8SCraig Rodrigues 
174d00947d8SCraig Rodrigues 	return (0);
175996c772fSJohn Dyson }
176df8bae1dSRodney W. Grimes 
1772a31267eSMatthew Dillon /*
178dc2dd185SDaichi GOTO  * Clean up the unionfs node.
1792a31267eSMatthew Dillon  */
180d00947d8SCraig Rodrigues void
181dc2dd185SDaichi GOTO unionfs_noderem(struct vnode *vp, struct thread *td)
182d00947d8SCraig Rodrigues {
183d00947d8SCraig Rodrigues 	int		vfslocked;
184d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
185acc4bab1SCraig Rodrigues 	struct unionfs_node_status *unsp, *unsp_tmp;
186d00947d8SCraig Rodrigues 	struct vnode   *lvp;
187d00947d8SCraig Rodrigues 	struct vnode   *uvp;
1882a31267eSMatthew Dillon 
1892a31267eSMatthew Dillon 	/*
190d00947d8SCraig Rodrigues 	 * Use the interlock to protect the clearing of v_data to
191d00947d8SCraig Rodrigues 	 * prevent faults in unionfs_lock().
1922a31267eSMatthew Dillon 	 */
193d00947d8SCraig Rodrigues 	VI_LOCK(vp);
194d00947d8SCraig Rodrigues 	unp = VTOUNIONFS(vp);
195d00947d8SCraig Rodrigues 	lvp = unp->un_lowervp;
196d00947d8SCraig Rodrigues 	uvp = unp->un_uppervp;
197d00947d8SCraig Rodrigues 	unp->un_lowervp = unp->un_uppervp = NULLVP;
198d00947d8SCraig Rodrigues 
199d00947d8SCraig Rodrigues 	vp->v_vnlock = &(vp->v_lock);
200d00947d8SCraig Rodrigues 	vp->v_data = NULL;
201d00947d8SCraig Rodrigues 	lockmgr(vp->v_vnlock, LK_EXCLUSIVE | LK_INTERLOCK, VI_MTX(vp), td);
202d00947d8SCraig Rodrigues 	if (lvp != NULLVP)
203d00947d8SCraig Rodrigues 		VOP_UNLOCK(lvp, 0, td);
204d00947d8SCraig Rodrigues 	if (uvp != NULLVP)
205d00947d8SCraig Rodrigues 		VOP_UNLOCK(uvp, 0, td);
206d00947d8SCraig Rodrigues 	vp->v_object = NULL;
207d00947d8SCraig Rodrigues 
208d00947d8SCraig Rodrigues 	if (lvp != NULLVP) {
209d00947d8SCraig Rodrigues 		vfslocked = VFS_LOCK_GIANT(lvp->v_mount);
210d00947d8SCraig Rodrigues 		vrele(lvp);
211d00947d8SCraig Rodrigues 		VFS_UNLOCK_GIANT(vfslocked);
212d00947d8SCraig Rodrigues 	}
213d00947d8SCraig Rodrigues 	if (uvp != NULLVP) {
214d00947d8SCraig Rodrigues 		vfslocked = VFS_LOCK_GIANT(uvp->v_mount);
215d00947d8SCraig Rodrigues 		vrele(uvp);
216d00947d8SCraig Rodrigues 		VFS_UNLOCK_GIANT(vfslocked);
217d00947d8SCraig Rodrigues 	}
218d00947d8SCraig Rodrigues 	if (unp->un_dvp != NULLVP) {
219d00947d8SCraig Rodrigues 		vfslocked = VFS_LOCK_GIANT(unp->un_dvp->v_mount);
220d00947d8SCraig Rodrigues 		vrele(unp->un_dvp);
221d00947d8SCraig Rodrigues 		VFS_UNLOCK_GIANT(vfslocked);
222d00947d8SCraig Rodrigues 		unp->un_dvp = NULLVP;
223d00947d8SCraig Rodrigues 	}
224d00947d8SCraig Rodrigues 	if (unp->un_path) {
225d00947d8SCraig Rodrigues 		free(unp->un_path, M_UNIONFSPATH);
226d00947d8SCraig Rodrigues 		unp->un_path = NULL;
227d00947d8SCraig Rodrigues 	}
228acc4bab1SCraig Rodrigues 
229acc4bab1SCraig Rodrigues 	LIST_FOREACH_SAFE(unsp, &(unp->un_unshead), uns_list, unsp_tmp) {
230d00947d8SCraig Rodrigues 		LIST_REMOVE(unsp, uns_list);
231d00947d8SCraig Rodrigues 		free(unsp, M_TEMP);
232d00947d8SCraig Rodrigues 	}
233d00947d8SCraig Rodrigues 	FREE(unp, M_UNIONFSNODE);
234df8bae1dSRodney W. Grimes }
235df8bae1dSRodney W. Grimes 
236d00947d8SCraig Rodrigues /*
237d00947d8SCraig Rodrigues  * Get the unionfs node status.
238d00947d8SCraig Rodrigues  * You need exclusive lock this vnode.
239d00947d8SCraig Rodrigues  */
240d00947d8SCraig Rodrigues void
241d00947d8SCraig Rodrigues unionfs_get_node_status(struct unionfs_node *unp, struct thread *td,
242d00947d8SCraig Rodrigues 			struct unionfs_node_status **unspp)
243d00947d8SCraig Rodrigues {
244d00947d8SCraig Rodrigues 	struct unionfs_node_status *unsp;
245df8bae1dSRodney W. Grimes 
246d00947d8SCraig Rodrigues 	KASSERT(NULL != unspp, ("null pointer"));
247d00947d8SCraig Rodrigues 	ASSERT_VOP_ELOCKED(UNIONFSTOV(unp), "unionfs_get_node_status");
2482a31267eSMatthew Dillon 
249d00947d8SCraig Rodrigues 	LIST_FOREACH(unsp, &(unp->un_unshead), uns_list) {
250d00947d8SCraig Rodrigues 		if (unsp->uns_tid == td->td_tid) {
251d00947d8SCraig Rodrigues 			*unspp = unsp;
252d00947d8SCraig Rodrigues 			return;
253d00947d8SCraig Rodrigues 		}
254d00947d8SCraig Rodrigues 	}
2552a31267eSMatthew Dillon 
256d00947d8SCraig Rodrigues 	/* create a new unionfs node status */
257d00947d8SCraig Rodrigues 	MALLOC(unsp, struct unionfs_node_status *,
258d00947d8SCraig Rodrigues 	    sizeof(struct unionfs_node_status), M_TEMP, M_WAITOK | M_ZERO);
2592a31267eSMatthew Dillon 
260d00947d8SCraig Rodrigues 	unsp->uns_tid = td->td_tid;
261d00947d8SCraig Rodrigues 	LIST_INSERT_HEAD(&(unp->un_unshead), unsp, uns_list);
2622a31267eSMatthew Dillon 
263d00947d8SCraig Rodrigues 	*unspp = unsp;
264d00947d8SCraig Rodrigues }
265d00947d8SCraig Rodrigues 
266d00947d8SCraig Rodrigues /*
267d00947d8SCraig Rodrigues  * Remove the unionfs node status, if you can.
268d00947d8SCraig Rodrigues  * You need exclusive lock this vnode.
269d00947d8SCraig Rodrigues  */
270d00947d8SCraig Rodrigues void
271d00947d8SCraig Rodrigues unionfs_tryrem_node_status(struct unionfs_node *unp, struct thread *td,
272d00947d8SCraig Rodrigues 			   struct unionfs_node_status *unsp)
273d00947d8SCraig Rodrigues {
274d00947d8SCraig Rodrigues 	KASSERT(NULL != unsp, ("null pointer"));
275d00947d8SCraig Rodrigues 	ASSERT_VOP_ELOCKED(UNIONFSTOV(unp), "unionfs_get_node_status");
276d00947d8SCraig Rodrigues 
277d00947d8SCraig Rodrigues 	if (0 < unsp->uns_lower_opencnt || 0 < unsp->uns_upper_opencnt)
278d00947d8SCraig Rodrigues 		return;
279d00947d8SCraig Rodrigues 
280d00947d8SCraig Rodrigues 	LIST_REMOVE(unsp, uns_list);
281d00947d8SCraig Rodrigues 	free(unsp, M_TEMP);
282d00947d8SCraig Rodrigues }
283d00947d8SCraig Rodrigues 
284d00947d8SCraig Rodrigues /*
285d00947d8SCraig Rodrigues  * Create upper node attr.
286d00947d8SCraig Rodrigues  */
287d00947d8SCraig Rodrigues void
288d00947d8SCraig Rodrigues unionfs_create_uppervattr_core(struct unionfs_mount *ump,
289d00947d8SCraig Rodrigues 			       struct vattr *lva,
290d00947d8SCraig Rodrigues 			       struct vattr *uva,
291d00947d8SCraig Rodrigues 			       struct thread *td)
292d00947d8SCraig Rodrigues {
293d00947d8SCraig Rodrigues 	VATTR_NULL(uva);
294d00947d8SCraig Rodrigues 	uva->va_type = lva->va_type;
295d00947d8SCraig Rodrigues 	uva->va_atime = lva->va_atime;
296d00947d8SCraig Rodrigues 	uva->va_mtime = lva->va_mtime;
297d00947d8SCraig Rodrigues 	uva->va_ctime = lva->va_ctime;
298d00947d8SCraig Rodrigues 
299d00947d8SCraig Rodrigues 	switch (ump->um_copymode) {
300d00947d8SCraig Rodrigues 	case UNIONFS_TRANSPARENT:
301d00947d8SCraig Rodrigues 		uva->va_mode = lva->va_mode;
302d00947d8SCraig Rodrigues 		uva->va_uid = lva->va_uid;
303d00947d8SCraig Rodrigues 		uva->va_gid = lva->va_gid;
304d00947d8SCraig Rodrigues 		break;
305d00947d8SCraig Rodrigues 	case UNIONFS_MASQUERADE:
306d00947d8SCraig Rodrigues 		if (ump->um_uid == lva->va_uid) {
307d00947d8SCraig Rodrigues 			uva->va_mode = lva->va_mode & 077077;
308d00947d8SCraig Rodrigues 			uva->va_mode |= (lva->va_type == VDIR ? ump->um_udir : ump->um_ufile) & 0700;
309d00947d8SCraig Rodrigues 			uva->va_uid = lva->va_uid;
310d00947d8SCraig Rodrigues 			uva->va_gid = lva->va_gid;
311df8bae1dSRodney W. Grimes 		} else {
312d00947d8SCraig Rodrigues 			uva->va_mode = (lva->va_type == VDIR ? ump->um_udir : ump->um_ufile);
313d00947d8SCraig Rodrigues 			uva->va_uid = ump->um_uid;
314d00947d8SCraig Rodrigues 			uva->va_gid = ump->um_gid;
315d00947d8SCraig Rodrigues 		}
316d00947d8SCraig Rodrigues 		break;
317d00947d8SCraig Rodrigues 	default:		/* UNIONFS_TRADITIONAL */
3185e3f7694SRobert Watson 		FILEDESC_SLOCK(td->td_proc->p_fd);
319d00947d8SCraig Rodrigues 		uva->va_mode = 0777 & ~td->td_proc->p_fd->fd_cmask;
3205e3f7694SRobert Watson 		FILEDESC_SUNLOCK(td->td_proc->p_fd);
321d00947d8SCraig Rodrigues 		uva->va_uid = ump->um_uid;
322d00947d8SCraig Rodrigues 		uva->va_gid = ump->um_gid;
323d00947d8SCraig Rodrigues 		break;
324d00947d8SCraig Rodrigues 	}
325df8bae1dSRodney W. Grimes }
326df8bae1dSRodney W. Grimes 
327d00947d8SCraig Rodrigues /*
328d00947d8SCraig Rodrigues  * Create upper node attr.
329d00947d8SCraig Rodrigues  */
330d00947d8SCraig Rodrigues int
331d00947d8SCraig Rodrigues unionfs_create_uppervattr(struct unionfs_mount *ump,
332d00947d8SCraig Rodrigues 			  struct vnode *lvp,
333d00947d8SCraig Rodrigues 			  struct vattr *uva,
334d00947d8SCraig Rodrigues 			  struct ucred *cred,
335d00947d8SCraig Rodrigues 			  struct thread *td)
336d00947d8SCraig Rodrigues {
337d00947d8SCraig Rodrigues 	int		error;
338d00947d8SCraig Rodrigues 	struct vattr	lva;
339df8bae1dSRodney W. Grimes 
340d00947d8SCraig Rodrigues 	if ((error = VOP_GETATTR(lvp, &lva, cred, td)))
341d00947d8SCraig Rodrigues 		return (error);
342d00947d8SCraig Rodrigues 
343d00947d8SCraig Rodrigues 	unionfs_create_uppervattr_core(ump, &lva, uva, td);
344df8bae1dSRodney W. Grimes 
345df8bae1dSRodney W. Grimes 	return (error);
346df8bae1dSRodney W. Grimes }
347df8bae1dSRodney W. Grimes 
348d00947d8SCraig Rodrigues /*
349d00947d8SCraig Rodrigues  * relookup
350d00947d8SCraig Rodrigues  *
351d00947d8SCraig Rodrigues  * dvp should be locked on entry and will be locked on return.
352d00947d8SCraig Rodrigues  *
353d00947d8SCraig Rodrigues  * If an error is returned, *vpp will be invalid, otherwise it will hold a
354d00947d8SCraig Rodrigues  * locked, referenced vnode. If *vpp == dvp then remember that only one
355d00947d8SCraig Rodrigues  * LK_EXCLUSIVE lock is held.
356d00947d8SCraig Rodrigues  */
357d00947d8SCraig Rodrigues static int
358d00947d8SCraig Rodrigues unionfs_relookup(struct vnode *dvp, struct vnode **vpp,
359d00947d8SCraig Rodrigues 		 struct componentname *cnp, struct componentname *cn,
360d00947d8SCraig Rodrigues 		 struct thread *td, char *path, int pathlen, u_long nameiop)
361df8bae1dSRodney W. Grimes {
362d00947d8SCraig Rodrigues 	int	error;
363df8bae1dSRodney W. Grimes 
364d00947d8SCraig Rodrigues 	cn->cn_namelen = pathlen;
365d00947d8SCraig Rodrigues 	cn->cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK);
366d00947d8SCraig Rodrigues 	bcopy(path, cn->cn_pnbuf, pathlen);
367d00947d8SCraig Rodrigues 	cn->cn_pnbuf[pathlen] = '\0';
368df8bae1dSRodney W. Grimes 
369d00947d8SCraig Rodrigues 	cn->cn_nameiop = nameiop;
370d00947d8SCraig Rodrigues 	cn->cn_flags = (LOCKPARENT | LOCKLEAF | HASBUF | SAVENAME | ISLASTCN);
371d00947d8SCraig Rodrigues 	cn->cn_lkflags = LK_EXCLUSIVE;
372d00947d8SCraig Rodrigues 	cn->cn_thread = td;
373d00947d8SCraig Rodrigues 	cn->cn_cred = cnp->cn_cred;
374df8bae1dSRodney W. Grimes 
375d00947d8SCraig Rodrigues 	cn->cn_nameptr = cn->cn_pnbuf;
376d00947d8SCraig Rodrigues 	cn->cn_consume = cnp->cn_consume;
377df8bae1dSRodney W. Grimes 
378d00947d8SCraig Rodrigues 	if (nameiop == DELETE)
379d00947d8SCraig Rodrigues 		cn->cn_flags |= (cnp->cn_flags & (DOWHITEOUT | SAVESTART));
380d00947d8SCraig Rodrigues 	else if (RENAME == nameiop)
381d00947d8SCraig Rodrigues 		cn->cn_flags |= (cnp->cn_flags & SAVESTART);
382d00947d8SCraig Rodrigues 
383d00947d8SCraig Rodrigues 	vref(dvp);
384d00947d8SCraig Rodrigues 	VOP_UNLOCK(dvp, 0, td);
385d00947d8SCraig Rodrigues 
386d00947d8SCraig Rodrigues 	if ((error = relookup(dvp, vpp, cn))) {
387d00947d8SCraig Rodrigues 		uma_zfree(namei_zone, cn->cn_pnbuf);
388d00947d8SCraig Rodrigues 		cn->cn_flags &= ~HASBUF;
389cb05b60aSAttilio Rao 		vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
390d00947d8SCraig Rodrigues 	} else
391d00947d8SCraig Rodrigues 		vrele(dvp);
392d00947d8SCraig Rodrigues 
393d00947d8SCraig Rodrigues 	return (error);
394df8bae1dSRodney W. Grimes }
395df8bae1dSRodney W. Grimes 
396df8bae1dSRodney W. Grimes /*
397d00947d8SCraig Rodrigues  * relookup for CREATE namei operation.
3982a31267eSMatthew Dillon  *
399d00947d8SCraig Rodrigues  * dvp is unionfs vnode. dvp should be locked.
400d00947d8SCraig Rodrigues  *
401d00947d8SCraig Rodrigues  * If it called 'unionfs_copyfile' function by unionfs_link etc,
402d00947d8SCraig Rodrigues  * VOP_LOOKUP information is broken.
403d00947d8SCraig Rodrigues  * So it need relookup in order to create link etc.
404d00947d8SCraig Rodrigues  */
405d00947d8SCraig Rodrigues int
406d00947d8SCraig Rodrigues unionfs_relookup_for_create(struct vnode *dvp, struct componentname *cnp,
407d00947d8SCraig Rodrigues 			    struct thread *td)
408d00947d8SCraig Rodrigues {
409d00947d8SCraig Rodrigues 	int	error;
410d00947d8SCraig Rodrigues 	struct vnode *udvp;
411d00947d8SCraig Rodrigues 	struct vnode *vp;
412d00947d8SCraig Rodrigues 	struct componentname cn;
413d00947d8SCraig Rodrigues 
414d00947d8SCraig Rodrigues 	udvp = UNIONFSVPTOUPPERVP(dvp);
415d00947d8SCraig Rodrigues 	vp = NULLVP;
416d00947d8SCraig Rodrigues 
417d00947d8SCraig Rodrigues 	error = unionfs_relookup(udvp, &vp, cnp, &cn, td, cnp->cn_nameptr,
418d00947d8SCraig Rodrigues 	    strlen(cnp->cn_nameptr), CREATE);
419d00947d8SCraig Rodrigues 	if (error)
420d00947d8SCraig Rodrigues 		return (error);
421d00947d8SCraig Rodrigues 
422d00947d8SCraig Rodrigues 	if (vp != NULLVP) {
423d00947d8SCraig Rodrigues 		if (udvp == vp)
424d00947d8SCraig Rodrigues 			vrele(vp);
425d00947d8SCraig Rodrigues 		else
426d00947d8SCraig Rodrigues 			vput(vp);
427d00947d8SCraig Rodrigues 
428d00947d8SCraig Rodrigues 		error = EEXIST;
429d00947d8SCraig Rodrigues 	}
430d00947d8SCraig Rodrigues 
431d00947d8SCraig Rodrigues 	if (cn.cn_flags & HASBUF) {
432d00947d8SCraig Rodrigues 		uma_zfree(namei_zone, cn.cn_pnbuf);
433d00947d8SCraig Rodrigues 		cn.cn_flags &= ~HASBUF;
434d00947d8SCraig Rodrigues 	}
435d00947d8SCraig Rodrigues 
436d00947d8SCraig Rodrigues 	if (!error) {
437d00947d8SCraig Rodrigues 		cn.cn_flags |= (cnp->cn_flags & HASBUF);
438d00947d8SCraig Rodrigues 		cnp->cn_flags = cn.cn_flags;
439d00947d8SCraig Rodrigues 	}
440d00947d8SCraig Rodrigues 
441d00947d8SCraig Rodrigues 	return (error);
442d00947d8SCraig Rodrigues }
443d00947d8SCraig Rodrigues 
444d00947d8SCraig Rodrigues /*
445d00947d8SCraig Rodrigues  * relookup for DELETE namei operation.
446d00947d8SCraig Rodrigues  *
447d00947d8SCraig Rodrigues  * dvp is unionfs vnode. dvp should be locked.
448d00947d8SCraig Rodrigues  */
449d00947d8SCraig Rodrigues int
450d00947d8SCraig Rodrigues unionfs_relookup_for_delete(struct vnode *dvp, struct componentname *cnp,
451d00947d8SCraig Rodrigues 			    struct thread *td)
452d00947d8SCraig Rodrigues {
453d00947d8SCraig Rodrigues 	int	error;
454d00947d8SCraig Rodrigues 	struct vnode *udvp;
455d00947d8SCraig Rodrigues 	struct vnode *vp;
456d00947d8SCraig Rodrigues 	struct componentname cn;
457d00947d8SCraig Rodrigues 
458d00947d8SCraig Rodrigues 	udvp = UNIONFSVPTOUPPERVP(dvp);
459d00947d8SCraig Rodrigues 	vp = NULLVP;
460d00947d8SCraig Rodrigues 
461d00947d8SCraig Rodrigues 	error = unionfs_relookup(udvp, &vp, cnp, &cn, td, cnp->cn_nameptr,
462d00947d8SCraig Rodrigues 	    strlen(cnp->cn_nameptr), DELETE);
463d00947d8SCraig Rodrigues 	if (error)
464d00947d8SCraig Rodrigues 		return (error);
465d00947d8SCraig Rodrigues 
466d00947d8SCraig Rodrigues 	if (vp == NULLVP)
467d00947d8SCraig Rodrigues 		error = ENOENT;
468d00947d8SCraig Rodrigues 	else {
469d00947d8SCraig Rodrigues 		if (udvp == vp)
470d00947d8SCraig Rodrigues 			vrele(vp);
471d00947d8SCraig Rodrigues 		else
472d00947d8SCraig Rodrigues 			vput(vp);
473d00947d8SCraig Rodrigues 	}
474d00947d8SCraig Rodrigues 
475d00947d8SCraig Rodrigues 	if (cn.cn_flags & HASBUF) {
476d00947d8SCraig Rodrigues 		uma_zfree(namei_zone, cn.cn_pnbuf);
477d00947d8SCraig Rodrigues 		cn.cn_flags &= ~HASBUF;
478d00947d8SCraig Rodrigues 	}
479d00947d8SCraig Rodrigues 
480d00947d8SCraig Rodrigues 	if (!error) {
481d00947d8SCraig Rodrigues 		cn.cn_flags |= (cnp->cn_flags & HASBUF);
482d00947d8SCraig Rodrigues 		cnp->cn_flags = cn.cn_flags;
483d00947d8SCraig Rodrigues 	}
484d00947d8SCraig Rodrigues 
485d00947d8SCraig Rodrigues 	return (error);
486d00947d8SCraig Rodrigues }
487d00947d8SCraig Rodrigues 
488d00947d8SCraig Rodrigues /*
489d00947d8SCraig Rodrigues  * relookup for RENAME namei operation.
490d00947d8SCraig Rodrigues  *
491d00947d8SCraig Rodrigues  * dvp is unionfs vnode. dvp should be locked.
492d00947d8SCraig Rodrigues  */
493d00947d8SCraig Rodrigues int
494d00947d8SCraig Rodrigues unionfs_relookup_for_rename(struct vnode *dvp, struct componentname *cnp,
495d00947d8SCraig Rodrigues 			    struct thread *td)
496d00947d8SCraig Rodrigues {
497d00947d8SCraig Rodrigues 	int error;
498d00947d8SCraig Rodrigues 	struct vnode *udvp;
499d00947d8SCraig Rodrigues 	struct vnode *vp;
500d00947d8SCraig Rodrigues 	struct componentname cn;
501d00947d8SCraig Rodrigues 
502d00947d8SCraig Rodrigues 	udvp = UNIONFSVPTOUPPERVP(dvp);
503d00947d8SCraig Rodrigues 	vp = NULLVP;
504d00947d8SCraig Rodrigues 
505d00947d8SCraig Rodrigues 	error = unionfs_relookup(udvp, &vp, cnp, &cn, td, cnp->cn_nameptr,
506d00947d8SCraig Rodrigues 	    strlen(cnp->cn_nameptr), RENAME);
507d00947d8SCraig Rodrigues 	if (error)
508d00947d8SCraig Rodrigues 		return (error);
509d00947d8SCraig Rodrigues 
510d00947d8SCraig Rodrigues 	if (vp != NULLVP) {
511d00947d8SCraig Rodrigues 		if (udvp == vp)
512d00947d8SCraig Rodrigues 			vrele(vp);
513d00947d8SCraig Rodrigues 		else
514d00947d8SCraig Rodrigues 			vput(vp);
515d00947d8SCraig Rodrigues 	}
516d00947d8SCraig Rodrigues 
517d00947d8SCraig Rodrigues 	if (cn.cn_flags & HASBUF) {
518d00947d8SCraig Rodrigues 		uma_zfree(namei_zone, cn.cn_pnbuf);
519d00947d8SCraig Rodrigues 		cn.cn_flags &= ~HASBUF;
520d00947d8SCraig Rodrigues 	}
521d00947d8SCraig Rodrigues 
522d00947d8SCraig Rodrigues 	if (!error) {
523d00947d8SCraig Rodrigues 		cn.cn_flags |= (cnp->cn_flags & HASBUF);
524d00947d8SCraig Rodrigues 		cnp->cn_flags = cn.cn_flags;
525d00947d8SCraig Rodrigues 	}
526d00947d8SCraig Rodrigues 
527d00947d8SCraig Rodrigues 	return (error);
528d00947d8SCraig Rodrigues 
529d00947d8SCraig Rodrigues }
530d00947d8SCraig Rodrigues 
531d00947d8SCraig Rodrigues /*
532d00947d8SCraig Rodrigues  * Update the unionfs_node.
533d00947d8SCraig Rodrigues  *
534d00947d8SCraig Rodrigues  * uvp is new locked upper vnode. unionfs vnode's lock will be exchanged to the
535d00947d8SCraig Rodrigues  * uvp's lock and lower's lock will be unlocked.
536d00947d8SCraig Rodrigues  */
537d00947d8SCraig Rodrigues static void
538d00947d8SCraig Rodrigues unionfs_node_update(struct unionfs_node *unp, struct vnode *uvp,
539d00947d8SCraig Rodrigues 		    struct thread *td)
540d00947d8SCraig Rodrigues {
541d00947d8SCraig Rodrigues 	int		count, lockcnt;
542d00947d8SCraig Rodrigues 	struct vnode   *vp;
543d00947d8SCraig Rodrigues 	struct vnode   *lvp;
544d00947d8SCraig Rodrigues 
545d00947d8SCraig Rodrigues 	vp = UNIONFSTOV(unp);
546d00947d8SCraig Rodrigues 	lvp = unp->un_lowervp;
547d00947d8SCraig Rodrigues 
548d00947d8SCraig Rodrigues 	/*
549d00947d8SCraig Rodrigues 	 * lock update
550d00947d8SCraig Rodrigues 	 */
551d00947d8SCraig Rodrigues 	VI_LOCK(vp);
552d00947d8SCraig Rodrigues 	unp->un_uppervp = uvp;
553d00947d8SCraig Rodrigues 	vp->v_vnlock = uvp->v_vnlock;
554d00947d8SCraig Rodrigues 	lockcnt = lvp->v_vnlock->lk_exclusivecount;
555d00947d8SCraig Rodrigues 	if (lockcnt <= 0)
556d00947d8SCraig Rodrigues 		panic("unionfs: no exclusive lock");
557d00947d8SCraig Rodrigues 	VI_UNLOCK(vp);
558d00947d8SCraig Rodrigues 	for (count = 1; count < lockcnt; count++)
559cb05b60aSAttilio Rao 		vn_lock(uvp, LK_EXCLUSIVE | LK_CANRECURSE | LK_RETRY);
560d00947d8SCraig Rodrigues }
561d00947d8SCraig Rodrigues 
562d00947d8SCraig Rodrigues /*
563d00947d8SCraig Rodrigues  * Create a new shadow dir.
564d00947d8SCraig Rodrigues  *
565d00947d8SCraig Rodrigues  * udvp should be locked on entry and will be locked on return.
566d00947d8SCraig Rodrigues  *
567d00947d8SCraig Rodrigues  * If no error returned, unp will be updated.
568d00947d8SCraig Rodrigues  */
569d00947d8SCraig Rodrigues int
570d00947d8SCraig Rodrigues unionfs_mkshadowdir(struct unionfs_mount *ump, struct vnode *udvp,
571d00947d8SCraig Rodrigues 		    struct unionfs_node *unp, struct componentname *cnp,
572d00947d8SCraig Rodrigues 		    struct thread *td)
573d00947d8SCraig Rodrigues {
574d00947d8SCraig Rodrigues 	int		error;
575d00947d8SCraig Rodrigues 	struct vnode   *lvp;
576d00947d8SCraig Rodrigues 	struct vnode   *uvp;
577d00947d8SCraig Rodrigues 	struct vattr	va;
578d00947d8SCraig Rodrigues 	struct vattr	lva;
579d00947d8SCraig Rodrigues 	struct componentname cn;
580d00947d8SCraig Rodrigues 	struct mount   *mp;
581d00947d8SCraig Rodrigues 	struct ucred   *cred;
582d00947d8SCraig Rodrigues 	struct ucred   *credbk;
583d00947d8SCraig Rodrigues 	struct uidinfo *rootinfo;
584d00947d8SCraig Rodrigues 
585d00947d8SCraig Rodrigues 	if (unp->un_uppervp != NULLVP)
586d00947d8SCraig Rodrigues 		return (EEXIST);
587d00947d8SCraig Rodrigues 
588d00947d8SCraig Rodrigues 	lvp = unp->un_lowervp;
589d00947d8SCraig Rodrigues 	uvp = NULLVP;
590d00947d8SCraig Rodrigues 	credbk = cnp->cn_cred;
591d00947d8SCraig Rodrigues 
592d00947d8SCraig Rodrigues 	/* Authority change to root */
593d00947d8SCraig Rodrigues 	rootinfo = uifind((uid_t)0);
594d00947d8SCraig Rodrigues 	cred = crdup(cnp->cn_cred);
595d00947d8SCraig Rodrigues 	chgproccnt(cred->cr_ruidinfo, 1, 0);
596d00947d8SCraig Rodrigues 	change_euid(cred, rootinfo);
597d00947d8SCraig Rodrigues 	change_ruid(cred, rootinfo);
598d00947d8SCraig Rodrigues 	change_svuid(cred, (uid_t)0);
599d00947d8SCraig Rodrigues 	uifree(rootinfo);
600d00947d8SCraig Rodrigues 	cnp->cn_cred = cred;
601d00947d8SCraig Rodrigues 
602d00947d8SCraig Rodrigues 	memset(&cn, 0, sizeof(cn));
603d00947d8SCraig Rodrigues 
604d00947d8SCraig Rodrigues 	if ((error = VOP_GETATTR(lvp, &lva, cnp->cn_cred, td)))
605d00947d8SCraig Rodrigues 		goto unionfs_mkshadowdir_abort;
606d00947d8SCraig Rodrigues 
607d00947d8SCraig Rodrigues 	if ((error = unionfs_relookup(udvp, &uvp, cnp, &cn, td, cnp->cn_nameptr, cnp->cn_namelen, CREATE)))
608d00947d8SCraig Rodrigues 		goto unionfs_mkshadowdir_abort;
609d00947d8SCraig Rodrigues 	if (uvp != NULLVP) {
610d00947d8SCraig Rodrigues 		if (udvp == uvp)
611d00947d8SCraig Rodrigues 			vrele(uvp);
612d00947d8SCraig Rodrigues 		else
613d00947d8SCraig Rodrigues 			vput(uvp);
614d00947d8SCraig Rodrigues 
615d00947d8SCraig Rodrigues 		error = EEXIST;
616d00947d8SCraig Rodrigues 		goto unionfs_mkshadowdir_free_out;
617d00947d8SCraig Rodrigues 	}
618d00947d8SCraig Rodrigues 
619d00947d8SCraig Rodrigues 	if ((error = vn_start_write(udvp, &mp, V_WAIT | PCATCH)))
620d00947d8SCraig Rodrigues 		goto unionfs_mkshadowdir_free_out;
621d00947d8SCraig Rodrigues 	if ((error = VOP_LEASE(udvp, td, cn.cn_cred, LEASE_WRITE))) {
622d00947d8SCraig Rodrigues 		vn_finished_write(mp);
623d00947d8SCraig Rodrigues 		goto unionfs_mkshadowdir_free_out;
624d00947d8SCraig Rodrigues 	}
625d00947d8SCraig Rodrigues 	unionfs_create_uppervattr_core(ump, &lva, &va, td);
626d00947d8SCraig Rodrigues 
627d00947d8SCraig Rodrigues 	error = VOP_MKDIR(udvp, &uvp, &cn, &va);
628d00947d8SCraig Rodrigues 
629d00947d8SCraig Rodrigues 	if (!error) {
630d00947d8SCraig Rodrigues 		unionfs_node_update(unp, uvp, td);
631d00947d8SCraig Rodrigues 
632d00947d8SCraig Rodrigues 		/*
633d00947d8SCraig Rodrigues 		 * XXX The bug which cannot set uid/gid was corrected.
634d00947d8SCraig Rodrigues 		 * Ignore errors.
635d00947d8SCraig Rodrigues 		 */
636d00947d8SCraig Rodrigues 		va.va_type = VNON;
637d00947d8SCraig Rodrigues 		VOP_SETATTR(uvp, &va, cn.cn_cred, td);
638d00947d8SCraig Rodrigues 	}
639d00947d8SCraig Rodrigues 	vn_finished_write(mp);
640d00947d8SCraig Rodrigues 
641d00947d8SCraig Rodrigues unionfs_mkshadowdir_free_out:
642d00947d8SCraig Rodrigues 	if (cn.cn_flags & HASBUF) {
643d00947d8SCraig Rodrigues 		uma_zfree(namei_zone, cn.cn_pnbuf);
644d00947d8SCraig Rodrigues 		cn.cn_flags &= ~HASBUF;
645d00947d8SCraig Rodrigues 	}
646d00947d8SCraig Rodrigues 
647d00947d8SCraig Rodrigues unionfs_mkshadowdir_abort:
648d00947d8SCraig Rodrigues 	cnp->cn_cred = credbk;
649d00947d8SCraig Rodrigues 	chgproccnt(cred->cr_ruidinfo, -1, 0);
650d00947d8SCraig Rodrigues 	crfree(cred);
651d00947d8SCraig Rodrigues 
652d00947d8SCraig Rodrigues 	return (error);
653d00947d8SCraig Rodrigues }
654d00947d8SCraig Rodrigues 
655d00947d8SCraig Rodrigues /*
656d00947d8SCraig Rodrigues  * Create a new whiteout.
657d00947d8SCraig Rodrigues  *
658d00947d8SCraig Rodrigues  * dvp should be locked on entry and will be locked on return.
659d00947d8SCraig Rodrigues  */
660d00947d8SCraig Rodrigues int
661d00947d8SCraig Rodrigues unionfs_mkwhiteout(struct vnode *dvp, struct componentname *cnp,
662d00947d8SCraig Rodrigues 		   struct thread *td, char *path)
663d00947d8SCraig Rodrigues {
664d00947d8SCraig Rodrigues 	int		error;
665d00947d8SCraig Rodrigues 	struct vnode   *wvp;
666d00947d8SCraig Rodrigues 	struct componentname cn;
667d00947d8SCraig Rodrigues 	struct mount   *mp;
668d00947d8SCraig Rodrigues 
669d00947d8SCraig Rodrigues 	if (path == NULL)
670d00947d8SCraig Rodrigues 		path = cnp->cn_nameptr;
671d00947d8SCraig Rodrigues 
672d00947d8SCraig Rodrigues 	wvp = NULLVP;
673d00947d8SCraig Rodrigues 	if ((error = unionfs_relookup(dvp, &wvp, cnp, &cn, td, path, strlen(path), CREATE)))
674d00947d8SCraig Rodrigues 		return (error);
675d00947d8SCraig Rodrigues 	if (wvp != NULLVP) {
676d00947d8SCraig Rodrigues 		if (cn.cn_flags & HASBUF) {
677d00947d8SCraig Rodrigues 			uma_zfree(namei_zone, cn.cn_pnbuf);
678d00947d8SCraig Rodrigues 			cn.cn_flags &= ~HASBUF;
679d00947d8SCraig Rodrigues 		}
680d00947d8SCraig Rodrigues 		if (dvp == wvp)
681d00947d8SCraig Rodrigues 			vrele(wvp);
682d00947d8SCraig Rodrigues 		else
683d00947d8SCraig Rodrigues 			vput(wvp);
684d00947d8SCraig Rodrigues 
685d00947d8SCraig Rodrigues 		return (EEXIST);
686d00947d8SCraig Rodrigues 	}
687d00947d8SCraig Rodrigues 
688d00947d8SCraig Rodrigues 	if ((error = vn_start_write(dvp, &mp, V_WAIT | PCATCH)))
689d00947d8SCraig Rodrigues 		goto unionfs_mkwhiteout_free_out;
690d00947d8SCraig Rodrigues 	if (!(error = VOP_LEASE(dvp, td, td->td_ucred, LEASE_WRITE)))
691d00947d8SCraig Rodrigues 		error = VOP_WHITEOUT(dvp, &cn, CREATE);
692d00947d8SCraig Rodrigues 
693d00947d8SCraig Rodrigues 	vn_finished_write(mp);
694d00947d8SCraig Rodrigues 
695d00947d8SCraig Rodrigues unionfs_mkwhiteout_free_out:
696d00947d8SCraig Rodrigues 	if (cn.cn_flags & HASBUF) {
697d00947d8SCraig Rodrigues 		uma_zfree(namei_zone, cn.cn_pnbuf);
698d00947d8SCraig Rodrigues 		cn.cn_flags &= ~HASBUF;
699d00947d8SCraig Rodrigues 	}
700d00947d8SCraig Rodrigues 
701d00947d8SCraig Rodrigues 	return (error);
702d00947d8SCraig Rodrigues }
703d00947d8SCraig Rodrigues 
704d00947d8SCraig Rodrigues /*
705d00947d8SCraig Rodrigues  * Create a new vnode for create a new shadow file.
706d00947d8SCraig Rodrigues  *
707d00947d8SCraig Rodrigues  * If an error is returned, *vpp will be invalid, otherwise it will hold a
708d00947d8SCraig Rodrigues  * locked, referenced and opened vnode.
709d00947d8SCraig Rodrigues  *
710d00947d8SCraig Rodrigues  * unp is never updated.
711df8bae1dSRodney W. Grimes  */
71280b301c3SPoul-Henning Kamp static int
713d00947d8SCraig Rodrigues unionfs_vn_create_on_upper(struct vnode **vpp, struct vnode *udvp,
714d00947d8SCraig Rodrigues 			   struct unionfs_node *unp, struct vattr *uvap,
715d00947d8SCraig Rodrigues 			   struct thread *td)
716df8bae1dSRodney W. Grimes {
717d00947d8SCraig Rodrigues 	struct unionfs_mount *ump;
718d00947d8SCraig Rodrigues 	struct vnode   *vp;
719d00947d8SCraig Rodrigues 	struct vnode   *lvp;
720d00947d8SCraig Rodrigues 	struct ucred   *cred;
721d00947d8SCraig Rodrigues 	struct vattr	lva;
722d00947d8SCraig Rodrigues 	int		fmode;
723d00947d8SCraig Rodrigues 	int		error;
724d00947d8SCraig Rodrigues 	struct componentname cn;
725d00947d8SCraig Rodrigues 
726d00947d8SCraig Rodrigues 	ump = MOUNTTOUNIONFSMOUNT(UNIONFSTOV(unp)->v_mount);
727d00947d8SCraig Rodrigues 	vp = NULLVP;
728d00947d8SCraig Rodrigues 	lvp = unp->un_lowervp;
729d00947d8SCraig Rodrigues 	cred = td->td_ucred;
730d00947d8SCraig Rodrigues 	fmode = FFLAGS(O_WRONLY | O_CREAT | O_TRUNC | O_EXCL);
731d00947d8SCraig Rodrigues 	error = 0;
732d00947d8SCraig Rodrigues 
733d00947d8SCraig Rodrigues 	if ((error = VOP_GETATTR(lvp, &lva, cred, td)) != 0)
734d00947d8SCraig Rodrigues 		return (error);
735d00947d8SCraig Rodrigues 	unionfs_create_uppervattr_core(ump, &lva, uvap, td);
736d00947d8SCraig Rodrigues 
737d00947d8SCraig Rodrigues 	if (unp->un_path == NULL)
738d00947d8SCraig Rodrigues 		panic("unionfs: un_path is null");
739d00947d8SCraig Rodrigues 
740d00947d8SCraig Rodrigues 	cn.cn_namelen = strlen(unp->un_path);
741d00947d8SCraig Rodrigues 	cn.cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK);
742d00947d8SCraig Rodrigues 	bcopy(unp->un_path, cn.cn_pnbuf, cn.cn_namelen + 1);
743d00947d8SCraig Rodrigues 	cn.cn_nameiop = CREATE;
744d00947d8SCraig Rodrigues 	cn.cn_flags = (LOCKPARENT | LOCKLEAF | HASBUF | SAVENAME | ISLASTCN);
745d00947d8SCraig Rodrigues 	cn.cn_lkflags = LK_EXCLUSIVE;
746d00947d8SCraig Rodrigues 	cn.cn_thread = td;
747d00947d8SCraig Rodrigues 	cn.cn_cred = cred;
748d00947d8SCraig Rodrigues 	cn.cn_nameptr = cn.cn_pnbuf;
749d00947d8SCraig Rodrigues 	cn.cn_consume = 0;
750d00947d8SCraig Rodrigues 
751d00947d8SCraig Rodrigues 	vref(udvp);
752d00947d8SCraig Rodrigues 	if ((error = relookup(udvp, &vp, &cn)) != 0)
753d00947d8SCraig Rodrigues 		goto unionfs_vn_create_on_upper_free_out2;
754d00947d8SCraig Rodrigues 	vrele(udvp);
755d00947d8SCraig Rodrigues 
756d00947d8SCraig Rodrigues 	if (vp != NULLVP) {
757d00947d8SCraig Rodrigues 		if (vp == udvp)
758d00947d8SCraig Rodrigues 			vrele(vp);
759d00947d8SCraig Rodrigues 		else
760d00947d8SCraig Rodrigues 			vput(vp);
761d00947d8SCraig Rodrigues 		error = EEXIST;
762d00947d8SCraig Rodrigues 		goto unionfs_vn_create_on_upper_free_out1;
763d00947d8SCraig Rodrigues 	}
764d00947d8SCraig Rodrigues 
765d00947d8SCraig Rodrigues 	if ((error = VOP_LEASE(udvp, td, cred, LEASE_WRITE)) != 0)
766d00947d8SCraig Rodrigues 		goto unionfs_vn_create_on_upper_free_out1;
767d00947d8SCraig Rodrigues 
768d00947d8SCraig Rodrigues 	if ((error = VOP_CREATE(udvp, &vp, &cn, uvap)) != 0)
769d00947d8SCraig Rodrigues 		goto unionfs_vn_create_on_upper_free_out1;
770d00947d8SCraig Rodrigues 
7719e223287SKonstantin Belousov 	if ((error = VOP_OPEN(vp, fmode, cred, td, NULL)) != 0) {
772d00947d8SCraig Rodrigues 		vput(vp);
773d00947d8SCraig Rodrigues 		goto unionfs_vn_create_on_upper_free_out1;
774d00947d8SCraig Rodrigues 	}
775d00947d8SCraig Rodrigues 	vp->v_writecount++;
776d00947d8SCraig Rodrigues 	*vpp = vp;
777d00947d8SCraig Rodrigues 
778d00947d8SCraig Rodrigues unionfs_vn_create_on_upper_free_out1:
779d00947d8SCraig Rodrigues 	VOP_UNLOCK(udvp, 0, td);
780d00947d8SCraig Rodrigues 
781d00947d8SCraig Rodrigues unionfs_vn_create_on_upper_free_out2:
782d00947d8SCraig Rodrigues 	if (cn.cn_flags & HASBUF) {
783d00947d8SCraig Rodrigues 		uma_zfree(namei_zone, cn.cn_pnbuf);
784d00947d8SCraig Rodrigues 		cn.cn_flags &= ~HASBUF;
785d00947d8SCraig Rodrigues 	}
786d00947d8SCraig Rodrigues 
787d00947d8SCraig Rodrigues 	return (error);
788d00947d8SCraig Rodrigues }
789d00947d8SCraig Rodrigues 
790d00947d8SCraig Rodrigues /*
791d00947d8SCraig Rodrigues  * Copy from lvp to uvp.
792d00947d8SCraig Rodrigues  *
793d00947d8SCraig Rodrigues  * lvp and uvp should be locked and opened on entry and will be locked and
794d00947d8SCraig Rodrigues  * opened on return.
795d00947d8SCraig Rodrigues  */
796d00947d8SCraig Rodrigues static int
797d00947d8SCraig Rodrigues unionfs_copyfile_core(struct vnode *lvp, struct vnode *uvp,
798d00947d8SCraig Rodrigues 		      struct ucred *cred, struct thread *td)
799d00947d8SCraig Rodrigues {
800d00947d8SCraig Rodrigues 	int		error;
801d00947d8SCraig Rodrigues 	off_t		offset;
802d00947d8SCraig Rodrigues 	int		count;
803d00947d8SCraig Rodrigues 	int		bufoffset;
804df8bae1dSRodney W. Grimes 	char           *buf;
805df8bae1dSRodney W. Grimes 	struct uio	uio;
806df8bae1dSRodney W. Grimes 	struct iovec	iov;
807df8bae1dSRodney W. Grimes 
808d00947d8SCraig Rodrigues 	error = 0;
809d00947d8SCraig Rodrigues 	memset(&uio, 0, sizeof(uio));
8102a31267eSMatthew Dillon 
811b40ce416SJulian Elischer 	uio.uio_td = td;
812df8bae1dSRodney W. Grimes 	uio.uio_segflg = UIO_SYSSPACE;
813df8bae1dSRodney W. Grimes 	uio.uio_offset = 0;
814df8bae1dSRodney W. Grimes 
815d00947d8SCraig Rodrigues 	if ((error = VOP_LEASE(lvp, td, cred, LEASE_READ)) != 0)
816d00947d8SCraig Rodrigues 		return (error);
817d00947d8SCraig Rodrigues 	if ((error = VOP_LEASE(uvp, td, cred, LEASE_WRITE)) != 0)
818d00947d8SCraig Rodrigues 		return (error);
819a163d034SWarner Losh 	buf = malloc(MAXBSIZE, M_TEMP, M_WAITOK);
820df8bae1dSRodney W. Grimes 
821d00947d8SCraig Rodrigues 	while (error == 0) {
822d00947d8SCraig Rodrigues 		offset = uio.uio_offset;
823df8bae1dSRodney W. Grimes 
824df8bae1dSRodney W. Grimes 		uio.uio_iov = &iov;
825df8bae1dSRodney W. Grimes 		uio.uio_iovcnt = 1;
826df8bae1dSRodney W. Grimes 		iov.iov_base = buf;
827df8bae1dSRodney W. Grimes 		iov.iov_len = MAXBSIZE;
828df8bae1dSRodney W. Grimes 		uio.uio_resid = iov.iov_len;
829df8bae1dSRodney W. Grimes 		uio.uio_rw = UIO_READ;
830df8bae1dSRodney W. Grimes 
831d00947d8SCraig Rodrigues 		if ((error = VOP_READ(lvp, &uio, 0, cred)) != 0)
8322a31267eSMatthew Dillon 			break;
8332a31267eSMatthew Dillon 		if ((count = MAXBSIZE - uio.uio_resid) == 0)
8342a31267eSMatthew Dillon 			break;
8352a31267eSMatthew Dillon 
836d00947d8SCraig Rodrigues 		bufoffset = 0;
8372a31267eSMatthew Dillon 		while (bufoffset < count) {
838df8bae1dSRodney W. Grimes 			uio.uio_iov = &iov;
839df8bae1dSRodney W. Grimes 			uio.uio_iovcnt = 1;
8402a31267eSMatthew Dillon 			iov.iov_base = buf + bufoffset;
8412a31267eSMatthew Dillon 			iov.iov_len = count - bufoffset;
8422a31267eSMatthew Dillon 			uio.uio_offset = offset + bufoffset;
843df8bae1dSRodney W. Grimes 			uio.uio_resid = iov.iov_len;
844d00947d8SCraig Rodrigues 			uio.uio_rw = UIO_WRITE;
845df8bae1dSRodney W. Grimes 
846d00947d8SCraig Rodrigues 			if ((error = VOP_WRITE(uvp, &uio, 0, cred)) != 0)
847df8bae1dSRodney W. Grimes 				break;
848d00947d8SCraig Rodrigues 
8492a31267eSMatthew Dillon 			bufoffset += (count - bufoffset) - uio.uio_resid;
850df8bae1dSRodney W. Grimes 		}
851d00947d8SCraig Rodrigues 
8522a31267eSMatthew Dillon 		uio.uio_offset = offset + bufoffset;
853d00947d8SCraig Rodrigues 	}
854df8bae1dSRodney W. Grimes 
855df8bae1dSRodney W. Grimes 	free(buf, M_TEMP);
856d00947d8SCraig Rodrigues 
857df8bae1dSRodney W. Grimes 	return (error);
858df8bae1dSRodney W. Grimes }
859df8bae1dSRodney W. Grimes 
860df8bae1dSRodney W. Grimes /*
861d00947d8SCraig Rodrigues  * Copy file from lower to upper.
8622a31267eSMatthew Dillon  *
863d00947d8SCraig Rodrigues  * If you need copy of the contents, set 1 to docopy. Otherwise, set 0 to
864d00947d8SCraig Rodrigues  * docopy.
865d00947d8SCraig Rodrigues  *
866d00947d8SCraig Rodrigues  * If no error returned, unp will be updated.
867996c772fSJohn Dyson  */
868996c772fSJohn Dyson int
869d00947d8SCraig Rodrigues unionfs_copyfile(struct unionfs_node *unp, int docopy, struct ucred *cred,
870d00947d8SCraig Rodrigues 		 struct thread *td)
871996c772fSJohn Dyson {
872996c772fSJohn Dyson 	int		error;
873f2a2857bSKirk McKusick 	struct mount   *mp;
874d00947d8SCraig Rodrigues 	struct vnode   *udvp;
875d00947d8SCraig Rodrigues 	struct vnode   *lvp;
876d00947d8SCraig Rodrigues 	struct vnode   *uvp;
877d00947d8SCraig Rodrigues 	struct vattr	uva;
878996c772fSJohn Dyson 
879d00947d8SCraig Rodrigues 	lvp = unp->un_lowervp;
880d00947d8SCraig Rodrigues 	uvp = NULLVP;
881d00947d8SCraig Rodrigues 
882d00947d8SCraig Rodrigues 	if ((UNIONFSTOV(unp)->v_mount->mnt_flag & MNT_RDONLY))
883d00947d8SCraig Rodrigues 		return (EROFS);
884d00947d8SCraig Rodrigues 	if (unp->un_dvp == NULLVP)
885d00947d8SCraig Rodrigues 		return (EINVAL);
886d00947d8SCraig Rodrigues 	if (unp->un_uppervp != NULLVP)
887d00947d8SCraig Rodrigues 		return (EEXIST);
888d00947d8SCraig Rodrigues 	udvp = VTOUNIONFS(unp->un_dvp)->un_uppervp;
889d00947d8SCraig Rodrigues 	if (udvp == NULLVP)
890d00947d8SCraig Rodrigues 		return (EROFS);
891d00947d8SCraig Rodrigues 	if ((udvp->v_mount->mnt_flag & MNT_RDONLY))
892d00947d8SCraig Rodrigues 		return (EROFS);
893d00947d8SCraig Rodrigues 
894d00947d8SCraig Rodrigues 	error = VOP_ACCESS(lvp, VREAD, cred, td);
895d00947d8SCraig Rodrigues 	if (error != 0)
8965842d4e5SKATO Takenori 		return (error);
8975842d4e5SKATO Takenori 
898d00947d8SCraig Rodrigues 	if ((error = vn_start_write(udvp, &mp, V_WAIT | PCATCH)) != 0)
899996c772fSJohn Dyson 		return (error);
900d00947d8SCraig Rodrigues 	error = unionfs_vn_create_on_upper(&uvp, udvp, unp, &uva, td);
901d00947d8SCraig Rodrigues 	if (error != 0) {
902f2a2857bSKirk McKusick 		vn_finished_write(mp);
903f2a2857bSKirk McKusick 		return (error);
904f2a2857bSKirk McKusick 	}
905996c772fSJohn Dyson 
906d00947d8SCraig Rodrigues 	if (docopy != 0) {
9079e223287SKonstantin Belousov 		error = VOP_OPEN(lvp, FREAD, cred, td, NULL);
908996c772fSJohn Dyson 		if (error == 0) {
909d00947d8SCraig Rodrigues 			error = unionfs_copyfile_core(lvp, uvp, cred, td);
910d00947d8SCraig Rodrigues 			VOP_CLOSE(lvp, FREAD, cred, td);
911996c772fSJohn Dyson 		}
912d00947d8SCraig Rodrigues 	}
913d00947d8SCraig Rodrigues 	VOP_CLOSE(uvp, FWRITE, cred, td);
914d00947d8SCraig Rodrigues 	uvp->v_writecount--;
915996c772fSJohn Dyson 
916f2a2857bSKirk McKusick 	vn_finished_write(mp);
917d00947d8SCraig Rodrigues 
918996c772fSJohn Dyson 	if (error == 0) {
919d00947d8SCraig Rodrigues 		/* Reset the attributes. Ignore errors. */
920d00947d8SCraig Rodrigues 		uva.va_type = VNON;
921d00947d8SCraig Rodrigues 		VOP_SETATTR(uvp, &uva, cred, td);
922996c772fSJohn Dyson 	}
923996c772fSJohn Dyson 
924d00947d8SCraig Rodrigues 	unionfs_node_update(unp, uvp, td);
925996c772fSJohn Dyson 
9262a31267eSMatthew Dillon 	return (error);
927b422956cSPoul-Henning Kamp }
928996c772fSJohn Dyson 
9292a31267eSMatthew Dillon /*
930d00947d8SCraig Rodrigues  * It checks whether vp can rmdir. (check empty)
9312a31267eSMatthew Dillon  *
932d00947d8SCraig Rodrigues  * vp is unionfs vnode.
933d00947d8SCraig Rodrigues  * vp should be locked.
934df8bae1dSRodney W. Grimes  */
935df8bae1dSRodney W. Grimes int
936d00947d8SCraig Rodrigues unionfs_check_rmdir(struct vnode *vp, struct ucred *cred, struct thread *td)
937df8bae1dSRodney W. Grimes {
938df8bae1dSRodney W. Grimes 	int		error;
939d00947d8SCraig Rodrigues 	int		eofflag;
940d00947d8SCraig Rodrigues 	int		lookuperr;
941d00947d8SCraig Rodrigues 	struct vnode   *uvp;
942d00947d8SCraig Rodrigues 	struct vnode   *lvp;
943d00947d8SCraig Rodrigues 	struct vnode   *tvp;
944df8bae1dSRodney W. Grimes 	struct vattr	va;
945df8bae1dSRodney W. Grimes 	struct componentname cn;
946df8bae1dSRodney W. Grimes 	/*
947d00947d8SCraig Rodrigues 	 * The size of buf needs to be larger than DIRBLKSIZ.
948df8bae1dSRodney W. Grimes 	 */
949d00947d8SCraig Rodrigues 	char		buf[256 * 6];
950d00947d8SCraig Rodrigues 	struct dirent  *dp;
951d00947d8SCraig Rodrigues 	struct dirent  *edp;
952d00947d8SCraig Rodrigues 	struct uio	uio;
953d00947d8SCraig Rodrigues 	struct iovec	iov;
954df8bae1dSRodney W. Grimes 
955d00947d8SCraig Rodrigues 	ASSERT_VOP_ELOCKED(vp, "unionfs_check_rmdir");
956df8bae1dSRodney W. Grimes 
957d00947d8SCraig Rodrigues 	eofflag = 0;
958d00947d8SCraig Rodrigues 	uvp = UNIONFSVPTOUPPERVP(vp);
959d00947d8SCraig Rodrigues 	lvp = UNIONFSVPTOLOWERVP(vp);
960df8bae1dSRodney W. Grimes 
961d00947d8SCraig Rodrigues 	/* check opaque */
962d00947d8SCraig Rodrigues 	if ((error = VOP_GETATTR(uvp, &va, cred, td)) != 0)
963df8bae1dSRodney W. Grimes 		return (error);
964d00947d8SCraig Rodrigues 	if (va.va_flags & OPAQUE)
965d00947d8SCraig Rodrigues 		return (0);
966df8bae1dSRodney W. Grimes 
967d00947d8SCraig Rodrigues 	/* open vnode */
9683282e2c4SDaichi GOTO #ifdef MAC
96930d239bcSRobert Watson 	if ((error = mac_vnode_check_open(cred, vp, VEXEC|VREAD)) != 0)
9703282e2c4SDaichi GOTO 		return (error);
9713282e2c4SDaichi GOTO #endif
9723282e2c4SDaichi GOTO 	if ((error = VOP_ACCESS(vp, VEXEC|VREAD, cred, td)) != 0)
9733282e2c4SDaichi GOTO 		return (error);
9749e223287SKonstantin Belousov 	if ((error = VOP_OPEN(vp, FREAD, cred, td, NULL)) != 0)
975996c772fSJohn Dyson 		return (error);
976996c772fSJohn Dyson 
977d00947d8SCraig Rodrigues 	uio.uio_rw = UIO_READ;
978d00947d8SCraig Rodrigues 	uio.uio_segflg = UIO_SYSSPACE;
979d00947d8SCraig Rodrigues 	uio.uio_td = td;
980d00947d8SCraig Rodrigues 	uio.uio_offset = 0;
981996c772fSJohn Dyson 
982d00947d8SCraig Rodrigues #ifdef MAC
98330d239bcSRobert Watson 	error = mac_vnode_check_readdir(td->td_ucred, lvp);
984d00947d8SCraig Rodrigues #endif
985d00947d8SCraig Rodrigues 	while (!error && !eofflag) {
986d00947d8SCraig Rodrigues 		iov.iov_base = buf;
987d00947d8SCraig Rodrigues 		iov.iov_len = sizeof(buf);
988d00947d8SCraig Rodrigues 		uio.uio_iov = &iov;
989d00947d8SCraig Rodrigues 		uio.uio_iovcnt = 1;
990d00947d8SCraig Rodrigues 		uio.uio_resid = iov.iov_len;
991996c772fSJohn Dyson 
992d00947d8SCraig Rodrigues 		error = VOP_READDIR(lvp, &uio, cred, &eofflag, NULL, NULL);
993d00947d8SCraig Rodrigues 		if (error)
994d00947d8SCraig Rodrigues 			break;
995996c772fSJohn Dyson 
996d00947d8SCraig Rodrigues 		edp = (struct dirent*)&buf[sizeof(buf) - uio.uio_resid];
997d00947d8SCraig Rodrigues 		for (dp = (struct dirent*)buf; !error && dp < edp;
998d00947d8SCraig Rodrigues 		     dp = (struct dirent*)((caddr_t)dp + dp->d_reclen)) {
999d00947d8SCraig Rodrigues 			if (dp->d_type == DT_WHT ||
1000d00947d8SCraig Rodrigues 			    (dp->d_namlen == 1 && dp->d_name[0] == '.') ||
1001d00947d8SCraig Rodrigues 			    (dp->d_namlen == 2 && !bcmp(dp->d_name, "..", 2)))
1002d00947d8SCraig Rodrigues 				continue;
1003df8bae1dSRodney W. Grimes 
1004d00947d8SCraig Rodrigues 			cn.cn_namelen = dp->d_namlen;
1005d00947d8SCraig Rodrigues 			cn.cn_pnbuf = NULL;
1006d00947d8SCraig Rodrigues 			cn.cn_nameptr = dp->d_name;
1007d00947d8SCraig Rodrigues 			cn.cn_nameiop = LOOKUP;
1008d00947d8SCraig Rodrigues 			cn.cn_flags = (LOCKPARENT | LOCKLEAF | SAVENAME | RDONLY | ISLASTCN);
1009d00947d8SCraig Rodrigues 			cn.cn_lkflags = LK_EXCLUSIVE;
1010b40ce416SJulian Elischer 			cn.cn_thread = td;
1011d00947d8SCraig Rodrigues 			cn.cn_cred = cred;
1012df8bae1dSRodney W. Grimes 			cn.cn_consume = 0;
1013df8bae1dSRodney W. Grimes 
10142a31267eSMatthew Dillon 			/*
1015d00947d8SCraig Rodrigues 			 * check entry in lower.
1016d00947d8SCraig Rodrigues 			 * Sometimes, readdir function returns
1017d00947d8SCraig Rodrigues 			 * wrong entry.
10182a31267eSMatthew Dillon 			 */
1019d00947d8SCraig Rodrigues 			lookuperr = VOP_LOOKUP(lvp, &tvp, &cn);
1020df8bae1dSRodney W. Grimes 
1021d00947d8SCraig Rodrigues 			if (!lookuperr)
1022d00947d8SCraig Rodrigues 				vput(tvp);
10232a31267eSMatthew Dillon 			else
1024d00947d8SCraig Rodrigues 				continue; /* skip entry */
1025df8bae1dSRodney W. Grimes 
1026df8bae1dSRodney W. Grimes 			/*
1027d00947d8SCraig Rodrigues 			 * check entry
1028d00947d8SCraig Rodrigues 			 * If it has no exist/whiteout entry in upper,
1029d00947d8SCraig Rodrigues 			 * directory is not empty.
1030df8bae1dSRodney W. Grimes 			 */
1031d00947d8SCraig Rodrigues 			cn.cn_flags = (LOCKPARENT | LOCKLEAF | SAVENAME | RDONLY | ISLASTCN);
1032d00947d8SCraig Rodrigues 			lookuperr = VOP_LOOKUP(uvp, &tvp, &cn);
1033df8bae1dSRodney W. Grimes 
1034d00947d8SCraig Rodrigues 			if (!lookuperr)
1035d00947d8SCraig Rodrigues 				vput(tvp);
1036df8bae1dSRodney W. Grimes 
1037d00947d8SCraig Rodrigues 			/* ignore exist or whiteout entry */
1038d00947d8SCraig Rodrigues 			if (!lookuperr ||
1039d00947d8SCraig Rodrigues 			    (lookuperr == ENOENT && (cn.cn_flags & ISWHITEOUT)))
104001634480SBrian Feldman 				continue;
1041d00947d8SCraig Rodrigues 
1042d00947d8SCraig Rodrigues 			error = ENOTEMPTY;
1043df8bae1dSRodney W. Grimes 		}
1044996c772fSJohn Dyson 	}
1045996c772fSJohn Dyson 
1046d00947d8SCraig Rodrigues 	/* close vnode */
1047d00947d8SCraig Rodrigues 	VOP_CLOSE(vp, FREAD, cred, td);
1048d00947d8SCraig Rodrigues 
1049d00947d8SCraig Rodrigues 	return (error);
1050d00947d8SCraig Rodrigues }
1051d00947d8SCraig Rodrigues 
1052d00947d8SCraig Rodrigues #ifdef DIAGNOSTIC
1053d00947d8SCraig Rodrigues 
1054d00947d8SCraig Rodrigues struct vnode   *
1055d00947d8SCraig Rodrigues unionfs_checkuppervp(struct vnode *vp, char *fil, int lno)
1056996c772fSJohn Dyson {
1057d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
1058996c772fSJohn Dyson 
1059d00947d8SCraig Rodrigues 	unp = VTOUNIONFS(vp);
1060996c772fSJohn Dyson 
1061d00947d8SCraig Rodrigues #ifdef notyet
1062d00947d8SCraig Rodrigues 	if (vp->v_op != unionfs_vnodeop_p) {
1063d00947d8SCraig Rodrigues 		printf("unionfs_checkuppervp: on non-unionfs-node.\n");
1064d00947d8SCraig Rodrigues #ifdef KDB
10653de213ccSRobert Watson 		kdb_enter(KDB_WHY_UNIONFS,
10663de213ccSRobert Watson 		    "unionfs_checkuppervp: on non-unionfs-node.\n");
1067d00947d8SCraig Rodrigues #endif
1068d00947d8SCraig Rodrigues 		panic("unionfs_checkuppervp");
1069d00947d8SCraig Rodrigues 	};
1070d00947d8SCraig Rodrigues #endif
1071d00947d8SCraig Rodrigues 	return (unp->un_uppervp);
1072d8c6e674SDavid Schultz }
1073996c772fSJohn Dyson 
1074996c772fSJohn Dyson struct vnode   *
1075d00947d8SCraig Rodrigues unionfs_checklowervp(struct vnode *vp, char *fil, int lno)
1076996c772fSJohn Dyson {
1077d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
1078996c772fSJohn Dyson 
1079d00947d8SCraig Rodrigues 	unp = VTOUNIONFS(vp);
1080996c772fSJohn Dyson 
1081d00947d8SCraig Rodrigues #ifdef notyet
1082d00947d8SCraig Rodrigues 	if (vp->v_op != unionfs_vnodeop_p) {
1083d00947d8SCraig Rodrigues 		printf("unionfs_checklowervp: on non-unionfs-node.\n");
1084d00947d8SCraig Rodrigues #ifdef KDB
10853de213ccSRobert Watson 		kdb_enter(KDB_WHY_UNIONFS,
10863de213ccSRobert Watson 		    "unionfs_checklowervp: on non-unionfs-node.\n");
1087d00947d8SCraig Rodrigues #endif
1088d00947d8SCraig Rodrigues 		panic("unionfs_checklowervp");
10898c14bf40SPeter Wemm 	};
1090d00947d8SCraig Rodrigues #endif
1091d00947d8SCraig Rodrigues 	return (unp->un_lowervp);
1092d00947d8SCraig Rodrigues }
1093d00947d8SCraig Rodrigues #endif
1094