xref: /freebsd/sys/fs/unionfs/union_vnops.c (revision 2ed053cde55869d3440377d479deb00f42ba1cf8)
1d167cf6fSWarner Losh /*-
251369649SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
351369649SPedro F. Giffuni  *
4996c772fSJohn Dyson  * Copyright (c) 1992, 1993, 1994, 1995 Jan-Simon Pendry.
5996c772fSJohn Dyson  * Copyright (c) 1992, 1993, 1994, 1995
6d00947d8SCraig Rodrigues  *      The Regents of the University of California.
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>
9d00947d8SCraig Rodrigues  * All rights reserved.
10df8bae1dSRodney W. Grimes  *
11df8bae1dSRodney W. Grimes  * This code is derived from software contributed to Berkeley by
12df8bae1dSRodney W. Grimes  * Jan-Simon Pendry.
13df8bae1dSRodney W. Grimes  *
14df8bae1dSRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
15df8bae1dSRodney W. Grimes  * modification, are permitted provided that the following conditions
16df8bae1dSRodney W. Grimes  * are met:
17df8bae1dSRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
18df8bae1dSRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
19df8bae1dSRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
20df8bae1dSRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
21df8bae1dSRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
22fbbd9655SWarner Losh  * 3. Neither the name of the University nor the names of its contributors
23df8bae1dSRodney W. Grimes  *    may be used to endorse or promote products derived from this software
24df8bae1dSRodney W. Grimes  *    without specific prior written permission.
25df8bae1dSRodney W. Grimes  *
26df8bae1dSRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27df8bae1dSRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28df8bae1dSRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29df8bae1dSRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30df8bae1dSRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31df8bae1dSRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32df8bae1dSRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33df8bae1dSRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34df8bae1dSRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35df8bae1dSRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36df8bae1dSRodney W. Grimes  * SUCH DAMAGE.
37df8bae1dSRodney W. Grimes  *
38df8bae1dSRodney W. Grimes  */
39df8bae1dSRodney W. Grimes 
40df8bae1dSRodney W. Grimes #include <sys/param.h>
41df8bae1dSRodney W. Grimes #include <sys/systm.h>
42d00947d8SCraig Rodrigues #include <sys/conf.h>
43d00947d8SCraig Rodrigues #include <sys/kernel.h>
44d00947d8SCraig Rodrigues #include <sys/lock.h>
45d00947d8SCraig Rodrigues #include <sys/malloc.h>
46d00947d8SCraig Rodrigues #include <sys/mount.h>
47d00947d8SCraig Rodrigues #include <sys/mutex.h>
48d00947d8SCraig Rodrigues #include <sys/namei.h>
49d00947d8SCraig Rodrigues #include <sys/sysctl.h>
50d00947d8SCraig Rodrigues #include <sys/vnode.h>
51d00947d8SCraig Rodrigues #include <sys/kdb.h>
523ac4d1efSBruce Evans #include <sys/fcntl.h>
53996c772fSJohn Dyson #include <sys/stat.h>
54d00947d8SCraig Rodrigues #include <sys/dirent.h>
55d00947d8SCraig Rodrigues #include <sys/proc.h>
563d253c11SCraig Rodrigues #include <sys/bio.h>
573d253c11SCraig Rodrigues #include <sys/buf.h>
58d00947d8SCraig Rodrigues 
5999d300a1SRuslan Ermilov #include <fs/unionfs/union.h>
60df8bae1dSRodney W. Grimes 
61d877dd57SJason A. Harmening #include <machine/atomic.h>
62d877dd57SJason A. Harmening 
632a31267eSMatthew Dillon #include <vm/vm.h>
64d00947d8SCraig Rodrigues #include <vm/vm_extern.h>
65d00947d8SCraig Rodrigues #include <vm/vm_object.h>
662a31267eSMatthew Dillon #include <vm/vnode_pager.h>
672a31267eSMatthew Dillon 
68d00947d8SCraig Rodrigues #if 0
69d00947d8SCraig Rodrigues #define UNIONFS_INTERNAL_DEBUG(msg, args...)    printf(msg, ## args)
70d00947d8SCraig Rodrigues #define UNIONFS_IDBG_RENAME
712a31267eSMatthew Dillon #else
72d00947d8SCraig Rodrigues #define UNIONFS_INTERNAL_DEBUG(msg, args...)
732a31267eSMatthew Dillon #endif
74df8bae1dSRodney W. Grimes 
751e5da15aSDaichi GOTO #define KASSERT_UNIONFS_VNODE(vp) \
760cd8f3e9SJason A. Harmening 	VNASSERT(((vp)->v_op == &unionfs_vnodeops), vp, \
770cd8f3e9SJason A. Harmening 	    ("%s: non-unionfs vnode", __func__))
781e5da15aSDaichi GOTO 
79df8bae1dSRodney W. Grimes static int
unionfs_lookup(struct vop_cachedlookup_args * ap)80dc2dd185SDaichi GOTO unionfs_lookup(struct vop_cachedlookup_args *ap)
81df8bae1dSRodney W. Grimes {
82b18029bcSJason A. Harmening 	struct unionfs_node *dunp, *unp;
83d00947d8SCraig Rodrigues 	struct vnode   *dvp, *udvp, *ldvp, *vp, *uvp, *lvp, *dtmpvp;
84d00947d8SCraig Rodrigues 	struct vattr	va;
85d00947d8SCraig Rodrigues 	struct componentname *cnp;
86d00947d8SCraig Rodrigues 	struct thread  *td;
87312d49efSJason A. Harmening 	u_long		nameiop;
88eb60ff1eSJason A. Harmening 	u_long		cnflags;
89312d49efSJason A. Harmening 	int		lockflag;
90eb60ff1eSJason A. Harmening 	int		lkflags;
91312d49efSJason A. Harmening 	int		error, uerror, lerror;
92d00947d8SCraig Rodrigues 
93d00947d8SCraig Rodrigues 	lockflag = 0;
94d00947d8SCraig Rodrigues 	error = uerror = lerror = ENOENT;
95d00947d8SCraig Rodrigues 	cnp = ap->a_cnp;
96d00947d8SCraig Rodrigues 	nameiop = cnp->cn_nameiop;
97d00947d8SCraig Rodrigues 	cnflags = cnp->cn_flags;
98d00947d8SCraig Rodrigues 	dvp = ap->a_dvp;
99d00947d8SCraig Rodrigues 	dunp = VTOUNIONFS(dvp);
100d00947d8SCraig Rodrigues 	udvp = dunp->un_uppervp;
101d00947d8SCraig Rodrigues 	ldvp = dunp->un_lowervp;
102d00947d8SCraig Rodrigues 	vp = uvp = lvp = NULLVP;
103d00947d8SCraig Rodrigues 	td = curthread;
104d00947d8SCraig Rodrigues 	*(ap->a_vpp) = NULLVP;
105d00947d8SCraig Rodrigues 
106312d49efSJason A. Harmening 	UNIONFS_INTERNAL_DEBUG(
107312d49efSJason A. Harmening 	    "unionfs_lookup: enter: nameiop=%ld, flags=%lx, path=%s\n",
108312d49efSJason A. Harmening 	    nameiop, cnflags, cnp->cn_nameptr);
109d00947d8SCraig Rodrigues 
110d00947d8SCraig Rodrigues 	if (dvp->v_type != VDIR)
111d00947d8SCraig Rodrigues 		return (ENOTDIR);
112df8bae1dSRodney W. Grimes 
113df8bae1dSRodney W. Grimes 	/*
114d00947d8SCraig Rodrigues 	 * If read-only and op is not LOOKUP, will return EROFS.
115df8bae1dSRodney W. Grimes 	 */
116d00947d8SCraig Rodrigues 	if ((cnflags & ISLASTCN) &&
117d00947d8SCraig Rodrigues 	    (dvp->v_mount->mnt_flag & MNT_RDONLY) &&
118d00947d8SCraig Rodrigues 	    LOOKUP != nameiop)
119d00947d8SCraig Rodrigues 		return (EROFS);
120d00947d8SCraig Rodrigues 
121df8bae1dSRodney W. Grimes 	/*
122eb60ff1eSJason A. Harmening 	 * Note that a lookup is in-flight, and block if another lookup
123eb60ff1eSJason A. Harmening 	 * is already in-flight against dvp.  This is done because we may
124eb60ff1eSJason A. Harmening 	 * end up dropping dvp's lock to look up a lower vnode or to create
125eb60ff1eSJason A. Harmening 	 * a shadow directory, opening up the possibility of parallel lookups
126eb60ff1eSJason A. Harmening 	 * against the same directory creating duplicate unionfs vnodes for
127eb60ff1eSJason A. Harmening 	 * the same file(s).  Note that if this function encounters an
128eb60ff1eSJason A. Harmening 	 * in-progress lookup for the directory, it will block until the
129eb60ff1eSJason A. Harmening 	 * lookup is complete and then return ERELOOKUP to allow any
130eb60ff1eSJason A. Harmening 	 * existing unionfs vnode to be loaded from the VFS cache.
131eb60ff1eSJason A. Harmening 	 * This is really a hack; filesystems that support MNTK_LOOKUP_SHARED
132eb60ff1eSJason A. Harmening 	 * (which unionfs currently doesn't) seem to deal with this by using
133eb60ff1eSJason A. Harmening 	 * the vfs_hash_* functions to manage a per-mount vnode cache keyed
134eb60ff1eSJason A. Harmening 	 * by the inode number (or some roughly equivalent unique ID
135eb60ff1eSJason A. Harmening 	 * usually assocated with the storage medium).  It may make sense
136eb60ff1eSJason A. Harmening 	 * for unionfs to adopt something similar as a replacement for its
137eb60ff1eSJason A. Harmening 	 * current half-baked directory-only cache implementation, particularly
138eb60ff1eSJason A. Harmening 	 * if we want to support MNTK_LOOKUP_SHARED here.
139eb60ff1eSJason A. Harmening 	 */
140eb60ff1eSJason A. Harmening 	error = unionfs_set_in_progress_flag(dvp, UNIONFS_LOOKUP_IN_PROGRESS);
141eb60ff1eSJason A. Harmening 	if (error != 0)
142eb60ff1eSJason A. Harmening 		return (error);
143eb60ff1eSJason A. Harmening 	/*
144d00947d8SCraig Rodrigues 	 * lookup dotdot
145df8bae1dSRodney W. Grimes 	 */
146d00947d8SCraig Rodrigues 	if (cnflags & ISDOTDOT) {
147eb60ff1eSJason A. Harmening 		if (LOOKUP != nameiop && udvp == NULLVP) {
148eb60ff1eSJason A. Harmening 			error = EROFS;
149eb60ff1eSJason A. Harmening 			goto unionfs_lookup_return;
150d00947d8SCraig Rodrigues 		}
151eb60ff1eSJason A. Harmening 
152eb60ff1eSJason A. Harmening 		if (udvp != NULLVP)
153eb60ff1eSJason A. Harmening 			dtmpvp = udvp;
154d00947d8SCraig Rodrigues 		else
155d00947d8SCraig Rodrigues 			dtmpvp = ldvp;
156d00947d8SCraig Rodrigues 
157eb60ff1eSJason A. Harmening 		unionfs_forward_vop_start(dtmpvp, &lkflags);
158d00947d8SCraig Rodrigues 		error = VOP_LOOKUP(dtmpvp, &vp, cnp);
159eb60ff1eSJason A. Harmening 		unionfs_forward_vop_finish(dvp, dtmpvp, lkflags);
160d00947d8SCraig Rodrigues 
161eb60ff1eSJason A. Harmening 		/*
162eb60ff1eSJason A. Harmening 		 * Drop the lock and reference on vp.  If the lookup was
163eb60ff1eSJason A. Harmening 		 * successful, we'll either need to exchange vp's lock and
164eb60ff1eSJason A. Harmening 		 * reference for the unionfs parent vnode's lock and
165eb60ff1eSJason A. Harmening 		 * reference, or (if dvp was reclaimed) we'll need to drop
166eb60ff1eSJason A. Harmening 		 * vp's lock and reference to return early.
167eb60ff1eSJason A. Harmening 		 */
168eb60ff1eSJason A. Harmening 		if (vp != NULLVP)
169eb60ff1eSJason A. Harmening 			vput(vp);
170b18029bcSJason A. Harmening 		dunp = VTOUNIONFS(dvp);
171b18029bcSJason A. Harmening 		if (error == 0 && dunp == NULL)
172b18029bcSJason A. Harmening 			error = ENOENT;
173df8bae1dSRodney W. Grimes 
1742a31267eSMatthew Dillon 		if (error == 0) {
175b18029bcSJason A. Harmening 			dtmpvp = dunp->un_dvp;
176b18029bcSJason A. Harmening 			vref(dtmpvp);
177b249ce48SMateusz Guzik 			VOP_UNLOCK(dvp);
178b18029bcSJason A. Harmening 			*(ap->a_vpp) = dtmpvp;
179d00947d8SCraig Rodrigues 
180eb60ff1eSJason A. Harmening 			vn_lock(dtmpvp, cnp->cn_lkflags | LK_RETRY);
181d00947d8SCraig Rodrigues 
182eb60ff1eSJason A. Harmening 			if (VN_IS_DOOMED(dtmpvp)) {
183eb60ff1eSJason A. Harmening 				vput(dtmpvp);
184eb60ff1eSJason A. Harmening 				*(ap->a_vpp) = NULLVP;
185eb60ff1eSJason A. Harmening 				error = ENOENT;
186eb60ff1eSJason A. Harmening 			}
187cb05b60aSAttilio Rao 			vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
1882a31267eSMatthew Dillon 		}
189d00947d8SCraig Rodrigues 
190eb60ff1eSJason A. Harmening 		goto unionfs_lookup_cleanup;
191eb60ff1eSJason A. Harmening 	}
192eb60ff1eSJason A. Harmening 
193eb60ff1eSJason A. Harmening 	/*
194eb60ff1eSJason A. Harmening 	 * Lookup lower layer.  We do this before looking up the the upper
195eb60ff1eSJason A. Harmening 	 * layer, as we may drop the upper parent directory's lock, and we
196eb60ff1eSJason A. Harmening 	 * want to ensure the upper parent remains locked from the point of
197eb60ff1eSJason A. Harmening 	 * lookup through any ensuing VOP that may require it to be locked.
198eb60ff1eSJason A. Harmening 	 * The cost of this is that we may end up performing an unnecessary
199eb60ff1eSJason A. Harmening 	 * lower layer lookup if a whiteout is present in the upper layer.
200eb60ff1eSJason A. Harmening 	 */
201eb60ff1eSJason A. Harmening 	if (ldvp != NULLVP && !(cnflags & DOWHITEOUT)) {
202eb60ff1eSJason A. Harmening 		struct componentname lcn;
203eb60ff1eSJason A. Harmening 		bool is_dot;
204eb60ff1eSJason A. Harmening 
205eb60ff1eSJason A. Harmening 		if (udvp != NULLVP) {
206eb60ff1eSJason A. Harmening 			vref(ldvp);
207eb60ff1eSJason A. Harmening 			VOP_UNLOCK(dvp);
208eb60ff1eSJason A. Harmening 			vn_lock(ldvp, LK_EXCLUSIVE | LK_RETRY);
209eb60ff1eSJason A. Harmening 		}
210eb60ff1eSJason A. Harmening 
211eb60ff1eSJason A. Harmening 		lcn = *cnp;
212eb60ff1eSJason A. Harmening 		/* always op is LOOKUP */
213eb60ff1eSJason A. Harmening 		lcn.cn_nameiop = LOOKUP;
214eb60ff1eSJason A. Harmening 		lcn.cn_flags = cnflags;
215eb60ff1eSJason A. Harmening 		is_dot = false;
216eb60ff1eSJason A. Harmening 
217eb60ff1eSJason A. Harmening 		if (udvp == NULLVP)
218eb60ff1eSJason A. Harmening 			unionfs_forward_vop_start(ldvp, &lkflags);
219eb60ff1eSJason A. Harmening 		lerror = VOP_LOOKUP(ldvp, &lvp, &lcn);
220eb60ff1eSJason A. Harmening 		if (udvp == NULLVP &&
221eb60ff1eSJason A. Harmening 		    unionfs_forward_vop_finish(dvp, ldvp, lkflags)) {
222eb60ff1eSJason A. Harmening 			if (lvp != NULLVP)
223eb60ff1eSJason A. Harmening 				VOP_UNLOCK(lvp);
224eb60ff1eSJason A. Harmening 			error =  ENOENT;
225eb60ff1eSJason A. Harmening 			goto unionfs_lookup_cleanup;
226eb60ff1eSJason A. Harmening 		}
227eb60ff1eSJason A. Harmening 
228eb60ff1eSJason A. Harmening 		if (udvp == NULLVP)
229eb60ff1eSJason A. Harmening 			cnp->cn_flags = lcn.cn_flags;
230eb60ff1eSJason A. Harmening 
231eb60ff1eSJason A. Harmening 		if (lerror == 0) {
232eb60ff1eSJason A. Harmening 			if (ldvp == lvp) {	/* is dot */
233eb60ff1eSJason A. Harmening 				vrele(lvp);
234eb60ff1eSJason A. Harmening 				*(ap->a_vpp) = dvp;
235eb60ff1eSJason A. Harmening 				vref(dvp);
236eb60ff1eSJason A. Harmening 				is_dot = true;
237eb60ff1eSJason A. Harmening 				error = lerror;
238eb60ff1eSJason A. Harmening 			} else if (lvp != NULLVP)
239eb60ff1eSJason A. Harmening 				VOP_UNLOCK(lvp);
240eb60ff1eSJason A. Harmening 		}
241eb60ff1eSJason A. Harmening 
242eb60ff1eSJason A. Harmening 		if (udvp != NULLVP) {
243eb60ff1eSJason A. Harmening 			vput(ldvp);
244eb60ff1eSJason A. Harmening 			vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
245eb60ff1eSJason A. Harmening 			if (VN_IS_DOOMED(dvp))
246eb60ff1eSJason A. Harmening 				error = ENOENT;
247eb60ff1eSJason A. Harmening 		}
248eb60ff1eSJason A. Harmening 		if (is_dot)
249eb60ff1eSJason A. Harmening 			goto unionfs_lookup_return;
250eb60ff1eSJason A. Harmening 		else if (error != 0)
251eb60ff1eSJason A. Harmening 			goto unionfs_lookup_cleanup;
252eb60ff1eSJason A. Harmening 	}
253d00947d8SCraig Rodrigues 	/*
254d00947d8SCraig Rodrigues 	 * lookup upper layer
255d00947d8SCraig Rodrigues 	 */
256d00947d8SCraig Rodrigues 	if (udvp != NULLVP) {
257eb60ff1eSJason A. Harmening 		bool iswhiteout = false;
258eb60ff1eSJason A. Harmening 
259eb60ff1eSJason A. Harmening 		unionfs_forward_vop_start(udvp, &lkflags);
260d00947d8SCraig Rodrigues 		uerror = VOP_LOOKUP(udvp, &uvp, cnp);
261eb60ff1eSJason A. Harmening 		if (unionfs_forward_vop_finish(dvp, udvp, lkflags)) {
262eb60ff1eSJason A. Harmening 			if (uvp != NULLVP)
263eb60ff1eSJason A. Harmening 				VOP_UNLOCK(uvp);
264eb60ff1eSJason A. Harmening 			error = ENOENT;
265eb60ff1eSJason A. Harmening 			goto unionfs_lookup_cleanup;
266eb60ff1eSJason A. Harmening 		}
267d00947d8SCraig Rodrigues 
268d00947d8SCraig Rodrigues 		if (uerror == 0) {
269d00947d8SCraig Rodrigues 			if (udvp == uvp) {	/* is dot */
270eb60ff1eSJason A. Harmening 				if (lvp != NULLVP)
271eb60ff1eSJason A. Harmening 					vrele(lvp);
272d00947d8SCraig Rodrigues 				vrele(uvp);
273d00947d8SCraig Rodrigues 				*(ap->a_vpp) = dvp;
274d00947d8SCraig Rodrigues 				vref(dvp);
275d00947d8SCraig Rodrigues 
276152c35eeSJason A. Harmening 				error = uerror;
277152c35eeSJason A. Harmening 				goto unionfs_lookup_return;
278eb60ff1eSJason A. Harmening 			} else if (uvp != NULLVP)
279b249ce48SMateusz Guzik 				VOP_UNLOCK(uvp);
280d00947d8SCraig Rodrigues 		}
281d00947d8SCraig Rodrigues 
282d00947d8SCraig Rodrigues 		/* check whiteout */
283eb60ff1eSJason A. Harmening 		if ((uerror == ENOENT || uerror == EJUSTRETURN) &&
284eb60ff1eSJason A. Harmening 		    (cnp->cn_flags & ISWHITEOUT))
285eb60ff1eSJason A. Harmening 			iswhiteout = true;
286eb60ff1eSJason A. Harmening 		else if (VOP_GETATTR(udvp, &va, cnp->cn_cred) == 0 &&
287d00947d8SCraig Rodrigues 		    (va.va_flags & OPAQUE))
288eb60ff1eSJason A. Harmening 			iswhiteout = true;
289eb60ff1eSJason A. Harmening 
290eb60ff1eSJason A. Harmening 		if (iswhiteout && lvp != NULLVP) {
291eb60ff1eSJason A. Harmening 			vrele(lvp);
292eb60ff1eSJason A. Harmening 			lvp = NULLVP;
293eb60ff1eSJason A. Harmening 		}
294eb60ff1eSJason A. Harmening 
295d00947d8SCraig Rodrigues #if 0
296312d49efSJason A. Harmening 		UNIONFS_INTERNAL_DEBUG(
297312d49efSJason A. Harmening 		    "unionfs_lookup: debug: whiteout=%d, path=%s\n",
298312d49efSJason A. Harmening 		    iswhiteout, cnp->cn_nameptr);
299d00947d8SCraig Rodrigues #endif
300d00947d8SCraig Rodrigues 	}
301d00947d8SCraig Rodrigues 
302d00947d8SCraig Rodrigues 	/*
303d00947d8SCraig Rodrigues 	 * check lookup result
304d00947d8SCraig Rodrigues 	 */
305d00947d8SCraig Rodrigues 	if (uvp == NULLVP && lvp == NULLVP) {
306152c35eeSJason A. Harmening 		error = (udvp != NULLVP ? uerror : lerror);
307152c35eeSJason A. Harmening 		goto unionfs_lookup_return;
308d00947d8SCraig Rodrigues 	}
309d00947d8SCraig Rodrigues 
310d00947d8SCraig Rodrigues 	/*
311d00947d8SCraig Rodrigues 	 * check vnode type
312d00947d8SCraig Rodrigues 	 */
313d00947d8SCraig Rodrigues 	if (uvp != NULLVP && lvp != NULLVP && uvp->v_type != lvp->v_type) {
314d00947d8SCraig Rodrigues 		vrele(lvp);
315d00947d8SCraig Rodrigues 		lvp = NULLVP;
316d00947d8SCraig Rodrigues 	}
317d00947d8SCraig Rodrigues 
318d00947d8SCraig Rodrigues 	/*
319d00947d8SCraig Rodrigues 	 * check shadow dir
320d00947d8SCraig Rodrigues 	 */
321d00947d8SCraig Rodrigues 	if (uerror != 0 && uerror != EJUSTRETURN && udvp != NULLVP &&
322d00947d8SCraig Rodrigues 	    lerror == 0 && lvp != NULLVP && lvp->v_type == VDIR &&
323d00947d8SCraig Rodrigues 	    !(dvp->v_mount->mnt_flag & MNT_RDONLY) &&
324d00947d8SCraig Rodrigues 	    (1 < cnp->cn_namelen || '.' != *(cnp->cn_nameptr))) {
325d00947d8SCraig Rodrigues 		/* get unionfs vnode in order to create a new shadow dir. */
326d00947d8SCraig Rodrigues 		error = unionfs_nodeget(dvp->v_mount, NULLVP, lvp, dvp, &vp,
3276d8420d4SJason A. Harmening 		    cnp);
328d00947d8SCraig Rodrigues 		if (error != 0)
329152c35eeSJason A. Harmening 			goto unionfs_lookup_cleanup;
330d00947d8SCraig Rodrigues 
331d00947d8SCraig Rodrigues 		if (LK_SHARED == (cnp->cn_lkflags & LK_TYPE_MASK))
332b249ce48SMateusz Guzik 			VOP_UNLOCK(vp);
33381c794f9SAttilio Rao 		if (LK_EXCLUSIVE != VOP_ISLOCKED(vp)) {
334cb05b60aSAttilio Rao 			vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
335d00947d8SCraig Rodrigues 			lockflag = 1;
336d00947d8SCraig Rodrigues 		}
337b18029bcSJason A. Harmening 		unp = VTOUNIONFS(vp);
338b18029bcSJason A. Harmening 		if (unp == NULL)
339b18029bcSJason A. Harmening 			error = ENOENT;
340b18029bcSJason A. Harmening 		else
341eb60ff1eSJason A. Harmening 			error = unionfs_mkshadowdir(dvp, vp, cnp, td);
342d00947d8SCraig Rodrigues 		if (lockflag != 0)
343b249ce48SMateusz Guzik 			VOP_UNLOCK(vp);
344d00947d8SCraig Rodrigues 		if (error != 0) {
345312d49efSJason A. Harmening 			UNIONFSDEBUG(
346312d49efSJason A. Harmening 			    "unionfs_lookup: Unable to create shadow dir.");
347d00947d8SCraig Rodrigues 			if ((cnp->cn_lkflags & LK_TYPE_MASK) == LK_EXCLUSIVE)
348d00947d8SCraig Rodrigues 				vput(vp);
349d00947d8SCraig Rodrigues 			else
350d00947d8SCraig Rodrigues 				vrele(vp);
351152c35eeSJason A. Harmening 			goto unionfs_lookup_cleanup;
352d00947d8SCraig Rodrigues 		}
353eb60ff1eSJason A. Harmening 		/*
354eb60ff1eSJason A. Harmening 		 * TODO: Since unionfs_mkshadowdir() relocks udvp after
355eb60ff1eSJason A. Harmening 		 * creating the new directory, return ERELOOKUP here?
356eb60ff1eSJason A. Harmening 		 */
357d00947d8SCraig Rodrigues 		if ((cnp->cn_lkflags & LK_TYPE_MASK) == LK_SHARED)
358cb05b60aSAttilio Rao 			vn_lock(vp, LK_SHARED | LK_RETRY);
359d00947d8SCraig Rodrigues 	}
360d00947d8SCraig Rodrigues 	/*
361d00947d8SCraig Rodrigues 	 * get unionfs vnode.
362d00947d8SCraig Rodrigues 	 */
363d00947d8SCraig Rodrigues 	else {
364d00947d8SCraig Rodrigues 		if (uvp != NULLVP)
365d00947d8SCraig Rodrigues 			error = uerror;
366d00947d8SCraig Rodrigues 		else
367d00947d8SCraig Rodrigues 			error = lerror;
368d00947d8SCraig Rodrigues 		if (error != 0)
369152c35eeSJason A. Harmening 			goto unionfs_lookup_cleanup;
3701e5da15aSDaichi GOTO 		error = unionfs_nodeget(dvp->v_mount, uvp, lvp,
3716d8420d4SJason A. Harmening 		    dvp, &vp, cnp);
372d00947d8SCraig Rodrigues 		if (error != 0) {
373312d49efSJason A. Harmening 			UNIONFSDEBUG(
374312d49efSJason A. Harmening 			    "unionfs_lookup: Unable to create unionfs vnode.");
375152c35eeSJason A. Harmening 			goto unionfs_lookup_cleanup;
376d00947d8SCraig Rodrigues 		}
377eb60ff1eSJason A. Harmening 	}
378eb60ff1eSJason A. Harmening 
379eb60ff1eSJason A. Harmening 	if (VN_IS_DOOMED(dvp) || VN_IS_DOOMED(vp)) {
380eb60ff1eSJason A. Harmening 		error = ENOENT;
381eb60ff1eSJason A. Harmening 		vput(vp);
382eb60ff1eSJason A. Harmening 		goto unionfs_lookup_cleanup;
383d00947d8SCraig Rodrigues 	}
384d00947d8SCraig Rodrigues 
385d00947d8SCraig Rodrigues 	*(ap->a_vpp) = vp;
386d00947d8SCraig Rodrigues 
387eee6217bSJason A. Harmening 	if (cnflags & MAKEENTRY)
388dc2dd185SDaichi GOTO 		cache_enter(dvp, vp, cnp);
389dc2dd185SDaichi GOTO 
390152c35eeSJason A. Harmening unionfs_lookup_cleanup:
391d00947d8SCraig Rodrigues 	if (uvp != NULLVP)
392d00947d8SCraig Rodrigues 		vrele(uvp);
393d00947d8SCraig Rodrigues 	if (lvp != NULLVP)
394d00947d8SCraig Rodrigues 		vrele(lvp);
395d00947d8SCraig Rodrigues 
396eb60ff1eSJason A. Harmening 	if (error == ENOENT && (cnflags & MAKEENTRY) != 0 &&
397eb60ff1eSJason A. Harmening 	    !VN_IS_DOOMED(dvp))
398dc2dd185SDaichi GOTO 		cache_enter(dvp, NULLVP, cnp);
399dc2dd185SDaichi GOTO 
400152c35eeSJason A. Harmening unionfs_lookup_return:
401eb60ff1eSJason A. Harmening 	unionfs_clear_in_progress_flag(dvp, UNIONFS_LOOKUP_IN_PROGRESS);
402152c35eeSJason A. Harmening 
403d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_lookup: leave (%d)\n", error);
404d00947d8SCraig Rodrigues 
4052a31267eSMatthew Dillon 	return (error);
406df8bae1dSRodney W. Grimes }
407df8bae1dSRodney W. Grimes 
408c9bf0111SKATO Takenori static int
unionfs_create(struct vop_create_args * ap)409d00947d8SCraig Rodrigues unionfs_create(struct vop_create_args *ap)
410996c772fSJohn Dyson {
411d00947d8SCraig Rodrigues 	struct unionfs_node *dunp;
412d00947d8SCraig Rodrigues 	struct componentname *cnp;
413d00947d8SCraig Rodrigues 	struct vnode   *udvp;
414d00947d8SCraig Rodrigues 	struct vnode   *vp;
4153bb3827fSDavid Schultz 	int		error;
416996c772fSJohn Dyson 
417d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_create: enter\n");
418d00947d8SCraig Rodrigues 
4191e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_dvp);
4201e5da15aSDaichi GOTO 
421d00947d8SCraig Rodrigues 	dunp = VTOUNIONFS(ap->a_dvp);
422d00947d8SCraig Rodrigues 	cnp = ap->a_cnp;
423d00947d8SCraig Rodrigues 	udvp = dunp->un_uppervp;
424d00947d8SCraig Rodrigues 	error = EROFS;
425d00947d8SCraig Rodrigues 
426d00947d8SCraig Rodrigues 	if (udvp != NULLVP) {
4276c8ded00SJason A. Harmening 		int lkflags;
4286c8ded00SJason A. Harmening 		bool vp_created = false;
4296c8ded00SJason A. Harmening 		unionfs_forward_vop_start(udvp, &lkflags);
4301e5da15aSDaichi GOTO 		error = VOP_CREATE(udvp, &vp, cnp, ap->a_vap);
4316c8ded00SJason A. Harmening 		if (error == 0)
4326c8ded00SJason A. Harmening 			vp_created = true;
4336c8ded00SJason A. Harmening 		if (__predict_false(unionfs_forward_vop_finish(ap->a_dvp, udvp,
4346c8ded00SJason A. Harmening 		    lkflags)) && error == 0) {
4356c8ded00SJason A. Harmening 			error = ENOENT;
4366c8ded00SJason A. Harmening 		}
437eee6217bSJason A. Harmening 		if (error == 0) {
438b249ce48SMateusz Guzik 			VOP_UNLOCK(vp);
439d00947d8SCraig Rodrigues 			error = unionfs_nodeget(ap->a_dvp->v_mount, vp, NULLVP,
4406d8420d4SJason A. Harmening 			    ap->a_dvp, ap->a_vpp, cnp);
441d00947d8SCraig Rodrigues 			vrele(vp);
4426c8ded00SJason A. Harmening 		} else if (vp_created)
4436c8ded00SJason A. Harmening 			vput(vp);
444d00947d8SCraig Rodrigues 	}
445d00947d8SCraig Rodrigues 
446d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_create: leave (%d)\n", error);
447d00947d8SCraig Rodrigues 
448d00947d8SCraig Rodrigues 	return (error);
449d00947d8SCraig Rodrigues }
450d00947d8SCraig Rodrigues 
451d00947d8SCraig Rodrigues static int
unionfs_whiteout(struct vop_whiteout_args * ap)452d00947d8SCraig Rodrigues unionfs_whiteout(struct vop_whiteout_args *ap)
453d00947d8SCraig Rodrigues {
454d00947d8SCraig Rodrigues 	struct unionfs_node *dunp;
455d00947d8SCraig Rodrigues 	struct componentname *cnp;
456d00947d8SCraig Rodrigues 	struct vnode   *udvp;
457d00947d8SCraig Rodrigues 	int		error;
458d00947d8SCraig Rodrigues 
459d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_whiteout: enter\n");
460d00947d8SCraig Rodrigues 
4611e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_dvp);
4621e5da15aSDaichi GOTO 
463d00947d8SCraig Rodrigues 	dunp = VTOUNIONFS(ap->a_dvp);
464d00947d8SCraig Rodrigues 	cnp = ap->a_cnp;
465d00947d8SCraig Rodrigues 	udvp = dunp->un_uppervp;
466d00947d8SCraig Rodrigues 	error = EOPNOTSUPP;
467d00947d8SCraig Rodrigues 
468d00947d8SCraig Rodrigues 	if (udvp != NULLVP) {
4696c8ded00SJason A. Harmening 		int lkflags;
470ac092fb3SDavid Schultz 		switch (ap->a_flags) {
471ac092fb3SDavid Schultz 		case CREATE:
472ac092fb3SDavid Schultz 		case DELETE:
4733bb3827fSDavid Schultz 		case LOOKUP:
4746c8ded00SJason A. Harmening 			unionfs_forward_vop_start(udvp, &lkflags);
475d00947d8SCraig Rodrigues 			error = VOP_WHITEOUT(udvp, cnp, ap->a_flags);
4766c8ded00SJason A. Harmening 			unionfs_forward_vop_finish(ap->a_dvp, udvp, lkflags);
477ac092fb3SDavid Schultz 			break;
478ac092fb3SDavid Schultz 		default:
479d00947d8SCraig Rodrigues 			error = EINVAL;
480d00947d8SCraig Rodrigues 			break;
481ac092fb3SDavid Schultz 		}
482d00947d8SCraig Rodrigues 	}
483d00947d8SCraig Rodrigues 
484d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_whiteout: leave (%d)\n", error);
485d00947d8SCraig Rodrigues 
4862a31267eSMatthew Dillon 	return (error);
4872a31267eSMatthew Dillon }
4882a31267eSMatthew Dillon 
489c9bf0111SKATO Takenori static int
unionfs_mknod(struct vop_mknod_args * ap)490d00947d8SCraig Rodrigues unionfs_mknod(struct vop_mknod_args *ap)
491df8bae1dSRodney W. Grimes {
492d00947d8SCraig Rodrigues 	struct unionfs_node *dunp;
493d00947d8SCraig Rodrigues 	struct componentname *cnp;
494d00947d8SCraig Rodrigues 	struct vnode   *udvp;
495df8bae1dSRodney W. Grimes 	struct vnode   *vp;
496d00947d8SCraig Rodrigues 	int		error;
497df8bae1dSRodney W. Grimes 
498d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_mknod: enter\n");
499d00947d8SCraig Rodrigues 
5001e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_dvp);
5011e5da15aSDaichi GOTO 
502d00947d8SCraig Rodrigues 	dunp = VTOUNIONFS(ap->a_dvp);
503d00947d8SCraig Rodrigues 	cnp = ap->a_cnp;
504d00947d8SCraig Rodrigues 	udvp = dunp->un_uppervp;
505d00947d8SCraig Rodrigues 	error = EROFS;
506d00947d8SCraig Rodrigues 
507d00947d8SCraig Rodrigues 	if (udvp != NULLVP) {
5086c8ded00SJason A. Harmening 		int lkflags;
5096c8ded00SJason A. Harmening 		bool vp_created = false;
5106c8ded00SJason A. Harmening 		unionfs_forward_vop_start(udvp, &lkflags);
5111e5da15aSDaichi GOTO 		error = VOP_MKNOD(udvp, &vp, cnp, ap->a_vap);
5126c8ded00SJason A. Harmening 		if (error == 0)
5136c8ded00SJason A. Harmening 			vp_created = true;
5146c8ded00SJason A. Harmening 		if (__predict_false(unionfs_forward_vop_finish(ap->a_dvp, udvp,
5156c8ded00SJason A. Harmening 		    lkflags)) && error == 0) {
5166c8ded00SJason A. Harmening 			error = ENOENT;
5176c8ded00SJason A. Harmening 		}
518eee6217bSJason A. Harmening 		if (error == 0) {
519b249ce48SMateusz Guzik 			VOP_UNLOCK(vp);
520d00947d8SCraig Rodrigues 			error = unionfs_nodeget(ap->a_dvp->v_mount, vp, NULLVP,
5216d8420d4SJason A. Harmening 			    ap->a_dvp, ap->a_vpp, cnp);
522d00947d8SCraig Rodrigues 			vrele(vp);
5236c8ded00SJason A. Harmening 		} else if (vp_created)
5246c8ded00SJason A. Harmening 			vput(vp);
525d00947d8SCraig Rodrigues 	}
526d00947d8SCraig Rodrigues 
527d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_mknod: leave (%d)\n", error);
528d00947d8SCraig Rodrigues 
529d00947d8SCraig Rodrigues 	return (error);
530d00947d8SCraig Rodrigues }
531d00947d8SCraig Rodrigues 
53239a2dc44SJason A. Harmening enum unionfs_lkupgrade {
53339a2dc44SJason A. Harmening 	UNIONFS_LKUPGRADE_SUCCESS, /* lock successfully upgraded */
53439a2dc44SJason A. Harmening 	UNIONFS_LKUPGRADE_ALREADY, /* lock already held exclusive */
53539a2dc44SJason A. Harmening 	UNIONFS_LKUPGRADE_DOOMED   /* lock was upgraded, but vnode reclaimed */
53639a2dc44SJason A. Harmening };
53739a2dc44SJason A. Harmening 
53839a2dc44SJason A. Harmening static inline enum unionfs_lkupgrade
unionfs_upgrade_lock(struct vnode * vp)53939a2dc44SJason A. Harmening unionfs_upgrade_lock(struct vnode *vp)
54039a2dc44SJason A. Harmening {
54139a2dc44SJason A. Harmening 	ASSERT_VOP_LOCKED(vp, __func__);
54239a2dc44SJason A. Harmening 
54339a2dc44SJason A. Harmening 	if (VOP_ISLOCKED(vp) == LK_EXCLUSIVE)
54439a2dc44SJason A. Harmening 		return (UNIONFS_LKUPGRADE_ALREADY);
54539a2dc44SJason A. Harmening 
54639a2dc44SJason A. Harmening 	if (vn_lock(vp, LK_UPGRADE) != 0) {
54739a2dc44SJason A. Harmening 		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
54839a2dc44SJason A. Harmening 		if (VN_IS_DOOMED(vp))
54939a2dc44SJason A. Harmening 			return (UNIONFS_LKUPGRADE_DOOMED);
55039a2dc44SJason A. Harmening 	}
55139a2dc44SJason A. Harmening 	return (UNIONFS_LKUPGRADE_SUCCESS);
55239a2dc44SJason A. Harmening }
55339a2dc44SJason A. Harmening 
55439a2dc44SJason A. Harmening static inline void
unionfs_downgrade_lock(struct vnode * vp,enum unionfs_lkupgrade status)55539a2dc44SJason A. Harmening unionfs_downgrade_lock(struct vnode *vp, enum unionfs_lkupgrade status)
55639a2dc44SJason A. Harmening {
55739a2dc44SJason A. Harmening 	if (status != UNIONFS_LKUPGRADE_ALREADY)
55839a2dc44SJason A. Harmening 		vn_lock(vp, LK_DOWNGRADE | LK_RETRY);
55939a2dc44SJason A. Harmening }
56039a2dc44SJason A. Harmening 
561eb60ff1eSJason A. Harmening /*
562eb60ff1eSJason A. Harmening  * Exchange the default (upper vnode) lock on a unionfs vnode for the lower
563eb60ff1eSJason A. Harmening  * vnode lock, in support of operations that require access to the lower vnode
564eb60ff1eSJason A. Harmening  * even when an upper vnode is present.  We don't use vn_lock_pair() to hold
565eb60ff1eSJason A. Harmening  * both vnodes at the same time, primarily because the caller may proceed
566eb60ff1eSJason A. Harmening  * to issue VOPs to the lower layer which re-lock or perform other operations
567eb60ff1eSJason A. Harmening  * which may not be safe in the presence of a locked vnode from another FS.
568eb60ff1eSJason A. Harmening  * Moreover, vn_lock_pair()'s deadlock resolution approach can introduce
569eb60ff1eSJason A. Harmening  * additional overhead that isn't necessary on these paths.
570eb60ff1eSJason A. Harmening  *
571eb60ff1eSJason A. Harmening  * vp must be a locked unionfs vnode; the lock state of this vnode is
572eb60ff1eSJason A. Harmening  * returned through *lkflags for later use in unionfs_unlock_lvp().
573eb60ff1eSJason A. Harmening  *
574eb60ff1eSJason A. Harmening  * Returns the locked lower vnode, or NULL if the lower vnode (and therefore
575eb60ff1eSJason A. Harmening  * also the unionfs vnode above it) has been doomed.
576eb60ff1eSJason A. Harmening  */
577eb60ff1eSJason A. Harmening static struct vnode *
unionfs_lock_lvp(struct vnode * vp,int * lkflags)578eb60ff1eSJason A. Harmening unionfs_lock_lvp(struct vnode *vp, int *lkflags)
579eb60ff1eSJason A. Harmening {
580eb60ff1eSJason A. Harmening 	struct unionfs_node *unp;
581eb60ff1eSJason A. Harmening 	struct vnode *lvp;
582eb60ff1eSJason A. Harmening 
583eb60ff1eSJason A. Harmening 	unp = VTOUNIONFS(vp);
584eb60ff1eSJason A. Harmening 	lvp = unp->un_lowervp;
585eb60ff1eSJason A. Harmening 	ASSERT_VOP_LOCKED(vp, __func__);
586eb60ff1eSJason A. Harmening 	ASSERT_VOP_UNLOCKED(lvp, __func__);
587eb60ff1eSJason A. Harmening 	*lkflags = VOP_ISLOCKED(vp);
588eb60ff1eSJason A. Harmening 	vref(lvp);
589eb60ff1eSJason A. Harmening 	VOP_UNLOCK(vp);
590eb60ff1eSJason A. Harmening 	vn_lock(lvp, *lkflags | LK_RETRY);
591eb60ff1eSJason A. Harmening 	if (VN_IS_DOOMED(lvp)) {
592eb60ff1eSJason A. Harmening 		vput(lvp);
593eb60ff1eSJason A. Harmening 		lvp = NULLVP;
594eb60ff1eSJason A. Harmening 		vn_lock(vp, *lkflags | LK_RETRY);
595eb60ff1eSJason A. Harmening 	}
596eb60ff1eSJason A. Harmening 	return (lvp);
597eb60ff1eSJason A. Harmening }
598eb60ff1eSJason A. Harmening 
599eb60ff1eSJason A. Harmening /*
600eb60ff1eSJason A. Harmening  * Undo a previous call to unionfs_lock_lvp(), restoring the default lock
601eb60ff1eSJason A. Harmening  * on the unionfs vnode.  This function reloads and returns the vnode
602eb60ff1eSJason A. Harmening  * private data for the unionfs vnode, which will be NULL if the unionfs
603eb60ff1eSJason A. Harmening  * vnode became doomed while its lock was dropped.  The caller must check
604eb60ff1eSJason A. Harmening  * for this case.
605eb60ff1eSJason A. Harmening  */
606eb60ff1eSJason A. Harmening static struct unionfs_node *
unionfs_unlock_lvp(struct vnode * vp,struct vnode * lvp,int lkflags)607eb60ff1eSJason A. Harmening unionfs_unlock_lvp(struct vnode *vp, struct vnode *lvp, int lkflags)
608eb60ff1eSJason A. Harmening {
609eb60ff1eSJason A. Harmening 	ASSERT_VOP_LOCKED(lvp, __func__);
610eb60ff1eSJason A. Harmening 	ASSERT_VOP_UNLOCKED(vp, __func__);
611eb60ff1eSJason A. Harmening 	vput(lvp);
612eb60ff1eSJason A. Harmening 	vn_lock(vp, lkflags | LK_RETRY);
613eb60ff1eSJason A. Harmening 	return (VTOUNIONFS(vp));
614eb60ff1eSJason A. Harmening }
615eb60ff1eSJason A. Harmening 
616d00947d8SCraig Rodrigues static int
unionfs_open(struct vop_open_args * ap)617d00947d8SCraig Rodrigues unionfs_open(struct vop_open_args *ap)
618d00947d8SCraig Rodrigues {
619d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
620d00947d8SCraig Rodrigues 	struct unionfs_node_status *unsp;
62139a2dc44SJason A. Harmening 	struct vnode   *vp;
622d00947d8SCraig Rodrigues 	struct vnode   *uvp;
623d00947d8SCraig Rodrigues 	struct vnode   *lvp;
624d00947d8SCraig Rodrigues 	struct vnode   *targetvp;
625d00947d8SCraig Rodrigues 	struct ucred   *cred;
626d00947d8SCraig Rodrigues 	struct thread  *td;
627312d49efSJason A. Harmening 	int		error;
628eb60ff1eSJason A. Harmening 	int		lkflags;
62939a2dc44SJason A. Harmening 	enum unionfs_lkupgrade lkstatus;
630eb60ff1eSJason A. Harmening 	bool		lock_lvp, open_lvp;
631d00947d8SCraig Rodrigues 
632d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_open: enter\n");
633d00947d8SCraig Rodrigues 
6341e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_vp);
6351e5da15aSDaichi GOTO 
636d00947d8SCraig Rodrigues 	error = 0;
63739a2dc44SJason A. Harmening 	vp = ap->a_vp;
638d00947d8SCraig Rodrigues 	targetvp = NULLVP;
639d00947d8SCraig Rodrigues 	cred = ap->a_cred;
640d00947d8SCraig Rodrigues 	td = ap->a_td;
641eb60ff1eSJason A. Harmening 	open_lvp = lock_lvp = false;
642d00947d8SCraig Rodrigues 
64339a2dc44SJason A. Harmening 	/*
64439a2dc44SJason A. Harmening 	 * The executable loader path may call this function with vp locked
64539a2dc44SJason A. Harmening 	 * shared.  If the vnode is reclaimed while upgrading, we can't safely
64639a2dc44SJason A. Harmening 	 * use unp or do anything else unionfs- specific.
64739a2dc44SJason A. Harmening 	 */
64839a2dc44SJason A. Harmening 	lkstatus = unionfs_upgrade_lock(vp);
64939a2dc44SJason A. Harmening 	if (lkstatus == UNIONFS_LKUPGRADE_DOOMED) {
65039a2dc44SJason A. Harmening 		error = ENOENT;
65139a2dc44SJason A. Harmening 		goto unionfs_open_cleanup;
65239a2dc44SJason A. Harmening 	}
65339a2dc44SJason A. Harmening 
65439a2dc44SJason A. Harmening 	unp = VTOUNIONFS(vp);
65539a2dc44SJason A. Harmening 	uvp = unp->un_uppervp;
65639a2dc44SJason A. Harmening 	lvp = unp->un_lowervp;
657d00947d8SCraig Rodrigues 	unionfs_get_node_status(unp, td, &unsp);
658d00947d8SCraig Rodrigues 
659d00947d8SCraig Rodrigues 	if (unsp->uns_lower_opencnt > 0 || unsp->uns_upper_opencnt > 0) {
660d00947d8SCraig Rodrigues 		/* vnode is already opend. */
661d00947d8SCraig Rodrigues 		if (unsp->uns_upper_opencnt > 0)
662d00947d8SCraig Rodrigues 			targetvp = uvp;
663d00947d8SCraig Rodrigues 		else
664d00947d8SCraig Rodrigues 			targetvp = lvp;
665d00947d8SCraig Rodrigues 
666d00947d8SCraig Rodrigues 		if (targetvp == lvp &&
667d00947d8SCraig Rodrigues 		    (ap->a_mode & FWRITE) && lvp->v_type == VREG)
668d00947d8SCraig Rodrigues 			targetvp = NULLVP;
669d00947d8SCraig Rodrigues 	}
670d00947d8SCraig Rodrigues 	if (targetvp == NULLVP) {
671d00947d8SCraig Rodrigues 		if (uvp == NULLVP) {
672d00947d8SCraig Rodrigues 			if ((ap->a_mode & FWRITE) && lvp->v_type == VREG) {
673eb60ff1eSJason A. Harmening 				error = unionfs_copyfile(vp,
674d00947d8SCraig Rodrigues 				    !(ap->a_mode & O_TRUNC), cred, td);
675eb60ff1eSJason A. Harmening 				if (error != 0) {
676eb60ff1eSJason A. Harmening 					unp = VTOUNIONFS(vp);
677d00947d8SCraig Rodrigues 					goto unionfs_open_abort;
678eb60ff1eSJason A. Harmening 				}
679d00947d8SCraig Rodrigues 				targetvp = uvp = unp->un_uppervp;
680d00947d8SCraig Rodrigues 			} else
681d00947d8SCraig Rodrigues 				targetvp = lvp;
682d00947d8SCraig Rodrigues 		} else
683d00947d8SCraig Rodrigues 			targetvp = uvp;
684d00947d8SCraig Rodrigues 	}
685d00947d8SCraig Rodrigues 
686eb60ff1eSJason A. Harmening 	if (targetvp == uvp && uvp->v_type == VDIR && lvp != NULLVP &&
687eb60ff1eSJason A. Harmening 	    unsp->uns_lower_opencnt <= 0)
688eb60ff1eSJason A. Harmening 		open_lvp = true;
689eb60ff1eSJason A. Harmening 	else if (targetvp == lvp && uvp != NULLVP)
690eb60ff1eSJason A. Harmening 		lock_lvp = true;
691eb60ff1eSJason A. Harmening 
692eb60ff1eSJason A. Harmening 	if (lock_lvp) {
693eb60ff1eSJason A. Harmening 		unp = NULL;
694eb60ff1eSJason A. Harmening 		lvp = unionfs_lock_lvp(vp, &lkflags);
695eb60ff1eSJason A. Harmening 		if (lvp == NULLVP) {
696eb60ff1eSJason A. Harmening 			error = ENOENT;
697eb60ff1eSJason A. Harmening 			goto unionfs_open_abort;
698eb60ff1eSJason A. Harmening 		}
699eb60ff1eSJason A. Harmening 	} else
700eb60ff1eSJason A. Harmening 		unionfs_forward_vop_start(targetvp, &lkflags);
701eb60ff1eSJason A. Harmening 
7029e223287SKonstantin Belousov 	error = VOP_OPEN(targetvp, ap->a_mode, cred, td, ap->a_fp);
703eb60ff1eSJason A. Harmening 
704eb60ff1eSJason A. Harmening 	if (lock_lvp) {
705eb60ff1eSJason A. Harmening 		unp = unionfs_unlock_lvp(vp, lvp, lkflags);
706eb60ff1eSJason A. Harmening 		if (unp == NULL && error == 0)
707eb60ff1eSJason A. Harmening 			error = ENOENT;
708eb60ff1eSJason A. Harmening 	} else if (unionfs_forward_vop_finish(vp, targetvp, lkflags))
709eb60ff1eSJason A. Harmening 		error = error ? error : ENOENT;
710eb60ff1eSJason A. Harmening 
711eb60ff1eSJason A. Harmening 	if (error != 0)
712eb60ff1eSJason A. Harmening 		goto unionfs_open_abort;
713eb60ff1eSJason A. Harmening 
714d00947d8SCraig Rodrigues 	if (targetvp == uvp) {
715eb60ff1eSJason A. Harmening 		if (open_lvp) {
716eb60ff1eSJason A. Harmening 			unp = NULL;
717eb60ff1eSJason A. Harmening 			lvp = unionfs_lock_lvp(vp, &lkflags);
718eb60ff1eSJason A. Harmening 			if (lvp == NULLVP) {
719eb60ff1eSJason A. Harmening 				error = ENOENT;
720eb60ff1eSJason A. Harmening 				goto unionfs_open_abort;
721eb60ff1eSJason A. Harmening 			}
722d00947d8SCraig Rodrigues 			/* open lower for readdir */
7239e223287SKonstantin Belousov 			error = VOP_OPEN(lvp, FREAD, cred, td, NULL);
724eb60ff1eSJason A. Harmening 			unp = unionfs_unlock_lvp(vp, lvp, lkflags);
725eb60ff1eSJason A. Harmening 			if (unp == NULL) {
726eb60ff1eSJason A. Harmening 				error = error ? error : ENOENT;
727eb60ff1eSJason A. Harmening 				goto unionfs_open_abort;
728eb60ff1eSJason A. Harmening 			}
729d00947d8SCraig Rodrigues 			if (error != 0) {
730eb60ff1eSJason A. Harmening 				unionfs_forward_vop_start(uvp, &lkflags);
731d00947d8SCraig Rodrigues 				VOP_CLOSE(uvp, ap->a_mode, cred, td);
732eb60ff1eSJason A. Harmening 				if (unionfs_forward_vop_finish(vp, uvp, lkflags))
733eb60ff1eSJason A. Harmening 					unp = NULL;
734d00947d8SCraig Rodrigues 				goto unionfs_open_abort;
735d00947d8SCraig Rodrigues 			}
736d00947d8SCraig Rodrigues 			unsp->uns_node_flag |= UNS_OPENL_4_READDIR;
737d00947d8SCraig Rodrigues 			unsp->uns_lower_opencnt++;
738d00947d8SCraig Rodrigues 		}
739d00947d8SCraig Rodrigues 		unsp->uns_upper_opencnt++;
740d00947d8SCraig Rodrigues 	} else {
741d00947d8SCraig Rodrigues 		unsp->uns_lower_opencnt++;
742d00947d8SCraig Rodrigues 		unsp->uns_lower_openmode = ap->a_mode;
743d00947d8SCraig Rodrigues 	}
74439a2dc44SJason A. Harmening 	vp->v_object = targetvp->v_object;
745d00947d8SCraig Rodrigues 
746d00947d8SCraig Rodrigues unionfs_open_abort:
747eb60ff1eSJason A. Harmening 
748eb60ff1eSJason A. Harmening 	if (error != 0 && unp != NULL)
749fe5f08cdSDaichi GOTO 		unionfs_tryrem_node_status(unp, unsp);
750d00947d8SCraig Rodrigues 
75139a2dc44SJason A. Harmening unionfs_open_cleanup:
75239a2dc44SJason A. Harmening 	unionfs_downgrade_lock(vp, lkstatus);
75339a2dc44SJason A. Harmening 
754d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_open: leave (%d)\n", error);
755d00947d8SCraig Rodrigues 
756d00947d8SCraig Rodrigues 	return (error);
757d00947d8SCraig Rodrigues }
758d00947d8SCraig Rodrigues 
759d00947d8SCraig Rodrigues static int
unionfs_close(struct vop_close_args * ap)760d00947d8SCraig Rodrigues unionfs_close(struct vop_close_args *ap)
761d00947d8SCraig Rodrigues {
762d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
763d00947d8SCraig Rodrigues 	struct unionfs_node_status *unsp;
764d00947d8SCraig Rodrigues 	struct ucred   *cred;
765d00947d8SCraig Rodrigues 	struct thread  *td;
766cb5736b7SDaichi GOTO 	struct vnode   *vp;
767eb60ff1eSJason A. Harmening 	struct vnode   *uvp;
768eb60ff1eSJason A. Harmening 	struct vnode   *lvp;
769d00947d8SCraig Rodrigues 	struct vnode   *ovp;
770312d49efSJason A. Harmening 	int		error;
771eb60ff1eSJason A. Harmening 	int		lkflags;
772552581e7Srilysh 	enum unionfs_lkupgrade lkstatus;
773eb60ff1eSJason A. Harmening 	bool		lock_lvp;
774d00947d8SCraig Rodrigues 
775d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_close: enter\n");
776d00947d8SCraig Rodrigues 
7771e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_vp);
7781e5da15aSDaichi GOTO 
779cb5736b7SDaichi GOTO 	vp = ap->a_vp;
780d00947d8SCraig Rodrigues 	cred = ap->a_cred;
781d00947d8SCraig Rodrigues 	td = ap->a_td;
78239a2dc44SJason A. Harmening 	error = 0;
783eb60ff1eSJason A. Harmening 	lock_lvp = false;
784d00947d8SCraig Rodrigues 
78539a2dc44SJason A. Harmening 	/*
78639a2dc44SJason A. Harmening 	 * If the vnode is reclaimed while upgrading, we can't safely use unp
78739a2dc44SJason A. Harmening 	 * or do anything else unionfs- specific.
78839a2dc44SJason A. Harmening 	 */
78939a2dc44SJason A. Harmening 	lkstatus = unionfs_upgrade_lock(vp);
79039a2dc44SJason A. Harmening 	if (lkstatus == UNIONFS_LKUPGRADE_DOOMED)
79139a2dc44SJason A. Harmening 		goto unionfs_close_cleanup;
79239a2dc44SJason A. Harmening 
79339a2dc44SJason A. Harmening 	unp = VTOUNIONFS(vp);
794eb60ff1eSJason A. Harmening 	lvp = unp->un_lowervp;
795eb60ff1eSJason A. Harmening 	uvp = unp->un_uppervp;
79653a777bbSJason A. Harmening 	unsp = unionfs_find_node_status(unp, td);
797d00947d8SCraig Rodrigues 
79853a777bbSJason A. Harmening 	if (unsp == NULL ||
79953a777bbSJason A. Harmening 	    (unsp->uns_lower_opencnt <= 0 && unsp->uns_upper_opencnt <= 0)) {
80053a777bbSJason A. Harmening #ifdef DIAGNOSTIC
80153a777bbSJason A. Harmening 		if (unsp != NULL)
80253a777bbSJason A. Harmening 			printf("unionfs_close: warning: open count is 0\n");
80353a777bbSJason A. Harmening #endif
804eb60ff1eSJason A. Harmening 		if (uvp != NULLVP)
805eb60ff1eSJason A. Harmening 			ovp = uvp;
806d00947d8SCraig Rodrigues 		else
807eb60ff1eSJason A. Harmening 			ovp = lvp;
808d00947d8SCraig Rodrigues 	} else if (unsp->uns_upper_opencnt > 0)
809eb60ff1eSJason A. Harmening 		ovp = uvp;
810d00947d8SCraig Rodrigues 	else
811eb60ff1eSJason A. Harmening 		ovp = lvp;
812eb60ff1eSJason A. Harmening 
813eb60ff1eSJason A. Harmening 	if (ovp == lvp && uvp != NULLVP) {
814eb60ff1eSJason A. Harmening 		lock_lvp = true;
815eb60ff1eSJason A. Harmening 		unp = NULL;
816eb60ff1eSJason A. Harmening 		lvp = unionfs_lock_lvp(vp, &lkflags);
817eb60ff1eSJason A. Harmening 		if (lvp == NULLVP) {
818eb60ff1eSJason A. Harmening 			error = ENOENT;
819eb60ff1eSJason A. Harmening 			goto unionfs_close_abort;
820eb60ff1eSJason A. Harmening 		}
821eb60ff1eSJason A. Harmening 	} else
822eb60ff1eSJason A. Harmening 		unionfs_forward_vop_start(ovp, &lkflags);
823d00947d8SCraig Rodrigues 
824d00947d8SCraig Rodrigues 	error = VOP_CLOSE(ovp, ap->a_fflag, cred, td);
825d00947d8SCraig Rodrigues 
826eb60ff1eSJason A. Harmening 	if (lock_lvp) {
827eb60ff1eSJason A. Harmening 		unp = unionfs_unlock_lvp(vp, lvp, lkflags);
828eb60ff1eSJason A. Harmening 		if (unp == NULL && error == 0)
829eb60ff1eSJason A. Harmening 			error = ENOENT;
830eb60ff1eSJason A. Harmening 	} else if (unionfs_forward_vop_finish(vp, ovp, lkflags))
831eb60ff1eSJason A. Harmening 		error = error ? error : ENOENT;
832eb60ff1eSJason A. Harmening 
833d00947d8SCraig Rodrigues 	if (error != 0)
834d00947d8SCraig Rodrigues 		goto unionfs_close_abort;
835d00947d8SCraig Rodrigues 
836cb5736b7SDaichi GOTO 	vp->v_object = ovp->v_object;
837d00947d8SCraig Rodrigues 
838eb60ff1eSJason A. Harmening 	if (ovp == uvp) {
839eb60ff1eSJason A. Harmening 		if (unsp != NULL && ((--unsp->uns_upper_opencnt) == 0)) {
840d00947d8SCraig Rodrigues 			if (unsp->uns_node_flag & UNS_OPENL_4_READDIR) {
841eb60ff1eSJason A. Harmening 				unp = NULL;
842eb60ff1eSJason A. Harmening 				lvp = unionfs_lock_lvp(vp, &lkflags);
843eb60ff1eSJason A. Harmening 				if (lvp == NULLVP) {
844eb60ff1eSJason A. Harmening 					error = ENOENT;
845eb60ff1eSJason A. Harmening 					goto unionfs_close_abort;
846eb60ff1eSJason A. Harmening 				}
847eb60ff1eSJason A. Harmening 				VOP_CLOSE(lvp, FREAD, cred, td);
848eb60ff1eSJason A. Harmening 				unp = unionfs_unlock_lvp(vp, lvp, lkflags);
849eb60ff1eSJason A. Harmening 				if (unp == NULL) {
850eb60ff1eSJason A. Harmening 					error = ENOENT;
851eb60ff1eSJason A. Harmening 					goto unionfs_close_abort;
852eb60ff1eSJason A. Harmening 				}
853d00947d8SCraig Rodrigues 				unsp->uns_node_flag &= ~UNS_OPENL_4_READDIR;
854d00947d8SCraig Rodrigues 				unsp->uns_lower_opencnt--;
855df8bae1dSRodney W. Grimes 			}
856d00947d8SCraig Rodrigues 			if (unsp->uns_lower_opencnt > 0)
857eb60ff1eSJason A. Harmening 				vp->v_object = lvp->v_object;
858d00947d8SCraig Rodrigues 		}
859eb60ff1eSJason A. Harmening 	} else if (unsp != NULL)
860d00947d8SCraig Rodrigues 		unsp->uns_lower_opencnt--;
861d00947d8SCraig Rodrigues 
862d00947d8SCraig Rodrigues unionfs_close_abort:
863eb60ff1eSJason A. Harmening 	if (unp != NULL && unsp != NULL)
864fe5f08cdSDaichi GOTO 		unionfs_tryrem_node_status(unp, unsp);
865d00947d8SCraig Rodrigues 
86639a2dc44SJason A. Harmening unionfs_close_cleanup:
86739a2dc44SJason A. Harmening 	unionfs_downgrade_lock(vp, lkstatus);
868d00947d8SCraig Rodrigues 
869d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_close: leave (%d)\n", error);
870d00947d8SCraig Rodrigues 
871d00947d8SCraig Rodrigues 	return (error);
872df8bae1dSRodney W. Grimes }
873df8bae1dSRodney W. Grimes 
874df8bae1dSRodney W. Grimes /*
875d00947d8SCraig Rodrigues  * Check the access mode toward shadow file/dir.
876df8bae1dSRodney W. Grimes  */
877c9bf0111SKATO Takenori static int
unionfs_check_corrected_access(accmode_t accmode,struct vattr * va,struct ucred * cred)878312d49efSJason A. Harmening unionfs_check_corrected_access(accmode_t accmode, struct vattr *va,
879d00947d8SCraig Rodrigues     struct ucred *cred)
880df8bae1dSRodney W. Grimes {
881d00947d8SCraig Rodrigues 	uid_t		uid;	/* upper side vnode's uid */
882d00947d8SCraig Rodrigues 	gid_t		gid;	/* upper side vnode's gid */
883d00947d8SCraig Rodrigues 	u_short		vmode;	/* upper side vnode's mode */
884d00947d8SCraig Rodrigues 	u_short		mask;
885df8bae1dSRodney W. Grimes 
886d00947d8SCraig Rodrigues 	mask = 0;
887d00947d8SCraig Rodrigues 	uid = va->va_uid;
888d00947d8SCraig Rodrigues 	gid = va->va_gid;
889d00947d8SCraig Rodrigues 	vmode = va->va_mode;
890d00947d8SCraig Rodrigues 
891d00947d8SCraig Rodrigues 	/* check owner */
892d00947d8SCraig Rodrigues 	if (cred->cr_uid == uid) {
89315bc6b2bSEdward Tomasz Napierala 		if (accmode & VEXEC)
894d00947d8SCraig Rodrigues 			mask |= S_IXUSR;
89515bc6b2bSEdward Tomasz Napierala 		if (accmode & VREAD)
896d00947d8SCraig Rodrigues 			mask |= S_IRUSR;
89715bc6b2bSEdward Tomasz Napierala 		if (accmode & VWRITE)
898d00947d8SCraig Rodrigues 			mask |= S_IWUSR;
899d00947d8SCraig Rodrigues 		return ((vmode & mask) == mask ? 0 : EACCES);
900d00947d8SCraig Rodrigues 	}
901d00947d8SCraig Rodrigues 
902d00947d8SCraig Rodrigues 	/* check group */
903838d9858SBrooks Davis 	if (groupmember(gid, cred)) {
90415bc6b2bSEdward Tomasz Napierala 		if (accmode & VEXEC)
905d00947d8SCraig Rodrigues 			mask |= S_IXGRP;
90615bc6b2bSEdward Tomasz Napierala 		if (accmode & VREAD)
907d00947d8SCraig Rodrigues 			mask |= S_IRGRP;
90815bc6b2bSEdward Tomasz Napierala 		if (accmode & VWRITE)
909d00947d8SCraig Rodrigues 			mask |= S_IWGRP;
910d00947d8SCraig Rodrigues 		return ((vmode & mask) == mask ? 0 : EACCES);
911d00947d8SCraig Rodrigues 	}
912d00947d8SCraig Rodrigues 
913d00947d8SCraig Rodrigues 	/* check other */
91415bc6b2bSEdward Tomasz Napierala 	if (accmode & VEXEC)
915d00947d8SCraig Rodrigues 		mask |= S_IXOTH;
91615bc6b2bSEdward Tomasz Napierala 	if (accmode & VREAD)
917d00947d8SCraig Rodrigues 		mask |= S_IROTH;
91815bc6b2bSEdward Tomasz Napierala 	if (accmode & VWRITE)
919d00947d8SCraig Rodrigues 		mask |= S_IWOTH;
920d00947d8SCraig Rodrigues 
921d00947d8SCraig Rodrigues 	return ((vmode & mask) == mask ? 0 : EACCES);
922d00947d8SCraig Rodrigues }
923d00947d8SCraig Rodrigues 
924d00947d8SCraig Rodrigues static int
unionfs_access(struct vop_access_args * ap)925d00947d8SCraig Rodrigues unionfs_access(struct vop_access_args *ap)
926d00947d8SCraig Rodrigues {
927d00947d8SCraig Rodrigues 	struct unionfs_mount *ump;
928d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
929d00947d8SCraig Rodrigues 	struct vnode   *uvp;
930d00947d8SCraig Rodrigues 	struct vnode   *lvp;
931d00947d8SCraig Rodrigues 	struct thread  *td;
932d00947d8SCraig Rodrigues 	struct vattr	va;
93315bc6b2bSEdward Tomasz Napierala 	accmode_t	accmode;
934d00947d8SCraig Rodrigues 	int		error;
935d00947d8SCraig Rodrigues 
936d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_access: enter\n");
937d00947d8SCraig Rodrigues 
9381e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_vp);
9391e5da15aSDaichi GOTO 
940d00947d8SCraig Rodrigues 	ump = MOUNTTOUNIONFSMOUNT(ap->a_vp->v_mount);
941d00947d8SCraig Rodrigues 	unp = VTOUNIONFS(ap->a_vp);
942d00947d8SCraig Rodrigues 	uvp = unp->un_uppervp;
943d00947d8SCraig Rodrigues 	lvp = unp->un_lowervp;
944d00947d8SCraig Rodrigues 	td = ap->a_td;
94515bc6b2bSEdward Tomasz Napierala 	accmode = ap->a_accmode;
946d00947d8SCraig Rodrigues 	error = EACCES;
947d00947d8SCraig Rodrigues 
94815bc6b2bSEdward Tomasz Napierala 	if ((accmode & VWRITE) &&
9492a31267eSMatthew Dillon 	    (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY)) {
9506ca02614SKATO Takenori 		switch (ap->a_vp->v_type) {
951831a80b0SMatthew Dillon 		case VREG:
952831a80b0SMatthew Dillon 		case VDIR:
953831a80b0SMatthew Dillon 		case VLNK:
9546ca02614SKATO Takenori 			return (EROFS);
955831a80b0SMatthew Dillon 		default:
956831a80b0SMatthew Dillon 			break;
9576ca02614SKATO Takenori 		}
9586ca02614SKATO Takenori 	}
9592a31267eSMatthew Dillon 
960d00947d8SCraig Rodrigues 	if (uvp != NULLVP) {
96115bc6b2bSEdward Tomasz Napierala 		error = VOP_ACCESS(uvp, accmode, ap->a_cred, td);
962d00947d8SCraig Rodrigues 
963d00947d8SCraig Rodrigues 		UNIONFS_INTERNAL_DEBUG("unionfs_access: leave (%d)\n", error);
964d00947d8SCraig Rodrigues 
9652a31267eSMatthew Dillon 		return (error);
966df8bae1dSRodney W. Grimes 	}
967df8bae1dSRodney W. Grimes 
968d00947d8SCraig Rodrigues 	if (lvp != NULLVP) {
96915bc6b2bSEdward Tomasz Napierala 		if (accmode & VWRITE) {
970cc3ec9f7SJason A. Harmening 			if ((ump->um_uppermp->mnt_flag & MNT_RDONLY) != 0) {
971d00947d8SCraig Rodrigues 				switch (ap->a_vp->v_type) {
972d00947d8SCraig Rodrigues 				case VREG:
973d00947d8SCraig Rodrigues 				case VDIR:
974d00947d8SCraig Rodrigues 				case VLNK:
975d00947d8SCraig Rodrigues 					return (EROFS);
976d00947d8SCraig Rodrigues 				default:
977d00947d8SCraig Rodrigues 					break;
978df8bae1dSRodney W. Grimes 				}
979312d49efSJason A. Harmening 			} else if (ap->a_vp->v_type == VREG ||
980312d49efSJason A. Harmening 			    ap->a_vp->v_type == VDIR) {
981d00947d8SCraig Rodrigues 				/* check shadow file/dir */
982d00947d8SCraig Rodrigues 				if (ump->um_copymode != UNIONFS_TRANSPARENT) {
983d00947d8SCraig Rodrigues 					error = unionfs_create_uppervattr(ump,
984d00947d8SCraig Rodrigues 					    lvp, &va, ap->a_cred, td);
985d00947d8SCraig Rodrigues 					if (error != 0)
986d00947d8SCraig Rodrigues 						return (error);
987d00947d8SCraig Rodrigues 
988d00947d8SCraig Rodrigues 					error = unionfs_check_corrected_access(
98915bc6b2bSEdward Tomasz Napierala 					    accmode, &va, ap->a_cred);
990d00947d8SCraig Rodrigues 					if (error != 0)
991df8bae1dSRodney W. Grimes 						return (error);
992df8bae1dSRodney W. Grimes 				}
993d00947d8SCraig Rodrigues 			}
9945c0c5a18SEdward Tomasz Napierala 			accmode &= ~(VWRITE | VAPPEND);
99515bc6b2bSEdward Tomasz Napierala 			accmode |= VREAD; /* will copy to upper */
996d00947d8SCraig Rodrigues 		}
99715bc6b2bSEdward Tomasz Napierala 		error = VOP_ACCESS(lvp, accmode, ap->a_cred, td);
998d00947d8SCraig Rodrigues 	}
999df8bae1dSRodney W. Grimes 
1000d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_access: leave (%d)\n", error);
1001d00947d8SCraig Rodrigues 
1002d00947d8SCraig Rodrigues 	return (error);
1003d00947d8SCraig Rodrigues }
10042a31267eSMatthew Dillon 
1005c9bf0111SKATO Takenori static int
unionfs_getattr(struct vop_getattr_args * ap)1006d00947d8SCraig Rodrigues unionfs_getattr(struct vop_getattr_args *ap)
1007df8bae1dSRodney W. Grimes {
1008d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
1009d00947d8SCraig Rodrigues 	struct unionfs_mount *ump;
1010d00947d8SCraig Rodrigues 	struct vnode   *uvp;
1011d00947d8SCraig Rodrigues 	struct vnode   *lvp;
1012d00947d8SCraig Rodrigues 	struct thread  *td;
1013df8bae1dSRodney W. Grimes 	struct vattr	va;
1014312d49efSJason A. Harmening 	int		error;
1015df8bae1dSRodney W. Grimes 
1016d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_getattr: enter\n");
1017df8bae1dSRodney W. Grimes 
10181e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_vp);
10191e5da15aSDaichi GOTO 
1020d00947d8SCraig Rodrigues 	unp = VTOUNIONFS(ap->a_vp);
1021d00947d8SCraig Rodrigues 	ump = MOUNTTOUNIONFSMOUNT(ap->a_vp->v_mount);
1022d00947d8SCraig Rodrigues 	uvp = unp->un_uppervp;
1023d00947d8SCraig Rodrigues 	lvp = unp->un_lowervp;
10240359a12eSAttilio Rao 	td = curthread;
1025df8bae1dSRodney W. Grimes 
1026d00947d8SCraig Rodrigues 	if (uvp != NULLVP) {
10270359a12eSAttilio Rao 		if ((error = VOP_GETATTR(uvp, ap->a_vap, ap->a_cred)) == 0)
1028312d49efSJason A. Harmening 			ap->a_vap->va_fsid =
1029312d49efSJason A. Harmening 			    ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
1030d354520eSTakanori Watanabe 
1031312d49efSJason A. Harmening 		UNIONFS_INTERNAL_DEBUG(
1032312d49efSJason A. Harmening 		    "unionfs_getattr: leave mode=%o, uid=%d, gid=%d (%d)\n",
1033d00947d8SCraig Rodrigues 		    ap->a_vap->va_mode, ap->a_vap->va_uid,
1034d00947d8SCraig Rodrigues 		    ap->a_vap->va_gid, error);
1035d00947d8SCraig Rodrigues 
1036d00947d8SCraig Rodrigues 		return (error);
1037d00947d8SCraig Rodrigues 	}
1038d00947d8SCraig Rodrigues 
10390359a12eSAttilio Rao 	error = VOP_GETATTR(lvp, ap->a_vap, ap->a_cred);
1040d00947d8SCraig Rodrigues 
1041cc3ec9f7SJason A. Harmening 	if (error == 0 && (ump->um_uppermp->mnt_flag & MNT_RDONLY) == 0) {
1042d00947d8SCraig Rodrigues 		/* correct the attr toward shadow file/dir. */
1043d00947d8SCraig Rodrigues 		if (ap->a_vp->v_type == VREG || ap->a_vp->v_type == VDIR) {
1044d00947d8SCraig Rodrigues 			unionfs_create_uppervattr_core(ump, ap->a_vap, &va, td);
1045d00947d8SCraig Rodrigues 			ap->a_vap->va_mode = va.va_mode;
1046d00947d8SCraig Rodrigues 			ap->a_vap->va_uid = va.va_uid;
1047d00947d8SCraig Rodrigues 			ap->a_vap->va_gid = va.va_gid;
1048d00947d8SCraig Rodrigues 		}
1049d00947d8SCraig Rodrigues 	}
1050d00947d8SCraig Rodrigues 
1051d00947d8SCraig Rodrigues 	if (error == 0)
1052d00947d8SCraig Rodrigues 		ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
1053d00947d8SCraig Rodrigues 
1054312d49efSJason A. Harmening 	UNIONFS_INTERNAL_DEBUG(
1055312d49efSJason A. Harmening 	    "unionfs_getattr: leave mode=%o, uid=%d, gid=%d (%d)\n",
1056d00947d8SCraig Rodrigues 	    ap->a_vap->va_mode, ap->a_vap->va_uid, ap->a_vap->va_gid, error);
1057d00947d8SCraig Rodrigues 
1058d00947d8SCraig Rodrigues 	return (error);
1059df8bae1dSRodney W. Grimes }
1060df8bae1dSRodney W. Grimes 
1061c9bf0111SKATO Takenori static int
unionfs_setattr(struct vop_setattr_args * ap)1062d00947d8SCraig Rodrigues unionfs_setattr(struct vop_setattr_args *ap)
1063df8bae1dSRodney W. Grimes {
1064d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
1065d00947d8SCraig Rodrigues 	struct vnode   *uvp;
1066d00947d8SCraig Rodrigues 	struct vnode   *lvp;
1067d00947d8SCraig Rodrigues 	struct thread  *td;
1068d00947d8SCraig Rodrigues 	struct vattr   *vap;
1069312d49efSJason A. Harmening 	int		error;
1070df8bae1dSRodney W. Grimes 
1071d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_setattr: enter\n");
1072d00947d8SCraig Rodrigues 
10731e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_vp);
10741e5da15aSDaichi GOTO 
1075d00947d8SCraig Rodrigues 	error = EROFS;
1076d00947d8SCraig Rodrigues 	unp = VTOUNIONFS(ap->a_vp);
1077d00947d8SCraig Rodrigues 	uvp = unp->un_uppervp;
1078d00947d8SCraig Rodrigues 	lvp = unp->un_lowervp;
10790359a12eSAttilio Rao 	td = curthread;
1080d00947d8SCraig Rodrigues 	vap = ap->a_vap;
1081d00947d8SCraig Rodrigues 
10826ca02614SKATO Takenori 	if ((ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) &&
10836ca02614SKATO Takenori 	    (vap->va_flags != VNOVAL || vap->va_uid != (uid_t)VNOVAL ||
10846ca02614SKATO Takenori 	     vap->va_gid != (gid_t)VNOVAL || vap->va_atime.tv_sec != VNOVAL ||
1085d00947d8SCraig Rodrigues 	     vap->va_mtime.tv_sec != VNOVAL || vap->va_mode != (mode_t)VNOVAL))
10866ca02614SKATO Takenori 		return (EROFS);
10876ca02614SKATO Takenori 
1088d00947d8SCraig Rodrigues 	if (uvp == NULLVP && lvp->v_type == VREG) {
1089eb60ff1eSJason A. Harmening 		error = unionfs_copyfile(ap->a_vp, (vap->va_size != 0),
1090d00947d8SCraig Rodrigues 		    ap->a_cred, td);
1091d00947d8SCraig Rodrigues 		if (error != 0)
1092df8bae1dSRodney W. Grimes 			return (error);
1093d00947d8SCraig Rodrigues 		uvp = unp->un_uppervp;
1094df8bae1dSRodney W. Grimes 	}
1095df8bae1dSRodney W. Grimes 
10966c8ded00SJason A. Harmening 	if (uvp != NULLVP) {
10976c8ded00SJason A. Harmening 		int lkflags;
10986c8ded00SJason A. Harmening 		unionfs_forward_vop_start(uvp, &lkflags);
10990359a12eSAttilio Rao 		error = VOP_SETATTR(uvp, vap, ap->a_cred);
11006c8ded00SJason A. Harmening 		unionfs_forward_vop_finish(ap->a_vp, uvp, lkflags);
11016c8ded00SJason A. Harmening 	}
1102d00947d8SCraig Rodrigues 
1103d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_setattr: leave (%d)\n", error);
1104d00947d8SCraig Rodrigues 
11052a31267eSMatthew Dillon 	return (error);
1106df8bae1dSRodney W. Grimes }
1107df8bae1dSRodney W. Grimes 
1108c9bf0111SKATO Takenori static int
unionfs_read(struct vop_read_args * ap)1109d00947d8SCraig Rodrigues unionfs_read(struct vop_read_args *ap)
1110df8bae1dSRodney W. Grimes {
1111d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
1112d00947d8SCraig Rodrigues 	struct vnode   *tvp;
1113312d49efSJason A. Harmening 	int		error;
1114df8bae1dSRodney W. Grimes 
1115d00947d8SCraig Rodrigues 	/* UNIONFS_INTERNAL_DEBUG("unionfs_read: enter\n"); */
11162a31267eSMatthew Dillon 
11171e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_vp);
11181e5da15aSDaichi GOTO 
1119d00947d8SCraig Rodrigues 	unp = VTOUNIONFS(ap->a_vp);
1120d00947d8SCraig Rodrigues 	tvp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp);
1121996c772fSJohn Dyson 
1122d00947d8SCraig Rodrigues 	error = VOP_READ(tvp, ap->a_uio, ap->a_ioflag, ap->a_cred);
1123996c772fSJohn Dyson 
1124d00947d8SCraig Rodrigues 	/* UNIONFS_INTERNAL_DEBUG("unionfs_read: leave (%d)\n", error); */
1125d00947d8SCraig Rodrigues 
1126df8bae1dSRodney W. Grimes 	return (error);
1127df8bae1dSRodney W. Grimes }
1128df8bae1dSRodney W. Grimes 
1129c9bf0111SKATO Takenori static int
unionfs_write(struct vop_write_args * ap)1130d00947d8SCraig Rodrigues unionfs_write(struct vop_write_args *ap)
1131df8bae1dSRodney W. Grimes {
1132d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
1133d00947d8SCraig Rodrigues 	struct vnode   *tvp;
1134312d49efSJason A. Harmening 	int		error;
11356c8ded00SJason A. Harmening 	int		lkflags;
1136df8bae1dSRodney W. Grimes 
1137d00947d8SCraig Rodrigues 	/* UNIONFS_INTERNAL_DEBUG("unionfs_write: enter\n"); */
1138996c772fSJohn Dyson 
11391e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_vp);
11401e5da15aSDaichi GOTO 
1141d00947d8SCraig Rodrigues 	unp = VTOUNIONFS(ap->a_vp);
1142d00947d8SCraig Rodrigues 	tvp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp);
1143996c772fSJohn Dyson 
11446c8ded00SJason A. Harmening 	unionfs_forward_vop_start(tvp, &lkflags);
1145d00947d8SCraig Rodrigues 	error = VOP_WRITE(tvp, ap->a_uio, ap->a_ioflag, ap->a_cred);
11466c8ded00SJason A. Harmening 	unionfs_forward_vop_finish(ap->a_vp, tvp, lkflags);
1147996c772fSJohn Dyson 
1148d00947d8SCraig Rodrigues 	/* UNIONFS_INTERNAL_DEBUG("unionfs_write: leave (%d)\n", error); */
1149df8bae1dSRodney W. Grimes 
1150df8bae1dSRodney W. Grimes 	return (error);
1151df8bae1dSRodney W. Grimes }
1152df8bae1dSRodney W. Grimes 
1153c9bf0111SKATO Takenori static int
unionfs_ioctl(struct vop_ioctl_args * ap)1154d00947d8SCraig Rodrigues unionfs_ioctl(struct vop_ioctl_args *ap)
1155df8bae1dSRodney W. Grimes {
1156d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
1157d00947d8SCraig Rodrigues 	struct unionfs_node_status *unsp;
1158d00947d8SCraig Rodrigues 	struct vnode   *ovp;
1159312d49efSJason A. Harmening 	int error;
1160df8bae1dSRodney W. Grimes 
1161d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_ioctl: enter\n");
11622a31267eSMatthew Dillon 
11631e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_vp);
11641e5da15aSDaichi GOTO 
1165cb05b60aSAttilio Rao  	vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY);
1166d00947d8SCraig Rodrigues 	unp = VTOUNIONFS(ap->a_vp);
1167d00947d8SCraig Rodrigues 	unionfs_get_node_status(unp, ap->a_td, &unsp);
1168d00947d8SCraig Rodrigues 	ovp = (unsp->uns_upper_opencnt ? unp->un_uppervp : unp->un_lowervp);
1169fe5f08cdSDaichi GOTO 	unionfs_tryrem_node_status(unp, unsp);
1170b249ce48SMateusz Guzik 	VOP_UNLOCK(ap->a_vp);
1171d00947d8SCraig Rodrigues 
1172d00947d8SCraig Rodrigues 	if (ovp == NULLVP)
1173d00947d8SCraig Rodrigues 		return (EBADF);
1174d00947d8SCraig Rodrigues 
1175d00947d8SCraig Rodrigues 	error = VOP_IOCTL(ovp, ap->a_command, ap->a_data, ap->a_fflag,
1176d00947d8SCraig Rodrigues 	    ap->a_cred, ap->a_td);
1177d00947d8SCraig Rodrigues 
1178885868cdSRobert Watson 	UNIONFS_INTERNAL_DEBUG("unionfs_ioctl: leave (%d)\n", error);
1179d00947d8SCraig Rodrigues 
1180d00947d8SCraig Rodrigues 	return (error);
1181df8bae1dSRodney W. Grimes }
1182df8bae1dSRodney W. Grimes 
1183d00947d8SCraig Rodrigues static int
unionfs_poll(struct vop_poll_args * ap)1184d00947d8SCraig Rodrigues unionfs_poll(struct vop_poll_args *ap)
1185d00947d8SCraig Rodrigues {
1186d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
1187d00947d8SCraig Rodrigues 	struct unionfs_node_status *unsp;
1188d00947d8SCraig Rodrigues 	struct vnode *ovp;
11892a31267eSMatthew Dillon 
11901e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_vp);
11911e5da15aSDaichi GOTO 
1192cb05b60aSAttilio Rao  	vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY);
1193d00947d8SCraig Rodrigues 	unp = VTOUNIONFS(ap->a_vp);
1194d00947d8SCraig Rodrigues 	unionfs_get_node_status(unp, ap->a_td, &unsp);
1195d00947d8SCraig Rodrigues 	ovp = (unsp->uns_upper_opencnt ? unp->un_uppervp : unp->un_lowervp);
1196fe5f08cdSDaichi GOTO 	unionfs_tryrem_node_status(unp, unsp);
1197b249ce48SMateusz Guzik 	VOP_UNLOCK(ap->a_vp);
1198d00947d8SCraig Rodrigues 
1199d00947d8SCraig Rodrigues 	if (ovp == NULLVP)
1200d00947d8SCraig Rodrigues 		return (EBADF);
1201d00947d8SCraig Rodrigues 
1202d00947d8SCraig Rodrigues 	return (VOP_POLL(ovp, ap->a_events, ap->a_cred, ap->a_td));
1203d00947d8SCraig Rodrigues }
1204d00947d8SCraig Rodrigues 
1205d00947d8SCraig Rodrigues static int
unionfs_fsync(struct vop_fsync_args * ap)1206d00947d8SCraig Rodrigues unionfs_fsync(struct vop_fsync_args *ap)
1207d00947d8SCraig Rodrigues {
1208d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
1209d00947d8SCraig Rodrigues 	struct unionfs_node_status *unsp;
1210d00947d8SCraig Rodrigues 	struct vnode *ovp;
12112656fc29SJason A. Harmening 	enum unionfs_lkupgrade lkstatus;
12126c8ded00SJason A. Harmening 	int error, lkflags;
1213d00947d8SCraig Rodrigues 
12141e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_vp);
12151e5da15aSDaichi GOTO 
1216d00947d8SCraig Rodrigues 	unp = VTOUNIONFS(ap->a_vp);
12172656fc29SJason A. Harmening 	lkstatus = unionfs_upgrade_lock(ap->a_vp);
12182656fc29SJason A. Harmening 	if (lkstatus == UNIONFS_LKUPGRADE_DOOMED) {
12192656fc29SJason A. Harmening 		unionfs_downgrade_lock(ap->a_vp, lkstatus);
12202656fc29SJason A. Harmening 		return (ENOENT);
12212656fc29SJason A. Harmening 	}
1222d00947d8SCraig Rodrigues 	unionfs_get_node_status(unp, ap->a_td, &unsp);
1223d00947d8SCraig Rodrigues 	ovp = (unsp->uns_upper_opencnt ? unp->un_uppervp : unp->un_lowervp);
1224fe5f08cdSDaichi GOTO 	unionfs_tryrem_node_status(unp, unsp);
1225d00947d8SCraig Rodrigues 
12262656fc29SJason A. Harmening 	unionfs_downgrade_lock(ap->a_vp, lkstatus);
12272656fc29SJason A. Harmening 
1228d00947d8SCraig Rodrigues 	if (ovp == NULLVP)
1229d00947d8SCraig Rodrigues 		return (EBADF);
1230d00947d8SCraig Rodrigues 
12316c8ded00SJason A. Harmening 	unionfs_forward_vop_start(ovp, &lkflags);
12326c8ded00SJason A. Harmening 	error = VOP_FSYNC(ovp, ap->a_waitfor, ap->a_td);
12336c8ded00SJason A. Harmening 	unionfs_forward_vop_finish(ap->a_vp, ovp, lkflags);
12346c8ded00SJason A. Harmening 
12356c8ded00SJason A. Harmening 	return (error);
1236d00947d8SCraig Rodrigues }
1237d00947d8SCraig Rodrigues 
1238d00947d8SCraig Rodrigues static int
unionfs_remove(struct vop_remove_args * ap)1239d00947d8SCraig Rodrigues unionfs_remove(struct vop_remove_args *ap)
1240d00947d8SCraig Rodrigues {
12411e5da15aSDaichi GOTO 	char	       *path;
1242d00947d8SCraig Rodrigues 	struct unionfs_node *dunp;
1243d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
124420885defSDaichi GOTO 	struct unionfs_mount *ump;
1245d00947d8SCraig Rodrigues 	struct vnode   *udvp;
1246d00947d8SCraig Rodrigues 	struct vnode   *uvp;
1247d00947d8SCraig Rodrigues 	struct vnode   *lvp;
1248d00947d8SCraig Rodrigues 	struct componentname *cnp;
1249d00947d8SCraig Rodrigues 	struct thread  *td;
1250abe95116SJason A. Harmening 	int		error;
1251abe95116SJason A. Harmening 	int		pathlen;
1252d00947d8SCraig Rodrigues 
1253d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_remove: enter\n");
1254d00947d8SCraig Rodrigues 
12551e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_dvp);
1256eee6217bSJason A. Harmening 	KASSERT_UNIONFS_VNODE(ap->a_vp);
12571e5da15aSDaichi GOTO 
1258d00947d8SCraig Rodrigues 	error = 0;
1259d00947d8SCraig Rodrigues 	dunp = VTOUNIONFS(ap->a_dvp);
1260d00947d8SCraig Rodrigues 	udvp = dunp->un_uppervp;
1261d00947d8SCraig Rodrigues 	cnp = ap->a_cnp;
1262d00947d8SCraig Rodrigues 	td = curthread;
1263d00947d8SCraig Rodrigues 
12641e5da15aSDaichi GOTO 	ump = MOUNTTOUNIONFSMOUNT(ap->a_vp->v_mount);
12651e5da15aSDaichi GOTO 	unp = VTOUNIONFS(ap->a_vp);
12661e5da15aSDaichi GOTO 	uvp = unp->un_uppervp;
12671e5da15aSDaichi GOTO 	lvp = unp->un_lowervp;
12681e5da15aSDaichi GOTO 	path = unp->un_path;
1269abe95116SJason A. Harmening 	pathlen = unp->un_pathlen;
12701e5da15aSDaichi GOTO 
1271d00947d8SCraig Rodrigues 	if (udvp == NULLVP)
1272d00947d8SCraig Rodrigues 		return (EROFS);
1273d00947d8SCraig Rodrigues 
1274d00947d8SCraig Rodrigues 	if (uvp != NULLVP) {
12756c8ded00SJason A. Harmening 		int udvp_lkflags, uvp_lkflags;
12761e5da15aSDaichi GOTO 		if (ump == NULL || ump->um_whitemode == UNIONFS_WHITE_ALWAYS ||
12771e5da15aSDaichi GOTO 		    lvp != NULLVP)
1278d00947d8SCraig Rodrigues 			cnp->cn_flags |= DOWHITEOUT;
12796c8ded00SJason A. Harmening 		unionfs_forward_vop_start_pair(udvp, &udvp_lkflags,
1280eee6217bSJason A. Harmening 		    uvp, &uvp_lkflags);
1281d00947d8SCraig Rodrigues 		error = VOP_REMOVE(udvp, uvp, cnp);
12826c8ded00SJason A. Harmening 		unionfs_forward_vop_finish_pair(ap->a_dvp, udvp, udvp_lkflags,
1283eee6217bSJason A. Harmening 		    ap->a_vp, uvp, uvp_lkflags);
1284eb60ff1eSJason A. Harmening 	} else if (lvp != NULLVP) {
1285eb60ff1eSJason A. Harmening 		error = unionfs_mkwhiteout(ap->a_dvp, ap->a_vp, cnp, td,
1286eb60ff1eSJason A. Harmening 		    path, pathlen);
1287eb60ff1eSJason A. Harmening 	}
1288d00947d8SCraig Rodrigues 
1289d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_remove: leave (%d)\n", error);
1290d00947d8SCraig Rodrigues 
1291d00947d8SCraig Rodrigues 	return (error);
1292d00947d8SCraig Rodrigues }
1293d00947d8SCraig Rodrigues 
1294d00947d8SCraig Rodrigues static int
unionfs_link(struct vop_link_args * ap)1295d00947d8SCraig Rodrigues unionfs_link(struct vop_link_args *ap)
1296d00947d8SCraig Rodrigues {
1297d00947d8SCraig Rodrigues 	struct unionfs_node *dunp;
1298d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
1299d00947d8SCraig Rodrigues 	struct vnode   *udvp;
1300d00947d8SCraig Rodrigues 	struct vnode   *uvp;
1301d00947d8SCraig Rodrigues 	struct componentname *cnp;
1302d00947d8SCraig Rodrigues 	struct thread  *td;
1303312d49efSJason A. Harmening 	int		error;
1304d00947d8SCraig Rodrigues 
1305d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_link: enter\n");
1306d00947d8SCraig Rodrigues 
13071e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_tdvp);
13081e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_vp);
13091e5da15aSDaichi GOTO 
1310d00947d8SCraig Rodrigues 	error = 0;
1311d00947d8SCraig Rodrigues 	dunp = VTOUNIONFS(ap->a_tdvp);
1312d00947d8SCraig Rodrigues 	unp = NULL;
1313d00947d8SCraig Rodrigues 	udvp = dunp->un_uppervp;
1314d00947d8SCraig Rodrigues 	uvp = NULLVP;
1315d00947d8SCraig Rodrigues 	cnp = ap->a_cnp;
1316d00947d8SCraig Rodrigues 	td = curthread;
1317d00947d8SCraig Rodrigues 
1318d00947d8SCraig Rodrigues 	if (udvp == NULLVP)
1319d00947d8SCraig Rodrigues 		return (EROFS);
1320d00947d8SCraig Rodrigues 
1321d00947d8SCraig Rodrigues 	unp = VTOUNIONFS(ap->a_vp);
1322d00947d8SCraig Rodrigues 
1323d00947d8SCraig Rodrigues 	if (unp->un_uppervp == NULLVP) {
1324d00947d8SCraig Rodrigues 		if (ap->a_vp->v_type != VREG)
1325d00947d8SCraig Rodrigues 			return (EOPNOTSUPP);
1326d00947d8SCraig Rodrigues 
1327eb60ff1eSJason A. Harmening 		VOP_UNLOCK(ap->a_tdvp);
1328eb60ff1eSJason A. Harmening 		error = unionfs_copyfile(ap->a_vp, 1, cnp->cn_cred, td);
1329eb60ff1eSJason A. Harmening 		vn_lock(ap->a_tdvp, LK_EXCLUSIVE | LK_RETRY);
1330eb60ff1eSJason A. Harmening 		if (error == 0)
1331eb60ff1eSJason A. Harmening 			error = ERELOOKUP;
1332d00947d8SCraig Rodrigues 		return (error);
1333d00947d8SCraig Rodrigues 	}
1334d00947d8SCraig Rodrigues 	uvp = unp->un_uppervp;
1335d00947d8SCraig Rodrigues 
13366c8ded00SJason A. Harmening 	if (error == 0) {
13376c8ded00SJason A. Harmening 		int udvp_lkflags, uvp_lkflags;
13386c8ded00SJason A. Harmening 		unionfs_forward_vop_start_pair(udvp, &udvp_lkflags,
13396c8ded00SJason A. Harmening 		    uvp, &uvp_lkflags);
1340d00947d8SCraig Rodrigues 		error = VOP_LINK(udvp, uvp, cnp);
13416c8ded00SJason A. Harmening 		unionfs_forward_vop_finish_pair(ap->a_tdvp, udvp, udvp_lkflags,
13426c8ded00SJason A. Harmening 		    ap->a_vp, uvp, uvp_lkflags);
13436c8ded00SJason A. Harmening 	}
1344d00947d8SCraig Rodrigues 
1345d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_link: leave (%d)\n", error);
1346d00947d8SCraig Rodrigues 
1347d00947d8SCraig Rodrigues 	return (error);
1348d00947d8SCraig Rodrigues }
1349d00947d8SCraig Rodrigues 
1350d00947d8SCraig Rodrigues static int
unionfs_rename(struct vop_rename_args * ap)1351d00947d8SCraig Rodrigues unionfs_rename(struct vop_rename_args *ap)
1352d00947d8SCraig Rodrigues {
1353d00947d8SCraig Rodrigues 	struct vnode   *fdvp;
1354d00947d8SCraig Rodrigues 	struct vnode   *fvp;
1355d00947d8SCraig Rodrigues 	struct componentname *fcnp;
1356d00947d8SCraig Rodrigues 	struct vnode   *tdvp;
1357d00947d8SCraig Rodrigues 	struct vnode   *tvp;
1358d00947d8SCraig Rodrigues 	struct componentname *tcnp;
1359d00947d8SCraig Rodrigues 	struct thread  *td;
1360d00947d8SCraig Rodrigues 
1361d00947d8SCraig Rodrigues 	/* rename target vnodes */
1362d00947d8SCraig Rodrigues 	struct vnode   *rfdvp;
1363d00947d8SCraig Rodrigues 	struct vnode   *rfvp;
1364d00947d8SCraig Rodrigues 	struct vnode   *rtdvp;
1365d00947d8SCraig Rodrigues 	struct vnode   *rtvp;
1366d00947d8SCraig Rodrigues 
1367d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
1368312d49efSJason A. Harmening 	int		error;
1369d00947d8SCraig Rodrigues 
1370d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_rename: enter\n");
1371d00947d8SCraig Rodrigues 
1372d00947d8SCraig Rodrigues 	error = 0;
1373d00947d8SCraig Rodrigues 	fdvp = ap->a_fdvp;
1374d00947d8SCraig Rodrigues 	fvp = ap->a_fvp;
1375d00947d8SCraig Rodrigues 	fcnp = ap->a_fcnp;
1376d00947d8SCraig Rodrigues 	tdvp = ap->a_tdvp;
1377d00947d8SCraig Rodrigues 	tvp = ap->a_tvp;
1378d00947d8SCraig Rodrigues 	tcnp = ap->a_tcnp;
1379d00947d8SCraig Rodrigues 	td = curthread;
1380d00947d8SCraig Rodrigues 	rfdvp = fdvp;
1381d00947d8SCraig Rodrigues 	rfvp = fvp;
1382d00947d8SCraig Rodrigues 	rtdvp = tdvp;
1383d00947d8SCraig Rodrigues 	rtvp = tvp;
1384d00947d8SCraig Rodrigues 
1385d00947d8SCraig Rodrigues 	/* check for cross device rename */
1386d00947d8SCraig Rodrigues 	if (fvp->v_mount != tdvp->v_mount ||
1387d00947d8SCraig Rodrigues 	    (tvp != NULLVP && fvp->v_mount != tvp->v_mount)) {
13881e5da15aSDaichi GOTO 		if (fvp->v_op != &unionfs_vnodeops)
13891e5da15aSDaichi GOTO 			error = ENODEV;
13901e5da15aSDaichi GOTO 		else
1391d00947d8SCraig Rodrigues 			error = EXDEV;
1392d00947d8SCraig Rodrigues 		goto unionfs_rename_abort;
1393d00947d8SCraig Rodrigues 	}
1394d00947d8SCraig Rodrigues 
1395d00947d8SCraig Rodrigues 	/* Renaming a file to itself has no effect. */
1396d00947d8SCraig Rodrigues 	if (fvp == tvp)
1397d00947d8SCraig Rodrigues 		goto unionfs_rename_abort;
1398d00947d8SCraig Rodrigues 
13991e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(tdvp);
14001e5da15aSDaichi GOTO 	if (tvp != NULLVP)
14011e5da15aSDaichi GOTO 		KASSERT_UNIONFS_VNODE(tvp);
140205e8ab62SJason A. Harmening 	if (fdvp != tdvp)
140305e8ab62SJason A. Harmening 		VI_LOCK(fdvp);
1404d00947d8SCraig Rodrigues 	unp = VTOUNIONFS(fdvp);
140505e8ab62SJason A. Harmening 	if (unp == NULL) {
140605e8ab62SJason A. Harmening 		if (fdvp != tdvp)
140705e8ab62SJason A. Harmening 			VI_UNLOCK(fdvp);
140805e8ab62SJason A. Harmening 		error = ENOENT;
140905e8ab62SJason A. Harmening 		goto unionfs_rename_abort;
141005e8ab62SJason A. Harmening 	}
1411d00947d8SCraig Rodrigues #ifdef UNIONFS_IDBG_RENAME
1412312d49efSJason A. Harmening 	UNIONFS_INTERNAL_DEBUG("fdvp=%p, ufdvp=%p, lfdvp=%p\n",
1413312d49efSJason A. Harmening 	    fdvp, unp->un_uppervp, unp->un_lowervp);
1414d00947d8SCraig Rodrigues #endif
1415d00947d8SCraig Rodrigues 	if (unp->un_uppervp == NULLVP) {
1416d00947d8SCraig Rodrigues 		error = ENODEV;
141705e8ab62SJason A. Harmening 	} else {
1418d00947d8SCraig Rodrigues 		rfdvp = unp->un_uppervp;
1419d00947d8SCraig Rodrigues 		vref(rfdvp);
142005e8ab62SJason A. Harmening 	}
142105e8ab62SJason A. Harmening 	if (fdvp != tdvp)
142205e8ab62SJason A. Harmening 		VI_UNLOCK(fdvp);
142305e8ab62SJason A. Harmening 	if (error != 0)
142405e8ab62SJason A. Harmening 		goto unionfs_rename_abort;
1425d00947d8SCraig Rodrigues 
142605e8ab62SJason A. Harmening 	VI_LOCK(fvp);
1427d00947d8SCraig Rodrigues 	unp = VTOUNIONFS(fvp);
142805e8ab62SJason A. Harmening 	if (unp == NULL) {
142905e8ab62SJason A. Harmening 		VI_UNLOCK(fvp);
143005e8ab62SJason A. Harmening 		error = ENOENT;
143105e8ab62SJason A. Harmening 		goto unionfs_rename_abort;
143205e8ab62SJason A. Harmening 	}
143305e8ab62SJason A. Harmening 
1434d00947d8SCraig Rodrigues #ifdef UNIONFS_IDBG_RENAME
1435312d49efSJason A. Harmening 	UNIONFS_INTERNAL_DEBUG("fvp=%p, ufvp=%p, lfvp=%p\n",
1436312d49efSJason A. Harmening 	    fvp, unp->un_uppervp, unp->un_lowervp);
1437d00947d8SCraig Rodrigues #endif
143805e8ab62SJason A. Harmening 	/*
143905e8ab62SJason A. Harmening 	 * If we only have a lower vnode, copy the source file to the upper
144005e8ab62SJason A. Harmening 	 * FS so that the rename operation can be issued against the upper FS.
144105e8ab62SJason A. Harmening 	 */
1442d00947d8SCraig Rodrigues 	if (unp->un_uppervp == NULLVP) {
144305e8ab62SJason A. Harmening 		bool unlock_fdvp = false, relock_tdvp = false;
144405e8ab62SJason A. Harmening 		VI_UNLOCK(fvp);
144505e8ab62SJason A. Harmening 		if (tvp != NULLVP)
144605e8ab62SJason A. Harmening 			VOP_UNLOCK(tvp);
144705e8ab62SJason A. Harmening 		if (fvp->v_type == VREG) {
144805e8ab62SJason A. Harmening 			/*
144905e8ab62SJason A. Harmening 			 * For regular files, unionfs_copyfile() will expect
145005e8ab62SJason A. Harmening 			 * fdvp's upper parent directory vnode to be unlocked
145105e8ab62SJason A. Harmening 			 * and will temporarily lock it.  If fdvp == tdvp, we
145205e8ab62SJason A. Harmening 			 * should unlock tdvp to avoid recursion on tdvp's
145305e8ab62SJason A. Harmening 			 * lock.  If fdvp != tdvp, we should also unlock tdvp
145405e8ab62SJason A. Harmening 			 * to avoid potential deadlock due to holding tdvp's
145505e8ab62SJason A. Harmening 			 * lock while locking unrelated vnodes associated with
145605e8ab62SJason A. Harmening 			 * fdvp/fvp.
145705e8ab62SJason A. Harmening 			 */
145805e8ab62SJason A. Harmening 			VOP_UNLOCK(tdvp);
145905e8ab62SJason A. Harmening 			relock_tdvp = true;
146005e8ab62SJason A. Harmening 		} else if (fvp->v_type == VDIR && tdvp != fdvp) {
146105e8ab62SJason A. Harmening 			/*
146205e8ab62SJason A. Harmening 			 * For directories, unionfs_mkshadowdir() will expect
146305e8ab62SJason A. Harmening 			 * fdvp's upper parent directory vnode to be locked
146405e8ab62SJason A. Harmening 			 * and will temporarily unlock it.  If fdvp == tdvp,
146505e8ab62SJason A. Harmening 			 * we can therefore leave tdvp locked.  If fdvp !=
146605e8ab62SJason A. Harmening 			 * tdvp, we should exchange the lock on tdvp for a
146705e8ab62SJason A. Harmening 			 * lock on fdvp.
146805e8ab62SJason A. Harmening 			 */
146905e8ab62SJason A. Harmening 			VOP_UNLOCK(tdvp);
147005e8ab62SJason A. Harmening 			unlock_fdvp = true;
147105e8ab62SJason A. Harmening 			relock_tdvp = true;
147205e8ab62SJason A. Harmening 			vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY);
147305e8ab62SJason A. Harmening 		}
147405e8ab62SJason A. Harmening 		vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY);
147505e8ab62SJason A. Harmening 		unp = VTOUNIONFS(fvp);
147605e8ab62SJason A. Harmening 		if (unp == NULL)
147705e8ab62SJason A. Harmening 			error = ENOENT;
147805e8ab62SJason A. Harmening 		else if (unp->un_uppervp == NULLVP) {
14792a31267eSMatthew Dillon 			switch (fvp->v_type) {
14802a31267eSMatthew Dillon 			case VREG:
1481eb60ff1eSJason A. Harmening 				error = unionfs_copyfile(fvp, 1, fcnp->cn_cred, td);
14822a31267eSMatthew Dillon 				break;
14832a31267eSMatthew Dillon 			case VDIR:
1484eb60ff1eSJason A. Harmening 				error = unionfs_mkshadowdir(fdvp, fvp, fcnp, td);
14852a31267eSMatthew Dillon 				break;
14862a31267eSMatthew Dillon 			default:
1487d00947d8SCraig Rodrigues 				error = ENODEV;
148805e8ab62SJason A. Harmening 				break;
1489d00947d8SCraig Rodrigues 			}
149005e8ab62SJason A. Harmening 		}
149105e8ab62SJason A. Harmening 		VOP_UNLOCK(fvp);
149205e8ab62SJason A. Harmening 		if (unlock_fdvp)
149305e8ab62SJason A. Harmening 			VOP_UNLOCK(fdvp);
149405e8ab62SJason A. Harmening 		if (relock_tdvp)
149505e8ab62SJason A. Harmening 			vn_lock(tdvp, LK_EXCLUSIVE | LK_RETRY);
149605e8ab62SJason A. Harmening 		if (tvp != NULLVP)
149705e8ab62SJason A. Harmening 			vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY);
149805e8ab62SJason A. Harmening 		/*
149905e8ab62SJason A. Harmening 		 * Since we've dropped tdvp's lock at some point in the copy
150005e8ab62SJason A. Harmening 		 * sequence above, force the caller to re-drive the lookup
150105e8ab62SJason A. Harmening 		 * in case the relationship between tdvp and tvp has changed.
150205e8ab62SJason A. Harmening 		 */
150305e8ab62SJason A. Harmening 		if (error == 0)
150405e8ab62SJason A. Harmening 			error = ERELOOKUP;
150505e8ab62SJason A. Harmening 		goto unionfs_rename_abort;
1506d00947d8SCraig Rodrigues 	}
1507d00947d8SCraig Rodrigues 
1508d00947d8SCraig Rodrigues 	if (unp->un_lowervp != NULLVP)
1509d00947d8SCraig Rodrigues 		fcnp->cn_flags |= DOWHITEOUT;
1510d00947d8SCraig Rodrigues 	rfvp = unp->un_uppervp;
1511d00947d8SCraig Rodrigues 	vref(rfvp);
1512d00947d8SCraig Rodrigues 
151305e8ab62SJason A. Harmening 	VI_UNLOCK(fvp);
151405e8ab62SJason A. Harmening 
1515d00947d8SCraig Rodrigues 	unp = VTOUNIONFS(tdvp);
151605e8ab62SJason A. Harmening 
1517d00947d8SCraig Rodrigues #ifdef UNIONFS_IDBG_RENAME
1518312d49efSJason A. Harmening 	UNIONFS_INTERNAL_DEBUG("tdvp=%p, utdvp=%p, ltdvp=%p\n",
1519312d49efSJason A. Harmening 	    tdvp, unp->un_uppervp, unp->un_lowervp);
1520d00947d8SCraig Rodrigues #endif
1521d00947d8SCraig Rodrigues 	if (unp->un_uppervp == NULLVP) {
1522d00947d8SCraig Rodrigues 		error = ENODEV;
1523d00947d8SCraig Rodrigues 		goto unionfs_rename_abort;
1524d00947d8SCraig Rodrigues 	}
1525d00947d8SCraig Rodrigues 	rtdvp = unp->un_uppervp;
1526d00947d8SCraig Rodrigues 	vref(rtdvp);
1527d00947d8SCraig Rodrigues 
152805e8ab62SJason A. Harmening 	if (tvp != NULLVP) {
1529d00947d8SCraig Rodrigues 		unp = VTOUNIONFS(tvp);
153005e8ab62SJason A. Harmening 		if (unp == NULL) {
153105e8ab62SJason A. Harmening 			error = ENOENT;
153205e8ab62SJason A. Harmening 			goto unionfs_rename_abort;
153305e8ab62SJason A. Harmening 		}
1534d00947d8SCraig Rodrigues #ifdef UNIONFS_IDBG_RENAME
1535312d49efSJason A. Harmening 		UNIONFS_INTERNAL_DEBUG("tvp=%p, utvp=%p, ltvp=%p\n",
1536312d49efSJason A. Harmening 		    tvp, unp->un_uppervp, unp->un_lowervp);
1537d00947d8SCraig Rodrigues #endif
1538d00947d8SCraig Rodrigues 		if (unp->un_uppervp == NULLVP)
1539d00947d8SCraig Rodrigues 			rtvp = NULLVP;
1540d00947d8SCraig Rodrigues 		else {
1541d00947d8SCraig Rodrigues 			if (tvp->v_type == VDIR) {
1542d00947d8SCraig Rodrigues 				error = EINVAL;
1543d00947d8SCraig Rodrigues 				goto unionfs_rename_abort;
1544d00947d8SCraig Rodrigues 			}
1545d00947d8SCraig Rodrigues 			rtvp = unp->un_uppervp;
1546d00947d8SCraig Rodrigues 			vref(rtvp);
1547df8bae1dSRodney W. Grimes 		}
15482a31267eSMatthew Dillon 	}
1549df8bae1dSRodney W. Grimes 
15505307411cSDaichi GOTO 	if (rfvp == rtvp)
15515307411cSDaichi GOTO 		goto unionfs_rename_abort;
15525307411cSDaichi GOTO 
1553d00947d8SCraig Rodrigues 	error = VOP_RENAME(rfdvp, rfvp, fcnp, rtdvp, rtvp, tcnp);
15542a31267eSMatthew Dillon 
1555dc2dd185SDaichi GOTO 	if (error == 0) {
1556dc2dd185SDaichi GOTO 		if (rtvp != NULLVP && rtvp->v_type == VDIR)
1557dc2dd185SDaichi GOTO 			cache_purge(tdvp);
1558dc2dd185SDaichi GOTO 		if (fvp->v_type == VDIR && fdvp != tdvp)
1559dc2dd185SDaichi GOTO 			cache_purge(fdvp);
1560dc2dd185SDaichi GOTO 	}
1561dc2dd185SDaichi GOTO 
1562d00947d8SCraig Rodrigues 	if (tdvp != rtdvp)
1563d00947d8SCraig Rodrigues 		vrele(tdvp);
1564d00947d8SCraig Rodrigues 	if (tvp != rtvp && tvp != NULLVP) {
1565d00947d8SCraig Rodrigues 		if (rtvp == NULLVP)
1566df8bae1dSRodney W. Grimes 			vput(tvp);
15672a31267eSMatthew Dillon 		else
15682a31267eSMatthew Dillon 			vrele(tvp);
15692a31267eSMatthew Dillon 	}
15705307411cSDaichi GOTO 	if (fdvp != rfdvp)
15715307411cSDaichi GOTO 		vrele(fdvp);
15725307411cSDaichi GOTO 	if (fvp != rfvp)
15735307411cSDaichi GOTO 		vrele(fvp);
1574d00947d8SCraig Rodrigues 
1575d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_rename: leave (%d)\n", error);
1576d00947d8SCraig Rodrigues 
1577d00947d8SCraig Rodrigues 	return (error);
1578d00947d8SCraig Rodrigues 
1579d00947d8SCraig Rodrigues unionfs_rename_abort:
15805307411cSDaichi GOTO 	vput(tdvp);
1581d00947d8SCraig Rodrigues 	if (tdvp != rtdvp)
1582d00947d8SCraig Rodrigues 		vrele(rtdvp);
1583d00947d8SCraig Rodrigues 	if (tvp != NULLVP) {
1584d00947d8SCraig Rodrigues 		if (tdvp != tvp)
1585d00947d8SCraig Rodrigues 			vput(tvp);
1586d00947d8SCraig Rodrigues 		else
1587d00947d8SCraig Rodrigues 			vrele(tvp);
1588d00947d8SCraig Rodrigues 	}
15895307411cSDaichi GOTO 	if (tvp != rtvp && rtvp != NULLVP)
15905307411cSDaichi GOTO 		vrele(rtvp);
15915307411cSDaichi GOTO 	if (fdvp != rfdvp)
15925307411cSDaichi GOTO 		vrele(rfdvp);
15935307411cSDaichi GOTO 	if (fvp != rfvp)
15945307411cSDaichi GOTO 		vrele(rfvp);
1595d00947d8SCraig Rodrigues 	vrele(fdvp);
1596d00947d8SCraig Rodrigues 	vrele(fvp);
1597d00947d8SCraig Rodrigues 
1598d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_rename: leave (%d)\n", error);
1599d00947d8SCraig Rodrigues 
1600df8bae1dSRodney W. Grimes 	return (error);
1601df8bae1dSRodney W. Grimes }
1602df8bae1dSRodney W. Grimes 
1603c9bf0111SKATO Takenori static int
unionfs_mkdir(struct vop_mkdir_args * ap)1604d00947d8SCraig Rodrigues unionfs_mkdir(struct vop_mkdir_args *ap)
1605df8bae1dSRodney W. Grimes {
1606d00947d8SCraig Rodrigues 	struct unionfs_node *dunp;
1607d00947d8SCraig Rodrigues 	struct componentname *cnp;
160893fe61afSJason A. Harmening 	struct vnode   *dvp;
1609d00947d8SCraig Rodrigues 	struct vnode   *udvp;
1610d00947d8SCraig Rodrigues 	struct vnode   *uvp;
1611d00947d8SCraig Rodrigues 	struct vattr	va;
1612312d49efSJason A. Harmening 	int		error;
1613312d49efSJason A. Harmening 	int		lkflags;
1614df8bae1dSRodney W. Grimes 
1615d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_mkdir: enter\n");
1616d00947d8SCraig Rodrigues 
16171e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_dvp);
16181e5da15aSDaichi GOTO 
1619d00947d8SCraig Rodrigues 	error = EROFS;
162093fe61afSJason A. Harmening 	dvp = ap->a_dvp;
162193fe61afSJason A. Harmening 	dunp = VTOUNIONFS(dvp);
1622d00947d8SCraig Rodrigues 	cnp = ap->a_cnp;
162398155f1fSCraig Rodrigues 	lkflags = cnp->cn_lkflags;
1624d00947d8SCraig Rodrigues 	udvp = dunp->un_uppervp;
1625d00947d8SCraig Rodrigues 
1626d00947d8SCraig Rodrigues 	if (udvp != NULLVP) {
1627d00947d8SCraig Rodrigues 		/* check opaque */
1628d00947d8SCraig Rodrigues 		if (!(cnp->cn_flags & ISWHITEOUT)) {
16290359a12eSAttilio Rao 			error = VOP_GETATTR(udvp, &va, cnp->cn_cred);
1630d00947d8SCraig Rodrigues 			if (error != 0)
163193fe61afSJason A. Harmening 				goto unionfs_mkdir_cleanup;
1632152c35eeSJason A. Harmening 			if ((va.va_flags & OPAQUE) != 0)
1633d00947d8SCraig Rodrigues 				cnp->cn_flags |= ISWHITEOUT;
1634d00947d8SCraig Rodrigues 		}
1635d00947d8SCraig Rodrigues 
16366c8ded00SJason A. Harmening 		int udvp_lkflags;
16376c8ded00SJason A. Harmening 		bool uvp_created = false;
16386c8ded00SJason A. Harmening 		unionfs_forward_vop_start(udvp, &udvp_lkflags);
16396c8ded00SJason A. Harmening 		error = VOP_MKDIR(udvp, &uvp, cnp, ap->a_vap);
16406c8ded00SJason A. Harmening 		if (error == 0)
16416c8ded00SJason A. Harmening 			uvp_created = true;
16426c8ded00SJason A. Harmening 		if (__predict_false(unionfs_forward_vop_finish(dvp, udvp,
16436c8ded00SJason A. Harmening 		    udvp_lkflags)) && error == 0)
16446c8ded00SJason A. Harmening 			error = ENOENT;
16456c8ded00SJason A. Harmening 		if (error == 0) {
1646b249ce48SMateusz Guzik 			VOP_UNLOCK(uvp);
164798155f1fSCraig Rodrigues 			cnp->cn_lkflags = LK_EXCLUSIVE;
164893fe61afSJason A. Harmening 			error = unionfs_nodeget(dvp->v_mount, uvp, NULLVP,
164993fe61afSJason A. Harmening 			    dvp, ap->a_vpp, cnp);
1650d00947d8SCraig Rodrigues 			vrele(uvp);
16516c8ded00SJason A. Harmening 			cnp->cn_lkflags = lkflags;
16526c8ded00SJason A. Harmening 		} else if (uvp_created)
16536c8ded00SJason A. Harmening 			vput(uvp);
1654d00947d8SCraig Rodrigues 	}
1655d00947d8SCraig Rodrigues 
165693fe61afSJason A. Harmening unionfs_mkdir_cleanup:
1657d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_mkdir: leave (%d)\n", error);
1658d00947d8SCraig Rodrigues 
1659d00947d8SCraig Rodrigues 	return (error);
1660d00947d8SCraig Rodrigues }
1661d00947d8SCraig Rodrigues 
1662d00947d8SCraig Rodrigues static int
unionfs_rmdir(struct vop_rmdir_args * ap)1663d00947d8SCraig Rodrigues unionfs_rmdir(struct vop_rmdir_args *ap)
1664d00947d8SCraig Rodrigues {
1665d00947d8SCraig Rodrigues 	struct unionfs_node *dunp;
1666d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
166720885defSDaichi GOTO 	struct unionfs_mount *ump;
1668d00947d8SCraig Rodrigues 	struct componentname *cnp;
1669d00947d8SCraig Rodrigues 	struct thread  *td;
1670d00947d8SCraig Rodrigues 	struct vnode   *udvp;
1671d00947d8SCraig Rodrigues 	struct vnode   *uvp;
1672d00947d8SCraig Rodrigues 	struct vnode   *lvp;
1673312d49efSJason A. Harmening 	int		error;
1674d00947d8SCraig Rodrigues 
1675d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_rmdir: enter\n");
1676d00947d8SCraig Rodrigues 
16771e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_dvp);
16781e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_vp);
16791e5da15aSDaichi GOTO 
1680d00947d8SCraig Rodrigues 	error = 0;
1681d00947d8SCraig Rodrigues 	dunp = VTOUNIONFS(ap->a_dvp);
1682d00947d8SCraig Rodrigues 	unp = VTOUNIONFS(ap->a_vp);
1683d00947d8SCraig Rodrigues 	cnp = ap->a_cnp;
1684d00947d8SCraig Rodrigues 	td = curthread;
1685d00947d8SCraig Rodrigues 	udvp = dunp->un_uppervp;
1686d00947d8SCraig Rodrigues 	uvp = unp->un_uppervp;
1687d00947d8SCraig Rodrigues 	lvp = unp->un_lowervp;
1688d00947d8SCraig Rodrigues 
1689d00947d8SCraig Rodrigues 	if (udvp == NULLVP)
1690d00947d8SCraig Rodrigues 		return (EROFS);
1691d00947d8SCraig Rodrigues 
1692d00947d8SCraig Rodrigues 	if (udvp == uvp)
1693d00947d8SCraig Rodrigues 		return (EOPNOTSUPP);
1694d00947d8SCraig Rodrigues 
1695d00947d8SCraig Rodrigues 	if (uvp != NULLVP) {
1696d00947d8SCraig Rodrigues 		if (lvp != NULLVP) {
1697eb60ff1eSJason A. Harmening 			/*
1698eb60ff1eSJason A. Harmening 			 * We need to keep dvp and vp's upper vnodes locked
1699eb60ff1eSJason A. Harmening 			 * going into the VOP_RMDIR() call, but the empty
1700eb60ff1eSJason A. Harmening 			 * directory check also requires the lower vnode lock.
1701eb60ff1eSJason A. Harmening 			 * For this third, cross-filesystem lock we use a
1702eb60ff1eSJason A. Harmening 			 * similar approach taken by various FS' VOP_RENAME
1703eb60ff1eSJason A. Harmening 			 * implementations (which require 2-4 vnode locks).
1704eb60ff1eSJason A. Harmening 			 * First we attempt a NOWAIT acquisition, then if
1705eb60ff1eSJason A. Harmening 			 * that fails we drops the other two vnode locks,
1706eb60ff1eSJason A. Harmening 			 * acquire lvp's lock in the normal fashion to reduce
1707eb60ff1eSJason A. Harmening 			 * the likelihood of spinning on it in the future,
1708eb60ff1eSJason A. Harmening 			 * then drop, reacquire the other locks, and return
1709eb60ff1eSJason A. Harmening 			 * ERELOOKUP to re-drive the lookup in case the dvp->
1710eb60ff1eSJason A. Harmening 			 * vp relationship has changed.
1711eb60ff1eSJason A. Harmening 			 */
1712eb60ff1eSJason A. Harmening 			if (vn_lock(lvp, LK_SHARED | LK_NOWAIT) != 0) {
1713eb60ff1eSJason A. Harmening 				VOP_UNLOCK(ap->a_vp);
1714eb60ff1eSJason A. Harmening 				VOP_UNLOCK(ap->a_dvp);
1715eb60ff1eSJason A. Harmening 				vn_lock(lvp, LK_SHARED | LK_RETRY);
1716eb60ff1eSJason A. Harmening 				VOP_UNLOCK(lvp);
1717eb60ff1eSJason A. Harmening 				vn_lock(ap->a_dvp, LK_EXCLUSIVE | LK_RETRY);
1718eb60ff1eSJason A. Harmening 				vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY);
1719eb60ff1eSJason A. Harmening 				return (ERELOOKUP);
1720eb60ff1eSJason A. Harmening 			}
1721d00947d8SCraig Rodrigues 			error = unionfs_check_rmdir(ap->a_vp, cnp->cn_cred, td);
1722eb60ff1eSJason A. Harmening 			/*
1723eb60ff1eSJason A. Harmening 			 * It's possible for a direct operation on the lower FS
1724eb60ff1eSJason A. Harmening 			 * to make the lower directory non-empty after we drop
1725eb60ff1eSJason A. Harmening 			 * the lock, but it's also possible for the upper-layer
1726eb60ff1eSJason A. Harmening 			 * VOP_RMDIR to relock udvp/uvp which would lead to
1727eb60ff1eSJason A. Harmening 			 * LOR if we kept lvp locked across that call.
1728eb60ff1eSJason A. Harmening 			 */
1729eb60ff1eSJason A. Harmening 			VOP_UNLOCK(lvp);
1730d00947d8SCraig Rodrigues 			if (error != 0)
1731d00947d8SCraig Rodrigues 				return (error);
1732d00947d8SCraig Rodrigues 		}
173320885defSDaichi GOTO 		ump = MOUNTTOUNIONFSMOUNT(ap->a_vp->v_mount);
173420885defSDaichi GOTO 		if (ump->um_whitemode == UNIONFS_WHITE_ALWAYS || lvp != NULLVP)
1735*2ed053cdSJason A. Harmening 			cnp->cn_flags |= (DOWHITEOUT | IGNOREWHITEOUT);
17366c8ded00SJason A. Harmening 		int udvp_lkflags, uvp_lkflags;
17376c8ded00SJason A. Harmening 		unionfs_forward_vop_start_pair(udvp, &udvp_lkflags,
17386c8ded00SJason A. Harmening 		    uvp, &uvp_lkflags);
1739d00947d8SCraig Rodrigues 		error = VOP_RMDIR(udvp, uvp, cnp);
17406c8ded00SJason A. Harmening 		unionfs_forward_vop_finish_pair(ap->a_dvp, udvp, udvp_lkflags,
17416c8ded00SJason A. Harmening 		    ap->a_vp, uvp, uvp_lkflags);
1742eb60ff1eSJason A. Harmening 	} else if (lvp != NULLVP) {
1743eb60ff1eSJason A. Harmening 		error = unionfs_mkwhiteout(ap->a_dvp, ap->a_vp, cnp, td,
1744abe95116SJason A. Harmening 		    unp->un_path, unp->un_pathlen);
1745eb60ff1eSJason A. Harmening 	}
1746d00947d8SCraig Rodrigues 
1747dc2dd185SDaichi GOTO 	if (error == 0) {
1748dc2dd185SDaichi GOTO 		cache_purge(ap->a_dvp);
1749dc2dd185SDaichi GOTO 		cache_purge(ap->a_vp);
1750dc2dd185SDaichi GOTO 	}
1751dc2dd185SDaichi GOTO 
1752d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_rmdir: leave (%d)\n", error);
1753d00947d8SCraig Rodrigues 
1754d00947d8SCraig Rodrigues 	return (error);
1755d00947d8SCraig Rodrigues }
1756d00947d8SCraig Rodrigues 
1757d00947d8SCraig Rodrigues static int
unionfs_symlink(struct vop_symlink_args * ap)1758d00947d8SCraig Rodrigues unionfs_symlink(struct vop_symlink_args *ap)
1759d00947d8SCraig Rodrigues {
1760d00947d8SCraig Rodrigues 	struct unionfs_node *dunp;
1761d00947d8SCraig Rodrigues 	struct componentname *cnp;
1762d00947d8SCraig Rodrigues 	struct vnode   *udvp;
1763d00947d8SCraig Rodrigues 	struct vnode   *uvp;
1764312d49efSJason A. Harmening 	int		error;
1765312d49efSJason A. Harmening 	int		lkflags;
1766d00947d8SCraig Rodrigues 
1767d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_symlink: enter\n");
1768d00947d8SCraig Rodrigues 
17691e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_dvp);
17701e5da15aSDaichi GOTO 
1771d00947d8SCraig Rodrigues 	error = EROFS;
1772d00947d8SCraig Rodrigues 	dunp = VTOUNIONFS(ap->a_dvp);
1773d00947d8SCraig Rodrigues 	cnp = ap->a_cnp;
177498155f1fSCraig Rodrigues 	lkflags = cnp->cn_lkflags;
1775d00947d8SCraig Rodrigues 	udvp = dunp->un_uppervp;
1776d00947d8SCraig Rodrigues 
1777d00947d8SCraig Rodrigues 	if (udvp != NULLVP) {
17786c8ded00SJason A. Harmening 		int udvp_lkflags;
17796c8ded00SJason A. Harmening 		bool uvp_created = false;
17806c8ded00SJason A. Harmening 		unionfs_forward_vop_start(udvp, &udvp_lkflags);
1781d00947d8SCraig Rodrigues 		error = VOP_SYMLINK(udvp, &uvp, cnp, ap->a_vap, ap->a_target);
17826c8ded00SJason A. Harmening 		if (error == 0)
17836c8ded00SJason A. Harmening 			uvp_created = true;
17846c8ded00SJason A. Harmening 		if (__predict_false(unionfs_forward_vop_finish(ap->a_dvp, udvp,
17856c8ded00SJason A. Harmening 		    udvp_lkflags)) && error == 0)
17866c8ded00SJason A. Harmening 			error = ENOENT;
1787d00947d8SCraig Rodrigues 		if (error == 0) {
1788b249ce48SMateusz Guzik 			VOP_UNLOCK(uvp);
178998155f1fSCraig Rodrigues 			cnp->cn_lkflags = LK_EXCLUSIVE;
1790d00947d8SCraig Rodrigues 			error = unionfs_nodeget(ap->a_dvp->v_mount, uvp, NULLVP,
17916d8420d4SJason A. Harmening 			    ap->a_dvp, ap->a_vpp, cnp);
1792d00947d8SCraig Rodrigues 			vrele(uvp);
17936c8ded00SJason A. Harmening 			cnp->cn_lkflags = lkflags;
17946c8ded00SJason A. Harmening 		} else if (uvp_created)
17956c8ded00SJason A. Harmening 			vput(uvp);
1796d00947d8SCraig Rodrigues 	}
1797d00947d8SCraig Rodrigues 
1798d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_symlink: leave (%d)\n", error);
1799d00947d8SCraig Rodrigues 
1800d00947d8SCraig Rodrigues 	return (error);
1801d00947d8SCraig Rodrigues }
1802d00947d8SCraig Rodrigues 
1803d00947d8SCraig Rodrigues static int
unionfs_readdir(struct vop_readdir_args * ap)1804d00947d8SCraig Rodrigues unionfs_readdir(struct vop_readdir_args *ap)
1805d00947d8SCraig Rodrigues {
1806d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
1807d00947d8SCraig Rodrigues 	struct unionfs_node_status *unsp;
1808d00947d8SCraig Rodrigues 	struct uio     *uio;
1809cb5736b7SDaichi GOTO 	struct vnode   *vp;
1810d00947d8SCraig Rodrigues 	struct vnode   *uvp;
1811d00947d8SCraig Rodrigues 	struct vnode   *lvp;
1812d00947d8SCraig Rodrigues 	struct thread  *td;
1813d00947d8SCraig Rodrigues 	struct vattr    va;
1814d00947d8SCraig Rodrigues 
1815b214fcceSAlan Somers 	uint64_t	*cookies_bk;
1816312d49efSJason A. Harmening 	int		error;
1817312d49efSJason A. Harmening 	int		eofflag;
1818eb60ff1eSJason A. Harmening 	int		lkflags;
1819312d49efSJason A. Harmening 	int		ncookies_bk;
1820312d49efSJason A. Harmening 	int		uio_offset_bk;
182139a2dc44SJason A. Harmening 	enum unionfs_lkupgrade lkstatus;
1822d00947d8SCraig Rodrigues 
1823d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_readdir: enter\n");
1824d00947d8SCraig Rodrigues 
18251e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_vp);
18261e5da15aSDaichi GOTO 
1827d00947d8SCraig Rodrigues 	error = 0;
1828d00947d8SCraig Rodrigues 	eofflag = 0;
1829cb5736b7SDaichi GOTO 	uio_offset_bk = 0;
1830d00947d8SCraig Rodrigues 	uio = ap->a_uio;
1831cb5736b7SDaichi GOTO 	uvp = NULLVP;
1832cb5736b7SDaichi GOTO 	lvp = NULLVP;
1833d00947d8SCraig Rodrigues 	td = uio->uio_td;
1834d00947d8SCraig Rodrigues 	ncookies_bk = 0;
1835d00947d8SCraig Rodrigues 	cookies_bk = NULL;
1836d00947d8SCraig Rodrigues 
1837cb5736b7SDaichi GOTO 	vp = ap->a_vp;
1838cb5736b7SDaichi GOTO 	if (vp->v_type != VDIR)
1839d00947d8SCraig Rodrigues 		return (ENOTDIR);
1840d00947d8SCraig Rodrigues 
184139a2dc44SJason A. Harmening 	/*
184239a2dc44SJason A. Harmening 	 * If the vnode is reclaimed while upgrading, we can't safely use unp
184339a2dc44SJason A. Harmening 	 * or do anything else unionfs- specific.
184439a2dc44SJason A. Harmening 	 */
184539a2dc44SJason A. Harmening 	lkstatus = unionfs_upgrade_lock(vp);
184639a2dc44SJason A. Harmening 	if (lkstatus == UNIONFS_LKUPGRADE_DOOMED)
1847cb5736b7SDaichi GOTO 		error = EBADF;
184839a2dc44SJason A. Harmening 	if (error == 0) {
184939a2dc44SJason A. Harmening 		unp = VTOUNIONFS(vp);
1850cb5736b7SDaichi GOTO 		uvp = unp->un_uppervp;
1851cb5736b7SDaichi GOTO 		lvp = unp->un_lowervp;
185239a2dc44SJason A. Harmening 		/* check the open count. unionfs needs open before readdir. */
1853cb5736b7SDaichi GOTO 		unionfs_get_node_status(unp, td, &unsp);
1854cb5736b7SDaichi GOTO 		if ((uvp != NULLVP && unsp->uns_upper_opencnt <= 0) ||
1855cb5736b7SDaichi GOTO 			(lvp != NULLVP && unsp->uns_lower_opencnt <= 0)) {
1856cb5736b7SDaichi GOTO 			unionfs_tryrem_node_status(unp, unsp);
1857cb5736b7SDaichi GOTO 			error = EBADF;
1858cb5736b7SDaichi GOTO 		}
1859cb5736b7SDaichi GOTO 	}
186039a2dc44SJason A. Harmening 	unionfs_downgrade_lock(vp, lkstatus);
1861cb5736b7SDaichi GOTO 	if (error != 0)
1862cb5736b7SDaichi GOTO 		goto unionfs_readdir_exit;
1863cb5736b7SDaichi GOTO 
1864d00947d8SCraig Rodrigues 	/* check opaque */
1865d00947d8SCraig Rodrigues 	if (uvp != NULLVP && lvp != NULLVP) {
18660359a12eSAttilio Rao 		if ((error = VOP_GETATTR(uvp, &va, ap->a_cred)) != 0)
18675adc4080SDaichi GOTO 			goto unionfs_readdir_exit;
1868d00947d8SCraig Rodrigues 		if (va.va_flags & OPAQUE)
1869d00947d8SCraig Rodrigues 			lvp = NULLVP;
1870d00947d8SCraig Rodrigues 	}
1871d00947d8SCraig Rodrigues 
1872d00947d8SCraig Rodrigues 	/* upper only */
1873d00947d8SCraig Rodrigues 	if (uvp != NULLVP && lvp == NULLVP) {
1874eb60ff1eSJason A. Harmening 		unionfs_forward_vop_start(uvp, &lkflags);
1875d00947d8SCraig Rodrigues 		error = VOP_READDIR(uvp, uio, ap->a_cred, ap->a_eofflag,
1876d00947d8SCraig Rodrigues 		    ap->a_ncookies, ap->a_cookies);
1877eb60ff1eSJason A. Harmening 		if (unionfs_forward_vop_finish(vp, uvp, lkflags))
1878eb60ff1eSJason A. Harmening 			error = error ? error : ENOENT;
1879eb60ff1eSJason A. Harmening 		else
1880d00947d8SCraig Rodrigues 			unsp->uns_readdir_status = 0;
1881d00947d8SCraig Rodrigues 
1882d00947d8SCraig Rodrigues 		goto unionfs_readdir_exit;
1883d00947d8SCraig Rodrigues 	}
1884d00947d8SCraig Rodrigues 
1885d00947d8SCraig Rodrigues 	/* lower only */
1886d00947d8SCraig Rodrigues 	if (uvp == NULLVP && lvp != NULLVP) {
1887eb60ff1eSJason A. Harmening 		unionfs_forward_vop_start(lvp, &lkflags);
1888d00947d8SCraig Rodrigues 		error = VOP_READDIR(lvp, uio, ap->a_cred, ap->a_eofflag,
1889d00947d8SCraig Rodrigues 		    ap->a_ncookies, ap->a_cookies);
1890eb60ff1eSJason A. Harmening 		if (unionfs_forward_vop_finish(vp, lvp, lkflags))
1891eb60ff1eSJason A. Harmening 			error = error ? error : ENOENT;
1892eb60ff1eSJason A. Harmening 		else
1893d00947d8SCraig Rodrigues 			unsp->uns_readdir_status = 2;
1894d00947d8SCraig Rodrigues 
1895d00947d8SCraig Rodrigues 		goto unionfs_readdir_exit;
1896d00947d8SCraig Rodrigues 	}
1897d00947d8SCraig Rodrigues 
1898d00947d8SCraig Rodrigues 	/*
1899d00947d8SCraig Rodrigues 	 * readdir upper and lower
1900d00947d8SCraig Rodrigues 	 */
19016c98d0e9SDaichi GOTO 	KASSERT(uvp != NULLVP, ("unionfs_readdir: null upper vp"));
19026c98d0e9SDaichi GOTO 	KASSERT(lvp != NULLVP, ("unionfs_readdir: null lower vp"));
1903eb60ff1eSJason A. Harmening 
1904d00947d8SCraig Rodrigues 	if (uio->uio_offset == 0)
1905d00947d8SCraig Rodrigues 		unsp->uns_readdir_status = 0;
1906d00947d8SCraig Rodrigues 
1907d00947d8SCraig Rodrigues 	if (unsp->uns_readdir_status == 0) {
1908d00947d8SCraig Rodrigues 		/* read upper */
1909eb60ff1eSJason A. Harmening 		unionfs_forward_vop_start(uvp, &lkflags);
1910d00947d8SCraig Rodrigues 		error = VOP_READDIR(uvp, uio, ap->a_cred, &eofflag,
1911d00947d8SCraig Rodrigues 				    ap->a_ncookies, ap->a_cookies);
1912eb60ff1eSJason A. Harmening 		if (unionfs_forward_vop_finish(vp, uvp, lkflags) && error == 0)
1913eb60ff1eSJason A. Harmening 			error = ENOENT;
19145adc4080SDaichi GOTO 		if (error != 0 || eofflag == 0)
19155adc4080SDaichi GOTO 			goto unionfs_readdir_exit;
1916d00947d8SCraig Rodrigues 		unsp->uns_readdir_status = 1;
1917d00947d8SCraig Rodrigues 
1918d00947d8SCraig Rodrigues 		/*
1919cb5736b7SDaichi GOTO 		 * UFS(and other FS) needs size of uio_resid larger than
1920d00947d8SCraig Rodrigues 		 * DIRBLKSIZ.
1921d00947d8SCraig Rodrigues 		 * size of DIRBLKSIZ equals DEV_BSIZE.
1922d00947d8SCraig Rodrigues 		 * (see: ufs/ufs/ufs_vnops.c ufs_readdir func , ufs/ufs/dir.h)
1923d00947d8SCraig Rodrigues 		 */
19245adc4080SDaichi GOTO 		if (uio->uio_resid <= (uio->uio_resid & (DEV_BSIZE -1)))
19255adc4080SDaichi GOTO 			goto unionfs_readdir_exit;
1926d00947d8SCraig Rodrigues 
1927d00947d8SCraig Rodrigues 		/*
1928cb5736b7SDaichi GOTO 		 * Backup cookies.
1929d00947d8SCraig Rodrigues 		 * It prepares to readdir in lower.
1930d00947d8SCraig Rodrigues 		 */
1931d00947d8SCraig Rodrigues 		if (ap->a_ncookies != NULL) {
1932d00947d8SCraig Rodrigues 			ncookies_bk = *(ap->a_ncookies);
1933d00947d8SCraig Rodrigues 			*(ap->a_ncookies) = 0;
1934d00947d8SCraig Rodrigues 		}
1935d00947d8SCraig Rodrigues 		if (ap->a_cookies != NULL) {
1936d00947d8SCraig Rodrigues 			cookies_bk = *(ap->a_cookies);
1937d00947d8SCraig Rodrigues 			*(ap->a_cookies) = NULL;
1938d00947d8SCraig Rodrigues 		}
1939d00947d8SCraig Rodrigues 	}
1940d00947d8SCraig Rodrigues 
1941d00947d8SCraig Rodrigues 	/* initialize for readdir in lower */
1942d00947d8SCraig Rodrigues 	if (unsp->uns_readdir_status == 1) {
1943d00947d8SCraig Rodrigues 		unsp->uns_readdir_status = 2;
1944cb5736b7SDaichi GOTO 		/*
1945cb5736b7SDaichi GOTO 		 * Backup uio_offset. See the comment after the
1946cb5736b7SDaichi GOTO 		 * VOP_READDIR call on the lower layer.
1947cb5736b7SDaichi GOTO 		 */
1948cb5736b7SDaichi GOTO 		uio_offset_bk = uio->uio_offset;
1949d00947d8SCraig Rodrigues 		uio->uio_offset = 0;
1950d00947d8SCraig Rodrigues 	}
1951d00947d8SCraig Rodrigues 
1952eb60ff1eSJason A. Harmening 	lvp = unionfs_lock_lvp(vp, &lkflags);
1953eb60ff1eSJason A. Harmening 	if (lvp == NULL) {
1954eb60ff1eSJason A. Harmening 		error = ENOENT;
1955b16f4eecSCraig Rodrigues 		goto unionfs_readdir_exit;
1956b16f4eecSCraig Rodrigues 	}
1957eb60ff1eSJason A. Harmening 
1958d00947d8SCraig Rodrigues 	/* read lower */
1959d00947d8SCraig Rodrigues 	error = VOP_READDIR(lvp, uio, ap->a_cred, ap->a_eofflag,
1960d00947d8SCraig Rodrigues 			    ap->a_ncookies, ap->a_cookies);
1961d00947d8SCraig Rodrigues 
1962eb60ff1eSJason A. Harmening 
1963eb60ff1eSJason A. Harmening 	unp = unionfs_unlock_lvp(vp, lvp, lkflags);
1964eb60ff1eSJason A. Harmening 	if (unp == NULL && error == 0)
1965eb60ff1eSJason A. Harmening 		error = ENOENT;
1966eb60ff1eSJason A. Harmening 
1967eb60ff1eSJason A. Harmening 
1968cb5736b7SDaichi GOTO 	/*
1969cb5736b7SDaichi GOTO 	 * We can't return an uio_offset of 0: this would trigger an
1970cb5736b7SDaichi GOTO 	 * infinite loop, because the next call to unionfs_readdir would
1971cb5736b7SDaichi GOTO 	 * always restart with the upper layer (uio_offset == 0) and
1972cb5736b7SDaichi GOTO 	 * always return some data.
1973cb5736b7SDaichi GOTO 	 *
1974cb5736b7SDaichi GOTO 	 * This happens when the lower layer root directory is removed.
1975cb5736b7SDaichi GOTO 	 * (A root directory deleting of unionfs should not be permitted.
1976cb5736b7SDaichi GOTO 	 *  But current VFS can not do it.)
1977cb5736b7SDaichi GOTO 	 */
1978cb5736b7SDaichi GOTO 	if (uio->uio_offset == 0)
1979cb5736b7SDaichi GOTO 		uio->uio_offset = uio_offset_bk;
1980cb5736b7SDaichi GOTO 
1981d00947d8SCraig Rodrigues 	if (cookies_bk != NULL) {
1982d00947d8SCraig Rodrigues 		/* merge cookies */
1983d00947d8SCraig Rodrigues 		int		size;
1984b214fcceSAlan Somers 		uint64_t         *newcookies, *pos;
1985d00947d8SCraig Rodrigues 
1986d00947d8SCraig Rodrigues 		size = *(ap->a_ncookies) + ncookies_bk;
1987b214fcceSAlan Somers 		newcookies = (uint64_t *) malloc(size * sizeof(*newcookies),
1988d00947d8SCraig Rodrigues 		    M_TEMP, M_WAITOK);
1989d00947d8SCraig Rodrigues 		pos = newcookies;
1990d00947d8SCraig Rodrigues 
1991b214fcceSAlan Somers 		memcpy(pos, cookies_bk, ncookies_bk * sizeof(*newcookies));
1992508a31f1SDaichi GOTO 		pos += ncookies_bk;
1993312d49efSJason A. Harmening 		memcpy(pos, *(ap->a_cookies),
1994b214fcceSAlan Somers 		    *(ap->a_ncookies) * sizeof(*newcookies));
1995d00947d8SCraig Rodrigues 		free(cookies_bk, M_TEMP);
1996d00947d8SCraig Rodrigues 		free(*(ap->a_cookies), M_TEMP);
1997d00947d8SCraig Rodrigues 		*(ap->a_ncookies) = size;
1998d00947d8SCraig Rodrigues 		*(ap->a_cookies) = newcookies;
1999d00947d8SCraig Rodrigues 	}
2000d00947d8SCraig Rodrigues 
2001d00947d8SCraig Rodrigues unionfs_readdir_exit:
20025adc4080SDaichi GOTO 	if (error != 0 && ap->a_eofflag != NULL)
20035adc4080SDaichi GOTO 		*(ap->a_eofflag) = 1;
20045adc4080SDaichi GOTO 
2005d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_readdir: leave (%d)\n", error);
2006d00947d8SCraig Rodrigues 
2007d00947d8SCraig Rodrigues 	return (error);
2008d00947d8SCraig Rodrigues }
2009d00947d8SCraig Rodrigues 
2010d00947d8SCraig Rodrigues static int
unionfs_readlink(struct vop_readlink_args * ap)2011d00947d8SCraig Rodrigues unionfs_readlink(struct vop_readlink_args *ap)
2012d00947d8SCraig Rodrigues {
2013d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
2014df8bae1dSRodney W. Grimes 	struct vnode   *vp;
2015312d49efSJason A. Harmening 	int error;
2016df8bae1dSRodney W. Grimes 
2017d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_readlink: enter\n");
20182a31267eSMatthew Dillon 
20191e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_vp);
20201e5da15aSDaichi GOTO 
2021d00947d8SCraig Rodrigues 	unp = VTOUNIONFS(ap->a_vp);
2022d00947d8SCraig Rodrigues 	vp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp);
2023d00947d8SCraig Rodrigues 
2024d00947d8SCraig Rodrigues 	error = VOP_READLINK(vp, ap->a_uio, ap->a_cred);
2025d00947d8SCraig Rodrigues 
2026d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_readlink: leave (%d)\n", error);
2027d00947d8SCraig Rodrigues 
20282a31267eSMatthew Dillon 	return (error);
2029df8bae1dSRodney W. Grimes }
2030df8bae1dSRodney W. Grimes 
2031c9bf0111SKATO Takenori static int
unionfs_getwritemount(struct vop_getwritemount_args * ap)2032d00947d8SCraig Rodrigues unionfs_getwritemount(struct vop_getwritemount_args *ap)
2033df8bae1dSRodney W. Grimes {
2034fcb16474SJason A. Harmening 	struct unionfs_node *unp;
2035d00947d8SCraig Rodrigues 	struct vnode   *uvp;
2036fcb16474SJason A. Harmening 	struct vnode   *vp, *ovp;
2037312d49efSJason A. Harmening 	int		error;
2038df8bae1dSRodney W. Grimes 
2039d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_getwritemount: enter\n");
2040996c772fSJohn Dyson 
2041d00947d8SCraig Rodrigues 	error = 0;
2042d00947d8SCraig Rodrigues 	vp = ap->a_vp;
2043fcb16474SJason A. Harmening 	uvp = NULLVP;
2044d00947d8SCraig Rodrigues 
2045d00947d8SCraig Rodrigues 	VI_LOCK(vp);
2046fcb16474SJason A. Harmening 	unp = VTOUNIONFS(vp);
2047fcb16474SJason A. Harmening 	if (unp != NULL)
2048fcb16474SJason A. Harmening 		uvp = unp->un_uppervp;
2049fcb16474SJason A. Harmening 
2050fcb16474SJason A. Harmening 	/*
2051fcb16474SJason A. Harmening 	 * If our node has no upper vnode, check the parent directory.
2052fcb16474SJason A. Harmening 	 * We may be initiating a write operation that will produce a
2053fcb16474SJason A. Harmening 	 * new upper vnode through CoW.
2054fcb16474SJason A. Harmening 	 */
2055fcb16474SJason A. Harmening 	if (uvp == NULLVP && unp != NULL) {
2056fcb16474SJason A. Harmening 		ovp = vp;
2057fcb16474SJason A. Harmening 		vp = unp->un_dvp;
2058fcb16474SJason A. Harmening 		/*
2059fcb16474SJason A. Harmening 		 * Only the root vnode should have an empty parent, but it
2060fcb16474SJason A. Harmening 		 * should not have an empty uppervp, so we shouldn't get here.
2061fcb16474SJason A. Harmening 		 */
2062fcb16474SJason A. Harmening 		VNASSERT(vp != NULL, ovp, ("%s: NULL parent vnode", __func__));
2063fcb16474SJason A. Harmening 		VI_UNLOCK(ovp);
2064fcb16474SJason A. Harmening 		VI_LOCK(vp);
2065fcb16474SJason A. Harmening 		unp = VTOUNIONFS(vp);
2066fcb16474SJason A. Harmening 		if (unp != NULL)
2067fcb16474SJason A. Harmening 			uvp = unp->un_uppervp;
2068fcb16474SJason A. Harmening 		if (uvp == NULLVP)
2069d00947d8SCraig Rodrigues 			error = EACCES;
2070fcb16474SJason A. Harmening 	}
2071fcb16474SJason A. Harmening 
2072fcb16474SJason A. Harmening 	if (uvp != NULLVP) {
2073fcb16474SJason A. Harmening 		vholdnz(uvp);
2074d00947d8SCraig Rodrigues 		VI_UNLOCK(vp);
2075fcb16474SJason A. Harmening 		error = VOP_GETWRITEMOUNT(uvp, ap->a_mpp);
2076fcb16474SJason A. Harmening 		vdrop(uvp);
2077fcb16474SJason A. Harmening 	} else {
2078fcb16474SJason A. Harmening 		VI_UNLOCK(vp);
2079fcb16474SJason A. Harmening 		*(ap->a_mpp) = NULL;
2080df8bae1dSRodney W. Grimes 	}
2081d00947d8SCraig Rodrigues 
2082d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_getwritemount: leave (%d)\n", error);
2083d00947d8SCraig Rodrigues 
2084df8bae1dSRodney W. Grimes 	return (error);
2085df8bae1dSRodney W. Grimes }
2086df8bae1dSRodney W. Grimes 
2087c9bf0111SKATO Takenori static int
unionfs_inactive(struct vop_inactive_args * ap)2088d00947d8SCraig Rodrigues unionfs_inactive(struct vop_inactive_args *ap)
2089df8bae1dSRodney W. Grimes {
2090dc2dd185SDaichi GOTO 	ap->a_vp->v_object = NULL;
2091af6e6b87SEdward Tomasz Napierala 	vrecycle(ap->a_vp);
2092d00947d8SCraig Rodrigues 	return (0);
2093df8bae1dSRodney W. Grimes }
2094df8bae1dSRodney W. Grimes 
2095c9bf0111SKATO Takenori static int
unionfs_reclaim(struct vop_reclaim_args * ap)2096d00947d8SCraig Rodrigues unionfs_reclaim(struct vop_reclaim_args *ap)
2097df8bae1dSRodney W. Grimes {
2098d00947d8SCraig Rodrigues 	/* UNIONFS_INTERNAL_DEBUG("unionfs_reclaim: enter\n"); */
2099d00947d8SCraig Rodrigues 
21006d8420d4SJason A. Harmening 	unionfs_noderem(ap->a_vp);
2101d00947d8SCraig Rodrigues 
2102d00947d8SCraig Rodrigues 	/* UNIONFS_INTERNAL_DEBUG("unionfs_reclaim: leave\n"); */
2103d00947d8SCraig Rodrigues 
2104d00947d8SCraig Rodrigues 	return (0);
2105d00947d8SCraig Rodrigues }
2106d00947d8SCraig Rodrigues 
2107d00947d8SCraig Rodrigues static int
unionfs_print(struct vop_print_args * ap)2108d00947d8SCraig Rodrigues unionfs_print(struct vop_print_args *ap)
2109d00947d8SCraig Rodrigues {
2110d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
2111d00947d8SCraig Rodrigues 	/* struct unionfs_node_status *unsp; */
2112d00947d8SCraig Rodrigues 
2113d00947d8SCraig Rodrigues 	unp = VTOUNIONFS(ap->a_vp);
2114d00947d8SCraig Rodrigues 	/* unionfs_get_node_status(unp, curthread, &unsp); */
2115d00947d8SCraig Rodrigues 
2116d00947d8SCraig Rodrigues 	printf("unionfs_vp=%p, uppervp=%p, lowervp=%p\n",
2117d00947d8SCraig Rodrigues 	    ap->a_vp, unp->un_uppervp, unp->un_lowervp);
2118d00947d8SCraig Rodrigues 	/*
2119d00947d8SCraig Rodrigues 	printf("unionfs opencnt: uppervp=%d, lowervp=%d\n",
2120d00947d8SCraig Rodrigues 	    unsp->uns_upper_opencnt, unsp->uns_lower_opencnt);
2121d00947d8SCraig Rodrigues 	*/
2122d00947d8SCraig Rodrigues 
2123d00947d8SCraig Rodrigues 	if (unp->un_uppervp != NULLVP)
2124411455a8SEdward Tomasz Napierala 		vn_printf(unp->un_uppervp, "unionfs: upper ");
2125d00947d8SCraig Rodrigues 	if (unp->un_lowervp != NULLVP)
2126411455a8SEdward Tomasz Napierala 		vn_printf(unp->un_lowervp, "unionfs: lower ");
2127d00947d8SCraig Rodrigues 
2128d00947d8SCraig Rodrigues 	return (0);
2129d00947d8SCraig Rodrigues }
2130d00947d8SCraig Rodrigues 
2131d00947d8SCraig Rodrigues static int
unionfs_lock(struct vop_lock1_args * ap)2132d413d210SKonstantin Belousov unionfs_lock(struct vop_lock1_args *ap)
2133d00947d8SCraig Rodrigues {
2134d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
2135d00947d8SCraig Rodrigues 	struct vnode   *vp;
2136eb60ff1eSJason A. Harmening 	struct vnode   *tvp;
2137312d49efSJason A. Harmening 	int		error;
2138312d49efSJason A. Harmening 	int		flags;
2139eb60ff1eSJason A. Harmening 	bool		lvp_locked;
2140df8bae1dSRodney W. Grimes 
2141d00947d8SCraig Rodrigues 	error = 0;
2142d00947d8SCraig Rodrigues 	flags = ap->a_flags;
2143d00947d8SCraig Rodrigues 	vp = ap->a_vp;
2144df8bae1dSRodney W. Grimes 
2145d00947d8SCraig Rodrigues 	if (LK_RELEASE == (flags & LK_TYPE_MASK) || !(flags & LK_TYPE_MASK))
21464a20fe31SMateusz Guzik 		return (VOP_UNLOCK_FLAGS(vp, flags | LK_RELEASE));
21472a31267eSMatthew Dillon 
2148eb60ff1eSJason A. Harmening unionfs_lock_restart:
2149eb60ff1eSJason A. Harmening 	/*
2150eb60ff1eSJason A. Harmening 	 * We currently need the interlock here to ensure we can safely
2151eb60ff1eSJason A. Harmening 	 * access the unionfs vnode's private data.  We may be able to
2152eb60ff1eSJason A. Harmening 	 * eliminate this extra locking by instead using vfs_smr_enter()
2153eb60ff1eSJason A. Harmening 	 * and vn_load_v_data_smr() here in conjunction with an SMR UMA
2154eb60ff1eSJason A. Harmening 	 * zone for unionfs nodes.
2155eb60ff1eSJason A. Harmening 	 */
215657821163SDaichi GOTO 	if ((flags & LK_INTERLOCK) == 0)
2157f3d1ec67SBoris Popov 		VI_LOCK(vp);
2158eb60ff1eSJason A. Harmening 	else
2159eb60ff1eSJason A. Harmening 		flags &= ~LK_INTERLOCK;
2160d00947d8SCraig Rodrigues 
2161d00947d8SCraig Rodrigues 	unp = VTOUNIONFS(vp);
2162eb60ff1eSJason A. Harmening 	if (unp == NULL) {
2163eb60ff1eSJason A. Harmening 		VI_UNLOCK(vp);
2164eb60ff1eSJason A. Harmening 		ap->a_flags = flags;
2165eb60ff1eSJason A. Harmening 		return (vop_stdlock(ap));
2166eb60ff1eSJason A. Harmening 	}
21670cd8f3e9SJason A. Harmening 
2168eb60ff1eSJason A. Harmening 	if (unp->un_uppervp != NULL) {
2169eb60ff1eSJason A. Harmening 		tvp = unp->un_uppervp;
2170eb60ff1eSJason A. Harmening 		lvp_locked = false;
2171eb60ff1eSJason A. Harmening 	} else {
2172eb60ff1eSJason A. Harmening 		tvp = unp->un_lowervp;
2173eb60ff1eSJason A. Harmening 		lvp_locked = true;
2174eb60ff1eSJason A. Harmening 	}
2175cb5736b7SDaichi GOTO 
21762a31267eSMatthew Dillon 	/*
21776ff167aaSJason A. Harmening 	 * During unmount, the root vnode lock may be taken recursively,
21786ff167aaSJason A. Harmening 	 * because it may share the same v_vnlock field as the vnode covered by
21796ff167aaSJason A. Harmening 	 * the unionfs mount.  The covered vnode is locked across VFS_UNMOUNT(),
21806ff167aaSJason A. Harmening 	 * and the same lock may be taken recursively here during vflush()
21816ff167aaSJason A. Harmening 	 * issued by unionfs_unmount().
21822a31267eSMatthew Dillon 	 */
2183d00947d8SCraig Rodrigues 	if ((flags & LK_TYPE_MASK) == LK_EXCLUSIVE &&
2184a01ca46bSJason A. Harmening 	    (vp->v_vflag & VV_ROOT) != 0)
2185d00947d8SCraig Rodrigues 		flags |= LK_CANRECURSE;
21862a31267eSMatthew Dillon 
2187eb60ff1eSJason A. Harmening 	vholdnz(tvp);
2188eb60ff1eSJason A. Harmening 	VI_UNLOCK(vp);
2189eb60ff1eSJason A. Harmening 	error = VOP_LOCK(tvp, flags);
2190eb60ff1eSJason A. Harmening 	vdrop(tvp);
2191eb60ff1eSJason A. Harmening 	if (error == 0 && (lvp_locked || VTOUNIONFS(vp) == NULL)) {
2192312d49efSJason A. Harmening 		/*
2193eb60ff1eSJason A. Harmening 		 * After dropping the interlock above, there exists a window
2194eb60ff1eSJason A. Harmening 		 * in which another thread may acquire the lower vnode lock
2195eb60ff1eSJason A. Harmening 		 * and then either doom the unionfs vnode or create an upper
2196eb60ff1eSJason A. Harmening 		 * vnode.  In either case, we will effectively be holding the
2197eb60ff1eSJason A. Harmening 		 * wrong lock, so we must drop the lower vnode lock and
2198eb60ff1eSJason A. Harmening 		 * restart the lock operation.
2199eb60ff1eSJason A. Harmening 		 *
2200eb60ff1eSJason A. Harmening 		 * If unp is not already NULL, we assume that we can safely
2201eb60ff1eSJason A. Harmening 		 * access it because we currently hold lvp's lock.
2202eb60ff1eSJason A. Harmening 		 * unionfs_noderem() acquires lvp's lock before freeing
2203eb60ff1eSJason A. Harmening 		 * the vnode private data, ensuring it can't be concurrently
2204eb60ff1eSJason A. Harmening 		 * freed while we are using it here.  Likewise,
2205eb60ff1eSJason A. Harmening 		 * unionfs_node_update() acquires lvp's lock before installing
2206eb60ff1eSJason A. Harmening 		 * an upper vnode.  Without those guarantees, we would need to
2207eb60ff1eSJason A. Harmening 		 * reacquire the vnode interlock here.
2208eb60ff1eSJason A. Harmening 		 * Note that unionfs_noderem() doesn't acquire lvp's lock if
2209eb60ff1eSJason A. Harmening 		 * this is the root vnode, but the root vnode should always
2210eb60ff1eSJason A. Harmening 		 * have an upper vnode and therefore we should never use its
2211eb60ff1eSJason A. Harmening 		 * lower vnode lock here.
2212312d49efSJason A. Harmening 		 */
2213d00947d8SCraig Rodrigues 		unp = VTOUNIONFS(vp);
2214eb60ff1eSJason A. Harmening 		if (unp == NULL || unp->un_uppervp != NULLVP) {
2215eb60ff1eSJason A. Harmening 			VOP_UNLOCK(tvp);
2216eb60ff1eSJason A. Harmening 			/*
2217eb60ff1eSJason A. Harmening 			 * If we previously held the lock, the upgrade may
2218eb60ff1eSJason A. Harmening 			 * have temporarily dropped the lock, in which case
2219eb60ff1eSJason A. Harmening 			 * concurrent dooming or copy-up will necessitate
2220eb60ff1eSJason A. Harmening 			 * acquiring a different lock.  Since we never held
2221eb60ff1eSJason A. Harmening 			 * the new lock, LK_UPGRADE must be cleared here to
2222eb60ff1eSJason A. Harmening 			 * avoid triggering a lockmgr panic.
2223eb60ff1eSJason A. Harmening 			 */
2224eb60ff1eSJason A. Harmening 			if (flags & LK_UPGRADE)
2225eb60ff1eSJason A. Harmening 				flags = (flags & ~LK_TYPE_MASK) | LK_EXCLUSIVE;
2226eb60ff1eSJason A. Harmening 			VNASSERT((flags & LK_DOWNGRADE) == 0, vp,
2227eb60ff1eSJason A. Harmening 			    ("%s: vnode doomed during downgrade", __func__));
2228eb60ff1eSJason A. Harmening 			goto unionfs_lock_restart;
22292a31267eSMatthew Dillon 		}
2230df8bae1dSRodney W. Grimes 	}
2231df8bae1dSRodney W. Grimes 
2232df8bae1dSRodney W. Grimes 	return (error);
2233df8bae1dSRodney W. Grimes }
2234df8bae1dSRodney W. Grimes 
2235c9bf0111SKATO Takenori static int
unionfs_unlock(struct vop_unlock_args * ap)2236d00947d8SCraig Rodrigues unionfs_unlock(struct vop_unlock_args *ap)
2237df8bae1dSRodney W. Grimes {
2238d00947d8SCraig Rodrigues 	struct vnode   *vp;
2239eb60ff1eSJason A. Harmening 	struct vnode   *tvp;
2240d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
2241312d49efSJason A. Harmening 	int		error;
2242df8bae1dSRodney W. Grimes 
22431e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_vp);
22441e5da15aSDaichi GOTO 
2245d00947d8SCraig Rodrigues 	vp = ap->a_vp;
2246d00947d8SCraig Rodrigues 
2247d00947d8SCraig Rodrigues 	unp = VTOUNIONFS(vp);
2248d00947d8SCraig Rodrigues 	if (unp == NULL)
2249d00947d8SCraig Rodrigues 		return (vop_stdunlock(ap));
2250eb60ff1eSJason A. Harmening 
2251eb60ff1eSJason A. Harmening 	tvp = (unp->un_uppervp != NULL ? unp->un_uppervp : unp->un_lowervp);
2252eb60ff1eSJason A. Harmening 
2253eb60ff1eSJason A. Harmening 	vholdnz(tvp);
2254eb60ff1eSJason A. Harmening 	error = VOP_UNLOCK(tvp);
2255eb60ff1eSJason A. Harmening 	vdrop(tvp);
2256eb60ff1eSJason A. Harmening 
2257eb60ff1eSJason A. Harmening 	return (error);
2258d00947d8SCraig Rodrigues }
2259d00947d8SCraig Rodrigues 
2260c9bf0111SKATO Takenori static int
unionfs_pathconf(struct vop_pathconf_args * ap)2261d00947d8SCraig Rodrigues unionfs_pathconf(struct vop_pathconf_args *ap)
2262df8bae1dSRodney W. Grimes {
2263d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
2264d00947d8SCraig Rodrigues 	struct vnode   *vp;
2265d00947d8SCraig Rodrigues 
22661e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_vp);
22671e5da15aSDaichi GOTO 
2268d00947d8SCraig Rodrigues 	unp = VTOUNIONFS(ap->a_vp);
2269d00947d8SCraig Rodrigues 	vp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp);
2270d00947d8SCraig Rodrigues 
2271d00947d8SCraig Rodrigues 	return (VOP_PATHCONF(vp, ap->a_name, ap->a_retval));
2272d00947d8SCraig Rodrigues }
2273d00947d8SCraig Rodrigues 
2274d00947d8SCraig Rodrigues static int
unionfs_advlock(struct vop_advlock_args * ap)2275d00947d8SCraig Rodrigues unionfs_advlock(struct vop_advlock_args *ap)
2276d00947d8SCraig Rodrigues {
2277d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
2278d00947d8SCraig Rodrigues 	struct unionfs_node_status *unsp;
2279d00947d8SCraig Rodrigues 	struct vnode   *vp;
2280d00947d8SCraig Rodrigues 	struct vnode   *uvp;
2281d00947d8SCraig Rodrigues 	struct thread  *td;
2282312d49efSJason A. Harmening 	int error;
2283d00947d8SCraig Rodrigues 
2284d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_advlock: enter\n");
2285d00947d8SCraig Rodrigues 
22861e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_vp);
22871e5da15aSDaichi GOTO 
2288d00947d8SCraig Rodrigues 	vp = ap->a_vp;
2289d00947d8SCraig Rodrigues 	td = curthread;
2290d00947d8SCraig Rodrigues 
2291cb05b60aSAttilio Rao 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
2292d00947d8SCraig Rodrigues 
2293d00947d8SCraig Rodrigues 	unp = VTOUNIONFS(ap->a_vp);
2294d00947d8SCraig Rodrigues 	uvp = unp->un_uppervp;
2295d00947d8SCraig Rodrigues 
2296d00947d8SCraig Rodrigues 	if (uvp == NULLVP) {
2297eb60ff1eSJason A. Harmening 		error = unionfs_copyfile(ap->a_vp, 1, td->td_ucred, td);
2298d00947d8SCraig Rodrigues 		if (error != 0)
2299d00947d8SCraig Rodrigues 			goto unionfs_advlock_abort;
2300d00947d8SCraig Rodrigues 		uvp = unp->un_uppervp;
2301d00947d8SCraig Rodrigues 
2302d00947d8SCraig Rodrigues 		unionfs_get_node_status(unp, td, &unsp);
2303d00947d8SCraig Rodrigues 		if (unsp->uns_lower_opencnt > 0) {
2304d00947d8SCraig Rodrigues 			/* try reopen the vnode */
23059e223287SKonstantin Belousov 			error = VOP_OPEN(uvp, unsp->uns_lower_openmode,
23069e223287SKonstantin Belousov 				td->td_ucred, td, NULL);
2307d00947d8SCraig Rodrigues 			if (error)
2308d00947d8SCraig Rodrigues 				goto unionfs_advlock_abort;
2309d00947d8SCraig Rodrigues 			unsp->uns_upper_opencnt++;
2310312d49efSJason A. Harmening 			VOP_CLOSE(unp->un_lowervp, unsp->uns_lower_openmode,
2311312d49efSJason A. Harmening 			    td->td_ucred, td);
2312d00947d8SCraig Rodrigues 			unsp->uns_lower_opencnt--;
2313b2b0db08SDaichi GOTO 		} else
2314fe5f08cdSDaichi GOTO 			unionfs_tryrem_node_status(unp, unsp);
2315d00947d8SCraig Rodrigues 	}
2316d00947d8SCraig Rodrigues 
2317b249ce48SMateusz Guzik 	VOP_UNLOCK(vp);
2318d00947d8SCraig Rodrigues 
2319d00947d8SCraig Rodrigues 	error = VOP_ADVLOCK(uvp, ap->a_id, ap->a_op, ap->a_fl, ap->a_flags);
2320d00947d8SCraig Rodrigues 
2321d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_advlock: leave (%d)\n", error);
2322d00947d8SCraig Rodrigues 
2323d00947d8SCraig Rodrigues 	return error;
2324d00947d8SCraig Rodrigues 
2325d00947d8SCraig Rodrigues unionfs_advlock_abort:
2326b249ce48SMateusz Guzik 	VOP_UNLOCK(vp);
2327d00947d8SCraig Rodrigues 
2328d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_advlock: leave (%d)\n", error);
2329d00947d8SCraig Rodrigues 
2330d00947d8SCraig Rodrigues 	return error;
2331d00947d8SCraig Rodrigues }
2332d00947d8SCraig Rodrigues 
2333d00947d8SCraig Rodrigues static int
unionfs_strategy(struct vop_strategy_args * ap)2334d00947d8SCraig Rodrigues unionfs_strategy(struct vop_strategy_args *ap)
2335d00947d8SCraig Rodrigues {
2336d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
2337d00947d8SCraig Rodrigues 	struct vnode   *vp;
2338d00947d8SCraig Rodrigues 
23391e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_vp);
23401e5da15aSDaichi GOTO 
2341d00947d8SCraig Rodrigues 	unp = VTOUNIONFS(ap->a_vp);
2342d00947d8SCraig Rodrigues 	vp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp);
2343df8bae1dSRodney W. Grimes 
2344df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
2345d00947d8SCraig Rodrigues 	if (vp == NULLVP)
2346d00947d8SCraig Rodrigues 		panic("unionfs_strategy: nullvp");
2347d00947d8SCraig Rodrigues 
2348d00947d8SCraig Rodrigues 	if (ap->a_bp->b_iocmd == BIO_WRITE && vp == unp->un_lowervp)
2349d00947d8SCraig Rodrigues 		panic("unionfs_strategy: writing to lowervp");
2350df8bae1dSRodney W. Grimes #endif
2351d00947d8SCraig Rodrigues 
2352d00947d8SCraig Rodrigues 	return (VOP_STRATEGY(vp, ap->a_bp));
2353df8bae1dSRodney W. Grimes }
2354df8bae1dSRodney W. Grimes 
235500fff2c7STim J. Robbins static int
unionfs_getacl(struct vop_getacl_args * ap)2356d00947d8SCraig Rodrigues unionfs_getacl(struct vop_getacl_args *ap)
235700fff2c7STim J. Robbins {
2358d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
235900fff2c7STim J. Robbins 	struct vnode   *vp;
2360312d49efSJason A. Harmening 	int		error;
236100fff2c7STim J. Robbins 
23621e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_vp);
23631e5da15aSDaichi GOTO 
2364d00947d8SCraig Rodrigues 	unp = VTOUNIONFS(ap->a_vp);
2365d00947d8SCraig Rodrigues 	vp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp);
2366d00947d8SCraig Rodrigues 
2367d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_getacl: enter\n");
2368d00947d8SCraig Rodrigues 
2369d00947d8SCraig Rodrigues 	error = VOP_GETACL(vp, ap->a_type, ap->a_aclp, ap->a_cred, ap->a_td);
2370d00947d8SCraig Rodrigues 
2371d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_getacl: leave (%d)\n", error);
237200fff2c7STim J. Robbins 
237300fff2c7STim J. Robbins 	return (error);
237400fff2c7STim J. Robbins }
237500fff2c7STim J. Robbins 
237600fff2c7STim J. Robbins static int
unionfs_setacl(struct vop_setacl_args * ap)2377d00947d8SCraig Rodrigues unionfs_setacl(struct vop_setacl_args *ap)
237800fff2c7STim J. Robbins {
2379d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
2380d00947d8SCraig Rodrigues 	struct vnode   *uvp;
2381d00947d8SCraig Rodrigues 	struct vnode   *lvp;
2382d00947d8SCraig Rodrigues 	struct thread  *td;
2383312d49efSJason A. Harmening 	int		error;
238400fff2c7STim J. Robbins 
2385d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_setacl: enter\n");
2386d00947d8SCraig Rodrigues 
23871e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_vp);
23881e5da15aSDaichi GOTO 
2389d00947d8SCraig Rodrigues 	error = EROFS;
2390d00947d8SCraig Rodrigues 	unp = VTOUNIONFS(ap->a_vp);
2391d00947d8SCraig Rodrigues 	uvp = unp->un_uppervp;
2392d00947d8SCraig Rodrigues 	lvp = unp->un_lowervp;
2393d00947d8SCraig Rodrigues 	td = ap->a_td;
2394d00947d8SCraig Rodrigues 
2395d00947d8SCraig Rodrigues 	if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY)
2396d00947d8SCraig Rodrigues 		return (EROFS);
2397d00947d8SCraig Rodrigues 
2398d00947d8SCraig Rodrigues 	if (uvp == NULLVP && lvp->v_type == VREG) {
2399eb60ff1eSJason A. Harmening 		if ((error = unionfs_copyfile(ap->a_vp, 1, ap->a_cred, td)) != 0)
2400d00947d8SCraig Rodrigues 			return (error);
2401d00947d8SCraig Rodrigues 		uvp = unp->un_uppervp;
2402d00947d8SCraig Rodrigues 	}
2403d00947d8SCraig Rodrigues 
24046c8ded00SJason A. Harmening 	if (uvp != NULLVP) {
24056c8ded00SJason A. Harmening 		int lkflags;
24066c8ded00SJason A. Harmening 		unionfs_forward_vop_start(uvp, &lkflags);
2407d00947d8SCraig Rodrigues 		error = VOP_SETACL(uvp, ap->a_type, ap->a_aclp, ap->a_cred, td);
24086c8ded00SJason A. Harmening 		unionfs_forward_vop_finish(ap->a_vp, uvp, lkflags);
24096c8ded00SJason A. Harmening 	}
2410d00947d8SCraig Rodrigues 
2411d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_setacl: leave (%d)\n", error);
241200fff2c7STim J. Robbins 
241300fff2c7STim J. Robbins 	return (error);
241400fff2c7STim J. Robbins }
241500fff2c7STim J. Robbins 
241600fff2c7STim J. Robbins static int
unionfs_aclcheck(struct vop_aclcheck_args * ap)2417d00947d8SCraig Rodrigues unionfs_aclcheck(struct vop_aclcheck_args *ap)
241800fff2c7STim J. Robbins {
2419d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
242000fff2c7STim J. Robbins 	struct vnode   *vp;
2421312d49efSJason A. Harmening 	int		error;
242200fff2c7STim J. Robbins 
2423d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_aclcheck: enter\n");
2424d00947d8SCraig Rodrigues 
24251e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_vp);
24261e5da15aSDaichi GOTO 
2427d00947d8SCraig Rodrigues 	unp = VTOUNIONFS(ap->a_vp);
2428d00947d8SCraig Rodrigues 	vp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp);
2429d00947d8SCraig Rodrigues 
2430d00947d8SCraig Rodrigues 	error = VOP_ACLCHECK(vp, ap->a_type, ap->a_aclp, ap->a_cred, ap->a_td);
2431d00947d8SCraig Rodrigues 
2432d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_aclcheck: leave (%d)\n", error);
243300fff2c7STim J. Robbins 
243400fff2c7STim J. Robbins 	return (error);
243500fff2c7STim J. Robbins }
243600fff2c7STim J. Robbins 
243700fff2c7STim J. Robbins static int
unionfs_openextattr(struct vop_openextattr_args * ap)2438d00947d8SCraig Rodrigues unionfs_openextattr(struct vop_openextattr_args *ap)
243900fff2c7STim J. Robbins {
2440d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
244100fff2c7STim J. Robbins 	struct vnode   *vp;
244257821163SDaichi GOTO 	struct vnode   *tvp;
2443312d49efSJason A. Harmening 	int		error;
244400fff2c7STim J. Robbins 
24451e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_vp);
24461e5da15aSDaichi GOTO 
244757821163SDaichi GOTO 	vp = ap->a_vp;
244857821163SDaichi GOTO 	unp = VTOUNIONFS(vp);
244957821163SDaichi GOTO 	tvp = (unp->un_uppervp != NULLVP ? unp->un_uppervp : unp->un_lowervp);
2450d00947d8SCraig Rodrigues 
245157821163SDaichi GOTO 	if ((tvp == unp->un_uppervp && (unp->un_flag & UNIONFS_OPENEXTU)) ||
245257821163SDaichi GOTO 	    (tvp == unp->un_lowervp && (unp->un_flag & UNIONFS_OPENEXTL)))
2453d00947d8SCraig Rodrigues 		return (EBUSY);
2454d00947d8SCraig Rodrigues 
245557821163SDaichi GOTO 	error = VOP_OPENEXTATTR(tvp, ap->a_cred, ap->a_td);
2456d00947d8SCraig Rodrigues 
2457d00947d8SCraig Rodrigues 	if (error == 0) {
2458cb5736b7SDaichi GOTO 		if (vn_lock(vp, LK_UPGRADE) != 0)
2459cb5736b7SDaichi GOTO 			vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
246039a2dc44SJason A. Harmening 		if (!VN_IS_DOOMED(vp)) {
246157821163SDaichi GOTO 			if (tvp == unp->un_uppervp)
2462d00947d8SCraig Rodrigues 				unp->un_flag |= UNIONFS_OPENEXTU;
2463d00947d8SCraig Rodrigues 			else
2464d00947d8SCraig Rodrigues 				unp->un_flag |= UNIONFS_OPENEXTL;
246539a2dc44SJason A. Harmening 		}
2466cb05b60aSAttilio Rao 		vn_lock(vp, LK_DOWNGRADE | LK_RETRY);
2467d00947d8SCraig Rodrigues 	}
246800fff2c7STim J. Robbins 
246900fff2c7STim J. Robbins 	return (error);
247000fff2c7STim J. Robbins }
247100fff2c7STim J. Robbins 
247200fff2c7STim J. Robbins static int
unionfs_closeextattr(struct vop_closeextattr_args * ap)2473d00947d8SCraig Rodrigues unionfs_closeextattr(struct vop_closeextattr_args *ap)
247400fff2c7STim J. Robbins {
2475d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
247600fff2c7STim J. Robbins 	struct vnode   *vp;
247757821163SDaichi GOTO 	struct vnode   *tvp;
2478312d49efSJason A. Harmening 	int		error;
247900fff2c7STim J. Robbins 
24801e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_vp);
24811e5da15aSDaichi GOTO 
248257821163SDaichi GOTO 	vp = ap->a_vp;
248357821163SDaichi GOTO 	unp = VTOUNIONFS(vp);
248457821163SDaichi GOTO 	tvp = NULLVP;
2485d00947d8SCraig Rodrigues 
2486d00947d8SCraig Rodrigues 	if (unp->un_flag & UNIONFS_OPENEXTU)
248757821163SDaichi GOTO 		tvp = unp->un_uppervp;
2488d00947d8SCraig Rodrigues 	else if (unp->un_flag & UNIONFS_OPENEXTL)
248957821163SDaichi GOTO 		tvp = unp->un_lowervp;
2490d00947d8SCraig Rodrigues 
249157821163SDaichi GOTO 	if (tvp == NULLVP)
2492d00947d8SCraig Rodrigues 		return (EOPNOTSUPP);
2493d00947d8SCraig Rodrigues 
249457821163SDaichi GOTO 	error = VOP_CLOSEEXTATTR(tvp, ap->a_commit, ap->a_cred, ap->a_td);
2495d00947d8SCraig Rodrigues 
2496d00947d8SCraig Rodrigues 	if (error == 0) {
2497cb5736b7SDaichi GOTO 		if (vn_lock(vp, LK_UPGRADE) != 0)
2498cb5736b7SDaichi GOTO 			vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
249939a2dc44SJason A. Harmening 		if (!VN_IS_DOOMED(vp)) {
250057821163SDaichi GOTO 			if (tvp == unp->un_uppervp)
2501d00947d8SCraig Rodrigues 				unp->un_flag &= ~UNIONFS_OPENEXTU;
2502d00947d8SCraig Rodrigues 			else
2503d00947d8SCraig Rodrigues 				unp->un_flag &= ~UNIONFS_OPENEXTL;
250439a2dc44SJason A. Harmening 		}
2505cb05b60aSAttilio Rao 		vn_lock(vp, LK_DOWNGRADE | LK_RETRY);
2506d00947d8SCraig Rodrigues 	}
250700fff2c7STim J. Robbins 
250800fff2c7STim J. Robbins 	return (error);
250900fff2c7STim J. Robbins }
251000fff2c7STim J. Robbins 
251100fff2c7STim J. Robbins static int
unionfs_getextattr(struct vop_getextattr_args * ap)2512d00947d8SCraig Rodrigues unionfs_getextattr(struct vop_getextattr_args *ap)
251300fff2c7STim J. Robbins {
2514d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
251500fff2c7STim J. Robbins 	struct vnode   *vp;
251600fff2c7STim J. Robbins 
25171e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_vp);
25181e5da15aSDaichi GOTO 
2519d00947d8SCraig Rodrigues 	unp = VTOUNIONFS(ap->a_vp);
2520d00947d8SCraig Rodrigues 	vp = NULLVP;
2521d00947d8SCraig Rodrigues 
2522d00947d8SCraig Rodrigues 	if (unp->un_flag & UNIONFS_OPENEXTU)
2523d00947d8SCraig Rodrigues 		vp = unp->un_uppervp;
2524d00947d8SCraig Rodrigues 	else if (unp->un_flag & UNIONFS_OPENEXTL)
2525d00947d8SCraig Rodrigues 		vp = unp->un_lowervp;
2526d00947d8SCraig Rodrigues 
2527d00947d8SCraig Rodrigues 	if (vp == NULLVP)
2528d00947d8SCraig Rodrigues 		return (EOPNOTSUPP);
2529d00947d8SCraig Rodrigues 
2530d00947d8SCraig Rodrigues 	return (VOP_GETEXTATTR(vp, ap->a_attrnamespace, ap->a_name,
2531d00947d8SCraig Rodrigues 	    ap->a_uio, ap->a_size, ap->a_cred, ap->a_td));
2532d00947d8SCraig Rodrigues }
2533d00947d8SCraig Rodrigues 
2534d00947d8SCraig Rodrigues static int
unionfs_setextattr(struct vop_setextattr_args * ap)2535d00947d8SCraig Rodrigues unionfs_setextattr(struct vop_setextattr_args *ap)
2536d00947d8SCraig Rodrigues {
2537d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
2538d00947d8SCraig Rodrigues 	struct vnode   *uvp;
2539d00947d8SCraig Rodrigues 	struct vnode   *lvp;
2540d00947d8SCraig Rodrigues 	struct vnode   *ovp;
2541d00947d8SCraig Rodrigues 	struct ucred   *cred;
2542d00947d8SCraig Rodrigues 	struct thread  *td;
2543312d49efSJason A. Harmening 	int		error;
2544d00947d8SCraig Rodrigues 
25451e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_vp);
25461e5da15aSDaichi GOTO 
2547d00947d8SCraig Rodrigues 	error = EROFS;
2548d00947d8SCraig Rodrigues 	unp = VTOUNIONFS(ap->a_vp);
2549d00947d8SCraig Rodrigues 	uvp = unp->un_uppervp;
2550d00947d8SCraig Rodrigues 	lvp = unp->un_lowervp;
2551d00947d8SCraig Rodrigues 	ovp = NULLVP;
2552d00947d8SCraig Rodrigues 	cred = ap->a_cred;
2553d00947d8SCraig Rodrigues 	td = ap->a_td;
2554d00947d8SCraig Rodrigues 
2555312d49efSJason A. Harmening 	UNIONFS_INTERNAL_DEBUG("unionfs_setextattr: enter (un_flag=%x)\n",
2556312d49efSJason A. Harmening 	    unp->un_flag);
2557d00947d8SCraig Rodrigues 
2558d00947d8SCraig Rodrigues 	if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY)
2559d00947d8SCraig Rodrigues 		return (EROFS);
2560d00947d8SCraig Rodrigues 
2561d00947d8SCraig Rodrigues 	if (unp->un_flag & UNIONFS_OPENEXTU)
2562d00947d8SCraig Rodrigues 		ovp = unp->un_uppervp;
2563d00947d8SCraig Rodrigues 	else if (unp->un_flag & UNIONFS_OPENEXTL)
2564d00947d8SCraig Rodrigues 		ovp = unp->un_lowervp;
2565d00947d8SCraig Rodrigues 
2566d00947d8SCraig Rodrigues 	if (ovp == NULLVP)
2567d00947d8SCraig Rodrigues 		return (EOPNOTSUPP);
2568d00947d8SCraig Rodrigues 
2569d00947d8SCraig Rodrigues 	if (ovp == lvp && lvp->v_type == VREG) {
2570d00947d8SCraig Rodrigues 		VOP_CLOSEEXTATTR(lvp, 0, cred, td);
2571d00947d8SCraig Rodrigues 		if (uvp == NULLVP &&
2572eb60ff1eSJason A. Harmening 		    (error = unionfs_copyfile(ap->a_vp, 1, cred, td)) != 0) {
2573d00947d8SCraig Rodrigues unionfs_setextattr_reopen:
2574eb60ff1eSJason A. Harmening 			unp = VTOUNIONFS(ap->a_vp);
2575eb60ff1eSJason A. Harmening 			if (unp != NULL && (unp->un_flag & UNIONFS_OPENEXTL) &&
2576d00947d8SCraig Rodrigues 			    VOP_OPENEXTATTR(lvp, cred, td)) {
2577d00947d8SCraig Rodrigues #ifdef DIAGNOSTIC
2578d00947d8SCraig Rodrigues 				panic("unionfs: VOP_OPENEXTATTR failed");
2579d00947d8SCraig Rodrigues #endif
2580d00947d8SCraig Rodrigues 				unp->un_flag &= ~UNIONFS_OPENEXTL;
2581d00947d8SCraig Rodrigues 			}
2582d00947d8SCraig Rodrigues 			goto unionfs_setextattr_abort;
2583d00947d8SCraig Rodrigues 		}
2584d00947d8SCraig Rodrigues 		uvp = unp->un_uppervp;
2585d00947d8SCraig Rodrigues 		if ((error = VOP_OPENEXTATTR(uvp, cred, td)) != 0)
2586d00947d8SCraig Rodrigues 			goto unionfs_setextattr_reopen;
2587d00947d8SCraig Rodrigues 		unp->un_flag &= ~UNIONFS_OPENEXTL;
2588d00947d8SCraig Rodrigues 		unp->un_flag |= UNIONFS_OPENEXTU;
2589d00947d8SCraig Rodrigues 		ovp = uvp;
2590d00947d8SCraig Rodrigues 	}
2591d00947d8SCraig Rodrigues 
25926c8ded00SJason A. Harmening 	if (ovp == uvp) {
25936c8ded00SJason A. Harmening 		int lkflags;
25946c8ded00SJason A. Harmening 		unionfs_forward_vop_start(ovp, &lkflags);
2595d00947d8SCraig Rodrigues 		error = VOP_SETEXTATTR(ovp, ap->a_attrnamespace, ap->a_name,
2596d00947d8SCraig Rodrigues 		    ap->a_uio, cred, td);
25976c8ded00SJason A. Harmening 		unionfs_forward_vop_finish(ap->a_vp, ovp, lkflags);
25986c8ded00SJason A. Harmening 	}
2599d00947d8SCraig Rodrigues 
2600d00947d8SCraig Rodrigues unionfs_setextattr_abort:
2601d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_setextattr: leave (%d)\n", error);
260200fff2c7STim J. Robbins 
260300fff2c7STim J. Robbins 	return (error);
260400fff2c7STim J. Robbins }
260500fff2c7STim J. Robbins 
260600fff2c7STim J. Robbins static int
unionfs_listextattr(struct vop_listextattr_args * ap)2607d00947d8SCraig Rodrigues unionfs_listextattr(struct vop_listextattr_args *ap)
260800fff2c7STim J. Robbins {
2609d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
261000fff2c7STim J. Robbins 	struct vnode *vp;
261100fff2c7STim J. Robbins 
26121e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_vp);
26131e5da15aSDaichi GOTO 
2614d00947d8SCraig Rodrigues 	unp = VTOUNIONFS(ap->a_vp);
2615d00947d8SCraig Rodrigues 	vp = NULLVP;
2616d00947d8SCraig Rodrigues 
2617d00947d8SCraig Rodrigues 	if (unp->un_flag & UNIONFS_OPENEXTU)
2618d00947d8SCraig Rodrigues 		vp = unp->un_uppervp;
2619d00947d8SCraig Rodrigues 	else if (unp->un_flag & UNIONFS_OPENEXTL)
2620d00947d8SCraig Rodrigues 		vp = unp->un_lowervp;
2621d00947d8SCraig Rodrigues 
2622d00947d8SCraig Rodrigues 	if (vp == NULLVP)
2623d00947d8SCraig Rodrigues 		return (EOPNOTSUPP);
2624d00947d8SCraig Rodrigues 
2625d00947d8SCraig Rodrigues 	return (VOP_LISTEXTATTR(vp, ap->a_attrnamespace, ap->a_uio,
2626d00947d8SCraig Rodrigues 	    ap->a_size, ap->a_cred, ap->a_td));
2627d00947d8SCraig Rodrigues }
2628d00947d8SCraig Rodrigues 
2629d00947d8SCraig Rodrigues static int
unionfs_deleteextattr(struct vop_deleteextattr_args * ap)2630d00947d8SCraig Rodrigues unionfs_deleteextattr(struct vop_deleteextattr_args *ap)
2631d00947d8SCraig Rodrigues {
2632d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
2633d00947d8SCraig Rodrigues 	struct vnode   *uvp;
2634d00947d8SCraig Rodrigues 	struct vnode   *lvp;
2635d00947d8SCraig Rodrigues 	struct vnode   *ovp;
2636d00947d8SCraig Rodrigues 	struct ucred   *cred;
2637d00947d8SCraig Rodrigues 	struct thread  *td;
2638312d49efSJason A. Harmening 	int		error;
2639d00947d8SCraig Rodrigues 
26401e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_vp);
26411e5da15aSDaichi GOTO 
2642d00947d8SCraig Rodrigues 	error = EROFS;
2643d00947d8SCraig Rodrigues 	unp = VTOUNIONFS(ap->a_vp);
2644d00947d8SCraig Rodrigues 	uvp = unp->un_uppervp;
2645d00947d8SCraig Rodrigues 	lvp = unp->un_lowervp;
2646d00947d8SCraig Rodrigues 	ovp = NULLVP;
2647d00947d8SCraig Rodrigues 	cred = ap->a_cred;
2648d00947d8SCraig Rodrigues 	td = ap->a_td;
2649d00947d8SCraig Rodrigues 
2650312d49efSJason A. Harmening 	UNIONFS_INTERNAL_DEBUG("unionfs_deleteextattr: enter (un_flag=%x)\n",
2651312d49efSJason A. Harmening 	    unp->un_flag);
2652d00947d8SCraig Rodrigues 
2653d00947d8SCraig Rodrigues 	if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY)
2654d00947d8SCraig Rodrigues 		return (EROFS);
2655d00947d8SCraig Rodrigues 
2656d00947d8SCraig Rodrigues 	if (unp->un_flag & UNIONFS_OPENEXTU)
2657d00947d8SCraig Rodrigues 		ovp = unp->un_uppervp;
2658d00947d8SCraig Rodrigues 	else if (unp->un_flag & UNIONFS_OPENEXTL)
2659d00947d8SCraig Rodrigues 		ovp = unp->un_lowervp;
2660d00947d8SCraig Rodrigues 
2661d00947d8SCraig Rodrigues 	if (ovp == NULLVP)
2662d00947d8SCraig Rodrigues 		return (EOPNOTSUPP);
2663d00947d8SCraig Rodrigues 
2664d00947d8SCraig Rodrigues 	if (ovp == lvp && lvp->v_type == VREG) {
2665d00947d8SCraig Rodrigues 		VOP_CLOSEEXTATTR(lvp, 0, cred, td);
2666d00947d8SCraig Rodrigues 		if (uvp == NULLVP &&
2667eb60ff1eSJason A. Harmening 		    (error = unionfs_copyfile(ap->a_vp, 1, cred, td)) != 0) {
2668d00947d8SCraig Rodrigues unionfs_deleteextattr_reopen:
2669eb60ff1eSJason A. Harmening 			unp = VTOUNIONFS(ap->a_vp);
2670eb60ff1eSJason A. Harmening 			if (unp != NULL && (unp->un_flag & UNIONFS_OPENEXTL) &&
2671d00947d8SCraig Rodrigues 			    VOP_OPENEXTATTR(lvp, cred, td)) {
2672d00947d8SCraig Rodrigues #ifdef DIAGNOSTIC
2673d00947d8SCraig Rodrigues 				panic("unionfs: VOP_OPENEXTATTR failed");
2674d00947d8SCraig Rodrigues #endif
2675d00947d8SCraig Rodrigues 				unp->un_flag &= ~UNIONFS_OPENEXTL;
2676d00947d8SCraig Rodrigues 			}
2677d00947d8SCraig Rodrigues 			goto unionfs_deleteextattr_abort;
2678d00947d8SCraig Rodrigues 		}
2679d00947d8SCraig Rodrigues 		uvp = unp->un_uppervp;
2680d00947d8SCraig Rodrigues 		if ((error = VOP_OPENEXTATTR(uvp, cred, td)) != 0)
2681d00947d8SCraig Rodrigues 			goto unionfs_deleteextattr_reopen;
2682d00947d8SCraig Rodrigues 		unp->un_flag &= ~UNIONFS_OPENEXTL;
2683d00947d8SCraig Rodrigues 		unp->un_flag |= UNIONFS_OPENEXTU;
2684d00947d8SCraig Rodrigues 		ovp = uvp;
2685d00947d8SCraig Rodrigues 	}
2686d00947d8SCraig Rodrigues 
2687d00947d8SCraig Rodrigues 	if (ovp == uvp)
2688d00947d8SCraig Rodrigues 		error = VOP_DELETEEXTATTR(ovp, ap->a_attrnamespace, ap->a_name,
2689d00947d8SCraig Rodrigues 		    ap->a_cred, ap->a_td);
2690d00947d8SCraig Rodrigues 
2691d00947d8SCraig Rodrigues unionfs_deleteextattr_abort:
2692d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_deleteextattr: leave (%d)\n", error);
269300fff2c7STim J. Robbins 
269400fff2c7STim J. Robbins 	return (error);
269500fff2c7STim J. Robbins }
269600fff2c7STim J. Robbins 
269700fff2c7STim J. Robbins static int
unionfs_setlabel(struct vop_setlabel_args * ap)2698d00947d8SCraig Rodrigues unionfs_setlabel(struct vop_setlabel_args *ap)
269900fff2c7STim J. Robbins {
2700d00947d8SCraig Rodrigues 	struct unionfs_node *unp;
2701d00947d8SCraig Rodrigues 	struct vnode   *uvp;
2702d00947d8SCraig Rodrigues 	struct vnode   *lvp;
2703d00947d8SCraig Rodrigues 	struct thread  *td;
2704312d49efSJason A. Harmening 	int		error;
270500fff2c7STim J. Robbins 
2706d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_setlabel: enter\n");
2707d00947d8SCraig Rodrigues 
27081e5da15aSDaichi GOTO 	KASSERT_UNIONFS_VNODE(ap->a_vp);
27091e5da15aSDaichi GOTO 
2710d00947d8SCraig Rodrigues 	error = EROFS;
2711d00947d8SCraig Rodrigues 	unp = VTOUNIONFS(ap->a_vp);
2712d00947d8SCraig Rodrigues 	uvp = unp->un_uppervp;
2713d00947d8SCraig Rodrigues 	lvp = unp->un_lowervp;
2714d00947d8SCraig Rodrigues 	td = ap->a_td;
2715d00947d8SCraig Rodrigues 
2716d00947d8SCraig Rodrigues 	if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY)
2717d00947d8SCraig Rodrigues 		return (EROFS);
2718d00947d8SCraig Rodrigues 
2719d00947d8SCraig Rodrigues 	if (uvp == NULLVP && lvp->v_type == VREG) {
2720eb60ff1eSJason A. Harmening 		if ((error = unionfs_copyfile(ap->a_vp, 1, ap->a_cred, td)) != 0)
2721d00947d8SCraig Rodrigues 			return (error);
2722d00947d8SCraig Rodrigues 		uvp = unp->un_uppervp;
2723d00947d8SCraig Rodrigues 	}
2724d00947d8SCraig Rodrigues 
2725d00947d8SCraig Rodrigues 	if (uvp != NULLVP)
2726d00947d8SCraig Rodrigues 		error = VOP_SETLABEL(uvp, ap->a_label, ap->a_cred, td);
2727d00947d8SCraig Rodrigues 
2728d00947d8SCraig Rodrigues 	UNIONFS_INTERNAL_DEBUG("unionfs_setlabel: leave (%d)\n", error);
272900fff2c7STim J. Robbins 
273000fff2c7STim J. Robbins 	return (error);
273100fff2c7STim J. Robbins }
273200fff2c7STim J. Robbins 
273310bcafe9SPawel Jakub Dawidek static int
unionfs_vptofh(struct vop_vptofh_args * ap)273410bcafe9SPawel Jakub Dawidek unionfs_vptofh(struct vop_vptofh_args *ap)
273510bcafe9SPawel Jakub Dawidek {
273610bcafe9SPawel Jakub Dawidek 	return (EOPNOTSUPP);
273710bcafe9SPawel Jakub Dawidek }
273810bcafe9SPawel Jakub Dawidek 
273930d49d53SKonstantin Belousov static int
unionfs_add_writecount(struct vop_add_writecount_args * ap)274030d49d53SKonstantin Belousov unionfs_add_writecount(struct vop_add_writecount_args *ap)
274130d49d53SKonstantin Belousov {
274230d49d53SKonstantin Belousov 	struct vnode *tvp, *vp;
274330d49d53SKonstantin Belousov 	struct unionfs_node *unp;
2744ac9c3c32SJohn Baldwin 	int error, writerefs __diagused;
274530d49d53SKonstantin Belousov 
274630d49d53SKonstantin Belousov 	vp = ap->a_vp;
274730d49d53SKonstantin Belousov 	unp = VTOUNIONFS(vp);
2748d877dd57SJason A. Harmening 	tvp = unp->un_uppervp;
2749d877dd57SJason A. Harmening 	KASSERT(tvp != NULL,
2750d877dd57SJason A. Harmening 	    ("%s: adding write ref without upper vnode", __func__));
275130d49d53SKonstantin Belousov 	error = VOP_ADD_WRITECOUNT(tvp, ap->a_inc);
2752d877dd57SJason A. Harmening 	if (error != 0)
275330d49d53SKonstantin Belousov 		return (error);
2754d877dd57SJason A. Harmening 	/*
2755d877dd57SJason A. Harmening 	 * We need to track the write refs we've passed to the underlying
2756d877dd57SJason A. Harmening 	 * vnodes so that we can undo them in case we are forcibly unmounted.
2757d877dd57SJason A. Harmening 	 */
2758d877dd57SJason A. Harmening 	writerefs = atomic_fetchadd_int(&vp->v_writecount, ap->a_inc);
2759d877dd57SJason A. Harmening 	/* text refs are bypassed to lowervp */
2760d877dd57SJason A. Harmening 	VNASSERT(writerefs >= 0, vp,
2761d877dd57SJason A. Harmening 	    ("%s: invalid write count %d", __func__, writerefs));
2762d877dd57SJason A. Harmening 	VNASSERT(writerefs + ap->a_inc >= 0, vp,
2763d877dd57SJason A. Harmening 	    ("%s: invalid write count inc %d + %d", __func__,
2764d877dd57SJason A. Harmening 	    writerefs, ap->a_inc));
2765d877dd57SJason A. Harmening 	return (0);
276630d49d53SKonstantin Belousov }
276730d49d53SKonstantin Belousov 
2768cfc2cfecSJason A. Harmening static int
unionfs_vput_pair(struct vop_vput_pair_args * ap)2769cfc2cfecSJason A. Harmening unionfs_vput_pair(struct vop_vput_pair_args *ap)
2770cfc2cfecSJason A. Harmening {
2771cfc2cfecSJason A. Harmening 	struct mount *mp;
2772eb60ff1eSJason A. Harmening 	struct vnode *dvp, *vp, **vpp, *lvp, *uvp, *tvp, *tdvp, *tempvp;
2773cfc2cfecSJason A. Harmening 	struct unionfs_node *dunp, *unp;
2774cfc2cfecSJason A. Harmening 	int error, res;
2775cfc2cfecSJason A. Harmening 
2776cfc2cfecSJason A. Harmening 	dvp = ap->a_dvp;
2777cfc2cfecSJason A. Harmening 	vpp = ap->a_vpp;
2778cfc2cfecSJason A. Harmening 	vp = NULLVP;
2779cfc2cfecSJason A. Harmening 	lvp = NULLVP;
2780cfc2cfecSJason A. Harmening 	uvp = NULLVP;
2781eb60ff1eSJason A. Harmening 	tvp = NULLVP;
2782cfc2cfecSJason A. Harmening 	unp = NULL;
2783cfc2cfecSJason A. Harmening 
2784cfc2cfecSJason A. Harmening 	dunp = VTOUNIONFS(dvp);
2785eb60ff1eSJason A. Harmening 	if (dunp->un_uppervp != NULL)
2786eb60ff1eSJason A. Harmening 		tdvp = dunp->un_uppervp;
2787eb60ff1eSJason A. Harmening 	else
2788eb60ff1eSJason A. Harmening 		tdvp = dunp->un_lowervp;
2789cfc2cfecSJason A. Harmening 
2790cfc2cfecSJason A. Harmening 	/*
2791cfc2cfecSJason A. Harmening 	 * Underlying vnodes should be locked because the encompassing unionfs
2792cfc2cfecSJason A. Harmening 	 * node is locked, but will not be referenced, as the reference will
2793cfc2cfecSJason A. Harmening 	 * only be on the unionfs node.  Reference them now so that the vput()s
2794cfc2cfecSJason A. Harmening 	 * performed by VOP_VPUT_PAIR() will have a reference to drop.
2795cfc2cfecSJason A. Harmening 	 */
2796eb60ff1eSJason A. Harmening 	vref(tdvp);
2797cfc2cfecSJason A. Harmening 
2798cfc2cfecSJason A. Harmening 	if (vpp != NULL)
2799cfc2cfecSJason A. Harmening 		vp = *vpp;
2800cfc2cfecSJason A. Harmening 
2801cfc2cfecSJason A. Harmening 	if (vp != NULLVP) {
2802cfc2cfecSJason A. Harmening 		unp = VTOUNIONFS(vp);
2803cfc2cfecSJason A. Harmening 		uvp = unp->un_uppervp;
2804cfc2cfecSJason A. Harmening 		lvp = unp->un_lowervp;
2805cfc2cfecSJason A. Harmening 		if (uvp != NULLVP)
2806eb60ff1eSJason A. Harmening 			tvp = uvp;
2807eb60ff1eSJason A. Harmening 		else
2808eb60ff1eSJason A. Harmening 			tvp = lvp;
2809eb60ff1eSJason A. Harmening 		vref(tvp);
2810cfc2cfecSJason A. Harmening 
2811cfc2cfecSJason A. Harmening 		/*
2812cfc2cfecSJason A. Harmening 		 * If we're being asked to return a locked child vnode, then
2813cfc2cfecSJason A. Harmening 		 * we may need to create a replacement vnode in case the
2814cfc2cfecSJason A. Harmening 		 * original is reclaimed while the lock is dropped.  In that
2815cfc2cfecSJason A. Harmening 		 * case we'll need to ensure the mount and the underlying
2816cfc2cfecSJason A. Harmening 		 * vnodes aren't also recycled during that window.
2817cfc2cfecSJason A. Harmening 		 */
2818cfc2cfecSJason A. Harmening 		if (!ap->a_unlock_vp) {
2819cfc2cfecSJason A. Harmening 			vhold(vp);
2820cfc2cfecSJason A. Harmening 			if (uvp != NULLVP)
2821cfc2cfecSJason A. Harmening 				vhold(uvp);
2822cfc2cfecSJason A. Harmening 			if (lvp != NULLVP)
2823cfc2cfecSJason A. Harmening 				vhold(lvp);
2824cfc2cfecSJason A. Harmening 			mp = vp->v_mount;
2825cfc2cfecSJason A. Harmening 			vfs_ref(mp);
2826cfc2cfecSJason A. Harmening 		}
2827cfc2cfecSJason A. Harmening 	}
2828cfc2cfecSJason A. Harmening 
2829eb60ff1eSJason A. Harmening 	ASSERT_VOP_LOCKED(tdvp, __func__);
2830eb60ff1eSJason A. Harmening 	ASSERT_VOP_LOCKED(tvp, __func__);
2831cfc2cfecSJason A. Harmening 
2832eb60ff1eSJason A. Harmening 	if (tdvp == dunp->un_uppervp && tvp != NULLVP && tvp == lvp) {
2833eb60ff1eSJason A. Harmening 		vput(tvp);
2834eb60ff1eSJason A. Harmening 		vput(tdvp);
2835eb60ff1eSJason A. Harmening 		res = 0;
2836eb60ff1eSJason A. Harmening 	} else {
2837eb60ff1eSJason A. Harmening 		res = VOP_VPUT_PAIR(tdvp, tvp != NULLVP ? &tvp : NULL, true);
2838eb60ff1eSJason A. Harmening 	}
2839cfc2cfecSJason A. Harmening 
2840eb60ff1eSJason A. Harmening 	ASSERT_VOP_UNLOCKED(tdvp, __func__);
2841eb60ff1eSJason A. Harmening 	ASSERT_VOP_UNLOCKED(tvp, __func__);
2842cfc2cfecSJason A. Harmening 
2843cfc2cfecSJason A. Harmening 	/*
2844cfc2cfecSJason A. Harmening 	 * VOP_VPUT_PAIR() dropped the references we added to the underlying
2845cfc2cfecSJason A. Harmening 	 * vnodes, now drop the caller's reference to the unionfs vnodes.
2846cfc2cfecSJason A. Harmening 	 */
2847cfc2cfecSJason A. Harmening 	if (vp != NULLVP && ap->a_unlock_vp)
2848cfc2cfecSJason A. Harmening 		vrele(vp);
2849cfc2cfecSJason A. Harmening 	vrele(dvp);
2850cfc2cfecSJason A. Harmening 
2851cfc2cfecSJason A. Harmening 	if (vp == NULLVP || ap->a_unlock_vp)
2852cfc2cfecSJason A. Harmening 		return (res);
2853cfc2cfecSJason A. Harmening 
2854cfc2cfecSJason A. Harmening 	/*
2855cfc2cfecSJason A. Harmening 	 * We're being asked to return a locked vnode.  At this point, the
2856cfc2cfecSJason A. Harmening 	 * underlying vnodes have been unlocked, so vp may have been reclaimed.
2857cfc2cfecSJason A. Harmening 	 */
2858cfc2cfecSJason A. Harmening 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
2859cfc2cfecSJason A. Harmening 	if (vp->v_data == NULL && vfs_busy(mp, MBF_NOWAIT) == 0) {
2860cfc2cfecSJason A. Harmening 		vput(vp);
2861cfc2cfecSJason A. Harmening 		error = unionfs_nodeget(mp, uvp, lvp, dvp, &tempvp, NULL);
2862cfc2cfecSJason A. Harmening 		if (error == 0) {
2863cfc2cfecSJason A. Harmening 			vn_lock(tempvp, LK_EXCLUSIVE | LK_RETRY);
2864cfc2cfecSJason A. Harmening 			*vpp = tempvp;
2865cfc2cfecSJason A. Harmening 		} else
2866cfc2cfecSJason A. Harmening 			vget(vp, LK_EXCLUSIVE | LK_RETRY);
2867cfc2cfecSJason A. Harmening 		vfs_unbusy(mp);
2868cfc2cfecSJason A. Harmening 	}
2869cfc2cfecSJason A. Harmening 	if (lvp != NULLVP)
2870cfc2cfecSJason A. Harmening 		vdrop(lvp);
2871cfc2cfecSJason A. Harmening 	if (uvp != NULLVP)
2872cfc2cfecSJason A. Harmening 		vdrop(uvp);
2873cfc2cfecSJason A. Harmening 	vdrop(vp);
2874cfc2cfecSJason A. Harmening 	vfs_rel(mp);
2875cfc2cfecSJason A. Harmening 
2876cfc2cfecSJason A. Harmening 	return (res);
2877cfc2cfecSJason A. Harmening }
2878cfc2cfecSJason A. Harmening 
28799e891d43SJason A. Harmening static int
unionfs_set_text(struct vop_set_text_args * ap)28809e891d43SJason A. Harmening unionfs_set_text(struct vop_set_text_args *ap)
28819e891d43SJason A. Harmening {
28829e891d43SJason A. Harmening 	struct vnode *tvp;
28839e891d43SJason A. Harmening 	struct unionfs_node *unp;
28849e891d43SJason A. Harmening 	int error;
28859e891d43SJason A. Harmening 
28869e891d43SJason A. Harmening 	/*
28879e891d43SJason A. Harmening 	 * We assume text refs are managed against lvp/uvp through the
28889e891d43SJason A. Harmening 	 * executable mapping backed by its VM object.  We therefore don't
28899e891d43SJason A. Harmening 	 * need to track leased text refs in the case of a forcible unmount.
28909e891d43SJason A. Harmening 	 */
28919e891d43SJason A. Harmening 	unp = VTOUNIONFS(ap->a_vp);
28929e891d43SJason A. Harmening 	ASSERT_VOP_LOCKED(ap->a_vp, __func__);
28939e891d43SJason A. Harmening 	tvp = unp->un_uppervp != NULL ? unp->un_uppervp : unp->un_lowervp;
28949e891d43SJason A. Harmening 	error = VOP_SET_TEXT(tvp);
28959e891d43SJason A. Harmening 	return (error);
28969e891d43SJason A. Harmening }
28979e891d43SJason A. Harmening 
28989e891d43SJason A. Harmening static int
unionfs_unset_text(struct vop_unset_text_args * ap)28999e891d43SJason A. Harmening unionfs_unset_text(struct vop_unset_text_args *ap)
29009e891d43SJason A. Harmening {
29019e891d43SJason A. Harmening 	struct vnode *tvp;
29029e891d43SJason A. Harmening 	struct unionfs_node *unp;
29039e891d43SJason A. Harmening 
29049e891d43SJason A. Harmening 	ASSERT_VOP_LOCKED(ap->a_vp, __func__);
29059e891d43SJason A. Harmening 	unp = VTOUNIONFS(ap->a_vp);
29069e891d43SJason A. Harmening 	tvp = unp->un_uppervp != NULL ? unp->un_uppervp : unp->un_lowervp;
29079e891d43SJason A. Harmening 	VOP_UNSET_TEXT_CHECKED(tvp);
29089e891d43SJason A. Harmening 	return (0);
29099e891d43SJason A. Harmening }
29109e891d43SJason A. Harmening 
2911eee6217bSJason A. Harmening static int
unionfs_unp_bind(struct vop_unp_bind_args * ap)2912eee6217bSJason A. Harmening unionfs_unp_bind(struct vop_unp_bind_args *ap)
2913eee6217bSJason A. Harmening {
2914eee6217bSJason A. Harmening 	struct vnode *tvp;
2915eee6217bSJason A. Harmening 	struct unionfs_node *unp;
2916eee6217bSJason A. Harmening 
2917eee6217bSJason A. Harmening 	ASSERT_VOP_LOCKED(ap->a_vp, __func__);
2918eee6217bSJason A. Harmening 	unp = VTOUNIONFS(ap->a_vp);
2919eee6217bSJason A. Harmening 	tvp = unp->un_uppervp != NULL ? unp->un_uppervp : unp->un_lowervp;
2920eee6217bSJason A. Harmening 	VOP_UNP_BIND(tvp, ap->a_unpcb);
2921eee6217bSJason A. Harmening 	return (0);
2922eee6217bSJason A. Harmening }
2923eee6217bSJason A. Harmening 
2924eee6217bSJason A. Harmening static int
unionfs_unp_connect(struct vop_unp_connect_args * ap)2925eee6217bSJason A. Harmening unionfs_unp_connect(struct vop_unp_connect_args *ap)
2926eee6217bSJason A. Harmening {
2927eee6217bSJason A. Harmening 	struct vnode *tvp;
2928eee6217bSJason A. Harmening 	struct unionfs_node *unp;
2929eee6217bSJason A. Harmening 
2930eee6217bSJason A. Harmening 	ASSERT_VOP_LOCKED(ap->a_vp, __func__);
2931eee6217bSJason A. Harmening 	unp = VTOUNIONFS(ap->a_vp);
2932eee6217bSJason A. Harmening 	tvp = unp->un_uppervp != NULL ? unp->un_uppervp : unp->un_lowervp;
2933eee6217bSJason A. Harmening 	VOP_UNP_CONNECT(tvp, ap->a_unpcb);
2934eee6217bSJason A. Harmening 	return (0);
2935eee6217bSJason A. Harmening }
2936eee6217bSJason A. Harmening 
2937eee6217bSJason A. Harmening static int
unionfs_unp_detach(struct vop_unp_detach_args * ap)2938eee6217bSJason A. Harmening unionfs_unp_detach(struct vop_unp_detach_args *ap)
2939eee6217bSJason A. Harmening {
2940eee6217bSJason A. Harmening 	struct vnode *tvp;
2941eee6217bSJason A. Harmening 	struct unionfs_node *unp;
2942eee6217bSJason A. Harmening 
2943eee6217bSJason A. Harmening 	tvp = NULL;
2944eee6217bSJason A. Harmening 	/*
2945eee6217bSJason A. Harmening 	 * VOP_UNP_DETACH() is not guaranteed to be called with the unionfs
2946eee6217bSJason A. Harmening 	 * vnode locked, so we take the interlock to prevent a concurrent
2947eee6217bSJason A. Harmening 	 * unmount from freeing the unionfs private data.
2948eee6217bSJason A. Harmening 	 */
2949eee6217bSJason A. Harmening 	VI_LOCK(ap->a_vp);
2950eee6217bSJason A. Harmening 	unp = VTOUNIONFS(ap->a_vp);
2951eee6217bSJason A. Harmening 	if (unp != NULL) {
2952eee6217bSJason A. Harmening 		tvp = unp->un_uppervp != NULL ?
2953eee6217bSJason A. Harmening 		    unp->un_uppervp : unp->un_lowervp;
2954eee6217bSJason A. Harmening 		/*
2955eee6217bSJason A. Harmening 		 * Hold the target vnode to prevent a concurrent unionfs
2956eee6217bSJason A. Harmening 		 * unmount from causing it to be recycled once the interlock
2957eee6217bSJason A. Harmening 		 * is dropped.
2958eee6217bSJason A. Harmening 		 */
2959eee6217bSJason A. Harmening 		vholdnz(tvp);
2960eee6217bSJason A. Harmening 	}
2961eee6217bSJason A. Harmening 	VI_UNLOCK(ap->a_vp);
2962eee6217bSJason A. Harmening 	if (tvp != NULL) {
2963eee6217bSJason A. Harmening 		VOP_UNP_DETACH(tvp);
2964eee6217bSJason A. Harmening 		vdrop(tvp);
2965eee6217bSJason A. Harmening 	}
2966eee6217bSJason A. Harmening 	return (0);
2967eee6217bSJason A. Harmening }
2968eee6217bSJason A. Harmening 
2969d00947d8SCraig Rodrigues struct vop_vector unionfs_vnodeops = {
2970aec0fb7bSPoul-Henning Kamp 	.vop_default =		&default_vnodeops,
297183c64397SPoul-Henning Kamp 
2972d00947d8SCraig Rodrigues 	.vop_access =		unionfs_access,
2973d00947d8SCraig Rodrigues 	.vop_aclcheck =		unionfs_aclcheck,
2974d00947d8SCraig Rodrigues 	.vop_advlock =		unionfs_advlock,
2975aec0fb7bSPoul-Henning Kamp 	.vop_bmap =		VOP_EOPNOTSUPP,
2976dc2dd185SDaichi GOTO 	.vop_cachedlookup =	unionfs_lookup,
2977d00947d8SCraig Rodrigues 	.vop_close =		unionfs_close,
2978d00947d8SCraig Rodrigues 	.vop_closeextattr =	unionfs_closeextattr,
2979d00947d8SCraig Rodrigues 	.vop_create =		unionfs_create,
2980d00947d8SCraig Rodrigues 	.vop_deleteextattr =	unionfs_deleteextattr,
2981d00947d8SCraig Rodrigues 	.vop_fsync =		unionfs_fsync,
2982d00947d8SCraig Rodrigues 	.vop_getacl =		unionfs_getacl,
2983d00947d8SCraig Rodrigues 	.vop_getattr =		unionfs_getattr,
2984d00947d8SCraig Rodrigues 	.vop_getextattr =	unionfs_getextattr,
2985d00947d8SCraig Rodrigues 	.vop_getwritemount =	unionfs_getwritemount,
2986d00947d8SCraig Rodrigues 	.vop_inactive =		unionfs_inactive,
29871e2f0cebSMateusz Guzik 	.vop_need_inactive =	vop_stdneed_inactive,
2988d711884eSJason A. Harmening 	.vop_islocked =		vop_stdislocked,
2989d00947d8SCraig Rodrigues 	.vop_ioctl =		unionfs_ioctl,
2990d00947d8SCraig Rodrigues 	.vop_link =		unionfs_link,
2991d00947d8SCraig Rodrigues 	.vop_listextattr =	unionfs_listextattr,
2992d413d210SKonstantin Belousov 	.vop_lock1 =		unionfs_lock,
2993dc2dd185SDaichi GOTO 	.vop_lookup =		vfs_cache_lookup,
2994d00947d8SCraig Rodrigues 	.vop_mkdir =		unionfs_mkdir,
2995d00947d8SCraig Rodrigues 	.vop_mknod =		unionfs_mknod,
2996d00947d8SCraig Rodrigues 	.vop_open =		unionfs_open,
2997d00947d8SCraig Rodrigues 	.vop_openextattr =	unionfs_openextattr,
2998d00947d8SCraig Rodrigues 	.vop_pathconf =		unionfs_pathconf,
2999d00947d8SCraig Rodrigues 	.vop_poll =		unionfs_poll,
3000d00947d8SCraig Rodrigues 	.vop_print =		unionfs_print,
3001d00947d8SCraig Rodrigues 	.vop_read =		unionfs_read,
3002d00947d8SCraig Rodrigues 	.vop_readdir =		unionfs_readdir,
3003d00947d8SCraig Rodrigues 	.vop_readlink =		unionfs_readlink,
3004d00947d8SCraig Rodrigues 	.vop_reclaim =		unionfs_reclaim,
3005d00947d8SCraig Rodrigues 	.vop_remove =		unionfs_remove,
3006d00947d8SCraig Rodrigues 	.vop_rename =		unionfs_rename,
3007d00947d8SCraig Rodrigues 	.vop_rmdir =		unionfs_rmdir,
3008d00947d8SCraig Rodrigues 	.vop_setacl =		unionfs_setacl,
3009d00947d8SCraig Rodrigues 	.vop_setattr =		unionfs_setattr,
3010d00947d8SCraig Rodrigues 	.vop_setextattr =	unionfs_setextattr,
3011d00947d8SCraig Rodrigues 	.vop_setlabel =		unionfs_setlabel,
3012d00947d8SCraig Rodrigues 	.vop_strategy =		unionfs_strategy,
3013d00947d8SCraig Rodrigues 	.vop_symlink =		unionfs_symlink,
3014d00947d8SCraig Rodrigues 	.vop_unlock =		unionfs_unlock,
3015d00947d8SCraig Rodrigues 	.vop_whiteout =		unionfs_whiteout,
3016d00947d8SCraig Rodrigues 	.vop_write =		unionfs_write,
301710bcafe9SPawel Jakub Dawidek 	.vop_vptofh =		unionfs_vptofh,
301830d49d53SKonstantin Belousov 	.vop_add_writecount =	unionfs_add_writecount,
3019cfc2cfecSJason A. Harmening 	.vop_vput_pair =	unionfs_vput_pair,
30209e891d43SJason A. Harmening 	.vop_set_text =		unionfs_set_text,
30219e891d43SJason A. Harmening 	.vop_unset_text = 	unionfs_unset_text,
3022eee6217bSJason A. Harmening 	.vop_unp_bind =		unionfs_unp_bind,
3023eee6217bSJason A. Harmening 	.vop_unp_connect =	unionfs_unp_connect,
3024eee6217bSJason A. Harmening 	.vop_unp_detach =	unionfs_unp_detach,
3025df8bae1dSRodney W. Grimes };
30266fa079fcSMateusz Guzik VFS_VOP_VECTOR_REGISTER(unionfs_vnodeops);
3027