1 /* $OpenBSD: fuse_lookup.c,v 1.22 2024/10/31 13:55:21 claudio 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->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->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->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 /* file system is dead */ 88 if (error == ENXIO) 89 return (error); 90 91 if ((nameiop == CREATE || nameiop == RENAME) && 92 (flags & ISLASTCN)) { 93 /* 94 * Access for write is interpreted as allowing 95 * creation of files in the directory. 96 */ 97 if ((error = VOP_ACCESS(vdp, VWRITE, cred, 98 cnp->cn_proc)) != 0) 99 return (error); 100 101 cnp->cn_flags |= SAVENAME; 102 103 if (!lockparent) { 104 VOP_UNLOCK(vdp); 105 cnp->cn_flags |= PDIRUNLOCK; 106 } 107 108 return (EJUSTRETURN); 109 } 110 111 return (ENOENT); 112 } 113 114 nid = fbuf->fb_ino; 115 nvtype = IFTOVT(fbuf->fb_attr.st_mode); 116 fb_delete(fbuf); 117 } 118 119 if (nameiop == DELETE && (flags & ISLASTCN)) { 120 /* 121 * Write access to directory required to delete files. 122 */ 123 error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc); 124 if (error) 125 goto reclaim; 126 127 cnp->cn_flags |= SAVENAME; 128 } 129 130 if (nameiop == RENAME && wantparent && (flags & ISLASTCN)) { 131 /* 132 * Write access to directory required to delete files. 133 */ 134 if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc)) != 0) 135 goto reclaim; 136 137 if (nid == dp->i_number) 138 return (EISDIR); 139 140 error = VFS_VGET(fmp->mp, nid, &tdp); 141 if (error) 142 goto reclaim; 143 144 tdp->v_type = nvtype; 145 *vpp = tdp; 146 cnp->cn_flags |= SAVENAME; 147 148 return (0); 149 } 150 151 if (flags & ISDOTDOT) { 152 VOP_UNLOCK(vdp); /* race to get the inode */ 153 cnp->cn_flags |= PDIRUNLOCK; 154 155 error = VFS_VGET(fmp->mp, nid, &tdp); 156 157 if (error) { 158 if (vn_lock(vdp, LK_EXCLUSIVE | LK_RETRY) == 0) 159 cnp->cn_flags &= ~PDIRUNLOCK; 160 161 goto reclaim; 162 } 163 164 tdp->v_type = nvtype; 165 166 if (lockparent && (flags & ISLASTCN)) { 167 if ((error = vn_lock(vdp, LK_EXCLUSIVE))) { 168 vput(tdp); 169 return (error); 170 } 171 cnp->cn_flags &= ~PDIRUNLOCK; 172 } 173 *vpp = tdp; 174 175 } else if (nid == dp->i_number) { 176 vref(vdp); 177 *vpp = vdp; 178 error = 0; 179 } else { 180 error = VFS_VGET(fmp->mp, nid, &tdp); 181 if (error) 182 goto reclaim; 183 184 tdp->v_type = nvtype; 185 186 if (!lockparent || !(flags & ISLASTCN)) { 187 VOP_UNLOCK(vdp); 188 cnp->cn_flags |= PDIRUNLOCK; 189 } 190 191 *vpp = tdp; 192 } 193 194 return (error); 195 196 reclaim: 197 if (nid != dp->i_number && nid != FUSE_ROOTINO) { 198 fbuf = fb_setup(0, nid, FBT_RECLAIM, p); 199 if (fb_queue(fmp->dev, fbuf)) 200 printf("fusefs: libfuse vnode reclaim failed\n"); 201 fb_delete(fbuf); 202 } 203 return (error); 204 } 205