linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 01/18] f2fs: avoid value overflow in showing current status
@ 2015-05-09  4:20 Jaegeuk Kim
  2015-05-09  4:20 ` [PATCH 02/18] f2fs: report unwritten area in f2fs_fiemap Jaegeuk Kim
                   ` (16 more replies)
  0 siblings, 17 replies; 34+ messages in thread
From: Jaegeuk Kim @ 2015-05-09  4:20 UTC (permalink / raw)
  To: linux-kernel, linux-fsdevel, linux-f2fs-devel; +Cc: Jaegeuk Kim

This patch fixes overflow when do cat /sys/kernel/debug/f2fs/status.
If a section is relatively large, dist value can be overflowed.

Reported-by: Yossi Goldfill <ygoldfill@radianmemory.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---
 fs/f2fs/debug.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c
index f50acbc..efbc83f 100644
--- a/fs/f2fs/debug.c
+++ b/fs/f2fs/debug.c
@@ -94,7 +94,8 @@ static void update_general_status(struct f2fs_sb_info *sbi)
 static void update_sit_info(struct f2fs_sb_info *sbi)
 {
 	struct f2fs_stat_info *si = F2FS_STAT(sbi);
-	unsigned int blks_per_sec, hblks_per_sec, total_vblocks, bimodal, dist;
+	unsigned long long blks_per_sec, hblks_per_sec, total_vblocks;
+	unsigned long long bimodal, dist;
 	unsigned int segno, vblocks;
 	int ndirty = 0;
 
-- 
2.1.1


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

* [PATCH 02/18] f2fs: report unwritten area in f2fs_fiemap
  2015-05-09  4:20 [PATCH 01/18] f2fs: avoid value overflow in showing current status Jaegeuk Kim
@ 2015-05-09  4:20 ` Jaegeuk Kim
  2015-05-09  4:20 ` [PATCH 03/18] f2fs crypto: declare some definitions for f2fs encryption feature Jaegeuk Kim
                   ` (15 subsequent siblings)
  16 siblings, 0 replies; 34+ messages in thread
From: Jaegeuk Kim @ 2015-05-09  4:20 UTC (permalink / raw)
  To: linux-kernel, linux-fsdevel, linux-f2fs-devel; +Cc: Jaegeuk Kim

This patch slightly changes f2fs_fiemap function to report unwritten area.

Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---
 fs/f2fs/data.c | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 fs/f2fs/f2fs.h |   4 +-
 2 files changed, 117 insertions(+), 4 deletions(-)

diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 3b76261..842fcdd 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -1241,6 +1241,8 @@ static int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map,
 	if (dn.data_blkaddr != NULL_ADDR) {
 		map->m_flags = F2FS_MAP_MAPPED;
 		map->m_pblk = dn.data_blkaddr;
+		if (dn.data_blkaddr == NEW_ADDR)
+			map->m_flags |= F2FS_MAP_UNWRITTEN;
 	} else if (create) {
 		err = __allocate_data_block(&dn);
 		if (err)
@@ -1288,7 +1290,10 @@ get_next:
 			blkaddr = dn.data_blkaddr;
 		}
 		/* Give more consecutive addresses for the readahead */
-		if (map->m_pblk != NEW_ADDR && blkaddr == (map->m_pblk + ofs)) {
+		if ((map->m_pblk != NEW_ADDR &&
+				blkaddr == (map->m_pblk + ofs)) ||
+				(map->m_pblk == NEW_ADDR &&
+				blkaddr == NEW_ADDR)) {
 			ofs++;
 			dn.ofs_in_node++;
 			pgofs++;
@@ -1339,11 +1344,117 @@ static int get_data_block_fiemap(struct inode *inode, sector_t iblock,
 	return __get_data_block(inode, iblock, bh_result, create, true);
 }
 
+static inline sector_t logical_to_blk(struct inode *inode, loff_t offset)
+{
+	return (offset >> inode->i_blkbits);
+}
+
+static inline loff_t blk_to_logical(struct inode *inode, sector_t blk)
+{
+	return (blk << inode->i_blkbits);
+}
+
 int f2fs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
 		u64 start, u64 len)
 {
-	return generic_block_fiemap(inode, fieinfo,
-				start, len, get_data_block_fiemap);
+	struct buffer_head map_bh;
+	sector_t start_blk, last_blk;
+	loff_t isize = i_size_read(inode);
+	u64 logical = 0, phys = 0, size = 0;
+	u32 flags = 0;
+	bool past_eof = false, whole_file = false;
+	int ret = 0;
+
+	ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC);
+	if (ret)
+		return ret;
+
+	mutex_lock(&inode->i_mutex);
+
+	if (len >= isize) {
+		whole_file = true;
+		len = isize;
+	}
+
+	if (logical_to_blk(inode, len) == 0)
+		len = blk_to_logical(inode, 1);
+
+	start_blk = logical_to_blk(inode, start);
+	last_blk = logical_to_blk(inode, start + len - 1);
+next:
+	memset(&map_bh, 0, sizeof(struct buffer_head));
+	map_bh.b_size = len;
+
+	ret = get_data_block_fiemap(inode, start_blk, &map_bh, 0);
+	if (ret)
+		goto out;
+
+	/* HOLE */
+	if (!buffer_mapped(&map_bh)) {
+		start_blk++;
+
+		if (!past_eof && blk_to_logical(inode, start_blk) >= isize)
+			past_eof = 1;
+
+		if (past_eof && size) {
+			flags |= FIEMAP_EXTENT_LAST;
+			ret = fiemap_fill_next_extent(fieinfo, logical,
+					phys, size, flags);
+		} else if (size) {
+			ret = fiemap_fill_next_extent(fieinfo, logical,
+					phys, size, flags);
+			size = 0;
+		}
+
+		/* if we have holes up to/past EOF then we're done */
+		if (start_blk > last_blk || past_eof || ret)
+			goto out;
+	} else {
+		if (start_blk > last_blk && !whole_file) {
+			ret = fiemap_fill_next_extent(fieinfo, logical,
+					phys, size, flags);
+			goto out;
+		}
+
+		/*
+		 * if size != 0 then we know we already have an extent
+		 * to add, so add it.
+		 */
+		if (size) {
+			ret = fiemap_fill_next_extent(fieinfo, logical,
+					phys, size, flags);
+			if (ret)
+				goto out;
+		}
+
+		logical = blk_to_logical(inode, start_blk);
+		phys = blk_to_logical(inode, map_bh.b_blocknr);
+		size = map_bh.b_size;
+		flags = 0;
+		if (buffer_unwritten(&map_bh))
+			flags = FIEMAP_EXTENT_UNWRITTEN;
+
+		start_blk += logical_to_blk(inode, size);
+
+		/*
+		 * If we are past the EOF, then we need to make sure as
+		 * soon as we find a hole that the last extent we found
+		 * is marked with FIEMAP_EXTENT_LAST
+		 */
+		if (!past_eof && logical + size >= isize)
+			past_eof = true;
+	}
+	cond_resched();
+	if (fatal_signal_pending(current))
+		ret = -EINTR;
+	else
+		goto next;
+out:
+	if (ret == 1)
+		ret = 0;
+
+	mutex_unlock(&inode->i_mutex);
+	return ret;
 }
 
 /*
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 160945f..477e65f 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -330,7 +330,9 @@ struct extent_tree {
  */
 #define F2FS_MAP_NEW		(1 << BH_New)
 #define F2FS_MAP_MAPPED		(1 << BH_Mapped)
-#define F2FS_MAP_FLAGS		(F2FS_MAP_NEW | F2FS_MAP_MAPPED)
+#define F2FS_MAP_UNWRITTEN	(1 << BH_Unwritten)
+#define F2FS_MAP_FLAGS		(F2FS_MAP_NEW | F2FS_MAP_MAPPED |\
+				F2FS_MAP_UNWRITTEN)
 
 struct f2fs_map_blocks {
 	block_t m_pblk;
-- 
2.1.1


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

* [PATCH 03/18] f2fs crypto: declare some definitions for f2fs encryption feature
  2015-05-09  4:20 [PATCH 01/18] f2fs: avoid value overflow in showing current status Jaegeuk Kim
  2015-05-09  4:20 ` [PATCH 02/18] f2fs: report unwritten area in f2fs_fiemap Jaegeuk Kim
@ 2015-05-09  4:20 ` Jaegeuk Kim
  2015-05-13  2:02   ` Dave Chinner
  2015-05-09  4:20 ` [PATCH 04/18] f2fs crypto: add f2fs encryption Kconfig Jaegeuk Kim
                   ` (14 subsequent siblings)
  16 siblings, 1 reply; 34+ messages in thread
From: Jaegeuk Kim @ 2015-05-09  4:20 UTC (permalink / raw)
  To: linux-kernel, linux-fsdevel, linux-f2fs-devel; +Cc: Jaegeuk Kim

This definitions will be used by inode and superblock for encyption.

Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---
 fs/f2fs/f2fs.h          |  54 ++++++++++++++++++
 fs/f2fs/f2fs_crypto.h   | 149 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/f2fs_fs.h |   4 +-
 3 files changed, 206 insertions(+), 1 deletion(-)
 create mode 100644 fs/f2fs/f2fs_crypto.h

diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 477e65f..c3c4deb 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -70,6 +70,8 @@ struct f2fs_mount_info {
 	unsigned int	opt;
 };
 
+#define F2FS_FEATURE_ENCRYPT	0x0001
+
 #define F2FS_HAS_FEATURE(sb, mask)					\
 	((F2FS_SB(sb)->raw_super->feature & cpu_to_le32(mask)) != 0)
 #define F2FS_SET_FEATURE(sb, mask)					\
@@ -346,6 +348,7 @@ struct f2fs_map_blocks {
  */
 #define FADVISE_COLD_BIT	0x01
 #define FADVISE_LOST_PINO_BIT	0x02
+#define FADVISE_ENCRYPT_BIT	0x04
 
 #define file_is_cold(inode)	is_file(inode, FADVISE_COLD_BIT)
 #define file_wrong_pino(inode)	is_file(inode, FADVISE_LOST_PINO_BIT)
@@ -353,6 +356,16 @@ struct f2fs_map_blocks {
 #define file_lost_pino(inode)	set_file(inode, FADVISE_LOST_PINO_BIT)
 #define file_clear_cold(inode)	clear_file(inode, FADVISE_COLD_BIT)
 #define file_got_pino(inode)	clear_file(inode, FADVISE_LOST_PINO_BIT)
+#define file_is_encrypt(inode)	is_file(inode, FADVISE_ENCRYPT_BIT)
+#define file_set_encrypt(inode)	set_file(inode, FADVISE_ENCRYPT_BIT)
+#define file_clear_encrypt(inode) clear_file(inode, FADVISE_ENCRYPT_BIT)
+
+/* Encryption algorithms */
+#define F2FS_ENCRYPTION_MODE_INVALID		0
+#define F2FS_ENCRYPTION_MODE_AES_256_XTS	1
+#define F2FS_ENCRYPTION_MODE_AES_256_GCM	2
+#define F2FS_ENCRYPTION_MODE_AES_256_CBC	3
+#define F2FS_ENCRYPTION_MODE_AES_256_CTS	4
 
 #define DEF_DIR_LEVEL		0
 
@@ -380,6 +393,11 @@ struct f2fs_inode_info {
 	struct radix_tree_root inmem_root;	/* radix tree for inmem pages */
 	struct list_head inmem_pages;	/* inmemory pages managed by f2fs */
 	struct mutex inmem_lock;	/* lock for inmemory pages */
+
+#ifdef CONFIG_F2FS_FS_ENCRYPTION
+	/* Encryption params */
+	struct f2fs_crypt_info *i_crypt_info;
+#endif
 };
 
 static inline void get_extent_info(struct extent_info *ext,
@@ -1891,4 +1909,40 @@ void f2fs_delete_inline_entry(struct f2fs_dir_entry *, struct page *,
 						struct inode *, struct inode *);
 bool f2fs_empty_inline_dir(struct inode *);
 int f2fs_read_inline_dir(struct file *, struct dir_context *);
+
+/*
+ * crypto support
+ */
+static inline int f2fs_encrypted_inode(struct inode *inode)
+{
+#ifdef CONFIG_F2FS_FS_ENCRYPTION
+	return file_is_encrypt(inode);
+#else
+	return 0;
+#endif
+}
+
+static inline void f2fs_set_encrypted_inode(struct inode *inode)
+{
+#ifdef CONFIG_F2FS_FS_ENCRYPTION
+	file_set_encrypt(inode);
+#endif
+}
+
+static inline bool f2fs_bio_encrypted(struct bio *bio)
+{
+#ifdef CONFIG_F2FS_FS_ENCRYPTION
+	return unlikely(bio->bi_private != NULL);
+#else
+	return false;
+#endif
+}
+
+static inline int f2fs_sb_has_crypto(struct super_block *sb)
+{
+#ifdef CONFIG_F2FS_FS_ENCRYPTION
+	return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_ENCRYPT);
+#else
+	return 0;
+#endif
 #endif
diff --git a/fs/f2fs/f2fs_crypto.h b/fs/f2fs/f2fs_crypto.h
new file mode 100644
index 0000000..cfc37c1
--- /dev/null
+++ b/fs/f2fs/f2fs_crypto.h
@@ -0,0 +1,149 @@
+/*
+ * linux/fs/f2fs/f2fs_crypto.h
+ *
+ * Copied from linux/fs/ext4/ext4_crypto.h
+ *
+ * Copyright (C) 2015, Google, Inc.
+ *
+ * This contains encryption header content for f2fs
+ *
+ * Written by Michael Halcrow, 2015.
+ * Modified by Jaegeuk Kim, 2015.
+ */
+#ifndef _F2FS_CRYPTO_H
+#define _F2FS_CRYPTO_H
+
+#include <linux/fs.h>
+
+#define F2FS_KEY_DESCRIPTOR_SIZE	8
+
+/* Policy provided via an ioctl on the topmost directory */
+struct f2fs_encryption_policy {
+	char version;
+	char contents_encryption_mode;
+	char filenames_encryption_mode;
+	char flags;
+	char master_key_descriptor[F2FS_KEY_DESCRIPTOR_SIZE];
+} __attribute__((__packed__));
+
+#define F2FS_ENCRYPTION_CONTEXT_FORMAT_V1	1
+#define F2FS_KEY_DERIVATION_NONCE_SIZE		16
+
+#define F2FS_POLICY_FLAGS_PAD_4		0x00
+#define F2FS_POLICY_FLAGS_PAD_8		0x01
+#define F2FS_POLICY_FLAGS_PAD_16	0x02
+#define F2FS_POLICY_FLAGS_PAD_32	0x03
+#define F2FS_POLICY_FLAGS_PAD_MASK	0x03
+#define F2FS_POLICY_FLAGS_VALID		0x03
+
+/**
+ * Encryption context for inode
+ *
+ * Protector format:
+ *  1 byte: Protector format (1 = this version)
+ *  1 byte: File contents encryption mode
+ *  1 byte: File names encryption mode
+ *  1 byte: Flags
+ *  8 bytes: Master Key descriptor
+ *  16 bytes: Encryption Key derivation nonce
+ */
+struct f2fs_encryption_context {
+	char format;
+	char contents_encryption_mode;
+	char filenames_encryption_mode;
+	char flags;
+	char master_key_descriptor[F2FS_KEY_DESCRIPTOR_SIZE];
+	char nonce[F2FS_KEY_DERIVATION_NONCE_SIZE];
+} __attribute__((__packed__));
+
+/* Encryption parameters */
+#define F2FS_XTS_TWEAK_SIZE 16
+#define F2FS_AES_128_ECB_KEY_SIZE 16
+#define F2FS_AES_256_GCM_KEY_SIZE 32
+#define F2FS_AES_256_CBC_KEY_SIZE 32
+#define F2FS_AES_256_CTS_KEY_SIZE 32
+#define F2FS_AES_256_XTS_KEY_SIZE 64
+#define F2FS_MAX_KEY_SIZE 64
+
+struct f2fs_encryption_key {
+	__u32 mode;
+	char raw[F2FS_MAX_KEY_SIZE];
+	__u32 size;
+} __attribute__((__packed__));
+
+struct f2fs_crypt_info {
+	unsigned char	ci_mode;
+	unsigned char	ci_size;
+	char		ci_data_mode;
+	char		ci_filename_mode;
+	char		ci_flags;
+	struct crypto_ablkcipher *ci_ctfm;
+	struct key	*ci_keyring_key;
+	char		ci_raw[F2FS_MAX_KEY_SIZE];
+	char		ci_master_key[F2FS_KEY_DESCRIPTOR_SIZE];
+};
+
+#define F2FS_CTX_REQUIRES_FREE_ENCRYPT_FL             0x00000001
+#define F2FS_BOUNCE_PAGE_REQUIRES_FREE_ENCRYPT_FL     0x00000002
+
+struct f2fs_crypto_ctx {
+	struct crypto_tfm *tfm;         /* Crypto API context */
+	struct page *bounce_page;       /* Ciphertext page on write path */
+	struct page *control_page;      /* Original page on write path */
+	struct bio *bio;                /* The bio for this context */
+	struct work_struct work;        /* Work queue for read complete path */
+	struct list_head free_list;     /* Free list */
+	int flags;                      /* Flags */
+	int mode;                       /* Encryption mode for tfm */
+};
+
+struct f2fs_completion_result {
+	struct completion completion;
+	int res;
+};
+
+#define DECLARE_F2FS_COMPLETION_RESULT(ecr) \
+	struct f2fs_completion_result ecr = { \
+		COMPLETION_INITIALIZER((ecr).completion), 0 }
+
+static inline int f2fs_encryption_key_size(int mode)
+{
+	switch (mode) {
+	case F2FS_ENCRYPTION_MODE_AES_256_XTS:
+		return F2FS_AES_256_XTS_KEY_SIZE;
+	case F2FS_ENCRYPTION_MODE_AES_256_GCM:
+		return F2FS_AES_256_GCM_KEY_SIZE;
+	case F2FS_ENCRYPTION_MODE_AES_256_CBC:
+		return F2FS_AES_256_CBC_KEY_SIZE;
+	case F2FS_ENCRYPTION_MODE_AES_256_CTS:
+		return F2FS_AES_256_CTS_KEY_SIZE;
+	default:
+		BUG();
+	}
+	return 0;
+}
+
+#define F2FS_FNAME_NUM_SCATTER_ENTRIES	4
+#define F2FS_CRYPTO_BLOCK_SIZE		16
+#define F2FS_FNAME_CRYPTO_DIGEST_SIZE	32
+
+/**
+ * For encrypted symlinks, the ciphertext length is stored at the beginning
+ * of the string in little-endian format.
+ */
+struct f2fs_encrypted_symlink_data {
+	__le16 len;
+	char encrypted_path[1];
+} __attribute__((__packed__));
+
+/**
+ * This function is used to calculate the disk space required to
+ * store a filename of length l in encrypted symlink format.
+ */
+static inline u32 encrypted_symlink_data_len(u32 l)
+{
+	if (l < F2FS_CRYPTO_BLOCK_SIZE)
+		l = F2FS_CRYPTO_BLOCK_SIZE;
+	return (l + sizeof(struct f2fs_encrypted_symlink_data) - 1);
+}
+#endif	/* _F2FS_CRYPTO_H */
diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
index d44e97f..920408a 100644
--- a/include/linux/f2fs_fs.h
+++ b/include/linux/f2fs_fs.h
@@ -91,7 +91,9 @@ struct f2fs_super_block {
 	__u8 version[VERSION_LEN];	/* the kernel version */
 	__u8 init_version[VERSION_LEN];	/* the initial kernel version */
 	__le32 feature;			/* defined features */
-	__u8 reserved[888];		/* valid reserved region */
+	__u8 encryption_level;		/* versioning level for encryption */
+	__u8 encrypt_pw_salt[16];	/* Salt used for string2key algorithm */
+	__u8 reserved[871];		/* valid reserved region */
 } __packed;
 
 /*
-- 
2.1.1


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

* [PATCH 04/18] f2fs crypto: add f2fs encryption Kconfig
  2015-05-09  4:20 [PATCH 01/18] f2fs: avoid value overflow in showing current status Jaegeuk Kim
  2015-05-09  4:20 ` [PATCH 02/18] f2fs: report unwritten area in f2fs_fiemap Jaegeuk Kim
  2015-05-09  4:20 ` [PATCH 03/18] f2fs crypto: declare some definitions for f2fs encryption feature Jaegeuk Kim
@ 2015-05-09  4:20 ` Jaegeuk Kim
  2015-05-09  4:20 ` [PATCH 05/18] f2fs crypto: add encryption xattr support Jaegeuk Kim
                   ` (13 subsequent siblings)
  16 siblings, 0 replies; 34+ messages in thread
From: Jaegeuk Kim @ 2015-05-09  4:20 UTC (permalink / raw)
  To: linux-kernel, linux-fsdevel, linux-f2fs-devel
  Cc: Jaegeuk Kim, Michael Halcrow, Theodore Ts'o

This patch adds f2fs encryption config.

Signed-off-by: Michael Halcrow <mhalcrow@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---
 fs/f2fs/Kconfig | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/fs/f2fs/Kconfig b/fs/f2fs/Kconfig
index 05f0f66..28f21fe 100644
--- a/fs/f2fs/Kconfig
+++ b/fs/f2fs/Kconfig
@@ -72,6 +72,24 @@ config F2FS_CHECK_FS
 
 	  If you want to improve the performance, say N.
 
+config F2FS_FS_ENCRYPTION
+	bool "F2FS Encryption"
+	depends on F2FS_FS
+	depends on F2FS_FS_XATTR
+	select CRYPTO_AES
+	select CRYPTO_CBC
+	select CRYPTO_ECB
+	select CRYPTO_XTS
+	select CRYPTO_CTS
+	select CRYPTO_SHA256
+	select KEYS
+	select ENCRYPTED_KEYS
+	help
+	  Enable encryption of f2fs files and directories.  This
+	  feature is similar to ecryptfs, but it is more memory
+	  efficient since it avoids caching the encrypted and
+	  decrypted pages in the page cache.
+
 config F2FS_IO_TRACE
 	bool "F2FS IO tracer"
 	depends on F2FS_FS
-- 
2.1.1


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

* [PATCH 05/18] f2fs crypto: add encryption xattr support
  2015-05-09  4:20 [PATCH 01/18] f2fs: avoid value overflow in showing current status Jaegeuk Kim
                   ` (2 preceding siblings ...)
  2015-05-09  4:20 ` [PATCH 04/18] f2fs crypto: add f2fs encryption Kconfig Jaegeuk Kim
@ 2015-05-09  4:20 ` Jaegeuk Kim
  2015-05-09  4:20 ` [PATCH 06/18] f2fs crypto: add encryption policy and password salt support Jaegeuk Kim
                   ` (12 subsequent siblings)
  16 siblings, 0 replies; 34+ messages in thread
From: Jaegeuk Kim @ 2015-05-09  4:20 UTC (permalink / raw)
  To: linux-kernel, linux-fsdevel, linux-f2fs-devel; +Cc: Jaegeuk Kim

This patch add some definition for enrcyption xattr.

Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---
 fs/f2fs/xattr.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/fs/f2fs/xattr.h b/fs/f2fs/xattr.h
index 969d792..71a7100 100644
--- a/fs/f2fs/xattr.h
+++ b/fs/f2fs/xattr.h
@@ -35,6 +35,10 @@
 #define F2FS_XATTR_INDEX_LUSTRE			5
 #define F2FS_XATTR_INDEX_SECURITY		6
 #define F2FS_XATTR_INDEX_ADVISE			7
+/* Should be same as EXT4_XATTR_INDEX_ENCRYPTION */
+#define F2FS_XATTR_INDEX_ENCRYPTION		9
+
+#define F2FS_XATTR_NAME_ENCRYPTION_CONTEXT	"c"
 
 struct f2fs_xattr_header {
 	__le32  h_magic;        /* magic number for identification */
-- 
2.1.1


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

* [PATCH 06/18] f2fs crypto: add encryption policy and password salt support
  2015-05-09  4:20 [PATCH 01/18] f2fs: avoid value overflow in showing current status Jaegeuk Kim
                   ` (3 preceding siblings ...)
  2015-05-09  4:20 ` [PATCH 05/18] f2fs crypto: add encryption xattr support Jaegeuk Kim
@ 2015-05-09  4:20 ` Jaegeuk Kim
  2015-05-09  4:20 ` [PATCH 07/18] f2fs crypto: add f2fs encryption facilities Jaegeuk Kim
                   ` (11 subsequent siblings)
  16 siblings, 0 replies; 34+ messages in thread
From: Jaegeuk Kim @ 2015-05-09  4:20 UTC (permalink / raw)
  To: linux-kernel, linux-fsdevel, linux-f2fs-devel
  Cc: Jaegeuk Kim, Michael Halcrow, Theodore Ts'o, Ildar Muslukhov

This patch adds encryption policy and password salt support through ioctl
implementation.

It adds three ioctls:
 F2FS_IOC_SET_ENCRYPTION_POLICY,
 F2FS_IOC_GET_ENCRYPTION_POLICY,
 F2FS_IOC_GET_ENCRYPTION_PWSALT, which use xattr operations.

Note that, these definition and codes are taken from ext4 crypto support.
For f2fs, xattr operations and on-disk flags for superblock and inode were
changed.

Signed-off-by: Michael Halcrow <mhalcrow@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Ildar Muslukhov <muslukhovi@gmail.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---
 fs/f2fs/Makefile        |   1 +
 fs/f2fs/crypto_policy.c | 206 ++++++++++++++++++++++++++++++++++++++++++++++++
 fs/f2fs/f2fs.h          |  16 ++++
 fs/f2fs/file.c          |  91 +++++++++++++++++++++
 fs/f2fs/xattr.c         |   3 +
 5 files changed, 317 insertions(+)
 create mode 100644 fs/f2fs/crypto_policy.c

diff --git a/fs/f2fs/Makefile b/fs/f2fs/Makefile
index d923977..7864f4f 100644
--- a/fs/f2fs/Makefile
+++ b/fs/f2fs/Makefile
@@ -6,3 +6,4 @@ f2fs-$(CONFIG_F2FS_STAT_FS) += debug.o
 f2fs-$(CONFIG_F2FS_FS_XATTR) += xattr.o
 f2fs-$(CONFIG_F2FS_FS_POSIX_ACL) += acl.o
 f2fs-$(CONFIG_F2FS_IO_TRACE) += trace.o
+f2fs-$(CONFIG_F2FS_FS_ENCRYPTION) += crypto_policy.o
diff --git a/fs/f2fs/crypto_policy.c b/fs/f2fs/crypto_policy.c
new file mode 100644
index 0000000..bef254b
--- /dev/null
+++ b/fs/f2fs/crypto_policy.c
@@ -0,0 +1,206 @@
+/*
+ * copied from linux/fs/ext4/crypto_policy.c
+ *
+ * Copyright (C) 2015, Google, Inc.
+ * Copyright (C) 2015, Motorola Mobility.
+ *
+ * This contains encryption policy functions for f2fs with some modifications
+ * to support f2fs-specific xattr APIs.
+ *
+ * Written by Michael Halcrow, 2015.
+ * Modified by Jaegeuk Kim, 2015.
+ */
+#include <linux/random.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/f2fs_fs.h>
+
+#include "f2fs.h"
+#include "xattr.h"
+
+static int f2fs_inode_has_encryption_context(struct inode *inode)
+{
+	int res = f2fs_getxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION,
+			F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, NULL, 0, NULL);
+	return (res > 0);
+}
+
+/*
+ * check whether the policy is consistent with the encryption context
+ * for the inode
+ */
+static int f2fs_is_encryption_context_consistent_with_policy(
+	struct inode *inode, const struct f2fs_encryption_policy *policy)
+{
+	struct f2fs_encryption_context ctx;
+	int res = f2fs_getxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION,
+				F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx,
+				sizeof(ctx), NULL);
+
+	if (res != sizeof(ctx))
+		return 0;
+
+	return (memcmp(ctx.master_key_descriptor, policy->master_key_descriptor,
+				F2FS_KEY_DESCRIPTOR_SIZE) == 0 &&
+			(ctx.flags == policy->flags) &&
+			(ctx.contents_encryption_mode ==
+			 policy->contents_encryption_mode) &&
+			(ctx.filenames_encryption_mode ==
+			 policy->filenames_encryption_mode));
+}
+
+static int f2fs_create_encryption_context_from_policy(
+	struct inode *inode, const struct f2fs_encryption_policy *policy)
+{
+	struct f2fs_encryption_context ctx;
+
+	ctx.format = F2FS_ENCRYPTION_CONTEXT_FORMAT_V1;
+	memcpy(ctx.master_key_descriptor, policy->master_key_descriptor,
+			F2FS_KEY_DESCRIPTOR_SIZE);
+
+	if (!f2fs_valid_contents_enc_mode(policy->contents_encryption_mode)) {
+		printk(KERN_WARNING
+		       "%s: Invalid contents encryption mode %d\n", __func__,
+			policy->contents_encryption_mode);
+		return -EINVAL;
+	}
+
+	if (!f2fs_valid_filenames_enc_mode(policy->filenames_encryption_mode)) {
+		printk(KERN_WARNING
+		       "%s: Invalid filenames encryption mode %d\n", __func__,
+			policy->filenames_encryption_mode);
+		return -EINVAL;
+	}
+
+	if (policy->flags & ~F2FS_POLICY_FLAGS_VALID)
+		return -EINVAL;
+
+	ctx.contents_encryption_mode = policy->contents_encryption_mode;
+	ctx.filenames_encryption_mode = policy->filenames_encryption_mode;
+	ctx.flags = policy->flags;
+	BUILD_BUG_ON(sizeof(ctx.nonce) != F2FS_KEY_DERIVATION_NONCE_SIZE);
+	get_random_bytes(ctx.nonce, F2FS_KEY_DERIVATION_NONCE_SIZE);
+
+	return f2fs_setxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION,
+			F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx,
+			sizeof(ctx), NULL, 0);
+}
+
+int f2fs_process_policy(const struct f2fs_encryption_policy *policy,
+			struct inode *inode)
+{
+	if (policy->version != 0)
+		return -EINVAL;
+
+	if (!f2fs_inode_has_encryption_context(inode)) {
+		if (!f2fs_empty_dir(inode))
+			return -ENOTEMPTY;
+		return f2fs_create_encryption_context_from_policy(inode,
+								  policy);
+	}
+
+	if (f2fs_is_encryption_context_consistent_with_policy(inode, policy))
+		return 0;
+
+	printk(KERN_WARNING "%s: Policy inconsistent with encryption context\n",
+	       __func__);
+	return -EINVAL;
+}
+
+int f2fs_get_policy(struct inode *inode, struct f2fs_encryption_policy *policy)
+{
+	struct f2fs_encryption_context ctx;
+	int res;
+
+	if (!f2fs_encrypted_inode(inode))
+		return -ENODATA;
+
+	res = f2fs_getxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION,
+				F2FS_XATTR_NAME_ENCRYPTION_CONTEXT,
+				&ctx, sizeof(ctx), NULL);
+	if (res != sizeof(ctx))
+		return -ENODATA;
+	if (ctx.format != F2FS_ENCRYPTION_CONTEXT_FORMAT_V1)
+		return -EINVAL;
+
+	policy->version = 0;
+	policy->contents_encryption_mode = ctx.contents_encryption_mode;
+	policy->filenames_encryption_mode = ctx.filenames_encryption_mode;
+	policy->flags = ctx.flags;
+	memcpy(&policy->master_key_descriptor, ctx.master_key_descriptor,
+			F2FS_KEY_DESCRIPTOR_SIZE);
+	return 0;
+}
+
+int f2fs_is_child_context_consistent_with_parent(struct inode *parent,
+						struct inode *child)
+{
+	struct f2fs_crypt_info *parent_ci, *child_ci;
+	int res;
+
+	if ((parent == NULL) || (child == NULL)) {
+		pr_err("parent %p child %p\n", parent, child);
+		BUG_ON(1);
+	}
+
+	/* no restrictions if the parent directory is not encrypted */
+	if (!f2fs_encrypted_inode(parent))
+		return 1;
+	/* if the child directory is not encrypted, this is always a problem */
+	if (!f2fs_encrypted_inode(child))
+		return 0;
+	res = f2fs_get_encryption_info(parent);
+	if (res)
+		return 0;
+	res = f2fs_get_encryption_info(child);
+	if (res)
+		return 0;
+	parent_ci = F2FS_I(parent)->i_crypt_info;
+	child_ci = F2FS_I(child)->i_crypt_info;
+	if (!parent_ci && !child_ci)
+		return 1;
+	if (!parent_ci || !child_ci)
+		return 0;
+
+	return (memcmp(parent_ci->ci_master_key,
+			child_ci->ci_master_key,
+			F2FS_KEY_DESCRIPTOR_SIZE) == 0 &&
+		(parent_ci->ci_data_mode == child_ci->ci_data_mode) &&
+		(parent_ci->ci_filename_mode == child_ci->ci_filename_mode) &&
+		(parent_ci->ci_flags == child_ci->ci_flags));
+}
+
+/**
+ * f2fs_inherit_context() - Sets a child context from its parent
+ * @parent: Parent inode from which the context is inherited.
+ * @child:  Child inode that inherits the context from @parent.
+ *
+ * Return: Zero on success, non-zero otherwise
+ */
+int f2fs_inherit_context(struct inode *parent, struct inode *child,
+						struct page *ipage)
+{
+	struct f2fs_encryption_context ctx;
+	struct f2fs_crypt_info *ci;
+	int res;
+
+	res = f2fs_get_encryption_info(parent);
+	if (res < 0)
+		return res;
+
+	ci = F2FS_I(parent)->i_crypt_info;
+	BUG_ON(ci == NULL);
+
+	ctx.format = F2FS_ENCRYPTION_CONTEXT_FORMAT_V1;
+
+	ctx.contents_encryption_mode = ci->ci_data_mode;
+	ctx.filenames_encryption_mode = ci->ci_filename_mode;
+	ctx.flags = ci->ci_flags;
+	memcpy(ctx.master_key_descriptor, ci->ci_master_key,
+			F2FS_KEY_DESCRIPTOR_SIZE);
+
+	get_random_bytes(ctx.nonce, F2FS_KEY_DERIVATION_NONCE_SIZE);
+	return f2fs_setxattr(child, F2FS_XATTR_INDEX_ENCRYPTION,
+				F2FS_XATTR_NAME_ENCRYPTION_CONTEXT, &ctx,
+				sizeof(ctx), ipage, 0);
+}
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index c3c4deb..d8ce158 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -229,6 +229,13 @@ static inline bool __has_cursum_space(struct f2fs_summary_block *sum, int size,
 #define F2FS_IOC_RELEASE_VOLATILE_WRITE	_IO(F2FS_IOCTL_MAGIC, 4)
 #define F2FS_IOC_ABORT_VOLATILE_WRITE	_IO(F2FS_IOCTL_MAGIC, 5)
 
+#define F2FS_IOC_SET_ENCRYPTION_POLICY					\
+		_IOR('f', 19, struct f2fs_encryption_policy)
+#define F2FS_IOC_GET_ENCRYPTION_PWSALT					\
+		_IOW('f', 20, __u8[16])
+#define F2FS_IOC_GET_ENCRYPTION_POLICY					\
+		_IOW('f', 21, struct f2fs_encryption_policy)
+
 /*
  * should be same as XFS_IOC_GOINGDOWN.
  * Flags for going down operation used by FS_IOC_GOINGDOWN
@@ -367,6 +374,8 @@ struct f2fs_map_blocks {
 #define F2FS_ENCRYPTION_MODE_AES_256_CBC	3
 #define F2FS_ENCRYPTION_MODE_AES_256_CTS	4
 
+#include "f2fs_crypto.h"
+
 #define DEF_DIR_LEVEL		0
 
 struct f2fs_inode_info {
@@ -1945,4 +1954,11 @@ static inline int f2fs_sb_has_crypto(struct super_block *sb)
 #else
 	return 0;
 #endif
+
+/* crypto_policy.c */
+int f2fs_is_child_context_consistent_with_parent(struct inode *,
+							struct inode *);
+int f2fs_inherit_context(struct inode *, struct inode *, struct page *);
+int f2fs_process_policy(const struct f2fs_encryption_policy *, struct inode *);
+int f2fs_get_policy(struct inode *, struct f2fs_encryption_policy *);
 #endif
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 7293746..7236be4 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -20,6 +20,7 @@
 #include <linux/uaccess.h>
 #include <linux/mount.h>
 #include <linux/pagevec.h>
+#include <linux/random.h>
 
 #include "f2fs.h"
 #include "node.h"
@@ -1347,6 +1348,90 @@ static int f2fs_ioc_fitrim(struct file *filp, unsigned long arg)
 	return 0;
 }
 
+static bool uuid_is_nonzero(__u8 u[16])
+{
+	int i;
+
+	for (i = 0; i < 16; i++)
+		if (u[i])
+			return true;
+	return false;
+}
+
+static int f2fs_ioc_set_encryption_policy(struct file *filp, unsigned long arg)
+{
+#ifdef CONFIG_F2FS_FS_ENCRYPTION
+	struct f2fs_encryption_policy policy;
+	struct inode *inode = file_inode(filp);
+
+	if (copy_from_user(&policy, (struct f2fs_encryption_policy __user *)arg,
+				sizeof(policy)))
+		return -EFAULT;
+
+	if (f2fs_has_inline_data(inode)) {
+		int ret = f2fs_convert_inline_inode(inode);
+		if (ret)
+			return ret;
+	}
+
+	return f2fs_process_policy(&policy, inode);
+#else
+	return -EOPNOTSUPP;
+#endif
+}
+
+static int f2fs_ioc_get_encryption_policy(struct file *filp, unsigned long arg)
+{
+#ifdef CONFIG_F2FS_FS_ENCRYPTION
+	struct f2fs_encryption_policy policy;
+	struct inode *inode = file_inode(filp);
+	int err;
+
+	err = f2fs_get_policy(inode, &policy);
+	if (err)
+		return err;
+
+	if (copy_to_user((void *)arg, &policy, sizeof(policy)))
+		return -EFAULT;
+	return 0;
+#else
+	return -EOPNOTSUPP;
+#endif
+}
+
+static int f2fs_ioc_get_encryption_pwsalt(struct file *filp, unsigned long arg)
+{
+	struct inode *inode = file_inode(filp);
+	struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
+	int err;
+
+	if (!f2fs_sb_has_crypto(inode->i_sb))
+		return -EOPNOTSUPP;
+
+	if (uuid_is_nonzero(sbi->raw_super->encrypt_pw_salt))
+		goto got_it;
+
+	err = mnt_want_write_file(filp);
+	if (err)
+		return err;
+
+	/* update superblock with uuid */
+	generate_random_uuid(sbi->raw_super->encrypt_pw_salt);
+
+	err = f2fs_commit_super(sbi);
+
+	mnt_drop_write_file(filp);
+	if (err) {
+		/* undo new data */
+		memset(sbi->raw_super->encrypt_pw_salt, 0, 16);
+		return err;
+	}
+got_it:
+	if (copy_to_user((void *)arg, sbi->raw_super->encrypt_pw_salt, 16))
+		return -EFAULT;
+	return 0;
+}
+
 long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
 	switch (cmd) {
@@ -1370,6 +1455,12 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 		return f2fs_ioc_shutdown(filp, arg);
 	case FITRIM:
 		return f2fs_ioc_fitrim(filp, arg);
+	case F2FS_IOC_SET_ENCRYPTION_POLICY:
+		return f2fs_ioc_set_encryption_policy(filp, arg);
+	case F2FS_IOC_GET_ENCRYPTION_POLICY:
+		return f2fs_ioc_get_encryption_policy(filp, arg);
+	case F2FS_IOC_GET_ENCRYPTION_PWSALT:
+		return f2fs_ioc_get_encryption_pwsalt(filp, arg);
 	default:
 		return -ENOTTY;
 	}
diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c
index 9757f65..07449b98 100644
--- a/fs/f2fs/xattr.c
+++ b/fs/f2fs/xattr.c
@@ -584,6 +584,9 @@ static int __f2fs_setxattr(struct inode *inode, int index,
 		inode->i_ctime = CURRENT_TIME;
 		clear_inode_flag(fi, FI_ACL_MODE);
 	}
+	if (index == F2FS_XATTR_INDEX_ENCRYPTION &&
+			!strcmp(name, F2FS_XATTR_NAME_ENCRYPTION_CONTEXT))
+		f2fs_set_encrypted_inode(inode);
 
 	if (ipage)
 		update_inode(inode, ipage);
-- 
2.1.1


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

* [PATCH 07/18] f2fs crypto: add f2fs encryption facilities
  2015-05-09  4:20 [PATCH 01/18] f2fs: avoid value overflow in showing current status Jaegeuk Kim
                   ` (4 preceding siblings ...)
  2015-05-09  4:20 ` [PATCH 06/18] f2fs crypto: add encryption policy and password salt support Jaegeuk Kim
@ 2015-05-09  4:20 ` Jaegeuk Kim
  2015-05-09  4:20 ` [PATCH 08/18] f2fs crypto: add encryption key management facilities Jaegeuk Kim
                   ` (10 subsequent siblings)
  16 siblings, 0 replies; 34+ messages in thread
From: Jaegeuk Kim @ 2015-05-09  4:20 UTC (permalink / raw)
  To: linux-kernel, linux-fsdevel, linux-f2fs-devel
  Cc: Jaegeuk Kim, Michael Halcrow, Ildar Muslukhov, Theodore Ts'o

Most of parts were copied from ext4, except:

 - add f2fs_restore_and_release_control_page which returns control page and
   restore control page
 - remove ext4_encrypted_zeroout()
 - remove sbi->s_file_encryption_mode & sbi->s_dir_encryption_mode
 - add f2fs_end_io_crypto_work for mpage_end_io
 - set num_prealloc_crypto_pages to 128, max size for one bio
 - call f2fs_exit_crypto() in put_super
 - call f2fs_init_crypto() in fill_super to avoid runtime GFP_KERNEL allocation
   in writepage path

Signed-off-by: Michael Halcrow <mhalcrow@google.com>
Signed-off-by: Ildar Muslukhov <ildarm@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---
 fs/f2fs/Makefile |   2 +-
 fs/f2fs/crypto.c | 561 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/f2fs/f2fs.h   |  25 +++
 fs/f2fs/super.c  |  19 +-
 4 files changed, 603 insertions(+), 4 deletions(-)
 create mode 100644 fs/f2fs/crypto.c

diff --git a/fs/f2fs/Makefile b/fs/f2fs/Makefile
index 7864f4f..a79907b 100644
--- a/fs/f2fs/Makefile
+++ b/fs/f2fs/Makefile
@@ -6,4 +6,4 @@ f2fs-$(CONFIG_F2FS_STAT_FS) += debug.o
 f2fs-$(CONFIG_F2FS_FS_XATTR) += xattr.o
 f2fs-$(CONFIG_F2FS_FS_POSIX_ACL) += acl.o
 f2fs-$(CONFIG_F2FS_IO_TRACE) += trace.o
-f2fs-$(CONFIG_F2FS_FS_ENCRYPTION) += crypto_policy.o
+f2fs-$(CONFIG_F2FS_FS_ENCRYPTION) += crypto_policy.o crypto.o
diff --git a/fs/f2fs/crypto.c b/fs/f2fs/crypto.c
new file mode 100644
index 0000000..38c005c
--- /dev/null
+++ b/fs/f2fs/crypto.c
@@ -0,0 +1,561 @@
+/*
+ * linux/fs/f2fs/crypto.c
+ *
+ * Copied from linux/fs/ext4/crypto.c
+ *
+ * Copyright (C) 2015, Google, Inc.
+ * Copyright (C) 2015, Motorola Mobility
+ *
+ * This contains encryption functions for f2fs
+ *
+ * Written by Michael Halcrow, 2014.
+ *
+ * Filename encryption additions
+ *	Uday Savagaonkar, 2014
+ * Encryption policy handling additions
+ *	Ildar Muslukhov, 2014
+ * Remove ext4_encrypted_zeroout(),
+ *   add f2fs_restore_and_release_control_page()
+ *	Jaegeuk Kim, 2015.
+ *
+ * This has not yet undergone a rigorous security audit.
+ *
+ * The usage of AES-XTS should conform to recommendations in NIST
+ * Special Publication 800-38E and IEEE P1619/D16.
+ */
+#include <crypto/hash.h>
+#include <crypto/sha.h>
+#include <keys/user-type.h>
+#include <keys/encrypted-type.h>
+#include <linux/crypto.h>
+#include <linux/ecryptfs.h>
+#include <linux/gfp.h>
+#include <linux/kernel.h>
+#include <linux/key.h>
+#include <linux/list.h>
+#include <linux/mempool.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/random.h>
+#include <linux/scatterlist.h>
+#include <linux/spinlock_types.h>
+#include <linux/f2fs_fs.h>
+#include <linux/ratelimit.h>
+#include <linux/bio.h>
+
+#include "f2fs.h"
+#include "xattr.h"
+
+/* Encryption added and removed here! (L: */
+
+static unsigned int num_prealloc_crypto_pages = 128;
+static unsigned int num_prealloc_crypto_ctxs = 128;
+
+module_param(num_prealloc_crypto_pages, uint, 0444);
+MODULE_PARM_DESC(num_prealloc_crypto_pages,
+		"Number of crypto pages to preallocate");
+module_param(num_prealloc_crypto_ctxs, uint, 0444);
+MODULE_PARM_DESC(num_prealloc_crypto_ctxs,
+		"Number of crypto contexts to preallocate");
+
+static mempool_t *f2fs_bounce_page_pool;
+
+static LIST_HEAD(f2fs_free_crypto_ctxs);
+static DEFINE_SPINLOCK(f2fs_crypto_ctx_lock);
+
+static struct workqueue_struct *f2fs_read_workqueue;
+static DEFINE_MUTEX(crypto_init);
+
+/**
+ * f2fs_release_crypto_ctx() - Releases an encryption context
+ * @ctx: The encryption context to release.
+ *
+ * If the encryption context was allocated from the pre-allocated pool, returns
+ * it to that pool. Else, frees it.
+ *
+ * If there's a bounce page in the context, this frees that.
+ */
+void f2fs_release_crypto_ctx(struct f2fs_crypto_ctx *ctx)
+{
+	unsigned long flags;
+
+	if (ctx->bounce_page) {
+		if (ctx->flags & F2FS_BOUNCE_PAGE_REQUIRES_FREE_ENCRYPT_FL)
+			__free_page(ctx->bounce_page);
+		else
+			mempool_free(ctx->bounce_page, f2fs_bounce_page_pool);
+		ctx->bounce_page = NULL;
+	}
+	ctx->control_page = NULL;
+	if (ctx->flags & F2FS_CTX_REQUIRES_FREE_ENCRYPT_FL) {
+		if (ctx->tfm)
+			crypto_free_tfm(ctx->tfm);
+		kfree(ctx);
+	} else {
+		spin_lock_irqsave(&f2fs_crypto_ctx_lock, flags);
+		list_add(&ctx->free_list, &f2fs_free_crypto_ctxs);
+		spin_unlock_irqrestore(&f2fs_crypto_ctx_lock, flags);
+	}
+}
+
+/**
+ * f2fs_alloc_and_init_crypto_ctx() - Allocates and inits an encryption context
+ * @mask: The allocation mask.
+ *
+ * Return: An allocated and initialized encryption context on success. An error
+ * value or NULL otherwise.
+ */
+static struct f2fs_crypto_ctx *f2fs_alloc_and_init_crypto_ctx(gfp_t mask)
+{
+	struct f2fs_crypto_ctx *ctx = kzalloc(sizeof(struct f2fs_crypto_ctx),
+						mask);
+
+	if (!ctx)
+		return ERR_PTR(-ENOMEM);
+	return ctx;
+}
+
+/**
+ * f2fs_get_crypto_ctx() - Gets an encryption context
+ * @inode:       The inode for which we are doing the crypto
+ *
+ * Allocates and initializes an encryption context.
+ *
+ * Return: An allocated and initialized encryption context on success; error
+ * value or NULL otherwise.
+ */
+struct f2fs_crypto_ctx *f2fs_get_crypto_ctx(struct inode *inode)
+{
+	struct f2fs_crypto_ctx *ctx = NULL;
+	int res = 0;
+	unsigned long flags;
+	struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info;
+
+	BUG_ON(ci == NULL);
+	/*
+	 * We first try getting the ctx from a free list because in
+	 * the common case the ctx will have an allocated and
+	 * initialized crypto tfm, so it's probably a worthwhile
+	 * optimization. For the bounce page, we first try getting it
+	 * from the kernel allocator because that's just about as fast
+	 * as getting it from a list and because a cache of free pages
+	 * should generally be a "last resort" option for a filesystem
+	 * to be able to do its job.
+	 */
+	spin_lock_irqsave(&f2fs_crypto_ctx_lock, flags);
+	ctx = list_first_entry_or_null(&f2fs_free_crypto_ctxs,
+					struct f2fs_crypto_ctx, free_list);
+	if (ctx)
+		list_del(&ctx->free_list);
+	spin_unlock_irqrestore(&f2fs_crypto_ctx_lock, flags);
+	if (!ctx) {
+		ctx = f2fs_alloc_and_init_crypto_ctx(GFP_NOFS);
+		if (IS_ERR(ctx)) {
+			res = PTR_ERR(ctx);
+			goto out;
+		}
+		ctx->flags |= F2FS_CTX_REQUIRES_FREE_ENCRYPT_FL;
+	} else {
+		ctx->flags &= ~F2FS_CTX_REQUIRES_FREE_ENCRYPT_FL;
+	}
+
+	/*
+	 * Allocate a new Crypto API context if we don't already have
+	 * one or if it isn't the right mode.
+	 */
+	BUG_ON(ci->ci_mode == F2FS_ENCRYPTION_MODE_INVALID);
+	if (ctx->tfm && (ctx->mode != ci->ci_mode)) {
+		crypto_free_tfm(ctx->tfm);
+		ctx->tfm = NULL;
+		ctx->mode = F2FS_ENCRYPTION_MODE_INVALID;
+	}
+	if (!ctx->tfm) {
+		switch (ci->ci_mode) {
+		case F2FS_ENCRYPTION_MODE_AES_256_XTS:
+			ctx->tfm = crypto_ablkcipher_tfm(
+				crypto_alloc_ablkcipher("xts(aes)", 0, 0));
+			break;
+		case F2FS_ENCRYPTION_MODE_AES_256_GCM:
+			/*
+			 * TODO(mhalcrow): AEAD w/ gcm(aes);
+			 * crypto_aead_setauthsize()
+			 */
+			ctx->tfm = ERR_PTR(-ENOTSUPP);
+			break;
+		default:
+			BUG();
+		}
+		if (IS_ERR_OR_NULL(ctx->tfm)) {
+			res = PTR_ERR(ctx->tfm);
+			ctx->tfm = NULL;
+			goto out;
+		}
+		ctx->mode = ci->ci_mode;
+	}
+	BUG_ON(ci->ci_size != f2fs_encryption_key_size(ci->ci_mode));
+
+	/*
+	 * There shouldn't be a bounce page attached to the crypto
+	 * context at this point.
+	 */
+	BUG_ON(ctx->bounce_page);
+
+out:
+	if (res) {
+		if (!IS_ERR_OR_NULL(ctx))
+			f2fs_release_crypto_ctx(ctx);
+		ctx = ERR_PTR(res);
+	}
+	return ctx;
+}
+
+/*
+ * Call f2fs_decrypt on every single page, reusing the encryption
+ * context.
+ */
+static void completion_pages(struct work_struct *work)
+{
+	struct f2fs_crypto_ctx *ctx =
+		container_of(work, struct f2fs_crypto_ctx, work);
+	struct bio *bio	= ctx->bio;
+	struct bio_vec *bv;
+	int i;
+
+	bio_for_each_segment_all(bv, bio, i) {
+		struct page *page = bv->bv_page;
+
+		int ret = f2fs_decrypt(ctx, page);
+		if (ret) {
+			WARN_ON_ONCE(1);
+			SetPageError(page);
+		} else
+			SetPageUptodate(page);
+		unlock_page(page);
+	}
+	f2fs_release_crypto_ctx(ctx);
+	bio_put(bio);
+}
+
+void f2fs_end_io_crypto_work(struct f2fs_crypto_ctx *ctx, struct bio *bio)
+{
+	INIT_WORK(&ctx->work, completion_pages);
+	ctx->bio = bio;
+	queue_work(f2fs_read_workqueue, &ctx->work);
+}
+
+/**
+ * f2fs_exit_crypto() - Shutdown the f2fs encryption system
+ */
+void f2fs_exit_crypto(void)
+{
+	struct f2fs_crypto_ctx *pos, *n;
+
+	list_for_each_entry_safe(pos, n, &f2fs_free_crypto_ctxs, free_list) {
+		if (pos->bounce_page) {
+			if (pos->flags &
+				F2FS_BOUNCE_PAGE_REQUIRES_FREE_ENCRYPT_FL)
+				__free_page(pos->bounce_page);
+			else
+				mempool_free(pos->bounce_page,
+						f2fs_bounce_page_pool);
+		}
+		if (pos->tfm)
+			crypto_free_tfm(pos->tfm);
+		kfree(pos);
+	}
+	INIT_LIST_HEAD(&f2fs_free_crypto_ctxs);
+	if (f2fs_bounce_page_pool)
+		mempool_destroy(f2fs_bounce_page_pool);
+	f2fs_bounce_page_pool = NULL;
+	if (f2fs_read_workqueue)
+		destroy_workqueue(f2fs_read_workqueue);
+	f2fs_read_workqueue = NULL;
+}
+
+/**
+ * f2fs_init_crypto() - Set up for f2fs encryption.
+ *
+ * We only call this when we start accessing encrypted files, since it
+ * results in memory getting allocated that wouldn't otherwise be used.
+ *
+ * Return: Zero on success, non-zero otherwise.
+ */
+int f2fs_init_crypto(void)
+{
+	int i, res;
+
+	mutex_lock(&crypto_init);
+	if (f2fs_read_workqueue)
+		goto already_initialized;
+
+	f2fs_read_workqueue = alloc_workqueue("f2fs_crypto", WQ_HIGHPRI, 0);
+	if (!f2fs_read_workqueue) {
+		res = -ENOMEM;
+		goto fail;
+	}
+
+	for (i = 0; i < num_prealloc_crypto_ctxs; i++) {
+		struct f2fs_crypto_ctx *ctx;
+
+		ctx = f2fs_alloc_and_init_crypto_ctx(GFP_KERNEL);
+		if (IS_ERR(ctx)) {
+			res = PTR_ERR(ctx);
+			goto fail;
+		}
+		list_add(&ctx->free_list, &f2fs_free_crypto_ctxs);
+	}
+
+	f2fs_bounce_page_pool =
+		mempool_create_page_pool(num_prealloc_crypto_pages, 0);
+	if (!f2fs_bounce_page_pool) {
+		res = -ENOMEM;
+		goto fail;
+	}
+already_initialized:
+	mutex_unlock(&crypto_init);
+	return 0;
+fail:
+	f2fs_exit_crypto();
+	mutex_unlock(&crypto_init);
+	return res;
+}
+
+void f2fs_restore_and_release_control_page(struct page **page)
+{
+	struct f2fs_crypto_ctx *ctx;
+	struct page *bounce_page;
+
+	/* The bounce data pages are unmapped. */
+	if ((*page)->mapping)
+		return;
+
+	/* The bounce data page is unmapped. */
+	bounce_page = *page;
+	ctx = (struct f2fs_crypto_ctx *)page_private(bounce_page);
+
+	/* restore control page */
+	*page = ctx->control_page;
+
+	f2fs_restore_control_page(bounce_page);
+}
+
+void f2fs_restore_control_page(struct page *data_page)
+{
+	struct f2fs_crypto_ctx *ctx =
+		(struct f2fs_crypto_ctx *)page_private(data_page);
+
+	set_page_private(data_page, (unsigned long)NULL);
+	ClearPagePrivate(data_page);
+	unlock_page(data_page);
+	f2fs_release_crypto_ctx(ctx);
+}
+
+/**
+ * f2fs_crypt_complete() - The completion callback for page encryption
+ * @req: The asynchronous encryption request context
+ * @res: The result of the encryption operation
+ */
+static void f2fs_crypt_complete(struct crypto_async_request *req, int res)
+{
+	struct f2fs_completion_result *ecr = req->data;
+
+	if (res == -EINPROGRESS)
+		return;
+	ecr->res = res;
+	complete(&ecr->completion);
+}
+
+typedef enum {
+	F2FS_DECRYPT = 0,
+	F2FS_ENCRYPT,
+} f2fs_direction_t;
+
+static int f2fs_page_crypto(struct f2fs_crypto_ctx *ctx,
+				struct inode *inode,
+				f2fs_direction_t rw,
+				pgoff_t index,
+				struct page *src_page,
+				struct page *dest_page)
+
+{
+	u8 xts_tweak[F2FS_XTS_TWEAK_SIZE];
+	struct ablkcipher_request *req = NULL;
+	DECLARE_F2FS_COMPLETION_RESULT(ecr);
+	struct scatterlist dst, src;
+	struct f2fs_inode_info *fi = F2FS_I(inode);
+	struct crypto_ablkcipher *atfm = __crypto_ablkcipher_cast(ctx->tfm);
+	int res = 0;
+
+	BUG_ON(!ctx->tfm);
+	BUG_ON(ctx->mode != fi->i_crypt_info->ci_mode);
+
+	if (ctx->mode != F2FS_ENCRYPTION_MODE_AES_256_XTS) {
+		printk_ratelimited(KERN_ERR
+				"%s: unsupported crypto algorithm: %d\n",
+				__func__, ctx->mode);
+		return -ENOTSUPP;
+	}
+
+	crypto_ablkcipher_clear_flags(atfm, ~0);
+	crypto_tfm_set_flags(ctx->tfm, CRYPTO_TFM_REQ_WEAK_KEY);
+
+	res = crypto_ablkcipher_setkey(atfm, fi->i_crypt_info->ci_raw,
+					fi->i_crypt_info->ci_size);
+	if (res) {
+		printk_ratelimited(KERN_ERR
+				"%s: crypto_ablkcipher_setkey() failed\n",
+				__func__);
+		return res;
+	}
+	req = ablkcipher_request_alloc(atfm, GFP_NOFS);
+	if (!req) {
+		printk_ratelimited(KERN_ERR
+				"%s: crypto_request_alloc() failed\n",
+				__func__);
+		return -ENOMEM;
+	}
+	ablkcipher_request_set_callback(
+		req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
+		f2fs_crypt_complete, &ecr);
+
+	BUILD_BUG_ON(F2FS_XTS_TWEAK_SIZE < sizeof(index));
+	memcpy(xts_tweak, &index, sizeof(index));
+	memset(&xts_tweak[sizeof(index)], 0,
+			F2FS_XTS_TWEAK_SIZE - sizeof(index));
+
+	sg_init_table(&dst, 1);
+	sg_set_page(&dst, dest_page, PAGE_CACHE_SIZE, 0);
+	sg_init_table(&src, 1);
+	sg_set_page(&src, src_page, PAGE_CACHE_SIZE, 0);
+	ablkcipher_request_set_crypt(req, &src, &dst, PAGE_CACHE_SIZE,
+					xts_tweak);
+	if (rw == F2FS_DECRYPT)
+		res = crypto_ablkcipher_decrypt(req);
+	else
+		res = crypto_ablkcipher_encrypt(req);
+	if (res == -EINPROGRESS || res == -EBUSY) {
+		BUG_ON(req->base.data != &ecr);
+		wait_for_completion(&ecr.completion);
+		res = ecr.res;
+	}
+	ablkcipher_request_free(req);
+	if (res) {
+		printk_ratelimited(KERN_ERR
+			"%s: crypto_ablkcipher_encrypt() returned %d\n",
+			__func__, res);
+		return res;
+	}
+	return 0;
+}
+
+/**
+ * f2fs_encrypt() - Encrypts a page
+ * @inode:          The inode for which the encryption should take place
+ * @plaintext_page: The page to encrypt. Must be locked.
+ *
+ * Allocates a ciphertext page and encrypts plaintext_page into it using the ctx
+ * encryption context.
+ *
+ * Called on the page write path.  The caller must call
+ * f2fs_restore_control_page() on the returned ciphertext page to
+ * release the bounce buffer and the encryption context.
+ *
+ * Return: An allocated page with the encrypted content on success. Else, an
+ * error value or NULL.
+ */
+struct page *f2fs_encrypt(struct inode *inode,
+			  struct page *plaintext_page)
+{
+	struct f2fs_crypto_ctx *ctx;
+	struct page *ciphertext_page = NULL;
+	int err;
+
+	BUG_ON(!PageLocked(plaintext_page));
+
+	ctx = f2fs_get_crypto_ctx(inode);
+	if (IS_ERR(ctx))
+		return (struct page *)ctx;
+
+	/* The encryption operation will require a bounce page. */
+	ciphertext_page = alloc_page(GFP_NOFS);
+	if (!ciphertext_page) {
+		/*
+		 * This is a potential bottleneck, but at least we'll have
+		 * forward progress.
+		 */
+		ciphertext_page = mempool_alloc(f2fs_bounce_page_pool,
+							GFP_NOFS);
+		if (WARN_ON_ONCE(!ciphertext_page))
+			ciphertext_page = mempool_alloc(f2fs_bounce_page_pool,
+						GFP_NOFS | __GFP_WAIT);
+		ctx->flags &= ~F2FS_BOUNCE_PAGE_REQUIRES_FREE_ENCRYPT_FL;
+	} else {
+		ctx->flags |= F2FS_BOUNCE_PAGE_REQUIRES_FREE_ENCRYPT_FL;
+	}
+	ctx->bounce_page = ciphertext_page;
+	ctx->control_page = plaintext_page;
+	err = f2fs_page_crypto(ctx, inode, F2FS_ENCRYPT, plaintext_page->index,
+					plaintext_page, ciphertext_page);
+	if (err) {
+		f2fs_release_crypto_ctx(ctx);
+		return ERR_PTR(err);
+	}
+	SetPagePrivate(ciphertext_page);
+	set_page_private(ciphertext_page, (unsigned long)ctx);
+	lock_page(ciphertext_page);
+	return ciphertext_page;
+}
+
+/**
+ * f2fs_decrypt() - Decrypts a page in-place
+ * @ctx:  The encryption context.
+ * @page: The page to decrypt. Must be locked.
+ *
+ * Decrypts page in-place using the ctx encryption context.
+ *
+ * Called from the read completion callback.
+ *
+ * Return: Zero on success, non-zero otherwise.
+ */
+int f2fs_decrypt(struct f2fs_crypto_ctx *ctx, struct page *page)
+{
+	BUG_ON(!PageLocked(page));
+
+	return f2fs_page_crypto(ctx, page->mapping->host,
+				F2FS_DECRYPT, page->index, page, page);
+}
+
+/*
+ * Convenience function which takes care of allocating and
+ * deallocating the encryption context
+ */
+int f2fs_decrypt_one(struct inode *inode, struct page *page)
+{
+	struct f2fs_crypto_ctx *ctx = f2fs_get_crypto_ctx(inode);
+	int ret;
+
+	if (!ctx)
+		return -ENOMEM;
+	ret = f2fs_decrypt(ctx, page);
+	f2fs_release_crypto_ctx(ctx);
+	return ret;
+}
+
+bool f2fs_valid_contents_enc_mode(uint32_t mode)
+{
+	return (mode == F2FS_ENCRYPTION_MODE_AES_256_XTS);
+}
+
+/**
+ * f2fs_validate_encryption_key_size() - Validate the encryption key size
+ * @mode: The key mode.
+ * @size: The key size to validate.
+ *
+ * Return: The validated key size for @mode. Zero if invalid.
+ */
+uint32_t f2fs_validate_encryption_key_size(uint32_t mode, uint32_t size)
+{
+	if (size == f2fs_encryption_key_size(mode))
+		return size;
+	return 0;
+}
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index d8ce158..d586346 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -1954,6 +1954,7 @@ static inline int f2fs_sb_has_crypto(struct super_block *sb)
 #else
 	return 0;
 #endif
+}
 
 /* crypto_policy.c */
 int f2fs_is_child_context_consistent_with_parent(struct inode *,
@@ -1961,4 +1962,28 @@ int f2fs_is_child_context_consistent_with_parent(struct inode *,
 int f2fs_inherit_context(struct inode *, struct inode *, struct page *);
 int f2fs_process_policy(const struct f2fs_encryption_policy *, struct inode *);
 int f2fs_get_policy(struct inode *, struct f2fs_encryption_policy *);
+
+/* crypt.c */
+bool f2fs_valid_contents_enc_mode(uint32_t);
+uint32_t f2fs_validate_encryption_key_size(uint32_t, uint32_t);
+struct f2fs_crypto_ctx *f2fs_get_crypto_ctx(struct inode *);
+void f2fs_release_crypto_ctx(struct f2fs_crypto_ctx *);
+struct page *f2fs_encrypt(struct inode *, struct page *);
+int f2fs_decrypt(struct f2fs_crypto_ctx *, struct page *);
+int f2fs_decrypt_one(struct inode *, struct page *);
+void f2fs_end_io_crypto_work(struct f2fs_crypto_ctx *, struct bio *);
+
+#ifdef CONFIG_F2FS_FS_ENCRYPTION
+void f2fs_restore_and_release_control_page(struct page **);
+void f2fs_restore_control_page(struct page *);
+
+int f2fs_init_crypto(void);
+void f2fs_exit_crypto(void);
+#else
+static inline void f2fs_restore_and_release_control_page(struct page **p) { }
+static inline void f2fs_restore_control_page(struct page *p) { }
+
+static inline int f2fs_init_crypto(void) { return 0; }
+static inline void f2fs_exit_crypto(void) { }
+#endif
 #endif
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 138fa93..21102a2 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -416,6 +416,9 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb)
 	/* Will be used by directory only */
 	fi->i_dir_level = F2FS_SB(sb)->dir_level;
 
+#ifdef CONFIG_F2FS_FS_ENCRYPTION
+	fi->i_crypt_info = NULL;
+#endif
 	return &fi->vfs_inode;
 }
 
@@ -494,6 +497,9 @@ static void f2fs_put_super(struct super_block *sb)
 	destroy_node_manager(sbi);
 	destroy_segment_manager(sbi);
 
+	/* destroy crypto buffers */
+	f2fs_exit_crypto();
+
 	kfree(sbi->ckpt);
 	kobject_put(&sbi->s_kobj);
 	wait_for_completion(&sbi->s_kobj_unregister);
@@ -1187,6 +1193,11 @@ try_onemore:
 	if (err)
 		goto free_proc;
 
+	/* init crypto buffers */
+	err = f2fs_init_crypto();
+	if (err)
+		goto free_kobj;
+
 	/* recover fsynced data */
 	if (!test_opt(sbi, DISABLE_ROLL_FORWARD)) {
 		/*
@@ -1196,7 +1207,7 @@ try_onemore:
 		if (bdev_read_only(sb->s_bdev) &&
 				!is_set_ckpt_flags(sbi->ckpt, CP_UMOUNT_FLAG)) {
 			err = -EROFS;
-			goto free_kobj;
+			goto free_crypto;
 		}
 
 		if (need_fsck)
@@ -1207,7 +1218,7 @@ try_onemore:
 			need_fsck = true;
 			f2fs_msg(sb, KERN_ERR,
 				"Cannot recover all fsync data errno=%ld", err);
-			goto free_kobj;
+			goto free_crypto;
 		}
 	}
 
@@ -1219,11 +1230,13 @@ try_onemore:
 		/* After POR, we can run background GC thread.*/
 		err = start_gc_thread(sbi);
 		if (err)
-			goto free_kobj;
+			goto free_crypto;
 	}
 	kfree(options);
 	return 0;
 
+free_crypto:
+	f2fs_exit_crypto();
 free_kobj:
 	kobject_del(&sbi->s_kobj);
 free_proc:
-- 
2.1.1


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

* [PATCH 08/18] f2fs crypto: add encryption key management facilities
  2015-05-09  4:20 [PATCH 01/18] f2fs: avoid value overflow in showing current status Jaegeuk Kim
                   ` (5 preceding siblings ...)
  2015-05-09  4:20 ` [PATCH 07/18] f2fs crypto: add f2fs encryption facilities Jaegeuk Kim
@ 2015-05-09  4:20 ` Jaegeuk Kim
  2015-05-09  4:20 ` [PATCH 09/18] f2fs crypto: filename encryption facilities Jaegeuk Kim
                   ` (9 subsequent siblings)
  16 siblings, 0 replies; 34+ messages in thread
From: Jaegeuk Kim @ 2015-05-09  4:20 UTC (permalink / raw)
  To: linux-kernel, linux-fsdevel, linux-f2fs-devel
  Cc: Jaegeuk Kim, Michael Halcrow, Ildar Muslukhov, Theodore Ts'o

This patch copies from encrypt_key.c in ext4, and modifies for f2fs.

Use GFP_NOFS, since _f2fs_get_encryption_info is called under f2fs_lock_op.

Signed-off-by: Michael Halcrow <mhalcrow@google.com>
Signed-off-by: Ildar Muslukhov <muslukhovi@gmail.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---
 fs/f2fs/Makefile      |   2 +-
 fs/f2fs/crypto_key.c  | 200 ++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/f2fs/f2fs.h        |  22 ++++++
 fs/f2fs/f2fs_crypto.h |   3 +
 4 files changed, 226 insertions(+), 1 deletion(-)
 create mode 100644 fs/f2fs/crypto_key.c

diff --git a/fs/f2fs/Makefile b/fs/f2fs/Makefile
index a79907b..b08925d 100644
--- a/fs/f2fs/Makefile
+++ b/fs/f2fs/Makefile
@@ -6,4 +6,4 @@ f2fs-$(CONFIG_F2FS_STAT_FS) += debug.o
 f2fs-$(CONFIG_F2FS_FS_XATTR) += xattr.o
 f2fs-$(CONFIG_F2FS_FS_POSIX_ACL) += acl.o
 f2fs-$(CONFIG_F2FS_IO_TRACE) += trace.o
-f2fs-$(CONFIG_F2FS_FS_ENCRYPTION) += crypto_policy.o crypto.o
+f2fs-$(CONFIG_F2FS_FS_ENCRYPTION) += crypto_policy.o crypto.o crypto_key.o
diff --git a/fs/f2fs/crypto_key.c b/fs/f2fs/crypto_key.c
new file mode 100644
index 0000000..aec7e17
--- /dev/null
+++ b/fs/f2fs/crypto_key.c
@@ -0,0 +1,200 @@
+/*
+ * linux/fs/f2fs/crypto_key.c
+ *
+ * Copied from linux/fs/f2fs/crypto_key.c
+ *
+ * Copyright (C) 2015, Google, Inc.
+ *
+ * This contains encryption key functions for f2fs
+ *
+ * Written by Michael Halcrow, Ildar Muslukhov, and Uday Savagaonkar, 2015.
+ */
+#include <keys/encrypted-type.h>
+#include <keys/user-type.h>
+#include <linux/random.h>
+#include <linux/scatterlist.h>
+#include <uapi/linux/keyctl.h>
+#include <crypto/hash.h>
+#include <linux/f2fs_fs.h>
+
+#include "f2fs.h"
+#include "xattr.h"
+
+static void derive_crypt_complete(struct crypto_async_request *req, int rc)
+{
+	struct f2fs_completion_result *ecr = req->data;
+
+	if (rc == -EINPROGRESS)
+		return;
+
+	ecr->res = rc;
+	complete(&ecr->completion);
+}
+
+/**
+ * f2fs_derive_key_aes() - Derive a key using AES-128-ECB
+ * @deriving_key: Encryption key used for derivatio.
+ * @source_key:   Source key to which to apply derivation.
+ * @derived_key:  Derived key.
+ *
+ * Return: Zero on success; non-zero otherwise.
+ */
+static int f2fs_derive_key_aes(char deriving_key[F2FS_AES_128_ECB_KEY_SIZE],
+				char source_key[F2FS_AES_256_XTS_KEY_SIZE],
+				char derived_key[F2FS_AES_256_XTS_KEY_SIZE])
+{
+	int res = 0;
+	struct ablkcipher_request *req = NULL;
+	DECLARE_F2FS_COMPLETION_RESULT(ecr);
+	struct scatterlist src_sg, dst_sg;
+	struct crypto_ablkcipher *tfm = crypto_alloc_ablkcipher("ecb(aes)", 0,
+								0);
+
+	if (IS_ERR(tfm)) {
+		res = PTR_ERR(tfm);
+		tfm = NULL;
+		goto out;
+	}
+	crypto_ablkcipher_set_flags(tfm, CRYPTO_TFM_REQ_WEAK_KEY);
+	req = ablkcipher_request_alloc(tfm, GFP_NOFS);
+	if (!req) {
+		res = -ENOMEM;
+		goto out;
+	}
+	ablkcipher_request_set_callback(req,
+			CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
+			derive_crypt_complete, &ecr);
+	res = crypto_ablkcipher_setkey(tfm, deriving_key,
+				F2FS_AES_128_ECB_KEY_SIZE);
+	if (res < 0)
+		goto out;
+
+	sg_init_one(&src_sg, source_key, F2FS_AES_256_XTS_KEY_SIZE);
+	sg_init_one(&dst_sg, derived_key, F2FS_AES_256_XTS_KEY_SIZE);
+	ablkcipher_request_set_crypt(req, &src_sg, &dst_sg,
+					F2FS_AES_256_XTS_KEY_SIZE, NULL);
+	res = crypto_ablkcipher_encrypt(req);
+	if (res == -EINPROGRESS || res == -EBUSY) {
+		BUG_ON(req->base.data != &ecr);
+		wait_for_completion(&ecr.completion);
+		res = ecr.res;
+	}
+out:
+	if (req)
+		ablkcipher_request_free(req);
+	if (tfm)
+		crypto_free_ablkcipher(tfm);
+	return res;
+}
+
+void f2fs_free_encryption_info(struct inode *inode)
+{
+	struct f2fs_inode_info *fi = F2FS_I(inode);
+	struct f2fs_crypt_info *ci = fi->i_crypt_info;
+
+	if (!ci)
+		return;
+
+	if (ci->ci_keyring_key)
+		key_put(ci->ci_keyring_key);
+	crypto_free_ablkcipher(ci->ci_ctfm);
+	memzero_explicit(&ci->ci_raw, sizeof(ci->ci_raw));
+	kfree(ci);
+	fi->i_crypt_info = NULL;
+}
+
+int _f2fs_get_encryption_info(struct inode *inode)
+{
+	struct f2fs_inode_info *fi = F2FS_I(inode);
+	struct f2fs_crypt_info *crypt_info;
+	char full_key_descriptor[F2FS_KEY_DESC_PREFIX_SIZE +
+				(F2FS_KEY_DESCRIPTOR_SIZE * 2) + 1];
+	struct key *keyring_key = NULL;
+	struct f2fs_encryption_key *master_key;
+	struct f2fs_encryption_context ctx;
+	struct user_key_payload *ukp;
+	int res;
+
+	if (fi->i_crypt_info) {
+		if (!fi->i_crypt_info->ci_keyring_key ||
+			key_validate(fi->i_crypt_info->ci_keyring_key) == 0)
+			return 0;
+		f2fs_free_encryption_info(inode);
+	}
+
+	res = f2fs_getxattr(inode, F2FS_XATTR_INDEX_ENCRYPTION,
+				F2FS_XATTR_NAME_ENCRYPTION_CONTEXT,
+				&ctx, sizeof(ctx), NULL);
+	if (res < 0)
+		return res;
+	else if (res != sizeof(ctx))
+		return -EINVAL;
+	res = 0;
+
+	crypt_info = kmalloc(sizeof(struct f2fs_crypt_info), GFP_NOFS);
+	if (!crypt_info)
+		return -ENOMEM;
+
+	crypt_info->ci_flags = ctx.flags;
+	crypt_info->ci_data_mode = ctx.contents_encryption_mode;
+	crypt_info->ci_filename_mode = ctx.filenames_encryption_mode;
+	crypt_info->ci_ctfm = NULL;
+	memcpy(crypt_info->ci_master_key, ctx.master_key_descriptor,
+				sizeof(crypt_info->ci_master_key));
+	if (S_ISREG(inode->i_mode))
+		crypt_info->ci_mode = ctx.contents_encryption_mode;
+	else if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
+		crypt_info->ci_mode = ctx.filenames_encryption_mode;
+	else {
+		printk(KERN_ERR "f2fs crypto: Unsupported inode type.\n");
+		BUG();
+	}
+	crypt_info->ci_size = f2fs_encryption_key_size(crypt_info->ci_mode);
+	BUG_ON(!crypt_info->ci_size);
+
+	memcpy(full_key_descriptor, F2FS_KEY_DESC_PREFIX,
+					F2FS_KEY_DESC_PREFIX_SIZE);
+	sprintf(full_key_descriptor + F2FS_KEY_DESC_PREFIX_SIZE,
+					"%*phN", F2FS_KEY_DESCRIPTOR_SIZE,
+					ctx.master_key_descriptor);
+	full_key_descriptor[F2FS_KEY_DESC_PREFIX_SIZE +
+					(2 * F2FS_KEY_DESCRIPTOR_SIZE)] = '\0';
+	keyring_key = request_key(&key_type_logon, full_key_descriptor, NULL);
+	if (IS_ERR(keyring_key)) {
+		res = PTR_ERR(keyring_key);
+		keyring_key = NULL;
+		goto out;
+	}
+	BUG_ON(keyring_key->type != &key_type_logon);
+	ukp = ((struct user_key_payload *)keyring_key->payload.data);
+	if (ukp->datalen != sizeof(struct f2fs_encryption_key)) {
+		res = -EINVAL;
+		goto out;
+	}
+	master_key = (struct f2fs_encryption_key *)ukp->data;
+	BUILD_BUG_ON(F2FS_AES_128_ECB_KEY_SIZE !=
+				F2FS_KEY_DERIVATION_NONCE_SIZE);
+	BUG_ON(master_key->size != F2FS_AES_256_XTS_KEY_SIZE);
+	res = f2fs_derive_key_aes(ctx.nonce, master_key->raw,
+					crypt_info->ci_raw);
+out:
+	if (res < 0) {
+		if (res == -ENOKEY)
+			res = 0;
+		kfree(crypt_info);
+	} else {
+		fi->i_crypt_info = crypt_info;
+		crypt_info->ci_keyring_key = keyring_key;
+		keyring_key = NULL;
+	}
+	if (keyring_key)
+		key_put(keyring_key);
+	return res;
+}
+
+int f2fs_has_encryption_key(struct inode *inode)
+{
+	struct f2fs_inode_info *fi = F2FS_I(inode);
+
+	return (fi->i_crypt_info != NULL);
+}
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index d586346..ef79ac8 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -1973,17 +1973,39 @@ int f2fs_decrypt(struct f2fs_crypto_ctx *, struct page *);
 int f2fs_decrypt_one(struct inode *, struct page *);
 void f2fs_end_io_crypto_work(struct f2fs_crypto_ctx *, struct bio *);
 
+/* crypto_key.c */
+void f2fs_free_encryption_info(struct inode *);
+int _f2fs_get_encryption_info(struct inode *inode);
+
 #ifdef CONFIG_F2FS_FS_ENCRYPTION
 void f2fs_restore_and_release_control_page(struct page **);
 void f2fs_restore_control_page(struct page *);
 
 int f2fs_init_crypto(void);
 void f2fs_exit_crypto(void);
+
+int f2fs_has_encryption_key(struct inode *);
+
+static inline int f2fs_get_encryption_info(struct inode *inode)
+{
+	struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info;
+
+	if (!ci ||
+		(ci->ci_keyring_key &&
+		 (ci->ci_keyring_key->flags & ((1 << KEY_FLAG_INVALIDATED) |
+					       (1 << KEY_FLAG_REVOKED) |
+					       (1 << KEY_FLAG_DEAD)))))
+		return _f2fs_get_encryption_info(inode);
+	return 0;
+}
 #else
 static inline void f2fs_restore_and_release_control_page(struct page **p) { }
 static inline void f2fs_restore_control_page(struct page *p) { }
 
 static inline int f2fs_init_crypto(void) { return 0; }
 static inline void f2fs_exit_crypto(void) { }
+
+static inline int f2fs_has_encryption_key(struct inode *i) { return 0; }
+static inline int f2fs_get_encryption_info(struct inode *i) { return 0; }
 #endif
 #endif
diff --git a/fs/f2fs/f2fs_crypto.h b/fs/f2fs/f2fs_crypto.h
index cfc37c1..bad32e6 100644
--- a/fs/f2fs/f2fs_crypto.h
+++ b/fs/f2fs/f2fs_crypto.h
@@ -65,6 +65,9 @@ struct f2fs_encryption_context {
 #define F2FS_AES_256_XTS_KEY_SIZE 64
 #define F2FS_MAX_KEY_SIZE 64
 
+#define F2FS_KEY_DESC_PREFIX "f2fs:"
+#define F2FS_KEY_DESC_PREFIX_SIZE 5
+
 struct f2fs_encryption_key {
 	__u32 mode;
 	char raw[F2FS_MAX_KEY_SIZE];
-- 
2.1.1


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

* [PATCH 09/18] f2fs crypto: filename encryption facilities
  2015-05-09  4:20 [PATCH 01/18] f2fs: avoid value overflow in showing current status Jaegeuk Kim
                   ` (6 preceding siblings ...)
  2015-05-09  4:20 ` [PATCH 08/18] f2fs crypto: add encryption key management facilities Jaegeuk Kim
@ 2015-05-09  4:20 ` Jaegeuk Kim
  2015-05-09  4:20 ` [PATCH 10/18] f2fs crypto: activate encryption support for fs APIs Jaegeuk Kim
                   ` (8 subsequent siblings)
  16 siblings, 0 replies; 34+ messages in thread
From: Jaegeuk Kim @ 2015-05-09  4:20 UTC (permalink / raw)
  To: linux-kernel, linux-fsdevel, linux-f2fs-devel
  Cc: Jaegeuk Kim, Uday Savagaonkar, Ildar Muslukhov, Michael Halcrow,
	Theodore Ts'o

This patch adds filename encryption infra.
Most of codes are copied from ext4 part, but changed to adjust f2fs
directory structure.

Signed-off-by: Uday Savagaonkar <savagaon@google.com>
Signed-off-by: Ildar Muslukhov <ildarm@google.com>
Signed-off-by: Michael Halcrow <mhalcrow@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---
 fs/f2fs/Makefile       |   3 +-
 fs/f2fs/crypto_fname.c | 490 +++++++++++++++++++++++++++++++++++++++++++++++++
 fs/f2fs/f2fs.h         |  52 ++++++
 3 files changed, 544 insertions(+), 1 deletion(-)
 create mode 100644 fs/f2fs/crypto_fname.c

diff --git a/fs/f2fs/Makefile b/fs/f2fs/Makefile
index b08925d..396be1a 100644
--- a/fs/f2fs/Makefile
+++ b/fs/f2fs/Makefile
@@ -6,4 +6,5 @@ f2fs-$(CONFIG_F2FS_STAT_FS) += debug.o
 f2fs-$(CONFIG_F2FS_FS_XATTR) += xattr.o
 f2fs-$(CONFIG_F2FS_FS_POSIX_ACL) += acl.o
 f2fs-$(CONFIG_F2FS_IO_TRACE) += trace.o
-f2fs-$(CONFIG_F2FS_FS_ENCRYPTION) += crypto_policy.o crypto.o crypto_key.o
+f2fs-$(CONFIG_F2FS_FS_ENCRYPTION) += crypto_policy.o crypto.o \
+		crypto_key.o crypto_fname.o
diff --git a/fs/f2fs/crypto_fname.c b/fs/f2fs/crypto_fname.c
new file mode 100644
index 0000000..8f3ff9b
--- /dev/null
+++ b/fs/f2fs/crypto_fname.c
@@ -0,0 +1,490 @@
+/*
+ * linux/fs/f2fs/crypto_fname.c
+ *
+ * Copied from linux/fs/ext4/crypto.c
+ *
+ * Copyright (C) 2015, Google, Inc.
+ * Copyright (C) 2015, Motorola Mobility
+ *
+ * This contains functions for filename crypto management in f2fs
+ *
+ * Written by Uday Savagaonkar, 2014.
+ *
+ * Adjust f2fs dentry structure
+ *	Jaegeuk Kim, 2015.
+ *
+ * This has not yet undergone a rigorous security audit.
+ */
+#include <crypto/hash.h>
+#include <crypto/sha.h>
+#include <keys/encrypted-type.h>
+#include <keys/user-type.h>
+#include <linux/crypto.h>
+#include <linux/gfp.h>
+#include <linux/kernel.h>
+#include <linux/key.h>
+#include <linux/list.h>
+#include <linux/mempool.h>
+#include <linux/random.h>
+#include <linux/scatterlist.h>
+#include <linux/spinlock_types.h>
+#include <linux/f2fs_fs.h>
+#include <linux/ratelimit.h>
+
+#include "f2fs.h"
+#include "f2fs_crypto.h"
+#include "xattr.h"
+
+/**
+ * f2fs_dir_crypt_complete() -
+ */
+static void f2fs_dir_crypt_complete(struct crypto_async_request *req, int res)
+{
+	struct f2fs_completion_result *ecr = req->data;
+
+	if (res == -EINPROGRESS)
+		return;
+	ecr->res = res;
+	complete(&ecr->completion);
+}
+
+bool f2fs_valid_filenames_enc_mode(uint32_t mode)
+{
+	return (mode == F2FS_ENCRYPTION_MODE_AES_256_CTS);
+}
+
+static unsigned max_name_len(struct inode *inode)
+{
+	return S_ISLNK(inode->i_mode) ? inode->i_sb->s_blocksize :
+					F2FS_NAME_LEN;
+}
+
+/**
+ * f2fs_fname_encrypt() -
+ *
+ * This function encrypts the input filename, and returns the length of the
+ * ciphertext. Errors are returned as negative numbers.  We trust the caller to
+ * allocate sufficient memory to oname string.
+ */
+static int f2fs_fname_encrypt(struct inode *inode,
+			const struct qstr *iname, struct f2fs_str *oname)
+{
+	u32 ciphertext_len;
+	struct ablkcipher_request *req = NULL;
+	DECLARE_F2FS_COMPLETION_RESULT(ecr);
+	struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info;
+	struct crypto_ablkcipher *tfm = ci->ci_ctfm;
+	int res = 0;
+	char iv[F2FS_CRYPTO_BLOCK_SIZE];
+	struct scatterlist src_sg, dst_sg;
+	int padding = 4 << (ci->ci_flags & F2FS_POLICY_FLAGS_PAD_MASK);
+	char *workbuf, buf[32], *alloc_buf = NULL;
+	unsigned lim = max_name_len(inode);
+
+	if (iname->len <= 0 || iname->len > lim)
+		return -EIO;
+
+	ciphertext_len = (iname->len < F2FS_CRYPTO_BLOCK_SIZE) ?
+		F2FS_CRYPTO_BLOCK_SIZE : iname->len;
+	ciphertext_len = f2fs_fname_crypto_round_up(ciphertext_len, padding);
+	ciphertext_len = (ciphertext_len > lim) ? lim : ciphertext_len;
+
+	if (ciphertext_len <= sizeof(buf)) {
+		workbuf = buf;
+	} else {
+		alloc_buf = kmalloc(ciphertext_len, GFP_NOFS);
+		if (!alloc_buf)
+			return -ENOMEM;
+		workbuf = alloc_buf;
+	}
+
+	/* Allocate request */
+	req = ablkcipher_request_alloc(tfm, GFP_NOFS);
+	if (!req) {
+		printk_ratelimited(KERN_ERR
+			"%s: crypto_request_alloc() failed\n", __func__);
+		kfree(alloc_buf);
+		return -ENOMEM;
+	}
+	ablkcipher_request_set_callback(req,
+			CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
+			f2fs_dir_crypt_complete, &ecr);
+
+	/* Copy the input */
+	memcpy(workbuf, iname->name, iname->len);
+	if (iname->len < ciphertext_len)
+		memset(workbuf + iname->len, 0, ciphertext_len - iname->len);
+
+	/* Initialize IV */
+	memset(iv, 0, F2FS_CRYPTO_BLOCK_SIZE);
+
+	/* Create encryption request */
+	sg_init_one(&src_sg, workbuf, ciphertext_len);
+	sg_init_one(&dst_sg, oname->name, ciphertext_len);
+	ablkcipher_request_set_crypt(req, &src_sg, &dst_sg, ciphertext_len, iv);
+	res = crypto_ablkcipher_encrypt(req);
+	if (res == -EINPROGRESS || res == -EBUSY) {
+		BUG_ON(req->base.data != &ecr);
+		wait_for_completion(&ecr.completion);
+		res = ecr.res;
+	}
+	kfree(alloc_buf);
+	ablkcipher_request_free(req);
+	if (res < 0) {
+		printk_ratelimited(KERN_ERR
+				"%s: Error (error code %d)\n", __func__, res);
+	}
+	oname->len = ciphertext_len;
+	return res;
+}
+
+/*
+ * f2fs_fname_decrypt()
+ *	This function decrypts the input filename, and returns
+ *	the length of the plaintext.
+ *	Errors are returned as negative numbers.
+ *	We trust the caller to allocate sufficient memory to oname string.
+ */
+static int f2fs_fname_decrypt(struct inode *inode,
+			const struct f2fs_str *iname, struct f2fs_str *oname)
+{
+	struct ablkcipher_request *req = NULL;
+	DECLARE_F2FS_COMPLETION_RESULT(ecr);
+	struct scatterlist src_sg, dst_sg;
+	struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info;
+	struct crypto_ablkcipher *tfm = ci->ci_ctfm;
+	int res = 0;
+	char iv[F2FS_CRYPTO_BLOCK_SIZE];
+	unsigned lim = max_name_len(inode);
+
+	if (iname->len <= 0 || iname->len > lim)
+		return -EIO;
+
+	/* Allocate request */
+	req = ablkcipher_request_alloc(tfm, GFP_NOFS);
+	if (!req) {
+		printk_ratelimited(KERN_ERR
+			"%s: crypto_request_alloc() failed\n",  __func__);
+		return -ENOMEM;
+	}
+	ablkcipher_request_set_callback(req,
+		CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
+		f2fs_dir_crypt_complete, &ecr);
+
+	/* Initialize IV */
+	memset(iv, 0, F2FS_CRYPTO_BLOCK_SIZE);
+
+	/* Create decryption request */
+	sg_init_one(&src_sg, iname->name, iname->len);
+	sg_init_one(&dst_sg, oname->name, oname->len);
+	ablkcipher_request_set_crypt(req, &src_sg, &dst_sg, iname->len, iv);
+	res = crypto_ablkcipher_decrypt(req);
+	if (res == -EINPROGRESS || res == -EBUSY) {
+		BUG_ON(req->base.data != &ecr);
+		wait_for_completion(&ecr.completion);
+		res = ecr.res;
+	}
+	ablkcipher_request_free(req);
+	if (res < 0) {
+		printk_ratelimited(KERN_ERR
+			"%s: Error in f2fs_fname_decrypt (error code %d)\n",
+			__func__, res);
+		return res;
+	}
+
+	oname->len = strnlen(oname->name, iname->len);
+	return oname->len;
+}
+
+static const char *lookup_table =
+	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
+
+/**
+ * f2fs_fname_encode_digest() -
+ *
+ * Encodes the input digest using characters from the set [a-zA-Z0-9_+].
+ * The encoded string is roughly 4/3 times the size of the input string.
+ */
+static int digest_encode(const char *src, int len, char *dst)
+{
+	int i = 0, bits = 0, ac = 0;
+	char *cp = dst;
+
+	while (i < len) {
+		ac += (((unsigned char) src[i]) << bits);
+		bits += 8;
+		do {
+			*cp++ = lookup_table[ac & 0x3f];
+			ac >>= 6;
+			bits -= 6;
+		} while (bits >= 6);
+		i++;
+	}
+	if (bits)
+		*cp++ = lookup_table[ac & 0x3f];
+	return cp - dst;
+}
+
+static int digest_decode(const char *src, int len, char *dst)
+{
+	int i = 0, bits = 0, ac = 0;
+	const char *p;
+	char *cp = dst;
+
+	while (i < len) {
+		p = strchr(lookup_table, src[i]);
+		if (p == NULL || src[i] == 0)
+			return -2;
+		ac += (p - lookup_table) << bits;
+		bits += 6;
+		if (bits >= 8) {
+			*cp++ = ac & 0xff;
+			ac >>= 8;
+			bits -= 8;
+		}
+		i++;
+	}
+	if (ac)
+		return -1;
+	return cp - dst;
+}
+
+int f2fs_setup_fname_crypto(struct inode *inode)
+{
+	struct f2fs_inode_info *fi = F2FS_I(inode);
+	struct f2fs_crypt_info *ci = fi->i_crypt_info;
+	struct crypto_ablkcipher *ctfm;
+	int res;
+
+	/* Check if the crypto policy is set on the inode */
+	res = f2fs_encrypted_inode(inode);
+	if (res == 0)
+		return 0;
+
+	res = f2fs_get_encryption_info(inode);
+	if (res < 0)
+		return res;
+	ci = fi->i_crypt_info;
+
+	if (!ci || ci->ci_ctfm)
+		return 0;
+
+	if (ci->ci_mode != F2FS_ENCRYPTION_MODE_AES_256_CTS) {
+		printk_once(KERN_WARNING "f2fs: unsupported key mode %d\n",
+				ci->ci_mode);
+		return -ENOKEY;
+	}
+
+	ctfm = crypto_alloc_ablkcipher("cts(cbc(aes))", 0, 0);
+	if (!ctfm || IS_ERR(ctfm)) {
+		res = ctfm ? PTR_ERR(ctfm) : -ENOMEM;
+		printk(KERN_DEBUG "%s: error (%d) allocating crypto tfm\n",
+				__func__, res);
+		return res;
+	}
+	crypto_ablkcipher_clear_flags(ctfm, ~0);
+	crypto_tfm_set_flags(crypto_ablkcipher_tfm(ctfm),
+			     CRYPTO_TFM_REQ_WEAK_KEY);
+
+	res = crypto_ablkcipher_setkey(ctfm, ci->ci_raw, ci->ci_size);
+	if (res) {
+		crypto_free_ablkcipher(ctfm);
+		return -EIO;
+	}
+	ci->ci_ctfm = ctfm;
+	return 0;
+}
+
+/**
+ * f2fs_fname_crypto_round_up() -
+ *
+ * Return: The next multiple of block size
+ */
+u32 f2fs_fname_crypto_round_up(u32 size, u32 blksize)
+{
+	return ((size + blksize - 1) / blksize) * blksize;
+}
+
+/**
+ * f2fs_fname_crypto_alloc_obuff() -
+ *
+ * Allocates an output buffer that is sufficient for the crypto operation
+ * specified by the context and the direction.
+ */
+int f2fs_fname_crypto_alloc_buffer(struct inode *inode,
+				   u32 ilen, struct f2fs_str *crypto_str)
+{
+	unsigned int olen;
+	int padding = 16;
+	struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info;
+
+	if (ci)
+		padding = 4 << (ci->ci_flags & F2FS_POLICY_FLAGS_PAD_MASK);
+	if (padding < F2FS_CRYPTO_BLOCK_SIZE)
+		padding = F2FS_CRYPTO_BLOCK_SIZE;
+	olen = f2fs_fname_crypto_round_up(ilen, padding);
+	crypto_str->len = olen;
+	if (olen < F2FS_FNAME_CRYPTO_DIGEST_SIZE * 2)
+		olen = F2FS_FNAME_CRYPTO_DIGEST_SIZE * 2;
+	/* Allocated buffer can hold one more character to null-terminate the
+	 * string */
+	crypto_str->name = kmalloc(olen + 1, GFP_NOFS);
+	if (!(crypto_str->name))
+		return -ENOMEM;
+	return 0;
+}
+
+/**
+ * f2fs_fname_crypto_free_buffer() -
+ *
+ * Frees the buffer allocated for crypto operation.
+ */
+void f2fs_fname_crypto_free_buffer(struct f2fs_str *crypto_str)
+{
+	if (!crypto_str)
+		return;
+	kfree(crypto_str->name);
+	crypto_str->name = NULL;
+}
+
+/**
+ * f2fs_fname_disk_to_usr() - converts a filename from disk space to user space
+ */
+int f2fs_fname_disk_to_usr(struct inode *inode,
+			f2fs_hash_t *hash,
+			const struct f2fs_str *iname,
+			struct f2fs_str *oname)
+{
+	const struct qstr qname = FSTR_TO_QSTR(iname);
+	char buf[24];
+	int ret;
+
+	if (is_dot_dotdot(&qname)) {
+		oname->name[0] = '.';
+		oname->name[iname->len - 1] = '.';
+		oname->len = iname->len;
+		return oname->len;
+	}
+
+	if (F2FS_I(inode)->i_crypt_info)
+		return f2fs_fname_decrypt(inode, iname, oname);
+
+	if (iname->len <= F2FS_FNAME_CRYPTO_DIGEST_SIZE) {
+		ret = digest_encode(iname->name, iname->len, oname->name);
+		oname->len = ret;
+		return ret;
+	}
+	if (hash) {
+		memcpy(buf, hash, 4);
+		memset(buf + 4, 0, 4);
+	} else
+		memset(buf, 0, 8);
+	memcpy(buf + 8, iname->name + iname->len - 16, 16);
+	oname->name[0] = '_';
+	ret = digest_encode(buf, 24, oname->name + 1);
+	oname->len = ret + 1;
+	return ret + 1;
+}
+
+/**
+ * f2fs_fname_usr_to_disk() - converts a filename from user space to disk space
+ */
+int f2fs_fname_usr_to_disk(struct inode *inode,
+			const struct qstr *iname,
+			struct f2fs_str *oname)
+{
+	int res;
+	struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info;
+
+	if (is_dot_dotdot(iname)) {
+		oname->name[0] = '.';
+		oname->name[iname->len - 1] = '.';
+		oname->len = iname->len;
+		return oname->len;
+	}
+
+	if (ci) {
+		res = f2fs_fname_encrypt(inode, iname, oname);
+		return res;
+	}
+	/* Without a proper key, a user is not allowed to modify the filenames
+	 * in a directory. Consequently, a user space name cannot be mapped to
+	 * a disk-space name */
+	return -EACCES;
+}
+
+int f2fs_fname_setup_filename(struct inode *dir, const struct qstr *iname,
+			      int lookup, struct f2fs_filename *fname)
+{
+	struct f2fs_crypt_info *ci;
+	int ret = 0, bigname = 0;
+
+	memset(fname, 0, sizeof(struct f2fs_filename));
+	fname->usr_fname = iname;
+
+	if (!f2fs_encrypted_inode(dir) || is_dot_dotdot(iname)) {
+		fname->disk_name.name = (unsigned char *)iname->name;
+		fname->disk_name.len = iname->len;
+		goto out;
+	}
+	ret = f2fs_setup_fname_crypto(dir);
+	if (ret)
+		return ret;
+	ci = F2FS_I(dir)->i_crypt_info;
+	if (ci) {
+		ret = f2fs_fname_crypto_alloc_buffer(dir, iname->len,
+						     &fname->crypto_buf);
+		if (ret < 0)
+			goto out;
+		ret = f2fs_fname_encrypt(dir, iname, &fname->crypto_buf);
+		if (ret < 0)
+			goto out;
+		fname->disk_name.name = fname->crypto_buf.name;
+		fname->disk_name.len = fname->crypto_buf.len;
+		ret = 0;
+		goto out;
+	}
+	if (!lookup) {
+		ret = -EACCES;
+		goto out;
+	}
+
+	/* We don't have the key and we are doing a lookup; decode the
+	 * user-supplied name
+	 */
+	if (iname->name[0] == '_')
+		bigname = 1;
+	if ((bigname && (iname->len != 33)) ||
+	    (!bigname && (iname->len > 43))) {
+		ret = -ENOENT;
+	}
+	fname->crypto_buf.name = kmalloc(32, GFP_KERNEL);
+	if (fname->crypto_buf.name == NULL) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	ret = digest_decode(iname->name + bigname, iname->len - bigname,
+				fname->crypto_buf.name);
+	if (ret < 0) {
+		ret = -ENOENT;
+		goto out;
+	}
+	fname->crypto_buf.len = ret;
+	if (bigname) {
+		memcpy(&fname->hash, fname->crypto_buf.name, 4);
+	} else {
+		fname->disk_name.name = fname->crypto_buf.name;
+		fname->disk_name.len = fname->crypto_buf.len;
+	}
+	ret = 0;
+out:
+	return ret;
+}
+
+void f2fs_fname_free_filename(struct f2fs_filename *fname)
+{
+	kfree(fname->crypto_buf.name);
+	fname->crypto_buf.name = NULL;
+	fname->usr_fname = NULL;
+	fname->disk_name.name = NULL;
+}
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index ef79ac8..e99205b 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -257,6 +257,25 @@ static inline bool __has_cursum_space(struct f2fs_summary_block *sum, int size,
  * For INODE and NODE manager
  */
 /* for directory operations */
+struct f2fs_str {
+	unsigned char *name;
+	u32 len;
+};
+
+struct f2fs_filename {
+	const struct qstr *usr_fname;
+	struct f2fs_str disk_name;
+	f2fs_hash_t hash;
+#ifdef CONFIG_F2FS_FS_ENCRYPTION
+	struct f2fs_str crypto_buf;
+#endif
+};
+
+#define FSTR_INIT(n, l)		{ .name = n, .len = l }
+#define FSTR_TO_QSTR(f)		FSTR_INIT((f)->name, (f)->len)
+#define fname_name(p)		((p)->disk_name.name)
+#define fname_len(p)		((p)->disk_name.len)
+
 struct f2fs_dentry_ptr {
 	const void *bitmap;
 	struct f2fs_dir_entry *dentry;
@@ -1977,6 +1996,17 @@ void f2fs_end_io_crypto_work(struct f2fs_crypto_ctx *, struct bio *);
 void f2fs_free_encryption_info(struct inode *);
 int _f2fs_get_encryption_info(struct inode *inode);
 
+/* crypto_fname.c */
+bool f2fs_valid_filenames_enc_mode(uint32_t);
+u32 f2fs_fname_crypto_round_up(u32, u32);
+int f2fs_fname_crypto_alloc_buffer(struct inode *, u32, struct f2fs_str *);
+int f2fs_fname_disk_to_usr(struct inode *, f2fs_hash_t *,
+			const struct f2fs_str *, struct f2fs_str *);
+int f2fs_fname_disk_to_usr(struct inode *, f2fs_hash_t *,
+			const struct f2fs_str *, struct f2fs_str *);
+int f2fs_fname_usr_to_disk(struct inode *, const struct qstr *,
+			struct f2fs_str *);
+
 #ifdef CONFIG_F2FS_FS_ENCRYPTION
 void f2fs_restore_and_release_control_page(struct page **);
 void f2fs_restore_control_page(struct page *);
@@ -1998,6 +2028,12 @@ static inline int f2fs_get_encryption_info(struct inode *inode)
 		return _f2fs_get_encryption_info(inode);
 	return 0;
 }
+
+int f2fs_setup_fname_crypto(struct inode *);
+void f2fs_fname_crypto_free_buffer(struct f2fs_str *);
+int f2fs_fname_setup_filename(struct inode *, const struct qstr *,
+				int lookup, struct f2fs_filename *);
+void f2fs_fname_free_filename(struct f2fs_filename *);
 #else
 static inline void f2fs_restore_and_release_control_page(struct page **p) { }
 static inline void f2fs_restore_control_page(struct page *p) { }
@@ -2007,5 +2043,21 @@ static inline void f2fs_exit_crypto(void) { }
 
 static inline int f2fs_has_encryption_key(struct inode *i) { return 0; }
 static inline int f2fs_get_encryption_info(struct inode *i) { return 0; }
+
+static inline int f2fs_setup_fname_crypto(struct inode *i) { return 0; }
+static inline void f2fs_fname_crypto_free_buffer(struct f2fs_str *p) { }
+
+static inline int f2fs_fname_setup_filename(struct inode *dir,
+					const struct qstr *iname,
+					int lookup, struct f2fs_filename *fname)
+{
+	memset(fname, 0, sizeof(struct f2fs_filename));
+	fname->usr_fname = iname;
+	fname->disk_name.name = (unsigned char *)iname->name;
+	fname->disk_name.len = iname->len;
+	return 0;
+}
+
+static inline void f2fs_fname_free_filename(struct f2fs_filename *fname) { }
 #endif
 #endif
-- 
2.1.1


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

* [PATCH 10/18] f2fs crypto: activate encryption support for fs APIs
  2015-05-09  4:20 [PATCH 01/18] f2fs: avoid value overflow in showing current status Jaegeuk Kim
                   ` (7 preceding siblings ...)
  2015-05-09  4:20 ` [PATCH 09/18] f2fs crypto: filename encryption facilities Jaegeuk Kim
@ 2015-05-09  4:20 ` Jaegeuk Kim
  2015-05-09  4:20 ` [PATCH 11/18] f2fs crypto: add encryption support in read/write paths Jaegeuk Kim
                   ` (7 subsequent siblings)
  16 siblings, 0 replies; 34+ messages in thread
From: Jaegeuk Kim @ 2015-05-09  4:20 UTC (permalink / raw)
  To: linux-kernel, linux-fsdevel, linux-f2fs-devel
  Cc: Jaegeuk Kim, Michael Halcrow, Theodore Ts'o

This patch activates the following APIs for encryption support.

The rules quoted by ext4 are:
 - An unencrypted directory may contain encrypted or unencrypted files
   or directories.
 - All files or directories in a directory must be protected using the
   same key as their containing directory.
 - Encrypted inode for regular file should not have inline_data.
 - Encrypted symlink and directory may have inline_data and inline_dentry.

This patch activates the following APIs.
1. f2fs_link              : validate context
2. f2fs_lookup            :      ''
3. f2fs_rename            :      ''
4. f2fs_create/f2fs_mkdir : inherit its dir's context
5. f2fs_direct_IO         : do buffered io for regular files
6. f2fs_open              : check encryption info
7. f2fs_file_mmap         :      ''
8. f2fs_setattr           :      ''
9. f2fs_file_write_iter   :      ''           (Called by sys_io_submit)

Signed-off-by: Michael Halcrow <mhalcrow@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---
 fs/f2fs/data.c   |  3 +++
 fs/f2fs/dir.c    |  6 ++++++
 fs/f2fs/f2fs.h   | 11 +++++++++++
 fs/f2fs/file.c   | 34 ++++++++++++++++++++++++++++++++--
 fs/f2fs/inline.c |  3 +++
 fs/f2fs/namei.c  | 38 ++++++++++++++++++++++++++++++++------
 6 files changed, 87 insertions(+), 8 deletions(-)

diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 842fcdd..473b4d4 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -1982,6 +1982,9 @@ static ssize_t f2fs_direct_IO(struct kiocb *iocb, struct iov_iter *iter,
 			return err;
 	}
 
+	if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode))
+		return 0;
+
 	if (check_direct_IO(inode, iter, offset))
 		return 0;
 
diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c
index 9d558d2..f7293a2 100644
--- a/fs/f2fs/dir.c
+++ b/fs/f2fs/dir.c
@@ -390,6 +390,12 @@ struct page *init_inode_metadata(struct inode *inode, struct inode *dir,
 		err = f2fs_init_security(inode, dir, name, page);
 		if (err)
 			goto put_error;
+
+		if (f2fs_encrypted_inode(dir) && f2fs_may_encrypt(inode)) {
+			err = f2fs_inherit_context(dir, inode, page);
+			if (err)
+				goto put_error;
+		}
 	} else {
 		page = get_node_page(F2FS_I_SB(dir), inode->i_ino);
 		if (IS_ERR(page))
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index e99205b..544766e 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -1975,6 +1975,17 @@ static inline int f2fs_sb_has_crypto(struct super_block *sb)
 #endif
 }
 
+static inline bool f2fs_may_encrypt(struct inode *inode)
+{
+#ifdef CONFIG_F2FS_FS_ENCRYPTION
+	mode_t mode = inode->i_mode;
+
+	return (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode));
+#else
+	return 0;
+#endif
+}
+
 /* crypto_policy.c */
 int f2fs_is_child_context_consistent_with_parent(struct inode *,
 							struct inode *);
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 7236be4..9f4b34c 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -408,6 +408,12 @@ static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma)
 {
 	struct inode *inode = file_inode(file);
 
+	if (f2fs_encrypted_inode(inode)) {
+		int err = f2fs_get_encryption_info(inode);
+		if (err)
+			return 0;
+	}
+
 	/* we don't need to use inline_data strictly */
 	if (f2fs_has_inline_data(inode)) {
 		int err = f2fs_convert_inline_inode(inode);
@@ -420,6 +426,14 @@ static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma)
 	return 0;
 }
 
+static int f2fs_file_open(struct inode *inode, struct file *filp)
+{
+	if (f2fs_encrypted_inode(inode) && f2fs_get_encryption_info(inode))
+		return -EACCES;
+
+	return generic_file_open(inode, filp);
+}
+
 int truncate_data_blocks_range(struct dnode_of_data *dn, int count)
 {
 	int nr_free = 0, ofs = dn->ofs_in_node;
@@ -627,6 +641,10 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr)
 		return err;
 
 	if (attr->ia_valid & ATTR_SIZE) {
+		if (f2fs_encrypted_inode(inode) &&
+				f2fs_get_encryption_info(inode))
+			return -EACCES;
+
 		if (attr->ia_size != i_size_read(inode)) {
 			truncate_setsize(inode, attr->ia_size);
 			f2fs_truncate(inode);
@@ -1466,6 +1484,18 @@ long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 	}
 }
 
+static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
+{
+	struct inode *inode = file_inode(iocb->ki_filp);
+
+	if (f2fs_encrypted_inode(inode) &&
+				!f2fs_has_encryption_key(inode) &&
+				f2fs_get_encryption_info(inode))
+		return -EACCES;
+
+	return generic_file_write_iter(iocb, from);
+}
+
 #ifdef CONFIG_COMPAT
 long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
@@ -1486,8 +1516,8 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 const struct file_operations f2fs_file_operations = {
 	.llseek		= f2fs_llseek,
 	.read_iter	= generic_file_read_iter,
-	.write_iter	= generic_file_write_iter,
-	.open		= generic_file_open,
+	.write_iter	= f2fs_file_write_iter,
+	.open		= f2fs_file_open,
 	.release	= f2fs_release_file,
 	.mmap		= f2fs_file_mmap,
 	.fsync		= f2fs_sync_file,
diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c
index d9b3033..5f5b34b 100644
--- a/fs/f2fs/inline.c
+++ b/fs/f2fs/inline.c
@@ -27,6 +27,9 @@ bool f2fs_may_inline_data(struct inode *inode)
 	if (i_size_read(inode) > MAX_INLINE_DATA)
 		return false;
 
+	if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode))
+		return false;
+
 	return true;
 }
 
diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c
index 90a9640..bc8992e 100644
--- a/fs/f2fs/namei.c
+++ b/fs/f2fs/namei.c
@@ -56,6 +56,10 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode)
 		goto out;
 	}
 
+	/* If the directory encrypted, then we should encrypt the inode. */
+	if (f2fs_encrypted_inode(dir) && f2fs_may_encrypt(inode))
+		f2fs_set_encrypted_inode(inode);
+
 	if (f2fs_may_inline_data(inode))
 		set_inode_flag(F2FS_I(inode), FI_INLINE_DATA);
 	if (f2fs_may_inline_dentry(inode))
@@ -157,6 +161,10 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir,
 	struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
 	int err;
 
+	if (f2fs_encrypted_inode(dir) &&
+		!f2fs_is_child_context_consistent_with_parent(dir, inode))
+		return -EPERM;
+
 	f2fs_balance_fs(sbi);
 
 	inode->i_ctime = CURRENT_TIME;
@@ -235,6 +243,7 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
 	struct f2fs_dir_entry *de;
 	struct page *page;
 	nid_t ino;
+	int err = 0;
 
 	if (dentry->d_name.len > F2FS_NAME_LEN)
 		return ERR_PTR(-ENAMETOOLONG);
@@ -251,16 +260,26 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
 	if (IS_ERR(inode))
 		return ERR_CAST(inode);
 
-	if (f2fs_has_inline_dots(inode)) {
-		int err;
+	if (f2fs_encrypted_inode(dir) && f2fs_may_encrypt(inode) &&
+		!f2fs_is_child_context_consistent_with_parent(dir, inode)) {
+		iput(inode);
+		f2fs_msg(inode->i_sb, KERN_WARNING,
+				"Inconsistent encryption contexts: %lu/%lu\n",
+				(unsigned long)dir->i_ino,
+				(unsigned long)inode->i_ino);
+		return ERR_PTR(-EPERM);
+	}
 
+	if (f2fs_has_inline_dots(inode)) {
 		err = __recover_dot_dentries(inode, dir->i_ino);
-		if (err) {
-			iget_failed(inode);
-			return ERR_PTR(err);
-		}
+		if (err)
+			goto err_out;
 	}
 	return d_splice_alias(inode, dentry);
+
+err_out:
+	iget_failed(inode);
+	return ERR_PTR(err);
 }
 
 static int f2fs_unlink(struct inode *dir, struct dentry *dentry)
@@ -460,6 +479,13 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
 	struct f2fs_dir_entry *new_entry;
 	int err = -ENOENT;
 
+	if ((old_dir != new_dir) && f2fs_encrypted_inode(new_dir) &&
+		!f2fs_is_child_context_consistent_with_parent(new_dir,
+							old_inode)) {
+		err = -EPERM;
+		goto out;
+	}
+
 	f2fs_balance_fs(sbi);
 
 	old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page);
-- 
2.1.1


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

* [PATCH 11/18] f2fs crypto: add encryption support in read/write paths
  2015-05-09  4:20 [PATCH 01/18] f2fs: avoid value overflow in showing current status Jaegeuk Kim
                   ` (8 preceding siblings ...)
  2015-05-09  4:20 ` [PATCH 10/18] f2fs crypto: activate encryption support for fs APIs Jaegeuk Kim
@ 2015-05-09  4:20 ` Jaegeuk Kim
  2015-05-09  4:20 ` [PATCH 12/18] f2fs crypto: add filename encryption for f2fs_add_link Jaegeuk Kim
                   ` (6 subsequent siblings)
  16 siblings, 0 replies; 34+ messages in thread
From: Jaegeuk Kim @ 2015-05-09  4:20 UTC (permalink / raw)
  To: linux-kernel, linux-fsdevel, linux-f2fs-devel; +Cc: Jaegeuk Kim

This patch adds encryption support in read and write paths.

Note that, in f2fs, we need to consider cleaning operation.
In cleaning procedure, we must avoid encrypting and decrypting written blocks.
So, this patch implements move_encrypted_block().

Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---
 fs/f2fs/checkpoint.c |  4 ++-
 fs/f2fs/data.c       | 96 ++++++++++++++++++++++++++++++++++++++++------------
 fs/f2fs/f2fs.h       |  1 +
 fs/f2fs/file.c       |  2 +-
 fs/f2fs/gc.c         | 79 +++++++++++++++++++++++++++++++++++++++++-
 fs/f2fs/inline.c     |  1 +
 fs/f2fs/node.c       |  2 ++
 fs/f2fs/segment.c    | 24 ++++++++++---
 8 files changed, 180 insertions(+), 29 deletions(-)

diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c
index 1da20a6..98c31db 100644
--- a/fs/f2fs/checkpoint.c
+++ b/fs/f2fs/checkpoint.c
@@ -56,6 +56,7 @@ struct page *get_meta_page(struct f2fs_sb_info *sbi, pgoff_t index)
 		.type = META,
 		.rw = READ_SYNC | REQ_META | REQ_PRIO,
 		.blk_addr = index,
+		.encrypted_page = NULL,
 	};
 repeat:
 	page = grab_cache_page(mapping, index);
@@ -122,7 +123,8 @@ int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages, int type
 	struct f2fs_io_info fio = {
 		.sbi = sbi,
 		.type = META,
-		.rw = READ_SYNC | REQ_META | REQ_PRIO
+		.rw = READ_SYNC | REQ_META | REQ_PRIO,
+		.encrypted_page = NULL,
 	};
 
 	for (; nrpages-- > 0; blkno++) {
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 473b4d4..deb6b69 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -57,6 +57,15 @@ static void mpage_end_io(struct bio *bio, int err)
 	struct bio_vec *bv;
 	int i;
 
+	if (f2fs_bio_encrypted(bio)) {
+		if (err) {
+			f2fs_release_crypto_ctx(bio->bi_private);
+		} else {
+			f2fs_end_io_crypto_work(bio->bi_private, bio);
+			return;
+		}
+	}
+
 	bio_for_each_segment_all(bv, bio, i) {
 		struct page *page = bv->bv_page;
 
@@ -81,6 +90,8 @@ static void f2fs_write_end_io(struct bio *bio, int err)
 	bio_for_each_segment_all(bvec, bio, i) {
 		struct page *page = bvec->bv_page;
 
+		f2fs_restore_and_release_control_page(&page);
+
 		if (unlikely(err)) {
 			set_page_dirty(page);
 			set_bit(AS_EIO, &page->mapping->flags);
@@ -161,7 +172,7 @@ void f2fs_submit_merged_bio(struct f2fs_sb_info *sbi,
 int f2fs_submit_page_bio(struct f2fs_io_info *fio)
 {
 	struct bio *bio;
-	struct page *page = fio->page;
+	struct page *page = fio->encrypted_page ? fio->encrypted_page : fio->page;
 
 	trace_f2fs_submit_page_bio(page, fio);
 	f2fs_trace_ios(fio, 0);
@@ -185,6 +196,7 @@ void f2fs_submit_page_mbio(struct f2fs_io_info *fio)
 	enum page_type btype = PAGE_TYPE_OF_BIO(fio->type);
 	struct f2fs_bio_info *io;
 	bool is_read = is_read_io(fio->rw);
+	struct page *bio_page;
 
 	io = is_read ? &sbi->read_io : &sbi->write_io[btype];
 
@@ -206,7 +218,9 @@ alloc_new:
 		io->fio = *fio;
 	}
 
-	if (bio_add_page(io->bio, fio->page, PAGE_CACHE_SIZE, 0) <
+	bio_page = fio->encrypted_page ? fio->encrypted_page : fio->page;
+
+	if (bio_add_page(io->bio, bio_page, PAGE_CACHE_SIZE, 0) <
 							PAGE_CACHE_SIZE) {
 		__submit_merged_bio(io);
 		goto alloc_new;
@@ -928,8 +942,12 @@ struct page *get_read_data_page(struct inode *inode, pgoff_t index, int rw)
 		.sbi = F2FS_I_SB(inode),
 		.type = DATA,
 		.rw = rw,
+		.encrypted_page = NULL,
 	};
 
+	if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode))
+		return read_mapping_page(mapping, index, NULL);
+
 	page = grab_cache_page(mapping, index);
 	if (!page)
 		return ERR_PTR(-ENOMEM);
@@ -1066,26 +1084,14 @@ repeat:
 		zero_user_segment(page, 0, PAGE_CACHE_SIZE);
 		SetPageUptodate(page);
 	} else {
-		struct f2fs_io_info fio = {
-			.sbi = F2FS_I_SB(inode),
-			.type = DATA,
-			.rw = READ_SYNC,
-			.blk_addr = dn.data_blkaddr,
-			.page = page,
-		};
-		err = f2fs_submit_page_bio(&fio);
-		if (err)
-			return ERR_PTR(err);
+		f2fs_put_page(page, 1);
 
-		lock_page(page);
-		if (unlikely(!PageUptodate(page))) {
-			f2fs_put_page(page, 1);
-			return ERR_PTR(-EIO);
-		}
-		if (unlikely(page->mapping != mapping)) {
-			f2fs_put_page(page, 1);
+		page = get_read_data_page(inode, index, READ_SYNC);
+		if (IS_ERR(page))
 			goto repeat;
-		}
+
+		/* wait for read completion */
+		lock_page(page);
 	}
 got_it:
 	if (new_i_size &&
@@ -1548,14 +1554,38 @@ submit_and_realloc:
 			bio = NULL;
 		}
 		if (bio == NULL) {
+			struct f2fs_crypto_ctx *ctx = NULL;
+
+			if (f2fs_encrypted_inode(inode) &&
+					S_ISREG(inode->i_mode)) {
+				struct page *cpage;
+
+				ctx = f2fs_get_crypto_ctx(inode);
+				if (IS_ERR(ctx))
+					goto set_error_page;
+
+				/* wait the page to be moved by cleaning */
+				cpage = find_lock_page(
+						META_MAPPING(F2FS_I_SB(inode)),
+						block_nr);
+				if (cpage) {
+					f2fs_wait_on_page_writeback(cpage,
+									DATA);
+					f2fs_put_page(cpage, 1);
+				}
+			}
+
 			bio = bio_alloc(GFP_KERNEL,
 				min_t(int, nr_pages, bio_get_nr_vecs(bdev)));
-			if (!bio)
+			if (!bio) {
+				if (ctx)
+					f2fs_release_crypto_ctx(ctx);
 				goto set_error_page;
+			}
 			bio->bi_bdev = bdev;
 			bio->bi_iter.bi_sector = SECTOR_FROM_BLOCK(block_nr);
 			bio->bi_end_io = mpage_end_io;
-			bio->bi_private = NULL;
+			bio->bi_private = ctx;
 		}
 
 		if (bio_add_page(bio, page, blocksize, 0) < blocksize)
@@ -1674,6 +1704,7 @@ static int f2fs_write_data_page(struct page *page,
 		.type = DATA,
 		.rw = (wbc->sync_mode == WB_SYNC_ALL) ? WRITE_SYNC : WRITE,
 		.page = page,
+		.encrypted_page = NULL,
 	};
 
 	trace_f2fs_writepage(page, DATA);
@@ -1718,6 +1749,15 @@ write:
 	else if (has_not_enough_free_secs(sbi, 0))
 		goto redirty_out;
 
+	if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) {
+		f2fs_bug_on(sbi, f2fs_has_inline_data(inode));
+		fio.encrypted_page = f2fs_encrypt(inode, page);
+		if (IS_ERR(fio.encrypted_page)) {
+			err = PTR_ERR(fio.encrypted_page);
+			goto out;
+		}
+	}
+
 	err = -EAGAIN;
 	f2fs_lock_op(sbi);
 	if (f2fs_has_inline_data(inode))
@@ -1726,6 +1766,8 @@ write:
 		err = do_write_data_page(&fio);
 	f2fs_unlock_op(sbi);
 done:
+	if (err && fio.encrypted_page)
+		f2fs_restore_control_page(fio.encrypted_page);
 	if (err && err != -ENOENT)
 		goto redirty_out;
 
@@ -1897,6 +1939,7 @@ put_next:
 			.rw = READ_SYNC,
 			.blk_addr = dn.data_blkaddr,
 			.page = page,
+			.encrypted_page = NULL,
 		};
 		err = f2fs_submit_page_bio(&fio);
 		if (err)
@@ -1912,6 +1955,15 @@ put_next:
 			f2fs_put_page(page, 1);
 			goto repeat;
 		}
+
+		/* avoid symlink page */
+		if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) {
+			err = f2fs_decrypt_one(inode, page);
+			if (err) {
+				f2fs_put_page(page, 1);
+				goto fail;
+			}
+		}
 	}
 out:
 	SetPageUptodate(page);
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 544766e..1632151 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -656,6 +656,7 @@ struct f2fs_io_info {
 	int rw;			/* contains R/RS/W/WS with REQ_META/REQ_PRIO */
 	block_t blk_addr;	/* block address to be written */
 	struct page *page;	/* page to be written */
+	struct page *encrypted_page;	/* encrypted page */
 };
 
 #define is_read_io(rw)	(((rw) & 1) == READ)
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 9f4b34c..d7daff8 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -500,7 +500,7 @@ static int truncate_partial_data_page(struct inode *inode, u64 from,
 truncate_out:
 	f2fs_wait_on_page_writeback(page, DATA);
 	zero_user(page, offset, PAGE_CACHE_SIZE - offset);
-	if (!cache_only)
+	if (!cache_only || !f2fs_encrypted_inode(inode) || !S_ISREG(inode->i_mode))
 		set_page_dirty(page);
 	f2fs_put_page(page, 1);
 	return 0;
diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
index 2e2afeb..43354cb 100644
--- a/fs/f2fs/gc.c
+++ b/fs/f2fs/gc.c
@@ -518,6 +518,72 @@ static int check_dnode(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
 	return 1;
 }
 
+static void move_encrypted_block(struct inode *inode, block_t bidx)
+{
+	struct f2fs_io_info fio = {
+		.sbi = F2FS_I_SB(inode),
+		.type = DATA,
+		.rw = READ_SYNC,
+		.encrypted_page = NULL,
+	};
+	struct dnode_of_data dn;
+	struct f2fs_summary sum;
+	struct node_info ni;
+	struct page *page;
+	int err;
+
+	/* do not read out */
+	page = grab_cache_page(inode->i_mapping, bidx);
+	if (!page)
+		return;
+
+	set_new_dnode(&dn, inode, NULL, NULL, 0);
+	err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE);
+	if (err)
+		goto out;
+
+	if (unlikely(dn.data_blkaddr == NULL_ADDR))
+		goto put_out;
+
+	get_node_info(fio.sbi, dn.nid, &ni);
+	set_summary(&sum, dn.nid, dn.ofs_in_node, ni.version);
+
+	/* read page */
+	fio.page = page;
+	fio.blk_addr = dn.data_blkaddr;
+
+	fio.encrypted_page = grab_cache_page(META_MAPPING(fio.sbi), fio.blk_addr);
+	if (!fio.encrypted_page)
+		goto put_out;
+
+	f2fs_submit_page_bio(&fio);
+
+	/* allocate block address */
+	f2fs_wait_on_page_writeback(dn.node_page, NODE);
+
+	allocate_data_block(fio.sbi, NULL, fio.blk_addr,
+					&fio.blk_addr, &sum, CURSEG_COLD_DATA);
+	dn.data_blkaddr = fio.blk_addr;
+
+	/* write page */
+	lock_page(fio.encrypted_page);
+	set_page_writeback(fio.encrypted_page);
+	fio.rw = WRITE_SYNC;
+	f2fs_submit_page_mbio(&fio);
+
+	set_data_blkaddr(&dn);
+	f2fs_update_extent_cache(&dn);
+	set_inode_flag(F2FS_I(inode), FI_APPEND_WRITE);
+	if (page->index == 0)
+		set_inode_flag(F2FS_I(inode), FI_FIRST_BLOCK_WRITTEN);
+
+	f2fs_put_page(fio.encrypted_page, 1);
+put_out:
+	f2fs_put_dnode(&dn);
+out:
+	f2fs_put_page(page, 1);
+}
+
 static void move_data_page(struct inode *inode, block_t bidx, int gc_type)
 {
 	struct page *page;
@@ -537,6 +603,7 @@ static void move_data_page(struct inode *inode, block_t bidx, int gc_type)
 			.type = DATA,
 			.rw = WRITE_SYNC,
 			.page = page,
+			.encrypted_page = NULL,
 		};
 		f2fs_wait_on_page_writeback(page, DATA);
 
@@ -606,6 +673,13 @@ next_step:
 			if (IS_ERR(inode) || is_bad_inode(inode))
 				continue;
 
+			/* if encrypted inode, let's go phase 3 */
+			if (f2fs_encrypted_inode(inode) &&
+						S_ISREG(inode->i_mode)) {
+				add_gc_inode(gc_list, inode);
+				continue;
+			}
+
 			start_bidx = start_bidx_of_node(nofs, F2FS_I(inode));
 			data_page = get_read_data_page(inode,
 					start_bidx + ofs_in_node, READA);
@@ -624,7 +698,10 @@ next_step:
 		if (inode) {
 			start_bidx = start_bidx_of_node(nofs, F2FS_I(inode))
 								+ ofs_in_node;
-			move_data_page(inode, start_bidx, gc_type);
+			if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode))
+				move_encrypted_block(inode, start_bidx);
+			else
+				move_data_page(inode, start_bidx, gc_type);
 			stat_inc_data_blk_count(sbi, 1, gc_type);
 		}
 	}
diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c
index 5f5b34b..b0b7805 100644
--- a/fs/f2fs/inline.c
+++ b/fs/f2fs/inline.c
@@ -113,6 +113,7 @@ int f2fs_convert_inline_page(struct dnode_of_data *dn, struct page *page)
 		.type = DATA,
 		.rw = WRITE_SYNC | REQ_PRIO,
 		.page = page,
+		.encrypted_page = NULL,
 	};
 	int dirty, err;
 
diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c
index 7717256..d9c5242 100644
--- a/fs/f2fs/node.c
+++ b/fs/f2fs/node.c
@@ -1003,6 +1003,7 @@ static int read_node_page(struct page *page, int rw)
 		.type = NODE,
 		.rw = rw,
 		.page = page,
+		.encrypted_page = NULL,
 	};
 
 	get_node_info(sbi, page->index, &ni);
@@ -1299,6 +1300,7 @@ static int f2fs_write_node_page(struct page *page,
 		.type = NODE,
 		.rw = (wbc->sync_mode == WB_SYNC_ALL) ? WRITE_SYNC : WRITE,
 		.page = page,
+		.encrypted_page = NULL,
 	};
 
 	trace_f2fs_writepage(page, NODE);
diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
index bdf487a..4c38601 100644
--- a/fs/f2fs/segment.c
+++ b/fs/f2fs/segment.c
@@ -219,6 +219,7 @@ void commit_inmem_pages(struct inode *inode, bool abort)
 		.sbi = sbi,
 		.type = DATA,
 		.rw = WRITE_SYNC | REQ_PRIO,
+		.encrypted_page = NULL,
 	};
 
 	/*
@@ -1246,6 +1247,7 @@ void write_meta_page(struct f2fs_sb_info *sbi, struct page *page)
 		.rw = WRITE_SYNC | REQ_META | REQ_PRIO,
 		.blk_addr = page->index,
 		.page = page,
+		.encrypted_page = NULL,
 	};
 
 	set_page_writeback(page);
@@ -1345,20 +1347,34 @@ static inline bool is_merged_page(struct f2fs_sb_info *sbi,
 	enum page_type btype = PAGE_TYPE_OF_BIO(type);
 	struct f2fs_bio_info *io = &sbi->write_io[btype];
 	struct bio_vec *bvec;
+	struct page *target;
 	int i;
 
 	down_read(&io->io_rwsem);
-	if (!io->bio)
-		goto out;
+	if (!io->bio) {
+		up_read(&io->io_rwsem);
+		return false;
+	}
 
 	bio_for_each_segment_all(bvec, io->bio, i) {
-		if (page == bvec->bv_page) {
+
+		if (bvec->bv_page->mapping) {
+			target = bvec->bv_page;
+		} else {
+			struct f2fs_crypto_ctx *ctx;
+
+			/* encrypted page */
+			ctx = (struct f2fs_crypto_ctx *)page_private(
+								bvec->bv_page);
+			target = ctx->control_page;
+		}
+
+		if (page == target) {
 			up_read(&io->io_rwsem);
 			return true;
 		}
 	}
 
-out:
 	up_read(&io->io_rwsem);
 	return false;
 }
-- 
2.1.1


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

* [PATCH 12/18] f2fs crypto: add filename encryption for f2fs_add_link
  2015-05-09  4:20 [PATCH 01/18] f2fs: avoid value overflow in showing current status Jaegeuk Kim
                   ` (9 preceding siblings ...)
  2015-05-09  4:20 ` [PATCH 11/18] f2fs crypto: add encryption support in read/write paths Jaegeuk Kim
@ 2015-05-09  4:20 ` Jaegeuk Kim
  2015-05-09  4:20 ` [PATCH 13/18] f2fs crypto: add filename encryption for f2fs_readdir Jaegeuk Kim
                   ` (5 subsequent siblings)
  16 siblings, 0 replies; 34+ messages in thread
From: Jaegeuk Kim @ 2015-05-09  4:20 UTC (permalink / raw)
  To: linux-kernel, linux-fsdevel, linux-f2fs-devel; +Cc: Jaegeuk Kim

This patch adds filename encryption support for f2fs_add_link.

Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---
 fs/f2fs/dir.c | 39 +++++++++++++++++++++++++++------------
 1 file changed, 27 insertions(+), 12 deletions(-)

diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c
index f7293a2..750a688 100644
--- a/fs/f2fs/dir.c
+++ b/fs/f2fs/dir.c
@@ -507,24 +507,33 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name,
 	unsigned long bidx, block;
 	f2fs_hash_t dentry_hash;
 	unsigned int nbucket, nblock;
-	size_t namelen = name->len;
 	struct page *dentry_page = NULL;
 	struct f2fs_dentry_block *dentry_blk = NULL;
 	struct f2fs_dentry_ptr d;
-	int slots = GET_DENTRY_SLOTS(namelen);
 	struct page *page = NULL;
-	int err = 0;
+	struct f2fs_filename fname;
+	struct qstr new_name;
+	int slots, err;
+
+	err = f2fs_fname_setup_filename(dir, name, 0, &fname);
+	if (err)
+		return err;
+
+	new_name.name = fname_name(&fname);
+	new_name.len = fname_len(&fname);
 
 	if (f2fs_has_inline_dentry(dir)) {
-		err = f2fs_add_inline_entry(dir, name, inode, ino, mode);
+		err = f2fs_add_inline_entry(dir, &new_name, inode, ino, mode);
 		if (!err || err != -EAGAIN)
-			return err;
+			goto out;
 		else
 			err = 0;
 	}
 
-	dentry_hash = f2fs_dentry_hash(name);
 	level = 0;
+	slots = GET_DENTRY_SLOTS(new_name.len);
+	dentry_hash = f2fs_dentry_hash(&new_name);
+
 	current_depth = F2FS_I(dir)->i_current_depth;
 	if (F2FS_I(dir)->chash == dentry_hash) {
 		level = F2FS_I(dir)->clevel;
@@ -532,8 +541,10 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name,
 	}
 
 start:
-	if (unlikely(current_depth == MAX_DIR_HASH_DEPTH))
-		return -ENOSPC;
+	if (unlikely(current_depth == MAX_DIR_HASH_DEPTH)) {
+		err = -ENOSPC;
+		goto out;
+	}
 
 	/* Increase the depth, if required */
 	if (level == current_depth)
@@ -547,8 +558,10 @@ start:
 
 	for (block = bidx; block <= (bidx + nblock - 1); block++) {
 		dentry_page = get_new_data_page(dir, NULL, block, true);
-		if (IS_ERR(dentry_page))
-			return PTR_ERR(dentry_page);
+		if (IS_ERR(dentry_page)) {
+			err = PTR_ERR(dentry_page);
+			goto out;
+		}
 
 		dentry_blk = kmap(dentry_page);
 		bit_pos = room_for_filename(&dentry_blk->dentry_bitmap,
@@ -568,7 +581,7 @@ add_dentry:
 
 	if (inode) {
 		down_write(&F2FS_I(inode)->i_sem);
-		page = init_inode_metadata(inode, dir, name, NULL);
+		page = init_inode_metadata(inode, dir, &new_name, NULL);
 		if (IS_ERR(page)) {
 			err = PTR_ERR(page);
 			goto fail;
@@ -576,7 +589,7 @@ add_dentry:
 	}
 
 	make_dentry_ptr(&d, (void *)dentry_blk, 1);
-	f2fs_update_dentry(ino, mode, &d, name, dentry_hash, bit_pos);
+	f2fs_update_dentry(ino, mode, &d, &new_name, dentry_hash, bit_pos);
 
 	set_page_dirty(dentry_page);
 
@@ -598,6 +611,8 @@ fail:
 	}
 	kunmap(dentry_page);
 	f2fs_put_page(dentry_page, 1);
+out:
+	f2fs_fname_free_filename(&fname);
 	return err;
 }
 
-- 
2.1.1


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

* [PATCH 13/18] f2fs crypto: add filename encryption for f2fs_readdir
  2015-05-09  4:20 [PATCH 01/18] f2fs: avoid value overflow in showing current status Jaegeuk Kim
                   ` (10 preceding siblings ...)
  2015-05-09  4:20 ` [PATCH 12/18] f2fs crypto: add filename encryption for f2fs_add_link Jaegeuk Kim
@ 2015-05-09  4:20 ` Jaegeuk Kim
  2015-05-09  4:20 ` [PATCH 14/18] f2fs crypto: add filename encryption for f2fs_lookup Jaegeuk Kim
                   ` (4 subsequent siblings)
  16 siblings, 0 replies; 34+ messages in thread
From: Jaegeuk Kim @ 2015-05-09  4:20 UTC (permalink / raw)
  To: linux-kernel, linux-fsdevel, linux-f2fs-devel; +Cc: Jaegeuk Kim

This patch implements filename encryption support for f2fs_readdir.

Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---
 fs/f2fs/dir.c    | 57 ++++++++++++++++++++++++++++++++++++++++++++------------
 fs/f2fs/f2fs.h   | 12 ++++++++----
 fs/f2fs/inline.c | 13 +++++++------
 3 files changed, 60 insertions(+), 22 deletions(-)

diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c
index 750a688..ab6455d 100644
--- a/fs/f2fs/dir.c
+++ b/fs/f2fs/dir.c
@@ -98,7 +98,7 @@ static struct f2fs_dir_entry *find_in_block(struct page *dentry_page,
 
 	dentry_blk = (struct f2fs_dentry_block *)kmap(dentry_page);
 
-	make_dentry_ptr(&d, (void *)dentry_blk, 1);
+	make_dentry_ptr(NULL, &d, (void *)dentry_blk, 1);
 	de = find_target_dentry(name, max_slots, &d);
 
 	if (de)
@@ -356,7 +356,7 @@ static int make_empty_dir(struct inode *inode,
 
 	dentry_blk = kmap_atomic(dentry_page);
 
-	make_dentry_ptr(&d, (void *)dentry_blk, 1);
+	make_dentry_ptr(NULL, &d, (void *)dentry_blk, 1);
 	do_make_empty_dir(inode, parent, &d);
 
 	kunmap_atomic(dentry_blk);
@@ -588,7 +588,7 @@ add_dentry:
 		}
 	}
 
-	make_dentry_ptr(&d, (void *)dentry_blk, 1);
+	make_dentry_ptr(NULL, &d, (void *)dentry_blk, 1);
 	f2fs_update_dentry(ino, mode, &d, &new_name, dentry_hash, bit_pos);
 
 	set_page_dirty(dentry_page);
@@ -750,11 +750,12 @@ bool f2fs_empty_dir(struct inode *dir)
 }
 
 bool f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d,
-						unsigned int start_pos)
+				unsigned int start_pos, struct f2fs_str *fstr)
 {
 	unsigned char d_type = DT_UNKNOWN;
 	unsigned int bit_pos;
 	struct f2fs_dir_entry *de = NULL;
+	struct f2fs_str de_name = FSTR_INIT(NULL, 0);
 
 	bit_pos = ((unsigned long)ctx->pos % d->max);
 
@@ -768,8 +769,24 @@ bool f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d,
 			d_type = f2fs_filetype_table[de->file_type];
 		else
 			d_type = DT_UNKNOWN;
-		if (!dir_emit(ctx, d->filename[bit_pos],
-					le16_to_cpu(de->name_len),
+
+		/* encrypted case */
+		de_name.name = d->filename[bit_pos];
+		de_name.len = le16_to_cpu(de->name_len);
+
+		if (f2fs_encrypted_inode(d->inode)) {
+			int save_len = fstr->len;
+			int ret;
+
+			ret = f2fs_fname_disk_to_usr(d->inode, &de->hash_code,
+							&de_name, fstr);
+			de_name = *fstr;
+			fstr->len = save_len;
+			if (ret < 0)
+				return true;
+		}
+
+		if (!dir_emit(ctx, de_name.name, de_name.len,
 					le32_to_cpu(de->ino), d_type))
 			return true;
 
@@ -788,9 +805,24 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx)
 	struct file_ra_state *ra = &file->f_ra;
 	unsigned int n = ((unsigned long)ctx->pos / NR_DENTRY_IN_BLOCK);
 	struct f2fs_dentry_ptr d;
+	struct f2fs_str fstr = FSTR_INIT(NULL, 0);
+	int err = 0;
 
-	if (f2fs_has_inline_dentry(inode))
-		return f2fs_read_inline_dir(file, ctx);
+	err = f2fs_setup_fname_crypto(inode);
+	if (err)
+		return err;
+
+	if (f2fs_encrypted_inode(inode)) {
+		err = f2fs_fname_crypto_alloc_buffer(inode, F2FS_NAME_LEN,
+								&fstr);
+		if (err < 0)
+			return err;
+	}
+
+	if (f2fs_has_inline_dentry(inode)) {
+		err = f2fs_read_inline_dir(file, ctx, &fstr);
+		goto out;
+	}
 
 	/* readahead for multi pages of dir */
 	if (npages - n > 1 && !ra_has_index(ra, n))
@@ -804,9 +836,9 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx)
 
 		dentry_blk = kmap(dentry_page);
 
-		make_dentry_ptr(&d, (void *)dentry_blk, 1);
+		make_dentry_ptr(inode, &d, (void *)dentry_blk, 1);
 
-		if (f2fs_fill_dentries(ctx, &d, n * NR_DENTRY_IN_BLOCK))
+		if (f2fs_fill_dentries(ctx, &d, n * NR_DENTRY_IN_BLOCK, &fstr))
 			goto stop;
 
 		ctx->pos = (n + 1) * NR_DENTRY_IN_BLOCK;
@@ -819,8 +851,9 @@ stop:
 		kunmap(dentry_page);
 		f2fs_put_page(dentry_page, 1);
 	}
-
-	return 0;
+out:
+	f2fs_fname_crypto_free_buffer(&fstr);
+	return err;
 }
 
 const struct file_operations f2fs_dir_operations = {
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 1632151..963616f 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -277,15 +277,18 @@ struct f2fs_filename {
 #define fname_len(p)		((p)->disk_name.len)
 
 struct f2fs_dentry_ptr {
+	struct inode *inode;
 	const void *bitmap;
 	struct f2fs_dir_entry *dentry;
 	__u8 (*filename)[F2FS_SLOT_LEN];
 	int max;
 };
 
-static inline void make_dentry_ptr(struct f2fs_dentry_ptr *d,
-					void *src, int type)
+static inline void make_dentry_ptr(struct inode *inode,
+		struct f2fs_dentry_ptr *d, void *src, int type)
 {
+	d->inode = inode;
+
 	if (type == 1) {
 		struct f2fs_dentry_block *t = (struct f2fs_dentry_block *)src;
 		d->max = NR_DENTRY_IN_BLOCK;
@@ -1584,7 +1587,7 @@ void set_de_type(struct f2fs_dir_entry *, umode_t);
 struct f2fs_dir_entry *find_target_dentry(struct qstr *, int *,
 			struct f2fs_dentry_ptr *);
 bool f2fs_fill_dentries(struct dir_context *, struct f2fs_dentry_ptr *,
-			unsigned int);
+			unsigned int, struct f2fs_str *);
 void do_make_empty_dir(struct inode *, struct inode *,
 			struct f2fs_dentry_ptr *);
 struct page *init_inode_metadata(struct inode *, struct inode *,
@@ -1937,7 +1940,8 @@ int f2fs_add_inline_entry(struct inode *, const struct qstr *, struct inode *,
 void f2fs_delete_inline_entry(struct f2fs_dir_entry *, struct page *,
 						struct inode *, struct inode *);
 bool f2fs_empty_inline_dir(struct inode *);
-int f2fs_read_inline_dir(struct file *, struct dir_context *);
+int f2fs_read_inline_dir(struct file *, struct dir_context *,
+						struct f2fs_str *);
 
 /*
  * crypto support
diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c
index b0b7805..dd51d3f 100644
--- a/fs/f2fs/inline.c
+++ b/fs/f2fs/inline.c
@@ -298,7 +298,7 @@ struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir,
 
 	inline_dentry = inline_data_addr(ipage);
 
-	make_dentry_ptr(&d, (void *)inline_dentry, 2);
+	make_dentry_ptr(NULL, &d, (void *)inline_dentry, 2);
 	de = find_target_dentry(name, NULL, &d);
 
 	unlock_page(ipage);
@@ -342,7 +342,7 @@ int make_empty_inline_dir(struct inode *inode, struct inode *parent,
 
 	dentry_blk = inline_data_addr(ipage);
 
-	make_dentry_ptr(&d, (void *)dentry_blk, 2);
+	make_dentry_ptr(NULL, &d, (void *)dentry_blk, 2);
 	do_make_empty_dir(inode, parent, &d);
 
 	set_page_dirty(ipage);
@@ -446,7 +446,7 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *name,
 	f2fs_wait_on_page_writeback(ipage, NODE);
 
 	name_hash = f2fs_dentry_hash(name);
-	make_dentry_ptr(&d, (void *)dentry_blk, 2);
+	make_dentry_ptr(NULL, &d, (void *)dentry_blk, 2);
 	f2fs_update_dentry(ino, mode, &d, name, name_hash, bit_pos);
 
 	set_page_dirty(ipage);
@@ -523,7 +523,8 @@ bool f2fs_empty_inline_dir(struct inode *dir)
 	return true;
 }
 
-int f2fs_read_inline_dir(struct file *file, struct dir_context *ctx)
+int f2fs_read_inline_dir(struct file *file, struct dir_context *ctx,
+				struct f2fs_str *fstr)
 {
 	struct inode *inode = file_inode(file);
 	struct f2fs_inline_dentry *inline_dentry = NULL;
@@ -539,9 +540,9 @@ int f2fs_read_inline_dir(struct file *file, struct dir_context *ctx)
 
 	inline_dentry = inline_data_addr(ipage);
 
-	make_dentry_ptr(&d, (void *)inline_dentry, 2);
+	make_dentry_ptr(inode, &d, (void *)inline_dentry, 2);
 
-	if (!f2fs_fill_dentries(ctx, &d, 0))
+	if (!f2fs_fill_dentries(ctx, &d, 0, fstr))
 		ctx->pos = NR_INLINE_DENTRY;
 
 	f2fs_put_page(ipage, 1);
-- 
2.1.1


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

* [PATCH 14/18] f2fs crypto: add filename encryption for f2fs_lookup
  2015-05-09  4:20 [PATCH 01/18] f2fs: avoid value overflow in showing current status Jaegeuk Kim
                   ` (11 preceding siblings ...)
  2015-05-09  4:20 ` [PATCH 13/18] f2fs crypto: add filename encryption for f2fs_readdir Jaegeuk Kim
@ 2015-05-09  4:20 ` Jaegeuk Kim
  2015-05-11  2:52   ` [f2fs-dev] " hujianyang
  2015-05-09  4:20 ` [PATCH 15/18] f2fs crypto: add filename encryption for roll-forward recovery Jaegeuk Kim
                   ` (3 subsequent siblings)
  16 siblings, 1 reply; 34+ messages in thread
From: Jaegeuk Kim @ 2015-05-09  4:20 UTC (permalink / raw)
  To: linux-kernel, linux-fsdevel, linux-f2fs-devel; +Cc: Jaegeuk Kim

This patch implements filename encryption support for f2fs_lookup.

Note that, f2fs_find_entry should be outside of f2fs_(un)lock_op().

Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---
 fs/f2fs/dir.c    | 79 ++++++++++++++++++++++++++++++++------------------------
 fs/f2fs/f2fs.h   |  9 ++++---
 fs/f2fs/inline.c |  9 ++++---
 3 files changed, 56 insertions(+), 41 deletions(-)

diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c
index ab6455d..5e10d9d 100644
--- a/fs/f2fs/dir.c
+++ b/fs/f2fs/dir.c
@@ -76,20 +76,10 @@ static unsigned long dir_block_index(unsigned int level,
 	return bidx;
 }
 
-static bool early_match_name(size_t namelen, f2fs_hash_t namehash,
-				struct f2fs_dir_entry *de)
-{
-	if (le16_to_cpu(de->name_len) != namelen)
-		return false;
-
-	if (de->hash_code != namehash)
-		return false;
-
-	return true;
-}
-
 static struct f2fs_dir_entry *find_in_block(struct page *dentry_page,
-				struct qstr *name, int *max_slots,
+				struct f2fs_filename *fname,
+				f2fs_hash_t namehash,
+				int *max_slots,
 				struct page **res_page)
 {
 	struct f2fs_dentry_block *dentry_blk;
@@ -99,8 +89,7 @@ static struct f2fs_dir_entry *find_in_block(struct page *dentry_page,
 	dentry_blk = (struct f2fs_dentry_block *)kmap(dentry_page);
 
 	make_dentry_ptr(NULL, &d, (void *)dentry_blk, 1);
-	de = find_target_dentry(name, max_slots, &d);
-
+	de = find_target_dentry(fname, namehash, max_slots, &d);
 	if (de)
 		*res_page = dentry_page;
 	else
@@ -114,13 +103,15 @@ static struct f2fs_dir_entry *find_in_block(struct page *dentry_page,
 	return de;
 }
 
-struct f2fs_dir_entry *find_target_dentry(struct qstr *name, int *max_slots,
-						struct f2fs_dentry_ptr *d)
+struct f2fs_dir_entry *find_target_dentry(struct f2fs_filename *fname,
+			f2fs_hash_t namehash, int *max_slots,
+			struct f2fs_dentry_ptr *d)
 {
 	struct f2fs_dir_entry *de;
 	unsigned long bit_pos = 0;
-	f2fs_hash_t namehash = f2fs_dentry_hash(name);
 	int max_len = 0;
+	struct f2fs_str de_name = FSTR_INIT(NULL, 0);
+	struct f2fs_str *name = &fname->disk_name;
 
 	if (max_slots)
 		*max_slots = 0;
@@ -132,8 +123,18 @@ struct f2fs_dir_entry *find_target_dentry(struct qstr *name, int *max_slots,
 		}
 
 		de = &d->dentry[bit_pos];
-		if (early_match_name(name->len, namehash, de) &&
-			!memcmp(d->filename[bit_pos], name->name, name->len))
+
+		/* encrypted case */
+		de_name.name = d->filename[bit_pos];
+		de_name.len = le16_to_cpu(de->name_len);
+
+		/* show encrypted name */
+		if (fname->hash) {
+			if (de->hash_code == fname->hash)
+				goto found;
+		} else if (de_name.len == name->len &&
+			de->hash_code == namehash &&
+			!memcmp(de_name.name, name->name, name->len))
 			goto found;
 
 		if (max_slots && max_len > *max_slots)
@@ -155,16 +156,21 @@ found:
 }
 
 static struct f2fs_dir_entry *find_in_level(struct inode *dir,
-			unsigned int level, struct qstr *name,
-			f2fs_hash_t namehash, struct page **res_page)
+					unsigned int level,
+					struct f2fs_filename *fname,
+					struct page **res_page)
 {
-	int s = GET_DENTRY_SLOTS(name->len);
+	struct qstr name = FSTR_TO_QSTR(&fname->disk_name);
+	int s = GET_DENTRY_SLOTS(name.len);
 	unsigned int nbucket, nblock;
 	unsigned int bidx, end_block;
 	struct page *dentry_page;
 	struct f2fs_dir_entry *de = NULL;
 	bool room = false;
 	int max_slots;
+	f2fs_hash_t namehash;
+
+	namehash = f2fs_dentry_hash(&name);
 
 	f2fs_bug_on(F2FS_I_SB(dir), level > MAX_DIR_HASH_DEPTH);
 
@@ -183,7 +189,8 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir,
 			continue;
 		}
 
-		de = find_in_block(dentry_page, name, &max_slots, res_page);
+		de = find_in_block(dentry_page, fname, namehash, &max_slots,
+								res_page);
 		if (de)
 			break;
 
@@ -211,30 +218,34 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir,
 {
 	unsigned long npages = dir_blocks(dir);
 	struct f2fs_dir_entry *de = NULL;
-	f2fs_hash_t name_hash;
 	unsigned int max_depth;
 	unsigned int level;
+	struct f2fs_filename fname;
+	int err;
 
 	*res_page = NULL;
 
-	if (f2fs_has_inline_dentry(dir))
-		return find_in_inline_dir(dir, child, res_page);
+	err = f2fs_fname_setup_filename(dir, child, 1, &fname);
+	if (err)
+		return NULL;
+
+	if (f2fs_has_inline_dentry(dir)) {
+		de = find_in_inline_dir(dir, &fname, res_page);
+		goto out;
+	}
 
 	if (npages == 0)
-		return NULL;
+		goto out;
 
-	name_hash = f2fs_dentry_hash(child);
 	max_depth = F2FS_I(dir)->i_current_depth;
 
 	for (level = 0; level < max_depth; level++) {
-		de = find_in_level(dir, level, child, name_hash, res_page);
+		de = find_in_level(dir, level, &fname, res_page);
 		if (de)
 			break;
 	}
-	if (!de && F2FS_I(dir)->chash != name_hash) {
-		F2FS_I(dir)->chash = name_hash;
-		F2FS_I(dir)->clevel = level - 1;
-	}
+out:
+	f2fs_fname_free_filename(&fname);
 	return de;
 }
 
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 963616f..6898331 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -1584,8 +1584,9 @@ struct dentry *f2fs_get_parent(struct dentry *child);
  */
 extern unsigned char f2fs_filetype_table[F2FS_FT_MAX];
 void set_de_type(struct f2fs_dir_entry *, umode_t);
-struct f2fs_dir_entry *find_target_dentry(struct qstr *, int *,
-			struct f2fs_dentry_ptr *);
+
+struct f2fs_dir_entry *find_target_dentry(struct f2fs_filename *,
+			f2fs_hash_t, int *, struct f2fs_dentry_ptr *);
 bool f2fs_fill_dentries(struct dir_context *, struct f2fs_dentry_ptr *,
 			unsigned int, struct f2fs_str *);
 void do_make_empty_dir(struct inode *, struct inode *,
@@ -1931,8 +1932,8 @@ int f2fs_convert_inline_page(struct dnode_of_data *, struct page *);
 int f2fs_convert_inline_inode(struct inode *);
 int f2fs_write_inline_data(struct inode *, struct page *);
 bool recover_inline_data(struct inode *, struct page *);
-struct f2fs_dir_entry *find_in_inline_dir(struct inode *, struct qstr *,
-							struct page **);
+struct f2fs_dir_entry *find_in_inline_dir(struct inode *,
+				struct f2fs_filename *, struct page **);
 struct f2fs_dir_entry *f2fs_parent_inline_dir(struct inode *, struct page **);
 int make_empty_inline_dir(struct inode *inode, struct inode *, struct page *);
 int f2fs_add_inline_entry(struct inode *, const struct qstr *, struct inode *,
diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c
index dd51d3f..38e75fb 100644
--- a/fs/f2fs/inline.c
+++ b/fs/f2fs/inline.c
@@ -284,23 +284,26 @@ process_inline:
 }
 
 struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir,
-				struct qstr *name, struct page **res_page)
+			struct f2fs_filename *fname, struct page **res_page)
 {
 	struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb);
 	struct f2fs_inline_dentry *inline_dentry;
+	struct qstr name = FSTR_TO_QSTR(&fname->disk_name);
 	struct f2fs_dir_entry *de;
 	struct f2fs_dentry_ptr d;
 	struct page *ipage;
+	f2fs_hash_t namehash;
 
 	ipage = get_node_page(sbi, dir->i_ino);
 	if (IS_ERR(ipage))
 		return NULL;
 
+	namehash = f2fs_dentry_hash(&name);
+
 	inline_dentry = inline_data_addr(ipage);
 
 	make_dentry_ptr(NULL, &d, (void *)inline_dentry, 2);
-	de = find_target_dentry(name, NULL, &d);
-
+	de = find_target_dentry(fname, namehash, NULL, &d);
 	unlock_page(ipage);
 	if (de)
 		*res_page = ipage;
-- 
2.1.1


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

* [PATCH 15/18] f2fs crypto: add filename encryption for roll-forward recovery
  2015-05-09  4:20 [PATCH 01/18] f2fs: avoid value overflow in showing current status Jaegeuk Kim
                   ` (12 preceding siblings ...)
  2015-05-09  4:20 ` [PATCH 14/18] f2fs crypto: add filename encryption for f2fs_lookup Jaegeuk Kim
@ 2015-05-09  4:20 ` Jaegeuk Kim
  2015-05-09  4:20 ` [PATCH 16/18] f2fs crypto: add symlink encryption Jaegeuk Kim
                   ` (2 subsequent siblings)
  16 siblings, 0 replies; 34+ messages in thread
From: Jaegeuk Kim @ 2015-05-09  4:20 UTC (permalink / raw)
  To: linux-kernel, linux-fsdevel, linux-f2fs-devel; +Cc: Jaegeuk Kim

This patch adds a bit flag to indicate whether or not i_name in the inode
is encrypted.

If this name is encrypted, we can't do recover_dentry during roll-forward.
So, f2fs_sync_file() needs to do checkpoint, if this will be needed in future.

Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---
 fs/f2fs/dir.c      |  8 +++++++-
 fs/f2fs/f2fs.h     |  5 ++++-
 fs/f2fs/file.c     |  4 +++-
 fs/f2fs/namei.c    | 20 +++++++++++++++-----
 fs/f2fs/recovery.c | 13 ++++++++++++-
 5 files changed, 41 insertions(+), 9 deletions(-)

diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c
index 5e10d9d..12f6869 100644
--- a/fs/f2fs/dir.c
+++ b/fs/f2fs/dir.c
@@ -314,10 +314,14 @@ static void init_dent_inode(const struct qstr *name, struct page *ipage)
 	set_page_dirty(ipage);
 }
 
-int update_dent_inode(struct inode *inode, const struct qstr *name)
+int update_dent_inode(struct inode *inode, struct inode *to,
+					const struct qstr *name)
 {
 	struct page *page;
 
+	if (file_enc_name(to))
+		return 0;
+
 	page = get_node_page(F2FS_I_SB(inode), inode->i_ino);
 	if (IS_ERR(page))
 		return PTR_ERR(page);
@@ -597,6 +601,8 @@ add_dentry:
 			err = PTR_ERR(page);
 			goto fail;
 		}
+		if (f2fs_encrypted_inode(dir))
+			file_set_enc_name(inode);
 	}
 
 	make_dentry_ptr(NULL, &d, (void *)dentry_blk, 1);
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 6898331..fda040b 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -378,6 +378,7 @@ struct f2fs_map_blocks {
 #define FADVISE_COLD_BIT	0x01
 #define FADVISE_LOST_PINO_BIT	0x02
 #define FADVISE_ENCRYPT_BIT	0x04
+#define FADVISE_ENC_NAME_BIT	0x08
 
 #define file_is_cold(inode)	is_file(inode, FADVISE_COLD_BIT)
 #define file_wrong_pino(inode)	is_file(inode, FADVISE_LOST_PINO_BIT)
@@ -388,6 +389,8 @@ struct f2fs_map_blocks {
 #define file_is_encrypt(inode)	is_file(inode, FADVISE_ENCRYPT_BIT)
 #define file_set_encrypt(inode)	set_file(inode, FADVISE_ENCRYPT_BIT)
 #define file_clear_encrypt(inode) clear_file(inode, FADVISE_ENCRYPT_BIT)
+#define file_enc_name(inode)	is_file(inode, FADVISE_ENC_NAME_BIT)
+#define file_set_enc_name(inode) set_file(inode, FADVISE_ENC_NAME_BIT)
 
 /* Encryption algorithms */
 #define F2FS_ENCRYPTION_MODE_INVALID		0
@@ -1602,7 +1605,7 @@ struct f2fs_dir_entry *f2fs_parent_dir(struct inode *, struct page **);
 ino_t f2fs_inode_by_name(struct inode *, struct qstr *);
 void f2fs_set_link(struct inode *, struct f2fs_dir_entry *,
 				struct page *, struct inode *);
-int update_dent_inode(struct inode *, const struct qstr *);
+int update_dent_inode(struct inode *, struct inode *, const struct qstr *);
 void f2fs_update_dentry(nid_t ino, umode_t mode, struct f2fs_dentry_ptr *,
 			const struct qstr *, f2fs_hash_t , unsigned int);
 int __f2fs_add_link(struct inode *, const struct qstr *, struct inode *, nid_t,
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index d7daff8..14eb4f7 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -106,7 +106,7 @@ static int get_parent_ino(struct inode *inode, nid_t *pino)
 	if (!dentry)
 		return 0;
 
-	if (update_dent_inode(inode, &dentry->d_name)) {
+	if (update_dent_inode(inode, inode, &dentry->d_name)) {
 		dput(dentry);
 		return 0;
 	}
@@ -123,6 +123,8 @@ static inline bool need_do_checkpoint(struct inode *inode)
 
 	if (!S_ISREG(inode->i_mode) || inode->i_nlink != 1)
 		need_cp = true;
+	else if (file_enc_name(inode) && need_dentry_mark(sbi, inode->i_ino))
+		need_cp = true;
 	else if (file_wrong_pino(inode))
 		need_cp = true;
 	else if (!space_for_roll_forward(sbi))
diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c
index bc8992e..c857f82 100644
--- a/fs/f2fs/namei.c
+++ b/fs/f2fs/namei.c
@@ -517,7 +517,8 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
 		if (err)
 			goto put_out_dir;
 
-		if (update_dent_inode(old_inode, &new_dentry->d_name)) {
+		if (update_dent_inode(old_inode, new_inode,
+						&new_dentry->d_name)) {
 			release_orphan_inode(sbi);
 			goto put_out_dir;
 		}
@@ -557,6 +558,8 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
 
 	down_write(&F2FS_I(old_inode)->i_sem);
 	file_lost_pino(old_inode);
+	if (new_inode && file_enc_name(new_inode))
+		file_set_enc_name(old_inode);
 	up_write(&F2FS_I(old_inode)->i_sem);
 
 	old_inode->i_ctime = CURRENT_TIME;
@@ -659,13 +662,17 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
 
 	f2fs_lock_op(sbi);
 
-	err = update_dent_inode(old_inode, &new_dentry->d_name);
+	err = update_dent_inode(old_inode, new_inode, &new_dentry->d_name);
 	if (err)
 		goto out_unlock;
+	if (file_enc_name(new_inode))
+		file_set_enc_name(old_inode);
 
-	err = update_dent_inode(new_inode, &old_dentry->d_name);
+	err = update_dent_inode(new_inode, old_inode, &old_dentry->d_name);
 	if (err)
 		goto out_undo;
+	if (file_enc_name(old_inode))
+		file_set_enc_name(new_inode);
 
 	/* update ".." directory entry info of old dentry */
 	if (old_dir_entry)
@@ -723,8 +730,11 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
 		f2fs_sync_fs(sbi->sb, 1);
 	return 0;
 out_undo:
-	/* Still we may fail to recover name info of f2fs_inode here */
-	update_dent_inode(old_inode, &old_dentry->d_name);
+	/*
+	 * Still we may fail to recover name info of f2fs_inode here
+	 * Drop it, once its name is set as encrypted
+	 */
+	update_dent_inode(old_inode, old_inode, &old_dentry->d_name);
 out_unlock:
 	f2fs_unlock_op(sbi);
 out_new_dir:
diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c
index f77a1be..9de2587 100644
--- a/fs/f2fs/recovery.c
+++ b/fs/f2fs/recovery.c
@@ -83,6 +83,11 @@ static int recover_dentry(struct inode *inode, struct page *ipage)
 		goto out;
 	}
 
+	if (file_enc_name(inode)) {
+		iput(dir);
+		return 0;
+	}
+
 	name.len = le32_to_cpu(raw_inode->i_namelen);
 	name.name = raw_inode->i_name;
 
@@ -143,6 +148,7 @@ out:
 static void recover_inode(struct inode *inode, struct page *page)
 {
 	struct f2fs_inode *raw = F2FS_INODE(page);
+	char *name;
 
 	inode->i_mode = le16_to_cpu(raw->i_mode);
 	i_size_write(inode, le64_to_cpu(raw->i_size));
@@ -153,8 +159,13 @@ static void recover_inode(struct inode *inode, struct page *page)
 	inode->i_ctime.tv_nsec = le32_to_cpu(raw->i_ctime_nsec);
 	inode->i_mtime.tv_nsec = le32_to_cpu(raw->i_mtime_nsec);
 
+	if (file_enc_name(inode))
+		name = "<encrypted>";
+	else
+		name = F2FS_INODE(page)->i_name;
+
 	f2fs_msg(inode->i_sb, KERN_NOTICE, "recover_inode: ino = %x, name = %s",
-			ino_of_node(page), F2FS_INODE(page)->i_name);
+			ino_of_node(page), name);
 }
 
 static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
-- 
2.1.1


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

* [PATCH 16/18] f2fs crypto: add symlink encryption
  2015-05-09  4:20 [PATCH 01/18] f2fs: avoid value overflow in showing current status Jaegeuk Kim
                   ` (13 preceding siblings ...)
  2015-05-09  4:20 ` [PATCH 15/18] f2fs crypto: add filename encryption for roll-forward recovery Jaegeuk Kim
@ 2015-05-09  4:20 ` Jaegeuk Kim
  2015-05-09  4:25   ` Al Viro
  2015-05-12  3:48   ` [PATCH 16/18 v2] " Jaegeuk Kim
  2015-05-09  4:20 ` [PATCH 17/18] f2fs crypto: fix missing key when reading a page Jaegeuk Kim
  2015-05-09  4:20 ` [PATCH 18/18] f2fs crypto: remove checking key context during lookup Jaegeuk Kim
  16 siblings, 2 replies; 34+ messages in thread
From: Jaegeuk Kim @ 2015-05-09  4:20 UTC (permalink / raw)
  To: linux-kernel, linux-fsdevel, linux-f2fs-devel
  Cc: Jaegeuk Kim, Uday Savagaonkar, Theodore Ts'o

This patch implements encryption support for symlink.

The codes refered the ext4 symlink path.

Signed-off-by: Uday Savagaonkar <savagaon@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---
 fs/f2fs/f2fs_crypto.h |   2 -
 fs/f2fs/namei.c       | 138 ++++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 135 insertions(+), 5 deletions(-)

diff --git a/fs/f2fs/f2fs_crypto.h b/fs/f2fs/f2fs_crypto.h
index bad32e6..6e41394 100644
--- a/fs/f2fs/f2fs_crypto.h
+++ b/fs/f2fs/f2fs_crypto.h
@@ -145,8 +145,6 @@ struct f2fs_encrypted_symlink_data {
  */
 static inline u32 encrypted_symlink_data_len(u32 l)
 {
-	if (l < F2FS_CRYPTO_BLOCK_SIZE)
-		l = F2FS_CRYPTO_BLOCK_SIZE;
 	return (l + sizeof(struct f2fs_encrypted_symlink_data) - 1);
 }
 #endif	/* _F2FS_CRYPTO_H */
diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c
index c857f82..e6a6310 100644
--- a/fs/f2fs/namei.c
+++ b/fs/f2fs/namei.c
@@ -333,12 +333,102 @@ static void *f2fs_follow_link(struct dentry *dentry, struct nameidata *nd)
 	return page;
 }
 
+#ifdef CONFIG_F2FS_FS_ENCRYPTION
+static void *f2fs_encrypted_follow_link(struct dentry *dentry,
+						struct nameidata *nd)
+{
+	struct page *cpage = NULL;
+	char *caddr, *paddr = NULL;
+	struct f2fs_str cstr, pstr;
+	struct inode *inode = d_inode(dentry);
+	struct f2fs_encrypted_symlink_data *sd;
+	loff_t size = min_t(loff_t, i_size_read(inode), PAGE_SIZE - 1);
+	int res;
+	u32 max_size = inode->i_sb->s_blocksize;
+
+	if (!f2fs_encrypted_inode(inode))
+		return f2fs_follow_link(dentry, nd);
+
+	res = f2fs_setup_fname_crypto(inode);
+	if (res)
+		return ERR_PTR(res);
+
+	cpage = read_mapping_page(inode->i_mapping, 0, NULL);
+	if (IS_ERR(cpage))
+		return cpage;
+	caddr = kmap(cpage);
+	caddr[size] = 0;
+
+	/* Symlink is encrypted */
+	sd = (struct f2fs_encrypted_symlink_data *)caddr;
+	cstr.name = sd->encrypted_path;
+	cstr.len = le16_to_cpu(sd->len);
+
+	/* this is broken symlink case */
+	if (cstr.name[0] == 0 && cstr.len == 0) {
+		res = -ENOENT;
+		goto errout;
+	}
+
+	if ((cstr.len + sizeof(struct f2fs_encrypted_symlink_data) - 1) >
+								max_size) {
+		/* Symlink data on the disk is corrupted */
+		res = -EIO;
+		goto errout;
+	}
+	paddr = kmalloc(cstr.len + 1, GFP_NOFS);
+	if (!paddr) {
+		res = -ENOMEM;
+		goto errout;
+	}
+	pstr.name = paddr;
+	pstr.len = cstr.len;
+	res = f2fs_fname_disk_to_usr(inode, NULL, &cstr, &pstr);
+	if (res < 0)
+		goto errout;
+
+	/* Null-terminate the name */
+	if (res <= cstr.len)
+		paddr[res] = '\0';
+	nd_set_link(nd, paddr);
+	if (cpage) {
+		kunmap(cpage);
+		page_cache_release(cpage);
+	}
+	return NULL;
+errout:
+	if (cpage) {
+		kunmap(cpage);
+		page_cache_release(cpage);
+	}
+	kfree(paddr);
+	return ERR_PTR(res);
+}
+
+static void f2fs_encrypted_put_link(struct dentry *dentry, struct nameidata *nd,
+			  void *cookie)
+{
+	struct page *page = cookie;
+
+	if (!page) {
+		kfree(nd_get_link(nd));
+	} else {
+		kunmap(page);
+		page_cache_release(page);
+	}
+}
+#endif
+
 static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
 					const char *symname)
 {
 	struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
 	struct inode *inode;
-	size_t symlen = strlen(symname) + 1;
+	size_t len = strlen(symname);
+	size_t p_len;
+	char *p_str;
+	struct f2fs_str disk_link = FSTR_INIT(NULL, 0);
+	struct f2fs_encrypted_symlink_data *sd = NULL;
 	int err;
 
 	f2fs_balance_fs(sbi);
@@ -356,7 +446,40 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
 		goto out;
 	f2fs_unlock_op(sbi);
 
-	err = page_symlink(inode, symname, symlen);
+	if (f2fs_encrypted_inode(dir)) {
+		struct qstr istr = QSTR_INIT(symname, len);
+
+		err = f2fs_inherit_context(dir, inode, NULL);
+		if (err)
+			goto out;
+
+		err = f2fs_setup_fname_crypto(inode);
+		if (err)
+			goto out;
+
+		err = f2fs_fname_crypto_alloc_buffer(inode, len, &disk_link);
+		if (err)
+			goto out;
+
+		err = f2fs_fname_usr_to_disk(inode, &istr, &disk_link);
+		if (err < 0)
+			goto out;
+
+		p_len = encrypted_symlink_data_len(disk_link.len) + 1;
+		sd = kzalloc(p_len, GFP_NOFS);
+		if (!sd) {
+			err = -ENOMEM;
+			goto out;
+		}
+		memcpy(sd->encrypted_path, disk_link.name, disk_link.len);
+		sd->len = cpu_to_le16(disk_link.len);
+		p_str = (char *)sd;
+	} else {
+		p_len = len + 1;
+		p_str = (char *)symname;
+	}
+
+	err = page_symlink(inode, p_str, p_len);
 	alloc_nid_done(sbi, inode->i_ino);
 
 	d_instantiate(dentry, inode);
@@ -371,12 +494,16 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
 	 * If the symlink path is stored into inline_data, there is no
 	 * performance regression.
 	 */
-	filemap_write_and_wait_range(inode->i_mapping, 0, symlen - 1);
+	filemap_write_and_wait_range(inode->i_mapping, 0, p_len);
 
 	if (IS_DIRSYNC(dir))
 		f2fs_sync_fs(sbi->sb, 1);
+	kfree(sd);
+	f2fs_fname_crypto_free_buffer(&disk_link);
 	return err;
 out:
+	kfree(sd);
+	f2fs_fname_crypto_free_buffer(&disk_link);
 	handle_failed_inode(inode);
 	return err;
 }
@@ -843,8 +970,13 @@ const struct inode_operations f2fs_dir_inode_operations = {
 
 const struct inode_operations f2fs_symlink_inode_operations = {
 	.readlink       = generic_readlink,
+#ifdef CONFIG_F2FS_FS_ENCRYPTION
+	.follow_link    = f2fs_encrypted_follow_link,
+	.put_link       = f2fs_encrypted_put_link,
+#else
 	.follow_link    = f2fs_follow_link,
 	.put_link       = page_put_link,
+#endif
 	.getattr	= f2fs_getattr,
 	.setattr	= f2fs_setattr,
 #ifdef CONFIG_F2FS_FS_XATTR
-- 
2.1.1


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

* [PATCH 17/18] f2fs crypto: fix missing key when reading a page
  2015-05-09  4:20 [PATCH 01/18] f2fs: avoid value overflow in showing current status Jaegeuk Kim
                   ` (14 preceding siblings ...)
  2015-05-09  4:20 ` [PATCH 16/18] f2fs crypto: add symlink encryption Jaegeuk Kim
@ 2015-05-09  4:20 ` Jaegeuk Kim
  2015-05-09  4:20 ` [PATCH 18/18] f2fs crypto: remove checking key context during lookup Jaegeuk Kim
  16 siblings, 0 replies; 34+ messages in thread
From: Jaegeuk Kim @ 2015-05-09  4:20 UTC (permalink / raw)
  To: linux-kernel, linux-fsdevel, linux-f2fs-devel; +Cc: Jaegeuk Kim

1. mount $mnt
2. cp data $mnt/
3. umount $mnt
4. log out
5. log in
6. cat $mnt/data

-> panic, due to no i_crypt_info.

Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---
 fs/f2fs/crypto.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/fs/f2fs/crypto.c b/fs/f2fs/crypto.c
index 38c005c..509d19a 100644
--- a/fs/f2fs/crypto.c
+++ b/fs/f2fs/crypto.c
@@ -131,7 +131,9 @@ struct f2fs_crypto_ctx *f2fs_get_crypto_ctx(struct inode *inode)
 	unsigned long flags;
 	struct f2fs_crypt_info *ci = F2FS_I(inode)->i_crypt_info;
 
-	BUG_ON(ci == NULL);
+	if (ci == NULL)
+		return ERR_PTR(-EACCES);
+
 	/*
 	 * We first try getting the ctx from a free list because in
 	 * the common case the ctx will have an allocated and
-- 
2.1.1


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

* [PATCH 18/18] f2fs crypto: remove checking key context during lookup
  2015-05-09  4:20 [PATCH 01/18] f2fs: avoid value overflow in showing current status Jaegeuk Kim
                   ` (15 preceding siblings ...)
  2015-05-09  4:20 ` [PATCH 17/18] f2fs crypto: fix missing key when reading a page Jaegeuk Kim
@ 2015-05-09  4:20 ` Jaegeuk Kim
  16 siblings, 0 replies; 34+ messages in thread
From: Jaegeuk Kim @ 2015-05-09  4:20 UTC (permalink / raw)
  To: linux-kernel, linux-fsdevel, linux-f2fs-devel; +Cc: Jaegeuk Kim

No matter what the key is valid or not, readdir shows the dir entries correctly.
So, lookup should not failed.
But, we expect further accesses should be denied from open, rename, link, and so
on.

Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---
 fs/f2fs/namei.c | 10 ----------
 1 file changed, 10 deletions(-)

diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c
index e6a6310..cbedf56 100644
--- a/fs/f2fs/namei.c
+++ b/fs/f2fs/namei.c
@@ -260,16 +260,6 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry,
 	if (IS_ERR(inode))
 		return ERR_CAST(inode);
 
-	if (f2fs_encrypted_inode(dir) && f2fs_may_encrypt(inode) &&
-		!f2fs_is_child_context_consistent_with_parent(dir, inode)) {
-		iput(inode);
-		f2fs_msg(inode->i_sb, KERN_WARNING,
-				"Inconsistent encryption contexts: %lu/%lu\n",
-				(unsigned long)dir->i_ino,
-				(unsigned long)inode->i_ino);
-		return ERR_PTR(-EPERM);
-	}
-
 	if (f2fs_has_inline_dots(inode)) {
 		err = __recover_dot_dentries(inode, dir->i_ino);
 		if (err)
-- 
2.1.1


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

* Re: [PATCH 16/18] f2fs crypto: add symlink encryption
  2015-05-09  4:20 ` [PATCH 16/18] f2fs crypto: add symlink encryption Jaegeuk Kim
@ 2015-05-09  4:25   ` Al Viro
  2015-05-11  5:15     ` Jaegeuk Kim
  2015-05-12  3:48   ` [PATCH 16/18 v2] " Jaegeuk Kim
  1 sibling, 1 reply; 34+ messages in thread
From: Al Viro @ 2015-05-09  4:25 UTC (permalink / raw)
  To: Jaegeuk Kim
  Cc: linux-kernel, linux-fsdevel, linux-f2fs-devel, Uday Savagaonkar,
	Theodore Ts'o

On Fri, May 08, 2015 at 09:20:51PM -0700, Jaegeuk Kim wrote:
> This patch implements encryption support for symlink.
> 
> The codes refered the ext4 symlink path.

ext4 symlink patches are seriously misguided - don't mix encrypted and
unencrypted cases in the same inode_operations.

NAK.

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

* Re: [f2fs-dev] [PATCH 14/18] f2fs crypto: add filename encryption for f2fs_lookup
  2015-05-09  4:20 ` [PATCH 14/18] f2fs crypto: add filename encryption for f2fs_lookup Jaegeuk Kim
@ 2015-05-11  2:52   ` hujianyang
  2015-05-11  5:12     ` Jaegeuk Kim
  0 siblings, 1 reply; 34+ messages in thread
From: hujianyang @ 2015-05-11  2:52 UTC (permalink / raw)
  To: Jaegeuk Kim; +Cc: linux-kernel, linux-fsdevel, linux-f2fs-devel

Hi Jaegeuk,

While compiling git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs.git
branch dev(commit 5af6e74892a f2fs crypto: remove checking key context during lookup),
I saw an error:

fs/f2fs/dir.c: In function ‘find_in_level’:
fs/f2fs/dir.c:163: error: unknown field ‘len’ specified in initializer
fs/f2fs/dir.c:163: warning: excess elements in struct initializer
fs/f2fs/dir.c:163: warning: (near initialization for ‘name’)

I think it's related to this patch.
If there is anything wrong in my configuration, please let me know.

Thanks,
Hu



On 2015/5/9 12:20, Jaegeuk Kim wrote:
> This patch implements filename encryption support for f2fs_lookup.
> 
> Note that, f2fs_find_entry should be outside of f2fs_(un)lock_op().
> 
> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
> ---
>  fs/f2fs/dir.c    | 79 ++++++++++++++++++++++++++++++++------------------------
>  fs/f2fs/f2fs.h   |  9 ++++---
>  fs/f2fs/inline.c |  9 ++++---
>  3 files changed, 56 insertions(+), 41 deletions(-)
> 
> diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c
> index ab6455d..5e10d9d 100644
> --- a/fs/f2fs/dir.c
> +++ b/fs/f2fs/dir.c
> @@ -76,20 +76,10 @@ static unsigned long dir_block_index(unsigned int level,
>  	return bidx;
>  }
>  
> -static bool early_match_name(size_t namelen, f2fs_hash_t namehash,
> -				struct f2fs_dir_entry *de)
> -{
> -	if (le16_to_cpu(de->name_len) != namelen)
> -		return false;
> -
> -	if (de->hash_code != namehash)
> -		return false;
> -
> -	return true;
> -}
> -
>  static struct f2fs_dir_entry *find_in_block(struct page *dentry_page,
> -				struct qstr *name, int *max_slots,
> +				struct f2fs_filename *fname,
> +				f2fs_hash_t namehash,
> +				int *max_slots,
>  				struct page **res_page)
>  {
>  	struct f2fs_dentry_block *dentry_blk;
> @@ -99,8 +89,7 @@ static struct f2fs_dir_entry *find_in_block(struct page *dentry_page,
>  	dentry_blk = (struct f2fs_dentry_block *)kmap(dentry_page);
>  
>  	make_dentry_ptr(NULL, &d, (void *)dentry_blk, 1);
> -	de = find_target_dentry(name, max_slots, &d);
> -
> +	de = find_target_dentry(fname, namehash, max_slots, &d);
>  	if (de)
>  		*res_page = dentry_page;
>  	else
> @@ -114,13 +103,15 @@ static struct f2fs_dir_entry *find_in_block(struct page *dentry_page,
>  	return de;
>  }
>  
> -struct f2fs_dir_entry *find_target_dentry(struct qstr *name, int *max_slots,
> -						struct f2fs_dentry_ptr *d)
> +struct f2fs_dir_entry *find_target_dentry(struct f2fs_filename *fname,
> +			f2fs_hash_t namehash, int *max_slots,
> +			struct f2fs_dentry_ptr *d)
>  {
>  	struct f2fs_dir_entry *de;
>  	unsigned long bit_pos = 0;
> -	f2fs_hash_t namehash = f2fs_dentry_hash(name);
>  	int max_len = 0;
> +	struct f2fs_str de_name = FSTR_INIT(NULL, 0);
> +	struct f2fs_str *name = &fname->disk_name;
>  
>  	if (max_slots)
>  		*max_slots = 0;
> @@ -132,8 +123,18 @@ struct f2fs_dir_entry *find_target_dentry(struct qstr *name, int *max_slots,
>  		}
>  
>  		de = &d->dentry[bit_pos];
> -		if (early_match_name(name->len, namehash, de) &&
> -			!memcmp(d->filename[bit_pos], name->name, name->len))
> +
> +		/* encrypted case */
> +		de_name.name = d->filename[bit_pos];
> +		de_name.len = le16_to_cpu(de->name_len);
> +
> +		/* show encrypted name */
> +		if (fname->hash) {
> +			if (de->hash_code == fname->hash)
> +				goto found;
> +		} else if (de_name.len == name->len &&
> +			de->hash_code == namehash &&
> +			!memcmp(de_name.name, name->name, name->len))
>  			goto found;
>  
>  		if (max_slots && max_len > *max_slots)
> @@ -155,16 +156,21 @@ found:
>  }
>  
>  static struct f2fs_dir_entry *find_in_level(struct inode *dir,
> -			unsigned int level, struct qstr *name,
> -			f2fs_hash_t namehash, struct page **res_page)
> +					unsigned int level,
> +					struct f2fs_filename *fname,
> +					struct page **res_page)
>  {
> -	int s = GET_DENTRY_SLOTS(name->len);
> +	struct qstr name = FSTR_TO_QSTR(&fname->disk_name);
> +	int s = GET_DENTRY_SLOTS(name.len);
>  	unsigned int nbucket, nblock;
>  	unsigned int bidx, end_block;
>  	struct page *dentry_page;
>  	struct f2fs_dir_entry *de = NULL;
>  	bool room = false;
>  	int max_slots;
> +	f2fs_hash_t namehash;
> +
> +	namehash = f2fs_dentry_hash(&name);
>  
>  	f2fs_bug_on(F2FS_I_SB(dir), level > MAX_DIR_HASH_DEPTH);
>  
> @@ -183,7 +189,8 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir,
>  			continue;
>  		}
>  
> -		de = find_in_block(dentry_page, name, &max_slots, res_page);
> +		de = find_in_block(dentry_page, fname, namehash, &max_slots,
> +								res_page);
>  		if (de)
>  			break;
>  
> @@ -211,30 +218,34 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir,
>  {
>  	unsigned long npages = dir_blocks(dir);
>  	struct f2fs_dir_entry *de = NULL;
> -	f2fs_hash_t name_hash;
>  	unsigned int max_depth;
>  	unsigned int level;
> +	struct f2fs_filename fname;
> +	int err;
>  
>  	*res_page = NULL;
>  
> -	if (f2fs_has_inline_dentry(dir))
> -		return find_in_inline_dir(dir, child, res_page);
> +	err = f2fs_fname_setup_filename(dir, child, 1, &fname);
> +	if (err)
> +		return NULL;
> +
> +	if (f2fs_has_inline_dentry(dir)) {
> +		de = find_in_inline_dir(dir, &fname, res_page);
> +		goto out;
> +	}
>  
>  	if (npages == 0)
> -		return NULL;
> +		goto out;
>  
> -	name_hash = f2fs_dentry_hash(child);
>  	max_depth = F2FS_I(dir)->i_current_depth;
>  
>  	for (level = 0; level < max_depth; level++) {
> -		de = find_in_level(dir, level, child, name_hash, res_page);
> +		de = find_in_level(dir, level, &fname, res_page);
>  		if (de)
>  			break;
>  	}
> -	if (!de && F2FS_I(dir)->chash != name_hash) {
> -		F2FS_I(dir)->chash = name_hash;
> -		F2FS_I(dir)->clevel = level - 1;
> -	}
> +out:
> +	f2fs_fname_free_filename(&fname);
>  	return de;
>  }
>  
> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
> index 963616f..6898331 100644
> --- a/fs/f2fs/f2fs.h
> +++ b/fs/f2fs/f2fs.h
> @@ -1584,8 +1584,9 @@ struct dentry *f2fs_get_parent(struct dentry *child);
>   */
>  extern unsigned char f2fs_filetype_table[F2FS_FT_MAX];
>  void set_de_type(struct f2fs_dir_entry *, umode_t);
> -struct f2fs_dir_entry *find_target_dentry(struct qstr *, int *,
> -			struct f2fs_dentry_ptr *);
> +
> +struct f2fs_dir_entry *find_target_dentry(struct f2fs_filename *,
> +			f2fs_hash_t, int *, struct f2fs_dentry_ptr *);
>  bool f2fs_fill_dentries(struct dir_context *, struct f2fs_dentry_ptr *,
>  			unsigned int, struct f2fs_str *);
>  void do_make_empty_dir(struct inode *, struct inode *,
> @@ -1931,8 +1932,8 @@ int f2fs_convert_inline_page(struct dnode_of_data *, struct page *);
>  int f2fs_convert_inline_inode(struct inode *);
>  int f2fs_write_inline_data(struct inode *, struct page *);
>  bool recover_inline_data(struct inode *, struct page *);
> -struct f2fs_dir_entry *find_in_inline_dir(struct inode *, struct qstr *,
> -							struct page **);
> +struct f2fs_dir_entry *find_in_inline_dir(struct inode *,
> +				struct f2fs_filename *, struct page **);
>  struct f2fs_dir_entry *f2fs_parent_inline_dir(struct inode *, struct page **);
>  int make_empty_inline_dir(struct inode *inode, struct inode *, struct page *);
>  int f2fs_add_inline_entry(struct inode *, const struct qstr *, struct inode *,
> diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c
> index dd51d3f..38e75fb 100644
> --- a/fs/f2fs/inline.c
> +++ b/fs/f2fs/inline.c
> @@ -284,23 +284,26 @@ process_inline:
>  }
>  
>  struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir,
> -				struct qstr *name, struct page **res_page)
> +			struct f2fs_filename *fname, struct page **res_page)
>  {
>  	struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb);
>  	struct f2fs_inline_dentry *inline_dentry;
> +	struct qstr name = FSTR_TO_QSTR(&fname->disk_name);
>  	struct f2fs_dir_entry *de;
>  	struct f2fs_dentry_ptr d;
>  	struct page *ipage;
> +	f2fs_hash_t namehash;
>  
>  	ipage = get_node_page(sbi, dir->i_ino);
>  	if (IS_ERR(ipage))
>  		return NULL;
>  
> +	namehash = f2fs_dentry_hash(&name);
> +
>  	inline_dentry = inline_data_addr(ipage);
>  
>  	make_dentry_ptr(NULL, &d, (void *)inline_dentry, 2);
> -	de = find_target_dentry(name, NULL, &d);
> -
> +	de = find_target_dentry(fname, namehash, NULL, &d);
>  	unlock_page(ipage);
>  	if (de)
>  		*res_page = ipage;
> 



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

* Re: [f2fs-dev] [PATCH 14/18] f2fs crypto: add filename encryption for f2fs_lookup
  2015-05-11  2:52   ` [f2fs-dev] " hujianyang
@ 2015-05-11  5:12     ` Jaegeuk Kim
  2015-05-11  6:38       ` hujianyang
  0 siblings, 1 reply; 34+ messages in thread
From: Jaegeuk Kim @ 2015-05-11  5:12 UTC (permalink / raw)
  To: hujianyang; +Cc: linux-kernel, linux-fsdevel, linux-f2fs-devel

Hi Hujianynag,

I just fixed it up, and rebased the dev branch.
Could you check them out?

Thanks,

On Mon, May 11, 2015 at 10:52:48AM +0800, hujianyang wrote:
> Hi Jaegeuk,
> 
> While compiling git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs.git
> branch dev(commit 5af6e74892a f2fs crypto: remove checking key context during lookup),
> I saw an error:
> 
> fs/f2fs/dir.c: In function ‘find_in_level’:
> fs/f2fs/dir.c:163: error: unknown field ‘len’ specified in initializer
> fs/f2fs/dir.c:163: warning: excess elements in struct initializer
> fs/f2fs/dir.c:163: warning: (near initialization for ‘name’)
> 
> I think it's related to this patch.
> If there is anything wrong in my configuration, please let me know.
> 
> Thanks,
> Hu
> 
> 
> 
> On 2015/5/9 12:20, Jaegeuk Kim wrote:
> > This patch implements filename encryption support for f2fs_lookup.
> > 
> > Note that, f2fs_find_entry should be outside of f2fs_(un)lock_op().
> > 
> > Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
> > ---
> >  fs/f2fs/dir.c    | 79 ++++++++++++++++++++++++++++++++------------------------
> >  fs/f2fs/f2fs.h   |  9 ++++---
> >  fs/f2fs/inline.c |  9 ++++---
> >  3 files changed, 56 insertions(+), 41 deletions(-)
> > 
> > diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c
> > index ab6455d..5e10d9d 100644
> > --- a/fs/f2fs/dir.c
> > +++ b/fs/f2fs/dir.c
> > @@ -76,20 +76,10 @@ static unsigned long dir_block_index(unsigned int level,
> >  	return bidx;
> >  }
> >  
> > -static bool early_match_name(size_t namelen, f2fs_hash_t namehash,
> > -				struct f2fs_dir_entry *de)
> > -{
> > -	if (le16_to_cpu(de->name_len) != namelen)
> > -		return false;
> > -
> > -	if (de->hash_code != namehash)
> > -		return false;
> > -
> > -	return true;
> > -}
> > -
> >  static struct f2fs_dir_entry *find_in_block(struct page *dentry_page,
> > -				struct qstr *name, int *max_slots,
> > +				struct f2fs_filename *fname,
> > +				f2fs_hash_t namehash,
> > +				int *max_slots,
> >  				struct page **res_page)
> >  {
> >  	struct f2fs_dentry_block *dentry_blk;
> > @@ -99,8 +89,7 @@ static struct f2fs_dir_entry *find_in_block(struct page *dentry_page,
> >  	dentry_blk = (struct f2fs_dentry_block *)kmap(dentry_page);
> >  
> >  	make_dentry_ptr(NULL, &d, (void *)dentry_blk, 1);
> > -	de = find_target_dentry(name, max_slots, &d);
> > -
> > +	de = find_target_dentry(fname, namehash, max_slots, &d);
> >  	if (de)
> >  		*res_page = dentry_page;
> >  	else
> > @@ -114,13 +103,15 @@ static struct f2fs_dir_entry *find_in_block(struct page *dentry_page,
> >  	return de;
> >  }
> >  
> > -struct f2fs_dir_entry *find_target_dentry(struct qstr *name, int *max_slots,
> > -						struct f2fs_dentry_ptr *d)
> > +struct f2fs_dir_entry *find_target_dentry(struct f2fs_filename *fname,
> > +			f2fs_hash_t namehash, int *max_slots,
> > +			struct f2fs_dentry_ptr *d)
> >  {
> >  	struct f2fs_dir_entry *de;
> >  	unsigned long bit_pos = 0;
> > -	f2fs_hash_t namehash = f2fs_dentry_hash(name);
> >  	int max_len = 0;
> > +	struct f2fs_str de_name = FSTR_INIT(NULL, 0);
> > +	struct f2fs_str *name = &fname->disk_name;
> >  
> >  	if (max_slots)
> >  		*max_slots = 0;
> > @@ -132,8 +123,18 @@ struct f2fs_dir_entry *find_target_dentry(struct qstr *name, int *max_slots,
> >  		}
> >  
> >  		de = &d->dentry[bit_pos];
> > -		if (early_match_name(name->len, namehash, de) &&
> > -			!memcmp(d->filename[bit_pos], name->name, name->len))
> > +
> > +		/* encrypted case */
> > +		de_name.name = d->filename[bit_pos];
> > +		de_name.len = le16_to_cpu(de->name_len);
> > +
> > +		/* show encrypted name */
> > +		if (fname->hash) {
> > +			if (de->hash_code == fname->hash)
> > +				goto found;
> > +		} else if (de_name.len == name->len &&
> > +			de->hash_code == namehash &&
> > +			!memcmp(de_name.name, name->name, name->len))
> >  			goto found;
> >  
> >  		if (max_slots && max_len > *max_slots)
> > @@ -155,16 +156,21 @@ found:
> >  }
> >  
> >  static struct f2fs_dir_entry *find_in_level(struct inode *dir,
> > -			unsigned int level, struct qstr *name,
> > -			f2fs_hash_t namehash, struct page **res_page)
> > +					unsigned int level,
> > +					struct f2fs_filename *fname,
> > +					struct page **res_page)
> >  {
> > -	int s = GET_DENTRY_SLOTS(name->len);
> > +	struct qstr name = FSTR_TO_QSTR(&fname->disk_name);
> > +	int s = GET_DENTRY_SLOTS(name.len);
> >  	unsigned int nbucket, nblock;
> >  	unsigned int bidx, end_block;
> >  	struct page *dentry_page;
> >  	struct f2fs_dir_entry *de = NULL;
> >  	bool room = false;
> >  	int max_slots;
> > +	f2fs_hash_t namehash;
> > +
> > +	namehash = f2fs_dentry_hash(&name);
> >  
> >  	f2fs_bug_on(F2FS_I_SB(dir), level > MAX_DIR_HASH_DEPTH);
> >  
> > @@ -183,7 +189,8 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir,
> >  			continue;
> >  		}
> >  
> > -		de = find_in_block(dentry_page, name, &max_slots, res_page);
> > +		de = find_in_block(dentry_page, fname, namehash, &max_slots,
> > +								res_page);
> >  		if (de)
> >  			break;
> >  
> > @@ -211,30 +218,34 @@ struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir,
> >  {
> >  	unsigned long npages = dir_blocks(dir);
> >  	struct f2fs_dir_entry *de = NULL;
> > -	f2fs_hash_t name_hash;
> >  	unsigned int max_depth;
> >  	unsigned int level;
> > +	struct f2fs_filename fname;
> > +	int err;
> >  
> >  	*res_page = NULL;
> >  
> > -	if (f2fs_has_inline_dentry(dir))
> > -		return find_in_inline_dir(dir, child, res_page);
> > +	err = f2fs_fname_setup_filename(dir, child, 1, &fname);
> > +	if (err)
> > +		return NULL;
> > +
> > +	if (f2fs_has_inline_dentry(dir)) {
> > +		de = find_in_inline_dir(dir, &fname, res_page);
> > +		goto out;
> > +	}
> >  
> >  	if (npages == 0)
> > -		return NULL;
> > +		goto out;
> >  
> > -	name_hash = f2fs_dentry_hash(child);
> >  	max_depth = F2FS_I(dir)->i_current_depth;
> >  
> >  	for (level = 0; level < max_depth; level++) {
> > -		de = find_in_level(dir, level, child, name_hash, res_page);
> > +		de = find_in_level(dir, level, &fname, res_page);
> >  		if (de)
> >  			break;
> >  	}
> > -	if (!de && F2FS_I(dir)->chash != name_hash) {
> > -		F2FS_I(dir)->chash = name_hash;
> > -		F2FS_I(dir)->clevel = level - 1;
> > -	}
> > +out:
> > +	f2fs_fname_free_filename(&fname);
> >  	return de;
> >  }
> >  
> > diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
> > index 963616f..6898331 100644
> > --- a/fs/f2fs/f2fs.h
> > +++ b/fs/f2fs/f2fs.h
> > @@ -1584,8 +1584,9 @@ struct dentry *f2fs_get_parent(struct dentry *child);
> >   */
> >  extern unsigned char f2fs_filetype_table[F2FS_FT_MAX];
> >  void set_de_type(struct f2fs_dir_entry *, umode_t);
> > -struct f2fs_dir_entry *find_target_dentry(struct qstr *, int *,
> > -			struct f2fs_dentry_ptr *);
> > +
> > +struct f2fs_dir_entry *find_target_dentry(struct f2fs_filename *,
> > +			f2fs_hash_t, int *, struct f2fs_dentry_ptr *);
> >  bool f2fs_fill_dentries(struct dir_context *, struct f2fs_dentry_ptr *,
> >  			unsigned int, struct f2fs_str *);
> >  void do_make_empty_dir(struct inode *, struct inode *,
> > @@ -1931,8 +1932,8 @@ int f2fs_convert_inline_page(struct dnode_of_data *, struct page *);
> >  int f2fs_convert_inline_inode(struct inode *);
> >  int f2fs_write_inline_data(struct inode *, struct page *);
> >  bool recover_inline_data(struct inode *, struct page *);
> > -struct f2fs_dir_entry *find_in_inline_dir(struct inode *, struct qstr *,
> > -							struct page **);
> > +struct f2fs_dir_entry *find_in_inline_dir(struct inode *,
> > +				struct f2fs_filename *, struct page **);
> >  struct f2fs_dir_entry *f2fs_parent_inline_dir(struct inode *, struct page **);
> >  int make_empty_inline_dir(struct inode *inode, struct inode *, struct page *);
> >  int f2fs_add_inline_entry(struct inode *, const struct qstr *, struct inode *,
> > diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c
> > index dd51d3f..38e75fb 100644
> > --- a/fs/f2fs/inline.c
> > +++ b/fs/f2fs/inline.c
> > @@ -284,23 +284,26 @@ process_inline:
> >  }
> >  
> >  struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir,
> > -				struct qstr *name, struct page **res_page)
> > +			struct f2fs_filename *fname, struct page **res_page)
> >  {
> >  	struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb);
> >  	struct f2fs_inline_dentry *inline_dentry;
> > +	struct qstr name = FSTR_TO_QSTR(&fname->disk_name);
> >  	struct f2fs_dir_entry *de;
> >  	struct f2fs_dentry_ptr d;
> >  	struct page *ipage;
> > +	f2fs_hash_t namehash;
> >  
> >  	ipage = get_node_page(sbi, dir->i_ino);
> >  	if (IS_ERR(ipage))
> >  		return NULL;
> >  
> > +	namehash = f2fs_dentry_hash(&name);
> > +
> >  	inline_dentry = inline_data_addr(ipage);
> >  
> >  	make_dentry_ptr(NULL, &d, (void *)inline_dentry, 2);
> > -	de = find_target_dentry(name, NULL, &d);
> > -
> > +	de = find_target_dentry(fname, namehash, NULL, &d);
> >  	unlock_page(ipage);
> >  	if (de)
> >  		*res_page = ipage;
> > 

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

* Re: [PATCH 16/18] f2fs crypto: add symlink encryption
  2015-05-09  4:25   ` Al Viro
@ 2015-05-11  5:15     ` Jaegeuk Kim
  0 siblings, 0 replies; 34+ messages in thread
From: Jaegeuk Kim @ 2015-05-11  5:15 UTC (permalink / raw)
  To: Al Viro
  Cc: linux-kernel, linux-fsdevel, linux-f2fs-devel, Uday Savagaonkar,
	Theodore Ts'o

On Sat, May 09, 2015 at 05:25:54AM +0100, Al Viro wrote:
> On Fri, May 08, 2015 at 09:20:51PM -0700, Jaegeuk Kim wrote:
> > This patch implements encryption support for symlink.
> > 
> > The codes refered the ext4 symlink path.
> 
> ext4 symlink patches are seriously misguided - don't mix encrypted and
> unencrypted cases in the same inode_operations.

Ok. Let me split them into separated inode_operations.

Thanks,

> 
> NAK.

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

* Re: [f2fs-dev] [PATCH 14/18] f2fs crypto: add filename encryption for f2fs_lookup
  2015-05-11  5:12     ` Jaegeuk Kim
@ 2015-05-11  6:38       ` hujianyang
  0 siblings, 0 replies; 34+ messages in thread
From: hujianyang @ 2015-05-11  6:38 UTC (permalink / raw)
  To: Jaegeuk Kim; +Cc: linux-kernel, linux-fsdevel, linux-f2fs-devel

It's OK now.

Thanks,
Hu

On 2015/5/11 13:12, Jaegeuk Kim wrote:
> Hi Hujianynag,
> 
> I just fixed it up, and rebased the dev branch.
> Could you check them out?
> 
> Thanks,
> 
> On Mon, May 11, 2015 at 10:52:48AM +0800, hujianyang wrote:
>> Hi Jaegeuk,
>>
>> While compiling git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs.git
>> branch dev(commit 5af6e74892a f2fs crypto: remove checking key context during lookup),
>> I saw an error:
>>
>> fs/f2fs/dir.c: In function ‘find_in_level’:
>> fs/f2fs/dir.c:163: error: unknown field ‘len’ specified in initializer
>> fs/f2fs/dir.c:163: warning: excess elements in struct initializer
>> fs/f2fs/dir.c:163: warning: (near initialization for ‘name’)
>>
>> I think it's related to this patch.
>> If there is anything wrong in my configuration, please let me know.
>>
>> Thanks,
>> Hu
>>



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

* Re: [PATCH 16/18 v2] f2fs crypto: add symlink encryption
  2015-05-09  4:20 ` [PATCH 16/18] f2fs crypto: add symlink encryption Jaegeuk Kim
  2015-05-09  4:25   ` Al Viro
@ 2015-05-12  3:48   ` Jaegeuk Kim
  1 sibling, 0 replies; 34+ messages in thread
From: Jaegeuk Kim @ 2015-05-12  3:48 UTC (permalink / raw)
  To: linux-kernel, linux-fsdevel, linux-f2fs-devel
  Cc: Uday Savagaonkar, Theodore Ts'o

Chage log from v1:
 o split inode_operations suggested by Al

This patch implements encryption support for symlink.

Signed-off-by: Uday Savagaonkar <savagaon@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---
 fs/f2fs/f2fs.h  |   1 +
 fs/f2fs/inode.c |   5 +-
 fs/f2fs/namei.c | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 145 insertions(+), 6 deletions(-)

diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 686517d..5809590 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -1920,6 +1920,7 @@ extern const struct address_space_operations f2fs_node_aops;
 extern const struct address_space_operations f2fs_meta_aops;
 extern const struct inode_operations f2fs_dir_inode_operations;
 extern const struct inode_operations f2fs_symlink_inode_operations;
+extern const struct inode_operations f2fs_encrypted_symlink_inode_operations;
 extern const struct inode_operations f2fs_special_inode_operations;
 extern struct kmem_cache *inode_entry_slab;
 
diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c
index e622ec9..13936f9 100644
--- a/fs/f2fs/inode.c
+++ b/fs/f2fs/inode.c
@@ -198,7 +198,10 @@ make_now:
 		inode->i_mapping->a_ops = &f2fs_dblock_aops;
 		mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_HIGH_ZERO);
 	} else if (S_ISLNK(inode->i_mode)) {
-		inode->i_op = &f2fs_symlink_inode_operations;
+		if (f2fs_encrypted_inode(inode))
+			inode->i_op = &f2fs_encrypted_symlink_inode_operations;
+		else
+			inode->i_op = &f2fs_symlink_inode_operations;
 		inode->i_mapping->a_ops = &f2fs_dblock_aops;
 	} else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) ||
 			S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c
index c857f82..5287818 100644
--- a/fs/f2fs/namei.c
+++ b/fs/f2fs/namei.c
@@ -338,16 +338,26 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
 {
 	struct f2fs_sb_info *sbi = F2FS_I_SB(dir);
 	struct inode *inode;
-	size_t symlen = strlen(symname) + 1;
+	size_t len = strlen(symname);
+	size_t p_len;
+	char *p_str;
+	struct f2fs_str disk_link = FSTR_INIT(NULL, 0);
+	struct f2fs_encrypted_symlink_data *sd = NULL;
 	int err;
 
+	if (len > dir->i_sb->s_blocksize)
+		return -ENAMETOOLONG;
+
 	f2fs_balance_fs(sbi);
 
 	inode = f2fs_new_inode(dir, S_IFLNK | S_IRWXUGO);
 	if (IS_ERR(inode))
 		return PTR_ERR(inode);
 
-	inode->i_op = &f2fs_symlink_inode_operations;
+	if (f2fs_encrypted_inode(inode))
+		inode->i_op = &f2fs_encrypted_symlink_inode_operations;
+	else
+		inode->i_op = &f2fs_symlink_inode_operations;
 	inode->i_mapping->a_ops = &f2fs_dblock_aops;
 
 	f2fs_lock_op(sbi);
@@ -355,10 +365,50 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
 	if (err)
 		goto out;
 	f2fs_unlock_op(sbi);
-
-	err = page_symlink(inode, symname, symlen);
 	alloc_nid_done(sbi, inode->i_ino);
 
+	if (f2fs_encrypted_inode(dir)) {
+		struct qstr istr = QSTR_INIT(symname, len);
+
+		err = f2fs_inherit_context(dir, inode, NULL);
+		if (err)
+			goto err_out;
+
+		err = f2fs_setup_fname_crypto(inode);
+		if (err)
+			goto err_out;
+
+		err = f2fs_fname_crypto_alloc_buffer(inode, len, &disk_link);
+		if (err)
+			goto err_out;
+
+		err = f2fs_fname_usr_to_disk(inode, &istr, &disk_link);
+		if (err < 0)
+			goto err_out;
+
+		p_len = encrypted_symlink_data_len(disk_link.len) + 1;
+
+		if (p_len > dir->i_sb->s_blocksize) {
+			err = -ENAMETOOLONG;
+			goto err_out;
+		}
+
+		sd = kzalloc(p_len, GFP_NOFS);
+		if (!sd) {
+			err = -ENOMEM;
+			goto err_out;
+		}
+		memcpy(sd->encrypted_path, disk_link.name, disk_link.len);
+		sd->len = cpu_to_le16(disk_link.len);
+		p_str = (char *)sd;
+	} else {
+		p_len = len + 1;
+		p_str = (char *)symname;
+	}
+
+	err = page_symlink(inode, p_str, p_len);
+
+err_out:
 	d_instantiate(dentry, inode);
 	unlock_new_inode(inode);
 
@@ -371,10 +421,14 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
 	 * If the symlink path is stored into inline_data, there is no
 	 * performance regression.
 	 */
-	filemap_write_and_wait_range(inode->i_mapping, 0, symlen - 1);
+	if (!err)
+		filemap_write_and_wait_range(inode->i_mapping, 0, p_len - 1);
 
 	if (IS_DIRSYNC(dir))
 		f2fs_sync_fs(sbi->sb, 1);
+
+	kfree(sd);
+	f2fs_fname_crypto_free_buffer(&disk_link);
 	return err;
 out:
 	handle_failed_inode(inode);
@@ -818,6 +872,87 @@ out:
 	return err;
 }
 
+#ifdef CONFIG_F2FS_FS_ENCRYPTION
+static void *f2fs_encrypted_follow_link(struct dentry *dentry,
+						struct nameidata *nd)
+{
+	struct page *cpage = NULL;
+	char *caddr, *paddr = NULL;
+	struct f2fs_str cstr;
+	struct f2fs_str pstr = FSTR_INIT(NULL, 0);
+	struct inode *inode = d_inode(dentry);
+	struct f2fs_encrypted_symlink_data *sd;
+	loff_t size = min_t(loff_t, i_size_read(inode), PAGE_SIZE - 1);
+	u32 max_size = inode->i_sb->s_blocksize;
+	int res;
+
+	res = f2fs_setup_fname_crypto(inode);
+	if (res)
+		return ERR_PTR(res);
+
+	cpage = read_mapping_page(inode->i_mapping, 0, NULL);
+	if (IS_ERR(cpage))
+		return cpage;
+	caddr = kmap(cpage);
+	caddr[size] = 0;
+
+	/* Symlink is encrypted */
+	sd = (struct f2fs_encrypted_symlink_data *)caddr;
+	cstr.name = sd->encrypted_path;
+	cstr.len = le16_to_cpu(sd->len);
+
+	/* this is broken symlink case */
+	if (cstr.name[0] == 0 && cstr.len == 0) {
+		res = -ENOENT;
+		goto errout;
+	}
+
+	if ((cstr.len + sizeof(struct f2fs_encrypted_symlink_data) - 1) >
+								max_size) {
+		/* Symlink data on the disk is corrupted */
+		res = -EIO;
+		goto errout;
+	}
+	res = f2fs_fname_crypto_alloc_buffer(inode, cstr.len, &pstr);
+	if (res)
+		goto errout;
+
+	res = f2fs_fname_disk_to_usr(inode, NULL, &cstr, &pstr);
+	if (res < 0)
+		goto errout;
+
+	paddr = pstr.name;
+
+	/* Null-terminate the name */
+	if (res <= cstr.len)
+		paddr[res] = '\0';
+	nd_set_link(nd, paddr);
+
+	kunmap(cpage);
+	page_cache_release(cpage);
+	return NULL;
+errout:
+	f2fs_fname_crypto_free_buffer(&pstr);
+	kunmap(cpage);
+	page_cache_release(cpage);
+	return ERR_PTR(res);
+}
+
+const struct inode_operations f2fs_encrypted_symlink_inode_operations = {
+	.readlink       = generic_readlink,
+	.follow_link    = f2fs_encrypted_follow_link,
+	.put_link       = kfree_put_link,
+	.getattr	= f2fs_getattr,
+	.setattr	= f2fs_setattr,
+#ifdef CONFIG_F2FS_FS_XATTR
+	.setxattr	= generic_setxattr,
+	.getxattr	= generic_getxattr,
+	.listxattr	= f2fs_listxattr,
+	.removexattr	= generic_removexattr,
+#endif
+};
+#endif
+
 const struct inode_operations f2fs_dir_inode_operations = {
 	.create		= f2fs_create,
 	.lookup		= f2fs_lookup,
-- 
2.1.1


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

* Re: [PATCH 03/18] f2fs crypto: declare some definitions for f2fs encryption feature
  2015-05-09  4:20 ` [PATCH 03/18] f2fs crypto: declare some definitions for f2fs encryption feature Jaegeuk Kim
@ 2015-05-13  2:02   ` Dave Chinner
  2015-05-13  6:48     ` Jaegeuk Kim
  0 siblings, 1 reply; 34+ messages in thread
From: Dave Chinner @ 2015-05-13  2:02 UTC (permalink / raw)
  To: Jaegeuk Kim; +Cc: linux-kernel, linux-fsdevel, linux-f2fs-devel

On Fri, May 08, 2015 at 09:20:38PM -0700, Jaegeuk Kim wrote:
> This definitions will be used by inode and superblock for encyption.

How much of this crypto stuff is common with or only slightly
modified from the ext4 code?  Is the behaviour and features the
same? Is the user API and management tools the same?

IMO, if there is any amount of overlap, then we should be
implementing this stuff as generic code, not propagating the same
code through multiple filesystems via copy-n-paste-n-modify. This
will simply end up with diverging code, different bugs and feature
sets, and none of the implementations will get the review and
maintenance they really require...

And, FWIW, this is the reason why I originally asked for the ext4
encryption code to be pulled up to the VFS: precisely so we didn't
end up with a rapid proliferation of individual in-filesystem
encryption implementations that are all slightly different...

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

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

* Re: [PATCH 03/18] f2fs crypto: declare some definitions for f2fs encryption feature
  2015-05-13  2:02   ` Dave Chinner
@ 2015-05-13  6:48     ` Jaegeuk Kim
  2015-05-14  0:37       ` Dave Chinner
  0 siblings, 1 reply; 34+ messages in thread
From: Jaegeuk Kim @ 2015-05-13  6:48 UTC (permalink / raw)
  To: Dave Chinner; +Cc: linux-kernel, linux-fsdevel, linux-f2fs-devel

On Wed, May 13, 2015 at 12:02:08PM +1000, Dave Chinner wrote:
> On Fri, May 08, 2015 at 09:20:38PM -0700, Jaegeuk Kim wrote:
> > This definitions will be used by inode and superblock for encyption.
> 
> How much of this crypto stuff is common with or only slightly
> modified from the ext4 code?  Is the behaviour and features the
> same? Is the user API and management tools the same?
> 
> IMO, if there is any amount of overlap, then we should be
> implementing this stuff as generic code, not propagating the same
> code through multiple filesystems via copy-n-paste-n-modify. This
> will simply end up with diverging code, different bugs and feature
> sets, and none of the implementations will get the review and
> maintenance they really require...
> 
> And, FWIW, this is the reason why I originally asked for the ext4
> encryption code to be pulled up to the VFS: precisely so we didn't
> end up with a rapid proliferation of individual in-filesystem
> encryption implementations that are all slightly different...

Totally agreed!

AFAIK, Ted wants to push the codes as a crypto library into fs/ finally, so
I believe most part of crypto codes are common.

But, in order to realize that quickly, Ted implemented the feature to finalize
on-disk and in-memory design in EXT4 as a first step.
Then, I've been catching up and validating its design by implementing it in
F2FS, which also intends to figure out what crypto codes can be exactly common.

As Ted mentioned before, since next android version tries to use per-file
encryption, F2FS also needs to support it as quick as possible likewise EXT4.

Meanwhile, surely I've been working on writing patches to push them into fs/;
currenlty, I did for cryto.c and will do for crypto_key.c and crypto_fname.c.
But, it needs to think about crypto_policy.c differently, since it may depend
on how each filesystem stores the policy information respectively; we cannot
push all the filesystems should use xattrs, right?

Anyway, let me take a time to work on this and submit RFC patches.

Thanks,

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

* Re: [PATCH 03/18] f2fs crypto: declare some definitions for f2fs encryption feature
  2015-05-13  6:48     ` Jaegeuk Kim
@ 2015-05-14  0:37       ` Dave Chinner
  2015-05-14  1:56         ` Jaegeuk Kim
  2015-05-16 13:24         ` Theodore Ts'o
  0 siblings, 2 replies; 34+ messages in thread
From: Dave Chinner @ 2015-05-14  0:37 UTC (permalink / raw)
  To: Jaegeuk Kim; +Cc: linux-kernel, linux-fsdevel, linux-f2fs-devel

On Tue, May 12, 2015 at 11:48:02PM -0700, Jaegeuk Kim wrote:
> On Wed, May 13, 2015 at 12:02:08PM +1000, Dave Chinner wrote:
> > On Fri, May 08, 2015 at 09:20:38PM -0700, Jaegeuk Kim wrote:
> > > This definitions will be used by inode and superblock for encyption.
> > 
> > How much of this crypto stuff is common with or only slightly
> > modified from the ext4 code?  Is the behaviour and features the
> > same? Is the user API and management tools the same?
> > 
> > IMO, if there is any amount of overlap, then we should be
> > implementing this stuff as generic code, not propagating the same
> > code through multiple filesystems via copy-n-paste-n-modify. This
> > will simply end up with diverging code, different bugs and feature
> > sets, and none of the implementations will get the review and
> > maintenance they really require...
> > 
> > And, FWIW, this is the reason why I originally asked for the ext4
> > encryption code to be pulled up to the VFS: precisely so we didn't
> > end up with a rapid proliferation of individual in-filesystem
> > encryption implementations that are all slightly different...
> 
> Totally agreed!
> 
> AFAIK, Ted wants to push the codes as a crypto library into fs/ finally, so
> I believe most part of crypto codes are common.

Can I suggest fs/crypto/ if there are going to be multiple files?

> But, in order to realize that quickly, Ted implemented the feature to finalize
> on-disk and in-memory design in EXT4 as a first step.
> Then, I've been catching up and validating its design by implementing it in
> F2FS, which also intends to figure out what crypto codes can be exactly common.

Excellent. That will make it easier and less error prone for other
filesystems to implement it, too!

> As Ted mentioned before, since next android version tries to use per-file
> encryption, F2FS also needs to support it as quick as possible likewise EXT4.

Fair enough.

> Meanwhile, surely I've been working on writing patches to push them into fs/;
> currenlty, I did for cryto.c and will do for crypto_key.c and crypto_fname.c.
> But, it needs to think about crypto_policy.c differently, since it may depend
> on how each filesystem stores the policy information respectively; we cannot
> push all the filesystems should use xattrs, right?

All filesystems likely to implement per-file crypto support xattrs,
and this is exactly what xattrs are designed for. e.g. we already
require xattrs for generic security labels, ACLs, etc. Hence
per-file crypto information should also use a common, shared xattr
format. That way it only needs to be implemented once in the generic
code and there's very little (hopefully nothing!) each filesystem
has to customise to store the crypto information for each file.

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

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

* Re: [PATCH 03/18] f2fs crypto: declare some definitions for f2fs encryption feature
  2015-05-14  0:37       ` Dave Chinner
@ 2015-05-14  1:56         ` Jaegeuk Kim
  2015-05-14 16:50           ` Tom Marshall
  2015-05-16 13:24         ` Theodore Ts'o
  1 sibling, 1 reply; 34+ messages in thread
From: Jaegeuk Kim @ 2015-05-14  1:56 UTC (permalink / raw)
  To: Dave Chinner; +Cc: linux-kernel, linux-fsdevel, linux-f2fs-devel

On Thu, May 14, 2015 at 10:37:21AM +1000, Dave Chinner wrote:
> On Tue, May 12, 2015 at 11:48:02PM -0700, Jaegeuk Kim wrote:
> > On Wed, May 13, 2015 at 12:02:08PM +1000, Dave Chinner wrote:
> > > On Fri, May 08, 2015 at 09:20:38PM -0700, Jaegeuk Kim wrote:
> > > > This definitions will be used by inode and superblock for encyption.
> > > 
> > > How much of this crypto stuff is common with or only slightly
> > > modified from the ext4 code?  Is the behaviour and features the
> > > same? Is the user API and management tools the same?
> > > 
> > > IMO, if there is any amount of overlap, then we should be
> > > implementing this stuff as generic code, not propagating the same
> > > code through multiple filesystems via copy-n-paste-n-modify. This
> > > will simply end up with diverging code, different bugs and feature
> > > sets, and none of the implementations will get the review and
> > > maintenance they really require...
> > > 
> > > And, FWIW, this is the reason why I originally asked for the ext4
> > > encryption code to be pulled up to the VFS: precisely so we didn't
> > > end up with a rapid proliferation of individual in-filesystem
> > > encryption implementations that are all slightly different...
> > 
> > Totally agreed!
> > 
> > AFAIK, Ted wants to push the codes as a crypto library into fs/ finally, so
> > I believe most part of crypto codes are common.
> 
> Can I suggest fs/crypto/ if there are going to be multiple files?

No problem at all. I'll do.

> 
> > But, in order to realize that quickly, Ted implemented the feature to finalize
> > on-disk and in-memory design in EXT4 as a first step.
> > Then, I've been catching up and validating its design by implementing it in
> > F2FS, which also intends to figure out what crypto codes can be exactly common.
> 
> Excellent. That will make it easier and less error prone for other
> filesystems to implement it, too!
> 
> > As Ted mentioned before, since next android version tries to use per-file
> > encryption, F2FS also needs to support it as quick as possible likewise EXT4.
> 
> Fair enough.
> 
> > Meanwhile, surely I've been working on writing patches to push them into fs/;
> > currenlty, I did for cryto.c and will do for crypto_key.c and crypto_fname.c.
> > But, it needs to think about crypto_policy.c differently, since it may depend
> > on how each filesystem stores the policy information respectively; we cannot
> > push all the filesystems should use xattrs, right?
> 
> All filesystems likely to implement per-file crypto support xattrs,
> and this is exactly what xattrs are designed for. e.g. we already
> require xattrs for generic security labels, ACLs, etc. Hence
> per-file crypto information should also use a common, shared xattr
> format. That way it only needs to be implemented once in the generic
> code and there's very little (hopefully nothing!) each filesystem
> has to customise to store the crypto information for each file.

Ok, I see. Let me take a look at that too.
Thank you for sharing your thoughts. :)

> 
> Cheers,
> 
> Dave.
> -- 
> Dave Chinner
> david@fromorbit.com

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

* Re: [PATCH 03/18] f2fs crypto: declare some definitions for f2fs encryption feature
  2015-05-14  1:56         ` Jaegeuk Kim
@ 2015-05-14 16:50           ` Tom Marshall
  2015-05-16  1:14             ` Jaegeuk Kim
  0 siblings, 1 reply; 34+ messages in thread
From: Tom Marshall @ 2015-05-14 16:50 UTC (permalink / raw)
  To: Jaegeuk Kim, Dave Chinner; +Cc: linux-kernel, linux-fsdevel, linux-f2fs-devel

Please keep in mind that I'm also working on transparent compression.  
I'm watching this thread closely so that I can implement a compression 
library alongside the crypto library.  If there is any interest or 
benefit, I would be glad to work together so that the two can be done 
cooperatively at the same time.

On 05/13/2015 06:56 PM, Jaegeuk Kim wrote:
> On Thu, May 14, 2015 at 10:37:21AM +1000, Dave Chinner wrote:
>> On Tue, May 12, 2015 at 11:48:02PM -0700, Jaegeuk Kim wrote:
>>> On Wed, May 13, 2015 at 12:02:08PM +1000, Dave Chinner wrote:
>>>> On Fri, May 08, 2015 at 09:20:38PM -0700, Jaegeuk Kim wrote:
>>>>> This definitions will be used by inode and superblock for encyption.
>>>> How much of this crypto stuff is common with or only slightly
>>>> modified from the ext4 code?  Is the behaviour and features the
>>>> same? Is the user API and management tools the same?
>>>>
>>>> IMO, if there is any amount of overlap, then we should be
>>>> implementing this stuff as generic code, not propagating the same
>>>> code through multiple filesystems via copy-n-paste-n-modify. This
>>>> will simply end up with diverging code, different bugs and feature
>>>> sets, and none of the implementations will get the review and
>>>> maintenance they really require...
>>>>
>>>> And, FWIW, this is the reason why I originally asked for the ext4
>>>> encryption code to be pulled up to the VFS: precisely so we didn't
>>>> end up with a rapid proliferation of individual in-filesystem
>>>> encryption implementations that are all slightly different...
>>> Totally agreed!
>>>
>>> AFAIK, Ted wants to push the codes as a crypto library into fs/ finally, so
>>> I believe most part of crypto codes are common.
>> Can I suggest fs/crypto/ if there are going to be multiple files?
> No problem at all. I'll do.
>
>>> But, in order to realize that quickly, Ted implemented the feature to finalize
>>> on-disk and in-memory design in EXT4 as a first step.
>>> Then, I've been catching up and validating its design by implementing it in
>>> F2FS, which also intends to figure out what crypto codes can be exactly common.
>> Excellent. That will make it easier and less error prone for other
>> filesystems to implement it, too!
>>
>>> As Ted mentioned before, since next android version tries to use per-file
>>> encryption, F2FS also needs to support it as quick as possible likewise EXT4.
>> Fair enough.
>>
>>> Meanwhile, surely I've been working on writing patches to push them into fs/;
>>> currenlty, I did for cryto.c and will do for crypto_key.c and crypto_fname.c.
>>> But, it needs to think about crypto_policy.c differently, since it may depend
>>> on how each filesystem stores the policy information respectively; we cannot
>>> push all the filesystems should use xattrs, right?
>> All filesystems likely to implement per-file crypto support xattrs,
>> and this is exactly what xattrs are designed for. e.g. we already
>> require xattrs for generic security labels, ACLs, etc. Hence
>> per-file crypto information should also use a common, shared xattr
>> format. That way it only needs to be implemented once in the generic
>> code and there's very little (hopefully nothing!) each filesystem
>> has to customise to store the crypto information for each file.
> Ok, I see. Let me take a look at that too.
> Thank you for sharing your thoughts. :)
>
>> Cheers,
>>
>> Dave.
>> -- 
>> Dave Chinner
>> david@fromorbit.com
> --
> To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


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

* Re: [PATCH 03/18] f2fs crypto: declare some definitions for f2fs encryption feature
  2015-05-14 16:50           ` Tom Marshall
@ 2015-05-16  1:14             ` Jaegeuk Kim
  2015-05-16  4:47               ` Tom Marshall
  0 siblings, 1 reply; 34+ messages in thread
From: Jaegeuk Kim @ 2015-05-16  1:14 UTC (permalink / raw)
  To: Tom Marshall; +Cc: Dave Chinner, linux-kernel, linux-fsdevel, linux-f2fs-devel

On Thu, May 14, 2015 at 09:50:44AM -0700, Tom Marshall wrote:
> Please keep in mind that I'm also working on transparent
> compression.  I'm watching this thread closely so that I can
> implement a compression library alongside the crypto library.  If
> there is any interest or benefit, I would be glad to work together
> so that the two can be done cooperatively at the same time.

I can't imagine quickly how compression code can be shared with crypto.
The basic approach for compression would be that X pages can be compressed into
small number of pages, Y, which can be a X to Y mapping.
But, this per-file encryption supports only 1 to 1 4KB mapping, so that it could
be quite a simple implementation.

Could you elaborate on your approach or design? Or, codes?
Whatever, IMO, it needs to implement it by any filesystem first.

Thanks,

> 
> On 05/13/2015 06:56 PM, Jaegeuk Kim wrote:
> >On Thu, May 14, 2015 at 10:37:21AM +1000, Dave Chinner wrote:
> >>On Tue, May 12, 2015 at 11:48:02PM -0700, Jaegeuk Kim wrote:
> >>>On Wed, May 13, 2015 at 12:02:08PM +1000, Dave Chinner wrote:
> >>>>On Fri, May 08, 2015 at 09:20:38PM -0700, Jaegeuk Kim wrote:
> >>>>>This definitions will be used by inode and superblock for encyption.
> >>>>How much of this crypto stuff is common with or only slightly
> >>>>modified from the ext4 code?  Is the behaviour and features the
> >>>>same? Is the user API and management tools the same?
> >>>>
> >>>>IMO, if there is any amount of overlap, then we should be
> >>>>implementing this stuff as generic code, not propagating the same
> >>>>code through multiple filesystems via copy-n-paste-n-modify. This
> >>>>will simply end up with diverging code, different bugs and feature
> >>>>sets, and none of the implementations will get the review and
> >>>>maintenance they really require...
> >>>>
> >>>>And, FWIW, this is the reason why I originally asked for the ext4
> >>>>encryption code to be pulled up to the VFS: precisely so we didn't
> >>>>end up with a rapid proliferation of individual in-filesystem
> >>>>encryption implementations that are all slightly different...
> >>>Totally agreed!
> >>>
> >>>AFAIK, Ted wants to push the codes as a crypto library into fs/ finally, so
> >>>I believe most part of crypto codes are common.
> >>Can I suggest fs/crypto/ if there are going to be multiple files?
> >No problem at all. I'll do.
> >
> >>>But, in order to realize that quickly, Ted implemented the feature to finalize
> >>>on-disk and in-memory design in EXT4 as a first step.
> >>>Then, I've been catching up and validating its design by implementing it in
> >>>F2FS, which also intends to figure out what crypto codes can be exactly common.
> >>Excellent. That will make it easier and less error prone for other
> >>filesystems to implement it, too!
> >>
> >>>As Ted mentioned before, since next android version tries to use per-file
> >>>encryption, F2FS also needs to support it as quick as possible likewise EXT4.
> >>Fair enough.
> >>
> >>>Meanwhile, surely I've been working on writing patches to push them into fs/;
> >>>currenlty, I did for cryto.c and will do for crypto_key.c and crypto_fname.c.
> >>>But, it needs to think about crypto_policy.c differently, since it may depend
> >>>on how each filesystem stores the policy information respectively; we cannot
> >>>push all the filesystems should use xattrs, right?
> >>All filesystems likely to implement per-file crypto support xattrs,
> >>and this is exactly what xattrs are designed for. e.g. we already
> >>require xattrs for generic security labels, ACLs, etc. Hence
> >>per-file crypto information should also use a common, shared xattr
> >>format. That way it only needs to be implemented once in the generic
> >>code and there's very little (hopefully nothing!) each filesystem
> >>has to customise to store the crypto information for each file.
> >Ok, I see. Let me take a look at that too.
> >Thank you for sharing your thoughts. :)
> >
> >>Cheers,
> >>
> >>Dave.
> >>-- 
> >>Dave Chinner
> >>david@fromorbit.com
> >--
> >To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
> >the body of a message to majordomo@vger.kernel.org
> >More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH 03/18] f2fs crypto: declare some definitions for f2fs encryption feature
  2015-05-16  1:14             ` Jaegeuk Kim
@ 2015-05-16  4:47               ` Tom Marshall
  2015-05-18  6:24                 ` Jaegeuk Kim
  0 siblings, 1 reply; 34+ messages in thread
From: Tom Marshall @ 2015-05-16  4:47 UTC (permalink / raw)
  To: Jaegeuk Kim; +Cc: Dave Chinner, linux-kernel, linux-fsdevel, linux-f2fs-devel

On Fri, May 15, 2015 at 06:14:24PM -0700, Jaegeuk Kim wrote:
> On Thu, May 14, 2015 at 09:50:44AM -0700, Tom Marshall wrote:
> > Please keep in mind that I'm also working on transparent
> > compression.  I'm watching this thread closely so that I can
> > implement a compression library alongside the crypto library.  If
> > there is any interest or benefit, I would be glad to work together
> > so that the two can be done cooperatively at the same time.
> 
> I can't imagine quickly how compression code can be shared with crypto.
> The basic approach for compression would be that X pages can be compressed into
> small number of pages, Y, which can be a X to Y mapping.
> But, this per-file encryption supports only 1 to 1 4KB mapping, so that it could
> be quite a simple implementation.

No, I don't intend to share actual code with crypto -- at least not much. 
I'm more interested in looking at how the crypto layer is implemented to
give me clues about how to implement a compression layer.

> Could you elaborate on your approach or design? Or, codes?
> Whatever, IMO, it needs to implement it by any filesystem first.

I don't really have any working code yet.  I will probably get to that in
the coming few weeks.  Right now I'm still working with the ugly VFS
stacking implementation that I posted initially.

The thing that I have done is dismissed the standard compression framing
formats.

zlib (and gzip) are designed for streaming and it is quite difficult to
implement random access on it.  See the example code in the zlib source,
zran.c.  It's not really tenable because 32kb of prior data is required to
be kept as priming information.  Even doing fully encapsulated blocks with
Z_FINISH, there is still no way to skip over data without decompressing it
first to build an index.

lz4 is somewhat better in that blocks are self contained.  But block lengths
must be read sequentially.  This means that reading an arbitrary position in
a file requires a proportional number of reads to find the desired block.

So, I am working with a simple framing format that I threw together.  The
header has a compression method (zlib or lz4), block size, original input
size, and a block map.

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

* Re: [PATCH 03/18] f2fs crypto: declare some definitions for f2fs encryption feature
  2015-05-14  0:37       ` Dave Chinner
  2015-05-14  1:56         ` Jaegeuk Kim
@ 2015-05-16 13:24         ` Theodore Ts'o
  2015-05-16 17:13           ` Tom Marshall
  1 sibling, 1 reply; 34+ messages in thread
From: Theodore Ts'o @ 2015-05-16 13:24 UTC (permalink / raw)
  To: Dave Chinner; +Cc: Jaegeuk Kim, linux-kernel, linux-fsdevel, linux-f2fs-devel

On Thu, May 14, 2015 at 10:37:21AM +1000, Dave Chinner wrote:
> > 
> > AFAIK, Ted wants to push the codes as a crypto library into fs/ finally, so
> > I believe most part of crypto codes are common.
> 
> Can I suggest fs/crypto/ if there are going to be multiple files?

Yes, I think we definitely want to do this as fs/crypto; it makes
things a bit more straightforward on a number of fronts; not just from
a Makefile point of view, but also in setting up a separate tree to
push into linux-next.

> > But, in order to realize that quickly, Ted implemented the feature to finalize
> > on-disk and in-memory design in EXT4 as a first step.
> > Then, I've been catching up and validating its design by implementing it in
> > F2FS, which also intends to figure out what crypto codes can be exactly common.
> 
> Excellent. That will make it easier and less error prone for other
> filesystems to implement it, too!

Yeah, that's why I figured it would be easier to get it into ext4 and
f2fs first.  We can keep the functions in sync for a release or so,
and then we can start creating patches that gradually move each of
fs/{ext4,f2fs}/{crypto,crypo_fname,crypto_policy}.c, etc. into a
common fs/crypto directory.  I'm sure there will be a bit of
refactoring that will be necessary as we go along, but it will be a
lot easier to validate that we've gotten things once we have to file
systems both depending on the common code.

If Michael and I had tried to create fs/cryto up-front, I'm *sure* we
would have gotten a number of things wrong.  :-)

> > Meanwhile, surely I've been working on writing patches to push them into fs/;
> > currenlty, I did for cryto.c and will do for crypto_key.c and crypto_fname.c.
> > But, it needs to think about crypto_policy.c differently, since it may depend
> > on how each filesystem stores the policy information respectively; we cannot
> > push all the filesystems should use xattrs, right?
> 
> All filesystems likely to implement per-file crypto support xattrs,
> and this is exactly what xattrs are designed for. e.g. we already
> require xattrs for generic security labels, ACLs, etc. Hence
> per-file crypto information should also use a common, shared xattr
> format. That way it only needs to be implemented once in the generic
> code and there's very little (hopefully nothing!) each filesystem
> has to customise to store the crypto information for each file.

Yes, absolutely.  What's going to be more difficult in the long run is
when we want to support per-block data integrity, using (for example)
AES/GCM, which will require 32 bytes of per-block "authentication tag"
(think Message Authentication Code) that have to be committed
atomically with the data block write.

Quite frankly, this is going to be much easier for COW file systems
like f2fs and btrfs.  I have some ideas about how to do this for
update-in-place file systems (using either a compression hack and/or
storing two generations worth of authentication tag per block), but
it's not pretty.  (Or in the case of ext4 we could use
data=journalling mode, but that's even less pretty from a performance
standpoint.)

Cheers,

					- Ted

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

* Re: [PATCH 03/18] f2fs crypto: declare some definitions for f2fs encryption feature
  2015-05-16 13:24         ` Theodore Ts'o
@ 2015-05-16 17:13           ` Tom Marshall
  0 siblings, 0 replies; 34+ messages in thread
From: Tom Marshall @ 2015-05-16 17:13 UTC (permalink / raw)
  To: Theodore Ts'o, Dave Chinner, Jaegeuk Kim, linux-kernel,
	linux-fsdevel, linux-f2fs-devel

On Sat, May 16, 2015 at 09:24:03AM -0400, Theodore Ts'o wrote:
> [...] What's going to be more difficult in the long run is
> when we want to support per-block data integrity, using (for example)
> AES/GCM, which will require 32 bytes of per-block "authentication tag"
> (think Message Authentication Code) that have to be committed
> atomically with the data block write.
> 
> Quite frankly, this is going to be much easier for COW file systems
> like f2fs and btrfs.  I have some ideas about how to do this for
> update-in-place file systems (using either a compression hack and/or
> storing two generations worth of authentication tag per block), but
> it's not pretty.  (Or in the case of ext4 we could use
> data=journalling mode, but that's even less pretty from a performance
> standpoint.)

It seems to me that compression has a similar issue with framing metadata. 
The block map can be stored upfront, which then would require two writes to
update a data block.  Or it can be stored inline, which then would require
scanning through the file sequentially for random access.

The big difference, of course, is that in the case of compression, we have
the luxury of assuming or mandating read-only/read-mostly.

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

* Re: [PATCH 03/18] f2fs crypto: declare some definitions for f2fs encryption feature
  2015-05-16  4:47               ` Tom Marshall
@ 2015-05-18  6:24                 ` Jaegeuk Kim
  0 siblings, 0 replies; 34+ messages in thread
From: Jaegeuk Kim @ 2015-05-18  6:24 UTC (permalink / raw)
  To: Tom Marshall; +Cc: Dave Chinner, linux-kernel, linux-fsdevel, linux-f2fs-devel

On Fri, May 15, 2015 at 09:47:05PM -0700, Tom Marshall wrote:
> On Fri, May 15, 2015 at 06:14:24PM -0700, Jaegeuk Kim wrote:
> > On Thu, May 14, 2015 at 09:50:44AM -0700, Tom Marshall wrote:
> > > Please keep in mind that I'm also working on transparent
> > > compression.  I'm watching this thread closely so that I can
> > > implement a compression library alongside the crypto library.  If
> > > there is any interest or benefit, I would be glad to work together
> > > so that the two can be done cooperatively at the same time.
> > 
> > I can't imagine quickly how compression code can be shared with crypto.
> > The basic approach for compression would be that X pages can be compressed into
> > small number of pages, Y, which can be a X to Y mapping.
> > But, this per-file encryption supports only 1 to 1 4KB mapping, so that it could
> > be quite a simple implementation.
> 
> No, I don't intend to share actual code with crypto -- at least not much. 
> I'm more interested in looking at how the crypto layer is implemented to
> give me clues about how to implement a compression layer.

Ok, I see.

Currently, I've been writing up fs/crypto having shared crypto codes between
ext4 and f2fs; I refactored existing codes a little bit though.

I'm approaching to introduce a fscypt_operations for each filesystems like this.

struct fscrypt_operations {
	int (*get_context)(struct inode *, void *, size_t, void *);
	int (*set_context)(struct inode *, const void *, size_t, int, void *);
	int (*prepare_new_context)(struct inode *);
	bool (*is_encrypted)(struct inode *);
	bool (*empty_dir)(struct inode *);
	unsigned (*max_namelen)(struct inode *);
};

And, the following two basic functions will be used by filesystems:
fscrypt_encrypt_page() and fscrypt_decrypt_page().

> 
> > Could you elaborate on your approach or design? Or, codes?
> > Whatever, IMO, it needs to implement it by any filesystem first.
> 
> I don't really have any working code yet.  I will probably get to that in
> the coming few weeks.  Right now I'm still working with the ugly VFS
> stacking implementation that I posted initially.
> 
> The thing that I have done is dismissed the standard compression framing
> formats.
> 
> zlib (and gzip) are designed for streaming and it is quite difficult to
> implement random access on it.  See the example code in the zlib source,
> zran.c.  It's not really tenable because 32kb of prior data is required to
> be kept as priming information.  Even doing fully encapsulated blocks with
> Z_FINISH, there is still no way to skip over data without decompressing it
> first to build an index.
> 
> lz4 is somewhat better in that blocks are self contained.  But block lengths
> must be read sequentially.  This means that reading an arbitrary position in
> a file requires a proportional number of reads to find the desired block.
> 
> So, I am working with a simple framing format that I threw together.  The
> header has a compression method (zlib or lz4), block size, original input
> size, and a block map.

Thank you for sharing the approach, and it makes sense to improve random
read performance.

Thanks,

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

end of thread, other threads:[~2015-05-18  6:24 UTC | newest]

Thread overview: 34+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-05-09  4:20 [PATCH 01/18] f2fs: avoid value overflow in showing current status Jaegeuk Kim
2015-05-09  4:20 ` [PATCH 02/18] f2fs: report unwritten area in f2fs_fiemap Jaegeuk Kim
2015-05-09  4:20 ` [PATCH 03/18] f2fs crypto: declare some definitions for f2fs encryption feature Jaegeuk Kim
2015-05-13  2:02   ` Dave Chinner
2015-05-13  6:48     ` Jaegeuk Kim
2015-05-14  0:37       ` Dave Chinner
2015-05-14  1:56         ` Jaegeuk Kim
2015-05-14 16:50           ` Tom Marshall
2015-05-16  1:14             ` Jaegeuk Kim
2015-05-16  4:47               ` Tom Marshall
2015-05-18  6:24                 ` Jaegeuk Kim
2015-05-16 13:24         ` Theodore Ts'o
2015-05-16 17:13           ` Tom Marshall
2015-05-09  4:20 ` [PATCH 04/18] f2fs crypto: add f2fs encryption Kconfig Jaegeuk Kim
2015-05-09  4:20 ` [PATCH 05/18] f2fs crypto: add encryption xattr support Jaegeuk Kim
2015-05-09  4:20 ` [PATCH 06/18] f2fs crypto: add encryption policy and password salt support Jaegeuk Kim
2015-05-09  4:20 ` [PATCH 07/18] f2fs crypto: add f2fs encryption facilities Jaegeuk Kim
2015-05-09  4:20 ` [PATCH 08/18] f2fs crypto: add encryption key management facilities Jaegeuk Kim
2015-05-09  4:20 ` [PATCH 09/18] f2fs crypto: filename encryption facilities Jaegeuk Kim
2015-05-09  4:20 ` [PATCH 10/18] f2fs crypto: activate encryption support for fs APIs Jaegeuk Kim
2015-05-09  4:20 ` [PATCH 11/18] f2fs crypto: add encryption support in read/write paths Jaegeuk Kim
2015-05-09  4:20 ` [PATCH 12/18] f2fs crypto: add filename encryption for f2fs_add_link Jaegeuk Kim
2015-05-09  4:20 ` [PATCH 13/18] f2fs crypto: add filename encryption for f2fs_readdir Jaegeuk Kim
2015-05-09  4:20 ` [PATCH 14/18] f2fs crypto: add filename encryption for f2fs_lookup Jaegeuk Kim
2015-05-11  2:52   ` [f2fs-dev] " hujianyang
2015-05-11  5:12     ` Jaegeuk Kim
2015-05-11  6:38       ` hujianyang
2015-05-09  4:20 ` [PATCH 15/18] f2fs crypto: add filename encryption for roll-forward recovery Jaegeuk Kim
2015-05-09  4:20 ` [PATCH 16/18] f2fs crypto: add symlink encryption Jaegeuk Kim
2015-05-09  4:25   ` Al Viro
2015-05-11  5:15     ` Jaegeuk Kim
2015-05-12  3:48   ` [PATCH 16/18 v2] " Jaegeuk Kim
2015-05-09  4:20 ` [PATCH 17/18] f2fs crypto: fix missing key when reading a page Jaegeuk Kim
2015-05-09  4:20 ` [PATCH 18/18] f2fs crypto: remove checking key context during lookup Jaegeuk Kim

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).