diff options
Diffstat (limited to 'drivers/staging/pohmelfs/dir.c')
-rw-r--r-- | drivers/staging/pohmelfs/dir.c | 1101 |
1 files changed, 1101 insertions, 0 deletions
diff --git a/drivers/staging/pohmelfs/dir.c b/drivers/staging/pohmelfs/dir.c new file mode 100644 index 00000000000..7598e77672a --- /dev/null +++ b/drivers/staging/pohmelfs/dir.c | |||
@@ -0,0 +1,1101 @@ | |||
1 | /* | ||
2 | * 2007+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net> | ||
3 | * All rights reserved. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | */ | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/fs.h> | ||
18 | #include <linux/jhash.h> | ||
19 | #include <linux/namei.h> | ||
20 | #include <linux/slab.h> | ||
21 | #include <linux/pagemap.h> | ||
22 | |||
23 | #include "netfs.h" | ||
24 | |||
25 | static int pohmelfs_cmp_hash(struct pohmelfs_name *n, u32 hash) | ||
26 | { | ||
27 | if (n->hash > hash) | ||
28 | return -1; | ||
29 | if (n->hash < hash) | ||
30 | return 1; | ||
31 | |||
32 | return 0; | ||
33 | } | ||
34 | |||
35 | static struct pohmelfs_name *pohmelfs_search_hash_unprecise(struct pohmelfs_inode *pi, u32 hash) | ||
36 | { | ||
37 | struct rb_node *n = pi->hash_root.rb_node; | ||
38 | struct pohmelfs_name *tmp = NULL; | ||
39 | int cmp; | ||
40 | |||
41 | while (n) { | ||
42 | tmp = rb_entry(n, struct pohmelfs_name, hash_node); | ||
43 | |||
44 | cmp = pohmelfs_cmp_hash(tmp, hash); | ||
45 | if (cmp < 0) | ||
46 | n = n->rb_left; | ||
47 | else if (cmp > 0) | ||
48 | n = n->rb_right; | ||
49 | else | ||
50 | break; | ||
51 | |||
52 | } | ||
53 | |||
54 | return tmp; | ||
55 | } | ||
56 | |||
57 | struct pohmelfs_name *pohmelfs_search_hash(struct pohmelfs_inode *pi, u32 hash) | ||
58 | { | ||
59 | struct pohmelfs_name *tmp; | ||
60 | |||
61 | tmp = pohmelfs_search_hash_unprecise(pi, hash); | ||
62 | if (tmp && (tmp->hash == hash)) | ||
63 | return tmp; | ||
64 | |||
65 | return NULL; | ||
66 | } | ||
67 | |||
68 | static void __pohmelfs_name_del(struct pohmelfs_inode *parent, struct pohmelfs_name *node) | ||
69 | { | ||
70 | rb_erase(&node->hash_node, &parent->hash_root); | ||
71 | } | ||
72 | |||
73 | /* | ||
74 | * Remove name cache entry from its caches and free it. | ||
75 | */ | ||
76 | static void pohmelfs_name_free(struct pohmelfs_inode *parent, struct pohmelfs_name *node) | ||
77 | { | ||
78 | __pohmelfs_name_del(parent, node); | ||
79 | list_del(&node->sync_create_entry); | ||
80 | kfree(node); | ||
81 | } | ||
82 | |||
83 | static struct pohmelfs_name *pohmelfs_insert_hash(struct pohmelfs_inode *pi, | ||
84 | struct pohmelfs_name *new) | ||
85 | { | ||
86 | struct rb_node **n = &pi->hash_root.rb_node, *parent = NULL; | ||
87 | struct pohmelfs_name *ret = NULL, *tmp; | ||
88 | int cmp; | ||
89 | |||
90 | while (*n) { | ||
91 | parent = *n; | ||
92 | |||
93 | tmp = rb_entry(parent, struct pohmelfs_name, hash_node); | ||
94 | |||
95 | cmp = pohmelfs_cmp_hash(tmp, new->hash); | ||
96 | if (cmp < 0) | ||
97 | n = &parent->rb_left; | ||
98 | else if (cmp > 0) | ||
99 | n = &parent->rb_right; | ||
100 | else { | ||
101 | ret = tmp; | ||
102 | break; | ||
103 | } | ||
104 | } | ||
105 | |||
106 | if (ret) { | ||
107 | printk("%s: exist: parent: %llu, ino: %llu, hash: %x, len: %u, data: '%s', " | ||
108 | "new: ino: %llu, hash: %x, len: %u, data: '%s'.\n", | ||
109 | __func__, pi->ino, | ||
110 | ret->ino, ret->hash, ret->len, ret->data, | ||
111 | new->ino, new->hash, new->len, new->data); | ||
112 | ret->ino = new->ino; | ||
113 | return ret; | ||
114 | } | ||
115 | |||
116 | rb_link_node(&new->hash_node, parent, n); | ||
117 | rb_insert_color(&new->hash_node, &pi->hash_root); | ||
118 | |||
119 | return NULL; | ||
120 | } | ||
121 | |||
122 | /* | ||
123 | * Free name cache for given inode. | ||
124 | */ | ||
125 | void pohmelfs_free_names(struct pohmelfs_inode *parent) | ||
126 | { | ||
127 | struct rb_node *rb_node; | ||
128 | struct pohmelfs_name *n; | ||
129 | |||
130 | for (rb_node = rb_first(&parent->hash_root); rb_node;) { | ||
131 | n = rb_entry(rb_node, struct pohmelfs_name, hash_node); | ||
132 | rb_node = rb_next(rb_node); | ||
133 | |||
134 | pohmelfs_name_free(parent, n); | ||
135 | } | ||
136 | } | ||
137 | |||
138 | static void pohmelfs_fix_offset(struct pohmelfs_inode *parent, struct pohmelfs_name *node) | ||
139 | { | ||
140 | parent->total_len -= node->len; | ||
141 | } | ||
142 | |||
143 | /* | ||
144 | * Free name cache entry helper. | ||
145 | */ | ||
146 | void pohmelfs_name_del(struct pohmelfs_inode *parent, struct pohmelfs_name *node) | ||
147 | { | ||
148 | pohmelfs_fix_offset(parent, node); | ||
149 | pohmelfs_name_free(parent, node); | ||
150 | } | ||
151 | |||
152 | /* | ||
153 | * Insert new name cache entry into all hash cache. | ||
154 | */ | ||
155 | static int pohmelfs_insert_name(struct pohmelfs_inode *parent, struct pohmelfs_name *n) | ||
156 | { | ||
157 | struct pohmelfs_name *name; | ||
158 | |||
159 | name = pohmelfs_insert_hash(parent, n); | ||
160 | if (name) | ||
161 | return -EEXIST; | ||
162 | |||
163 | parent->total_len += n->len; | ||
164 | list_add_tail(&n->sync_create_entry, &parent->sync_create_list); | ||
165 | |||
166 | return 0; | ||
167 | } | ||
168 | |||
169 | /* | ||
170 | * Allocate new name cache entry. | ||
171 | */ | ||
172 | static struct pohmelfs_name *pohmelfs_name_alloc(unsigned int len) | ||
173 | { | ||
174 | struct pohmelfs_name *n; | ||
175 | |||
176 | n = kzalloc(sizeof(struct pohmelfs_name) + len, GFP_KERNEL); | ||
177 | if (!n) | ||
178 | return NULL; | ||
179 | |||
180 | INIT_LIST_HEAD(&n->sync_create_entry); | ||
181 | |||
182 | n->data = (char *)(n+1); | ||
183 | |||
184 | return n; | ||
185 | } | ||
186 | |||
187 | /* | ||
188 | * Add new name entry into directory's cache. | ||
189 | */ | ||
190 | static int pohmelfs_add_dir(struct pohmelfs_sb *psb, struct pohmelfs_inode *parent, | ||
191 | struct pohmelfs_inode *npi, struct qstr *str, unsigned int mode, int link) | ||
192 | { | ||
193 | int err = -ENOMEM; | ||
194 | struct pohmelfs_name *n; | ||
195 | |||
196 | n = pohmelfs_name_alloc(str->len + 1); | ||
197 | if (!n) | ||
198 | goto err_out_exit; | ||
199 | |||
200 | n->ino = npi->ino; | ||
201 | n->mode = mode; | ||
202 | n->len = str->len; | ||
203 | n->hash = str->hash; | ||
204 | sprintf(n->data, "%s", str->name); | ||
205 | |||
206 | mutex_lock(&parent->offset_lock); | ||
207 | err = pohmelfs_insert_name(parent, n); | ||
208 | mutex_unlock(&parent->offset_lock); | ||
209 | |||
210 | if (err) { | ||
211 | if (err != -EEXIST) | ||
212 | goto err_out_free; | ||
213 | kfree(n); | ||
214 | } | ||
215 | |||
216 | return 0; | ||
217 | |||
218 | err_out_free: | ||
219 | kfree(n); | ||
220 | err_out_exit: | ||
221 | return err; | ||
222 | } | ||
223 | |||
224 | /* | ||
225 | * Create new inode for given parameters (name, inode info, parent). | ||
226 | * This does not create object on the server, it will be synced there during writeback. | ||
227 | */ | ||
228 | struct pohmelfs_inode *pohmelfs_new_inode(struct pohmelfs_sb *psb, | ||
229 | struct pohmelfs_inode *parent, struct qstr *str, | ||
230 | struct netfs_inode_info *info, int link) | ||
231 | { | ||
232 | struct inode *new = NULL; | ||
233 | struct pohmelfs_inode *npi; | ||
234 | int err = -EEXIST; | ||
235 | |||
236 | dprintk("%s: creating inode: parent: %llu, ino: %llu, str: %p.\n", | ||
237 | __func__, (parent) ? parent->ino : 0, info->ino, str); | ||
238 | |||
239 | err = -ENOMEM; | ||
240 | new = iget_locked(psb->sb, info->ino); | ||
241 | if (!new) | ||
242 | goto err_out_exit; | ||
243 | |||
244 | npi = POHMELFS_I(new); | ||
245 | npi->ino = info->ino; | ||
246 | err = 0; | ||
247 | |||
248 | if (new->i_state & I_NEW) { | ||
249 | dprintk("%s: filling VFS inode: %lu/%llu.\n", | ||
250 | __func__, new->i_ino, info->ino); | ||
251 | pohmelfs_fill_inode(new, info); | ||
252 | |||
253 | if (S_ISDIR(info->mode)) { | ||
254 | struct qstr s; | ||
255 | |||
256 | s.name = "."; | ||
257 | s.len = 1; | ||
258 | s.hash = jhash(s.name, s.len, 0); | ||
259 | |||
260 | err = pohmelfs_add_dir(psb, npi, npi, &s, info->mode, 0); | ||
261 | if (err) | ||
262 | goto err_out_put; | ||
263 | |||
264 | s.name = ".."; | ||
265 | s.len = 2; | ||
266 | s.hash = jhash(s.name, s.len, 0); | ||
267 | |||
268 | err = pohmelfs_add_dir(psb, npi, (parent) ? parent : npi, &s, | ||
269 | (parent) ? parent->vfs_inode.i_mode : npi->vfs_inode.i_mode, 0); | ||
270 | if (err) | ||
271 | goto err_out_put; | ||
272 | } | ||
273 | } | ||
274 | |||
275 | if (str) { | ||
276 | if (parent) { | ||
277 | err = pohmelfs_add_dir(psb, parent, npi, str, info->mode, link); | ||
278 | |||
279 | dprintk("%s: %s inserted name: '%s', new_offset: %llu, ino: %llu, parent: %llu.\n", | ||
280 | __func__, (err) ? "unsuccessfully" : "successfully", | ||
281 | str->name, parent->total_len, info->ino, parent->ino); | ||
282 | |||
283 | if (err && err != -EEXIST) | ||
284 | goto err_out_put; | ||
285 | } | ||
286 | } | ||
287 | |||
288 | if (new->i_state & I_NEW) { | ||
289 | if (parent) | ||
290 | mark_inode_dirty(&parent->vfs_inode); | ||
291 | mark_inode_dirty(new); | ||
292 | } | ||
293 | |||
294 | set_bit(NETFS_INODE_OWNED, &npi->state); | ||
295 | npi->lock_type = POHMELFS_WRITE_LOCK; | ||
296 | unlock_new_inode(new); | ||
297 | |||
298 | return npi; | ||
299 | |||
300 | err_out_put: | ||
301 | printk("%s: putting inode: %p, npi: %p, error: %d.\n", __func__, new, npi, err); | ||
302 | iput(new); | ||
303 | err_out_exit: | ||
304 | return ERR_PTR(err); | ||
305 | } | ||
306 | |||
307 | static int pohmelfs_remote_sync_complete(struct page **pages, unsigned int page_num, | ||
308 | void *private, int err) | ||
309 | { | ||
310 | struct pohmelfs_inode *pi = private; | ||
311 | struct pohmelfs_sb *psb = POHMELFS_SB(pi->vfs_inode.i_sb); | ||
312 | |||
313 | dprintk("%s: ino: %llu, err: %d.\n", __func__, pi->ino, err); | ||
314 | |||
315 | if (err) | ||
316 | pi->error = err; | ||
317 | wake_up(&psb->wait); | ||
318 | pohmelfs_put_inode(pi); | ||
319 | |||
320 | return err; | ||
321 | } | ||
322 | |||
323 | /* | ||
324 | * Receive directory content from the server. | ||
325 | * This should be only done for objects, which were not created locally, | ||
326 | * and which were not synced previously. | ||
327 | */ | ||
328 | static int pohmelfs_sync_remote_dir(struct pohmelfs_inode *pi) | ||
329 | { | ||
330 | struct inode *inode = &pi->vfs_inode; | ||
331 | struct pohmelfs_sb *psb = POHMELFS_SB(inode->i_sb); | ||
332 | long ret = psb->wait_on_page_timeout; | ||
333 | int err; | ||
334 | |||
335 | dprintk("%s: dir: %llu, state: %lx: remote_synced: %d.\n", | ||
336 | __func__, pi->ino, pi->state, test_bit(NETFS_INODE_REMOTE_SYNCED, &pi->state)); | ||
337 | |||
338 | if (test_bit(NETFS_INODE_REMOTE_DIR_SYNCED, &pi->state)) | ||
339 | return 0; | ||
340 | |||
341 | if (!igrab(inode)) { | ||
342 | err = -ENOENT; | ||
343 | goto err_out_exit; | ||
344 | } | ||
345 | |||
346 | err = pohmelfs_meta_command(pi, NETFS_READDIR, NETFS_TRANS_SINGLE_DST, | ||
347 | pohmelfs_remote_sync_complete, pi, 0); | ||
348 | if (err) | ||
349 | goto err_out_exit; | ||
350 | |||
351 | pi->error = 0; | ||
352 | ret = wait_event_interruptible_timeout(psb->wait, | ||
353 | test_bit(NETFS_INODE_REMOTE_DIR_SYNCED, &pi->state) || pi->error, ret); | ||
354 | dprintk("%s: awake dir: %llu, ret: %ld, err: %d.\n", __func__, pi->ino, ret, pi->error); | ||
355 | if (ret <= 0) { | ||
356 | err = ret; | ||
357 | if (!err) | ||
358 | err = -ETIMEDOUT; | ||
359 | goto err_out_exit; | ||
360 | } | ||
361 | |||
362 | if (pi->error) | ||
363 | return pi->error; | ||
364 | |||
365 | return 0; | ||
366 | |||
367 | err_out_exit: | ||
368 | clear_bit(NETFS_INODE_REMOTE_SYNCED, &pi->state); | ||
369 | |||
370 | return err; | ||
371 | } | ||
372 | |||
373 | static int pohmelfs_dir_open(struct inode *inode, struct file *file) | ||
374 | { | ||
375 | file->private_data = NULL; | ||
376 | return 0; | ||
377 | } | ||
378 | |||
379 | /* | ||
380 | * VFS readdir callback. Syncs directory content from server if needed, | ||
381 | * and provides direntry info to the userspace. | ||
382 | */ | ||
383 | static int pohmelfs_readdir(struct file *file, void *dirent, filldir_t filldir) | ||
384 | { | ||
385 | struct inode *inode = file->f_path.dentry->d_inode; | ||
386 | struct pohmelfs_inode *pi = POHMELFS_I(inode); | ||
387 | struct pohmelfs_name *n; | ||
388 | struct rb_node *rb_node; | ||
389 | int err = 0, mode; | ||
390 | u64 len; | ||
391 | |||
392 | dprintk("%s: parent: %llu, fpos: %llu, hash: %08lx.\n", | ||
393 | __func__, pi->ino, (u64)file->f_pos, | ||
394 | (unsigned long)file->private_data); | ||
395 | #if 0 | ||
396 | err = pohmelfs_data_lock(pi, 0, ~0, POHMELFS_READ_LOCK); | ||
397 | if (err) | ||
398 | return err; | ||
399 | #endif | ||
400 | err = pohmelfs_sync_remote_dir(pi); | ||
401 | if (err) | ||
402 | return err; | ||
403 | |||
404 | if (file->private_data && (file->private_data == (void *)(unsigned long)file->f_pos)) | ||
405 | return 0; | ||
406 | |||
407 | mutex_lock(&pi->offset_lock); | ||
408 | n = pohmelfs_search_hash_unprecise(pi, (unsigned long)file->private_data); | ||
409 | |||
410 | while (n) { | ||
411 | mode = (n->mode >> 12) & 15; | ||
412 | |||
413 | dprintk("%s: offset: %llu, parent ino: %llu, name: '%s', len: %u, ino: %llu, " | ||
414 | "mode: %o/%o, fpos: %llu, hash: %08x.\n", | ||
415 | __func__, file->f_pos, pi->ino, n->data, n->len, | ||
416 | n->ino, n->mode, mode, file->f_pos, n->hash); | ||
417 | |||
418 | file->private_data = (void *)(unsigned long)n->hash; | ||
419 | |||
420 | len = n->len; | ||
421 | err = filldir(dirent, n->data, n->len, file->f_pos, n->ino, mode); | ||
422 | |||
423 | if (err < 0) { | ||
424 | dprintk("%s: err: %d.\n", __func__, err); | ||
425 | err = 0; | ||
426 | break; | ||
427 | } | ||
428 | |||
429 | file->f_pos += len; | ||
430 | |||
431 | rb_node = rb_next(&n->hash_node); | ||
432 | |||
433 | if (!rb_node || (rb_node == &n->hash_node)) { | ||
434 | file->private_data = (void *)(unsigned long)file->f_pos; | ||
435 | break; | ||
436 | } | ||
437 | |||
438 | n = rb_entry(rb_node, struct pohmelfs_name, hash_node); | ||
439 | } | ||
440 | mutex_unlock(&pi->offset_lock); | ||
441 | |||
442 | return err; | ||
443 | } | ||
444 | |||
445 | static loff_t pohmelfs_dir_lseek(struct file *file, loff_t offset, int origin) | ||
446 | { | ||
447 | file->f_pos = offset; | ||
448 | file->private_data = NULL; | ||
449 | return offset; | ||
450 | } | ||
451 | |||
452 | const struct file_operations pohmelfs_dir_fops = { | ||
453 | .open = pohmelfs_dir_open, | ||
454 | .read = generic_read_dir, | ||
455 | .llseek = pohmelfs_dir_lseek, | ||
456 | .readdir = pohmelfs_readdir, | ||
457 | }; | ||
458 | |||
459 | /* | ||
460 | * Lookup single object on server. | ||
461 | */ | ||
462 | static int pohmelfs_lookup_single(struct pohmelfs_inode *parent, | ||
463 | struct qstr *str, u64 ino) | ||
464 | { | ||
465 | struct pohmelfs_sb *psb = POHMELFS_SB(parent->vfs_inode.i_sb); | ||
466 | long ret = msecs_to_jiffies(5000); | ||
467 | int err; | ||
468 | |||
469 | set_bit(NETFS_COMMAND_PENDING, &parent->state); | ||
470 | err = pohmelfs_meta_command_data(parent, parent->ino, NETFS_LOOKUP, | ||
471 | (char *)str->name, NETFS_TRANS_SINGLE_DST, NULL, NULL, ino); | ||
472 | if (err) | ||
473 | goto err_out_exit; | ||
474 | |||
475 | err = 0; | ||
476 | ret = wait_event_interruptible_timeout(psb->wait, | ||
477 | !test_bit(NETFS_COMMAND_PENDING, &parent->state), ret); | ||
478 | if (ret <= 0) { | ||
479 | err = ret; | ||
480 | if (!err) | ||
481 | err = -ETIMEDOUT; | ||
482 | } | ||
483 | |||
484 | if (err) | ||
485 | goto err_out_exit; | ||
486 | |||
487 | return 0; | ||
488 | |||
489 | err_out_exit: | ||
490 | clear_bit(NETFS_COMMAND_PENDING, &parent->state); | ||
491 | |||
492 | printk("%s: failed: parent: %llu, ino: %llu, name: '%s', err: %d.\n", | ||
493 | __func__, parent->ino, ino, str->name, err); | ||
494 | |||
495 | return err; | ||
496 | } | ||
497 | |||
498 | /* | ||
499 | * VFS lookup callback. | ||
500 | * We first try to get inode number from local name cache, if we have one, | ||
501 | * then inode can be found in inode cache. If there is no inode or no object in | ||
502 | * local cache, try to lookup it on server. This only should be done for directories, | ||
503 | * which were not created locally, otherwise remote server does not know about dir at all, | ||
504 | * so no need to try to know that. | ||
505 | */ | ||
506 | struct dentry *pohmelfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) | ||
507 | { | ||
508 | struct pohmelfs_inode *parent = POHMELFS_I(dir); | ||
509 | struct pohmelfs_name *n; | ||
510 | struct inode *inode = NULL; | ||
511 | unsigned long ino = 0; | ||
512 | int err, lock_type = POHMELFS_READ_LOCK, need_lock = 1; | ||
513 | struct qstr str = dentry->d_name; | ||
514 | |||
515 | if ((nd->intent.open.flags & O_ACCMODE) != O_RDONLY) | ||
516 | lock_type = POHMELFS_WRITE_LOCK; | ||
517 | |||
518 | if (test_bit(NETFS_INODE_OWNED, &parent->state)) { | ||
519 | if (lock_type == parent->lock_type) | ||
520 | need_lock = 0; | ||
521 | if ((lock_type == POHMELFS_READ_LOCK) && (parent->lock_type == POHMELFS_WRITE_LOCK)) | ||
522 | need_lock = 0; | ||
523 | } | ||
524 | |||
525 | if ((lock_type == POHMELFS_READ_LOCK) && !test_bit(NETFS_INODE_REMOTE_DIR_SYNCED, &parent->state)) | ||
526 | need_lock = 1; | ||
527 | |||
528 | str.hash = jhash(dentry->d_name.name, dentry->d_name.len, 0); | ||
529 | |||
530 | mutex_lock(&parent->offset_lock); | ||
531 | n = pohmelfs_search_hash(parent, str.hash); | ||
532 | if (n) | ||
533 | ino = n->ino; | ||
534 | mutex_unlock(&parent->offset_lock); | ||
535 | |||
536 | dprintk("%s: start ino: %lu, inode: %p, name: '%s', hash: %x, parent_state: %lx, need_lock: %d.\n", | ||
537 | __func__, ino, inode, str.name, str.hash, parent->state, need_lock); | ||
538 | |||
539 | if (ino) { | ||
540 | inode = ilookup(dir->i_sb, ino); | ||
541 | if (inode) | ||
542 | goto out; | ||
543 | } | ||
544 | |||
545 | dprintk("%s: no inode dir: %p, dir_ino: %llu, name: '%s', len: %u, dir_state: %lx, ino: %lu.\n", | ||
546 | __func__, dir, parent->ino, | ||
547 | str.name, str.len, parent->state, ino); | ||
548 | |||
549 | if (!ino) { | ||
550 | if (!need_lock) | ||
551 | goto out; | ||
552 | } | ||
553 | |||
554 | err = pohmelfs_data_lock(parent, 0, ~0, lock_type); | ||
555 | if (err) | ||
556 | goto out; | ||
557 | |||
558 | err = pohmelfs_lookup_single(parent, &str, ino); | ||
559 | if (err) | ||
560 | goto out; | ||
561 | |||
562 | if (!ino) { | ||
563 | mutex_lock(&parent->offset_lock); | ||
564 | n = pohmelfs_search_hash(parent, str.hash); | ||
565 | if (n) | ||
566 | ino = n->ino; | ||
567 | mutex_unlock(&parent->offset_lock); | ||
568 | } | ||
569 | |||
570 | if (ino) { | ||
571 | inode = ilookup(dir->i_sb, ino); | ||
572 | dprintk("%s: second lookup ino: %lu, inode: %p, name: '%s', hash: %x.\n", | ||
573 | __func__, ino, inode, str.name, str.hash); | ||
574 | if (!inode) { | ||
575 | dprintk("%s: No inode for ino: %lu, name: '%s', hash: %x.\n", | ||
576 | __func__, ino, str.name, str.hash); | ||
577 | /* return NULL; */ | ||
578 | return ERR_PTR(-EACCES); | ||
579 | } | ||
580 | } else { | ||
581 | printk("%s: No inode number : name: '%s', hash: %x.\n", | ||
582 | __func__, str.name, str.hash); | ||
583 | } | ||
584 | out: | ||
585 | return d_splice_alias(inode, dentry); | ||
586 | } | ||
587 | |||
588 | /* | ||
589 | * Create new object in local cache. Object will be synced to server | ||
590 | * during writeback for given inode. | ||
591 | */ | ||
592 | struct pohmelfs_inode *pohmelfs_create_entry_local(struct pohmelfs_sb *psb, | ||
593 | struct pohmelfs_inode *parent, struct qstr *str, u64 start, int mode) | ||
594 | { | ||
595 | struct pohmelfs_inode *npi; | ||
596 | int err = -ENOMEM; | ||
597 | struct netfs_inode_info info; | ||
598 | |||
599 | dprintk("%s: name: '%s', mode: %o, start: %llu.\n", | ||
600 | __func__, str->name, mode, start); | ||
601 | |||
602 | info.mode = mode; | ||
603 | info.ino = start; | ||
604 | |||
605 | if (!start) | ||
606 | info.ino = pohmelfs_new_ino(psb); | ||
607 | |||
608 | info.nlink = S_ISDIR(mode) ? 2 : 1; | ||
609 | info.uid = current_fsuid(); | ||
610 | info.gid = current_fsgid(); | ||
611 | info.size = 0; | ||
612 | info.blocksize = 512; | ||
613 | info.blocks = 0; | ||
614 | info.rdev = 0; | ||
615 | info.version = 0; | ||
616 | |||
617 | npi = pohmelfs_new_inode(psb, parent, str, &info, !!start); | ||
618 | if (IS_ERR(npi)) { | ||
619 | err = PTR_ERR(npi); | ||
620 | goto err_out_unlock; | ||
621 | } | ||
622 | |||
623 | return npi; | ||
624 | |||
625 | err_out_unlock: | ||
626 | dprintk("%s: err: %d.\n", __func__, err); | ||
627 | return ERR_PTR(err); | ||
628 | } | ||
629 | |||
630 | /* | ||
631 | * Create local object and bind it to dentry. | ||
632 | */ | ||
633 | static int pohmelfs_create_entry(struct inode *dir, struct dentry *dentry, u64 start, int mode) | ||
634 | { | ||
635 | struct pohmelfs_sb *psb = POHMELFS_SB(dir->i_sb); | ||
636 | struct pohmelfs_inode *npi, *parent; | ||
637 | struct qstr str = dentry->d_name; | ||
638 | int err; | ||
639 | |||
640 | parent = POHMELFS_I(dir); | ||
641 | |||
642 | err = pohmelfs_data_lock(parent, 0, ~0, POHMELFS_WRITE_LOCK); | ||
643 | if (err) | ||
644 | return err; | ||
645 | |||
646 | str.hash = jhash(dentry->d_name.name, dentry->d_name.len, 0); | ||
647 | |||
648 | npi = pohmelfs_create_entry_local(psb, parent, &str, start, mode); | ||
649 | if (IS_ERR(npi)) | ||
650 | return PTR_ERR(npi); | ||
651 | |||
652 | d_instantiate(dentry, &npi->vfs_inode); | ||
653 | |||
654 | dprintk("%s: parent: %llu, inode: %llu, name: '%s', parent_nlink: %d, nlink: %d.\n", | ||
655 | __func__, parent->ino, npi->ino, dentry->d_name.name, | ||
656 | (signed)dir->i_nlink, (signed)npi->vfs_inode.i_nlink); | ||
657 | |||
658 | return 0; | ||
659 | } | ||
660 | |||
661 | /* | ||
662 | * VFS create and mkdir callbacks. | ||
663 | */ | ||
664 | static int pohmelfs_create(struct inode *dir, struct dentry *dentry, int mode, | ||
665 | struct nameidata *nd) | ||
666 | { | ||
667 | return pohmelfs_create_entry(dir, dentry, 0, mode); | ||
668 | } | ||
669 | |||
670 | static int pohmelfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) | ||
671 | { | ||
672 | int err; | ||
673 | |||
674 | inode_inc_link_count(dir); | ||
675 | err = pohmelfs_create_entry(dir, dentry, 0, mode | S_IFDIR); | ||
676 | if (err) | ||
677 | inode_dec_link_count(dir); | ||
678 | |||
679 | return err; | ||
680 | } | ||
681 | |||
682 | static int pohmelfs_remove_entry(struct inode *dir, struct dentry *dentry) | ||
683 | { | ||
684 | struct pohmelfs_sb *psb = POHMELFS_SB(dir->i_sb); | ||
685 | struct inode *inode = dentry->d_inode; | ||
686 | struct pohmelfs_inode *parent = POHMELFS_I(dir), *pi = POHMELFS_I(inode); | ||
687 | struct pohmelfs_name *n; | ||
688 | int err = -ENOENT; | ||
689 | struct qstr str = dentry->d_name; | ||
690 | |||
691 | err = pohmelfs_data_lock(parent, 0, ~0, POHMELFS_WRITE_LOCK); | ||
692 | if (err) | ||
693 | return err; | ||
694 | |||
695 | str.hash = jhash(dentry->d_name.name, dentry->d_name.len, 0); | ||
696 | |||
697 | dprintk("%s: dir_ino: %llu, inode: %llu, name: '%s', nlink: %d.\n", | ||
698 | __func__, parent->ino, pi->ino, | ||
699 | str.name, (signed)inode->i_nlink); | ||
700 | |||
701 | BUG_ON(!inode); | ||
702 | |||
703 | mutex_lock(&parent->offset_lock); | ||
704 | n = pohmelfs_search_hash(parent, str.hash); | ||
705 | if (n) { | ||
706 | pohmelfs_fix_offset(parent, n); | ||
707 | if (test_bit(NETFS_INODE_REMOTE_SYNCED, &pi->state)) | ||
708 | pohmelfs_remove_child(pi, n); | ||
709 | |||
710 | pohmelfs_name_free(parent, n); | ||
711 | err = 0; | ||
712 | } | ||
713 | mutex_unlock(&parent->offset_lock); | ||
714 | |||
715 | if (!err) { | ||
716 | psb->avail_size += inode->i_size; | ||
717 | |||
718 | pohmelfs_inode_del_inode(psb, pi); | ||
719 | |||
720 | mark_inode_dirty(dir); | ||
721 | |||
722 | inode->i_ctime = dir->i_ctime; | ||
723 | if (inode->i_nlink) | ||
724 | inode_dec_link_count(inode); | ||
725 | } | ||
726 | |||
727 | return err; | ||
728 | } | ||
729 | |||
730 | /* | ||
731 | * Unlink and rmdir VFS callbacks. | ||
732 | */ | ||
733 | static int pohmelfs_unlink(struct inode *dir, struct dentry *dentry) | ||
734 | { | ||
735 | return pohmelfs_remove_entry(dir, dentry); | ||
736 | } | ||
737 | |||
738 | static int pohmelfs_rmdir(struct inode *dir, struct dentry *dentry) | ||
739 | { | ||
740 | int err; | ||
741 | struct inode *inode = dentry->d_inode; | ||
742 | |||
743 | dprintk("%s: parent: %llu, inode: %llu, name: '%s', parent_nlink: %d, nlink: %d.\n", | ||
744 | __func__, POHMELFS_I(dir)->ino, POHMELFS_I(inode)->ino, | ||
745 | dentry->d_name.name, (signed)dir->i_nlink, (signed)inode->i_nlink); | ||
746 | |||
747 | err = pohmelfs_remove_entry(dir, dentry); | ||
748 | if (!err) { | ||
749 | inode_dec_link_count(dir); | ||
750 | inode_dec_link_count(inode); | ||
751 | } | ||
752 | |||
753 | return err; | ||
754 | } | ||
755 | |||
756 | /* | ||
757 | * Link creation is synchronous. | ||
758 | * I'm lazy. | ||
759 | * Earth is somewhat round. | ||
760 | */ | ||
761 | static int pohmelfs_create_link(struct pohmelfs_inode *parent, struct qstr *obj, | ||
762 | struct pohmelfs_inode *target, struct qstr *tstr) | ||
763 | { | ||
764 | struct super_block *sb = parent->vfs_inode.i_sb; | ||
765 | struct pohmelfs_sb *psb = POHMELFS_SB(sb); | ||
766 | struct netfs_cmd *cmd; | ||
767 | struct netfs_trans *t; | ||
768 | void *data; | ||
769 | int err, parent_len, target_len = 0, cur_len, path_size = 0; | ||
770 | |||
771 | err = pohmelfs_data_lock(parent, 0, ~0, POHMELFS_WRITE_LOCK); | ||
772 | if (err) | ||
773 | return err; | ||
774 | |||
775 | err = sb->s_op->write_inode(&parent->vfs_inode, 0); | ||
776 | if (err) | ||
777 | goto err_out_exit; | ||
778 | |||
779 | if (tstr) | ||
780 | target_len = tstr->len; | ||
781 | |||
782 | parent_len = pohmelfs_path_length(parent); | ||
783 | if (target) | ||
784 | target_len += pohmelfs_path_length(target); | ||
785 | |||
786 | if (parent_len < 0) { | ||
787 | err = parent_len; | ||
788 | goto err_out_exit; | ||
789 | } | ||
790 | |||
791 | if (target_len < 0) { | ||
792 | err = target_len; | ||
793 | goto err_out_exit; | ||
794 | } | ||
795 | |||
796 | t = netfs_trans_alloc(psb, parent_len + target_len + obj->len + 2, 0, 0); | ||
797 | if (!t) { | ||
798 | err = -ENOMEM; | ||
799 | goto err_out_exit; | ||
800 | } | ||
801 | cur_len = netfs_trans_cur_len(t); | ||
802 | |||
803 | cmd = netfs_trans_current(t); | ||
804 | if (IS_ERR(cmd)) { | ||
805 | err = PTR_ERR(cmd); | ||
806 | goto err_out_free; | ||
807 | } | ||
808 | |||
809 | data = (void *)(cmd + 1); | ||
810 | cur_len -= sizeof(struct netfs_cmd); | ||
811 | |||
812 | err = pohmelfs_construct_path_string(parent, data, parent_len); | ||
813 | if (err > 0) { | ||
814 | /* Do not place null-byte before the slash */ | ||
815 | path_size = err - 1; | ||
816 | cur_len -= path_size; | ||
817 | |||
818 | err = snprintf(data + path_size, cur_len, "/%s|", obj->name); | ||
819 | |||
820 | path_size += err; | ||
821 | cur_len -= err; | ||
822 | |||
823 | cmd->ext = path_size - 1; /* No | symbol */ | ||
824 | |||
825 | if (target) { | ||
826 | err = pohmelfs_construct_path_string(target, data + path_size, target_len); | ||
827 | if (err > 0) { | ||
828 | path_size += err; | ||
829 | cur_len -= err; | ||
830 | } | ||
831 | } | ||
832 | } | ||
833 | |||
834 | if (err < 0) | ||
835 | goto err_out_free; | ||
836 | |||
837 | cmd->start = 0; | ||
838 | |||
839 | if (!target && tstr) { | ||
840 | if (tstr->len > cur_len - 1) { | ||
841 | err = -ENAMETOOLONG; | ||
842 | goto err_out_free; | ||
843 | } | ||
844 | |||
845 | err = snprintf(data + path_size, cur_len, "%s", tstr->name) + 1; /* 0-byte */ | ||
846 | path_size += err; | ||
847 | cur_len -= err; | ||
848 | cmd->start = 1; | ||
849 | } | ||
850 | |||
851 | dprintk("%s: parent: %llu, obj: '%s', target_inode: %llu, target_str: '%s', full: '%s'.\n", | ||
852 | __func__, parent->ino, obj->name, (target) ? target->ino : 0, (tstr) ? tstr->name : NULL, | ||
853 | (char *)data); | ||
854 | |||
855 | cmd->cmd = NETFS_LINK; | ||
856 | cmd->size = path_size; | ||
857 | cmd->id = parent->ino; | ||
858 | |||
859 | netfs_convert_cmd(cmd); | ||
860 | |||
861 | netfs_trans_update(cmd, t, path_size); | ||
862 | |||
863 | err = netfs_trans_finish(t, psb); | ||
864 | if (err) | ||
865 | goto err_out_exit; | ||
866 | |||
867 | return 0; | ||
868 | |||
869 | err_out_free: | ||
870 | t->result = err; | ||
871 | netfs_trans_put(t); | ||
872 | err_out_exit: | ||
873 | return err; | ||
874 | } | ||
875 | |||
876 | /* | ||
877 | * VFS hard and soft link callbacks. | ||
878 | */ | ||
879 | static int pohmelfs_link(struct dentry *old_dentry, struct inode *dir, | ||
880 | struct dentry *dentry) | ||
881 | { | ||
882 | struct inode *inode = old_dentry->d_inode; | ||
883 | struct pohmelfs_inode *pi = POHMELFS_I(inode); | ||
884 | int err; | ||
885 | struct qstr str = dentry->d_name; | ||
886 | |||
887 | str.hash = jhash(dentry->d_name.name, dentry->d_name.len, 0); | ||
888 | |||
889 | err = inode->i_sb->s_op->write_inode(inode, 0); | ||
890 | if (err) | ||
891 | return err; | ||
892 | |||
893 | err = pohmelfs_create_link(POHMELFS_I(dir), &str, pi, NULL); | ||
894 | if (err) | ||
895 | return err; | ||
896 | |||
897 | return pohmelfs_create_entry(dir, dentry, pi->ino, inode->i_mode); | ||
898 | } | ||
899 | |||
900 | static int pohmelfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) | ||
901 | { | ||
902 | struct qstr sym_str; | ||
903 | struct qstr str = dentry->d_name; | ||
904 | struct inode *inode; | ||
905 | int err; | ||
906 | |||
907 | str.hash = jhash(dentry->d_name.name, dentry->d_name.len, 0); | ||
908 | |||
909 | sym_str.name = symname; | ||
910 | sym_str.len = strlen(symname); | ||
911 | |||
912 | err = pohmelfs_create_link(POHMELFS_I(dir), &str, NULL, &sym_str); | ||
913 | if (err) | ||
914 | goto err_out_exit; | ||
915 | |||
916 | err = pohmelfs_create_entry(dir, dentry, 0, S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO); | ||
917 | if (err) | ||
918 | goto err_out_exit; | ||
919 | |||
920 | inode = dentry->d_inode; | ||
921 | |||
922 | err = page_symlink(inode, symname, sym_str.len + 1); | ||
923 | if (err) | ||
924 | goto err_out_put; | ||
925 | |||
926 | return 0; | ||
927 | |||
928 | err_out_put: | ||
929 | iput(inode); | ||
930 | err_out_exit: | ||
931 | return err; | ||
932 | } | ||
933 | |||
934 | static int pohmelfs_send_rename(struct pohmelfs_inode *pi, struct pohmelfs_inode *parent, | ||
935 | struct qstr *str) | ||
936 | { | ||
937 | int path_len, err, total_len = 0, inode_len, parent_len; | ||
938 | char *path; | ||
939 | struct netfs_trans *t; | ||
940 | struct netfs_cmd *cmd; | ||
941 | struct pohmelfs_sb *psb = POHMELFS_SB(pi->vfs_inode.i_sb); | ||
942 | |||
943 | parent_len = pohmelfs_path_length(parent); | ||
944 | inode_len = pohmelfs_path_length(pi); | ||
945 | |||
946 | if (parent_len < 0 || inode_len < 0) | ||
947 | return -EINVAL; | ||
948 | |||
949 | path_len = parent_len + inode_len + str->len + 3; | ||
950 | |||
951 | t = netfs_trans_alloc(psb, path_len, 0, 0); | ||
952 | if (!t) | ||
953 | return -ENOMEM; | ||
954 | |||
955 | cmd = netfs_trans_current(t); | ||
956 | path = (char *)(cmd + 1); | ||
957 | |||
958 | err = pohmelfs_construct_path_string(pi, path, inode_len); | ||
959 | if (err < 0) | ||
960 | goto err_out_unlock; | ||
961 | |||
962 | cmd->ext = err; | ||
963 | |||
964 | path += err; | ||
965 | total_len += err; | ||
966 | path_len -= err; | ||
967 | |||
968 | *path = '|'; | ||
969 | path++; | ||
970 | total_len++; | ||
971 | path_len--; | ||
972 | |||
973 | err = pohmelfs_construct_path_string(parent, path, parent_len); | ||
974 | if (err < 0) | ||
975 | goto err_out_unlock; | ||
976 | |||
977 | /* | ||
978 | * Do not place a null-byte before the final slash and the name. | ||
979 | */ | ||
980 | err--; | ||
981 | path += err; | ||
982 | total_len += err; | ||
983 | path_len -= err; | ||
984 | |||
985 | err = snprintf(path, path_len - 1, "/%s", str->name); | ||
986 | |||
987 | total_len += err + 1; /* 0 symbol */ | ||
988 | path_len -= err + 1; | ||
989 | |||
990 | cmd->cmd = NETFS_RENAME; | ||
991 | cmd->id = pi->ino; | ||
992 | cmd->start = parent->ino; | ||
993 | cmd->size = total_len; | ||
994 | |||
995 | netfs_convert_cmd(cmd); | ||
996 | |||
997 | netfs_trans_update(cmd, t, total_len); | ||
998 | |||
999 | return netfs_trans_finish(t, psb); | ||
1000 | |||
1001 | err_out_unlock: | ||
1002 | netfs_trans_free(t); | ||
1003 | return err; | ||
1004 | } | ||
1005 | |||
1006 | static int pohmelfs_rename(struct inode *old_dir, struct dentry *old_dentry, | ||
1007 | struct inode *new_dir, struct dentry *new_dentry) | ||
1008 | { | ||
1009 | struct inode *inode = old_dentry->d_inode; | ||
1010 | struct pohmelfs_inode *old_parent, *pi, *new_parent; | ||
1011 | struct qstr str = new_dentry->d_name; | ||
1012 | struct pohmelfs_name *n; | ||
1013 | unsigned int old_hash; | ||
1014 | int err = -ENOENT; | ||
1015 | |||
1016 | pi = POHMELFS_I(inode); | ||
1017 | old_parent = POHMELFS_I(old_dir); | ||
1018 | |||
1019 | if (new_dir) | ||
1020 | new_dir->i_sb->s_op->write_inode(new_dir, 0); | ||
1021 | |||
1022 | old_hash = jhash(old_dentry->d_name.name, old_dentry->d_name.len, 0); | ||
1023 | str.hash = jhash(new_dentry->d_name.name, new_dentry->d_name.len, 0); | ||
1024 | |||
1025 | str.len = new_dentry->d_name.len; | ||
1026 | str.name = new_dentry->d_name.name; | ||
1027 | str.hash = jhash(new_dentry->d_name.name, new_dentry->d_name.len, 0); | ||
1028 | |||
1029 | if (new_dir) { | ||
1030 | new_parent = POHMELFS_I(new_dir); | ||
1031 | err = -ENOTEMPTY; | ||
1032 | |||
1033 | if (S_ISDIR(inode->i_mode) && | ||
1034 | new_parent->total_len <= 3) | ||
1035 | goto err_out_exit; | ||
1036 | } else { | ||
1037 | new_parent = old_parent; | ||
1038 | } | ||
1039 | |||
1040 | dprintk("%s: ino: %llu, parent: %llu, name: '%s' -> parent: %llu, name: '%s', i_size: %llu.\n", | ||
1041 | __func__, pi->ino, old_parent->ino, old_dentry->d_name.name, | ||
1042 | new_parent->ino, new_dentry->d_name.name, inode->i_size); | ||
1043 | |||
1044 | if (test_bit(NETFS_INODE_REMOTE_SYNCED, &pi->state) && | ||
1045 | test_bit(NETFS_INODE_OWNED, &pi->state)) { | ||
1046 | err = pohmelfs_send_rename(pi, new_parent, &str); | ||
1047 | if (err) | ||
1048 | goto err_out_exit; | ||
1049 | } | ||
1050 | |||
1051 | n = pohmelfs_name_alloc(str.len + 1); | ||
1052 | if (!n) | ||
1053 | goto err_out_exit; | ||
1054 | |||
1055 | mutex_lock(&new_parent->offset_lock); | ||
1056 | n->ino = pi->ino; | ||
1057 | n->mode = inode->i_mode; | ||
1058 | n->len = str.len; | ||
1059 | n->hash = str.hash; | ||
1060 | sprintf(n->data, "%s", str.name); | ||
1061 | |||
1062 | err = pohmelfs_insert_name(new_parent, n); | ||
1063 | mutex_unlock(&new_parent->offset_lock); | ||
1064 | |||
1065 | if (err) | ||
1066 | goto err_out_exit; | ||
1067 | |||
1068 | mutex_lock(&old_parent->offset_lock); | ||
1069 | n = pohmelfs_search_hash(old_parent, old_hash); | ||
1070 | if (n) | ||
1071 | pohmelfs_name_del(old_parent, n); | ||
1072 | mutex_unlock(&old_parent->offset_lock); | ||
1073 | |||
1074 | mark_inode_dirty(inode); | ||
1075 | mark_inode_dirty(&new_parent->vfs_inode); | ||
1076 | |||
1077 | WARN_ON_ONCE(list_empty(&inode->i_dentry)); | ||
1078 | |||
1079 | return 0; | ||
1080 | |||
1081 | err_out_exit: | ||
1082 | |||
1083 | clear_bit(NETFS_INODE_REMOTE_SYNCED, &pi->state); | ||
1084 | |||
1085 | return err; | ||
1086 | } | ||
1087 | |||
1088 | /* | ||
1089 | * POHMELFS directory inode operations. | ||
1090 | */ | ||
1091 | const struct inode_operations pohmelfs_dir_inode_ops = { | ||
1092 | .link = pohmelfs_link, | ||
1093 | .symlink = pohmelfs_symlink, | ||
1094 | .unlink = pohmelfs_unlink, | ||
1095 | .mkdir = pohmelfs_mkdir, | ||
1096 | .rmdir = pohmelfs_rmdir, | ||
1097 | .create = pohmelfs_create, | ||
1098 | .lookup = pohmelfs_lookup, | ||
1099 | .setattr = pohmelfs_setattr, | ||
1100 | .rename = pohmelfs_rename, | ||
1101 | }; | ||