xref: /openbsd/sys/miscfs/fuse/fuse_lookup.c (revision 51d8761d)
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