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