X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=fs%2Fecryptfs%2Finode.c;h=89209f00f9c74ecc752ed944f26ecff6e2f5c974;hb=624ae5284516870657505103ada531c64dba2a9a;hp=2f2c6cf972f77152bf6ab78dd43e74b2c05fbcac;hpb=7ff1d74f5670329ac4b5959a675f8698ba95be20;p=safe%2Fjmp%2Flinux-2.6 diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 2f2c6cf..89209f0 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -3,7 +3,7 @@ * * Copyright (C) 1997-2004 Erez Zadok * Copyright (C) 2001-2004 Stony Brook University - * Copyright (C) 2004-2006 International Business Machines Corp. + * Copyright (C) 2004-2007 International Business Machines Corp. * Author(s): Michael A. Halcrow * Michael C. Thompsion * @@ -30,71 +30,25 @@ #include #include #include +#include +#include #include "ecryptfs_kernel.h" static struct dentry *lock_parent(struct dentry *dentry) { struct dentry *dir; - dir = dget(dentry->d_parent); - mutex_lock(&(dir->d_inode->i_mutex)); + dir = dget_parent(dentry); + mutex_lock_nested(&(dir->d_inode->i_mutex), I_MUTEX_PARENT); return dir; } -static void unlock_parent(struct dentry *dentry) -{ - mutex_unlock(&(dentry->d_parent->d_inode->i_mutex)); - dput(dentry->d_parent); -} - static void unlock_dir(struct dentry *dir) { mutex_unlock(&dir->d_inode->i_mutex); dput(dir); } -void ecryptfs_copy_inode_size(struct inode *dst, const struct inode *src) -{ - i_size_write(dst, i_size_read((struct inode *)src)); - dst->i_blocks = src->i_blocks; -} - -void ecryptfs_copy_attr_atime(struct inode *dest, const struct inode *src) -{ - dest->i_atime = src->i_atime; -} - -static void ecryptfs_copy_attr_times(struct inode *dest, - const struct inode *src) -{ - dest->i_atime = src->i_atime; - dest->i_mtime = src->i_mtime; - dest->i_ctime = src->i_ctime; -} - -static void ecryptfs_copy_attr_timesizes(struct inode *dest, - const struct inode *src) -{ - dest->i_atime = src->i_atime; - dest->i_mtime = src->i_mtime; - dest->i_ctime = src->i_ctime; - ecryptfs_copy_inode_size(dest, src); -} - -void ecryptfs_copy_attr_all(struct inode *dest, const struct inode *src) -{ - dest->i_mode = src->i_mode; - dest->i_nlink = src->i_nlink; - dest->i_uid = src->i_uid; - dest->i_gid = src->i_gid; - dest->i_rdev = src->i_rdev; - dest->i_atime = src->i_atime; - dest->i_mtime = src->i_mtime; - dest->i_ctime = src->i_ctime; - dest->i_blkbits = src->i_blkbits; - dest->i_flags = src->i_flags; -} - /** * ecryptfs_create_underlying_file * @lower_dir_inode: inode of the parent in the lower fs of the new file @@ -118,13 +72,13 @@ ecryptfs_create_underlying_file(struct inode *lower_dir_inode, struct vfsmount *vfsmount_save; int rc; - dentry_save = nd->dentry; - vfsmount_save = nd->mnt; - nd->dentry = lower_dentry; - nd->mnt = lower_mnt; + dentry_save = nd->path.dentry; + vfsmount_save = nd->path.mnt; + nd->path.dentry = lower_dentry; + nd->path.mnt = lower_mnt; rc = vfs_create(lower_dir_inode, lower_dentry, mode, nd); - nd->dentry = dentry_save; - nd->mnt = vfsmount_save; + nd->path.dentry = dentry_save; + nd->path.mnt = vfsmount_save; return rc; } @@ -152,7 +106,7 @@ ecryptfs_do_create(struct inode *directory_inode, lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry); lower_dir_dentry = lock_parent(lower_dentry); - if (unlikely(IS_ERR(lower_dir_dentry))) { + if (IS_ERR(lower_dir_dentry)) { ecryptfs_printk(KERN_ERR, "Error locking directory of " "dentry\n"); rc = PTR_ERR(lower_dir_dentry); @@ -160,9 +114,9 @@ ecryptfs_do_create(struct inode *directory_inode, } rc = ecryptfs_create_underlying_file(lower_dir_dentry->d_inode, ecryptfs_dentry, mode, nd); - if (unlikely(rc)) { - ecryptfs_printk(KERN_ERR, - "Failure to create underlying file\n"); + if (rc) { + printk(KERN_ERR "%s: Failure to create dentry in lower fs; " + "rc = [%d]\n", __func__, rc); goto out_lock; } rc = ecryptfs_interpose(lower_dentry, ecryptfs_dentry, @@ -171,8 +125,8 @@ ecryptfs_do_create(struct inode *directory_inode, ecryptfs_printk(KERN_ERR, "Failure in ecryptfs_interpose\n"); goto out_lock; } - ecryptfs_copy_attr_timesizes(directory_inode, - lower_dir_dentry->d_inode); + fsstack_copy_attr_times(directory_inode, lower_dir_dentry->d_inode); + fsstack_copy_inode_size(directory_inode, lower_dir_dentry->d_inode); out_lock: unlock_dir(lower_dir_dentry); out: @@ -181,39 +135,30 @@ out: /** * grow_file - * @ecryptfs_dentry: the ecryptfs dentry - * @lower_file: The lower file - * @inode: The ecryptfs inode - * @lower_inode: The lower inode + * @ecryptfs_dentry: the eCryptfs dentry * * This is the code which will grow the file to its correct size. */ -static int grow_file(struct dentry *ecryptfs_dentry, struct file *lower_file, - struct inode *inode, struct inode *lower_inode) +static int grow_file(struct dentry *ecryptfs_dentry) { - int rc = 0; + 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_dentry = ecryptfs_dentry; + 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, lower_file); - rc = ecryptfs_fill_zeros(&fake_file, 1); - if (rc) { - ECRYPTFS_SET_FLAG( - ecryptfs_inode_to_private(inode)->crypt_stat.flags, - ECRYPTFS_SECURITY_WARNING); - ecryptfs_printk(KERN_WARNING, "Error attempting to fill zeros " - "in file; rc = [%d]\n", rc); - goto out; - } - i_size_write(inode, 0); - ecryptfs_write_inode_size_to_header(lower_file, lower_inode, inode); - ECRYPTFS_SET_FLAG(ecryptfs_inode_to_private(inode)->crypt_stat.flags, - ECRYPTFS_NEW_FILE); -out: + ecryptfs_set_file_lower( + &fake_file, + ecryptfs_inode_to_private(ecryptfs_inode)->lower_file); + rc = ecryptfs_write(&fake_file, 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 |= + ECRYPTFS_NEW_FILE; return rc; } @@ -227,54 +172,41 @@ out: */ static int ecryptfs_initialize_file(struct dentry *ecryptfs_dentry) { + struct ecryptfs_crypt_stat *crypt_stat = + &ecryptfs_inode_to_private(ecryptfs_dentry->d_inode)->crypt_stat; int rc = 0; - int lower_flags; - struct ecryptfs_crypt_stat *crypt_stat; - struct dentry *lower_dentry; - struct file *lower_file; - struct inode *inode, *lower_inode; - struct vfsmount *lower_mnt; - lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry); - ecryptfs_printk(KERN_DEBUG, "lower_dentry->d_name.name = [%s]\n", - lower_dentry->d_name.name); - inode = ecryptfs_dentry->d_inode; - crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat; - lower_flags = ((O_CREAT | O_WRONLY | O_TRUNC) & O_ACCMODE) | O_RDWR; -#if BITS_PER_LONG != 32 - lower_flags |= O_LARGEFILE; -#endif - lower_mnt = ecryptfs_dentry_to_lower_mnt(ecryptfs_dentry); - /* Corresponding fput() at end of this function */ - if ((rc = ecryptfs_open_lower_file(&lower_file, lower_dentry, lower_mnt, - lower_flags))) { - ecryptfs_printk(KERN_ERR, - "Error opening dentry; rc = [%i]\n", rc); - goto out; - } - lower_inode = lower_dentry->d_inode; if (S_ISDIR(ecryptfs_dentry->d_inode->i_mode)) { ecryptfs_printk(KERN_DEBUG, "This is a directory\n"); - ECRYPTFS_CLEAR_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED); - goto out_fput; + crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED); + goto out; } - ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_NEW_FILE); + crypt_stat->flags |= ECRYPTFS_NEW_FILE; ecryptfs_printk(KERN_DEBUG, "Initializing crypto context\n"); rc = ecryptfs_new_file_context(ecryptfs_dentry); if (rc) { - ecryptfs_printk(KERN_DEBUG, "Error creating new file " - "context\n"); - goto out_fput; + ecryptfs_printk(KERN_ERR, "Error creating new file " + "context; rc = [%d]\n", rc); + goto out; } - rc = ecryptfs_write_headers(ecryptfs_dentry, lower_file); + if (!ecryptfs_inode_to_private(ecryptfs_dentry->d_inode)->lower_file) { + rc = ecryptfs_init_persistent_file(ecryptfs_dentry); + if (rc) { + printk(KERN_ERR "%s: Error attempting to initialize " + "the persistent file for the dentry with name " + "[%s]; rc = [%d]\n", __func__, + ecryptfs_dentry->d_name.name, rc); + goto out; + } + } + rc = ecryptfs_write_metadata(ecryptfs_dentry); if (rc) { - ecryptfs_printk(KERN_DEBUG, "Error writing headers\n"); - goto out_fput; + printk(KERN_ERR "Error writing headers; rc = [%d]\n", rc); + goto out; } - rc = grow_file(ecryptfs_dentry, lower_file, inode, lower_inode); -out_fput: - if ((rc = ecryptfs_close_lower_file(lower_file))) - printk(KERN_ERR "Error closing lower_file\n"); + rc = grow_file(ecryptfs_dentry); + if (rc) + printk(KERN_ERR "Error growing file; rc = [%d]\n", rc); out: return rc; } @@ -296,6 +228,8 @@ ecryptfs_create(struct inode *directory_inode, struct dentry *ecryptfs_dentry, { int rc; + /* ecryptfs_do_create() calls ecryptfs_interpose(), which opens + * the crypt_stat->lower_file (persistent file) */ rc = ecryptfs_do_create(directory_inode, ecryptfs_dentry, mode, nd); if (unlikely(rc)) { ecryptfs_printk(KERN_WARNING, "Failed to create file in" @@ -325,10 +259,10 @@ static struct dentry *ecryptfs_lookup(struct inode *dir, struct dentry *dentry, struct dentry *lower_dir_dentry; struct dentry *lower_dentry; struct vfsmount *lower_mnt; - struct dentry *tlower_dentry = NULL; char *encoded_name; - unsigned int encoded_namelen; + int encoded_namelen; struct ecryptfs_crypt_stat *crypt_stat = NULL; + struct ecryptfs_mount_crypt_stat *mount_crypt_stat; char *page_virt = NULL; struct inode *lower_inode; u64 file_size; @@ -336,36 +270,41 @@ static struct dentry *ecryptfs_lookup(struct inode *dir, struct dentry *dentry, lower_dir_dentry = ecryptfs_dentry_to_lower(dentry->d_parent); dentry->d_op = &ecryptfs_dops; if ((dentry->d_name.len == 1 && !strcmp(dentry->d_name.name, ".")) - || (dentry->d_name.len == 2 && !strcmp(dentry->d_name.name, ".."))) - goto out_drop; + || (dentry->d_name.len == 2 + && !strcmp(dentry->d_name.name, ".."))) { + d_drop(dentry); + goto out; + } encoded_namelen = ecryptfs_encode_filename(crypt_stat, dentry->d_name.name, dentry->d_name.len, &encoded_name); if (encoded_namelen < 0) { rc = encoded_namelen; - goto out_drop; + d_drop(dentry); + goto out; } ecryptfs_printk(KERN_DEBUG, "encoded_name = [%s]; encoded_namelen " "= [%d]\n", encoded_name, encoded_namelen); lower_dentry = lookup_one_len(encoded_name, lower_dir_dentry, encoded_namelen - 1); kfree(encoded_name); - lower_mnt = mntget(ecryptfs_dentry_to_lower_mnt(dentry->d_parent)); if (IS_ERR(lower_dentry)) { ecryptfs_printk(KERN_ERR, "ERR from lower_dentry\n"); rc = PTR_ERR(lower_dentry); - goto out_drop; + d_drop(dentry); + goto out; } + lower_mnt = mntget(ecryptfs_dentry_to_lower_mnt(dentry->d_parent)); ecryptfs_printk(KERN_DEBUG, "lower_dentry = [%p]; lower_dentry->" "d_name.name = [%s]\n", lower_dentry, lower_dentry->d_name.name); lower_inode = lower_dentry->d_inode; - ecryptfs_copy_attr_atime(dir, lower_dir_dentry->d_inode); + fsstack_copy_attr_atime(dir, lower_dir_dentry->d_inode); BUG_ON(!atomic_read(&lower_dentry->d_count)); ecryptfs_set_dentry_private(dentry, kmem_cache_alloc(ecryptfs_dentry_info_cache, - SLAB_KERNEL)); + GFP_KERNEL)); if (!ecryptfs_dentry_to_private(dentry)) { rc = -ENOMEM; ecryptfs_printk(KERN_ERR, "Out of memory whilst attempting " @@ -379,10 +318,11 @@ static struct dentry *ecryptfs_lookup(struct inode *dir, struct dentry *dentry, d_add(dentry, NULL); goto out; } - rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 1); + rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, + ECRYPTFS_INTERPOSE_FLAG_D_ADD); if (rc) { ecryptfs_printk(KERN_ERR, "Error interposing\n"); - goto out_dput; + goto out; } if (S_ISDIR(lower_inode->i_mode)) { ecryptfs_printk(KERN_DEBUG, "Is a directory; returning\n"); @@ -392,54 +332,68 @@ static struct dentry *ecryptfs_lookup(struct inode *dir, struct dentry *dentry, ecryptfs_printk(KERN_DEBUG, "Is a symlink; returning\n"); goto out; } + if (special_file(lower_inode->i_mode)) { + ecryptfs_printk(KERN_DEBUG, "Is a special file; returning\n"); + goto out; + } if (!nd) { ecryptfs_printk(KERN_DEBUG, "We have a NULL nd, just leave" "as we *think* we are about to unlink\n"); goto out; } - tlower_dentry = dget(lower_dentry); - if (!tlower_dentry || IS_ERR(tlower_dentry)) { - rc = -ENOMEM; - ecryptfs_printk(KERN_ERR, "Cannot dget lower_dentry\n"); - goto out_dput; - } /* Released in this function */ - page_virt = - (char *)kmem_cache_alloc(ecryptfs_header_cache_2, - SLAB_USER); + page_virt = kmem_cache_zalloc(ecryptfs_header_cache_2, + GFP_USER); if (!page_virt) { rc = -ENOMEM; ecryptfs_printk(KERN_ERR, "Cannot ecryptfs_kmalloc a page\n"); - goto out_dput; + goto out; } - memset(page_virt, 0, PAGE_CACHE_SIZE); - rc = ecryptfs_read_header_region(page_virt, tlower_dentry, nd->mnt); crypt_stat = &ecryptfs_inode_to_private(dentry->d_inode)->crypt_stat; - if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_POLICY_APPLIED)) + if (!(crypt_stat->flags & ECRYPTFS_POLICY_APPLIED)) ecryptfs_set_default_sizes(crypt_stat); + if (!ecryptfs_inode_to_private(dentry->d_inode)->lower_file) { + rc = ecryptfs_init_persistent_file(dentry); + if (rc) { + printk(KERN_ERR "%s: Error attempting to initialize " + "the persistent file for the dentry with name " + "[%s]; rc = [%d]\n", __func__, + dentry->d_name.name, rc); + goto out; + } + } + rc = ecryptfs_read_and_validate_header_region(page_virt, + dentry->d_inode); if (rc) { - rc = 0; - ecryptfs_printk(KERN_WARNING, "Error reading header region;" - " assuming unencrypted\n"); - } else { - if (!contains_ecryptfs_marker(page_virt - + ECRYPTFS_FILE_SIZE_BYTES)) { + rc = ecryptfs_read_and_validate_xattr_region(page_virt, dentry); + if (rc) { + printk(KERN_DEBUG "Valid metadata not found in header " + "region or xattr region; treating file as " + "unencrypted\n"); + rc = 0; kmem_cache_free(ecryptfs_header_cache_2, page_virt); goto out; } - memcpy(&file_size, page_virt, sizeof(file_size)); - file_size = be64_to_cpu(file_size); - i_size_write(dentry->d_inode, (loff_t)file_size); + crypt_stat->flags |= ECRYPTFS_METADATA_IN_XATTR; } + mount_crypt_stat = &ecryptfs_superblock_to_private( + dentry->d_sb)->mount_crypt_stat; + if (mount_crypt_stat->flags & ECRYPTFS_ENCRYPTED_VIEW_ENABLED) { + if (crypt_stat->flags & ECRYPTFS_METADATA_IN_XATTR) + file_size = (crypt_stat->num_header_bytes_at_front + + i_size_read(lower_dentry->d_inode)); + else + file_size = i_size_read(lower_dentry->d_inode); + } else { + file_size = get_unaligned_be64(page_virt); + } + i_size_write(dentry->d_inode, (loff_t)file_size); kmem_cache_free(ecryptfs_header_cache_2, page_virt); goto out; out_dput: dput(lower_dentry); - if (tlower_dentry) - dput(tlower_dentry); -out_drop: d_drop(dentry); out: return ERR_PTR(rc); @@ -467,7 +421,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; - ecryptfs_copy_attr_timesizes(dir, lower_new_dentry->d_inode); + fsstack_copy_attr_times(dir, lower_new_dentry->d_inode); + fsstack_copy_inode_size(dir, lower_new_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); @@ -475,8 +430,9 @@ out_lock: unlock_dir(lower_dir_dentry); dput(lower_new_dentry); dput(lower_old_dentry); - if (!new_dentry->d_inode) - d_drop(new_dentry); + d_drop(lower_old_dentry); + d_drop(new_dentry); + d_drop(old_dentry); return rc; } @@ -485,19 +441,21 @@ static int ecryptfs_unlink(struct inode *dir, struct dentry *dentry) int rc = 0; struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); struct inode *lower_dir_inode = ecryptfs_inode_to_lower(dir); + struct dentry *lower_dir_dentry; - lock_parent(lower_dentry); + lower_dir_dentry = lock_parent(lower_dentry); rc = vfs_unlink(lower_dir_inode, lower_dentry); if (rc) { - ecryptfs_printk(KERN_ERR, "Error in vfs_unlink\n"); + printk(KERN_ERR "Error in vfs_unlink; rc = [%d]\n", rc); goto out_unlock; } - ecryptfs_copy_attr_times(dir, lower_dir_inode); + fsstack_copy_attr_times(dir, lower_dir_inode); dentry->d_inode->i_nlink = ecryptfs_inode_to_lower(dentry->d_inode)->i_nlink; dentry->d_inode->i_ctime = dir->i_ctime; + d_drop(dentry); out_unlock: - unlock_parent(lower_dentry); + unlock_dir(lower_dir_dentry); return rc; } @@ -507,15 +465,13 @@ static int ecryptfs_symlink(struct inode *dir, struct dentry *dentry, int rc; struct dentry *lower_dentry; struct dentry *lower_dir_dentry; - umode_t mode; char *encoded_symname; - unsigned int encoded_symlen; + int encoded_symlen; struct ecryptfs_crypt_stat *crypt_stat = NULL; lower_dentry = ecryptfs_dentry_to_lower(dentry); dget(lower_dentry); lower_dir_dentry = lock_parent(lower_dentry); - mode = S_IALLUGO; encoded_symlen = ecryptfs_encode_filename(crypt_stat, symname, strlen(symname), &encoded_symname); @@ -524,14 +480,15 @@ static int ecryptfs_symlink(struct inode *dir, struct dentry *dentry, goto out_lock; } rc = vfs_symlink(lower_dir_dentry->d_inode, lower_dentry, - encoded_symname, mode); + encoded_symname); kfree(encoded_symname); if (rc || !lower_dentry->d_inode) goto out_lock; rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0); if (rc) goto out_lock; - ecryptfs_copy_attr_timesizes(dir, lower_dir_dentry->d_inode); + fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode); + fsstack_copy_inode_size(dir, lower_dir_dentry->d_inode); out_lock: unlock_dir(lower_dir_dentry); dput(lower_dentry); @@ -554,7 +511,8 @@ static int ecryptfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0); if (rc) goto out; - ecryptfs_copy_attr_timesizes(dir, lower_dir_dentry->d_inode); + fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode); + fsstack_copy_inode_size(dir, lower_dir_dentry->d_inode); dir->i_nlink = lower_dir_dentry->d_inode->i_nlink; out: unlock_dir(lower_dir_dentry); @@ -565,41 +523,24 @@ out: static int ecryptfs_rmdir(struct inode *dir, struct dentry *dentry) { - int rc = 0; - struct dentry *tdentry = NULL; struct dentry *lower_dentry; - struct dentry *tlower_dentry = NULL; struct dentry *lower_dir_dentry; + int rc; lower_dentry = ecryptfs_dentry_to_lower(dentry); - if (!(tdentry = dget(dentry))) { - rc = -EINVAL; - ecryptfs_printk(KERN_ERR, "Error dget'ing dentry [%p]\n", - dentry); - goto out; - } + dget(dentry); lower_dir_dentry = lock_parent(lower_dentry); - if (!(tlower_dentry = dget(lower_dentry))) { - rc = -EINVAL; - ecryptfs_printk(KERN_ERR, "Error dget'ing lower_dentry " - "[%p]\n", lower_dentry); - goto out; - } + dget(lower_dentry); rc = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry); - if (!rc) { - d_delete(tlower_dentry); - tlower_dentry = NULL; - } - ecryptfs_copy_attr_times(dir, lower_dir_dentry->d_inode); + dput(lower_dentry); + if (!rc) + d_delete(lower_dentry); + fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode); dir->i_nlink = lower_dir_dentry->d_inode->i_nlink; unlock_dir(lower_dir_dentry); if (!rc) d_drop(dentry); -out: - if (tdentry) - dput(tdentry); - if (tlower_dentry) - dput(tlower_dentry); + dput(dentry); return rc; } @@ -618,7 +559,8 @@ ecryptfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0); if (rc) goto out; - ecryptfs_copy_attr_timesizes(dir, lower_dir_dentry->d_inode); + fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode); + fsstack_copy_inode_size(dir, lower_dir_dentry->d_inode); out: unlock_dir(lower_dir_dentry); if (!dentry->d_inode) @@ -647,11 +589,13 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry, lower_new_dir_dentry->d_inode, lower_new_dentry); if (rc) goto out_lock; - ecryptfs_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode); + fsstack_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode, NULL); if (new_dir != old_dir) - ecryptfs_copy_attr_all(old_dir, lower_old_dir_dentry->d_inode); + fsstack_copy_attr_all(old_dir, lower_old_dir_dentry->d_inode, NULL); out_lock: unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); + dput(lower_new_dentry->d_parent); + dput(lower_old_dentry->d_parent); dput(lower_new_dentry); dput(lower_old_dentry); return rc; @@ -703,8 +647,8 @@ ecryptfs_readlink(struct dentry *dentry, char __user * buf, int bufsiz) rc = -EFAULT; } kfree(decoded_name); - ecryptfs_copy_attr_atime(dentry->d_inode, - lower_dentry->d_inode); + fsstack_copy_attr_atime(dentry->d_inode, + lower_dentry->d_inode); } out_free_lower_buf: kfree(lower_buf); @@ -754,7 +698,7 @@ ecryptfs_put_link(struct dentry *dentry, struct nameidata *nd, void *ptr) * @crypt_stat: Crypt_stat associated with file * @upper_size: Size of the upper file * - * Calculate the requried size of the lower file based on the + * Calculate the required size of the lower file based on the * specified size of the upper file. This calculation is based on the * number of headers in the underlying file and the extent size. * @@ -766,8 +710,7 @@ upper_size_to_lower_size(struct ecryptfs_crypt_stat *crypt_stat, { loff_t lower_size; - lower_size = ( crypt_stat->header_extent_size - * crypt_stat->num_header_extents_at_front ); + lower_size = crypt_stat->num_header_bytes_at_front; if (upper_size != 0) { loff_t num_extents; @@ -796,8 +739,7 @@ int ecryptfs_truncate(struct dentry *dentry, loff_t new_length) int rc = 0; struct inode *inode = dentry->d_inode; struct dentry *lower_dentry; - struct vfsmount *lower_mnt; - struct file fake_ecryptfs_file, *lower_file = NULL; + 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; @@ -810,48 +752,63 @@ int ecryptfs_truncate(struct dentry *dentry, loff_t new_length) * 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_dentry = dentry; + 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, - SLAB_KERNEL)); + GFP_KERNEL)); if (unlikely(!ecryptfs_file_to_private(&fake_ecryptfs_file))) { rc = -ENOMEM; goto out; } lower_dentry = ecryptfs_dentry_to_lower(dentry); - /* This dget & mntget is released through fput at out_fput: */ - lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry); - if ((rc = ecryptfs_open_lower_file(&lower_file, lower_dentry, lower_mnt, - O_RDWR))) { - ecryptfs_printk(KERN_ERR, - "Error opening dentry; rc = [%i]\n", rc); - goto out_free; - } - ecryptfs_set_file_lower(&fake_ecryptfs_file, lower_file); + ecryptfs_set_file_lower( + &fake_ecryptfs_file, + ecryptfs_inode_to_private(dentry->d_inode)->lower_file); /* Switch on growing or shrinking file */ if (new_length > i_size) { - rc = ecryptfs_fill_zeros(&fake_ecryptfs_file, new_length); - if (rc) { - ecryptfs_printk(KERN_ERR, - "Problem with fill_zeros\n"); - goto out_fput; + char zero[] = { 0x00 }; + + /* Write a single 0 at the last position of the file; + * 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, + (new_length - 1), 1); + } else { /* new_length < i_size_read(inode) */ + /* We're chopping off all the pages down do the page + * in which new_length is located. Fill in the end of + * that page from (new_length & ~PAGE_CACHE_MASK) to + * PAGE_CACHE_SIZE with zeros. */ + size_t num_zeros = (PAGE_CACHE_SIZE + - (new_length & ~PAGE_CACHE_MASK)); + + if (num_zeros) { + char *zeros_virt; + + zeros_virt = kzalloc(num_zeros, GFP_KERNEL); + if (!zeros_virt) { + rc = -ENOMEM; + goto out_free; + } + rc = ecryptfs_write(&fake_ecryptfs_file, zeros_virt, + new_length, 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; + } } - i_size_write(inode, new_length); - rc = ecryptfs_write_inode_size_to_header(lower_file, - lower_dentry->d_inode, - inode); + vmtruncate(inode, new_length); + rc = ecryptfs_write_inode_size_to_metadata(inode); if (rc) { - ecryptfs_printk(KERN_ERR, - "Problem with ecryptfs_write" - "_inode_size\n"); - goto out_fput; + printk(KERN_ERR "Problem with " + "ecryptfs_write_inode_size_to_metadata; " + "rc = [%d]\n", rc); + goto out_free; } - } else { /* new_length < i_size_read(inode) */ - vmtruncate(inode, new_length); - ecryptfs_write_inode_size_to_header(lower_file, - lower_dentry->d_inode, - inode); /* We are reducing the size of the ecryptfs file, and need to * know if we need to reduce the size of the lower file. */ lower_size_before_truncate = @@ -862,13 +819,6 @@ int ecryptfs_truncate(struct dentry *dentry, loff_t new_length) vmtruncate(lower_dentry->d_inode, lower_size_after_truncate); } - /* Update the access times */ - lower_dentry->d_inode->i_mtime = lower_dentry->d_inode->i_ctime - = CURRENT_TIME; - mark_inode_dirty_sync(inode); -out_fput: - if ((rc = ecryptfs_close_lower_file(lower_file))) - printk(KERN_ERR "Error closing lower_file\n"); out_free: if (ecryptfs_file_to_private(&fake_ecryptfs_file)) kmem_cache_free(ecryptfs_file_info_cache, @@ -878,22 +828,9 @@ out: } static int -ecryptfs_permission(struct inode *inode, int mask, struct nameidata *nd) +ecryptfs_permission(struct inode *inode, int mask) { - int rc; - - if (nd) { - struct vfsmount *vfsmnt_save = nd->mnt; - struct dentry *dentry_save = nd->dentry; - - nd->mnt = ecryptfs_dentry_to_lower_mnt(nd->dentry); - nd->dentry = ecryptfs_dentry_to_lower(nd->dentry); - rc = permission(ecryptfs_inode_to_lower(inode), mask, nd); - nd->mnt = vfsmnt_save; - nd->dentry = dentry_save; - } else - rc = permission(ecryptfs_inode_to_lower(inode), mask, NULL); - return rc; + return inode_permission(ecryptfs_inode_to_lower(inode), mask); } /** @@ -917,9 +854,41 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia) struct ecryptfs_crypt_stat *crypt_stat; crypt_stat = &ecryptfs_inode_to_private(dentry->d_inode)->crypt_stat; - lower_dentry = ecryptfs_dentry_to_lower(dentry); + if (!(crypt_stat->flags & ECRYPTFS_STRUCT_INITIALIZED)) + ecryptfs_init_crypt_stat(crypt_stat); inode = dentry->d_inode; lower_inode = ecryptfs_inode_to_lower(inode); + lower_dentry = ecryptfs_dentry_to_lower(dentry); + mutex_lock(&crypt_stat->cs_mutex); + if (S_ISDIR(dentry->d_inode->i_mode)) + crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED); + else if (S_ISREG(dentry->d_inode->i_mode) + && (!(crypt_stat->flags & ECRYPTFS_POLICY_APPLIED) + || !(crypt_stat->flags & ECRYPTFS_KEY_VALID))) { + struct ecryptfs_mount_crypt_stat *mount_crypt_stat; + + mount_crypt_stat = &ecryptfs_superblock_to_private( + dentry->d_sb)->mount_crypt_stat; + rc = ecryptfs_read_metadata(dentry); + if (rc) { + if (!(mount_crypt_stat->flags + & ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED)) { + rc = -EIO; + printk(KERN_WARNING "Either the lower file " + "is not in a valid eCryptfs format, " + "or the key could not be retrieved. " + "Plaintext passthrough mode is not " + "enabled; returning -EIO\n"); + mutex_unlock(&crypt_stat->cs_mutex); + goto out; + } + rc = 0; + crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED); + mutex_unlock(&crypt_stat->cs_mutex); + goto out; + } + } + mutex_unlock(&crypt_stat->cs_mutex); if (ia->ia_valid & ATTR_SIZE) { ecryptfs_printk(KERN_DEBUG, "ia->ia_valid = [0x%x] ATTR_SIZE" " = [0x%x]\n", @@ -932,13 +901,23 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia) if (rc < 0) goto out; } + + /* + * mode change is for clearing setuid/setgid bits. Allow lower fs + * to interpret this in its own way. + */ + if (ia->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID)) + ia->ia_valid &= ~ATTR_MODE; + + mutex_lock(&lower_dentry->d_inode->i_mutex); rc = notify_change(lower_dentry, ia); + mutex_unlock(&lower_dentry->d_inode->i_mutex); out: - ecryptfs_copy_attr_all(inode, lower_inode); + fsstack_copy_attr_all(inode, lower_inode, NULL); return rc; } -static int +int ecryptfs_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { @@ -958,14 +937,12 @@ out: return rc; } -static ssize_t -ecryptfs_getxattr(struct dentry *dentry, const char *name, void *value, - size_t size) +ssize_t +ecryptfs_getxattr_lower(struct dentry *lower_dentry, const char *name, + void *value, size_t size) { int rc = 0; - struct dentry *lower_dentry; - lower_dentry = ecryptfs_dentry_to_lower(dentry); if (!lower_dentry->d_inode->i_op->getxattr) { rc = -ENOSYS; goto out; @@ -979,6 +956,14 @@ out: } static ssize_t +ecryptfs_getxattr(struct dentry *dentry, const char *name, void *value, + size_t size) +{ + return ecryptfs_getxattr_lower(ecryptfs_dentry_to_lower(dentry), name, + value, size); +} + +static ssize_t ecryptfs_listxattr(struct dentry *dentry, char *list, size_t size) { int rc = 0; @@ -1028,7 +1013,7 @@ int ecryptfs_inode_set(struct inode *inode, void *lower_inode) return 0; } -struct inode_operations ecryptfs_symlink_iops = { +const struct inode_operations ecryptfs_symlink_iops = { .readlink = ecryptfs_readlink, .follow_link = ecryptfs_follow_link, .put_link = ecryptfs_put_link, @@ -1040,7 +1025,7 @@ struct inode_operations ecryptfs_symlink_iops = { .removexattr = ecryptfs_removexattr }; -struct inode_operations ecryptfs_dir_iops = { +const struct inode_operations ecryptfs_dir_iops = { .create = ecryptfs_create, .lookup = ecryptfs_lookup, .link = ecryptfs_link, @@ -1058,7 +1043,7 @@ struct inode_operations ecryptfs_dir_iops = { .removexattr = ecryptfs_removexattr }; -struct inode_operations ecryptfs_main_iops = { +const struct inode_operations ecryptfs_main_iops = { .permission = ecryptfs_permission, .setattr = ecryptfs_setattr, .setxattr = ecryptfs_setxattr,