This patch series implements file level encryption for UBIFS.
It makes use of the generic fscrypto framework as used by ext4 and f2fs.
Among file contents also file names are encrypted,
for more details on fscrypto please see [0] and [1].
To support encrypted files in UBIFS multiple preparations were needed.
Patches 1/26 and 2/26 touch fscrypto code and add support to work on
buffers instead of pages.
UBIFS has a different IO model than ext4 and f2fs because it uses MTD
instead of the block layer. But the changes are small and non-invasive.
In UBIFS itself the biggest change was supporting hash lookups.
Now UBIFS is able to provide a 64bit cookie which can be used later
to locate a file. This change will also allow us implementing proper
NFS and telldir() support, but that will be a different patch series.
Because of these changes the UBIFS write version is now 5.
As userspace component I'm currently using e4crypt from e2fsprogs with
EXT2FS_KEY_DESC_PREFIX set to "fscrypt:" instead of "ext4:".
A common tool will hopefully emerge soon[2]. I don't want an UBIFS
specific tool in mtd-utils.
The series is based in 4.8-rc7, but will be rebased soon to 4.9-rcX.
I'm still sending the series now to get feedback as soon as possible.
It can be obtained from:
git://git.infradead.org/users/rw/linux.git ubifs_crypt_v0
[0] https://lwn.net/Articles/639427/
[1] https://docs.google.com/document/d/1ft26lUQyuSpiu6VleP70_npaWdRfXFoNnB8JYnykNTg/edit
[2] http://www.spinics.net/lists/linux-fsdevel/msg103107.html
Richard Weinberger (26):
fscrypto: Add buffer operations
fscrypto: Constify struct inode pointer
ubifs: Export ubifs_check_dir_empty()
ubifs: Export xattr get and set functions
ubifs: Define UBIFS crypto context xattr
ubifs: Add skeleton for fscrypto
ubifs: Massage ubifs_listxattr() for encryption context
ubifs: Implement directory open operation
ubifs: Implement file open operation
ubifs: Enforce crypto policy in ->link and ->rename
ubifs: Preload crypto context in ->lookup()
ubifs: Massage assert in ubifs_xattr_set() wrt. fscrypto
ubifs: Enforce crypto policy in mmap
ubifs: Introduce new data node field, compr_size
ubifs: Implement encrypt/decrypt for all IO
ubifs: Relax checks in ubifs_validate_entry()
ubifs: Make r5 hash binary string aware
ubifs: Constify struct inode pointer in ubifs_crypt_is_encrypted()
ubifs: Implement encrypted filenames
ubifs: Add support for encrypted symlinks
ubifs: Rename tnc_read_node_nm
ubifs: Add full hash lookup support
ubifs: Use a random number for cookies
ubifs: Implement UBIFS_FLG_DOUBLE_HASH
ubifs: Implement UBIFS_FLG_ENCRYPTION
ubifs: Raise write version to 5
fs/crypto/crypto.c | 65 +++++--
fs/ubifs/Kconfig | 11 ++
fs/ubifs/Makefile | 1 +
fs/ubifs/crypto.c | 45 +++++
fs/ubifs/debug.c | 14 +-
fs/ubifs/dir.c | 431 +++++++++++++++++++++++++++++++++++++++--------
fs/ubifs/file.c | 130 +++++++++++++-
fs/ubifs/ioctl.c | 40 +++++
fs/ubifs/journal.c | 202 ++++++++++++++--------
fs/ubifs/key.h | 21 +--
fs/ubifs/replay.c | 10 +-
fs/ubifs/sb.c | 59 +++++++
fs/ubifs/super.c | 17 +-
fs/ubifs/tnc.c | 168 ++++++++++++++----
fs/ubifs/ubifs-media.h | 29 +++-
fs/ubifs/ubifs.h | 82 ++++++++-
fs/ubifs/xattr.c | 112 +++++++-----
include/linux/fscrypto.h | 30 +++-
18 files changed, 1202 insertions(+), 265 deletions(-)
create mode 100644 fs/ubifs/crypto.c
--
2.7.3
Like ext4 UBIFS will store the crypto context in a xattr
attribute.
Signed-off-by: Richard Weinberger <[email protected]>
---
fs/ubifs/ubifs-media.h | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/fs/ubifs/ubifs-media.h b/fs/ubifs/ubifs-media.h
index e24380cf46ed..d47e9569b3de 100644
--- a/fs/ubifs/ubifs-media.h
+++ b/fs/ubifs/ubifs-media.h
@@ -301,6 +301,13 @@ enum {
#define UBIFS_MAX_NODE_SZ UBIFS_MAX_INO_NODE_SZ
/*
+ * xattr name of UBIFS encryption context, we don't use a prefix
+ * nor a long name to not waste space on the flash.
+ */
+#define UBIFS_XATTR_NAME_ENCRYPTION_CONTEXT "c"
+
+
+/*
* On-flash inode flags.
*
* UBIFS_COMPR_FL: use compression for this inode
--
2.7.3
Starting with version 5 the following properties change:
- UBIFS_FLG_DOUBLE_HASH is mandatory
- UBIFS_FLG_ENCRYPTION is optional but depdens on UBIFS_FLG_DOUBLE_HASH
- Filesystems with unknown super block flags will be rejected, this
allows us in future to add new features without raising the UBIFS
write version.
Signed-off-by: Richard Weinberger <[email protected]>
---
fs/ubifs/sb.c | 17 +++++++++++++++++
fs/ubifs/ubifs-media.h | 4 +++-
2 files changed, 20 insertions(+), 1 deletion(-)
diff --git a/fs/ubifs/sb.c b/fs/ubifs/sb.c
index 54cef70ea16f..7f1ead29e727 100644
--- a/fs/ubifs/sb.c
+++ b/fs/ubifs/sb.c
@@ -466,6 +466,16 @@ static int validate_sb(struct ubifs_info *c, struct ubifs_sb_node *sup)
goto failed;
}
+ if (!c->double_hash && c->fmt_version >= 5) {
+ err = 16;
+ goto failed;
+ }
+
+ if (c->encrypted && c->fmt_version < 5) {
+ err = 17;
+ goto failed;
+ }
+
return 0;
failed:
@@ -624,6 +634,13 @@ int ubifs_read_superblock(struct ubifs_info *c)
c->double_hash = !!(sup_flags & UBIFS_FLG_DOUBLE_HASH);
c->encrypted = !!(sup_flags & UBIFS_FLG_ENCRYPTION);
+ if ((sup_flags & ~UBIFS_FLG_MASK) != 0) {
+ ubifs_err(c, "Unknown feature flags found: %#x",
+ sup_flags & ~UBIFS_FLG_MASK);
+ err = -EINVAL;
+ goto out;
+ }
+
#ifndef CONFIG_UBIFS_FS_ENCRYPTION
if (c->encrypted) {
ubifs_err(c, "file system contains encrypted files but UBIFS"
diff --git a/fs/ubifs/ubifs-media.h b/fs/ubifs/ubifs-media.h
index bdc7935a5e41..e8c23c9d4f4a 100644
--- a/fs/ubifs/ubifs-media.h
+++ b/fs/ubifs/ubifs-media.h
@@ -46,7 +46,7 @@
* UBIFS went into mainline kernel with format version 4. The older formats
* were development formats.
*/
-#define UBIFS_FORMAT_VERSION 4
+#define UBIFS_FORMAT_VERSION 5
/*
* Read-only compatibility version. If the UBIFS format is changed, older UBIFS
@@ -429,6 +429,8 @@ enum {
UBIFS_FLG_ENCRYPTION = 0x10,
};
+#define UBIFS_FLG_MASK (UBIFS_FLG_BIGLPT|UBIFS_FLG_SPACE_FIXUP|UBIFS_FLG_DOUBLE_HASH|UBIFS_FLG_ENCRYPTION)
+
/**
* struct ubifs_ch - common header node.
* @magic: UBIFS node magic number (%UBIFS_NODE_MAGIC)
--
2.7.3
When a file is moved or linked into another directory
its current crypto policy has to be compatible with the
target policy.
Signed-off-by: Richard Weinberger <[email protected]>
---
fs/ubifs/dir.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c
index e80de3852c3e..a77edc5ca5c9 100644
--- a/fs/ubifs/dir.c
+++ b/fs/ubifs/dir.c
@@ -544,6 +544,10 @@ static int ubifs_link(struct dentry *old_dentry, struct inode *dir,
ubifs_assert(inode_is_locked(dir));
ubifs_assert(inode_is_locked(inode));
+ if (ubifs_crypt_is_encrypted(dir) &&
+ !fscrypt_has_permitted_context(dir, inode))
+ return -EPERM;
+
err = dbg_check_synced_i_size(c, inode);
if (err)
return err;
@@ -1027,6 +1031,11 @@ static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (unlink)
ubifs_assert(inode_is_locked(new_inode));
+ if (old_dir != new_dir) {
+ if (ubifs_crypt_is_encrypted(new_dir) &&
+ !fscrypt_has_permitted_context(new_dir, old_inode))
+ return -EPERM;
+ }
if (unlink && is_dir) {
err = ubifs_check_dir_empty(new_inode);
--
2.7.3
This feature flag indicates that the filesystem contains encrypted
files.
Signed-off-by: Richard Weinberger <[email protected]>
---
fs/ubifs/ioctl.c | 5 +++++
fs/ubifs/sb.c | 40 ++++++++++++++++++++++++++++++++++++++++
fs/ubifs/ubifs-media.h | 2 ++
fs/ubifs/ubifs.h | 3 +++
4 files changed, 50 insertions(+)
diff --git a/fs/ubifs/ioctl.c b/fs/ubifs/ioctl.c
index 6bb5b35050de..3d10f5525274 100644
--- a/fs/ubifs/ioctl.c
+++ b/fs/ubifs/ioctl.c
@@ -183,6 +183,7 @@ long ubifs_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
}
case FS_IOC_SET_ENCRYPTION_POLICY: {
#ifdef CONFIG_UBIFS_FS_ENCRYPTION
+ struct ubifs_info *c = inode->i_sb->s_fs_info;
struct fscrypt_policy policy;
if (copy_from_user(&policy,
@@ -190,6 +191,10 @@ long ubifs_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
sizeof(policy)))
return -EFAULT;
+ err = ubifs_enable_encryption(c);
+ if (err)
+ return err;
+
err = fscrypt_process_policy(file, &policy);
return err;
diff --git a/fs/ubifs/sb.c b/fs/ubifs/sb.c
index 4a2b4c361587..54cef70ea16f 100644
--- a/fs/ubifs/sb.c
+++ b/fs/ubifs/sb.c
@@ -622,6 +622,16 @@ int ubifs_read_superblock(struct ubifs_info *c)
c->big_lpt = !!(sup_flags & UBIFS_FLG_BIGLPT);
c->space_fixup = !!(sup_flags & UBIFS_FLG_SPACE_FIXUP);
c->double_hash = !!(sup_flags & UBIFS_FLG_DOUBLE_HASH);
+ c->encrypted = !!(sup_flags & UBIFS_FLG_ENCRYPTION);
+
+#ifndef CONFIG_UBIFS_FS_ENCRYPTION
+ if (c->encrypted) {
+ ubifs_err(c, "file system contains encrypted files but UBIFS"
+ " was built without crypto support.");
+ err = -EINVAL;
+ goto out;
+ }
+#endif
/* Automatically increase file system size to the maximum size */
c->old_leb_cnt = c->leb_cnt;
@@ -809,3 +819,33 @@ int ubifs_fixup_free_space(struct ubifs_info *c)
ubifs_msg(c, "free space fixup complete");
return err;
}
+
+int ubifs_enable_encryption(struct ubifs_info *c)
+{
+ int err;
+ struct ubifs_sb_node *sup;
+
+ if (c->encrypted)
+ return 0;
+
+ if (c->ro_mount || c->ro_media)
+ return -EROFS;
+
+ if (c->fmt_version < 5) {
+ ubifs_err(c, "on-flash format version 5 is needed for encryption");
+ return -EINVAL;
+ }
+
+ sup = ubifs_read_sb_node(c);
+ if (IS_ERR(sup))
+ return PTR_ERR(sup);
+
+ sup->flags |= cpu_to_le32(UBIFS_FLG_ENCRYPTION);
+
+ err = ubifs_write_sb_node(c, sup);
+ if (!err)
+ c->encrypted = 1;
+ kfree(sup);
+
+ return err;
+}
diff --git a/fs/ubifs/ubifs-media.h b/fs/ubifs/ubifs-media.h
index 0cbdc6b70a00..bdc7935a5e41 100644
--- a/fs/ubifs/ubifs-media.h
+++ b/fs/ubifs/ubifs-media.h
@@ -420,11 +420,13 @@ enum {
* UBIFS_FLG_SPACE_FIXUP: first-mount "fixup" of free space within LEBs needed
* UBIFS_FLG_DOUBLE_HASH: store a 32bit cookie in directory entry nodes to
* support 64bit cookies for lookups by hash
+ * UBIFS_FLG_ENCRYPTION: this filesystem contains encrypted files
*/
enum {
UBIFS_FLG_BIGLPT = 0x02,
UBIFS_FLG_SPACE_FIXUP = 0x04,
UBIFS_FLG_DOUBLE_HASH = 0x08,
+ UBIFS_FLG_ENCRYPTION = 0x10,
};
/**
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index 59d55887b3d8..85e2929aa6db 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -1015,6 +1015,7 @@ struct ubifs_debug_info;
* @big_lpt: flag that LPT is too big to write whole during commit
* @space_fixup: flag indicating that free space in LEBs needs to be cleaned up
* @double_hash: flag indicating that we can do lookups by hash
+ * @encrypted: flag indicating that this file system contains encrypted files
* @no_chk_data_crc: do not check CRCs when reading data nodes (except during
* recovery)
* @bulk_read: enable bulk-reads
@@ -1258,6 +1259,7 @@ struct ubifs_info {
unsigned int big_lpt:1;
unsigned int space_fixup:1;
unsigned int double_hash:1;
+ unsigned int encrypted:1;
unsigned int no_chk_data_crc:1;
unsigned int bulk_read:1;
unsigned int default_compr:2;
@@ -1651,6 +1653,7 @@ int ubifs_read_superblock(struct ubifs_info *c);
struct ubifs_sb_node *ubifs_read_sb_node(struct ubifs_info *c);
int ubifs_write_sb_node(struct ubifs_info *c, struct ubifs_sb_node *sup);
int ubifs_fixup_free_space(struct ubifs_info *c);
+int ubifs_enable_encryption(struct ubifs_info *c);
/* replay.c */
int ubifs_validate_entry(struct ubifs_info *c,
--
2.7.3
This feature flag indicates that all directory entry nodes have a 32bit
cookie set and therefore UBIFS is allowed to perform lookups by hash.
Signed-off-by: Richard Weinberger <[email protected]>
---
fs/ubifs/journal.c | 14 +++++++++++---
fs/ubifs/sb.c | 2 ++
fs/ubifs/tnc.c | 3 +++
fs/ubifs/ubifs-media.h | 3 +++
fs/ubifs/ubifs.h | 2 ++
5 files changed, 21 insertions(+), 3 deletions(-)
diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c
index dcecfbfbf40e..c8a483a0df2c 100644
--- a/fs/ubifs/journal.c
+++ b/fs/ubifs/journal.c
@@ -501,6 +501,14 @@ static void mark_inode_clean(struct ubifs_info *c, struct ubifs_inode *ui)
ui->dirty = 0;
}
+static void set_dent_cookie(struct ubifs_info *c, struct ubifs_dent_node *dent)
+{
+ if (c->double_hash)
+ dent->cookie = prandom_u32();
+ else
+ dent->cookie = 0;
+}
+
/**
* ubifs_jnl_update - update inode.
* @c: UBIFS file-system description object
@@ -589,7 +597,7 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
dent->nlen = cpu_to_le16(fname_len(nm));
memcpy(dent->name, fname_name(nm), fname_len(nm));
dent->name[fname_len(nm)] = '\0';
- dent->cookie = prandom_u32();
+ set_dent_cookie(c, dent);
zero_dent_node_unused(dent);
ubifs_prep_grp_node(c, dent, dlen, 0);
@@ -993,7 +1001,7 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
dent->nlen = cpu_to_le16(fname_len(new_nm));
memcpy(dent->name, fname_name(new_nm), fname_len(new_nm));
dent->name[fname_len(new_nm)] = '\0';
- dent->cookie = prandom_u32();
+ set_dent_cookie(c, dent);
zero_dent_node_unused(dent);
ubifs_prep_grp_node(c, dent, dlen1, 0);
@@ -1006,7 +1014,7 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
dent2->nlen = cpu_to_le16(fname_len(old_nm));
memcpy(dent2->name, fname_name(old_nm), fname_len(old_nm));
dent2->name[fname_len(old_nm)] = '\0';
- dent2->cookie = prandom_u32();
+ set_dent_cookie(c, dent2);
zero_dent_node_unused(dent2);
ubifs_prep_grp_node(c, dent2, dlen2, 0);
diff --git a/fs/ubifs/sb.c b/fs/ubifs/sb.c
index 3cbb904a6d7d..4a2b4c361587 100644
--- a/fs/ubifs/sb.c
+++ b/fs/ubifs/sb.c
@@ -163,6 +163,7 @@ static int create_default_filesystem(struct ubifs_info *c)
tmp64 = (long long)max_buds * c->leb_size;
if (big_lpt)
sup_flags |= UBIFS_FLG_BIGLPT;
+ sup_flags |= UBIFS_FLG_DOUBLE_HASH;
sup->ch.node_type = UBIFS_SB_NODE;
sup->key_hash = UBIFS_KEY_HASH_R5;
@@ -620,6 +621,7 @@ int ubifs_read_superblock(struct ubifs_info *c)
memcpy(&c->uuid, &sup->uuid, 16);
c->big_lpt = !!(sup_flags & UBIFS_FLG_BIGLPT);
c->space_fixup = !!(sup_flags & UBIFS_FLG_SPACE_FIXUP);
+ c->double_hash = !!(sup_flags & UBIFS_FLG_DOUBLE_HASH);
/* Automatically increase file system size to the maximum size */
c->old_leb_cnt = c->leb_cnt;
diff --git a/fs/ubifs/tnc.c b/fs/ubifs/tnc.c
index 1eaf994addb4..02205a68f851 100644
--- a/fs/ubifs/tnc.c
+++ b/fs/ubifs/tnc.c
@@ -1939,6 +1939,9 @@ int ubifs_tnc_lookup_dh(struct ubifs_info *c, const union ubifs_key *key,
int err;
const struct ubifs_dent_node *dent = node;
+ if (!c->double_hash)
+ return -EOPNOTSUPP;
+
/*
* We assume that in most of the cases there are no name collisions and
* 'ubifs_tnc_lookup()' returns us the right direntry.
diff --git a/fs/ubifs/ubifs-media.h b/fs/ubifs/ubifs-media.h
index 249124d9a801..0cbdc6b70a00 100644
--- a/fs/ubifs/ubifs-media.h
+++ b/fs/ubifs/ubifs-media.h
@@ -418,10 +418,13 @@ enum {
*
* UBIFS_FLG_BIGLPT: if "big" LPT model is used if set
* UBIFS_FLG_SPACE_FIXUP: first-mount "fixup" of free space within LEBs needed
+ * UBIFS_FLG_DOUBLE_HASH: store a 32bit cookie in directory entry nodes to
+ * support 64bit cookies for lookups by hash
*/
enum {
UBIFS_FLG_BIGLPT = 0x02,
UBIFS_FLG_SPACE_FIXUP = 0x04,
+ UBIFS_FLG_DOUBLE_HASH = 0x08,
};
/**
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index 294e4047bcc0..59d55887b3d8 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -1014,6 +1014,7 @@ struct ubifs_debug_info;
*
* @big_lpt: flag that LPT is too big to write whole during commit
* @space_fixup: flag indicating that free space in LEBs needs to be cleaned up
+ * @double_hash: flag indicating that we can do lookups by hash
* @no_chk_data_crc: do not check CRCs when reading data nodes (except during
* recovery)
* @bulk_read: enable bulk-reads
@@ -1256,6 +1257,7 @@ struct ubifs_info {
unsigned int big_lpt:1;
unsigned int space_fixup:1;
+ unsigned int double_hash:1;
unsigned int no_chk_data_crc:1;
unsigned int bulk_read:1;
unsigned int default_compr:2;
--
2.7.3
Signed-off-by: Richard Weinberger <[email protected]>
---
fs/ubifs/debug.c | 14 +--
fs/ubifs/dir.c | 284 +++++++++++++++++++++++++++++++++++++++++++----------
fs/ubifs/journal.c | 76 +++++++-------
fs/ubifs/key.h | 19 ++--
fs/ubifs/replay.c | 8 +-
fs/ubifs/tnc.c | 54 +++++-----
fs/ubifs/ubifs.h | 19 ++--
fs/ubifs/xattr.c | 42 ++++----
8 files changed, 350 insertions(+), 166 deletions(-)
diff --git a/fs/ubifs/debug.c b/fs/ubifs/debug.c
index 69e287e20732..1e712a364680 100644
--- a/fs/ubifs/debug.c
+++ b/fs/ubifs/debug.c
@@ -233,7 +233,7 @@ static void dump_ch(const struct ubifs_ch *ch)
void ubifs_dump_inode(struct ubifs_info *c, const struct inode *inode)
{
const struct ubifs_inode *ui = ubifs_inode(inode);
- struct qstr nm = { .name = NULL };
+ struct fscrypt_name nm = {0};
union ubifs_key key;
struct ubifs_dent_node *dent, *pdent = NULL;
int count = 2;
@@ -289,8 +289,8 @@ void ubifs_dump_inode(struct ubifs_info *c, const struct inode *inode)
pr_err("\t%d: %s (%s)\n",
count++, dent->name, get_dent_type(dent->type));
- nm.name = dent->name;
- nm.len = le16_to_cpu(dent->nlen);
+ fname_name(&nm) = dent->name;
+ fname_len(&nm) = le16_to_cpu(dent->nlen);
kfree(pdent);
pdent = dent;
key_read(c, &dent->key, &key);
@@ -1107,7 +1107,7 @@ int dbg_check_dir(struct ubifs_info *c, const struct inode *dir)
unsigned int nlink = 2;
union ubifs_key key;
struct ubifs_dent_node *dent, *pdent = NULL;
- struct qstr nm = { .name = NULL };
+ struct fscrypt_name nm = {0};
loff_t size = UBIFS_INO_NODE_SZ;
if (!dbg_is_chk_gen(c))
@@ -1128,9 +1128,9 @@ int dbg_check_dir(struct ubifs_info *c, const struct inode *dir)
return err;
}
- nm.name = dent->name;
- nm.len = le16_to_cpu(dent->nlen);
- size += CALC_DENT_SIZE(nm.len);
+ fname_name(&nm) = dent->name;
+ fname_len(&nm) = le16_to_cpu(dent->nlen);
+ size += CALC_DENT_SIZE(fname_len(&nm));
if (dent->type == UBIFS_ITYPE_DIR)
nlink += 1;
kfree(pdent);
diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c
index 7ee32768ba19..723bae4dec30 100644
--- a/fs/ubifs/dir.c
+++ b/fs/ubifs/dir.c
@@ -196,13 +196,13 @@ struct inode *ubifs_new_inode(struct ubifs_info *c, struct inode *dir,
static int dbg_check_name(const struct ubifs_info *c,
const struct ubifs_dent_node *dent,
- const struct qstr *nm)
+ const struct fscrypt_name *nm)
{
if (!dbg_is_chk_gen(c))
return 0;
- if (le16_to_cpu(dent->nlen) != nm->len)
+ if (le16_to_cpu(dent->nlen) != fname_len(nm))
return -EINVAL;
- if (memcmp(dent->name, nm->name, nm->len))
+ if (memcmp(dent->name, fname_name(nm), fname_len(nm)))
return -EINVAL;
return 0;
}
@@ -215,6 +215,7 @@ static struct dentry *ubifs_lookup(struct inode *dir, struct dentry *dentry,
struct inode *inode = NULL;
struct ubifs_dent_node *dent;
struct ubifs_info *c = dir->i_sb->s_fs_info;
+ struct fscrypt_name nm;
dbg_gen("'%pd' in dir ino %lu", dentry, dir->i_ino);
@@ -233,27 +234,42 @@ static struct dentry *ubifs_lookup(struct inode *dir, struct dentry *dentry,
return ERR_PTR(err);
}
- if (dentry->d_name.len > UBIFS_MAX_NLEN)
- return ERR_PTR(-ENAMETOOLONG);
+ err = fscrypt_setup_filename(dir, &dentry->d_name, 1, &nm);
+ if (err)
+ return ERR_PTR(err);
+
+ if (fname_len(&nm) > UBIFS_MAX_NLEN) {
+ err = -ENAMETOOLONG;
+ goto out_fname;
+ }
dent = kmalloc(UBIFS_MAX_DENT_NODE_SZ, GFP_NOFS);
- if (!dent)
- return ERR_PTR(-ENOMEM);
+ if (!dent) {
+ err = -ENOMEM;
+ goto out_fname;
+ }
- dent_key_init(c, &key, dir->i_ino, &dentry->d_name);
+ if (nm.hash) {
+ ubifs_assert(fname_len(&nm) == 0);
+ ubifs_assert(fname_name(&nm) == NULL);
+ dent_key_init_hash(c, &key, dir->i_ino, nm.hash);
+ err = ubifs_tnc_lookup(c, &key, dent);
+ } else {
+ dent_key_init(c, &key, dir->i_ino, &nm);
+ err = ubifs_tnc_lookup_nm(c, &key, dent, &nm);
+ }
- err = ubifs_tnc_lookup_nm(c, &key, dent, &dentry->d_name);
if (err) {
if (err == -ENOENT) {
dbg_gen("not found");
goto done;
}
- goto out;
+ goto out_dent;
}
- if (dbg_check_name(c, dent, &dentry->d_name)) {
+ if (dbg_check_name(c, dent, &nm)) {
err = -EINVAL;
- goto out;
+ goto out_dent;
}
inode = ubifs_iget(dir->i_sb, le64_to_cpu(dent->inum));
@@ -266,11 +282,12 @@ static struct dentry *ubifs_lookup(struct inode *dir, struct dentry *dentry,
ubifs_err(c, "dead directory entry '%pd', error %d",
dentry, err);
ubifs_ro_mode(c, err);
- goto out;
+ goto out_dent;
}
done:
kfree(dent);
+ fscrypt_free_filename(&nm);
/*
* Note, d_splice_alias() would be required instead if we supported
* NFS.
@@ -278,8 +295,10 @@ done:
d_add(dentry, inode);
return NULL;
-out:
+out_dent:
kfree(dent);
+out_fname:
+ fscrypt_free_filename(&nm);
return ERR_PTR(err);
}
@@ -288,10 +307,11 @@ static int ubifs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
{
struct inode *inode;
struct ubifs_info *c = dir->i_sb->s_fs_info;
- int err, sz_change = CALC_DENT_SIZE(dentry->d_name.len);
struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1,
.dirtied_ino = 1 };
struct ubifs_inode *dir_ui = ubifs_inode(dir);
+ struct fscrypt_name nm;
+ int err, sz_change;
/*
* Budget request settings: new inode, new direntry, changing the
@@ -305,10 +325,16 @@ static int ubifs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
if (err)
return err;
+ err = fscrypt_setup_filename(dir, &dentry->d_name, 0, &nm);
+ if (err)
+ goto out_budg;
+
+ sz_change = CALC_DENT_SIZE(fname_len(&nm));
+
inode = ubifs_new_inode(c, dir, mode);
if (IS_ERR(inode)) {
err = PTR_ERR(inode);
- goto out_budg;
+ goto out_fname;
}
err = ubifs_init_security(dir, inode, &dentry->d_name);
@@ -319,12 +345,13 @@ static int ubifs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
dir->i_size += sz_change;
dir_ui->ui_size = dir->i_size;
dir->i_mtime = dir->i_ctime = inode->i_ctime;
- err = ubifs_jnl_update(c, dir, &dentry->d_name, inode, 0, 0);
+ err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0);
if (err)
goto out_cancel;
mutex_unlock(&dir_ui->ui_mutex);
ubifs_release_budget(c, &req);
+ fscrypt_free_filename(&nm);
insert_inode_hash(inode);
d_instantiate(dentry, inode);
return 0;
@@ -336,6 +363,8 @@ out_cancel:
out_inode:
make_bad_inode(inode);
iput(inode);
+out_fname:
+ fscrypt_free_filename(&nm);
out_budg:
ubifs_release_budget(c, &req);
ubifs_err(c, "cannot create regular file, error %d", err);
@@ -391,21 +420,36 @@ static unsigned int vfs_dent_type(uint8_t type)
*/
static int ubifs_readdir(struct file *file, struct dir_context *ctx)
{
- int err;
- struct qstr nm;
+ int err, fstr_real_len;
+ struct fscrypt_name nm;
+ struct fscrypt_str fstr = {0};
union ubifs_key key;
struct ubifs_dent_node *dent;
struct inode *dir = file_inode(file);
struct ubifs_info *c = dir->i_sb->s_fs_info;
+ bool encrypted = ubifs_crypt_is_encrypted(dir);
dbg_gen("dir ino %lu, f_pos %#llx", dir->i_ino, ctx->pos);
- if (ctx->pos > UBIFS_S_KEY_HASH_MASK || ctx->pos == 2)
+ if (ctx->pos > UBIFS_S_KEY_HASH_MASK || ctx->pos == 2) {
/*
* The directory was seek'ed to a senseless position or there
* are no more entries.
*/
return 0;
+ }
+
+ if (encrypted) {
+ err = fscrypt_get_encryption_info(dir);
+ if (err && err != -ENOKEY)
+ return err;
+
+ err = fscrypt_fname_alloc_buffer(dir, UBIFS_MAX_NLEN, &fstr);
+ if (err)
+ return err;
+
+ fstr_real_len = fstr.len;
+ }
if (file->f_version == 0) {
/*
@@ -428,12 +472,15 @@ static int ubifs_readdir(struct file *file, struct dir_context *ctx)
/* File positions 0 and 1 correspond to "." and ".." */
if (ctx->pos < 2) {
ubifs_assert(!file->private_data);
- if (!dir_emit_dots(file, ctx))
+ if (!dir_emit_dots(file, ctx)) {
+ if (encrypted)
+ fscrypt_fname_free_buffer(&fstr);
return 0;
+ }
/* Find the first entry in TNC and save it */
lowest_dent_key(c, &key, dir->i_ino);
- nm.name = NULL;
+ fname_len(&nm) = 0;
dent = ubifs_tnc_next_ent(c, &key, &nm);
if (IS_ERR(dent)) {
err = PTR_ERR(dent);
@@ -451,7 +498,7 @@ static int ubifs_readdir(struct file *file, struct dir_context *ctx)
* Find the entry corresponding to @ctx->pos or the closest one.
*/
dent_key_init_hash(c, &key, dir->i_ino, ctx->pos);
- nm.name = NULL;
+ fname_len(&nm) = 0;
dent = ubifs_tnc_next_ent(c, &key, &nm);
if (IS_ERR(dent)) {
err = PTR_ERR(dent);
@@ -468,15 +515,30 @@ static int ubifs_readdir(struct file *file, struct dir_context *ctx)
ubifs_assert(le64_to_cpu(dent->ch.sqnum) >
ubifs_inode(dir)->creat_sqnum);
- nm.len = le16_to_cpu(dent->nlen);
- if (!dir_emit(ctx, dent->name, nm.len,
+ fname_len(&nm) = le16_to_cpu(dent->nlen);
+ fname_name(&nm) = dent->name;
+
+ if (encrypted) {
+ fstr.len = fstr_real_len;
+
+ err = fscrypt_fname_disk_to_usr(dir, key_hash_flash(c, &dent->key), 0, &nm.disk_name, &fstr);
+ if (err < 0)
+ goto out;
+ } else {
+ fstr.len = fname_len(&nm);
+ fstr.name = fname_name(&nm);
+ }
+
+ if (!dir_emit(ctx, fstr.name, fstr.len,
le64_to_cpu(dent->inum),
- vfs_dent_type(dent->type)))
+ vfs_dent_type(dent->type))) {
+ if (encrypted)
+ fscrypt_fname_free_buffer(&fstr);
return 0;
+ }
/* Switch to the next entry */
key_read(c, &dent->key, &key);
- nm.name = dent->name;
dent = ubifs_tnc_next_ent(c, &key, &nm);
if (IS_ERR(dent)) {
err = PTR_ERR(dent);
@@ -493,6 +555,9 @@ out:
kfree(file->private_data);
file->private_data = NULL;
+ if (encrypted)
+ fscrypt_fname_free_buffer(&fstr);
+
if (err != -ENOENT) {
ubifs_err(c, "cannot find next direntry, error %d", err);
return err;
@@ -547,6 +612,7 @@ static int ubifs_link(struct dentry *old_dentry, struct inode *dir,
int err, sz_change = CALC_DENT_SIZE(dentry->d_name.len);
struct ubifs_budget_req req = { .new_dent = 1, .dirtied_ino = 2,
.dirtied_ino_d = ALIGN(ui->data_len, 8) };
+ struct fscrypt_name nm;
/*
* Budget request settings: new direntry, changing the target inode,
@@ -559,17 +625,29 @@ static int ubifs_link(struct dentry *old_dentry, struct inode *dir,
ubifs_assert(inode_is_locked(dir));
ubifs_assert(inode_is_locked(inode));
- if (ubifs_crypt_is_encrypted(dir) &&
- !fscrypt_has_permitted_context(dir, inode))
- return -EPERM;
+ if (ubifs_crypt_is_encrypted(dir)) {
+ if (!fscrypt_has_permitted_context(dir, inode))
+ return -EPERM;
- err = dbg_check_synced_i_size(c, inode);
+ err = fscrypt_get_encryption_info(inode);
+ if (err)
+ return err;
+
+ if (!fscrypt_has_encryption_key(inode))
+ return -EPERM;
+ }
+
+ err = fscrypt_setup_filename(dir, &dentry->d_name, 0, &nm);
if (err)
return err;
+ err = dbg_check_synced_i_size(c, inode);
+ if (err)
+ goto out_fname;
+
err = ubifs_budget_space(c, &req);
if (err)
- return err;
+ goto out_fname;
lock_2_inodes(dir, inode);
inc_nlink(inode);
@@ -578,13 +656,14 @@ static int ubifs_link(struct dentry *old_dentry, struct inode *dir,
dir->i_size += sz_change;
dir_ui->ui_size = dir->i_size;
dir->i_mtime = dir->i_ctime = inode->i_ctime;
- err = ubifs_jnl_update(c, dir, &dentry->d_name, inode, 0, 0);
+ err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0);
if (err)
goto out_cancel;
unlock_2_inodes(dir, inode);
ubifs_release_budget(c, &req);
d_instantiate(dentry, inode);
+ fscrypt_free_filename(&nm);
return 0;
out_cancel:
@@ -594,6 +673,8 @@ out_cancel:
unlock_2_inodes(dir, inode);
ubifs_release_budget(c, &req);
iput(inode);
+out_fname:
+ fscrypt_free_filename(&nm);
return err;
}
@@ -602,10 +683,10 @@ static int ubifs_unlink(struct inode *dir, struct dentry *dentry)
struct ubifs_info *c = dir->i_sb->s_fs_info;
struct inode *inode = d_inode(dentry);
struct ubifs_inode *dir_ui = ubifs_inode(dir);
- int sz_change = CALC_DENT_SIZE(dentry->d_name.len);
- int err, budgeted = 1;
+ int err, sz_change, budgeted = 1;
struct ubifs_budget_req req = { .mod_dent = 1, .dirtied_ino = 2 };
unsigned int saved_nlink = inode->i_nlink;
+ struct fscrypt_name nm;
/*
* Budget request settings: deletion direntry, deletion inode (+1 for
@@ -617,16 +698,29 @@ static int ubifs_unlink(struct inode *dir, struct dentry *dentry)
dbg_gen("dent '%pd' from ino %lu (nlink %d) in dir ino %lu",
dentry, inode->i_ino,
inode->i_nlink, dir->i_ino);
+
+ if (ubifs_crypt_is_encrypted(dir)) {
+ err = fscrypt_get_encryption_info(dir);
+ if (err && err != -ENOKEY)
+ return err;
+ }
+
+ err = fscrypt_setup_filename(dir, &dentry->d_name, 1, &nm);
+ if (err)
+ return err;
+
+ sz_change = CALC_DENT_SIZE(fname_len(&nm));
+
ubifs_assert(inode_is_locked(dir));
ubifs_assert(inode_is_locked(inode));
err = dbg_check_synced_i_size(c, inode);
if (err)
- return err;
+ goto out_fname;
err = ubifs_budget_space(c, &req);
if (err) {
if (err != -ENOSPC)
- return err;
+ goto out_fname;
budgeted = 0;
}
@@ -636,7 +730,7 @@ static int ubifs_unlink(struct inode *dir, struct dentry *dentry)
dir->i_size -= sz_change;
dir_ui->ui_size = dir->i_size;
dir->i_mtime = dir->i_ctime = inode->i_ctime;
- err = ubifs_jnl_update(c, dir, &dentry->d_name, inode, 1, 0);
+ err = ubifs_jnl_update(c, dir, &nm, inode, 1, 0);
if (err)
goto out_cancel;
unlock_2_inodes(dir, inode);
@@ -648,6 +742,7 @@ static int ubifs_unlink(struct inode *dir, struct dentry *dentry)
c->bi.nospace = c->bi.nospace_rp = 0;
smp_wmb();
}
+ fscrypt_free_filename(&nm);
return 0;
out_cancel:
@@ -657,6 +752,8 @@ out_cancel:
unlock_2_inodes(dir, inode);
if (budgeted)
ubifs_release_budget(c, &req);
+out_fname:
+ fscrypt_free_filename(&nm);
return err;
}
@@ -671,7 +768,7 @@ out_cancel:
int ubifs_check_dir_empty(struct inode *dir)
{
struct ubifs_info *c = dir->i_sb->s_fs_info;
- struct qstr nm = { .name = NULL };
+ struct fscrypt_name nm = { 0 };
struct ubifs_dent_node *dent;
union ubifs_key key;
int err;
@@ -693,10 +790,10 @@ static int ubifs_rmdir(struct inode *dir, struct dentry *dentry)
{
struct ubifs_info *c = dir->i_sb->s_fs_info;
struct inode *inode = d_inode(dentry);
- int sz_change = CALC_DENT_SIZE(dentry->d_name.len);
- int err, budgeted = 1;
+ int err, sz_change, budgeted = 1;
struct ubifs_inode *dir_ui = ubifs_inode(dir);
struct ubifs_budget_req req = { .mod_dent = 1, .dirtied_ino = 2 };
+ struct fscrypt_name nm;
/*
* Budget request settings: deletion direntry, deletion inode and
@@ -712,10 +809,22 @@ static int ubifs_rmdir(struct inode *dir, struct dentry *dentry)
if (err)
return err;
+ if (ubifs_crypt_is_encrypted(dir)) {
+ err = fscrypt_get_encryption_info(dir);
+ if (err && err != -ENOKEY)
+ return err;
+ }
+
+ err = fscrypt_setup_filename(dir, &dentry->d_name, 1, &nm);
+ if (err)
+ return err;
+
+ sz_change = CALC_DENT_SIZE(fname_len(&nm));
+
err = ubifs_budget_space(c, &req);
if (err) {
if (err != -ENOSPC)
- return err;
+ goto out_fname;
budgeted = 0;
}
@@ -726,7 +835,7 @@ static int ubifs_rmdir(struct inode *dir, struct dentry *dentry)
dir->i_size -= sz_change;
dir_ui->ui_size = dir->i_size;
dir->i_mtime = dir->i_ctime = inode->i_ctime;
- err = ubifs_jnl_update(c, dir, &dentry->d_name, inode, 1, 0);
+ err = ubifs_jnl_update(c, dir, &nm, inode, 1, 0);
if (err)
goto out_cancel;
unlock_2_inodes(dir, inode);
@@ -738,6 +847,7 @@ static int ubifs_rmdir(struct inode *dir, struct dentry *dentry)
c->bi.nospace = c->bi.nospace_rp = 0;
smp_wmb();
}
+ fscrypt_free_filename(&nm);
return 0;
out_cancel:
@@ -748,6 +858,8 @@ out_cancel:
unlock_2_inodes(dir, inode);
if (budgeted)
ubifs_release_budget(c, &req);
+out_fname:
+ fscrypt_free_filename(&nm);
return err;
}
@@ -756,8 +868,9 @@ static int ubifs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
struct inode *inode;
struct ubifs_inode *dir_ui = ubifs_inode(dir);
struct ubifs_info *c = dir->i_sb->s_fs_info;
- int err, sz_change = CALC_DENT_SIZE(dentry->d_name.len);
+ int err, sz_change;
struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1 };
+ struct fscrypt_name nm;
/*
* Budget request settings: new inode, new direntry and changing parent
@@ -771,10 +884,27 @@ static int ubifs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
if (err)
return err;
+ if (ubifs_crypt_is_encrypted(dir)) {
+ err = fscrypt_get_encryption_info(dir);
+ if (err)
+ goto out_budg;
+
+ if (!fscrypt_has_encryption_key(dir)) {
+ err = -EPERM;
+ goto out_budg;
+ }
+ }
+
+ err = fscrypt_setup_filename(dir, &dentry->d_name, 0, &nm);
+ if (err)
+ goto out_budg;
+
+ sz_change = CALC_DENT_SIZE(fname_len(&nm));
+
inode = ubifs_new_inode(c, dir, S_IFDIR | mode);
if (IS_ERR(inode)) {
err = PTR_ERR(inode);
- goto out_budg;
+ goto out_fname;
}
err = ubifs_init_security(dir, inode, &dentry->d_name);
@@ -788,7 +918,7 @@ static int ubifs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
dir->i_size += sz_change;
dir_ui->ui_size = dir->i_size;
dir->i_mtime = dir->i_ctime = inode->i_ctime;
- err = ubifs_jnl_update(c, dir, &dentry->d_name, inode, 0, 0);
+ err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0);
if (err) {
ubifs_err(c, "cannot create directory, error %d", err);
goto out_cancel;
@@ -797,6 +927,7 @@ static int ubifs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
ubifs_release_budget(c, &req);
d_instantiate(dentry, inode);
+ fscrypt_free_filename(&nm);
return 0;
out_cancel:
@@ -807,6 +938,8 @@ out_cancel:
out_inode:
make_bad_inode(inode);
iput(inode);
+out_fname:
+ fscrypt_free_filename(&nm);
out_budg:
ubifs_release_budget(c, &req);
return err;
@@ -820,11 +953,12 @@ static int ubifs_mknod(struct inode *dir, struct dentry *dentry,
struct ubifs_inode *dir_ui = ubifs_inode(dir);
struct ubifs_info *c = dir->i_sb->s_fs_info;
union ubifs_dev_desc *dev = NULL;
- int sz_change = CALC_DENT_SIZE(dentry->d_name.len);
+ int sz_change;
int err, devlen = 0;
struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1,
.new_ino_d = ALIGN(devlen, 8),
.dirtied_ino = 1 };
+ struct fscrypt_name nm;
/*
* Budget request settings: new inode, new direntry and changing parent
@@ -846,11 +980,28 @@ static int ubifs_mknod(struct inode *dir, struct dentry *dentry,
return err;
}
+ if (ubifs_crypt_is_encrypted(dir)) {
+ err = fscrypt_get_encryption_info(dir);
+ if (err)
+ goto out_budg;
+
+ if (!fscrypt_has_encryption_key(dir)) {
+ err = -EPERM;
+ goto out_budg;
+ }
+ }
+
+ err = fscrypt_setup_filename(dir, &dentry->d_name, 0, &nm);
+ if (err)
+ goto out_budg;
+
+ sz_change = CALC_DENT_SIZE(fname_len(&nm));
+
inode = ubifs_new_inode(c, dir, mode);
if (IS_ERR(inode)) {
kfree(dev);
err = PTR_ERR(inode);
- goto out_budg;
+ goto out_fname;
}
init_special_inode(inode, inode->i_mode, rdev);
@@ -867,7 +1018,7 @@ static int ubifs_mknod(struct inode *dir, struct dentry *dentry,
dir->i_size += sz_change;
dir_ui->ui_size = dir->i_size;
dir->i_mtime = dir->i_ctime = inode->i_ctime;
- err = ubifs_jnl_update(c, dir, &dentry->d_name, inode, 0, 0);
+ err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0);
if (err)
goto out_cancel;
mutex_unlock(&dir_ui->ui_mutex);
@@ -875,6 +1026,7 @@ static int ubifs_mknod(struct inode *dir, struct dentry *dentry,
ubifs_release_budget(c, &req);
insert_inode_hash(inode);
d_instantiate(dentry, inode);
+ fscrypt_free_filename(&nm);
return 0;
out_cancel:
@@ -884,6 +1036,8 @@ out_cancel:
out_inode:
make_bad_inode(inode);
iput(inode);
+out_fname:
+ fscrypt_free_filename(&nm);
out_budg:
ubifs_release_budget(c, &req);
return err;
@@ -1019,15 +1173,14 @@ static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct ubifs_inode *old_inode_ui = ubifs_inode(old_inode);
int err, release, sync = 0, move = (new_dir != old_dir);
int is_dir = S_ISDIR(old_inode->i_mode);
- int unlink = !!new_inode;
- int new_sz = CALC_DENT_SIZE(new_dentry->d_name.len);
- int old_sz = CALC_DENT_SIZE(old_dentry->d_name.len);
+ int unlink = !!new_inode, new_sz, old_sz;
struct ubifs_budget_req req = { .new_dent = 1, .mod_dent = 1,
.dirtied_ino = 3 };
struct ubifs_budget_req ino_req = { .dirtied_ino = 1,
.dirtied_ino_d = ALIGN(old_inode_ui->data_len, 8) };
struct timespec time;
unsigned int uninitialized_var(saved_nlink);
+ struct fscrypt_name old_nm, new_nm;
/*
* Budget request settings: deletion direntry, new direntry, removing
@@ -1058,11 +1211,29 @@ static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry,
return err;
}
- err = ubifs_budget_space(c, &req);
+ err = fscrypt_setup_filename(old_dir, &old_dentry->d_name, 0, &old_nm);
if (err)
return err;
+
+ err = fscrypt_setup_filename(new_dir, &new_dentry->d_name, 0, &new_nm);
+ if (err) {
+ fscrypt_free_filename(&old_nm);
+ return err;
+ }
+
+ new_sz = CALC_DENT_SIZE(fname_len(&new_nm));
+ old_sz = CALC_DENT_SIZE(fname_len(&old_nm));
+
+ err = ubifs_budget_space(c, &req);
+ if (err) {
+ fscrypt_free_filename(&old_nm);
+ fscrypt_free_filename(&new_nm);
+ return err;
+ }
err = ubifs_budget_space(c, &ino_req);
if (err) {
+ fscrypt_free_filename(&old_nm);
+ fscrypt_free_filename(&new_nm);
ubifs_release_budget(c, &req);
return err;
}
@@ -1137,7 +1308,7 @@ static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (unlink && IS_SYNC(new_inode))
sync = 1;
}
- err = ubifs_jnl_rename(c, old_dir, old_dentry, new_dir, new_dentry,
+ err = ubifs_jnl_rename(c, old_dir, old_inode, &old_nm, new_dir, new_inode, &new_nm,
sync);
if (err)
goto out_cancel;
@@ -1154,6 +1325,9 @@ static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry,
ubifs_release_budget(c, &ino_req);
if (IS_SYNC(old_inode))
err = old_inode->i_sb->s_op->write_inode(old_inode, NULL);
+
+ fscrypt_free_filename(&old_nm);
+ fscrypt_free_filename(&new_nm);
return err;
out_cancel:
@@ -1178,6 +1352,8 @@ out_cancel:
unlock_3_inodes(old_dir, new_dir, new_inode);
ubifs_release_budget(c, &ino_req);
ubifs_release_budget(c, &req);
+ fscrypt_free_filename(&old_nm);
+ fscrypt_free_filename(&new_nm);
return err;
}
diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c
index da694e520ec8..eb1cef8032e7 100644
--- a/fs/ubifs/journal.c
+++ b/fs/ubifs/journal.c
@@ -530,7 +530,7 @@ static void mark_inode_clean(struct ubifs_info *c, struct ubifs_inode *ui)
* success. In case of failure, a negative error code is returned.
*/
int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
- const struct qstr *nm, const struct inode *inode,
+ const struct fscrypt_name *nm, const struct inode *inode,
int deletion, int xent)
{
int err, dlen, ilen, len, lnum, ino_offs, dent_offs;
@@ -542,11 +542,11 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
struct ubifs_ino_node *ino;
union ubifs_key dent_key, ino_key;
- dbg_jnl("ino %lu, dent '%.*s', data len %d in dir ino %lu",
- inode->i_ino, nm->len, nm->name, ui->data_len, dir->i_ino);
+ //dbg_jnl("ino %lu, dent '%.*s', data len %d in dir ino %lu",
+ // inode->i_ino, nm->len, nm->name, ui->data_len, dir->i_ino);
ubifs_assert(mutex_is_locked(&host_ui->ui_mutex));
- dlen = UBIFS_DENT_NODE_SZ + nm->len + 1;
+ dlen = UBIFS_DENT_NODE_SZ + fname_len(nm) + 1;
ilen = UBIFS_INO_NODE_SZ;
/*
@@ -587,9 +587,10 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
key_write(c, &dent_key, dent->key);
dent->inum = deletion ? 0 : cpu_to_le64(inode->i_ino);
dent->type = get_dent_type(inode->i_mode);
- dent->nlen = cpu_to_le16(nm->len);
- memcpy(dent->name, nm->name, nm->len);
- dent->name[nm->len] = '\0';
+ dent->nlen = cpu_to_le16(fname_len(nm));
+ memcpy(dent->name, fname_name(nm), fname_len(nm));
+ dent->name[fname_len(nm)] = '\0';
+
zero_dent_node_unused(dent);
ubifs_prep_grp_node(c, dent, dlen, 0);
@@ -936,30 +937,31 @@ int ubifs_jnl_delete_inode(struct ubifs_info *c, const struct inode *inode)
* returned.
*/
int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
- const struct dentry *old_dentry,
+ const struct inode *old_inode,
+ const struct fscrypt_name *old_nm,
const struct inode *new_dir,
- const struct dentry *new_dentry, int sync)
+ const struct inode *new_inode,
+ const struct fscrypt_name *new_nm,
+ int sync)
{
void *p;
union ubifs_key key;
struct ubifs_dent_node *dent, *dent2;
int err, dlen1, dlen2, ilen, lnum, offs, len;
- const struct inode *old_inode = d_inode(old_dentry);
- const struct inode *new_inode = d_inode(new_dentry);
int aligned_dlen1, aligned_dlen2, plen = UBIFS_INO_NODE_SZ;
int last_reference = !!(new_inode && new_inode->i_nlink == 0);
int move = (old_dir != new_dir);
struct ubifs_inode *uninitialized_var(new_ui);
- dbg_jnl("dent '%pd' in dir ino %lu to dent '%pd' in dir ino %lu",
- old_dentry, old_dir->i_ino, new_dentry, new_dir->i_ino);
+ //dbg_jnl("dent '%pd' in dir ino %lu to dent '%pd' in dir ino %lu",
+ // old_dentry, old_dir->i_ino, new_dentry, new_dir->i_ino);
ubifs_assert(ubifs_inode(old_dir)->data_len == 0);
ubifs_assert(ubifs_inode(new_dir)->data_len == 0);
ubifs_assert(mutex_is_locked(&ubifs_inode(old_dir)->ui_mutex));
ubifs_assert(mutex_is_locked(&ubifs_inode(new_dir)->ui_mutex));
- dlen1 = UBIFS_DENT_NODE_SZ + new_dentry->d_name.len + 1;
- dlen2 = UBIFS_DENT_NODE_SZ + old_dentry->d_name.len + 1;
+ dlen1 = UBIFS_DENT_NODE_SZ + fname_len(new_nm) + 1;
+ dlen2 = UBIFS_DENT_NODE_SZ + fname_len(old_nm) + 1;
if (new_inode) {
new_ui = ubifs_inode(new_inode);
ubifs_assert(mutex_is_locked(&new_ui->ui_mutex));
@@ -985,25 +987,24 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
/* Make new dent */
dent->ch.node_type = UBIFS_DENT_NODE;
- dent_key_init_flash(c, &dent->key, new_dir->i_ino, &new_dentry->d_name);
+ dent_key_init_flash(c, &dent->key, new_dir->i_ino, new_nm);
dent->inum = cpu_to_le64(old_inode->i_ino);
dent->type = get_dent_type(old_inode->i_mode);
- dent->nlen = cpu_to_le16(new_dentry->d_name.len);
- memcpy(dent->name, new_dentry->d_name.name, new_dentry->d_name.len);
- dent->name[new_dentry->d_name.len] = '\0';
+ dent->nlen = cpu_to_le16(fname_len(new_nm));
+ memcpy(dent->name, fname_name(new_nm), fname_len(new_nm));
+ dent->name[fname_len(new_nm)] = '\0';
zero_dent_node_unused(dent);
ubifs_prep_grp_node(c, dent, dlen1, 0);
/* Make deletion dent */
dent2 = (void *)dent + aligned_dlen1;
dent2->ch.node_type = UBIFS_DENT_NODE;
- dent_key_init_flash(c, &dent2->key, old_dir->i_ino,
- &old_dentry->d_name);
+ dent_key_init_flash(c, &dent2->key, old_dir->i_ino, old_nm);
dent2->inum = 0;
dent2->type = DT_UNKNOWN;
- dent2->nlen = cpu_to_le16(old_dentry->d_name.len);
- memcpy(dent2->name, old_dentry->d_name.name, old_dentry->d_name.len);
- dent2->name[old_dentry->d_name.len] = '\0';
+ dent2->nlen = cpu_to_le16(fname_len(old_nm));
+ memcpy(dent2->name, fname_name(old_nm), fname_len(old_nm));
+ dent2->name[fname_len(old_nm)] = '\0';
zero_dent_node_unused(dent2);
ubifs_prep_grp_node(c, dent2, dlen2, 0);
@@ -1044,8 +1045,8 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
}
release_head(c, BASEHD);
- dent_key_init(c, &key, new_dir->i_ino, &new_dentry->d_name);
- err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen1, &new_dentry->d_name);
+ dent_key_init(c, &key, new_dir->i_ino, new_nm);
+ err = ubifs_tnc_add_nm(c, &key, lnum, offs, dlen1, new_nm);
if (err)
goto out_ro;
@@ -1053,8 +1054,8 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
if (err)
goto out_ro;
- dent_key_init(c, &key, old_dir->i_ino, &old_dentry->d_name);
- err = ubifs_tnc_remove_nm(c, &key, &old_dentry->d_name);
+ dent_key_init(c, &key, old_dir->i_ino, old_nm);
+ err = ubifs_tnc_remove_nm(c, &key, old_nm);
if (err)
goto out_ro;
@@ -1117,7 +1118,7 @@ out_free:
* This function is used when an inode is truncated and the last data node of
* the inode has to be re-compressed/encrypted and re-written.
*/
-static int truncate_data_node(const struct ubifs_info *c, struct inode *inode,
+static int truncate_data_node(const struct ubifs_info *c, const struct inode *inode,
unsigned int block, struct ubifs_data_node *dn,
int *new_len)
{
@@ -1338,7 +1339,8 @@ out_free:
* error code in case of failure.
*/
int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host,
- const struct inode *inode, const struct qstr *nm)
+ const struct inode *inode,
+ const struct fscrypt_name *nm)
{
int err, xlen, hlen, len, lnum, xent_offs, aligned_xlen;
struct ubifs_dent_node *xent;
@@ -1347,9 +1349,9 @@ int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host,
int sync = IS_DIRSYNC(host);
struct ubifs_inode *host_ui = ubifs_inode(host);
- dbg_jnl("host %lu, xattr ino %lu, name '%s', data len %d",
- host->i_ino, inode->i_ino, nm->name,
- ubifs_inode(inode)->data_len);
+ //dbg_jnl("host %lu, xattr ino %lu, name '%s', data len %d",
+ // host->i_ino, inode->i_ino, nm->name,
+ // ubifs_inode(inode)->data_len);
ubifs_assert(inode->i_nlink == 0);
ubifs_assert(mutex_is_locked(&host_ui->ui_mutex));
@@ -1357,7 +1359,7 @@ int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host,
* Since we are deleting the inode, we do not bother to attach any data
* to it and assume its length is %UBIFS_INO_NODE_SZ.
*/
- xlen = UBIFS_DENT_NODE_SZ + nm->len + 1;
+ xlen = UBIFS_DENT_NODE_SZ + fname_len(nm) + 1;
aligned_xlen = ALIGN(xlen, 8);
hlen = host_ui->data_len + UBIFS_INO_NODE_SZ;
len = aligned_xlen + UBIFS_INO_NODE_SZ + ALIGN(hlen, 8);
@@ -1378,9 +1380,9 @@ int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host,
key_write(c, &xent_key, xent->key);
xent->inum = 0;
xent->type = get_dent_type(inode->i_mode);
- xent->nlen = cpu_to_le16(nm->len);
- memcpy(xent->name, nm->name, nm->len);
- xent->name[nm->len] = '\0';
+ xent->nlen = cpu_to_le16(fname_len(nm));
+ memcpy(xent->name, fname_name(nm), fname_len(nm));
+ xent->name[fname_len(nm)] = '\0';
zero_dent_node_unused(xent);
ubifs_prep_grp_node(c, xent, xlen, 0);
diff --git a/fs/ubifs/key.h b/fs/ubifs/key.h
index ca4371fdfa7d..7547be512db2 100644
--- a/fs/ubifs/key.h
+++ b/fs/ubifs/key.h
@@ -153,13 +153,13 @@ static inline void highest_ino_key(const struct ubifs_info *c,
* @c: UBIFS file-system description object
* @key: key to initialize
* @inum: parent inode number
- * @nm: direntry name and length
+ * @nm: direntry name and length. Not a string when encrypted!
*/
static inline void dent_key_init(const struct ubifs_info *c,
union ubifs_key *key, ino_t inum,
- const struct qstr *nm)
+ const struct fscrypt_name *nm)
{
- uint32_t hash = c->key_hash(nm->name, nm->len);
+ uint32_t hash = c->key_hash(fname_name(nm), fname_len(nm));
ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK));
key->u32[0] = inum;
@@ -191,10 +191,11 @@ static inline void dent_key_init_hash(const struct ubifs_info *c,
* @nm: direntry name and length
*/
static inline void dent_key_init_flash(const struct ubifs_info *c, void *k,
- ino_t inum, const struct qstr *nm)
+ ino_t inum,
+ const struct fscrypt_name *nm)
{
union ubifs_key *key = k;
- uint32_t hash = c->key_hash(nm->name, nm->len);
+ uint32_t hash = c->key_hash(fname_name(nm), fname_len(nm));
ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK));
key->j32[0] = cpu_to_le32(inum);
@@ -225,9 +226,9 @@ static inline void lowest_dent_key(const struct ubifs_info *c,
*/
static inline void xent_key_init(const struct ubifs_info *c,
union ubifs_key *key, ino_t inum,
- const struct qstr *nm)
+ const struct fscrypt_name *nm)
{
- uint32_t hash = c->key_hash(nm->name, nm->len);
+ uint32_t hash = c->key_hash(fname_name(nm), fname_len(nm));
ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK));
key->u32[0] = inum;
@@ -242,10 +243,10 @@ static inline void xent_key_init(const struct ubifs_info *c,
* @nm: extended attribute entry name and length
*/
static inline void xent_key_init_flash(const struct ubifs_info *c, void *k,
- ino_t inum, const struct qstr *nm)
+ ino_t inum, const struct fscrypt_name *nm)
{
union ubifs_key *key = k;
- uint32_t hash = c->key_hash(nm->name, nm->len);
+ uint32_t hash = c->key_hash(fname_name(nm), fname_len(nm));
ubifs_assert(!(hash & ~UBIFS_S_KEY_HASH_MASK));
key->j32[0] = cpu_to_le32(inum);
diff --git a/fs/ubifs/replay.c b/fs/ubifs/replay.c
index f02adc49425b..4143982e3701 100644
--- a/fs/ubifs/replay.c
+++ b/fs/ubifs/replay.c
@@ -61,7 +61,7 @@ struct replay_entry {
struct list_head list;
union ubifs_key key;
union {
- struct qstr nm;
+ struct fscrypt_name nm;
struct {
loff_t old_size;
loff_t new_size;
@@ -327,7 +327,7 @@ static void destroy_replay_list(struct ubifs_info *c)
list_for_each_entry_safe(r, tmp, &c->replay_list, list) {
if (is_hash_key(c, &r->key))
- kfree(r->nm.name);
+ kfree(fname_name(&r->nm));
list_del(&r->list);
kfree(r);
}
@@ -430,10 +430,10 @@ static int insert_dent(struct ubifs_info *c, int lnum, int offs, int len,
r->deletion = !!deletion;
r->sqnum = sqnum;
key_copy(c, key, &r->key);
- r->nm.len = nlen;
+ fname_len(&r->nm) = nlen;
memcpy(nbuf, name, nlen);
nbuf[nlen] = '\0';
- r->nm.name = nbuf;
+ fname_name(&r->nm) = nbuf;
list_add_tail(&r->list, &c->replay_list);
return 0;
diff --git a/fs/ubifs/tnc.c b/fs/ubifs/tnc.c
index fa9a20cc60d6..0d0030461cb4 100644
--- a/fs/ubifs/tnc.c
+++ b/fs/ubifs/tnc.c
@@ -519,7 +519,7 @@ static int fallible_read_node(struct ubifs_info *c, const union ubifs_key *key,
* of failure, a negative error code is returned.
*/
static int matches_name(struct ubifs_info *c, struct ubifs_zbranch *zbr,
- const struct qstr *nm)
+ const struct fscrypt_name *nm)
{
struct ubifs_dent_node *dent;
int nlen, err;
@@ -542,11 +542,11 @@ static int matches_name(struct ubifs_info *c, struct ubifs_zbranch *zbr,
dent = zbr->leaf;
nlen = le16_to_cpu(dent->nlen);
- err = memcmp(dent->name, nm->name, min_t(int, nlen, nm->len));
+ err = memcmp(dent->name, fname_name(nm), min_t(int, nlen, fname_len(nm)));
if (err == 0) {
- if (nlen == nm->len)
+ if (nlen == fname_len(nm))
return NAME_MATCHES;
- else if (nlen < nm->len)
+ else if (nlen < fname_len(nm))
return NAME_LESS;
else
return NAME_GREATER;
@@ -689,7 +689,7 @@ static int tnc_prev(struct ubifs_info *c, struct ubifs_znode **zn, int *n)
*/
static int resolve_collision(struct ubifs_info *c, const union ubifs_key *key,
struct ubifs_znode **zn, int *n,
- const struct qstr *nm)
+ const struct fscrypt_name *nm)
{
int err;
@@ -807,7 +807,7 @@ static int resolve_collision(struct ubifs_info *c, const union ubifs_key *key,
*/
static int fallible_matches_name(struct ubifs_info *c,
struct ubifs_zbranch *zbr,
- const struct qstr *nm)
+ const struct fscrypt_name *nm)
{
struct ubifs_dent_node *dent;
int nlen, err;
@@ -835,11 +835,11 @@ static int fallible_matches_name(struct ubifs_info *c,
dent = zbr->leaf;
nlen = le16_to_cpu(dent->nlen);
- err = memcmp(dent->name, nm->name, min_t(int, nlen, nm->len));
+ err = memcmp(dent->name, fname_name(nm), min_t(int, nlen, fname_len(nm)));
if (err == 0) {
- if (nlen == nm->len)
+ if (nlen == fname_len(nm))
return NAME_MATCHES;
- else if (nlen < nm->len)
+ else if (nlen < fname_len(nm))
return NAME_LESS;
else
return NAME_GREATER;
@@ -878,7 +878,8 @@ out_free:
static int fallible_resolve_collision(struct ubifs_info *c,
const union ubifs_key *key,
struct ubifs_znode **zn, int *n,
- const struct qstr *nm, int adding)
+ const struct fscrypt_name *nm,
+ int adding)
{
struct ubifs_znode *o_znode = NULL, *znode = *zn;
int uninitialized_var(o_n), err, cmp, unsure = 0, nn = *n;
@@ -1789,12 +1790,12 @@ int ubifs_tnc_bulk_read(struct ubifs_info *c, struct bu_info *bu)
* was not found, and a negative error code in case of failure.
*/
static int do_lookup_nm(struct ubifs_info *c, const union ubifs_key *key,
- void *node, const struct qstr *nm)
+ void *node, const struct fscrypt_name *nm)
{
int found, n, err;
struct ubifs_znode *znode;
- dbg_tnck(key, "name '%.*s' key ", nm->len, nm->name);
+ //dbg_tnck(key, "name '%.*s' key ", nm->len, nm->name);
mutex_lock(&c->tnc_mutex);
found = ubifs_lookup_level0(c, key, &znode, &n);
if (!found) {
@@ -1837,7 +1838,7 @@ out_unlock:
* was not found, and a negative error code in case of failure.
*/
int ubifs_tnc_lookup_nm(struct ubifs_info *c, const union ubifs_key *key,
- void *node, const struct qstr *nm)
+ void *node, const struct fscrypt_name *nm)
{
int err, len;
const struct ubifs_dent_node *dent = node;
@@ -1851,7 +1852,7 @@ int ubifs_tnc_lookup_nm(struct ubifs_info *c, const union ubifs_key *key,
return err;
len = le16_to_cpu(dent->nlen);
- if (nm->len == len && !memcmp(dent->name, nm->name, len))
+ if (fname_len(nm) == len && !memcmp(dent->name, fname_name(nm), len))
return 0;
/*
@@ -2279,14 +2280,15 @@ out_unlock:
* may have collisions, like directory entry keys.
*/
int ubifs_tnc_add_nm(struct ubifs_info *c, const union ubifs_key *key,
- int lnum, int offs, int len, const struct qstr *nm)
+ int lnum, int offs, int len,
+ const struct fscrypt_name *nm)
{
int found, n, err = 0;
struct ubifs_znode *znode;
mutex_lock(&c->tnc_mutex);
- dbg_tnck(key, "LEB %d:%d, name '%.*s', key ",
- lnum, offs, nm->len, nm->name);
+ //dbg_tnck(key, "LEB %d:%d, name '%.*s', key ",
+ // lnum, offs, nm->len, nm->name);
found = lookup_level0_dirty(c, key, &znode, &n);
if (found < 0) {
err = found;
@@ -2344,7 +2346,7 @@ int ubifs_tnc_add_nm(struct ubifs_info *c, const union ubifs_key *key,
* by passing 'ubifs_tnc_remove_nm()' the same key but
* an unmatchable name.
*/
- struct qstr noname = { .name = "" };
+ struct fscrypt_name noname = { .disk_name = { .name = "", .len = 1 } };
err = dbg_check_tnc(c, 0);
mutex_unlock(&c->tnc_mutex);
@@ -2514,13 +2516,13 @@ out_unlock:
* Returns %0 on success or negative error code on failure.
*/
int ubifs_tnc_remove_nm(struct ubifs_info *c, const union ubifs_key *key,
- const struct qstr *nm)
+ const struct fscrypt_name *nm)
{
int n, err;
struct ubifs_znode *znode;
mutex_lock(&c->tnc_mutex);
- dbg_tnck(key, "%.*s, key ", nm->len, nm->name);
+ //dbg_tnck(key, "%.*s, key ", nm->len, nm->name);
err = lookup_level0_dirty(c, key, &znode, &n);
if (err < 0)
goto out_unlock;
@@ -2669,7 +2671,7 @@ int ubifs_tnc_remove_ino(struct ubifs_info *c, ino_t inum)
{
union ubifs_key key1, key2;
struct ubifs_dent_node *xent, *pxent = NULL;
- struct qstr nm = { .name = NULL };
+ struct fscrypt_name nm = {0};
dbg_tnc("ino %lu", (unsigned long)inum);
@@ -2694,8 +2696,8 @@ int ubifs_tnc_remove_ino(struct ubifs_info *c, ino_t inum)
dbg_tnc("xent '%s', ino %lu", xent->name,
(unsigned long)xattr_inum);
- nm.name = xent->name;
- nm.len = le16_to_cpu(xent->nlen);
+ fname_name(&nm) = xent->name;
+ fname_len(&nm) = le16_to_cpu(xent->nlen);
err = ubifs_tnc_remove_nm(c, &key1, &nm);
if (err) {
kfree(xent);
@@ -2747,7 +2749,7 @@ int ubifs_tnc_remove_ino(struct ubifs_info *c, ino_t inum)
*/
struct ubifs_dent_node *ubifs_tnc_next_ent(struct ubifs_info *c,
union ubifs_key *key,
- const struct qstr *nm)
+ const struct fscrypt_name *nm)
{
int n, err, type = key_type(c, key);
struct ubifs_znode *znode;
@@ -2755,7 +2757,7 @@ struct ubifs_dent_node *ubifs_tnc_next_ent(struct ubifs_info *c,
struct ubifs_zbranch *zbr;
union ubifs_key *dkey;
- dbg_tnck(key, "%s ", nm->name ? (char *)nm->name : "(lowest)");
+ //dbg_tnck(key, "%s ", nm->name ? (char *)nm->name : "(lowest)");
ubifs_assert(is_hash_key(c, key));
mutex_lock(&c->tnc_mutex);
@@ -2763,7 +2765,7 @@ struct ubifs_dent_node *ubifs_tnc_next_ent(struct ubifs_info *c,
if (unlikely(err < 0))
goto out_unlock;
- if (nm->name) {
+ if (fname_len(nm) > 0) {
if (err) {
/* Handle collisions */
err = resolve_collision(c, key, &znode, &n, nm);
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index 456f22aed35c..f91ef52a4523 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -1521,20 +1521,23 @@ int ubifs_consolidate_log(struct ubifs_info *c);
/* journal.c */
int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
- const struct qstr *nm, const struct inode *inode,
+ const struct fscrypt_name *nm, const struct inode *inode,
int deletion, int xent);
int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
const union ubifs_key *key, const void *buf, int len);
int ubifs_jnl_write_inode(struct ubifs_info *c, const struct inode *inode);
int ubifs_jnl_delete_inode(struct ubifs_info *c, const struct inode *inode);
int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
- const struct dentry *old_dentry,
+ const struct inode *old_inode,
+ const struct fscrypt_name *old_nm,
const struct inode *new_dir,
- const struct dentry *new_dentry, int sync);
+ const struct inode *new_inode,
+ const struct fscrypt_name *new_nm,
+ int sync);
int ubifs_jnl_truncate(struct ubifs_info *c, const struct inode *inode,
loff_t old_size, loff_t new_size);
int ubifs_jnl_delete_xattr(struct ubifs_info *c, const struct inode *host,
- const struct inode *inode, const struct qstr *nm);
+ const struct inode *inode, const struct fscrypt_name *nm);
int ubifs_jnl_change_xattr(struct ubifs_info *c, const struct inode *inode1,
const struct inode *inode2);
@@ -1569,7 +1572,7 @@ int ubifs_save_dirty_idx_lnums(struct ubifs_info *c);
int ubifs_lookup_level0(struct ubifs_info *c, const union ubifs_key *key,
struct ubifs_znode **zn, int *n);
int ubifs_tnc_lookup_nm(struct ubifs_info *c, const union ubifs_key *key,
- void *node, const struct qstr *nm);
+ void *node, const struct fscrypt_name *nm);
int ubifs_tnc_locate(struct ubifs_info *c, const union ubifs_key *key,
void *node, int *lnum, int *offs);
int ubifs_tnc_add(struct ubifs_info *c, const union ubifs_key *key, int lnum,
@@ -1577,16 +1580,16 @@ int ubifs_tnc_add(struct ubifs_info *c, const union ubifs_key *key, int lnum,
int ubifs_tnc_replace(struct ubifs_info *c, const union ubifs_key *key,
int old_lnum, int old_offs, int lnum, int offs, int len);
int ubifs_tnc_add_nm(struct ubifs_info *c, const union ubifs_key *key,
- int lnum, int offs, int len, const struct qstr *nm);
+ int lnum, int offs, int len, const struct fscrypt_name *nm);
int ubifs_tnc_remove(struct ubifs_info *c, const union ubifs_key *key);
int ubifs_tnc_remove_nm(struct ubifs_info *c, const union ubifs_key *key,
- const struct qstr *nm);
+ const struct fscrypt_name *nm);
int ubifs_tnc_remove_range(struct ubifs_info *c, union ubifs_key *from_key,
union ubifs_key *to_key);
int ubifs_tnc_remove_ino(struct ubifs_info *c, ino_t inum);
struct ubifs_dent_node *ubifs_tnc_next_ent(struct ubifs_info *c,
union ubifs_key *key,
- const struct qstr *nm);
+ const struct fscrypt_name *nm);
void ubifs_tnc_close(struct ubifs_info *c);
int ubifs_tnc_has_node(struct ubifs_info *c, union ubifs_key *key, int level,
int lnum, int offs, int is_idx);
diff --git a/fs/ubifs/xattr.c b/fs/ubifs/xattr.c
index d3b53c55ea39..469b3ff9748c 100644
--- a/fs/ubifs/xattr.c
+++ b/fs/ubifs/xattr.c
@@ -97,7 +97,7 @@ static const struct file_operations empty_fops;
* of failure.
*/
static int create_xattr(struct ubifs_info *c, struct inode *host,
- const struct qstr *nm, const void *value, int size)
+ const struct fscrypt_name *nm, const void *value, int size)
{
int err, names_len;
struct inode *inode;
@@ -117,7 +117,7 @@ static int create_xattr(struct ubifs_info *c, struct inode *host,
* extended attributes if the name list becomes larger. This limitation
* is artificial for UBIFS, though.
*/
- names_len = host_ui->xattr_names + host_ui->xattr_cnt + nm->len + 1;
+ names_len = host_ui->xattr_names + host_ui->xattr_cnt + fname_len(nm) + 1;
if (names_len > XATTR_LIST_MAX) {
ubifs_err(c, "cannot add one more xattr name to inode %lu, total names length would become %d, max. is %d",
host->i_ino, names_len, XATTR_LIST_MAX);
@@ -154,9 +154,9 @@ static int create_xattr(struct ubifs_info *c, struct inode *host,
mutex_lock(&host_ui->ui_mutex);
host->i_ctime = ubifs_current_time(host);
host_ui->xattr_cnt += 1;
- host_ui->xattr_size += CALC_DENT_SIZE(nm->len);
+ host_ui->xattr_size += CALC_DENT_SIZE(fname_len(nm));
host_ui->xattr_size += CALC_XATTR_BYTES(size);
- host_ui->xattr_names += nm->len;
+ host_ui->xattr_names += fname_len(nm);
/*
* We handle UBIFS_XATTR_NAME_ENCRYPTION_CONTEXT here because we
@@ -164,7 +164,7 @@ static int create_xattr(struct ubifs_info *c, struct inode *host,
* To avoid multiple updates of the same inode in the same operation,
* let's do it here.
*/
- if (strcmp(nm->name, UBIFS_XATTR_NAME_ENCRYPTION_CONTEXT) == 0)
+ if (strcmp(fname_name(nm), UBIFS_XATTR_NAME_ENCRYPTION_CONTEXT) == 0)
host_ui->flags |= UBIFS_CRYPT_FL;
err = ubifs_jnl_update(c, host, nm, inode, 0, 1);
@@ -179,7 +179,7 @@ static int create_xattr(struct ubifs_info *c, struct inode *host,
out_cancel:
host_ui->xattr_cnt -= 1;
- host_ui->xattr_size -= CALC_DENT_SIZE(nm->len);
+ host_ui->xattr_size -= CALC_DENT_SIZE(fname_len(nm));
host_ui->xattr_size -= CALC_XATTR_BYTES(size);
host_ui->flags &= ~UBIFS_CRYPT_FL;
mutex_unlock(&host_ui->ui_mutex);
@@ -281,7 +281,7 @@ int ubifs_xattr_set(struct inode *host, const char *name, const void *value,
{
struct inode *inode;
struct ubifs_info *c = host->i_sb->s_fs_info;
- struct qstr nm = QSTR_INIT(name, strlen(name));
+ struct fscrypt_name nm = { .disk_name = FSTR_INIT((char *)name, strlen(name))};
struct ubifs_dent_node *xent;
union ubifs_key key;
int err;
@@ -297,7 +297,7 @@ int ubifs_xattr_set(struct inode *host, const char *name, const void *value,
if (size > UBIFS_MAX_INO_DATA)
return -ERANGE;
- if (nm.len > UBIFS_MAX_NLEN)
+ if (fname_len(&nm) > UBIFS_MAX_NLEN)
return -ENAMETOOLONG;
xent = kmalloc(UBIFS_MAX_XENT_NODE_SZ, GFP_NOFS);
@@ -347,13 +347,13 @@ ssize_t ubifs_xattr_get(struct inode *host, const char *name, void *buf,
{
struct inode *inode;
struct ubifs_info *c = host->i_sb->s_fs_info;
- struct qstr nm = QSTR_INIT(name, strlen(name));
+ struct fscrypt_name nm = { .disk_name = FSTR_INIT((char *)name, strlen(name))};
struct ubifs_inode *ui;
struct ubifs_dent_node *xent;
union ubifs_key key;
int err;
- if (nm.len > UBIFS_MAX_NLEN)
+ if (fname_len(&nm) > UBIFS_MAX_NLEN)
return -ENAMETOOLONG;
xent = kmalloc(UBIFS_MAX_XENT_NODE_SZ, GFP_NOFS);
@@ -422,7 +422,7 @@ ssize_t ubifs_listxattr(struct dentry *dentry, char *buffer, size_t size)
struct ubifs_inode *host_ui = ubifs_inode(host);
struct ubifs_dent_node *xent, *pxent = NULL;
int err, len, written = 0;
- struct qstr nm = { .name = NULL };
+ struct fscrypt_name nm = {0};
dbg_gen("ino %lu ('%pd'), buffer size %zd", host->i_ino,
dentry, size);
@@ -446,12 +446,12 @@ ssize_t ubifs_listxattr(struct dentry *dentry, char *buffer, size_t size)
break;
}
- nm.name = xent->name;
- nm.len = le16_to_cpu(xent->nlen);
+ fname_name(&nm) = xent->name;
+ fname_len(&nm) = le16_to_cpu(xent->nlen);
if (xattr_visible(xent->name)) {
- memcpy(buffer + written, nm.name, nm.len + 1);
- written += nm.len + 1;
+ memcpy(buffer + written, fname_name(&nm), fname_len(&nm) + 1);
+ written += fname_len(&nm) + 1;
}
kfree(pxent);
@@ -470,7 +470,7 @@ ssize_t ubifs_listxattr(struct dentry *dentry, char *buffer, size_t size)
}
static int remove_xattr(struct ubifs_info *c, struct inode *host,
- struct inode *inode, const struct qstr *nm)
+ struct inode *inode, const struct fscrypt_name *nm)
{
int err;
struct ubifs_inode *host_ui = ubifs_inode(host);
@@ -487,9 +487,9 @@ static int remove_xattr(struct ubifs_info *c, struct inode *host,
mutex_lock(&host_ui->ui_mutex);
host->i_ctime = ubifs_current_time(host);
host_ui->xattr_cnt -= 1;
- host_ui->xattr_size -= CALC_DENT_SIZE(nm->len);
+ host_ui->xattr_size -= CALC_DENT_SIZE(fname_len(nm));
host_ui->xattr_size -= CALC_XATTR_BYTES(ui->data_len);
- host_ui->xattr_names -= nm->len;
+ host_ui->xattr_names -= fname_len(nm);
err = ubifs_jnl_delete_xattr(c, host, inode, nm);
if (err)
@@ -501,7 +501,7 @@ static int remove_xattr(struct ubifs_info *c, struct inode *host,
out_cancel:
host_ui->xattr_cnt += 1;
- host_ui->xattr_size += CALC_DENT_SIZE(nm->len);
+ host_ui->xattr_size += CALC_DENT_SIZE(fname_len(nm));
host_ui->xattr_size += CALC_XATTR_BYTES(ui->data_len);
mutex_unlock(&host_ui->ui_mutex);
ubifs_release_budget(c, &req);
@@ -513,14 +513,14 @@ static int ubifs_xattr_remove(struct inode *host, const char *name)
{
struct inode *inode;
struct ubifs_info *c = host->i_sb->s_fs_info;
- struct qstr nm = QSTR_INIT(name, strlen(name));
+ struct fscrypt_name nm = { .disk_name = FSTR_INIT((char *)name, strlen(name))};
struct ubifs_dent_node *xent;
union ubifs_key key;
int err;
ubifs_assert(inode_is_locked(host));
- if (nm.len > UBIFS_MAX_NLEN)
+ if (fname_len(&nm) > UBIFS_MAX_NLEN)
return -ENAMETOOLONG;
xent = kmalloc(UBIFS_MAX_XENT_NODE_SZ, GFP_NOFS);
--
2.7.3
Signed-off-by: Richard Weinberger <[email protected]>
---
fs/ubifs/journal.c | 3 +++
fs/ubifs/ubifs.h | 1 +
2 files changed, 4 insertions(+)
diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c
index 50696c6e0ae2..dcecfbfbf40e 100644
--- a/fs/ubifs/journal.c
+++ b/fs/ubifs/journal.c
@@ -589,6 +589,7 @@ int ubifs_jnl_update(struct ubifs_info *c, const struct inode *dir,
dent->nlen = cpu_to_le16(fname_len(nm));
memcpy(dent->name, fname_name(nm), fname_len(nm));
dent->name[fname_len(nm)] = '\0';
+ dent->cookie = prandom_u32();
zero_dent_node_unused(dent);
ubifs_prep_grp_node(c, dent, dlen, 0);
@@ -992,6 +993,7 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
dent->nlen = cpu_to_le16(fname_len(new_nm));
memcpy(dent->name, fname_name(new_nm), fname_len(new_nm));
dent->name[fname_len(new_nm)] = '\0';
+ dent->cookie = prandom_u32();
zero_dent_node_unused(dent);
ubifs_prep_grp_node(c, dent, dlen1, 0);
@@ -1004,6 +1006,7 @@ int ubifs_jnl_rename(struct ubifs_info *c, const struct inode *old_dir,
dent2->nlen = cpu_to_le16(fname_len(old_nm));
memcpy(dent2->name, fname_name(old_nm), fname_len(old_nm));
dent2->name[fname_len(old_nm)] = '\0';
+ dent2->cookie = prandom_u32();
zero_dent_node_unused(dent2);
ubifs_prep_grp_node(c, dent2, dlen2, 0);
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index 3f68b26b120f..294e4047bcc0 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -39,6 +39,7 @@
#include <linux/security.h>
#include <linux/xattr.h>
#include <linux/fscrypto.h>
+#include <linux/random.h>
#include "ubifs-media.h"
/* Version of this UBIFS implementation */
--
2.7.3
UBIFS stores a 32bit hash of every file, for traditional lookups by name
this scheme is fine since UBIFS can first try to find the file by the
hash of the filename and upon collisions it can walk through all entries
with the same hash and do a string compare.
When filesnames are encrypted fscrypto will ask the filesystem for a
unique cookie, based on this cookie the filesystem has to be able to
locate the target file again. With 32bit hashes this is impossible
because the chance for collisions is very high. Do deal with that we
store a 32bit cookie directly in the UBIFS directory entry node such
that we get a 64bit cookie (32bit from filename hash and the dent
cookie). For a lookup by hash UBIFS finds the entry by the first 32bit
and then compares the dent cookie. If it does not match, it has to do a
linear search of the whole directory and compares all dent cookies until
the correct entry is found.
Signed-off-by: Richard Weinberger <[email protected]>
---
fs/ubifs/dir.c | 7 +++-
fs/ubifs/journal.c | 1 -
fs/ubifs/tnc.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++-
fs/ubifs/ubifs-media.h | 5 ++-
fs/ubifs/ubifs.h | 2 +
5 files changed, 107 insertions(+), 7 deletions(-)
diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c
index 2f33be5ca57c..4b2db102a0a9 100644
--- a/fs/ubifs/dir.c
+++ b/fs/ubifs/dir.c
@@ -253,7 +253,7 @@ static struct dentry *ubifs_lookup(struct inode *dir, struct dentry *dentry,
ubifs_assert(fname_len(&nm) == 0);
ubifs_assert(fname_name(&nm) == NULL);
dent_key_init_hash(c, &key, dir->i_ino, nm.hash);
- err = ubifs_tnc_lookup(c, &key, dent);
+ err = ubifs_tnc_lookup_dh(c, &key, dent, nm.minor_hash);
} else {
dent_key_init(c, &key, dir->i_ino, &nm);
err = ubifs_tnc_lookup_nm(c, &key, dent, &nm);
@@ -521,7 +521,10 @@ static int ubifs_readdir(struct file *file, struct dir_context *ctx)
if (encrypted) {
fstr.len = fstr_real_len;
- err = fscrypt_fname_disk_to_usr(dir, key_hash_flash(c, &dent->key), 0, &nm.disk_name, &fstr);
+ err = fscrypt_fname_disk_to_usr(dir, key_hash_flash(c,
+ &dent->key),
+ le32_to_cpu(dent->cookie),
+ &nm.disk_name, &fstr);
if (err < 0)
goto out;
} else {
diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c
index eb1cef8032e7..50696c6e0ae2 100644
--- a/fs/ubifs/journal.c
+++ b/fs/ubifs/journal.c
@@ -78,7 +78,6 @@ static inline void zero_ino_node_unused(struct ubifs_ino_node *ino)
static inline void zero_dent_node_unused(struct ubifs_dent_node *dent)
{
dent->padding1 = 0;
- memset(dent->padding2, 0, 4);
}
/**
diff --git a/fs/ubifs/tnc.c b/fs/ubifs/tnc.c
index 0d751297873e..1eaf994addb4 100644
--- a/fs/ubifs/tnc.c
+++ b/fs/ubifs/tnc.c
@@ -1783,7 +1783,7 @@ int ubifs_tnc_bulk_read(struct ubifs_info *c, struct bu_info *bu)
* @node: the node is returned here
* @nm: node name
*
- * This function look up and reads a node which contains name hash in the key.
+ * This function looks up and reads a node which contains name hash in the key.
* Since the hash may have collisions, there may be many nodes with the same
* key, so we have to sequentially look to all of them until the needed one is
* found. This function returns zero in case of success, %-ENOENT if the node
@@ -1831,7 +1831,7 @@ out_unlock:
* @node: the node is returned here
* @nm: node name
*
- * This function look up and reads a node which contains name hash in the key.
+ * This function looks up and reads a node which contains name hash in the key.
* Since the hash may have collisions, there may be many nodes with the same
* key, so we have to sequentially look to all of them until the needed one is
* found. This function returns zero in case of success, %-ENOENT if the node
@@ -1859,9 +1859,104 @@ int ubifs_tnc_lookup_nm(struct ubifs_info *c, const union ubifs_key *key,
* Unluckily, there are hash collisions and we have to iterate over
* them look at each direntry with colliding name hash sequentially.
*/
+
return do_lookup_nm(c, key, node, nm);
}
+static int do_lookup_dh(struct ubifs_info *c, const union ubifs_key *key,
+ struct ubifs_dent_node *dent, uint32_t cookie)
+{
+ int n, err, type = key_type(c, key);
+ struct ubifs_znode *znode;
+ struct ubifs_zbranch *zbr;
+ union ubifs_key *cur_key, start_key;
+
+ ubifs_assert(is_hash_key(c, key));
+
+ lowest_dent_key(c, &start_key, key_inum(c, key));
+ cur_key = &start_key;
+
+ for (;;) {
+ mutex_lock(&c->tnc_mutex);
+
+ err = ubifs_lookup_level0(c, cur_key, &znode, &n);
+ if (unlikely(err < 0))
+ goto out_unlock;
+
+ if (!err) {
+ err = tnc_next(c, &znode, &n);
+ if (err)
+ goto out_unlock;
+ }
+
+ zbr = &znode->zbranch[n];
+ cur_key = &zbr->key;
+
+ if (key_inum(c, cur_key) != key_inum(c, key) ||
+ key_type(c, cur_key) != type) {
+ err = -ENOENT;
+ goto out_unlock;
+ }
+
+ err = tnc_read_hashed_node(c, zbr, dent);
+ if (err)
+ goto out_unlock;
+
+ if (key_hash(c, key) == key_hash(c, cur_key) &&
+ le32_to_cpu(dent->cookie) == cookie) {
+ /* We found the entry. :-) */
+ err = 0;
+ goto out_unlock;
+ }
+
+ mutex_unlock(&c->tnc_mutex);
+ }
+
+ ubifs_assert(0);
+
+out_unlock:
+ mutex_unlock(&c->tnc_mutex);
+ return err;
+}
+
+/**
+ * ubifs_tnc_lookup_dh - look up a "double hashed" node.
+ * @c: UBIFS file-system description object
+ * @key: node key to lookup
+ * @node: the node is returned here
+ * @cookie: node cookie for collision resolution
+ *
+ * This function looks up and reads a node which contains name hash in the key.
+ * Since the hash may have collisions, there may be many nodes with the same
+ * key, so we have to sequentially look to all of them until the needed one
+ * with the same cookie value is found.
+ * This function returns zero in case of success, %-ENOENT if the node
+ * was not found, and a negative error code in case of failure.
+ */
+int ubifs_tnc_lookup_dh(struct ubifs_info *c, const union ubifs_key *key,
+ void *node, uint32_t cookie)
+{
+ int err;
+ const struct ubifs_dent_node *dent = node;
+
+ /*
+ * We assume that in most of the cases there are no name collisions and
+ * 'ubifs_tnc_lookup()' returns us the right direntry.
+ */
+ err = ubifs_tnc_lookup(c, key, node);
+ if (err)
+ return err;
+
+ if (le32_to_cpu(dent->cookie) == cookie)
+ return 0;
+
+ /*
+ * Unluckily, there are hash collisions and we have to iterate over
+ * them look at each direntry with colliding name hash sequentially.
+ */
+ return do_lookup_dh(c, key, node, cookie);
+}
+
/**
* correct_parent_keys - correct parent znodes' keys.
* @c: UBIFS file-system description object
diff --git a/fs/ubifs/ubifs-media.h b/fs/ubifs/ubifs-media.h
index e46331dcca4c..249124d9a801 100644
--- a/fs/ubifs/ubifs-media.h
+++ b/fs/ubifs/ubifs-media.h
@@ -530,7 +530,8 @@ struct ubifs_ino_node {
* @padding1: reserved for future, zeroes
* @type: type of the target inode (%UBIFS_ITYPE_REG, %UBIFS_ITYPE_DIR, etc)
* @nlen: name length
- * @padding2: reserved for future, zeroes
+ * @cookie: A 32bits random number, used to construct a 64bits
+ * identifier.
* @name: zero-terminated name
*
* Note, do not forget to amend 'zero_dent_node_unused()' function when
@@ -543,7 +544,7 @@ struct ubifs_dent_node {
__u8 padding1;
__u8 type;
__le16 nlen;
- __u8 padding2[4]; /* Watch 'zero_dent_node_unused()' if changing! */
+ __le32 cookie;
__u8 name[];
} __packed;
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index f91ef52a4523..3f68b26b120f 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -1573,6 +1573,8 @@ int ubifs_lookup_level0(struct ubifs_info *c, const union ubifs_key *key,
struct ubifs_znode **zn, int *n);
int ubifs_tnc_lookup_nm(struct ubifs_info *c, const union ubifs_key *key,
void *node, const struct fscrypt_name *nm);
+int ubifs_tnc_lookup_dh(struct ubifs_info *c, const union ubifs_key *key,
+ void *node, uint32_t secondary_hash);
int ubifs_tnc_locate(struct ubifs_info *c, const union ubifs_key *key,
void *node, int *lnum, int *offs);
int ubifs_tnc_add(struct ubifs_info *c, const union ubifs_key *key, int lnum,
--
2.7.3
Some filesystems, such as UBIFS, maintain a const pointer
for struct inode.
Signed-off-by: Richard Weinberger <[email protected]>
---
fs/crypto/crypto.c | 12 +++++++-----
include/linux/fscrypto.h | 12 +++++++-----
2 files changed, 14 insertions(+), 10 deletions(-)
diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c
index 1c2f9516b4be..8c59912feebb 100644
--- a/fs/crypto/crypto.c
+++ b/fs/crypto/crypto.c
@@ -147,7 +147,7 @@ typedef enum {
FS_ENCRYPT,
} fscrypt_direction_t;
-static int do_crypto(struct inode *inode,
+static int do_crypto(const struct inode *inode,
fscrypt_direction_t rw, pgoff_t index,
struct scatterlist *src, struct scatterlist *dst,
unsigned int cryptlen, gfp_t gfp_flags)
@@ -212,7 +212,7 @@ static int do_page_crypto(struct inode *inode,
return do_crypto(inode, rw, index, &src, &dst, PAGE_SIZE, gfp_flags);
}
-static int do_buf_crypto(struct inode *inode,
+static int do_buf_crypto(const struct inode *inode,
fscrypt_direction_t rw, pgoff_t index,
const void *src_buf, const void *dst_buf,
unsigned int buflen, gfp_t gfp_flags)
@@ -287,7 +287,7 @@ errout:
}
EXPORT_SYMBOL(fscrypt_encrypt_page);
-int fscrypt_encrypt_buffer(struct inode *inode, const void *plaintext_buf,
+int fscrypt_encrypt_buffer(const struct inode *inode, const void *plaintext_buf,
const void *ciphertext_buf, unsigned int buflen,
pgoff_t index, gfp_t gfp_flags)
{
@@ -296,8 +296,10 @@ int fscrypt_encrypt_buffer(struct inode *inode, const void *plaintext_buf,
}
EXPORT_SYMBOL(fscrypt_encrypt_buffer);
-int fscrypt_decrypt_buffer(struct inode *inode, const void *ciphertext_buf,
- const void *plaintext_buf, unsigned int buflen,
+int fscrypt_decrypt_buffer(const struct inode *inode,
+ const void *ciphertext_buf,
+ const void *plaintext_buf,
+ unsigned int buflen,
pgoff_t index, gfp_t gfp_flags)
{
return do_buf_crypto(inode, FS_DECRYPT, index, ciphertext_buf,
diff --git a/include/linux/fscrypto.h b/include/linux/fscrypto.h
index a9628b4882e7..d18ee29cf69a 100644
--- a/include/linux/fscrypto.h
+++ b/include/linux/fscrypto.h
@@ -273,11 +273,13 @@ extern void fscrypt_pullback_bio_page(struct page **, bool);
extern void fscrypt_restore_control_page(struct page *);
extern int fscrypt_zeroout_range(struct inode *, pgoff_t, sector_t,
unsigned int);
-int fscrypt_encrypt_buffer(struct inode *inode, const void *plaintext_buf,
+int fscrypt_encrypt_buffer(const struct inode *inode, const void *plaintext_buf,
const void *ciphertext_buf, unsigned int buflen,
pgoff_t index, gfp_t gfp_flags);
-int fscrypt_decrypt_buffer(struct inode *inode, const void *ciphertext_buf,
- const void *plaintext_buf, unsigned int buflen,
+int fscrypt_decrypt_buffer(const struct inode *inode,
+ const void *ciphertext_buf,
+ const void *plaintext_buf,
+ unsigned int buflen,
pgoff_t index, gfp_t gfp_flags);
/* policy.c */
extern int fscrypt_process_policy(struct file *, const struct fscrypt_policy *);
@@ -291,8 +293,8 @@ extern int fscrypt_get_encryption_info(struct inode *);
extern void fscrypt_put_encryption_info(struct inode *, struct fscrypt_info *);
/* fname.c */
-extern int fscrypt_setup_filename(struct inode *, const struct qstr *,
- int lookup, struct fscrypt_name *);
+extern int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname,
+ int lookup, struct fscrypt_name *fname);
extern void fscrypt_free_filename(struct fscrypt_name *);
extern u32 fscrypt_fname_encrypted_size(struct inode *, u32);
extern int fscrypt_fname_alloc_buffer(struct inode *, u32,
--
2.7.3
Signed-off-by: Richard Weinberger <[email protected]>
---
fs/ubifs/dir.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++-------
fs/ubifs/file.c | 55 +++++++++++++++++++++++++++++++++++++-
fs/ubifs/super.c | 1 -
3 files changed, 125 insertions(+), 12 deletions(-)
diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c
index 723bae4dec30..2f33be5ca57c 100644
--- a/fs/ubifs/dir.c
+++ b/fs/ubifs/dir.c
@@ -1051,10 +1051,27 @@ static int ubifs_symlink(struct inode *dir, struct dentry *dentry,
struct ubifs_inode *dir_ui = ubifs_inode(dir);
struct ubifs_info *c = dir->i_sb->s_fs_info;
int err, len = strlen(symname);
- int sz_change = CALC_DENT_SIZE(dentry->d_name.len);
+ int sz_change = CALC_DENT_SIZE(len);
+ struct fscrypt_str disk_link = FSTR_INIT((char *)symname, len + 1);
+ struct fscrypt_symlink_data *sd = NULL;
struct ubifs_budget_req req = { .new_ino = 1, .new_dent = 1,
.new_ino_d = ALIGN(len, 8),
.dirtied_ino = 1 };
+ struct fscrypt_name nm;
+
+ if (ubifs_crypt_is_encrypted(dir)) {
+ err = fscrypt_get_encryption_info(dir);
+ if (err)
+ goto out_budg;
+
+ if (!fscrypt_has_encryption_key(dir)) {
+ err = -EPERM;
+ goto out_budg;
+ }
+
+ disk_link.len = (fscrypt_fname_encrypted_size(dir, len) +
+ sizeof(struct fscrypt_symlink_data));
+ }
/*
* Budget request settings: new inode, new direntry and changing parent
@@ -1064,36 +1081,77 @@ static int ubifs_symlink(struct inode *dir, struct dentry *dentry,
dbg_gen("dent '%pd', target '%s' in dir ino %lu", dentry,
symname, dir->i_ino);
- if (len > UBIFS_MAX_INO_DATA)
+ if (disk_link.len > UBIFS_MAX_INO_DATA)
return -ENAMETOOLONG;
err = ubifs_budget_space(c, &req);
if (err)
return err;
+ err = fscrypt_setup_filename(dir, &dentry->d_name, 0, &nm);
+ if (err)
+ goto out_budg;
+
inode = ubifs_new_inode(c, dir, S_IFLNK | S_IRWXUGO);
if (IS_ERR(inode)) {
err = PTR_ERR(inode);
- goto out_budg;
+ goto out_fname;
}
ui = ubifs_inode(inode);
- ui->data = kmalloc(len + 1, GFP_NOFS);
+ ui->data = kmalloc(disk_link.len, GFP_NOFS);
if (!ui->data) {
err = -ENOMEM;
goto out_inode;
}
- memcpy(ui->data, symname, len);
- ((char *)ui->data)[len] = '\0';
- inode->i_link = ui->data;
+ if (ubifs_crypt_is_encrypted(dir)) {
+ struct qstr istr = QSTR_INIT(symname, len);
+ struct fscrypt_str ostr;
+
+ sd = kzalloc(disk_link.len, GFP_NOFS);
+ if (!sd) {
+ err = -ENOMEM;
+ goto out_inode;
+ }
+
+ err = fscrypt_get_encryption_info(inode);
+ if (err) {
+ kfree(sd);
+ goto out_inode;
+ }
+
+ if (!fscrypt_has_encryption_key(inode)) {
+ kfree(sd);
+ err = -EPERM;
+ goto out_inode;
+ }
+
+ ostr.name = sd->encrypted_path;
+ ostr.len = disk_link.len;
+
+ err = fscrypt_fname_usr_to_disk(inode, &istr, &ostr);
+ if (err < 0) {
+ kfree(sd);
+ goto out_inode;
+ }
+
+ sd->len = cpu_to_le16(ostr.len);
+ disk_link.name = (char *)sd;
+ } else {
+ inode->i_link = ui->data;
+ }
+
+ memcpy(ui->data, disk_link.name, disk_link.len);
+ ((char *)ui->data)[disk_link.len - 1] = '\0';
+
/*
* The terminating zero byte is not written to the flash media and it
* is put just to make later in-memory string processing simpler. Thus,
* data length is @len, not @len + %1.
*/
- ui->data_len = len;
- inode->i_size = ubifs_inode(inode)->ui_size = len;
+ ui->data_len = disk_link.len - 1;
+ inode->i_size = ubifs_inode(inode)->ui_size = disk_link.len - 1;
err = ubifs_init_security(dir, inode, &dentry->d_name);
if (err)
@@ -1103,7 +1161,7 @@ static int ubifs_symlink(struct inode *dir, struct dentry *dentry,
dir->i_size += sz_change;
dir_ui->ui_size = dir->i_size;
dir->i_mtime = dir->i_ctime = inode->i_ctime;
- err = ubifs_jnl_update(c, dir, &dentry->d_name, inode, 0, 0);
+ err = ubifs_jnl_update(c, dir, &nm, inode, 0, 0);
if (err)
goto out_cancel;
mutex_unlock(&dir_ui->ui_mutex);
@@ -1111,6 +1169,7 @@ static int ubifs_symlink(struct inode *dir, struct dentry *dentry,
ubifs_release_budget(c, &req);
insert_inode_hash(inode);
d_instantiate(dentry, inode);
+ fscrypt_free_filename(&nm);
return 0;
out_cancel:
@@ -1120,6 +1179,8 @@ out_cancel:
out_inode:
make_bad_inode(inode);
iput(inode);
+out_fname:
+ fscrypt_free_filename(&nm);
out_budg:
ubifs_release_budget(c, &req);
return err;
diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
index 60e089d50c82..ce168b3f61ca 100644
--- a/fs/ubifs/file.c
+++ b/fs/ubifs/file.c
@@ -1679,6 +1679,59 @@ static int ubifs_file_open(struct inode *inode, struct file *filp)
return 0;
}
+static const char *ubifs_get_link(struct dentry *dentry,
+ struct inode *inode,
+ struct delayed_call *done)
+{
+ int err;
+ struct fscrypt_symlink_data *sd;
+ struct ubifs_inode *ui = ubifs_inode(inode);
+ struct fscrypt_str cstr;
+ struct fscrypt_str pstr;
+
+ if (!dentry)
+ return ERR_PTR(-ECHILD);
+
+ if (ubifs_crypt_is_encrypted(inode)) {
+ err = fscrypt_get_encryption_info(inode);
+ if (err)
+ return ERR_PTR(err);
+ } else
+ return ui->data;
+
+
+ sd = (struct fscrypt_symlink_data *)ui->data;
+ cstr.name = sd->encrypted_path;
+ cstr.len = le16_to_cpu(sd->len);
+
+ if (cstr.len == 0)
+ return ERR_PTR(-ENOENT);
+
+ if ((cstr.len + sizeof(struct fscrypt_symlink_data) - 1) > ui->data_len)
+ return ERR_PTR(-EIO);
+
+ err = fscrypt_fname_alloc_buffer(inode, cstr.len, &pstr);
+ if (err)
+ return ERR_PTR(err);
+
+ err = fscrypt_fname_disk_to_usr(inode, 0, 0, &cstr, &pstr);
+ if (err < 0) {
+ fscrypt_fname_free_buffer(&pstr);
+ return ERR_PTR(err);
+ }
+
+ pstr.name[err] = '\0';
+
+ if (pstr.name[0] == '\0') {
+ fscrypt_fname_free_buffer(&pstr);
+ return ERR_PTR(-ENOENT);
+ }
+
+ set_delayed_call(done, kfree_link, pstr.name);
+ return pstr.name;
+}
+
+
const struct address_space_operations ubifs_file_address_operations = {
.readpage = ubifs_readpage,
.writepage = ubifs_writepage,
@@ -1706,7 +1759,7 @@ const struct inode_operations ubifs_file_inode_operations = {
const struct inode_operations ubifs_symlink_inode_operations = {
.readlink = generic_readlink,
- .get_link = simple_get_link,
+ .get_link = ubifs_get_link,
.setattr = ubifs_setattr,
.getattr = ubifs_getattr,
.setxattr = generic_setxattr,
diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c
index ae25c908fbe5..e08aa04fc835 100644
--- a/fs/ubifs/super.c
+++ b/fs/ubifs/super.c
@@ -198,7 +198,6 @@ struct inode *ubifs_iget(struct super_block *sb, unsigned long inum)
}
memcpy(ui->data, ino->data, ui->data_len);
((char *)ui->data)[ui->data_len] = '\0';
- inode->i_link = ui->data;
break;
case S_IFBLK:
case S_IFCHR:
--
2.7.3
tnc_read_hashed_node() is a better name since we read a node
by a given hash, not a name.
Signed-off-by: Richard Weinberger <[email protected]>
---
fs/ubifs/tnc.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/fs/ubifs/tnc.c b/fs/ubifs/tnc.c
index 0d0030461cb4..0d751297873e 100644
--- a/fs/ubifs/tnc.c
+++ b/fs/ubifs/tnc.c
@@ -378,7 +378,7 @@ static void lnc_free(struct ubifs_zbranch *zbr)
}
/**
- * tnc_read_node_nm - read a "hashed" leaf node.
+ * tnc_read_hashed_node - read a "hashed" leaf node.
* @c: UBIFS file-system description object
* @zbr: key and position of the node
* @node: node is returned here
@@ -388,8 +388,8 @@ static void lnc_free(struct ubifs_zbranch *zbr)
* added to LNC. Returns zero in case of success or a negative negative error
* code in case of failure.
*/
-static int tnc_read_node_nm(struct ubifs_info *c, struct ubifs_zbranch *zbr,
- void *node)
+static int tnc_read_hashed_node(struct ubifs_info *c, struct ubifs_zbranch *zbr,
+ void *node)
{
int err;
@@ -1454,7 +1454,7 @@ again:
* In this case the leaf node cache gets used, so we pass the
* address of the zbranch and keep the mutex locked
*/
- err = tnc_read_node_nm(c, zt, node);
+ err = tnc_read_hashed_node(c, zt, node);
goto out;
}
if (safely) {
@@ -1817,7 +1817,7 @@ static int do_lookup_nm(struct ubifs_info *c, const union ubifs_key *key,
goto out_unlock;
}
- err = tnc_read_node_nm(c, &znode->zbranch[n], node);
+ err = tnc_read_hashed_node(c, &znode->zbranch[n], node);
out_unlock:
mutex_unlock(&c->tnc_mutex);
@@ -2815,7 +2815,7 @@ struct ubifs_dent_node *ubifs_tnc_next_ent(struct ubifs_info *c,
goto out_free;
}
- err = tnc_read_node_nm(c, zbr, dent);
+ err = tnc_read_hashed_node(c, zbr, dent);
if (unlikely(err))
goto out_free;
--
2.7.3
...and provide a non const variant for fscrypto
Signed-off-by: Richard Weinberger <[email protected]>
---
fs/ubifs/crypto.c | 2 +-
fs/ubifs/ubifs.h | 7 ++++++-
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/fs/ubifs/crypto.c b/fs/ubifs/crypto.c
index 160c31479e0c..d19a097435f0 100644
--- a/fs/ubifs/crypto.c
+++ b/fs/ubifs/crypto.c
@@ -38,7 +38,7 @@ static int ubifs_key_prefix(struct inode *inode, u8 **key)
struct fscrypt_operations ubifs_crypt_operations = {
.get_context = ubifs_crypt_get_context,
.set_context = ubifs_crypt_set_context,
- .is_encrypted = ubifs_crypt_is_encrypted,
+ .is_encrypted = __ubifs_crypt_is_encrypted,
.empty_dir = ubifs_crypt_empty_dir,
.max_namelen = ubifs_crypt_max_namelen,
.key_prefix = ubifs_key_prefix,
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index 6c56a08afcd7..456f22aed35c 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -1816,13 +1816,18 @@ extern struct fscrypt_operations ubifs_crypt_operations;
#define fscrypt_fname_usr_to_disk fscrypt_notsupp_fname_usr_to_disk
#endif
-static inline bool ubifs_crypt_is_encrypted(struct inode *inode)
+static inline bool __ubifs_crypt_is_encrypted(struct inode *inode)
{
struct ubifs_inode *ui = ubifs_inode(inode);
return ui->flags & UBIFS_CRYPT_FL;
}
+static inline bool ubifs_crypt_is_encrypted(const struct inode *inode)
+{
+ return __ubifs_crypt_is_encrypted((struct inode *)inode);
+}
+
/* Normal UBIFS messages */
__printf(2, 3)
void ubifs_msg(const struct ubifs_info *c, const char *fmt, ...);
--
2.7.3
As of now all filenames known by UBIFS are strings with a NUL
terminator. With encrypted filenames a filename can be any binary
string and the r5 function cannot search for the NUL terminator.
UBIFS always knows how long a filename is, therefore we can change
the hash function to iterate over the filename length to work
correctly with binary strings.
Signed-off-by: Richard Weinberger <[email protected]>
---
fs/ubifs/key.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/ubifs/key.h b/fs/ubifs/key.h
index c0a95e393347..ca4371fdfa7d 100644
--- a/fs/ubifs/key.h
+++ b/fs/ubifs/key.h
@@ -69,7 +69,7 @@ static inline uint32_t key_r5_hash(const char *s, int len)
uint32_t a = 0;
const signed char *str = (const signed char *)s;
- while (*str) {
+ while (len--) {
a += *str << 4;
a += *str >> 4;
a *= 11;
--
2.7.3
With encrypted filenames we store raw binary data, doing
string tests is no longer possible.
Signed-off-by: Richard Weinberger <[email protected]>
---
fs/ubifs/replay.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/ubifs/replay.c b/fs/ubifs/replay.c
index 3ca4540130b5..f02adc49425b 100644
--- a/fs/ubifs/replay.c
+++ b/fs/ubifs/replay.c
@@ -456,7 +456,7 @@ int ubifs_validate_entry(struct ubifs_info *c,
if (le32_to_cpu(dent->ch.len) != nlen + UBIFS_DENT_NODE_SZ + 1 ||
dent->type >= UBIFS_ITYPES_CNT ||
nlen > UBIFS_MAX_NLEN || dent->name[nlen] != 0 ||
- strnlen(dent->name, nlen) != nlen ||
+ (key_type == UBIFS_XENT_KEY && strnlen(dent->name, nlen) != nlen) ||
le64_to_cpu(dent->inum) > MAX_INUM) {
ubifs_err(c, "bad %s node", key_type == UBIFS_DENT_KEY ?
"directory entry" : "extended attribute entry");
--
2.7.3
We need this extra check in mmap because a process could
gain an already opened fd.
Signed-off-by: Richard Weinberger <[email protected]>
---
fs/ubifs/file.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
index fc79278969e5..bd0049788e24 100644
--- a/fs/ubifs/file.c
+++ b/fs/ubifs/file.c
@@ -1594,6 +1594,15 @@ static const struct vm_operations_struct ubifs_file_vm_ops = {
static int ubifs_file_mmap(struct file *file, struct vm_area_struct *vma)
{
int err;
+ struct inode *inode = file->f_mapping->host;
+
+ if (ubifs_crypt_is_encrypted(inode)) {
+ err = fscrypt_get_encryption_info(inode);
+ if (err)
+ return -EACCES;
+ if (!fscrypt_has_encryption_key(inode))
+ return -ENOKEY;
+ }
err = generic_file_mmap(file, vma);
if (err)
--
2.7.3
When data of a data node is compressed and encrypted
we need to store the size of the compressed data because
before encryption we may have to add padding bytes.
For the new field we consume the last two padding bytes
in struct ubifs_data_node. Two bytes are fine because
the data length is at most 4096.
Signed-off-by: Richard Weinberger <[email protected]>
---
fs/ubifs/journal.c | 11 -----------
fs/ubifs/ubifs-media.h | 6 ++----
2 files changed, 2 insertions(+), 15 deletions(-)
diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c
index 0b9da5b6e0f9..a643de4e3d91 100644
--- a/fs/ubifs/journal.c
+++ b/fs/ubifs/journal.c
@@ -82,15 +82,6 @@ static inline void zero_dent_node_unused(struct ubifs_dent_node *dent)
}
/**
- * zero_data_node_unused - zero out unused fields of an on-flash data node.
- * @data: the data node to zero out
- */
-static inline void zero_data_node_unused(struct ubifs_data_node *data)
-{
- memset(data->padding, 0, 2);
-}
-
-/**
* zero_trun_node_unused - zero out unused fields of an on-flash truncation
* node.
* @trun: the truncation node to zero out
@@ -722,7 +713,6 @@ int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
data->ch.node_type = UBIFS_DATA_NODE;
key_write(c, key, &data->key);
data->size = cpu_to_le32(len);
- zero_data_node_unused(data);
if (!(ui->flags & UBIFS_COMPR_FL))
/* Compression is disabled for this inode */
@@ -1199,7 +1189,6 @@ int ubifs_jnl_truncate(struct ubifs_info *c, const struct inode *inode,
dn->size = cpu_to_le32(dlen);
dlen += UBIFS_DATA_NODE_SZ;
}
- zero_data_node_unused(dn);
}
}
}
diff --git a/fs/ubifs/ubifs-media.h b/fs/ubifs/ubifs-media.h
index aa302b11aec8..e46331dcca4c 100644
--- a/fs/ubifs/ubifs-media.h
+++ b/fs/ubifs/ubifs-media.h
@@ -553,18 +553,16 @@ struct ubifs_dent_node {
* @key: node key
* @size: uncompressed data size in bytes
* @compr_type: compression type (%UBIFS_COMPR_NONE, %UBIFS_COMPR_LZO, etc)
- * @padding: reserved for future, zeroes
+ * @compr_size: compressed data size in bytes, only valid when data is encrypted
* @data: data
*
- * Note, do not forget to amend 'zero_data_node_unused()' function when
- * changing the padding fields.
*/
struct ubifs_data_node {
struct ubifs_ch ch;
__u8 key[UBIFS_MAX_KEY_LEN];
__le32 size;
__le16 compr_type;
- __u8 padding[2]; /* Watch 'zero_data_node_unused()' if changing! */
+ __le16 compr_size;
__u8 data[];
} __packed;
--
2.7.3
Signed-off-by: Richard Weinberger <[email protected]>
---
fs/ubifs/file.c | 36 ++++++++++++++++++
fs/ubifs/journal.c | 105 +++++++++++++++++++++++++++++++++++++++++++----------
fs/ubifs/super.c | 6 ++-
fs/ubifs/ubifs.h | 6 +++
4 files changed, 131 insertions(+), 22 deletions(-)
diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
index bd0049788e24..60e089d50c82 100644
--- a/fs/ubifs/file.c
+++ b/fs/ubifs/file.c
@@ -78,6 +78,24 @@ static int read_block(struct inode *inode, void *addr, unsigned int block,
goto dump;
dlen = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ;
+
+ if (ubifs_crypt_is_encrypted(inode)) {
+ int clen = le16_to_cpu(dn->compr_size);
+
+ if (clen <= 0 || clen > UBIFS_BLOCK_SIZE || clen > dlen)
+ goto dump;
+
+ ubifs_assert(dlen <= UBIFS_BLOCK_SIZE);
+ err = fscrypt_decrypt_buffer(inode, &dn->data, &dn->data, dlen, block, GFP_NOFS);
+ if (err) {
+ ubifs_err(c, "fscrypt_decrypt_buffer failed: %i", err);
+ return err;
+ }
+
+ ubifs_assert(clen <= dlen);
+ dlen = clen;
+ }
+
out_len = UBIFS_BLOCK_SIZE;
err = ubifs_decompress(c, &dn->data, dlen, addr, &out_len,
le16_to_cpu(dn->compr_type));
@@ -650,6 +668,24 @@ static int populate_page(struct ubifs_info *c, struct page *page,
dlen = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ;
out_len = UBIFS_BLOCK_SIZE;
+
+ if (ubifs_crypt_is_encrypted(inode)) {
+ int clen = le16_to_cpu(dn->compr_size);
+
+ if (clen <= 0 || clen > UBIFS_BLOCK_SIZE || clen > dlen)
+ goto out_err;
+
+ ubifs_assert(dlen <= UBIFS_BLOCK_SIZE);
+ err = fscrypt_decrypt_buffer(inode, &dn->data, &dn->data, dlen, page_block, GFP_NOFS);
+ if (err) {
+ ubifs_err(c, "fscrypt_decrypt_buffer failed: %i", err);
+ goto out_err;
+ }
+
+ ubifs_assert(clen <= dlen);
+ dlen = clen;
+ }
+
err = ubifs_decompress(c, &dn->data, dlen, addr, &out_len,
le16_to_cpu(dn->compr_type));
if (err || len != out_len)
diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c
index a643de4e3d91..da694e520ec8 100644
--- a/fs/ubifs/journal.c
+++ b/fs/ubifs/journal.c
@@ -691,11 +691,15 @@ int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
int err, lnum, offs, compr_type, out_len;
int dlen = COMPRESSED_DATA_NODE_BUF_SZ, allocated = 1;
struct ubifs_inode *ui = ubifs_inode(inode);
+ bool encrypted = ubifs_crypt_is_encrypted(inode);
dbg_jnlk(key, "ino %lu, blk %u, len %d, key ",
(unsigned long)key_inum(c, key), key_block(c, key), len);
ubifs_assert(len <= UBIFS_BLOCK_SIZE);
+ if (encrypted)
+ dlen += UBIFS_CIPHER_BLOCK_SIZE;
+
data = kmalloc(dlen, GFP_NOFS | __GFP_NOWARN);
if (!data) {
/*
@@ -724,6 +728,26 @@ int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
ubifs_compress(c, buf, len, &data->data, &out_len, &compr_type);
ubifs_assert(out_len <= UBIFS_BLOCK_SIZE);
+ if (encrypted) {
+ void *p = &data->data;
+ int plen = round_up(out_len, UBIFS_CIPHER_BLOCK_SIZE);
+
+ data->compr_size = cpu_to_le16(out_len);
+
+ if (plen != out_len) {
+ memset(p + out_len, 0, plen - out_len);
+ out_len = plen;
+ }
+
+ err = fscrypt_encrypt_buffer(inode, &data->data, &data->data, out_len, key_block(c, key), GFP_NOFS);
+ if (err) {
+ ubifs_err(c, "fscrypt_encrypt_buffer failed: %i", err);
+ goto out_free;
+ }
+ } else {
+ data->compr_size = 0;
+ }
+
dlen = UBIFS_DATA_NODE_SZ + out_len;
data->compr_type = cpu_to_le16(compr_type);
@@ -1083,31 +1107,79 @@ out_free:
}
/**
- * recomp_data_node - re-compress a truncated data node.
+ * truncate_data_node - re-compress/encrypt a truncated data node.
+ * @c: UBIFS file-system description object
+ * @inode: inode which referes to the data node
+ * @block: data block number
* @dn: data node to re-compress
* @new_len: new length
*
* This function is used when an inode is truncated and the last data node of
- * the inode has to be re-compressed and re-written.
+ * the inode has to be re-compressed/encrypted and re-written.
*/
-static int recomp_data_node(const struct ubifs_info *c,
- struct ubifs_data_node *dn, int *new_len)
+static int truncate_data_node(const struct ubifs_info *c, struct inode *inode,
+ unsigned int block, struct ubifs_data_node *dn,
+ int *new_len)
{
void *buf;
- int err, len, compr_type, out_len;
+ int err, dlen, compr_type, out_len, old_dlen;
out_len = le32_to_cpu(dn->size);
buf = kmalloc(out_len * WORST_COMPR_FACTOR, GFP_NOFS);
if (!buf)
return -ENOMEM;
- len = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ;
+ dlen = old_dlen = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ;
compr_type = le16_to_cpu(dn->compr_type);
- err = ubifs_decompress(c, &dn->data, len, buf, &out_len, compr_type);
- if (err)
- goto out;
- ubifs_compress(c, buf, *new_len, &dn->data, &out_len, &compr_type);
+ if (ubifs_crypt_is_encrypted(inode)) {
+ int clen = le16_to_cpu(dn->compr_size);
+
+ if (clen <= 0 || clen > UBIFS_BLOCK_SIZE || clen > dlen) {
+ ubifs_err(c, "bad compr_size: %i", clen);
+ err = -EINVAL;
+ goto out;
+ }
+
+ err = fscrypt_decrypt_buffer(inode, &dn->data, &dn->data, dlen, block, GFP_NOFS);
+ if (err) {
+ ubifs_err(c, "fscrypt_decrypt_buffer failed: %i", err);
+ goto out;
+ }
+
+ ubifs_assert(clen <= dlen);
+ dlen = clen;
+ }
+
+ if (compr_type != UBIFS_COMPR_NONE) {
+ err = ubifs_decompress(c, &dn->data, dlen, buf, &out_len, compr_type);
+ if (err)
+ goto out;
+
+ ubifs_compress(c, buf, *new_len, &dn->data, &out_len, &compr_type);
+ }
+
+ if (ubifs_crypt_is_encrypted(inode)) {
+ void *p = &dn->data;
+ int plen = round_up(out_len, UBIFS_CIPHER_BLOCK_SIZE);
+
+ ubifs_assert(old_dlen >= plen);
+ dn->compr_size = cpu_to_le16(out_len);
+
+ if (plen != out_len) {
+ memset(p + out_len, 0, plen - out_len);
+ out_len = plen;
+ }
+
+ err = fscrypt_encrypt_buffer(inode, &dn->data, &dn->data, out_len, block, GFP_NOFS);
+ if (err) {
+ ubifs_msg(c, "fscrypt_encrypt_buffer failed: %i", err);
+ goto out;
+ }
+ } else {
+ dn->compr_size = 0;
+ }
+
ubifs_assert(out_len <= UBIFS_BLOCK_SIZE);
dn->compr_type = cpu_to_le16(compr_type);
dn->size = cpu_to_le32(*new_len);
@@ -1179,16 +1251,9 @@ int ubifs_jnl_truncate(struct ubifs_info *c, const struct inode *inode,
if (le32_to_cpu(dn->size) <= dlen)
dlen = 0; /* Nothing to do */
else {
- int compr_type = le16_to_cpu(dn->compr_type);
-
- if (compr_type != UBIFS_COMPR_NONE) {
- err = recomp_data_node(c, dn, &dlen);
- if (err)
- goto out_free;
- } else {
- dn->size = cpu_to_le32(dlen);
- dlen += UBIFS_DATA_NODE_SZ;
- }
+ err = truncate_data_node(c, inode, blk, dn, &dlen);
+ if (err)
+ goto out_free;
}
}
}
diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c
index a31222947265..ae25c908fbe5 100644
--- a/fs/ubifs/super.c
+++ b/fs/ubifs/super.c
@@ -1210,7 +1210,8 @@ static int mount_ubifs(struct ubifs_info *c)
bu_init(c);
if (!c->ro_mount) {
- c->write_reserve_buf = kmalloc(COMPRESSED_DATA_NODE_BUF_SZ,
+ c->write_reserve_buf = kmalloc(COMPRESSED_DATA_NODE_BUF_SZ + \
+ UBIFS_CIPHER_BLOCK_SIZE,
GFP_KERNEL);
if (!c->write_reserve_buf)
goto out_free;
@@ -1623,7 +1624,8 @@ static int ubifs_remount_rw(struct ubifs_info *c)
goto out;
}
- c->write_reserve_buf = kmalloc(COMPRESSED_DATA_NODE_BUF_SZ, GFP_KERNEL);
+ c->write_reserve_buf = kmalloc(COMPRESSED_DATA_NODE_BUF_SZ + \
+ UBIFS_CIPHER_BLOCK_SIZE, GFP_KERNEL);
if (!c->write_reserve_buf) {
err = -ENOMEM;
goto out;
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index 63dd027727ca..6c56a08afcd7 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -139,6 +139,12 @@
*/
#define WORST_COMPR_FACTOR 2
+#ifdef CONFIG_UBIFS_FS_ENCRYPTION
+#define UBIFS_CIPHER_BLOCK_SIZE FS_CRYPTO_BLOCK_SIZE
+#else
+#define UBIFS_CIPHER_BLOCK_SIZE 0
+#endif
+
/*
* How much memory is needed for a buffer where we compress a data node.
*/
--
2.7.3
When we're creating a new inode in UBIFS the inode is not
yet exposed and fscrypto calls ubifs_xattr_set() without
holding the inode mutex. This is okay but ubifs_xattr_set()
has to know about this.
Signed-off-by: Richard Weinberger <[email protected]>
---
fs/ubifs/xattr.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/fs/ubifs/xattr.c b/fs/ubifs/xattr.c
index 54ec2b9fd506..d3b53c55ea39 100644
--- a/fs/ubifs/xattr.c
+++ b/fs/ubifs/xattr.c
@@ -286,7 +286,13 @@ int ubifs_xattr_set(struct inode *host, const char *name, const void *value,
union ubifs_key key;
int err;
- ubifs_assert(inode_is_locked(host));
+ /*
+ * Creating an encryption context is done unlocked since we
+ * operate on a new inode which is not visible to other users
+ * at this point.
+ */
+ if (strcmp(name, UBIFS_XATTR_NAME_ENCRYPTION_CONTEXT) != 0)
+ ubifs_assert(inode_is_locked(host));
if (size > UBIFS_MAX_INO_DATA)
return -ERANGE;
--
2.7.3
...and mark the dentry as encrypted.
Signed-off-by: Richard Weinberger <[email protected]>
---
fs/ubifs/dir.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c
index a77edc5ca5c9..7ee32768ba19 100644
--- a/fs/ubifs/dir.c
+++ b/fs/ubifs/dir.c
@@ -218,6 +218,21 @@ static struct dentry *ubifs_lookup(struct inode *dir, struct dentry *dentry,
dbg_gen("'%pd' in dir ino %lu", dentry, dir->i_ino);
+ if (ubifs_crypt_is_encrypted(dir)) {
+ err = fscrypt_get_encryption_info(dir);
+
+ /*
+ * DCACHE_ENCRYPTED_WITH_KEY is set if the dentry is
+ * created while the directory was encrypted and we
+ * have access to the key.
+ */
+ if (fscrypt_has_encryption_key(dir))
+ fscrypt_set_encrypted_dentry(dentry);
+ fscrypt_set_d_op(dentry);
+ if (err && err != -ENOKEY)
+ return ERR_PTR(err);
+ }
+
if (dentry->d_name.len > UBIFS_MAX_NLEN)
return ERR_PTR(-ENAMETOOLONG);
--
2.7.3
We have to make sure that we don't expose our internal
crypto context to userspace.
Signed-off-by: Richard Weinberger <[email protected]>
---
fs/ubifs/xattr.c | 19 +++++++++++++++----
1 file changed, 15 insertions(+), 4 deletions(-)
diff --git a/fs/ubifs/xattr.c b/fs/ubifs/xattr.c
index b390902c48aa..54ec2b9fd506 100644
--- a/fs/ubifs/xattr.c
+++ b/fs/ubifs/xattr.c
@@ -394,6 +394,20 @@ out_unlock:
return err;
}
+static bool xattr_visible(const char *name)
+{
+ /* File encryption related xattrs are for internal use only */
+ if (strcmp(name, UBIFS_XATTR_NAME_ENCRYPTION_CONTEXT) == 0)
+ return false;
+
+ /* Show trusted namespace only for "power" users */
+ if (strncmp(name, XATTR_TRUSTED_PREFIX,
+ XATTR_TRUSTED_PREFIX_LEN) == 0 && !capable(CAP_SYS_ADMIN))
+ return false;
+
+ return true;
+}
+
ssize_t ubifs_listxattr(struct dentry *dentry, char *buffer, size_t size)
{
union ubifs_key key;
@@ -429,10 +443,7 @@ ssize_t ubifs_listxattr(struct dentry *dentry, char *buffer, size_t size)
nm.name = xent->name;
nm.len = le16_to_cpu(xent->nlen);
- /* Show trusted namespace only for "power" users */
- if (strncmp(xent->name, XATTR_TRUSTED_PREFIX,
- XATTR_TRUSTED_PREFIX_LEN) ||
- capable(CAP_SYS_ADMIN)) {
+ if (xattr_visible(xent->name)) {
memcpy(buffer + written, nm.name, nm.len + 1);
written += nm.len + 1;
}
--
2.7.3
We need the ->open() hook to load the crypto context
which is needed for all crypto operations within that
directory.
Signed-off-by: Richard Weinberger <[email protected]>
---
fs/ubifs/dir.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c
index 0e47127352ed..e80de3852c3e 100644
--- a/fs/ubifs/dir.c
+++ b/fs/ubifs/dir.c
@@ -1196,6 +1196,14 @@ int ubifs_getattr(struct vfsmount *mnt, struct dentry *dentry,
return 0;
}
+static int ubifs_dir_open(struct inode *dir, struct file *file)
+{
+ if (ubifs_crypt_is_encrypted(dir))
+ return fscrypt_get_encryption_info(dir) ? -EACCES : 0;
+
+ return 0;
+}
+
const struct inode_operations ubifs_dir_inode_operations = {
.lookup = ubifs_lookup,
.create = ubifs_create,
@@ -1224,6 +1232,7 @@ const struct file_operations ubifs_dir_operations = {
.iterate_shared = ubifs_readdir,
.fsync = ubifs_fsync,
.unlocked_ioctl = ubifs_ioctl,
+ .open = ubifs_dir_open,
#ifdef CONFIG_COMPAT
.compat_ioctl = ubifs_compat_ioctl,
#endif
--
2.7.3
Not all filesystems operate on pages, therefore offer
operations to en/decrypt buffers.
Of course these buffers have to be allocated in a way such that
the kernel crypto framework can work with them.
Signed-off-by: Richard Weinberger <[email protected]>
---
fs/crypto/crypto.c | 63 +++++++++++++++++++++++++++++++++++++++---------
include/linux/fscrypto.h | 24 ++++++++++++++++++
2 files changed, 76 insertions(+), 11 deletions(-)
diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c
index c502c116924c..1c2f9516b4be 100644
--- a/fs/crypto/crypto.c
+++ b/fs/crypto/crypto.c
@@ -147,15 +147,14 @@ typedef enum {
FS_ENCRYPT,
} fscrypt_direction_t;
-static int do_page_crypto(struct inode *inode,
- fscrypt_direction_t rw, pgoff_t index,
- struct page *src_page, struct page *dest_page,
- gfp_t gfp_flags)
+static int do_crypto(struct inode *inode,
+ fscrypt_direction_t rw, pgoff_t index,
+ struct scatterlist *src, struct scatterlist *dst,
+ unsigned int cryptlen, gfp_t gfp_flags)
{
u8 xts_tweak[FS_XTS_TWEAK_SIZE];
struct skcipher_request *req = NULL;
DECLARE_FS_COMPLETION_RESULT(ecr);
- struct scatterlist dst, src;
struct fscrypt_info *ci = inode->i_crypt_info;
struct crypto_skcipher *tfm = ci->ci_ctfm;
int res = 0;
@@ -177,12 +176,8 @@ static int do_page_crypto(struct inode *inode,
memset(&xts_tweak[sizeof(index)], 0,
FS_XTS_TWEAK_SIZE - sizeof(index));
- sg_init_table(&dst, 1);
- sg_set_page(&dst, dest_page, PAGE_SIZE, 0);
- sg_init_table(&src, 1);
- sg_set_page(&src, src_page, PAGE_SIZE, 0);
- skcipher_request_set_crypt(req, &src, &dst, PAGE_SIZE,
- xts_tweak);
+ skcipher_request_set_crypt(req, src, dst, cryptlen,
+ xts_tweak);
if (rw == FS_DECRYPT)
res = crypto_skcipher_decrypt(req);
else
@@ -202,6 +197,34 @@ static int do_page_crypto(struct inode *inode,
return 0;
}
+static int do_page_crypto(struct inode *inode,
+ fscrypt_direction_t rw, pgoff_t index,
+ struct page *src_page, struct page *dst_page,
+ gfp_t gfp_flags)
+{
+ struct scatterlist src, dst;
+
+ sg_init_table(&src, 1);
+ sg_set_page(&src, src_page, PAGE_SIZE, 0);
+ sg_init_table(&dst, 1);
+ sg_set_page(&dst, dst_page, PAGE_SIZE, 0);
+
+ return do_crypto(inode, rw, index, &src, &dst, PAGE_SIZE, gfp_flags);
+}
+
+static int do_buf_crypto(struct inode *inode,
+ fscrypt_direction_t rw, pgoff_t index,
+ const void *src_buf, const void *dst_buf,
+ unsigned int buflen, gfp_t gfp_flags)
+{
+ struct scatterlist src, dst;
+
+ sg_init_one(&src, src_buf, buflen);
+ sg_init_one(&dst, dst_buf, buflen);
+
+ return do_crypto(inode, rw, index, &src, &dst, buflen, gfp_flags);
+}
+
static struct page *alloc_bounce_page(struct fscrypt_ctx *ctx, gfp_t gfp_flags)
{
ctx->w.bounce_page = mempool_alloc(fscrypt_bounce_page_pool, gfp_flags);
@@ -264,6 +287,24 @@ errout:
}
EXPORT_SYMBOL(fscrypt_encrypt_page);
+int fscrypt_encrypt_buffer(struct inode *inode, const void *plaintext_buf,
+ const void *ciphertext_buf, unsigned int buflen,
+ pgoff_t index, gfp_t gfp_flags)
+{
+ return do_buf_crypto(inode, FS_ENCRYPT, index, plaintext_buf,
+ ciphertext_buf, buflen, gfp_flags);
+}
+EXPORT_SYMBOL(fscrypt_encrypt_buffer);
+
+int fscrypt_decrypt_buffer(struct inode *inode, const void *ciphertext_buf,
+ const void *plaintext_buf, unsigned int buflen,
+ pgoff_t index, gfp_t gfp_flags)
+{
+ return do_buf_crypto(inode, FS_DECRYPT, index, ciphertext_buf,
+ plaintext_buf, buflen, gfp_flags);
+}
+EXPORT_SYMBOL(fscrypt_decrypt_buffer);
+
/**
* f2crypt_decrypt_page() - Decrypts a page in-place
* @page: The page to decrypt. Must be locked.
diff --git a/include/linux/fscrypto.h b/include/linux/fscrypto.h
index 76cff18bb032..a9628b4882e7 100644
--- a/include/linux/fscrypto.h
+++ b/include/linux/fscrypto.h
@@ -273,6 +273,12 @@ extern void fscrypt_pullback_bio_page(struct page **, bool);
extern void fscrypt_restore_control_page(struct page *);
extern int fscrypt_zeroout_range(struct inode *, pgoff_t, sector_t,
unsigned int);
+int fscrypt_encrypt_buffer(struct inode *inode, const void *plaintext_buf,
+ const void *ciphertext_buf, unsigned int buflen,
+ pgoff_t index, gfp_t gfp_flags);
+int fscrypt_decrypt_buffer(struct inode *inode, const void *ciphertext_buf,
+ const void *plaintext_buf, unsigned int buflen,
+ pgoff_t index, gfp_t gfp_flags);
/* policy.c */
extern int fscrypt_process_policy(struct file *, const struct fscrypt_policy *);
extern int fscrypt_get_policy(struct inode *, struct fscrypt_policy *);
@@ -418,6 +424,24 @@ static inline void fscrypt_notsupp_fname_free_buffer(struct fscrypt_str *c)
return;
}
+static inline int fscrypt_notsupp_encrypt_buffer(const struct inode *inode,
+ const void *plaintext_buf,
+ const void *ciphertext_buf,
+ unsigned int buflen,
+ pgoff_t index, gfp_t gfp_flags)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int fscrypt_notsupp_decrypt_buffer(const struct inode *inode,
+ const void *ciphertext_buf,
+ const void *plaintext_buf,
+ unsigned int buflen,
+ pgoff_t index, gfp_t gfp_flags)
+{
+ return -EOPNOTSUPP;
+}
+
static inline int fscrypt_notsupp_fname_disk_to_usr(struct inode *inode,
u32 hash, u32 minor_hash,
const struct fscrypt_str *iname,
--
2.7.3
This is the first building block to provide file level
encryption on UBIFS.
Signed-off-by: Richard Weinberger <[email protected]>
---
fs/ubifs/Kconfig | 11 +++++++++++
fs/ubifs/Makefile | 1 +
fs/ubifs/crypto.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
fs/ubifs/dir.c | 28 +++++++++++++++++++++++++++-
fs/ubifs/ioctl.c | 35 +++++++++++++++++++++++++++++++++++
fs/ubifs/super.c | 10 ++++++++++
fs/ubifs/ubifs-media.h | 2 ++
fs/ubifs/ubifs.h | 39 ++++++++++++++++++++++++++++++++++++++-
fs/ubifs/xattr.c | 10 ++++++++++
9 files changed, 179 insertions(+), 2 deletions(-)
create mode 100644 fs/ubifs/crypto.c
diff --git a/fs/ubifs/Kconfig b/fs/ubifs/Kconfig
index 7ff7712f284e..0a908ae7af13 100644
--- a/fs/ubifs/Kconfig
+++ b/fs/ubifs/Kconfig
@@ -50,3 +50,14 @@ config UBIFS_ATIME_SUPPORT
strictatime is the "heavy", relatime is "lighter", etc.
If unsure, say 'N'
+
+config UBIFS_FS_ENCRYPTION
+ bool "UBIFS Encryption"
+ depends on UBIFS_FS
+ select FS_ENCRYPTION
+ default n
+ help
+ Enable encryption of UBIFS 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.
diff --git a/fs/ubifs/Makefile b/fs/ubifs/Makefile
index c54a24360f85..6f3251c2bf08 100644
--- a/fs/ubifs/Makefile
+++ b/fs/ubifs/Makefile
@@ -5,3 +5,4 @@ ubifs-y += tnc.o master.o scan.o replay.o log.o commit.o gc.o orphan.o
ubifs-y += budget.o find.o tnc_commit.o compress.o lpt.o lprops.o
ubifs-y += recovery.o ioctl.o lpt_commit.o tnc_misc.o xattr.o debug.o
ubifs-y += misc.o
+ubifs-$(CONFIG_UBIFS_FS_ENCRYPTION) += crypto.o
diff --git a/fs/ubifs/crypto.c b/fs/ubifs/crypto.c
new file mode 100644
index 000000000000..160c31479e0c
--- /dev/null
+++ b/fs/ubifs/crypto.c
@@ -0,0 +1,45 @@
+#include "ubifs.h"
+
+static int ubifs_crypt_get_context(struct inode *inode, void *ctx, size_t len)
+{
+ return ubifs_xattr_get(inode, UBIFS_XATTR_NAME_ENCRYPTION_CONTEXT,
+ ctx, len);
+}
+
+static int ubifs_crypt_set_context(struct inode *inode, const void *ctx,
+ size_t len, void *fs_data)
+{
+ return ubifs_xattr_set(inode, UBIFS_XATTR_NAME_ENCRYPTION_CONTEXT,
+ ctx, len, 0);
+}
+
+static bool ubifs_crypt_empty_dir(struct inode *inode)
+{
+ return ubifs_check_dir_empty(inode) == 0;
+}
+
+static unsigned int ubifs_crypt_max_namelen(struct inode *inode)
+{
+ if (S_ISLNK(inode->i_mode))
+ return UBIFS_MAX_INO_DATA;
+ else
+ return UBIFS_MAX_NLEN;
+}
+
+static int ubifs_key_prefix(struct inode *inode, u8 **key)
+{
+ static char prefix[] = "ubifs:";
+
+ *key = prefix;
+
+ return sizeof(prefix) - 1;
+}
+
+struct fscrypt_operations ubifs_crypt_operations = {
+ .get_context = ubifs_crypt_get_context,
+ .set_context = ubifs_crypt_set_context,
+ .is_encrypted = ubifs_crypt_is_encrypted,
+ .empty_dir = ubifs_crypt_empty_dir,
+ .max_namelen = ubifs_crypt_max_namelen,
+ .key_prefix = ubifs_key_prefix,
+};
diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c
index 177343bce143..0e47127352ed 100644
--- a/fs/ubifs/dir.c
+++ b/fs/ubifs/dir.c
@@ -85,11 +85,26 @@ static int inherit_flags(const struct inode *dir, umode_t mode)
* initializes it. Returns new inode in case of success and an error code in
* case of failure.
*/
-struct inode *ubifs_new_inode(struct ubifs_info *c, const struct inode *dir,
+struct inode *ubifs_new_inode(struct ubifs_info *c, struct inode *dir,
umode_t mode)
{
+ int err;
struct inode *inode;
struct ubifs_inode *ui;
+ bool encrypted = false;
+
+ if (ubifs_crypt_is_encrypted(dir)) {
+ err = fscrypt_get_encryption_info(dir);
+ if (err) {
+ ubifs_err(c, "fscrypt_get_encryption_info failed: %i", err);
+ return ERR_PTR(err);
+ }
+
+ if (!fscrypt_has_encryption_key(dir))
+ return ERR_PTR(-EPERM);
+
+ encrypted = true;
+ }
inode = new_inode(c->vfs_sb);
ui = ubifs_inode(inode);
@@ -165,6 +180,17 @@ struct inode *ubifs_new_inode(struct ubifs_info *c, const struct inode *dir,
*/
ui->creat_sqnum = ++c->max_sqnum;
spin_unlock(&c->cnt_lock);
+
+ if (encrypted) {
+ err = fscrypt_inherit_context(dir, inode, &encrypted, true);
+ if (err) {
+ ubifs_err(c, "fscrypt_inherit_context failed: %i", err);
+ make_bad_inode(inode);
+ iput(inode);
+ return ERR_PTR(err);
+ }
+ }
+
return inode;
}
diff --git a/fs/ubifs/ioctl.c b/fs/ubifs/ioctl.c
index 3c7b29de0ca7..6bb5b35050de 100644
--- a/fs/ubifs/ioctl.c
+++ b/fs/ubifs/ioctl.c
@@ -181,6 +181,41 @@ long ubifs_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
mnt_drop_write_file(file);
return err;
}
+ case FS_IOC_SET_ENCRYPTION_POLICY: {
+#ifdef CONFIG_UBIFS_FS_ENCRYPTION
+ struct fscrypt_policy policy;
+
+ if (copy_from_user(&policy,
+ (struct fscrypt_policy __user *)arg,
+ sizeof(policy)))
+ return -EFAULT;
+
+ err = fscrypt_process_policy(file, &policy);
+
+ return err;
+#else
+ return -EOPNOTSUPP;
+#endif
+ }
+ case FS_IOC_GET_ENCRYPTION_POLICY: {
+#ifdef CONFIG_UBIFS_FS_ENCRYPTION
+ struct fscrypt_policy policy;
+
+ if (!ubifs_crypt_is_encrypted(inode))
+ return -ENOENT;
+
+ err = fscrypt_get_policy(inode, &policy);
+ if (err)
+ return err;
+
+ if (copy_to_user((void __user *)arg, &policy, sizeof(policy)))
+ return -EFAULT;
+
+ return 0;
+#else
+ return -EOPNOTSUPP;
+#endif
+ }
default:
return -ENOTTY;
diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c
index 4ec051089186..a31222947265 100644
--- a/fs/ubifs/super.c
+++ b/fs/ubifs/super.c
@@ -380,6 +380,9 @@ out:
}
done:
clear_inode(inode);
+#ifdef CONFIG_UBIFS_FS_ENCRYPTION
+ fscrypt_put_encryption_info(inode, NULL);
+#endif
}
static void ubifs_dirty_inode(struct inode *inode, int flags)
@@ -1995,6 +1998,12 @@ static struct ubifs_info *alloc_ubifs_info(struct ubi_volume_desc *ubi)
return c;
}
+#ifndef CONFIG_UBIFS_FS_ENCRYPTION
+struct fscrypt_operations ubifs_crypt_operations = {
+ .is_encrypted = __ubifs_crypt_is_encrypted,
+};
+#endif
+
static int ubifs_fill_super(struct super_block *sb, void *data, int silent)
{
struct ubifs_info *c = sb->s_fs_info;
@@ -2041,6 +2050,7 @@ static int ubifs_fill_super(struct super_block *sb, void *data, int silent)
sb->s_maxbytes = c->max_inode_sz = MAX_LFS_FILESIZE;
sb->s_op = &ubifs_super_operations;
sb->s_xattr = ubifs_xattr_handlers;
+ sb->s_cop = &ubifs_crypt_operations;
mutex_lock(&c->umount_mutex);
err = mount_ubifs(c);
diff --git a/fs/ubifs/ubifs-media.h b/fs/ubifs/ubifs-media.h
index d47e9569b3de..aa302b11aec8 100644
--- a/fs/ubifs/ubifs-media.h
+++ b/fs/ubifs/ubifs-media.h
@@ -316,6 +316,7 @@ enum {
* UBIFS_APPEND_FL: writes to the inode may only append data
* UBIFS_DIRSYNC_FL: I/O on this directory inode has to be synchronous
* UBIFS_XATTR_FL: this inode is the inode for an extended attribute value
+ * UBIFS_CRYPT_FL: use encryption for this inode
*
* Note, these are on-flash flags which correspond to ioctl flags
* (@FS_COMPR_FL, etc). They have the same values now, but generally, do not
@@ -328,6 +329,7 @@ enum {
UBIFS_APPEND_FL = 0x08,
UBIFS_DIRSYNC_FL = 0x10,
UBIFS_XATTR_FL = 0x20,
+ UBIFS_CRYPT_FL = 0x40,
};
/* Inode flag bits used by UBIFS */
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index 94c1f5563c01..63dd027727ca 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -38,6 +38,7 @@
#include <linux/backing-dev.h>
#include <linux/security.h>
#include <linux/xattr.h>
+#include <linux/fscrypto.h>
#include "ubifs-media.h"
/* Version of this UBIFS implementation */
@@ -1727,7 +1728,7 @@ int ubifs_update_time(struct inode *inode, struct timespec *time, int flags);
#endif
/* dir.c */
-struct inode *ubifs_new_inode(struct ubifs_info *c, const struct inode *dir,
+struct inode *ubifs_new_inode(struct ubifs_info *c, struct inode *dir,
umode_t mode);
int ubifs_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat);
@@ -1776,10 +1777,46 @@ void ubifs_compress(const struct ubifs_info *c, const void *in_buf, int in_len,
int ubifs_decompress(const struct ubifs_info *c, const void *buf, int len,
void *out, int *out_len, int compr_type);
+extern struct fscrypt_operations ubifs_crypt_operations;
+
#include "debug.h"
#include "misc.h"
#include "key.h"
+#ifndef CONFIG_UBIFS_FS_ENCRYPTION
+#define fscrypt_set_d_op(i)
+#define fscrypt_get_ctx fscrypt_notsupp_get_ctx
+#define fscrypt_release_ctx fscrypt_notsupp_release_ctx
+#define fscrypt_encrypt_page fscrypt_notsupp_encrypt_page
+#define fscrypt_decrypt_page fscrypt_notsupp_decrypt_page
+#define fscrypt_encrypt_buffer fscrypt_notsupp_encrypt_buffer
+#define fscrypt_decrypt_buffer fscrypt_notsupp_decrypt_buffer
+#define fscrypt_decrypt_bio_pages fscrypt_notsupp_decrypt_bio_pages
+#define fscrypt_pullback_bio_page fscrypt_notsupp_pullback_bio_page
+#define fscrypt_restore_control_page fscrypt_notsupp_restore_control_page
+#define fscrypt_zeroout_range fscrypt_notsupp_zeroout_range
+#define fscrypt_process_policy fscrypt_notsupp_process_policy
+#define fscrypt_get_policy fscrypt_notsupp_get_policy
+#define fscrypt_has_permitted_context fscrypt_notsupp_has_permitted_context
+#define fscrypt_inherit_context fscrypt_notsupp_inherit_context
+#define fscrypt_get_encryption_info fscrypt_notsupp_get_encryption_info
+#define fscrypt_put_encryption_info fscrypt_notsupp_put_encryption_info
+#define fscrypt_setup_filename fscrypt_notsupp_setup_filename
+#define fscrypt_free_filename fscrypt_notsupp_free_filename
+#define fscrypt_fname_encrypted_size fscrypt_notsupp_fname_encrypted_size
+#define fscrypt_fname_alloc_buffer fscrypt_notsupp_fname_alloc_buffer
+#define fscrypt_fname_free_buffer fscrypt_notsupp_fname_free_buffer
+#define fscrypt_fname_disk_to_usr fscrypt_notsupp_fname_disk_to_usr
+#define fscrypt_fname_usr_to_disk fscrypt_notsupp_fname_usr_to_disk
+#endif
+
+static inline bool ubifs_crypt_is_encrypted(struct inode *inode)
+{
+ struct ubifs_inode *ui = ubifs_inode(inode);
+
+ return ui->flags & UBIFS_CRYPT_FL;
+}
+
/* Normal UBIFS messages */
__printf(2, 3)
void ubifs_msg(const struct ubifs_info *c, const char *fmt, ...);
diff --git a/fs/ubifs/xattr.c b/fs/ubifs/xattr.c
index ad4463752709..b390902c48aa 100644
--- a/fs/ubifs/xattr.c
+++ b/fs/ubifs/xattr.c
@@ -158,6 +158,15 @@ static int create_xattr(struct ubifs_info *c, struct inode *host,
host_ui->xattr_size += CALC_XATTR_BYTES(size);
host_ui->xattr_names += nm->len;
+ /*
+ * We handle UBIFS_XATTR_NAME_ENCRYPTION_CONTEXT here because we
+ * have to set the UBIFS_CRYPT_FL flag on the host inode.
+ * To avoid multiple updates of the same inode in the same operation,
+ * let's do it here.
+ */
+ if (strcmp(nm->name, UBIFS_XATTR_NAME_ENCRYPTION_CONTEXT) == 0)
+ host_ui->flags |= UBIFS_CRYPT_FL;
+
err = ubifs_jnl_update(c, host, nm, inode, 0, 1);
if (err)
goto out_cancel;
@@ -172,6 +181,7 @@ out_cancel:
host_ui->xattr_cnt -= 1;
host_ui->xattr_size -= CALC_DENT_SIZE(nm->len);
host_ui->xattr_size -= CALC_XATTR_BYTES(size);
+ host_ui->flags &= ~UBIFS_CRYPT_FL;
mutex_unlock(&host_ui->ui_mutex);
out_free:
make_bad_inode(inode);
--
2.7.3
We need ->open() for files to load the crypto key.
If the no key is present and the file is encrypted,
refuse to open.
Signed-off-by: Richard Weinberger <[email protected]>
---
fs/ubifs/file.c | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
index 7bbf420d1289..fc79278969e5 100644
--- a/fs/ubifs/file.c
+++ b/fs/ubifs/file.c
@@ -1605,6 +1605,35 @@ static int ubifs_file_mmap(struct file *file, struct vm_area_struct *vma)
return 0;
}
+static int ubifs_file_open(struct inode *inode, struct file *filp)
+{
+ int ret;
+ struct dentry *dir;
+ struct ubifs_info *c = inode->i_sb->s_fs_info;
+
+ if (ubifs_crypt_is_encrypted(inode)) {
+ ret = fscrypt_get_encryption_info(inode);
+ if (ret)
+ return -EACCES;
+ if (!fscrypt_has_encryption_key(inode))
+ return -ENOKEY;
+ }
+
+ dir = dget_parent(file_dentry(filp));
+ if (ubifs_crypt_is_encrypted(d_inode(dir)) &&
+ !fscrypt_has_permitted_context(d_inode(dir), inode)) {
+ ubifs_err(c, "Inconsistent encryption contexts: %lu/%lu",
+ (unsigned long) d_inode(dir)->i_ino,
+ (unsigned long) inode->i_ino);
+ dput(dir);
+ ubifs_ro_mode(c, -EPERM);
+ return -EPERM;
+ }
+ dput(dir);
+
+ return 0;
+}
+
const struct address_space_operations ubifs_file_address_operations = {
.readpage = ubifs_readpage,
.writepage = ubifs_writepage,
@@ -1653,6 +1682,7 @@ const struct file_operations ubifs_file_operations = {
.unlocked_ioctl = ubifs_ioctl,
.splice_read = generic_file_splice_read,
.splice_write = iter_file_splice_write,
+ .open = ubifs_file_open,
#ifdef CONFIG_COMPAT
.compat_ioctl = ubifs_compat_ioctl,
#endif
--
2.7.3
For fscrypto we need this function outside of xattr.c.
Signed-off-by: Richard Weinberger <[email protected]>
---
fs/ubifs/ubifs.h | 4 ++++
fs/ubifs/xattr.c | 35 ++++++++++++++++++-----------------
2 files changed, 22 insertions(+), 17 deletions(-)
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index f7d649eb61d5..94c1f5563c01 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -1738,6 +1738,10 @@ extern const struct xattr_handler *ubifs_xattr_handlers[];
ssize_t ubifs_listxattr(struct dentry *dentry, char *buffer, size_t size);
int ubifs_init_security(struct inode *dentry, struct inode *inode,
const struct qstr *qstr);
+int ubifs_xattr_set(struct inode *host, const char *name, const void *value,
+ size_t size, int flags);
+ssize_t ubifs_xattr_get(struct inode *host, const char *name, void *buf,
+ size_t size);
/* super.c */
struct inode *ubifs_iget(struct super_block *sb, unsigned long inum);
diff --git a/fs/ubifs/xattr.c b/fs/ubifs/xattr.c
index 11a004114eba..ad4463752709 100644
--- a/fs/ubifs/xattr.c
+++ b/fs/ubifs/xattr.c
@@ -266,8 +266,8 @@ static struct inode *iget_xattr(struct ubifs_info *c, ino_t inum)
return ERR_PTR(-EINVAL);
}
-static int __ubifs_setxattr(struct inode *host, const char *name,
- const void *value, size_t size, int flags)
+int ubifs_xattr_set(struct inode *host, const char *name, const void *value,
+ size_t size, int flags)
{
struct inode *inode;
struct ubifs_info *c = host->i_sb->s_fs_info;
@@ -326,8 +326,8 @@ out_free:
return err;
}
-static ssize_t __ubifs_getxattr(struct inode *host, const char *name,
- void *buf, size_t size)
+ssize_t ubifs_xattr_get(struct inode *host, const char *name, void *buf,
+ size_t size)
{
struct inode *inode;
struct ubifs_info *c = host->i_sb->s_fs_info;
@@ -482,7 +482,7 @@ out_cancel:
return err;
}
-static int __ubifs_removexattr(struct inode *host, const char *name)
+static int ubifs_xattr_remove(struct inode *host, const char *name)
{
struct inode *inode;
struct ubifs_info *c = host->i_sb->s_fs_info;
@@ -544,7 +544,8 @@ static int init_xattrs(struct inode *inode, const struct xattr *xattr_array,
}
strcpy(name, XATTR_SECURITY_PREFIX);
strcpy(name + XATTR_SECURITY_PREFIX_LEN, xattr->name);
- err = __ubifs_setxattr(inode, name, xattr->value, xattr->value_len, 0);
+ err = ubifs_xattr_set(inode, name, xattr->value,
+ xattr->value_len, 0);
kfree(name);
if (err < 0)
break;
@@ -568,7 +569,7 @@ int ubifs_init_security(struct inode *dentry, struct inode *inode,
return err;
}
-static int ubifs_xattr_get(const struct xattr_handler *handler,
+static int xattr_get(const struct xattr_handler *handler,
struct dentry *dentry, struct inode *inode,
const char *name, void *buffer, size_t size)
{
@@ -576,10 +577,10 @@ static int ubifs_xattr_get(const struct xattr_handler *handler,
inode->i_ino, dentry, size);
name = xattr_full_name(handler, name);
- return __ubifs_getxattr(inode, name, buffer, size);
+ return ubifs_xattr_get(inode, name, buffer, size);
}
-static int ubifs_xattr_set(const struct xattr_handler *handler,
+static int xattr_set(const struct xattr_handler *handler,
struct dentry *dentry, struct inode *inode,
const char *name, const void *value,
size_t size, int flags)
@@ -590,27 +591,27 @@ static int ubifs_xattr_set(const struct xattr_handler *handler,
name = xattr_full_name(handler, name);
if (value)
- return __ubifs_setxattr(inode, name, value, size, flags);
+ return ubifs_xattr_set(inode, name, value, size, flags);
else
- return __ubifs_removexattr(inode, name);
+ return ubifs_xattr_remove(inode, name);
}
static const struct xattr_handler ubifs_user_xattr_handler = {
.prefix = XATTR_USER_PREFIX,
- .get = ubifs_xattr_get,
- .set = ubifs_xattr_set,
+ .get = xattr_get,
+ .set = xattr_set,
};
static const struct xattr_handler ubifs_trusted_xattr_handler = {
.prefix = XATTR_TRUSTED_PREFIX,
- .get = ubifs_xattr_get,
- .set = ubifs_xattr_set,
+ .get = xattr_get,
+ .set = xattr_set,
};
static const struct xattr_handler ubifs_security_xattr_handler = {
.prefix = XATTR_SECURITY_PREFIX,
- .get = ubifs_xattr_get,
- .set = ubifs_xattr_set,
+ .get = xattr_get,
+ .set = xattr_set,
};
const struct xattr_handler *ubifs_xattr_handlers[] = {
--
2.7.3
fscrypto will need this function too. Also get struct ubifs_info
from the provided inode. Not all callers will have a reference to
struct ubifs_info.
Signed-off-by: Richard Weinberger <[email protected]>
---
fs/ubifs/dir.c | 8 ++++----
fs/ubifs/ubifs.h | 1 +
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c
index 4b86d3a738e1..177343bce143 100644
--- a/fs/ubifs/dir.c
+++ b/fs/ubifs/dir.c
@@ -617,15 +617,15 @@ out_cancel:
/**
* check_dir_empty - check if a directory is empty or not.
- * @c: UBIFS file-system description object
* @dir: VFS inode object of the directory to check
*
* This function checks if directory @dir is empty. Returns zero if the
* directory is empty, %-ENOTEMPTY if it is not, and other negative error codes
* in case of of errors.
*/
-static int check_dir_empty(struct ubifs_info *c, struct inode *dir)
+int ubifs_check_dir_empty(struct inode *dir)
{
+ struct ubifs_info *c = dir->i_sb->s_fs_info;
struct qstr nm = { .name = NULL };
struct ubifs_dent_node *dent;
union ubifs_key key;
@@ -663,7 +663,7 @@ static int ubifs_rmdir(struct inode *dir, struct dentry *dentry)
inode->i_ino, dir->i_ino);
ubifs_assert(inode_is_locked(dir));
ubifs_assert(inode_is_locked(inode));
- err = check_dir_empty(c, d_inode(dentry));
+ err = ubifs_check_dir_empty(d_inode(dentry));
if (err)
return err;
@@ -1003,7 +1003,7 @@ static int ubifs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (unlink && is_dir) {
- err = check_dir_empty(c, new_inode);
+ err = ubifs_check_dir_empty(new_inode);
if (err)
return err;
}
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index 4617d459022a..f7d649eb61d5 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -1731,6 +1731,7 @@ struct inode *ubifs_new_inode(struct ubifs_info *c, const struct inode *dir,
umode_t mode);
int ubifs_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat);
+int ubifs_check_dir_empty(struct inode *dir);
/* xattr.c */
extern const struct xattr_handler *ubifs_xattr_handlers[];
--
2.7.3
On Fri, Oct 21, 2016 at 02:48:16PM +0200, Richard Weinberger wrote:
> Not all filesystems operate on pages, therefore offer
> operations to en/decrypt buffers.
> Of course these buffers have to be allocated in a way such that
> the kernel crypto framework can work with them.
Which means they need to be backed by the page allocator eventually.
I think we'd better off providing the pages from ubifs from the API
point of view. What are the issues with doing that?
Christoph,
On 21.10.2016 15:05, Christoph Hellwig wrote:
> On Fri, Oct 21, 2016 at 02:48:16PM +0200, Richard Weinberger wrote:
>> Not all filesystems operate on pages, therefore offer
>> operations to en/decrypt buffers.
>> Of course these buffers have to be allocated in a way such that
>> the kernel crypto framework can work with them.
>
> Which means they need to be backed by the page allocator eventually.
> I think we'd better off providing the pages from ubifs from the API
> point of view. What are the issues with doing that?
>
UBIFS works on kmalloc()'ed buffers where it constructs the NAND/NOR pages
which will be written to the MTD. JFFS2 does the same.
So you suggest obtaining the struct page from the kmalloc()'ed buffer and
feeding it into fscrypto? This should work too.
I found the buffer operations approach more suitable.
Another reason why I did the buffer functions is because fscrypt_encrypt_page()
always allocates a bounce page as temporary memory. For ext4 this is needed,
for UBIFS not.
UBIFS has already a construction buffer, especially since it also does compression.
Thanks,
//richard
On Fri, Oct 21, 2016 at 03:17:03PM +0200, Richard Weinberger wrote:
> UBIFS works on kmalloc()'ed buffers where it constructs the NAND/NOR pages
> which will be written to the MTD. JFFS2 does the same.
Yes, you can trivially do a virt_to_page on a kmalloc buffer.
> Another reason why I did the buffer functions is because fscrypt_encrypt_page()
> always allocates a bounce page as temporary memory. For ext4 this is needed,
> for UBIFS not.
> UBIFS has already a construction buffer, especially since it also does compression.
We should defintively find a way to avoid that, but it's a separate
issue from adding another API just to pass buffers.
On Fri, Oct 21, 2016 at 02:48:17PM +0200, Richard Weinberger wrote:
> Some filesystems, such as UBIFS, maintain a const pointer
> for struct inode.
>
> /* fname.c */
> -extern int fscrypt_setup_filename(struct inode *, const struct qstr *,
> - int lookup, struct fscrypt_name *);
> +extern int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname,
> + int lookup, struct fscrypt_name *fname);
Was it deliberate that you didn't add a const pointer here?
I take it that ubifs is basically using const in certain places to
essentially promise that those functions don't actually modify the
inode structure?
- Ted
Ted,
On 21.10.2016 16:57, Theodore Ts'o wrote:
> On Fri, Oct 21, 2016 at 02:48:17PM +0200, Richard Weinberger wrote:
>> Some filesystems, such as UBIFS, maintain a const pointer
>> for struct inode.
>>
>> /* fname.c */
>> -extern int fscrypt_setup_filename(struct inode *, const struct qstr *,
>> - int lookup, struct fscrypt_name *);
>> +extern int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname,
>> + int lookup, struct fscrypt_name *fname);
>
> Was it deliberate that you didn't add a const pointer here?
Erm, no. Either I forgot or while rebasing my mess to something
sane the change got lost.
> I take it that ubifs is basically using const in certain places to
> essentially promise that those functions don't actually modify the
> inode structure?
Yes.
Thanks,
//richard
On Fri, Oct 21, 2016 at 06:24:00AM -0700, Christoph Hellwig wrote:
> > Another reason why I did the buffer functions is because fscrypt_encrypt_page()
> > always allocates a bounce page as temporary memory. For ext4 this is needed,
> > for UBIFS not.
> > UBIFS has already a construction buffer, especially since it also does compression.
>
> We should defintively find a way to avoid that, but it's a separate
> issue from adding another API just to pass buffers.
Hmm, one approach we could use is to avoid allocating a bounce page if
the passed-in plaintext_page has the PageSlab flag set. That would
work for ubifs, but if there are file systems that are using
get_free_page() for their particular construction buffer. it wouldn't
work for them.
Perhaps more importantly, are you planning on making compression +
encryption work? Some security purists will say that compression +
encryption will leak some information about the plaintext (which is
technically true, but it's much like the people who don't want make it
easy to discard + encrypt, which Linus recently railed against). So
my take is that as long as users understand that there are minor
leakage issues with compression + encryption, we should let them do
that --- and that would be an argument for supporting buffer
operations, and only requiring that the buffer size must be a multiple
of the underlying encryption block size.
- Ted
On Fri, Oct 21, 2016 at 02:48:30PM +0200, Richard Weinberger wrote:
> Signed-off-by: Richard Weinberger <[email protected]>
> ---
> fs/ubifs/file.c | 36 ++++++++++++++++++
> fs/ubifs/journal.c | 105 +++++++++++++++++++++++++++++++++++++++++++----------
> fs/ubifs/super.c | 6 ++-
> fs/ubifs/ubifs.h | 6 +++
> 4 files changed, 131 insertions(+), 22 deletions(-)
>
> diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
> index bd0049788e24..60e089d50c82 100644
> --- a/fs/ubifs/file.c
> +++ b/fs/ubifs/file.c
> @@ -78,6 +78,24 @@ static int read_block(struct inode *inode, void *addr, unsigned int block,
> goto dump;
>
> dlen = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ;
> +
> + if (ubifs_crypt_is_encrypted(inode)) {
> + int clen = le16_to_cpu(dn->compr_size);
> +
> + if (clen <= 0 || clen > UBIFS_BLOCK_SIZE || clen > dlen)
> + goto dump;
> +
> + ubifs_assert(dlen <= UBIFS_BLOCK_SIZE);
> + err = fscrypt_decrypt_buffer(inode, &dn->data, &dn->data, dlen, block, GFP_NOFS);
> + if (err) {
> + ubifs_err(c, "fscrypt_decrypt_buffer failed: %i", err);
> + return err;
> + }
> +
> + ubifs_assert(clen <= dlen);
> + dlen = clen;
> + }
> +
> out_len = UBIFS_BLOCK_SIZE;
> err = ubifs_decompress(c, &dn->data, dlen, addr, &out_len,
> le16_to_cpu(dn->compr_type));
> @@ -650,6 +668,24 @@ static int populate_page(struct ubifs_info *c, struct page *page,
>
> dlen = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ;
> out_len = UBIFS_BLOCK_SIZE;
> +
> + if (ubifs_crypt_is_encrypted(inode)) {
> + int clen = le16_to_cpu(dn->compr_size);
> +
> + if (clen <= 0 || clen > UBIFS_BLOCK_SIZE || clen > dlen)
> + goto out_err;
> +
> + ubifs_assert(dlen <= UBIFS_BLOCK_SIZE);
> + err = fscrypt_decrypt_buffer(inode, &dn->data, &dn->data, dlen, page_block, GFP_NOFS);
> + if (err) {
> + ubifs_err(c, "fscrypt_decrypt_buffer failed: %i", err);
> + goto out_err;
> + }
> +
> + ubifs_assert(clen <= dlen);
> + dlen = clen;
> + }
> +
> err = ubifs_decompress(c, &dn->data, dlen, addr, &out_len,
> le16_to_cpu(dn->compr_type));
> if (err || len != out_len)
> diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c
> index a643de4e3d91..da694e520ec8 100644
> --- a/fs/ubifs/journal.c
> +++ b/fs/ubifs/journal.c
> @@ -691,11 +691,15 @@ int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
> int err, lnum, offs, compr_type, out_len;
> int dlen = COMPRESSED_DATA_NODE_BUF_SZ, allocated = 1;
> struct ubifs_inode *ui = ubifs_inode(inode);
> + bool encrypted = ubifs_crypt_is_encrypted(inode);
>
> dbg_jnlk(key, "ino %lu, blk %u, len %d, key ",
> (unsigned long)key_inum(c, key), key_block(c, key), len);
> ubifs_assert(len <= UBIFS_BLOCK_SIZE);
>
> + if (encrypted)
> + dlen += UBIFS_CIPHER_BLOCK_SIZE;
> +
> data = kmalloc(dlen, GFP_NOFS | __GFP_NOWARN);
> if (!data) {
> /*
> @@ -724,6 +728,26 @@ int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode,
> ubifs_compress(c, buf, len, &data->data, &out_len, &compr_type);
Compress-before-encrypt is a hazard.
http://www.iacr.org/cryptodb/archive/2002/FSE/3091/3091.pdf
> ubifs_assert(out_len <= UBIFS_BLOCK_SIZE);
>
> + if (encrypted) {
> + void *p = &data->data;
> + int plen = round_up(out_len, UBIFS_CIPHER_BLOCK_SIZE);
> +
> + data->compr_size = cpu_to_le16(out_len);
> +
> + if (plen != out_len) {
> + memset(p + out_len, 0, plen - out_len);
> + out_len = plen;
> + }
> +
> + err = fscrypt_encrypt_buffer(inode, &data->data, &data->data, out_len, key_block(c, key), GFP_NOFS);
> + if (err) {
> + ubifs_err(c, "fscrypt_encrypt_buffer failed: %i", err);
> + goto out_free;
> + }
> + } else {
> + data->compr_size = 0;
> + }
> +
> dlen = UBIFS_DATA_NODE_SZ + out_len;
> data->compr_type = cpu_to_le16(compr_type);
>
> @@ -1083,31 +1107,79 @@ out_free:
> }
>
> /**
> - * recomp_data_node - re-compress a truncated data node.
> + * truncate_data_node - re-compress/encrypt a truncated data node.
> + * @c: UBIFS file-system description object
> + * @inode: inode which referes to the data node
> + * @block: data block number
> * @dn: data node to re-compress
> * @new_len: new length
> *
> * This function is used when an inode is truncated and the last data node of
> - * the inode has to be re-compressed and re-written.
> + * the inode has to be re-compressed/encrypted and re-written.
> */
> -static int recomp_data_node(const struct ubifs_info *c,
> - struct ubifs_data_node *dn, int *new_len)
> +static int truncate_data_node(const struct ubifs_info *c, struct inode *inode,
> + unsigned int block, struct ubifs_data_node *dn,
> + int *new_len)
> {
> void *buf;
> - int err, len, compr_type, out_len;
> + int err, dlen, compr_type, out_len, old_dlen;
>
> out_len = le32_to_cpu(dn->size);
> buf = kmalloc(out_len * WORST_COMPR_FACTOR, GFP_NOFS);
> if (!buf)
> return -ENOMEM;
>
> - len = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ;
> + dlen = old_dlen = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ;
> compr_type = le16_to_cpu(dn->compr_type);
> - err = ubifs_decompress(c, &dn->data, len, buf, &out_len, compr_type);
> - if (err)
> - goto out;
>
> - ubifs_compress(c, buf, *new_len, &dn->data, &out_len, &compr_type);
> + if (ubifs_crypt_is_encrypted(inode)) {
> + int clen = le16_to_cpu(dn->compr_size);
> +
> + if (clen <= 0 || clen > UBIFS_BLOCK_SIZE || clen > dlen) {
> + ubifs_err(c, "bad compr_size: %i", clen);
> + err = -EINVAL;
> + goto out;
> + }
> +
> + err = fscrypt_decrypt_buffer(inode, &dn->data, &dn->data, dlen, block, GFP_NOFS);
> + if (err) {
> + ubifs_err(c, "fscrypt_decrypt_buffer failed: %i", err);
> + goto out;
> + }
> +
> + ubifs_assert(clen <= dlen);
> + dlen = clen;
> + }
> +
> + if (compr_type != UBIFS_COMPR_NONE) {
> + err = ubifs_decompress(c, &dn->data, dlen, buf, &out_len, compr_type);
> + if (err)
> + goto out;
> +
> + ubifs_compress(c, buf, *new_len, &dn->data, &out_len, &compr_type);
> + }
> +
> + if (ubifs_crypt_is_encrypted(inode)) {
> + void *p = &dn->data;
> + int plen = round_up(out_len, UBIFS_CIPHER_BLOCK_SIZE);
> +
> + ubifs_assert(old_dlen >= plen);
> + dn->compr_size = cpu_to_le16(out_len);
> +
> + if (plen != out_len) {
> + memset(p + out_len, 0, plen - out_len);
> + out_len = plen;
> + }
> +
> + err = fscrypt_encrypt_buffer(inode, &dn->data, &dn->data, out_len, block, GFP_NOFS);
> + if (err) {
> + ubifs_msg(c, "fscrypt_encrypt_buffer failed: %i", err);
> + goto out;
> + }
> + } else {
> + dn->compr_size = 0;
> + }
> +
> ubifs_assert(out_len <= UBIFS_BLOCK_SIZE);
> dn->compr_type = cpu_to_le16(compr_type);
> dn->size = cpu_to_le32(*new_len);
> @@ -1179,16 +1251,9 @@ int ubifs_jnl_truncate(struct ubifs_info *c, const struct inode *inode,
> if (le32_to_cpu(dn->size) <= dlen)
> dlen = 0; /* Nothing to do */
> else {
> - int compr_type = le16_to_cpu(dn->compr_type);
> -
> - if (compr_type != UBIFS_COMPR_NONE) {
> - err = recomp_data_node(c, dn, &dlen);
> - if (err)
> - goto out_free;
> - } else {
> - dn->size = cpu_to_le32(dlen);
> - dlen += UBIFS_DATA_NODE_SZ;
> - }
> + err = truncate_data_node(c, inode, blk, dn, &dlen);
> + if (err)
> + goto out_free;
> }
> }
> }
> diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c
> index a31222947265..ae25c908fbe5 100644
> --- a/fs/ubifs/super.c
> +++ b/fs/ubifs/super.c
> @@ -1210,7 +1210,8 @@ static int mount_ubifs(struct ubifs_info *c)
> bu_init(c);
>
> if (!c->ro_mount) {
> - c->write_reserve_buf = kmalloc(COMPRESSED_DATA_NODE_BUF_SZ,
> + c->write_reserve_buf = kmalloc(COMPRESSED_DATA_NODE_BUF_SZ + \
> + UBIFS_CIPHER_BLOCK_SIZE,
> GFP_KERNEL);
> if (!c->write_reserve_buf)
> goto out_free;
> @@ -1623,7 +1624,8 @@ static int ubifs_remount_rw(struct ubifs_info *c)
> goto out;
> }
>
> - c->write_reserve_buf = kmalloc(COMPRESSED_DATA_NODE_BUF_SZ, GFP_KERNEL);
> + c->write_reserve_buf = kmalloc(COMPRESSED_DATA_NODE_BUF_SZ + \
> + UBIFS_CIPHER_BLOCK_SIZE, GFP_KERNEL);
> if (!c->write_reserve_buf) {
> err = -ENOMEM;
> goto out;
> diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
> index 63dd027727ca..6c56a08afcd7 100644
> --- a/fs/ubifs/ubifs.h
> +++ b/fs/ubifs/ubifs.h
> @@ -139,6 +139,12 @@
> */
> #define WORST_COMPR_FACTOR 2
>
> +#ifdef CONFIG_UBIFS_FS_ENCRYPTION
> +#define UBIFS_CIPHER_BLOCK_SIZE FS_CRYPTO_BLOCK_SIZE
> +#else
> +#define UBIFS_CIPHER_BLOCK_SIZE 0
> +#endif
> +
> /*
> * How much memory is needed for a buffer where we compress a data node.
> */
> --
> 2.7.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
On 21.10.2016 19:14, Michael Halcrow wrote:
> Compress-before-encrypt is a hazard.
>
> http://www.iacr.org/cryptodb/archive/2002/FSE/3091/3091.pdf
I'm fully aware of that. But as usual it depends on the use case.
Compression is optional in UBIFS, paranoid users can disable it
when encryption is enabled.
Thanks,
//richard
On Fri, Oct 21, 2016 at 02:48:41PM +0200, Richard Weinberger wrote:
> Starting with version 5 the following properties change:
> - UBIFS_FLG_DOUBLE_HASH is mandatory
> - UBIFS_FLG_ENCRYPTION is optional but depdens on UBIFS_FLG_DOUBLE_HASH
> - Filesystems with unknown super block flags will be rejected, this
> allows us in future to add new features without raising the UBIFS
> write version.
>
> Signed-off-by: Richard Weinberger <[email protected]>
> ---
> fs/ubifs/sb.c | 17 +++++++++++++++++
> fs/ubifs/ubifs-media.h | 4 +++-
> 2 files changed, 20 insertions(+), 1 deletion(-)
>
> diff --git a/fs/ubifs/sb.c b/fs/ubifs/sb.c
> index 54cef70ea16f..7f1ead29e727 100644
> --- a/fs/ubifs/sb.c
> +++ b/fs/ubifs/sb.c
> @@ -466,6 +466,16 @@ static int validate_sb(struct ubifs_info *c, struct ubifs_sb_node *sup)
> goto failed;
> }
>
> + if (!c->double_hash && c->fmt_version >= 5) {
> + err = 16;
> + goto failed;
> + }
> +
> + if (c->encrypted && c->fmt_version < 5) {
> + err = 17;
> + goto failed;
> + }
> +
> return 0;
>
> failed:
> @@ -624,6 +634,13 @@ int ubifs_read_superblock(struct ubifs_info *c)
> c->double_hash = !!(sup_flags & UBIFS_FLG_DOUBLE_HASH);
> c->encrypted = !!(sup_flags & UBIFS_FLG_ENCRYPTION);
>
> + if ((sup_flags & ~UBIFS_FLG_MASK) != 0) {
> + ubifs_err(c, "Unknown feature flags found: %#x",
> + sup_flags & ~UBIFS_FLG_MASK);
> + err = -EINVAL;
> + goto out;
> + }
> +
> #ifndef CONFIG_UBIFS_FS_ENCRYPTION
> if (c->encrypted) {
> ubifs_err(c, "file system contains encrypted files but UBIFS"
> diff --git a/fs/ubifs/ubifs-media.h b/fs/ubifs/ubifs-media.h
> index bdc7935a5e41..e8c23c9d4f4a 100644
> --- a/fs/ubifs/ubifs-media.h
> +++ b/fs/ubifs/ubifs-media.h
> @@ -46,7 +46,7 @@
> * UBIFS went into mainline kernel with format version 4. The older formats
> * were development formats.
> */
> -#define UBIFS_FORMAT_VERSION 4
> +#define UBIFS_FORMAT_VERSION 5
Alex Cope is working on a fix for file name encryption in ext4 so that
common plaintext prefixes don't result in common ciphertext prefixes.
Older kernels will not be able to read the new file names.
>
> /*
> * Read-only compatibility version. If the UBIFS format is changed, older UBIFS
> @@ -429,6 +429,8 @@ enum {
> UBIFS_FLG_ENCRYPTION = 0x10,
> };
>
> +#define UBIFS_FLG_MASK (UBIFS_FLG_BIGLPT|UBIFS_FLG_SPACE_FIXUP|UBIFS_FLG_DOUBLE_HASH|UBIFS_FLG_ENCRYPTION)
> +
> /**
> * struct ubifs_ch - common header node.
> * @magic: UBIFS node magic number (%UBIFS_NODE_MAGIC)
> --
> 2.7.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
On Fri, Oct 21, 2016 at 10:31:54AM -0700, Michael Halcrow wrote:
> > diff --git a/fs/ubifs/ubifs-media.h b/fs/ubifs/ubifs-media.h
> > index bdc7935a5e41..e8c23c9d4f4a 100644
> > --- a/fs/ubifs/ubifs-media.h
> > +++ b/fs/ubifs/ubifs-media.h
> > @@ -46,7 +46,7 @@
> > * UBIFS went into mainline kernel with format version 4. The older formats
> > * were development formats.
> > */
> > -#define UBIFS_FORMAT_VERSION 4
> > +#define UBIFS_FORMAT_VERSION 5
>
> Alex Cope is working on a fix for file name encryption in ext4 so that
> common plaintext prefixes don't result in common ciphertext prefixes.
> Older kernels will not be able to read the new file names.
To be clear, this will be done in the context of a new encryption
mode. In terms of how Ubifs will handle things, that's going to
depend on whether ubifs uses a single major version number or whether
they have a feature bitmask like other filesystems, including ext4.
This is better because if the user doesn't use a particular feature,
especially a feature like encryption which is optional, we don't want
the file system to reject the mount unnecessarily. In the case of
encryption, where there may be new encryption algorithms used, or
maybe even hardware-specific encryption modes if you are using in-line
encryption where the encryption is done in hardware, the question is
whether you want to refuse the mount if you know it won't work, or
just throw an error when there is an attempt to access a directory or
file which is encrypted using an encryption algorithm which a
particular kernel version doesn't support.
We did leave some room in the ext4 superblock for an encryption format
version number, as ewll as an array of which encryption algorithsm are
in use in the superblock, but we haven't really decided which strategy
we want to use.
My current thinking is that there ought to be a warning at mount time
if there are some directories which the kernel being used won't be
able to access, but we should just fail the mount entirely, since
there might be a lot of unencrypted files that the user might want to access.
Cheers,
- Ted
On Fri, Oct 21, 2016 at 07:21:04PM +0200, Richard Weinberger wrote:
> On 21.10.2016 19:14, Michael Halcrow wrote:
> > Compress-before-encrypt is a hazard.
> >
> > http://www.iacr.org/cryptodb/archive/2002/FSE/3091/3091.pdf
>
> I'm fully aware of that. But as usual it depends on the use case.
> Compression is optional in UBIFS, paranoid users can disable it
> when encryption is enabled.
It's not the paranoid users I'm concerned about. It's those building
systems with complexity and nuance on top of UBIFS who aren't paranoid
enough.
I suggest disabling compression by default when encryption is enabled,
unless the user explicitly enables both.
>
> Thanks,
> //richard
On Fri, Oct 21, 2016 at 01:47:59PM -0400, Theodore Ts'o wrote:
> On Fri, Oct 21, 2016 at 10:31:54AM -0700, Michael Halcrow wrote:
> > > diff --git a/fs/ubifs/ubifs-media.h b/fs/ubifs/ubifs-media.h
> > > index bdc7935a5e41..e8c23c9d4f4a 100644
> > > --- a/fs/ubifs/ubifs-media.h
> > > +++ b/fs/ubifs/ubifs-media.h
> > > @@ -46,7 +46,7 @@
> > > * UBIFS went into mainline kernel with format version 4. The older formats
> > > * were development formats.
> > > */
> > > -#define UBIFS_FORMAT_VERSION 4
> > > +#define UBIFS_FORMAT_VERSION 5
> >
> > Alex Cope is working on a fix for file name encryption in ext4 so that
> > common plaintext prefixes don't result in common ciphertext prefixes.
> > Older kernels will not be able to read the new file names.
>
> To be clear, this will be done in the context of a new encryption
> mode. In terms of how Ubifs will handle things, that's going to
> depend on whether ubifs uses a single major version number or whether
> they have a feature bitmask like other filesystems, including ext4.
>
I don't think it's reasonable to require require changes to filesystems whenever
someone introduces a new encryption mode --- contents, filenames, or both.
Filesystems need to be able to handle unsupported encryption modes in some way
that makes sense. Currently, when it sees an unsupported encryption mode
fscrypto will behave as if the encryption key is not available and will also
print a one-time warning to the kernel log. This happens when a file is
accessed, not when the filesystem is mounted. As far as I can tell, ext4, f2fs,
and ubifs would all behave this way because this code is shared. I think this
is probably the most realistic behavior.
Eric
On 21.10.2016 19:52, Michael Halcrow wrote:
> On Fri, Oct 21, 2016 at 07:21:04PM +0200, Richard Weinberger wrote:
>> On 21.10.2016 19:14, Michael Halcrow wrote:
>>> Compress-before-encrypt is a hazard.
>>>
>>> http://www.iacr.org/cryptodb/archive/2002/FSE/3091/3091.pdf
>>
>> I'm fully aware of that. But as usual it depends on the use case.
>> Compression is optional in UBIFS, paranoid users can disable it
>> when encryption is enabled.
>
> It's not the paranoid users I'm concerned about. It's those building
> systems with complexity and nuance on top of UBIFS who aren't paranoid
> enough.
>
> I suggest disabling compression by default when encryption is enabled,
> unless the user explicitly enables both.
That's definitely an option, yes.
Thanks,
//richard
On Fri, Oct 21, 2016 at 02:48:30PM +0200, Richard Weinberger wrote:
> +
> + if (ubifs_crypt_is_encrypted(inode)) {
> + int clen = le16_to_cpu(dn->compr_size);
> +
> + if (clen <= 0 || clen > UBIFS_BLOCK_SIZE || clen > dlen)
> + goto dump;
> +
> + ubifs_assert(dlen <= UBIFS_BLOCK_SIZE);
> + err = fscrypt_decrypt_buffer(inode, &dn->data, &dn->data, dlen, block, GFP_NOFS);
> + if (err) {
> + ubifs_err(c, "fscrypt_decrypt_buffer failed: %i", err);
> + return err;
> + }
> +
> + ubifs_assert(clen <= dlen);
> + dlen = clen;
> + }
> +
There are several code blocks like this, calling either fscrypt_decrypt_buffer()
or fscrypt_encrypt_buffer(), which seem to be mostly duplicated. Is it possible
to refactor them into helper functions? There are also some lines well over 80
characters.
Eric
On Fri, Oct 21, 2016 at 02:48:40PM +0200, Richard Weinberger wrote:
> @@ -190,6 +191,10 @@ long ubifs_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> sizeof(policy)))
> return -EFAULT;
>
> + err = ubifs_enable_encryption(c);
> + if (err)
> + return err;
> +
> err = fscrypt_process_policy(file, &policy);
Is ubifs_enable_encryption() being done with proper locking and authorization?
As-is, anyone can call this at any time if they can open some file on UBIFS.
FYI, the approach being taken for ext4 is that the encryption feature flag has
to be explicitly turned on before FS_IOC_SET_ENCRYPTION_POLICY will work.
Eric
On Fri, Oct 21, 2016 at 02:48:35PM +0200, Richard Weinberger wrote:
> +
> + if (!dentry)
> + return ERR_PTR(-ECHILD);
> +
> + if (ubifs_crypt_is_encrypted(inode)) {
> + err = fscrypt_get_encryption_info(inode);
> + if (err)
> + return ERR_PTR(err);
> + } else
> + return ui->data;
> +
This will make path lookups drop out of RCU mode when an unencrypted symlink is
followed. This can be avoided by checking for unencrypted symlinks first:
if (!ubifs_crypt_is_encrypted(inode))
return ui->data;
if (!dentry)
return ERR_PTR(-ECHILD);
err = fscrypt_get_encryption_info(inode);
if (err)
return ERR_PTR(err);
> +
> + pstr.name[err] = '\0';
> +
In 4.9, fscrypt_fname_{disk_to_usr,usr_to_disk} return 0 instead of the output
length, so this will need to be changed to 'pstr.name[pstr.len] = '\0''.
On Fri, Oct 21, 2016 at 11:19:31AM -0700, Eric Biggers wrote:
>
> I don't think it's reasonable to require require changes to filesystems whenever
> someone introduces a new encryption mode --- contents, filenames, or both.
> Filesystems need to be able to handle unsupported encryption modes in some way
> that makes sense. Currently, when it sees an unsupported encryption mode
> fscrypto will behave as if the encryption key is not available and will also
> print a one-time warning to the kernel log. This happens when a file is
> accessed, not when the filesystem is mounted. As far as I can tell, ext4, f2fs,
> and ubifs would all behave this way because this code is shared. I think this
> is probably the most realistic behavior.
I tend to agree, but file systems may choose some alternate approach
if they want to "fail fast" (e.g., at mount time). I wouldn't want to
do that for ext4, but if ubifs (or some other file system) wants do
something more draconian, they can be afraid to do that. Failing
that, some kind of one-time warning makes sense.
What I would like to do though is to is to have a callback so that
code in fs/crypto can call a file system specific notification
routine. e.g., for ext4, we would probably want to be able to call
ext4_warning() and ext4_error() from fs/crypto, and other file systems
might want to have a different set of notification routines.
This way we can print a message like
kernel: EXT4-fs warning (device sdb1): fscrypto_xxx: foo bar baz
and if we later on have a way of sending file system specific warnings
or errors through some kind of IPC mechanism, such as netlink or some
future kdbus scheme, we can send the warning and error messages out
the same way we send other filesystem specific error messages.
- Ted
P.S. BTW, we actually _do_ have something hacked together inside the
Google production kernel which pipes ext4_error() messages to a
netlink socket, so that monitoring systems don't have scrape dmesg or
/var/log/messages.
If anyone inside or outside google is interested in that
functionality, I can make the code available. There's nothing
sensitive or Google specific in it; it's just that unfortunately,
getting that code cleaned up and upstreamed has just never made it
"above the fold" on the priority list, the engineer who originally
implemented it is no longer on the team --- and I never had the time
to cleanup work to get the code to upstream quality myself.
Eric,
On 21.10.2016 20:42, Eric Biggers wrote:
> On Fri, Oct 21, 2016 at 02:48:35PM +0200, Richard Weinberger wrote:
>> +
>> + if (!dentry)
>> + return ERR_PTR(-ECHILD);
>> +
>> + if (ubifs_crypt_is_encrypted(inode)) {
>> + err = fscrypt_get_encryption_info(inode);
>> + if (err)
>> + return ERR_PTR(err);
>> + } else
>> + return ui->data;
>> +
>
> This will make path lookups drop out of RCU mode when an unencrypted symlink is
> followed. This can be avoided by checking for unencrypted symlinks first:
Oh! Thanks for pointing this out!
> if (!ubifs_crypt_is_encrypted(inode))
> return ui->data;
>
> if (!dentry)
> return ERR_PTR(-ECHILD);
>
> err = fscrypt_get_encryption_info(inode);
> if (err)
> return ERR_PTR(err);
>
>> +
>> + pstr.name[err] = '\0';
>> +
>
> In 4.9, fscrypt_fname_{disk_to_usr,usr_to_disk} return 0 instead of the output
> length, so this will need to be changed to 'pstr.name[pstr.len] = '\0''.
Noted.
Thanks,
//richard
Eric,
On 21.10.2016 20:30, Eric Biggers wrote:
> On Fri, Oct 21, 2016 at 02:48:40PM +0200, Richard Weinberger wrote:
>> @@ -190,6 +191,10 @@ long ubifs_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>> sizeof(policy)))
>> return -EFAULT;
>>
>> + err = ubifs_enable_encryption(c);
>> + if (err)
>> + return err;
>> +
>> err = fscrypt_process_policy(file, &policy);
>
> Is ubifs_enable_encryption() being done with proper locking and authorization?
Anyone can encrypted files when UBIFS is build with that feature. Locking is fine
since the operation is atomic. (UBI LEB change).
> As-is, anyone can call this at any time if they can open some file on UBIFS.
That's what I had in mind.
> FYI, the approach being taken for ext4 is that the encryption feature flag has
> to be explicitly turned on before FS_IOC_SET_ENCRYPTION_POLICY will work.
So, on ext4 (f2fs too?) root has to enable to feature first before users
can use it?
IOW for ext4 the encryption flag means "file encryption is allowed", for UBIFS it
means "at least one file got encrypted on this fs".
I think I'll do what ext4 does to have common policies.
BTW: Are these policies documented somewhere?
Thanks,
//richard
Eric,
On 21.10.2016 20:25, Eric Biggers wrote:
> On Fri, Oct 21, 2016 at 02:48:30PM +0200, Richard Weinberger wrote:
>> +
>> + if (ubifs_crypt_is_encrypted(inode)) {
>> + int clen = le16_to_cpu(dn->compr_size);
>> +
>> + if (clen <= 0 || clen > UBIFS_BLOCK_SIZE || clen > dlen)
>> + goto dump;
>> +
>> + ubifs_assert(dlen <= UBIFS_BLOCK_SIZE);
>> + err = fscrypt_decrypt_buffer(inode, &dn->data, &dn->data, dlen, block, GFP_NOFS);
>> + if (err) {
>> + ubifs_err(c, "fscrypt_decrypt_buffer failed: %i", err);
>> + return err;
>> + }
>> +
>> + ubifs_assert(clen <= dlen);
>> + dlen = clen;
>> + }
>> +
>
> There are several code blocks like this, calling either fscrypt_decrypt_buffer()
> or fscrypt_encrypt_buffer(), which seem to be mostly duplicated. Is it possible
> to refactor them into helper functions? There are also some lines well over 80
> characters.
Yes, that's already on my TODO.
Thanks,
//richard
Ted,
On 21.10.2016 19:47, Theodore Ts'o wrote:
> On Fri, Oct 21, 2016 at 10:31:54AM -0700, Michael Halcrow wrote:
>>> diff --git a/fs/ubifs/ubifs-media.h b/fs/ubifs/ubifs-media.h
>>> index bdc7935a5e41..e8c23c9d4f4a 100644
>>> --- a/fs/ubifs/ubifs-media.h
>>> +++ b/fs/ubifs/ubifs-media.h
>>> @@ -46,7 +46,7 @@
>>> * UBIFS went into mainline kernel with format version 4. The older formats
>>> * were development formats.
>>> */
>>> -#define UBIFS_FORMAT_VERSION 4
>>> +#define UBIFS_FORMAT_VERSION 5
>>
>> Alex Cope is working on a fix for file name encryption in ext4 so that
>> common plaintext prefixes don't result in common ciphertext prefixes.
>> Older kernels will not be able to read the new file names.
>
> To be clear, this will be done in the context of a new encryption
> mode. In terms of how Ubifs will handle things, that's going to
> depend on whether ubifs uses a single major version number or whether
> they have a feature bitmask like other filesystems, including ext4.
With write version 5, UBIFS has a real feature bitmask.
UBIFS has a feature bitmask since ever but never enforced it.
i.e. you could set bits which are unknown to UBIFS and it sill mounted.
Now UBIFS will refuse to mount when features are set which are not known/enabled
by this implementation.
Maybe I'll add another bitmap just for crypto do be able to support different cipher
modes.
Thanks,
//richard
Christoph,
On 21.10.2016 15:24, Christoph Hellwig wrote:
> On Fri, Oct 21, 2016 at 03:17:03PM +0200, Richard Weinberger wrote:
>> UBIFS works on kmalloc()'ed buffers where it constructs the NAND/NOR pages
>> which will be written to the MTD. JFFS2 does the same.
>
> Yes, you can trivially do a virt_to_page on a kmalloc buffer.
>
>> Another reason why I did the buffer functions is because fscrypt_encrypt_page()
>> always allocates a bounce page as temporary memory. For ext4 this is needed,
>> for UBIFS not.
>> UBIFS has already a construction buffer, especially since it also does compression.
>
> We should defintively find a way to avoid that, but it's a separate
> issue from adding another API just to pass buffers.
Okay, I'll address this in v2 of my series.
Thanks,
//richard
On Mon, Oct 24, 2016 at 08:59:26AM +0200, Richard Weinberger wrote:
>
> So, on ext4 (f2fs too?) root has to enable to feature first before users
> can use it?
> IOW for ext4 the encryption flag means "file encryption is allowed", for UBIFS it
> means "at least one file got encrypted on this fs".
Historically there have been one or two feature flags which ext4 has
enabled automatically, behind the system administrator's back. We've
since decided that's a bad thing to do, and so it's a file system
level policy not to do this.
An example of why this can cause problems is if a system administrator
boots a rescue CD that happens to use a newer kernel, and that newer
kernel (plus potentially an updated userspace on said rescue CD)
silently the feature flag to enable some feature (say, the first time
we have files larger than 2GB, for example), and then when the system
administrator books back into their RHEL or SLES kernel, they can no
longer mount the file system.
So ext4 has a policy now that we won't do that, since it's unfriendly
to system administrators. That's a general rule, and not one which is
specific to encryption. That way you can create a file system, and
for the most part, modulo some really old, ancient flags which
hopefully all Linux systems understand (I don't *think* there are any
RHEL 2.x systems out there still :-), if a feature flag is not
enabled, the kernel will return an error if you try to use some
feature which is not enabled, instead of silently enabling it behind
the user and/or sysadmin's back.
I believe xfs may have a similar policy, and for similar reasons.
(Actually, I'm not sure if xfs supports enabling features after the
file system has been created at all; certainly Red Hat has a
distro-level policy of not supporting features added post-mkfs,
although I don't know how much that is enforced at the open source
coding level. At one point there were some grumbles that Red Hat
might want to disable some of tune2fs's features of enabling features
post-mkfs time, but I think they've settled for just laughing at users
who show up with bug reports after they converted a file system from
ext3 to ext4. :-)
> I think I'll do what ext4 does to have common policies.
If you want to, feel free; but if there is good reason for Ubifs to
want to be more flexible, I think there is room for differences at
this level.
The specific reason why we do this probably doesn't matter so much on
embedded systems --- although I will note that many/most handset
manufacturers have a policy of not supporting any new features that
require new *kernel* fatures after the device has been released,
because they don't want to deal with potential upgrade problems and
device instabilities issues; users tend to get unhappy when their
phones get bricked after an upgrade.
Cheers,
- Ted