static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir, struct dentry *dst_dentry) { struct hfsplus_sb_info *sbi = HFSPLUS_SB(dst_dir->i_sb); struct inode *inode = src_dentry->d_inode; struct inode *src_dir = src_dentry->d_parent->d_inode; struct qstr str; char name[32]; u32 cnid, id; int res; if (HFSPLUS_IS_RSRC(inode)) return -EPERM; if (!S_ISREG(inode->i_mode)) return -EPERM; mutex_lock(&sbi->vh_mutex); if (inode->i_ino == (u32)(unsigned long)src_dentry->d_fsdata) { for (;;) { get_random_bytes(&id, sizeof(cnid)); id &= 0x3fffffff; str.name = name; str.len = sprintf(name, "iNode%d", id); res = hfsplus_rename_cat(inode->i_ino, src_dir, &src_dentry->d_name, sbi->hidden_dir, &str); if (!res) break; if (res != -EEXIST) goto out; } HFSPLUS_I(inode)->linkid = id; cnid = sbi->next_cnid++; src_dentry->d_fsdata = (void *)(unsigned long)cnid; res = hfsplus_create_cat(cnid, src_dir, &src_dentry->d_name, inode); if (res) /* panic? */ goto out; sbi->file_count++; } cnid = sbi->next_cnid++; res = hfsplus_create_cat(cnid, dst_dir, &dst_dentry->d_name, inode); if (res) goto out; inc_nlink(inode); hfsplus_instantiate(dst_dentry, inode, cnid); ihold(inode); inode->i_ctime = CURRENT_TIME_SEC; mark_inode_dirty(inode); sbi->file_count++; hfsplus_mark_mdb_dirty(dst_dir->i_sb); out: mutex_unlock(&sbi->vh_mutex); return res; }
static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir, struct dentry *dst_dentry) { struct super_block *sb = dst_dir->i_sb; struct inode *inode = src_dentry->d_inode; struct inode *src_dir = src_dentry->d_parent->d_inode; struct qstr str; char name[32]; u32 cnid, id; int res; if (HFSPLUS_IS_RSRC(inode)) return -EPERM; if (inode->i_ino == (u32)(unsigned long)src_dentry->d_fsdata) { for (;;) { get_random_bytes(&id, sizeof(cnid)); id &= 0x3fffffff; str.name = name; str.len = sprintf(name, "iNode%d", id); res = hfsplus_rename_cat(inode->i_ino, src_dir, &src_dentry->d_name, HFSPLUS_SB(sb).hidden_dir, &str); if (!res) break; if (res != -EEXIST) return res; } HFSPLUS_I(inode).dev = id; cnid = HFSPLUS_SB(sb).next_cnid++; src_dentry->d_fsdata = (void *)(unsigned long)cnid; res = hfsplus_create_cat(cnid, src_dir, &src_dentry->d_name, inode); if (res) /* panic? */ return res; HFSPLUS_SB(sb).file_count++; } cnid = HFSPLUS_SB(sb).next_cnid++; res = hfsplus_create_cat(cnid, dst_dir, &dst_dentry->d_name, inode); if (res) return res; inc_nlink(inode); hfsplus_instantiate(dst_dentry, inode, cnid); atomic_inc(&inode->i_count); inode->i_ctime = CURRENT_TIME_SEC; mark_inode_dirty(inode); HFSPLUS_SB(sb).file_count++; sb->s_dirt = 1; return 0; }
static int hfsplus_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t rdev) { struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb); struct inode *inode; int res = -ENOSPC; mutex_lock(&sbi->vh_mutex); inode = hfsplus_new_inode(dir->i_sb, mode); if (!inode) goto out; if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)) init_special_inode(inode, mode, rdev); res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode); if (res) { clear_nlink(inode); hfsplus_delete_inode(inode); iput(inode); goto out; } hfsplus_instantiate(dentry, inode, inode->i_ino); mark_inode_dirty(inode); out: mutex_unlock(&sbi->vh_mutex); return res; }
static int hfsplus_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb); struct inode *inode; int res = -ENOSPC; mutex_lock(&sbi->vh_mutex); inode = hfsplus_new_inode(dir->i_sb, S_IFLNK | S_IRWXUGO); if (!inode) goto out; res = page_symlink(inode, symname, strlen(symname) + 1); if (res) goto out_err; res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode); if (res) goto out_err; hfsplus_instantiate(dentry, inode, inode->i_ino); mark_inode_dirty(inode); goto out; out_err: clear_nlink(inode); hfsplus_delete_inode(inode); iput(inode); out: mutex_unlock(&sbi->vh_mutex); return res; }
static int hfsplus_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev) { struct super_block *sb; struct inode *inode; int res; sb = dir->i_sb; inode = hfsplus_new_inode(sb, mode); if (!inode) return -ENOSPC; res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode); if (res) { inode->i_nlink = 0; hfsplus_delete_inode(inode); iput(inode); return res; } init_special_inode(inode, mode, rdev); hfsplus_instantiate(dentry, inode, inode->i_ino); mark_inode_dirty(inode); return 0; }
static int hfsplus_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { struct super_block *sb; struct inode *inode; int res; sb = dir->i_sb; inode = hfsplus_new_inode(sb, S_IFLNK | S_IRWXUGO); if (!inode) return -ENOSPC; res = page_symlink(inode, symname, strlen(symname) + 1); if (res) { inode->i_nlink = 0; hfsplus_delete_inode(inode); iput(inode); return res; } mark_inode_dirty(inode); res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode); if (!res) { hfsplus_instantiate(dentry, inode, inode->i_ino); mark_inode_dirty(inode); } return res; }
static int hfsplus_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev) { struct super_block *sb; struct inode *inode; hfsplus_handle_t hfsplus_handle; int res; if ((res = hfsplus_journal_start(__FUNCTION__, dir->i_sb, &hfsplus_handle))) return res; sb = dir->i_sb; inode = hfsplus_new_inode(&hfsplus_handle, sb, mode); if (!inode) { hfsplus_journal_stop(&hfsplus_handle); return -ENOSPC; } res = hfsplus_create_cat(&hfsplus_handle, inode->i_ino, dir, &dentry->d_name, inode); if (res) { inode->i_nlink = 0; hfsplus_delete_inode(&hfsplus_handle, inode); iput(inode); hfsplus_journal_stop(&hfsplus_handle); return res; } init_special_inode(inode, mode, rdev); hfsplus_instantiate(dentry, inode, inode->i_ino); res = hfsplus_journalled_mark_inode_dirty(__FUNCTION__, &hfsplus_handle, inode); hfsplus_journal_stop(&hfsplus_handle); return 0; }
static int hfsplus_mkdir(struct inode *dir, struct dentry *dentry, int mode) { struct inode *inode; hfsplus_handle_t hfsplus_handle; int res; if ((res = hfsplus_journal_start(__FUNCTION__, dir->i_sb, &hfsplus_handle))) return res; inode = hfsplus_new_inode(&hfsplus_handle, dir->i_sb, S_IFDIR | mode); if (!inode) { hfsplus_journal_stop(&hfsplus_handle); return -ENOSPC; } res = hfsplus_create_cat(&hfsplus_handle, inode->i_ino, dir, &dentry->d_name, inode); if (res) { inode->i_nlink = 0; hfsplus_delete_inode(&hfsplus_handle, inode); iput(inode); hfsplus_journal_stop(&hfsplus_handle); return res; } hfsplus_instantiate(dentry, inode, inode->i_ino); res = hfsplus_journalled_mark_inode_dirty(__FUNCTION__, &hfsplus_handle, inode); hfsplus_journal_stop(&hfsplus_handle); return res; }
static int hfsplus_mkdir(struct inode *dir, struct dentry *dentry, int mode) { struct inode *inode; int res; inode = hfsplus_new_inode(dir->i_sb, S_IFDIR | mode); if (!inode) return -ENOSPC; res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode); if (res) { inode->i_nlink = 0; hfsplus_delete_inode(inode); iput(inode); return res; } hfsplus_instantiate(dentry, inode, inode->i_ino); mark_inode_dirty(inode); return 0; }
static int hfsplus_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb); struct inode *inode; int res = -ENOSPC; mutex_lock(&sbi->vh_mutex); inode = hfsplus_new_inode(dir->i_sb, S_IFLNK | S_IRWXUGO); if (!inode) goto out; res = page_symlink(inode, symname, strlen(symname) + 1); if (res) goto out_err; res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode); if (res) goto out_err; res = hfsplus_init_inode_security(inode, dir, &dentry->d_name); if (res == -EOPNOTSUPP) res = 0; /* Operation is not supported. */ else if (res) { /* Try to delete anyway without error analysis. */ hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name); goto out_err; } hfsplus_instantiate(dentry, inode, inode->i_ino); mark_inode_dirty(inode); goto out; out_err: clear_nlink(inode); hfsplus_delete_inode(inode); iput(inode); out: mutex_unlock(&sbi->vh_mutex); return res; }
static int hfsplus_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t rdev) { struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb); struct inode *inode; int res = -ENOSPC; mutex_lock(&sbi->vh_mutex); inode = hfsplus_new_inode(dir->i_sb, mode); if (!inode) goto out; if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)) init_special_inode(inode, mode, rdev); res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode); if (res) goto failed_mknod; res = hfsplus_init_inode_security(inode, dir, &dentry->d_name); if (res == -EOPNOTSUPP) res = 0; /* Operation is not supported. */ else if (res) { /* Try to delete anyway without error analysis. */ hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name); goto failed_mknod; } hfsplus_instantiate(dentry, inode, inode->i_ino); mark_inode_dirty(inode); goto out; failed_mknod: clear_nlink(inode); hfsplus_delete_inode(inode); iput(inode); out: mutex_unlock(&sbi->vh_mutex); return res; }
static int hfsplus_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { struct super_block *sb; struct inode *inode; hfsplus_handle_t hfsplus_handle; int res; if ((res = hfsplus_journal_start(__FUNCTION__, dir->i_sb, &hfsplus_handle))) return res; sb = dir->i_sb; inode = hfsplus_new_inode(&hfsplus_handle, sb, S_IFLNK | S_IRWXUGO); if (!inode) { hfsplus_journal_stop(&hfsplus_handle); return -ENOSPC; } res = page_symlink(inode, symname, strlen(symname) + 1); if (res) { inode->i_nlink = 0; hfsplus_delete_inode(&hfsplus_handle, inode); iput(inode); hfsplus_journal_stop(&hfsplus_handle); return res; } if ((res = hfsplus_journalled_mark_inode_dirty(__FUNCTION__, &hfsplus_handle, inode))) goto symlink_out; res = hfsplus_create_cat(&hfsplus_handle, inode->i_ino, dir, &dentry->d_name, inode); if (!res) { hfsplus_instantiate(dentry, inode, inode->i_ino); res = hfsplus_journalled_mark_inode_dirty(__FUNCTION__, &hfsplus_handle, inode); } symlink_out: hfsplus_journal_stop(&hfsplus_handle); return res; }
static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) { struct hfsplus_vh *vhdr; struct hfsplus_sb_info *sbi; hfsplus_cat_entry entry; struct hfs_find_data fd; struct inode *root, *inode; struct qstr str; struct nls_table *nls = NULL; int err; err = -EINVAL; sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); if (!sbi) goto out; sb->s_fs_info = sbi; mutex_init(&sbi->alloc_mutex); mutex_init(&sbi->vh_mutex); hfsplus_fill_defaults(sbi); err = -EINVAL; if (!hfsplus_parse_options(data, sbi)) { printk(KERN_ERR "hfs: unable to parse mount options\n"); goto out_unload_nls; } /* temporarily use utf8 to correctly find the hidden dir below */ nls = sbi->nls; sbi->nls = load_nls("utf8"); if (!sbi->nls) { printk(KERN_ERR "hfs: unable to load nls for utf8\n"); goto out_unload_nls; } /* Grab the volume header */ if (hfsplus_read_wrapper(sb)) { if (!silent) printk(KERN_WARNING "hfs: unable to find HFS+ superblock\n"); goto out_unload_nls; } vhdr = sbi->s_vhdr; /* Copy parts of the volume header into the superblock */ sb->s_magic = HFSPLUS_VOLHEAD_SIG; if (be16_to_cpu(vhdr->version) < HFSPLUS_MIN_VERSION || be16_to_cpu(vhdr->version) > HFSPLUS_CURRENT_VERSION) { printk(KERN_ERR "hfs: wrong filesystem version\n"); goto out_free_vhdr; } sbi->total_blocks = be32_to_cpu(vhdr->total_blocks); sbi->free_blocks = be32_to_cpu(vhdr->free_blocks); sbi->next_cnid = be32_to_cpu(vhdr->next_cnid); sbi->file_count = be32_to_cpu(vhdr->file_count); sbi->folder_count = be32_to_cpu(vhdr->folder_count); sbi->data_clump_blocks = be32_to_cpu(vhdr->data_clump_sz) >> sbi->alloc_blksz_shift; if (!sbi->data_clump_blocks) sbi->data_clump_blocks = 1; sbi->rsrc_clump_blocks = be32_to_cpu(vhdr->rsrc_clump_sz) >> sbi->alloc_blksz_shift; if (!sbi->rsrc_clump_blocks) sbi->rsrc_clump_blocks = 1; /* Set up operations so we can load metadata */ sb->s_op = &hfsplus_sops; sb->s_maxbytes = MAX_LFS_FILESIZE; if (!(vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_UNMNT))) { printk(KERN_WARNING "hfs: Filesystem was " "not cleanly unmounted, " "running fsck.hfsplus is recommended. " "mounting read-only.\n"); sb->s_flags |= MS_RDONLY; } else if (test_and_clear_bit(HFSPLUS_SB_FORCE, &sbi->flags)) { /* nothing */ } else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) { printk(KERN_WARNING "hfs: Filesystem is marked locked, mounting read-only.\n"); sb->s_flags |= MS_RDONLY; } else if ((vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_JOURNALED)) && !(sb->s_flags & MS_RDONLY)) { printk(KERN_WARNING "hfs: write access to " "a journaled filesystem is not supported, " "use the force option at your own risk, " "mounting read-only.\n"); sb->s_flags |= MS_RDONLY; } /* Load metadata objects (B*Trees) */ sbi->ext_tree = hfs_btree_open(sb, HFSPLUS_EXT_CNID); if (!sbi->ext_tree) { printk(KERN_ERR "hfs: failed to load extents file\n"); goto out_free_vhdr; } sbi->cat_tree = hfs_btree_open(sb, HFSPLUS_CAT_CNID); if (!sbi->cat_tree) { printk(KERN_ERR "hfs: failed to load catalog file\n"); goto out_close_ext_tree; } inode = hfsplus_iget(sb, HFSPLUS_ALLOC_CNID); if (IS_ERR(inode)) { printk(KERN_ERR "hfs: failed to load allocation file\n"); err = PTR_ERR(inode); goto out_close_cat_tree; } sbi->alloc_file = inode; /* Load the root directory */ root = hfsplus_iget(sb, HFSPLUS_ROOT_CNID); if (IS_ERR(root)) { printk(KERN_ERR "hfs: failed to load root directory\n"); err = PTR_ERR(root); goto out_put_alloc_file; } str.len = sizeof(HFSP_HIDDENDIR_NAME) - 1; str.name = HFSP_HIDDENDIR_NAME; hfs_find_init(sbi->cat_tree, &fd); hfsplus_cat_build_key(sb, fd.search_key, HFSPLUS_ROOT_CNID, &str); if (!hfs_brec_read(&fd, &entry, sizeof(entry))) { hfs_find_exit(&fd); if (entry.type != cpu_to_be16(HFSPLUS_FOLDER)) goto out_put_root; inode = hfsplus_iget(sb, be32_to_cpu(entry.folder.id)); if (IS_ERR(inode)) { err = PTR_ERR(inode); goto out_put_root; } sbi->hidden_dir = inode; } else hfs_find_exit(&fd); if (!(sb->s_flags & MS_RDONLY)) { /* * H+LX == hfsplusutils, H+Lx == this driver, H+lx is unused * all three are registered with Apple for our use */ vhdr->last_mount_vers = cpu_to_be32(HFSP_MOUNT_VERSION); vhdr->modify_date = hfsp_now2mt(); be32_add_cpu(&vhdr->write_count, 1); vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_UNMNT); vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_INCNSTNT); hfsplus_sync_fs(sb, 1); if (!sbi->hidden_dir) { mutex_lock(&sbi->vh_mutex); sbi->hidden_dir = hfsplus_new_inode(sb, S_IFDIR); hfsplus_create_cat(sbi->hidden_dir->i_ino, root, &str, sbi->hidden_dir); mutex_unlock(&sbi->vh_mutex); hfsplus_mark_inode_dirty(sbi->hidden_dir, HFSPLUS_I_CAT_DIRTY); } } sb->s_d_op = &hfsplus_dentry_operations; sb->s_root = d_alloc_root(root); if (!sb->s_root) { err = -ENOMEM; goto out_put_hidden_dir; } unload_nls(sbi->nls); sbi->nls = nls; return 0; out_put_hidden_dir: iput(sbi->hidden_dir); out_put_root: iput(root); out_put_alloc_file: iput(sbi->alloc_file); out_close_cat_tree: hfs_btree_close(sbi->cat_tree); out_close_ext_tree: hfs_btree_close(sbi->ext_tree); out_free_vhdr: kfree(sbi->s_vhdr); kfree(sbi->s_backup_vhdr); out_unload_nls: unload_nls(sbi->nls); unload_nls(nls); kfree(sbi); out: return err; }