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