Use column 3 to store inode information instead of saving it in directory. This fixes...
[pohmelfs.git] / fs / pohmelfs / dir.c
blob1598f87abfe28ca096f9b9b87575f9ef9b61358f
1 /*
2 * Copyright (C) 2011+ Evgeniy Polyakov <zbr@ioremap.net>
3 */
5 #include <linux/fs.h>
6 #include <linux/dcache.h>
7 #include <linux/quotaops.h>
9 #include "pohmelfs.h"
11 #define POHMELFS_LOOKUP_SCRIPT "pohmelfs_lookup.py"
12 #define POHMELFS_UNLINK_SCRIPT "pohmelfs_unlink.py"
13 #define POHMELFS_DATA_UNLINK_SCRIPT "pohmelfs_data_unlink.py"
14 #define POHMELFS_HARDLINK_SCRIPT "pohmelfs_hardlink.py"
15 #define POHMELFS_RENAME_SCRIPT "pohmelfs_rename.py"
16 #define POHMELFS_INODE_INFO_SCRIPT_INSERT "pohmelfs_inode_info_insert.py"
17 #define POHMELFS_READDIR_SCRIPT "pohmelfs_readdir.py"
18 #define POHMELFS_DENTRY_NAME_SCRIPT "pohmelfs_dentry_name="
20 static void pohmelfs_inode_dirty(struct pohmelfs_inode *parent, struct pohmelfs_inode *pi)
22 struct inode *inode = &pi->vfs_inode;
23 struct inode *dir = &parent->vfs_inode;
25 pi->parent_id = parent->id;
26 inode_init_owner(inode, dir, inode->i_mode);
28 inode->i_mtime = inode->i_ctime = CURRENT_TIME;
29 dir->i_mtime = CURRENT_TIME;
31 mark_inode_dirty(inode);
32 mark_inode_dirty(dir);
35 static int pohmelfs_send_dentry_complete(struct pohmelfs_trans *t, struct pohmelfs_state *recv)
37 struct pohmelfs_inode *pi = pohmelfs_inode(t->inode);
38 struct pohmelfs_wait *wait = t->priv;
39 struct dnet_cmd *cmd = &recv->cmd;
40 unsigned long long trans = cmd->trans & ~DNET_TRANS_REPLY;
42 if (cmd->flags & DNET_FLAGS_MORE) {
43 if (cmd->status == 0 && cmd->size != sizeof(struct dnet_attr) + 2)
44 cmd->status = -EINVAL;
46 pr_debug("pohmelfs: %s: pohmelfs_send_dentry_complete: %llu, cmd_size: %llu, flags: %x, status: %d\n",
47 pohmelfs_dump_id(pi->id.id), trans, cmd->size, cmd->flags, cmd->status);
49 if (!cmd->status)
50 wait->condition = 1;
51 else
52 wait->condition = cmd->status;
53 wake_up(&wait->wq);
56 return 0;
59 static int pohmelfs_send_inode_info_init(struct pohmelfs_trans *t)
61 struct pohmelfs_wait *wait = t->priv;
63 pohmelfs_wait_get(wait);
64 return 0;
67 static void pohmelfs_send_inode_info_destroy(struct pohmelfs_trans *t)
69 struct pohmelfs_wait *wait = t->priv;
71 wake_up(&wait->wq);
72 pohmelfs_wait_put(wait);
75 static int pohmelfs_lookup_complete(struct pohmelfs_trans *t, struct pohmelfs_state *recv)
77 struct pohmelfs_inode *parent = pohmelfs_inode(t->inode);
78 struct pohmelfs_wait *wait = t->priv;
79 struct dnet_cmd *cmd = &recv->cmd;
80 unsigned long long trans = cmd->trans & ~DNET_TRANS_REPLY;
81 int err = cmd->status;
83 if (err)
84 goto err_out_exit;
86 if (cmd->flags & DNET_FLAGS_MORE) {
87 struct pohmelfs_sb *psb = pohmelfs_sb(t->inode->i_sb);
88 struct pohmelfs_inode_info *info;
89 struct pohmelfs_inode *pi;
91 if (cmd->size != sizeof(struct dnet_attr) + sizeof(struct pohmelfs_inode_info)) {
92 err = -ENOENT;
93 goto err_out_exit;
96 pr_debug("pohmelfs: %s: pohmelfs_lookup_complete: %llu, size: %llu, min size: %zu, flags: %x, status: %d\n",
97 pohmelfs_dump_id(parent->id.id), trans, cmd->size,
98 sizeof(struct dnet_attr) + sizeof(struct pohmelfs_inode_info), cmd->flags, cmd->status);
101 info = t->recv_data + sizeof(struct dnet_attr);
102 pohmelfs_convert_inode_info(info);
104 pi = pohmelfs_existing_inode(psb, info);
105 if (IS_ERR(pi)) {
106 err = PTR_ERR(pi);
108 if (err != -EEXIST)
109 goto err_out_exit;
111 err = 0;
112 pi = pohmelfs_sb_inode_lookup(psb, &info->id);
113 if (!pi) {
114 err = -ENOENT;
115 goto err_out_exit;
118 pohmelfs_fill_inode(&pi->vfs_inode, info);
121 pi->parent_id = parent->id;
122 wait->ret = pi;
125 err_out_exit:
126 if (err)
127 wait->condition = err;
128 else
129 wait->condition = 1;
130 wake_up(&wait->wq);
132 return 0;
135 int pohmelfs_send_script_request(struct pohmelfs_inode *parent, struct pohmelfs_script_req *req)
137 struct pohmelfs_sb *psb = pohmelfs_sb(parent->vfs_inode.i_sb);
138 struct pohmelfs_wait *wait;
139 struct pohmelfs_io *pio;
140 struct dnet_exec *e;
141 int script_len;
142 long ret;
143 int err;
145 /* 2 commas, \n and 0-byte, which is accounted in sizeof(string) */
146 script_len = sizeof(POHMELFS_DENTRY_NAME_SCRIPT) + req->obj_len + 3;
148 wait = pohmelfs_wait_alloc(parent);
149 if (!wait) {
150 err = -ENOMEM;
151 goto err_out_exit;
154 pio = kmem_cache_zalloc(pohmelfs_io_cache, GFP_NOIO);
155 if (!pio) {
156 err = -ENOMEM;
157 goto err_out_wait_put;
160 e = kmalloc(sizeof(struct dnet_exec) + req->script_namelen + script_len + req->binary_size, GFP_NOIO);
161 if (!e) {
162 err = -ENOMEM;
163 goto err_out_free_pio;
166 memset(e, 0, sizeof(struct dnet_exec));
168 snprintf(e->data, req->script_namelen + script_len, "%s%s'%s'\n", req->script_name, POHMELFS_DENTRY_NAME_SCRIPT, req->obj_name);
169 script_len--; /* do not include last 0-byte in the script */
171 memcpy(e->data + req->script_namelen + script_len, req->binary, req->binary_size);
173 e->type = DNET_EXEC_PYTHON_SCRIPT_NAME;
174 e->name_size = req->script_namelen;
175 e->script_size = script_len;
176 e->binary_size = req->binary_size;
177 dnet_convert_exec(e);
179 pio->pi = parent;
180 pio->id = req->id;
181 pio->group_id = req->group_id;
182 pio->cflags = DNET_FLAGS_NEED_ACK;
183 if (req->complete == pohmelfs_lookup_complete)
184 pio->cflags |= DNET_FLAGS_NOLOCK;
186 pio->cmd = DNET_CMD_EXEC;
187 pio->size = sizeof(struct dnet_exec) + req->script_namelen + script_len + req->binary_size;
188 pio->data = e;
189 pio->priv = wait;
190 pio->cb.init = pohmelfs_send_inode_info_init;
191 pio->cb.destroy = pohmelfs_send_inode_info_destroy;
192 pio->cb.complete = req->complete;
194 if (pio->group_id) {
195 err = pohmelfs_send_buf_single(pio, NULL);
196 } else {
197 err = pohmelfs_send_buf(pio);
199 if (err)
200 goto err_out_free;
202 if (req->sync) {
203 ret = wait_event_interruptible_timeout(wait->wq, wait->condition != 0, msecs_to_jiffies(psb->read_wait_timeout));
204 if (ret <= 0) {
205 err = ret;
206 if (ret == 0)
207 err = -ETIMEDOUT;
208 goto err_out_free;
211 if (wait->condition < 0)
212 err = wait->condition;
214 req->ret = wait->ret;
215 req->ret_cond = wait->condition;
219 int len = 6;
220 char parent_id_str[len*2+1];
222 pr_debug("pohmelfs: %.*s: %s: inode->id: %s, ino: %lu, object: %s, binary size: %d, ret: %p, condition: %d\n",
223 req->script_namelen, req->script_name,
224 pohmelfs_dump_id(req->id->id),
225 pohmelfs_dump_id_len_raw(parent->id.id, len, parent_id_str),
226 parent->vfs_inode.i_ino, req->obj_name, req->binary_size,
227 req->ret, req->ret_cond);
230 err_out_free:
231 kfree(e);
232 err_out_free_pio:
233 kmem_cache_free(pohmelfs_io_cache, pio);
234 err_out_wait_put:
235 pohmelfs_wait_put(wait);
236 err_out_exit:
237 return err;
240 int pohmelfs_send_dentry(struct pohmelfs_inode *pi, struct dnet_raw_id *id, const char *sname, int len, int sync)
242 struct pohmelfs_script_req req;
243 struct pohmelfs_dentry *pd;
244 int err;
246 if (!len) {
247 err = -EINVAL;
248 goto err_out_exit;
251 pd = kmem_cache_alloc(pohmelfs_dentry_cache, GFP_NOIO);
252 if (!pd) {
253 err = -ENOMEM;
254 goto err_out_exit;
257 pd->parent_id = *id;
258 pd->disk.id = pi->id;
259 pd->disk.ino = le64_to_cpu(pi->vfs_inode.i_ino);
260 pd->disk.type = (pi->vfs_inode.i_mode >> 12) & 15;
261 pd->disk.len = len;
263 req.id = id;
265 req.script_name = POHMELFS_INODE_INFO_SCRIPT_INSERT;
266 req.script_namelen = sizeof(POHMELFS_INODE_INFO_SCRIPT_INSERT) - 1; /* not including 0-byte */
268 req.obj_name = (char *)sname;
269 req.obj_len = len;
271 req.binary = pd;
272 req.binary_size = sizeof(struct pohmelfs_dentry);
274 req.group_id = 0;
275 req.id = id;
277 req.sync = sync;
278 req.complete = pohmelfs_send_dentry_complete;
280 err = pohmelfs_send_script_request(pi, &req);
281 if (err)
282 goto err_out_free;
284 err_out_free:
285 kmem_cache_free(pohmelfs_dentry_cache, pd);
286 err_out_exit:
287 return err;
290 static int pohmelfs_create(struct inode *dir, struct dentry *dentry, int mode,
291 struct nameidata *nd)
293 struct pohmelfs_sb *psb = pohmelfs_sb(dir->i_sb);
294 struct pohmelfs_inode *parent = pohmelfs_inode(dir);
295 struct pohmelfs_inode *pi;
296 int err;
298 pi = pohmelfs_new_inode(psb, mode);
299 if (IS_ERR(pi)) {
300 err = PTR_ERR(pi);
301 goto err_out_exit;
304 inode_inc_link_count(dir);
305 pohmelfs_inode_dirty(parent, pi);
307 err = pohmelfs_send_dentry(pi, &pi->parent_id, dentry->d_name.name, dentry->d_name.len, 0);
308 if (err)
309 goto err_out_put;
311 pr_debug("pohmelfs: create: %s, ino: %lu, parent dir: %lu, object: %s\n",
312 pohmelfs_dump_id(pi->id.id), pi->vfs_inode.i_ino,
313 dir->i_ino, dentry->d_name.name);
316 * calling d_instantiate() implies that
317 * ->lookup() used d_splice_alias() with NULL inode
318 * when it failed to find requested object
320 d_instantiate(dentry, &pi->vfs_inode);
322 return 0;
324 err_out_put:
325 inode_dec_link_count(dir);
326 iput(&pi->vfs_inode);
327 err_out_exit:
328 return err;
331 static struct pohmelfs_inode *pohmelfs_lookup_group(struct inode *dir, struct dentry *dentry, int group_id)
333 struct pohmelfs_inode *parent = pohmelfs_inode(dir);
334 struct pohmelfs_script_req req;
335 struct pohmelfs_inode *pi;
336 int err;
338 req.script_name = POHMELFS_LOOKUP_SCRIPT;
339 req.script_namelen = sizeof(POHMELFS_LOOKUP_SCRIPT) - 1; /* not including 0-byte */
341 req.obj_name = (char *)dentry->d_name.name;
342 req.obj_len = dentry->d_name.len;
344 req.binary = &parent->id;
345 req.binary_size = sizeof(struct dnet_raw_id);
347 req.id = &parent->id;
348 req.complete = pohmelfs_lookup_complete;
350 req.group_id = group_id;
351 req.sync = 1;
353 err = pohmelfs_send_script_request(parent, &req);
354 if (err)
355 goto err_out_exit;
357 pi = req.ret;
358 if (!pi) {
359 err = -ENOENT;
360 goto err_out_exit;
363 return pi;
365 err_out_exit:
366 pr_debug("pohmelfs: pohmelfs_lookup_group: %s: group: %d: parent ino: %lu, name: %s: %d\n",
367 pohmelfs_dump_id(parent->id.id), group_id, parent->vfs_inode.i_ino, dentry->d_name.name, err);
368 return ERR_PTR(err);
371 static struct dentry *pohmelfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
373 struct pohmelfs_sb *psb = pohmelfs_sb(dir->i_sb);
374 struct inode *inode = NULL;
375 struct pohmelfs_inode *pi;
376 int i, err = -ENOENT;
378 for (i = 0; i < psb->group_num; ++i) {
379 pi = pohmelfs_lookup_group(dir, dentry, psb->groups[i]);
380 if (IS_ERR(pi)) {
381 err = PTR_ERR(pi);
382 continue;
385 inode = &pi->vfs_inode;
386 err = 0;
387 break;
390 if (err && (err != -ENOENT) && (err != -EOPNOTSUPP))
391 return ERR_PTR(err);
393 return d_splice_alias(inode, dentry);
396 static int pohmelfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
398 struct pohmelfs_sb *psb = pohmelfs_sb(dir->i_sb);
399 struct pohmelfs_inode *parent = pohmelfs_inode(dir);
400 struct pohmelfs_inode *pi;
401 int err;
403 inode_inc_link_count(dir);
405 pi = pohmelfs_new_inode(psb, mode | S_IFDIR);
406 if (IS_ERR(pi)) {
407 err = PTR_ERR(pi);
408 goto err_out_dir;
411 pohmelfs_inode_dirty(parent, pi);
413 d_instantiate(dentry, &pi->vfs_inode);
414 pr_debug("pohmelfs: mkdir: %s, ino: %lu, parent dir: %lu, object: %s, refcnt: %d\n",
415 pohmelfs_dump_id(pi->id.id), pi->vfs_inode.i_ino,
416 dir->i_ino, dentry->d_name.name, dentry->d_count);
418 return 0;
420 err_out_dir:
421 inode_dec_link_count(dir);
422 return err;
425 static int pohmelfs_unlink(struct inode *dir, struct dentry *dentry)
427 struct pohmelfs_inode *parent = pohmelfs_inode(dir);
428 struct pohmelfs_inode *pi = pohmelfs_inode(dentry->d_inode);
429 struct pohmelfs_script_req req;
430 int err;
432 req.script_name = POHMELFS_UNLINK_SCRIPT;
433 req.script_namelen = sizeof(POHMELFS_UNLINK_SCRIPT) - 1; /* not including 0-byte */
435 req.obj_name = (char *)dentry->d_name.name;
436 req.obj_len = dentry->d_name.len;
438 req.binary = &parent->id;
439 req.binary_size = sizeof(struct dnet_raw_id);
441 req.group_id = 0;
442 req.id = &parent->id;
443 req.complete = pohmelfs_send_dentry_complete;
445 req.sync = 0;
447 err = pohmelfs_send_script_request(parent, &req);
448 if (err)
449 return err;
451 req.script_name = POHMELFS_DATA_UNLINK_SCRIPT;
452 req.script_namelen = sizeof(POHMELFS_DATA_UNLINK_SCRIPT) - 1; /* not including 0-byte */
454 req.binary = &pi->id;
455 req.binary_size = sizeof(struct dnet_raw_id);
457 return pohmelfs_send_script_request(parent, &req);
460 static int pohmelfs_rmdir(struct inode *dir, struct dentry *dentry)
462 return pohmelfs_unlink(dir, dentry);
465 struct pohmelfs_rename_req {
466 struct dnet_raw_id old_dir_id;
467 struct dnet_raw_id new_dir_id;
468 int new_len;
469 char new_name[0];
470 } __attribute__ ((packed));
472 static int pohmelfs_rename(struct inode *old_dir, struct dentry *old_dentry,
473 struct inode *new_dir, struct dentry *new_dentry)
475 struct pohmelfs_inode *old_parent = pohmelfs_inode(old_dir);
476 struct pohmelfs_script_req req;
477 struct pohmelfs_rename_req *r;
478 int size = sizeof(struct pohmelfs_rename_req) + new_dentry->d_name.len;
479 int err;
481 r = kmalloc(size, GFP_NOIO);
482 if (!r) {
483 err = -ENOMEM;
484 goto err_out_exit;
487 r->old_dir_id = pohmelfs_inode(old_dir)->id;
488 r->new_dir_id = pohmelfs_inode(new_dir)->id;
489 r->new_len = cpu_to_le32(new_dentry->d_name.len);
490 memcpy(r->new_name, new_dentry->d_name.name, new_dentry->d_name.len);
492 req.script_name = POHMELFS_RENAME_SCRIPT;
493 req.script_namelen = sizeof(POHMELFS_RENAME_SCRIPT) - 1; /* not including 0-byte */
495 req.obj_name = (char *)old_dentry->d_name.name;
496 req.obj_len = old_dentry->d_name.len;
498 req.binary = r;
499 req.binary_size = size;
501 req.sync = 0;
502 req.group_id = 0;
503 req.id = &old_parent->id;
504 req.complete = pohmelfs_send_dentry_complete;
506 err = pohmelfs_send_script_request(old_parent, &req);
507 if (err)
508 goto err_out_free;
510 err_out_free:
511 kfree(r);
512 err_out_exit:
513 return err;
516 static int pohmelfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
518 struct pohmelfs_sb *psb = pohmelfs_sb(dir->i_sb);
519 struct pohmelfs_inode *pi;
520 struct inode *inode;
521 unsigned len = strlen(symname)+1;
522 int err = 0;
524 pi = pohmelfs_new_inode(psb, S_IFLNK | S_IRWXUGO);
525 if (IS_ERR(pi)) {
526 err = PTR_ERR(pi);
527 goto err_out_exit;
530 pohmelfs_inode_dirty(pohmelfs_inode(dir), pi);
531 inode = &pi->vfs_inode;
533 err = page_symlink(inode, symname, len);
534 if (err)
535 goto err_out_put;
537 d_instantiate(dentry, inode);
539 return 0;
541 err_out_put:
542 iput(inode);
543 err_out_exit:
544 return err;
547 static int pohmelfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
549 struct inode *inode = old_dentry->d_inode;
550 struct pohmelfs_inode *parent = pohmelfs_inode(dir);
551 struct pohmelfs_inode *pi = pohmelfs_inode(inode);
552 struct pohmelfs_script_req req;
553 int err;
555 dquot_initialize(dir);
557 inode->i_ctime = CURRENT_TIME_SEC;
558 inode_inc_link_count(inode);
559 ihold(inode);
561 pohmelfs_inode_dirty(parent, pi);
563 err = pohmelfs_send_dentry(pi, &pi->parent_id, dentry->d_name.name, dentry->d_name.len, 1);
564 if (err) {
565 goto err_out_put;
568 req.script_name = POHMELFS_HARDLINK_SCRIPT;
569 req.script_namelen = sizeof(POHMELFS_HARDLINK_SCRIPT) - 1; /* not including 0-byte */
571 req.obj_name = (char *)dentry->d_name.name;
572 req.obj_len = dentry->d_name.len;
574 req.binary = &pi->id;
575 req.binary_size = sizeof(struct dnet_raw_id);
577 req.group_id = 0;
578 req.id = &pi->id;
579 req.complete = pohmelfs_send_dentry_complete;
581 req.sync = 0;
583 err = pohmelfs_send_script_request(parent, &req);
584 if (err)
585 goto err_out_unlink;
587 d_instantiate(dentry, inode);
588 return 0;
590 err_out_unlink:
591 req.binary = &parent->id;
592 req.script_name = POHMELFS_UNLINK_SCRIPT;
593 req.script_namelen = sizeof(POHMELFS_UNLINK_SCRIPT) - 1; /* not including 0-byte */
594 pohmelfs_send_script_request(parent, &req);
596 err_out_put:
597 inode_dec_link_count(inode);
598 iput(inode);
599 return err;
602 static int pohmelfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)
604 struct pohmelfs_sb *psb = pohmelfs_sb(dir->i_sb);
605 struct pohmelfs_inode *pi;
606 struct inode *inode;
607 int err;
609 if (!new_valid_dev(rdev))
610 return -EINVAL;
612 dquot_initialize(dir);
614 pi = pohmelfs_new_inode(psb, mode);
615 if (IS_ERR(pi)) {
616 err = PTR_ERR(pi);
617 goto err_out_exit;
620 inode = &pi->vfs_inode;
622 init_special_inode(inode, inode->i_mode, rdev);
623 inode->i_op = &pohmelfs_special_inode_operations;
625 pohmelfs_inode_dirty(pohmelfs_inode(dir), pi);
627 d_instantiate(dentry, inode);
628 return 0;
630 err_out_exit:
631 return err;
634 const struct inode_operations pohmelfs_dir_inode_operations = {
635 .create = pohmelfs_create,
636 .lookup = pohmelfs_lookup,
637 .mkdir = pohmelfs_mkdir,
638 .unlink = pohmelfs_unlink,
639 .rmdir = pohmelfs_rmdir,
640 .rename = pohmelfs_rename,
641 .symlink = pohmelfs_symlink,
642 .link = pohmelfs_link,
643 .mknod = pohmelfs_mknod,
646 struct pohmelfs_readdir_header {
647 char magic[8];
648 unsigned short version;
649 unsigned short chunk_size;
650 unsigned int chunk_num;
651 } __attribute__((packed));
653 static void pohmelfs_convert_readdir_header(struct pohmelfs_readdir_header *h)
655 h->version = dnet_bswap16(h->version);
656 h->chunk_size = dnet_bswap16(h->chunk_size);
657 h->chunk_num = dnet_bswap32(h->chunk_num);
660 struct pohmelfs_readdir_chunk_header {
661 unsigned short length;
662 unsigned short num;
663 unsigned short key_size;
664 unsigned short payload_size;
665 } __attribute__((packed));
667 static void pohmelfs_convert_readdir_chunk_header(struct pohmelfs_readdir_chunk_header *h)
669 h->length = dnet_bswap16(h->length);
670 h->num = dnet_bswap16(h->num);
671 h->key_size = dnet_bswap16(h->key_size);
672 h->payload_size = dnet_bswap16(h->payload_size);
675 /* Chunk size = maximum file name length + sizeof header + sizeof pohmelfs_inode_info
676 * It allows to store whole file entry on 1 chunk
678 #define POHMELFS_CHUNK_SIZE (NAME_MAX + 1 + sizeof(struct pohmelfs_inode_info) + sizeof(struct pohmelfs_readdir_chunk_header))
680 enum pohmelfs_readdir_warm_states {
681 POHMELFS_READDIR_WANT_HEADER = 1,
682 POHMELFS_READDIR_WANT_RECV_CHUNK,
685 struct pohmelfs_readdir_warm_priv {
686 struct pohmelfs_wait *wait;
688 struct kref refcnt;
690 struct pohmelfs_readdir_header header;
692 int state;
693 int read_total; /* number of inode offsets read or processed total (in all chunks summed) */
694 int read_in_inode; /* offset of name+pohmelfs_inode_info read in below buffer */
696 char chunk[POHMELFS_CHUNK_SIZE];
699 static void pohmelfs_readdir_warm_free(struct kref *kref)
701 struct pohmelfs_readdir_warm_priv *priv = container_of(kref, struct pohmelfs_readdir_warm_priv, refcnt);
703 if (priv->wait)
704 pohmelfs_wait_put(priv->wait);
705 kfree(priv);
708 static void pohmelfs_readdir_warm_destroy(struct pohmelfs_trans *t)
710 struct pohmelfs_readdir_warm_priv *priv = t->priv;
711 struct pohmelfs_wait *wait = priv->wait;
713 wake_up(&wait->wq);
714 kref_put(&priv->refcnt, pohmelfs_readdir_warm_free);
717 static int pohmelfs_readdir_warm_complete(struct pohmelfs_trans *t, struct pohmelfs_state *recv)
719 struct pohmelfs_inode *pi = pohmelfs_inode(t->inode);
720 struct pohmelfs_readdir_warm_priv *priv = t->priv;
721 struct pohmelfs_wait *wait = priv->wait;
722 struct dnet_cmd *cmd = &recv->cmd;
724 if (t->recv_data) {
725 kfree(t->recv_data);
726 t->recv_data = NULL;
729 pr_debug("pohmelfs: %s: comlete cmd size: %llu, recv offset: %llu, flags: %x\n",
730 pohmelfs_dump_id(pi->id.id), (unsigned long long)cmd->size, t->recv_offset, cmd->flags);
732 if (!(cmd->flags & DNET_FLAGS_MORE)) {
733 wait->condition = cmd->status;
734 if (!wait->condition)
735 wait->condition = 1;
738 return 0;
741 static int pohmelfs_dentry_add(struct pohmelfs_inode *parent, struct pohmelfs_inode *pi, char *name, int len)
743 struct inode *inode = &pi->vfs_inode;
744 struct inode *dir = &parent->vfs_inode;
745 struct dentry *dentry, *parent_dentry, *old;
746 struct qstr str;
747 int err;
749 str.name = name;
750 str.len = len;
751 str.hash = full_name_hash(str.name, str.len);
753 /* we do not need to hold dir->i_mutex here, don't we? :) */
754 parent_dentry = d_find_alias(dir);
755 if (!parent_dentry) {
756 err = -ENOENT;
757 goto err_out_exit;
760 dentry = d_lookup(parent_dentry, &str);
761 if (dentry) {
762 err = -EEXIST;
764 dput(dentry);
765 goto err_out_put_parent;
768 * if things are ok, dentry has 2 references -
769 * one in parent dir, and another its own,
770 * which we should drop
772 dentry = d_alloc(parent_dentry, &str);
773 if (!dentry) {
774 err = -ENOMEM;
775 goto err_out_put_parent;
778 old = d_splice_alias(inode, dentry);
779 if (unlikely(old)) {
780 dput(dentry);
781 dentry = old;
782 } else {
783 dput(dentry);
786 dput(parent_dentry);
787 return 0;
789 err_out_put_parent:
790 dput(parent_dentry);
791 err_out_exit:
792 return err;
795 static int pohmelfs_update_inode(struct pohmelfs_inode *parent, struct pohmelfs_inode_info *info, char *name)
797 struct pohmelfs_sb *psb = pohmelfs_sb(parent->vfs_inode.i_sb);
798 struct pohmelfs_inode *pi;
799 struct inode *inode;
800 int err = 0;
802 pi = pohmelfs_sb_inode_lookup(psb, &info->id);
803 if (pi) {
804 inode = &pi->vfs_inode;
805 pohmelfs_fill_inode(inode, info);
807 } else {
808 pi = pohmelfs_existing_inode(psb, info);
809 if (IS_ERR(pi)) {
810 err = PTR_ERR(pi);
811 goto err_out_exit;
813 inode = &pi->vfs_inode;
815 pi->parent_id = parent->id;
818 err = pohmelfs_dentry_add(parent, pi, name, info->namelen);
819 if (err)
820 iput(inode);
822 err_out_exit:
823 return err;
826 static int pohmelfs_readdir_warm_scratch(struct pohmelfs_trans *t, struct pohmelfs_state *recv)
828 struct pohmelfs_inode *pi = pohmelfs_inode(t->inode);
829 struct pohmelfs_readdir_warm_priv *priv = t->priv;
830 struct dnet_cmd *cmd = &recv->cmd;
831 int err = 0;
833 pr_debug("pohmelfs: %s: cmd size: %llu, recv offset: %llu\n",
834 pohmelfs_dump_id(pi->id.id), (unsigned long long)cmd->size, t->recv_offset);
835 while (t->recv_offset != cmd->size) {
836 long rest = cmd->size - t->recv_offset;
838 if (rest > POHMELFS_CHUNK_SIZE)
839 rest = POHMELFS_CHUNK_SIZE;
841 err = pohmelfs_recv(t, recv, priv->chunk, rest);
842 if (err < 0)
843 break;
845 err = 0;
847 pr_debug("pohmelfs: %s: done cmd size: %llu, recv offset: %llu\n",
848 pohmelfs_dump_id(pi->id.id), (unsigned long long)cmd->size, t->recv_offset);
850 return err;
853 static int pohmelfs_readdir_warm_recv_reply(struct pohmelfs_trans *t, struct pohmelfs_state *recv)
855 struct pohmelfs_inode *pi = pohmelfs_inode(t->inode);
856 struct pohmelfs_readdir_warm_priv *priv = t->priv;
857 struct dnet_cmd *cmd = &recv->cmd;
858 int attr_size = sizeof(struct dnet_attr) + sizeof(struct dnet_io_attr);
859 long old_recv_offset;
860 void *data;
861 int size;
862 int err = 0;
864 pr_debug("pohmelfs: %s: recv reply: cmd size: %llu, recv offset: %llu\n",
865 pohmelfs_dump_id(pi->id.id), (unsigned long long)cmd->size, t->recv_offset);
867 if (t->recv_offset < attr_size) {
868 data = &t->cmd.attr;
870 data += t->recv_offset;
871 size = attr_size - t->recv_offset;
873 err = pohmelfs_recv(t, recv, data, size);
874 if (err < 0)
875 goto err_out_exit;
877 if (t->recv_offset == attr_size) {
878 dnet_convert_attr(&t->cmd.attr);
879 dnet_convert_io_attr(&t->cmd.p.io);
881 pr_debug("pohmelfs: %d:%s: cmd size: %llu, io size: %llu\n",
882 cmd->id.group_id, pohmelfs_dump_id(cmd->id.id),
883 (unsigned long long)cmd->size, (unsigned long long)t->cmd.p.io.size);
885 priv->state = POHMELFS_READDIR_WANT_HEADER;
889 if (priv->state == POHMELFS_READDIR_WANT_HEADER) {
890 int header_size_to_read = sizeof(struct pohmelfs_readdir_header) - (t->recv_offset - attr_size);
892 data = &priv->header;
893 data += sizeof(struct pohmelfs_readdir_header) - header_size_to_read;
895 err = pohmelfs_recv(t, recv, data, header_size_to_read);
896 if (err < 0)
897 goto err_out_exit;
899 pohmelfs_convert_readdir_header(&priv->header);
900 priv->read_total = 0;
901 priv->read_in_inode = 0;
902 priv->state = POHMELFS_READDIR_WANT_RECV_CHUNK;
904 pr_debug("pohmelfs: %d:%s: header: header size: %d, version: %hd, chunk_size: %hd, chunk_num: %d\n",
905 cmd->id.group_id, pohmelfs_dump_id(cmd->id.id), header_size_to_read,
906 priv->header.version, priv->header.chunk_size, priv->header.chunk_num);
908 if (priv->header.chunk_size > POHMELFS_CHUNK_SIZE) {
909 err = -E2BIG;
910 goto err_out_exit;
914 get_new_chunk:
915 if (priv->read_total == priv->header.chunk_num) {
916 err = pohmelfs_readdir_warm_scratch(t, recv);
917 goto err_out_exit;
920 if (priv->state == POHMELFS_READDIR_WANT_RECV_CHUNK) {
921 data = priv->chunk + priv->read_in_inode;
922 size = POHMELFS_CHUNK_SIZE - priv->read_in_inode;
924 old_recv_offset = t->recv_offset;
926 err = pohmelfs_recv(t, recv, data, size);
927 if (err < 0)
928 goto err_out_exit;
930 priv->read_in_inode += t->recv_offset - old_recv_offset;
932 if (priv->read_in_inode == POHMELFS_CHUNK_SIZE) {
933 struct pohmelfs_readdir_chunk_header *chunk_header;
934 struct pohmelfs_inode_info *info;
935 char *filename;
937 priv->read_in_inode = 0;
938 priv->read_total++;
940 chunk_header = (struct pohmelfs_readdir_chunk_header *)priv->chunk;
941 pohmelfs_convert_readdir_chunk_header(chunk_header);
944 * Here we assume that record always fits in 1 chunk.
945 * In future this code should be changed to read several chunks
946 * and concatenate it to build continous buffer for
947 * file name and pohmelfs_inode_info structure
950 info = (struct pohmelfs_inode_info *)(priv->chunk + sizeof(struct pohmelfs_readdir_chunk_header) + chunk_header->key_size);
951 pohmelfs_convert_inode_info(info);
953 filename = (char *)(priv->chunk + sizeof(struct pohmelfs_readdir_chunk_header));
955 err = pohmelfs_update_inode(priv->wait->pi, info, filename);
956 pr_debug("pohmelfs: %d:%s: inode: %llu, namelen: %d, name: %.*s: %d\n",
957 cmd->id.group_id, pohmelfs_dump_id(info->id.id), (unsigned long long)info->ino,
958 info->namelen, info->namelen, filename, err);
959 } else {
960 err = -EAGAIN;
961 goto err_out_exit;
964 if ((priv->read_total < priv->header.chunk_num) && (t->recv_offset < cmd->size))
965 goto get_new_chunk;
968 return 0;
970 err_out_exit:
971 return err;
974 static int pohmelfs_readdir_warm_init(struct pohmelfs_trans *t)
976 struct pohmelfs_readdir_warm_priv *priv = t->priv;
978 kref_get(&priv->refcnt);
979 return 0;
982 static int pohmelfs_warm_dir_group(struct inode *dir, int group_id)
984 struct pohmelfs_sb *psb = pohmelfs_sb(dir->i_sb);
985 struct pohmelfs_inode *parent = pohmelfs_inode(dir);
986 struct pohmelfs_io *io;
987 struct pohmelfs_readdir_warm_priv *priv;
988 struct pohmelfs_wait *wait;
989 long ret;
990 int err;
992 io = kmem_cache_zalloc(pohmelfs_io_cache, GFP_NOIO);
993 if (!io) {
994 err = -ENOMEM;
995 goto err_out_exit;
998 priv = kzalloc(sizeof(struct pohmelfs_readdir_warm_priv), GFP_NOIO);
999 if (!priv) {
1000 err = -ENOMEM;
1001 goto err_out_free;
1004 kref_init(&priv->refcnt);
1006 wait = pohmelfs_wait_alloc(parent);
1007 if (!wait) {
1008 err = -ENOMEM;
1009 goto err_out_put;
1012 priv->wait = wait;
1014 io->pi = parent;
1015 io->id = &parent->id;
1016 io->cflags = DNET_FLAGS_NEED_ACK | DNET_FLAGS_NOLOCK;
1017 io->cmd = DNET_CMD_READ;
1018 io->cb.recv_reply = pohmelfs_readdir_warm_recv_reply;
1019 io->cb.complete = pohmelfs_readdir_warm_complete;
1020 io->cb.destroy = pohmelfs_readdir_warm_destroy;
1021 io->cb.init = pohmelfs_readdir_warm_init;
1022 io->priv = priv;
1024 err = pohmelfs_send_io_group(io, group_id);
1025 if (err)
1026 goto err_out_put;
1028 /* destruction callback will drop reference */
1029 ret = wait_event_interruptible_timeout(wait->wq, wait->condition != 0, msecs_to_jiffies(psb->read_wait_timeout));
1030 if (ret <= 0) {
1031 err = ret;
1032 if (ret == 0)
1033 err = -ETIMEDOUT;
1034 goto err_out_put;
1037 if (wait->condition < 0) {
1038 err = wait->condition;
1039 goto err_out_put;
1042 /* drop the reference we grabbed at creation time */
1043 kref_put(&priv->refcnt, pohmelfs_readdir_warm_free);
1044 kmem_cache_free(pohmelfs_io_cache, io);
1045 return 0;
1047 err_out_put:
1048 kref_put(&priv->refcnt, pohmelfs_readdir_warm_free);
1049 err_out_free:
1050 kmem_cache_free(pohmelfs_io_cache, io);
1051 err_out_exit:
1052 return err;
1055 static int pohmelfs_warm_dir(struct inode *dir)
1057 struct pohmelfs_sb *psb = pohmelfs_sb(dir->i_sb);
1058 int i, err = -ENOENT;
1060 for (i = 0; i < psb->group_num; ++i) {
1061 err = pohmelfs_warm_dir_group(dir, psb->groups[i]);
1062 if (err)
1063 continue;
1065 return 0;
1068 return err;
1071 static int pohmelfs_readdir_complete(struct pohmelfs_trans *t, struct pohmelfs_state *recv)
1073 struct pohmelfs_inode *pi = pohmelfs_inode(t->inode);
1074 struct pohmelfs_wait *wait = t->priv;
1075 struct dnet_cmd *cmd = &recv->cmd;
1077 pr_debug("pohmelfs: %s: readdir comlete: cmd size: %llu, recv offset: %llu, flags: %x\n",
1078 pohmelfs_dump_id(pi->id.id), (unsigned long long)cmd->size, t->recv_offset, cmd->flags);
1080 if (cmd->flags & DNET_FLAGS_MORE) {
1081 if (cmd->size > sizeof(struct dnet_attr)) {
1082 wait->ret = t->recv_data;
1083 wait->condition = cmd->size;
1085 t->recv_data = NULL;
1087 } else {
1088 if (!wait->condition) {
1089 wait->condition = cmd->status;
1090 if (!wait->condition)
1091 wait->condition = 1;
1095 return 0;
1098 static int pohmelfs_readdir_process(void *data, int size, struct file *filp, void *dirent, filldir_t filldir)
1100 int err = 0;
1102 while (size > 0) {
1103 struct pohmelfs_dentry_disk *d = data;
1105 if (size < sizeof(struct pohmelfs_dentry_disk)) {
1106 err = -EINVAL;
1107 break;
1110 if (size < d->len) {
1111 err = -EINVAL;
1112 break;
1115 err = filldir(dirent, d->name, d->len, filp->f_pos, le64_to_cpu(d->ino), d->type);
1116 if (err)
1117 break;
1119 filp->f_pos++;
1120 size -= sizeof(struct pohmelfs_dentry_disk) + d->len;
1121 data += sizeof(struct pohmelfs_dentry_disk) + d->len;
1124 return err;
1127 struct pohmelfs_readdir {
1128 struct dnet_raw_id id;
1129 int max_size;
1130 int fpos;
1133 static int pohmelfs_readdir_group(int group_id, struct file *filp, void *dirent, filldir_t filldir)
1135 struct dentry *dentry = filp->f_path.dentry;
1136 struct inode *dir = dentry->d_inode;
1137 struct pohmelfs_sb *psb = pohmelfs_sb(dir->i_sb);
1138 struct pohmelfs_inode *parent = pohmelfs_inode(dir);
1139 struct pohmelfs_readdir rd;
1140 struct pohmelfs_script_req req;
1141 void *data;
1142 int size;
1143 int err;
1145 req.script_name = POHMELFS_READDIR_SCRIPT;
1146 req.script_namelen = sizeof(POHMELFS_READDIR_SCRIPT) - 1; /* not including 0-byte */
1148 req.obj_name = (char *)dentry->d_name.name;
1149 req.obj_len = dentry->d_name.len;
1151 rd.id = parent->id;
1152 rd.max_size = psb->readdir_allocation * PAGE_SIZE - sizeof(struct dnet_attr); /* cmd->size should fit one page */
1153 rd.fpos = filp->f_pos;
1155 req.binary = &rd;
1156 req.binary_size = sizeof(struct pohmelfs_readdir);
1158 req.id = &parent->id;
1159 req.complete = pohmelfs_readdir_complete;
1161 req.group_id = group_id;
1162 req.sync = 1;
1164 err = pohmelfs_send_script_request(parent, &req);
1165 if (err < 0)
1166 goto err_out_exit;
1168 data = req.ret;
1169 size = req.ret_cond;
1170 if (!data || !size) {
1171 err = -ENOENT;
1172 goto err_out_exit;
1175 err = pohmelfs_readdir_process(data + sizeof(struct dnet_attr), size - sizeof(struct dnet_attr), filp, dirent, filldir);
1177 kfree(data);
1179 err_out_exit:
1180 return err;
1183 static int pohmelfs_dir_open(struct inode *dir, struct file *filp)
1185 struct pohmelfs_sb *psb = pohmelfs_sb(dir->i_sb);
1186 struct pohmelfs_inode *pi = pohmelfs_inode(dir);
1188 if (get_seconds() < pi->update + psb->sync_timeout)
1189 return dcache_dir_open(dir, filp);
1191 filp->f_pos = 0;
1192 return 0;
1195 static int pohmelfs_dir_close(struct inode *inode, struct file *filp)
1197 if (filp->private_data)
1198 return dcache_dir_close(inode, filp);
1199 return 0;
1202 static int pohmelfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
1204 struct dentry *dentry = filp->f_path.dentry;
1205 struct inode *dir = dentry->d_inode;
1206 struct pohmelfs_inode *pi = pohmelfs_inode(dir);
1207 struct pohmelfs_sb *psb = pohmelfs_sb(dir->i_sb);
1208 int i, err = -ENOENT;
1210 if (filp->private_data) {
1211 return dcache_readdir(filp, dirent, filldir);
1214 for (i = 0; i < psb->group_num; ++i) {
1215 err = pohmelfs_readdir_group(psb->groups[i], filp, dirent, filldir);
1216 if (err)
1217 continue;
1219 pi->update = get_seconds();
1220 return 0;
1223 return err;
1226 const struct file_operations pohmelfs_dir_fops = {
1227 .open = pohmelfs_dir_open,
1228 .release = pohmelfs_dir_close,
1229 .read = generic_read_dir,
1230 .readdir = pohmelfs_readdir,