162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/fs/hfsplus/super.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2001 662306a36Sopenharmony_ci * Brad Boyer (flar@allandria.com) 762306a36Sopenharmony_ci * (C) 2003 Ardis Technologies <roman@ardistech.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/pagemap.h> 1462306a36Sopenharmony_ci#include <linux/blkdev.h> 1562306a36Sopenharmony_ci#include <linux/backing-dev.h> 1662306a36Sopenharmony_ci#include <linux/fs.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/vfs.h> 1962306a36Sopenharmony_ci#include <linux/nls.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic struct inode *hfsplus_alloc_inode(struct super_block *sb); 2262306a36Sopenharmony_cistatic void hfsplus_free_inode(struct inode *inode); 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include "hfsplus_fs.h" 2562306a36Sopenharmony_ci#include "xattr.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic int hfsplus_system_read_inode(struct inode *inode) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci struct hfsplus_vh *vhdr = HFSPLUS_SB(inode->i_sb)->s_vhdr; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci switch (inode->i_ino) { 3262306a36Sopenharmony_ci case HFSPLUS_EXT_CNID: 3362306a36Sopenharmony_ci hfsplus_inode_read_fork(inode, &vhdr->ext_file); 3462306a36Sopenharmony_ci inode->i_mapping->a_ops = &hfsplus_btree_aops; 3562306a36Sopenharmony_ci break; 3662306a36Sopenharmony_ci case HFSPLUS_CAT_CNID: 3762306a36Sopenharmony_ci hfsplus_inode_read_fork(inode, &vhdr->cat_file); 3862306a36Sopenharmony_ci inode->i_mapping->a_ops = &hfsplus_btree_aops; 3962306a36Sopenharmony_ci break; 4062306a36Sopenharmony_ci case HFSPLUS_ALLOC_CNID: 4162306a36Sopenharmony_ci hfsplus_inode_read_fork(inode, &vhdr->alloc_file); 4262306a36Sopenharmony_ci inode->i_mapping->a_ops = &hfsplus_aops; 4362306a36Sopenharmony_ci break; 4462306a36Sopenharmony_ci case HFSPLUS_START_CNID: 4562306a36Sopenharmony_ci hfsplus_inode_read_fork(inode, &vhdr->start_file); 4662306a36Sopenharmony_ci break; 4762306a36Sopenharmony_ci case HFSPLUS_ATTR_CNID: 4862306a36Sopenharmony_ci hfsplus_inode_read_fork(inode, &vhdr->attr_file); 4962306a36Sopenharmony_ci inode->i_mapping->a_ops = &hfsplus_btree_aops; 5062306a36Sopenharmony_ci break; 5162306a36Sopenharmony_ci default: 5262306a36Sopenharmony_ci return -EIO; 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci return 0; 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistruct inode *hfsplus_iget(struct super_block *sb, unsigned long ino) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci struct hfs_find_data fd; 6162306a36Sopenharmony_ci struct inode *inode; 6262306a36Sopenharmony_ci int err; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci inode = iget_locked(sb, ino); 6562306a36Sopenharmony_ci if (!inode) 6662306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 6762306a36Sopenharmony_ci if (!(inode->i_state & I_NEW)) 6862306a36Sopenharmony_ci return inode; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci INIT_LIST_HEAD(&HFSPLUS_I(inode)->open_dir_list); 7162306a36Sopenharmony_ci spin_lock_init(&HFSPLUS_I(inode)->open_dir_lock); 7262306a36Sopenharmony_ci mutex_init(&HFSPLUS_I(inode)->extents_lock); 7362306a36Sopenharmony_ci HFSPLUS_I(inode)->flags = 0; 7462306a36Sopenharmony_ci HFSPLUS_I(inode)->extent_state = 0; 7562306a36Sopenharmony_ci HFSPLUS_I(inode)->rsrc_inode = NULL; 7662306a36Sopenharmony_ci atomic_set(&HFSPLUS_I(inode)->opencnt, 0); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID || 7962306a36Sopenharmony_ci inode->i_ino == HFSPLUS_ROOT_CNID) { 8062306a36Sopenharmony_ci err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd); 8162306a36Sopenharmony_ci if (!err) { 8262306a36Sopenharmony_ci err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd); 8362306a36Sopenharmony_ci if (!err) 8462306a36Sopenharmony_ci err = hfsplus_cat_read_inode(inode, &fd); 8562306a36Sopenharmony_ci hfs_find_exit(&fd); 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci } else { 8862306a36Sopenharmony_ci err = hfsplus_system_read_inode(inode); 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (err) { 9262306a36Sopenharmony_ci iget_failed(inode); 9362306a36Sopenharmony_ci return ERR_PTR(err); 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci unlock_new_inode(inode); 9762306a36Sopenharmony_ci return inode; 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic int hfsplus_system_write_inode(struct inode *inode) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct hfsplus_sb_info *sbi = HFSPLUS_SB(inode->i_sb); 10362306a36Sopenharmony_ci struct hfsplus_vh *vhdr = sbi->s_vhdr; 10462306a36Sopenharmony_ci struct hfsplus_fork_raw *fork; 10562306a36Sopenharmony_ci struct hfs_btree *tree = NULL; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci switch (inode->i_ino) { 10862306a36Sopenharmony_ci case HFSPLUS_EXT_CNID: 10962306a36Sopenharmony_ci fork = &vhdr->ext_file; 11062306a36Sopenharmony_ci tree = sbi->ext_tree; 11162306a36Sopenharmony_ci break; 11262306a36Sopenharmony_ci case HFSPLUS_CAT_CNID: 11362306a36Sopenharmony_ci fork = &vhdr->cat_file; 11462306a36Sopenharmony_ci tree = sbi->cat_tree; 11562306a36Sopenharmony_ci break; 11662306a36Sopenharmony_ci case HFSPLUS_ALLOC_CNID: 11762306a36Sopenharmony_ci fork = &vhdr->alloc_file; 11862306a36Sopenharmony_ci break; 11962306a36Sopenharmony_ci case HFSPLUS_START_CNID: 12062306a36Sopenharmony_ci fork = &vhdr->start_file; 12162306a36Sopenharmony_ci break; 12262306a36Sopenharmony_ci case HFSPLUS_ATTR_CNID: 12362306a36Sopenharmony_ci fork = &vhdr->attr_file; 12462306a36Sopenharmony_ci tree = sbi->attr_tree; 12562306a36Sopenharmony_ci break; 12662306a36Sopenharmony_ci default: 12762306a36Sopenharmony_ci return -EIO; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (fork->total_size != cpu_to_be64(inode->i_size)) { 13162306a36Sopenharmony_ci set_bit(HFSPLUS_SB_WRITEBACKUP, &sbi->flags); 13262306a36Sopenharmony_ci hfsplus_mark_mdb_dirty(inode->i_sb); 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci hfsplus_inode_write_fork(inode, fork); 13562306a36Sopenharmony_ci if (tree) { 13662306a36Sopenharmony_ci int err = hfs_btree_write(tree); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (err) { 13962306a36Sopenharmony_ci pr_err("b-tree write err: %d, ino %lu\n", 14062306a36Sopenharmony_ci err, inode->i_ino); 14162306a36Sopenharmony_ci return err; 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci return 0; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic int hfsplus_write_inode(struct inode *inode, 14862306a36Sopenharmony_ci struct writeback_control *wbc) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci int err; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci hfs_dbg(INODE, "hfsplus_write_inode: %lu\n", inode->i_ino); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci err = hfsplus_ext_write_extent(inode); 15562306a36Sopenharmony_ci if (err) 15662306a36Sopenharmony_ci return err; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID || 15962306a36Sopenharmony_ci inode->i_ino == HFSPLUS_ROOT_CNID) 16062306a36Sopenharmony_ci return hfsplus_cat_write_inode(inode); 16162306a36Sopenharmony_ci else 16262306a36Sopenharmony_ci return hfsplus_system_write_inode(inode); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic void hfsplus_evict_inode(struct inode *inode) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci hfs_dbg(INODE, "hfsplus_evict_inode: %lu\n", inode->i_ino); 16862306a36Sopenharmony_ci truncate_inode_pages_final(&inode->i_data); 16962306a36Sopenharmony_ci clear_inode(inode); 17062306a36Sopenharmony_ci if (HFSPLUS_IS_RSRC(inode)) { 17162306a36Sopenharmony_ci HFSPLUS_I(HFSPLUS_I(inode)->rsrc_inode)->rsrc_inode = NULL; 17262306a36Sopenharmony_ci iput(HFSPLUS_I(inode)->rsrc_inode); 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic int hfsplus_sync_fs(struct super_block *sb, int wait) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); 17962306a36Sopenharmony_ci struct hfsplus_vh *vhdr = sbi->s_vhdr; 18062306a36Sopenharmony_ci int write_backup = 0; 18162306a36Sopenharmony_ci int error, error2; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (!wait) 18462306a36Sopenharmony_ci return 0; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci hfs_dbg(SUPER, "hfsplus_sync_fs\n"); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* 18962306a36Sopenharmony_ci * Explicitly write out the special metadata inodes. 19062306a36Sopenharmony_ci * 19162306a36Sopenharmony_ci * While these special inodes are marked as hashed and written 19262306a36Sopenharmony_ci * out peridocically by the flusher threads we redirty them 19362306a36Sopenharmony_ci * during writeout of normal inodes, and thus the life lock 19462306a36Sopenharmony_ci * prevents us from getting the latest state to disk. 19562306a36Sopenharmony_ci */ 19662306a36Sopenharmony_ci error = filemap_write_and_wait(sbi->cat_tree->inode->i_mapping); 19762306a36Sopenharmony_ci error2 = filemap_write_and_wait(sbi->ext_tree->inode->i_mapping); 19862306a36Sopenharmony_ci if (!error) 19962306a36Sopenharmony_ci error = error2; 20062306a36Sopenharmony_ci if (sbi->attr_tree) { 20162306a36Sopenharmony_ci error2 = 20262306a36Sopenharmony_ci filemap_write_and_wait(sbi->attr_tree->inode->i_mapping); 20362306a36Sopenharmony_ci if (!error) 20462306a36Sopenharmony_ci error = error2; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci error2 = filemap_write_and_wait(sbi->alloc_file->i_mapping); 20762306a36Sopenharmony_ci if (!error) 20862306a36Sopenharmony_ci error = error2; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci mutex_lock(&sbi->vh_mutex); 21162306a36Sopenharmony_ci mutex_lock(&sbi->alloc_mutex); 21262306a36Sopenharmony_ci vhdr->free_blocks = cpu_to_be32(sbi->free_blocks); 21362306a36Sopenharmony_ci vhdr->next_cnid = cpu_to_be32(sbi->next_cnid); 21462306a36Sopenharmony_ci vhdr->folder_count = cpu_to_be32(sbi->folder_count); 21562306a36Sopenharmony_ci vhdr->file_count = cpu_to_be32(sbi->file_count); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (test_and_clear_bit(HFSPLUS_SB_WRITEBACKUP, &sbi->flags)) { 21862306a36Sopenharmony_ci memcpy(sbi->s_backup_vhdr, sbi->s_vhdr, sizeof(*sbi->s_vhdr)); 21962306a36Sopenharmony_ci write_backup = 1; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci error2 = hfsplus_submit_bio(sb, 22362306a36Sopenharmony_ci sbi->part_start + HFSPLUS_VOLHEAD_SECTOR, 22462306a36Sopenharmony_ci sbi->s_vhdr_buf, NULL, REQ_OP_WRITE | 22562306a36Sopenharmony_ci REQ_SYNC); 22662306a36Sopenharmony_ci if (!error) 22762306a36Sopenharmony_ci error = error2; 22862306a36Sopenharmony_ci if (!write_backup) 22962306a36Sopenharmony_ci goto out; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci error2 = hfsplus_submit_bio(sb, 23262306a36Sopenharmony_ci sbi->part_start + sbi->sect_count - 2, 23362306a36Sopenharmony_ci sbi->s_backup_vhdr_buf, NULL, REQ_OP_WRITE | 23462306a36Sopenharmony_ci REQ_SYNC); 23562306a36Sopenharmony_ci if (!error) 23662306a36Sopenharmony_ci error2 = error; 23762306a36Sopenharmony_ciout: 23862306a36Sopenharmony_ci mutex_unlock(&sbi->alloc_mutex); 23962306a36Sopenharmony_ci mutex_unlock(&sbi->vh_mutex); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (!test_bit(HFSPLUS_SB_NOBARRIER, &sbi->flags)) 24262306a36Sopenharmony_ci blkdev_issue_flush(sb->s_bdev); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci return error; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic void delayed_sync_fs(struct work_struct *work) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci int err; 25062306a36Sopenharmony_ci struct hfsplus_sb_info *sbi; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci sbi = container_of(work, struct hfsplus_sb_info, sync_work.work); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci spin_lock(&sbi->work_lock); 25562306a36Sopenharmony_ci sbi->work_queued = 0; 25662306a36Sopenharmony_ci spin_unlock(&sbi->work_lock); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci err = hfsplus_sync_fs(sbi->alloc_file->i_sb, 1); 25962306a36Sopenharmony_ci if (err) 26062306a36Sopenharmony_ci pr_err("delayed sync fs err %d\n", err); 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_civoid hfsplus_mark_mdb_dirty(struct super_block *sb) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); 26662306a36Sopenharmony_ci unsigned long delay; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci if (sb_rdonly(sb)) 26962306a36Sopenharmony_ci return; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci spin_lock(&sbi->work_lock); 27262306a36Sopenharmony_ci if (!sbi->work_queued) { 27362306a36Sopenharmony_ci delay = msecs_to_jiffies(dirty_writeback_interval * 10); 27462306a36Sopenharmony_ci queue_delayed_work(system_long_wq, &sbi->sync_work, delay); 27562306a36Sopenharmony_ci sbi->work_queued = 1; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci spin_unlock(&sbi->work_lock); 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic void hfsplus_put_super(struct super_block *sb) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci hfs_dbg(SUPER, "hfsplus_put_super\n"); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci cancel_delayed_work_sync(&sbi->sync_work); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (!sb_rdonly(sb) && sbi->s_vhdr) { 28962306a36Sopenharmony_ci struct hfsplus_vh *vhdr = sbi->s_vhdr; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci vhdr->modify_date = hfsp_now2mt(); 29262306a36Sopenharmony_ci vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_UNMNT); 29362306a36Sopenharmony_ci vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_INCNSTNT); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci hfsplus_sync_fs(sb, 1); 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci iput(sbi->alloc_file); 29962306a36Sopenharmony_ci iput(sbi->hidden_dir); 30062306a36Sopenharmony_ci hfs_btree_close(sbi->attr_tree); 30162306a36Sopenharmony_ci hfs_btree_close(sbi->cat_tree); 30262306a36Sopenharmony_ci hfs_btree_close(sbi->ext_tree); 30362306a36Sopenharmony_ci kfree(sbi->s_vhdr_buf); 30462306a36Sopenharmony_ci kfree(sbi->s_backup_vhdr_buf); 30562306a36Sopenharmony_ci unload_nls(sbi->nls); 30662306a36Sopenharmony_ci kfree(sb->s_fs_info); 30762306a36Sopenharmony_ci sb->s_fs_info = NULL; 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic int hfsplus_statfs(struct dentry *dentry, struct kstatfs *buf) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci struct super_block *sb = dentry->d_sb; 31362306a36Sopenharmony_ci struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); 31462306a36Sopenharmony_ci u64 id = huge_encode_dev(sb->s_bdev->bd_dev); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci buf->f_type = HFSPLUS_SUPER_MAGIC; 31762306a36Sopenharmony_ci buf->f_bsize = sb->s_blocksize; 31862306a36Sopenharmony_ci buf->f_blocks = sbi->total_blocks << sbi->fs_shift; 31962306a36Sopenharmony_ci buf->f_bfree = sbi->free_blocks << sbi->fs_shift; 32062306a36Sopenharmony_ci buf->f_bavail = buf->f_bfree; 32162306a36Sopenharmony_ci buf->f_files = 0xFFFFFFFF; 32262306a36Sopenharmony_ci buf->f_ffree = 0xFFFFFFFF - sbi->next_cnid; 32362306a36Sopenharmony_ci buf->f_fsid = u64_to_fsid(id); 32462306a36Sopenharmony_ci buf->f_namelen = HFSPLUS_MAX_STRLEN; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci return 0; 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic int hfsplus_remount(struct super_block *sb, int *flags, char *data) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci sync_filesystem(sb); 33262306a36Sopenharmony_ci if ((bool)(*flags & SB_RDONLY) == sb_rdonly(sb)) 33362306a36Sopenharmony_ci return 0; 33462306a36Sopenharmony_ci if (!(*flags & SB_RDONLY)) { 33562306a36Sopenharmony_ci struct hfsplus_vh *vhdr = HFSPLUS_SB(sb)->s_vhdr; 33662306a36Sopenharmony_ci int force = 0; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (!hfsplus_parse_options_remount(data, &force)) 33962306a36Sopenharmony_ci return -EINVAL; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (!(vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_UNMNT))) { 34262306a36Sopenharmony_ci pr_warn("filesystem was not cleanly unmounted, running fsck.hfsplus is recommended. leaving read-only.\n"); 34362306a36Sopenharmony_ci sb->s_flags |= SB_RDONLY; 34462306a36Sopenharmony_ci *flags |= SB_RDONLY; 34562306a36Sopenharmony_ci } else if (force) { 34662306a36Sopenharmony_ci /* nothing */ 34762306a36Sopenharmony_ci } else if (vhdr->attributes & 34862306a36Sopenharmony_ci cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) { 34962306a36Sopenharmony_ci pr_warn("filesystem is marked locked, leaving read-only.\n"); 35062306a36Sopenharmony_ci sb->s_flags |= SB_RDONLY; 35162306a36Sopenharmony_ci *flags |= SB_RDONLY; 35262306a36Sopenharmony_ci } else if (vhdr->attributes & 35362306a36Sopenharmony_ci cpu_to_be32(HFSPLUS_VOL_JOURNALED)) { 35462306a36Sopenharmony_ci pr_warn("filesystem is marked journaled, leaving read-only.\n"); 35562306a36Sopenharmony_ci sb->s_flags |= SB_RDONLY; 35662306a36Sopenharmony_ci *flags |= SB_RDONLY; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci return 0; 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_cistatic const struct super_operations hfsplus_sops = { 36362306a36Sopenharmony_ci .alloc_inode = hfsplus_alloc_inode, 36462306a36Sopenharmony_ci .free_inode = hfsplus_free_inode, 36562306a36Sopenharmony_ci .write_inode = hfsplus_write_inode, 36662306a36Sopenharmony_ci .evict_inode = hfsplus_evict_inode, 36762306a36Sopenharmony_ci .put_super = hfsplus_put_super, 36862306a36Sopenharmony_ci .sync_fs = hfsplus_sync_fs, 36962306a36Sopenharmony_ci .statfs = hfsplus_statfs, 37062306a36Sopenharmony_ci .remount_fs = hfsplus_remount, 37162306a36Sopenharmony_ci .show_options = hfsplus_show_options, 37262306a36Sopenharmony_ci}; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic int hfsplus_fill_super(struct super_block *sb, void *data, int silent) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct hfsplus_vh *vhdr; 37762306a36Sopenharmony_ci struct hfsplus_sb_info *sbi; 37862306a36Sopenharmony_ci hfsplus_cat_entry entry; 37962306a36Sopenharmony_ci struct hfs_find_data fd; 38062306a36Sopenharmony_ci struct inode *root, *inode; 38162306a36Sopenharmony_ci struct qstr str; 38262306a36Sopenharmony_ci struct nls_table *nls = NULL; 38362306a36Sopenharmony_ci u64 last_fs_block, last_fs_page; 38462306a36Sopenharmony_ci int err; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci err = -ENOMEM; 38762306a36Sopenharmony_ci sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); 38862306a36Sopenharmony_ci if (!sbi) 38962306a36Sopenharmony_ci goto out; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci sb->s_fs_info = sbi; 39262306a36Sopenharmony_ci mutex_init(&sbi->alloc_mutex); 39362306a36Sopenharmony_ci mutex_init(&sbi->vh_mutex); 39462306a36Sopenharmony_ci spin_lock_init(&sbi->work_lock); 39562306a36Sopenharmony_ci INIT_DELAYED_WORK(&sbi->sync_work, delayed_sync_fs); 39662306a36Sopenharmony_ci hfsplus_fill_defaults(sbi); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci err = -EINVAL; 39962306a36Sopenharmony_ci if (!hfsplus_parse_options(data, sbi)) { 40062306a36Sopenharmony_ci pr_err("unable to parse mount options\n"); 40162306a36Sopenharmony_ci goto out_unload_nls; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci /* temporarily use utf8 to correctly find the hidden dir below */ 40562306a36Sopenharmony_ci nls = sbi->nls; 40662306a36Sopenharmony_ci sbi->nls = load_nls("utf8"); 40762306a36Sopenharmony_ci if (!sbi->nls) { 40862306a36Sopenharmony_ci pr_err("unable to load nls for utf8\n"); 40962306a36Sopenharmony_ci goto out_unload_nls; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* Grab the volume header */ 41362306a36Sopenharmony_ci if (hfsplus_read_wrapper(sb)) { 41462306a36Sopenharmony_ci if (!silent) 41562306a36Sopenharmony_ci pr_warn("unable to find HFS+ superblock\n"); 41662306a36Sopenharmony_ci goto out_unload_nls; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci vhdr = sbi->s_vhdr; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci /* Copy parts of the volume header into the superblock */ 42162306a36Sopenharmony_ci sb->s_magic = HFSPLUS_VOLHEAD_SIG; 42262306a36Sopenharmony_ci if (be16_to_cpu(vhdr->version) < HFSPLUS_MIN_VERSION || 42362306a36Sopenharmony_ci be16_to_cpu(vhdr->version) > HFSPLUS_CURRENT_VERSION) { 42462306a36Sopenharmony_ci pr_err("wrong filesystem version\n"); 42562306a36Sopenharmony_ci goto out_free_vhdr; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci sbi->total_blocks = be32_to_cpu(vhdr->total_blocks); 42862306a36Sopenharmony_ci sbi->free_blocks = be32_to_cpu(vhdr->free_blocks); 42962306a36Sopenharmony_ci sbi->next_cnid = be32_to_cpu(vhdr->next_cnid); 43062306a36Sopenharmony_ci sbi->file_count = be32_to_cpu(vhdr->file_count); 43162306a36Sopenharmony_ci sbi->folder_count = be32_to_cpu(vhdr->folder_count); 43262306a36Sopenharmony_ci sbi->data_clump_blocks = 43362306a36Sopenharmony_ci be32_to_cpu(vhdr->data_clump_sz) >> sbi->alloc_blksz_shift; 43462306a36Sopenharmony_ci if (!sbi->data_clump_blocks) 43562306a36Sopenharmony_ci sbi->data_clump_blocks = 1; 43662306a36Sopenharmony_ci sbi->rsrc_clump_blocks = 43762306a36Sopenharmony_ci be32_to_cpu(vhdr->rsrc_clump_sz) >> sbi->alloc_blksz_shift; 43862306a36Sopenharmony_ci if (!sbi->rsrc_clump_blocks) 43962306a36Sopenharmony_ci sbi->rsrc_clump_blocks = 1; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci err = -EFBIG; 44262306a36Sopenharmony_ci last_fs_block = sbi->total_blocks - 1; 44362306a36Sopenharmony_ci last_fs_page = (last_fs_block << sbi->alloc_blksz_shift) >> 44462306a36Sopenharmony_ci PAGE_SHIFT; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci if ((last_fs_block > (sector_t)(~0ULL) >> (sbi->alloc_blksz_shift - 9)) || 44762306a36Sopenharmony_ci (last_fs_page > (pgoff_t)(~0ULL))) { 44862306a36Sopenharmony_ci pr_err("filesystem size too large\n"); 44962306a36Sopenharmony_ci goto out_free_vhdr; 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci /* Set up operations so we can load metadata */ 45362306a36Sopenharmony_ci sb->s_op = &hfsplus_sops; 45462306a36Sopenharmony_ci sb->s_maxbytes = MAX_LFS_FILESIZE; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci if (!(vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_UNMNT))) { 45762306a36Sopenharmony_ci pr_warn("Filesystem was not cleanly unmounted, running fsck.hfsplus is recommended. mounting read-only.\n"); 45862306a36Sopenharmony_ci sb->s_flags |= SB_RDONLY; 45962306a36Sopenharmony_ci } else if (test_and_clear_bit(HFSPLUS_SB_FORCE, &sbi->flags)) { 46062306a36Sopenharmony_ci /* nothing */ 46162306a36Sopenharmony_ci } else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) { 46262306a36Sopenharmony_ci pr_warn("Filesystem is marked locked, mounting read-only.\n"); 46362306a36Sopenharmony_ci sb->s_flags |= SB_RDONLY; 46462306a36Sopenharmony_ci } else if ((vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_JOURNALED)) && 46562306a36Sopenharmony_ci !sb_rdonly(sb)) { 46662306a36Sopenharmony_ci pr_warn("write access to a journaled filesystem is not supported, use the force option at your own risk, mounting read-only.\n"); 46762306a36Sopenharmony_ci sb->s_flags |= SB_RDONLY; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci err = -EINVAL; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci /* Load metadata objects (B*Trees) */ 47362306a36Sopenharmony_ci sbi->ext_tree = hfs_btree_open(sb, HFSPLUS_EXT_CNID); 47462306a36Sopenharmony_ci if (!sbi->ext_tree) { 47562306a36Sopenharmony_ci pr_err("failed to load extents file\n"); 47662306a36Sopenharmony_ci goto out_free_vhdr; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci sbi->cat_tree = hfs_btree_open(sb, HFSPLUS_CAT_CNID); 47962306a36Sopenharmony_ci if (!sbi->cat_tree) { 48062306a36Sopenharmony_ci pr_err("failed to load catalog file\n"); 48162306a36Sopenharmony_ci goto out_close_ext_tree; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci atomic_set(&sbi->attr_tree_state, HFSPLUS_EMPTY_ATTR_TREE); 48462306a36Sopenharmony_ci if (vhdr->attr_file.total_blocks != 0) { 48562306a36Sopenharmony_ci sbi->attr_tree = hfs_btree_open(sb, HFSPLUS_ATTR_CNID); 48662306a36Sopenharmony_ci if (!sbi->attr_tree) { 48762306a36Sopenharmony_ci pr_err("failed to load attributes file\n"); 48862306a36Sopenharmony_ci goto out_close_cat_tree; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci atomic_set(&sbi->attr_tree_state, HFSPLUS_VALID_ATTR_TREE); 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci sb->s_xattr = hfsplus_xattr_handlers; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci inode = hfsplus_iget(sb, HFSPLUS_ALLOC_CNID); 49562306a36Sopenharmony_ci if (IS_ERR(inode)) { 49662306a36Sopenharmony_ci pr_err("failed to load allocation file\n"); 49762306a36Sopenharmony_ci err = PTR_ERR(inode); 49862306a36Sopenharmony_ci goto out_close_attr_tree; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci sbi->alloc_file = inode; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci /* Load the root directory */ 50362306a36Sopenharmony_ci root = hfsplus_iget(sb, HFSPLUS_ROOT_CNID); 50462306a36Sopenharmony_ci if (IS_ERR(root)) { 50562306a36Sopenharmony_ci pr_err("failed to load root directory\n"); 50662306a36Sopenharmony_ci err = PTR_ERR(root); 50762306a36Sopenharmony_ci goto out_put_alloc_file; 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci sb->s_d_op = &hfsplus_dentry_operations; 51162306a36Sopenharmony_ci sb->s_root = d_make_root(root); 51262306a36Sopenharmony_ci if (!sb->s_root) { 51362306a36Sopenharmony_ci err = -ENOMEM; 51462306a36Sopenharmony_ci goto out_put_alloc_file; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci str.len = sizeof(HFSP_HIDDENDIR_NAME) - 1; 51862306a36Sopenharmony_ci str.name = HFSP_HIDDENDIR_NAME; 51962306a36Sopenharmony_ci err = hfs_find_init(sbi->cat_tree, &fd); 52062306a36Sopenharmony_ci if (err) 52162306a36Sopenharmony_ci goto out_put_root; 52262306a36Sopenharmony_ci err = hfsplus_cat_build_key(sb, fd.search_key, HFSPLUS_ROOT_CNID, &str); 52362306a36Sopenharmony_ci if (unlikely(err < 0)) 52462306a36Sopenharmony_ci goto out_put_root; 52562306a36Sopenharmony_ci if (!hfs_brec_read(&fd, &entry, sizeof(entry))) { 52662306a36Sopenharmony_ci hfs_find_exit(&fd); 52762306a36Sopenharmony_ci if (entry.type != cpu_to_be16(HFSPLUS_FOLDER)) { 52862306a36Sopenharmony_ci err = -EINVAL; 52962306a36Sopenharmony_ci goto out_put_root; 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci inode = hfsplus_iget(sb, be32_to_cpu(entry.folder.id)); 53262306a36Sopenharmony_ci if (IS_ERR(inode)) { 53362306a36Sopenharmony_ci err = PTR_ERR(inode); 53462306a36Sopenharmony_ci goto out_put_root; 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci sbi->hidden_dir = inode; 53762306a36Sopenharmony_ci } else 53862306a36Sopenharmony_ci hfs_find_exit(&fd); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci if (!sb_rdonly(sb)) { 54162306a36Sopenharmony_ci /* 54262306a36Sopenharmony_ci * H+LX == hfsplusutils, H+Lx == this driver, H+lx is unused 54362306a36Sopenharmony_ci * all three are registered with Apple for our use 54462306a36Sopenharmony_ci */ 54562306a36Sopenharmony_ci vhdr->last_mount_vers = cpu_to_be32(HFSP_MOUNT_VERSION); 54662306a36Sopenharmony_ci vhdr->modify_date = hfsp_now2mt(); 54762306a36Sopenharmony_ci be32_add_cpu(&vhdr->write_count, 1); 54862306a36Sopenharmony_ci vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_UNMNT); 54962306a36Sopenharmony_ci vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_INCNSTNT); 55062306a36Sopenharmony_ci hfsplus_sync_fs(sb, 1); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci if (!sbi->hidden_dir) { 55362306a36Sopenharmony_ci mutex_lock(&sbi->vh_mutex); 55462306a36Sopenharmony_ci sbi->hidden_dir = hfsplus_new_inode(sb, root, S_IFDIR); 55562306a36Sopenharmony_ci if (!sbi->hidden_dir) { 55662306a36Sopenharmony_ci mutex_unlock(&sbi->vh_mutex); 55762306a36Sopenharmony_ci err = -ENOMEM; 55862306a36Sopenharmony_ci goto out_put_root; 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci err = hfsplus_create_cat(sbi->hidden_dir->i_ino, root, 56162306a36Sopenharmony_ci &str, sbi->hidden_dir); 56262306a36Sopenharmony_ci if (err) { 56362306a36Sopenharmony_ci mutex_unlock(&sbi->vh_mutex); 56462306a36Sopenharmony_ci goto out_put_hidden_dir; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci err = hfsplus_init_security(sbi->hidden_dir, 56862306a36Sopenharmony_ci root, &str); 56962306a36Sopenharmony_ci if (err == -EOPNOTSUPP) 57062306a36Sopenharmony_ci err = 0; /* Operation is not supported. */ 57162306a36Sopenharmony_ci else if (err) { 57262306a36Sopenharmony_ci /* 57362306a36Sopenharmony_ci * Try to delete anyway without 57462306a36Sopenharmony_ci * error analysis. 57562306a36Sopenharmony_ci */ 57662306a36Sopenharmony_ci hfsplus_delete_cat(sbi->hidden_dir->i_ino, 57762306a36Sopenharmony_ci root, &str); 57862306a36Sopenharmony_ci mutex_unlock(&sbi->vh_mutex); 57962306a36Sopenharmony_ci goto out_put_hidden_dir; 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci mutex_unlock(&sbi->vh_mutex); 58362306a36Sopenharmony_ci hfsplus_mark_inode_dirty(sbi->hidden_dir, 58462306a36Sopenharmony_ci HFSPLUS_I_CAT_DIRTY); 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci unload_nls(sbi->nls); 58962306a36Sopenharmony_ci sbi->nls = nls; 59062306a36Sopenharmony_ci return 0; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ciout_put_hidden_dir: 59362306a36Sopenharmony_ci cancel_delayed_work_sync(&sbi->sync_work); 59462306a36Sopenharmony_ci iput(sbi->hidden_dir); 59562306a36Sopenharmony_ciout_put_root: 59662306a36Sopenharmony_ci dput(sb->s_root); 59762306a36Sopenharmony_ci sb->s_root = NULL; 59862306a36Sopenharmony_ciout_put_alloc_file: 59962306a36Sopenharmony_ci iput(sbi->alloc_file); 60062306a36Sopenharmony_ciout_close_attr_tree: 60162306a36Sopenharmony_ci hfs_btree_close(sbi->attr_tree); 60262306a36Sopenharmony_ciout_close_cat_tree: 60362306a36Sopenharmony_ci hfs_btree_close(sbi->cat_tree); 60462306a36Sopenharmony_ciout_close_ext_tree: 60562306a36Sopenharmony_ci hfs_btree_close(sbi->ext_tree); 60662306a36Sopenharmony_ciout_free_vhdr: 60762306a36Sopenharmony_ci kfree(sbi->s_vhdr_buf); 60862306a36Sopenharmony_ci kfree(sbi->s_backup_vhdr_buf); 60962306a36Sopenharmony_ciout_unload_nls: 61062306a36Sopenharmony_ci unload_nls(sbi->nls); 61162306a36Sopenharmony_ci unload_nls(nls); 61262306a36Sopenharmony_ci kfree(sbi); 61362306a36Sopenharmony_ciout: 61462306a36Sopenharmony_ci return err; 61562306a36Sopenharmony_ci} 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ciMODULE_AUTHOR("Brad Boyer"); 61862306a36Sopenharmony_ciMODULE_DESCRIPTION("Extended Macintosh Filesystem"); 61962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_cistatic struct kmem_cache *hfsplus_inode_cachep; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cistatic struct inode *hfsplus_alloc_inode(struct super_block *sb) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci struct hfsplus_inode_info *i; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci i = alloc_inode_sb(sb, hfsplus_inode_cachep, GFP_KERNEL); 62862306a36Sopenharmony_ci return i ? &i->vfs_inode : NULL; 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_cistatic void hfsplus_free_inode(struct inode *inode) 63262306a36Sopenharmony_ci{ 63362306a36Sopenharmony_ci kmem_cache_free(hfsplus_inode_cachep, HFSPLUS_I(inode)); 63462306a36Sopenharmony_ci} 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci#define HFSPLUS_INODE_SIZE sizeof(struct hfsplus_inode_info) 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_cistatic struct dentry *hfsplus_mount(struct file_system_type *fs_type, 63962306a36Sopenharmony_ci int flags, const char *dev_name, void *data) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci return mount_bdev(fs_type, flags, dev_name, data, hfsplus_fill_super); 64262306a36Sopenharmony_ci} 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_cistatic struct file_system_type hfsplus_fs_type = { 64562306a36Sopenharmony_ci .owner = THIS_MODULE, 64662306a36Sopenharmony_ci .name = "hfsplus", 64762306a36Sopenharmony_ci .mount = hfsplus_mount, 64862306a36Sopenharmony_ci .kill_sb = kill_block_super, 64962306a36Sopenharmony_ci .fs_flags = FS_REQUIRES_DEV, 65062306a36Sopenharmony_ci}; 65162306a36Sopenharmony_ciMODULE_ALIAS_FS("hfsplus"); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_cistatic void hfsplus_init_once(void *p) 65462306a36Sopenharmony_ci{ 65562306a36Sopenharmony_ci struct hfsplus_inode_info *i = p; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci inode_init_once(&i->vfs_inode); 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_cistatic int __init init_hfsplus_fs(void) 66162306a36Sopenharmony_ci{ 66262306a36Sopenharmony_ci int err; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci hfsplus_inode_cachep = kmem_cache_create("hfsplus_icache", 66562306a36Sopenharmony_ci HFSPLUS_INODE_SIZE, 0, SLAB_HWCACHE_ALIGN|SLAB_ACCOUNT, 66662306a36Sopenharmony_ci hfsplus_init_once); 66762306a36Sopenharmony_ci if (!hfsplus_inode_cachep) 66862306a36Sopenharmony_ci return -ENOMEM; 66962306a36Sopenharmony_ci err = hfsplus_create_attr_tree_cache(); 67062306a36Sopenharmony_ci if (err) 67162306a36Sopenharmony_ci goto destroy_inode_cache; 67262306a36Sopenharmony_ci err = register_filesystem(&hfsplus_fs_type); 67362306a36Sopenharmony_ci if (err) 67462306a36Sopenharmony_ci goto destroy_attr_tree_cache; 67562306a36Sopenharmony_ci return 0; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_cidestroy_attr_tree_cache: 67862306a36Sopenharmony_ci hfsplus_destroy_attr_tree_cache(); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_cidestroy_inode_cache: 68162306a36Sopenharmony_ci kmem_cache_destroy(hfsplus_inode_cachep); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci return err; 68462306a36Sopenharmony_ci} 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_cistatic void __exit exit_hfsplus_fs(void) 68762306a36Sopenharmony_ci{ 68862306a36Sopenharmony_ci unregister_filesystem(&hfsplus_fs_type); 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci /* 69162306a36Sopenharmony_ci * Make sure all delayed rcu free inodes are flushed before we 69262306a36Sopenharmony_ci * destroy cache. 69362306a36Sopenharmony_ci */ 69462306a36Sopenharmony_ci rcu_barrier(); 69562306a36Sopenharmony_ci hfsplus_destroy_attr_tree_cache(); 69662306a36Sopenharmony_ci kmem_cache_destroy(hfsplus_inode_cachep); 69762306a36Sopenharmony_ci} 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_cimodule_init(init_hfsplus_fs) 70062306a36Sopenharmony_cimodule_exit(exit_hfsplus_fs) 701