11a710239Sad /*-
21a710239Sad * Copyright (c) 1994 Jan-Simon Pendry
31a710239Sad * Copyright (c) 1994
41a710239Sad * The Regents of the University of California. All rights reserved.
51a710239Sad * Copyright (c) 2005, 2006 Masanori Ozawa <ozawa@ongs.co.jp>, ONGS Inc.
61a710239Sad * Copyright (c) 2006 Daichi Goto <daichi@freebsd.org>
71a710239Sad *
81a710239Sad * This code is derived from software contributed to Berkeley by
91a710239Sad * Jan-Simon Pendry.
101a710239Sad *
111a710239Sad * Redistribution and use in source and binary forms, with or without
121a710239Sad * modification, are permitted provided that the following conditions
131a710239Sad * are met:
141a710239Sad * 1. Redistributions of source code must retain the above copyright
151a710239Sad * notice, this list of conditions and the following disclaimer.
161a710239Sad * 2. Redistributions in binary form must reproduce the above copyright
171a710239Sad * notice, this list of conditions and the following disclaimer in the
181a710239Sad * documentation and/or other materials provided with the distribution.
191a710239Sad * 4. Neither the name of the University nor the names of its contributors
201a710239Sad * may be used to endorse or promote products derived from this software
211a710239Sad * without specific prior written permission.
221a710239Sad *
231a710239Sad * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
241a710239Sad * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
251a710239Sad * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
261a710239Sad * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
271a710239Sad * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
281a710239Sad * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
291a710239Sad * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
301a710239Sad * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
311a710239Sad * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
321a710239Sad * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
331a710239Sad * SUCH DAMAGE.
341a710239Sad *
351a710239Sad * @(#)union_subr.c 8.20 (Berkeley) 5/20/95
361a710239Sad * $FreeBSD: src/sys/fs/unionfs/union_subr.c,v 1.99 2008/01/24 12:34:27 attilio Exp $
371a710239Sad */
381a710239Sad
391a710239Sad #include <sys/param.h>
401a710239Sad #include <sys/systm.h>
411a710239Sad #include <sys/kernel.h>
421a710239Sad #include <sys/lock.h>
431a710239Sad #include <sys/mutex.h>
441a710239Sad #include <sys/malloc.h>
451a710239Sad #include <sys/mount.h>
461a710239Sad #include <sys/namei.h>
471a710239Sad #include <sys/proc.h>
481a710239Sad #include <sys/vnode.h>
491a710239Sad #include <sys/dirent.h>
501a710239Sad #include <sys/fcntl.h>
511a710239Sad #include <sys/filedesc.h>
521a710239Sad #include <sys/stat.h>
531a710239Sad #include <sys/kauth.h>
541a710239Sad #include <sys/resourcevar.h>
551a710239Sad
561a710239Sad #include <fs/unionfs/unionfs.h>
571a710239Sad
581a710239Sad MALLOC_DEFINE(M_UNIONFSPATH, "UNIONFS path", "UNIONFS path private part");
591a710239Sad
601a710239Sad /*
611a710239Sad * Make a new or get existing unionfs node.
621a710239Sad *
631a710239Sad * uppervp and lowervp should be unlocked. Because if new unionfs vnode is
641a710239Sad * locked, uppervp or lowervp is locked too. In order to prevent dead lock,
651a710239Sad * you should not lock plurality simultaneously.
661a710239Sad */
671a710239Sad int
unionfs_nodeget(struct mount * mp,struct vnode * uppervp,struct vnode * lowervp,struct vnode * dvp,struct vnode ** vpp,struct componentname * cnp)681a710239Sad unionfs_nodeget(struct mount *mp, struct vnode *uppervp,
691a710239Sad struct vnode *lowervp, struct vnode *dvp,
701a710239Sad struct vnode **vpp, struct componentname *cnp)
711a710239Sad {
721a710239Sad struct unionfs_mount *ump;
731a710239Sad struct unionfs_node *unp;
741a710239Sad struct vnode *vp;
751a710239Sad int error;
761a710239Sad const char *path;
771a710239Sad
781a710239Sad ump = MOUNTTOUNIONFSMOUNT(mp);
791a710239Sad path = (cnp ? cnp->cn_nameptr : NULL);
801a710239Sad
811a710239Sad if (uppervp == NULLVP && lowervp == NULLVP)
821a710239Sad panic("unionfs_nodeget: upper and lower is null");
831a710239Sad
841a710239Sad /* If it has no ISLASTCN flag, path check is skipped. */
851a710239Sad if (cnp && !(cnp->cn_flags & ISLASTCN))
861a710239Sad path = NULL;
871a710239Sad
881a710239Sad if ((uppervp == NULLVP || ump->um_uppervp != uppervp) ||
891a710239Sad (lowervp == NULLVP || ump->um_lowervp != lowervp)) {
901a710239Sad if (dvp == NULLVP)
911a710239Sad return (EINVAL);
921a710239Sad }
931a710239Sad
94f1146e73Srmind /*
95f1146e73Srmind * Get a new vnode and share the lock with upper layer vnode,
96f1146e73Srmind * unless layers are inverted.
97f1146e73Srmind */
98f1146e73Srmind vnode_t *svp = (uppervp != NULLVP) ? uppervp : lowervp;
99*bfecfc14Schristos error = vcache_get(mp, svp, sizeof(svp), &vp);
1001a710239Sad if (error != 0) {
1011a710239Sad return (error);
1021a710239Sad }
1031a710239Sad if (dvp != NULLVP)
1041a710239Sad vref(dvp);
1051a710239Sad if (uppervp != NULLVP)
1061a710239Sad vref(uppervp);
1071a710239Sad if (lowervp != NULLVP)
1081a710239Sad vref(lowervp);
1091a710239Sad
110f1146e73Srmind unp = kmem_zalloc(sizeof(*unp), KM_SLEEP);
1111a710239Sad unp->un_vnode = vp;
1121a710239Sad unp->un_uppervp = uppervp;
1131a710239Sad unp->un_lowervp = lowervp;
1141a710239Sad unp->un_dvp = dvp;
1151a710239Sad
1161a710239Sad if (path != NULL) {
1171a710239Sad unp->un_path = (char *)
1181a710239Sad malloc(cnp->cn_namelen +1, M_UNIONFSPATH, M_WAITOK|M_ZERO);
119e2cb8590Scegger memcpy(unp->un_path, cnp->cn_nameptr, cnp->cn_namelen);
1201a710239Sad unp->un_path[cnp->cn_namelen] = '\0';
1211a710239Sad }
1221a710239Sad vp->v_type = (uppervp != NULLVP ? uppervp->v_type : lowervp->v_type);
1231a710239Sad vp->v_data = unp;
1241a710239Sad uvm_vnp_setsize(vp, 0);
1251a710239Sad
1261a710239Sad if ((uppervp != NULLVP && ump->um_uppervp == uppervp) &&
1271a710239Sad (lowervp != NULLVP && ump->um_lowervp == lowervp))
1281a710239Sad vp->v_vflag |= VV_ROOT;
1291a710239Sad
1301a710239Sad *vpp = vp;
1311a710239Sad
1321a710239Sad return (0);
1331a710239Sad }
1341a710239Sad
1351a710239Sad /*
1361a710239Sad * Clean up the unionfs node.
1371a710239Sad */
1381a710239Sad void
unionfs_noderem(struct vnode * vp)1391a710239Sad unionfs_noderem(struct vnode *vp)
1401a710239Sad {
1411a710239Sad struct unionfs_node *unp;
1421a710239Sad struct unionfs_node_status *unsp;
1431a710239Sad struct vnode *lvp;
1441a710239Sad struct vnode *uvp;
1451a710239Sad
1461a710239Sad /*
1471a710239Sad * Use the interlock to protect the clearing of v_data to
1481a710239Sad * prevent faults in unionfs_lock().
1491a710239Sad */
1501a710239Sad unp = VTOUNIONFS(vp);
1511a710239Sad lvp = unp->un_lowervp;
1521a710239Sad uvp = unp->un_uppervp;
1531a710239Sad unp->un_lowervp = unp->un_uppervp = NULLVP;
1541a710239Sad vp->v_data = NULL;
1551a710239Sad
1561a710239Sad if (lvp != NULLVP)
1571a710239Sad vrele(lvp);
1581a710239Sad if (uvp != NULLVP)
1591a710239Sad vrele(uvp);
1601a710239Sad if (unp->un_dvp != NULLVP) {
1611a710239Sad vrele(unp->un_dvp);
1621a710239Sad unp->un_dvp = NULLVP;
1631a710239Sad }
1641a710239Sad if (unp->un_path) {
1651a710239Sad free(unp->un_path, M_UNIONFSPATH);
1661a710239Sad unp->un_path = NULL;
1671a710239Sad }
1681a710239Sad
1691a710239Sad while ((unsp = LIST_FIRST(&(unp->un_unshead))) != NULL) {
1701a710239Sad LIST_REMOVE(unsp, uns_list);
1711a710239Sad free(unsp, M_TEMP);
1721a710239Sad }
1731a710239Sad kmem_free(unp, sizeof(*unp));
1741a710239Sad }
1751a710239Sad
1761a710239Sad /*
1771a710239Sad * Get the unionfs node status.
1781a710239Sad * You need exclusive lock this vnode.
1791a710239Sad */
1801a710239Sad void
unionfs_get_node_status(struct unionfs_node * unp,struct unionfs_node_status ** unspp)1811a710239Sad unionfs_get_node_status(struct unionfs_node *unp,
1821a710239Sad struct unionfs_node_status **unspp)
1831a710239Sad {
1841a710239Sad struct unionfs_node_status *unsp;
1851a710239Sad pid_t pid;
1861a710239Sad lwpid_t lid;
1871a710239Sad
1881a710239Sad KASSERT(NULL != unspp);
1891a710239Sad KASSERT(VOP_ISLOCKED(UNIONFSTOV(unp)) == LK_EXCLUSIVE);
1901a710239Sad
1911a710239Sad pid = curproc->p_pid;
1921a710239Sad lid = curlwp->l_lid;
1931a710239Sad
1941a710239Sad LIST_FOREACH(unsp, &(unp->un_unshead), uns_list) {
1951a710239Sad if (unsp->uns_pid == pid && unsp->uns_lid == lid) {
1961a710239Sad *unspp = unsp;
1971a710239Sad return;
1981a710239Sad }
1991a710239Sad }
2001a710239Sad
2011a710239Sad /* create a new unionfs node status */
2021a710239Sad unsp = kmem_zalloc(sizeof(*unsp), KM_SLEEP);
2031a710239Sad unsp->uns_pid = pid;
2041a710239Sad unsp->uns_lid = lid;
2051a710239Sad LIST_INSERT_HEAD(&(unp->un_unshead), unsp, uns_list);
2061a710239Sad
2071a710239Sad *unspp = unsp;
2081a710239Sad }
2091a710239Sad
2101a710239Sad /*
2111a710239Sad * Remove the unionfs node status, if you can.
2121a710239Sad * You need exclusive lock this vnode.
2131a710239Sad */
2141a710239Sad void
unionfs_tryrem_node_status(struct unionfs_node * unp,struct unionfs_node_status * unsp)2151a710239Sad unionfs_tryrem_node_status(struct unionfs_node *unp,
2161a710239Sad struct unionfs_node_status *unsp)
2171a710239Sad {
2181a710239Sad KASSERT(NULL != unsp);
2191a710239Sad KASSERT(VOP_ISLOCKED(UNIONFSTOV(unp)) == LK_EXCLUSIVE);
2201a710239Sad
2211a710239Sad if (0 < unsp->uns_lower_opencnt || 0 < unsp->uns_upper_opencnt)
2221a710239Sad return;
2231a710239Sad
2241a710239Sad LIST_REMOVE(unsp, uns_list);
2251a710239Sad kmem_free(unsp, sizeof(*unsp));
2261a710239Sad }
2271a710239Sad
2281a710239Sad /*
2291a710239Sad * Create upper node attr.
2301a710239Sad */
2311a710239Sad void
unionfs_create_uppervattr_core(struct unionfs_mount * ump,struct vattr * lva,struct vattr * uva)2321a710239Sad unionfs_create_uppervattr_core(struct unionfs_mount *ump,
2331a710239Sad struct vattr *lva,
2341a710239Sad struct vattr *uva)
2351a710239Sad {
236c3183f32Spooka vattr_null(uva);
2371a710239Sad uva->va_type = lva->va_type;
2381a710239Sad uva->va_atime = lva->va_atime;
2391a710239Sad uva->va_mtime = lva->va_mtime;
2401a710239Sad uva->va_ctime = lva->va_ctime;
2411a710239Sad
2421a710239Sad switch (ump->um_copymode) {
2431a710239Sad case UNIONFS_TRANSPARENT:
2441a710239Sad uva->va_mode = lva->va_mode;
2451a710239Sad uva->va_uid = lva->va_uid;
2461a710239Sad uva->va_gid = lva->va_gid;
2471a710239Sad break;
2481a710239Sad case UNIONFS_MASQUERADE:
2491a710239Sad if (ump->um_uid == lva->va_uid) {
2501a710239Sad uva->va_mode = lva->va_mode & 077077;
2511a710239Sad uva->va_mode |= (lva->va_type == VDIR ? ump->um_udir : ump->um_ufile) & 0700;
2521a710239Sad uva->va_uid = lva->va_uid;
2531a710239Sad uva->va_gid = lva->va_gid;
2541a710239Sad } else {
2551a710239Sad uva->va_mode = (lva->va_type == VDIR ? ump->um_udir : ump->um_ufile);
2561a710239Sad uva->va_uid = ump->um_uid;
2571a710239Sad uva->va_gid = ump->um_gid;
2581a710239Sad }
2591a710239Sad break;
2601a710239Sad default: /* UNIONFS_TRADITIONAL */
2611a710239Sad uva->va_mode = 0777 & ~curproc->p_cwdi->cwdi_cmask;
2621a710239Sad uva->va_uid = ump->um_uid;
2631a710239Sad uva->va_gid = ump->um_gid;
2641a710239Sad break;
2651a710239Sad }
2661a710239Sad }
2671a710239Sad
2681a710239Sad /*
2691a710239Sad * Create upper node attr.
2701a710239Sad */
2711a710239Sad int
unionfs_create_uppervattr(struct unionfs_mount * ump,struct vnode * lvp,struct vattr * uva,kauth_cred_t cred)2721a710239Sad unionfs_create_uppervattr(struct unionfs_mount *ump,
2731a710239Sad struct vnode *lvp,
2741a710239Sad struct vattr *uva,
2751a710239Sad kauth_cred_t cred)
2761a710239Sad {
2771a710239Sad int error;
2781a710239Sad struct vattr lva;
2791a710239Sad
2801a710239Sad if ((error = VOP_GETATTR(lvp, &lva, cred)))
2811a710239Sad return (error);
2821a710239Sad
2831a710239Sad unionfs_create_uppervattr_core(ump, &lva, uva);
2841a710239Sad
2851a710239Sad return (error);
2861a710239Sad }
2871a710239Sad
2881a710239Sad /*
2891a710239Sad * relookup
2901a710239Sad *
2911a710239Sad * dvp should be locked on entry and will be locked on return.
2921a710239Sad *
2931a710239Sad * If an error is returned, *vpp will be invalid, otherwise it will hold a
2941a710239Sad * locked, referenced vnode. If *vpp == dvp then remember that only one
2951a710239Sad * LK_EXCLUSIVE lock is held.
2961a710239Sad */
2971a710239Sad static int
unionfs_relookup(struct vnode * dvp,struct vnode ** vpp,struct componentname * cnp,struct componentname * cn,char ** pnbuf_ret,const char * path,int pathlen,u_long nameiop)2981a710239Sad unionfs_relookup(struct vnode *dvp, struct vnode **vpp,
2991a710239Sad struct componentname *cnp, struct componentname *cn,
300d4eb0539Sdholland char **pnbuf_ret,
3011a710239Sad const char *path, int pathlen, u_long nameiop)
3021a710239Sad {
3031a710239Sad int error;
304d4eb0539Sdholland char *pnbuf;
3051a710239Sad
3061a710239Sad cn->cn_namelen = pathlen;
307d4eb0539Sdholland pnbuf = PNBUF_GET();
308d4eb0539Sdholland memcpy(pnbuf, path, pathlen);
309d4eb0539Sdholland pnbuf[pathlen] = '\0';
3101a710239Sad
3111a710239Sad cn->cn_nameiop = nameiop;
31214402d0fSdholland cn->cn_flags = (LOCKPARENT | LOCKLEAF | ISLASTCN);
3131a710239Sad cn->cn_cred = cnp->cn_cred;
3141a710239Sad
315d4eb0539Sdholland cn->cn_nameptr = pnbuf;
3161a710239Sad
3171a710239Sad if (nameiop == DELETE)
318eb2c22d2Sdholland cn->cn_flags |= (cnp->cn_flags & DOWHITEOUT);
3191a710239Sad
3201a710239Sad vref(dvp);
3211423e65bShannken VOP_UNLOCK(dvp);
3221a710239Sad
323eb2c22d2Sdholland if ((error = relookup(dvp, vpp, cn, 0))) {
324d4eb0539Sdholland PNBUF_PUT(pnbuf);
325d4eb0539Sdholland *pnbuf_ret = NULL;
3261a710239Sad vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
327d4eb0539Sdholland } else {
328d4eb0539Sdholland *pnbuf_ret = pnbuf;
3291a710239Sad vrele(dvp);
330d4eb0539Sdholland }
3311a710239Sad
3321a710239Sad return (error);
3331a710239Sad }
3341a710239Sad
3351a710239Sad /*
3361a710239Sad * relookup for CREATE namei operation.
3371a710239Sad *
3381a710239Sad * dvp is unionfs vnode. dvp should be locked.
3391a710239Sad *
3401a710239Sad * If it called 'unionfs_copyfile' function by unionfs_link etc,
3411a710239Sad * VOP_LOOKUP information is broken.
3421a710239Sad * So it need relookup in order to create link etc.
3431a710239Sad */
3441a710239Sad int
unionfs_relookup_for_create(struct vnode * dvp,struct componentname * cnp)3451a710239Sad unionfs_relookup_for_create(struct vnode *dvp, struct componentname *cnp)
3461a710239Sad {
3471a710239Sad int error;
3481a710239Sad struct vnode *udvp;
3491a710239Sad struct vnode *vp;
3501a710239Sad struct componentname cn;
351d4eb0539Sdholland char *pnbuf;
3521a710239Sad
3531a710239Sad udvp = UNIONFSVPTOUPPERVP(dvp);
3541a710239Sad vp = NULLVP;
3551a710239Sad
356d4eb0539Sdholland error = unionfs_relookup(udvp, &vp, cnp, &cn, &pnbuf,
357d4eb0539Sdholland cnp->cn_nameptr,
3581a710239Sad strlen(cnp->cn_nameptr), CREATE);
3591a710239Sad if (error)
3601a710239Sad return (error);
3611a710239Sad
3621a710239Sad if (vp != NULLVP) {
3631a710239Sad if (udvp == vp)
3641a710239Sad vrele(vp);
3651a710239Sad else
3661a710239Sad vput(vp);
3671a710239Sad
3681a710239Sad error = EEXIST;
3691a710239Sad }
3701a710239Sad
371d4eb0539Sdholland PNBUF_PUT(pnbuf);
3721a710239Sad
3731a710239Sad if (!error) {
3741a710239Sad cnp->cn_flags = cn.cn_flags;
3751a710239Sad }
3761a710239Sad
3771a710239Sad return (error);
3781a710239Sad }
3791a710239Sad
3801a710239Sad /*
3811a710239Sad * relookup for DELETE namei operation.
3821a710239Sad *
3831a710239Sad * dvp is unionfs vnode. dvp should be locked.
3841a710239Sad */
3851a710239Sad int
unionfs_relookup_for_delete(struct vnode * dvp,struct componentname * cnp)3861a710239Sad unionfs_relookup_for_delete(struct vnode *dvp, struct componentname *cnp)
3871a710239Sad {
3881a710239Sad int error;
3891a710239Sad struct vnode *udvp;
3901a710239Sad struct vnode *vp;
3911a710239Sad struct componentname cn;
392d4eb0539Sdholland char *pnbuf;
3931a710239Sad
3941a710239Sad udvp = UNIONFSVPTOUPPERVP(dvp);
3951a710239Sad vp = NULLVP;
3961a710239Sad
397d4eb0539Sdholland error = unionfs_relookup(udvp, &vp, cnp, &cn, &pnbuf, cnp->cn_nameptr,
3981a710239Sad strlen(cnp->cn_nameptr), DELETE);
3991a710239Sad if (error)
4001a710239Sad return (error);
4011a710239Sad
4021a710239Sad if (vp == NULLVP)
4031a710239Sad error = ENOENT;
4041a710239Sad else {
4051a710239Sad if (udvp == vp)
4061a710239Sad vrele(vp);
4071a710239Sad else
4081a710239Sad vput(vp);
4091a710239Sad }
4101a710239Sad
411d4eb0539Sdholland PNBUF_PUT(pnbuf);
4121a710239Sad
4131a710239Sad if (!error) {
4141a710239Sad cnp->cn_flags = cn.cn_flags;
4151a710239Sad }
4161a710239Sad
4171a710239Sad return (error);
4181a710239Sad }
4191a710239Sad
4201a710239Sad /*
4211a710239Sad * relookup for RENAME namei operation.
4221a710239Sad *
4231a710239Sad * dvp is unionfs vnode. dvp should be locked.
4241a710239Sad */
4251a710239Sad int
unionfs_relookup_for_rename(struct vnode * dvp,struct componentname * cnp)4261a710239Sad unionfs_relookup_for_rename(struct vnode *dvp, struct componentname *cnp)
4271a710239Sad {
4281a710239Sad int error;
4291a710239Sad struct vnode *udvp;
4301a710239Sad struct vnode *vp;
4311a710239Sad struct componentname cn;
432d4eb0539Sdholland char *pnbuf;
4331a710239Sad
4341a710239Sad udvp = UNIONFSVPTOUPPERVP(dvp);
4351a710239Sad vp = NULLVP;
4361a710239Sad
437d4eb0539Sdholland error = unionfs_relookup(udvp, &vp, cnp, &cn, &pnbuf, cnp->cn_nameptr,
4381a710239Sad strlen(cnp->cn_nameptr), RENAME);
4391a710239Sad if (error)
4401a710239Sad return (error);
4411a710239Sad
4421a710239Sad if (vp != NULLVP) {
4431a710239Sad if (udvp == vp)
4441a710239Sad vrele(vp);
4451a710239Sad else
4461a710239Sad vput(vp);
4471a710239Sad }
4481a710239Sad
449d4eb0539Sdholland PNBUF_PUT(pnbuf);
4501a710239Sad
4511a710239Sad if (!error) {
4521a710239Sad cnp->cn_flags = cn.cn_flags;
4531a710239Sad }
4541a710239Sad
4551a710239Sad return (error);
4561a710239Sad
4571a710239Sad }
4581a710239Sad
4591a710239Sad /*
4601a710239Sad * Update the unionfs_node.
4611a710239Sad *
4621a710239Sad * uvp is new locked upper vnode. unionfs vnode's lock will be exchanged to the
4631a710239Sad * uvp's lock and lower's lock will be unlocked.
4641a710239Sad */
4651a710239Sad static void
unionfs_node_update(struct unionfs_node * unp,struct vnode * uvp)4661a710239Sad unionfs_node_update(struct unionfs_node *unp, struct vnode *uvp)
4671a710239Sad {
4681a710239Sad struct vnode *vp;
4691a710239Sad struct vnode *lvp;
4701a710239Sad
4711a710239Sad vp = UNIONFSTOV(unp);
4721a710239Sad lvp = unp->un_lowervp;
4731a710239Sad
4741a710239Sad /*
4751a710239Sad * lock update
4761a710239Sad */
477f1146e73Srmind mutex_enter(vp->v_interlock);
4781a710239Sad unp->un_uppervp = uvp;
479f6c438baShannken KASSERT(VOP_ISLOCKED(lvp) == LK_EXCLUSIVE);
480f1146e73Srmind mutex_exit(vp->v_interlock);
4811a710239Sad }
4821a710239Sad
4831a710239Sad /*
4841a710239Sad * Create a new shadow dir.
4851a710239Sad *
4861a710239Sad * udvp should be locked on entry and will be locked on return.
4871a710239Sad *
4881a710239Sad * If no error returned, unp will be updated.
4891a710239Sad */
4901a710239Sad int
unionfs_mkshadowdir(struct unionfs_mount * ump,struct vnode * udvp,struct unionfs_node * unp,struct componentname * cnp)4911a710239Sad unionfs_mkshadowdir(struct unionfs_mount *ump, struct vnode *udvp,
4921a710239Sad struct unionfs_node *unp, struct componentname *cnp)
4931a710239Sad {
4941a710239Sad int error;
4951a710239Sad struct vnode *lvp;
4961a710239Sad struct vnode *uvp;
4971a710239Sad struct vattr va;
4981a710239Sad struct vattr lva;
4991a710239Sad struct componentname cn;
500d4eb0539Sdholland char *pnbuf;
5011a710239Sad
5021a710239Sad if (unp->un_uppervp != NULLVP)
5031a710239Sad return (EEXIST);
5041a710239Sad
5051a710239Sad lvp = unp->un_lowervp;
5061a710239Sad uvp = NULLVP;
5071a710239Sad
5081a710239Sad memset(&cn, 0, sizeof(cn));
5091a710239Sad
5101a710239Sad if ((error = VOP_GETATTR(lvp, &lva, cnp->cn_cred)))
5111a710239Sad goto unionfs_mkshadowdir_abort;
5121a710239Sad
513d4eb0539Sdholland if ((error = unionfs_relookup(udvp, &uvp, cnp, &cn, &pnbuf,
514d4eb0539Sdholland cnp->cn_nameptr, cnp->cn_namelen, CREATE)))
5151a710239Sad goto unionfs_mkshadowdir_abort;
5161a710239Sad if (uvp != NULLVP) {
5171a710239Sad if (udvp == uvp)
5181a710239Sad vrele(uvp);
5191a710239Sad else
5201a710239Sad vput(uvp);
5211a710239Sad
5221a710239Sad error = EEXIST;
5231a710239Sad goto unionfs_mkshadowdir_free_out;
5241a710239Sad }
5251a710239Sad
5261a710239Sad unionfs_create_uppervattr_core(ump, &lva, &va);
5271a710239Sad
5281a710239Sad error = VOP_MKDIR(udvp, &uvp, &cn, &va);
5291a710239Sad
5301a710239Sad if (!error) {
5311a710239Sad unionfs_node_update(unp, uvp);
5321a710239Sad
5331a710239Sad /*
5341a710239Sad * XXX The bug which cannot set uid/gid was corrected.
5351a710239Sad * Ignore errors. XXXNETBSD Why is this done as root?
5361a710239Sad */
5371a710239Sad va.va_type = VNON;
5381a710239Sad VOP_SETATTR(uvp, &va, lwp0.l_cred);
5391a710239Sad }
5401a710239Sad
5411a710239Sad unionfs_mkshadowdir_free_out:
542d4eb0539Sdholland PNBUF_PUT(pnbuf);
5431a710239Sad
5441a710239Sad unionfs_mkshadowdir_abort:
5451a710239Sad
5461a710239Sad return (error);
5471a710239Sad }
5481a710239Sad
5491a710239Sad /*
5501a710239Sad * Create a new whiteout.
5511a710239Sad *
5521a710239Sad * dvp should be locked on entry and will be locked on return.
5531a710239Sad */
5541a710239Sad int
unionfs_mkwhiteout(struct vnode * dvp,struct componentname * cnp,const char * path)5551a710239Sad unionfs_mkwhiteout(struct vnode *dvp, struct componentname *cnp, const char *path)
5561a710239Sad {
5571a710239Sad int error;
5581a710239Sad struct vnode *wvp;
5591a710239Sad struct componentname cn;
560d4eb0539Sdholland char *pnbuf;
5611a710239Sad
5621a710239Sad if (path == NULL)
5631a710239Sad path = cnp->cn_nameptr;
5641a710239Sad
5651a710239Sad wvp = NULLVP;
566d4eb0539Sdholland if ((error = unionfs_relookup(dvp, &wvp, cnp, &cn, &pnbuf,
567d4eb0539Sdholland path, strlen(path), CREATE)))
5681a710239Sad return (error);
5691a710239Sad if (wvp != NULLVP) {
570d4eb0539Sdholland PNBUF_PUT(pnbuf);
5711a710239Sad if (dvp == wvp)
5721a710239Sad vrele(wvp);
5731a710239Sad else
5741a710239Sad vput(wvp);
5751a710239Sad
5761a710239Sad return (EEXIST);
5771a710239Sad }
5781a710239Sad
579d4eb0539Sdholland PNBUF_PUT(pnbuf);
5801a710239Sad
5811a710239Sad return (error);
5821a710239Sad }
5831a710239Sad
5841a710239Sad /*
5851a710239Sad * Create a new vnode for create a new shadow file.
5861a710239Sad *
5871a710239Sad * If an error is returned, *vpp will be invalid, otherwise it will hold a
5881a710239Sad * locked, referenced and opened vnode.
5891a710239Sad *
5901a710239Sad * unp is never updated.
5911a710239Sad */
5921a710239Sad static int
unionfs_vn_create_on_upper(struct vnode ** vpp,struct vnode * udvp,struct unionfs_node * unp,struct vattr * uvap)5931a710239Sad unionfs_vn_create_on_upper(struct vnode **vpp, struct vnode *udvp,
5941a710239Sad struct unionfs_node *unp, struct vattr *uvap)
5951a710239Sad {
5961a710239Sad struct unionfs_mount *ump;
5971a710239Sad struct vnode *vp;
5981a710239Sad struct vnode *lvp;
5991a710239Sad kauth_cred_t cred;
6001a710239Sad struct vattr lva;
6011a710239Sad int fmode;
6021a710239Sad int error;
6031a710239Sad struct componentname cn;
604d4eb0539Sdholland char *pnbuf;
6051a710239Sad
6061a710239Sad ump = MOUNTTOUNIONFSMOUNT(UNIONFSTOV(unp)->v_mount);
6071a710239Sad vp = NULLVP;
6081a710239Sad lvp = unp->un_lowervp;
6091a710239Sad cred = kauth_cred_get();
6101a710239Sad fmode = FFLAGS(O_WRONLY | O_CREAT | O_TRUNC | O_EXCL);
6111a710239Sad error = 0;
6121a710239Sad
6131a710239Sad if ((error = VOP_GETATTR(lvp, &lva, cred)) != 0)
6141a710239Sad return (error);
6151a710239Sad unionfs_create_uppervattr_core(ump, &lva, uvap);
6161a710239Sad
6171a710239Sad if (unp->un_path == NULL)
6181a710239Sad panic("unionfs: un_path is null");
6191a710239Sad
6201a710239Sad cn.cn_namelen = strlen(unp->un_path);
621d4eb0539Sdholland pnbuf = PNBUF_GET();
622d4eb0539Sdholland memcpy(pnbuf, unp->un_path, cn.cn_namelen + 1);
6231a710239Sad cn.cn_nameiop = CREATE;
62414402d0fSdholland cn.cn_flags = (LOCKPARENT | LOCKLEAF | ISLASTCN);
6251a710239Sad cn.cn_cred = cred;
626d4eb0539Sdholland cn.cn_nameptr = pnbuf;
6271a710239Sad
6281a710239Sad vref(udvp);
629eb2c22d2Sdholland if ((error = relookup(udvp, &vp, &cn, 0)) != 0)
6301a710239Sad goto unionfs_vn_create_on_upper_free_out2;
6311a710239Sad vrele(udvp);
6321a710239Sad
6331a710239Sad if (vp != NULLVP) {
6341a710239Sad if (vp == udvp)
6351a710239Sad vrele(vp);
6361a710239Sad else
6371a710239Sad vput(vp);
6381a710239Sad error = EEXIST;
6391a710239Sad goto unionfs_vn_create_on_upper_free_out1;
6401a710239Sad }
6411a710239Sad
6421a710239Sad if ((error = VOP_CREATE(udvp, &vp, &cn, uvap)) != 0)
6431a710239Sad goto unionfs_vn_create_on_upper_free_out1;
6441a710239Sad
6451a710239Sad if ((error = VOP_OPEN(vp, fmode, cred)) != 0) {
6461a710239Sad vput(vp);
6471a710239Sad goto unionfs_vn_create_on_upper_free_out1;
6481a710239Sad }
6491a710239Sad vp->v_writecount++;
6501a710239Sad *vpp = vp;
6511a710239Sad
6521a710239Sad unionfs_vn_create_on_upper_free_out1:
6531423e65bShannken VOP_UNLOCK(udvp);
6541a710239Sad
6551a710239Sad unionfs_vn_create_on_upper_free_out2:
656d4eb0539Sdholland PNBUF_PUT(pnbuf);
6571a710239Sad
6581a710239Sad return (error);
6591a710239Sad }
6601a710239Sad
6611a710239Sad /*
6621a710239Sad * Copy from lvp to uvp.
6631a710239Sad *
6641a710239Sad * lvp and uvp should be locked and opened on entry and will be locked and
6651a710239Sad * opened on return.
6661a710239Sad */
6671a710239Sad static int
unionfs_copyfile_core(struct vnode * lvp,struct vnode * uvp,kauth_cred_t cred)6681a710239Sad unionfs_copyfile_core(struct vnode *lvp, struct vnode *uvp,
6691a710239Sad kauth_cred_t cred)
6701a710239Sad {
6711a710239Sad int error;
6721a710239Sad off_t offset;
6731a710239Sad int count;
6741a710239Sad int bufoffset;
6751a710239Sad char *buf;
6761a710239Sad struct uio uio;
6771a710239Sad struct iovec iov;
6781a710239Sad
6791a710239Sad error = 0;
6801a710239Sad memset(&uio, 0, sizeof(uio));
6811a710239Sad UIO_SETUP_SYSSPACE(&uio);
6821a710239Sad uio.uio_offset = 0;
6831a710239Sad
6841a710239Sad buf = kmem_alloc(MAXBSIZE, KM_SLEEP);
6851a710239Sad while (error == 0) {
6861a710239Sad offset = uio.uio_offset;
6871a710239Sad
6881a710239Sad uio.uio_iov = &iov;
6891a710239Sad uio.uio_iovcnt = 1;
6901a710239Sad iov.iov_base = buf;
6911a710239Sad iov.iov_len = MAXBSIZE;
6921a710239Sad uio.uio_resid = iov.iov_len;
6931a710239Sad uio.uio_rw = UIO_READ;
6941a710239Sad
6951a710239Sad if ((error = VOP_READ(lvp, &uio, 0, cred)) != 0)
6961a710239Sad break;
6971a710239Sad if ((count = MAXBSIZE - uio.uio_resid) == 0)
6981a710239Sad break;
6991a710239Sad
7001a710239Sad bufoffset = 0;
7011a710239Sad while (bufoffset < count) {
7021a710239Sad uio.uio_iov = &iov;
7031a710239Sad uio.uio_iovcnt = 1;
7041a710239Sad iov.iov_base = buf + bufoffset;
7051a710239Sad iov.iov_len = count - bufoffset;
7061a710239Sad uio.uio_offset = offset + bufoffset;
7071a710239Sad uio.uio_resid = iov.iov_len;
7081a710239Sad uio.uio_rw = UIO_WRITE;
7091a710239Sad
7101a710239Sad if ((error = VOP_WRITE(uvp, &uio, 0, cred)) != 0)
7111a710239Sad break;
7121a710239Sad
7131a710239Sad bufoffset += (count - bufoffset) - uio.uio_resid;
7141a710239Sad }
7151a710239Sad
7161a710239Sad uio.uio_offset = offset + bufoffset;
7171a710239Sad }
7181a710239Sad
7191a710239Sad kmem_free(buf, MAXBSIZE);
7201a710239Sad
7211a710239Sad return (error);
7221a710239Sad }
7231a710239Sad
7241a710239Sad /*
7251a710239Sad * Copy file from lower to upper.
7261a710239Sad *
7271a710239Sad * If you need copy of the contents, set 1 to docopy. Otherwise, set 0 to
7281a710239Sad * docopy.
7291a710239Sad *
7301a710239Sad * If no error returned, unp will be updated.
7311a710239Sad */
7321a710239Sad int
unionfs_copyfile(struct unionfs_node * unp,int docopy,kauth_cred_t cred)7331a710239Sad unionfs_copyfile(struct unionfs_node *unp, int docopy, kauth_cred_t cred)
7341a710239Sad {
7351a710239Sad int error;
7361a710239Sad struct vnode *udvp;
7371a710239Sad struct vnode *lvp;
7381a710239Sad struct vnode *uvp;
7391a710239Sad struct vattr uva;
7401a710239Sad
7411a710239Sad lvp = unp->un_lowervp;
7421a710239Sad uvp = NULLVP;
7431a710239Sad
7441a710239Sad if ((UNIONFSTOV(unp)->v_mount->mnt_flag & MNT_RDONLY))
7451a710239Sad return (EROFS);
7461a710239Sad if (unp->un_dvp == NULLVP)
7471a710239Sad return (EINVAL);
7481a710239Sad if (unp->un_uppervp != NULLVP)
7491a710239Sad return (EEXIST);
7501a710239Sad udvp = VTOUNIONFS(unp->un_dvp)->un_uppervp;
7511a710239Sad if (udvp == NULLVP)
7521a710239Sad return (EROFS);
7531a710239Sad if ((udvp->v_mount->mnt_flag & MNT_RDONLY))
7541a710239Sad return (EROFS);
7551a710239Sad
7561a710239Sad error = VOP_ACCESS(lvp, VREAD, cred);
7571a710239Sad if (error != 0)
7581a710239Sad return (error);
7591a710239Sad
7601a710239Sad error = unionfs_vn_create_on_upper(&uvp, udvp, unp, &uva);
7611a710239Sad if (error != 0)
7621a710239Sad return (error);
7631a710239Sad
7641a710239Sad if (docopy != 0) {
7651a710239Sad error = VOP_OPEN(lvp, FREAD, cred);
7661a710239Sad if (error == 0) {
7671a710239Sad error = unionfs_copyfile_core(lvp, uvp, cred);
7681a710239Sad VOP_CLOSE(lvp, FREAD, cred);
7691a710239Sad }
7701a710239Sad }
7711a710239Sad VOP_CLOSE(uvp, FWRITE, cred);
7721a710239Sad uvp->v_writecount--;
7731a710239Sad
7741a710239Sad if (error == 0) {
7751a710239Sad /* Reset the attributes. Ignore errors. */
7761a710239Sad uva.va_type = VNON;
7771a710239Sad VOP_SETATTR(uvp, &uva, cred);
7781a710239Sad }
7791a710239Sad
7801a710239Sad unionfs_node_update(unp, uvp);
7811a710239Sad
7821a710239Sad return (error);
7831a710239Sad }
7841a710239Sad
7851a710239Sad /*
7861a710239Sad * It checks whether vp can rmdir. (check empty)
7871a710239Sad *
7881a710239Sad * vp is unionfs vnode.
7891a710239Sad * vp should be locked.
7901a710239Sad */
7911a710239Sad int
unionfs_check_rmdir(struct vnode * vp,kauth_cred_t cred)7921a710239Sad unionfs_check_rmdir(struct vnode *vp, kauth_cred_t cred)
7931a710239Sad {
7941a710239Sad int error;
7951a710239Sad int eofflag;
7961a710239Sad int lookuperr;
7971a710239Sad struct vnode *uvp;
7981a710239Sad struct vnode *lvp;
7991a710239Sad struct vnode *tvp;
8001a710239Sad struct vattr va;
8011a710239Sad struct componentname cn;
8021a710239Sad /*
8031a710239Sad * The size of buf needs to be larger than DIRBLKSIZ.
8041a710239Sad */
8051a710239Sad char buf[256 * 6];
8061a710239Sad struct dirent *dp;
8071a710239Sad struct dirent *edp;
8081a710239Sad struct uio uio;
8091a710239Sad struct iovec iov;
8101a710239Sad
8111a710239Sad KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
8121a710239Sad
8131a710239Sad eofflag = 0;
8141a710239Sad uvp = UNIONFSVPTOUPPERVP(vp);
8151a710239Sad lvp = UNIONFSVPTOLOWERVP(vp);
8161a710239Sad
8171a710239Sad /* check opaque */
8181a710239Sad if ((error = VOP_GETATTR(uvp, &va, cred)) != 0)
8191a710239Sad return (error);
8201a710239Sad if (va.va_flags & OPAQUE)
8211a710239Sad return (0);
8221a710239Sad
8231a710239Sad /* open vnode */
8241a710239Sad if ((error = VOP_ACCESS(vp, VEXEC|VREAD, cred)) != 0)
8251a710239Sad return (error);
8261a710239Sad if ((error = VOP_OPEN(vp, FREAD, cred)) != 0)
8271a710239Sad return (error);
8281a710239Sad
8291a710239Sad UIO_SETUP_SYSSPACE(&uio);
8301a710239Sad uio.uio_rw = UIO_READ;
8311a710239Sad uio.uio_offset = 0;
8321a710239Sad
8331a710239Sad while (!error && !eofflag) {
8341a710239Sad iov.iov_base = buf;
8351a710239Sad iov.iov_len = sizeof(buf);
8361a710239Sad uio.uio_iov = &iov;
8371a710239Sad uio.uio_iovcnt = 1;
8381a710239Sad uio.uio_resid = iov.iov_len;
8391a710239Sad
8401a710239Sad error = VOP_READDIR(lvp, &uio, cred, &eofflag, NULL, NULL);
8411a710239Sad if (error)
8421a710239Sad break;
8431a710239Sad
8441a710239Sad edp = (struct dirent*)&buf[sizeof(buf) - uio.uio_resid];
8451a710239Sad for (dp = (struct dirent*)buf; !error && dp < edp;
8461a710239Sad dp = (struct dirent*)((char *)dp + dp->d_reclen)) {
8471a710239Sad if (dp->d_type == DT_WHT ||
8481a710239Sad (dp->d_namlen == 1 && dp->d_name[0] == '.') ||
84935fb6474Scegger (dp->d_namlen == 2 && !memcmp(dp->d_name, "..", 2)))
8501a710239Sad continue;
8511a710239Sad
8521a710239Sad cn.cn_namelen = dp->d_namlen;
8531a710239Sad cn.cn_nameptr = dp->d_name;
8541a710239Sad cn.cn_nameiop = LOOKUP;
85514402d0fSdholland cn.cn_flags = (LOCKPARENT | LOCKLEAF | RDONLY | ISLASTCN);
8561a710239Sad cn.cn_cred = cred;
8571a710239Sad
8581a710239Sad /*
8591a710239Sad * check entry in lower.
8601a710239Sad * Sometimes, readdir function returns
8611a710239Sad * wrong entry.
8621a710239Sad */
8631a710239Sad lookuperr = VOP_LOOKUP(lvp, &tvp, &cn);
8641a710239Sad
8651a710239Sad if (!lookuperr)
8661a710239Sad vput(tvp);
8671a710239Sad else
8681a710239Sad continue; /* skip entry */
8691a710239Sad
8701a710239Sad /*
8711a710239Sad * check entry
8721a710239Sad * If it has no exist/whiteout entry in upper,
8731a710239Sad * directory is not empty.
8741a710239Sad */
87514402d0fSdholland cn.cn_flags = (LOCKPARENT | LOCKLEAF | RDONLY | ISLASTCN);
8761a710239Sad lookuperr = VOP_LOOKUP(uvp, &tvp, &cn);
8771a710239Sad
8781a710239Sad if (!lookuperr)
8791a710239Sad vput(tvp);
8801a710239Sad
8811a710239Sad /* ignore exist or whiteout entry */
8821a710239Sad if (!lookuperr ||
8831a710239Sad (lookuperr == ENOENT && (cn.cn_flags & ISWHITEOUT)))
8841a710239Sad continue;
8851a710239Sad
8861a710239Sad error = ENOTEMPTY;
8871a710239Sad }
8881a710239Sad }
8891a710239Sad
8901a710239Sad /* close vnode */
8911a710239Sad VOP_CLOSE(vp, FREAD, cred);
8921a710239Sad
8931a710239Sad return (error);
8941a710239Sad }
8951a710239Sad
8961a710239Sad #ifdef DIAGNOSTIC
8971a710239Sad
8981a710239Sad struct vnode *
unionfs_checkuppervp(struct vnode * vp,const char * fil,int lno)8991a710239Sad unionfs_checkuppervp(struct vnode *vp, const char *fil, int lno)
9001a710239Sad {
9011a710239Sad struct unionfs_node *unp;
9021a710239Sad
9031a710239Sad unp = VTOUNIONFS(vp);
9041a710239Sad
9051a710239Sad #ifdef notyet
9061a710239Sad if (vp->v_op != unionfs_vnodeop_p) {
9071a710239Sad printf("unionfs_checkuppervp: on non-unionfs-node.\n");
9081a710239Sad #ifdef KDB
9091a710239Sad kdb_enter(KDB_WHY_UNIONFS,
9101a710239Sad "unionfs_checkuppervp: on non-unionfs-node.\n");
9111a710239Sad #endif
9121a710239Sad panic("unionfs_checkuppervp");
9131a710239Sad };
9141a710239Sad #endif
9151a710239Sad return (unp->un_uppervp);
9161a710239Sad }
9171a710239Sad
9181a710239Sad struct vnode *
unionfs_checklowervp(struct vnode * vp,const char * fil,int lno)9191a710239Sad unionfs_checklowervp(struct vnode *vp, const char *fil, int lno)
9201a710239Sad {
9211a710239Sad struct unionfs_node *unp;
9221a710239Sad
9231a710239Sad unp = VTOUNIONFS(vp);
9241a710239Sad
9251a710239Sad #ifdef notyet
9261a710239Sad if (vp->v_op != unionfs_vnodeop_p) {
9271a710239Sad printf("unionfs_checklowervp: on non-unionfs-node.\n");
9281a710239Sad #ifdef KDB
9291a710239Sad kdb_enter(KDB_WHY_UNIONFS,
9301a710239Sad "unionfs_checklowervp: on non-unionfs-node.\n");
9311a710239Sad #endif
9321a710239Sad panic("unionfs_checklowervp");
9331a710239Sad };
9341a710239Sad #endif
9351a710239Sad return (unp->un_lowervp);
9361a710239Sad }
9371a710239Sad #endif
938