diff -urN oldtree/Documentation/ecryptfs.txt newtree/Documentation/ecryptfs.txt --- oldtree/Documentation/ecryptfs.txt 1970-01-01 00:00:00.000000000 +0000 +++ newtree/Documentation/ecryptfs.txt 2006-02-18 17:24:58.707097720 +0000 @@ -0,0 +1,81 @@ +eCryptfs: A stacked cryptographic filesystem for Linux +Maintainer: Phillip Hellewell +Lead developer: Michael A. Halcrow +Developers: Michael C. Thompson + Kent Yoder +Current Release Version: 0.1 + +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/ + +Requirements include: + - Kernel version 2.6.15-rc1-mm1 or higher + - eCryptfs will work with 2.6.14, but you will need the + export_user_type.patch applied + - David Howells' userspace keyring headers and libraries, obtainable + from http://people.redhat.com/~dhowells/keyutils/ + - (You will need to apply the keyutil_h_fix.diff patch to version 0.3) + - GnuPG Made Easy (GPGME) + - Building the kernel with: + - Cryptographic API + - Blowfish cipher + - Key retention support + - Filesystems->Miscellaneous filesystems->eCryptfs + + +BUILD AND INSTALL INSTRUCTIONS + +If you are installing from the patch set: +1) Apply export_user_type.patch to the kernel if you are running + kernel version 2.6.14 +2) Apply all patches in the patches/ directory to the kernel + +Once eCryptfs is already in the kernel: +3) Select build options (see Requirements) and build kernel +4) Apply keyutil_h_fix.diff to the keyutils if you are running version + 0.3 +5) Run make and make install from the request-key/ directory. + + +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 3 pages (12288 bytes) in 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 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. + +Mike Halcrow +mhalcrow@us.ibm.com diff -urN oldtree/fs/Kconfig newtree/fs/Kconfig --- oldtree/fs/Kconfig 2006-02-18 17:12:40.743285272 +0000 +++ newtree/fs/Kconfig 2006-02-18 17:24:58.710097264 +0000 @@ -937,6 +937,18 @@ To compile this file system support as a module, choose M here: the module will be called affs. If unsure, say N. +config ECRYPTFS + tristate "eCrypt filesystem layer support (EXPERIMENTAL)" + depends on EXPERIMENTAL + select KEYS + select CRYPTO + help + Encrypted filesystem that operates on the VFS layer. See + Documentation/ecryptfs.txt to learn more about eCryptfs. + + 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 EXPERIMENTAL diff -urN oldtree/fs/Kconfig.orig newtree/fs/Kconfig.orig --- oldtree/fs/Kconfig.orig 1970-01-01 00:00:00.000000000 +0000 +++ newtree/fs/Kconfig.orig 2006-02-18 17:12:40.000000000 +0000 @@ -0,0 +1,1833 @@ +# +# File system configuration +# + +menu "File systems" + +config EXT2_FS + tristate "Second extended fs support" + help + Ext2 is a standard Linux file system for hard disks. + + To compile this file system support as a module, choose M here: the + module will be called ext2. Be aware however that the file system + of your root partition (the one containing the directory /) cannot + be compiled as a module, and so this could be dangerous. + + If unsure, say Y. + +config EXT2_FS_XATTR + bool "Ext2 extended attributes" + depends on EXT2_FS + help + Extended attributes are name:value pairs associated with inodes by + the kernel or by users (see the attr(5) manual page, or visit + for details). + + If unsure, say N. + +config EXT2_FS_POSIX_ACL + bool "Ext2 POSIX Access Control Lists" + depends on EXT2_FS_XATTR + select FS_POSIX_ACL + help + Posix Access Control Lists (ACLs) support permissions for users and + groups beyond the owner/group/world scheme. + + To learn more about Access Control Lists, visit the Posix ACLs for + Linux website . + + If you don't know what Access Control Lists are, say N + +config EXT2_FS_SECURITY + bool "Ext2 Security Labels" + depends on EXT2_FS_XATTR + help + Security labels support alternative access control models + implemented by security modules like SELinux. This option + enables an extended attribute handler for file security + labels in the ext2 filesystem. + + If you are not using a security module that requires using + extended attributes for file security labels, say N. + +config EXT2_FS_XIP + bool "Ext2 execute in place support" + depends on EXT2_FS + help + Execute in place can be used on memory-backed block devices. If you + enable this option, you can select to mount block devices which are + capable of this feature without using the page cache. + + If you do not use a block device that is capable of using this, + or if unsure, say N. + +config FS_XIP +# execute in place + bool + depends on EXT2_FS_XIP + default y + +config EXT3_FS + tristate "Ext3 journalling file system support" + select JBD + help + This is the journaling version of the Second extended file system + (often called ext3), the de facto standard Linux file system + (method to organize files on a storage device) for hard disks. + + The journaling code included in this driver means you do not have + to run e2fsck (file system checker) on your file systems after a + crash. The journal keeps track of any changes that were being made + at the time the system crashed, and can ensure that your file system + is consistent without the need for a lengthy check. + + Other than adding the journal to the file system, the on-disk format + of ext3 is identical to ext2. It is possible to freely switch + between using the ext3 driver and the ext2 driver, as long as the + file system has been cleanly unmounted, or e2fsck is run on the file + system. + + To add a journal on an existing ext2 file system or change the + behavior of ext3 file systems, you can use the tune2fs utility ("man + tune2fs"). To modify attributes of files and directories on ext3 + file systems, use chattr ("man chattr"). You need to be using + e2fsprogs version 1.20 or later in order to create ext3 journals + (available at ). + + To compile this file system support as a module, choose M here: the + module will be called ext3. Be aware however that the file system + of your root partition (the one containing the directory /) cannot + be compiled as a module, and so this may be dangerous. + +config EXT3_FS_XATTR + bool "Ext3 extended attributes" + depends on EXT3_FS + default y + help + Extended attributes are name:value pairs associated with inodes by + the kernel or by users (see the attr(5) manual page, or visit + for details). + + If unsure, say N. + + You need this for POSIX ACL support on ext3. + +config EXT3_FS_POSIX_ACL + bool "Ext3 POSIX Access Control Lists" + depends on EXT3_FS_XATTR + select FS_POSIX_ACL + help + Posix Access Control Lists (ACLs) support permissions for users and + groups beyond the owner/group/world scheme. + + To learn more about Access Control Lists, visit the Posix ACLs for + Linux website . + + If you don't know what Access Control Lists are, say N + +config EXT3_FS_SECURITY + bool "Ext3 Security Labels" + depends on EXT3_FS_XATTR + help + Security labels support alternative access control models + implemented by security modules like SELinux. This option + enables an extended attribute handler for file security + labels in the ext3 filesystem. + + If you are not using a security module that requires using + extended attributes for file security labels, say N. + +config JBD + tristate + help + This is a generic journaling layer for block devices. It is + currently used by the ext3 and OCFS2 file systems, but it could + also be used to add journal support to other file systems or block + devices such as RAID or LVM. + + If you are using the ext3 or OCFS2 file systems, you need to + say Y here. If you are not using ext3 OCFS2 then you will probably + want to say N. + + To compile this device as a module, choose M here: the module will be + called jbd. If you are compiling ext3 or OCFS2 into the kernel, + you cannot compile this code as a module. + +config JBD_DEBUG + bool "JBD (ext3) debugging support" + depends on JBD + help + If you are using the ext3 journaled file system (or potentially any + other file system/device using JBD), this option allows you to + enable debugging output while the system is running, in order to + help track down any problems you are having. By default the + debugging output will be turned off. + + If you select Y here, then you will be able to turn on debugging + with "echo N > /proc/sys/fs/jbd-debug", where N is a number between + 1 and 5, the higher the number, the more debugging output is + generated. To turn debugging off again, do + "echo 0 > /proc/sys/fs/jbd-debug". + +config FS_MBCACHE +# Meta block cache for Extended Attributes (ext2/ext3) + tristate + depends on EXT2_FS_XATTR || EXT3_FS_XATTR + default y if EXT2_FS=y || EXT3_FS=y + default m if EXT2_FS=m || EXT3_FS=m + +config REISERFS_FS + tristate "Reiserfs support" + help + Stores not just filenames but the files themselves in a balanced + tree. Uses journaling. + + Balanced trees are more efficient than traditional file system + architectural foundations. + + In general, ReiserFS is as fast as ext2, but is very efficient with + large directories and small files. Additional patches are needed + for NFS and quotas, please see for links. + + It is more easily extended to have features currently found in + database and keyword search systems than block allocation based file + systems are. The next version will be so extended, and will support + plugins consistent with our motto ``It takes more than a license to + make source code open.'' + + Read to learn more about reiserfs. + + Sponsored by Threshold Networks, Emusic.com, and Bigstorage.com. + + If you like it, you can pay us to add new features to it that you + need, buy a support contract, or pay us to port it to another OS. + +config REISERFS_CHECK + bool "Enable reiserfs debug mode" + depends on REISERFS_FS + help + If you set this to Y, then ReiserFS will perform every check it can + possibly imagine of its internal consistency throughout its + operation. It will also go substantially slower. More than once we + have forgotten that this was on, and then gone despondent over the + latest benchmarks.:-) Use of this option allows our team to go all + out in checking for consistency when debugging without fear of its + effect on end users. If you are on the verge of sending in a bug + report, say Y and you might get a useful error message. Almost + everyone should say N. + +config REISERFS_PROC_INFO + bool "Stats in /proc/fs/reiserfs" + depends on REISERFS_FS + help + Create under /proc/fs/reiserfs a hierarchy of files, displaying + various ReiserFS statistics and internal data at the expense of + making your kernel or module slightly larger (+8 KB). This also + increases the amount of kernel memory required for each mount. + Almost everyone but ReiserFS developers and people fine-tuning + reiserfs or tracing problems should say N. + +config REISERFS_FS_XATTR + bool "ReiserFS extended attributes" + depends on REISERFS_FS + help + Extended attributes are name:value pairs associated with inodes by + the kernel or by users (see the attr(5) manual page, or visit + for details). + + If unsure, say N. + +config REISERFS_FS_POSIX_ACL + bool "ReiserFS POSIX Access Control Lists" + depends on REISERFS_FS_XATTR + select FS_POSIX_ACL + help + Posix Access Control Lists (ACLs) support permissions for users and + groups beyond the owner/group/world scheme. + + To learn more about Access Control Lists, visit the Posix ACLs for + Linux website . + + If you don't know what Access Control Lists are, say N + +config REISERFS_FS_SECURITY + bool "ReiserFS Security Labels" + depends on REISERFS_FS_XATTR + help + Security labels support alternative access control models + implemented by security modules like SELinux. This option + enables an extended attribute handler for file security + labels in the ReiserFS filesystem. + + If you are not using a security module that requires using + extended attributes for file security labels, say N. + +config JFS_FS + tristate "JFS filesystem support" + select NLS + help + This is a port of IBM's Journaled Filesystem . More information is + available in the file . + + If you do not intend to use the JFS filesystem, say N. + +config JFS_POSIX_ACL + bool "JFS POSIX Access Control Lists" + depends on JFS_FS + select FS_POSIX_ACL + help + Posix Access Control Lists (ACLs) support permissions for users and + groups beyond the owner/group/world scheme. + + To learn more about Access Control Lists, visit the Posix ACLs for + Linux website . + + If you don't know what Access Control Lists are, say N + +config JFS_SECURITY + bool "JFS Security Labels" + depends on JFS_FS + help + Security labels support alternative access control models + implemented by security modules like SELinux. This option + enables an extended attribute handler for file security + labels in the jfs filesystem. + + If you are not using a security module that requires using + extended attributes for file security labels, say N. + +config JFS_DEBUG + bool "JFS debugging" + depends on JFS_FS + help + If you are experiencing any problems with the JFS filesystem, say + Y here. This will result in additional debugging messages to be + written to the system log. Under normal circumstances, this + results in very little overhead. + +config JFS_STATISTICS + bool "JFS statistics" + depends on JFS_FS + help + Enabling this option will cause statistics from the JFS file system + to be made available to the user in the /proc/fs/jfs/ directory. + +config FS_POSIX_ACL +# Posix ACL utility routines (for now, only ext2/ext3/jfs/reiserfs) +# +# NOTE: you can implement Posix ACLs without these helpers (XFS does). +# Never use this symbol for ifdefs. +# + bool + default n + +source "fs/xfs/Kconfig" + +config OCFS2_FS + tristate "OCFS2 file system support (EXPERIMENTAL)" + depends on NET && EXPERIMENTAL + select CONFIGFS_FS + select JBD + select CRC32 + select INET + help + OCFS2 is a general purpose extent based shared disk cluster file + system with many similarities to ext3. It supports 64 bit inode + numbers, and has automatically extending metadata groups which may + also make it attractive for non-clustered use. + + You'll want to install the ocfs2-tools package in order to at least + get "mount.ocfs2". + + Project web page: http://oss.oracle.com/projects/ocfs2 + Tools web page: http://oss.oracle.com/projects/ocfs2-tools + OCFS2 mailing lists: http://oss.oracle.com/projects/ocfs2/mailman/ + + Note: Features which OCFS2 does not support yet: + - extended attributes + - shared writeable mmap + - loopback is supported, but data written will not + be cluster coherent. + - quotas + - cluster aware flock + - Directory change notification (F_NOTIFY) + - Distributed Caching (F_SETLEASE/F_GETLEASE/break_lease) + - POSIX ACLs + - readpages / writepages (not user visible) + +config MINIX_FS + tristate "Minix fs support" + help + Minix is a simple operating system used in many classes about OS's. + The minix file system (method to organize files on a hard disk + partition or a floppy disk) was the original file system for Linux, + but has been superseded by the second extended file system ext2fs. + You don't want to use the minix file system on your hard disk + because of certain built-in restrictions, but it is sometimes found + on older Linux floppy disks. This option will enlarge your kernel + by about 28 KB. If unsure, say N. + + To compile this file system support as a module, choose M here: the + module will be called minix. Note that the file system of your root + partition (the one containing the directory /) cannot be compiled as + a module. + +config ROMFS_FS + tristate "ROM file system support" + ---help--- + This is a very small read-only file system mainly intended for + initial ram disks of installation disks, but it could be used for + other read-only media as well. Read + for details. + + To compile this file system support as a module, choose M here: the + module will be called romfs. Note that the file system of your + root partition (the one containing the directory /) cannot be a + module. + + If you don't know whether you need it, then you don't need it: + answer N. + +config INOTIFY + bool "Inotify file change notification support" + default y + ---help--- + Say Y here to enable inotify support and the associated system + calls. Inotify is a file change notification system and a + replacement for dnotify. Inotify fixes numerous shortcomings in + dnotify and introduces several new features. It allows monitoring + of both files and directories via a single open fd. Other features + include multiple file events, one-shot support, and unmount + notification. + + For more information, see Documentation/filesystems/inotify.txt + + If unsure, say Y. + +config QUOTA + bool "Quota support" + help + If you say Y here, you will be able to set per user limits for disk + usage (also called disk quotas). Currently, it works for the + ext2, ext3, and reiserfs file system. ext3 also supports journalled + quotas for which you don't need to run quotacheck(8) after an unclean + shutdown. + For further details, read the Quota mini-HOWTO, available from + , or the documentation provided + with the quota tools. Probably the quota support is only useful for + multi user systems. If unsure, say N. + +config QFMT_V1 + tristate "Old quota format support" + depends on QUOTA + help + This quota format was (is) used by kernels earlier than 2.4.22. If + you have quota working and you don't want to convert to new quota + format say Y here. + +config QFMT_V2 + tristate "Quota format v2 support" + depends on QUOTA + help + This quota format allows using quotas with 32-bit UIDs/GIDs. If you + need this functionality say Y here. + +config QUOTACTL + bool + depends on XFS_QUOTA || QUOTA + default y + +config DNOTIFY + bool "Dnotify support" if EMBEDDED + default y + help + Dnotify is a directory-based per-fd file change notification system + that uses signals to communicate events to user-space. There exist + superior alternatives, but some applications may still rely on + dnotify. + + Because of this, if unsure, say Y. + +config AUTOFS_FS + tristate "Kernel automounter support" + help + The automounter is a tool to automatically mount remote file systems + on demand. This implementation is partially kernel-based to reduce + overhead in the already-mounted case; this is unlike the BSD + automounter (amd), which is a pure user space daemon. + + To use the automounter you need the user-space tools from the autofs + package; you can find the location in . + You also want to answer Y to "NFS file system support", below. + + If you want to use the newer version of the automounter with more + features, say N here and say Y to "Kernel automounter v4 support", + below. + + To compile this support as a module, choose M here: the module will be + called autofs. + + If you are not a part of a fairly large, distributed network, you + probably do not need an automounter, and can say N here. + +config AUTOFS4_FS + tristate "Kernel automounter version 4 support (also supports v3)" + help + The automounter is a tool to automatically mount remote file systems + on demand. This implementation is partially kernel-based to reduce + overhead in the already-mounted case; this is unlike the BSD + automounter (amd), which is a pure user space daemon. + + To use the automounter you need the user-space tools from + ; you also + want to answer Y to "NFS file system support", below. + + To compile this support as a module, choose M here: the module will be + called autofs4. You will need to add "alias autofs autofs4" to your + modules configuration file. + + If you are not a part of a fairly large, distributed network or + don't have a laptop which needs to dynamically reconfigure to the + local network, you probably do not need an automounter, and can say + N here. + +config FUSE_FS + tristate "Filesystem in Userspace support" + help + With FUSE it is possible to implement a fully functional filesystem + in a userspace program. + + There's also companion library: libfuse. This library along with + utilities is available from the FUSE homepage: + + + See for more information. + See for needed library/utility version. + + If you want to develop a userspace FS, or if you want to use + a filesystem based on FUSE, answer Y or M. + +menu "CD-ROM/DVD Filesystems" + +config ISO9660_FS + tristate "ISO 9660 CDROM file system support" + help + This is the standard file system used on CD-ROMs. It was previously + known as "High Sierra File System" and is called "hsfs" on other + Unix systems. The so-called Rock-Ridge extensions which allow for + long Unix filenames and symbolic links are also supported by this + driver. If you have a CD-ROM drive and want to do more with it than + just listen to audio CDs and watch its LEDs, say Y (and read + and the CD-ROM-HOWTO, + available from ), thereby + enlarging your kernel by about 27 KB; otherwise say N. + + To compile this file system support as a module, choose M here: the + module will be called isofs. + +config JOLIET + bool "Microsoft Joliet CDROM extensions" + depends on ISO9660_FS + select NLS + help + Joliet is a Microsoft extension for the ISO 9660 CD-ROM file system + which allows for long filenames in unicode format (unicode is the + new 16 bit character code, successor to ASCII, which encodes the + characters of almost all languages of the world; see + for more information). Say Y here if you + want to be able to read Joliet CD-ROMs under Linux. + +config ZISOFS + bool "Transparent decompression extension" + depends on ISO9660_FS + select ZLIB_INFLATE + help + This is a Linux-specific extension to RockRidge which lets you store + data in compressed form on a CD-ROM and have it transparently + decompressed when the CD-ROM is accessed. See + for the tools + necessary to create such a filesystem. Say Y here if you want to be + able to read such compressed CD-ROMs. + +config ZISOFS_FS +# for fs/nls/Config.in + tristate + depends on ZISOFS + default ISO9660_FS + +config UDF_FS + tristate "UDF file system support" + help + This is the new file system used on some CD-ROMs and DVDs. Say Y if + you intend to mount DVD discs or CDRW's written in packet mode, or + if written to by other UDF utilities, such as DirectCD. + Please read . + + To compile this file system support as a module, choose M here: the + module will be called udf. + + If unsure, say N. + +config UDF_NLS + bool + default y + depends on (UDF_FS=m && NLS) || (UDF_FS=y && NLS=y) + +endmenu + +menu "DOS/FAT/NT Filesystems" + +config FAT_FS + tristate + select NLS + help + If you want to use one of the FAT-based file systems (the MS-DOS and + VFAT (Windows 95) file systems), then you must say Y or M here + to include FAT support. You will then be able to mount partitions or + diskettes with FAT-based file systems and transparently access the + files on them, i.e. MSDOS files will look and behave just like all + other Unix files. + + This FAT support is not a file system in itself, it only provides + the foundation for the other file systems. You will have to say Y or + M to at least one of "MSDOS fs support" or "VFAT fs support" in + order to make use of it. + + Another way to read and write MSDOS floppies and hard drive + partitions from within Linux (but not transparently) is with the + mtools ("man mtools") program suite. You don't need to say Y here in + order to do that. + + If you need to move large files on floppies between a DOS and a + Linux box, say Y here, mount the floppy under Linux with an MSDOS + file system and use GNU tar's M option. GNU tar is a program + available for Unix and DOS ("man tar" or "info tar"). + + It is now also becoming possible to read and write compressed FAT + file systems; read for + details. + + The FAT support will enlarge your kernel by about 37 KB. If unsure, + say Y. + + To compile this as a module, choose M here: the module will be called + fat. Note that if you compile the FAT support as a module, you + cannot compile any of the FAT-based file systems into the kernel + -- they will have to be modules as well. + +config MSDOS_FS + tristate "MSDOS fs support" + select FAT_FS + help + This allows you to mount MSDOS partitions of your hard drive (unless + they are compressed; to access compressed MSDOS partitions under + Linux, you can either use the DOS emulator DOSEMU, described in the + DOSEMU-HOWTO, available from + , or try dmsdosfs in + . If you + intend to use dosemu with a non-compressed MSDOS partition, say Y + here) and MSDOS floppies. This means that file access becomes + transparent, i.e. the MSDOS files look and behave just like all + other Unix files. + + If you have Windows 95 or Windows NT installed on your MSDOS + partitions, you should use the VFAT file system (say Y to "VFAT fs + support" below), or you will not be able to see the long filenames + generated by Windows 95 / Windows NT. + + This option will enlarge your kernel by about 7 KB. If unsure, + answer Y. This will only work if you said Y to "DOS FAT fs support" + as well. To compile this as a module, choose M here: the module will + be called msdos. + +config VFAT_FS + tristate "VFAT (Windows-95) fs support" + select FAT_FS + help + This option provides support for normal Windows file systems with + long filenames. That includes non-compressed FAT-based file systems + used by Windows 95, Windows 98, Windows NT 4.0, and the Unix + programs from the mtools package. + + The VFAT support enlarges your kernel by about 10 KB and it only + works if you said Y to the "DOS FAT fs support" above. Please read + the file for details. If + unsure, say Y. + + To compile this as a module, choose M here: the module will be called + vfat. + +config FAT_DEFAULT_CODEPAGE + int "Default codepage for FAT" + depends on MSDOS_FS || VFAT_FS + default 437 + help + This option should be set to the codepage of your FAT filesystems. + It can be overridden with the "codepage" mount option. + See for more information. + +config FAT_DEFAULT_IOCHARSET + string "Default iocharset for FAT" + depends on VFAT_FS + default "iso8859-1" + help + Set this to the default input/output character set you'd + like FAT to use. It should probably match the character set + that most of your FAT filesystems use, and can be overridden + with the "iocharset" mount option for FAT filesystems. + Note that "utf8" is not recommended for FAT filesystems. + If unsure, you shouldn't set "utf8" here. + See for more information. + +config NTFS_FS + tristate "NTFS file system support" + select NLS + help + NTFS is the file system of Microsoft Windows NT, 2000, XP and 2003. + + Saying Y or M here enables read support. There is partial, but + safe, write support available. For write support you must also + say Y to "NTFS write support" below. + + There are also a number of user-space tools available, called + ntfsprogs. These include ntfsundelete and ntfsresize, that work + without NTFS support enabled in the kernel. + + This is a rewrite from scratch of Linux NTFS support and replaced + the old NTFS code starting with Linux 2.5.11. A backport to + the Linux 2.4 kernel series is separately available as a patch + from the project web site. + + For more information see + and . + + To compile this file system support as a module, choose M here: the + module will be called ntfs. + + If you are not using Windows NT, 2000, XP or 2003 in addition to + Linux on your computer it is safe to say N. + +config NTFS_DEBUG + bool "NTFS debugging support" + depends on NTFS_FS + help + If you are experiencing any problems with the NTFS file system, say + Y here. This will result in additional consistency checks to be + performed by the driver as well as additional debugging messages to + be written to the system log. Note that debugging messages are + disabled by default. To enable them, supply the option debug_msgs=1 + at the kernel command line when booting the kernel or as an option + to insmod when loading the ntfs module. Once the driver is active, + you can enable debugging messages by doing (as root): + echo 1 > /proc/sys/fs/ntfs-debug + Replacing the "1" with "0" would disable debug messages. + + If you leave debugging messages disabled, this results in little + overhead, but enabling debug messages results in very significant + slowdown of the system. + + When reporting bugs, please try to have available a full dump of + debugging messages while the misbehaviour was occurring. + +config NTFS_RW + bool "NTFS write support" + depends on NTFS_FS + help + This enables the partial, but safe, write support in the NTFS driver. + + The only supported operation is overwriting existing files, without + changing the file length. No file or directory creation, deletion or + renaming is possible. Note only non-resident files can be written to + so you may find that some very small files (<500 bytes or so) cannot + be written to. + + While we cannot guarantee that it will not damage any data, we have + so far not received a single report where the driver would have + damaged someones data so we assume it is perfectly safe to use. + + Note: While write support is safe in this version (a rewrite from + scratch of the NTFS support), it should be noted that the old NTFS + write support, included in Linux 2.5.10 and before (since 1997), + is not safe. + + This is currently useful with TopologiLinux. TopologiLinux is run + on top of any DOS/Microsoft Windows system without partitioning your + hard disk. Unlike other Linux distributions TopologiLinux does not + need its own partition. For more information see + + + It is perfectly safe to say N here. + +endmenu + +menu "Pseudo filesystems" + +config PROC_FS + bool "/proc file system support" + help + This is a virtual file system providing information about the status + of the system. "Virtual" means that it doesn't take up any space on + your hard disk: the files are created on the fly by the kernel when + you try to access them. Also, you cannot read the files with older + version of the program less: you need to use more or cat. + + It's totally cool; for example, "cat /proc/interrupts" gives + information about what the different IRQs are used for at the moment + (there is a small number of Interrupt ReQuest lines in your computer + that are used by the attached devices to gain the CPU's attention -- + often a source of trouble if two devices are mistakenly configured + to use the same IRQ). The program procinfo to display some + information about your system gathered from the /proc file system. + + Before you can use the /proc file system, it has to be mounted, + meaning it has to be given a location in the directory hierarchy. + That location should be /proc. A command such as "mount -t proc proc + /proc" or the equivalent line in /etc/fstab does the job. + + The /proc file system is explained in the file + and on the proc(5) manpage + ("man 5 proc"). + + This option will enlarge your kernel by about 67 KB. Several + programs depend on this, so everyone should say Y here. + +config PROC_KCORE + bool "/proc/kcore support" if !ARM + depends on PROC_FS && MMU + +config PROC_VMCORE + bool "/proc/vmcore support (EXPERIMENTAL)" + depends on PROC_FS && EXPERIMENTAL && CRASH_DUMP + help + Exports the dump image of crashed kernel in ELF format. + +config SYSFS + bool "sysfs file system support" if EMBEDDED + default y + help + The sysfs filesystem is a virtual filesystem that the kernel uses to + export internal kernel objects, their attributes, and their + relationships to one another. + + Users can use sysfs to ascertain useful information about the running + kernel, such as the devices the kernel has discovered on each bus and + which driver each is bound to. sysfs can also be used to tune devices + and other kernel subsystems. + + Some system agents rely on the information in sysfs to operate. + /sbin/hotplug uses device and object attributes in sysfs to assist in + delegating policy decisions, like persistantly naming devices. + + sysfs is currently used by the block subsystem to mount the root + partition. If sysfs is disabled you must specify the boot device on + the kernel boot command line via its major and minor numbers. For + example, "root=03:01" for /dev/hda1. + + Designers of embedded systems may wish to say N here to conserve space. + +config TMPFS + bool "Virtual memory file system support (former shm fs)" + help + Tmpfs is a file system which keeps all files in virtual memory. + + Everything in tmpfs is temporary in the sense that no files will be + created on your hard drive. The files live in memory and swap + space. If you unmount a tmpfs instance, everything stored therein is + lost. + + See for details. + +config HUGETLBFS + bool "HugeTLB file system support" + depends X86 || IA64 || PPC64 || SPARC64 || SUPERH || BROKEN + +config HUGETLB_PAGE + def_bool HUGETLBFS + +config RAMFS + bool + default y + ---help--- + Ramfs is a file system which keeps all files in RAM. It allows + read and write access. + + It is more of an programming example than a useable file system. If + you need a file system which lives in RAM with limit checking use + tmpfs. + + To compile this as a module, choose M here: the module will be called + ramfs. + +config RELAYFS_FS + tristate "Relayfs file system support" + ---help--- + Relayfs is a high-speed data relay filesystem designed to provide + an efficient mechanism for tools and facilities to relay large + amounts of data from kernel space to user space. + + To compile this code as a module, choose M here: the module will be + called relayfs. + + If unsure, say N. + +config CONFIGFS_FS + tristate "Userspace-driven configuration filesystem (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + configfs is a ram-based filesystem that provides the converse + of sysfs's functionality. Where sysfs is a filesystem-based + view of kernel objects, configfs is a filesystem-based manager + of kernel objects, or config_items. + + Both sysfs and configfs can and should exist together on the + same system. One is not a replacement for the other. + +endmenu + +menu "Miscellaneous filesystems" + +config ADFS_FS + tristate "ADFS file system support (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + The Acorn Disc Filing System is the standard file system of the + RiscOS operating system which runs on Acorn's ARM-based Risc PC + systems and the Acorn Archimedes range of machines. If you say Y + here, Linux will be able to read from ADFS partitions on hard drives + and from ADFS-formatted floppy discs. If you also want to be able to + write to those devices, say Y to "ADFS write support" below. + + The ADFS partition should be the first partition (i.e., + /dev/[hs]d?1) on each of your drives. Please read the file + for further details. + + To compile this code as a module, choose M here: the module will be + called adfs. + + If unsure, say N. + +config ADFS_FS_RW + bool "ADFS write support (DANGEROUS)" + depends on ADFS_FS + help + If you say Y here, you will be able to write to ADFS partitions on + hard drives and ADFS-formatted floppy disks. This is experimental + codes, so if you're unsure, say N. + +config AFFS_FS + tristate "Amiga FFS file system support (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + The Fast File System (FFS) is the common file system used on hard + disks by Amiga(tm) systems since AmigaOS Version 1.3 (34.20). Say Y + if you want to be able to read and write files from and to an Amiga + FFS partition on your hard drive. Amiga floppies however cannot be + read with this driver due to an incompatibility of the floppy + controller used in an Amiga and the standard floppy controller in + PCs and workstations. Read + and . + + With this driver you can also mount disk files used by Bernd + Schmidt's Un*X Amiga Emulator + (). + If you want to do this, you will also need to say Y or M to "Loop + device support", above. + + To compile this file system support as a module, choose M here: the + module will be called affs. If unsure, say N. + +config HFS_FS + tristate "Apple Macintosh file system support (EXPERIMENTAL)" + depends on EXPERIMENTAL + select NLS + help + If you say Y here, you will be able to mount Macintosh-formatted + floppy disks and hard drive partitions with full read-write access. + Please read to learn about the available mount + options. + + To compile this file system support as a module, choose M here: the + module will be called hfs. + +config HFSPLUS_FS + tristate "Apple Extended HFS file system support" + select NLS + select NLS_UTF8 + help + If you say Y here, you will be able to mount extended format + Macintosh-formatted hard drive partitions with full read-write access. + + This file system is often called HFS+ and was introduced with + MacOS 8. It includes all Mac specific filesystem data such as + data forks and creator codes, but it also has several UNIX + style features such as file ownership and permissions. + +config BEFS_FS + tristate "BeOS file system (BeFS) support (read only) (EXPERIMENTAL)" + depends on EXPERIMENTAL + select NLS + help + The BeOS File System (BeFS) is the native file system of Be, Inc's + BeOS. Notable features include support for arbitrary attributes + on files and directories, and database-like indeces on selected + attributes. (Also note that this driver doesn't make those features + available at this time). It is a 64 bit filesystem, so it supports + extremly large volumes and files. + + If you use this filesystem, you should also say Y to at least one + of the NLS (native language support) options below. + + If you don't know what this is about, say N. + + To compile this as a module, choose M here: the module will be + called befs. + +config BEFS_DEBUG + bool "Debug BeFS" + depends on BEFS_FS + help + If you say Y here, you can use the 'debug' mount option to enable + debugging output from the driver. + +config BFS_FS + tristate "BFS file system support (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + Boot File System (BFS) is a file system used under SCO UnixWare to + allow the bootloader access to the kernel image and other important + files during the boot process. It is usually mounted under /stand + and corresponds to the slice marked as "STAND" in the UnixWare + partition. You should say Y if you want to read or write the files + on your /stand slice from within Linux. You then also need to say Y + to "UnixWare slices support", below. More information about the BFS + file system is contained in the file + . + + If you don't know what this is about, say N. + + To compile this as a module, choose M here: the module will be called + bfs. Note that the file system of your root partition (the one + containing the directory /) cannot be compiled as a module. + + + +config EFS_FS + tristate "EFS file system support (read only) (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + EFS is an older file system used for non-ISO9660 CD-ROMs and hard + disk partitions by SGI's IRIX operating system (IRIX 6.0 and newer + uses the XFS file system for hard disk partitions however). + + This implementation only offers read-only access. If you don't know + what all this is about, it's safe to say N. For more information + about EFS see its home page at . + + To compile the EFS file system support as a module, choose M here: the + module will be called efs. + +config JFFS_FS + tristate "Journalling Flash File System (JFFS) support" + depends on MTD + help + JFFS is the Journaling Flash File System developed by Axis + Communications in Sweden, aimed at providing a crash/powerdown-safe + file system for disk-less embedded devices. Further information is + available at (). + +config JFFS_FS_VERBOSE + int "JFFS debugging verbosity (0 = quiet, 3 = noisy)" + depends on JFFS_FS + default "0" + help + Determines the verbosity level of the JFFS debugging messages. + +config JFFS_PROC_FS + bool "JFFS stats available in /proc filesystem" + depends on JFFS_FS && PROC_FS + help + Enabling this option will cause statistics from mounted JFFS file systems + to be made available to the user in the /proc/fs/jffs/ directory. + +config JFFS2_FS + tristate "Journalling Flash File System v2 (JFFS2) support" + select CRC32 + depends on MTD + help + JFFS2 is the second generation of the Journalling Flash File System + for use on diskless embedded devices. It provides improved wear + levelling, compression and support for hard links. You cannot use + this on normal block devices, only on 'MTD' devices. + + Further information on the design and implementation of JFFS2 is + available at . + +config JFFS2_FS_DEBUG + int "JFFS2 debugging verbosity (0 = quiet, 2 = noisy)" + depends on JFFS2_FS + default "0" + help + This controls the amount of debugging messages produced by the JFFS2 + code. Set it to zero for use in production systems. For evaluation, + testing and debugging, it's advisable to set it to one. This will + enable a few assertions and will print debugging messages at the + KERN_DEBUG loglevel, where they won't normally be visible. Level 2 + is unlikely to be useful - it enables extra debugging in certain + areas which at one point needed debugging, but when the bugs were + located and fixed, the detailed messages were relegated to level 2. + + If reporting bugs, please try to have available a full dump of the + messages at debug level 1 while the misbehaviour was occurring. + +config JFFS2_FS_WRITEBUFFER + bool "JFFS2 write-buffering support" + depends on JFFS2_FS + default y + help + This enables the write-buffering support in JFFS2. + + This functionality is required to support JFFS2 on the following + types of flash devices: + - NAND flash + - NOR flash with transparent ECC + - DataFlash + +config JFFS2_SUMMARY + bool "JFFS2 summary support (EXPERIMENTAL)" + depends on JFFS2_FS && EXPERIMENTAL + default n + help + This feature makes it possible to use summary information + for faster filesystem mount. + + The summary information can be inserted into a filesystem image + by the utility 'sumtool'. + + If unsure, say 'N'. + +config JFFS2_COMPRESSION_OPTIONS + bool "Advanced compression options for JFFS2" + depends on JFFS2_FS + default n + help + Enabling this option allows you to explicitly choose which + compression modules, if any, are enabled in JFFS2. Removing + compressors and mean you cannot read existing file systems, + and enabling experimental compressors can mean that you + write a file system which cannot be read by a standard kernel. + + If unsure, you should _definitely_ say 'N'. + +config JFFS2_ZLIB + bool "JFFS2 ZLIB compression support" if JFFS2_COMPRESSION_OPTIONS + select ZLIB_INFLATE + select ZLIB_DEFLATE + depends on JFFS2_FS + default y + help + Zlib is designed to be a free, general-purpose, legally unencumbered, + lossless data-compression library for use on virtually any computer + hardware and operating system. See for + further information. + + Say 'Y' if unsure. + +config JFFS2_RTIME + bool "JFFS2 RTIME compression support" if JFFS2_COMPRESSION_OPTIONS + depends on JFFS2_FS + default y + help + Rtime does manage to recompress already-compressed data. Say 'Y' if unsure. + +config JFFS2_RUBIN + bool "JFFS2 RUBIN compression support" if JFFS2_COMPRESSION_OPTIONS + depends on JFFS2_FS + default n + help + RUBINMIPS and DYNRUBIN compressors. Say 'N' if unsure. + +choice + prompt "JFFS2 default compression mode" if JFFS2_COMPRESSION_OPTIONS + default JFFS2_CMODE_PRIORITY + depends on JFFS2_FS + help + You can set here the default compression mode of JFFS2 from + the available compression modes. Don't touch if unsure. + +config JFFS2_CMODE_NONE + bool "no compression" + help + Uses no compression. + +config JFFS2_CMODE_PRIORITY + bool "priority" + help + Tries the compressors in a predefinied order and chooses the first + successful one. + +config JFFS2_CMODE_SIZE + bool "size (EXPERIMENTAL)" + help + Tries all compressors and chooses the one which has the smallest + result. + +endchoice + +config CRAMFS + tristate "Compressed ROM file system support (cramfs)" + select ZLIB_INFLATE + help + Saying Y here includes support for CramFs (Compressed ROM File + System). CramFs is designed to be a simple, small, and compressed + file system for ROM based embedded systems. CramFs is read-only, + limited to 256MB file systems (with 16MB files), and doesn't support + 16/32 bits uid/gid, hard links and timestamps. + + See and + for further information. + + To compile this as a module, choose M here: the module will be called + cramfs. Note that the root file system (the one containing the + directory /) cannot be compiled as a module. + + If unsure, say N. + +config VXFS_FS + tristate "FreeVxFS file system support (VERITAS VxFS(TM) compatible)" + help + FreeVxFS is a file system driver that support the VERITAS VxFS(TM) + file system format. VERITAS VxFS(TM) is the standard file system + of SCO UnixWare (and possibly others) and optionally available + for Sunsoft Solaris, HP-UX and many other operating systems. + Currently only readonly access is supported. + + NOTE: the file system type as used by mount(1), mount(2) and + fstab(5) is 'vxfs' as it describes the file system format, not + the actual driver. + + To compile this as a module, choose M here: the module will be + called freevxfs. If unsure, say N. + + +config HPFS_FS + tristate "OS/2 HPFS file system support" + help + OS/2 is IBM's operating system for PC's, the same as Warp, and HPFS + is the file system used for organizing files on OS/2 hard disk + partitions. Say Y if you want to be able to read files from and + write files to an OS/2 HPFS partition on your hard drive. OS/2 + floppies however are in regular MSDOS format, so you don't need this + option in order to be able to read them. Read + . + + To compile this file system support as a module, choose M here: the + module will be called hpfs. If unsure, say N. + + + +config QNX4FS_FS + tristate "QNX4 file system support (read only)" + help + This is the file system used by the real-time operating systems + QNX 4 and QNX 6 (the latter is also called QNX RTP). + Further information is available at . + Say Y if you intend to mount QNX hard disks or floppies. + Unless you say Y to "QNX4FS read-write support" below, you will + only be able to read these file systems. + + To compile this file system support as a module, choose M here: the + module will be called qnx4. + + If you don't know whether you need it, then you don't need it: + answer N. + +config QNX4FS_RW + bool "QNX4FS write support (DANGEROUS)" + depends on QNX4FS_FS && EXPERIMENTAL && BROKEN + help + Say Y if you want to test write support for QNX4 file systems. + + It's currently broken, so for now: + answer N. + + + +config SYSV_FS + tristate "System V/Xenix/V7/Coherent file system support" + help + SCO, Xenix and Coherent are commercial Unix systems for Intel + machines, and Version 7 was used on the DEC PDP-11. Saying Y + here would allow you to read from their floppies and hard disk + partitions. + + If you have floppies or hard disk partitions like that, it is likely + that they contain binaries from those other Unix systems; in order + to run these binaries, you will want to install linux-abi which is a + a set of kernel modules that lets you run SCO, Xenix, Wyse, + UnixWare, Dell Unix and System V programs under Linux. It is + available via FTP (user: ftp) from + ). + NOTE: that will work only for binaries from Intel-based systems; + PDP ones will have to wait until somebody ports Linux to -11 ;-) + + If you only intend to mount files from some other Unix over the + network using NFS, you don't need the System V file system support + (but you need NFS file system support obviously). + + Note that this option is generally not needed for floppies, since a + good portable way to transport files and directories between unixes + (and even other operating systems) is given by the tar program ("man + tar" or preferably "info tar"). Note also that this option has + nothing whatsoever to do with the option "System V IPC". Read about + the System V file system in + . + Saying Y here will enlarge your kernel by about 27 KB. + + To compile this as a module, choose M here: the module will be called + sysv. + + If you haven't heard about all of this before, it's safe to say N. + + + +config UFS_FS + tristate "UFS file system support (read only)" + help + BSD and derivate versions of Unix (such as SunOS, FreeBSD, NetBSD, + OpenBSD and NeXTstep) use a file system called UFS. Some System V + Unixes can create and mount hard disk partitions and diskettes using + this file system as well. Saying Y here will allow you to read from + these partitions; if you also want to write to them, say Y to the + experimental "UFS file system write support", below. Please read the + file for more information. + + The recently released UFS2 variant (used in FreeBSD 5.x) is + READ-ONLY supported. + + If you only intend to mount files from some other Unix over the + network using NFS, you don't need the UFS file system support (but + you need NFS file system support obviously). + + Note that this option is generally not needed for floppies, since a + good portable way to transport files and directories between unixes + (and even other operating systems) is given by the tar program ("man + tar" or preferably "info tar"). + + When accessing NeXTstep files, you may need to convert them from the + NeXT character set to the Latin1 character set; use the program + recode ("info recode") for this purpose. + + To compile the UFS file system support as a module, choose M here: the + module will be called ufs. + + If you haven't heard about all of this before, it's safe to say N. + +config UFS_FS_WRITE + bool "UFS file system write support (DANGEROUS)" + depends on UFS_FS && EXPERIMENTAL && BROKEN + help + Say Y here if you want to try writing to UFS partitions. This is + experimental, so you should back up your UFS partitions beforehand. + +endmenu + +menu "Network File Systems" + depends on NET + +config NFS_FS + tristate "NFS file system support" + depends on INET + select LOCKD + select SUNRPC + select NFS_ACL_SUPPORT if NFS_V3_ACL + help + If you are connected to some other (usually local) Unix computer + (using SLIP, PLIP, PPP or Ethernet) and want to mount files residing + on that computer (the NFS server) using the Network File Sharing + protocol, say Y. "Mounting files" means that the client can access + the files with usual UNIX commands as if they were sitting on the + client's hard disk. For this to work, the server must run the + programs nfsd and mountd (but does not need to have NFS file system + support enabled in its kernel). NFS is explained in the Network + Administrator's Guide, available from + , on its man page: "man + nfs", and in the NFS-HOWTO. + + A superior but less widely used alternative to NFS is provided by + the Coda file system; see "Coda file system support" below. + + If you say Y here, you should have said Y to TCP/IP networking also. + This option would enlarge your kernel by about 27 KB. + + To compile this file system support as a module, choose M here: the + module will be called nfs. + + If you are configuring a diskless machine which will mount its root + file system over NFS at boot time, say Y here and to "Kernel + level IP autoconfiguration" above and to "Root file system on NFS" + below. You cannot compile this driver as a module in this case. + There are two packages designed for booting diskless machines over + the net: netboot, available from + , and Etherboot, + available from . + + If you don't know what all this is about, say N. + +config NFS_V3 + bool "Provide NFSv3 client support" + depends on NFS_FS + help + Say Y here if you want your NFS client to be able to speak version + 3 of the NFS protocol. + + If unsure, say Y. + +config NFS_V3_ACL + bool "Provide client support for the NFSv3 ACL protocol extension" + depends on NFS_V3 + help + Implement the NFSv3 ACL protocol extension for manipulating POSIX + Access Control Lists. The server should also be compiled with + the NFSv3 ACL protocol extension; see the CONFIG_NFSD_V3_ACL option. + + If unsure, say N. + +config NFS_V4 + bool "Provide NFSv4 client support (EXPERIMENTAL)" + depends on NFS_FS && EXPERIMENTAL + select RPCSEC_GSS_KRB5 + help + Say Y here if you want your NFS client to be able to speak the newer + version 4 of the NFS protocol. + + Note: Requires auxiliary userspace daemons which may be found on + http://www.citi.umich.edu/projects/nfsv4/ + + If unsure, say N. + +config NFS_DIRECTIO + bool "Allow direct I/O on NFS files (EXPERIMENTAL)" + depends on NFS_FS && EXPERIMENTAL + help + This option enables applications to perform uncached I/O on files + in NFS file systems using the O_DIRECT open() flag. When O_DIRECT + is set for a file, its data is not cached in the system's page + cache. Data is moved to and from user-level application buffers + directly. Unlike local disk-based file systems, NFS O_DIRECT has + no alignment restrictions. + + Unless your program is designed to use O_DIRECT properly, you are + much better off allowing the NFS client to manage data caching for + you. Misusing O_DIRECT can cause poor server performance or network + storms. This kernel build option defaults OFF to avoid exposing + system administrators unwittingly to a potentially hazardous + feature. + + For more details on NFS O_DIRECT, see fs/nfs/direct.c. + + If unsure, say N. This reduces the size of the NFS client, and + causes open() to return EINVAL if a file residing in NFS is + opened with the O_DIRECT flag. + +config NFSD + tristate "NFS server support" + depends on INET + select LOCKD + select SUNRPC + select EXPORTFS + select NFS_ACL_SUPPORT if NFSD_V3_ACL || NFSD_V2_ACL + help + If you want your Linux box to act as an NFS *server*, so that other + computers on your local network which support NFS can access certain + directories on your box transparently, you have two options: you can + use the self-contained user space program nfsd, in which case you + should say N here, or you can say Y and use the kernel based NFS + server. The advantage of the kernel based solution is that it is + faster. + + In either case, you will need support software; the respective + locations are given in the file in the + NFS section. + + If you say Y here, you will get support for version 2 of the NFS + protocol (NFSv2). If you also want NFSv3, say Y to the next question + as well. + + Please read the NFS-HOWTO, available from + . + + To compile the NFS server support as a module, choose M here: the + module will be called nfsd. If unsure, say N. + +config NFSD_V2_ACL + bool + depends on NFSD + +config NFSD_V3 + bool "Provide NFSv3 server support" + depends on NFSD + help + If you would like to include the NFSv3 server as well as the NFSv2 + server, say Y here. If unsure, say Y. + +config NFSD_V3_ACL + bool "Provide server support for the NFSv3 ACL protocol extension" + depends on NFSD_V3 + select NFSD_V2_ACL + help + Implement the NFSv3 ACL protocol extension for manipulating POSIX + Access Control Lists on exported file systems. NFS clients should + be compiled with the NFSv3 ACL protocol extension; see the + CONFIG_NFS_V3_ACL option. If unsure, say N. + +config NFSD_V4 + bool "Provide NFSv4 server support (EXPERIMENTAL)" + depends on NFSD_V3 && EXPERIMENTAL + select NFSD_TCP + select CRYPTO_MD5 + select CRYPTO + select FS_POSIX_ACL + help + If you would like to include the NFSv4 server as well as the NFSv2 + and NFSv3 servers, say Y here. This feature is experimental, and + should only be used if you are interested in helping to test NFSv4. + If unsure, say N. + +config NFSD_TCP + bool "Provide NFS server over TCP support" + depends on NFSD + default y + help + If you want your NFS server to support TCP connections, say Y here. + TCP connections usually perform better than the default UDP when + the network is lossy or congested. If unsure, say Y. + +config ROOT_NFS + bool "Root file system on NFS" + depends on NFS_FS=y && IP_PNP + help + If you want your Linux box to mount its whole root file system (the + one containing the directory /) from some other computer over the + net via NFS (presumably because your box doesn't have a hard disk), + say Y. Read for details. It is + likely that in this case, you also want to say Y to "Kernel level IP + autoconfiguration" so that your box can discover its network address + at boot time. + + Most people say N here. + +config LOCKD + tristate + +config LOCKD_V4 + bool + depends on NFSD_V3 || NFS_V3 + default y + +config EXPORTFS + tristate + +config NFS_ACL_SUPPORT + tristate + select FS_POSIX_ACL + +config NFS_COMMON + bool + depends on NFSD || NFS_FS + default y + +config SUNRPC + tristate + +config SUNRPC_GSS + tristate + +config RPCSEC_GSS_KRB5 + tristate "Secure RPC: Kerberos V mechanism (EXPERIMENTAL)" + depends on SUNRPC && EXPERIMENTAL + select SUNRPC_GSS + select CRYPTO + select CRYPTO_MD5 + select CRYPTO_DES + help + Provides for secure RPC calls by means of a gss-api + mechanism based on Kerberos V5. This is required for + NFSv4. + + Note: Requires an auxiliary userspace daemon which may be found on + http://www.citi.umich.edu/projects/nfsv4/ + + If unsure, say N. + +config RPCSEC_GSS_SPKM3 + tristate "Secure RPC: SPKM3 mechanism (EXPERIMENTAL)" + depends on SUNRPC && EXPERIMENTAL + select SUNRPC_GSS + select CRYPTO + select CRYPTO_MD5 + select CRYPTO_DES + help + Provides for secure RPC calls by means of a gss-api + mechanism based on the SPKM3 public-key mechanism. + + Note: Requires an auxiliary userspace daemon which may be found on + http://www.citi.umich.edu/projects/nfsv4/ + + If unsure, say N. + +config SMB_FS + tristate "SMB file system support (to mount Windows shares etc.)" + depends on INET + select NLS + help + SMB (Server Message Block) is the protocol Windows for Workgroups + (WfW), Windows 95/98, Windows NT and OS/2 Lan Manager use to share + files and printers over local networks. Saying Y here allows you to + mount their file systems (often called "shares" in this context) and + access them just like any other Unix directory. Currently, this + works only if the Windows machines use TCP/IP as the underlying + transport protocol, and not NetBEUI. For details, read + and the SMB-HOWTO, + available from . + + Note: if you just want your box to act as an SMB *server* and make + files and printing services available to Windows clients (which need + to have a TCP/IP stack), you don't need to say Y here; you can use + the program SAMBA (available from ) + for that. + + General information about how to connect Linux, Windows machines and + Macs is on the WWW at . + + To compile the SMB support as a module, choose M here: the module will + be called smbfs. Most people say N, however. + +config SMB_NLS_DEFAULT + bool "Use a default NLS" + depends on SMB_FS + help + Enabling this will make smbfs use nls translations by default. You + need to specify the local charset (CONFIG_NLS_DEFAULT) in the nls + settings and you need to give the default nls for the SMB server as + CONFIG_SMB_NLS_REMOTE. + + The nls settings can be changed at mount time, if your smbmount + supports that, using the codepage and iocharset parameters. + + smbmount from samba 2.2.0 or later supports this. + +config SMB_NLS_REMOTE + string "Default Remote NLS Option" + depends on SMB_NLS_DEFAULT + default "cp437" + help + This setting allows you to specify a default value for which + codepage the server uses. If this field is left blank no + translations will be done by default. The local codepage/charset + default to CONFIG_NLS_DEFAULT. + + The nls settings can be changed at mount time, if your smbmount + supports that, using the codepage and iocharset parameters. + + smbmount from samba 2.2.0 or later supports this. + +config CIFS + tristate "CIFS support (advanced network filesystem for Samba, Window and other CIFS compliant servers)" + depends on INET + select NLS + help + This is the client VFS module for the Common Internet File System + (CIFS) protocol which is the successor to the Server Message Block + (SMB) protocol, the native file sharing mechanism for most early + PC operating systems. The CIFS protocol is fully supported by + file servers such as Windows 2000 (including Windows 2003, NT 4 + and Windows XP) as well by Samba (which provides excellent CIFS + server support for Linux and many other operating systems). Limited + support for Windows ME and similar servers is provided as well. + You must use the smbfs client filesystem to access older SMB servers + such as OS/2 and DOS. + + The intent of the cifs module is to provide an advanced + network file system client for mounting to CIFS compliant servers, + including support for dfs (hierarchical name space), secure per-user + session establishment, safe distributed caching (oplock), optional + packet signing, Unicode and other internationalization improvements, + and optional Winbind (nsswitch) integration. You do not need to enable + cifs if running only a (Samba) server. It is possible to enable both + smbfs and cifs (e.g. if you are using CIFS for accessing Windows 2003 + and Samba 3 servers, and smbfs for accessing old servers). If you need + to mount to Samba or Windows from this machine, say Y. + +config CIFS_STATS + bool "CIFS statistics" + depends on CIFS + help + Enabling this option will cause statistics for each server share + mounted by the cifs client to be displayed in /proc/fs/cifs/Stats + +config CIFS_STATS2 + bool "CIFS extended statistics" + depends on CIFS_STATS + help + Enabling this option will allow more detailed statistics on SMB + request timing to be displayed in /proc/fs/cifs/DebugData and also + allow optional logging of slow responses to dmesg (depending on the + value of /proc/fs/cifs/cifsFYI, see fs/cifs/README for more details). + These additional statistics may have a minor effect on performance + and memory utilization. + + Unless you are a developer or are doing network performance analysis + or tuning, say N. + +config CIFS_XATTR + bool "CIFS extended attributes" + depends on CIFS + help + Extended attributes are name:value pairs associated with inodes by + the kernel or by users (see the attr(5) manual page, or visit + for details). CIFS maps the name of + extended attributes beginning with the user namespace prefix + to SMB/CIFS EAs. EAs are stored on Windows servers without the + user namespace prefix, but their names are seen by Linux cifs clients + prefaced by the user namespace prefix. The system namespace + (used by some filesystems to store ACLs) is not supported at + this time. + + If unsure, say N. + +config CIFS_POSIX + bool "CIFS POSIX Extensions" + depends on CIFS_XATTR + help + Enabling this option will cause the cifs client to attempt to + negotiate a newer dialect with servers, such as Samba 3.0.5 + or later, that optionally can handle more POSIX like (rather + than Windows like) file behavior. It also enables + support for POSIX ACLs (getfacl and setfacl) to servers + (such as Samba 3.10 and later) which can negotiate + CIFS POSIX ACL support. If unsure, say N. + +config CIFS_EXPERIMENTAL + bool "CIFS Experimental Features (EXPERIMENTAL)" + depends on CIFS && EXPERIMENTAL + help + Enables cifs features under testing. These features are + experimental and currently include support for writepages + (multipage writebehind performance improvements) and directory + change notification ie fcntl(F_DNOTIFY) as well as some security + improvements. Some also depend on setting at runtime the + pseudo-file /proc/fs/cifs/Experimental (which is disabled by + default). See the file fs/cifs/README for more details. + + If unsure, say N. + +config CIFS_UPCALL + bool "CIFS Kerberos/SPNEGO advanced session setup (EXPERIMENTAL)" + depends on CIFS_EXPERIMENTAL + select CONNECTOR + help + Enables an upcall mechanism for CIFS which will be used to contact + userspace helper utilities to provide SPNEGO packaged Kerberos + tickets which are needed to mount to certain secure servers + (for which more secure Kerberos authentication is required). If + unsure, say N. + +config NCP_FS + tristate "NCP file system support (to mount NetWare volumes)" + depends on IPX!=n || INET + help + NCP (NetWare Core Protocol) is a protocol that runs over IPX and is + used by Novell NetWare clients to talk to file servers. It is to + IPX what NFS is to TCP/IP, if that helps. Saying Y here allows you + to mount NetWare file server volumes and to access them just like + any other Unix directory. For details, please read the file + in the kernel source and + the IPX-HOWTO from . + + You do not have to say Y here if you want your Linux box to act as a + file *server* for Novell NetWare clients. + + General information about how to connect Linux, Windows machines and + Macs is on the WWW at . + + To compile this as a module, choose M here: the module will be called + ncpfs. Say N unless you are connected to a Novell network. + +source "fs/ncpfs/Kconfig" + +config CODA_FS + tristate "Coda file system support (advanced network fs)" + depends on INET + help + Coda is an advanced network file system, similar to NFS in that it + enables you to mount file systems of a remote server and access them + with regular Unix commands as if they were sitting on your hard + disk. Coda has several advantages over NFS: support for + disconnected operation (e.g. for laptops), read/write server + replication, security model for authentication and encryption, + persistent client caches and write back caching. + + If you say Y here, your Linux box will be able to act as a Coda + *client*. You will need user level code as well, both for the + client and server. Servers are currently user level, i.e. they need + no kernel support. Please read + and check out the Coda + home page . + + To compile the coda client support as a module, choose M here: the + module will be called coda. + +config CODA_FS_OLD_API + bool "Use 96-bit Coda file identifiers" + depends on CODA_FS + help + A new kernel-userspace API had to be introduced for Coda v6.0 + to support larger 128-bit file identifiers as needed by the + new realms implementation. + + However this new API is not backward compatible with older + clients. If you really need to run the old Coda userspace + cache manager then say Y. + + For most cases you probably want to say N. + +config AFS_FS +# for fs/nls/Config.in + tristate "Andrew File System support (AFS) (Experimental)" + depends on INET && EXPERIMENTAL + select RXRPC + help + If you say Y here, you will get an experimental Andrew File System + driver. It currently only supports unsecured read-only AFS access. + + See for more intormation. + + If unsure, say N. + +config RXRPC + tristate + +config 9P_FS + tristate "Plan 9 Resource Sharing Support (9P2000) (Experimental)" + depends on INET && EXPERIMENTAL + help + If you say Y here, you will get experimental support for + Plan 9 resource sharing via the 9P2000 protocol. + + See for more information. + + If unsure, say N. + +endmenu + +menu "Partition Types" + +source "fs/partitions/Kconfig" + +endmenu + +source "fs/nls/Kconfig" + +endmenu + diff -urN oldtree/fs/Makefile newtree/fs/Makefile --- oldtree/fs/Makefile 2006-02-18 17:12:40.745284968 +0000 +++ newtree/fs/Makefile 2006-02-18 17:24:58.711097112 +0000 @@ -66,6 +66,7 @@ obj-$(CONFIG_ISO9660_FS) += isofs/ obj-$(CONFIG_DEVFS_FS) += devfs/ obj-$(CONFIG_HFSPLUS_FS) += hfsplus/ # Before hfs to find wrapped HFS+ +obj-$(CONFIG_ECRYPTFS) += ecryptfs/ obj-$(CONFIG_HFS_FS) += hfs/ obj-$(CONFIG_VXFS_FS) += freevxfs/ obj-$(CONFIG_NFS_FS) += nfs/ diff -urN oldtree/fs/Makefile.orig newtree/fs/Makefile.orig --- oldtree/fs/Makefile.orig 1970-01-01 00:00:00.000000000 +0000 +++ newtree/fs/Makefile.orig 2006-02-18 17:24:58.712096960 +0000 @@ -0,0 +1,103 @@ +# +# Makefile for the Linux filesystems. +# +# 14 Sep 2000, Christoph Hellwig +# Rewritten to use lists instead of if-statements. +# + +obj-y := open.o read_write.o file_table.o buffer.o bio.o super.o \ + block_dev.o char_dev.o stat.o exec.o pipe.o namei.o fcntl.o \ + ioctl.o readdir.o select.o fifo.o locks.o dcache.o inode.o \ + attr.o bad_inode.o file.o filesystems.o namespace.o aio.o \ + seq_file.o xattr.o libfs.o fs-writeback.o mpage.o direct-io.o \ + ioprio.o pnode.o + +obj-$(CONFIG_INOTIFY) += inotify.o +obj-$(CONFIG_EPOLL) += eventpoll.o +obj-$(CONFIG_COMPAT) += compat.o + +nfsd-$(CONFIG_NFSD) := nfsctl.o +obj-y += $(nfsd-y) $(nfsd-m) + +obj-$(CONFIG_BINFMT_AOUT) += binfmt_aout.o +obj-$(CONFIG_BINFMT_EM86) += binfmt_em86.o +obj-$(CONFIG_BINFMT_MISC) += binfmt_misc.o + +# binfmt_script is always there +obj-y += binfmt_script.o + +obj-$(CONFIG_BINFMT_ELF) += binfmt_elf.o +obj-$(CONFIG_BINFMT_ELF_FDPIC) += binfmt_elf_fdpic.o +obj-$(CONFIG_BINFMT_SOM) += binfmt_som.o +obj-$(CONFIG_BINFMT_FLAT) += binfmt_flat.o + +obj-$(CONFIG_FS_MBCACHE) += mbcache.o +obj-$(CONFIG_FS_POSIX_ACL) += posix_acl.o xattr_acl.o +obj-$(CONFIG_NFS_COMMON) += nfs_common/ + +obj-$(CONFIG_QUOTA) += dquot.o +obj-$(CONFIG_QFMT_V1) += quota_v1.o +obj-$(CONFIG_QFMT_V2) += quota_v2.o +obj-$(CONFIG_QUOTACTL) += quota.o + +obj-$(CONFIG_DNOTIFY) += dnotify.o + +obj-$(CONFIG_PROC_FS) += proc/ +obj-y += partitions/ +obj-$(CONFIG_SYSFS) += sysfs/ +obj-y += devpts/ + +obj-$(CONFIG_PROFILING) += dcookies.o + +# Do not add any filesystems before this line +obj-$(CONFIG_REISERFS_FS) += reiserfs/ +obj-$(CONFIG_EXT3_FS) += ext3/ # Before ext2 so root fs can be ext3 +obj-$(CONFIG_JBD) += jbd/ +obj-$(CONFIG_EXT2_FS) += ext2/ +obj-$(CONFIG_CRAMFS) += cramfs/ +obj-$(CONFIG_RAMFS) += ramfs/ +obj-$(CONFIG_HUGETLBFS) += hugetlbfs/ +obj-$(CONFIG_CODA_FS) += coda/ +obj-$(CONFIG_MINIX_FS) += minix/ +obj-$(CONFIG_FAT_FS) += fat/ +obj-$(CONFIG_MSDOS_FS) += msdos/ +obj-$(CONFIG_VFAT_FS) += vfat/ +obj-$(CONFIG_BFS_FS) += bfs/ +obj-$(CONFIG_ISO9660_FS) += isofs/ +obj-$(CONFIG_DEVFS_FS) += devfs/ +obj-$(CONFIG_HFSPLUS_FS) += hfsplus/ # Before hfs to find wrapped HFS+ +obj-$(CONFIG_HFS_FS) += hfs/ +obj-$(CONFIG_VXFS_FS) += freevxfs/ +obj-$(CONFIG_NFS_FS) += nfs/ +obj-$(CONFIG_EXPORTFS) += exportfs/ +obj-$(CONFIG_NFSD) += nfsd/ +obj-$(CONFIG_LOCKD) += lockd/ +obj-$(CONFIG_NLS) += nls/ +obj-$(CONFIG_SYSV_FS) += sysv/ +obj-$(CONFIG_SMB_FS) += smbfs/ +obj-$(CONFIG_CIFS) += cifs/ +obj-$(CONFIG_NCP_FS) += ncpfs/ +obj-$(CONFIG_HPFS_FS) += hpfs/ +obj-$(CONFIG_NTFS_FS) += ntfs/ +obj-$(CONFIG_UFS_FS) += ufs/ +obj-$(CONFIG_EFS_FS) += efs/ +obj-$(CONFIG_JFFS_FS) += jffs/ +obj-$(CONFIG_JFFS2_FS) += jffs2/ +obj-$(CONFIG_AFFS_FS) += affs/ +obj-$(CONFIG_ROMFS_FS) += romfs/ +obj-$(CONFIG_QNX4FS_FS) += qnx4/ +obj-$(CONFIG_AUTOFS_FS) += autofs/ +obj-$(CONFIG_AUTOFS4_FS) += autofs4/ +obj-$(CONFIG_ADFS_FS) += adfs/ +obj-$(CONFIG_FUSE_FS) += fuse/ +obj-$(CONFIG_UDF_FS) += udf/ +obj-$(CONFIG_RELAYFS_FS) += relayfs/ +obj-$(CONFIG_SUN_OPENPROMFS) += openpromfs/ +obj-$(CONFIG_JFS_FS) += jfs/ +obj-$(CONFIG_XFS_FS) += xfs/ +obj-$(CONFIG_9P_FS) += 9p/ +obj-$(CONFIG_AFS_FS) += afs/ +obj-$(CONFIG_BEFS_FS) += befs/ +obj-$(CONFIG_HOSTFS) += hostfs/ +obj-$(CONFIG_HPPFS) += hppfs/ +obj-$(CONFIG_DEBUG_FS) += debugfs/ diff -urN oldtree/fs/ecryptfs/Makefile newtree/fs/ecryptfs/Makefile --- oldtree/fs/ecryptfs/Makefile 1970-01-01 00:00:00.000000000 +0000 +++ newtree/fs/ecryptfs/Makefile 2006-02-18 17:24:58.713096808 +0000 @@ -0,0 +1,7 @@ +# +# Makefile for the Linux 2.6 eCryptfs +# + +obj-$(CONFIG_ECRYPTFS) += ecryptfs.o + +ecryptfs-objs := dentry.o file.o inode.o main.o super.o mmap.o crypto.o keystore.o diff -urN oldtree/fs/ecryptfs/crypto.c newtree/fs/ecryptfs/crypto.c --- oldtree/fs/ecryptfs/crypto.c 1970-01-01 00:00:00.000000000 +0000 +++ newtree/fs/ecryptfs/crypto.c 2006-02-18 17:24:58.715096504 +0000 @@ -0,0 +1,955 @@ +/** + * eCryptfs: Linux filesystem encryption layer + * + * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 2001-2004 Stony Brook University + * Copyright (c) 2004 International Business Machines Corp. + * Author(s): Michael A. Halcrow + * Michael C. Thompson + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ecryptfs_kernel.h" + +/** + * Requirement: + * Size of dst buffer needs to be atleast src_size * 2 + */ +inline void ecryptfs_to_hex(char *dst, char *src, int src_size) +{ + int x; + + for (x = 0; x < src_size; x++) + sprintf(&dst[x * 2], "%.2x", (unsigned char)src[x]); +} + +/** + * Requirement: + * Size of src buffer needs to be atleast twice that of dst_size + */ +inline void ecryptfs_from_hex(char *dst, char *src, int dst_size) +{ + int x; + char tmp[3] = { 0, }; + + for (x = 0; x < dst_size; x++) { + tmp[0] = src[x * 2]; + tmp[1] = src[x * 2 + 1]; + dst[x] = (unsigned char)simple_strtol(tmp, NULL, 16); + } +} + +static int iv_mixer; + +/** + * Rotate the initialization vector for an extent. This stirs things + * up to help protect against linear cryptanalysis when an attacker + * may have access to several encryptions based on the same IV. + */ +void ecryptfs_rotate_iv(unsigned char *iv) +{ + int i = (ECRYPTFS_MAX_IV_BYTES - sizeof(iv_mixer)); + int zero_test = 0; + + while ((i -= sizeof(iv_mixer)) >= 0) + zero_test |= ((*((int *)(iv + i))) ^= + (iv_mixer *= (*(int *)(iv + i)))); + while (unlikely(!zero_test)) { + get_random_bytes(iv, ECRYPTFS_MAX_IV_BYTES); + zero_test = 0; + i = ECRYPTFS_MAX_IV_BYTES / sizeof(int); + while (i--) + zero_test |= *((int *)(iv + i)); + } +} + +/** + * Initialize the crypt_stats structure. This involves setting an + * initial IV, indicating how many header pages we have on the file by + * default, initializing the list of raw authentication token packets + * (TODO: deprecated/replaced w/ auth_tok sigs pointing to keyring + * structures), setting the extent size (TODO: this is the page size; + * as it now stands, everything falls apart if the page size is + * anything but 4096), and finally setting a flag to indicate that the + * structure is initialized. + * + * @param crypt_stats Pointer to the crypt_stats struct to + * initialize. + */ +void ecryptfs_init_crypt_stats(struct ecryptfs_crypt_stats *crypt_stats) +{ + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + memset((void *)crypt_stats, 0, sizeof(struct ecryptfs_crypt_stats)); + init_MUTEX(&crypt_stats->iv_sem); + down(&crypt_stats->iv_sem); + get_random_bytes(&crypt_stats->iv, ECRYPTFS_MAX_IV_BYTES); + up(&crypt_stats->iv_sem); + get_random_bytes(&iv_mixer, sizeof(iv_mixer)); + crypt_stats->num_header_pages = 1; /* TODO: Remove with policy */ + crypt_stats->struct_initialized = 1; + ecryptfs_printk(1, KERN_NOTICE, "Exit\n"); +} + +/** + * Releases all memory associated with a crypt_stats struct. + */ +void ecryptfs_destruct_crypt_stats(struct ecryptfs_crypt_stats *crypt_stats) +{ + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + if (crypt_stats->tfm) { + crypto_free_tfm(crypt_stats->tfm); + crypt_stats->tfm = NULL; + } + ecryptfs_printk(1, KERN_NOTICE, "Exit\n"); +} + +/** + * Dump hexadecimal representation of char array + * + * @param data + * @param bytes + */ +void ecryptfs_dump_hex(char *data, int bytes) +{ + int i = 0; + int pretty_print = 1; + + if (bytes != 0) { + printk(KERN_NOTICE "0x%.2x.", (unsigned char)data[i]); + i++; + } + while (i < bytes) { + printk("0x%.2x.", (unsigned char)data[i]); + i++; + if (i % 16 == 0) { + printk("\n"); + pretty_print = 0; + } else + pretty_print = 1; + } + if (pretty_print) + printk("\n"); +} + +/** + * Fills in a scatterlist array with page references for a passed + * virtual address: James Morris + * + * @param addr Virtual address + * @param size Size of data; should be an even multiple of the block + * size + * @param sg Pointer to scatterlist array + * @param sg_size Max array size + * @return Number of scatterlist structs in array used + */ +int virt_to_scatterlist(const void *addr, int size, struct scatterlist *sg, + int sg_size) +{ + int i = 0; + struct page *pg; + int offset; + int remainder_of_page; + + while (size > 0 && i < sg_size) { + pg = virt_to_page(addr); + offset = offset_in_page(addr); + sg[i].page = pg; + sg[i].offset = offset; + remainder_of_page = PAGE_CACHE_SIZE - offset; + if (size >= remainder_of_page) { + sg[i].length = remainder_of_page; + addr += remainder_of_page; + size -= remainder_of_page; + } else { + sg[i].length = size; + addr += size; + size = 0; + } + i++; + } + if (size > 0) + return -ENOMEM; + return i; +} + +/** + * @return Number of bytes encrypted; negative value on error + */ +static int do_encrypt_scatterlist(struct ecryptfs_crypt_stats *crypt_stats, + struct scatterlist *dest_sg, + struct scatterlist *src_sg, int size, + unsigned char *iv) +{ + int rc = 0; + + if (!crypt_stats || !crypt_stats->tfm + || !crypt_stats->struct_initialized) { + ecryptfs_printk(0, KERN_ERR, + "Called w/ invalid crypt_stats state\n"); + rc = -EINVAL; + goto out; + } + ecryptfs_printk(1, KERN_NOTICE, "Key size [%d]; key:\n", + crypt_stats->key_size_bits / 8); + if (ecryptfs_verbosity > 0) + ecryptfs_dump_hex(crypt_stats->key, + crypt_stats->key_size_bits / 8); + rc = crypto_cipher_setkey(crypt_stats->tfm, crypt_stats->key, + crypt_stats->key_size_bits / 8); + if (rc) { + ecryptfs_printk(0, KERN_ERR, "Error setting key; rc = [%d]\n", + rc); + rc = -EINVAL; + goto out; + } + ecryptfs_printk(1, KERN_NOTICE, "Encrypting [%d] bytes.\n", size); + if (crypt_stats->tfm->crt_cipher.cit_mode == CRYPTO_TFM_MODE_ECB) { + crypt_stats->security_warning = 1; + crypto_cipher_encrypt(crypt_stats->tfm, dest_sg, src_sg, size); + } else if (crypt_stats->tfm->crt_cipher.cit_mode + == CRYPTO_TFM_MODE_CFB + || crypt_stats->tfm->crt_cipher.cit_mode + == CRYPTO_TFM_MODE_CBC) + crypto_cipher_encrypt_iv(crypt_stats->tfm, dest_sg, src_sg, + size, iv); + else { + ecryptfs_printk(0, KERN_ERR, + "Unsupported block cipher mode: [%d]\n", + crypt_stats->tfm->crt_cipher.cit_mode); + rc = -ENOSYS; + goto out; + } + rc = size; + /* TODO: crypt_stats->iv size must be equal to the block size; + * verify this*/ +out: + return rc; +} + +int do_encrypt_page_offset(struct ecryptfs_crypt_stats *crypt_stats, + struct page *dest_page, int dest_offset, + struct page *src_page, int src_offset, int size, + unsigned char *iv) +{ + struct scatterlist src_sg[2], dest_sg[2]; + + ecryptfs_printk(1, KERN_NOTICE, "Called with dest_page->index = [%lu], " + "src_page->index = [%lu], dest_offset = [%d], " + "src_offset = [%d]\n", dest_page->index, + src_page->index, dest_offset, src_offset); + src_sg[0].page = src_page; + src_sg[0].offset = src_offset; + src_sg[0].length = size; + dest_sg[0].page = dest_page; + dest_sg[0].offset = dest_offset; + dest_sg[0].length = size; + return do_encrypt_scatterlist(crypt_stats, dest_sg, src_sg, size, iv);; +} + +int +do_encrypt_page(struct ecryptfs_crypt_stats *crypt_stats, + struct page *dest_page, struct page *src_page, + unsigned char *iv) +{ + ecryptfs_printk(1, KERN_NOTICE, "Called with dest_page->index = [%lu] " + "and src_page->index = [%lu]\n", dest_page->index, + src_page->index); + return do_encrypt_page_offset(crypt_stats, dest_page, 0, src_page, 0, + PAGE_CACHE_SIZE, iv); +} + +/** + * Encrypt from a virtual address to a virtual address. + * + * @return + */ +int do_encrypt_virt(struct ecryptfs_crypt_stats *crypt_stats, + char *dest_virt_addr, const char *src_virt_addr, + int size, unsigned char *iv) +{ + /* TODO: 32 is a magic number */ + struct scatterlist src_sg[32]; + struct scatterlist dest_sg[32]; + int rc; + + ecryptfs_printk(1, KERN_NOTICE, "Source:\n"); + if (unlikely(ecryptfs_verbosity > 0)) + ecryptfs_dump_hex((char *)src_virt_addr, size); + rc = virt_to_scatterlist(src_virt_addr, size, src_sg, 32); + if (rc == -ENOMEM) { + ecryptfs_printk(0, KERN_ERR, "do_encrypt_virt: No memory for " + "this operation\n"); + goto out; + + } + rc = virt_to_scatterlist(dest_virt_addr, size, dest_sg, 32); + if (rc == -ENOMEM) { + ecryptfs_printk(0, KERN_ERR, "do_encrypt_virt: No memory for " + "this operation\n"); + goto out; + } + rc = do_encrypt_scatterlist(crypt_stats, dest_sg, src_sg, size, iv); + ecryptfs_printk(1, KERN_NOTICE, "Destination:\n"); + if (unlikely(ecryptfs_verbosity > 0)) + ecryptfs_dump_hex((char *)dest_virt_addr, size); +out: + return rc; +} + +/** + * @return Number of bytes decrypted; negative value on error + */ +static int do_decrypt_scatterlist(struct ecryptfs_crypt_stats *crypt_stats, + struct scatterlist *dest_sg, + struct scatterlist *src_sg, int size, + unsigned char *iv) +{ + int rc = 0; + + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + /* TODO: This should be done when the file is opened */ + ecryptfs_printk(1, KERN_NOTICE, "Key size [%d]; key:\n", + crypt_stats->key_size_bits / 8); + if (unlikely(ecryptfs_verbosity > 0)) + ecryptfs_dump_hex(crypt_stats->key, + crypt_stats->key_size_bits / 8); + rc = crypto_cipher_setkey(crypt_stats->tfm, crypt_stats->key, + crypt_stats->key_size_bits / 8); + if (rc) { + ecryptfs_printk(0, KERN_ERR, "Error setting key; rc = [%d]\n", + rc); + rc = -EINVAL; + goto out; + } + ecryptfs_printk(1, KERN_NOTICE, "Decrypting [%d] bytes.\n", size); + if (crypt_stats->tfm->crt_cipher.cit_mode == CRYPTO_TFM_MODE_ECB) { + rc = crypto_cipher_decrypt(crypt_stats->tfm, dest_sg, src_sg, + size); + + } else if (crypt_stats->tfm->crt_cipher.cit_mode + == CRYPTO_TFM_MODE_CFB + || crypt_stats->tfm->crt_cipher.cit_mode + == CRYPTO_TFM_MODE_CBC) { + rc = crypto_cipher_decrypt_iv(crypt_stats->tfm, dest_sg, src_sg, + size, iv); + if (rc) { + ecryptfs_printk(0, KERN_ERR, "Error decrypting; rc = " + "[%d]\n", rc); + goto out; + } + } else { + ecryptfs_printk(0, KERN_ERR, + "Unsupported block cipher mode: [%d]\n", + crypt_stats->tfm->crt_cipher.cit_mode); + rc = -ENOSYS; + goto out; + } + rc = size; + out: + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc); + return rc; +} + +/** + * @return Number of bytes decrypted + */ +int do_decrypt_page_offset(struct ecryptfs_crypt_stats *crypt_stats, + struct page *dest_page, int dest_offset, + struct page *src_page, int src_offset, int size, + unsigned char *iv) +{ + struct scatterlist src_sg[2], dest_sg[2]; + + src_sg[0].page = src_page; + src_sg[0].offset = src_offset; + src_sg[0].length = size; + dest_sg[0].page = dest_page; + dest_sg[0].offset = dest_offset; + dest_sg[0].length = size; + return do_decrypt_scatterlist(crypt_stats, dest_sg, src_sg, size, iv);; +} + +int +do_decrypt_page(struct ecryptfs_crypt_stats *crypt_stats, + struct page *dest_page, struct page *src_page, + unsigned char *iv) +{ + return do_decrypt_page_offset(crypt_stats, dest_page, 0, src_page, 0, + PAGE_CACHE_SIZE, iv); +} + +#define ECRYPTFS_MAX_SCATTERLIST_LEN 4 + +int do_decrypt_virt(struct ecryptfs_crypt_stats *crypt_stats, + char *dest_virt_addr, const char *src_virt_addr, + int size, unsigned char *iv) +{ + struct scatterlist src_sg[ECRYPTFS_MAX_SCATTERLIST_LEN]; + struct scatterlist dest_sg[ECRYPTFS_MAX_SCATTERLIST_LEN]; + int rc; + + ecryptfs_printk(1, KERN_NOTICE, "Source:\n"); + if (ecryptfs_verbosity > 0) + ecryptfs_dump_hex((char *)src_virt_addr, size); + rc = virt_to_scatterlist(src_virt_addr, size, src_sg, + ECRYPTFS_MAX_SCATTERLIST_LEN); + if (rc == -ENOMEM) { + ecryptfs_printk(0, KERN_ERR, "do_decrypt_virt: No memory for " + "this operation\n"); + goto out; + + } + rc = virt_to_scatterlist(dest_virt_addr, size, dest_sg, + ECRYPTFS_MAX_SCATTERLIST_LEN); + if (rc == -ENOMEM) { + ecryptfs_printk(0, KERN_ERR, "do_decrypt_virt: No memory for " + "this operation\n"); + goto out; + } + rc = do_decrypt_scatterlist(crypt_stats, dest_sg, src_sg, size, iv); + ecryptfs_printk(1, KERN_NOTICE, "Destination:\n"); + if (unlikely(ecryptfs_verbosity > 0)) + ecryptfs_dump_hex((char *)dest_virt_addr, size); +out: + return rc; +} + +/** + * Initialize the crypto context + * + * TODO: Performance: Keep a cache of initialized cipher contexts; + * only init if needed + */ +int ecryptfs_init_crypt_ctx(struct ecryptfs_crypt_stats *crypt_stats) +{ + int rc = -EINVAL; + + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + if (crypt_stats->cipher == NULL) { + ecryptfs_printk(1, KERN_NOTICE, "No cipher specified\n"); + goto out; + } + ecryptfs_printk(1, KERN_NOTICE, + "Initializing cipher [%s]; strlen = [%d]\n", + crypt_stats->cipher, (int)strlen(crypt_stats->cipher)); + if (crypt_stats->tfm != NULL) { + ecryptfs_printk(1, KERN_WARNING, "Crypto context already " + "initialized\n"); + goto out; + } + crypt_stats->tfm = crypto_alloc_tfm(crypt_stats->cipher, + CRYPTO_TFM_MODE_CBC); + if (crypt_stats->tfm == NULL) { + ecryptfs_printk(0, KERN_ERR, "cryptfs: init_crypt_ctx(): Error " + "initializing cipher [%s]\n", + crypt_stats->cipher); + goto out; + } + rc = 0; +out: + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc); + return rc; +} + +static inline pgoff_t records_per_page(struct ecryptfs_crypt_stats *crypt_stats) +{ + return (crypt_stats->extent_size / crypt_stats->iv_bytes); +} + +void ecryptfs_set_default_sizes(struct ecryptfs_crypt_stats *crypt_stats) +{ + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + /* Default values; may be overwritten as we are parsing the + * packets. */ + crypt_stats->extent_size = PAGE_SIZE; + crypt_stats->iv_bytes = ECRYPTFS_DEFAULT_IV_BYTES; + crypt_stats->records_per_page = records_per_page(crypt_stats); + ecryptfs_printk(1, KERN_NOTICE, "Exit\n"); +} + +/** + * Default values in the event that policy does not override them. + */ +static void +ecryptfs_set_default_crypt_stats_vals(struct ecryptfs_crypt_stats *crypt_stats) +{ + int key_size_bits = ECRYPTFS_DEFAULT_KEY_BYTES * 8; + + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + strcpy(crypt_stats->cipher, ECRYPTFS_DEFAULT_CIPHER); + get_random_bytes(crypt_stats->key, key_size_bits / 8); + crypt_stats->key_size_bits = key_size_bits; + crypt_stats->key_valid = 1; + ecryptfs_printk(1, KERN_NOTICE, "Generated new session key:\n"); + if (unlikely(ecryptfs_verbosity > 0)) + ecryptfs_dump_hex(crypt_stats->key, + crypt_stats->key_size_bits / 8); + ecryptfs_set_default_sizes(crypt_stats); + ecryptfs_printk(1, KERN_NOTICE, "Exit\n"); +} + +/** + * If the crypto context for the file has not yet been established, + * this is where we do that. Establishing a new crypto context + * involves the following decisions: + * - What cipher to use? + * - What set of authentication tokens to use? + * Here we just worry about getting enough information into the + * authentication tokens so that we know that they are available. + * We associate the available authentication tokens with the new file + * via the set of signatures in the crypt_stats struct. Later, when + * the headers are actually written out, we may again defer to + * userspace to perform the encryption of the session key; for the + * foreseeable future, this will be the case with public key packets. + * + * @param ecryptfs_dentry + * @return Zero on success; non-zero otherwise + */ +/* Associate an authentication token(s) with the file */ +int ecryptfs_new_file_context(struct dentry *ecryptfs_dentry) +{ + int rc = 0; + struct ecryptfs_crypt_stats *crypt_stats = + &ECRYPTFS_INODE_TO_PRIVATE(ecryptfs_dentry->d_inode)->crypt_stats; + struct ecryptfs_mount_crypt_stats *mount_crypt_stats = + &(ECRYPTFS_SUPERBLOCK_TO_PRIVATE( + ecryptfs_dentry->d_sb)->mount_crypt_stats); + int cipher_name_len; + + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + ecryptfs_set_default_crypt_stats_vals(crypt_stats); + /* See if there are mount crypt options */ + if (mount_crypt_stats->global_auth_tok) { + ecryptfs_printk(1, KERN_NOTICE, "Initializing context for new " + "file using mount_crypt_stats\n"); + crypt_stats->encrypted = 1; + crypt_stats->key_valid = 1; + memcpy(crypt_stats->keysigs[crypt_stats->num_keysigs++], + mount_crypt_stats->global_auth_tok_sig, + ECRYPTFS_SIG_SIZE_HEX); + cipher_name_len = + strlen(mount_crypt_stats->global_default_cipher_name); + memcpy(crypt_stats->cipher, + mount_crypt_stats->global_default_cipher_name, + cipher_name_len); + crypt_stats->cipher[cipher_name_len] = '\0'; + } else + /* We should not encounter this scenario since we + * should detect lack of global_auth_tok at mount time + * TODO: Applies to 0.1 release only; remember to + * remove in future release */ + BUG(); + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc); + return rc; +} + +/** + * @return One if marker found; zero if not found + */ +int contains_ecryptfs_marker(char *data) +{ + u32 m_1, m_2, ver; + + memcpy(&m_1, data, 4); + memcpy(&m_2, (data + 4), 4); + ver = (m_2 ^ (m_1 ^ MAGIC_ECRYPTFS_MARKER)); + /* There is a 2^(32-4) chance for each file that a + * non-eCryptfs file will be start to be mistaken for an + * eCryptfs file */ + if (ver > 15) + ver = 0; + return (int)ver; +} + +/** + * Marker = 0x3c81b7f5 + */ +static int write_ecryptfs_marker(char *page_virt) +{ + u32 m_1, m_2, ver; + int version = 1; + + ver = (u32)version; + get_random_bytes(&m_1, (MAGIC_ECRYPTFS_MARKER_SIZE_BYTES / 2)); + memcpy(page_virt, &m_1, (MAGIC_ECRYPTFS_MARKER_SIZE_BYTES / 2)); + m_2 = ((m_1 ^ ver) ^ MAGIC_ECRYPTFS_MARKER); + memcpy(page_virt + (MAGIC_ECRYPTFS_MARKER_SIZE_BYTES / 2), &m_2, + (MAGIC_ECRYPTFS_MARKER_SIZE_BYTES / 2)); + return MAGIC_ECRYPTFS_MARKER_SIZE_BYTES; +} + +/** + * @return Zero on no match + */ +u16 ecryptfs_code_for_cipher_string(char *str) +{ + u16 rc = 0; + if (strcmp(str, "des3_ede") == 0) { + rc = 0x02; + } else if (strcmp(str, "cast5") == 0) { + rc = 0x03; + } else if (strcmp(str, "blowfish") == 0) { + rc = 0x04; + } else if (strcmp(str, "aes") == 0) { + rc = 0x07; + } + return rc; +} + +/** + * @return Zero on success + */ +int ecryptfs_cipher_code_to_string(char *str, u16 cipher_code) +{ + int rc = 0; + switch(cipher_code) { + case 0x01: + /* IDEA not supported */ + str[0] = '\0'; + ecryptfs_printk(0, KERN_WARNING, "Cipher code not supported: " + "[%d]\n", cipher_code); + rc = -ENOSYS; + break; + case 0x02: + /* Choose Triple-DES */ + strcpy(str, "des3_ede"); + break; + case 0x03: + /* Choose CAST5 */ + strcpy(str, "cast5"); + break; + case 0x04: + /* Choose blowfish */ + strcpy(str, "blowfish"); + break; + case 0x07: + /* Choose AES-128 */ + strcpy(str, "aes"); + break; + case 0x08: + /* Choose AES-192 */ + strcpy(str, "aes"); + break; + case 0x09: + /* Choose AES-256 */ + strcpy(str, "aes"); + break; + default: + str[0] = '\0'; + ecryptfs_printk(0, KERN_WARNING, "Cipher code not recognized: " + "[%d]\n", cipher_code); + rc = -EINVAL; + } + return rc; +} + +/** + * @return Zero on success; non-zero otherwise + */ +int ecryptfs_read_header_region(char *data, struct dentry *dentry, + struct nameidata *nd) +{ + int rc = 0; + struct vfsmount *mnt = NULL; + struct file *file = NULL; + mm_segment_t oldfs; + + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + mnt = mntget(nd->mnt); + file = dentry_open(dentry, mnt, O_RDONLY); + if (IS_ERR(file)) { + ecryptfs_printk(1, KERN_NOTICE, "Error opening file to " + "determine interpolated filesize\n"); + mntput(mnt); + rc = PTR_ERR(file); + goto out; + } + if (!file || !file->f_op || !file->f_op->read) { + ecryptfs_printk(1, KERN_NOTICE, "File has no read op\n"); + rc = -EINVAL; + goto out; + } + file->f_pos = 0; + oldfs = get_fs(); + set_fs(get_ds()); + rc = file->f_op->read(file, (char __user *)data, + PAGE_CACHE_SIZE, &file->f_pos); + set_fs(oldfs); + fput(file); + rc = 0; +out: + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n",rc); + return rc; +} + +kmem_cache_t *ecryptfs_header_cache_0; +kmem_cache_t *ecryptfs_header_cache_1; +kmem_cache_t *ecryptfs_header_cache_2; + +/** + * The file size is written along a different execution path than the + * rest of the headers; we treat it as a special case in eCryptfs. + * + * Proposed header format is in flux; this is the current proposal for + * later versions of eCryptfs, for compatibility: + * + * BYTE RANGE: DESCRIPTION + * ----------------------- + * 00 - 07: File size + * 08 - 11: Random value (M_1) + * 12 - 15: M_1 ^ VER ^ MARKER + * 16 - 16: Hash identifier + * 17 - 18: Cipher identifier + * 19 - 19: Number of header pages + * 20 - 63: Reserved + * 64 - ..: RFC2440 packet set + * + * @return Zero on success + */ +int ecryptfs_write_headers_virt(char *page_virt, + struct ecryptfs_crypt_stats *crypt_stats, + struct dentry *ecryptfs_dentry, int version) +{ + int ecryptfs_marker_len; + int rc = 0; + int written; + int offset; + + offset = ECRYPTFS_FILE_SIZE_BYTES; + ecryptfs_marker_len = write_ecryptfs_marker(page_virt + offset); + offset += ecryptfs_marker_len; + rc = ecryptfs_generate_key_packet_set((page_virt + offset), crypt_stats, + ecryptfs_dentry, &written); + if (rc) + ecryptfs_printk(0, KERN_WARNING, "Error generating key packet " + "set; rc = [%d]\n", rc); + return rc; +} + +/** + * Write the file headers out. This will likely involve a userspace + * callout, in which the session key is encrypted with one or more + * public keys and/or the passphrase necessary to do the encryption is + * retrieved via a prompt. Exactly what happens at this point should + * be policy-dependent. + * + * @param lower_file The lower file struct, which was returned from + * dentry_open + * @return Zero on success; non-zero on error + */ +int ecryptfs_write_headers(struct dentry *ecryptfs_dentry, + struct file *lower_file) +{ + int rc = 0; + char *page_virt; + struct ecryptfs_crypt_stats *crypt_stats; + mm_segment_t oldfs; + + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + crypt_stats = &ECRYPTFS_INODE_TO_PRIVATE( + ecryptfs_dentry->d_inode)->crypt_stats; + if (likely(1 == crypt_stats->encrypted)) { + if (!crypt_stats->key_valid) { + ecryptfs_printk(1, KERN_NOTICE, "Key is " + "invalid; bailing out\n"); + rc = -EINVAL; + goto out; + } + } else { + rc = -EINVAL; + ecryptfs_printk(0, KERN_WARNING, + "Called with crypt_stats->encrypted == 0\n"); + goto out; + } + /* Released in this function */ + page_virt = kmem_cache_alloc(ecryptfs_header_cache_0, SLAB_USER); + if (!page_virt) { + ecryptfs_printk(0, KERN_ERR, "Out of memory\n"); + return -ENOMEM; + } + rc = ecryptfs_write_headers_virt(page_virt, crypt_stats, + ecryptfs_dentry, + ECRYPTFS_FILE_VERSION); + if (unlikely(rc != 0)) { + ecryptfs_printk(0, KERN_ERR, "Error whilst writing headers\n"); + goto out_free; + } + rc = 0; + ecryptfs_printk(1, KERN_NOTICE, + "Writing key packet set to underlying file\n"); + lower_file->f_pos = 0; + oldfs = get_fs(); + set_fs(get_ds()); + lower_file->f_op->write(lower_file, (char __user *)page_virt, + PAGE_CACHE_SIZE, &lower_file->f_pos); + set_fs(oldfs); + ecryptfs_printk(1, KERN_NOTICE, + "Done writing key packet set to underlying file.\n"); +out_free: + kmem_cache_free(ecryptfs_header_cache_0, page_virt); +out: + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc); + return rc; +} + +/** + * @return Zero on success + */ +int ecryptfs_read_headers_virt(char *page_virt, + struct ecryptfs_crypt_stats* crypt_stats, + struct dentry *ecryptfs_dentry) +{ + int rc = 0; + int offset; + int version; + + offset = ECRYPTFS_FILE_SIZE_BYTES; + version = contains_ecryptfs_marker(page_virt + offset); + if (version == 0) + ecryptfs_printk(0, KERN_WARNING, "Valid eCryptfs marker not " + "found\n"); + offset += MAGIC_ECRYPTFS_MARKER_SIZE_BYTES; + rc = ecryptfs_parse_packet_set((page_virt + offset), crypt_stats, + ecryptfs_dentry, version); + return rc; +} + +/** + * @return Zero if valid headers found and parsed; non-zero otherwise + */ +int ecryptfs_read_headers(struct dentry *ecryptfs_dentry, + struct file *lower_file) +{ + int rc = 0; + char *page_virt; + mm_segment_t oldfs; + ssize_t bytes_read; + struct ecryptfs_crypt_stats *crypt_stats = + &ECRYPTFS_INODE_TO_PRIVATE(ecryptfs_dentry->d_inode)->crypt_stats; + + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + /* Read the first page from the underlying file */ + page_virt = kmem_cache_alloc(ecryptfs_header_cache_1, SLAB_USER); + if (!page_virt) { + rc = -ENOMEM; + ecryptfs_printk(0, KERN_ERR, "Unable to allocate page_virt\n"); + goto out; + } + lower_file->f_pos = 0; + oldfs = get_fs(); + set_fs(get_ds()); + bytes_read = + lower_file->f_op->read(lower_file, (char __user *)page_virt, + PAGE_CACHE_SIZE, &lower_file->f_pos); + set_fs(oldfs); + if (bytes_read != PAGE_CACHE_SIZE) { + rc = -EINVAL; + ecryptfs_printk(0, KERN_ERR, "Expected size of header not read." + "Instead [%d] bytes were read\n", bytes_read); + goto out; + } + rc = ecryptfs_read_headers_virt(page_virt, crypt_stats, + ecryptfs_dentry); + kmem_cache_free(ecryptfs_header_cache_1, page_virt); + if (rc) { + ecryptfs_printk(1, KERN_NOTICE, "File not encrypted\n"); + rc = -EINVAL; + goto out; + } +out: + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc); + return rc; +} + +/** + * N.B. The concept of encoded filenames does not apply for 0.1 release + * + * Encrypts and encodes a filename into something that constitutes a + * valid filename for a filesystem, with printable characters. + * + * We assume that we have a properly initialized crypto context, + * pointed to by crypt_stats->tfm. + * + * TODO: Implement filename encryption and encoding here, in place of + * memcpy. + * + * @return Length of encoded filename; negative if error + */ +int +ecryptfs_encode_filename(const char *name, int length, char **encoded_name, + int skip_dots, + struct ecryptfs_crypt_stats *crypt_stats) +{ + int error = 0; + + ecryptfs_printk(1, KERN_NOTICE, "Enter; length = [%d]\n", length); + (*encoded_name) = kmalloc(length + 2, GFP_KERNEL); + if (!(*encoded_name)) { + error = -ENOMEM; + goto out; + } + memcpy((void *)(*encoded_name), (void *)name, length); + (*encoded_name)[length] = '\0'; + error = length + 1; + out: + ecryptfs_printk(1, KERN_NOTICE, "Exit; error = [%d]\n", error); + return error; +} + +/** + * N.B. The concept of encoded filenames does not apply for 0.1 release + * + * Decrypts and decodes the filename + * + * TODO: Implement filename decoding and decryption here, in place of + * memcpy. + * + * @return Length of decoded filename; negative if error + */ +int +ecryptfs_decode_filename(const char *name, int length, char **decrypted_name, + int skip_dots, + struct ecryptfs_crypt_stats *crypt_stats) +{ + int error = 0; + + ecryptfs_printk(1, KERN_NOTICE, "Enter; length = [%d]\n", length); + (*decrypted_name) = kmalloc(length + 2, GFP_KERNEL); + if (!(*decrypted_name)) { + error = -ENOMEM; + goto out; + } + memcpy((void *)(*decrypted_name), (void *)name, length); + (*decrypted_name)[length + 1] = '\0'; /* Only for convenience + * in printing out the + * string in debug + * messages */ + error = length; +out: + ecryptfs_printk(1, KERN_NOTICE, "Exit; error = [%d]\n", error); + return error; +} diff -urN oldtree/fs/ecryptfs/dentry.c newtree/fs/ecryptfs/dentry.c --- oldtree/fs/ecryptfs/dentry.c 1970-01-01 00:00:00.000000000 +0000 +++ newtree/fs/ecryptfs/dentry.c 2006-02-18 17:24:58.716096352 +0000 @@ -0,0 +1,105 @@ +/** + * eCryptfs: Linux filesystem encryption layer + * + * Copyright (c) 1997-2003 Erez Zadok + * Copyright (c) 2001-2003 Stony Brook University + * Copyright (c) 2004 International Business Machines Corp. + * Author(s): Michael A. Halcrow + * + * 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 +#include +#include "ecryptfs_kernel.h" + +/** + * 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. + * + * + * @param dentry ecryptfs dentry + * @param nd + * @return 1 if valid, 0 otherwise + */ +static int ecryptfs_d_revalidate(struct dentry *dentry, struct nameidata *nd) +{ + int err = 1; + struct dentry *lower_dentry; + struct dentry *saved_dentry; + struct vfsmount *saved_vfsmount; + + ecryptfs_printk(1, KERN_NOTICE, "Enter; dentry->d_name.name = [%s]\n", + dentry->d_name.name); + lower_dentry = ecryptfs_lower_dentry(dentry); + if (!lower_dentry) { + err = 0; + goto out; + } + if (!lower_dentry->d_op || !lower_dentry->d_op->d_revalidate) + goto out; + /* Call the lower dentry's d_revalidate (assuming it has one) */ + saved_dentry = nd->dentry; + saved_vfsmount = nd->mnt; + nd->dentry = lower_dentry; + nd->mnt = ECRYPTFS_SUPERBLOCK_TO_PRIVATE(dentry->d_sb)->lower_mnt; + err = lower_dentry->d_op->d_revalidate(lower_dentry, nd); + nd->dentry = saved_dentry; + nd->mnt = saved_vfsmount; +out: + ecryptfs_printk(1, KERN_NOTICE, "Exit; err = [%d]\n", err); + return err; +} + +kmem_cache_t *ecryptfs_dentry_info_cache; + +/* Notes: + * Called when a dentry is really deallocated + * Sanity check? wrapper around ecryptfs_dput() + */ +static void ecryptfs_d_release(struct dentry *dentry) +{ + struct dentry *lower_dentry; + + ecryptfs_printk(1, KERN_NOTICE, "Enter; dentry->d_name->name = [%s]\n", + dentry->d_name.name); + if (!dentry) { + ecryptfs_printk(0, KERN_ERR, "NULL dentry\n"); + goto out; + } + if (!ECRYPTFS_DENTRY_TO_PRIVATE(dentry)) { + ecryptfs_printk(1, KERN_ERR, "dentry without private data: " + "[%*s]\n", dentry->d_name.len, + dentry->d_name.name); + goto out; + } + 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); +out: + ecryptfs_printk(1, KERN_NOTICE, "Exit\n"); + return; +} + +struct dentry_operations ecryptfs_dops = { + .d_revalidate = ecryptfs_d_revalidate, + .d_release = ecryptfs_d_release, +}; diff -urN oldtree/fs/ecryptfs/ecryptfs_kernel.h newtree/fs/ecryptfs/ecryptfs_kernel.h --- oldtree/fs/ecryptfs/ecryptfs_kernel.h 1970-01-01 00:00:00.000000000 +0000 +++ newtree/fs/ecryptfs/ecryptfs_kernel.h 2006-02-18 17:24:58.717096200 +0000 @@ -0,0 +1,430 @@ +/** + * eCryptfs: Linux filesystem encryption layer + * Kernel declarations. + * + * Copyright (c) 1997-2003 Erez Zadok + * Copyright (c) 2001-2003 Stony Brook University + * Copyright (c) 2004 International Business Machines Corp. + * Author(s): Michael A. Halcrow + * + * 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 +#include +#include + +#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 16 +#define ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES 512 +#define ECRYPTFS_DEFAULT_IV_BYTES 8 +#define ECRYPTFS_FILE_VERSION 0x01 + +/** + * 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 0x01 +#define ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT 0x02 +#define ECRYPTFS_CONTAINS_DECRYPTED_KEY 0x04 +#define ECRYPTFS_CONTAINS_ENCRYPTED_KEY 0x08 + int32_t flags; + int32_t encrypted_key_size; + int32_t decrypted_key_size; + uint8_t decrypted_key[ECRYPTFS_MAX_KEY_BYTES]; + uint8_t encrypted_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES]; +}; + +/** + * States for this struct are: + * - Uninstantiated: no password, but the salt, encrypted session + * key, etc. are filled in + * - Uninstantiated: no password, salt, etc. are filled in; at this + * time, there is no reason for the auth_tok to exist and be in + * this state + * - Instantiated: password, but no salt, and hence no encrypted + * session key, etc. eCryptfs will generate those items when the + * time come to write out the headers to disk + * - Instantiated: password, salt, encrypted session key, etc. This + * is typically used to actually obtain the session key for the + * file + */ +struct ecryptfs_password { + int32_t saltless; /* If set, this is the ``seed'' token + * from which other salted tokens are + * derived. Note that this is _not_ + * the same as a token that just has + * not received its salt yet. */ + int32_t password_size; + int32_t hash_algo; + int32_t hash_iterations; + int32_t session_key_encryption_key_size; /* In bytes */ + int32_t session_key_encryption_key_set; + uint8_t salt[ECRYPTFS_SALT_SIZE]; + /* Always in expanded hex */ + uint8_t signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1]; + uint8_t password[ECRYPTFS_MAX_PASSWORD_LENGTH]; + /* Iterated-hash concatenation of salt and passphrase */ + uint8_t session_key_encryption_key[ECRYPTFS_MAX_KEY_BYTES]; +}; + +/* May be a password or a private key */ +struct ecryptfs_auth_tok { + int32_t instantiated; /* When not instantiated, this struct only + * contains enough information to construct + * the file headers, which contain token + * descriptors */ +#define ECRYPTFS_PASSWORD 0 +#define ECRYPTFS_PRIVATE_KEY 1 + int32_t instanceof; + int32_t expired; + uid_t uid; + int64_t creation_time; + int64_t expiration_time; + /* This is in case we want userspace to extract the session + * key */ + struct ecryptfs_session_key session_key; + union { + struct ecryptfs_password password; + /* Private key is in future eCryptfs releases */ + } token; +}; + +void dump_auth_tok(struct ecryptfs_auth_tok *auth_tok); +extern void ecryptfs_to_hex(char *dst, char *src, int src_size); +extern void ecryptfs_from_hex(char *dst, char *src, int dst_size); + +/* For ecryptfs_auth_tok_packet_set.packet_set_type */ +#define ECRYPTFS_PACKET_SET_TYPE_PASSWORD 0 + +#ifndef ECRYPTFS_DEBUG +#define ECRYPTFS_DEBUG 1 +#endif + +/* See RFC 2440 */ +struct ecryptfs_key_record { + unsigned char type; + unsigned char sig[ECRYPTFS_SIG_SIZE]; + u16 enc_key_size_bits; + unsigned char enc_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES]; +}; +#define KEY_REC_SIZE(key_rec) \ + ( sizeof(struct ecryptfs_key_record) - ECRYPTFS_MAX_KEY_BYTES \ + + key_rec.enc_key_size_bits/8 ) + +/* TODO: kref */ +struct ecryptfs_auth_tok_list { + struct ecryptfs_auth_tok *auth_tok; + struct list_head list; +}; + +/* Structure prototypes */ +struct ecryptfs_crypt_stats; +struct ecryptfs_mount_crypt_stats; + +#define KEY_PAYLOAD_DATA(key) \ + ( ((struct user_key_payload*)key->payload.data)->data) +#define KEY_PAYLOAD_LEN(key) \ + ( ((struct user_key_payload*)key->payload.data)->datalen) + +#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 64 /* TODO: make it a list */ +#define ECRYPTFS_MAX_IV_BYTES 8 /* 64 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 "blowfish" +#define ECRYPTFS_DEFAULT_KEY_BYTES 16 + +/** + * IV_SIZE (i.e., 8 bytes) + * HMAC_SIZE (i.e., 20 bytes) + * IVS_PER_PAGE (i.e., 512) + * IVS_PER_PAGE * HMAC_SIZE = TOTAL_SIZE_PER_EXTENT; 512 * 20 = 10240 + * TOTAL_SIZE_PER_EXTENT / PAGE_SIZE = NUM_PAGES_FOR_HMACS = 2.5 + * PIIHHHHHDx1024IIHHHHHDx1024... + * PIDx512IDx512... + * Or: [Ix146+Hx146] (wastes 8 bytes per page) + */ + +/** + * This is the primary struct associated with each encrypted file. + * + * TODO: cache align/pack? + */ +struct ecryptfs_crypt_stats { + int struct_initialized; + int policy_applied; + int new_file; + int encrypted; + int security_warning; /* This flag is set if something happens + * that could weaken the security of the + * file */ + int iv_bytes; /* Set to 0 if encryption not enabled */ + int records_per_page; /* extent_size / (iv_bytes + hmac_bytes) */ +#define ECRYPTFS_IV_ROTATE_NO 0 +#define ECRYPTFS_IV_ROTATE_INCREMENT 1 +#define ECRYPTFS_IV_ROTATE_PERMUTATE 2 +#define ECRYPTFS_IV_ROTATE_RANDOM 3 + int rotate_iv; /* Whether or not to rotate the IV on each + * write (performance vs. security tradeoff) */ + int encrypt_iv_pages; /* To attempt to hide sparse regions? */ + int num_keysigs; + int num_header_pages; /* Number of pages that comprise the + * header */ + int extent_size; + int key_size_bits; + int key_valid; + struct crypto_tfm *tfm; + unsigned char iv[ECRYPTFS_MAX_IV_BYTES]; + unsigned char key[ECRYPTFS_MAX_KEY_BYTES]; + char cipher[ECRYPTFS_MAX_CIPHER_NAME_SIZE]; + struct semaphore iv_sem; + char keysigs[ECRYPTFS_MAX_NUM_KEYSIGS][ECRYPTFS_SIG_SIZE_HEX]; +}; + +/* inode private data. */ +struct ecryptfs_inode_info { + struct inode *wii_inode; + struct ecryptfs_crypt_stats crypt_stats; + struct inode vfs_inode; +}; + +/* dentry private data. */ +struct ecryptfs_dentry_info { + struct dentry *wdi_dentry; + struct ecryptfs_crypt_stats *crypt_stats; +}; + +/** + * 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_stats { + /* N.B. Pointers to memory we do not own, do not free these */ + struct ecryptfs_auth_tok *global_auth_tok; + struct key *global_auth_tok_key; + char global_auth_tok_sig[ECRYPTFS_SIG_SIZE_HEX + 1]; + char global_default_cipher_name[ECRYPTFS_MAX_CIPHER_NAME_SIZE + 1]; +}; + +/* super block private data. */ +struct ecryptfs_sb_info { + struct super_block *wsi_sb; + struct vfsmount *lower_mnt; + struct ecryptfs_mount_crypt_stats mount_crypt_stats; +}; + +/* file private data. */ +struct ecryptfs_file_info { + struct file *wfi_file; + struct ecryptfs_crypt_stats *crypt_stats; +}; + +/* auth_tok <=> encrypted_session_key mappings */ +struct ecryptfs_auth_tok_list_item { + struct list_head list; + struct ecryptfs_auth_tok auth_tok; + char encrypted_session_key[ECRYPTFS_MAX_KEY_BYTES]; +}; + +extern inline pgoff_t +ecryptfs_pg_idx_to_lwr_pg_idx(struct ecryptfs_crypt_stats *crypt_stats, + pgoff_t idx) +{ + return ((idx / crypt_stats->records_per_page) + idx + + crypt_stats->num_header_pages + 1); +} + +#ifndef DEFAULT_POLLMASK +#define DEFAULT_POLLMASK (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM) +#endif /* ndef DEFAULT_POLLMASK */ + +#define OBSERVE_ASSERTS 1 +#ifdef OBSERVE_ASSERTS +#define ASSERT(EX) \ +do { \ + if (unlikely(!(EX))) { \ + printk(KERN_CRIT "ASSERTION FAILED: %s at %s:%d (%s)\n", #EX, \ + __FILE__, __LINE__, __FUNCTION__); \ + BUG(); \ + } \ +} while (0) +#else +#define ASSERT(EX) ; +#endif /* OBSERVE_ASSERTS */ + +/** + * Halcrow: What does the kernel VFS do to ensure that there is no + * contention for file->private_data? + */ +#define ECRYPTFS_FILE_TO_PRIVATE(file) ((struct ecryptfs_file_info *) \ + ((file)->private_data)) +#define ECRYPTFS_FILE_TO_PRIVATE_SM(file) ((file)->private_data) +#define ECRYPTFS_FILE_TO_LOWER(file) \ + ((ECRYPTFS_FILE_TO_PRIVATE(file))->wfi_file) +#define ECRYPTFS_INODE_TO_PRIVATE(ino) ((struct ecryptfs_inode_info *) \ + (ino)->u.generic_ip) +#define ECRYPTFS_INODE_TO_PRIVATE_SM(ino) ((ino)->u.generic_ip) +#define ECRYPTFS_INODE_TO_LOWER(ino) (ECRYPTFS_INODE_TO_PRIVATE(ino)->wii_inode) +#define ECRYPTFS_SUPERBLOCK_TO_PRIVATE(super) ((struct ecryptfs_sb_info *) \ + (super)->s_fs_info) +#define ECRYPTFS_SUPERBLOCK_TO_PRIVATE_SM(super) ((super)->s_fs_info) +#define ECRYPTFS_SUPERBLOCK_TO_LOWER(super) \ + (ECRYPTFS_SUPERBLOCK_TO_PRIVATE(super)->wsi_sb) +#define ECRYPTFS_DENTRY_TO_PRIVATE_SM(dentry) ((dentry)->d_fsdata) +#define ECRYPTFS_DENTRY_TO_PRIVATE(dentry) ((struct ecryptfs_dentry_info *) \ + (dentry)->d_fsdata) +#define ECRYPTFS_DENTRY_TO_LOWER(dentry) \ + (ECRYPTFS_DENTRY_TO_PRIVATE(dentry)->wdi_dentry) + +/* + * Flags for ecryptfs_{en,de}code_filename + * DO_DOTS means the special entries . and .. should be encoded (for symlink) + * SKIP_DOTS means they should be preserved intact + */ +#define ECRYPTFS_DO_DOTS 0 +#define ECRYPTFS_SKIP_DOTS 1 + +/** + * EXTERNALS: + */ +extern struct file_operations ecryptfs_main_fops; +extern 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; +struct ecryptfs_key_record; +struct ecryptfs_auth_tok; + +/** + * TODO: Make eCryptfs's memory usage as lean as possible + */ +extern kmem_cache_t *ecryptfs_auth_tok_list_item_cache; +extern kmem_cache_t *ecryptfs_file_info_cache; +extern kmem_cache_t *ecryptfs_dentry_info_cache; +extern kmem_cache_t *ecryptfs_inode_info_cache; +extern kmem_cache_t *ecryptfs_sb_info_cache; +extern kmem_cache_t *ecryptfs_header_cache_0; +extern kmem_cache_t *ecryptfs_header_cache_1; +extern kmem_cache_t *ecryptfs_header_cache_2; +extern kmem_cache_t *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(const char *name, int length, + char **decrypted_name, int skip_dots, + struct ecryptfs_crypt_stats *crypt_stats); +int ecryptfs_encode_filename(const char *name, int length, + char **encoded_name, int skip_dots, + struct ecryptfs_crypt_stats *crypt_stats); +struct dentry *ecryptfs_lower_dentry(struct dentry *this_dentry); + +void ecryptfs_copy_attr_times(struct inode *dest, const struct inode *src); +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); + +#define ecryptfs_printk(verb, type, fmt, arg...) \ + __ecryptfs_printk((verb), type "%s: " fmt, __FUNCTION__, ## arg); +void __ecryptfs_printk(int verb, const char *fmt, ...); + +extern int ecryptfs_verbosity; + +/* crypto */ +void ecryptfs_dump_hex(char *data, int bytes); +int virt_to_scatterlist(const void *addr, int size, struct scatterlist *sg, + int sg_size); +void ecryptfs_rotate_iv(unsigned char *iv); +void ecryptfs_init_crypt_stats(struct ecryptfs_crypt_stats *crypt_stats); +void ecryptfs_destruct_crypt_stats(struct ecryptfs_crypt_stats *crypt_stats); +int ecryptfs_init_crypt_ctx(struct ecryptfs_crypt_stats *crypt_stats); +int ecryptfs_write_inode_size_to_header(struct file *lower_file, + struct inode *lower_inode, + struct inode *inode); +int do_encrypt_page(struct ecryptfs_crypt_stats *crypt_stats, + struct page *dest_page, struct page *src_page, + unsigned char *iv); +int do_encrypt_page_offset(struct ecryptfs_crypt_stats *crypt_stats, + struct page *dest_page, int dest_offset, + struct page *src_page, int src_offset, int size, + unsigned char *iv); +int do_decrypt_page(struct ecryptfs_crypt_stats *crypt_stats, + struct page *dest_page, struct page *src_page, + unsigned char *iv); +int do_decrypt_page_offset(struct ecryptfs_crypt_stats *crypt_stats, + struct page *dest_page, int dest_offset, + struct page *src_page, int src_offset, int size, + unsigned char *iv); +int do_encrypt_virt(struct ecryptfs_crypt_stats *crypt_stats, + char *dest_virt_addr, const char *src_virt_addr, + int size, unsigned char *iv); +int do_decrypt_virt(struct ecryptfs_crypt_stats *crypt_stats, + char *dest_virt_addr, const char *src_virt_addr, + int size, unsigned char *iv); +int ecryptfs_write_headers(struct dentry *ecryptfs_dentry, + struct file *lower_file); +int ecryptfs_write_headers_virt(char *page_virt, + struct ecryptfs_crypt_stats *crypt_stats, + struct dentry *ecryptfs_dentry, int version); +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 nameidata *nd); +u16 ecryptfs_code_for_cipher_string(char *str); +int ecryptfs_cipher_code_to_string(char *str, u16 cipher_code); +void ecryptfs_set_default_sizes(struct ecryptfs_crypt_stats *crypt_stats); +int ecryptfs_generate_key_packet_set(char *dest_base, + struct ecryptfs_crypt_stats *crypt_stats, + struct dentry *ecryptfs_dentry, int *len); +int process_request_key_err(long err_code); +int ecryptfs_parse_packet_set(unsigned char *dest, + struct ecryptfs_crypt_stats *crypt_stats, + struct dentry *ecryptfs_dentry, int version); + +/* inode.c */ +int ecryptfs_truncate(struct dentry *dentry, loff_t new_length); + +#endif /* ndef ECRYPTFS_KERNEL_H */ diff -urN oldtree/fs/ecryptfs/file.c newtree/fs/ecryptfs/file.c --- oldtree/fs/ecryptfs/file.c 1970-01-01 00:00:00.000000000 +0000 +++ newtree/fs/ecryptfs/file.c 2006-02-18 17:24:58.719095896 +0000 @@ -0,0 +1,710 @@ +/** + * eCryptfs: Linux filesystem encryption layer + * + * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 2001-2004 Stony Brook University + * Copyright (c) 2004 International Business Machines Corp. + * Author(s): Michael A. Halcrow + * Michael C. Thompson + * + * 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 +#include +#include +#include +#include +#include +#include +#include "ecryptfs_kernel.h" + +/** + * @param file File we are seeking in + * @param offset The offset to seek to + * @param origin Where to start seek from + * (0=beginning,1=cur pos,2=end of file) + * @return 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 ret; + int rc; + struct file *lower_file = NULL; + + ecryptfs_printk(1, KERN_NOTICE, "Enter; offset = [%lld] origin = [%d]", + "\n", offset, origin); + if (NULL != ECRYPTFS_FILE_TO_PRIVATE(file)) + lower_file = ECRYPTFS_FILE_TO_LOWER(file); + /* Intent: 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 */ + if (offset > i_size_read(file->f_dentry->d_inode)) { + rc = ecryptfs_truncate(file->f_dentry, offset); + if (rc) { + ret = rc; + ecryptfs_printk(0, KERN_ERR, "Error on attempt to " + "truncate to (higher) offset; rc = " + "[%d]\n", rc); + goto out; + } + } + ret = generic_file_llseek(file, offset, origin); +out: + ecryptfs_printk(1, KERN_NOTICE, "Exit; ret = [%lld]\n", ret); + return ret; +} + +/** + * 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 file *file, char __user * buf, + size_t count, loff_t * ppos) +{ + int err = 0; + struct dentry *lower_dentry; + struct vfsmount *lower_vfsmount; + + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + err = generic_file_read(file, buf, count, ppos); + if (err >= 0) { + lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(file->f_dentry); + lower_vfsmount = ECRYPTFS_SUPERBLOCK_TO_PRIVATE( + file->f_dentry->d_inode->i_sb)->lower_mnt; + touch_atime(lower_vfsmount, lower_dentry); + } + ecryptfs_printk(1, KERN_NOTICE, "Exit\n"); + return err; +} + +static ssize_t +ecryptfs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) +{ + int rc = -EINVAL; + struct file *lower_file = NULL; + loff_t pos = *ppos; + + ecryptfs_printk(1, KERN_NOTICE, "Enter; file = [%p]\n", file); + if (!lower_file->f_op || !lower_file->f_op->read) + goto out; + rc = lower_file->f_op->read(lower_file, buf, count, &pos); + if (rc >= 0) + /* atime should also be updated for reads of size zero + * or more */ + ecryptfs_copy_attr_atime(file->f_dentry->d_inode, + lower_file->f_dentry->d_inode); + lower_file->f_pos = *ppos = pos; + memcpy(&(file->f_ra), &(lower_file->f_ra), + sizeof(struct file_ra_state)); +out: + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc); + return rc; +} + +/** + * Directory write operation. + * + * TODO: Encrypt the directory pages also if policy calls for it + */ +static ssize_t ecryptfs_dir_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + int rc = -EINVAL; + struct file *lower_file = NULL; + struct inode *inode; + struct inode *lower_inode; + loff_t pos = *ppos; + + ecryptfs_printk(1, KERN_NOTICE, "Enter; file = [%p]\n", file); + inode = file->f_dentry->d_inode; + lower_inode = ECRYPTFS_INODE_TO_LOWER(inode); + if (!lower_file->f_op || !lower_file->f_op->write) + goto out; + /* adjust for append -- seek to the end of the file */ + if ((file->f_flags & O_APPEND) && (count != 0)) + pos = i_size_read(inode); + if (count != 0) + rc = lower_file->f_op->write(lower_file, buf, count, &pos); + else + rc = 0; + /* copy ctime and mtime from lower layer attributes + * atime is unchanged for both layers */ + if (rc >= 0) + ecryptfs_copy_attr_times(inode, lower_inode); + /* because pwrite() does not have any way to tell us that it + * is our caller, then we don't know for sure if we have to + * update the file positions. This hack relies on write() + * having passed us the "real" pointer of its struct file's + * f_pos field. */ + lower_file->f_pos = *ppos = pos; + /* update this inode's size */ + if (pos > i_size_read(inode)) + i_size_write(inode, pos); +out: + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc); + return rc; +} + +struct ecryptfs_getdents_callback { + void *dirent; + struct dentry *dentry; + filldir_t filldir; + int err; + int filldir_called; + int entries_written; +}; + +/* copied from generic filldir in fs/readir.c */ +static int +ecryptfs_filldir(void *dirent, const char *name, int namelen, loff_t offset, + ino_t ino, unsigned int d_type) +{ + struct ecryptfs_crypt_stats *crypt_stats; + struct ecryptfs_getdents_callback *buf = + (struct ecryptfs_getdents_callback *)dirent; + int rc; + char *decoded_name; + int decoded_length; + + ecryptfs_printk(1, KERN_NOTICE, "Enter w/ name = [%.*s]\n", namelen, + name); + /* Get the crypto stats for this file */ + /* TODO: If filldir oopses, look here ... */ + crypt_stats = ECRYPTFS_DENTRY_TO_PRIVATE(buf->dentry)->crypt_stats; + buf->filldir_called++; + /* TODO: Halcrow: Check the headers of the file to determine + * if the filename needs decryption (or hiding, or + * obfuscation, etc.) */ + decoded_length = ecryptfs_decode_filename(name, namelen, &decoded_name, + ECRYPTFS_SKIP_DOTS, + crypt_stats); + if (decoded_length < 0) + return 0; + rc = buf->filldir(buf->dirent, decoded_name, decoded_length, offset, + ino, d_type); + kfree(decoded_name); + if (rc >= 0) + buf->entries_written++; + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc); + return rc; +} + +/** + * @param file The ecryptfs file struct + * @param filldir The filldir callback function + */ +static int ecryptfs_readdir(struct file *file, void *dirent, filldir_t filldir) +{ + int rc = -ENOTDIR; + struct file *lower_file = NULL; + struct inode *inode; + struct ecryptfs_getdents_callback buf; + + ecryptfs_printk(1, KERN_NOTICE, "Enter; file = [%p]\n", file); + lower_file = ECRYPTFS_FILE_TO_LOWER(file); + inode = file->f_dentry->d_inode; + 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); + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc); + return rc; +} + +static unsigned int ecryptfs_poll(struct file *file, poll_table * wait) +{ + unsigned int mask = DEFAULT_POLLMASK; + struct file *lower_file = NULL; + + ecryptfs_printk(1, KERN_NOTICE, "Enter; file = [%p]\n", file); + if (!lower_file->f_op || !lower_file->f_op->poll) + goto out; + mask = lower_file->f_op->poll(lower_file, wait); +out: + ecryptfs_printk(1, KERN_NOTICE, "Exit; mask = [%x]\n", mask); + return mask; +} + +/** + * @return Zero on success; non-zero otherwise + */ +static int +read_inode_size_from_header(struct file *lower_file, + struct inode *lower_inode, struct inode *inode) +{ + int rc = 0; + struct page *header_page; + unsigned char *header_virt; + u64 data_size; + + ecryptfs_printk(1, KERN_NOTICE, "Enter w/ lower_inode = [%p]; inode = " + "[%p]\n", lower_inode, inode); + header_page = grab_cache_page(lower_inode->i_mapping, 0); + if (!header_page) { + rc = -EINVAL; + ecryptfs_printk(0, KERN_ERR, "grab_cache_page for header page " + "failed\n"); + goto out; + } + header_virt = kmap(header_page); + rc = lower_inode->i_mapping->a_ops->readpage(lower_file, header_page); + if (rc) { + ecryptfs_printk(0, KERN_ERR, "Error reading header page\n"); + goto out_unmap; + } + memcpy(&data_size, header_virt, sizeof(data_size)); + i_size_write(inode, (loff_t) data_size); + ecryptfs_printk(1, KERN_NOTICE, "Read inode size from header: [%llu]\n", + i_size_read(inode)); +out_unmap: + kunmap(header_page); + page_cache_release(header_page); +out: + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc); + return rc; +} + +kmem_cache_t *ecryptfs_file_info_cache; + +/** + * Opens the file specified by inode. + * + * @param inode inode speciying file to open + * @param file Structure to return filled in + * @return Zero on success; non-zero otherwise + */ +static int ecryptfs_open(struct inode *inode, struct file *file) +{ + int rc = 0; + struct ecryptfs_crypt_stats *crypt_stats = NULL; + struct dentry *ecryptfs_dentry = file->f_dentry; + struct dentry *lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(ecryptfs_dentry); + struct inode *lower_inode = NULL; + struct file *lower_file = NULL; + struct vfsmount *lower_mnt; + int lower_flags; + + ecryptfs_printk(1, KERN_NOTICE, "Enter; i_ino = [%lu] inode = [%p] " + "inode->i_size = [%lld] inode->i_count = [%d] file->" + "f_dentry = [%p] file->f_dentry->d_name.name = [%s] " + "file->f_dentry->d_name.len = [%d]\n", inode->i_ino, + inode, atomic_read(&inode->i_count), i_size_read(inode), + ecryptfs_dentry, ecryptfs_dentry->d_name.name, + ecryptfs_dentry->d_name.len); + + /* ECRYPTFS_DENTRY_TO_PRIVATE(ecryptfs_dentry) Allocated in + * ecryptfs_lookup() */ + /* Released in ecryptfs_release or end of function if failure */ + ECRYPTFS_FILE_TO_PRIVATE_SM(file) = + kmem_cache_alloc(ecryptfs_file_info_cache, SLAB_KERNEL); + if (!ECRYPTFS_FILE_TO_PRIVATE_SM(file)) { + ecryptfs_printk(0, KERN_ERR, + "Error attempting to allocate memory\n"); + rc = -ENOMEM; + goto out; + } + lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(ecryptfs_dentry); + crypt_stats = &(ECRYPTFS_INODE_TO_PRIVATE(inode)->crypt_stats); + if (!crypt_stats->policy_applied) { + ecryptfs_printk(1, KERN_NOTICE, "Setting flags for stats...\n"); + /* Policy code enabled in future release */ + crypt_stats->policy_applied = 1; + crypt_stats->encrypted = 1; + } + /* 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_SUPERBLOCK_TO_PRIVATE(inode->i_sb)->lower_mnt; + 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(0, KERN_ERR, "Error opening lower file\n"); + goto out_puts; + } + ECRYPTFS_FILE_TO_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(1, KERN_NOTICE, "This is a directory\n"); + crypt_stats->encrypted = 0; + rc = 0; + goto out; + } + ecryptfs_printk(1, KERN_NOTICE, "(file->f_flags & O_CREAT) = [%d] " + "lower_inode->i_size = [%llu] crypt_stats->struct_" + "initialized = [%d] crypt_stats->key_valid = [%d]\n", + (file->f_flags & O_CREAT), + (unsigned long long)lower_inode->i_size, + crypt_stats->struct_initialized, + crypt_stats->key_valid); + /* TODO: This was originally used to determine if it was a new file, + * but if we are allowing pass-through mode, then we couldbe opening + * a 0-length normal file.. in which case, this can't cause an error + * So.... what do we want to do? */ + if (lower_inode->i_size == 0) { + ecryptfs_printk(0, KERN_EMERG, "Zero-length lower file; " + "ecryptfs_create() had a problem?\n"); + rc = -ENOENT; + goto out_puts; + } else if (!crypt_stats->policy_applied || !crypt_stats->key_valid) { + /* crypto.c */ + rc = ecryptfs_read_headers(ecryptfs_dentry, lower_file); + if (rc) { + ecryptfs_printk(1, KERN_NOTICE, + "Valid headers not found\n"); + crypt_stats->encrypted = 0; + } else + read_inode_size_from_header(lower_file, lower_inode, + inode); + } else + ecryptfs_printk(1, KERN_NOTICE, "crypt_stats->struct_" + "initialized = [%d]; crypt_stats->key_valid = " + "[%d]\n", crypt_stats->struct_initialized, + crypt_stats->key_valid); + ECRYPTFS_FILE_TO_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: + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc); + return rc; +} + +static int ecryptfs_flush(struct file *file) +{ + int rc = 0; + struct file *lower_file = NULL; + + ecryptfs_printk(1, KERN_NOTICE, "Enter; file = [%p]\n", file); + 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); + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc); + return rc; +} + +static int ecryptfs_release(struct inode *ecryptfs_inode, struct file *file) +{ + int rc = 0; + struct file *lower_file = NULL; + struct inode *lower_inode; + + ecryptfs_printk(1, KERN_NOTICE, + "Enter; ecryptfs_inode->i_count = [%d]\n", + ecryptfs_inode->i_count); + lower_file = ECRYPTFS_FILE_TO_LOWER(file); + kmem_cache_free(ecryptfs_file_info_cache, + ECRYPTFS_FILE_TO_PRIVATE(file)); + lower_inode = ECRYPTFS_INODE_TO_LOWER(ecryptfs_inode); + fput(lower_file); + ecryptfs_inode->i_blocks = lower_inode->i_blocks; + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc); + return rc; +} + +static int +ecryptfs_fsync(struct file *file, struct dentry *dentry, int datasync) +{ + int rc = -EINVAL; + struct file *lower_file = NULL; + struct dentry *lower_dentry; + + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + if (NULL == file) { + lower_dentry = ecryptfs_lower_dentry(dentry); + if (lower_dentry->d_inode->i_fop + && lower_dentry->d_inode->i_fop->fsync) { + down(&lower_dentry->d_inode->i_sem); + rc = lower_dentry->d_inode->i_fop->fsync(lower_file, + lower_dentry, + datasync); + up(&lower_dentry->d_inode->i_sem); + } + } else { + if (NULL == ECRYPTFS_FILE_TO_PRIVATE(file)) { + rc = -EINVAL; + ecryptfs_printk(0, KERN_ERR, "ECRYPTFS_FILE_TO_PRIVATE" + "(file=[%p]) == NULL\n", file); + goto out; + } + lower_file = ECRYPTFS_FILE_TO_LOWER(file); + lower_dentry = ecryptfs_lower_dentry(dentry); + if (lower_file->f_op && lower_file->f_op->fsync) { + down(&lower_dentry->d_inode->i_sem); + rc = lower_file->f_op->fsync(lower_file, lower_dentry, + datasync); + up(&lower_dentry->d_inode->i_sem); + } + } +out: + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc); + return rc; +} + +static void locks_delete_block(struct file_lock *waiter) +{ + lock_kernel(); + list_del_init(&waiter->fl_block); + list_del_init(&waiter->fl_link); + waiter->fl_next = NULL; + unlock_kernel(); +} + +static int ecryptfs_posix_lock(struct file *file, struct file_lock *fl, int cmd) +{ + int rc; + +lock_file: + rc = posix_lock_file(file, fl); + if ((rc != -EAGAIN) || (cmd == F_SETLK)) + goto out; + rc = wait_event_interruptible(fl->fl_wait, !fl->fl_next); + if (!rc) + goto lock_file; + locks_delete_block(fl); +out: + return rc; +} + +static int ecryptfs_setlk(struct file *file, int cmd, struct file_lock *fl) +{ + int rc = -EINVAL; + struct inode *inode, *lower_inode; + struct file *lower_file = NULL; + + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + lower_file = ECRYPTFS_FILE_TO_LOWER(file); + inode = file->f_dentry->d_inode; + lower_inode = lower_file->f_dentry->d_inode; + /* Don't allow mandatory locks on files that may be memory mapped + * and shared. */ + if (IS_MANDLOCK(lower_inode) && + (lower_inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID && + mapping_writably_mapped(lower_file->f_mapping)) { + rc = -EAGAIN; + goto out; + } + if (cmd == F_SETLKW) + fl->fl_flags |= FL_SLEEP; + rc = -EBADF; + switch (fl->fl_type) { + case F_RDLCK: + if (!(lower_file->f_mode & FMODE_READ)) + goto out; + break; + case F_WRLCK: + if (!(lower_file->f_mode & FMODE_WRITE)) + goto out; + break; + case F_UNLCK: + break; + default: + rc = -EINVAL; + goto out; + } + fl->fl_file = lower_file; + rc = security_file_lock(lower_file, fl->fl_type); + if (rc) + goto out; + if (lower_file->f_op && lower_file->f_op->lock != NULL) { + rc = lower_file->f_op->lock(lower_file, cmd, fl); + if (rc) + goto out; + goto upper_lock; + } + rc = ecryptfs_posix_lock(lower_file, fl, cmd); + if (rc) + goto out; +upper_lock: + fl->fl_file = file; + rc = ecryptfs_posix_lock(file, fl, cmd); + if (rc) { + fl->fl_type = F_UNLCK; + fl->fl_file = lower_file; + ecryptfs_posix_lock(lower_file, fl, cmd); + } +out: + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc); + return rc; +} + +static int ecryptfs_getlk(struct file *file, struct file_lock *fl) +{ + int rc = 0; + struct file_lock *tempfl = NULL; + + if (file->f_op && file->f_op->lock) { + rc = file->f_op->lock(file, F_GETLK, fl); + if (rc < 0) + goto out; + } else + tempfl = posix_test_lock(file, fl); + if (!tempfl) + fl->fl_type = F_UNLCK; + else + memcpy(fl, tempfl, sizeof(struct file_lock)); +out: + return rc; +} + +static int ecryptfs_fasync(int fd, struct file *file, int flag) +{ + int rc = 0; + struct file *lower_file = NULL; + + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + if (NULL != ECRYPTFS_FILE_TO_PRIVATE(file)) + lower_file = ECRYPTFS_FILE_TO_LOWER(file); + else { + rc = -EINVAL; + goto out; + } + if (lower_file->f_op && lower_file->f_op->fasync) + rc = lower_file->f_op->fasync(fd, lower_file, flag); +out: + ecryptfs_printk(1, KERN_NOTICE, "Exit\n"); + return rc; +} + +static int ecryptfs_lock(struct file *file, int cmd, struct file_lock *fl) +{ + int rc = 0; + struct file *lower_file = NULL; + + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + if (ECRYPTFS_FILE_TO_PRIVATE(file) != NULL) + lower_file = ECRYPTFS_FILE_TO_LOWER(file); + ASSERT(lower_file != NULL); + rc = -EINVAL; + if (!fl) + goto out; + fl->fl_file = lower_file; + switch (cmd) { + case F_GETLK: + case F_GETLK64: + rc = ecryptfs_getlk(lower_file, fl); + break; + case F_SETLK: + case F_SETLKW: + case F_SETLK64: + case F_SETLKW64: + fl->fl_file = file; + rc = ecryptfs_setlk(file, cmd, fl); + break; + default: + rc = -EINVAL; + } + fl->fl_file = file; +out: + ecryptfs_printk(1, KERN_NOTICE, "Exit\n"); + 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; + + if (ECRYPTFS_FILE_TO_PRIVATE(file) != NULL) + lower_file = ECRYPTFS_FILE_TO_LOWER(file); + ASSERT(lower_file != NULL); + /* TODO: Is this a superfluous check for f_op? */ + 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); + +struct file_operations ecryptfs_dir_fops = { + .read = ecryptfs_read, + .write = ecryptfs_dir_write, + .readdir = ecryptfs_readdir, + .poll = ecryptfs_poll, + .ioctl = ecryptfs_ioctl, + .mmap = generic_file_mmap, + .open = ecryptfs_open, + .flush = ecryptfs_flush, + .release = ecryptfs_release, + .fsync = ecryptfs_fsync, + .fasync = ecryptfs_fasync, + .lock = ecryptfs_lock, + .sendfile = ecryptfs_sendfile, +}; + +struct file_operations ecryptfs_main_fops = { + .llseek = ecryptfs_llseek, + .read = ecryptfs_read_update_atime, + .write = generic_file_write, + .readdir = ecryptfs_readdir, + .poll = ecryptfs_poll, + .ioctl = ecryptfs_ioctl, + .mmap = generic_file_mmap, + .open = ecryptfs_open, + .flush = ecryptfs_flush, + .release = ecryptfs_release, + .fsync = ecryptfs_fsync, + .fasync = ecryptfs_fasync, + .lock = ecryptfs_lock, + .sendfile = ecryptfs_sendfile, +}; + +/** + * TODO: sysfs + */ +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) != NULL) + 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; +} diff -urN oldtree/fs/ecryptfs/inode.c newtree/fs/ecryptfs/inode.c --- oldtree/fs/ecryptfs/inode.c 1970-01-01 00:00:00.000000000 +0000 +++ newtree/fs/ecryptfs/inode.c 2006-02-18 17:24:58.721095592 +0000 @@ -0,0 +1,1084 @@ +/** + * eCryptfs: Linux filesystem encryption layer + * + * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 2001-2004 Stony Brook University + * Copyright (c) 2004 International Business Machines Corp. + * Author(s): Michael A. Halcrow + * Michael C. Thompsion + * + * 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 +#include +#include +#include +#include +#include +#include +#include "ecryptfs_kernel.h" + +static inline struct dentry *lock_parent(struct dentry *dentry) +{ + struct dentry *dir; + + dir = dget(dentry->d_parent); + down(&(dir->d_inode->i_sem)); + return dir; +} + +static inline void unlock_parent(struct dentry *dentry) +{ + up(&(dentry->d_parent->d_inode->i_sem)); + dput(dentry->d_parent); +} + +static inline void unlock_dir(struct dentry *dir) +{ + up(&dir->d_inode->i_sem); + dput(dir); +} + +void ecryptfs_copy_inode_size(struct inode *dst, const struct inode *src) +{ + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + ecryptfs_printk(1, KERN_NOTICE, "src->i_size = [%lld]\n", src->i_size); + ecryptfs_printk(1, KERN_NOTICE, "src->i_blocks = [%lu]\n", + src->i_blocks); + i_size_write(dst, i_size_read((struct inode *)src)); + dst->i_blocks = src->i_blocks; + ecryptfs_printk(1, KERN_NOTICE, "Exit\n"); +} + +void ecryptfs_copy_attr_atime(struct inode *dest, const struct inode *src) +{ + ASSERT(dest != NULL); + ASSERT(src != NULL); + dest->i_atime = src->i_atime; +} + +void ecryptfs_copy_attr_times(struct inode *dest, const struct inode *src) +{ + ASSERT(dest != NULL); + ASSERT(src != NULL); + dest->i_atime = src->i_atime; + dest->i_mtime = src->i_mtime; + dest->i_ctime = src->i_ctime; +} + +static void ecryptfs_copy_attr_timesizes(struct inode *dest, + const struct inode *src) +{ + ASSERT(dest != NULL); + ASSERT(src != NULL); + dest->i_atime = src->i_atime; + dest->i_mtime = src->i_mtime; + dest->i_ctime = src->i_ctime; + ecryptfs_copy_inode_size(dest, src); +} + +void ecryptfs_copy_attr_all(struct inode *dest, const struct inode *src) +{ + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + ASSERT(dest != NULL); + ASSERT(src != NULL); + dest->i_mode = src->i_mode; + dest->i_nlink = src->i_nlink; + dest->i_uid = src->i_uid; + dest->i_gid = src->i_gid; + dest->i_rdev = src->i_rdev; + dest->i_atime = src->i_atime; + dest->i_mtime = src->i_mtime; + dest->i_ctime = src->i_ctime; + dest->i_blksize = src->i_blksize; + ecryptfs_printk(1, KERN_NOTICE, "src->i_blksize = [%lu]\n", + src->i_blksize); + dest->i_blkbits = src->i_blkbits; + dest->i_flags = src->i_flags; + ecryptfs_printk(1, KERN_NOTICE, "Exit\n"); +} + +/** + * Creates our file in the lower file system + * + * @param lower_dir_inode inode of the parent in the lower fs of the new file + * @param lower_dentry New file's dentry in the lower fs + * @param ecryptfs_dentry New file's dentry in ecryptfs + * @param mode The mode of the new file + * @param nd nameidata of ecryptfs' parent's dentry & vfsmnt + * @return 0 on success; non-zero on error condition + */ +static int +ecryptfs_create_underlying_file(struct inode *lower_dir_inode, + struct dentry *lower_dentry, + struct dentry *ecryptfs_dentry, int mode, + struct nameidata *nd) +{ + int rc; + struct dentry *saved_dentry = NULL; + struct vfsmount *saved_vfsmount = NULL; + + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + saved_dentry = nd->dentry; + saved_vfsmount = nd->mnt; + nd->dentry = lower_dentry; + nd->mnt = ECRYPTFS_SUPERBLOCK_TO_PRIVATE( + ecryptfs_dentry->d_sb)->lower_mnt; + rc = vfs_create(lower_dir_inode, lower_dentry, mode, nd); + nd->dentry = saved_dentry; + nd->mnt = saved_vfsmount; + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc); + return rc; +} + +/** + * Creates the underlying file and the eCryptfs inode which will link to + * it. It will also update the eCryptfs directory inode to mimic the + * stats of the lower directory inode + * + * @param directory_inode inode of the new file's dentry's parent in ecryptfs + * @param ecryptfs_dentry New file's dentry in ecryptfs + * @param mode The mode of the new file + * @param nd nameidata of ecryptfs' parent's dentry & vfsmnt + * @return 0 on success; non-zero on error condition + */ +static int +ecryptfs_do_create(struct inode *directory_inode, + struct dentry *ecryptfs_dentry, int mode, + struct nameidata *nd) +{ + int rc; + struct dentry *lower_dentry; + struct dentry *lower_dir_dentry; + + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + lower_dentry = ecryptfs_lower_dentry(ecryptfs_dentry); + if (IS_ERR(lower_dentry)) { + ecryptfs_printk(0, KERN_ERR, "ecryptfs dentry doesn't know" + "about its lower counterpart\n"); + rc = PTR_ERR(lower_dentry); + goto out; + } + lower_dir_dentry = lock_parent(lower_dentry); + if (unlikely(IS_ERR(lower_dir_dentry))) { + ecryptfs_printk(0, KERN_ERR, "Error locking directory of " + "dentry\n"); + rc = PTR_ERR(lower_dir_dentry); + goto out; + } + rc = ecryptfs_create_underlying_file(lower_dir_dentry->d_inode, + lower_dentry, ecryptfs_dentry, + mode, nd); + if (unlikely(rc)) { + ecryptfs_printk(0, KERN_ERR, + "Failure to create underlying file\n"); + goto out_lock; + } + rc = ecryptfs_interpose(lower_dentry, ecryptfs_dentry, + directory_inode->i_sb, 0); + if (rc) { + ecryptfs_printk(0, KERN_ERR, "Failure in ecryptfs_interpose\n"); + goto out_lock; + } + ecryptfs_copy_attr_timesizes(directory_inode, + lower_dir_dentry->d_inode); +out_lock: + unlock_dir(lower_dir_dentry); +out: + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc); + return rc; +} + +/** + * This is the code which will grow the file to be 8192 or + * 12288, depending on whether the file derived-IV or + * written-IV formatted. + */ +static int grow_file(struct dentry *ecryptfs_dentry, struct file *lower_file, + struct inode *inode, struct inode *lower_inode) +{ + int rc = 0; + struct file fake_file; + + memset(&fake_file, 0, sizeof(fake_file)); + fake_file.f_dentry = ecryptfs_dentry; + ECRYPTFS_FILE_TO_PRIVATE_SM(&fake_file) = + kmem_cache_alloc(ecryptfs_file_info_cache, SLAB_KERNEL); + if (!(ECRYPTFS_FILE_TO_PRIVATE_SM(&fake_file))) { + rc = -ENOMEM; + goto out; + } + ECRYPTFS_FILE_TO_LOWER(&fake_file) = lower_file; + ecryptfs_fill_zeros(&fake_file, 1); + kmem_cache_free(ecryptfs_file_info_cache, + ECRYPTFS_FILE_TO_PRIVATE(&fake_file)); + i_size_write(inode, 0); + ecryptfs_write_inode_size_to_header(lower_file, lower_inode, inode); + ECRYPTFS_INODE_TO_PRIVATE(inode)->crypt_stats.new_file = 1; +out: + return rc; +} + +/** + * Force the file to be changed from a basic empty file to an ecryptfs file + * with a header, 1st IV page & 1st data page + * + * @return Zero on success + */ +static int ecryptfs_initialize_file(struct dentry *ecryptfs_dentry) +{ + int rc = 0; + int lower_flags; + struct ecryptfs_crypt_stats *crypt_stats; + struct dentry *lower_dentry; + struct dentry *tlower_dentry = NULL; + struct file *lower_file; + struct inode *inode, *lower_inode; + struct vfsmount *lower_mnt; + + ecryptfs_printk(1, KERN_NOTICE, "Enter; dentry->d_name.name = [%s]\n", + ecryptfs_dentry->d_name.name); + + lower_dentry = ecryptfs_lower_dentry(ecryptfs_dentry); + if (IS_ERR(lower_dentry)) { + ecryptfs_printk(0, KERN_ERR, "ecryptfs dentry doesn't know" + "about its lower counterpart\n"); + rc = PTR_ERR(lower_dentry); + goto out; + } + + ecryptfs_printk(1, KERN_NOTICE, "lower_dentry->d_name.name = [%s]\n", + lower_dentry->d_name.name); + + /* This code is migrated from ecryptfs_open... + * there is no reason that this shouldn't just "work" + * The flags and mode for the file passed into ecryptfs_open is: + * f->f_flags = flags; + * f->f_mode = ((flags+1) & O_ACCMODE) + * | FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE; + * + * Where flags = O_CREAT | O_WRONLY | O_TRUNC & + * ( #if BITS_PER_LONG != 32 ; flags |= O_LARGEFILE; #endif ) + * + * Code to initialize the file to be a valid ecryptfs file + */ + inode = ecryptfs_dentry->d_inode; + crypt_stats = &(ECRYPTFS_INODE_TO_PRIVATE(inode)->crypt_stats); + /* TODO: Initialization is done in alloc_inode, BUG if its not done */ + if (!crypt_stats->struct_initialized) + BUG(); + tlower_dentry = dget(lower_dentry); + if (!tlower_dentry) { + rc = -ENOMEM; + ecryptfs_printk(0, KERN_ERR, "Error dget'ing lower_dentry\n"); + goto out; + } + lower_flags = ((O_CREAT | O_WRONLY | O_TRUNC) & O_ACCMODE) | O_RDWR; +#if BITS_PER_LONG != 32 + lower_flags |= O_LARGEFILE; +#endif + lower_mnt = ECRYPTFS_SUPERBLOCK_TO_PRIVATE(inode->i_sb)->lower_mnt; + mntget(lower_mnt); + /* Corresponding fput() at end of func */ + lower_file = dentry_open(tlower_dentry, lower_mnt, lower_flags); + if (IS_ERR(lower_file)) { + rc = PTR_ERR(lower_file); + ecryptfs_printk(0, KERN_ERR, + "Error opening dentry; rc = [%i]\n", rc); + goto out; + } + /* fput(lower_file) should handle the puts if we do this */ + lower_file->f_dentry = tlower_dentry; + lower_file->f_vfsmnt = lower_mnt; + lower_inode = tlower_dentry->d_inode; + if (S_ISDIR(ecryptfs_dentry->d_inode->i_mode)) { + ecryptfs_printk(1, KERN_NOTICE, "This is a directory\n"); + crypt_stats->encrypted = 0; + goto out_fput; + } + crypt_stats->new_file = 1; + ecryptfs_printk(1, KERN_NOTICE, "Initializing crypto context\n"); + rc = ecryptfs_new_file_context(ecryptfs_dentry); /* crypto.c */ + if (rc) { + ecryptfs_printk(1, KERN_NOTICE, "Error creating new file " + "context\n"); + goto out_fput; + } + rc = ecryptfs_write_headers(ecryptfs_dentry, lower_file); + if (rc) { + ecryptfs_printk(1, KERN_NOTICE, "Error writing headers\n"); + goto out_fput; + } + rc = grow_file(ecryptfs_dentry, lower_file, inode, lower_inode); +out_fput: + fput(lower_file); +out: + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc); + return rc; +} + +/** + * Creates a new file. + * + * @param dir The inode of the directory in which to create the file. + * @param dentry The eCryptfs dentry + * @param mode The mode of the new file. + * @param nd nameidata + * @return 0 on success; non-zero on error condition + */ +static int +ecryptfs_create(struct inode *directory_inode, struct dentry *ecryptfs_dentry, + int mode, struct nameidata *nd) +{ + int rc; + + ecryptfs_printk(1, KERN_NOTICE, "Enter; ecryptfs_dentry->d_name.name = " + "[%s], directory_inode=[%p], ecryptfs_dentry->d_parent" + "->d_inode=[%p], nd->dentry=[%p], nd->last.name=[%s], " + "ecryptfs_dentry=[%p]\n", ecryptfs_dentry->d_name.name, + directory_inode, ecryptfs_dentry->d_parent->d_inode, + nd->dentry, nd->last.name, ecryptfs_dentry); + + rc = ecryptfs_do_create(directory_inode, ecryptfs_dentry, mode, nd); + if (unlikely(rc)) { + ecryptfs_printk(0, KERN_WARNING, "Failed to create file in" + "lower filesystem\n"); + goto out; + } + /* At this point, a file exists on "disk", we need to make sure + * that this on disk file is prepared to be an ecryptfs file */ + rc = ecryptfs_initialize_file(ecryptfs_dentry); + out: + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc); + return rc; +} + +/** + * Find a file on disk. If the file does not exist, then we'll add it to the + * dentry cache and continue on to read it from the disk. + * + * @param dir inode + * @param dentry dentry + * @param nd nameidata; may be NULL + */ +static struct dentry *ecryptfs_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *nd) +{ + int err = 0; + struct dentry *lower_dir_dentry; + struct dentry *lower_dentry; + struct dentry *tlower_dentry = NULL; + char *encoded_name; + unsigned int encoded_namelen; + struct ecryptfs_crypt_stats *crypt_stats = NULL; + char *page_virt = NULL; + struct inode *lower_inode; + unsigned long long file_size; + + ecryptfs_printk(1, KERN_NOTICE, "Enter; dir = [%p], dentry->d_name.nam" + "e = [%s], nd = [%p]\n", dir, dentry->d_name.name, nd); + + lower_dir_dentry = ecryptfs_lower_dentry(dentry->d_parent); + dentry->d_op = &ecryptfs_dops; + + /* Sanity Check: Make sure we can operate on the file */ + if ((dentry->d_name.len == 1 && !strcmp(dentry->d_name.name, ".")) + || (dentry->d_name.len == 2 && !strcmp(dentry->d_name.name, ".."))) + goto out_drop; + + encoded_namelen = ecryptfs_encode_filename(dentry->d_name.name, + dentry->d_name.len, + &encoded_name, + ECRYPTFS_SKIP_DOTS, + crypt_stats); + if (encoded_namelen < 0) { + err = encoded_namelen; + goto out_drop; + } + /* TODO: pretty sure we need to do a dput(lower_dentry) to + * counter */ + ecryptfs_printk(1, KERN_NOTICE, "encoded_name = [%s]; encoded_namelen " + "= [%d]\n", encoded_name, encoded_namelen); + /* TODO: BUG: What to do on symlink to directory? */ + lower_dentry = lookup_one_len(encoded_name, lower_dir_dentry, + encoded_namelen - 1); + kfree(encoded_name); + if (IS_ERR(lower_dentry)) { + ecryptfs_printk(0, KERN_ERR, "ERR from lower_dentry\n"); + err = PTR_ERR(lower_dentry); + goto out_drop; + } + ecryptfs_printk(1, KERN_NOTICE, "lower_dentry = [%p]; lower_dentry->" + "d_name.name = [%s]\n", lower_dentry, + lower_dentry->d_name.name); + lower_inode = lower_dentry->d_inode; + ecryptfs_copy_attr_atime(dir, lower_dir_dentry->d_inode); + + /* Sanity check */ + ASSERT(atomic_read(&lower_dentry->d_count)); + + /* Private allocation */ + ECRYPTFS_DENTRY_TO_PRIVATE_SM(dentry) = + kmem_cache_alloc(ecryptfs_dentry_info_cache, SLAB_KERNEL); + if (!ECRYPTFS_DENTRY_TO_PRIVATE_SM(dentry)) { + err = -ENOMEM; + ecryptfs_printk(0, KERN_ERR, "Out of memory whilst attempting " + "to allocate ecryptfs_dentry_info struct\n"); + goto out_dput; + } + + ECRYPTFS_DENTRY_TO_LOWER(dentry) = lower_dentry; + if (!lower_dentry->d_inode) { + /* We want to add because we couldn't find in lower */ + d_add(dentry, NULL); + goto out; + } + err = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 1); + if (err) { + ecryptfs_printk(0, KERN_ERR, "Error interposing\n"); + goto out_dput; + } + /* Do we want to just get a handle to the directory and its lower + * and not abort with puts? + */ + if (S_ISDIR(lower_inode->i_mode)) { + ecryptfs_printk(1, KERN_NOTICE, "Is a directory; returning\n"); + goto out; + } + if (S_ISLNK(lower_inode->i_mode)) { + ecryptfs_printk(1, KERN_NOTICE, "Is a symlink; returning\n"); + goto out; + } + /* We have a NULL dentry, can we just skip over the read here? */ + if (!nd) { + ecryptfs_printk(1, KERN_NOTICE, "We have a NULL nd, just leave" + "as we *think* we are about to unlink\n"); + goto out; + } + tlower_dentry = dget(lower_dentry); + if (!tlower_dentry || IS_ERR(tlower_dentry)) { + err = -ENOMEM; + ecryptfs_printk(0, KERN_ERR, "Cannot dget lower_dentry\n"); + goto out_dput; + } + /* Released in this function */ + page_virt = + (char *)kmem_cache_alloc(ecryptfs_header_cache_2, + SLAB_USER); + if (!page_virt) { + err = -ENOMEM; + ecryptfs_printk(0, KERN_ERR, + "Cannot ecryptfs_kmalloc a page\n"); + goto out_dput; + } + + /* Use the file's "header" to determine if its an ecryptfs file */ + err = ecryptfs_read_header_region(page_virt, tlower_dentry, nd); + /* Force default values if we haven't parsed the header */ + crypt_stats = + &(ECRYPTFS_INODE_TO_PRIVATE(dentry->d_inode)->crypt_stats); + if (!crypt_stats->policy_applied) + ecryptfs_set_default_sizes(crypt_stats); + + if (err) { + err = 0; + ecryptfs_printk(1, KERN_WARNING, "Error reading header region;" + " assuming unencrypted\n"); + } else { + if (!contains_ecryptfs_marker(page_virt + + ECRYPTFS_FILE_SIZE_BYTES)) { + ecryptfs_printk(0, KERN_WARNING, "Underlying file " + "lacks recognizable eCryptfs marker\n"); + } + memcpy(&file_size, page_virt, sizeof(file_size)); + dentry->d_inode->i_size = file_size; + } + kmem_cache_free(ecryptfs_header_cache_2, page_virt); + goto out; + +out_dput: + dput(lower_dentry); + if (tlower_dentry) + dput(tlower_dentry); +out_drop: + d_drop(dentry); +out: + ecryptfs_printk(1, KERN_NOTICE, "Exit; err = [%d]\n", err); + return ERR_PTR(err); +} + +static int ecryptfs_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *new_dentry) +{ + int err; + struct dentry *lower_old_dentry; + struct dentry *lower_new_dentry; + struct dentry *lower_dir_dentry; + + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + lower_old_dentry = ecryptfs_lower_dentry(old_dentry); + lower_new_dentry = ecryptfs_lower_dentry(new_dentry); + dget(lower_old_dentry); + dget(lower_new_dentry); + lower_dir_dentry = lock_parent(lower_new_dentry); + err = vfs_link(lower_old_dentry, lower_dir_dentry->d_inode, + lower_new_dentry); + if (err || !lower_new_dentry->d_inode) + goto out_lock; + err = ecryptfs_interpose(lower_new_dentry, new_dentry, dir->i_sb, 0); + if (err) + goto out_lock; + ecryptfs_copy_attr_timesizes(dir, lower_new_dentry->d_inode); + old_dentry->d_inode->i_nlink = + ECRYPTFS_INODE_TO_LOWER(old_dentry->d_inode)->i_nlink; +out_lock: + unlock_dir(lower_dir_dentry); + dput(lower_new_dentry); + dput(lower_old_dentry); + if (!new_dentry->d_inode) + d_drop(new_dentry); + ecryptfs_printk(1, KERN_NOTICE, "Exit; err = [%d]\n", err); + return err; +} + +static int ecryptfs_unlink(struct inode *dir, struct dentry *dentry) +{ + int rc = 0; + struct dentry *lower_dentry = ECRYPTFS_DENTRY_TO_LOWER(dentry); + struct inode *lower_dir_inode = ECRYPTFS_INODE_TO_LOWER(dir); + ecryptfs_printk(1, KERN_NOTICE, "Enter; dentry->d_name = [%s]\n", + dentry->d_name.name); + lock_parent(lower_dentry); + rc = vfs_unlink(lower_dir_inode, lower_dentry); + if (rc) { + ecryptfs_printk(1, KERN_ERR, "Error in vfs_unlink\n"); + goto out_unlock; + } + ecryptfs_copy_attr_times(dir, lower_dir_inode); + dentry->d_inode->i_nlink = + ECRYPTFS_INODE_TO_LOWER(dentry->d_inode)->i_nlink; + dentry->d_inode->i_ctime = dir->i_ctime; +out_unlock: + unlock_parent(lower_dentry); + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]",rc); + return rc; +} + +static int ecryptfs_symlink(struct inode *dir, struct dentry *dentry, + const char *symname) +{ + int err; + struct dentry *lower_dentry; + struct dentry *lower_dir_dentry; + umode_t mode; + char *encoded_symname; + unsigned int encoded_symlen; + struct ecryptfs_crypt_stats *crypt_stats = NULL; + + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + lower_dentry = ecryptfs_lower_dentry(dentry); + dget(lower_dentry); + lower_dir_dentry = lock_parent(lower_dentry); + mode = S_IALLUGO; + encoded_symlen = ecryptfs_encode_filename(symname, strlen(symname), + &encoded_symname, + ECRYPTFS_DO_DOTS, + crypt_stats); + if (encoded_symlen < 0) { + err = encoded_symlen; + goto out_lock; + } + err = vfs_symlink(lower_dir_dentry->d_inode, lower_dentry, + encoded_symname, mode); + kfree(encoded_symname); + if (err || !lower_dentry->d_inode) + goto out_lock; + err = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0); + if (err) + goto out_lock; + ecryptfs_copy_attr_timesizes(dir, lower_dir_dentry->d_inode); +out_lock: + unlock_dir(lower_dir_dentry); + dput(lower_dentry); + if (!dentry->d_inode) + d_drop(dentry); + return err; +} + +static int ecryptfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) +{ + int err; + struct dentry *lower_dentry; + struct dentry *lower_dir_dentry; + + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + lower_dentry = ecryptfs_lower_dentry(dentry); + lower_dir_dentry = lock_parent(lower_dentry); + err = vfs_mkdir(lower_dir_dentry->d_inode, lower_dentry, mode); + if (err || !lower_dentry->d_inode) + goto out; + err = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0); + if (err) + goto out; + ecryptfs_copy_attr_timesizes(dir, lower_dir_dentry->d_inode); + dir->i_nlink = lower_dir_dentry->d_inode->i_nlink; +out: + unlock_dir(lower_dir_dentry); + if (!dentry->d_inode) + d_drop(dentry); + ecryptfs_printk(1, KERN_NOTICE, "Exit; err = [%d]\n", err); + return err; +} + +static int ecryptfs_rmdir(struct inode *dir, struct dentry *dentry) +{ + int err = 0; + struct dentry *tdentry = NULL; + struct dentry *lower_dentry; + struct dentry *tlower_dentry = NULL; + struct dentry *lower_dir_dentry; + struct dentry *tlower_dir_dentry = NULL; + + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + lower_dentry = ecryptfs_lower_dentry(dentry); + if (!(tdentry = dget(dentry))) { + err = -EINVAL; + ecryptfs_printk(0, KERN_ERR, "Error dget'ing dentry [%p]\n", + dentry); + goto out; + } + lower_dir_dentry = lock_parent(lower_dentry); + if (!(tlower_dentry = dget(lower_dentry))) { + err = -EINVAL; + ecryptfs_printk(0, KERN_ERR, "Error dget'ing lower_dentry " + "[%p]\n", lower_dentry); + goto out; + } + err = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry); + if (!err) { + d_delete(tlower_dentry); + tlower_dentry = NULL; + } + ecryptfs_copy_attr_times(dir, lower_dir_dentry->d_inode); + dir->i_nlink = lower_dir_dentry->d_inode->i_nlink; + unlock_dir(lower_dir_dentry); + if (!err) + d_drop(dentry); +out: + if (tdentry) + dput(tdentry); + if (tlower_dentry) + dput(tlower_dentry); + if (tlower_dir_dentry) + dput(tlower_dir_dentry); + ecryptfs_printk(1, KERN_NOTICE, "Exit; err = [%d]\n", err); + return err; +} + +static int +ecryptfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) +{ + int err; + struct dentry *lower_dentry; + struct dentry *lower_dir_dentry; + + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + lower_dentry = ecryptfs_lower_dentry(dentry); + lower_dir_dentry = lock_parent(lower_dentry); + err = vfs_mknod(lower_dir_dentry->d_inode, lower_dentry, mode, dev); + if (err || !lower_dentry->d_inode) + goto out; + err = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0); + if (err) + goto out; + ecryptfs_copy_attr_timesizes(dir, lower_dir_dentry->d_inode); +out: + unlock_dir(lower_dir_dentry); + if (!dentry->d_inode) + d_drop(dentry); + return err; +} + +static int +ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + int err; + struct dentry *lower_old_dentry; + struct dentry *lower_new_dentry; + struct dentry *lower_old_dir_dentry; + struct dentry *lower_new_dir_dentry; + + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + lower_old_dentry = ecryptfs_lower_dentry(old_dentry); + lower_new_dentry = ecryptfs_lower_dentry(new_dentry); + dget(lower_old_dentry); + dget(lower_new_dentry); + lower_old_dir_dentry = dget_parent(lower_old_dentry); + lower_new_dir_dentry = dget_parent(lower_new_dentry); + lock_rename(lower_old_dir_dentry, lower_new_dir_dentry); + err = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry, + lower_new_dir_dentry->d_inode, lower_new_dentry); + if (err) + goto out_lock; + ecryptfs_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode); + if (new_dir != old_dir) + ecryptfs_copy_attr_all(old_dir, lower_old_dir_dentry->d_inode); +out_lock: + unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); + dput(lower_new_dentry); + dput(lower_old_dentry); + return err; +} + +static int +ecryptfs_readlink(struct dentry *dentry, char __user * buf, int bufsiz) +{ + int err; + struct dentry *lower_dentry; + char *decoded_name; + char *lower_buf; + mm_segment_t old_fs; + struct ecryptfs_crypt_stats *crypt_stats; + + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + lower_dentry = ecryptfs_lower_dentry(dentry); + if (!lower_dentry->d_inode->i_op || + !lower_dentry->d_inode->i_op->readlink) { + err = -EINVAL; + goto out; + } + /* Released in this function */ + lower_buf = kmalloc(bufsiz, GFP_KERNEL); + if (lower_buf == NULL) { + ecryptfs_printk(0, KERN_ERR, "Out of memory\n"); + err = -ENOMEM; + goto out; + } + old_fs = get_fs(); + set_fs(get_ds()); + ecryptfs_printk(1, KERN_NOTICE, "Calling readlink w/ " + "lower_dentry->d_name.name = [%s]\n", + lower_dentry->d_name.name); + err = lower_dentry->d_inode->i_op->readlink(lower_dentry, + (char __user *)lower_buf, + bufsiz); + set_fs(old_fs); + if (err >= 0) { + crypt_stats = NULL; + err = ecryptfs_decode_filename(lower_buf, err, + &decoded_name, + ECRYPTFS_DO_DOTS, crypt_stats); + if (err == -ENOMEM) + goto out_free_lower_buf; + if (err > 0) { + ecryptfs_printk(1, KERN_NOTICE, "Copying [%d] bytes " + "to userspace: [%*s]\n", err, err, + decoded_name); + if (copy_to_user(buf, decoded_name, err)) + err = -EFAULT; + } + kfree(decoded_name); + ecryptfs_copy_attr_atime(dentry->d_inode, + lower_dentry->d_inode); + } +out_free_lower_buf: + kfree(lower_buf); +out: + ecryptfs_printk(1, KERN_NOTICE, "Exit; err = [%d]\n", err); + return err; +} + +static void *ecryptfs_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + char *buf; + int len = PAGE_SIZE, rc; + mm_segment_t old_fs; + + ecryptfs_printk(1, KERN_NOTICE, "Enter; dentry->d_name.name = [%s]\n", + dentry->d_name.name); + /* Released in ecryptfs_put_link(); only release here on error */ + buf = kmalloc(len, GFP_KERNEL); + if (!buf) { + rc = -ENOMEM; + goto out; + } + old_fs = get_fs(); + set_fs(get_ds()); + ecryptfs_printk(1, KERN_NOTICE, "Calling readlink w/ " + "dentry->d_name.name = [%s]\n", dentry->d_name.name); + rc = dentry->d_inode->i_op->readlink(dentry, (char __user *)buf, len); + buf[rc] = '\0'; + set_fs(old_fs); + if (rc < 0) + goto out_free; + rc = 0; + nd_set_link(nd, buf); + goto out; +out_free: + kfree(buf); +out: + return ERR_PTR(rc); +} + +static inline void +ecryptfs_put_link(struct dentry *dentry, struct nameidata *nd, void *ptr) +{ + /* Free the char* */ + kfree(nd_get_link(nd)); +} + +/** + * Calculate the requried size of the lower file based on the specified size + * of the upper file. This calculation is based on the number of headers in + * the underlying file, the number of records (IV/HMAC/IV+HMAC) per page + * that can be stored, and the extent size of the underlying file (page size). + * + * @param crypt_stats crypt_stats associated with file + * @param upper_size size of the upper file + * @return calculated size of the lower file. + */ +static loff_t +upper_size_to_lower_size(struct ecryptfs_crypt_stats *crypt_stats, + loff_t upper_size) +{ + int upper_idx = 0; + int lower_idx; + loff_t lower_size = upper_size; + + ecryptfs_printk(1, KERN_NOTICE, "Enter; upper_size = [%lld]\n", + upper_size); + /* TODO: Does this need to be based on the extent size? */ + if (likely(upper_size > 0)) + upper_idx = (upper_size - 1) >> PAGE_CACHE_SHIFT; + else if (!upper_size) + upper_idx = 0; + else /* Sanity check, size shouldn't be negative */ + BUG(); + lower_idx = ecryptfs_pg_idx_to_lwr_pg_idx(crypt_stats, upper_idx); + lower_size = ((lower_idx + 1) << PAGE_CACHE_SHIFT); + ecryptfs_printk(1, KERN_NOTICE, "Exit; lower_size = [%lld]\n", + lower_size); + return lower_size; +} + +/** + * Function to handle truncations modifying the size of the file. Note + * that the file sizes are interpolated. When expanding, we are simply + * writing strings of 0's out. When truncating, we need to modify the + * underlying file size accordingly to the page index interpolations. + * + * N.B. No claims are made about integrety of the encrypted data when + * shrinking a file. (But its probably going to be lost) + * + * TODO: Support holes. Should we also be supporting preservation of + * the encrypted data? + * + * @param dentry The ecryptfs layer dentry + * @param new_length The length to expand the file to + * @return Zero on success; non-zero otherwise + */ +int ecryptfs_truncate(struct dentry *dentry, loff_t new_length) +{ + int rc = 0; + struct inode *inode = dentry->d_inode; + struct dentry *lower_dentry; + struct file fake_ecryptfs_file, *lower_file = NULL; + struct ecryptfs_crypt_stats *crypt_stats; + loff_t i_size = i_size_read(inode); + loff_t lower_size_before_truncate; + loff_t lower_size_after_truncate; + + ecryptfs_printk(1, KERN_NOTICE, "Enter; dentry = [%p], new_length = " + "[%lld], i_size_read(inode) = [%lld]\n", + dentry, new_length, i_size); + /* Sanity checks */ + if (unlikely((new_length == i_size))) + goto out; + crypt_stats = &ECRYPTFS_INODE_TO_PRIVATE(dentry->d_inode)->crypt_stats; + if (unlikely(!crypt_stats)) { + ecryptfs_printk(0, KERN_ERR, "NULL crypt_stats on dentry with " + "d_name.name = [%s]\n", dentry->d_name.name); + rc = -EINVAL; + goto out; + } + + /* Set up a fake ecryptfs file, this is used to interface with the file + * in the underlying filesystem so that the truncation has an effect + * there as well. */ + memset(&fake_ecryptfs_file, 0, sizeof(struct file)); + fake_ecryptfs_file.f_dentry = dentry; + /* Released at out_free: */ + ECRYPTFS_FILE_TO_PRIVATE_SM(&fake_ecryptfs_file) = + kmem_cache_alloc(ecryptfs_file_info_cache, SLAB_KERNEL); + if (unlikely(!ECRYPTFS_FILE_TO_PRIVATE(&fake_ecryptfs_file))) { + rc = -ENOMEM; + goto out; + } + lower_dentry = ecryptfs_lower_dentry(dentry); + /* This dget & mntget is released through fput at out_fput: */ + dget(lower_dentry); + mntget(ECRYPTFS_SUPERBLOCK_TO_PRIVATE(inode->i_sb)->lower_mnt); + lower_file = dentry_open( + lower_dentry, + ECRYPTFS_SUPERBLOCK_TO_PRIVATE(inode->i_sb)->lower_mnt, O_RDWR); + if (unlikely(IS_ERR(lower_file))) { + rc = PTR_ERR(lower_file); + goto out_free; + } + ECRYPTFS_FILE_TO_LOWER(&fake_ecryptfs_file) = lower_file; + + /* Switch on growing or shrinking file */ + if (new_length > i_size) { + rc = ecryptfs_fill_zeros(&fake_ecryptfs_file, new_length); + if (rc) { + ecryptfs_printk(0, KERN_ERR, + "Problem with fill_zeros\n"); + goto out_fput; + } + i_size_write(inode, new_length); + rc = ecryptfs_write_inode_size_to_header(lower_file, + lower_dentry->d_inode, + inode); + if (rc) { + ecryptfs_printk(0, KERN_ERR, + "Problem with ecryptfs_write" + "_inode_size\n"); + goto out_fput; + } + } else { /* new_length < i_size_read(inode) */ + vmtruncate(inode, new_length); + ecryptfs_write_inode_size_to_header(lower_file, + lower_dentry->d_inode, + inode); + /* We are reducing the size of the ecryptfs file, and need to + * know if we need to reduce the size of the lower file. */ + lower_size_before_truncate = + upper_size_to_lower_size(crypt_stats, i_size); + lower_size_after_truncate = + upper_size_to_lower_size(crypt_stats, new_length); + if (lower_size_after_truncate < lower_size_before_truncate) + vmtruncate(lower_dentry->d_inode, + lower_size_after_truncate); + } + /* Update the access times */ + lower_dentry->d_inode->i_mtime = CURRENT_TIME; + lower_dentry->d_inode->i_ctime = CURRENT_TIME; + mark_inode_dirty_sync(inode); +out_fput: + /* TODO: Determine which of these need to really be called. + * filp_close() will call fput(), but do we need to do the extra work + * that filp_close() provides? + if (lower_file) + filp_close(lower_file, NULL); + */ + fput(lower_file); +out_free: + if (ECRYPTFS_FILE_TO_PRIVATE(&fake_ecryptfs_file)) + kmem_cache_free(ecryptfs_file_info_cache, + ECRYPTFS_FILE_TO_PRIVATE(&fake_ecryptfs_file)); +out: + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc); + return rc; +} + +static int +ecryptfs_permission(struct inode *inode, int mask, struct nameidata *nd) +{ + struct inode *lower_inode; + int rc = 0; + + ecryptfs_printk(1, KERN_NOTICE, "Enter; inode = [%p], mask=[%d], nd =" + "[%p]\n", inode, mask, nd); + lower_inode = ECRYPTFS_INODE_TO_LOWER(inode); + if (nd) + ecryptfs_printk(1, KERN_NOTICE, "nd->dentry = [%p]\n", + nd->dentry); + rc = permission(lower_inode, mask, nd); + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc); + return rc; +} + +/** + * Updates the metadata of an inode. If the update is to the size + * i.e. truncation, then ecryptfs_truncate will handle the size modification + * of both the ecryptfs inode and the lower inode. + * + * All other metadata changes will be passed right to the lower filesystem, + * and we will just update our inode to look like the lower. + * + * @param dentry dentry handle to the inode to modify + * @param ia structure with flags of what to change and values + */ +static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia) +{ + int err = 0; + struct dentry *lower_dentry; + struct inode *inode; + struct inode *lower_inode; + struct ecryptfs_crypt_stats *crypt_stats; + ecryptfs_printk(1, KERN_NOTICE, "Enter; dentry->d_name.name = [%s]\n", + dentry->d_name.name); + crypt_stats = &ECRYPTFS_INODE_TO_PRIVATE(dentry->d_inode)->crypt_stats; + lower_dentry = ecryptfs_lower_dentry(dentry); + inode = dentry->d_inode; + lower_inode = ECRYPTFS_INODE_TO_LOWER(inode); + if (ia->ia_valid & ATTR_SIZE) { + ecryptfs_printk(1, KERN_NOTICE, + "ia->ia_valid = [0x%x] ATTR_SIZE" " = [0x%x]\n", + ia->ia_valid, ATTR_SIZE); + err = ecryptfs_truncate(dentry, ia->ia_size); + /* ecryptfs_truncate handles resizing of the lower file */ + ia->ia_valid &= ~ATTR_SIZE; + ecryptfs_printk(1, KERN_NOTICE, "ia->ia_valid = [%x]\n", + ia->ia_valid); + if (err < 0) + goto out; + } + err = notify_change(lower_dentry, ia); +out: + ecryptfs_copy_attr_all(inode, lower_inode); + ecryptfs_printk(1, KERN_NOTICE, "Exit; err = [%d]\n"); + return err; +} + +struct inode_operations ecryptfs_symlink_iops = { + .readlink = ecryptfs_readlink, + .follow_link = ecryptfs_follow_link, + .put_link = ecryptfs_put_link, + .permission = ecryptfs_permission, + .setattr = ecryptfs_setattr, +}; + +struct inode_operations ecryptfs_dir_iops = { + .create = ecryptfs_create, + .lookup = ecryptfs_lookup, + .link = ecryptfs_link, + .unlink = ecryptfs_unlink, + .symlink = ecryptfs_symlink, + .mkdir = ecryptfs_mkdir, + .rmdir = ecryptfs_rmdir, + .mknod = ecryptfs_mknod, + .rename = ecryptfs_rename, + .permission = ecryptfs_permission, + .setattr = ecryptfs_setattr, +}; + +struct inode_operations ecryptfs_main_iops = { + .permission = ecryptfs_permission, + .setattr = ecryptfs_setattr, +}; diff -urN oldtree/fs/ecryptfs/keystore.c newtree/fs/ecryptfs/keystore.c --- oldtree/fs/ecryptfs/keystore.c 1970-01-01 00:00:00.000000000 +0000 +++ newtree/fs/ecryptfs/keystore.c 2006-02-18 17:24:58.722095440 +0000 @@ -0,0 +1,820 @@ +/** + * eCryptfs: Linux filesystem encryption layer + * In-kernel key management code. Includes functions to parse and + * write authentication token-related packets with the underlying + * file. + * + * Copyright (c) 2004 International Business Machines Corp. + * Author(s): Michael A. Halcrow + * Michael C. Thompson + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include "ecryptfs_kernel.h" + +/** + * request_key returned an error instead of a valid key address; + * determine the type of error, make appropriate log entries, and + * return an error code. + */ +int process_request_key_err(long err_code) +{ + int rc = 0; + + switch (err_code) { + case ENOKEY: + ecryptfs_printk(0, KERN_WARNING, "No key\n"); + rc = -ENOENT; + break; + case EKEYEXPIRED: + ecryptfs_printk(0, KERN_WARNING, "Key expired\n"); + rc = -ETIME; + break; + case EKEYREVOKED: + ecryptfs_printk(0, KERN_WARNING, "Key revoked\n"); + rc = -EINVAL; + break; + default: + ecryptfs_printk(0, KERN_WARNING, "Unknown error code: " + "[%lu]\n", err_code); + rc = -EINVAL; + } + return rc; +} + +static 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(1, KERN_NOTICE, "Auth tok at mem loc [%p]:\n", + auth_tok); + ecryptfs_printk(1, KERN_NOTICE, " * instanceof = [%d]\n", + auth_tok->instanceof); + ecryptfs_printk(1, KERN_NOTICE, " * instantiated = [%d]\n", + auth_tok->instantiated); + switch (auth_tok->instanceof) { + case ECRYPTFS_PASSWORD: + ecryptfs_printk(1, KERN_NOTICE, " * password = [%s]\n", + auth_tok->token.password.password); + ecryptfs_printk(1, KERN_NOTICE, " * password_size = [%d]\n", + auth_tok->token.password.password_size); + ecryptfs_to_hex(salt, auth_tok->token.password.salt, + ECRYPTFS_SALT_SIZE); + salt[ECRYPTFS_SALT_SIZE * 2] = '\0'; + ecryptfs_printk(1, KERN_NOTICE, " * salt = [%s]\n", salt); + ecryptfs_printk(1, KERN_NOTICE, " * saltless = [%d]\n", + auth_tok->token.password.saltless); + memcpy(sig, auth_tok->token.password.signature, + ECRYPTFS_SIG_SIZE_HEX); + sig[ECRYPTFS_SIG_SIZE_HEX] = '\0'; + ecryptfs_printk(1, KERN_NOTICE, " * signature = [%s]\n", sig); + break; + case ECRYPTFS_PRIVATE_KEY: + ecryptfs_printk(1, KERN_NOTICE, " * (NO PRIVATE KEY SUPPORT " + "IN ECRYPTFS VERSION 0.1)\n"); + break; + default: + ecryptfs_printk(1, KERN_NOTICE, " * Unrecognized instanceof\n"); + } + ecryptfs_printk(1, KERN_NOTICE, " * 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(1, KERN_NOTICE, + " * Userspace decrypt request set\n"); + if (auth_tok->session_key.flags + & ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT) + ecryptfs_printk(1, KERN_NOTICE, + " * Userspace encrypt request set\n"); + if (auth_tok->session_key.flags & ECRYPTFS_CONTAINS_DECRYPTED_KEY) { + ecryptfs_printk(1, KERN_NOTICE, " * Contains decrypted key\n"); + ecryptfs_printk(1, KERN_NOTICE, + " * session_key.decrypted_key_size = [0x%x]\n", + auth_tok->session_key.decrypted_key_size); + ecryptfs_printk(1, KERN_NOTICE, " * 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(1, KERN_NOTICE, " * Contains encrypted key\n"); + ecryptfs_printk(1, KERN_NOTICE, + " * session_key.encrypted_key_size = [0x%x]\n", + auth_tok->session_key.encrypted_key_size); + ecryptfs_printk(1, KERN_NOTICE, " * 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); + } +} + +static void wipe_auth_tok_list(struct list_head *auth_tok_list_head) +{ + struct list_head *walker; + struct ecryptfs_auth_tok_list_item *auth_tok_list_item; + + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + walker = auth_tok_list_head->next; + while (walker != auth_tok_list_head) { + auth_tok_list_item = + list_entry(walker, struct ecryptfs_auth_tok_list_item, + list); + walker = auth_tok_list_item->list.next; + kmem_cache_free(ecryptfs_auth_tok_list_item_cache, + auth_tok_list_item); + } + ecryptfs_printk(1, KERN_NOTICE, "Exit\n"); +} + +kmem_cache_t *ecryptfs_auth_tok_list_item_cache; + +/** + * @return Zero on success + */ +static int parse_packet_length(unsigned char *data, int *offset, int *size) +{ + int rc = 0; + + if (data[(*offset)] < 192) { + /* One-byte length */ + (*size) = data[(*offset)++]; + } else if (data[(*offset)] < 224) { + /* Two-byte length */ + (*size) = ((data[(*offset)++] - 192) * 256); + (*size) += (data[(*offset)++] + 192); + } else if (data[(*offset)] == 255) { + /* Three-byte length; we're not supposed to see this */ + ecryptfs_printk(0, KERN_ERR, "Three-byte packet length not " + "supported\n"); + rc = -EINVAL; + goto out; + } else { + ecryptfs_printk(0, KERN_ERR, "Error parsing packet length\n"); + rc = -EINVAL; + goto out; + } +out: + return rc; +} + +static int write_packet_length(char *dest, int *dest_offset, int size) +{ + int rc = 0; + + if (size < 192) { + dest[(*dest_offset)] = size; + (*dest_offset)++; + } else if (size < 65536) { + dest[(*dest_offset)] = (((size - 192) / 256) + 192); + (*dest_offset)++; + dest[(*dest_offset)] = ((size - 192) % 256); + (*dest_offset)++; + } else { + rc = -EINVAL; + ecryptfs_printk(0, KERN_WARNING, + "Unsupported packet size: [%d]\n", size); + } + return rc; +} + +/** + * Passphrase packet. + * + * @return New offset in the packet set page; error value on error. + */ +static int +parse_tag_3_packet(struct ecryptfs_crypt_stats *crypt_stats, + unsigned char *data, struct list_head *auth_tok_list) +{ + int i = 0; + int rc = 0; + int body_size = 0; + struct ecryptfs_auth_tok *auth_tok; + struct ecryptfs_auth_tok_list_item *auth_tok_list_item; + + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + if (data[i++] != 0x8c) { + ecryptfs_printk(0, KERN_ERR, "Enter w/ first byte != 0x8c\n"); + rc = -EINVAL; + goto out_no_mem; + } + /* Released: wipe_auth_tok_list called in ecryptfs_parse_packet_set or + * at end of function upon failure */ + auth_tok_list_item = + kmem_cache_alloc(ecryptfs_auth_tok_list_item_cache, + SLAB_KERNEL); + if (!auth_tok_list_item) { + ecryptfs_printk(0, KERN_ERR, "Unable to allocate memory\n"); + rc = -ENOMEM; + goto out_no_mem; + } + auth_tok = &auth_tok_list_item->auth_tok; + memset(auth_tok, 0, sizeof(struct ecryptfs_auth_tok)); + /* TODO: Make *sure* it's encrypted; do this w/ policy tokens. + * Actually, this is probably not the right place to set this + * flag. If the eCryptfs marker matches, then we probably have + * a real tag 3 packet, and hence the file is most likely + * encrypted. */ + rc = parse_packet_length(data, &i, &body_size); + if (rc) + goto out; + auth_tok->session_key.encrypted_key_size = + body_size - (0x05 + ECRYPTFS_SALT_SIZE); + ecryptfs_printk(1, KERN_NOTICE, "Encrypted key size = [%d]\n", + auth_tok->session_key.encrypted_key_size); + if (data[i++] != 0x04) { + ecryptfs_printk(1, KERN_NOTICE, "Unknown version number " + "[%d]\n", data[i - 1]); + rc = -EINVAL; + goto out; + } + ecryptfs_cipher_code_to_string(crypt_stats->cipher, (u16)data[i]); + switch(data[i++]) { + case 0x07: + crypt_stats->key_size_bits = 128; + break; + case 0x08: + crypt_stats->key_size_bits = 192; + break; + case 0x09: + crypt_stats->key_size_bits = 256; + break; + } + ecryptfs_init_crypt_ctx(crypt_stats); + if (data[i++] != 0x03) { + ecryptfs_printk(0, KERN_ERR, "Only S2K ID 3 is currently " + "supported\n"); + rc = -ENOSYS; + goto out; + } + /* TODO: finish the hash mapping */ + switch (data[i++]) { + case 0x01: + /* Choose MD5 */ + memcpy(auth_tok->token.password.salt, &data[i], + ECRYPTFS_SALT_SIZE); + i += ECRYPTFS_SALT_SIZE; + auth_tok->token.password.hash_iterations = + ((u32) 16 + (data[i] & 15)) << ((data[i] >> 4) + 6); + i++; + memcpy(auth_tok->session_key.encrypted_key, &data[i], + auth_tok->session_key.encrypted_key_size); + i += auth_tok->session_key.encrypted_key_size; + auth_tok->session_key.flags &= ~ECRYPTFS_CONTAINS_DECRYPTED_KEY; + auth_tok->session_key.flags |= ECRYPTFS_CONTAINS_ENCRYPTED_KEY; + auth_tok->token.password.hash_algo = 0x01; + break; + default: + ecryptfs_printk(0, KERN_ERR, "Unsupported hash algorithm: " + "[%d]\n", data[i - 1]); + rc = -ENOSYS; + goto out; + } + /* TODO: Use the keyring */ + auth_tok->uid = current->uid; + auth_tok->instanceof = ECRYPTFS_PASSWORD; + if (data[i++] != 0xed) { + ecryptfs_printk(0, KERN_ERR, "No (ecryptfs-specific) literal " + "packet containing authentication token " + "signature found after tag 3 packet\n"); + rc = -EINVAL; + goto out; + } + if (data[i++] != (ECRYPTFS_SIG_SIZE + 13)) { + ecryptfs_printk(0, KERN_ERR, "Unrecognizable packet\n"); + rc = -EINVAL; + goto out; + } + if (data[i++] != 0x62) { + ecryptfs_printk(0, KERN_ERR, "Unrecognizable packet\n"); + rc = -EINVAL; + goto out; + } + if (data[i++] != 0x08) { + ecryptfs_printk(0, KERN_ERR, "Unrecognizable packet\n"); + rc = -EINVAL; + goto out; + } + i += 12; /* We don't care about the filename or the timestamp */ + /* TODO: Parametarize; we might actually want userspace to + * decrypt the session key. */ + auth_tok->session_key.flags &= + ~ECRYPTFS_USERSPACE_SHOULD_TRY_TO_DECRYPT; + auth_tok->session_key.flags &= + ~ECRYPTFS_USERSPACE_SHOULD_TRY_TO_ENCRYPT; + ecryptfs_to_hex(auth_tok->token.password.signature, &data[i], + ECRYPTFS_SIG_SIZE); + auth_tok->token.password.signature[ECRYPTFS_PASSWORD_SIG_SIZE] = '\0'; + rc = i; + list_add(&auth_tok_list_item->list, auth_tok_list); + crypt_stats->encrypted = 1; + goto out_success; +out: + kmem_cache_free(ecryptfs_auth_tok_list_item_cache, + auth_tok_list_item); +out_no_mem: +out_success: + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc); + return rc; +} + +/** + * Decrypt the session key with the given auth_tok. + * + * TODO: Performance: This is a good candidate for optimization. + * + * @param auth_tok + * @return 0 on success; non-zero error otherwise + */ +static int decrypt_session_key(struct ecryptfs_auth_tok *auth_tok, + struct ecryptfs_crypt_stats *crypt_stats) +{ + int rc = 0; + struct ecryptfs_password *password_s_ptr; + struct crypto_tfm *tfm = NULL; + struct scatterlist src_sg[2], dst_sg[2]; + /* TODO: Use virt_to_scatterlist for these */ + char *encrypted_session_key; + char *session_key; + + password_s_ptr = &auth_tok->token.password; + if (password_s_ptr->session_key_encryption_key_set) + ecryptfs_printk(1, KERN_NOTICE, "Session key encryption key " + "set; skipping key generation\n"); + ecryptfs_printk(1, KERN_NOTICE, "Session key encryption key (size [%d])" + ":\n", password_s_ptr->session_key_encryption_key_size); + if (ecryptfs_verbosity > 0) + ecryptfs_dump_hex(password_s_ptr->session_key_encryption_key, + password_s_ptr-> + session_key_encryption_key_size); + tfm = crypto_alloc_tfm(crypt_stats->cipher, 0); + if (!tfm) { + ecryptfs_printk(0, KERN_ERR, "Error allocating crypto " + "context\n"); + rc = -ENOMEM; + goto out; + } + crypto_cipher_setkey(tfm, password_s_ptr->session_key_encryption_key, + password_s_ptr->session_key_encryption_key_size); + /* TODO: virt_to_scatterlist */ + encrypted_session_key = (char *)__get_free_page(GFP_KERNEL); + if (!encrypted_session_key) { + ecryptfs_printk(0, KERN_ERR, "Out of memory\n"); + rc = -ENOMEM; + goto out; + } + session_key = (char *)__get_free_page(GFP_KERNEL); + if (!session_key) { + kfree(encrypted_session_key); + ecryptfs_printk(0, KERN_ERR, "Out of memory\n"); + rc = -ENOMEM; + goto out; + } + memcpy(encrypted_session_key, auth_tok->session_key.encrypted_key, + auth_tok->session_key.encrypted_key_size); + src_sg[0].page = virt_to_page(encrypted_session_key); + src_sg[0].offset = 0; + /* TODO: key_size < PAGE_CACHE_SIZE */ + src_sg[0].length = auth_tok->session_key.encrypted_key_size; + dst_sg[0].page = virt_to_page(session_key); + dst_sg[0].offset = 0; + auth_tok->session_key.decrypted_key_size = + auth_tok->session_key.encrypted_key_size; + dst_sg[0].length = auth_tok->session_key.encrypted_key_size; + /* TODO: Handle error condition */ + crypto_cipher_decrypt(tfm, dst_sg, src_sg, + auth_tok->session_key.encrypted_key_size); + auth_tok->session_key.decrypted_key_size = + auth_tok->session_key.encrypted_key_size; + memcpy(auth_tok->session_key.decrypted_key, session_key, + auth_tok->session_key.decrypted_key_size); + auth_tok->session_key.flags |= ECRYPTFS_CONTAINS_DECRYPTED_KEY; + memcpy(crypt_stats->key, auth_tok->session_key.decrypted_key, + auth_tok->session_key.decrypted_key_size); + crypt_stats->key_valid = 1; + crypt_stats->key_size_bits = + auth_tok->session_key.decrypted_key_size * 8; + ecryptfs_printk(1, KERN_NOTICE, "Decrypted session key:\n"); + if (ecryptfs_verbosity > 0) + ecryptfs_dump_hex(crypt_stats->key, + crypt_stats->key_size_bits / 8); + free_page((unsigned long)encrypted_session_key); + free_page((unsigned long)session_key); +out: + if (tfm) + crypto_free_tfm(tfm); + return rc; +} + +/** + * N.B. This comment is applicable to 0.2 release (and later) only. + * Extract the authentication token signatures. eCryptfs expects this + * function to recover the symmetric key of the crypt_stats structure + * if at all possible, given the current packet set. Authentication + * tokens are composed of the tokens themselves and their descriptors. + * It is possible to have an authentication token object in the + * keyring that only has a descriptor and not a token component. Each + * eCryptfs file header contains a concatenation of descriptors. For + * each descriptor, this function assures that an authentication token + * object is instantiated in the keyring. This instantiation takes + * place in the request_key callout application. Thus, the callout + * application requires the complete descriptor. The crypt_stats + * object contains a set of descriptors that apply to this file. When + * the headers are written out, they are re-constructed from the set + * of authentication token descriptors. + * + * If at any point we have a problem parsing the packets, we will -EIO and + * just bail out. + * + * GOAL: Get crypt_stats to have the file's session key. + * + * @param dest The header page in memory + * @param version Version of file format, to guide parsing behavior + * @return 0 if a valid authentication token was retrieved and processed; + * negative value for file not encrypted or for error conditions + */ +int ecryptfs_parse_packet_set(unsigned char *dest, + struct ecryptfs_crypt_stats *crypt_stats, + struct dentry *ecryptfs_dentry, int version) +{ + int i = 0; + int rc = 0; + int found_auth_tok = 0; + int next_packet_is_auth_tok_packet; + char sig[ECRYPTFS_SIG_SIZE_HEX]; + struct list_head auth_tok_list; + struct list_head *walker; + struct ecryptfs_auth_tok *chosen_auth_tok = NULL; + struct ecryptfs_mount_crypt_stats *mount_crypt_stats = + &(ECRYPTFS_SUPERBLOCK_TO_PRIVATE( + ecryptfs_dentry->d_sb)->mount_crypt_stats); + struct ecryptfs_auth_tok *candidate_auth_tok = NULL; + + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + ecryptfs_set_default_sizes(crypt_stats); + INIT_LIST_HEAD(&auth_tok_list); + + /* Parse the header to find as many packets as we can, these will be + * added the our &auth_tok_list */ + next_packet_is_auth_tok_packet = 1; + while (next_packet_is_auth_tok_packet) + switch (dest[i]) { + case 0x8c: /* tag 3 packet; s2k */ + rc = parse_tag_3_packet(crypt_stats, + (unsigned char *)&dest[i], + &auth_tok_list); + if (rc != -EINVAL && rc != -ENOMEM) { + i += rc; + rc = 0; + } else { + ecryptfs_printk(0, KERN_ERR, "Error parsing " + "tag 3 packet\n"); + rc = -EIO; + goto out_wipe_list; + } + break; + default: + ecryptfs_printk(1, KERN_NOTICE, "No packet at offset " + "[%d] of the file header; hex value of " + "character is [0x%.2x]\n", i, dest[i]); + next_packet_is_auth_tok_packet = 0; + } + if (list_empty(&auth_tok_list)) { + rc = -EINVAL; /* Do not support non-encrypted files */ + goto out; + } + + /* If we have a global auth tok, then use it should be tried */ + if (mount_crypt_stats->global_auth_tok) { + memcpy(sig, mount_crypt_stats->global_auth_tok_sig, + ECRYPTFS_SIG_SIZE_HEX); + chosen_auth_tok = mount_crypt_stats->global_auth_tok; + } else + BUG(); /* This should not be the case in 0.1 release */ + /* Scan list to see if our chosen_auth_tok works */ + list_for_each(walker, &auth_tok_list) { + struct ecryptfs_auth_tok_list_item *auth_tok_list_item; + auth_tok_list_item = + list_entry(walker, struct ecryptfs_auth_tok_list_item, + list); + candidate_auth_tok = &auth_tok_list_item->auth_tok; + if (unlikely(ecryptfs_verbosity > 0)) { + ecryptfs_printk(1, KERN_NOTICE, + "Considering cadidate auth tok:\n"); + ecryptfs_dump_auth_tok(candidate_auth_tok); + } + /* TODO: Replace ECRYPTFS_SIG_SIZE_HEX w/ dynamic value */ + if ((candidate_auth_tok->instanceof == ECRYPTFS_PASSWORD) && + !strncmp(candidate_auth_tok->token.password.signature, + sig, ECRYPTFS_SIG_SIZE_HEX)) { + found_auth_tok = 1; + goto leave_list; + /* TODO: Transfer the common salt into the + * crypt_stats salt */ + } + } +leave_list: + if (!found_auth_tok) { + ecryptfs_printk(0, KERN_ERR, "Could not find authentication " + "token on temporary list for sig [%.*s]\n", + ECRYPTFS_SIG_SIZE_HEX, sig); + goto out_wipe_list; + } else { + memcpy(&(candidate_auth_tok->token.password), + &(chosen_auth_tok->token.password), + sizeof(struct ecryptfs_password)); + decrypt_session_key(candidate_auth_tok, crypt_stats); + } + +out_wipe_list: + wipe_auth_tok_list(&auth_tok_list); +out: + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc); + return rc; +} + +/** + * Write passphrase packet + * + * @param dest Buffer into which to write the packet + * @param max Maximum number of bytes that can be writtn + * @return Number of bytes written; 0 on error + */ +static int +write_tag_3_packet(char *dest, int dest_offset, int max, + struct ecryptfs_auth_tok *auth_tok, + struct ecryptfs_crypt_stats *crypt_stats) +{ + int rc = 0; + struct ecryptfs_key_record key_rec; + int i; + int signature_is_valid = 0; + int encrypted_session_key_valid = 0; + char session_key_encryption_key[ECRYPTFS_MAX_KEY_BYTES]; + struct scatterlist dest_sg[2]; + struct scatterlist src_sg[2]; + struct crypto_tfm *tfm = NULL; + int key_rec_size; + int offset_save = 0; + + ecryptfs_printk(1, KERN_NOTICE, "Enter; dest_offset = [%d]\n", + dest_offset); + /* Check for a valid signature on the auth_tok */ + for (i = 0; i < ECRYPTFS_SIG_SIZE_HEX; i++) + signature_is_valid |= auth_tok->token.password.signature[i]; + if (!signature_is_valid) + BUG(); + ecryptfs_from_hex(key_rec.sig, auth_tok->token.password.signature, + ECRYPTFS_SIG_SIZE); + key_rec.enc_key_size_bits = crypt_stats->key_size_bits; + key_rec.type = ECRYPTFS_PACKET_SET_TYPE_PASSWORD; + + encrypted_session_key_valid = 0; + if (auth_tok->session_key.encrypted_key_size == 0) + auth_tok->session_key.encrypted_key_size = + ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES; + for (i = 0; i < auth_tok->session_key.encrypted_key_size; i++) + encrypted_session_key_valid |= + auth_tok->session_key.encrypted_key[i]; + if (auth_tok->session_key.encrypted_key_size == 0) { + ecryptfs_printk(0, KERN_WARNING, "auth_tok->session_key." + "encrypted_key_size == 0"); + auth_tok->session_key.encrypted_key_size = + ECRYPTFS_DEFAULT_KEY_BYTES; + } + if (encrypted_session_key_valid) { + memcpy(key_rec.enc_key, + auth_tok->session_key.encrypted_key, + auth_tok->session_key.encrypted_key_size); + goto encrypted_session_key_set; + } + if (auth_tok->token.password.session_key_encryption_key_set) { + ecryptfs_printk(1, KERN_NOTICE, "Using previously generated " + "session key encryption key of size [%d]\n", + auth_tok->token.password. + session_key_encryption_key_size); + memcpy(session_key_encryption_key, + auth_tok->token.password.session_key_encryption_key, + auth_tok->token.password. + session_key_encryption_key_size); + ecryptfs_printk(1, KERN_NOTICE, + "Cached session key " "encryption key: \n"); + if (ecryptfs_verbosity > 0) + ecryptfs_dump_hex(session_key_encryption_key, 16); + goto session_key_encryption_key_set; + } + session_key_encryption_key_set: + if (unlikely(ecryptfs_verbosity > 0)) { + ecryptfs_printk(1, KERN_NOTICE, "Session key encryption key:" + "\n"); + ecryptfs_dump_hex(session_key_encryption_key, 16); + } + /* Encrypt the key with the key encryption key */ + /* Set up the scatterlists */ + rc = virt_to_scatterlist(crypt_stats->key, + crypt_stats->key_size_bits / 8, src_sg, 2); + if (!rc) { + ecryptfs_printk(0, KERN_ERR, "Error generating scatterlist " + "for crypt_stats session key\n"); + rc = -ENOMEM; + goto out; + } + rc = virt_to_scatterlist(key_rec.enc_key, + key_rec.enc_key_size_bits / 8, dest_sg, 2); + if (!rc) { + ecryptfs_printk(0, KERN_ERR, "Error generating scatterlist " + "for crypt_stats encrypted session key\n"); + rc = -ENOMEM; + goto out; + } + /* Initialize the key encryption context */ + ASSERT(crypt_stats->cipher); + if ((tfm = crypto_alloc_tfm(crypt_stats->cipher, 0)) == NULL) { + ecryptfs_printk(0, KERN_ERR, "Could not initialize crypto " + "context for cipher [%s]\n", + crypt_stats->cipher); + rc = 0; + goto out; + } + /* Set the key encryption key */ + rc = crypto_cipher_setkey(tfm, session_key_encryption_key, + ECRYPTFS_DEFAULT_KEY_BYTES); + if (rc < 0) { + ecryptfs_printk(0, KERN_ERR, "Error setting key for crypto " + "context\n"); + rc = 0; + goto out; + } + ecryptfs_printk(1, KERN_NOTICE, "Encrypting [%d] bytes of the key\n", + crypt_stats->key_size_bits / 8); + crypto_cipher_encrypt(tfm, dest_sg, src_sg, + crypt_stats->key_size_bits / 8); + + ecryptfs_printk(1, KERN_NOTICE, "This should be the encrypted key:\n"); + if (ecryptfs_verbosity > 0) + ecryptfs_dump_hex(key_rec.enc_key, + key_rec.enc_key_size_bits / 8); +encrypted_session_key_set: + /* Now we have a valid key_rec. Append it to the + * key_rec set. */ + key_rec_size = KEY_REC_SIZE(key_rec); + if ((dest_offset + key_rec_size) >= ECRYPTFS_MAX_KEYSET_SIZE) { + ecryptfs_printk(0, KERN_ERR, "Keyset too large\n"); + rc = 0; + goto out; + } + offset_save = dest_offset; + if ((dest_offset + 0x05 + ECRYPTFS_SALT_SIZE + + (key_rec.enc_key_size_bits / 8)) >= PAGE_CACHE_SIZE) { + ecryptfs_printk(0, KERN_ERR, "Too many authentication tokens; " + "cryptfs does not yet support this many\n"); + rc = 0; + goto out; + } + /* This format is inspired by OpenPGP; see RFC 2440 + * packet tag 3 */ + *(dest + (dest_offset++)) = 0x8c; /* tag 3 */ + /* ver+cipher+s2k+hash+salt+iter+enc_key */ + rc = write_packet_length(dest, &dest_offset, + (0x05 + ECRYPTFS_SALT_SIZE + + (key_rec.enc_key_size_bits / 8))); + *(dest + (dest_offset++)) = 0x04; /* version 4 */ + + rc = ecryptfs_code_for_cipher_string(crypt_stats->cipher); + if (rc == 0) { + ecryptfs_printk(0, KERN_WARNING, "Unable to generate code for " + "cipher [%s]\n", crypt_stats->cipher); + goto out; + } + if (rc == 0x07) { + switch (crypt_stats->key_size_bits) { + case 128: + break; + case 192: + rc = 0x08; /* AES-192 */ + break; + case 256: + rc = 0x09; /* AES-256 */ + break; + default: + rc = -EINVAL; + ecryptfs_printk(0, KERN_WARNING, "Unsupported AES key " + "size: [%d]\n", + crypt_stats->key_size_bits); + goto out; + } + } + *(dest + (dest_offset++)) = rc; + rc = 0; + *(dest + (dest_offset++)) = 0x03; /* S2K */ + *(dest + (dest_offset++)) = 0x01; /* MD5 (TODO) */ + memcpy((dest + dest_offset), auth_tok->token.password.salt, + ECRYPTFS_SALT_SIZE); + dest_offset += ECRYPTFS_SALT_SIZE; /* salt */ + *(dest + (dest_offset++)) = 0x60; /* hash iterations */ + memcpy((dest + dest_offset), key_rec.enc_key, + key_rec.enc_key_size_bits / 8); + dest_offset += (key_rec.enc_key_size_bits / 8); + /* Write auth tok signature packet */ + *(dest + (dest_offset++)) = 0xed; /* tag 11 */ + *(dest + (dest_offset++)) = ECRYPTFS_SIG_SIZE + 13; /* packet + * length */ + *(dest + (dest_offset++)) = 0x62; /* binary type */ + *(dest + (dest_offset++)) = 0x08; /* filename length */ + strncpy((dest + dest_offset), "_CONSOLE", 0x08); + dest_offset += 0x08; + memset((dest + dest_offset), 0, 4); + dest_offset += 4; + memcpy((dest + dest_offset), key_rec.sig, ECRYPTFS_SIG_SIZE); + dest_offset += ECRYPTFS_SIG_SIZE; + *(dest + (dest_offset)) = 0x00; /* NULL terminator */ + rc = dest_offset; +out: + if (tfm) + crypto_free_tfm(tfm); + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc); + return rc; +} + +/** + * Generates a key packet set and writes it to the virtual address + * passed in. + * + * @param dest Page to which to write the key record set + * @param crypt_stats The cryptographic context from which the authentication + * tokens will be retrieved + * @param len The amount written + * @return Zero on success + */ +int +ecryptfs_generate_key_packet_set(char *dest_base, + struct ecryptfs_crypt_stats *crypt_stats, + struct dentry *ecryptfs_dentry, int *len) +{ + int rc = 0; + struct ecryptfs_auth_tok *auth_tok; + int dest_offset = 0; + struct ecryptfs_mount_crypt_stats *mount_crypt_stats = + &(ECRYPTFS_SUPERBLOCK_TO_PRIVATE( + ecryptfs_dentry->d_sb)->mount_crypt_stats); + int written; + + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + (*len) = 0; + if (mount_crypt_stats->global_auth_tok) { + auth_tok = mount_crypt_stats->global_auth_tok; + switch (auth_tok->instanceof) { + case ECRYPTFS_PASSWORD: + written = write_tag_3_packet(dest_base, dest_offset, + PAGE_CACHE_SIZE, auth_tok, + crypt_stats); + break; + default: + ecryptfs_printk(0, KERN_WARNING, "Unknown " + "authentication token type [%d]\n", + auth_tok->instanceof); + rc = -EINVAL; + goto out; + } + if (written == 0) { + ecryptfs_printk(1, KERN_WARNING, "Error writing " + "authentication token packet with sig " + "= [%s]\n", + mount_crypt_stats->global_auth_tok_sig); + rc = -EIO; + goto out; + } else { + dest_offset += written; + (*len) += written; + } + } else + BUG(); + out: + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc); + return rc; +} diff -urN oldtree/fs/ecryptfs/main.c newtree/fs/ecryptfs/main.c --- oldtree/fs/ecryptfs/main.c 1970-01-01 00:00:00.000000000 +0000 +++ newtree/fs/ecryptfs/main.c 2006-02-18 17:24:58.724095136 +0000 @@ -0,0 +1,734 @@ +/** + * eCryptfs: Linux filesystem encryption layer + * + * Copyright (c) 1997-2003 Erez Zadok + * Copyright (c) 2001-2003 Stony Brook University + * Copyright (c) 2004 International Business Machines Corp. + * Author(s): Michael A. Halcrow + * Michael C. Thompson + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ecryptfs_kernel.h" + +/** + * Module parameter that defines the ecryptfs_verbosity level. + */ +int ecryptfs_verbosity = 1; + +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(int verb, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + if ((ecryptfs_verbosity >= verb) && printk_ratelimit()) + vprintk(fmt, args); + va_end(args); +} + +/** + * Interposes upper and lower dentries. + * This function will call an ecryptfs_inode into existance through the call to + * iget(sb, lower_inode->i_ino). + * + * @param lower_dentry existing dentry in the lower filesystem + * @param dentry ecryptfs' dentry + * @param sb eCryptfs's super_block + * @param flag If set to true, then d_add is called, else d_instantiate + * is called. + * @return 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; + int err = 0; + struct inode *inode; + + ecryptfs_printk(1, KERN_NOTICE, "Enter; lower_dentry = [%p], " + "lower_dentry->d_name.name = [%s], dentry = " + "[%p], dentry->d_name.name = [%s], sb = [%p]; " + "flag = [%.4x]; lower_dentry->d_count " + "= [%d]; dentry->d_count = [%d]\n", lower_dentry, + lower_dentry->d_name.name, dentry, dentry->d_name.name, + sb, flag, atomic_read(&lower_dentry->d_count), + atomic_read(&dentry->d_count)); + lower_inode = lower_dentry->d_inode; + if (lower_inode->i_sb != ECRYPTFS_SUPERBLOCK_TO_LOWER(sb)) { + err = -EXDEV; + goto out; + } + inode = iget(sb, lower_inode->i_ino); + if (!inode) { + err = -EACCES; + goto out; + } + /* This check is required here because if we failed to allocated the + * required space for an inode_info_cache struct, then the only way + * we know we failed, is by the pointer being NULL */ + if (!ECRYPTFS_INODE_TO_PRIVATE(inode)) { + ecryptfs_printk(1, KERN_ERR, "Out of memory. Failure to " + "allocate memory in ecryptfs_read_inode.\n"); + err = -ENOMEM; + BUG(); + goto out; + } + + if (NULL == ECRYPTFS_INODE_TO_LOWER(inode)) { + ECRYPTFS_INODE_TO_LOWER(inode) = igrab(lower_inode); + /* If we are still NULL at this point, igrab failed. + * We are _NOT_ supposed to be failing here */ + if (NULL == ECRYPTFS_INODE_TO_LOWER(inode)) { + BUG(); + err = -EINVAL; + goto out; + } + } + 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: + ecryptfs_printk(1, KERN_NOTICE, "Exit; err = %d\n", err); + return err; +} + +/** + * For rvalue references; better to just use the macro. + */ +inline struct dentry *ecryptfs_lower_dentry(struct dentry *dentry) +{ + ASSERT(dentry); + return ECRYPTFS_DENTRY_TO_LOWER(dentry); +} + +enum { ecryptfs_opt_sig, ecryptfs_opt_debug, ecryptfs_opt_cipher, + ecryptfs_opt_err +}; + +static match_table_t tokens = { + {ecryptfs_opt_sig, "sig=%s"}, + {ecryptfs_opt_debug, "debug=%u"}, + {ecryptfs_opt_cipher, "cipher=%s"}, + {ecryptfs_opt_err, NULL} +}; + +/** + * 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. + * + * N.B. 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. + * + * @param sb + * @param options + * @return 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; + struct key *auth_tok_key = NULL; + struct ecryptfs_auth_tok *auth_tok = NULL; + struct ecryptfs_mount_crypt_stats *mount_crypt_stats = + &(ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb)->mount_crypt_stats); + 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; + int cipher_name_len; + + ecryptfs_printk(1, KERN_NOTICE, "Enter; options = [%s]\n", options); + 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: + sig_src = args[0].from; + sig_dst = + mount_crypt_stats->global_auth_tok_sig; + memcpy(sig_dst, sig_src, ECRYPTFS_SIG_SIZE_HEX); + sig_dst[ECRYPTFS_SIG_SIZE_HEX] = '\0'; + ecryptfs_printk(1, KERN_NOTICE, + "The mount_crypt_stats " + "global_auth_tok_sig set to: " + "[%s]\n", sig_dst); + sig_set = 1; + break; + case ecryptfs_opt_debug: + debug_src = args[0].from; + ecryptfs_verbosity = + (int)simple_strtol(debug_src, &debug_src, + 0); + ecryptfs_printk(1, KERN_NOTICE, + "Verbosity set to [%d]" "\n", + ecryptfs_verbosity); + break; + case ecryptfs_opt_cipher: + cipher_name_src = args[0].from; + cipher_name_dst = + mount_crypt_stats-> + global_default_cipher_name; + strncpy(cipher_name_dst, cipher_name_src, + ECRYPTFS_MAX_CIPHER_NAME_SIZE); + ecryptfs_printk(1, KERN_NOTICE, + "The mount_crypt_stats " + "global_default_cipher_name set to: " + "[%s]\n", cipher_name_dst); + cipher_name_set = 1; + break; + case ecryptfs_opt_err: + default: + ecryptfs_printk(1, KERN_WARNING, + "eCryptfs: unrecognized option '%s'\n", + options); + } + } + /* Do not support lack of mount-wide signature in 0.1 + * release */ + if (!sig_set) { + rc = -EINVAL; + ecryptfs_printk(0, 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_stats->global_default_cipher_name, + ECRYPTFS_DEFAULT_CIPHER, cipher_name_len); + mount_crypt_stats->global_default_cipher_name[cipher_name_len] + = '\0'; + } + ecryptfs_printk(1, KERN_NOTICE, "Requesting the key with description: " + "[%s]\n", mount_crypt_stats->global_auth_tok_sig); + /* N.B. 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_stats->global_auth_tok_sig, + NULL); + if (!auth_tok_key || IS_ERR(auth_tok_key)) { + ecryptfs_printk(0, KERN_ERR, "Could not find key with " + "description: [%s]\n", + mount_crypt_stats->global_auth_tok_sig); + process_request_key_err(PTR_ERR(auth_tok_key)); + rc = -EINVAL; + goto out; + } + auth_tok = (struct ecryptfs_auth_tok *)KEY_PAYLOAD_DATA(auth_tok_key); + if (auth_tok->instanceof != ECRYPTFS_PASSWORD) { + ecryptfs_printk(0, KERN_ERR, "Invalid auth_tok structure " + "returned from key"); + rc = -EINVAL; + goto out; + } + mount_crypt_stats->global_auth_tok_key = auth_tok_key; + mount_crypt_stats->global_auth_tok = auth_tok; +out: + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc); + return rc; +} + +kmem_cache_t *ecryptfs_sb_info_cache; + +/** + * Preform the cleanup for ecryptfs_read_super() + */ +static inline void ecryptfs_cleanup_read_super(struct super_block *sb) +{ + ecryptfs_printk(1, KERN_NOTICE, "Enter; sb = [%p], sb->s_root = [%p] " + "ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb) = [%p] " + "sb->s_root.d_name->name = [%s]\n", sb, + sb->s_root, ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb), + sb->s_root->d_name.name); + up_write(&sb->s_umount); + deactivate_super(sb); + ecryptfs_printk(1, KERN_NOTICE, "Exit\n"); +} + +/** + * Sets up what we can of the sb, rest is done in ecryptfs_read_super + * + * @param sb The ecryptfs super block + * @param raw_data The options passed to mount + * @param silent Not used but required by function prototype + * @return Zero on success; non-zero otherwise + */ +static int +ecryptfs_fill_super(struct super_block *sb, void *raw_data, int silent) +{ + int err = 0; + + ecryptfs_printk(1, KERN_NOTICE, "Enter; sb = [%p] raw_data = [%s] " + "silent = [%d]\n", sb, (char *)raw_data, silent); + /* Released in ecryptfs_put_super() */ + ECRYPTFS_SUPERBLOCK_TO_PRIVATE_SM(sb) = + kmem_cache_alloc(ecryptfs_sb_info_cache, SLAB_KERNEL); + if (!ECRYPTFS_SUPERBLOCK_TO_PRIVATE_SM(sb)) { + ecryptfs_printk(0, KERN_WARNING, "Out of memory\n"); + err = -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(0, KERN_ERR, "d_alloc failed\n"); + err = -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_DENTRY_TO_PRIVATE_SM(sb->s_root) = + (struct ecryptfs_dentry_info *) + kmem_cache_alloc(ecryptfs_dentry_info_cache, SLAB_KERNEL); + if (!ECRYPTFS_DENTRY_TO_PRIVATE_SM(sb->s_root)) { + ecryptfs_printk(0, KERN_ERR, + "dentry_info_cache alloc failed\n"); + err = -ENOMEM; + goto out; + } + memset(ECRYPTFS_DENTRY_TO_PRIVATE(sb->s_root), 0, + sizeof(struct ecryptfs_dentry_info)); + err = 0; +out: + /* Should be able to rely on deactive_super called from get_sb_nodev */ + ecryptfs_printk(1, KERN_NOTICE, "Exit; err = [%d]\n", err); + return err; +} + +/** + * 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 err; + struct nameidata nd; + struct dentry *lower_root; + + memset(&nd, 0, sizeof(struct nameidata)); + ecryptfs_printk(1, KERN_NOTICE, "Enter; sb = [%p], dev_name = [%s]\n", + sb, dev_name); + err = path_lookup(dev_name, LOOKUP_FOLLOW, &nd); + if (err) { + ecryptfs_printk(0, KERN_WARNING, "path_lookup() failed\n"); + goto out_free; + } + lower_root = nd.dentry; + ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb)->lower_mnt = nd.mnt; + if (!lower_root->d_inode) { + ecryptfs_printk(0, KERN_WARNING, + "No directory to interpose on\n"); + err = -ENOENT; + goto out_free; + } + ECRYPTFS_SUPERBLOCK_TO_LOWER(sb) = lower_root->d_sb; + sb->s_maxbytes = lower_root->d_sb->s_maxbytes; + ECRYPTFS_DENTRY_TO_LOWER(sb->s_root) = lower_root; + if ((err = ecryptfs_interpose(lower_root, sb->s_root, sb, 0))) + goto out_free; + err = 0; + goto out; +out_free: + path_release(&nd); + ecryptfs_cleanup_read_super(sb); +out: + ecryptfs_printk(1, KERN_NOTICE, "Exit; err = [%d]\n", err); + return err; +} + +/** + * 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 struct super_block *ecryptfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *raw_data) +{ + int err; + struct super_block *sb = NULL; + + ecryptfs_printk(1, KERN_NOTICE, "Enter; fs_type = [%p], flags = [%d]," + " dev_name = [%s], raw_data = [%s]\n", + fs_type, flags, dev_name, (char *)raw_data); + sb = get_sb_nodev(fs_type, flags, raw_data, ecryptfs_fill_super); + if (IS_ERR(sb)) { + ecryptfs_printk(0, KERN_ERR, "Getting sb failed. " + "sb = [%p]\n", sb); + goto out; + } + err = ecryptfs_parse_options(sb, raw_data); + if (err) { + sb = ERR_PTR(err); + goto out; + } + err = ecryptfs_read_super(sb, dev_name); + if (err) { + sb = ERR_PTR(err); + ecryptfs_printk(0, KERN_ERR, "Reading sb failed. " + "sb = [%p]\n", sb); + } +out: + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%p]\n", sb); + return sb; +} + +/** + * 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) +{ + ecryptfs_printk(1, KERN_NOTICE, "Enter; sb = [%p], sb->s_root = [%p] " + "ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb) = [%p]\n", sb, + sb->s_root, ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb)); + generic_shutdown_super(sb); + ecryptfs_printk(1, KERN_NOTICE, "Exit\n"); +} + +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 +}; + +/** + * Initializes the ecryptfs_inode_info_cache when it is created + */ +static void +inode_info_init_once(void *vptr, kmem_cache_t * 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); +} + +/* This provides a means of backing out cache creations out of the kernel + * so that we can elegantly fail should we run out of memory. + */ +#define ECRYPTFS_AUTH_TOK_LIST_ITEM_CACHE 0x0001 +#define ECRYPTFS_AUTH_TOK_PKT_SET_CACHE 0x0002 +#define ECRYPTFS_AUTH_TOK_REQUEST_CACHE 0x0004 +#define ECRYPTFS_AUTH_TOK_REQUEST_BLOB_CACHE 0x0008 +#define ECRYPTFS_FILE_INFO_CACHE 0x0010 +#define ECRYPTFS_DENTRY_INFO_CACHE 0x0020 +#define ECRYPTFS_INODE_INFO_CACHE 0x0040 +#define ECRYPTFS_SB_INFO_CACHE 0x0080 +#define ECRYPTFS_HEADER_CACHE_0 0x0100 +#define ECRYPTFS_HEADER_CACHE_1 0x0200 +#define ECRYPTFS_HEADER_CACHE_2 0x0400 +#define ECRYPTFS_LOWER_PAGE_CACHE 0x0800 +#define ECRYPTFS_CACHE_CREATION_SUCCESS 0x0FF1 + +static short ecryptfs_allocated_caches; + +/** + * @return Zero on success; non-zero otherwise + * + * Sets ecryptfs_allocated_caches with flags so that we can + * free created caches should we run out of memory during + * creation period. + * + * The overhead for doing this is offset by the fact that we + * only do this once, and that should there be insufficient + * memory, then we can elegantly fail, and not leave extra + * caches around, or worse, panic the kernel trying to free + * something that's not there. + */ +static int ecryptfs_init_kmem_caches(void) +{ + int rc = 0; + + ecryptfs_auth_tok_list_item_cache = + kmem_cache_create("ecryptfs_auth_tok_list_item", + sizeof(struct ecryptfs_auth_tok_list_item), + 0, SLAB_HWCACHE_ALIGN, NULL, NULL); + if (ecryptfs_auth_tok_list_item_cache) + rc |= ECRYPTFS_AUTH_TOK_LIST_ITEM_CACHE; + else + ecryptfs_printk(0, KERN_WARNING, "ecryptfs_auth_tok_list_item " + "kmem_cache_create failed\n"); + + ecryptfs_file_info_cache = + kmem_cache_create("ecryptfs_file_cache", + sizeof(struct ecryptfs_file_info), + 0, SLAB_HWCACHE_ALIGN, NULL, NULL); + if (ecryptfs_file_info_cache) + rc |= ECRYPTFS_FILE_INFO_CACHE; + else + ecryptfs_printk(0, KERN_WARNING, "ecryptfs_file_cache " + "kmem_cache_create failed\n"); + + ecryptfs_dentry_info_cache = + kmem_cache_create("ecryptfs_dentry_cache", + sizeof(struct ecryptfs_dentry_info), + 0, SLAB_HWCACHE_ALIGN, NULL, NULL); + if (ecryptfs_dentry_info_cache) + rc |= ECRYPTFS_DENTRY_INFO_CACHE; + else + ecryptfs_printk(0, KERN_WARNING, "ecryptfs_dentry_cache " + "kmem_cache_create failed\n"); + + ecryptfs_inode_info_cache = + kmem_cache_create("ecryptfs_inode_cache", + sizeof(struct ecryptfs_inode_info), 0, + SLAB_HWCACHE_ALIGN, inode_info_init_once, NULL); + if (ecryptfs_inode_info_cache) + rc |= ECRYPTFS_INODE_INFO_CACHE; + else + ecryptfs_printk(0, KERN_WARNING, "ecryptfs_inode_cache " + "kmem_cache_create failed\n"); + + ecryptfs_sb_info_cache = + kmem_cache_create("ecryptfs_sb_cache", + sizeof(struct ecryptfs_sb_info), + 0, SLAB_HWCACHE_ALIGN, NULL, NULL); + if (ecryptfs_sb_info_cache) + rc |= ECRYPTFS_SB_INFO_CACHE; + else + ecryptfs_printk(0, KERN_WARNING, "ecryptfs_sb_cache " + "kmem_cache_create failed\n"); + + ecryptfs_header_cache_0 = + kmem_cache_create("ecryptfs_headers_0", PAGE_CACHE_SIZE, + 0, SLAB_HWCACHE_ALIGN, NULL, NULL); + if (ecryptfs_header_cache_0) + rc |= ECRYPTFS_HEADER_CACHE_0; + else + ecryptfs_printk(0, KERN_WARNING, "ecryptfs_headers_0 " + "kmem_cache_create failed\n"); + + ecryptfs_header_cache_1 = + kmem_cache_create("ecryptfs_headers_1", PAGE_CACHE_SIZE, + 0, SLAB_HWCACHE_ALIGN, NULL, NULL); + if (ecryptfs_header_cache_1) + rc |= ECRYPTFS_HEADER_CACHE_1; + else + ecryptfs_printk(0, KERN_WARNING, "ecryptfs_headers_1 " + "kmem_cache_create failed\n"); + + ecryptfs_header_cache_2 = + kmem_cache_create("ecryptfs_headers_2", PAGE_CACHE_SIZE, + 0, SLAB_HWCACHE_ALIGN, NULL, NULL); + if (ecryptfs_header_cache_2) + rc |= ECRYPTFS_HEADER_CACHE_2; + else + ecryptfs_printk(0, KERN_WARNING, "ecryptfs_headers_2 " + "kmem_cache_create failed\n"); + + ecryptfs_lower_page_cache = + kmem_cache_create("ecryptfs_lower_page_cache", PAGE_CACHE_SIZE, + 0, SLAB_HWCACHE_ALIGN, NULL, NULL); + if (ecryptfs_lower_page_cache) + rc |= ECRYPTFS_LOWER_PAGE_CACHE; + else + ecryptfs_printk(0, KERN_WARNING, "ecryptfs_lower_page_cache " + "kmem_cache_create failed\n"); + + ecryptfs_allocated_caches = rc; + rc = ECRYPTFS_CACHE_CREATION_SUCCESS ^ rc; + return rc; +} + +/** + * @return Zero on success; non-zero otherwise + */ +static int ecryptfs_free_kmem_caches(void) +{ + int rc = 0; + int err; + + if (ecryptfs_allocated_caches & ECRYPTFS_AUTH_TOK_LIST_ITEM_CACHE) { + rc = kmem_cache_destroy(ecryptfs_auth_tok_list_item_cache); + if (rc) + ecryptfs_printk(0, KERN_WARNING, + "Not all ecryptfs_auth_tok_" + "list_item_cache structures were " + "freed\n"); + } + if (ecryptfs_allocated_caches & ECRYPTFS_FILE_INFO_CACHE) { + err = kmem_cache_destroy(ecryptfs_file_info_cache); + if (err) + ecryptfs_printk(0, KERN_WARNING, + "Not all ecryptfs_file_info_" + "cache regions were freed\n"); + rc |= err; + } + if (ecryptfs_allocated_caches & ECRYPTFS_DENTRY_INFO_CACHE) { + err = kmem_cache_destroy(ecryptfs_dentry_info_cache); + if (err) + ecryptfs_printk(0, KERN_WARNING, + "Not all ecryptfs_dentry_info_" + "cache regions were freed\n"); + rc |= err; + } + if (ecryptfs_allocated_caches & ECRYPTFS_INODE_INFO_CACHE) { + err = kmem_cache_destroy(ecryptfs_inode_info_cache); + if (err) + ecryptfs_printk(0, KERN_WARNING, + "Not all ecryptfs_inode_info_" + "cache regions were freed\n"); + rc |= err; + } + if (ecryptfs_allocated_caches & ECRYPTFS_SB_INFO_CACHE) { + err = kmem_cache_destroy(ecryptfs_sb_info_cache); + if (err) + ecryptfs_printk(0, KERN_WARNING, + "Not all ecryptfs_sb_info_" + "cache regions were freed\n"); + rc |= err; + } + if (ecryptfs_allocated_caches & ECRYPTFS_HEADER_CACHE_0) { + err = kmem_cache_destroy(ecryptfs_header_cache_0); + if (err) + ecryptfs_printk(0, KERN_WARNING, "Not all ecryptfs_" + "header_cache_0 regions were freed\n"); + rc |= err; + } + if (ecryptfs_allocated_caches & ECRYPTFS_HEADER_CACHE_1) { + err = kmem_cache_destroy(ecryptfs_header_cache_1); + if (err) + ecryptfs_printk(0, KERN_WARNING, "Not all ecryptfs_" + "header_cache_1 regions were freed\n"); + rc |= err; + } + if (ecryptfs_allocated_caches & ECRYPTFS_HEADER_CACHE_2) { + err = kmem_cache_destroy(ecryptfs_header_cache_2); + if (err) + ecryptfs_printk(0, KERN_WARNING, "Not all ecryptfs_" + "header_cache_2 regions were freed\n"); + rc |= err; + } + if (ecryptfs_allocated_caches & ECRYPTFS_LOWER_PAGE_CACHE) { + err = kmem_cache_destroy(ecryptfs_lower_page_cache); + if (err) + ecryptfs_printk(0, KERN_WARNING, "Not all ecryptfs_" + "lower_page_cache regions were " + "freed\n"); + rc |= err; + } + return rc; +} + +static int __init init_ecryptfs_fs(void) +{ + int rc; + + rc = ecryptfs_init_kmem_caches(); + if (rc) { + ecryptfs_printk(0, KERN_EMERG, "Failure occured while " + "attempting to create caches [CREATED: %x]." + "Now freeing caches.\n", + ecryptfs_allocated_caches); + ecryptfs_free_kmem_caches(); + return -ENOMEM; + } + ecryptfs_printk(1, KERN_NOTICE, "Registering eCryptfs\n"); + return register_filesystem(&ecryptfs_fs_type); +} + +static void __exit exit_ecryptfs_fs(void) +{ + int rc; + + ecryptfs_printk(1, KERN_NOTICE, "Unregistering eCryptfs\n"); + unregister_filesystem(&ecryptfs_fs_type); + rc = ecryptfs_free_kmem_caches(); + if (rc) + ecryptfs_printk(0, KERN_EMERG, "Failure occured while " + "attempting to free caches: [%d]\n", rc); +} + +MODULE_AUTHOR("Michael A. Halcrow "); +MODULE_DESCRIPTION("eCryptfs"); + +MODULE_LICENSE("GPL"); + +module_init(init_ecryptfs_fs) +module_exit(exit_ecryptfs_fs) diff -urN oldtree/fs/ecryptfs/mmap.c newtree/fs/ecryptfs/mmap.c --- oldtree/fs/ecryptfs/mmap.c 1970-01-01 00:00:00.000000000 +0000 +++ newtree/fs/ecryptfs/mmap.c 2006-02-18 17:24:58.726094832 +0000 @@ -0,0 +1,1153 @@ +/** + * eCryptfs: Linux filesystem encryption layer + * This is where eCryptfs handles the bulk encryption and decryption, + * with upper-lower file index interpolations. + * + * Copyright (c) 1997-2003 Erez Zadok + * Copyright (c) 2001-2003 Stony Brook University + * Copyright (c) 2004 International Business Machines Corp. + * Author(s): Michael A. Halcrow + * + * 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 +#include +#include +#include +#include +#include +#include +#include "ecryptfs_kernel.h" + +static int ecryptfs_commit_write(struct file *file, struct page *page, + unsigned from, unsigned to); +static int ecryptfs_prepare_write(struct file *file, struct page *page, + unsigned from, unsigned to); + +kmem_cache_t *ecryptfs_lower_page_cache; + +static inline pgoff_t +record_idx(struct ecryptfs_crypt_stats *crypt_stats, pgoff_t idx) +{ + return (idx % crypt_stats->records_per_page); +} + +static inline pgoff_t +last_records_page_idx(struct ecryptfs_crypt_stats *crypt_stats, pgoff_t idx) +{ + pgoff_t lower_data_idx; + pgoff_t lower_record_idx; + + lower_data_idx = ecryptfs_pg_idx_to_lwr_pg_idx(crypt_stats, idx); + lower_record_idx = record_idx(crypt_stats, idx); + return (lower_data_idx - lower_record_idx - 1); +} + +/** + * Get one page from cache or lower f/s, return error otherwise. + * + * @return 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; + int rc; + + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + dentry = file->f_dentry; + inode = dentry->d_inode; + mapping = inode->i_mapping; + if (index < 0) { + ecryptfs_printk(1, KERN_ERR, "BUG: index=%d\n", index); + page = ERR_PTR(-EIO); + goto out; + } + 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); + if (!PageUptodate(page)) { + lock_page(page); + rc = mapping->a_ops->readpage(file, page); + if (rc) { + page = ERR_PTR(rc); + goto out; + } + wait_on_page_locked(page); + if (!PageUptodate(page)) { + page = ERR_PTR(-EIO); + goto out; + } + } +out: + ecryptfs_printk(1, KERN_NOTICE, "Exit\n"); + return page; +} + +/** + * Write a specified number of zero's to a page. + * + * N.B. (start + num_zeros) _must_ be less than or equal to PAGE_CACHE_SIZE + * + * @param file The ecryptfs file + * @param index The index in which we are writing + * @param start The position after the last block of data + * @param num_zeros The number of zeros to write + */ +static +int write_zeros(struct file *file, pgoff_t index, int start, int num_zeros) +{ + int rc = 0; + struct page *tmp_page; + + ecryptfs_printk(1, KERN_NOTICE, "Enter; index = [%lu], start offset = " + "[%d] num_zeros = [%d]\n", index, start, num_zeros); + tmp_page = ecryptfs_get1page(file, index); + if (IS_ERR(tmp_page)) { + ecryptfs_printk(0, KERN_ERR, "Error getting page at index " + "[%lu]\n", index); + rc = PTR_ERR(tmp_page); + goto out; + } + kmap(tmp_page); + memset(((char *)page_address(tmp_page) + start), 0, num_zeros); + rc = ecryptfs_prepare_write(file, tmp_page, start, start + num_zeros); + if (rc) { + ecryptfs_printk(0, KERN_ERR, "Error preparing to write zero's " + "to remainder of page at index [%lu]\n", index); + kunmap(tmp_page); + page_cache_release(tmp_page); + goto out; + } + rc = ecryptfs_commit_write(file, tmp_page, start, start + num_zeros); + if (rc < 0) { + ecryptfs_printk(0, KERN_ERR, "Error attempting to write zero's " + "to remainder of page at index [%lu]\n", index); + kunmap(tmp_page); + page_cache_release(tmp_page); + goto out; + } + rc = 0; + kunmap(tmp_page); + page_cache_release(tmp_page); +out: + ecryptfs_printk(1, KERN_NOTICE, "Exit\n"); + return rc; +} + +/** + * Function for handling creation of holes when lseek-ing past the end + * of the file and then writing some data. + * + * N.B. + * This function does NOT support shrinking, only growing a file. + * The code _will_ BUG() if this requirement is not met. + * The new_length _myust_ be greater than the current length. + * + * @param file The ecryptfs file + * @param 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. + * N.B. new_length must be greater than current length + * @return 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); + + ecryptfs_printk(1, KERN_NOTICE, "Enter; inode->i_size = [%llu]; " + "new_length = [%llu]\n", cur_length, new_length); + /* Sanity check */ + if (cur_length >= new_length) { + ecryptfs_printk(0, KERN_ERR, "Called with new_length less than " + "or equal to the current length, this is " + "against function restrictions!\n"); + BUG(); + } + /* N.B. Notes on index calculations + * It is important to note that the size of the file will always be 1 + * greater than the last block of data's position added to the data's + * index shifted by the page cache size. + * size = index * PAGE_CACHE_SIZE + pos + 1 + * + * Example, a file with 1 block of data in the 0th index at the 0th + * position will be a file of size 1. Likewise, a file of 100 blocks + * of data will have it's last block in the 99th position. + * + * Therefore, a file of size page with PAGE_CACHE_SIZE blocks of data + * will have its last block of data in the (PAGE_CACHE_SIZE-1)th + * position, in the 0th index. A file with (PAGE_CACHE_SIZE + 1) blocks + * of data will have its last block of data in the 0th position of the + * 1st index. This calculation can be shown as: + * position = (size - 1) & ~PAGE_CACHE_MASK + * index = (size - 1) >> PAGE_CACHE_SHIFT + * + * Example: + * PAGE_CACHE_SHIFT = 12 + * PAGE_CACHE_SIZE = (1UL << PAGE_CACHE_SHIFT) [or 4096] + * PAGE_CACHE_MASK = (~(PAGE_CACHE_SIZE-1)) [or ~(4096-1)] + * + * A size of 4095 yields: + * position = (4095 - 1) & ~PAGE_CACHE_MASK => 4094 + * index = (4095 - 1) >> 12 => 0 + * + * A size of 4096 yields: + * position = (4096 - 1) & ~PAGE_CACHE_MASK => 4095 + * index = (4096 - 1) >> 12 => 0 + * + * A size of 4097 yields: + * position = (4097 - 1) & ~PAGE_CACHE_MASK => 0 + * index = (4097 - 1) >> 12 => 1 + * + * From these examples, you can see that the logic that a + * file's last block of data is at the (size - 1)th position + * follows. + * + * A 0-length file is by this logic, undefined if positions + * and indicies are required to be positive integers. However, + * this is intuative, as a 0-length file has no data which to + * give a position and index to. However, to make our + * calculations work, we will use the default value of -1 if + * the size is 0. The following will apply: + * old size = 0 + * old position = -1 + * old index = 0 + * new size = 1 + * new position = 0 + * new index = 0 + * + * write_zeros(file,0,(-1+1) => 0, ((size-1) - (-1)) => size) + * + * Therefore, if the current size of the file is 0, then + * setting our index and position to -1 will result in valid + * calculations for the new index and position for the new + * size. + */ + 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(1, KERN_NOTICE, "old_end_page_index = [%lu]; " + "old_end_pos_in_page = [%d]; " + "new_end_page_index = [%lu]; " + "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(0, KERN_ERR, "write_zeros(file=[%p], " + "index=[%lu], 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; + } + /* Else, new position is outside of the current index. + * Fill remainder of current page with zeros. + * For each page after that page, will entire page with zeros. + * Upon reaching new last page, write as many zeros as required + * to fullfil the new size. */ + /* 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(0, KERN_ERR, "write_zeros(file=[%p], " + "index=[%lu], 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(0, KERN_ERR, "write_zeros(file=[%p], " + "index=[%lu], 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(0, KERN_ERR, "write_zeros(file=" + "[%p], index=[%lu], 0, " + "new_end_pos_in_page=[%d]" + "returned [%d]\n", file, index, + new_end_pos_in_page, rc); + goto out; + } +out: + ecryptfs_printk((rc == 0 ? 1 : 0), KERN_NOTICE, "Exit; rc = [%d]\n", + rc); + return rc; +} + +/** + * @param lower_file Can be NULL + * @return Zero on success + */ +static int +ecryptfs_read_rotate_write_iv(char *iv, struct inode *inode, + int lower_iv_idx, struct file *lower_file, + struct page *page) +{ + int rc = 0; + pgoff_t records_page_index; + struct ecryptfs_crypt_stats *crypt_stats; + struct page *records_page; + char *records_virt; + int lower_file_needs_fput = 0; + struct address_space_operations *lower_a_ops; + struct inode *lower_inode; + + crypt_stats = &(ECRYPTFS_INODE_TO_PRIVATE(inode)->crypt_stats); + lower_inode = ECRYPTFS_INODE_TO_LOWER(inode); + lower_a_ops = lower_inode->i_mapping->a_ops; + records_page_index = last_records_page_idx(crypt_stats, page->index); + ecryptfs_printk(1, KERN_NOTICE, "records_page_index = [%lu]\n", + records_page_index); + records_page = grab_cache_page(lower_inode->i_mapping, + records_page_index); + if (!records_page) { + ecryptfs_printk(0, KERN_ERR, "records_page == NULL " + "after grab_cache_page at index [%lu]" + "\n", records_page_index); + rc = -EIO; + goto out; + } + /* TODO: Assume encrypted only for version 0.1 */ + ecryptfs_printk(1, KERN_NOTICE, "lower_iv_idx = [%d]\n", lower_iv_idx); + records_virt = kmap(records_page); + if (!records_virt) { + rc = -ENOMEM; + ecryptfs_printk(1, KERN_NOTICE, "Error in kmap\n"); + goto out_unlock_and_release; + } + if (!lower_file) { + struct dentry *lower_dentry; + struct vfsmount *lower_mnt; + + if (!lower_inode->i_dentry.next) { + rc = -EINVAL; + ecryptfs_printk(1, KERN_NOTICE, "No dentry for " + "lower_inode\n"); + goto out_unmap; + } + lower_dentry = list_entry(lower_inode->i_dentry.next, + struct dentry, d_alias); + mntget(ECRYPTFS_SUPERBLOCK_TO_PRIVATE(inode->i_sb)->lower_mnt); + lower_mnt = + ECRYPTFS_SUPERBLOCK_TO_PRIVATE(inode->i_sb)->lower_mnt; + lower_file = dentry_open(lower_dentry, lower_mnt, FMODE_READ); + if (IS_ERR(lower_file)) { + rc = PTR_ERR(lower_file); + ecryptfs_printk(0, KERN_ERR, + "Error opening dentry; rc = [%i]\n", + rc); + mntput(ECRYPTFS_SUPERBLOCK_TO_PRIVATE(inode->i_sb)->lower_mnt); + goto out_unmap; + } + lower_file_needs_fput = 1; + } + rc = lower_a_ops->prepare_write(lower_file, records_page, lower_iv_idx, + (lower_iv_idx + crypt_stats->iv_bytes)); + if (rc) { + ecryptfs_printk(0, KERN_ERR, "Error in lower prepare_write() " + "call: rc = [%d]\n", rc); + goto out_unmap; + } + down(&crypt_stats->iv_sem); + ecryptfs_rotate_iv(crypt_stats->iv); + memcpy(iv, crypt_stats->iv, crypt_stats->iv_bytes); + up(&crypt_stats->iv_sem); + memcpy(records_virt + lower_iv_idx, crypt_stats->iv, + crypt_stats->iv_bytes); + rc = lower_a_ops->commit_write(lower_file, records_page, lower_iv_idx, + (lower_iv_idx + crypt_stats->iv_bytes)); + if (rc) { + ecryptfs_printk(0, KERN_ERR, "Error in lower commit_write() " + "call: rc = [%d]\n", rc); + goto out_unmap; + } + lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME; + ecryptfs_printk(1, KERN_NOTICE, "Unlocking page with index = " + "[%lu]\n", records_page->index); +out_unmap: + kunmap(records_page); +out_unlock_and_release: + unlock_page(records_page); + page_cache_release(records_page); + ecryptfs_printk(1, KERN_NOTICE, "After committing IV write, " + "lower_inode->i_blocks = [%lu]\n", + lower_inode->i_blocks); +out: + if (lower_file_needs_fput) + fput(lower_file); + return rc; +} + +/** + * @return Zero on success; negative on error + */ +static int encrypt_page(struct ecryptfs_crypt_stats *crypt_stats, + struct page *page, struct page *lower_page, char *iv) +{ + int rc = 0; + + ecryptfs_printk(1, KERN_NOTICE, "Calling do_encrypt_page()\n"); + ecryptfs_printk(1, KERN_NOTICE, "Encrypting page with iv:\n"); + if (unlikely(ecryptfs_verbosity > 0)) + ecryptfs_dump_hex(iv, crypt_stats->iv_bytes); + ecryptfs_printk(1, KERN_NOTICE, "First 8 bytes before " + "encryption:\n"); + if (ecryptfs_verbosity > 0) + ecryptfs_dump_hex((char *)page_address(page), 8); + rc = do_encrypt_page_offset(crypt_stats, lower_page, 0, + page, 0, crypt_stats->extent_size, iv); + ecryptfs_printk(1, KERN_NOTICE, "Encrypted [%d] bytes\n", rc); + ecryptfs_printk(1, KERN_NOTICE, "First 8 bytes after " "encryption:\n"); + if (ecryptfs_verbosity > 0) + ecryptfs_dump_hex((char *)page_address(lower_page), 8); + if (rc > 0) + rc = 0; + return rc; +} + +/** + * @param page Page that is locked before this call is made + */ +static int ecryptfs_writepage(struct page *page, struct writeback_control *wbc) +{ + int rc = -EIO; + int err = 0; + unsigned long lower_index; + struct inode *inode; + struct inode *lower_inode; + struct page *lower_page; + char *kaddr, *lower_kaddr; + struct ecryptfs_crypt_stats *crypt_stats; + + ecryptfs_printk(1, KERN_NOTICE, "Enter; page->index = [%ld]; " + "page->mapping->host = [%p]\n", page->index, + page->mapping->host); + inode = page->mapping->host; + crypt_stats = &(ECRYPTFS_INODE_TO_PRIVATE(inode)->crypt_stats); + lower_inode = ECRYPTFS_INODE_TO_LOWER(inode); + lower_index = ecryptfs_pg_idx_to_lwr_pg_idx(crypt_stats, page->index); + ecryptfs_printk(1, KERN_NOTICE, "Grab lower_idx = [%ld]\n", + lower_index); + lower_page = grab_cache_page(lower_inode->i_mapping, lower_index); + if (!lower_page) { + rc = -ENOMEM; + goto out; + } + kaddr = (char *)kmap(page); + if (!kaddr) { + rc = -ENOMEM; + goto out; + } + lower_kaddr = (char *)kmap(lower_page); + if (!lower_kaddr) { + rc = -ENOMEM; + goto out; + } + if (crypt_stats->encrypted) { + char iv[ECRYPTFS_MAX_IV_BYTES]; + int record_byte_offset; + /* TODO: HMAC: Include HMAC bytes in the record size */ + record_byte_offset = (record_idx(crypt_stats, page->index) + * crypt_stats->iv_bytes); + rc = ecryptfs_read_rotate_write_iv(iv, inode, + record_byte_offset, NULL, + page); + if (rc) { + ecryptfs_printk(0, KERN_ERR, "Error rotating " + "IV; write failure. Assuming " + "IV page corruption; writing " + "0's to associated data extent" + ".\n"); + memset(lower_kaddr, 0, crypt_stats->extent_size); + err = -EIO; + goto do_lower_write; + } + ecryptfs_printk(1, KERN_NOTICE, "Encrypting page with iv:\n"); + if (unlikely(ecryptfs_verbosity > 0)) + ecryptfs_dump_hex(iv, crypt_stats->iv_bytes); + err = encrypt_page(crypt_stats, page, lower_page, iv); + if (err) + ecryptfs_printk(0, KERN_WARNING, "Error encrypting " + "page (upper index [%llu])\n", + page->index); + } else + memcpy(lower_kaddr, kaddr, crypt_stats->extent_size); +do_lower_write: + kunmap(page); + kunmap(lower_page); + rc = lower_inode->i_mapping->a_ops->writepage(lower_page, wbc); + if (rc) + ecryptfs_printk(0, KERN_ERR, "Error calling lower writepage(); " + "rc = [%d]\n", rc); + lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME; + page_cache_release(lower_page); + if (rc) + ClearPageUptodate(page); + else + SetPageUptodate(page); + unlock_page(page); +out: + if (err) + rc = err; + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc); + 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 + */ +static int ecryptfs_do_readpage(struct file *file, struct page *page, + pgoff_t lower_page_index) +{ + int rc = -EIO; + 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; + struct address_space_operations *lower_a_ops; + + ecryptfs_printk(1, KERN_NOTICE, "Enter; lower_page_index = [%lu]\n", + lower_page_index); + dentry = file->f_dentry; + if (NULL == ECRYPTFS_FILE_TO_PRIVATE_SM(file)) { + rc = -ENOENT; + ecryptfs_printk(0, KERN_ERR, "No lower file info\n"); + goto out; + } + 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(0, KERN_ERR, "Error reading from page cache\n"); + goto out; + } + wait_on_page_locked(lower_page); + if (!PageUptodate(lower_page)) { + lock_page(lower_page); + rc = lower_a_ops->readpage(lower_file, lower_page); + if (rc) { + lower_page = NULL; + rc = -EIO; + ecryptfs_printk(0, KERN_ERR, "Error reading lower " + "page at index=[%lu]\n", + lower_page_index); + goto out; + } + wait_on_page_locked(lower_page); + if (!PageUptodate(lower_page)) { + rc = -EIO; + ecryptfs_printk(0, KERN_ERR, "Error reading lower " + "page at index=[%lu]\n", + lower_page_index); + goto out; + } + } + page_data = (char *)kmap(page); + if (!page_data) { + rc = -ENOMEM; + ecryptfs_printk(0, KERN_ERR, "Error mapping page\n"); + goto out; + } + lower_page_data = (char *)kmap(lower_page); + if (!lower_page_data) { + rc = -ENOMEM; + ecryptfs_printk(0, KERN_ERR, "Error mapping page\n"); + kunmap(page); + goto out; + } + /* TODO: Copy sub-page amount of data? */ + 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); + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc); + return rc; +} + +/* Action descriptor for decrypt_page() */ +#define ECRYPTFS_ACTION_COPY 0 +#define ECRYPTFS_ACTION_DECRYPT 1 + +/** + * Decrypt a page of data + * + * @return Zero on success + */ +static int decrypt_page(struct ecryptfs_crypt_stats *crypt_stats, + struct file *file, char *iv, struct page *page, + pgoff_t lower_page_index, int decrypt) +{ + int rc = 0; + char *lower_page_encrypted_virt; + struct page *lower_page_encrypted; + int decrypt_to; + + ecryptfs_printk(1, KERN_NOTICE, "Enter; lower_page_index = [%d]\n", + lower_page_index); + lower_page_encrypted_virt = + kmem_cache_alloc(ecryptfs_lower_page_cache, SLAB_KERNEL); + if (!lower_page_encrypted_virt) { + rc = -ENOMEM; + ecryptfs_printk(0, KERN_ERR, "Error getting page for " + "encrypted lower page\n"); + ClearPageUptodate(page); + goto out; + } + lower_page_encrypted = virt_to_page(lower_page_encrypted_virt); + rc = ecryptfs_do_readpage(file, lower_page_encrypted, lower_page_index); + if (rc) { + ecryptfs_printk(0, KERN_ERR, "Error reading lower encrypted " + "page\n"); + ClearPageUptodate(page); + goto out; + } + ecryptfs_printk(1, KERN_NOTICE, "Decrypting page with iv:\n"); + if (ecryptfs_verbosity > 0) + ecryptfs_dump_hex(iv, crypt_stats->iv_bytes); + ecryptfs_printk(1, KERN_NOTICE, "Using session key encryption key:\n"); + if (ecryptfs_verbosity > 0) + ecryptfs_dump_hex(crypt_stats->key, + crypt_stats->key_size_bits / 8); + decrypt_to = crypt_stats->extent_size; + ecryptfs_printk(1, KERN_NOTICE, "Decrypting to: [%d]\n", decrypt_to); + ecryptfs_printk(1, KERN_NOTICE, "First 8 bytes before decryption:\n"); + if (ecryptfs_verbosity > 0) + ecryptfs_dump_hex((char *)page_address(lower_page_encrypted), + 8); + if (decrypt) + do_decrypt_page_offset(crypt_stats, page, + 0, lower_page_encrypted, + 0, decrypt_to, iv); + else + memcpy(page, lower_page_encrypted_virt, decrypt_to); + ecryptfs_printk(1, KERN_NOTICE, "First 8 bytes after decryption:\n"); + if (ecryptfs_verbosity > 0) + ecryptfs_dump_hex((char *)page_address(page), 8); + kmem_cache_free(ecryptfs_lower_page_cache, lower_page_encrypted_virt); +out: + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc); + return rc; +} + +/** + * Read in a page + * + * @param file This is an ecryptfs file + * @param page ecryptfs associated page to stick the read data into + * @return Zero on success; non-zero on error + */ +static int ecryptfs_readpage(struct file *file, struct page *page) +{ + int rc = 0; + struct ecryptfs_crypt_stats *crypt_stats = NULL; + char iv[ECRYPTFS_MAX_IV_BYTES]; + int iv_is_nonzero = 0; + char *records_virt; + struct page *records_page; + int record_byte_offset; + int iv_byte_offset; + int i; + pgoff_t records_page_index; + pgoff_t lower_page_index; + + ecryptfs_printk(1, KERN_NOTICE, "Enter; page->index = [%ld]\n", + page->index); + ASSERT(file && file->f_dentry && file->f_dentry->d_inode); + crypt_stats = + &ECRYPTFS_INODE_TO_PRIVATE(file->f_dentry->d_inode)->crypt_stats; + /* If the file is neither encrypted nor HMAC-verified, then we + * have passthrough mode. */ + if (!crypt_stats || !crypt_stats->encrypted) { + ecryptfs_printk(1, KERN_NOTICE, + "Passing through unencrypted page\n"); + rc = ecryptfs_do_readpage(file, page, page->index); + goto out; + } + /* The file is encrypted, hmac verified, or both. */ + ecryptfs_printk(1, KERN_NOTICE, + "crypt_stats->iv_bytes = [%d]\n", + crypt_stats->iv_bytes); + ecryptfs_printk(1, KERN_NOTICE, + "crypt_stats->records_per_page = [%d]\n", + crypt_stats->records_per_page); + + /* Get the relevant IV/HMAC page */ + records_page_index = last_records_page_idx(crypt_stats, page->index); + ecryptfs_printk(1, KERN_NOTICE, "records_page_index = [%lu]\n", + records_page_index); + records_virt = (char *)__get_free_page(GFP_KERNEL); + if (!records_virt) { + ecryptfs_printk(0, KERN_ERR, "Error getting free page"); + rc = -ENOMEM; + ClearPageUptodate(page); + goto out; + } + records_page = virt_to_page(records_virt); + rc = ecryptfs_do_readpage(file, records_page, records_page_index); + if (rc) { + ecryptfs_printk(0, KERN_ERR, "Error reading IV/HMAC page"); + ClearPageUptodate(page); + goto out; + } + /* TODO: HMAC: Include HMAC bytes in the record size */ + record_byte_offset = (record_idx(crypt_stats, page->index) + * crypt_stats->iv_bytes); + iv_byte_offset = -1; + if (crypt_stats->encrypted) { + iv_byte_offset = record_byte_offset; + iv_is_nonzero = 0; + for (i = 0; i < crypt_stats->iv_bytes; i++) + iv_is_nonzero |= (records_virt + iv_byte_offset)[i]; + memcpy(iv, (records_virt + iv_byte_offset), + crypt_stats->iv_bytes); + } + ecryptfs_printk(1, KERN_NOTICE, "record_byte_offset = [%d]\n", + record_byte_offset); + ecryptfs_printk(1, KERN_NOTICE, "iv_byte_offset = [%d]\n", + iv_byte_offset); + free_page((unsigned long)records_virt); + lower_page_index = ecryptfs_pg_idx_to_lwr_pg_idx(crypt_stats, + page->index); + ecryptfs_printk(1, KERN_NOTICE, "lower_page_index = [%lu]\n", + lower_page_index); + ecryptfs_printk(1, KERN_NOTICE, "iv_is_nonzero = [%d]\n", + iv_is_nonzero); + if (crypt_stats->encrypted && iv_is_nonzero) { + rc = decrypt_page(crypt_stats, file, iv, page, + lower_page_index, ECRYPTFS_ACTION_DECRYPT); + if (rc) { + ecryptfs_printk(0, KERN_ERR, "Error decrypting " + "page; rc = [%d]\n", rc); + goto out; + } + } else { + ecryptfs_printk(1, KERN_NOTICE, + "Passing through unencrypted page\n"); + rc = ecryptfs_do_readpage(file, page, lower_page_index); + } + SetPageUptodate(page); +out: + ecryptfs_printk(1, KERN_NOTICE, "Unlocking page with index = [%lu]\n", + page->index); + unlock_page(page); + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc); + return rc; +} + +static void fill_zeros_to_end_of_page(struct page *page, unsigned to) +{ + struct inode *inode = page->mapping->host; + int end_byte_in_page; + 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) { + goto out; + } + memset((page_virt + end_byte_in_page), 0, + (PAGE_CACHE_SIZE - end_byte_in_page)); + kunmap(page); + } +out: + return; +} + +static int ecryptfs_prepare_write(struct file *file, struct page *page, + unsigned from, unsigned to) +{ + int err = 0; + + ecryptfs_printk(1, KERN_NOTICE, "Enter; page->index = [%lu]; from = " + "[%d]; to = [%d]\n", page->index, from, to); + 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)) + err = ecryptfs_do_readpage(file, page, page->index); +out: + return err; +} + +/** + * @return Zero on success + */ +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; + struct address_space_operations *lower_a_ops; + unsigned long long file_size; + + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + header_page = grab_cache_page(lower_inode->i_mapping, 0); + if (!header_page) { + rc = -EINVAL; + ecryptfs_printk(0, KERN_ERR, "grab_cache_page for header page " + "failed\n"); + goto out; + } + header_virt = kmap(header_page); + lower_a_ops = lower_inode->i_mapping->a_ops; + rc = lower_a_ops->prepare_write(lower_file, header_page, 0, 8); + file_size = (unsigned long long)i_size_read(inode); + memcpy(header_virt, &file_size, 8); + rc = lower_a_ops->commit_write(lower_file, header_page, 0, 8); + if (rc < 0) + ecryptfs_printk(0, KERN_ERR, "Error commiting header page " + "write\n"); + kunmap(header_page); + ecryptfs_printk(1, KERN_NOTICE, "Unlocking page with index = [%lu]\n", + header_page->index); + unlock_page(header_page); + page_cache_release(header_page); + lower_inode->i_mtime = lower_inode->i_ctime = CURRENT_TIME; + mark_inode_dirty_sync(inode); +out: + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc); + return rc; +} + +/** + * 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. + * + * @param file The eCryptfs file object + * @param page The eCryptfs page + * @param from Ignored (we rotate the page IV on each write) + * @param to Ignored + * @return + */ +static int ecryptfs_commit_write(struct file *file, struct page *page, + unsigned from, unsigned to) +{ + int rc = -ENOMEM; + struct inode *inode; + struct inode *lower_inode; + struct page *lower_page = NULL; + struct file *lower_file = NULL; + loff_t pos; + unsigned bytes = to - from; + struct ecryptfs_crypt_stats *crypt_stats; + pgoff_t lower_page_index; + struct address_space_operations *lower_a_ops; + struct dentry *ecryptfs_dentry; + + ecryptfs_printk(1, KERN_NOTICE, + "Enter; page->index = [%lu]; from = [%d]; to = [%d]\n", + page->index, from, to); + ecryptfs_dentry = file->f_dentry; + inode = page->mapping->host; + lower_inode = ECRYPTFS_INODE_TO_LOWER(inode); + down(&lower_inode->i_sem); + if (!(lower_inode->i_mapping + && lower_inode->i_mapping->a_ops + && lower_inode->i_mapping->a_ops->prepare_write + && lower_inode->i_mapping->a_ops->commit_write)) { + ecryptfs_printk(0, KERN_ERR, + "a_ops of lower inode not valid\n"); + rc = -EINVAL; + goto out; + } + lower_a_ops = lower_inode->i_mapping->a_ops; + ASSERT(file && file->f_dentry && file->f_dentry->d_inode); + crypt_stats = + &ECRYPTFS_INODE_TO_PRIVATE(file->f_dentry->d_inode)->crypt_stats; + if (NULL != ECRYPTFS_FILE_TO_PRIVATE(file)) + lower_file = ECRYPTFS_FILE_TO_LOWER(file); + if (!crypt_stats) { + rc = -EINVAL; + goto out; + } + + rc = ecryptfs_init_crypt_ctx(crypt_stats); + if (rc) + ecryptfs_printk(1, KERN_NOTICE, "Problem with " + "initializing crypto context\n"); + if (crypt_stats->new_file) { + struct page *header_page; + char *header_virt; + + crypt_stats->new_file = 0; + header_page = grab_cache_page(lower_inode->i_mapping, 0); + if (!header_page) { + rc = -EINVAL; + ecryptfs_printk(0, KERN_ERR, "grab_cache_page for " + "header page failed\n"); + goto out; + } + header_virt = kmap(header_page); + rc = lower_a_ops->prepare_write(lower_file, header_page, + 0, crypt_stats->extent_size); + rc = ecryptfs_write_headers_virt(header_virt, crypt_stats, + ecryptfs_dentry, + ECRYPTFS_FILE_VERSION); + if (rc) { + ecryptfs_printk(0, KERN_WARNING, "Error generating " + "header; rc = [%d]\n", rc); + rc = -EIO; + goto out; + } + memset(header_virt, 0, 8); + rc = lower_a_ops->commit_write(lower_file, header_page, + 0, crypt_stats->extent_size); + if (rc < 0) + ecryptfs_printk(0, KERN_ERR, "Error commiting header " + "page write\n"); + kunmap(header_page); + ecryptfs_printk(1, KERN_NOTICE, + "Unlocking header page with index = " "[%lu]\n", + header_page->index); + unlock_page(header_page); + page_cache_release(header_page); + if (rc < 0) + goto out; + rc = 0; + ecryptfs_printk(1, KERN_NOTICE, "lower_inode->i_blocks = " + "[%lu]\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); + } else + ecryptfs_printk(1, KERN_NOTICE, "Not a new file\n"); + /* Translate the page index */ + lower_page_index = ecryptfs_pg_idx_to_lwr_pg_idx(crypt_stats, + page->index); + ecryptfs_printk(1, KERN_NOTICE, "lower_page_index = [%lu]\n", + lower_page_index); + lower_page = grab_cache_page(lower_inode->i_mapping, lower_page_index); + if (!lower_page) { + ecryptfs_printk(0, KERN_ERR, "grab_cache_page for " + "lower_page_index=[%lu] failed\n", + lower_page_index); + goto out; + } + kmap(lower_page); + rc = lower_a_ops->prepare_write(lower_file, lower_page, + 0, crypt_stats->extent_size); + if (rc) + goto out_unlock_lower; + fill_zeros_to_end_of_page(page, to); + if (!crypt_stats->encrypted) { + /* TODO: aops */ + memcpy((char *)page_address(lower_page), + (char *)page_address(page), crypt_stats->extent_size); + } else { + /* The file is either encrypted or HMAC'd */ + char iv[ECRYPTFS_MAX_IV_BYTES]; + int record_byte_offset; + ecryptfs_printk(1, KERN_NOTICE, + "crypt_stats->iv_bytes = [%d]\n", + crypt_stats->iv_bytes); + /* TODO: HMAC: Include HMAC bytes in the record size */ + record_byte_offset = (record_idx(crypt_stats, page->index) + * crypt_stats->iv_bytes); + rc = ecryptfs_read_rotate_write_iv(iv, inode, + record_byte_offset, + lower_file, page); + if (rc) { + ecryptfs_printk(0, KERN_ERR, "Error rotating IV\n"); + goto out_unlock_lower; + } + ecryptfs_printk(1, KERN_NOTICE, "Encrypting page with iv:\n"); + if (unlikely(ecryptfs_verbosity > 0)) + ecryptfs_dump_hex(iv, crypt_stats->iv_bytes); + rc = encrypt_page(crypt_stats, page, lower_page, iv); + if (rc) { + ecryptfs_printk(0, KERN_WARNING, "Error encrypting " + "page (upper index [%llu])\n", + page->index); + goto out; + } + } + rc = lower_a_ops->commit_write(lower_file, lower_page, 0, + crypt_stats->extent_size); + if (rc < 0) { + ecryptfs_printk(0, KERN_ERR, + "Error committing write; rc = [%d]\n", rc); + goto out_unlock_lower; + } + rc = bytes; + 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(1, KERN_NOTICE, "Expanded file size to " + "[%lld]\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_unlock_lower: + kunmap(lower_page); + ecryptfs_printk(1, KERN_NOTICE, + "Unlocking lower page with index = [%lu]\n", + lower_page->index); + unlock_page(lower_page); + page_cache_release(lower_page); + kunmap(page); /* mapped in prepare_write */ +out: + if (rc < 0) + ClearPageUptodate(page); + else + SetPageUptodate(page); + up(&lower_inode->i_sem); + ecryptfs_printk(1, KERN_NOTICE, "Exit; rc = [%d]\n", rc); + return rc; +} + +static sector_t ecryptfs_bmap(struct address_space *mapping, sector_t block) +{ + int rc = 0; + struct inode *inode; + struct inode *lower_inode; + + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + 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; +} + +/** + * This function is copied verbatim from mm/filemap.c. It should be + * simply moved to some header file instead. + */ +static int sync_page(struct page *page) +{ + struct address_space *mapping = page->mapping; + + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + if (mapping && mapping->a_ops && mapping->a_ops->sync_page) + return mapping->a_ops->sync_page(page); + return 0; +} + +static int ecryptfs_sync_page(struct page *page) +{ + int rc = 0; + struct inode *inode; + struct inode *lower_inode; + struct page *lower_page; + + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + inode = page->mapping->host; + lower_inode = ECRYPTFS_INODE_TO_LOWER(inode); + lower_page = grab_cache_page(lower_inode->i_mapping, page->index); + if (!lower_page) { + rc = -ENOMEM; + ecryptfs_printk(0, KERN_ERR, "Error from grab_cache_page " + "(no mem?)\n"); + goto out; + } + rc = sync_page(lower_page); + ecryptfs_printk(1, KERN_NOTICE, "Unlocking page with index = [%lu]\n", + lower_page->index); + unlock_page(lower_page); + page_cache_release(lower_page); +out: + return rc; +} + +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, +}; diff -urN oldtree/fs/ecryptfs/super.c newtree/fs/ecryptfs/super.c --- oldtree/fs/ecryptfs/super.c 1970-01-01 00:00:00.000000000 +0000 +++ newtree/fs/ecryptfs/super.c 2006-02-18 17:24:58.727094680 +0000 @@ -0,0 +1,250 @@ +/** + * eCryptfs: Linux filesystem encryption layer + * + * Copyright (c) 1997-2003 Erez Zadok + * Copyright (c) 2001-2003 Stony Brook University + * Copyright (c) 2004 International Business Machines Corp. + * Author(s): Michael A. Halcrow + * Michael C. Thompson + * + * 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 +#include +#include +#include "ecryptfs_kernel.h" + +kmem_cache_t *ecryptfs_inode_info_cache; + +/** + * Called to bring an inode into existence. + * + * Note that setting the self referencing pointer doesn't work here: + * i.e. ECRYPTFS_INODE_TO_PRIVATE_SM(inode)=ei; + * + * 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. + * + * @param sb Pointer to the super block of the filesystem + * @return Pointer to a newly allocated inode, NULL otherwise + */ +static struct inode *ecryptfs_alloc_inode(struct super_block *sb) { + struct ecryptfs_inode_info *ecryptfs_inode = NULL; + struct inode *inode = NULL; + + ecryptfs_printk(1, KERN_NOTICE, "Enter; sb = [%p]\n", sb); + ecryptfs_inode = kmem_cache_alloc(ecryptfs_inode_info_cache, + SLAB_KERNEL); + if (unlikely(!ecryptfs_inode)) { + ecryptfs_printk(1, KERN_WARNING, + "Failed to allocate new inode\n"); + goto out; + } + ecryptfs_init_crypt_stats(&(ecryptfs_inode->crypt_stats)); + inode = &(ecryptfs_inode->vfs_inode); + out: + ecryptfs_printk(1, KERN_NOTICE, "Exit; inode = [%p]\n", inode); + return 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_stats 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_crypt_stats *crypt_stats; + + ecryptfs_printk(1, KERN_NOTICE, "Enter; inode = [%p]\n", inode); + crypt_stats = &(ECRYPTFS_INODE_TO_PRIVATE(inode))->crypt_stats; + ecryptfs_destruct_crypt_stats(crypt_stats); + kmem_cache_free(ecryptfs_inode_info_cache, + ECRYPTFS_INODE_TO_PRIVATE(inode)); + ecryptfs_printk(1, KERN_NOTICE, "Exit\n"); +} + +/** + * Return a pointer to our ecryptfs_inode_info struct. + * This is specified by the kernel documentation for the alloc_inode & + * destroy_inode change. We use this function to get a handle to our + * ecryptfs specific data. + * + * @param inode The inode component of the ecryptfs_inode_info we want to find + * @return Handle to our ecryptfs_inode_info + */ +static inline struct ecryptfs_inode_info *ECRYPTFS_I(struct inode *inode) +{ + return list_entry(inode, struct ecryptfs_inode_info, vfs_inode); +} + +/** + * Set up the ecryptfs inode. + */ +static void ecryptfs_read_inode(struct inode *inode) +{ + ecryptfs_printk(1, KERN_NOTICE, "Enter; inode = [%p]\n", inode); + /* This is where we setup the self-reference in the vfs_inode's + * u.generic_ip. That way we don't have to walk the list again. */ + ECRYPTFS_INODE_TO_PRIVATE_SM(inode) = ECRYPTFS_I(inode); + ECRYPTFS_INODE_TO_LOWER(inode) = NULL; + inode->i_version++; + inode->i_op = &ecryptfs_main_iops; + inode->i_fop = &ecryptfs_main_fops; + inode->i_mapping->a_ops = &ecryptfs_aops; + ecryptfs_printk(1, KERN_NOTICE, "Exit\n"); +} + + +/** + * This is called through iput_final(). + * This is function will replace generic_drop_inode. The end result of which + * is we are skipping the check in inode->i_nlink, which we do not use. + */ +static void ecryptfs_drop_inode(struct inode *inode) { + generic_delete_inode(inode); +} + +/** + * 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_printk(1, KERN_NOTICE, "Enter\n"); + mntput(sb_info->lower_mnt); + key_put(sb_info->mount_crypt_stats.global_auth_tok_key); + kmem_cache_free(ecryptfs_sb_info_cache, sb_info); + ECRYPTFS_SUPERBLOCK_TO_PRIVATE_SM(sb) = NULL; + ecryptfs_printk(1, KERN_NOTICE, "Exit\n"); +} + +/** + * Get the filesystem statistics. Currently, we let this pass right through + * to the lower filesystem and take no action ourselves + * + * TODO: Any stats need to be transposed? + */ +static int ecryptfs_statfs(struct super_block *sb, struct kstatfs *buf) +{ + int err = 0; + + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + err = vfs_statfs(ECRYPTFS_SUPERBLOCK_TO_LOWER(sb), buf); + ecryptfs_printk(1, KERN_NOTICE, "Exit; err = [%d]\n",err); + return err; +} + +/** + * TODO: Not implemented. + * Called to ask filesystem to change mount options. + */ +static int ecryptfs_remount_fs(struct super_block *sb, int *flags, char *data) +{ + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + ecryptfs_printk(1, KERN_NOTICE, "Exit\n"); + return -ENOSYS; +} + +/** + * 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. + * + * TODO: Why do we just not drop the reference to the lower inode in + * ecryptfs_destroy_inode? + */ +static void ecryptfs_clear_inode(struct inode *inode) +{ + ecryptfs_printk(1, KERN_NOTICE, "Enter; inode = [%p]; i_ino = [%lu]\n", + inode, inode->i_ino); + iput(ECRYPTFS_INODE_TO_LOWER(inode)); + ecryptfs_printk(1, KERN_NOTICE, "Exit\n"); +} + +/** + * Called in do_umount() if the MNT_FORCE flag was used and this + * function is defined. See comment in linux/fs/super.c:do_umount(). + * Used only in nfs, to kill any pending RPC tasks, so that subsequent + * code can actually succeed and won't leave tasks that need handling. + * + * PS. I wonder if this is somehow useful to undo damage that was + * left in the kernel after a user level file server (such as amd) + * dies. + */ +static void ecryptfs_umount_begin(struct super_block *sb) +{ + struct super_block *lower_sb; + + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + /* TODO: Explain why we are umounting the lower superblock */ + lower_sb = ECRYPTFS_SUPERBLOCK_TO_LOWER(sb); + if (lower_sb->s_op->umount_begin) + lower_sb->s_op->umount_begin(lower_sb); + ecryptfs_printk(1, KERN_NOTICE, "Exit\n"); +} + +/* TODO: Where is this normally declared? */ +int seq_printf(struct seq_file *m, const char *f, ...); + +/** + * Prints the directory we are currently mounted over + * + * @return 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; + int ret = 0; + char *tmp = NULL; + char *path; + + ecryptfs_printk(1, KERN_NOTICE, "Enter\n"); + tmp = (char *)__get_free_page(GFP_KERNEL); + if (!tmp) { + ret = -ENOMEM; + goto out; + } + /* TODO: wrap? */ + path = d_path(ECRYPTFS_DENTRY_TO_LOWER(sb->s_root), + ECRYPTFS_SUPERBLOCK_TO_PRIVATE(sb)->lower_mnt, tmp, + PAGE_SIZE); + seq_printf(m, ",dir=%s", path); + free_page((unsigned long)tmp); + out: + ecryptfs_printk(1, KERN_NOTICE, "Exit; ret = [%d]\n",ret); + return ret; +} + +/* TODO: Provide operations? (quota stuff, dirty, sync) */ +struct super_operations ecryptfs_sops = { + .alloc_inode = ecryptfs_alloc_inode, + .destroy_inode = ecryptfs_destroy_inode, + .read_inode = ecryptfs_read_inode, + .drop_inode = ecryptfs_drop_inode, + .put_super = ecryptfs_put_super, + .statfs = ecryptfs_statfs, + .remount_fs = ecryptfs_remount_fs, + .clear_inode = ecryptfs_clear_inode, + .umount_begin = ecryptfs_umount_begin, + .show_options = ecryptfs_show_options +};