xref: /freebsd/sys/fs/unionfs/union_subr.c (revision a75d1ddd74312f5dd79bc1e965f7077679659f2e)
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;
665df8bae1dSRodney W. Grimes 
666d00947d8SCraig Rodrigues 	cn->cn_namelen = pathlen;
667abe95116SJason A. Harmening 	cn->cn_pnbuf = path;
668d00947d8SCraig Rodrigues 	cn->cn_nameiop = nameiop;
6695b5b7e2cSMateusz Guzik 	cn->cn_flags = (LOCKPARENT | LOCKLEAF | ISLASTCN);
670d00947d8SCraig Rodrigues 	cn->cn_lkflags = LK_EXCLUSIVE;
671d00947d8SCraig Rodrigues 	cn->cn_cred = cnp->cn_cred;
672d00947d8SCraig Rodrigues 	cn->cn_nameptr = cn->cn_pnbuf;
673df8bae1dSRodney W. Grimes 
674d00947d8SCraig Rodrigues 	if (nameiop == DELETE)
675d00947d8SCraig Rodrigues 		cn->cn_flags |= (cnp->cn_flags & (DOWHITEOUT | SAVESTART));
676d00947d8SCraig Rodrigues 	else if (RENAME == nameiop)
677d00947d8SCraig Rodrigues 		cn->cn_flags |= (cnp->cn_flags & SAVESTART);
6786c21f6edSKonstantin Belousov 	else if (nameiop == CREATE)
6796c21f6edSKonstantin Belousov 		cn->cn_flags |= NOCACHE;
680d00947d8SCraig Rodrigues 
681d00947d8SCraig Rodrigues 	vref(dvp);
682b249ce48SMateusz Guzik 	VOP_UNLOCK(dvp);
683d00947d8SCraig Rodrigues 
6840134bbe5SMateusz Guzik 	if ((error = vfs_relookup(dvp, vpp, cn))) {
685cb05b60aSAttilio Rao 		vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
686d00947d8SCraig Rodrigues 	} else
687d00947d8SCraig Rodrigues 		vrele(dvp);
688d00947d8SCraig Rodrigues 
689abe95116SJason A. Harmening 	KASSERT(cn->cn_pnbuf == path, ("%s: cn_pnbuf changed", __func__));
690abe95116SJason A. Harmening 
691d00947d8SCraig Rodrigues 	return (error);
692df8bae1dSRodney W. Grimes }
693df8bae1dSRodney W. Grimes 
694df8bae1dSRodney W. Grimes /*
695d00947d8SCraig Rodrigues  * relookup for CREATE namei operation.
6962a31267eSMatthew Dillon  *
697d00947d8SCraig Rodrigues  * dvp is unionfs vnode. dvp should be locked.
698d00947d8SCraig Rodrigues  *
699d00947d8SCraig Rodrigues  * If it called 'unionfs_copyfile' function by unionfs_link etc,
700d00947d8SCraig Rodrigues  * VOP_LOOKUP information is broken.
701d00947d8SCraig Rodrigues  * So it need relookup in order to create link etc.
702d00947d8SCraig Rodrigues  */
703d00947d8SCraig Rodrigues int
704d00947d8SCraig Rodrigues unionfs_relookup_for_create(struct vnode *dvp, struct componentname *cnp,
705d00947d8SCraig Rodrigues     struct thread *td)
706d00947d8SCraig Rodrigues {
707d00947d8SCraig Rodrigues 	struct vnode *udvp;
708d00947d8SCraig Rodrigues 	struct vnode *vp;
709d00947d8SCraig Rodrigues 	struct componentname cn;
710312d49efSJason A. Harmening 	int error;
711d00947d8SCraig Rodrigues 
712d00947d8SCraig Rodrigues 	udvp = UNIONFSVPTOUPPERVP(dvp);
713d00947d8SCraig Rodrigues 	vp = NULLVP;
714d00947d8SCraig Rodrigues 
715d00947d8SCraig Rodrigues 	error = unionfs_relookup(udvp, &vp, cnp, &cn, td, cnp->cn_nameptr,
716abe95116SJason A. Harmening 	    cnp->cn_namelen, CREATE);
717d00947d8SCraig Rodrigues 	if (error)
718d00947d8SCraig Rodrigues 		return (error);
719d00947d8SCraig Rodrigues 
720d00947d8SCraig Rodrigues 	if (vp != NULLVP) {
721d00947d8SCraig Rodrigues 		if (udvp == vp)
722d00947d8SCraig Rodrigues 			vrele(vp);
723d00947d8SCraig Rodrigues 		else
724d00947d8SCraig Rodrigues 			vput(vp);
725d00947d8SCraig Rodrigues 
726d00947d8SCraig Rodrigues 		error = EEXIST;
727d00947d8SCraig Rodrigues 	}
728d00947d8SCraig Rodrigues 
729d00947d8SCraig Rodrigues 	return (error);
730d00947d8SCraig Rodrigues }
731d00947d8SCraig Rodrigues 
732d00947d8SCraig Rodrigues /*
733d00947d8SCraig Rodrigues  * relookup for DELETE namei operation.
734d00947d8SCraig Rodrigues  *
735d00947d8SCraig Rodrigues  * dvp is unionfs vnode. dvp should be locked.
736d00947d8SCraig Rodrigues  */
737d00947d8SCraig Rodrigues int
738d00947d8SCraig Rodrigues unionfs_relookup_for_delete(struct vnode *dvp, struct componentname *cnp,
739d00947d8SCraig Rodrigues     struct thread *td)
740d00947d8SCraig Rodrigues {
741d00947d8SCraig Rodrigues 	struct vnode *udvp;
742d00947d8SCraig Rodrigues 	struct vnode *vp;
743d00947d8SCraig Rodrigues 	struct componentname cn;
744312d49efSJason A. Harmening 	int error;
745d00947d8SCraig Rodrigues 
746d00947d8SCraig Rodrigues 	udvp = UNIONFSVPTOUPPERVP(dvp);
747d00947d8SCraig Rodrigues 	vp = NULLVP;
748d00947d8SCraig Rodrigues 
749d00947d8SCraig Rodrigues 	error = unionfs_relookup(udvp, &vp, cnp, &cn, td, cnp->cn_nameptr,
750abe95116SJason A. Harmening 	    cnp->cn_namelen, DELETE);
751d00947d8SCraig Rodrigues 	if (error)
752d00947d8SCraig Rodrigues 		return (error);
753d00947d8SCraig Rodrigues 
754d00947d8SCraig Rodrigues 	if (vp == NULLVP)
755d00947d8SCraig Rodrigues 		error = ENOENT;
756d00947d8SCraig Rodrigues 	else {
757d00947d8SCraig Rodrigues 		if (udvp == vp)
758d00947d8SCraig Rodrigues 			vrele(vp);
759d00947d8SCraig Rodrigues 		else
760d00947d8SCraig Rodrigues 			vput(vp);
761d00947d8SCraig Rodrigues 	}
762d00947d8SCraig Rodrigues 
763d00947d8SCraig Rodrigues 	return (error);
764d00947d8SCraig Rodrigues }
765d00947d8SCraig Rodrigues 
766d00947d8SCraig Rodrigues /*
767d00947d8SCraig Rodrigues  * relookup for RENAME namei operation.
768d00947d8SCraig Rodrigues  *
769d00947d8SCraig Rodrigues  * dvp is unionfs vnode. dvp should be locked.
770d00947d8SCraig Rodrigues  */
771d00947d8SCraig Rodrigues int
772d00947d8SCraig Rodrigues unionfs_relookup_for_rename(struct vnode *dvp, struct componentname *cnp,
773d00947d8SCraig Rodrigues     struct thread *td)
774d00947d8SCraig Rodrigues {
775d00947d8SCraig Rodrigues 	struct vnode *udvp;
776d00947d8SCraig Rodrigues 	struct vnode *vp;
777d00947d8SCraig Rodrigues 	struct componentname cn;
778312d49efSJason A. Harmening 	int error;
779d00947d8SCraig Rodrigues 
780d00947d8SCraig Rodrigues 	udvp = UNIONFSVPTOUPPERVP(dvp);
781d00947d8SCraig Rodrigues 	vp = NULLVP;
782d00947d8SCraig Rodrigues 
783d00947d8SCraig Rodrigues 	error = unionfs_relookup(udvp, &vp, cnp, &cn, td, cnp->cn_nameptr,
784abe95116SJason A. Harmening 	    cnp->cn_namelen, RENAME);
785d00947d8SCraig Rodrigues 	if (error)
786d00947d8SCraig Rodrigues 		return (error);
787d00947d8SCraig Rodrigues 
788d00947d8SCraig Rodrigues 	if (vp != NULLVP) {
789d00947d8SCraig Rodrigues 		if (udvp == vp)
790d00947d8SCraig Rodrigues 			vrele(vp);
791d00947d8SCraig Rodrigues 		else
792d00947d8SCraig Rodrigues 			vput(vp);
793d00947d8SCraig Rodrigues 	}
794d00947d8SCraig Rodrigues 
795d00947d8SCraig Rodrigues 	return (error);
796d00947d8SCraig Rodrigues }
797d00947d8SCraig Rodrigues 
798d00947d8SCraig Rodrigues /*
799d00947d8SCraig Rodrigues  * Update the unionfs_node.
800d00947d8SCraig Rodrigues  *
801d00947d8SCraig Rodrigues  * uvp is new locked upper vnode. unionfs vnode's lock will be exchanged to the
802d00947d8SCraig Rodrigues  * uvp's lock and lower's lock will be unlocked.
803d00947d8SCraig Rodrigues  */
804d00947d8SCraig Rodrigues static void
805d00947d8SCraig Rodrigues unionfs_node_update(struct unionfs_node *unp, struct vnode *uvp,
806d00947d8SCraig Rodrigues     struct thread *td)
807d00947d8SCraig Rodrigues {
808fd8ad212SJason A. Harmening 	struct unionfs_node_hashhead *hd;
809d00947d8SCraig Rodrigues 	struct vnode   *vp;
810d00947d8SCraig Rodrigues 	struct vnode   *lvp;
811a9b794ffSDaichi GOTO 	struct vnode   *dvp;
812312d49efSJason A. Harmening 	unsigned	count, lockrec;
813d00947d8SCraig Rodrigues 
814d00947d8SCraig Rodrigues 	vp = UNIONFSTOV(unp);
815d00947d8SCraig Rodrigues 	lvp = unp->un_lowervp;
8163ecefc4aSJason A. Harmening 	ASSERT_VOP_ELOCKED(lvp, __func__);
81766191a76SJason A. Harmening 	ASSERT_VOP_ELOCKED(uvp, __func__);
818a9b794ffSDaichi GOTO 	dvp = unp->un_dvp;
819d00947d8SCraig Rodrigues 
820d877dd57SJason A. Harmening 	VNASSERT(vp->v_writecount == 0, vp,
821d877dd57SJason A. Harmening 	    ("%s: non-zero writecount", __func__));
822d00947d8SCraig Rodrigues 	/*
8236ff167aaSJason A. Harmening 	 * Update the upper vnode's lock state to match the lower vnode,
824866dd633SJason A. Harmening 	 * and then switch the unionfs vnode's lock to the upper vnode.
825d00947d8SCraig Rodrigues 	 */
826866dd633SJason A. Harmening 	lockrec = lvp->v_vnlock->lk_recurse;
827866dd633SJason A. Harmening 	for (count = 0; count < lockrec; count++)
828866dd633SJason A. Harmening 		vn_lock(uvp, LK_EXCLUSIVE | LK_CANRECURSE | LK_RETRY);
829d00947d8SCraig Rodrigues 	VI_LOCK(vp);
830d00947d8SCraig Rodrigues 	unp->un_uppervp = uvp;
831d00947d8SCraig Rodrigues 	vp->v_vnlock = uvp->v_vnlock;
832d00947d8SCraig Rodrigues 	VI_UNLOCK(vp);
833a9b794ffSDaichi GOTO 
834a9b794ffSDaichi GOTO 	/*
835fd8ad212SJason A. Harmening 	 * Re-cache the unionfs vnode against the upper vnode
836a9b794ffSDaichi GOTO 	 */
837fd8ad212SJason A. Harmening 	if (dvp != NULLVP && vp->v_type == VDIR) {
838a9b794ffSDaichi GOTO 		VI_LOCK(dvp);
839fd8ad212SJason A. Harmening 		if (unp->un_hash.le_prev != NULL) {
840a9b794ffSDaichi GOTO 			LIST_REMOVE(unp, un_hash);
841fd8ad212SJason A. Harmening 			hd = unionfs_get_hashhead(dvp, uvp);
842a9b794ffSDaichi GOTO 			LIST_INSERT_HEAD(hd, unp, un_hash);
843fd8ad212SJason A. Harmening 		}
844fd8ad212SJason A. Harmening 		VI_UNLOCK(unp->un_dvp);
845a9b794ffSDaichi GOTO 	}
846d00947d8SCraig Rodrigues }
847d00947d8SCraig Rodrigues 
848d00947d8SCraig Rodrigues /*
849d00947d8SCraig Rodrigues  * Create a new shadow dir.
850d00947d8SCraig Rodrigues  *
851d00947d8SCraig Rodrigues  * udvp should be locked on entry and will be locked on return.
852d00947d8SCraig Rodrigues  *
853d00947d8SCraig Rodrigues  * If no error returned, unp will be updated.
854d00947d8SCraig Rodrigues  */
855d00947d8SCraig Rodrigues int
856d00947d8SCraig Rodrigues unionfs_mkshadowdir(struct unionfs_mount *ump, struct vnode *udvp,
857312d49efSJason A. Harmening     struct unionfs_node *unp, struct componentname *cnp, struct thread *td)
858d00947d8SCraig Rodrigues {
859d00947d8SCraig Rodrigues 	struct vnode   *lvp;
860d00947d8SCraig Rodrigues 	struct vnode   *uvp;
861d00947d8SCraig Rodrigues 	struct vattr	va;
862d00947d8SCraig Rodrigues 	struct vattr	lva;
863190110f2SKonstantin Belousov 	struct nameidata nd;
864d00947d8SCraig Rodrigues 	struct mount   *mp;
865d00947d8SCraig Rodrigues 	struct ucred   *cred;
866d00947d8SCraig Rodrigues 	struct ucred   *credbk;
867d00947d8SCraig Rodrigues 	struct uidinfo *rootinfo;
868312d49efSJason A. Harmening 	int		error;
869d00947d8SCraig Rodrigues 
870d00947d8SCraig Rodrigues 	if (unp->un_uppervp != NULLVP)
871d00947d8SCraig Rodrigues 		return (EEXIST);
872d00947d8SCraig Rodrigues 
873d00947d8SCraig Rodrigues 	lvp = unp->un_lowervp;
874d00947d8SCraig Rodrigues 	uvp = NULLVP;
875d00947d8SCraig Rodrigues 	credbk = cnp->cn_cred;
876d00947d8SCraig Rodrigues 
877d00947d8SCraig Rodrigues 	/* Authority change to root */
878d00947d8SCraig Rodrigues 	rootinfo = uifind((uid_t)0);
879d00947d8SCraig Rodrigues 	cred = crdup(cnp->cn_cred);
88081f6480dSEdward Tomasz Napierala 	/*
88181f6480dSEdward Tomasz Napierala 	 * The calls to chgproccnt() are needed to compensate for change_ruid()
88281f6480dSEdward Tomasz Napierala 	 * calling chgproccnt().
88381f6480dSEdward Tomasz Napierala 	 */
88481f6480dSEdward Tomasz Napierala 	chgproccnt(cred->cr_ruidinfo, 1, 0);
885d00947d8SCraig Rodrigues 	change_euid(cred, rootinfo);
886d00947d8SCraig Rodrigues 	change_ruid(cred, rootinfo);
887d00947d8SCraig Rodrigues 	change_svuid(cred, (uid_t)0);
888d00947d8SCraig Rodrigues 	uifree(rootinfo);
889d00947d8SCraig Rodrigues 	cnp->cn_cred = cred;
890d00947d8SCraig Rodrigues 
891190110f2SKonstantin Belousov 	memset(&nd.ni_cnd, 0, sizeof(struct componentname));
892190110f2SKonstantin Belousov 	NDPREINIT(&nd);
893d00947d8SCraig Rodrigues 
8940359a12eSAttilio Rao 	if ((error = VOP_GETATTR(lvp, &lva, cnp->cn_cred)))
895d00947d8SCraig Rodrigues 		goto unionfs_mkshadowdir_abort;
896d00947d8SCraig Rodrigues 
897190110f2SKonstantin Belousov 	if ((error = unionfs_relookup(udvp, &uvp, cnp, &nd.ni_cnd, td,
898190110f2SKonstantin Belousov 	    cnp->cn_nameptr, cnp->cn_namelen, CREATE)))
899d00947d8SCraig Rodrigues 		goto unionfs_mkshadowdir_abort;
900d00947d8SCraig Rodrigues 	if (uvp != NULLVP) {
901d00947d8SCraig Rodrigues 		if (udvp == uvp)
902d00947d8SCraig Rodrigues 			vrele(uvp);
903d00947d8SCraig Rodrigues 		else
904d00947d8SCraig Rodrigues 			vput(uvp);
905d00947d8SCraig Rodrigues 
906d00947d8SCraig Rodrigues 		error = EEXIST;
907abe95116SJason A. Harmening 		goto unionfs_mkshadowdir_abort;
908d00947d8SCraig Rodrigues 	}
909d00947d8SCraig Rodrigues 
910*a75d1dddSMateusz Guzik 	if ((error = vn_start_write(udvp, &mp, V_WAIT | V_PCATCH)))
911abe95116SJason A. Harmening 		goto unionfs_mkshadowdir_abort;
912d00947d8SCraig Rodrigues 	unionfs_create_uppervattr_core(ump, &lva, &va, td);
913d00947d8SCraig Rodrigues 
914190110f2SKonstantin Belousov 	error = VOP_MKDIR(udvp, &uvp, &nd.ni_cnd, &va);
915d00947d8SCraig Rodrigues 
916d00947d8SCraig Rodrigues 	if (!error) {
917d00947d8SCraig Rodrigues 		unionfs_node_update(unp, uvp, td);
918d00947d8SCraig Rodrigues 
919d00947d8SCraig Rodrigues 		/*
920d00947d8SCraig Rodrigues 		 * XXX The bug which cannot set uid/gid was corrected.
921d00947d8SCraig Rodrigues 		 * Ignore errors.
922d00947d8SCraig Rodrigues 		 */
923d00947d8SCraig Rodrigues 		va.va_type = VNON;
924190110f2SKonstantin Belousov 		VOP_SETATTR(uvp, &va, nd.ni_cnd.cn_cred);
925d00947d8SCraig Rodrigues 	}
926d00947d8SCraig Rodrigues 	vn_finished_write(mp);
927d00947d8SCraig Rodrigues 
928d00947d8SCraig Rodrigues unionfs_mkshadowdir_abort:
929d00947d8SCraig Rodrigues 	cnp->cn_cred = credbk;
93081f6480dSEdward Tomasz Napierala 	chgproccnt(cred->cr_ruidinfo, -1, 0);
931d00947d8SCraig Rodrigues 	crfree(cred);
932d00947d8SCraig Rodrigues 
933d00947d8SCraig Rodrigues 	return (error);
934d00947d8SCraig Rodrigues }
935d00947d8SCraig Rodrigues 
936d00947d8SCraig Rodrigues /*
937d00947d8SCraig Rodrigues  * Create a new whiteout.
938d00947d8SCraig Rodrigues  *
939d00947d8SCraig Rodrigues  * dvp should be locked on entry and will be locked on return.
940d00947d8SCraig Rodrigues  */
941d00947d8SCraig Rodrigues int
942d00947d8SCraig Rodrigues unionfs_mkwhiteout(struct vnode *dvp, struct componentname *cnp,
943abe95116SJason A. Harmening     struct thread *td, char *path, int pathlen)
944d00947d8SCraig Rodrigues {
945d00947d8SCraig Rodrigues 	struct vnode   *wvp;
946190110f2SKonstantin Belousov 	struct nameidata nd;
947d00947d8SCraig Rodrigues 	struct mount   *mp;
948abe95116SJason A. Harmening 	int		error;
949d00947d8SCraig Rodrigues 
950d00947d8SCraig Rodrigues 	wvp = NULLVP;
951190110f2SKonstantin Belousov 	NDPREINIT(&nd);
952190110f2SKonstantin Belousov 	if ((error = unionfs_relookup(dvp, &wvp, cnp, &nd.ni_cnd, td, path,
953abe95116SJason A. Harmening 	    pathlen, CREATE))) {
954d00947d8SCraig Rodrigues 		return (error);
955d00947d8SCraig Rodrigues 	}
956abe95116SJason A. Harmening 	if (wvp != NULLVP) {
957d00947d8SCraig Rodrigues 		if (dvp == wvp)
958d00947d8SCraig Rodrigues 			vrele(wvp);
959d00947d8SCraig Rodrigues 		else
960d00947d8SCraig Rodrigues 			vput(wvp);
961d00947d8SCraig Rodrigues 
962d00947d8SCraig Rodrigues 		return (EEXIST);
963d00947d8SCraig Rodrigues 	}
964d00947d8SCraig Rodrigues 
965*a75d1dddSMateusz Guzik 	if ((error = vn_start_write(dvp, &mp, V_WAIT | V_PCATCH)))
966d00947d8SCraig Rodrigues 		goto unionfs_mkwhiteout_free_out;
967190110f2SKonstantin Belousov 	error = VOP_WHITEOUT(dvp, &nd.ni_cnd, CREATE);
968d00947d8SCraig Rodrigues 
969d00947d8SCraig Rodrigues 	vn_finished_write(mp);
970d00947d8SCraig Rodrigues 
971d00947d8SCraig Rodrigues unionfs_mkwhiteout_free_out:
972d00947d8SCraig Rodrigues 	return (error);
973d00947d8SCraig Rodrigues }
974d00947d8SCraig Rodrigues 
975d00947d8SCraig Rodrigues /*
976d00947d8SCraig Rodrigues  * Create a new vnode for create a new shadow file.
977d00947d8SCraig Rodrigues  *
978d00947d8SCraig Rodrigues  * If an error is returned, *vpp will be invalid, otherwise it will hold a
979d00947d8SCraig Rodrigues  * locked, referenced and opened vnode.
980d00947d8SCraig Rodrigues  *
981d00947d8SCraig Rodrigues  * unp is never updated.
982df8bae1dSRodney W. Grimes  */
98380b301c3SPoul-Henning Kamp static int
984d00947d8SCraig Rodrigues unionfs_vn_create_on_upper(struct vnode **vpp, struct vnode *udvp,
985312d49efSJason A. Harmening     struct unionfs_node *unp, struct vattr *uvap, struct thread *td)
986df8bae1dSRodney W. Grimes {
987d00947d8SCraig Rodrigues 	struct unionfs_mount *ump;
988d00947d8SCraig Rodrigues 	struct vnode   *vp;
989d00947d8SCraig Rodrigues 	struct vnode   *lvp;
990d00947d8SCraig Rodrigues 	struct ucred   *cred;
991d00947d8SCraig Rodrigues 	struct vattr	lva;
992312d49efSJason A. Harmening 	struct nameidata nd;
993d00947d8SCraig Rodrigues 	int		fmode;
994d00947d8SCraig Rodrigues 	int		error;
995d00947d8SCraig Rodrigues 
996d00947d8SCraig Rodrigues 	ump = MOUNTTOUNIONFSMOUNT(UNIONFSTOV(unp)->v_mount);
997d00947d8SCraig Rodrigues 	vp = NULLVP;
998d00947d8SCraig Rodrigues 	lvp = unp->un_lowervp;
999d00947d8SCraig Rodrigues 	cred = td->td_ucred;
1000d00947d8SCraig Rodrigues 	fmode = FFLAGS(O_WRONLY | O_CREAT | O_TRUNC | O_EXCL);
1001d00947d8SCraig Rodrigues 	error = 0;
1002d00947d8SCraig Rodrigues 
10030359a12eSAttilio Rao 	if ((error = VOP_GETATTR(lvp, &lva, cred)) != 0)
1004d00947d8SCraig Rodrigues 		return (error);
1005d00947d8SCraig Rodrigues 	unionfs_create_uppervattr_core(ump, &lva, uvap, td);
1006d00947d8SCraig Rodrigues 
1007d00947d8SCraig Rodrigues 	if (unp->un_path == NULL)
10083ecefc4aSJason A. Harmening 		panic("%s: NULL un_path", __func__);
1009d00947d8SCraig Rodrigues 
1010abe95116SJason A. Harmening 	nd.ni_cnd.cn_namelen = unp->un_pathlen;
1011abe95116SJason A. Harmening 	nd.ni_cnd.cn_pnbuf = unp->un_path;
1012190110f2SKonstantin Belousov 	nd.ni_cnd.cn_nameiop = CREATE;
10135b5b7e2cSMateusz Guzik 	nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF | ISLASTCN;
1014190110f2SKonstantin Belousov 	nd.ni_cnd.cn_lkflags = LK_EXCLUSIVE;
1015190110f2SKonstantin Belousov 	nd.ni_cnd.cn_cred = cred;
1016190110f2SKonstantin Belousov 	nd.ni_cnd.cn_nameptr = nd.ni_cnd.cn_pnbuf;
1017190110f2SKonstantin Belousov 	NDPREINIT(&nd);
1018d00947d8SCraig Rodrigues 
1019d00947d8SCraig Rodrigues 	vref(udvp);
10200134bbe5SMateusz Guzik 	if ((error = vfs_relookup(udvp, &vp, &nd.ni_cnd)) != 0)
1021d00947d8SCraig Rodrigues 		goto unionfs_vn_create_on_upper_free_out2;
1022d00947d8SCraig Rodrigues 	vrele(udvp);
1023d00947d8SCraig Rodrigues 
1024d00947d8SCraig Rodrigues 	if (vp != NULLVP) {
1025d00947d8SCraig Rodrigues 		if (vp == udvp)
1026d00947d8SCraig Rodrigues 			vrele(vp);
1027d00947d8SCraig Rodrigues 		else
1028d00947d8SCraig Rodrigues 			vput(vp);
1029d00947d8SCraig Rodrigues 		error = EEXIST;
1030d00947d8SCraig Rodrigues 		goto unionfs_vn_create_on_upper_free_out1;
1031d00947d8SCraig Rodrigues 	}
1032d00947d8SCraig Rodrigues 
1033190110f2SKonstantin Belousov 	if ((error = VOP_CREATE(udvp, &vp, &nd.ni_cnd, uvap)) != 0)
1034d00947d8SCraig Rodrigues 		goto unionfs_vn_create_on_upper_free_out1;
1035d00947d8SCraig Rodrigues 
10369e223287SKonstantin Belousov 	if ((error = VOP_OPEN(vp, fmode, cred, td, NULL)) != 0) {
1037d00947d8SCraig Rodrigues 		vput(vp);
1038d00947d8SCraig Rodrigues 		goto unionfs_vn_create_on_upper_free_out1;
1039d00947d8SCraig Rodrigues 	}
104078022527SKonstantin Belousov 	error = VOP_ADD_WRITECOUNT(vp, 1);
1041312d49efSJason A. Harmening 	CTR3(KTR_VFS, "%s: vp %p v_writecount increased to %d",
1042312d49efSJason A. Harmening 	    __func__, vp, vp->v_writecount);
104378022527SKonstantin Belousov 	if (error == 0) {
1044d00947d8SCraig Rodrigues 		*vpp = vp;
104578022527SKonstantin Belousov 	} else {
104678022527SKonstantin Belousov 		VOP_CLOSE(vp, fmode, cred, td);
104778022527SKonstantin Belousov 	}
1048d00947d8SCraig Rodrigues 
1049d00947d8SCraig Rodrigues unionfs_vn_create_on_upper_free_out1:
1050b249ce48SMateusz Guzik 	VOP_UNLOCK(udvp);
1051d00947d8SCraig Rodrigues 
1052d00947d8SCraig Rodrigues unionfs_vn_create_on_upper_free_out2:
1053abe95116SJason A. Harmening 	KASSERT(nd.ni_cnd.cn_pnbuf == unp->un_path,
1054abe95116SJason A. Harmening 	    ("%s: cn_pnbuf changed", __func__));
1055d00947d8SCraig Rodrigues 
1056d00947d8SCraig Rodrigues 	return (error);
1057d00947d8SCraig Rodrigues }
1058d00947d8SCraig Rodrigues 
1059d00947d8SCraig Rodrigues /*
1060d00947d8SCraig Rodrigues  * Copy from lvp to uvp.
1061d00947d8SCraig Rodrigues  *
1062d00947d8SCraig Rodrigues  * lvp and uvp should be locked and opened on entry and will be locked and
1063d00947d8SCraig Rodrigues  * opened on return.
1064d00947d8SCraig Rodrigues  */
1065d00947d8SCraig Rodrigues static int
1066d00947d8SCraig Rodrigues unionfs_copyfile_core(struct vnode *lvp, struct vnode *uvp,
1067d00947d8SCraig Rodrigues     struct ucred *cred, struct thread *td)
1068d00947d8SCraig Rodrigues {
1069df8bae1dSRodney W. Grimes 	char           *buf;
1070df8bae1dSRodney W. Grimes 	struct uio	uio;
1071df8bae1dSRodney W. Grimes 	struct iovec	iov;
1072312d49efSJason A. Harmening 	off_t		offset;
1073312d49efSJason A. Harmening 	int		count;
1074312d49efSJason A. Harmening 	int		error;
1075312d49efSJason A. Harmening 	int		bufoffset;
1076df8bae1dSRodney W. Grimes 
1077d00947d8SCraig Rodrigues 	error = 0;
1078d00947d8SCraig Rodrigues 	memset(&uio, 0, sizeof(uio));
10792a31267eSMatthew Dillon 
1080b40ce416SJulian Elischer 	uio.uio_td = td;
1081df8bae1dSRodney W. Grimes 	uio.uio_segflg = UIO_SYSSPACE;
1082df8bae1dSRodney W. Grimes 	uio.uio_offset = 0;
1083df8bae1dSRodney W. Grimes 
1084a163d034SWarner Losh 	buf = malloc(MAXBSIZE, M_TEMP, M_WAITOK);
1085df8bae1dSRodney W. Grimes 
1086d00947d8SCraig Rodrigues 	while (error == 0) {
1087d00947d8SCraig Rodrigues 		offset = uio.uio_offset;
1088df8bae1dSRodney W. Grimes 
1089df8bae1dSRodney W. Grimes 		uio.uio_iov = &iov;
1090df8bae1dSRodney W. Grimes 		uio.uio_iovcnt = 1;
1091df8bae1dSRodney W. Grimes 		iov.iov_base = buf;
1092df8bae1dSRodney W. Grimes 		iov.iov_len = MAXBSIZE;
1093df8bae1dSRodney W. Grimes 		uio.uio_resid = iov.iov_len;
1094df8bae1dSRodney W. Grimes 		uio.uio_rw = UIO_READ;
1095df8bae1dSRodney W. Grimes 
1096d00947d8SCraig Rodrigues 		if ((error = VOP_READ(lvp, &uio, 0, cred)) != 0)
10972a31267eSMatthew Dillon 			break;
10982a31267eSMatthew Dillon 		if ((count = MAXBSIZE - uio.uio_resid) == 0)
10992a31267eSMatthew Dillon 			break;
11002a31267eSMatthew Dillon 
1101d00947d8SCraig Rodrigues 		bufoffset = 0;
11022a31267eSMatthew Dillon 		while (bufoffset < count) {
1103df8bae1dSRodney W. Grimes 			uio.uio_iov = &iov;
1104df8bae1dSRodney W. Grimes 			uio.uio_iovcnt = 1;
11052a31267eSMatthew Dillon 			iov.iov_base = buf + bufoffset;
11062a31267eSMatthew Dillon 			iov.iov_len = count - bufoffset;
11072a31267eSMatthew Dillon 			uio.uio_offset = offset + bufoffset;
1108df8bae1dSRodney W. Grimes 			uio.uio_resid = iov.iov_len;
1109d00947d8SCraig Rodrigues 			uio.uio_rw = UIO_WRITE;
1110df8bae1dSRodney W. Grimes 
1111d00947d8SCraig Rodrigues 			if ((error = VOP_WRITE(uvp, &uio, 0, cred)) != 0)
1112df8bae1dSRodney W. Grimes 				break;
1113d00947d8SCraig Rodrigues 
11142a31267eSMatthew Dillon 			bufoffset += (count - bufoffset) - uio.uio_resid;
1115df8bae1dSRodney W. Grimes 		}
1116d00947d8SCraig Rodrigues 
11172a31267eSMatthew Dillon 		uio.uio_offset = offset + bufoffset;
1118d00947d8SCraig Rodrigues 	}
1119df8bae1dSRodney W. Grimes 
1120df8bae1dSRodney W. Grimes 	free(buf, M_TEMP);
1121d00947d8SCraig Rodrigues 
1122df8bae1dSRodney W. Grimes 	return (error);
1123df8bae1dSRodney W. Grimes }
1124df8bae1dSRodney W. Grimes 
1125df8bae1dSRodney W. Grimes /*
1126d00947d8SCraig Rodrigues  * Copy file from lower to upper.
11272a31267eSMatthew Dillon  *
1128d00947d8SCraig Rodrigues  * If you need copy of the contents, set 1 to docopy. Otherwise, set 0 to
1129d00947d8SCraig Rodrigues  * docopy.
1130d00947d8SCraig Rodrigues  *
1131d00947d8SCraig Rodrigues  * If no error returned, unp will be updated.
1132996c772fSJohn Dyson  */
1133996c772fSJohn Dyson int
1134d00947d8SCraig Rodrigues unionfs_copyfile(struct unionfs_node *unp, int docopy, struct ucred *cred,
1135d00947d8SCraig Rodrigues     struct thread *td)
1136996c772fSJohn Dyson {
1137f2a2857bSKirk McKusick 	struct mount   *mp;
1138d00947d8SCraig Rodrigues 	struct vnode   *udvp;
1139d00947d8SCraig Rodrigues 	struct vnode   *lvp;
1140d00947d8SCraig Rodrigues 	struct vnode   *uvp;
1141d00947d8SCraig Rodrigues 	struct vattr	uva;
1142312d49efSJason A. Harmening 	int		error;
1143996c772fSJohn Dyson 
1144d00947d8SCraig Rodrigues 	lvp = unp->un_lowervp;
1145d00947d8SCraig Rodrigues 	uvp = NULLVP;
1146d00947d8SCraig Rodrigues 
1147d00947d8SCraig Rodrigues 	if ((UNIONFSTOV(unp)->v_mount->mnt_flag & MNT_RDONLY))
1148d00947d8SCraig Rodrigues 		return (EROFS);
1149d00947d8SCraig Rodrigues 	if (unp->un_dvp == NULLVP)
1150d00947d8SCraig Rodrigues 		return (EINVAL);
1151d00947d8SCraig Rodrigues 	if (unp->un_uppervp != NULLVP)
1152d00947d8SCraig Rodrigues 		return (EEXIST);
1153d00947d8SCraig Rodrigues 	udvp = VTOUNIONFS(unp->un_dvp)->un_uppervp;
1154d00947d8SCraig Rodrigues 	if (udvp == NULLVP)
1155d00947d8SCraig Rodrigues 		return (EROFS);
1156d00947d8SCraig Rodrigues 	if ((udvp->v_mount->mnt_flag & MNT_RDONLY))
1157d00947d8SCraig Rodrigues 		return (EROFS);
1158d00947d8SCraig Rodrigues 
1159d00947d8SCraig Rodrigues 	error = VOP_ACCESS(lvp, VREAD, cred, td);
1160d00947d8SCraig Rodrigues 	if (error != 0)
11615842d4e5SKATO Takenori 		return (error);
11625842d4e5SKATO Takenori 
1163*a75d1dddSMateusz Guzik 	if ((error = vn_start_write(udvp, &mp, V_WAIT | V_PCATCH)) != 0)
1164996c772fSJohn Dyson 		return (error);
1165d00947d8SCraig Rodrigues 	error = unionfs_vn_create_on_upper(&uvp, udvp, unp, &uva, td);
1166d00947d8SCraig Rodrigues 	if (error != 0) {
1167f2a2857bSKirk McKusick 		vn_finished_write(mp);
1168f2a2857bSKirk McKusick 		return (error);
1169f2a2857bSKirk McKusick 	}
1170996c772fSJohn Dyson 
1171d00947d8SCraig Rodrigues 	if (docopy != 0) {
11729e223287SKonstantin Belousov 		error = VOP_OPEN(lvp, FREAD, cred, td, NULL);
1173996c772fSJohn Dyson 		if (error == 0) {
1174d00947d8SCraig Rodrigues 			error = unionfs_copyfile_core(lvp, uvp, cred, td);
1175d00947d8SCraig Rodrigues 			VOP_CLOSE(lvp, FREAD, cred, td);
1176996c772fSJohn Dyson 		}
1177d00947d8SCraig Rodrigues 	}
1178d00947d8SCraig Rodrigues 	VOP_CLOSE(uvp, FWRITE, cred, td);
117978022527SKonstantin Belousov 	VOP_ADD_WRITECOUNT_CHECKED(uvp, -1);
1180312d49efSJason A. Harmening 	CTR3(KTR_VFS, "%s: vp %p v_writecount decreased to %d",
1181312d49efSJason A. Harmening 	    __func__, uvp, uvp->v_writecount);
1182996c772fSJohn Dyson 
1183f2a2857bSKirk McKusick 	vn_finished_write(mp);
1184d00947d8SCraig Rodrigues 
1185996c772fSJohn Dyson 	if (error == 0) {
1186d00947d8SCraig Rodrigues 		/* Reset the attributes. Ignore errors. */
1187d00947d8SCraig Rodrigues 		uva.va_type = VNON;
11880359a12eSAttilio Rao 		VOP_SETATTR(uvp, &uva, cred);
1189996c772fSJohn Dyson 	}
1190996c772fSJohn Dyson 
1191d00947d8SCraig Rodrigues 	unionfs_node_update(unp, uvp, td);
1192996c772fSJohn Dyson 
11932a31267eSMatthew Dillon 	return (error);
1194b422956cSPoul-Henning Kamp }
1195996c772fSJohn Dyson 
11962a31267eSMatthew Dillon /*
1197d00947d8SCraig Rodrigues  * It checks whether vp can rmdir. (check empty)
11982a31267eSMatthew Dillon  *
1199d00947d8SCraig Rodrigues  * vp is unionfs vnode.
1200d00947d8SCraig Rodrigues  * vp should be locked.
1201df8bae1dSRodney W. Grimes  */
1202df8bae1dSRodney W. Grimes int
1203d00947d8SCraig Rodrigues unionfs_check_rmdir(struct vnode *vp, struct ucred *cred, struct thread *td)
1204df8bae1dSRodney W. Grimes {
1205d00947d8SCraig Rodrigues 	struct vnode   *uvp;
1206d00947d8SCraig Rodrigues 	struct vnode   *lvp;
1207d00947d8SCraig Rodrigues 	struct vnode   *tvp;
1208312d49efSJason A. Harmening 	struct dirent  *dp;
1209312d49efSJason A. Harmening 	struct dirent  *edp;
1210df8bae1dSRodney W. Grimes 	struct componentname cn;
1211312d49efSJason A. Harmening 	struct iovec	iov;
1212312d49efSJason A. Harmening 	struct uio	uio;
1213312d49efSJason A. Harmening 	struct vattr	va;
1214312d49efSJason A. Harmening 	int		error;
1215312d49efSJason A. Harmening 	int		eofflag;
1216312d49efSJason A. Harmening 	int		lookuperr;
1217312d49efSJason A. Harmening 
1218df8bae1dSRodney W. Grimes 	/*
1219d00947d8SCraig Rodrigues 	 * The size of buf needs to be larger than DIRBLKSIZ.
1220df8bae1dSRodney W. Grimes 	 */
1221d00947d8SCraig Rodrigues 	char		buf[256 * 6];
1222df8bae1dSRodney W. Grimes 
12233ecefc4aSJason A. Harmening 	ASSERT_VOP_ELOCKED(vp, __func__);
1224df8bae1dSRodney W. Grimes 
1225d00947d8SCraig Rodrigues 	eofflag = 0;
1226d00947d8SCraig Rodrigues 	uvp = UNIONFSVPTOUPPERVP(vp);
1227d00947d8SCraig Rodrigues 	lvp = UNIONFSVPTOLOWERVP(vp);
1228df8bae1dSRodney W. Grimes 
1229d00947d8SCraig Rodrigues 	/* check opaque */
12300359a12eSAttilio Rao 	if ((error = VOP_GETATTR(uvp, &va, cred)) != 0)
1231df8bae1dSRodney W. Grimes 		return (error);
1232d00947d8SCraig Rodrigues 	if (va.va_flags & OPAQUE)
1233d00947d8SCraig Rodrigues 		return (0);
1234df8bae1dSRodney W. Grimes 
1235d00947d8SCraig Rodrigues 	/* open vnode */
12363282e2c4SDaichi GOTO #ifdef MAC
123730d239bcSRobert Watson 	if ((error = mac_vnode_check_open(cred, vp, VEXEC|VREAD)) != 0)
12383282e2c4SDaichi GOTO 		return (error);
12393282e2c4SDaichi GOTO #endif
12403282e2c4SDaichi GOTO 	if ((error = VOP_ACCESS(vp, VEXEC|VREAD, cred, td)) != 0)
12413282e2c4SDaichi GOTO 		return (error);
12429e223287SKonstantin Belousov 	if ((error = VOP_OPEN(vp, FREAD, cred, td, NULL)) != 0)
1243996c772fSJohn Dyson 		return (error);
1244996c772fSJohn Dyson 
1245d00947d8SCraig Rodrigues 	uio.uio_rw = UIO_READ;
1246d00947d8SCraig Rodrigues 	uio.uio_segflg = UIO_SYSSPACE;
1247d00947d8SCraig Rodrigues 	uio.uio_td = td;
1248d00947d8SCraig Rodrigues 	uio.uio_offset = 0;
1249996c772fSJohn Dyson 
1250d00947d8SCraig Rodrigues #ifdef MAC
125130d239bcSRobert Watson 	error = mac_vnode_check_readdir(td->td_ucred, lvp);
1252d00947d8SCraig Rodrigues #endif
1253d00947d8SCraig Rodrigues 	while (!error && !eofflag) {
1254d00947d8SCraig Rodrigues 		iov.iov_base = buf;
1255d00947d8SCraig Rodrigues 		iov.iov_len = sizeof(buf);
1256d00947d8SCraig Rodrigues 		uio.uio_iov = &iov;
1257d00947d8SCraig Rodrigues 		uio.uio_iovcnt = 1;
1258d00947d8SCraig Rodrigues 		uio.uio_resid = iov.iov_len;
1259996c772fSJohn Dyson 
1260d00947d8SCraig Rodrigues 		error = VOP_READDIR(lvp, &uio, cred, &eofflag, NULL, NULL);
1261a68ae31cSDaichi GOTO 		if (error != 0)
1262d00947d8SCraig Rodrigues 			break;
1263fb273fe7SJason A. Harmening 		KASSERT(eofflag != 0 || uio.uio_resid < sizeof(buf),
1264fb273fe7SJason A. Harmening 		    ("%s: empty read from lower FS", __func__));
1265996c772fSJohn Dyson 
1266d00947d8SCraig Rodrigues 		edp = (struct dirent*)&buf[sizeof(buf) - uio.uio_resid];
1267d00947d8SCraig Rodrigues 		for (dp = (struct dirent*)buf; !error && dp < edp;
1268d00947d8SCraig Rodrigues 		     dp = (struct dirent*)((caddr_t)dp + dp->d_reclen)) {
1269ac13a90cSGleb Kurtsou 			if (dp->d_type == DT_WHT || dp->d_fileno == 0 ||
1270d00947d8SCraig Rodrigues 			    (dp->d_namlen == 1 && dp->d_name[0] == '.') ||
1271d00947d8SCraig Rodrigues 			    (dp->d_namlen == 2 && !bcmp(dp->d_name, "..", 2)))
1272d00947d8SCraig Rodrigues 				continue;
1273df8bae1dSRodney W. Grimes 
1274d00947d8SCraig Rodrigues 			cn.cn_namelen = dp->d_namlen;
1275d00947d8SCraig Rodrigues 			cn.cn_pnbuf = NULL;
1276d00947d8SCraig Rodrigues 			cn.cn_nameptr = dp->d_name;
1277d00947d8SCraig Rodrigues 			cn.cn_nameiop = LOOKUP;
12785b5b7e2cSMateusz Guzik 			cn.cn_flags = LOCKPARENT | LOCKLEAF | RDONLY | ISLASTCN;
1279d00947d8SCraig Rodrigues 			cn.cn_lkflags = LK_EXCLUSIVE;
1280d00947d8SCraig Rodrigues 			cn.cn_cred = cred;
1281df8bae1dSRodney W. Grimes 
12822a31267eSMatthew Dillon 			/*
1283d00947d8SCraig Rodrigues 			 * check entry in lower.
1284d00947d8SCraig Rodrigues 			 * Sometimes, readdir function returns
1285d00947d8SCraig Rodrigues 			 * wrong entry.
12862a31267eSMatthew Dillon 			 */
1287d00947d8SCraig Rodrigues 			lookuperr = VOP_LOOKUP(lvp, &tvp, &cn);
1288df8bae1dSRodney W. Grimes 
1289d00947d8SCraig Rodrigues 			if (!lookuperr)
1290d00947d8SCraig Rodrigues 				vput(tvp);
12912a31267eSMatthew Dillon 			else
1292d00947d8SCraig Rodrigues 				continue; /* skip entry */
1293df8bae1dSRodney W. Grimes 
1294df8bae1dSRodney W. Grimes 			/*
1295d00947d8SCraig Rodrigues 			 * check entry
1296d00947d8SCraig Rodrigues 			 * If it has no exist/whiteout entry in upper,
1297d00947d8SCraig Rodrigues 			 * directory is not empty.
1298df8bae1dSRodney W. Grimes 			 */
12995b5b7e2cSMateusz Guzik 			cn.cn_flags = LOCKPARENT | LOCKLEAF | RDONLY | ISLASTCN;
1300d00947d8SCraig Rodrigues 			lookuperr = VOP_LOOKUP(uvp, &tvp, &cn);
1301df8bae1dSRodney W. Grimes 
1302d00947d8SCraig Rodrigues 			if (!lookuperr)
1303d00947d8SCraig Rodrigues 				vput(tvp);
1304df8bae1dSRodney W. Grimes 
1305d00947d8SCraig Rodrigues 			/* ignore exist or whiteout entry */
1306d00947d8SCraig Rodrigues 			if (!lookuperr ||
1307d00947d8SCraig Rodrigues 			    (lookuperr == ENOENT && (cn.cn_flags & ISWHITEOUT)))
130801634480SBrian Feldman 				continue;
1309d00947d8SCraig Rodrigues 
1310d00947d8SCraig Rodrigues 			error = ENOTEMPTY;
1311df8bae1dSRodney W. Grimes 		}
1312996c772fSJohn Dyson 	}
1313996c772fSJohn Dyson 
1314d00947d8SCraig Rodrigues 	/* close vnode */
1315d00947d8SCraig Rodrigues 	VOP_CLOSE(vp, FREAD, cred, td);
1316d00947d8SCraig Rodrigues 
1317d00947d8SCraig Rodrigues 	return (error);
1318d00947d8SCraig Rodrigues }
1319d00947d8SCraig Rodrigues 
1320