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 struct nameidata *nd) 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 hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, 154 fd.entrylength); 155 if (be16_to_cpu(entry.type) != HFSPLUS_FOLDER_THREAD) { 156 printk(KERN_ERR "hfs: bad catalog folder thread\n"); 157 err = -EIO; 158 goto out; 159 } 160 if (fd.entrylength < HFSPLUS_MIN_THREAD_SZ) { 161 printk(KERN_ERR "hfs: truncated catalog thread\n"); 162 err = -EIO; 163 goto out; 164 } 165 if (filldir(dirent, "..", 2, 1, 166 be32_to_cpu(entry.thread.parentID), DT_DIR)) 167 goto out; 168 filp->f_pos++; 169 /* fall through */ 170 default: 171 if (filp->f_pos >= inode->i_size) 172 goto out; 173 err = hfs_brec_goto(&fd, filp->f_pos - 1); 174 if (err) 175 goto out; 176 } 177 178 for (;;) { 179 if (be32_to_cpu(fd.key->cat.parent) != inode->i_ino) { 180 printk(KERN_ERR "hfs: walked past end of dir\n"); 181 err = -EIO; 182 goto out; 183 } 184 hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, 185 fd.entrylength); 186 type = be16_to_cpu(entry.type); 187 len = HFSPLUS_MAX_STRLEN; 188 err = hfsplus_uni2asc(sb, &fd.key->cat.name, strbuf, &len); 189 if (err) 190 goto out; 191 if (type == HFSPLUS_FOLDER) { 192 if (fd.entrylength < 193 sizeof(struct hfsplus_cat_folder)) { 194 printk(KERN_ERR "hfs: small dir entry\n"); 195 err = -EIO; 196 goto out; 197 } 198 if (HFSPLUS_SB(sb)->hidden_dir && 199 HFSPLUS_SB(sb)->hidden_dir->i_ino == 200 be32_to_cpu(entry.folder.id)) 201 goto next; 202 if (filldir(dirent, strbuf, len, filp->f_pos, 203 be32_to_cpu(entry.folder.id), DT_DIR)) 204 break; 205 } else if (type == HFSPLUS_FILE) { 206 if (fd.entrylength < sizeof(struct hfsplus_cat_file)) { 207 printk(KERN_ERR "hfs: small file entry\n"); 208 err = -EIO; 209 goto out; 210 } 211 if (filldir(dirent, strbuf, len, filp->f_pos, 212 be32_to_cpu(entry.file.id), DT_REG)) 213 break; 214 } else { 215 printk(KERN_ERR "hfs: bad catalog entry type\n"); 216 err = -EIO; 217 goto out; 218 } 219 next: 220 filp->f_pos++; 221 if (filp->f_pos >= inode->i_size) 222 goto out; 223 err = hfs_brec_goto(&fd, 1); 224 if (err) 225 goto out; 226 } 227 rd = filp->private_data; 228 if (!rd) { 229 rd = kmalloc(sizeof(struct hfsplus_readdir_data), GFP_KERNEL); 230 if (!rd) { 231 err = -ENOMEM; 232 goto out; 233 } 234 filp->private_data = rd; 235 rd->file = filp; 236 list_add(&rd->list, &HFSPLUS_I(inode)->open_dir_list); 237 } 238 memcpy(&rd->key, fd.key, sizeof(struct hfsplus_cat_key)); 239 out: 240 hfs_find_exit(&fd); 241 return err; 242 } 243 244 static int hfsplus_dir_release(struct inode *inode, struct file *file) 245 { 246 struct hfsplus_readdir_data *rd = file->private_data; 247 if (rd) { 248 mutex_lock(&inode->i_mutex); 249 list_del(&rd->list); 250 mutex_unlock(&inode->i_mutex); 251 kfree(rd); 252 } 253 return 0; 254 } 255 256 static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir, 257 struct dentry *dst_dentry) 258 { 259 struct hfsplus_sb_info *sbi = HFSPLUS_SB(dst_dir->i_sb); 260 struct inode *inode = src_dentry->d_inode; 261 struct inode *src_dir = src_dentry->d_parent->d_inode; 262 struct qstr str; 263 char name[32]; 264 u32 cnid, id; 265 int res; 266 267 if (HFSPLUS_IS_RSRC(inode)) 268 return -EPERM; 269 if (!S_ISREG(inode->i_mode)) 270 return -EPERM; 271 272 mutex_lock(&sbi->vh_mutex); 273 if (inode->i_ino == (u32)(unsigned long)src_dentry->d_fsdata) { 274 for (;;) { 275 get_random_bytes(&id, sizeof(cnid)); 276 id &= 0x3fffffff; 277 str.name = name; 278 str.len = sprintf(name, "iNode%d", id); 279 res = hfsplus_rename_cat(inode->i_ino, 280 src_dir, &src_dentry->d_name, 281 sbi->hidden_dir, &str); 282 if (!res) 283 break; 284 if (res != -EEXIST) 285 goto out; 286 } 287 HFSPLUS_I(inode)->linkid = id; 288 cnid = sbi->next_cnid++; 289 src_dentry->d_fsdata = (void *)(unsigned long)cnid; 290 res = hfsplus_create_cat(cnid, src_dir, 291 &src_dentry->d_name, inode); 292 if (res) 293 /* panic? */ 294 goto out; 295 sbi->file_count++; 296 } 297 cnid = sbi->next_cnid++; 298 res = hfsplus_create_cat(cnid, dst_dir, &dst_dentry->d_name, inode); 299 if (res) 300 goto out; 301 302 inc_nlink(inode); 303 hfsplus_instantiate(dst_dentry, inode, cnid); 304 ihold(inode); 305 inode->i_ctime = CURRENT_TIME_SEC; 306 mark_inode_dirty(inode); 307 sbi->file_count++; 308 dst_dir->i_sb->s_dirt = 1; 309 out: 310 mutex_unlock(&sbi->vh_mutex); 311 return res; 312 } 313 314 static int hfsplus_unlink(struct inode *dir, struct dentry *dentry) 315 { 316 struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb); 317 struct inode *inode = dentry->d_inode; 318 struct qstr str; 319 char name[32]; 320 u32 cnid; 321 int res; 322 323 if (HFSPLUS_IS_RSRC(inode)) 324 return -EPERM; 325 326 mutex_lock(&sbi->vh_mutex); 327 cnid = (u32)(unsigned long)dentry->d_fsdata; 328 if (inode->i_ino == cnid && 329 atomic_read(&HFSPLUS_I(inode)->opencnt)) { 330 str.name = name; 331 str.len = sprintf(name, "temp%lu", inode->i_ino); 332 res = hfsplus_rename_cat(inode->i_ino, 333 dir, &dentry->d_name, 334 sbi->hidden_dir, &str); 335 if (!res) { 336 inode->i_flags |= S_DEAD; 337 drop_nlink(inode); 338 } 339 goto out; 340 } 341 res = hfsplus_delete_cat(cnid, dir, &dentry->d_name); 342 if (res) 343 goto out; 344 345 if (inode->i_nlink > 0) 346 drop_nlink(inode); 347 if (inode->i_ino == cnid) 348 clear_nlink(inode); 349 if (!inode->i_nlink) { 350 if (inode->i_ino != cnid) { 351 sbi->file_count--; 352 if (!atomic_read(&HFSPLUS_I(inode)->opencnt)) { 353 res = hfsplus_delete_cat(inode->i_ino, 354 sbi->hidden_dir, 355 NULL); 356 if (!res) 357 hfsplus_delete_inode(inode); 358 } else 359 inode->i_flags |= S_DEAD; 360 } else 361 hfsplus_delete_inode(inode); 362 } else 363 sbi->file_count--; 364 inode->i_ctime = CURRENT_TIME_SEC; 365 mark_inode_dirty(inode); 366 out: 367 mutex_unlock(&sbi->vh_mutex); 368 return res; 369 } 370 371 static int hfsplus_rmdir(struct inode *dir, struct dentry *dentry) 372 { 373 struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb); 374 struct inode *inode = dentry->d_inode; 375 int res; 376 377 if (inode->i_size != 2) 378 return -ENOTEMPTY; 379 380 mutex_lock(&sbi->vh_mutex); 381 res = hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name); 382 if (res) 383 goto out; 384 clear_nlink(inode); 385 inode->i_ctime = CURRENT_TIME_SEC; 386 hfsplus_delete_inode(inode); 387 mark_inode_dirty(inode); 388 out: 389 mutex_unlock(&sbi->vh_mutex); 390 return res; 391 } 392 393 static int hfsplus_symlink(struct inode *dir, struct dentry *dentry, 394 const char *symname) 395 { 396 struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb); 397 struct inode *inode; 398 int res = -ENOSPC; 399 400 mutex_lock(&sbi->vh_mutex); 401 inode = hfsplus_new_inode(dir->i_sb, S_IFLNK | S_IRWXUGO); 402 if (!inode) 403 goto out; 404 405 res = page_symlink(inode, symname, strlen(symname) + 1); 406 if (res) 407 goto out_err; 408 409 res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode); 410 if (res) 411 goto out_err; 412 413 hfsplus_instantiate(dentry, inode, inode->i_ino); 414 mark_inode_dirty(inode); 415 goto out; 416 417 out_err: 418 inode->i_nlink = 0; 419 hfsplus_delete_inode(inode); 420 iput(inode); 421 out: 422 mutex_unlock(&sbi->vh_mutex); 423 return res; 424 } 425 426 static int hfsplus_mknod(struct inode *dir, struct dentry *dentry, 427 int mode, dev_t rdev) 428 { 429 struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb); 430 struct inode *inode; 431 int res = -ENOSPC; 432 433 mutex_lock(&sbi->vh_mutex); 434 inode = hfsplus_new_inode(dir->i_sb, mode); 435 if (!inode) 436 goto out; 437 438 if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)) 439 init_special_inode(inode, mode, rdev); 440 441 res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode); 442 if (res) { 443 inode->i_nlink = 0; 444 hfsplus_delete_inode(inode); 445 iput(inode); 446 goto out; 447 } 448 449 hfsplus_instantiate(dentry, inode, inode->i_ino); 450 mark_inode_dirty(inode); 451 out: 452 mutex_unlock(&sbi->vh_mutex); 453 return res; 454 } 455 456 static int hfsplus_create(struct inode *dir, struct dentry *dentry, int mode, 457 struct nameidata *nd) 458 { 459 return hfsplus_mknod(dir, dentry, mode, 0); 460 } 461 462 static int hfsplus_mkdir(struct inode *dir, struct dentry *dentry, int mode) 463 { 464 return hfsplus_mknod(dir, dentry, mode | S_IFDIR, 0); 465 } 466 467 static int hfsplus_rename(struct inode *old_dir, struct dentry *old_dentry, 468 struct inode *new_dir, struct dentry *new_dentry) 469 { 470 int res; 471 472 /* Unlink destination if it already exists */ 473 if (new_dentry->d_inode) { 474 if (S_ISDIR(new_dentry->d_inode->i_mode)) 475 res = hfsplus_rmdir(new_dir, new_dentry); 476 else 477 res = hfsplus_unlink(new_dir, new_dentry); 478 if (res) 479 return res; 480 } 481 482 res = hfsplus_rename_cat((u32)(unsigned long)old_dentry->d_fsdata, 483 old_dir, &old_dentry->d_name, 484 new_dir, &new_dentry->d_name); 485 if (!res) 486 new_dentry->d_fsdata = old_dentry->d_fsdata; 487 return res; 488 } 489 490 const struct inode_operations hfsplus_dir_inode_operations = { 491 .lookup = hfsplus_lookup, 492 .create = hfsplus_create, 493 .link = hfsplus_link, 494 .unlink = hfsplus_unlink, 495 .mkdir = hfsplus_mkdir, 496 .rmdir = hfsplus_rmdir, 497 .symlink = hfsplus_symlink, 498 .mknod = hfsplus_mknod, 499 .rename = hfsplus_rename, 500 }; 501 502 const struct file_operations hfsplus_dir_operations = { 503 .fsync = hfsplus_file_fsync, 504 .read = generic_read_dir, 505 .readdir = hfsplus_readdir, 506 .unlocked_ioctl = hfsplus_ioctl, 507 .llseek = generic_file_llseek, 508 .release = hfsplus_dir_release, 509 }; 510