aboutsummaryrefslogtreecommitdiffstats
path: root/fs/fuse
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@redhat.com>2018-09-28 10:43:23 -0400
committerMiklos Szeredi <mszeredi@redhat.com>2018-09-28 10:43:23 -0400
commitd123d8e1833c5d854b56f2a7da17cafd0a901df8 (patch)
treeb90ca45d76440931fd8853013246f8a7916d0ce8 /fs/fuse
parentbe2ff42c5d6ebc8552c82a7d1697afae30510ed9 (diff)
fuse: split out readdir.c
Directory reading code is about to grow larger, so split it out from dir.c into a new source file. Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Diffstat (limited to 'fs/fuse')
-rw-r--r--fs/fuse/Makefile2
-rw-r--r--fs/fuse/dir.c259
-rw-r--r--fs/fuse/fuse_i.h12
-rw-r--r--fs/fuse/readdir.c259
4 files changed, 274 insertions, 258 deletions
diff --git a/fs/fuse/Makefile b/fs/fuse/Makefile
index 60da84a86dab..f7b807bc1027 100644
--- a/fs/fuse/Makefile
+++ b/fs/fuse/Makefile
@@ -5,4 +5,4 @@
5obj-$(CONFIG_FUSE_FS) += fuse.o 5obj-$(CONFIG_FUSE_FS) += fuse.o
6obj-$(CONFIG_CUSE) += cuse.o 6obj-$(CONFIG_CUSE) += cuse.o
7 7
8fuse-objs := dev.o dir.o file.o inode.o control.o xattr.o acl.o 8fuse-objs := dev.o dir.o file.o inode.o control.o xattr.o acl.o readdir.o
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 0979609d6eba..3a333b0ea9ad 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -16,22 +16,6 @@
16#include <linux/xattr.h> 16#include <linux/xattr.h>
17#include <linux/posix_acl.h> 17#include <linux/posix_acl.h>
18 18
19static bool fuse_use_readdirplus(struct inode *dir, struct dir_context *ctx)
20{
21 struct fuse_conn *fc = get_fuse_conn(dir);
22 struct fuse_inode *fi = get_fuse_inode(dir);
23
24 if (!fc->do_readdirplus)
25 return false;
26 if (!fc->readdirplus_auto)
27 return true;
28 if (test_and_clear_bit(FUSE_I_ADVISE_RDPLUS, &fi->state))
29 return true;
30 if (ctx->pos == 0)
31 return true;
32 return false;
33}
34
35static void fuse_advise_use_readdirplus(struct inode *dir) 19static void fuse_advise_use_readdirplus(struct inode *dir)
36{ 20{
37 struct fuse_inode *fi = get_fuse_inode(dir); 21 struct fuse_inode *fi = get_fuse_inode(dir);
@@ -80,8 +64,7 @@ static u64 time_to_jiffies(u64 sec, u32 nsec)
80 * Set dentry and possibly attribute timeouts from the lookup/mk* 64 * Set dentry and possibly attribute timeouts from the lookup/mk*
81 * replies 65 * replies
82 */ 66 */
83static void fuse_change_entry_timeout(struct dentry *entry, 67void fuse_change_entry_timeout(struct dentry *entry, struct fuse_entry_out *o)
84 struct fuse_entry_out *o)
85{ 68{
86 fuse_dentry_settime(entry, 69 fuse_dentry_settime(entry,
87 time_to_jiffies(o->entry_valid, o->entry_valid_nsec)); 70 time_to_jiffies(o->entry_valid, o->entry_valid_nsec));
@@ -92,7 +75,7 @@ static u64 attr_timeout(struct fuse_attr_out *o)
92 return time_to_jiffies(o->attr_valid, o->attr_valid_nsec); 75 return time_to_jiffies(o->attr_valid, o->attr_valid_nsec);
93} 76}
94 77
95static u64 entry_attr_timeout(struct fuse_entry_out *o) 78u64 entry_attr_timeout(struct fuse_entry_out *o)
96{ 79{
97 return time_to_jiffies(o->attr_valid, o->attr_valid_nsec); 80 return time_to_jiffies(o->attr_valid, o->attr_valid_nsec);
98} 81}
@@ -262,11 +245,6 @@ invalid:
262 goto out; 245 goto out;
263} 246}
264 247
265static int invalid_nodeid(u64 nodeid)
266{
267 return !nodeid || nodeid == FUSE_ROOT_ID;
268}
269
270static int fuse_dentry_init(struct dentry *dentry) 248static int fuse_dentry_init(struct dentry *dentry)
271{ 249{
272 dentry->d_fsdata = kzalloc(sizeof(union fuse_dentry), GFP_KERNEL); 250 dentry->d_fsdata = kzalloc(sizeof(union fuse_dentry), GFP_KERNEL);
@@ -1165,239 +1143,6 @@ static int fuse_permission(struct inode *inode, int mask)
1165 return err; 1143 return err;
1166} 1144}
1167 1145
1168static int parse_dirfile(char *buf, size_t nbytes, struct file *file,
1169 struct dir_context *ctx)
1170{
1171 while (nbytes >= FUSE_NAME_OFFSET) {
1172 struct fuse_dirent *dirent = (struct fuse_dirent *) buf;
1173 size_t reclen = FUSE_DIRENT_SIZE(dirent);
1174 if (!dirent->namelen || dirent->namelen > FUSE_NAME_MAX)
1175 return -EIO;
1176 if (reclen > nbytes)
1177 break;
1178 if (memchr(dirent->name, '/', dirent->namelen) != NULL)
1179 return -EIO;
1180
1181 if (!dir_emit(ctx, dirent->name, dirent->namelen,
1182 dirent->ino, dirent->type))
1183 break;
1184
1185 buf += reclen;
1186 nbytes -= reclen;
1187 ctx->pos = dirent->off;
1188 }
1189
1190 return 0;
1191}
1192
1193static int fuse_direntplus_link(struct file *file,
1194 struct fuse_direntplus *direntplus,
1195 u64 attr_version)
1196{
1197 struct fuse_entry_out *o = &direntplus->entry_out;
1198 struct fuse_dirent *dirent = &direntplus->dirent;
1199 struct dentry *parent = file->f_path.dentry;
1200 struct qstr name = QSTR_INIT(dirent->name, dirent->namelen);
1201 struct dentry *dentry;
1202 struct dentry *alias;
1203 struct inode *dir = d_inode(parent);
1204 struct fuse_conn *fc;
1205 struct inode *inode;
1206 DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
1207
1208 if (!o->nodeid) {
1209 /*
1210 * Unlike in the case of fuse_lookup, zero nodeid does not mean
1211 * ENOENT. Instead, it only means the userspace filesystem did
1212 * not want to return attributes/handle for this entry.
1213 *
1214 * So do nothing.
1215 */
1216 return 0;
1217 }
1218
1219 if (name.name[0] == '.') {
1220 /*
1221 * We could potentially refresh the attributes of the directory
1222 * and its parent?
1223 */
1224 if (name.len == 1)
1225 return 0;
1226 if (name.name[1] == '.' && name.len == 2)
1227 return 0;
1228 }
1229
1230 if (invalid_nodeid(o->nodeid))
1231 return -EIO;
1232 if (!fuse_valid_type(o->attr.mode))
1233 return -EIO;
1234
1235 fc = get_fuse_conn(dir);
1236
1237 name.hash = full_name_hash(parent, name.name, name.len);
1238 dentry = d_lookup(parent, &name);
1239 if (!dentry) {
1240retry:
1241 dentry = d_alloc_parallel(parent, &name, &wq);
1242 if (IS_ERR(dentry))
1243 return PTR_ERR(dentry);
1244 }
1245 if (!d_in_lookup(dentry)) {
1246 struct fuse_inode *fi;
1247 inode = d_inode(dentry);
1248 if (!inode ||
1249 get_node_id(inode) != o->nodeid ||
1250 ((o->attr.mode ^ inode->i_mode) & S_IFMT)) {
1251 d_invalidate(dentry);
1252 dput(dentry);
1253 goto retry;
1254 }
1255 if (is_bad_inode(inode)) {
1256 dput(dentry);
1257 return -EIO;
1258 }
1259
1260 fi = get_fuse_inode(inode);
1261 spin_lock(&fc->lock);
1262 fi->nlookup++;
1263 spin_unlock(&fc->lock);
1264
1265 forget_all_cached_acls(inode);
1266 fuse_change_attributes(inode, &o->attr,
1267 entry_attr_timeout(o),
1268 attr_version);
1269 /*
1270 * The other branch comes via fuse_iget()
1271 * which bumps nlookup inside
1272 */
1273 } else {
1274 inode = fuse_iget(dir->i_sb, o->nodeid, o->generation,
1275 &o->attr, entry_attr_timeout(o),
1276 attr_version);
1277 if (!inode)
1278 inode = ERR_PTR(-ENOMEM);
1279
1280 alias = d_splice_alias(inode, dentry);
1281 d_lookup_done(dentry);
1282 if (alias) {
1283 dput(dentry);
1284 dentry = alias;
1285 }
1286 if (IS_ERR(dentry))
1287 return PTR_ERR(dentry);
1288 }
1289 if (fc->readdirplus_auto)
1290 set_bit(FUSE_I_INIT_RDPLUS, &get_fuse_inode(inode)->state);
1291 fuse_change_entry_timeout(dentry, o);
1292
1293 dput(dentry);
1294 return 0;
1295}
1296
1297static int parse_dirplusfile(char *buf, size_t nbytes, struct file *file,
1298 struct dir_context *ctx, u64 attr_version)
1299{
1300 struct fuse_direntplus *direntplus;
1301 struct fuse_dirent *dirent;
1302 size_t reclen;
1303 int over = 0;
1304 int ret;
1305
1306 while (nbytes >= FUSE_NAME_OFFSET_DIRENTPLUS) {
1307 direntplus = (struct fuse_direntplus *) buf;
1308 dirent = &direntplus->dirent;
1309 reclen = FUSE_DIRENTPLUS_SIZE(direntplus);
1310
1311 if (!dirent->namelen || dirent->namelen > FUSE_NAME_MAX)
1312 return -EIO;
1313 if (reclen > nbytes)
1314 break;
1315 if (memchr(dirent->name, '/', dirent->namelen) != NULL)
1316 return -EIO;
1317
1318 if (!over) {
1319 /* We fill entries into dstbuf only as much as
1320 it can hold. But we still continue iterating
1321 over remaining entries to link them. If not,
1322 we need to send a FORGET for each of those
1323 which we did not link.
1324 */
1325 over = !dir_emit(ctx, dirent->name, dirent->namelen,
1326 dirent->ino, dirent->type);
1327 if (!over)
1328 ctx->pos = dirent->off;
1329 }
1330
1331 buf += reclen;
1332 nbytes -= reclen;
1333
1334 ret = fuse_direntplus_link(file, direntplus, attr_version);
1335 if (ret)
1336 fuse_force_forget(file, direntplus->entry_out.nodeid);
1337 }
1338
1339 return 0;
1340}
1341
1342static int fuse_readdir(struct file *file, struct dir_context *ctx)
1343{
1344 int plus, err;
1345 size_t nbytes;
1346 struct page *page;
1347 struct inode *inode = file_inode(file);
1348 struct fuse_conn *fc = get_fuse_conn(inode);
1349 struct fuse_req *req;
1350 u64 attr_version = 0;
1351 bool locked;
1352
1353 if (is_bad_inode(inode))
1354 return -EIO;
1355
1356 req = fuse_get_req(fc, 1);
1357 if (IS_ERR(req))
1358 return PTR_ERR(req);
1359
1360 page = alloc_page(GFP_KERNEL);
1361 if (!page) {
1362 fuse_put_request(fc, req);
1363 return -ENOMEM;
1364 }
1365
1366 plus = fuse_use_readdirplus(inode, ctx);
1367 req->out.argpages = 1;
1368 req->num_pages = 1;
1369 req->pages[0] = page;
1370 req->page_descs[0].length = PAGE_SIZE;
1371 if (plus) {
1372 attr_version = fuse_get_attr_version(fc);
1373 fuse_read_fill(req, file, ctx->pos, PAGE_SIZE,
1374 FUSE_READDIRPLUS);
1375 } else {
1376 fuse_read_fill(req, file, ctx->pos, PAGE_SIZE,
1377 FUSE_READDIR);
1378 }
1379 locked = fuse_lock_inode(inode);
1380 fuse_request_send(fc, req);
1381 fuse_unlock_inode(inode, locked);
1382 nbytes = req->out.args[0].size;
1383 err = req->out.h.error;
1384 fuse_put_request(fc, req);
1385 if (!err) {
1386 if (plus) {
1387 err = parse_dirplusfile(page_address(page), nbytes,
1388 file, ctx,
1389 attr_version);
1390 } else {
1391 err = parse_dirfile(page_address(page), nbytes, file,
1392 ctx);
1393 }
1394 }
1395
1396 __free_page(page);
1397 fuse_invalidate_atime(inode);
1398 return err;
1399}
1400
1401static const char *fuse_get_link(struct dentry *dentry, 1146static const char *fuse_get_link(struct dentry *dentry,
1402 struct inode *inode, 1147 struct inode *inode,
1403 struct delayed_call *done) 1148 struct delayed_call *done)
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 2c4272076f62..dfe10c2df6a9 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -704,6 +704,11 @@ static inline u64 get_node_id(struct inode *inode)
704 return get_fuse_inode(inode)->nodeid; 704 return get_fuse_inode(inode)->nodeid;
705} 705}
706 706
707static inline int invalid_nodeid(u64 nodeid)
708{
709 return !nodeid || nodeid == FUSE_ROOT_ID;
710}
711
707/** Device operations */ 712/** Device operations */
708extern const struct file_operations fuse_dev_operations; 713extern const struct file_operations fuse_dev_operations;
709 714
@@ -878,6 +883,9 @@ void fuse_invalidate_entry_cache(struct dentry *entry);
878 883
879void fuse_invalidate_atime(struct inode *inode); 884void fuse_invalidate_atime(struct inode *inode);
880 885
886u64 entry_attr_timeout(struct fuse_entry_out *o);
887void fuse_change_entry_timeout(struct dentry *entry, struct fuse_entry_out *o);
888
881/** 889/**
882 * Acquire reference to fuse_conn 890 * Acquire reference to fuse_conn
883 */ 891 */
@@ -997,4 +1005,8 @@ struct posix_acl;
997struct posix_acl *fuse_get_acl(struct inode *inode, int type); 1005struct posix_acl *fuse_get_acl(struct inode *inode, int type);
998int fuse_set_acl(struct inode *inode, struct posix_acl *acl, int type); 1006int fuse_set_acl(struct inode *inode, struct posix_acl *acl, int type);
999 1007
1008
1009/* readdir.c */
1010int fuse_readdir(struct file *file, struct dir_context *ctx);
1011
1000#endif /* _FS_FUSE_I_H */ 1012#endif /* _FS_FUSE_I_H */
diff --git a/fs/fuse/readdir.c b/fs/fuse/readdir.c
new file mode 100644
index 000000000000..3e100e00e21e
--- /dev/null
+++ b/fs/fuse/readdir.c
@@ -0,0 +1,259 @@
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2018 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU GPL.
6 See the file COPYING.
7*/
8
9
10#include "fuse_i.h"
11#include <linux/posix_acl.h>
12
13static bool fuse_use_readdirplus(struct inode *dir, struct dir_context *ctx)
14{
15 struct fuse_conn *fc = get_fuse_conn(dir);
16 struct fuse_inode *fi = get_fuse_inode(dir);
17
18 if (!fc->do_readdirplus)
19 return false;
20 if (!fc->readdirplus_auto)
21 return true;
22 if (test_and_clear_bit(FUSE_I_ADVISE_RDPLUS, &fi->state))
23 return true;
24 if (ctx->pos == 0)
25 return true;
26 return false;
27}
28
29static int parse_dirfile(char *buf, size_t nbytes, struct file *file,
30 struct dir_context *ctx)
31{
32 while (nbytes >= FUSE_NAME_OFFSET) {
33 struct fuse_dirent *dirent = (struct fuse_dirent *) buf;
34 size_t reclen = FUSE_DIRENT_SIZE(dirent);
35 if (!dirent->namelen || dirent->namelen > FUSE_NAME_MAX)
36 return -EIO;
37 if (reclen > nbytes)
38 break;
39 if (memchr(dirent->name, '/', dirent->namelen) != NULL)
40 return -EIO;
41
42 if (!dir_emit(ctx, dirent->name, dirent->namelen,
43 dirent->ino, dirent->type))
44 break;
45
46 buf += reclen;
47 nbytes -= reclen;
48 ctx->pos = dirent->off;
49 }
50
51 return 0;
52}
53
54static int fuse_direntplus_link(struct file *file,
55 struct fuse_direntplus *direntplus,
56 u64 attr_version)
57{
58 struct fuse_entry_out *o = &direntplus->entry_out;
59 struct fuse_dirent *dirent = &direntplus->dirent;
60 struct dentry *parent = file->f_path.dentry;
61 struct qstr name = QSTR_INIT(dirent->name, dirent->namelen);
62 struct dentry *dentry;
63 struct dentry *alias;
64 struct inode *dir = d_inode(parent);
65 struct fuse_conn *fc;
66 struct inode *inode;
67 DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
68
69 if (!o->nodeid) {
70 /*
71 * Unlike in the case of fuse_lookup, zero nodeid does not mean
72 * ENOENT. Instead, it only means the userspace filesystem did
73 * not want to return attributes/handle for this entry.
74 *
75 * So do nothing.
76 */
77 return 0;
78 }
79
80 if (name.name[0] == '.') {
81 /*
82 * We could potentially refresh the attributes of the directory
83 * and its parent?
84 */
85 if (name.len == 1)
86 return 0;
87 if (name.name[1] == '.' && name.len == 2)
88 return 0;
89 }
90
91 if (invalid_nodeid(o->nodeid))
92 return -EIO;
93 if (!fuse_valid_type(o->attr.mode))
94 return -EIO;
95
96 fc = get_fuse_conn(dir);
97
98 name.hash = full_name_hash(parent, name.name, name.len);
99 dentry = d_lookup(parent, &name);
100 if (!dentry) {
101retry:
102 dentry = d_alloc_parallel(parent, &name, &wq);
103 if (IS_ERR(dentry))
104 return PTR_ERR(dentry);
105 }
106 if (!d_in_lookup(dentry)) {
107 struct fuse_inode *fi;
108 inode = d_inode(dentry);
109 if (!inode ||
110 get_node_id(inode) != o->nodeid ||
111 ((o->attr.mode ^ inode->i_mode) & S_IFMT)) {
112 d_invalidate(dentry);
113 dput(dentry);
114 goto retry;
115 }
116 if (is_bad_inode(inode)) {
117 dput(dentry);
118 return -EIO;
119 }
120
121 fi = get_fuse_inode(inode);
122 spin_lock(&fc->lock);
123 fi->nlookup++;
124 spin_unlock(&fc->lock);
125
126 forget_all_cached_acls(inode);
127 fuse_change_attributes(inode, &o->attr,
128 entry_attr_timeout(o),
129 attr_version);
130 /*
131 * The other branch comes via fuse_iget()
132 * which bumps nlookup inside
133 */
134 } else {
135 inode = fuse_iget(dir->i_sb, o->nodeid, o->generation,
136 &o->attr, entry_attr_timeout(o),
137 attr_version);
138 if (!inode)
139 inode = ERR_PTR(-ENOMEM);
140
141 alias = d_splice_alias(inode, dentry);
142 d_lookup_done(dentry);
143 if (alias) {
144 dput(dentry);
145 dentry = alias;
146 }
147 if (IS_ERR(dentry))
148 return PTR_ERR(dentry);
149 }
150 if (fc->readdirplus_auto)
151 set_bit(FUSE_I_INIT_RDPLUS, &get_fuse_inode(inode)->state);
152 fuse_change_entry_timeout(dentry, o);
153
154 dput(dentry);
155 return 0;
156}
157
158static int parse_dirplusfile(char *buf, size_t nbytes, struct file *file,
159 struct dir_context *ctx, u64 attr_version)
160{
161 struct fuse_direntplus *direntplus;
162 struct fuse_dirent *dirent;
163 size_t reclen;
164 int over = 0;
165 int ret;
166
167 while (nbytes >= FUSE_NAME_OFFSET_DIRENTPLUS) {
168 direntplus = (struct fuse_direntplus *) buf;
169 dirent = &direntplus->dirent;
170 reclen = FUSE_DIRENTPLUS_SIZE(direntplus);
171
172 if (!dirent->namelen || dirent->namelen > FUSE_NAME_MAX)
173 return -EIO;
174 if (reclen > nbytes)
175 break;
176 if (memchr(dirent->name, '/', dirent->namelen) != NULL)
177 return -EIO;
178
179 if (!over) {
180 /* We fill entries into dstbuf only as much as
181 it can hold. But we still continue iterating
182 over remaining entries to link them. If not,
183 we need to send a FORGET for each of those
184 which we did not link.
185 */
186 over = !dir_emit(ctx, dirent->name, dirent->namelen,
187 dirent->ino, dirent->type);
188 if (!over)
189 ctx->pos = dirent->off;
190 }
191
192 buf += reclen;
193 nbytes -= reclen;
194
195 ret = fuse_direntplus_link(file, direntplus, attr_version);
196 if (ret)
197 fuse_force_forget(file, direntplus->entry_out.nodeid);
198 }
199
200 return 0;
201}
202
203int fuse_readdir(struct file *file, struct dir_context *ctx)
204{
205 int plus, err;
206 size_t nbytes;
207 struct page *page;
208 struct inode *inode = file_inode(file);
209 struct fuse_conn *fc = get_fuse_conn(inode);
210 struct fuse_req *req;
211 u64 attr_version = 0;
212 bool locked;
213
214 if (is_bad_inode(inode))
215 return -EIO;
216
217 req = fuse_get_req(fc, 1);
218 if (IS_ERR(req))
219 return PTR_ERR(req);
220
221 page = alloc_page(GFP_KERNEL);
222 if (!page) {
223 fuse_put_request(fc, req);
224 return -ENOMEM;
225 }
226
227 plus = fuse_use_readdirplus(inode, ctx);
228 req->out.argpages = 1;
229 req->num_pages = 1;
230 req->pages[0] = page;
231 req->page_descs[0].length = PAGE_SIZE;
232 if (plus) {
233 attr_version = fuse_get_attr_version(fc);
234 fuse_read_fill(req, file, ctx->pos, PAGE_SIZE,
235 FUSE_READDIRPLUS);
236 } else {
237 fuse_read_fill(req, file, ctx->pos, PAGE_SIZE,
238 FUSE_READDIR);
239 }
240 locked = fuse_lock_inode(inode);
241 fuse_request_send(fc, req);
242 fuse_unlock_inode(inode, locked);
243 nbytes = req->out.args[0].size;
244 err = req->out.h.error;
245 fuse_put_request(fc, req);
246 if (!err) {
247 if (plus) {
248 err = parse_dirplusfile(page_address(page), nbytes,
249 file, ctx, attr_version);
250 } else {
251 err = parse_dirfile(page_address(page), nbytes, file,
252 ctx);
253 }
254 }
255
256 __free_page(page);
257 fuse_invalidate_atime(inode);
258 return err;
259}