ecryptfs-devel team mailing list archive
-
ecryptfs-devel team
-
Mailing list archive
-
Message #00140
[PATCH 3/3] EcryptFS: Add support for file names that are too long after being encrypted
BugLink: https://bugs.launchpad.net/ecryptfs/+bug/344878
Fix Bug#344878 which occurs when a file is created with a file name that
would be valid before encrypting and encoding but after being encrypted and
encoded is too long for the underlying filesytem.
---
fs/ecryptfs/crypto.c | 13 +-
fs/ecryptfs/ecryptfs_kernel.h | 39 ++++
fs/ecryptfs/file.c | 43 ++++
fs/ecryptfs/inode.c | 437 ++++++++++++++++++++++++++++++++++++++++-
fs/ecryptfs/main.c | 64 ++++++
fs/ecryptfs/super.c | 3 +-
6 files changed, 592 insertions(+), 7 deletions(-)
diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c
index bfd8b68..e1a6a66 100644
--- a/fs/ecryptfs/crypto.c
+++ b/fs/ecryptfs/crypto.c
@@ -92,9 +92,8 @@ void ecryptfs_from_hex(char *dst, char *src, int dst_size)
* Uses the allocated crypto context that crypt_stat references to
* generate the MD5 sum of the contents of src.
*/
-static int ecryptfs_calculate_md5(char *dst,
- struct ecryptfs_crypt_stat *crypt_stat,
- char *src, int len)
+int ecryptfs_calculate_md5(char *dst, struct ecryptfs_crypt_stat *crypt_stat,
+ const char *src, int len)
{
struct scatterlist sg;
struct hash_desc desc = {
@@ -914,6 +913,9 @@ static void ecryptfs_copy_mount_wide_flags_to_inode_flags(
else if (mount_crypt_stat->flags
& ECRYPTFS_GLOBAL_ENCFN_USE_FEK)
crypt_stat->flags |= ECRYPTFS_ENCFN_USE_FEK;
+ if (mount_crypt_stat->flags &
+ ECRYPTFS_GLOBAL_FNE_LONGNAME_XATTR)
+ crypt_stat->flags |= ECRYPTFS_FNE_LONGNAME_XATTR;
}
}
@@ -1056,7 +1058,8 @@ static struct ecryptfs_flag_map_elem ecryptfs_flag_map[] = {
{0x00000001, ECRYPTFS_ENABLE_HMAC},
{0x00000002, ECRYPTFS_ENCRYPTED},
{0x00000004, ECRYPTFS_METADATA_IN_XATTR},
- {0x00000008, ECRYPTFS_ENCRYPT_FILENAMES}
+ {0x00000008, ECRYPTFS_ENCRYPT_FILENAMES},
+ {0x00000010, ECRYPTFS_FNE_LONGNAME_XATTR}
};
/**
@@ -1648,7 +1651,7 @@ out:
*
* Returns zero on success; non-zero otherwise
*/
-static int
+int
ecryptfs_encrypt_filename(struct ecryptfs_filename *filename,
struct ecryptfs_crypt_stat *crypt_stat,
struct ecryptfs_mount_crypt_stat *mount_crypt_stat)
diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h
index dbc84ed..f8a7116 100644
--- a/fs/ecryptfs/ecryptfs_kernel.h
+++ b/fs/ecryptfs/ecryptfs_kernel.h
@@ -85,6 +85,11 @@
#define ECRYPTFS_DEFAULT_NUM_USERS 4
#define ECRYPTFS_MAX_NUM_USERS 32768
#define ECRYPTFS_XATTR_NAME "user.ecryptfs"
+#define ECRYPTFS_LONGNAME_XATTR_NAME "trusted.ecryptfs."
+#define ECRYPTFS_LONGNAME_XATTR_NAME_SIZE (sizeof(ECRYPTFS_LONGNAME_XATTR_NAME)\
+ - 1)
+#define ECRYPTFS_SHORTNAME_PREFIX "ECRYPTFS_SHORTNAME."
+#define ECRYPTFS_SHORTNAME_PREFIX_SIZE (sizeof(ECRYPTFS_SHORTNAME_PREFIX) - 1)
#define RFC2440_CIPHER_DES3_EDE 0x02
#define RFC2440_CIPHER_CAST_5 0x03
@@ -230,6 +235,9 @@ ecryptfs_get_key_payload_data(struct key *key)
#define ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX "ECRYPTFS_FNEK_ENCRYPTED."
#define ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX_SIZE 24
#define ECRYPTFS_ENCRYPTED_DENTRY_NAME_LEN (18 + 1 + 4 + 1 + 32)
+#define ECRYPTFS_ENCODED_MD5_SIZE 24
+#define ECRYPTFS_SHORTNAME_SIZE (ECRYPTFS_SHORTNAME_PREFIX_SIZE + \
+ ECRYPTFS_ENCODED_MD5_SIZE)
struct ecryptfs_key_sig {
struct list_head crypt_stat_list;
@@ -270,6 +278,7 @@ struct ecryptfs_crypt_stat {
#define ECRYPTFS_ENCFN_USE_MOUNT_FNEK 0x00001000
#define ECRYPTFS_ENCFN_USE_FEK 0x00002000
#define ECRYPTFS_UNLINK_SIGS 0x00004000
+#define ECRYPTFS_FNE_LONGNAME_XATTR 0x00008000
u32 flags;
unsigned int file_version;
size_t iv_bytes;
@@ -306,6 +315,7 @@ struct ecryptfs_inode_info {
struct ecryptfs_dentry_info {
struct path lower_path;
struct ecryptfs_crypt_stat *crypt_stat;
+ int longname_flag;
};
/**
@@ -377,7 +387,9 @@ struct ecryptfs_mount_crypt_stat {
#define ECRYPTFS_GLOBAL_ENCFN_USE_MOUNT_FNEK 0x00000020
#define ECRYPTFS_GLOBAL_ENCFN_USE_FEK 0x00000040
#define ECRYPTFS_GLOBAL_MOUNT_AUTH_TOK_ONLY 0x00000080
+#define ECRYPTFS_GLOBAL_FNE_LONGNAME_XATTR 0x00000100
u32 flags;
+ long f_namelen;
struct list_head global_auth_tok_list;
struct mutex global_auth_tok_list_mutex;
size_t num_global_auth_toks;
@@ -581,6 +593,18 @@ ecryptfs_set_dentry_lower_mnt(struct dentry *dentry, struct vfsmount *lower_mnt)
lower_mnt;
}
+static inline int
+ecryptfs_dentry_is_longname(struct dentry *ecryptfs_dentry)
+{
+ return ecryptfs_dentry_to_private(ecryptfs_dentry)->longname_flag);
+}
+
+static inline void
+ecryptfs_set_dentry_longname_flag(struct dentry *ecryptfs_dentry, int flag)
+{
+ ecryptfs_dentry_to_private(ecryptfs_dentry)->longname_flag = flag;
+}
+
#define ecryptfs_printk(type, fmt, arg...) \
__ecryptfs_printk(type "%s: " fmt, __func__, ## arg);
__attribute__ ((format(printf, 1, 2)))
@@ -775,5 +799,20 @@ ecryptfs_parse_tag_70_packet(char **filename, size_t *filename_size,
char *data, size_t max_packet_size);
int ecryptfs_derive_iv(char *iv, struct ecryptfs_crypt_stat *crypt_stat,
loff_t offset);
+void ecryptfs_encode_for_filename(unsigned char *dst, size_t *dst_size,
+ unsigned char *src, size_t src_size);
+int
+ecryptfs_encrypt_filename(struct ecryptfs_filename *filename,
+ struct ecryptfs_crypt_stat *crypt_stat,
+ struct ecryptfs_mount_crypt_stat *mount_crypt_stat);
+int ecryptfs_calculate_md5(char *dst, struct ecryptfs_crypt_stat *crypt_stat,
+ const char *src, int len);
+
+int ecryptfs_get_longname(struct ecryptfs_mount_crypt_stat *mount_crypt_stat,
+ struct dentry *lower_dentry, char **long_name,
+ size_t *long_name_size);
+
+int ecryptfs_is_shortname(const char *name, size_t name_size,
+ const char *lower_name);
#endif /* #ifndef ECRYPTFS_KERNEL_H */
diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c
index 81e10e6..d1fdaf3 100644
--- a/fs/ecryptfs/file.c
+++ b/fs/ecryptfs/file.c
@@ -80,6 +80,7 @@ static int
ecryptfs_filldir(void *dirent, const char *lower_name, int lower_namelen,
loff_t offset, u64 ino, unsigned int d_type)
{
+ struct ecryptfs_mount_crypt_stat *mount_crypt_stat;
struct ecryptfs_getdents_callback *buf =
(struct ecryptfs_getdents_callback *)dirent;
size_t name_size;
@@ -96,6 +97,48 @@ ecryptfs_filldir(void *dirent, const char *lower_name, int lower_namelen,
rc);
goto out;
}
+
+ mount_crypt_stat = &ecryptfs_superblock_to_private(
+ buf->dentry->d_sb)->mount_crypt_stat;
+ if ((mount_crypt_stat->flags & ECRYPTFS_GLOBAL_FNE_LONGNAME_XATTR) &&
+ ecryptfs_is_shortname(name, name_size, lower_name)) {
+ /* we have a short name find the matching longname, which
+ * means finding the file it is stored on
+ */
+ struct dentry *lower_dir_dentry =
+ ecryptfs_dentry_to_lower(buf->dentry);
+ struct dentry *lower_dentry =
+ lookup_one_len(lower_name, lower_dir_dentry,
+ lower_namelen);
+ char *longname;
+ size_t longname_size;
+
+ if (!lower_dentry) {
+ ecryptfs_printk(KERN_ERR, "Error attempting to retrieve"
+ " dentry for longname for filename "
+ "[%s]; rc = [%d]\n",
+ lower_name, rc);
+ goto out;
+
+ }
+
+ rc = ecryptfs_get_longname(mount_crypt_stat, lower_dentry,
+ &longname, &longname_size);
+ if (rc) {
+ printk(KERN_ERR, "Error attempting to retrieve longname"
+ " for filename [%s], using [%s]; rc = [%d]\n",
+ lower_name, name, rc);
+ /* TODO: add option to fail instead of falling back
+ * to shortname
+ */
+ rc = 0;
+ } else {
+ kfree(name);
+ name = longname;
+ name_size = longname_size;
+ }
+
+ }
rc = buf->filldir(buf->dirent, name, name_size, offset, ino, d_type);
kfree(name);
if (rc >= 0)
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index fb7cc7d..4329fab 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -33,9 +33,275 @@
#include <linux/fs_stack.h>
#include <linux/slab.h>
#include <linux/xattr.h>
+#include <linux/statfs.h>
#include <asm/unaligned.h>
#include "ecryptfs_kernel.h"
+/**
+ * ecryptfs_gen_xattr_name - build the corresponding xattr name from @shortname
+ * @shortname: the encrypted and encoded shortname to create the xattr name for
+ *
+ * Returns: an xattr name or NULL if out of memory
+ *
+ * caller must free the generated xattr
+ */
+static char *ecryptfs_gen_xattr_name(const char *shortname)
+{
+ char *xattr_name;
+ int size;
+
+ /* Skip storing ecryptfs prefix as part of xattr name */
+ shortname += ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX_SIZE;
+
+ size = ECRYPTFS_LONGNAME_XATTR_NAME_SIZE + strlen(shortname) + 1;
+ xattr_name = kmalloc(size, GFP_KERNEL);
+ if (!xattr_name)
+ return NULL;
+
+ strcpy(xattr_name, ECRYPTFS_LONGNAME_XATTR_NAME);
+ strcat(xattr_name, shortname);
+
+ return xattr_name;
+}
+
+/**
+ * ecryptfs_get_longname - get the longname
+ * @mount_crypt_stat:
+ * @lower_dentry:
+ * @longname: Returns retrieved long name
+ * @longname_size: Returns size of retrieved long name
+ *
+ * Returns: 0 on success else error code
+ */
+int ecryptfs_get_longname(struct ecryptfs_mount_crypt_stat *mount_crypt_stat,
+ struct dentry *lower_dentry, char **longname,
+ size_t *longname_size)
+{
+ struct inode *inode = lower_dentry->d_inode;
+ size_t packet_size;
+ char *xattr_name = NULL, *value = NULL;
+ size_t xattr_size;
+ int rc = 0;
+
+ xattr_name = ecryptfs_gen_xattr_name(lower_dentry->d_name.name);
+ if (!xattr_name)
+ return -ENOMEM;
+
+ /* get the size of the xattr */
+ rc = inode->i_op->getxattr(lower_dentry, xattr_name, NULL, 0);
+ if (rc <= 0)
+ goto out;
+
+ xattr_size = rc;
+ value = kmalloc(xattr_size, GFP_KERNEL);
+ if (!value) {
+ ecryptfs_printk(KERN_ERR, "Out of memory whilst attempting to "
+ "kmalloc [%zd] bytes\n", xattr_size);
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ rc = inode->i_op->getxattr(lower_dentry, xattr_name, value, xattr_size);
+ if (rc <= 0) {
+ ecryptfs_printk(KERN_ERR, "Error getting longname xattr [%s]; "
+ "rc = [%d]\n", xattr_name, rc);
+ goto out;
+ }
+
+ rc = ecryptfs_parse_tag_70_packet(longname, longname_size,
+ &packet_size, mount_crypt_stat,
+ value, xattr_size);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Could not parse tag 70 packet from "
+ "filename\n");
+ goto out;
+ }
+
+ if (!longname)
+ goto out;
+
+ BUG_ON((*longname)[(*longname_size) - 1] != 0);
+
+out:
+ kfree(xattr_name);
+ kfree(value);
+ return rc;
+}
+
+/**
+ * ecryptfs_set_longname - set the long name xattr for @ecryptfs_dentry
+ * @ecryptfs_dentry: upper dentry to set a long name for
+ * @name: name to set
+ * @value: value for @name
+ * @value_len: length of value
+ *
+ * Returns: 0 on success else error code
+ *
+ * Set the upper (ecryptfs) dentry name on the corresponding inode using
+ * the encrypted and encoded shortname from the lower dentry as the
+ * key.
+ */
+
+static int ecryptfs_set_longname(struct dentry *ecryptfs_dentry,
+ const char *name, const char *value,
+ size_t value_len)
+{
+ struct ecryptfs_mount_crypt_stat *mount_crypt_stat =
+ &ecryptfs_superblock_to_private(
+ ecryptfs_dentry->d_sb)->mount_crypt_stat;
+ struct dentry *lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry);
+ struct inode *inode = lower_dentry->d_inode;
+ struct ecryptfs_filename filename;
+
+ int rc = 0;
+
+ filename.filename = kmalloc(value_len + 1, GFP_KERNEL);
+ if (!filename.filename) {
+ ecryptfs_printk(KERN_ERR, "Error attempting to allocate "
+ "filename buffer; rc = [%d]\n", -ENOMEM);
+ return -ENOMEM;
+ }
+ strncpy(filename.filename, value, value_len);
+ filename.filename[value_len] = 0;
+ /* include terminating null */
+ filename.filename_size = value_len + 1;
+
+ rc = ecryptfs_encrypt_filename(&filename, NULL, mount_crypt_stat);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error attempting to encrypt "
+ "filename; rc = [%d]\n", rc);
+ goto out;
+ }
+
+ rc = inode->i_op->setxattr(lower_dentry, name,
+ filename.encrypted_filename,
+ filename.encrypted_filename_size, 0);
+
+ if (rc < 0) {
+ ecryptfs_printk(KERN_ERR, "Error setting longname xattr [%s] "
+ "to [%s == %s]; rc = [%d]\n", name,
+ filename.encrypted_filename, value, rc);
+ goto out;
+ }
+
+out:
+ kfree(filename.encrypted_filename);
+ return rc;
+}
+
+static int ecryptfs_dentry_set_longname(struct dentry *ecryptfs_dentry)
+{
+ int rc = -ENOMEM;
+
+ struct dentry *lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry);
+ char *xattr_name = NULL;
+
+ xattr_name = ecryptfs_gen_xattr_name(lower_dentry->d_name.name);
+ if (!xattr_name)
+ goto out;
+
+ rc = ecryptfs_set_longname(ecryptfs_dentry, xattr_name,
+ ecryptfs_dentry->d_name.name,
+ ecryptfs_dentry->d_name.len);
+out:
+ kfree(xattr_name);
+ return rc;
+}
+
+/**
+ * ecryptfs_remove_longname - remove the matching long name xattr from an inode
+ * @lower_dentry: dentry with short name to remove matching long name xattr for
+ * @xattr_name: name of the xattr to remove from @lower_dentry
+ *
+ * Returns: 0 on success else error code
+ */
+static int ecryptfs_remove_longname(struct dentry *lower_dentry,
+ const char *xattr_name)
+{
+ struct inode *inode = lower_dentry->d_inode;
+
+ return inode->i_op->removexattr(lower_dentry, xattr_name);
+}
+
+/**
+ * ecryptfs_is_shortname - test if an unencrypted name is shortened name
+ * @name: unencrypted and encode name
+ * @name_size: size of @name
+ * @lower_name: lower dentry name (unencrypted and encoded)
+ *
+ * Returns: 1 if a shortname else 0
+ */
+int ecryptfs_is_shortname(const char *name, size_t name_size,
+ const char *lower_name)
+{
+ return (name_size == ECRYPTFS_SHORTNAME_SIZE &&
+ strncmp(name, ECRYPTFS_SHORTNAME_PREFIX,
+ ECRYPTFS_SHORTNAME_PREFIX_SIZE) == 0 &&
+ strncmp(lower_name, ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX,
+ ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX_SIZE) == 0);
+}
+
+/**
+ * ecryptfs_gen_shortname - generate a shortname hash from name
+ * @ecryptfs_dentry: dentry with original name that needs a short name
+ * @short_name: buffer containing the encrypted & encoded short name
+ * @short_size: size of the short name
+ *
+ * Returns: 0 if successful else error code
+ *
+ * Generate a unique short name for a filename that was found to be too long.
+ *
+ * NOTES:
+ * currently the short name consists of the ECRYPTFS_FNEK_ENCRYPTED
+ * prefix followed by an encoded md5 hash of the file name that is too long
+ * eg. ECRYPTFS_FNEK_ENCRYPTED.R3-j1H3386OUcEiLNK7nlk--
+ *
+ * This is then encrypted and encoded to get the final name.
+ */
+static int ecryptfs_gen_shortname(struct dentry *ecryptfs_dentry,
+ char **short_name, size_t *short_size)
+{
+ struct ecryptfs_crypt_stat * crypt_stat =
+ &ecryptfs_inode_to_private(
+ ecryptfs_dentry->d_parent->d_inode)->crypt_stat;
+ struct ecryptfs_mount_crypt_stat *mount_crypt_stat =
+ &ecryptfs_superblock_to_private(
+ ecryptfs_dentry->d_sb)->mount_crypt_stat;
+ char md5[16];
+ char unencoded_name[ECRYPTFS_SHORTNAME_SIZE + 1];
+ size_t unencoded_size;
+ int rc;
+
+ rc = ecryptfs_calculate_md5(md5, crypt_stat,
+ ecryptfs_dentry->d_name.name,
+ ecryptfs_dentry->d_name.len);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error md5 hashing name; rc = [%d]\n",
+ rc);
+ goto out;
+ }
+ ecryptfs_encode_for_filename(unencoded_name +
+ ECRYPTFS_SHORTNAME_PREFIX_SIZE,
+ &unencoded_size, md5, 16);
+ strncpy(unencoded_name, ECRYPTFS_SHORTNAME_PREFIX,
+ ECRYPTFS_SHORTNAME_PREFIX_SIZE);
+ unencoded_name[ECRYPTFS_SHORTNAME_SIZE] = 0;
+
+ rc = ecryptfs_encrypt_and_encode_filename(short_name, short_size,
+ NULL,
+ mount_crypt_stat,
+ unencoded_name,
+ ECRYPTFS_SHORTNAME_SIZE);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error attempting to encrypt and "
+ "encode filename; rc = [%d]\n", rc);
+ goto out;
+ }
+out:
+ return rc;
+}
+
+
static struct dentry *lock_parent(struct dentry *dentry)
{
struct dentry *dir;
@@ -124,6 +390,18 @@ ecryptfs_do_create(struct inode *directory_inode,
"rc = [%d]\n", __func__, rc);
goto out_lock;
}
+
+ if (ecryptfs_dentry_is_longname(ecryptfs_dentry)) {
+ /* This dentry was marked as having a longname during lookup */
+ rc = ecryptfs_dentry_set_longname(ecryptfs_dentry);
+ if (rc) {
+ vfs_unlink(lower_dir_dentry->d_inode, lower_dentry);
+ ecryptfs_printk(KERN_ERR, "Failure to set longname; "
+ "rc = [%d]\n", rc);
+ goto out_lock;
+ }
+ }
+
rc = ecryptfs_interpose(lower_dentry, ecryptfs_dentry,
directory_inode->i_sb, 0);
if (rc) {
@@ -437,6 +715,7 @@ static struct dentry *ecryptfs_lookup(struct inode *ecryptfs_dir_inode,
struct ecryptfs_mount_crypt_stat *mount_crypt_stat = NULL;
struct dentry *lower_dir_dentry, *lower_dentry;
struct qstr lower_name;
+ int set_longname_mark = 0;
int rc = 0;
if ((ecryptfs_dentry->d_name.len == 1
@@ -481,6 +760,35 @@ static struct dentry *ecryptfs_lookup(struct inode *ecryptfs_dir_inode,
"filename; rc = [%d]\n", __func__, rc);
goto out_d_drop;
}
+ /* check for lower filename limit and if the name before encryption
+ * and encoding is within that limit, truncate and "hash" the true
+ * name will be stored as encrypted metadata.
+ *
+ * TODO:
+ * handle collision of long name and other name that is same
+ * after encryption, encode and if necessary truncation
+ *
+ * Should handle current long encrypted names that aren't broken
+ * ie. match limit, so test that path first
+ */
+ if ((mount_crypt_stat->flags & ECRYPTFS_GLOBAL_FNE_LONGNAME_XATTR) &&
+ encrypted_and_encoded_name_size > mount_crypt_stat->f_namelen) {
+ rc = -ENAMETOOLONG;
+ if (ecryptfs_dentry->d_name.len > mount_crypt_stat->f_namelen)
+ goto out_d_drop;
+
+ kfree(encrypted_and_encoded_name);
+ rc = ecryptfs_gen_shortname(ecryptfs_dentry,
+ &encrypted_and_encoded_name,
+ &encrypted_and_encoded_name_size);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Error creating valid short "
+ "place holder name for long file name; "
+ "rc = [%d]\n", rc);
+ goto out_d_drop;
+ }
+ set_longname_mark = 1;
+ }
lower_name.name = encrypted_and_encoded_name;
lower_name.len = encrypted_and_encoded_name_size;
lower_name.hash = full_name_hash(lower_name.name, lower_name.len);
@@ -503,6 +811,9 @@ lookup_and_interpose:
rc = ecryptfs_lookup_and_interpose_lower(ecryptfs_dentry, lower_dentry,
ecryptfs_dir_inode,
ecryptfs_nd);
+ if (set_longname_mark)
+ ecryptfs_set_dentry_longname_flag(ecryptfs_dentry, 1);
+
goto out;
out_d_drop:
d_drop(ecryptfs_dentry);
@@ -519,20 +830,39 @@ static int ecryptfs_link(struct dentry *old_dentry, struct inode *dir,
struct dentry *lower_dir_dentry;
u64 file_size_save;
int rc;
+ const char *xattr_name = NULL;
file_size_save = i_size_read(old_dentry->d_inode);
lower_old_dentry = ecryptfs_dentry_to_lower(old_dentry);
lower_new_dentry = ecryptfs_dentry_to_lower(new_dentry);
dget(lower_old_dentry);
dget(lower_new_dentry);
+ if (ecryptfs_dentry_is_longname(new_dentry)) {
+ xattr_name = ecryptfs_gen_xattr_name(lower_new_dentry->d_name.name);
+ if (!xattr_name) {
+ ecryptfs_printk(KERN_ERR, "could not create xattr name "
+ "for %s\n", new_dentry->d_name.name);
+ rc = -ENOMEM;
+ goto out;
+ }
+ }
lower_dir_dentry = lock_parent(lower_new_dentry);
rc = vfs_link(lower_old_dentry, lower_dir_dentry->d_inode,
lower_new_dentry);
if (rc || !lower_new_dentry->d_inode)
goto out_lock;
+ if (xattr_name) {
+ rc = ecryptfs_dentry_set_longname(new_dentry);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Failure to set longname; "
+ "rc = [%d]\n", rc);
+ goto out_lock;
+ }
+ }
rc = ecryptfs_interpose(lower_new_dentry, new_dentry, dir->i_sb, 0);
if (rc)
goto out_lock;
+
fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode);
fsstack_copy_inode_size(dir, lower_dir_dentry->d_inode);
old_dentry->d_inode->i_nlink =
@@ -540,6 +870,7 @@ static int ecryptfs_link(struct dentry *old_dentry, struct inode *dir,
i_size_write(new_dentry->d_inode, file_size_save);
out_lock:
unlock_dir(lower_dir_dentry);
+out:
dput(lower_new_dentry);
dput(lower_old_dentry);
return rc;
@@ -551,14 +882,33 @@ static int ecryptfs_unlink(struct inode *dir, struct dentry *dentry)
struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
struct inode *lower_dir_inode = ecryptfs_inode_to_lower(dir);
struct dentry *lower_dir_dentry;
+ const char *xattr_name = NULL;
dget(lower_dentry);
+ if (ecryptfs_dentry_is_longname(dentry)) {
+ xattr_name = ecryptfs_gen_xattr_name(lower_dentry->d_name.name);
+ if (!xattr_name) {
+ ecryptfs_printk(KERN_ERR, "could not create xattr name "
+ "for %s\n", dentry->d_name.name);
+ rc = -ENOMEM;
+ goto out;
+ }
+ }
lower_dir_dentry = lock_parent(lower_dentry);
rc = vfs_unlink(lower_dir_inode, lower_dentry);
if (rc) {
printk(KERN_ERR "Error in vfs_unlink; rc = [%d]\n", rc);
goto out_unlock;
}
+ if (xattr_name) {
+ rc = ecryptfs_remove_longname(lower_dentry, xattr_name);
+
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "could not remove longname "
+ "%s\n", xattr_name);
+ rc = 0;
+ }
+ }
fsstack_copy_attr_times(dir, lower_dir_inode);
dentry->d_inode->i_nlink =
ecryptfs_inode_to_lower(dentry->d_inode)->i_nlink;
@@ -566,7 +916,9 @@ static int ecryptfs_unlink(struct inode *dir, struct dentry *dentry)
d_drop(dentry);
out_unlock:
unlock_dir(lower_dir_dentry);
+out:
dput(lower_dentry);
+ kfree(xattr_name);
return rc;
}
@@ -597,6 +949,17 @@ static int ecryptfs_symlink(struct inode *dir, struct dentry *dentry,
kfree(encoded_symname);
if (rc || !lower_dentry->d_inode)
goto out_lock;
+
+ if (ecryptfs_dentry_is_longname(dentry)) {
+ /* This dentry was marked as having a longname during lookup */
+ rc = ecryptfs_dentry_set_longname(dentry);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Failure to set longname; "
+ "rc = [%d]\n", rc);
+ goto out_lock;
+ }
+ }
+
rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0);
if (rc)
goto out_lock;
@@ -621,6 +984,17 @@ static int ecryptfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
rc = vfs_mkdir(lower_dir_dentry->d_inode, lower_dentry, mode);
if (rc || !lower_dentry->d_inode)
goto out;
+
+ if (ecryptfs_dentry_is_longname(dentry)) {
+ /* This dentry was marked as having a longname during lookup */
+ rc = ecryptfs_dentry_set_longname(dentry);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Failure to set longname; "
+ "rc = [%d]\n", rc);
+ goto out;
+ }
+ }
+
rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0);
if (rc)
goto out;
@@ -669,6 +1043,17 @@ ecryptfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
rc = vfs_mknod(lower_dir_dentry->d_inode, lower_dentry, mode, dev);
if (rc || !lower_dentry->d_inode)
goto out;
+
+ if (ecryptfs_dentry_is_longname(dentry)) {
+ /* This dentry was marked as having a longname during lookup */
+ rc = ecryptfs_dentry_set_longname(dentry);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Failure to set longname; "
+ "rc = [%d]\n", rc);
+ goto out;
+ }
+ }
+
rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0);
if (rc)
goto out;
@@ -691,6 +1076,7 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct dentry *lower_old_dir_dentry;
struct dentry *lower_new_dir_dentry;
struct dentry *trap = NULL;
+ const char *old_name = NULL, *new_name = NULL;
lower_old_dentry = ecryptfs_dentry_to_lower(old_dentry);
lower_new_dentry = ecryptfs_dentry_to_lower(new_dentry);
@@ -709,10 +1095,53 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,
rc = -ENOTEMPTY;
goto out_lock;
}
+
+ /* is there a long name xattr to remove for old_dentry */
+ if (ecryptfs_dentry_is_longname(old_dentry)) {
+ old_name = ecryptfs_gen_xattr_name(
+ lower_old_dentry->d_name.name);
+ if (!old_name) {
+ rc = -ENOMEM;
+ goto out_lock;
+ }
+ }
+ if (ecryptfs_dentry_is_longname(new_dentry)) {
+ /* make sure a new longname can be set before actual rename */
+ new_name = ecryptfs_gen_xattr_name(
+ lower_new_dentry->d_name.name);
+ if (!new_name) {
+ rc = -ENOMEM;
+ goto out_lock;
+ }
+
+ rc = ecryptfs_set_longname(old_dentry, new_name,
+ new_dentry->d_name.name,
+ new_dentry->d_name.len);
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Failure to set longname; "
+ "rc = [%d]\n", rc);
+ goto out_lock;
+ }
+ }
+
rc = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry,
lower_new_dir_dentry->d_inode, lower_new_dentry);
- if (rc)
+ if (rc) {
+ if (new_name)
+ ecryptfs_remove_longname(lower_old_dentry, new_name);
goto out_lock;
+ }
+
+ if (old_name) {
+ rc = ecryptfs_remove_longname(lower_old_dentry, old_name);
+
+ if (rc) {
+ ecryptfs_printk(KERN_ERR, "Failure to remove old "
+ "longname %s\n", old_name);
+ rc = 0;
+ }
+ }
+
fsstack_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode);
if (new_dir != old_dir)
fsstack_copy_attr_all(old_dir, lower_old_dir_dentry->d_inode);
@@ -722,6 +1151,8 @@ out_lock:
dput(lower_old_dentry->d_parent);
dput(lower_new_dentry);
dput(lower_old_dentry);
+ kfree(old_name);
+ kfree(new_name);
return rc;
}
@@ -765,6 +1196,7 @@ ecryptfs_readlink(struct dentry *dentry, char __user *buf, int bufsiz)
rc = ecryptfs_readlink_lower(dentry, &kbuf, &kbufsiz);
if (rc)
goto out;
+
copied = min_t(size_t, bufsiz, kbufsiz);
rc = copy_to_user(buf, kbuf, copied) ? -EFAULT : copied;
kfree(kbuf);
@@ -1140,6 +1572,7 @@ static ssize_t
ecryptfs_getxattr(struct dentry *dentry, const char *name, void *value,
size_t size)
{
+ /* TODO screen off requests for trusted.ecryptfs ???? */
return ecryptfs_getxattr_lower(ecryptfs_dentry_to_lower(dentry), name,
value, size);
}
@@ -1156,6 +1589,7 @@ ecryptfs_listxattr(struct dentry *dentry, char *list, size_t size)
goto out;
}
mutex_lock(&lower_dentry->d_inode->i_mutex);
+ /* TODO screen off requests for trusted.ecryptfs ??? */
rc = lower_dentry->d_inode->i_op->listxattr(lower_dentry, list, size);
mutex_unlock(&lower_dentry->d_inode->i_mutex);
out:
@@ -1173,6 +1607,7 @@ static int ecryptfs_removexattr(struct dentry *dentry, const char *name)
goto out;
}
mutex_lock(&lower_dentry->d_inode->i_mutex);
+ /* TODO screen off requests for trusted.ecryptfs ??? */
rc = lower_dentry->d_inode->i_op->removexattr(lower_dentry, name);
mutex_unlock(&lower_dentry->d_inode->i_mutex);
out:
diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c
index 758323a..e1abbe8 100644
--- a/fs/ecryptfs/main.c
+++ b/fs/ecryptfs/main.c
@@ -37,6 +37,7 @@
#include <linux/fs_stack.h>
#include <linux/slab.h>
#include <linux/magic.h>
+#include <linux/statfs.h>
#include "ecryptfs_kernel.h"
/**
@@ -218,6 +219,7 @@ enum { ecryptfs_opt_sig, ecryptfs_opt_ecryptfs_sig,
ecryptfs_opt_encrypted_view, ecryptfs_opt_fnek_sig,
ecryptfs_opt_fn_cipher, ecryptfs_opt_fn_cipher_key_bytes,
ecryptfs_opt_unlink_sigs, ecryptfs_opt_mount_auth_tok_only,
+ ecryptfs_opt_fne_no_longname_xattr,
ecryptfs_opt_err };
static const match_table_t tokens = {
@@ -234,6 +236,7 @@ static const match_table_t tokens = {
{ecryptfs_opt_fn_cipher_key_bytes, "ecryptfs_fn_key_bytes=%u"},
{ecryptfs_opt_unlink_sigs, "ecryptfs_unlink_sigs"},
{ecryptfs_opt_mount_auth_tok_only, "ecryptfs_mount_auth_tok_only"},
+ {ecryptfs_opt_fne_no_longname_xattr, "ecryptfs_no_longname_xattr"},
{ecryptfs_opt_err, NULL}
};
@@ -271,6 +274,7 @@ static void ecryptfs_init_mount_crypt_stat(
INIT_LIST_HEAD(&mount_crypt_stat->global_auth_tok_list);
mutex_init(&mount_crypt_stat->global_auth_tok_list_mutex);
mount_crypt_stat->flags |= ECRYPTFS_MOUNT_CRYPT_STAT_INITIALIZED;
+ mount_crypt_stat->flags |= ECRYPTFS_GLOBAL_FNE_LONGNAME_XATTR;
}
/**
@@ -395,6 +399,9 @@ static int ecryptfs_parse_options(struct ecryptfs_sb_info *sbi, char *options)
(ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES
| ECRYPTFS_GLOBAL_ENCFN_USE_MOUNT_FNEK);
break;
+ case ecryptfs_opt_fne_no_longname_xattr:
+ mount_crypt_stat->flags &= ~ECRYPTFS_GLOBAL_FNE_LONGNAME_XATTR;
+ break;
case ecryptfs_opt_fn_cipher:
fn_cipher_name_src = args[0].from;
fn_cipher_name_dst =
@@ -500,6 +507,59 @@ out:
struct kmem_cache *ecryptfs_sb_info_cache;
static struct file_system_type ecryptfs_fs_type;
+static int ecryptfs_mount_supports_longname(struct super_block *sb,
+ struct dentry *lower_dentry)
+{
+ struct ecryptfs_mount_crypt_stat *mount_crypt_stat;
+ struct kstatfs buf;
+ int rc = 0;
+
+ mount_crypt_stat = &ecryptfs_superblock_to_private(
+ sb)->mount_crypt_stat;
+ if (mount_crypt_stat->flags & ECRYPTFS_GLOBAL_FNE_LONGNAME_XATTR) {
+ rc = statfs_by_dentry(lower_dentry, &buf);
+ if (rc) {
+ printk(KERN_WARNING "lower filename size limit "
+ "unknown, disabling encrypted "
+ "longname in xattr\n");
+ mount_crypt_stat->flags &=
+ ~ECRYPTFS_GLOBAL_FNE_LONGNAME_XATTR;
+ goto out;
+ }
+ mount_crypt_stat->f_namelen = buf.f_namelen;
+ if (!lower_dentry->d_inode->i_op->getxattr) {
+ printk(KERN_WARNING "lower filesystem does"
+ "not support getxattr, disabling "
+ "encrypted longname in xattr\n");
+ rc = -EOPNOTSUPP;
+ mount_crypt_stat->flags &=
+ ~ECRYPTFS_GLOBAL_FNE_LONGNAME_XATTR;
+ goto out;
+ }
+ if (!lower_dentry->d_inode->i_op->setxattr) {
+ printk(KERN_WARNING "lower filesystem does "
+ "not support setxattr, disabling "
+ "encrypted longname in xattr\n");
+ rc = -EOPNOTSUPP;
+ mount_crypt_stat->flags &=
+ ~ECRYPTFS_GLOBAL_FNE_LONGNAME_XATTR;
+ goto out;
+ }
+ if (!lower_dentry->d_inode->i_op->removexattr) {
+ printk(KERN_WARNING "lower filesystem does "
+ "not support setxattr, disabling "
+ "encrypted longname in xattr\n");
+ rc = -EOPNOTSUPP;
+ mount_crypt_stat->flags &=
+ ~ECRYPTFS_GLOBAL_FNE_LONGNAME_XATTR;
+ goto out;
+ }
+ }
+
+out:
+ return rc;
+}
+
/**
* ecryptfs_get_sb
* @fs_type
@@ -589,6 +649,10 @@ static struct dentry *ecryptfs_mount(struct file_system_type *fs_type, int flags
ecryptfs_set_dentry_lower(s->s_root, path.dentry);
ecryptfs_set_dentry_lower_mnt(s->s_root, path.mnt);
+ rc = ecryptfs_mount_supports_longname(s, path.dentry);
+ if (rc)
+ goto out_free;
+
s->s_flags |= MS_ACTIVE;
return dget(s->s_root);
diff --git a/fs/ecryptfs/super.c b/fs/ecryptfs/super.c
index 3042fe1..fefb71e 100644
--- a/fs/ecryptfs/super.c
+++ b/fs/ecryptfs/super.c
@@ -191,7 +191,8 @@ static int ecryptfs_show_options(struct seq_file *m, struct vfsmount *mnt)
seq_printf(m, ",ecryptfs_unlink_sigs");
if (mount_crypt_stat->flags & ECRYPTFS_GLOBAL_MOUNT_AUTH_TOK_ONLY)
seq_printf(m, ",ecryptfs_mount_auth_tok_only");
-
+ if (mount_crypt_stat->flags & ECRYPTFS_GLOBAL_FNE_LONGNAME_XATTR)
+ seq_printf(m, ",ecryptfs_longname_xattr");
return 0;
}
--
1.7.1
References