xref: /freebsd/sys/fs/unionfs/union_subr.c (revision 3150cf0c134651588cf83f9ea1fc0cd4a0c6ba4d)
1d167cf6fSWarner Losh /*-
251369649SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
351369649SPedro F. Giffuni  *
4df8bae1dSRodney W. Grimes  * Copyright (c) 1994 Jan-Simon Pendry
5df8bae1dSRodney W. Grimes  * Copyright (c) 1994
6df8bae1dSRodney W. Grimes  *	The Regents of the University of California.  All rights reserved.
7cb5736b7SDaichi GOTO  * Copyright (c) 2005, 2006, 2012 Masanori Ozawa <ozawa@ongs.co.jp>, ONGS Inc.
8cb5736b7SDaichi GOTO  * Copyright (c) 2006, 2012 Daichi Goto <daichi@freebsd.org>
9df8bae1dSRodney W. Grimes  *
10df8bae1dSRodney W. Grimes  * This code is derived from software contributed to Berkeley by
11df8bae1dSRodney W. Grimes  * Jan-Simon Pendry.
12df8bae1dSRodney W. Grimes  *
13df8bae1dSRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
14df8bae1dSRodney W. Grimes  * modification, are permitted provided that the following conditions
15df8bae1dSRodney W. Grimes  * are met:
16df8bae1dSRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
17df8bae1dSRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
18df8bae1dSRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
19df8bae1dSRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
20df8bae1dSRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
21fbbd9655SWarner Losh  * 3. Neither the name of the University nor the names of its contributors
22df8bae1dSRodney W. Grimes  *    may be used to endorse or promote products derived from this software
23df8bae1dSRodney W. Grimes  *    without specific prior written permission.
24df8bae1dSRodney W. Grimes  *
25df8bae1dSRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26df8bae1dSRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27df8bae1dSRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28df8bae1dSRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29df8bae1dSRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30df8bae1dSRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31df8bae1dSRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32df8bae1dSRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33df8bae1dSRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34df8bae1dSRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35df8bae1dSRodney W. Grimes  * SUCH DAMAGE.
36df8bae1dSRodney W. Grimes  *
37996c772fSJohn Dyson  *	@(#)union_subr.c	8.20 (Berkeley) 5/20/95
38c3aac50fSPeter Wemm  * $FreeBSD$
39df8bae1dSRodney W. Grimes  */
40df8bae1dSRodney W. Grimes 
41df8bae1dSRodney W. Grimes #include <sys/param.h>
42df8bae1dSRodney W. Grimes #include <sys/systm.h>
43fb919e4dSMark Murray #include <sys/kernel.h>
44daec9284SConrad Meyer #include <sys/ktr.h>
45fb919e4dSMark Murray #include <sys/lock.h>
462178ff8bSJohn Baldwin #include <sys/mutex.h>
47d00947d8SCraig Rodrigues #include <sys/malloc.h>
48d00947d8SCraig Rodrigues #include <sys/mount.h>
49fb919e4dSMark Murray #include <sys/namei.h>
50d00947d8SCraig Rodrigues #include <sys/proc.h>
51fb919e4dSMark Murray #include <sys/vnode.h>
52d00947d8SCraig Rodrigues #include <sys/dirent.h>
53d00947d8SCraig Rodrigues #include <sys/fcntl.h>
54d00947d8SCraig Rodrigues #include <sys/filedesc.h>
55d00947d8SCraig Rodrigues #include <sys/stat.h>
56372691a7SJason A. Harmening #include <sys/sysctl.h>
57372691a7SJason A. Harmening #include <sys/taskqueue.h>
58dce36a01SEdward Tomasz Napierala #include <sys/resourcevar.h>
59fb919e4dSMark Murray 
60d877dd57SJason A. Harmening #include <machine/atomic.h>
61d877dd57SJason A. Harmening 
62bcf11e8dSRobert Watson #include <security/mac/mac_framework.h>
63d00947d8SCraig Rodrigues 
648396dd9eSJeff Roberson #include <vm/uma.h>
65fb919e4dSMark Murray 
6699d300a1SRuslan Ermilov #include <fs/unionfs/union.h>
67df8bae1dSRodney W. Grimes 
68a9b794ffSDaichi GOTO #define NUNIONFSNODECACHE 16
69fd8ad212SJason A. Harmening #define UNIONFSHASHMASK (NUNIONFSNODECACHE - 1)
70a9b794ffSDaichi GOTO 
71a9b794ffSDaichi GOTO static MALLOC_DEFINE(M_UNIONFSHASH, "UNIONFS hash", "UNIONFS hash table");
72d00947d8SCraig Rodrigues MALLOC_DEFINE(M_UNIONFSNODE, "UNIONFS node", "UNIONFS vnode private part");
73d00947d8SCraig Rodrigues MALLOC_DEFINE(M_UNIONFSPATH, "UNIONFS path", "UNIONFS path private part");
74df8bae1dSRodney W. Grimes 
75372691a7SJason A. Harmening static struct task unionfs_deferred_rele_task;
76372691a7SJason A. Harmening static struct mtx unionfs_deferred_rele_lock;
77372691a7SJason A. Harmening static STAILQ_HEAD(, unionfs_node) unionfs_deferred_rele_list =
78372691a7SJason A. Harmening     STAILQ_HEAD_INITIALIZER(unionfs_deferred_rele_list);
79372691a7SJason A. Harmening static TASKQUEUE_DEFINE_THREAD(unionfs_rele);
80372691a7SJason A. Harmening 
81372691a7SJason A. Harmening unsigned int unionfs_ndeferred = 0;
82372691a7SJason A. Harmening SYSCTL_UINT(_vfs, OID_AUTO, unionfs_ndeferred, CTLFLAG_RD,
83372691a7SJason A. Harmening     &unionfs_ndeferred, 0, "unionfs deferred vnode release");
84372691a7SJason A. Harmening 
85372691a7SJason A. Harmening static void unionfs_deferred_rele(void *, int);
86372691a7SJason A. Harmening 
87d00947d8SCraig Rodrigues /*
88dc2dd185SDaichi GOTO  * Initialize
89d00947d8SCraig Rodrigues  */
90df8bae1dSRodney W. Grimes int
91d00947d8SCraig Rodrigues unionfs_init(struct vfsconf *vfsp)
92df8bae1dSRodney W. Grimes {
93d00947d8SCraig Rodrigues 	UNIONFSDEBUG("unionfs_init\n");	/* printed during system boot */
94372691a7SJason A. Harmening 	TASK_INIT(&unionfs_deferred_rele_task, 0, unionfs_deferred_rele, NULL);
95372691a7SJason A. Harmening 	mtx_init(&unionfs_deferred_rele_lock, "uniondefr", NULL, MTX_DEF);
9626f9a767SRodney W. Grimes 	return (0);
97df8bae1dSRodney W. Grimes }
98df8bae1dSRodney W. Grimes 
99d00947d8SCraig Rodrigues /*
100dc2dd185SDaichi GOTO  * Uninitialize
101d00947d8SCraig Rodrigues  */
102d00947d8SCraig Rodrigues int
103d00947d8SCraig Rodrigues unionfs_uninit(struct vfsconf *vfsp)
104df8bae1dSRodney W. Grimes {
105372691a7SJason A. Harmening 	taskqueue_quiesce(taskqueue_unionfs_rele);
106372691a7SJason A. Harmening 	taskqueue_free(taskqueue_unionfs_rele);
107372691a7SJason A. Harmening 	mtx_destroy(&unionfs_deferred_rele_lock);
108df8bae1dSRodney W. Grimes 	return (0);
109df8bae1dSRodney W. Grimes }
110df8bae1dSRodney W. Grimes 
111372691a7SJason A. Harmening static void
112372691a7SJason A. Harmening unionfs_deferred_rele(void *arg __unused, int pending __unused)
113372691a7SJason A. Harmening {
114372691a7SJason A. Harmening 	STAILQ_HEAD(, unionfs_node) local_rele_list;
115372691a7SJason A. Harmening 	struct unionfs_node *unp, *tunp;
116372691a7SJason A. Harmening 	unsigned int ndeferred;
117372691a7SJason A. Harmening 
118372691a7SJason A. Harmening 	ndeferred = 0;
119372691a7SJason A. Harmening 	STAILQ_INIT(&local_rele_list);
120372691a7SJason A. Harmening 	mtx_lock(&unionfs_deferred_rele_lock);
121372691a7SJason A. Harmening 	STAILQ_CONCAT(&local_rele_list, &unionfs_deferred_rele_list);
122372691a7SJason A. Harmening 	mtx_unlock(&unionfs_deferred_rele_lock);
123372691a7SJason A. Harmening 	STAILQ_FOREACH_SAFE(unp, &local_rele_list, un_rele, tunp) {
124372691a7SJason A. Harmening 		++ndeferred;
125372691a7SJason A. Harmening 		MPASS(unp->un_dvp != NULL);
126372691a7SJason A. Harmening 		vrele(unp->un_dvp);
127372691a7SJason A. Harmening 		free(unp, M_UNIONFSNODE);
128372691a7SJason A. Harmening 	}
129372691a7SJason A. Harmening 
130372691a7SJason A. Harmening 	/* We expect this function to be single-threaded, thus no atomic */
131372691a7SJason A. Harmening 	unionfs_ndeferred += ndeferred;
132372691a7SJason A. Harmening }
133372691a7SJason A. Harmening 
134a9b794ffSDaichi GOTO static struct unionfs_node_hashhead *
135fd8ad212SJason A. Harmening unionfs_get_hashhead(struct vnode *dvp, struct vnode *lookup)
136a9b794ffSDaichi GOTO {
137312d49efSJason A. Harmening 	struct unionfs_node *unp;
138a9b794ffSDaichi GOTO 
139a9b794ffSDaichi GOTO 	unp = VTOUNIONFS(dvp);
140fd8ad212SJason A. Harmening 
141fd8ad212SJason A. Harmening 	return (&(unp->un_hashtbl[vfs_hash_index(lookup) & UNIONFSHASHMASK]));
142a9b794ffSDaichi GOTO }
143a9b794ffSDaichi GOTO 
144fd8ad212SJason A. Harmening /*
145fd8ad212SJason A. Harmening  * Attempt to lookup a cached unionfs vnode by upper/lower vp
146fd8ad212SJason A. Harmening  * from dvp, with dvp's interlock held.
147fd8ad212SJason A. Harmening  */
148fd8ad212SJason A. Harmening static struct vnode *
149fd8ad212SJason A. Harmening unionfs_get_cached_vnode_locked(struct vnode *lookup, struct vnode *dvp)
150fd8ad212SJason A. Harmening {
151fd8ad212SJason A. Harmening 	struct unionfs_node *unp;
152fd8ad212SJason A. Harmening 	struct unionfs_node_hashhead *hd;
153fd8ad212SJason A. Harmening 	struct vnode *vp;
154fd8ad212SJason A. Harmening 
155fd8ad212SJason A. Harmening 	hd = unionfs_get_hashhead(dvp, lookup);
156fd8ad212SJason A. Harmening 
157fd8ad212SJason A. Harmening 	LIST_FOREACH(unp, hd, un_hash) {
1583ecefc4aSJason A. Harmening 		if (unp->un_uppervp == lookup ||
1593ecefc4aSJason A. Harmening 		    unp->un_lowervp == lookup) {
160fd8ad212SJason A. Harmening 			vp = UNIONFSTOV(unp);
161fd8ad212SJason A. Harmening 			VI_LOCK_FLAGS(vp, MTX_DUPOK);
162fd8ad212SJason A. Harmening 			vp->v_iflag &= ~VI_OWEINACT;
163fd8ad212SJason A. Harmening 			if (VN_IS_DOOMED(vp) ||
164fd8ad212SJason A. Harmening 			    ((vp->v_iflag & VI_DOINGINACT) != 0)) {
165fd8ad212SJason A. Harmening 				VI_UNLOCK(vp);
166fd8ad212SJason A. Harmening 				vp = NULLVP;
167fd8ad212SJason A. Harmening 			} else {
168fd8ad212SJason A. Harmening 				vrefl(vp);
169fd8ad212SJason A. Harmening 				VI_UNLOCK(vp);
170a9b794ffSDaichi GOTO 			}
171fd8ad212SJason A. Harmening 			return (vp);
172fd8ad212SJason A. Harmening 		}
173fd8ad212SJason A. Harmening 	}
174fd8ad212SJason A. Harmening 
175fd8ad212SJason A. Harmening 	return (NULLVP);
176fd8ad212SJason A. Harmening }
177fd8ad212SJason A. Harmening 
178a9b794ffSDaichi GOTO 
179a9b794ffSDaichi GOTO /*
1803af387c9SDaichi GOTO  * Get the cached vnode.
181a9b794ffSDaichi GOTO  */
182a9b794ffSDaichi GOTO static struct vnode *
1833af387c9SDaichi GOTO unionfs_get_cached_vnode(struct vnode *uvp, struct vnode *lvp,
184fd8ad212SJason A. Harmening     struct vnode *dvp)
185a9b794ffSDaichi GOTO {
186a9b794ffSDaichi GOTO 	struct vnode *vp;
187a9b794ffSDaichi GOTO 
188a9b794ffSDaichi GOTO 	vp = NULLVP;
189fd8ad212SJason A. Harmening 	VI_LOCK(dvp);
190fd8ad212SJason A. Harmening 	if (uvp != NULLVP)
191fd8ad212SJason A. Harmening 		vp = unionfs_get_cached_vnode_locked(uvp, dvp);
192fd8ad212SJason A. Harmening 	else if (lvp != NULLVP)
193fd8ad212SJason A. Harmening 		vp = unionfs_get_cached_vnode_locked(lvp, dvp);
194a9b794ffSDaichi GOTO 	VI_UNLOCK(dvp);
195a9b794ffSDaichi GOTO 
196fd8ad212SJason A. Harmening 	return (vp);
197a9b794ffSDaichi GOTO }
198a9b794ffSDaichi GOTO 
199a9b794ffSDaichi GOTO /*
2003af387c9SDaichi GOTO  * Add the new vnode into cache.
201a9b794ffSDaichi GOTO  */
202a9b794ffSDaichi GOTO static struct vnode *
2033af387c9SDaichi GOTO unionfs_ins_cached_vnode(struct unionfs_node *uncp,
204fd8ad212SJason A. Harmening     struct vnode *dvp)
205a9b794ffSDaichi GOTO {
206a9b794ffSDaichi GOTO 	struct unionfs_node_hashhead *hd;
207a9b794ffSDaichi GOTO 	struct vnode *vp;
208a9b794ffSDaichi GOTO 
20966191a76SJason A. Harmening 	ASSERT_VOP_ELOCKED(uncp->un_uppervp, __func__);
21066191a76SJason A. Harmening 	ASSERT_VOP_ELOCKED(uncp->un_lowervp, __func__);
2113ecefc4aSJason A. Harmening 	KASSERT(uncp->un_uppervp == NULLVP || uncp->un_uppervp->v_type == VDIR,
2123ecefc4aSJason A. Harmening 	    ("%s: v_type != VDIR", __func__));
2133ecefc4aSJason A. Harmening 	KASSERT(uncp->un_lowervp == NULLVP || uncp->un_lowervp->v_type == VDIR,
2143ecefc4aSJason A. Harmening 	    ("%s: v_type != VDIR", __func__));
215a9b794ffSDaichi GOTO 
216a9b794ffSDaichi GOTO 	vp = NULLVP;
217fd8ad212SJason A. Harmening 	VI_LOCK(dvp);
218fd8ad212SJason A. Harmening 	if (uncp->un_uppervp != NULL)
219fd8ad212SJason A. Harmening 		vp = unionfs_get_cached_vnode_locked(uncp->un_uppervp, dvp);
220fd8ad212SJason A. Harmening 	else if (uncp->un_lowervp != NULL)
221fd8ad212SJason A. Harmening 		vp = unionfs_get_cached_vnode_locked(uncp->un_lowervp, dvp);
222fd8ad212SJason A. Harmening 	if (vp == NULLVP) {
223fd8ad212SJason A. Harmening 		hd = unionfs_get_hashhead(dvp, (uncp->un_uppervp != NULLVP ?
224fd8ad212SJason A. Harmening 		    uncp->un_uppervp : uncp->un_lowervp));
225a9b794ffSDaichi GOTO 		LIST_INSERT_HEAD(hd, uncp, un_hash);
226fd8ad212SJason A. Harmening 	}
227a9b794ffSDaichi GOTO 	VI_UNLOCK(dvp);
228a9b794ffSDaichi GOTO 
229fd8ad212SJason A. Harmening 	return (vp);
230a9b794ffSDaichi GOTO }
231a9b794ffSDaichi GOTO 
232a9b794ffSDaichi GOTO /*
2333af387c9SDaichi GOTO  * Remove the vnode.
234a9b794ffSDaichi GOTO  */
235a9b794ffSDaichi GOTO static void
2363af387c9SDaichi GOTO unionfs_rem_cached_vnode(struct unionfs_node *unp, struct vnode *dvp)
237a9b794ffSDaichi GOTO {
2383ecefc4aSJason A. Harmening 	KASSERT(unp != NULL, ("%s: null node", __func__));
2393ecefc4aSJason A. Harmening 	KASSERT(dvp != NULLVP,
2403ecefc4aSJason A. Harmening 	    ("%s: null parent vnode", __func__));
241a9b794ffSDaichi GOTO 
242a9b794ffSDaichi GOTO 	VI_LOCK(dvp);
243fd8ad212SJason A. Harmening 	if (unp->un_hash.le_prev != NULL) {
244a9b794ffSDaichi GOTO 		LIST_REMOVE(unp, un_hash);
2453af387c9SDaichi GOTO 		unp->un_hash.le_next = NULL;
2463af387c9SDaichi GOTO 		unp->un_hash.le_prev = NULL;
247fd8ad212SJason A. Harmening 	}
248a9b794ffSDaichi GOTO 	VI_UNLOCK(dvp);
249a9b794ffSDaichi GOTO }
250a9b794ffSDaichi GOTO 
251d00947d8SCraig Rodrigues /*
252f9e28f90SJason A. Harmening  * Common cleanup handling for unionfs_nodeget
253f9e28f90SJason A. Harmening  * Upper, lower, and parent directory vnodes are expected to be referenced by
254f9e28f90SJason A. Harmening  * the caller.  Upper and lower vnodes, if non-NULL, are also expected to be
255f9e28f90SJason A. Harmening  * exclusively locked by the caller.
256f9e28f90SJason A. Harmening  * This function will return with the caller's locks and references undone.
257f9e28f90SJason A. Harmening  */
258f9e28f90SJason A. Harmening static void
259*3150cf0cSMateusz Guzik unionfs_nodeget_cleanup(struct vnode *vp, struct unionfs_node *unp)
260f9e28f90SJason A. Harmening {
261f9e28f90SJason A. Harmening 
262f9e28f90SJason A. Harmening 	/*
263f9e28f90SJason A. Harmening 	 * Lock and reset the default vnode lock; vgone() expects a locked
264f9e28f90SJason A. Harmening 	 * vnode, and we're going to reset the vnode ops.
265f9e28f90SJason A. Harmening 	 */
266f9e28f90SJason A. Harmening 	lockmgr(&vp->v_lock, LK_EXCLUSIVE, NULL);
267f9e28f90SJason A. Harmening 
268f9e28f90SJason A. Harmening 	/*
269f9e28f90SJason A. Harmening 	 * Clear out private data and reset the vnode ops to avoid use of
270f9e28f90SJason A. Harmening 	 * unionfs vnode ops on a partially constructed vnode.
271f9e28f90SJason A. Harmening 	 */
272f9e28f90SJason A. Harmening 	VI_LOCK(vp);
273f9e28f90SJason A. Harmening 	vp->v_data = NULL;
274f9e28f90SJason A. Harmening 	vp->v_vnlock = &vp->v_lock;
275f9e28f90SJason A. Harmening 	vp->v_op = &dead_vnodeops;
276f9e28f90SJason A. Harmening 	VI_UNLOCK(vp);
277f9e28f90SJason A. Harmening 	vgone(vp);
278f9e28f90SJason A. Harmening 	vput(vp);
279f9e28f90SJason A. Harmening 
280f9e28f90SJason A. Harmening 	if (unp->un_dvp != NULLVP)
281f9e28f90SJason A. Harmening 		vrele(unp->un_dvp);
282f9e28f90SJason A. Harmening 	if (unp->un_uppervp != NULLVP)
283f9e28f90SJason A. Harmening 		vput(unp->un_uppervp);
284f9e28f90SJason A. Harmening 	if (unp->un_lowervp != NULLVP)
285f9e28f90SJason A. Harmening 		vput(unp->un_lowervp);
286f9e28f90SJason A. Harmening 	if (unp->un_hashtbl != NULL)
287fd8ad212SJason A. Harmening 		hashdestroy(unp->un_hashtbl, M_UNIONFSHASH, UNIONFSHASHMASK);
288f9e28f90SJason A. Harmening 	free(unp->un_path, M_UNIONFSPATH);
289f9e28f90SJason A. Harmening 	free(unp, M_UNIONFSNODE);
290f9e28f90SJason A. Harmening }
291f9e28f90SJason A. Harmening 
292f9e28f90SJason A. Harmening /*
293d00947d8SCraig Rodrigues  * Make a new or get existing unionfs node.
2942a31267eSMatthew Dillon  *
295d00947d8SCraig Rodrigues  * uppervp and lowervp should be unlocked. Because if new unionfs vnode is
296d00947d8SCraig Rodrigues  * locked, uppervp or lowervp is locked too. In order to prevent dead lock,
297d00947d8SCraig Rodrigues  * you should not lock plurality simultaneously.
298df8bae1dSRodney W. Grimes  */
299d00947d8SCraig Rodrigues int
300d00947d8SCraig Rodrigues unionfs_nodeget(struct mount *mp, struct vnode *uppervp,
301312d49efSJason A. Harmening     struct vnode *lowervp, struct vnode *dvp, struct vnode **vpp,
3026d8420d4SJason A. Harmening     struct componentname *cnp)
303d00947d8SCraig Rodrigues {
304312d49efSJason A. Harmening 	char	       *path;
305d00947d8SCraig Rodrigues 	struct unionfs_mount *ump;
306d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
307d00947d8SCraig Rodrigues 	struct vnode   *vp;
308fd8ad212SJason A. Harmening 	u_long		hashmask;
309d00947d8SCraig Rodrigues 	int		error;
310d00947d8SCraig Rodrigues 	int		lkflags;
311a9b794ffSDaichi GOTO 	enum vtype	vt;
312df8bae1dSRodney W. Grimes 
313f9e28f90SJason A. Harmening 	error = 0;
314d00947d8SCraig Rodrigues 	ump = MOUNTTOUNIONFSMOUNT(mp);
315d00947d8SCraig Rodrigues 	lkflags = (cnp ? cnp->cn_lkflags : 0);
316dc2dd185SDaichi GOTO 	path = (cnp ? cnp->cn_nameptr : NULL);
317a9b794ffSDaichi GOTO 	*vpp = NULLVP;
3182a31267eSMatthew Dillon 
319d00947d8SCraig Rodrigues 	if (uppervp == NULLVP && lowervp == NULLVP)
3203ecefc4aSJason A. Harmening 		panic("%s: upper and lower is null", __func__);
321d00947d8SCraig Rodrigues 
322a9b794ffSDaichi GOTO 	vt = (uppervp != NULLVP ? uppervp->v_type : lowervp->v_type);
323a9b794ffSDaichi GOTO 
324d00947d8SCraig Rodrigues 	/* If it has no ISLASTCN flag, path check is skipped. */
325dc2dd185SDaichi GOTO 	if (cnp && !(cnp->cn_flags & ISLASTCN))
326d00947d8SCraig Rodrigues 		path = NULL;
327d00947d8SCraig Rodrigues 
3283af387c9SDaichi GOTO 	/* check the cache */
329fd8ad212SJason A. Harmening 	if (dvp != NULLVP && vt == VDIR) {
330fd8ad212SJason A. Harmening 		vp = unionfs_get_cached_vnode(uppervp, lowervp, dvp);
331a9b794ffSDaichi GOTO 		if (vp != NULLVP) {
332a9b794ffSDaichi GOTO 			*vpp = vp;
333a9b794ffSDaichi GOTO 			goto unionfs_nodeget_out;
334a9b794ffSDaichi GOTO 		}
335a9b794ffSDaichi GOTO 	}
336a9b794ffSDaichi GOTO 
337d00947d8SCraig Rodrigues 	if ((uppervp == NULLVP || ump->um_uppervp != uppervp) ||
338d00947d8SCraig Rodrigues 	    (lowervp == NULLVP || ump->um_lowervp != lowervp)) {
339a9b794ffSDaichi GOTO 		/* dvp will be NULLVP only in case of root vnode. */
340d00947d8SCraig Rodrigues 		if (dvp == NULLVP)
341d00947d8SCraig Rodrigues 			return (EINVAL);
342d00947d8SCraig Rodrigues 	}
3431ede983cSDag-Erling Smørgrav 	unp = malloc(sizeof(struct unionfs_node),
344d00947d8SCraig Rodrigues 	    M_UNIONFSNODE, M_WAITOK | M_ZERO);
345d00947d8SCraig Rodrigues 
346d00947d8SCraig Rodrigues 	error = getnewvnode("unionfs", mp, &unionfs_vnodeops, &vp);
347dc2dd185SDaichi GOTO 	if (error != 0) {
3481ede983cSDag-Erling Smørgrav 		free(unp, M_UNIONFSNODE);
349d00947d8SCraig Rodrigues 		return (error);
350d00947d8SCraig Rodrigues 	}
351d00947d8SCraig Rodrigues 	if (dvp != NULLVP)
352d00947d8SCraig Rodrigues 		vref(dvp);
353d00947d8SCraig Rodrigues 	if (uppervp != NULLVP)
354d00947d8SCraig Rodrigues 		vref(uppervp);
355d00947d8SCraig Rodrigues 	if (lowervp != NULLVP)
356d00947d8SCraig Rodrigues 		vref(lowervp);
357d00947d8SCraig Rodrigues 
358fd8ad212SJason A. Harmening 	if (vt == VDIR) {
359a9b794ffSDaichi GOTO 		unp->un_hashtbl = hashinit(NUNIONFSNODECACHE, M_UNIONFSHASH,
360fd8ad212SJason A. Harmening 		    &hashmask);
361fd8ad212SJason A. Harmening 		KASSERT(hashmask == UNIONFSHASHMASK,
362fd8ad212SJason A. Harmening 		    ("unexpected unionfs hash mask 0x%lx", hashmask));
363fd8ad212SJason A. Harmening 	}
364a9b794ffSDaichi GOTO 
365d00947d8SCraig Rodrigues 	unp->un_vnode = vp;
366d00947d8SCraig Rodrigues 	unp->un_uppervp = uppervp;
367d00947d8SCraig Rodrigues 	unp->un_lowervp = lowervp;
368d00947d8SCraig Rodrigues 	unp->un_dvp = dvp;
369d00947d8SCraig Rodrigues 	if (uppervp != NULLVP)
370d00947d8SCraig Rodrigues 		vp->v_vnlock = uppervp->v_vnlock;
371d00947d8SCraig Rodrigues 	else
372d00947d8SCraig Rodrigues 		vp->v_vnlock = lowervp->v_vnlock;
373d00947d8SCraig Rodrigues 
374dc2dd185SDaichi GOTO 	if (path != NULL) {
375312d49efSJason A. Harmening 		unp->un_path = malloc(cnp->cn_namelen + 1,
376312d49efSJason A. Harmening 		    M_UNIONFSPATH, M_WAITOK | M_ZERO);
377d00947d8SCraig Rodrigues 		bcopy(cnp->cn_nameptr, unp->un_path, cnp->cn_namelen);
378d00947d8SCraig Rodrigues 		unp->un_path[cnp->cn_namelen] = '\0';
379abe95116SJason A. Harmening 		unp->un_pathlen = cnp->cn_namelen;
380d00947d8SCraig Rodrigues 	}
381a9b794ffSDaichi GOTO 	vp->v_type = vt;
382d00947d8SCraig Rodrigues 	vp->v_data = unp;
383d00947d8SCraig Rodrigues 
384d00947d8SCraig Rodrigues 	if ((uppervp != NULLVP && ump->um_uppervp == uppervp) &&
385d00947d8SCraig Rodrigues 	    (lowervp != NULLVP && ump->um_lowervp == lowervp))
386d00947d8SCraig Rodrigues 		vp->v_vflag |= VV_ROOT;
387d00947d8SCraig Rodrigues 
388f9e28f90SJason A. Harmening 	vn_lock_pair(lowervp, false, uppervp, false);
389*3150cf0cSMateusz Guzik 	error = insmntque(vp, mp);
390*3150cf0cSMateusz Guzik 	if (error != 0) {
391*3150cf0cSMateusz Guzik 		unionfs_nodeget_cleanup(vp, unp);
392f9e28f90SJason A. Harmening 		return (error);
393*3150cf0cSMateusz Guzik 	}
394f9e28f90SJason A. Harmening 	if (lowervp != NULL && VN_IS_DOOMED(lowervp)) {
395f9e28f90SJason A. Harmening 		vput(lowervp);
396f9e28f90SJason A. Harmening 		unp->un_lowervp = NULL;
397f9e28f90SJason A. Harmening 	}
398f9e28f90SJason A. Harmening 	if (uppervp != NULL && VN_IS_DOOMED(uppervp)) {
399f9e28f90SJason A. Harmening 		vput(uppervp);
400f9e28f90SJason A. Harmening 		unp->un_uppervp = NULL;
401f9e28f90SJason A. Harmening 	}
402f9e28f90SJason A. Harmening 	if (unp->un_lowervp == NULL && unp->un_uppervp == NULL) {
403f9e28f90SJason A. Harmening 		unionfs_nodeget_cleanup(vp, unp);
404f9e28f90SJason A. Harmening 		return (ENOENT);
405f9e28f90SJason A. Harmening 	}
406fd8ad212SJason A. Harmening 
407fd8ad212SJason A. Harmening 	if (dvp != NULLVP && vt == VDIR)
408fd8ad212SJason A. Harmening 		*vpp = unionfs_ins_cached_vnode(unp, dvp);
409f9e28f90SJason A. Harmening 	if (*vpp != NULLVP) {
410f9e28f90SJason A. Harmening 		unionfs_nodeget_cleanup(vp, unp);
411a9b794ffSDaichi GOTO 		vp = *vpp;
412f9e28f90SJason A. Harmening 	} else {
413f9e28f90SJason A. Harmening 		if (uppervp != NULL)
414f9e28f90SJason A. Harmening 			VOP_UNLOCK(uppervp);
415f9e28f90SJason A. Harmening 		if (lowervp != NULL)
416f9e28f90SJason A. Harmening 			VOP_UNLOCK(lowervp);
417a9b794ffSDaichi GOTO 		*vpp = vp;
418f9e28f90SJason A. Harmening 	}
419a9b794ffSDaichi GOTO 
420a9b794ffSDaichi GOTO unionfs_nodeget_out:
421d00947d8SCraig Rodrigues 	if (lkflags & LK_TYPE_MASK)
422cb05b60aSAttilio Rao 		vn_lock(vp, lkflags | LK_RETRY);
423df8bae1dSRodney W. Grimes 
424d00947d8SCraig Rodrigues 	return (0);
425996c772fSJohn Dyson }
426df8bae1dSRodney W. Grimes 
4272a31267eSMatthew Dillon /*
428dc2dd185SDaichi GOTO  * Clean up the unionfs node.
4292a31267eSMatthew Dillon  */
430d00947d8SCraig Rodrigues void
4316d8420d4SJason A. Harmening unionfs_noderem(struct vnode *vp)
432d00947d8SCraig Rodrigues {
4333af387c9SDaichi GOTO 	struct unionfs_node *unp, *unp_t1, *unp_t2;
4343af387c9SDaichi GOTO 	struct unionfs_node_hashhead *hd;
435acc4bab1SCraig Rodrigues 	struct unionfs_node_status *unsp, *unsp_tmp;
436d00947d8SCraig Rodrigues 	struct vnode   *lvp;
437d00947d8SCraig Rodrigues 	struct vnode   *uvp;
438a9b794ffSDaichi GOTO 	struct vnode   *dvp;
439312d49efSJason A. Harmening 	int		count;
440d877dd57SJason A. Harmening 	int		writerefs;
4412a31267eSMatthew Dillon 
44266191a76SJason A. Harmening 	KASSERT(vp->v_vnlock->lk_recurse == 0,
44366191a76SJason A. Harmening 	    ("%s: vnode %p locked recursively", __func__, vp));
44466191a76SJason A. Harmening 	if (lockmgr(&vp->v_lock, LK_EXCLUSIVE | LK_NOWAIT, NULL) != 0)
4453ecefc4aSJason A. Harmening 		panic("%s: failed to acquire lock for vnode lock", __func__);
446866dd633SJason A. Harmening 
4472a31267eSMatthew Dillon 	/*
448d00947d8SCraig Rodrigues 	 * Use the interlock to protect the clearing of v_data to
449d00947d8SCraig Rodrigues 	 * prevent faults in unionfs_lock().
4502a31267eSMatthew Dillon 	 */
451d00947d8SCraig Rodrigues 	VI_LOCK(vp);
452d00947d8SCraig Rodrigues 	unp = VTOUNIONFS(vp);
453d00947d8SCraig Rodrigues 	lvp = unp->un_lowervp;
454d00947d8SCraig Rodrigues 	uvp = unp->un_uppervp;
455a9b794ffSDaichi GOTO 	dvp = unp->un_dvp;
456d00947d8SCraig Rodrigues 	unp->un_lowervp = unp->un_uppervp = NULLVP;
457d00947d8SCraig Rodrigues 	vp->v_vnlock = &(vp->v_lock);
458d00947d8SCraig Rodrigues 	vp->v_data = NULL;
459d00947d8SCraig Rodrigues 	vp->v_object = NULL;
460866dd633SJason A. Harmening 	if (unp->un_hashtbl != NULL) {
461866dd633SJason A. Harmening 		/*
462866dd633SJason A. Harmening 		 * Clear out any cached child vnodes.  This should only
463866dd633SJason A. Harmening 		 * be necessary during forced unmount, when the vnode may
464866dd633SJason A. Harmening 		 * be reclaimed with a non-zero use count.  Otherwise the
465866dd633SJason A. Harmening 		 * reference held by each child should prevent reclamation.
466866dd633SJason A. Harmening 		 */
467866dd633SJason A. Harmening 		for (count = 0; count <= UNIONFSHASHMASK; count++) {
468866dd633SJason A. Harmening 			hd = unp->un_hashtbl + count;
469866dd633SJason A. Harmening 			LIST_FOREACH_SAFE(unp_t1, hd, un_hash, unp_t2) {
470866dd633SJason A. Harmening 				LIST_REMOVE(unp_t1, un_hash);
471866dd633SJason A. Harmening 				unp_t1->un_hash.le_next = NULL;
472866dd633SJason A. Harmening 				unp_t1->un_hash.le_prev = NULL;
473866dd633SJason A. Harmening 			}
474866dd633SJason A. Harmening 		}
475866dd633SJason A. Harmening 	}
476cb5736b7SDaichi GOTO 	VI_UNLOCK(vp);
477cb5736b7SDaichi GOTO 
478d877dd57SJason A. Harmening 	writerefs = atomic_load_int(&vp->v_writecount);
479d877dd57SJason A. Harmening 	VNASSERT(writerefs >= 0, vp,
480d877dd57SJason A. Harmening 	    ("%s: write count %d, unexpected text ref", __func__, writerefs));
481d877dd57SJason A. Harmening 	/*
482d877dd57SJason A. Harmening 	 * If we were opened for write, we leased the write reference
483d877dd57SJason A. Harmening 	 * to the lower vnode.  If this is a reclamation due to the
484d877dd57SJason A. Harmening 	 * forced unmount, undo the reference now.
485d877dd57SJason A. Harmening 	 */
486d877dd57SJason A. Harmening 	if (writerefs > 0) {
487d877dd57SJason A. Harmening 		VNASSERT(uvp != NULL, vp,
488d877dd57SJason A. Harmening 		    ("%s: write reference without upper vnode", __func__));
489d877dd57SJason A. Harmening 		VOP_ADD_WRITECOUNT(uvp, -writerefs);
490d877dd57SJason A. Harmening 	}
491cb5736b7SDaichi GOTO 	if (lvp != NULLVP)
492b249ce48SMateusz Guzik 		VOP_UNLOCK(lvp);
493cb5736b7SDaichi GOTO 	if (uvp != NULLVP)
494b249ce48SMateusz Guzik 		VOP_UNLOCK(uvp);
495d00947d8SCraig Rodrigues 
496fd8ad212SJason A. Harmening 	if (dvp != NULLVP)
4973af387c9SDaichi GOTO 		unionfs_rem_cached_vnode(unp, dvp);
498a9b794ffSDaichi GOTO 
4995050aa86SKonstantin Belousov 	if (lvp != NULLVP)
500d00947d8SCraig Rodrigues 		vrele(lvp);
5015050aa86SKonstantin Belousov 	if (uvp != NULLVP)
502d00947d8SCraig Rodrigues 		vrele(uvp);
503a9b794ffSDaichi GOTO 	if (unp->un_path != NULL) {
504d00947d8SCraig Rodrigues 		free(unp->un_path, M_UNIONFSPATH);
505d00947d8SCraig Rodrigues 		unp->un_path = NULL;
506abe95116SJason A. Harmening 		unp->un_pathlen = 0;
507d00947d8SCraig Rodrigues 	}
508acc4bab1SCraig Rodrigues 
5093af387c9SDaichi GOTO 	if (unp->un_hashtbl != NULL) {
510fd8ad212SJason A. Harmening 		hashdestroy(unp->un_hashtbl, M_UNIONFSHASH, UNIONFSHASHMASK);
5113af387c9SDaichi GOTO 	}
512a9b794ffSDaichi GOTO 
513acc4bab1SCraig Rodrigues 	LIST_FOREACH_SAFE(unsp, &(unp->un_unshead), uns_list, unsp_tmp) {
514d00947d8SCraig Rodrigues 		LIST_REMOVE(unsp, uns_list);
515d00947d8SCraig Rodrigues 		free(unsp, M_TEMP);
516d00947d8SCraig Rodrigues 	}
517372691a7SJason A. Harmening 	if (dvp != NULLVP) {
518372691a7SJason A. Harmening 		mtx_lock(&unionfs_deferred_rele_lock);
519372691a7SJason A. Harmening 		STAILQ_INSERT_TAIL(&unionfs_deferred_rele_list, unp, un_rele);
520372691a7SJason A. Harmening 		mtx_unlock(&unionfs_deferred_rele_lock);
521372691a7SJason A. Harmening 		taskqueue_enqueue(taskqueue_unionfs_rele,
522372691a7SJason A. Harmening 		    &unionfs_deferred_rele_task);
523372691a7SJason A. Harmening 	} else
5241ede983cSDag-Erling Smørgrav 		free(unp, M_UNIONFSNODE);
525df8bae1dSRodney W. Grimes }
526df8bae1dSRodney W. Grimes 
527d00947d8SCraig Rodrigues /*
52839a2dc44SJason A. Harmening  * Get the unionfs node status object for the vnode corresponding to unp,
52939a2dc44SJason A. Harmening  * for the process that owns td.  Allocate a new status object if one
53039a2dc44SJason A. Harmening  * does not already exist.
531d00947d8SCraig Rodrigues  */
532d00947d8SCraig Rodrigues void
533d00947d8SCraig Rodrigues unionfs_get_node_status(struct unionfs_node *unp, struct thread *td,
534d00947d8SCraig Rodrigues     struct unionfs_node_status **unspp)
535d00947d8SCraig Rodrigues {
536d00947d8SCraig Rodrigues 	struct unionfs_node_status *unsp;
537312d49efSJason A. Harmening 	pid_t pid;
538312d49efSJason A. Harmening 
539312d49efSJason A. Harmening 	pid = td->td_proc->p_pid;
540df8bae1dSRodney W. Grimes 
5413ecefc4aSJason A. Harmening 	KASSERT(NULL != unspp, ("%s: NULL status", __func__));
5423ecefc4aSJason A. Harmening 	ASSERT_VOP_ELOCKED(UNIONFSTOV(unp), __func__);
5432a31267eSMatthew Dillon 
544d00947d8SCraig Rodrigues 	LIST_FOREACH(unsp, &(unp->un_unshead), uns_list) {
545fe5f08cdSDaichi GOTO 		if (unsp->uns_pid == pid) {
546d00947d8SCraig Rodrigues 			*unspp = unsp;
547d00947d8SCraig Rodrigues 			return;
548d00947d8SCraig Rodrigues 		}
549d00947d8SCraig Rodrigues 	}
5502a31267eSMatthew Dillon 
551d00947d8SCraig Rodrigues 	/* create a new unionfs node status */
552e11e3f18SDag-Erling Smørgrav 	unsp = malloc(sizeof(struct unionfs_node_status),
553e11e3f18SDag-Erling Smørgrav 	    M_TEMP, M_WAITOK | M_ZERO);
5542a31267eSMatthew Dillon 
555fe5f08cdSDaichi GOTO 	unsp->uns_pid = pid;
556d00947d8SCraig Rodrigues 	LIST_INSERT_HEAD(&(unp->un_unshead), unsp, uns_list);
5572a31267eSMatthew Dillon 
558d00947d8SCraig Rodrigues 	*unspp = unsp;
559d00947d8SCraig Rodrigues }
560d00947d8SCraig Rodrigues 
561d00947d8SCraig Rodrigues /*
562d00947d8SCraig Rodrigues  * Remove the unionfs node status, if you can.
563d00947d8SCraig Rodrigues  * You need exclusive lock this vnode.
564d00947d8SCraig Rodrigues  */
565d00947d8SCraig Rodrigues void
566fe5f08cdSDaichi GOTO unionfs_tryrem_node_status(struct unionfs_node *unp,
567d00947d8SCraig Rodrigues     struct unionfs_node_status *unsp)
568d00947d8SCraig Rodrigues {
5693ecefc4aSJason A. Harmening 	KASSERT(NULL != unsp, ("%s: NULL status", __func__));
5703ecefc4aSJason A. Harmening 	ASSERT_VOP_ELOCKED(UNIONFSTOV(unp), __func__);
571d00947d8SCraig Rodrigues 
572d00947d8SCraig Rodrigues 	if (0 < unsp->uns_lower_opencnt || 0 < unsp->uns_upper_opencnt)
573d00947d8SCraig Rodrigues 		return;
574d00947d8SCraig Rodrigues 
575d00947d8SCraig Rodrigues 	LIST_REMOVE(unsp, uns_list);
576d00947d8SCraig Rodrigues 	free(unsp, M_TEMP);
577d00947d8SCraig Rodrigues }
578d00947d8SCraig Rodrigues 
579d00947d8SCraig Rodrigues /*
580d00947d8SCraig Rodrigues  * Create upper node attr.
581d00947d8SCraig Rodrigues  */
582d00947d8SCraig Rodrigues void
583312d49efSJason A. Harmening unionfs_create_uppervattr_core(struct unionfs_mount *ump, struct vattr *lva,
584312d49efSJason A. Harmening     struct vattr *uva, struct thread *td)
585d00947d8SCraig Rodrigues {
586d00947d8SCraig Rodrigues 	VATTR_NULL(uva);
587d00947d8SCraig Rodrigues 	uva->va_type = lva->va_type;
588d00947d8SCraig Rodrigues 	uva->va_atime = lva->va_atime;
589d00947d8SCraig Rodrigues 	uva->va_mtime = lva->va_mtime;
590d00947d8SCraig Rodrigues 	uva->va_ctime = lva->va_ctime;
591d00947d8SCraig Rodrigues 
592d00947d8SCraig Rodrigues 	switch (ump->um_copymode) {
593d00947d8SCraig Rodrigues 	case UNIONFS_TRANSPARENT:
594d00947d8SCraig Rodrigues 		uva->va_mode = lva->va_mode;
595d00947d8SCraig Rodrigues 		uva->va_uid = lva->va_uid;
596d00947d8SCraig Rodrigues 		uva->va_gid = lva->va_gid;
597d00947d8SCraig Rodrigues 		break;
598d00947d8SCraig Rodrigues 	case UNIONFS_MASQUERADE:
599d00947d8SCraig Rodrigues 		if (ump->um_uid == lva->va_uid) {
600d00947d8SCraig Rodrigues 			uva->va_mode = lva->va_mode & 077077;
601312d49efSJason A. Harmening 			uva->va_mode |= (lva->va_type == VDIR ?
602312d49efSJason A. Harmening 			    ump->um_udir : ump->um_ufile) & 0700;
603d00947d8SCraig Rodrigues 			uva->va_uid = lva->va_uid;
604d00947d8SCraig Rodrigues 			uva->va_gid = lva->va_gid;
605df8bae1dSRodney W. Grimes 		} else {
606312d49efSJason A. Harmening 			uva->va_mode = (lva->va_type == VDIR ?
607312d49efSJason A. Harmening 			    ump->um_udir : ump->um_ufile);
608d00947d8SCraig Rodrigues 			uva->va_uid = ump->um_uid;
609d00947d8SCraig Rodrigues 			uva->va_gid = ump->um_gid;
610d00947d8SCraig Rodrigues 		}
611d00947d8SCraig Rodrigues 		break;
612d00947d8SCraig Rodrigues 	default:		/* UNIONFS_TRADITIONAL */
61385078b85SConrad Meyer 		uva->va_mode = 0777 & ~td->td_proc->p_pd->pd_cmask;
614d00947d8SCraig Rodrigues 		uva->va_uid = ump->um_uid;
615d00947d8SCraig Rodrigues 		uva->va_gid = ump->um_gid;
616d00947d8SCraig Rodrigues 		break;
617d00947d8SCraig Rodrigues 	}
618df8bae1dSRodney W. Grimes }
619df8bae1dSRodney W. Grimes 
620d00947d8SCraig Rodrigues /*
621d00947d8SCraig Rodrigues  * Create upper node attr.
622d00947d8SCraig Rodrigues  */
623d00947d8SCraig Rodrigues int
624312d49efSJason A. Harmening unionfs_create_uppervattr(struct unionfs_mount *ump, struct vnode *lvp,
625312d49efSJason A. Harmening     struct vattr *uva, struct ucred *cred, struct thread *td)
626d00947d8SCraig Rodrigues {
627d00947d8SCraig Rodrigues 	struct vattr	lva;
628312d49efSJason A. Harmening 	int		error;
629df8bae1dSRodney W. Grimes 
6300359a12eSAttilio Rao 	if ((error = VOP_GETATTR(lvp, &lva, cred)))
631d00947d8SCraig Rodrigues 		return (error);
632d00947d8SCraig Rodrigues 
633d00947d8SCraig Rodrigues 	unionfs_create_uppervattr_core(ump, &lva, uva, td);
634df8bae1dSRodney W. Grimes 
635df8bae1dSRodney W. Grimes 	return (error);
636df8bae1dSRodney W. Grimes }
637df8bae1dSRodney W. Grimes 
638d00947d8SCraig Rodrigues /*
639d00947d8SCraig Rodrigues  * relookup
640d00947d8SCraig Rodrigues  *
641d00947d8SCraig Rodrigues  * dvp should be locked on entry and will be locked on return.
642d00947d8SCraig Rodrigues  *
643d00947d8SCraig Rodrigues  * If an error is returned, *vpp will be invalid, otherwise it will hold a
644d00947d8SCraig Rodrigues  * locked, referenced vnode. If *vpp == dvp then remember that only one
645d00947d8SCraig Rodrigues  * LK_EXCLUSIVE lock is held.
646d00947d8SCraig Rodrigues  */
6471e5da15aSDaichi GOTO int
648d00947d8SCraig Rodrigues unionfs_relookup(struct vnode *dvp, struct vnode **vpp,
649312d49efSJason A. Harmening     struct componentname *cnp, struct componentname *cn, struct thread *td,
650312d49efSJason A. Harmening     char *path, int pathlen, u_long nameiop)
651df8bae1dSRodney W. Grimes {
652d00947d8SCraig Rodrigues 	int error;
653df8bae1dSRodney W. Grimes 
654d00947d8SCraig Rodrigues 	cn->cn_namelen = pathlen;
655abe95116SJason A. Harmening 	cn->cn_pnbuf = path;
656d00947d8SCraig Rodrigues 	cn->cn_nameiop = nameiop;
657d00947d8SCraig Rodrigues 	cn->cn_flags = (LOCKPARENT | LOCKLEAF | HASBUF | SAVENAME | ISLASTCN);
658d00947d8SCraig Rodrigues 	cn->cn_lkflags = LK_EXCLUSIVE;
659d00947d8SCraig Rodrigues 	cn->cn_cred = cnp->cn_cred;
660d00947d8SCraig Rodrigues 	cn->cn_nameptr = cn->cn_pnbuf;
661df8bae1dSRodney W. Grimes 
662d00947d8SCraig Rodrigues 	if (nameiop == DELETE)
663d00947d8SCraig Rodrigues 		cn->cn_flags |= (cnp->cn_flags & (DOWHITEOUT | SAVESTART));
664d00947d8SCraig Rodrigues 	else if (RENAME == nameiop)
665d00947d8SCraig Rodrigues 		cn->cn_flags |= (cnp->cn_flags & SAVESTART);
6666c21f6edSKonstantin Belousov 	else if (nameiop == CREATE)
6676c21f6edSKonstantin Belousov 		cn->cn_flags |= NOCACHE;
668d00947d8SCraig Rodrigues 
669d00947d8SCraig Rodrigues 	vref(dvp);
670b249ce48SMateusz Guzik 	VOP_UNLOCK(dvp);
671d00947d8SCraig Rodrigues 
672d00947d8SCraig Rodrigues 	if ((error = relookup(dvp, vpp, cn))) {
673cb05b60aSAttilio Rao 		vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
674d00947d8SCraig Rodrigues 	} else
675d00947d8SCraig Rodrigues 		vrele(dvp);
676d00947d8SCraig Rodrigues 
677abe95116SJason A. Harmening 	KASSERT((cn->cn_flags & HASBUF) != 0,
678abe95116SJason A. Harmening 	    ("%s: HASBUF cleared", __func__));
679abe95116SJason A. Harmening 	KASSERT((cn->cn_flags & SAVENAME) != 0,
680abe95116SJason A. Harmening 	    ("%s: SAVENAME cleared", __func__));
681abe95116SJason A. Harmening 	KASSERT(cn->cn_pnbuf == path, ("%s: cn_pnbuf changed", __func__));
682abe95116SJason A. Harmening 
683d00947d8SCraig Rodrigues 	return (error);
684df8bae1dSRodney W. Grimes }
685df8bae1dSRodney W. Grimes 
686df8bae1dSRodney W. Grimes /*
687d00947d8SCraig Rodrigues  * relookup for CREATE namei operation.
6882a31267eSMatthew Dillon  *
689d00947d8SCraig Rodrigues  * dvp is unionfs vnode. dvp should be locked.
690d00947d8SCraig Rodrigues  *
691d00947d8SCraig Rodrigues  * If it called 'unionfs_copyfile' function by unionfs_link etc,
692d00947d8SCraig Rodrigues  * VOP_LOOKUP information is broken.
693d00947d8SCraig Rodrigues  * So it need relookup in order to create link etc.
694d00947d8SCraig Rodrigues  */
695d00947d8SCraig Rodrigues int
696d00947d8SCraig Rodrigues unionfs_relookup_for_create(struct vnode *dvp, struct componentname *cnp,
697d00947d8SCraig Rodrigues     struct thread *td)
698d00947d8SCraig Rodrigues {
699d00947d8SCraig Rodrigues 	struct vnode *udvp;
700d00947d8SCraig Rodrigues 	struct vnode *vp;
701d00947d8SCraig Rodrigues 	struct componentname cn;
702312d49efSJason A. Harmening 	int error;
703d00947d8SCraig Rodrigues 
704d00947d8SCraig Rodrigues 	udvp = UNIONFSVPTOUPPERVP(dvp);
705d00947d8SCraig Rodrigues 	vp = NULLVP;
706d00947d8SCraig Rodrigues 
707152c35eeSJason A. Harmening 	KASSERT((cnp->cn_flags & HASBUF) != 0,
708152c35eeSJason A. Harmening 	    ("%s called without HASBUF", __func__));
709d00947d8SCraig Rodrigues 	error = unionfs_relookup(udvp, &vp, cnp, &cn, td, cnp->cn_nameptr,
710abe95116SJason A. Harmening 	    cnp->cn_namelen, CREATE);
711d00947d8SCraig Rodrigues 	if (error)
712d00947d8SCraig Rodrigues 		return (error);
713d00947d8SCraig Rodrigues 
714d00947d8SCraig Rodrigues 	if (vp != NULLVP) {
715d00947d8SCraig Rodrigues 		if (udvp == vp)
716d00947d8SCraig Rodrigues 			vrele(vp);
717d00947d8SCraig Rodrigues 		else
718d00947d8SCraig Rodrigues 			vput(vp);
719d00947d8SCraig Rodrigues 
720d00947d8SCraig Rodrigues 		error = EEXIST;
721d00947d8SCraig Rodrigues 	}
722d00947d8SCraig Rodrigues 
723d00947d8SCraig Rodrigues 	return (error);
724d00947d8SCraig Rodrigues }
725d00947d8SCraig Rodrigues 
726d00947d8SCraig Rodrigues /*
727d00947d8SCraig Rodrigues  * relookup for DELETE namei operation.
728d00947d8SCraig Rodrigues  *
729d00947d8SCraig Rodrigues  * dvp is unionfs vnode. dvp should be locked.
730d00947d8SCraig Rodrigues  */
731d00947d8SCraig Rodrigues int
732d00947d8SCraig Rodrigues unionfs_relookup_for_delete(struct vnode *dvp, struct componentname *cnp,
733d00947d8SCraig Rodrigues     struct thread *td)
734d00947d8SCraig Rodrigues {
735d00947d8SCraig Rodrigues 	struct vnode *udvp;
736d00947d8SCraig Rodrigues 	struct vnode *vp;
737d00947d8SCraig Rodrigues 	struct componentname cn;
738312d49efSJason A. Harmening 	int error;
739d00947d8SCraig Rodrigues 
740d00947d8SCraig Rodrigues 	udvp = UNIONFSVPTOUPPERVP(dvp);
741d00947d8SCraig Rodrigues 	vp = NULLVP;
742d00947d8SCraig Rodrigues 
743152c35eeSJason A. Harmening 	KASSERT((cnp->cn_flags & HASBUF) != 0,
744152c35eeSJason A. Harmening 	    ("%s called without HASBUF", __func__));
745d00947d8SCraig Rodrigues 	error = unionfs_relookup(udvp, &vp, cnp, &cn, td, cnp->cn_nameptr,
746abe95116SJason A. Harmening 	    cnp->cn_namelen, DELETE);
747d00947d8SCraig Rodrigues 	if (error)
748d00947d8SCraig Rodrigues 		return (error);
749d00947d8SCraig Rodrigues 
750d00947d8SCraig Rodrigues 	if (vp == NULLVP)
751d00947d8SCraig Rodrigues 		error = ENOENT;
752d00947d8SCraig Rodrigues 	else {
753d00947d8SCraig Rodrigues 		if (udvp == vp)
754d00947d8SCraig Rodrigues 			vrele(vp);
755d00947d8SCraig Rodrigues 		else
756d00947d8SCraig Rodrigues 			vput(vp);
757d00947d8SCraig Rodrigues 	}
758d00947d8SCraig Rodrigues 
759d00947d8SCraig Rodrigues 	return (error);
760d00947d8SCraig Rodrigues }
761d00947d8SCraig Rodrigues 
762d00947d8SCraig Rodrigues /*
763d00947d8SCraig Rodrigues  * relookup for RENAME namei operation.
764d00947d8SCraig Rodrigues  *
765d00947d8SCraig Rodrigues  * dvp is unionfs vnode. dvp should be locked.
766d00947d8SCraig Rodrigues  */
767d00947d8SCraig Rodrigues int
768d00947d8SCraig Rodrigues unionfs_relookup_for_rename(struct vnode *dvp, struct componentname *cnp,
769d00947d8SCraig Rodrigues     struct thread *td)
770d00947d8SCraig Rodrigues {
771d00947d8SCraig Rodrigues 	struct vnode *udvp;
772d00947d8SCraig Rodrigues 	struct vnode *vp;
773d00947d8SCraig Rodrigues 	struct componentname cn;
774312d49efSJason A. Harmening 	int error;
775d00947d8SCraig Rodrigues 
776d00947d8SCraig Rodrigues 	udvp = UNIONFSVPTOUPPERVP(dvp);
777d00947d8SCraig Rodrigues 	vp = NULLVP;
778d00947d8SCraig Rodrigues 
779152c35eeSJason A. Harmening 	KASSERT((cnp->cn_flags & HASBUF) != 0,
780152c35eeSJason A. Harmening 	    ("%s called without HASBUF", __func__));
781d00947d8SCraig Rodrigues 	error = unionfs_relookup(udvp, &vp, cnp, &cn, td, cnp->cn_nameptr,
782abe95116SJason A. Harmening 	    cnp->cn_namelen, RENAME);
783d00947d8SCraig Rodrigues 	if (error)
784d00947d8SCraig Rodrigues 		return (error);
785d00947d8SCraig Rodrigues 
786d00947d8SCraig Rodrigues 	if (vp != NULLVP) {
787d00947d8SCraig Rodrigues 		if (udvp == vp)
788d00947d8SCraig Rodrigues 			vrele(vp);
789d00947d8SCraig Rodrigues 		else
790d00947d8SCraig Rodrigues 			vput(vp);
791d00947d8SCraig Rodrigues 	}
792d00947d8SCraig Rodrigues 
793d00947d8SCraig Rodrigues 	return (error);
794d00947d8SCraig Rodrigues }
795d00947d8SCraig Rodrigues 
796d00947d8SCraig Rodrigues /*
797d00947d8SCraig Rodrigues  * Update the unionfs_node.
798d00947d8SCraig Rodrigues  *
799d00947d8SCraig Rodrigues  * uvp is new locked upper vnode. unionfs vnode's lock will be exchanged to the
800d00947d8SCraig Rodrigues  * uvp's lock and lower's lock will be unlocked.
801d00947d8SCraig Rodrigues  */
802d00947d8SCraig Rodrigues static void
803d00947d8SCraig Rodrigues unionfs_node_update(struct unionfs_node *unp, struct vnode *uvp,
804d00947d8SCraig Rodrigues     struct thread *td)
805d00947d8SCraig Rodrigues {
806fd8ad212SJason A. Harmening 	struct unionfs_node_hashhead *hd;
807d00947d8SCraig Rodrigues 	struct vnode   *vp;
808d00947d8SCraig Rodrigues 	struct vnode   *lvp;
809a9b794ffSDaichi GOTO 	struct vnode   *dvp;
810312d49efSJason A. Harmening 	unsigned	count, lockrec;
811d00947d8SCraig Rodrigues 
812d00947d8SCraig Rodrigues 	vp = UNIONFSTOV(unp);
813d00947d8SCraig Rodrigues 	lvp = unp->un_lowervp;
8143ecefc4aSJason A. Harmening 	ASSERT_VOP_ELOCKED(lvp, __func__);
81566191a76SJason A. Harmening 	ASSERT_VOP_ELOCKED(uvp, __func__);
816a9b794ffSDaichi GOTO 	dvp = unp->un_dvp;
817d00947d8SCraig Rodrigues 
818d877dd57SJason A. Harmening 	VNASSERT(vp->v_writecount == 0, vp,
819d877dd57SJason A. Harmening 	    ("%s: non-zero writecount", __func__));
820d00947d8SCraig Rodrigues 	/*
821866dd633SJason A. Harmening 	 * Uppdate the upper vnode's lock state to match the lower vnode,
822866dd633SJason A. Harmening 	 * and then switch the unionfs vnode's lock to the upper vnode.
823d00947d8SCraig Rodrigues 	 */
824866dd633SJason A. Harmening 	lockrec = lvp->v_vnlock->lk_recurse;
825866dd633SJason A. Harmening 	for (count = 0; count < lockrec; count++)
826866dd633SJason A. Harmening 		vn_lock(uvp, LK_EXCLUSIVE | LK_CANRECURSE | LK_RETRY);
827d00947d8SCraig Rodrigues 	VI_LOCK(vp);
828d00947d8SCraig Rodrigues 	unp->un_uppervp = uvp;
829d00947d8SCraig Rodrigues 	vp->v_vnlock = uvp->v_vnlock;
830d00947d8SCraig Rodrigues 	VI_UNLOCK(vp);
831a9b794ffSDaichi GOTO 
832a9b794ffSDaichi GOTO 	/*
833fd8ad212SJason A. Harmening 	 * Re-cache the unionfs vnode against the upper vnode
834a9b794ffSDaichi GOTO 	 */
835fd8ad212SJason A. Harmening 	if (dvp != NULLVP && vp->v_type == VDIR) {
836a9b794ffSDaichi GOTO 		VI_LOCK(dvp);
837fd8ad212SJason A. Harmening 		if (unp->un_hash.le_prev != NULL) {
838a9b794ffSDaichi GOTO 			LIST_REMOVE(unp, un_hash);
839fd8ad212SJason A. Harmening 			hd = unionfs_get_hashhead(dvp, uvp);
840a9b794ffSDaichi GOTO 			LIST_INSERT_HEAD(hd, unp, un_hash);
841fd8ad212SJason A. Harmening 		}
842fd8ad212SJason A. Harmening 		VI_UNLOCK(unp->un_dvp);
843a9b794ffSDaichi GOTO 	}
844d00947d8SCraig Rodrigues }
845d00947d8SCraig Rodrigues 
846d00947d8SCraig Rodrigues /*
847d00947d8SCraig Rodrigues  * Create a new shadow dir.
848d00947d8SCraig Rodrigues  *
849d00947d8SCraig Rodrigues  * udvp should be locked on entry and will be locked on return.
850d00947d8SCraig Rodrigues  *
851d00947d8SCraig Rodrigues  * If no error returned, unp will be updated.
852d00947d8SCraig Rodrigues  */
853d00947d8SCraig Rodrigues int
854d00947d8SCraig Rodrigues unionfs_mkshadowdir(struct unionfs_mount *ump, struct vnode *udvp,
855312d49efSJason A. Harmening     struct unionfs_node *unp, struct componentname *cnp, struct thread *td)
856d00947d8SCraig Rodrigues {
857d00947d8SCraig Rodrigues 	struct vnode   *lvp;
858d00947d8SCraig Rodrigues 	struct vnode   *uvp;
859d00947d8SCraig Rodrigues 	struct vattr	va;
860d00947d8SCraig Rodrigues 	struct vattr	lva;
861190110f2SKonstantin Belousov 	struct nameidata nd;
862d00947d8SCraig Rodrigues 	struct mount   *mp;
863d00947d8SCraig Rodrigues 	struct ucred   *cred;
864d00947d8SCraig Rodrigues 	struct ucred   *credbk;
865d00947d8SCraig Rodrigues 	struct uidinfo *rootinfo;
866312d49efSJason A. Harmening 	int		error;
867d00947d8SCraig Rodrigues 
868d00947d8SCraig Rodrigues 	if (unp->un_uppervp != NULLVP)
869d00947d8SCraig Rodrigues 		return (EEXIST);
870d00947d8SCraig Rodrigues 
871d00947d8SCraig Rodrigues 	lvp = unp->un_lowervp;
872d00947d8SCraig Rodrigues 	uvp = NULLVP;
873d00947d8SCraig Rodrigues 	credbk = cnp->cn_cred;
874d00947d8SCraig Rodrigues 
875d00947d8SCraig Rodrigues 	/* Authority change to root */
876d00947d8SCraig Rodrigues 	rootinfo = uifind((uid_t)0);
877d00947d8SCraig Rodrigues 	cred = crdup(cnp->cn_cred);
87881f6480dSEdward Tomasz Napierala 	/*
87981f6480dSEdward Tomasz Napierala 	 * The calls to chgproccnt() are needed to compensate for change_ruid()
88081f6480dSEdward Tomasz Napierala 	 * calling chgproccnt().
88181f6480dSEdward Tomasz Napierala 	 */
88281f6480dSEdward Tomasz Napierala 	chgproccnt(cred->cr_ruidinfo, 1, 0);
883d00947d8SCraig Rodrigues 	change_euid(cred, rootinfo);
884d00947d8SCraig Rodrigues 	change_ruid(cred, rootinfo);
885d00947d8SCraig Rodrigues 	change_svuid(cred, (uid_t)0);
886d00947d8SCraig Rodrigues 	uifree(rootinfo);
887d00947d8SCraig Rodrigues 	cnp->cn_cred = cred;
888d00947d8SCraig Rodrigues 
889190110f2SKonstantin Belousov 	memset(&nd.ni_cnd, 0, sizeof(struct componentname));
890190110f2SKonstantin Belousov 	NDPREINIT(&nd);
891d00947d8SCraig Rodrigues 
8920359a12eSAttilio Rao 	if ((error = VOP_GETATTR(lvp, &lva, cnp->cn_cred)))
893d00947d8SCraig Rodrigues 		goto unionfs_mkshadowdir_abort;
894d00947d8SCraig Rodrigues 
895190110f2SKonstantin Belousov 	if ((error = unionfs_relookup(udvp, &uvp, cnp, &nd.ni_cnd, td,
896190110f2SKonstantin Belousov 	    cnp->cn_nameptr, cnp->cn_namelen, CREATE)))
897d00947d8SCraig Rodrigues 		goto unionfs_mkshadowdir_abort;
898d00947d8SCraig Rodrigues 	if (uvp != NULLVP) {
899d00947d8SCraig Rodrigues 		if (udvp == uvp)
900d00947d8SCraig Rodrigues 			vrele(uvp);
901d00947d8SCraig Rodrigues 		else
902d00947d8SCraig Rodrigues 			vput(uvp);
903d00947d8SCraig Rodrigues 
904d00947d8SCraig Rodrigues 		error = EEXIST;
905abe95116SJason A. Harmening 		goto unionfs_mkshadowdir_abort;
906d00947d8SCraig Rodrigues 	}
907d00947d8SCraig Rodrigues 
908d00947d8SCraig Rodrigues 	if ((error = vn_start_write(udvp, &mp, V_WAIT | PCATCH)))
909abe95116SJason A. Harmening 		goto unionfs_mkshadowdir_abort;
910d00947d8SCraig Rodrigues 	unionfs_create_uppervattr_core(ump, &lva, &va, td);
911d00947d8SCraig Rodrigues 
912190110f2SKonstantin Belousov 	error = VOP_MKDIR(udvp, &uvp, &nd.ni_cnd, &va);
913d00947d8SCraig Rodrigues 
914d00947d8SCraig Rodrigues 	if (!error) {
915d00947d8SCraig Rodrigues 		unionfs_node_update(unp, uvp, td);
916d00947d8SCraig Rodrigues 
917d00947d8SCraig Rodrigues 		/*
918d00947d8SCraig Rodrigues 		 * XXX The bug which cannot set uid/gid was corrected.
919d00947d8SCraig Rodrigues 		 * Ignore errors.
920d00947d8SCraig Rodrigues 		 */
921d00947d8SCraig Rodrigues 		va.va_type = VNON;
922190110f2SKonstantin Belousov 		VOP_SETATTR(uvp, &va, nd.ni_cnd.cn_cred);
923d00947d8SCraig Rodrigues 	}
924d00947d8SCraig Rodrigues 	vn_finished_write(mp);
925d00947d8SCraig Rodrigues 
926d00947d8SCraig Rodrigues unionfs_mkshadowdir_abort:
927d00947d8SCraig Rodrigues 	cnp->cn_cred = credbk;
92881f6480dSEdward Tomasz Napierala 	chgproccnt(cred->cr_ruidinfo, -1, 0);
929d00947d8SCraig Rodrigues 	crfree(cred);
930d00947d8SCraig Rodrigues 
931d00947d8SCraig Rodrigues 	return (error);
932d00947d8SCraig Rodrigues }
933d00947d8SCraig Rodrigues 
934d00947d8SCraig Rodrigues /*
935d00947d8SCraig Rodrigues  * Create a new whiteout.
936d00947d8SCraig Rodrigues  *
937d00947d8SCraig Rodrigues  * dvp should be locked on entry and will be locked on return.
938d00947d8SCraig Rodrigues  */
939d00947d8SCraig Rodrigues int
940d00947d8SCraig Rodrigues unionfs_mkwhiteout(struct vnode *dvp, struct componentname *cnp,
941abe95116SJason A. Harmening     struct thread *td, char *path, int pathlen)
942d00947d8SCraig Rodrigues {
943d00947d8SCraig Rodrigues 	struct vnode   *wvp;
944190110f2SKonstantin Belousov 	struct nameidata nd;
945d00947d8SCraig Rodrigues 	struct mount   *mp;
946abe95116SJason A. Harmening 	int		error;
947d00947d8SCraig Rodrigues 
948d00947d8SCraig Rodrigues 	wvp = NULLVP;
949190110f2SKonstantin Belousov 	NDPREINIT(&nd);
950190110f2SKonstantin Belousov 	if ((error = unionfs_relookup(dvp, &wvp, cnp, &nd.ni_cnd, td, path,
951abe95116SJason A. Harmening 	    pathlen, CREATE))) {
952d00947d8SCraig Rodrigues 		return (error);
953d00947d8SCraig Rodrigues 	}
954abe95116SJason A. Harmening 	if (wvp != NULLVP) {
955d00947d8SCraig Rodrigues 		if (dvp == wvp)
956d00947d8SCraig Rodrigues 			vrele(wvp);
957d00947d8SCraig Rodrigues 		else
958d00947d8SCraig Rodrigues 			vput(wvp);
959d00947d8SCraig Rodrigues 
960d00947d8SCraig Rodrigues 		return (EEXIST);
961d00947d8SCraig Rodrigues 	}
962d00947d8SCraig Rodrigues 
963d00947d8SCraig Rodrigues 	if ((error = vn_start_write(dvp, &mp, V_WAIT | PCATCH)))
964d00947d8SCraig Rodrigues 		goto unionfs_mkwhiteout_free_out;
965190110f2SKonstantin Belousov 	error = VOP_WHITEOUT(dvp, &nd.ni_cnd, CREATE);
966d00947d8SCraig Rodrigues 
967d00947d8SCraig Rodrigues 	vn_finished_write(mp);
968d00947d8SCraig Rodrigues 
969d00947d8SCraig Rodrigues unionfs_mkwhiteout_free_out:
970d00947d8SCraig Rodrigues 	return (error);
971d00947d8SCraig Rodrigues }
972d00947d8SCraig Rodrigues 
973d00947d8SCraig Rodrigues /*
974d00947d8SCraig Rodrigues  * Create a new vnode for create a new shadow file.
975d00947d8SCraig Rodrigues  *
976d00947d8SCraig Rodrigues  * If an error is returned, *vpp will be invalid, otherwise it will hold a
977d00947d8SCraig Rodrigues  * locked, referenced and opened vnode.
978d00947d8SCraig Rodrigues  *
979d00947d8SCraig Rodrigues  * unp is never updated.
980df8bae1dSRodney W. Grimes  */
98180b301c3SPoul-Henning Kamp static int
982d00947d8SCraig Rodrigues unionfs_vn_create_on_upper(struct vnode **vpp, struct vnode *udvp,
983312d49efSJason A. Harmening     struct unionfs_node *unp, struct vattr *uvap, struct thread *td)
984df8bae1dSRodney W. Grimes {
985d00947d8SCraig Rodrigues 	struct unionfs_mount *ump;
986d00947d8SCraig Rodrigues 	struct vnode   *vp;
987d00947d8SCraig Rodrigues 	struct vnode   *lvp;
988d00947d8SCraig Rodrigues 	struct ucred   *cred;
989d00947d8SCraig Rodrigues 	struct vattr	lva;
990312d49efSJason A. Harmening 	struct nameidata nd;
991d00947d8SCraig Rodrigues 	int		fmode;
992d00947d8SCraig Rodrigues 	int		error;
993d00947d8SCraig Rodrigues 
994d00947d8SCraig Rodrigues 	ump = MOUNTTOUNIONFSMOUNT(UNIONFSTOV(unp)->v_mount);
995d00947d8SCraig Rodrigues 	vp = NULLVP;
996d00947d8SCraig Rodrigues 	lvp = unp->un_lowervp;
997d00947d8SCraig Rodrigues 	cred = td->td_ucred;
998d00947d8SCraig Rodrigues 	fmode = FFLAGS(O_WRONLY | O_CREAT | O_TRUNC | O_EXCL);
999d00947d8SCraig Rodrigues 	error = 0;
1000d00947d8SCraig Rodrigues 
10010359a12eSAttilio Rao 	if ((error = VOP_GETATTR(lvp, &lva, cred)) != 0)
1002d00947d8SCraig Rodrigues 		return (error);
1003d00947d8SCraig Rodrigues 	unionfs_create_uppervattr_core(ump, &lva, uvap, td);
1004d00947d8SCraig Rodrigues 
1005d00947d8SCraig Rodrigues 	if (unp->un_path == NULL)
10063ecefc4aSJason A. Harmening 		panic("%s: NULL un_path", __func__);
1007d00947d8SCraig Rodrigues 
1008abe95116SJason A. Harmening 	nd.ni_cnd.cn_namelen = unp->un_pathlen;
1009abe95116SJason A. Harmening 	nd.ni_cnd.cn_pnbuf = unp->un_path;
1010190110f2SKonstantin Belousov 	nd.ni_cnd.cn_nameiop = CREATE;
1011190110f2SKonstantin Belousov 	nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF | HASBUF | SAVENAME |
1012190110f2SKonstantin Belousov 	    ISLASTCN;
1013190110f2SKonstantin Belousov 	nd.ni_cnd.cn_lkflags = LK_EXCLUSIVE;
1014190110f2SKonstantin Belousov 	nd.ni_cnd.cn_cred = cred;
1015190110f2SKonstantin Belousov 	nd.ni_cnd.cn_nameptr = nd.ni_cnd.cn_pnbuf;
1016190110f2SKonstantin Belousov 	NDPREINIT(&nd);
1017d00947d8SCraig Rodrigues 
1018d00947d8SCraig Rodrigues 	vref(udvp);
1019190110f2SKonstantin Belousov 	if ((error = relookup(udvp, &vp, &nd.ni_cnd)) != 0)
1020d00947d8SCraig Rodrigues 		goto unionfs_vn_create_on_upper_free_out2;
1021d00947d8SCraig Rodrigues 	vrele(udvp);
1022d00947d8SCraig Rodrigues 
1023d00947d8SCraig Rodrigues 	if (vp != NULLVP) {
1024d00947d8SCraig Rodrigues 		if (vp == udvp)
1025d00947d8SCraig Rodrigues 			vrele(vp);
1026d00947d8SCraig Rodrigues 		else
1027d00947d8SCraig Rodrigues 			vput(vp);
1028d00947d8SCraig Rodrigues 		error = EEXIST;
1029d00947d8SCraig Rodrigues 		goto unionfs_vn_create_on_upper_free_out1;
1030d00947d8SCraig Rodrigues 	}
1031d00947d8SCraig Rodrigues 
1032190110f2SKonstantin Belousov 	if ((error = VOP_CREATE(udvp, &vp, &nd.ni_cnd, uvap)) != 0)
1033d00947d8SCraig Rodrigues 		goto unionfs_vn_create_on_upper_free_out1;
1034d00947d8SCraig Rodrigues 
10359e223287SKonstantin Belousov 	if ((error = VOP_OPEN(vp, fmode, cred, td, NULL)) != 0) {
1036d00947d8SCraig Rodrigues 		vput(vp);
1037d00947d8SCraig Rodrigues 		goto unionfs_vn_create_on_upper_free_out1;
1038d00947d8SCraig Rodrigues 	}
103978022527SKonstantin Belousov 	error = VOP_ADD_WRITECOUNT(vp, 1);
1040312d49efSJason A. Harmening 	CTR3(KTR_VFS, "%s: vp %p v_writecount increased to %d",
1041312d49efSJason A. Harmening 	    __func__, vp, vp->v_writecount);
104278022527SKonstantin Belousov 	if (error == 0) {
1043d00947d8SCraig Rodrigues 		*vpp = vp;
104478022527SKonstantin Belousov 	} else {
104578022527SKonstantin Belousov 		VOP_CLOSE(vp, fmode, cred, td);
104678022527SKonstantin Belousov 	}
1047d00947d8SCraig Rodrigues 
1048d00947d8SCraig Rodrigues unionfs_vn_create_on_upper_free_out1:
1049b249ce48SMateusz Guzik 	VOP_UNLOCK(udvp);
1050d00947d8SCraig Rodrigues 
1051d00947d8SCraig Rodrigues unionfs_vn_create_on_upper_free_out2:
1052abe95116SJason A. Harmening 	KASSERT((nd.ni_cnd.cn_flags & HASBUF) != 0,
1053abe95116SJason A. Harmening 	    ("%s: HASBUF cleared", __func__));
1054abe95116SJason A. Harmening 	KASSERT((nd.ni_cnd.cn_flags & SAVENAME) != 0,
1055abe95116SJason A. Harmening 	    ("%s: SAVENAME cleared", __func__));
1056abe95116SJason A. Harmening 	KASSERT(nd.ni_cnd.cn_pnbuf == unp->un_path,
1057abe95116SJason A. Harmening 	    ("%s: cn_pnbuf changed", __func__));
1058d00947d8SCraig Rodrigues 
1059d00947d8SCraig Rodrigues 	return (error);
1060d00947d8SCraig Rodrigues }
1061d00947d8SCraig Rodrigues 
1062d00947d8SCraig Rodrigues /*
1063d00947d8SCraig Rodrigues  * Copy from lvp to uvp.
1064d00947d8SCraig Rodrigues  *
1065d00947d8SCraig Rodrigues  * lvp and uvp should be locked and opened on entry and will be locked and
1066d00947d8SCraig Rodrigues  * opened on return.
1067d00947d8SCraig Rodrigues  */
1068d00947d8SCraig Rodrigues static int
1069d00947d8SCraig Rodrigues unionfs_copyfile_core(struct vnode *lvp, struct vnode *uvp,
1070d00947d8SCraig Rodrigues     struct ucred *cred, struct thread *td)
1071d00947d8SCraig Rodrigues {
1072df8bae1dSRodney W. Grimes 	char           *buf;
1073df8bae1dSRodney W. Grimes 	struct uio	uio;
1074df8bae1dSRodney W. Grimes 	struct iovec	iov;
1075312d49efSJason A. Harmening 	off_t		offset;
1076312d49efSJason A. Harmening 	int		count;
1077312d49efSJason A. Harmening 	int		error;
1078312d49efSJason A. Harmening 	int		bufoffset;
1079df8bae1dSRodney W. Grimes 
1080d00947d8SCraig Rodrigues 	error = 0;
1081d00947d8SCraig Rodrigues 	memset(&uio, 0, sizeof(uio));
10822a31267eSMatthew Dillon 
1083b40ce416SJulian Elischer 	uio.uio_td = td;
1084df8bae1dSRodney W. Grimes 	uio.uio_segflg = UIO_SYSSPACE;
1085df8bae1dSRodney W. Grimes 	uio.uio_offset = 0;
1086df8bae1dSRodney W. Grimes 
1087a163d034SWarner Losh 	buf = malloc(MAXBSIZE, M_TEMP, M_WAITOK);
1088df8bae1dSRodney W. Grimes 
1089d00947d8SCraig Rodrigues 	while (error == 0) {
1090d00947d8SCraig Rodrigues 		offset = uio.uio_offset;
1091df8bae1dSRodney W. Grimes 
1092df8bae1dSRodney W. Grimes 		uio.uio_iov = &iov;
1093df8bae1dSRodney W. Grimes 		uio.uio_iovcnt = 1;
1094df8bae1dSRodney W. Grimes 		iov.iov_base = buf;
1095df8bae1dSRodney W. Grimes 		iov.iov_len = MAXBSIZE;
1096df8bae1dSRodney W. Grimes 		uio.uio_resid = iov.iov_len;
1097df8bae1dSRodney W. Grimes 		uio.uio_rw = UIO_READ;
1098df8bae1dSRodney W. Grimes 
1099d00947d8SCraig Rodrigues 		if ((error = VOP_READ(lvp, &uio, 0, cred)) != 0)
11002a31267eSMatthew Dillon 			break;
11012a31267eSMatthew Dillon 		if ((count = MAXBSIZE - uio.uio_resid) == 0)
11022a31267eSMatthew Dillon 			break;
11032a31267eSMatthew Dillon 
1104d00947d8SCraig Rodrigues 		bufoffset = 0;
11052a31267eSMatthew Dillon 		while (bufoffset < count) {
1106df8bae1dSRodney W. Grimes 			uio.uio_iov = &iov;
1107df8bae1dSRodney W. Grimes 			uio.uio_iovcnt = 1;
11082a31267eSMatthew Dillon 			iov.iov_base = buf + bufoffset;
11092a31267eSMatthew Dillon 			iov.iov_len = count - bufoffset;
11102a31267eSMatthew Dillon 			uio.uio_offset = offset + bufoffset;
1111df8bae1dSRodney W. Grimes 			uio.uio_resid = iov.iov_len;
1112d00947d8SCraig Rodrigues 			uio.uio_rw = UIO_WRITE;
1113df8bae1dSRodney W. Grimes 
1114d00947d8SCraig Rodrigues 			if ((error = VOP_WRITE(uvp, &uio, 0, cred)) != 0)
1115df8bae1dSRodney W. Grimes 				break;
1116d00947d8SCraig Rodrigues 
11172a31267eSMatthew Dillon 			bufoffset += (count - bufoffset) - uio.uio_resid;
1118df8bae1dSRodney W. Grimes 		}
1119d00947d8SCraig Rodrigues 
11202a31267eSMatthew Dillon 		uio.uio_offset = offset + bufoffset;
1121d00947d8SCraig Rodrigues 	}
1122df8bae1dSRodney W. Grimes 
1123df8bae1dSRodney W. Grimes 	free(buf, M_TEMP);
1124d00947d8SCraig Rodrigues 
1125df8bae1dSRodney W. Grimes 	return (error);
1126df8bae1dSRodney W. Grimes }
1127df8bae1dSRodney W. Grimes 
1128df8bae1dSRodney W. Grimes /*
1129d00947d8SCraig Rodrigues  * Copy file from lower to upper.
11302a31267eSMatthew Dillon  *
1131d00947d8SCraig Rodrigues  * If you need copy of the contents, set 1 to docopy. Otherwise, set 0 to
1132d00947d8SCraig Rodrigues  * docopy.
1133d00947d8SCraig Rodrigues  *
1134d00947d8SCraig Rodrigues  * If no error returned, unp will be updated.
1135996c772fSJohn Dyson  */
1136996c772fSJohn Dyson int
1137d00947d8SCraig Rodrigues unionfs_copyfile(struct unionfs_node *unp, int docopy, struct ucred *cred,
1138d00947d8SCraig Rodrigues     struct thread *td)
1139996c772fSJohn Dyson {
1140f2a2857bSKirk McKusick 	struct mount   *mp;
1141d00947d8SCraig Rodrigues 	struct vnode   *udvp;
1142d00947d8SCraig Rodrigues 	struct vnode   *lvp;
1143d00947d8SCraig Rodrigues 	struct vnode   *uvp;
1144d00947d8SCraig Rodrigues 	struct vattr	uva;
1145312d49efSJason A. Harmening 	int		error;
1146996c772fSJohn Dyson 
1147d00947d8SCraig Rodrigues 	lvp = unp->un_lowervp;
1148d00947d8SCraig Rodrigues 	uvp = NULLVP;
1149d00947d8SCraig Rodrigues 
1150d00947d8SCraig Rodrigues 	if ((UNIONFSTOV(unp)->v_mount->mnt_flag & MNT_RDONLY))
1151d00947d8SCraig Rodrigues 		return (EROFS);
1152d00947d8SCraig Rodrigues 	if (unp->un_dvp == NULLVP)
1153d00947d8SCraig Rodrigues 		return (EINVAL);
1154d00947d8SCraig Rodrigues 	if (unp->un_uppervp != NULLVP)
1155d00947d8SCraig Rodrigues 		return (EEXIST);
1156d00947d8SCraig Rodrigues 	udvp = VTOUNIONFS(unp->un_dvp)->un_uppervp;
1157d00947d8SCraig Rodrigues 	if (udvp == NULLVP)
1158d00947d8SCraig Rodrigues 		return (EROFS);
1159d00947d8SCraig Rodrigues 	if ((udvp->v_mount->mnt_flag & MNT_RDONLY))
1160d00947d8SCraig Rodrigues 		return (EROFS);
1161d00947d8SCraig Rodrigues 
1162d00947d8SCraig Rodrigues 	error = VOP_ACCESS(lvp, VREAD, cred, td);
1163d00947d8SCraig Rodrigues 	if (error != 0)
11645842d4e5SKATO Takenori 		return (error);
11655842d4e5SKATO Takenori 
1166d00947d8SCraig Rodrigues 	if ((error = vn_start_write(udvp, &mp, V_WAIT | PCATCH)) != 0)
1167996c772fSJohn Dyson 		return (error);
1168d00947d8SCraig Rodrigues 	error = unionfs_vn_create_on_upper(&uvp, udvp, unp, &uva, td);
1169d00947d8SCraig Rodrigues 	if (error != 0) {
1170f2a2857bSKirk McKusick 		vn_finished_write(mp);
1171f2a2857bSKirk McKusick 		return (error);
1172f2a2857bSKirk McKusick 	}
1173996c772fSJohn Dyson 
1174d00947d8SCraig Rodrigues 	if (docopy != 0) {
11759e223287SKonstantin Belousov 		error = VOP_OPEN(lvp, FREAD, cred, td, NULL);
1176996c772fSJohn Dyson 		if (error == 0) {
1177d00947d8SCraig Rodrigues 			error = unionfs_copyfile_core(lvp, uvp, cred, td);
1178d00947d8SCraig Rodrigues 			VOP_CLOSE(lvp, FREAD, cred, td);
1179996c772fSJohn Dyson 		}
1180d00947d8SCraig Rodrigues 	}
1181d00947d8SCraig Rodrigues 	VOP_CLOSE(uvp, FWRITE, cred, td);
118278022527SKonstantin Belousov 	VOP_ADD_WRITECOUNT_CHECKED(uvp, -1);
1183312d49efSJason A. Harmening 	CTR3(KTR_VFS, "%s: vp %p v_writecount decreased to %d",
1184312d49efSJason A. Harmening 	    __func__, uvp, uvp->v_writecount);
1185996c772fSJohn Dyson 
1186f2a2857bSKirk McKusick 	vn_finished_write(mp);
1187d00947d8SCraig Rodrigues 
1188996c772fSJohn Dyson 	if (error == 0) {
1189d00947d8SCraig Rodrigues 		/* Reset the attributes. Ignore errors. */
1190d00947d8SCraig Rodrigues 		uva.va_type = VNON;
11910359a12eSAttilio Rao 		VOP_SETATTR(uvp, &uva, cred);
1192996c772fSJohn Dyson 	}
1193996c772fSJohn Dyson 
1194d00947d8SCraig Rodrigues 	unionfs_node_update(unp, uvp, td);
1195996c772fSJohn Dyson 
11962a31267eSMatthew Dillon 	return (error);
1197b422956cSPoul-Henning Kamp }
1198996c772fSJohn Dyson 
11992a31267eSMatthew Dillon /*
1200d00947d8SCraig Rodrigues  * It checks whether vp can rmdir. (check empty)
12012a31267eSMatthew Dillon  *
1202d00947d8SCraig Rodrigues  * vp is unionfs vnode.
1203d00947d8SCraig Rodrigues  * vp should be locked.
1204df8bae1dSRodney W. Grimes  */
1205df8bae1dSRodney W. Grimes int
1206d00947d8SCraig Rodrigues unionfs_check_rmdir(struct vnode *vp, struct ucred *cred, struct thread *td)
1207df8bae1dSRodney W. Grimes {
1208d00947d8SCraig Rodrigues 	struct vnode   *uvp;
1209d00947d8SCraig Rodrigues 	struct vnode   *lvp;
1210d00947d8SCraig Rodrigues 	struct vnode   *tvp;
1211312d49efSJason A. Harmening 	struct dirent  *dp;
1212312d49efSJason A. Harmening 	struct dirent  *edp;
1213df8bae1dSRodney W. Grimes 	struct componentname cn;
1214312d49efSJason A. Harmening 	struct iovec	iov;
1215312d49efSJason A. Harmening 	struct uio	uio;
1216312d49efSJason A. Harmening 	struct vattr	va;
1217312d49efSJason A. Harmening 	int		error;
1218312d49efSJason A. Harmening 	int		eofflag;
1219312d49efSJason A. Harmening 	int		lookuperr;
1220312d49efSJason A. Harmening 
1221df8bae1dSRodney W. Grimes 	/*
1222d00947d8SCraig Rodrigues 	 * The size of buf needs to be larger than DIRBLKSIZ.
1223df8bae1dSRodney W. Grimes 	 */
1224d00947d8SCraig Rodrigues 	char		buf[256 * 6];
1225df8bae1dSRodney W. Grimes 
12263ecefc4aSJason A. Harmening 	ASSERT_VOP_ELOCKED(vp, __func__);
1227df8bae1dSRodney W. Grimes 
1228d00947d8SCraig Rodrigues 	eofflag = 0;
1229d00947d8SCraig Rodrigues 	uvp = UNIONFSVPTOUPPERVP(vp);
1230d00947d8SCraig Rodrigues 	lvp = UNIONFSVPTOLOWERVP(vp);
1231df8bae1dSRodney W. Grimes 
1232d00947d8SCraig Rodrigues 	/* check opaque */
12330359a12eSAttilio Rao 	if ((error = VOP_GETATTR(uvp, &va, cred)) != 0)
1234df8bae1dSRodney W. Grimes 		return (error);
1235d00947d8SCraig Rodrigues 	if (va.va_flags & OPAQUE)
1236d00947d8SCraig Rodrigues 		return (0);
1237df8bae1dSRodney W. Grimes 
1238d00947d8SCraig Rodrigues 	/* open vnode */
12393282e2c4SDaichi GOTO #ifdef MAC
124030d239bcSRobert Watson 	if ((error = mac_vnode_check_open(cred, vp, VEXEC|VREAD)) != 0)
12413282e2c4SDaichi GOTO 		return (error);
12423282e2c4SDaichi GOTO #endif
12433282e2c4SDaichi GOTO 	if ((error = VOP_ACCESS(vp, VEXEC|VREAD, cred, td)) != 0)
12443282e2c4SDaichi GOTO 		return (error);
12459e223287SKonstantin Belousov 	if ((error = VOP_OPEN(vp, FREAD, cred, td, NULL)) != 0)
1246996c772fSJohn Dyson 		return (error);
1247996c772fSJohn Dyson 
1248d00947d8SCraig Rodrigues 	uio.uio_rw = UIO_READ;
1249d00947d8SCraig Rodrigues 	uio.uio_segflg = UIO_SYSSPACE;
1250d00947d8SCraig Rodrigues 	uio.uio_td = td;
1251d00947d8SCraig Rodrigues 	uio.uio_offset = 0;
1252996c772fSJohn Dyson 
1253d00947d8SCraig Rodrigues #ifdef MAC
125430d239bcSRobert Watson 	error = mac_vnode_check_readdir(td->td_ucred, lvp);
1255d00947d8SCraig Rodrigues #endif
1256d00947d8SCraig Rodrigues 	while (!error && !eofflag) {
1257d00947d8SCraig Rodrigues 		iov.iov_base = buf;
1258d00947d8SCraig Rodrigues 		iov.iov_len = sizeof(buf);
1259d00947d8SCraig Rodrigues 		uio.uio_iov = &iov;
1260d00947d8SCraig Rodrigues 		uio.uio_iovcnt = 1;
1261d00947d8SCraig Rodrigues 		uio.uio_resid = iov.iov_len;
1262996c772fSJohn Dyson 
1263d00947d8SCraig Rodrigues 		error = VOP_READDIR(lvp, &uio, cred, &eofflag, NULL, NULL);
1264a68ae31cSDaichi GOTO 		if (error != 0)
1265d00947d8SCraig Rodrigues 			break;
1266fb273fe7SJason A. Harmening 		KASSERT(eofflag != 0 || uio.uio_resid < sizeof(buf),
1267fb273fe7SJason A. Harmening 		    ("%s: empty read from lower FS", __func__));
1268996c772fSJohn Dyson 
1269d00947d8SCraig Rodrigues 		edp = (struct dirent*)&buf[sizeof(buf) - uio.uio_resid];
1270d00947d8SCraig Rodrigues 		for (dp = (struct dirent*)buf; !error && dp < edp;
1271d00947d8SCraig Rodrigues 		     dp = (struct dirent*)((caddr_t)dp + dp->d_reclen)) {
1272ac13a90cSGleb Kurtsou 			if (dp->d_type == DT_WHT || dp->d_fileno == 0 ||
1273d00947d8SCraig Rodrigues 			    (dp->d_namlen == 1 && dp->d_name[0] == '.') ||
1274d00947d8SCraig Rodrigues 			    (dp->d_namlen == 2 && !bcmp(dp->d_name, "..", 2)))
1275d00947d8SCraig Rodrigues 				continue;
1276df8bae1dSRodney W. Grimes 
1277d00947d8SCraig Rodrigues 			cn.cn_namelen = dp->d_namlen;
1278d00947d8SCraig Rodrigues 			cn.cn_pnbuf = NULL;
1279d00947d8SCraig Rodrigues 			cn.cn_nameptr = dp->d_name;
1280d00947d8SCraig Rodrigues 			cn.cn_nameiop = LOOKUP;
1281312d49efSJason A. Harmening 			cn.cn_flags = LOCKPARENT | LOCKLEAF | SAVENAME |
1282312d49efSJason A. Harmening 			    RDONLY | ISLASTCN;
1283d00947d8SCraig Rodrigues 			cn.cn_lkflags = LK_EXCLUSIVE;
1284d00947d8SCraig Rodrigues 			cn.cn_cred = cred;
1285df8bae1dSRodney W. Grimes 
12862a31267eSMatthew Dillon 			/*
1287d00947d8SCraig Rodrigues 			 * check entry in lower.
1288d00947d8SCraig Rodrigues 			 * Sometimes, readdir function returns
1289d00947d8SCraig Rodrigues 			 * wrong entry.
12902a31267eSMatthew Dillon 			 */
1291d00947d8SCraig Rodrigues 			lookuperr = VOP_LOOKUP(lvp, &tvp, &cn);
1292df8bae1dSRodney W. Grimes 
1293d00947d8SCraig Rodrigues 			if (!lookuperr)
1294d00947d8SCraig Rodrigues 				vput(tvp);
12952a31267eSMatthew Dillon 			else
1296d00947d8SCraig Rodrigues 				continue; /* skip entry */
1297df8bae1dSRodney W. Grimes 
1298df8bae1dSRodney W. Grimes 			/*
1299d00947d8SCraig Rodrigues 			 * check entry
1300d00947d8SCraig Rodrigues 			 * If it has no exist/whiteout entry in upper,
1301d00947d8SCraig Rodrigues 			 * directory is not empty.
1302df8bae1dSRodney W. Grimes 			 */
1303312d49efSJason A. Harmening 			cn.cn_flags = LOCKPARENT | LOCKLEAF | SAVENAME |
1304312d49efSJason A. Harmening 			    RDONLY | ISLASTCN;
1305d00947d8SCraig Rodrigues 			lookuperr = VOP_LOOKUP(uvp, &tvp, &cn);
1306df8bae1dSRodney W. Grimes 
1307d00947d8SCraig Rodrigues 			if (!lookuperr)
1308d00947d8SCraig Rodrigues 				vput(tvp);
1309df8bae1dSRodney W. Grimes 
1310d00947d8SCraig Rodrigues 			/* ignore exist or whiteout entry */
1311d00947d8SCraig Rodrigues 			if (!lookuperr ||
1312d00947d8SCraig Rodrigues 			    (lookuperr == ENOENT && (cn.cn_flags & ISWHITEOUT)))
131301634480SBrian Feldman 				continue;
1314d00947d8SCraig Rodrigues 
1315d00947d8SCraig Rodrigues 			error = ENOTEMPTY;
1316df8bae1dSRodney W. Grimes 		}
1317996c772fSJohn Dyson 	}
1318996c772fSJohn Dyson 
1319d00947d8SCraig Rodrigues 	/* close vnode */
1320d00947d8SCraig Rodrigues 	VOP_CLOSE(vp, FREAD, cred, td);
1321d00947d8SCraig Rodrigues 
1322d00947d8SCraig Rodrigues 	return (error);
1323d00947d8SCraig Rodrigues }
1324d00947d8SCraig Rodrigues 
1325