1 /* $OpenBSD: fuse_lookup.c,v 1.16 2016/09/07 17:53:35 natano Exp $ */ 2 /* 3 * Copyright (c) 2012-2013 Sylvestre Gallon <ccna.syl@gmail.com> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/systm.h> 20 #include <sys/mount.h> 21 #include <sys/namei.h> 22 #include <sys/stat.h> 23 #include <sys/statvfs.h> 24 #include <sys/vnode.h> 25 #include <sys/lock.h> 26 #include <sys/fusebuf.h> 27 28 #include "fusefs_node.h" 29 #include "fusefs.h" 30 31 int fusefs_lookup(void *); 32 33 int 34 fusefs_lookup(void *v) 35 { 36 struct vop_lookup_args *ap = v; 37 struct vnode *vdp; /* vnode for directory being searched */ 38 struct fusefs_node *dp; /* inode for directory being searched */ 39 struct fusefs_mnt *fmp; /* file system that directory is in */ 40 int lockparent; /* 1 => lockparent flag is set */ 41 struct vnode *tdp; /* returned by VOP_VGET */ 42 struct fusebuf *fbuf; 43 struct vnode **vpp = ap->a_vpp; 44 struct componentname *cnp = ap->a_cnp; 45 struct proc *p = cnp->cn_proc; 46 struct ucred *cred = cnp->cn_cred; 47 uint64_t nid; 48 enum vtype nvtype; 49 int flags; 50 int nameiop = cnp->cn_nameiop; 51 int wantparent; 52 int error = 0; 53 54 flags = cnp->cn_flags; 55 *vpp = NULL; 56 vdp = ap->a_dvp; 57 dp = VTOI(vdp); 58 fmp = (struct fusefs_mnt *)dp->ufs_ino.i_ump; 59 lockparent = flags & LOCKPARENT; 60 wantparent = flags & (LOCKPARENT | WANTPARENT); 61 62 if ((error = VOP_ACCESS(vdp, VEXEC, cred, cnp->cn_proc)) != 0) 63 return (error); 64 65 if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) && 66 (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) 67 return (EROFS); 68 69 if (cnp->cn_namelen == 1 && *(cnp->cn_nameptr) == '.') { 70 nid = dp->ufs_ino.i_number; 71 } else { 72 if (!fmp->sess_init) 73 return (ENOENT); 74 75 /* got a real entry */ 76 fbuf = fb_setup(cnp->cn_namelen + 1, dp->ufs_ino.i_number, 77 FBT_LOOKUP, p); 78 79 memcpy(fbuf->fb_dat, cnp->cn_nameptr, cnp->cn_namelen); 80 fbuf->fb_dat[cnp->cn_namelen] = '\0'; 81 82 error = fb_queue(fmp->dev, fbuf); 83 84 if (error) { 85 fb_delete(fbuf); 86 87 /* tsleep return */ 88 if (error == EWOULDBLOCK) 89 return (error); 90 91 if ((nameiop == CREATE || nameiop == RENAME) && 92 (flags & ISLASTCN)) { 93 if (vdp->v_mount->mnt_flag & MNT_RDONLY) 94 return (EROFS); 95 96 cnp->cn_flags |= SAVENAME; 97 98 if (!lockparent) { 99 VOP_UNLOCK(vdp, p); 100 cnp->cn_flags |= PDIRUNLOCK; 101 } 102 103 return (EJUSTRETURN); 104 } 105 106 return (ENOENT); 107 } 108 109 nid = fbuf->fb_attr.st_ino; 110 nvtype = IFTOVT(fbuf->fb_attr.st_mode); 111 fb_delete(fbuf); 112 } 113 114 if (nameiop == DELETE && (flags & ISLASTCN)) { 115 /* 116 * Write access to directory required to delete files. 117 */ 118 error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc); 119 if (error) 120 goto reclaim; 121 122 cnp->cn_flags |= SAVENAME; 123 } 124 125 if (nameiop == RENAME && wantparent && (flags & ISLASTCN)) { 126 /* 127 * Write access to directory required to delete files. 128 */ 129 if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc)) != 0) 130 goto reclaim; 131 132 if (nid == dp->ufs_ino.i_number) 133 return (EISDIR); 134 135 error = VFS_VGET(fmp->mp, nid, &tdp); 136 if (error) 137 goto reclaim; 138 139 tdp->v_type = nvtype; 140 *vpp = tdp; 141 cnp->cn_flags |= SAVENAME; 142 143 return (0); 144 } 145 146 if (flags & ISDOTDOT) { 147 VOP_UNLOCK(vdp, p); /* race to get the inode */ 148 cnp->cn_flags |= PDIRUNLOCK; 149 150 error = VFS_VGET(fmp->mp, nid, &tdp); 151 152 if (error) { 153 if (vn_lock(vdp, LK_EXCLUSIVE | LK_RETRY, p) == 0) 154 cnp->cn_flags &= ~PDIRUNLOCK; 155 156 goto reclaim; 157 } 158 159 tdp->v_type = nvtype; 160 161 if (lockparent && (flags & ISLASTCN)) { 162 if ((error = vn_lock(vdp, LK_EXCLUSIVE, p))) { 163 vput(tdp); 164 return (error); 165 } 166 cnp->cn_flags &= ~PDIRUNLOCK; 167 } 168 *vpp = tdp; 169 170 } else if (nid == dp->ufs_ino.i_number) { 171 vref(vdp); 172 *vpp = vdp; 173 error = 0; 174 } else { 175 error = VFS_VGET(fmp->mp, nid, &tdp); 176 if (error) 177 goto reclaim; 178 179 tdp->v_type = nvtype; 180 181 if (!lockparent || !(flags & ISLASTCN)) { 182 VOP_UNLOCK(vdp, p); 183 cnp->cn_flags |= PDIRUNLOCK; 184 } 185 186 *vpp = tdp; 187 } 188 189 return (error); 190 191 reclaim: 192 if (nid != dp->ufs_ino.i_number && nid != FUSE_ROOTINO) { 193 fbuf = fb_setup(0, nid, FBT_RECLAIM, p); 194 if (fb_queue(fmp->dev, fbuf)) 195 printf("fusefs: libfuse vnode reclaim failed\n"); 196 fb_delete(fbuf); 197 } 198 return (error); 199 } 200