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