0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012 #include <linux/errno.h>
0013 #include <linux/fs.h>
0014 #include <linux/slab.h>
0015 #include <linux/random.h>
0016 #include <linux/nls.h>
0017
0018 #include "hfsplus_fs.h"
0019 #include "hfsplus_raw.h"
0020 #include "xattr.h"
0021
0022 static inline void hfsplus_instantiate(struct dentry *dentry,
0023 struct inode *inode, u32 cnid)
0024 {
0025 dentry->d_fsdata = (void *)(unsigned long)cnid;
0026 d_instantiate(dentry, inode);
0027 }
0028
0029
0030 static struct dentry *hfsplus_lookup(struct inode *dir, struct dentry *dentry,
0031 unsigned int flags)
0032 {
0033 struct inode *inode = NULL;
0034 struct hfs_find_data fd;
0035 struct super_block *sb;
0036 hfsplus_cat_entry entry;
0037 int err;
0038 u32 cnid, linkid = 0;
0039 u16 type;
0040
0041 sb = dir->i_sb;
0042
0043 dentry->d_fsdata = NULL;
0044 err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
0045 if (err)
0046 return ERR_PTR(err);
0047 err = hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino,
0048 &dentry->d_name);
0049 if (unlikely(err < 0))
0050 goto fail;
0051 again:
0052 err = hfs_brec_read(&fd, &entry, sizeof(entry));
0053 if (err) {
0054 if (err == -ENOENT) {
0055 hfs_find_exit(&fd);
0056
0057 inode = NULL;
0058 goto out;
0059 }
0060 goto fail;
0061 }
0062 type = be16_to_cpu(entry.type);
0063 if (type == HFSPLUS_FOLDER) {
0064 if (fd.entrylength < sizeof(struct hfsplus_cat_folder)) {
0065 err = -EIO;
0066 goto fail;
0067 }
0068 cnid = be32_to_cpu(entry.folder.id);
0069 dentry->d_fsdata = (void *)(unsigned long)cnid;
0070 } else if (type == HFSPLUS_FILE) {
0071 if (fd.entrylength < sizeof(struct hfsplus_cat_file)) {
0072 err = -EIO;
0073 goto fail;
0074 }
0075 cnid = be32_to_cpu(entry.file.id);
0076 if (entry.file.user_info.fdType ==
0077 cpu_to_be32(HFSP_HARDLINK_TYPE) &&
0078 entry.file.user_info.fdCreator ==
0079 cpu_to_be32(HFSP_HFSPLUS_CREATOR) &&
0080 HFSPLUS_SB(sb)->hidden_dir &&
0081 (entry.file.create_date ==
0082 HFSPLUS_I(HFSPLUS_SB(sb)->hidden_dir)->
0083 create_date ||
0084 entry.file.create_date ==
0085 HFSPLUS_I(d_inode(sb->s_root))->
0086 create_date)) {
0087 struct qstr str;
0088 char name[32];
0089
0090 if (dentry->d_fsdata) {
0091
0092
0093
0094
0095 cnid = (unsigned long)dentry->d_fsdata;
0096 linkid = 0;
0097 } else {
0098 dentry->d_fsdata = (void *)(unsigned long)cnid;
0099 linkid =
0100 be32_to_cpu(entry.file.permissions.dev);
0101 str.len = sprintf(name, "iNode%d", linkid);
0102 str.name = name;
0103 err = hfsplus_cat_build_key(sb, fd.search_key,
0104 HFSPLUS_SB(sb)->hidden_dir->i_ino,
0105 &str);
0106 if (unlikely(err < 0))
0107 goto fail;
0108 goto again;
0109 }
0110 } else if (!dentry->d_fsdata)
0111 dentry->d_fsdata = (void *)(unsigned long)cnid;
0112 } else {
0113 pr_err("invalid catalog entry type in lookup\n");
0114 err = -EIO;
0115 goto fail;
0116 }
0117 hfs_find_exit(&fd);
0118 inode = hfsplus_iget(dir->i_sb, cnid);
0119 if (IS_ERR(inode))
0120 return ERR_CAST(inode);
0121 if (S_ISREG(inode->i_mode))
0122 HFSPLUS_I(inode)->linkid = linkid;
0123 out:
0124 return d_splice_alias(inode, dentry);
0125 fail:
0126 hfs_find_exit(&fd);
0127 return ERR_PTR(err);
0128 }
0129
0130 static int hfsplus_readdir(struct file *file, struct dir_context *ctx)
0131 {
0132 struct inode *inode = file_inode(file);
0133 struct super_block *sb = inode->i_sb;
0134 int len, err;
0135 char *strbuf;
0136 hfsplus_cat_entry entry;
0137 struct hfs_find_data fd;
0138 struct hfsplus_readdir_data *rd;
0139 u16 type;
0140
0141 if (file->f_pos >= inode->i_size)
0142 return 0;
0143
0144 err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
0145 if (err)
0146 return err;
0147 strbuf = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_MAX_STRLEN + 1, GFP_KERNEL);
0148 if (!strbuf) {
0149 err = -ENOMEM;
0150 goto out;
0151 }
0152 hfsplus_cat_build_key_with_cnid(sb, fd.search_key, inode->i_ino);
0153 err = hfs_brec_find(&fd, hfs_find_rec_by_key);
0154 if (err)
0155 goto out;
0156
0157 if (ctx->pos == 0) {
0158
0159 if (!dir_emit_dot(file, ctx))
0160 goto out;
0161 ctx->pos = 1;
0162 }
0163 if (ctx->pos == 1) {
0164 if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) {
0165 err = -EIO;
0166 goto out;
0167 }
0168
0169 hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
0170 fd.entrylength);
0171 if (be16_to_cpu(entry.type) != HFSPLUS_FOLDER_THREAD) {
0172 pr_err("bad catalog folder thread\n");
0173 err = -EIO;
0174 goto out;
0175 }
0176 if (fd.entrylength < HFSPLUS_MIN_THREAD_SZ) {
0177 pr_err("truncated catalog thread\n");
0178 err = -EIO;
0179 goto out;
0180 }
0181 if (!dir_emit(ctx, "..", 2,
0182 be32_to_cpu(entry.thread.parentID), DT_DIR))
0183 goto out;
0184 ctx->pos = 2;
0185 }
0186 if (ctx->pos >= inode->i_size)
0187 goto out;
0188 err = hfs_brec_goto(&fd, ctx->pos - 1);
0189 if (err)
0190 goto out;
0191 for (;;) {
0192 if (be32_to_cpu(fd.key->cat.parent) != inode->i_ino) {
0193 pr_err("walked past end of dir\n");
0194 err = -EIO;
0195 goto out;
0196 }
0197
0198 if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) {
0199 err = -EIO;
0200 goto out;
0201 }
0202
0203 hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
0204 fd.entrylength);
0205 type = be16_to_cpu(entry.type);
0206 len = NLS_MAX_CHARSET_SIZE * HFSPLUS_MAX_STRLEN;
0207 err = hfsplus_uni2asc(sb, &fd.key->cat.name, strbuf, &len);
0208 if (err)
0209 goto out;
0210 if (type == HFSPLUS_FOLDER) {
0211 if (fd.entrylength <
0212 sizeof(struct hfsplus_cat_folder)) {
0213 pr_err("small dir entry\n");
0214 err = -EIO;
0215 goto out;
0216 }
0217 if (HFSPLUS_SB(sb)->hidden_dir &&
0218 HFSPLUS_SB(sb)->hidden_dir->i_ino ==
0219 be32_to_cpu(entry.folder.id))
0220 goto next;
0221 if (!dir_emit(ctx, strbuf, len,
0222 be32_to_cpu(entry.folder.id), DT_DIR))
0223 break;
0224 } else if (type == HFSPLUS_FILE) {
0225 u16 mode;
0226 unsigned type = DT_UNKNOWN;
0227
0228 if (fd.entrylength < sizeof(struct hfsplus_cat_file)) {
0229 pr_err("small file entry\n");
0230 err = -EIO;
0231 goto out;
0232 }
0233
0234 mode = be16_to_cpu(entry.file.permissions.mode);
0235 if (S_ISREG(mode))
0236 type = DT_REG;
0237 else if (S_ISLNK(mode))
0238 type = DT_LNK;
0239 else if (S_ISFIFO(mode))
0240 type = DT_FIFO;
0241 else if (S_ISCHR(mode))
0242 type = DT_CHR;
0243 else if (S_ISBLK(mode))
0244 type = DT_BLK;
0245 else if (S_ISSOCK(mode))
0246 type = DT_SOCK;
0247
0248 if (!dir_emit(ctx, strbuf, len,
0249 be32_to_cpu(entry.file.id), type))
0250 break;
0251 } else {
0252 pr_err("bad catalog entry type\n");
0253 err = -EIO;
0254 goto out;
0255 }
0256 next:
0257 ctx->pos++;
0258 if (ctx->pos >= inode->i_size)
0259 goto out;
0260 err = hfs_brec_goto(&fd, 1);
0261 if (err)
0262 goto out;
0263 }
0264 rd = file->private_data;
0265 if (!rd) {
0266 rd = kmalloc(sizeof(struct hfsplus_readdir_data), GFP_KERNEL);
0267 if (!rd) {
0268 err = -ENOMEM;
0269 goto out;
0270 }
0271 file->private_data = rd;
0272 rd->file = file;
0273 spin_lock(&HFSPLUS_I(inode)->open_dir_lock);
0274 list_add(&rd->list, &HFSPLUS_I(inode)->open_dir_list);
0275 spin_unlock(&HFSPLUS_I(inode)->open_dir_lock);
0276 }
0277
0278
0279
0280
0281 memcpy(&rd->key, fd.key, sizeof(struct hfsplus_cat_key));
0282 out:
0283 kfree(strbuf);
0284 hfs_find_exit(&fd);
0285 return err;
0286 }
0287
0288 static int hfsplus_dir_release(struct inode *inode, struct file *file)
0289 {
0290 struct hfsplus_readdir_data *rd = file->private_data;
0291 if (rd) {
0292 spin_lock(&HFSPLUS_I(inode)->open_dir_lock);
0293 list_del(&rd->list);
0294 spin_unlock(&HFSPLUS_I(inode)->open_dir_lock);
0295 kfree(rd);
0296 }
0297 return 0;
0298 }
0299
0300 static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir,
0301 struct dentry *dst_dentry)
0302 {
0303 struct hfsplus_sb_info *sbi = HFSPLUS_SB(dst_dir->i_sb);
0304 struct inode *inode = d_inode(src_dentry);
0305 struct inode *src_dir = d_inode(src_dentry->d_parent);
0306 struct qstr str;
0307 char name[32];
0308 u32 cnid, id;
0309 int res;
0310
0311 if (HFSPLUS_IS_RSRC(inode))
0312 return -EPERM;
0313 if (!S_ISREG(inode->i_mode))
0314 return -EPERM;
0315
0316 mutex_lock(&sbi->vh_mutex);
0317 if (inode->i_ino == (u32)(unsigned long)src_dentry->d_fsdata) {
0318 for (;;) {
0319 get_random_bytes(&id, sizeof(cnid));
0320 id &= 0x3fffffff;
0321 str.name = name;
0322 str.len = sprintf(name, "iNode%d", id);
0323 res = hfsplus_rename_cat(inode->i_ino,
0324 src_dir, &src_dentry->d_name,
0325 sbi->hidden_dir, &str);
0326 if (!res)
0327 break;
0328 if (res != -EEXIST)
0329 goto out;
0330 }
0331 HFSPLUS_I(inode)->linkid = id;
0332 cnid = sbi->next_cnid++;
0333 src_dentry->d_fsdata = (void *)(unsigned long)cnid;
0334 res = hfsplus_create_cat(cnid, src_dir,
0335 &src_dentry->d_name, inode);
0336 if (res)
0337
0338 goto out;
0339 sbi->file_count++;
0340 }
0341 cnid = sbi->next_cnid++;
0342 res = hfsplus_create_cat(cnid, dst_dir, &dst_dentry->d_name, inode);
0343 if (res)
0344 goto out;
0345
0346 inc_nlink(inode);
0347 hfsplus_instantiate(dst_dentry, inode, cnid);
0348 ihold(inode);
0349 inode->i_ctime = current_time(inode);
0350 mark_inode_dirty(inode);
0351 sbi->file_count++;
0352 hfsplus_mark_mdb_dirty(dst_dir->i_sb);
0353 out:
0354 mutex_unlock(&sbi->vh_mutex);
0355 return res;
0356 }
0357
0358 static int hfsplus_unlink(struct inode *dir, struct dentry *dentry)
0359 {
0360 struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
0361 struct inode *inode = d_inode(dentry);
0362 struct qstr str;
0363 char name[32];
0364 u32 cnid;
0365 int res;
0366
0367 if (HFSPLUS_IS_RSRC(inode))
0368 return -EPERM;
0369
0370 mutex_lock(&sbi->vh_mutex);
0371 cnid = (u32)(unsigned long)dentry->d_fsdata;
0372 if (inode->i_ino == cnid &&
0373 atomic_read(&HFSPLUS_I(inode)->opencnt)) {
0374 str.name = name;
0375 str.len = sprintf(name, "temp%lu", inode->i_ino);
0376 res = hfsplus_rename_cat(inode->i_ino,
0377 dir, &dentry->d_name,
0378 sbi->hidden_dir, &str);
0379 if (!res) {
0380 inode->i_flags |= S_DEAD;
0381 drop_nlink(inode);
0382 }
0383 goto out;
0384 }
0385 res = hfsplus_delete_cat(cnid, dir, &dentry->d_name);
0386 if (res)
0387 goto out;
0388
0389 if (inode->i_nlink > 0)
0390 drop_nlink(inode);
0391 if (inode->i_ino == cnid)
0392 clear_nlink(inode);
0393 if (!inode->i_nlink) {
0394 if (inode->i_ino != cnid) {
0395 sbi->file_count--;
0396 if (!atomic_read(&HFSPLUS_I(inode)->opencnt)) {
0397 res = hfsplus_delete_cat(inode->i_ino,
0398 sbi->hidden_dir,
0399 NULL);
0400 if (!res)
0401 hfsplus_delete_inode(inode);
0402 } else
0403 inode->i_flags |= S_DEAD;
0404 } else
0405 hfsplus_delete_inode(inode);
0406 } else
0407 sbi->file_count--;
0408 inode->i_ctime = current_time(inode);
0409 mark_inode_dirty(inode);
0410 out:
0411 mutex_unlock(&sbi->vh_mutex);
0412 return res;
0413 }
0414
0415 static int hfsplus_rmdir(struct inode *dir, struct dentry *dentry)
0416 {
0417 struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
0418 struct inode *inode = d_inode(dentry);
0419 int res;
0420
0421 if (inode->i_size != 2)
0422 return -ENOTEMPTY;
0423
0424 mutex_lock(&sbi->vh_mutex);
0425 res = hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
0426 if (res)
0427 goto out;
0428 clear_nlink(inode);
0429 inode->i_ctime = current_time(inode);
0430 hfsplus_delete_inode(inode);
0431 mark_inode_dirty(inode);
0432 out:
0433 mutex_unlock(&sbi->vh_mutex);
0434 return res;
0435 }
0436
0437 static int hfsplus_symlink(struct user_namespace *mnt_userns, struct inode *dir,
0438 struct dentry *dentry, const char *symname)
0439 {
0440 struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
0441 struct inode *inode;
0442 int res = -ENOMEM;
0443
0444 mutex_lock(&sbi->vh_mutex);
0445 inode = hfsplus_new_inode(dir->i_sb, dir, S_IFLNK | S_IRWXUGO);
0446 if (!inode)
0447 goto out;
0448
0449 res = page_symlink(inode, symname, strlen(symname) + 1);
0450 if (res)
0451 goto out_err;
0452
0453 res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
0454 if (res)
0455 goto out_err;
0456
0457 res = hfsplus_init_security(inode, dir, &dentry->d_name);
0458 if (res == -EOPNOTSUPP)
0459 res = 0;
0460 else if (res) {
0461
0462 hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
0463 goto out_err;
0464 }
0465
0466 hfsplus_instantiate(dentry, inode, inode->i_ino);
0467 mark_inode_dirty(inode);
0468 goto out;
0469
0470 out_err:
0471 clear_nlink(inode);
0472 hfsplus_delete_inode(inode);
0473 iput(inode);
0474 out:
0475 mutex_unlock(&sbi->vh_mutex);
0476 return res;
0477 }
0478
0479 static int hfsplus_mknod(struct user_namespace *mnt_userns, struct inode *dir,
0480 struct dentry *dentry, umode_t mode, dev_t rdev)
0481 {
0482 struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
0483 struct inode *inode;
0484 int res = -ENOMEM;
0485
0486 mutex_lock(&sbi->vh_mutex);
0487 inode = hfsplus_new_inode(dir->i_sb, dir, mode);
0488 if (!inode)
0489 goto out;
0490
0491 if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode))
0492 init_special_inode(inode, mode, rdev);
0493
0494 res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
0495 if (res)
0496 goto failed_mknod;
0497
0498 res = hfsplus_init_security(inode, dir, &dentry->d_name);
0499 if (res == -EOPNOTSUPP)
0500 res = 0;
0501 else if (res) {
0502
0503 hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
0504 goto failed_mknod;
0505 }
0506
0507 hfsplus_instantiate(dentry, inode, inode->i_ino);
0508 mark_inode_dirty(inode);
0509 goto out;
0510
0511 failed_mknod:
0512 clear_nlink(inode);
0513 hfsplus_delete_inode(inode);
0514 iput(inode);
0515 out:
0516 mutex_unlock(&sbi->vh_mutex);
0517 return res;
0518 }
0519
0520 static int hfsplus_create(struct user_namespace *mnt_userns, struct inode *dir,
0521 struct dentry *dentry, umode_t mode, bool excl)
0522 {
0523 return hfsplus_mknod(&init_user_ns, dir, dentry, mode, 0);
0524 }
0525
0526 static int hfsplus_mkdir(struct user_namespace *mnt_userns, struct inode *dir,
0527 struct dentry *dentry, umode_t mode)
0528 {
0529 return hfsplus_mknod(&init_user_ns, dir, dentry, mode | S_IFDIR, 0);
0530 }
0531
0532 static int hfsplus_rename(struct user_namespace *mnt_userns,
0533 struct inode *old_dir, struct dentry *old_dentry,
0534 struct inode *new_dir, struct dentry *new_dentry,
0535 unsigned int flags)
0536 {
0537 int res;
0538
0539 if (flags & ~RENAME_NOREPLACE)
0540 return -EINVAL;
0541
0542
0543 if (d_really_is_positive(new_dentry)) {
0544 if (d_is_dir(new_dentry))
0545 res = hfsplus_rmdir(new_dir, new_dentry);
0546 else
0547 res = hfsplus_unlink(new_dir, new_dentry);
0548 if (res)
0549 return res;
0550 }
0551
0552 res = hfsplus_rename_cat((u32)(unsigned long)old_dentry->d_fsdata,
0553 old_dir, &old_dentry->d_name,
0554 new_dir, &new_dentry->d_name);
0555 if (!res)
0556 new_dentry->d_fsdata = old_dentry->d_fsdata;
0557 return res;
0558 }
0559
0560 const struct inode_operations hfsplus_dir_inode_operations = {
0561 .lookup = hfsplus_lookup,
0562 .create = hfsplus_create,
0563 .link = hfsplus_link,
0564 .unlink = hfsplus_unlink,
0565 .mkdir = hfsplus_mkdir,
0566 .rmdir = hfsplus_rmdir,
0567 .symlink = hfsplus_symlink,
0568 .mknod = hfsplus_mknod,
0569 .rename = hfsplus_rename,
0570 .getattr = hfsplus_getattr,
0571 .listxattr = hfsplus_listxattr,
0572 .fileattr_get = hfsplus_fileattr_get,
0573 .fileattr_set = hfsplus_fileattr_set,
0574 };
0575
0576 const struct file_operations hfsplus_dir_operations = {
0577 .fsync = hfsplus_file_fsync,
0578 .read = generic_read_dir,
0579 .iterate_shared = hfsplus_readdir,
0580 .unlocked_ioctl = hfsplus_ioctl,
0581 .llseek = generic_file_llseek,
0582 .release = hfsplus_dir_release,
0583 };