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