Przeglądaj źródła

Merge git://git.kernel.org/pub/scm/linux/kernel/git/pkl/squashfs-linus

* git://git.kernel.org/pub/scm/linux/kernel/git/pkl/squashfs-linus:
  squashfs: update documentation to include description of xattr layout
  squashfs: fix name reading in squashfs_xattr_get
  squashfs: constify xattr handlers
  squashfs: xattr fix sparse warnings
  squashfs: xattr_lookup sparse fix
  squashfs: add xattr support configure option
  squashfs: add new extended inode types
  squashfs: add support for xattr reading
  squashfs: add xattr id support
Linus Torvalds 15 lat temu
rodzic
commit
63a6440326

+ 27 - 5
Documentation/filesystems/squashfs.txt

@@ -38,7 +38,8 @@ Hard link support:		yes			no
 Real inode numbers:		yes			no
 Real inode numbers:		yes			no
 32-bit uids/gids:		yes			no
 32-bit uids/gids:		yes			no
 File creation time:		yes			no
 File creation time:		yes			no
-Xattr and ACL support:		no			no
+Xattr support:			yes			no
+ACL support:			no			no
 
 
 Squashfs compresses data, inodes and directories.  In addition, inode and
 Squashfs compresses data, inodes and directories.  In addition, inode and
 directory data are highly compacted, and packed on byte boundaries.  Each
 directory data are highly compacted, and packed on byte boundaries.  Each
@@ -58,7 +59,7 @@ obtained from this site also.
 3. SQUASHFS FILESYSTEM DESIGN
 3. SQUASHFS FILESYSTEM DESIGN
 -----------------------------
 -----------------------------
 
 
-A squashfs filesystem consists of seven parts, packed together on a byte
+A squashfs filesystem consists of a maximum of eight parts, packed together on a byte
 alignment:
 alignment:
 
 
 	 ---------------
 	 ---------------
@@ -80,6 +81,9 @@ alignment:
 	|---------------|
 	|---------------|
 	|    uid/gid	|
 	|    uid/gid	|
 	|  lookup table	|
 	|  lookup table	|
+	|---------------|
+	|     xattr     |
+	|     table	|
 	 ---------------
 	 ---------------
 
 
 Compressed data blocks are written to the filesystem as files are read from
 Compressed data blocks are written to the filesystem as files are read from
@@ -192,6 +196,26 @@ This table is stored compressed into metadata blocks.  A second index table is
 used to locate these.  This second index table for speed of access (and because
 used to locate these.  This second index table for speed of access (and because
 it is small) is read at mount time and cached in memory.
 it is small) is read at mount time and cached in memory.
 
 
+3.7 Xattr table
+---------------
+
+The xattr table contains extended attributes for each inode.  The xattrs
+for each inode are stored in a list, each list entry containing a type,
+name and value field.  The type field encodes the xattr prefix
+("user.", "trusted." etc) and it also encodes how the name/value fields
+should be interpreted.  Currently the type indicates whether the value
+is stored inline (in which case the value field contains the xattr value),
+or if it is stored out of line (in which case the value field stores a
+reference to where the actual value is stored).  This allows large values
+to be stored out of line improving scanning and lookup performance and it
+also allows values to be de-duplicated, the value being stored once, and
+all other occurences holding an out of line reference to that value.
+
+The xattr lists are packed into compressed 8K metadata blocks.
+To reduce overhead in inodes, rather than storing the on-disk
+location of the xattr list inside each inode, a 32-bit xattr id
+is stored.  This xattr id is mapped into the location of the xattr
+list using a second xattr id lookup table.
 
 
 4. TODOS AND OUTSTANDING ISSUES
 4. TODOS AND OUTSTANDING ISSUES
 -------------------------------
 -------------------------------
@@ -199,9 +223,7 @@ it is small) is read at mount time and cached in memory.
 4.1 Todo list
 4.1 Todo list
 -------------
 -------------
 
 
-Implement Xattr and ACL support.  The Squashfs 4.0 filesystem layout has hooks
-for these but the code has not been written.  Once the code has been written
-the existing layout should not require modification.
+Implement ACL support.
 
 
 4.2 Squashfs internal cache
 4.2 Squashfs internal cache
 ---------------------------
 ---------------------------

+ 11 - 0
fs/squashfs/Kconfig

@@ -26,6 +26,17 @@ config SQUASHFS
 
 
 	  If unsure, say N.
 	  If unsure, say N.
 
 
+config SQUASHFS_XATTRS
+	bool "Squashfs XATTR support"
+	depends on SQUASHFS
+	default n
+	help
+	  Saying Y here includes support for extended attributes (xattrs).
+	  Xattrs are name:value pairs associated with inodes by
+	  the kernel or by users (see the attr(5) manual page).
+
+	  If unsure, say N.
+
 config SQUASHFS_EMBEDDED
 config SQUASHFS_EMBEDDED
 
 
 	bool "Additional option for memory-constrained systems" 
 	bool "Additional option for memory-constrained systems" 

+ 2 - 0
fs/squashfs/Makefile

@@ -5,3 +5,5 @@
 obj-$(CONFIG_SQUASHFS) += squashfs.o
 obj-$(CONFIG_SQUASHFS) += squashfs.o
 squashfs-y += block.o cache.o dir.o export.o file.o fragment.o id.o inode.o
 squashfs-y += block.o cache.o dir.o export.o file.o fragment.o id.o inode.o
 squashfs-y += namei.o super.o symlink.o zlib_wrapper.o decompressor.o
 squashfs-y += namei.o super.o symlink.o zlib_wrapper.o decompressor.o
+squashfs-$(CONFIG_SQUASHFS_XATTRS) += xattr.o xattr_id.o
+

+ 85 - 7
fs/squashfs/inode.c

@@ -40,11 +40,13 @@
 
 
 #include <linux/fs.h>
 #include <linux/fs.h>
 #include <linux/vfs.h>
 #include <linux/vfs.h>
+#include <linux/xattr.h>
 
 
 #include "squashfs_fs.h"
 #include "squashfs_fs.h"
 #include "squashfs_fs_sb.h"
 #include "squashfs_fs_sb.h"
 #include "squashfs_fs_i.h"
 #include "squashfs_fs_i.h"
 #include "squashfs.h"
 #include "squashfs.h"
+#include "xattr.h"
 
 
 /*
 /*
  * Initialise VFS inode with the base inode information common to all
  * Initialise VFS inode with the base inode information common to all
@@ -111,6 +113,7 @@ int squashfs_read_inode(struct inode *inode, long long ino)
 	int err, type, offset = SQUASHFS_INODE_OFFSET(ino);
 	int err, type, offset = SQUASHFS_INODE_OFFSET(ino);
 	union squashfs_inode squashfs_ino;
 	union squashfs_inode squashfs_ino;
 	struct squashfs_base_inode *sqshb_ino = &squashfs_ino.base;
 	struct squashfs_base_inode *sqshb_ino = &squashfs_ino.base;
+	int xattr_id = SQUASHFS_INVALID_XATTR;
 
 
 	TRACE("Entered squashfs_read_inode\n");
 	TRACE("Entered squashfs_read_inode\n");
 
 
@@ -199,8 +202,10 @@ int squashfs_read_inode(struct inode *inode, long long ino)
 			frag_offset = 0;
 			frag_offset = 0;
 		}
 		}
 
 
+		xattr_id = le32_to_cpu(sqsh_ino->xattr);
 		inode->i_nlink = le32_to_cpu(sqsh_ino->nlink);
 		inode->i_nlink = le32_to_cpu(sqsh_ino->nlink);
 		inode->i_size = le64_to_cpu(sqsh_ino->file_size);
 		inode->i_size = le64_to_cpu(sqsh_ino->file_size);
+		inode->i_op = &squashfs_inode_ops;
 		inode->i_fop = &generic_ro_fops;
 		inode->i_fop = &generic_ro_fops;
 		inode->i_mode |= S_IFREG;
 		inode->i_mode |= S_IFREG;
 		inode->i_blocks = ((inode->i_size -
 		inode->i_blocks = ((inode->i_size -
@@ -251,6 +256,7 @@ int squashfs_read_inode(struct inode *inode, long long ino)
 		if (err < 0)
 		if (err < 0)
 			goto failed_read;
 			goto failed_read;
 
 
+		xattr_id = le32_to_cpu(sqsh_ino->xattr);
 		inode->i_nlink = le32_to_cpu(sqsh_ino->nlink);
 		inode->i_nlink = le32_to_cpu(sqsh_ino->nlink);
 		inode->i_size = le32_to_cpu(sqsh_ino->file_size);
 		inode->i_size = le32_to_cpu(sqsh_ino->file_size);
 		inode->i_op = &squashfs_dir_inode_ops;
 		inode->i_op = &squashfs_dir_inode_ops;
@@ -280,21 +286,33 @@ int squashfs_read_inode(struct inode *inode, long long ino)
 
 
 		inode->i_nlink = le32_to_cpu(sqsh_ino->nlink);
 		inode->i_nlink = le32_to_cpu(sqsh_ino->nlink);
 		inode->i_size = le32_to_cpu(sqsh_ino->symlink_size);
 		inode->i_size = le32_to_cpu(sqsh_ino->symlink_size);
-		inode->i_op = &page_symlink_inode_operations;
+		inode->i_op = &squashfs_symlink_inode_ops;
 		inode->i_data.a_ops = &squashfs_symlink_aops;
 		inode->i_data.a_ops = &squashfs_symlink_aops;
 		inode->i_mode |= S_IFLNK;
 		inode->i_mode |= S_IFLNK;
 		squashfs_i(inode)->start = block;
 		squashfs_i(inode)->start = block;
 		squashfs_i(inode)->offset = offset;
 		squashfs_i(inode)->offset = offset;
 
 
+		if (type == SQUASHFS_LSYMLINK_TYPE) {
+			__le32 xattr;
+
+			err = squashfs_read_metadata(sb, NULL, &block,
+						&offset, inode->i_size);
+			if (err < 0)
+				goto failed_read;
+			err = squashfs_read_metadata(sb, &xattr, &block,
+						&offset, sizeof(xattr));
+			if (err < 0)
+				goto failed_read;
+			xattr_id = le32_to_cpu(xattr);
+		}
+
 		TRACE("Symbolic link inode %x:%x, start_block %llx, offset "
 		TRACE("Symbolic link inode %x:%x, start_block %llx, offset "
 				"%x\n", SQUASHFS_INODE_BLK(ino), offset,
 				"%x\n", SQUASHFS_INODE_BLK(ino), offset,
 				block, offset);
 				block, offset);
 		break;
 		break;
 	}
 	}
 	case SQUASHFS_BLKDEV_TYPE:
 	case SQUASHFS_BLKDEV_TYPE:
-	case SQUASHFS_CHRDEV_TYPE:
-	case SQUASHFS_LBLKDEV_TYPE:
-	case SQUASHFS_LCHRDEV_TYPE: {
+	case SQUASHFS_CHRDEV_TYPE: {
 		struct squashfs_dev_inode *sqsh_ino = &squashfs_ino.dev;
 		struct squashfs_dev_inode *sqsh_ino = &squashfs_ino.dev;
 		unsigned int rdev;
 		unsigned int rdev;
 
 
@@ -315,10 +333,32 @@ int squashfs_read_inode(struct inode *inode, long long ino)
 				SQUASHFS_INODE_BLK(ino), offset, rdev);
 				SQUASHFS_INODE_BLK(ino), offset, rdev);
 		break;
 		break;
 	}
 	}
+	case SQUASHFS_LBLKDEV_TYPE:
+	case SQUASHFS_LCHRDEV_TYPE: {
+		struct squashfs_ldev_inode *sqsh_ino = &squashfs_ino.ldev;
+		unsigned int rdev;
+
+		err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
+				sizeof(*sqsh_ino));
+		if (err < 0)
+			goto failed_read;
+
+		if (type == SQUASHFS_LCHRDEV_TYPE)
+			inode->i_mode |= S_IFCHR;
+		else
+			inode->i_mode |= S_IFBLK;
+		xattr_id = le32_to_cpu(sqsh_ino->xattr);
+		inode->i_op = &squashfs_inode_ops;
+		inode->i_nlink = le32_to_cpu(sqsh_ino->nlink);
+		rdev = le32_to_cpu(sqsh_ino->rdev);
+		init_special_inode(inode, inode->i_mode, new_decode_dev(rdev));
+
+		TRACE("Device inode %x:%x, rdev %x\n",
+				SQUASHFS_INODE_BLK(ino), offset, rdev);
+		break;
+	}
 	case SQUASHFS_FIFO_TYPE:
 	case SQUASHFS_FIFO_TYPE:
-	case SQUASHFS_SOCKET_TYPE:
-	case SQUASHFS_LFIFO_TYPE:
-	case SQUASHFS_LSOCKET_TYPE: {
+	case SQUASHFS_SOCKET_TYPE: {
 		struct squashfs_ipc_inode *sqsh_ino = &squashfs_ino.ipc;
 		struct squashfs_ipc_inode *sqsh_ino = &squashfs_ino.ipc;
 
 
 		err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
 		err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
@@ -334,14 +374,52 @@ int squashfs_read_inode(struct inode *inode, long long ino)
 		init_special_inode(inode, inode->i_mode, 0);
 		init_special_inode(inode, inode->i_mode, 0);
 		break;
 		break;
 	}
 	}
+	case SQUASHFS_LFIFO_TYPE:
+	case SQUASHFS_LSOCKET_TYPE: {
+		struct squashfs_lipc_inode *sqsh_ino = &squashfs_ino.lipc;
+
+		err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
+				sizeof(*sqsh_ino));
+		if (err < 0)
+			goto failed_read;
+
+		if (type == SQUASHFS_LFIFO_TYPE)
+			inode->i_mode |= S_IFIFO;
+		else
+			inode->i_mode |= S_IFSOCK;
+		xattr_id = le32_to_cpu(sqsh_ino->xattr);
+		inode->i_op = &squashfs_inode_ops;
+		inode->i_nlink = le32_to_cpu(sqsh_ino->nlink);
+		init_special_inode(inode, inode->i_mode, 0);
+		break;
+	}
 	default:
 	default:
 		ERROR("Unknown inode type %d in squashfs_iget!\n", type);
 		ERROR("Unknown inode type %d in squashfs_iget!\n", type);
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
+	if (xattr_id != SQUASHFS_INVALID_XATTR && msblk->xattr_id_table) {
+		err = squashfs_xattr_lookup(sb, xattr_id,
+					&squashfs_i(inode)->xattr_count,
+					&squashfs_i(inode)->xattr_size,
+					&squashfs_i(inode)->xattr);
+		if (err < 0)
+			goto failed_read;
+		inode->i_blocks += ((squashfs_i(inode)->xattr_size - 1) >> 9)
+				+ 1;
+	} else
+		squashfs_i(inode)->xattr_count = 0;
+
 	return 0;
 	return 0;
 
 
 failed_read:
 failed_read:
 	ERROR("Unable to read inode 0x%llx\n", ino);
 	ERROR("Unable to read inode 0x%llx\n", ino);
 	return err;
 	return err;
 }
 }
+
+
+const struct inode_operations squashfs_inode_ops = {
+	.getxattr = generic_getxattr,
+	.listxattr = squashfs_listxattr
+};
+

+ 5 - 1
fs/squashfs/namei.c

@@ -57,11 +57,13 @@
 #include <linux/slab.h>
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/string.h>
 #include <linux/dcache.h>
 #include <linux/dcache.h>
+#include <linux/xattr.h>
 
 
 #include "squashfs_fs.h"
 #include "squashfs_fs.h"
 #include "squashfs_fs_sb.h"
 #include "squashfs_fs_sb.h"
 #include "squashfs_fs_i.h"
 #include "squashfs_fs_i.h"
 #include "squashfs.h"
 #include "squashfs.h"
+#include "xattr.h"
 
 
 /*
 /*
  * Lookup name in the directory index, returning the location of the metadata
  * Lookup name in the directory index, returning the location of the metadata
@@ -237,5 +239,7 @@ failed:
 
 
 
 
 const struct inode_operations squashfs_dir_inode_ops = {
 const struct inode_operations squashfs_dir_inode_ops = {
-	.lookup = squashfs_lookup
+	.lookup = squashfs_lookup,
+	.getxattr = generic_getxattr,
+	.listxattr = squashfs_listxattr
 };
 };

+ 11 - 1
fs/squashfs/squashfs.h

@@ -73,8 +73,11 @@ extern struct inode *squashfs_iget(struct super_block *, long long,
 				unsigned int);
 				unsigned int);
 extern int squashfs_read_inode(struct inode *, long long);
 extern int squashfs_read_inode(struct inode *, long long);
 
 
+/* xattr.c */
+extern ssize_t squashfs_listxattr(struct dentry *, char *, size_t);
+
 /*
 /*
- * Inodes, files and decompressor operations
+ * Inodes, files,  decompressor and xattr operations
  */
  */
 
 
 /* dir.c */
 /* dir.c */
@@ -86,11 +89,18 @@ extern const struct export_operations squashfs_export_ops;
 /* file.c */
 /* file.c */
 extern const struct address_space_operations squashfs_aops;
 extern const struct address_space_operations squashfs_aops;
 
 
+/* inode.c */
+extern const struct inode_operations squashfs_inode_ops;
+
 /* namei.c */
 /* namei.c */
 extern const struct inode_operations squashfs_dir_inode_ops;
 extern const struct inode_operations squashfs_dir_inode_ops;
 
 
 /* symlink.c */
 /* symlink.c */
 extern const struct address_space_operations squashfs_symlink_aops;
 extern const struct address_space_operations squashfs_symlink_aops;
+extern const struct inode_operations squashfs_symlink_inode_ops;
+
+/* xattr.c */
+extern const struct xattr_handler *squashfs_xattr_handlers[];
 
 
 /* zlib_wrapper.c */
 /* zlib_wrapper.c */
 extern const struct squashfs_decompressor squashfs_zlib_comp_ops;
 extern const struct squashfs_decompressor squashfs_zlib_comp_ops;

+ 75 - 1
fs/squashfs/squashfs_fs.h

@@ -46,6 +46,7 @@
 #define SQUASHFS_NAME_LEN		256
 #define SQUASHFS_NAME_LEN		256
 
 
 #define SQUASHFS_INVALID_FRAG		(0xffffffffU)
 #define SQUASHFS_INVALID_FRAG		(0xffffffffU)
+#define SQUASHFS_INVALID_XATTR		(0xffffffffU)
 #define SQUASHFS_INVALID_BLK		(-1LL)
 #define SQUASHFS_INVALID_BLK		(-1LL)
 
 
 /* Filesystem flags */
 /* Filesystem flags */
@@ -96,6 +97,13 @@
 #define SQUASHFS_LFIFO_TYPE		13
 #define SQUASHFS_LFIFO_TYPE		13
 #define SQUASHFS_LSOCKET_TYPE		14
 #define SQUASHFS_LSOCKET_TYPE		14
 
 
+/* Xattr types */
+#define SQUASHFS_XATTR_USER             0
+#define SQUASHFS_XATTR_TRUSTED          1
+#define SQUASHFS_XATTR_SECURITY         2
+#define SQUASHFS_XATTR_VALUE_OOL        256
+#define SQUASHFS_XATTR_PREFIX_MASK      0xff
+
 /* Flag whether block is compressed or uncompressed, bit is set if block is
 /* Flag whether block is compressed or uncompressed, bit is set if block is
  * uncompressed */
  * uncompressed */
 #define SQUASHFS_COMPRESSED_BIT		(1 << 15)
 #define SQUASHFS_COMPRESSED_BIT		(1 << 15)
@@ -174,6 +182,24 @@
 
 
 #define SQUASHFS_ID_BLOCK_BYTES(A)	(SQUASHFS_ID_BLOCKS(A) *\
 #define SQUASHFS_ID_BLOCK_BYTES(A)	(SQUASHFS_ID_BLOCKS(A) *\
 					sizeof(u64))
 					sizeof(u64))
+/* xattr id lookup table defines */
+#define SQUASHFS_XATTR_BYTES(A)		((A) * sizeof(struct squashfs_xattr_id))
+
+#define SQUASHFS_XATTR_BLOCK(A)		(SQUASHFS_XATTR_BYTES(A) / \
+					SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_XATTR_BLOCK_OFFSET(A)	(SQUASHFS_XATTR_BYTES(A) % \
+					SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_XATTR_BLOCKS(A)	((SQUASHFS_XATTR_BYTES(A) + \
+					SQUASHFS_METADATA_SIZE - 1) / \
+					SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_XATTR_BLOCK_BYTES(A)	(SQUASHFS_XATTR_BLOCKS(A) *\
+					sizeof(u64))
+#define SQUASHFS_XATTR_BLK(A)		((unsigned int) ((A) >> 16))
+
+#define SQUASHFS_XATTR_OFFSET(A)	((unsigned int) ((A) & 0xffff))
 
 
 /* cached data constants for filesystem */
 /* cached data constants for filesystem */
 #define SQUASHFS_CACHED_BLKS		8
 #define SQUASHFS_CACHED_BLKS		8
@@ -228,7 +254,7 @@ struct squashfs_super_block {
 	__le64			root_inode;
 	__le64			root_inode;
 	__le64			bytes_used;
 	__le64			bytes_used;
 	__le64			id_table_start;
 	__le64			id_table_start;
-	__le64			xattr_table_start;
+	__le64			xattr_id_table_start;
 	__le64			inode_table_start;
 	__le64			inode_table_start;
 	__le64			directory_table_start;
 	__le64			directory_table_start;
 	__le64			fragment_table_start;
 	__le64			fragment_table_start;
@@ -261,6 +287,17 @@ struct squashfs_ipc_inode {
 	__le32			nlink;
 	__le32			nlink;
 };
 };
 
 
+struct squashfs_lipc_inode {
+	__le16			inode_type;
+	__le16			mode;
+	__le16			uid;
+	__le16			guid;
+	__le32			mtime;
+	__le32	 		inode_number;
+	__le32			nlink;
+	__le32			xattr;
+};
+
 struct squashfs_dev_inode {
 struct squashfs_dev_inode {
 	__le16			inode_type;
 	__le16			inode_type;
 	__le16			mode;
 	__le16			mode;
@@ -272,6 +309,18 @@ struct squashfs_dev_inode {
 	__le32			rdev;
 	__le32			rdev;
 };
 };
 
 
+struct squashfs_ldev_inode {
+	__le16			inode_type;
+	__le16			mode;
+	__le16			uid;
+	__le16			guid;
+	__le32			mtime;
+	__le32	 		inode_number;
+	__le32			nlink;
+	__le32			rdev;
+	__le32			xattr;
+};
+
 struct squashfs_symlink_inode {
 struct squashfs_symlink_inode {
 	__le16			inode_type;
 	__le16			inode_type;
 	__le16			mode;
 	__le16			mode;
@@ -349,12 +398,14 @@ struct squashfs_ldir_inode {
 union squashfs_inode {
 union squashfs_inode {
 	struct squashfs_base_inode		base;
 	struct squashfs_base_inode		base;
 	struct squashfs_dev_inode		dev;
 	struct squashfs_dev_inode		dev;
+	struct squashfs_ldev_inode		ldev;
 	struct squashfs_symlink_inode		symlink;
 	struct squashfs_symlink_inode		symlink;
 	struct squashfs_reg_inode		reg;
 	struct squashfs_reg_inode		reg;
 	struct squashfs_lreg_inode		lreg;
 	struct squashfs_lreg_inode		lreg;
 	struct squashfs_dir_inode		dir;
 	struct squashfs_dir_inode		dir;
 	struct squashfs_ldir_inode		ldir;
 	struct squashfs_ldir_inode		ldir;
 	struct squashfs_ipc_inode		ipc;
 	struct squashfs_ipc_inode		ipc;
+	struct squashfs_lipc_inode		lipc;
 };
 };
 
 
 struct squashfs_dir_entry {
 struct squashfs_dir_entry {
@@ -377,4 +428,27 @@ struct squashfs_fragment_entry {
 	unsigned int		unused;
 	unsigned int		unused;
 };
 };
 
 
+struct squashfs_xattr_entry {
+	__le16			type;
+	__le16			size;
+	char			data[0];
+};
+
+struct squashfs_xattr_val {
+	__le32			vsize;
+	char			value[0];
+};
+
+struct squashfs_xattr_id {
+	__le64			xattr;
+	__le32			count;
+	__le32			size;
+};
+
+struct squashfs_xattr_id_table {
+	__le64			xattr_table_start;
+	__le32			xattr_ids;
+	__le32			unused;
+};
+
 #endif
 #endif

+ 3 - 0
fs/squashfs/squashfs_fs_i.h

@@ -26,6 +26,9 @@
 struct squashfs_inode_info {
 struct squashfs_inode_info {
 	u64		start;
 	u64		start;
 	int		offset;
 	int		offset;
+	u64		xattr;
+	unsigned int	xattr_size;
+	int		xattr_count;
 	union {
 	union {
 		struct {
 		struct {
 			u64		fragment_block;
 			u64		fragment_block;

+ 3 - 0
fs/squashfs/squashfs_fs_sb.h

@@ -61,6 +61,7 @@ struct squashfs_sb_info {
 	int					next_meta_index;
 	int					next_meta_index;
 	__le64					*id_table;
 	__le64					*id_table;
 	__le64					*fragment_index;
 	__le64					*fragment_index;
+	__le64					*xattr_id_table;
 	struct mutex				read_data_mutex;
 	struct mutex				read_data_mutex;
 	struct mutex				meta_index_mutex;
 	struct mutex				meta_index_mutex;
 	struct meta_index			*meta_index;
 	struct meta_index			*meta_index;
@@ -68,9 +69,11 @@ struct squashfs_sb_info {
 	__le64					*inode_lookup_table;
 	__le64					*inode_lookup_table;
 	u64					inode_table;
 	u64					inode_table;
 	u64					directory_table;
 	u64					directory_table;
+	u64					xattr_table;
 	unsigned int				block_size;
 	unsigned int				block_size;
 	unsigned short				block_log;
 	unsigned short				block_log;
 	long long				bytes_used;
 	long long				bytes_used;
 	unsigned int				inodes;
 	unsigned int				inodes;
+	int					xattr_ids;
 };
 };
 #endif
 #endif

+ 21 - 9
fs/squashfs/super.c

@@ -36,12 +36,14 @@
 #include <linux/init.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/module.h>
 #include <linux/magic.h>
 #include <linux/magic.h>
+#include <linux/xattr.h>
 
 
 #include "squashfs_fs.h"
 #include "squashfs_fs.h"
 #include "squashfs_fs_sb.h"
 #include "squashfs_fs_sb.h"
 #include "squashfs_fs_i.h"
 #include "squashfs_fs_i.h"
 #include "squashfs.h"
 #include "squashfs.h"
 #include "decompressor.h"
 #include "decompressor.h"
+#include "xattr.h"
 
 
 static struct file_system_type squashfs_fs_type;
 static struct file_system_type squashfs_fs_type;
 static const struct super_operations squashfs_super_ops;
 static const struct super_operations squashfs_super_ops;
@@ -82,7 +84,7 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
 	long long root_inode;
 	long long root_inode;
 	unsigned short flags;
 	unsigned short flags;
 	unsigned int fragments;
 	unsigned int fragments;
-	u64 lookup_table_start;
+	u64 lookup_table_start, xattr_id_table_start;
 	int err;
 	int err;
 
 
 	TRACE("Entered squashfs_fill_superblock\n");
 	TRACE("Entered squashfs_fill_superblock\n");
@@ -139,13 +141,6 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
 	if (msblk->decompressor == NULL)
 	if (msblk->decompressor == NULL)
 		goto failed_mount;
 		goto failed_mount;
 
 
-	/*
-	 * Check if there's xattrs in the filesystem.  These are not
-	 * supported in this version, so warn that they will be ignored.
-	 */
-	if (le64_to_cpu(sblk->xattr_table_start) != SQUASHFS_INVALID_BLK)
-		ERROR("Xattrs in filesystem, these will be ignored\n");
-
 	/* Check the filesystem does not extend beyond the end of the
 	/* Check the filesystem does not extend beyond the end of the
 	   block device */
 	   block device */
 	msblk->bytes_used = le64_to_cpu(sblk->bytes_used);
 	msblk->bytes_used = le64_to_cpu(sblk->bytes_used);
@@ -253,7 +248,7 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
 allocate_lookup_table:
 allocate_lookup_table:
 	lookup_table_start = le64_to_cpu(sblk->lookup_table_start);
 	lookup_table_start = le64_to_cpu(sblk->lookup_table_start);
 	if (lookup_table_start == SQUASHFS_INVALID_BLK)
 	if (lookup_table_start == SQUASHFS_INVALID_BLK)
-		goto allocate_root;
+		goto allocate_xattr_table;
 
 
 	/* Allocate and read inode lookup table */
 	/* Allocate and read inode lookup table */
 	msblk->inode_lookup_table = squashfs_read_inode_lookup_table(sb,
 	msblk->inode_lookup_table = squashfs_read_inode_lookup_table(sb,
@@ -266,6 +261,21 @@ allocate_lookup_table:
 
 
 	sb->s_export_op = &squashfs_export_ops;
 	sb->s_export_op = &squashfs_export_ops;
 
 
+allocate_xattr_table:
+	sb->s_xattr = squashfs_xattr_handlers;
+	xattr_id_table_start = le64_to_cpu(sblk->xattr_id_table_start);
+	if (xattr_id_table_start == SQUASHFS_INVALID_BLK)
+		goto allocate_root;
+
+	/* Allocate and read xattr id lookup table */
+	msblk->xattr_id_table = squashfs_read_xattr_id_table(sb,
+		xattr_id_table_start, &msblk->xattr_table, &msblk->xattr_ids);
+	if (IS_ERR(msblk->xattr_id_table)) {
+		err = PTR_ERR(msblk->xattr_id_table);
+		msblk->xattr_id_table = NULL;
+		if (err != -ENOTSUPP)
+			goto failed_mount;
+	}
 allocate_root:
 allocate_root:
 	root = new_inode(sb);
 	root = new_inode(sb);
 	if (!root) {
 	if (!root) {
@@ -301,6 +311,7 @@ failed_mount:
 	kfree(msblk->inode_lookup_table);
 	kfree(msblk->inode_lookup_table);
 	kfree(msblk->fragment_index);
 	kfree(msblk->fragment_index);
 	kfree(msblk->id_table);
 	kfree(msblk->id_table);
+	kfree(msblk->xattr_id_table);
 	kfree(sb->s_fs_info);
 	kfree(sb->s_fs_info);
 	sb->s_fs_info = NULL;
 	sb->s_fs_info = NULL;
 	kfree(sblk);
 	kfree(sblk);
@@ -355,6 +366,7 @@ static void squashfs_put_super(struct super_block *sb)
 		kfree(sbi->fragment_index);
 		kfree(sbi->fragment_index);
 		kfree(sbi->meta_index);
 		kfree(sbi->meta_index);
 		kfree(sbi->inode_lookup_table);
 		kfree(sbi->inode_lookup_table);
+		kfree(sbi->xattr_id_table);
 		kfree(sb->s_fs_info);
 		kfree(sb->s_fs_info);
 		sb->s_fs_info = NULL;
 		sb->s_fs_info = NULL;
 	}
 	}

+ 11 - 0
fs/squashfs/symlink.c

@@ -35,11 +35,13 @@
 #include <linux/kernel.h>
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/string.h>
 #include <linux/pagemap.h>
 #include <linux/pagemap.h>
+#include <linux/xattr.h>
 
 
 #include "squashfs_fs.h"
 #include "squashfs_fs.h"
 #include "squashfs_fs_sb.h"
 #include "squashfs_fs_sb.h"
 #include "squashfs_fs_i.h"
 #include "squashfs_fs_i.h"
 #include "squashfs.h"
 #include "squashfs.h"
+#include "xattr.h"
 
 
 static int squashfs_symlink_readpage(struct file *file, struct page *page)
 static int squashfs_symlink_readpage(struct file *file, struct page *page)
 {
 {
@@ -114,3 +116,12 @@ error_out:
 const struct address_space_operations squashfs_symlink_aops = {
 const struct address_space_operations squashfs_symlink_aops = {
 	.readpage = squashfs_symlink_readpage
 	.readpage = squashfs_symlink_readpage
 };
 };
+
+const struct inode_operations squashfs_symlink_inode_ops = {
+	.readlink = generic_readlink,
+	.follow_link = page_follow_link_light,
+	.put_link = page_put_link,
+	.getxattr = generic_getxattr,
+	.listxattr = squashfs_listxattr
+};
+

+ 323 - 0
fs/squashfs/xattr.c

@@ -0,0 +1,323 @@
+/*
+ * Squashfs - a compressed read only filesystem for Linux
+ *
+ * Copyright (c) 2010
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * xattr_id.c
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/vfs.h>
+#include <linux/xattr.h>
+#include <linux/slab.h>
+
+#include "squashfs_fs.h"
+#include "squashfs_fs_sb.h"
+#include "squashfs_fs_i.h"
+#include "squashfs.h"
+
+static const struct xattr_handler *squashfs_xattr_handler(int);
+
+ssize_t squashfs_listxattr(struct dentry *d, char *buffer,
+	size_t buffer_size)
+{
+	struct inode *inode = d->d_inode;
+	struct super_block *sb = inode->i_sb;
+	struct squashfs_sb_info *msblk = sb->s_fs_info;
+	u64 start = SQUASHFS_XATTR_BLK(squashfs_i(inode)->xattr)
+						 + msblk->xattr_table;
+	int offset = SQUASHFS_XATTR_OFFSET(squashfs_i(inode)->xattr);
+	int count = squashfs_i(inode)->xattr_count;
+	size_t rest = buffer_size;
+	int err;
+
+	/* check that the file system has xattrs */
+	if (msblk->xattr_id_table == NULL)
+		return -EOPNOTSUPP;
+
+	/* loop reading each xattr name */
+	while (count--) {
+		struct squashfs_xattr_entry entry;
+		struct squashfs_xattr_val val;
+		const struct xattr_handler *handler;
+		int name_size, prefix_size = 0;
+
+		err = squashfs_read_metadata(sb, &entry, &start, &offset,
+							sizeof(entry));
+		if (err < 0)
+			goto failed;
+
+		name_size = le16_to_cpu(entry.size);
+		handler = squashfs_xattr_handler(le16_to_cpu(entry.type));
+		if (handler)
+			prefix_size = handler->list(d, buffer, rest, NULL,
+				name_size, handler->flags);
+		if (prefix_size) {
+			if (buffer) {
+				if (prefix_size + name_size + 1 > rest) {
+					err = -ERANGE;
+					goto failed;
+				}
+				buffer += prefix_size;
+			}
+			err = squashfs_read_metadata(sb, buffer, &start,
+				&offset, name_size);
+			if (err < 0)
+				goto failed;
+			if (buffer) {
+				buffer[name_size] = '\0';
+				buffer += name_size + 1;
+			}
+			rest -= prefix_size + name_size + 1;
+		} else  {
+			/* no handler or insuffficient privileges, so skip */
+			err = squashfs_read_metadata(sb, NULL, &start,
+				&offset, name_size);
+			if (err < 0)
+				goto failed;
+		}
+
+
+		/* skip remaining xattr entry */
+		err = squashfs_read_metadata(sb, &val, &start, &offset,
+						sizeof(val));
+		if (err < 0)
+			goto failed;
+
+		err = squashfs_read_metadata(sb, NULL, &start, &offset,
+						le32_to_cpu(val.vsize));
+		if (err < 0)
+			goto failed;
+	}
+	err = buffer_size - rest;
+
+failed:
+	return err;
+}
+
+
+static int squashfs_xattr_get(struct inode *inode, int name_index,
+	const char *name, void *buffer, size_t buffer_size)
+{
+	struct super_block *sb = inode->i_sb;
+	struct squashfs_sb_info *msblk = sb->s_fs_info;
+	u64 start = SQUASHFS_XATTR_BLK(squashfs_i(inode)->xattr)
+						 + msblk->xattr_table;
+	int offset = SQUASHFS_XATTR_OFFSET(squashfs_i(inode)->xattr);
+	int count = squashfs_i(inode)->xattr_count;
+	int name_len = strlen(name);
+	int err, vsize;
+	char *target = kmalloc(name_len, GFP_KERNEL);
+
+	if (target == NULL)
+		return  -ENOMEM;
+
+	/* loop reading each xattr name */
+	for (; count; count--) {
+		struct squashfs_xattr_entry entry;
+		struct squashfs_xattr_val val;
+		int type, prefix, name_size;
+
+		err = squashfs_read_metadata(sb, &entry, &start, &offset,
+							sizeof(entry));
+		if (err < 0)
+			goto failed;
+
+		name_size = le16_to_cpu(entry.size);
+		type = le16_to_cpu(entry.type);
+		prefix = type & SQUASHFS_XATTR_PREFIX_MASK;
+
+		if (prefix == name_index && name_size == name_len)
+			err = squashfs_read_metadata(sb, target, &start,
+						&offset, name_size);
+		else
+			err = squashfs_read_metadata(sb, NULL, &start,
+						&offset, name_size);
+		if (err < 0)
+			goto failed;
+
+		if (prefix == name_index && name_size == name_len &&
+					strncmp(target, name, name_size) == 0) {
+			/* found xattr */
+			if (type & SQUASHFS_XATTR_VALUE_OOL) {
+				__le64 xattr;
+				/* val is a reference to the real location */
+				err = squashfs_read_metadata(sb, &val, &start,
+						&offset, sizeof(val));
+				if (err < 0)
+					goto failed;
+				err = squashfs_read_metadata(sb, &xattr, &start,
+					 &offset, sizeof(xattr));
+				if (err < 0)
+					goto failed;
+				xattr = le64_to_cpu(xattr);
+				start = SQUASHFS_XATTR_BLK(xattr) +
+							msblk->xattr_table;
+				offset = SQUASHFS_XATTR_OFFSET(xattr);
+			}
+			/* read xattr value */
+			err = squashfs_read_metadata(sb, &val, &start, &offset,
+							sizeof(val));
+			if (err < 0)
+				goto failed;
+
+			vsize = le32_to_cpu(val.vsize);
+			if (buffer) {
+				if (vsize > buffer_size) {
+					err = -ERANGE;
+					goto failed;
+				}
+				err = squashfs_read_metadata(sb, buffer, &start,
+					 &offset, vsize);
+				if (err < 0)
+					goto failed;
+			}
+			break;
+		}
+
+		/* no match, skip remaining xattr entry */
+		err = squashfs_read_metadata(sb, &val, &start, &offset,
+							sizeof(val));
+		if (err < 0)
+			goto failed;
+		err = squashfs_read_metadata(sb, NULL, &start, &offset,
+						le32_to_cpu(val.vsize));
+		if (err < 0)
+			goto failed;
+	}
+	err = count ? vsize : -ENODATA;
+
+failed:
+	kfree(target);
+	return err;
+}
+
+
+/*
+ * User namespace support
+ */
+static size_t squashfs_user_list(struct dentry *d, char *list, size_t list_size,
+	const char *name, size_t name_len, int type)
+{
+	if (list && XATTR_USER_PREFIX_LEN <= list_size)
+		memcpy(list, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN);
+	return XATTR_USER_PREFIX_LEN;
+}
+
+static int squashfs_user_get(struct dentry *d, const char *name, void *buffer,
+	size_t size, int type)
+{
+	if (name[0] == '\0')
+		return  -EINVAL;
+
+	return squashfs_xattr_get(d->d_inode, SQUASHFS_XATTR_USER, name,
+		buffer, size);
+}
+
+static const struct xattr_handler squashfs_xattr_user_handler = {
+	.prefix	= XATTR_USER_PREFIX,
+	.list	= squashfs_user_list,
+	.get	= squashfs_user_get
+};
+
+/*
+ * Trusted namespace support
+ */
+static size_t squashfs_trusted_list(struct dentry *d, char *list,
+	size_t list_size, const char *name, size_t name_len, int type)
+{
+	if (!capable(CAP_SYS_ADMIN))
+		return 0;
+
+	if (list && XATTR_TRUSTED_PREFIX_LEN <= list_size)
+		memcpy(list, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN);
+	return XATTR_TRUSTED_PREFIX_LEN;
+}
+
+static int squashfs_trusted_get(struct dentry *d, const char *name,
+	void *buffer, size_t size, int type)
+{
+	if (name[0] == '\0')
+		return  -EINVAL;
+
+	return squashfs_xattr_get(d->d_inode, SQUASHFS_XATTR_TRUSTED, name,
+		buffer, size);
+}
+
+static const struct xattr_handler squashfs_xattr_trusted_handler = {
+	.prefix	= XATTR_TRUSTED_PREFIX,
+	.list	= squashfs_trusted_list,
+	.get	= squashfs_trusted_get
+};
+
+/*
+ * Security namespace support
+ */
+static size_t squashfs_security_list(struct dentry *d, char *list,
+	size_t list_size, const char *name, size_t name_len, int type)
+{
+	if (list && XATTR_SECURITY_PREFIX_LEN <= list_size)
+		memcpy(list, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN);
+	return XATTR_SECURITY_PREFIX_LEN;
+}
+
+static int squashfs_security_get(struct dentry *d, const char *name,
+	void *buffer, size_t size, int type)
+{
+	if (name[0] == '\0')
+		return  -EINVAL;
+
+	return squashfs_xattr_get(d->d_inode, SQUASHFS_XATTR_SECURITY, name,
+		buffer, size);
+}
+
+static const struct xattr_handler squashfs_xattr_security_handler = {
+	.prefix	= XATTR_SECURITY_PREFIX,
+	.list	= squashfs_security_list,
+	.get	= squashfs_security_get
+};
+
+static inline const struct xattr_handler *squashfs_xattr_handler(int type)
+{
+	if (type & ~(SQUASHFS_XATTR_PREFIX_MASK | SQUASHFS_XATTR_VALUE_OOL))
+		/* ignore unrecognised type */
+		return NULL;
+
+	switch (type & SQUASHFS_XATTR_PREFIX_MASK) {
+	case SQUASHFS_XATTR_USER:
+		return &squashfs_xattr_user_handler;
+	case SQUASHFS_XATTR_TRUSTED:
+		return &squashfs_xattr_trusted_handler;
+	case SQUASHFS_XATTR_SECURITY:
+		return &squashfs_xattr_security_handler;
+	default:
+		/* ignore unrecognised type */
+		return NULL;
+	}
+}
+
+const struct xattr_handler *squashfs_xattr_handlers[] = {
+	&squashfs_xattr_user_handler,
+	&squashfs_xattr_trusted_handler,
+	&squashfs_xattr_security_handler,
+	NULL
+};
+

+ 46 - 0
fs/squashfs/xattr.h

@@ -0,0 +1,46 @@
+/*
+ * Squashfs - a compressed read only filesystem for Linux
+ *
+ * Copyright (c) 2010
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * xattr.h
+ */
+
+#ifdef CONFIG_SQUASHFS_XATTRS
+extern __le64 *squashfs_read_xattr_id_table(struct super_block *, u64,
+		u64 *, int *);
+extern int squashfs_xattr_lookup(struct super_block *, unsigned int, int *,
+		int *, unsigned long long *);
+#else
+static inline __le64 *squashfs_read_xattr_id_table(struct super_block *sb,
+		u64 start, u64 *xattr_table_start, int *xattr_ids)
+{
+	ERROR("Xattrs in filesystem, these will be ignored\n");
+	return ERR_PTR(-ENOTSUPP);
+}
+
+static inline int squashfs_xattr_lookup(struct super_block *sb,
+		unsigned int index, int *count, int *size,
+		unsigned long long *xattr)
+{
+	return 0;
+}
+#define squashfs_listxattr NULL
+#define generic_getxattr NULL
+#define squashfs_xattr_handlers NULL
+#endif

+ 100 - 0
fs/squashfs/xattr_id.c

@@ -0,0 +1,100 @@
+/*
+ * Squashfs - a compressed read only filesystem for Linux
+ *
+ * Copyright (c) 2010
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * xattr_id.c
+ */
+
+/*
+ * This file implements code to map the 32-bit xattr id stored in the inode
+ * into the on disk location of the xattr data.
+ */
+
+#include <linux/fs.h>
+#include <linux/vfs.h>
+#include <linux/slab.h>
+
+#include "squashfs_fs.h"
+#include "squashfs_fs_sb.h"
+#include "squashfs_fs_i.h"
+#include "squashfs.h"
+
+/*
+ * Map xattr id using the xattr id look up table
+ */
+int squashfs_xattr_lookup(struct super_block *sb, unsigned int index,
+		int *count, unsigned int *size, unsigned long long *xattr)
+{
+	struct squashfs_sb_info *msblk = sb->s_fs_info;
+	int block = SQUASHFS_XATTR_BLOCK(index);
+	int offset = SQUASHFS_XATTR_BLOCK_OFFSET(index);
+	u64 start_block = le64_to_cpu(msblk->xattr_id_table[block]);
+	struct squashfs_xattr_id id;
+	int err;
+
+	err = squashfs_read_metadata(sb, &id, &start_block, &offset,
+							sizeof(id));
+	if (err < 0)
+		return err;
+
+	*xattr = le64_to_cpu(id.xattr);
+	*size = le32_to_cpu(id.size);
+	*count = le32_to_cpu(id.count);
+	return 0;
+}
+
+
+/*
+ * Read uncompressed xattr id lookup table indexes from disk into memory
+ */
+__le64 *squashfs_read_xattr_id_table(struct super_block *sb, u64 start,
+		u64 *xattr_table_start, int *xattr_ids)
+{
+	unsigned int len;
+	__le64 *xid_table;
+	struct squashfs_xattr_id_table id_table;
+	int err;
+
+	err = squashfs_read_table(sb, &id_table, start, sizeof(id_table));
+	if (err < 0) {
+		ERROR("unable to read xattr id table\n");
+		return ERR_PTR(err);
+	}
+	*xattr_table_start = le64_to_cpu(id_table.xattr_table_start);
+	*xattr_ids = le32_to_cpu(id_table.xattr_ids);
+	len = SQUASHFS_XATTR_BLOCK_BYTES(*xattr_ids);
+
+	TRACE("In read_xattr_index_table, length %d\n", len);
+
+	/* Allocate xattr id lookup table indexes */
+	xid_table = kmalloc(len, GFP_KERNEL);
+	if (xid_table == NULL) {
+		ERROR("Failed to allocate xattr id index table\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	err = squashfs_read_table(sb, xid_table, start + sizeof(id_table), len);
+	if (err < 0) {
+		ERROR("unable to read xattr id index table\n");
+		kfree(xid_table);
+		return ERR_PTR(err);
+	}
+
+	return xid_table;
+}