← Back to team overview

ecryptfs-devel team mailing list archive

[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