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