diff options
author | Miklos Szeredi <mszeredi@redhat.com> | 2018-09-28 10:43:23 -0400 |
---|---|---|
committer | Miklos Szeredi <mszeredi@redhat.com> | 2018-09-28 10:43:23 -0400 |
commit | d123d8e1833c5d854b56f2a7da17cafd0a901df8 (patch) | |
tree | b90ca45d76440931fd8853013246f8a7916d0ce8 /fs/fuse | |
parent | be2ff42c5d6ebc8552c82a7d1697afae30510ed9 (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/Makefile | 2 | ||||
-rw-r--r-- | fs/fuse/dir.c | 259 | ||||
-rw-r--r-- | fs/fuse/fuse_i.h | 12 | ||||
-rw-r--r-- | fs/fuse/readdir.c | 259 |
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 @@ | |||
5 | obj-$(CONFIG_FUSE_FS) += fuse.o | 5 | obj-$(CONFIG_FUSE_FS) += fuse.o |
6 | obj-$(CONFIG_CUSE) += cuse.o | 6 | obj-$(CONFIG_CUSE) += cuse.o |
7 | 7 | ||
8 | fuse-objs := dev.o dir.o file.o inode.o control.o xattr.o acl.o | 8 | fuse-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 | ||
19 | static 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 | |||
35 | static void fuse_advise_use_readdirplus(struct inode *dir) | 19 | static 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 | */ |
83 | static void fuse_change_entry_timeout(struct dentry *entry, | 67 | void 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 | ||
95 | static u64 entry_attr_timeout(struct fuse_entry_out *o) | 78 | u64 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 | ||
265 | static int invalid_nodeid(u64 nodeid) | ||
266 | { | ||
267 | return !nodeid || nodeid == FUSE_ROOT_ID; | ||
268 | } | ||
269 | |||
270 | static int fuse_dentry_init(struct dentry *dentry) | 248 | static 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 | ||
1168 | static 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 | |||
1193 | static 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) { | ||
1240 | retry: | ||
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 | |||
1297 | static 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 | |||
1342 | static 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 | |||
1401 | static const char *fuse_get_link(struct dentry *dentry, | 1146 | static 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 | ||
707 | static inline int invalid_nodeid(u64 nodeid) | ||
708 | { | ||
709 | return !nodeid || nodeid == FUSE_ROOT_ID; | ||
710 | } | ||
711 | |||
707 | /** Device operations */ | 712 | /** Device operations */ |
708 | extern const struct file_operations fuse_dev_operations; | 713 | extern const struct file_operations fuse_dev_operations; |
709 | 714 | ||
@@ -878,6 +883,9 @@ void fuse_invalidate_entry_cache(struct dentry *entry); | |||
878 | 883 | ||
879 | void fuse_invalidate_atime(struct inode *inode); | 884 | void fuse_invalidate_atime(struct inode *inode); |
880 | 885 | ||
886 | u64 entry_attr_timeout(struct fuse_entry_out *o); | ||
887 | void 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; | |||
997 | struct posix_acl *fuse_get_acl(struct inode *inode, int type); | 1005 | struct posix_acl *fuse_get_acl(struct inode *inode, int type); |
998 | int fuse_set_acl(struct inode *inode, struct posix_acl *acl, int type); | 1006 | int fuse_set_acl(struct inode *inode, struct posix_acl *acl, int type); |
999 | 1007 | ||
1008 | |||
1009 | /* readdir.c */ | ||
1010 | int 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 | |||
13 | static 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 | |||
29 | static 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 | |||
54 | static 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) { | ||
101 | retry: | ||
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 | |||
158 | static 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 | |||
203 | int 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 | } | ||