All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] hfsplus: read support for directory hardlinks
@ 2011-04-29 10:20 Gustav Munkby
  2011-05-02  8:46 ` Christoph Hellwig
  0 siblings, 1 reply; 12+ messages in thread
From: Gustav Munkby @ 2011-04-29 10:20 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-fsdevel

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


^ permalink raw reply related	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2011-05-20 10:31 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-04-29 10:20 [PATCH] hfsplus: read support for directory hardlinks Gustav Munkby
2011-05-02  8:46 ` 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

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.