From 7f6df1ee70ffdd7ac75f6990463ef3a48582ad8e Mon Sep 17 00:00:00 2001 From: =?utf-8?q?Diego=20E.=20'Flameeyes'=20Petten=C3=B2?= Date: Thu, 4 Dec 2008 13:32:06 +0100 Subject: [PATCH] Add basic export support to HFS+ filesystem. MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit The functions' skeleton is taken from ext2/super.c and seems to work fine for R/W access to HFS+ non-journaled case-sensitive filesystems. Signed-off-by: Diego E. 'Flameeyes' Pettenò Cc: Roman Zippel Cc: Christoph Hellwig Cc: Neil Brown --- fs/hfsplus/Makefile | 3 +- fs/hfsplus/export.c | 118 +++++++++++++++++++++++++++++++++++++++++++++++ fs/hfsplus/hfsplus_fs.h | 3 + fs/hfsplus/super.c | 1 + 4 files changed, 124 insertions(+), 1 deletions(-) create mode 100644 fs/hfsplus/export.c diff --git a/fs/hfsplus/Makefile b/fs/hfsplus/Makefile index 3cc0df7..374131e 100644 --- a/fs/hfsplus/Makefile +++ b/fs/hfsplus/Makefile @@ -5,5 +5,6 @@ obj-$(CONFIG_HFSPLUS_FS) += hfsplus.o hfsplus-objs := super.o options.o inode.o ioctl.o extents.o catalog.o dir.o btree.o \ - bnode.o brec.o bfind.o tables.o unicode.o wrapper.o bitmap.o part_tbl.o + bnode.o brec.o bfind.o tables.o unicode.o wrapper.o bitmap.o part_tbl.o \ + export.o diff --git a/fs/hfsplus/export.c b/fs/hfsplus/export.c new file mode 100644 index 0000000..91e5cb9 --- /dev/null +++ b/fs/hfsplus/export.c @@ -0,0 +1,118 @@ +/* + * linux/fs/hfsplus/export.c + * + * Copyright (C) 2001 + * Brad Boyer (flar@allandria.com) + * (C) 2003 Ardis Technologies + * (C) 2008 Diego E. Pettenò + * + * Export for NFS + */ + +#include +#include +#include + +#include "hfsplus_fs.h" +#include "hfsplus_raw.h" + +/* + * This is very different from most get_parent functions, since HFS+ + * does not have a ".." entry on their directories. + * + * Instead, the filesystem uses Catalog Thread Records to connect + * directories and files to their ancestors. + */ +static struct dentry *hfsplus_get_parent(struct dentry *child) +{ + struct super_block *sb; + hfsplus_cat_entry entry; + struct hfs_find_data fd; + struct inode *inode; + int err; + + sb = child->d_sb; + + hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd); + hfsplus_cat_build_key(sb, fd.search_key, child->d_inode->i_ino, NULL); + err = hfs_brec_find(&fd); + + if (err) { + printk(KERN_ERR "hfs: unable to find child, call the police\n"); + goto out; + } + + hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength); + if ( be16_to_cpu(entry.type) != HFSPLUS_FOLDER_THREAD) { + printk(KERN_ERR "hfs: bad catalog folder thread\n"); + err = -EIO; + goto out; + } + + if (fd.entrylength < HFSPLUS_MIN_THREAD_SZ) { + printk(KERN_ERR "hfs: truncated catalog thread\n"); + err = -EIO; + goto out; + } + + hfs_find_exit(&fd); + + inode = hfsplus_iget(child->d_sb, be32_to_cpu(entry.thread.parentID)); + if (IS_ERR(inode)) { + printk(KERN_ERR "hfs: unable to find parent, call the social services\n"); + return ERR_CAST(inode); + } + + return d_obtain_alias(inode); + +out: + hfs_find_exit(&fd); + return ERR_PTR(err); +} + +static struct inode *hfsplus_export_get_inode(struct super_block *sb, + u64 ino, u32 generation) +{ + struct inode *inode; + + if (ino < HFSPLUS_FIRSTUSER_CNID && ino != HFSPLUS_ROOT_CNID) + return ERR_PTR(-ESTALE); + + inode = hfsplus_iget(sb, ino); + if (IS_ERR(inode)) + return ERR_CAST(inode); + + /* probably superfluous but it does not seem to hurt */ + if (generation && inode->i_generation != generation) { + /* we didn't find the right inode.. */ + iput(inode); + return ERR_PTR(-ESTALE); + } + return inode; +} + +static struct dentry *hfsplus_fh_to_dentry(struct super_block *sb, struct fid *fid, + int fh_len, int fh_type) + +{ + return generic_fh_to_dentry(sb, fid, fh_len, fh_type, + hfsplus_export_get_inode); +} + +static struct dentry *hfsplus_fh_to_parent(struct super_block *sb, struct fid *fid, + int fh_len, int fh_type) + +{ + return generic_fh_to_parent(sb, fid, fh_len, fh_type, + hfsplus_export_get_inode); +} + +/* Yes, most of these are left as NULL!! + * A NULL value implies the default, which works with hfs+-like file + * systems, but can be improved upon. + */ +const struct export_operations hfsplus_export_ops = { + .get_parent = hfsplus_get_parent, + .fh_to_dentry = hfsplus_fh_to_dentry, + .fh_to_parent = hfsplus_fh_to_parent, +}; diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index f027a90..7c78525 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h @@ -371,6 +371,9 @@ int hfsplus_read_wrapper(struct super_block *); int hfs_part_find(struct super_block *, sector_t *, sector_t *); +/* export.c */ +extern const struct export_operations hfsplus_export_ops; + /* access macros */ /* static inline struct hfsplus_sb_info *HFSPLUS_SB(struct super_block *sb) diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index eb74531..b5d1153 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -345,6 +345,7 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) /* Set up operations so we can load metadata */ sb->s_op = &hfsplus_sops; + sb->s_export_op = &hfsplus_export_ops; sb->s_maxbytes = MAX_LFS_FILESIZE; if (!(vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_UNMNT))) { -- 1.6.0.6