All of lore.kernel.org
 help / color / mirror / Atom feed
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


             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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.