mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-25 05:04:09 +08:00
[PATCH] ecryptfs: fs/Makefile and fs/Kconfig
eCryptfs is a stacked cryptographic filesystem for Linux. It is derived from Erez Zadok's Cryptfs, implemented through the FiST framework for generating stacked filesystems. eCryptfs extends Cryptfs to provide advanced key management and policy features. eCryptfs stores cryptographic metadata in the header of each file written, so that encrypted files can be copied between hosts; the file will be decryptable with the proper key, and there is no need to keep track of any additional information aside from what is already in the encrypted file itself. [akpm@osdl.org: updates for ongoing API changes] [bunk@stusta.de: cleanups] [akpm@osdl.org: alpha build fix] [akpm@osdl.org: cleanups] [tytso@mit.edu: inode-diet updates] [pbadari@us.ibm.com: generic_file_*_read/write() interface updates] [rdunlap@xenotime.net: printk format fixes] [akpm@osdl.org: make slab creation and teardown table-driven] Signed-off-by: Phillip Hellewell <phillip@hellewell.homeip.net> Signed-off-by: Michael Halcrow <mhalcrow@us.ibm.com> Signed-off-by: Erez Zadok <ezk@cs.sunysb.edu> Signed-off-by: Adrian Bunk <bunk@stusta.de> Signed-off-by: Stephan Mueller <smueller@chronox.de> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu> Signed-off-by: Badari Pulavarty <pbadari@us.ibm.com> Signed-off-by: Randy Dunlap <rdunlap@xenotime.net> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
parent
f7aa2638f2
commit
237fead619
77
Documentation/ecryptfs.txt
Normal file
77
Documentation/ecryptfs.txt
Normal file
@ -0,0 +1,77 @@
|
||||
eCryptfs: A stacked cryptographic filesystem for Linux
|
||||
|
||||
eCryptfs is free software. Please see the file COPYING for details.
|
||||
For documentation, please see the files in the doc/ subdirectory. For
|
||||
building and installation instructions please see the INSTALL file.
|
||||
|
||||
Maintainer: Phillip Hellewell
|
||||
Lead developer: Michael A. Halcrow <mhalcrow@us.ibm.com>
|
||||
Developers: Michael C. Thompson
|
||||
Kent Yoder
|
||||
Web Site: http://ecryptfs.sf.net
|
||||
|
||||
This software is currently undergoing development. Make sure to
|
||||
maintain a backup copy of any data you write into eCryptfs.
|
||||
|
||||
eCryptfs requires the userspace tools downloadable from the
|
||||
SourceForge site:
|
||||
|
||||
http://sourceforge.net/projects/ecryptfs/
|
||||
|
||||
Userspace requirements include:
|
||||
- David Howells' userspace keyring headers and libraries (version
|
||||
1.0 or higher), obtainable from
|
||||
http://people.redhat.com/~dhowells/keyutils/
|
||||
- Libgcrypt
|
||||
|
||||
|
||||
NOTES
|
||||
|
||||
In the beta/experimental releases of eCryptfs, when you upgrade
|
||||
eCryptfs, you should copy the files to an unencrypted location and
|
||||
then copy the files back into the new eCryptfs mount to migrate the
|
||||
files.
|
||||
|
||||
|
||||
MOUNT-WIDE PASSPHRASE
|
||||
|
||||
Create a new directory into which eCryptfs will write its encrypted
|
||||
files (i.e., /root/crypt). Then, create the mount point directory
|
||||
(i.e., /mnt/crypt). Now it's time to mount eCryptfs:
|
||||
|
||||
mount -t ecryptfs /root/crypt /mnt/crypt
|
||||
|
||||
You should be prompted for a passphrase and a salt (the salt may be
|
||||
blank).
|
||||
|
||||
Try writing a new file:
|
||||
|
||||
echo "Hello, World" > /mnt/crypt/hello.txt
|
||||
|
||||
The operation will complete. Notice that there is a new file in
|
||||
/root/crypt that is at least 12288 bytes in size (depending on your
|
||||
host page size). This is the encrypted underlying file for what you
|
||||
just wrote. To test reading, from start to finish, you need to clear
|
||||
the user session keyring:
|
||||
|
||||
keyctl clear @u
|
||||
|
||||
Then umount /mnt/crypt and mount again per the instructions given
|
||||
above.
|
||||
|
||||
cat /mnt/crypt/hello.txt
|
||||
|
||||
|
||||
NOTES
|
||||
|
||||
eCryptfs version 0.1 should only be mounted on (1) empty directories
|
||||
or (2) directories containing files only created by eCryptfs. If you
|
||||
mount a directory that has pre-existing files not created by eCryptfs,
|
||||
then behavior is undefined. Do not run eCryptfs in higher verbosity
|
||||
levels unless you are doing so for the sole purpose of debugging or
|
||||
development, since secret values will be written out to the system log
|
||||
in that case.
|
||||
|
||||
|
||||
Mike Halcrow
|
||||
mhalcrow@us.ibm.com
|
@ -977,6 +977,13 @@ L: ebtables-devel@lists.sourceforge.net
|
||||
W: http://ebtables.sourceforge.net/
|
||||
S: Maintained
|
||||
|
||||
ECRYPT FILE SYSTEM
|
||||
P: Mike Halcrow, Phillip Hellewell
|
||||
M: mhalcrow@us.ibm.com, phillip@hellewell.homeip.net
|
||||
L: ecryptfs-devel@lists.sourceforge.net
|
||||
W: http://ecryptfs.sourceforge.net/
|
||||
S: Supported
|
||||
|
||||
EDAC-CORE
|
||||
P: Doug Thompson
|
||||
M: norsk5@xmission.com
|
||||
|
12
fs/Kconfig
12
fs/Kconfig
@ -995,6 +995,18 @@ config AFFS_FS
|
||||
To compile this file system support as a module, choose M here: the
|
||||
module will be called affs. If unsure, say N.
|
||||
|
||||
config ECRYPT_FS
|
||||
tristate "eCrypt filesystem layer support (EXPERIMENTAL)"
|
||||
depends on EXPERIMENTAL && KEYS && CRYPTO
|
||||
help
|
||||
Encrypted filesystem that operates on the VFS layer. See
|
||||
<file:Documentation/ecryptfs.txt> to learn more about
|
||||
eCryptfs. Userspace components are required and can be
|
||||
obtained from <http://ecryptfs.sf.net>.
|
||||
|
||||
To compile this file system support as a module, choose M here: the
|
||||
module will be called ecryptfs.
|
||||
|
||||
config HFS_FS
|
||||
tristate "Apple Macintosh file system support (EXPERIMENTAL)"
|
||||
depends on BLOCK && EXPERIMENTAL
|
||||
|
@ -75,6 +75,7 @@ obj-$(CONFIG_BFS_FS) += bfs/
|
||||
obj-$(CONFIG_ISO9660_FS) += isofs/
|
||||
obj-$(CONFIG_HFSPLUS_FS) += hfsplus/ # Before hfs to find wrapped HFS+
|
||||
obj-$(CONFIG_HFS_FS) += hfs/
|
||||
obj-$(CONFIG_ECRYPT_FS) += ecryptfs/
|
||||
obj-$(CONFIG_VXFS_FS) += freevxfs/
|
||||
obj-$(CONFIG_NFS_FS) += nfs/
|
||||
obj-$(CONFIG_EXPORTFS) += exportfs/
|
||||
|
7
fs/ecryptfs/Makefile
Normal file
7
fs/ecryptfs/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
#
|
||||
# Makefile for the Linux 2.6 eCryptfs
|
||||
#
|
||||
|
||||
obj-$(CONFIG_ECRYPT_FS) += ecryptfs.o
|
||||
|
||||
ecryptfs-objs := dentry.o file.o inode.o main.o super.o mmap.o crypto.o keystore.o debug.o
|
1659
fs/ecryptfs/crypto.c
Normal file
1659
fs/ecryptfs/crypto.c
Normal file
File diff suppressed because it is too large
Load Diff
123
fs/ecryptfs/debug.c
Normal file
123
fs/ecryptfs/debug.c
Normal file
@ -0,0 +1,123 @@
|
||||
/**
|
||||
* eCryptfs: Linux filesystem encryption layer
|
||||
* Functions only useful for debugging.
|
||||
*
|
||||
* Copyright (C) 2006 International Business Machines Corp.
|
||||
* Author(s): Michael A. Halcrow <mahalcro@us.ibm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "ecryptfs_kernel.h"
|
||||
|
||||
/**
|
||||
* ecryptfs_dump_auth_tok - debug function to print auth toks
|
||||
*
|
||||
* This function will print the contents of an ecryptfs authentication
|
||||
* token.
|
||||
*/
|
||||
void ecryptfs_dump_auth_tok(struct ecryptfs_auth_tok *auth_tok)
|
||||
{
|
||||
char salt[ECRYPTFS_SALT_SIZE * 2 + 1];
|
||||
char sig[ECRYPTFS_SIG_SIZE_HEX + 1];
|
||||
|
||||
ecryptfs_printk(KERN_DEBUG, "Auth tok at mem loc [%p]:\n",
|
||||
auth_tok);
|
||||
if (ECRYPTFS_CHECK_FLAG(auth_tok->flags, ECRYPTFS_PRIVATE_KEY)) {
|
||||
ecryptfs_printk(KERN_DEBUG, " * private key type\n");
|
||||
ecryptfs_printk(KERN_DEBUG, " * (NO PRIVATE KEY SUPPORT "
|
||||
"IN ECRYPTFS VERSION 0.1)\n");
|
||||
} else {
|
||||
ecryptfs_printk(KERN_DEBUG, " * passphrase type\n");
|
||||
ecryptfs_to_hex(salt, auth_tok->token.password.salt,
|
||||
ECRYPTFS_SALT_SIZE);
|
||||
salt[ECRYPTFS_SALT_SIZE * 2] = '\0';
|
||||
ecryptfs_printk(KERN_DEBUG, " * salt = [%s]\n", salt);
|
||||
if (ECRYPTFS_CHECK_FLAG(auth_tok->token.password.flags,
|
||||
ECRYPTFS_PERSISTENT_PASSWORD)) {
|
||||
ecryptfs_printk(KERN_DEBUG, " * persistent\n");
|
||||
}
|
||||
memcpy(sig, auth_tok->token.password.signature,
|
||||
ECRYPTFS_SIG_SIZE_HEX);
|
||||
sig[ECRYPTFS_SIG_SIZE_HEX] = '\0';
|
||||
ecryptfs_printk(KERN_DEBUG, " * signature = [%s]\n", sig);
|
||||
}
|
||||
ecryptfs_printk(KERN_DEBUG, " * session_key.flags = [0x%x]\n",
|
||||
auth_tok->session_key.flags);
|
||||
if (auth_tok->session_key.flags
|
||||
& ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT)
|
||||
ecryptfs_printk(KERN_DEBUG,
|
||||
" * Userspace decrypt request set\n");
|
||||
if (auth_tok->session_key.flags
|
||||
& ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT)
|
||||
ecryptfs_printk(KERN_DEBUG,
|
||||
" * Userspace encrypt request set\n");
|
||||
if (auth_tok->session_key.flags & ECRYPTFS_CONTAINS_DECRYPTED_KEY) {
|
||||
ecryptfs_printk(KERN_DEBUG, " * Contains decrypted key\n");
|
||||
ecryptfs_printk(KERN_DEBUG,
|
||||
" * session_key.decrypted_key_size = [0x%x]\n",
|
||||
auth_tok->session_key.decrypted_key_size);
|
||||
ecryptfs_printk(KERN_DEBUG, " * Decrypted session key "
|
||||
"dump:\n");
|
||||
if (ecryptfs_verbosity > 0)
|
||||
ecryptfs_dump_hex(auth_tok->session_key.decrypted_key,
|
||||
ECRYPTFS_DEFAULT_KEY_BYTES);
|
||||
}
|
||||
if (auth_tok->session_key.flags & ECRYPTFS_CONTAINS_ENCRYPTED_KEY) {
|
||||
ecryptfs_printk(KERN_DEBUG, " * Contains encrypted key\n");
|
||||
ecryptfs_printk(KERN_DEBUG,
|
||||
" * session_key.encrypted_key_size = [0x%x]\n",
|
||||
auth_tok->session_key.encrypted_key_size);
|
||||
ecryptfs_printk(KERN_DEBUG, " * Encrypted session key "
|
||||
"dump:\n");
|
||||
if (ecryptfs_verbosity > 0)
|
||||
ecryptfs_dump_hex(auth_tok->session_key.encrypted_key,
|
||||
auth_tok->session_key.
|
||||
encrypted_key_size);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_dump_hex - debug hex printer
|
||||
* @data: string of bytes to be printed
|
||||
* @bytes: number of bytes to print
|
||||
*
|
||||
* Dump hexadecimal representation of char array
|
||||
*/
|
||||
void ecryptfs_dump_hex(char *data, int bytes)
|
||||
{
|
||||
int i = 0;
|
||||
int add_newline = 1;
|
||||
|
||||
if (ecryptfs_verbosity < 1)
|
||||
return;
|
||||
if (bytes != 0) {
|
||||
printk(KERN_DEBUG "0x%.2x.", (unsigned char)data[i]);
|
||||
i++;
|
||||
}
|
||||
while (i < bytes) {
|
||||
printk("0x%.2x.", (unsigned char)data[i]);
|
||||
i++;
|
||||
if (i % 16 == 0) {
|
||||
printk("\n");
|
||||
add_newline = 0;
|
||||
} else
|
||||
add_newline = 1;
|
||||
}
|
||||
if (add_newline)
|
||||
printk("\n");
|
||||
}
|
||||
|
87
fs/ecryptfs/dentry.c
Normal file
87
fs/ecryptfs/dentry.c
Normal file
@ -0,0 +1,87 @@
|
||||
/**
|
||||
* eCryptfs: Linux filesystem encryption layer
|
||||
*
|
||||
* Copyright (C) 1997-2003 Erez Zadok
|
||||
* Copyright (C) 2001-2003 Stony Brook University
|
||||
* Copyright (C) 2004-2006 International Business Machines Corp.
|
||||
* Author(s): Michael A. Halcrow <mahalcro@us.ibm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <linux/dcache.h>
|
||||
#include <linux/namei.h>
|
||||
#include "ecryptfs_kernel.h"
|
||||
|
||||
/**
|
||||
* ecryptfs_d_revalidate - revalidate an ecryptfs dentry
|
||||
* @dentry: The ecryptfs dentry
|
||||
* @nd: The associated nameidata
|
||||
*
|
||||
* Called when the VFS needs to revalidate a dentry. This
|
||||
* is called whenever a name lookup finds a dentry in the
|
||||
* dcache. Most filesystems leave this as NULL, because all their
|
||||
* dentries in the dcache are valid.
|
||||
*
|
||||
* Returns 1 if valid, 0 otherwise.
|
||||
*
|
||||
*/
|
||||
static int ecryptfs_d_revalidate(struct dentry *dentry, struct nameidata *nd)
|
||||
{
|
||||
struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
|
||||
struct vfsmount *lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry);
|
||||
struct dentry *dentry_save;
|
||||
struct vfsmount *vfsmount_save;
|
||||
int rc = 1;
|
||||
|
||||
if (!lower_dentry->d_op || !lower_dentry->d_op->d_revalidate)
|
||||
goto out;
|
||||
dentry_save = nd->dentry;
|
||||
vfsmount_save = nd->mnt;
|
||||
nd->dentry = lower_dentry;
|
||||
nd->mnt = lower_mnt;
|
||||
rc = lower_dentry->d_op->d_revalidate(lower_dentry, nd);
|
||||
nd->dentry = dentry_save;
|
||||
nd->mnt = vfsmount_save;
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct kmem_cache *ecryptfs_dentry_info_cache;
|
||||
|
||||
/**
|
||||
* ecryptfs_d_release
|
||||
* @dentry: The ecryptfs dentry
|
||||
*
|
||||
* Called when a dentry is really deallocated.
|
||||
*/
|
||||
static void ecryptfs_d_release(struct dentry *dentry)
|
||||
{
|
||||
struct dentry *lower_dentry;
|
||||
|
||||
lower_dentry = ecryptfs_dentry_to_lower(dentry);
|
||||
if (ecryptfs_dentry_to_private(dentry))
|
||||
kmem_cache_free(ecryptfs_dentry_info_cache,
|
||||
ecryptfs_dentry_to_private(dentry));
|
||||
if (lower_dentry)
|
||||
dput(lower_dentry);
|
||||
return;
|
||||
}
|
||||
|
||||
struct dentry_operations ecryptfs_dops = {
|
||||
.d_revalidate = ecryptfs_d_revalidate,
|
||||
.d_release = ecryptfs_d_release,
|
||||
};
|
482
fs/ecryptfs/ecryptfs_kernel.h
Normal file
482
fs/ecryptfs/ecryptfs_kernel.h
Normal file
@ -0,0 +1,482 @@
|
||||
/**
|
||||
* eCryptfs: Linux filesystem encryption layer
|
||||
* Kernel declarations.
|
||||
*
|
||||
* Copyright (C) 1997-2003 Erez Zadok
|
||||
* Copyright (C) 2001-2003 Stony Brook University
|
||||
* Copyright (C) 2004-2006 International Business Machines Corp.
|
||||
* Author(s): Michael A. Halcrow <mahalcro@us.ibm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef ECRYPTFS_KERNEL_H
|
||||
#define ECRYPTFS_KERNEL_H
|
||||
|
||||
#include <keys/user-type.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/scatterlist.h>
|
||||
|
||||
/* Version verification for shared data structures w/ userspace */
|
||||
#define ECRYPTFS_VERSION_MAJOR 0x00
|
||||
#define ECRYPTFS_VERSION_MINOR 0x04
|
||||
#define ECRYPTFS_SUPPORTED_FILE_VERSION 0x01
|
||||
/* These flags indicate which features are supported by the kernel
|
||||
* module; userspace tools such as the mount helper read
|
||||
* ECRYPTFS_VERSIONING_MASK from a sysfs handle in order to determine
|
||||
* how to behave. */
|
||||
#define ECRYPTFS_VERSIONING_PASSPHRASE 0x00000001
|
||||
#define ECRYPTFS_VERSIONING_PUBKEY 0x00000002
|
||||
#define ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH 0x00000004
|
||||
#define ECRYPTFS_VERSIONING_POLICY 0x00000008
|
||||
#define ECRYPTFS_VERSIONING_MASK (ECRYPTFS_VERSIONING_PASSPHRASE \
|
||||
| ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH)
|
||||
|
||||
#define ECRYPTFS_MAX_PASSWORD_LENGTH 64
|
||||
#define ECRYPTFS_MAX_PASSPHRASE_BYTES ECRYPTFS_MAX_PASSWORD_LENGTH
|
||||
#define ECRYPTFS_SALT_SIZE 8
|
||||
#define ECRYPTFS_SALT_SIZE_HEX (ECRYPTFS_SALT_SIZE*2)
|
||||
/* The original signature size is only for what is stored on disk; all
|
||||
* in-memory representations are expanded hex, so it better adapted to
|
||||
* be passed around or referenced on the command line */
|
||||
#define ECRYPTFS_SIG_SIZE 8
|
||||
#define ECRYPTFS_SIG_SIZE_HEX (ECRYPTFS_SIG_SIZE*2)
|
||||
#define ECRYPTFS_PASSWORD_SIG_SIZE ECRYPTFS_SIG_SIZE_HEX
|
||||
#define ECRYPTFS_MAX_KEY_BYTES 64
|
||||
#define ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES 512
|
||||
#define ECRYPTFS_DEFAULT_IV_BYTES 16
|
||||
#define ECRYPTFS_FILE_VERSION 0x01
|
||||
#define ECRYPTFS_DEFAULT_HEADER_EXTENT_SIZE 8192
|
||||
#define ECRYPTFS_DEFAULT_EXTENT_SIZE 4096
|
||||
#define ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE 8192
|
||||
|
||||
#define RFC2440_CIPHER_DES3_EDE 0x02
|
||||
#define RFC2440_CIPHER_CAST_5 0x03
|
||||
#define RFC2440_CIPHER_BLOWFISH 0x04
|
||||
#define RFC2440_CIPHER_AES_128 0x07
|
||||
#define RFC2440_CIPHER_AES_192 0x08
|
||||
#define RFC2440_CIPHER_AES_256 0x09
|
||||
#define RFC2440_CIPHER_TWOFISH 0x0a
|
||||
#define RFC2440_CIPHER_CAST_6 0x0b
|
||||
|
||||
#define ECRYPTFS_SET_FLAG(flag_bit_vector, flag) (flag_bit_vector |= (flag))
|
||||
#define ECRYPTFS_CLEAR_FLAG(flag_bit_vector, flag) (flag_bit_vector &= ~(flag))
|
||||
#define ECRYPTFS_CHECK_FLAG(flag_bit_vector, flag) (flag_bit_vector & (flag))
|
||||
|
||||
/**
|
||||
* For convenience, we may need to pass around the encrypted session
|
||||
* key between kernel and userspace because the authentication token
|
||||
* may not be extractable. For example, the TPM may not release the
|
||||
* private key, instead requiring the encrypted data and returning the
|
||||
* decrypted data.
|
||||
*/
|
||||
struct ecryptfs_session_key {
|
||||
#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT 0x00000001
|
||||
#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT 0x00000002
|
||||
#define ECRYPTFS_CONTAINS_DECRYPTED_KEY 0x00000004
|
||||
#define ECRYPTFS_CONTAINS_ENCRYPTED_KEY 0x00000008
|
||||
u32 flags;
|
||||
u32 encrypted_key_size;
|
||||
u32 decrypted_key_size;
|
||||
u8 encrypted_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES];
|
||||
u8 decrypted_key[ECRYPTFS_MAX_KEY_BYTES];
|
||||
};
|
||||
|
||||
struct ecryptfs_password {
|
||||
u32 password_bytes;
|
||||
s32 hash_algo;
|
||||
u32 hash_iterations;
|
||||
u32 session_key_encryption_key_bytes;
|
||||
#define ECRYPTFS_PERSISTENT_PASSWORD 0x01
|
||||
#define ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET 0x02
|
||||
u32 flags;
|
||||
/* Iterated-hash concatenation of salt and passphrase */
|
||||
u8 session_key_encryption_key[ECRYPTFS_MAX_KEY_BYTES];
|
||||
u8 signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1];
|
||||
/* Always in expanded hex */
|
||||
u8 salt[ECRYPTFS_SALT_SIZE];
|
||||
};
|
||||
|
||||
enum ecryptfs_token_types {ECRYPTFS_PASSWORD, ECRYPTFS_PRIVATE_KEY};
|
||||
|
||||
/* May be a password or a private key */
|
||||
struct ecryptfs_auth_tok {
|
||||
u16 version; /* 8-bit major and 8-bit minor */
|
||||
u16 token_type;
|
||||
u32 flags;
|
||||
struct ecryptfs_session_key session_key;
|
||||
u8 reserved[32];
|
||||
union {
|
||||
struct ecryptfs_password password;
|
||||
/* Private key is in future eCryptfs releases */
|
||||
} token;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
void ecryptfs_dump_auth_tok(struct ecryptfs_auth_tok *auth_tok);
|
||||
extern void ecryptfs_to_hex(char *dst, char *src, size_t src_size);
|
||||
extern void ecryptfs_from_hex(char *dst, char *src, int dst_size);
|
||||
|
||||
struct ecryptfs_key_record {
|
||||
unsigned char type;
|
||||
size_t enc_key_size;
|
||||
unsigned char sig[ECRYPTFS_SIG_SIZE];
|
||||
unsigned char enc_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES];
|
||||
};
|
||||
|
||||
struct ecryptfs_auth_tok_list {
|
||||
struct ecryptfs_auth_tok *auth_tok;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct ecryptfs_crypt_stat;
|
||||
struct ecryptfs_mount_crypt_stat;
|
||||
|
||||
struct ecryptfs_page_crypt_context {
|
||||
struct page *page;
|
||||
#define ECRYPTFS_PREPARE_COMMIT_MODE 0
|
||||
#define ECRYPTFS_WRITEPAGE_MODE 1
|
||||
unsigned int mode;
|
||||
union {
|
||||
struct file *lower_file;
|
||||
struct writeback_control *wbc;
|
||||
} param;
|
||||
};
|
||||
|
||||
static inline struct ecryptfs_auth_tok *
|
||||
ecryptfs_get_key_payload_data(struct key *key)
|
||||
{
|
||||
return (struct ecryptfs_auth_tok *)
|
||||
(((struct user_key_payload*)key->payload.data)->data);
|
||||
}
|
||||
|
||||
#define ECRYPTFS_SUPER_MAGIC 0xf15f
|
||||
#define ECRYPTFS_MAX_KEYSET_SIZE 1024
|
||||
#define ECRYPTFS_MAX_CIPHER_NAME_SIZE 32
|
||||
#define ECRYPTFS_MAX_NUM_ENC_KEYS 64
|
||||
#define ECRYPTFS_MAX_NUM_KEYSIGS 2 /* TODO: Make this a linked list */
|
||||
#define ECRYPTFS_MAX_IV_BYTES 16 /* 128 bits */
|
||||
#define ECRYPTFS_SALT_BYTES 2
|
||||
#define MAGIC_ECRYPTFS_MARKER 0x3c81b7f5
|
||||
#define MAGIC_ECRYPTFS_MARKER_SIZE_BYTES 8 /* 4*2 */
|
||||
#define ECRYPTFS_FILE_SIZE_BYTES 8
|
||||
#define ECRYPTFS_DEFAULT_CIPHER "aes"
|
||||
#define ECRYPTFS_DEFAULT_KEY_BYTES 16
|
||||
#define ECRYPTFS_DEFAULT_CHAINING_MODE CRYPTO_TFM_MODE_CBC
|
||||
#define ECRYPTFS_TAG_3_PACKET_TYPE 0x8C
|
||||
#define ECRYPTFS_TAG_11_PACKET_TYPE 0xED
|
||||
#define MD5_DIGEST_SIZE 16
|
||||
|
||||
/**
|
||||
* This is the primary struct associated with each encrypted file.
|
||||
*
|
||||
* TODO: cache align/pack?
|
||||
*/
|
||||
struct ecryptfs_crypt_stat {
|
||||
#define ECRYPTFS_STRUCT_INITIALIZED 0x00000001
|
||||
#define ECRYPTFS_POLICY_APPLIED 0x00000002
|
||||
#define ECRYPTFS_NEW_FILE 0x00000004
|
||||
#define ECRYPTFS_ENCRYPTED 0x00000008
|
||||
#define ECRYPTFS_SECURITY_WARNING 0x00000010
|
||||
#define ECRYPTFS_ENABLE_HMAC 0x00000020
|
||||
#define ECRYPTFS_ENCRYPT_IV_PAGES 0x00000040
|
||||
#define ECRYPTFS_KEY_VALID 0x00000080
|
||||
u32 flags;
|
||||
unsigned int file_version;
|
||||
size_t iv_bytes;
|
||||
size_t num_keysigs;
|
||||
size_t header_extent_size;
|
||||
size_t num_header_extents_at_front;
|
||||
size_t extent_size; /* Data extent size; default is 4096 */
|
||||
size_t key_size;
|
||||
size_t extent_shift;
|
||||
unsigned int extent_mask;
|
||||
struct ecryptfs_mount_crypt_stat *mount_crypt_stat;
|
||||
struct crypto_tfm *tfm;
|
||||
struct crypto_tfm *md5_tfm; /* Crypto context for generating
|
||||
* the initialization vectors */
|
||||
unsigned char cipher[ECRYPTFS_MAX_CIPHER_NAME_SIZE];
|
||||
unsigned char key[ECRYPTFS_MAX_KEY_BYTES];
|
||||
unsigned char root_iv[ECRYPTFS_MAX_IV_BYTES];
|
||||
unsigned char keysigs[ECRYPTFS_MAX_NUM_KEYSIGS][ECRYPTFS_SIG_SIZE_HEX];
|
||||
struct mutex cs_tfm_mutex;
|
||||
struct mutex cs_md5_tfm_mutex;
|
||||
struct mutex cs_mutex;
|
||||
};
|
||||
|
||||
/* inode private data. */
|
||||
struct ecryptfs_inode_info {
|
||||
struct inode vfs_inode;
|
||||
struct inode *wii_inode;
|
||||
struct ecryptfs_crypt_stat crypt_stat;
|
||||
};
|
||||
|
||||
/* dentry private data. Each dentry must keep track of a lower
|
||||
* vfsmount too. */
|
||||
struct ecryptfs_dentry_info {
|
||||
struct dentry *wdi_dentry;
|
||||
struct vfsmount *lower_mnt;
|
||||
struct ecryptfs_crypt_stat *crypt_stat;
|
||||
};
|
||||
|
||||
/**
|
||||
* This struct is to enable a mount-wide passphrase/salt combo. This
|
||||
* is more or less a stopgap to provide similar functionality to other
|
||||
* crypto filesystems like EncFS or CFS until full policy support is
|
||||
* implemented in eCryptfs.
|
||||
*/
|
||||
struct ecryptfs_mount_crypt_stat {
|
||||
/* Pointers to memory we do not own, do not free these */
|
||||
#define ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED 0x00000001
|
||||
u32 flags;
|
||||
struct ecryptfs_auth_tok *global_auth_tok;
|
||||
struct key *global_auth_tok_key;
|
||||
size_t global_default_cipher_key_size;
|
||||
struct crypto_tfm *global_key_tfm;
|
||||
struct mutex global_key_tfm_mutex;
|
||||
unsigned char global_default_cipher_name[ECRYPTFS_MAX_CIPHER_NAME_SIZE
|
||||
+ 1];
|
||||
unsigned char global_auth_tok_sig[ECRYPTFS_SIG_SIZE_HEX + 1];
|
||||
};
|
||||
|
||||
/* superblock private data. */
|
||||
struct ecryptfs_sb_info {
|
||||
struct super_block *wsi_sb;
|
||||
struct ecryptfs_mount_crypt_stat mount_crypt_stat;
|
||||
};
|
||||
|
||||
/* file private data. */
|
||||
struct ecryptfs_file_info {
|
||||
struct file *wfi_file;
|
||||
struct ecryptfs_crypt_stat *crypt_stat;
|
||||
};
|
||||
|
||||
/* auth_tok <=> encrypted_session_key mappings */
|
||||
struct ecryptfs_auth_tok_list_item {
|
||||
unsigned char encrypted_session_key[ECRYPTFS_MAX_KEY_BYTES];
|
||||
struct list_head list;
|
||||
struct ecryptfs_auth_tok auth_tok;
|
||||
};
|
||||
|
||||
static inline struct ecryptfs_file_info *
|
||||
ecryptfs_file_to_private(struct file *file)
|
||||
{
|
||||
return (struct ecryptfs_file_info *)file->private_data;
|
||||
}
|
||||
|
||||
static inline void
|
||||
ecryptfs_set_file_private(struct file *file,
|
||||
struct ecryptfs_file_info *file_info)
|
||||
{
|
||||
file->private_data = file_info;
|
||||
}
|
||||
|
||||
static inline struct file *ecryptfs_file_to_lower(struct file *file)
|
||||
{
|
||||
return ((struct ecryptfs_file_info *)file->private_data)->wfi_file;
|
||||
}
|
||||
|
||||
static inline void
|
||||
ecryptfs_set_file_lower(struct file *file, struct file *lower_file)
|
||||
{
|
||||
((struct ecryptfs_file_info *)file->private_data)->wfi_file =
|
||||
lower_file;
|
||||
}
|
||||
|
||||
static inline struct ecryptfs_inode_info *
|
||||
ecryptfs_inode_to_private(struct inode *inode)
|
||||
{
|
||||
return container_of(inode, struct ecryptfs_inode_info, vfs_inode);
|
||||
}
|
||||
|
||||
static inline struct inode *ecryptfs_inode_to_lower(struct inode *inode)
|
||||
{
|
||||
return ecryptfs_inode_to_private(inode)->wii_inode;
|
||||
}
|
||||
|
||||
static inline void
|
||||
ecryptfs_set_inode_lower(struct inode *inode, struct inode *lower_inode)
|
||||
{
|
||||
ecryptfs_inode_to_private(inode)->wii_inode = lower_inode;
|
||||
}
|
||||
|
||||
static inline struct ecryptfs_sb_info *
|
||||
ecryptfs_superblock_to_private(struct super_block *sb)
|
||||
{
|
||||
return (struct ecryptfs_sb_info *)sb->s_fs_info;
|
||||
}
|
||||
|
||||
static inline void
|
||||
ecryptfs_set_superblock_private(struct super_block *sb,
|
||||
struct ecryptfs_sb_info *sb_info)
|
||||
{
|
||||
sb->s_fs_info = sb_info;
|
||||
}
|
||||
|
||||
static inline struct super_block *
|
||||
ecryptfs_superblock_to_lower(struct super_block *sb)
|
||||
{
|
||||
return ((struct ecryptfs_sb_info *)sb->s_fs_info)->wsi_sb;
|
||||
}
|
||||
|
||||
static inline void
|
||||
ecryptfs_set_superblock_lower(struct super_block *sb,
|
||||
struct super_block *lower_sb)
|
||||
{
|
||||
((struct ecryptfs_sb_info *)sb->s_fs_info)->wsi_sb = lower_sb;
|
||||
}
|
||||
|
||||
static inline struct ecryptfs_dentry_info *
|
||||
ecryptfs_dentry_to_private(struct dentry *dentry)
|
||||
{
|
||||
return (struct ecryptfs_dentry_info *)dentry->d_fsdata;
|
||||
}
|
||||
|
||||
static inline void
|
||||
ecryptfs_set_dentry_private(struct dentry *dentry,
|
||||
struct ecryptfs_dentry_info *dentry_info)
|
||||
{
|
||||
dentry->d_fsdata = dentry_info;
|
||||
}
|
||||
|
||||
static inline struct dentry *
|
||||
ecryptfs_dentry_to_lower(struct dentry *dentry)
|
||||
{
|
||||
return ((struct ecryptfs_dentry_info *)dentry->d_fsdata)->wdi_dentry;
|
||||
}
|
||||
|
||||
static inline void
|
||||
ecryptfs_set_dentry_lower(struct dentry *dentry, struct dentry *lower_dentry)
|
||||
{
|
||||
((struct ecryptfs_dentry_info *)dentry->d_fsdata)->wdi_dentry =
|
||||
lower_dentry;
|
||||
}
|
||||
|
||||
static inline struct vfsmount *
|
||||
ecryptfs_dentry_to_lower_mnt(struct dentry *dentry)
|
||||
{
|
||||
return ((struct ecryptfs_dentry_info *)dentry->d_fsdata)->lower_mnt;
|
||||
}
|
||||
|
||||
static inline void
|
||||
ecryptfs_set_dentry_lower_mnt(struct dentry *dentry, struct vfsmount *lower_mnt)
|
||||
{
|
||||
((struct ecryptfs_dentry_info *)dentry->d_fsdata)->lower_mnt =
|
||||
lower_mnt;
|
||||
}
|
||||
|
||||
#define ecryptfs_printk(type, fmt, arg...) \
|
||||
__ecryptfs_printk(type "%s: " fmt, __FUNCTION__, ## arg);
|
||||
void __ecryptfs_printk(const char *fmt, ...);
|
||||
|
||||
extern const struct file_operations ecryptfs_main_fops;
|
||||
extern const struct file_operations ecryptfs_dir_fops;
|
||||
extern struct inode_operations ecryptfs_main_iops;
|
||||
extern struct inode_operations ecryptfs_dir_iops;
|
||||
extern struct inode_operations ecryptfs_symlink_iops;
|
||||
extern struct super_operations ecryptfs_sops;
|
||||
extern struct dentry_operations ecryptfs_dops;
|
||||
extern struct address_space_operations ecryptfs_aops;
|
||||
extern int ecryptfs_verbosity;
|
||||
|
||||
extern struct kmem_cache *ecryptfs_auth_tok_list_item_cache;
|
||||
extern struct kmem_cache *ecryptfs_file_info_cache;
|
||||
extern struct kmem_cache *ecryptfs_dentry_info_cache;
|
||||
extern struct kmem_cache *ecryptfs_inode_info_cache;
|
||||
extern struct kmem_cache *ecryptfs_sb_info_cache;
|
||||
extern struct kmem_cache *ecryptfs_header_cache_0;
|
||||
extern struct kmem_cache *ecryptfs_header_cache_1;
|
||||
extern struct kmem_cache *ecryptfs_header_cache_2;
|
||||
extern struct kmem_cache *ecryptfs_lower_page_cache;
|
||||
|
||||
int ecryptfs_interpose(struct dentry *hidden_dentry,
|
||||
struct dentry *this_dentry, struct super_block *sb,
|
||||
int flag);
|
||||
int ecryptfs_fill_zeros(struct file *file, loff_t new_length);
|
||||
int ecryptfs_decode_filename(struct ecryptfs_crypt_stat *crypt_stat,
|
||||
const char *name, int length,
|
||||
char **decrypted_name);
|
||||
int ecryptfs_encode_filename(struct ecryptfs_crypt_stat *crypt_stat,
|
||||
const char *name, int length,
|
||||
char **encoded_name);
|
||||
struct dentry *ecryptfs_lower_dentry(struct dentry *this_dentry);
|
||||
void ecryptfs_copy_attr_atime(struct inode *dest, const struct inode *src);
|
||||
void ecryptfs_copy_attr_all(struct inode *dest, const struct inode *src);
|
||||
void ecryptfs_copy_inode_size(struct inode *dst, const struct inode *src);
|
||||
void ecryptfs_dump_hex(char *data, int bytes);
|
||||
int virt_to_scatterlist(const void *addr, int size, struct scatterlist *sg,
|
||||
int sg_size);
|
||||
int ecryptfs_compute_root_iv(struct ecryptfs_crypt_stat *crypt_stat);
|
||||
void ecryptfs_rotate_iv(unsigned char *iv);
|
||||
void ecryptfs_init_crypt_stat(struct ecryptfs_crypt_stat *crypt_stat);
|
||||
void ecryptfs_destruct_crypt_stat(struct ecryptfs_crypt_stat *crypt_stat);
|
||||
void ecryptfs_destruct_mount_crypt_stat(
|
||||
struct ecryptfs_mount_crypt_stat *mount_crypt_stat);
|
||||
int ecryptfs_init_crypt_ctx(struct ecryptfs_crypt_stat *crypt_stat);
|
||||
int ecryptfs_write_inode_size_to_header(struct file *lower_file,
|
||||
struct inode *lower_inode,
|
||||
struct inode *inode);
|
||||
int ecryptfs_get_lower_page(struct page **lower_page, struct inode *lower_inode,
|
||||
struct file *lower_file,
|
||||
unsigned long lower_page_index, int byte_offset,
|
||||
int region_bytes);
|
||||
int
|
||||
ecryptfs_commit_lower_page(struct page *lower_page, struct inode *lower_inode,
|
||||
struct file *lower_file, int byte_offset,
|
||||
int region_size);
|
||||
int ecryptfs_copy_page_to_lower(struct page *page, struct inode *lower_inode,
|
||||
struct file *lower_file);
|
||||
int ecryptfs_do_readpage(struct file *file, struct page *page,
|
||||
pgoff_t lower_page_index);
|
||||
int ecryptfs_grab_and_map_lower_page(struct page **lower_page,
|
||||
char **lower_virt,
|
||||
struct inode *lower_inode,
|
||||
unsigned long lower_page_index);
|
||||
int ecryptfs_writepage_and_release_lower_page(struct page *lower_page,
|
||||
struct inode *lower_inode,
|
||||
struct writeback_control *wbc);
|
||||
int ecryptfs_encrypt_page(struct ecryptfs_page_crypt_context *ctx);
|
||||
int ecryptfs_decrypt_page(struct file *file, struct page *page);
|
||||
int ecryptfs_write_headers(struct dentry *ecryptfs_dentry,
|
||||
struct file *lower_file);
|
||||
int ecryptfs_write_headers_virt(char *page_virt,
|
||||
struct ecryptfs_crypt_stat *crypt_stat,
|
||||
struct dentry *ecryptfs_dentry);
|
||||
int ecryptfs_read_headers(struct dentry *ecryptfs_dentry,
|
||||
struct file *lower_file);
|
||||
int ecryptfs_new_file_context(struct dentry *ecryptfs_dentry);
|
||||
int contains_ecryptfs_marker(char *data);
|
||||
int ecryptfs_read_header_region(char *data, struct dentry *dentry,
|
||||
struct vfsmount *mnt);
|
||||
u16 ecryptfs_code_for_cipher_string(struct ecryptfs_crypt_stat *crypt_stat);
|
||||
int ecryptfs_cipher_code_to_string(char *str, u16 cipher_code);
|
||||
void ecryptfs_set_default_sizes(struct ecryptfs_crypt_stat *crypt_stat);
|
||||
int ecryptfs_generate_key_packet_set(char *dest_base,
|
||||
struct ecryptfs_crypt_stat *crypt_stat,
|
||||
struct dentry *ecryptfs_dentry,
|
||||
size_t *len, size_t max);
|
||||
int process_request_key_err(long err_code);
|
||||
int
|
||||
ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat,
|
||||
unsigned char *src, struct dentry *ecryptfs_dentry);
|
||||
int ecryptfs_truncate(struct dentry *dentry, loff_t new_length);
|
||||
int
|
||||
ecryptfs_process_cipher(struct crypto_tfm **tfm, struct crypto_tfm **key_tfm,
|
||||
char *cipher_name, size_t key_size);
|
||||
int ecryptfs_inode_test(struct inode *inode, void *candidate_lower_inode);
|
||||
int ecryptfs_inode_set(struct inode *inode, void *lower_inode);
|
||||
void ecryptfs_init_inode(struct inode *inode, struct inode *lower_inode);
|
||||
|
||||
#endif /* #ifndef ECRYPTFS_KERNEL_H */
|
440
fs/ecryptfs/file.c
Normal file
440
fs/ecryptfs/file.c
Normal file
@ -0,0 +1,440 @@
|
||||
/**
|
||||
* eCryptfs: Linux filesystem encryption layer
|
||||
*
|
||||
* Copyright (C) 1997-2004 Erez Zadok
|
||||
* Copyright (C) 2001-2004 Stony Brook University
|
||||
* Copyright (C) 2004-2006 International Business Machines Corp.
|
||||
* Author(s): Michael A. Halcrow <mhalcrow@us.ibm.com>
|
||||
* Michael C. Thompson <mcthomps@us.ibm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <linux/file.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/compat.h>
|
||||
#include "ecryptfs_kernel.h"
|
||||
|
||||
/**
|
||||
* ecryptfs_llseek
|
||||
* @file: File we are seeking in
|
||||
* @offset: The offset to seek to
|
||||
* @origin: 2 - offset from i_size; 1 - offset from f_pos
|
||||
*
|
||||
* Returns the position we have seeked to, or negative on error
|
||||
*/
|
||||
static loff_t ecryptfs_llseek(struct file *file, loff_t offset, int origin)
|
||||
{
|
||||
loff_t rv;
|
||||
loff_t new_end_pos;
|
||||
int rc;
|
||||
int expanding_file = 0;
|
||||
struct inode *inode = file->f_mapping->host;
|
||||
|
||||
/* If our offset is past the end of our file, we're going to
|
||||
* need to grow it so we have a valid length of 0's */
|
||||
new_end_pos = offset;
|
||||
switch (origin) {
|
||||
case 2:
|
||||
new_end_pos += i_size_read(inode);
|
||||
expanding_file = 1;
|
||||
break;
|
||||
case 1:
|
||||
new_end_pos += file->f_pos;
|
||||
if (new_end_pos > i_size_read(inode)) {
|
||||
ecryptfs_printk(KERN_DEBUG, "new_end_pos(=[0x%.16x]) "
|
||||
"> i_size_read(inode)(=[0x%.16x])\n",
|
||||
new_end_pos, i_size_read(inode));
|
||||
expanding_file = 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (new_end_pos > i_size_read(inode)) {
|
||||
ecryptfs_printk(KERN_DEBUG, "new_end_pos(=[0x%.16x]) "
|
||||
"> i_size_read(inode)(=[0x%.16x])\n",
|
||||
new_end_pos, i_size_read(inode));
|
||||
expanding_file = 1;
|
||||
}
|
||||
}
|
||||
ecryptfs_printk(KERN_DEBUG, "new_end_pos = [0x%.16x]\n", new_end_pos);
|
||||
if (expanding_file) {
|
||||
rc = ecryptfs_truncate(file->f_dentry, new_end_pos);
|
||||
if (rc) {
|
||||
rv = rc;
|
||||
ecryptfs_printk(KERN_ERR, "Error on attempt to "
|
||||
"truncate to (higher) offset [0x%.16x];"
|
||||
" rc = [%d]\n", new_end_pos, rc);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
rv = generic_file_llseek(file, offset, origin);
|
||||
out:
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_read_update_atime
|
||||
*
|
||||
* generic_file_read updates the atime of upper layer inode. But, it
|
||||
* doesn't give us a chance to update the atime of the lower layer
|
||||
* inode. This function is a wrapper to generic_file_read. It
|
||||
* updates the atime of the lower level inode if generic_file_read
|
||||
* returns without any errors. This is to be used only for file reads.
|
||||
* The function to be used for directory reads is ecryptfs_read.
|
||||
*/
|
||||
static ssize_t ecryptfs_read_update_atime(struct kiocb *iocb,
|
||||
const struct iovec *iov,
|
||||
unsigned long nr_segs, loff_t pos)
|
||||
{
|
||||
int rc;
|
||||
struct dentry *lower_dentry;
|
||||
struct vfsmount *lower_vfsmount;
|
||||
struct file *file = iocb->ki_filp;
|
||||
|
||||
rc = generic_file_aio_read(iocb, iov, nr_segs, pos);
|
||||
/*
|
||||
* Even though this is a async interface, we need to wait
|
||||
* for IO to finish to update atime
|
||||
*/
|
||||
if (-EIOCBQUEUED == rc)
|
||||
rc = wait_on_sync_kiocb(iocb);
|
||||
if (rc >= 0) {
|
||||
lower_dentry = ecryptfs_dentry_to_lower(file->f_dentry);
|
||||
lower_vfsmount = ecryptfs_dentry_to_lower_mnt(file->f_dentry);
|
||||
touch_atime(lower_vfsmount, lower_dentry);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct ecryptfs_getdents_callback {
|
||||
void *dirent;
|
||||
struct dentry *dentry;
|
||||
filldir_t filldir;
|
||||
int err;
|
||||
int filldir_called;
|
||||
int entries_written;
|
||||
};
|
||||
|
||||
/* Inspired by generic filldir in fs/readir.c */
|
||||
static int
|
||||
ecryptfs_filldir(void *dirent, const char *name, int namelen, loff_t offset,
|
||||
u64 ino, unsigned int d_type)
|
||||
{
|
||||
struct ecryptfs_crypt_stat *crypt_stat;
|
||||
struct ecryptfs_getdents_callback *buf =
|
||||
(struct ecryptfs_getdents_callback *)dirent;
|
||||
int rc;
|
||||
int decoded_length;
|
||||
char *decoded_name;
|
||||
|
||||
crypt_stat = ecryptfs_dentry_to_private(buf->dentry)->crypt_stat;
|
||||
buf->filldir_called++;
|
||||
decoded_length = ecryptfs_decode_filename(crypt_stat, name, namelen,
|
||||
&decoded_name);
|
||||
if (decoded_length < 0) {
|
||||
rc = decoded_length;
|
||||
goto out;
|
||||
}
|
||||
rc = buf->filldir(buf->dirent, decoded_name, decoded_length, offset,
|
||||
ino, d_type);
|
||||
kfree(decoded_name);
|
||||
if (rc >= 0)
|
||||
buf->entries_written++;
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_readdir
|
||||
* @file: The ecryptfs file struct
|
||||
* @dirent: Directory entry
|
||||
* @filldir: The filldir callback function
|
||||
*/
|
||||
static int ecryptfs_readdir(struct file *file, void *dirent, filldir_t filldir)
|
||||
{
|
||||
int rc;
|
||||
struct file *lower_file;
|
||||
struct inode *inode;
|
||||
struct ecryptfs_getdents_callback buf;
|
||||
|
||||
lower_file = ecryptfs_file_to_lower(file);
|
||||
lower_file->f_pos = file->f_pos;
|
||||
inode = file->f_dentry->d_inode;
|
||||
memset(&buf, 0, sizeof(buf));
|
||||
buf.dirent = dirent;
|
||||
buf.dentry = file->f_dentry;
|
||||
buf.filldir = filldir;
|
||||
retry:
|
||||
buf.filldir_called = 0;
|
||||
buf.entries_written = 0;
|
||||
buf.err = 0;
|
||||
rc = vfs_readdir(lower_file, ecryptfs_filldir, (void *)&buf);
|
||||
if (buf.err)
|
||||
rc = buf.err;
|
||||
if (buf.filldir_called && !buf.entries_written)
|
||||
goto retry;
|
||||
file->f_pos = lower_file->f_pos;
|
||||
if (rc >= 0)
|
||||
ecryptfs_copy_attr_atime(inode, lower_file->f_dentry->d_inode);
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct kmem_cache *ecryptfs_file_info_cache;
|
||||
|
||||
/**
|
||||
* ecryptfs_open
|
||||
* @inode: inode speciying file to open
|
||||
* @file: Structure to return filled in
|
||||
*
|
||||
* Opens the file specified by inode.
|
||||
*
|
||||
* Returns zero on success; non-zero otherwise
|
||||
*/
|
||||
static int ecryptfs_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int rc = 0;
|
||||
struct ecryptfs_crypt_stat *crypt_stat = NULL;
|
||||
struct ecryptfs_mount_crypt_stat *mount_crypt_stat;
|
||||
struct dentry *ecryptfs_dentry = file->f_dentry;
|
||||
/* Private value of ecryptfs_dentry allocated in
|
||||
* ecryptfs_lookup() */
|
||||
struct dentry *lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry);
|
||||
struct inode *lower_inode = NULL;
|
||||
struct file *lower_file = NULL;
|
||||
struct vfsmount *lower_mnt;
|
||||
struct ecryptfs_file_info *file_info;
|
||||
int lower_flags;
|
||||
|
||||
/* Released in ecryptfs_release or end of function if failure */
|
||||
file_info = kmem_cache_alloc(ecryptfs_file_info_cache, SLAB_KERNEL);
|
||||
ecryptfs_set_file_private(file, file_info);
|
||||
if (!file_info) {
|
||||
ecryptfs_printk(KERN_ERR,
|
||||
"Error attempting to allocate memory\n");
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
memset(file_info, 0, sizeof(*file_info));
|
||||
lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry);
|
||||
crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat;
|
||||
mount_crypt_stat = &ecryptfs_superblock_to_private(
|
||||
ecryptfs_dentry->d_sb)->mount_crypt_stat;
|
||||
mutex_lock(&crypt_stat->cs_mutex);
|
||||
if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_POLICY_APPLIED)) {
|
||||
ecryptfs_printk(KERN_DEBUG, "Setting flags for stat...\n");
|
||||
/* Policy code enabled in future release */
|
||||
ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_POLICY_APPLIED);
|
||||
ECRYPTFS_SET_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED);
|
||||
}
|
||||
mutex_unlock(&crypt_stat->cs_mutex);
|
||||
/* This mntget & dget is undone via fput when the file is released */
|
||||
dget(lower_dentry);
|
||||
lower_flags = file->f_flags;
|
||||
if ((lower_flags & O_ACCMODE) == O_WRONLY)
|
||||
lower_flags = (lower_flags & O_ACCMODE) | O_RDWR;
|
||||
if (file->f_flags & O_APPEND)
|
||||
lower_flags &= ~O_APPEND;
|
||||
lower_mnt = ecryptfs_dentry_to_lower_mnt(ecryptfs_dentry);
|
||||
mntget(lower_mnt);
|
||||
/* Corresponding fput() in ecryptfs_release() */
|
||||
lower_file = dentry_open(lower_dentry, lower_mnt, lower_flags);
|
||||
if (IS_ERR(lower_file)) {
|
||||
rc = PTR_ERR(lower_file);
|
||||
ecryptfs_printk(KERN_ERR, "Error opening lower file\n");
|
||||
goto out_puts;
|
||||
}
|
||||
ecryptfs_set_file_lower(file, lower_file);
|
||||
/* Isn't this check the same as the one in lookup? */
|
||||
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);
|
||||
rc = 0;
|
||||
goto out;
|
||||
}
|
||||
mutex_lock(&crypt_stat->cs_mutex);
|
||||
if (i_size_read(lower_inode) < ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE) {
|
||||
if (!(mount_crypt_stat->flags
|
||||
& ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED)) {
|
||||
rc = -EIO;
|
||||
printk(KERN_WARNING "Attempt to read file that is "
|
||||
"not in a valid eCryptfs format, and plaintext "
|
||||
"passthrough mode is not enabled; returning "
|
||||
"-EIO\n");
|
||||
mutex_unlock(&crypt_stat->cs_mutex);
|
||||
goto out_puts;
|
||||
}
|
||||
crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED);
|
||||
rc = 0;
|
||||
mutex_unlock(&crypt_stat->cs_mutex);
|
||||
goto out;
|
||||
} else if (!ECRYPTFS_CHECK_FLAG(crypt_stat->flags,
|
||||
ECRYPTFS_POLICY_APPLIED)
|
||||
|| !ECRYPTFS_CHECK_FLAG(crypt_stat->flags,
|
||||
ECRYPTFS_KEY_VALID)) {
|
||||
rc = ecryptfs_read_headers(ecryptfs_dentry, lower_file);
|
||||
if (rc) {
|
||||
ecryptfs_printk(KERN_DEBUG,
|
||||
"Valid headers not found\n");
|
||||
if (!(mount_crypt_stat->flags
|
||||
& ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED)) {
|
||||
rc = -EIO;
|
||||
printk(KERN_WARNING "Attempt to read file that "
|
||||
"is not in a valid eCryptfs format, "
|
||||
"and plaintext passthrough mode is not "
|
||||
"enabled; returning -EIO\n");
|
||||
mutex_unlock(&crypt_stat->cs_mutex);
|
||||
goto out_puts;
|
||||
}
|
||||
ECRYPTFS_CLEAR_FLAG(crypt_stat->flags,
|
||||
ECRYPTFS_ENCRYPTED);
|
||||
rc = 0;
|
||||
mutex_unlock(&crypt_stat->cs_mutex);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&crypt_stat->cs_mutex);
|
||||
ecryptfs_printk(KERN_DEBUG, "inode w/ addr = [0x%p], i_ino = [0x%.16x] "
|
||||
"size: [0x%.16x]\n", inode, inode->i_ino,
|
||||
i_size_read(inode));
|
||||
ecryptfs_set_file_lower(file, lower_file);
|
||||
goto out;
|
||||
out_puts:
|
||||
mntput(lower_mnt);
|
||||
dput(lower_dentry);
|
||||
kmem_cache_free(ecryptfs_file_info_cache,
|
||||
ecryptfs_file_to_private(file));
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int ecryptfs_flush(struct file *file, fl_owner_t td)
|
||||
{
|
||||
int rc = 0;
|
||||
struct file *lower_file = NULL;
|
||||
|
||||
lower_file = ecryptfs_file_to_lower(file);
|
||||
if (lower_file->f_op && lower_file->f_op->flush)
|
||||
rc = lower_file->f_op->flush(lower_file, td);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int ecryptfs_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct file *lower_file = ecryptfs_file_to_lower(file);
|
||||
struct ecryptfs_file_info *file_info = ecryptfs_file_to_private(file);
|
||||
struct inode *lower_inode = ecryptfs_inode_to_lower(inode);
|
||||
|
||||
fput(lower_file);
|
||||
inode->i_blocks = lower_inode->i_blocks;
|
||||
kmem_cache_free(ecryptfs_file_info_cache, file_info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ecryptfs_fsync(struct file *file, struct dentry *dentry, int datasync)
|
||||
{
|
||||
struct file *lower_file = ecryptfs_file_to_lower(file);
|
||||
struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
|
||||
struct inode *lower_inode = lower_dentry->d_inode;
|
||||
int rc = -EINVAL;
|
||||
|
||||
if (lower_inode->i_fop->fsync) {
|
||||
mutex_lock(&lower_inode->i_mutex);
|
||||
rc = lower_inode->i_fop->fsync(lower_file, lower_dentry,
|
||||
datasync);
|
||||
mutex_unlock(&lower_inode->i_mutex);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int ecryptfs_fasync(int fd, struct file *file, int flag)
|
||||
{
|
||||
int rc = 0;
|
||||
struct file *lower_file = NULL;
|
||||
|
||||
lower_file = ecryptfs_file_to_lower(file);
|
||||
if (lower_file->f_op && lower_file->f_op->fasync)
|
||||
rc = lower_file->f_op->fasync(fd, lower_file, flag);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static ssize_t ecryptfs_sendfile(struct file *file, loff_t * ppos,
|
||||
size_t count, read_actor_t actor, void *target)
|
||||
{
|
||||
struct file *lower_file = NULL;
|
||||
int rc = -EINVAL;
|
||||
|
||||
lower_file = ecryptfs_file_to_lower(file);
|
||||
if (lower_file->f_op && lower_file->f_op->sendfile)
|
||||
rc = lower_file->f_op->sendfile(lower_file, ppos, count,
|
||||
actor, target);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int ecryptfs_ioctl(struct inode *inode, struct file *file,
|
||||
unsigned int cmd, unsigned long arg);
|
||||
|
||||
const struct file_operations ecryptfs_dir_fops = {
|
||||
.readdir = ecryptfs_readdir,
|
||||
.ioctl = ecryptfs_ioctl,
|
||||
.mmap = generic_file_mmap,
|
||||
.open = ecryptfs_open,
|
||||
.flush = ecryptfs_flush,
|
||||
.release = ecryptfs_release,
|
||||
.fsync = ecryptfs_fsync,
|
||||
.fasync = ecryptfs_fasync,
|
||||
.sendfile = ecryptfs_sendfile,
|
||||
};
|
||||
|
||||
const struct file_operations ecryptfs_main_fops = {
|
||||
.llseek = ecryptfs_llseek,
|
||||
.read = do_sync_read,
|
||||
.aio_read = ecryptfs_read_update_atime,
|
||||
.write = do_sync_write,
|
||||
.aio_write = generic_file_aio_write,
|
||||
.readdir = ecryptfs_readdir,
|
||||
.ioctl = ecryptfs_ioctl,
|
||||
.mmap = generic_file_mmap,
|
||||
.open = ecryptfs_open,
|
||||
.flush = ecryptfs_flush,
|
||||
.release = ecryptfs_release,
|
||||
.fsync = ecryptfs_fsync,
|
||||
.fasync = ecryptfs_fasync,
|
||||
.sendfile = ecryptfs_sendfile,
|
||||
};
|
||||
|
||||
static int
|
||||
ecryptfs_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
int rc = 0;
|
||||
struct file *lower_file = NULL;
|
||||
|
||||
if (ecryptfs_file_to_private(file))
|
||||
lower_file = ecryptfs_file_to_lower(file);
|
||||
if (lower_file && lower_file->f_op && lower_file->f_op->ioctl)
|
||||
rc = lower_file->f_op->ioctl(ecryptfs_inode_to_lower(inode),
|
||||
lower_file, cmd, arg);
|
||||
else
|
||||
rc = -ENOTTY;
|
||||
return rc;
|
||||
}
|
1079
fs/ecryptfs/inode.c
Normal file
1079
fs/ecryptfs/inode.c
Normal file
File diff suppressed because it is too large
Load Diff
1061
fs/ecryptfs/keystore.c
Normal file
1061
fs/ecryptfs/keystore.c
Normal file
File diff suppressed because it is too large
Load Diff
831
fs/ecryptfs/main.c
Normal file
831
fs/ecryptfs/main.c
Normal file
@ -0,0 +1,831 @@
|
||||
/**
|
||||
* eCryptfs: Linux filesystem encryption layer
|
||||
*
|
||||
* Copyright (C) 1997-2003 Erez Zadok
|
||||
* Copyright (C) 2001-2003 Stony Brook University
|
||||
* Copyright (C) 2004-2006 International Business Machines Corp.
|
||||
* Author(s): Michael A. Halcrow <mahalcro@us.ibm.com>
|
||||
* Michael C. Thompson <mcthomps@us.ibm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <linux/dcache.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/dcache.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/key.h>
|
||||
#include <linux/parser.h>
|
||||
#include "ecryptfs_kernel.h"
|
||||
|
||||
/**
|
||||
* Module parameter that defines the ecryptfs_verbosity level.
|
||||
*/
|
||||
int ecryptfs_verbosity = 0;
|
||||
|
||||
module_param(ecryptfs_verbosity, int, 0);
|
||||
MODULE_PARM_DESC(ecryptfs_verbosity,
|
||||
"Initial verbosity level (0 or 1; defaults to "
|
||||
"0, which is Quiet)");
|
||||
|
||||
void __ecryptfs_printk(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
if (fmt[1] == '7') { /* KERN_DEBUG */
|
||||
if (ecryptfs_verbosity >= 1)
|
||||
vprintk(fmt, args);
|
||||
} else
|
||||
vprintk(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_interpose
|
||||
* @lower_dentry: Existing dentry in the lower filesystem
|
||||
* @dentry: ecryptfs' dentry
|
||||
* @sb: ecryptfs's super_block
|
||||
* @flag: If set to true, then d_add is called, else d_instantiate is called
|
||||
*
|
||||
* Interposes upper and lower dentries.
|
||||
*
|
||||
* Returns zero on success; non-zero otherwise
|
||||
*/
|
||||
int ecryptfs_interpose(struct dentry *lower_dentry, struct dentry *dentry,
|
||||
struct super_block *sb, int flag)
|
||||
{
|
||||
struct inode *lower_inode;
|
||||
struct inode *inode;
|
||||
int rc = 0;
|
||||
|
||||
lower_inode = lower_dentry->d_inode;
|
||||
if (lower_inode->i_sb != ecryptfs_superblock_to_lower(sb)) {
|
||||
rc = -EXDEV;
|
||||
goto out;
|
||||
}
|
||||
if (!igrab(lower_inode)) {
|
||||
rc = -ESTALE;
|
||||
goto out;
|
||||
}
|
||||
inode = iget5_locked(sb, (unsigned long)lower_inode,
|
||||
ecryptfs_inode_test, ecryptfs_inode_set,
|
||||
lower_inode);
|
||||
if (!inode) {
|
||||
rc = -EACCES;
|
||||
iput(lower_inode);
|
||||
goto out;
|
||||
}
|
||||
if (inode->i_state & I_NEW)
|
||||
unlock_new_inode(inode);
|
||||
else
|
||||
iput(lower_inode);
|
||||
if (S_ISLNK(lower_inode->i_mode))
|
||||
inode->i_op = &ecryptfs_symlink_iops;
|
||||
else if (S_ISDIR(lower_inode->i_mode))
|
||||
inode->i_op = &ecryptfs_dir_iops;
|
||||
if (S_ISDIR(lower_inode->i_mode))
|
||||
inode->i_fop = &ecryptfs_dir_fops;
|
||||
/* TODO: Is there a better way to identify if the inode is
|
||||
* special? */
|
||||
if (S_ISBLK(lower_inode->i_mode) || S_ISCHR(lower_inode->i_mode) ||
|
||||
S_ISFIFO(lower_inode->i_mode) || S_ISSOCK(lower_inode->i_mode))
|
||||
init_special_inode(inode, lower_inode->i_mode,
|
||||
lower_inode->i_rdev);
|
||||
dentry->d_op = &ecryptfs_dops;
|
||||
if (flag)
|
||||
d_add(dentry, inode);
|
||||
else
|
||||
d_instantiate(dentry, inode);
|
||||
ecryptfs_copy_attr_all(inode, lower_inode);
|
||||
/* This size will be overwritten for real files w/ headers and
|
||||
* other metadata */
|
||||
ecryptfs_copy_inode_size(inode, lower_inode);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
enum { ecryptfs_opt_sig, ecryptfs_opt_ecryptfs_sig, ecryptfs_opt_debug,
|
||||
ecryptfs_opt_ecryptfs_debug, ecryptfs_opt_cipher,
|
||||
ecryptfs_opt_ecryptfs_cipher, ecryptfs_opt_ecryptfs_key_bytes,
|
||||
ecryptfs_opt_passthrough, ecryptfs_opt_err };
|
||||
|
||||
static match_table_t tokens = {
|
||||
{ecryptfs_opt_sig, "sig=%s"},
|
||||
{ecryptfs_opt_ecryptfs_sig, "ecryptfs_sig=%s"},
|
||||
{ecryptfs_opt_debug, "debug=%u"},
|
||||
{ecryptfs_opt_ecryptfs_debug, "ecryptfs_debug=%u"},
|
||||
{ecryptfs_opt_cipher, "cipher=%s"},
|
||||
{ecryptfs_opt_ecryptfs_cipher, "ecryptfs_cipher=%s"},
|
||||
{ecryptfs_opt_ecryptfs_key_bytes, "ecryptfs_key_bytes=%u"},
|
||||
{ecryptfs_opt_passthrough, "ecryptfs_passthrough"},
|
||||
{ecryptfs_opt_err, NULL}
|
||||
};
|
||||
|
||||
/**
|
||||
* ecryptfs_verify_version
|
||||
* @version: The version number to confirm
|
||||
*
|
||||
* Returns zero on good version; non-zero otherwise
|
||||
*/
|
||||
static int ecryptfs_verify_version(u16 version)
|
||||
{
|
||||
int rc = 0;
|
||||
unsigned char major;
|
||||
unsigned char minor;
|
||||
|
||||
major = ((version >> 8) & 0xFF);
|
||||
minor = (version & 0xFF);
|
||||
if (major != ECRYPTFS_VERSION_MAJOR) {
|
||||
ecryptfs_printk(KERN_ERR, "Major version number mismatch. "
|
||||
"Expected [%d]; got [%d]\n",
|
||||
ECRYPTFS_VERSION_MAJOR, major);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (minor != ECRYPTFS_VERSION_MINOR) {
|
||||
ecryptfs_printk(KERN_ERR, "Minor version number mismatch. "
|
||||
"Expected [%d]; got [%d]\n",
|
||||
ECRYPTFS_VERSION_MINOR, minor);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_parse_options
|
||||
* @sb: The ecryptfs super block
|
||||
* @options: The options pased to the kernel
|
||||
*
|
||||
* Parse mount options:
|
||||
* debug=N - ecryptfs_verbosity level for debug output
|
||||
* sig=XXX - description(signature) of the key to use
|
||||
*
|
||||
* Returns the dentry object of the lower-level (lower/interposed)
|
||||
* directory; We want to mount our stackable file system on top of
|
||||
* that lower directory.
|
||||
*
|
||||
* The signature of the key to use must be the description of a key
|
||||
* already in the keyring. Mounting will fail if the key can not be
|
||||
* found.
|
||||
*
|
||||
* Returns zero on success; non-zero on error
|
||||
*/
|
||||
static int ecryptfs_parse_options(struct super_block *sb, char *options)
|
||||
{
|
||||
char *p;
|
||||
int rc = 0;
|
||||
int sig_set = 0;
|
||||
int cipher_name_set = 0;
|
||||
int cipher_key_bytes;
|
||||
int cipher_key_bytes_set = 0;
|
||||
struct key *auth_tok_key = NULL;
|
||||
struct ecryptfs_auth_tok *auth_tok = NULL;
|
||||
struct ecryptfs_mount_crypt_stat *mount_crypt_stat =
|
||||
&ecryptfs_superblock_to_private(sb)->mount_crypt_stat;
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
int token;
|
||||
char *sig_src;
|
||||
char *sig_dst;
|
||||
char *debug_src;
|
||||
char *cipher_name_dst;
|
||||
char *cipher_name_src;
|
||||
char *cipher_key_bytes_src;
|
||||
struct crypto_tfm *tmp_tfm;
|
||||
int cipher_name_len;
|
||||
|
||||
if (!options) {
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
while ((p = strsep(&options, ",")) != NULL) {
|
||||
if (!*p)
|
||||
continue;
|
||||
token = match_token(p, tokens, args);
|
||||
switch (token) {
|
||||
case ecryptfs_opt_sig:
|
||||
case ecryptfs_opt_ecryptfs_sig:
|
||||
sig_src = args[0].from;
|
||||
sig_dst =
|
||||
mount_crypt_stat->global_auth_tok_sig;
|
||||
memcpy(sig_dst, sig_src, ECRYPTFS_SIG_SIZE_HEX);
|
||||
sig_dst[ECRYPTFS_SIG_SIZE_HEX] = '\0';
|
||||
ecryptfs_printk(KERN_DEBUG,
|
||||
"The mount_crypt_stat "
|
||||
"global_auth_tok_sig set to: "
|
||||
"[%s]\n", sig_dst);
|
||||
sig_set = 1;
|
||||
break;
|
||||
case ecryptfs_opt_debug:
|
||||
case ecryptfs_opt_ecryptfs_debug:
|
||||
debug_src = args[0].from;
|
||||
ecryptfs_verbosity =
|
||||
(int)simple_strtol(debug_src, &debug_src,
|
||||
0);
|
||||
ecryptfs_printk(KERN_DEBUG,
|
||||
"Verbosity set to [%d]" "\n",
|
||||
ecryptfs_verbosity);
|
||||
break;
|
||||
case ecryptfs_opt_cipher:
|
||||
case ecryptfs_opt_ecryptfs_cipher:
|
||||
cipher_name_src = args[0].from;
|
||||
cipher_name_dst =
|
||||
mount_crypt_stat->
|
||||
global_default_cipher_name;
|
||||
strncpy(cipher_name_dst, cipher_name_src,
|
||||
ECRYPTFS_MAX_CIPHER_NAME_SIZE);
|
||||
ecryptfs_printk(KERN_DEBUG,
|
||||
"The mount_crypt_stat "
|
||||
"global_default_cipher_name set to: "
|
||||
"[%s]\n", cipher_name_dst);
|
||||
cipher_name_set = 1;
|
||||
break;
|
||||
case ecryptfs_opt_ecryptfs_key_bytes:
|
||||
cipher_key_bytes_src = args[0].from;
|
||||
cipher_key_bytes =
|
||||
(int)simple_strtol(cipher_key_bytes_src,
|
||||
&cipher_key_bytes_src, 0);
|
||||
mount_crypt_stat->global_default_cipher_key_size =
|
||||
cipher_key_bytes;
|
||||
ecryptfs_printk(KERN_DEBUG,
|
||||
"The mount_crypt_stat "
|
||||
"global_default_cipher_key_size "
|
||||
"set to: [%d]\n", mount_crypt_stat->
|
||||
global_default_cipher_key_size);
|
||||
cipher_key_bytes_set = 1;
|
||||
break;
|
||||
case ecryptfs_opt_passthrough:
|
||||
mount_crypt_stat->flags |=
|
||||
ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED;
|
||||
break;
|
||||
case ecryptfs_opt_err:
|
||||
default:
|
||||
ecryptfs_printk(KERN_WARNING,
|
||||
"eCryptfs: unrecognized option '%s'\n",
|
||||
p);
|
||||
}
|
||||
}
|
||||
/* Do not support lack of mount-wide signature in 0.1
|
||||
* release */
|
||||
if (!sig_set) {
|
||||
rc = -EINVAL;
|
||||
ecryptfs_printk(KERN_ERR, "You must supply a valid "
|
||||
"passphrase auth tok signature as a mount "
|
||||
"parameter; see the eCryptfs README\n");
|
||||
goto out;
|
||||
}
|
||||
if (!cipher_name_set) {
|
||||
cipher_name_len = strlen(ECRYPTFS_DEFAULT_CIPHER);
|
||||
if (unlikely(cipher_name_len
|
||||
>= ECRYPTFS_MAX_CIPHER_NAME_SIZE)) {
|
||||
rc = -EINVAL;
|
||||
BUG();
|
||||
goto out;
|
||||
}
|
||||
memcpy(mount_crypt_stat->global_default_cipher_name,
|
||||
ECRYPTFS_DEFAULT_CIPHER, cipher_name_len);
|
||||
mount_crypt_stat->global_default_cipher_name[cipher_name_len]
|
||||
= '\0';
|
||||
}
|
||||
if (!cipher_key_bytes_set) {
|
||||
mount_crypt_stat->global_default_cipher_key_size =
|
||||
ECRYPTFS_DEFAULT_KEY_BYTES;
|
||||
ecryptfs_printk(KERN_DEBUG, "Cipher key size was not "
|
||||
"specified. Defaulting to [%d]\n",
|
||||
mount_crypt_stat->
|
||||
global_default_cipher_key_size);
|
||||
}
|
||||
rc = ecryptfs_process_cipher(
|
||||
&tmp_tfm,
|
||||
&mount_crypt_stat->global_key_tfm,
|
||||
mount_crypt_stat->global_default_cipher_name,
|
||||
mount_crypt_stat->global_default_cipher_key_size);
|
||||
if (tmp_tfm)
|
||||
crypto_free_tfm(tmp_tfm);
|
||||
if (rc) {
|
||||
printk(KERN_ERR "Error attempting to initialize cipher [%s] "
|
||||
"with key size [%Zd] bytes; rc = [%d]\n",
|
||||
mount_crypt_stat->global_default_cipher_name,
|
||||
mount_crypt_stat->global_default_cipher_key_size, rc);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
mutex_init(&mount_crypt_stat->global_key_tfm_mutex);
|
||||
ecryptfs_printk(KERN_DEBUG, "Requesting the key with description: "
|
||||
"[%s]\n", mount_crypt_stat->global_auth_tok_sig);
|
||||
/* The reference to this key is held until umount is done The
|
||||
* call to key_put is done in ecryptfs_put_super() */
|
||||
auth_tok_key = request_key(&key_type_user,
|
||||
mount_crypt_stat->global_auth_tok_sig,
|
||||
NULL);
|
||||
if (!auth_tok_key || IS_ERR(auth_tok_key)) {
|
||||
ecryptfs_printk(KERN_ERR, "Could not find key with "
|
||||
"description: [%s]\n",
|
||||
mount_crypt_stat->global_auth_tok_sig);
|
||||
process_request_key_err(PTR_ERR(auth_tok_key));
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
auth_tok = ecryptfs_get_key_payload_data(auth_tok_key);
|
||||
if (ecryptfs_verify_version(auth_tok->version)) {
|
||||
ecryptfs_printk(KERN_ERR, "Data structure version mismatch. "
|
||||
"Userspace tools must match eCryptfs kernel "
|
||||
"module with major version [%d] and minor "
|
||||
"version [%d]\n", ECRYPTFS_VERSION_MAJOR,
|
||||
ECRYPTFS_VERSION_MINOR);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (auth_tok->token_type != ECRYPTFS_PASSWORD) {
|
||||
ecryptfs_printk(KERN_ERR, "Invalid auth_tok structure "
|
||||
"returned from key\n");
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
mount_crypt_stat->global_auth_tok_key = auth_tok_key;
|
||||
mount_crypt_stat->global_auth_tok = auth_tok;
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct kmem_cache *ecryptfs_sb_info_cache;
|
||||
|
||||
/**
|
||||
* ecryptfs_fill_super
|
||||
* @sb: The ecryptfs super block
|
||||
* @raw_data: The options passed to mount
|
||||
* @silent: Not used but required by function prototype
|
||||
*
|
||||
* Sets up what we can of the sb, rest is done in ecryptfs_read_super
|
||||
*
|
||||
* Returns zero on success; non-zero otherwise
|
||||
*/
|
||||
static int
|
||||
ecryptfs_fill_super(struct super_block *sb, void *raw_data, int silent)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
/* Released in ecryptfs_put_super() */
|
||||
ecryptfs_set_superblock_private(sb,
|
||||
kmem_cache_alloc(ecryptfs_sb_info_cache,
|
||||
SLAB_KERNEL));
|
||||
if (!ecryptfs_superblock_to_private(sb)) {
|
||||
ecryptfs_printk(KERN_WARNING, "Out of memory\n");
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
memset(ecryptfs_superblock_to_private(sb), 0,
|
||||
sizeof(struct ecryptfs_sb_info));
|
||||
sb->s_op = &ecryptfs_sops;
|
||||
/* Released through deactivate_super(sb) from get_sb_nodev */
|
||||
sb->s_root = d_alloc(NULL, &(const struct qstr) {
|
||||
.hash = 0,.name = "/",.len = 1});
|
||||
if (!sb->s_root) {
|
||||
ecryptfs_printk(KERN_ERR, "d_alloc failed\n");
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
sb->s_root->d_op = &ecryptfs_dops;
|
||||
sb->s_root->d_sb = sb;
|
||||
sb->s_root->d_parent = sb->s_root;
|
||||
/* Released in d_release when dput(sb->s_root) is called */
|
||||
/* through deactivate_super(sb) from get_sb_nodev() */
|
||||
ecryptfs_set_dentry_private(sb->s_root,
|
||||
kmem_cache_alloc(ecryptfs_dentry_info_cache,
|
||||
SLAB_KERNEL));
|
||||
if (!ecryptfs_dentry_to_private(sb->s_root)) {
|
||||
ecryptfs_printk(KERN_ERR,
|
||||
"dentry_info_cache alloc failed\n");
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
memset(ecryptfs_dentry_to_private(sb->s_root), 0,
|
||||
sizeof(struct ecryptfs_dentry_info));
|
||||
rc = 0;
|
||||
out:
|
||||
/* Should be able to rely on deactivate_super called from
|
||||
* get_sb_nodev */
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_read_super
|
||||
* @sb: The ecryptfs super block
|
||||
* @dev_name: The path to mount over
|
||||
*
|
||||
* Read the super block of the lower filesystem, and use
|
||||
* ecryptfs_interpose to create our initial inode and super block
|
||||
* struct.
|
||||
*/
|
||||
static int ecryptfs_read_super(struct super_block *sb, const char *dev_name)
|
||||
{
|
||||
int rc;
|
||||
struct nameidata nd;
|
||||
struct dentry *lower_root;
|
||||
struct vfsmount *lower_mnt;
|
||||
|
||||
memset(&nd, 0, sizeof(struct nameidata));
|
||||
rc = path_lookup(dev_name, LOOKUP_FOLLOW, &nd);
|
||||
if (rc) {
|
||||
ecryptfs_printk(KERN_WARNING, "path_lookup() failed\n");
|
||||
goto out_free;
|
||||
}
|
||||
lower_root = nd.dentry;
|
||||
if (!lower_root->d_inode) {
|
||||
ecryptfs_printk(KERN_WARNING,
|
||||
"No directory to interpose on\n");
|
||||
rc = -ENOENT;
|
||||
goto out_free;
|
||||
}
|
||||
lower_mnt = nd.mnt;
|
||||
ecryptfs_set_superblock_lower(sb, lower_root->d_sb);
|
||||
sb->s_maxbytes = lower_root->d_sb->s_maxbytes;
|
||||
ecryptfs_set_dentry_lower(sb->s_root, lower_root);
|
||||
ecryptfs_set_dentry_lower_mnt(sb->s_root, lower_mnt);
|
||||
if ((rc = ecryptfs_interpose(lower_root, sb->s_root, sb, 0)))
|
||||
goto out_free;
|
||||
rc = 0;
|
||||
goto out;
|
||||
out_free:
|
||||
path_release(&nd);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_get_sb
|
||||
* @fs_type
|
||||
* @flags
|
||||
* @dev_name: The path to mount over
|
||||
* @raw_data: The options passed into the kernel
|
||||
*
|
||||
* The whole ecryptfs_get_sb process is broken into 4 functions:
|
||||
* ecryptfs_parse_options(): handle options passed to ecryptfs, if any
|
||||
* ecryptfs_fill_super(): used by get_sb_nodev, fills out the super_block
|
||||
* with as much information as it can before needing
|
||||
* the lower filesystem.
|
||||
* ecryptfs_read_super(): this accesses the lower filesystem and uses
|
||||
* ecryptfs_interpolate to perform most of the linking
|
||||
* ecryptfs_interpolate(): links the lower filesystem into ecryptfs
|
||||
*/
|
||||
static int ecryptfs_get_sb(struct file_system_type *fs_type, int flags,
|
||||
const char *dev_name, void *raw_data,
|
||||
struct vfsmount *mnt)
|
||||
{
|
||||
int rc;
|
||||
struct super_block *sb;
|
||||
|
||||
rc = get_sb_nodev(fs_type, flags, raw_data, ecryptfs_fill_super, mnt);
|
||||
if (rc < 0) {
|
||||
printk(KERN_ERR "Getting sb failed; rc = [%d]\n", rc);
|
||||
goto out;
|
||||
}
|
||||
sb = mnt->mnt_sb;
|
||||
rc = ecryptfs_parse_options(sb, raw_data);
|
||||
if (rc) {
|
||||
printk(KERN_ERR "Error parsing options; rc = [%d]\n", rc);
|
||||
goto out_abort;
|
||||
}
|
||||
rc = ecryptfs_read_super(sb, dev_name);
|
||||
if (rc) {
|
||||
printk(KERN_ERR "Reading sb failed; rc = [%d]\n", rc);
|
||||
goto out_abort;
|
||||
}
|
||||
goto out;
|
||||
out_abort:
|
||||
dput(sb->s_root);
|
||||
up_write(&sb->s_umount);
|
||||
deactivate_super(sb);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_kill_block_super
|
||||
* @sb: The ecryptfs super block
|
||||
*
|
||||
* Used to bring the superblock down and free the private data.
|
||||
* Private data is free'd in ecryptfs_put_super()
|
||||
*/
|
||||
static void ecryptfs_kill_block_super(struct super_block *sb)
|
||||
{
|
||||
generic_shutdown_super(sb);
|
||||
}
|
||||
|
||||
static struct file_system_type ecryptfs_fs_type = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "ecryptfs",
|
||||
.get_sb = ecryptfs_get_sb,
|
||||
.kill_sb = ecryptfs_kill_block_super,
|
||||
.fs_flags = 0
|
||||
};
|
||||
|
||||
/**
|
||||
* inode_info_init_once
|
||||
*
|
||||
* Initializes the ecryptfs_inode_info_cache when it is created
|
||||
*/
|
||||
static void
|
||||
inode_info_init_once(void *vptr, struct kmem_cache *cachep, unsigned long flags)
|
||||
{
|
||||
struct ecryptfs_inode_info *ei = (struct ecryptfs_inode_info *)vptr;
|
||||
|
||||
if ((flags & (SLAB_CTOR_VERIFY | SLAB_CTOR_CONSTRUCTOR)) ==
|
||||
SLAB_CTOR_CONSTRUCTOR)
|
||||
inode_init_once(&ei->vfs_inode);
|
||||
}
|
||||
|
||||
static struct ecryptfs_cache_info {
|
||||
kmem_cache_t **cache;
|
||||
const char *name;
|
||||
size_t size;
|
||||
void (*ctor)(void*, struct kmem_cache *, unsigned long);
|
||||
} ecryptfs_cache_infos[] = {
|
||||
{
|
||||
.cache = &ecryptfs_auth_tok_list_item_cache,
|
||||
.name = "ecryptfs_auth_tok_list_item",
|
||||
.size = sizeof(struct ecryptfs_auth_tok_list_item),
|
||||
},
|
||||
{
|
||||
.cache = &ecryptfs_file_info_cache,
|
||||
.name = "ecryptfs_file_cache",
|
||||
.size = sizeof(struct ecryptfs_file_info),
|
||||
},
|
||||
{
|
||||
.cache = &ecryptfs_dentry_info_cache,
|
||||
.name = "ecryptfs_dentry_info_cache",
|
||||
.size = sizeof(struct ecryptfs_dentry_info),
|
||||
},
|
||||
{
|
||||
.cache = &ecryptfs_inode_info_cache,
|
||||
.name = "ecryptfs_inode_cache",
|
||||
.size = sizeof(struct ecryptfs_inode_info),
|
||||
.ctor = inode_info_init_once,
|
||||
},
|
||||
{
|
||||
.cache = &ecryptfs_sb_info_cache,
|
||||
.name = "ecryptfs_sb_cache",
|
||||
.size = sizeof(struct ecryptfs_sb_info),
|
||||
},
|
||||
{
|
||||
.cache = &ecryptfs_header_cache_0,
|
||||
.name = "ecryptfs_headers_0",
|
||||
.size = PAGE_CACHE_SIZE,
|
||||
},
|
||||
{
|
||||
.cache = &ecryptfs_header_cache_1,
|
||||
.name = "ecryptfs_headers_1",
|
||||
.size = PAGE_CACHE_SIZE,
|
||||
},
|
||||
{
|
||||
.cache = &ecryptfs_header_cache_2,
|
||||
.name = "ecryptfs_headers_2",
|
||||
.size = PAGE_CACHE_SIZE,
|
||||
},
|
||||
{
|
||||
.cache = &ecryptfs_lower_page_cache,
|
||||
.name = "ecryptfs_lower_page_cache",
|
||||
.size = PAGE_CACHE_SIZE,
|
||||
},
|
||||
};
|
||||
|
||||
static void ecryptfs_free_kmem_caches(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ecryptfs_cache_infos); i++) {
|
||||
struct ecryptfs_cache_info *info;
|
||||
|
||||
info = &ecryptfs_cache_infos[i];
|
||||
if (*(info->cache))
|
||||
kmem_cache_destroy(*(info->cache));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_init_kmem_caches
|
||||
*
|
||||
* Returns zero on success; non-zero otherwise
|
||||
*/
|
||||
static int ecryptfs_init_kmem_caches(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ecryptfs_cache_infos); i++) {
|
||||
struct ecryptfs_cache_info *info;
|
||||
|
||||
info = &ecryptfs_cache_infos[i];
|
||||
*(info->cache) = kmem_cache_create(info->name, info->size,
|
||||
0, SLAB_HWCACHE_ALIGN, info->ctor, NULL);
|
||||
if (!*(info->cache)) {
|
||||
ecryptfs_free_kmem_caches();
|
||||
ecryptfs_printk(KERN_WARNING, "%s: "
|
||||
"kmem_cache_create failed\n",
|
||||
info->name);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ecryptfs_obj {
|
||||
char *name;
|
||||
struct list_head slot_list;
|
||||
struct kobject kobj;
|
||||
};
|
||||
|
||||
struct ecryptfs_attribute {
|
||||
struct attribute attr;
|
||||
ssize_t(*show) (struct ecryptfs_obj *, char *);
|
||||
ssize_t(*store) (struct ecryptfs_obj *, const char *, size_t);
|
||||
};
|
||||
|
||||
static ssize_t
|
||||
ecryptfs_attr_store(struct kobject *kobj,
|
||||
struct attribute *attr, const char *buf, size_t len)
|
||||
{
|
||||
struct ecryptfs_obj *obj = container_of(kobj, struct ecryptfs_obj,
|
||||
kobj);
|
||||
struct ecryptfs_attribute *attribute =
|
||||
container_of(attr, struct ecryptfs_attribute, attr);
|
||||
|
||||
return (attribute->store ? attribute->store(obj, buf, len) : 0);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
ecryptfs_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
|
||||
{
|
||||
struct ecryptfs_obj *obj = container_of(kobj, struct ecryptfs_obj,
|
||||
kobj);
|
||||
struct ecryptfs_attribute *attribute =
|
||||
container_of(attr, struct ecryptfs_attribute, attr);
|
||||
|
||||
return (attribute->show ? attribute->show(obj, buf) : 0);
|
||||
}
|
||||
|
||||
static struct sysfs_ops ecryptfs_sysfs_ops = {
|
||||
.show = ecryptfs_attr_show,
|
||||
.store = ecryptfs_attr_store
|
||||
};
|
||||
|
||||
static struct kobj_type ecryptfs_ktype = {
|
||||
.sysfs_ops = &ecryptfs_sysfs_ops
|
||||
};
|
||||
|
||||
static decl_subsys(ecryptfs, &ecryptfs_ktype, NULL);
|
||||
|
||||
static ssize_t version_show(struct ecryptfs_obj *obj, char *buff)
|
||||
{
|
||||
return snprintf(buff, PAGE_SIZE, "%d\n", ECRYPTFS_VERSIONING_MASK);
|
||||
}
|
||||
|
||||
static struct ecryptfs_attribute sysfs_attr_version = __ATTR_RO(version);
|
||||
|
||||
struct ecryptfs_version_str_map_elem {
|
||||
u32 flag;
|
||||
char *str;
|
||||
} ecryptfs_version_str_map[] = {
|
||||
{ECRYPTFS_VERSIONING_PASSPHRASE, "passphrase"},
|
||||
{ECRYPTFS_VERSIONING_PUBKEY, "pubkey"},
|
||||
{ECRYPTFS_VERSIONING_PLAINTEXT_PASSTHROUGH, "plaintext passthrough"},
|
||||
{ECRYPTFS_VERSIONING_POLICY, "policy"}
|
||||
};
|
||||
|
||||
static ssize_t version_str_show(struct ecryptfs_obj *obj, char *buff)
|
||||
{
|
||||
int i;
|
||||
int remaining = PAGE_SIZE;
|
||||
int total_written = 0;
|
||||
|
||||
buff[0] = '\0';
|
||||
for (i = 0; i < ARRAY_SIZE(ecryptfs_version_str_map); i++) {
|
||||
int entry_size;
|
||||
|
||||
if (!(ECRYPTFS_VERSIONING_MASK
|
||||
& ecryptfs_version_str_map[i].flag))
|
||||
continue;
|
||||
entry_size = strlen(ecryptfs_version_str_map[i].str);
|
||||
if ((entry_size + 2) > remaining)
|
||||
goto out;
|
||||
memcpy(buff, ecryptfs_version_str_map[i].str, entry_size);
|
||||
buff[entry_size++] = '\n';
|
||||
buff[entry_size] = '\0';
|
||||
buff += entry_size;
|
||||
total_written += entry_size;
|
||||
remaining -= entry_size;
|
||||
}
|
||||
out:
|
||||
return total_written;
|
||||
}
|
||||
|
||||
static struct ecryptfs_attribute sysfs_attr_version_str = __ATTR_RO(version_str);
|
||||
|
||||
static int do_sysfs_registration(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if ((rc = subsystem_register(&ecryptfs_subsys))) {
|
||||
printk(KERN_ERR
|
||||
"Unable to register ecryptfs sysfs subsystem\n");
|
||||
goto out;
|
||||
}
|
||||
rc = sysfs_create_file(&ecryptfs_subsys.kset.kobj,
|
||||
&sysfs_attr_version.attr);
|
||||
if (rc) {
|
||||
printk(KERN_ERR
|
||||
"Unable to create ecryptfs version attribute\n");
|
||||
subsystem_unregister(&ecryptfs_subsys);
|
||||
goto out;
|
||||
}
|
||||
rc = sysfs_create_file(&ecryptfs_subsys.kset.kobj,
|
||||
&sysfs_attr_version_str.attr);
|
||||
if (rc) {
|
||||
printk(KERN_ERR
|
||||
"Unable to create ecryptfs version_str attribute\n");
|
||||
sysfs_remove_file(&ecryptfs_subsys.kset.kobj,
|
||||
&sysfs_attr_version.attr);
|
||||
subsystem_unregister(&ecryptfs_subsys);
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int __init ecryptfs_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (ECRYPTFS_DEFAULT_EXTENT_SIZE > PAGE_CACHE_SIZE) {
|
||||
rc = -EINVAL;
|
||||
ecryptfs_printk(KERN_ERR, "The eCryptfs extent size is "
|
||||
"larger than the host's page size, and so "
|
||||
"eCryptfs cannot run on this system. The "
|
||||
"default eCryptfs extent size is [%d] bytes; "
|
||||
"the page size is [%d] bytes.\n",
|
||||
ECRYPTFS_DEFAULT_EXTENT_SIZE, PAGE_CACHE_SIZE);
|
||||
goto out;
|
||||
}
|
||||
rc = ecryptfs_init_kmem_caches();
|
||||
if (rc) {
|
||||
printk(KERN_ERR
|
||||
"Failed to allocate one or more kmem_cache objects\n");
|
||||
goto out;
|
||||
}
|
||||
rc = register_filesystem(&ecryptfs_fs_type);
|
||||
if (rc) {
|
||||
printk(KERN_ERR "Failed to register filesystem\n");
|
||||
ecryptfs_free_kmem_caches();
|
||||
goto out;
|
||||
}
|
||||
kset_set_kset_s(&ecryptfs_subsys, fs_subsys);
|
||||
sysfs_attr_version.attr.owner = THIS_MODULE;
|
||||
sysfs_attr_version_str.attr.owner = THIS_MODULE;
|
||||
rc = do_sysfs_registration();
|
||||
if (rc) {
|
||||
printk(KERN_ERR "sysfs registration failed\n");
|
||||
unregister_filesystem(&ecryptfs_fs_type);
|
||||
ecryptfs_free_kmem_caches();
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void __exit ecryptfs_exit(void)
|
||||
{
|
||||
sysfs_remove_file(&ecryptfs_subsys.kset.kobj,
|
||||
&sysfs_attr_version.attr);
|
||||
sysfs_remove_file(&ecryptfs_subsys.kset.kobj,
|
||||
&sysfs_attr_version_str.attr);
|
||||
subsystem_unregister(&ecryptfs_subsys);
|
||||
unregister_filesystem(&ecryptfs_fs_type);
|
||||
ecryptfs_free_kmem_caches();
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Michael A. Halcrow <mhalcrow@us.ibm.com>");
|
||||
MODULE_DESCRIPTION("eCryptfs");
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(ecryptfs_init)
|
||||
module_exit(ecryptfs_exit)
|
788
fs/ecryptfs/mmap.c
Normal file
788
fs/ecryptfs/mmap.c
Normal file
@ -0,0 +1,788 @@
|
||||
/**
|
||||
* eCryptfs: Linux filesystem encryption layer
|
||||
* This is where eCryptfs coordinates the symmetric encryption and
|
||||
* decryption of the file data as it passes between the lower
|
||||
* encrypted file and the upper decrypted file.
|
||||
*
|
||||
* Copyright (C) 1997-2003 Erez Zadok
|
||||
* Copyright (C) 2001-2003 Stony Brook University
|
||||
* Copyright (C) 2004-2006 International Business Machines Corp.
|
||||
* Author(s): Michael A. Halcrow <mahalcro@us.ibm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/writeback.h>
|
||||
#include <linux/page-flags.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include "ecryptfs_kernel.h"
|
||||
|
||||
struct kmem_cache *ecryptfs_lower_page_cache;
|
||||
|
||||
/**
|
||||
* ecryptfs_get1page
|
||||
*
|
||||
* Get one page from cache or lower f/s, return error otherwise.
|
||||
*
|
||||
* Returns unlocked and up-to-date page (if ok), with increased
|
||||
* refcnt.
|
||||
*/
|
||||
static struct page *ecryptfs_get1page(struct file *file, int index)
|
||||
{
|
||||
struct page *page;
|
||||
struct dentry *dentry;
|
||||
struct inode *inode;
|
||||
struct address_space *mapping;
|
||||
|
||||
dentry = file->f_dentry;
|
||||
inode = dentry->d_inode;
|
||||
mapping = inode->i_mapping;
|
||||
page = read_cache_page(mapping, index,
|
||||
(filler_t *)mapping->a_ops->readpage,
|
||||
(void *)file);
|
||||
if (IS_ERR(page))
|
||||
goto out;
|
||||
wait_on_page_locked(page);
|
||||
out:
|
||||
return page;
|
||||
}
|
||||
|
||||
static
|
||||
int write_zeros(struct file *file, pgoff_t index, int start, int num_zeros);
|
||||
|
||||
/**
|
||||
* ecryptfs_fill_zeros
|
||||
* @file: The ecryptfs file
|
||||
* @new_length: The new length of the data in the underlying file;
|
||||
* everything between the prior end of the file and the
|
||||
* new end of the file will be filled with zero's.
|
||||
* new_length must be greater than current length
|
||||
*
|
||||
* Function for handling lseek-ing past the end of the file.
|
||||
*
|
||||
* This function does not support shrinking, only growing a file.
|
||||
*
|
||||
* Returns zero on success; non-zero otherwise.
|
||||
*/
|
||||
int ecryptfs_fill_zeros(struct file *file, loff_t new_length)
|
||||
{
|
||||
int rc = 0;
|
||||
struct dentry *dentry = file->f_dentry;
|
||||
struct inode *inode = dentry->d_inode;
|
||||
pgoff_t old_end_page_index = 0;
|
||||
pgoff_t index = old_end_page_index;
|
||||
int old_end_pos_in_page = -1;
|
||||
pgoff_t new_end_page_index;
|
||||
int new_end_pos_in_page;
|
||||
loff_t cur_length = i_size_read(inode);
|
||||
|
||||
if (cur_length != 0) {
|
||||
index = old_end_page_index =
|
||||
((cur_length - 1) >> PAGE_CACHE_SHIFT);
|
||||
old_end_pos_in_page = ((cur_length - 1) & ~PAGE_CACHE_MASK);
|
||||
}
|
||||
new_end_page_index = ((new_length - 1) >> PAGE_CACHE_SHIFT);
|
||||
new_end_pos_in_page = ((new_length - 1) & ~PAGE_CACHE_MASK);
|
||||
ecryptfs_printk(KERN_DEBUG, "old_end_page_index = [0x%.16x]; "
|
||||
"old_end_pos_in_page = [%d]; "
|
||||
"new_end_page_index = [0x%.16x]; "
|
||||
"new_end_pos_in_page = [%d]\n",
|
||||
old_end_page_index, old_end_pos_in_page,
|
||||
new_end_page_index, new_end_pos_in_page);
|
||||
if (old_end_page_index == new_end_page_index) {
|
||||
/* Start and end are in the same page; we just need to
|
||||
* set a portion of the existing page to zero's */
|
||||
rc = write_zeros(file, index, (old_end_pos_in_page + 1),
|
||||
(new_end_pos_in_page - old_end_pos_in_page));
|
||||
if (rc)
|
||||
ecryptfs_printk(KERN_ERR, "write_zeros(file=[%p], "
|
||||
"index=[0x%.16x], "
|
||||
"old_end_pos_in_page=[d], "
|
||||
"(PAGE_CACHE_SIZE - new_end_pos_in_page"
|
||||
"=[%d]"
|
||||
")=[d]) returned [%d]\n", file, index,
|
||||
old_end_pos_in_page,
|
||||
new_end_pos_in_page,
|
||||
(PAGE_CACHE_SIZE - new_end_pos_in_page),
|
||||
rc);
|
||||
goto out;
|
||||
}
|
||||
/* Fill the remainder of the previous last page with zeros */
|
||||
rc = write_zeros(file, index, (old_end_pos_in_page + 1),
|
||||
((PAGE_CACHE_SIZE - 1) - old_end_pos_in_page));
|
||||
if (rc) {
|
||||
ecryptfs_printk(KERN_ERR, "write_zeros(file=[%p], "
|
||||
"index=[0x%.16x], old_end_pos_in_page=[d], "
|
||||
"(PAGE_CACHE_SIZE - old_end_pos_in_page)=[d]) "
|
||||
"returned [%d]\n", file, index,
|
||||
old_end_pos_in_page,
|
||||
(PAGE_CACHE_SIZE - old_end_pos_in_page), rc);
|
||||
goto out;
|
||||
}
|
||||
index++;
|
||||
while (index < new_end_page_index) {
|
||||
/* Fill all intermediate pages with zeros */
|
||||
rc = write_zeros(file, index, 0, PAGE_CACHE_SIZE);
|
||||
if (rc) {
|
||||
ecryptfs_printk(KERN_ERR, "write_zeros(file=[%p], "
|
||||
"index=[0x%.16x], "
|
||||
"old_end_pos_in_page=[d], "
|
||||
"(PAGE_CACHE_SIZE - new_end_pos_in_page"
|
||||
"=[%d]"
|
||||
")=[d]) returned [%d]\n", file, index,
|
||||
old_end_pos_in_page,
|
||||
new_end_pos_in_page,
|
||||
(PAGE_CACHE_SIZE - new_end_pos_in_page),
|
||||
rc);
|
||||
goto out;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
/* Fill the portion at the beginning of the last new page with
|
||||
* zero's */
|
||||
rc = write_zeros(file, index, 0, (new_end_pos_in_page + 1));
|
||||
if (rc) {
|
||||
ecryptfs_printk(KERN_ERR, "write_zeros(file="
|
||||
"[%p], index=[0x%.16x], 0, "
|
||||
"new_end_pos_in_page=[%d]"
|
||||
"returned [%d]\n", file, index,
|
||||
new_end_pos_in_page, rc);
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_writepage
|
||||
* @page: Page that is locked before this call is made
|
||||
*
|
||||
* Returns zero on success; non-zero otherwise
|
||||
*/
|
||||
static int ecryptfs_writepage(struct page *page, struct writeback_control *wbc)
|
||||
{
|
||||
struct ecryptfs_page_crypt_context ctx;
|
||||
int rc;
|
||||
|
||||
ctx.page = page;
|
||||
ctx.mode = ECRYPTFS_WRITEPAGE_MODE;
|
||||
ctx.param.wbc = wbc;
|
||||
rc = ecryptfs_encrypt_page(&ctx);
|
||||
if (rc) {
|
||||
ecryptfs_printk(KERN_WARNING, "Error encrypting "
|
||||
"page (upper index [0x%.16x])\n", page->index);
|
||||
ClearPageUptodate(page);
|
||||
goto out;
|
||||
}
|
||||
SetPageUptodate(page);
|
||||
unlock_page(page);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the data from the lower file file at index lower_page_index
|
||||
* and copies that data into page.
|
||||
*
|
||||
* @param page Page to fill
|
||||
* @param lower_page_index Index of the page in the lower file to get
|
||||
*/
|
||||
int ecryptfs_do_readpage(struct file *file, struct page *page,
|
||||
pgoff_t lower_page_index)
|
||||
{
|
||||
int rc;
|
||||
struct dentry *dentry;
|
||||
struct file *lower_file;
|
||||
struct dentry *lower_dentry;
|
||||
struct inode *inode;
|
||||
struct inode *lower_inode;
|
||||
char *page_data;
|
||||
struct page *lower_page = NULL;
|
||||
char *lower_page_data;
|
||||
const struct address_space_operations *lower_a_ops;
|
||||
|
||||
dentry = file->f_dentry;
|
||||
lower_file = ecryptfs_file_to_lower(file);
|
||||
lower_dentry = ecryptfs_dentry_to_lower(dentry);
|
||||
inode = dentry->d_inode;
|
||||
lower_inode = ecryptfs_inode_to_lower(inode);
|
||||
lower_a_ops = lower_inode->i_mapping->a_ops;
|
||||
lower_page = read_cache_page(lower_inode->i_mapping, lower_page_index,
|
||||
(filler_t *)lower_a_ops->readpage,
|
||||
(void *)lower_file);
|
||||
if (IS_ERR(lower_page)) {
|
||||
rc = PTR_ERR(lower_page);
|
||||
lower_page = NULL;
|
||||
ecryptfs_printk(KERN_ERR, "Error reading from page cache\n");
|
||||
goto out;
|
||||
}
|
||||
wait_on_page_locked(lower_page);
|
||||
page_data = (char *)kmap(page);
|
||||
if (!page_data) {
|
||||
rc = -ENOMEM;
|
||||
ecryptfs_printk(KERN_ERR, "Error mapping page\n");
|
||||
goto out;
|
||||
}
|
||||
lower_page_data = (char *)kmap(lower_page);
|
||||
if (!lower_page_data) {
|
||||
rc = -ENOMEM;
|
||||
ecryptfs_printk(KERN_ERR, "Error mapping page\n");
|
||||
kunmap(page);
|
||||
goto out;
|
||||
}
|
||||
memcpy(page_data, lower_page_data, PAGE_CACHE_SIZE);
|
||||
kunmap(lower_page);
|
||||
kunmap(page);
|
||||
rc = 0;
|
||||
out:
|
||||
if (likely(lower_page))
|
||||
page_cache_release(lower_page);
|
||||
if (rc == 0)
|
||||
SetPageUptodate(page);
|
||||
else
|
||||
ClearPageUptodate(page);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_readpage
|
||||
* @file: This is an ecryptfs file
|
||||
* @page: ecryptfs associated page to stick the read data into
|
||||
*
|
||||
* Read in a page, decrypting if necessary.
|
||||
*
|
||||
* Returns zero on success; non-zero on error.
|
||||
*/
|
||||
static int ecryptfs_readpage(struct file *file, struct page *page)
|
||||
{
|
||||
int rc = 0;
|
||||
struct ecryptfs_crypt_stat *crypt_stat;
|
||||
|
||||
BUG_ON(!(file && file->f_dentry && file->f_dentry->d_inode));
|
||||
crypt_stat =
|
||||
&ecryptfs_inode_to_private(file->f_dentry->d_inode)->crypt_stat;
|
||||
if (!crypt_stat
|
||||
|| !ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_ENCRYPTED)
|
||||
|| ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_NEW_FILE)) {
|
||||
ecryptfs_printk(KERN_DEBUG,
|
||||
"Passing through unencrypted page\n");
|
||||
rc = ecryptfs_do_readpage(file, page, page->index);
|
||||
if (rc) {
|
||||
ecryptfs_printk(KERN_ERR, "Error reading page; rc = "
|
||||
"[%d]\n", rc);
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
rc = ecryptfs_decrypt_page(file, page);
|
||||
if (rc) {
|
||||
|
||||
ecryptfs_printk(KERN_ERR, "Error decrypting page; "
|
||||
"rc = [%d]\n", rc);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
SetPageUptodate(page);
|
||||
out:
|
||||
if (rc)
|
||||
ClearPageUptodate(page);
|
||||
ecryptfs_printk(KERN_DEBUG, "Unlocking page with index = [0x%.16x]\n",
|
||||
page->index);
|
||||
unlock_page(page);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int fill_zeros_to_end_of_page(struct page *page, unsigned int to)
|
||||
{
|
||||
struct inode *inode = page->mapping->host;
|
||||
int end_byte_in_page;
|
||||
int rc = 0;
|
||||
char *page_virt;
|
||||
|
||||
if ((i_size_read(inode) / PAGE_CACHE_SIZE) == page->index) {
|
||||
end_byte_in_page = i_size_read(inode) % PAGE_CACHE_SIZE;
|
||||
if (to > end_byte_in_page)
|
||||
end_byte_in_page = to;
|
||||
page_virt = kmap(page);
|
||||
if (!page_virt) {
|
||||
rc = -ENOMEM;
|
||||
ecryptfs_printk(KERN_WARNING,
|
||||
"Could not map page\n");
|
||||
goto out;
|
||||
}
|
||||
memset((page_virt + end_byte_in_page), 0,
|
||||
(PAGE_CACHE_SIZE - end_byte_in_page));
|
||||
kunmap(page);
|
||||
}
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int ecryptfs_prepare_write(struct file *file, struct page *page,
|
||||
unsigned from, unsigned to)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
kmap(page);
|
||||
if (from == 0 && to == PAGE_CACHE_SIZE)
|
||||
goto out; /* If we are writing a full page, it will be
|
||||
up to date. */
|
||||
if (!PageUptodate(page))
|
||||
rc = ecryptfs_do_readpage(file, page, page->index);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
int ecryptfs_grab_and_map_lower_page(struct page **lower_page,
|
||||
char **lower_virt,
|
||||
struct inode *lower_inode,
|
||||
unsigned long lower_page_index)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
(*lower_page) = grab_cache_page(lower_inode->i_mapping,
|
||||
lower_page_index);
|
||||
if (!(*lower_page)) {
|
||||
ecryptfs_printk(KERN_ERR, "grab_cache_page for "
|
||||
"lower_page_index = [0x%.16x] failed\n",
|
||||
lower_page_index);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (lower_virt)
|
||||
(*lower_virt) = kmap((*lower_page));
|
||||
else
|
||||
kmap((*lower_page));
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
int ecryptfs_writepage_and_release_lower_page(struct page *lower_page,
|
||||
struct inode *lower_inode,
|
||||
struct writeback_control *wbc)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
rc = lower_inode->i_mapping->a_ops->writepage(lower_page, wbc);
|
||||
if (rc) {
|
||||
ecryptfs_printk(KERN_ERR, "Error calling lower writepage(); "
|
||||
"rc = [%d]\n", rc);
|
||||
goto out;
|
||||
}
|
||||
lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME;
|
||||
page_cache_release(lower_page);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void ecryptfs_unmap_and_release_lower_page(struct page *lower_page)
|
||||
{
|
||||
kunmap(lower_page);
|
||||
ecryptfs_printk(KERN_DEBUG, "Unlocking lower page with index = "
|
||||
"[0x%.16x]\n", lower_page->index);
|
||||
unlock_page(lower_page);
|
||||
page_cache_release(lower_page);
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_write_inode_size_to_header
|
||||
*
|
||||
* Writes the lower file size to the first 8 bytes of the header.
|
||||
*
|
||||
* Returns zero on success; non-zero on error.
|
||||
*/
|
||||
int
|
||||
ecryptfs_write_inode_size_to_header(struct file *lower_file,
|
||||
struct inode *lower_inode,
|
||||
struct inode *inode)
|
||||
{
|
||||
int rc = 0;
|
||||
struct page *header_page;
|
||||
char *header_virt;
|
||||
const struct address_space_operations *lower_a_ops;
|
||||
u64 file_size;
|
||||
|
||||
rc = ecryptfs_grab_and_map_lower_page(&header_page, &header_virt,
|
||||
lower_inode, 0);
|
||||
if (rc) {
|
||||
ecryptfs_printk(KERN_ERR, "grab_cache_page for header page "
|
||||
"failed\n");
|
||||
goto out;
|
||||
}
|
||||
lower_a_ops = lower_inode->i_mapping->a_ops;
|
||||
rc = lower_a_ops->prepare_write(lower_file, header_page, 0, 8);
|
||||
file_size = (u64)i_size_read(inode);
|
||||
ecryptfs_printk(KERN_DEBUG, "Writing size: [0x%.16x]\n", file_size);
|
||||
file_size = cpu_to_be64(file_size);
|
||||
memcpy(header_virt, &file_size, sizeof(u64));
|
||||
rc = lower_a_ops->commit_write(lower_file, header_page, 0, 8);
|
||||
if (rc < 0)
|
||||
ecryptfs_printk(KERN_ERR, "Error commiting header page "
|
||||
"write\n");
|
||||
ecryptfs_unmap_and_release_lower_page(header_page);
|
||||
lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME;
|
||||
mark_inode_dirty_sync(inode);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
int ecryptfs_get_lower_page(struct page **lower_page, struct inode *lower_inode,
|
||||
struct file *lower_file,
|
||||
unsigned long lower_page_index, int byte_offset,
|
||||
int region_bytes)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
rc = ecryptfs_grab_and_map_lower_page(lower_page, NULL, lower_inode,
|
||||
lower_page_index);
|
||||
if (rc) {
|
||||
ecryptfs_printk(KERN_ERR, "Error attempting to grab and map "
|
||||
"lower page with index [0x%.16x]\n",
|
||||
lower_page_index);
|
||||
goto out;
|
||||
}
|
||||
rc = lower_inode->i_mapping->a_ops->prepare_write(lower_file,
|
||||
(*lower_page),
|
||||
byte_offset,
|
||||
region_bytes);
|
||||
if (rc) {
|
||||
ecryptfs_printk(KERN_ERR, "prepare_write for "
|
||||
"lower_page_index = [0x%.16x] failed; rc = "
|
||||
"[%d]\n", lower_page_index, rc);
|
||||
}
|
||||
out:
|
||||
if (rc && (*lower_page)) {
|
||||
ecryptfs_unmap_and_release_lower_page(*lower_page);
|
||||
(*lower_page) = NULL;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_commit_lower_page
|
||||
*
|
||||
* Returns zero on success; non-zero on error
|
||||
*/
|
||||
int
|
||||
ecryptfs_commit_lower_page(struct page *lower_page, struct inode *lower_inode,
|
||||
struct file *lower_file, int byte_offset,
|
||||
int region_size)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
rc = lower_inode->i_mapping->a_ops->commit_write(
|
||||
lower_file, lower_page, byte_offset, region_size);
|
||||
if (rc < 0) {
|
||||
ecryptfs_printk(KERN_ERR,
|
||||
"Error committing write; rc = [%d]\n", rc);
|
||||
} else
|
||||
rc = 0;
|
||||
ecryptfs_unmap_and_release_lower_page(lower_page);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_copy_page_to_lower
|
||||
*
|
||||
* Used for plaintext pass-through; no page index interpolation
|
||||
* required.
|
||||
*/
|
||||
int ecryptfs_copy_page_to_lower(struct page *page, struct inode *lower_inode,
|
||||
struct file *lower_file)
|
||||
{
|
||||
int rc = 0;
|
||||
struct page *lower_page;
|
||||
|
||||
rc = ecryptfs_get_lower_page(&lower_page, lower_inode, lower_file,
|
||||
page->index, 0, PAGE_CACHE_SIZE);
|
||||
if (rc) {
|
||||
ecryptfs_printk(KERN_ERR, "Error attempting to get page "
|
||||
"at index [0x%.16x]\n", page->index);
|
||||
goto out;
|
||||
}
|
||||
/* TODO: aops */
|
||||
memcpy((char *)page_address(lower_page), page_address(page),
|
||||
PAGE_CACHE_SIZE);
|
||||
rc = ecryptfs_commit_lower_page(lower_page, lower_inode, lower_file,
|
||||
0, PAGE_CACHE_SIZE);
|
||||
if (rc)
|
||||
ecryptfs_printk(KERN_ERR, "Error attempting to commit page "
|
||||
"at index [0x%.16x]\n", page->index);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
process_new_file(struct ecryptfs_crypt_stat *crypt_stat,
|
||||
struct file *file, struct inode *inode)
|
||||
{
|
||||
struct page *header_page;
|
||||
const struct address_space_operations *lower_a_ops;
|
||||
struct inode *lower_inode;
|
||||
struct file *lower_file;
|
||||
char *header_virt;
|
||||
int rc = 0;
|
||||
int current_header_page = 0;
|
||||
int header_pages;
|
||||
int more_header_data_to_be_written = 1;
|
||||
|
||||
lower_inode = ecryptfs_inode_to_lower(inode);
|
||||
lower_file = ecryptfs_file_to_lower(file);
|
||||
lower_a_ops = lower_inode->i_mapping->a_ops;
|
||||
header_pages = ((crypt_stat->header_extent_size
|
||||
* crypt_stat->num_header_extents_at_front)
|
||||
/ PAGE_CACHE_SIZE);
|
||||
BUG_ON(header_pages < 1);
|
||||
while (current_header_page < header_pages) {
|
||||
rc = ecryptfs_grab_and_map_lower_page(&header_page,
|
||||
&header_virt,
|
||||
lower_inode,
|
||||
current_header_page);
|
||||
if (rc) {
|
||||
ecryptfs_printk(KERN_ERR, "grab_cache_page for "
|
||||
"header page [%d] failed; rc = [%d]\n",
|
||||
current_header_page, rc);
|
||||
goto out;
|
||||
}
|
||||
rc = lower_a_ops->prepare_write(lower_file, header_page, 0,
|
||||
PAGE_CACHE_SIZE);
|
||||
if (rc) {
|
||||
ecryptfs_printk(KERN_ERR, "Error preparing to write "
|
||||
"header page out; rc = [%d]\n", rc);
|
||||
goto out;
|
||||
}
|
||||
memset(header_virt, 0, PAGE_CACHE_SIZE);
|
||||
if (more_header_data_to_be_written) {
|
||||
rc = ecryptfs_write_headers_virt(header_virt,
|
||||
crypt_stat,
|
||||
file->f_dentry);
|
||||
if (rc) {
|
||||
ecryptfs_printk(KERN_WARNING, "Error "
|
||||
"generating header; rc = "
|
||||
"[%d]\n", rc);
|
||||
rc = -EIO;
|
||||
memset(header_virt, 0, PAGE_CACHE_SIZE);
|
||||
ecryptfs_unmap_and_release_lower_page(
|
||||
header_page);
|
||||
goto out;
|
||||
}
|
||||
if (current_header_page == 0)
|
||||
memset(header_virt, 0, 8);
|
||||
more_header_data_to_be_written = 0;
|
||||
}
|
||||
rc = lower_a_ops->commit_write(lower_file, header_page, 0,
|
||||
PAGE_CACHE_SIZE);
|
||||
ecryptfs_unmap_and_release_lower_page(header_page);
|
||||
if (rc < 0) {
|
||||
ecryptfs_printk(KERN_ERR,
|
||||
"Error commiting header page write; "
|
||||
"rc = [%d]\n", rc);
|
||||
break;
|
||||
}
|
||||
current_header_page++;
|
||||
}
|
||||
if (rc >= 0) {
|
||||
rc = 0;
|
||||
ecryptfs_printk(KERN_DEBUG, "lower_inode->i_blocks = "
|
||||
"[0x%.16x]\n", lower_inode->i_blocks);
|
||||
i_size_write(inode, 0);
|
||||
lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME;
|
||||
mark_inode_dirty_sync(inode);
|
||||
}
|
||||
ecryptfs_printk(KERN_DEBUG, "Clearing ECRYPTFS_NEW_FILE flag in "
|
||||
"crypt_stat at memory location [%p]\n", crypt_stat);
|
||||
ECRYPTFS_CLEAR_FLAG(crypt_stat->flags, ECRYPTFS_NEW_FILE);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_commit_write
|
||||
* @file: The eCryptfs file object
|
||||
* @page: The eCryptfs page
|
||||
* @from: Ignored (we rotate the page IV on each write)
|
||||
* @to: Ignored
|
||||
*
|
||||
* This is where we encrypt the data and pass the encrypted data to
|
||||
* the lower filesystem. In OpenPGP-compatible mode, we operate on
|
||||
* entire underlying packets.
|
||||
*/
|
||||
static int ecryptfs_commit_write(struct file *file, struct page *page,
|
||||
unsigned from, unsigned to)
|
||||
{
|
||||
struct ecryptfs_page_crypt_context ctx;
|
||||
loff_t pos;
|
||||
struct inode *inode;
|
||||
struct inode *lower_inode;
|
||||
struct file *lower_file;
|
||||
struct ecryptfs_crypt_stat *crypt_stat;
|
||||
int rc;
|
||||
|
||||
inode = page->mapping->host;
|
||||
lower_inode = ecryptfs_inode_to_lower(inode);
|
||||
lower_file = ecryptfs_file_to_lower(file);
|
||||
mutex_lock(&lower_inode->i_mutex);
|
||||
crypt_stat =
|
||||
&ecryptfs_inode_to_private(file->f_dentry->d_inode)->crypt_stat;
|
||||
if (ECRYPTFS_CHECK_FLAG(crypt_stat->flags, ECRYPTFS_NEW_FILE)) {
|
||||
ecryptfs_printk(KERN_DEBUG, "ECRYPTFS_NEW_FILE flag set in "
|
||||
"crypt_stat at memory location [%p]\n", crypt_stat);
|
||||
rc = process_new_file(crypt_stat, file, inode);
|
||||
if (rc) {
|
||||
ecryptfs_printk(KERN_ERR, "Error processing new "
|
||||
"file; rc = [%d]\n", rc);
|
||||
goto out;
|
||||
}
|
||||
} else
|
||||
ecryptfs_printk(KERN_DEBUG, "Not a new file\n");
|
||||
ecryptfs_printk(KERN_DEBUG, "Calling fill_zeros_to_end_of_page"
|
||||
"(page w/ index = [0x%.16x], to = [%d])\n", page->index,
|
||||
to);
|
||||
rc = fill_zeros_to_end_of_page(page, to);
|
||||
if (rc) {
|
||||
ecryptfs_printk(KERN_WARNING, "Error attempting to fill "
|
||||
"zeros in page with index = [0x%.16x]\n",
|
||||
page->index);
|
||||
goto out;
|
||||
}
|
||||
ctx.page = page;
|
||||
ctx.mode = ECRYPTFS_PREPARE_COMMIT_MODE;
|
||||
ctx.param.lower_file = lower_file;
|
||||
rc = ecryptfs_encrypt_page(&ctx);
|
||||
if (rc) {
|
||||
ecryptfs_printk(KERN_WARNING, "Error encrypting page (upper "
|
||||
"index [0x%.16x])\n", page->index);
|
||||
goto out;
|
||||
}
|
||||
rc = 0;
|
||||
inode->i_blocks = lower_inode->i_blocks;
|
||||
pos = (page->index << PAGE_CACHE_SHIFT) + to;
|
||||
if (pos > i_size_read(inode)) {
|
||||
i_size_write(inode, pos);
|
||||
ecryptfs_printk(KERN_DEBUG, "Expanded file size to "
|
||||
"[0x%.16x]\n", i_size_read(inode));
|
||||
}
|
||||
ecryptfs_write_inode_size_to_header(lower_file, lower_inode, inode);
|
||||
lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME;
|
||||
mark_inode_dirty_sync(inode);
|
||||
out:
|
||||
kunmap(page); /* mapped in prior call (prepare_write) */
|
||||
if (rc < 0)
|
||||
ClearPageUptodate(page);
|
||||
else
|
||||
SetPageUptodate(page);
|
||||
mutex_unlock(&lower_inode->i_mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* write_zeros
|
||||
* @file: The ecryptfs file
|
||||
* @index: The index in which we are writing
|
||||
* @start: The position after the last block of data
|
||||
* @num_zeros: The number of zeros to write
|
||||
*
|
||||
* Write a specified number of zero's to a page.
|
||||
*
|
||||
* (start + num_zeros) must be less than or equal to PAGE_CACHE_SIZE
|
||||
*/
|
||||
static
|
||||
int write_zeros(struct file *file, pgoff_t index, int start, int num_zeros)
|
||||
{
|
||||
int rc = 0;
|
||||
struct page *tmp_page;
|
||||
|
||||
tmp_page = ecryptfs_get1page(file, index);
|
||||
if (IS_ERR(tmp_page)) {
|
||||
ecryptfs_printk(KERN_ERR, "Error getting page at index "
|
||||
"[0x%.16x]\n", index);
|
||||
rc = PTR_ERR(tmp_page);
|
||||
goto out;
|
||||
}
|
||||
kmap(tmp_page);
|
||||
rc = ecryptfs_prepare_write(file, tmp_page, start, start + num_zeros);
|
||||
if (rc) {
|
||||
ecryptfs_printk(KERN_ERR, "Error preparing to write zero's "
|
||||
"to remainder of page at index [0x%.16x]\n",
|
||||
index);
|
||||
kunmap(tmp_page);
|
||||
page_cache_release(tmp_page);
|
||||
goto out;
|
||||
}
|
||||
memset(((char *)page_address(tmp_page) + start), 0, num_zeros);
|
||||
rc = ecryptfs_commit_write(file, tmp_page, start, start + num_zeros);
|
||||
if (rc < 0) {
|
||||
ecryptfs_printk(KERN_ERR, "Error attempting to write zero's "
|
||||
"to remainder of page at index [0x%.16x]\n",
|
||||
index);
|
||||
kunmap(tmp_page);
|
||||
page_cache_release(tmp_page);
|
||||
goto out;
|
||||
}
|
||||
rc = 0;
|
||||
kunmap(tmp_page);
|
||||
page_cache_release(tmp_page);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static sector_t ecryptfs_bmap(struct address_space *mapping, sector_t block)
|
||||
{
|
||||
int rc = 0;
|
||||
struct inode *inode;
|
||||
struct inode *lower_inode;
|
||||
|
||||
inode = (struct inode *)mapping->host;
|
||||
lower_inode = ecryptfs_inode_to_lower(inode);
|
||||
if (lower_inode->i_mapping->a_ops->bmap)
|
||||
rc = lower_inode->i_mapping->a_ops->bmap(lower_inode->i_mapping,
|
||||
block);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void ecryptfs_sync_page(struct page *page)
|
||||
{
|
||||
struct inode *inode;
|
||||
struct inode *lower_inode;
|
||||
struct page *lower_page;
|
||||
|
||||
inode = page->mapping->host;
|
||||
lower_inode = ecryptfs_inode_to_lower(inode);
|
||||
/* NOTE: Recently swapped with grab_cache_page(), since
|
||||
* sync_page() just makes sure that pending I/O gets done. */
|
||||
lower_page = find_lock_page(lower_inode->i_mapping, page->index);
|
||||
if (!lower_page) {
|
||||
ecryptfs_printk(KERN_DEBUG, "find_lock_page failed\n");
|
||||
return;
|
||||
}
|
||||
lower_page->mapping->a_ops->sync_page(lower_page);
|
||||
ecryptfs_printk(KERN_DEBUG, "Unlocking page with index = [0x%.16x]\n",
|
||||
lower_page->index);
|
||||
unlock_page(lower_page);
|
||||
page_cache_release(lower_page);
|
||||
}
|
||||
|
||||
struct address_space_operations ecryptfs_aops = {
|
||||
.writepage = ecryptfs_writepage,
|
||||
.readpage = ecryptfs_readpage,
|
||||
.prepare_write = ecryptfs_prepare_write,
|
||||
.commit_write = ecryptfs_commit_write,
|
||||
.bmap = ecryptfs_bmap,
|
||||
.sync_page = ecryptfs_sync_page,
|
||||
};
|
198
fs/ecryptfs/super.c
Normal file
198
fs/ecryptfs/super.c
Normal file
@ -0,0 +1,198 @@
|
||||
/**
|
||||
* eCryptfs: Linux filesystem encryption layer
|
||||
*
|
||||
* Copyright (C) 1997-2003 Erez Zadok
|
||||
* Copyright (C) 2001-2003 Stony Brook University
|
||||
* Copyright (C) 2004-2006 International Business Machines Corp.
|
||||
* Author(s): Michael A. Halcrow <mahalcro@us.ibm.com>
|
||||
* Michael C. Thompson <mcthomps@us.ibm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/key.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/crypto.h>
|
||||
#include "ecryptfs_kernel.h"
|
||||
|
||||
struct kmem_cache *ecryptfs_inode_info_cache;
|
||||
|
||||
/**
|
||||
* ecryptfs_alloc_inode - allocate an ecryptfs inode
|
||||
* @sb: Pointer to the ecryptfs super block
|
||||
*
|
||||
* Called to bring an inode into existence.
|
||||
*
|
||||
* Only handle allocation, setting up structures should be done in
|
||||
* ecryptfs_read_inode. This is because the kernel, between now and
|
||||
* then, will 0 out the private data pointer.
|
||||
*
|
||||
* Returns a pointer to a newly allocated inode, NULL otherwise
|
||||
*/
|
||||
static struct inode *ecryptfs_alloc_inode(struct super_block *sb)
|
||||
{
|
||||
struct ecryptfs_inode_info *ecryptfs_inode;
|
||||
struct inode *inode = NULL;
|
||||
|
||||
ecryptfs_inode = kmem_cache_alloc(ecryptfs_inode_info_cache,
|
||||
SLAB_KERNEL);
|
||||
if (unlikely(!ecryptfs_inode))
|
||||
goto out;
|
||||
ecryptfs_init_crypt_stat(&ecryptfs_inode->crypt_stat);
|
||||
inode = &ecryptfs_inode->vfs_inode;
|
||||
out:
|
||||
return inode;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_destroy_inode
|
||||
* @inode: The ecryptfs inode
|
||||
*
|
||||
* This is used during the final destruction of the inode.
|
||||
* All allocation of memory related to the inode, including allocated
|
||||
* memory in the crypt_stat struct, will be released here.
|
||||
* There should be no chance that this deallocation will be missed.
|
||||
*/
|
||||
static void ecryptfs_destroy_inode(struct inode *inode)
|
||||
{
|
||||
struct ecryptfs_inode_info *inode_info;
|
||||
|
||||
inode_info = ecryptfs_inode_to_private(inode);
|
||||
ecryptfs_destruct_crypt_stat(&inode_info->crypt_stat);
|
||||
kmem_cache_free(ecryptfs_inode_info_cache, inode_info);
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_init_inode
|
||||
* @inode: The ecryptfs inode
|
||||
*
|
||||
* Set up the ecryptfs inode.
|
||||
*/
|
||||
void ecryptfs_init_inode(struct inode *inode, struct inode *lower_inode)
|
||||
{
|
||||
ecryptfs_set_inode_lower(inode, lower_inode);
|
||||
inode->i_ino = lower_inode->i_ino;
|
||||
inode->i_version++;
|
||||
inode->i_op = &ecryptfs_main_iops;
|
||||
inode->i_fop = &ecryptfs_main_fops;
|
||||
inode->i_mapping->a_ops = &ecryptfs_aops;
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_put_super
|
||||
* @sb: Pointer to the ecryptfs super block
|
||||
*
|
||||
* Final actions when unmounting a file system.
|
||||
* This will handle deallocation and release of our private data.
|
||||
*/
|
||||
static void ecryptfs_put_super(struct super_block *sb)
|
||||
{
|
||||
struct ecryptfs_sb_info *sb_info = ecryptfs_superblock_to_private(sb);
|
||||
|
||||
ecryptfs_destruct_mount_crypt_stat(&sb_info->mount_crypt_stat);
|
||||
kmem_cache_free(ecryptfs_sb_info_cache, sb_info);
|
||||
ecryptfs_set_superblock_private(sb, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_statfs
|
||||
* @sb: The ecryptfs super block
|
||||
* @buf: The struct kstatfs to fill in with stats
|
||||
*
|
||||
* Get the filesystem statistics. Currently, we let this pass right through
|
||||
* to the lower filesystem and take no action ourselves.
|
||||
*/
|
||||
static int ecryptfs_statfs(struct dentry *dentry, struct kstatfs *buf)
|
||||
{
|
||||
return vfs_statfs(ecryptfs_dentry_to_lower(dentry), buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_clear_inode
|
||||
* @inode - The ecryptfs inode
|
||||
*
|
||||
* Called by iput() when the inode reference count reached zero
|
||||
* and the inode is not hashed anywhere. Used to clear anything
|
||||
* that needs to be, before the inode is completely destroyed and put
|
||||
* on the inode free list. We use this to drop out reference to the
|
||||
* lower inode.
|
||||
*/
|
||||
static void ecryptfs_clear_inode(struct inode *inode)
|
||||
{
|
||||
iput(ecryptfs_inode_to_lower(inode));
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_umount_begin
|
||||
*
|
||||
* Called in do_umount().
|
||||
*/
|
||||
static void ecryptfs_umount_begin(struct vfsmount *vfsmnt, int flags)
|
||||
{
|
||||
struct vfsmount *lower_mnt =
|
||||
ecryptfs_dentry_to_lower_mnt(vfsmnt->mnt_sb->s_root);
|
||||
struct super_block *lower_sb;
|
||||
|
||||
mntput(lower_mnt);
|
||||
lower_sb = lower_mnt->mnt_sb;
|
||||
if (lower_sb->s_op->umount_begin)
|
||||
lower_sb->s_op->umount_begin(lower_mnt, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* ecryptfs_show_options
|
||||
*
|
||||
* Prints the directory we are currently mounted over.
|
||||
* Returns zero on success; non-zero otherwise
|
||||
*/
|
||||
static int ecryptfs_show_options(struct seq_file *m, struct vfsmount *mnt)
|
||||
{
|
||||
struct super_block *sb = mnt->mnt_sb;
|
||||
struct dentry *lower_root_dentry = ecryptfs_dentry_to_lower(sb->s_root);
|
||||
struct vfsmount *lower_mnt = ecryptfs_dentry_to_lower_mnt(sb->s_root);
|
||||
char *tmp_page;
|
||||
char *path;
|
||||
int rc = 0;
|
||||
|
||||
tmp_page = (char *)__get_free_page(GFP_KERNEL);
|
||||
if (!tmp_page) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
path = d_path(lower_root_dentry, lower_mnt, tmp_page, PAGE_SIZE);
|
||||
if (IS_ERR(path)) {
|
||||
rc = PTR_ERR(path);
|
||||
goto out;
|
||||
}
|
||||
seq_printf(m, ",dir=%s", path);
|
||||
free_page((unsigned long)tmp_page);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct super_operations ecryptfs_sops = {
|
||||
.alloc_inode = ecryptfs_alloc_inode,
|
||||
.destroy_inode = ecryptfs_destroy_inode,
|
||||
.drop_inode = generic_delete_inode,
|
||||
.put_super = ecryptfs_put_super,
|
||||
.statfs = ecryptfs_statfs,
|
||||
.remount_fs = NULL,
|
||||
.clear_inode = ecryptfs_clear_inode,
|
||||
.umount_begin = ecryptfs_umount_begin,
|
||||
.show_options = ecryptfs_show_options
|
||||
};
|
Loading…
Reference in New Issue
Block a user