linux-ext4.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4 0/4] ext4 crypto: backup and restore encrypted files
@ 2015-12-16 22:46 Theodore Ts'o
  2015-12-16 22:46 ` [PATCH v4 1/4] ext4 crypto: add missing locking for keyring_key access Theodore Ts'o
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Theodore Ts'o @ 2015-12-16 22:46 UTC (permalink / raw)
  To: Ext4 Developers List; +Cc: Theodore Ts'o

These patches allow backing up and restoring encrypted files without
having access to the key.  To do this we add four ioctls; two to
backup and restore the encryption metadata, and two to backup and
restore the encrypted filename.

One tricky bit is that if the file's i_size is not a multiple of the
AES block size, we need to be able to copy the bytes between i_size
and the end of the block, since they contain the ciphertext
bytes of the zeros between EOF and the end of the block.

There are two ways of solving this.  One would be an new DIO_FLAG that
rounds i_size up to the file system blocksize, which we would pass
when reading encrypted files using O_DIRECT.  This would require
changes to the core direct I/O, and may be controversial.  It also may
make it more difficult to back port these patches to ancient BSP
kernels.

So what we're doing for now is admittedly a hack.  Since encrypted
files are read-only without access to the key, it is safe to create a
shadow copy of the inode structure, and round up i_size to the end of
the block in the shadow inode.  We only do this when reading
the last block in the file, so the overhead shouldn't be too bad.

The original i_size is returned as part of the encryption metadata
returned by EXT4_IOC_GET_ENCRYPTION_METADATA and when the encryption
metadata is set using EXT4_IOC_GET_ENCRYPTION_METADATA, the i_size
will be restored to its original value.

Changes from v3:
 - Add EXT4_IOC_SET_ENCRYPTED_FILENAME ioctl which will restore the
   encrypted hard link.
 - Add mnt_want_write_file() and mnt_drop_write_file() calls.
 - Add security / permission checks to the ioctls.


Theodore Ts'o (4):
  ext4 crypto: add missing locking for keyring_key access
  ext4 crypto: add ciphertext_access mount option
  ext4 crypto: simplify interfaces to directory entry insert functions
  ext4 crypto: add ioctls to allow backup of encryption metadata

 fs/ext4/crypto_key.c  | 128 ++++++++++++++++++++++++++++++++++++-
 fs/ext4/ext4.h        |  18 +++++-
 fs/ext4/ext4_crypto.h |  16 +++++
 fs/ext4/file.c        |   5 +-
 fs/ext4/indirect.c    |  24 +++++--
 fs/ext4/inline.c      |  10 ++-
 fs/ext4/inode.c       |  17 +++--
 fs/ext4/ioctl.c       |  87 +++++++++++++++++++++++++
 fs/ext4/namei.c       | 174 +++++++++++++++++++++++++++++++++++++++++---------
 fs/ext4/super.c       |  48 ++++++++++++++
 10 files changed, 475 insertions(+), 52 deletions(-)

-- cut here for sample test program --

/*
 * ext4-crypto-cp-md.c
 *
 * Test program to test the new crypto metadata backup/restore ioctl's
 *
 */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>

typedef unsigned long u32;
typedef signed long s32;

struct ext4_encrypted_metadata {
	u32 len;
	char metadata[288];
};

struct ext4_set_encrypted_fname {
	s32 fd;
	u32 len;
	unsigned char enc_fname[256];
};

#ifndef EXT4_IOC_GET_ENCRYPTION_METADATA
#define EXT4_IOC_GET_ENCRYPTION_METADATA _IOWR('f', 22, struct ext4_encrypted_metadata)
#endif
#ifndef EXT4_IOC_SET_ENCRYPTION_METADATA
#define EXT4_IOC_SET_ENCRYPTION_METADATA _IOR('f', 23, struct ext4_encrypted_metadata)
#endif
#ifndef EXT4_IOC_GET_ENCRYPTED_FILENAME
#define EXT4_IOC_GET_ENCRYPTED_FILENAME	_IOWR('f', 24, struct ext4_encrypted_metadata)
#endif
#ifndef EXT4_IOC_SET_ENCRYPTED_FILENAME
#define EXT4_IOC_SET_ENCRYPTED_FILENAME	_IOR('f', 25, struct ext4_set_encrypted_fname)
#endif

void print_mdata(const char *s, struct ext4_encrypted_metadata *mdata)
{
	int i;

	printf("%s len %d: \n", s, mdata->len);
	for (i = 0; i < mdata->len; i++)
		printf("%02x ", mdata->metadata[i] & 0xFF);
	printf("\n");
}

int main(int argc, char **argv)
{
	int	s_fd, d_fd = -1, dir_fd = -1;
	int	oflags = O_RDONLY;
	struct ext4_encrypted_metadata f_mdata, fn_mdata;

	if (argc < 2 || argc > 4) {
		fprintf(stderr, "Usage: %s source [destination] [destdir]\n",
			argv[0]);
		exit(1);
	}
	s_fd = open(argv[1], O_RDONLY);
	if (s_fd < 0) {
		perror(argv[1]);
		exit(1);
	}
	if (argc > 2) {
		d_fd = open(argv[2], O_RDONLY);
		if (d_fd < 0) {
			perror(argv[2]);
			exit(1);
		}
	}
	if (argc > 3) {
		dir_fd = open(argv[3], O_DIRECTORY);
		if (dir_fd < 0) {
			perror(argv[3]);
			exit(1);
		}
	}
	f_mdata.len = sizeof(f_mdata.metadata);
	if (ioctl(s_fd, EXT4_IOC_GET_ENCRYPTION_METADATA, &f_mdata)) {
		perror("EXT4_IOC_GET_ENCRYPTION_METADATA");
		f_mdata.len = 0;
	} else {
		print_mdata("file", &f_mdata);
	}
	fn_mdata.len = sizeof(fn_mdata.metadata);
	if (ioctl(s_fd, EXT4_IOC_GET_ENCRYPTED_FILENAME, &fn_mdata)) {
		perror("EXT4_IOC_GET_ENCRYPTED_FILENAME");
		fn_mdata.len = 0;
	} else {
		print_mdata("filename", &fn_mdata);
	}
	if (d_fd >= 0 && f_mdata.len > 0) {
		if (ioctl(d_fd, EXT4_IOC_SET_ENCRYPTION_METADATA, &f_mdata)) {
			perror("EXT4_IOC_SET_ENCRYPTION_METADATA");
		}
	}
	if (d_fd >= 0 && dir_fd >= 0 && fn_mdata.len > 0) {
		struct ext4_set_encrypted_fname set_fn;

		set_fn.fd = d_fd;
		set_fn.len = fn_mdata.len;
		memcpy(&set_fn.enc_fname, &fn_mdata.metadata, fn_mdata.len);
		if (ioctl(dir_fd, EXT4_IOC_SET_ENCRYPTED_FILENAME, &set_fn)) {
			perror("EXT4_IOC_SET_ENCRYPTED_FILENAME");
		}
	}
	return 0;
}

-- cut here for a sample shell script --

#!/bin/bash -vx
#
# Sample shell script which demonstrates how to use the demonstration
# C program.  We use dd to copy the file using O_DIRECT, but the backup
# process should use SEEK_HOLE and SEEK_DATA to efficiently copy sparse
# files.
# For more details, please see:
#     http://linux.die.net/man/2/lseek
#     https://blogs.oracle.com/bonwick/en/entry/seek_hole_and_seek_data

umount /vdc
dmesg -n 7
mke2fs -Fq -t ext4 -O encrypt /dev/vdc
debugfs -w -R "ssv encrypt_pw_salt deadbeef-dead-beef-1234-5678deadbeef" /dev/vdc
mount -t ext4 /dev/vdc /vdc
mkdir /vdc/a
echo foobar | e4crypt add_key /vdc/a
cat << EOF > /vdc/a/test_file
Lorem ipsum dolor sit amet, consectetur adipiscing elit. In accumsan
mi ac magna vestibulum commodo. Cras facilisis posuere tellus in
efficitur. Sed mollis mi eget elit vulputate pellentesque. Ut vitae
laoreet diam. Aliquam sem leo, luctus eget leo eu, hendrerit egestas
risus. Nulla non nisi ut nisl suscipit dictum. Donec eleifend dapibus
mi eu porttitor. Nulla lacinia tellus nec porttitor tincidunt. Nam
lectus nibh, fringilla sit amet enim id, consequat tincidunt
mauris. Ut blandit orci vitae elit suscipit varius. Donec vel sem
tristique, efficitur felis sit amet, sagittis metus. In laoreet
ultricies interdum. Aliquam felis est, pharetra eget nisl vel,
fringilla aliquet velit. Etiam ut augue ut ante fringilla gravida quis
a arcu.
EOF
umount /vdc
keyctl purge logon
mount -t ext4 -o ciphertext_access /dev/vdc /vdc
F=/vdc/a/$(ls /vdc/a)
dd if=$F of=/vdc/tmpfile iflag=direct oflag=direct bs=4k
mkdir /vdc/b
/vdb/ext4-crypto-cp-md /vdc/a /vdc/b
/vdb/ext4-crypto-cp-md $F /vdc/tmpfile /vdc/b
#
# note: after this point we would normally delete /vdc/tmpfile
# but for the purposes of this script we keep it for test purposes
#
umount /vdc
mount -t ext4 /dev/vdc /vdc
echo foobar | e4crypt add_key
md5sum /vdc/b/test_file /vdc/a/test_file
umount /vdc
e2fsck -fn /dev/vdc
keyctl purge logon
exit 0

-- cut here --

-- 
2.5.0


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

* [PATCH v4 1/4] ext4 crypto: add missing locking for keyring_key access
  2015-12-16 22:46 [PATCH v4 0/4] ext4 crypto: backup and restore encrypted files Theodore Ts'o
@ 2015-12-16 22:46 ` Theodore Ts'o
  2015-12-16 22:46 ` [PATCH v4 2/4] ext4 crypto: add ciphertext_access mount option Theodore Ts'o
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Theodore Ts'o @ 2015-12-16 22:46 UTC (permalink / raw)
  To: Ext4 Developers List; +Cc: Theodore Ts'o, stable

Cc: stable@kernel.org
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/crypto_key.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/fs/ext4/crypto_key.c b/fs/ext4/crypto_key.c
index c5882b3..9a16d1e 100644
--- a/fs/ext4/crypto_key.c
+++ b/fs/ext4/crypto_key.c
@@ -213,9 +213,11 @@ retry:
 		res = -ENOKEY;
 		goto out;
 	}
+	down_read(&keyring_key->sem);
 	ukp = user_key_payload(keyring_key);
 	if (ukp->datalen != sizeof(struct ext4_encryption_key)) {
 		res = -EINVAL;
+		up_read(&keyring_key->sem);
 		goto out;
 	}
 	master_key = (struct ext4_encryption_key *)ukp->data;
@@ -226,10 +228,12 @@ retry:
 			    "ext4: key size incorrect: %d\n",
 			    master_key->size);
 		res = -ENOKEY;
+		up_read(&keyring_key->sem);
 		goto out;
 	}
 	res = ext4_derive_key_aes(ctx.nonce, master_key->raw,
 				  raw_key);
+	up_read(&keyring_key->sem);
 	if (res)
 		goto out;
 got_key:
-- 
2.5.0


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

* [PATCH v4 2/4] ext4 crypto: add ciphertext_access mount option
  2015-12-16 22:46 [PATCH v4 0/4] ext4 crypto: backup and restore encrypted files Theodore Ts'o
  2015-12-16 22:46 ` [PATCH v4 1/4] ext4 crypto: add missing locking for keyring_key access Theodore Ts'o
@ 2015-12-16 22:46 ` Theodore Ts'o
  2015-12-16 22:46 ` [PATCH v4 3/4] ext4 crypto: simplify interfaces to directory entry insert functions Theodore Ts'o
  2015-12-16 22:46 ` [PATCH v4 4/4] ext4 crypto: add ioctls to allow backup of encryption metadata Theodore Ts'o
  3 siblings, 0 replies; 5+ messages in thread
From: Theodore Ts'o @ 2015-12-16 22:46 UTC (permalink / raw)
  To: Ext4 Developers List; +Cc: Theodore Ts'o

Add a mount option which allows root to be able to access the
ciphertext of a file by reading it using O_DIRECT.

Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/ext4.h     |  3 +++
 fs/ext4/file.c     |  5 ++++-
 fs/ext4/indirect.c | 24 +++++++++++++++++++-----
 fs/ext4/inode.c    | 17 ++++++++++-------
 fs/ext4/super.c    | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 84 insertions(+), 13 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 1e20fa9..cf7a885 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1052,6 +1052,7 @@ struct ext4_inode_info {
 #define EXT4_MOUNT_DIOREAD_NOLOCK	0x400000 /* Enable support for dio read nolocking */
 #define EXT4_MOUNT_JOURNAL_CHECKSUM	0x800000 /* Journal checksums */
 #define EXT4_MOUNT_JOURNAL_ASYNC_COMMIT	0x1000000 /* Journal Async Commit */
+#define EXT4_MOUNT_CIPHERTEXT_ACCESS	0x2000000 /* Direct I/O to ciphertext */
 #define EXT4_MOUNT_DELALLOC		0x8000000 /* Delalloc support */
 #define EXT4_MOUNT_DATA_ERR_ABORT	0x10000000 /* Abort on file data write */
 #define EXT4_MOUNT_BLOCK_VALIDITY	0x20000000 /* Block validity checking */
@@ -2564,6 +2565,8 @@ extern int ext4_alloc_flex_bg_array(struct super_block *sb,
 				    ext4_group_t ngroup);
 extern const char *ext4_decode_error(struct super_block *sb, int errno,
 				     char nbuf[16]);
+extern struct inode *ext4_alloc_shadow_inode(struct inode *inode);
+extern void ext4_free_shadow_inode(struct inode *shadow);
 
 extern __printf(4, 5)
 void __ext4_error(struct super_block *, const char *, unsigned int,
diff --git a/fs/ext4/file.c b/fs/ext4/file.c
index 749b222..60683ab 100644
--- a/fs/ext4/file.c
+++ b/fs/ext4/file.c
@@ -388,7 +388,10 @@ static int ext4_file_open(struct inode * inode, struct file * filp)
 		ret = ext4_get_encryption_info(inode);
 		if (ret)
 			return -EACCES;
-		if (ext4_encryption_info(inode) == NULL)
+		if ((ext4_encryption_info(inode) == NULL) &&
+		    !(test_opt(inode->i_sb, CIPHERTEXT_ACCESS) &&
+		      ((filp->f_flags & O_ACCMODE) == O_RDONLY) &&
+		      capable(CAP_SYS_ADMIN)))
 			return -ENOKEY;
 	}
 	/*
diff --git a/fs/ext4/indirect.c b/fs/ext4/indirect.c
index 355ef9c..e685736 100644
--- a/fs/ext4/indirect.c
+++ b/fs/ext4/indirect.c
@@ -649,17 +649,17 @@ ssize_t ext4_ind_direct_IO(struct kiocb *iocb, struct iov_iter *iter,
 {
 	struct file *file = iocb->ki_filp;
 	struct inode *inode = file->f_mapping->host;
+	struct inode *shadow = inode;
 	struct ext4_inode_info *ei = EXT4_I(inode);
 	handle_t *handle;
 	ssize_t ret;
 	int orphan = 0;
 	size_t count = iov_iter_count(iter);
 	int retries = 0;
+	loff_t final_size = offset + count;
 
 	if (iov_iter_rw(iter) == WRITE) {
-		loff_t final_size = offset + count;
-
-		if (final_size > inode->i_size) {
+		if (final_size > i_size_read(inode)) {
 			/* Credits for sb + inode write */
 			handle = ext4_journal_start(inode, EXT4_HT_INODE, 2);
 			if (IS_ERR(handle)) {
@@ -676,6 +676,18 @@ ssize_t ext4_ind_direct_IO(struct kiocb *iocb, struct iov_iter *iter,
 			ext4_journal_stop(handle);
 		}
 	}
+	if (iov_iter_rw(iter) == READ &&
+	    ext4_encrypted_inode(inode) &&
+	    is_sync_kiocb(iocb) &&
+	    final_size > i_size_read(inode)) {
+		shadow = ext4_alloc_shadow_inode(inode);
+		if (shadow)
+			i_size_write(shadow,
+				     round_up(i_size_read(inode),
+					      inode->i_sb->s_blocksize));
+		else
+			shadow = inode;
+	}
 
 retry:
 	if (iov_iter_rw(iter) == READ && ext4_should_dioread_nolock(inode)) {
@@ -695,7 +707,7 @@ retry:
 			ret = dax_do_io(iocb, inode, iter, offset,
 					ext4_get_block, NULL, 0);
 		else
-			ret = __blockdev_direct_IO(iocb, inode,
+			ret = __blockdev_direct_IO(iocb, shadow,
 						   inode->i_sb->s_bdev, iter,
 						   offset, ext4_get_block, NULL,
 						   NULL, 0);
@@ -706,7 +718,7 @@ locked:
 			ret = dax_do_io(iocb, inode, iter, offset,
 					ext4_get_block, NULL, DIO_LOCKING);
 		else
-			ret = blockdev_direct_IO(iocb, inode, iter, offset,
+			ret = blockdev_direct_IO(iocb, shadow, iter, offset,
 						 ext4_get_block);
 
 		if (unlikely(iov_iter_rw(iter) == WRITE && ret < 0)) {
@@ -757,6 +769,8 @@ locked:
 			ret = err;
 	}
 out:
+	if (shadow != inode)
+		ext4_free_shadow_inode(shadow);
 	return ret;
 }
 
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index ff2f3cd..16f6537 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -3279,9 +3279,6 @@ static ssize_t ext4_ext_direct_IO(struct kiocb *iocb, struct iov_iter *iter,
 		get_block_func = ext4_get_block_write;
 		dio_flags = DIO_LOCKING;
 	}
-#ifdef CONFIG_EXT4_FS_ENCRYPTION
-	BUG_ON(ext4_encrypted_inode(inode) && S_ISREG(inode->i_mode));
-#endif
 	if (IS_DAX(inode))
 		ret = dax_do_io(iocb, inode, iter, offset, get_block_func,
 				ext4_end_io_dio, dio_flags);
@@ -3344,10 +3341,16 @@ static ssize_t ext4_direct_IO(struct kiocb *iocb, struct iov_iter *iter,
 	size_t count = iov_iter_count(iter);
 	ssize_t ret;
 
-#ifdef CONFIG_EXT4_FS_ENCRYPTION
-	if (ext4_encrypted_inode(inode) && S_ISREG(inode->i_mode))
-		return 0;
-#endif
+	if (ext4_encrypted_inode(inode) && S_ISREG(inode->i_mode)) {
+		if (iov_iter_rw(iter) == WRITE)
+			return 0;
+		if (test_opt(inode->i_sb, CIPHERTEXT_ACCESS) &&
+		    capable(CAP_SYS_ADMIN)) {
+			if (iov_iter_rw(iter) == WRITE)
+				return -EPERM;
+		} else
+			return 0;
+	}
 
 	/*
 	 * If we are doing data journalling we don't support O_DIRECT
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 486e869..6173b46 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1033,6 +1033,49 @@ void ext4_clear_inode(struct inode *inode)
 #endif
 }
 
+/*
+ * Create a copy of the inode structure so when we are reading the
+ * last block of an encrypted inode using direct I/O to get the
+ * ciphertext, we can futz with the i_size in the shadow inode.  This
+ * is necessary so that we can make a copy of the full AES block when
+ * i_size is not a multiple of the AES block size.
+ */
+struct inode *ext4_alloc_shadow_inode(struct inode *inode)
+{
+	struct ext4_inode_info *shadow_ei, *ei = EXT4_I(inode);
+	struct inode *shadow;
+
+	shadow_ei = kmem_cache_alloc(ext4_inode_cachep, GFP_NOFS);
+	if (!shadow_ei)
+		return NULL;
+
+	memcpy(shadow_ei, ei, sizeof(struct ext4_inode_info));
+	shadow = &shadow_ei->vfs_inode;
+
+	init_rwsem(&shadow_ei->xattr_sem);
+	init_rwsem(&shadow_ei->i_data_sem);
+	init_rwsem(&shadow_ei->i_mmap_sem);
+	i_size_ordered_init(shadow);
+	mutex_init(&shadow->i_mutex);
+	spin_lock_init(&shadow_ei->i_raw_lock);
+	spin_lock_init(&shadow_ei->i_prealloc_lock);
+	spin_lock_init(&(shadow_ei->i_block_reservation_lock));
+	spin_lock_init(&shadow_ei->i_completed_io_lock);
+	rwlock_init(&shadow_ei->i_es_lock);
+	ext4_es_init_tree(&shadow_ei->i_es_tree);
+	INIT_LIST_HEAD(&shadow_ei->i_es_list);
+	shadow_ei->i_es_all_nr = 0;
+	shadow_ei->i_es_shk_nr = 0;
+
+	return shadow;
+}
+
+void ext4_free_shadow_inode(struct inode *shadow)
+{
+	ext4_es_remove_extent(shadow, 0, EXT_MAX_BLOCKS);
+	kmem_cache_free(ext4_inode_cachep, EXT4_I(shadow));
+}
+
 static struct inode *ext4_nfs_get_inode(struct super_block *sb,
 					u64 ino, u32 generation)
 {
@@ -1182,6 +1225,7 @@ enum {
 	Opt_journal_path, Opt_journal_checksum, Opt_journal_async_commit,
 	Opt_abort, Opt_data_journal, Opt_data_ordered, Opt_data_writeback,
 	Opt_data_err_abort, Opt_data_err_ignore, Opt_test_dummy_encryption,
+	Opt_ciphertext_access, Opt_nociphertext_access,
 	Opt_usrjquota, Opt_grpjquota, Opt_offusrjquota, Opt_offgrpjquota,
 	Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0, Opt_jqfmt_vfsv1, Opt_quota,
 	Opt_noquota, Opt_barrier, Opt_nobarrier, Opt_err,
@@ -1273,6 +1317,8 @@ static const match_table_t tokens = {
 	{Opt_noinit_itable, "noinit_itable"},
 	{Opt_max_dir_size_kb, "max_dir_size_kb=%u"},
 	{Opt_test_dummy_encryption, "test_dummy_encryption"},
+	{Opt_ciphertext_access, "ciphertext_access"},
+	{Opt_nociphertext_access, "nociphertext_access"},
 	{Opt_removed, "check=none"},	/* mount option from ext2/3 */
 	{Opt_removed, "nocheck"},	/* mount option from ext2/3 */
 	{Opt_removed, "reservation"},	/* mount option from ext2/3 */
@@ -1475,6 +1521,8 @@ static const struct mount_opts {
 	{Opt_jqfmt_vfsv1, QFMT_VFS_V1, MOPT_QFMT},
 	{Opt_max_dir_size_kb, 0, MOPT_GTE0},
 	{Opt_test_dummy_encryption, 0, MOPT_GTE0},
+	{Opt_ciphertext_access, EXT4_MOUNT_CIPHERTEXT_ACCESS, MOPT_SET},
+	{Opt_nociphertext_access, EXT4_MOUNT_CIPHERTEXT_ACCESS, MOPT_CLEAR},
 	{Opt_err, 0, 0}
 };
 
-- 
2.5.0


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

* [PATCH v4 3/4] ext4 crypto: simplify interfaces to directory entry insert functions
  2015-12-16 22:46 [PATCH v4 0/4] ext4 crypto: backup and restore encrypted files Theodore Ts'o
  2015-12-16 22:46 ` [PATCH v4 1/4] ext4 crypto: add missing locking for keyring_key access Theodore Ts'o
  2015-12-16 22:46 ` [PATCH v4 2/4] ext4 crypto: add ciphertext_access mount option Theodore Ts'o
@ 2015-12-16 22:46 ` Theodore Ts'o
  2015-12-16 22:46 ` [PATCH v4 4/4] ext4 crypto: add ioctls to allow backup of encryption metadata Theodore Ts'o
  3 siblings, 0 replies; 5+ messages in thread
From: Theodore Ts'o @ 2015-12-16 22:46 UTC (permalink / raw)
  To: Ext4 Developers List; +Cc: Theodore Ts'o

A number of functions include ext4_add_dx_entry, make_indexed_dir,
etc. are being passed a dentry even though the only thing they use is
the containing parent.  We can shrink the code size slightly by maing
this replacement.  This will also be useful in cases where we don't
have a dentry as the argument to the directory entry insert functions.

Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/ext4.h   |  3 +--
 fs/ext4/inline.c | 10 ++++------
 fs/ext4/namei.c  | 15 ++++++---------
 3 files changed, 11 insertions(+), 17 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index cf7a885..9fdbd06 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -3007,8 +3007,7 @@ extern int ext4_da_write_inline_data_end(struct inode *inode, loff_t pos,
 					 struct page *page);
 extern int ext4_try_add_inline_entry(handle_t *handle,
 				     struct ext4_filename *fname,
-				     struct dentry *dentry,
-				     struct inode *inode);
+				     struct inode *dir, struct inode *inode);
 extern int ext4_try_create_inline_dir(handle_t *handle,
 				      struct inode *parent,
 				      struct inode *inode);
diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index d884989..dfe3b9b 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -995,12 +995,11 @@ void ext4_show_inline_dir(struct inode *dir, struct buffer_head *bh,
  */
 static int ext4_add_dirent_to_inline(handle_t *handle,
 				     struct ext4_filename *fname,
-				     struct dentry *dentry,
+				     struct inode *dir,
 				     struct inode *inode,
 				     struct ext4_iloc *iloc,
 				     void *inline_start, int inline_size)
 {
-	struct inode	*dir = d_inode(dentry->d_parent);
 	int		err;
 	struct ext4_dir_entry_2 *de;
 
@@ -1245,12 +1244,11 @@ out:
  * the new created block.
  */
 int ext4_try_add_inline_entry(handle_t *handle, struct ext4_filename *fname,
-			      struct dentry *dentry, struct inode *inode)
+			      struct inode *dir, struct inode *inode)
 {
 	int ret, inline_size;
 	void *inline_start;
 	struct ext4_iloc iloc;
-	struct inode *dir = d_inode(dentry->d_parent);
 
 	ret = ext4_get_inode_loc(dir, &iloc);
 	if (ret)
@@ -1264,7 +1262,7 @@ int ext4_try_add_inline_entry(handle_t *handle, struct ext4_filename *fname,
 						 EXT4_INLINE_DOTDOT_SIZE;
 	inline_size = EXT4_MIN_INLINE_DATA_SIZE - EXT4_INLINE_DOTDOT_SIZE;
 
-	ret = ext4_add_dirent_to_inline(handle, fname, dentry, inode, &iloc,
+	ret = ext4_add_dirent_to_inline(handle, fname, dir, inode, &iloc,
 					inline_start, inline_size);
 	if (ret != -ENOSPC)
 		goto out;
@@ -1285,7 +1283,7 @@ int ext4_try_add_inline_entry(handle_t *handle, struct ext4_filename *fname,
 	if (inline_size) {
 		inline_start = ext4_get_inline_xattr_pos(dir, &iloc);
 
-		ret = ext4_add_dirent_to_inline(handle, fname, dentry,
+		ret = ext4_add_dirent_to_inline(handle, fname, dir,
 						inode, &iloc, inline_start,
 						inline_size);
 
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index a969ab3..06c3afc 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -273,7 +273,7 @@ static struct buffer_head * ext4_dx_find_entry(struct inode *dir,
 		struct ext4_filename *fname,
 		struct ext4_dir_entry_2 **res_dir);
 static int ext4_dx_add_entry(handle_t *handle, struct ext4_filename *fname,
-			     struct dentry *dentry, struct inode *inode);
+			     struct inode *dir, struct inode *inode);
 
 /* checksumming functions */
 void initialize_dirent_tail(struct ext4_dir_entry_tail *t,
@@ -1928,10 +1928,9 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname,
  * directory, and adds the dentry to the indexed directory.
  */
 static int make_indexed_dir(handle_t *handle, struct ext4_filename *fname,
-			    struct dentry *dentry,
+			    struct inode *dir,
 			    struct inode *inode, struct buffer_head *bh)
 {
-	struct inode	*dir = d_inode(dentry->d_parent);
 	struct buffer_head *bh2;
 	struct dx_root	*root;
 	struct dx_frame	frames[2], *frame;
@@ -2086,8 +2085,7 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
 		return retval;
 
 	if (ext4_has_inline_data(dir)) {
-		retval = ext4_try_add_inline_entry(handle, &fname,
-						   dentry, inode);
+		retval = ext4_try_add_inline_entry(handle, &fname, dir, inode);
 		if (retval < 0)
 			goto out;
 		if (retval == 1) {
@@ -2097,7 +2095,7 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
 	}
 
 	if (is_dx(dir)) {
-		retval = ext4_dx_add_entry(handle, &fname, dentry, inode);
+		retval = ext4_dx_add_entry(handle, &fname, dir, inode);
 		if (!retval || (retval != ERR_BAD_DX_DIR))
 			goto out;
 		ext4_clear_inode_flag(dir, EXT4_INODE_INDEX);
@@ -2119,7 +2117,7 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
 
 		if (blocks == 1 && !dx_fallback &&
 		    ext4_has_feature_dir_index(sb)) {
-			retval = make_indexed_dir(handle, &fname, dentry,
+			retval = make_indexed_dir(handle, &fname, dir,
 						  inode, bh);
 			bh = NULL; /* make_indexed_dir releases bh */
 			goto out;
@@ -2154,12 +2152,11 @@ out:
  * Returns 0 for success, or a negative error value
  */
 static int ext4_dx_add_entry(handle_t *handle, struct ext4_filename *fname,
-			     struct dentry *dentry, struct inode *inode)
+			     struct inode *dir, struct inode *inode)
 {
 	struct dx_frame frames[2], *frame;
 	struct dx_entry *entries, *at;
 	struct buffer_head *bh;
-	struct inode *dir = d_inode(dentry->d_parent);
 	struct super_block *sb = dir->i_sb;
 	struct ext4_dir_entry_2 *de;
 	int err;
-- 
2.5.0


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

* [PATCH v4 4/4] ext4 crypto: add ioctls to allow backup of encryption metadata
  2015-12-16 22:46 [PATCH v4 0/4] ext4 crypto: backup and restore encrypted files Theodore Ts'o
                   ` (2 preceding siblings ...)
  2015-12-16 22:46 ` [PATCH v4 3/4] ext4 crypto: simplify interfaces to directory entry insert functions Theodore Ts'o
@ 2015-12-16 22:46 ` Theodore Ts'o
  3 siblings, 0 replies; 5+ messages in thread
From: Theodore Ts'o @ 2015-12-16 22:46 UTC (permalink / raw)
  To: Ext4 Developers List; +Cc: Theodore Ts'o

Add new ioctls which allow for the metadata of encrypted files (both
the filename and the crypto policy) to be backed up and restored.

Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/crypto_key.c  | 124 ++++++++++++++++++++++++++++++++++++-
 fs/ext4/ext4.h        |  12 ++++
 fs/ext4/ext4_crypto.h |  16 +++++
 fs/ext4/ioctl.c       |  87 ++++++++++++++++++++++++++
 fs/ext4/namei.c       | 165 ++++++++++++++++++++++++++++++++++++++++++--------
 5 files changed, 379 insertions(+), 25 deletions(-)

diff --git a/fs/ext4/crypto_key.c b/fs/ext4/crypto_key.c
index 9a16d1e..4f4b2c8 100644
--- a/fs/ext4/crypto_key.c
+++ b/fs/ext4/crypto_key.c
@@ -10,11 +10,12 @@
 
 #include <keys/encrypted-type.h>
 #include <keys/user-type.h>
+#include <linux/crc16.h>
 #include <linux/random.h>
 #include <linux/scatterlist.h>
 #include <uapi/linux/keyctl.h>
 
-#include "ext4.h"
+#include "ext4_jbd2.h"
 #include "xattr.h"
 
 static void derive_crypt_complete(struct crypto_async_request *req, int rc)
@@ -274,3 +275,124 @@ int ext4_has_encryption_key(struct inode *inode)
 
 	return (ei->i_crypt_info != NULL);
 }
+
+int ext4_get_encryption_metadata(struct inode *inode,
+				 struct ext4_encrypted_metadata *mdata)
+{
+	unsigned char *cp = &mdata->metadata[0];
+	size_t size = mdata->len;
+	loff_t isize;
+	int res;
+
+	if (size < sizeof(struct ext4_encryption_context) + 12)
+		return -EINVAL;
+
+	if (!inode_owner_or_capable(inode) && !capable(CAP_SYS_ADMIN))
+		return -EACCES;
+
+	*cp++ = 'e';
+	*cp++ = '5';
+	*cp++ = 0;
+	*cp++ = 0;
+	isize = i_size_read(inode);
+	*((u32 *)cp) = cpu_to_le32(isize & 0xFFFFFFFF);
+	cp += 4;
+	*((u32 *)cp) = cpu_to_le32(isize >> 32);
+	cp += 4;
+	size -= 12;
+
+	res = ext4_xattr_get(inode, EXT4_XATTR_INDEX_ENCRYPTION,
+			     EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
+			     cp, size);
+
+	if (res < 0)
+		return res;
+	if (res > size)
+		return -ENOSPC;
+
+	mdata->len = res + 12;
+
+	*((u16 *) &mdata->metadata[2]) = cpu_to_le16(crc16(~0, mdata->metadata, mdata->len));
+	return 0;
+}
+
+int ext4_set_encryption_metadata(struct inode *inode,
+				 struct ext4_encrypted_metadata *mdata)
+{
+	struct ext4_encryption_context *ctx;
+	unsigned char *cp = &mdata->metadata[0];
+	handle_t *handle = NULL;
+	loff_t size;
+	unsigned bs = inode->i_sb->s_blocksize;
+	int res;
+	u16 crc;
+
+	if (!inode_owner_or_capable(inode) && !capable(CAP_SYS_ADMIN))
+		return -EACCES;
+
+	if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode))
+		return -EINVAL;
+
+	if (mdata->len != sizeof(struct ext4_encryption_context) + 12)
+		return -EINVAL;
+
+	if (cp[0] != 'e' || cp[1] != '5')
+		return -EINVAL;
+	crc = le16_to_cpu(*(u16 *)(cp+2));
+	cp[2] = cp[3] = 0;
+	cp += 4;
+
+	if (crc != crc16(~0, mdata->metadata, mdata->len))
+		return -EINVAL;
+
+	size = le32_to_cpu(*(u32 *) cp);
+	cp += 4;
+	size += ((u64) le32_to_cpu(*(u32 *) cp)) << 32;
+	cp += 4;
+
+	ctx = (struct ext4_encryption_context *) cp;
+	if ((ctx->format != EXT4_ENCRYPTION_CONTEXT_FORMAT_V1) ||
+	    !ext4_valid_contents_enc_mode(ctx->contents_encryption_mode) ||
+	    !ext4_valid_filenames_enc_mode(ctx->filenames_encryption_mode) ||
+	    (ctx->flags & ~EXT4_POLICY_FLAGS_VALID))
+		return -EINVAL;
+
+	res = ext4_convert_inline_data(inode);
+	if (res)
+		return res;
+
+	res = filemap_write_and_wait(&inode->i_data);
+	if (res)
+		return res;
+
+	mutex_lock(&inode->i_mutex);
+	if (round_up(size, bs) != round_up(i_size_read(inode), bs)) {
+		res = -EINVAL;
+		goto errout;
+	}
+
+	handle = ext4_journal_start(inode, EXT4_HT_MISC,
+				    ext4_jbd2_credits_xattr(inode));
+	if (IS_ERR(handle))
+		return PTR_ERR(handle);
+	res = ext4_xattr_set(inode, EXT4_XATTR_INDEX_ENCRYPTION,
+			     EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, ctx,
+			     sizeof(struct ext4_encryption_context), 0);
+	if (res < 0)
+		goto errout;
+	ext4_set_inode_flag(inode, EXT4_INODE_ENCRYPT);
+	ext4_clear_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);
+
+	i_size_write(inode, size);
+	EXT4_I(inode)->i_disksize = size;
+	res = ext4_mark_inode_dirty(handle, inode);
+	if (res)
+		EXT4_ERROR_INODE(inode, "Failed to mark inode dirty");
+	else
+		res = ext4_get_encryption_info(inode);
+errout:
+	mutex_unlock(&inode->i_mutex);
+	if (handle)
+		ext4_journal_stop(handle);
+	return res;
+}
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 9fdbd06..1136f03 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -617,6 +617,10 @@ enum {
 #define EXT4_IOC_SET_ENCRYPTION_POLICY	_IOR('f', 19, struct ext4_encryption_policy)
 #define EXT4_IOC_GET_ENCRYPTION_PWSALT	_IOW('f', 20, __u8[16])
 #define EXT4_IOC_GET_ENCRYPTION_POLICY	_IOW('f', 21, struct ext4_encryption_policy)
+#define EXT4_IOC_GET_ENCRYPTION_METADATA _IOWR('f', 22, struct ext4_encrypted_metadata)
+#define EXT4_IOC_SET_ENCRYPTION_METADATA _IOR('f', 23, struct ext4_encrypted_metadata)
+#define EXT4_IOC_GET_ENCRYPTED_FILENAME	_IOWR('f', 24, struct ext4_encrypted_metadata)
+#define EXT4_IOC_SET_ENCRYPTED_FILENAME	_IOR('f', 25, struct ext4_set_encrypted_fname)
 
 #if defined(__KERNEL__) && defined(CONFIG_COMPAT)
 /*
@@ -2311,6 +2315,10 @@ static inline void ext4_fname_free_filename(struct ext4_filename *fname) { }
 void ext4_free_crypt_info(struct ext4_crypt_info *ci);
 void ext4_free_encryption_info(struct inode *inode, struct ext4_crypt_info *ci);
 int _ext4_get_encryption_info(struct inode *inode);
+int ext4_set_encryption_metadata(struct inode *inode,
+				 struct ext4_encrypted_metadata *mdata);
+int ext4_get_encryption_metadata(struct inode *inode,
+				 struct ext4_encrypted_metadata *mdata);
 
 #ifdef CONFIG_EXT4_FS_ENCRYPTION
 int ext4_has_encryption_key(struct inode *inode);
@@ -2546,6 +2554,10 @@ extern int ext4_generic_delete_entry(handle_t *handle,
 				     int buf_size,
 				     int csum_size);
 extern int ext4_empty_dir(struct inode *inode);
+extern int ext4_get_encrypted_filename(struct file *filp,
+				       struct ext4_encrypted_metadata *mdata);
+extern int ext4_set_encrypted_filename(struct inode *dir,
+				       struct ext4_set_encrypted_fname *efn);
 
 /* resize.c */
 extern int ext4_group_add(struct super_block *sb,
diff --git a/fs/ext4/ext4_crypto.h b/fs/ext4/ext4_crypto.h
index ac7d4e8..eb7088a 100644
--- a/fs/ext4/ext4_crypto.h
+++ b/fs/ext4/ext4_crypto.h
@@ -156,4 +156,20 @@ static inline u32 encrypted_symlink_data_len(u32 l)
 	return (l + sizeof(struct ext4_encrypted_symlink_data) - 1);
 }
 
+/**
+ * Structure used for communicating encrypted metadata with userspace
+ */
+struct ext4_encrypted_metadata {
+	u32 len;
+	unsigned char metadata[288];
+};
+
+/**
+ * Structured used for setting an encrypted file name
+ */
+struct ext4_set_encrypted_fname {
+	s32 fd;
+	u32 len;
+	unsigned char enc_fname[256];
+};
 #endif	/* _EXT4_CRYPTO_H */
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index 5e872fd..e86a39e 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -689,6 +689,90 @@ encryption_policy_out:
 		return -EOPNOTSUPP;
 #endif
 	}
+	case EXT4_IOC_GET_ENCRYPTION_METADATA: {
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+		struct ext4_encrypted_metadata mdata;
+		int err = 0;
+
+		if (get_user(mdata.len, (u32 __user *) arg))
+			return -EFAULT;
+		if (mdata.len > sizeof(mdata.metadata))
+			return -EINVAL;
+
+		if (!ext4_encrypted_inode(inode))
+			return -ENOENT;
+		err = ext4_get_encryption_metadata(inode, &mdata);
+		if (err)
+			return err;
+		if (copy_to_user((void __user *)arg, &mdata, sizeof(mdata)))
+			return -EFAULT;
+		return 0;
+#else
+		return -EOPNOTSUPP;
+#endif
+	}
+	case EXT4_IOC_SET_ENCRYPTION_METADATA: {
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+		struct ext4_encrypted_metadata mdata;
+		int err = 0;
+
+		if (ext4_encrypted_inode(inode))
+			return -EINVAL;
+		if (copy_from_user(&mdata,
+				   (struct ext4_encrypted_metadata __user *)arg,
+				   sizeof(mdata)))
+			return -EFAULT;
+		err = mnt_want_write_file(filp);
+		if (err)
+			return err;
+		err = ext4_set_encryption_metadata(inode, &mdata);
+		mnt_drop_write_file(filp);
+		return err;
+#else
+		return -EOPNOTSUPP;
+#endif
+	}
+	case EXT4_IOC_GET_ENCRYPTED_FILENAME: {
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+		struct ext4_encrypted_metadata mdata;
+		int err = 0;
+
+		if (get_user(mdata.len, (u32 __user *) arg))
+			return -EFAULT;
+		if (mdata.len > sizeof(mdata.metadata))
+			return -EINVAL;
+
+		if (!ext4_encrypted_inode(inode))
+			return -ENOENT;
+		err = ext4_get_encrypted_filename(filp, &mdata);
+		if (err)
+			return err;
+		if (copy_to_user((void __user *)arg, &mdata, sizeof(mdata)))
+			return -EFAULT;
+		return 0;
+#else
+		return -EOPNOTSUPP;
+#endif
+	}
+	case EXT4_IOC_SET_ENCRYPTED_FILENAME: {
+#ifdef CONFIG_EXT4_FS_ENCRYPTION
+		struct ext4_set_encrypted_fname enc_fname;
+		int err = 0;
+
+		if (copy_from_user(&enc_fname,
+				   (struct ext4_set_encrypted_fname __user *)arg,
+				   sizeof(enc_fname)))
+			return -EFAULT;
+		err = mnt_want_write_file(filp);
+		if (err)
+			return err;
+		err = ext4_set_encrypted_filename(inode, &enc_fname);
+		mnt_drop_write_file(filp);
+		return err;
+#else
+		return -EOPNOTSUPP;
+#endif
+	}
 	default:
 		return -ENOTTY;
 	}
@@ -755,6 +839,9 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 	case EXT4_IOC_SET_ENCRYPTION_POLICY:
 	case EXT4_IOC_GET_ENCRYPTION_PWSALT:
 	case EXT4_IOC_GET_ENCRYPTION_POLICY:
+	case EXT4_IOC_GET_ENCRYPTION_METADATA:
+	case EXT4_IOC_SET_ENCRYPTION_METADATA:
+	case EXT4_IOC_GET_ENCRYPTED_FILENAME:
 		break;
 	default:
 		return -ENOIOCTLCMD;
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 06c3afc..9e4d983 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -33,6 +33,7 @@
 #include <linux/quotaops.h>
 #include <linux/buffer_head.h>
 #include <linux/bio.h>
+#include <linux/file.h>
 #include "ext4.h"
 #include "ext4_jbd2.h"
 
@@ -2048,24 +2049,16 @@ out_frames:
 }
 
 /*
- *	ext4_add_entry()
- *
- * adds a file entry to the specified directory, using the same
- * semantics as ext4_find_entry(). It returns NULL if it failed.
- *
- * NOTE!! The inode part of 'de' is left at 0 - which means you
- * may not sleep between calling this and putting something into
- * the entry, as someone else might have used it while you slept.
+ * Add a directory entry to a directory, given the filename and the
+ * inode it will point to.
  */
-static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
-			  struct inode *inode)
+static int ext4_add_fname(handle_t *handle, struct inode *dir,
+			  struct ext4_filename *fname, struct inode *inode)
 {
-	struct inode *dir = d_inode(dentry->d_parent);
 	struct buffer_head *bh = NULL;
 	struct ext4_dir_entry_2 *de;
 	struct ext4_dir_entry_tail *t;
 	struct super_block *sb;
-	struct ext4_filename fname;
 	int	retval;
 	int	dx_fallback=0;
 	unsigned blocksize;
@@ -2077,15 +2070,9 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
 
 	sb = dir->i_sb;
 	blocksize = sb->s_blocksize;
-	if (!dentry->d_name.len)
-		return -EINVAL;
-
-	retval = ext4_fname_setup_filename(dir, &dentry->d_name, 0, &fname);
-	if (retval)
-		return retval;
 
 	if (ext4_has_inline_data(dir)) {
-		retval = ext4_try_add_inline_entry(handle, &fname, dir, inode);
+		retval = ext4_try_add_inline_entry(handle, fname, dir, inode);
 		if (retval < 0)
 			goto out;
 		if (retval == 1) {
@@ -2095,7 +2082,7 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
 	}
 
 	if (is_dx(dir)) {
-		retval = ext4_dx_add_entry(handle, &fname, dir, inode);
+		retval = ext4_dx_add_entry(handle, fname, dir, inode);
 		if (!retval || (retval != ERR_BAD_DX_DIR))
 			goto out;
 		ext4_clear_inode_flag(dir, EXT4_INODE_INDEX);
@@ -2110,14 +2097,14 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
 			bh = NULL;
 			goto out;
 		}
-		retval = add_dirent_to_buf(handle, &fname, dir, inode,
+		retval = add_dirent_to_buf(handle, fname, dir, inode,
 					   NULL, bh);
 		if (retval != -ENOSPC)
 			goto out;
 
 		if (blocks == 1 && !dx_fallback &&
 		    ext4_has_feature_dir_index(sb)) {
-			retval = make_indexed_dir(handle, &fname, dir,
+			retval = make_indexed_dir(handle, fname, dir,
 						  inode, bh);
 			bh = NULL; /* make_indexed_dir releases bh */
 			goto out;
@@ -2139,9 +2126,8 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
 		initialize_dirent_tail(t, blocksize);
 	}
 
-	retval = add_dirent_to_buf(handle, &fname, dir, inode, de, bh);
+	retval = add_dirent_to_buf(handle, fname, dir, inode, de, bh);
 out:
-	ext4_fname_free_filename(&fname);
 	brelse(bh);
 	if (retval == 0)
 		ext4_set_inode_state(inode, EXT4_STATE_NEWENTRY);
@@ -2149,6 +2135,29 @@ out:
 }
 
 /*
+ * Create a directory entry associated with the specified dentry and
+ * inode.
+ */
+static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
+			  struct inode *inode)
+{
+	struct inode *dir = d_inode(dentry->d_parent);
+	struct ext4_filename fname;
+	int	retval;
+
+	if (!dentry->d_name.len)
+		return -EINVAL;
+
+	retval = ext4_fname_setup_filename(dir, &dentry->d_name, 0, &fname);
+	if (retval)
+		return retval;
+
+	retval = ext4_add_fname(handle, dir, &fname, inode);
+	ext4_fname_free_filename(&fname);
+	return retval;
+}
+
+/*
  * Returns 0 for success, or a negative error value
  */
 static int ext4_dx_add_entry(handle_t *handle, struct ext4_filename *fname,
@@ -3858,3 +3867,111 @@ const struct inode_operations ext4_special_inode_operations = {
 	.get_acl	= ext4_get_acl,
 	.set_acl	= ext4_set_acl,
 };
+
+int ext4_get_encrypted_filename(struct file *filp,
+				struct ext4_encrypted_metadata *mdata)
+{
+	struct dentry *dentry = filp->f_path.dentry;
+	struct inode *dir = dentry->d_parent->d_inode;
+	struct buffer_head *bh;
+	struct ext4_dir_entry_2 *de;
+
+	if (!dir || !ext4_encrypted_inode(dir))
+		return -EINVAL;
+
+	if (!inode_owner_or_capable(dir) && !capable(CAP_SYS_ADMIN))
+		return -EACCES;
+
+	bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL);
+	if (IS_ERR(bh))
+		return PTR_ERR(bh);
+	if (de == NULL)
+		return -ENOENT;
+
+	if (mdata->len < de->name_len)
+		return -ENOSPC;
+	mdata->len = de->name_len;
+	memcpy(mdata->metadata, de->name, de->name_len);
+	return 0;
+}
+
+int ext4_set_encrypted_filename(struct inode *dir,
+				struct ext4_set_encrypted_fname *efn)
+{
+	handle_t *handle = NULL;
+	struct ext4_filename fname;
+	struct fd fd;
+	struct inode *inode;
+	umode_t mode;
+	int retval = 0;
+
+	retval = inode_permission(dir, MAY_WRITE | MAY_EXEC);
+	if (retval)
+		return retval;
+
+	if (efn->len >= sizeof(efn->enc_fname))
+		return -EINVAL;
+
+	fd = fdget(efn->fd);
+	if (!fd.file)
+		return -EBADF;
+	inode = file_inode(fd.file);
+	mode = inode->i_mode;
+
+	retval = -EPERM;
+	if (!S_ISREG(mode))
+		goto out;
+
+	if ((mode & S_ISUID) ||
+	    ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))) {
+		/*
+		 * root or the inode owner can link even in the case
+		 * of "unsafe" hard link sources.  See
+		 * safe_hardlink_sources() in fs/namei.c
+		 */
+		if (!inode_owner_or_capable(inode) && !capable(CAP_SYS_ADMIN)) {
+			retval = -EACCES;
+			goto out;
+		}
+	}
+
+	retval = inode_permission(inode, MAY_READ | MAY_WRITE);
+	if (!retval && !inode_owner_or_capable(inode) &&
+	    !capable(CAP_SYS_ADMIN))
+		goto out;
+
+	if (!ext4_is_child_context_consistent_with_parent(dir, inode)) {
+		retval = -EPERM;
+		goto out;
+	}
+
+	memset(&fname, 0, sizeof(fname));
+	fname.disk_name.name = efn->enc_fname;
+	fname.disk_name.len = efn->len;
+
+	handle = ext4_journal_start(dir, EXT4_HT_DIR,
+		(EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
+		 EXT4_INDEX_EXTRA_TRANS_BLOCKS) + 2);
+	if (IS_ERR(handle)) {
+		retval = PTR_ERR(handle);
+		goto out;
+	}
+
+	pr_err("ext4_add_fname\n");
+	retval = ext4_add_fname(handle, dir, &fname, file_inode(fd.file));
+	if (retval)
+		goto out;
+
+	ext4_inc_count(handle, inode);
+	ext4_mark_inode_dirty(handle, inode);
+	if (S_ISDIR(inode->i_mode))
+		ext4_inc_count(handle, dir);
+	ext4_update_dx_flag(dir);
+	ext4_mark_inode_dirty(handle, dir);
+
+out:
+	fdput(fd);
+	if (handle)
+		ext4_journal_stop(handle);
+	return retval;
+}
-- 
2.5.0


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

end of thread, other threads:[~2015-12-16 22:46 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-12-16 22:46 [PATCH v4 0/4] ext4 crypto: backup and restore encrypted files Theodore Ts'o
2015-12-16 22:46 ` [PATCH v4 1/4] ext4 crypto: add missing locking for keyring_key access Theodore Ts'o
2015-12-16 22:46 ` [PATCH v4 2/4] ext4 crypto: add ciphertext_access mount option Theodore Ts'o
2015-12-16 22:46 ` [PATCH v4 3/4] ext4 crypto: simplify interfaces to directory entry insert functions Theodore Ts'o
2015-12-16 22:46 ` [PATCH v4 4/4] ext4 crypto: add ioctls to allow backup of encryption metadata Theodore Ts'o

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).