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
fusefs_lookup(void * v)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