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