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