1/* 2 * linux/fs/hfsplus/dir.c 3 * 4 * Copyright (C) 2001 5 * Brad Boyer (flar@allandria.com) 6 * (C) 2003 Ardis Technologies <roman@ardistech.com> 7 * 8 * Handling of directories 9 */ 10 11#include <linux/errno.h> 12#include <linux/fs.h> 13#include <linux/slab.h> 14#include <linux/random.h> 15 16#include "hfsplus_fs.h" 17#include "hfsplus_raw.h" 18 19static inline void hfsplus_instantiate(struct dentry *dentry, 20 struct inode *inode, u32 cnid) 21{ 22 dentry->d_fsdata = (void *)(unsigned long)cnid; 23 d_instantiate(dentry, inode); 24} 25 26/* Find the entry inside dir named dentry->d_name */ 27static struct dentry *hfsplus_lookup(struct inode *dir, struct dentry *dentry, 28 struct nameidata *nd) 29{ 30 struct inode *inode = NULL; 31 struct hfs_find_data fd; 32 struct super_block *sb; 33 hfsplus_handle_t hfsplus_handle; 34 hfsplus_cat_entry entry; 35 int err; 36 u32 cnid, linkid = 0; 37 u16 type; 38 39 if (hfsplus_journal_start(__FUNCTION__, dir->i_sb, &hfsplus_handle)) 40 return NULL; 41 sb = dir->i_sb; 42 dentry->d_fsdata = NULL; 43 hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd); 44 hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, &dentry->d_name); 45again: 46 err = hfs_brec_read(&hfsplus_handle, &fd, &entry, sizeof(entry)); 47 if (err) { 48 if (err == -ENOENT) { 49 hfs_find_exit(&hfsplus_handle, &fd); 50 /* No such entry */ 51 inode = NULL; 52 goto out; 53 } 54 goto fail; 55 } 56 type = be16_to_cpu(entry.type); 57 if (type == HFSPLUS_FOLDER) { 58 if (fd.entrylength < sizeof(struct hfsplus_cat_folder)) { 59 err = -EIO; 60 goto fail; 61 } 62 cnid = be32_to_cpu(entry.folder.id); 63 dentry->d_fsdata = (void *)(unsigned long)cnid; 64 } else if (type == HFSPLUS_FILE) { 65 if (fd.entrylength < sizeof(struct hfsplus_cat_file)) { 66 err = -EIO; 67 goto fail; 68 } 69 cnid = be32_to_cpu(entry.file.id); 70 if (entry.file.user_info.fdType == cpu_to_be32(HFSP_HARDLINK_TYPE) && 71 entry.file.user_info.fdCreator == cpu_to_be32(HFSP_HFSPLUS_CREATOR) && 72 (entry.file.create_date == HFSPLUS_I(HFSPLUS_SB(sb).hidden_dir).create_date || 73 entry.file.create_date == HFSPLUS_I(sb->s_root->d_inode).create_date) && 74 HFSPLUS_SB(sb).hidden_dir) { 75 struct qstr str; 76 char name[32]; 77 78 if (dentry->d_fsdata) { 79 /* 80 * We found a link pointing to another link, 81 * so ignore it and treat it as regular file. 82 */ 83 cnid = (unsigned long)dentry->d_fsdata; 84 linkid = 0; 85 } else { 86 dentry->d_fsdata = (void *)(unsigned long)cnid; 87 linkid = be32_to_cpu(entry.file.permissions.dev); 88 str.len = sprintf(name, "iNode%d", linkid); 89 str.name = name; 90 hfsplus_cat_build_key(sb, fd.search_key, HFSPLUS_SB(sb).hidden_dir->i_ino, &str); 91 goto again; 92 } 93 } else if (!dentry->d_fsdata) 94 dentry->d_fsdata = (void *)(unsigned long)cnid; 95 } else { 96 printk(KERN_ERR "hfs: invalid catalog entry type in lookup\n"); 97 err = -EIO; 98 goto fail; 99 } 100 hfs_find_exit(&hfsplus_handle, &fd); 101 inode = iget(dir->i_sb, cnid); 102 if (!inode) 103 return ERR_PTR(-EACCES); 104 if (S_ISREG(inode->i_mode)) 105 HFSPLUS_I(inode).dev = linkid; 106out: 107 d_add(dentry, inode); 108 hfsplus_journal_stop(&hfsplus_handle); 109 return NULL; 110fail: 111 hfs_find_exit(&hfsplus_handle, &fd); 112 hfsplus_journal_stop(&hfsplus_handle); 113 return ERR_PTR(err); 114} 115 116static int hfsplus_readdir(struct file *filp, void *dirent, filldir_t filldir) 117{ 118 struct inode *inode = filp->f_path.dentry->d_inode; 119 struct super_block *sb = inode->i_sb; 120 int len, err; 121 char strbuf[HFSPLUS_MAX_STRLEN + 1]; 122 hfsplus_cat_entry entry; 123 struct hfs_find_data fd; 124 struct hfsplus_readdir_data *rd; 125 hfsplus_handle_t hfsplus_handle; 126 u16 type; 127 128 if (filp->f_pos >= inode->i_size) 129 return 0; 130 131 if ((err = hfsplus_journal_start(__FUNCTION__, sb, &hfsplus_handle))) 132 return err; 133 hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd); 134 hfsplus_cat_build_key(sb, fd.search_key, inode->i_ino, NULL); 135 err = hfs_brec_find(&hfsplus_handle, &fd); 136 if (err) 137 goto out; 138 139 switch ((u32)filp->f_pos) { 140 case 0: 141 /* This is completely artificial... */ 142 if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR)) 143 goto out; 144 filp->f_pos++; 145 /* fall through */ 146 case 1: 147 hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength); 148 if (be16_to_cpu(entry.type) != HFSPLUS_FOLDER_THREAD) { 149 printk(KERN_ERR "hfs: bad catalog folder thread\n"); 150 err = -EIO; 151 goto out; 152 } 153 if (fd.entrylength < HFSPLUS_MIN_THREAD_SZ) { 154 printk(KERN_ERR "hfs: truncated catalog thread\n"); 155 err = -EIO; 156 goto out; 157 } 158 if (filldir(dirent, "..", 2, 1, 159 be32_to_cpu(entry.thread.parentID), DT_DIR)) 160 goto out; 161 filp->f_pos++; 162 /* fall through */ 163 default: 164 if (filp->f_pos >= inode->i_size) 165 goto out; 166 err = hfs_brec_goto(&hfsplus_handle, &fd, filp->f_pos - 1); 167 if (err) 168 goto out; 169 } 170 171 for (;;) { 172 if (be32_to_cpu(fd.key->cat.parent) != inode->i_ino) { 173 printk(KERN_ERR "hfs: walked past end of dir\n"); 174 err = -EIO; 175 goto out; 176 } 177 hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength); 178 type = be16_to_cpu(entry.type); 179 len = HFSPLUS_MAX_STRLEN; 180 err = hfsplus_uni2asc(sb, &fd.key->cat.name, strbuf, &len); 181 if (err) 182 goto out; 183 if (type == HFSPLUS_FOLDER) { 184 if (fd.entrylength < sizeof(struct hfsplus_cat_folder)) { 185 printk(KERN_ERR "hfs: small dir entry\n"); 186 err = -EIO; 187 goto out; 188 } 189 if (HFSPLUS_SB(sb).hidden_dir && 190 HFSPLUS_SB(sb).hidden_dir->i_ino == be32_to_cpu(entry.folder.id)) 191 goto next; 192 if (filldir(dirent, strbuf, len, filp->f_pos, 193 be32_to_cpu(entry.folder.id), DT_DIR)) 194 break; 195 } else if (type == HFSPLUS_FILE) { 196 if (fd.entrylength < sizeof(struct hfsplus_cat_file)) { 197 printk(KERN_ERR "hfs: small file entry\n"); 198 err = -EIO; 199 goto out; 200 } 201 if (filldir(dirent, strbuf, len, filp->f_pos, 202 be32_to_cpu(entry.file.id), DT_REG)) 203 break; 204 } else { 205 printk(KERN_ERR "hfs: bad catalog entry type\n"); 206 err = -EIO; 207 goto out; 208 } 209 next: 210 filp->f_pos++; 211 if (filp->f_pos >= inode->i_size) 212 goto out; 213 err = hfs_brec_goto(&hfsplus_handle, &fd, 1); 214 if (err) 215 goto out; 216 } 217 rd = filp->private_data; 218 if (!rd) { 219 rd = kmalloc(sizeof(struct hfsplus_readdir_data), GFP_KERNEL); 220 if (!rd) { 221 err = -ENOMEM; 222 goto out; 223 } 224 filp->private_data = rd; 225 rd->file = filp; 226 list_add(&rd->list, &HFSPLUS_I(inode).open_dir_list); 227 } 228 memcpy(&rd->key, fd.key, sizeof(struct hfsplus_cat_key)); 229out: 230 hfs_find_exit(&hfsplus_handle, &fd); 231 hfsplus_journal_stop(&hfsplus_handle); 232 return err; 233} 234 235static int hfsplus_dir_release(struct inode *inode, struct file *file) 236{ 237 struct hfsplus_readdir_data *rd = file->private_data; 238 if (rd) { 239 list_del(&rd->list); 240 kfree(rd); 241 } 242 return 0; 243} 244 245static int hfsplus_create(struct inode *dir, struct dentry *dentry, int mode, 246 struct nameidata *nd) 247{ 248 struct inode *inode; 249 int res; 250 hfsplus_handle_t hfsplus_handle; 251 252 if ((res = hfsplus_journal_start(__FUNCTION__, dir->i_sb, &hfsplus_handle))) 253 return res; 254 255 inode = hfsplus_new_inode(&hfsplus_handle, dir->i_sb, mode); 256 if (!inode) { 257 hfsplus_journal_stop(&hfsplus_handle); 258 return -ENOSPC; 259 } 260 261 res = hfsplus_create_cat(&hfsplus_handle, inode->i_ino, dir, &dentry->d_name, inode); 262 if (res) { 263 inode->i_nlink = 0; 264 hfsplus_delete_inode(&hfsplus_handle, inode); 265 iput(inode); 266 hfsplus_journal_stop(&hfsplus_handle); 267 return res; 268 } 269 hfsplus_instantiate(dentry, inode, inode->i_ino); 270 res = hfsplus_journalled_mark_inode_dirty(__FUNCTION__, &hfsplus_handle, inode); 271 hfsplus_journal_stop(&hfsplus_handle); 272 return res; 273} 274 275static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir, 276 struct dentry *dst_dentry) 277{ 278 struct super_block *sb = dst_dir->i_sb; 279 struct inode *inode = src_dentry->d_inode; 280 struct inode *src_dir = src_dentry->d_parent->d_inode; 281 hfsplus_handle_t hfsplus_handle; 282 struct qstr str; 283 char name[32]; 284 u32 cnid, id; 285 int res; 286 287 if (HFSPLUS_IS_RSRC(inode)) 288 return -EPERM; 289 290 if ((res = hfsplus_journal_start(__FUNCTION__, dst_dir->i_sb, &hfsplus_handle))) 291 return res; 292 293 if (inode->i_ino == (u32)(unsigned long)src_dentry->d_fsdata) { 294 for (;;) { 295 get_random_bytes(&id, sizeof(cnid)); 296 id &= 0x3fffffff; 297 str.name = name; 298 str.len = sprintf(name, "iNode%d", id); 299 res = hfsplus_rename_cat(&hfsplus_handle, inode->i_ino, 300 src_dir, &src_dentry->d_name, 301 HFSPLUS_SB(sb).hidden_dir, &str); 302 if (!res) 303 break; 304 if (res != -EEXIST) { 305 hfsplus_journal_stop(&hfsplus_handle); 306 return res; 307 } 308 } 309 HFSPLUS_I(inode).dev = id; 310 cnid = HFSPLUS_SB(sb).next_cnid++; 311 src_dentry->d_fsdata = (void *)(unsigned long)cnid; 312 res = hfsplus_create_cat(&hfsplus_handle, cnid, src_dir, &src_dentry->d_name, inode); 313 if (res) { 314 /* panic? */ 315 hfsplus_journal_stop(&hfsplus_handle); 316 return res; 317 } 318 HFSPLUS_SB(sb).file_count++; 319 } 320 cnid = HFSPLUS_SB(sb).next_cnid++; 321 res = hfsplus_create_cat(&hfsplus_handle, cnid, dst_dir, &dst_dentry->d_name, inode); 322 if (res) { 323 hfsplus_journal_stop(&hfsplus_handle); 324 return res; 325 } 326 327 inc_nlink(inode); 328 hfsplus_instantiate(dst_dentry, inode, cnid); 329 atomic_inc(&inode->i_count); 330 inode->i_ctime = CURRENT_TIME_SEC; 331 res = hfsplus_journalled_mark_inode_dirty(__FUNCTION__, &hfsplus_handle, inode); 332 HFSPLUS_SB(sb).file_count++; 333 sb->s_dirt = 1; 334 335 hfsplus_journal_stop(&hfsplus_handle); 336 return res; 337} 338 339static int hfsplus_unlink(struct inode *dir, struct dentry *dentry) 340{ 341 struct super_block *sb = dir->i_sb; 342 struct inode *inode = dentry->d_inode; 343 struct qstr str; 344 char name[32]; 345 u32 cnid; 346 int res; 347 hfsplus_handle_t hfsplus_handle; 348 349 if (HFSPLUS_IS_RSRC(inode)) 350 return -EPERM; 351 352 if ((res = hfsplus_journal_start(__FUNCTION__, dir->i_sb, &hfsplus_handle))) 353 return res; 354 355 cnid = (u32)(unsigned long)dentry->d_fsdata; 356 if (inode->i_ino == cnid && 357 atomic_read(&HFSPLUS_I(inode).opencnt)) { 358 str.name = name; 359 str.len = sprintf(name, "temp%lu", inode->i_ino); 360 res = hfsplus_rename_cat(&hfsplus_handle, inode->i_ino, 361 dir, &dentry->d_name, 362 HFSPLUS_SB(sb).hidden_dir, &str); 363 if (!res) 364 inode->i_flags |= S_DEAD; 365 hfsplus_journal_stop(&hfsplus_handle); 366 return res; 367 } 368 res = hfsplus_delete_cat(&hfsplus_handle, cnid, dir, &dentry->d_name); 369 if (res) { 370 hfsplus_journal_stop(&hfsplus_handle); 371 return res; 372 } 373 if (inode->i_nlink > 0) 374 drop_nlink(inode); 375 hfsplus_delete_inode(&hfsplus_handle, inode); 376 if (inode->i_ino != cnid && !inode->i_nlink) { 377 if (!atomic_read(&HFSPLUS_I(inode).opencnt)) { 378 res = hfsplus_delete_cat(&hfsplus_handle, inode->i_ino, HFSPLUS_SB(sb).hidden_dir, NULL); 379 if (!res) 380 hfsplus_delete_inode(&hfsplus_handle, inode); 381 } else 382 inode->i_flags |= S_DEAD; 383 } else 384 clear_nlink(inode); 385 inode->i_ctime = CURRENT_TIME_SEC; 386 res = hfsplus_journalled_mark_inode_dirty(__FUNCTION__, &hfsplus_handle, inode); 387 388 hfsplus_journal_stop(&hfsplus_handle); 389 return res; 390} 391 392static int hfsplus_mkdir(struct inode *dir, struct dentry *dentry, int mode) 393{ 394 struct inode *inode; 395 hfsplus_handle_t hfsplus_handle; 396 int res; 397 398 if ((res = hfsplus_journal_start(__FUNCTION__, dir->i_sb, &hfsplus_handle))) 399 return res; 400 401 inode = hfsplus_new_inode(&hfsplus_handle, dir->i_sb, S_IFDIR | mode); 402 if (!inode) { 403 hfsplus_journal_stop(&hfsplus_handle); 404 return -ENOSPC; 405 } 406 407 res = hfsplus_create_cat(&hfsplus_handle, inode->i_ino, dir, &dentry->d_name, inode); 408 if (res) { 409 inode->i_nlink = 0; 410 hfsplus_delete_inode(&hfsplus_handle, inode); 411 iput(inode); 412 hfsplus_journal_stop(&hfsplus_handle); 413 return res; 414 } 415 hfsplus_instantiate(dentry, inode, inode->i_ino); 416 res = hfsplus_journalled_mark_inode_dirty(__FUNCTION__, &hfsplus_handle, inode); 417 hfsplus_journal_stop(&hfsplus_handle); 418 return res; 419} 420 421static int hfsplus_rmdir(struct inode *dir, struct dentry *dentry) 422{ 423 struct inode *inode; 424 hfsplus_handle_t hfsplus_handle; 425 int res; 426 427 inode = dentry->d_inode; 428 if (inode->i_size != 2) 429 return -ENOTEMPTY; 430 431 if ((res = hfsplus_journal_start(__FUNCTION__, dir->i_sb, &hfsplus_handle))) 432 return res; 433 434 res = hfsplus_delete_cat(&hfsplus_handle, inode->i_ino, dir, &dentry->d_name); 435 if (res) { 436 hfsplus_journal_stop(&hfsplus_handle); 437 return res; 438 } 439 inode->i_nlink = 0; 440 inode->i_ctime = CURRENT_TIME_SEC; 441 hfsplus_delete_inode(&hfsplus_handle, inode); 442 res = hfsplus_journalled_mark_inode_dirty(__FUNCTION__, &hfsplus_handle, inode); 443 hfsplus_journal_stop(&hfsplus_handle); 444 return res; 445} 446 447static int hfsplus_symlink(struct inode *dir, struct dentry *dentry, 448 const char *symname) 449{ 450 struct super_block *sb; 451 struct inode *inode; 452 hfsplus_handle_t hfsplus_handle; 453 int res; 454 455 if ((res = hfsplus_journal_start(__FUNCTION__, dir->i_sb, &hfsplus_handle))) 456 return res; 457 458 sb = dir->i_sb; 459 inode = hfsplus_new_inode(&hfsplus_handle, sb, S_IFLNK | S_IRWXUGO); 460 if (!inode) { 461 hfsplus_journal_stop(&hfsplus_handle); 462 return -ENOSPC; 463 } 464 465 res = page_symlink(inode, symname, strlen(symname) + 1); 466 if (res) { 467 inode->i_nlink = 0; 468 hfsplus_delete_inode(&hfsplus_handle, inode); 469 iput(inode); 470 hfsplus_journal_stop(&hfsplus_handle); 471 return res; 472 } 473 474 if ((res = hfsplus_journalled_mark_inode_dirty(__FUNCTION__, &hfsplus_handle, inode))) 475 goto symlink_out; 476 res = hfsplus_create_cat(&hfsplus_handle, inode->i_ino, dir, &dentry->d_name, inode); 477 478 if (!res) { 479 hfsplus_instantiate(dentry, inode, inode->i_ino); 480 res = hfsplus_journalled_mark_inode_dirty(__FUNCTION__, &hfsplus_handle, inode); 481 } 482 483symlink_out: 484 hfsplus_journal_stop(&hfsplus_handle); 485 return res; 486} 487 488static int hfsplus_mknod(struct inode *dir, struct dentry *dentry, 489 int mode, dev_t rdev) 490{ 491 struct super_block *sb; 492 struct inode *inode; 493 hfsplus_handle_t hfsplus_handle; 494 int res; 495 496 if ((res = hfsplus_journal_start(__FUNCTION__, dir->i_sb, &hfsplus_handle))) 497 return res; 498 499 sb = dir->i_sb; 500 inode = hfsplus_new_inode(&hfsplus_handle, sb, mode); 501 if (!inode) { 502 hfsplus_journal_stop(&hfsplus_handle); 503 return -ENOSPC; 504 } 505 506 res = hfsplus_create_cat(&hfsplus_handle, inode->i_ino, dir, &dentry->d_name, inode); 507 if (res) { 508 inode->i_nlink = 0; 509 hfsplus_delete_inode(&hfsplus_handle, inode); 510 iput(inode); 511 hfsplus_journal_stop(&hfsplus_handle); 512 return res; 513 } 514 init_special_inode(inode, mode, rdev); 515 hfsplus_instantiate(dentry, inode, inode->i_ino); 516 res = hfsplus_journalled_mark_inode_dirty(__FUNCTION__, &hfsplus_handle, inode); 517 518 hfsplus_journal_stop(&hfsplus_handle); 519 return 0; 520} 521 522static int hfsplus_rename(struct inode *old_dir, struct dentry *old_dentry, 523 struct inode *new_dir, struct dentry *new_dentry) 524{ 525 int res; 526 hfsplus_handle_t hfsplus_handle; 527 528 /* Unlink destination if it already exists */ 529 if (new_dentry->d_inode) { 530 res = hfsplus_unlink(new_dir, new_dentry); 531 if (res) 532 return res; 533 } 534 535 if ((res = hfsplus_journal_start(__FUNCTION__, old_dir->i_sb, &hfsplus_handle))) 536 return res; 537 538 res = hfsplus_rename_cat(&hfsplus_handle, (u32)(unsigned long)old_dentry->d_fsdata, 539 old_dir, &old_dentry->d_name, 540 new_dir, &new_dentry->d_name); 541 if (!res) 542 new_dentry->d_fsdata = old_dentry->d_fsdata; 543 544 hfsplus_journal_stop(&hfsplus_handle); 545 return res; 546} 547 548const struct inode_operations hfsplus_dir_inode_operations = { 549 .lookup = hfsplus_lookup, 550 .create = hfsplus_create, 551 .link = hfsplus_link, 552 .unlink = hfsplus_unlink, 553 .mkdir = hfsplus_mkdir, 554 .rmdir = hfsplus_rmdir, 555 .symlink = hfsplus_symlink, 556 .mknod = hfsplus_mknod, 557 .rename = hfsplus_rename, 558}; 559 560const struct file_operations hfsplus_dir_operations = { 561 .read = generic_read_dir, 562 .readdir = hfsplus_readdir, 563 .ioctl = hfsplus_ioctl, 564 .llseek = generic_file_llseek, 565 .release = hfsplus_dir_release, 566}; 567