kcore: add _text to KCORE_TEXT
[safe/jmp/linux-2.6] / fs / ecryptfs / inode.c
index a50efb1..65dee2f 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/mount.h>
 #include <linux/crypto.h>
 #include <linux/fs_stack.h>
+#include <linux/slab.h>
 #include <asm/unaligned.h>
 #include "ecryptfs_kernel.h"
 
@@ -141,19 +142,10 @@ out:
 static int grow_file(struct dentry *ecryptfs_dentry)
 {
        struct inode *ecryptfs_inode = ecryptfs_dentry->d_inode;
-       struct file fake_file;
-       struct ecryptfs_file_info tmp_file_info;
        char zero_virt[] = { 0x00 };
        int rc = 0;
 
-       memset(&fake_file, 0, sizeof(fake_file));
-       fake_file.f_path.dentry = ecryptfs_dentry;
-       memset(&tmp_file_info, 0, sizeof(tmp_file_info));
-       ecryptfs_set_file_private(&fake_file, &tmp_file_info);
-       ecryptfs_set_file_lower(
-               &fake_file,
-               ecryptfs_inode_to_private(ecryptfs_inode)->lower_file);
-       rc = ecryptfs_write(&fake_file, zero_virt, 0, 1);
+       rc = ecryptfs_write(ecryptfs_inode, zero_virt, 0, 1);
        i_size_write(ecryptfs_inode, 0);
        rc = ecryptfs_write_inode_size_to_metadata(ecryptfs_inode);
        ecryptfs_inode_to_private(ecryptfs_inode)->crypt_stat.flags |=
@@ -323,6 +315,7 @@ int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry,
        rc = ecryptfs_read_and_validate_header_region(page_virt,
                                                      ecryptfs_dentry->d_inode);
        if (rc) {
+               memset(page_virt, 0, PAGE_CACHE_SIZE);
                rc = ecryptfs_read_and_validate_xattr_region(page_virt,
                                                             ecryptfs_dentry);
                if (rc) {
@@ -387,9 +380,9 @@ static struct dentry *ecryptfs_lookup(struct inode *ecryptfs_dir_inode,
        mutex_unlock(&lower_dir_dentry->d_inode->i_mutex);
        if (IS_ERR(lower_dentry)) {
                rc = PTR_ERR(lower_dentry);
-               printk(KERN_ERR "%s: lookup_one_len() returned [%d] on "
-                      "lower_dentry = [%s]\n", __func__, rc,
-                      ecryptfs_dentry->d_name.name);
+               ecryptfs_printk(KERN_DEBUG, "%s: lookup_one_len() returned "
+                               "[%d] on lower_dentry = [%s]\n", __func__, rc,
+                               encrypted_and_encoded_name);
                goto out_d_drop;
        }
        if (lower_dentry->d_inode)
@@ -416,9 +409,9 @@ static struct dentry *ecryptfs_lookup(struct inode *ecryptfs_dir_inode,
        mutex_unlock(&lower_dir_dentry->d_inode->i_mutex);
        if (IS_ERR(lower_dentry)) {
                rc = PTR_ERR(lower_dentry);
-               printk(KERN_ERR "%s: lookup_one_len() returned [%d] on "
-                      "lower_dentry = [%s]\n", __func__, rc,
-                      encrypted_and_encoded_name);
+               ecryptfs_printk(KERN_DEBUG, "%s: lookup_one_len() returned "
+                               "[%d] on lower_dentry = [%s]\n", __func__, rc,
+                               encrypted_and_encoded_name);
                goto out_d_drop;
        }
 lookup_and_interpose:
@@ -455,8 +448,8 @@ static int ecryptfs_link(struct dentry *old_dentry, struct inode *dir,
        rc = ecryptfs_interpose(lower_new_dentry, new_dentry, dir->i_sb, 0);
        if (rc)
                goto out_lock;
-       fsstack_copy_attr_times(dir, lower_new_dentry->d_inode);
-       fsstack_copy_inode_size(dir, lower_new_dentry->d_inode);
+       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 =
                ecryptfs_inode_to_lower(old_dentry->d_inode)->i_nlink;
        i_size_write(new_dentry->d_inode, file_size_save);
@@ -647,38 +640,17 @@ out_lock:
        return rc;
 }
 
-static int
-ecryptfs_readlink(struct dentry *dentry, char __user *buf, int bufsiz)
+static int ecryptfs_readlink_lower(struct dentry *dentry, char **buf,
+                                  size_t *bufsiz)
 {
+       struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
        char *lower_buf;
-       size_t lower_bufsiz;
-       struct dentry *lower_dentry;
-       struct ecryptfs_mount_crypt_stat *mount_crypt_stat;
-       char *plaintext_name;
-       size_t plaintext_name_size;
+       size_t lower_bufsiz = PATH_MAX;
        mm_segment_t old_fs;
        int rc;
 
-       lower_dentry = ecryptfs_dentry_to_lower(dentry);
-       if (!lower_dentry->d_inode->i_op->readlink) {
-               rc = -EINVAL;
-               goto out;
-       }
-       mount_crypt_stat = &ecryptfs_superblock_to_private(
-                                               dentry->d_sb)->mount_crypt_stat;
-       /*
-        * If the lower filename is encrypted, it will result in a significantly
-        * longer name.  If needed, truncate the name after decode and decrypt.
-        */
-       if (mount_crypt_stat->flags & ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES)
-               lower_bufsiz = PATH_MAX;
-       else
-               lower_bufsiz = bufsiz;
-       /* Released in this function */
        lower_buf = kmalloc(lower_bufsiz, GFP_KERNEL);
-       if (lower_buf == NULL) {
-               printk(KERN_ERR "%s: Out of memory whilst attempting to "
-                      "kmalloc [%zd] bytes\n", __func__, lower_bufsiz);
+       if (!lower_buf) {
                rc = -ENOMEM;
                goto out;
        }
@@ -688,29 +660,31 @@ ecryptfs_readlink(struct dentry *dentry, char __user *buf, int bufsiz)
                                                   (char __user *)lower_buf,
                                                   lower_bufsiz);
        set_fs(old_fs);
-       if (rc >= 0) {
-               rc = ecryptfs_decode_and_decrypt_filename(&plaintext_name,
-                                                         &plaintext_name_size,
-                                                         dentry, lower_buf,
-                                                         rc);
-               if (rc) {
-                       printk(KERN_ERR "%s: Error attempting to decode and "
-                              "decrypt filename; rc = [%d]\n", __func__,
-                               rc);
-                       goto out_free_lower_buf;
-               }
-               /* Check for bufsiz <= 0 done in sys_readlinkat() */
-               rc = copy_to_user(buf, plaintext_name,
-                                 min((size_t) bufsiz, plaintext_name_size));
-               if (rc)
-                       rc = -EFAULT;
-               else
-                       rc = plaintext_name_size;
-               kfree(plaintext_name);
-               fsstack_copy_attr_atime(dentry->d_inode, lower_dentry->d_inode);
-       }
-out_free_lower_buf:
+       if (rc < 0)
+               goto out;
+       lower_bufsiz = rc;
+       rc = ecryptfs_decode_and_decrypt_filename(buf, bufsiz, dentry,
+                                                 lower_buf, lower_bufsiz);
+out:
        kfree(lower_buf);
+       return rc;
+}
+
+static int
+ecryptfs_readlink(struct dentry *dentry, char __user *buf, int bufsiz)
+{
+       char *kbuf;
+       size_t kbufsiz, copied;
+       int rc;
+
+       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);
+       fsstack_copy_attr_atime(dentry->d_inode,
+                               ecryptfs_dentry_to_lower(dentry)->d_inode);
 out:
        return rc;
 }
@@ -801,8 +775,6 @@ static int truncate_upper(struct dentry *dentry, struct iattr *ia,
 {
        int rc = 0;
        struct inode *inode = dentry->d_inode;
-       struct dentry *lower_dentry;
-       struct file fake_ecryptfs_file;
        struct ecryptfs_crypt_stat *crypt_stat;
        loff_t i_size = i_size_read(inode);
        loff_t lower_size_before_truncate;
@@ -813,23 +785,6 @@ static int truncate_upper(struct dentry *dentry, struct iattr *ia,
                goto out;
        }
        crypt_stat = &ecryptfs_inode_to_private(dentry->d_inode)->crypt_stat;
-       /* Set up a fake ecryptfs file, this is used to interface with
-        * the file in the underlying filesystem so that the
-        * truncation has an effect there as well. */
-       memset(&fake_ecryptfs_file, 0, sizeof(fake_ecryptfs_file));
-       fake_ecryptfs_file.f_path.dentry = dentry;
-       /* Released at out_free: label */
-       ecryptfs_set_file_private(&fake_ecryptfs_file,
-                                 kmem_cache_alloc(ecryptfs_file_info_cache,
-                                                  GFP_KERNEL));
-       if (unlikely(!ecryptfs_file_to_private(&fake_ecryptfs_file))) {
-               rc = -ENOMEM;
-               goto out;
-       }
-       lower_dentry = ecryptfs_dentry_to_lower(dentry);
-       ecryptfs_set_file_lower(
-               &fake_ecryptfs_file,
-               ecryptfs_inode_to_private(dentry->d_inode)->lower_file);
        /* Switch on growing or shrinking file */
        if (ia->ia_size > i_size) {
                char zero[] = { 0x00 };
@@ -839,7 +794,7 @@ static int truncate_upper(struct dentry *dentry, struct iattr *ia,
                 * this triggers code that will fill in 0's throughout
                 * the intermediate portion of the previous end of the
                 * file and the new and of the file */
-               rc = ecryptfs_write(&fake_ecryptfs_file, zero,
+               rc = ecryptfs_write(inode, zero,
                                    (ia->ia_size - 1), 1);
        } else { /* ia->ia_size < i_size_read(inode) */
                /* We're chopping off all the pages down to the page
@@ -852,10 +807,10 @@ static int truncate_upper(struct dentry *dentry, struct iattr *ia,
                if (!(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) {
                        rc = vmtruncate(inode, ia->ia_size);
                        if (rc)
-                               goto out_free;
+                               goto out;
                        lower_ia->ia_size = ia->ia_size;
                        lower_ia->ia_valid |= ATTR_SIZE;
-                       goto out_free;
+                       goto out;
                }
                if (num_zeros) {
                        char *zeros_virt;
@@ -863,16 +818,16 @@ static int truncate_upper(struct dentry *dentry, struct iattr *ia,
                        zeros_virt = kzalloc(num_zeros, GFP_KERNEL);
                        if (!zeros_virt) {
                                rc = -ENOMEM;
-                               goto out_free;
+                               goto out;
                        }
-                       rc = ecryptfs_write(&fake_ecryptfs_file, zeros_virt,
+                       rc = ecryptfs_write(inode, zeros_virt,
                                            ia->ia_size, num_zeros);
                        kfree(zeros_virt);
                        if (rc) {
                                printk(KERN_ERR "Error attempting to zero out "
                                       "the remainder of the end page on "
                                       "reducing truncate; rc = [%d]\n", rc);
-                               goto out_free;
+                               goto out;
                        }
                }
                vmtruncate(inode, ia->ia_size);
@@ -881,7 +836,7 @@ static int truncate_upper(struct dentry *dentry, struct iattr *ia,
                        printk(KERN_ERR "Problem with "
                               "ecryptfs_write_inode_size_to_metadata; "
                               "rc = [%d]\n", rc);
-                       goto out_free;
+                       goto out;
                }
                /* We are reducing the size of the ecryptfs file, and need to
                 * know if we need to reduce the size of the lower file. */
@@ -895,10 +850,6 @@ static int truncate_upper(struct dentry *dentry, struct iattr *ia,
                } else
                        lower_ia->ia_valid &= ~ATTR_SIZE;
        }
-out_free:
-       if (ecryptfs_file_to_private(&fake_ecryptfs_file))
-               kmem_cache_free(ecryptfs_file_info_cache,
-                               ecryptfs_file_to_private(&fake_ecryptfs_file));
 out:
        return rc;
 }
@@ -1015,6 +966,28 @@ out:
        return rc;
 }
 
+int ecryptfs_getattr_link(struct vfsmount *mnt, struct dentry *dentry,
+                         struct kstat *stat)
+{
+       struct ecryptfs_mount_crypt_stat *mount_crypt_stat;
+       int rc = 0;
+
+       mount_crypt_stat = &ecryptfs_superblock_to_private(
+                                               dentry->d_sb)->mount_crypt_stat;
+       generic_fillattr(dentry->d_inode, stat);
+       if (mount_crypt_stat->flags & ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES) {
+               char *target;
+               size_t targetsiz;
+
+               rc = ecryptfs_readlink_lower(dentry, &target, &targetsiz);
+               if (!rc) {
+                       kfree(target);
+                       stat->size = targetsiz;
+               }
+       }
+       return rc;
+}
+
 int ecryptfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
                     struct kstat *stat)
 {
@@ -1039,7 +1012,7 @@ ecryptfs_setxattr(struct dentry *dentry, const char *name, const void *value,
 
        lower_dentry = ecryptfs_dentry_to_lower(dentry);
        if (!lower_dentry->d_inode->i_op->setxattr) {
-               rc = -ENOSYS;
+               rc = -EOPNOTSUPP;
                goto out;
        }
        mutex_lock(&lower_dentry->d_inode->i_mutex);
@@ -1057,7 +1030,7 @@ ecryptfs_getxattr_lower(struct dentry *lower_dentry, const char *name,
        int rc = 0;
 
        if (!lower_dentry->d_inode->i_op->getxattr) {
-               rc = -ENOSYS;
+               rc = -EOPNOTSUPP;
                goto out;
        }
        mutex_lock(&lower_dentry->d_inode->i_mutex);
@@ -1084,7 +1057,7 @@ ecryptfs_listxattr(struct dentry *dentry, char *list, size_t size)
 
        lower_dentry = ecryptfs_dentry_to_lower(dentry);
        if (!lower_dentry->d_inode->i_op->listxattr) {
-               rc = -ENOSYS;
+               rc = -EOPNOTSUPP;
                goto out;
        }
        mutex_lock(&lower_dentry->d_inode->i_mutex);
@@ -1101,7 +1074,7 @@ static int ecryptfs_removexattr(struct dentry *dentry, const char *name)
 
        lower_dentry = ecryptfs_dentry_to_lower(dentry);
        if (!lower_dentry->d_inode->i_op->removexattr) {
-               rc = -ENOSYS;
+               rc = -EOPNOTSUPP;
                goto out;
        }
        mutex_lock(&lower_dentry->d_inode->i_mutex);
@@ -1132,6 +1105,7 @@ const struct inode_operations ecryptfs_symlink_iops = {
        .put_link = ecryptfs_put_link,
        .permission = ecryptfs_permission,
        .setattr = ecryptfs_setattr,
+       .getattr = ecryptfs_getattr_link,
        .setxattr = ecryptfs_setxattr,
        .getxattr = ecryptfs_getxattr,
        .listxattr = ecryptfs_listxattr,