xref: /openbsd/sys/miscfs/fuse/fuse_lookup.c (revision 9b7c3dbb)
1 /* $OpenBSD: fuse_lookup.c,v 1.15 2016/08/30 16:45:54 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 = NULL;
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 	int flags;
48 	int nameiop = cnp->cn_nameiop;
49 	int wantparent;
50 	int error = 0;
51 	uint64_t nid;
52 
53 	flags = cnp->cn_flags;
54 	*vpp = NULL;
55 	vdp = ap->a_dvp;
56 	dp = VTOI(vdp);
57 	fmp = (struct fusefs_mnt *)dp->ufs_ino.i_ump;
58 	lockparent = flags & LOCKPARENT;
59 	wantparent = flags & (LOCKPARENT | WANTPARENT);
60 
61 	if ((error = VOP_ACCESS(vdp, VEXEC, cred, cnp->cn_proc)) != 0)
62 		return (error);
63 
64 	if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) &&
65 	    (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
66 		return (EROFS);
67 
68 	if (flags & ISDOTDOT) {
69 		/* got ".." */
70 		nid = dp->parent;
71 		if (nid == 0)
72 			return (ENOENT);
73 	} else if (cnp->cn_namelen == 1 && *(cnp->cn_nameptr) == '.') {
74 		nid = dp->ufs_ino.i_number;
75 	} else {
76 		if (!fmp->sess_init)
77 			return (ENOENT);
78 
79 		/* got a real entry */
80 		fbuf = fb_setup(cnp->cn_namelen + 1, dp->ufs_ino.i_number,
81 		    FBT_LOOKUP, p);
82 
83 		memcpy(fbuf->fb_dat, cnp->cn_nameptr, cnp->cn_namelen);
84 		fbuf->fb_dat[cnp->cn_namelen] = '\0';
85 
86 		error = fb_queue(fmp->dev, fbuf);
87 
88 		/* tsleep return */
89 		if (error == EWOULDBLOCK)
90 			goto out;
91 
92 		if (error) {
93 			if ((nameiop == CREATE || nameiop == RENAME) &&
94 			    (flags & ISLASTCN)) {
95 				if (vdp->v_mount->mnt_flag & MNT_RDONLY)
96 					return (EROFS);
97 
98 				cnp->cn_flags |= SAVENAME;
99 
100 				if (!lockparent) {
101 					VOP_UNLOCK(vdp, p);
102 					cnp->cn_flags |= PDIRUNLOCK;
103 				}
104 
105 				error = EJUSTRETURN;
106 				goto out;
107 			}
108 
109 			error = ENOENT;
110 			goto out;
111 		}
112 
113 		nid = fbuf->fb_attr.st_ino;
114 	}
115 
116 	if (nameiop == DELETE && (flags & ISLASTCN)) {
117 		/*
118 		 * Write access to directory required to delete files.
119 		 */
120 		error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc);
121 		if (error)
122 			goto out;
123 
124 		cnp->cn_flags |= SAVENAME;
125 	}
126 
127 	if (nameiop == RENAME && wantparent && (flags & ISLASTCN)) {
128 		/*
129 		 * Write access to directory required to delete files.
130 		 */
131 		if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc)) != 0)
132 			goto out;
133 
134 		if (nid == VTOI(vdp)->ufs_ino.i_number) {
135 			error = EISDIR;
136 			goto out;
137 		}
138 
139 		error = VFS_VGET(fmp->mp, nid, &tdp);
140 		if (error)
141 			goto out;
142 
143 		tdp->v_type = IFTOVT(fbuf->fb_attr.st_mode);
144 		*vpp = tdp;
145 		cnp->cn_flags |= SAVENAME;
146 
147 		goto out;
148 	}
149 
150 	if (flags & ISDOTDOT) {
151 		VOP_UNLOCK(vdp, p);	/* race to get the inode */
152 		cnp->cn_flags |= PDIRUNLOCK;
153 
154 		error = VFS_VGET(fmp->mp, nid, &tdp);
155 
156 		if (error) {
157 			if (vn_lock(vdp, LK_EXCLUSIVE | LK_RETRY, p) == 0)
158 				cnp->cn_flags &= ~PDIRUNLOCK;
159 
160 			return (error);
161 		}
162 
163 		if (lockparent && (flags & ISLASTCN)) {
164 			if ((error = vn_lock(vdp, LK_EXCLUSIVE, p))) {
165 				vput(tdp);
166 				return (error);
167 			}
168 			cnp->cn_flags &= ~PDIRUNLOCK;
169 		}
170 		*vpp = tdp;
171 
172 	} else if (nid == dp->ufs_ino.i_number) {
173 		vref(vdp);
174 		*vpp = vdp;
175 		error = 0;
176 	} else {
177 		error = VFS_VGET(fmp->mp, nid, &tdp);
178 		if (error)
179 			goto out;
180 
181 		tdp->v_type = IFTOVT(fbuf->fb_attr.st_mode);
182 
183 		if (vdp != NULL && vdp->v_type == VDIR)
184 			VTOI(tdp)->parent = dp->ufs_ino.i_number;
185 
186 		if (!lockparent || !(flags & ISLASTCN)) {
187 			VOP_UNLOCK(vdp, p);
188 			cnp->cn_flags |= PDIRUNLOCK;
189 		}
190 
191 		*vpp = tdp;
192 	}
193 
194 out:
195 	fb_delete(fbuf);
196 	return (error);
197 }
198