Merge git://git.kernel.org/pub/scm/linux/kernel/git/pkl/squashfs-linus
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 26 May 2010 15:57:20 +0000 (08:57 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 26 May 2010 15:57:20 +0000 (08:57 -0700)
* 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

14 files changed:
Documentation/filesystems/squashfs.txt
fs/squashfs/Kconfig
fs/squashfs/Makefile
fs/squashfs/inode.c
fs/squashfs/namei.c
fs/squashfs/squashfs.h
fs/squashfs/squashfs_fs.h
fs/squashfs/squashfs_fs_i.h
fs/squashfs/squashfs_fs_sb.h
fs/squashfs/super.c
fs/squashfs/symlink.c
fs/squashfs/xattr.c [new file with mode: 0644]
fs/squashfs/xattr.h [new file with mode: 0644]
fs/squashfs/xattr_id.c [new file with mode: 0644]

index b324c033035ad097fda59b86897ae0720df9f09b..203f7202cc9e3439607d4131b5b0196b83ab7ea1 100644 (file)
@@ -38,7 +38,8 @@ Hard link support:            yes                     no
 Real inode numbers:            yes                     no
 32-bit uids/gids:              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
 directory data are highly compacted, and packed on byte boundaries.  Each
@@ -58,7 +59,7 @@ obtained from this site also.
 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:
 
         ---------------
@@ -80,6 +81,9 @@ alignment:
        |---------------|
        |    uid/gid    |
        |  lookup table |
+       |---------------|
+       |     xattr     |
+       |     table     |
         ---------------
 
 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
 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
 -------------------------------
@@ -199,9 +223,7 @@ it is small) is read at mount time and cached in memory.
 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
 ---------------------------
index 25a00d19d68681687d0f9990a661ab8a7321fb75..cc6ce8a84c217d50fb82aef341d26fd56da6e991 100644 (file)
@@ -26,6 +26,17 @@ config SQUASHFS
 
          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
 
        bool "Additional option for memory-constrained systems" 
index df8a19ef870d6db72cca324e4ef4667931b8dbd7..2cee3e9fa452b81219a04145f77c1ba827723a6e 100644 (file)
@@ -5,3 +5,5 @@
 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 += namei.o super.o symlink.o zlib_wrapper.o decompressor.o
+squashfs-$(CONFIG_SQUASHFS_XATTRS) += xattr.o xattr_id.o
+
index 49daaf669e41809d33b377c40c2f39e0f2e462c7..62e63ad250755ed6d51fd56aa75f2c91f82e74e7 100644 (file)
 
 #include <linux/fs.h>
 #include <linux/vfs.h>
+#include <linux/xattr.h>
 
 #include "squashfs_fs.h"
 #include "squashfs_fs_sb.h"
 #include "squashfs_fs_i.h"
 #include "squashfs.h"
+#include "xattr.h"
 
 /*
  * 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);
        union squashfs_inode squashfs_ino;
        struct squashfs_base_inode *sqshb_ino = &squashfs_ino.base;
+       int xattr_id = SQUASHFS_INVALID_XATTR;
 
        TRACE("Entered squashfs_read_inode\n");
 
@@ -199,8 +202,10 @@ int squashfs_read_inode(struct inode *inode, long long ino)
                        frag_offset = 0;
                }
 
+               xattr_id = le32_to_cpu(sqsh_ino->xattr);
                inode->i_nlink = le32_to_cpu(sqsh_ino->nlink);
                inode->i_size = le64_to_cpu(sqsh_ino->file_size);
+               inode->i_op = &squashfs_inode_ops;
                inode->i_fop = &generic_ro_fops;
                inode->i_mode |= S_IFREG;
                inode->i_blocks = ((inode->i_size -
@@ -251,6 +256,7 @@ int squashfs_read_inode(struct inode *inode, long long ino)
                if (err < 0)
                        goto failed_read;
 
+               xattr_id = le32_to_cpu(sqsh_ino->xattr);
                inode->i_nlink = le32_to_cpu(sqsh_ino->nlink);
                inode->i_size = le32_to_cpu(sqsh_ino->file_size);
                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_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_mode |= S_IFLNK;
                squashfs_i(inode)->start = block;
                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 "
                                "%x\n", SQUASHFS_INODE_BLK(ino), offset,
                                block, offset);
                break;
        }
        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;
                unsigned int rdev;
 
@@ -315,10 +333,32 @@ int squashfs_read_inode(struct inode *inode, long long ino)
                                SQUASHFS_INODE_BLK(ino), offset, rdev);
                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_SOCKET_TYPE:
-       case SQUASHFS_LFIFO_TYPE:
-       case SQUASHFS_LSOCKET_TYPE: {
+       case SQUASHFS_SOCKET_TYPE: {
                struct squashfs_ipc_inode *sqsh_ino = &squashfs_ino.ipc;
 
                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);
                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:
                ERROR("Unknown inode type %d in squashfs_iget!\n", type);
                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;
 
 failed_read:
        ERROR("Unable to read inode 0x%llx\n", ino);
        return err;
 }
+
+
+const struct inode_operations squashfs_inode_ops = {
+       .getxattr = generic_getxattr,
+       .listxattr = squashfs_listxattr
+};
+
index 5266bd8ad932edb3a288217d8992693d649b7112..7a9464d08cf632bef2395baea734c9eae9d8d6a7 100644 (file)
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/dcache.h>
+#include <linux/xattr.h>
 
 #include "squashfs_fs.h"
 #include "squashfs_fs_sb.h"
 #include "squashfs_fs_i.h"
 #include "squashfs.h"
+#include "xattr.h"
 
 /*
  * 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 = {
-       .lookup = squashfs_lookup
+       .lookup = squashfs_lookup,
+       .getxattr = generic_getxattr,
+       .listxattr = squashfs_listxattr
 };
index fe2587af5512edc3947622a2e949c53c4fa4d539..733a17c42945661d505474cf600b57037e1ff556 100644 (file)
@@ -73,8 +73,11 @@ extern struct inode *squashfs_iget(struct super_block *, long long,
                                unsigned int);
 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 */
@@ -86,11 +89,18 @@ extern const struct export_operations squashfs_export_ops;
 /* file.c */
 extern const struct address_space_operations squashfs_aops;
 
+/* inode.c */
+extern const struct inode_operations squashfs_inode_ops;
+
 /* namei.c */
 extern const struct inode_operations squashfs_dir_inode_ops;
 
 /* symlink.c */
 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 */
 extern const struct squashfs_decompressor squashfs_zlib_comp_ops;
index 79024245ea00f7fb370e7aa8dbef6819da33854b..8eabb808b78dcd18009565bbc5ed213b1261cb8b 100644 (file)
@@ -46,6 +46,7 @@
 #define SQUASHFS_NAME_LEN              256
 
 #define SQUASHFS_INVALID_FRAG          (0xffffffffU)
+#define SQUASHFS_INVALID_XATTR         (0xffffffffU)
 #define SQUASHFS_INVALID_BLK           (-1LL)
 
 /* Filesystem flags */
 #define SQUASHFS_LFIFO_TYPE            13
 #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
  * uncompressed */
 #define SQUASHFS_COMPRESSED_BIT                (1 << 15)
 
 #define SQUASHFS_ID_BLOCK_BYTES(A)     (SQUASHFS_ID_BLOCKS(A) *\
                                        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 */
 #define SQUASHFS_CACHED_BLKS           8
@@ -228,7 +254,7 @@ struct squashfs_super_block {
        __le64                  root_inode;
        __le64                  bytes_used;
        __le64                  id_table_start;
-       __le64                  xattr_table_start;
+       __le64                  xattr_id_table_start;
        __le64                  inode_table_start;
        __le64                  directory_table_start;
        __le64                  fragment_table_start;
@@ -261,6 +287,17 @@ struct squashfs_ipc_inode {
        __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 {
        __le16                  inode_type;
        __le16                  mode;
@@ -272,6 +309,18 @@ struct squashfs_dev_inode {
        __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 {
        __le16                  inode_type;
        __le16                  mode;
@@ -349,12 +398,14 @@ struct squashfs_ldir_inode {
 union squashfs_inode {
        struct squashfs_base_inode              base;
        struct squashfs_dev_inode               dev;
+       struct squashfs_ldev_inode              ldev;
        struct squashfs_symlink_inode           symlink;
        struct squashfs_reg_inode               reg;
        struct squashfs_lreg_inode              lreg;
        struct squashfs_dir_inode               dir;
        struct squashfs_ldir_inode              ldir;
        struct squashfs_ipc_inode               ipc;
+       struct squashfs_lipc_inode              lipc;
 };
 
 struct squashfs_dir_entry {
@@ -377,4 +428,27 @@ struct squashfs_fragment_entry {
        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
index fbfca30c0c688a0c17c902db1300e08d6adcbdea..d3e3a37f28a14609c17a4d4da4759e6567346bc4 100644 (file)
@@ -26,6 +26,9 @@
 struct squashfs_inode_info {
        u64             start;
        int             offset;
+       u64             xattr;
+       unsigned int    xattr_size;
+       int             xattr_count;
        union {
                struct {
                        u64             fragment_block;
index 2e77dc547e253df03d94f8dfe0a849358810d082..d9037a5215f00b77044c030b37c1255fe4273a41 100644 (file)
@@ -61,6 +61,7 @@ struct squashfs_sb_info {
        int                                     next_meta_index;
        __le64                                  *id_table;
        __le64                                  *fragment_index;
+       __le64                                  *xattr_id_table;
        struct mutex                            read_data_mutex;
        struct mutex                            meta_index_mutex;
        struct meta_index                       *meta_index;
@@ -68,9 +69,11 @@ struct squashfs_sb_info {
        __le64                                  *inode_lookup_table;
        u64                                     inode_table;
        u64                                     directory_table;
+       u64                                     xattr_table;
        unsigned int                            block_size;
        unsigned short                          block_log;
        long long                               bytes_used;
        unsigned int                            inodes;
+       int                                     xattr_ids;
 };
 #endif
index 48b6f4a385a60ccf40276bb08c0e591edd3561b1..88b4f8606652b8e675621a2f545aff357ea176ae 100644 (file)
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/magic.h>
+#include <linux/xattr.h>
 
 #include "squashfs_fs.h"
 #include "squashfs_fs_sb.h"
 #include "squashfs_fs_i.h"
 #include "squashfs.h"
 #include "decompressor.h"
+#include "xattr.h"
 
 static struct file_system_type squashfs_fs_type;
 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;
        unsigned short flags;
        unsigned int fragments;
-       u64 lookup_table_start;
+       u64 lookup_table_start, xattr_id_table_start;
        int err;
 
        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)
                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
           block device */
        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:
        lookup_table_start = le64_to_cpu(sblk->lookup_table_start);
        if (lookup_table_start == SQUASHFS_INVALID_BLK)
-               goto allocate_root;
+               goto allocate_xattr_table;
 
        /* Allocate and read inode lookup table */
        msblk->inode_lookup_table = squashfs_read_inode_lookup_table(sb,
@@ -266,6 +261,21 @@ allocate_lookup_table:
 
        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:
        root = new_inode(sb);
        if (!root) {
@@ -301,6 +311,7 @@ failed_mount:
        kfree(msblk->inode_lookup_table);
        kfree(msblk->fragment_index);
        kfree(msblk->id_table);
+       kfree(msblk->xattr_id_table);
        kfree(sb->s_fs_info);
        sb->s_fs_info = NULL;
        kfree(sblk);
@@ -355,6 +366,7 @@ static void squashfs_put_super(struct super_block *sb)
                kfree(sbi->fragment_index);
                kfree(sbi->meta_index);
                kfree(sbi->inode_lookup_table);
+               kfree(sbi->xattr_id_table);
                kfree(sb->s_fs_info);
                sb->s_fs_info = NULL;
        }
index 32b911f4ee39e859d9a03d7b68b8ef7e33e11552..ec86434921e18c6a5bd180246439f84b56a5d00b 100644 (file)
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/pagemap.h>
+#include <linux/xattr.h>
 
 #include "squashfs_fs.h"
 #include "squashfs_fs_sb.h"
 #include "squashfs_fs_i.h"
 #include "squashfs.h"
+#include "xattr.h"
 
 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 = {
        .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
+};
+
diff --git a/fs/squashfs/xattr.c b/fs/squashfs/xattr.c
new file mode 100644 (file)
index 0000000..c7655e8
--- /dev/null
@@ -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
+};
+
diff --git a/fs/squashfs/xattr.h b/fs/squashfs/xattr.h
new file mode 100644 (file)
index 0000000..9da071a
--- /dev/null
@@ -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
diff --git a/fs/squashfs/xattr_id.c b/fs/squashfs/xattr_id.c
new file mode 100644 (file)
index 0000000..cfb4110
--- /dev/null
@@ -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;
+}