xref: /freebsd/sys/fs/unionfs/union_subr.c (revision 8f7859e800d998e5518b4f0d6c9545106dc96f68)
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
2593150cf0cSMateusz 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 
3371ede983cSDag-Erling Smørgrav 	unp = malloc(sizeof(struct unionfs_node),
338d00947d8SCraig Rodrigues 	    M_UNIONFSNODE, M_WAITOK | M_ZERO);
339d00947d8SCraig Rodrigues 
340d00947d8SCraig Rodrigues 	error = getnewvnode("unionfs", mp, &unionfs_vnodeops, &vp);
341dc2dd185SDaichi GOTO 	if (error != 0) {
3421ede983cSDag-Erling Smørgrav 		free(unp, M_UNIONFSNODE);
343d00947d8SCraig Rodrigues 		return (error);
344d00947d8SCraig Rodrigues 	}
345d00947d8SCraig Rodrigues 	if (dvp != NULLVP)
346d00947d8SCraig Rodrigues 		vref(dvp);
347d00947d8SCraig Rodrigues 	if (uppervp != NULLVP)
348d00947d8SCraig Rodrigues 		vref(uppervp);
349d00947d8SCraig Rodrigues 	if (lowervp != NULLVP)
350d00947d8SCraig Rodrigues 		vref(lowervp);
351d00947d8SCraig Rodrigues 
352fd8ad212SJason A. Harmening 	if (vt == VDIR) {
353a9b794ffSDaichi GOTO 		unp->un_hashtbl = hashinit(NUNIONFSNODECACHE, M_UNIONFSHASH,
354fd8ad212SJason A. Harmening 		    &hashmask);
355fd8ad212SJason A. Harmening 		KASSERT(hashmask == UNIONFSHASHMASK,
356fd8ad212SJason A. Harmening 		    ("unexpected unionfs hash mask 0x%lx", hashmask));
357fd8ad212SJason A. Harmening 	}
358a9b794ffSDaichi GOTO 
359d00947d8SCraig Rodrigues 	unp->un_vnode = vp;
360d00947d8SCraig Rodrigues 	unp->un_uppervp = uppervp;
361d00947d8SCraig Rodrigues 	unp->un_lowervp = lowervp;
362d00947d8SCraig Rodrigues 	unp->un_dvp = dvp;
363d00947d8SCraig Rodrigues 	if (uppervp != NULLVP)
364d00947d8SCraig Rodrigues 		vp->v_vnlock = uppervp->v_vnlock;
365d00947d8SCraig Rodrigues 	else
366d00947d8SCraig Rodrigues 		vp->v_vnlock = lowervp->v_vnlock;
367d00947d8SCraig Rodrigues 
368dc2dd185SDaichi GOTO 	if (path != NULL) {
369312d49efSJason A. Harmening 		unp->un_path = malloc(cnp->cn_namelen + 1,
370312d49efSJason A. Harmening 		    M_UNIONFSPATH, M_WAITOK | M_ZERO);
371d00947d8SCraig Rodrigues 		bcopy(cnp->cn_nameptr, unp->un_path, cnp->cn_namelen);
372d00947d8SCraig Rodrigues 		unp->un_path[cnp->cn_namelen] = '\0';
373abe95116SJason A. Harmening 		unp->un_pathlen = cnp->cn_namelen;
374d00947d8SCraig Rodrigues 	}
375a9b794ffSDaichi GOTO 	vp->v_type = vt;
376d00947d8SCraig Rodrigues 	vp->v_data = unp;
377d00947d8SCraig Rodrigues 
378a01ca46bSJason A. Harmening 	/*
379a01ca46bSJason A. Harmening 	 * TODO: This is an imperfect check, as there's no guarantee that
380a01ca46bSJason A. Harmening 	 * the underlying filesystems will always return vnode pointers
381a01ca46bSJason A. Harmening 	 * for the root inodes that match our cached values.  To reduce
382a01ca46bSJason A. Harmening 	 * the likelihood of failure, for example in the case where either
383a01ca46bSJason A. Harmening 	 * vnode has been forcibly doomed, we check both pointers and set
384a01ca46bSJason A. Harmening 	 * VV_ROOT if either matches.
385a01ca46bSJason A. Harmening 	 */
386a01ca46bSJason A. Harmening 	if (ump->um_uppervp == uppervp || ump->um_lowervp == lowervp)
387d00947d8SCraig Rodrigues 		vp->v_vflag |= VV_ROOT;
388a01ca46bSJason A. Harmening 	KASSERT(dvp != NULL || (vp->v_vflag & VV_ROOT) != 0,
389a01ca46bSJason A. Harmening 	    ("%s: NULL dvp for non-root vp %p", __func__, vp));
390d00947d8SCraig Rodrigues 
391f9e28f90SJason A. Harmening 	vn_lock_pair(lowervp, false, uppervp, false);
39266c5fbcaSKonstantin Belousov 	error = insmntque1(vp, mp);
3933150cf0cSMateusz Guzik 	if (error != 0) {
3943150cf0cSMateusz Guzik 		unionfs_nodeget_cleanup(vp, unp);
395f9e28f90SJason A. Harmening 		return (error);
3963150cf0cSMateusz Guzik 	}
397f9e28f90SJason A. Harmening 	if (lowervp != NULL && VN_IS_DOOMED(lowervp)) {
398f9e28f90SJason A. Harmening 		vput(lowervp);
399f9e28f90SJason A. Harmening 		unp->un_lowervp = NULL;
400f9e28f90SJason A. Harmening 	}
401f9e28f90SJason A. Harmening 	if (uppervp != NULL && VN_IS_DOOMED(uppervp)) {
402f9e28f90SJason A. Harmening 		vput(uppervp);
403f9e28f90SJason A. Harmening 		unp->un_uppervp = NULL;
404f9e28f90SJason A. Harmening 	}
405f9e28f90SJason A. Harmening 	if (unp->un_lowervp == NULL && unp->un_uppervp == NULL) {
406f9e28f90SJason A. Harmening 		unionfs_nodeget_cleanup(vp, unp);
407f9e28f90SJason A. Harmening 		return (ENOENT);
408f9e28f90SJason A. Harmening 	}
409fd8ad212SJason A. Harmening 
410fd8ad212SJason A. Harmening 	if (dvp != NULLVP && vt == VDIR)
411fd8ad212SJason A. Harmening 		*vpp = unionfs_ins_cached_vnode(unp, dvp);
412f9e28f90SJason A. Harmening 	if (*vpp != NULLVP) {
413f9e28f90SJason A. Harmening 		unionfs_nodeget_cleanup(vp, unp);
414a9b794ffSDaichi GOTO 		vp = *vpp;
415f9e28f90SJason A. Harmening 	} else {
416f9e28f90SJason A. Harmening 		if (uppervp != NULL)
417f9e28f90SJason A. Harmening 			VOP_UNLOCK(uppervp);
418f9e28f90SJason A. Harmening 		if (lowervp != NULL)
419f9e28f90SJason A. Harmening 			VOP_UNLOCK(lowervp);
420a9b794ffSDaichi GOTO 		*vpp = vp;
421f9e28f90SJason A. Harmening 	}
422a9b794ffSDaichi GOTO 
423a9b794ffSDaichi GOTO unionfs_nodeget_out:
424d00947d8SCraig Rodrigues 	if (lkflags & LK_TYPE_MASK)
425cb05b60aSAttilio Rao 		vn_lock(vp, lkflags | LK_RETRY);
426df8bae1dSRodney W. Grimes 
427d00947d8SCraig Rodrigues 	return (0);
428996c772fSJohn Dyson }
429df8bae1dSRodney W. Grimes 
4302a31267eSMatthew Dillon /*
431dc2dd185SDaichi GOTO  * Clean up the unionfs node.
4322a31267eSMatthew Dillon  */
433d00947d8SCraig Rodrigues void
4346d8420d4SJason A. Harmening unionfs_noderem(struct vnode *vp)
435d00947d8SCraig Rodrigues {
4363af387c9SDaichi GOTO 	struct unionfs_node *unp, *unp_t1, *unp_t2;
4373af387c9SDaichi GOTO 	struct unionfs_node_hashhead *hd;
438acc4bab1SCraig Rodrigues 	struct unionfs_node_status *unsp, *unsp_tmp;
439d00947d8SCraig Rodrigues 	struct vnode   *lvp;
440d00947d8SCraig Rodrigues 	struct vnode   *uvp;
441a9b794ffSDaichi GOTO 	struct vnode   *dvp;
442312d49efSJason A. Harmening 	int		count;
443d877dd57SJason A. Harmening 	int		writerefs;
4442a31267eSMatthew Dillon 
4456ff167aaSJason A. Harmening 	/*
446974efbb3SJason A. Harmening 	 * The root vnode lock may be recursed during unmount, because
4476ff167aaSJason A. Harmening 	 * it may share the same lock as the unionfs mount's covered vnode,
4486ff167aaSJason A. Harmening 	 * which is locked across VFS_UNMOUNT().  This lock will then be
4496ff167aaSJason A. Harmening 	 * recursively taken during the vflush() issued by unionfs_unmount().
4506ff167aaSJason A. Harmening 	 * But we still only need to lock the unionfs lock once, because only
4516ff167aaSJason A. Harmening 	 * one of those lock operations was taken against a unionfs vnode and
4526ff167aaSJason A. Harmening 	 * will be undone against a unionfs vnode.
4536ff167aaSJason A. Harmening 	 */
4546ff167aaSJason A. Harmening 	KASSERT(vp->v_vnlock->lk_recurse == 0 || (vp->v_vflag & VV_ROOT) != 0,
45566191a76SJason A. Harmening 	    ("%s: vnode %p locked recursively", __func__, vp));
45666191a76SJason A. Harmening 	if (lockmgr(&vp->v_lock, LK_EXCLUSIVE | LK_NOWAIT, NULL) != 0)
4573ecefc4aSJason A. Harmening 		panic("%s: failed to acquire lock for vnode lock", __func__);
458866dd633SJason A. Harmening 
4592a31267eSMatthew Dillon 	/*
460d00947d8SCraig Rodrigues 	 * Use the interlock to protect the clearing of v_data to
461d00947d8SCraig Rodrigues 	 * prevent faults in unionfs_lock().
4622a31267eSMatthew Dillon 	 */
463d00947d8SCraig Rodrigues 	VI_LOCK(vp);
464d00947d8SCraig Rodrigues 	unp = VTOUNIONFS(vp);
465d00947d8SCraig Rodrigues 	lvp = unp->un_lowervp;
466d00947d8SCraig Rodrigues 	uvp = unp->un_uppervp;
467a9b794ffSDaichi GOTO 	dvp = unp->un_dvp;
468d00947d8SCraig Rodrigues 	unp->un_lowervp = unp->un_uppervp = NULLVP;
469d00947d8SCraig Rodrigues 	vp->v_vnlock = &(vp->v_lock);
470d00947d8SCraig Rodrigues 	vp->v_data = NULL;
471d00947d8SCraig Rodrigues 	vp->v_object = NULL;
472866dd633SJason A. Harmening 	if (unp->un_hashtbl != NULL) {
473866dd633SJason A. Harmening 		/*
474866dd633SJason A. Harmening 		 * Clear out any cached child vnodes.  This should only
475866dd633SJason A. Harmening 		 * be necessary during forced unmount, when the vnode may
476866dd633SJason A. Harmening 		 * be reclaimed with a non-zero use count.  Otherwise the
477866dd633SJason A. Harmening 		 * reference held by each child should prevent reclamation.
478866dd633SJason A. Harmening 		 */
479866dd633SJason A. Harmening 		for (count = 0; count <= UNIONFSHASHMASK; count++) {
480866dd633SJason A. Harmening 			hd = unp->un_hashtbl + count;
481866dd633SJason A. Harmening 			LIST_FOREACH_SAFE(unp_t1, hd, un_hash, unp_t2) {
482866dd633SJason A. Harmening 				LIST_REMOVE(unp_t1, un_hash);
483866dd633SJason A. Harmening 				unp_t1->un_hash.le_next = NULL;
484866dd633SJason A. Harmening 				unp_t1->un_hash.le_prev = NULL;
485866dd633SJason A. Harmening 			}
486866dd633SJason A. Harmening 		}
487866dd633SJason A. Harmening 	}
488cb5736b7SDaichi GOTO 	VI_UNLOCK(vp);
489cb5736b7SDaichi GOTO 
490d877dd57SJason A. Harmening 	writerefs = atomic_load_int(&vp->v_writecount);
491d877dd57SJason A. Harmening 	VNASSERT(writerefs >= 0, vp,
492d877dd57SJason A. Harmening 	    ("%s: write count %d, unexpected text ref", __func__, writerefs));
493d877dd57SJason A. Harmening 	/*
494d877dd57SJason A. Harmening 	 * If we were opened for write, we leased the write reference
495d877dd57SJason A. Harmening 	 * to the lower vnode.  If this is a reclamation due to the
496d877dd57SJason A. Harmening 	 * forced unmount, undo the reference now.
497d877dd57SJason A. Harmening 	 */
498d877dd57SJason A. Harmening 	if (writerefs > 0) {
499d877dd57SJason A. Harmening 		VNASSERT(uvp != NULL, vp,
500d877dd57SJason A. Harmening 		    ("%s: write reference without upper vnode", __func__));
501d877dd57SJason A. Harmening 		VOP_ADD_WRITECOUNT(uvp, -writerefs);
502d877dd57SJason A. Harmening 	}
503cb5736b7SDaichi GOTO 	if (lvp != NULLVP)
504b249ce48SMateusz Guzik 		VOP_UNLOCK(lvp);
505cb5736b7SDaichi GOTO 	if (uvp != NULLVP)
506b249ce48SMateusz Guzik 		VOP_UNLOCK(uvp);
507d00947d8SCraig Rodrigues 
508fd8ad212SJason A. Harmening 	if (dvp != NULLVP)
5093af387c9SDaichi GOTO 		unionfs_rem_cached_vnode(unp, dvp);
510a9b794ffSDaichi GOTO 
5115050aa86SKonstantin Belousov 	if (lvp != NULLVP)
512d00947d8SCraig Rodrigues 		vrele(lvp);
5135050aa86SKonstantin Belousov 	if (uvp != NULLVP)
514d00947d8SCraig Rodrigues 		vrele(uvp);
515a9b794ffSDaichi GOTO 	if (unp->un_path != NULL) {
516d00947d8SCraig Rodrigues 		free(unp->un_path, M_UNIONFSPATH);
517d00947d8SCraig Rodrigues 		unp->un_path = NULL;
518abe95116SJason A. Harmening 		unp->un_pathlen = 0;
519d00947d8SCraig Rodrigues 	}
520acc4bab1SCraig Rodrigues 
5213af387c9SDaichi GOTO 	if (unp->un_hashtbl != NULL) {
522fd8ad212SJason A. Harmening 		hashdestroy(unp->un_hashtbl, M_UNIONFSHASH, UNIONFSHASHMASK);
5233af387c9SDaichi GOTO 	}
524a9b794ffSDaichi GOTO 
525acc4bab1SCraig Rodrigues 	LIST_FOREACH_SAFE(unsp, &(unp->un_unshead), uns_list, unsp_tmp) {
526d00947d8SCraig Rodrigues 		LIST_REMOVE(unsp, uns_list);
527d00947d8SCraig Rodrigues 		free(unsp, M_TEMP);
528d00947d8SCraig Rodrigues 	}
529372691a7SJason A. Harmening 	if (dvp != NULLVP) {
530372691a7SJason A. Harmening 		mtx_lock(&unionfs_deferred_rele_lock);
531372691a7SJason A. Harmening 		STAILQ_INSERT_TAIL(&unionfs_deferred_rele_list, unp, un_rele);
532372691a7SJason A. Harmening 		mtx_unlock(&unionfs_deferred_rele_lock);
533372691a7SJason A. Harmening 		taskqueue_enqueue(taskqueue_unionfs_rele,
534372691a7SJason A. Harmening 		    &unionfs_deferred_rele_task);
535372691a7SJason A. Harmening 	} else
5361ede983cSDag-Erling Smørgrav 		free(unp, M_UNIONFSNODE);
537df8bae1dSRodney W. Grimes }
538df8bae1dSRodney W. Grimes 
539d00947d8SCraig Rodrigues /*
54039a2dc44SJason A. Harmening  * Get the unionfs node status object for the vnode corresponding to unp,
54139a2dc44SJason A. Harmening  * for the process that owns td.  Allocate a new status object if one
54239a2dc44SJason A. Harmening  * does not already exist.
543d00947d8SCraig Rodrigues  */
544d00947d8SCraig Rodrigues void
545d00947d8SCraig Rodrigues unionfs_get_node_status(struct unionfs_node *unp, struct thread *td,
546d00947d8SCraig Rodrigues     struct unionfs_node_status **unspp)
547d00947d8SCraig Rodrigues {
548d00947d8SCraig Rodrigues 	struct unionfs_node_status *unsp;
549312d49efSJason A. Harmening 	pid_t pid;
550312d49efSJason A. Harmening 
551312d49efSJason A. Harmening 	pid = td->td_proc->p_pid;
552df8bae1dSRodney W. Grimes 
5533ecefc4aSJason A. Harmening 	KASSERT(NULL != unspp, ("%s: NULL status", __func__));
5543ecefc4aSJason A. Harmening 	ASSERT_VOP_ELOCKED(UNIONFSTOV(unp), __func__);
5552a31267eSMatthew Dillon 
556d00947d8SCraig Rodrigues 	LIST_FOREACH(unsp, &(unp->un_unshead), uns_list) {
557fe5f08cdSDaichi GOTO 		if (unsp->uns_pid == pid) {
558d00947d8SCraig Rodrigues 			*unspp = unsp;
559d00947d8SCraig Rodrigues 			return;
560d00947d8SCraig Rodrigues 		}
561d00947d8SCraig Rodrigues 	}
5622a31267eSMatthew Dillon 
563d00947d8SCraig Rodrigues 	/* create a new unionfs node status */
564e11e3f18SDag-Erling Smørgrav 	unsp = malloc(sizeof(struct unionfs_node_status),
565e11e3f18SDag-Erling Smørgrav 	    M_TEMP, M_WAITOK | M_ZERO);
5662a31267eSMatthew Dillon 
567fe5f08cdSDaichi GOTO 	unsp->uns_pid = pid;
568d00947d8SCraig Rodrigues 	LIST_INSERT_HEAD(&(unp->un_unshead), unsp, uns_list);
5692a31267eSMatthew Dillon 
570d00947d8SCraig Rodrigues 	*unspp = unsp;
571d00947d8SCraig Rodrigues }
572d00947d8SCraig Rodrigues 
573d00947d8SCraig Rodrigues /*
574d00947d8SCraig Rodrigues  * Remove the unionfs node status, if you can.
575d00947d8SCraig Rodrigues  * You need exclusive lock this vnode.
576d00947d8SCraig Rodrigues  */
577d00947d8SCraig Rodrigues void
578fe5f08cdSDaichi GOTO unionfs_tryrem_node_status(struct unionfs_node *unp,
579d00947d8SCraig Rodrigues     struct unionfs_node_status *unsp)
580d00947d8SCraig Rodrigues {
5813ecefc4aSJason A. Harmening 	KASSERT(NULL != unsp, ("%s: NULL status", __func__));
5823ecefc4aSJason A. Harmening 	ASSERT_VOP_ELOCKED(UNIONFSTOV(unp), __func__);
583d00947d8SCraig Rodrigues 
584d00947d8SCraig Rodrigues 	if (0 < unsp->uns_lower_opencnt || 0 < unsp->uns_upper_opencnt)
585d00947d8SCraig Rodrigues 		return;
586d00947d8SCraig Rodrigues 
587d00947d8SCraig Rodrigues 	LIST_REMOVE(unsp, uns_list);
588d00947d8SCraig Rodrigues 	free(unsp, M_TEMP);
589d00947d8SCraig Rodrigues }
590d00947d8SCraig Rodrigues 
591d00947d8SCraig Rodrigues /*
592d00947d8SCraig Rodrigues  * Create upper node attr.
593d00947d8SCraig Rodrigues  */
594d00947d8SCraig Rodrigues void
595312d49efSJason A. Harmening unionfs_create_uppervattr_core(struct unionfs_mount *ump, struct vattr *lva,
596312d49efSJason A. Harmening     struct vattr *uva, struct thread *td)
597d00947d8SCraig Rodrigues {
598d00947d8SCraig Rodrigues 	VATTR_NULL(uva);
599d00947d8SCraig Rodrigues 	uva->va_type = lva->va_type;
600d00947d8SCraig Rodrigues 	uva->va_atime = lva->va_atime;
601d00947d8SCraig Rodrigues 	uva->va_mtime = lva->va_mtime;
602d00947d8SCraig Rodrigues 	uva->va_ctime = lva->va_ctime;
603d00947d8SCraig Rodrigues 
604d00947d8SCraig Rodrigues 	switch (ump->um_copymode) {
605d00947d8SCraig Rodrigues 	case UNIONFS_TRANSPARENT:
606d00947d8SCraig Rodrigues 		uva->va_mode = lva->va_mode;
607d00947d8SCraig Rodrigues 		uva->va_uid = lva->va_uid;
608d00947d8SCraig Rodrigues 		uva->va_gid = lva->va_gid;
609d00947d8SCraig Rodrigues 		break;
610d00947d8SCraig Rodrigues 	case UNIONFS_MASQUERADE:
611d00947d8SCraig Rodrigues 		if (ump->um_uid == lva->va_uid) {
612d00947d8SCraig Rodrigues 			uva->va_mode = lva->va_mode & 077077;
613312d49efSJason A. Harmening 			uva->va_mode |= (lva->va_type == VDIR ?
614312d49efSJason A. Harmening 			    ump->um_udir : ump->um_ufile) & 0700;
615d00947d8SCraig Rodrigues 			uva->va_uid = lva->va_uid;
616d00947d8SCraig Rodrigues 			uva->va_gid = lva->va_gid;
617df8bae1dSRodney W. Grimes 		} else {
618312d49efSJason A. Harmening 			uva->va_mode = (lva->va_type == VDIR ?
619312d49efSJason A. Harmening 			    ump->um_udir : ump->um_ufile);
620d00947d8SCraig Rodrigues 			uva->va_uid = ump->um_uid;
621d00947d8SCraig Rodrigues 			uva->va_gid = ump->um_gid;
622d00947d8SCraig Rodrigues 		}
623d00947d8SCraig Rodrigues 		break;
624d00947d8SCraig Rodrigues 	default:		/* UNIONFS_TRADITIONAL */
62585078b85SConrad Meyer 		uva->va_mode = 0777 & ~td->td_proc->p_pd->pd_cmask;
626d00947d8SCraig Rodrigues 		uva->va_uid = ump->um_uid;
627d00947d8SCraig Rodrigues 		uva->va_gid = ump->um_gid;
628d00947d8SCraig Rodrigues 		break;
629d00947d8SCraig Rodrigues 	}
630df8bae1dSRodney W. Grimes }
631df8bae1dSRodney W. Grimes 
632d00947d8SCraig Rodrigues /*
633d00947d8SCraig Rodrigues  * Create upper node attr.
634d00947d8SCraig Rodrigues  */
635d00947d8SCraig Rodrigues int
636312d49efSJason A. Harmening unionfs_create_uppervattr(struct unionfs_mount *ump, struct vnode *lvp,
637312d49efSJason A. Harmening     struct vattr *uva, struct ucred *cred, struct thread *td)
638d00947d8SCraig Rodrigues {
639d00947d8SCraig Rodrigues 	struct vattr	lva;
640312d49efSJason A. Harmening 	int		error;
641df8bae1dSRodney W. Grimes 
6420359a12eSAttilio Rao 	if ((error = VOP_GETATTR(lvp, &lva, cred)))
643d00947d8SCraig Rodrigues 		return (error);
644d00947d8SCraig Rodrigues 
645d00947d8SCraig Rodrigues 	unionfs_create_uppervattr_core(ump, &lva, uva, td);
646df8bae1dSRodney W. Grimes 
647df8bae1dSRodney W. Grimes 	return (error);
648df8bae1dSRodney W. Grimes }
649df8bae1dSRodney W. Grimes 
650d00947d8SCraig Rodrigues /*
651d00947d8SCraig Rodrigues  * relookup
652d00947d8SCraig Rodrigues  *
653d00947d8SCraig Rodrigues  * dvp should be locked on entry and will be locked on return.
654d00947d8SCraig Rodrigues  *
655d00947d8SCraig Rodrigues  * If an error is returned, *vpp will be invalid, otherwise it will hold a
656d00947d8SCraig Rodrigues  * locked, referenced vnode. If *vpp == dvp then remember that only one
657d00947d8SCraig Rodrigues  * LK_EXCLUSIVE lock is held.
658d00947d8SCraig Rodrigues  */
6591e5da15aSDaichi GOTO int
660d00947d8SCraig Rodrigues unionfs_relookup(struct vnode *dvp, struct vnode **vpp,
661312d49efSJason A. Harmening     struct componentname *cnp, struct componentname *cn, struct thread *td,
662312d49efSJason A. Harmening     char *path, int pathlen, u_long nameiop)
663df8bae1dSRodney W. Grimes {
664d00947d8SCraig Rodrigues 	int error;
6658f874e92SMateusz Guzik 	bool refstart;
666df8bae1dSRodney W. Grimes 
667d00947d8SCraig Rodrigues 	cn->cn_namelen = pathlen;
668abe95116SJason A. Harmening 	cn->cn_pnbuf = path;
669d00947d8SCraig Rodrigues 	cn->cn_nameiop = nameiop;
6705b5b7e2cSMateusz Guzik 	cn->cn_flags = (LOCKPARENT | LOCKLEAF | ISLASTCN);
671d00947d8SCraig Rodrigues 	cn->cn_lkflags = LK_EXCLUSIVE;
672d00947d8SCraig Rodrigues 	cn->cn_cred = cnp->cn_cred;
673d00947d8SCraig Rodrigues 	cn->cn_nameptr = cn->cn_pnbuf;
674df8bae1dSRodney W. Grimes 
6758f874e92SMateusz Guzik 	refstart = false;
6768f874e92SMateusz Guzik 	if (nameiop == DELETE) {
6778f874e92SMateusz Guzik 		cn->cn_flags |= (cnp->cn_flags & DOWHITEOUT);
678*8f7859e8SMateusz Guzik 	} else if (nameiop == RENAME) {
679*8f7859e8SMateusz Guzik 		refstart = true;
6808f874e92SMateusz Guzik 	} else if (nameiop == CREATE) {
6816c21f6edSKonstantin Belousov 		cn->cn_flags |= NOCACHE;
6828f874e92SMateusz Guzik 	}
683d00947d8SCraig Rodrigues 
684d00947d8SCraig Rodrigues 	vref(dvp);
685b249ce48SMateusz Guzik 	VOP_UNLOCK(dvp);
686d00947d8SCraig Rodrigues 
6878f874e92SMateusz Guzik 	if ((error = vfs_relookup(dvp, vpp, cn, refstart))) {
688cb05b60aSAttilio Rao 		vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
689d00947d8SCraig Rodrigues 	} else
690d00947d8SCraig Rodrigues 		vrele(dvp);
691d00947d8SCraig Rodrigues 
692abe95116SJason A. Harmening 	KASSERT(cn->cn_pnbuf == path, ("%s: cn_pnbuf changed", __func__));
693abe95116SJason A. Harmening 
694d00947d8SCraig Rodrigues 	return (error);
695df8bae1dSRodney W. Grimes }
696df8bae1dSRodney W. Grimes 
697df8bae1dSRodney W. Grimes /*
698d00947d8SCraig Rodrigues  * relookup for CREATE namei operation.
6992a31267eSMatthew Dillon  *
700d00947d8SCraig Rodrigues  * dvp is unionfs vnode. dvp should be locked.
701d00947d8SCraig Rodrigues  *
702d00947d8SCraig Rodrigues  * If it called 'unionfs_copyfile' function by unionfs_link etc,
703d00947d8SCraig Rodrigues  * VOP_LOOKUP information is broken.
704d00947d8SCraig Rodrigues  * So it need relookup in order to create link etc.
705d00947d8SCraig Rodrigues  */
706d00947d8SCraig Rodrigues int
707d00947d8SCraig Rodrigues unionfs_relookup_for_create(struct vnode *dvp, struct componentname *cnp,
708d00947d8SCraig Rodrigues     struct thread *td)
709d00947d8SCraig Rodrigues {
710d00947d8SCraig Rodrigues 	struct vnode *udvp;
711d00947d8SCraig Rodrigues 	struct vnode *vp;
712d00947d8SCraig Rodrigues 	struct componentname cn;
713312d49efSJason A. Harmening 	int error;
714d00947d8SCraig Rodrigues 
715d00947d8SCraig Rodrigues 	udvp = UNIONFSVPTOUPPERVP(dvp);
716d00947d8SCraig Rodrigues 	vp = NULLVP;
717d00947d8SCraig Rodrigues 
718d00947d8SCraig Rodrigues 	error = unionfs_relookup(udvp, &vp, cnp, &cn, td, cnp->cn_nameptr,
719abe95116SJason A. Harmening 	    cnp->cn_namelen, CREATE);
720d00947d8SCraig Rodrigues 	if (error)
721d00947d8SCraig Rodrigues 		return (error);
722d00947d8SCraig Rodrigues 
723d00947d8SCraig Rodrigues 	if (vp != NULLVP) {
724d00947d8SCraig Rodrigues 		if (udvp == vp)
725d00947d8SCraig Rodrigues 			vrele(vp);
726d00947d8SCraig Rodrigues 		else
727d00947d8SCraig Rodrigues 			vput(vp);
728d00947d8SCraig Rodrigues 
729d00947d8SCraig Rodrigues 		error = EEXIST;
730d00947d8SCraig Rodrigues 	}
731d00947d8SCraig Rodrigues 
732d00947d8SCraig Rodrigues 	return (error);
733d00947d8SCraig Rodrigues }
734d00947d8SCraig Rodrigues 
735d00947d8SCraig Rodrigues /*
736d00947d8SCraig Rodrigues  * relookup for DELETE namei operation.
737d00947d8SCraig Rodrigues  *
738d00947d8SCraig Rodrigues  * dvp is unionfs vnode. dvp should be locked.
739d00947d8SCraig Rodrigues  */
740d00947d8SCraig Rodrigues int
741d00947d8SCraig Rodrigues unionfs_relookup_for_delete(struct vnode *dvp, struct componentname *cnp,
742d00947d8SCraig Rodrigues     struct thread *td)
743d00947d8SCraig Rodrigues {
744d00947d8SCraig Rodrigues 	struct vnode *udvp;
745d00947d8SCraig Rodrigues 	struct vnode *vp;
746d00947d8SCraig Rodrigues 	struct componentname cn;
747312d49efSJason A. Harmening 	int error;
748d00947d8SCraig Rodrigues 
749d00947d8SCraig Rodrigues 	udvp = UNIONFSVPTOUPPERVP(dvp);
750d00947d8SCraig Rodrigues 	vp = NULLVP;
751d00947d8SCraig Rodrigues 
752d00947d8SCraig Rodrigues 	error = unionfs_relookup(udvp, &vp, cnp, &cn, td, cnp->cn_nameptr,
753abe95116SJason A. Harmening 	    cnp->cn_namelen, DELETE);
754d00947d8SCraig Rodrigues 	if (error)
755d00947d8SCraig Rodrigues 		return (error);
756d00947d8SCraig Rodrigues 
757d00947d8SCraig Rodrigues 	if (vp == NULLVP)
758d00947d8SCraig Rodrigues 		error = ENOENT;
759d00947d8SCraig Rodrigues 	else {
760d00947d8SCraig Rodrigues 		if (udvp == vp)
761d00947d8SCraig Rodrigues 			vrele(vp);
762d00947d8SCraig Rodrigues 		else
763d00947d8SCraig Rodrigues 			vput(vp);
764d00947d8SCraig Rodrigues 	}
765d00947d8SCraig Rodrigues 
766d00947d8SCraig Rodrigues 	return (error);
767d00947d8SCraig Rodrigues }
768d00947d8SCraig Rodrigues 
769d00947d8SCraig Rodrigues /*
770d00947d8SCraig Rodrigues  * relookup for RENAME namei operation.
771d00947d8SCraig Rodrigues  *
772d00947d8SCraig Rodrigues  * dvp is unionfs vnode. dvp should be locked.
773d00947d8SCraig Rodrigues  */
774d00947d8SCraig Rodrigues int
775d00947d8SCraig Rodrigues unionfs_relookup_for_rename(struct vnode *dvp, struct componentname *cnp,
776d00947d8SCraig Rodrigues     struct thread *td)
777d00947d8SCraig Rodrigues {
778d00947d8SCraig Rodrigues 	struct vnode *udvp;
779d00947d8SCraig Rodrigues 	struct vnode *vp;
780d00947d8SCraig Rodrigues 	struct componentname cn;
781312d49efSJason A. Harmening 	int error;
782d00947d8SCraig Rodrigues 
783d00947d8SCraig Rodrigues 	udvp = UNIONFSVPTOUPPERVP(dvp);
784d00947d8SCraig Rodrigues 	vp = NULLVP;
785d00947d8SCraig Rodrigues 
786d00947d8SCraig Rodrigues 	error = unionfs_relookup(udvp, &vp, cnp, &cn, td, cnp->cn_nameptr,
787abe95116SJason A. Harmening 	    cnp->cn_namelen, RENAME);
788d00947d8SCraig Rodrigues 	if (error)
789d00947d8SCraig Rodrigues 		return (error);
790d00947d8SCraig Rodrigues 
791d00947d8SCraig Rodrigues 	if (vp != NULLVP) {
792d00947d8SCraig Rodrigues 		if (udvp == vp)
793d00947d8SCraig Rodrigues 			vrele(vp);
794d00947d8SCraig Rodrigues 		else
795d00947d8SCraig Rodrigues 			vput(vp);
796d00947d8SCraig Rodrigues 	}
797d00947d8SCraig Rodrigues 
798d00947d8SCraig Rodrigues 	return (error);
799d00947d8SCraig Rodrigues }
800d00947d8SCraig Rodrigues 
801d00947d8SCraig Rodrigues /*
802d00947d8SCraig Rodrigues  * Update the unionfs_node.
803d00947d8SCraig Rodrigues  *
804d00947d8SCraig Rodrigues  * uvp is new locked upper vnode. unionfs vnode's lock will be exchanged to the
805d00947d8SCraig Rodrigues  * uvp's lock and lower's lock will be unlocked.
806d00947d8SCraig Rodrigues  */
807d00947d8SCraig Rodrigues static void
808d00947d8SCraig Rodrigues unionfs_node_update(struct unionfs_node *unp, struct vnode *uvp,
809d00947d8SCraig Rodrigues     struct thread *td)
810d00947d8SCraig Rodrigues {
811fd8ad212SJason A. Harmening 	struct unionfs_node_hashhead *hd;
812d00947d8SCraig Rodrigues 	struct vnode   *vp;
813d00947d8SCraig Rodrigues 	struct vnode   *lvp;
814a9b794ffSDaichi GOTO 	struct vnode   *dvp;
815312d49efSJason A. Harmening 	unsigned	count, lockrec;
816d00947d8SCraig Rodrigues 
817d00947d8SCraig Rodrigues 	vp = UNIONFSTOV(unp);
818d00947d8SCraig Rodrigues 	lvp = unp->un_lowervp;
8193ecefc4aSJason A. Harmening 	ASSERT_VOP_ELOCKED(lvp, __func__);
82066191a76SJason A. Harmening 	ASSERT_VOP_ELOCKED(uvp, __func__);
821a9b794ffSDaichi GOTO 	dvp = unp->un_dvp;
822d00947d8SCraig Rodrigues 
823d877dd57SJason A. Harmening 	VNASSERT(vp->v_writecount == 0, vp,
824d877dd57SJason A. Harmening 	    ("%s: non-zero writecount", __func__));
825d00947d8SCraig Rodrigues 	/*
8266ff167aaSJason A. Harmening 	 * Update the upper vnode's lock state to match the lower vnode,
827866dd633SJason A. Harmening 	 * and then switch the unionfs vnode's lock to the upper vnode.
828d00947d8SCraig Rodrigues 	 */
829866dd633SJason A. Harmening 	lockrec = lvp->v_vnlock->lk_recurse;
830866dd633SJason A. Harmening 	for (count = 0; count < lockrec; count++)
831866dd633SJason A. Harmening 		vn_lock(uvp, LK_EXCLUSIVE | LK_CANRECURSE | LK_RETRY);
832d00947d8SCraig Rodrigues 	VI_LOCK(vp);
833d00947d8SCraig Rodrigues 	unp->un_uppervp = uvp;
834d00947d8SCraig Rodrigues 	vp->v_vnlock = uvp->v_vnlock;
835d00947d8SCraig Rodrigues 	VI_UNLOCK(vp);
836a9b794ffSDaichi GOTO 
837a9b794ffSDaichi GOTO 	/*
838fd8ad212SJason A. Harmening 	 * Re-cache the unionfs vnode against the upper vnode
839a9b794ffSDaichi GOTO 	 */
840fd8ad212SJason A. Harmening 	if (dvp != NULLVP && vp->v_type == VDIR) {
841a9b794ffSDaichi GOTO 		VI_LOCK(dvp);
842fd8ad212SJason A. Harmening 		if (unp->un_hash.le_prev != NULL) {
843a9b794ffSDaichi GOTO 			LIST_REMOVE(unp, un_hash);
844fd8ad212SJason A. Harmening 			hd = unionfs_get_hashhead(dvp, uvp);
845a9b794ffSDaichi GOTO 			LIST_INSERT_HEAD(hd, unp, un_hash);
846fd8ad212SJason A. Harmening 		}
847fd8ad212SJason A. Harmening 		VI_UNLOCK(unp->un_dvp);
848a9b794ffSDaichi GOTO 	}
849d00947d8SCraig Rodrigues }
850d00947d8SCraig Rodrigues 
851d00947d8SCraig Rodrigues /*
852d00947d8SCraig Rodrigues  * Create a new shadow dir.
853d00947d8SCraig Rodrigues  *
854d00947d8SCraig Rodrigues  * udvp should be locked on entry and will be locked on return.
855d00947d8SCraig Rodrigues  *
856d00947d8SCraig Rodrigues  * If no error returned, unp will be updated.
857d00947d8SCraig Rodrigues  */
858d00947d8SCraig Rodrigues int
859d00947d8SCraig Rodrigues unionfs_mkshadowdir(struct unionfs_mount *ump, struct vnode *udvp,
860312d49efSJason A. Harmening     struct unionfs_node *unp, struct componentname *cnp, struct thread *td)
861d00947d8SCraig Rodrigues {
862d00947d8SCraig Rodrigues 	struct vnode   *lvp;
863d00947d8SCraig Rodrigues 	struct vnode   *uvp;
864d00947d8SCraig Rodrigues 	struct vattr	va;
865d00947d8SCraig Rodrigues 	struct vattr	lva;
866190110f2SKonstantin Belousov 	struct nameidata nd;
867d00947d8SCraig Rodrigues 	struct mount   *mp;
868d00947d8SCraig Rodrigues 	struct ucred   *cred;
869d00947d8SCraig Rodrigues 	struct ucred   *credbk;
870d00947d8SCraig Rodrigues 	struct uidinfo *rootinfo;
871312d49efSJason A. Harmening 	int		error;
872d00947d8SCraig Rodrigues 
873d00947d8SCraig Rodrigues 	if (unp->un_uppervp != NULLVP)
874d00947d8SCraig Rodrigues 		return (EEXIST);
875d00947d8SCraig Rodrigues 
876d00947d8SCraig Rodrigues 	lvp = unp->un_lowervp;
877d00947d8SCraig Rodrigues 	uvp = NULLVP;
878d00947d8SCraig Rodrigues 	credbk = cnp->cn_cred;
879d00947d8SCraig Rodrigues 
880d00947d8SCraig Rodrigues 	/* Authority change to root */
881d00947d8SCraig Rodrigues 	rootinfo = uifind((uid_t)0);
882d00947d8SCraig Rodrigues 	cred = crdup(cnp->cn_cred);
88381f6480dSEdward Tomasz Napierala 	/*
88481f6480dSEdward Tomasz Napierala 	 * The calls to chgproccnt() are needed to compensate for change_ruid()
88581f6480dSEdward Tomasz Napierala 	 * calling chgproccnt().
88681f6480dSEdward Tomasz Napierala 	 */
88781f6480dSEdward Tomasz Napierala 	chgproccnt(cred->cr_ruidinfo, 1, 0);
888d00947d8SCraig Rodrigues 	change_euid(cred, rootinfo);
889d00947d8SCraig Rodrigues 	change_ruid(cred, rootinfo);
890d00947d8SCraig Rodrigues 	change_svuid(cred, (uid_t)0);
891d00947d8SCraig Rodrigues 	uifree(rootinfo);
892d00947d8SCraig Rodrigues 	cnp->cn_cred = cred;
893d00947d8SCraig Rodrigues 
894190110f2SKonstantin Belousov 	memset(&nd.ni_cnd, 0, sizeof(struct componentname));
895190110f2SKonstantin Belousov 	NDPREINIT(&nd);
896d00947d8SCraig Rodrigues 
8970359a12eSAttilio Rao 	if ((error = VOP_GETATTR(lvp, &lva, cnp->cn_cred)))
898d00947d8SCraig Rodrigues 		goto unionfs_mkshadowdir_abort;
899d00947d8SCraig Rodrigues 
900190110f2SKonstantin Belousov 	if ((error = unionfs_relookup(udvp, &uvp, cnp, &nd.ni_cnd, td,
901190110f2SKonstantin Belousov 	    cnp->cn_nameptr, cnp->cn_namelen, CREATE)))
902d00947d8SCraig Rodrigues 		goto unionfs_mkshadowdir_abort;
903d00947d8SCraig Rodrigues 	if (uvp != NULLVP) {
904d00947d8SCraig Rodrigues 		if (udvp == uvp)
905d00947d8SCraig Rodrigues 			vrele(uvp);
906d00947d8SCraig Rodrigues 		else
907d00947d8SCraig Rodrigues 			vput(uvp);
908d00947d8SCraig Rodrigues 
909d00947d8SCraig Rodrigues 		error = EEXIST;
910abe95116SJason A. Harmening 		goto unionfs_mkshadowdir_abort;
911d00947d8SCraig Rodrigues 	}
912d00947d8SCraig Rodrigues 
913a75d1dddSMateusz Guzik 	if ((error = vn_start_write(udvp, &mp, V_WAIT | V_PCATCH)))
914abe95116SJason A. Harmening 		goto unionfs_mkshadowdir_abort;
915d00947d8SCraig Rodrigues 	unionfs_create_uppervattr_core(ump, &lva, &va, td);
916d00947d8SCraig Rodrigues 
917190110f2SKonstantin Belousov 	error = VOP_MKDIR(udvp, &uvp, &nd.ni_cnd, &va);
918d00947d8SCraig Rodrigues 
919d00947d8SCraig Rodrigues 	if (!error) {
920d00947d8SCraig Rodrigues 		unionfs_node_update(unp, uvp, td);
921d00947d8SCraig Rodrigues 
922d00947d8SCraig Rodrigues 		/*
923d00947d8SCraig Rodrigues 		 * XXX The bug which cannot set uid/gid was corrected.
924d00947d8SCraig Rodrigues 		 * Ignore errors.
925d00947d8SCraig Rodrigues 		 */
926d00947d8SCraig Rodrigues 		va.va_type = VNON;
927190110f2SKonstantin Belousov 		VOP_SETATTR(uvp, &va, nd.ni_cnd.cn_cred);
928d00947d8SCraig Rodrigues 	}
929d00947d8SCraig Rodrigues 	vn_finished_write(mp);
930d00947d8SCraig Rodrigues 
931d00947d8SCraig Rodrigues unionfs_mkshadowdir_abort:
932d00947d8SCraig Rodrigues 	cnp->cn_cred = credbk;
93381f6480dSEdward Tomasz Napierala 	chgproccnt(cred->cr_ruidinfo, -1, 0);
934d00947d8SCraig Rodrigues 	crfree(cred);
935d00947d8SCraig Rodrigues 
936d00947d8SCraig Rodrigues 	return (error);
937d00947d8SCraig Rodrigues }
938d00947d8SCraig Rodrigues 
939d00947d8SCraig Rodrigues /*
940d00947d8SCraig Rodrigues  * Create a new whiteout.
941d00947d8SCraig Rodrigues  *
942d00947d8SCraig Rodrigues  * dvp should be locked on entry and will be locked on return.
943d00947d8SCraig Rodrigues  */
944d00947d8SCraig Rodrigues int
945d00947d8SCraig Rodrigues unionfs_mkwhiteout(struct vnode *dvp, struct componentname *cnp,
946abe95116SJason A. Harmening     struct thread *td, char *path, int pathlen)
947d00947d8SCraig Rodrigues {
948d00947d8SCraig Rodrigues 	struct vnode   *wvp;
949190110f2SKonstantin Belousov 	struct nameidata nd;
950d00947d8SCraig Rodrigues 	struct mount   *mp;
951abe95116SJason A. Harmening 	int		error;
952d00947d8SCraig Rodrigues 
953d00947d8SCraig Rodrigues 	wvp = NULLVP;
954190110f2SKonstantin Belousov 	NDPREINIT(&nd);
955190110f2SKonstantin Belousov 	if ((error = unionfs_relookup(dvp, &wvp, cnp, &nd.ni_cnd, td, path,
956abe95116SJason A. Harmening 	    pathlen, CREATE))) {
957d00947d8SCraig Rodrigues 		return (error);
958d00947d8SCraig Rodrigues 	}
959abe95116SJason A. Harmening 	if (wvp != NULLVP) {
960d00947d8SCraig Rodrigues 		if (dvp == wvp)
961d00947d8SCraig Rodrigues 			vrele(wvp);
962d00947d8SCraig Rodrigues 		else
963d00947d8SCraig Rodrigues 			vput(wvp);
964d00947d8SCraig Rodrigues 
965d00947d8SCraig Rodrigues 		return (EEXIST);
966d00947d8SCraig Rodrigues 	}
967d00947d8SCraig Rodrigues 
968a75d1dddSMateusz Guzik 	if ((error = vn_start_write(dvp, &mp, V_WAIT | V_PCATCH)))
969d00947d8SCraig Rodrigues 		goto unionfs_mkwhiteout_free_out;
970190110f2SKonstantin Belousov 	error = VOP_WHITEOUT(dvp, &nd.ni_cnd, CREATE);
971d00947d8SCraig Rodrigues 
972d00947d8SCraig Rodrigues 	vn_finished_write(mp);
973d00947d8SCraig Rodrigues 
974d00947d8SCraig Rodrigues unionfs_mkwhiteout_free_out:
975d00947d8SCraig Rodrigues 	return (error);
976d00947d8SCraig Rodrigues }
977d00947d8SCraig Rodrigues 
978d00947d8SCraig Rodrigues /*
979d00947d8SCraig Rodrigues  * Create a new vnode for create a new shadow file.
980d00947d8SCraig Rodrigues  *
981d00947d8SCraig Rodrigues  * If an error is returned, *vpp will be invalid, otherwise it will hold a
982d00947d8SCraig Rodrigues  * locked, referenced and opened vnode.
983d00947d8SCraig Rodrigues  *
984d00947d8SCraig Rodrigues  * unp is never updated.
985df8bae1dSRodney W. Grimes  */
98680b301c3SPoul-Henning Kamp static int
987d00947d8SCraig Rodrigues unionfs_vn_create_on_upper(struct vnode **vpp, struct vnode *udvp,
988312d49efSJason A. Harmening     struct unionfs_node *unp, struct vattr *uvap, struct thread *td)
989df8bae1dSRodney W. Grimes {
990d00947d8SCraig Rodrigues 	struct unionfs_mount *ump;
991d00947d8SCraig Rodrigues 	struct vnode   *vp;
992d00947d8SCraig Rodrigues 	struct vnode   *lvp;
993d00947d8SCraig Rodrigues 	struct ucred   *cred;
994d00947d8SCraig Rodrigues 	struct vattr	lva;
995312d49efSJason A. Harmening 	struct nameidata nd;
996d00947d8SCraig Rodrigues 	int		fmode;
997d00947d8SCraig Rodrigues 	int		error;
998d00947d8SCraig Rodrigues 
999d00947d8SCraig Rodrigues 	ump = MOUNTTOUNIONFSMOUNT(UNIONFSTOV(unp)->v_mount);
1000d00947d8SCraig Rodrigues 	vp = NULLVP;
1001d00947d8SCraig Rodrigues 	lvp = unp->un_lowervp;
1002d00947d8SCraig Rodrigues 	cred = td->td_ucred;
1003d00947d8SCraig Rodrigues 	fmode = FFLAGS(O_WRONLY | O_CREAT | O_TRUNC | O_EXCL);
1004d00947d8SCraig Rodrigues 	error = 0;
1005d00947d8SCraig Rodrigues 
10060359a12eSAttilio Rao 	if ((error = VOP_GETATTR(lvp, &lva, cred)) != 0)
1007d00947d8SCraig Rodrigues 		return (error);
1008d00947d8SCraig Rodrigues 	unionfs_create_uppervattr_core(ump, &lva, uvap, td);
1009d00947d8SCraig Rodrigues 
1010d00947d8SCraig Rodrigues 	if (unp->un_path == NULL)
10113ecefc4aSJason A. Harmening 		panic("%s: NULL un_path", __func__);
1012d00947d8SCraig Rodrigues 
1013abe95116SJason A. Harmening 	nd.ni_cnd.cn_namelen = unp->un_pathlen;
1014abe95116SJason A. Harmening 	nd.ni_cnd.cn_pnbuf = unp->un_path;
1015190110f2SKonstantin Belousov 	nd.ni_cnd.cn_nameiop = CREATE;
10165b5b7e2cSMateusz Guzik 	nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF | ISLASTCN;
1017190110f2SKonstantin Belousov 	nd.ni_cnd.cn_lkflags = LK_EXCLUSIVE;
1018190110f2SKonstantin Belousov 	nd.ni_cnd.cn_cred = cred;
1019190110f2SKonstantin Belousov 	nd.ni_cnd.cn_nameptr = nd.ni_cnd.cn_pnbuf;
1020190110f2SKonstantin Belousov 	NDPREINIT(&nd);
1021d00947d8SCraig Rodrigues 
1022d00947d8SCraig Rodrigues 	vref(udvp);
10238f874e92SMateusz Guzik 	if ((error = vfs_relookup(udvp, &vp, &nd.ni_cnd, false)) != 0)
1024d00947d8SCraig Rodrigues 		goto unionfs_vn_create_on_upper_free_out2;
1025d00947d8SCraig Rodrigues 	vrele(udvp);
1026d00947d8SCraig Rodrigues 
1027d00947d8SCraig Rodrigues 	if (vp != NULLVP) {
1028d00947d8SCraig Rodrigues 		if (vp == udvp)
1029d00947d8SCraig Rodrigues 			vrele(vp);
1030d00947d8SCraig Rodrigues 		else
1031d00947d8SCraig Rodrigues 			vput(vp);
1032d00947d8SCraig Rodrigues 		error = EEXIST;
1033d00947d8SCraig Rodrigues 		goto unionfs_vn_create_on_upper_free_out1;
1034d00947d8SCraig Rodrigues 	}
1035d00947d8SCraig Rodrigues 
1036190110f2SKonstantin Belousov 	if ((error = VOP_CREATE(udvp, &vp, &nd.ni_cnd, uvap)) != 0)
1037d00947d8SCraig Rodrigues 		goto unionfs_vn_create_on_upper_free_out1;
1038d00947d8SCraig Rodrigues 
10399e223287SKonstantin Belousov 	if ((error = VOP_OPEN(vp, fmode, cred, td, NULL)) != 0) {
1040d00947d8SCraig Rodrigues 		vput(vp);
1041d00947d8SCraig Rodrigues 		goto unionfs_vn_create_on_upper_free_out1;
1042d00947d8SCraig Rodrigues 	}
104378022527SKonstantin Belousov 	error = VOP_ADD_WRITECOUNT(vp, 1);
1044312d49efSJason A. Harmening 	CTR3(KTR_VFS, "%s: vp %p v_writecount increased to %d",
1045312d49efSJason A. Harmening 	    __func__, vp, vp->v_writecount);
104678022527SKonstantin Belousov 	if (error == 0) {
1047d00947d8SCraig Rodrigues 		*vpp = vp;
104878022527SKonstantin Belousov 	} else {
104978022527SKonstantin Belousov 		VOP_CLOSE(vp, fmode, cred, td);
105078022527SKonstantin Belousov 	}
1051d00947d8SCraig Rodrigues 
1052d00947d8SCraig Rodrigues unionfs_vn_create_on_upper_free_out1:
1053b249ce48SMateusz Guzik 	VOP_UNLOCK(udvp);
1054d00947d8SCraig Rodrigues 
1055d00947d8SCraig Rodrigues unionfs_vn_create_on_upper_free_out2:
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 
1166a75d1dddSMateusz Guzik 	if ((error = vn_start_write(udvp, &mp, V_WAIT | V_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;
12815b5b7e2cSMateusz Guzik 			cn.cn_flags = LOCKPARENT | LOCKLEAF | RDONLY | ISLASTCN;
1282d00947d8SCraig Rodrigues 			cn.cn_lkflags = LK_EXCLUSIVE;
1283d00947d8SCraig Rodrigues 			cn.cn_cred = cred;
1284df8bae1dSRodney W. Grimes 
12852a31267eSMatthew Dillon 			/*
1286d00947d8SCraig Rodrigues 			 * check entry in lower.
1287d00947d8SCraig Rodrigues 			 * Sometimes, readdir function returns
1288d00947d8SCraig Rodrigues 			 * wrong entry.
12892a31267eSMatthew Dillon 			 */
1290d00947d8SCraig Rodrigues 			lookuperr = VOP_LOOKUP(lvp, &tvp, &cn);
1291df8bae1dSRodney W. Grimes 
1292d00947d8SCraig Rodrigues 			if (!lookuperr)
1293d00947d8SCraig Rodrigues 				vput(tvp);
12942a31267eSMatthew Dillon 			else
1295d00947d8SCraig Rodrigues 				continue; /* skip entry */
1296df8bae1dSRodney W. Grimes 
1297df8bae1dSRodney W. Grimes 			/*
1298d00947d8SCraig Rodrigues 			 * check entry
1299d00947d8SCraig Rodrigues 			 * If it has no exist/whiteout entry in upper,
1300d00947d8SCraig Rodrigues 			 * directory is not empty.
1301df8bae1dSRodney W. Grimes 			 */
13025b5b7e2cSMateusz Guzik 			cn.cn_flags = LOCKPARENT | LOCKLEAF | RDONLY | ISLASTCN;
1303d00947d8SCraig Rodrigues 			lookuperr = VOP_LOOKUP(uvp, &tvp, &cn);
1304df8bae1dSRodney W. Grimes 
1305d00947d8SCraig Rodrigues 			if (!lookuperr)
1306d00947d8SCraig Rodrigues 				vput(tvp);
1307df8bae1dSRodney W. Grimes 
1308d00947d8SCraig Rodrigues 			/* ignore exist or whiteout entry */
1309d00947d8SCraig Rodrigues 			if (!lookuperr ||
1310d00947d8SCraig Rodrigues 			    (lookuperr == ENOENT && (cn.cn_flags & ISWHITEOUT)))
131101634480SBrian Feldman 				continue;
1312d00947d8SCraig Rodrigues 
1313d00947d8SCraig Rodrigues 			error = ENOTEMPTY;
1314df8bae1dSRodney W. Grimes 		}
1315996c772fSJohn Dyson 	}
1316996c772fSJohn Dyson 
1317d00947d8SCraig Rodrigues 	/* close vnode */
1318d00947d8SCraig Rodrigues 	VOP_CLOSE(vp, FREAD, cred, td);
1319d00947d8SCraig Rodrigues 
1320d00947d8SCraig Rodrigues 	return (error);
1321d00947d8SCraig Rodrigues }
1322d00947d8SCraig Rodrigues 
1323