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 | }; | ||
