diff mbox

mtd: mtd-utils: Support Capabilities (extended attributes)

Message ID CAF3KAoS=Hv25eUrHg2a5gRqMAhZZnZ8jgEY59U9hNk2vmioP1g@mail.gmail.com
State Not Applicable
Headers show

Commit Message

Bruno Vernay Dec. 10, 2014, 9:45 a.m. UTC
[PATCH]  mtd: mtd-utils: Support Capabilities (extended attributes)

Write the files capabilities into the target image.
Capabilities are a kind of security extended attributes (similar to SELinux)
The extended attributes are writen in an additional inode.
Note that we don't use the "capability" functions, but the raw
Extended Attributes http://www.linuxhowtos.org/manpages/2/getxattr.htm
Inspired from: http://patchwork.ozlabs.org/patch/321572/
About Capabilities: http://www.friedhoff.org/posixfilecaps.html

[From: Bruno VERNAY <brunovern.a@gmail.com>]
diff mbox

Patch

From ead64714066a08a9a2f6bdee078a72d8503c4570 Mon Sep 17 00:00:00 2001
From: Bruno Vernay <Bruno.Vernay@Schneider-Electric.com>
Date: Wed, 10 Dec 2014 10:25:48 +0100
Subject: [PATCH] Support Capabilities

---
 Makefile                |   2 +-
 mkfs.ubifs/key.h        |  18 +++++
 mkfs.ubifs/mkfs.ubifs.c | 185 +++++++++++++++++++++++++++++++++++++++++++++++-
 mkfs.ubifs/ubifs.h      |   9 +++
 4 files changed, 211 insertions(+), 3 deletions(-)

diff --git a/Makefile b/Makefile
index 0b57a3f..20a0dde 100644
--- a/Makefile
+++ b/Makefile
@@ -104,7 +104,7 @@  $(call _mkdep,lib/,libmtd.a)
 obj-mkfs.ubifs = crc16.o lpt.o compr.o devtable.o \
 	hashtable/hashtable.o hashtable/hashtable_itr.o
 LDFLAGS_mkfs.ubifs = $(ZLIBLDFLAGS) $(LZOLDFLAGS) $(UUIDLDFLAGS)
-LDLIBS_mkfs.ubifs = -lz -llzo2 -lm -luuid
+LDLIBS_mkfs.ubifs = -lz -llzo2 -lm -luuid -lcap
 $(call mkdep,mkfs.ubifs/,mkfs.ubifs,,ubi-utils/libubi.a)
 
 #
diff --git a/mkfs.ubifs/key.h b/mkfs.ubifs/key.h
index d3a02d4..8d9112c 100644
--- a/mkfs.ubifs/key.h
+++ b/mkfs.ubifs/key.h
@@ -119,6 +119,24 @@  static inline void dent_key_init(const struct ubifs_info *c,
 }
 
 /**
+ * xent_key_init - initialize extended attribute entry key.
+ * @c: UBIFS file-system description object
+ * @key: key to initialize
+ * @inum: host inode number
+ * @nm: extended attribute entry name and length
+ */
+static inline void xent_key_init(const struct ubifs_info *c,
+             union ubifs_key *key, ino_t inum,
+             const struct qstr *nm)
+{
+   uint32_t hash = c->key_hash(nm->name, nm->len);
+
+   ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK));
+   key->u32[0] = inum;
+   key->u32[1] = hash | (UBIFS_XENT_KEY << UBIFS_S_KEY_HASH_BITS);
+}
+
+/**
  * data_key_init - initialize data key.
  * @c: UBIFS file-system description object
  * @key: key to initialize
diff --git a/mkfs.ubifs/mkfs.ubifs.c b/mkfs.ubifs/mkfs.ubifs.c
index ca17e2b..abde979 100644
--- a/mkfs.ubifs/mkfs.ubifs.c
+++ b/mkfs.ubifs/mkfs.ubifs.c
@@ -25,6 +25,9 @@ 
 #include "mkfs.ubifs.h"
 #include <crc32.h>
 #include "common.h"
+#include <sys/capability.h>
+#include <sys/types.h>
+#include <sys/xattr.h>
 
 /* Size (prime number) of hash table for link counting */
 #define HASH_TABLE_SIZE 10099
@@ -1054,6 +1057,163 @@  static int add_inode_with_data(struct stat *st, ino_t inum, void *data,
 }
 
 /**
+ * add_inode_with_xattr - write an inode with xattr support.  much like the
+ *    with_data function above but creates 3 nodes: The inode for the file/
+ *    directory with xattr size set, the xent node with the same inum as the
+ *    previous inode and finally the inode with the actual capability data and
+ *    a new unique inum value.
+ * @st: stat information of source inode
+ * @inum: target inode number
+ * @data: inode data (for special inodes e.g. symlink path etc)
+ * @data_len: inode data length
+ * @flags: source inode flags
+ */
+static int add_inode_with_xattr(struct stat *st, ino_t inum, void *data,
+                               unsigned int data_len, int flags)
+{
+        struct ubifs_ino_node *ino = node_buf;
+        struct ubifs_dent_node *xent = node_buf;
+        union ubifs_key key, xkey, nkey;
+        int len, use_flags = 0, ret;
+        char *name;
+        struct qstr nm;
+
+        if (c->default_compr != UBIFS_COMPR_NONE)
+                use_flags |= UBIFS_COMPR_FL;
+        if (flags & FS_COMPR_FL)
+                use_flags |= UBIFS_COMPR_FL;
+        if (flags & FS_SYNC_FL)
+                use_flags |= UBIFS_SYNC_FL;
+        if (flags & FS_IMMUTABLE_FL)
+                use_flags |= UBIFS_IMMUTABLE_FL;
+        if (flags & FS_APPEND_FL)
+                use_flags |= UBIFS_APPEND_FL;
+        if (flags & FS_DIRSYNC_FL && S_ISDIR(st->st_mode))
+                use_flags |= UBIFS_DIRSYNC_FL;
+
+        // file inode
+        memset(ino, 0, UBIFS_INO_NODE_SZ);
+
+        ino_key_init(&key, inum);
+        ino->ch.node_type = UBIFS_INO_NODE;
+        key_write(&key, &ino->key);
+        ino->creat_sqnum = cpu_to_le64(creat_sqnum);
+        ino->size       = cpu_to_le64(st->st_size);
+        ino->nlink      = cpu_to_le32(st->st_nlink);
+        /*
+         * The time fields are updated assuming the default time granularity
+         * of 1 second. To support finer granularities, utime() would be needed.
+         */
+        ino->atime_sec  = cpu_to_le64(st->st_atime);
+        ino->ctime_sec  = cpu_to_le64(st->st_ctime);
+        ino->mtime_sec  = cpu_to_le64(st->st_mtime);
+        ino->atime_nsec = 0;
+        ino->ctime_nsec = 0;
+        ino->mtime_nsec = 0;
+        ino->uid        = cpu_to_le32(st->st_uid);
+        ino->gid        = cpu_to_le32(st->st_gid);
+        ino->mode       = cpu_to_le32(st->st_mode);
+        ino->flags      = cpu_to_le32(use_flags);
+        ino->data_len   = 0;
+        ino->compr_type = cpu_to_le16(c->default_compr);
+
+        name = strdup(XATTR_NAME_CAPS);
+        if ( name == NULL ) printf("alloc failed\n");
+        nm.name = (void *)name;
+        nm.len = strlen(name);
+
+        ino->xattr_cnt = 1;
+        ino->xattr_size += CALC_DENT_SIZE(strlen(name));
+        ino->xattr_size += CALC_XATTR_BYTES(data_len);
+        ino->xattr_names += strlen(name);
+
+        len = UBIFS_INO_NODE_SZ;
+
+        ret = add_node(&key, name, ino, len) ;
+        if (ret) {
+                printf("host inode add failed\n");
+                return ret;
+        }
+
+        /* do xattr node */
+        memset(xent, 0, UBIFS_XENT_NODE_SZ);
+
+        xent_key_init(c, &xkey, inum, &nm);
+        xent->ch.node_type = UBIFS_XENT_NODE;
+        key_write(&xkey, &xent->key);
+
+        len = UBIFS_XENT_NODE_SZ + strlen(name) + 1;
+
+        xent->ch.len       = len;
+        xent->padding1 = 0;
+        xent->type = UBIFS_ITYPE_DIR;
+        xent->nlen = cpu_to_le16(strlen(name));
+
+        memcpy(xent->name, name, strlen(name) + 1);
+
+        /* unique inum for inode with xattr */
+        inum = ++c->highest_inum;
+        creat_sqnum = ++c->max_sqnum;
+
+        xent->inum = cpu_to_le64(inum);
+
+        ret = add_node(&xkey, name, xent, len);
+        if (ret) {
+                printf("xent inode add failed\n");
+                return ret;
+        }
+
+    /* add inode with xattr data */
+        memset(ino, 0, UBIFS_INO_NODE_SZ);
+
+        ino_key_init(&nkey, inum);
+        ino->ch.node_type = UBIFS_INO_NODE;
+        key_write(&nkey, &ino->key);
+        ino->creat_sqnum = cpu_to_le64(creat_sqnum);
+        ino->size       = cpu_to_le64(data_len);
+        /*
+         * The time fields are updated assuming the default time granularity
+         * of 1 second. To support finer granularities, utime() would be needed.
+         */
+        ino->atime_sec  = cpu_to_le64(st->st_atime);
+        ino->ctime_sec  = cpu_to_le64(st->st_ctime);
+        ino->mtime_sec  = cpu_to_le64(st->st_mtime);
+        ino->atime_nsec = 0;
+        ino->ctime_nsec = 0;
+        ino->mtime_nsec = 0;
+        ino->uid        = cpu_to_le32(st->st_uid);
+        ino->gid        = cpu_to_le32(st->st_gid);
+        ino->mode       = cpu_to_le32((st->st_mode & (~S_IFDIR)) | (S_IFREG));
+        ino->flags      = cpu_to_le32(UBIFS_XATTR_FL);
+        ino->data_len   = cpu_to_le32(data_len);
+        ino->compr_type = cpu_to_le16(c->default_compr);
+
+        name = strdup(XATTR_NAME_CAPS);
+        if ( name == NULL ) printf("alloc failed\n");
+        nm.name = (void *)name;
+        nm.len = strlen(name);
+
+        ino->xattr_cnt = 1;
+        ino->xattr_size += CALC_DENT_SIZE(strlen(name));
+        ino->xattr_size += CALC_XATTR_BYTES(data_len);
+        ino->xattr_names += strlen(name);
+
+        if (data_len)
+                memcpy(&ino->data, data, data_len);
+
+        len = UBIFS_INO_NODE_SZ + data_len;
+
+        ret = add_node(&nkey, name, ino, len) ;
+        if (ret) {
+                printf("new inode add failed\n");
+                return ret;
+        }
+
+        return ret;
+}
+
+
+/**
  * add_inode - write an inode.
  * @st: stat information of source inode
  * @inum: target inode number
@@ -1234,17 +1394,36 @@  static int add_file(const char *path_name, struct stat *st, ino_t inum,
 		    int flags)
 {
 	struct ubifs_data_node *dn = node_buf;
-	void *buf = block_buf;
+	void *buf = block_buf, *caps;
 	loff_t file_size = 0;
-	ssize_t ret, bytes_read;
+	ssize_t ret, bytes_read, xattr_len;
 	union ubifs_key key;
 	int fd, dn_len, err, compr_type, use_compr;
 	unsigned int block_no = 0;
 	size_t out_len;
+	char *cap_res, *cap_res_ptr, *cap_buf;
 
 	fd = open(path_name, O_RDONLY | O_LARGEFILE);
 	if (fd == -1)
 		return sys_err_msg("failed to open file '%s'", path_name);
+	xattr_len = fgetxattr(fd, XATTR_NAME_CAPS, NULL, 0);
+	if (xattr_len > 0) {
+		caps = malloc(xattr_len);
+		xattr_len = fgetxattr(fd, XATTR_NAME_CAPS, caps, xattr_len);
+		if (debug_level >= 2) {
+			cap_buf = (char *)caps;
+	                cap_res = (char*) malloc(3*xattr_len);
+	                cap_res_ptr = cap_res;
+	                int i;
+	                for (i=0 ; i < xattr_len ; i++) {
+	                        cap_res_ptr += sprintf(cap_res_ptr, "%02X:", cap_buf[i]);
+	                };
+                	sprintf(cap_res_ptr-1, "\n");
+        	        *(cap_res_ptr) = '\0';
+	                printf("\n\t%s\tCapability: %s\n", path_name, cap_res);
+			free(cap_res);
+		}
+	};
 	do {
 		/* Read next block */
 		bytes_read = 0;
@@ -1295,6 +1474,8 @@  static int add_file(const char *path_name, struct stat *st, ino_t inum,
 	if (file_size != st->st_size)
 		return err_msg("file size changed during writing file '%s'",
 			       path_name);
+        if (xattr_len > 0)
+                return add_inode_with_xattr(st, inum, caps, xattr_len, flags);
 	return add_inode(st, inum, flags);
 }
 
diff --git a/mkfs.ubifs/ubifs.h b/mkfs.ubifs/ubifs.h
index 434b651..2f080a8 100644
--- a/mkfs.ubifs/ubifs.h
+++ b/mkfs.ubifs/ubifs.h
@@ -42,6 +42,15 @@ 
  */
 #define UBIFS_TRUN_KEY UBIFS_KEY_TYPES_CNT
 
+/*
+ * How much a directory entry/extended attribute entry adds to the parent/host
+ * inode.
+ */
+#define CALC_DENT_SIZE(name_len) ALIGN(UBIFS_DENT_NODE_SZ + (name_len) + 1, 8)
+
+/* How much an extended attribute adds to the host inode */
+#define CALC_XATTR_BYTES(data_len) ALIGN(UBIFS_INO_NODE_SZ + (data_len) + 1, 8)
+
 /* The below union makes it easier to deal with keys */
 union ubifs_key
 {
-- 
1.9.1