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