diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/Documentation/filesystems/00-INDEX kernel-source-diablo-2.6.21/kernel-source/Documentation/filesystems/00-INDEX --- kernel-source-diablo-2.6.21.orig/kernel-source/Documentation/filesystems/00-INDEX 2007-10-02 08:09:30.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/Documentation/filesystems/00-INDEX 2009-07-01 17:29:10.000000000 +0100 @@ -84,6 +84,8 @@ udf.txt - info and mount options for the UDF filesystem. ufs.txt - info on the ufs filesystem. +unionfs/ + - info on the unionfs filesystem vfat.txt - info on using the VFAT filesystem used in Windows NT and Windows 95 vfs.txt diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/Documentation/filesystems/unionfs/00-INDEX kernel-source-diablo-2.6.21/kernel-source/Documentation/filesystems/unionfs/00-INDEX --- kernel-source-diablo-2.6.21.orig/kernel-source/Documentation/filesystems/unionfs/00-INDEX 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/Documentation/filesystems/unionfs/00-INDEX 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,10 @@ +00-INDEX + - this file. +concepts.txt + - A brief introduction of concepts. +issues.txt + - A summary of known issues with unionfs. +rename.txt + - Information regarding rename operations. +usage.txt + - Usage information and examples. diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/Documentation/filesystems/unionfs/concepts.txt kernel-source-diablo-2.6.21/kernel-source/Documentation/filesystems/unionfs/concepts.txt --- kernel-source-diablo-2.6.21.orig/kernel-source/Documentation/filesystems/unionfs/concepts.txt 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/Documentation/filesystems/unionfs/concepts.txt 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,287 @@ +Unionfs 2.x CONCEPTS: +===================== + +This file describes the concepts needed by a namespace unification file +system. + + +Branch Priority: +================ + +Each branch is assigned a unique priority - starting from 0 (highest +priority). No two branches can have the same priority. + + +Branch Mode: +============ + +Each branch is assigned a mode - read-write or read-only. This allows +directories on media mounted read-write to be used in a read-only manner. + + +Whiteouts: +========== + +A whiteout removes a file name from the namespace. Whiteouts are needed when +one attempts to remove a file on a read-only branch. + +Suppose we have a two-branch union, where branch 0 is read-write and branch +1 is read-only. And a file 'foo' on branch 1: + +./b0/ +./b1/ +./b1/foo + +The unified view would simply be: + +./union/ +./union/foo + +Since 'foo' is stored on a read-only branch, it cannot be removed. A +whiteout is used to remove the name 'foo' from the unified namespace. Again, +since branch 1 is read-only, the whiteout cannot be created there. So, we +try on a higher priority (lower numerically) branch and create the whiteout +there. + +./b0/ +./b0/.wh.foo +./b1/ +./b1/foo + +Later, when Unionfs traverses branches (due to lookup or readdir), it +eliminate 'foo' from the namespace (as well as the whiteout itself.) + + +Opaque Directories: +=================== + +Assume we have a unionfs mount comprising of two branches. Branch 0 is +empty; branch 1 has the directory /a and file /a/f. Let's say we mount a +union of branch 0 as read-write and branch 1 as read-only. Now, let's say +we try to perform the following operation in the union: + + rm -fr a + +Because branch 1 is not writable, we cannot physically remove the file /a/f +or the directory /a. So instead, we will create a whiteout in branch 0 +named /.wh.a, masking out the name "a" from branch 1. Next, let's say we +try to create a directory named "a" as follows: + + mkdir a + +Because we have a whiteout for "a" already, Unionfs behaves as if "a" +doesn't exist, and thus will delete the whiteout and replace it with an +actual directory named "a". + +The problem now is that if you try to "ls" in the union, Unionfs will +perform is normal directory name unification, for *all* directories named +"a" in all branches. This will cause the file /a/f from branch 1 to +re-appear in the union's namespace, which violates Unix semantics. + +To avoid this problem, we have a different form of whiteouts for +directories, called "opaque directories" (same as BSD Union Mount does). +Whenever we replace a whiteout with a directory, that directory is marked as +opaque. In Unionfs 2.x, it means that we create a file named +/a/.wh.__dir_opaque in branch 0, after having created directory /a there. +When unionfs notices that a directory is opaque, it stops all namespace +operations (including merging readdir contents) at that opaque directory. +This prevents re-exposing names from masked out directories. + + +Duplicate Elimination: +====================== + +It is possible for files on different branches to have the same name. +Unionfs then has to select which instance of the file to show to the user. +Given the fact that each branch has a priority associated with it, the +simplest solution is to take the instance from the highest priority +(numerically lowest value) and "hide" the others. + + +Unlinking: +========= + +Unlink operation on non-directory instances is optimized to remove the +maximum possible objects in case multiple underlying branches have the same +file name. The unlink operation will first try to delete file instances +from highest priority branch and then move further to delete from remaining +branches in order of their decreasing priority. Consider a case (F..D..F), +where F is a file and D is a directory of the same name; here, some +intermediate branch could have an empty directory instance with the same +name, so this operation also tries to delete this directory instance and +proceed further to delete from next possible lower priority branch. The +unionfs unlink operation will smoothly delete the files with same name from +all possible underlying branches. In case if some error occurs, it creates +whiteout in highest priority branch that will hide file instance in rest of +the branches. An error could occur either if an unlink operations in any of +the underlying branch failed or if a branch has no write permission. + +This unlinking policy is known as "delete all" and it has the benefit of +overall reducing the number of inodes used by duplicate files, and further +reducing the total number of inodes consumed by whiteouts. The cost is of +extra processing, but testing shows this extra processing is well worth the +savings. + + +Copyup: +======= + +When a change is made to the contents of a file's data or meta-data, they +have to be stored somewhere. The best way is to create a copy of the +original file on a branch that is writable, and then redirect the write +though to this copy. The copy must be made on a higher priority branch so +that lookup and readdir return this newer "version" of the file rather than +the original (see duplicate elimination). + +An entire unionfs mount can be read-only or read-write. If it's read-only, +then none of the branches will be written to, even if some of the branches +are physically writeable. If the unionfs mount is read-write, then the +leftmost (highest priority) branch must be writeable (for copyup to take +place); the remaining branches can be any mix of read-write and read-only. + +In a writeable mount, unionfs will create new files/dir in the leftmost +branch. If one tries to modify a file in a read-only branch/media, unionfs +will copyup the file to the leftmost branch and modify it there. If you try +to modify a file from a writeable branch which is not the leftmost branch, +then unionfs will modify it in that branch; this is useful if you, say, +unify differnet packages (e.g., apache, sendmail, ftpd, etc.) and you want +changes to specific package files to remain logically in the directory where +they came from. + +Cache Coherency: +================ + +Unionfs users often want to be able to modify files and directories directly +on the lower branches, and have those changes be visible at the Unionfs +level. This means that data (e.g., pages) and meta-data (dentries, inodes, +open files, etc.) have to be synchronized between the upper and lower +layers. In other words, the newest changes from a layer below have to be +propagated to the Unionfs layer above. If the two layers are not in sync, a +cache incoherency ensues, which could lead to application failures and even +oopses. The Linux kernel, however, has a rather limited set of mechanisms +to ensure this inter-layer cache coherency---so Unionfs has to do most of +the hard work on its own. + +Maintaining Invariants: + +The way Unionfs ensures cache coherency is as follows. At each entry point +to a Unionfs file system method, we call a utility function to validate the +primary objects of this method. Generally, we call unionfs_file_revalidate +on open files, and __unionfs_d_revalidate_chain on dentries (which also +validates inodes). These utility functions check to see whether the upper +Unionfs object is in sync with any of the lower objects that it represents. +The checks we perform include whether the Unionfs superblock has a newer +generation number, or if any of the lower objects mtime's or ctime's are +newer. (Note: generation numbers change when branch-management commands are +issued, so in a way, maintaining cache coherency is also very important for +branch-management.) If indeed we determine that any Unionfs object is no +longer in sync with its lower counterparts, then we rebuild that object +similarly to how we do so for branch-management. + +While rebuilding Unionfs's objects, we also purge any page mappings and +truncate inode pages (see fs/unionfs/dentry.c:purge_inode_data). This is to +ensure that Unionfs will re-get the newer data from the lower branches. We +perform this purging only if the Unionfs operation in question is a reading +operation; if Unionfs is performing a data writing operation (e.g., ->write, +->commit_write, etc.) then we do NOT flush the lower mappings/pages: this is +because (1) a self-deadlock could occur and (2) the upper Unionfs pages are +considered more authoritative anyway, as they are newer and will overwrite +any lower pages. + +Unionfs maintains the following important invariant regarding mtime's, +ctime's, and atime's: the upper inode object's times are the max() of all of +the lower ones. For non-directory objects, there's only one object below, +so the mapping is simple; for directory objects, there could me multiple +lower objects and we have to sync up with the newest one of all the lower +ones. This invariant is important to maintain, especially for directories +(besides, we need this to be POSIX compliant). A union could comprise +multiple writable branches, each of which could change. If we don't reflect +the newest possible mtime/ctime, some applications could fail. For example, +NFSv2/v3 exports check for newer directory mtimes on the server to determine +if the client-side attribute cache should be purged. + +To maintain these important invariants, of course, Unionfs carefully +synchronizes upper and lower times in various places. For example, if we +copy-up a file to a top-level branch, the parent directory where the file +was copied up to will now have a new mtime: so after a successful copy-up, +we sync up with the new top-level branch's parent directory mtime. + +Implementation: + +This cache-coherency implementation is efficient because it defers any +synchronizing between the upper and lower layers until absolutely needed. +Consider the example a common situation where users perform a lot of lower +changes, such as untarring a whole package. While these take place, +typically the user doesn't access the files via Unionfs; only after the +lower changes are done, does the user try to access the lower files. With +our cache-coherency implementation, the entirety of the changes to the lower +branches will not result in a single CPU cycle spent at the Unionfs level +until the user invokes a system call that goes through Unionfs. + +We have considered two alternate cache-coherency designs. (1) Using the +dentry/inode notify functionality to register interest in finding out about +any lower changes. This is a somewhat limited and also a heavy-handed +approach which could result in many notifications to the Unionfs layer upon +each small change at the lower layer (imagine a file being modified multiple +times in rapid succession). (2) Rewriting the VFS to support explicit +callbacks from lower objects to upper objects. We began exploring such an +implementation, but found it to be very complicated--it would have resulted +in massive VFS/MM changes which are unlikely to be accepted by the LKML +community. We therefore believe that our current cache-coherency design and +implementation represent the best approach at this time. + +Limitations: + +Our implementation works in that as long as a user process will have caused +Unionfs to be called, directly or indirectly, even to just do +->d_revalidate; then we will have purged the current Unionfs data and the +process will see the new data. For example, a process that continually +re-reads the same file's data will see the NEW data as soon as the lower +file had changed, upon the next read(2) syscall (even if the file is still +open!) However, this doesn't work when the process re-reads the open file's +data via mmap(2) (unless the user unmaps/closes the file and remaps/reopens +it). Once we respond to ->readpage(s), then the kernel maps the page into +the process's address space and there doesn't appear to be a way to force +the kernel to invalidate those pages/mappings, and force the process to +re-issue ->readpage. If there's a way to invalidate active mappings and +force a ->readpage, let us know please (invalidate_inode_pages2 doesn't do +the trick). + +Our current Unionfs code has to perform many file-revalidation calls. It +would be really nice if the VFS would export an optional file system hook +->file_revalidate (similarly to dentry->d_revalidate) that will be called +before each VFS op that has a "struct file" in it. + +Certain file systems have micro-second granularity (or better) for inode +times, and asynchronous actions could cause those times to change with some +small delay. In such cases, Unionfs may see a changed inode time that only +differs by a tiny fraction of a second: such a change may be a false +positive indication that the lower object has changed, whereas if unionfs +waits a little longer, that false indication will not be seen. (These false +positives are harmless, because they would at most cause unionfs to +re-validate an object that may need no revalidation, and print a debugging +message that clutters the console/logs.) Therefore, to minimize the chances +of these situations, we delay the detection of changed times by a small +factor of a few seconds, called UNIONFS_MIN_CC_TIME (which defaults to 3 +seconds, as does NFS). This means that we will detect the change, only a +couple of seconds later, if indeed the time change persists in the lower +file object. This delayed detection has an added performance benefit: we +reduce the number of times that unionfs has to revalidate objects, in case +there's a lot of concurrent activity on both the upper and lower objects, +for the same file(s). Lastly, this delayed time attribute detection is +similar to how NFS clients operate (e.g., acregmin). + +Finally, there is no way currently in Linux to prevent lower directories +from being moved around (i.e., topology changes); there's no way to prevent +modifications to directory sub-trees of whole file systems which are mounted +read-write. It is therefore possible for in-flight operations in unionfs to +take place, while a lower directory is being moved around. Therefore, if +you try to, say, create a new file in a directory through unionfs, while the +directory is being moved around directly, then the new file may get created +in the new location where that directory was moved to. This is a somewhat +similar behaviour in NFS: an NFS client could be creating a new file while +th NFS server is moving th directory around; the file will get successfully +created in the new location. (The one exception in unionfs is that if the +branch is marked read-only by unionfs, then a copyup will take place.) + +For more information, see . diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/Documentation/filesystems/unionfs/issues.txt kernel-source-diablo-2.6.21/kernel-source/Documentation/filesystems/unionfs/issues.txt --- kernel-source-diablo-2.6.21.orig/kernel-source/Documentation/filesystems/unionfs/issues.txt 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/Documentation/filesystems/unionfs/issues.txt 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,28 @@ +KNOWN Unionfs 2.x ISSUES: +========================= + +1. Unionfs should not use lookup_one_len() on the underlying f/s as it + confuses NFSv4. Currently, unionfs_lookup() passes lookup intents to the + lower file-system, this eliminates part of the problem. The remaining + calls to lookup_one_len may need to be changed to pass an intent. We are + currently introducing VFS changes to fs/namei.c's do_path_lookup() to + allow proper file lookup and opening in stackable file systems. + +2. Lockdep (a debugging feature) isn't aware of stacking, and so it + incorrectly complains about locking problems. The problem boils down to + this: Lockdep considers all objects of a certain type to be in the same + class, for example, all inodes. Lockdep doesn't like to see a lock held + on two inodes within the same task, and warns that it could lead to a + deadlock. However, stackable file systems do precisely that: they lock + an upper object, and then a lower object, in a strict order to avoid + locking problems; in addition, Unionfs, as a fan-out file system, may + have to lock several lower inodes. We are currently looking into Lockdep + to see how to make it aware of stackable file systems. For now, we + temporarily disable lockdep when calling vfs methods on lower objects, + but only for those places where lockdep complained. While this solution + may seem unclean, it is not without precedent: other places in the kernel + also do similar temporary disabling, of course after carefully having + checked that it is the right thing to do. Anyway, you get any warnings + from Lockdep, please report them to the Unionfs maintainers. + +For more information, see . diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/Documentation/filesystems/unionfs/rename.txt kernel-source-diablo-2.6.21/kernel-source/Documentation/filesystems/unionfs/rename.txt --- kernel-source-diablo-2.6.21.orig/kernel-source/Documentation/filesystems/unionfs/rename.txt 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/Documentation/filesystems/unionfs/rename.txt 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,31 @@ +Rename is a complex beast. The following table shows which rename(2) operations +should succeed and which should fail. + +o: success +E: error (either unionfs or vfs) +X: EXDEV + +none = file does not exist +file = file is a file +dir = file is a empty directory +child= file is a non-empty directory +wh = file is a directory containing only whiteouts; this makes it logically + empty + + none file dir child wh +file o o E E E +dir o E o E o +child X E X E X +wh o E o E o + + +Renaming directories: +===================== + +Whenever a empty (either physically or logically) directory is being renamed, +the following sequence of events should take place: + +1) Remove whiteouts from both source and destination directory +2) Rename source to destination +3) Make destination opaque to prevent anything under it from showing up + diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/Documentation/filesystems/unionfs/usage.txt kernel-source-diablo-2.6.21/kernel-source/Documentation/filesystems/unionfs/usage.txt --- kernel-source-diablo-2.6.21.orig/kernel-source/Documentation/filesystems/unionfs/usage.txt 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/Documentation/filesystems/unionfs/usage.txt 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,134 @@ +Unionfs is a stackable unification file system, which can appear to merge +the contents of several directories (branches), while keeping their physical +content separate. Unionfs is useful for unified source tree management, +merged contents of split CD-ROM, merged separate software package +directories, data grids, and more. Unionfs allows any mix of read-only and +read-write branches, as well as insertion and deletion of branches anywhere +in the fan-out. To maintain Unix semantics, Unionfs handles elimination of +duplicates, partial-error conditions, and more. + +GENERAL SYNTAX +============== + +# mount -t unionfs -o , none MOUNTPOINT + +OPTIONS can be any legal combination of: + +- ro # mount file system read-only +- rw # mount file system read-write +- remount # remount the file system (see Branch Management below) +- incgen # increment generation no. (see Cache Consistency below) + +BRANCH-OPTIONS can be either (1) a list of branches given to the "dirs=" +option, or (2) a list of individual branch manipulation commands, combined +with the "remount" option, and is further described in the "Branch +Management" section below. + +The syntax for the "dirs=" mount option is: + + dirs=branch[=ro|=rw][:...] + +The "dirs=" option takes a colon-delimited list of directories to compose +the union, with an optional branch mode for each of those directories. +Directories that come earlier (specified first, on the left) in the list +have a higher precedence than those which come later. Additionally, +read-only or read-write permissions of the branch can be specified by +appending =ro or =rw (default) to each directory. See the Copyup section in +concepts.txt, for a description of Unionfs's behavior when mixing read-only +and read-write branches and mounts. + +Syntax: + + dirs=/branch1[=ro|=rw]:/branch2[=ro|=rw]:...:/branchN[=ro|=rw] + +Example: + + dirs=/writable_branch=rw:/read-only_branch=ro + + +BRANCH MANAGEMENT +================= + +Once you mount your union for the first time, using the "dirs=" option, you +can then change the union's overall mode or reconfigure the branches, using +the remount option, as follows. + +To downgrade a union from read-write to read-only: + +# mount -t unionfs -o remount,ro none MOUNTPOINT + +To upgrade a union from read-only to read-write: + +# mount -t unionfs -o remount,rw none MOUNTPOINT + +To delete a branch /foo, regardless where it is in the current union: + +# mount -t unionfs -o remount,del=/foo none MOUNTPOINT + +To insert (add) a branch /foo before /bar: + +# mount -t unionfs -o remount,add=/bar:/foo none MOUNTPOINT + +To insert (add) a branch /foo (with the "rw" mode flag) before /bar: + +# mount -t unionfs -o remount,add=/bar:/foo=rw none MOUNTPOINT + +To insert (add) a branch /foo (in "rw" mode) at the very beginning (i.e., a +new highest-priority branch), you can use the above syntax, or use a short +hand version as follows: + +# mount -t unionfs -o remount,add=/foo none MOUNTPOINT + +To append a branch to the very end (new lowest-priority branch): + +# mount -t unionfs -o remount,add=:/foo none MOUNTPOINT + +To append a branch to the very end (new lowest-priority branch), in +read-only mode: + +# mount -t unionfs -o remount,add=:/foo=ro none MOUNTPOINT + +Finally, to change the mode of one existing branch, say /foo, from read-only +to read-write, and change /bar from read-write to read-only: + +# mount -t unionfs -o remount,mode=/foo=rw,mode=/bar=ro none MOUNTPOINT + +Note: in Unionfs 2.x, you cannot set the leftmost branch to readonly because +then Unionfs won't have any writable place for copyups to take place. +Moreover, the VFS can get confused when it tries to modify something in a +file system mounted read-write, but isn't permitted to write to it. +Instead, you should set the whole union as readonly, as described above. +If, however, you must set the leftmost branch as readonly, perhaps so you +can get a snapshot of it at a point in time, then you should insert a new +writable top-level branch, and mark the one you want as readonly. This can +be accomplished as follows, assuming that /foo is your current leftmost +branch: + +# mount -t tmpfs -o size=NNN /new +# mount -t unionfs -o remount,add=/new,mode=/foo=ro none MOUNTPOINT + +# mount -t unionfs -o remount,del=/new,mode=/foo=rw none MOUNTPOINT + +# umount /new + +CACHE CONSISTENCY +================= + +If you modify any file on any of the lower branches directly, while there is +a Unionfs 2.x mounted above any of those branches, you should tell Unionfs +to purge its caches and re-get the objects. To do that, you have to +increment the generation number of the superblock using the following +command: + +# mount -t unionfs -o remount,incgen none MOUNTPOINT + +Note that the older way of incrementing the generation number using an +ioctl, is no longer supported in Unionfs 2.0 and newer. Ioctls in general +are not encouraged. Plus, an ioctl is per-file concept, whereas the +generation number is a per-file-system concept. Worse, such an ioctl +requires an open file, which then has to be invalidated by the very nature +of the generation number increase (read: the old generation increase ioctl +was pretty racy). + + +For more information, see . diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/Documentation/kprobes.txt kernel-source-diablo-2.6.21/kernel-source/Documentation/kprobes.txt --- kernel-source-diablo-2.6.21.orig/kernel-source/Documentation/kprobes.txt 2007-10-02 08:09:30.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/Documentation/kprobes.txt 2009-07-01 17:29:10.000000000 +0100 @@ -140,6 +140,7 @@ architectures: - ppc64 - ia64 (Does not support probes on instruction slot1.) - sparc64 (Return probes not yet implemented.) +- arm 3. Configuring Kprobes diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/MAINTAINERS kernel-source-diablo-2.6.21/kernel-source/MAINTAINERS --- kernel-source-diablo-2.6.21.orig/kernel-source/MAINTAINERS 2007-10-02 08:09:30.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/MAINTAINERS 2009-07-01 17:29:10.000000000 +0100 @@ -3364,6 +3364,14 @@ L: linux-kernel@vger.kernel.org W: http://www.kernel.dk S: Maintained +UNIONFS +P: Erez Zadok +M: ezk@cs.sunysb.edu +L: unionfs@filesystems.org +W: http://unionfs.filesystems.org +T: git git.kernel.org/pub/scm/linux/kernel/git/ezk/unionfs.git +S: Maintained + USB ACM DRIVER P: Oliver Neukum M: oliver@neukum.name diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/arch/arm/Kconfig kernel-source-diablo-2.6.21/kernel-source/arch/arm/Kconfig --- kernel-source-diablo-2.6.21.orig/kernel-source/arch/arm/Kconfig 2007-10-02 08:09:30.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/arch/arm/Kconfig 2009-07-01 17:29:10.000000000 +0100 @@ -1009,8 +1009,15 @@ endmenu source "fs/Kconfig" +menu "Instrumentation Support" + depends on EXPERIMENTAL + source "arch/arm/oprofile/Kconfig" +source "arch/arm/kernel/Kconfig" + +endmenu + source "arch/arm/Kconfig.debug" source "security/Kconfig" diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/arch/arm/configs/nokia_2420_defconfig kernel-source-diablo-2.6.21/kernel-source/arch/arm/configs/nokia_2420_defconfig --- kernel-source-diablo-2.6.21.orig/kernel-source/arch/arm/configs/nokia_2420_defconfig 2009-07-01 17:15:49.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/arch/arm/configs/nokia_2420_defconfig 2009-07-01 17:30:01.000000000 +0100 @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux kernel version: 2.6.21-omap1 -# Wed Jul 30 11:33:54 2008 +# Fri Jun 12 09:02:33 2009 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -76,7 +76,7 @@ CONFIG_BASE_SMALL=0 # CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y -# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_MODULE_FORCE_UNLOAD=y # CONFIG_MODVERSIONS is not set # CONFIG_MODULE_SRCVERSION_ALL is not set # CONFIG_KMOD is not set @@ -278,7 +278,7 @@ CONFIG_CPU_FREQ_DEFAULT_GOV_NULL=y CONFIG_CPU_FREQ_GOV_PERFORMANCE=y CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_NULL=y -# CONFIG_CPU_FREQ_GOV_USERSPACE is not set +CONFIG_CPU_FREQ_GOV_USERSPACE=m CONFIG_CPU_FREQ_GOV_ONDEMAND=y CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y @@ -331,7 +331,10 @@ CONFIG_INET=y CONFIG_IP_MULTICAST=y # CONFIG_IP_ADVANCED_ROUTER is not set CONFIG_IP_FIB_HASH=y -# CONFIG_IP_PNP is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set # CONFIG_NET_IPIP is not set # CONFIG_NET_IPGRE is not set # CONFIG_IP_MROUTE is not set @@ -589,8 +592,10 @@ CONFIG_MTD_CFI_I2=y # CONFIG_MTD_M25P80 is not set # CONFIG_MTD_SLRAM is not set # CONFIG_MTD_PHRAM is not set -# CONFIG_MTD_MTDRAM is not set -# CONFIG_MTD_BLOCK2MTD is not set +CONFIG_MTD_MTDRAM=m +CONFIG_MTDRAM_TOTAL_SIZE=4096 +CONFIG_MTDRAM_ERASE_SIZE=128 +CONFIG_MTD_BLOCK2MTD=m # # Disk-On-Chip Device Drivers @@ -653,8 +658,9 @@ CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y # CONFIG_CHR_DEV_ST is not set # CONFIG_CHR_DEV_OSST is not set -# CONFIG_BLK_DEV_SR is not set -# CONFIG_CHR_DEV_SG is not set +CONFIG_BLK_DEV_SR=m +CONFIG_BLK_DEV_SR_VENDOR=y +CONFIG_CHR_DEV_SG=m # CONFIG_CHR_DEV_SCH is not set # @@ -688,7 +694,16 @@ CONFIG_SCSI_MULTI_LUN=y # # Multi-device support (RAID and LVM) # -# CONFIG_MD is not set +CONFIG_MD=y +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_DM=m +# CONFIG_DM_DEBUG is not set +CONFIG_DM_CRYPT=m +CONFIG_DM_LOOP=m +# CONFIG_DM_SNAPSHOT is not set +# CONFIG_DM_MIRROR is not set +# CONFIG_DM_ZERO is not set +# CONFIG_DM_MULTIPATH is not set # # Fusion MPT device support @@ -766,7 +781,7 @@ CONFIG_PPP_ASYNC=y CONFIG_PPP_SYNC_TTY=y CONFIG_PPP_DEFLATE=y CONFIG_PPP_BSDCOMP=y -# CONFIG_PPP_MPPE is not set +CONFIG_PPP_MPPE=m # CONFIG_PPPOE is not set # CONFIG_SLIP is not set CONFIG_SLHC=y @@ -793,7 +808,7 @@ CONFIG_INPUT_MOUSEDEV=y # CONFIG_INPUT_MOUSEDEV_PSAUX is not set CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 -# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_JOYDEV=m # CONFIG_INPUT_TSDEV is not set CONFIG_INPUT_EVDEV=y # CONFIG_INPUT_EVBUG is not set @@ -826,7 +841,8 @@ CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_TSC2005=y CONFIG_TOUCHSCREEN_TSC2301=y # CONFIG_TOUCHSCREEN_TSC2046 is not set -# CONFIG_INPUT_MISC is not set +CONFIG_INPUT_MISC=y +CONFIG_INPUT_UINPUT=m # # Hardware I/O ports @@ -1020,6 +1036,7 @@ CONFIG_SENSORS_TMP105=y # # Misc devices # +CONFIG_POWERVR=m # # Multifunction device drivers @@ -1124,7 +1141,11 @@ CONFIG_FB_OMAP_PAUSING=y # # CONFIG_VGA_CONSOLE is not set CONFIG_DUMMY_CONSOLE=y -# CONFIG_FRAMEBUFFER_CONSOLE is not set +CONFIG_FRAMEBUFFER_CONSOLE=m +# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set +# CONFIG_FONTS is not set +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y # # Logo configuration @@ -1205,7 +1226,7 @@ CONFIG_USB_DEVICEFS=y CONFIG_USB_DYNAMIC_MINORS=y CONFIG_USB_SUSPEND=y CONFIG_USB_OTG=y -CONFIG_USB_OTG_WHITELIST=y +# CONFIG_USB_OTG_WHITELIST is not set # CONFIG_USB_OTG_BLACKLIST_HUB is not set # @@ -1269,7 +1290,7 @@ CONFIG_USB_HID=y # CONFIG_USB_POWERMATE is not set # CONFIG_USB_TOUCHSCREEN is not set # CONFIG_USB_YEALINK is not set -# CONFIG_USB_XPAD is not set +CONFIG_USB_XPAD=m # CONFIG_USB_ATI_REMOTE is not set # CONFIG_USB_ATI_REMOTE2 is not set # CONFIG_USB_KEYSPAN_REMOTE is not set @@ -1315,7 +1336,45 @@ CONFIG_USB_MON=y # # USB Serial Converter support # -# CONFIG_USB_SERIAL is not set +CONFIG_USB_SERIAL=m +CONFIG_USB_SERIAL_GENERIC=y +# CONFIG_USB_SERIAL_AIRCABLE is not set +# CONFIG_USB_SERIAL_AIRPRIME is not set +# CONFIG_USB_SERIAL_ARK3116 is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_CP2101 is not set +# CONFIG_USB_SERIAL_CYPRESS_M8 is not set +# CONFIG_USB_SERIAL_EMPEG is not set +CONFIG_USB_SERIAL_FTDI_SIO=m +# CONFIG_USB_SERIAL_FUNSOFT is not set +CONFIG_USB_SERIAL_VISOR=m +CONFIG_USB_SERIAL_IPAQ=m +CONFIG_USB_SERIAL_IR=m +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_EDGEPORT_TI is not set +CONFIG_USB_SERIAL_GARMIN=m +# CONFIG_USB_SERIAL_IPW is not set +CONFIG_USB_SERIAL_KEYSPAN_PDA=m +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KLSI is not set +# CONFIG_USB_SERIAL_KOBIL_SCT is not set +CONFIG_USB_SERIAL_MCT_U232=m +# CONFIG_USB_SERIAL_MOS7720 is not set +# CONFIG_USB_SERIAL_MOS7840 is not set +CONFIG_USB_SERIAL_NAVMAN=m +CONFIG_USB_SERIAL_PL2303=m +# CONFIG_USB_SERIAL_HP4X is not set +# CONFIG_USB_SERIAL_SAFE is not set +# CONFIG_USB_SERIAL_SIERRAWIRELESS is not set +# CONFIG_USB_SERIAL_TI is not set +# CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_XIRCOM is not set +CONFIG_USB_SERIAL_OPTION=m +# CONFIG_USB_SERIAL_OMNINET is not set +# CONFIG_USB_SERIAL_DEBUG is not set +CONFIG_USB_EZUSB=y # # USB Miscellaneous drivers @@ -1366,7 +1425,7 @@ CONFIG_USB_ETH_RNDIS=y CONFIG_USB_GADGETFS=m CONFIG_USB_FILE_STORAGE=m CONFIG_USB_FILE_STORAGE_TEST=y -# CONFIG_USB_G_SERIAL is not set +CONFIG_USB_G_SERIAL=m # CONFIG_USB_MIDI_GADGET is not set # @@ -1414,10 +1473,23 @@ CONFIG_EXT3_FS_XATTR=y CONFIG_JBD=m # CONFIG_JBD_DEBUG is not set CONFIG_FS_MBCACHE=m -# CONFIG_REISERFS_FS is not set -# CONFIG_JFS_FS is not set +CONFIG_REISERFS_FS=m +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +CONFIG_REISERFS_FS_XATTR=y +# CONFIG_REISERFS_FS_POSIX_ACL is not set +# CONFIG_REISERFS_FS_SECURITY is not set +CONFIG_JFS_FS=m +# CONFIG_JFS_POSIX_ACL is not set +# CONFIG_JFS_SECURITY is not set +# CONFIG_JFS_DEBUG is not set +# CONFIG_JFS_STATISTICS is not set # CONFIG_FS_POSIX_ACL is not set -# CONFIG_XFS_FS is not set +CONFIG_XFS_FS=m +# CONFIG_XFS_QUOTA is not set +# CONFIG_XFS_SECURITY is not set +# CONFIG_XFS_POSIX_ACL is not set +# CONFIG_XFS_RT is not set # CONFIG_GFS2_FS is not set # CONFIG_OCFS2_FS is not set # CONFIG_MINIX_FS is not set @@ -1428,13 +1500,16 @@ CONFIG_INOTIFY_USER=y CONFIG_DNOTIFY=y # CONFIG_AUTOFS_FS is not set # CONFIG_AUTOFS4_FS is not set -# CONFIG_FUSE_FS is not set +CONFIG_FUSE_FS=m # # CD-ROM/DVD Filesystems # -# CONFIG_ISO9660_FS is not set -# CONFIG_UDF_FS is not set +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_UDF_FS=m +CONFIG_UDF_NLS=y # # DOS/FAT/NT Filesystems @@ -1444,7 +1519,9 @@ CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y CONFIG_FAT_DEFAULT_CODEPAGE=437 CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" -# CONFIG_NTFS_FS is not set +CONFIG_NTFS_FS=m +# CONFIG_NTFS_DEBUG is not set +CONFIG_NTFS_RW=y # # Pseudo filesystems @@ -1459,6 +1536,13 @@ CONFIG_RAMFS=y # CONFIG_CONFIGFS_FS is not set # +# Layered filesystems +# +CONFIG_UNION_FS=m +# CONFIG_UNION_FS_XATTR is not set +# CONFIG_UNION_FS_DEBUG is not set + +# # Miscellaneous filesystems # # CONFIG_ADFS_FS is not set @@ -1493,10 +1577,27 @@ CONFIG_JFFS2_CMODE_PRIORITY=y # # Network File Systems # -# CONFIG_NFS_FS is not set +CONFIG_NFS_FS=m +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set # CONFIG_NFSD is not set +CONFIG_LOCKD=m +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=m +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set # CONFIG_SMB_FS is not set -# CONFIG_CIFS is not set +CONFIG_CIFS=m +CONFIG_CIFS_STATS=y +CONFIG_CIFS_STATS2=y +# CONFIG_CIFS_WEAK_PW_HASH is not set +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y +# CONFIG_CIFS_DEBUG2 is not set +# CONFIG_CIFS_EXPERIMENTAL is not set # CONFIG_NCP_FS is not set # CONFIG_CODA_FS is not set # CONFIG_AFS_FS is not set @@ -1558,11 +1659,20 @@ CONFIG_NLS_UTF8=y # CONFIG_DLM is not set # +# Instrumentation Support +# + +# # Profiling support # # CONFIG_PROFILING is not set # +# Kernel Probes(Kprobes) +# +# CONFIG_KPROBES is not set + +# # Kernel hacking # CONFIG_PRINTK_TIME=y @@ -1573,7 +1683,7 @@ CONFIG_DEBUG_FS=y # CONFIG_HEADERS_CHECK is not set CONFIG_DEBUG_KERNEL=y # CONFIG_DEBUG_SHIRQ is not set -CONFIG_LOG_BUF_SHIFT=14 +CONFIG_LOG_BUF_SHIFT=16 CONFIG_DETECT_SOFTLOCKUP=y # CONFIG_SCHEDSTATS is not set # CONFIG_TIMER_STATS is not set @@ -1615,7 +1725,47 @@ CONFIG_SECURITY_LOWMEM=y # # Cryptographic options # -# CONFIG_CRYPTO is not set +CONFIG_CRYPTO=y +CONFIG_CRYPTO_ALGAPI=m +CONFIG_CRYPTO_BLKCIPHER=m +CONFIG_CRYPTO_MANAGER=m +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +CONFIG_CRYPTO_SHA1=m +CONFIG_CRYPTO_SHA256=m +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_WP512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_GF128MUL is not set +CONFIG_CRYPTO_ECB=m +CONFIG_CRYPTO_CBC=m +CONFIG_CRYPTO_PCBC=m +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_TWOFISH is not set +# CONFIG_CRYPTO_SERPENT is not set +CONFIG_CRYPTO_AES=m +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_TEA is not set +CONFIG_CRYPTO_ARC4=m +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_ANUBIS is not set +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_LZO is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Hardware crypto devices +# # # Library routines diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/arch/arm/kernel/Kconfig kernel-source-diablo-2.6.21/kernel-source/arch/arm/kernel/Kconfig --- kernel-source-diablo-2.6.21.orig/kernel-source/arch/arm/kernel/Kconfig 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/arch/arm/kernel/Kconfig 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,13 @@ +menu "Kernel Probes(Kprobes)" + depends on KALLSYMS && EXPERIMENTAL && MODULES + +config KPROBES + bool "Kernel Probes (EXPERIMENTAL)" + help + Kprobes allows you to trap at almost any kernel address and + execute a callback function. register_kprobe() establishes + a probepoint and specifies the callback. Kprobes is useful + for kernel debugging, non-intrusive instrumentation and testing. + If in doubt, say "N". + +endmenu diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/arch/arm/kernel/Makefile kernel-source-diablo-2.6.21/kernel-source/arch/arm/kernel/Makefile --- kernel-source-diablo-2.6.21.orig/kernel-source/arch/arm/kernel/Makefile 2007-10-02 08:09:30.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/arch/arm/kernel/Makefile 2009-07-01 17:29:10.000000000 +0100 @@ -19,6 +19,7 @@ obj-$(CONFIG_ISA_DMA) += dma-isa.o obj-$(CONFIG_PCI) += bios32.o isa.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o +obj-$(CONFIG_KPROBES) += kprobes.o kprobes-decode.o obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/arch/arm/kernel/armksyms.c kernel-source-diablo-2.6.21/kernel-source/arch/arm/kernel/armksyms.c --- kernel-source-diablo-2.6.21.orig/kernel-source/arch/arm/kernel/armksyms.c 2007-10-02 08:09:30.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/arch/arm/kernel/armksyms.c 2009-07-01 17:29:10.000000000 +0100 @@ -111,6 +111,7 @@ EXPORT_SYMBOL(__memzero); /* user mem (segment) */ EXPORT_SYMBOL(__strnlen_user); EXPORT_SYMBOL(__strncpy_from_user); +EXPORT_SYMBOL(copy_page); #ifdef CONFIG_MMU EXPORT_SYMBOL(__copy_from_user); diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/arch/arm/kernel/entry-armv.S kernel-source-diablo-2.6.21/kernel-source/arch/arm/kernel/entry-armv.S --- kernel-source-diablo-2.6.21.orig/kernel-source/arch/arm/kernel/entry-armv.S 2007-10-02 08:09:30.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/arch/arm/kernel/entry-armv.S 2009-07-01 17:29:10.000000000 +0100 @@ -11,8 +11,8 @@ * * Low-level vector interface routines * - * Note: there is a StrongARM bug in the STMIA rn, {regs}^ instruction that causes - * it to save wrong values... Be aware! + * Note: there is a StrongARM bug in the STMIA rn, {regs}^ instruction + * that causes it to save wrong values... Be aware! */ #include @@ -58,6 +58,12 @@ .endm +#ifdef CONFIG_KPROBES + .section .kprobes.text,"ax",%progbits +#else + .text +#endif + /* * Invalid mode handlers */ @@ -242,7 +248,16 @@ svc_preempt: .align 5 __und_svc: +#ifdef CONFIG_KPROBES + @ Give kprobe'd instruction stack space to finish in case of STM. + sub sp, sp, #64 +#endif svc_entry +#ifdef CONFIG_KPROBES + @ Adjust saved SP back to its original value. + add r0, r0, #64 + str r0, [sp, #S_SP] +#endif @ @ call emulation code, which returns using r9 if it has emulated @@ -548,7 +563,7 @@ do_fpe: .data ENTRY(fp_enter) .word no_fp - .text + .previous no_fp: mov pc, lr diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/arch/arm/kernel/kprobes-decode.c kernel-source-diablo-2.6.21/kernel-source/arch/arm/kernel/kprobes-decode.c --- kernel-source-diablo-2.6.21.orig/kernel-source/arch/arm/kernel/kprobes-decode.c 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/arch/arm/kernel/kprobes-decode.c 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,1599 @@ +/* arch/arm/kernel/kprobes-decode.c + * + * Copyright (C) 2006, 2007 Motorola Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + */ + +/* We do not have hardware single-stepping on ARM, This + * effort is further complicated by the ARM not having a + * "next PC" register. Instructions that change the PC + * can't be safely single-stepped in a MP environment, so + * we have a lot of work to do: + * + * In the prepare phase: + * *) If it is an instruction that does anything + * with the CPU mode, we reject it for a kprobe. + * (This is out of laziness rather than need. The + * instructions could be simulated.) + * + * *) Otherwise, decode the instruction rewriting its + * registers to take fixed, ordered registers and + * setting a handler for it to run the instruction. + * + * In the execution phase by an instruction's handler: + * + * *) If the PC is written to by the instruction, the + * instruction must be fully simulated in software. + * If it is a conditional instruction, the handler + * will use insn[0] to copy its condition code to + * set r0 to 1 and insn[1] to "mov pc, lr" to return. + * + * *) Otherwise, a modified form of the instruction can + * directly executed. Its handler calls the + * instruction in insn[0]. In insn[1] is a + * "mov pc, lr" to return. + * + * Before calling, load up the reordered registers + * from the original instruction's registers. If one + * of the original input registers is the PC, compute + * and adjust the appropriate input register. + * + * After call completes, copy the output registers to + * the original instruction's original registers. + * + * We don't use a real breakpoint instruction since that + * would have us in the kernel go from SVC mode to SVC + * mode losing the link register. Instead we use an + * undefined instruction. To simply processing, the + * undefined instruction used for kprobes must be reserved + * exclusively for kprobes use. + */ + +/* TODO: ifdef instruction decoding based on architecture. + */ + +#include +#include +#include + +#define array_cnt(x) (sizeof((x))/sizeof(*(x))) + +#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit))))) + +#define branch_displacement(insn) sign_extend(((insn) & 0xffffff) << 2, 25) + +#define PSR_fs (PSR_f|PSR_s) + +#define SET_R0_TRUE_INSTRUCTION 0xe3a00001 /* mov r0, #1 */ +#define truecc_insn(insn) ( ((insn) & 0xf0000000) | \ + (SET_R0_TRUE_INSTRUCTION & 0x0fffffff) ) + +typedef long (insn_0arg_fn_t)(void); +typedef long (insn_1arg_fn_t)(long); +typedef long (insn_2arg_fn_t)(long, long); +typedef long (insn_3arg_fn_t)(long, long, long); +typedef long (insn_4arg_fn_t)(long, long, long, long); +typedef long long (insn_llret_0arg_fn_t)(void); +typedef long long (insn_llret_3arg_fn_t)(long, long, long); +typedef long long (insn_llret_4arg_fn_t)(long, long, long, long); + +typedef union { + long long dr; +#ifdef __LITTLE_ENDIAN + struct { long r0, r1; }; +#else + struct { long r1, r0; }; +#endif +} reg_pair; + +extern int kprobes_str_pc_offset; + + +/* The insnslot_?arg_r[w]flags() functions below are to keep the + * msr -> *fn -> mrs instruction sequences indivisible so that + * the state of the CPSR flags aren't inadvertently modified + * just before or just after the call. + */ + +static inline long __kprobes insnslot_0arg_rflags(long cpsr, insn_0arg_fn_t *fn) +{ + register long ret asm("r0"); + + __asm__ volatile( + "msr cpsr_fs, %[cpsr] \n\t" + "mov lr, pc \n\t" + "mov pc, %[fn] \n\t" + : "=r" (ret) + : [cpsr] "r" (cpsr), [fn] "r" (fn) + : "lr", "cc" + ); + return ret; +} + +static inline long long __kprobes insnslot_llret_0arg_rflags(long cpsr, + insn_llret_0arg_fn_t *fn) +{ + register long ret0 asm("r0"); + register long ret1 asm("r1"); + reg_pair fnr; + + __asm__ volatile( + "msr cpsr_fs, %[cpsr] \n\t" + "mov lr, pc \n\t" + "mov pc, %[fn] \n\t" + : "=r" (ret0), "=r" (ret1) + : [cpsr] "r" (cpsr), [fn] "r" (fn) + : "lr", "cc" + ); + fnr.r0 = ret0; + fnr.r1 = ret1; + return fnr.dr; +} + + +static inline long __kprobes insnslot_1arg_rflags(long r0, long cpsr, + insn_1arg_fn_t *fn) +{ + register long rr0 asm("r0") = r0; + register long ret asm("r0"); + + __asm__ volatile( + "msr cpsr_fs, %[cpsr] \n\t" + "mov lr, pc \n\t" + "mov pc, %[fn] \n\t" + : "=r" (ret) + : "0" (rr0), [cpsr] "r" (cpsr), [fn] "r" (fn) + : "lr", "cc" + ); + return ret; +} + + +static inline long __kprobes insnslot_2arg_rflags(long r0, long r1, long cpsr, + insn_2arg_fn_t *fn) +{ + register long rr0 asm("r0") = r0; + register long rr1 asm("r1") = r1; + register long ret asm("r0"); + + __asm__ volatile( + "msr cpsr_fs, %[cpsr] \n\t" + "mov lr, pc \n\t" + "mov pc, %[fn] \n\t" + : "=r" (ret) + : "0" (rr0), "r" (rr1), + [cpsr] "r" (cpsr), [fn] "r" (fn) + : "lr", "cc" + ); + return ret; +} + + +static inline long __kprobes insnslot_3arg_rflags(long r0, long r1, long r2, + long cpsr, insn_3arg_fn_t *fn) +{ + register long rr0 asm("r0") = r0; + register long rr1 asm("r1") = r1; + register long rr2 asm("r2") = r2; + register long ret asm("r0"); + + __asm__ volatile( + "msr cpsr_fs, %[cpsr] \n\t" + "mov lr, pc \n\t" + "mov pc, %[fn] \n\t" + : "=r" (ret) + : "0" (rr0), "r" (rr1), "r" (rr2), + [cpsr] "r" (cpsr), [fn] "r" (fn) + : "lr", "cc" + ); + return ret; +} + + +static inline long long __kprobes insnslot_llret_3arg_rflags(long r0, long r1, + long r2, long cpsr, + insn_llret_3arg_fn_t *fn) +{ + register long rr0 asm("r0") = r0; + register long rr1 asm("r1") = r1; + register long rr2 asm("r2") = r2; + register long ret0 asm("r0"); + register long ret1 asm("r1"); + reg_pair fnr; + + __asm__ volatile( + "msr cpsr_fs, %[cpsr] \n\t" + "mov lr, pc \n\t" + "mov pc, %[fn] \n\t" + : "=r" (ret0), "=r" (ret1) + : "0" (rr0), "r" (rr1), "r" (rr2), + [cpsr] "r" (cpsr), [fn] "r" (fn) + : "lr", "cc" + ); + fnr.r0 = ret0; + fnr.r1 = ret1; + return fnr.dr; +} + + +static inline long __kprobes insnslot_4arg_rflags(long r0, long r1, long r2, + long r3, long cpsr, + insn_4arg_fn_t *fn) +{ + register long rr0 asm("r0") = r0; + register long rr1 asm("r1") = r1; + register long rr2 asm("r2") = r2; + register long rr3 asm("r3") = r3; + register long ret asm("r0"); + + __asm__ volatile( + "msr cpsr_fs, %[cpsr] \n\t" + "mov lr, pc \n\t" + "mov pc, %[fn] \n\t" + : "=r" (ret) + : "0" (rr0), "r" (rr1), "r" (rr2), "r" (rr3), + [cpsr] "r" (cpsr), [fn] "r" (fn) + : "lr", "cc" + ); + return ret; +} + + +static inline long __kprobes insnslot_1arg_rwflags(long r0, long *cpsr, + insn_1arg_fn_t *fn) +{ + register long rr0 asm("r0") = r0; + register long ret asm("r0"); + long oldcpsr = *cpsr; + long newcpsr; + + __asm__ volatile( + "msr cpsr_fs, %[oldcpsr] \n\t" + "mov lr, pc \n\t" + "mov pc, %[fn] \n\t" + "mrs %[newcpsr], cpsr \n\t" + : "=r" (ret), [newcpsr] "=r" (newcpsr) + : "0" (rr0), [oldcpsr] "r" (oldcpsr), [fn] "r" (fn) + : "lr", "cc" + ); + *cpsr = (oldcpsr & ~PSR_fs) | (newcpsr & PSR_fs); + return ret; +} + + +static inline long __kprobes insnslot_2arg_rwflags(long r0, long r1, long *cpsr, + insn_2arg_fn_t *fn) +{ + register long rr0 asm("r0") = r0; + register long rr1 asm("r1") = r1; + register long ret asm("r0"); + long oldcpsr = *cpsr; + long newcpsr; + + __asm__ volatile( + "msr cpsr_fs, %[oldcpsr] \n\t" + "mov lr, pc \n\t" + "mov pc, %[fn] \n\t" + "mrs %[newcpsr], cpsr \n\t" + : "=r" (ret), [newcpsr] "=r" (newcpsr) + : "0" (rr0), "r" (rr1), [oldcpsr] "r" (oldcpsr), [fn] "r" (fn) + : "lr", "cc" + ); + *cpsr = (oldcpsr & ~PSR_fs) | (newcpsr & PSR_fs); + return ret; +} + + +static inline long __kprobes insnslot_3arg_rwflags(long r0, long r1, long r2, + long *cpsr, + insn_3arg_fn_t *fn) +{ + register long rr0 asm("r0") = r0; + register long rr1 asm("r1") = r1; + register long rr2 asm("r2") = r2; + register long ret asm("r0"); + long oldcpsr = *cpsr; + long newcpsr; + + __asm__ volatile( + "msr cpsr_fs, %[oldcpsr] \n\t" + "mov lr, pc \n\t" + "mov pc, %[fn] \n\t" + "mrs %[newcpsr], cpsr \n\t" + : "=r" (ret), [newcpsr] "=r" (newcpsr) + : "0" (rr0), "r" (rr1), "r" (rr2), + [oldcpsr] "r" (oldcpsr), [fn] "r" (fn) + : "lr", "cc" + ); + *cpsr = (oldcpsr & ~PSR_fs) | (newcpsr & PSR_fs); + return ret; +} + + +static inline long __kprobes insnslot_4arg_rwflags(long r0, long r1, long r2, + long r3, long *cpsr, + insn_4arg_fn_t *fn) +{ + register long rr0 asm("r0") = r0; + register long rr1 asm("r1") = r1; + register long rr2 asm("r2") = r2; + register long rr3 asm("r3") = r3; + register long ret asm("r0"); + long oldcpsr = *cpsr; + long newcpsr; + + __asm__ volatile( + "msr cpsr_fs, %[oldcpsr] \n\t" + "mov lr, pc \n\t" + "mov pc, %[fn] \n\t" + "mrs %[newcpsr], cpsr \n\t" + : "=r" (ret), [newcpsr] "=r" (newcpsr) + : "0" (rr0), "r" (rr1), "r" (rr2), "r" (rr3), + [oldcpsr] "r" (oldcpsr), [fn] "r" (fn) + : "lr", "cc" + ); + *cpsr = (oldcpsr & ~PSR_fs) | (newcpsr & PSR_fs); + return ret; +} + + +static inline long long __kprobes insnslot_llret_4arg_rwflags(long r0, long r1, + long r2, long r3, long *cpsr, + insn_llret_4arg_fn_t *fn) +{ + register long rr0 asm("r0") = r0; + register long rr1 asm("r1") = r1; + register long rr2 asm("r2") = r2; + register long rr3 asm("r3") = r3; + register long ret0 asm("r0"); + register long ret1 asm("r1"); + long oldcpsr = *cpsr; + long newcpsr; + reg_pair fnr; + + __asm__ volatile( + "msr cpsr_fs, %[oldcpsr] \n\t" + "mov lr, pc \n\t" + "mov pc, %[fn] \n\t" + "mrs %[newcpsr], cpsr \n\t" + : "=r" (ret0), "=r" (ret1), [newcpsr] "=r" (newcpsr) + : "0" (rr0), "r" (rr1), "r" (rr2), "r" (rr3), + [oldcpsr] "r" (oldcpsr), [fn] "r" (fn) + : "lr", "cc" + ); + *cpsr = (oldcpsr & ~PSR_fs) | (newcpsr & PSR_fs); + fnr.r0 = ret0; + fnr.r1 = ret1; + return fnr.dr; +} + + +/* + * To avoid having to the complications of mimicing single-stepping + * on a processor without a Next-PC or a single-step mode, and to + * avoid having to deal with the side-effects of boosting, we + * simulate or emulate (almost) all ARM instructions. + * + * "Simulation" is where the instruction's behavior is duplicated in + * C code. "Emulation" is where the original instruction is rewritten + * and executed, often by altering its registers. + * + * By having all behavior of the kprobe'd instruction completed before + * returning from the kprobe_handler(), all locks (scheduler and + * interrupt) can safely be released. There is no need for secondary + * breakpoints, no race with MP or preemptable kernels, nor having to + * clean up resources counts at a later time impacting overall system + * performance. By rewriting the instruction, only the minimum registers + * need to be loaded and saved back optimizing perfoamance. + * + * Calling the insnslot_*_rwflags version of a function doesn't hurt + * anything even when the CPSR flags aren't updated by the + * instruction. It's just a little slower in return for saving + * a little space by not having a duplicate function that doesn't + * update the flags. (The same optimization can be said for + * instructions that do or don't update the writeback register.) + * Also, instructions can either read the flags, only write the + * flags, or read and write the flags. To save combinations + * rather than for sheer performance, flag functions just assume + * read and write of flags. + */ + +static void __kprobes simulate_bbl(struct kprobe *p, struct pt_regs *regs) +{ + insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + long iaddr = (long)p->addr; + int disp = branch_displacement(insn); + + if (!insnslot_1arg_rflags(0, regs->ARM_cpsr, i_fn)) + return; + + if (insn & (1 << 24)) + regs->ARM_lr = iaddr + 4; + + regs->ARM_pc = iaddr + 8 + disp; +} + + +static void __kprobes simulate_blx1(struct kprobe *p, struct pt_regs *regs) +{ + kprobe_opcode_t insn = p->opcode; + long iaddr = (long)p->addr; + int disp = branch_displacement(insn); + + regs->ARM_lr = iaddr + 4; + regs->ARM_pc = iaddr + 8 + disp + ((insn >> 23) & 0x2); + regs->ARM_cpsr |= PSR_T_BIT; +} + + +static void __kprobes simulate_blx2bx(struct kprobe *p, struct pt_regs *regs) +{ + insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + int rm = insn & 0xf; + long rmv = regs->uregs[rm]; + + if (!insnslot_1arg_rflags(0, regs->ARM_cpsr, i_fn)) + return; + + if (insn & (1 << 5)) + regs->ARM_lr = (long)p->addr + 4; + + regs->ARM_pc = rmv & ~0x1; + regs->ARM_cpsr = (regs->ARM_cpsr & ~PSR_T_BIT) | ((rmv & 0x1) << 5); +} + + +static void __kprobes simulate_ldm1stm1(struct kprobe *p, struct pt_regs *regs) +{ + insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + int rn = (insn >> 16) & 0xf; + int lbit = (insn >> 20) & 0x1; + int wbit = (insn >> 21) & 0x1; + int ubit = (insn >> 23) & 0x1; + int pbit = (insn >> 24) & 0x1; + long *addr = (long *)regs->uregs[rn]; + int reg_bit_vector; + int reg_count; + + if (!insnslot_1arg_rflags(0, regs->ARM_cpsr, i_fn)) + return; + + reg_count = 0; + reg_bit_vector = insn & 0xffff; + + while (reg_bit_vector) { + reg_bit_vector &= ~(1 << __ffs(reg_bit_vector)); + ++reg_count; + } + + if (!ubit) addr -= reg_count; + if (!(pbit ^ ubit)) addr += 1; + + reg_bit_vector = insn & 0xffff; + + while (reg_bit_vector) { + int reg = __ffs(reg_bit_vector); + reg_bit_vector &= ~(1 << reg); + if (lbit) { + regs->uregs[reg] = *addr++; + } else { + *addr++ = regs->uregs[reg]; + } + } + + if (wbit) { + if (!ubit) addr -= reg_count; + if (!(pbit ^ ubit)) addr -= 1; + regs->uregs[rn] = (long)addr; + } +} + + +static void __kprobes simulate_stm1_pc(struct kprobe *p, struct pt_regs *regs) +{ + insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0]; + + if (!insnslot_1arg_rflags(0, regs->ARM_cpsr, i_fn)) + return; + + regs->ARM_pc = (long)p->addr + kprobes_str_pc_offset; + simulate_ldm1stm1(p, regs); + regs->ARM_pc = (long)p->addr + 4; +} + + +static void __kprobes simulate_mov_ipsp(struct kprobe *p, struct pt_regs *regs) +{ + regs->uregs[12] = regs->uregs[13]; +} + + +static void __kprobes emulate_ldcstc(struct kprobe *p, struct pt_regs *regs) +{ + insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + int rn = (insn >> 16) & 0xf; + long rnv = regs->uregs[rn]; + + /* Save Rn in case of writeback. */ + regs->uregs[rn] = insnslot_1arg_rflags(rnv, regs->ARM_cpsr, i_fn); +} + + +static void __kprobes emulate_ldrd(struct kprobe *p, struct pt_regs *regs) +{ + insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + int rd = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; + int rm = insn & 0xf; + long rnv = regs->uregs[rn]; + long rmv = regs->uregs[rm]; /* rm/rmv may be invalid, don't care. */ + + /* Not following the C calling convention here, so need asm(). */ + __asm__("mov r0, %3 \n\t" + "mov r1, %4 \n\t" + "msr cpsr_fs, %5 \n\t" + "mov lr, pc \n\t" + "mov pc, %5 \n\t" + "str r0, %0 \n\t" /* Rn in case of writeback. */ + "str r2, %1 \n\t" /* Rd */ + "str r3, %2 \n\t" /* R(d+1) */ + : "=m" (regs->uregs[rn]), + "=m" (regs->uregs[rd]), + "=m" (regs->uregs[rd+1]) + : "r" (rnv), "r" (rmv), "r" (i_fn), "r" (regs->ARM_cpsr) + : "r0", "r1", "r2", "r3", "lr", "cc" + ); +} + + +static void __kprobes emulate_strd(struct kprobe *p, struct pt_regs *regs) +{ + insn_4arg_fn_t *i_fn = (insn_4arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + int rd = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; + int rm = insn & 0xf; + long rnv = regs->uregs[rn]; + long rmv = regs->uregs[rm]; /* rm/rmv may be invalid, don't care. */ + + regs->uregs[rn] = insnslot_4arg_rflags(rnv, rmv, regs->uregs[rd], + regs->uregs[rd+1], + regs->ARM_cpsr, i_fn); +} + + +static void __kprobes emulate_ldr(struct kprobe *p, struct pt_regs *regs) +{ + insn_llret_3arg_fn_t *i_fn = (insn_llret_3arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + reg_pair fnr; + int rd = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; + int rm = insn & 0xf; + long rdv; + long rnv = regs->uregs[rn]; + long rmv = regs->uregs[rm]; /* rm/rmv may be invalid, don't care. */ + long cpsr = regs->ARM_cpsr; + + fnr.dr = insnslot_llret_3arg_rflags(rnv, 0, rmv, cpsr, i_fn); + regs->uregs[rn] = fnr.r0; /* Save Rn in case of writeback. */ + rdv = fnr.r1; + + if (rd == 15) { +#if __LINUX_ARM_ARCH__ >= 5 + rdv &= ~0x1; + regs->ARM_cpsr = (cpsr & ~PSR_T_BIT) | ((rdv & 0x1) << 5); +#else + rdv &= ~0x2; +#endif + } + + regs->uregs[rd] = rdv; +} + + +static void __kprobes emulate_str(struct kprobe *p, struct pt_regs *regs) +{ + insn_3arg_fn_t *i_fn = (insn_3arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + long iaddr = (long)p->addr; + int rd = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; + int rm = insn & 0xf; + long rdv = (rd == 15) ? iaddr + kprobes_str_pc_offset : + regs->uregs[rd]; + long rnv = (rn == 15) ? iaddr + 8 : regs->uregs[rn]; + long rmv = regs->uregs[rm]; /* rm/rmv may be invalid, don't care. */ + + /* Save Rn in case of writeback. */ + regs->uregs[rn] = insnslot_3arg_rflags(rnv, rdv, rmv, + regs->ARM_cpsr, i_fn); +} + + +static void __kprobes emulate_mrrc(struct kprobe *p, struct pt_regs *regs) +{ + insn_llret_0arg_fn_t *i_fn = (insn_llret_0arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + reg_pair fnr; + int rd = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; + + fnr.dr = insnslot_llret_0arg_rflags(regs->ARM_cpsr, i_fn); + regs->uregs[rn] = fnr.r0; + regs->uregs[rd] = fnr.r1; +} + + +static void __kprobes emulate_mcrr(struct kprobe *p, struct pt_regs *regs) +{ + insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + int rd = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; + long rnv = regs->uregs[rn]; + long rdv = regs->uregs[rd]; + + insnslot_2arg_rflags(rnv, rdv, regs->ARM_cpsr, i_fn); +} + + +static void __kprobes emulate_sat(struct kprobe *p, struct pt_regs *regs) +{ + insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + int rd = (insn >> 12) & 0xf; + int rm = insn & 0xf; + long rmv = regs->uregs[rm]; + + /* Writes Q flag */ + regs->uregs[rd] = insnslot_1arg_rwflags(rmv, ®s->ARM_cpsr, i_fn); +} + + +static void __kprobes emulate_sel(struct kprobe *p, struct pt_regs *regs) +{ + insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + int rd = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; + int rm = insn & 0xf; + long rnv = regs->uregs[rn]; + long rmv = regs->uregs[rm]; + + /* Reads GE bits */ + regs->uregs[rd] = insnslot_2arg_rflags(rnv, rmv, regs->ARM_cpsr, i_fn); +} + + +static void __kprobes emulate_none(struct kprobe *p, struct pt_regs *regs) +{ + insn_0arg_fn_t *i_fn = (insn_0arg_fn_t *)&p->ainsn.insn[0]; + + insnslot_0arg_rflags(regs->ARM_cpsr, i_fn); +} + + +static void __kprobes emulate_rd12(struct kprobe *p, struct pt_regs *regs) +{ + insn_0arg_fn_t *i_fn = (insn_0arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + int rd = (insn >> 12) & 0xf; + + regs->uregs[rd] = insnslot_0arg_rflags(regs->ARM_cpsr, i_fn); +} + + +static void __kprobes emulate_ird12(struct kprobe *p, struct pt_regs *regs) +{ + insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + int ird = (insn >> 12) & 0xf; + + insnslot_1arg_rflags(regs->uregs[ird], regs->ARM_cpsr, i_fn); +} + + +static void __kprobes emulate_rn16(struct kprobe *p, struct pt_regs *regs) +{ + insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + int rn = (insn >> 16) & 0xf; + long rnv = regs->uregs[rn]; + + insnslot_1arg_rflags(rnv, regs->ARM_cpsr, i_fn); +} + + +static void __kprobes emulate_rd12rm0(struct kprobe *p, struct pt_regs *regs) +{ + insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + int rd = (insn >> 12) & 0xf; + int rm = insn & 0xf; + long rmv = regs->uregs[rm]; + + regs->uregs[rd] = insnslot_1arg_rflags(rmv, regs->ARM_cpsr, i_fn); +} + + +static void __kprobes emulate_rd12rn16rm0_rwflags(struct kprobe *p, + struct pt_regs *regs) +{ + insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + int rd = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; + int rm = insn & 0xf; + long rnv = regs->uregs[rn]; + long rmv = regs->uregs[rm]; + + regs->uregs[rd] = insnslot_2arg_rwflags(rnv, rmv, + ®s->ARM_cpsr, i_fn); +} + + +static void __kprobes emulate_rd16rn12rs8rm0_rwflags(struct kprobe *p, + struct pt_regs *regs) +{ + insn_3arg_fn_t *i_fn = (insn_3arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + int rd = (insn >> 16) & 0xf; + int rn = (insn >> 12) & 0xf; + int rs = (insn >> 8) & 0xf; + int rm = insn & 0xf; + long rnv = regs->uregs[rn]; + long rsv = regs->uregs[rs]; + long rmv = regs->uregs[rm]; + + regs->uregs[rd] = insnslot_3arg_rwflags(rnv, rsv, rmv, + ®s->ARM_cpsr, i_fn); +} + + +static void __kprobes emulate_rd16rs8rm0_rwflags(struct kprobe *p, + struct pt_regs *regs) +{ + insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + int rd = (insn >> 16) & 0xf; + int rs = (insn >> 8) & 0xf; + int rm = insn & 0xf; + long rsv = regs->uregs[rs]; + long rmv = regs->uregs[rm]; + + regs->uregs[rd] = insnslot_2arg_rwflags(rsv, rmv, + ®s->ARM_cpsr, i_fn); +} + + +static void __kprobes emulate_rdhi16rdlo12rs8rm0_rwflags(struct kprobe *p, + struct pt_regs *regs) +{ + insn_llret_4arg_fn_t *i_fn = (insn_llret_4arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + reg_pair fnr; + int rdhi = (insn >> 16) & 0xf; + int rdlo = (insn >> 12) & 0xf; + int rs = (insn >> 8) & 0xf; + int rm = insn & 0xf; + long rsv = regs->uregs[rs]; + long rmv = regs->uregs[rm]; + + fnr.dr = insnslot_llret_4arg_rwflags(regs->uregs[rdhi], + regs->uregs[rdlo], rsv, rmv, + ®s->ARM_cpsr, i_fn); + regs->uregs[rdhi] = fnr.r0; + regs->uregs[rdlo] = fnr.r1; +} + + +static void __kprobes emulate_alu_imm_rflags(struct kprobe *p, + struct pt_regs *regs) +{ + insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + int rd = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; + long rnv = (rn == 15) ? (long)p->addr + 8 : regs->uregs[rn]; + + regs->uregs[rd] = insnslot_1arg_rflags(rnv, regs->ARM_cpsr, i_fn); +} + + +static void __kprobes emulate_alu_imm_rwflags(struct kprobe *p, + struct pt_regs *regs) +{ + insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + int rd = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; + long rnv = (rn == 15) ? (long)p->addr + 8 : regs->uregs[rn]; + + regs->uregs[rd] = insnslot_1arg_rwflags(rnv, ®s->ARM_cpsr, i_fn); +} + + +static void __kprobes emulate_alu_rflags(struct kprobe *p, + struct pt_regs *regs) +{ + insn_3arg_fn_t *i_fn = (insn_3arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + long ppc = (long)p->addr + 8; + int rd = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; /* rn/rnv/rs/rsv may be */ + int rs = (insn >> 8) & 0xf; /* invalid, don't care. */ + int rm = insn & 0xf; + long rnv = (rn == 15) ? ppc : regs->uregs[rn]; + long rmv = (rm == 15) ? ppc : regs->uregs[rm]; + long rsv = regs->uregs[rs]; + + regs->uregs[rd] = insnslot_3arg_rflags(rnv, rmv, rsv, + regs->ARM_cpsr, i_fn); +} + + +static void __kprobes emulate_alu_rwflags(struct kprobe *p, + struct pt_regs *regs) +{ + insn_3arg_fn_t *i_fn = (insn_3arg_fn_t *)&p->ainsn.insn[0]; + kprobe_opcode_t insn = p->opcode; + long ppc = (long)p->addr + 8; + int rd = (insn >> 12) & 0xf; + int rn = (insn >> 16) & 0xf; /* rn/rnv/rs/rsv may be */ + int rs = (insn >> 8) & 0xf; /* invalid, don't care. */ + int rm = insn & 0xf; + long rnv = (rn == 15) ? ppc : regs->uregs[rn]; + long rmv = (rm == 15) ? ppc : regs->uregs[rm]; + long rsv = regs->uregs[rs]; + + regs->uregs[rd] = insnslot_3arg_rwflags(rnv, rmv, rsv, + ®s->ARM_cpsr, i_fn); +} + + +static enum kprobe_insn __kprobes prep_emulate_ldr_str(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + int ibit = (insn & (1 << 26)) ? 25 : 22; + + insn &= 0xfff00fff; + insn |= 0x00001000; /* Rn = r0, Rd = r1 */ + if (insn & (1 << ibit)) { + insn &= ~0xf; + insn |= 2; /* Rm = r2 */ + } + asi->insn[0] = insn; + asi->insn_handler = (insn & (1 << 20)) ? emulate_ldr : emulate_str; + return INSN_GOOD; +} + +static enum kprobe_insn __kprobes prep_emulate_rd12rm0(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + insn &= 0xffff0ff0; /* Rd = r0, Rm = r0 */ + asi->insn[0] = insn; + asi->insn_handler = emulate_rd12rm0; + return INSN_GOOD; +} + +static enum kprobe_insn __kprobes prep_emulate_rd12(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + insn &= 0xffff0fff; /* Rd = r0 */ + asi->insn[0] = insn; + asi->insn_handler = emulate_rd12; + return INSN_GOOD; +} + +static enum kprobe_insn __kprobes + prep_emulate_rd12rn16rm0_wflags(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + insn &= 0xfff00ff0; /* Rd = r0, Rn = r0 */ + insn |= 0x00000001; /* Rm = r1 */ + asi->insn[0] = insn; + asi->insn_handler = emulate_rd12rn16rm0_rwflags; + return INSN_GOOD; +} + +static enum kprobe_insn __kprobes + prep_emulate_rd16rs8rm0_wflags(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + insn &= 0xfff0f0f0; /* Rd = r0, Rs = r0 */ + insn |= 0x00000001; /* Rm = r1 */ + asi->insn[0] = insn; + asi->insn_handler = emulate_rd16rs8rm0_rwflags; + return INSN_GOOD; +} + +static enum kprobe_insn __kprobes + prep_emulate_rd16rn12rs8rm0_wflags(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + insn &= 0xfff000f0; /* Rd = r0, Rn = r0 */ + insn |= 0x00000102; /* Rs = r1, Rm = r2 */ + asi->insn[0] = insn; + asi->insn_handler = emulate_rd16rn12rs8rm0_rwflags; + return INSN_GOOD; +} + +static enum kprobe_insn __kprobes + prep_emulate_rdhi16rdlo12rs8rm0_wflags(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + insn &= 0xfff000f0; /* RdHi = r0, RdLo = r1 */ + insn |= 0x00001203; /* Rs = r2, Rm = r3 */ + asi->insn[0] = insn; + asi->insn_handler = emulate_rdhi16rdlo12rs8rm0_rwflags; + return INSN_GOOD; +} + + +/* + * For the instruction masking and comparisons in all the "space_*" + * functions below, Do _not_ rearrange the order of tests unless + * you're very, very sure of what you are doing. For the sake of + * efficiency, the masks for some tests sometimes assume other test + * have been done prior to them so the number of patterns to test + * for an instruction set can be as broad as possible to reduce the + * number of tests needed. + */ + +static enum kprobe_insn __kprobes space_1111(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + /* CPS mmod == 1 : 1111 0001 0000 xx10 xxxx xxxx xx0x xxxx + * RFE : 1111 100x x0x1 xxxx xxxx 1010 xxxx xxxx + * SRS : 1111 100x x1x0 1101 xxxx 0101 xxxx xxxx + */ + if ( (insn & 0xfff30020) == 0xf1020000 || + (insn & 0xfe500f00) == 0xf8100a00 || + (insn & 0xfe5f0f00) == 0xf84d0500 ) + return INSN_REJECTED; + + /* PLD : 1111 01x1 x101 xxxx xxxx xxxx xxxx xxxx : */ + if ( (insn & 0xfd700000) == 0xf4500000 ) { + insn &= 0xfff0ffff; /* Rn = r0 */ + + asi->insn[0] = insn; + asi->insn_handler = emulate_rn16; + return INSN_GOOD; + } + + /* BLX(1) : 1111 101x xxxx xxxx xxxx xxxx xxxx xxxx : */ + if ( (insn & 0xfe000000) == 0xfa000000 ) { + asi->insn_handler = simulate_blx1; + return INSN_GOOD_NO_SLOT; + } + + /* SETEND : 1111 0001 0000 0001 xxxx xxxx 0000 xxxx : + * CDP2 : 1111 1110 xxxx xxxx xxxx xxxx xxx0 xxxx : + */ + if ( (insn & 0xffff00f0) == 0xf1010000 || + (insn & 0xff000010) == 0xfe000000 ) { + asi->insn[0] = insn; + asi->insn_handler = emulate_none; + return INSN_GOOD; + } + + /* MCRR2 : 1111 1100 0100 xxxx xxxx xxxx xxxx xxxx : (Rd != Rn) + * MRRC2 : 1111 1100 0101 xxxx xxxx xxxx xxxx xxxx : (Rd != Rn) + */ + if ( (insn & 0xffe00000) == 0xfc400000 ) { + insn &= 0xfff00fff; /* Rn = r0 */ + insn |= 0x00001000; /* Rd = r1 */ + + asi->insn[0] = insn; + asi->insn_handler = (insn & (1 << 20)) ? + emulate_mrrc : emulate_mcrr; + return INSN_GOOD; + } + + /* LDC2 : 1111 110x xxx1 xxxx xxxx xxxx xxxx xxxx : + * STC2 : 1111 110x xxx0 xxxx xxxx xxxx xxxx xxxx : + */ + if ( (insn & 0xfe000000) == 0xfc000000 ) { + insn &= 0xfff0ffff; /* Rn = r0 */ + + asi->insn[0] = insn; + asi->insn_handler = emulate_ldcstc; + return INSN_GOOD; + } + + /* MCR2 : 1111 1110 xxx0 xxxx xxxx xxxx xxx1 xxxx : + * MRC2 : 1111 1110 xxx1 xxxx xxxx xxxx xxx1 xxxx : + */ + insn &= 0xffff0fff; /* Rd = r0 */ + + asi->insn[0] = insn; + asi->insn_handler = (insn & (1 << 20)) ? emulate_rd12 : emulate_ird12; + return INSN_GOOD; +} + + +static enum kprobe_insn __kprobes space_cccc_000x(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + if ( (insn & 0x0f900010) == 0x01000000 ) { + /* cccc 0001 0xx0 xxxx xxxx xxxx xxxx xxx0 xxxx */ + + /* BXJ : cccc 0001 0010 xxxx xxxx xxxx 0010 xxxx + * MSR : cccc 0001 0x10 xxxx xxxx xxxx 0000 xxxx + */ + if ( (insn & 0x0ff000f0) == 0x01200020 || + (insn & 0x0fb000f0) == 0x01200000 ) + return INSN_REJECTED; + + /* MRS : cccc 0001 0x00 xxxx xxxx xxxx 0000 xxxx : */ + if ( (insn & 0x0fb00010) == 0x01000000 ) + return prep_emulate_rd12(insn, asi); + + /* SMLALxy : cccc 0001 0100 xxxx xxxx xxxx 1xx0 xxxx : */ + if ( (insn & 0x0ff00090) == 0x01400080 ) + return prep_emulate_rdhi16rdlo12rs8rm0_wflags(insn, asi); + + /* SMULWy : cccc 0001 0010 xxxx xxxx xxxx 1x10 xxxx : + * SMULxy : cccc 0001 0110 xxxx xxxx xxxx 1xx0 xxxx : + */ + if ( (insn & 0x0ff000b0) == 0x012000a0 || + (insn & 0x0ff00090) == 0x01600080 ) + return prep_emulate_rd16rs8rm0_wflags(insn, asi); + + /* SMLAxy : cccc 0001 0000 xxxx xxxx xxxx 1xx0 xxxx :Q + * SMLAWy : cccc 0001 0010 xxxx xxxx xxxx 0x00 xxxx :Q + */ + return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi); + + } else if ( (insn & 0x0f900090) == 0x01000010 ) { + /* cccc 0001 0xx0 xxxx xxxx xxxx xxxx 0xx1 xxxx */ + + /* BKPT : 1110 0001 0010 xxxx xxxx xxxx 0111 xxxx */ + if ( (insn & 0xfff000f0) == 0xe1200070 ) + return INSN_REJECTED; + + /* BLX(2) : cccc 0001 0010 xxxx xxxx xxxx 0011 xxxx : + * BX : cccc 0001 0010 xxxx xxxx xxxx 0001 xxxx : + */ + + if ( (insn & 0x0ff000d0) == 0x01200010 ) { + asi->insn[0] = truecc_insn(insn); + asi->insn_handler = simulate_blx2bx; + return INSN_GOOD; + } + + /* CLZ : cccc 0001 0110 xxxx xxxx xxxx 0001 xxxx : */ + if ( (insn & 0x0ff000f0) == 0x01600010 ) + return prep_emulate_rd12rm0(insn, asi); + + /* QADD : cccc 0001 0000 xxxx xxxx xxxx 0101 xxxx :Q + * QSUB : cccc 0001 0010 xxxx xxxx xxxx 0101 xxxx :Q + * QDADD : cccc 0001 0100 xxxx xxxx xxxx 0101 xxxx :Q + * QDSUB : cccc 0001 0110 xxxx xxxx xxxx 0101 xxxx :Q + */ + return prep_emulate_rd12rn16rm0_wflags(insn, asi); + + } else if ( (insn & 0x0f000090) == 0x00000090 ) { + /* cccc 0000 xxxx xxxx xxxx xxxx xxxx 1001 xxxx */ + + /* MUL : cccc 0000 0000 xxxx xxxx xxxx 1001 xxxx : + * MULS : cccc 0000 0001 xxxx xxxx xxxx 1001 xxxx :cc + * MLA : cccc 0000 0010 xxxx xxxx xxxx 1001 xxxx : + * MLAS : cccc 0000 0011 xxxx xxxx xxxx 1001 xxxx :cc + * UMAAL : cccc 0000 0100 xxxx xxxx xxxx 1001 xxxx : + * UMULL : cccc 0000 1000 xxxx xxxx xxxx 1001 xxxx : + * UMULLS : cccc 0000 1001 xxxx xxxx xxxx 1001 xxxx :cc + * UMLAL : cccc 0000 1010 xxxx xxxx xxxx 1001 xxxx : + * UMLALS : cccc 0000 1011 xxxx xxxx xxxx 1001 xxxx :cc + * SMULL : cccc 0000 1100 xxxx xxxx xxxx 1001 xxxx : + * SMULLS : cccc 0000 1101 xxxx xxxx xxxx 1001 xxxx :cc + * SMLAL : cccc 0000 1110 xxxx xxxx xxxx 1001 xxxx : + * SMLALS : cccc 0000 1111 xxxx xxxx xxxx 1001 xxxx :cc + */ + + if ( (insn & 0x0fe000f0) == 0x00000090 ) { + return prep_emulate_rd16rs8rm0_wflags(insn, asi); + } else if ( (insn & 0x0fe000f0) == 0x00200090 ) { + return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi); + } else { + return prep_emulate_rdhi16rdlo12rs8rm0_wflags(insn, asi); + } + + } else if ( (insn & 0x0e000090) == 0x00000090 ) { + /* cccc 000x xxxx xxxx xxxx xxxx xxxx 1xx1 xxxx */ + + /* SWP : cccc 0001 0000 xxxx xxxx xxxx 1001 xxxx : + * SWPB : cccc 0001 0100 xxxx xxxx xxxx 1001 xxxx : + * LDRD : cccc 000x xxx0 xxxx xxxx xxxx 1101 xxxx : + * STRD : cccc 000x xxx0 xxxx xxxx xxxx 1111 xxxx : + * STREX : cccc 0001 1000 xxxx xxxx xxxx 1001 xxxx : + * LDREX : cccc 0001 1001 xxxx xxxx xxxx 1001 xxxx : + * LDRH : cccc 000x xxx1 xxxx xxxx xxxx 1011 xxxx : + * STRH : cccc 000x xxx0 xxxx xxxx xxxx 1011 xxxx : + * LDRSB : cccc 000x xxx1 xxxx xxxx xxxx 1101 xxxx : + * LDRSH : cccc 000x xxx1 xxxx xxxx xxxx 1111 xxxx : + */ + + if ( (insn & 0x0fb000f0) == 0x01000090 ) { /* SWP/SWPB */ + return prep_emulate_rd12rn16rm0_wflags(insn, asi); + } else if ( (insn & 0x0e1000d0) == 0x00000d0 ) { /* STRD/LDRD */ + insn &= 0xfff00fff; + insn |= 0x00002000; /* Rn = r0, Rd = r2 */ + + if (insn & (1 << 22)) { /* I bit */ + insn &= ~0xf; + insn |= 1; /* Rm = r1 */ + } + asi->insn[0] = insn; + asi->insn_handler = (insn & (1 << 5)) ? + emulate_strd : + emulate_ldrd; + return INSN_GOOD; + } + + return prep_emulate_ldr_str(insn, asi); + } + + /* cccc 000x xxxx xxxx xxxx xxxx xxxx xxxx xxxx */ + + /* ALU op with S bit and Rd == 15 : + * cccc 000x xxx1 xxxx 1111 xxxx xxxx xxxx + */ + if ( (insn & 0x0e10f000) == 0x0010f000 ) + return INSN_REJECTED; + + /* MOV r12,r13 is the most common kprobe'd instruction by far. + * Check and optimize for it explicitly. + */ + + if (insn == 0xe1a0c00d) { + asi->insn_handler = simulate_mov_ipsp; + return INSN_GOOD_NO_SLOT; + } + + /* Data processing: Immediate-shift / Register-shift + * ALU op : cccc 000x xxxx xxxx xxxx xxxx xxxx xxxx + * CPY : cccc 0001 1010 xxxx xxxx 0000 0000 xxxx + * MOV : cccc 0001 101x xxxx xxxx xxxx xxxx xxxx + * *S (bit 20) updates condition codes + * ADC/SBC/RSC reads the C flag + */ + + insn &= 0xfff00ff0; /* Rn = r0, Rd = r0 */ + insn |= 0x00000001; /* Rm = r1 */ + + if (insn & 0x010) { + insn &= 0xfffff0ff; /* register shift */ + insn |= 0x00000200; /* Rs = r2 */ + } + + asi->insn[0] = insn; + asi->insn_handler = emulate_alu_rwflags; + asi->insn_handler = (insn & (1 << 20)) ? /* S-bit */ + emulate_alu_rwflags : emulate_alu_rflags; + return INSN_GOOD; +} + + +static enum kprobe_insn __kprobes space_cccc_001x(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + /* MSR : cccc 0011 0x10 xxxx xxxx xxxx xxxx xxxx + * Undef : cccc 0011 0x00 xxxx xxxx xxxx xxxx xxxx + * ALU op with S bit and Rd == 15 : + * cccc 001x xxx1 xxxx 1111 xxxx xxxx xxxx + */ + + if ( (insn & 0x0f900000) == 0x03200000 || /* MSR & Undef */ + (insn & 0x0e10f000) == 0x0210f000 ) /* ALU s-bit, R15 */ + return INSN_REJECTED; + + /* Data processing: 32-bit Immediate + * ALU op : cccc 001x xxxx xxxx xxxx xxxx xxxx xxxx + * MOV : cccc 0011 101x xxxx xxxx xxxx xxxx xxxx + * *S (bit 20) updates condition codes + * ADC/SBC/RSC reads the C flag + */ + + insn &= 0xfff00ff0; /* Rn = r0, Rd = r0 */ + asi->insn[0] = insn; + asi->insn_handler = (insn & (1 << 20)) ? /* S-bit */ + emulate_alu_imm_rwflags : emulate_alu_imm_rflags; + return INSN_GOOD; +} + + +static enum kprobe_insn __kprobes space_cccc_0110__1(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + /* SEL : cccc 0110 1000 xxxx xxxx xxxx 1011 xxxx GE: !!! */ + + if ( (insn & 0x0ff000f0) == 0x068000b0 ) { + insn &= 0xfff00ff0; /* Rd = r0, Rn = r0 */ + insn |= 0x00000001; /* Rm = r1 */ + + asi->insn[0] = insn; + asi->insn_handler = emulate_sel; + return INSN_GOOD; + } + + /* SSAT : cccc 0110 101x xxxx xxxx xxxx xx01 xxxx :Q + * USAT : cccc 0110 111x xxxx xxxx xxxx xx01 xxxx :Q + * SSAT16 : cccc 0110 1010 xxxx xxxx xxxx 0011 xxxx :Q + * USAT16 : cccc 0110 1110 xxxx xxxx xxxx 0011 xxxx :Q + */ + + if ( (insn & 0x0fa00030) == 0x06a00010 || + (insn & 0x0fb000f0) == 0x06a00030 ) { + insn &= 0xffff0ff0; /* Rd = r0, Rm = r0 */ + + asi->insn[0] = insn; + asi->insn_handler = emulate_sat; + return INSN_GOOD; + } + + /* + * REV : cccc 0110 1011 xxxx xxxx xxxx 0011 xxxx : + * REV16 : cccc 0110 1011 xxxx xxxx xxxx 1011 xxxx : + * REVSH : cccc 0110 1111 xxxx xxxx xxxx 1011 xxxx : + */ + + if ( (insn & 0x0ff00070) == 0x06b00030 || + (insn & 0x0ff000f0) == 0x06f000b0 ) + return prep_emulate_rd12rm0(insn, asi); + + /* SADD16 : cccc 0110 0001 xxxx xxxx xxxx 0001 xxxx :GE + * SADDSUBX : cccc 0110 0001 xxxx xxxx xxxx 0011 xxxx :GE + * SSUBADDX : cccc 0110 0001 xxxx xxxx xxxx 0101 xxxx :GE + * SSUB16 : cccc 0110 0001 xxxx xxxx xxxx 0111 xxxx :GE + * SADD8 : cccc 0110 0001 xxxx xxxx xxxx 1001 xxxx :GE + * SSUB8 : cccc 0110 0001 xxxx xxxx xxxx 1111 xxxx :GE + * QADD16 : cccc 0110 0010 xxxx xxxx xxxx 0001 xxxx : + * QADDSUBX : cccc 0110 0010 xxxx xxxx xxxx 0011 xxxx : + * QSUBADDX : cccc 0110 0010 xxxx xxxx xxxx 0101 xxxx : + * QSUB16 : cccc 0110 0010 xxxx xxxx xxxx 0111 xxxx : + * QADD8 : cccc 0110 0010 xxxx xxxx xxxx 1001 xxxx : + * QSUB8 : cccc 0110 0010 xxxx xxxx xxxx 1111 xxxx : + * SHADD16 : cccc 0110 0011 xxxx xxxx xxxx 0001 xxxx : + * SHADDSUBX : cccc 0110 0011 xxxx xxxx xxxx 0011 xxxx : + * SHSUBADDX : cccc 0110 0011 xxxx xxxx xxxx 0101 xxxx : + * SHSUB16 : cccc 0110 0011 xxxx xxxx xxxx 0111 xxxx : + * SHADD8 : cccc 0110 0011 xxxx xxxx xxxx 1001 xxxx : + * SHSUB8 : cccc 0110 0011 xxxx xxxx xxxx 1111 xxxx : + * UADD16 : cccc 0110 0101 xxxx xxxx xxxx 0001 xxxx :GE + * UADDSUBX : cccc 0110 0101 xxxx xxxx xxxx 0011 xxxx :GE + * USUBADDX : cccc 0110 0101 xxxx xxxx xxxx 0101 xxxx :GE + * USUB16 : cccc 0110 0101 xxxx xxxx xxxx 0111 xxxx :GE + * UADD8 : cccc 0110 0101 xxxx xxxx xxxx 1001 xxxx :GE + * USUB8 : cccc 0110 0101 xxxx xxxx xxxx 1111 xxxx :GE + * UQADD16 : cccc 0110 0110 xxxx xxxx xxxx 0001 xxxx : + * UQADDSUBX : cccc 0110 0110 xxxx xxxx xxxx 0011 xxxx : + * UQSUBADDX : cccc 0110 0110 xxxx xxxx xxxx 0101 xxxx : + * UQSUB16 : cccc 0110 0110 xxxx xxxx xxxx 0111 xxxx : + * UQADD8 : cccc 0110 0110 xxxx xxxx xxxx 1001 xxxx : + * UQSUB8 : cccc 0110 0110 xxxx xxxx xxxx 1111 xxxx : + * UHADD16 : cccc 0110 0111 xxxx xxxx xxxx 0001 xxxx : + * UHADDSUBX : cccc 0110 0111 xxxx xxxx xxxx 0011 xxxx : + * UHSUBADDX : cccc 0110 0111 xxxx xxxx xxxx 0101 xxxx : + * UHSUB16 : cccc 0110 0111 xxxx xxxx xxxx 0111 xxxx : + * UHADD8 : cccc 0110 0111 xxxx xxxx xxxx 1001 xxxx : + * UHSUB8 : cccc 0110 0111 xxxx xxxx xxxx 1111 xxxx : + * PKHBT : cccc 0110 1000 xxxx xxxx xxxx x001 xxxx : + * PKHTB : cccc 0110 1000 xxxx xxxx xxxx x101 xxxx : + * SXTAB16 : cccc 0110 1000 xxxx xxxx xxxx 0111 xxxx : + * SXTB : cccc 0110 1010 xxxx xxxx xxxx 0111 xxxx : + * SXTAB : cccc 0110 1010 xxxx xxxx xxxx 0111 xxxx : + * SXTAH : cccc 0110 1011 xxxx xxxx xxxx 0111 xxxx : + * UXTAB16 : cccc 0110 1100 xxxx xxxx xxxx 0111 xxxx : + * UXTAB : cccc 0110 1110 xxxx xxxx xxxx 0111 xxxx : + * UXTAH : cccc 0110 1111 xxxx xxxx xxxx 0111 xxxx : + */ + + return prep_emulate_rd12rn16rm0_wflags(insn, asi); +} + +static enum kprobe_insn __kprobes space_cccc_0111__1(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + /* Undef : cccc 0111 1111 xxxx xxxx xxxx 1111 xxxx */ + if ( (insn & 0x0ff000f0) == 0x03f000f0 ) + return INSN_REJECTED; + + /* USADA8 : cccc 0111 1000 xxxx xxxx xxxx 0001 xxxx : + * USAD8 : cccc 0111 1000 xxxx 1111 xxxx 0001 xxxx : + */ + + if ( (insn & 0x0ff000f0) == 0x07800010 ) + return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi); + + /* SMLALD : cccc 0111 0100 xxxx xxxx xxxx 00x1 xxxx : + * SMLSLD : cccc 0111 0100 xxxx xxxx xxxx 01x1 xxxx : + */ + + if ( (insn & 0x0ff00090) == 0x07400010 ) + return prep_emulate_rdhi16rdlo12rs8rm0_wflags(insn, asi); + + /* SMLAD : cccc 0111 0000 xxxx xxxx xxxx 00x1 xxxx :Q + * SMLSD : cccc 0111 0000 xxxx xxxx xxxx 01x1 xxxx :Q + * SMMLA : cccc 0111 0101 xxxx xxxx xxxx 00x1 xxxx : + * SMMLS : cccc 0111 0101 xxxx xxxx xxxx 11x1 xxxx : + */ + + if ( (insn & 0x0ff00090) == 0x07000010 || + (insn & 0x0ff000d0) == 0x07500010 || + (insn & 0x0ff000d0) == 0x075000d0 ) + return prep_emulate_rd16rn12rs8rm0_wflags(insn, asi); + + /* SMUSD : cccc 0111 0000 xxxx xxxx xxxx 01x1 xxxx : + * SMUAD : cccc 0111 0000 xxxx 1111 xxxx 00x1 xxxx :Q + * SMMUL : cccc 0111 0101 xxxx 1111 xxxx 00x1 xxxx : + */ + + return prep_emulate_rd16rs8rm0_wflags(insn, asi); +} + + +static enum kprobe_insn __kprobes space_cccc_01xx(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + /* LDR : cccc 01xx x0x1 xxxx xxxx xxxx xxxx xxxx : + * LDRB : cccc 01xx x1x1 xxxx xxxx xxxx xxxx xxxx : + * LDRBT : cccc 01x0 x111 xxxx xxxx xxxx xxxx xxxx : + * LDRT : cccc 01x0 x011 xxxx xxxx xxxx xxxx xxxx : + * STR : cccc 01xx x0x0 xxxx xxxx xxxx xxxx xxxx : + * STRB : cccc 01xx x1x0 xxxx xxxx xxxx xxxx xxxx : + * STRBT : cccc 01x0 x110 xxxx xxxx xxxx xxxx xxxx : + * STRT : cccc 01x0 x010 xxxx xxxx xxxx xxxx xxxx : + */ + + return prep_emulate_ldr_str(insn, asi); +} + + +static enum kprobe_insn __kprobes space_cccc_100x(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + /* LDM(2) : cccc 100x x101 xxxx 0xxx xxxx xxxx xxxx + * LDM(3) : cccc 100x x1x1 xxxx 1xxx xxxx xxxx xxxx + */ + + if ( (insn & 0x0e708000) == 0x85000000 || + (insn & 0x0e508000) == 0x85010000 ) + return INSN_REJECTED; + + /* LDM(1) : cccc 100x x0x1 xxxx xxxx xxxx xxxx xxxx : + * STM(1) : cccc 100x x0x0 xxxx xxxx xxxx xxxx xxxx : + */ + + asi->insn[0] = truecc_insn(insn); + asi->insn_handler = ((insn & 0x108000) == 0x008000) ? /* STM & R15 */ + simulate_stm1_pc : simulate_ldm1stm1; + + return INSN_GOOD; +} + + +static enum kprobe_insn __kprobes space_cccc_101x(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + /* B : cccc 1010 xxxx xxxx xxxx xxxx xxxx xxxx : + * BL : cccc 1011 xxxx xxxx xxxx xxxx xxxx xxxx : + */ + + asi->insn[0] = truecc_insn(insn); + asi->insn_handler = simulate_bbl; + + return INSN_GOOD; +} + + +static enum kprobe_insn __kprobes space_cccc_1100_010x(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + /* MCRR : cccc 1100 0100 xxxx xxxx xxxx xxxx xxxx : (Rd!=Rn) + * MRRC : cccc 1100 0101 xxxx xxxx xxxx xxxx xxxx : (Rd!=Rn) + */ + insn &= 0xfff00fff; + insn |= 0x00001000; /* Rn = r0, Rd = r1 */ + asi->insn[0] = insn; + asi->insn_handler = (insn & (1 << 20)) ? emulate_mrrc : emulate_mcrr; + return INSN_GOOD; +} + + +static enum kprobe_insn __kprobes space_cccc_110x(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + /* LDC : cccc 110x xxx1 xxxx xxxx xxxx xxxx xxxx : + * STC : cccc 110x xxx0 xxxx xxxx xxxx xxxx xxxx : + */ + insn &= 0xfff0ffff; /* Rn = r0 */ + asi->insn[0] = insn; + asi->insn_handler = emulate_ldcstc; + + return INSN_GOOD; +} + + +static enum kprobe_insn __kprobes space_cccc_111x(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + /* BKPT : 1110 0001 0010 xxxx xxxx xxxx 0111 xxxx + * SWI : cccc 1111 xxxx xxxx xxxx xxxx xxxx xxxx + */ + if ( (insn & 0xfff000f0) == 0xe1200070 || + (insn & 0x0f000000) == 0x0f000000 ) + return INSN_REJECTED; + + /* CDP : cccc 1110 xxxx xxxx xxxx xxxx xxx0 xxxx : */ + if ( (insn & 0x0f000010) == 0x0e000000 ) { + asi->insn[0] = insn; + asi->insn_handler = emulate_none; + return INSN_GOOD; + } + + /* MCR : cccc 1110 xxx0 xxxx xxxx xxxx xxx1 xxxx : + * MRC : cccc 1110 xxx1 xxxx xxxx xxxx xxx1 xxxx : + */ + insn &= 0xffff0fff; /* Rd = r0 */ + asi->insn[0] = insn; + asi->insn_handler = (insn & (1 << 20)) ? emulate_rd12 : emulate_ird12; + return INSN_GOOD; +} + + +/* Return: + * INSN_REJECTED If instruction is one not allowed to kprobe, + * INSN_GOOD If instruction is supported and uses instruction slot, + * INSN_GOOD_NO_SLOT If instruction is supported but doesn't use its slot. + * + * For instructions we don't want to kprobe (INSN_REJECTED return result): + * These are generally ones that modify the processor state making + * them "hard" to simulate such as switches processor modes or + * make accesses in alternate modes. Any of these could be simulated + * if the work was put into it, but low return considering they + * should also be very rare. + */ + +enum kprobe_insn __kprobes kprobe_decode_insn(kprobe_opcode_t insn, + struct arch_specific_insn *asi) +{ + asi->insn[1] = KPROBE_RETURN_INSTRUCTION; + + if ( (insn & 0xf0000000) == 0xf0000000 ) { + + return space_1111(insn, asi); + + } else if ( (insn & 0x0e000000) == 0x00000000 ) { + + return space_cccc_000x(insn, asi); + + } else if ( (insn & 0x0e000000) == 0x02000000 ) { + + return space_cccc_001x(insn, asi); + + } else if ( (insn & 0x0f000010) == 0x06000010 ) { + + return space_cccc_0110__1(insn, asi); + + } else if ( (insn & 0x0f000010) == 0x07000010 ) { + + return space_cccc_0111__1(insn, asi); + + } else if ( (insn & 0x0c000000) == 0x04000000 ) { + + return space_cccc_01xx(insn, asi); + + } else if ( (insn & 0x0e000000) == 0x08000000 ) { + + return space_cccc_100x(insn, asi); + + } else if ( (insn & 0x0e000000) == 0x0a000000 ) { + + return space_cccc_101x(insn, asi); + + } else if ( (insn & 0x0fe00000) == 0x0c400000 ) { + + return space_cccc_1100_010x(insn, asi); + + } else if ( (insn & 0x0e000000) == 0x0c400000 ) { + + return space_cccc_110x(insn, asi); + + } + + return space_cccc_111x(insn, asi); +} + + +/* + * All ARM instructions listed below. + * + * Instructions and their general purpose registers are given. + * If a particular register may not use R15, it it prefixed with a "!". + * If it is parked with a "*" means the value returned by readering R15 + * is implementation defined. + * + * ADC/ADD/AND/BIC/CMN/CMP/EOR/MOV/MVN/ORR/RSB/RSC/SBC/SUB/TEQ + * TST: Rd, Rn, Rm, !Rs + * BX: Rm + * BLX(2): !Rm + * BX: Rm (R15 legal, but discouraged) + * BXJ: !Rm, + * CLZ: !Rd, !Rm + * CPY: Rd, Rm + * LDC/2,STC/2 immediate offset & unindex: Rn + * LDC/2,STC/2 immediate pre/post-indexed: !Rn + * LDM(1/3): !Rn, register_list + * LDM(2): !Rn, !register_list + * LDR,STR,PLD immediate offset: Rd, Rn + * LDR,STR,PLD register offset: Rd, Rn, !Rm + * LDR,STR,PLD scaled register offset: Rd, !Rn, !Rm + * LDR,STR immediate pre/post-indexed: Rd, !Rn + * LDR,STR register pre/post-indexed: Rd, !Rn, !Rm + * LDR,STR scaled register pre/post-indexed: Rd, !Rn, !Rm + * LDRB,STRB immediate offset: !Rd, Rn + * LDRB,STRB register offset: !Rd, Rn, !Rm + * LDRB,STRB scaled register offset: !Rd, !Rn, !Rm + * LDRB,STRB immediate pre/post-indexed: !Rd, !Rn + * LDRB,STRB register pre/post-indexed: !Rd, !Rn, !Rm + * LDRB,STRB scaled register pre/post-indexed: !Rd, !Rn, !Rm + * LDRT,LDRBT,STRBT immediate pre/post-indexed: !Rd, !Rn + * LDRT,LDRBT,STRBT register pre/post-indexed: !Rd, !Rn, !Rm + * LDRT,LDRBT,STRBT scaled register pre/post-indexed: !Rd, !Rn, !Rm + * LDRH/SH/SB/D,STRH/SH/SB/D immediate offset: !Rd, Rn + * LDRH/SH/SB/D,STRH/SH/SB/D register offset: !Rd, Rn, !Rm + * LDRH/SH/SB/D,STRH/SH/SB/D immediate pre/post-indexed: !Rd, !Rn + * LDRH/SH/SB/D,STRH/SH/SB/D register pre/post-indexed: !Rd, !Rn, !Rm + * LDREX: !Rd, !Rn + * MCR/2: !Rd + * MCRR/2,MRRC/2: !Rd, !Rn + * MLA: !Rd, !Rn, !Rm, !Rs + * MOV: Rd + * MRC/2: !Rd (if Rd==15, only changes cond codes, not the register) + * MRS,MSR: !Rd + * MUL: !Rd, !Rm, !Rs + * PKH{BT,TB}: !Rd, !Rn, !Rm + * QDADD,[U]QADD/16/8/SUBX: !Rd, !Rm, !Rn + * QDSUB,[U]QSUB/16/8/ADDX: !Rd, !Rm, !Rn + * REV/16/SH: !Rd, !Rm + * RFE: !Rn + * {S,U}[H]ADD{16,8,SUBX},{S,U}[H]SUB{16,8,ADDX}: !Rd, !Rn, !Rm + * SEL: !Rd, !Rn, !Rm + * SMLA,SMLA{D,W},SMLSD,SMML{A,S}: !Rd, !Rn, !Rm, !Rs + * SMLAL,SMLA{D,LD},SMLSLD,SMMULL,SMULW: !RdHi, !RdLo, !Rm, !Rs + * SMMUL,SMUAD,SMUL,SMUSD: !Rd, !Rm, !Rs + * SSAT/16: !Rd, !Rm + * STM(1/2): !Rn, register_list* (R15 in reg list not recommended) + * STRT immediate pre/post-indexed: Rd*, !Rn + * STRT register pre/post-indexed: Rd*, !Rn, !Rm + * STRT scaled register pre/post-indexed: Rd*, !Rn, !Rm + * STREX: !Rd, !Rn, !Rm + * SWP/B: !Rd, !Rn, !Rm + * {S,U}XTA{B,B16,H}: !Rd, !Rn, !Rm + * {S,U}XT{B,B16,H}: !Rd, !Rm + * UM{AA,LA,UL}L: !RdHi, !RdLo, !Rm, !Rs + * USA{D8,A8,T,T16}: !Rd, !Rm, !Rs + * + * May transfer control by writing R15 (possible mode changes or alternate + * mode accesses marked by "*"): + * ALU op (* with s-bit), B, BL, BKPT, BLX(1/2), BX, BXJ, CPS*, CPY, + * LDM(1), LDM(2/3)*, LDR, MOV, RFE*, SWI* + * + * Instructions that do not take general registers, nor transfer control: + * CDP/2, SETEND, SRS* + */ diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/arch/arm/kernel/kprobes.c kernel-source-diablo-2.6.21/kernel-source/arch/arm/kernel/kprobes.c --- kernel-source-diablo-2.6.21.orig/kernel-source/arch/arm/kernel/kprobes.c 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/arch/arm/kernel/kprobes.c 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,535 @@ +/* arch/arm/kernel/kprobes.c + * + * Kprobes on ARM + * + * Abhishek Sagar + * + * Copyright (C) 2006, 2007 Motorola Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define flush_insns(addr, cnt) \ + flush_icache_range((unsigned long)(addr), \ + (unsigned long)(addr) + \ + sizeof(kprobe_opcode_t) * (cnt)) + +/* Used as a marker in ARM_pc to note when we're in a jprobe. */ +#define JPROBE_MAGIC_ADDR 0xffffffff + +void jprobe_return_end(void); +void kretprobe_trampoline(void); + +DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; +DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); + +int kprobes_str_pc_offset; + + +int __kprobes arch_prepare_kprobe(struct kprobe *p) +{ + kprobe_opcode_t insn; + kprobe_opcode_t tmp_insn[MAX_INSN_SIZE]; + int is; + + if ((unsigned long)p->addr & 0x3) + return -EINVAL; + + insn = *p->addr; + + p->opcode = insn; + p->ainsn.insn = tmp_insn; + + switch (kprobe_decode_insn(insn, &p->ainsn)) { + case INSN_REJECTED: /* not supported */ + return -EINVAL; + + case INSN_GOOD: /* instruction uses slot */ + p->ainsn.insn = get_insn_slot(); + if (!p->ainsn.insn) + return -ENOMEM; + + for (is = 0; is < MAX_INSN_SIZE; ++is) + p->ainsn.insn[is] = tmp_insn[is]; + + flush_insns(&p->ainsn.insn, MAX_INSN_SIZE); + break; + + case INSN_GOOD_NO_SLOT: /* instruction doesn't need insn slot */ + p->ainsn.insn = 0; + break; + } + + return 0; +} + + +void __kprobes arch_arm_kprobe(struct kprobe *p) +{ + *p->addr = KPROBE_BREAKPOINT_INSTRUCTION; + flush_insns(p->addr, 1); +} + + +void __kprobes arch_disarm_kprobe(struct kprobe *p) +{ + *p->addr = p->opcode; + flush_insns(p->addr, 1); +} + + +void __kprobes arch_remove_kprobe(struct kprobe *p) +{ + if (p->ainsn.insn) { + mutex_lock(&kprobe_mutex); + free_insn_slot(p->ainsn.insn, 0); + mutex_unlock(&kprobe_mutex); + p->ainsn.insn = 0; + } +} + +static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb) +{ + kcb->prev_kprobe.kp = kprobe_running(); + kcb->prev_kprobe.status = kcb->kprobe_status; +} + + +static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb) +{ + __get_cpu_var(current_kprobe) = kcb->prev_kprobe.kp; + kcb->kprobe_status = kcb->prev_kprobe.status; +} + + +static void __kprobes set_current_kprobe(struct kprobe *p) +{ + __get_cpu_var(current_kprobe) = p; +} + + +/* Called with kretprobe_lock held. */ +void __kprobes arch_prepare_kretprobe(struct kretprobe *rp, + struct pt_regs *regs) +{ + struct kretprobe_instance *ri; + + ri = get_free_rp_inst(rp); + if (ri != NULL) { + ri->rp = rp; + ri->task = current; + ri->ret_addr = (kprobe_opcode_t *)regs->ARM_lr; + + /* Replace the return addr with trampoline addr. */ + regs->ARM_lr = (unsigned long)&kretprobe_trampoline; + add_rp_inst(ri); + } else { + rp->nmissed++; + } +} + + +static void __kprobes singlestep(struct kprobe *p, struct pt_regs *regs, + struct kprobe_ctlblk *kcb) +{ + regs->ARM_pc += 4; + + p->ainsn.insn_handler(p, regs); + + if ((kcb->kprobe_status != KPROBE_REENTER) && p->post_handler) { + kcb->kprobe_status = KPROBE_HIT_SSDONE; + p->post_handler(p, regs, 0); + } + + if (kcb->kprobe_status == KPROBE_REENTER) { + restore_previous_kprobe(kcb); + } else { + reset_current_kprobe(); + } +} + + +/* IRQs were automatically disabled when executing the kprobe + * instruction. IRQs must remain disabled from that point all the + * way until processing this kprobe is complete. The current kprobes + * implementation cannot process more than one nested level of kprobe, + * and that level is reserved for user kprobe handlers, so we can't + * risk encountering a new kprobe in an interrupt handler. + */ + +int __kprobes kprobe_handler(struct pt_regs *regs) +{ + struct kprobe *p, *cur; + struct kprobe_ctlblk *kcb; + kprobe_opcode_t *addr = (kprobe_opcode_t *)regs->ARM_pc; + + kcb = get_kprobe_ctlblk(); + cur = kprobe_running(); + p = get_kprobe(addr); + + if (p) { + if (cur) { /* Kprobe is pending, so we're recursing. */ + switch (kcb->kprobe_status) { + case KPROBE_HIT_ACTIVE: + case KPROBE_HIT_SSDONE: + /* A pre- or post-handler probe got us here. */ + kprobes_inc_nmissed_count(p); + save_previous_kprobe(kcb); + set_current_kprobe(p); + kcb->kprobe_status = KPROBE_REENTER; + singlestep(p, regs, kcb); + break; + default: + /* impossible cases */ + BUG(); + } + } else { + set_current_kprobe(p); + kcb->kprobe_status = KPROBE_HIT_ACTIVE; + + /* If we have no pre-handler or it returned 0, we + * continue with normal processing. If we have a + * pre-handler and it returned non-zero, it prepped + * for calling the break_handler below on re-entry, + * so get out doing nothing more here. + */ + + if (!p->pre_handler || !p->pre_handler(p, regs)) { + kcb->kprobe_status = KPROBE_HIT_SS; + singlestep(p, regs, kcb); + } + } + } else { + /* Two ways we could have gotten here. Either the probe + * was removed and a race is in progress or we hit a + * jprobe. + * + * If it's a race, nothing we can do about it. Restart + * the instruction. By the time we can restart, the + * real instruction will be there. If we're in a jprobe + * call its break handler. + */ + + if (cur) { + if ( (addr == (kprobe_opcode_t *)JPROBE_MAGIC_ADDR) && + cur->break_handler && + cur->break_handler(cur, regs) ) { + kcb->kprobe_status = KPROBE_HIT_SS; + singlestep(cur, regs, kcb); + } else { + reset_current_kprobe(); + } + } + } + + return 1; +} + + +static int __kprobes kprobe_fault_handler(struct pt_regs *regs, + unsigned int fsr) +{ + struct kprobe *cur = kprobe_running(); + struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); + + switch (kcb->kprobe_status) { + case KPROBE_HIT_SS: + case KPROBE_REENTER: + /* + * We are here because the instruction being single + * stepped caused a page fault. We reset the current + * kprobe and the PC to point back to the probe address + * and allow the page fault handler to continue as a + * normal page fault. + */ + regs->ARM_pc = (long)cur->addr; + if (kcb->kprobe_status == KPROBE_REENTER) { + restore_previous_kprobe(kcb); + } else { + reset_current_kprobe(); + } + break; + + case KPROBE_HIT_ACTIVE: + case KPROBE_HIT_SSDONE: + /* + * We increment the nmissed count for accounting, + * we can also use npre/npostfault count for accounting + * these specific fault cases. + */ + kprobes_inc_nmissed_count(cur); + + /* + * We come here because instructions in the pre/post + * handler caused the page_fault, this could happen + * if handler tries to access user space by + * copy_from_user(), get_user() etc. Let the + * user-specified handler try to fix it first. + */ + if (cur->fault_handler && cur->fault_handler(cur, regs, fsr)) + return 1; + + /* + * In case the user-specified fault handler returned + * zero, try to fix up. + */ + + if (fixup_exception(regs)) + return 1; + + /* + * fixup_exception() could not handle it, + * Let do_page_fault() fix it. + */ + break; + + default: + break; + } + + return 0; +} + + +/* + * Wrapper routine to for handling exceptions. + */ +int __kprobes kprobe_exceptions_notify(struct notifier_block *self, + unsigned long val, void *data) +{ + struct die_args *args = (struct die_args *)data; + struct pt_regs *regs = args->regs; + int ret = NOTIFY_DONE; + + /* This function is in a very critical path. If in user + * mode, get out as quick as we can. */ + if (!regs || user_mode(regs)) + return ret; + + switch (val) { + case DIE_TRANS_FAULT: + case DIE_PAGE_FAULT: + /* To be potentially processing a kprobe fault and to + * trust the result from kprobe_running(), we have + * be non-preemptible. */ + if (!preemptible() && + kprobe_running() && + kprobe_fault_handler(regs, args->fsr)) { + ret = NOTIFY_STOP; + } + break; + default: + break; + } + + return ret; +} + + +/* + * When a retprobed function returns, trampoline_handler() is called, + * calling the kretprobe's handler. We contruct a struct pt_regs to + * give a view of registers r0-r11 to the user return-handler. + */ +void __attribute__((naked)) __kprobes kretprobe_trampoline_holder(void) +{ + __asm__ volatile ( + ".global kretprobe_trampoline \n\t" + "kretprobe_trampoline: \n\t" + "stmdb sp!, {r0-r11} \n\t" + "mov r0, sp \n\t" + "bl trampoline_handler \n\t" + "mov lr, r0 \n\t" + "ldmia sp!, {r0-r11} \n\t" + "mov pc, lr \n\t" + : + : + :"memory"); +} + + +/* + * Called from kretprobe_trampoline_holder + */ +void *__kprobes trampoline_handler(struct pt_regs *regs) +{ + struct kretprobe_instance *ri = NULL; + struct hlist_head *head, empty_rp; + struct hlist_node *node, *tmp; + unsigned long flags, orig_ret_address = 0; + unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline; + + INIT_HLIST_HEAD(&empty_rp); + spin_lock_irqsave(&kretprobe_lock, flags); + head = kretprobe_inst_table_head(current); + + /* + * It is possible to have multiple instances associated with a given + * task either because multiple functions in the call path have + * a return probe installed on them, and/or more then one return + * return probe was registered for a target function. + * + * We can handle this because: + * - instances are always inserted at the head of the list + * - when multiple return probes are registered for the same + * function, the first instance's ret_addr will point to the + * real return address, and all the rest will point to + * kretprobe_trampoline + */ + hlist_for_each_entry_safe(ri, node, tmp, head, hlist) { + if (ri->task != current) + /* another task is sharing our hash bucket */ + continue; + + if (ri->rp && ri->rp->handler) { + __get_cpu_var(current_kprobe) = &ri->rp->kp; + get_kprobe_ctlblk()->kprobe_status = KPROBE_HIT_ACTIVE; + ri->rp->handler(ri, regs); + __get_cpu_var(current_kprobe) = NULL; + } + + orig_ret_address = (unsigned long)ri->ret_addr; + recycle_rp_inst(ri, &empty_rp); + + if (orig_ret_address != trampoline_address) + /* + * This is the real return address. Any other + * instances associated with this task are for + * other calls deeper on the call stack + */ + break; + } + + BUG_ON(!orig_ret_address || (orig_ret_address == trampoline_address)); + + spin_unlock_irqrestore(&kretprobe_lock, flags); + + hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) { + hlist_del(&ri->hlist); + kfree(ri); + } + + return (void *)orig_ret_address; +} + + +int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) +{ + struct jprobe *jp = container_of(p, struct jprobe, kp); + struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); + long sp_addr = regs->ARM_sp; + + kcb->jprobe_saved_regs = *regs; + memcpy(kcb->jprobes_stack, (void *)sp_addr, MIN_STACK_SIZE(sp_addr)); + regs->ARM_pc = (long)jp->entry; + regs->ARM_cpsr |= PSR_I_BIT; + preempt_disable(); + return 1; +} + + +#define str(s) #s +#define xstr(s) str(s) + +void __kprobes jprobe_return(void) +{ + struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); + + __asm__ volatile( + /* Setup an empty pt_regs. Fill SP and PC fields as + * they're needed by longjmp_break_handler. + */ + "sub sp, %0, %1 \n\t" + "ldr r0, =" xstr(JPROBE_MAGIC_ADDR) "\n\t" + "str r0, [sp, %3] \n\t" + "str %0, [sp, %2] \n\t" + + "mov r0, sp \n\t" + "bl kprobe_handler \n\t" + + /* Return to the context saved by setjmp_pre_handler + * and restored by longjmp_break_handler. */ + "ldr r0, [sp, %4] \n\t" + "msr cpsr_cxsf, r0 \n\t" + "ldmia sp, {r0-pc} \n\t" + : + : "r" (kcb->jprobe_saved_regs.ARM_sp), + "I"(sizeof(struct pt_regs)), + "J"(offsetof(struct pt_regs, ARM_sp)), + "J"(offsetof(struct pt_regs, ARM_pc)), + "J"(offsetof(struct pt_regs, ARM_cpsr)) + : "memory", "cc"); +} + +int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) +{ + struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); + long stack_addr = kcb->jprobe_saved_regs.ARM_sp; + long orig_sp = regs->ARM_sp; + struct jprobe *jp = container_of(p, struct jprobe, kp); + + if (orig_sp != stack_addr) { + struct pt_regs *saved_regs = + (struct pt_regs *)kcb->jprobe_saved_regs.ARM_sp; + printk("current sp %lx does not match saved sp %lx\n", + orig_sp, stack_addr); + printk("Saved registers for jprobe %p\n", jp); + show_regs(saved_regs); + printk("Current registers\n"); + show_regs(regs); + BUG(); + } + + *regs = kcb->jprobe_saved_regs; + memcpy((void *)stack_addr, kcb->jprobes_stack, + MIN_STACK_SIZE(stack_addr)); + preempt_enable_no_resched(); + + return 1; +} + + +/* + * For STR and STM instructions, an ARM core may choose to use either + * a +8 or a +12 displacement from the current instruction's address. + * Whichever value is chosen for a given core, it must be the same for + * both instructions and may not change. This function measures it. + */ + +static int __init find_str_pc_offset(void) +{ + int addr; + int scratch; + int ret; + + __asm__("sub %[ret], pc, #4 \n\t" + "str pc, %[addr] \n\t" + "ldr %[scr], %[addr] \n\t" + "sub %[ret], %[scr], %[ret] \n\t" + : [ret] "=r" (ret), [scr] "=r" (scratch), [addr] "+m" (addr) ); + + return ret; +} + +int __init arch_init_kprobes() +{ + kprobes_str_pc_offset = find_str_pc_offset(); + return 0; +} diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/arch/arm/kernel/traps.c kernel-source-diablo-2.6.21/kernel-source/arch/arm/kernel/traps.c --- kernel-source-diablo-2.6.21.orig/kernel-source/arch/arm/kernel/traps.c 2007-10-02 08:09:30.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/arch/arm/kernel/traps.c 2009-07-01 17:29:10.000000000 +0100 @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -266,7 +267,7 @@ void unregister_undef_hook(struct undef_ spin_unlock_irqrestore(&undef_lock, flags); } -asmlinkage void do_undefinstr(struct pt_regs *regs) +asmlinkage void __kprobes do_undefinstr(struct pt_regs *regs) { unsigned int correction = thumb_mode(regs) ? 2 : 4; unsigned int instr; @@ -281,6 +282,19 @@ asmlinkage void do_undefinstr(struct pt_ */ regs->ARM_pc -= correction; +#ifdef CONFIG_KPROBES + /* + * Redirect kernel mode undef exceptions to kprobes. + */ + if (!user_mode(regs) && !thumb_mode(regs)) { + if ( (*(unsigned long *)regs->ARM_pc == + KPROBE_BREAKPOINT_INSTRUCTION) && + kprobe_handler(regs) ) { + return; + } + } +#endif /* CONFIG_KPROBES */ + pc = (void __user *)instruction_pointer(regs); if (processor_mode(regs) == SVC_MODE) { diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/arch/arm/kernel/vmlinux.lds.S kernel-source-diablo-2.6.21/kernel-source/arch/arm/kernel/vmlinux.lds.S --- kernel-source-diablo-2.6.21.orig/kernel-source/arch/arm/kernel/vmlinux.lds.S 2007-10-02 08:09:30.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/arch/arm/kernel/vmlinux.lds.S 2009-07-01 17:29:10.000000000 +0100 @@ -86,6 +86,7 @@ SECTIONS *(.text) SCHED_TEXT LOCK_TEXT + KPROBES_TEXT #ifdef CONFIG_MMU *(.fixup) #endif diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/arch/arm/mach-omap2/board-n800-dvfs.c kernel-source-diablo-2.6.21/kernel-source/arch/arm/mach-omap2/board-n800-dvfs.c --- kernel-source-diablo-2.6.21.orig/kernel-source/arch/arm/mach-omap2/board-n800-dvfs.c 2009-07-01 17:15:49.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/arch/arm/mach-omap2/board-n800-dvfs.c 2009-07-01 17:29:10.000000000 +0100 @@ -41,11 +41,12 @@ enum op_indexes { OP_3, NUM_OP, DEFAULT_OP = OP_1, - OP_DSP_H = OP_1, OP_CPU_H = OP_0, OP_CPU_ONDEMAND = 255, }; +static unsigned int op_dsp_h = OP_1; + static unsigned int op_list[NUM_OP][NUM_OP_PARMS] = { [OP_0] = { [_54M_SRC_CLK_KHZ] = 54000, @@ -177,9 +178,9 @@ static int update_op(enum dvfs_op_policy } if (op_policy.task) - new_op = OP_DSP_H; + new_op = op_dsp_h; else if (op_policy.cam) - new_op = OP_DSP_H; + new_op = op_dsp_h; else if (op_policy.path) new_op = OP_CPU_H; else @@ -266,8 +267,47 @@ static struct notifier_block dsp_state_c .notifier_call = op_policy_notifier, }; +static ssize_t op_dsp_show(struct subsystem * subsys, char *buf) +{ + return sprintf(buf, "%u\n", op_dsp_h); +} + +static ssize_t op_dsp_store(struct subsystem * subsys, + const char * buf, size_t n) +{ + unsigned value; + + if (sscanf(buf, "%u", &value) != 1 || (value > OP_1)) { + printk(KERN_ERR "OP: Invalid index\n"); + return -EINVAL; + } + + mutex_lock(&op_policy.lock); + if (op_policy.task) { + n = -EBUSY; + } else { + op_dsp_h = value; + } + mutex_unlock(&op_policy.lock); + + return n; +} + +static struct subsys_attribute op_dsp_attr = { + .attr = { + .name = __stringify(op_dsp), + .mode = 0644, + }, + .show = op_dsp_show, + .store = op_dsp_store, +}; + +extern struct subsystem power_subsys; + int __init n800_dvfs_init(void) { + int ret; + mutex_init(&op_policy.lock); op_policy.dvfs_wq = create_workqueue("dvfs"); INIT_WORK(&op_policy.update_policy_work, update_policy_work); @@ -278,5 +318,10 @@ int __init n800_dvfs_init(void) NUM_OP, DEFAULT_OP); cpufreq_register_notifier(&dsp_state_change_block, CPUFREQ_POLICY_NOTIFIER); + + ret = subsys_create_file(&power_subsys, &op_dsp_attr); + if (ret) + printk(KERN_ERR "subsys_create_file failed: %d\n", ret); + return 0; } diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/arch/arm/mach-omap2/board-n800-mmc.c kernel-source-diablo-2.6.21/kernel-source/arch/arm/mach-omap2/board-n800-mmc.c --- kernel-source-diablo-2.6.21.orig/kernel-source/arch/arm/mach-omap2/board-n800-mmc.c 2009-07-01 17:15:49.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/arch/arm/mach-omap2/board-n800-mmc.c 2009-07-01 17:29:10.000000000 +0100 @@ -299,7 +299,6 @@ static struct omap_mmc_platform_data n80 .init = n800_mmc_late_init, .cleanup = n800_mmc_cleanup, .shutdown = n800_mmc_shutdown, - .max_freq = 24000000, .slots[0] = { .set_power = n800_mmc_set_power, .set_bus_mode = n800_mmc_set_bus_mode, diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/arch/arm/mm/fault.c kernel-source-diablo-2.6.21/kernel-source/arch/arm/mm/fault.c --- kernel-source-diablo-2.6.21.orig/kernel-source/arch/arm/mm/fault.c 2007-10-02 08:09:30.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/arch/arm/mm/fault.c 2009-07-01 17:29:10.000000000 +0100 @@ -13,11 +13,13 @@ #include #include #include +#include #include #include #include #include +#include #include "fault.h" @@ -73,6 +75,44 @@ void show_pte(struct mm_struct *mm, unsi printk("\n"); } +#ifdef CONFIG_KPROBES +ATOMIC_NOTIFIER_HEAD(notify_page_fault_chain); + +/* Hook to register for page fault notifications */ +int register_page_fault_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_register(¬ify_page_fault_chain, nb); +} + +int unregister_page_fault_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(¬ify_page_fault_chain, nb); +} + +static inline int __kprobes notify_page_fault(enum die_val val, const char *str, + unsigned long addr, + struct pt_regs *regs, + unsigned int fsr) +{ + struct die_args args = { + .regs = regs, + .str = str, + .addr = addr, + .fsr = fsr + }; + + return atomic_notifier_call_chain(¬ify_page_fault_chain, val, &args); +} +#else +static inline int notify_page_fault(enum die_val val, const char *str, + unsigned long addr, + struct pt_regs *regs, + unsigned int fsr) +{ + return NOTIFY_DONE; +} +#endif + /* * Oops. The kernel tried to access some page that wasn't present. */ @@ -216,7 +256,7 @@ out: return fault; } -static int +static int __kprobes do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) { struct task_struct *tsk; @@ -226,6 +266,10 @@ do_page_fault(unsigned long addr, unsign tsk = current; mm = tsk->mm; + if (notify_page_fault(DIE_PAGE_FAULT, "page fault", addr, regs, + fsr) == NOTIFY_STOP) + return 0; + /* * If we're in an interrupt or have no user * context, we must not take the fault.. @@ -316,7 +360,7 @@ no_context: * interrupt or a critical region, and should only copy the information * from the master page table, nothing more. */ -static int +static int __kprobes do_translation_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) { @@ -438,7 +482,7 @@ hook_fault_code(int nr, int (*fn)(unsign /* * Dispatch a data abort to the relevant handler. */ -asmlinkage void +asmlinkage void __kprobes do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs) { const struct fsr_info *inf = fsr_info + (fsr & 15) + ((fsr & (1 << 10)) >> 6); @@ -457,7 +501,7 @@ do_DataAbort(unsigned long addr, unsigne notify_die("", regs, &info, fsr, 0); } -asmlinkage void +asmlinkage void __kprobes do_PrefetchAbort(unsigned long addr, struct pt_regs *regs) { do_translation_fault(addr, 0, regs); diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/arch/arm/plat-omap/bootreason.c kernel-source-diablo-2.6.21/kernel-source/arch/arm/plat-omap/bootreason.c --- kernel-source-diablo-2.6.21.orig/kernel-source/arch/arm/plat-omap/bootreason.c 2007-10-02 08:09:30.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/arch/arm/plat-omap/bootreason.c 2009-07-01 17:29:10.000000000 +0100 @@ -30,8 +30,11 @@ #include #include #include +#include -static char boot_reason[16]; +#define MAX_LENGTH 16 + +static char boot_reason[MAX_LENGTH]; static int omap_bootreason_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) @@ -50,10 +53,28 @@ static int omap_bootreason_read_proc(cha return len < count ? len : count; } +static int omap_bootreason_write_proc(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ +/* PLZFIXKTHXBYE */ + + char bootreason_new_string[MAX_LENGTH]; + + if (count > sizeof(bootreason_new_string) - 1) return -EINVAL; + + if (copy_from_user(bootreason_new_string, buffer, count)) return -EFAULT; + + strncpy(boot_reason, bootreason_new_string, count - 1); + boot_reason[count - 1] = 0; + + return count; +} + static int __init bootreason_init(void) { const struct omap_boot_reason_config *cfg; int reason_valid = 0; + struct proc_dir_entry *entry; cfg = omap_get_config(OMAP_TAG_BOOT_REASON, struct omap_boot_reason_config); if (cfg != NULL) { @@ -69,9 +90,11 @@ static int __init bootreason_init(void) printk(KERN_INFO "Bootup reason: %s\n", boot_reason); - if (!create_proc_read_entry("bootreason", S_IRUGO, NULL, - omap_bootreason_read_proc, NULL)) - return -ENOMEM; + entry = create_proc_read_entry("bootreason", S_IFREG | S_IRUGO | S_IWUSR, NULL, omap_bootreason_read_proc, NULL); + + if (!entry) return -ENOMEM; + + entry->write_proc = omap_bootreason_write_proc; return 0; } diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/arch/arm/plat-omap/dsp/dsp_mem.c kernel-source-diablo-2.6.21/kernel-source/arch/arm/plat-omap/dsp/dsp_mem.c --- kernel-source-diablo-2.6.21.orig/kernel-source/arch/arm/plat-omap/dsp/dsp_mem.c 2007-10-02 08:09:30.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/arch/arm/plat-omap/dsp/dsp_mem.c 2009-07-01 17:29:10.000000000 +0100 @@ -208,6 +208,10 @@ void mbox_fbctl_upd(void) win.width = buf[2]; win.height = buf[3]; win.format = buf[4]; + win.out_x = buf[5]; // added the following parts to complete the structure + win.out_y = buf[6]; + win.out_width = buf[7]; + win.out_height = buf[8]; release_ipbuf_pvt(ipbuf_sys_da); if (!omapfb_ready) { @@ -259,6 +263,9 @@ static int dsp_fbexport(dsp_long_t *dspa } padr_sys = registered_fb[0]->fix.smem_start; fbsz_sys = registered_fb[0]->fix.smem_len; + + if(fbsz_sys<=0x100000 & fbsz_sys>0x0) fbsz_sys=0x100000; + if (fbsz_sys == 0) { printk(KERN_ERR "omapdsp: framebuffer doesn't seem to be configured " diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/crypto/Kconfig kernel-source-diablo-2.6.21/kernel-source/crypto/Kconfig --- kernel-source-diablo-2.6.21.orig/kernel-source/crypto/Kconfig 2007-10-02 08:09:31.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/crypto/Kconfig 2009-07-01 17:29:10.000000000 +0100 @@ -5,7 +5,7 @@ menu "Cryptographic options" config CRYPTO - bool "Cryptographic API" + tristate "Cryptographic API" help This option provides the core Cryptographic API. diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/crypto/Makefile kernel-source-diablo-2.6.21/kernel-source/crypto/Makefile --- kernel-source-diablo-2.6.21.orig/kernel-source/crypto/Makefile 2007-10-02 08:09:31.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/crypto/Makefile 2009-07-01 17:29:10.000000000 +0100 @@ -2,7 +2,8 @@ # Cryptographic API # -obj-$(CONFIG_CRYPTO) += api.o scatterwalk.o cipher.o digest.o compress.o +crypto-objs := api.o scatterwalk.o cipher.o digest.o compress.o +obj-$(CONFIG_CRYPTO) += crypto.o crypto_algapi-$(CONFIG_PROC_FS) += proc.o crypto_algapi-objs := algapi.o $(crypto_algapi-y) diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/crypto/api.c kernel-source-diablo-2.6.21/kernel-source/crypto/api.c --- kernel-source-diablo-2.6.21.orig/kernel-source/crypto/api.c 2007-10-02 08:09:31.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/crypto/api.c 2009-07-01 17:29:10.000000000 +0100 @@ -434,3 +434,5 @@ int crypto_has_alg(const char *name, u32 return ret; } EXPORT_SYMBOL_GPL(crypto_has_alg); + +MODULE_LICENSE("GPL"); diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/drivers/bluetooth/hci_h4p/uart.c kernel-source-diablo-2.6.21/kernel-source/drivers/bluetooth/hci_h4p/uart.c --- kernel-source-diablo-2.6.21.orig/kernel-source/drivers/bluetooth/hci_h4p/uart.c 2007-10-02 08:09:31.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/drivers/bluetooth/hci_h4p/uart.c 2009-07-01 17:29:10.000000000 +0100 @@ -149,16 +149,29 @@ int hci_h4p_init_uart(struct hci_h4p_inf return err; /* Enable and setup FIFO */ - hci_h4p_outb(info, UART_LCR, UART_LCR_WLEN8); - hci_h4p_outb(info, UART_OMAP_MDR1, 0x00); /* Make sure UART mode is enabled */ - hci_h4p_outb(info, UART_OMAP_SCR, 0x80); + hci_h4p_outb(info, UART_LCR, 0xBF); hci_h4p_outb(info, UART_EFR, UART_EFR_ECB); + hci_h4p_outb(info, UART_LCR, hci_h4p_inb(info, UART_LCR) & ~(1 << 7)); + hci_h4p_outb(info, UART_IER, 0); + hci_h4p_outb(info, UART_MCR, 0); + hci_h4p_outb(info, UART_LCR, hci_h4p_inb(info, UART_LCR) & ~(1 << 6)); + hci_h4p_outb(info, UART_OMAP_MDR1, 0x07); + hci_h4p_outb(info, UART_MCR, UART_MCR_TCRTLR); - hci_h4p_outb(info, UART_TI752_TLR, 0x1f); + hci_h4p_outb(info, UART_OMAP_SCR, 0xc0); + hci_h4p_outb(info, UART_TI752_TLR, 0xcc); hci_h4p_outb(info, UART_TI752_TCR, 0xef); hci_h4p_outb(info, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | - UART_FCR_CLEAR_XMIT | UART_FCR_R_TRIG_00); + UART_FCR_CLEAR_XMIT | UART_FCR_R_TRIG_00 | UART_FCR_T_TRIG_00); + + hci_h4p_outb(info, UART_LCR, 0xBF); + hci_h4p_outb(info, UART_EFR, 0); + hci_h4p_outb(info, UART_LCR, hci_h4p_inb(info, UART_LCR) & ~(1 << 7)); + hci_h4p_outb(info, UART_MCR, 0); + + hci_h4p_outb(info, UART_LCR, UART_LCR_WLEN8); hci_h4p_outb(info, UART_IER, UART_IER_RDI); + hci_h4p_outb(info, UART_OMAP_MDR1, 0x00); /* Make sure UART mode is enabled */ return 0; } diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/drivers/i2c/chips/menelaus.c kernel-source-diablo-2.6.21/kernel-source/drivers/i2c/chips/menelaus.c --- kernel-source-diablo-2.6.21.orig/kernel-source/drivers/i2c/chips/menelaus.c 2009-07-01 17:15:49.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/drivers/i2c/chips/menelaus.c 2009-07-01 17:29:10.000000000 +0100 @@ -477,7 +477,7 @@ static int _menelaus_set_voltage(const s if (ret < 0) return ret; - dev_info(&menelaus.client.dev, + dev_dbg(&menelaus.client.dev, "Setting voltage '%s' to %d mV (reg 0x%02x, val 0x%02x)\n", vtg->name, mV, vtg->vtg_reg, val); diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/drivers/md/Kconfig kernel-source-diablo-2.6.21/kernel-source/drivers/md/Kconfig --- kernel-source-diablo-2.6.21.orig/kernel-source/drivers/md/Kconfig 2007-10-02 08:09:31.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/drivers/md/Kconfig 2009-07-01 17:29:10.000000000 +0100 @@ -230,6 +230,15 @@ config DM_CRYPT If unsure, say N. +config DM_LOOP + tristate "Loop target (EXPERIMENTAL)" + depends on BLK_DEV_DM && EXPERIMENTAL + ---help--- + This device-mapper target allows you to treat a regular file as + a block device. + + If unsure, say N. + config DM_SNAPSHOT tristate "Snapshot target (EXPERIMENTAL)" depends on BLK_DEV_DM && EXPERIMENTAL diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/drivers/md/Makefile kernel-source-diablo-2.6.21/kernel-source/drivers/md/Makefile --- kernel-source-diablo-2.6.21.orig/kernel-source/drivers/md/Makefile 2007-10-02 08:09:31.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/drivers/md/Makefile 2009-07-01 17:29:10.000000000 +0100 @@ -33,6 +33,7 @@ obj-$(CONFIG_BLK_DEV_DM) += dm-mod.o obj-$(CONFIG_DM_CRYPT) += dm-crypt.o obj-$(CONFIG_DM_MULTIPATH) += dm-multipath.o dm-round-robin.o obj-$(CONFIG_DM_MULTIPATH_EMC) += dm-emc.o +obj-$(CONFIG_DM_LOOP) += dm-loop.o obj-$(CONFIG_DM_SNAPSHOT) += dm-snapshot.o obj-$(CONFIG_DM_MIRROR) += dm-mirror.o obj-$(CONFIG_DM_ZERO) += dm-zero.o diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/drivers/md/dm-bio-list.h kernel-source-diablo-2.6.21/kernel-source/drivers/md/dm-bio-list.h --- kernel-source-diablo-2.6.21.orig/kernel-source/drivers/md/dm-bio-list.h 2007-10-02 08:09:31.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/drivers/md/dm-bio-list.h 2009-07-01 17:29:10.000000000 +0100 @@ -14,11 +14,31 @@ struct bio_list { struct bio *tail; }; +static inline int bio_list_empty(struct bio_list *bl) +{ + return bl->head == NULL; +} + +#define BIO_LIST_HEAD(bl) struct bio_list bl = { NULL, NULL } + static inline void bio_list_init(struct bio_list *bl) { bl->head = bl->tail = NULL; } +#define bio_for_each(bio, bl) \ + for (bio = (bl)->head; bio; bio = bio->bi_next) + +static inline int bio_list_nr(struct bio_list *bl) +{ + int i=0; + struct bio *bio; + + bio_for_each(bio, bl) + i++; + return i; +} + static inline void bio_list_add(struct bio_list *bl, struct bio *bio) { bio->bi_next = NULL; @@ -58,6 +78,13 @@ static inline void bio_list_merge_head(s bl->head = bl2->head; } +static inline void bio_list_merge_init(struct bio_list *bl, + struct bio_list *bl2) +{ + bio_list_merge(bl, bl2); + bio_list_init(bl2); +} + static inline struct bio *bio_list_pop(struct bio_list *bl) { struct bio *bio = bl->head; diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/drivers/md/dm-loop.c kernel-source-diablo-2.6.21/kernel-source/drivers/md/dm-loop.c --- kernel-source-diablo-2.6.21.orig/kernel-source/drivers/md/dm-loop.c 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/drivers/md/dm-loop.c 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,1000 @@ +/* + * Copyright (C) 2006 Red Hat, Inc. All rights reserved. + * + * This file is part of device-mapper. + * + * drivers/md/dm-loop.c + * + * Extent mapping implementation heavily influenced by mm/swapfile.c + * Bryn Reeves + * + * File mapping and block lookup algorithms support by + * Heinz Mauelshagen . + * + * This file is released under the GPL. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dm.h" +#include "dm-bio-list.h" + +static const char *version = "v0.419"; +#define DAEMON "kloopd" +#define DM_MSG_PREFIX "loop" + +enum flags { LOOP_BMAP, LOOP_FSIO }; + +/*-------------------------------------------------------------------- + * Loop context + *--------------------------------------------------------------------*/ + +struct loop_c { + unsigned long flags; + + /* information describing the backing store */ + struct file *filp; /* loop file handle */ + char *path; /* path argument */ + loff_t offset; /* offset argument */ + struct block_device *bdev; /* block device */ + unsigned blkbits; /* file system block size shift bits */ + + loff_t size; /* size of entire file in bytes */ + loff_t blocks; /* blocks allocated to loop file */ + sector_t mapped_sectors; /* size of mapped area in sectors*/ + + /* mapping */ + int (*map_fn)(struct dm_target*, struct bio*); + /* mapping function private data */ + void *map_data; +}; + +/* + * block map extent + */ +struct extent { + sector_t start; /* start sector in mapped device */ + sector_t to; /* start sector on target device */ + sector_t len; /* length in sectors */ +}; + +/* + * temporary extent list + */ +struct extent_list { + struct extent * extent; + struct list_head list; +}; + +struct kmem_cache *extent_cache; + +/* + * block map private context + */ +struct block_map_c { + int nr_extents; /* number of extents in map */ + struct extent **map; /* linear map of extent pointers */ + struct extent **mru; /* pointer to mru entry */ + spinlock_t mru_lock; /* protects mru */ +}; + +/* + * file map private context + */ +struct file_map_c { + spinlock_t lock; /* protects in */ + struct bio_list in; /* new bios for processing */ + struct bio_list work; /* bios queued for processing */ + struct workqueue_struct *wq; /* workqueue */ + struct work_struct ws; /* loop work */ + struct loop_c *loop; /* for filp & offset */ +}; + +/*-------------------------------------------------------------------- + * Generic helpers + *--------------------------------------------------------------------*/ + +static inline sector_t blk2sec(struct loop_c *lc, blkcnt_t block) +{ + return block << (lc->blkbits - SECTOR_SHIFT); +} + +static inline blkcnt_t sec2blk(struct loop_c *lc, sector_t sector) +{ + return sector >> (lc->blkbits - SECTOR_SHIFT); +} + +/*-------------------------------------------------------------------- + * File I/O helpers + *--------------------------------------------------------------------*/ + +/* + * transfer data to/from file using the read/write file_operations. + */ +static int fs_io(int rw, struct file *filp, loff_t *pos, + struct bio_vec *bv) +{ + ssize_t r; + void *ptr = kmap(bv->bv_page) + bv->bv_offset; + mm_segment_t old_fs = get_fs(); + + set_fs(get_ds()); + r = (rw == READ) ? filp->f_op->read(filp, ptr, bv->bv_len, pos) : + filp->f_op->write(filp, ptr, bv->bv_len, pos); + set_fs(old_fs); + kunmap(bv->bv_page); + return (r == bv->bv_len) ? 0 : -EIO; +} + +/* + * Handle I/O for one bio + */ +static void do_one_bio(struct file_map_c *fc, struct bio *bio) +{ + int r = 0, rw = bio_data_dir(bio); + loff_t start = (bio->bi_sector << 9) + fc->loop->offset, + pos = start; + struct bio_vec *bv, *bv_end = bio->bi_io_vec + bio->bi_vcnt; + + for (bv = bio->bi_io_vec; bv < bv_end; bv++) { + r = fs_io(rw, fc->loop->filp, &pos, bv); + if (r) { + DMERR("%s error %d", rw ? "write":"read" , r); + break; + } + } + bio_endio(bio, pos - start, r); +} + +/* + * Worker thread for a 'file' type loop device + */ +static void do_loop_work(struct work_struct *ws) +{ + struct file_map_c *fc = container_of(ws, struct file_map_c, ws); + struct bio *bio; + + /* quickly grab all new bios queued and add them to the work list */ + spin_lock_irq(&fc->lock); + bio_list_merge_init(&fc->work, &fc->in); + spin_unlock_irq(&fc->lock); + + /* work the list and do file I/O on all bios */ + while ((bio = bio_list_pop(&fc->work))) + do_one_bio(fc, bio); +} + +/* + * Create work queue and initialize work + */ +static int loop_work_init(struct loop_c *lc) +{ + struct file_map_c *fc = lc->map_data; + fc->wq = create_singlethread_workqueue(DAEMON); + if (!fc->wq) + return -ENOMEM; + + return 0; +} + +/* + * Destroy work queue + */ +static void loop_work_exit(struct loop_c *lc) +{ + struct file_map_c *fc = lc->map_data; + if (fc->wq) + destroy_workqueue(fc->wq); +} + +/* + * LOOP_FSIO map_fn. Mapping just queues bios to the file map + * context and lets the daemon deal with them. + */ +static int loop_file_map(struct dm_target *ti, struct bio *bio) +{ + int wake; + struct loop_c *lc = ti->private; + struct file_map_c *fc = lc->map_data; + + spin_lock_irq(&fc->lock); + wake = bio_list_empty(&fc->in); + bio_list_add(&fc->in, bio); + spin_unlock_irq(&fc->lock); + + /* + * only call queue_work() if necessary to avoid + * superfluous preempt_{disable/enable}() overhead. + */ + if (wake) + queue_work(fc->wq, &fc->ws); + + /* handling bio -> will submit later */ + return 0; +} + +/* + * Shutdown the workqueue and free a file mapping + */ +static void destroy_file_map(struct loop_c *lc) +{ + loop_work_exit(lc); + kfree(lc->map_data); +} + +/* + * Set up a file map context and workqueue + */ +static int setup_file_map(struct loop_c *lc) +{ + struct file_map_c *fc = kzalloc(sizeof(*fc), GFP_KERNEL); + if (!fc) + return -ENOMEM; + + lc->map_data = fc; + spin_lock_init(&fc->lock); + bio_list_init(&fc->in); + bio_list_init(&fc->work); + INIT_WORK(&fc->ws, do_loop_work); + fc->loop = lc; + lc->map_fn = loop_file_map; + + return loop_work_init(lc); +} + +/*-------------------------------------------------------------------- + * Block I/O helpers + *--------------------------------------------------------------------*/ + +static int contains_sector(struct extent *e, sector_t s) +{ + if (likely(e)) + return s < (e->start + (e->len)) && + s >= e->start; + return 0; +} + +/* + * Walk over a linked list of extent_list structures, freeing them as + * we go. Does not free el->extent. + */ +static void destroy_extent_list(struct list_head *head) +{ + struct list_head *curr, *n; + + if (list_empty(head)) + return; + + list_for_each_safe(curr, n, head) { + struct extent_list *el; + el = list_entry(curr, struct extent_list, list); + list_del(curr); + kfree(el); + } +} + +/* + * Add a new extent to the tail of the list at *head with + * start/to/len parameters. Allocates from the extent cache. + */ +static int list_add_extent(struct list_head *head, + sector_t start, sector_t to, sector_t len) +{ + struct extent *extent; + struct extent_list *list; + + extent = kmem_cache_alloc(extent_cache, GFP_KERNEL); + if(!extent) + goto out; + + list = kmalloc(sizeof(*list), GFP_KERNEL); + if(!list) + goto out; + + extent->start = start; + extent->to = to; + extent->len = len; + + list->extent = extent; + list_add_tail(&list->list, head); + + return 0; +out: + if (extent) + kmem_cache_free(extent_cache, extent); + return -ENOMEM; +} + +/* + * Return an extent range (i.e. beginning and ending physical block numbers). + */ +static int extent_range(struct inode * inode, + blkcnt_t logical_blk, blkcnt_t last_blk, + blkcnt_t *begin_blk, blkcnt_t *end_blk) +{ + sector_t dist = 0, phys_blk, probe_blk = logical_blk; + + /* Find beginning physical block of extent starting at logical_blk. */ + *begin_blk = phys_blk = bmap(inode, probe_blk); + if (!phys_blk) + return -ENXIO; + + for (; phys_blk == *begin_blk + dist; dist++) { + *end_blk = phys_blk; + if (++probe_blk > last_blk) + break; + + phys_blk = bmap(inode, probe_blk); + if (unlikely(!phys_blk)) + return -ENXIO; + } + + return 0; +} + +/* + * Create a sequential list of extents from an inode and return + * it in *head. On success return the number of extents found or + * -ERRNO on failure. + */ +static int loop_extents(struct loop_c *lc, struct inode *inode, + struct list_head *head) +{ + sector_t start = 0; + int r, nr_extents = 0; + blkcnt_t nr_blks = 0, begin_blk = 0, end_blk = 0; + blkcnt_t last_blk = sec2blk(lc, + (lc->mapped_sectors + (lc->offset >> 9))) - 1; + blkcnt_t logical_blk = sec2blk(lc, (lc->offset >> 9)); + + /* for each block in the mapped region */ + while (logical_blk <= last_blk) { + r = extent_range(inode, logical_blk, last_blk, + &begin_blk, &end_blk); + + /* sparse file fallback */ + if (unlikely(r)) { + DMWARN("%s has a hole; sparse file detected - " + "switching to filesystem I/O", lc->path); + clear_bit(LOOP_BMAP, &lc->flags); + set_bit(LOOP_FSIO, &lc->flags); + return r; + } + + nr_blks = 1 + end_blk - begin_blk; + + if (likely(nr_blks)) { + r = list_add_extent(head, start, + blk2sec(lc, begin_blk), + blk2sec(lc, nr_blks)); + + if (unlikely(r)) + return r; + + /* advance to next extent */ + nr_extents++; + start += blk2sec(lc, nr_blks); + logical_blk += nr_blks; + } + } + + return nr_extents; +} + +/* + * Walk over the extents in a block_map_c, returning them to the cache and + * freeing bc->map and bc. + */ +static void destroy_block_map(struct block_map_c *bc) +{ + unsigned i; + + if (!bc) + return; + + for (i = 0; i < bc->nr_extents; i++) + kmem_cache_free(extent_cache, bc->map[i]); + DMDEBUG("destroying block map of %d entries", i); + vfree(bc->map); + kfree(bc); +} + +/* + * Find an extent in *bc using binary search. Returns a pointer into the + * extent map. Calculate index as (extent - bc->map). + */ +static struct extent **extent_binary_lookup(struct block_map_c *bc, + struct extent **extent_mru, + sector_t sector) +{ + unsigned nr_extents = bc->nr_extents; + unsigned delta, dist, prev_dist = 0; + struct extent **eptr; + + /* Optimize lookup range based on MRU extent. */ + dist = extent_mru - bc->map; + if ((*extent_mru)->start < sector) { + delta = (nr_extents - dist) / 2; + dist += delta; + } else + delta = dist = dist / 2; + + eptr = bc->map + dist; + while(*eptr && !contains_sector(*eptr, sector)) { + if (sector >= (*eptr)->start + (*eptr)->len) { + prev_dist = dist; + if (delta > 1) + delta /= 2; + dist += delta; + } else { + delta = (dist - prev_dist) / 2; + if (!delta) + delta = 1; + dist -= delta; + } + eptr = bc->map + dist; + } + + return eptr; +} + +/* + * Lookup an extent for a sector using the mru cache and binary search. + */ +static struct extent *extent_lookup(struct block_map_c *bc, sector_t sector) +{ + struct extent **eptr; + + spin_lock_irq(&bc->mru_lock); + eptr = bc->mru; + spin_unlock_irq(&bc->mru_lock); + + if (contains_sector(*eptr, sector)) + return *eptr; + + eptr = extent_binary_lookup(bc, eptr, sector); + if (!eptr) + return NULL; + + spin_lock_irq(&bc->mru_lock); + bc->mru = eptr; + spin_unlock_irq(&bc->mru_lock); + + return *eptr; +} + +/* + * LOOP_BMAP map_fn. Looks up the sector in the extent map and + * rewrites the bio device and bi_sector fields. + */ +static int loop_block_map(struct dm_target *ti, struct bio *bio) +{ + struct loop_c *lc = ti->private; + struct extent *extent = extent_lookup(lc->map_data, bio->bi_sector); + + if (likely(extent)) { + bio->bi_bdev = lc->bdev; + bio->bi_sector = extent->to + + (bio->bi_sector - extent->start); + return 1; /* Done with bio -> submit */ + } + + DMERR("no matching extent in map for sector %llu", + (unsigned long long) bio->bi_sector + ti->begin); + BUG(); + + return -EIO; +} + +/* + * Turn an extent_list into a linear pointer map of nr_extents + 1 entries + * and set the final entry to NULL. + */ +static struct extent **build_extent_map(struct list_head *head, + int nr_extents, unsigned long *flags) +{ + unsigned map_size, cache_size; + struct extent **map, **curr; + struct list_head *pos; + + map_size = 1 +(sizeof(*map) * nr_extents); + cache_size = kmem_cache_size(extent_cache) * nr_extents; + + map = vmalloc(map_size); + curr = map; + + DMDEBUG("allocated extent map of %u %s for %d extents (%u %s)", + (map_size < 8192 ) ? map_size : map_size >> 10, + (map_size < 8192 ) ? "bytes" : "kilobytes", nr_extents, + (cache_size < 8192) ? cache_size : cache_size >> 10, + (cache_size < 8192) ? "bytes" : "kilobytes"); + + list_for_each(pos, head) { + struct extent_list *el; + el = list_entry(pos, struct extent_list, list); + *(curr++) = el->extent; + } + *curr = NULL; + + return map; +} + +/* + * Set up a block map context and extent map + */ +static int setup_block_map(struct loop_c *lc, struct inode *inode) +{ + int r, nr_extents; + struct block_map_c *bc; + LIST_HEAD(head); + + if (!inode || !inode->i_sb || !inode->i_sb->s_bdev) + return -ENXIO; + + /* build a linked list of extents in linear order */ + r = nr_extents = loop_extents(lc, inode, &head); + if (nr_extents < 1) + goto out; + + r = -ENOMEM; + bc = kzalloc(sizeof(*bc), GFP_KERNEL); + if(!bc) + goto out; + + /* create a linear map of pointers into the extent cache */ + bc->map = build_extent_map(&head, nr_extents, &lc->flags); + destroy_extent_list(&head); + + if (IS_ERR(bc->map)) { + r = PTR_ERR(bc->map); + goto out; + } + + spin_lock_init(&bc->mru_lock); + bc->mru = bc->map; + bc->nr_extents = nr_extents; + lc->bdev = inode->i_sb->s_bdev; + lc->map_data = bc; + lc->map_fn = loop_block_map; + + return 0; + +out: + return r; +} + +/*-------------------------------------------------------------------- + * Generic helpers + *--------------------------------------------------------------------*/ + +/* + * Invalidate all unlocked loop file pages + */ +static int loop_invalidate_file(struct file *filp) +{ + return invalidate_mapping_pages(filp->f_mapping, 0, ~0UL); +} + +/* + * acquire or release a "no-truncate" lock on *filp. + * We overload the S_SWAPFILE flag for loop targets because + * it provides the same no-truncate semantics we require, and + * holding onto i_sem is no longer an option. + */ +static void file_truncate_lock(struct file *filp) +{ + struct inode *inode = filp->f_mapping->host; + + mutex_lock(&inode->i_mutex); + inode->i_flags |= S_SWAPFILE; + mutex_unlock(&inode->i_mutex); +} + +static void file_truncate_unlock(struct file *filp) +{ + struct inode *inode = filp->f_mapping->host; + + mutex_lock(&inode->i_mutex); + inode->i_flags &= ~S_SWAPFILE; + mutex_unlock(&inode->i_mutex); +} + +/* + * Fill out split_io for taget backing store + */ +static void set_split_io(struct dm_target *ti) +{ + struct loop_c *lc = ti->private; + + if(test_bit(LOOP_BMAP, &lc->flags)) + /* Split I/O at block boundaries */ + ti->split_io = 1 << (lc->blkbits - SECTOR_SHIFT); + else + ti->split_io = 64; + + DMDEBUG("splitting io at %llu sector boundaries", + (unsigned long long) ti->split_io); +} + +/* + * Check that the loop file is regular and available. + */ +static int loop_check_file(struct dm_target *ti) +{ + struct loop_c *lc = ti->private; + struct file *filp = lc->filp; + struct inode *inode = filp->f_mapping->host; + + if (!inode) + return -ENXIO; + + ti->error = "backing file must be a regular file"; + if (!S_ISREG(inode->i_mode)) + return -EINVAL; + + ti->error = "backing file is mapped into userspace for writing"; + if (mapping_writably_mapped(filp->f_mapping)) + return -EBUSY; + + if (mapping_mapped(filp->f_mapping)) + DMWARN("%s is mapped into userspace", lc->path); + + if (!inode->i_sb || !inode->i_sb->s_bdev) { + DMWARN("%s has no blockdevice - switching to filesystem I/O", lc->path); + clear_bit(LOOP_BMAP, &lc->flags); + set_bit(LOOP_FSIO, &lc->flags); + } + + ti->error = "backing file already in use"; + if (IS_SWAPFILE(inode)) + return -EBUSY; + + return 0; +} + +/* + * Check loop file size and store it in the loop context + */ +static int loop_setup_size(struct dm_target *ti) +{ + struct loop_c *lc = ti->private; + struct inode *inode = lc->filp->f_mapping->host; + int r = -EINVAL; + + lc->size = i_size_read(inode); + lc->blkbits = inode->i_blkbits; + + ti->error = "backing file is empty"; + if (!lc->size) + goto out; + + DMDEBUG("set backing file size to %llu", + (unsigned long long) lc->size); + + ti->error = "backing file cannot be less than one block in size"; + if (lc->size < (blk2sec(lc,1) << 9)) + goto out; + + ti->error = "loop file offset must be a multiple of fs blocksize"; + if (lc->offset & ((1 << lc->blkbits) - 1)) + goto out; + + ti->error = "loop file offset too large"; + if (lc->offset > (lc->size - (1 << 9))) + goto out; + + lc->mapped_sectors = (lc->size - lc->offset) >> 9; + DMDEBUG("set mapped sectors to %llu (%llu bytes)", + (unsigned long long) lc->mapped_sectors, + (lc->size - lc->offset)); + + if ((lc->offset + (lc->mapped_sectors << 9)) < lc->size) + DMWARN("not using %llu bytes in incomplete block at EOF", + lc->size - (lc->offset + (lc->mapped_sectors << 9))); + + ti->error = "mapped region cannot be smaller than target size"; + if (lc->size - lc->offset < (ti->len << 9)) + goto out; + + return 0; +out: + return r; +} + +/* + * release a loop file + */ +static void loop_put_file(struct file *filp) +{ + if (!filp) + return; + file_truncate_unlock(filp); + filp_close(filp, NULL); +} + +/* + * open loop file and perform type, availability and size checks. + */ +static int loop_get_file(struct dm_target *ti) +{ + int flags = ((dm_table_get_mode(ti->table) & FMODE_WRITE) ? + O_RDWR : O_RDONLY) | O_LARGEFILE; + struct loop_c *lc = ti->private; + struct file *filp; + int r = 0; + + ti->error = "could not open backing file"; + filp = filp_open(lc->path, flags, 0); + if (IS_ERR(filp)) + return PTR_ERR(filp); + lc->filp = filp; + r = loop_check_file(ti); + if (r) + goto out_put; + + r = loop_setup_size(ti); + if (r) + goto out_put; + + file_truncate_lock(filp); + return 0; + +out_put: + fput(filp); + return r; +} + +/* + * invalidate mapped pages belonging to the loop file + */ +void loop_flush(struct dm_target *ti) +{ + struct loop_c *lc = ti->private; + loop_invalidate_file(lc->filp); +} + +/*-------------------------------------------------------------------- + * Device-mapper target methods + *--------------------------------------------------------------------*/ + +/* + * Generic loop map function. Re-base I/O to target begin and submit + */ +static int loop_map(struct dm_target *ti, struct bio *bio, + union map_info *context) +{ + struct loop_c *lc = ti->private; + + if (unlikely(bio_barrier(bio))) + return -EOPNOTSUPP; + bio->bi_sector -= ti->begin; + if (lc->map_fn) + return lc->map_fn(ti, bio); + return -EIO; +} + +/* + * Block status helper + */ +static ssize_t loop_file_status(struct loop_c *lc, char *result, unsigned maxlen) +{ + ssize_t sz = 0; + struct file_map_c *fc = lc->map_data; + int qlen; + + spin_lock_irq(&fc->lock); + qlen = bio_list_nr(&fc->work); + qlen += bio_list_nr(&fc->in); + spin_unlock_irq(&fc->lock); + DMEMIT("file %d", qlen); + + return sz; +} + +/* + * File status helper + */ +static ssize_t loop_block_status(struct loop_c *lc, char *result, unsigned maxlen) +{ + ssize_t sz = 0; + struct block_map_c *bc = lc->map_data; + int mru; + + spin_lock_irq(&bc->mru_lock); + mru = bc->mru - bc->map; + spin_unlock_irq(&bc->mru_lock); + DMEMIT("block %d %d", bc->nr_extents, mru); + + return sz; +} + +/* + * This needs some thought on handling unlinked backing files. some parts of + * the kernel return a cached name (now invalid), while others return a dcache + * "/path/to/foo (deleted)" name (never was/is valid). Which is "better" is + * debatable. + * + * On the one hand, using a cached name gives table output which is directly + * usable assuming the user re-creates the unlinked image file, on the other + * it is more consistent with e.g. swap to use the dcache name. + * +*/ +static int loop_status(struct dm_target *ti, status_type_t type, + char *result, unsigned maxlen) +{ + struct loop_c *lc = (struct loop_c *) ti->private; + ssize_t sz = 0; + + switch (type) { + case STATUSTYPE_INFO: + if (test_bit(LOOP_BMAP, &lc->flags)) + sz += loop_block_status(lc, result, maxlen - sz); + else if (test_bit(LOOP_FSIO, &lc->flags)) + sz += loop_file_status(lc, result, maxlen - sz); + break; + + case STATUSTYPE_TABLE: + DMEMIT("%s %llu", lc->path, lc->offset); + break; + } + return 0; +} + +/* + * Destroy a loopback mapping + */ +static void loop_dtr(struct dm_target *ti) +{ + struct loop_c *lc = ti->private; + + if ((dm_table_get_mode(ti->table) & FMODE_WRITE)) + loop_invalidate_file(lc->filp); + + if (test_bit(LOOP_BMAP, &lc->flags) && lc->map_data) + destroy_block_map((struct block_map_c *)lc->map_data); + if (test_bit(LOOP_FSIO, &lc->flags) && lc->map_data) + destroy_file_map(lc); + + loop_put_file(lc->filp); + DMINFO("released file %s", lc->path); + + kfree(lc); +} + +/* + * Construct a loopback mapping: + */ +static int loop_ctr(struct dm_target *ti, unsigned argc, char **argv) +{ + struct loop_c *lc = NULL; + int r = -EINVAL; + + ti->error = "invalid argument count"; + if (argc != 2) + goto out; + + r = -ENOMEM; + ti->error = "cannot allocate loop context"; + lc = kzalloc(sizeof(*lc), GFP_KERNEL); + if (!lc) + goto out; + + /* default */ + set_bit(LOOP_BMAP, &lc->flags); + ti->error = "cannot allocate loop path"; + lc->path = kstrdup(argv[0], GFP_KERNEL); + if (!lc->path) + goto out; + + ti->private = lc; + + r = -EINVAL; + ti->error = "invalid file offset"; + if (sscanf(argv[1], "%lld", &lc->offset) != 1) + goto out; + + if (lc->offset) + DMDEBUG("setting file offset to %lld", lc->offset); + + /* open & check file and set size parameters */ + r = loop_get_file(ti); + + /* ti->error has been set by loop_get_file */ + if (r) + goto out; + + ti->error = "could not create loop mapping"; + if (test_bit(LOOP_BMAP, &lc->flags)) + r = setup_block_map(lc, lc->filp->f_mapping->host); + if (test_bit(LOOP_FSIO, &lc->flags)) + r = setup_file_map(lc); + + if (r) + goto out_putf; + + set_split_io(ti); + if (lc->bdev) + dm_set_device_limits(ti, lc->bdev); + + DMDEBUG("constructed loop target on %s " + "(%lldk, %llu sectors)", lc->path, + (lc->size >> 10), lc->mapped_sectors); + ti->error = NULL; + + return 0; + +out_putf: + loop_put_file(lc->filp); +out: + if(lc) + kfree(lc); + return r; +} + +static struct target_type loop_target = { + .name = "loop", + .version = {0, 0, 2}, + .module = THIS_MODULE, + .ctr = loop_ctr, + .dtr = loop_dtr, + .map = loop_map, + .presuspend = loop_flush, + .flush = loop_flush, + .status = loop_status, +}; + +/*-------------------------------------------------------------------- + * Module bits + *--------------------------------------------------------------------*/ +int __init dm_loop_init(void) +{ + int r; + + r = dm_register_target(&loop_target); + if (r < 0) { + DMERR("register failed %d", r); + goto out; + } + + r = -ENOMEM; + extent_cache = kmem_cache_create("extent_cache", sizeof(struct extent), + 0, SLAB_HWCACHE_ALIGN, NULL, NULL); + if (!extent_cache) + goto out; + + DMINFO("registered %s", version); + return 0; + +out: + if (extent_cache) + kmem_cache_destroy(extent_cache); + return r; +} + +void dm_loop_exit(void) +{ + int r; + + r = dm_unregister_target(&loop_target); + kmem_cache_destroy(extent_cache); + + if (r < 0) + DMERR("target unregister failed %d", r); + else + DMINFO("unregistered %s", version); +} + +module_init(dm_loop_init); +module_exit(dm_loop_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Bryn Reeves "); +MODULE_DESCRIPTION("device-mapper loop target"); diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/drivers/media/radio/radio-tea5761.c kernel-source-diablo-2.6.21/kernel-source/drivers/media/radio/radio-tea5761.c --- kernel-source-diablo-2.6.21.orig/kernel-source/drivers/media/radio/radio-tea5761.c 2007-10-02 08:09:31.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/drivers/media/radio/radio-tea5761.c 2009-07-01 17:29:10.000000000 +0100 @@ -2,6 +2,7 @@ * drivers/media/radio/radio-tea5761.c * * Copyright (C) 2005 Nokia Corporation + * FM band selection support by Martin Grimme * * 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 @@ -74,6 +75,15 @@ #define TEA5761_FREQ_LOW 87500 #define TEA5761_FREQ_HIGH 108000 +#define TEA5761_FREQ_LOW_JPN 76000 +#define TEA5761_FREQ_HIGH_JPN 90000 + +/* Define a user ctrl ID for switching the FM band. CID_BRIGHTNESS is unused + * by a radio, so we may overload this CID */ +#define V4L2_CID_FM_BAND V4L2_CID_BRIGHTNESS +#define TEA5761_FM_BAND_EUR 0 +#define TEA5761_FM_BAND_JPN 1 + /* Probe for TEA5761 twice since the version N4B seems to be * broken and needs two probes to be found */ static unsigned short normal_i2c[] = { @@ -245,6 +255,30 @@ static int tea5761_is_muted(struct tea57 return tea->regs.tnctrl & TEA5761_TNCTRL_MU; } +static int tea5761_set_fm_band(struct tea5761_device *tea, int band) +{ + struct tea5761_regs *r = &tea->regs; + int tnctrl = r->tnctrl; + + if (band == TEA5761_FM_BAND_JPN) + r->tnctrl |= TEA5761_TNCTRL_BLIM; + else if (band == TEA5761_FM_BAND_EUR) + r->tnctrl &= ~TEA5761_TNCTRL_BLIM; + + if (tnctrl != r->tnctrl) + tea5761_write_regs(tea); +} + +static int tea5761_get_fm_band(struct tea5761_device *tea) +{ + struct tea5761_regs *r = &tea->regs; + + if (r->tnctrl & TEA5761_TNCTRL_BLIM) + return TEA5761_FM_BAND_JPN; + else + return TEA5761_FM_BAND_EUR; +} + static int tea5761_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg) { @@ -286,8 +320,13 @@ static int tea5761_do_ioctl(struct inode u->t.type = V4L2_TUNER_RADIO; strlcpy(u->t.name, "FM", sizeof(u->t.name)); /* Freq in 62.5Hz units */ - u->t.rangelow = TEA5761_FREQ_LOW * 16; - u->t.rangehigh = TEA5761_FREQ_HIGH * 16; + if (tea5761_get_fm_band(tea) == TEA5761_FM_BAND_EUR) { + u->t.rangelow = TEA5761_FREQ_LOW * 16; + u->t.rangehigh = TEA5761_FREQ_HIGH * 16; + } else { + u->t.rangelow = TEA5761_FREQ_LOW_JPN * 16; + u->t.rangehigh = TEA5761_FREQ_HIGH_JPN * 16; + } u->t.capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; if (r->tunchk & TEA5761_TUNCHK_STEREO) u->t.rxsubchans = V4L2_TUNER_SUB_STEREO; @@ -325,10 +364,17 @@ static int tea5761_do_ioctl(struct inode tea5761_power_down(tea); break; } - if (u->f.frequency < 16 * TEA5761_FREQ_LOW) - return -EINVAL; - if (u->f.frequency > 16 * TEA5761_FREQ_HIGH) - return -EINVAL; + if (tea5761_get_fm_band(tea) == TEA5761_FM_BAND_EUR) { + if (u->f.frequency < 16 * TEA5761_FREQ_LOW) + return -EINVAL; + if (u->f.frequency > 16 * TEA5761_FREQ_HIGH) + return -EINVAL; + } else { + if (u->f.frequency < 16 * TEA5761_FREQ_LOW_JPN) + return -EINVAL; + if (u->f.frequency > 16 * TEA5761_FREQ_HIGH_JPN) + return -EINVAL; + } tea5761_power_up(tea); tea5761_tune(tea, (u->f.frequency * 125) / 2); @@ -348,21 +394,27 @@ static int tea5761_do_ioctl(struct inode case VIDIOC_G_CTRL: dev_dbg(&client->dev, "VIDIOC_G_CTRL %d\n", u->ct.id); - if (u->ct.id != V4L2_CID_AUDIO_MUTE) + if (u->ct.id == V4L2_CID_FM_BAND) + u->ct.value = tea5761_get_fm_band(tea); + else if (u->ct.id == V4L2_CID_AUDIO_MUTE) { + if (r->tnctrl & TEA5761_TNCTRL_PUPD0) + u->ct.value = tea5761_is_muted(tea) ? 1 : 0; + else + u->ct.value = 0; + } else return -EINVAL; - if (r->tnctrl & TEA5761_TNCTRL_PUPD0) - u->ct.value = tea5761_is_muted(tea) ? 1 : 0; - else - u->ct.value = 0; break; - + case VIDIOC_S_CTRL: dev_dbg(&client->dev, "VIDIOC_S_CTRL %d\n", u->ct.id); - if (u->ct.id != V4L2_CID_AUDIO_MUTE) - return -EINVAL; - tea5761_mute(tea, u->ct.value); + if (u->ct.id == V4L2_CID_FM_BAND) + tea5761_set_fm_band(tea, u->ct.value); + else if (u->ct.id == V4L2_CID_AUDIO_MUTE) + tea5761_mute(tea, u->ct.value); + else + return -EINVAL; break; - + default: return -ENOIOCTLCMD; } diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/drivers/media/video/omap/omap24xxcam.c kernel-source-diablo-2.6.21/kernel-source/drivers/media/video/omap/omap24xxcam.c --- kernel-source-diablo-2.6.21.orig/kernel-source/drivers/media/video/omap/omap24xxcam.c 2009-07-01 17:15:49.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/drivers/media/video/omap/omap24xxcam.c 2009-07-01 17:29:10.000000000 +0100 @@ -1064,6 +1064,7 @@ static int vidioc_dqbuf(struct file *fil struct videobuf_buffer *vb; int rval; +videobuf_dqbuf_again: rval = videobuf_dqbuf(&ofh->vbq, b, file->f_flags & O_NONBLOCK); if (rval) goto out; @@ -1082,11 +1083,21 @@ static int vidioc_dqbuf(struct file *fil schedule_work(&cam->sensor_reset_work); } out: - /* This is a hack. User space won't get the index of this - * buffer and does not want to requeue it so we requeue it - * here. */ - if (rval == -EIO) + /* + * This is a hack. We don't want to show -EIO to the user + * space. Requeue the buffer and try again if we're not doing + * this in non-blocking mode. + */ + if (rval == -EIO) { videobuf_qbuf(&ofh->vbq, b); + if (!(file->f_flags & O_NONBLOCK)) + goto videobuf_dqbuf_again; + /* + * We don't have a videobuf_buffer now --- maybe next + * time... + */ + rval = -EAGAIN; + } return rval; } diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/drivers/misc/Kconfig kernel-source-diablo-2.6.21/kernel-source/drivers/misc/Kconfig --- kernel-source-diablo-2.6.21.orig/kernel-source/drivers/misc/Kconfig 2007-10-02 08:09:31.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/drivers/misc/Kconfig 2009-07-01 17:29:10.000000000 +0100 @@ -122,4 +122,11 @@ config SONY_LAPTOP Read for more information. +config POWERVR + tristate "PowerVR MBX test driver for OMAP2420" + depends on ARCH_OMAP2420 + ---help--- + For now this driver only turns on the clocks and power to + the PowerVR MBX peripheral integrated into OMAP2420 CPUs. + endmenu diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/drivers/misc/Makefile kernel-source-diablo-2.6.21/kernel-source/drivers/misc/Makefile --- kernel-source-diablo-2.6.21.orig/kernel-source/drivers/misc/Makefile 2007-10-02 08:09:31.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/drivers/misc/Makefile 2009-07-01 17:29:10.000000000 +0100 @@ -12,3 +12,5 @@ obj-$(CONFIG_TIFM_CORE) += tifm_c obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o obj-$(CONFIG_SGI_IOC4) += ioc4.o obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o +obj-$(CONFIG_POWERVR) += powervr.o + diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/drivers/misc/powervr.c kernel-source-diablo-2.6.21/kernel-source/drivers/misc/powervr.c --- kernel-source-diablo-2.6.21.orig/kernel-source/drivers/misc/powervr.c 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/drivers/misc/powervr.c 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2008 Philipp Zabel + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include +#include +#include +#include +#include /* udelay */ + +#include + +#define CM_CLKSEL_GFX 0x340 +#define PRCM_CLKCFG_STATUS 0x84 +#define PRCM_CLKCFG_CTRL 0x80 +#define CM_ICLKEN_GFX 0x310 +#define CM_FCLKEN_GFX 0x300 +#define CM_CLKSTCTRL_GFX 0x348 +#define AUTOSTAT_GFX (1 << 0) +#define RM_RSTST_GFX 0x358 +#define OMAP24XX_GFX_SW_RST 0x10 +#define RM_RSTCTRL_GFX 0x350 +#define OMAP24XX_GFX_RESET 0x1 +#define PM_WKDEP_GFX 0x3c8 +#define EN_WKUP (1 << 4) +#define EN_GFX (1 << 3) +#define EN_DSP (1 << 2) +#define EN_MPU (1 << 1) +#define EN_CORE (1 << 0) +#define PM_PWSTCTRL_GFX 0x3e0 +#define FORCE_STATE 0x40000 +#define POWERSTATE_ON 0 +#define POWERSTATE_OFF 3 +#define PM_PWSTST_GFX 0x3e4 + +#define OMAP24XX_PVR_BASE 0x50000000 /* 0x4000 */ +#define PVR_TASLAVEPORT 0x50008000 /* 0x2000 */ +#define PVR_TACONTROLSLAVEPORT 0x5000c000 /* 0x2000 */ +#define PVR_2DSLAVEPORT 0x5000a000 /* 0x2000 */ +#define PVR_SOCREGS 0x4808a000 + +/* from arch/arm/mach-omap2/pm.c */ +static u32 prcm_base = IO_ADDRESS(OMAP24XX_PRCM_BASE); + +static inline void prcm_write_reg(int idx, u32 val) +{ + __raw_writel(val, prcm_base + idx); +} + +static inline u32 prcm_read_reg(int idx) +{ + return __raw_readl(prcm_base + idx); +} + +static void __iomem *pvr_base; + +static inline void pvr_write_reg(int idx, u32 val) +{ + __raw_writel(val, pvr_base + idx); +} + +static inline u32 pvr_read_reg(int idx) +{ + return __raw_readl(pvr_base + idx); +} + +static struct clk *gfx_3d_fck; +static struct clk *gfx_2d_fck; +static struct clk *gfx_ick; + +int enable_system_clocks(void) +{ + gfx_ick = clk_get(NULL, "gfx_ick"); + if (!gfx_ick) + goto fail_ick; + gfx_3d_fck = clk_get(NULL, "gfx_3d_fck"); + if (!gfx_3d_fck) + goto fail_3d_fck; + gfx_2d_fck = clk_get(NULL, "gfx_2d_fck"); + if (!gfx_2d_fck) + goto fail_2d_fck; + + prcm_write_reg(PM_PWSTCTRL_GFX, POWERSTATE_ON); + prcm_write_reg(PM_WKDEP_GFX, 0x13 /* EN_WKUP|EN_MPU|EN_CORE */); + + pr_info("PM_WKDEP_GFX = %x\n", prcm_read_reg(PM_WKDEP_GFX)); + pr_info("CM_CLKSTCTRL_GFX = %x\n", prcm_read_reg(CM_CLKSTCTRL_GFX)); + pr_info("PM_PWSTST_GFX = %x\n", prcm_read_reg(PM_PWSTST_GFX)); + + //pr_info("CM_CLKSEL_GFX = %x\n", prcm_read_reg(CM_CLKSEL_GFX)); + //prcm_write_reg(CM_CLKSEL_GFX, 0x2); +#if 0 + /* DELAYED_APP flag? */ + r0 = HostReadHWReg(pvClkCtrl, 0x84); // OMAP24XX_PRCM_CLKCFG_STATUS + if unlikely(!(r0 & 1)) + HostWriteHWReg(pvClkCtrl, 0x80, 1); // OMAP24XX_PRCM_CLKCFG_CTRL +#endif + + clk_enable(gfx_ick); + clk_enable(gfx_3d_fck); + clk_enable(gfx_2d_fck); + + prcm_write_reg(RM_RSTST_GFX, OMAP24XX_GFX_SW_RST); + prcm_write_reg(RM_RSTCTRL_GFX, OMAP24XX_GFX_RESET); + prcm_write_reg(RM_RSTCTRL_GFX, 0); + + if (!(prcm_read_reg(RM_RSTST_GFX) & OMAP24XX_GFX_SW_RST)) { + pr_info("waiting 1ms\n"); + udelay(1000); + } + prcm_write_reg(RM_RSTST_GFX, OMAP24XX_GFX_SW_RST); + + return 0; + + clk_put(gfx_2d_fck); + gfx_2d_fck = NULL; +fail_2d_fck: + clk_put(gfx_3d_fck); + gfx_3d_fck = NULL; +fail_3d_fck: + clk_put(gfx_ick); + gfx_ick = NULL; +fail_ick: + return -ENOMEM; +} + +void disable_system_clocks(void) +{ + prcm_write_reg(RM_RSTCTRL_GFX, OMAP24XX_GFX_RESET); + + clk_disable(gfx_2d_fck); + clk_disable(gfx_3d_fck); + clk_disable(gfx_ick); + + prcm_write_reg(PM_WKDEP_GFX, 0); + prcm_write_reg(PM_PWSTCTRL_GFX, FORCE_STATE | POWERSTATE_OFF); + + clk_put(gfx_2d_fck); + gfx_2d_fck = NULL; + clk_put(gfx_3d_fck); + gfx_3d_fck = NULL; + clk_put(gfx_ick); + gfx_ick = NULL; +} + +static int __init powervr_init(void) +{ + int ret; + + pvr_base = ioremap(OMAP24XX_PVR_BASE, 0x4000); + if (!pvr_base) + return -ENOMEM; + + ret = enable_system_clocks(); + if (ret == 0) { + int core_id; + int revision; + int version_minor; + int version_major; + char *name; + + pr_info("F00 = %x\n", pvr_read_reg(0xf00)); + pr_info("F10 = %x\n", pvr_read_reg(0xf10)); + + core_id = (pvr_read_reg(0xf00) >> 16) & 0xff; /* 00ff0000 */ + revision = pvr_read_reg(0xf00) & 0xffff; /* 0000ffff */ + version_minor = (pvr_read_reg(0xf10) >> 8) & 0xff; /* 0000ff00 */ + version_major = (pvr_read_reg(0xf10) >> 16) & 0xff; /* 00ff0000 */ + + switch (core_id) { + case 1: name = ""; break; + case 2: name = " Lite"; break; + default: + return -ENODEV; + } + pr_info("core: PowerVR MBX%s, revision %d, version %d.%d\n", name, revision, version_major, version_minor); + + pr_info("F04 = %x\n", pvr_read_reg(0xf04)); + pr_info("FE0 = %x\n", pvr_read_reg(0xfe0)); + pr_info("FE4 = %x\n", pvr_read_reg(0xfe4)); + pr_info("FF0 = %x\n", pvr_read_reg(0xff0)); + pr_info("FF4 = %x\n", pvr_read_reg(0xff4)); + pr_info("FF8 = %x\n", pvr_read_reg(0xff8)); + pr_info("FFC = %x\n", pvr_read_reg(0xffc)); + + pr_info("80 = %x\n", pvr_read_reg(0x80)); + pr_info("12c = %x\n", pvr_read_reg(0x12c)); + pr_info("130 = %x\n", pvr_read_reg(0x130)); + pr_info("134 = %x\n", pvr_read_reg(0x134)); + + pr_info("powervr: driver loaded.\n"); + } + return ret; +} + +static void __exit powervr_cleanup(void) +{ + disable_system_clocks(); + iounmap(pvr_base); + pr_info("powervr: driver unloaded.\n"); +} + +module_init(powervr_init); +module_exit(powervr_cleanup); + +MODULE_AUTHOR("Philipp Zabel"); +MODULE_DESCRIPTION("PowerVR MBX test driver for OMAP2420"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/drivers/mmc/mmc.c kernel-source-diablo-2.6.21/kernel-source/drivers/mmc/mmc.c --- kernel-source-diablo-2.6.21.orig/kernel-source/drivers/mmc/mmc.c 2009-07-01 17:15:49.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/drivers/mmc/mmc.c 2009-07-01 17:29:10.000000000 +0100 @@ -423,6 +423,14 @@ static inline void mmc_set_ios(struct mm ios->power_mode, ios->chip_select, ios->vdd, ios->bus_width, ios->timing); +#ifdef CONFIG_MMC_DEBUG + printk("%s: clock %uHz busmode %u powermode %u cs %u Vdd %u " + "width %u timing %u\n", + mmc_hostname(host), ios->clock, ios->bus_mode, + ios->power_mode, ios->chip_select, ios->vdd, + ios->bus_width, ios->timing); +#endif + host->ops->set_ios(host, ios); } diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/drivers/mmc/mmc_block.c kernel-source-diablo-2.6.21/kernel-source/drivers/mmc/mmc_block.c --- kernel-source-diablo-2.6.21.orig/kernel-source/drivers/mmc/mmc_block.c 2009-07-01 17:15:49.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/drivers/mmc/mmc_block.c 2009-07-01 17:29:10.000000000 +0100 @@ -463,7 +463,7 @@ static struct mmc_blk_data *mmc_blk_allo struct mmc_blk_data *md; int devidx, ret; - devidx = find_first_zero_bit(dev_use, MMC_NUM_MINORS); + devidx = find_next_zero_bit(dev_use, MMC_NUM_MINORS, card->host->first_devidx); if (devidx >= MMC_NUM_MINORS) return ERR_PTR(-ENOSPC); __set_bit(devidx, dev_use); diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/drivers/mmc/omap.c kernel-source-diablo-2.6.21/kernel-source/drivers/mmc/omap.c --- kernel-source-diablo-2.6.21.orig/kernel-source/drivers/mmc/omap.c 2009-07-01 17:15:49.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/drivers/mmc/omap.c 2009-07-01 17:29:10.000000000 +0100 @@ -1377,7 +1377,7 @@ static int __init mmc_omap_new_slot(stru mmc->caps = MMC_CAP_BYTEBLOCK | MMC_CAP_MULTIWRITE; /* High speed modes are not officially supported. */ - /* mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED; */ + mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED; mmc->caps |= MMC_CAP_STANDBY; if (host->pdata->conf.wire4) @@ -1395,8 +1395,10 @@ static int __init mmc_omap_new_slot(stru mmc->f_max = 48000000; else mmc->f_max = 24000000; +#if 0 if (host->pdata->max_freq) mmc->f_max = min(host->pdata->max_freq, mmc->f_max); +#endif mmc->ocr_avail = slot->pdata->ocr_mask; /* @@ -1416,6 +1418,8 @@ static int __init mmc_omap_new_slot(stru return r; if (slot->pdata->name != NULL) { + if (slot->pdata->name[0]=='e') + mmc->first_devidx=1; /* 'e'xternal slot should start at mmcblk1 */ r = device_create_file(&mmc->class_dev, &dev_attr_slot_name); if (r < 0) goto err_remove_host; diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/drivers/usb/core/generic.c kernel-source-diablo-2.6.21/kernel-source/drivers/usb/core/generic.c --- kernel-source-diablo-2.6.21.orig/kernel-source/drivers/usb/core/generic.c 2009-07-01 17:15:49.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/drivers/usb/core/generic.c 2009-07-01 17:29:10.000000000 +0100 @@ -97,12 +97,13 @@ static int choose_configuration(struct u * accepted. */ +#if 0 /* Rule out configs that draw too much bus current */ if (c->desc.bMaxPower * 2 > udev->bus_mA) { insufficient_power++; continue; } - +#endif /* When the first config's first interface is one of Microsoft's * pet nonstandard Ethernet-over-USB protocols, ignore it unless * this kernel has enabled the necessary host side driver. diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/drivers/usb/musb/musb_core.c kernel-source-diablo-2.6.21/kernel-source/drivers/usb/musb/musb_core.c --- kernel-source-diablo-2.6.21.orig/kernel-source/drivers/usb/musb/musb_core.c 2009-07-01 17:15:49.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/drivers/usb/musb/musb_core.c 2009-07-01 17:29:10.000000000 +0100 @@ -1694,16 +1694,30 @@ musb_mode_store(struct device *dev, stru { struct musb *musb = dev_to_musb(dev); unsigned long flags; + static int last_state; + int new_state = 0; + //reset driver only if we are changing states... seems to fix id pullup sticking + if (!strncmp(buf, "host", 4)) new_state = 3; + if (!strncmp(buf, "peripheral", 10)) new_state = 2; + if (!strncmp(buf, "otg", 3)) new_state = 1; + if (!last_state) last_state = 1; + + if (!(last_state == new_state)){ + musb_stop(musb); + musb_start(musb); + + spin_lock_irqsave(&musb->lock, flags); + if (!strncmp(buf, "host", 4)) + musb_platform_set_mode(musb, MUSB_HOST); + if (!strncmp(buf, "peripheral", 10)) + musb_platform_set_mode(musb, MUSB_PERIPHERAL); + if (!strncmp(buf, "otg", 3)) + musb_platform_set_mode(musb, MUSB_OTG); - spin_lock_irqsave(&musb->lock, flags); - if (!strncmp(buf, "host", 4)) - musb_platform_set_mode(musb, MUSB_HOST); - if (!strncmp(buf, "peripheral", 10)) - musb_platform_set_mode(musb, MUSB_PERIPHERAL); - if (!strncmp(buf, "otg", 3)) - musb_platform_set_mode(musb, MUSB_OTG); - spin_unlock_irqrestore(&musb->lock, flags); + spin_unlock_irqrestore(&musb->lock, flags); + } + last_state = new_state; return n; } static DEVICE_ATTR(mode, 0644, musb_mode_show, musb_mode_store); diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/drivers/usb/musb/musb_gadget_ep0.c kernel-source-diablo-2.6.21/kernel-source/drivers/usb/musb/musb_gadget_ep0.c --- kernel-source-diablo-2.6.21.orig/kernel-source/drivers/usb/musb/musb_gadget_ep0.c 2009-07-01 17:15:49.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/drivers/usb/musb/musb_gadget_ep0.c 2009-07-01 17:29:10.000000000 +0100 @@ -477,6 +477,7 @@ static void ep0_rxstate(struct musb *mus return; musb->ackpend = 0; } + musb_ep_select(musb->mregs, 0); musb_writew(regs, MUSB_CSR0, tmp); } @@ -529,6 +530,7 @@ static void ep0_txstate(struct musb *mus } /* send it out, triggering a "txpktrdy cleared" irq */ + musb_ep_select(musb->mregs, 0); musb_writew(regs, MUSB_CSR0, csr); } diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/drivers/video/omap/blizzard.c kernel-source-diablo-2.6.21/kernel-source/drivers/video/omap/blizzard.c --- kernel-source-diablo-2.6.21.orig/kernel-source/drivers/video/omap/blizzard.c 2009-07-01 17:15:49.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/drivers/video/omap/blizzard.c 2009-07-01 17:29:10.000000000 +0100 @@ -46,6 +46,7 @@ #define BLIZZARD_CLK_SRC 0x0e #define BLIZZARD_MEM_BANK0_ACTIVATE 0x10 #define BLIZZARD_MEM_BANK0_STATUS 0x14 +#define BLIZZARD_PANEL_CONFIGURATION 0x28 #define BLIZZARD_HDISP 0x2a #define BLIZZARD_HNDP 0x2c #define BLIZZARD_VDISP0 0x2e @@ -935,6 +936,62 @@ static int blizzard_set_scale(int plane, return 0; } +static int blizzard_set_rotate(int angle) +{ + u8 l; + struct omapfb_device *fbdev = blizzard.fbdev; + + + if (blizzard.update_mode == OMAPFB_UPDATE_DISABLED) + l = blizzard_reg_cache[BLIZZARD_PANEL_CONFIGURATION / 2]; + else + l = blizzard_read_reg(BLIZZARD_PANEL_CONFIGURATION); + + l &= ~0x03; + + switch (angle) { + case 0: + l = l | 0x00; + break; + case 90: + l = l | 0x03; + break; + case 180: + l = l | 0x02; + break; + case 270: + l = l | 0x01; + break; + default: + return -EINVAL; + } + + if (blizzard.update_mode == OMAPFB_UPDATE_DISABLED) + blizzard_reg_cache[BLIZZARD_PANEL_CONFIGURATION / 2] = l; /* FIXME: don't change cached register */ + else + blizzard_write_reg(BLIZZARD_PANEL_CONFIGURATION, l); + + /* update screen width and height */ + if (l & 0x01) { + blizzard.auto_update_window.width = fbdev->panel->y_res; + blizzard.auto_update_window.height = fbdev->panel->x_res; + blizzard.auto_update_window.out_width = fbdev->panel->y_res; + blizzard.auto_update_window.out_height = fbdev->panel->x_res; + blizzard.screen_width = fbdev->panel->y_res; + blizzard.screen_height = fbdev->panel->x_res; + } + else { + blizzard.auto_update_window.width = fbdev->panel->x_res; + blizzard.auto_update_window.height = fbdev->panel->y_res; + blizzard.auto_update_window.out_width = fbdev->panel->x_res; + blizzard.auto_update_window.out_height = fbdev->panel->y_res; + blizzard.screen_width = fbdev->panel->x_res; + blizzard.screen_height = fbdev->panel->y_res; + } + + return 0; +} + static int blizzard_enable_plane(int plane, int enable) { if (enable) @@ -1356,7 +1413,8 @@ static void blizzard_get_caps(int plane, caps->ctrl |= OMAPFB_CAPS_MANUAL_UPDATE | OMAPFB_CAPS_WINDOW_PIXEL_DOUBLE | OMAPFB_CAPS_WINDOW_SCALE | - OMAPFB_CAPS_WINDOW_OVERLAY; + OMAPFB_CAPS_WINDOW_OVERLAY | + OMAPFB_CAPS_WINDOW_ROTATE; if (blizzard.te_connected) caps->ctrl |= OMAPFB_CAPS_TEARSYNC; caps->wnd_color |= (1 << OMAPFB_COLOR_RGB565) | @@ -1625,6 +1683,7 @@ struct lcd_ctrl blizzard_ctrl = { .setup_plane = blizzard_setup_plane, .set_scale = blizzard_set_scale, .enable_plane = blizzard_enable_plane, + .set_rotate = blizzard_set_rotate, .update_window = blizzard_update_window_async, .sync = blizzard_sync, .suspend = blizzard_suspend, diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/drivers/video/omap/lcd_mipid.c kernel-source-diablo-2.6.21/kernel-source/drivers/video/omap/lcd_mipid.c --- kernel-source-diablo-2.6.21.orig/kernel-source/drivers/video/omap/lcd_mipid.c 2009-07-01 17:15:49.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/drivers/video/omap/lcd_mipid.c 2009-07-01 17:29:10.000000000 +0100 @@ -50,6 +50,13 @@ #define to_mipid_device(p) container_of(p, struct mipid_device, \ panel) +#define BACKLIGHT_USE_TABLE + +#ifdef BACKLIGHT_USE_TABLE +static unsigned char bklight_table[] = {3,25,50,76,101,127}; +#define BACKLIGHT_TABLE_SIZE 6 +#endif + struct mipid_device { int enabled; int model; @@ -72,6 +79,12 @@ struct mipid_device { void (*shutdown)(struct mipid_platform_data *pdata); struct mipid_platform_data *pdata; +#ifdef BACKLIGHT_USE_TABLE + unsigned char bklight_table_src[BACKLIGHT_TABLE_SIZE]; + unsigned char bklight_table_dst[BACKLIGHT_TABLE_SIZE]; + signed int orig_bklight_level; // signed because we use -1 as uninitialized value and we need 0-127 + unsigned char ignore_other_levels; +#endif }; static void mipid_transfer(struct mipid_device *md, int cmd, const u8 *wbuf, @@ -221,8 +234,70 @@ static int mipid_set_bklight_level(struc { struct mipid_device *md = to_mipid_device(panel); +#ifndef BACKLIGHT_USE_TABLE if (level > tahvo_get_max_backlight_level()) return -EINVAL; +#else + int prevlevel=md->orig_bklight_level; // remember for code below + int i,nearestidx=0; + if (level > tahvo_get_max_backlight_level()){ + /* + values greater than valid maximum will change translation table + to map srclevel to dstlevel value 0x80|srclevel + (dstlevel<<8) needs to be written + if srclevel is 0, current active level is changed + if highest bit is set (0x8000) the code will try to kill fading effect = values not in table + will be ignored or rounded to nearest table value to avoid jumps in brightness level + */ + unsigned int srclevel,dstlevel; + //level -= 0x80; //1 + tahvo_get_max_backlight_level(); + md->ignore_other_levels = ((level&0x8000) != 0); + level&=0x7FFF; + srclevel = level & 0x7F; + dstlevel = (level >> 8); + if (dstlevel > tahvo_get_max_backlight_level()) + return -EINVAL; + if (srclevel == 0) + srclevel = md->orig_bklight_level; /* zero means change current level */ +// srclevel is used as table index, not direct value to remap +// remove those 5 lines below to use it as direct value + else { + if (srclevel>BACKLIGHT_TABLE_SIZE) + return -EINVAL; + srclevel=md->bklight_table_src[srclevel-1]; + } +// + for (i=0;ibklight_table_src[i]){ + /* OK, found matching src level, set destination */ + md->bklight_table_dst[i] = dstlevel; + break; + } + } + if (srclevel != md->orig_bklight_level) + return 0; /* not current level */ + + /* we are changing current active level, continue */ + level = srclevel; + } else + md->orig_bklight_level = level; + /* try to translate level */ + for (i=0;ibklight_table_src[i]){ + level = md->bklight_table_dst[i]; + break; + } else // doesn not match, remember nearest + if (level>md->bklight_table_src[i]) nearestidx=i; + } + if (level && md->ignore_other_levels && i==BACKLIGHT_TABLE_SIZE) { + // not zero, not found in table, try to kill fading effect + //return 0; // simply ignore + // skip directly to nearest in table + if (level>prevlevel) // increase ? + level=md->bklight_table_dst[nearestidx+1]; + else + level=md->bklight_table_dst[nearestidx]; + } +#endif if (!md->enabled) { md->saved_bklight_level = level; return 0; @@ -234,7 +309,15 @@ static int mipid_set_bklight_level(struc static unsigned int mipid_get_bklight_level(struct lcd_panel *panel) { +#ifdef BACKLIGHT_USE_TABLE + struct mipid_device *md = to_mipid_device(panel); + if (md->orig_bklight_level>=0) + return md->orig_bklight_level; // ok, orig value initialized, return it + else + return (md->orig_bklight_level=tahvo_get_backlight_level()); // read real value and initialize orig_bklight_level +#else return tahvo_get_backlight_level(); +#endif } static unsigned int mipid_get_bklight_max(struct lcd_panel *panel) @@ -466,6 +549,12 @@ static int mipid_init(struct lcd_panel * struct mipid_device *md = to_mipid_device(panel); md->fbdev = fbdev; +#ifdef BACKLIGHT_USE_TABLE + /* initialize translation table */ + memcpy(md->bklight_table_src,bklight_table,BACKLIGHT_TABLE_SIZE); + memcpy(md->bklight_table_dst,bklight_table,BACKLIGHT_TABLE_SIZE); + md->orig_bklight_level=-1; +#endif md->esd_wq = create_singlethread_workqueue("mipid_esd"); if (md->esd_wq == NULL) { dev_err(&md->spi->dev, "can't create ESD workqueue\n"); diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/drivers/video/omap/omapfb_main.c kernel-source-diablo-2.6.21/kernel-source/drivers/video/omap/omapfb_main.c --- kernel-source-diablo-2.6.21.orig/kernel-source/drivers/video/omap/omapfb_main.c 2009-07-01 17:15:49.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/drivers/video/omap/omapfb_main.c 2009-07-01 17:29:10.000000000 +0100 @@ -66,6 +66,7 @@ static struct caps_table_struct ctrl_cap { OMAPFB_CAPS_WINDOW_PIXEL_DOUBLE, "pixel double window" }, { OMAPFB_CAPS_WINDOW_SCALE, "scale window" }, { OMAPFB_CAPS_WINDOW_OVERLAY,"overlay window" }, + { OMAPFB_CAPS_WINDOW_ROTATE, "rotate window" }, { OMAPFB_CAPS_SET_BACKLIGHT, "backlight setting" }, }; @@ -245,6 +246,13 @@ static int ctrl_change_mode(struct fb_in offset, var->xres_virtual, plane->info.pos_x, plane->info.pos_y, var->xres, var->yres, plane->color_mode); + if (r < 0) + return r; + + if (fbdev->ctrl->set_rotate != NULL) + if((r = fbdev->ctrl->set_rotate(var->rotate)) < 0) + return r; + if (fbdev->ctrl->set_scale != NULL) r = fbdev->ctrl->set_scale(plane->idx, var->xres, var->yres, @@ -626,7 +634,7 @@ static void omapfb_rotate(struct fb_info struct omapfb_device *fbdev = plane->fbdev; omapfb_rqueue_lock(fbdev); - if (cpu_is_omap15xx() && rotate != fbi->var.rotate) { + if (rotate != fbi->var.rotate) { struct fb_var_screeninfo *new_var = &fbdev->new_var; memcpy(new_var, &fbi->var, sizeof(*new_var)); @@ -730,28 +738,42 @@ int omapfb_update_window_async(struct fb void (*callback)(void *), void *callback_data) { + int xres, yres; struct omapfb_plane_struct *plane = fbi->par; struct omapfb_device *fbdev = plane->fbdev; - struct fb_var_screeninfo *var; + struct fb_var_screeninfo *var = &fbi->var; + + switch (var->rotate) { + case 0: + case 180: + xres = fbdev->panel->x_res; + yres = fbdev->panel->y_res; + break; + case 90: + case 270: + xres = fbdev->panel->y_res; + yres = fbdev->panel->x_res; + break; + default: + return -EINVAL; + } - var = &fbi->var; - if (win->x >= var->xres || win->y >= var->yres || - win->out_x > var->xres || win->out_y >= var->yres) + if (win->x >= xres || win->y >= yres || + win->out_x > xres || win->out_y > yres) return -EINVAL; if (!fbdev->ctrl->update_window || fbdev->ctrl->get_update_mode() != OMAPFB_MANUAL_UPDATE) return -ENODEV; - if (win->x + win->width >= var->xres) - win->width = var->xres - win->x; - if (win->y + win->height >= var->yres) - win->height = var->yres - win->y; - /* The out sizes should be cropped to the LCD size */ - if (win->out_x + win->out_width > fbdev->panel->x_res) - win->out_width = fbdev->panel->x_res - win->out_x; - if (win->out_y + win->out_height > fbdev->panel->y_res) - win->out_height = fbdev->panel->y_res - win->out_y; + if (win->x + win->width > xres) + win->width = xres - win->x; + if (win->y + win->height > yres) + win->height = yres - win->y; + if (win->out_x + win->out_width > xres) + win->out_width = xres - win->out_x; + if (win->out_y + win->out_height > yres) + win->out_height = yres - win->out_y; if (!win->width || !win->height || !win->out_width || !win->out_height) return 0; diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/fs/Kconfig kernel-source-diablo-2.6.21/kernel-source/fs/Kconfig --- kernel-source-diablo-2.6.21.orig/kernel-source/fs/Kconfig 2007-10-02 08:09:32.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/fs/Kconfig 2009-07-01 17:29:10.000000000 +0100 @@ -1034,6 +1034,47 @@ config CONFIGFS_FS endmenu +menu "Layered filesystems" + +config ECRYPT_FS + tristate "eCrypt filesystem layer support (EXPERIMENTAL)" + depends on EXPERIMENTAL && KEYS && CRYPTO && NET + help + Encrypted filesystem that operates on the VFS layer. See + to learn more about + eCryptfs. Userspace components are required and can be + obtained from . + + To compile this file system support as a module, choose M here: the + module will be called ecryptfs. + +config UNION_FS + tristate "Union file system (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + Unionfs is a stackable unification file system, which appears to + merge the contents of several directories (branches), while keeping + their physical content separate. + + See for details + +config UNION_FS_XATTR + bool "Unionfs extended attributes" + depends on UNION_FS + help + Extended attributes are name:value pairs associated with inodes by + the kernel or by users (see the attr(5) manual page). + + If unsure, say N. + +config UNION_FS_DEBUG + bool "Debug Unionfs" + depends on UNION_FS + help + If you say Y here, you can turn on debugging output from Unionfs. + +endmenu + menu "Miscellaneous filesystems" config ADFS_FS @@ -1086,18 +1127,6 @@ config AFFS_FS To compile this file system support as a module, choose M here: the module will be called affs. If unsure, say N. -config ECRYPT_FS - tristate "eCrypt filesystem layer support (EXPERIMENTAL)" - depends on EXPERIMENTAL && KEYS && CRYPTO && NET - help - Encrypted filesystem that operates on the VFS layer. See - to learn more about - eCryptfs. Userspace components are required and can be - obtained from . - - To compile this file system support as a module, choose M here: the - module will be called ecryptfs. - config HFS_FS tristate "Apple Macintosh file system support (EXPERIMENTAL)" depends on BLOCK && EXPERIMENTAL diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/fs/Makefile kernel-source-diablo-2.6.21/kernel-source/fs/Makefile --- kernel-source-diablo-2.6.21.orig/kernel-source/fs/Makefile 2007-10-02 08:09:32.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/fs/Makefile 2009-07-01 17:29:10.000000000 +0100 @@ -80,6 +80,7 @@ obj-$(CONFIG_ISO9660_FS) += isofs/ obj-$(CONFIG_HFSPLUS_FS) += hfsplus/ # Before hfs to find wrapped HFS+ obj-$(CONFIG_HFS_FS) += hfs/ obj-$(CONFIG_ECRYPT_FS) += ecryptfs/ +obj-$(CONFIG_UNION_FS) += unionfs/ obj-$(CONFIG_VXFS_FS) += freevxfs/ obj-$(CONFIG_NFS_FS) += nfs/ obj-$(CONFIG_EXPORTFS) += exportfs/ diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/fs/ecryptfs/dentry.c kernel-source-diablo-2.6.21/kernel-source/fs/ecryptfs/dentry.c --- kernel-source-diablo-2.6.21.orig/kernel-source/fs/ecryptfs/dentry.c 2007-10-02 08:09:32.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/fs/ecryptfs/dentry.c 2009-07-01 17:29:10.000000000 +0100 @@ -62,7 +62,7 @@ static int ecryptfs_d_revalidate(struct struct inode *lower_inode = ecryptfs_inode_to_lower(dentry->d_inode); - fsstack_copy_attr_all(dentry->d_inode, lower_inode, NULL); + fsstack_copy_attr_all(dentry->d_inode, lower_inode); } out: return rc; diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/fs/ecryptfs/inode.c kernel-source-diablo-2.6.21/kernel-source/fs/ecryptfs/inode.c --- kernel-source-diablo-2.6.21.orig/kernel-source/fs/ecryptfs/inode.c 2007-10-02 08:09:32.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/fs/ecryptfs/inode.c 2009-07-01 17:29:10.000000000 +0100 @@ -280,7 +280,9 @@ static struct dentry *ecryptfs_lookup(st int rc = 0; struct dentry *lower_dir_dentry; struct dentry *lower_dentry; + struct dentry *dentry_save; struct vfsmount *lower_mnt; + struct vfsmount *mnt_save; char *encoded_name; unsigned int encoded_namelen; struct ecryptfs_crypt_stat *crypt_stat = NULL; @@ -308,9 +310,13 @@ static struct dentry *ecryptfs_lookup(st } ecryptfs_printk(KERN_DEBUG, "encoded_name = [%s]; encoded_namelen " "= [%d]\n", encoded_name, encoded_namelen); - lower_dentry = lookup_one_len(encoded_name, lower_dir_dentry, - encoded_namelen - 1); + dentry_save = nd->dentry; + mnt_save = nd->mnt; + lower_dentry = lookup_one_len_nd(encoded_name, lower_dir_dentry, + (encoded_namelen - 1), nd); kfree(encoded_name); + nd->mnt = mnt_save; + nd->dentry = dentry_save; if (IS_ERR(lower_dentry)) { ecryptfs_printk(KERN_ERR, "ERR from lower_dentry\n"); rc = PTR_ERR(lower_dentry); @@ -597,9 +603,9 @@ ecryptfs_rename(struct inode *old_dir, s lower_new_dir_dentry->d_inode, lower_new_dentry); if (rc) goto out_lock; - fsstack_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode, NULL); + fsstack_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode); if (new_dir != old_dir) - fsstack_copy_attr_all(old_dir, lower_old_dir_dentry->d_inode, NULL); + fsstack_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->d_parent); @@ -892,7 +898,7 @@ static int ecryptfs_setattr(struct dentr } rc = notify_change(lower_dentry, ia); out: - fsstack_copy_attr_all(inode, lower_inode, NULL); + fsstack_copy_attr_all(inode, lower_inode); return rc; } diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/fs/ecryptfs/main.c kernel-source-diablo-2.6.21/kernel-source/fs/ecryptfs/main.c --- kernel-source-diablo-2.6.21.orig/kernel-source/fs/ecryptfs/main.c 2007-10-02 08:09:32.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/fs/ecryptfs/main.c 2009-07-01 17:29:10.000000000 +0100 @@ -151,7 +151,7 @@ int ecryptfs_interpose(struct dentry *lo d_add(dentry, inode); else d_instantiate(dentry, inode); - fsstack_copy_attr_all(inode, lower_inode, NULL); + fsstack_copy_attr_all(inode, lower_inode); /* This size will be overwritten for real files w/ headers and * other metadata */ fsstack_copy_inode_size(inode, lower_inode); diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/fs/namei.c kernel-source-diablo-2.6.21/kernel-source/fs/namei.c --- kernel-source-diablo-2.6.21.orig/kernel-source/fs/namei.c 2007-10-02 08:09:32.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/fs/namei.c 2009-07-01 17:29:10.000000000 +0100 @@ -1125,6 +1125,10 @@ static int fastcall do_path_lookup(int d nd->mnt = mntget(fs->rootmnt); nd->dentry = dget(fs->root); read_unlock(&fs->lock); + } else if (flags & LOOKUP_ONE) { + /* nd->mnt and nd->dentry already set, just grab references */ + mntget(nd->mnt); + dget(nd->dentry); } else if (dfd == AT_FDCWD) { read_lock(&fs->lock); nd->mnt = mntget(fs->pwdmnt); @@ -1175,6 +1179,37 @@ int fastcall path_lookup(const char *nam return do_path_lookup(AT_FDCWD, name, flags, nd); } +/** + * vfs_path_lookup - lookup a file path relative to a dentry-vfsmount pair + * @dentry: pointer to dentry of the base directory + * @mnt: pointer to vfs mount of the base directory + * @name: pointer to file name + * @flags: lookup flags + * @nd: pointer to nameidata + */ +int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt, + const char *name, unsigned int flags, + struct nameidata *nd) +{ + int retval; + + /* same as do_path_lookup */ + nd->last_type = LAST_ROOT; + nd->flags = flags; + nd->depth = 0; + + nd->mnt = mntget(mnt); + nd->dentry = dget(dentry); + + retval = path_walk(name, nd); + if (unlikely(!retval && !audit_dummy_context() && nd->dentry && + nd->dentry->d_inode)) + audit_inode(name, nd->dentry->d_inode); + + return retval; + +} + static int __path_lookup_intent_open(int dfd, const char *name, unsigned int lookup_flags, struct nameidata *nd, int open_flags, int create_mode) @@ -1293,29 +1328,37 @@ static struct dentry *lookup_hash(struct } /* SMP-safe */ -struct dentry * lookup_one_len(const char * name, struct dentry * base, int len) +static inline int __lookup_one_len(const char *name, struct qstr *this, struct dentry *base, int len) { unsigned long hash; - struct qstr this; unsigned int c; - this.name = name; - this.len = len; + this->name = name; + this->len = len; if (!len) - goto access; + return -EACCES; hash = init_name_hash(); while (len--) { c = *(const unsigned char *)name++; if (c == '/' || c == '\0') - goto access; + return -EACCES; hash = partial_name_hash(c, hash); } - this.hash = end_name_hash(hash); + this->hash = end_name_hash(hash); + return 0; +} - return __lookup_hash(&this, base, NULL); -access: - return ERR_PTR(-EACCES); +struct dentry *lookup_one_len_nd(const char *name, struct dentry *base, + int len, struct nameidata *nd) +{ + int err; + struct qstr this; + + err = __lookup_one_len(name, &this, base, len); + if (err) + return ERR_PTR(err); + return __lookup_hash(&this, base, nd); } /* @@ -2758,7 +2801,7 @@ EXPORT_SYMBOL(follow_up); EXPORT_SYMBOL(get_write_access); /* binfmt_aout */ EXPORT_SYMBOL(getname); EXPORT_SYMBOL(lock_rename); -EXPORT_SYMBOL(lookup_one_len); +EXPORT_SYMBOL(lookup_one_len_nd); EXPORT_SYMBOL(page_follow_link_light); EXPORT_SYMBOL(page_put_link); EXPORT_SYMBOL(page_readlink); @@ -2766,6 +2809,7 @@ EXPORT_SYMBOL(__page_symlink); EXPORT_SYMBOL(page_symlink); EXPORT_SYMBOL(page_symlink_inode_operations); EXPORT_SYMBOL(path_lookup); +EXPORT_SYMBOL(vfs_path_lookup); EXPORT_SYMBOL(path_release); EXPORT_SYMBOL(path_walk); EXPORT_SYMBOL(permission); diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/fs/splice.c kernel-source-diablo-2.6.21/kernel-source/fs/splice.c --- kernel-source-diablo-2.6.21.orig/kernel-source/fs/splice.c 2007-10-02 08:09:32.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/fs/splice.c 2009-07-01 17:29:10.000000000 +0100 @@ -898,8 +898,8 @@ EXPORT_SYMBOL(generic_splice_sendpage); /* * Attempt to initiate a splice from pipe to file. */ -static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, - loff_t *ppos, size_t len, unsigned int flags) +long vfs_splice_from(struct pipe_inode_info *pipe, struct file *out, + loff_t *ppos, size_t len, unsigned int flags) { int ret; @@ -915,13 +915,14 @@ static long do_splice_from(struct pipe_i return out->f_op->splice_write(pipe, out, ppos, len, flags); } +EXPORT_SYMBOL_GPL(vfs_splice_from); /* * Attempt to initiate a splice from a file to a pipe. */ -static long do_splice_to(struct file *in, loff_t *ppos, - struct pipe_inode_info *pipe, size_t len, - unsigned int flags) +long vfs_splice_to(struct file *in, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, + unsigned int flags) { loff_t isize, left; int ret; @@ -946,6 +947,7 @@ static long do_splice_to(struct file *in return in->f_op->splice_read(in, ppos, pipe, len, flags); } +EXPORT_SYMBOL_GPL(vfs_splice_to); long do_splice_direct(struct file *in, loff_t *ppos, struct file *out, size_t len, unsigned int flags) @@ -1000,7 +1002,7 @@ long do_splice_direct(struct file *in, l */ max_read_len = min(len, (size_t)(PIPE_BUFFERS*PAGE_SIZE)); - ret = do_splice_to(in, ppos, pipe, max_read_len, flags); + ret = vfs_splice_to(in, ppos, pipe, max_read_len, flags); if (unlikely(ret < 0)) goto out_release; @@ -1011,8 +1013,8 @@ long do_splice_direct(struct file *in, l * must not do the output in nonblocking mode as then we * could get stuck data in the internal pipe: */ - ret = do_splice_from(pipe, out, &out_off, read_len, - flags & ~SPLICE_F_NONBLOCK); + ret = vfs_splice_from(pipe, out, &out_off, read_len, + flags & ~SPLICE_F_NONBLOCK); if (unlikely(ret < 0)) goto out_release; @@ -1098,7 +1100,7 @@ static long do_splice(struct file *in, l } else off = &out->f_pos; - ret = do_splice_from(pipe, out, off, len, flags); + ret = vfs_splice_from(pipe, out, off, len, flags); if (off_out && copy_to_user(off_out, off, sizeof(loff_t))) ret = -EFAULT; @@ -1119,7 +1121,7 @@ static long do_splice(struct file *in, l } else off = &in->f_pos; - ret = do_splice_to(in, off, pipe, len, flags); + ret = vfs_splice_to(in, off, pipe, len, flags); if (off_in && copy_to_user(off_in, off, sizeof(loff_t))) ret = -EFAULT; diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/fs/stack.c kernel-source-diablo-2.6.21/kernel-source/fs/stack.c --- kernel-source-diablo-2.6.21.orig/kernel-source/fs/stack.c 2007-10-02 08:09:32.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/fs/stack.c 2009-07-01 17:29:10.000000000 +0100 @@ -1,24 +1,82 @@ +/* + * Copyright (c) 2006-2009 Erez Zadok + * Copyright (c) 2006-2007 Josef 'Jeff' Sipek + * Copyright (c) 2006-2009 Stony Brook University + * Copyright (c) 2006-2009 The Research Foundation of SUNY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + #include #include #include -/* does _NOT_ require i_mutex to be held. +/* + * does _NOT_ require i_mutex to be held. * * This function cannot be inlined since i_size_{read,write} is rather * heavy-weight on 32-bit systems */ -void fsstack_copy_inode_size(struct inode *dst, const struct inode *src) +void fsstack_copy_inode_size(struct inode *dst, struct inode *src) { - i_size_write(dst, i_size_read((struct inode *)src)); - dst->i_blocks = src->i_blocks; + loff_t i_size; + blkcnt_t i_blocks; + + /* + * i_size_read() includes its own seqlocking and protection from + * preemption (see include/linux/fs.h): we need nothing extra for + * that here, and prefer to avoid nesting locks than attempt to + * keep i_size and i_blocks in synch together. + */ + i_size = i_size_read(src); + + /* + * But if CONFIG_LSF (on 32-bit), we ought to make an effort to keep + * the two halves of i_blocks in synch despite SMP or PREEMPT - though + * stat's generic_fillattr() doesn't bother, and we won't be applying + * quotas (where i_blocks does become important) at the upper level. + * + * We don't actually know what locking is used at the lower level; but + * if it's a filesystem that supports quotas, it will be using i_lock + * as in inode_add_bytes(). tmpfs uses other locking, and its 32-bit + * is (just) able to exceed 2TB i_size with the aid of holes; but its + * i_blocks cannot carry into the upper long without almost 2TB swap - + * let's ignore that case. + */ + if (sizeof(i_blocks) > sizeof(long)) + spin_lock(&src->i_lock); + i_blocks = src->i_blocks; + if (sizeof(i_blocks) > sizeof(long)) + spin_unlock(&src->i_lock); + + /* + * If CONFIG_SMP on 32-bit, it's vital for fsstack_copy_inode_size() + * to hold some lock around i_size_write(), otherwise i_size_read() + * may spin forever (see include/linux/fs.h). We don't necessarily + * hold i_mutex when this is called, so take i_lock for that case. + * + * And if CONFIG_LSF (on 32-bit), continue our effort to keep the + * two halves of i_blocks in synch despite SMP or PREEMPT: use i_lock + * for that case too, and do both at once by combining the tests. + * + * There is none of this locking overhead in the 64-bit case. + */ + if (sizeof(i_size) > sizeof(long) || sizeof(i_blocks) > sizeof(long)) + spin_lock(&dst->i_lock); + i_size_write(dst, i_size); + dst->i_blocks = i_blocks; + if (sizeof(i_size) > sizeof(long) || sizeof(i_blocks) > sizeof(long)) + spin_unlock(&dst->i_lock); } EXPORT_SYMBOL_GPL(fsstack_copy_inode_size); -/* copy all attributes; get_nlinks is optional way to override the i_nlink +/* + * copy all attributes; get_nlinks is optional way to override the i_nlink * copying */ -void fsstack_copy_attr_all(struct inode *dest, const struct inode *src, - int (*get_nlinks)(struct inode *)) +void fsstack_copy_attr_all(struct inode *dest, const struct inode *src) { dest->i_mode = src->i_mode; dest->i_uid = src->i_uid; @@ -29,14 +87,6 @@ void fsstack_copy_attr_all(struct inode dest->i_ctime = src->i_ctime; dest->i_blkbits = src->i_blkbits; dest->i_flags = src->i_flags; - - /* - * Update the nlinks AFTER updating the above fields, because the - * get_links callback may depend on them. - */ - if (!get_nlinks) - dest->i_nlink = src->i_nlink; - else - dest->i_nlink = (*get_nlinks)(dest); + dest->i_nlink = src->i_nlink; } EXPORT_SYMBOL_GPL(fsstack_copy_attr_all); diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/Makefile kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/Makefile --- kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/Makefile 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,17 @@ +UNIONFS_VERSION="2.5.2 (for 2.6.21.7)" + +EXTRA_CFLAGS += -DUNIONFS_VERSION=\"$(UNIONFS_VERSION)\" + +obj-$(CONFIG_UNION_FS) += unionfs.o + +unionfs-y := subr.o dentry.o file.o inode.o main.o super.o \ + rdstate.o copyup.o dirhelper.o rename.o unlink.o \ + lookup.o commonfops.o dirfops.o sioq.o mmap.o whiteout.o + +unionfs-$(CONFIG_UNION_FS_XATTR) += xattr.o + +unionfs-$(CONFIG_UNION_FS_DEBUG) += debug.o + +ifeq ($(CONFIG_UNION_FS_DEBUG),y) +EXTRA_CFLAGS += -DDEBUG +endif diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/commonfops.c kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/commonfops.c --- kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/commonfops.c 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/commonfops.c 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,879 @@ +/* + * Copyright (c) 2003-2009 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2007 Josef 'Jeff' Sipek + * Copyright (c) 2005-2006 Junjiro Okajima + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2004-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2009 Stony Brook University + * Copyright (c) 2003-2009 The Research Foundation of SUNY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "union.h" + +/* + * 1) Copyup the file + * 2) Rename the file to '.unionfs' - obviously + * stolen from NFS's silly rename + */ +static int copyup_deleted_file(struct file *file, struct dentry *dentry, + struct dentry *parent, int bstart, int bindex) +{ + static unsigned int counter; + const int i_inosize = sizeof(dentry->d_inode->i_ino) * 2; + const int countersize = sizeof(counter) * 2; + const int nlen = sizeof(".unionfs") + i_inosize + countersize - 1; + char name[nlen + 1]; + int err; + struct dentry *tmp_dentry = NULL; + struct dentry *lower_dentry; + struct dentry *lower_dir_dentry = NULL; + + lower_dentry = unionfs_lower_dentry_idx(dentry, bstart); + + sprintf(name, ".unionfs%*.*lx", + i_inosize, i_inosize, lower_dentry->d_inode->i_ino); + + /* + * Loop, looking for an unused temp name to copyup to. + * + * It's somewhat silly that we look for a free temp tmp name in the + * source branch (bstart) instead of the dest branch (bindex), where + * the final name will be created. We _will_ catch it if somehow + * the name exists in the dest branch, but it'd be nice to catch it + * sooner than later. + */ +retry: + tmp_dentry = NULL; + do { + char *suffix = name + nlen - countersize; + + dput(tmp_dentry); + counter++; + sprintf(suffix, "%*.*x", countersize, countersize, counter); + + pr_debug("unionfs: trying to rename %s to %s\n", + dentry->d_name.name, name); + + tmp_dentry = lookup_one_len(name, lower_dentry->d_parent, + nlen); + if (IS_ERR(tmp_dentry)) { + err = PTR_ERR(tmp_dentry); + goto out; + } + } while (tmp_dentry->d_inode != NULL); /* need negative dentry */ + dput(tmp_dentry); + + err = copyup_named_file(parent->d_inode, file, name, bstart, bindex, + i_size_read(file->f_path.dentry->d_inode)); + if (err) { + if (unlikely(err == -EEXIST)) + goto retry; + goto out; + } + + /* bring it to the same state as an unlinked file */ + lower_dentry = unionfs_lower_dentry_idx(dentry, dbstart(dentry)); + if (!unionfs_lower_inode_idx(dentry->d_inode, bindex)) { + atomic_inc(&lower_dentry->d_inode->i_count); + unionfs_set_lower_inode_idx(dentry->d_inode, bindex, + lower_dentry->d_inode); + } + lower_dir_dentry = lock_parent(lower_dentry); + err = vfs_unlink(lower_dir_dentry->d_inode, lower_dentry); + unlock_dir(lower_dir_dentry); + +out: + if (!err) + unionfs_check_dentry(dentry); + return err; +} + +/* + * put all references held by upper struct file and free lower file pointer + * array + */ +static void cleanup_file(struct file *file) +{ + int bindex, bstart, bend; + struct file **lower_files; + struct file *lower_file; + struct super_block *sb = file->f_path.dentry->d_sb; + + lower_files = UNIONFS_F(file)->lower_files; + bstart = fbstart(file); + bend = fbend(file); + + for (bindex = bstart; bindex <= bend; bindex++) { + int i; /* holds (possibly) updated branch index */ + int old_bid; + + lower_file = unionfs_lower_file_idx(file, bindex); + if (!lower_file) + continue; + + /* + * Find new index of matching branch with an open + * file, since branches could have been added or + * deleted causing the one with open files to shift. + */ + old_bid = UNIONFS_F(file)->saved_branch_ids[bindex]; + i = branch_id_to_idx(sb, old_bid); + if (unlikely(i < 0)) { + printk(KERN_ERR "unionfs: no superblock for " + "file %p\n", file); + continue; + } + + /* decrement count of open files */ + branchput(sb, i); + /* + * fput will perform an mntput for us on the correct branch. + * Although we're using the file's old branch configuration, + * bindex, which is the old index, correctly points to the + * right branch in the file's branch list. In other words, + * we're going to mntput the correct branch even if branches + * have been added/removed. + */ + fput(lower_file); + UNIONFS_F(file)->lower_files[bindex] = NULL; + UNIONFS_F(file)->saved_branch_ids[bindex] = -1; + } + + UNIONFS_F(file)->lower_files = NULL; + kfree(lower_files); + kfree(UNIONFS_F(file)->saved_branch_ids); + /* set to NULL because caller needs to know if to kfree on error */ + UNIONFS_F(file)->saved_branch_ids = NULL; +} + +/* open all lower files for a given file */ +static int open_all_files(struct file *file) +{ + int bindex, bstart, bend, err = 0; + struct file *lower_file; + struct dentry *lower_dentry; + struct dentry *dentry = file->f_path.dentry; + struct super_block *sb = dentry->d_sb; + + bstart = dbstart(dentry); + bend = dbend(dentry); + + for (bindex = bstart; bindex <= bend; bindex++) { + lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); + if (!lower_dentry) + continue; + + dget(lower_dentry); + unionfs_mntget(dentry, bindex); + branchget(sb, bindex); + + lower_file = + dentry_open(lower_dentry, + unionfs_lower_mnt_idx(dentry, bindex), + file->f_flags); + if (IS_ERR(lower_file)) { + branchput(sb, bindex); + err = PTR_ERR(lower_file); + goto out; + } else { + unionfs_set_lower_file_idx(file, bindex, lower_file); + } + } +out: + return err; +} + +/* open the highest priority file for a given upper file */ +static int open_highest_file(struct file *file, bool willwrite) +{ + int bindex, bstart, bend, err = 0; + struct file *lower_file; + struct dentry *lower_dentry; + struct dentry *dentry = file->f_path.dentry; + struct dentry *parent = dget_parent(dentry); + struct inode *parent_inode = parent->d_inode; + struct super_block *sb = dentry->d_sb; + + bstart = dbstart(dentry); + bend = dbend(dentry); + + lower_dentry = unionfs_lower_dentry(dentry); + if (willwrite && IS_WRITE_FLAG(file->f_flags) && is_robranch(dentry)) { + for (bindex = bstart - 1; bindex >= 0; bindex--) { + err = copyup_file(parent_inode, file, bstart, bindex, + i_size_read(dentry->d_inode)); + if (!err) + break; + } + atomic_set(&UNIONFS_F(file)->generation, + atomic_read(&UNIONFS_I(dentry->d_inode)-> + generation)); + goto out; + } + + dget(lower_dentry); + unionfs_mntget(dentry, bstart); + lower_file = dentry_open(lower_dentry, + unionfs_lower_mnt_idx(dentry, bstart), + file->f_flags); + if (IS_ERR(lower_file)) { + err = PTR_ERR(lower_file); + goto out; + } + branchget(sb, bstart); + unionfs_set_lower_file(file, lower_file); + /* Fix up the position. */ + lower_file->f_pos = file->f_pos; + + memcpy(&lower_file->f_ra, &file->f_ra, sizeof(struct file_ra_state)); +out: + dput(parent); + return err; +} + +/* perform a delayed copyup of a read-write file on a read-only branch */ +static int do_delayed_copyup(struct file *file, struct dentry *parent) +{ + int bindex, bstart, bend, err = 0; + struct dentry *dentry = file->f_path.dentry; + struct inode *parent_inode = parent->d_inode; + + bstart = fbstart(file); + bend = fbend(file); + + BUG_ON(!S_ISREG(dentry->d_inode->i_mode)); + + unionfs_check_file(file); + for (bindex = bstart - 1; bindex >= 0; bindex--) { + if (!d_deleted(dentry)) + err = copyup_file(parent_inode, file, bstart, + bindex, + i_size_read(dentry->d_inode)); + else + err = copyup_deleted_file(file, dentry, parent, + bstart, bindex); + /* if succeeded, set lower open-file flags and break */ + if (!err) { + struct file *lower_file; + lower_file = unionfs_lower_file_idx(file, bindex); + lower_file->f_flags = file->f_flags; + break; + } + } + if (err || (bstart <= fbstart(file))) + goto out; + bend = fbend(file); + for (bindex = bstart; bindex <= bend; bindex++) { + if (unionfs_lower_file_idx(file, bindex)) { + branchput(dentry->d_sb, bindex); + fput(unionfs_lower_file_idx(file, bindex)); + unionfs_set_lower_file_idx(file, bindex, NULL); + } + } + path_put_lowers(dentry, bstart, bend, false); + iput_lowers(dentry->d_inode, bstart, bend, false); + /* for reg file, we only open it "once" */ + fbend(file) = fbstart(file); + dbend(dentry) = dbstart(dentry); + ibend(dentry->d_inode) = ibstart(dentry->d_inode); + +out: + unionfs_check_file(file); + return err; +} + +/* + * Helper function for unionfs_file_revalidate/locked. + * Expects dentry/parent to be locked already, and revalidated. + */ +static int __unionfs_file_revalidate(struct file *file, struct dentry *dentry, + struct dentry *parent, + struct super_block *sb, int sbgen, + int dgen, bool willwrite) +{ + int fgen; + int bstart, bend, orig_brid; + int size; + int err = 0; + + fgen = atomic_read(&UNIONFS_F(file)->generation); + + /* + * There are two cases we are interested in. The first is if the + * generation is lower than the super-block. The second is if + * someone has copied up this file from underneath us, we also need + * to refresh things. + */ + if (d_deleted(dentry) || + (sbgen <= fgen && + dbstart(dentry) == fbstart(file) && + unionfs_lower_file(file))) + goto out_may_copyup; + + /* save orig branch ID */ + orig_brid = UNIONFS_F(file)->saved_branch_ids[fbstart(file)]; + + /* First we throw out the existing files. */ + cleanup_file(file); + + /* Now we reopen the file(s) as in unionfs_open. */ + bstart = fbstart(file) = dbstart(dentry); + bend = fbend(file) = dbend(dentry); + + size = sizeof(struct file *) * sbmax(sb); + UNIONFS_F(file)->lower_files = kzalloc(size, GFP_KERNEL); + if (unlikely(!UNIONFS_F(file)->lower_files)) { + err = -ENOMEM; + goto out; + } + size = sizeof(int) * sbmax(sb); + UNIONFS_F(file)->saved_branch_ids = kzalloc(size, GFP_KERNEL); + if (unlikely(!UNIONFS_F(file)->saved_branch_ids)) { + err = -ENOMEM; + goto out; + } + + if (S_ISDIR(dentry->d_inode->i_mode)) { + /* We need to open all the files. */ + err = open_all_files(file); + if (err) + goto out; + } else { + int new_brid; + /* We only open the highest priority branch. */ + err = open_highest_file(file, willwrite); + if (err) + goto out; + new_brid = UNIONFS_F(file)->saved_branch_ids[fbstart(file)]; + if (unlikely(new_brid != orig_brid && sbgen > fgen)) { + /* + * If we re-opened the file on a different branch + * than the original one, and this was due to a new + * branch inserted, then update the mnt counts of + * the old and new branches accordingly. + */ + unionfs_mntget(dentry, bstart); + unionfs_mntput(sb->s_root, + branch_id_to_idx(sb, orig_brid)); + } + /* regular files have only one open lower file */ + fbend(file) = fbstart(file); + } + atomic_set(&UNIONFS_F(file)->generation, + atomic_read(&UNIONFS_I(dentry->d_inode)->generation)); + +out_may_copyup: + /* Copyup on the first write to a file on a readonly branch. */ + if (willwrite && IS_WRITE_FLAG(file->f_flags) && + !IS_WRITE_FLAG(unionfs_lower_file(file)->f_flags) && + is_robranch(dentry)) { + pr_debug("unionfs: do delay copyup of \"%s\"\n", + dentry->d_name.name); + err = do_delayed_copyup(file, parent); + /* regular files have only one open lower file */ + if (!err && !S_ISDIR(dentry->d_inode->i_mode)) + fbend(file) = fbstart(file); + } + +out: + if (err) { + kfree(UNIONFS_F(file)->lower_files); + kfree(UNIONFS_F(file)->saved_branch_ids); + } + return err; +} + +/* + * Revalidate the struct file + * @file: file to revalidate + * @parent: parent dentry (locked by caller) + * @willwrite: true if caller may cause changes to the file; false otherwise. + * Caller must lock/unlock dentry's branch configuration. + */ +int unionfs_file_revalidate(struct file *file, struct dentry *parent, + bool willwrite) +{ + struct super_block *sb; + struct dentry *dentry; + int sbgen, dgen; + int err = 0; + + dentry = file->f_path.dentry; + sb = dentry->d_sb; + verify_locked(dentry); + verify_locked(parent); + + /* + * First revalidate the dentry inside struct file, + * but not unhashed dentries. + */ + if (!d_deleted(dentry) && + !__unionfs_d_revalidate(dentry, parent, willwrite)) { + err = -ESTALE; + goto out; + } + + sbgen = atomic_read(&UNIONFS_SB(sb)->generation); + dgen = atomic_read(&UNIONFS_D(dentry)->generation); + + if (unlikely(sbgen > dgen)) { /* XXX: should never happen */ + pr_debug("unionfs: failed to revalidate dentry (%s)\n", + dentry->d_name.name); + err = -ESTALE; + goto out; + } + + err = __unionfs_file_revalidate(file, dentry, parent, sb, + sbgen, dgen, willwrite); +out: + return err; +} + +/* unionfs_open helper function: open a directory */ +static int __open_dir(struct inode *inode, struct file *file) +{ + struct dentry *lower_dentry; + struct file *lower_file; + int bindex, bstart, bend; + struct vfsmount *mnt; + + bstart = fbstart(file) = dbstart(file->f_path.dentry); + bend = fbend(file) = dbend(file->f_path.dentry); + + for (bindex = bstart; bindex <= bend; bindex++) { + lower_dentry = + unionfs_lower_dentry_idx(file->f_path.dentry, bindex); + if (!lower_dentry) + continue; + + dget(lower_dentry); + unionfs_mntget(file->f_path.dentry, bindex); + mnt = unionfs_lower_mnt_idx(file->f_path.dentry, bindex); + lower_file = dentry_open(lower_dentry, mnt, file->f_flags); + if (IS_ERR(lower_file)) + return PTR_ERR(lower_file); + + unionfs_set_lower_file_idx(file, bindex, lower_file); + + /* + * The branchget goes after the open, because otherwise + * we would miss the reference on release. + */ + branchget(inode->i_sb, bindex); + } + + return 0; +} + +/* unionfs_open helper function: open a file */ +static int __open_file(struct inode *inode, struct file *file, + struct dentry *parent) +{ + struct dentry *lower_dentry; + struct file *lower_file; + int lower_flags; + int bindex, bstart, bend; + + lower_dentry = unionfs_lower_dentry(file->f_path.dentry); + lower_flags = file->f_flags; + + bstart = fbstart(file) = dbstart(file->f_path.dentry); + bend = fbend(file) = dbend(file->f_path.dentry); + + /* + * check for the permission for lower file. If the error is + * COPYUP_ERR, copyup the file. + */ + if (lower_dentry->d_inode && is_robranch(file->f_path.dentry)) { + /* + * if the open will change the file, copy it up otherwise + * defer it. + */ + if (lower_flags & O_TRUNC) { + int size = 0; + int err = -EROFS; + + /* copyup the file */ + for (bindex = bstart - 1; bindex >= 0; bindex--) { + err = copyup_file(parent->d_inode, file, + bstart, bindex, size); + if (!err) + break; + } + return err; + } else { + /* + * turn off writeable flags, to force delayed copyup + * by caller. + */ + lower_flags &= ~(OPEN_WRITE_FLAGS); + } + } + + dget(lower_dentry); + + /* + * dentry_open will decrement mnt refcnt if err. + * otherwise fput() will do an mntput() for us upon file close. + */ + unionfs_mntget(file->f_path.dentry, bstart); + lower_file = + dentry_open(lower_dentry, + unionfs_lower_mnt_idx(file->f_path.dentry, bstart), + lower_flags); + if (IS_ERR(lower_file)) + return PTR_ERR(lower_file); + + unionfs_set_lower_file(file, lower_file); + branchget(inode->i_sb, bstart); + + return 0; +} + +int unionfs_open(struct inode *inode, struct file *file) +{ + int err = 0; + struct file *lower_file = NULL; + struct dentry *dentry = file->f_path.dentry; + struct dentry *parent; + int bindex = 0, bstart = 0, bend = 0; + int size; + int valid = 0; + + unionfs_read_lock(inode->i_sb, UNIONFS_SMUTEX_PARENT); + parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); + unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); + + /* don't open unhashed/deleted files */ + if (d_deleted(dentry)) { + err = -ENOENT; + goto out_nofree; + } + + /* XXX: should I change 'false' below to the 'willwrite' flag? */ + valid = __unionfs_d_revalidate(dentry, parent, false); + if (unlikely(!valid)) { + err = -ESTALE; + goto out_nofree; + } + + file->private_data = + kzalloc(sizeof(struct unionfs_file_info), GFP_KERNEL); + if (unlikely(!UNIONFS_F(file))) { + err = -ENOMEM; + goto out_nofree; + } + fbstart(file) = -1; + fbend(file) = -1; + atomic_set(&UNIONFS_F(file)->generation, + atomic_read(&UNIONFS_I(inode)->generation)); + + size = sizeof(struct file *) * sbmax(inode->i_sb); + UNIONFS_F(file)->lower_files = kzalloc(size, GFP_KERNEL); + if (unlikely(!UNIONFS_F(file)->lower_files)) { + err = -ENOMEM; + goto out; + } + size = sizeof(int) * sbmax(inode->i_sb); + UNIONFS_F(file)->saved_branch_ids = kzalloc(size, GFP_KERNEL); + if (unlikely(!UNIONFS_F(file)->saved_branch_ids)) { + err = -ENOMEM; + goto out; + } + + bstart = fbstart(file) = dbstart(dentry); + bend = fbend(file) = dbend(dentry); + + /* + * open all directories and make the unionfs file struct point to + * these lower file structs + */ + if (S_ISDIR(inode->i_mode)) + err = __open_dir(inode, file); /* open a dir */ + else + err = __open_file(inode, file, parent); /* open a file */ + + /* freeing the allocated resources, and fput the opened files */ + if (err) { + for (bindex = bstart; bindex <= bend; bindex++) { + lower_file = unionfs_lower_file_idx(file, bindex); + if (!lower_file) + continue; + + branchput(dentry->d_sb, bindex); + /* fput calls dput for lower_dentry */ + fput(lower_file); + } + } + +out: + if (err) { + kfree(UNIONFS_F(file)->lower_files); + kfree(UNIONFS_F(file)->saved_branch_ids); + kfree(UNIONFS_F(file)); + } +out_nofree: + if (!err) { + unionfs_postcopyup_setmnt(dentry); + unionfs_copy_attr_times(inode); + unionfs_check_file(file); + unionfs_check_inode(inode); + } + unionfs_unlock_dentry(dentry); + unionfs_unlock_parent(dentry, parent); + unionfs_read_unlock(inode->i_sb); + return err; +} + +/* + * release all lower object references & free the file info structure + * + * No need to grab sb info's rwsem. + */ +int unionfs_file_release(struct inode *inode, struct file *file) +{ + struct file *lower_file = NULL; + struct unionfs_file_info *fileinfo; + struct unionfs_inode_info *inodeinfo; + struct super_block *sb = inode->i_sb; + struct dentry *dentry = file->f_path.dentry; + struct dentry *parent; + int bindex, bstart, bend; + int fgen, err = 0; + + unionfs_read_lock(sb, UNIONFS_SMUTEX_PARENT); + parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); + unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); + + /* + * We try to revalidate, but the VFS ignores return return values + * from file->release, so we must always try to succeed here, + * including to do the kfree and dput below. So if revalidation + * failed, all we can do is print some message and keep going. + */ + err = unionfs_file_revalidate(file, parent, + UNIONFS_F(file)->wrote_to_file); + if (!err) + unionfs_check_file(file); + fileinfo = UNIONFS_F(file); + BUG_ON(file->f_path.dentry->d_inode != inode); + inodeinfo = UNIONFS_I(inode); + + /* fput all the lower files */ + fgen = atomic_read(&fileinfo->generation); + bstart = fbstart(file); + bend = fbend(file); + + for (bindex = bstart; bindex <= bend; bindex++) { + lower_file = unionfs_lower_file_idx(file, bindex); + + if (lower_file) { + unionfs_set_lower_file_idx(file, bindex, NULL); + fput(lower_file); + branchput(sb, bindex); + } + + /* if there are no more refs to the dentry, dput it */ + if (d_deleted(dentry)) { + dput(unionfs_lower_dentry_idx(dentry, bindex)); + unionfs_set_lower_dentry_idx(dentry, bindex, NULL); + } + } + + kfree(fileinfo->lower_files); + kfree(fileinfo->saved_branch_ids); + + if (fileinfo->rdstate) { + fileinfo->rdstate->access = jiffies; + spin_lock(&inodeinfo->rdlock); + inodeinfo->rdcount++; + list_add_tail(&fileinfo->rdstate->cache, + &inodeinfo->readdircache); + mark_inode_dirty(inode); + spin_unlock(&inodeinfo->rdlock); + fileinfo->rdstate = NULL; + } + kfree(fileinfo); + + unionfs_unlock_dentry(dentry); + unionfs_unlock_parent(dentry, parent); + unionfs_read_unlock(sb); + return err; +} + +/* pass the ioctl to the lower fs */ +static long do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct file *lower_file; + int err; + + lower_file = unionfs_lower_file(file); + + err = -ENOTTY; + if (!lower_file || !lower_file->f_op) + goto out; + if (lower_file->f_op->unlocked_ioctl) { + err = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg); + } else if (lower_file->f_op->ioctl) { + lock_kernel(); + err = lower_file->f_op->ioctl( + lower_file->f_path.dentry->d_inode, + lower_file, cmd, arg); + unlock_kernel(); + } + +out: + return err; +} + +/* + * return to user-space the branch indices containing the file in question + * + * We use fd_set and therefore we are limited to the number of the branches + * to FD_SETSIZE, which is currently 1024 - plenty for most people + */ +static int unionfs_ioctl_queryfile(struct file *file, struct dentry *parent, + unsigned int cmd, unsigned long arg) +{ + int err = 0; + fd_set branchlist; + int bstart = 0, bend = 0, bindex = 0; + int orig_bstart, orig_bend; + struct dentry *dentry, *lower_dentry; + struct vfsmount *mnt; + + dentry = file->f_path.dentry; + orig_bstart = dbstart(dentry); + orig_bend = dbend(dentry); + err = unionfs_partial_lookup(dentry, parent); + if (err) + goto out; + bstart = dbstart(dentry); + bend = dbend(dentry); + + FD_ZERO(&branchlist); + + for (bindex = bstart; bindex <= bend; bindex++) { + lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); + if (!lower_dentry) + continue; + if (likely(lower_dentry->d_inode)) + FD_SET(bindex, &branchlist); + /* purge any lower objects after partial_lookup */ + if (bindex < orig_bstart || bindex > orig_bend) { + dput(lower_dentry); + unionfs_set_lower_dentry_idx(dentry, bindex, NULL); + iput(unionfs_lower_inode_idx(dentry->d_inode, bindex)); + unionfs_set_lower_inode_idx(dentry->d_inode, bindex, + NULL); + mnt = unionfs_lower_mnt_idx(dentry, bindex); + if (!mnt) + continue; + unionfs_mntput(dentry, bindex); + unionfs_set_lower_mnt_idx(dentry, bindex, NULL); + } + } + /* restore original dentry's offsets */ + dbstart(dentry) = orig_bstart; + dbend(dentry) = orig_bend; + ibstart(dentry->d_inode) = orig_bstart; + ibend(dentry->d_inode) = orig_bend; + + err = copy_to_user((void __user *)arg, &branchlist, sizeof(fd_set)); + if (unlikely(err)) + err = -EFAULT; + +out: + return err < 0 ? err : bend; +} + +long unionfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + long err; + struct dentry *dentry = file->f_path.dentry; + struct dentry *parent; + + unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT); + parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); + unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); + + err = unionfs_file_revalidate(file, parent, true); + if (unlikely(err)) + goto out; + + /* check if asked for local commands */ + switch (cmd) { + case UNIONFS_IOCTL_INCGEN: + /* Increment the superblock generation count */ + pr_info("unionfs: incgen ioctl deprecated; " + "use \"-o remount,incgen\"\n"); + err = -ENOSYS; + break; + + case UNIONFS_IOCTL_QUERYFILE: + /* Return list of branches containing the given file */ + err = unionfs_ioctl_queryfile(file, parent, cmd, arg); + break; + + default: + /* pass the ioctl down */ + err = do_ioctl(file, cmd, arg); + break; + } + +out: + unionfs_check_file(file); + unionfs_unlock_dentry(dentry); + unionfs_unlock_parent(dentry, parent); + unionfs_read_unlock(dentry->d_sb); + return err; +} + +int unionfs_flush(struct file *file, fl_owner_t id) +{ + int err = 0; + struct file *lower_file = NULL; + struct dentry *dentry = file->f_path.dentry; + struct dentry *parent; + int bindex, bstart, bend; + + unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT); + parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); + unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); + + err = unionfs_file_revalidate(file, parent, + UNIONFS_F(file)->wrote_to_file); + if (unlikely(err)) + goto out; + unionfs_check_file(file); + + bstart = fbstart(file); + bend = fbend(file); + for (bindex = bstart; bindex <= bend; bindex++) { + lower_file = unionfs_lower_file_idx(file, bindex); + + if (lower_file && lower_file->f_op && + lower_file->f_op->flush) { + err = lower_file->f_op->flush(lower_file, id); + if (err) + goto out; + } + + } + +out: + if (!err) + unionfs_check_file(file); + unionfs_unlock_dentry(dentry); + unionfs_unlock_parent(dentry, parent); + unionfs_read_unlock(dentry->d_sb); + return err; +} diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/copyup.c kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/copyup.c --- kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/copyup.c 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/copyup.c 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,883 @@ +/* + * Copyright (c) 2003-2009 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2007 Josef 'Jeff' Sipek + * Copyright (c) 2005-2006 Junjiro Okajima + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2004-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2009 Stony Brook University + * Copyright (c) 2003-2009 The Research Foundation of SUNY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "union.h" + +/* + * For detailed explanation of copyup see: + * Documentation/filesystems/unionfs/concepts.txt + */ + +#ifdef CONFIG_UNION_FS_XATTR +/* copyup all extended attrs for a given dentry */ +static int copyup_xattrs(struct dentry *old_lower_dentry, + struct dentry *new_lower_dentry) +{ + int err = 0; + ssize_t list_size = -1; + char *name_list = NULL; + char *attr_value = NULL; + char *name_list_buf = NULL; + + /* query the actual size of the xattr list */ + list_size = vfs_listxattr(old_lower_dentry, NULL, 0); + if (list_size <= 0) { + err = list_size; + goto out; + } + + /* allocate space for the actual list */ + name_list = unionfs_xattr_alloc(list_size + 1, XATTR_LIST_MAX); + if (unlikely(!name_list || IS_ERR(name_list))) { + err = PTR_ERR(name_list); + goto out; + } + + name_list_buf = name_list; /* save for kfree at end */ + + /* now get the actual xattr list of the source file */ + list_size = vfs_listxattr(old_lower_dentry, name_list, list_size); + if (list_size <= 0) { + err = list_size; + goto out; + } + + /* allocate space to hold each xattr's value */ + attr_value = unionfs_xattr_alloc(XATTR_SIZE_MAX, XATTR_SIZE_MAX); + if (unlikely(!attr_value || IS_ERR(attr_value))) { + err = PTR_ERR(name_list); + goto out; + } + + /* in a loop, get and set each xattr from src to dst file */ + while (*name_list) { + ssize_t size; + + /* Lock here since vfs_getxattr doesn't lock for us */ + mutex_lock(&old_lower_dentry->d_inode->i_mutex); + size = vfs_getxattr(old_lower_dentry, name_list, + attr_value, XATTR_SIZE_MAX); + mutex_unlock(&old_lower_dentry->d_inode->i_mutex); + if (size < 0) { + err = size; + goto out; + } + if (size > XATTR_SIZE_MAX) { + err = -E2BIG; + goto out; + } + /* Don't lock here since vfs_setxattr does it for us. */ + err = vfs_setxattr(new_lower_dentry, name_list, attr_value, + size, 0); + /* + * Selinux depends on "security.*" xattrs, so to maintain + * the security of copied-up files, if Selinux is active, + * then we must copy these xattrs as well. So we need to + * temporarily get FOWNER privileges. + * XXX: move entire copyup code to SIOQ. + */ + if (err == -EPERM && !capable(CAP_FOWNER)) { + cap_raise(current->cap_effective, CAP_FOWNER); + err = vfs_setxattr(new_lower_dentry, name_list, + attr_value, size, 0); + cap_lower(current->cap_effective, CAP_FOWNER); + } + if (err < 0) + goto out; + name_list += strlen(name_list) + 1; + } +out: + unionfs_xattr_kfree(name_list_buf); + unionfs_xattr_kfree(attr_value); + /* Ignore if xattr isn't supported */ + if (err == -ENOTSUPP || err == -EOPNOTSUPP) + err = 0; + return err; +} +#endif /* CONFIG_UNION_FS_XATTR */ + +/* + * Determine the mode based on the copyup flags, and the existing dentry. + * + * Handle file systems which may not support certain options. For example + * jffs2 doesn't allow one to chmod a symlink. So we ignore such harmless + * errors, rather than propagating them up, which results in copyup errors + * and errors returned back to users. + */ +static int copyup_permissions(struct super_block *sb, + struct dentry *old_lower_dentry, + struct dentry *new_lower_dentry) +{ + struct inode *i = old_lower_dentry->d_inode; + struct iattr newattrs; + int err; + + newattrs.ia_atime = i->i_atime; + newattrs.ia_mtime = i->i_mtime; + newattrs.ia_ctime = i->i_ctime; + newattrs.ia_gid = i->i_gid; + newattrs.ia_uid = i->i_uid; + newattrs.ia_valid = ATTR_CTIME | ATTR_ATIME | ATTR_MTIME | + ATTR_ATIME_SET | ATTR_MTIME_SET | ATTR_FORCE | + ATTR_GID | ATTR_UID; + mutex_lock(&new_lower_dentry->d_inode->i_mutex); + err = notify_change(new_lower_dentry, &newattrs); + if (err) + goto out; + + /* now try to change the mode and ignore EOPNOTSUPP on symlinks */ + newattrs.ia_mode = i->i_mode; + newattrs.ia_valid = ATTR_MODE | ATTR_FORCE; + err = notify_change(new_lower_dentry, &newattrs); + if (err == -EOPNOTSUPP && + S_ISLNK(new_lower_dentry->d_inode->i_mode)) { + printk(KERN_WARNING + "unionfs: changing \"%s\" symlink mode unsupported\n", + new_lower_dentry->d_name.name); + err = 0; + } + +out: + mutex_unlock(&new_lower_dentry->d_inode->i_mutex); + return err; +} + +/* + * create the new device/file/directory - use copyup_permission to copyup + * times, and mode + * + * if the object being copied up is a regular file, the file is only created, + * the contents have to be copied up separately + */ +static int __copyup_ndentry(struct dentry *old_lower_dentry, + struct dentry *new_lower_dentry, + struct dentry *new_lower_parent_dentry, + char *symbuf) +{ + int err = 0; + umode_t old_mode = old_lower_dentry->d_inode->i_mode; + struct sioq_args args; + + if (S_ISDIR(old_mode)) { + args.mkdir.parent = new_lower_parent_dentry->d_inode; + args.mkdir.dentry = new_lower_dentry; + args.mkdir.mode = old_mode; + + run_sioq(__unionfs_mkdir, &args); + err = args.err; + } else if (S_ISLNK(old_mode)) { + args.symlink.parent = new_lower_parent_dentry->d_inode; + args.symlink.dentry = new_lower_dentry; + args.symlink.symbuf = symbuf; + args.symlink.mode = old_mode; + + run_sioq(__unionfs_symlink, &args); + err = args.err; + } else if (S_ISBLK(old_mode) || S_ISCHR(old_mode) || + S_ISFIFO(old_mode) || S_ISSOCK(old_mode)) { + args.mknod.parent = new_lower_parent_dentry->d_inode; + args.mknod.dentry = new_lower_dentry; + args.mknod.mode = old_mode; + args.mknod.dev = old_lower_dentry->d_inode->i_rdev; + + run_sioq(__unionfs_mknod, &args); + err = args.err; + } else if (S_ISREG(old_mode)) { + args.create.parent = new_lower_parent_dentry->d_inode; + args.create.dentry = new_lower_dentry; + args.create.mode = old_mode; + args.create.nd = NULL; + + run_sioq(__unionfs_create, &args); + err = args.err; + } else { + printk(KERN_CRIT "unionfs: unknown inode type %d\n", + old_mode); + BUG(); + } + + return err; +} + +static int __copyup_reg_data(struct dentry *dentry, + struct dentry *new_lower_dentry, int new_bindex, + struct dentry *old_lower_dentry, int old_bindex, + struct file **copyup_file, loff_t len) +{ + struct super_block *sb = dentry->d_sb; + struct file *input_file; + struct file *output_file; + struct vfsmount *output_mnt; + mm_segment_t old_fs; + char *buf = NULL; + ssize_t read_bytes, write_bytes; + loff_t size; + int err = 0; + + /* open old file */ + unionfs_mntget(dentry, old_bindex); + branchget(sb, old_bindex); + /* dentry_open calls dput and mntput if it returns an error */ + input_file = dentry_open(old_lower_dentry, + unionfs_lower_mnt_idx(dentry, old_bindex), + O_RDONLY | O_LARGEFILE); + if (IS_ERR(input_file)) { + dput(old_lower_dentry); + err = PTR_ERR(input_file); + goto out; + } + if (unlikely(!input_file->f_op || !input_file->f_op->read)) { + err = -EINVAL; + goto out_close_in; + } + + /* open new file */ + dget(new_lower_dentry); + output_mnt = unionfs_mntget(sb->s_root, new_bindex); + branchget(sb, new_bindex); + output_file = dentry_open(new_lower_dentry, output_mnt, + O_RDWR | O_LARGEFILE); + if (IS_ERR(output_file)) { + err = PTR_ERR(output_file); + goto out_close_in2; + } + if (unlikely(!output_file->f_op || !output_file->f_op->write)) { + err = -EINVAL; + goto out_close_out; + } + + /* allocating a buffer */ + buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (unlikely(!buf)) { + err = -ENOMEM; + goto out_close_out; + } + + input_file->f_pos = 0; + output_file->f_pos = 0; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + size = len; + err = 0; + do { + if (len >= PAGE_SIZE) + size = PAGE_SIZE; + else if ((len < PAGE_SIZE) && (len > 0)) + size = len; + + len -= PAGE_SIZE; + + read_bytes = + input_file->f_op->read(input_file, + (char __user *)buf, size, + &input_file->f_pos); + if (read_bytes <= 0) { + err = read_bytes; + break; + } + + /* see Documentation/filesystems/unionfs/issues.txt */ + lockdep_off(); + write_bytes = + output_file->f_op->write(output_file, + (char __user *)buf, + read_bytes, + &output_file->f_pos); + lockdep_on(); + if ((write_bytes < 0) || (write_bytes < read_bytes)) { + err = write_bytes; + break; + } + } while ((read_bytes > 0) && (len > 0)); + + set_fs(old_fs); + + kfree(buf); + + if (!err) + err = output_file->f_op->fsync(output_file, + new_lower_dentry, 0); + + if (err) + goto out_close_out; + + if (copyup_file) { + *copyup_file = output_file; + goto out_close_in; + } + +out_close_out: + fput(output_file); + +out_close_in2: + branchput(sb, new_bindex); + +out_close_in: + fput(input_file); + +out: + branchput(sb, old_bindex); + + return err; +} + +/* + * dput the lower references for old and new dentry & clear a lower dentry + * pointer + */ +static void __clear(struct dentry *dentry, struct dentry *old_lower_dentry, + int old_bstart, int old_bend, + struct dentry *new_lower_dentry, int new_bindex) +{ + /* get rid of the lower dentry and all its traces */ + unionfs_set_lower_dentry_idx(dentry, new_bindex, NULL); + dbstart(dentry) = old_bstart; + dbend(dentry) = old_bend; + + dput(new_lower_dentry); + dput(old_lower_dentry); +} + +/* + * Copy up a dentry to a file of specified name. + * + * @dir: used to pull the ->i_sb to access other branches + * @dentry: the non-negative dentry whose lower_inode we should copy + * @bstart: the branch of the lower_inode to copy from + * @new_bindex: the branch to create the new file in + * @name: the name of the file to create + * @namelen: length of @name + * @copyup_file: the "struct file" to return (optional) + * @len: how many bytes to copy-up? + */ +int copyup_dentry(struct inode *dir, struct dentry *dentry, int bstart, + int new_bindex, const char *name, int namelen, + struct file **copyup_file, loff_t len) +{ + struct dentry *new_lower_dentry; + struct dentry *old_lower_dentry = NULL; + struct super_block *sb; + int err = 0; + int old_bindex; + int old_bstart; + int old_bend; + struct dentry *new_lower_parent_dentry = NULL; + mm_segment_t oldfs; + char *symbuf = NULL; + + verify_locked(dentry); + + old_bindex = bstart; + old_bstart = dbstart(dentry); + old_bend = dbend(dentry); + + BUG_ON(new_bindex < 0); + BUG_ON(new_bindex >= old_bindex); + + sb = dir->i_sb; + + err = is_robranch_super(sb, new_bindex); + if (err) + goto out; + + /* Create the directory structure above this dentry. */ + new_lower_dentry = create_parents(dir, dentry, name, new_bindex); + if (IS_ERR(new_lower_dentry)) { + err = PTR_ERR(new_lower_dentry); + goto out; + } + + old_lower_dentry = unionfs_lower_dentry_idx(dentry, old_bindex); + /* we conditionally dput this old_lower_dentry at end of function */ + dget(old_lower_dentry); + + /* For symlinks, we must read the link before we lock the directory. */ + if (S_ISLNK(old_lower_dentry->d_inode->i_mode)) { + + symbuf = kmalloc(PATH_MAX, GFP_KERNEL); + if (unlikely(!symbuf)) { + __clear(dentry, old_lower_dentry, + old_bstart, old_bend, + new_lower_dentry, new_bindex); + err = -ENOMEM; + goto out_free; + } + + oldfs = get_fs(); + set_fs(KERNEL_DS); + err = old_lower_dentry->d_inode->i_op->readlink( + old_lower_dentry, + (char __user *)symbuf, + PATH_MAX); + set_fs(oldfs); + if (err < 0) { + __clear(dentry, old_lower_dentry, + old_bstart, old_bend, + new_lower_dentry, new_bindex); + goto out_free; + } + symbuf[err] = '\0'; + } + + /* Now we lock the parent, and create the object in the new branch. */ + new_lower_parent_dentry = lock_parent(new_lower_dentry); + + /* create the new inode */ + err = __copyup_ndentry(old_lower_dentry, new_lower_dentry, + new_lower_parent_dentry, symbuf); + + if (err) { + __clear(dentry, old_lower_dentry, + old_bstart, old_bend, + new_lower_dentry, new_bindex); + goto out_unlock; + } + + /* We actually copyup the file here. */ + if (S_ISREG(old_lower_dentry->d_inode->i_mode)) + err = __copyup_reg_data(dentry, new_lower_dentry, new_bindex, + old_lower_dentry, old_bindex, + copyup_file, len); + if (err) + goto out_unlink; + + /* Set permissions. */ + err = copyup_permissions(sb, old_lower_dentry, new_lower_dentry); + if (err) + goto out_unlink; + +#ifdef CONFIG_UNION_FS_XATTR + /* Selinux uses extended attributes for permissions. */ + err = copyup_xattrs(old_lower_dentry, new_lower_dentry); + if (err) + goto out_unlink; +#endif /* CONFIG_UNION_FS_XATTR */ + + /* do not allow files getting deleted to be re-interposed */ + if (!d_deleted(dentry)) + unionfs_reinterpose(dentry); + + goto out_unlock; + +out_unlink: + /* + * copyup failed, because we possibly ran out of space or + * quota, or something else happened so let's unlink; we don't + * really care about the return value of vfs_unlink + */ + vfs_unlink(new_lower_parent_dentry->d_inode, new_lower_dentry); + + if (copyup_file) { + /* need to close the file */ + + fput(*copyup_file); + branchput(sb, new_bindex); + } + + /* + * TODO: should we reset the error to something like -EIO? + * + * If we don't reset, the user may get some nonsensical errors, but + * on the other hand, if we reset to EIO, we guarantee that the user + * will get a "confusing" error message. + */ + +out_unlock: + unlock_dir(new_lower_parent_dentry); + +out_free: + /* + * If old_lower_dentry was not a file, then we need to dput it. If + * it was a file, then it was already dput indirectly by other + * functions we call above which operate on regular files. + */ + if (old_lower_dentry && old_lower_dentry->d_inode && + !S_ISREG(old_lower_dentry->d_inode->i_mode)) + dput(old_lower_dentry); + kfree(symbuf); + + if (err) { + /* + * if directory creation succeeded, but inode copyup failed, + * then purge new dentries. + */ + if (dbstart(dentry) < old_bstart && + ibstart(dentry->d_inode) > dbstart(dentry)) + __clear(dentry, NULL, old_bstart, old_bend, + unionfs_lower_dentry(dentry), dbstart(dentry)); + goto out; + } + if (!S_ISDIR(dentry->d_inode->i_mode)) { + unionfs_postcopyup_release(dentry); + if (!unionfs_lower_inode(dentry->d_inode)) { + /* + * If we got here, then we copied up to an + * unlinked-open file, whose name is .unionfsXXXXX. + */ + struct inode *inode = new_lower_dentry->d_inode; + atomic_inc(&inode->i_count); + unionfs_set_lower_inode_idx(dentry->d_inode, + ibstart(dentry->d_inode), + inode); + } + } + unionfs_postcopyup_setmnt(dentry); + /* sync inode times from copied-up inode to our inode */ + unionfs_copy_attr_times(dentry->d_inode); + unionfs_check_inode(dir); + unionfs_check_dentry(dentry); +out: + return err; +} + +/* + * This function creates a copy of a file represented by 'file' which + * currently resides in branch 'bstart' to branch 'new_bindex.' The copy + * will be named "name". + */ +int copyup_named_file(struct inode *dir, struct file *file, char *name, + int bstart, int new_bindex, loff_t len) +{ + int err = 0; + struct file *output_file = NULL; + + err = copyup_dentry(dir, file->f_path.dentry, bstart, new_bindex, + name, strlen(name), &output_file, len); + if (!err) { + fbstart(file) = new_bindex; + unionfs_set_lower_file_idx(file, new_bindex, output_file); + } + + return err; +} + +/* + * This function creates a copy of a file represented by 'file' which + * currently resides in branch 'bstart' to branch 'new_bindex'. + */ +int copyup_file(struct inode *dir, struct file *file, int bstart, + int new_bindex, loff_t len) +{ + int err = 0; + struct file *output_file = NULL; + struct dentry *dentry = file->f_path.dentry; + + err = copyup_dentry(dir, dentry, bstart, new_bindex, + dentry->d_name.name, dentry->d_name.len, + &output_file, len); + if (!err) { + fbstart(file) = new_bindex; + unionfs_set_lower_file_idx(file, new_bindex, output_file); + } + + return err; +} + +/* purge a dentry's lower-branch states (dput/mntput, etc.) */ +static void __cleanup_dentry(struct dentry *dentry, int bindex, + int old_bstart, int old_bend) +{ + int loop_start; + int loop_end; + int new_bstart = -1; + int new_bend = -1; + int i; + + loop_start = min(old_bstart, bindex); + loop_end = max(old_bend, bindex); + + /* + * This loop sets the bstart and bend for the new dentry by + * traversing from left to right. It also dputs all negative + * dentries except bindex + */ + for (i = loop_start; i <= loop_end; i++) { + if (!unionfs_lower_dentry_idx(dentry, i)) + continue; + + if (i == bindex) { + new_bend = i; + if (new_bstart < 0) + new_bstart = i; + continue; + } + + if (!unionfs_lower_dentry_idx(dentry, i)->d_inode) { + dput(unionfs_lower_dentry_idx(dentry, i)); + unionfs_set_lower_dentry_idx(dentry, i, NULL); + + unionfs_mntput(dentry, i); + unionfs_set_lower_mnt_idx(dentry, i, NULL); + } else { + if (new_bstart < 0) + new_bstart = i; + new_bend = i; + } + } + + if (new_bstart < 0) + new_bstart = bindex; + if (new_bend < 0) + new_bend = bindex; + dbstart(dentry) = new_bstart; + dbend(dentry) = new_bend; + +} + +/* set lower inode ptr and update bstart & bend if necessary */ +static void __set_inode(struct dentry *upper, struct dentry *lower, + int bindex) +{ + unionfs_set_lower_inode_idx(upper->d_inode, bindex, + igrab(lower->d_inode)); + if (likely(ibstart(upper->d_inode) > bindex)) + ibstart(upper->d_inode) = bindex; + if (likely(ibend(upper->d_inode) < bindex)) + ibend(upper->d_inode) = bindex; + +} + +/* set lower dentry ptr and update bstart & bend if necessary */ +static void __set_dentry(struct dentry *upper, struct dentry *lower, + int bindex) +{ + unionfs_set_lower_dentry_idx(upper, bindex, lower); + if (likely(dbstart(upper) > bindex)) + dbstart(upper) = bindex; + if (likely(dbend(upper) < bindex)) + dbend(upper) = bindex; +} + +/* + * This function replicates the directory structure up-to given dentry + * in the bindex branch. + */ +struct dentry *create_parents(struct inode *dir, struct dentry *dentry, + const char *name, int bindex) +{ + int err; + struct dentry *child_dentry; + struct dentry *parent_dentry; + struct dentry *lower_parent_dentry = NULL; + struct dentry *lower_dentry = NULL; + const char *childname; + unsigned int childnamelen; + int nr_dentry; + int count = 0; + int old_bstart; + int old_bend; + struct dentry **path = NULL; + struct super_block *sb; + + verify_locked(dentry); + + err = is_robranch_super(dir->i_sb, bindex); + if (err) { + lower_dentry = ERR_PTR(err); + goto out; + } + + old_bstart = dbstart(dentry); + old_bend = dbend(dentry); + + lower_dentry = ERR_PTR(-ENOMEM); + + /* There is no sense allocating any less than the minimum. */ + nr_dentry = 1; + path = kmalloc(nr_dentry * sizeof(struct dentry *), GFP_KERNEL); + if (unlikely(!path)) + goto out; + + /* assume the negative dentry of unionfs as the parent dentry */ + parent_dentry = dentry; + + /* + * This loop finds the first parent that exists in the given branch. + * We start building the directory structure from there. At the end + * of the loop, the following should hold: + * - child_dentry is the first nonexistent child + * - parent_dentry is the first existent parent + * - path[0] is the = deepest child + * - path[count] is the first child to create + */ + do { + child_dentry = parent_dentry; + + /* find the parent directory dentry in unionfs */ + parent_dentry = dget_parent(child_dentry); + + /* find out the lower_parent_dentry in the given branch */ + lower_parent_dentry = + unionfs_lower_dentry_idx(parent_dentry, bindex); + + /* grow path table */ + if (count == nr_dentry) { + void *p; + + nr_dentry *= 2; + p = krealloc(path, nr_dentry * sizeof(struct dentry *), + GFP_KERNEL); + if (unlikely(!p)) { + lower_dentry = ERR_PTR(-ENOMEM); + goto out; + } + path = p; + } + + /* store the child dentry */ + path[count++] = child_dentry; + } while (!lower_parent_dentry); + count--; + + sb = dentry->d_sb; + + /* + * This code goes between the begin/end labels and basically + * emulates a while(child_dentry != dentry), only cleaner and + * shorter than what would be a much longer while loop. + */ +begin: + /* get lower parent dir in the current branch */ + lower_parent_dentry = unionfs_lower_dentry_idx(parent_dentry, bindex); + dput(parent_dentry); + + /* init the values to lookup */ + childname = child_dentry->d_name.name; + childnamelen = child_dentry->d_name.len; + + if (child_dentry != dentry) { + /* lookup child in the underlying file system */ + lower_dentry = lookup_one_len(childname, lower_parent_dentry, + childnamelen); + if (IS_ERR(lower_dentry)) + goto out; + } else { + /* + * Is the name a whiteout of the child name ? lookup the + * whiteout child in the underlying file system + */ + lower_dentry = lookup_one_len(name, lower_parent_dentry, + strlen(name)); + if (IS_ERR(lower_dentry)) + goto out; + + /* Replace the current dentry (if any) with the new one */ + dput(unionfs_lower_dentry_idx(dentry, bindex)); + unionfs_set_lower_dentry_idx(dentry, bindex, + lower_dentry); + + __cleanup_dentry(dentry, bindex, old_bstart, old_bend); + goto out; + } + + if (lower_dentry->d_inode) { + /* + * since this already exists we dput to avoid + * multiple references on the same dentry + */ + dput(lower_dentry); + } else { + struct sioq_args args; + + /* it's a negative dentry, create a new dir */ + lower_parent_dentry = lock_parent(lower_dentry); + + args.mkdir.parent = lower_parent_dentry->d_inode; + args.mkdir.dentry = lower_dentry; + args.mkdir.mode = child_dentry->d_inode->i_mode; + + run_sioq(__unionfs_mkdir, &args); + err = args.err; + + if (!err) + err = copyup_permissions(dir->i_sb, child_dentry, + lower_dentry); + unlock_dir(lower_parent_dentry); + if (err) { + dput(lower_dentry); + lower_dentry = ERR_PTR(err); + goto out; + } + + } + + __set_inode(child_dentry, lower_dentry, bindex); + __set_dentry(child_dentry, lower_dentry, bindex); + /* + * update times of this dentry, but also the parent, because if + * we changed, the parent may have changed too. + */ + fsstack_copy_attr_times(parent_dentry->d_inode, + lower_parent_dentry->d_inode); + unionfs_copy_attr_times(child_dentry->d_inode); + + parent_dentry = child_dentry; + child_dentry = path[--count]; + goto begin; +out: + /* cleanup any leftover locks from the do/while loop above */ + if (IS_ERR(lower_dentry)) + while (count) + dput(path[count--]); + kfree(path); + return lower_dentry; +} + +/* + * Post-copyup helper to ensure we have valid mnts: set lower mnt of + * dentry+parents to the first parent node that has an mnt. + */ +void unionfs_postcopyup_setmnt(struct dentry *dentry) +{ + struct dentry *parent, *hasone; + int bindex = dbstart(dentry); + + if (unionfs_lower_mnt_idx(dentry, bindex)) + return; + hasone = dentry->d_parent; + /* this loop should stop at root dentry */ + while (!unionfs_lower_mnt_idx(hasone, bindex)) + hasone = hasone->d_parent; + parent = dentry; + while (!unionfs_lower_mnt_idx(parent, bindex)) { + unionfs_set_lower_mnt_idx(parent, bindex, + unionfs_mntget(hasone, bindex)); + parent = parent->d_parent; + } +} + +/* + * Post-copyup helper to release all non-directory source objects of a + * copied-up file. Regular files should have only one lower object. + */ +void unionfs_postcopyup_release(struct dentry *dentry) +{ + int bstart, bend; + + BUG_ON(S_ISDIR(dentry->d_inode->i_mode)); + bstart = dbstart(dentry); + bend = dbend(dentry); + + path_put_lowers(dentry, bstart + 1, bend, false); + iput_lowers(dentry->d_inode, bstart + 1, bend, false); + + dbend(dentry) = bstart; + ibend(dentry->d_inode) = ibstart(dentry->d_inode) = bstart; +} diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/debug.c kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/debug.c --- kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/debug.c 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/debug.c 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,516 @@ +/* + * Copyright (c) 2003-2009 Erez Zadok + * Copyright (c) 2005-2007 Josef 'Jeff' Sipek + * Copyright (c) 2003-2009 Stony Brook University + * Copyright (c) 2003-2009 The Research Foundation of SUNY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "union.h" + +/* + * Helper debugging functions for maintainers (and for users to report back + * useful information back to maintainers) + */ + +#ifndef KERN_CONT +# define KERN_CONT "" +#endif /* not KERN_CONT */ + +/* it's always useful to know what part of the code called us */ +#define PRINT_CALLER(fname, fxn, line) \ + do { \ + if (!printed_caller) { \ + pr_debug("PC:%s:%s:%d\n", (fname), (fxn), (line)); \ + printed_caller = 1; \ + } \ + } while (0) + +/* + * __unionfs_check_{inode,dentry,file} perform exhaustive sanity checking on + * the fan-out of various Unionfs objects. We check that no lower objects + * exist outside the start/end branch range; that all objects within are + * non-NULL (with some allowed exceptions); that for every lower file + * there's a lower dentry+inode; that the start/end ranges match for all + * corresponding lower objects; that open files/symlinks have only one lower + * objects, but directories can have several; and more. + */ +void __unionfs_check_inode(const struct inode *inode, + const char *fname, const char *fxn, int line) +{ + int bindex; + int istart, iend; + struct inode *lower_inode; + struct super_block *sb; + int printed_caller = 0; + void *poison_ptr; + + /* for inodes now */ + BUG_ON(!inode); + sb = inode->i_sb; + istart = ibstart(inode); + iend = ibend(inode); + /* don't check inode if no lower branches */ + if (istart < 0 && iend < 0) + return; + if (unlikely(istart > iend)) { + PRINT_CALLER(fname, fxn, line); + pr_debug(" Ci0: inode=%p istart/end=%d:%d\n", + inode, istart, iend); + } + if (unlikely((istart == -1 && iend != -1) || + (istart != -1 && iend == -1))) { + PRINT_CALLER(fname, fxn, line); + pr_debug(" Ci1: inode=%p istart/end=%d:%d\n", + inode, istart, iend); + } + if (!S_ISDIR(inode->i_mode)) { + if (unlikely(iend != istart)) { + PRINT_CALLER(fname, fxn, line); + pr_debug(" Ci2: inode=%p istart=%d iend=%d\n", + inode, istart, iend); + } + } + + for (bindex = sbstart(sb); bindex < sbmax(sb); bindex++) { + if (unlikely(!UNIONFS_I(inode))) { + PRINT_CALLER(fname, fxn, line); + pr_debug(" Ci3: no inode_info %p\n", inode); + return; + } + if (unlikely(!UNIONFS_I(inode)->lower_inodes)) { + PRINT_CALLER(fname, fxn, line); + pr_debug(" Ci4: no lower_inodes %p\n", inode); + return; + } + lower_inode = unionfs_lower_inode_idx(inode, bindex); + if (lower_inode) { + memset(&poison_ptr, POISON_INUSE, sizeof(void *)); + if (unlikely(bindex < istart || bindex > iend)) { + PRINT_CALLER(fname, fxn, line); + pr_debug(" Ci5: inode/linode=%p:%p bindex=%d " + "istart/end=%d:%d\n", inode, + lower_inode, bindex, istart, iend); + } else if (unlikely(lower_inode == poison_ptr)) { + /* freed inode! */ + PRINT_CALLER(fname, fxn, line); + pr_debug(" Ci6: inode/linode=%p:%p bindex=%d " + "istart/end=%d:%d\n", inode, + lower_inode, bindex, istart, iend); + } + continue; + } + /* if we get here, then lower_inode == NULL */ + if (bindex < istart || bindex > iend) + continue; + /* + * directories can have NULL lower inodes in b/t start/end, + * but NOT if at the start/end range. + */ + if (unlikely(S_ISDIR(inode->i_mode) && + bindex > istart && bindex < iend)) + continue; + PRINT_CALLER(fname, fxn, line); + pr_debug(" Ci7: inode/linode=%p:%p " + "bindex=%d istart/end=%d:%d\n", + inode, lower_inode, bindex, istart, iend); + } +} + +void __unionfs_check_dentry(const struct dentry *dentry, + const char *fname, const char *fxn, int line) +{ + int bindex; + int dstart, dend, istart, iend; + struct dentry *lower_dentry; + struct inode *inode, *lower_inode; + struct super_block *sb; + struct vfsmount *lower_mnt; + int printed_caller = 0; + void *poison_ptr; + + BUG_ON(!dentry); + sb = dentry->d_sb; + inode = dentry->d_inode; + dstart = dbstart(dentry); + dend = dbend(dentry); + /* don't check dentry/mnt if no lower branches */ + if (dstart < 0 && dend < 0) + goto check_inode; + BUG_ON(dstart > dend); + + if (unlikely((dstart == -1 && dend != -1) || + (dstart != -1 && dend == -1))) { + PRINT_CALLER(fname, fxn, line); + pr_debug(" CD0: dentry=%p dstart/end=%d:%d\n", + dentry, dstart, dend); + } + /* + * check for NULL dentries inside the start/end range, or + * non-NULL dentries outside the start/end range. + */ + for (bindex = sbstart(sb); bindex < sbmax(sb); bindex++) { + lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); + if (lower_dentry) { + if (unlikely(bindex < dstart || bindex > dend)) { + PRINT_CALLER(fname, fxn, line); + pr_debug(" CD1: dentry/lower=%p:%p(%p) " + "bindex=%d dstart/end=%d:%d\n", + dentry, lower_dentry, + (lower_dentry ? lower_dentry->d_inode : + (void *) -1L), + bindex, dstart, dend); + } + } else { /* lower_dentry == NULL */ + if (bindex < dstart || bindex > dend) + continue; + /* + * Directories can have NULL lower inodes in b/t + * start/end, but NOT if at the start/end range. + * Ignore this rule, however, if this is a NULL + * dentry or a deleted dentry. + */ + if (unlikely(!d_deleted((struct dentry *) dentry) && + inode && + !(inode && S_ISDIR(inode->i_mode) && + bindex > dstart && bindex < dend))) { + PRINT_CALLER(fname, fxn, line); + pr_debug(" CD2: dentry/lower=%p:%p(%p) " + "bindex=%d dstart/end=%d:%d\n", + dentry, lower_dentry, + (lower_dentry ? + lower_dentry->d_inode : + (void *) -1L), + bindex, dstart, dend); + } + } + } + + /* check for vfsmounts same as for dentries */ + for (bindex = sbstart(sb); bindex < sbmax(sb); bindex++) { + lower_mnt = unionfs_lower_mnt_idx(dentry, bindex); + if (lower_mnt) { + if (unlikely(bindex < dstart || bindex > dend)) { + PRINT_CALLER(fname, fxn, line); + pr_debug(" CM0: dentry/lmnt=%p:%p bindex=%d " + "dstart/end=%d:%d\n", dentry, + lower_mnt, bindex, dstart, dend); + } + } else { /* lower_mnt == NULL */ + if (bindex < dstart || bindex > dend) + continue; + /* + * Directories can have NULL lower inodes in b/t + * start/end, but NOT if at the start/end range. + * Ignore this rule, however, if this is a NULL + * dentry. + */ + if (unlikely(inode && + !(inode && S_ISDIR(inode->i_mode) && + bindex > dstart && bindex < dend))) { + PRINT_CALLER(fname, fxn, line); + pr_debug(" CM1: dentry/lmnt=%p:%p " + "bindex=%d dstart/end=%d:%d\n", + dentry, lower_mnt, bindex, + dstart, dend); + } + } + } + +check_inode: + /* for inodes now */ + if (!inode) + return; + istart = ibstart(inode); + iend = ibend(inode); + /* don't check inode if no lower branches */ + if (istart < 0 && iend < 0) + return; + BUG_ON(istart > iend); + if (unlikely((istart == -1 && iend != -1) || + (istart != -1 && iend == -1))) { + PRINT_CALLER(fname, fxn, line); + pr_debug(" CI0: dentry/inode=%p:%p istart/end=%d:%d\n", + dentry, inode, istart, iend); + } + if (unlikely(istart != dstart)) { + PRINT_CALLER(fname, fxn, line); + pr_debug(" CI1: dentry/inode=%p:%p istart=%d dstart=%d\n", + dentry, inode, istart, dstart); + } + if (unlikely(iend != dend)) { + PRINT_CALLER(fname, fxn, line); + pr_debug(" CI2: dentry/inode=%p:%p iend=%d dend=%d\n", + dentry, inode, iend, dend); + } + + if (!S_ISDIR(inode->i_mode)) { + if (unlikely(dend != dstart)) { + PRINT_CALLER(fname, fxn, line); + pr_debug(" CI3: dentry/inode=%p:%p dstart=%d dend=%d\n", + dentry, inode, dstart, dend); + } + if (unlikely(iend != istart)) { + PRINT_CALLER(fname, fxn, line); + pr_debug(" CI4: dentry/inode=%p:%p istart=%d iend=%d\n", + dentry, inode, istart, iend); + } + } + + for (bindex = sbstart(sb); bindex < sbmax(sb); bindex++) { + lower_inode = unionfs_lower_inode_idx(inode, bindex); + if (lower_inode) { + memset(&poison_ptr, POISON_INUSE, sizeof(void *)); + if (unlikely(bindex < istart || bindex > iend)) { + PRINT_CALLER(fname, fxn, line); + pr_debug(" CI5: dentry/linode=%p:%p bindex=%d " + "istart/end=%d:%d\n", dentry, + lower_inode, bindex, istart, iend); + } else if (unlikely(lower_inode == poison_ptr)) { + /* freed inode! */ + PRINT_CALLER(fname, fxn, line); + pr_debug(" CI6: dentry/linode=%p:%p bindex=%d " + "istart/end=%d:%d\n", dentry, + lower_inode, bindex, istart, iend); + } + continue; + } + /* if we get here, then lower_inode == NULL */ + if (bindex < istart || bindex > iend) + continue; + /* + * directories can have NULL lower inodes in b/t start/end, + * but NOT if at the start/end range. + */ + if (unlikely(S_ISDIR(inode->i_mode) && + bindex > istart && bindex < iend)) + continue; + PRINT_CALLER(fname, fxn, line); + pr_debug(" CI7: dentry/linode=%p:%p " + "bindex=%d istart/end=%d:%d\n", + dentry, lower_inode, bindex, istart, iend); + } + + /* + * If it's a directory, then intermediate objects b/t start/end can + * be NULL. But, check that all three are NULL: lower dentry, mnt, + * and inode. + */ + if (dstart >= 0 && dend >= 0 && S_ISDIR(inode->i_mode)) + for (bindex = dstart+1; bindex < dend; bindex++) { + lower_inode = unionfs_lower_inode_idx(inode, bindex); + lower_dentry = unionfs_lower_dentry_idx(dentry, + bindex); + lower_mnt = unionfs_lower_mnt_idx(dentry, bindex); + if (unlikely(!((lower_inode && lower_dentry && + lower_mnt) || + (!lower_inode && + !lower_dentry && !lower_mnt)))) { + PRINT_CALLER(fname, fxn, line); + pr_debug(" Cx: lmnt/ldentry/linode=%p:%p:%p " + "bindex=%d dstart/end=%d:%d\n", + lower_mnt, lower_dentry, lower_inode, + bindex, dstart, dend); + } + } + /* check if lower inode is newer than upper one (it shouldn't) */ + if (unlikely(is_newer_lower(dentry) && !is_negative_lower(dentry))) { + PRINT_CALLER(fname, fxn, line); + for (bindex = ibstart(inode); bindex <= ibend(inode); + bindex++) { + lower_inode = unionfs_lower_inode_idx(inode, bindex); + if (unlikely(!lower_inode)) + continue; + pr_debug(" CI8: bindex=%d mtime/lmtime=%lu.%lu/%lu.%lu " + "ctime/lctime=%lu.%lu/%lu.%lu\n", + bindex, + inode->i_mtime.tv_sec, + inode->i_mtime.tv_nsec, + lower_inode->i_mtime.tv_sec, + lower_inode->i_mtime.tv_nsec, + inode->i_ctime.tv_sec, + inode->i_ctime.tv_nsec, + lower_inode->i_ctime.tv_sec, + lower_inode->i_ctime.tv_nsec); + } + } +} + +void __unionfs_check_file(const struct file *file, + const char *fname, const char *fxn, int line) +{ + int bindex; + int dstart, dend, fstart, fend; + struct dentry *dentry; + struct file *lower_file; + struct inode *inode; + struct super_block *sb; + int printed_caller = 0; + + BUG_ON(!file); + dentry = file->f_path.dentry; + sb = dentry->d_sb; + dstart = dbstart(dentry); + dend = dbend(dentry); + BUG_ON(dstart > dend); + fstart = fbstart(file); + fend = fbend(file); + BUG_ON(fstart > fend); + + if (unlikely((fstart == -1 && fend != -1) || + (fstart != -1 && fend == -1))) { + PRINT_CALLER(fname, fxn, line); + pr_debug(" CF0: file/dentry=%p:%p fstart/end=%d:%d\n", + file, dentry, fstart, fend); + } + if (unlikely(fstart != dstart)) { + PRINT_CALLER(fname, fxn, line); + pr_debug(" CF1: file/dentry=%p:%p fstart=%d dstart=%d\n", + file, dentry, fstart, dstart); + } + if (unlikely(fend != dend)) { + PRINT_CALLER(fname, fxn, line); + pr_debug(" CF2: file/dentry=%p:%p fend=%d dend=%d\n", + file, dentry, fend, dend); + } + inode = dentry->d_inode; + if (!S_ISDIR(inode->i_mode)) { + if (unlikely(fend != fstart)) { + PRINT_CALLER(fname, fxn, line); + pr_debug(" CF3: file/inode=%p:%p fstart=%d fend=%d\n", + file, inode, fstart, fend); + } + if (unlikely(dend != dstart)) { + PRINT_CALLER(fname, fxn, line); + pr_debug(" CF4: file/dentry=%p:%p dstart=%d dend=%d\n", + file, dentry, dstart, dend); + } + } + + /* + * check for NULL dentries inside the start/end range, or + * non-NULL dentries outside the start/end range. + */ + for (bindex = sbstart(sb); bindex < sbmax(sb); bindex++) { + lower_file = unionfs_lower_file_idx(file, bindex); + if (lower_file) { + if (unlikely(bindex < fstart || bindex > fend)) { + PRINT_CALLER(fname, fxn, line); + pr_debug(" CF5: file/lower=%p:%p bindex=%d " + "fstart/end=%d:%d\n", file, + lower_file, bindex, fstart, fend); + } + } else { /* lower_file == NULL */ + if (bindex >= fstart && bindex <= fend) { + /* + * directories can have NULL lower inodes in + * b/t start/end, but NOT if at the + * start/end range. + */ + if (unlikely(!(S_ISDIR(inode->i_mode) && + bindex > fstart && + bindex < fend))) { + PRINT_CALLER(fname, fxn, line); + pr_debug(" CF6: file/lower=%p:%p " + "bindex=%d fstart/end=%d:%d\n", + file, lower_file, bindex, + fstart, fend); + } + } + } + } + + __unionfs_check_dentry(dentry, fname, fxn, line); +} + +/* useful to track vfsmount leaks that could cause EBUSY on unmount */ +void __show_branch_counts(const struct super_block *sb, + const char *file, const char *fxn, int line) +{ + int i; + struct vfsmount *mnt; + + pr_debug("BC:"); + for (i = 0; i < sbmax(sb); i++) { + if (likely(sb->s_root)) + mnt = UNIONFS_D(sb->s_root)->lower_paths[i].mnt; + else + mnt = NULL; + printk(KERN_CONT "%d:", + (mnt ? atomic_read(&mnt->mnt_count) : -99)); + } + printk(KERN_CONT "%s:%s:%d\n", file, fxn, line); +} + +void __show_inode_times(const struct inode *inode, + const char *file, const char *fxn, int line) +{ + struct inode *lower_inode; + int bindex; + + for (bindex = ibstart(inode); bindex <= ibend(inode); bindex++) { + lower_inode = unionfs_lower_inode_idx(inode, bindex); + if (unlikely(!lower_inode)) + continue; + pr_debug("IT(%lu:%d): %s:%s:%d " + "um=%lu/%lu lm=%lu/%lu uc=%lu/%lu lc=%lu/%lu\n", + inode->i_ino, bindex, + file, fxn, line, + inode->i_mtime.tv_sec, inode->i_mtime.tv_nsec, + lower_inode->i_mtime.tv_sec, + lower_inode->i_mtime.tv_nsec, + inode->i_ctime.tv_sec, inode->i_ctime.tv_nsec, + lower_inode->i_ctime.tv_sec, + lower_inode->i_ctime.tv_nsec); + } +} + +void __show_dinode_times(const struct dentry *dentry, + const char *file, const char *fxn, int line) +{ + struct inode *inode = dentry->d_inode; + struct inode *lower_inode; + int bindex; + + for (bindex = ibstart(inode); bindex <= ibend(inode); bindex++) { + lower_inode = unionfs_lower_inode_idx(inode, bindex); + if (!lower_inode) + continue; + pr_debug("DT(%s:%lu:%d): %s:%s:%d " + "um=%lu/%lu lm=%lu/%lu uc=%lu/%lu lc=%lu/%lu\n", + dentry->d_name.name, inode->i_ino, bindex, + file, fxn, line, + inode->i_mtime.tv_sec, inode->i_mtime.tv_nsec, + lower_inode->i_mtime.tv_sec, + lower_inode->i_mtime.tv_nsec, + inode->i_ctime.tv_sec, inode->i_ctime.tv_nsec, + lower_inode->i_ctime.tv_sec, + lower_inode->i_ctime.tv_nsec); + } +} + +void __show_inode_counts(const struct inode *inode, + const char *file, const char *fxn, int line) +{ + struct inode *lower_inode; + int bindex; + + if (unlikely(!inode)) { + pr_debug("SiC: Null inode\n"); + return; + } + for (bindex = sbstart(inode->i_sb); bindex <= sbend(inode->i_sb); + bindex++) { + lower_inode = unionfs_lower_inode_idx(inode, bindex); + if (unlikely(!lower_inode)) + continue; + pr_debug("SIC(%lu:%d:%d): lc=%d %s:%s:%d\n", + inode->i_ino, bindex, + atomic_read(&(inode)->i_count), + atomic_read(&(lower_inode)->i_count), + file, fxn, line); + } +} diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/dentry.c kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/dentry.c --- kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/dentry.c 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/dentry.c 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,389 @@ +/* + * Copyright (c) 2003-2009 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2007 Josef 'Jeff' Sipek + * Copyright (c) 2005-2006 Junjiro Okajima + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2004-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2009 Stony Brook University + * Copyright (c) 2003-2009 The Research Foundation of SUNY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "union.h" + +bool is_negative_lower(const struct dentry *dentry) +{ + int bindex; + struct dentry *lower_dentry; + + BUG_ON(!dentry); + /* cache coherency: check if file was deleted on lower branch */ + if (dbstart(dentry) < 0) + return true; + for (bindex = dbstart(dentry); bindex <= dbend(dentry); bindex++) { + lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); + /* unhashed (i.e., unlinked) lower dentries don't count */ + if (lower_dentry && lower_dentry->d_inode && + !d_deleted(lower_dentry) && + !(lower_dentry->d_flags & DCACHE_NFSFS_RENAMED)) + return false; + } + return true; +} + +static inline void __dput_lowers(struct dentry *dentry, int start, int end) +{ + struct dentry *lower_dentry; + int bindex; + + if (start < 0) + return; + for (bindex = start; bindex <= end; bindex++) { + lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); + if (!lower_dentry) + continue; + unionfs_set_lower_dentry_idx(dentry, bindex, NULL); + dput(lower_dentry); + } +} + +/* + * Purge and invalidate as many data pages of a unionfs inode. This is + * called when the lower inode has changed, and we want to force processes + * to re-get the new data. + */ +static inline void purge_inode_data(struct inode *inode) +{ + /* remove all non-private mappings */ + unmap_mapping_range(inode->i_mapping, 0, 0, 0); + /* invalidate as many pages as possible */ + invalidate_mapping_pages(inode->i_mapping, 0, -1); + /* + * Don't try to truncate_inode_pages here, because this could lead + * to a deadlock between some of address_space ops and dentry + * revalidation: the address space op is invoked with a lock on our + * own page, and truncate_inode_pages will block on locked pages. + */ +} + +/* + * Revalidate a single file/symlink/special dentry. Assume that info nodes + * of the @dentry and its @parent are locked. Assume parent is valid, + * otherwise return false (and let's hope the VFS will try to re-lookup this + * dentry). Returns true if valid, false otherwise. + */ +bool __unionfs_d_revalidate(struct dentry *dentry, struct dentry *parent, + bool willwrite) +{ + bool valid = true; /* default is valid */ + struct dentry *lower_dentry; + struct dentry *result; + int bindex, bstart, bend; + int sbgen, dgen, pdgen; + int positive = 0; + int interpose_flag; + + verify_locked(dentry); + verify_locked(parent); + + /* if the dentry is unhashed, do NOT revalidate */ + if (d_deleted(dentry)) + goto out; + + dgen = atomic_read(&UNIONFS_D(dentry)->generation); + + if (is_newer_lower(dentry)) { + /* root dentry is always valid */ + if (IS_ROOT(dentry)) { + unionfs_copy_attr_times(dentry->d_inode); + } else { + /* + * reset generation number to zero, guaranteed to be + * "old" + */ + dgen = 0; + atomic_set(&UNIONFS_D(dentry)->generation, dgen); + } + if (!willwrite) + purge_inode_data(dentry->d_inode); + } + + sbgen = atomic_read(&UNIONFS_SB(dentry->d_sb)->generation); + + BUG_ON(dbstart(dentry) == -1); + if (dentry->d_inode) + positive = 1; + + /* if our dentry is valid, then validate all lower ones */ + if (sbgen == dgen) + goto validate_lowers; + + /* The root entry should always be valid */ + BUG_ON(IS_ROOT(dentry)); + + /* We can't work correctly if our parent isn't valid. */ + pdgen = atomic_read(&UNIONFS_D(parent)->generation); + + /* Free the pointers for our inodes and this dentry. */ + path_put_lowers_all(dentry, false); + + interpose_flag = INTERPOSE_REVAL_NEG; + if (positive) { + interpose_flag = INTERPOSE_REVAL; + iput_lowers_all(dentry->d_inode, true); + } + + if (realloc_dentry_private_data(dentry) != 0) { + valid = false; + goto out; + } + + result = unionfs_lookup_full(dentry, parent, interpose_flag); + if (result) { + if (IS_ERR(result)) { + valid = false; + goto out; + } + /* + * current unionfs_lookup_backend() doesn't return + * a valid dentry + */ + dput(dentry); + dentry = result; + } + + if (unlikely(positive && is_negative_lower(dentry))) { + /* call make_bad_inode here ? */ + d_drop(dentry); + valid = false; + goto out; + } + + /* + * if we got here then we have revalidated our dentry and all lower + * ones, so we can return safely. + */ + if (!valid) /* lower dentry revalidation failed */ + goto out; + + /* + * If the parent's gen no. matches the superblock's gen no., then + * we can update our denty's gen no. If they didn't match, then it + * was OK to revalidate this dentry with a stale parent, but we'll + * purposely not update our dentry's gen no. (so it can be redone); + * and, we'll mark our parent dentry as invalid so it'll force it + * (and our dentry) to be revalidated. + */ + if (pdgen == sbgen) + atomic_set(&UNIONFS_D(dentry)->generation, sbgen); + goto out; + +validate_lowers: + + /* The revalidation must occur across all branches */ + bstart = dbstart(dentry); + bend = dbend(dentry); + BUG_ON(bstart == -1); + for (bindex = bstart; bindex <= bend; bindex++) { + lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); + if (!lower_dentry || !lower_dentry->d_op + || !lower_dentry->d_op->d_revalidate) + continue; + if (!lower_dentry->d_op->d_revalidate(lower_dentry, NULL)) + valid = false; + } + + if (!dentry->d_inode || + ibstart(dentry->d_inode) < 0 || + ibend(dentry->d_inode) < 0) { + valid = false; + goto out; + } + + if (valid) { + /* + * If we get here, and we copy the meta-data from the lower + * inode to our inode, then it is vital that we have already + * purged all unionfs-level file data. We do that in the + * caller (__unionfs_d_revalidate) by calling + * purge_inode_data. + */ + unionfs_copy_attr_all(dentry->d_inode, + unionfs_lower_inode(dentry->d_inode)); + fsstack_copy_inode_size(dentry->d_inode, + unionfs_lower_inode(dentry->d_inode)); + } + +out: + return valid; +} + +/* + * Determine if the lower inode objects have changed from below the unionfs + * inode. Return true if changed, false otherwise. + * + * We check if the mtime or ctime have changed. However, the inode times + * can be changed by anyone without much protection, including + * asynchronously. This can sometimes cause unionfs to find that the lower + * file system doesn't change its inode times quick enough, resulting in a + * false positive indication (which is harmless, it just makes unionfs do + * extra work in re-validating the objects). To minimize the chances of + * these situations, we still consider such small time changes valid, but we + * don't print debugging messages unless the time changes are greater than + * UNIONFS_MIN_CC_TIME (which defaults to 3 seconds, as with NFS's acregmin) + * because significant changes are more likely due to users manually + * touching lower files. + */ +bool is_newer_lower(const struct dentry *dentry) +{ + int bindex; + struct inode *inode; + struct inode *lower_inode; + + /* ignore if we're called on semi-initialized dentries/inodes */ + if (!dentry || !UNIONFS_D(dentry)) + return false; + inode = dentry->d_inode; + if (!inode || !UNIONFS_I(inode)->lower_inodes || + ibstart(inode) < 0 || ibend(inode) < 0) + return false; + + for (bindex = ibstart(inode); bindex <= ibend(inode); bindex++) { + lower_inode = unionfs_lower_inode_idx(inode, bindex); + if (!lower_inode) + continue; + + /* check if mtime/ctime have changed */ + if (unlikely(timespec_compare(&inode->i_mtime, + &lower_inode->i_mtime) < 0)) { + if ((lower_inode->i_mtime.tv_sec - + inode->i_mtime.tv_sec) > UNIONFS_MIN_CC_TIME) { + pr_info("unionfs: new lower inode mtime " + "(bindex=%d, name=%s)\n", bindex, + dentry->d_name.name); + show_dinode_times(dentry); + } + return true; + } + if (unlikely(timespec_compare(&inode->i_ctime, + &lower_inode->i_ctime) < 0)) { + if ((lower_inode->i_ctime.tv_sec - + inode->i_ctime.tv_sec) > UNIONFS_MIN_CC_TIME) { + pr_info("unionfs: new lower inode ctime " + "(bindex=%d, name=%s)\n", bindex, + dentry->d_name.name); + show_dinode_times(dentry); + } + return true; + } + } + + /* + * Last check: if this is a positive dentry, but somehow all lower + * dentries are negative or unhashed, then this dentry needs to be + * revalidated, because someone probably deleted the objects from + * the lower branches directly. + */ + if (is_negative_lower(dentry)) + return true; + + return false; /* default: lower is not newer */ +} + +static int unionfs_d_revalidate(struct dentry *dentry, + struct nameidata *nd_unused) +{ + bool valid = true; + int err = 1; /* 1 means valid for the VFS */ + struct dentry *parent; + + unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); + parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); + unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); + + valid = __unionfs_d_revalidate(dentry, parent, false); + if (valid) { + unionfs_postcopyup_setmnt(dentry); + unionfs_check_dentry(dentry); + } else { + d_drop(dentry); + err = valid; + } + unionfs_unlock_dentry(dentry); + unionfs_unlock_parent(dentry, parent); + unionfs_read_unlock(dentry->d_sb); + + return err; +} + +static void unionfs_d_release(struct dentry *dentry) +{ + unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); + if (unlikely(!UNIONFS_D(dentry))) + goto out; /* skip if no lower branches */ + /* must lock our branch configuration here */ + unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); + + unionfs_check_dentry(dentry); + /* this could be a negative dentry, so check first */ + if (dbstart(dentry) < 0) { + unionfs_unlock_dentry(dentry); + goto out; /* due to a (normal) failed lookup */ + } + + /* Release all the lower dentries */ + path_put_lowers_all(dentry, true); + + unionfs_unlock_dentry(dentry); + +out: + free_dentry_private_data(dentry); + unionfs_read_unlock(dentry->d_sb); + return; +} + +/* + * Called when we're removing the last reference to our dentry. So we + * should drop all lower references too. + */ +static void unionfs_d_iput(struct dentry *dentry, struct inode *inode) +{ + int rc; + + BUG_ON(!dentry); + unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); + unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); + + if (!UNIONFS_D(dentry) || dbstart(dentry) < 0) + goto drop_lower_inodes; + path_put_lowers_all(dentry, false); + +drop_lower_inodes: + rc = atomic_read(&inode->i_count); + if (rc == 1 && inode->i_nlink == 1 && ibstart(inode) >= 0) { + /* see Documentation/filesystems/unionfs/issues.txt */ + lockdep_off(); + iput(unionfs_lower_inode(inode)); + lockdep_on(); + unionfs_set_lower_inode(inode, NULL); + /* XXX: may need to set start/end to -1? */ + } + + iput(inode); + + unionfs_unlock_dentry(dentry); + unionfs_read_unlock(dentry->d_sb); +} + +struct dentry_operations unionfs_dops = { + .d_revalidate = unionfs_d_revalidate, + .d_release = unionfs_d_release, + .d_iput = unionfs_d_iput, +}; diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/dirfops.c kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/dirfops.c --- kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/dirfops.c 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/dirfops.c 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2003-2009 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2007 Josef 'Jeff' Sipek + * Copyright (c) 2005-2006 Junjiro Okajima + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2004-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2009 Stony Brook University + * Copyright (c) 2003-2009 The Research Foundation of SUNY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "union.h" + +/* Make sure our rdstate is playing by the rules. */ +static void verify_rdstate_offset(struct unionfs_dir_state *rdstate) +{ + BUG_ON(rdstate->offset >= DIREOF); + BUG_ON(rdstate->cookie >= MAXRDCOOKIE); +} + +struct unionfs_getdents_callback { + struct unionfs_dir_state *rdstate; + void *dirent; + int entries_written; + int filldir_called; + int filldir_error; + filldir_t filldir; + struct super_block *sb; +}; + +/* based on generic filldir in fs/readir.c */ +static int unionfs_filldir(void *dirent, const char *oname, int namelen, + loff_t offset, u64 ino, unsigned int d_type) +{ + struct unionfs_getdents_callback *buf = dirent; + struct filldir_node *found = NULL; + int err = 0; + int is_whiteout; + char *name = (char *) oname; + + buf->filldir_called++; + + is_whiteout = is_whiteout_name(&name, &namelen); + + found = find_filldir_node(buf->rdstate, name, namelen, is_whiteout); + + if (found) { + /* + * If we had non-whiteout entry in dir cache, then mark it + * as a whiteout and but leave it in the dir cache. + */ + if (is_whiteout && !found->whiteout) + found->whiteout = is_whiteout; + goto out; + } + + /* if 'name' isn't a whiteout, filldir it. */ + if (!is_whiteout) { + off_t pos = rdstate2offset(buf->rdstate); + u64 unionfs_ino = ino; + + err = buf->filldir(buf->dirent, name, namelen, pos, + unionfs_ino, d_type); + buf->rdstate->offset++; + verify_rdstate_offset(buf->rdstate); + } + /* + * If we did fill it, stuff it in our hash, otherwise return an + * error. + */ + if (err) { + buf->filldir_error = err; + goto out; + } + buf->entries_written++; + err = add_filldir_node(buf->rdstate, name, namelen, + buf->rdstate->bindex, is_whiteout); + if (err) + buf->filldir_error = err; + +out: + return err; +} + +static int unionfs_readdir(struct file *file, void *dirent, filldir_t filldir) +{ + int err = 0; + struct file *lower_file = NULL; + struct dentry *dentry = file->f_path.dentry; + struct dentry *parent; + struct inode *inode = NULL; + struct unionfs_getdents_callback buf; + struct unionfs_dir_state *uds; + int bend; + loff_t offset; + + unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT); + parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); + unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); + + err = unionfs_file_revalidate(file, parent, false); + if (unlikely(err)) + goto out; + + inode = dentry->d_inode; + + uds = UNIONFS_F(file)->rdstate; + if (!uds) { + if (file->f_pos == DIREOF) { + goto out; + } else if (file->f_pos > 0) { + uds = find_rdstate(inode, file->f_pos); + if (unlikely(!uds)) { + err = -ESTALE; + goto out; + } + UNIONFS_F(file)->rdstate = uds; + } else { + init_rdstate(file); + uds = UNIONFS_F(file)->rdstate; + } + } + bend = fbend(file); + + while (uds->bindex <= bend) { + lower_file = unionfs_lower_file_idx(file, uds->bindex); + if (!lower_file) { + uds->bindex++; + uds->dirpos = 0; + continue; + } + + /* prepare callback buffer */ + buf.filldir_called = 0; + buf.filldir_error = 0; + buf.entries_written = 0; + buf.dirent = dirent; + buf.filldir = filldir; + buf.rdstate = uds; + buf.sb = inode->i_sb; + + /* Read starting from where we last left off. */ + offset = vfs_llseek(lower_file, uds->dirpos, SEEK_SET); + if (offset < 0) { + err = offset; + goto out; + } + err = vfs_readdir(lower_file, unionfs_filldir, &buf); + + /* Save the position for when we continue. */ + offset = vfs_llseek(lower_file, 0, SEEK_CUR); + if (offset < 0) { + err = offset; + goto out; + } + uds->dirpos = offset; + + /* Copy the atime. */ + fsstack_copy_attr_atime(inode, + lower_file->f_path.dentry->d_inode); + + if (err < 0) + goto out; + + if (buf.filldir_error) + break; + + if (!buf.entries_written) { + uds->bindex++; + uds->dirpos = 0; + } + } + + if (!buf.filldir_error && uds->bindex >= bend) { + /* Save the number of hash entries for next time. */ + UNIONFS_I(inode)->hashsize = uds->hashentries; + free_rdstate(uds); + UNIONFS_F(file)->rdstate = NULL; + file->f_pos = DIREOF; + } else { + file->f_pos = rdstate2offset(uds); + } + +out: + if (!err) + unionfs_check_file(file); + unionfs_unlock_dentry(dentry); + unionfs_unlock_parent(dentry, parent); + unionfs_read_unlock(dentry->d_sb); + return err; +} + +/* + * This is not meant to be a generic repositioning function. If you do + * things that aren't supported, then we return EINVAL. + * + * What is allowed: + * (1) seeking to the same position that you are currently at + * This really has no effect, but returns where you are. + * (2) seeking to the beginning of the file + * This throws out all state, and lets you begin again. + */ +static loff_t unionfs_dir_llseek(struct file *file, loff_t offset, int origin) +{ + struct unionfs_dir_state *rdstate; + struct dentry *dentry = file->f_path.dentry; + struct dentry *parent; + loff_t err; + + unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT); + parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); + unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); + + err = unionfs_file_revalidate(file, parent, false); + if (unlikely(err)) + goto out; + + rdstate = UNIONFS_F(file)->rdstate; + + /* + * we let users seek to their current position, but not anywhere + * else. + */ + if (!offset) { + switch (origin) { + case SEEK_SET: + if (rdstate) { + free_rdstate(rdstate); + UNIONFS_F(file)->rdstate = NULL; + } + init_rdstate(file); + err = 0; + break; + case SEEK_CUR: + err = file->f_pos; + break; + case SEEK_END: + /* Unsupported, because we would break everything. */ + err = -EINVAL; + break; + } + } else { + switch (origin) { + case SEEK_SET: + if (rdstate) { + if (offset == rdstate2offset(rdstate)) + err = offset; + else if (file->f_pos == DIREOF) + err = DIREOF; + else + err = -EINVAL; + } else { + struct inode *inode; + inode = dentry->d_inode; + rdstate = find_rdstate(inode, offset); + if (rdstate) { + UNIONFS_F(file)->rdstate = rdstate; + err = rdstate->offset; + } else { + err = -EINVAL; + } + } + break; + case SEEK_CUR: + case SEEK_END: + /* Unsupported, because we would break everything. */ + err = -EINVAL; + break; + } + } + +out: + if (!err) + unionfs_check_file(file); + unionfs_unlock_dentry(dentry); + unionfs_unlock_parent(dentry, parent); + unionfs_read_unlock(dentry->d_sb); + return err; +} + +/* + * Trimmed directory options, we shouldn't pass everything down since + * we don't want to operate on partial directories. + */ +struct file_operations unionfs_dir_fops = { + .llseek = unionfs_dir_llseek, + .read = generic_read_dir, + .readdir = unionfs_readdir, + .unlocked_ioctl = unionfs_ioctl, + .open = unionfs_open, + .release = unionfs_file_release, + .flush = unionfs_flush, + .fsync = unionfs_fsync, + .fasync = unionfs_fasync, +}; diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/dirhelper.c kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/dirhelper.c --- kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/dirhelper.c 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/dirhelper.c 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2003-2009 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2007 Josef 'Jeff' Sipek + * Copyright (c) 2005-2006 Junjiro Okajima + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2004-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2009 Stony Brook University + * Copyright (c) 2003-2009 The Research Foundation of SUNY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "union.h" + +#define RD_NONE 0 +#define RD_CHECK_EMPTY 1 +/* The callback structure for check_empty. */ +struct unionfs_rdutil_callback { + int err; + int filldir_called; + struct unionfs_dir_state *rdstate; + int mode; +}; + +/* This filldir function makes sure only whiteouts exist within a directory. */ +static int readdir_util_callback(void *dirent, const char *oname, int namelen, + loff_t offset, u64 ino, unsigned int d_type) +{ + int err = 0; + struct unionfs_rdutil_callback *buf = dirent; + int is_whiteout; + struct filldir_node *found; + char *name = (char *) oname; + + buf->filldir_called = 1; + + if (name[0] == '.' && (namelen == 1 || + (name[1] == '.' && namelen == 2))) + goto out; + + is_whiteout = is_whiteout_name(&name, &namelen); + + found = find_filldir_node(buf->rdstate, name, namelen, is_whiteout); + /* If it was found in the table there was a previous whiteout. */ + if (found) + goto out; + + /* + * if it wasn't found and isn't a whiteout, the directory isn't + * empty. + */ + err = -ENOTEMPTY; + if ((buf->mode == RD_CHECK_EMPTY) && !is_whiteout) + goto out; + + err = add_filldir_node(buf->rdstate, name, namelen, + buf->rdstate->bindex, is_whiteout); + +out: + buf->err = err; + return err; +} + +/* Is a directory logically empty? */ +int check_empty(struct dentry *dentry, struct dentry *parent, + struct unionfs_dir_state **namelist) +{ + int err = 0; + struct dentry *lower_dentry = NULL; + struct vfsmount *mnt; + struct super_block *sb; + struct file *lower_file; + struct unionfs_rdutil_callback *buf = NULL; + int bindex, bstart, bend, bopaque; + + sb = dentry->d_sb; + + + BUG_ON(!S_ISDIR(dentry->d_inode->i_mode)); + + err = unionfs_partial_lookup(dentry, parent); + if (err) + goto out; + + bstart = dbstart(dentry); + bend = dbend(dentry); + bopaque = dbopaque(dentry); + if (0 <= bopaque && bopaque < bend) + bend = bopaque; + + buf = kmalloc(sizeof(struct unionfs_rdutil_callback), GFP_KERNEL); + if (unlikely(!buf)) { + err = -ENOMEM; + goto out; + } + buf->err = 0; + buf->mode = RD_CHECK_EMPTY; + buf->rdstate = alloc_rdstate(dentry->d_inode, bstart); + if (unlikely(!buf->rdstate)) { + err = -ENOMEM; + goto out; + } + + /* Process the lower directories with rdutil_callback as a filldir. */ + for (bindex = bstart; bindex <= bend; bindex++) { + lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); + if (!lower_dentry) + continue; + if (!lower_dentry->d_inode) + continue; + if (!S_ISDIR(lower_dentry->d_inode->i_mode)) + continue; + + dget(lower_dentry); + mnt = unionfs_mntget(dentry, bindex); + branchget(sb, bindex); + lower_file = dentry_open(lower_dentry, mnt, O_RDONLY); + if (IS_ERR(lower_file)) { + err = PTR_ERR(lower_file); + branchput(sb, bindex); + goto out; + } + + do { + buf->filldir_called = 0; + buf->rdstate->bindex = bindex; + err = vfs_readdir(lower_file, + readdir_util_callback, buf); + if (buf->err) + err = buf->err; + } while ((err >= 0) && buf->filldir_called); + + /* fput calls dput for lower_dentry */ + fput(lower_file); + branchput(sb, bindex); + + if (err < 0) + goto out; + } + +out: + if (buf) { + if (namelist && !err) + *namelist = buf->rdstate; + else if (buf->rdstate) + free_rdstate(buf->rdstate); + kfree(buf); + } + + + return err; +} diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/fanout.h kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/fanout.h --- kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/fanout.h 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/fanout.h 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,407 @@ +/* + * Copyright (c) 2003-2009 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2007 Josef 'Jeff' Sipek + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2004-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2009 Stony Brook University + * Copyright (c) 2003-2009 The Research Foundation of SUNY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _FANOUT_H_ +#define _FANOUT_H_ + +/* + * Inode to private data + * + * Since we use containers and the struct inode is _inside_ the + * unionfs_inode_info structure, UNIONFS_I will always (given a non-NULL + * inode pointer), return a valid non-NULL pointer. + */ +static inline struct unionfs_inode_info *UNIONFS_I(const struct inode *inode) +{ + return container_of(inode, struct unionfs_inode_info, vfs_inode); +} + +#define ibstart(ino) (UNIONFS_I(ino)->bstart) +#define ibend(ino) (UNIONFS_I(ino)->bend) + +/* Dentry to private data */ +#define UNIONFS_D(dent) ((struct unionfs_dentry_info *)(dent)->d_fsdata) +#define dbstart(dent) (UNIONFS_D(dent)->bstart) +#define dbend(dent) (UNIONFS_D(dent)->bend) +#define dbopaque(dent) (UNIONFS_D(dent)->bopaque) + +/* Superblock to private data */ +#define UNIONFS_SB(super) ((struct unionfs_sb_info *)(super)->s_fs_info) +#define sbstart(sb) 0 +#define sbend(sb) (UNIONFS_SB(sb)->bend) +#define sbmax(sb) (UNIONFS_SB(sb)->bend + 1) +#define sbhbid(sb) (UNIONFS_SB(sb)->high_branch_id) + +/* File to private Data */ +#define UNIONFS_F(file) ((struct unionfs_file_info *)((file)->private_data)) +#define fbstart(file) (UNIONFS_F(file)->bstart) +#define fbend(file) (UNIONFS_F(file)->bend) + +/* macros to manipulate branch IDs in stored in our superblock */ +static inline int branch_id(struct super_block *sb, int index) +{ + BUG_ON(!sb || index < 0); + return UNIONFS_SB(sb)->data[index].branch_id; +} + +static inline void set_branch_id(struct super_block *sb, int index, int val) +{ + BUG_ON(!sb || index < 0); + UNIONFS_SB(sb)->data[index].branch_id = val; +} + +static inline void new_branch_id(struct super_block *sb, int index) +{ + BUG_ON(!sb || index < 0); + set_branch_id(sb, index, ++UNIONFS_SB(sb)->high_branch_id); +} + +/* + * Find new index of matching branch with an existing superblock of a known + * (possibly old) id. This is needed because branches could have been + * added/deleted causing the branches of any open files to shift. + * + * @sb: the new superblock which may have new/different branch IDs + * @id: the old/existing id we're looking for + * Returns index of newly found branch (0 or greater), -1 otherwise. + */ +static inline int branch_id_to_idx(struct super_block *sb, int id) +{ + int i; + for (i = 0; i < sbmax(sb); i++) { + if (branch_id(sb, i) == id) + return i; + } + /* in the non-ODF code, this should really never happen */ + printk(KERN_WARNING "unionfs: cannot find branch with id %d\n", id); + return -1; +} + +/* File to lower file. */ +static inline struct file *unionfs_lower_file(const struct file *f) +{ + BUG_ON(!f); + return UNIONFS_F(f)->lower_files[fbstart(f)]; +} + +static inline struct file *unionfs_lower_file_idx(const struct file *f, + int index) +{ + BUG_ON(!f || index < 0); + return UNIONFS_F(f)->lower_files[index]; +} + +static inline void unionfs_set_lower_file_idx(struct file *f, int index, + struct file *val) +{ + BUG_ON(!f || index < 0); + UNIONFS_F(f)->lower_files[index] = val; + /* save branch ID (may be redundant?) */ + UNIONFS_F(f)->saved_branch_ids[index] = + branch_id((f)->f_path.dentry->d_sb, index); +} + +static inline void unionfs_set_lower_file(struct file *f, struct file *val) +{ + BUG_ON(!f); + unionfs_set_lower_file_idx((f), fbstart(f), (val)); +} + +/* Inode to lower inode. */ +static inline struct inode *unionfs_lower_inode(const struct inode *i) +{ + BUG_ON(!i); + return UNIONFS_I(i)->lower_inodes[ibstart(i)]; +} + +static inline struct inode *unionfs_lower_inode_idx(const struct inode *i, + int index) +{ + BUG_ON(!i || index < 0); + return UNIONFS_I(i)->lower_inodes[index]; +} + +static inline void unionfs_set_lower_inode_idx(struct inode *i, int index, + struct inode *val) +{ + BUG_ON(!i || index < 0); + UNIONFS_I(i)->lower_inodes[index] = val; +} + +static inline void unionfs_set_lower_inode(struct inode *i, struct inode *val) +{ + BUG_ON(!i); + UNIONFS_I(i)->lower_inodes[ibstart(i)] = val; +} + +/* Superblock to lower superblock. */ +static inline struct super_block *unionfs_lower_super( + const struct super_block *sb) +{ + BUG_ON(!sb); + return UNIONFS_SB(sb)->data[sbstart(sb)].sb; +} + +static inline struct super_block *unionfs_lower_super_idx( + const struct super_block *sb, + int index) +{ + BUG_ON(!sb || index < 0); + return UNIONFS_SB(sb)->data[index].sb; +} + +static inline void unionfs_set_lower_super_idx(struct super_block *sb, + int index, + struct super_block *val) +{ + BUG_ON(!sb || index < 0); + UNIONFS_SB(sb)->data[index].sb = val; +} + +static inline void unionfs_set_lower_super(struct super_block *sb, + struct super_block *val) +{ + BUG_ON(!sb); + UNIONFS_SB(sb)->data[sbstart(sb)].sb = val; +} + +/* Branch count macros. */ +static inline int branch_count(const struct super_block *sb, int index) +{ + BUG_ON(!sb || index < 0); + return atomic_read(&UNIONFS_SB(sb)->data[index].open_files); +} + +static inline void set_branch_count(struct super_block *sb, int index, int val) +{ + BUG_ON(!sb || index < 0); + atomic_set(&UNIONFS_SB(sb)->data[index].open_files, val); +} + +static inline void branchget(struct super_block *sb, int index) +{ + BUG_ON(!sb || index < 0); + atomic_inc(&UNIONFS_SB(sb)->data[index].open_files); +} + +static inline void branchput(struct super_block *sb, int index) +{ + BUG_ON(!sb || index < 0); + atomic_dec(&UNIONFS_SB(sb)->data[index].open_files); +} + +/* Dentry macros */ +static inline void unionfs_set_lower_dentry_idx(struct dentry *dent, int index, + struct dentry *val) +{ + BUG_ON(!dent || index < 0); + UNIONFS_D(dent)->lower_paths[index].dentry = val; +} + +static inline struct dentry *unionfs_lower_dentry_idx( + const struct dentry *dent, + int index) +{ + BUG_ON(!dent || index < 0); + return UNIONFS_D(dent)->lower_paths[index].dentry; +} + +static inline struct dentry *unionfs_lower_dentry(const struct dentry *dent) +{ + BUG_ON(!dent); + return unionfs_lower_dentry_idx(dent, dbstart(dent)); +} + +static inline void unionfs_set_lower_mnt_idx(struct dentry *dent, int index, + struct vfsmount *mnt) +{ + BUG_ON(!dent || index < 0); + UNIONFS_D(dent)->lower_paths[index].mnt = mnt; +} + +static inline struct vfsmount *unionfs_lower_mnt_idx( + const struct dentry *dent, + int index) +{ + BUG_ON(!dent || index < 0); + return UNIONFS_D(dent)->lower_paths[index].mnt; +} + +static inline struct vfsmount *unionfs_lower_mnt(const struct dentry *dent) +{ + BUG_ON(!dent); + return unionfs_lower_mnt_idx(dent, dbstart(dent)); +} + +/* Macros for locking a dentry. */ +enum unionfs_dentry_lock_class { + UNIONFS_DMUTEX_NORMAL, + UNIONFS_DMUTEX_ROOT, + UNIONFS_DMUTEX_PARENT, + UNIONFS_DMUTEX_CHILD, + UNIONFS_DMUTEX_WHITEOUT, + UNIONFS_DMUTEX_REVAL_PARENT, /* for file/dentry revalidate */ + UNIONFS_DMUTEX_REVAL_CHILD, /* for file/dentry revalidate */ +}; + +static inline void unionfs_lock_dentry(struct dentry *d, + unsigned int subclass) +{ + BUG_ON(!d); + mutex_lock_nested(&UNIONFS_D(d)->lock, subclass); +} + +static inline void unionfs_unlock_dentry(struct dentry *d) +{ + BUG_ON(!d); + mutex_unlock(&UNIONFS_D(d)->lock); +} + +static inline struct dentry *unionfs_lock_parent(struct dentry *d, + unsigned int subclass) +{ + struct dentry *p; + + BUG_ON(!d); + p = dget_parent(d); + if (p != d) + mutex_lock_nested(&UNIONFS_D(p)->lock, subclass); + return p; +} + +static inline void unionfs_unlock_parent(struct dentry *d, struct dentry *p) +{ + BUG_ON(!d); + BUG_ON(!p); + if (p != d) { + BUG_ON(!mutex_is_locked(&UNIONFS_D(p)->lock)); + mutex_unlock(&UNIONFS_D(p)->lock); + } + dput(p); +} + +static inline void verify_locked(struct dentry *d) +{ + BUG_ON(!d); + BUG_ON(!mutex_is_locked(&UNIONFS_D(d)->lock)); +} + +/* macros to put lower objects */ + +/* + * iput lower inodes of an unionfs dentry, from bstart to bend. If + * @free_lower is true, then also kfree the memory used to hold the lower + * object pointers. + */ +static inline void iput_lowers(struct inode *inode, + int bstart, int bend, bool free_lower) +{ + struct inode *lower_inode; + int bindex; + + BUG_ON(!inode); + BUG_ON(!UNIONFS_I(inode)); + BUG_ON(bstart < 0); + + for (bindex = bstart; bindex <= bend; bindex++) { + lower_inode = unionfs_lower_inode_idx(inode, bindex); + if (lower_inode) { + unionfs_set_lower_inode_idx(inode, bindex, NULL); + /* see Documentation/filesystems/unionfs/issues.txt */ + lockdep_off(); + iput(lower_inode); + lockdep_on(); + } + } + + if (free_lower) { + kfree(UNIONFS_I(inode)->lower_inodes); + UNIONFS_I(inode)->lower_inodes = NULL; + } +} + +/* iput all lower inodes, and reset start/end branch indices to -1 */ +static inline void iput_lowers_all(struct inode *inode, bool free_lower) +{ + int bstart, bend; + + BUG_ON(!inode); + BUG_ON(!UNIONFS_I(inode)); + bstart = ibstart(inode); + bend = ibend(inode); + BUG_ON(bstart < 0); + + iput_lowers(inode, bstart, bend, free_lower); + ibstart(inode) = ibend(inode) = -1; +} + +/* + * dput/mntput all lower dentries and vfsmounts of an unionfs dentry, from + * bstart to bend. If @free_lower is true, then also kfree the memory used + * to hold the lower object pointers. + * + * XXX: implement using path_put VFS macros + */ +static inline void path_put_lowers(struct dentry *dentry, + int bstart, int bend, bool free_lower) +{ + struct dentry *lower_dentry; + struct vfsmount *lower_mnt; + int bindex; + + BUG_ON(!dentry); + BUG_ON(!UNIONFS_D(dentry)); + BUG_ON(bstart < 0); + + for (bindex = bstart; bindex <= bend; bindex++) { + lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); + if (lower_dentry) { + unionfs_set_lower_dentry_idx(dentry, bindex, NULL); + dput(lower_dentry); + } + lower_mnt = unionfs_lower_mnt_idx(dentry, bindex); + if (lower_mnt) { + unionfs_set_lower_mnt_idx(dentry, bindex, NULL); + mntput(lower_mnt); + } + } + + if (free_lower) { + kfree(UNIONFS_D(dentry)->lower_paths); + UNIONFS_D(dentry)->lower_paths = NULL; + } +} + +/* + * dput/mntput all lower dentries and vfsmounts, and reset start/end branch + * indices to -1. + */ +static inline void path_put_lowers_all(struct dentry *dentry, bool free_lower) +{ + int bstart, bend; + + BUG_ON(!dentry); + BUG_ON(!UNIONFS_D(dentry)); + bstart = dbstart(dentry); + bend = dbend(dentry); + BUG_ON(bstart < 0); + + path_put_lowers(dentry, bstart, bend, free_lower); + dbstart(dentry) = dbend(dentry) = -1; +} + +#endif /* not _FANOUT_H */ diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/file.c kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/file.c --- kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/file.c 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/file.c 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,404 @@ +/* + * Copyright (c) 2003-2009 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2007 Josef 'Jeff' Sipek + * Copyright (c) 2005-2006 Junjiro Okajima + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2004-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2009 Stony Brook University + * Copyright (c) 2003-2009 The Research Foundation of SUNY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "union.h" + +static ssize_t unionfs_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int err; + struct file *lower_file; + struct dentry *dentry = file->f_path.dentry; + struct dentry *parent; + + unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT); + parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); + unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); + + err = unionfs_file_revalidate(file, parent, false); + if (unlikely(err)) + goto out; + + lower_file = unionfs_lower_file(file); + err = vfs_read(lower_file, buf, count, ppos); + /* update our inode atime upon a successful lower read */ + if (err >= 0) { + fsstack_copy_attr_atime(dentry->d_inode, + lower_file->f_path.dentry->d_inode); + unionfs_check_file(file); + } + +out: + unionfs_unlock_dentry(dentry); + unionfs_unlock_parent(dentry, parent); + unionfs_read_unlock(dentry->d_sb); + return err; +} + +static ssize_t unionfs_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + int err = 0; + struct file *lower_file; + struct dentry *dentry = file->f_path.dentry; + struct dentry *parent; + + unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT); + parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); + unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); + + err = unionfs_file_revalidate(file, parent, true); + if (unlikely(err)) + goto out; + + lower_file = unionfs_lower_file(file); + err = vfs_write(lower_file, buf, count, ppos); + /* update our inode times+sizes upon a successful lower write */ + if (err >= 0) { + fsstack_copy_inode_size(dentry->d_inode, + lower_file->f_path.dentry->d_inode); + fsstack_copy_attr_times(dentry->d_inode, + lower_file->f_path.dentry->d_inode); + UNIONFS_F(file)->wrote_to_file = true; /* for delayed copyup */ + unionfs_check_file(file); + } + +out: + unionfs_unlock_dentry(dentry); + unionfs_unlock_parent(dentry, parent); + unionfs_read_unlock(dentry->d_sb); + return err; +} + +static int unionfs_file_readdir(struct file *file, void *dirent, + filldir_t filldir) +{ + return -ENOTDIR; +} + +static int unionfs_mmap(struct file *file, struct vm_area_struct *vma) +{ + int err = 0; + bool willwrite; + struct file *lower_file; + struct dentry *dentry = file->f_path.dentry; + struct dentry *parent; + struct vm_operations_struct *saved_vm_ops = NULL; + + unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT); + parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); + unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); + + /* This might be deferred to mmap's writepage */ + willwrite = ((vma->vm_flags | VM_SHARED | VM_WRITE) == vma->vm_flags); + err = unionfs_file_revalidate(file, parent, willwrite); + if (unlikely(err)) + goto out; + unionfs_check_file(file); + + /* + * File systems which do not implement ->writepage may use + * generic_file_readonly_mmap as their ->mmap op. If you call + * generic_file_readonly_mmap with VM_WRITE, you'd get an -EINVAL. + * But we cannot call the lower ->mmap op, so we can't tell that + * writeable mappings won't work. Therefore, our only choice is to + * check if the lower file system supports the ->writepage, and if + * not, return EINVAL (the same error that + * generic_file_readonly_mmap returns in that case). + */ + lower_file = unionfs_lower_file(file); + if (willwrite && !lower_file->f_mapping->a_ops->writepage) { + err = -EINVAL; + printk(KERN_ERR "unionfs: branch %d file system does not " + "support writeable mmap\n", fbstart(file)); + goto out; + } + + /* + * find and save lower vm_ops. + * + * XXX: the VFS should have a cleaner way of finding the lower vm_ops + */ + if (!UNIONFS_F(file)->lower_vm_ops) { + err = lower_file->f_op->mmap(lower_file, vma); + if (err) { + printk(KERN_ERR "unionfs: lower mmap failed %d\n", err); + goto out; + } + saved_vm_ops = vma->vm_ops; + err = do_munmap(current->mm, vma->vm_start, + vma->vm_end - vma->vm_start); + if (err) { + printk(KERN_ERR "unionfs: do_munmap failed %d\n", err); + goto out; + } + } + + file->f_mapping->a_ops = &unionfs_dummy_aops; + err = generic_file_mmap(file, vma); + file->f_mapping->a_ops = &unionfs_aops; + if (err) { + printk(KERN_ERR "unionfs: generic_file_mmap failed %d\n", err); + goto out; + } + vma->vm_ops = &unionfs_vm_ops; + if (!UNIONFS_F(file)->lower_vm_ops) + UNIONFS_F(file)->lower_vm_ops = saved_vm_ops; + +out: + if (!err) { + /* copyup could cause parent dir times to change */ + unionfs_copy_attr_times(parent->d_inode); + unionfs_check_file(file); + } + unionfs_unlock_dentry(dentry); + unionfs_unlock_parent(dentry, parent); + unionfs_read_unlock(dentry->d_sb); + return err; +} + +int unionfs_fsync(struct file *file, struct dentry *dentry, int datasync) +{ + int bindex, bstart, bend; + struct file *lower_file; + struct dentry *lower_dentry; + struct dentry *parent; + struct inode *lower_inode, *inode; + int err = -EINVAL; + + unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT); + parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); + unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); + + err = unionfs_file_revalidate(file, parent, true); + if (unlikely(err)) + goto out; + unionfs_check_file(file); + + bstart = fbstart(file); + bend = fbend(file); + if (bstart < 0 || bend < 0) + goto out; + + inode = dentry->d_inode; + if (unlikely(!inode)) { + printk(KERN_ERR + "unionfs: null lower inode in unionfs_fsync\n"); + goto out; + } + for (bindex = bstart; bindex <= bend; bindex++) { + lower_inode = unionfs_lower_inode_idx(inode, bindex); + if (!lower_inode || !lower_inode->i_fop->fsync) + continue; + lower_file = unionfs_lower_file_idx(file, bindex); + lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); + mutex_lock(&lower_inode->i_mutex); + err = lower_inode->i_fop->fsync(lower_file, + lower_dentry, + datasync); + if (!err && bindex == bstart) + fsstack_copy_attr_times(inode, lower_inode); + mutex_unlock(&lower_inode->i_mutex); + if (err) + goto out; + } + +out: + if (!err) + unionfs_check_file(file); + unionfs_unlock_dentry(dentry); + unionfs_unlock_parent(dentry, parent); + unionfs_read_unlock(dentry->d_sb); + return err; +} + +int unionfs_fasync(int fd, struct file *file, int flag) +{ + int bindex, bstart, bend; + struct file *lower_file; + struct dentry *dentry = file->f_path.dentry; + struct dentry *parent; + struct inode *lower_inode, *inode; + int err = 0; + + unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT); + parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); + unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); + + err = unionfs_file_revalidate(file, parent, true); + if (unlikely(err)) + goto out; + unionfs_check_file(file); + + bstart = fbstart(file); + bend = fbend(file); + if (bstart < 0 || bend < 0) + goto out; + + inode = dentry->d_inode; + if (unlikely(!inode)) { + printk(KERN_ERR + "unionfs: null lower inode in unionfs_fasync\n"); + goto out; + } + for (bindex = bstart; bindex <= bend; bindex++) { + lower_inode = unionfs_lower_inode_idx(inode, bindex); + if (!lower_inode || !lower_inode->i_fop->fasync) + continue; + lower_file = unionfs_lower_file_idx(file, bindex); + mutex_lock(&lower_inode->i_mutex); + err = lower_inode->i_fop->fasync(fd, lower_file, flag); + if (!err && bindex == bstart) + fsstack_copy_attr_times(inode, lower_inode); + mutex_unlock(&lower_inode->i_mutex); + if (err) + goto out; + } + +out: + if (!err) + unionfs_check_file(file); + unionfs_unlock_dentry(dentry); + unionfs_unlock_parent(dentry, parent); + unionfs_read_unlock(dentry->d_sb); + return err; +} + +static ssize_t unionfs_splice_read(struct file *file, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, + unsigned int flags) +{ + ssize_t err; + struct file *lower_file; + struct dentry *dentry = file->f_path.dentry; + struct dentry *parent; + + unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT); + parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); + unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); + + err = unionfs_file_revalidate(file, parent, false); + if (unlikely(err)) + goto out; + + lower_file = unionfs_lower_file(file); + err = vfs_splice_to(lower_file, ppos, pipe, len, flags); + /* update our inode atime upon a successful lower splice-read */ + if (err >= 0) { + fsstack_copy_attr_atime(dentry->d_inode, + lower_file->f_path.dentry->d_inode); + unionfs_check_file(file); + } + +out: + unionfs_unlock_dentry(dentry); + unionfs_unlock_parent(dentry, parent); + unionfs_read_unlock(dentry->d_sb); + return err; +} + +static ssize_t unionfs_splice_write(struct pipe_inode_info *pipe, + struct file *file, loff_t *ppos, + size_t len, unsigned int flags) +{ + ssize_t err = 0; + struct file *lower_file; + struct dentry *dentry = file->f_path.dentry; + struct dentry *parent; + + unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT); + parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); + unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); + + err = unionfs_file_revalidate(file, parent, true); + if (unlikely(err)) + goto out; + + lower_file = unionfs_lower_file(file); + err = vfs_splice_from(pipe, lower_file, ppos, len, flags); + /* update our inode times+sizes upon a successful lower write */ + if (err >= 0) { + fsstack_copy_inode_size(dentry->d_inode, + lower_file->f_path.dentry->d_inode); + fsstack_copy_attr_times(dentry->d_inode, + lower_file->f_path.dentry->d_inode); + unionfs_check_file(file); + } + +out: + unionfs_unlock_dentry(dentry); + unionfs_unlock_parent(dentry, parent); + unionfs_read_unlock(dentry->d_sb); + return err; +} + +static ssize_t unionfs_sendfile(struct file *file, loff_t *ppos, size_t count, + read_actor_t actor, void *target) +{ + ssize_t err; + struct file *lower_file; + struct dentry *dentry = file->f_path.dentry; + struct dentry *parent; + + unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT); + parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); + unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); + + err = unionfs_file_revalidate(file, parent, false); + if (unlikely(err)) + goto out; + + lower_file = unionfs_lower_file(file); + + if (!lower_file->f_op || !lower_file->f_op->sendfile) { + err = -EINVAL; + goto out; + } + + err = lower_file->f_op->sendfile(lower_file, ppos, count, + actor, target); + /* update our inode atime upon a successful lower sendfile */ + if (err >= 0) { + fsstack_copy_attr_atime(dentry->d_inode, + lower_file->f_path.dentry->d_inode); + unionfs_check_file(file); + } + +out: + unionfs_unlock_dentry(dentry); + unionfs_unlock_parent(dentry, parent); + unionfs_read_unlock(dentry->d_sb); + return err; +} + +struct file_operations unionfs_main_fops = { + .llseek = generic_file_llseek, + .read = unionfs_read, + .write = unionfs_write, + .readdir = unionfs_file_readdir, + .unlocked_ioctl = unionfs_ioctl, + .mmap = unionfs_mmap, + .open = unionfs_open, + .flush = unionfs_flush, + .release = unionfs_file_release, + .fsync = unionfs_fsync, + .fasync = unionfs_fasync, + .sendfile = unionfs_sendfile, + .splice_read = unionfs_splice_read, + .splice_write = unionfs_splice_write, +}; diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/inode.c kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/inode.c --- kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/inode.c 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/inode.c 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,1021 @@ +/* + * Copyright (c) 2003-2009 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2007 Josef 'Jeff' Sipek + * Copyright (c) 2005-2006 Junjiro Okajima + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2004-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2009 Stony Brook University + * Copyright (c) 2003-2009 The Research Foundation of SUNY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "union.h" + +/* + * Find a writeable branch to create new object in. Checks all writeble + * branches of the parent inode, from istart to iend order; if none are + * suitable, also tries branch 0 (which may require a copyup). + * + * Return a lower_dentry we can use to create object in, or ERR_PTR. + */ +static struct dentry *find_writeable_branch(struct inode *parent, + struct dentry *dentry) +{ + int err = -EINVAL; + int bindex, istart, iend; + struct dentry *lower_dentry = NULL; + + istart = ibstart(parent); + iend = ibend(parent); + if (istart < 0) + goto out; + +begin: + for (bindex = istart; bindex <= iend; bindex++) { + /* skip non-writeable branches */ + err = is_robranch_super(dentry->d_sb, bindex); + if (err) { + err = -EROFS; + continue; + } + lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); + if (!lower_dentry) + continue; + /* + * check for whiteouts in writeable branch, and remove them + * if necessary. + */ + err = check_unlink_whiteout(dentry, lower_dentry, bindex); + if (err > 0) /* ignore if whiteout found and removed */ + err = 0; + if (err) + continue; + /* if get here, we can write to the branch */ + break; + } + /* + * If istart wasn't already branch 0, and we got any error, then try + * branch 0 (which may require copyup) + */ + if (err && istart > 0) { + istart = iend = 0; + goto begin; + } + + /* + * If we tried even branch 0, and still got an error, abort. But if + * the error was an EROFS, then we should try to copyup. + */ + if (err && err != -EROFS) + goto out; + + /* + * If we get here, then check if copyup needed. If lower_dentry is + * NULL, create the entire dentry directory structure in branch 0. + */ + if (!lower_dentry) { + bindex = 0; + lower_dentry = create_parents(parent, dentry, + dentry->d_name.name, bindex); + if (IS_ERR(lower_dentry)) { + err = PTR_ERR(lower_dentry); + goto out; + } + } + err = 0; /* all's well */ +out: + if (err) + return ERR_PTR(err); + return lower_dentry; +} + +static int unionfs_create(struct inode *dir, struct dentry *dentry, + int mode, struct nameidata *nd_unused) +{ + int err = 0; + struct dentry *lower_dentry = NULL; + struct dentry *lower_parent_dentry = NULL; + struct dentry *parent; + int valid = 0; + + unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); + parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); + unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); + + valid = __unionfs_d_revalidate(dentry, parent, false); + if (unlikely(!valid)) { + err = -ESTALE; /* same as what real_lookup does */ + goto out; + } + + lower_dentry = find_writeable_branch(dir, dentry); + if (IS_ERR(lower_dentry)) { + err = PTR_ERR(lower_dentry); + goto out; + } + + lower_parent_dentry = lock_parent(lower_dentry); + if (IS_ERR(lower_parent_dentry)) { + err = PTR_ERR(lower_parent_dentry); + goto out; + } + + err = vfs_create(lower_parent_dentry->d_inode, lower_dentry, mode, + NULL); + + if (!err) { + err = PTR_ERR(unionfs_interpose(dentry, dir->i_sb, 0)); + if (!err) { + unionfs_copy_attr_times(dir); + fsstack_copy_inode_size(dir, + lower_parent_dentry->d_inode); + /* update no. of links on parent directory */ + dir->i_nlink = unionfs_get_nlinks(dir); + } + } + + unlock_dir(lower_parent_dentry); + +out: + if (!err) { + unionfs_postcopyup_setmnt(dentry); + unionfs_check_inode(dir); + unionfs_check_dentry(dentry); + } + unionfs_unlock_dentry(dentry); + unionfs_unlock_parent(dentry, parent); + unionfs_read_unlock(dentry->d_sb); + return err; +} + +/* + * unionfs_lookup is the only special function which takes a dentry, yet we + * do NOT want to call __unionfs_d_revalidate_chain because by definition, + * we don't have a valid dentry here yet. + */ +static struct dentry *unionfs_lookup(struct inode *dir, + struct dentry *dentry, + struct nameidata *nd_unused) +{ + struct dentry *ret, *parent; + int err = 0; + + unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); + parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); + + /* + * As long as we lock/dget the parent, then can skip validating the + * parent now; we may have to rebuild this dentry on the next + * ->d_revalidate, however. + */ + + /* allocate dentry private data. We free it in ->d_release */ + err = new_dentry_private_data(dentry, UNIONFS_DMUTEX_CHILD); + if (unlikely(err)) { + ret = ERR_PTR(err); + goto out; + } + + ret = unionfs_lookup_full(dentry, parent, INTERPOSE_LOOKUP); + + if (!IS_ERR(ret)) { + if (ret) + dentry = ret; + /* lookup_full can return multiple positive dentries */ + if (dentry->d_inode && !S_ISDIR(dentry->d_inode->i_mode)) { + BUG_ON(dbstart(dentry) < 0); + unionfs_postcopyup_release(dentry); + } + unionfs_copy_attr_times(dentry->d_inode); + } + + unionfs_check_inode(dir); + if (!IS_ERR(ret)) + unionfs_check_dentry(dentry); + unionfs_check_dentry(parent); + unionfs_unlock_dentry(dentry); /* locked in new_dentry_private data */ + +out: + unionfs_unlock_parent(dentry, parent); + unionfs_read_unlock(dentry->d_sb); + + return ret; +} + +static int unionfs_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *new_dentry) +{ + int err = 0; + struct dentry *lower_old_dentry = NULL; + struct dentry *lower_new_dentry = NULL; + struct dentry *lower_dir_dentry = NULL; + struct dentry *old_parent, *new_parent; + char *name = NULL; + bool valid; + + unionfs_read_lock(old_dentry->d_sb, UNIONFS_SMUTEX_CHILD); + old_parent = dget_parent(old_dentry); + new_parent = dget_parent(new_dentry); + unionfs_double_lock_parents(old_parent, new_parent); + unionfs_double_lock_dentry(old_dentry, new_dentry); + + valid = __unionfs_d_revalidate(old_dentry, old_parent, false); + if (unlikely(!valid)) { + err = -ESTALE; + goto out; + } + if (new_dentry->d_inode) { + valid = __unionfs_d_revalidate(new_dentry, new_parent, false); + if (unlikely(!valid)) { + err = -ESTALE; + goto out; + } + } + + lower_new_dentry = unionfs_lower_dentry(new_dentry); + + /* check for a whiteout in new dentry branch, and delete it */ + err = check_unlink_whiteout(new_dentry, lower_new_dentry, + dbstart(new_dentry)); + if (err > 0) { /* whiteout found and removed successfully */ + lower_dir_dentry = dget_parent(lower_new_dentry); + fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode); + dput(lower_dir_dentry); + dir->i_nlink = unionfs_get_nlinks(dir); + err = 0; + } + if (err) + goto out; + + /* check if parent hierachy is needed, then link in same branch */ + if (dbstart(old_dentry) != dbstart(new_dentry)) { + lower_new_dentry = create_parents(dir, new_dentry, + new_dentry->d_name.name, + dbstart(old_dentry)); + err = PTR_ERR(lower_new_dentry); + if (IS_COPYUP_ERR(err)) + goto docopyup; + if (!lower_new_dentry || IS_ERR(lower_new_dentry)) + goto out; + } + lower_new_dentry = unionfs_lower_dentry(new_dentry); + lower_old_dentry = unionfs_lower_dentry(old_dentry); + + BUG_ON(dbstart(old_dentry) != dbstart(new_dentry)); + lower_dir_dentry = lock_parent(lower_new_dentry); + err = is_robranch(old_dentry); + if (!err) { + /* see Documentation/filesystems/unionfs/issues.txt */ + lockdep_off(); + err = vfs_link(lower_old_dentry, lower_dir_dentry->d_inode, + lower_new_dentry); + lockdep_on(); + } + unlock_dir(lower_dir_dentry); + +docopyup: + if (IS_COPYUP_ERR(err)) { + int old_bstart = dbstart(old_dentry); + int bindex; + + for (bindex = old_bstart - 1; bindex >= 0; bindex--) { + err = copyup_dentry(old_parent->d_inode, + old_dentry, old_bstart, + bindex, old_dentry->d_name.name, + old_dentry->d_name.len, NULL, + i_size_read(old_dentry->d_inode)); + if (err) + continue; + lower_new_dentry = + create_parents(dir, new_dentry, + new_dentry->d_name.name, + bindex); + lower_old_dentry = unionfs_lower_dentry(old_dentry); + lower_dir_dentry = lock_parent(lower_new_dentry); + /* see Documentation/filesystems/unionfs/issues.txt */ + lockdep_off(); + /* do vfs_link */ + err = vfs_link(lower_old_dentry, + lower_dir_dentry->d_inode, + lower_new_dentry); + lockdep_on(); + unlock_dir(lower_dir_dentry); + goto check_link; + } + goto out; + } + +check_link: + if (err || !lower_new_dentry->d_inode) + goto out; + + /* Its a hard link, so use the same inode */ + new_dentry->d_inode = igrab(old_dentry->d_inode); + d_add(new_dentry, new_dentry->d_inode); + unionfs_copy_attr_all(dir, lower_new_dentry->d_parent->d_inode); + fsstack_copy_inode_size(dir, lower_new_dentry->d_parent->d_inode); + + /* propagate number of hard-links */ + old_dentry->d_inode->i_nlink = unionfs_get_nlinks(old_dentry->d_inode); + /* new dentry's ctime may have changed due to hard-link counts */ + unionfs_copy_attr_times(new_dentry->d_inode); + +out: + if (!new_dentry->d_inode) + d_drop(new_dentry); + + kfree(name); + if (!err) + unionfs_postcopyup_setmnt(new_dentry); + + unionfs_check_inode(dir); + unionfs_check_dentry(new_dentry); + unionfs_check_dentry(old_dentry); + + unionfs_double_unlock_dentry(old_dentry, new_dentry); + unionfs_double_unlock_parents(old_parent, new_parent); + dput(new_parent); + dput(old_parent); + unionfs_read_unlock(old_dentry->d_sb); + + return err; +} + +static int unionfs_symlink(struct inode *dir, struct dentry *dentry, + const char *symname) +{ + int err = 0; + struct dentry *lower_dentry = NULL; + struct dentry *wh_dentry = NULL; + struct dentry *lower_parent_dentry = NULL; + struct dentry *parent; + char *name = NULL; + int valid = 0; + umode_t mode; + + unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); + parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); + unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); + + valid = __unionfs_d_revalidate(dentry, parent, false); + if (unlikely(!valid)) { + err = -ESTALE; + goto out; + } + + /* + * It's only a bug if this dentry was not negative and couldn't be + * revalidated (shouldn't happen). + */ + BUG_ON(!valid && dentry->d_inode); + + lower_dentry = find_writeable_branch(dir, dentry); + if (IS_ERR(lower_dentry)) { + err = PTR_ERR(lower_dentry); + goto out; + } + + lower_parent_dentry = lock_parent(lower_dentry); + if (IS_ERR(lower_parent_dentry)) { + err = PTR_ERR(lower_parent_dentry); + goto out; + } + + mode = S_IALLUGO; + err = vfs_symlink(lower_parent_dentry->d_inode, lower_dentry, + symname, mode); + if (!err) { + err = PTR_ERR(unionfs_interpose(dentry, dir->i_sb, 0)); + if (!err) { + unionfs_copy_attr_times(dir); + fsstack_copy_inode_size(dir, + lower_parent_dentry->d_inode); + /* update no. of links on parent directory */ + dir->i_nlink = unionfs_get_nlinks(dir); + } + } + + unlock_dir(lower_parent_dentry); + +out: + dput(wh_dentry); + kfree(name); + + if (!err) { + unionfs_postcopyup_setmnt(dentry); + unionfs_check_inode(dir); + unionfs_check_dentry(dentry); + } + unionfs_unlock_dentry(dentry); + unionfs_unlock_parent(dentry, parent); + unionfs_read_unlock(dentry->d_sb); + return err; +} + +static int unionfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) +{ + int err = 0; + struct dentry *lower_dentry = NULL; + struct dentry *lower_parent_dentry = NULL; + struct dentry *parent; + int bindex = 0, bstart; + char *name = NULL; + int valid; + + unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); + parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); + unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); + + valid = __unionfs_d_revalidate(dentry, parent, false); + if (unlikely(!valid)) { + err = -ESTALE; /* same as what real_lookup does */ + goto out; + } + + bstart = dbstart(dentry); + + lower_dentry = unionfs_lower_dentry(dentry); + + /* check for a whiteout in new dentry branch, and delete it */ + err = check_unlink_whiteout(dentry, lower_dentry, bstart); + if (err > 0) /* whiteout found and removed successfully */ + err = 0; + if (err) { + /* exit if the error returned was NOT -EROFS */ + if (!IS_COPYUP_ERR(err)) + goto out; + bstart--; + } + + /* check if copyup's needed, and mkdir */ + for (bindex = bstart; bindex >= 0; bindex--) { + int i; + int bend = dbend(dentry); + + if (is_robranch_super(dentry->d_sb, bindex)) + continue; + + lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); + if (!lower_dentry) { + lower_dentry = create_parents(dir, dentry, + dentry->d_name.name, + bindex); + if (!lower_dentry || IS_ERR(lower_dentry)) { + printk(KERN_ERR "unionfs: lower dentry " + " NULL for bindex = %d\n", bindex); + continue; + } + } + + lower_parent_dentry = lock_parent(lower_dentry); + + if (IS_ERR(lower_parent_dentry)) { + err = PTR_ERR(lower_parent_dentry); + goto out; + } + + err = vfs_mkdir(lower_parent_dentry->d_inode, lower_dentry, + mode); + + unlock_dir(lower_parent_dentry); + + /* did the mkdir succeed? */ + if (err) + break; + + for (i = bindex + 1; i <= bend; i++) { + /* XXX: use path_put_lowers? */ + if (unionfs_lower_dentry_idx(dentry, i)) { + dput(unionfs_lower_dentry_idx(dentry, i)); + unionfs_set_lower_dentry_idx(dentry, i, NULL); + } + } + dbend(dentry) = bindex; + + /* + * Only INTERPOSE_LOOKUP can return a value other than 0 on + * err. + */ + err = PTR_ERR(unionfs_interpose(dentry, dir->i_sb, 0)); + if (!err) { + unionfs_copy_attr_times(dir); + fsstack_copy_inode_size(dir, + lower_parent_dentry->d_inode); + + /* update number of links on parent directory */ + dir->i_nlink = unionfs_get_nlinks(dir); + } + + err = make_dir_opaque(dentry, dbstart(dentry)); + if (err) { + printk(KERN_ERR "unionfs: mkdir: error creating " + ".wh.__dir_opaque: %d\n", err); + goto out; + } + + /* we are done! */ + break; + } + +out: + if (!dentry->d_inode) + d_drop(dentry); + + kfree(name); + + if (!err) { + unionfs_copy_attr_times(dentry->d_inode); + unionfs_postcopyup_setmnt(dentry); + } + unionfs_check_inode(dir); + unionfs_check_dentry(dentry); + unionfs_unlock_dentry(dentry); + unionfs_unlock_parent(dentry, parent); + unionfs_read_unlock(dentry->d_sb); + + return err; +} + +static int unionfs_mknod(struct inode *dir, struct dentry *dentry, int mode, + dev_t dev) +{ + int err = 0; + struct dentry *lower_dentry = NULL; + struct dentry *wh_dentry = NULL; + struct dentry *lower_parent_dentry = NULL; + struct dentry *parent; + char *name = NULL; + int valid = 0; + + unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); + parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); + unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); + + valid = __unionfs_d_revalidate(dentry, parent, false); + if (unlikely(!valid)) { + err = -ESTALE; + goto out; + } + + /* + * It's only a bug if this dentry was not negative and couldn't be + * revalidated (shouldn't happen). + */ + BUG_ON(!valid && dentry->d_inode); + + lower_dentry = find_writeable_branch(dir, dentry); + if (IS_ERR(lower_dentry)) { + err = PTR_ERR(lower_dentry); + goto out; + } + + lower_parent_dentry = lock_parent(lower_dentry); + if (IS_ERR(lower_parent_dentry)) { + err = PTR_ERR(lower_parent_dentry); + goto out; + } + + err = vfs_mknod(lower_parent_dentry->d_inode, lower_dentry, mode, dev); + if (!err) { + err = PTR_ERR(unionfs_interpose(dentry, dir->i_sb, 0)); + if (!err) { + unionfs_copy_attr_times(dir); + fsstack_copy_inode_size(dir, + lower_parent_dentry->d_inode); + /* update no. of links on parent directory */ + dir->i_nlink = unionfs_get_nlinks(dir); + } + } + + unlock_dir(lower_parent_dentry); + +out: + dput(wh_dentry); + kfree(name); + + if (!err) { + unionfs_postcopyup_setmnt(dentry); + unionfs_check_inode(dir); + unionfs_check_dentry(dentry); + } + unionfs_unlock_dentry(dentry); + unionfs_unlock_parent(dentry, parent); + unionfs_read_unlock(dentry->d_sb); + return err; +} + +/* requires sb, dentry, and parent to already be locked */ +static int __unionfs_readlink(struct dentry *dentry, char __user *buf, + int bufsiz) +{ + int err; + struct dentry *lower_dentry; + + lower_dentry = unionfs_lower_dentry(dentry); + + if (!lower_dentry->d_inode->i_op || + !lower_dentry->d_inode->i_op->readlink) { + err = -EINVAL; + goto out; + } + + err = lower_dentry->d_inode->i_op->readlink(lower_dentry, + buf, bufsiz); + if (err >= 0) + fsstack_copy_attr_atime(dentry->d_inode, + lower_dentry->d_inode); + +out: + return err; +} + +static int unionfs_readlink(struct dentry *dentry, char __user *buf, + int bufsiz) +{ + int err; + struct dentry *parent; + + unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); + parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); + unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); + + if (unlikely(!__unionfs_d_revalidate(dentry, parent, false))) { + err = -ESTALE; + goto out; + } + + err = __unionfs_readlink(dentry, buf, bufsiz); + +out: + unionfs_check_dentry(dentry); + unionfs_unlock_dentry(dentry); + unionfs_unlock_parent(dentry, parent); + unionfs_read_unlock(dentry->d_sb); + + return err; +} + +static void *unionfs_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + char *buf; + int len = PAGE_SIZE, err; + mm_segment_t old_fs; + struct dentry *parent; + + unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); + parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); + unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); + + /* This is freed by the put_link method assuming a successful call. */ + buf = kmalloc(len, GFP_KERNEL); + if (unlikely(!buf)) { + err = -ENOMEM; + goto out; + } + + /* read the symlink, and then we will follow it */ + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = __unionfs_readlink(dentry, buf, len); + set_fs(old_fs); + if (err < 0) { + kfree(buf); + buf = NULL; + goto out; + } + buf[err] = 0; + nd_set_link(nd, buf); + err = 0; + +out: + if (err >= 0) + unionfs_check_dentry(dentry); + + unionfs_unlock_dentry(dentry); + unionfs_unlock_parent(dentry, parent); + unionfs_read_unlock(dentry->d_sb); + + return ERR_PTR(err); +} + +/* this @nd *IS* still used */ +static void unionfs_put_link(struct dentry *dentry, struct nameidata *nd, + void *cookie) +{ + struct dentry *parent; + + unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); + parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); + unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); + + if (unlikely(!__unionfs_d_revalidate(dentry, parent, false))) + printk(KERN_ERR + "unionfs: put_link failed to revalidate dentry\n"); + + unionfs_check_dentry(dentry); + kfree(nd_get_link(nd)); + unionfs_unlock_dentry(dentry); + unionfs_unlock_parent(dentry, parent); + unionfs_read_unlock(dentry->d_sb); +} + +/* + * This is a variant of fs/namei.c:permission() or inode_permission() which + * skips over EROFS tests (because we perform copyup on EROFS). + */ +static int __inode_permission(struct inode *inode, int mask, + struct nameidata *nd) +{ + int retval; + + /* nobody gets write access to an immutable file */ + if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode)) + return -EACCES; + + /* Ordinary permission routines do not understand MAY_APPEND. */ + if (inode->i_op && inode->i_op->permission) { + retval = inode->i_op->permission(inode, mask, nd); + if (!retval) { + /* + * Exec permission on a regular file is denied if none + * of the execute bits are set. + * + * This check should be done by the ->permission() + * method. + */ + if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode) && + !(inode->i_mode & S_IXUGO)) + return -EACCES; + } + } else { + retval = generic_permission(inode, mask, NULL); + } + if (retval) + return retval; + + return security_inode_permission(inode, + mask & (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND), + nd); +} + +/* + * Don't grab the superblock read-lock in unionfs_permission, which prevents + * a deadlock with the branch-management "add branch" code (which grabbed + * the write lock). It is safe to not grab the read lock here, because even + * with branch management taking place, there is no chance that + * unionfs_permission, or anything it calls, will use stale branch + * information. + */ +static int unionfs_permission(struct inode *inode, int mask, + struct nameidata *nd) +{ + struct inode *lower_inode = NULL; + int err = 0; + int bindex, bstart, bend; + const int is_file = !S_ISDIR(inode->i_mode); + const int write_mask = (mask & MAY_WRITE) && !(mask & MAY_READ); + struct inode *inode_grabbed = igrab(inode); + + if (nd) + unionfs_lock_dentry(nd->dentry, UNIONFS_DMUTEX_CHILD); + + if (!UNIONFS_I(inode)->lower_inodes) { + if (is_file) /* dirs can be unlinked but chdir'ed to */ + err = -ESTALE; /* force revalidate */ + goto out; + } + bstart = ibstart(inode); + bend = ibend(inode); + if (unlikely(bstart < 0 || bend < 0)) { + /* + * With branch-management, we can get a stale inode here. + * If so, we return ESTALE back to link_path_walk, which + * would discard the dcache entry and re-lookup the + * dentry+inode. This should be equivalent to issuing + * __unionfs_d_revalidate_chain on nd.dentry here. + */ + if (is_file) /* dirs can be unlinked but chdir'ed to */ + err = -ESTALE; /* force revalidate */ + goto out; + } + + for (bindex = bstart; bindex <= bend; bindex++) { + lower_inode = unionfs_lower_inode_idx(inode, bindex); + if (!lower_inode) + continue; + + /* + * check the condition for D-F-D underlying files/directories, + * we don't have to check for files, if we are checking for + * directories. + */ + if (!is_file && !S_ISDIR(lower_inode->i_mode)) + continue; + + /* + * We check basic permissions, but we ignore any conditions + * such as readonly file systems or branches marked as + * readonly, because those conditions should lead to a + * copyup taking place later on. However, if user never had + * access to the file, then no copyup could ever take place. + */ + err = __inode_permission(lower_inode, mask, nd); + if (err && err != -EACCES && err != EPERM && bindex > 0) { + umode_t mode = lower_inode->i_mode; + if ((is_robranch_super(inode->i_sb, bindex) || + IS_RDONLY(lower_inode)) && + (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) + err = 0; + if (IS_COPYUP_ERR(err)) + err = 0; + } + + /* + * The permissions are an intersection of the overall directory + * permissions, so we fail if one fails. + */ + if (err) + goto out; + + /* only the leftmost file matters. */ + if (is_file || write_mask) { + if (is_file && write_mask) { + err = get_write_access(lower_inode); + if (!err) + put_write_access(lower_inode); + } + break; + } + } + /* sync times which may have changed (asynchronously) below */ + unionfs_copy_attr_times(inode); + +out: + unionfs_check_inode(inode); + if (nd) + unionfs_unlock_dentry(nd->dentry); + iput(inode_grabbed); + return err; +} + +static int unionfs_setattr(struct dentry *dentry, struct iattr *ia) +{ + int err = 0; + struct dentry *lower_dentry; + struct dentry *parent; + struct inode *inode; + struct inode *lower_inode; + int bstart, bend, bindex; + loff_t size; + + unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); + parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); + unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); + + if (unlikely(!__unionfs_d_revalidate(dentry, parent, false))) { + err = -ESTALE; + goto out; + } + + bstart = dbstart(dentry); + bend = dbend(dentry); + inode = dentry->d_inode; + + lower_dentry = unionfs_lower_dentry(dentry); + if (!lower_dentry) { /* should never happen after above revalidate */ + err = -EINVAL; + goto out; + } + lower_inode = unionfs_lower_inode(inode); + + /* check if user has permission to change lower inode */ + err = inode_change_ok(lower_inode, ia); + if (err) + goto out; + + /* copyup if the file is on a read only branch */ + if (is_robranch_super(dentry->d_sb, bstart) + || IS_RDONLY(lower_inode)) { + /* check if we have a branch to copy up to */ + if (bstart <= 0) { + err = -EACCES; + goto out; + } + + if (ia->ia_valid & ATTR_SIZE) + size = ia->ia_size; + else + size = i_size_read(inode); + /* copyup to next available branch */ + for (bindex = bstart - 1; bindex >= 0; bindex--) { + err = copyup_dentry(parent->d_inode, + dentry, bstart, bindex, + dentry->d_name.name, + dentry->d_name.len, + NULL, size); + if (!err) + break; + } + if (err) + goto out; + /* get updated lower_dentry/inode after copyup */ + lower_dentry = unionfs_lower_dentry(dentry); + lower_inode = unionfs_lower_inode(inode); + } + + /* + * If shrinking, first truncate upper level to cancel writing dirty + * pages beyond the new eof; and also if its' maxbytes is more + * limiting (fail with -EFBIG before making any change to the lower + * level). There is no need to vmtruncate the upper level + * afterwards in the other cases: we fsstack_copy_inode_size from + * the lower level. + */ + if (ia->ia_valid & ATTR_SIZE) { + size = i_size_read(inode); + if (ia->ia_size < size || (ia->ia_size > size && + inode->i_sb->s_maxbytes < lower_inode->i_sb->s_maxbytes)) { + err = vmtruncate(inode, ia->ia_size); + if (err) + goto out; + } + } + + /* notify the (possibly copied-up) lower inode */ + mutex_lock(&lower_inode->i_mutex); + err = notify_change(lower_dentry, ia); + mutex_unlock(&lower_inode->i_mutex); + if (err) + goto out; + + /* get attributes from the first lower inode */ + unionfs_copy_attr_all(inode, lower_inode); + /* + * unionfs_copy_attr_all will copy the lower times to our inode if + * the lower ones are newer (useful for cache coherency). However, + * ->setattr is the only place in which we may have to copy the + * lower inode times absolutely, to support utimes(2). + */ + if (ia->ia_valid & ATTR_MTIME_SET) + inode->i_mtime = lower_inode->i_mtime; + if (ia->ia_valid & ATTR_CTIME) + inode->i_ctime = lower_inode->i_ctime; + if (ia->ia_valid & ATTR_ATIME_SET) + inode->i_atime = lower_inode->i_atime; + fsstack_copy_inode_size(inode, lower_inode); + +out: + if (!err) + unionfs_check_dentry(dentry); + unionfs_unlock_dentry(dentry); + unionfs_unlock_parent(dentry, parent); + unionfs_read_unlock(dentry->d_sb); + + return err; +} + +struct inode_operations unionfs_symlink_iops = { + .readlink = unionfs_readlink, + .permission = unionfs_permission, + .follow_link = unionfs_follow_link, + .setattr = unionfs_setattr, + .put_link = unionfs_put_link, +}; + +struct inode_operations unionfs_dir_iops = { + .create = unionfs_create, + .lookup = unionfs_lookup, + .link = unionfs_link, + .unlink = unionfs_unlink, + .symlink = unionfs_symlink, + .mkdir = unionfs_mkdir, + .rmdir = unionfs_rmdir, + .mknod = unionfs_mknod, + .rename = unionfs_rename, + .permission = unionfs_permission, + .setattr = unionfs_setattr, +#ifdef CONFIG_UNION_FS_XATTR + .setxattr = unionfs_setxattr, + .getxattr = unionfs_getxattr, + .removexattr = unionfs_removexattr, + .listxattr = unionfs_listxattr, +#endif /* CONFIG_UNION_FS_XATTR */ +}; + +struct inode_operations unionfs_main_iops = { + .permission = unionfs_permission, + .setattr = unionfs_setattr, +#ifdef CONFIG_UNION_FS_XATTR + .setxattr = unionfs_setxattr, + .getxattr = unionfs_getxattr, + .removexattr = unionfs_removexattr, + .listxattr = unionfs_listxattr, +#endif /* CONFIG_UNION_FS_XATTR */ +}; diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/lookup.c kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/lookup.c --- kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/lookup.c 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/lookup.c 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,500 @@ +/* + * Copyright (c) 2003-2009 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2007 Josef 'Jeff' Sipek + * Copyright (c) 2005-2006 Junjiro Okajima + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2004-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2009 Stony Brook University + * Copyright (c) 2003-2009 The Research Foundation of SUNY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "union.h" + +/* + * Lookup one path component @name relative to a path pair. + * Behaves nearly the same as lookup_one_len (i.e., return negative dentry + * on ENOENT), but uses the @mnt passed, so it can cross bind mounts and + * other lower mounts properly. If @new_mnt is non-null, will fill in the + * new mnt there. Caller is responsible to dput/mntput/path_put returned + * @dentry and @new_mnt. + */ +struct dentry *__lookup_one(struct dentry *base, struct vfsmount *mnt, + const char *name, struct vfsmount **new_mnt) +{ + struct dentry *dentry = NULL; + struct nameidata lower_nd; + int err; + + /* we use flags=0 to get basic lookup */ + err = vfs_path_lookup(base, mnt, name, 0, &lower_nd); + + switch (err) { + case 0: /* no error */ + dentry = lower_nd.dentry; + if (new_mnt) + *new_mnt = lower_nd.mnt; /* rc already inc'ed */ + break; + case -ENOENT: + /* + * We don't consider ENOENT an error, and we want to return + * a negative dentry (ala lookup_one_len). As we know + * there was no inode for this name before (-ENOENT), then + * it's safe to call lookup_one_len (which doesn't take a + * vfsmount). + */ + dentry = lookup_one_len(name, base, strlen(name)); + if (new_mnt) + *new_mnt = mntget(lower_nd.mnt); + break; + default: /* all other real errors */ + dentry = ERR_PTR(err); + break; + } + + return dentry; +} + +/* + * This is a utility function that fills in a unionfs dentry. + * Caller must lock this dentry with unionfs_lock_dentry. + * + * Returns: 0 (ok), or -ERRNO if an error occurred. + * XXX: get rid of _partial_lookup and make callers call _lookup_full directly + */ +int unionfs_partial_lookup(struct dentry *dentry, struct dentry *parent) +{ + struct dentry *tmp; + int err = -ENOSYS; + + tmp = unionfs_lookup_full(dentry, parent, INTERPOSE_PARTIAL); + + if (!tmp) { + err = 0; + goto out; + } + if (IS_ERR(tmp)) { + err = PTR_ERR(tmp); + goto out; + } + /* XXX: need to change the interface */ + BUG_ON(tmp != dentry); +out: + return err; +} + +/* The dentry cache is just so we have properly sized dentries. */ +static struct kmem_cache *unionfs_dentry_cachep; +int unionfs_init_dentry_cache(void) +{ + unionfs_dentry_cachep = + kmem_cache_create("unionfs_dentry", + sizeof(struct unionfs_dentry_info), + 0, SLAB_RECLAIM_ACCOUNT, NULL, NULL); + + return (unionfs_dentry_cachep ? 0 : -ENOMEM); +} + +void unionfs_destroy_dentry_cache(void) +{ + if (unionfs_dentry_cachep) + kmem_cache_destroy(unionfs_dentry_cachep); +} + +void free_dentry_private_data(struct dentry *dentry) +{ + if (!dentry || !dentry->d_fsdata) + return; + kfree(UNIONFS_D(dentry)->lower_paths); + UNIONFS_D(dentry)->lower_paths = NULL; + kmem_cache_free(unionfs_dentry_cachep, dentry->d_fsdata); + dentry->d_fsdata = NULL; +} + +static inline int __realloc_dentry_private_data(struct dentry *dentry) +{ + struct unionfs_dentry_info *info = UNIONFS_D(dentry); + void *p; + int size; + + BUG_ON(!info); + + size = sizeof(struct path) * sbmax(dentry->d_sb); + p = krealloc(info->lower_paths, size, GFP_ATOMIC); + if (unlikely(!p)) + return -ENOMEM; + + info->lower_paths = p; + + info->bstart = -1; + info->bend = -1; + info->bopaque = -1; + info->bcount = sbmax(dentry->d_sb); + atomic_set(&info->generation, + atomic_read(&UNIONFS_SB(dentry->d_sb)->generation)); + + memset(info->lower_paths, 0, size); + + return 0; +} + +/* UNIONFS_D(dentry)->lock must be locked */ +int realloc_dentry_private_data(struct dentry *dentry) +{ + if (!__realloc_dentry_private_data(dentry)) + return 0; + + kfree(UNIONFS_D(dentry)->lower_paths); + free_dentry_private_data(dentry); + return -ENOMEM; +} + +/* allocate new dentry private data */ +int new_dentry_private_data(struct dentry *dentry, int subclass) +{ + struct unionfs_dentry_info *info = UNIONFS_D(dentry); + + BUG_ON(info); + + info = kmem_cache_alloc(unionfs_dentry_cachep, GFP_ATOMIC); + if (unlikely(!info)) + return -ENOMEM; + + mutex_init(&info->lock); + mutex_lock_nested(&info->lock, subclass); + + info->lower_paths = NULL; + + dentry->d_fsdata = info; + + if (!__realloc_dentry_private_data(dentry)) + return 0; + + mutex_unlock(&info->lock); + free_dentry_private_data(dentry); + return -ENOMEM; +} + +/* + * scan through the lower dentry objects, and set bstart to reflect the + * starting branch + */ +void update_bstart(struct dentry *dentry) +{ + int bindex; + int bstart = dbstart(dentry); + int bend = dbend(dentry); + struct dentry *lower_dentry; + + for (bindex = bstart; bindex <= bend; bindex++) { + lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); + if (!lower_dentry) + continue; + if (lower_dentry->d_inode) { + dbstart(dentry) = bindex; + break; + } + dput(lower_dentry); + unionfs_set_lower_dentry_idx(dentry, bindex, NULL); + } +} + +/* + * Main (and complex) driver function for Unionfs's lookup + * + * Returns: NULL (ok), ERR_PTR if an error occurred, or a non-null non-error + * PTR if d_splice returned a different dentry. + * + * If lookupmode is INTERPOSE_PARTIAL/REVAL/REVAL_NEG, the passed dentry's + * inode info must be locked. If lookupmode is INTERPOSE_LOOKUP (i.e., a + * newly looked-up dentry), then unionfs_lookup_backend will return a locked + * dentry's info, which the caller must unlock. + */ +struct dentry *unionfs_lookup_full(struct dentry *dentry, + struct dentry *parent, int lookupmode) +{ + int err = 0; + struct dentry *lower_dentry = NULL; + struct vfsmount *lower_mnt; + struct vfsmount *lower_dir_mnt; + struct dentry *wh_lower_dentry = NULL; + struct dentry *lower_dir_dentry = NULL; + struct dentry *d_interposed = NULL; + int bindex, bstart, bend, bopaque; + int opaque, num_positive = 0; + const char *name; + int namelen; + int pos_start, pos_end; + + /* + * We should already have a lock on this dentry in the case of a + * partial lookup, or a revalidation. Otherwise it is returned from + * new_dentry_private_data already locked. + */ + verify_locked(dentry); + verify_locked(parent); + + /* must initialize dentry operations */ + dentry->d_op = &unionfs_dops; + + /* We never partial lookup the root directory. */ + if (IS_ROOT(dentry)) + goto out; + + name = dentry->d_name.name; + namelen = dentry->d_name.len; + + /* No dentries should get created for possible whiteout names. */ + if (!is_validname(name)) { + err = -EPERM; + goto out_free; + } + + /* Now start the actual lookup procedure. */ + bstart = dbstart(parent); + bend = dbend(parent); + bopaque = dbopaque(parent); + BUG_ON(bstart < 0); + + /* adjust bend to bopaque if needed */ + if ((bopaque >= 0) && (bopaque < bend)) + bend = bopaque; + + /* lookup all possible dentries */ + for (bindex = bstart; bindex <= bend; bindex++) { + + lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); + lower_mnt = unionfs_lower_mnt_idx(dentry, bindex); + + /* skip if we already have a positive lower dentry */ + if (lower_dentry) { + if (dbstart(dentry) < 0) + dbstart(dentry) = bindex; + if (bindex > dbend(dentry)) + dbend(dentry) = bindex; + if (lower_dentry->d_inode) + num_positive++; + continue; + } + + lower_dir_dentry = + unionfs_lower_dentry_idx(parent, bindex); + /* if the lower dentry's parent does not exist, skip this */ + if (!lower_dir_dentry || !lower_dir_dentry->d_inode) + continue; + + /* also skip it if the parent isn't a directory. */ + if (!S_ISDIR(lower_dir_dentry->d_inode->i_mode)) + continue; /* XXX: should be BUG_ON */ + + /* check for whiteouts: stop lookup if found */ + wh_lower_dentry = lookup_whiteout(name, lower_dir_dentry); + if (IS_ERR(wh_lower_dentry)) { + err = PTR_ERR(wh_lower_dentry); + goto out_free; + } + if (wh_lower_dentry->d_inode) { + dbend(dentry) = dbopaque(dentry) = bindex; + if (dbstart(dentry) < 0) + dbstart(dentry) = bindex; + dput(wh_lower_dentry); + break; + } + dput(wh_lower_dentry); + + /* Now do regular lookup; lookup @name */ + lower_dir_mnt = unionfs_lower_mnt_idx(parent, bindex); + lower_mnt = NULL; /* XXX: needed? */ + + lower_dentry = __lookup_one(lower_dir_dentry, lower_dir_mnt, + name, &lower_mnt); + + if (IS_ERR(lower_dentry)) { + err = PTR_ERR(lower_dentry); + goto out_free; + } + unionfs_set_lower_dentry_idx(dentry, bindex, lower_dentry); + if (!lower_mnt) + lower_mnt = unionfs_mntget(dentry->d_sb->s_root, + bindex); + unionfs_set_lower_mnt_idx(dentry, bindex, lower_mnt); + + /* adjust dbstart/end */ + if (dbstart(dentry) < 0) + dbstart(dentry) = bindex; + if (bindex > dbend(dentry)) + dbend(dentry) = bindex; + /* + * We always store the lower dentries above, and update + * dbstart/dbend, even if the whole unionfs dentry is + * negative (i.e., no lower inodes). + */ + if (!lower_dentry->d_inode) + continue; + num_positive++; + + /* + * check if we just found an opaque directory, if so, stop + * lookups here. + */ + if (!S_ISDIR(lower_dentry->d_inode->i_mode)) + continue; + opaque = is_opaque_dir(dentry, bindex); + if (opaque < 0) { + err = opaque; + goto out_free; + } else if (opaque) { + dbend(dentry) = dbopaque(dentry) = bindex; + break; + } + dbend(dentry) = bindex; + + /* update parent directory's atime with the bindex */ + fsstack_copy_attr_atime(parent->d_inode, + lower_dir_dentry->d_inode); + } + + /* sanity checks, then decide if to process a negative dentry */ + BUG_ON(dbstart(dentry) < 0 && dbend(dentry) >= 0); + BUG_ON(dbstart(dentry) >= 0 && dbend(dentry) < 0); + + if (num_positive > 0) + goto out_positive; + + /*** handle NEGATIVE dentries ***/ + + /* + * If negative, keep only first lower negative dentry, to save on + * memory. + */ + if (dbstart(dentry) < dbend(dentry)) { + path_put_lowers(dentry, dbstart(dentry) + 1, + dbend(dentry), false); + dbend(dentry) = dbstart(dentry); + } + if (lookupmode == INTERPOSE_PARTIAL) + goto out; + if (lookupmode == INTERPOSE_LOOKUP) { + /* + * If all we found was a whiteout in the first available + * branch, then create a negative dentry for a possibly new + * file to be created. + */ + if (dbopaque(dentry) < 0) + goto out; + /* XXX: need to get mnt here */ + bindex = dbstart(dentry); + if (unionfs_lower_dentry_idx(dentry, bindex)) + goto out; + lower_dir_dentry = + unionfs_lower_dentry_idx(parent, bindex); + if (!lower_dir_dentry || !lower_dir_dentry->d_inode) + goto out; + if (!S_ISDIR(lower_dir_dentry->d_inode->i_mode)) + goto out; /* XXX: should be BUG_ON */ + /* XXX: do we need to cross bind mounts here? */ + lower_dentry = lookup_one_len(name, lower_dir_dentry, namelen); + if (IS_ERR(lower_dentry)) { + err = PTR_ERR(lower_dentry); + goto out; + } + /* XXX: need to mntget/mntput as needed too! */ + unionfs_set_lower_dentry_idx(dentry, bindex, lower_dentry); + /* XXX: wrong mnt for crossing bind mounts! */ + lower_mnt = unionfs_mntget(dentry->d_sb->s_root, bindex); + unionfs_set_lower_mnt_idx(dentry, bindex, lower_mnt); + + goto out; + } + + /* if we're revalidating a positive dentry, don't make it negative */ + if (lookupmode != INTERPOSE_REVAL) + d_add(dentry, NULL); + + goto out; + +out_positive: + /*** handle POSITIVE dentries ***/ + + /* + * This unionfs dentry is positive (at least one lower inode + * exists), so scan entire dentry from beginning to end, and remove + * any negative lower dentries, if any. Then, update dbstart/dbend + * to reflect the start/end of positive dentries. + */ + pos_start = pos_end = -1; + for (bindex = bstart; bindex <= bend; bindex++) { + lower_dentry = unionfs_lower_dentry_idx(dentry, + bindex); + if (lower_dentry && lower_dentry->d_inode) { + if (pos_start < 0) + pos_start = bindex; + if (bindex > pos_end) + pos_end = bindex; + continue; + } + path_put_lowers(dentry, bindex, bindex, false); + } + if (pos_start >= 0) + dbstart(dentry) = pos_start; + if (pos_end >= 0) + dbend(dentry) = pos_end; + + /* Partial lookups need to re-interpose, or throw away older negs. */ + if (lookupmode == INTERPOSE_PARTIAL) { + if (dentry->d_inode) { + unionfs_reinterpose(dentry); + goto out; + } + + /* + * This dentry was positive, so it is as if we had a + * negative revalidation. + */ + lookupmode = INTERPOSE_REVAL_NEG; + update_bstart(dentry); + } + + /* + * Interpose can return a dentry if d_splice returned a different + * dentry. + */ + d_interposed = unionfs_interpose(dentry, dentry->d_sb, lookupmode); + if (IS_ERR(d_interposed)) + err = PTR_ERR(d_interposed); + else if (d_interposed) + dentry = d_interposed; + + if (!err) + goto out; + d_drop(dentry); + +out_free: + /* should dput/mntput all the underlying dentries on error condition */ + if (dbstart(dentry) >= 0) + path_put_lowers_all(dentry, false); + /* free lower_paths unconditionally */ + kfree(UNIONFS_D(dentry)->lower_paths); + UNIONFS_D(dentry)->lower_paths = NULL; + +out: + if (dentry && UNIONFS_D(dentry)) { + BUG_ON(dbstart(dentry) < 0 && dbend(dentry) >= 0); + BUG_ON(dbstart(dentry) >= 0 && dbend(dentry) < 0); + } + if (d_interposed && UNIONFS_D(d_interposed)) { + BUG_ON(dbstart(d_interposed) < 0 && dbend(d_interposed) >= 0); + BUG_ON(dbstart(d_interposed) >= 0 && dbend(d_interposed) < 0); + } + + if (!err && d_interposed) + return d_interposed; + return ERR_PTR(err); +} diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/main.c kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/main.c --- kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/main.c 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/main.c 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,778 @@ +/* + * Copyright (c) 2003-2009 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2007 Josef 'Jeff' Sipek + * Copyright (c) 2005-2006 Junjiro Okajima + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2004-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2009 Stony Brook University + * Copyright (c) 2003-2009 The Research Foundation of SUNY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "union.h" +#include +#include + +static void unionfs_fill_inode(struct dentry *dentry, + struct inode *inode) +{ + struct inode *lower_inode; + struct dentry *lower_dentry; + int bindex, bstart, bend; + + bstart = dbstart(dentry); + bend = dbend(dentry); + + for (bindex = bstart; bindex <= bend; bindex++) { + lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); + if (!lower_dentry) { + unionfs_set_lower_inode_idx(inode, bindex, NULL); + continue; + } + + /* Initialize the lower inode to the new lower inode. */ + if (!lower_dentry->d_inode) + continue; + + unionfs_set_lower_inode_idx(inode, bindex, + igrab(lower_dentry->d_inode)); + } + + ibstart(inode) = dbstart(dentry); + ibend(inode) = dbend(dentry); + + /* Use attributes from the first branch. */ + lower_inode = unionfs_lower_inode(inode); + + /* Use different set of inode ops for symlinks & directories */ + if (S_ISLNK(lower_inode->i_mode)) + inode->i_op = &unionfs_symlink_iops; + else if (S_ISDIR(lower_inode->i_mode)) + inode->i_op = &unionfs_dir_iops; + + /* Use different set of file ops for directories */ + if (S_ISDIR(lower_inode->i_mode)) + inode->i_fop = &unionfs_dir_fops; + + /* properly initialize special inodes */ + 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); + + /* all well, copy inode attributes */ + unionfs_copy_attr_all(inode, lower_inode); + fsstack_copy_inode_size(inode, lower_inode); +} + +/* + * Connect a unionfs inode dentry/inode with several lower ones. This is + * the classic stackable file system "vnode interposition" action. + * + * @sb: unionfs's super_block + */ +struct dentry *unionfs_interpose(struct dentry *dentry, struct super_block *sb, + int flag) +{ + int err = 0; + struct inode *inode; + int need_fill_inode = 1; + struct dentry *spliced = NULL; + + verify_locked(dentry); + + /* + * We allocate our new inode below, by calling iget. + * iget will call our read_inode which will initialize some + * of the new inode's fields + */ + + /* + * On revalidate we've already got our own inode and just need + * to fix it up. + */ + if (flag == INTERPOSE_REVAL) { + inode = dentry->d_inode; + UNIONFS_I(inode)->bstart = -1; + UNIONFS_I(inode)->bend = -1; + atomic_set(&UNIONFS_I(inode)->generation, + atomic_read(&UNIONFS_SB(sb)->generation)); + + UNIONFS_I(inode)->lower_inodes = + kcalloc(sbmax(sb), sizeof(struct inode *), GFP_KERNEL); + if (unlikely(!UNIONFS_I(inode)->lower_inodes)) { + err = -ENOMEM; + goto out; + } + } else { + /* get unique inode number for unionfs */ + inode = iget(sb, iunique(sb, UNIONFS_ROOT_INO)); + if (!inode) { + err = -EACCES; + goto out; + } + if (atomic_read(&inode->i_count) > 1) + goto skip; + } + + need_fill_inode = 0; + unionfs_fill_inode(dentry, inode); + +skip: + /* only (our) lookup wants to do a d_add */ + switch (flag) { + case INTERPOSE_DEFAULT: + /* for operations which create new inodes */ + d_add(dentry, inode); + break; + case INTERPOSE_REVAL_NEG: + d_instantiate(dentry, inode); + break; + case INTERPOSE_LOOKUP: + spliced = d_splice_alias(inode, dentry); + if (spliced && spliced != dentry) { + /* + * d_splice can return a dentry if it was + * disconnected and had to be moved. We must ensure + * that the private data of the new dentry is + * correct and that the inode info was filled + * properly. Finally we must return this new + * dentry. + */ + spliced->d_op = &unionfs_dops; + spliced->d_fsdata = dentry->d_fsdata; + dentry->d_fsdata = NULL; + dentry = spliced; + if (need_fill_inode) { + need_fill_inode = 0; + unionfs_fill_inode(dentry, inode); + } + goto out_spliced; + } else if (!spliced) { + if (need_fill_inode) { + need_fill_inode = 0; + unionfs_fill_inode(dentry, inode); + goto out_spliced; + } + } + break; + case INTERPOSE_REVAL: + /* Do nothing. */ + break; + default: + printk(KERN_CRIT "unionfs: invalid interpose flag passed!\n"); + BUG(); + } + goto out; + +out_spliced: + if (!err) + return spliced; +out: + return ERR_PTR(err); +} + +/* like interpose above, but for an already existing dentry */ +void unionfs_reinterpose(struct dentry *dentry) +{ + struct dentry *lower_dentry; + struct inode *inode; + int bindex, bstart, bend; + + verify_locked(dentry); + + /* This is pre-allocated inode */ + inode = dentry->d_inode; + + bstart = dbstart(dentry); + bend = dbend(dentry); + for (bindex = bstart; bindex <= bend; bindex++) { + lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); + if (!lower_dentry) + continue; + + if (!lower_dentry->d_inode) + continue; + if (unionfs_lower_inode_idx(inode, bindex)) + continue; + unionfs_set_lower_inode_idx(inode, bindex, + igrab(lower_dentry->d_inode)); + } + ibstart(inode) = dbstart(dentry); + ibend(inode) = dbend(dentry); +} + +/* + * make sure the branch we just looked up (nd) makes sense: + * + * 1) we're not trying to stack unionfs on top of unionfs + * 2) it exists + * 3) is a directory + */ +int check_branch(struct nameidata *nd) +{ + /* XXX: remove in ODF code -- stacking unions allowed there */ + if (!strcmp(nd->dentry->d_sb->s_type->name, UNIONFS_NAME)) + return -EINVAL; + if (!nd->dentry->d_inode) + return -ENOENT; + if (!S_ISDIR(nd->dentry->d_inode->i_mode)) + return -ENOTDIR; + return 0; +} + +/* checks if two lower_dentries have overlapping branches */ +static int is_branch_overlap(struct dentry *dent1, struct dentry *dent2) +{ + struct dentry *dent = NULL; + + dent = dent1; + while ((dent != dent2) && (dent->d_parent != dent)) + dent = dent->d_parent; + + if (dent == dent2) + return 1; + + dent = dent2; + while ((dent != dent1) && (dent->d_parent != dent)) + dent = dent->d_parent; + + return (dent == dent1); +} + +/* + * Parse "ro" or "rw" options, but default to "rw" if no mode options was + * specified. Fill the mode bits in @perms. If encounter an unknown + * string, return -EINVAL. Otherwise return 0. + */ +int parse_branch_mode(const char *name, int *perms) +{ + if (!name || !strcmp(name, "rw")) { + *perms = MAY_READ | MAY_WRITE; + return 0; + } + if (!strcmp(name, "ro")) { + *perms = MAY_READ; + return 0; + } + return -EINVAL; +} + +/* + * parse the dirs= mount argument + * + * We don't need to lock the superblock private data's rwsem, as we get + * called only by unionfs_read_super - it is still a long time before anyone + * can even get a reference to us. + */ +static int parse_dirs_option(struct super_block *sb, struct unionfs_dentry_info + *lower_root_info, char *options) +{ + struct nameidata nd; + char *name; + int err = 0; + int branches = 1; + int bindex = 0; + int i = 0; + int j = 0; + struct dentry *dent1; + struct dentry *dent2; + + if (options[0] == '\0') { + printk(KERN_ERR "unionfs: no branches specified\n"); + err = -EINVAL; + goto out; + } + + /* + * Each colon means we have a separator, this is really just a rough + * guess, since strsep will handle empty fields for us. + */ + for (i = 0; options[i]; i++) + if (options[i] == ':') + branches++; + + /* allocate space for underlying pointers to lower dentry */ + UNIONFS_SB(sb)->data = + kcalloc(branches, sizeof(struct unionfs_data), GFP_KERNEL); + if (unlikely(!UNIONFS_SB(sb)->data)) { + err = -ENOMEM; + goto out; + } + + lower_root_info->lower_paths = + kcalloc(branches, sizeof(struct path), GFP_KERNEL); + if (unlikely(!lower_root_info->lower_paths)) { + err = -ENOMEM; + goto out; + } + + /* now parsing a string such as "b1:b2=rw:b3=ro:b4" */ + branches = 0; + while ((name = strsep(&options, ":")) != NULL) { + int perms; + char *mode = strchr(name, '='); + + if (!name) + continue; + if (!*name) { /* bad use of ':' (extra colons) */ + err = -EINVAL; + goto out; + } + + branches++; + + /* strip off '=' if any */ + if (mode) + *mode++ = '\0'; + + err = parse_branch_mode(mode, &perms); + if (err) { + printk(KERN_ERR "unionfs: invalid mode \"%s\" for " + "branch %d\n", mode, bindex); + goto out; + } + /* ensure that leftmost branch is writeable */ + if (!bindex && !(perms & MAY_WRITE)) { + printk(KERN_ERR "unionfs: leftmost branch cannot be " + "read-only (use \"-o ro\" to create a " + "read-only union)\n"); + err = -EINVAL; + goto out; + } + + err = path_lookup(name, LOOKUP_FOLLOW, &nd); + if (err) { + printk(KERN_ERR "unionfs: error accessing " + "lower directory '%s' (error %d)\n", + name, err); + goto out; + } + + err = check_branch(&nd); + if (err) { + printk(KERN_ERR "unionfs: lower directory " + "'%s' is not a valid branch\n", name); + path_release(&nd); + goto out; + } + + lower_root_info->lower_paths[bindex].dentry = nd.dentry; + lower_root_info->lower_paths[bindex].mnt = nd.mnt; + + set_branchperms(sb, bindex, perms); + set_branch_count(sb, bindex, 0); + new_branch_id(sb, bindex); + + if (lower_root_info->bstart < 0) + lower_root_info->bstart = bindex; + lower_root_info->bend = bindex; + bindex++; + } + + if (branches == 0) { + printk(KERN_ERR "unionfs: no branches specified\n"); + err = -EINVAL; + goto out; + } + + BUG_ON(branches != (lower_root_info->bend + 1)); + + /* + * Ensure that no overlaps exist in the branches. + * + * This test is required because the Linux kernel has no support + * currently for ensuring coherency between stackable layers and + * branches. If we were to allow overlapping branches, it would be + * possible, for example, to delete a file via one branch, which + * would not be reflected in another branch. Such incoherency could + * lead to inconsistencies and even kernel oopses. Rather than + * implement hacks to work around some of these cache-coherency + * problems, we prevent branch overlapping, for now. A complete + * solution will involve proper kernel/VFS support for cache + * coherency, at which time we could safely remove this + * branch-overlapping test. + */ + for (i = 0; i < branches; i++) { + dent1 = lower_root_info->lower_paths[i].dentry; + for (j = i + 1; j < branches; j++) { + dent2 = lower_root_info->lower_paths[j].dentry; + if (is_branch_overlap(dent1, dent2)) { + printk(KERN_ERR "unionfs: branches %d and " + "%d overlap\n", i, j); + err = -EINVAL; + goto out; + } + } + } + +out: + if (err) { + for (i = 0; i < branches; i++) + if (lower_root_info->lower_paths[i].dentry) { + dput(lower_root_info->lower_paths[i].dentry); + /* initialize: can't use unionfs_mntput here */ + mntput(lower_root_info->lower_paths[i].mnt); + } + + kfree(lower_root_info->lower_paths); + kfree(UNIONFS_SB(sb)->data); + + /* + * MUST clear the pointers to prevent potential double free if + * the caller dies later on + */ + lower_root_info->lower_paths = NULL; + UNIONFS_SB(sb)->data = NULL; + } + return err; +} + +/* + * Parse mount options. See the manual page for usage instructions. + * + * Returns the dentry object of the lower-level (lower) directory; + * We want to mount our stackable file system on top of that lower directory. + */ +static struct unionfs_dentry_info *unionfs_parse_options( + struct super_block *sb, + char *options) +{ + struct unionfs_dentry_info *lower_root_info; + char *optname; + int err = 0; + int bindex; + int dirsfound = 0; + + /* allocate private data area */ + err = -ENOMEM; + lower_root_info = + kzalloc(sizeof(struct unionfs_dentry_info), GFP_KERNEL); + if (unlikely(!lower_root_info)) + goto out_error; + lower_root_info->bstart = -1; + lower_root_info->bend = -1; + lower_root_info->bopaque = -1; + + while ((optname = strsep(&options, ",")) != NULL) { + char *optarg; + + if (!optname || !*optname) + continue; + + optarg = strchr(optname, '='); + if (optarg) + *optarg++ = '\0'; + + /* + * All of our options take an argument now. Insert ones that + * don't, above this check. + */ + if (!optarg) { + printk(KERN_ERR "unionfs: %s requires an argument\n", + optname); + err = -EINVAL; + goto out_error; + } + + if (!strcmp("dirs", optname)) { + if (++dirsfound > 1) { + printk(KERN_ERR + "unionfs: multiple dirs specified\n"); + err = -EINVAL; + goto out_error; + } + err = parse_dirs_option(sb, lower_root_info, optarg); + if (err) + goto out_error; + continue; + } + + err = -EINVAL; + printk(KERN_ERR + "unionfs: unrecognized option '%s'\n", optname); + goto out_error; + } + if (dirsfound != 1) { + printk(KERN_ERR "unionfs: dirs option required\n"); + err = -EINVAL; + goto out_error; + } + goto out; + +out_error: + if (lower_root_info && lower_root_info->lower_paths) { + for (bindex = lower_root_info->bstart; + bindex >= 0 && bindex <= lower_root_info->bend; + bindex++) { + struct dentry *d; + struct vfsmount *m; + + d = lower_root_info->lower_paths[bindex].dentry; + m = lower_root_info->lower_paths[bindex].mnt; + + dput(d); + /* initializing: can't use unionfs_mntput here */ + mntput(m); + } + } + + kfree(lower_root_info->lower_paths); + kfree(lower_root_info); + + kfree(UNIONFS_SB(sb)->data); + UNIONFS_SB(sb)->data = NULL; + + lower_root_info = ERR_PTR(err); +out: + return lower_root_info; +} + +/* + * our custom d_alloc_root work-alike + * + * we can't use d_alloc_root if we want to use our own interpose function + * unchanged, so we simply call our own "fake" d_alloc_root + */ +static struct dentry *unionfs_d_alloc_root(struct super_block *sb) +{ + struct dentry *ret = NULL; + + if (sb) { + static const struct qstr name = { + .name = "/", + .len = 1 + }; + + ret = d_alloc(NULL, &name); + if (likely(ret)) { + ret->d_op = &unionfs_dops; + ret->d_sb = sb; + ret->d_parent = ret; + } + } + return ret; +} + +/* + * There is no need to lock the unionfs_super_info's rwsem as there is no + * way anyone can have a reference to the superblock at this point in time. + */ +static int unionfs_read_super(struct super_block *sb, void *raw_data, + int silent) +{ + int err = 0; + struct unionfs_dentry_info *lower_root_info = NULL; + int bindex, bstart, bend; + + if (!raw_data) { + printk(KERN_ERR + "unionfs: read_super: missing data argument\n"); + err = -EINVAL; + goto out; + } + + /* Allocate superblock private data */ + sb->s_fs_info = kzalloc(sizeof(struct unionfs_sb_info), GFP_KERNEL); + if (unlikely(!UNIONFS_SB(sb))) { + printk(KERN_CRIT "unionfs: read_super: out of memory\n"); + err = -ENOMEM; + goto out; + } + + UNIONFS_SB(sb)->bend = -1; + atomic_set(&UNIONFS_SB(sb)->generation, 1); + init_rwsem(&UNIONFS_SB(sb)->rwsem); + UNIONFS_SB(sb)->high_branch_id = -1; /* -1 == invalid branch ID */ + + lower_root_info = unionfs_parse_options(sb, raw_data); + if (IS_ERR(lower_root_info)) { + printk(KERN_ERR + "unionfs: read_super: error while parsing options " + "(err = %ld)\n", PTR_ERR(lower_root_info)); + err = PTR_ERR(lower_root_info); + lower_root_info = NULL; + goto out_free; + } + if (lower_root_info->bstart == -1) { + err = -ENOENT; + goto out_free; + } + + /* set the lower superblock field of upper superblock */ + bstart = lower_root_info->bstart; + BUG_ON(bstart != 0); + sbend(sb) = bend = lower_root_info->bend; + for (bindex = bstart; bindex <= bend; bindex++) { + struct dentry *d = lower_root_info->lower_paths[bindex].dentry; + atomic_inc(&d->d_sb->s_active); + unionfs_set_lower_super_idx(sb, bindex, d->d_sb); + } + + /* max Bytes is the maximum bytes from highest priority branch */ + sb->s_maxbytes = unionfs_lower_super_idx(sb, 0)->s_maxbytes; + + /* + * Our c/m/atime granularity is 1 ns because we may stack on file + * systems whose granularity is as good. This is important for our + * time-based cache coherency. + */ + sb->s_time_gran = 1; + + sb->s_op = &unionfs_sops; + + /* See comment next to the definition of unionfs_d_alloc_root */ + sb->s_root = unionfs_d_alloc_root(sb); + if (unlikely(!sb->s_root)) { + err = -ENOMEM; + goto out_dput; + } + + /* link the upper and lower dentries */ + sb->s_root->d_fsdata = NULL; + err = new_dentry_private_data(sb->s_root, UNIONFS_DMUTEX_ROOT); + if (unlikely(err)) + goto out_freedpd; + + /* Set the lower dentries for s_root */ + for (bindex = bstart; bindex <= bend; bindex++) { + struct dentry *d; + struct vfsmount *m; + + d = lower_root_info->lower_paths[bindex].dentry; + m = lower_root_info->lower_paths[bindex].mnt; + + unionfs_set_lower_dentry_idx(sb->s_root, bindex, d); + unionfs_set_lower_mnt_idx(sb->s_root, bindex, m); + } + dbstart(sb->s_root) = bstart; + dbend(sb->s_root) = bend; + + /* Set the generation number to one, since this is for the mount. */ + atomic_set(&UNIONFS_D(sb->s_root)->generation, 1); + + /* + * Call interpose to create the upper level inode. Only + * INTERPOSE_LOOKUP can return a value other than 0 on err. + */ + err = PTR_ERR(unionfs_interpose(sb->s_root, sb, 0)); + unionfs_unlock_dentry(sb->s_root); + if (!err) + goto out; + /* else fall through */ + +out_freedpd: + if (UNIONFS_D(sb->s_root)) { + kfree(UNIONFS_D(sb->s_root)->lower_paths); + free_dentry_private_data(sb->s_root); + } + dput(sb->s_root); + +out_dput: + if (lower_root_info && !IS_ERR(lower_root_info)) { + for (bindex = lower_root_info->bstart; + bindex <= lower_root_info->bend; bindex++) { + struct dentry *d; + struct vfsmount *m; + + d = lower_root_info->lower_paths[bindex].dentry; + m = lower_root_info->lower_paths[bindex].mnt; + + dput(d); + /* initializing: can't use unionfs_mntput here */ + mntput(m); + /* drop refs we took earlier */ + atomic_dec(&d->d_sb->s_active); + } + kfree(lower_root_info->lower_paths); + kfree(lower_root_info); + lower_root_info = NULL; + } + +out_free: + kfree(UNIONFS_SB(sb)->data); + kfree(UNIONFS_SB(sb)); + sb->s_fs_info = NULL; + +out: + if (lower_root_info && !IS_ERR(lower_root_info)) { + kfree(lower_root_info->lower_paths); + kfree(lower_root_info); + } + return err; +} + +static int unionfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *raw_data, struct vfsmount *mnt) +{ + int err; + err = get_sb_nodev(fs_type, flags, raw_data, unionfs_read_super, mnt); + if (!err) + UNIONFS_SB(mnt->mnt_sb)->dev_name = + kstrdup(dev_name, GFP_KERNEL); + return err; +} + +static struct file_system_type unionfs_fs_type = { + .owner = THIS_MODULE, + .name = UNIONFS_NAME, + .get_sb = unionfs_get_sb, + .kill_sb = generic_shutdown_super, + .fs_flags = FS_REVAL_DOT, +}; + +static int __init init_unionfs_fs(void) +{ + int err; + + pr_info("Registering unionfs " UNIONFS_VERSION "\n"); + + err = unionfs_init_filldir_cache(); + if (unlikely(err)) + goto out; + err = unionfs_init_inode_cache(); + if (unlikely(err)) + goto out; + err = unionfs_init_dentry_cache(); + if (unlikely(err)) + goto out; + err = init_sioq(); + if (unlikely(err)) + goto out; + err = register_filesystem(&unionfs_fs_type); +out: + if (unlikely(err)) { + stop_sioq(); + unionfs_destroy_filldir_cache(); + unionfs_destroy_inode_cache(); + unionfs_destroy_dentry_cache(); + } + return err; +} + +static void __exit exit_unionfs_fs(void) +{ + stop_sioq(); + unionfs_destroy_filldir_cache(); + unionfs_destroy_inode_cache(); + unionfs_destroy_dentry_cache(); + unregister_filesystem(&unionfs_fs_type); + pr_info("Completed unionfs module unload\n"); +} + +MODULE_AUTHOR("Erez Zadok, Filesystems and Storage Lab, Stony Brook University" + " (http://www.fsl.cs.sunysb.edu)"); +MODULE_DESCRIPTION("Unionfs " UNIONFS_VERSION + " (http://unionfs.filesystems.org)"); +MODULE_LICENSE("GPL"); + +module_init(init_unionfs_fs); +module_exit(exit_unionfs_fs); diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/mmap.c kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/mmap.c --- kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/mmap.c 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/mmap.c 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2003-2009 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2007 Josef 'Jeff' Sipek + * Copyright (c) 2005-2006 Junjiro Okajima + * Copyright (c) 2006 Shaya Potter + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2004-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2009 Stony Brook University + * Copyright (c) 2003-2009 The Research Foundation of SUNY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "union.h" + + +/* + * XXX: we need a dummy readpage handler because generic_file_mmap (which we + * use in unionfs_mmap) checks for the existence of + * mapping->a_ops->readpage, else it returns -ENOEXEC. The VFS will need to + * be fixed to allow a file system to define vm_ops->fault without any + * address_space_ops whatsoever. + * + * Otherwise, we don't want to use our readpage method at all. + */ +static int unionfs_readpage(struct file *file, struct page *page) +{ + BUG(); + return -EINVAL; +} + +static struct page *unionfs_nopage(struct vm_area_struct *vma, + unsigned long address, int *type) +{ + struct file *file, *lower_file; + struct vm_operations_struct *lower_vm_ops; + struct vm_area_struct lower_vma; + struct page *page; + + BUG_ON(!vma); + memcpy(&lower_vma, vma, sizeof(struct vm_area_struct)); + file = lower_vma.vm_file; + lower_vm_ops = UNIONFS_F(file)->lower_vm_ops; + BUG_ON(!lower_vm_ops); + + lower_file = unionfs_lower_file(file); + BUG_ON(!lower_file); + /* + * XXX: vm_ops->nopage may be called in parallel. Because we have to + * resort to temporarily changing the vma->vm_file to point to the + * lower file, a concurrent invocation of unionfs_fault could see a + * different value. In this workaround, we keep a different copy of + * the vma structure in our stack, so we never expose a different + * value of the vma->vm_file called to us, even temporarily. A + * better fix would be to change the calling semantics of ->fault to + * take an explicit file pointer. + */ + lower_vma.vm_file = lower_file; + page = lower_vm_ops->nopage(&lower_vma, address, type); + return page; +} + +static int unionfs_populate(struct vm_area_struct *vma, + unsigned long address, unsigned long len, + pgprot_t prot, unsigned long pgoff, int nonblock) + +{ + int err = -EOPNOTSUPP; + struct file *file, *lower_file; + struct vm_operations_struct *lower_vm_ops; + struct vm_area_struct lower_vma; + + pr_debug("Unionfs: vm operation populate unsupported\n"); + + if (!vma) + goto out; + memcpy(&lower_vma, vma, sizeof(struct vm_area_struct)); + file = lower_vma.vm_file; + lower_vm_ops = UNIONFS_F(file)->lower_vm_ops; + if (!lower_vm_ops) + goto out; + + lower_file = unionfs_lower_file(file); + if (!lower_file) + goto out; + + lower_vma.vm_file = lower_file; + err = lower_vm_ops->populate(&lower_vma, address, len, prot, + pgoff, nonblock); +out: + return err; +} + +/* + * XXX: the default address_space_ops for unionfs is empty. We cannot set + * our inode->i_mapping->a_ops to NULL because too many code paths expect + * the a_ops vector to be non-NULL. + */ +struct address_space_operations unionfs_aops = { + /* empty on purpose */ +}; + +/* + * XXX: we need a second, dummy address_space_ops vector, to be used + * temporarily during unionfs_mmap, because the latter calls + * generic_file_mmap, which checks if ->readpage exists, else returns + * -ENOEXEC. + */ +struct address_space_operations unionfs_dummy_aops = { + .readpage = unionfs_readpage, +}; + +struct vm_operations_struct unionfs_vm_ops = { + .nopage = unionfs_nopage, + .populate = unionfs_populate, +}; diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/rdstate.c kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/rdstate.c --- kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/rdstate.c 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/rdstate.c 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2003-2009 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2007 Josef 'Jeff' Sipek + * Copyright (c) 2005-2006 Junjiro Okajima + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2004-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2009 Stony Brook University + * Copyright (c) 2003-2009 The Research Foundation of SUNY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "union.h" + +/* This file contains the routines for maintaining readdir state. */ + +/* + * There are two structures here, rdstate which is a hash table + * of the second structure which is a filldir_node. + */ + +/* + * This is a struct kmem_cache for filldir nodes, because we allocate a lot + * of them and they shouldn't waste memory. If the node has a small name + * (as defined by the dentry structure), then we use an inline name to + * preserve kmalloc space. + */ +static struct kmem_cache *unionfs_filldir_cachep; + +int unionfs_init_filldir_cache(void) +{ + unionfs_filldir_cachep = + kmem_cache_create("unionfs_filldir", + sizeof(struct filldir_node), 0, + SLAB_RECLAIM_ACCOUNT, NULL, NULL); + + return (unionfs_filldir_cachep ? 0 : -ENOMEM); +} + +void unionfs_destroy_filldir_cache(void) +{ + if (unionfs_filldir_cachep) + kmem_cache_destroy(unionfs_filldir_cachep); +} + +/* + * This is a tuning parameter that tells us roughly how big to make the + * hash table in directory entries per page. This isn't perfect, but + * at least we get a hash table size that shouldn't be too overloaded. + * The following averages are based on my home directory. + * 14.44693 Overall + * 12.29 Single Page Directories + * 117.93 Multi-page directories + */ +#define DENTPAGE 4096 +#define DENTPERONEPAGE 12 +#define DENTPERPAGE 118 +#define MINHASHSIZE 1 +static int guesstimate_hash_size(struct inode *inode) +{ + struct inode *lower_inode; + int bindex; + int hashsize = MINHASHSIZE; + + if (UNIONFS_I(inode)->hashsize > 0) + return UNIONFS_I(inode)->hashsize; + + for (bindex = ibstart(inode); bindex <= ibend(inode); bindex++) { + lower_inode = unionfs_lower_inode_idx(inode, bindex); + if (!lower_inode) + continue; + + if (i_size_read(lower_inode) == DENTPAGE) + hashsize += DENTPERONEPAGE; + else + hashsize += (i_size_read(lower_inode) / DENTPAGE) * + DENTPERPAGE; + } + + return hashsize; +} + +int init_rdstate(struct file *file) +{ + BUG_ON(sizeof(loff_t) != + (sizeof(unsigned int) + sizeof(unsigned int))); + BUG_ON(UNIONFS_F(file)->rdstate != NULL); + + UNIONFS_F(file)->rdstate = alloc_rdstate(file->f_path.dentry->d_inode, + fbstart(file)); + + return (UNIONFS_F(file)->rdstate ? 0 : -ENOMEM); +} + +struct unionfs_dir_state *find_rdstate(struct inode *inode, loff_t fpos) +{ + struct unionfs_dir_state *rdstate = NULL; + struct list_head *pos; + + spin_lock(&UNIONFS_I(inode)->rdlock); + list_for_each(pos, &UNIONFS_I(inode)->readdircache) { + struct unionfs_dir_state *r = + list_entry(pos, struct unionfs_dir_state, cache); + if (fpos == rdstate2offset(r)) { + UNIONFS_I(inode)->rdcount--; + list_del(&r->cache); + rdstate = r; + break; + } + } + spin_unlock(&UNIONFS_I(inode)->rdlock); + return rdstate; +} + +struct unionfs_dir_state *alloc_rdstate(struct inode *inode, int bindex) +{ + int i = 0; + int hashsize; + unsigned long mallocsize = sizeof(struct unionfs_dir_state); + struct unionfs_dir_state *rdstate; + + hashsize = guesstimate_hash_size(inode); + mallocsize += hashsize * sizeof(struct list_head); + mallocsize = __roundup_pow_of_two(mallocsize); + + /* This should give us about 500 entries anyway. */ + if (mallocsize > PAGE_SIZE) + mallocsize = PAGE_SIZE; + + hashsize = (mallocsize - sizeof(struct unionfs_dir_state)) / + sizeof(struct list_head); + + rdstate = kmalloc(mallocsize, GFP_KERNEL); + if (unlikely(!rdstate)) + return NULL; + + spin_lock(&UNIONFS_I(inode)->rdlock); + if (UNIONFS_I(inode)->cookie >= (MAXRDCOOKIE - 1)) + UNIONFS_I(inode)->cookie = 1; + else + UNIONFS_I(inode)->cookie++; + + rdstate->cookie = UNIONFS_I(inode)->cookie; + spin_unlock(&UNIONFS_I(inode)->rdlock); + rdstate->offset = 1; + rdstate->access = jiffies; + rdstate->bindex = bindex; + rdstate->dirpos = 0; + rdstate->hashentries = 0; + rdstate->size = hashsize; + for (i = 0; i < rdstate->size; i++) + INIT_LIST_HEAD(&rdstate->list[i]); + + return rdstate; +} + +static void free_filldir_node(struct filldir_node *node) +{ + if (node->namelen >= DNAME_INLINE_LEN_MIN) + kfree(node->name); + kmem_cache_free(unionfs_filldir_cachep, node); +} + +void free_rdstate(struct unionfs_dir_state *state) +{ + struct filldir_node *tmp; + int i; + + for (i = 0; i < state->size; i++) { + struct list_head *head = &(state->list[i]); + struct list_head *pos, *n; + + /* traverse the list and deallocate space */ + list_for_each_safe(pos, n, head) { + tmp = list_entry(pos, struct filldir_node, file_list); + list_del(&tmp->file_list); + free_filldir_node(tmp); + } + } + + kfree(state); +} + +struct filldir_node *find_filldir_node(struct unionfs_dir_state *rdstate, + const char *name, int namelen, + int is_whiteout) +{ + int index; + unsigned int hash; + struct list_head *head; + struct list_head *pos; + struct filldir_node *cursor = NULL; + int found = 0; + + BUG_ON(namelen <= 0); + + hash = full_name_hash(name, namelen); + index = hash % rdstate->size; + + head = &(rdstate->list[index]); + list_for_each(pos, head) { + cursor = list_entry(pos, struct filldir_node, file_list); + + if (cursor->namelen == namelen && cursor->hash == hash && + !strncmp(cursor->name, name, namelen)) { + /* + * a duplicate exists, and hence no need to create + * entry to the list + */ + found = 1; + + /* + * if a duplicate is found in this branch, and is + * not due to the caller looking for an entry to + * whiteout, then the file system may be corrupted. + */ + if (unlikely(!is_whiteout && + cursor->bindex == rdstate->bindex)) + printk(KERN_ERR "unionfs: filldir: possible " + "I/O error: a file is duplicated " + "in the same branch %d: %s\n", + rdstate->bindex, cursor->name); + break; + } + } + + if (!found) + cursor = NULL; + + return cursor; +} + +int add_filldir_node(struct unionfs_dir_state *rdstate, const char *name, + int namelen, int bindex, int whiteout) +{ + struct filldir_node *new; + unsigned int hash; + int index; + int err = 0; + struct list_head *head; + + BUG_ON(namelen <= 0); + + hash = full_name_hash(name, namelen); + index = hash % rdstate->size; + head = &(rdstate->list[index]); + + new = kmem_cache_alloc(unionfs_filldir_cachep, GFP_KERNEL); + if (unlikely(!new)) { + err = -ENOMEM; + goto out; + } + + INIT_LIST_HEAD(&new->file_list); + new->namelen = namelen; + new->hash = hash; + new->bindex = bindex; + new->whiteout = whiteout; + + if (namelen < DNAME_INLINE_LEN_MIN) { + new->name = new->iname; + } else { + new->name = kmalloc(namelen + 1, GFP_KERNEL); + if (unlikely(!new->name)) { + kmem_cache_free(unionfs_filldir_cachep, new); + new = NULL; + goto out; + } + } + + memcpy(new->name, name, namelen); + new->name[namelen] = '\0'; + + rdstate->hashentries++; + + list_add(&(new->file_list), head); +out: + return err; +} diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/rename.c kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/rename.c --- kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/rename.c 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/rename.c 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,520 @@ +/* + * Copyright (c) 2003-2009 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2007 Josef 'Jeff' Sipek + * Copyright (c) 2005-2006 Junjiro Okajima + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2004-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2009 Stony Brook University + * Copyright (c) 2003-2009 The Research Foundation of SUNY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "union.h" + +/* + * This is a helper function for rename, used when rename ends up with hosed + * over dentries and we need to revert. + */ +static int unionfs_refresh_lower_dentry(struct dentry *dentry, + struct dentry *parent, int bindex) +{ + struct dentry *lower_dentry; + struct dentry *lower_parent; + int err = 0; + + verify_locked(dentry); + + lower_parent = unionfs_lower_dentry_idx(parent, bindex); + + BUG_ON(!S_ISDIR(lower_parent->d_inode->i_mode)); + + lower_dentry = lookup_one_len(dentry->d_name.name, lower_parent, + dentry->d_name.len); + if (IS_ERR(lower_dentry)) { + err = PTR_ERR(lower_dentry); + goto out; + } + + dput(unionfs_lower_dentry_idx(dentry, bindex)); + iput(unionfs_lower_inode_idx(dentry->d_inode, bindex)); + unionfs_set_lower_inode_idx(dentry->d_inode, bindex, NULL); + + if (!lower_dentry->d_inode) { + dput(lower_dentry); + unionfs_set_lower_dentry_idx(dentry, bindex, NULL); + } else { + unionfs_set_lower_dentry_idx(dentry, bindex, lower_dentry); + unionfs_set_lower_inode_idx(dentry->d_inode, bindex, + igrab(lower_dentry->d_inode)); + } + +out: + return err; +} + +static int __unionfs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct dentry *old_parent, + struct inode *new_dir, struct dentry *new_dentry, + struct dentry *new_parent, + int bindex) +{ + int err = 0; + struct dentry *lower_old_dentry; + struct dentry *lower_new_dentry; + struct dentry *lower_old_dir_dentry; + struct dentry *lower_new_dir_dentry; + struct dentry *trap; + + lower_new_dentry = unionfs_lower_dentry_idx(new_dentry, bindex); + lower_old_dentry = unionfs_lower_dentry_idx(old_dentry, bindex); + + if (!lower_new_dentry) { + lower_new_dentry = + create_parents(new_parent->d_inode, + new_dentry, new_dentry->d_name.name, + bindex); + if (IS_ERR(lower_new_dentry)) { + err = PTR_ERR(lower_new_dentry); + if (IS_COPYUP_ERR(err)) + goto out; + printk(KERN_ERR "unionfs: error creating directory " + "tree for rename, bindex=%d err=%d\n", + bindex, err); + goto out; + } + } + + /* check for and remove whiteout, if any */ + err = check_unlink_whiteout(new_dentry, lower_new_dentry, bindex); + if (err > 0) /* ignore if whiteout found and successfully removed */ + err = 0; + if (err) + goto out; + + /* check of old_dentry branch is writable */ + err = is_robranch_super(old_dentry->d_sb, bindex); + if (err) + goto out; + + 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); + + /* see Documentation/filesystems/unionfs/issues.txt */ + lockdep_off(); + trap = lock_rename(lower_old_dir_dentry, lower_new_dir_dentry); + /* source should not be ancenstor of target */ + if (trap == lower_old_dentry) { + err = -EINVAL; + goto out_err_unlock; + } + /* target should not be ancenstor of source */ + if (trap == lower_new_dentry) { + err = -ENOTEMPTY; + goto out_err_unlock; + } + err = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry, + lower_new_dir_dentry->d_inode, lower_new_dentry); +out_err_unlock: + if (!err) { + /* update parent dir times */ + fsstack_copy_attr_times(old_dir, lower_old_dir_dentry->d_inode); + fsstack_copy_attr_times(new_dir, lower_new_dir_dentry->d_inode); + } + unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); + lockdep_on(); + + dput(lower_old_dir_dentry); + dput(lower_new_dir_dentry); + dput(lower_old_dentry); + dput(lower_new_dentry); + +out: + if (!err) { + /* Fixup the new_dentry. */ + if (bindex < dbstart(new_dentry)) + dbstart(new_dentry) = bindex; + else if (bindex > dbend(new_dentry)) + dbend(new_dentry) = bindex; + } + + return err; +} + +/* + * Main rename code. This is sufficiently complex, that it's documented in + * Documentation/filesystems/unionfs/rename.txt. This routine calls + * __unionfs_rename() above to perform some of the work. + */ +static int do_unionfs_rename(struct inode *old_dir, + struct dentry *old_dentry, + struct dentry *old_parent, + struct inode *new_dir, + struct dentry *new_dentry, + struct dentry *new_parent) +{ + int err = 0; + int bindex; + int old_bstart, old_bend; + int new_bstart, new_bend; + int do_copyup = -1; + int local_err = 0; + int eio = 0; + int revert = 0; + + old_bstart = dbstart(old_dentry); + old_bend = dbend(old_dentry); + + new_bstart = dbstart(new_dentry); + new_bend = dbend(new_dentry); + + /* Rename source to destination. */ + err = __unionfs_rename(old_dir, old_dentry, old_parent, + new_dir, new_dentry, new_parent, + old_bstart); + if (err) { + if (!IS_COPYUP_ERR(err)) + goto out; + do_copyup = old_bstart - 1; + } else { + revert = 1; + } + + /* + * Unlink all instances of destination that exist to the left of + * bstart of source. On error, revert back, goto out. + */ + for (bindex = old_bstart - 1; bindex >= new_bstart; bindex--) { + struct dentry *unlink_dentry; + struct dentry *unlink_dir_dentry; + + BUG_ON(bindex < 0); + unlink_dentry = unionfs_lower_dentry_idx(new_dentry, bindex); + if (!unlink_dentry) + continue; + + unlink_dir_dentry = lock_parent(unlink_dentry); + err = is_robranch_super(old_dir->i_sb, bindex); + if (!err) + err = vfs_unlink(unlink_dir_dentry->d_inode, + unlink_dentry); + + fsstack_copy_attr_times(new_parent->d_inode, + unlink_dir_dentry->d_inode); + /* propagate number of hard-links */ + new_parent->d_inode->i_nlink = + unionfs_get_nlinks(new_parent->d_inode); + + unlock_dir(unlink_dir_dentry); + if (!err) { + if (bindex != new_bstart) { + dput(unlink_dentry); + unionfs_set_lower_dentry_idx(new_dentry, + bindex, NULL); + } + } else if (IS_COPYUP_ERR(err)) { + do_copyup = bindex - 1; + } else if (revert) { + goto revert; + } + } + + if (do_copyup != -1) { + for (bindex = do_copyup; bindex >= 0; bindex--) { + /* + * copyup the file into some left directory, so that + * you can rename it + */ + err = copyup_dentry(old_parent->d_inode, + old_dentry, old_bstart, bindex, + old_dentry->d_name.name, + old_dentry->d_name.len, NULL, + i_size_read(old_dentry->d_inode)); + /* if copyup failed, try next branch to the left */ + if (err) + continue; + /* + * create whiteout before calling __unionfs_rename + * because the latter will change the old_dentry's + * lower name and parent dir, resulting in the + * whiteout getting created in the wrong dir. + */ + err = create_whiteout(old_dentry, bindex); + if (err) { + printk(KERN_ERR "unionfs: can't create a " + "whiteout for %s in rename (err=%d)\n", + old_dentry->d_name.name, err); + continue; + } + err = __unionfs_rename(old_dir, old_dentry, old_parent, + new_dir, new_dentry, new_parent, + bindex); + break; + } + } + + /* make it opaque */ + if (S_ISDIR(old_dentry->d_inode->i_mode)) { + err = make_dir_opaque(old_dentry, dbstart(old_dentry)); + if (err) + goto revert; + } + + /* + * Create whiteout for source, only if: + * (1) There is more than one underlying instance of source. + * (We did a copy_up is taken care of above). + */ + if ((old_bstart != old_bend) && (do_copyup == -1)) { + err = create_whiteout(old_dentry, old_bstart); + if (err) { + /* can't fix anything now, so we exit with -EIO */ + printk(KERN_ERR "unionfs: can't create a whiteout for " + "%s in rename!\n", old_dentry->d_name.name); + err = -EIO; + } + } + +out: + return err; + +revert: + /* Do revert here. */ + local_err = unionfs_refresh_lower_dentry(new_dentry, new_parent, + old_bstart); + if (local_err) { + printk(KERN_ERR "unionfs: revert failed in rename: " + "the new refresh failed\n"); + eio = -EIO; + } + + local_err = unionfs_refresh_lower_dentry(old_dentry, old_parent, + old_bstart); + if (local_err) { + printk(KERN_ERR "unionfs: revert failed in rename: " + "the old refresh failed\n"); + eio = -EIO; + goto revert_out; + } + + if (!unionfs_lower_dentry_idx(new_dentry, bindex) || + !unionfs_lower_dentry_idx(new_dentry, bindex)->d_inode) { + printk(KERN_ERR "unionfs: revert failed in rename: " + "the object disappeared from under us!\n"); + eio = -EIO; + goto revert_out; + } + + if (unionfs_lower_dentry_idx(old_dentry, bindex) && + unionfs_lower_dentry_idx(old_dentry, bindex)->d_inode) { + printk(KERN_ERR "unionfs: revert failed in rename: " + "the object was created underneath us!\n"); + eio = -EIO; + goto revert_out; + } + + local_err = __unionfs_rename(new_dir, new_dentry, new_parent, + old_dir, old_dentry, old_parent, + old_bstart); + + /* If we can't fix it, then we cop-out with -EIO. */ + if (local_err) { + printk(KERN_ERR "unionfs: revert failed in rename!\n"); + eio = -EIO; + } + + local_err = unionfs_refresh_lower_dentry(new_dentry, new_parent, + bindex); + if (local_err) + eio = -EIO; + local_err = unionfs_refresh_lower_dentry(old_dentry, old_parent, + bindex); + if (local_err) + eio = -EIO; + +revert_out: + if (eio) + err = eio; + return err; +} + +/* + * We can't copyup a directory, because it may involve huge numbers of + * children, etc. Doing that in the kernel would be bad, so instead we + * return EXDEV to the user-space utility that caused this, and let the + * user-space recurse and ask us to copy up each file separately. + */ +static int may_rename_dir(struct dentry *dentry, struct dentry *parent) +{ + int err, bstart; + + err = check_empty(dentry, parent, NULL); + if (err == -ENOTEMPTY) { + if (is_robranch(dentry)) + return -EXDEV; + } else if (err) { + return err; + } + + bstart = dbstart(dentry); + if (dbend(dentry) == bstart || dbopaque(dentry) == bstart) + return 0; + + dbstart(dentry) = bstart + 1; + err = check_empty(dentry, parent, NULL); + dbstart(dentry) = bstart; + if (err == -ENOTEMPTY) + err = -EXDEV; + return err; +} + +/* + * The locking rules in unionfs_rename are complex. We could use a simpler + * superblock-level name-space lock for renames and copy-ups. + */ +int unionfs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + int err = 0; + struct dentry *wh_dentry; + struct dentry *old_parent, *new_parent; + int valid = true; + + unionfs_read_lock(old_dentry->d_sb, UNIONFS_SMUTEX_CHILD); + old_parent = dget_parent(old_dentry); + new_parent = dget_parent(new_dentry); + /* un/lock parent dentries only if they differ from old/new_dentry */ + if (old_parent != old_dentry && + old_parent != new_dentry) + unionfs_lock_dentry(old_parent, UNIONFS_DMUTEX_REVAL_PARENT); + if (new_parent != old_dentry && + new_parent != new_dentry && + new_parent != old_parent) + unionfs_lock_dentry(new_parent, UNIONFS_DMUTEX_REVAL_CHILD); + unionfs_double_lock_dentry(old_dentry, new_dentry); + + valid = __unionfs_d_revalidate(old_dentry, old_parent, false); + if (!valid) { + err = -ESTALE; + goto out; + } + if (!d_deleted(new_dentry) && new_dentry->d_inode) { + valid = __unionfs_d_revalidate(new_dentry, new_parent, false); + if (!valid) { + err = -ESTALE; + goto out; + } + } + + if (!S_ISDIR(old_dentry->d_inode->i_mode)) + err = unionfs_partial_lookup(old_dentry, old_parent); + else + err = may_rename_dir(old_dentry, old_parent); + + if (err) + goto out; + + err = unionfs_partial_lookup(new_dentry, new_parent); + if (err) + goto out; + + /* + * if new_dentry is already lower because of whiteout, + * simply override it even if the whited-out dir is not empty. + */ + wh_dentry = find_first_whiteout(new_dentry); + if (!IS_ERR(wh_dentry)) { + dput(wh_dentry); + } else if (new_dentry->d_inode) { + if (S_ISDIR(old_dentry->d_inode->i_mode) != + S_ISDIR(new_dentry->d_inode->i_mode)) { + err = S_ISDIR(old_dentry->d_inode->i_mode) ? + -ENOTDIR : -EISDIR; + goto out; + } + + if (S_ISDIR(new_dentry->d_inode->i_mode)) { + struct unionfs_dir_state *namelist = NULL; + /* check if this unionfs directory is empty or not */ + err = check_empty(new_dentry, new_parent, &namelist); + if (err) + goto out; + + if (!is_robranch(new_dentry)) + err = delete_whiteouts(new_dentry, + dbstart(new_dentry), + namelist); + + free_rdstate(namelist); + + if (err) + goto out; + } + } + + err = do_unionfs_rename(old_dir, old_dentry, old_parent, + new_dir, new_dentry, new_parent); + if (err) + goto out; + + /* + * force re-lookup since the dir on ro branch is not renamed, and + * lower dentries still indicate the un-renamed ones. + */ + if (S_ISDIR(old_dentry->d_inode->i_mode)) + atomic_dec(&UNIONFS_D(old_dentry)->generation); + else + unionfs_postcopyup_release(old_dentry); + if (new_dentry->d_inode && !S_ISDIR(new_dentry->d_inode->i_mode)) { + unionfs_postcopyup_release(new_dentry); + unionfs_postcopyup_setmnt(new_dentry); + if (!unionfs_lower_inode(new_dentry->d_inode)) { + /* + * If we get here, it means that no copyup was + * needed, and that a file by the old name already + * existing on the destination branch; that file got + * renamed earlier in this function, so all we need + * to do here is set the lower inode. + */ + struct inode *inode; + inode = unionfs_lower_inode(old_dentry->d_inode); + igrab(inode); + unionfs_set_lower_inode_idx(new_dentry->d_inode, + dbstart(new_dentry), + inode); + } + } + /* if all of this renaming succeeded, update our times */ + unionfs_copy_attr_times(old_dentry->d_inode); + unionfs_copy_attr_times(new_dentry->d_inode); + unionfs_check_inode(old_dir); + unionfs_check_inode(new_dir); + unionfs_check_dentry(old_dentry); + unionfs_check_dentry(new_dentry); + +out: + if (err) /* clear the new_dentry stuff created */ + d_drop(new_dentry); + + unionfs_double_unlock_dentry(old_dentry, new_dentry); + if (new_parent != old_dentry && + new_parent != new_dentry && + new_parent != old_parent) + unionfs_unlock_dentry(new_parent); + if (old_parent != old_dentry && + old_parent != new_dentry) + unionfs_unlock_dentry(old_parent); + dput(new_parent); + dput(old_parent); + unionfs_read_unlock(old_dentry->d_sb); + + return err; +} diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/sioq.c kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/sioq.c --- kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/sioq.c 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/sioq.c 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2006-2009 Erez Zadok + * Copyright (c) 2006 Charles P. Wright + * Copyright (c) 2006-2007 Josef 'Jeff' Sipek + * Copyright (c) 2006 Junjiro Okajima + * Copyright (c) 2006 David P. Quigley + * Copyright (c) 2006-2009 Stony Brook University + * Copyright (c) 2006-2009 The Research Foundation of SUNY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "union.h" + +/* + * Super-user IO work Queue - sometimes we need to perform actions which + * would fail due to the unix permissions on the parent directory (e.g., + * rmdir a directory which appears empty, but in reality contains + * whiteouts). + */ + +static struct workqueue_struct *superio_workqueue; + +int __init init_sioq(void) +{ + int err; + + superio_workqueue = create_workqueue("unionfs_siod"); + if (!IS_ERR(superio_workqueue)) + return 0; + + err = PTR_ERR(superio_workqueue); + printk(KERN_ERR "unionfs: create_workqueue failed %d\n", err); + superio_workqueue = NULL; + return err; +} + +void stop_sioq(void) +{ + if (superio_workqueue) + destroy_workqueue(superio_workqueue); +} + +void run_sioq(work_func_t func, struct sioq_args *args) +{ + INIT_WORK(&args->work, func); + + init_completion(&args->comp); + while (!queue_work(superio_workqueue, &args->work)) { + /* TODO: do accounting if needed */ + schedule(); + } + wait_for_completion(&args->comp); +} + +void __unionfs_create(struct work_struct *work) +{ + struct sioq_args *args = container_of(work, struct sioq_args, work); + struct create_args *c = &args->create; + + args->err = vfs_create(c->parent, c->dentry, c->mode, c->nd); + complete(&args->comp); +} + +void __unionfs_mkdir(struct work_struct *work) +{ + struct sioq_args *args = container_of(work, struct sioq_args, work); + struct mkdir_args *m = &args->mkdir; + + args->err = vfs_mkdir(m->parent, m->dentry, m->mode); + complete(&args->comp); +} + +void __unionfs_mknod(struct work_struct *work) +{ + struct sioq_args *args = container_of(work, struct sioq_args, work); + struct mknod_args *m = &args->mknod; + + args->err = vfs_mknod(m->parent, m->dentry, m->mode, m->dev); + complete(&args->comp); +} + +void __unionfs_symlink(struct work_struct *work) +{ + struct sioq_args *args = container_of(work, struct sioq_args, work); + struct symlink_args *s = &args->symlink; + + args->err = vfs_symlink(s->parent, s->dentry, s->symbuf, s->mode); + complete(&args->comp); +} + +void __unionfs_unlink(struct work_struct *work) +{ + struct sioq_args *args = container_of(work, struct sioq_args, work); + struct unlink_args *u = &args->unlink; + + args->err = vfs_unlink(u->parent, u->dentry); + complete(&args->comp); +} diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/sioq.h kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/sioq.h --- kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/sioq.h 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/sioq.h 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2006-2009 Erez Zadok + * Copyright (c) 2006 Charles P. Wright + * Copyright (c) 2006-2007 Josef 'Jeff' Sipek + * Copyright (c) 2006 Junjiro Okajima + * Copyright (c) 2006 David P. Quigley + * Copyright (c) 2006-2009 Stony Brook University + * Copyright (c) 2006-2009 The Research Foundation of SUNY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _SIOQ_H +#define _SIOQ_H + +struct deletewh_args { + struct unionfs_dir_state *namelist; + struct dentry *dentry; + int bindex; +}; + +struct is_opaque_args { + struct dentry *dentry; +}; + +struct create_args { + struct inode *parent; + struct dentry *dentry; + umode_t mode; + struct nameidata *nd; +}; + +struct mkdir_args { + struct inode *parent; + struct dentry *dentry; + umode_t mode; +}; + +struct mknod_args { + struct inode *parent; + struct dentry *dentry; + umode_t mode; + dev_t dev; +}; + +struct symlink_args { + struct inode *parent; + struct dentry *dentry; + char *symbuf; + umode_t mode; +}; + +struct unlink_args { + struct inode *parent; + struct dentry *dentry; +}; + + +struct sioq_args { + struct completion comp; + struct work_struct work; + int err; + void *ret; + + union { + struct deletewh_args deletewh; + struct is_opaque_args is_opaque; + struct create_args create; + struct mkdir_args mkdir; + struct mknod_args mknod; + struct symlink_args symlink; + struct unlink_args unlink; + }; +}; + +/* Extern definitions for SIOQ functions */ +extern int __init init_sioq(void); +extern void stop_sioq(void); +extern void run_sioq(work_func_t func, struct sioq_args *args); + +/* Extern definitions for our privilege escalation helpers */ +extern void __unionfs_create(struct work_struct *work); +extern void __unionfs_mkdir(struct work_struct *work); +extern void __unionfs_mknod(struct work_struct *work); +extern void __unionfs_symlink(struct work_struct *work); +extern void __unionfs_unlink(struct work_struct *work); +extern void __delete_whiteouts(struct work_struct *work); +extern void __is_opaque_dir(struct work_struct *work); + +#endif /* not _SIOQ_H */ diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/subr.c kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/subr.c --- kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/subr.c 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/subr.c 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2003-2009 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2007 Josef 'Jeff' Sipek + * Copyright (c) 2005-2006 Junjiro Okajima + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2004-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2009 Stony Brook University + * Copyright (c) 2003-2009 The Research Foundation of SUNY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "union.h" + +/* + * returns the right n_link value based on the inode type + */ +int unionfs_get_nlinks(const struct inode *inode) +{ + /* don't bother to do all the work since we're unlinked */ + if (inode->i_nlink == 0) + return 0; + + if (!S_ISDIR(inode->i_mode)) + return unionfs_lower_inode(inode)->i_nlink; + + /* + * For directories, we return 1. The only place that could cares + * about links is readdir, and there's d_type there so even that + * doesn't matter. + */ + return 1; +} + +/* copy a/m/ctime from the lower branch with the newest times */ +void unionfs_copy_attr_times(struct inode *upper) +{ + int bindex; + struct inode *lower; + + if (!upper) + return; + if (ibstart(upper) < 0) { +#ifdef CONFIG_UNION_FS_DEBUG + WARN_ON(ibstart(upper) < 0); +#endif /* CONFIG_UNION_FS_DEBUG */ + return; + } + for (bindex = ibstart(upper); bindex <= ibend(upper); bindex++) { + lower = unionfs_lower_inode_idx(upper, bindex); + if (!lower) + continue; /* not all lower dir objects may exist */ + if (unlikely(timespec_compare(&upper->i_mtime, + &lower->i_mtime) < 0)) + upper->i_mtime = lower->i_mtime; + if (unlikely(timespec_compare(&upper->i_ctime, + &lower->i_ctime) < 0)) + upper->i_ctime = lower->i_ctime; + if (unlikely(timespec_compare(&upper->i_atime, + &lower->i_atime) < 0)) + upper->i_atime = lower->i_atime; + } +} + +/* + * A unionfs/fanout version of fsstack_copy_attr_all. Uses a + * unionfs_get_nlinks to properly calcluate the number of links to a file. + * Also, copies the max() of all a/m/ctimes for all lower inodes (which is + * important if the lower inode is a directory type) + */ +void unionfs_copy_attr_all(struct inode *dest, + const struct inode *src) +{ + dest->i_mode = src->i_mode; + dest->i_uid = src->i_uid; + dest->i_gid = src->i_gid; + dest->i_rdev = src->i_rdev; + + unionfs_copy_attr_times(dest); + + dest->i_blkbits = src->i_blkbits; + dest->i_flags = src->i_flags; + + /* + * Update the nlinks AFTER updating the above fields, because the + * get_links callback may depend on them. + */ + dest->i_nlink = unionfs_get_nlinks(dest); +} diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/super.c kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/super.c --- kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/super.c 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/super.c 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,1048 @@ +/* + * Copyright (c) 2003-2009 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2007 Josef 'Jeff' Sipek + * Copyright (c) 2005-2006 Junjiro Okajima + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2004-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2009 Stony Brook University + * Copyright (c) 2003-2009 The Research Foundation of SUNY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "union.h" + +/* + * The inode cache is used with alloc_inode for both our inode info and the + * vfs inode. + */ +static struct kmem_cache *unionfs_inode_cachep; + +static void unionfs_read_inode(struct inode *inode) +{ + int size; + struct unionfs_inode_info *info = UNIONFS_I(inode); + + memset(info, 0, offsetof(struct unionfs_inode_info, vfs_inode)); + info->bstart = -1; + info->bend = -1; + atomic_set(&info->generation, + atomic_read(&UNIONFS_SB(inode->i_sb)->generation)); + spin_lock_init(&info->rdlock); + info->rdcount = 1; + info->hashsize = -1; + INIT_LIST_HEAD(&info->readdircache); + + size = sbmax(inode->i_sb) * sizeof(struct inode *); + info->lower_inodes = kzalloc(size, GFP_KERNEL); + if (unlikely(!info->lower_inodes)) { + printk(KERN_CRIT "unionfs: no kernel memory when allocating " + "lower-pointer array!\n"); + BUG(); + } + + inode->i_version++; + inode->i_op = &unionfs_main_iops; + inode->i_fop = &unionfs_main_fops; + + inode->i_mapping->a_ops = &unionfs_aops; + + /* + * reset times so unionfs_copy_attr_all can keep out time invariants + * right (upper inode time being the max of all lower ones). + */ + inode->i_atime.tv_sec = inode->i_atime.tv_nsec = 0; + inode->i_mtime.tv_sec = inode->i_mtime.tv_nsec = 0; + inode->i_ctime.tv_sec = inode->i_ctime.tv_nsec = 0; + +} + +/* + * we now define delete_inode, because there are two VFS paths that may + * destroy an inode: one of them calls clear inode before doing everything + * else that's needed, and the other is fine. This way we truncate the inode + * size (and its pages) and then clear our own inode, which will do an iput + * on our and the lower inode. + * + * No need to lock sb info's rwsem. + */ +static void unionfs_delete_inode(struct inode *inode) +{ +#if BITS_PER_LONG == 32 && defined(CONFIG_SMP) + spin_lock(&inode->i_lock); +#endif + i_size_write(inode, 0); /* every f/s seems to do that */ +#if BITS_PER_LONG == 32 && defined(CONFIG_SMP) + spin_unlock(&inode->i_lock); +#endif + + if (inode->i_data.nrpages) + truncate_inode_pages(&inode->i_data, 0); + + clear_inode(inode); +} + +/* + * final actions when unmounting a file system + * + * No need to lock rwsem. + */ +static void unionfs_put_super(struct super_block *sb) +{ + int bindex, bstart, bend; + struct unionfs_sb_info *spd; + int leaks = 0; + + spd = UNIONFS_SB(sb); + if (!spd) + return; + + bstart = sbstart(sb); + bend = sbend(sb); + + /* Make sure we have no leaks of branchget/branchput. */ + for (bindex = bstart; bindex <= bend; bindex++) + if (unlikely(branch_count(sb, bindex) != 0)) { + printk(KERN_CRIT + "unionfs: branch %d has %d references left!\n", + bindex, branch_count(sb, bindex)); + leaks = 1; + } + WARN_ON(leaks != 0); + + /* decrement lower super references */ + for (bindex = bstart; bindex <= bend; bindex++) { + struct super_block *s; + s = unionfs_lower_super_idx(sb, bindex); + unionfs_set_lower_super_idx(sb, bindex, NULL); + atomic_dec(&s->s_active); + } + + kfree(spd->dev_name); + kfree(spd->data); + kfree(spd); + sb->s_fs_info = NULL; +} + +/* + * Since people use this to answer the "How big of a file can I write?" + * question, we report the size of the highest priority branch as the size of + * the union. + */ +static int unionfs_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + int err = 0; + struct super_block *sb; + struct dentry *lower_dentry; + struct dentry *parent; + bool valid; + + sb = dentry->d_sb; + + unionfs_read_lock(sb, UNIONFS_SMUTEX_CHILD); + parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); + unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); + + valid = __unionfs_d_revalidate(dentry, parent, false); + if (unlikely(!valid)) { + err = -ESTALE; + goto out; + } + unionfs_check_dentry(dentry); + + lower_dentry = unionfs_lower_dentry(sb->s_root); + err = vfs_statfs(lower_dentry, buf); + + /* set return buf to our f/s to avoid confusing user-level utils */ + buf->f_type = UNIONFS_SUPER_MAGIC; + /* + * Our maximum file name can is shorter by a few bytes because every + * file name could potentially be whited-out. + * + * XXX: this restriction goes away with ODF. + */ + unionfs_set_max_namelen(&buf->f_namelen); + + /* + * reset two fields to avoid confusing user-land. + * XXX: is this still necessary? + */ + memset(&buf->f_fsid, 0, sizeof(__kernel_fsid_t)); + memset(&buf->f_spare, 0, sizeof(buf->f_spare)); + +out: + unionfs_check_dentry(dentry); + unionfs_unlock_dentry(dentry); + unionfs_unlock_parent(dentry, parent); + unionfs_read_unlock(sb); + return err; +} + +/* handle mode changing during remount */ +static noinline_for_stack int do_remount_mode_option( + char *optarg, + int cur_branches, + struct unionfs_data *new_data, + struct path *new_lower_paths) +{ + int err = -EINVAL; + int perms, idx; + char *modename = strchr(optarg, '='); + struct nameidata nd; + + /* by now, optarg contains the branch name */ + if (!*optarg) { + printk(KERN_ERR + "unionfs: no branch specified for mode change\n"); + goto out; + } + if (!modename) { + printk(KERN_ERR "unionfs: branch \"%s\" requires a mode\n", + optarg); + goto out; + } + *modename++ = '\0'; + err = parse_branch_mode(modename, &perms); + if (err) { + printk(KERN_ERR "unionfs: invalid mode \"%s\" for \"%s\"\n", + modename, optarg); + goto out; + } + + /* + * Find matching branch index. For now, this assumes that nothing + * has been mounted on top of this Unionfs stack. Once we have /odf + * and cache-coherency resolved, we'll address the branch-path + * uniqueness. + */ + err = path_lookup(optarg, LOOKUP_FOLLOW, &nd); + if (err) { + printk(KERN_ERR "unionfs: error accessing " + "lower directory \"%s\" (error %d)\n", + optarg, err); + goto out; + } + for (idx = 0; idx < cur_branches; idx++) + if (nd.mnt == new_lower_paths[idx].mnt && + nd.dentry == new_lower_paths[idx].dentry) + break; + path_release(&nd); /* no longer needed */ + if (idx == cur_branches) { + err = -ENOENT; /* err may have been reset above */ + printk(KERN_ERR "unionfs: branch \"%s\" " + "not found\n", optarg); + goto out; + } + /* check/change mode for existing branch */ + /* we don't warn if perms==branchperms */ + new_data[idx].branchperms = perms; + err = 0; +out: + return err; +} + +/* handle branch deletion during remount */ +static noinline_for_stack int do_remount_del_option( + char *optarg, int cur_branches, + struct unionfs_data *new_data, + struct path *new_lower_paths) +{ + int err = -EINVAL; + int idx; + struct nameidata nd; + + /* optarg contains the branch name to delete */ + + /* + * Find matching branch index. For now, this assumes that nothing + * has been mounted on top of this Unionfs stack. Once we have /odf + * and cache-coherency resolved, we'll address the branch-path + * uniqueness. + */ + err = path_lookup(optarg, LOOKUP_FOLLOW, &nd); + if (err) { + printk(KERN_ERR "unionfs: error accessing " + "lower directory \"%s\" (error %d)\n", + optarg, err); + goto out; + } + for (idx = 0; idx < cur_branches; idx++) + if (nd.mnt == new_lower_paths[idx].mnt && + nd.dentry == new_lower_paths[idx].dentry) + break; + path_release(&nd); /* no longer needed */ + if (idx == cur_branches) { + printk(KERN_ERR "unionfs: branch \"%s\" " + "not found\n", optarg); + err = -ENOENT; + goto out; + } + /* check if there are any open files on the branch to be deleted */ + if (atomic_read(&new_data[idx].open_files) > 0) { + err = -EBUSY; + goto out; + } + + /* + * Now we have to delete the branch. First, release any handles it + * has. Then, move the remaining array indexes past "idx" in + * new_data and new_lower_paths one to the left. Finally, adjust + * cur_branches. + */ + pathput(&new_lower_paths[idx]); + + if (idx < cur_branches - 1) { + /* if idx==cur_branches-1, we delete last branch: easy */ + memmove(&new_data[idx], &new_data[idx+1], + (cur_branches - 1 - idx) * + sizeof(struct unionfs_data)); + memmove(&new_lower_paths[idx], &new_lower_paths[idx+1], + (cur_branches - 1 - idx) * sizeof(struct path)); + } + + err = 0; +out: + return err; +} + +/* handle branch insertion during remount */ +static noinline_for_stack int do_remount_add_option( + char *optarg, int cur_branches, + struct unionfs_data *new_data, + struct path *new_lower_paths, + int *high_branch_id) +{ + int err = -EINVAL; + int perms; + int idx = 0; /* default: insert at beginning */ + char *new_branch , *modename = NULL; + struct nameidata nd; + + /* + * optarg can be of several forms: + * + * /bar:/foo insert /foo before /bar + * /bar:/foo=ro insert /foo in ro mode before /bar + * /foo insert /foo in the beginning (prepend) + * :/foo insert /foo at the end (append) + */ + if (*optarg == ':') { /* append? */ + new_branch = optarg + 1; /* skip ':' */ + idx = cur_branches; + goto found_insertion_point; + } + new_branch = strchr(optarg, ':'); + if (!new_branch) { /* prepend? */ + new_branch = optarg; + goto found_insertion_point; + } + *new_branch++ = '\0'; /* holds path+mode of new branch */ + + /* + * Find matching branch index. For now, this assumes that nothing + * has been mounted on top of this Unionfs stack. Once we have /odf + * and cache-coherency resolved, we'll address the branch-path + * uniqueness. + */ + err = path_lookup(optarg, LOOKUP_FOLLOW, &nd); + if (err) { + printk(KERN_ERR "unionfs: error accessing " + "lower directory \"%s\" (error %d)\n", + optarg, err); + goto out; + } + for (idx = 0; idx < cur_branches; idx++) + if (nd.mnt == new_lower_paths[idx].mnt && + nd.dentry == new_lower_paths[idx].dentry) + break; + path_release(&nd); /* no longer needed */ + if (idx == cur_branches) { + printk(KERN_ERR "unionfs: branch \"%s\" " + "not found\n", optarg); + err = -ENOENT; + goto out; + } + + /* + * At this point idx will hold the index where the new branch should + * be inserted before. + */ +found_insertion_point: + /* find the mode for the new branch */ + if (new_branch) + modename = strchr(new_branch, '='); + if (modename) + *modename++ = '\0'; + if (!new_branch || !*new_branch) { + printk(KERN_ERR "unionfs: null new branch\n"); + err = -EINVAL; + goto out; + } + err = parse_branch_mode(modename, &perms); + if (err) { + printk(KERN_ERR "unionfs: invalid mode \"%s\" for " + "branch \"%s\"\n", modename, new_branch); + goto out; + } + err = path_lookup(new_branch, LOOKUP_FOLLOW, &nd); + if (err) { + printk(KERN_ERR "unionfs: error accessing " + "lower directory \"%s\" (error %d)\n", + new_branch, err); + goto out; + } + /* + * It's probably safe to check_mode the new branch to insert. Note: + * we don't allow inserting branches which are unionfs's by + * themselves (check_branch returns EINVAL in that case). This is + * because this code base doesn't support stacking unionfs: the ODF + * code base supports that correctly. + */ + err = check_branch(&nd); + if (err) { + printk(KERN_ERR "unionfs: lower directory " + "\"%s\" is not a valid branch\n", optarg); + path_release(&nd); + goto out; + } + + /* + * Now we have to insert the new branch. But first, move the bits + * to make space for the new branch, if needed. Finally, adjust + * cur_branches. + * We don't release nd here; it's kept until umount/remount. + */ + if (idx < cur_branches) { + /* if idx==cur_branches, we append: easy */ + memmove(&new_data[idx+1], &new_data[idx], + (cur_branches - idx) * sizeof(struct unionfs_data)); + memmove(&new_lower_paths[idx+1], &new_lower_paths[idx], + (cur_branches - idx) * sizeof(struct path)); + } + new_lower_paths[idx].dentry = nd.dentry; + new_lower_paths[idx].mnt = nd.mnt; + + new_data[idx].sb = nd.dentry->d_sb; + atomic_set(&new_data[idx].open_files, 0); + new_data[idx].branchperms = perms; + new_data[idx].branch_id = ++*high_branch_id; /* assign new branch ID */ + + err = 0; +out: + return err; +} + + +/* + * Support branch management options on remount. + * + * See Documentation/filesystems/unionfs/ for details. + * + * @flags: numeric mount options + * @options: mount options string + * + * This function can rearrange a mounted union dynamically, adding and + * removing branches, including changing branch modes. Clearly this has to + * be done safely and atomically. Luckily, the VFS already calls this + * function with lock_super(sb) and lock_kernel() held, preventing + * concurrent mixing of new mounts, remounts, and unmounts. Moreover, + * do_remount_sb(), our caller function, already called shrink_dcache_sb(sb) + * to purge dentries/inodes from our superblock, and also called + * fsync_super(sb) to purge any dirty pages. So we're good. + * + * XXX: however, our remount code may also need to invalidate mapped pages + * so as to force them to be re-gotten from the (newly reconfigured) lower + * branches. This has to wait for proper mmap and cache coherency support + * in the VFS. + * + */ +static int unionfs_remount_fs(struct super_block *sb, int *flags, + char *options) +{ + int err = 0; + int i; + char *optionstmp, *tmp_to_free; /* kstrdup'ed of "options" */ + char *optname; + int cur_branches = 0; /* no. of current branches */ + int new_branches = 0; /* no. of branches actually left in the end */ + int add_branches; /* est. no. of branches to add */ + int del_branches; /* est. no. of branches to del */ + int max_branches; /* max possible no. of branches */ + struct unionfs_data *new_data = NULL, *tmp_data = NULL; + struct path *new_lower_paths = NULL, *tmp_lower_paths = NULL; + struct inode **new_lower_inodes = NULL; + int new_high_branch_id; /* new high branch ID */ + int size; /* memory allocation size, temp var */ + int old_ibstart, old_ibend; + + unionfs_write_lock(sb); + + /* + * The VFS will take care of "ro" and "rw" flags, and we can safely + * ignore MS_SILENT, but anything else left over is an error. So we + * need to check if any other flags may have been passed (none are + * allowed/supported as of now). + */ + if ((*flags & ~(MS_RDONLY | MS_SILENT)) != 0) { + printk(KERN_ERR + "unionfs: remount flags 0x%x unsupported\n", *flags); + err = -EINVAL; + goto out_error; + } + + /* + * If 'options' is NULL, it's probably because the user just changed + * the union to a "ro" or "rw" and the VFS took care of it. So + * nothing to do and we're done. + */ + if (!options || options[0] == '\0') + goto out_error; + + /* + * Find out how many branches we will have in the end, counting + * "add" and "del" commands. Copy the "options" string because + * strsep modifies the string and we need it later. + */ + tmp_to_free = kstrdup(options, GFP_KERNEL); + optionstmp = tmp_to_free; + if (unlikely(!optionstmp)) { + err = -ENOMEM; + goto out_free; + } + cur_branches = sbmax(sb); /* current no. branches */ + new_branches = sbmax(sb); + del_branches = 0; + add_branches = 0; + new_high_branch_id = sbhbid(sb); /* save current high_branch_id */ + while ((optname = strsep(&optionstmp, ",")) != NULL) { + char *optarg; + + if (!optname || !*optname) + continue; + + optarg = strchr(optname, '='); + if (optarg) + *optarg++ = '\0'; + + if (!strcmp("add", optname)) + add_branches++; + else if (!strcmp("del", optname)) + del_branches++; + } + kfree(tmp_to_free); + /* after all changes, will we have at least one branch left? */ + if ((new_branches + add_branches - del_branches) < 1) { + printk(KERN_ERR + "unionfs: no branches left after remount\n"); + err = -EINVAL; + goto out_free; + } + + /* + * Since we haven't actually parsed all the add/del options, nor + * have we checked them for errors, we don't know for sure how many + * branches we will have after all changes have taken place. In + * fact, the total number of branches left could be less than what + * we have now. So we need to allocate space for a temporary + * placeholder that is at least as large as the maximum number of + * branches we *could* have, which is the current number plus all + * the additions. Once we're done with these temp placeholders, we + * may have to re-allocate the final size, copy over from the temp, + * and then free the temps (done near the end of this function). + */ + max_branches = cur_branches + add_branches; + /* allocate space for new pointers to lower dentry */ + tmp_data = kcalloc(max_branches, + sizeof(struct unionfs_data), GFP_KERNEL); + if (unlikely(!tmp_data)) { + err = -ENOMEM; + goto out_free; + } + /* allocate space for new pointers to lower paths */ + tmp_lower_paths = kcalloc(max_branches, + sizeof(struct path), GFP_KERNEL); + if (unlikely(!tmp_lower_paths)) { + err = -ENOMEM; + goto out_free; + } + /* copy current info into new placeholders, incrementing refcnts */ + memcpy(tmp_data, UNIONFS_SB(sb)->data, + cur_branches * sizeof(struct unionfs_data)); + memcpy(tmp_lower_paths, UNIONFS_D(sb->s_root)->lower_paths, + cur_branches * sizeof(struct path)); + for (i = 0; i < cur_branches; i++) + pathget(&tmp_lower_paths[i]); /* drop refs at end of fxn */ + + /******************************************************************* + * For each branch command, do path_lookup on the requested branch, + * and apply the change to a temp branch list. To handle errors, we + * already dup'ed the old arrays (above), and increased the refcnts + * on various f/s objects. So now we can do all the path_lookups + * and branch-management commands on the new arrays. If it fail mid + * way, we free the tmp arrays and *put all objects. If we succeed, + * then we free old arrays and *put its objects, and then replace + * the arrays with the new tmp list (we may have to re-allocate the + * memory because the temp lists could have been larger than what we + * actually needed). + *******************************************************************/ + + while ((optname = strsep(&options, ",")) != NULL) { + char *optarg; + + if (!optname || !*optname) + continue; + /* + * At this stage optname holds a comma-delimited option, but + * without the commas. Next, we need to break the string on + * the '=' symbol to separate CMD=ARG, where ARG itself can + * be KEY=VAL. For example, in mode=/foo=rw, CMD is "mode", + * KEY is "/foo", and VAL is "rw". + */ + optarg = strchr(optname, '='); + if (optarg) + *optarg++ = '\0'; + /* incgen remount option (instead of old ioctl) */ + if (!strcmp("incgen", optname)) { + err = 0; + goto out_no_change; + } + + /* + * All of our options take an argument now. (Insert ones + * that don't above this check.) So at this stage optname + * contains the CMD part and optarg contains the ARG part. + */ + if (!optarg || !*optarg) { + printk(KERN_ERR "unionfs: all remount options require " + "an argument (%s)\n", optname); + err = -EINVAL; + goto out_release; + } + + if (!strcmp("add", optname)) { + err = do_remount_add_option(optarg, new_branches, + tmp_data, + tmp_lower_paths, + &new_high_branch_id); + if (err) + goto out_release; + new_branches++; + if (new_branches > UNIONFS_MAX_BRANCHES) { + printk(KERN_ERR "unionfs: command exceeds " + "%d branches\n", UNIONFS_MAX_BRANCHES); + err = -E2BIG; + goto out_release; + } + continue; + } + if (!strcmp("del", optname)) { + err = do_remount_del_option(optarg, new_branches, + tmp_data, + tmp_lower_paths); + if (err) + goto out_release; + new_branches--; + continue; + } + if (!strcmp("mode", optname)) { + err = do_remount_mode_option(optarg, new_branches, + tmp_data, + tmp_lower_paths); + if (err) + goto out_release; + continue; + } + + /* + * When you use "mount -o remount,ro", mount(8) will + * reportedly pass the original dirs= string from + * /proc/mounts. So for now, we have to ignore dirs= and + * not consider it an error, unless we want to allow users + * to pass dirs= in remount. Note that to allow the VFS to + * actually process the ro/rw remount options, we have to + * return 0 from this function. + */ + if (!strcmp("dirs", optname)) { + printk(KERN_WARNING + "unionfs: remount ignoring option \"%s\"\n", + optname); + continue; + } + + err = -EINVAL; + printk(KERN_ERR + "unionfs: unrecognized option \"%s\"\n", optname); + goto out_release; + } + +out_no_change: + + /****************************************************************** + * WE'RE ALMOST DONE: check if leftmost branch might be read-only, + * see if we need to allocate a small-sized new vector, copy the + * vectors to their correct place, release the refcnt of the older + * ones, and return. Also handle invalidating any pages that will + * have to be re-read. + *******************************************************************/ + + if (!(tmp_data[0].branchperms & MAY_WRITE)) { + printk(KERN_ERR "unionfs: leftmost branch cannot be read-only " + "(use \"remount,ro\" to create a read-only union)\n"); + err = -EINVAL; + goto out_release; + } + + /* (re)allocate space for new pointers to lower dentry */ + size = new_branches * sizeof(struct unionfs_data); + new_data = krealloc(tmp_data, size, GFP_KERNEL); + if (unlikely(!new_data)) { + err = -ENOMEM; + goto out_release; + } + + /* allocate space for new pointers to lower paths */ + size = new_branches * sizeof(struct path); + new_lower_paths = krealloc(tmp_lower_paths, size, GFP_KERNEL); + if (unlikely(!new_lower_paths)) { + err = -ENOMEM; + goto out_release; + } + + /* allocate space for new pointers to lower inodes */ + new_lower_inodes = kcalloc(new_branches, + sizeof(struct inode *), GFP_KERNEL); + if (unlikely(!new_lower_inodes)) { + err = -ENOMEM; + goto out_release; + } + + /* + * OK, just before we actually put the new set of branches in place, + * we need to ensure that our own f/s has no dirty objects left. + * Luckily, do_remount_sb() already calls shrink_dcache_sb(sb) and + * fsync_super(sb), taking care of dentries, inodes, and dirty + * pages. So all that's left is for us to invalidate any leftover + * (non-dirty) pages to ensure that they will be re-read from the + * new lower branches (and to support mmap). + */ + + /* + * Once we finish the remounting successfully, our superblock + * generation number will have increased. This will be detected by + * our dentry-revalidation code upon subsequent f/s operations + * through unionfs. The revalidation code will rebuild the union of + * lower inodes for a given unionfs inode and invalidate any pages + * of such "stale" inodes (by calling our purge_inode_data + * function). This revalidation will happen lazily and + * incrementally, as users perform operations on cached inodes. We + * would like to encourage this revalidation to happen sooner if + * possible, so we like to try to invalidate as many other pages in + * our superblock as we can. We used to call drop_pagecache_sb() or + * a variant thereof, but either method was racy (drop_caches alone + * is known to be racy). So now we let the revalidation happen on a + * per file basis in ->d_revalidate. + */ + + /* grab new lower super references; release old ones */ + for (i = 0; i < new_branches; i++) + atomic_inc(&new_data[i].sb->s_active); + for (i = 0; i < sbmax(sb); i++) + atomic_dec(&UNIONFS_SB(sb)->data[i].sb->s_active); + + /* copy new vectors into their correct place */ + tmp_data = UNIONFS_SB(sb)->data; + UNIONFS_SB(sb)->data = new_data; + new_data = NULL; /* so don't free good pointers below */ + tmp_lower_paths = UNIONFS_D(sb->s_root)->lower_paths; + UNIONFS_D(sb->s_root)->lower_paths = new_lower_paths; + new_lower_paths = NULL; /* so don't free good pointers below */ + + /* update our unionfs_sb_info and root dentry index of last branch */ + i = sbmax(sb); /* save no. of branches to release at end */ + sbend(sb) = new_branches - 1; + dbend(sb->s_root) = new_branches - 1; + old_ibstart = ibstart(sb->s_root->d_inode); + old_ibend = ibend(sb->s_root->d_inode); + ibend(sb->s_root->d_inode) = new_branches - 1; + UNIONFS_D(sb->s_root)->bcount = new_branches; + new_branches = i; /* no. of branches to release below */ + + /* + * Update lower inodes: 3 steps + * 1. grab ref on all new lower inodes + */ + for (i = dbstart(sb->s_root); i <= dbend(sb->s_root); i++) { + struct dentry *lower_dentry = + unionfs_lower_dentry_idx(sb->s_root, i); + igrab(lower_dentry->d_inode); + new_lower_inodes[i] = lower_dentry->d_inode; + } + /* 2. release reference on all older lower inodes */ + iput_lowers(sb->s_root->d_inode, old_ibstart, old_ibend, true); + /* 3. update root dentry's inode to new lower_inodes array */ + UNIONFS_I(sb->s_root->d_inode)->lower_inodes = new_lower_inodes; + new_lower_inodes = NULL; + + /* maxbytes may have changed */ + sb->s_maxbytes = unionfs_lower_super_idx(sb, 0)->s_maxbytes; + /* update high branch ID */ + sbhbid(sb) = new_high_branch_id; + + /* update our sb->generation for revalidating objects */ + i = atomic_inc_return(&UNIONFS_SB(sb)->generation); + atomic_set(&UNIONFS_D(sb->s_root)->generation, i); + atomic_set(&UNIONFS_I(sb->s_root->d_inode)->generation, i); + if (!(*flags & MS_SILENT)) + pr_info("unionfs: %s: new generation number %d\n", + UNIONFS_SB(sb)->dev_name, i); + /* finally, update the root dentry's times */ + unionfs_copy_attr_times(sb->s_root->d_inode); + err = 0; /* reset to success */ + + /* + * The code above falls through to the next label, and releases the + * refcnts of the older ones (stored in tmp_*): if we fell through + * here, it means success. However, if we jump directly to this + * label from any error above, then an error occurred after we + * grabbed various refcnts, and so we have to release the + * temporarily constructed structures. + */ +out_release: + /* no need to cleanup/release anything in tmp_data */ + if (tmp_lower_paths) + for (i = 0; i < new_branches; i++) + pathput(&tmp_lower_paths[i]); +out_free: + kfree(tmp_lower_paths); + kfree(tmp_data); + kfree(new_lower_paths); + kfree(new_data); + kfree(new_lower_inodes); +out_error: + unionfs_check_dentry(sb->s_root); + unionfs_write_unlock(sb); + return err; +} + +/* + * 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. + * + * No need to lock sb info's rwsem. + */ +static void unionfs_clear_inode(struct inode *inode) +{ + int bindex, bstart, bend; + struct inode *lower_inode; + struct list_head *pos, *n; + struct unionfs_dir_state *rdstate; + + list_for_each_safe(pos, n, &UNIONFS_I(inode)->readdircache) { + rdstate = list_entry(pos, struct unionfs_dir_state, cache); + list_del(&rdstate->cache); + free_rdstate(rdstate); + } + + /* + * Decrement a reference to a lower_inode, which was incremented + * by our read_inode when it was created initially. + */ + bstart = ibstart(inode); + bend = ibend(inode); + if (bstart >= 0) { + for (bindex = bstart; bindex <= bend; bindex++) { + lower_inode = unionfs_lower_inode_idx(inode, bindex); + if (!lower_inode) + continue; + unionfs_set_lower_inode_idx(inode, bindex, NULL); + /* see Documentation/filesystems/unionfs/issues.txt */ + lockdep_off(); + iput(lower_inode); + lockdep_on(); + } + } + + kfree(UNIONFS_I(inode)->lower_inodes); + UNIONFS_I(inode)->lower_inodes = NULL; +} + +static struct inode *unionfs_alloc_inode(struct super_block *sb) +{ + struct unionfs_inode_info *i; + + i = kmem_cache_alloc(unionfs_inode_cachep, GFP_KERNEL); + if (unlikely(!i)) + return NULL; + + /* memset everything up to the inode to 0 */ + memset(i, 0, offsetof(struct unionfs_inode_info, vfs_inode)); + + i->vfs_inode.i_version = 1; + return &i->vfs_inode; +} + +static void unionfs_destroy_inode(struct inode *inode) +{ + kmem_cache_free(unionfs_inode_cachep, UNIONFS_I(inode)); +} + +/* unionfs inode cache constructor */ +static void init_once(void *v, struct kmem_cache *cachep, unsigned long flags) +{ + struct unionfs_inode_info *i = v; + + inode_init_once(&i->vfs_inode); +} + +int unionfs_init_inode_cache(void) +{ + int err = 0; + + unionfs_inode_cachep = + kmem_cache_create("unionfs_inode_cache", + sizeof(struct unionfs_inode_info), 0, + SLAB_RECLAIM_ACCOUNT, init_once, NULL); + if (unlikely(!unionfs_inode_cachep)) + err = -ENOMEM; + return err; +} + +/* unionfs inode cache destructor */ +void unionfs_destroy_inode_cache(void) +{ + if (unionfs_inode_cachep) + kmem_cache_destroy(unionfs_inode_cachep); +} + +/* + * Called when we have a dirty inode, right here we only throw out + * parts of our readdir list that are too old. + * + * No need to grab sb info's rwsem. + */ +static int unionfs_write_inode(struct inode *inode, int sync) +{ + struct list_head *pos, *n; + struct unionfs_dir_state *rdstate; + + spin_lock(&UNIONFS_I(inode)->rdlock); + list_for_each_safe(pos, n, &UNIONFS_I(inode)->readdircache) { + rdstate = list_entry(pos, struct unionfs_dir_state, cache); + /* We keep this list in LRU order. */ + if ((rdstate->access + RDCACHE_JIFFIES) > jiffies) + break; + UNIONFS_I(inode)->rdcount--; + list_del(&rdstate->cache); + free_rdstate(rdstate); + } + spin_unlock(&UNIONFS_I(inode)->rdlock); + + return 0; +} + +/* + * 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. + */ +static void unionfs_umount_begin(struct vfsmount *mnt, int flags) +{ + struct super_block *sb, *lower_sb; + struct vfsmount *lower_mnt; + int bindex, bstart, bend; + + if (!(flags & MNT_FORCE)) + /* + * we are not being MNT_FORCE'd, therefore we should emulate + * old behavior + */ + return; + + sb = mnt->mnt_sb; + + unionfs_read_lock(sb, UNIONFS_SMUTEX_CHILD); + + bstart = sbstart(sb); + bend = sbend(sb); + for (bindex = bstart; bindex <= bend; bindex++) { + lower_mnt = unionfs_lower_mnt_idx(sb->s_root, bindex); + lower_sb = unionfs_lower_super_idx(sb, bindex); + + if (lower_mnt && lower_sb && lower_sb->s_op && + lower_sb->s_op->umount_begin) + lower_sb->s_op->umount_begin(lower_mnt, flags); + } + + unionfs_read_unlock(sb); +} + +static int unionfs_show_options(struct seq_file *m, struct vfsmount *mnt) +{ + struct super_block *sb = mnt->mnt_sb; + int ret = 0; + char *tmp_page; + char *path; + int bindex, bstart, bend; + int perms; + + unionfs_read_lock(sb, UNIONFS_SMUTEX_CHILD); + + unionfs_lock_dentry(sb->s_root, UNIONFS_DMUTEX_CHILD); + + tmp_page = (char *) __get_free_page(GFP_KERNEL); + if (unlikely(!tmp_page)) { + ret = -ENOMEM; + goto out; + } + + bstart = sbstart(sb); + bend = sbend(sb); + + seq_printf(m, ",dirs="); + for (bindex = bstart; bindex <= bend; bindex++) { + path = d_path(unionfs_lower_dentry_idx(sb->s_root, bindex), + unionfs_lower_mnt_idx(sb->s_root, bindex), + tmp_page, PAGE_SIZE); + if (IS_ERR(path)) { + ret = PTR_ERR(path); + goto out; + } + + perms = branchperms(sb, bindex); + + seq_printf(m, "%s=%s", path, + perms & MAY_WRITE ? "rw" : "ro"); + if (bindex != bend) + seq_printf(m, ":"); + } + +out: + free_page((unsigned long) tmp_page); + + unionfs_unlock_dentry(sb->s_root); + + unionfs_read_unlock(sb); + + return ret; +} + +struct super_operations unionfs_sops = { + .read_inode = unionfs_read_inode, + .delete_inode = unionfs_delete_inode, + .put_super = unionfs_put_super, + .statfs = unionfs_statfs, + .remount_fs = unionfs_remount_fs, + .clear_inode = unionfs_clear_inode, + .umount_begin = unionfs_umount_begin, + .show_options = unionfs_show_options, + .write_inode = unionfs_write_inode, + .alloc_inode = unionfs_alloc_inode, + .destroy_inode = unionfs_destroy_inode, +}; diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/union.h kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/union.h --- kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/union.h 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/union.h 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,646 @@ +/* + * Copyright (c) 2003-2009 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2007 Josef 'Jeff' Sipek + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2004-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2009 Stony Brook University + * Copyright (c) 2003-2009 The Research Foundation of SUNY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _UNION_H_ +#define _UNION_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +/* the file system name */ +#define UNIONFS_NAME "unionfs" + +/* unionfs root inode number */ +#define UNIONFS_ROOT_INO 1 + +/* number of times we try to get a unique temporary file name */ +#define GET_TMPNAM_MAX_RETRY 5 + +/* maximum number of branches we support, to avoid memory blowup */ +#define UNIONFS_MAX_BRANCHES 128 + +/* minimum time (seconds) required for time-based cache-coherency */ +#define UNIONFS_MIN_CC_TIME 3 + +/* Operations vectors defined in specific files. */ +extern struct file_operations unionfs_main_fops; +extern struct file_operations unionfs_dir_fops; +extern struct inode_operations unionfs_main_iops; +extern struct inode_operations unionfs_dir_iops; +extern struct inode_operations unionfs_symlink_iops; +extern struct super_operations unionfs_sops; +extern struct dentry_operations unionfs_dops; +extern struct address_space_operations unionfs_aops, unionfs_dummy_aops; +extern struct vm_operations_struct unionfs_vm_ops; + +/* How long should an entry be allowed to persist */ +#define RDCACHE_JIFFIES (5*HZ) + +/* compatibility with Real-Time patches */ +#ifdef CONFIG_PREEMPT_RT +# define unionfs_rw_semaphore compat_rw_semaphore +#else /* not CONFIG_PREEMPT_RT */ +# define unionfs_rw_semaphore rw_semaphore +#endif /* not CONFIG_PREEMPT_RT */ + +#ifndef noinline_for_stack +# define noinline_for_stack noinline +#endif /* not noinline_for_stack */ + +/* file private data. */ +struct unionfs_file_info { + int bstart; + int bend; + atomic_t generation; + + struct unionfs_dir_state *rdstate; + struct file **lower_files; + int *saved_branch_ids; /* IDs of branches when file was opened */ + struct vm_operations_struct *lower_vm_ops; + bool wrote_to_file; /* for delayed copyup */ +}; + +/* unionfs inode data in memory */ +struct unionfs_inode_info { + int bstart; + int bend; + atomic_t generation; + /* Stuff for readdir over NFS. */ + spinlock_t rdlock; + struct list_head readdircache; + int rdcount; + int hashsize; + int cookie; + + /* The lower inodes */ + struct inode **lower_inodes; + + struct inode vfs_inode; +}; + +/* unionfs dentry data in memory */ +struct unionfs_dentry_info { + /* + * The semaphore is used to lock the dentry as soon as we get into a + * unionfs function from the VFS. Our lock ordering is that children + * go before their parents. + */ + struct mutex lock; + int bstart; + int bend; + int bopaque; + int bcount; + atomic_t generation; + struct path *lower_paths; +}; + +/* These are the pointers to our various objects. */ +struct unionfs_data { + struct super_block *sb; /* lower super_block */ + atomic_t open_files; /* number of open files on branch */ + int branchperms; + int branch_id; /* unique branch ID at re/mount time */ +}; + +/* unionfs super-block data in memory */ +struct unionfs_sb_info { + int bend; + + atomic_t generation; + + /* + * This rwsem is used to make sure that a branch management + * operation... + * 1) will not begin before all currently in-flight operations + * complete. + * 2) any new operations do not execute until the currently + * running branch management operation completes. + * + * The write_lock_owner records the PID of the task which grabbed + * the rw_sem for writing. If the same task also tries to grab the + * read lock, we allow it. This prevents a self-deadlock when + * branch-management is used on a pivot_root'ed union, because we + * have to ->lookup paths which belong to the same union. + */ + struct unionfs_rw_semaphore rwsem; + pid_t write_lock_owner; /* PID of rw_sem owner (write lock) */ + int high_branch_id; /* last unique branch ID given */ + char *dev_name; /* to identify different unions in pr_debug */ + struct unionfs_data *data; +}; + +/* + * structure for making the linked list of entries by readdir on left branch + * to compare with entries on right branch + */ +struct filldir_node { + struct list_head file_list; /* list for directory entries */ + char *name; /* name entry */ + int hash; /* name hash */ + int namelen; /* name len since name is not 0 terminated */ + + /* + * we can check for duplicate whiteouts and files in the same branch + * in order to return -EIO. + */ + int bindex; + + /* is this a whiteout entry? */ + int whiteout; + + /* Inline name, so we don't need to separately kmalloc small ones */ + char iname[DNAME_INLINE_LEN_MIN]; +}; + +/* Directory hash table. */ +struct unionfs_dir_state { + unsigned int cookie; /* the cookie, based off of rdversion */ + unsigned int offset; /* The entry we have returned. */ + int bindex; + loff_t dirpos; /* offset within the lower level directory */ + int size; /* How big is the hash table? */ + int hashentries; /* How many entries have been inserted? */ + unsigned long access; + + /* This cache list is used when the inode keeps us around. */ + struct list_head cache; + struct list_head list[0]; +}; + +/* externs needed for fanout.h or sioq.h */ +extern int unionfs_get_nlinks(const struct inode *inode); +extern void unionfs_copy_attr_times(struct inode *upper); +extern void unionfs_copy_attr_all(struct inode *dest, const struct inode *src); + +/* include miscellaneous macros */ +#include "fanout.h" +#include "sioq.h" + +/* externs for cache creation/deletion routines */ +extern void unionfs_destroy_filldir_cache(void); +extern int unionfs_init_filldir_cache(void); +extern int unionfs_init_inode_cache(void); +extern void unionfs_destroy_inode_cache(void); +extern int unionfs_init_dentry_cache(void); +extern void unionfs_destroy_dentry_cache(void); + +/* Initialize and free readdir-specific state. */ +extern int init_rdstate(struct file *file); +extern struct unionfs_dir_state *alloc_rdstate(struct inode *inode, + int bindex); +extern struct unionfs_dir_state *find_rdstate(struct inode *inode, + loff_t fpos); +extern void free_rdstate(struct unionfs_dir_state *state); +extern int add_filldir_node(struct unionfs_dir_state *rdstate, + const char *name, int namelen, int bindex, + int whiteout); +extern struct filldir_node *find_filldir_node(struct unionfs_dir_state *rdstate, + const char *name, int namelen, + int is_whiteout); + +extern struct dentry **alloc_new_dentries(int objs); +extern struct unionfs_data *alloc_new_data(int objs); + +/* We can only use 32-bits of offset for rdstate --- blech! */ +#define DIREOF (0xfffff) +#define RDOFFBITS 20 /* This is the number of bits in DIREOF. */ +#define MAXRDCOOKIE (0xfff) +/* Turn an rdstate into an offset. */ +static inline off_t rdstate2offset(struct unionfs_dir_state *buf) +{ + off_t tmp; + + tmp = ((buf->cookie & MAXRDCOOKIE) << RDOFFBITS) + | (buf->offset & DIREOF); + return tmp; +} + +/* Macros for locking a super_block. */ +enum unionfs_super_lock_class { + UNIONFS_SMUTEX_NORMAL, + UNIONFS_SMUTEX_PARENT, /* when locking on behalf of file */ + UNIONFS_SMUTEX_CHILD, /* when locking on behalf of dentry */ +}; +static inline void unionfs_read_lock(struct super_block *sb, int subclass) +{ + if (UNIONFS_SB(sb)->write_lock_owner && + UNIONFS_SB(sb)->write_lock_owner == current->pid) + return; + down_read_nested(&UNIONFS_SB(sb)->rwsem, subclass); +} +static inline void unionfs_read_unlock(struct super_block *sb) +{ + if (UNIONFS_SB(sb)->write_lock_owner && + UNIONFS_SB(sb)->write_lock_owner == current->pid) + return; + up_read(&UNIONFS_SB(sb)->rwsem); +} +static inline void unionfs_write_lock(struct super_block *sb) +{ + down_write(&UNIONFS_SB(sb)->rwsem); + UNIONFS_SB(sb)->write_lock_owner = current->pid; +} +static inline void unionfs_write_unlock(struct super_block *sb) +{ + up_write(&UNIONFS_SB(sb)->rwsem); + UNIONFS_SB(sb)->write_lock_owner = 0; +} + +static inline void unionfs_double_lock_dentry(struct dentry *d1, + struct dentry *d2) +{ + BUG_ON(d1 == d2); + if (d1 < d2) { + unionfs_lock_dentry(d1, UNIONFS_DMUTEX_PARENT); + unionfs_lock_dentry(d2, UNIONFS_DMUTEX_CHILD); + } else { + unionfs_lock_dentry(d2, UNIONFS_DMUTEX_PARENT); + unionfs_lock_dentry(d1, UNIONFS_DMUTEX_CHILD); + } +} + +static inline void unionfs_double_unlock_dentry(struct dentry *d1, + struct dentry *d2) +{ + BUG_ON(d1 == d2); + if (d1 < d2) { /* unlock in reverse order than double_lock_dentry */ + unionfs_unlock_dentry(d1); + unionfs_unlock_dentry(d2); + } else { + unionfs_unlock_dentry(d2); + unionfs_unlock_dentry(d1); + } +} + +static inline void unionfs_double_lock_parents(struct dentry *p1, + struct dentry *p2) +{ + if (p1 == p2) { + unionfs_lock_dentry(p1, UNIONFS_DMUTEX_REVAL_PARENT); + return; + } + if (p1 < p2) { + unionfs_lock_dentry(p1, UNIONFS_DMUTEX_REVAL_PARENT); + unionfs_lock_dentry(p2, UNIONFS_DMUTEX_REVAL_CHILD); + } else { + unionfs_lock_dentry(p2, UNIONFS_DMUTEX_REVAL_PARENT); + unionfs_lock_dentry(p1, UNIONFS_DMUTEX_REVAL_CHILD); + } +} + +static inline void unionfs_double_unlock_parents(struct dentry *p1, + struct dentry *p2) +{ + if (p1 == p2) { + unionfs_unlock_dentry(p1); + return; + } + if (p1 < p2) { /* unlock in reverse order of double_lock_parents */ + unionfs_unlock_dentry(p1); + unionfs_unlock_dentry(p2); + } else { + unionfs_unlock_dentry(p2); + unionfs_unlock_dentry(p1); + } +} + +extern int new_dentry_private_data(struct dentry *dentry, int subclass); +extern int realloc_dentry_private_data(struct dentry *dentry); +extern void free_dentry_private_data(struct dentry *dentry); +extern void update_bstart(struct dentry *dentry); + +/* + * EXTERNALS: + */ + +/* replicates the directory structure up to given dentry in given branch */ +extern struct dentry *create_parents(struct inode *dir, struct dentry *dentry, + const char *name, int bindex); + +/* partial lookup */ +extern int unionfs_partial_lookup(struct dentry *dentry, + struct dentry *parent); +extern struct dentry *unionfs_lookup_full(struct dentry *dentry, + struct dentry *parent, + int lookupmode); + +/* copies a file from dbstart to newbindex branch */ +extern int copyup_file(struct inode *dir, struct file *file, int bstart, + int newbindex, loff_t size); +extern int copyup_named_file(struct inode *dir, struct file *file, + char *name, int bstart, int new_bindex, + loff_t len); +/* copies a dentry from dbstart to newbindex branch */ +extern int copyup_dentry(struct inode *dir, struct dentry *dentry, + int bstart, int new_bindex, const char *name, + int namelen, struct file **copyup_file, loff_t len); +/* helper functions for post-copyup actions */ +extern void unionfs_postcopyup_setmnt(struct dentry *dentry); +extern void unionfs_postcopyup_release(struct dentry *dentry); + +/* Is this directory empty: 0 if it is empty, -ENOTEMPTY if not. */ +extern int check_empty(struct dentry *dentry, struct dentry *parent, + struct unionfs_dir_state **namelist); +/* whiteout and opaque directory helpers */ +extern char *alloc_whname(const char *name, int len); +extern bool is_whiteout_name(char **namep, int *namelenp); +extern bool is_validname(const char *name); +extern struct dentry *lookup_whiteout(const char *name, + struct dentry *lower_parent); +extern struct dentry *find_first_whiteout(struct dentry *dentry); +extern int unlink_whiteout(struct dentry *wh_dentry); +extern int check_unlink_whiteout(struct dentry *dentry, + struct dentry *lower_dentry, int bindex); +extern int create_whiteout(struct dentry *dentry, int start); +extern int delete_whiteouts(struct dentry *dentry, int bindex, + struct unionfs_dir_state *namelist); +extern int is_opaque_dir(struct dentry *dentry, int bindex); +extern int make_dir_opaque(struct dentry *dir, int bindex); +extern void unionfs_set_max_namelen(long *namelen); + +extern void unionfs_reinterpose(struct dentry *this_dentry); +extern struct super_block *unionfs_duplicate_super(struct super_block *sb); + +/* Locking functions. */ +extern int unionfs_setlk(struct file *file, int cmd, struct file_lock *fl); +extern int unionfs_getlk(struct file *file, struct file_lock *fl); + +/* Common file operations. */ +extern int unionfs_file_revalidate(struct file *file, struct dentry *parent, + bool willwrite); +extern int unionfs_open(struct inode *inode, struct file *file); +extern int unionfs_file_release(struct inode *inode, struct file *file); +extern int unionfs_flush(struct file *file, fl_owner_t id); +extern long unionfs_ioctl(struct file *file, unsigned int cmd, + unsigned long arg); +extern int unionfs_fsync(struct file *file, struct dentry *dentry, + int datasync); +extern int unionfs_fasync(int fd, struct file *file, int flag); + +/* Inode operations */ +extern int unionfs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry); +extern int unionfs_unlink(struct inode *dir, struct dentry *dentry); +extern int unionfs_rmdir(struct inode *dir, struct dentry *dentry); + +extern bool __unionfs_d_revalidate(struct dentry *dentry, + struct dentry *parent, bool willwrite); +extern bool is_negative_lower(const struct dentry *dentry); +extern bool is_newer_lower(const struct dentry *dentry); +extern void purge_sb_data(struct super_block *sb); + +/* The values for unionfs_interpose's flag. */ +#define INTERPOSE_DEFAULT 0 +#define INTERPOSE_LOOKUP 1 +#define INTERPOSE_REVAL 2 +#define INTERPOSE_REVAL_NEG 3 +#define INTERPOSE_PARTIAL 4 + +extern struct dentry *unionfs_interpose(struct dentry *this_dentry, + struct super_block *sb, int flag); + +#ifdef CONFIG_UNION_FS_XATTR +/* Extended attribute functions. */ +extern void *unionfs_xattr_alloc(size_t size, size_t limit); +static inline void unionfs_xattr_kfree(const void *p) +{ + kfree(p); +} +extern ssize_t unionfs_getxattr(struct dentry *dentry, const char *name, + void *value, size_t size); +extern int unionfs_removexattr(struct dentry *dentry, const char *name); +extern ssize_t unionfs_listxattr(struct dentry *dentry, char *list, + size_t size); +extern int unionfs_setxattr(struct dentry *dentry, const char *name, + const void *value, size_t size, int flags); +#endif /* CONFIG_UNION_FS_XATTR */ + +/* The root directory is unhashed, but isn't deleted. */ +static inline int d_deleted(struct dentry *d) +{ + return d_unhashed(d) && (d != d->d_sb->s_root); +} + +/* unionfs_permission, check if we should bypass error to facilitate copyup */ +#define IS_COPYUP_ERR(err) ((err) == -EROFS) + +/* unionfs_open, check if we need to copyup the file */ +#define OPEN_WRITE_FLAGS (O_WRONLY | O_RDWR | O_APPEND) +#define IS_WRITE_FLAG(flag) ((flag) & OPEN_WRITE_FLAGS) + +static inline int branchperms(const struct super_block *sb, int index) +{ + BUG_ON(index < 0); + return UNIONFS_SB(sb)->data[index].branchperms; +} + +static inline int set_branchperms(struct super_block *sb, int index, int perms) +{ + BUG_ON(index < 0); + UNIONFS_SB(sb)->data[index].branchperms = perms; + return perms; +} + +/* Is this file on a read-only branch? */ +static inline int is_robranch_super(const struct super_block *sb, int index) +{ + int ret; + + ret = (!(branchperms(sb, index) & MAY_WRITE)) ? -EROFS : 0; + return ret; +} + +/* Is this file on a read-only branch? */ +static inline int is_robranch_idx(const struct dentry *dentry, int index) +{ + struct super_block *lower_sb; + + BUG_ON(index < 0); + + if (!(branchperms(dentry->d_sb, index) & MAY_WRITE)) + return -EROFS; + + lower_sb = unionfs_lower_super_idx(dentry->d_sb, index); + BUG_ON(lower_sb == NULL); + /* + * test sb flags directly, not IS_RDONLY(lower_inode) because the + * lower_dentry could be a negative. + */ + if (lower_sb->s_flags & MS_RDONLY) + return -EROFS; + + return 0; +} + +static inline int is_robranch(const struct dentry *dentry) +{ + int index; + + index = UNIONFS_D(dentry)->bstart; + BUG_ON(index < 0); + + return is_robranch_idx(dentry, index); +} + +/* + * EXTERNALS: + */ +extern int check_branch(struct nameidata *nd); +extern int parse_branch_mode(const char *name, int *perms); + +/* locking helpers */ +static inline struct dentry *lock_parent(struct dentry *dentry) +{ + struct dentry *dir = dget_parent(dentry); + mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); + return dir; +} +static inline struct dentry *lock_parent_wh(struct dentry *dentry) +{ + struct dentry *dir = dget_parent(dentry); + + mutex_lock_nested(&dir->d_inode->i_mutex, UNIONFS_DMUTEX_WHITEOUT); + return dir; +} + +static inline void unlock_dir(struct dentry *dir) +{ + mutex_unlock(&dir->d_inode->i_mutex); + dput(dir); +} + +static inline struct vfsmount *unionfs_mntget(struct dentry *dentry, + int bindex) +{ + struct vfsmount *mnt; + + BUG_ON(!dentry || bindex < 0); + + mnt = mntget(unionfs_lower_mnt_idx(dentry, bindex)); +#ifdef CONFIG_UNION_FS_DEBUG + if (!mnt) + pr_debug("unionfs: mntget: mnt=%p bindex=%d\n", + mnt, bindex); +#endif /* CONFIG_UNION_FS_DEBUG */ + + return mnt; +} + +static inline void unionfs_mntput(struct dentry *dentry, int bindex) +{ + struct vfsmount *mnt; + + if (!dentry && bindex < 0) + return; + BUG_ON(!dentry || bindex < 0); + + mnt = unionfs_lower_mnt_idx(dentry, bindex); +#ifdef CONFIG_UNION_FS_DEBUG + /* + * Directories can have NULL lower objects in between start/end, but + * NOT if at the start/end range. We cannot verify that this dentry + * is a type=DIR, because it may already be a negative dentry. But + * if dbstart is greater than dbend, we know that this couldn't have + * been a regular file: it had to have been a directory. + */ + if (!mnt && !(bindex > dbstart(dentry) && bindex < dbend(dentry))) + pr_debug("unionfs: mntput: mnt=%p bindex=%d\n", mnt, bindex); +#endif /* CONFIG_UNION_FS_DEBUG */ + mntput(mnt); +} + +#ifdef CONFIG_UNION_FS_DEBUG + +/* useful for tracking code reachability */ +#define UDBG pr_debug("DBG:%s:%s:%d\n", __FILE__, __func__, __LINE__) + +#define unionfs_check_inode(i) __unionfs_check_inode((i), \ + __FILE__, __func__, __LINE__) +#define unionfs_check_dentry(d) __unionfs_check_dentry((d), \ + __FILE__, __func__, __LINE__) +#define unionfs_check_file(f) __unionfs_check_file((f), \ + __FILE__, __func__, __LINE__) +#define show_branch_counts(sb) __show_branch_counts((sb), \ + __FILE__, __func__, __LINE__) +#define show_inode_times(i) __show_inode_times((i), \ + __FILE__, __func__, __LINE__) +#define show_dinode_times(d) __show_dinode_times((d), \ + __FILE__, __func__, __LINE__) +#define show_inode_counts(i) __show_inode_counts((i), \ + __FILE__, __func__, __LINE__) + +extern void __unionfs_check_inode(const struct inode *inode, const char *fname, + const char *fxn, int line); +extern void __unionfs_check_dentry(const struct dentry *dentry, + const char *fname, const char *fxn, + int line); +extern void __unionfs_check_file(const struct file *file, + const char *fname, const char *fxn, int line); +extern void __show_branch_counts(const struct super_block *sb, + const char *file, const char *fxn, int line); +extern void __show_inode_times(const struct inode *inode, + const char *file, const char *fxn, int line); +extern void __show_dinode_times(const struct dentry *dentry, + const char *file, const char *fxn, int line); +extern void __show_inode_counts(const struct inode *inode, + const char *file, const char *fxn, int line); + +#else /* not CONFIG_UNION_FS_DEBUG */ + +/* we leave useful hooks for these check functions throughout the code */ +#define unionfs_check_inode(i) do { } while (0) +#define unionfs_check_dentry(d) do { } while (0) +#define unionfs_check_file(f) do { } while (0) +#define unionfs_check_nd(n) do { } while (0) +#define show_branch_counts(sb) do { } while (0) +#define show_inode_times(i) do { } while (0) +#define show_dinode_times(d) do { } while (0) +#define show_inode_counts(i) do { } while (0) + +#endif /* not CONFIG_UNION_FS_DEBUG */ + +#endif /* not _UNION_H_ */ diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/unlink.c kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/unlink.c --- kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/unlink.c 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/unlink.c 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2003-2009 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2007 Josef 'Jeff' Sipek + * Copyright (c) 2005-2006 Junjiro Okajima + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2004-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2009 Stony Brook University + * Copyright (c) 2003-2009 The Research Foundation of SUNY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "union.h" + +/* + * Helper function for Unionfs's unlink operation. + * + * The main goal of this function is to optimize the unlinking of non-dir + * objects in unionfs by deleting all possible lower inode objects from the + * underlying branches having same dentry name as the non-dir dentry on + * which this unlink operation is called. This way we delete as many lower + * inodes as possible, and save space. Whiteouts need to be created in + * branch0 only if unlinking fails on any of the lower branch other than + * branch0, or if a lower branch is marked read-only. + * + * Also, while unlinking a file, if we encounter any dir type entry in any + * intermediate branch, then we remove the directory by calling vfs_rmdir. + * The following special cases are also handled: + + * (1) If an error occurs in branch0 during vfs_unlink, then we return + * appropriate error. + * + * (2) If we get an error during unlink in any of other lower branch other + * than branch0, then we create a whiteout in branch0. + * + * (3) If a whiteout already exists in any intermediate branch, we delete + * all possible inodes only up to that branch (this is an "opaqueness" + * as as per Documentation/filesystems/unionfs/concepts.txt). + * + */ +static int unionfs_unlink_whiteout(struct inode *dir, struct dentry *dentry, + struct dentry *parent) +{ + struct dentry *lower_dentry; + struct dentry *lower_dir_dentry; + int bindex; + int err = 0; + + err = unionfs_partial_lookup(dentry, parent); + if (err) + goto out; + + /* trying to unlink all possible valid instances */ + for (bindex = dbstart(dentry); bindex <= dbend(dentry); bindex++) { + lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); + if (!lower_dentry || !lower_dentry->d_inode) + continue; + + lower_dir_dentry = lock_parent(lower_dentry); + + /* avoid destroying the lower inode if the object is in use */ + dget(lower_dentry); + err = is_robranch_super(dentry->d_sb, bindex); + if (!err) { + /* see Documentation/filesystems/unionfs/issues.txt */ + lockdep_off(); + if (!S_ISDIR(lower_dentry->d_inode->i_mode)) + err = vfs_unlink(lower_dir_dentry->d_inode, + lower_dentry); + else + err = vfs_rmdir(lower_dir_dentry->d_inode, + lower_dentry); + lockdep_on(); + } + + /* if lower object deletion succeeds, update inode's times */ + if (!err) + unionfs_copy_attr_times(dentry->d_inode); + dput(lower_dentry); + fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode); + unlock_dir(lower_dir_dentry); + + if (err) + break; + } + + /* + * Create the whiteout in branch 0 (highest priority) only if (a) + * there was an error in any intermediate branch other than branch 0 + * due to failure of vfs_unlink/vfs_rmdir or (b) a branch marked or + * mounted read-only. + */ + if (err) { + if ((bindex == 0) || + ((bindex == dbstart(dentry)) && + (!IS_COPYUP_ERR(err)))) + goto out; + else { + if (!IS_COPYUP_ERR(err)) + pr_debug("unionfs: lower object deletion " + "failed in branch:%d\n", bindex); + err = create_whiteout(dentry, sbstart(dentry->d_sb)); + } + } + +out: + if (!err) + inode_dec_link_count(dentry->d_inode); + + /* We don't want to leave negative leftover dentries for revalidate. */ + if (!err && (dbopaque(dentry) != -1)) + update_bstart(dentry); + + return err; +} + +int unionfs_unlink(struct inode *dir, struct dentry *dentry) +{ + int err = 0; + struct inode *inode = dentry->d_inode; + struct dentry *parent; + int valid; + + BUG_ON(S_ISDIR(inode->i_mode)); + unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); + parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); + unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); + + valid = __unionfs_d_revalidate(dentry, parent, false); + if (unlikely(!valid)) { + err = -ESTALE; + goto out; + } + unionfs_check_dentry(dentry); + + err = unionfs_unlink_whiteout(dir, dentry, parent); + /* call d_drop so the system "forgets" about us */ + if (!err) { + unionfs_postcopyup_release(dentry); + unionfs_postcopyup_setmnt(parent); + if (inode->i_nlink == 0) /* drop lower inodes */ + iput_lowers_all(inode, false); + d_drop(dentry); + /* + * if unlink/whiteout succeeded, parent dir mtime has + * changed + */ + unionfs_copy_attr_times(dir); + } + +out: + if (!err) { + unionfs_check_dentry(dentry); + unionfs_check_inode(dir); + } + unionfs_unlock_dentry(dentry); + unionfs_unlock_parent(dentry, parent); + unionfs_read_unlock(dentry->d_sb); + return err; +} + +static int unionfs_rmdir_first(struct inode *dir, struct dentry *dentry, + struct unionfs_dir_state *namelist) +{ + int err; + struct dentry *lower_dentry; + struct dentry *lower_dir_dentry = NULL; + + /* Here we need to remove whiteout entries. */ + err = delete_whiteouts(dentry, dbstart(dentry), namelist); + if (err) + goto out; + + lower_dentry = unionfs_lower_dentry(dentry); + + lower_dir_dentry = lock_parent(lower_dentry); + + /* avoid destroying the lower inode if the file is in use */ + dget(lower_dentry); + err = is_robranch(dentry); + if (!err) { + /* see Documentation/filesystems/unionfs/issues.txt */ + lockdep_off(); + err = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry); + lockdep_on(); + } + dput(lower_dentry); + + fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode); + /* propagate number of hard-links */ + dentry->d_inode->i_nlink = unionfs_get_nlinks(dentry->d_inode); + +out: + if (lower_dir_dentry) + unlock_dir(lower_dir_dentry); + return err; +} + +int unionfs_rmdir(struct inode *dir, struct dentry *dentry) +{ + int err = 0; + struct unionfs_dir_state *namelist = NULL; + struct dentry *parent; + int dstart, dend; + bool valid; + + unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); + parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); + unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); + + valid = __unionfs_d_revalidate(dentry, parent, false); + if (unlikely(!valid)) { + err = -ESTALE; + goto out; + } + unionfs_check_dentry(dentry); + + /* check if this unionfs directory is empty or not */ + err = check_empty(dentry, parent, &namelist); + if (err) + goto out; + + err = unionfs_rmdir_first(dir, dentry, namelist); + dstart = dbstart(dentry); + dend = dbend(dentry); + /* + * We create a whiteout for the directory if there was an error to + * rmdir the first directory entry in the union. Otherwise, we + * create a whiteout only if there is no chance that a lower + * priority branch might also have the same named directory. IOW, + * if there is not another same-named directory at a lower priority + * branch, then we don't need to create a whiteout for it. + */ + if (!err) { + if (dstart < dend) + err = create_whiteout(dentry, dstart); + } else { + int new_err; + + if (dstart == 0) + goto out; + + /* exit if the error returned was NOT -EROFS */ + if (!IS_COPYUP_ERR(err)) + goto out; + + new_err = create_whiteout(dentry, dstart - 1); + if (new_err != -EEXIST) + err = new_err; + } + +out: + /* + * Drop references to lower dentry/inode so storage space for them + * can be reclaimed. Then, call d_drop so the system "forgets" + * about us. + */ + if (!err) { + iput_lowers_all(dentry->d_inode, false); + dput(unionfs_lower_dentry_idx(dentry, dstart)); + unionfs_set_lower_dentry_idx(dentry, dstart, NULL); + d_drop(dentry); + /* update our lower vfsmnts, in case a copyup took place */ + unionfs_postcopyup_setmnt(dentry); + unionfs_check_dentry(dentry); + unionfs_check_inode(dir); + } + + if (namelist) + free_rdstate(namelist); + + unionfs_unlock_dentry(dentry); + unionfs_unlock_parent(dentry, parent); + unionfs_read_unlock(dentry->d_sb); + return err; +} diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/whiteout.c kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/whiteout.c --- kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/whiteout.c 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/whiteout.c 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,567 @@ +/* + * Copyright (c) 2003-2009 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2007 Josef 'Jeff' Sipek + * Copyright (c) 2005-2006 Junjiro Okajima + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2004-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2009 Stony Brook University + * Copyright (c) 2003-2009 The Research Foundation of SUNY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "union.h" + +/* + * whiteout and opaque directory helpers + */ + +/* What do we use for whiteouts. */ +#define UNIONFS_WHPFX ".wh." +#define UNIONFS_WHLEN 4 +/* + * If a directory contains this file, then it is opaque. We start with the + * .wh. flag so that it is blocked by lookup. + */ +#define UNIONFS_DIR_OPAQUE_NAME "__dir_opaque" +#define UNIONFS_DIR_OPAQUE UNIONFS_WHPFX UNIONFS_DIR_OPAQUE_NAME + +/* construct whiteout filename */ +char *alloc_whname(const char *name, int len) +{ + char *buf; + + buf = kmalloc(len + UNIONFS_WHLEN + 1, GFP_KERNEL); + if (unlikely(!buf)) + return ERR_PTR(-ENOMEM); + + strcpy(buf, UNIONFS_WHPFX); + strlcat(buf, name, len + UNIONFS_WHLEN + 1); + + return buf; +} + +/* + * XXX: this can be inline or CPP macro, but is here to keep all whiteout + * code in one place. + */ +void unionfs_set_max_namelen(long *namelen) +{ + *namelen -= UNIONFS_WHLEN; +} + +/* check if @namep is a whiteout, update @namep and @namelenp accordingly */ +bool is_whiteout_name(char **namep, int *namelenp) +{ + if (*namelenp > UNIONFS_WHLEN && + !strncmp(*namep, UNIONFS_WHPFX, UNIONFS_WHLEN)) { + *namep += UNIONFS_WHLEN; + *namelenp -= UNIONFS_WHLEN; + return true; + } + return false; +} + +/* is the filename valid == !(whiteout for a file or opaque dir marker) */ +bool is_validname(const char *name) +{ + if (!strncmp(name, UNIONFS_WHPFX, UNIONFS_WHLEN)) + return false; + if (!strncmp(name, UNIONFS_DIR_OPAQUE_NAME, + sizeof(UNIONFS_DIR_OPAQUE_NAME) - 1)) + return false; + return true; +} + +/* + * Look for a whiteout @name in @lower_parent directory. If error, return + * ERR_PTR. Caller must dput() the returned dentry if not an error. + * + * XXX: some callers can reuse the whname allocated buffer to avoid repeated + * free then re-malloc calls. Need to provide a different API for those + * callers. + */ +struct dentry *lookup_whiteout(const char *name, struct dentry *lower_parent) +{ + char *whname = NULL; + int err = 0, namelen; + struct dentry *wh_dentry = NULL; + + namelen = strlen(name); + whname = alloc_whname(name, namelen); + if (unlikely(IS_ERR(whname))) { + err = PTR_ERR(whname); + goto out; + } + + /* check if whiteout exists in this branch: lookup .wh.foo */ + wh_dentry = lookup_one_len(whname, lower_parent, strlen(whname)); + if (IS_ERR(wh_dentry)) { + err = PTR_ERR(wh_dentry); + goto out; + } + + /* check if negative dentry (ENOENT) */ + if (!wh_dentry->d_inode) + goto out; + + /* whiteout found: check if valid type */ + if (!S_ISREG(wh_dentry->d_inode->i_mode)) { + printk(KERN_ERR "unionfs: invalid whiteout %s entry type %d\n", + whname, wh_dentry->d_inode->i_mode); + dput(wh_dentry); + err = -EIO; + goto out; + } + +out: + kfree(whname); + if (err) + wh_dentry = ERR_PTR(err); + return wh_dentry; +} + +/* find and return first whiteout in parent directory, else ENOENT */ +struct dentry *find_first_whiteout(struct dentry *dentry) +{ + int bindex, bstart, bend; + struct dentry *parent, *lower_parent, *wh_dentry; + + parent = dget_parent(dentry); + + bstart = dbstart(parent); + bend = dbend(parent); + wh_dentry = ERR_PTR(-ENOENT); + + for (bindex = bstart; bindex <= bend; bindex++) { + lower_parent = unionfs_lower_dentry_idx(parent, bindex); + if (!lower_parent) + continue; + wh_dentry = lookup_whiteout(dentry->d_name.name, lower_parent); + if (IS_ERR(wh_dentry)) + continue; + if (wh_dentry->d_inode) + break; + dput(wh_dentry); + wh_dentry = ERR_PTR(-ENOENT); + } + + dput(parent); + + return wh_dentry; +} + +/* + * Unlink a whiteout dentry. Returns 0 or -errno. Caller must hold and + * release dentry reference. + */ +int unlink_whiteout(struct dentry *wh_dentry) +{ + int err; + struct dentry *lower_dir_dentry; + + /* dget and lock parent dentry */ + lower_dir_dentry = lock_parent_wh(wh_dentry); + + /* see Documentation/filesystems/unionfs/issues.txt */ + lockdep_off(); + err = vfs_unlink(lower_dir_dentry->d_inode, wh_dentry); + lockdep_on(); + unlock_dir(lower_dir_dentry); + + /* + * Whiteouts are special files and should be deleted no matter what + * (as if they never existed), in order to allow this create + * operation to succeed. This is especially important in sticky + * directories: a whiteout may have been created by one user, but + * the newly created file may be created by another user. + * Therefore, in order to maintain Unix semantics, if the vfs_unlink + * above failed, then we have to try to directly unlink the + * whiteout. Note: in the ODF version of unionfs, whiteout are + * handled much more cleanly. + */ + if (err == -EPERM) { + struct inode *inode = lower_dir_dentry->d_inode; + err = inode->i_op->unlink(inode, wh_dentry); + } + if (err) + printk(KERN_ERR "unionfs: could not unlink whiteout %s, " + "err = %d\n", wh_dentry->d_name.name, err); + + return err; + +} + +/* + * Helper function when creating new objects (create, symlink, mknod, etc.). + * Checks to see if there's a whiteout in @lower_dentry's parent directory, + * whose name is taken from @dentry. Then tries to remove that whiteout, if + * found. If is a branch marked readonly, return -EROFS. + * If it finds both a regular file and a whiteout, return -EIO (this should + * never happen). + * + * Return 0 if no whiteout was found. Return 1 if one was found and + * successfully removed. Therefore a value >= 0 tells the caller that + * @lower_dentry belongs to a good branch to create the new object in). + * Return -ERRNO if an error occurred during whiteout lookup or in trying to + * unlink the whiteout. + */ +int check_unlink_whiteout(struct dentry *dentry, struct dentry *lower_dentry, + int bindex) +{ + int err; + struct dentry *wh_dentry = NULL; + struct dentry *lower_dir_dentry = NULL; + + /* look for whiteout dentry first */ + lower_dir_dentry = dget_parent(lower_dentry); + wh_dentry = lookup_whiteout(dentry->d_name.name, lower_dir_dentry); + dput(lower_dir_dentry); + if (IS_ERR(wh_dentry)) { + err = PTR_ERR(wh_dentry); + goto out; + } + + if (!wh_dentry->d_inode) { /* no whiteout exists*/ + err = 0; + goto out_dput; + } + + /* check if regular file and whiteout were both found */ + if (unlikely(lower_dentry->d_inode)) { + err = -EIO; + printk(KERN_ERR "unionfs: found both whiteout and regular " + "file in directory %s (branch %d)\n", + lower_dir_dentry->d_name.name, bindex); + goto out_dput; + } + + /* check if branch is writeable */ + err = is_robranch_super(dentry->d_sb, bindex); + if (err) + goto out_dput; + + /* .wh.foo has been found, so let's unlink it */ + err = unlink_whiteout(wh_dentry); + if (!err) + err = 1; /* a whiteout was found and successfully removed */ +out_dput: + dput(wh_dentry); +out: + return err; +} + +/* + * Pass an unionfs dentry and an index. It will try to create a whiteout + * for the filename in dentry, and will try in branch 'index'. On error, + * it will proceed to a branch to the left. + */ +int create_whiteout(struct dentry *dentry, int start) +{ + int bstart, bend, bindex; + struct dentry *lower_dir_dentry; + struct dentry *lower_dentry; + struct dentry *lower_wh_dentry; + char *name = NULL; + int err = -EINVAL; + + verify_locked(dentry); + + bstart = dbstart(dentry); + bend = dbend(dentry); + + /* create dentry's whiteout equivalent */ + name = alloc_whname(dentry->d_name.name, dentry->d_name.len); + if (unlikely(IS_ERR(name))) { + err = PTR_ERR(name); + goto out; + } + + for (bindex = start; bindex >= 0; bindex--) { + lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); + + if (!lower_dentry) { + /* + * if lower dentry is not present, create the + * entire lower dentry directory structure and go + * ahead. Since we want to just create whiteout, we + * only want the parent dentry, and hence get rid of + * this dentry. + */ + lower_dentry = create_parents(dentry->d_inode, + dentry, + dentry->d_name.name, + bindex); + if (!lower_dentry || IS_ERR(lower_dentry)) { + int ret = PTR_ERR(lower_dentry); + if (!IS_COPYUP_ERR(ret)) + printk(KERN_ERR + "unionfs: create_parents for " + "whiteout failed: bindex=%d " + "err=%d\n", bindex, ret); + continue; + } + } + + lower_wh_dentry = + lookup_one_len(name, lower_dentry->d_parent, + dentry->d_name.len + UNIONFS_WHLEN); + if (IS_ERR(lower_wh_dentry)) + continue; + + /* + * The whiteout already exists. This used to be impossible, + * but now is possible because of opaqueness. + */ + if (lower_wh_dentry->d_inode) { + dput(lower_wh_dentry); + err = 0; + goto out; + } + + lower_dir_dentry = lock_parent_wh(lower_wh_dentry); + err = is_robranch_super(dentry->d_sb, bindex); + if (!err) + err = vfs_create(lower_dir_dentry->d_inode, + lower_wh_dentry, + ~current->fs->umask & S_IRUGO, + NULL); + unlock_dir(lower_dir_dentry); + dput(lower_wh_dentry); + + if (!err || !IS_COPYUP_ERR(err)) + break; + } + + /* set dbopaque so that lookup will not proceed after this branch */ + if (!err) + dbopaque(dentry) = bindex; + +out: + kfree(name); + return err; +} + +/* + * Delete all of the whiteouts in a given directory for rmdir. + * + * lower directory inode should be locked + */ +static int do_delete_whiteouts(struct dentry *dentry, int bindex, + struct unionfs_dir_state *namelist) +{ + int err = 0; + struct dentry *lower_dir_dentry = NULL; + struct dentry *lower_dentry; + char *name = NULL, *p; + struct inode *lower_dir; + int i; + struct list_head *pos; + struct filldir_node *cursor; + + /* Find out lower parent dentry */ + lower_dir_dentry = unionfs_lower_dentry_idx(dentry, bindex); + BUG_ON(!S_ISDIR(lower_dir_dentry->d_inode->i_mode)); + lower_dir = lower_dir_dentry->d_inode; + BUG_ON(!S_ISDIR(lower_dir->i_mode)); + + err = -ENOMEM; + name = __getname(); + if (unlikely(!name)) + goto out; + strcpy(name, UNIONFS_WHPFX); + p = name + UNIONFS_WHLEN; + + err = 0; + for (i = 0; !err && i < namelist->size; i++) { + list_for_each(pos, &namelist->list[i]) { + cursor = + list_entry(pos, struct filldir_node, + file_list); + /* Only operate on whiteouts in this branch. */ + if (cursor->bindex != bindex) + continue; + if (!cursor->whiteout) + continue; + + strlcpy(p, cursor->name, PATH_MAX - UNIONFS_WHLEN); + lower_dentry = + lookup_one_len(name, lower_dir_dentry, + cursor->namelen + + UNIONFS_WHLEN); + if (IS_ERR(lower_dentry)) { + err = PTR_ERR(lower_dentry); + break; + } + if (lower_dentry->d_inode) + err = vfs_unlink(lower_dir, lower_dentry); + dput(lower_dentry); + if (err) + break; + } + } + + __putname(name); + + /* After all of the removals, we should copy the attributes once. */ + fsstack_copy_attr_times(dentry->d_inode, lower_dir_dentry->d_inode); + +out: + return err; +} + + +void __delete_whiteouts(struct work_struct *work) +{ + struct sioq_args *args = container_of(work, struct sioq_args, work); + struct deletewh_args *d = &args->deletewh; + + args->err = do_delete_whiteouts(d->dentry, d->bindex, d->namelist); + complete(&args->comp); +} + +/* delete whiteouts in a dir (for rmdir operation) using sioq if necessary */ +int delete_whiteouts(struct dentry *dentry, int bindex, + struct unionfs_dir_state *namelist) +{ + int err; + struct super_block *sb; + struct dentry *lower_dir_dentry; + struct inode *lower_dir; + struct sioq_args args; + + sb = dentry->d_sb; + + BUG_ON(!S_ISDIR(dentry->d_inode->i_mode)); + BUG_ON(bindex < dbstart(dentry)); + BUG_ON(bindex > dbend(dentry)); + err = is_robranch_super(sb, bindex); + if (err) + goto out; + + lower_dir_dentry = unionfs_lower_dentry_idx(dentry, bindex); + BUG_ON(!S_ISDIR(lower_dir_dentry->d_inode->i_mode)); + lower_dir = lower_dir_dentry->d_inode; + BUG_ON(!S_ISDIR(lower_dir->i_mode)); + + if (!permission(lower_dir, MAY_WRITE | MAY_EXEC, NULL)) { + err = do_delete_whiteouts(dentry, bindex, namelist); + } else { + args.deletewh.namelist = namelist; + args.deletewh.dentry = dentry; + args.deletewh.bindex = bindex; + run_sioq(__delete_whiteouts, &args); + err = args.err; + } + +out: + return err; +} + +/**************************************************************************** + * Opaque directory helpers * + ****************************************************************************/ + +/* + * is_opaque_dir: returns 0 if it is NOT an opaque dir, 1 if it is, and + * -errno if an error occurred trying to figure this out. + */ +int is_opaque_dir(struct dentry *dentry, int bindex) +{ + int err = 0; + struct dentry *lower_dentry; + struct dentry *wh_lower_dentry; + struct inode *lower_inode; + struct sioq_args args; + + lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); + lower_inode = lower_dentry->d_inode; + + BUG_ON(!S_ISDIR(lower_inode->i_mode)); + + mutex_lock(&lower_inode->i_mutex); + + if (!permission(lower_inode, MAY_EXEC, NULL)) { + wh_lower_dentry = + lookup_one_len(UNIONFS_DIR_OPAQUE, lower_dentry, + sizeof(UNIONFS_DIR_OPAQUE) - 1); + } else { + args.is_opaque.dentry = lower_dentry; + run_sioq(__is_opaque_dir, &args); + wh_lower_dentry = args.ret; + } + + mutex_unlock(&lower_inode->i_mutex); + + if (IS_ERR(wh_lower_dentry)) { + err = PTR_ERR(wh_lower_dentry); + goto out; + } + + /* This is an opaque dir iff wh_lower_dentry is positive */ + err = !!wh_lower_dentry->d_inode; + + dput(wh_lower_dentry); +out: + return err; +} + +void __is_opaque_dir(struct work_struct *work) +{ + struct sioq_args *args = container_of(work, struct sioq_args, work); + + args->ret = lookup_one_len(UNIONFS_DIR_OPAQUE, args->is_opaque.dentry, + sizeof(UNIONFS_DIR_OPAQUE) - 1); + complete(&args->comp); +} + +int make_dir_opaque(struct dentry *dentry, int bindex) +{ + int err = 0; + struct dentry *lower_dentry, *diropq; + struct inode *lower_dir; + kernel_cap_t orig_cap; + + /* + * Opaque directory whiteout markers are special files (like regular + * whiteouts), and should appear to the users as if they don't + * exist. They should be created/deleted regardless of directory + * search/create permissions, but only for the duration of this + * creation of the .wh.__dir_opaque: file. Note, this does not + * circumvent normal ->permission). + */ + orig_cap = current->cap_effective; + cap_raise(current->cap_effective, CAP_DAC_READ_SEARCH); + cap_raise(current->cap_effective, CAP_DAC_OVERRIDE); + + lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); + lower_dir = lower_dentry->d_inode; + BUG_ON(!S_ISDIR(dentry->d_inode->i_mode) || + !S_ISDIR(lower_dir->i_mode)); + + mutex_lock(&lower_dir->i_mutex); + diropq = lookup_one_len(UNIONFS_DIR_OPAQUE, lower_dentry, + sizeof(UNIONFS_DIR_OPAQUE) - 1); + if (IS_ERR(diropq)) { + err = PTR_ERR(diropq); + goto out; + } + + if (!diropq->d_inode) + err = vfs_create(lower_dir, diropq, S_IRUGO, NULL); + if (!err) + dbopaque(dentry) = bindex; + + dput(diropq); + +out: + mutex_unlock(&lower_dir->i_mutex); + current->cap_effective = orig_cap; + return err; +} diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/xattr.c kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/xattr.c --- kernel-source-diablo-2.6.21.orig/kernel-source/fs/unionfs/xattr.c 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/fs/unionfs/xattr.c 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2003-2009 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2007 Josef 'Jeff' Sipek + * Copyright (c) 2005-2006 Junjiro Okajima + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2004-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2009 Stony Brook University + * Copyright (c) 2003-2009 The Research Foundation of SUNY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "union.h" + +/* This is lifted from fs/xattr.c */ +void *unionfs_xattr_alloc(size_t size, size_t limit) +{ + void *ptr; + + if (size > limit) + return ERR_PTR(-E2BIG); + + if (!size) /* size request, no buffer is needed */ + return NULL; + + ptr = kmalloc(size, GFP_KERNEL); + if (unlikely(!ptr)) + return ERR_PTR(-ENOMEM); + return ptr; +} + +/* + * BKL held by caller. + * dentry->d_inode->i_mutex locked + */ +ssize_t unionfs_getxattr(struct dentry *dentry, const char *name, void *value, + size_t size) +{ + struct dentry *lower_dentry = NULL; + struct dentry *parent; + int err = -EOPNOTSUPP; + bool valid; + + unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); + parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); + unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); + + valid = __unionfs_d_revalidate(dentry, parent, false); + if (unlikely(!valid)) { + err = -ESTALE; + goto out; + } + + lower_dentry = unionfs_lower_dentry(dentry); + + err = vfs_getxattr(lower_dentry, (char *) name, value, size); + +out: + unionfs_check_dentry(dentry); + unionfs_unlock_dentry(dentry); + unionfs_unlock_parent(dentry, parent); + unionfs_read_unlock(dentry->d_sb); + return err; +} + +/* + * BKL held by caller. + * dentry->d_inode->i_mutex locked + */ +int unionfs_setxattr(struct dentry *dentry, const char *name, + const void *value, size_t size, int flags) +{ + struct dentry *lower_dentry = NULL; + struct dentry *parent; + int err = -EOPNOTSUPP; + bool valid; + + unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); + parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); + unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); + + valid = __unionfs_d_revalidate(dentry, parent, false); + if (unlikely(!valid)) { + err = -ESTALE; + goto out; + } + + lower_dentry = unionfs_lower_dentry(dentry); + + err = vfs_setxattr(lower_dentry, (char *) name, (void *) value, + size, flags); + +out: + unionfs_check_dentry(dentry); + unionfs_unlock_dentry(dentry); + unionfs_unlock_parent(dentry, parent); + unionfs_read_unlock(dentry->d_sb); + return err; +} + +/* + * BKL held by caller. + * dentry->d_inode->i_mutex locked + */ +int unionfs_removexattr(struct dentry *dentry, const char *name) +{ + struct dentry *lower_dentry = NULL; + struct dentry *parent; + int err = -EOPNOTSUPP; + bool valid; + + unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); + parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); + unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); + + valid = __unionfs_d_revalidate(dentry, parent, false); + if (unlikely(!valid)) { + err = -ESTALE; + goto out; + } + + lower_dentry = unionfs_lower_dentry(dentry); + + err = vfs_removexattr(lower_dentry, (char *) name); + +out: + unionfs_check_dentry(dentry); + unionfs_unlock_dentry(dentry); + unionfs_unlock_parent(dentry, parent); + unionfs_read_unlock(dentry->d_sb); + return err; +} + +/* + * BKL held by caller. + * dentry->d_inode->i_mutex locked + */ +ssize_t unionfs_listxattr(struct dentry *dentry, char *list, size_t size) +{ + struct dentry *lower_dentry = NULL; + struct dentry *parent; + int err = -EOPNOTSUPP; + char *encoded_list = NULL; + bool valid; + + unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); + parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); + unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); + + valid = __unionfs_d_revalidate(dentry, parent, false); + if (unlikely(!valid)) { + err = -ESTALE; + goto out; + } + + lower_dentry = unionfs_lower_dentry(dentry); + + encoded_list = list; + err = vfs_listxattr(lower_dentry, encoded_list, size); + +out: + unionfs_check_dentry(dentry); + unionfs_unlock_dentry(dentry); + unionfs_unlock_parent(dentry, parent); + unionfs_read_unlock(dentry->d_sb); + return err; +} diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/include/asm-arm/arch-omap/omapfb.h kernel-source-diablo-2.6.21/kernel-source/include/asm-arm/arch-omap/omapfb.h --- kernel-source-diablo-2.6.21.orig/kernel-source/include/asm-arm/arch-omap/omapfb.h 2009-07-01 17:15:49.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/include/asm-arm/arch-omap/omapfb.h 2009-07-01 17:29:10.000000000 +0100 @@ -62,6 +62,7 @@ #define OMAPFB_CAPS_WINDOW_PIXEL_DOUBLE 0x00010000 #define OMAPFB_CAPS_WINDOW_SCALE 0x00020000 #define OMAPFB_CAPS_WINDOW_OVERLAY 0x00040000 +#define OMAPFB_CAPS_WINDOW_ROTATE 0x00080000 #define OMAPFB_CAPS_SET_BACKLIGHT 0x01000000 /* Values from DSP must map to lower 16-bits */ @@ -305,6 +306,7 @@ struct lcd_ctrl { int screen_width, int pos_x, int pos_y, int width, int height, int color_mode); + int (*set_rotate) (int angle); int (*setup_mem) (int plane, size_t size, int mem_type, unsigned long *paddr); int (*mmap) (struct fb_info *info, diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/include/asm-arm/kdebug.h kernel-source-diablo-2.6.21/kernel-source/include/asm-arm/kdebug.h --- kernel-source-diablo-2.6.21.orig/kernel-source/include/asm-arm/kdebug.h 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/include/asm-arm/kdebug.h 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,35 @@ +#ifndef _ARM_KDEBUG_H +#define _ARM_KDEBUG_H + +#include + +struct pt_regs; + +struct die_args { + struct pt_regs *regs; + const char *str; + unsigned long addr; + unsigned int fsr; +}; + +extern int register_page_fault_notifier(struct notifier_block *); +extern int unregister_page_fault_notifier(struct notifier_block *); + +extern void notify_die(const char *str, struct pt_regs *regs, + struct siginfo *info, unsigned long err, + unsigned long trap); + +enum die_val { + DIE_TRANS_FAULT = 1, + DIE_PAGE_FAULT +}; + + +/* Die notifier kernel feature only used by kprobes. We don't use it + * for kprobes on ARM. Disable it with this dummy function. */ +static inline int register_die_notifier(struct notifier_block *nb) +{ + return 0; +} + +#endif /* _ARM_KDEBUG_H */ diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/include/asm-arm/kprobes.h kernel-source-diablo-2.6.21/kernel-source/include/asm-arm/kprobes.h --- kernel-source-diablo-2.6.21.orig/kernel-source/include/asm-arm/kprobes.h 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/include/asm-arm/kprobes.h 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,84 @@ +#ifndef _ARM_KPROBES_H +#define _ARM_KPROBES_H + +/* include/asm-arm/kprobes.h + * + * Copyright (C) 2006, 2007 Motorola Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + */ + +#include +#include +#include +#include +#include + +#define ARCH_SUPPORTS_KRETPROBES +#define __ARCH_WANT_KPROBES_INSN_SLOT +#define MAX_INSN_SIZE 2 +#define ARCH_INACTIVE_KPROBE_COUNT 0 + +#define flush_insn_slot(p) do { } while (0) + +#define MAX_STACK_SIZE 64 /* Probably 32 should suffice */ +#define MIN_STACK_SIZE(addr) \ + min( (unsigned long)MAX_STACK_SIZE, \ + (unsigned long)current_thread_info() + \ + THREAD_START_SP - (addr) ) + +/* This undefined instruction must be unique and + * reserved solely for kprobes' use. */ +#define KPROBE_BREAKPOINT_INSTRUCTION 0xe7f001f8 + +#define KPROBE_RETURN_INSTRUCTION 0xe1a0f00e /* mov pc, lr */ + +#define JPROBE_ENTRY(pentry) ((kprobe_opcode_t *)(pentry)) + +enum kprobe_insn { + INSN_REJECTED, + INSN_GOOD, + INSN_GOOD_NO_SLOT +}; + +typedef u32 kprobe_opcode_t; + +struct kprobe; +typedef void (kprobe_insn_handler_t)(struct kprobe *, struct pt_regs *); + +/* Architecture specific copy of original instruction. */ +struct arch_specific_insn { + kprobe_opcode_t *insn; + kprobe_insn_handler_t *insn_handler; +}; + +struct prev_kprobe { + struct kprobe *kp; + unsigned int status; +}; + + +/* per-cpu kprobe control block */ +struct kprobe_ctlblk { + unsigned int kprobe_status; + struct prev_kprobe prev_kprobe; + struct pt_regs jprobe_saved_regs; + char jprobes_stack[MAX_STACK_SIZE]; +}; + +extern void arch_flush_insn_slot(struct kprobe *); +extern int kprobe_exceptions_notify(struct notifier_block *self, + unsigned long val, void *data); +extern void arch_remove_kprobe(struct kprobe *); +extern enum kprobe_insn kprobe_decode_insn(kprobe_opcode_t, + struct arch_specific_insn *); +extern int kprobe_handler(struct pt_regs *regs); + +#endif /* _ARM_KPROBES_H */ diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/include/asm-arm/ptrace.h kernel-source-diablo-2.6.21/kernel-source/include/asm-arm/ptrace.h --- kernel-source-diablo-2.6.21.orig/kernel-source/include/asm-arm/ptrace.h 2007-10-02 08:09:32.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/include/asm-arm/ptrace.h 2009-07-01 17:29:10.000000000 +0100 @@ -144,6 +144,8 @@ static inline int valid_user_regs(struct #define instruction_pointer(regs) \ (pc_pointer((regs)->ARM_pc)) +#define regs_return_value(regs) ((regs)->ARM_r0) + #ifdef CONFIG_SMP extern unsigned long profile_pc(struct pt_regs *regs); #else diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/include/linux/crypto.h kernel-source-diablo-2.6.21/kernel-source/include/linux/crypto.h --- kernel-source-diablo-2.6.21.orig/kernel-source/include/linux/crypto.h 2007-10-02 08:09:33.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/include/linux/crypto.h 2009-07-01 17:29:10.000000000 +0100 @@ -217,7 +217,7 @@ int crypto_unregister_alg(struct crypto_ /* * Algorithm query interface. */ -#ifdef CONFIG_CRYPTO +#if defined(CONFIG_CRYPTO) || (defined(CONFIG_CRYPTO_MODULE) && defined(MODULE)) int crypto_has_alg(const char *name, u32 type, u32 mask); #else static inline int crypto_has_alg(const char *name, u32 type, u32 mask) diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/include/linux/fs.h kernel-source-diablo-2.6.21/kernel-source/include/linux/fs.h --- kernel-source-diablo-2.6.21.orig/kernel-source/include/linux/fs.h 2007-10-02 08:09:33.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/include/linux/fs.h 2009-07-01 17:29:10.000000000 +0100 @@ -1738,6 +1738,11 @@ extern ssize_t generic_splice_sendpage(s struct file *out, loff_t *, size_t len, unsigned int flags); extern long do_splice_direct(struct file *in, loff_t *ppos, struct file *out, size_t len, unsigned int flags); +extern long vfs_splice_from(struct pipe_inode_info *pipe, struct file *out, + loff_t *ppos, size_t len, unsigned int flags); +extern long vfs_splice_to(struct file *in, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, + unsigned int flags); extern void file_ra_state_init(struct file_ra_state *ra, struct address_space *mapping); diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/include/linux/fs_stack.h kernel-source-diablo-2.6.21/kernel-source/include/linux/fs_stack.h --- kernel-source-diablo-2.6.21.orig/kernel-source/include/linux/fs_stack.h 2007-10-02 08:09:33.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/include/linux/fs_stack.h 2009-07-01 17:29:10.000000000 +0100 @@ -1,17 +1,27 @@ +/* + * Copyright (c) 2006-2009 Erez Zadok + * Copyright (c) 2006-2007 Josef 'Jeff' Sipek + * Copyright (c) 2006-2009 Stony Brook University + * Copyright (c) 2006-2009 The Research Foundation of SUNY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + #ifndef _LINUX_FS_STACK_H #define _LINUX_FS_STACK_H -/* This file defines generic functions used primarily by stackable +/* + * This file defines generic functions used primarily by stackable * filesystems; none of these functions require i_mutex to be held. */ #include /* externs for fs/stack.c */ -extern void fsstack_copy_attr_all(struct inode *dest, const struct inode *src, - int (*get_nlinks)(struct inode *)); - -extern void fsstack_copy_inode_size(struct inode *dst, const struct inode *src); +extern void fsstack_copy_attr_all(struct inode *dest, const struct inode *src); +extern void fsstack_copy_inode_size(struct inode *dst, struct inode *src); /* inlines */ static inline void fsstack_copy_attr_atime(struct inode *dest, diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/include/linux/magic.h kernel-source-diablo-2.6.21/kernel-source/include/linux/magic.h --- kernel-source-diablo-2.6.21.orig/kernel-source/include/linux/magic.h 2007-10-02 08:09:33.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/include/linux/magic.h 2009-07-01 17:29:10.000000000 +0100 @@ -35,6 +35,8 @@ #define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs" #define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs" +#define UNIONFS_SUPER_MAGIC 0xf15f083d + #define SMB_SUPER_MAGIC 0x517B #define USBDEVICE_SUPER_MAGIC 0x9fa2 diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/include/linux/mmc/host.h kernel-source-diablo-2.6.21/kernel-source/include/linux/mmc/host.h --- kernel-source-diablo-2.6.21.orig/kernel-source/include/linux/mmc/host.h 2009-07-01 17:15:49.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/include/linux/mmc/host.h 2009-07-01 17:29:10.000000000 +0100 @@ -105,7 +105,8 @@ struct mmc_host { struct work_struct idle_work; struct timer_list idle_timer; unsigned int idle_delay; - + unsigned int first_devidx; /* preferred starting mmc block device index */ + unsigned long private[0] ____cacheline_aligned; }; diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/include/linux/namei.h kernel-source-diablo-2.6.21/kernel-source/include/linux/namei.h --- kernel-source-diablo-2.6.21.orig/kernel-source/include/linux/namei.h 2007-10-02 08:09:33.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/include/linux/namei.h 2009-07-01 17:29:10.000000000 +0100 @@ -3,6 +3,7 @@ #include #include +#include struct vfsmount; @@ -47,6 +48,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LA * - internal "there are more path compnents" flag * - locked when lookup done with dcache_lock held * - dentry cache is untrusted; force a real lookup + * - lookup path from given dentry/vfsmount pair */ #define LOOKUP_FOLLOW 1 #define LOOKUP_DIRECTORY 2 @@ -54,6 +56,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LA #define LOOKUP_PARENT 16 #define LOOKUP_NOALT 32 #define LOOKUP_REVAL 64 +#define LOOKUP_ONE 128 /* * Intent data */ @@ -71,6 +74,8 @@ extern int FASTCALL(__user_walk_fd(int d extern int FASTCALL(path_lookup(const char *, unsigned, struct nameidata *)); extern int FASTCALL(path_walk(const char *, struct nameidata *)); extern int FASTCALL(link_path_walk(const char *, struct nameidata *)); +extern int vfs_path_lookup(struct dentry *, struct vfsmount *, + const char *, unsigned int, struct nameidata *); extern void path_release(struct nameidata *); extern void path_release_on_umount(struct nameidata *); @@ -81,7 +86,14 @@ extern struct file *lookup_instantiate_f extern struct file *nameidata_to_filp(struct nameidata *nd, int flags); extern void release_open_intent(struct nameidata *); -extern struct dentry * lookup_one_len(const char *, struct dentry *, int); +extern struct dentry * lookup_one_len_nd(const char *, struct dentry *, + int, struct nameidata *); + +static inline struct dentry *lookup_one_len(const char *name, + struct dentry *dir, int len) +{ + return lookup_one_len_nd(name, dir, len, NULL); +} extern int follow_down(struct vfsmount **, struct dentry **); extern int follow_up(struct vfsmount **, struct dentry **); @@ -99,4 +111,16 @@ static inline char *nd_get_link(struct n return nd->saved_names[nd->depth]; } +static inline void pathget(struct path *path) +{ + mntget(path->mnt); + dget(path->dentry); +} + +static inline void pathput(struct path *path) +{ + dput(path->dentry); + mntput(path->mnt); +} + #endif /* _LINUX_NAMEI_H */ diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/include/linux/slab.h kernel-source-diablo-2.6.21/kernel-source/include/linux/slab.h --- kernel-source-diablo-2.6.21.orig/kernel-source/include/linux/slab.h 2007-10-02 08:09:33.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/include/linux/slab.h 2009-07-01 17:29:10.000000000 +0100 @@ -72,6 +72,7 @@ static inline void *kmem_cache_alloc_nod */ void *__kmalloc(size_t, gfp_t); void *__kzalloc(size_t, gfp_t); +void * __must_check krealloc(const void *, size_t, gfp_t); void kfree(const void *); unsigned int ksize(const void *); diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/include/linux/union_fs.h kernel-source-diablo-2.6.21/kernel-source/include/linux/union_fs.h --- kernel-source-diablo-2.6.21.orig/kernel-source/include/linux/union_fs.h 1970-01-01 01:00:00.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/include/linux/union_fs.h 2009-07-01 17:29:10.000000000 +0100 @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2003-2009 Erez Zadok + * Copyright (c) 2005-2007 Josef 'Jeff' Sipek + * Copyright (c) 2003-2009 Stony Brook University + * Copyright (c) 2003-2009 The Research Foundation of SUNY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _LINUX_UNION_FS_H +#define _LINUX_UNION_FS_H + +/* + * DEFINITIONS FOR USER AND KERNEL CODE: + */ +# define UNIONFS_IOCTL_INCGEN _IOR(0x15, 11, int) +# define UNIONFS_IOCTL_QUERYFILE _IOR(0x15, 15, int) + +#endif /* _LINUX_UNIONFS_H */ + diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/include/linux/vermagic.h kernel-source-diablo-2.6.21/kernel-source/include/linux/vermagic.h --- kernel-source-diablo-2.6.21.orig/kernel-source/include/linux/vermagic.h 2007-10-02 08:09:33.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/include/linux/vermagic.h 2009-07-01 17:29:10.000000000 +0100 @@ -8,7 +8,7 @@ #define MODULE_VERMAGIC_SMP "" #endif #ifdef CONFIG_PREEMPT -#define MODULE_VERMAGIC_PREEMPT "preempt " +#define MODULE_VERMAGIC_PREEMPT "" #else #define MODULE_VERMAGIC_PREEMPT "" #endif diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/mm/slab.c kernel-source-diablo-2.6.21/kernel-source/mm/slab.c --- kernel-source-diablo-2.6.21.orig/kernel-source/mm/slab.c 2009-07-01 17:15:49.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/mm/slab.c 2009-07-01 17:29:10.000000000 +0100 @@ -3736,6 +3736,53 @@ void *__kmalloc(size_t size, gfp_t flags EXPORT_SYMBOL(__kmalloc); #endif + +/** + * krealloc - reallocate memory. The contents will remain unchanged. + * @p: object to reallocate memory for. + * @new_size: how many bytes of memory are required. + * @flags: the type of memory to allocate. + * + * The contents of the object pointed to are preserved up to the + * lesser of the new and old sizes. If @p is %NULL, krealloc() + * behaves exactly like kmalloc(). If @size is 0 and @p is not a + * %NULL pointer, the object pointed to is freed. + */ +void *krealloc(const void *p, size_t new_size, gfp_t flags) +{ + struct kmem_cache *cache, *new_cache; + void *ret; + + if (unlikely(!p)) + return kmalloc_track_caller(new_size, flags); + + if (unlikely(!new_size)) { + kfree(p); + return NULL; + } + + cache = virt_to_cache(p); + new_cache = __find_general_cachep(new_size, flags); + + /* + * If new size fits in the current cache, bail out. + */ + if (likely(cache == new_cache)) + return (void *)p; + + /* + * We are on the slow-path here so do not use __cache_alloc + * because it bloats kernel text. + */ + ret = kmalloc_track_caller(new_size, flags); + if (ret) { + memcpy(ret, p, min(new_size, ksize(p))); + kfree(p); + } + return ret; +} +EXPORT_SYMBOL(krealloc); + /** * kmem_cache_free - Deallocate an object * @cachep: The cache the allocation was from. diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/mm/slob.c kernel-source-diablo-2.6.21/kernel-source/mm/slob.c --- kernel-source-diablo-2.6.21.orig/kernel-source/mm/slob.c 2007-10-02 08:09:33.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/mm/slob.c 2009-07-01 17:29:10.000000000 +0100 @@ -190,6 +190,39 @@ void *__kmalloc(size_t size, gfp_t gfp) } EXPORT_SYMBOL(__kmalloc); +/** + * krealloc - reallocate memory. The contents will remain unchanged. + * + * @p: object to reallocate memory for. + * @new_size: how many bytes of memory are required. + * @flags: the type of memory to allocate. + * + * The contents of the object pointed to are preserved up to the + * lesser of the new and old sizes. If @p is %NULL, krealloc() + * behaves exactly like kmalloc(). If @size is 0 and @p is not a + * %NULL pointer, the object pointed to is freed. + */ +void *krealloc(const void *p, size_t new_size, gfp_t flags) +{ + void *ret; + + if (unlikely(!p)) + return kmalloc_track_caller(new_size, flags); + + if (unlikely(!new_size)) { + kfree(p); + return NULL; + } + + ret = kmalloc_track_caller(new_size, flags); + if (ret) { + memcpy(ret, p, min(new_size, ksize(p))); + kfree(p); + } + return ret; +} +EXPORT_SYMBOL(krealloc); + void kfree(const void *block) { bigblock_t *bb, **last = &bigblocks; diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/scripts/kconfig/lxdialog/check-lxdialog.sh kernel-source-diablo-2.6.21/kernel-source/scripts/kconfig/lxdialog/check-lxdialog.sh --- kernel-source-diablo-2.6.21.orig/kernel-source/scripts/kconfig/lxdialog/check-lxdialog.sh 2007-10-02 08:09:34.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/scripts/kconfig/lxdialog/check-lxdialog.sh 2009-07-01 17:29:10.000000000 +0100 @@ -9,11 +9,11 @@ ldflags() echo '-lncursesw' exit fi - $cc -print-file-name=libncurses.so | grep -q / - if [ $? -eq 0 ]; then + #$cc -print-file-name=libncurses.so | grep -q / + #if [ $? -eq 0 ]; then echo '-lncurses' exit - fi + #fi $cc -print-file-name=libcurses.so | grep -q / if [ $? -eq 0 ]; then echo '-lcurses' diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/scripts/mkcompile_h kernel-source-diablo-2.6.21/kernel-source/scripts/mkcompile_h --- kernel-source-diablo-2.6.21.orig/kernel-source/scripts/mkcompile_h 2007-10-02 08:09:34.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/scripts/mkcompile_h 2009-07-01 17:29:10.000000000 +0100 @@ -29,7 +29,6 @@ fi UTS_VERSION="#$VERSION" CONFIG_FLAGS="" if [ -n "$SMP" ] ; then CONFIG_FLAGS="SMP"; fi -if [ -n "$PREEMPT" ] ; then CONFIG_FLAGS="$CONFIG_FLAGS PREEMPT"; fi UTS_VERSION="$UTS_VERSION $CONFIG_FLAGS `LC_ALL=C LANG=C date`" # Truncate to maximum length diff -Naurp kernel-source-diablo-2.6.21.orig/kernel-source/sound/arm/omap/eac.c kernel-source-diablo-2.6.21/kernel-source/sound/arm/omap/eac.c --- kernel-source-diablo-2.6.21.orig/kernel-source/sound/arm/omap/eac.c 2009-07-01 17:15:49.000000000 +0100 +++ kernel-source-diablo-2.6.21/kernel-source/sound/arm/omap/eac.c 2009-07-01 17:29:10.000000000 +0100 @@ -746,11 +746,13 @@ int eac_set_mode(struct device *dev, int { struct omap_eac *eac = dev_get_drvdata(dev); +#if 0 #ifdef DEBUG printk(KERN_DEBUG "EAC mode: play %s, rec %s\n", play ? "enabled" : "disabled", rec ? "enabled" : "disabled"); #endif +#endif BUG_ON(eac == NULL); mutex_lock(&eac->mutex); if (play || rec) {