From: Gustav Munkby <grddev@gmail.com>
To: Christoph Hellwig <hch@lst.de>
Cc: linux-fsdevel@vger.kernel.org
Subject: [PATCH] hfsplus: read support for directory hardlinks
Date: Fri, 29 Apr 2011 12:20:52 +0200 [thread overview]
Message-ID: <1304072452-6590-1-git-send-email-grddev@gmail.com> (raw)
With OS X 10.5 in general, and Time Machine backups in particular,
Apple added support for directory hardlinks in HFS+. As for file
hardlinks, directory hardlinks are represented by normal files with
special creator and type attributes, storing an pseudo inode number
in the link count, referencing a secret file in a special folder.
This patch extends the existing lookup of file hardlinks to also
support folder hardlinks.
Signed-off-by: Gustav Munkby <grddev@gmail.com>
---
fs/hfsplus/catalog.c | 2 +-
fs/hfsplus/dir.c | 59 +++++++++++++++++++++++++++++++++++----------
fs/hfsplus/hfsplus_fs.h | 1 +
fs/hfsplus/hfsplus_raw.h | 5 ++++
fs/hfsplus/super.c | 55 +++++++++++++++++++++++++++++-------------
5 files changed, 91 insertions(+), 31 deletions(-)
diff --git a/fs/hfsplus/catalog.c b/fs/hfsplus/catalog.c
index b4ba1b3..e12919a 100644
--- a/fs/hfsplus/catalog.c
+++ b/fs/hfsplus/catalog.c
@@ -109,7 +109,7 @@ static int hfsplus_cat_build_record(hfsplus_cat_entry *entry,
folder->attribute_mod_date =
folder->access_date = hfsp_now2mt();
hfsplus_cat_set_perms(inode, &folder->permissions);
- if (inode == sbi->hidden_dir)
+ if (inode == sbi->hidden_dir || inode == sbi->alias_dir)
/* invisible and namelocked */
folder->user_info.frFlags = cpu_to_be16(0x5000);
return sizeof(*folder);
diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c
index 4df5059..eadff10 100644
--- a/fs/hfsplus/dir.c
+++ b/fs/hfsplus/dir.c
@@ -23,6 +23,33 @@ static inline void hfsplus_instantiate(struct dentry *dentry,
d_instantiate(dentry, inode);
}
+static inline u32 hfsplus_hardlink_type(struct super_block *sb,
+ struct hfsplus_cat_file *file)
+{
+ struct inode *hdir = HFSPLUS_SB(sb)->hidden_dir;
+ struct inode *adir = HFSPLUS_SB(sb)->alias_dir;
+ struct inode *root = sb->s_root->d_inode;
+ u32 fdType = be32_to_cpu(file->user_info.fdType);
+ u32 fdCreator = be32_to_cpu(file->user_info.fdCreator);
+ __be32 create_date = file->create_date;
+
+ if (hdir && fdType == HFSP_HARDLINK_TYPE &&
+ fdCreator == HFSP_HFSPLUS_CREATOR &&
+ (create_date == HFSPLUS_I(root)->create_date ||
+ create_date == HFSPLUS_I(hdir)->create_date))
+ return fdType;
+
+ /* Apple's Time Machine creates folder hardlinks
+ * similarly to normal file hardlinks.
+ */
+ if (adir && fdType == HFSP_FOLDER_ALIAS_TYPE &&
+ fdCreator == HFSP_MACS_CREATOR &&
+ be32_to_cpu(file->permissions.dev) >= 127)
+ return fdType;
+
+ return 0;
+}
+
/* Find the entry inside dir named dentry->d_name */
static struct dentry *hfsplus_lookup(struct inode *dir, struct dentry *dentry,
struct nameidata *nd)
@@ -34,6 +61,7 @@ static struct dentry *hfsplus_lookup(struct inode *dir, struct dentry *dentry,
int err;
u32 cnid, linkid = 0;
u16 type;
+ u32 fdType;
sb = dir->i_sb;
@@ -65,19 +93,20 @@ again:
goto fail;
}
cnid = be32_to_cpu(entry.file.id);
- if (entry.file.user_info.fdType ==
- cpu_to_be32(HFSP_HARDLINK_TYPE) &&
- entry.file.user_info.fdCreator ==
- cpu_to_be32(HFSP_HFSPLUS_CREATOR) &&
- (entry.file.create_date ==
- HFSPLUS_I(HFSPLUS_SB(sb)->hidden_dir)->
- create_date ||
- entry.file.create_date ==
- HFSPLUS_I(sb->s_root->d_inode)->
- create_date) &&
- HFSPLUS_SB(sb)->hidden_dir) {
+ fdType = hfsplus_hardlink_type(sb, &entry.file);
+ if (fdType) {
struct qstr str;
char name[32];
+ struct inode *dir;
+ char *namefmt;
+
+ if (fdType == HFSP_FOLDER_ALIAS_TYPE) {
+ dir = HFSPLUS_SB(sb)->alias_dir;
+ namefmt = "dir_%d";
+ } else {
+ dir = HFSPLUS_SB(sb)->hidden_dir;
+ namefmt = "iNode%d";
+ }
if (dentry->d_fsdata) {
/*
@@ -90,10 +119,10 @@ again:
dentry->d_fsdata = (void *)(unsigned long)cnid;
linkid =
be32_to_cpu(entry.file.permissions.dev);
- str.len = sprintf(name, "iNode%d", linkid);
+ str.len = sprintf(name, namefmt, linkid);
str.name = name;
hfsplus_cat_build_key(sb, fd.search_key,
- HFSPLUS_SB(sb)->hidden_dir->i_ino,
+ dir->i_ino,
&str);
goto again;
}
@@ -195,6 +224,10 @@ static int hfsplus_readdir(struct file *filp, void *dirent, filldir_t filldir)
HFSPLUS_SB(sb)->hidden_dir->i_ino ==
be32_to_cpu(entry.folder.id))
goto next;
+ if (HFSPLUS_SB(sb)->alias_dir &&
+ HFSPLUS_SB(sb)->alias_dir->i_ino ==
+ be32_to_cpu(entry.folder.id))
+ goto next;
if (filldir(dirent, strbuf, len, filp->f_pos,
be32_to_cpu(entry.folder.id), DT_DIR))
break;
diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h
index d685752..42171ae 100644
--- a/fs/hfsplus/hfsplus_fs.h
+++ b/fs/hfsplus/hfsplus_fs.h
@@ -117,6 +117,7 @@ struct hfsplus_sb_info {
struct hfs_btree *attr_tree;
struct inode *alloc_file;
struct inode *hidden_dir;
+ struct inode *alias_dir;
struct nls_table *nls;
/* Runtime variables */
diff --git a/fs/hfsplus/hfsplus_raw.h b/fs/hfsplus/hfsplus_raw.h
index 927cdd6..c6d8859 100644
--- a/fs/hfsplus/hfsplus_raw.h
+++ b/fs/hfsplus/hfsplus_raw.h
@@ -36,6 +36,11 @@
#define HFSP_WRAPOFF_EMBEDSIG 0x7C
#define HFSP_WRAPOFF_EMBEDEXT 0x7E
+#define HFSP_ALIASDIR_NAME ".HFS+ Private Directory Data\r"
+
+#define HFSP_FOLDER_ALIAS_TYPE 0x66647270 /* 'fdrp' */
+#define HFSP_MACS_CREATOR 0x4d414353 /* 'MACS' */
+
#define HFSP_HIDDENDIR_NAME \
"\xe2\x90\x80\xe2\x90\x80\xe2\x90\x80\xe2\x90\x80HFS+ Private Data"
diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c
index b49b555..1ba6618 100644
--- a/fs/hfsplus/super.c
+++ b/fs/hfsplus/super.c
@@ -251,6 +251,7 @@ static void hfsplus_put_super(struct super_block *sb)
hfs_btree_close(sbi->ext_tree);
iput(sbi->alloc_file);
iput(sbi->hidden_dir);
+ iput(sbi->alias_dir);
kfree(sbi->s_vhdr);
kfree(sbi->s_backup_vhdr);
unload_nls(sbi->nls);
@@ -329,12 +330,34 @@ static const struct super_operations hfsplus_sops = {
.show_options = hfsplus_show_options,
};
+static int hfsplus_find_hidden_folder(struct super_block *sb,
+ struct qstr *name,
+ struct inode **inode)
+{
+ hfsplus_cat_entry entry;
+ struct hfs_find_data fd;
+
+ hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
+ hfsplus_cat_build_key(sb, fd.search_key, HFSPLUS_ROOT_CNID, name);
+ if (hfs_brec_read(&fd, &entry, sizeof(entry))) {
+ hfs_find_exit(&fd);
+ return 0;
+ }
+
+ hfs_find_exit(&fd);
+ if (entry.type != cpu_to_be16(HFSPLUS_FOLDER))
+ return -EINVAL;
+
+ *inode = hfsplus_iget(sb, be32_to_cpu(entry.folder.id));
+ if (IS_ERR(*inode))
+ return PTR_ERR(*inode);
+ return 0;
+}
+
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;
@@ -447,20 +470,16 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
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);
+
+ err = hfsplus_find_hidden_folder(sb, &str, &sbi->hidden_dir);
+ if (err)
+ goto out_put_root;
+
+ str.len = sizeof(HFSP_ALIASDIR_NAME) - 1;
+ str.name = HFSP_ALIASDIR_NAME;
+ err = hfsplus_find_hidden_folder(sb, &str, &sbi->alias_dir);
+ if (err)
+ goto out_put_hidden_dir;
if (!(sb->s_flags & MS_RDONLY)) {
/*
@@ -490,13 +509,15 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
sb->s_root = d_alloc_root(root);
if (!sb->s_root) {
err = -ENOMEM;
- goto out_put_hidden_dir;
+ goto out_put_alias_dir;
}
unload_nls(sbi->nls);
sbi->nls = nls;
return 0;
+out_put_alias_dir:
+ iput(sbi->alias_dir);
out_put_hidden_dir:
iput(sbi->hidden_dir);
out_put_root:
--
1.7.5
next reply other threads:[~2011-04-29 10:21 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-04-29 10:20 Gustav Munkby [this message]
2011-05-02 8:46 ` [PATCH] hfsplus: read support for directory hardlinks Christoph Hellwig
2011-05-02 12:40 ` Al Viro
2011-05-03 14:11 ` Gustav Munkby
2011-05-03 14:26 ` [PATCH] hfsplus: disable rename of " Gustav Munkby
2011-05-03 17:10 ` Andreas Dilger
2011-05-03 21:29 ` Gustav Munkby
2011-05-04 9:30 ` [PATCH] hfsplus: read support for " Christoph Hellwig
2011-05-04 15:04 ` Gustav Munkby
2011-05-19 11:09 ` Christoph Hellwig
2011-05-19 15:57 ` Gustav Munkby
2011-05-20 10:30 ` [PATCH v2] hfsplus: readonly " Gustav Munkby
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1304072452-6590-1-git-send-email-grddev@gmail.com \
--to=grddev@gmail.com \
--cc=hch@lst.de \
--cc=linux-fsdevel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).