Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+274 -258
+1 -1
fs/fuse/Makefile
··· 5 5 obj-$(CONFIG_FUSE_FS) += fuse.o 6 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
+2 -257
fs/fuse/dir.c
··· 16 16 #include <linux/xattr.h> 17 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 19 static void fuse_advise_use_readdirplus(struct inode *dir) 36 20 { 37 21 struct fuse_inode *fi = get_fuse_inode(dir); ··· 64 80 * Set dentry and possibly attribute timeouts from the lookup/mk* 65 81 * replies 66 82 */ 67 - static void fuse_change_entry_timeout(struct dentry *entry, 68 - struct fuse_entry_out *o) 83 + void fuse_change_entry_timeout(struct dentry *entry, struct fuse_entry_out *o) 69 84 { 70 85 fuse_dentry_settime(entry, 71 86 time_to_jiffies(o->entry_valid, o->entry_valid_nsec)); ··· 75 92 return time_to_jiffies(o->attr_valid, o->attr_valid_nsec); 76 93 } 77 94 78 - static u64 entry_attr_timeout(struct fuse_entry_out *o) 95 + u64 entry_attr_timeout(struct fuse_entry_out *o) 79 96 { 80 97 return time_to_jiffies(o->attr_valid, o->attr_valid_nsec); 81 98 } ··· 243 260 invalid: 244 261 ret = 0; 245 262 goto out; 246 - } 247 - 248 - static int invalid_nodeid(u64 nodeid) 249 - { 250 - return !nodeid || nodeid == FUSE_ROOT_ID; 251 263 } 252 264 253 265 static int fuse_dentry_init(struct dentry *dentry) ··· 1140 1162 return -EACCES; 1141 1163 } 1142 1164 } 1143 - return err; 1144 - } 1145 - 1146 - static int parse_dirfile(char *buf, size_t nbytes, struct file *file, 1147 - struct dir_context *ctx) 1148 - { 1149 - while (nbytes >= FUSE_NAME_OFFSET) { 1150 - struct fuse_dirent *dirent = (struct fuse_dirent *) buf; 1151 - size_t reclen = FUSE_DIRENT_SIZE(dirent); 1152 - if (!dirent->namelen || dirent->namelen > FUSE_NAME_MAX) 1153 - return -EIO; 1154 - if (reclen > nbytes) 1155 - break; 1156 - if (memchr(dirent->name, '/', dirent->namelen) != NULL) 1157 - return -EIO; 1158 - 1159 - if (!dir_emit(ctx, dirent->name, dirent->namelen, 1160 - dirent->ino, dirent->type)) 1161 - break; 1162 - 1163 - buf += reclen; 1164 - nbytes -= reclen; 1165 - ctx->pos = dirent->off; 1166 - } 1167 - 1168 - return 0; 1169 - } 1170 - 1171 - static int fuse_direntplus_link(struct file *file, 1172 - struct fuse_direntplus *direntplus, 1173 - u64 attr_version) 1174 - { 1175 - struct fuse_entry_out *o = &direntplus->entry_out; 1176 - struct fuse_dirent *dirent = &direntplus->dirent; 1177 - struct dentry *parent = file->f_path.dentry; 1178 - struct qstr name = QSTR_INIT(dirent->name, dirent->namelen); 1179 - struct dentry *dentry; 1180 - struct dentry *alias; 1181 - struct inode *dir = d_inode(parent); 1182 - struct fuse_conn *fc; 1183 - struct inode *inode; 1184 - DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); 1185 - 1186 - if (!o->nodeid) { 1187 - /* 1188 - * Unlike in the case of fuse_lookup, zero nodeid does not mean 1189 - * ENOENT. Instead, it only means the userspace filesystem did 1190 - * not want to return attributes/handle for this entry. 1191 - * 1192 - * So do nothing. 1193 - */ 1194 - return 0; 1195 - } 1196 - 1197 - if (name.name[0] == '.') { 1198 - /* 1199 - * We could potentially refresh the attributes of the directory 1200 - * and its parent? 1201 - */ 1202 - if (name.len == 1) 1203 - return 0; 1204 - if (name.name[1] == '.' && name.len == 2) 1205 - return 0; 1206 - } 1207 - 1208 - if (invalid_nodeid(o->nodeid)) 1209 - return -EIO; 1210 - if (!fuse_valid_type(o->attr.mode)) 1211 - return -EIO; 1212 - 1213 - fc = get_fuse_conn(dir); 1214 - 1215 - name.hash = full_name_hash(parent, name.name, name.len); 1216 - dentry = d_lookup(parent, &name); 1217 - if (!dentry) { 1218 - retry: 1219 - dentry = d_alloc_parallel(parent, &name, &wq); 1220 - if (IS_ERR(dentry)) 1221 - return PTR_ERR(dentry); 1222 - } 1223 - if (!d_in_lookup(dentry)) { 1224 - struct fuse_inode *fi; 1225 - inode = d_inode(dentry); 1226 - if (!inode || 1227 - get_node_id(inode) != o->nodeid || 1228 - ((o->attr.mode ^ inode->i_mode) & S_IFMT)) { 1229 - d_invalidate(dentry); 1230 - dput(dentry); 1231 - goto retry; 1232 - } 1233 - if (is_bad_inode(inode)) { 1234 - dput(dentry); 1235 - return -EIO; 1236 - } 1237 - 1238 - fi = get_fuse_inode(inode); 1239 - spin_lock(&fc->lock); 1240 - fi->nlookup++; 1241 - spin_unlock(&fc->lock); 1242 - 1243 - forget_all_cached_acls(inode); 1244 - fuse_change_attributes(inode, &o->attr, 1245 - entry_attr_timeout(o), 1246 - attr_version); 1247 - /* 1248 - * The other branch comes via fuse_iget() 1249 - * which bumps nlookup inside 1250 - */ 1251 - } else { 1252 - inode = fuse_iget(dir->i_sb, o->nodeid, o->generation, 1253 - &o->attr, entry_attr_timeout(o), 1254 - attr_version); 1255 - if (!inode) 1256 - inode = ERR_PTR(-ENOMEM); 1257 - 1258 - alias = d_splice_alias(inode, dentry); 1259 - d_lookup_done(dentry); 1260 - if (alias) { 1261 - dput(dentry); 1262 - dentry = alias; 1263 - } 1264 - if (IS_ERR(dentry)) 1265 - return PTR_ERR(dentry); 1266 - } 1267 - if (fc->readdirplus_auto) 1268 - set_bit(FUSE_I_INIT_RDPLUS, &get_fuse_inode(inode)->state); 1269 - fuse_change_entry_timeout(dentry, o); 1270 - 1271 - dput(dentry); 1272 - return 0; 1273 - } 1274 - 1275 - static int parse_dirplusfile(char *buf, size_t nbytes, struct file *file, 1276 - struct dir_context *ctx, u64 attr_version) 1277 - { 1278 - struct fuse_direntplus *direntplus; 1279 - struct fuse_dirent *dirent; 1280 - size_t reclen; 1281 - int over = 0; 1282 - int ret; 1283 - 1284 - while (nbytes >= FUSE_NAME_OFFSET_DIRENTPLUS) { 1285 - direntplus = (struct fuse_direntplus *) buf; 1286 - dirent = &direntplus->dirent; 1287 - reclen = FUSE_DIRENTPLUS_SIZE(direntplus); 1288 - 1289 - if (!dirent->namelen || dirent->namelen > FUSE_NAME_MAX) 1290 - return -EIO; 1291 - if (reclen > nbytes) 1292 - break; 1293 - if (memchr(dirent->name, '/', dirent->namelen) != NULL) 1294 - return -EIO; 1295 - 1296 - if (!over) { 1297 - /* We fill entries into dstbuf only as much as 1298 - it can hold. But we still continue iterating 1299 - over remaining entries to link them. If not, 1300 - we need to send a FORGET for each of those 1301 - which we did not link. 1302 - */ 1303 - over = !dir_emit(ctx, dirent->name, dirent->namelen, 1304 - dirent->ino, dirent->type); 1305 - if (!over) 1306 - ctx->pos = dirent->off; 1307 - } 1308 - 1309 - buf += reclen; 1310 - nbytes -= reclen; 1311 - 1312 - ret = fuse_direntplus_link(file, direntplus, attr_version); 1313 - if (ret) 1314 - fuse_force_forget(file, direntplus->entry_out.nodeid); 1315 - } 1316 - 1317 - return 0; 1318 - } 1319 - 1320 - static int fuse_readdir(struct file *file, struct dir_context *ctx) 1321 - { 1322 - int plus, err; 1323 - size_t nbytes; 1324 - struct page *page; 1325 - struct inode *inode = file_inode(file); 1326 - struct fuse_conn *fc = get_fuse_conn(inode); 1327 - struct fuse_req *req; 1328 - u64 attr_version = 0; 1329 - bool locked; 1330 - 1331 - if (is_bad_inode(inode)) 1332 - return -EIO; 1333 - 1334 - req = fuse_get_req(fc, 1); 1335 - if (IS_ERR(req)) 1336 - return PTR_ERR(req); 1337 - 1338 - page = alloc_page(GFP_KERNEL); 1339 - if (!page) { 1340 - fuse_put_request(fc, req); 1341 - return -ENOMEM; 1342 - } 1343 - 1344 - plus = fuse_use_readdirplus(inode, ctx); 1345 - req->out.argpages = 1; 1346 - req->num_pages = 1; 1347 - req->pages[0] = page; 1348 - req->page_descs[0].length = PAGE_SIZE; 1349 - if (plus) { 1350 - attr_version = fuse_get_attr_version(fc); 1351 - fuse_read_fill(req, file, ctx->pos, PAGE_SIZE, 1352 - FUSE_READDIRPLUS); 1353 - } else { 1354 - fuse_read_fill(req, file, ctx->pos, PAGE_SIZE, 1355 - FUSE_READDIR); 1356 - } 1357 - locked = fuse_lock_inode(inode); 1358 - fuse_request_send(fc, req); 1359 - fuse_unlock_inode(inode, locked); 1360 - nbytes = req->out.args[0].size; 1361 - err = req->out.h.error; 1362 - fuse_put_request(fc, req); 1363 - if (!err) { 1364 - if (plus) { 1365 - err = parse_dirplusfile(page_address(page), nbytes, 1366 - file, ctx, 1367 - attr_version); 1368 - } else { 1369 - err = parse_dirfile(page_address(page), nbytes, file, 1370 - ctx); 1371 - } 1372 - } 1373 - 1374 - __free_page(page); 1375 - fuse_invalidate_atime(inode); 1376 1165 return err; 1377 1166 } 1378 1167
+12
fs/fuse/fuse_i.h
··· 704 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 712 /** Device operations */ 708 713 extern const struct file_operations fuse_dev_operations; 709 714 ··· 883 878 884 879 void fuse_invalidate_atime(struct inode *inode); 885 880 881 + u64 entry_attr_timeout(struct fuse_entry_out *o); 882 + void fuse_change_entry_timeout(struct dentry *entry, struct fuse_entry_out *o); 883 + 886 884 /** 887 885 * Acquire reference to fuse_conn 888 886 */ ··· 1004 996 struct posix_acl; 1005 997 struct posix_acl *fuse_get_acl(struct inode *inode, int type); 1006 998 int fuse_set_acl(struct inode *inode, struct posix_acl *acl, int type); 999 + 1000 + 1001 + /* readdir.c */ 1002 + int fuse_readdir(struct file *file, struct dir_context *ctx); 1007 1003 1008 1004 #endif /* _FS_FUSE_I_H */
+259
fs/fuse/readdir.c
··· 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 + }