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