linux/fs/hfsplus/super.c
<<
>>
Prefs
   1/*
   2 *  linux/fs/hfsplus/super.c
   3 *
   4 * Copyright (C) 2001
   5 * Brad Boyer (flar@allandria.com)
   6 * (C) 2003 Ardis Technologies <roman@ardistech.com>
   7 *
   8 */
   9
  10#include <linux/module.h>
  11#include <linux/init.h>
  12#include <linux/pagemap.h>
  13#include <linux/fs.h>
  14#include <linux/slab.h>
  15#include <linux/vfs.h>
  16#include <linux/nls.h>
  17
  18static struct inode *hfsplus_alloc_inode(struct super_block *sb);
  19static void hfsplus_destroy_inode(struct inode *inode);
  20
  21#include "hfsplus_fs.h"
  22
  23struct inode *hfsplus_iget(struct super_block *sb, unsigned long ino)
  24{
  25        struct hfs_find_data fd;
  26        struct hfsplus_vh *vhdr;
  27        struct inode *inode;
  28        long err = -EIO;
  29
  30        inode = iget_locked(sb, ino);
  31        if (!inode)
  32                return ERR_PTR(-ENOMEM);
  33        if (!(inode->i_state & I_NEW))
  34                return inode;
  35
  36        INIT_LIST_HEAD(&HFSPLUS_I(inode).open_dir_list);
  37        mutex_init(&HFSPLUS_I(inode).extents_lock);
  38        HFSPLUS_I(inode).flags = 0;
  39        HFSPLUS_I(inode).rsrc_inode = NULL;
  40        atomic_set(&HFSPLUS_I(inode).opencnt, 0);
  41
  42        if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID) {
  43        read_inode:
  44                hfs_find_init(HFSPLUS_SB(inode->i_sb).cat_tree, &fd);
  45                err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
  46                if (!err)
  47                        err = hfsplus_cat_read_inode(inode, &fd);
  48                hfs_find_exit(&fd);
  49                if (err)
  50                        goto bad_inode;
  51                goto done;
  52        }
  53        vhdr = HFSPLUS_SB(inode->i_sb).s_vhdr;
  54        switch(inode->i_ino) {
  55        case HFSPLUS_ROOT_CNID:
  56                goto read_inode;
  57        case HFSPLUS_EXT_CNID:
  58                hfsplus_inode_read_fork(inode, &vhdr->ext_file);
  59                inode->i_mapping->a_ops = &hfsplus_btree_aops;
  60                break;
  61        case HFSPLUS_CAT_CNID:
  62                hfsplus_inode_read_fork(inode, &vhdr->cat_file);
  63                inode->i_mapping->a_ops = &hfsplus_btree_aops;
  64                break;
  65        case HFSPLUS_ALLOC_CNID:
  66                hfsplus_inode_read_fork(inode, &vhdr->alloc_file);
  67                inode->i_mapping->a_ops = &hfsplus_aops;
  68                break;
  69        case HFSPLUS_START_CNID:
  70                hfsplus_inode_read_fork(inode, &vhdr->start_file);
  71                break;
  72        case HFSPLUS_ATTR_CNID:
  73                hfsplus_inode_read_fork(inode, &vhdr->attr_file);
  74                inode->i_mapping->a_ops = &hfsplus_btree_aops;
  75                break;
  76        default:
  77                goto bad_inode;
  78        }
  79
  80done:
  81        unlock_new_inode(inode);
  82        return inode;
  83
  84bad_inode:
  85        iget_failed(inode);
  86        return ERR_PTR(err);
  87}
  88
  89static int hfsplus_write_inode(struct inode *inode, int unused)
  90{
  91        struct hfsplus_vh *vhdr;
  92        int ret = 0;
  93
  94        dprint(DBG_INODE, "hfsplus_write_inode: %lu\n", inode->i_ino);
  95        hfsplus_ext_write_extent(inode);
  96        if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID) {
  97                return hfsplus_cat_write_inode(inode);
  98        }
  99        vhdr = HFSPLUS_SB(inode->i_sb).s_vhdr;
 100        switch (inode->i_ino) {
 101        case HFSPLUS_ROOT_CNID:
 102                ret = hfsplus_cat_write_inode(inode);
 103                break;
 104        case HFSPLUS_EXT_CNID:
 105                if (vhdr->ext_file.total_size != cpu_to_be64(inode->i_size)) {
 106                        HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP;
 107                        inode->i_sb->s_dirt = 1;
 108                }
 109                hfsplus_inode_write_fork(inode, &vhdr->ext_file);
 110                hfs_btree_write(HFSPLUS_SB(inode->i_sb).ext_tree);
 111                break;
 112        case HFSPLUS_CAT_CNID:
 113                if (vhdr->cat_file.total_size != cpu_to_be64(inode->i_size)) {
 114                        HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP;
 115                        inode->i_sb->s_dirt = 1;
 116                }
 117                hfsplus_inode_write_fork(inode, &vhdr->cat_file);
 118                hfs_btree_write(HFSPLUS_SB(inode->i_sb).cat_tree);
 119                break;
 120        case HFSPLUS_ALLOC_CNID:
 121                if (vhdr->alloc_file.total_size != cpu_to_be64(inode->i_size)) {
 122                        HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP;
 123                        inode->i_sb->s_dirt = 1;
 124                }
 125                hfsplus_inode_write_fork(inode, &vhdr->alloc_file);
 126                break;
 127        case HFSPLUS_START_CNID:
 128                if (vhdr->start_file.total_size != cpu_to_be64(inode->i_size)) {
 129                        HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP;
 130                        inode->i_sb->s_dirt = 1;
 131                }
 132                hfsplus_inode_write_fork(inode, &vhdr->start_file);
 133                break;
 134        case HFSPLUS_ATTR_CNID:
 135                if (vhdr->attr_file.total_size != cpu_to_be64(inode->i_size)) {
 136                        HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP;
 137                        inode->i_sb->s_dirt = 1;
 138                }
 139                hfsplus_inode_write_fork(inode, &vhdr->attr_file);
 140                hfs_btree_write(HFSPLUS_SB(inode->i_sb).attr_tree);
 141                break;
 142        }
 143        return ret;
 144}
 145
 146static void hfsplus_clear_inode(struct inode *inode)
 147{
 148        dprint(DBG_INODE, "hfsplus_clear_inode: %lu\n", inode->i_ino);
 149        if (HFSPLUS_IS_RSRC(inode)) {
 150                HFSPLUS_I(HFSPLUS_I(inode).rsrc_inode).rsrc_inode = NULL;
 151                iput(HFSPLUS_I(inode).rsrc_inode);
 152        }
 153}
 154
 155static void hfsplus_write_super(struct super_block *sb)
 156{
 157        struct hfsplus_vh *vhdr = HFSPLUS_SB(sb).s_vhdr;
 158
 159        dprint(DBG_SUPER, "hfsplus_write_super\n");
 160        sb->s_dirt = 0;
 161        if (sb->s_flags & MS_RDONLY)
 162                /* warn? */
 163                return;
 164
 165        vhdr->free_blocks = cpu_to_be32(HFSPLUS_SB(sb).free_blocks);
 166        vhdr->next_alloc = cpu_to_be32(HFSPLUS_SB(sb).next_alloc);
 167        vhdr->next_cnid = cpu_to_be32(HFSPLUS_SB(sb).next_cnid);
 168        vhdr->folder_count = cpu_to_be32(HFSPLUS_SB(sb).folder_count);
 169        vhdr->file_count = cpu_to_be32(HFSPLUS_SB(sb).file_count);
 170
 171        mark_buffer_dirty(HFSPLUS_SB(sb).s_vhbh);
 172        if (HFSPLUS_SB(sb).flags & HFSPLUS_SB_WRITEBACKUP) {
 173                if (HFSPLUS_SB(sb).sect_count) {
 174                        struct buffer_head *bh;
 175                        u32 block, offset;
 176
 177                        block = HFSPLUS_SB(sb).blockoffset;
 178                        block += (HFSPLUS_SB(sb).sect_count - 2) >> (sb->s_blocksize_bits - 9);
 179                        offset = ((HFSPLUS_SB(sb).sect_count - 2) << 9) & (sb->s_blocksize - 1);
 180                        printk(KERN_DEBUG "hfs: backup: %u,%u,%u,%u\n", HFSPLUS_SB(sb).blockoffset,
 181                                HFSPLUS_SB(sb).sect_count, block, offset);
 182                        bh = sb_bread(sb, block);
 183                        if (bh) {
 184                                vhdr = (struct hfsplus_vh *)(bh->b_data + offset);
 185                                if (be16_to_cpu(vhdr->signature) == HFSPLUS_VOLHEAD_SIG) {
 186                                        memcpy(vhdr, HFSPLUS_SB(sb).s_vhdr, sizeof(*vhdr));
 187                                        mark_buffer_dirty(bh);
 188                                        brelse(bh);
 189                                } else
 190                                        printk(KERN_WARNING "hfs: backup not found!\n");
 191                        }
 192                }
 193                HFSPLUS_SB(sb).flags &= ~HFSPLUS_SB_WRITEBACKUP;
 194        }
 195}
 196
 197static void hfsplus_put_super(struct super_block *sb)
 198{
 199        dprint(DBG_SUPER, "hfsplus_put_super\n");
 200        if (!sb->s_fs_info)
 201                return;
 202        if (!(sb->s_flags & MS_RDONLY) && HFSPLUS_SB(sb).s_vhdr) {
 203                struct hfsplus_vh *vhdr = HFSPLUS_SB(sb).s_vhdr;
 204
 205                vhdr->modify_date = hfsp_now2mt();
 206                vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_UNMNT);
 207                vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_INCNSTNT);
 208                mark_buffer_dirty(HFSPLUS_SB(sb).s_vhbh);
 209                sync_dirty_buffer(HFSPLUS_SB(sb).s_vhbh);
 210        }
 211
 212        hfs_btree_close(HFSPLUS_SB(sb).cat_tree);
 213        hfs_btree_close(HFSPLUS_SB(sb).ext_tree);
 214        iput(HFSPLUS_SB(sb).alloc_file);
 215        iput(HFSPLUS_SB(sb).hidden_dir);
 216        brelse(HFSPLUS_SB(sb).s_vhbh);
 217        if (HFSPLUS_SB(sb).nls)
 218                unload_nls(HFSPLUS_SB(sb).nls);
 219        kfree(sb->s_fs_info);
 220        sb->s_fs_info = NULL;
 221}
 222
 223static int hfsplus_statfs(struct dentry *dentry, struct kstatfs *buf)
 224{
 225        struct super_block *sb = dentry->d_sb;
 226        u64 id = huge_encode_dev(sb->s_bdev->bd_dev);
 227
 228        buf->f_type = HFSPLUS_SUPER_MAGIC;
 229        buf->f_bsize = sb->s_blocksize;
 230        buf->f_blocks = HFSPLUS_SB(sb).total_blocks << HFSPLUS_SB(sb).fs_shift;
 231        buf->f_bfree = HFSPLUS_SB(sb).free_blocks << HFSPLUS_SB(sb).fs_shift;
 232        buf->f_bavail = buf->f_bfree;
 233        buf->f_files = 0xFFFFFFFF;
 234        buf->f_ffree = 0xFFFFFFFF - HFSPLUS_SB(sb).next_cnid;
 235        buf->f_fsid.val[0] = (u32)id;
 236        buf->f_fsid.val[1] = (u32)(id >> 32);
 237        buf->f_namelen = HFSPLUS_MAX_STRLEN;
 238
 239        return 0;
 240}
 241
 242static int hfsplus_remount(struct super_block *sb, int *flags, char *data)
 243{
 244        if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
 245                return 0;
 246        if (!(*flags & MS_RDONLY)) {
 247                struct hfsplus_vh *vhdr = HFSPLUS_SB(sb).s_vhdr;
 248                struct hfsplus_sb_info sbi;
 249
 250                memset(&sbi, 0, sizeof(struct hfsplus_sb_info));
 251                sbi.nls = HFSPLUS_SB(sb).nls;
 252                if (!hfsplus_parse_options(data, &sbi))
 253                        return -EINVAL;
 254
 255                if (!(vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_UNMNT))) {
 256                        printk(KERN_WARNING "hfs: filesystem was not cleanly unmounted, "
 257                               "running fsck.hfsplus is recommended.  leaving read-only.\n");
 258                        sb->s_flags |= MS_RDONLY;
 259                        *flags |= MS_RDONLY;
 260                } else if (sbi.flags & HFSPLUS_SB_FORCE) {
 261                        /* nothing */
 262                } else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) {
 263                        printk(KERN_WARNING "hfs: filesystem is marked locked, leaving read-only.\n");
 264                        sb->s_flags |= MS_RDONLY;
 265                        *flags |= MS_RDONLY;
 266                } else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_JOURNALED)) {
 267                        printk(KERN_WARNING "hfs: filesystem is marked journaled, leaving read-only.\n");
 268                        sb->s_flags |= MS_RDONLY;
 269                        *flags |= MS_RDONLY;
 270                }
 271        }
 272        return 0;
 273}
 274
 275static const struct super_operations hfsplus_sops = {
 276        .alloc_inode    = hfsplus_alloc_inode,
 277        .destroy_inode  = hfsplus_destroy_inode,
 278        .write_inode    = hfsplus_write_inode,
 279        .clear_inode    = hfsplus_clear_inode,
 280        .put_super      = hfsplus_put_super,
 281        .write_super    = hfsplus_write_super,
 282        .statfs         = hfsplus_statfs,
 283        .remount_fs     = hfsplus_remount,
 284        .show_options   = hfsplus_show_options,
 285};
 286
 287static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
 288{
 289        struct hfsplus_vh *vhdr;
 290        struct hfsplus_sb_info *sbi;
 291        hfsplus_cat_entry entry;
 292        struct hfs_find_data fd;
 293        struct inode *root, *inode;
 294        struct qstr str;
 295        struct nls_table *nls = NULL;
 296        int err = -EINVAL;
 297
 298        sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
 299        if (!sbi)
 300                return -ENOMEM;
 301
 302        sb->s_fs_info = sbi;
 303        INIT_HLIST_HEAD(&sbi->rsrc_inodes);
 304        hfsplus_fill_defaults(sbi);
 305        if (!hfsplus_parse_options(data, sbi)) {
 306                printk(KERN_ERR "hfs: unable to parse mount options\n");
 307                err = -EINVAL;
 308                goto cleanup;
 309        }
 310
 311        /* temporarily use utf8 to correctly find the hidden dir below */
 312        nls = sbi->nls;
 313        sbi->nls = load_nls("utf8");
 314        if (!sbi->nls) {
 315                printk(KERN_ERR "hfs: unable to load nls for utf8\n");
 316                err = -EINVAL;
 317                goto cleanup;
 318        }
 319
 320        /* Grab the volume header */
 321        if (hfsplus_read_wrapper(sb)) {
 322                if (!silent)
 323                        printk(KERN_WARNING "hfs: unable to find HFS+ superblock\n");
 324                err = -EINVAL;
 325                goto cleanup;
 326        }
 327        vhdr = HFSPLUS_SB(sb).s_vhdr;
 328
 329        /* Copy parts of the volume header into the superblock */
 330        sb->s_magic = HFSPLUS_VOLHEAD_SIG;
 331        if (be16_to_cpu(vhdr->version) < HFSPLUS_MIN_VERSION ||
 332            be16_to_cpu(vhdr->version) > HFSPLUS_CURRENT_VERSION) {
 333                printk(KERN_ERR "hfs: wrong filesystem version\n");
 334                goto cleanup;
 335        }
 336        HFSPLUS_SB(sb).total_blocks = be32_to_cpu(vhdr->total_blocks);
 337        HFSPLUS_SB(sb).free_blocks = be32_to_cpu(vhdr->free_blocks);
 338        HFSPLUS_SB(sb).next_alloc = be32_to_cpu(vhdr->next_alloc);
 339        HFSPLUS_SB(sb).next_cnid = be32_to_cpu(vhdr->next_cnid);
 340        HFSPLUS_SB(sb).file_count = be32_to_cpu(vhdr->file_count);
 341        HFSPLUS_SB(sb).folder_count = be32_to_cpu(vhdr->folder_count);
 342        HFSPLUS_SB(sb).data_clump_blocks = be32_to_cpu(vhdr->data_clump_sz) >> HFSPLUS_SB(sb).alloc_blksz_shift;
 343        if (!HFSPLUS_SB(sb).data_clump_blocks)
 344                HFSPLUS_SB(sb).data_clump_blocks = 1;
 345        HFSPLUS_SB(sb).rsrc_clump_blocks = be32_to_cpu(vhdr->rsrc_clump_sz) >> HFSPLUS_SB(sb).alloc_blksz_shift;
 346        if (!HFSPLUS_SB(sb).rsrc_clump_blocks)
 347                HFSPLUS_SB(sb).rsrc_clump_blocks = 1;
 348
 349        /* Set up operations so we can load metadata */
 350        sb->s_op = &hfsplus_sops;
 351        sb->s_maxbytes = MAX_LFS_FILESIZE;
 352
 353        if (!(vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_UNMNT))) {
 354                printk(KERN_WARNING "hfs: Filesystem was not cleanly unmounted, "
 355                       "running fsck.hfsplus is recommended.  mounting read-only.\n");
 356                sb->s_flags |= MS_RDONLY;
 357        } else if (sbi->flags & HFSPLUS_SB_FORCE) {
 358                /* nothing */
 359        } else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) {
 360                printk(KERN_WARNING "hfs: Filesystem is marked locked, mounting read-only.\n");
 361                sb->s_flags |= MS_RDONLY;
 362        } else if ((vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_JOURNALED)) && !(sb->s_flags & MS_RDONLY)) {
 363                printk(KERN_WARNING "hfs: write access to a journaled filesystem is not supported, "
 364                       "use the force option at your own risk, mounting read-only.\n");
 365                sb->s_flags |= MS_RDONLY;
 366        }
 367        sbi->flags &= ~HFSPLUS_SB_FORCE;
 368
 369        /* Load metadata objects (B*Trees) */
 370        HFSPLUS_SB(sb).ext_tree = hfs_btree_open(sb, HFSPLUS_EXT_CNID);
 371        if (!HFSPLUS_SB(sb).ext_tree) {
 372                printk(KERN_ERR "hfs: failed to load extents file\n");
 373                goto cleanup;
 374        }
 375        HFSPLUS_SB(sb).cat_tree = hfs_btree_open(sb, HFSPLUS_CAT_CNID);
 376        if (!HFSPLUS_SB(sb).cat_tree) {
 377                printk(KERN_ERR "hfs: failed to load catalog file\n");
 378                goto cleanup;
 379        }
 380
 381        inode = hfsplus_iget(sb, HFSPLUS_ALLOC_CNID);
 382        if (IS_ERR(inode)) {
 383                printk(KERN_ERR "hfs: failed to load allocation file\n");
 384                err = PTR_ERR(inode);
 385                goto cleanup;
 386        }
 387        HFSPLUS_SB(sb).alloc_file = inode;
 388
 389        /* Load the root directory */
 390        root = hfsplus_iget(sb, HFSPLUS_ROOT_CNID);
 391        if (IS_ERR(root)) {
 392                printk(KERN_ERR "hfs: failed to load root directory\n");
 393                err = PTR_ERR(root);
 394                goto cleanup;
 395        }
 396        sb->s_root = d_alloc_root(root);
 397        if (!sb->s_root) {
 398                iput(root);
 399                err = -ENOMEM;
 400                goto cleanup;
 401        }
 402        sb->s_root->d_op = &hfsplus_dentry_operations;
 403
 404        str.len = sizeof(HFSP_HIDDENDIR_NAME) - 1;
 405        str.name = HFSP_HIDDENDIR_NAME;
 406        hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd);
 407        hfsplus_cat_build_key(sb, fd.search_key, HFSPLUS_ROOT_CNID, &str);
 408        if (!hfs_brec_read(&fd, &entry, sizeof(entry))) {
 409                hfs_find_exit(&fd);
 410                if (entry.type != cpu_to_be16(HFSPLUS_FOLDER))
 411                        goto cleanup;
 412                inode = hfsplus_iget(sb, be32_to_cpu(entry.folder.id));
 413                if (IS_ERR(inode)) {
 414                        err = PTR_ERR(inode);
 415                        goto cleanup;
 416                }
 417                HFSPLUS_SB(sb).hidden_dir = inode;
 418        } else
 419                hfs_find_exit(&fd);
 420
 421        if (sb->s_flags & MS_RDONLY)
 422                goto out;
 423
 424        /* H+LX == hfsplusutils, H+Lx == this driver, H+lx is unused
 425         * all three are registered with Apple for our use
 426         */
 427        vhdr->last_mount_vers = cpu_to_be32(HFSP_MOUNT_VERSION);
 428        vhdr->modify_date = hfsp_now2mt();
 429        be32_add_cpu(&vhdr->write_count, 1);
 430        vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_UNMNT);
 431        vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_INCNSTNT);
 432        mark_buffer_dirty(HFSPLUS_SB(sb).s_vhbh);
 433        sync_dirty_buffer(HFSPLUS_SB(sb).s_vhbh);
 434
 435        if (!HFSPLUS_SB(sb).hidden_dir) {
 436                printk(KERN_DEBUG "hfs: create hidden dir...\n");
 437                HFSPLUS_SB(sb).hidden_dir = hfsplus_new_inode(sb, S_IFDIR);
 438                hfsplus_create_cat(HFSPLUS_SB(sb).hidden_dir->i_ino, sb->s_root->d_inode,
 439                                   &str, HFSPLUS_SB(sb).hidden_dir);
 440                mark_inode_dirty(HFSPLUS_SB(sb).hidden_dir);
 441        }
 442out:
 443        unload_nls(sbi->nls);
 444        sbi->nls = nls;
 445        return 0;
 446
 447cleanup:
 448        hfsplus_put_super(sb);
 449        if (nls)
 450                unload_nls(nls);
 451        return err;
 452}
 453
 454MODULE_AUTHOR("Brad Boyer");
 455MODULE_DESCRIPTION("Extended Macintosh Filesystem");
 456MODULE_LICENSE("GPL");
 457
 458static struct kmem_cache *hfsplus_inode_cachep;
 459
 460static struct inode *hfsplus_alloc_inode(struct super_block *sb)
 461{
 462        struct hfsplus_inode_info *i;
 463
 464        i = kmem_cache_alloc(hfsplus_inode_cachep, GFP_KERNEL);
 465        return i ? &i->vfs_inode : NULL;
 466}
 467
 468static void hfsplus_destroy_inode(struct inode *inode)
 469{
 470        kmem_cache_free(hfsplus_inode_cachep, &HFSPLUS_I(inode));
 471}
 472
 473#define HFSPLUS_INODE_SIZE      sizeof(struct hfsplus_inode_info)
 474
 475static int hfsplus_get_sb(struct file_system_type *fs_type,
 476                          int flags, const char *dev_name, void *data,
 477                          struct vfsmount *mnt)
 478{
 479        return get_sb_bdev(fs_type, flags, dev_name, data, hfsplus_fill_super,
 480                           mnt);
 481}
 482
 483static struct file_system_type hfsplus_fs_type = {
 484        .owner          = THIS_MODULE,
 485        .name           = "hfsplus",
 486        .get_sb         = hfsplus_get_sb,
 487        .kill_sb        = kill_block_super,
 488        .fs_flags       = FS_REQUIRES_DEV,
 489};
 490
 491static void hfsplus_init_once(void *p)
 492{
 493        struct hfsplus_inode_info *i = p;
 494
 495        inode_init_once(&i->vfs_inode);
 496}
 497
 498static int __init init_hfsplus_fs(void)
 499{
 500        int err;
 501
 502        hfsplus_inode_cachep = kmem_cache_create("hfsplus_icache",
 503                HFSPLUS_INODE_SIZE, 0, SLAB_HWCACHE_ALIGN,
 504                hfsplus_init_once);
 505        if (!hfsplus_inode_cachep)
 506                return -ENOMEM;
 507        err = register_filesystem(&hfsplus_fs_type);
 508        if (err)
 509                kmem_cache_destroy(hfsplus_inode_cachep);
 510        return err;
 511}
 512
 513static void __exit exit_hfsplus_fs(void)
 514{
 515        unregister_filesystem(&hfsplus_fs_type);
 516        kmem_cache_destroy(hfsplus_inode_cachep);
 517}
 518
 519module_init(init_hfsplus_fs)
 520module_exit(exit_hfsplus_fs)
 521
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.