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