diff mbox

[v2,4/5] mkfs.ubifs: Add extended attribute support

Message ID 1447320688-3086-5-git-send-email-mkl@pengutronix.de
State Accepted
Headers show

Commit Message

Marc Kleine-Budde Nov. 12, 2015, 9:31 a.m. UTC
From: Sascha Hauer <s.hauer@pengutronix.de>

This adds extended attribute support to mkfs.ubifs. When creating
an image from a directory tree the existing extended attributes are
added to the UBIFS image.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
---
 ubifs-utils/mkfs.ubifs/key.h        |  18 ++++
 ubifs-utils/mkfs.ubifs/mkfs.ubifs.c | 205 +++++++++++++++++++++++++++++++-----
 ubifs-utils/mkfs.ubifs/ubifs.h      |   9 ++
 3 files changed, 205 insertions(+), 27 deletions(-)
diff mbox

Patch

diff --git a/ubifs-utils/mkfs.ubifs/key.h b/ubifs-utils/mkfs.ubifs/key.h
index d3a02d4ff1a6..39379fd48178 100644
--- a/ubifs-utils/mkfs.ubifs/key.h
+++ b/ubifs-utils/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/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
index a99c61382304..30cd10c25819 100644
--- a/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
+++ b/ubifs-utils/mkfs.ubifs/mkfs.ubifs.c
@@ -25,6 +25,10 @@ 
 #include "mkfs.ubifs.h"
 #include <crc32.h>
 #include "common.h"
+#include <sys/types.h>
+#ifndef WITHOUT_XATTR
+#include <attr/xattr.h>
+#endif
 
 /* Size (prime number) of hash table for link counting */
 #define HASH_TABLE_SIZE 10099
@@ -980,20 +984,170 @@  static int add_node(union ubifs_key *key, char *name, void *node, int len)
 	return 0;
 }
 
+#ifdef WITHOUT_XATTR
+static inline int inode_add_xattr(struct ubifs_ino_node *host_ino,
+				  const char *path_name, struct stat *st, ino_t inum)
+{
+	(void)host_ino;
+	(void)path_name;
+	(void)st;
+	(void)inum;
+
+	return 0;
+}
+#else
+static int add_xattr(struct stat *st, ino_t inum, const void *data,
+		     unsigned int data_len, struct qstr *nm)
+{
+	struct ubifs_ino_node *ino;
+	struct ubifs_dent_node *xent;
+	union ubifs_key xkey, nkey;
+	int len, ret;
+
+	xent = xzalloc(sizeof(*xent) + nm->len + 1);
+	ino = xzalloc(sizeof(*ino) + data_len);
+
+	xent_key_init(c, &xkey, inum, nm);
+	xent->ch.node_type = UBIFS_XENT_NODE;
+	key_write(&xkey, &xent->key);
+
+	len = UBIFS_XENT_NODE_SZ + nm->len + 1;
+
+	xent->ch.len = len;
+	xent->padding1 = 0;
+	xent->type = UBIFS_ITYPE_DIR;
+	xent->nlen = cpu_to_le16(nm->len);
+
+	memcpy(xent->name, nm->name, nm->len + 1);
+
+	inum = ++c->highest_inum;
+	creat_sqnum = ++c->max_sqnum;
+
+	xent->inum = cpu_to_le64(inum);
+
+	ret = add_node(&xkey, nm->name, xent, len);
+	if (ret)
+		goto out;
+
+	ino->creat_sqnum = cpu_to_le64(creat_sqnum);
+	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->compr_type = cpu_to_le16(c->default_compr);
+	ino->ch.node_type = UBIFS_INO_NODE;
+
+	ino_key_init(&nkey, inum);
+	key_write(&nkey, &ino->key);
+
+	ino->size       = cpu_to_le64(data_len);
+	ino->mode       = cpu_to_le32(S_IFREG);
+	ino->data_len   = cpu_to_le32(data_len);
+	ino->flags      = cpu_to_le32(UBIFS_XATTR_FL);
+
+	if (data_len)
+		memcpy(&ino->data, data, data_len);
+
+	ret = add_node(&nkey, nm->name, ino, UBIFS_INO_NODE_SZ + data_len) ;
+
+out:
+	free(xent);
+	free(ino);
+
+	return ret;
+}
+
+static int inode_add_xattr(struct ubifs_ino_node *host_ino,
+			   const char *path_name, struct stat *st, ino_t inum)
+{
+	int ret;
+	struct qstr nm;
+	void *buf = NULL;
+	ssize_t len;
+	ssize_t pos = 0;
+
+	len = llistxattr(path_name, NULL, 0);
+	if (len < 0) {
+		if (errno == ENOENT)
+			return 0;
+
+		sys_err_msg("llistxattr failed on %s", path_name);
+
+		return len;
+	}
+
+	if (len == 0)
+		goto noxattr;
+
+	buf = xmalloc(len);
+
+	len = llistxattr(path_name, buf, len);
+	if (len < 0) {
+		sys_err_msg("llistxattr failed on %s", path_name);
+		goto out_free;
+	}
+
+	while (pos < len) {
+		char attrbuf[1024] = { };
+		char *name;
+		ssize_t attrsize;
+
+		name = buf + pos;
+		pos += strlen(name) + 1;
+
+		attrsize = lgetxattr(path_name, name, attrbuf, sizeof(attrbuf) - 1);
+		if (attrsize < 0) {
+			sys_err_msg("lgetxattr failed on %s", path_name);
+			goto out_free;
+		}
+
+		nm.name = name;
+		nm.len = strlen(name);
+
+		host_ino->xattr_cnt++;
+		host_ino->xattr_size += CALC_DENT_SIZE(nm.len);
+		host_ino->xattr_size += CALC_XATTR_BYTES(attrsize);
+		host_ino->xattr_names += nm.len;
+
+		ret = add_xattr(st, inum, attrbuf, attrsize, &nm);
+		if (ret < 0)
+			goto out_free;
+	}
+
+noxattr:
+	free(buf);
+	return 0;
+
+out_free:
+	free(buf);
+
+	return -1;
+}
+#endif
+
 /**
- * add_inode_with_data - write an inode.
+ * add_inode - write an inode.
  * @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_data(struct stat *st, ino_t inum, void *data,
-			       unsigned int data_len, int flags)
+static int add_inode(struct stat *st, ino_t inum, void *data,
+		     unsigned int data_len, int flags, const char *xattr_path)
 {
 	struct ubifs_ino_node *ino = node_buf;
 	union ubifs_key key;
-	int len, use_flags = 0;
+	int len, use_flags = 0, ret;
 
 	if (c->default_compr != UBIFS_COMPR_NONE)
 		use_flags |= UBIFS_COMPR_FL;
@@ -1037,18 +1191,13 @@  static int add_inode_with_data(struct stat *st, ino_t inum, void *data,
 
 	len = UBIFS_INO_NODE_SZ + data_len;
 
-	return add_node(&key, NULL, ino, len);
-}
+	if (xattr_path) {
+		ret = inode_add_xattr(ino, xattr_path, st, inum);
+		if (ret < 0)
+			return ret;
+	}
 
-/**
- * add_inode - write an inode.
- * @st: stat information of source inode
- * @inum: target inode number
- * @flags: source inode flags
- */
-static int add_inode(struct stat *st, ino_t inum, int flags)
-{
-	return add_inode_with_data(st, inum, NULL, 0, flags);
+	return add_node(&key, NULL, ino, len);
 }
 
 /**
@@ -1064,8 +1213,8 @@  static int add_inode(struct stat *st, ino_t inum, int flags)
  * is being created does not exist at the host file system, but is defined by
  * the device table.
  */
-static int add_dir_inode(DIR *dir, ino_t inum, loff_t size, unsigned int nlink,
-			 struct stat *st)
+static int add_dir_inode(const char *path_name, DIR *dir, ino_t inum, loff_t size,
+			 unsigned int nlink, struct stat *st)
 {
 	int fd, flags = 0;
 
@@ -1080,7 +1229,7 @@  static int add_dir_inode(DIR *dir, ino_t inum, loff_t size, unsigned int nlink,
 			flags = 0;
 	}
 
-	return add_inode(st, inum, flags);
+	return add_inode(st, inum, NULL, 0, flags, path_name);
 }
 
 /**
@@ -1089,12 +1238,12 @@  static int add_dir_inode(DIR *dir, ino_t inum, loff_t size, unsigned int nlink,
  * @inum: target inode number
  * @flags: source inode flags
  */
-static int add_dev_inode(struct stat *st, ino_t inum, int flags)
+static int add_dev_inode(const char *path_name, struct stat *st, ino_t inum, int flags)
 {
 	union ubifs_dev_desc dev;
 
 	dev.huge = cpu_to_le64(makedev(major(st->st_rdev), minor(st->st_rdev)));
-	return add_inode_with_data(st, inum, &dev, 8, flags);
+	return add_inode(st, inum, &dev, 8, flags, path_name);
 }
 
 /**
@@ -1117,7 +1266,7 @@  static int add_symlink_inode(const char *path_name, struct stat *st, ino_t inum,
 	if (len > UBIFS_MAX_INO_DATA)
 		return err_msg("symlink too long for %s", path_name);
 
-	return add_inode_with_data(st, inum, buf, len, flags);
+	return add_inode(st, inum, buf, len, flags, path_name);
 }
 
 /**
@@ -1275,12 +1424,14 @@  static int add_file(const char *path_name, struct stat *st, ino_t inum,
 			return err;
 		}
 	} while (ret != 0);
+
 	if (close(fd) == -1)
 		return sys_err_msg("failed to close file '%s'", path_name);
 	if (file_size != st->st_size)
 		return err_msg("file size changed during writing file '%s'",
 			       path_name);
-	return add_inode(st, inum, flags);
+
+	return add_inode(st, inum, NULL, 0, flags, path_name);
 }
 
 /**
@@ -1360,15 +1511,15 @@  static int add_non_dir(const char *path_name, ino_t *inum, unsigned int nlink,
 	if (S_ISREG(st->st_mode))
 		return add_file(path_name, st, *inum, flags);
 	if (S_ISCHR(st->st_mode))
-		return add_dev_inode(st, *inum, flags);
+		return add_dev_inode(path_name, st, *inum, flags);
 	if (S_ISBLK(st->st_mode))
-		return add_dev_inode(st, *inum, flags);
+		return add_dev_inode(path_name, st, *inum, flags);
 	if (S_ISLNK(st->st_mode))
 		return add_symlink_inode(path_name, st, *inum, flags);
 	if (S_ISSOCK(st->st_mode))
-		return add_inode(st, *inum, flags);
+		return add_inode(st, *inum, NULL, 0, flags, NULL);
 	if (S_ISFIFO(st->st_mode))
-		return add_inode(st, *inum, flags);
+		return add_inode(st, *inum, NULL, 0, flags, NULL);
 
 	return err_msg("file '%s' has unknown inode type", path_name);
 }
@@ -1548,7 +1699,7 @@  static int add_directory(const char *dir_name, ino_t dir_inum, struct stat *st,
 
 	creat_sqnum = dir_creat_sqnum;
 
-	err = add_dir_inode(dir, dir_inum, size, nlink, st);
+	err = add_dir_inode(dir ? dir_name : NULL, dir, dir_inum, size, nlink, st);
 	if (err)
 		goto out_free;
 
diff --git a/ubifs-utils/mkfs.ubifs/ubifs.h b/ubifs-utils/mkfs.ubifs/ubifs.h
index 434b651859b3..2f080a8ce708 100644
--- a/ubifs-utils/mkfs.ubifs/ubifs.h
+++ b/ubifs-utils/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
 {