diff -urN linux-2.6.17-rc1-mm3-orig/fs/Kconfig linux-2.6.17-rc1-mm3-new/fs/Kconfig --- linux-2.6.17-rc1-mm3-orig/fs/Kconfig 2006-04-21 22:15:21.222831250 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/Kconfig 2006-04-22 12:42:04.469419822 +0000 @@ -1327,6 +1327,16 @@ Say Y here if you want to try writing to UFS partitions. This is experimental, so you should back up your UFS partitions beforehand. +config UNION_FS + tristate "Union fs support" + depends on EXPERIMENTAL + help + Unionfs is a stackable unification file system, which can + appear to merge the contents of several directories (branches), + while keeping their physical content separate. + + see for details + endmenu menu "Network File Systems" diff -urN linux-2.6.17-rc1-mm3-orig/fs/Makefile linux-2.6.17-rc1-mm3-new/fs/Makefile --- linux-2.6.17-rc1-mm3-orig/fs/Makefile 2006-04-21 22:15:21.230831750 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/Makefile 2006-04-22 12:42:04.480418149 +0000 @@ -103,3 +103,4 @@ obj-$(CONFIG_DEBUG_FS) += debugfs/ obj-$(CONFIG_CONFIGFS_FS) += configfs/ obj-$(CONFIG_OCFS2_FS) += ocfs2/ +obj-$(CONFIG_UNION_FS) += unionfs/ diff -urN linux-2.6.17-rc1-mm3-orig/fs/unionfs/AUTHORS linux-2.6.17-rc1-mm3-new/fs/unionfs/AUTHORS --- linux-2.6.17-rc1-mm3-orig/fs/unionfs/AUTHORS 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/unionfs/AUTHORS 2006-04-11 20:24:21.000000000 +0000 @@ -0,0 +1,164 @@ +PRIMARY AUTHORS AND MAJOR CONTRIBUTORS TO AM_UTILS: +The primary authors work at the Filesystems and Storage Lab at Stony Brook +University. They also currently maintain the package. + +* Erez Zadok +- Primary Investigator + +* Charles P. Wright +- Primary maintainer (12/2004-Present) +- Snapshotting support +- Initial development + +* Dave Quigley +- 2.6 Port +- Maintenance (1/2005-Present) + +* Arun Krishna Kumar +- Maintenance (6/2005-12/2005) + +* Mohammad Nayyer Zubair +- Initial development +- Regression Suite + +* Puja Gupta +- Initial development + +* Harikesavan Krishnan +- Initial development + +* Josef "Jeff" Sipek +- Maintenance (12/2005-Present) + +Other contributors: +* Sai Suman +January 10, 2005: NFS Export patch +January 10, 2005: Copyup bug fix. + +* Alex de Landgraaf +January 10, 2005: Fixes for gcc 2.9.5 + +* Anton Farygin +February 2, 2005: Fixes for non-privileged copyup. +March 2, 2005: vfs_readdir bug in dirhelper.c +March 2, 2005: Fix copyup on symlinks +March 2, 2005: Fix handling of failed whiteout lookup +August 15, 2005: Fix possible deadlock in incgen when memory allocaiton fails. + +* Jaspreet Singh +February 8, 2005: Use security functions for xattr copyup. +May 11, 2005: Fix for Xattr copyup on Selinux +May 12, 2005: Selinux requires valid i_mode before d_instantiate. + +* Fabian Franz +February 22, 2005: Hardlinks should have the same inode number. +February 22, 2005: Device copyup fix. +February 22, 2005: Identified d_delete problem over tmpfs +March 2, 2005: vfs_readdir bug in dirhelper.c + +* Terry Barnaby +March 3, 2005: Copy attributes on d_revalidate +March 2, 2005: Fix for readdir over NFS +March 16, 2005: Fix for unionfs_dir_llseek +April 20, 2005: Submitted opaque directory patch, wich the current code +is inspired by. + +* Lucas Correia Villa Real +March 7, 2005: Makefile uses MODDIR. + +* Eduard Bloch +March 7, 2005: Fix man page sections, improve Makefile + +* Fernando Freiregomez +March 7, 2005: Have snapmerge fix times on created files. + +* Markus F.X.J. Oberhumer +April 18, 2005: Fixes for compilation on AMD64 + +* Tomas Matejicek +May 10, 2005: I used his linuxrc as the basis for the Unionfs as a root +file system instructions. + +* Shaya Potter +July 19, 2005: Symbolic links should not be renamed to whiteout files, as +that confuses Unionfs later. +September 2, 2005: Fix copyup checking for mmap. +October 11, 2005: Deadlock fix. +October 20, 2005: Improved locking for branch manipulation +October 20, 2005: Fix for removing opaque directories. +November 18, 2005: NULL check in lookup_whiteout. + +* Jan Engelhardt +July 22, 2005: Support for realpath in unionctl. +August 1, 2005: Use vprintk instead of vsnprintf/printk combo. +August 9, 2005: Fix unionctl so that it doesn't truncate "/" to "". + +* Malcom Lashley +August 9, 2005: AMD64 compile fixes. + +* Eduard Bloch +August 9, 2005: Debian packaging files. + +* Klaus Knopper +August 22, 2005: Fix from lookup_one_len in unionfs_create. +September 23, 2005: Fix for unionfs_permission pertaining to +read only file systems + +* Junjiro Okajima +September 21, 2005: Fix for of by one error in KMALLOC. +September 26, 2005: Fix for d_revalidate. +September 28, 2005: rmdir fix. +October 13, 2005: rename fix (Bug #425). +October 19, 2005: NFS security hole fix. +November 5, 2005: Fix for race b/t lookup and new_dentry_private_data. +November 8, 2005: Fix error checking in lookup_backend. +December 27, 2005: Fixed create whiteout bug, forgotten dput() +December 27, 2005: Fixed unlink bug, forgotten dput()s +December 27, 2005: Fixed create bug, forgotten dput(), extra GET_PARENT +December 27, 2005: Fixed permission bug, creat/open truncates the running +executable +December 27, 2005: Properly copyup atime, mtime, and ctime. +December 28, 2005: Fixed missing DPUT()s in unionfs_lookup_backend +December 28, 2005: Fixed privileges-related bug in is_opaque_dir +December 29, 2005: Fixed missing/misplaced DPUT()sg DPUT() +January 20, 2006: Introduced per-branch nfsro flag (unionctl.c) +January 22, 2006: Fixed persistant inode code: link, rmdir, shrinking of +dcache, map validation +January 24, 2006: Fixed hidden inode not being iput() since ibstart and +ibend is not updated. +January 31, 2006: Fixes "pseudo hardlink" via persistent inode +February 2, 2006: Fixed minor bug in unionfs_create regarding stale atime and +mtime +February 5, 2006: Changed get_uin() to read_uid() +February 10, 2006: Fixed stale inode problem of regression/bug418.sh +March 4, 2006: Fixed unionfs_permission on reiserfs and xfs + +* Robert Glowczynski +October 6, 2005: Fix for fsync over squashfs. + +* Charles Duffy +October 22, 2005: readdir.sh regression script + +* Alessio Curri +November 11, 2005: Fix for RPM spec file. + +* Martin Walter +November 16, 2005: Fix a thinko in NFS_SECURITY_HOLE. + +* Martin Kreiner +January 13, 2006: Introduced mount option nfsperms, and removed NFS_SECURITY_HOLE +January 20, 2006: Introduced per-branch nfsro flag, and removed nfsperms + +* Peeka J. Enberg +January 14, 2006: Removed the use of GFP_UNIONFS and replaced it with GFP_KERNEL + +* Amnon Aaronsohn +January 25, 2006: Fixed patch-kernel.sh error msg + +* Konstantin Olchanski +February 8, 2006: Fixed inode_permission (check for nameidata being null) + +* And many more ... diff -urN linux-2.6.17-rc1-mm3-orig/fs/unionfs/COPYING linux-2.6.17-rc1-mm3-new/fs/unionfs/COPYING --- linux-2.6.17-rc1-mm3-orig/fs/unionfs/COPYING 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/unionfs/COPYING 2006-04-11 20:24:21.000000000 +0000 @@ -0,0 +1,373 @@ +This Unionfs-1.0 release is licensed under the terms of the GNU General +Public License (GPL). + +For information on commercial licensing through the SUNY Research +Foundation, contact Erez Zadok . + +Copyright (c) 2003-2006 Erez Zadok +Copyright (c) 2003-2006 Charles P. Wright +Copyright (c) 2005-2006 Josef Sipek +Copyright (c) 2005 Arun M. Krishnakumar +Copyright (c) 2005-2006 David P. Quigley +Copyright (c) 2003-2004 Mohammad Nayyer Zubair +Copyright (c) 2003 Puja Gupta +Copyright (c) 2003 Harikesavan Krishnan +Copyright (c) 2003-2006 Stony Brook University +Copyright (c) 2003-2006 The Research Foundation of State University of New York + +All rights reserved. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + + + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff -urN linux-2.6.17-rc1-mm3-orig/fs/unionfs/ChangeLog linux-2.6.17-rc1-mm3-new/fs/unionfs/ChangeLog --- linux-2.6.17-rc1-mm3-orig/fs/unionfs/ChangeLog 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/unionfs/ChangeLog 2006-04-11 20:24:21.000000000 +0000 @@ -0,0 +1,3624 @@ +2006-04-11 Josef "Jeff" Sipek + + * NEWS, Makefile, Makefile.kernel: Version 1.1.4 + + * unionfs.h: Modified kernel version check to allow <= 2.6.15 and + >= 2.6.9 + +2006-04-11 Junjiro Okajima + + * inode.c (unionfs_permission): Fixed unionfs_permission on reiserfs + and xfs + +2006-04-11 Josef "Jeff" Sipek + + * main.c (parse_dirs_option): On error, NULL the pointers after free + +2006-04-11 Josef "Jeff" Sipek + + * .cvsignore: ignore .malloc_debug.o.cmd + +2006-02-20 Charles P. Wright + + * Makefile: Increment version number. + * INSTALL, NEWS: Add new define options. + +2006-02-20 David P. Quigley + + * man/unionfs.4: removed all references to copyup mount option. + +2006-02-20 David P. Quigley + + * main.c, rename.c, unionfs.h, super.c, unlink.c: Moved + DELETE_WHITEOUT to the default mode for delete and wrapped all + DELETE_ALL code in ifdefs. + * unlink.c (unionfs_unlink_whitout): Rewrote this function to use + unlink/create instead of the atomic rename operation we used before. + * misc/preprocess.pl: added entry for UNIONFS_DELETE_ALL ifdef. + +2006-02-15 Josef "Jeff" Sipek + + * main.c (unionfs_read_super, unionfs_d_alloc_root): Created a "fake" + d_alloc_root + +2006-02-15 Josef "Jeff" Sipek + + * unionfs_debugmacros.h: Cleanup; removed unused __FILE__, etc. + parameters from inline functions; removed unnecessary macro wrappers + + * commonfops.c (unionfs_file_revalidate, unionfs_open): C99 doesn't + allow mixing of code and variable declarations + +2006-02-13 Charles P. Wright + + * file.c, unionfs.h: Use simpler cpp directives. + * misc/preprocess.pl: Simple preprocessor for stripping #ifdefs and + KERNEL_VERSION defines. + +2006-02-12 Josef "Jeff" Sipek + + * branchman.c, commonfops.c, dentry.c, lookup.c, main.c, + persistent_inode.c, super.c, unionfs.h, unionfs_debugmacros.h: + Removed inline objects from unionfs_inode_info, unionfs_dentry_info, + unionfs_sb_info, and unionfs_file_info + + * unionfs_macros.h: Removed inline objects from unionfs_inode_info, + unionfs_dentry_info, unionfs_sb_info, and unionfs_file_info; Replaced + many macros by inline functions + +2006-02-10 Junjiro Okajima + + * dentry.c (unionfs_d_revalidate): Merges two auto variables 'err' and + 'invalid'. And also checks the return value of unionfs_lookup_backend + correctly. It solves the stale inode problem of regression/bug418.sh + + * lookup.c (unionfs_partial_lookup): Same as above + +2006-02-09 David P. Quigley + + * unionfs.h: reverted change + +2006-02-09 David P. Quigley + + * super.c (unionfs_show_options): removed reference to mounter. + + * unionfs.h: removed reference to mounter. + +2006-02-08 Josef "Jeff" Sipek + + * dirhelper.c (delete_whiteouts): Use WHLEN instead of 4 + + * persisten_inode.c (__fread, __fwrite): Make sparse happy + + * unionfs.h, unionfs_debugmarcros.h (__dtopd): Misc cleanups + +2006-02-08 Konstantin Olchanski + + * inode.c (inode_permission): nameidata can be null, add checks for it + +2006-02-08 David P. Quigley + + * copyup.c (copyup_permissions): Removed code referencing the + copyup=mounter option. + * main.c (unionfs_parse_options): same as above + * unionfs.h (unionfs_sb_info): same as above. + +2006-02-07 David P. Quigley + + * INSTALL: Added fistdev.mk description for UNIONFS_IMAP. + +2006-02-07 David P. Quigley + + * copyup.c (copyup_named_dentry,create_parent_named): ifdefed + persistent inode code. + * dirfops.c (unionfs_filldir): Same as first. + * main.c (unionfs_parse_options): Same as first. + * persistent_inode.c: ifdefed out entire file. + * super.c (unionfs_put_super): Same as two lines up. + * unionfs.h: ifdefed persistent inode variables from unionfs_sb_info + and the externed functions. + + +2006-02-05 Josef "Jeff" Sipek + + * copyup.c (copyup_named_dentry): Any error, not only ENOSPC/EDQUOT, + should unlink the partial copyup + + * ChangeLog: Clarified the meaning of a changelog entry + +2006-02-05 Josef "Jeff" Sipek + + * main.c (parse_dirs_option): Added a check for people attempting to + make the left most branch read-only + +2006-02-05 Josef "Jeff" Sipek + + * copyup.c (copyup_named_dentry): If the copyup failed because of + quota or lack of disk space, unlink the partial copyup + + * rename.c (unionfs_rename_all, unionfs_rename_whiteout): Fixed rename + which was copying up to all rw branches to the left of the source + +2006-02-05 David P. Quigley + + * unionimap.c: we shouldn't be using such a complex open + statement when the creat function is exactly what we are doing + with open + +2006-02-05 Junjiro Okajima + + * dirfops.c (unionfs_filldir): With current get_uin() is hard to + distinguish the error from success. Writing persistent ino files + may meet the file size limit. Now, users should be careful about + filesystem quota. + + * main.c (unionfs_interpose): See above + + * persistent_inode.c (__fread, __fwrite, parse_imap_option, __get_uid, + __read_uin, __write_uin, get_uin, read_uin): See above + + * unionfs.h: See above + +2006-02-05 David P. Quigley + + * unionimap.c: Modified open statements to create the files + with u+wr instead of u+wrx + +2006-02-04 Josef "Jeff" Sipek + + * README: Remove reference to 2.4 as we don't support it anymore + +2006-02-04 David P. Quigley + + * debug_malloc.c: Moved all malloc debugging functions to this file. + + * Makefile: added debug_malloc.o to object list. + + * persistent_inode.c (remove_map): removed a decrement that wasent + needed. + +2006-02-04 Josef "Jeff" Sipek + + * inode.c (unionfs_create): Fixed uid/gid/mode not being reset when + file is created by renaming a whiteout + +2006-02-02 Junjiro Okajima + + * inode.c (unionfs_create): Fixed minor bug regarding stale atime and + mtime + +2006-02-01 David P. Quigley + + * unionfs.h: Removed usi_fsnum_table since its not used and makes + no sense to have. + + * persistent_inode.c: Removed usi_fsnum_table code. Added remove_map + + * unionimap.c: Fixed code that prints forward maps since it was always + printing 0 for the inode number. + +2006-01-31 Junjiro Okajima + + * dentry.c (unionfs_d_revalidate): Get the hidden inode, not the + hidden dentry's inode; fixes "pseudo hardlink" via persistent inode + +2006-01-25 Josef "Jeff" Sipek + + * Makefile, Makefile.kernel: Incremented version + + * NEWS, Makefile, Makefile.kernel: Version 1.1.2 + +2006-01-25 Amnon Aaronsohn + + * patch-kernel.sh: Fixed patch-kernel.sh error msg + +2006-01-24 Junjiro Okajima + + * subr.c (create_whiteout_parent): Hidden inode is not iput() since + ibstart and ibend is not updated. + +2006-01-22 Junjiro Okajima + + * copyup.c (copyup_named_dentry, create_parents_named): Fixed + persistant inode code: link, rmdir, shrinking of dcache, map validation + + * main.c (copyup_xattrs): Fixed persistant inode code: link, rmdir, + shrinking of dcache, map validation + + * persistent_inode.c (__fread, __fread, verify_forwardmap, + verify_reversemap, init_imap_data, parse_imap_option, __get_uin, + __write_uin, get_uin, write_uin): Fixed persistant inode code: link, + rmdir, shrinking of dcache, map validation + + * unionfs.h: Fixed persistant inode code: link, rmdir, shrinking of + dcache, map validation + +2006-01-20 Martin Kreiner + + * BUGS, INSTALL, man/unionctl.8, man/unionfs.4: Documentation update + + * branchman.c (unionfs_ioctl_addbranch, unionfs_ioctl_rdwrbranch): + Per branch nfsro option + + * inode.c (inode_permission): Per branch nfsro option + + * main.c (parse_dirs_option, unionfs_parse_options): Per branch nfsro + option + + * super.c (unionfs_show_options): Per branch nfsro option + + * unionctl.c (__usage, parse_rw, parse_options, dump_branches, main): + Per branch nfsro option + + * unionfs.h: Per branch nfsro option + +2006-01-20 Josef "Jeff" Sipek + + * branchman.c (unionfs_ioctl_addbranch, unionfs_ioctl_delbranch): + Inode refcount debugging tool calls + + * copyup.c (create_parents_named):Inode refcount debugging tool calls + + * dentry.c (unionfs_d_revalidate, unionfs_d_iput):Inode refcount + debugging tool calls + + * inode.c (unionfs_link): Inode refcount debugging tool calls + + * main.c (unionfs_interpose, unionfs_reinterpose, unionfs_igrab, + unionfs_iput, unionfs_iget): Inode refcount debugging helper code; + Fixed init of atomic_t + + * subr.c (create_whiteout_parent, unionfs_refresh_hidden_dentry): + Inode refcount debugging tool calls + + * super.c (unionfs_clear_inode): Inode refcount debugging tool calls + + * unionfs.h: Definitions for IGET, IGRAB, and IPUT + + * match-iget.pl: Inode refcount debugging code output "matcher" + +2006-01-17 Josef "Jeff" Sipek + + * main.c, unionfs.h: Added #if'd kzalloc for kernels older than 2.6.14 + +2006-01-14 David P. Quigley + + * branchman.c: Replaced pairs of KMALLOC and memset calls to + KZALLOC calls. + + * commonfops.c: Replaced pairs of KMALLOC and memset calls to + KZALLOC calls. + + * copyup.c: Replaced pairs of KMALLOC and memset calls to + KZALLOC calls. + + * main.c: Replaced pairs of KMALLOC and memset calls to + KZALLOC calls. + + * persistent_inode.c: Replaced pairs of KMALLOC and memset calls to + KZALLOC calls. + + * super.c: Replaced pairs of KMALLOC and memset calls to + KZALLOC calls. + + +2006-01-14 Josef "Jeff" Sipek + + * unionfs.h: Fixed unionfs_kmalloc prototype + + * main.c (unionfs_kzalloc, unionfs_kmalloc): Use kzalloc, not + the non-existent kzmalloc + +2006-01-14 David P. Quigley + + * AUTHORS: added entry for Peeka J. Enberg + +2006-01-14 Peeka J. Enberg + + * ALL: Changed all use of GFP_UNIONFS to GFP_KERNEL + + * unionfs.h: Removed definition for GFP_UNIONFS + +2006-01-14 David P. Quigley + + * unionfs.h : Included defines for KZMALLOC and extern for + unionfs_kzalloc. NOTE: This change makes the minimum kernel + version for unionfs 2.6.14. + + * main.c (unionfs_kmalloc): Changed prototype to use the + actual type of GFP_KERNEL instead of int and removed a + (void *) since the kernel coding conventions say that this + is not necessary. + + * main.c (unionfs_kzalloc): New wrapper function to track + kzallocs when debugging. + +2006-01-13 Josef "Jeff" Sipek + + * BUGS, INSTALL: Removed all references to NFS_SECURITY_HOLE + +2006-01-13 Martin Kreiner + + * inode.c (inode_permission): Introduce nfsperms mount option and + remove NFS_SECURITY_HOLE + + * main.c (unionfs_dentry_info): Introduce nfsperms mount option and + remove NFS_SECURITY_HOLE + + * super.c (unionfs_show_options): Introduce nfsperms mount option and + remove NFS_SECURITY_HOLE + + * unionfs.h:Introduce nfsperms mount option and + remove NFS_SECURITY_HOLE + +2006-01-12 Josef "Jeff" Sipek + + * fist.h: Code moved to unionfs.h, unionfs_macros.h, and + unionfs_debugmacros.h + + * ALL, misc/*.c: Removed all references to fist.h + +2006-01-09 Josef "Jeff" Sipek + + * rename.c (__rename_all{,_unlink,_revert,_clobber}): Make functions + static + +2006-01-08 Josef "Jeff" Sipek + + * rename.c (unionfs_rename_all,lookup_whiteout): Split up the + nearly 300 line unionfs_rename_all function into several more + understandable "double underscore" functions; removed get_whname + it used __getname which allocates a whole page, updated + lookup_whiteout to use alloc_whname instead + + * lookup.c (unionfs_lookup_backend): Use this should be the last + alloc_whname patch + +2006-01-07 Josef "Jeff" Sipek + + * file.c: Use unlocked_ioctl iff the kernel is 2.6.11 or newer + (unionfs_main_fops) + + * dirfops.c: Use unlocked_ioctl iff the kernel is 2.6.11 or newer + (for unonfs_dir_fops) + + * commonfops.c (unionfs_ioctl): Use unlocked_ioctl iff the kernel + is 2.6.11 or newer, the prototype for unionfs_ioctl is also different + on 2.6.11 or newer + + * inode.c (inode_permission): If kernel is older than 2.6.10, use + vfs_permission otherwise generic_permission + +2006-01-03 David P. Quigley + + * ChangeLog : fixed two gramatical errors in the changelog. + +2006-01-03 David P. Quigley + + * unionfs.h: Added a define for the first valid inode number so we + aren't using magic numbers in the persistent inode code. + + * super.c: added a call to cleanup_imap_data to properly free + resources on unmount. + + * persistent_inode.c: (imap_parse_options): cleanedup code, + (init_imap_data): new function (cleanup_imap_data): new + function. + + * unionimap.c : cleaned up some brackets from single line if + statements. + +2006-01-03 Josef "Jeff" Sipek + + * unionfs.h: Renamed make_whname to alloc_whname, use strlcat instead + of strncat (the strcpy is safe since WHPFX will always be NULL + terminated), the NULL termination is dony by strlcat + + * inode.c (unionfs_create, unionfs_link, unionfs_symlink, + unionfs_mkdir, unionfs_mknod): Renamed make_whname to alloc_whname + + * rename.c (do_rename): Renamed make_whname to alloc_whname + + * subr.c (create_whiteout, create_whiteout_parent): Renamed + make_whname to alloc_whname + + * unlink.c (unionfs_unlink_whiteout): Renamed make_whname to + alloc_whname + +2006-01-03 Josef "Jeff" Sipek + + * branchman.c (unionfs_ioctl_branchcount, unionfs_ioctl_addbranch, + unionfs_ioctl_rdwrbranch, unionfs_ioctl_queryfile): Make sparse happy + + * commonfops.c (unionfs_ioctl): Make sparse happy + + * copyup.c (copyup_named_dentry): Make sparse happy + + * file.c (unionfs_read, unionfs_write): Make sparse happy + + * inode.c (unionfs_readlink, unionfs_follow_link): Make sparse happy + + * main.c (unionfs_read_super): Make sparse happy + + * persistent_inode.c (verify_forwardmap, verify_reversemap, get_uin, + get_lin): Make sparse happy + +2006-01-02 Josef "Jeff" Sipek + + * unionfs.h: added make_whname to replace scattered and duplicate + code that allocates memory, copies into it WHPFX and the rest of + the filename + + * inode.c (unionfs_create, unionfs_link, unionfs_symlink, + unionfs_mkdir, unionfs_mknod): Use make_whname instead of manually + allocating, and copying data + + * rename.c (do_rename): Use make_whname instead of manually + allocating, and copying data + + * subr.c (create_whiteout, create_whiteout_parent): Use make_whname + instead of manually allocating, and copying data + + * unlink.c (unionfs_unlink_whiteout): Use make_whname instead of + manually allocating, and copying data + +2006-01-02 Josef "Jeff" Sipek + + * ALL: Copyright year updated + + * man/*: Date updated + + * misc/split-views-2.4.26.patch: removed because 2.4 is not supported + +2006-01-01 Josef "Jeff" Sipek + + * rename.c (do_rename): use WHLEN+1 instead of 5 + +2005-12-30 Josef "Jeff" Sipek + + * rename.c (do_rename): Added two missing DPUT()s + + * subr.c (create_whiteout_parent): use LOOKUP_ONE_LEN instead of + lookup_one_len + +2005-12-29 David P. Quigley + + * commonfops.c, copyup.c, main.c, print.c, super.c: converted FISTBUG + commands to printk and BUG pairs. + * fist.h: removed definition for FISTBUG + +2005-12-29 Josef "Jeff" Sipek + + * lookup.c (unionfs_lookup_backend): Use WHLEN+1 instead of 5 + + * unionfs_debugmacros.h (__ftohf_index, __set_ftohf_index, __set_itohi_index, + __set_dbend, __set_dbstart, __set_dbopaque, __dtohd_index): BUG_ON with a more + complex condition is more optimal than "if(partial_condition) BUG_ON(...);" + +2005-12-29 Junjiro Okajima + + * lookup.c (unionfs_lookup_backend): DPUT() when done with the dentry + not before, added missing DPUT() + +2005-12-28 Charles P. Wright + + * AUTHORS: Some maintenance dates. + +2005-12-28 Josef "Jeff" Sipek + + * AUTHORS: updated to reflect the patches commited in the last two days + +2005-12-28 Junjiro Okajima + + * lookup.c (unionfs_lookup_backend): Fixed missing DPUT()s + +2005-12-28 Junjiro Okajima + + * lookup.c: change the process's privilege temporally when + creating/searching/deleting the whiteouts (in is_opaque_dir,) and + forgot DPUT() after failing is_opaque_dir() (in unionfs_lookup_backend) + +2005-12-27 Junjiro Okajima + + * subr.c (create_whiteout): create whiteout bug, forgotten dput() + +2005-12-27 Junjiro Okajima + + * unlink.c (unionfs_unlink_whiteout): unlink bug, forgotten dput()s + +2005-12-27 Junjiro Okajima + + * inode.c (unionfs_create): create bug, forgotten dput(), extra GET_PARENT + +2005-12-27 Josef "Jeff" Sipek + + * lookup.c (new_dentry_private_data): Use GFP_ATOMIC instead + of GFP_UNIONFS (which currently is same as GFP_KERNEL) to prevent + sleeping while atomic bug + +2005-12-27 Junjiro Okajima + + * inode.c (unionfs_permission): permission bug, creat/open truncates + the running executable + +2005-12-27 David P. Quigley + + * unionctl.c : Fixed check to see if a branch was already in the union and + fixed --before and --after logic. + + * regression/branchman.sh: BUG370 will not work anymore due to code to avoid + duplicate branches being added. Need to reevaluate if it should be kept + anymore. + +2005-12-27 David P. Quigley + + * *.[ch]: Went through every file and replaced ASSERT and ASSERT2 with + BUG_ON calls. The logic for BUG_ON is the opposite of ASSERT but I believe + they are all converted properly. + + * fist.h: Removed definitions for ASSERT and ASSERT2 + +2005-12-27 David P. Quigley + + * *.[ch]: Went through every file and removed PASSERT and PASSERT2 + statments. If there was an if that inclosed it then that was removed + also. + + * fist.h: Removed definitions for PASSERT and PASSERT2. + +2005-12-27 Junjiro Okajima + + * copyup.c (copyup_permissions): Properly copyup atime, mtime, and + ctime. + +2005-12-27 Josef "Jeff" Sipek + + * inode.c (unionfs_lookup): Bugfix for bug #451 is not valid, + change reverted + +2005-12-20 Josef "Jeff" Sipek + + * lookup.c (new_dentry_private_data): Use SLAB_ATOMIC instead + of SLAB_KERNEL (prevent sleeping while atomic bug) + +2005-11-30 Josef "Jeff" Sipek + + * main.c: Updated module info (now includes unionfs version + number) + +2005-11-29 Arun M. Krishnakumar + + * branchman.c: Added check for addition of branches with + overlapping paths. Fixes rest of Bug #374. + +2005-11-29 Arun M. Krishnakumar + + * unionctl.c: Fixed branch addition with just branch (and + neither mode nor before/after specified. + +2005-11-29 Charles P. Wright + + * Fix Coverity flagged errors. + +2005-11-29 Arun M. Krishnakumar + + * main.c : Corrected a boundary case, so that one cannot + use "/" as a branch if /ro is a branch. + +2005-11-28 Arun M. Krishnakumar + + * main.c : Ensure that the branches getting added during a + mount operation do not have overlapping branch paths. This + is part of Bug #374. + +2005-11-28 Arun M. Krishnakumar + + * inode.c: unionfs_lookup was not incrementing the reference + count of the dentry. this was causing the chmod bug. added + this, and fixed bug #451 + +2005-11-26 Arun M. Krishnakumar + + * unlink.c: Updated unionfs_unlink_whiteout to fix bug #434 + +2005-11-24 Charles P. Wright + + * print.c: Remove unused function. + + * Makefile: Don't duplicate source list. + +2005-11-23 Charles P. Wright + + * unionimap.c: Fix unchecked malloc. + + * usercommon.c: Fix double free. + +2005-11-20 Arun M. Krishnakumar + + * global : Changes made for "sparse" + +2005-11-18 Josef "Jeff" Sipek + + * rename.c (get_whname): Make constant's type clearer + +2005-11-18 Arun M. Krishnakumar + + * unionctl.c : Changes made for "sparse" + +2005-11-18 Josef "Jeff" Sipek + + * rename.c (get_whname): Add NULL termination. + +2005-11-18 Shaya Potter + + * rename.c (lookup_whiteout): Add NULL Check. + +2005-11-18 Charles P. Wright + + * Remove UNIONFS_XATTR define, because 2.6 has consistent prototypes. + +2005-11-16 Martin Walter + + * inode.c: Fix IS_NFS. + +2005-11-15 Charles P. Wright + + * main.c: Allow debug= to be passed to the Unionfs module. + +2005-11-11 Allessio Curri + + * rpm/unionfs.spec: Update RPM spec file to include unionimap. + +2005-11-09 Charles P. Wright + + * commonfops.c (unionfs_open): Slightly rework reader/writer locks. + +2005-10-24 Shaya Potter + + * Fix scope of readers/writer locks on branch configuration. + +2005-11-09 Charles P. Wright + + * unlink.c: Add comment w/ Junjiro's rmdir fix. + +2005-09-26 Junjiro Okajima + + * dentry.c (unionfs_d_revalidate): Don't copy attributes to nonexistent + inodes. + + * lookup.c (new_dentry_private_data): Fix a race b/t lookup and d_free. + +2005-11-08 Charles P. Wright + + * Makefile: use /lib/modules/`uname -r`/kernel/fs/unionfs/unionfs.ko + to be consistent with other file systems. + + * patch-kernel.sh: Add more double-patching checks. + + * Use WHPFX and WHLEN instead of ".wh." and 4. + + * dirhelper.c (delete_whiteouts): Remove useless partial lookup. + * unlink (unionfs_rmdir_all): Remove useless partial lookup. + + * subr.c (create_whiteout): Silently succeed if the whiteout already + exists. + * inode.c (unionfs_unlink_all): Fix some coding style. + +2005-11-07 David P. Quigley + + * unionctl.c (parse_options): Added a check in to see if a branch + already exists in the union and if it does the operation fails. + +2005-11-07 Charles P. Wright + + * dentry.c (dentry_revalidate): Remove extra d_deleted check. + * commonfops.c (unionfs_open): Undo add debug print of opened dentry. + + * commonfops.c (unionfs_open): Add debug print of opened dentry. + +2005-10-26 Charles P. Wright + + * inode.c (inode_permission): Fix typo in NFS_SECURITY_HOLE. + +2005-11-06 Josef "Jeff" Sipek + + * fist.h (lock_parent, unlock_dir): moved from now non-existent + missing_vfs_funcs.h + * inode.c (unionfs_create): use {,un}lock_rename instead of + double_{,un}lock + * rename.c (do_rename): use {,un}lock_rename instead of double_{,un}lock + * unlink.c (unionfs_unlink_whiteout): use {,un}lock_rename instead of + double_{,un}lock + * missing_vfs_funcs.h: removed + +2005-10-25 Charles P. Wright + + * Makefile: Increment version to 1.1.2pre. + * regression/readdir.sh: readdir regression script. + It doesn't reproduce the bug for us, but extra tests can't hurt. + + * dirfops.c (unionfs_readdir): Properly update uds_dirpos, + which fixes directory reading operations. + +2005-10-24 Charles P. Wright + + * compat.[ch]: Remove old files. + +2005-10-24 Shaya Potter + + * copyup.c (copyup_file): Don't fput errors. + * copyup.c, unionfs.h: Use loff_t for copyup size. + + * copyup.c (copyup_named_dentry): Code cleanup. + +2005-10-21 Charles P. Wright + + * AUTHORS: Update AUTHORS. + +2005-10-21 Charles Duffy + + * xattr.c: Use ssize_t for xattr functions. + +2005-10-20 Shaya Potter + + * dirhelper.c (check_empty): Respect opaqueness. + + * Replace lock_super around branch management operations with a + Unionfs read/write semaphore. This will allow branchput/branchget + to operate concurrently, but prevent them from racing against + branchman operations. + +2005-10-19 Charles P. Wright + + * inode.c (unionfs_permission): Don't call normal permission before + inode_permission (our modified version of permission), because it + just duplicates work. + + * commonfops.c (branchput_gen): Lock the super when we read putmaps. + + * inode.c (inode_permission): If NFS_SECURITY_HOLE is defined + treat -EACCESS as if we should fall back on inode_permission. + + * branchman.c (unionfs_ioctl_rdwrbranch): Make super/dentry + lock/unlock symmetric. + +2005-10-18 David P. Quigley + + * NEWS: Updates NEWS file for release + +2005-10-14 Arun M. Krishnakumar + + * rename.c : Used the create_whiteout_parent function to + create the whiteout in unionfs_rename_all. + * subr.c : Brought the create_whiteout_parent back. These + squash Bug #442. + +2005-10-13 Arun M. Krishnakumar + + * rename.c : Fixed rest of Bug #425. Applied the patch + sent in by Junjiro Okajima. + +2005-10-13 David P. Quigley + + * rmdir-all.sh: added test for bug 430. Not sure if + we are going to patch it yet but if we do the test + is there. + * regression/Makefile: added rmdircheckinode.c + * regression/progs/rmdircheckinode.c: program to check + if the inode numbers match after a failed rmdir. + +2005-10-13 Arun M. Krishnakumar + + * rename.c (unionfs_rename) : Fixed bug #425. The new_dentry + affects the unlink called by "mv" after the rename has failed. + It makes unlink return with -EISDIR. (partial fix) + +2005-10-11 Shaya Potter + + * dentry.c (unionfs_d_revalidate): Fix a deadlock. + +2005-10-06 Robert Glowczynski + + * file.c (unionfs_fsync): Fix check for NULL lower-level operation. + +2005-09-29 Patrik Weiskircher + + * Makefile.kernel: Remove reference to locks.c. + +2005-09-28 Junjiro Okajima + + * unlink.c (unionfs_rmdir_all): Always create whiteouts on directory + removal. + +2005-09-28 Charles P. Wright + + * commonfops.c (unionfs_file_revalidate): Properly update + generation when we combine copyup and reopening. + * Makefile: Remove some 2.4 cruft. + +2005-09-27 Charles P. Wright + + * branchman.c (unionfs_ioctl_incgen): Fix print indentation bug. + + * unionimap.c: AMD64 fix from Gentoo. + +2005-09-27 David P. Quigley + + * inode.c: Made change to use both inode_permissions and + permissions. + +2005-09-27 Charles P. Wright + + * INSTALL: Document MODDIR. + +2005-09-23 Klaus Knopper + + * inode.c: added inode_permissions to check for permissions + on a file even if its on an ROFS and used it in + unionfs_permissions + +2005-09-22 Josef "Jeff" Sipek + + * Makefile: remove 'tags' target dependency from 'all' + +2005-09-21 David P. Quigley + + * Makefile: forgot to remove locks.c from source list + +2005-09-21 Junjiro Okajima + + * inode.c: Fixed several off-by-one kmalloc bugs. + +2005-09-20 David P. Quigley + + * removed locking code from codebase since we no longer + handle locking (vfs will do it) + +2005-09-18 Josef "Jeff" Sipek + + * removed 2.4 code from fist.h, inode.c, lookup.c, + persistent_inode.c, rdstate.c + +2005-09-18 David P. Quigley + + * removed 2.4 code from copyup.c, dentry.c, dirfops.c + and file.c + +2005-09-18 Josef "Jeff" Sipek + + * removed 2.4 code from stale_inode.c, subr.c, unionfs.h, + unlink.c, xattr.c + +2005-09-17 Josef "Jeff" Sipek + + * removed 2.4 code from main.c, print.c, super.c, + xattr.c + +2005-09-17 David P. Quigley + + * removed 2.4 code from branchman.c, commonfops.c + +2005-09-16 Charles P. Wright + + * Makefile.kernel: Use EXTRA_CFLAGS. + + * Makefile: Use ${LD} instead of ld, for cross compilation. + +2005-09-15 David P. Quigley + + * Makefile: Updated to 1.1.0pre + * Makefile.kernel: Updated to 1.1.0pre + +2005-09-15 David P. Quigley + + * NEWS: Updated for release. + * Makefile: updated for release. + * Makefile.kernel: updated for release + +2005-09-12 David P. Quigley + + * locks.c: Removed locking code since its broken big define 0 around + it + * file.c: Removed locking references have the vfs handle it. + * unionfs.h: fixed bug with 2.4 compilation. + +2005-09-08 Charles P. Wright + + * main.c: Remove comment leftover from templates that doesn't make + sense in current context. + +2005-09-05 Charles P. Wright + + * commonfops.c: Remove lower fput debug printks. + +2005-09-02 Shaya Potter + + * file.c (unionfs_mmap): Fix flag checking for mmap. + +2005-09-01 Charles P. Wright + + * We shouldn't use d_delete, vfs_unlink already does it. The only + known remaining 2.6.13 issue is the unionfs_lock (flock.sh in the + regression suite). + + * inode.c,stale_inode.c: Support for 2.6.13's new follow_link + prototype. However, many of the regression tests fail for + unrelated reasons (or at least I think they are unrelated). + + * unionctl.c: Allow --FOO to go before the union specifier (e.g., + unionctl --list /mnt/unionfs now works). + * lookup.c: Don't partial lookup the root dentry. + + * copyup.c: Check for permission setting errors. + +2005-08-31 Charles P. Wright + + * lookup_one_len never returns NULL. + +2005-08-30 Charles P. Wright + + * inode.c (unionfs_permission): Simplified code. + + * inode.c (unionfs_link): Copy directory attrs to directory + (BUG391). Unfortunately, regression test doesn't quite catch the + bug because of a revalidate for the stat. + * commit: Don't complain about .sh file's indentation. + +2005-08-29 Charles P. Wright + + * INSTALL: tmpfs doesn't support fsync on directories + + * Makefile: Update release target for new directory structure. + + * patch-kernel.sh: Depend on Experimental, and move configuration + option to File Systems -> Miscellaneous file systems at the end, + not as the very first file system. Add UNIONFS_VERSION define. + Use tail -n +7 instead of tail +7. + + * file.c (unionfs_fsync): Don't use dtohd on unlocked dentries. + +2005-08-28 Charles P. Wright + + * inode.c (unionfs_link): Don't use unionfs_interpose with + INTERPOSE_LINK any more, because only one line of the whole + function was being used anyway. This fixes an inode refcount leak + in unionfs_link, so the regression suite now passes with the new + locking, without any leaks. + + * commonfops.c (unionfs_file_release): Update rdstate access time when + saving it in the inode (so that it won't be so quickly discarded). + +2005-08-26 Arun M. Krishnakumar + + * dirfops.c (unionfs_readdir) : removed the changes made + for the special way in which vfs_llseek was handles for + Reiser4, as Reiser4 introduced a patch which made it + behave properly. + +2005-08-26 Charles P. Wright + + * Passes regression tests. + + * Locking for create_parents. Regression tests from branchman to + open.sh PASS. + + * Basic inode operations pass sniff test with new locking (regression + tests not yet tried). + + * Dentries need locking, and there isn't much way around it.This + snapshot adds some untested locking, and you won't want to use it + yet. The basic principles are: + 1. As soon as a VFS operation that touches a dentry is entered, + the dentry should be locked. + 2. The lock ordering is: + Children before parents + Two children are tie broken with their address + There are several functions not done yet, most notably create_parents, + because it is going to require more thought (we walk up the parent + list and then back down it). This in part is why children need to go + before parents (also revalidate walks up the parent list). + +2005-08-25 David P. Quigley + + * xattr.c: unused label causing a warning and inturn an error. + +2005-08-25 Charles P. Wright + + * main.c (interpose,reinterpose): Interposition should lock the dentry. + + * file.c: CodingStyle, and we don't need to check ftopd before ftohf. + * file.c (unionfs_mmap): We were checking our file instead of the lower + -level file for having valid operations. + * dirfops.c,dirhelper.c: CodyingStyle + + * copyup.c: There is no need for _len at the end of functions, because + other versions don't exist any longer. + + * lookup.c (unionfs_lookup_backend): Lock the dentry private data + for the whole lookup routine. + * branchman.c: CodingStyle fixes. + * rdstate.c, super.c: Use KERN_ERR if we have unfreed objects. + +2005-08-25 Charles P. Wright + + * INSTALL: Fix losetup instructions. + +2005-08-24 Charles P. Wright + + * print.c: Avoid kmallocs. + * rdstate.c,dirfops.c: Code style cleanups. + +2005-08-24 Anton Farygin + + * dentry.c (d_revalidate): Lock inode when freeing lower level ones. + +2005-08-24 David P. Quigley + * unionfs.h: Updated forwardmap version and added a new data + structure bmapent.(Later on these should be unified into one header + rather than being in 2 header files). + * persistant_inode.c: Updated code to use bmapents now. + +2005-08-24 David P. Quigley + + * unionimap.c: Added code to ensure you dont add a filesystem with + the same fsid twice (no duplicate entries in the maps). + * unionimap.h: Added a new data structrue bmapent. + * The code in the kernel to read the maps hasent been changed so it + doesent work yet. Check back soon for that. + +2005-08-24 Charles P. Wright + + * inode.c (unionfs_create): Use proper permissions when recreating + a deleted file (BUG383). + +2005-08-23 Charles P. Wright + + * commonfops.c (copyup_deleted_file): Fix leak of name on subsequent + loop iterations. + + * commonfops.c (copyup_deleted_file): Cleanup of major loop. + + * unionfs.h (DPUT,KFREE): Don't try to free errors. + + * main.c (unionfs_reinterpose): Remove d_unhashed assertion. + * unionfs.h (d_deleted): Create an inline function to tell + if a directory is deleted (i.e., unhashed and not the root). + +2005-08-23 Charles P. Wright + + * main.c (unionfs_parse_options): More checking for copyupmode. + +2005-08-22 Charles P. Wright + + * unionfs.h (get_nlinks): More intelligent link counting. + + * inode.c (unionfs_create): Don't dput an IS_ERR. + + * unionfs.h: sbstart is always zero + * main.c (unionfs_read_super): check_branch does existence checking + + * unionfs_debugmacros.h: Fix stray printks. + + The following fixes are so that the regression suite runs without + any memory or dentry leaks: + * branchman.c (unionfs_ioctl_addbranch): Zero newly allocated + arrays. + * copyup.c (unionfs_create_named_dirs): Fix leak of path stack. + * main.c: Handle get_parent. + * lookup.c: Make the logic to put preceding negative dentries a + function. + * unlink.c (unionfs_unlink_all): Fix dentry reference count leak. + * inode.c (unionfs_link): Properly handle lock_ and unlock_dir. + * match-dget.pl: Handle DS and DD for set and unset records. + +2005-08-20 Charles P. Wright + + * INSTALL: sendfile conflicts w/ Unionfs. + +2005-08-19 Arun M. Krishnakumar + + * dirfops.c (unionfs_readdir) : changed the way the + return value of the repeated vfs_llseek (with origin + =1) is used. this is to allow readdir in Reiser4, + which returns -ENOENT (!!!) for the llseek in same + cases. this is for Bug #358 + +2005-08-18 Charles P. Wright + + * match-dget.pl: Insert both gets and puts into list of actions + for unreleased dentries. + + * match-dget.pl: Dget matching script. + * Use DGET, DPUT, DENTRY_OPEN, and LOOKUP_ONE_LEN to + record when we dget and dput dentries. + +2005-08-17 Charles P. Wright + + * match-malloc.pl: Return number of errors. + + * print.c (fist_print_generic_dentry): Print inode number. + + * copyup.c (unionfs_create_named_dirs): Fix dentry reference leak. + +2005-08-15 Charles P. Wright + + * print.c (fist_print_file): Divide into generic and Unionfs halves, + ASSERT if we are passed a lower-level file. + + * file.c (unionfs_lock): Handle write locks on r/o branches. + +2005-03-03 Anton Farygin + + * dentry.c (unionfs_d_revalidate): Fix possible double unlock. + + * branchman.c (unionfs_ioctl_incgen): Fix possible deadlock when + memory allocation fails. + +2005-08-12 Arun M. Krishnakumar + + * rename.c, commonfops.c : removed the + dget/dput functions sandwiching the vfs_unlink as + these raise the refcounts and cause the .nfsXYZ + files to get created after unlinks. This knocks + out Bug#364 for 2.6. + +2005-08-11 Charles P. Wright + + * main.c (unionfs_parse_dirs): Prevent recursive Unionfs mounts. + + * branchman.c: Plug a few memory leaks. + +2005-08-11 Arun M. Krishnakumar + + * dentry.c (unionfs_d_release) : fixed a case that would + cause a reference to the lower-level nfs inode to be + present. This is part of #364. + +2005-08-11 Charles P. Wright + + * unionctl.c: Support --mode ro branch or --mode branch ro + + * branchman.c: Only count putmaps that exist. + +2005-08-10 Charles P. Wright + + * man/unionctl.8: No need for warning about root directory anymore. + * commonfops.c: Kill branch deletion ioctl. + + * super.c: Magic MS_REMOUNT to remove a branch. + * unionctl.c: Use remount instead of ioctl to remove a branch. + * branchman.c (unionfs_ioctl_delbranch): Take the super, not an inode. + +2005-08-10 Charles P. Wright + + * dentry.c (unionfs_d_revalidate): All exit paths should go through + out. + + * main.c (unionfs_parse_options,unionfs_parse_dirs): Simplified + option parsing using strsep rather than direct pointer manipulation. + Organized the options into types to factor out common code. + +2005-08-09 Charles P. Wright + + * debian/*: Use official debian packaging files. + + * Some AMD64 fixes. + * vprintk wrapper for 2.4. + +2005-08-09 Jan Engelhardt + + * unionctl.c: Do not truncate "/" to "" when stripping the last "/" + from directories. + +2005-08-05 Charles P. Wright + + * main: Use FS_REVAL_DOT so that directories are always revalidated + during lookup. + + * Drop err=passup, as it is unmaintained. + +2005-08-05 Arun M. Krishnakumar + + * commonfops.c (unionfs_flush) : changed the dput to give + more symmetry and readability to the code. + +2005-08-04 Arun M. Krishnakumar + + * dentry.c (unionfs_d_revalidate) : return successfully if the + dentry is unhashed + * commonfops.c (unionfs_flush) : corrected the d_unhashed check + +2005-08-04 Charles P. Wright + + * branchman.c (QUERY): Don't copy FD_SET before ioctl. FD_ZERO the + set before the call. + + * branchman.c (newputmap): Subtract the count from old putmaps from + the brand new putmap's count. + + * branchman.c (unionfs_ioctl_delbranch): Use putmaps. + * lookup.c (new_dentry_private_data): Fix branch removal when + transitioning from > UNIONFS_INLINE_OBJECTS to == + UNIONFS_INLINE_OBJECTS. + +2005-08-03 David P. Quigley + + * unionfs.h,main.c,super.c,inode.c: removed all code + pertaining SETATTR_ALL since the feature is not necessary + and complicates things. + * man/unionfs.4: removed text pertaining to the setattr + mount option. + +2005-08-03 Charles P. Wright + + * unionfs.h (putmap): We need to keep track of the mapping between + branch numbers for older generation numbers and the current + generation so that we can properly branchput. + * branchman.c: Add functions to manipulate putmaps. + * commonfops.c: Use putmaps for revalidation and close. + +2005-08-03 Arun M. Krishnakumar + + * dentry.c (unionfs_d_release) : added a check to take care + of the case when the dentry coming into release is from a + failed lookup. This knocks bug #303 out. + +2005-08-03 Charles P. Wright + + * Remove older non-opaque directory mode. + +2005-08-01 Arun M. Krishnakumar + + * lookup.c (unionfs_lookup_backend): Skip those hidden + dentries that are NULL. + +2005-08-01 Jan Engelhardt + + * print.c (fist_dprint_internal): Use vprintk instead of + vsnprintf/printk combo. + +2005-08-01 Charles P. Wright + + * unionfs_debugmacros.h (branchget,branchput): + ASSERT if a branch's count goes negative. + * super.c (unionfs_put_super): ASSERT if the branch + counts are not zero on unmount. + * commonfops.c (unionfs_file_release): Don't put branches + that we didn't get. + * commonfops.c (unionfs_open): Don't get a branch until + we open it. + + * commonfops.c: Fix generation number increment. + +2005-07-26 David P. Quigley + + * main.c: cleaned up parse_options code for copyup, + copyupuid,copyupgid,copyupmode. + +2005-07-26 Charles P. Wright + + * unlink.c, main.c, rmdir.c, rename.c: Remove obsolete + and unmaintained DELETE_FIRST mode. + +2005-07-26 David P. Quigley + + * man/unionfs.4: Added default behavior into the options + section for entries that didnt already have it. + +2005-07-26 Charles P. Wright + + * main.c (unionfs_parse_options): Move directory parsing + out of parse options function, so we don't have as many 4+ level + indents. + +2005-07-25 Charles P. Wright + + * Makefile: Update version number to 1.0.14pre. + +2005-07-25 Charles P. Wright + + * man/unionimap.8: Minor editing. + +2005-07-24 David P. Quigley + + * man/unionimap.8 : finished the man page: added a verbose + description and examples. + +2005-07-22 David P. Quigley + + * man/unionimap.8 : added descriptions to options in the + option section. + +2005-07-22 Jan Engelhardt + + * unionctl.c: Use realpath so that relative pathnames are allowed + for mountpoints and branches. + +2005-07-22 Arun M. Krishnakumar + + * subr.c (create_whiteout_parent) : removed this function + as it is not called anymore (#258). + +2005-07-22 Charles P. Wright + + * Makefile: Update version to 1.0.13. + * Makefile: Include new regression tests in distribution. + * man/unionimap.8: An empty placeholder man page. + +2005-07-21 David P. Quigley + + * missing_vfs_funcs.h: Removed get_parent function since the 2.6 + kernel has a dget_parent function which is the appropriate one to use + also made note that the templates do not use either triple_up or triple_down + * unionfs.h: checked to see if we are in 2.6 and if so define get_parent to be + dget_parent. + * unionimap.c: fixed a typo in the usage example. + * man/unionfs.4: added an entry for imap + +2005-07-21 Arun M. Krishnakumar + + * copyup.c (unionfs_create_named_dirs) : corrected the + function to get rid of problems with the open-unlink + regression test. + +2005-07-20 Arun M. Krishnakumar + + * unlink.c (unionfs_rmdir_all) : added checks to ensure + that the delete_whiteouts is not called for files. this + is a fix for bug 323 + + * rename.c (unionfs_rename_all) : changed the whiteout + creation call to "create_whiteout" instead of the + "create_whiteout_parent". this must solve 332. + + * rename.c (unionfs_rename_whiteout) : change similar + to above to correct #336. + +2005-07-20 Charles P. Wright + + * file.c (unionfs_file_revalidate): Handle GFS and don't try + to reopen files that no longer exist. + * subr.c (unionfs_copyup_named_dentry_len): Add a bit of debugging. + * Fix comments that are past 80 characters and some other + minor style issues. + + * subr.c (unionfs_create_whiteout): Don't set the parent's + opaque field when creating a whiteout, set your own. + +2005-07-20 Charles P. Wright + + * Fix things so that we compile on 2.4 again, and use the + older form of ioctl for kernels less than 2.6.11. + + * ChangeLog: 80 character entries. + +2005-07-19 Charles P. Wright + + * unionctl.c: Check if we are trying to remove a branch while + this process is causing it to be busy and print an appropriate + error message. + + * man/unionctl.8: Minor edits. + +2005-07-19 Arun M. Krishnakumar + + * branchman.c (unionfs_ioctl_queryfile) : changed + function replacing lookup_one_len by partial lookups + (second commit) : changed O_NONBLOCK to O_RDONLY + +2005-07-19 Shaya Potter + + * unlink.c (unionfs_unlink_whiteout): Don't rename a symlink + to a whiteout, we need to unlink and create instead. + +2005-07-18 Arun M. Krishnakumar + + * unlink.c (unionfs_unlink_all) : checked if the dbopaque + value is set for the current dentry, as this will + indicate if there is a file to the right of the current + file (Fix BUG #319). + +2005-07-18 Charles P. Wright + + * commonfops.c: Use unlocked_ioctl when it is defined. + * branchman.c: Change prototypes to be more consistent with + unlocked_ioctl. + + * commit: Script for committing files + * Lindent: Linux indentation script. + * *.[ch]: Add emacs magic. + +2005-07-14 Arun M. Krishnakumar + + * lookup.c (unionfs_lookup_backend) : fixed bug #321 + (that is, removed the semicolon that was introduced + in the unionfs-042605-1324.tar.gz snapshot. + +2005-07-14 Arun M. Krishnakumar + + * copyup.c (unionfs_create_named_dirs) : corrected the usage of + the name and namelen arguments sent into the function. This + should take care of both bugs #299 and #322. + +2005-07-13 Arun M. Krishnakumar + + * branchman.c (unionfs_ioctl_queryfile) : added ioctl definition + that lists those branches where the specified file exists(#253). + * commonfops.c (unionfs_ioctl) : added the new ioctl. + * fist.h : added an include for the fd_set helper functions + * unionfs.h : added a structure unionfs_queryfile_args to + pass variables to the new structure. + * unionctl.c : added the user interface + * uniondbg.c : rearranged the includes to help compilation + +2005-07-08 Arun M. Krishnakumar + + * unionfs.h : added variables uii_totalopens and uii_writeopens to + help in keeping filehandles valid after unlinks until closes. + * fist.h : included random.h to help get random bytes for file names + * commonfops.c (unionfs_file_revalidate) : added functionality to copy + the lower-level file into a file with a randomly generated name + obtained by using the newly added function "get_random_name" in + commonfops.c + (unionfs_open, unionfs_flush) : updated the uii_totalopens and + uii_writeopens to be set and checked during the time of opening and + flushing the file. + * copyup.c (unionfs_copyup_dentry_len) : made this into a wrapper + function which now accepts the name of the file + (unionfs_copyup_named_dentry_len). + (unionfs_copyup_file) : similar wrapper to unionfs_copyup_named_file + as above. + (unionfs_create_dirs) : similar wrapper to unionfs_create_named_dirs + as above + (unionfs_copyup_named_file) : added function similar to + unionfs_copyup_file which takes the file name as argument as well. + * main.c (unionfs_reinterpose) : ensured that deleted dentries are not + reinterposed. + * dentry.c (unionfs_d_revalidate) : changed the check for deleted + dentries to use the d_unhashed function also. + +2005-07-07 David P. Quigley + * main.c: Ohh my god the inodes are persistent. Ohh and changed + interpose to use get_uin if we are using persistent inode maps. + +2005-07-07 David P. Quigley + * super.c: fixed problem with kernel version number in an ifdef + +2005-07-07 David P. Quigley + + * dirfops.c: removed some debugging printk statments. + * persistent_inode.c: finished debugging and cleanedup get_uin. + +2005-06-30 David P. Quigley + + * unionfs.h added extern decls for get_uin and get_lin + * persistent_inode.c: more work on the loading code and get_uin and + get_lin + * dirfops.c: changed to make use of the persistent inode code only + if maps are loaded. + +2005-06-28 David P. Quigley + + * persistent_inode.c: changed how files are loaded in should be + done still need to test + * unionfs.h: added a new variable into unionfs_superblock_info + * unionimap.c: changed called to mkfsid + * usercommon.c: changed fillfsid and mkfsid. No longer uses the inode + since it will always be 2 since we are using the root of the fs. + +2005-06-24 David P. Quigley + + * Makefile: added usercommon.c into the make targets + * usercommon.c: Moves find_union to here and fixed it up + * unionimap.c: uses mkfsid now if fsid comes back as 0 + * unionimap.h: added extern for mkfsid + * persistent_inode.c: fixed some bugs + * unionctl.c: removed find_union from here and updated calls to it. + +2005-06-23 Charles P. Wright + + * dentry.c (d_revalidate): We shouldn't re-lookup + non-connected dentries. + +2005-06-23 Arun M. Krishnakumar + + * Makefile: Include Makefile.kernel. + + * subr.c (create_whiteout, create_whiteout_parent): Set the dbopaque + value to the branch where the whiteout gets created. This is to solve + bug #294. + +2005-06-22 Arun M. Krishnakumar + + * commonfops.c (unionfs_open): If the highest-priority branch + is read-only return -EROFS for opens with O_WRITE. + +2005-06-21 David P. Quigley + + * persistent_inode.c: parse_imap_options and associated calls are + working. maps are loaded in properly. Still need to finish and test + calls to use the maps. + +2005-06-17 David P. Quigley + + * persistent_inode.c: it appears that parse_imap_options is working + and so is verify_forwardmap. verify_reversemap needs to be fixed. + +2005-06-17 David P. Quigley + + * persistent_inode.c: Fixed some bugs still not working properly + + * unionfs.h: added typedef for uuid_t. + + * doit.sh: added imap entry into script. + +2005-06-17 Charles P. Wright + + * INSTALL: If you have Fedora Core 4, then you need kernel-devel. + + * Makefile: Fix the Makefile so that it won't recompile every + object every time. + + * unionimap.c: Fix printf formats. + +2005-06-16 David P. Quigley + + * unionimap.c : Fixed a bug that prevented compiling on newer + gcc versions + +2005-06-16 David P. Quigley + + * unionimap.c : Tested and working we can now make and print valid + unionfs imap files. + +2005-06-16 David P. Quigley + + * unionimap.c : finished coding starting testing and debugging. + * unionimap.h : minor changes + +2005-06-15 David P. Quigley + + * unionimap.c : wrote some more code not tested yet + * unionimap.h : added a struct that is needed. + * Makefile: Cleanedup a conflict. + +2005-06-15 Charles P. Wright + + * fist.h,print.c,unionfs.h: Change NODEBUG to UNIONFS_NDEBUG to be + more inline with the rest of the kernel. + * print.c: Wrap in UNIONFS_NDEBUG, so there is no need for a separate + flag to not compile it. + + * unionctl.c: Add Unionfs version to help message + (not just unionctl version). + * unionimap.c: Add version to help message. + +2005-06-12 Charles P. Wright + + * main.c, match-malloc.pl: Handle KFREE(NULL). + * commonfops.c (unionfs_file_revalidate): NULL ftohf_ptr after freeing + it. + * commonfosp.c (unionfs_open): Allocate correctly sized ftohf_ptr. + +2005-06-10 David P. Quigley + + * unionimap.c: Beginning of a user mode program to create inode map + files. + * unionimap.h: Header for program. + * unionfs.h: added definitions for imap structs + * Makefile: added make targets for unionimap.c and .h + +2005-05-26 Charles P. Wright + + * persistent_inode.c: Comment out 64-bit division. + +2005-05-25 David P. Quigley + + * unionfs.h : added some variables to the unionfs_sb_info struct to + handle persistent inodes. + * persistent_inode.c : more work done on the parsing functions almost + done but not quite there yet. + * main.c : added entry in parse_options to handle persistent inodes. + +2005-05-24 Charles P. Wright + + * unlink.c (unionfs_unlink_whiteout): Missing dput in truncating fix. + + * unlink.c (unionfs_unlink_whiteout): Truncate whiteout after it + is created. + +2005-05-23 Charles P. Wright + + * Test commit. + + * inode.c (unionfs_create): Fix ASSERT that had a side-effect. + This fixes a dentry reference count bug if you compile with + -DNODEBUG. + +2005-05-19 Charles P. Wright + + * unlink.c: Empty a directory of its whiteouts before deleting it, + and refresh the hidden dentry on a failed rmdir. + + * unionfs.h: Always include ufi_file_i, so we can compile with zero + inline entries if we want. + + * Test commit. + + * Makefile: Define UNIONFS_VERSION to be the current version. + * main.c: Print the version of Unionfs, not main.c. + +2005-05-17 Charles P. Wright + + * lookup.c (unionfs_lookup_backend): Fix partial lookups for + three or more branches. + +2005-05-16 Charles P. Wright + + * lookup.c (unionfs_lookup_backend): Change UNIONFS_LOOKUP_PARTIAL + to UNIONFS_LOOKUP_REVAL_NEG, when a dentry magically turns positive + on us. + +2005-05-14 Charles P. Wright + + * lookup.c (new_dentry_private_data): Fix memset'ing logic. + * branchman.c (unionfs_ioctl_addbranch): Fix copying logic. + + * inode.c(unionfs_follow_link): Return 0 not the number of bytes + in follow_link. + + * Reduce Unionfs module size when NODEBUG is set by not compiling + print.c, and using a different set of macros that don't require + __FILE__, __FUNCTION__, and __LINE__, and don't do as much checking + because we've aleady turned ASSERT off. + + * Support for inline objects, so that we don't need more + than one allocation for inodes and only two for dentries (the dentry + itself and its private data). + + * Separate directory file operations from normal file operations. + +2005-05-13 Charles P. Wright + + * Makefile: Increment version number. + + * Makefile: Release 1.0.12 + +2005-05-13 Charles P. Wright + + * file.c: Disable generic_file_sendfile, because it causes nfsd + to Oops. + + * lookup.c (unionfs_lookup): Don't ASSERT dentry goodness along + the error path. + * dentry.c (unionfs_d_release): Handle dentries without dthod_ptrs. + +2005-05-11 Jaspreet Singh + + * main.c (unionfs_interpose): Move fist_copy_attr_all before + d_instantiate so Selinux can decide what type of inode it is. + +2005-05-13 David P. Quigley + + * Makefile : fixed spelling mistake + * persistent_inode.c : fixed typo for compilation + +2005-05-12 David P. Quigley + * persistent_inode.c : Stupid spelling mistake in the name + of the file. + +2005-05-12 David P. Quigley + + * persistant_inode.c : Functions needed to establish and maintain + a persistant inode scheme for unionfs. + * main.c : modified parse options for persistant inodes. Not done yet + +2005-05-11 Jaspreet Singh + + * copyup.c (unionfs_copyup_xattrs): Don't use XATTR_CREATE, + instead use 0 so that it is replaced or created as needed + (Selinux automatically creates attributes. + +2005-05-11 Charles P. Wright + + * print.c (fist_print_generic_dentry): ASSERT(d_count > 0) so + that we find dentries gone bad earlier rather than later. + + * inode.c (unionfs_mkdir): bend should not go past the opaque + directory, but stay there. + +2005-05-10 Charles P. Wright + + * INSTALL: Add root file system instructions based on Linux + Live's linuxrc. + +2005-05-08 Charles P. Wright + + * main.c,super.c,lookup.c: Use alloc_inode in 2.6, so that only + two allocations need to be made per inode instead of two. In + both 2.4 and 2.6 use a private kmem_cache for our dentry data, + so that we don't need to waste space by having kmalloc round up. + + * lookup.c (new_dentry_private_data): Merge initialization and + reinitialization code from lookup and read_super. Also don't + reallocate dentry private data if we already have enough space. + + * INSTALL: Add Jaspreet Singh's selinux instructions. + + * INSTALL: Add EXDEV on ro branch rename as a limitation. + + * inode.c: follow_link and put_link fix for 2.6. + + * lookup.c: Compile fix for 2.4. + + * rename.c: If a directory is moved on a read-only branch return EXDEV, + so that mv essentially does a cp -r (recursive copyup in the kernel + would be too ugly). + +2005-05-06 Charles P. Wright + + * lookup.c: Merge unionfs_lookup_backend and unionfs_partial_lookup, + partial lookups will now properly handle whiteouts. + + * print.c: Fix compile error when NODEBUG is defined. + + * lookup.c: Fix BUG 264. + * print.c: Separate generic inode printing from itohi printing. + Use const for print functions. + +2005-05-05 Charles P. Wright + + * Code style fixes. + +2005-04-27 David P. Quigley + + * inode.c changed ops vectors to C99 style initialization. + * file.c changed ops vectors to C99 style initialization. + * dentry.c changed ops vectors to C99 style initialization. + * super.c changed ops vectors to C99 style initialization. + * stale_inode.c changed ops vectors to C99 style initialization. + +2005-04-21 David P. Quigley + + * unionfs.h: added no check versions ot set_dtohd_index + and dtohd_index. + * dentry.c: changed a function call to dtohd_index_nocheck + +2005-04-26 Charles P. Wright + + * lookup.c (unionfs_lookup_backend): When a whiteout is encountered, + set opauqeness as we do with opaque directories, to prevent partial + lookup from seeing past the whiteout. + + * lookup.c: Separate file for lookup code. + * subr.c: Move unionfs_partial_lookup to lookup.c. + * inode.c: Move unionfs_lookup to lookup.c: + + * rename.c: Don't print lower-level dentries using a Unionfs + specific function. + + * print.c: Divide fist_print_dentry into one function for Unionfs + specific dentries, and then a generic function. Also ASSERT that + we are really printing a Unionfs dentry (so we don't dereference + bad private data). + + * Fix indentation comments. + +2005-04-25 Charles P. Wright + + * subr.c: Code style fixes. + +2005-04-22 Charles P. Wright + + * NEWS: Keep NEWS up to date. + +2005-04-21 Charles P. Wright + + * file.c (unionfs_filldir): Don't strncmp for .wh., if the + name's length is less than 4. Old cruft can make things + go horribly bad (BUG 254). + + * inode.c (unionfs_lookup_backend): Properly handle file + transitioning from a negative to a positive dentry. (BUG 215) + +2005-04-21 Charles P. Wright + + * file.c: Sendfile only exists on 2.6. + * main.c: Catch UNIONFS_REVAL_NEG specifically, still + need to understand/fix. + +2005-04-20 Charles P. Wright + + * subr.c (unionfs_partial_lookup) ,unionfs.h + (unionfs_dentry_info), inode.c(unionfs_lookup): + Correctly mark a directory as opaque and respect that + during partial lookups. + + * Exorcise struct typedefs and "fake" STATIC functions. + + * inode.c: Experimental support for opaque directories, + not all of the loose ends are tied up, so this snapshot + will have some associated oddities. + + * xattr.c: Use EOPNOTSUPP, not ENOTSUPP. + + * Makefile, unionfs.h: Separation of rename/unlink code. + * rename.c: Separate rename code from inode.c + * unlink.c: Separate unlink code from inode.c + +2005-04-20 Charles P. Wright + + * rdstate.c, unionfs.h: DOH, We were checking if + uii_rdversion <= MAXRDCOOKIE to decide whether or not + to wrap uii_cookie. + +2005-04-19 Fabian Franz + + * applied patch to file.c that uses generic_file_sendfile + to implement sendfile. + +2005-04-18 David P. Quigley + + * removed a stray printk + +2005-04-18 Markus F.X.J. Oberhumer + + * file.c, locks.c: Use #ifdef for 64-bit locking + commands, which are not defined on amd64. + +2005-04-18 David P. Quigley + + * copyup.c: Extended attributes now copyup properly. + This has only been tested in 2.6 but I see no reason + that it shouldent work in 2.4 + * copyup.c: unionfs_create_dirs is now nolonger bound + by MAX_DIRS_CREATE. We now use kmalloc to dynamically + allocate memory for it and "realloc" the memory when + needed. + +2005-04-18 Charles P. Wright + + * copyup.c: Use list_size as the argument to xattr_free, not the + MAX_LIST_SIZE. + + * file.c: ASSERT was asserting on the dentry, not the i_mode. + +2005-04-16 Charles P. Wright + + * file.c: Delay copyup of read-write files located on read-only + branches, until an operation will actually write to them (BUG 225). + + * INSTALL: Cleanup kernel instructions a bit. + +2005-04-13 David P. Quigley + + * inode.c: unionfs_mkdir now spoofes the uid and gid of the owner + of the whiteout file for the purpose of removing it. + +2005-04-11 David P. Quigley + * patch-kernel.sh : Script written to move unionfs into the kernel + source tree. The original script was submitted by Sven Geggus however + the script was cleaned up and modified for various reasons. Check the + file for more details. + * Makefile: PHONY added to the utils target to aid in building them + for the kernel. + * INSTALL: Directions added for building unionfs into a monolithic kernel. + +2005-04-11 David P. Quigley + + * copyup.c : changed comparison from > to >= to be compatable with 2.6.0 + * fist.h : same as above + * unionfs.h : same as above + * rdstate.c : same as above + * main.c : same as above + * inode.c : same as above + * super.c : same as above + * xattr.c : sme as above + +2005-03-24 David P. Quigley + + * unionfs.h : changed UNIONFS_SUPER_MAGIC. All fist based file systems + will have the first 2 bytes of the MAGIC number be f15f. + * super.c : statfs struct is now properly filled. + +2005-03-23 David P. Quigley + + * INSTALL: removed the warning about NFS exports + * Makefile: Increases release number. + * NEWS: Added release news for 1.0.11 + * unionfs.h: added a function rdstate2offset + * file.c: redid llseek and readdir implementation + * rdstate: redid find_rdstate + +2005-03-21 Charles P. Wright + + * Add big warnings that NFS exports won't work in 1.0.10. + +2005-03-17 Terry Barnaby + + * rdstate.c (find_rdstate): Factor out rdstate search. + * file.c (unionfs_dir_llseek): Use rdstate if it exists. + +2005-03-16 Terry Barnaby + + * Makefile: fix clean target + + * file.c (unionfs_dir_llseek): Fix mistaken use of origin + instead of offset. + +2005-03-16 Charles P. Wright + + * super.c (unionfs_statfs): Use shifting instead of division. + +2005-03-15 David P. Quigley + + * super.c (unionfs_statfs) : df now reports the proper numbers. + duplicate super blocks are not factored into the calculations and + all the block sizes are normalized to the first partition. + +2005-03-14 David P. Quigley + + * main.c (unionfs_read_super) : In 2.4 the kernel expects a valid + super block or null we were returning an error pointer and this + caused an Oops. This has been fixed. + +2005-03-10 Charles P. Wright + + * unionfs_getlk (unionfs_getlk): Add preprocessor define for + 2.6.11+, and add a missing case. + +2005-03-10 David P. Quigley + + * locks.c (unionfs_setlk): fixed a typo in the 2.6 function + +2005-03-10 Charles P. Wright + + * misc/snapmerge: Explain how to get two snapshots merged into one + snapshot. + + * file.c (unionfs_dir_llseek), locks.c (unionfs_setlk): Fix build + on 2.6. + +2005-03-09 David P. Quigley + + * file.c (unionfs_lock): function is completely rewritten to actually + work. + * locks.c (unionfs_setlk,unionfs_getlk): locks.c was added to house + locking functions so file.c doesent get overcrowded. unionfs_setlk + and unionfs_getlk were added and placed in this file with some + helper functions for 2.6 + +2005-03-08 Terry Barnaby + + * file.c (unionfs_dir_lseek): Allow seek to beginning/end of dirs. + +2005-03-07 Fernando Freiregomez + + * misc/snapmerge: Fix permissions/times after copying files. + +2005-03-07 Lucas Correia Villa Real + * Makefile: Use MODDIR instead of /lib/modules/`uname -r` + +2005-03-07 Eduard Bloch + * man/*: Fix man page sections + * Makefile: Add separate modules target, remove excessive uname -r's + so that kernel version can be overridden. + +2005-03-03 Anton Farygin + + * Makefile: Can now build utilities without building kernel module. + +2005-03-03 Terry Barnaby + + * dentry.c (unionfs_d_revalidate): Use fist_copy_attr_all to + make the cache appear more coherent. + +2005-03-02 Fabian Franz +2005-03-02 Anton Farygin + * dirhelper.c: Fix BUG 184: vfs_readdir is allowed to return positive + results. + +2005-03-02 Charles P. Wright + + * inode.c (unionfs_readdir): Remove buf.error which was unused. + +2005-03-02 Anton Farygin + + * Fix BUG 205: mv on symlinks + + * Fix BUG 203: Kernel oops on creat file with len of name > 252 + +2005-02-24 Charles P. Wright + + * RPM spec file included in release. Remove CVS directory from + debian directory in release. + + * Add fsid= to interactions, with pointer to exports(5). + +2005-02-22 Charles P. Wright + + * unionctl.c (find_union): Start off w/ a 1K buffer and double it + if the lines in /proc/mounts don't fit. Thanks to J. H. Wilson for + finding and patching this bug. + + * main.c (unionfs_interpose): Reorganize if statement. + + * rdstate.c (add_filldir_node,find_filldir_node): Don't print the + names in debug mode because they aren't '\0' terminated. + +2005-02-22 Fabian Franz + + * Applied patch to bug 193 Fixed the problem with + hardlinks not having the same inode number. + +2005-02-22 Charles P. Wright + + * dentry.c (unionfs_d_delete): d_delete should not be defined (or + call the lower d_delete). Thanks to Fabian Franz for identifying + this issue. + +2005-02-22 Fabian Franz + + * copyup.c (unionfs_copyup_dentry_len): Applied patch provided + for bug #196. A device was being decoded when it didnt have to be. + +2005-02-22 Charles P. Wright + + * README: Article URL. + +2005-02-18 David P. Quigley + + * dirhelper.c (create_dir_whs) : commiting chips change to make + sure it creates whiteouts properly. + +2005-02-17 Charles P. Wright + + * INSTALL: Squashfs is generally OK, but we do have flock problems. + + * file.c (unionfs_dir_llseek): Untested fix for BUG 187. + +2005-02-09 David P. Quigley + + * inode.c (unionfs_link): Fixed linking if the underlying file + system is read-only. The fix causes an issue with device being + busy on umount. * rdstate.c (find_filldir_node): Print statement + was referencing a bad pointer. + + +2005-02-08 Charles P. Wright + + * copyup.c (unionfs_copyup_xattrs): Use PASSERT, other compile fixes. + * xattr.c (xattr_alloc, xattr_free): Shouldn't be static. + * Go back to GFP_KERNEL, because GFP_NOFS causes problems with vserver. + +2005-02-08 Jaspreet Singh + + * copyup.c (unionfs_copyup_xattrs): Use security functions. + +2005-02-08 Charles P. Wright + + * copyup.c (unionfs_copyup_xattrs): Take the inode lock as is done in + setxattr. + * inode.c (unionfs_link): Reference counting on error paths/compile fix. + +2005-02-07 Charles P. Wright + + * inode.c (unionfs_link): On copyup path directory was not unlocked. + +2005-02-07 David P. Quigley + + * copyup.c (unionfs_xattr_copyup): Review of code, not yet tested. + + * inode.c (unionfs_link): Revert to Chip's version of + unionfs_link. + +2005-02-07 Charles P. Wright + + * Makefile: Allow module prefixes to be specified for + RPM_BUILD_ROOT + * rpm/unionfs.spec: Compiled, tested, and installed. + +2005-02-06 David P. Quigley + + * Makefile added a target for uninstall to accomodate rpm removal + +2005-02-02 Anton Farygin + + * copyup.c (unionfs_copyup_permissions): Use ATTR_FORCE to make + sure any user can change permissions. + * copyup.c (unionfs_create_dirs): Twiddle current->fsuid/fsgid + so that any user's copyup will behave properly. + +2005-02-02 Charles P. Wright + + * file.c (unionfs_mmap): Don't ASSERT that the lower mmap + operation is there, instead return ENODEV, as is done in + do_mmap_pgoff. NTFS (and others) don't support mmap, so + neither should we when stacked on top. + +2005-02-01 David P. Quigley + + * dirhelper.c : fixed 2.6 compilation. call to vfs_creat missing + last param in 2.6 + + * Makefile : namei.c is no longer needed so it was removed from + the make file. + +2005-01-28 Charles P. Wright + + * subr.c, dirhelper.c: Move mkdir/rmdir helpers into separate + file. + + * unionfs.h, fist.h: Remove unused/redundant definitions. + +2005-01-26 Charles P. Wright + + * rdstate.c, unionfs.h: Move finding and adding filldir nodes + into rdstate.c. This reduced the readdir code size, and should + also let us reuse the code for the subr.c functions. + * subr.c: Use an rdstate instead of a second readdir to remove + files in delete_whiteouts. + * namei.c: Removed vfs_unlink_nozombie. + +2005-01-19 Charles P. Wright + + * INSTALL: Add more CFLAGS options to make things smaller. + + * unionfs.h, file.c, rdstate.c: Change DIREOF from -1 to 2^31-1 to + fix readdir. + + * unionctl.c: Don't strip trailing slash for "/" when searching for + the Union. + + * Makefile: Include INSTALL. + +2005-01-14 Charles P. Wright + + * branchman.c (unionfs_ioctl_addbranch): Fix error path resource leak. + +2005-01-14 David P. Quigley + + * copyup.c (unionfs_copyup_permissions): Changed so it actually works now + instead of corrupting certian bits. + * copyup.c (unionfs_create_dirs): Function actually uses unionfs_copyup_permissions + for setting directory permissions instead of setting them explicitly. + +2005-01-14 Charles P. Wright + + * file.c (unionfs_dir_llseek): Allow llseek to the current + offset in the directory, but nowhere else. + * file.c (unionfs_readdir): Save the last offset, so we + can lseek to it (for pretend). + + * Remove all occurrences of GFP_KERNEL and replace with GFP_NOFS, + so our allocations can't cause file system calls. + +2005-01-13 Rakesh N. Iyer + + * file.c (unionfs_readdir): Fixed deadlock in rdstate search. + +2005-01-13 Charles P. Wright + + * Split README into INSTALL as well. + + * Makefile: Spit out a message saying to read install. + + * Don't build xattr functions w/o -DUNIONFS_XATTR + +2005-01-11 David P. Quigley + + * inode.c: Initial implementation of hardlinking a file on a read only branch. + Attributes are not copied properly yet. + +2005-01-10 Sai Suman + + * copyup.c: Fix usage of unionfs_copyup_permissions. + +2005-01-10 Charles P. Wright + + * super.c: 2.4 compile fixes + + * fist.h: Updated FISTBUG for gcc 2.9.5 + +2005-01-10 David P. Quigley + + * xattr.c: Format changes for size_t. + + * doit.sh: Call optional postdoit script + +2005-01-10 Charles P. Wright + + * AUTHORS file. + + * README updates. + + * Allow NFS exports on 2.6 (Sai Suman) + * Fixes to print format in xattr.c (Sai Suman) + + * debian/*: Debian packaging from Alex de Landgraaf. + + * Remove -Wno-unused-label for gcc 2.9.5 (alex@delandgraaf.com). + * Allow "EXTRACFLAGS=-DNODEBUG" in fistdev.mk to disable + the printing/ASSERT facility, cutting object size in half. + +2005-01-09 Charles P. Wright + + * unionfs.h: Warn when compiling on unsupported kernels. + * super.c: Compile fix for 2.6.8. + * Makefile: Include ChangeLog in the release. + +2005-01-07 Charles P. Wright + + * Prepare 1.0.6 release. + + * Fix 2.6 compile issues in new code. + + * rdstate.c: Move readdir state away from subr.c. + Use a kmem_cache for filldir nodes. + + * copyup.c: Move copyup functions away from subr.c + +2005-01-06 Charles P. Wright + + * file.c: Working NFS readdir. On close we store left-over + readdir state in the inode for up to 5 seconds. If a readdir + has a matching cookie, then we pull the state out of the inode + and use it. We also needed to use our own offsets, otherwise + the NFS client got confused. + + * subr.c: Remember the number of entries in our hash table for + each inode, and use that as the number for the next readdir. + + * Makefile: Fix dependencies on .h files. + + * print.c: Consistencify printing of files and superblocks + and shorten some overly verbose output that added no information, + but reduced the effective kernel log size. + + * fist.h: Make gcc check fist_dprint_internal formats. + + * file.c,unionfs.h: Separate readdir state into a separate structure. + + * file.c,unionfs.h,subr.c: Make readdir state use variable sized + hash tables based on number of pages in lower-level directories. + + * Makefile: Install unionfs.ko, not unionfs.o if it exists. + + * main.c: Remove MODULE_PARM because it is deprecated. + +2005-01-04 Charles P. Wright + + * file.c (unionfs_read): Fix "inifite" cat bug on 2.6 + + * Makefile: Auto-select 2.4 vs. 2.6 for build. + + * Unify 2.4 and 2.6 Makefiles + + * Fixes for printing file structures at debug level 18. + + * Fixes for 2.4 compilation. + +2004-12-30 Charles P. Wright + + * *.[ch]: Update year. + +2004-12-29 Charles P. Wright + + * inode.c: Embed rename.txt into comment. + +2004-12-29 Charles P. Wright + + * inode.c, unionfs.h: Changed directory copyup function back to + old prototype. + + * subr.c (unionfs_copyup_dentry_len): Handle directories, devices, + and symlinks. + +2004-12-27 Charles P. Wright + + * inode.c: Only request copyup of parent directories. + + * subr.c (unionfs_copyup_dentry_len): Handle directory copyup + (required for chmod). + + * main.c (unionfs_parse_options): Fix along error path. + +2004-12-06 Charles P. Wright + + * main.c (unionfs_interpose): Allow mount point crossing. + + * README, Makefile, inode.c: Turn off extended attributes, unless + the user specifically turns them back on by defining UNIONFS_XATTR. + This prevents us from getting various compile errors depending on + vendor-specific patches. + +2004-11-10 Charles P. Wright + + * Fix .cvsignore and release. + + * README: Added warning that anything less than 2.4.20 is unsupported. + + * namei.c: Removed inode_dir_notify calls when we are on less than + 2.4.20, because the symbol is not exported. + + * Fixed lots of bad casts from pointer to int in printks. + + * main.c (unionfs_parse_options): Fix for badly ordered variables. + + * main.c (unionfs_read_super): Remove double free along error path. + + * wrapfs.h (dtopd_lhs): Remove cast from LHS of assignments. + +2004-11-09 Erez Zadok + + * released unionfs-1.0.2. + + * README (site): fix typo. missing "`". + + * Makefile (clean): remove tarball. + + * README: updated. + +2004-11-09 Charles P. Wright + + * released unionfs-1.0.1. + + * Makefile: Include man pages in the release. + +2004-11-07 Erez Zadok + + * released unionfs-1.0. + + * announce-email.txt: revised announcement text. + +2004-09-23 Erez Zadok + + * announce-email.txt: draft announcement email. + + * README: revised. + +2004-09-15 Charles P. Wright + + * file.c: Don't allocate directory hash table for files. + + * inode.c (unionfs_setattr): Don't do partial lookup if setattr is not + set to all (should improve delete_whiteout). + + * Some grayout code (we don't discuss rename_first anymore, so it + actually doesn't matter). + +2004-08-24 Charles P. Wright + + * namei.c (vfs_create_nozombie): Whiteout creation needs a version + of vfs_create that won't down the i_zombie. + + * subr.c (create_dir_whs_filldir): Don't ASSERT that the whiteout + doesn't already exist, because we can have multiple files with + the same name in other branches. + +2004-08-23 Mohammad Nayyer Zubair + + * inode.c (unionfs_mkdir, unionfs_symlink, unionfs_mknod): lookup + whiteout in 'bstart' not 'bindex'. We always looked up on first branch + as bindex was set to 0 + +2004-08-20 Charles P. Wright + + * inode.c (unionfs_inode_revalidate): Walk up the tree to revalidate + inodes like we do for dentries. + + * inode.c, subr.c, unionfs.h: Use multilocks to protect against + unionfs_partial_lookup and revalidate operations racing against + each other. + + * unionfs.h: Implementation of "multilocks". + + * Makefile: install target + + * We don't change size, so no sca_*.[ch] files. + + * main.c (unionfs_hidden_dentry_index): This function is unused now + that Mohammad took it out, so I removed it. + + * Removed unnecessary files. + + * COPYING: SUNY we can't be blamed for anything notice; and the GPL. + +2004-08-19 Mohammad Nayyer Zubair + + * inode.c (unionfs_setattr): using dtohd_index() instead of + unionfs_hidden_dentry() because an intermediate hidden dentry + could be NULL and we dont want PASSERT to oops on us + +2004-08-19 Charles P. Wright + + * inode.c (unionfs_unlink_all): We shouldn't have negative dentries + unless they are the first one. + + * inode.c (unionfs_inode_revalidate): PASSERT(hidden_inode) instead + of Oopsing. + + * inode.c (unionfs_rename_all): Typo in the clobbering unlink. + + * inode.c (unionfs_rename_all): No debug printks. + + * unionfs.h (get_nlinks): Don't sum up non-directories. + + * subr.c (unionfs_refresh_hidden_dentry): Re-lookup a dentry, this + is needed on rename reverts because vfs_rename trashes the dentry + of the target. + + * inode.c (unionfs_rename_all): Working revert. + +2004-08-18 Charles P. Wright + + * inode.c (unionfs_rename_all): New rename that handles more cases, + revert code doesn't work yet. + +2004-08-18 Mohammad Nayyer Zubair + + * inode.c (rmdir_all): exit if error is not -EROFS and not -ENOTEMPTY + + * inode.c (rmdir_all): exit from function and dont create whiteout + if vfs_rmdir returns error other than -EROFS + + +2004-08-17 Charles P. Wright + + * inode.c (unionfs_unlink_all): Unlock the directory on error. + + * inode.c (unionfs_setattr): Lock on all attribute changes, not just + some. + + * inode.c (do_rename): Check renames based on index rather than + new_dentry which might not exist. + + * Makefile: debugging symbols are very useful for Oops tracing + + * unionfs.h (CUR_MAX_BRANCH): Removed CUR_MAX_BRANCH which was always + sbend + 1, and replaced with sbmax (that doesn't require a redundant + and possbly inconsistent field within the super-block). + + * unionfs.h (dtopd): Converted from a macro to an inline function + so that we can do more checking of the private data that we are + returning. + + * unionfs.h (dtohd_index) Added a udi_bcount field to the private data + so we would Oops rather than silently overflow array bounds. + + * dentry.c: Recursively revalidate the parent (the ASSERT that was + checking this was mis-written). It turns out that our parent was not + always valid. + + * file.c (unionfs_file_revalidate): Always reassign the read-ahead + values because the older file might have different values and llseek + will cause an assertion failure. + + * inode.c (unionfs_lookup_backend): Treat revalidated negative and + positive dentries differently. + +2004-08-16 Charles P. Wright + + * inode.c: Use an fd_set instead of an integer for tracking successful + renames. + + * branchman.c: Don't allow more than FD_SETSIZE (1024) branches. + + * unionctl.c: Specific message for exceeding FD_SETSIZE. + +2004-08-11 Charles P. Wright + + * inode.c: Return ENAMETOOLONG when looking up ".wh.*" + + * unionctl.c: Add ioctl. + + * branchman.c: Don't allow branches without MAY_READ + + * unionctl.c: Convert branch pathnames into index automatically. + + * unionctl.c: Match the longest prefix of our path inside of + /proc/mounts instead of the path itself. This is required so that + remove doesn't need to open the root of the union. + + * unionctl.c: Remove branch ioctl and list branch configuration. + +2004-08-10 Mohammad Nayyer Zubair + + * file.c: added CAP_SYS_ADMIN checks before calling unionfs ioctls + + * uniondbg.c: now contains debugging related ioctls + + * unionctl.c: now contains ioctls to add, remove and set branch + permissions moved here + + * uniondbg.c: ioctls to add, remove and set branch permissions + moved here + + * fist_ioctl.c: remaining ioctls stay here + + * subr.c: making sure copyupuid, copyupgid and copyupmode are all + specified when copyup option is set to mounter. + + * inode.c: implemented unionfs_rename_first() + +2004-08-09 Mohammad Nayyer Zubair + + * inode.c: more places where we should exit if get an error other than + -EROFS in unlink/rmdir and related functions. + + * subr.c: exit if get an error other than -EROFS when + creating/deleting whiteouts + + * inode.c: making sure that we create/mkdir/symlink/mknod to the + left only if the error returned is -EROFS, otherwise passup + + * subr.c: use notify_change instead of directly modifying the inode + fields. + + * subr.c: use notify_change instead of directly modifying the inode + fields. + + * main.c: added copyupuid, copyupgid and copyupmode mount options. + These options will specify the mode, uid and gid of copied-up files. + copyup option should be set to mounter. + + * subr.c: using the above mount time values for copied-up files. + + * unionfs.h added these values in the unionfs super block. + +2004-06-17 Charles P. Wright + + * file.c (unionfs_flush): Flush all branches (only makes a + difference for directories). + +2004-08-03 Mohammad Nayyer Zubair + + * super.c: Implemented unionfs_show_options. Can now view unionfs + mount time options in /proc/mounts + + * inode.c: using is_robranch_super instead of is_robranch in + create, mkdir, symlink, mknod operations, whenever we try to + remove whiteouts if they exist. + + * main.c: while parsing options using strcmp wherever we can, + removed all magic numbers, copyup options are now preserve, + currentuser, mounter + + * subr.c: when copying up the default mode should be original + owner (preserve) + +2004-08-02 Mohammad Nayyer Zubair + + * main.c: unionfs mount time flag changed from being an integer + flag to several text options that will together make up the flag. + +2004-05-17 Charles P. Wright + + * branchman.c: Fixed off by one error when adding more than 8 + branches. + +2004-05-10 Charles P. Wright + + * main.c (unionfs_read_super): Error check to prevent oops on bad + mount-time options. + +2004-04-21 Charles P. Wright + + * file.c: revalidate files when the are passed into our methods, + this allows copy-up of open files. Yeah snapshots. + + * dentry.c: Should not use d_hash and d_compare because Unionfs is + in charge of the namespace, not the lower-level file systems. + + * Unionfs now has split-view caches. If compiled with + -DSPLIT_VIEW_CACHES, then Unionfs supports duplicating the super + block structure and dynamically selecting the correct structure to + use when crossing into the unionfs mountpoint. Right now the + default behavior is that root has one view, and every other user + has another view. + + If the super block is not duplicated then everything works as + before. + +2004-04-20 Charles P. Wright + + * branchman.c: Super Duper works! An ioctl can let you create a + super block that is a copy of the original. When you stat the + mountpoint that super block is returned. Now to fix the reference + counting bugs associated with unmount. :) + +2004-04-19 Charles P. Wright + + * super.c (unionfs_select_super): return s_root for now + +2004-03-15 Charles P. Wright + + * dentry.c (unionfs_d_revalidate): Fix refcounts. + + * inode.c (unionfs_inode_revalidate): Fix reference counting. + + * inode revalidate "works" (e.g., no Oopses or other broken f/s + behavior), but has broken reference counting. + +2004-03-14 Charles P. Wright + + * main.c (unionfs_interpose): Changed neg_dent_flag to which was + true if we did *not* have a negative dentry to is_negative_dentry + which *is* true if we have a negative dentry. Fixed related + assert (which was ASSERT(1)). + + * inode.c (unionfs_lookup): Read through and simplified lookup by + removing duplicated code or nasty if/else statements when if + continue would work(to the tune of 20%). Clarified comments as + well. Now instead of being a 248 line monster it is a 199 line + monster. + + * UNIONFS-TODO: We still need to solve whiteouts (i.e., you can + sucessfully stat a whiteout in Unionfs). Note this is not + introduced by my changes. + +2004-03-12 Charles P. Wright + + * branchman.c (unionfs_ioctl_rdwrbranch): Read/write branch setting. + +2004-03-11 Charles P. Wright + + * branchman.c (unionfs_ioctl_addbranch): Refcount fix. + +2004-03-04 Charles P. Wright + + * inode.c (do_rename): Correctly check is_readonly. + + * fist.h (print_exit_pointer): Print out pointer using %p and + don't convert using PTR_ERR if it is not IS_ERR. + + * subr.c (unionfs_create_parent_dir): Return error as pointer + + * subr.c (create_whiteout): Check branch before creating whiteouts. + + * unionfs.h (VALID_MOUNT_FLAGS): Define valid mount flags in the + header, so main.c doesn't need to know all of them. + + * doit.sh (FLAG): Default mode should be UNLINK_ALL, not + UNLINK_WHITEOUT. + + * inode.c (unionfs_unlink): Unlink creates whiteout to the left. + +2004-03-03 Charles P. Wright + + * inode.c (unionfs_create): Creation on a robranch will + cause the file to be created to the left. + + * unionfs.h: More macros for checking if things are on read-only + branches. + + * xattr.c: Separate xattrs from inode.c. + + * ATTACH-TODO.txt: Unionfs doesn't attach. + + * README.attach: Unionfs doesn't attach. + + * mmap.c: Unionfs doesn't filter data. + + * vm_area.c: No data filtering. + +2004-03-02 Charles P. Wright + + * main.c: Updated parsing code so that dirs can be specified as + b0=rw,b1,b2=ro,b3. Defaults to rw. Need to actually integrate + the permission checking code elsewhere. + +2003-11-10 Harikesavan Pathangi Krishnan + + * file.c (unionfs_readdir): Changed code to fix the NFS related bug. + +2003-10-27 Harikesavan Pathangi Krishnan + + * subr.c (unionfs_copyup_dentry_len): This is a modified form of + unionfs_copyup_dentry() that takes in the length of file to be + copied up. The length is useful when the file size is changed in + the setattr. + +2003-10-01 Puja Gupta , Jay, Mohammad. + + * subr.c (unionfs_copyup_dentry): fixed copyup bug, update bstart, bend + on error on vfs_create. + +2003-09-27 Puja Gupta , Hari, Jay. + + * inode.c (unionfs_rename_all): fixed to call rename and create + whiteout for test_rename script. + +2003-09-26 Puja Gupta , Zubair, Hari. + + * subr.c (create_whiteout): removed unnecessary dput of hidden_dentry. + +2003-09-26 Puja Gupta , Hari + + * subr.c, inode.c, unionfs.h: Better function name and comments. + + * inode.c (unionfs_rename_all): changed to create whiteouts + properly on error conditions. + (unionfs_rename_whiteout): initilize parent dentry. + +2003-09-26 Harikesavan Pathangi Krishnan , Puja. + + * print.c (fist_print_dentry): added print for d_parent, aligned. + + * subr.c (create_whiteout_left_parent): Added function for + creating whiteout in a parent directory by a char * string. + (unionfs_create_parent_dir): initialize dtohd before checking and + changing the bstart and bend for new dentry. + (unionfs_copyup_dentry): removed extra dput, since fput internally + does that. + + * inode.c (unionfs_rename_whiteout): fixed to support copyup for EROFS. + +2003-09-24 Puja Gupta , Hari, Jay. + + * subr.c (unionfs_create_parent_dir): Fixed updating bstart for copyup. + +2003-09-23 Puja Gupta , Hari, Jay. + + * subr.c (unionfs_create_parent_dir): Updates bstart, bend, dputs + extra negative dentries. + +2003-09-23 Puja Gupta , Hari, Jay. + + * subr.c (unionfs_create_parent_dir): Dput the previous negative + dentry. + + * inode.c (unionfs_create, unionfs_mkdir, unionfs_mknod, unionfs_link) + (unionfs_symlink): Removed dgets and dputs to handle errors in + vfs functions. Now handled in unionfs_create_parent_dir. + (do_rename): removed dputs and dgets to balance with create_parent. + +2003-09-22 Harikesavan Pathangi Krishnan , Mohammad. + + * subr.c (unionfs_create_parent_dir): Fixed a bug related to + igrabbing lower inode. + +2003-09-22 Akshat Aranya + + * main.c (unionfs_parse_options): Fix some memory leaks in error + paths. + +2003-09-22 Charles P. Wright + + * main.c (unionfs_read_super): Fixed error path. + +2003-09-22 Erez Zadok + + * match-malloc.pl: document this script with a procedure telling + how to use malloc debugging. Use -DFIST_MALLOC_DEBUG from now on. + +2003-09-22 Puja Gupta + + * fist.h, main.c, subr.c: turned off MEMORY_DEBUG. + +2003-09-22 Puja Gupta + + * main.c (unionfs_read_super): Memory fix. + +2003-09-22 Puja Gupta + + * inode.c, dentry.c, super.c: Memory Leak Checks. + +2003-09-22 Erez Zadok + + * subr.c: add transcation counter to malloc/free debugging. + + * match-malloc.pl: perl script to parse log output from + KMALLOC/KFREE macros, and report leaks etc. + +2003-09-21 Mohammad Nayyer Zubair , Puja, Hari, Jay. + + * inode.c (unionfs_lookup): changed the dput location of hidden + whiteout dentry in the code. + +2003-09-22 Puja Gupta + + * subr.c (check_empty): added list_del. + + * inode.c (unionfs_lookup): kfree moved at out. + (unionfs_unlink_whiteout): added kfree. + +2003-09-21 Mohammad Nayyer Zubair , Puja, Hari, Jay + + * subr.c (create_parent_dir()): added support for left to right + copy up. Could be used in unionfs_link() + +2003-09-21 Puja Gupta , Jay, Hari, Mohammad. + + * subr.c (check_whiteout): check for error on kmalloc. + (check_empty): moved kfree after 'out' label. + + * main.c (unionfs_read_super): changed to dput superblock dentries on + error conditions. + + * file.c (unionfs_filldir): check for error on kmalloc. + +2003-09-21 Mohammad Nayyer Zubair , Puja, Hari, Jay. + + * inode.c (unionfs_link()): unlinking .wh.foo (if exists) + while creating link called foo + +2003-09-21 Puja Gupta , Hari, Jay, Mohammad. + + * inode.c (unionfs_mkdir, unionfs_mknod): dget hidden_dentry if not + calling create_parent_dir. + + * inode.c (unionfs_mkdir, unionfs_mknod): removed extra dget before + removing whiteout. + +2003-09-21 Mohammad Nayyer Zubair , Hari, Jay, Puja. + + * inode.c (unionfs_symlink()): removed extra dget() in symlink + +2003-09-21 Puja Gupta + + * inode.c, dentry.c, unionfs.h: variable declaration re-shuffled to + compile on local machine. + +2003-09-21 Mohammad Nayyer Zubair , Hari, Jay, Puja + + * subr.c (create_dir_whs()): removed extra dget on hidden dentry + + * file.c (unionfs_open()): calling branchput if get an error in + opening hidden dentry + +2003-09-21 Puja Gupta , Jay, Hari, Mohammad. + + * inode.c: removed extra bstart, and comments. + + * file.c (unionfs_readdir): Added a bindex++ to {un,re}-reverted code. + Works now!! ;-) + +2003-09-21 Mohammad Nayyer Zubair , Hari, Jay, Puja. + + * subr.c (create_whiteout_left()): synched reference counts for + hidden_dentry; added a dget for the hidden_dentry + +2003-09-21 Puja Gupta , Jay. + + * inode.c (unionfs_create): removed extra dget, dput for vfs_rename. + Removed extra dputs, d_drop on error. + +2003-09-21 Puja Gupta + + * subr.c (unionfs_create_parent_dir): removed extra dput on error + in lookup. + (create_whiteout_left): mode set to create whiteout. + (delete_whiteouts): removed dput on NULL hidden_dentry. + +2003-09-20 Mohammad Nayyer Zubair Puja, Jay + + * inode.c: removed all "dtohd_index(dentry, bindex) = NULL" references and any + extra dputs + + * subr.c: modified code in troublesome create_whiteout_left() function. + Removed extra dgets on the parent and dputting the hidden dentry + +2003-09-20 Puja Gupta + + * subr.c: added branchput for branchgets. + + * print.c (fist_checkinode): fixed the count print condition. + + * inode.c (unionfs_unlink_whiteout): changes to dget, notify_change. + match dget and dput. + +2003-09-20 Charles P. Wright + + * Makefile (tags): target for tags + + * inode.c (unionfs_unlink_whiteout): Refcount fix. + +2003-09-20 Puja Gupta + + * file.c (unionfs_readdir): reverting back to file.c before changes + related to readdir_called flag. Was going into an infinite loop due + to some condition. The current version works fine. + + * subr.c (create_dir_whs_filldir): replaced "return err" with + "goto out", which was causing an infinite loop. + (check_whiteout, delete_whiteouts): replaced 'ret' with err. + +2003-09-19 Puja Gupta + + * subr.c (create_dir_whs): added filldir_called flag. + +2003-09-19 Puja Gupta , Jay, Hari, Mohammad. + + * subr.c (check_empty): check if filldir is called or not. + +2003-09-19 Harikesavan Pathangi Krishnan , Jay, Chip, Puja, Mohammad + + * subr.c (check_empty): Added a flag field in the callback + structure that makes readdir call once again if all the contents + of the directory are not read. + +2003-09-19 Charles P. Wright + + * file.c (unionfs_release): free resources on file close. + + * file.c (unionfs_filldir): Print a warning on any duplicate, but + don't return -EIO. + + * subr.c (create_whiteout_left): fix a refcount leak + +2003-09-19 Mohammad Nayyer Zubair + + * file.c (unionfs_filldir()): fixed the rm -Rf (-EIO, ENOTEMTPY + error) cases. return -EIO ONLY when get a 'foo' and 'foo' exists + in the same directory, which would indicate corruption in the file + system. Before were returning -EIO ALSO if filldir gets a + '.wh.foo' and 'foo', latter being in the linked list already + +2003-09-17 Puja Gupta , Hari, Mohammed. + + * Makefile: Changing back to old file. + + * subr.c (create_whiteout_left): removed dput, hidden_dentry == NULL. + + * inode.c (do_rename): If error, make hidden_dentry NULL. + (unionfs_rename_all): copyup file on err = -EROFS. + + * unionfs.h: added copyup_dentry. + + * subr.c (unionfs_copyup_dentry): added this function, called from + setattr. + (unionfs_copyup_file): modified, calls copyup_dentry internally. Added + checks before kfree. + + * inode.c (unionfs_setattr): calls copyup when trying to setattr for a + RO branch. + + * fist.h (FISTBUG): imported from ncryptfs. + +2003-09-17 Harikesavan Pathangi Krishnan + + * inode.c: Fixed the bug related to creation of whiteouts on the + leftmost branch when a directory on that branch is rmdir'ed. + +2003-09-16 Charles P. Wright + + * subr.c: fixed indentation for readdir actors + + * inode.c (unionfs_rmdir): create_whiteout can just not do anything + + * unionfs.h (itohi, dtohd): Reference count check before returning + to user space. + +2003-09-16 Puja Gupta , Hari, Mohammad. + + * inode.c (unionfs_create): removed a dget, was giving a seg fault. + (unionfs_lookup): error check for lookup_one_len added. + (unionfs_rmdir_all): checking if rmdir failed on leftmost, and if it + had whiteouts, remove them, and call vfs_rmdir again, to create + whiteout. + +2003-09-16 Mohammad Nayyer Zubair Hari + + * main.c (unionfs_interpose()): removed unnecessary fist_prints() + +2003-09-16 Mohammad Nayyer Zubair Hari + + * print.c (fist_print_dentry() and fist_print_inode()): removed + the anding code when printing the mode of the inode. Was printing + a negative mode value. + +2003-09-15 Puja Gupta , Hari, Mohammed. + + * subr.c: fput for copied up file not required. + + * print.c: print level 9 changed to 8. Level 9 doesn't get printed. + + * unionfs.h (IS_COPYUP_ERR, IS_WRITE_FLAG): Added. + + * subr.c (unionfs_copyup_file): Checks added, replaced variables, fixed. + + * inode.c (unionfs_permission): One code for files and directories. + Error bypassed for -EROFS, but not for leftmost branch. + + * file.c (unionfs_open): Check added for RO partition, and call copyup. + +2003-09-15 Mohammad Nayyer Zubair Hari + + * inode.c (unionfs_mknod): done. removes whiteout if + present. format similar to unionfs_mkdir(). Tested. + +2003-09-14 Puja Gupta + + * inode.c (do_rename): Fixed parent directory for lookup of whiteout. + (unionfs_rename_whiteout): Added case for DELETE_WHITEOUT for rename. + (unionfs_rename): Check destination->inode before S_ISDIR. + +2003-09-14 Charles P. Wright + + * file.c (unionfs_filldir): Remove a duplicate from the list if + filldir fails. + + * dentry.c (unionfs_d_revalidate, unionfs_d_compare, unionfs_d_hash): + Turned into utility functions for printing to reduce volume of debug + output. + +2003-09-14 Puja Gupta + + * inode.c (unionfs_rename_all): fix for bindex counter. + (unionfs_rename_all): check if error not occured in bstart of + destination. + (unionfs_rename_all): whiteout is always create in the bstart of + source, not to go to left of it. + +2003-09-14 Charles P. Wright + + * Detect EIO on single directories by doing duplicate elimination + routine. + +2003-09-13 Puja Gupta , Jay. + + * inode.c: added dput for hidden_dentry, whiteout_dentry that + are not used after lookup. + + * subr.c: added dput for hidden_dentry, whiteout_dentry that + are not used after lookup. + +2003-09-13 Charles P. Wright + + * PASSERT is for pointers. ASSERT is for non-pointer conditions. + +2003-09-12 Charles P. Wright + + * main.c: Sanity checks on flag=. + + * subr.c (create_whiteout_left): Balanced a dput w/ a dget. + + * print.c: exit should be level 5, not 4. hidden_dentries/inodes + are level 9 instead of 8. + + * Changed ASSERT to Oops when the pointer is poisoned. + + * Changed ASSERT(foo != NULL) to ASSERT(foo) + + * Default flag is 0x0. + +2003-09-11 Puja Gupta , Hari, Zubair, Jay. + + * inode.c (unionfs_create, unionfs_symlink): whiteouts truncated + to zero and ctime updated. + + * unionfs.h: added flags COPYUP_OWNER, COPYUP_FS_MOUNTER, + redefined flags. + + * inode.c: unlock parent added. + + * file.c (unionfs_open): check for error and call copyup on EROFS. + + * inode.c (unionfs_unlink): changed the function like rmdir. Used + rename instead of unlink, create for whiteouts. + (unionfs_mkdir): restructured code. + + * unionfs.h (GLOBAL_ERR_PASSUP): removed DELETE_ERR_PASSUP. + GLOBAL_ERR_PASSUP indicates whether to pass error back or create + whiteouts/try again. + + * inode.c (unionfs_create, unionfs_symlink, unionfs_link): + restructured changed *_ERR_PASSUP to GLOBAL_ERR_PASSUP. + + * unionfs.h (GLOBAL_ERR_PASSUP): added, for all functions, + whether, on error, should passup or try to make up for error + encountered. + + * inode.c (unionfs_symlink): updating bstart, bend for symlink + create. Restructured the code. + +2003-09-11 Mohammad Nayyer Zubair + + * inode.c (unionfs_symlink): done. removes whiteout if + present. same format as in unionfs_create() + + * inode.c (unionfs_link): creating destination path in source's + branch instead of vice versa + + * inode.c (unionfs_rmdir_all): removed unneccessary d_drop() + call. unionfs_rmdir() calls it at the end + +2003-09-10 Charles P. Wright + + * inode.c (unionfs_rmdir): Split into several functions. + + * subr.c (delete_whiteouts): Delete all whiteouts in + a given directory in a given branch. + + * subr.c (check_empty): Check if a directory is empty. + +2003-09-10 Puja Gupta , Hari. + + * unionfs.h: unionfs_interpose now returns void. + + * subr.c (unionfs_partial_lookup): removed extra dput. + unionfs_reinterpose now returns void. + + * main.c (unionfs_reinterpose): is now a void function and igrab + was called twice for already existing hidden inode. Fixed. + + * file.c (single_branch_filldir): removed fist_dprint printed + extra newline character on commandline during readdir. + + * unionfs.h (COPYUP_CURRENT): removed multiple definition. + +2003-09-09 Puja Gupta + + * subr.c (unionfs_full_lookup): Removed this function, was similar + to unionfs_partial_lookup. + + * inode.c (unionfs_unlink): added if-else for three different + options for unlink, and also for error handling (passup or + whiteout). Replaced unionfs_full_lookup with + unionfs_partial_lookup (Both functions were same, just change in + name). Added dget, dput at appropriate places. + + * unionfs.h (DELETE_FIRST, DELETE_WHITEOUT, DELETE_ERR_PASSUP): + added, and removed all other unlink flags. + +2003-09-09 Mohammad Nayyer Zubair Hari + + * inode.c (unionfs_lookup): returning EIO for directory whiteout. + +2003-09-02 Mohammad Nayyer Zubair Hari + + * inode.c: mkdir and create revisted, restructured and tested + +2003-09-08 Charles P. Wright + + * file.c (unionfs_write): Fixed positioning code. + +2003-09-07 Puja Gupta + + * inode.c (unionfs_create): added dget, get_parent, double_lock + and error check for vfs_rename of whiteouts. Added d_drop for any + negative dentry at the end of function. + (unionfs_mkdir): added error check for vfs_unlink of whiteouts, + unionfs_interpose, create_dir_whs. Removed multiple unlock_dir. + + * subr.c (create_dir_whs_filldir, create_whiteout_left): + changed char name[PATH_MAX] to char *name. + + * inode.c (unionfs_create, unionfs_lookup, unionfs_unlink) + (unionfs_mkdir): changed char name[PATH_MAX] to char *name. + (unionfs_rmdir): removed unused name, wh_name. + +2003-09-06 Puja Gupta + + * inode.c (unionfs_create): reverted back to the loop for create + from bstart to zeroth branch. + +2003-09-02 Mohammad Nayyer Zubair + + * inode.c: file to file rename done. + + * major items left: dir to dir rename + + * minor items left: mknod, symlink + +2003-09-05 Charles P. Wright + + * file.c (unionfs_filldir, single_branch_filldir): Removed memory + leak for debug output in filldirs. + (unionfs_readdir): Return -failure from filldir (previously our + filldir had no error propagation). + + * ioctls via extended attributes works. + + * branchman.c (unionfs_ioctl_delbranch): Don't let people delete + the last remaining branch. It would be cooler if we could, but + probably not worth all the effort to make the root dentry behave + properly when there is nothing underneath it. + + * file.c: Removed branch management functions. + + * inode.c): xattr functions. + + * branchman.c: Branch management functions. + +2003-09-03 Charles P. Wright + + * super.c (unionfs_clear_inode): Fixed cleanup of stale inodes. + + * fist.h (ASSERT2): Macro to print out caller when we fail. + + * Changed ASSERT2's to ASSERT, since they weren't really being + used as an ASSERT2. + + * unionfs.h (itohi, dtohd): Converted to a function, added an + ASSERT2 to make sure we don't underflow the array. + +2003-09-02 Mohammad Nayyer Zubair + + * subr.c: removed the unnecessary vfs_rename() function call in + create_whiteout_left(). Doing vfs_create() directly. + + * inode.c: fixes in lookup(). Started on unionfs_rename(). + + +2003-09-01 Mohammad Nayyer Zubair + + * subr.c: added function int remove_whiteouts(dentry_t *dentry, + dentry_t *hidden_dentry, int bindex) + Called by unionfs_rmdir(). Does vfs_readdir() and then unlinks all + whiteouts entries in it. + +2003-09-01 Charles P. Wright + + * Updated CUR_MAX_BRANCH and MOUNT_FLAG. + + * dentry.c (unionfs_d_revalidate): Make negative dentries that + just turned negative stale. + + * inode.c (unionfs_inode_revalidate): Make negative inodes stale. + + * stale_inode.c: Functions to make a magic stale inode. + +2003-09-01 Mohammad Nayyer Zubair + + * inode.c: unionfs_mkdir() done and works as per updated design. + Creating whiteout entries for all entires to the right. Details on + this are in the updated function_description.html file + + * file.c: a separate filldir function for single branch directory. + Ignoring whiteout entries. + + * subr.c: added a function: int create_whs_right(dentry_t *dentry, + int cur_index) Does a vfs_readdir on all directories starting from + cur_index + 1 and creates whiteout entries in cur_index (called in + unionfs_mkdir()). Details on this are in the updated + function_description.html file + + preliminary cases are working as expected + + Left: rename, mknod, symlink. + +2003-09-01 Charles P. Wright + + * file.c: Add branch ioctl and trimmed unionfs_dir_fops. + + * fist.h: Structure for add branch ioctl. + + * fist_ioctl.c: Add branch and cleaner branchcount ioctl. + +2003-08-31 Charles P. Wright + + * dentry.c (unionfs_d_revalidate): Do revalidation of dentries, + we now can remove the leftmost branch and expose old contents. + +2003-08-27 Mohammad Nayyer Zubair + + * inode.c: unionfs_unlink() implemented as per new design specs + (ignoring intermiate directories for now) + + * subr.c: added create_whiteout_left(dentry, index) function: + creates a whiteout in index, on error it proceeds to the left. + + * file.c: in fill_dir() ignoring whiteout entries + + * Left: mkdir, mknod, symlink, rmdir, rename + +2003-08-27 Charles P. Wright + + * Branch removal sort of works. + +2003-08-27 Mohammad Nayyer Zubair + + * inode.c: unionfs_create() implemented as per new design specs. + If found a .wh.foo entry, vfs_rename it to foo. + + * inode.c: unionfs_lookup() implemented as per new design specs. + If found a whiteout entry, just stop lookup + + * inode.c: unionfs_link() implemented as per new design specs + + * inode.c: unionfs_setattr() implemented as per new design specs + + * inode.c: unionfs_permission() implemented as per new design specs. + + * file.c: calling unionfs_copyup_file() in unionfs_open() if open + with specified flags fails with the current underlying file + + * subr.c: fixes in unionfs_copyup_file() + + * unionfs.h: modified mount time flags + + * Still left (according to new specs): mkdir, symlink, mknod, + unlink, rmdir, rename hard. + +2003-08-26 Charles P. Wright + + * inode.c (unionfs_inode_revalidate): Refresh inode from lower level. + + * fist_ioctl.c: Increment super generation number. + + * file.c (unionfs_ioctl_incgen): Increment super generation number. + +2003-08-20 Charles P. Wright + + * file.c: Branch reference counters updated on open/close. Need + to figure out how do to update them when a unionfs directory + becomes the cwd of a process. + + * doit.sh: Source doitopts or doitopts.`uname -n` so different + developers can have different setups, w/o changing CVS. + + * unionfs.h: Added generation number to super block, inode, and dentries. + + * attach.c: Not used. + +2003-08-13 Mohammad Nayyer Zubair + + * subr.c: some fixes in unionfs_copyup_file() (untested yet) + + * file.c: calling unionfs_copyup_file() if write() fails on the + leftmost file. Failure could result because of read-only + permissions on the file so probably should do copy up only if + branch is mounted RO. + + * unionfs.h: modified definition for unionfs_copyup_file() + +2003-08-13 Mohammad Nayyer Zubair + + * subr.c: added function: unionfs_copyup_file(inode_t *dir, + dentry_t *dentry, int oldbindex, int newbindex) first does + recursive directory creation, then does vfs_create() and then + reads PAGE_SIZE bytes from old file to new file + + * subr.c, inode.c: moved useful functions: + unionfs_create_parent_dir() and unionfs_partial_lookup() to subr.c + from inode.c + + * unionfs.h: definition for unionfs_copyup_file + + * Makefile: added subr.o to the list of OBJS + +2003-08-13 Puja Gupta + + * unionfs.h: added definition for unionfs_create_whiteout. + +2003-08-12 Puja Gupta + + * inode.c: reverting back to old copy without changes to rename. + +2003-08-04 Puja Gupta + + * unionfs.h (UNLINK_WHITEOUT, UNLINK_ALL_FIRST, UNLINK_ERR): New flags + for handling various unlink options. + + * main.c (unionfs_parse_options): fixed check for getting mount flags. + + * inode.c (unionfs_unlink): added unlink for whiteout. If flag is set + for UNLINK_WHITEOUT, try to create whiteout in branches to left, create + recursive subdirectories. Also added check for various flags for + unlink_first or unlink_all, and how to handle error on unlink. + (unionfs_create_parent_dir): check in for loop fixed. + +2003-08-04 Mohammad Nayyer Zubair + + * file.c: readdir is working fine with the hashtable in the private + data of unionfs file. + diff -ru on gcc tarball is successful + + * inode.c: implemented option 2 of link(), recursively creating source + path in destination branch when branches are different + + * unionfs.h: added two mount time flags + LINK_EXDEV: just return -EXDEV when dentries are on different branches + LINK_RECURSIVE: recursively create source path in target branch + +2003-07-31 Mohammad Nayyer Zubair + + * file.c: readdir/filldir's linked list converted into a hashtable + of size HASHTABLE_SIZE Duplicate elimation is working. Still to + test the gcc tarball. + + * unionfs.h: converted dir_list list head to an array of list + heads of size HASHTABLE_SIZE + + +2003-07-30 Puja Gupta + + * unionfs.h: changed to new flag, CREATE_RW_ERR, UNLINK_WHITEOUT, + UNLINK_ERR. Function definition for unionfs_partial_lookup. + + * inode.c (unionfs_partial_lookup): added, called from + unlink. Looks up the remaining files that were not looked up in + unionfs_lookup. + (unionfs_unlink): code now transferred to unionfs_partial_lookup. + (unionfs_create): flag name is CREATE_RW_ERR. + + * inode.c (unionfs_create): changed to new flag values. + + * unionfs.h (IS_SET): changed flags to be exclusive. Also, changed + IS_SET to work on place value of bit. + +2003-07-27 Puja Gupta + + * file.c (unionfs_open): removed ftohd_index(file, bindex) = NULL. + unionfs_getdents_callback now has file_t *. + (unionfs_filldir, unionfs_readdir): changed the global list_head + for duplicate elimination to have a list_head in private data of + file. So, the list of name, namelen is stored in this new + list_head, and compared against this. + + * unionfs.h: added struct filldir_node to private data of file. + Thus, every file has unique and seperate list for readdir. + + * doit.sh: "flag=" instead of "flags=". + +2003-07-28 Mohammad Nayyer Zubair + + * inode.c: unionfs_link's option 1 done. Return -EXDEV if old and + new dentries are in different branches / starting option 2: if + different branches, creating destination path in the source branch + + * unionfs.h: added a simple function which returns the sum of all + the underlying inodes' nlink value + + * inode.c: calling the above function wherever nlinks value was + being set + +2003-07-24 Puja Gupta + + * unionfs.h: #defines for MOUNT_FLAG, CREATE_PASSUP, + CREATE_RW_ERR_PASSUP, CREATE_TRY_LEFT and IS_SET. + + * main.c (unionfs_parse_options): parse the mount time flag and + initialize it. + + * inode.c (unionfs_create): added handling of partial errors to + handle various cases: 1) Try to Left (default), 2) PassUp, 3) If + EPERM on RW branch, PassUp. + + * doit.sh: changed mount options to include "flag=". + +2003-07-24 Puja Gupta + + * inode.c (unionfs_setattr): added check for NULL hidden dentry. + (unionfs_inode_revalidate): added ASSERT2 for inode. + +2003-07-24 Mohammad Nayyer Zubair + + * file.c: in unionfs_open, now only opening leftmost file instead + of opening all of the hidden files. Opening all directories + though. + + * inode.c: setattr function done. Assigning 'correct' value to the + n_links variable in the unionfs inode. In inode_revalidate + function assigning 'correct' value to the n_links variable in the + unionfs inode. + + * main.c: just moved sum_nlinks variable to the beginning of the + interpose function. diff -ru on a randomly distributed + (recursive) gcc tarball is failing. Some duplicate files are + being listed. 'ls' on the directory containing above duplicate + files also lists the files twice. Files have same unionfs inode. + Checking out whats happening. + +2003-07-22 Mohammad Nayyer Zubair + + * main.c: in interpose() added lines which compute the sum of the + nlinks of the underlying inodes and assigns this sum to the + unionfs inode. + + * inode.c: in permission() added check for a null hidden inode + +2003-07-22 Puja Gupta + + * unionfs.h: init_file_array is now init_ftohf_ptr, + init_inode_array is now init_itohi_ptr, init_sb_array is now + init_stohs_ptr, added init_stohiddenmnt_ptr, init_dentry_info, + init_dentry_array is now init_dtohd_ptr. All this makes sure that + all allocated memory is initialized to zero before its being used. + + * super.c (unionfs_read_inode): init_inode_array is now init_itohi_ptr. + + * main.c (unionfs_parse_options): init_priv_inode is now + init_dentry_info. + (unionfs_parse_options): memset, init_stohs_ptr, init_stohiddenmnt_ptr + added for initialize memory allocated to NULL. + (unionfs_read_super): init_dtohd_ptr added to initialize to NULL. + + * inode.c (unionfs_lookup): init_dentry_array is now init_dtohd_ptr. + + * file.c (unionfs_open): added init_ftohf_ptr, setting underlying + file pointer to NULL. + +2003-07-21 Puja Gupta + + * inode.c (unionfs_create): get rid of the hidden dentry that lead + to an unsuccessful attempt to create. Update bstart, bend + accordingly. + (unionfs_create_parent_dir): added "count--" for proper pointer + position. Updated private data, bstart for the dentry and inode of + intermediate directories created. Also, updated bend for the + negative dentry returned from this function to create. + + * main.c (unionfs_interpose): removed comments and extra line + spaces. + +2003-07-20 Puja Gupta + + * unionfs.h (MAX_DIR_CREATE): added MAX_DIR_CREATE for the number of + directories allowed to be created in recursive subdir creation. + Should be dynamic. + + * inode.c (unionfs_create): changed to handle partial error by + recursively creating directory to left branches. + (unionfs_create_parent_dir): added this function, it creates + hidden directory path for a given parent inode and dentry in + specified branch. (Still to test) + +2003-07-15 Mohammad Nayyer Zubair + + * inode.c : Found a bug while mounting unionfs on + a randomly distributed (recursive) am-utils tarball on 5 branches. + Basically we were not memsetting the array of pointers to hidden + objects after kmallocing a CUR_MAX_BRANCH number of pointers for + dentry, inode, file and sb. They were assumed to be NULL in + lookup and hence were not explicitly set to NULL in the array when + requireed. + + * unionfs.h: Added macros which initialize hidden arrays to NULL + +2003-07-15 Puja Gupta + + * inode.c (unionfs_lookup): added few debug statements. + (unionfs_unlink): Since lookup for file had just the leftmost + entry, unlink now internally calls lookup for all remaining + branches. And the current policy for unlink is, unlink + all. (Tested). + + * unionfs.h: added definition for unionfs_reinterpose. + + * main.c (unionfs_reinterpose): This new function takes a dentry + with leftmost hidden inode interposed, and reinterposes the newly + lookedup hidden inodes in remaining branches. Called from + unionfs_unlink. + + * mount_tarball.sh, mount_tarball_random.sh: These scripts are now + moved to ../tools directory. + +2003-07-13 Puja Gupta + + * inode.c (unionfs_lookup): fixed initialization of negative dentry. + + * print.c (fist_print_inode), (fist_print_file),(fist_print_dentry), + (fist_print_sb): made more readable, pointers now printed with %p. + +2003-07-10 Puja Gupta + + * super.c (unionfs_statfs): check for duplicate superblock data being + added. + (unionfs_umount_begin): check for NULL hidden_sb, hidden_sb->s_op. + +2003-07-10 Mohammad Nayyer Zubair + + * print.c: fist_print_inode(): prints underlying inodes if any. + fist_print_dentry(): prints underlying dentries if any. + fist_print_files(): prints underlying files if any. + fist_print_superblock(): prints underlying sbs if any. + +2003-07-10 Mohammad Nayyer Zubair + + * print.c: improved the formatting of printing inode, dentry and + sb + + * main.c, inode.c removed unneccessary lines printing debugging + info in interpose() + +2003-07-13 Mohammad Nayyer Zubair + + * file.c inode.c: removed debugging info lines + + * created mount_tarball.sh. right now, distributes an equal + number of files/dirs among N branches tar-ing am-utils + through unionfs mount point gives same package buildall + through mount point gives same structure/files diff -urN linux-2.6.17-rc1-mm3-orig/fs/unionfs/INSTALL linux-2.6.17-rc1-mm3-new/fs/unionfs/INSTALL --- linux-2.6.17-rc1-mm3-orig/fs/unionfs/INSTALL 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/unionfs/INSTALL 2006-04-11 20:24:21.000000000 +0000 @@ -0,0 +1,184 @@ +To build Unionfs, there are two main components: + +- unionfs.ko: the kernel module in 2.6 + +- unionctl: a user utility which allows you to add and remove branches + +You should be able to just type "make" and Unionfs will build itself for the +running kernel. The Makefile will look for your running kernel sources in +/lib/modules/`uname -r`/build/include. + +If your kernel sources are located in a different directory, create a +"fistdev.mk" file along the lines of: +LINUXSRC=/path/to/my/kernel/sources/linux-2.6.xx +TOPINC=-I$(LINUXSRC)/include +MODDIR=/lib/modules/2.6.xx + +LINUXSRC points to the root of a Linux source tree. TOPINC should point to +that tree's include directory. Finally, the module will be copied to +$MODDIR/kernel/fs/unionfs.ko. + +You must set all three variables together, otherwise you can have problems +compiling, installing, or loading the module. + +You also need to have the headers for e2fsprogs installed, on Red Hat +derived systems (like Fedora Core), this means that you need to have +e2fsprogs-devel installed. + +There are two Makefile options related to extended attribute support, +which is turned off by default. You should define UNIONFS_XATTR to turn +it on. Vanilla kernels should work automatically, but if you (or your +vendor) has applied the ACL/EA patches you might need to define +FIST_SETXATTR_CONSTVOID to correct the setxattr operation's function +prototype. + +Using fistdev.mk, you can also turn off the debugging print system, +which adds to the modules code size significantly. Just add +"EXTRACFLAGS=-DUNIONFS_NDEBUG" to fistdev.mk. + +The doit.sh script included in the distribution will mount unionfs +with two branches (/branch0 and /branch1) by default. You can use it +as an example and edit to your tastes. + +To install unionfs run "make install". This copies unionfs.o into +/lib/modules/`uname -r`/kernel/fs/; copies the utilities into +/usr/local/sbin; and copies man pages into /usr/local/man; + +fistdev.mk summary: +UNIONFS_OPT_CFLAG By default -O2. If you want a different optimization + level change this variable. +UNIONFS_DEBUG_CFLAG By default -g. If you want to remove debug, set + this variable to nothing. This will result in a + smaller (but harder to debug) Unionfs. +EXTRACFLAGS Additional stuff to pass to the compiler, this + is useful when combined with the definitions below. + (e.g., EXTRACFLAGS=-DUNIONFS_NDEBUG to turn off + debugging). +LINUXSRC Where to find the kernel build directory. +TOPINC Where to find the kernel headers. +PREFIX Where to install Unionfs utilities. + By default /usr/local. +MODPREFIX What is the prefix to the root directory for modules, + by default this is unset. Your modules will end up + in /lib/modules/`uname -r`/kernel/fs. With + MODPREFIX=/foo they end up in + /foo/lib/modules/`uname -r`/kernel/fs. +UNIONFS_IMAP Compiles in persistent inode code. Mount should fail + if you try to use the imap option without this. +Define options summary: +UNIONFS_NDEBUG Turn off debugging facility (reduces code size). +UNIONFS_UNSUPPORTED Bypass kernel versions checks. +SUPPORT_BROKEN_SETUP Enable sendfile, which allows you to run losetup, but + if you try to write to a loop file, you will cause + an Oops. +UNIONFS_DELETE_ALL Enable delete=all mode. + +Known interactions with other kernel features/patches: +* If you get an error like this on Fedora Core 4: + make: *** /lib/modules/2.6.11-1.1369_FC4/build: No such file or directory. Stop. + Then you need to install the kernel-devel package. + +* If you get an error like this: + unionimap.h:9:23: uuid/uuid.h: No such file or directory + Followed, by more parse errors, then you need to make sure you have + e2fsprogs-devel installed (or your distribution's equivalent). + +* Some NFS servers return -EACCES instead of -EROFS when they are exported + read-only. This means that we can't legitimately determine when a user is + not allowed to access a file. To enable a hack so that NFS appears to work + correctly (but NFS ACLs will break), mount the nfs-branch with mode nfsro. + +* If you want to export Unionfs over NFS, then you need to add + extra information to /etc/exports. knfsd will not export Unionfs unless + you have an fsid option in /etc/exports. This is because Unionfs has no + real device. See man exports(5) for more information on fsid. + +* If you want to use Unionfs as your root file system you need to use + pivot_root. www.linux-live.org provides scripts for creating live CDs with + Unionfs as the root. The following commands are adapted from the linuxrc + from linux-live 5.0.16. You may want to look at the original script from + www.linux-live.org if you are having problems. SLAX (www.slax.org) is a + an actual live CD distribution based on these scripts. + + UNION=/union + MEMORY=/memory + MOUNTDIR=/mnt + CHANGES=$MEMORY/changes + + mkdir -p $UNION + + mkdir -p $MEMORY + mount -t tmpfs tmpfs $MEMORY + + mkdir -p $CHANGES + mount -t unionfs -o dirs=$CHANGES=rw unionfs $UNION + + # Here other things are added to the Union, by using unionctl. + + cd $UNION + mkdir -p initrd + pivot_root . initrd + exec chroot . /bin/bash + # You should never get here + +* If you want to use losetup, then you need to define -DSUPPORT_BROKEN_LOSETUP + You will be able to use it read-only, but when you try to use it read-write, + you will get an Oops. This should eventually be fixed when we have our + own address-space operations (so that we can support sendfile). + +* Selinux requires extended attributes (but has not been tested by us). + You should compile Unionfs with Extended Attribute support by adding + EXTRACFLAGS=-DUNIONFS_XATTR to fistdev.mk. After this, you can follow + the following instructions are from Jaspreet Singh: + + 1. Install strict/targetted selinux policy sources + 2. Open /etc/selinux//src/policy/fs_use + 3. Append "fs_use_xattr unionfs system_u:object_r:fs_t;" + 4. Compile, install, and reload the selinux policy + + "There were a couple of issues with Unionfs but they were minor." + +* tmpfs does not support fsyncing directories, so if you have a Union with + tmpfs as the leftmost branch, fsync returns EINVAL. + +Other known limitations: +* Unionfs does not provide cache coherency. What this means to you is that + if you directly modify the lower-level branches, then Unionfs will get + confused. You can tell Unionfs to throw out its cache and recreate all + of its objects (lazily), by running "uniondbg -g UNION". + + It is especially dangerous to create or remove whiteouts from underneath + Unionfs, as there are several places where it ASSERTs on invariants + that must be true (e.g., if the file exists, the whiteout should not and + vice versa). + +* Unionfs doesn't support sendfile. This often manifests itself as apache + serving zero length files. You can turn off sendfile n Apache with the + EnableSendfile httpd.conf directive (see + http://httpd.apache.org/docs/2.0/mod/core.html). This is also the reason + that losetup is unsupported, Unionfs needs its own address space operations + otherwise upper and lower-level files and pages get mixed and matched. + +* If you restart an NFS server, you will get ESTALE errors on the client + because Unionfs does not have persistent inode numbers. You should also + consider NFS over TCP, so lost packets don't cause readdir to get confused. + +* Renaming a directory on a read-only branch returns an EXDEV error. The "mv" + utility is designed to handle this error (at least GNU coreutils and busybox + will handle this correctly), so a user will not see anything. The only + issue will be applications that internally rename a directory, but do not + properly handle EXDEV (which is really a bug on the application's part). + +* The documentation needs to improve + +* The object code is much larger than it could be, but this can be solved by + enabling UNIONFS_NDEBUG and setting UNIONFS_DEBUG_CFLAG to the empty string. + +Integrating Unionfs into a 2.6 kernel source tree (2.4 is not supported): +1. First run patch-kernel.sh included with Unionfs. Its first argument is the + path to your kernel source tree. +2. Configure and compile your kernel as you normally would. Unionfs is at the + bottom of the the File systems -> Miscellaenous filesystems menu. +3. To install the Unionfs utilities (i.e., unionctl and uniondbg), run + "make utils install-utils" from the Unionfs source directory. +4. Boot into your new kernel, and enjoy Unionfs. diff -urN linux-2.6.17-rc1-mm3-orig/fs/unionfs/Makefile linux-2.6.17-rc1-mm3-new/fs/unionfs/Makefile --- linux-2.6.17-rc1-mm3-orig/fs/unionfs/Makefile 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/unionfs/Makefile 2006-04-22 12:42:04.479418302 +0000 @@ -0,0 +1,11 @@ +UNIONFS_VERSION = 1.1.4 +EXTRA_CFLAGS+=-DUNIONFS_VERSION=\"${UNIONFS_VERSION}\" +# This will disable debugging support +# EXTRA_CFLAGS+=-DUNIONFS_NDEBUG + +obj-$(CONFIG_UNION_FS) += unionfs.o + +unionfs-objs := subr.o dentry.o file.o inode.o main.o super.o \ + stale_inode.o branchman.o xattr.o rdstate.o copyup.o \ + dirhelper.o rename.o unlink.o lookup.o persistent_inode.o \ + commonfops.o dirfops.o print.o diff -urN linux-2.6.17-rc1-mm3-orig/fs/unionfs/NEWS linux-2.6.17-rc1-mm3-new/fs/unionfs/NEWS --- linux-2.6.17-rc1-mm3-orig/fs/unionfs/NEWS 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/unionfs/NEWS 2006-04-11 20:24:21.000000000 +0000 @@ -0,0 +1,209 @@ +* Unionfs 1.1.4 +- Backport: On error, NULL the pointers after free - Fixes nasty mount bug +- Backport: Fixed unionfs_permission on reiserfs and XFS +- Backport: Added check to see if its necessary to run the delete_all + regression tests +- Added explicit check for kernel versions newer than 2.6.15 +(If you want to use 2.6.16 or newer, look at Unionfs 1.2.x) + +* Unionfs 1.1.3 +- Simplified delete=whiteout +- Switched default delete mode to the simpler whiteout mode +- Disable delete=all by default (enable with -DUNIONFS_DELETE_ALL) +- Removed all copy up modes except preserve (the old default) +- Unlink partially copied up files +- Compartmentalize malloc debugging +- Fix uid/gid/mode not being reset when a whiteout is recreated +- Fix stale atime/mtime bugs for recreated whiteouts +- Cleaned up peristent inode code a bit (still unstable) + +* Unionfs 1.1.2 +- Added inode refcounting debugging tool +- Fixed race between lookup and d_free +- Fixed double free/unchecked malloc +- Fixed permission bug, creat/open truncates the running executable +- Fixed many reference counting bugs +- Moved code from fist.h into unionfs*.h +- Removed unneeded UNIONFS_XATTR +- Removed ASSERTs in favor of BUG_ONs +- Removed FISTBUG +- Rename dentry locking changed to use 2.6 calls +- Replaced NFS_SECURITY_HOLE by nfsro branch option +- Cleaned up code to "pass" a Sparse run +- RPM spec file updated +- Miscellaneous cleanups + +* Unionfs 1.1.1 +- Directory reading is fixed +- Improved locking for branch manipulation operations +- Fix for removing an opaque directory +- Some cleanups and error handling fixes to the copyup code + +* Unionfs 1.1.0 +- Removed excess 2.4 code (Unionfs 1.1.0 is for 2.6 only) +- Removed file locking code, as the VFS does a better job of it. +- Fixed off by one errors when kmallocing names in inode.c. +- Fixed several deadlock bugs. +- Fixed several rename and rmdir bugs. +- Permissions checked on files that are on read-only file systems +- AMD64 fix for compiling on gentoo. +- Properly update generation when we combine copyup and reopening. +- Cleanedup Makefile to remove 2.4 clutter. +- Added documentation for the MODDIR option in the INSTALL file. + +* Unionfs 1.0.14 +Features: +- Dropped unmaintained setattr,diropaque,delete=first and mount flags +- Updated unionfs.4 to reflect default mount modes +- You can use the root of the Union to remove branches with unionctl +- Use official Debian packaging files. +- Linux 2.6.13 support. + +Bug fixes: +- Fixed several dentry refcount bugs introduced by new deletion framework. +- Fixed uninitialized fd_set in the query ioctl. +- Branch reference counting now works across insertion and removal of branches. + This prevents the branch counts from getting "confused". +- Handle "/" as a branch in unionctl. +- Removed static buffer for debug prints in favor of vprintk. +- NFS silly renames avoided during several cases. +- Attempting a write lock causes a copyup, so that the underying flock will + work. +- Cleaned up mount option parsing. +- Improved link counting for directories. + +* Unionfs 1.0.13 +- Unionfs now handles deleting an open file (and several other deletion fixes). + Thanks to the Knoppix people and Tomas Matejicek for finding these bugs. +- The NODEBUG flag has been renamed to UNIONFS_NDEBUG. +- Instead of allocating all of the lower-level pointers in dentries, inodes + and files, we have a fixed number of them preallocated and malloc only if + we exceed that number. +- Fixed a dentry reference count bug with UNIONFS_NDEBUG. +- Support for persistent inode numbers across mounts. +- Added a "which-branch" ioctl and corresponding --query to unionctl, so that + it is possible to find out where a file or directory is coming from. +- In 2.6.11 and onwards, don't take the BKL for ioctls. + +* Unionfs 1.0.12 +- Copy-up is delayed until a write operation occurs, so spurious copy-ups are + avoided. +- Improved copyup UID spoofing for mkdir +- Instructions and shell script for compiling Unionfs into a monolothic kernel. +- Added support for opaque directories, which mask the contents of + lower-priority directories. This makes recreating a removed directory an + O(1) operation instead of an O(n) operation, and also makes subsequent + operations faster. There were also several whiteout related fixes bundled + in here. +- Fixed bug with whiteouts on Reiserfs +- Fixed bug when a file transitioned from a negative dentry to a positive + dentry (i.e., if you add a branch that inserts a file you previously + looked up, but did not exist) +- Fixed return code for extended attribute functions +- amd64 compile fixes +- Extended attribute copy-up +- Unionfs returns EXDEV instead of copying up a directory on move, this + fixes a bug where Unionfs would "forget" the contents of a copied up + directory after unmount. +- Fix for following symlinks +- Improved print routines (separated generic from unionfs printing routines) +- INSTALL has some instructions for using Unionfs as a root file system and + also for using Unionfs with Selinux. +- Dentry private data uses a separate kmem_cache and inodes in 2.6 use their + own kmem_cache. This should improve performance a bit because there are + fewer individual memory allocations (and less wasted space). + +* Unionfs 1.0.11 +This release fixes NFS exporting which was broken in 1.0.10. +- f_pos and lseek's offsets are unified, so readdir works on NFS exported + unionfs mounts again. + +* Unionfs 1.0.10 +This release fixes several bugs over 1.0.9, but it will not correctly work if +you want to NFS export it. For that you need to use 1.0.9 on 2.4. In 1.0.11, +we will fix NFS exports correctly. + +- Don't d_delete entries that don't have positive refcounts (Fabian Franz) +- Hardlinks should point to the same inode (Fabian Franz) +- File locking works correctly. +- Improved directory llseeking. +- snapmerge preserves permissions/times after copying files +- Improved Makefile +- Unionfs_d_revalidate copies attributes so the cache appears more coherent. +- Directory reading bug fixed. +- symlink copyup fixed +- Don't oops when creating files longer than 252 characters +- Some readdir fixes. +- df produces correct size results + +* Unionfs 1.0.9 +- Fixed copyup permissions bugs (Anton Farygin) +- NTFS mmap will no longer cause Oops (but it won't do the mmap, as NTFS + doesn't support mmap). +- Moved directory reading helper routines into dirhelper.c from subr.c +- We no longer require hacked copies of vfs_create and vfs_unlink +- Moved finding and adding filldir_nodes into rdstate.c +- Added a sample RPM spec file, for those who want to package Unionfs + (Note, we are currently not distributing source or binary RPMS) +- Fixed unionfs_link to allow hard-linking of files with a source of a + readonly file system. +- Fixed apt-get on rw/ro branch configuration (related to unionfs_link) +- Modified regression/link.sh to reflect the changes in unionfs_link. It + now includes tests to make sure linking on a read only filesystem works. +- Fixed an issue where unionfs_dir_llseek would cause an Oops + +* Unionfs 1.0.8 +- Fixed bug with readdir (but oddly not ls) that prevented that last entry + from being shown. +- Include INSTALL file with information about installing Unionfs. +- Fix unionctl bug with "/" as the path to search for +- Fix to copyup permissions on directories +- Allow llseek to current directory position, but no others + +* Unionfs 1.0.7 +- Fixed locking for readdir state, this fixes a kernel Oops on preemptive + kernels, and deadlocks on SMP kernels. +- Prevent readdir from giving -ESTALE on last entry. +- Fixes for gcc 2.9.5 (Alex de Landegraaf) +- Debian package scripts (Alex de Landegraaf) +- NFS Export on 2.6 (Sai Suman) +- Copyup permissions fix (Sai Suman) +- Warn when compiling on unsupported kernels (<2.6.9 or <2.4.20) +- Improved hardlink support (but not quite there yet) + +* Unionfs 1.0.6 +A lot of code has changed in the 1.0.6 release, so it may not be as stable as +the 1.0.5 release. + +- Makefile fix over 1.0.5 for install target on 2.6 +- unionfs.h, fist.h Makefile dependency fixed +- Support for readdir over NFS +- Hash table sizes are now based on the size of the lower-level directory +- In 2.4 inode private data resides in the inode.u field, saving memory +- Use kmem caches for filldir nodes rather than wasting space with extra + large kmallocs, also move short names inline + +* Unionfs 1.0.5 +- Support for both 2.6 and 2.4 kernels + +* Unionfs 1.0.4 +- Copyup now correctly handles directories, devices, and symlinks. +- Extended attributes are now off by default because they cause too many + compile problems. +- The regression tests is now included in the release, but is not actually + installed. These tests are rather primitive, but do check some fundamental + Unionfs behavior on multiple branches. + +* Unionfs 1.0.3 +- Compile fixes for older (and newer) compilers than found on Redhat 9. +- Compile fixes for older kernels. +- Don't Oops when passed bad directories. + +* Unionfs 1.0.2 +- Minor README updates + +* Unionfs 1.0.1 +- Fix to release target which includes manual pages. + +* Unionfs 1.0 +- First public release diff -urN linux-2.6.17-rc1-mm3-orig/fs/unionfs/README linux-2.6.17-rc1-mm3-new/fs/unionfs/README --- linux-2.6.17-rc1-mm3-orig/fs/unionfs/README 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/unionfs/README 2006-04-11 20:24:21.000000000 +0000 @@ -0,0 +1,86 @@ +Copyright (c) 2003-2006 Erez Zadok +Copyright (c) 2003-2006 Charles P. Wright +Copyright (c) 2005-2006 Josef Sipek +Copyright (c) 2005 Arun M. Krishnakumar +Copyright (c) 2005-2006 David P. Quigley +Copyright (c) 2003-2004 Mohammad Nayyer Zubair +Copyright (c) 2003 Puja Gupta +Copyright (c) 2003 Harikesavan Krishnan +Copyright (c) 2003-2006 Stony Brook University +Copyright (c) 2003-2006 The Research Foundation of State University of New York + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +We're pleased to announce the first release of a unioning file system +for Linux, called Unionfs. To download software and documentation, +see + + http://www.fsl.cs.sunysb.edu/project-unionfs.html + +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. This release also includes additional +preliminary features that were specifically designed for security +applications, such as snapshotting and sandboxing. + +This Unionfs release has been tested with recent 2.6 kernels. For +2.6 kernels, the minimum requirement is 2.6.9. You also need to have +the development headers for e2fsprogs installed. + +Unionfs is released under the GPL (see the COPYING file in the +distribution for details). + +For more information on using Unionfs, download the tarball and see +the following man pages: + +- unionfs.4: Describes how to mount unionfs + +- unionctl.8: Describes how to control an already mounted Union + +For more information about Unionfs internals (which we think are +really cool :-), see the following technical report at the above Web +site: + + C. P. Wright, J. Dave, P. Gupta, H. Krishnan, E. Zadok, and M. Zubair + "Versatility and Unix Semantics in a Fan-Out Unification File System" + Technical Report FSL-04-01b, October 2004 + Computer Science Department, Stony Brook University + http://www.fsl.cs.sunysb.edu/docs/unionfs-tr/unionfs.pdf + +In addition, you can find an article in Linux Journal (December 2004 +issue) titled "Unionfs: Bringing File Systems Together." It is available +online at http://www.linuxjournal.com/article/7714. + +See the INSTALL file for instructions on building Unionfs, iteractions with +kernel features, and known bugs and limitations. + +To report bugs, please email them to the "fistgen@filesystems.org" +list (see www.filesystems.org), or submit them via Bugzilla to +https://bugzilla.filesystems.org/. But reports with fixes are most +welcome. + +Enjoy, +Erez and Charles (on behalf of the Unionfs team) + +############################################################################## +## For Emacs: +# Local variables: +# fill-column: 70 +# End: +############################################################################## diff -urN linux-2.6.17-rc1-mm3-orig/fs/unionfs/branchman.c linux-2.6.17-rc1-mm3-new/fs/unionfs/branchman.c --- linux-2.6.17-rc1-mm3-orig/fs/unionfs/branchman.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/unionfs/branchman.c 2006-04-11 20:24:21.000000000 +0000 @@ -0,0 +1,563 @@ +/* + * Copyright (c) 2003-2006 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2006 Josef Sipek + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2005-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2006 Stony Brook University + * Copyright (c) 2003-2006 The Research Foundation of State University of New York + * + * For specific licensing information, see the COPYING file distributed with + * this package. + * + * This Copyright notice must be kept intact and distributed with all sources. + */ +/* + * $Id: branchman.c,v 1.62 2006/02/13 00:18:40 jsipek Exp $ + */ + +#include "unionfs.h" + +struct dentry **alloc_new_dentries(int objs) +{ + if (!objs) + return NULL; + + return KZALLOC(sizeof(struct dentry *) * objs, GFP_KERNEL); +} + +struct unionfs_usi_data *alloc_new_data(int objs) +{ + if (!objs) + return NULL; + + return KZALLOC(sizeof(struct unionfs_usi_data) * objs, GFP_KERNEL); +} + +static void fixputmaps(struct super_block *sb) +{ + struct unionfs_sb_info *spd; + struct putmap *cur; + int gen; + int i; + + print_entry_location(); + + spd = stopd(sb); + cur = spd->usi_putmaps[spd->usi_lastputmap - spd->usi_firstputmap]; + + for (gen = 0; gen < spd->usi_lastputmap - spd->usi_firstputmap; gen++) { + if (!spd->usi_putmaps[gen]) + continue; + for (i = 0; i <= spd->usi_putmaps[gen]->bend; i++) + spd->usi_putmaps[gen]->map[i] = + cur->map[spd->usi_putmaps[gen]->map[i]]; + } + + print_exit_location(); +} + +static int newputmap(struct super_block *sb) +{ + struct unionfs_sb_info *spd; + struct putmap *newmap; + int count = 0; + int i; + + print_entry_location(); + + spd = stopd(sb); + + i = sizeof(int) * (sbend(sb) + 1); + newmap = KMALLOC(sizeof(struct putmap) + i, GFP_KERNEL); + if (!newmap) { + print_exit_status(-ENOMEM); + return -ENOMEM; + } + + if (!spd->usi_firstputmap) { + spd->usi_firstputmap = 1; + spd->usi_lastputmap = 1; + + spd->usi_putmaps = KMALLOC(sizeof(struct putmap *), GFP_KERNEL); + if (!spd->usi_putmaps) { + KFREE(newmap); + print_exit_status(-ENOMEM); + return -ENOMEM; + } + } else { + struct putmap **newlist; + int newfirst = spd->usi_firstputmap; + + while (!spd->usi_putmaps[newfirst - spd->usi_firstputmap] && + newfirst <= spd->usi_lastputmap) { + newfirst++; + } + + newlist = + KMALLOC(sizeof(struct putmap *) * + (1 + spd->usi_lastputmap - newfirst), GFP_KERNEL); + if (!newlist) { + KFREE(newmap); + print_exit_status(-ENOMEM); + return -ENOMEM; + } + + for (i = newfirst; i <= spd->usi_lastputmap; i++) { + newlist[i - newfirst] = + spd->usi_putmaps[i - spd->usi_firstputmap]; + } + + KFREE(spd->usi_putmaps); + spd->usi_putmaps = newlist; + spd->usi_firstputmap = newfirst; + spd->usi_lastputmap++; + } + + newmap->bend = sbend(sb); + for (i = 0; i <= sbend(sb); i++) { + count += branch_count(sb, i); + newmap->map[i] = i; + } + for (i = spd->usi_firstputmap; i < spd->usi_lastputmap; i++) { + struct putmap *cur; + cur = spd->usi_putmaps[i - spd->usi_firstputmap]; + if (!cur) + continue; + count -= atomic_read(&cur->count); + } + atomic_set(&newmap->count, count); + spd->usi_putmaps[spd->usi_lastputmap - spd->usi_firstputmap] = newmap; + + print_exit_status(0); + return 0; +} + +int unionfs_ioctl_branchcount(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int err = 0; + int bstart, bend; + int i; + struct super_block *sb = file->f_dentry->d_sb; + + print_entry_location(); + + bstart = sbstart(sb); + bend = sbend(sb); + + err = bend + 1; + if (!arg) + goto out; + + for (i = bstart; i <= bend; i++) { + if (put_user(branch_count(sb, i), ((int __user *)arg) + i)) { + err = -EFAULT; + goto out; + } + } + + out: + print_exit_status(err); + return err; +} + +int unionfs_ioctl_incgen(struct file *file, unsigned int cmd, unsigned long arg) +{ + int err = 0; + struct super_block *sb; + + print_entry_location(); + + sb = file->f_dentry->d_sb; + + unionfs_write_lock(sb); + if ((err = newputmap(sb))) + goto out; + + atomic_inc(&stopd(sb)->usi_generation); + err = atomic_read(&stopd(sb)->usi_generation); + + atomic_set(&dtopd(sb->s_root)->udi_generation, err); + atomic_set(&itopd(sb->s_root->d_inode)->uii_generation, err); + + out: + unionfs_write_unlock(sb); + print_exit_status(err); + return err; +} + +int unionfs_ioctl_addbranch(struct inode *inode, unsigned int cmd, + unsigned long arg) +{ + int err; + struct unionfs_addbranch_args *addargs = NULL; + struct nameidata nd; + char *path = NULL; + int gen; + int i; + + int pobjects; + + struct unionfs_usi_data *new_data = NULL; + struct dentry **new_udi_dentry = NULL; + struct inode **new_uii_inode = NULL; + + struct dentry *root = NULL; + struct dentry *hidden_root = NULL; + + print_entry_location(); + + err = -ENOMEM; + addargs = KMALLOC(sizeof(struct unionfs_addbranch_args), GFP_KERNEL); + if (!addargs) + goto out; + + err = -EFAULT; + if (copy_from_user + (addargs, (const void __user *)arg, + sizeof(struct unionfs_addbranch_args))) + goto out; + + err = -EINVAL; + if (addargs->ab_perms & ~(MAY_READ | MAY_WRITE | MAY_NFSRO)) + goto out; + if (!(addargs->ab_perms & MAY_READ)) + goto out; + + err = -E2BIG; + if (sbend(inode->i_sb) > FD_SETSIZE) + goto out; + + err = -ENOMEM; + if (!(path = getname((const char __user *)addargs->ab_path))) + goto out; + + err = path_lookup(path, LOOKUP_FOLLOW, &nd); + + RECORD_PATH_LOOKUP(&nd); + if (err) + goto out; + if ((err = check_branch(&nd))) { + path_release(&nd); + RECORD_PATH_RELEASE(&nd); + goto out; + } + + unionfs_write_lock(inode->i_sb); + lock_dentry(inode->i_sb->s_root); + + root = inode->i_sb->s_root; + for (i = dbstart(inode->i_sb->s_root); i <= dbend(inode->i_sb->s_root); + i++) { + hidden_root = dtohd_index(root, i); + if (is_branch_overlap(hidden_root, nd.dentry)) { + err = -EINVAL; + goto out; + } + } + + err = -EINVAL; + if (addargs->ab_branch < 0 + || (addargs->ab_branch > (sbend(inode->i_sb) + 1))) + goto out; + + if ((err = newputmap(inode->i_sb))) + goto out; + + stopd(inode->i_sb)->b_end++; + dtopd(inode->i_sb->s_root)->udi_bcount++; + set_dbend(inode->i_sb->s_root, dbend(inode->i_sb->s_root) + 1); + itopd(inode->i_sb->s_root->d_inode)->b_end++; + + atomic_inc(&stopd(inode->i_sb)->usi_generation); + gen = atomic_read(&stopd(inode->i_sb)->usi_generation); + + pobjects = sbend(inode->i_sb) + 1; + + /* Reallocate the dynamic structures. */ + new_data = alloc_new_data(pobjects); + new_udi_dentry = alloc_new_dentries(pobjects); + new_uii_inode = KZALLOC(sizeof(struct inode *) * pobjects, GFP_KERNEL); + + if (!new_udi_dentry || !new_uii_inode || !new_data) { + err = -ENOMEM; + goto out; + } + + /* Copy the in-place values to our new structure. */ + for (i = 0; i < addargs->ab_branch; i++) { + atomic_set(&(new_data[i].sbcount), + branch_count(inode->i_sb, i)); + + new_data[i].branchperms = branchperms(inode->i_sb, i); + new_data[i].hidden_mnt = stohiddenmnt_index(inode->i_sb, i); + new_data[i].sb = stohs_index(inode->i_sb, i); + + new_udi_dentry[i] = dtohd_index(inode->i_sb->s_root, i); + new_uii_inode[i] = itohi_index(inode->i_sb->s_root->d_inode, i); + } + + /* Shift the ends to the right (only handle reallocated bits). */ + for (i = sbend(inode->i_sb) - 1; i >= (int)addargs->ab_branch; i--) { + int j = i + 1; + int pmindex; + + atomic_set(&new_data[j].sbcount, branch_count(inode->i_sb, i)); + + new_data[j].branchperms = branchperms(inode->i_sb, i); + new_data[j].hidden_mnt = stohiddenmnt_index(inode->i_sb, i); + new_data[j].sb = stohs_index(inode->i_sb, i); + new_udi_dentry[j] = dtohd_index(inode->i_sb->s_root, i); + new_uii_inode[j] = itohi_index(inode->i_sb->s_root->d_inode, i); + + /* Update the newest putmap, so it is correct for later. */ + pmindex = stopd(inode->i_sb)->usi_lastputmap; + pmindex -= stopd(inode->i_sb)->usi_firstputmap; + stopd(inode->i_sb)->usi_putmaps[pmindex]->map[i] = j; + + } + + /* Now we can free the old ones. */ + KFREE(dtopd(inode->i_sb->s_root)->udi_dentry); + KFREE(itopd(inode->i_sb->s_root->d_inode)->uii_inode); + KFREE(stopd(inode->i_sb)->usi_data); + + /* Update the real pointers. */ + dtohd_ptr(inode->i_sb->s_root) = new_udi_dentry; + itohi_ptr(inode->i_sb->s_root->d_inode) = new_uii_inode; + stopd(inode->i_sb)->usi_data = new_data; + + /* Re-NULL the new ones so we don't try to free them. */ + new_data = NULL; + new_udi_dentry = NULL; + new_uii_inode = NULL; + + /* Put the new dentry information into it's slot. */ + set_dtohd_index(inode->i_sb->s_root, addargs->ab_branch, nd.dentry); + set_itohi_index(inode->i_sb->s_root->d_inode, addargs->ab_branch, + IGRAB(nd.dentry->d_inode)); + set_branchperms(inode->i_sb, addargs->ab_branch, addargs->ab_perms); + set_branch_count(inode->i_sb, addargs->ab_branch, 0); + set_stohiddenmnt_index(inode->i_sb, addargs->ab_branch, nd.mnt); + set_stohs_index(inode->i_sb, addargs->ab_branch, nd.dentry->d_sb); + + atomic_set(&dtopd(inode->i_sb->s_root)->udi_generation, gen); + atomic_set(&itopd(inode->i_sb->s_root->d_inode)->uii_generation, gen); + + fixputmaps(inode->i_sb); + + out: + unlock_dentry(inode->i_sb->s_root); + unionfs_write_unlock(inode->i_sb); + + KFREE(new_udi_dentry); + KFREE(new_uii_inode); + KFREE(new_data); + KFREE(addargs); + if (path) + putname(path); + + print_exit_status(err); + + return err; +} + +/* This must be called with the super block already locked. */ +int unionfs_ioctl_delbranch(struct super_block *sb, unsigned long arg) +{ + struct dentry *hidden_dentry; + struct inode *hidden_inode; + struct vfsmount *hidden_mnt; + struct dentry *root_dentry; + struct inode *root_inode; + int err = 0; + int pmindex, i, gen; + + print_entry("branch = %lu ", arg); + lock_dentry(sb->s_root); + + err = -EBUSY; + if (sbmax(sb) == 1) + goto out; + err = -EINVAL; + if (arg < 0 || arg > stopd(sb)->b_end) + goto out; + err = -EBUSY; + if (branch_count(sb, arg)) + goto out; + + if ((err = newputmap(sb))) + goto out; + + pmindex = stopd(sb)->usi_lastputmap; + pmindex -= stopd(sb)->usi_firstputmap; + + atomic_inc(&stopd(sb)->usi_generation); + gen = atomic_read(&stopd(sb)->usi_generation); + + root_dentry = sb->s_root; + root_inode = sb->s_root->d_inode; + + hidden_dentry = dtohd_index(root_dentry, arg); + hidden_mnt = stohiddenmnt_index(sb, arg); + hidden_inode = itohi_index(root_inode, arg); + + DPUT(hidden_dentry); + IPUT(hidden_inode); + mntput(hidden_mnt); + + for (i = arg; i <= (sbend(sb) - 1); i++) { + set_branch_count(sb, i, branch_count(sb, i + 1)); + set_stohiddenmnt_index(sb, i, stohiddenmnt_index(sb, i + 1)); + set_stohs_index(sb, i, stohs_index(sb, i + 1)); + set_branchperms(sb, i, branchperms(sb, i + 1)); + set_dtohd_index(root_dentry, i, + dtohd_index(root_dentry, i + 1)); + set_itohi_index(root_inode, i, itohi_index(root_inode, i + 1)); + stopd(sb)->usi_putmaps[pmindex]->map[i + 1] = i; + } + + set_dtohd_index(root_dentry, sbend(sb), NULL); + set_itohi_index(root_inode, sbend(sb), NULL); + set_stohiddenmnt_index(sb, sbend(sb), NULL); + set_stohs_index(sb, sbend(sb), NULL); + + //XXX: Place check for inode maps and removal of branch here + + stopd(sb)->b_end--; + set_dbend(root_dentry, dbend(root_dentry) - 1); + dtopd(root_dentry)->udi_bcount--; + itopd(root_inode)->b_end--; + + atomic_set(&dtopd(root_dentry)->udi_generation, gen); + atomic_set(&itopd(root_inode)->uii_generation, gen); + + fixputmaps(sb); + + /* This doesn't open a file, so we might have to free the map here. */ + if (atomic_read(&stopd(sb)->usi_putmaps[pmindex]->count) == 0) { + KFREE(stopd(sb)->usi_putmaps[pmindex]); + stopd(sb)->usi_putmaps[pmindex] = NULL; + } + + out: + unlock_dentry(sb->s_root); + print_exit_status(err); + + return err; +} + +int unionfs_ioctl_rdwrbranch(struct inode *inode, unsigned int cmd, + unsigned long arg) +{ + int err; + struct unionfs_rdwrbranch_args *rdwrargs = NULL; + int gen; + + print_entry_location(); + + unionfs_write_lock(inode->i_sb); + lock_dentry(inode->i_sb->s_root); + + if ((err = newputmap(inode->i_sb))) + goto out; + + err = -ENOMEM; + rdwrargs = KMALLOC(sizeof(struct unionfs_rdwrbranch_args), GFP_KERNEL); + if (!rdwrargs) + goto out; + + err = -EFAULT; + if (copy_from_user + (rdwrargs, (const void __user *)arg, + sizeof(struct unionfs_rdwrbranch_args))) + goto out; + + err = -EINVAL; + if (rdwrargs->rwb_branch < 0 + || (rdwrargs->rwb_branch > (sbend(inode->i_sb) + 1))) + goto out; + if (rdwrargs->rwb_perms & ~(MAY_READ | MAY_WRITE | MAY_NFSRO)) + goto out; + if (!(rdwrargs->rwb_perms & MAY_READ)) + goto out; + + set_branchperms(inode->i_sb, rdwrargs->rwb_branch, rdwrargs->rwb_perms); + + atomic_inc(&stopd(inode->i_sb)->usi_generation); + gen = atomic_read(&stopd(inode->i_sb)->usi_generation); + atomic_set(&dtopd(inode->i_sb->s_root)->udi_generation, gen); + atomic_set(&itopd(inode->i_sb->s_root->d_inode)->uii_generation, gen); + + err = 0; + + out: + unlock_dentry(inode->i_sb->s_root); + unionfs_write_unlock(inode->i_sb); + KFREE(rdwrargs); + + print_exit_status(err); + + return err; +} + +int unionfs_ioctl_queryfile(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int err = 0; + fd_set branchlist; + + int bstart = 0, bend = 0, bindex = 0; + struct dentry *dentry, *hidden_dentry; + + print_entry_location(); + + dentry = file->f_dentry; + lock_dentry(dentry); + if ((err = unionfs_partial_lookup(dentry))) + goto out; + bstart = dbstart(dentry); + bend = dbend(dentry); + + FD_ZERO(&branchlist); + + for (bindex = bstart; bindex <= bend; bindex++) { + hidden_dentry = dtohd_index(dentry, bindex); + if (!hidden_dentry) + continue; + if (hidden_dentry->d_inode) + FD_SET(bindex, &branchlist); + } + + err = copy_to_user((void __user *)arg, &branchlist, sizeof(fd_set)); + if (err) { + err = -EFAULT; + goto out; + } + + out: + unlock_dentry(dentry); + err = err < 0 ? err : bend; + print_exit_status(err); + return (err); +} + +/* + * + * vim:shiftwidth=8 + * vim:tabstop=8 + * + * For Emacs: + * Local variables: + * c-basic-offset: 8 + * c-comment-only-line-offset: 0 + * c-offsets-alist: ((statement-block-intro . +) (knr-argdecl-intro . 0) + * (substatement-open . 0) (label . 0) (statement-cont . +)) + * indent-tabs-mode: t + * tab-width: 8 + * End: + */ diff -urN linux-2.6.17-rc1-mm3-orig/fs/unionfs/commonfops.c linux-2.6.17-rc1-mm3-new/fs/unionfs/commonfops.c --- linux-2.6.17-rc1-mm3-orig/fs/unionfs/commonfops.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/unionfs/commonfops.c 2006-04-11 20:24:21.000000000 +0000 @@ -0,0 +1,716 @@ +/* + * Copyright (c) 2003-2006 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2006 Josef Sipek + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2005-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2006 Stony Brook University + * Copyright (c) 2003-2006 The Research Foundation of State University of New York + * + * For specific licensing information, see the COPYING file distributed with + * this package. + * + * This Copyright notice must be kept intact and distributed with all sources. + */ +/* + * $Id: commonfops.c,v 1.56 2006/02/15 09:07:28 jsipek Exp $ + */ + +#include "unionfs.h" + +/* We only need this function here, but it could get promoted to unionfs.h, if + * other things need a generation specific branch putting function. */ +static inline void branchput_gen(int generation, struct super_block *sb, + int index) +{ + struct putmap *putmap; + + if (generation == atomic_read(&stopd(sb)->usi_generation)) { + branchput(sb, index); + return; + } + + BUG_ON(stopd(sb)->usi_firstputmap > generation); + BUG_ON(stopd(sb)->usi_lastputmap < generation); + + putmap = + stopd(sb)->usi_putmaps[generation - stopd(sb)->usi_firstputmap]; + BUG_ON(index < 0); + BUG_ON(index > putmap->bend); + BUG_ON(putmap->map[index] < 0); + branchput(sb, putmap->map[index]); + if (atomic_dec_and_test(&putmap->count)) { + stopd(sb)->usi_putmaps[generation - stopd(sb)->usi_firstputmap] + = NULL; + fist_dprint(8, "Freeing putmap %d.\n", generation); + KFREE(putmap); + } +} + +static char *get_random_name(int size, unsigned char *name) +{ + int i; + int j; + unsigned char *tmpbuf = NULL; + + if (size <= WHLEN) + return NULL; + + if (!name) + name = KMALLOC(size + 1, GFP_KERNEL); + if (!name) { + name = ERR_PTR(-ENOMEM); + goto out; + } + strncpy(name, WHPFX, WHLEN); + + tmpbuf = KMALLOC(size, GFP_KERNEL); + if (!tmpbuf) { + KFREE(name); + name = ERR_PTR(-ENOMEM); + goto out; + } + + get_random_bytes((void *)tmpbuf, (size - 3) / 2); + + j = WHLEN; + i = 0; + while ((i < (size - 3) / 2) && (j < size)) { + /* get characters in the 0-9, A-F range */ + + name[j] = + (tmpbuf[i] % 16) < + 10 ? (tmpbuf[i] % 16) + '0' : (tmpbuf[i] % 16) + 'a'; + j++; + if (j == size) + break; + name[j] = + (tmpbuf[i] >> 4) < + 10 ? (tmpbuf[i] >> 4) + '0' : (tmpbuf[i] >> 4) + 'a'; + j++; + + i++; + } + + name[size] = '\0'; + + out: + KFREE(tmpbuf); + return (name); + +} + +static int copyup_deleted_file(struct file *file, struct dentry *dentry, + int bstart, int bindex) +{ + int attempts = 0; + int err; + int exists = 1; + char *name = NULL; + struct dentry *tmp_dentry = NULL; + struct dentry *hidden_dentry = NULL; + struct dentry *hidden_dir_dentry = NULL; + + print_entry_location(); + + /* Try five times to get a unique file name, fail after that. Five is + * simply a magic number, because we shouldn't try forever. */ + while (exists) { + /* The first call allocates, the subsequent ones reuse. */ + name = get_random_name(UNIONFS_TMPNAM_LEN, name); + err = -ENOMEM; + if (!name) + goto out; + + hidden_dentry = dtohd_index(dentry, bstart); + + tmp_dentry = LOOKUP_ONE_LEN(name, hidden_dentry->d_parent, + UNIONFS_TMPNAM_LEN); + err = PTR_ERR(tmp_dentry); + if (IS_ERR(tmp_dentry)) + goto out; + exists = tmp_dentry->d_inode ? 1 : 0; + DPUT(tmp_dentry); + + err = -EEXIST; + if (++attempts > 5) + goto out; + } + + err = copyup_named_file(dentry->d_parent->d_inode, file, name, bstart, + bindex, file->f_dentry->d_inode->i_size); + if (err) + goto out; + + /* bring it to the same state as an unlinked file */ + hidden_dentry = dtohd_index(dentry, dbstart(dentry)); + hidden_dir_dentry = lock_parent(hidden_dentry); + err = vfs_unlink(hidden_dir_dentry->d_inode, hidden_dentry); + unlock_dir(hidden_dir_dentry); + + out: + KFREE(name); + print_exit_status(err); + return err; +} + +int unionfs_file_revalidate(struct file *file, int willwrite) +{ + struct super_block *sb; + struct dentry *dentry; + int sbgen, fgen, dgen; + int bindex, bstart, bend; + struct file *hidden_file; + struct dentry *hidden_dentry; + int size; + + int err = 0; + + print_entry(" file = %p", file); + + dentry = file->f_dentry; + lock_dentry(dentry); + sb = dentry->d_sb; + unionfs_read_lock(sb); + if (!unionfs_d_revalidate(dentry, NULL) && !d_deleted(dentry)) { + err = -ESTALE; + goto out; + } + fist_print_dentry("file revalidate in", dentry); + + sbgen = atomic_read(&stopd(sb)->usi_generation); + dgen = atomic_read(&dtopd(dentry)->udi_generation); + fgen = atomic_read(&ftopd(file)->ufi_generation); + + BUG_ON(sbgen > dgen); + + /* 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)))) { + /* First we throw out the existing files. */ + bstart = fbstart(file); + bend = fbend(file); + for (bindex = bstart; bindex <= bend; bindex++) { + if (ftohf_index(file, bindex)) { + branchput_gen(fgen, dentry->d_sb, bindex); + fput(ftohf_index(file, bindex)); + } + } + + if (ftohf_ptr(file)) { + KFREE(ftohf_ptr(file)); + ftohf_ptr(file) = NULL; + } + + /* 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); + ftohf_ptr(file) = KZALLOC(size, GFP_KERNEL); + if (!ftohf_ptr(file)) { + err = -ENOMEM; + goto out; + } + + if (S_ISDIR(dentry->d_inode->i_mode)) { + /* We need to open all the files. */ + for (bindex = bstart; bindex <= bend; bindex++) { + hidden_dentry = dtohd_index(dentry, bindex); + if (!hidden_dentry) + continue; + + DGET(hidden_dentry); + mntget(stohiddenmnt_index(sb, bindex)); + branchget(sb, bindex); + + hidden_file = + DENTRY_OPEN(hidden_dentry, + stohiddenmnt_index(sb, bindex), + file->f_flags); + if (IS_ERR(hidden_file)) { + err = PTR_ERR(hidden_file); + goto out; + } else { + set_ftohf_index(file, bindex, + hidden_file); + } + } + } else { + /* We only open the highest priority branch. */ + hidden_dentry = dtohd(dentry); + if (willwrite && IS_WRITE_FLAG(file->f_flags) + && is_robranch(dentry)) { + for (bindex = bstart - 1; bindex >= 0; bindex--) { + + err = copyup_file(dentry-> + d_parent-> + d_inode, + file, + bstart, + bindex, + file-> + f_dentry-> + d_inode->i_size); + + if (!err) + break; + else + continue; + + } + atomic_set(&ftopd(file)->ufi_generation, + atomic_read(&itopd(dentry->d_inode)-> + uii_generation)); + goto out; + } + + DGET(hidden_dentry); + mntget(stohiddenmnt_index(sb, bstart)); + branchget(sb, bstart); + hidden_file = + DENTRY_OPEN(hidden_dentry, + stohiddenmnt_index(sb, bstart), + file->f_flags); + if (IS_ERR(hidden_file)) { + err = PTR_ERR(hidden_file); + goto out; + } + set_ftohf(file, hidden_file); + /* Fix up the position. */ + hidden_file->f_pos = file->f_pos; + + memcpy(&(hidden_file->f_ra), &(file->f_ra), + sizeof(struct file_ra_state)); + } + atomic_set(&ftopd(file)->ufi_generation, + atomic_read(&itopd(dentry->d_inode)-> + uii_generation)); + } + + /* Copyup on the first write to a file on a readonly branch. */ + if (willwrite && IS_WRITE_FLAG(file->f_flags) + && !IS_WRITE_FLAG(ftohf(file)->f_flags) && is_robranch(dentry)) { + fist_dprint(3, + "Doing delayed copyup of a read-write file on a read-only branch.\n"); + bstart = fbstart(file); + bend = fbend(file); + + BUG_ON(!S_ISREG(file->f_dentry->d_inode->i_mode)); + + for (bindex = bstart - 1; bindex >= 0; bindex--) { + if (!d_deleted(file->f_dentry)) { + err = + copyup_file(dentry->d_parent-> + d_inode, file, bstart, + bindex, + file->f_dentry-> + d_inode->i_size); + } else { + err = + copyup_deleted_file(file, dentry, bstart, + bindex); + } + + if (!err) + break; + else + continue; + + } + if (!err && (bstart > fbstart(file))) { + bend = fbend(file); + for (bindex = bstart; bindex <= bend; bindex++) { + if (ftohf_index(file, bindex)) { + branchput(dentry->d_sb, bindex); + fput(ftohf_index(file, bindex)); + set_ftohf_index(file, bindex, NULL); + } + } + fbend(file) = bend; + } + } + + out: + fist_print_dentry("file revalidate out", dentry); + unlock_dentry(dentry); + unionfs_read_unlock(dentry->d_sb); + print_exit_status(err); + return err; +} + +int unionfs_open(struct inode *inode, struct file *file) +{ + int err = 0; + int hidden_flags; + struct file *hidden_file = NULL; + struct dentry *hidden_dentry = NULL; + struct dentry *dentry = NULL; + int bindex = 0, bstart = 0, bend = 0; + int locked = 0; + int size; + + print_entry_location(); + + ftopd_lhs(file) = KZALLOC(sizeof(struct unionfs_file_info), GFP_KERNEL); + if (!ftopd(file)) { + err = -ENOMEM; + goto out; + } + fbstart(file) = -1; + fbend(file) = -1; + atomic_set(&ftopd(file)->ufi_generation, + atomic_read(&itopd(inode)->uii_generation)); + + size = sizeof(struct file *) * sbmax(inode->i_sb); + ftohf_ptr(file) = KZALLOC(size, GFP_KERNEL); + if (!ftohf_ptr(file)) { + err = -ENOMEM; + goto out; + } + + hidden_flags = file->f_flags; + + dentry = file->f_dentry; + fist_dprint(8, "dentry to open is %p\n", dentry); + lock_dentry(dentry); + unionfs_read_lock(inode->i_sb); + locked = 1; + + bstart = fbstart(file) = dbstart(dentry); + bend = fbend(file) = dbend(dentry); + + /* increment to show the kind of open, so that we can + * flush appropriately + */ + atomic_inc(&itopd(dentry->d_inode)->uii_totalopens); + + /* open all directories and make the unionfs file struct point to these hidden file structs */ + if (S_ISDIR(inode->i_mode)) { + for (bindex = bstart; bindex <= bend; bindex++) { + hidden_dentry = dtohd_index(dentry, bindex); + if (!hidden_dentry) + continue; + + DGET(hidden_dentry); + mntget(stohiddenmnt_index(inode->i_sb, bindex)); + hidden_file = + DENTRY_OPEN(hidden_dentry, + stohiddenmnt_index(inode->i_sb, bindex), + hidden_flags); + if (IS_ERR(hidden_file)) { + err = PTR_ERR(hidden_file); + goto out; + } + + set_ftohf_index(file, bindex, hidden_file); + /* The branchget goes after the open, because otherwise + * we would miss the reference on release. */ + branchget(inode->i_sb, bindex); + } + } else { + /* open a file */ + hidden_dentry = dtohd(dentry); + + /* check for the permission for hidden file. If the error is COPYUP_ERR, + * copyup the file. + */ + if (hidden_dentry->d_inode && is_robranch(dentry)) { + /* if the open will change the file, copy it up otherwise defer it. */ + if (hidden_flags & O_TRUNC) { + int size = 0; + + err = -EROFS; + /* copyup the file */ + for (bindex = bstart - 1; bindex >= 0; bindex--) { + err = + copyup_file(dentry-> + d_parent-> + d_inode, file, + bstart, bindex, size); + if (!err) { + break; + } + } + goto out; + } else { + hidden_flags &= ~(OPEN_WRITE_FLAGS); + } + } + + DGET(hidden_dentry); + /* dentry_open will decrement mnt refcnt if err. + * otherwise fput() will do an mntput() for us upon file close. + */ + mntget(stohiddenmnt_index(inode->i_sb, bstart)); + hidden_file = DENTRY_OPEN(hidden_dentry, + stohiddenmnt_index(inode->i_sb, + bstart), + hidden_flags); + if (IS_ERR(hidden_file)) { + err = PTR_ERR(hidden_file); + goto out; + } else { + set_ftohf(file, hidden_file); + branchget(inode->i_sb, bstart); + } + } + + out: + /* freeing the allocated resources, and fput the opened files */ + if (err < 0 && ftopd(file)) { + if (!locked) + unionfs_read_lock(file->f_dentry->d_sb); + for (bindex = bstart; bindex <= bend; bindex++) { + hidden_file = ftohf_index(file, bindex); + if (hidden_file) { + branchput(file->f_dentry->d_sb, bindex); + /* fput calls dput for hidden_dentry */ + fput(hidden_file); + } + } + if (!locked) + unionfs_read_unlock(file->f_dentry->d_sb); + KFREE(ftohf_ptr(file)); + KFREE(ftopd(file)); + } + + fist_print_file("OUT: unionfs_open", file); + + if (locked) { + unlock_dentry(dentry); + unionfs_read_unlock(inode->i_sb); + } + print_exit_status(err); + return err; +} + +int unionfs_file_release(struct inode *inode, struct file *file) +{ + int err = 0; + struct file *hidden_file = NULL; + int bindex, bstart, bend; + int fgen; + + print_entry_location(); + + fist_checkinode(inode, "unionfs_release"); + + /* fput all the hidden files */ + fgen = atomic_read(&ftopd(file)->ufi_generation); + bstart = fbstart(file); + bend = fbend(file); + + for (bindex = bstart; bindex <= bend; bindex++) { + hidden_file = ftohf_index(file, bindex); + + if (hidden_file) { + fput(hidden_file); + unionfs_read_lock(inode->i_sb); + branchput_gen(fgen, inode->i_sb, bindex); + unionfs_read_unlock(inode->i_sb); + } + } + KFREE(ftohf_ptr(file)); + + if (ftopd(file)->rdstate) { + ftopd(file)->rdstate->uds_access = jiffies; + fist_dprint(1, "Saving rdstate with cookie %u [%d.%lld]\n", + ftopd(file)->rdstate->uds_cookie, + ftopd(file)->rdstate->uds_bindex, + (long long)ftopd(file)->rdstate->uds_dirpos); + spin_lock(&itopd(inode)->uii_rdlock); + itopd(inode)->uii_rdcount++; + list_add_tail(&ftopd(file)->rdstate->uds_cache, + &itopd(inode)->uii_readdircache); + mark_inode_dirty(inode); + spin_unlock(&itopd(inode)->uii_rdlock); + ftopd(file)->rdstate = NULL; + } + KFREE(ftopd(file)); + + fist_checkinode(inode, "post unionfs_release"); + + print_exit_status(err); + return err; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11) +long unionfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +#else +int unionfs_ioctl(struct inode *unused, struct file *file, + unsigned int cmd, unsigned long arg) +#endif +{ + long err = 0; /* don't fail by default */ + struct file *hidden_file = NULL; + int val; + + print_entry_location(); + + if ((err = unionfs_file_revalidate(file, 1))) + goto out; + + /* check if asked for local commands */ + switch (cmd) { + case FIST_IOCTL_GET_DEBUG_VALUE: + if (!capable(CAP_SYS_ADMIN)) { + err = -EACCES; + goto out; + } + val = fist_get_debug_value(); + err = put_user(val, (int __user *)arg); + break; + + case FIST_IOCTL_SET_DEBUG_VALUE: + if (!capable(CAP_SYS_ADMIN)) { + err = -EACCES; + goto out; + } + err = get_user(val, (int __user *)arg); + if (err) + break; + fist_dprint(6, "IOCTL SET: got arg %d\n", val); + if (val < 0 || val > 20) { + err = -EINVAL; + break; + } + fist_set_debug_value(val); + break; + + /* add non-debugging fist ioctl's here */ + + case UNIONFS_IOCTL_BRANCH_COUNT: + if (!capable(CAP_SYS_ADMIN)) { + err = -EACCES; + goto out; + } + err = unionfs_ioctl_branchcount(file, cmd, arg); + break; + + case UNIONFS_IOCTL_INCGEN: + if (!capable(CAP_SYS_ADMIN)) { + err = -EACCES; + goto out; + } + err = unionfs_ioctl_incgen(file, cmd, arg); + break; + + case UNIONFS_IOCTL_ADDBRANCH: + if (!capable(CAP_SYS_ADMIN)) { + err = -EACCES; + goto out; + } + err = + unionfs_ioctl_addbranch(file->f_dentry->d_inode, cmd, arg); + break; + + case UNIONFS_IOCTL_RDWRBRANCH: + if (!capable(CAP_SYS_ADMIN)) { + err = -EACCES; + goto out; + } + err = + unionfs_ioctl_rdwrbranch(file->f_dentry->d_inode, cmd, arg); + break; + + case UNIONFS_IOCTL_QUERYFILE: + /* XXX: This should take the file. */ + err = unionfs_ioctl_queryfile(file, cmd, arg); + break; + + default: + hidden_file = ftohf(file); + + err = security_file_ioctl(hidden_file, cmd, arg); + if (err) + goto out; + err = -ENOTTY; + if (!hidden_file || !hidden_file->f_op) + goto out; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11) + if (hidden_file->f_op->unlocked_ioctl) { + err = + hidden_file->f_op->unlocked_ioctl(hidden_file, cmd, + arg); + } else +#endif + if (hidden_file->f_op->ioctl) { + lock_kernel(); + err = + hidden_file->f_op->ioctl(hidden_file->f_dentry-> + d_inode, hidden_file, cmd, + arg); + unlock_kernel(); + } + } /* end of outer switch statement */ + + out: + print_exit_status((int)err); + return err; +} + +int unionfs_flush(struct file *file) +{ + int err = 0; /* assume ok (see open.c:close_fp) */ + struct file *hidden_file = NULL; + int bindex, bstart, bend; + + print_entry_location(); + + if ((err = unionfs_file_revalidate(file, 1))) + goto out; + if (!atomic_dec_and_test + (&itopd(file->f_dentry->d_inode)->uii_totalopens)) + goto out; + + lock_dentry(file->f_dentry); + + bstart = fbstart(file); + bend = fbend(file); + for (bindex = bstart; bindex <= bend; bindex++) { + hidden_file = ftohf_index(file, bindex); + + if (hidden_file && hidden_file->f_op + && hidden_file->f_op->flush) { + err = hidden_file->f_op->flush(hidden_file); + if (err) + goto out_lock; + /* This was earlier done in the unlink_all function in unlink.c */ + /* if there are no more references to the dentry, dput it */ + if (d_deleted(file->f_dentry)) { + DPUT(dtohd_index(file->f_dentry, bindex)); + set_dtohd_index(file->f_dentry, bindex, NULL); + } + } + + } + + out_lock: + unlock_dentry(file->f_dentry); + out: + print_exit_status(err); + return err; +} + +/* + * + * vim:shiftwidth=8 + * vim:tabstop=8 + * + * For Emacs: + * Local variables: + * c-basic-offset: 8 + * c-comment-only-line-offset: 0 + * c-offsets-alist: ((statement-block-intro . +) (knr-argdecl-intro . 0) + * (substatement-open . 0) (label . 0) (statement-cont . +)) + * indent-tabs-mode: t + * tab-width: 8 + * End: + */ diff -urN linux-2.6.17-rc1-mm3-orig/fs/unionfs/copyup.c linux-2.6.17-rc1-mm3-new/fs/unionfs/copyup.c --- linux-2.6.17-rc1-mm3-orig/fs/unionfs/copyup.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/unionfs/copyup.c 2006-04-11 20:24:21.000000000 +0000 @@ -0,0 +1,749 @@ +/* + * Copyright (c) 2003-2006 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2006 Josef Sipek + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2005-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2006 Stony Brook University + * Copyright (c) 2003-2006 The Research Foundation of State University of New York* + * + * For specific licensing information, see the COPYING file distributed with + * this package. + * + * This Copyright notice must be kept intact and distributed with all sources. + */ +/* + * $Id: copyup.c,v 1.65 2006/02/08 21:33:40 dquigley Exp $ + */ + +#include "unionfs.h" + +/*Not Working Yet*/ +static int copyup_xattrs(struct dentry *old_hidden_dentry, + struct dentry *new_hidden_dentry) +{ + int err = 0; + ssize_t list_size = -1; + char *name_list = NULL; + char *attr_value = NULL; + char *name_list_orig = NULL; + + print_entry_location(); + + if (!old_hidden_dentry->d_inode->i_op->getxattr || + !old_hidden_dentry->d_inode->i_op->listxattr || + !new_hidden_dentry->d_inode->i_op->setxattr) { + err = -ENOTSUPP; + goto out; + } + + list_size = + old_hidden_dentry->d_inode->i_op->listxattr(old_hidden_dentry, NULL, + 0); + if (list_size <= 0) { + err = list_size; + goto out; + } + + name_list = xattr_alloc(list_size + 1, XATTR_LIST_MAX); + if (!name_list || IS_ERR(name_list)) { + err = PTR_ERR(name_list); + goto out; + } + list_size = + old_hidden_dentry->d_inode->i_op->listxattr(old_hidden_dentry, + name_list, list_size); + attr_value = xattr_alloc(XATTR_SIZE_MAX, XATTR_SIZE_MAX); + if (!attr_value || IS_ERR(attr_value)) { + err = PTR_ERR(name_list); + goto out; + } + name_list_orig = name_list; + while (*name_list) { + ssize_t size; + down(&old_hidden_dentry->d_inode->i_sem); + err = security_inode_getxattr(old_hidden_dentry, name_list); + if (err) + size = err; + else + size = + old_hidden_dentry->d_inode->i_op-> + getxattr(old_hidden_dentry, name_list, attr_value, + XATTR_SIZE_MAX); + up(&old_hidden_dentry->d_inode->i_sem); + if (size < 0) { + err = size; + goto out; + } + + if (size > XATTR_SIZE_MAX) { + err = -E2BIG; + goto out; + } + + down(&new_hidden_dentry->d_inode->i_sem); + + err = + security_inode_setxattr(old_hidden_dentry, name_list, + attr_value, size, 0); + + if (!err) { + err = + new_hidden_dentry->d_inode->i_op-> + setxattr(new_hidden_dentry, name_list, attr_value, + size, 0); + if (!err) + security_inode_post_setxattr(old_hidden_dentry, + name_list, + attr_value, size, + 0); + } + up(&new_hidden_dentry->d_inode->i_sem); + + if (err < 0) + goto out; + name_list += strlen(name_list) + 1; + } + out: + name_list = name_list_orig; + + if (name_list) + xattr_free(name_list, list_size + 1); + if (attr_value) + xattr_free(attr_value, XATTR_SIZE_MAX); + /* It is no big deal if this fails, we just roll with the punches. */ + if (err == -ENOTSUPP) + err = 0; + return err; +} + +/* Determine the mode based on the copyup flags, and the existing dentry. */ +static int copyup_permissions(struct super_block *sb, + struct dentry *old_hidden_dentry, + struct dentry *new_hidden_dentry) +{ + struct iattr newattrs; + int err; + + print_entry_location(); + + newattrs.ia_atime = old_hidden_dentry->d_inode->i_atime; + newattrs.ia_mtime = old_hidden_dentry->d_inode->i_mtime; + newattrs.ia_ctime = old_hidden_dentry->d_inode->i_ctime; + newattrs.ia_valid = ATTR_CTIME | ATTR_ATIME | ATTR_MTIME | + ATTR_ATIME_SET | ATTR_MTIME_SET; + if (IS_SET(sb, COPYUP_CURRENT_USER)) { + /* current file permission */ + newattrs.ia_mode = ~current->fs->umask & S_IRWXUGO; + newattrs.ia_valid |= ATTR_FORCE | ATTR_MODE; + } else { + /* original mode of old file */ + newattrs.ia_mode = old_hidden_dentry->d_inode->i_mode; + newattrs.ia_gid = old_hidden_dentry->d_inode->i_gid; + newattrs.ia_uid = old_hidden_dentry->d_inode->i_uid; + newattrs.ia_valid |= + ATTR_FORCE | ATTR_GID | ATTR_UID | ATTR_MODE; + } + if (newattrs.ia_valid & ATTR_MODE) { + newattrs.ia_mode = + (newattrs.ia_mode & S_IALLUGO) | (old_hidden_dentry-> + d_inode-> + i_mode & ~S_IALLUGO); + } + + err = notify_change(new_hidden_dentry, &newattrs); + + print_exit_status(err); + return err; +} + +int copyup_dentry(struct inode *dir, struct dentry *dentry, + int bstart, int new_bindex, + struct file **copyup_file, loff_t len) +{ + return copyup_named_dentry(dir, dentry, bstart, new_bindex, + dentry->d_name.name, + dentry->d_name.len, copyup_file, len); +} + +int copyup_named_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_hidden_dentry; + struct dentry *old_hidden_dentry = NULL; + struct super_block *sb; + struct file *input_file = NULL; + struct file *output_file = NULL; + ssize_t read_bytes, write_bytes; + mm_segment_t old_fs; + int err = 0; + char *buf; + int old_bindex; + int got_branch_input = -1; + int got_branch_output = -1; + int old_bstart; + int old_bend; + int size = len; + struct dentry *new_hidden_parent_dentry = NULL; + mm_segment_t oldfs; + char *symbuf = NULL; + uid_t saved_uid = current->fsuid; + gid_t saved_gid = current->fsgid; + + print_entry_location(); + verify_locked(dentry); + fist_print_dentry("IN: copyup_named_dentry", 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; + + unionfs_read_lock(sb); + + if ((err = is_robranch_super(sb, new_bindex))) + goto out; + + /* Create the directory structure above this dentry. */ + new_hidden_dentry = create_parents_named(dir, dentry, name, new_bindex); + if (IS_ERR(new_hidden_dentry)) { + err = PTR_ERR(new_hidden_dentry); + goto out; + } + + fist_print_generic_dentry("Copyup Object", new_hidden_dentry); + + /* Now we actually create the object. */ + old_hidden_dentry = dtohd_index(dentry, old_bindex); + DGET(old_hidden_dentry); + + /* For symlinks, we must read the link before we lock the directory. */ + if (S_ISLNK(old_hidden_dentry->d_inode->i_mode)) { + + symbuf = KMALLOC(PATH_MAX, GFP_KERNEL); + if (!symbuf) { + err = -ENOMEM; + goto copyup_readlink_err; + } + + oldfs = get_fs(); + set_fs(KERNEL_DS); + err = + old_hidden_dentry->d_inode->i_op-> + readlink(old_hidden_dentry, (char __user *)symbuf, + PATH_MAX); + set_fs(oldfs); + if (err < 0) + goto copyup_readlink_err; + symbuf[err] = '\0'; + } + + /* Now we lock the parent, and create the object in the new branch. */ + new_hidden_parent_dentry = lock_parent(new_hidden_dentry); + current->fsuid = new_hidden_parent_dentry->d_inode->i_uid; + current->fsgid = new_hidden_parent_dentry->d_inode->i_gid; + if (S_ISDIR(old_hidden_dentry->d_inode->i_mode)) { + err = vfs_mkdir(new_hidden_parent_dentry->d_inode, + new_hidden_dentry, S_IRWXU); + } else if (S_ISLNK(old_hidden_dentry->d_inode->i_mode)) { + err = vfs_symlink(new_hidden_parent_dentry->d_inode, + new_hidden_dentry, symbuf, S_IRWXU); + } else if (S_ISBLK(old_hidden_dentry->d_inode->i_mode) + || S_ISCHR(old_hidden_dentry->d_inode->i_mode) + || S_ISFIFO(old_hidden_dentry->d_inode->i_mode) + || S_ISSOCK(old_hidden_dentry->d_inode->i_mode)) { + err = vfs_mknod(new_hidden_parent_dentry->d_inode, + new_hidden_dentry, + old_hidden_dentry->d_inode->i_mode, + old_hidden_dentry->d_inode->i_rdev); + } else if (S_ISREG(old_hidden_dentry->d_inode->i_mode)) { + err = vfs_create(new_hidden_parent_dentry->d_inode, + new_hidden_dentry, S_IRWXU, NULL); + } else { + char diemsg[100]; + snprintf(diemsg, sizeof(diemsg), "Unknown inode type %d\n", + old_hidden_dentry->d_inode->i_mode); + printk(KERN_ERR "%s\n", diemsg); + BUG(); + } + current->fsuid = saved_uid; + current->fsgid = saved_gid; + copyup_readlink_err: + KFREE(symbuf); + if (err) { + /* get rid of the hidden dentry and all its traces */ + DPUT(new_hidden_dentry); + set_dtohd_index(dentry, new_bindex, NULL); + set_dbstart(dentry, old_bstart); + set_dbend(dentry, old_bend); + goto out_dir; + } +#ifdef UNIONFS_IMAP + if (stopd(sb)->usi_persistent) { + err = write_uin(dentry->d_sb, dentry->d_inode->i_ino, + new_bindex, new_hidden_dentry->d_inode->i_ino); + if (err) + goto out_dir; + } +#endif + /* We actually copyup the file here. */ + if (S_ISREG(old_hidden_dentry->d_inode->i_mode)) { + mntget(stohiddenmnt_index(sb, old_bindex)); + branchget(sb, old_bindex); + got_branch_input = old_bindex; + input_file = + DENTRY_OPEN(old_hidden_dentry, + stohiddenmnt_index(sb, old_bindex), O_RDONLY); + if (IS_ERR(input_file)) { + err = PTR_ERR(input_file); + goto out_dir; + } + if (!input_file->f_op || !input_file->f_op->read) { + err = -EINVAL; + goto out_dir; + } + + /* copy the new file */ + DGET(new_hidden_dentry); + mntget(stohiddenmnt_index(sb, new_bindex)); + branchget(sb, new_bindex); + got_branch_output = new_bindex; + output_file = + DENTRY_OPEN(new_hidden_dentry, + stohiddenmnt_index(sb, new_bindex), O_WRONLY); + if (IS_ERR(output_file)) { + err = PTR_ERR(output_file); + goto out_dir; + } + if (!output_file->f_op || !output_file->f_op->write) { + err = -EINVAL; + goto out_dir; + } + + /* allocating a buffer */ + buf = (char *)KMALLOC(PAGE_SIZE, GFP_KERNEL); + if (!buf) { + err = -ENOMEM; + goto out_dir; + } + + /* now read PAGE_SIZE bytes from offset 0 in a loop */ + old_fs = get_fs(); + + input_file->f_pos = 0; + output_file->f_pos = 0; + + err = 0; // reset error just in case + set_fs(KERNEL_DS); + 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; + } + + write_bytes = + output_file->f_op->write(output_file, + (char __user *)buf, + read_bytes, + &output_file->f_pos); + 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) { + /* copyup failed, because we 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_hidden_parent_dentry->d_inode, + new_hidden_dentry); + + goto out_dir; + } + } + + /* Set permissions. */ + if ((err = + copyup_permissions(sb, old_hidden_dentry, new_hidden_dentry))) + goto out_dir; + /* Selinux uses extended attributes for permissions. */ + if ((err = copyup_xattrs(old_hidden_dentry, new_hidden_dentry))) + goto out_dir; + + /* do not allow files getting deleted to be reinterposed */ + if (!d_deleted(dentry)) + unionfs_reinterpose(dentry); + + out_dir: + if (new_hidden_parent_dentry) + unlock_dir(new_hidden_parent_dentry); + + out: + if (input_file && !IS_ERR(input_file)) { + fput(input_file); + } else { + /* since input file was not opened, we need to explicitly + * dput the old_hidden_dentry + */ + DPUT(old_hidden_dentry); + } + + /* in any case, we have to branchput */ + if (got_branch_input >= 0) + branchput(sb, got_branch_input); + + if (output_file) { + if (copyup_file && !err) { + *copyup_file = output_file; + } else { + /* close the file if there was no error, or if we ran + * out of space in which case we unlinked the file */ + if (!IS_ERR(output_file)) + fput(output_file); + branchput(sb, got_branch_output); + } + } + + unionfs_read_unlock(sb); + + fist_print_dentry("OUT: copyup_dentry", dentry); + fist_print_inode("OUT: copyup_dentry", dentry->d_inode); + + print_exit_status(err); + 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; + + print_entry_location(); + + err = copyup_named_dentry(dir, file->f_dentry, bstart, + new_bindex, name, strlen(name), &output_file, + len); + if (!err) { + fbstart(file) = new_bindex; + set_ftohf_index(file, new_bindex, output_file); + } + + print_exit_status(err); + 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; + + print_entry_location(); + + err = copyup_dentry(dir, file->f_dentry, bstart, new_bindex, + &output_file, len); + if (!err) { + fbstart(file) = new_bindex; + set_ftohf_index(file, new_bindex, output_file); + } + + print_exit_status(err); + return err; +} + +/* This function replicates the directory structure upto given dentry + * in the bindex branch. Can create directory structure recursively to the right + * also. + */ +struct dentry *create_parents(struct inode *dir, struct dentry *dentry, + int bindex) +{ + struct dentry *hidden_dentry; + + print_entry_location(); + hidden_dentry = + create_parents_named(dir, dentry, dentry->d_name.name, bindex); + print_exit_location(); + + return (hidden_dentry); +} + +/* This function replicates the directory structure upto given dentry + * in the bindex branch. */ +struct dentry *create_parents_named(struct inode *dir, struct dentry *dentry, + const char *name, int bindex) +{ + int err; + struct dentry *child_dentry; + struct dentry *parent_dentry; + struct dentry *hidden_parent_dentry = NULL; + struct dentry *hidden_dentry = NULL; + const char *childname; + unsigned int childnamelen; + + int old_kmalloc_size; + int kmalloc_size; + int num_dentry; + int count; + + int old_bstart; + int old_bend; + struct dentry **path = NULL; + struct dentry **tmp_path; + struct super_block *sb; +#ifdef UNIONFS_IMAP + int persistent; +#endif + print_entry_location(); + + verify_locked(dentry); + + /* There is no sense allocating any less than the minimum. */ + kmalloc_size = malloc_sizes[0].cs_size; + num_dentry = kmalloc_size / sizeof(struct dentry *); + + if ((err = is_robranch_super(dir->i_sb, bindex))) { + hidden_dentry = ERR_PTR(err); + goto out; + } + + fist_print_dentry("IN: create_parents_named", dentry); + fist_dprint(8, "name = %s\n", name); + + old_bstart = dbstart(dentry); + old_bend = dbend(dentry); + + hidden_dentry = ERR_PTR(-ENOMEM); + path = (struct dentry **)KZALLOC(kmalloc_size, GFP_KERNEL); + if (!path) + goto out; + + /* assume the negative dentry of unionfs as the parent dentry */ + parent_dentry = dentry; + + count = 0; + /* 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 = child_dentry->d_parent; + lock_dentry(parent_dentry); + + /* find out the hidden_parent_dentry in the given branch */ + hidden_parent_dentry = dtohd_index(parent_dentry, bindex); + + /* store the child dentry */ + path[count++] = child_dentry; + if (count == num_dentry) { + old_kmalloc_size = kmalloc_size; + kmalloc_size *= 2; + num_dentry = kmalloc_size / sizeof(struct dentry *); + + tmp_path = + (struct dentry **)KZALLOC(kmalloc_size, GFP_KERNEL); + if (!tmp_path) { + hidden_dentry = ERR_PTR(-ENOMEM); + goto out; + } + memcpy(tmp_path, path, old_kmalloc_size); + KFREE(path); + path = tmp_path; + tmp_path = NULL; + } + + } while (!hidden_parent_dentry); + count--; + + sb = dentry->d_sb; +#ifdef UNIONFS_IMAP + persistent = stopd(sb)->usi_persistent; +#endif + /* This is basically while(child_dentry != dentry). This loop is + * horrible to follow and should be replaced with cleaner code. */ + while (1) { + // get hidden parent dir in the current branch + hidden_parent_dentry = dtohd_index(parent_dentry, bindex); + unlock_dentry(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 + hidden_dentry = + LOOKUP_ONE_LEN(childname, hidden_parent_dentry, + childnamelen); + if (IS_ERR(hidden_dentry)) + goto out; + } else { + int loop_start; + int loop_end; + int new_bstart = -1; + int new_bend = -1; + int i; + + /* is the name a whiteout of the childname ? */ + //lookup the whiteout child in the underlying file system + hidden_dentry = + LOOKUP_ONE_LEN(name, hidden_parent_dentry, + strlen(name)); + if (IS_ERR(hidden_dentry)) + goto out; + + /* Replace the current dentry (if any) with the new one. */ + DPUT(dtohd_index(dentry, bindex)); + set_dtohd_index(dentry, bindex, hidden_dentry); + + loop_start = + (old_bstart < bindex) ? old_bstart : bindex; + loop_end = (old_bend > bindex) ? 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 (the newly looked dentry + */ + for (i = loop_start; i <= loop_end; i++) { + if (!dtohd_index(dentry, i)) + continue; + + if (i == bindex) { + new_bend = i; + if (new_bstart < 0) + new_bstart = i; + continue; + } + + if (!dtohd_index(dentry, i)->d_inode) { + DPUT(dtohd_index(dentry, i)); + set_dtohd_index(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; + set_dbstart(dentry, new_bstart); + set_dbend(dentry, new_bend); + break; + } + + if (hidden_dentry->d_inode) { + /* since this already exists we dput to avoid + * multiple references on the same dentry */ + DPUT(hidden_dentry); + } else { + uid_t saved_uid = current->fsuid; + gid_t saved_gid = current->fsgid; + + /* its a negative dentry, create a new dir */ + hidden_parent_dentry = lock_parent(hidden_dentry); + current->fsuid = hidden_parent_dentry->d_inode->i_uid; + current->fsgid = hidden_parent_dentry->d_inode->i_gid; + err = vfs_mkdir(hidden_parent_dentry->d_inode, + hidden_dentry, S_IRWXUGO); + current->fsuid = saved_uid; + current->fsgid = saved_gid; + if (!err) + err = copyup_permissions + (dir->i_sb, child_dentry, hidden_dentry); + unlock_dir(hidden_parent_dentry); + if (err) { + DPUT(hidden_dentry); + hidden_dentry = ERR_PTR(err); + goto out; + } +#ifdef UNIONFS_IMAP + if (persistent) { + err = write_uin + (sb, child_dentry->d_inode->i_ino, + bindex, hidden_dentry->d_inode->i_ino); + if (err) { + DPUT(hidden_dentry); + hidden_dentry = ERR_PTR(err); + goto out; + } + } +#endif + set_itohi_index(child_dentry->d_inode, bindex, + IGRAB(hidden_dentry->d_inode)); + if (ibstart(child_dentry->d_inode) > bindex) + ibstart(child_dentry->d_inode) = bindex; + if (ibend(child_dentry->d_inode) < bindex) + ibend(child_dentry->d_inode) = bindex; + + set_dtohd_index(child_dentry, bindex, hidden_dentry); + if (dbstart(child_dentry) > bindex) + set_dbstart(child_dentry, bindex); + if (dbend(child_dentry) < bindex) + set_dbend(child_dentry, bindex); + } + + parent_dentry = child_dentry; + child_dentry = path[--count]; + } + out: + KFREE(path); + fist_print_dentry("OUT: create_parents_named", dentry); + print_exit_pointer(hidden_dentry); + return hidden_dentry; +} + +/* + * + * vim:shiftwidth=8 + * vim:tabstop=8 + * + * For Emacs: + * Local variables: + * c-basic-offset: 8 + * c-comment-only-line-offset: 0 + * c-offsets-alist: ((statement-block-intro . +) (knr-argdecl-intro . 0) + * (substatement-open . 0) (label . 0) (statement-cont . +)) + * indent-tabs-mode: t + * tab-width: 8 + * End: + */ diff -urN linux-2.6.17-rc1-mm3-orig/fs/unionfs/dentry.c linux-2.6.17-rc1-mm3-new/fs/unionfs/dentry.c --- linux-2.6.17-rc1-mm3-orig/fs/unionfs/dentry.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/unionfs/dentry.c 2006-04-11 20:24:21.000000000 +0000 @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2003-2006 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2006 Josef Sipek + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2005-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2006 Stony Brook University + * Copyright (c) 2003-2006 The Research Foundation of State University of New York + * + * For specific licensing information, see the COPYING file distributed with + * this package. + * + * This Copyright notice must be kept intact and distributed with all sources. + */ +/* + * $Id: dentry.c,v 1.74 2006/02/13 00:18:40 jsipek Exp $ + */ + +#include "unionfs.h" + +/* declarations added for "sparse" */ +extern int unionfs_d_revalidate_wrap(struct dentry *dentry, + struct nameidata *nd); +extern void unionfs_d_release(struct dentry *dentry); +extern void unionfs_d_iput(struct dentry *dentry, struct inode *inode); + +/* + * THIS IS A BOOLEAN FUNCTION: returns 1 if valid, 0 otherwise. + */ +int unionfs_d_revalidate(struct dentry *dentry, struct nameidata *nd) +{ + int valid = 1; /* default is valid (1); invalid is 0. */ + struct dentry *hidden_dentry; + int bindex, bstart, bend; + int sbgen, dgen; + int positive = 0; + int locked = 0; + int restart = 0; + int interpose_flag; + + print_util_entry_location(); + + restart: + verify_locked(dentry); + + /* if the dentry is unhashed, do NOT revalidate */ + if (d_deleted(dentry)) { + fist_dprint(6, "unhashed dentry being revalidated: %*s\n", + dentry->d_name.len, dentry->d_name.name); + goto out; + } + + BUG_ON(dbstart(dentry) == -1); + if (dentry->d_inode) + positive = 1; + dgen = atomic_read(&dtopd(dentry)->udi_generation); + sbgen = atomic_read(&stopd(dentry->d_sb)->usi_generation); + /* If we are working on an unconnected dentry, then there is no + * revalidation to be done, because this file does not exist within the + * namespace, and Unionfs operates on the namespace, not data. + */ + if (sbgen != dgen) { + struct dentry *result; + int pdgen; + + unionfs_read_lock(dentry->d_sb); + locked = 1; + + /* 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(&dtopd(dentry->d_parent)->udi_generation); + if (!restart && (pdgen != sbgen)) { + unionfs_read_unlock(dentry->d_sb); + locked = 0; + /* We must be locked before our parent. */ + if (! + (dentry->d_parent->d_op-> + d_revalidate(dentry->d_parent, nd))) { + valid = 0; + goto out; + } + restart = 1; + goto restart; + } + BUG_ON(pdgen != sbgen); + + /* Free the pointers for our inodes and this dentry. */ + bstart = dbstart(dentry); + bend = dbend(dentry); + if (bstart >= 0) { + struct dentry *hidden_dentry; + for (bindex = bstart; bindex <= bend; bindex++) { + hidden_dentry = + dtohd_index_nocheck(dentry, bindex); + if (!hidden_dentry) + continue; + DPUT(hidden_dentry); + } + } + set_dbstart(dentry, -1); + set_dbend(dentry, -1); + + interpose_flag = INTERPOSE_REVAL_NEG; + if (positive) { + interpose_flag = INTERPOSE_REVAL; + down(&dentry->d_inode->i_sem); + bstart = ibstart(dentry->d_inode); + bend = ibend(dentry->d_inode); + if (bstart >= 0) { + struct inode *hidden_inode; + for (bindex = bstart; bindex <= bend; bindex++) { + hidden_inode = + itohi_index(dentry->d_inode, + bindex); + if (!hidden_inode) + continue; + IPUT(hidden_inode); + } + } + KFREE(itohi_ptr(dentry->d_inode)); + itohi_ptr(dentry->d_inode) = NULL; + ibstart(dentry->d_inode) = -1; + ibend(dentry->d_inode) = -1; + up(&dentry->d_inode->i_sem); + } + + result = unionfs_lookup_backend(dentry, interpose_flag); + if (result) { + if (IS_ERR(result)) { + valid = 0; + goto out; + } + /* current unionfs_lookup_backend() doesn't return + a valid dentry */ + DPUT(dentry); + dentry = result; + } + + if (positive && itopd(dentry->d_inode)->uii_stale) { + make_stale_inode(dentry->d_inode); + d_drop(dentry); + valid = 0; + goto out; + } + goto out; + } + + /* The revalidation must occur across all branches */ + bstart = dbstart(dentry); + bend = dbend(dentry); + BUG_ON(bstart == -1); + for (bindex = bstart; bindex <= bend; bindex++) { + hidden_dentry = dtohd_index(dentry, bindex); + if (!hidden_dentry || !hidden_dentry->d_op + || !hidden_dentry->d_op->d_revalidate) + continue; + + if (!hidden_dentry->d_op->d_revalidate(hidden_dentry, nd)) + valid = 0; + } + + if (!dentry->d_inode) + valid = 0; + if (valid) + fist_copy_attr_all(dentry->d_inode, itohi(dentry->d_inode)); + + out: + if (locked) + unionfs_read_unlock(dentry->d_sb); + fist_print_dentry("revalidate out", dentry); + print_util_exit_status(valid); + return valid; +} + +int unionfs_d_revalidate_wrap(struct dentry *dentry, struct nameidata *nd) +{ + int err; + + print_entry_location(); + lock_dentry(dentry); + + err = unionfs_d_revalidate(dentry, nd); + + unlock_dentry(dentry); + print_exit_status(err); + return err; +} + +void unionfs_d_release(struct dentry *dentry) +{ + struct dentry *hidden_dentry; + int bindex, bstart, bend; + + print_entry_location(); + /* There is no reason to lock the dentry, because we have the only + * reference, but the printing functions verify that we have a lock + * on the dentry before calling dbstart, etc. */ + lock_dentry(dentry); + __fist_print_dentry("unionfs_d_release IN dentry", dentry, 0); + + /* this could be a negative dentry, so check first */ + if (!dtopd(dentry)) { + fist_dprint(6, "dentry without private data: %*s", + dentry->d_name.len, dentry->d_name.name); + goto out; + } else if (dbstart(dentry) < 0) { + /* this is due to a failed lookup */ + /* the failed lookup has a dtohd_ptr set to null, + but this is a better check */ + fist_dprint(6, "dentry without hidden dentries : %*s", + dentry->d_name.len, dentry->d_name.name); + goto out_free; + } + + /* Release all the hidden dentries */ + bstart = dbstart(dentry); + bend = dbend(dentry); + for (bindex = bstart; bindex <= bend; bindex++) { + hidden_dentry = dtohd_index(dentry, bindex); + DPUT(hidden_dentry); + set_dtohd_index(dentry, bindex, NULL); + } + /* free private data (unionfs_dentry_info) here */ + KFREE(dtohd_ptr(dentry)); + dtohd_ptr(dentry) = NULL; + out_free: + /* No need to unlock it, because it is disappeared. */ +#ifdef TRACKLOCK + printk("DESTROYLOCK:%p\n", dentry); +#endif + free_dentry_private_data(dtopd(dentry)); + dtopd_lhs(dentry) = NULL; /* just to be safe */ + out: + print_exit_location(); +} + +/* + * we don't really need unionfs_d_iput, because dentry_iput will call iput() if + * unionfs_d_iput is not defined. We left this implemented for ease of + * tracing/debugging. + */ +void unionfs_d_iput(struct dentry *dentry, struct inode *inode) +{ + print_entry_location(); + IPUT(inode); + print_exit_location(); +} + +struct dentry_operations unionfs_dops = { + .d_revalidate = unionfs_d_revalidate_wrap, + .d_release = unionfs_d_release, + .d_iput = unionfs_d_iput, +}; + +/* + * + * vim:shiftwidth=8 + * vim:tabstop=8 + * + * For Emacs: + * Local variables: + * c-basic-offset: 8 + * c-comment-only-line-offset: 0 + * c-offsets-alist: ((statement-block-intro . +) (knr-argdecl-intro . 0) + * (substatement-open . 0) (label . 0) (statement-cont . +)) + * indent-tabs-mode: t + * tab-width: 8 + * End: + */ diff -urN linux-2.6.17-rc1-mm3-orig/fs/unionfs/dirfops.c linux-2.6.17-rc1-mm3-new/fs/unionfs/dirfops.c --- linux-2.6.17-rc1-mm3-orig/fs/unionfs/dirfops.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/unionfs/dirfops.c 2006-04-11 20:24:21.000000000 +0000 @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2003-2006 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2006 Josef Sipek + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2005-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2006 Stony Brook University + * Copyright (c) 2003-2006 The Research Foundation of State University of New York + * + * For specific licensing information, see the COPYING file distributed with + * this package. + * + * This Copyright notice must be kept intact and distributed with all sources. + */ +/* + * $Id: dirfops.c,v 1.22 2006/02/07 23:21:50 dquigley Exp $ + */ + +#include "unionfs.h" + +/* Make sure our rdstate is playing by the rules. */ +static void verify_rdstate_offset(struct unionfs_dir_state *rdstate) +{ + BUG_ON(rdstate->uds_offset >= DIREOF); + BUG_ON(rdstate->uds_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; +}; + +/* copied from generic filldir in fs/readir.c */ +static int unionfs_filldir(void *dirent, const char *name, int namelen, + loff_t offset, ino_t ino, unsigned int d_type) +{ + struct unionfs_getdents_callback *buf = + (struct unionfs_getdents_callback *)dirent; + struct filldir_node *found = NULL; + int err = 0; + int is_wh_entry = 0; + + fist_dprint(9, "unionfs_filldir name=%*s\n", namelen, name); + + buf->filldir_called++; + + if ((namelen > WHLEN) && !strncmp(name, WHPFX, WHLEN)) { + name += WHLEN; + namelen -= WHLEN; + is_wh_entry = 1; + } + + found = find_filldir_node(buf->rdstate, name, namelen); + + if (found) + goto out; + + /* if 'name' isn't a whiteout filldir it. */ + if (!is_wh_entry) { + off_t pos = rdstate2offset(buf->rdstate); + ino_t unionfs_ino = ino; +#ifdef UNIONFS_IMAP + if (stopd(buf->sb)->usi_persistent) + err = read_uin(buf->sb, buf->rdstate->uds_bindex, + ino, O_CREAT, &unionfs_ino); +#endif + if (!err) { + err = buf->filldir(buf->dirent, name, namelen, pos, + unionfs_ino, d_type); + buf->rdstate->uds_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++; + if ((err = add_filldir_node(buf->rdstate, name, namelen, + buf->rdstate->uds_bindex, is_wh_entry))) + buf->filldir_error = err; + + out: + return err; +} + +static int unionfs_readdir(struct file *file, void *dirent, filldir_t filldir) +{ + int err = 0; + struct file *hidden_file = NULL; + struct inode *inode = NULL; + struct unionfs_getdents_callback buf; + struct unionfs_dir_state *uds; + int bend; + loff_t offset; + + print_entry("file = %p, pos = %llx", file, file->f_pos); + + fist_print_file("In unionfs_readdir()", file); + + if ((err = unionfs_file_revalidate(file, 0))) + goto out; + + inode = file->f_dentry->d_inode; + fist_checkinode(inode, "unionfs_readdir"); + + uds = ftopd(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 (!uds) { + err = -ESTALE; + goto out; + } + ftopd(file)->rdstate = uds; + } else { + init_rdstate(file); + uds = ftopd(file)->rdstate; + } + } + bend = fbend(file); + + while (uds->uds_bindex <= bend) { + hidden_file = ftohf_index(file, uds->uds_bindex); + if (!hidden_file) { + fist_dprint(7, + "Incremented bindex to %d of %d," + " because hidden file is NULL.\n", + uds->uds_bindex, bend); + uds->uds_bindex++; + uds->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(hidden_file, uds->uds_dirpos, 0); + if (offset < 0) { + err = offset; + goto out; + } + fist_dprint(7, "calling readdir for %d.%lld (offset = %lld)\n", + uds->uds_bindex, uds->uds_dirpos, offset); + err = vfs_readdir(hidden_file, unionfs_filldir, (void *)&buf); + fist_dprint(7, + "readdir on %d.%lld = %d (entries written %d, filldir called %d)\n", + uds->uds_bindex, (long long)uds->uds_dirpos, err, + buf.entries_written, buf.filldir_called); + /* Save the position for when we continue. */ + + offset = vfs_llseek(hidden_file, 0, 1); + if (offset < 0) { + err = offset; + goto out; + } + uds->uds_dirpos = offset; + + /* Copy the atime. */ + fist_copy_attr_atime(inode, hidden_file->f_dentry->d_inode); + + if (err < 0) { + goto out; + } + + if (buf.filldir_error) { + break; + } + + if (!buf.entries_written) { + uds->uds_bindex++; + uds->uds_dirpos = 0; + } + } + + if (!buf.filldir_error && uds->uds_bindex >= bend) { + fist_dprint(3, + "Discarding rdstate because readdir is over (hashsize = %d)\n", + uds->uds_hashentries); + /* Save the number of hash entries for next time. */ + itopd(inode)->uii_hashsize = uds->uds_hashentries; + free_rdstate(uds); + ftopd(file)->rdstate = NULL; + file->f_pos = DIREOF; + } else { + file->f_pos = rdstate2offset(uds); + fist_dprint(3, "rdstate now has a cookie of %u (err = %d)\n", + uds->uds_cookie, err); + } + + out: + fist_checkinode(inode, "post unionfs_readdir"); + print_exit_status(err); + 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 end of the file, if you've read everything + * This really has no effect, but returns where you are. + * (3) 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; + loff_t err; + + print_entry(" file=%p, offset=0x%llx, origin = %d", file, offset, + origin); + + if ((err = unionfs_file_revalidate(file, 0))) + goto out; + + rdstate = ftopd(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); + ftopd(file)->rdstate = NULL; + } + init_rdstate(file); + err = 0; + break; + case SEEK_CUR: + if (file->f_pos) { + if (file->f_pos == DIREOF) + err = DIREOF; + else + BUG_ON(file->f_pos != + rdstate2offset(rdstate)); + err = file->f_pos; + } else { + err = 0; + } + 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 { + if ((rdstate = + find_rdstate(file->f_dentry->d_inode, + offset))) { + ftopd(file)->rdstate = rdstate; + err = rdstate->uds_offset; + } else { + err = -EINVAL; + } + } + break; + case SEEK_CUR: + case SEEK_END: + /* Unsupported, because we would break everything. */ + err = -EINVAL; + break; + } + } + + out: + print_exit_status((int)err); + 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, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11) + .unlocked_ioctl = unionfs_ioctl, +#endif + .open = unionfs_open, + .release = unionfs_file_release, + .flush = unionfs_flush, +}; + +/* + * + * vim:shiftwidth=8 + * vim:tabstop=8 + * + * For Emacs: + * Local variables: + * c-basic-offset: 8 + * c-comment-only-line-offset: 0 + * c-offsets-alist: ((statement-block-intro . +) (knr-argdecl-intro . 0) + * (substatement-open . 0) (label . 0) (statement-cont . +)) + * indent-tabs-mode: t + * tab-width: 8 + * End: + */ diff -urN linux-2.6.17-rc1-mm3-orig/fs/unionfs/dirhelper.c linux-2.6.17-rc1-mm3-new/fs/unionfs/dirhelper.c --- linux-2.6.17-rc1-mm3-orig/fs/unionfs/dirhelper.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/unionfs/dirhelper.c 2006-04-11 20:24:21.000000000 +0000 @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2003-2006 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2006 Josef Sipek + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2005-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2006 Stony Brook University + * Copyright (c) 2003-2006 The Research Foundation of State University of New York + * + * For specific licensing information, see the COPYING file distributed with + * this package. + * + * This Copyright notice must be kept intact and distributed with all sources. + */ +/* + * $Id: dirhelper.c,v 1.27 2006/02/09 04:41:00 jsipek Exp $ + */ + +#include "unionfs.h" + +/* Delete all of the whiteouts in a given directory for rmdir. */ +int delete_whiteouts(struct dentry *dentry, int bindex, + struct unionfs_dir_state *namelist) +{ + int err = 0; + struct dentry *hidden_dir_dentry = NULL; + struct dentry *hidden_dentry; + struct super_block *sb; + char *name = NULL; + + int i; + struct list_head *pos; + struct filldir_node *cursor; + + print_entry_location(); + + sb = dentry->d_sb; + + BUG_ON(!S_ISDIR(dentry->d_inode->i_mode)); + BUG_ON(bindex < dbstart(dentry)); + BUG_ON(bindex > dbend(dentry)); + + /* Find out hidden parent dentry */ + hidden_dir_dentry = dtohd_index(dentry, bindex); + BUG_ON(!S_ISDIR(hidden_dir_dentry->d_inode->i_mode)); + + name = (char *)__get_free_page(GFP_KERNEL); + if (!name) { + err = -ENOMEM; + goto out; + } + + for (i = 0; i < namelist->uds_size; i++) { + list_for_each(pos, &namelist->uds_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; + + strcpy(name, WHPFX); + strncpy(name + WHLEN, cursor->name, PAGE_SIZE - WHLEN); + + hidden_dentry = + LOOKUP_ONE_LEN(name, hidden_dir_dentry, + cursor->namelen + WHLEN); + if (IS_ERR(hidden_dentry)) { + err = PTR_ERR(hidden_dentry); + goto out; + } + if (!hidden_dentry->d_inode) { + DPUT(hidden_dentry); + continue; + } + + down(&hidden_dir_dentry->d_inode->i_sem); + err = + vfs_unlink(hidden_dir_dentry->d_inode, + hidden_dentry); + up(&hidden_dir_dentry->d_inode->i_sem); + DPUT(hidden_dentry); + + if (err && !IS_COPYUP_ERR(err)) + goto out; + } + } + + out: + /* After all of the removals, we should copy the attributes once. */ + fist_copy_attr_times(dentry->d_inode, hidden_dir_dentry->d_inode); + dentry->d_inode->i_nlink = get_nlinks(dentry->d_inode); + + if (name) + free_page((unsigned long)name); + print_exit_status(err); + return err; +} + +#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 *name, int namelen, + loff_t offset, ino_t ino, unsigned int d_type) +{ + int err = 0; + struct unionfs_rdutil_callback *buf = + (struct unionfs_rdutil_callback *)dirent; + int whiteout = 0; + struct filldir_node *found; + + print_entry_location(); + + buf->filldir_called = 1; + + if (name[0] == '.' + && (namelen == 1 || (name[1] == '.' && namelen == 2))) + goto out; + + if ((namelen > WHLEN) && !strncmp(name, WHPFX, WHLEN)) { + namelen -= WHLEN; + name += WHLEN; + whiteout = 1; + } + + found = find_filldir_node(buf->rdstate, name, namelen); + /* 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) && !whiteout) + goto out; + + err = add_filldir_node(buf->rdstate, name, namelen, + buf->rdstate->uds_bindex, whiteout); + + out: + buf->err = err; + print_exit_status(err); + return err; +} + +/* Is a directory logically empty? */ +int check_empty(struct dentry *dentry, struct unionfs_dir_state **namelist) +{ + int err = 0; + struct dentry *hidden_dentry = NULL; + struct super_block *sb; + struct file *hidden_file; + struct unionfs_rdutil_callback *buf = NULL; + int bindex, bstart, bend, bopaque; + + print_entry_location(); + + sb = dentry->d_sb; + + unionfs_read_lock(sb); + + BUG_ON(!S_ISDIR(dentry->d_inode->i_mode)); + + if ((err = unionfs_partial_lookup(dentry))) + 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 (!buf) { + err = -ENOMEM; + goto out; + } + buf->err = 0; + buf->mode = RD_CHECK_EMPTY; + buf->rdstate = alloc_rdstate(dentry->d_inode, bstart); + if (!buf->rdstate) { + err = -ENOMEM; + goto out; + } + + /* Process the hidden directories with rdutil_callback as a filldir. */ + for (bindex = bstart; bindex <= bend; bindex++) { + hidden_dentry = dtohd_index(dentry, bindex); + if (!hidden_dentry) + continue; + if (!hidden_dentry->d_inode) + continue; + if (!S_ISDIR(hidden_dentry->d_inode->i_mode)) + continue; + + DGET(hidden_dentry); + mntget(stohiddenmnt_index(sb, bindex)); + branchget(sb, bindex); + hidden_file = + DENTRY_OPEN(hidden_dentry, stohiddenmnt_index(sb, bindex), + O_RDONLY); + if (IS_ERR(hidden_file)) { + err = PTR_ERR(hidden_file); + DPUT(hidden_dentry); + branchput(sb, bindex); + goto out; + } + + do { + buf->filldir_called = 0; + buf->rdstate->uds_bindex = bindex; + err = vfs_readdir(hidden_file, + readdir_util_callback, buf); + if (buf->err) + err = buf->err; + } while ((err >= 0) && buf->filldir_called); + + /* fput calls dput for hidden_dentry */ + fput(hidden_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); + } + + unionfs_read_unlock(sb); + + print_exit_status(err); + return err; +} + +/* + * + * vim:shiftwidth=8 + * vim:tabstop=8 + * + * For Emacs: + * Local variables: + * c-basic-offset: 8 + * c-comment-only-line-offset: 0 + * c-offsets-alist: ((statement-block-intro . +) (knr-argdecl-intro . 0) + * (substatement-open . 0) (label . 0) (statement-cont . +)) + * indent-tabs-mode: t + * tab-width: 8 + * End: + */ diff -urN linux-2.6.17-rc1-mm3-orig/fs/unionfs/file.c linux-2.6.17-rc1-mm3-new/fs/unionfs/file.c --- linux-2.6.17-rc1-mm3-orig/fs/unionfs/file.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/unionfs/file.c 2006-04-11 20:24:21.000000000 +0000 @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2003-2006 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2006 Josef Sipek + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2005-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2006 Stony Brook University + * Copyright (c) 2003-2006 The Research Foundation of State University of New York + * + * For specific licensing information, see the COPYING file distributed with + * this package. + * + * This Copyright notice must be kept intact and distributed with all sources. + */ +/* + * $Id: file.c,v 1.136 2006/02/13 18:07:33 cwright Exp $ + */ + +#include "unionfs.h" + +/* declarations for sparse */ +extern ssize_t unionfs_read(struct file *, char __user *, size_t, loff_t *); +extern ssize_t unionfs_write(struct file *, const char __user *, size_t, + loff_t *); + +/******************* + * File Operations * + *******************/ + +static loff_t unionfs_llseek(struct file *file, loff_t offset, int origin) +{ + loff_t err; + struct file *hidden_file = NULL; + + print_entry_location(); + + fist_dprint(6, "unionfs_llseek: file=%p, offset=0x%llx, origin=%d\n", + file, offset, origin); + + if ((err = unionfs_file_revalidate(file, 0))) + goto out; + + hidden_file = ftohf(file); + /* always set hidden position to this one */ + hidden_file->f_pos = file->f_pos; + + memcpy(&(hidden_file->f_ra), &(file->f_ra), + sizeof(struct file_ra_state)); + + if (hidden_file->f_op && hidden_file->f_op->llseek) + err = hidden_file->f_op->llseek(hidden_file, offset, origin); + else + err = generic_file_llseek(hidden_file, offset, origin); + + if (err < 0) + goto out; + if (err != file->f_pos) { + file->f_pos = err; + // ION maybe this? + // file->f_pos = hidden_file->f_pos; + + file->f_version++; + } + out: + print_exit_status((int)err); + return err; +} + +ssize_t unionfs_read(struct file * file, char __user * buf, size_t count, + loff_t * ppos) +{ + int err = -EINVAL; + struct file *hidden_file = NULL; + loff_t pos = *ppos; + + print_entry_location(); + + if ((err = unionfs_file_revalidate(file, 0))) + goto out; + + fist_print_file("entering read()", file); + + hidden_file = ftohf(file); + + if (!hidden_file->f_op || !hidden_file->f_op->read) + goto out; + + err = hidden_file->f_op->read(hidden_file, buf, count, &pos); + *ppos = pos; + if (err >= 0) { + /* atime should also be updated for reads of size zero or more */ + fist_copy_attr_atime(file->f_dentry->d_inode, + hidden_file->f_dentry->d_inode); + } + memcpy(&(file->f_ra), &(hidden_file->f_ra), + sizeof(struct file_ra_state)); + + out: + fist_print_file("leaving read()", file); + print_exit_status(err); + return err; +} + +#ifdef SUPPORT_BROKEN_LOSETUP +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 *hidden_file = NULL; + + print_entry_location(); + + if ((err = unionfs_file_revalidate(file, 0))) + goto out; + + hidden_file = ftohf(file); + + err = -EINVAL; + if (!hidden_file->f_op || !hidden_file->f_op->sendfile) + goto out; + + err = hidden_file->f_op->sendfile(hidden_file, ppos, count, actor, + target); + + out: + print_exit_status(err); + return err; +} +#endif + +/* this unionfs_write() does not modify data pages! */ +ssize_t unionfs_write(struct file * file, const char __user * buf, size_t count, + loff_t * ppos) +{ + int err = -EINVAL; + struct file *hidden_file = NULL; + struct inode *inode; + struct inode *hidden_inode; + loff_t pos = *ppos; + int bstart, bend; + + print_entry_location(); + + if ((err = unionfs_file_revalidate(file, 1))) + goto out; + + inode = file->f_dentry->d_inode; + + bstart = fbstart(file); + bend = fbend(file); + + BUG_ON(bstart == -1); + + hidden_file = ftohf(file); + hidden_inode = hidden_file->f_dentry->d_inode; + + if (!hidden_file->f_op || !hidden_file->f_op->write) + goto out; + + /* adjust for append -- seek to the end of the file */ + if (file->f_flags & O_APPEND) + pos = inode->i_size; + + err = hidden_file->f_op->write(hidden_file, buf, count, &pos); + + /* + * copy ctime and mtime from lower layer attributes + * atime is unchanged for both layers + */ + if (err >= 0) + fist_copy_attr_times(inode, hidden_inode); + + *ppos = pos; + + /* update this inode's size */ + if (pos > inode->i_size) + inode->i_size = pos; + + out: + print_exit_status(err); + return err; +} + +static int unionfs_file_readdir(struct file *file, void *dirent, + filldir_t filldir) +{ + int err = -ENOTDIR; + print_entry_location(); + print_exit_status(err); + return err; +} + +static unsigned int unionfs_poll(struct file *file, poll_table * wait) +{ + unsigned int mask = DEFAULT_POLLMASK; + struct file *hidden_file = NULL; + + print_entry_location(); + + if (unionfs_file_revalidate(file, 0)) { + /* We should pretend an error happend. */ + mask = POLLERR | POLLIN | POLLOUT; + goto out; + } + + hidden_file = ftohf(file); + + if (!hidden_file->f_op || !hidden_file->f_op->poll) + goto out; + + mask = hidden_file->f_op->poll(hidden_file, wait); + + out: + print_exit_status(mask); + return mask; +} + +/* FIST-LITE special version of mmap */ +static int unionfs_mmap(struct file *file, struct vm_area_struct *vma) +{ + int err = 0; + struct file *hidden_file = NULL; + int willwrite; + + print_entry_location(); + + /* This might could be deferred to mmap's writepage. */ + willwrite = ((vma->vm_flags | VM_SHARED | VM_WRITE) == vma->vm_flags); + if ((err = unionfs_file_revalidate(file, willwrite))) + goto out; + + hidden_file = ftohf(file); + + err = -ENODEV; + if (!hidden_file->f_op || !hidden_file->f_op->mmap) + goto out; + + vma->vm_file = hidden_file; + err = hidden_file->f_op->mmap(hidden_file, vma); + get_file(hidden_file); /* make sure it doesn't get freed on us */ + fput(file); /* no need to keep extra ref on ours */ + + out: + print_exit_status(err); + return err; +} + +static int unionfs_fsync(struct file *file, struct dentry *dentry, int datasync) +{ + int err; + struct file *hidden_file = NULL; + + print_entry_location(); + + if ((err = unionfs_file_revalidate(file, 1))) + goto out; + + hidden_file = ftohf(file); + + err = -EINVAL; + if (!hidden_file->f_op || !hidden_file->f_op->fsync) + goto out; + + down(&hidden_file->f_dentry->d_inode->i_sem); + err = hidden_file->f_op->fsync(hidden_file, hidden_file->f_dentry, + datasync); + up(&hidden_file->f_dentry->d_inode->i_sem); + + out: + print_exit_status(err); + return err; +} + +static int unionfs_fasync(int fd, struct file *file, int flag) +{ + int err = 0; + struct file *hidden_file = NULL; + + print_entry_location(); + + if ((err = unionfs_file_revalidate(file, 1))) + goto out; + + hidden_file = ftohf(file); + + if (hidden_file->f_op && hidden_file->f_op->fasync) + err = hidden_file->f_op->fasync(fd, hidden_file, flag); + + out: + print_exit_status(err); + return err; +} + +struct file_operations unionfs_main_fops = { + .llseek = unionfs_llseek, + .read = unionfs_read, + .write = unionfs_write, + .readdir = unionfs_file_readdir, + .poll = unionfs_poll, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11) + .unlocked_ioctl = unionfs_ioctl, +#endif + .mmap = unionfs_mmap, + .open = unionfs_open, + .flush = unionfs_flush, + .release = unionfs_file_release, + .fsync = unionfs_fsync, + .fasync = unionfs_fasync, +#ifdef SUPPORT_BROKEN_LOSETUP + .sendfile = unionfs_sendfile, +#endif +}; + +/* + * + * vim:shiftwidth=8 + * vim:tabstop=8 + * + * For Emacs: + * Local variables: + * c-basic-offset: 8 + * c-comment-only-line-offset: 0 + * c-offsets-alist: ((statement-block-intro . +) (knr-argdecl-intro . 0) + * (substatement-open . 0) (label . 0) (statement-cont . +)) + * indent-tabs-mode: t + * tab-width: 8 + * End: + */ diff -urN linux-2.6.17-rc1-mm3-orig/fs/unionfs/inode.c linux-2.6.17-rc1-mm3-new/fs/unionfs/inode.c --- linux-2.6.17-rc1-mm3-orig/fs/unionfs/inode.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/unionfs/inode.c 2006-04-11 20:24:21.000000000 +0000 @@ -0,0 +1,1039 @@ +/* + * Copyright (c) 2003-2006 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2006 Josef Sipek + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2005-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2006 Stony Brook University + * Copyright (c) 2003-2006 The Research Foundation of State University of New York + * + * For specific licensing information, see the COPYING file distributed with + * this package. + * + * This Copyright notice must be kept intact and distributed with all sources. + */ +/* + * $Id: inode.c,v 1.263.2.1 2006/04/11 19:37:35 jsipek Exp $ + */ + +#include "unionfs.h" + +/* declarations added for "sparse" */ +extern struct dentry *unionfs_lookup(struct inode *, struct dentry *, + struct nameidata *); +extern int unionfs_readlink(struct dentry *dentry, char __user * buf, + int bufsiz); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) +extern void unionfs_put_link(struct dentry *dentry, struct nameidata *nd); +#else +extern void unionfs_put_link(struct dentry *dentry, struct nameidata *nd, + void *cookie); +#endif + +static int unionfs_create(struct inode *parent, struct dentry *dentry, + int mode, struct nameidata *nd) +{ + int err = 0; + struct dentry *hidden_dentry = NULL; + struct dentry *whiteout_dentry = NULL; + struct dentry *new_hidden_dentry; + struct dentry *hidden_parent_dentry = NULL; + int bindex = 0, bstart; + char *name = NULL; + + print_entry_location(); + lock_dentry(dentry); + fist_print_dentry("IN unionfs_create", dentry); + + /* We start out in the leftmost branch. */ + bstart = dbstart(dentry); + hidden_dentry = dtohd(dentry); + + /* check if whiteout exists in this branch, i.e. lookup .wh.foo first */ + name = alloc_whname(dentry->d_name.name, dentry->d_name.len); + if (IS_ERR(name)) { + err = PTR_ERR(name); + goto out; + } + + whiteout_dentry = + LOOKUP_ONE_LEN(name, hidden_dentry->d_parent, + dentry->d_name.len + WHLEN); + if (IS_ERR(whiteout_dentry)) { + err = PTR_ERR(whiteout_dentry); + whiteout_dentry = NULL; + goto out; + } + + if (whiteout_dentry->d_inode) { + /* .wh.foo has been found. */ + /* First truncate it and then rename it to foo (hence having + * the same overall effect as a normal create. + * + * XXX: This is not strictly correct. If we have unlinked the + * file and it still has a reference count, then we should + * actually unlink the whiteout so that user's data isn't + * hosed over. + */ + struct dentry *hidden_dir_dentry; + struct iattr newattrs; + + down(&whiteout_dentry->d_inode->i_sem); + newattrs.ia_valid = ATTR_CTIME | ATTR_MODE | ATTR_ATIME + | ATTR_MTIME | ATTR_UID | ATTR_GID | ATTR_FORCE + | ATTR_KILL_SUID | ATTR_KILL_SGID; + + newattrs.ia_mode = mode & ~current->fs->umask; + newattrs.ia_uid = current->fsuid; + newattrs.ia_gid = current->fsgid; + + if (whiteout_dentry->d_inode->i_size != 0) { + newattrs.ia_valid |= ATTR_SIZE; + newattrs.ia_size = 0; + } + + err = notify_change(whiteout_dentry, &newattrs); + + up(&whiteout_dentry->d_inode->i_sem); + + if (err) + printk(KERN_WARNING + "unionfs: %s:%d: notify_change failed: %d, ignoring..\n", + __FILE__, __LINE__, err); + + new_hidden_dentry = dtohd(dentry); + DGET(new_hidden_dentry); + + hidden_dir_dentry = GET_PARENT(whiteout_dentry); + lock_rename(hidden_dir_dentry, hidden_dir_dentry); + + if (!(err = is_robranch_super(dentry->d_sb, bstart))) { + err = + vfs_rename(hidden_dir_dentry->d_inode, + whiteout_dentry, + hidden_dir_dentry->d_inode, + new_hidden_dentry); + } + if (!err) { + fist_copy_attr_timesizes(parent, + new_hidden_dentry->d_parent-> + d_inode); + parent->i_nlink = get_nlinks(parent); + } + + unlock_rename(hidden_dir_dentry, hidden_dir_dentry); + DPUT(hidden_dir_dentry); + + DPUT(new_hidden_dentry); + + if (err) { + /* exit if the error returned was NOT -EROFS */ + if (!IS_COPYUP_ERR(err)) + goto out; + /* We were not able to create the file in this branch, + * so, we try to create it in one branch to left + */ + bstart--; + } else { + /* reset the unionfs dentry to point to the .wh.foo entry. */ + + /* Discard any old reference. */ + DPUT(dtohd(dentry)); + + /* Trade one reference to another. */ + set_dtohd_index(dentry, bstart, whiteout_dentry); + whiteout_dentry = NULL; + + err = unionfs_interpose(dentry, parent->i_sb, 0); + goto out; + } + } + + for (bindex = bstart; bindex >= 0; bindex--) { + hidden_dentry = dtohd_index(dentry, bindex); + if (!hidden_dentry) { + /* if hidden_dentry is NULL, create the entire + * dentry directory structure in branch 'bindex'. + * hidden_dentry will NOT be null when bindex == bstart + * because lookup passed as a negative unionfs dentry + * pointing to a lone negative underlying dentry */ + hidden_dentry = create_parents(parent, dentry, bindex); + if (!hidden_dentry || IS_ERR(hidden_dentry)) { + if (IS_ERR(hidden_dentry)) + err = PTR_ERR(hidden_dentry); + continue; + } + } + + fist_checkinode(parent, "unionfs_create"); + + hidden_parent_dentry = lock_parent(hidden_dentry); + if (IS_ERR(hidden_parent_dentry)) { + err = PTR_ERR(hidden_parent_dentry); + goto out; + } + /* We shouldn't create things in a read-only branch. */ + if (!(err = is_robranch_super(dentry->d_sb, bindex))) { + //DQ: vfs_create has a different prototype in 2.6 + err = vfs_create(hidden_parent_dentry->d_inode, + hidden_dentry, mode, nd); + } + if (err || !hidden_dentry->d_inode) { + unlock_dir(hidden_parent_dentry); + + /* break out of for loop if the error wasn't -EROFS */ + if (!IS_COPYUP_ERR(err)) + break; + } else { + err = unionfs_interpose(dentry, parent->i_sb, 0); + if (!err) { + fist_copy_attr_timesizes(parent, + hidden_parent_dentry-> + d_inode); + /* update number of links on parent directory */ + parent->i_nlink = get_nlinks(parent); + } + unlock_dir(hidden_parent_dentry); + break; + } + } + + out: + DPUT(whiteout_dentry); + KFREE(name); + + fist_print_dentry("OUT unionfs_create :", dentry); + unlock_dentry(dentry); + print_exit_status(err); + return err; +} + +struct dentry *unionfs_lookup(struct inode *parent, struct dentry *dentry, + struct nameidata *nd) +{ + /* The locking is done by unionfs_lookup_backend. */ + return unionfs_lookup_backend(dentry, INTERPOSE_LOOKUP); +} + +static int unionfs_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *new_dentry) +{ + int err = 0; + struct dentry *hidden_old_dentry = NULL; + struct dentry *hidden_new_dentry = NULL; + struct dentry *hidden_dir_dentry = NULL; + struct dentry *whiteout_dentry; + char *name = NULL; + + print_entry_location(); + double_lock_dentry(new_dentry, old_dentry); + + hidden_new_dentry = dtohd(new_dentry); + + /* check if whiteout exists in the branch of new dentry, i.e. lookup + * .wh.foo first. If present, delete it */ + name = alloc_whname(new_dentry->d_name.name, new_dentry->d_name.len); + if (IS_ERR(name)) { + err = PTR_ERR(name); + goto out; + } + + whiteout_dentry = + LOOKUP_ONE_LEN(name, hidden_new_dentry->d_parent, + new_dentry->d_name.len + WHLEN); + if (IS_ERR(whiteout_dentry)) { + err = PTR_ERR(whiteout_dentry); + goto out; + } + + if (!whiteout_dentry->d_inode) { + DPUT(whiteout_dentry); + whiteout_dentry = NULL; + } else { + /* found a .wh.foo entry, unlink it and then call vfs_link() */ + hidden_dir_dentry = lock_parent(whiteout_dentry); + if (! + (err = + is_robranch_super(new_dentry->d_sb, + dbstart(new_dentry)))) { + err = + vfs_unlink(hidden_dir_dentry->d_inode, + whiteout_dentry); + } + fist_copy_attr_times(dir, hidden_dir_dentry->d_inode); + dir->i_nlink = get_nlinks(dir); + unlock_dir(hidden_dir_dentry); + hidden_dir_dentry = NULL; + DPUT(whiteout_dentry); + if (err) + goto out; + } + + if (dbstart(old_dentry) != dbstart(new_dentry)) { + hidden_new_dentry = + create_parents(dir, new_dentry, dbstart(old_dentry)); + err = PTR_ERR(hidden_new_dentry); + if (IS_COPYUP_ERR(err)) + goto docopyup; + if (!hidden_new_dentry || IS_ERR(hidden_new_dentry)) + goto out; + } + hidden_new_dentry = dtohd(new_dentry); + hidden_old_dentry = dtohd(old_dentry); + + BUG_ON(dbstart(old_dentry) != dbstart(new_dentry)); + hidden_dir_dentry = lock_parent(hidden_new_dentry); + if (!(err = is_robranch(old_dentry))) + err = + vfs_link(hidden_old_dentry, hidden_dir_dentry->d_inode, + hidden_new_dentry); + unlock_dir(hidden_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_dentry->d_parent-> + d_inode, old_dentry, + old_bstart, bindex, NULL, + old_dentry->d_inode->i_size); + if (!err) { + hidden_new_dentry = + create_parents(dir, new_dentry, bindex); + hidden_old_dentry = dtohd(old_dentry); + hidden_dir_dentry = + lock_parent(hidden_new_dentry); + /* do vfs_link */ + err = + vfs_link(hidden_old_dentry, + hidden_dir_dentry->d_inode, + hidden_new_dentry); + unlock_dir(hidden_dir_dentry); + goto check_link; + } + } + goto out; + } + check_link: + if (err || !hidden_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_instantiate(new_dentry, new_dentry->d_inode); + fist_copy_attr_all(dir, hidden_new_dentry->d_parent->d_inode); + /* propagate number of hard-links */ + old_dentry->d_inode->i_nlink = get_nlinks(old_dentry->d_inode); + + out: + if (!new_dentry->d_inode) + d_drop(new_dentry); + + KFREE(name); + + unlock_dentry(new_dentry); + unlock_dentry(old_dentry); + + print_exit_status(err); + return err; +} + +static int unionfs_symlink(struct inode *dir, struct dentry *dentry, + const char *symname) +{ + int err = 0; + struct dentry *hidden_dentry = NULL; + struct dentry *whiteout_dentry = NULL; + struct dentry *hidden_dir_dentry = NULL; + umode_t mode; + int bindex = 0, bstart; + char *name = NULL; + + print_entry_location(); + lock_dentry(dentry); + fist_print_dentry("IN unionfs_symlink", dentry); + + /* We start out in the leftmost branch. */ + bstart = dbstart(dentry); + + hidden_dentry = dtohd(dentry); + + /* check if whiteout exists in this branch, i.e. lookup .wh.foo first. If present, delete it */ + name = alloc_whname(dentry->d_name.name, dentry->d_name.len); + if (IS_ERR(name)) { + err = PTR_ERR(name); + goto out; + } + + whiteout_dentry = + LOOKUP_ONE_LEN(name, hidden_dentry->d_parent, + dentry->d_name.len + WHLEN); + if (IS_ERR(whiteout_dentry)) { + err = PTR_ERR(whiteout_dentry); + goto out; + } + + if (!whiteout_dentry->d_inode) { + DPUT(whiteout_dentry); + whiteout_dentry = NULL; + } else { + /* found a .wh.foo entry, unlink it and then call vfs_symlink() */ + hidden_dir_dentry = lock_parent(whiteout_dentry); + + fist_print_generic_dentry("HDD", hidden_dir_dentry); + fist_print_generic_dentry("WD", whiteout_dentry); + + if (!(err = is_robranch_super(dentry->d_sb, bstart))) { + err = + vfs_unlink(hidden_dir_dentry->d_inode, + whiteout_dentry); + } + DPUT(whiteout_dentry); + + fist_copy_attr_times(dir, hidden_dir_dentry->d_inode); + /* propagate number of hard-links */ + dir->i_nlink = get_nlinks(dir); + + unlock_dir(hidden_dir_dentry); + + if (err) { + /* exit if the error returned was NOT -EROFS */ + if (!IS_COPYUP_ERR(err)) + goto out; + /* should now try to create symlink in the another branch */ + bstart--; + } + } + + /* deleted whiteout if it was present, now do a normal vfs_symlink() with + possible recursive directory creation */ + for (bindex = bstart; bindex >= 0; bindex--) { + hidden_dentry = dtohd_index(dentry, bindex); + if (!hidden_dentry) { + /* if hidden_dentry is NULL, create the entire + * dentry directory structure in branch 'bindex'. hidden_dentry will NOT be null when + * bindex == bstart because lookup passed as a negative unionfs dentry pointing to a + * lone negative underlying dentry */ + hidden_dentry = create_parents(dir, dentry, bindex); + if (!hidden_dentry || IS_ERR(hidden_dentry)) { + if (IS_ERR(hidden_dentry)) { + err = PTR_ERR(hidden_dentry); + } + fist_dprint(8, + "hidden dentry NULL (or error) for bindex = %d\n", + bindex); + continue; + } + } + + hidden_dir_dentry = lock_parent(hidden_dentry); + + if (!(err = is_robranch_super(dentry->d_sb, bindex))) { + mode = S_IALLUGO; + err = + vfs_symlink(hidden_dir_dentry->d_inode, + hidden_dentry, symname, mode); + } + unlock_dir(hidden_dir_dentry); + + if (err || !hidden_dentry->d_inode) { + /* break out of for loop if error returned was NOT -EROFS */ + if (!IS_COPYUP_ERR(err)) + break; + } else { + err = unionfs_interpose(dentry, dir->i_sb, 0); + if (!err) { + fist_copy_attr_timesizes(dir, + hidden_dir_dentry-> + d_inode); + /* update number of links on parent directory */ + dir->i_nlink = get_nlinks(dir); + } + break; + } + } + + out: + if (!dentry->d_inode) + d_drop(dentry); + + KFREE(name); + fist_print_dentry("OUT unionfs_symlink :", dentry); + unlock_dentry(dentry); + print_exit_status(err); + return err; +} + +static int unionfs_mkdir(struct inode *parent, struct dentry *dentry, int mode) +{ + int err = 0; + struct dentry *hidden_dentry = NULL, *whiteout_dentry = NULL; + struct dentry *hidden_parent_dentry = NULL; + int bindex = 0, bstart; + char *name = NULL; + int whiteout_unlinked = 0; + uid_t saved_uid = current->fsuid; + gid_t saved_gid = current->fsgid; + + print_entry_location(); + lock_dentry(dentry); + fist_print_dentry("IN unionfs_mkdir", dentry); + bstart = dbstart(dentry); + + hidden_dentry = dtohd(dentry); + + // check if whiteout exists in this branch, i.e. lookup .wh.foo first + name = alloc_whname(dentry->d_name.name, dentry->d_name.len); + if (IS_ERR(name)) { + err = PTR_ERR(name); + goto out; + } + + whiteout_dentry = + LOOKUP_ONE_LEN(name, hidden_dentry->d_parent, + dentry->d_name.len + WHLEN); + if (IS_ERR(whiteout_dentry)) { + err = PTR_ERR(whiteout_dentry); + goto out; + } + + if (!whiteout_dentry->d_inode) { + DPUT(whiteout_dentry); + whiteout_dentry = NULL; + } else { + hidden_parent_dentry = lock_parent(whiteout_dentry); + + /* Set the uid and gid to trick the fs into allowing us to create + * the file */ + current->fsuid = hidden_parent_dentry->d_inode->i_uid; + current->fsgid = hidden_parent_dentry->d_inode->i_gid; + //found a.wh.foo entry, remove it then do vfs_mkdir + if (!(err = is_robranch_super(dentry->d_sb, bstart))) { + err = + vfs_unlink(hidden_parent_dentry->d_inode, + whiteout_dentry); + } + DPUT(whiteout_dentry); + + current->fsuid = saved_uid; + current->fsgid = saved_gid; + + unlock_dir(hidden_parent_dentry); + + if (err) { + /* exit if the error returned was NOT -EROFS */ + if (!IS_COPYUP_ERR(err)) + goto out; + bstart--; + } else { + whiteout_unlinked = 1; + } + } + + for (bindex = bstart; bindex >= 0; bindex--) { + hidden_dentry = dtohd_index(dentry, bindex); + if (!hidden_dentry) { + hidden_dentry = create_parents(parent, dentry, bindex); + if (!hidden_dentry || IS_ERR(hidden_dentry)) { + fist_dprint(8, + "hidden dentry NULL for bindex = %d\n", + bindex); + continue; + } + } + + hidden_parent_dentry = lock_parent(hidden_dentry); + if (IS_ERR(hidden_parent_dentry)) { + err = PTR_ERR(hidden_parent_dentry); + goto out; + } + if (!(err = is_robranch_super(dentry->d_sb, bindex))) { + err = + vfs_mkdir(hidden_parent_dentry->d_inode, + hidden_dentry, mode); + } + unlock_dir(hidden_parent_dentry); + + /* XXX this could potentially return a negative hidden_dentry! */ + if (err || !hidden_dentry->d_inode) { + /* break out of for loop if error returned was NOT -EROFS */ + if (!IS_COPYUP_ERR(err)) + break; + } else { + int i; + int bend = dbend(dentry); + for (i = bindex + 1; i < bend; i++) { + if (dtohd_index(dentry, i)) { + DPUT(dtohd_index(dentry, i)); + set_dtohd_index(dentry, i, NULL); + } + } + bend = bindex; + set_dbend(dentry, bend); + + err = unionfs_interpose(dentry, parent->i_sb, 0); + if (!err) { + fist_copy_attr_timesizes(parent, + hidden_parent_dentry-> + d_inode); + /* update number of links on parent directory */ + parent->i_nlink = get_nlinks(parent); + } + whiteout_dentry = LOOKUP_ONE_LEN(UNIONFS_DIR_OPAQUE, + hidden_dentry, + sizeof + (UNIONFS_DIR_OPAQUE) - + 1); + if (IS_ERR(whiteout_dentry)) { + err = PTR_ERR(whiteout_dentry); + goto out; + } + down(&hidden_dentry->d_inode->i_sem); + err = vfs_create(hidden_dentry->d_inode, + whiteout_dentry, 0600, NULL); + up(&hidden_dentry->d_inode->i_sem); + DPUT(whiteout_dentry); + + if (err) { + fist_dprint(8, + "mkdir: error creating directory override entry: %d\n", + err); + goto out; + } + break; + } + } + + out: + if (!dentry->d_inode) + d_drop(dentry); + + KFREE(name); + + fist_print_dentry("OUT unionfs_mkdir :", dentry); + unlock_dentry(dentry); + print_exit_status(err); + return err; +} + +static int unionfs_mknod(struct inode *dir, struct dentry *dentry, int mode, + dev_t dev) +{ + int err = 0; + struct dentry *hidden_dentry = NULL, *whiteout_dentry = NULL; + struct dentry *hidden_parent_dentry = NULL; + int bindex = 0, bstart; + char *name = NULL; + int whiteout_unlinked = 0; + + print_entry_location(); + lock_dentry(dentry); + fist_print_dentry("IN unionfs_mknod", dentry); + bstart = dbstart(dentry); + + hidden_dentry = dtohd(dentry); + + // check if whiteout exists in this branch, i.e. lookup .wh.foo first + name = alloc_whname(dentry->d_name.name, dentry->d_name.len); + if (IS_ERR(name)) { + err = PTR_ERR(name); + goto out; + } + + whiteout_dentry = + LOOKUP_ONE_LEN(name, hidden_dentry->d_parent, + dentry->d_name.len + WHLEN); + if (IS_ERR(whiteout_dentry)) { + err = PTR_ERR(whiteout_dentry); + goto out; + } + + if (!whiteout_dentry->d_inode) { + DPUT(whiteout_dentry); + whiteout_dentry = NULL; + } else { + /* found .wh.foo, unlink it */ + hidden_parent_dentry = lock_parent(whiteout_dentry); + + //found a.wh.foo entry, remove it then do vfs_mkdir + if (!(err = is_robranch_super(dentry->d_sb, bstart))) + err = vfs_unlink(hidden_parent_dentry->d_inode, + whiteout_dentry); + DPUT(whiteout_dentry); + + unlock_dir(hidden_parent_dentry); + + if (err) { + if (!IS_COPYUP_ERR(err)) + goto out; + + bstart--; + } else { + whiteout_unlinked = 1; + } + } + + for (bindex = bstart; bindex >= 0; bindex--) { + hidden_dentry = dtohd_index(dentry, bindex); + if (!hidden_dentry) { + hidden_dentry = create_parents(dir, dentry, bindex); + if (!hidden_dentry || IS_ERR(hidden_dentry)) { + fist_dprint(8, + "hidden dentry NULL for bindex = %d\n", + bindex); + continue; + } + } + + hidden_parent_dentry = lock_parent(hidden_dentry); + if (IS_ERR(hidden_parent_dentry)) { + err = PTR_ERR(hidden_parent_dentry); + goto out; + } + if (!(err = is_robranch_super(dentry->d_sb, bindex))) { + err = vfs_mknod(hidden_parent_dentry->d_inode, + hidden_dentry, mode, dev); + } + /* XXX this could potentially return a negative hidden_dentry! */ + if (err || !hidden_dentry->d_inode) { + unlock_dir(hidden_parent_dentry); + /* break out of for, if error was NOT -EROFS */ + if (!IS_COPYUP_ERR(err)) + break; + } else { + err = unionfs_interpose(dentry, dir->i_sb, 0); + if (!err) { + fist_copy_attr_timesizes(dir, + hidden_parent_dentry-> + d_inode); + /* update number of links on parent directory */ + dir->i_nlink = get_nlinks(dir); + } + unlock_dir(hidden_parent_dentry); + + break; + } + } + + out: + if (!dentry->d_inode) + d_drop(dentry); + + if (name) { + KFREE(name); + } + + fist_print_dentry("OUT unionfs_mknod :", dentry); + unlock_dentry(dentry); + print_exit_status(err); + return err; +} + +int unionfs_readlink(struct dentry *dentry, char __user * buf, int bufsiz) +{ + int err; + struct dentry *hidden_dentry; + + print_entry_location(); + lock_dentry(dentry); + hidden_dentry = dtohd(dentry); + fist_print_dentry("unionfs_readlink IN", dentry); + + if (!hidden_dentry->d_inode->i_op || + !hidden_dentry->d_inode->i_op->readlink) { + err = -EINVAL; + goto out; + } + + err = hidden_dentry->d_inode->i_op->readlink(hidden_dentry, + buf, bufsiz); + if (err > 0) + fist_copy_attr_atime(dentry->d_inode, hidden_dentry->d_inode); + + out: + unlock_dentry(dentry); + print_exit_status(err); + return err; +} + +/* We don't lock the dentry here, because readlink does the heavy lifting. */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) +static int unionfs_follow_link(struct dentry *dentry, struct nameidata *nd) +#else +static void *unionfs_follow_link(struct dentry *dentry, struct nameidata *nd) +#endif +{ + char *buf; + int len = PAGE_SIZE, err; + mm_segment_t old_fs; + + print_entry_location(); + + /* This is freed by the put_link method assuming a successful call. */ + buf = (char *)KMALLOC(len, GFP_KERNEL); + if (!buf) { + err = -ENOMEM; + goto out; + } + + /* read the symlink, and then we will follow it */ + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = dentry->d_inode->i_op->readlink(dentry, (char __user *)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: + print_exit_status(err); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) + return err; +#else + return ERR_PTR(err); +#endif +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) +void unionfs_put_link(struct dentry *dentry, struct nameidata *nd) +#else +void unionfs_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie) +#endif +{ + char *link; + print_entry_location(); + link = nd_get_link(nd); + KFREE(link); + print_exit_location(); +} + +/* Basically copied from the kernel vfs permission(), but we've changed + * the following: (1) the IS_RDONLY check is skipped, and (2) if you set + * the mount option `nfsperms=insceure', we assume that -EACCES means that + * the export is read-only and we should check standard Unix permissions. + * This means that NFS ACL checks (or other advanced permission features) + * are bypassed. + */ +static int inode_permission(struct inode *inode, int mask, struct nameidata *nd, + int bindex) +{ + int retval, submask; + + if (mask & MAY_WRITE) { + /* The first branch is allowed to be really readonly. */ + if (bindex == 0) { + umode_t mode = inode->i_mode; + if (IS_RDONLY(inode) && (S_ISREG(mode) || S_ISDIR(mode) + || S_ISLNK(mode))) + return -EROFS; + } + /* + * Nobody gets write access to an immutable file. + */ + if (IS_IMMUTABLE(inode)) + return -EACCES; + } + + /* Ordinary permission routines do not understand MAY_APPEND. */ + submask = mask & ~MAY_APPEND; + if (inode->i_op && inode->i_op->permission) { + retval = inode->i_op->permission(inode, submask, nd); + if ((retval == -EACCES) && (submask & MAY_WRITE) && + (!strcmp("nfs", (inode)->i_sb->s_type->name)) && + (nd) && (nd->mnt) && (nd->mnt->mnt_sb) && + (branchperms(nd->mnt->mnt_sb, bindex) & MAY_NFSRO)) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) + retval = vfs_permission(inode, submask); +#else + retval = generic_permission(inode, submask, NULL); +#endif + } + } else { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) + retval = vfs_permission(inode, submask); +#else + retval = generic_permission(inode, submask, NULL); +#endif + } + if (retval) + return retval; + + return security_inode_permission(inode, mask, nd); +} + +static int unionfs_permission(struct inode *inode, int mask, + struct nameidata *nd) +{ + struct inode *hidden_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); + + print_entry_location(); + + bstart = ibstart(inode); + bend = ibend(inode); + + fist_print_inode("IN unionfs_permission", inode); + + for (bindex = bstart; bindex <= bend; bindex++) { + hidden_inode = itohi_index(inode, bindex); + if (!hidden_inode) + continue; + + /* check the condition for D-F-D underlying files/directories, + * we dont have to check for files, if we are checking for + * directories. + */ + if (!is_file && !S_ISDIR(hidden_inode->i_mode)) + continue; + /* We use our own special version of permission, such that + * only the first branch returns -EROFS. */ + err = inode_permission(hidden_inode, mask, nd, bindex); + /* 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(hidden_inode); + if (!err) + put_write_access(hidden_inode); + } + break; + } + } + + out: + print_exit_status(err); + return err; +} + +static int unionfs_setattr(struct dentry *dentry, struct iattr *ia) +{ + int err = 0; + struct dentry *hidden_dentry; + struct inode *inode = NULL; + struct inode *hidden_inode = NULL; + int bstart, bend, bindex; + int i; + int copyup = 0; + + print_entry_location(); + lock_dentry(dentry); + bstart = dbstart(dentry); + bend = dbend(dentry); + inode = dentry->d_inode; + + for (bindex = bstart; (bindex <= bend) || (bindex == bstart); bindex++) { + hidden_dentry = dtohd_index(dentry, bindex); + if (!hidden_dentry) + continue; + BUG_ON(hidden_dentry->d_inode == NULL); + + /* If the file is on a read only branch */ + if (is_robranch_super(dentry->d_sb, bindex) + || IS_RDONLY(hidden_dentry->d_inode)) { + if (copyup || (bindex != bstart)) + continue; + /* Only if its the leftmost file, copyup the file */ + for (i = bstart - 1; i >= 0; i--) { + size_t size = dentry->d_inode->i_size; + if (ia->ia_valid & ATTR_SIZE) + size = ia->ia_size; + err = copyup_dentry(dentry->d_parent->d_inode, + dentry, bstart, i, NULL, + size); + + if (!err) { + copyup = 1; + hidden_dentry = dtohd(dentry); + break; + } + /* if error is in the leftmost f/s, pass it up */ + if (i == 0) + goto out; + } + + } + err = notify_change(hidden_dentry, ia); + if (err) + goto out; + break; + } + + /* get the size from the first hidden inode */ + hidden_inode = itohi(dentry->d_inode); + fist_checkinode(inode, "unionfs_setattr"); + fist_copy_attr_all(inode, hidden_inode); + + out: + unlock_dentry(dentry); + fist_checkinode(inode, "post unionfs_setattr"); + print_exit_status(err); + 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, + .setxattr = unionfs_setxattr, + .getxattr = unionfs_getxattr, + .removexattr = unionfs_removexattr, + .listxattr = unionfs_listxattr, +}; + +struct inode_operations unionfs_main_iops = { + .permission = unionfs_permission, + .setattr = unionfs_setattr, + .setxattr = unionfs_setxattr, + .getxattr = unionfs_getxattr, + .removexattr = unionfs_removexattr, + .listxattr = unionfs_listxattr, +}; + +/* + * + * vim:shiftwidth=8 + * vim:tabstop=8 + * + * For Emacs: + * Local variables: + * c-basic-offset: 8 + * c-comment-only-line-offset: 0 + * c-offsets-alist: ((statement-block-intro . +) (knr-argdecl-intro . 0) + * (substatement-open . 0) (label . 0) (statement-cont . +)) + * indent-tabs-mode: t + * tab-width: 8 + * End: + */ diff -urN linux-2.6.17-rc1-mm3-orig/fs/unionfs/lookup.c linux-2.6.17-rc1-mm3-new/fs/unionfs/lookup.c --- linux-2.6.17-rc1-mm3-orig/fs/unionfs/lookup.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/unionfs/lookup.c 2006-04-11 20:24:21.000000000 +0000 @@ -0,0 +1,505 @@ +/* + * Copyright (c) 2003-2006 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2006 Josef Sipek + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2005-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2006 Stony Brook University + * Copyright (c) 2003-2006 The Research Foundation of State University of New York + * + * For specific licensing information, see the COPYING file distributed with + * this package. + * + * This Copyright notice must be kept intact and distributed with all sources. + */ +/* + * $Id: lookup.c,v 1.43 2006/02/13 00:18:40 jsipek Exp $ + */ + +#include "unionfs.h" + +static int is_opaque_dir(struct dentry *dentry, int bindex); +static int is_validname(const char *name); + +struct dentry *unionfs_lookup_backend(struct dentry *dentry, int lookupmode) +{ + int err = 0; + struct dentry *hidden_dentry = NULL; + struct dentry *wh_hidden_dentry = NULL; + struct dentry *hidden_dir_dentry = NULL; + struct dentry *parent_dentry = NULL; + int bindex, bstart, bend, bopaque; + int dentry_count = 0; /* Number of positive dentries. */ + int first_dentry_offset = -1; + struct dentry *first_hidden_dentry = NULL; + int locked_parent = 0; + int locked_child = 0; + + int opaque; + char *whname = NULL; + const char *name; + int namelen; + + print_entry("mode = %d", lookupmode); + + /* 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. */ + if (lookupmode == INTERPOSE_PARTIAL || lookupmode == INTERPOSE_REVAL + || lookupmode == INTERPOSE_REVAL_NEG) { + verify_locked(dentry); + } else { + BUG_ON(dtopd_nocheck(dentry) != NULL); + locked_child = 1; + } + if (lookupmode != INTERPOSE_PARTIAL) + if ((err = new_dentry_private_data(dentry))) + goto out; + /* must initialize dentry operations */ + dentry->d_op = &unionfs_dops; + + parent_dentry = GET_PARENT(dentry); + /* We never partial lookup the root directory. */ + if (parent_dentry != dentry) { + lock_dentry(parent_dentry); + locked_parent = 1; + } else { + DPUT(parent_dentry); + parent_dentry = NULL; + goto out; + } + + fist_print_dentry("IN unionfs_lookup (parent)", parent_dentry); + fist_print_dentry("IN unionfs_lookup (child)", dentry); + + 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_dentry); + bend = dbend(parent_dentry); + bopaque = dbopaque(parent_dentry); + BUG_ON(bstart < 0); + + /* It would be ideal if we could convert partial lookups to only have + * to do this work when they really need to. It could probably improve + * performance quite a bit, and maybe simplify the rest of the code. */ + if (lookupmode == INTERPOSE_PARTIAL) { + bstart++; + if ((bopaque != -1) && (bopaque < bend)) + bend = bopaque; + } + + fist_dprint(8, "bstart = %d, bend = %d\n", bstart, bend); + for (bindex = bstart; bindex <= bend; bindex++) { + hidden_dentry = dtohd_index(dentry, bindex); + if (lookupmode == INTERPOSE_PARTIAL && hidden_dentry) + continue; + BUG_ON(hidden_dentry != NULL); + + hidden_dir_dentry = dtohd_index(parent_dentry, bindex); + + /* if the parent hidden dentry does not exist skip this */ + if (!(hidden_dir_dentry && hidden_dir_dentry->d_inode)) + continue; + + /* also skip it if the parent isn't a directory. */ + if (!S_ISDIR(hidden_dir_dentry->d_inode->i_mode)) + continue; + + /* Reuse the whiteout name because its value doesn't change. */ + if (!whname) { + whname = alloc_whname(name, namelen); + if (IS_ERR(whname)) { + err = PTR_ERR(whname); + goto out_free; + } + } + + /* check if whiteout exists in this branch: lookup .wh.foo */ + wh_hidden_dentry = LOOKUP_ONE_LEN(whname, hidden_dir_dentry, + namelen + WHLEN); + if (IS_ERR(wh_hidden_dentry)) { + DPUT(first_hidden_dentry); + err = PTR_ERR(wh_hidden_dentry); + goto out_free; + } + + if (wh_hidden_dentry->d_inode) { + /* We found a whiteout so lets give up. */ + fist_dprint(8, "whiteout found in %d\n", bindex); + if (S_ISREG(wh_hidden_dentry->d_inode->i_mode)) { + set_dbend(dentry, bindex); + set_dbopaque(dentry, bindex); + DPUT(wh_hidden_dentry); + break; + } + err = -EIO; + printk(KERN_NOTICE "EIO: Invalid whiteout entry type" + " %d.\n", wh_hidden_dentry->d_inode->i_mode); + DPUT(wh_hidden_dentry); + DPUT(first_hidden_dentry); + goto out_free; + } + + DPUT(wh_hidden_dentry); + wh_hidden_dentry = NULL; + + /* Now do regular lookup; lookup foo */ + hidden_dentry = LOOKUP_ONE_LEN(name, hidden_dir_dentry, + namelen); + fist_print_generic_dentry("hidden result", hidden_dentry); + if (IS_ERR(hidden_dentry)) { + DPUT(first_hidden_dentry); + err = PTR_ERR(hidden_dentry); + goto out_free; + } + + /* Store the first negative dentry specially, because if they + * are all negative we need this for future creates. */ + if (!hidden_dentry->d_inode) { + if (!first_hidden_dentry && (dbstart(dentry) == -1)) { + first_hidden_dentry = hidden_dentry; + first_dentry_offset = bindex; + } else { + DPUT(hidden_dentry); + } + continue; + } + + /* number of positive dentries */ + dentry_count++; + + /* store underlying dentry */ + if (dbstart(dentry) == -1) + set_dbstart(dentry, bindex); + set_dtohd_index(dentry, bindex, hidden_dentry); + set_dbend(dentry, bindex); + + /* update parent directory's atime with the bindex */ + fist_copy_attr_atime(parent_dentry->d_inode, + hidden_dir_dentry->d_inode); + + /* We terminate file lookups here. */ + if (!S_ISDIR(hidden_dentry->d_inode->i_mode)) { + if (lookupmode == INTERPOSE_PARTIAL) + continue; + if (dentry_count == 1) + goto out_positive; + /* This can only happen with mixed D-*-F-* */ + BUG_ON(!S_ISDIR(dtohd(dentry)->d_inode->i_mode)); + continue; + } + + opaque = is_opaque_dir(dentry, bindex); + if (opaque < 0) { + DPUT(first_hidden_dentry); + err = opaque; + goto out_free; + } + if (opaque) { + set_dbend(dentry, bindex); + set_dbopaque(dentry, bindex); + break; + } + } + + if (dentry_count) + goto out_positive; + else + goto out_negative; + + out_negative: + if (lookupmode == INTERPOSE_PARTIAL) + goto out; + + /* If we've only got negative dentries, then use the leftmost one. */ + if (lookupmode == INTERPOSE_REVAL) { + if (dentry->d_inode) { + itopd(dentry->d_inode)->uii_stale = 1; + } + goto out; + } + /* This should only happen if we found a whiteout. */ + if (first_dentry_offset == -1) { + first_hidden_dentry = LOOKUP_ONE_LEN(name, hidden_dir_dentry, + namelen); + first_dentry_offset = bindex; + if (IS_ERR(first_hidden_dentry)) { + err = PTR_ERR(first_hidden_dentry); + goto out; + } + } + set_dtohd_index(dentry, first_dentry_offset, first_hidden_dentry); + set_dbstart(dentry, first_dentry_offset); + set_dbend(dentry, first_dentry_offset); + + if (lookupmode == INTERPOSE_REVAL_NEG) + BUG_ON(dentry->d_inode != NULL); + else + d_add(dentry, NULL); + goto out; + +/* This part of the code is for positive dentries. */ + out_positive: + BUG_ON(dentry_count <= 0); + + /* If we're holding onto the first negative dentry throw it out. */ + DPUT(first_hidden_dentry); + + /* Partial lookups need to reinterpose, or throw away older negs. */ + if (lookupmode == INTERPOSE_PARTIAL) { + if (dentry->d_inode) { + unionfs_reinterpose(dentry); + goto out; + } + + /* This somehow turned positive, so it is as if we had a + * negative revalidation. */ + lookupmode = INTERPOSE_REVAL_NEG; + + update_bstart(dentry); + bstart = dbstart(dentry); + bend = dbend(dentry); + } + + err = unionfs_interpose(dentry, dentry->d_sb, lookupmode); + if (err) + goto out_drop; + + fist_checkinode(dentry->d_inode, "unionfs_lookup OUT: child"); + fist_checkinode(parent_dentry->d_inode, "unionfs_lookup OUT: dir"); + goto out; + + out_drop: + d_drop(dentry); + + out_free: + /* should dput all the underlying dentries on error condition */ + bstart = dbstart(dentry); + if (bstart >= 0) { + bend = dbend(dentry); + for (bindex = bstart; bindex <= bend; bindex++) + DPUT(dtohd_index(dentry, bindex)); + } + KFREE(dtohd_ptr(dentry)); + dtohd_ptr(dentry) = NULL; + set_dbstart(dentry, -1); + set_dbend(dentry, -1); + + out: + if (!err && dtopd(dentry)) { + BUG_ON(dbend(dentry) > dtopd(dentry)->udi_bcount); + BUG_ON(dbend(dentry) > sbmax(dentry->d_sb)); + BUG_ON(dbstart(dentry) < 0); + } + KFREE(whname); + fist_print_dentry("OUT unionfs_lookup (parent)", parent_dentry); + fist_print_dentry("OUT unionfs_lookup (child)", dentry); + if (locked_parent) + unlock_dentry(parent_dentry); + DPUT(parent_dentry); + if (locked_child) + unlock_dentry(dentry); + print_exit_status(err); + return ERR_PTR(err); +} + +/* This is a utility function that fills in a unionfs dentry.*/ +int unionfs_partial_lookup(struct dentry *dentry) +{ + struct dentry *tmp; + + tmp = unionfs_lookup_backend(dentry, INTERPOSE_PARTIAL); + if (!tmp) + return 0; + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + /* need to change the interface */ + BUG_ON(tmp != dentry); + return -ENOSYS; +} + +/* The rest of these are utility functions for lookup. */ +static int is_opaque_dir(struct dentry *dentry, int bindex) +{ + int err = 0; + struct dentry *hidden_dentry; + struct dentry *wh_hidden_dentry; + struct inode *hidden_inode; + uid_t saved_uid = current->fsuid; + gid_t saved_gid = current->fsgid; + + print_entry_location(); + + hidden_dentry = dtohd_index(dentry, bindex); + hidden_inode = hidden_dentry->d_inode; + + BUG_ON(!S_ISDIR(hidden_inode->i_mode)); + + current->fsuid = hidden_inode->i_uid; + current->fsgid = hidden_inode->i_gid; + wh_hidden_dentry = LOOKUP_ONE_LEN(UNIONFS_DIR_OPAQUE, hidden_dentry, + sizeof(UNIONFS_DIR_OPAQUE) - 1); + current->fsuid = saved_uid; + current->fsgid = saved_gid; + if (IS_ERR(wh_hidden_dentry)) { + err = PTR_ERR(wh_hidden_dentry); + fist_dprint(1, "LOOKUP_ONE_LEN returned: %d\n", err); + goto out; + } + if (wh_hidden_dentry->d_inode) + err = 1; + DPUT(wh_hidden_dentry); + out: + print_exit_status(err); + return err; +} + +static int is_validname(const char *name) +{ + if (!strncmp(name, WHPFX, WHLEN)) + return 0; + if (!strncmp(name, UNIONFS_DIR_OPAQUE_NAME, + sizeof(UNIONFS_DIR_OPAQUE_NAME) - 1)) + return 0; + return 1; +} + +/* The dentry cache is just so we have properly sized dentries. */ +static kmem_cache_t *unionfs_dentry_cachep; +int init_dentry_cache(void) +{ + unionfs_dentry_cachep = + kmem_cache_create("unionfs_dentry", + sizeof(struct unionfs_dentry_info), 0, + SLAB_RECLAIM_ACCOUNT, NULL, NULL); + + if (!unionfs_dentry_cachep) + return -ENOMEM; + return 0; +} + +void destroy_dentry_cache(void) +{ + if (!unionfs_dentry_cachep) + return; + if (kmem_cache_destroy(unionfs_dentry_cachep)) + printk(KERN_ERR + "unionfs_dentry_cache: not all structures were freed\n"); + return; +} + +void free_dentry_private_data(struct unionfs_dentry_info *udi) +{ + if (!udi) + return; + kmem_cache_free(unionfs_dentry_cachep, udi); +} + +int new_dentry_private_data(struct dentry *dentry) +{ + int newsize; + int oldsize = 0; + + spin_lock(&dentry->d_lock); + if (!dtopd_nocheck(dentry)) { + dtopd_lhs(dentry) = (struct unionfs_dentry_info *) + kmem_cache_alloc(unionfs_dentry_cachep, SLAB_ATOMIC); + if (!dtopd_nocheck(dentry)) + goto out; + init_MUTEX_LOCKED(&dtopd_nocheck(dentry)->udi_sem); +#ifdef TRACKLOCK + printk("INITLOCK:%p\n", dentry); +#endif + dtohd_ptr(dentry) = NULL; + } else { + oldsize = sizeof(struct dentry *) * dtopd(dentry)->udi_bcount; + } + + dtopd_nocheck(dentry)->udi_bstart = -1; + dtopd_nocheck(dentry)->udi_bend = -1; + dtopd_nocheck(dentry)->udi_bopaque = -1; + dtopd_nocheck(dentry)->udi_bcount = sbmax(dentry->d_sb); + atomic_set(&dtopd_nocheck(dentry)->udi_generation, + atomic_read(&stopd(dentry->d_sb)->usi_generation)); + newsize = sizeof(struct dentry *) * sbmax(dentry->d_sb); + + /* Don't reallocate when we already have enough space. */ + /* It would be ideal if we could actually use the slab macros to + * determine what our object sizes is, but those are not exported. + */ + if (oldsize) { + int minsize = malloc_sizes[0].cs_size; + + if (!newsize || ((oldsize < newsize) && (newsize > minsize))) { + KFREE(dtohd_ptr(dentry)); + dtohd_ptr(dentry) = NULL; + } + } + + if (!dtohd_ptr(dentry) && newsize) { + dtohd_ptr(dentry) = KMALLOC(newsize, GFP_ATOMIC); + if (!dtohd_ptr(dentry)) + goto out; + } + + if (oldsize > newsize) + memset(dtohd_ptr(dentry), 0, oldsize); + else + memset(dtohd_ptr(dentry), 0, newsize); + + spin_unlock(&dentry->d_lock); + return 0; + + out: + free_dentry_private_data(dtopd_nocheck(dentry)); + dtopd_lhs(dentry) = NULL; + spin_unlock(&dentry->d_lock); + return -ENOMEM; +} + +void update_bstart(struct dentry *dentry) +{ + int bindex; + int bstart = dbstart(dentry); + int bend = dbend(dentry); + struct dentry *hidden_dentry; + + for (bindex = bstart; bindex <= bend; bindex++) { + hidden_dentry = dtohd_index(dentry, bindex); + if (!hidden_dentry) + continue; + if (hidden_dentry->d_inode) { + set_dbstart(dentry, bindex); + break; + } + DPUT(hidden_dentry); + set_dtohd_index(dentry, bindex, NULL); + } +} + +/* + * + * vim:shiftwidth=8 + * vim:tabstop=8 + * + * For Emacs: + * Local variables: + * c-basic-offset: 8 + * c-comment-only-line-offset: 0 + * c-offsets-alist: ((statement-block-intro . +) (knr-argdecl-intro . 0) + * (substatement-open . 0) (label . 0) (statement-cont . +)) + * indent-tabs-mode: t + * tab-width: 8 + * End: + */ diff -urN linux-2.6.17-rc1-mm3-orig/fs/unionfs/main.c linux-2.6.17-rc1-mm3-new/fs/unionfs/main.c --- linux-2.6.17-rc1-mm3-orig/fs/unionfs/main.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/unionfs/main.c 2006-04-11 20:24:21.000000000 +0000 @@ -0,0 +1,811 @@ +/* + * Copyright (c) 2003-2006 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2006 Josef Sipek + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2005-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2006 Stony Brook University + * Copyright (c) 2003-2006 The Research Foundation of State University of New York + * + * For specific licensing information, see the COPYING file distributed with + * this package. + * + * This Copyright notice must be kept intact and distributed with all sources. + */ +/* + * $Id: main.c,v 1.161.2.1 2006/04/11 05:36:03 jsipek Exp $ + */ + +#include "unionfs.h" +#include +#include + +/* declarations added for "sparse" */ +extern void unionfs_kill_block_super(struct super_block *sb); + +/* declarations added for malloc_debugging */ + +#ifdef FIST_MALLOC_DEBUG +extern atomic_t unionfs_malloc_counter; +extern atomic_t unionfs_mallocs_outstanding; +#endif +/* sb we pass is unionfs's super_block */ +int unionfs_interpose(struct dentry *dentry, struct super_block *sb, int flag) +{ + struct inode *hidden_inode; + struct dentry *hidden_dentry; + int err = 0; + struct inode *inode; + int is_negative_dentry = 1; + int bindex, bstart, bend; + + print_entry("flag = %d", flag); + + verify_locked(dentry); + + fist_print_dentry("In unionfs_interpose", dentry); + + bstart = dbstart(dentry); + bend = dbend(dentry); + + /* Make sure that we didn't get a negative dentry. */ + for (bindex = bstart; bindex <= bend; bindex++) { + if (dtohd_index(dentry, bindex) && + dtohd_index(dentry, bindex)->d_inode) { + is_negative_dentry = 0; + break; + } + } + BUG_ON(is_negative_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; + itopd(inode)->b_start = -1; + itopd(inode)->b_end = -1; + atomic_set(&itopd(inode)->uii_generation, + atomic_read(&stopd(sb)->usi_generation)); + + itohi_ptr(inode) = + KZALLOC(sbmax(sb) * sizeof(struct inode *), GFP_KERNEL); + if (!itohi_ptr(inode)) { + err = -ENOMEM; + goto out; + } + } else { + ino_t ino; + /* get unique inode number for unionfs */ +#ifdef UNIONFS_IMAP + if (stopd(sb)->usi_persistent) { + err = read_uin(sb, bindex, + dtohd_index(dentry, + bindex)->d_inode->i_ino, + O_CREAT, &ino); + if (err) + goto out; + } else +#endif + ino = iunique(sb, UNIONFS_ROOT_INO); + + inode = IGET(sb, ino); + if (!inode) { + err = -EACCES; /* should be impossible??? */ + goto out; + } + } + + down(&inode->i_sem); + if (atomic_read(&inode->i_count) > 1) + goto skip; + + for (bindex = bstart; bindex <= bend; bindex++) { + hidden_dentry = dtohd_index(dentry, bindex); + if (!hidden_dentry) { + set_itohi_index(inode, bindex, NULL); + continue; + } + /* Initialize the hidden inode to the new hidden inode. */ + if (!hidden_dentry->d_inode) + continue; + set_itohi_index(inode, bindex, IGRAB(hidden_dentry->d_inode)); + } + + ibstart(inode) = dbstart(dentry); + ibend(inode) = dbend(dentry); + + /* Use attributes from the first branch. */ + hidden_inode = itohi(inode); + + /* Use different set of inode ops for symlinks & directories */ + if (S_ISLNK(hidden_inode->i_mode)) + inode->i_op = &unionfs_symlink_iops; + else if (S_ISDIR(hidden_inode->i_mode)) + inode->i_op = &unionfs_dir_iops; + + /* Use different set of file ops for directories */ + if (S_ISDIR(hidden_inode->i_mode)) + inode->i_fop = &unionfs_dir_fops; + + /* properly initialize special inodes */ + if (S_ISBLK(hidden_inode->i_mode) || S_ISCHR(hidden_inode->i_mode) || + S_ISFIFO(hidden_inode->i_mode) || S_ISSOCK(hidden_inode->i_mode)) + init_special_inode(inode, hidden_inode->i_mode, + hidden_inode->i_rdev); + + /* Fix our inode's address operations to that of the lower inode (Unionfs is FiST-Lite) */ + if (inode->i_mapping->a_ops != hidden_inode->i_mapping->a_ops) { + fist_dprint(7, "fixing inode 0x%p a_ops (0x%p -> 0x%p)\n", + inode, inode->i_mapping->a_ops, + hidden_inode->i_mapping->a_ops); + inode->i_mapping->a_ops = hidden_inode->i_mapping->a_ops; + } + + /* all well, copy inode attributes */ + fist_copy_attr_all(inode, hidden_inode); + + skip: + /* only (our) lookup wants to do a d_add */ + switch (flag) { + case INTERPOSE_DEFAULT: + case INTERPOSE_REVAL_NEG: + d_instantiate(dentry, inode); + break; + case INTERPOSE_LOOKUP: + err = PTR_ERR(d_splice_alias(inode, dentry)); + break; + case INTERPOSE_REVAL: + /* Do nothing. */ + break; + default: + printk(KERN_ERR "Invalid interpose flag passed!"); + BUG(); + } + + fist_print_dentry("Leaving unionfs_interpose", dentry); + fist_print_inode("Leaving unionfs_interpose", inode); + up(&inode->i_sem); + + out: + print_exit_status(err); + return err; +} + +void unionfs_reinterpose(struct dentry *dentry) +{ + struct dentry *hidden_dentry; + struct inode *inode; + int bindex, bstart, bend; + + print_entry_location(); + verify_locked(dentry); + fist_print_dentry("IN: unionfs_reinterpose: ", dentry); + + /* This is pre-allocated inode */ + inode = dentry->d_inode; + + bstart = dbstart(dentry); + bend = dbend(dentry); + for (bindex = bstart; bindex <= bend; bindex++) { + hidden_dentry = dtohd_index(dentry, bindex); + if (!hidden_dentry) + continue; + + if (!hidden_dentry->d_inode) + continue; + if (itohi_index(inode, bindex)) + continue; + set_itohi_index(inode, bindex, IGRAB(hidden_dentry->d_inode)); + } + ibstart(inode) = dbstart(dentry); + ibend(inode) = dbend(dentry); + + fist_print_dentry("OUT: unionfs_reinterpose: ", dentry); + fist_print_inode("OUT: unionfs_reinterpose: ", inode); + + print_exit_location(); +} + +int check_branch(struct nameidata *nd) +{ + if (!strcmp(nd->dentry->d_sb->s_type->name, "unionfs")) + 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 hidden_dentries have overlapping branches */ +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; + } + if (dent == dent1) { + return 1; + } + + return 0; +} +static int parse_branch_mode(char *name) +{ + int perms; + int l = strlen(name); + if (!strcmp(name + l - 3, "=ro")) { + perms = MAY_READ; + name[l - 3] = '\0'; + } else if (!strcmp(name + l - 6, "=nfsro")) { + perms = MAY_READ | MAY_NFSRO; + name[l - 6] = '\0'; + } else if (!strcmp(name + l - 3, "=rw")) { + perms = MAY_READ | MAY_WRITE; + name[l - 3] = '\0'; + } else { + perms = MAY_READ | MAY_WRITE; + } + + return perms; +} + +static int parse_dirs_option(struct super_block *sb, struct unionfs_dentry_info + *hidden_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 = NULL; + struct dentry *dent2 = NULL; + + if (options[0] == '\0') { + printk(KERN_WARNING "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 hidden dentry */ + if (!(stopd(sb)->usi_data = alloc_new_data(branches))) { + err = -ENOMEM; + goto out; + } + + if (!(hidden_root_info->udi_dentry = alloc_new_dentries(branches))) { + err = -ENOMEM; + goto out; + } + + /* now parsing the string b1:b2=rw:b3=ro:b4 */ + branches = 0; + while ((name = strsep(&options, ":")) != NULL) { + int perms; + + if (!*name) + continue; + + branches++; + + /* strip off =rw or =ro if it is specified. */ + perms = parse_branch_mode(name); + if (!bindex && !(perms & MAY_WRITE)) { + err = -EINVAL; + goto out; + } + + fist_dprint(4, "unionfs: using directory: %s (%c%c%c)\n", + name, perms & MAY_READ ? 'r' : '-', + perms & MAY_WRITE ? 'w' : '-', + perms & MAY_NFSRO ? 'n' : '-'); + + err = path_lookup(name, LOOKUP_FOLLOW, &nd); + RECORD_PATH_LOOKUP(&nd); + if (err) { + printk(KERN_WARNING "unionfs: error accessing " + "hidden directory '%s' (error %d)\n", name, err); + goto out; + } + + if ((err = check_branch(&nd))) { + printk(KERN_WARNING "unionfs: hidden directory " + "'%s' is not a valid branch\n", name); + path_release(&nd); + RECORD_PATH_RELEASE(&nd); + goto out; + } + + hidden_root_info->udi_dentry[bindex] = nd.dentry; + + set_stohiddenmnt_index(sb, bindex, nd.mnt); + set_branchperms(sb, bindex, perms); + set_branch_count(sb, bindex, 0); + + if (hidden_root_info->udi_bstart < 0) + hidden_root_info->udi_bstart = bindex; + hidden_root_info->udi_bend = bindex; + bindex++; + } + + if (branches == 0) { + printk(KERN_WARNING "unionfs: no branches specified\n"); + err = -EINVAL; + goto out; + } + + BUG_ON(branches != (hidden_root_info->udi_bend + 1)); + + /* ensure that no overlaps exist in the branches */ + for (i = 0; i < branches; i++) { + for (j = i + 1; j < branches; j++) { + dent1 = hidden_root_info->udi_dentry[i]; + dent2 = hidden_root_info->udi_dentry[j]; + + if (is_branch_overlap(dent1, dent2)) { + goto out_overlap; + } + } + } + + out_overlap: + + if (i != branches) { + printk(KERN_WARNING "unionfs: branches %d and %d overlap\n", i, + j); + err = -EINVAL; + goto out; + } + + out: + if (err) { + KFREE(hidden_root_info->udi_dentry); + KFREE(stopd(sb)->usi_data); + + /* MUST clear the pointers to prevent potential double free if + * the caller dies later on + */ + hidden_root_info->udi_dentry = NULL; + stopd(sb)->usi_data = NULL; + } + return err; +} + +/* + * Parse mount options. See the manual page for usage instructions. + * + * Returns the dentry object of the lower-level (hidden) directory; + * We want to mount our stackable file system on top of that hidden directory. + * + * Sets default debugging level to N, if any. + */ +static struct unionfs_dentry_info *unionfs_parse_options(struct super_block *sb, + char *options) +{ + struct unionfs_dentry_info *hidden_root_info; + char *optname; + int err = 0; + int bindex; + int dirsfound = 0; +#ifdef UNIONFS_IMAP + int imapfound = 0; +#endif + print_entry_location(); + + /* allocate private data area */ + err = -ENOMEM; + hidden_root_info = + KZALLOC(sizeof(struct unionfs_dentry_info), GFP_KERNEL); + if (!hidden_root_info) + goto out_error; + hidden_root_info->udi_bstart = -1; + hidden_root_info->udi_bend = -1; + hidden_root_info->udi_bopaque = -1; + + while ((optname = strsep(&options, ",")) != NULL) { + char *optarg; + char *endptr; + int intval; + + if (!*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("unionfs: %s requires an argument.\n", optname); + err = -EINVAL; + goto out_error; + } + + if (!strcmp("dirs", optname)) { + if (++dirsfound > 1) { + printk(KERN_WARNING + "unionfs: multiple dirs specified\n"); + err = -EINVAL; + goto out_error; + } + err = parse_dirs_option(sb, hidden_root_info, optarg); + if (err) + goto out_error; + continue; + } +#ifdef UNIONFS_IMAP + if (!strcmp("imap", optname)) { + if (++imapfound > 1) { + printk(KERN_WARNING + "unionfs: multiple imap specified\n"); + err = -EINVAL; + goto out_error; + } + err = parse_imap_option(sb, hidden_root_info, optarg); + if (err) + goto out_error; + continue; + } +#endif + if (!strcmp("delete", optname)) { + if (!strcmp("whiteout", optarg)) { + /* default */ +#ifdef UNIONFS_DELETE_ALL + } else if (!strcmp("all", optarg)) { + MOUNT_FLAG(sb) |= DELETE_ALL; +#endif + } else { + printk(KERN_WARNING + "unionfs: invalid delete option '%s'\n", + optarg); + err = -EINVAL; + goto out_error; + } + continue; + } + if (!strcmp("copyup", optname)) { + if (!strcmp("preserve", optarg)) { + /* default */ + } else if (!strcmp("currentuser", optarg)) { + MOUNT_FLAG(sb) |= COPYUP_CURRENT_USER; + } else { + printk(KERN_WARNING + "unionfs: could not parse copyup option value '%s'\n", + optarg); + err = -EINVAL; + goto out_error; + } + continue; + } + + /* All of these options require an integer argument. */ + intval = simple_strtoul(optarg, &endptr, 0); + if (*endptr) { + printk(KERN_WARNING + "unionfs: invalid %s option '%s'\n", + optname, optarg); + err = -EINVAL; + goto out_error; + } + + if (!strcmp("debug", optname)) { + fist_set_debug_value(intval); + continue; + } + + err = -EINVAL; + printk(KERN_WARNING + "unionfs: unrecognized option '%s'\n", optname); + goto out_error; + } + if (dirsfound != 1) { + printk(KERN_WARNING "unionfs: dirs option required\n"); + err = -EINVAL; + goto out_error; + } + goto out; + + out_error: + for (bindex = hidden_root_info->udi_bstart; + bindex >= 0 && bindex <= hidden_root_info->udi_bend; bindex++) { + struct dentry *d; + d = hidden_root_info->udi_dentry[bindex]; + DPUT(d); + if (stohiddenmnt_index(sb, bindex)) + mntput(stohiddenmnt_index(sb, bindex)); + } + KFREE(hidden_root_info->udi_dentry); + KFREE(hidden_root_info); + + KFREE(stopd(sb)->usi_data); + stopd(sb)->usi_data = NULL; + + hidden_root_info = ERR_PTR(err); + out: + print_exit_location(); + return hidden_root_info; +} + +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 (ret) { + ret->d_op = &unionfs_dops; + ret->d_sb = sb; + ret->d_parent = ret; + } + } + return ret; +} + +static int unionfs_read_super(struct super_block *sb, void *raw_data, + int silent) +{ + int err = 0; + + struct unionfs_dentry_info *hidden_root_info = NULL; + int bindex, bstart, bend; + unsigned long long maxbytes; + + print_entry_location(); + + if (!raw_data) { + printk(KERN_WARNING + "unionfs_read_super: missing data argument\n"); + err = -EINVAL; + goto out; + } + + /* + * Allocate superblock private data + */ + stopd_lhs(sb) = KZALLOC(sizeof(struct unionfs_sb_info), GFP_KERNEL); + if (!stopd(sb)) { + printk(KERN_WARNING "%s: out of memory\n", __FUNCTION__); + err = -ENOMEM; + goto out; + } + stopd(sb)->b_end = -1; + atomic_set(&stopd(sb)->usi_generation, 1); + init_rwsem(&stopd(sb)->usi_rwsem); + + hidden_root_info = unionfs_parse_options(sb, raw_data); + if (IS_ERR(hidden_root_info)) { + printk(KERN_WARNING + "unionfs_read_super: error while parsing options (err = %ld)\n", + PTR_ERR(hidden_root_info)); + err = PTR_ERR(hidden_root_info); + hidden_root_info = NULL; + goto out_free; + } + if (hidden_root_info->udi_bstart == -1) { + err = -ENOENT; + goto out_free; + } + + /* set the hidden superblock field of upper superblock */ + bstart = hidden_root_info->udi_bstart; + BUG_ON(bstart != 0); + sbend(sb) = bend = hidden_root_info->udi_bend; + for (bindex = bstart; bindex <= bend; bindex++) { + struct dentry *d; + + d = hidden_root_info->udi_dentry[bindex]; + + set_stohs_index(sb, bindex, d->d_sb); + } + + /* Unionfs: Max Bytes is the maximum bytes from among all the branches */ + maxbytes = -1; + for (bindex = bstart; bindex <= bend; bindex++) + if (maxbytes < stohs_index(sb, bindex)->s_maxbytes) + maxbytes = stohs_index(sb, bindex)->s_maxbytes; + sb->s_maxbytes = maxbytes; + + sb->s_op = &unionfs_sops; + sb->s_export_op = &unionfs_export_ops; + + /* + * 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 + */ + sb->s_root = unionfs_d_alloc_root(sb); + if (!sb->s_root) { + err = -ENOMEM; + goto out_dput; + } + + /* link the upper and lower dentries */ + dtopd_lhs(sb->s_root) = NULL; + if ((err = new_dentry_private_data(sb->s_root))) + goto out_freedpd; + + /* Set the hidden dentries for s_root */ + for (bindex = bstart; bindex <= bend; bindex++) { + struct dentry *d; + + d = hidden_root_info->udi_dentry[bindex]; + + set_dtohd_index(sb->s_root, bindex, d); + } + set_dbstart(sb->s_root, bstart); + set_dbend(sb->s_root, bend); + + /* Set the generation number to one, since this is for the mount. */ + atomic_set(&dtopd(sb->s_root)->udi_generation, 1); + + /* call interpose to create the upper level inode */ + if ((err = unionfs_interpose(sb->s_root, sb, 0))) + goto out_freedpd; + unlock_dentry(sb->s_root); + goto out; + + out_freedpd: + if (dtopd(sb->s_root)) { + KFREE(dtohd_ptr(sb->s_root)); + free_dentry_private_data(dtopd(sb->s_root)); + } + DPUT(sb->s_root); + out_dput: + if (hidden_root_info && !IS_ERR(hidden_root_info)) { + for (bindex = hidden_root_info->udi_bstart; + bindex <= hidden_root_info->udi_bend; bindex++) { + struct dentry *d; + + d = hidden_root_info->udi_dentry[bindex]; + + if (d) + DPUT(d); + + if (stopd(sb) && stohiddenmnt_index(sb, bindex)) + mntput(stohiddenmnt_index(sb, bindex)); + } + KFREE(hidden_root_info->udi_dentry); + KFREE(hidden_root_info); + hidden_root_info = NULL; + } + out_free: + KFREE(stopd(sb)->usi_data); + KFREE(stopd(sb)); + stopd_lhs(sb) = NULL; + out: + if (hidden_root_info && !IS_ERR(hidden_root_info)) { + KFREE(hidden_root_info->udi_dentry); + KFREE(hidden_root_info); + } + print_exit_status(err); + return err; +} + +static struct super_block *unionfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *raw_data) +{ + return get_sb_nodev(fs_type, flags, raw_data, unionfs_read_super); +} + +void unionfs_kill_block_super(struct super_block *sb) +{ + generic_shutdown_super(sb); +} + +/* Compat..it is simpler to have it here, than to duplicate the code in + unionfs_kzalloc and KZALLOC non-debug macro */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14) +void *kzalloc(size_t size, gfp_t flags) +{ + void *ret = kmalloc(size, flags); + if (ret) + memset(ret, 0, size); + return ret; +} +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14) */ + +static struct file_system_type unionfs_fs_type = { + .owner = THIS_MODULE, + .name = "unionfs", + .get_sb = unionfs_get_sb, + .kill_sb = unionfs_kill_block_super, + .fs_flags = FS_REVAL_DOT, +}; + +static int init_debug = 0; +module_param_named(debug, init_debug, int, 0444); +MODULE_PARM_DESC(debug, "Initial Unionfs debug value."); + +static int __init init_unionfs_fs(void) +{ + int err; + printk("Registering unionfs " UNIONFS_VERSION "\n"); + + fist_set_debug_value(init_debug); + +#ifdef FIST_MALLOC_DEBUG + atomic_set(&unionfs_malloc_counter, 0); + atomic_set(&unionfs_mallocs_outstanding, 0); +#endif /* FIST_MALLOC_DEBUG */ + + if ((err = init_filldir_cache())) + goto out; + if ((err = init_inode_cache())) + goto out; + if ((err = init_dentry_cache())) + goto out; + err = register_filesystem(&unionfs_fs_type); + out: + if (err) { + destroy_filldir_cache(); + destroy_inode_cache(); + destroy_dentry_cache(); + } + return err; +} +static void __exit exit_unionfs_fs(void) +{ + destroy_filldir_cache(); + destroy_inode_cache(); + destroy_dentry_cache(); + unregister_filesystem(&unionfs_fs_type); + printk("Completed unionfs module unload.\n"); +} + +MODULE_AUTHOR + ("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); +/* + * + * vim:shiftwidth=8 + * vim:tabstop=8 + * + * For Emacs: + * Local variables: + * c-basic-offset: 8 + * c-comment-only-line-offset: 0 + * c-offsets-alist: ((statement-block-intro . +) (knr-argdecl-intro . 0) + * (substatement-open . 0) (label . 0) (statement-cont . +)) + * indent-tabs-mode: t + * tab-width: 8 + * End: + */ diff -urN linux-2.6.17-rc1-mm3-orig/fs/unionfs/malloc_debug.c linux-2.6.17-rc1-mm3-new/fs/unionfs/malloc_debug.c --- linux-2.6.17-rc1-mm3-orig/fs/unionfs/malloc_debug.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/unionfs/malloc_debug.c 2006-04-11 20:24:21.000000000 +0000 @@ -0,0 +1,199 @@ +#ifdef FIST_MALLOC_DEBUG + +#include "unionfs.h" + +/* for malloc debugging */ +atomic_t unionfs_malloc_counter = ATOMIC_INIT(0); +atomic_t unionfs_mallocs_outstanding = ATOMIC_INIT(0); +atomic_t unionfs_dget_counter = ATOMIC_INIT(0); +atomic_t unionfs_dgets_outstanding = ATOMIC_INIT(0); +atomic_t unionfs_iget_counter = ATOMIC_INIT(0); +atomic_t unionfs_igets_outstanding = ATOMIC_INIT(0); + +void *unionfs_kzalloc(size_t size, gfp_t flags, int line, const char *file) +{ + void *ptr = kzalloc(size, flags); + if (ptr) { + atomic_inc(&unionfs_malloc_counter); + atomic_inc(&unionfs_mallocs_outstanding); + printk("KZA:%d:%d:%p:%d:%s\n", + atomic_read(&unionfs_malloc_counter), + atomic_read(&unionfs_mallocs_outstanding), ptr, line, + file); + } + return ptr; +} +void *unionfs_kmalloc(size_t size, gfp_t flags, int line, const char *file) +{ + void *ptr = kmalloc(size, flags); + if (ptr) { + atomic_inc(&unionfs_malloc_counter); + atomic_inc(&unionfs_mallocs_outstanding); + printk("KM:%d:%d:%p:%d:%s\n", + atomic_read(&unionfs_malloc_counter), + atomic_read(&unionfs_mallocs_outstanding), ptr, line, + file); + } + return ptr; +} + +void unionfs_kfree(void *ptr, int line, const char *file) +{ + atomic_inc(&unionfs_malloc_counter); + if (ptr) { + BUG_ON(IS_ERR(ptr)); + atomic_dec(&unionfs_mallocs_outstanding); + } + printk("KF:%d:%d:%p:%d:%s\n", atomic_read(&unionfs_malloc_counter), + atomic_read(&unionfs_mallocs_outstanding), ptr, line, file); + kfree(ptr); +} + +void record_set(struct dentry *upper, int index, struct dentry *ptr, + struct dentry *old, int line, const char *file) +{ + atomic_inc(&unionfs_dget_counter); + printk("DD:%d:%d:%d:%p:%d:%s %p, %d\n", + atomic_read(&unionfs_dget_counter), + atomic_read(&unionfs_dgets_outstanding), + old ? atomic_read(&old->d_count) : 0, old, line, file, upper, + index); + atomic_inc(&unionfs_dget_counter); + printk("DS:%d:%d:%d:%p:%d:%s %p, %d\n", + atomic_read(&unionfs_dget_counter), + atomic_read(&unionfs_dgets_outstanding), + ptr ? atomic_read(&ptr->d_count) : 0, ptr, line, file, upper, + index); +} + +void record_path_lookup(struct nameidata *nd, int line, const char *file) +{ + struct dentry *ptr = nd->dentry; + if (ptr) { + atomic_inc(&unionfs_dget_counter); + atomic_inc(&unionfs_dgets_outstanding); + printk("DL:%d:%d:%d:%p:%d:%s\n", + atomic_read(&unionfs_dget_counter), + atomic_read(&unionfs_dgets_outstanding), + atomic_read(&ptr->d_count), ptr, line, file); + } +} + +void record_path_release(struct nameidata *nd, int line, const char *file) +{ + struct dentry *ptr = nd->dentry; + + atomic_inc(&unionfs_dget_counter); + if (ptr) + atomic_dec(&unionfs_dgets_outstanding); + printk("DP:%d:%d:%d:%p:%d:%s\n", atomic_read(&unionfs_dget_counter), + atomic_read(&unionfs_dgets_outstanding), + ptr ? atomic_read(&ptr->d_count) : 0, ptr, line, file); +} + +struct file *unionfs_dentry_open(struct dentry *ptr, struct vfsmount *mnt, + int flags, int line, const char *file) +{ + atomic_inc(&unionfs_dget_counter); + if (ptr) + atomic_dec(&unionfs_dgets_outstanding); + printk("DO:%d:%d:%d:%p:%d:%s\n", atomic_read(&unionfs_dget_counter), + atomic_read(&unionfs_dgets_outstanding), + ptr ? atomic_read(&ptr->d_count) : 0, ptr, line, file); + return dentry_open(ptr, mnt, flags); +} + +struct dentry *unionfs_dget(struct dentry *ptr, int line, const char *file) +{ + ptr = dget(ptr); + if (ptr) { + atomic_inc(&unionfs_dget_counter); + atomic_inc(&unionfs_dgets_outstanding); + printk("DG:%d:%d:%d:%p:%d:%s\n", + atomic_read(&unionfs_dget_counter), + atomic_read(&unionfs_dgets_outstanding), + atomic_read(&ptr->d_count), ptr, line, file); + } + return ptr; +} + +struct dentry *unionfs_dget_parent(struct dentry *child, int line, + const char *file) +{ + struct dentry *ptr; + + ptr = dget_parent(child); + atomic_inc(&unionfs_dget_counter); + atomic_inc(&unionfs_dgets_outstanding); + printk("DG:%d:%d:%d:%p:%d:%s\n", + atomic_read(&unionfs_dget_counter), + atomic_read(&unionfs_dgets_outstanding), + atomic_read(&ptr->d_count), ptr, line, file); + + return ptr; +} + +struct dentry *unionfs_lookup_one_len(const char *name, struct dentry *parent, + int len, int line, const char *file) +{ + struct dentry *ptr = lookup_one_len(name, parent, len); + if (ptr && !IS_ERR(ptr)) { + atomic_inc(&unionfs_dget_counter); + atomic_inc(&unionfs_dgets_outstanding); + printk("DL:%d:%d:%d:%p:%d:%s\n", + atomic_read(&unionfs_dget_counter), + atomic_read(&unionfs_dgets_outstanding), + atomic_read(&ptr->d_count), ptr, line, file); + } + return ptr; +} + +void unionfs_dput(struct dentry *ptr, int line, const char *file) +{ + atomic_inc(&unionfs_dget_counter); + if (ptr) { + BUG_ON(IS_ERR(ptr)); + atomic_dec(&unionfs_dgets_outstanding); + } + printk("DP:%d:%d:%d:%p:%d:%s\n", atomic_read(&unionfs_dget_counter), + atomic_read(&unionfs_dgets_outstanding), + ptr ? atomic_read(&ptr->d_count) : 0, ptr, line, file); + dput(ptr); +} + +struct inode *unionfs_igrab(struct inode *inode, int line, char *file) +{ + atomic_inc(&unionfs_iget_counter); + if (inode) + atomic_inc(&unionfs_igets_outstanding); + printk("IR:%d:%d:%d:%p:%d:%s\n", atomic_read(&unionfs_iget_counter), + atomic_read(&unionfs_igets_outstanding), + inode ? atomic_read(&inode->i_count) : 0, inode, line, file); + return igrab(inode); +} + +void unionfs_iput(struct inode *inode, int line, char *file) +{ + atomic_inc(&unionfs_iget_counter); + if (inode) + atomic_dec(&unionfs_igets_outstanding); + printk("IP:%d:%d:%d:%p:%d:%s\n", atomic_read(&unionfs_iget_counter), + atomic_read(&unionfs_igets_outstanding), + inode ? atomic_read(&inode->i_count) : 0, inode, line, file); + iput(inode); +} + +struct inode *unionfs_iget(struct super_block *sb, unsigned long ino, int line, + char *file) +{ + struct inode *inode = iget(sb, ino); + atomic_inc(&unionfs_iget_counter); + if (inode) + atomic_inc(&unionfs_igets_outstanding); + printk("IG:%d:%d:%d:%p:%d:%s\n", atomic_read(&unionfs_iget_counter), + atomic_read(&unionfs_igets_outstanding), + inode ? atomic_read(&inode->i_count) : 0, inode, line, file); + return inode; +} + +#endif /* FIST_MALLOC_DEBUG */ diff -urN linux-2.6.17-rc1-mm3-orig/fs/unionfs/persistent_inode.c linux-2.6.17-rc1-mm3-new/fs/unionfs/persistent_inode.c --- linux-2.6.17-rc1-mm3-orig/fs/unionfs/persistent_inode.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/unionfs/persistent_inode.c 2006-04-11 20:24:21.000000000 +0000 @@ -0,0 +1,662 @@ +/* + * Copyright (c) 2003-2006 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2006 Josef Sipek + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2005-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2006 Stony Brook University + * Copyright (c) 2003-2006 The Research Foundation of State University of New York + * + * For specific licensing information, see the COPYING file distributed with + * this package. + * + * This Copyright notice must be kept intact and distributed with all sources. + */ +/* + * $Id: persistent_inode.c,v 1.33 2006/02/13 00:18:40 jsipek Exp $ + */ +#ifdef UNIONFS_IMAP + +#include "unionfs.h" + +static ssize_t __fread(struct file *filp, void *buf, size_t size, loff_t * pos) +{ + int err; + mm_segment_t oldfs; + ssize_t(*func) (struct file *, char __user *, size_t, loff_t *); + + func = do_sync_read; + if (filp->f_op && filp->f_op->read) + func = filp->f_op->read; + + oldfs = get_fs(); + set_fs(KERNEL_DS); + do { + err = func(filp, (char __user *)buf, size, pos); + } while (err == -EAGAIN || err == -EINTR); + set_fs(oldfs); + return err; +} + +static ssize_t __fwrite(struct file *filp, void *buf, size_t size, loff_t * pos) +{ + int err; + mm_segment_t oldfs; + unsigned long flim; + struct rlimit *rl; + ssize_t(*func) (struct file *, const char __user *, size_t, loff_t *); + + func = do_sync_write; + if (filp->f_op && filp->f_op->write) + func = filp->f_op->write; + + /* + * it breaks RLIMIT_FSIZE, + * but users should be careful to quota. + */ + rl = current->signal->rlim + RLIMIT_FSIZE; + flim = rl->rlim_cur; + rl->rlim_cur = RLIM_INFINITY; + oldfs = get_fs(); + set_fs(KERNEL_DS); + do { + err = func(filp, (const char __user *)buf, size, pos); + } while (err == -EAGAIN || err == -EINTR); + set_fs(oldfs); + rl->rlim_cur = flim; + return err; +} + +/* + * verify_forwardmap(super_block *sb) + * sb: pointer to a superblock containing the forwardmap. + * returns: 0 on success EINVAL or ENOMEM on failure; + */ +static int verify_forwardmap(struct super_block *sb) +{ + int err = 0, bytesread = 0, bindex = 0, mallocsize = 0; + loff_t readpos = 0; + struct file *forwardmap = NULL; + struct fmaphdr header; + struct unionfs_sb_info *spd = NULL; + print_entry_location(); + + spd = stopd(sb); + BUG_ON(!spd); + + forwardmap = spd->usi_forwardmap; + if (!forwardmap) { + err = -EINVAL; + goto out; + } + bytesread = __fread(forwardmap, &header, sizeof(struct fmaphdr), + &readpos); + if (bytesread < sizeof(struct fmaphdr)) { + err = -EINVAL; + goto out; + } + if (header.magic != FORWARDMAP_MAGIC + || header.version != FORWARDMAP_VERSION) { + err = -EINVAL; + goto out; + } + spd->usi_bmap = + KMALLOC(sizeof(struct bmapent) * header.usedbranches, GFP_KERNEL); + + if (!spd->usi_bmap) { + err = -ENOMEM; + goto out; + } + + while (bindex < header.usedbranches) { + bytesread = __fread(forwardmap, &stopd(sb)->usi_bmap[bindex], + sizeof(struct bmapent), &readpos); + if (bytesread < sizeof(struct bmapent)) { + err = -EINVAL; + goto out_err; + } + bindex++; + } + + mallocsize = sizeof(int) * header.usedbranches; + goto out; + out_err: + if (spd->usi_bmap) + KFREE(spd->usi_bmap); + out: + print_exit_status(err); + return err; +} + +/* + * verify_reversemap(struct super_block sb, int rmapindex) + * + * sb: The unionfs superblock containing all of the current imap info + * rmapindex: the index in the usi_reversemaps array that we wish to + * verify + * + * Assumes the reverse maps less than rmapindex are valid. + * + * returns: 0 if the opperation succeds + * -EINVAL if the map file does not belong to the forward map + * + */ +static int verify_reversemap(struct super_block *sb, int rmapindex, + struct unionfs_dentry_info *hidden_root_info) +{ + int err = 0, i = 0, bindex = 0, found = 0, bytesread; + loff_t readpos = 0; + struct file *forwardmap, *reversemap; + struct fmaphdr fheader; + struct rmaphdr rheader; + struct kstatfs st; + struct unionfs_sb_info *spd = NULL; + + print_entry_location(); + + spd = stopd(sb); + BUG_ON(!spd); + + forwardmap = spd->usi_forwardmap; + if (!forwardmap) { + err = -EINVAL; + goto out; + } + reversemap = spd->usi_reversemaps[rmapindex]; + if (!reversemap) { + err = -EINVAL; + goto out; + } + bytesread = __fread(forwardmap, &fheader, sizeof(struct fmaphdr), + &readpos); + if (bytesread < sizeof(struct fmaphdr)) { + err = -EINVAL; + goto out; + } + readpos = 0; + bytesread = __fread(reversemap, &rheader, sizeof(struct rmaphdr), + &readpos); + if (bytesread < sizeof(struct rmaphdr)) { + err = -EINVAL; + goto out; + } + if (rheader.magic != REVERSEMAP_MAGIC + || rheader.version != REVERSEMAP_VERSION) { + err = -EINVAL; + goto out; + } + if (memcmp(fheader.uuid, rheader.fwduuid, sizeof(fheader.uuid))) { + err = -EINVAL; + goto out; + } + + /* XXX: Ok so here we take the new map and read the fsid from it. Then + * we go through all the branches in the union and see which ones it + * matches with*/ + for (i = 0; i < spd->usi_num_bmapents && !found; i++) { + if (memcmp + (rheader.revuuid, spd->usi_bmap[i].uuid, + sizeof(rheader.revuuid))) + continue; + + found = 1; + for (bindex = 0; bindex <= hidden_root_info->udi_bend; bindex++) { + struct dentry *d; + fsid_t fsid; + dev_t dev; + memset(&st, 0, sizeof(struct kstatfs)); + + d = hidden_root_info->udi_dentry[bindex]; + + err = d->d_sb->s_op->statfs(d->d_sb, &st); + if (err) + goto out; + + if (st.f_fsid.val[0] || st.f_fsid.val[1]) { + fsid = st.f_fsid; + } else { + + dev = d->d_sb->s_dev; + fsid.val[0] = MAJOR(dev); + fsid.val[1] = MINOR(dev); + } + + if (memcmp(&fsid, &rheader.fsid, sizeof(fsid))) + continue; + + if (spd->usi_bnum_table[bindex] == -1) + spd->usi_bnum_table[bindex] = i; + if (spd->usi_map_table[bindex]) { + printk(KERN_WARNING + "Two reverse maps share fsid %u%u!\n", + rheader.fsid.val[0], + rheader.fsid.val[1]); + err = -EINVAL; + goto out; + } else { + spd->usi_map_table[bindex] = reversemap; + } + } + } + if (!found) { + printk(KERN_WARNING + "Could not match the reversemap uuid with an entry in the forwardmap table\n"); + err = -EINVAL; + } + out: + print_exit_status(err); + return err; +} + +int init_imap_data(struct super_block *sb, + struct unionfs_dentry_info *hidden_root_info) +{ + int i, err = 0, mallocsize = 0; + struct unionfs_sb_info *spd; + + print_entry_location(); + + spd = stopd(sb); + + spd->usi_forwardmap = NULL; + spd->usi_reversemaps = NULL; + spd->usi_bnum_table = NULL; + + mallocsize = sizeof(struct file *) * (hidden_root_info->udi_bend + 1); + spd->usi_reversemaps = KZALLOC(mallocsize, GFP_KERNEL); + if (!spd->usi_reversemaps) { + err = -ENOMEM; + goto out_error; + } + + spd->usi_map_table = KZALLOC(mallocsize, GFP_KERNEL); + if (!spd->usi_map_table) { + err = -ENOMEM; + goto out_error; + } + + mallocsize = sizeof(int) * (hidden_root_info->udi_bend + 1); + spd->usi_bnum_table = KMALLOC(mallocsize, GFP_KERNEL); + if (!spd->usi_bnum_table) { + err = -ENOMEM; + goto out_error; + } + + for (i = 0; i <= hidden_root_info->udi_bend; i++) { + spd->usi_bnum_table[i] = -1; + } + + if (!err) + goto out; + out_error: + + if (spd->usi_reversemaps) { + KFREE(spd->usi_reversemaps); + spd->usi_reversemaps = NULL; + } + + if (spd->usi_map_table) { + KFREE(spd->usi_map_table); + spd->usi_map_table = NULL; + } + + if (spd->usi_bnum_table) { + KFREE(spd->usi_bnum_table); + spd->usi_bnum_table = NULL; + + } + + out: + print_exit_status(err); + return err; + +} + +void cleanup_imap_data(struct super_block *sb) +{ + int count = 0; + struct unionfs_sb_info *spd; + + print_entry_location(); + + spd = stopd(sb); + + spd->usi_persistent = 0; + count = spd->usi_num_bmapents; + while (count - 1 >= 0) { + if (spd->usi_reversemaps[count - 1]) { + filp_close(spd->usi_reversemaps[count - 1], NULL); + spd->usi_reversemaps[count - 1] = NULL; + } + count--; + } + if (spd->usi_reversemaps) { + KFREE(spd->usi_reversemaps); + spd->usi_reversemaps = NULL; + } + + if (spd->usi_map_table) { + KFREE(spd->usi_map_table); + spd->usi_map_table = NULL; + } + + if (spd->usi_bnum_table) { + KFREE(spd->usi_bnum_table); + spd->usi_bnum_table = NULL; + } + if (spd->usi_forwardmap) { + filp_close(spd->usi_forwardmap, NULL); + spd->usi_forwardmap = NULL; + } + print_exit_location(); +} + +int parse_imap_option(struct super_block *sb, + struct unionfs_dentry_info *hidden_root_info, + char *options) +{ + int count = 0, err = 0; + char *name; + struct unionfs_sb_info *spd = NULL; + + print_entry_location(); + spd = stopd(sb); + BUG_ON(!spd); + + err = init_imap_data(sb, hidden_root_info); + if (err) + goto out_error; + while ((name = strsep(&options, ":")) != NULL) { + if (!*name) + continue; + if (!spd->usi_forwardmap) { + spd->usi_forwardmap = filp_open(name, O_RDWR, 0); + if (IS_ERR(spd->usi_forwardmap)) { + err = PTR_ERR(spd->usi_forwardmap); + spd->usi_forwardmap = NULL; + goto out_error; + } + } else { + spd->usi_reversemaps[count] = + filp_open(name, O_RDWR, 0); + if (IS_ERR(spd->usi_reversemaps[count])) { + err = PTR_ERR(spd->usi_reversemaps[count]); + spd->usi_reversemaps[count] = NULL; + goto out_error; + + } + count++; + } + } + if (count <= 0) { + printk(KERN_WARNING "unionfs: no reverse maps specified.\n"); + err = -EINVAL; + } + if (err) + goto out_error; + + /* Initialize the super block's next_avail field */ + /* Dave, you can't use 64-bit division here because the i386 doesn't + * support it natively. Instead you need to punt if the size is + * greater than unsigned long, and then cast it down. Then you should + * be able to assign to this value, without having these problems. */ + + if (spd->usi_forwardmap->f_dentry->d_inode->i_size > ULONG_MAX) { + err = -EFBIG; + goto out_error; + } + spd->usi_next_avail = + ((unsigned long)(spd->usi_forwardmap->f_dentry->d_inode-> + i_size - (sizeof(struct fmaphdr) + + sizeof(struct bmapent[256]))) + / sizeof(struct fmapent)); + + if (spd->usi_next_avail < FIRST_VALID_INODE) + spd->usi_next_avail = FIRST_VALID_INODE; + + spd->usi_num_bmapents = count; + err = verify_forwardmap(sb); + if (err) + goto out_error; + while (count > 0) { + err = verify_reversemap(sb, --count, hidden_root_info); + if (err) + goto out_error; + } + spd->usi_persistent = 1; + + goto out; + + out_error: + spd->usi_num_bmapents = count; + cleanup_imap_data(sb); + + out: + print_exit_status(err); + return err; +} + + /* + * get @ino from @hidden_ino. + */ +static int __read_uin(struct unionfs_sb_info *sbi, ino_t hidden_ino, int bindex, + ino_t * ino) +{ + int err; + struct file *rev; + loff_t pos; + ssize_t sz; + uint64_t ino64; + const int elmnt = sizeof(ino64); + + rev = sbi->usi_map_table[bindex]; + pos = sizeof(struct rmaphdr) + elmnt * hidden_ino; + *ino = 0; + err = 0; + if (pos + elmnt > rev->f_dentry->d_inode->i_size) + goto out; + + sz = __fread(rev, &ino64, elmnt, &pos); + err = sz; + if (err < 0) + goto out; + err = 0; + *ino = -1; + if (sz != elmnt || ino64 > *ino) + err = -EIO; + *ino = ino64; + out: + print_exit_status(err); + return err; +} + +/* + * put unionfs @ino for @hidden_ino on @bindex. + */ +static int __write_uin(struct unionfs_sb_info *sbi, ino_t ino, int bindex, + ino_t hidden_ino) +{ + struct file *fwd, *rev; + struct fmapent ent; + loff_t pos; + ssize_t sz; + int err; + uint64_t ino64; + const int fwdhdr = sizeof(struct fmaphdr) + sizeof(struct bmapent[256]); + const int fwd_elmnt = sizeof(ent); + const int rev_elmnt = sizeof(ino64); + + err = -ENOSPC; + if (ino < FIRST_VALID_INODE) + goto out; + + fwd = sbi->usi_forwardmap; + ent.fsnum = sbi->usi_bnum_table[bindex]; + ent.inode = ino; + pos = fwdhdr + fwd_elmnt * hidden_ino; + sz = __fwrite(fwd, &ent, fwd_elmnt, &pos); + err = sz; + if (err < 0) + goto out; + err = -EIO; + if (sz != fwd_elmnt) + goto out; + + rev = sbi->usi_map_table[bindex]; + pos = sizeof(struct rmaphdr) + rev_elmnt * hidden_ino; + ino64 = ino; + sz = __fwrite(rev, &ino64, rev_elmnt, &pos); + err = sz; + if (err < 0) + goto out; + err = 0; + if (sz != rev_elmnt) + err = -EIO; + out: + print_exit_status(err); + return err; +} + +/* + * read_uin(struct super_block *sb, uint8_t branchnum, ino_t inode_number, int flag, ino_t *uino) + * fsnum: branch to reference when getting the inode number + * inode_number: lower level inode number use to reference the proper inode. + * flag: if set to O_CREAT it will creat the entry if it doesent exist + * otherwise it will return the existing one. + * returns: the unionfs inode number either created or retrieved based on + * the information. + */ +int read_uin(struct super_block *sb, uint8_t branchnum, ino_t inode_number, + int flag, ino_t * uino) +{ + int err = 0; + struct unionfs_sb_info *spd; + + print_entry_location(); + + spd = stopd(sb); + BUG_ON(!spd); + + /* Find appropriate reverse map and then read from the required position */ + /* get it from the array. */ + err = __read_uin(spd, inode_number, branchnum, uino); + if (err || *uino) + goto out; + + err = -EIO; + if (!(flag & O_CREAT)) + goto out; + + /* If we haven't found an entry and we have the O_CREAT flag set we want to + * create a new entry write it out to the file and return its index + */ + *uino = spd->usi_next_avail++; + down(&sb->s_lock); + err = __write_uin(spd, *uino, branchnum, inode_number); + if (err) + spd->usi_next_avail--; + up(&sb->s_lock); + out: + print_exit_status(err); + return err; +} + +int write_uin(struct super_block *sb, ino_t ino, int bindex, ino_t hidden_ino) +{ + int err; + + print_entry_location(); + err = __write_uin(stopd(sb), ino, bindex, hidden_ino); + print_exit_status(err); + return err; +} + +/* + * get_lin(ino_t inode_number) + * inode_number : inode number for the unionfs inode + * returns: the lower level inode# and branch# + */ +/* entry should use a poiner on the stack. should be staticly allocated one + * level up*/ +int get_lin(struct super_block *sb, ino_t inode_number, struct fmapent *entry) +{ + struct file *forwardmap; + loff_t seek_size; + mm_segment_t oldfs; + int err = 0, bytesread = 0; + + print_entry_location(); + + if (!entry) { + entry = ERR_PTR(-ENOMEM); + goto out; + } + forwardmap = stopd(sb)->usi_forwardmap; + seek_size = + sizeof(struct fmaphdr) + sizeof(struct bmapent[256]) + + (sizeof(struct fmapent) * inode_number); + oldfs = get_fs(); + set_fs(KERNEL_DS); + bytesread = + forwardmap->f_op->read(forwardmap, (char __user *)entry, + sizeof(struct fmapent), &seek_size); + set_fs(oldfs); + if (bytesread != sizeof(struct fmapent)) { + entry = ERR_PTR(-EINVAL); + goto out; + } + + out: + print_exit_location(); + return err; +} + +/* + * remove_map(struct super_block *sb,int bindex) + * + * sb: The super block containing all the current imap info + * bindex: the index of the branch that is being removed. + * + * This assumes that end hasen't been decremented yet. + * + * Returns: This function really can't fail. The only thing + * that could possibly happen is that it will oops but that + * requires unionfs to be in an inconsistant state which + * shoulden't happen. + */ +int remove_map(struct super_block *sb, int bindex) +{ + int i; + struct unionfs_sb_info *spd; + + print_entry_location(); + + spd = stopd(sb); + BUG_ON(!spd); + + for (i = bindex; i < sbend(sb); i++) { + spd->usi_map_table[i] = spd->usi_map_table[i + 1]; + spd->usi_bnum_table[i] = spd->usi_bnum_table[i + 1]; + } + return 0; +} + +#endif +/* + * + * vim:shiftwidth=8 + * vim:tabstop=8 + * + * For Emacs: + * Local variables: + * c-basic-offset: 8 + * c-comment-only-line-offset: 0 + * c-offsets-alist: ((statement-block-intro . +) (knr-argdecl-intro . 0) + * (substatement-open . 0) (label . 0) (statement-cont . +)) + * indent-tabs-mode: t + * tab-width: 8 + * End: + */ diff -urN linux-2.6.17-rc1-mm3-orig/fs/unionfs/print.c linux-2.6.17-rc1-mm3-new/fs/unionfs/print.c --- linux-2.6.17-rc1-mm3-orig/fs/unionfs/print.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/unionfs/print.c 2006-04-11 20:24:21.000000000 +0000 @@ -0,0 +1,491 @@ +/* + * Copyright (c) 2003-2006 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2006 Josef Sipek + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2005-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2006 Stony Brook University + * Copyright (c) 2003-2006 The Research Foundation of State University of New York + * + * For specific licensing information, see the COPYING file distributed with + * this package. + * + * This Copyright notice must be kept intact and distributed with all sources. + */ +/* + * $Id: print.c,v 1.74 2006/01/13 03:00:24 jsipek Exp $ + */ + +/* Print debugging functions */ + +#ifndef UNIONFS_NDEBUG + +#include "unionfs.h" + +static int fist_debug_var = 0; + +/* get value of debugging variable */ +int fist_get_debug_value(void) +{ + return fist_debug_var; +} + +/* set debug level variable and return the previous value */ +int fist_set_debug_value(int val) +{ + int prev = fist_debug_var; + + fist_debug_var = val; + fist_dprint(1, "unionfs: setting debug level to %d\n", val); + return prev; +} + +/* + * Utilities used by both client and server + * Standard levels: + * 0) no debugging + * 1) hard failures + * 2) soft failures + * 3) current test software + * 4) main procedure entry points + * 5) main procedure exit points + * 6) utility procedure entry points + * 7) utility procedure exit points + * 8) obscure procedure entry points + * 9) obscure procedure exit points + * 10) random stuff + * 11) all <= 1 + * 12) all <= 2 + * 13) all <= 3 + * ... + */ + +void fist_dprint_internal(const char *file, const char *function, int line, + int level, char *str, ...) +{ + va_list ap; + int var = fist_get_debug_value(); + + if (level >= 10 || level < 0) { + printk(KERN_ERR "fist_dprint_internal: Invalid level passed" + "from %s:%s:%d\n", file, function, line); + } + + if (var == level || (var > 10 && (var - 10) >= level)) { + va_start(ap, str); + vprintk(str, ap); + va_end(ap); + } + return; +} + +static int num_indents = 0; +static char indent_buf[80] = + " "; +char *add_indent(void) +{ + indent_buf[num_indents] = ' '; + num_indents++; + if (num_indents > 79) + num_indents = 79; + indent_buf[num_indents] = '\0'; + return indent_buf; +} + +char *del_indent(void) +{ + if (num_indents <= 0) + return ""; + indent_buf[num_indents] = ' '; + num_indents--; + indent_buf[num_indents] = '\0'; + return indent_buf; +} + +static void fist_print_generic_inode3(const char *str, const char *str2, + const struct inode *inode) +{ + if (!inode) { + printk("PI:%s%s: NULL INODE PASSED!\n", str, str2); + return; + } + if (IS_ERR(inode)) { + printk("PI:%s%s: ERROR INODE PASSED: %ld\n", str, str2, + PTR_ERR(inode)); + return; + } + fist_dprint(8, "PI:%s%s: %s=%lu\n", str, str2, "i_ino", inode->i_ino); + fist_dprint(8, "PI:%s%s: %s=%u\n", str, str2, "i_count", + atomic_read(&inode->i_count)); + fist_dprint(8, "PI:%s%s: %s=%u\n", str, str2, "i_nlink", + inode->i_nlink); + fist_dprint(8, "PI:%s%s: %s=%o\n", str, str2, "i_mode", inode->i_mode); + fist_dprint(8, "PI:%s%s: %s=%llu\n", str, str2, "i_size", + inode->i_size); + fist_dprint(8, "PI:%s%s: %s=%p\n", str, str2, "i_op", inode->i_op); + fist_dprint(8, "PI:%s%s: %s=%p (%s)\n", str, str2, "i_sb", + inode->i_sb, (inode->i_sb ? sbt(inode->i_sb) : "NullTypeSB") + ); +} + +void fist_print_generic_inode(const char *str, const struct inode *inode) +{ + fist_print_generic_inode3(str, "", inode); +} + +void fist_print_inode(const char *str, const struct inode *inode) +{ + int bindex; + + if (!inode) { + printk("PI:%s: NULL INODE PASSED!\n", str); + return; + } + if (IS_ERR(inode)) { + printk("PI:%s: ERROR INODE PASSED: %ld\n", str, PTR_ERR(inode)); + return; + } + + if (strcmp("unionfs", sbt(inode->i_sb))) { + char msg[100]; + snprintf(msg, sizeof(msg), "Invalid inode passed to" + "fist_print_inode: %s\n", sbt(inode->i_sb)); + printk(KERN_ERR "%s\n", msg); + BUG(); + } + + fist_print_generic_inode(str, inode); + + if (!itopd(inode)) + return; + fist_dprint(8, "PI:%s: ibstart=%d, ibend=%d\n", str, + ibstart(inode), ibend(inode)); + + if (ibstart(inode) == -1) + return; + + for (bindex = ibstart(inode); bindex <= ibend(inode); bindex++) { + struct inode *hidden_inode = itohi_index(inode, bindex); + char newstr[10]; + if (!hidden_inode) { + fist_dprint(8, "PI:%s: HI#%d: NULL\n", str, bindex); + continue; + } + sprintf(newstr, ": HI%d", bindex); + fist_print_generic_inode3(str, newstr, hidden_inode); + } +} + +static void fist_print_generic_file3(const char *str, const char *str2, + const struct file *file) +{ + fist_dprint(8, "PF:%s%s: %s=0x%p\n", str, str2, "f_dentry", + file->f_dentry); + fist_dprint(8, "PF:%s%s: name=%s\n", str, str2, + file->f_dentry->d_name.name); + if (file->f_dentry->d_inode) { + fist_dprint(8, "PF:%s%s: %s=%lu\n", str, str2, + "f_dentry->d_inode->i_ino", + file->f_dentry->d_inode->i_ino); + fist_dprint(8, "PF:%s%s: %s=%o\n", str, str2, + "f_dentry->d_inode->i_mode", + file->f_dentry->d_inode->i_mode); + } + fist_dprint(8, "PF:%s%s: %s=0x%p\n", str, str2, "f_op", file->f_op); + fist_dprint(8, "PF:%s%s: %s=0x%x\n", str, str2, "f_mode", file->f_mode); + fist_dprint(8, "PF:%s%s: %s=0x%llu\n", str, str2, "f_pos", file->f_pos); + fist_dprint(8, "PF:%s%s: %s=%u\n", str, str2, "f_count", + atomic_read(&file->f_count)); + fist_dprint(8, "PF:%s%s: %s=0x%x\n", str, str2, "f_flags", + file->f_flags); + fist_dprint(8, "PF:%s%s: %s=%lu\n", str, str2, "f_version", + file->f_version); +} + +static void fist_print_generic_file(const char *str, const struct file *file) +{ + fist_print_generic_file3(str, "", file); +} + +void fist_print_file(const char *str, const struct file *file) +{ + struct file *hidden_file; + + if (!file) { + fist_dprint(8, "PF:%s: NULL FILE PASSED!\n", str); + return; + } + + if (strcmp("unionfs", sbt(file->f_dentry->d_sb))) { + char msg[100]; + snprintf(msg, sizeof(msg), "Invalid file passed to" + "fist_print_file: %s\n", sbt(file->f_dentry->d_sb)); + printk(KERN_ERR "%s\n", msg); + BUG(); + + } + + fist_print_generic_file(str, file); + + if (ftopd(file)) { + int bindex; + + fist_dprint(8, "PF:%s: fbstart=%d, fbend=%d\n", str, + fbstart(file), fbend(file)); + + for (bindex = fbstart(file); bindex <= fbend(file); bindex++) { + char newstr[10]; + hidden_file = ftohf_index(file, bindex); + if (!hidden_file) { + fist_dprint(8, "PF:%s: HF#%d is NULL\n", str, + bindex); + continue; + } + sprintf(newstr, ": HF%d", bindex); + fist_print_generic_file3(str, newstr, hidden_file); + } + } +} + +static char mode_to_type(mode_t mode) +{ + if (S_ISDIR(mode)) + return 'd'; + if (S_ISLNK(mode)) + return 'l'; + if (S_ISCHR(mode)) + return 'c'; + if (S_ISBLK(mode)) + return 'b'; + if (S_ISREG(mode)) + return 'f'; + return '?'; +} + +void __fist_print_dentry(const char *str, const struct dentry *dentry, + int check) +{ + if (!dentry) { + fist_dprint(8, "PD:%s: NULL DENTRY PASSED!\n", str); + return; + } + if (IS_ERR(dentry)) { + fist_dprint(8, "PD:%s: ERROR DENTRY (%ld)!\n", str, + PTR_ERR(dentry)); + return; + } + + if (strcmp("unionfs", sbt(dentry->d_sb))) { + char msg[100]; + snprintf(msg, sizeof(msg), "Invalid dentry passed to" + "fist_print_dentry: %s\n", sbt(dentry->d_sb)); + printk(KERN_ERR "%s\n", msg); + BUG(); + + } + + __fist_print_generic_dentry(str, "", dentry, check); + + if (!dtopd(dentry)) + return; + fist_dprint(8, "PD:%s: dbstart=%d, dbend=%d, dbopaque=%d\n", + str, dbstart(dentry), dbend(dentry), dbopaque(dentry)); + if (dbstart(dentry) != -1) { + int bindex; + char newstr[10]; + struct dentry *hidden_dentry; + + for (bindex = dbstart(dentry); bindex <= dbend(dentry); + bindex++) { + hidden_dentry = dtohd_index(dentry, bindex); + if (!hidden_dentry) { + fist_dprint(8, "PD:%s: HD#%d: NULL\n", str, + bindex); + continue; + } + sprintf(newstr, ": HD%d", bindex); + fist_print_generic_dentry3(str, newstr, hidden_dentry); + } + } +} +void fist_print_dentry(const char *str, const struct dentry *dentry) +{ + __fist_print_dentry(str, dentry, 1); +} + +void __fist_print_generic_dentry(const char *str, const char *str2, const + struct dentry *dentry, int check) +{ + if (!dentry) { + fist_dprint(8, "PD:%s%s: NULL DENTRY PASSED!\n", str, str2); + return; + } + if (IS_ERR(dentry)) { + fist_dprint(8, "PD:%s%s: ERROR DENTRY (%ld)!\n", str, str2, + PTR_ERR(dentry)); + return; + } + + fist_dprint(8, "PD:%s%s: dentry = %p\n", str, str2, dentry); + fist_dprint(8, "PD:%s%s: %s=%d\n", str, str2, "d_count", + atomic_read(&dentry->d_count)); + fist_dprint(8, "PD:%s%s: %s=%x\n", str, str2, "d_flags", + (int)dentry->d_flags); + fist_dprint(8, "PD:%s%s: %s=\"%s\" (len = %d)\n", str, str2, + "d_name.name", dentry->d_name.name, dentry->d_name.len); + fist_dprint(8, "PD:%s%s: %s=%p (%s)\n", str, str2, "d_sb", dentry->d_sb, + sbt(dentry->d_sb)); + fist_dprint(8, "PD:%s%s: %s=%p\n", str, str2, "d_inode", + dentry->d_inode); + if (dentry->d_inode) { + fist_dprint(8, "PD:%s%s: %s=%ld (%s)\n", str, str2, + "d_inode->i_ino", dentry->d_inode->i_ino, + sbt(dentry->d_inode->i_sb)); + fist_dprint(8, "PD:%s%s: dentry->d_inode->i_mode: %c%o\n", str, + str2, mode_to_type(dentry->d_inode->i_mode), + dentry->d_inode->i_mode); + } + fist_dprint(8, "PD:%s%s: %s=%p (%s)\n", str, str2, "d_parent", + dentry->d_parent, + (dentry->d_parent ? sbt(dentry->d_parent->d_sb) : "nil")); + fist_dprint(8, "PD:%s%s: %s=\"%s\"\n", str, str2, + "d_parent->d_name.name", dentry->d_parent->d_name.name); + fist_dprint(8, "PD:%s%s: %s=%d\n", str, str2, "d_parent->d_count", + atomic_read(&dentry->d_parent->d_count)); + fist_dprint(8, "PD:%s%s: %s=%p\n", str, str2, "d_op", dentry->d_op); + fist_dprint(8, "PD:%s%s: %s=%p\n", str, str2, "d_fsdata", + dentry->d_fsdata); + fist_dprint(8, "PD:%s%s: %s=%d\n", str, str2, "hlist_unhashed(d_hash)", + hlist_unhashed(&((struct dentry *)dentry)->d_hash)); + /* After we have printed it, we can assert something about it. */ + if (check) + BUG_ON(atomic_read(&dentry->d_count) <= 0); +} + +void fist_print_generic_dentry(const char *str, const struct dentry *dentry) +{ + __fist_print_generic_dentry(str, "", dentry, 1); +} +void fist_print_generic_dentry3(const char *str, const char *str2, + const struct dentry *dentry) +{ + __fist_print_generic_dentry(str, str2, dentry, 1); +} + +void fist_checkinode(const struct inode *inode, const char *msg) +{ + if (!inode) { + printk(KERN_WARNING "fist_checkinode - inode is NULL! (%s)\n", + msg); + return; + } + if (!itopd(inode)) { + fist_dprint(8, "fist_checkinode(%ld) - no private data (%s)\n", + inode->i_ino, msg); + return; + } + if ((itopd(inode)->b_start < 0) || !itohi(inode)) { + fist_dprint(8, + "fist_checkinode(%ld) - underlying is NULL! (%s)\n", + inode->i_ino, msg); + return; + } + if (!inode->i_sb) { + fist_dprint(8, + "fist_checkinode(%ld) - inode->i_sb is NULL! (%s)\n", + inode->i_ino, msg); + return; + } + fist_dprint(8, "inode->i_sb->s_type %p\n", inode->i_sb->s_type); + if (!inode->i_sb->s_type) { + fist_dprint(8, + "fist_checkinode(%ld) - inode->i_sb->s_type is NULL! (%s)\n", + inode->i_ino, msg); + return; + } + fist_dprint(6, + "CI: %s: inode->i_count = %d, hidden_inode->i_count = %d, inode = %lu, sb = %s, hidden_sb = %s\n", + msg, atomic_read(&inode->i_count), + itopd(inode)->b_start >= + 0 ? atomic_read(&itohi(inode)->i_count) : -1, inode->i_ino, + inode->i_sb->s_type->name, + itopd(inode)->b_start >= + 0 ? itohi(inode)->i_sb->s_type->name : "(none)"); +} + +void fist_print_sb(const char *str, const struct super_block *sb) +{ + struct super_block *hidden_superblock; + + if (!sb) { + fist_dprint(8, "PSB:%s: NULL SB PASSED!\n", str); + return; + } + + fist_dprint(8, "PSB:%s: %s=%u\n", str, "s_blocksize", + (int)sb->s_blocksize); + fist_dprint(8, "PSB:%s: %s=%u\n", str, "s_blocksize_bits", + (int)sb->s_blocksize_bits); + fist_dprint(8, "PSB:%s: %s=0x%x\n", str, "s_flags", (int)sb->s_flags); + fist_dprint(8, "PSB:%s: %s=0x%x\n", str, "s_magic", (int)sb->s_magic); + fist_dprint(8, "PSB:%s: %s=%llu\n", str, "s_maxbytes", sb->s_maxbytes); + fist_dprint(8, "PSB:%s: %s=%d\n", str, "s_count", (int)sb->s_count); + fist_dprint(8, "PSB:%s: %s=%d\n", str, "s_active", + (int)atomic_read(&sb->s_active)); + if (stopd(sb)) + fist_dprint(8, "sbstart=%d, sbend=%d\n", sbstart(sb), + sbend(sb)); + fist_dprint(8, "\n"); + + if (stopd(sb)) { + int bindex; + for (bindex = sbstart(sb); bindex <= sbend(sb); bindex++) { + hidden_superblock = stohs_index(sb, bindex); + if (!hidden_superblock) { + fist_dprint(8, "PSB:%s: HS#%d is NULL", str, + bindex); + continue; + } + + fist_dprint(8, "PSB:%s: HS#%d: %s=%u\n", str, bindex, + "s_blocksize", + (int)hidden_superblock->s_blocksize); + fist_dprint(8, "PSB:%s: HS#%d: %s=%u\n", str, bindex, + "s_blocksize_bits", + (int)hidden_superblock->s_blocksize_bits); + fist_dprint(8, "PSB:%s: HS#%d: %s=0x%x\n", str, bindex, + "s_flags", (int)hidden_superblock->s_flags); + fist_dprint(8, "PSB:%s: HS#%d: %s=0x%x\n", str, bindex, + "s_magic", (int)hidden_superblock->s_magic); + fist_dprint(8, "PSB:%s: HS#%d: %s=%llu\n", str, bindex, + "s_maxbytes", + hidden_superblock->s_maxbytes); + fist_dprint(8, "PSB:%s: HS#%d: %s=%d\n", str, bindex, + "s_count", (int)hidden_superblock->s_count); + fist_dprint(8, "PSB:%s: HS#%d: %s=%d\n", str, bindex, + "s_active", + (int)atomic_read(&hidden_superblock-> + s_active)); + } + } +} + +#endif +/* + * + * vim:shiftwidth=8 + * vim:tabstop=8 + * + * For Emacs: + * Local variables: + * c-basic-offset: 8 + * c-comment-only-line-offset: 0 + * c-offsets-alist: ((statement-block-intro . +) (knr-argdecl-intro . 0) + * (substatement-open . 0) (label . 0) (statement-cont . +)) + * indent-tabs-mode: t + * tab-width: 8 + * End: + */ diff -urN linux-2.6.17-rc1-mm3-orig/fs/unionfs/rdstate.c linux-2.6.17-rc1-mm3-new/fs/unionfs/rdstate.c --- linux-2.6.17-rc1-mm3-orig/fs/unionfs/rdstate.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/unionfs/rdstate.c 2006-04-11 20:24:21.000000000 +0000 @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2003-2006 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2006 Josef Sipek + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2005-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2006 Stony Brook University + * Copyright (c) 2003-2006 The Research Foundation of State University of New York + * + * For specific licensing information, see the COPYING file distributed with + * this package. + * + * This Copyright notice must be kept intact and distributed with all sources. + */ +/* + * $Id: rdstate.c,v 1.31 2006/01/14 19:56:01 dquigley Exp $ + */ + +#include "unionfs.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 kmem_cache_t 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 kmem_cache_t *unionfs_filldir_cachep; +int init_filldir_cache(void) +{ + unionfs_filldir_cachep = + kmem_cache_create("unionfs_filldir", sizeof(struct filldir_node), 0, + SLAB_RECLAIM_ACCOUNT, NULL, NULL); + + if (!unionfs_filldir_cachep) { + return -ENOMEM; + } + return 0; +} + +void destroy_filldir_cache(void) +{ + if (!unionfs_filldir_cachep) + return; + if (kmem_cache_destroy(unionfs_filldir_cachep)) { + printk(KERN_ERR + "unionfs_filldir_cache: not all structures were freed\n"); + } + return; +} + +/* 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 *hidden_inode; + int bindex; + int hashsize = MINHASHSIZE; + + if (itopd(inode)->uii_hashsize > 0) + return itopd(inode)->uii_hashsize; + + for (bindex = ibstart(inode); bindex <= ibend(inode); bindex++) { + if (!(hidden_inode = itohi_index(inode, bindex))) + continue; + + if (hidden_inode->i_size == DENTPAGE) { + hashsize += DENTPERONEPAGE; + } else { + hashsize += + (hidden_inode->i_size / DENTPAGE) * DENTPERPAGE; + } + } + + return hashsize; +} + +int init_rdstate(struct file *file) +{ + BUG_ON(sizeof(loff_t) != (sizeof(unsigned int) + sizeof(unsigned int))); + BUG_ON(ftopd(file)->rdstate != NULL); + + ftopd(file)->rdstate = + alloc_rdstate(file->f_dentry->d_inode, fbstart(file)); + if (!ftopd(file)->rdstate) + return -ENOMEM; + return 0; +} + +struct unionfs_dir_state *find_rdstate(struct inode *inode, loff_t fpos) +{ + struct unionfs_dir_state *rdstate = NULL; + struct list_head *pos; + + print_entry("f_pos: %lld", fpos); + spin_lock(&itopd(inode)->uii_rdlock); + list_for_each(pos, &itopd(inode)->uii_readdircache) { + struct unionfs_dir_state *r = + list_entry(pos, struct unionfs_dir_state, uds_cache); + if (fpos == rdstate2offset(r)) { + itopd(inode)->uii_rdcount--; + list_del(&r->uds_cache); + rdstate = r; + break; + } + } + spin_unlock(&itopd(inode)->uii_rdlock); + print_exit_pointer(rdstate); + return rdstate; +} + +struct unionfs_dir_state *alloc_rdstate(struct inode *inode, int bindex) +{ + int i = 0; + int hashsize; + int mallocsize = sizeof(struct unionfs_dir_state); + struct unionfs_dir_state *rdstate; + + hashsize = guesstimate_hash_size(inode); + mallocsize += hashsize * sizeof(struct list_head); + /* Round it up to the next highest power of two. */ + mallocsize--; + mallocsize |= mallocsize >> 1; + mallocsize |= mallocsize >> 2; + mallocsize |= mallocsize >> 4; + mallocsize |= mallocsize >> 8; + mallocsize |= mallocsize >> 16; + 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 (!rdstate) + return NULL; + + spin_lock(&itopd(inode)->uii_rdlock); + if (itopd(inode)->uii_cookie >= (MAXRDCOOKIE - 1)) + itopd(inode)->uii_cookie = 1; + else + itopd(inode)->uii_cookie++; + + rdstate->uds_cookie = itopd(inode)->uii_cookie; + spin_unlock(&itopd(inode)->uii_rdlock); + rdstate->uds_offset = 1; + rdstate->uds_access = jiffies; + rdstate->uds_bindex = bindex; + rdstate->uds_dirpos = 0; + rdstate->uds_hashentries = 0; + rdstate->uds_size = hashsize; + for (i = 0; i < rdstate->uds_size; i++) + INIT_LIST_HEAD(&rdstate->uds_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->uds_size; i++) { + struct list_head *head = &(state->uds_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 index; + unsigned int hash; + struct list_head *head; + struct list_head *pos; + struct filldir_node *cursor = NULL; + int found = 0; + + /* If we print entry, we end up with spurious data. */ + /* print_entry("name = %*s", namelen, name); */ + print_entry_location(); + + BUG_ON(namelen <= 0); + + hash = full_name_hash(name, namelen); + index = hash % rdstate->uds_size; + + head = &(rdstate->uds_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 the duplicate is in this branch, then the file system is corrupted. */ + if (cursor->bindex == rdstate->uds_bindex) { + //buf->error = err = -EIO; + fist_dprint(8, + "Possible I/O error unionfs_filldir: a file is duplicated in the same branch %d: %s\n", + rdstate->uds_bindex, cursor->name); + } + break; + } + } + + if (!found) { + cursor = NULL; + } + print_exit_pointer(cursor); + return cursor; +} + +inline struct filldir_node *alloc_filldir_node(const char *name, int namelen, + unsigned int hash, int bindex) +{ + struct filldir_node *newnode; + + newnode = + (struct filldir_node *)kmem_cache_alloc(unionfs_filldir_cachep, + SLAB_KERNEL); + if (!newnode) + goto out; + + out: + return newnode; +} + +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; + + /* We can't print this because we end up Oopsing. */ + /* print_entry("name = %*s", namelen, name); */ + print_entry_location(); + + BUG_ON(namelen <= 0); + + hash = full_name_hash(name, namelen); + index = hash % rdstate->uds_size; + head = &(rdstate->uds_list[index]); + + new = alloc_filldir_node(name, namelen, hash, bindex); + if (!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 = (char *)KMALLOC(namelen + 1, GFP_KERNEL); + if (!new->name) { + kmem_cache_free(unionfs_filldir_cachep, new); + new = NULL; + goto out; + } + } + + memcpy(new->name, name, namelen); + new->name[namelen] = '\0'; + + rdstate->uds_hashentries++; + + list_add(&(new->file_list), head); + out: + print_exit_status(err); + return err; +} + +/* + * + * vim:shiftwidth=8 + * vim:tabstop=8 + * + * For Emacs: + * Local variables: + * c-basic-offset: 8 + * c-comment-only-line-offset: 0 + * c-offsets-alist: ((statement-block-intro . +) (knr-argdecl-intro . 0) + * (substatement-open . 0) (label . 0) (statement-cont . +)) + * indent-tabs-mode: t + * tab-width: 8 + * End: + */ diff -urN linux-2.6.17-rc1-mm3-orig/fs/unionfs/rename.c linux-2.6.17-rc1-mm3-new/fs/unionfs/rename.c --- linux-2.6.17-rc1-mm3-orig/fs/unionfs/rename.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/unionfs/rename.c 2006-04-11 20:24:21.000000000 +0000 @@ -0,0 +1,845 @@ +/* + * Copyright (c) 2003-2006 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2006 Josef Sipek + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2005-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2006 Stony Brook University + * Copyright (c) 2003-2006 The Research Foundation of State University of New York + * + * For specific licensing information, see the COPYING file distributed with + * this package. + * + * This Copyright notice must be kept intact and distributed with all sources. + */ +/* + * $Id: rename.c,v 1.40 2006/02/20 22:23:01 dquigley Exp $ + */ + +#include "unionfs.h" + +static int do_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry, + int bindex) +{ + int err = 0; + struct dentry *hidden_old_dentry; + struct dentry *hidden_new_dentry; + struct dentry *hidden_old_dir_dentry; + struct dentry *hidden_new_dir_dentry; + struct dentry *hidden_wh_dentry; + struct dentry *hidden_wh_dir_dentry; + char *wh_name = NULL; + + print_entry(" bindex=%d", bindex); + + fist_print_dentry("IN: do_rename, old_dentry", old_dentry); + fist_print_dentry("IN: do_rename, new_dentry", new_dentry); + fist_dprint(7, "do_rename for bindex = %d\n", bindex); + + hidden_new_dentry = dtohd_index(new_dentry, bindex); + hidden_old_dentry = dtohd_index(old_dentry, bindex); + + if (!hidden_new_dentry) { + hidden_new_dentry = + create_parents(new_dentry->d_parent->d_inode, new_dentry, + bindex); + if (IS_ERR(hidden_new_dentry)) { + fist_dprint(7, + "error creating directory tree for rename, bindex = %d\n", + bindex); + err = PTR_ERR(hidden_new_dentry); + goto out; + } + } + + wh_name = alloc_whname(new_dentry->d_name.name, new_dentry->d_name.len); + if (IS_ERR(wh_name)) { + err = PTR_ERR(wh_name); + goto out; + } + + hidden_wh_dentry = + LOOKUP_ONE_LEN(wh_name, hidden_new_dentry->d_parent, + new_dentry->d_name.len + WHLEN); + if (IS_ERR(hidden_wh_dentry)) { + err = PTR_ERR(hidden_wh_dentry); + goto out; + } + + if (hidden_wh_dentry->d_inode) { + /* get rid of the whiteout that is existing */ + if (hidden_new_dentry->d_inode) { + printk(KERN_WARNING + "Both a whiteout and a dentry exist when doing a rename!\n"); + err = -EIO; + + DPUT(hidden_wh_dentry); + goto out; + } + + hidden_wh_dir_dentry = lock_parent(hidden_wh_dentry); + if (!(err = is_robranch_super(old_dentry->d_sb, bindex))) { + err = + vfs_unlink(hidden_wh_dir_dentry->d_inode, + hidden_wh_dentry); + } + DPUT(hidden_wh_dentry); + unlock_dir(hidden_wh_dir_dentry); + if (err) + goto out; + } else + DPUT(hidden_wh_dentry); + + DGET(hidden_old_dentry); + hidden_old_dir_dentry = GET_PARENT(hidden_old_dentry); + hidden_new_dir_dentry = GET_PARENT(hidden_new_dentry); + + lock_rename(hidden_old_dir_dentry, hidden_new_dir_dentry); + + if (!(err = is_robranch_super(old_dentry->d_sb, bindex))) { + fist_print_dentry("NEWBEF", new_dentry); + fist_print_dentry("OLDBEF", old_dentry); + err = + vfs_rename(hidden_old_dir_dentry->d_inode, + hidden_old_dentry, + hidden_new_dir_dentry->d_inode, + hidden_new_dentry); + fist_print_dentry("NEWAFT", new_dentry); + fist_print_dentry("OLDAFT", old_dentry); + } + + unlock_rename(hidden_old_dir_dentry, hidden_new_dir_dentry); + + DPUT(hidden_old_dir_dentry); + DPUT(hidden_new_dir_dentry); + DPUT(hidden_old_dentry); + + out: + if (!err) { + /* Fixup the newdentry. */ + if (bindex < dbstart(new_dentry)) + set_dbstart(new_dentry, bindex); + else if (bindex > dbend(new_dentry)) + set_dbend(new_dentry, bindex); + } + + KFREE(wh_name); + + fist_print_dentry("OUT: do_rename, old_dentry", old_dentry); + fist_print_dentry("OUT: do_rename, new_dentry", new_dentry); + + print_exit_status(err); + return err; +} + +static int unionfs_rename_whiteout(struct inode *old_dir, + struct dentry *old_dentry, + struct inode *new_dir, + struct dentry *new_dentry) +{ + int err = 0; + int bindex; + int old_bstart, old_bend; + int new_bstart, new_bend; + int do_copyup = -1; + struct dentry *parent_dentry = NULL; + int local_err = 0; + int eio = 0; + int revert = 0; + + print_entry_location(); + + old_bstart = dbstart(old_dentry); + old_bend = dbend(old_dentry); + parent_dentry = old_dentry->d_parent; + + new_bstart = dbstart(new_dentry); + new_bend = dbend(new_dentry); + + /* Rename source to destination. */ + err = do_rename(old_dir, old_dentry, new_dir, new_dentry, 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; + + unlink_dentry = dtohd_index(new_dentry, bindex); + if (!unlink_dentry) { + continue; + } + + unlink_dir_dentry = lock_parent(unlink_dentry); + if (!(err = is_robranch_super(old_dir->i_sb, bindex))) { + err = + vfs_unlink(unlink_dir_dentry->d_inode, + unlink_dentry); + } + + fist_copy_attr_times(new_dentry->d_parent->d_inode, + unlink_dir_dentry->d_inode); + /* propagate number of hard-links */ + new_dentry->d_parent->d_inode->i_nlink = + get_nlinks(new_dentry->d_parent->d_inode); + + unlock_dir(unlink_dir_dentry); + if (!err) { + if (bindex != new_bstart) { + DPUT(unlink_dentry); + set_dtohd_index(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_dentry->d_parent->d_inode, + old_dentry, old_bstart, bindex, NULL, + old_dentry->d_inode->i_size); + if (!err) { + parent_dentry = old_dentry->d_parent; + err = + do_rename(old_dir, old_dentry, new_dir, + new_dentry, bindex); + break; + } + } + } + + /* Create whiteout for source, only if: + * (1) There is more than one underlying instance of source. + * (2) We did a copy_up + */ + if ((old_bstart != old_bend) || (do_copyup != -1)) { + int start = (do_copyup == -1) ? old_bstart : do_copyup; + /* we want to create a whiteout for name in this parent dentry */ + local_err = create_whiteout(old_dentry, start); + if (local_err) { + /* We can't fix anything now, so we cop-out and use -EIO. */ + printk + ("<0>We can't create a whiteout for the source in rename!\n"); + err = -EIO; + goto out; + } + } + + out: + + print_exit_status(err); + return err; + + revert: + /* Do revert here. */ + local_err = unionfs_refresh_hidden_dentry(new_dentry, old_bstart); + if (local_err) { + printk(KERN_WARNING + "Revert failed in rename: the new refresh failed.\n"); + eio = -EIO; + } + + local_err = unionfs_refresh_hidden_dentry(old_dentry, old_bstart); + if (local_err) { + printk(KERN_WARNING + "Revert failed in rename: the old refresh failed.\n"); + eio = -EIO; + goto revert_out; + } + + if (!dtohd_index(new_dentry, bindex) + || !dtohd_index(new_dentry, bindex)->d_inode) { + printk(KERN_WARNING + "Revert failed in rename: the object disappeared from under us!\n"); + eio = -EIO; + goto revert_out; + } + + if (dtohd_index(old_dentry, bindex) + && dtohd_index(old_dentry, bindex)->d_inode) { + printk(KERN_WARNING + "Revert failed in rename: the object was created underneath us!\n"); + eio = -EIO; + goto revert_out; + } + + local_err = + do_rename(new_dir, new_dentry, old_dir, old_dentry, old_bstart); + + /* If we can't fix it, then we cop-out with -EIO. */ + if (local_err) { + printk(KERN_WARNING "Revert failed in rename!\n"); + eio = -EIO; + } + + local_err = unionfs_refresh_hidden_dentry(new_dentry, bindex); + if (local_err) + eio = -EIO; + local_err = unionfs_refresh_hidden_dentry(old_dentry, bindex); + if (local_err) + eio = -EIO; + + revert_out: + if (eio) + err = eio; + print_exit_status(err); + return err; +} + +/* + * Unfortunately, we cannot simply call things like dbstart() in different + * places of the rename code because we move things around. So, we use this + * structure to pass the necessary information around to all the places that + * need it. + */ +struct rename_info { + int do_copyup; + int do_whiteout; + int rename_ok; + + int old_bstart; + int old_bend; + int new_bstart; + int new_bend; + + int isdir; /* Is the source a directory? */ + int clobber; /* Are we clobbering the destination? */ +}; +#ifdef UNIONFS_DELETE_ALL +/* + * Rename all occurences of source except for the leftmost destination + */ +static int __rename_all(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry, + fd_set * success_mask, struct rename_info *info) +{ + int bindex; + + int err = 0; + + print_entry_location(); + + /* Loop through all the branches from right to left and rename all + * instances of source to destination, except the leftmost destination + */ + for (bindex = info->old_bend; bindex >= info->old_bstart; bindex--) { + /* We don't rename if there is no source. */ + if (dtohd_index(old_dentry, bindex) == NULL) + continue; + + /* we rename the bstart of destination only at the last of + * all operations, so that we don't lose it on error + */ + if (info->clobber && (bindex == info->new_bstart)) + continue; + + /* We shouldn't have a handle on this if there is no inode. */ + err = + do_rename(old_dir, old_dentry, new_dir, new_dentry, bindex); + if (!err) { + /* For reverting. */ + FD_SET(bindex, success_mask); + /* So we know not to copyup on failures the right */ + info->rename_ok = bindex; + } else if (IS_COPYUP_ERR(err)) { + if (info->isdir) { + err = -EXDEV; + break; + } + + /* we need a whiteout... */ + info->do_whiteout = bindex - 1; + + if (bindex == info->old_bstart) + /* ...and a copyup */ + info->do_copyup = bindex - 1; + + err = 0; /* reset error */ + } else + break; /* error is set by do_rename */ + } + + print_exit_status(err); + return err; +} + +/* + * Unlink all destinations (if they exist) to the left of the left-most + * source + */ +static int __rename_all_unlink(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry, + struct rename_info *info) +{ + int bindex; + + struct dentry *unlink_dentry; + struct dentry *unlink_dir_dentry; + + int err = 0; + + print_entry_location(); + + for (bindex = info->old_bstart - 1; bindex > info->new_bstart; bindex--) { + unlink_dentry = dtohd_index(new_dentry, bindex); + if (!unlink_dentry) + continue; + + /* lock, unlink if possible, copyup times, unlock */ + unlink_dir_dentry = lock_parent(unlink_dentry); + if (!(err = is_robranch_super(old_dir->i_sb, bindex))) + err = + vfs_unlink(unlink_dir_dentry->d_inode, + unlink_dentry); + + fist_copy_attr_times(new_dentry->d_parent->d_inode, + unlink_dir_dentry->d_inode); + new_dentry->d_parent->d_inode->i_nlink = + get_nlinks(new_dentry->d_parent->d_inode); + + unlock_dir(unlink_dir_dentry); + + if (!err) { + if (bindex != info->new_bstart) { + DPUT(unlink_dentry); + set_dtohd_index(new_dentry, bindex, NULL); + } + } else if (IS_COPYUP_ERR(err)) { + if (info->isdir) { + err = -EXDEV; + break; + } + info->do_copyup = bindex - 1; + + err = 0; /* reset error */ + } else + break; /* err is set by is_ro_branch_super or vfs_unlink */ + } + + print_exit_status(err); + return err; +} + +/* + * Try to revert everything we have done in __rename_all and __rename_all_unlink + */ +static int __rename_all_revert(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry, + fd_set * success_mask, struct rename_info *info) +{ + int bindex; + + int err; + int eio = 0; + + print_entry_location(); + + for (bindex = info->old_bstart; bindex <= info->old_bend; bindex++) { + if (!FD_ISSET(bindex, success_mask)) + continue; + + err = unionfs_refresh_hidden_dentry(new_dentry, bindex); + if (err) { + printk(KERN_WARNING "Revert failed in rename: " + "the new refresh failed.\n"); + eio = -EIO; + } + + err = unionfs_refresh_hidden_dentry(old_dentry, bindex); + if (err) { + printk(KERN_WARNING "Revert failed in rename: " + "the old refresh failed.\n"); + eio = -EIO; + continue; + } + + if (!dtohd_index(new_dentry, bindex) + || !dtohd_index(new_dentry, bindex)->d_inode) { + printk(KERN_WARNING "Revert failed in rename: " + "the object disappeared from under us!\n"); + eio = -EIO; + continue; + } + + if (dtohd_index(old_dentry, bindex) + && dtohd_index(old_dentry, bindex)->d_inode) { + printk(KERN_WARNING "Revert failed in rename: " + "the object was created underneath us!\n"); + eio = -EIO; + continue; + } + + err = + do_rename(new_dir, new_dentry, old_dir, old_dentry, bindex); + /* If we can't fix it, then we cop-out with -EIO. */ + if (err) { + printk(KERN_WARNING "Revert failed in rename!\n"); + eio = -EIO; + } + + err = unionfs_refresh_hidden_dentry(new_dentry, bindex); + if (err) + eio = -EIO; + err = unionfs_refresh_hidden_dentry(old_dentry, bindex); + if (err) + eio = -EIO; + } + + print_exit_status(eio); + return eio; +} + +/* + * Finish off the rename, by either over writing the last destination or + * unlinking the last destination to the left of us + */ +static int __rename_all_clobber(struct inode *old_dir, + struct dentry *old_dentry, + struct inode *new_dir, + struct dentry *new_dentry, + struct rename_info *info) +{ + int err = 0; + + print_entry_location(); + + if (dtohd_index(old_dentry, info->new_bstart)) { + /* rename the last source, knowing we're overwriting something */ + err = + do_rename(old_dir, old_dentry, new_dir, new_dentry, + info->new_bstart); + if (IS_COPYUP_ERR(err)) { + if (info->isdir) { + err = -EXDEV; + goto out; + } + if (info->rename_ok > info->new_bstart) { + if ((info->do_copyup == -1) + || (info->new_bstart - 1 < info->do_copyup)) + info->do_copyup = info->new_bstart - 1; + } + if ((info->do_whiteout == -1) + || (info->new_bstart - 1 < info->do_whiteout)) { + info->do_whiteout = info->new_bstart - 1; + } + err = 0; // reset error + } + } else if (info->new_bstart < info->old_bstart) { + /* the newly renamed file would get hidden, let's unlink the + * file to the left of it */ + struct dentry *unlink_dentry; + struct dentry *unlink_dir_dentry; + + unlink_dentry = dtohd_index(new_dentry, info->new_bstart); + + unlink_dir_dentry = lock_parent(unlink_dentry); + if (!(err = is_robranch_super(old_dir->i_sb, info->new_bstart))) + err = vfs_unlink(unlink_dir_dentry->d_inode, + unlink_dentry); + + fist_copy_attr_times(new_dentry->d_parent->d_inode, + unlink_dir_dentry->d_inode); + new_dentry->d_parent->d_inode->i_nlink = + get_nlinks(new_dentry->d_parent->d_inode); + + unlock_dir(unlink_dir_dentry); + + if (IS_COPYUP_ERR(err)) { + if (info->isdir) { + err = -EXDEV; + goto out; + } + if ((info->do_copyup == -1) + || (info->new_bstart - 1 < info->do_copyup)) + info->do_copyup = info->new_bstart - 1; + + err = 0; // reset error + } + } + + out: + print_exit_status(err); + return err; +} + +/* + * The function is nasty, nasty, nasty, but so is rename. :( + */ +static int unionfs_rename_all(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + struct dentry *parent_dentry = NULL; + int err = 0; + int eio; + + /* These variables control error handling. */ + fd_set success_mask; + char *name = NULL; + + /* unfortunately, we have to resort to this, because dbstart/dbend would + return different things in different place of the rename code */ + struct rename_info info; + + info.rename_ok = FD_SETSIZE; /* The last rename that is ok. */ + info.do_copyup = -1; /* Where we should start copyup. */ + info.do_whiteout = -1; /* Where we should start whiteouts of the source. */ + + print_entry_location(); + + parent_dentry = old_dentry->d_parent; + name = KMALLOC(old_dentry->d_name.len + 1, GFP_KERNEL); + if (!name) { + err = -ENOMEM; + goto out; + } + strncpy(name, old_dentry->d_name.name, old_dentry->d_name.len + 1); + + info.new_bstart = dbstart(new_dentry); + info.new_bend = dbend(new_dentry); + + info.old_bstart = dbstart(old_dentry); + info.old_bend = dbend(old_dentry); + + BUG_ON(info.new_bstart < 0); + BUG_ON(info.old_bstart < 0); + + /* The failure mask only can deal with FD_SETSIZE entries. */ + BUG_ON(info.old_bend > FD_SETSIZE); + BUG_ON(info.new_bend > FD_SETSIZE); + FD_ZERO(&success_mask); + + /* Life is simpler if the dentry doesn't exist. */ + info.clobber = + (dtohd_index(new_dentry, info.new_bstart)->d_inode) ? 1 : 0; + info.isdir = S_ISDIR(old_dentry->d_inode->i_mode); + + /* rename everything we can */ + err = + __rename_all(old_dir, old_dentry, new_dir, new_dentry, + &success_mask, &info); + if (err) + goto revert; + + /* unlink destinations even further left */ + err = + __rename_all_unlink(old_dir, old_dentry, new_dir, new_dentry, + &info); + if (err) + goto revert; + + if (info.clobber) { + /* Now we need to handle the leftmost of the destination. */ + err = + __rename_all_clobber(old_dir, old_dentry, new_dir, + new_dentry, &info); + if (err) + goto revert; + } + + /* Create a whiteout for the source. */ + if (info.do_whiteout != -1) { + BUG_ON(info.do_whiteout < 0); + /* create a lookup in the old_dentry's actual parent */ + lock_dentry(parent_dentry); + err = + create_whiteout_parent(parent_dentry, name, + info.do_whiteout); + unlock_dentry(parent_dentry); + if (err) { + /* We can't fix anything now, so we -EIO. */ + printk(KERN_WARNING "We can't create a whiteout for the" + "source in rename!\n"); + err = -EIO; + goto out; + } + } + + /* Copy up if necessary */ + if (info.do_copyup != -1) { + int bindex; + + /* 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 let the userspace recurse and ask us + * to copy up each file separately + */ + if (S_ISDIR(old_dentry->d_inode->i_mode)) { + err = -EXDEV; + goto out; + } + + for (bindex = info.do_copyup; bindex >= 0; bindex--) { + err = + copyup_dentry(old_dentry->d_parent->d_inode, + old_dentry, info.old_bstart, bindex, + NULL, old_dentry->d_inode->i_size); + if (!err) { + err = + do_rename(old_dir, old_dentry, new_dir, + new_dentry, bindex); + break; + } + } + } + + /* We are at the point where reverting doesn't happen. */ + goto out; + + revert: + /* something bad happened, try to revert */ + eio = + __rename_all_revert(old_dir, old_dentry, new_dir, new_dentry, + &success_mask, &info); + if (eio) + err = eio; + + out: + KFREE(name); + print_exit_status(err); + return err; +} +#endif + +static struct dentry *lookup_whiteout(struct dentry *dentry) +{ + char *whname; + int bindex = -1, bstart = -1, bend = -1; + struct dentry *parent, *hidden_parent, *wh_dentry; + + whname = alloc_whname(dentry->d_name.name, dentry->d_name.len); + if (IS_ERR(whname)) + return (void *)whname; + + parent = GET_PARENT(dentry); + lock_dentry(parent); + bstart = dbstart(parent); + bend = dbend(parent); + wh_dentry = ERR_PTR(-ENOENT); + for (bindex = bstart; bindex <= bend; bindex++) { + hidden_parent = dtohd_index(parent, bindex); + if (!hidden_parent) + continue; + wh_dentry = + LOOKUP_ONE_LEN(whname, hidden_parent, + dentry->d_name.len + WHLEN); + if (IS_ERR(wh_dentry)) + continue; + if (wh_dentry->d_inode) + break; + DPUT(wh_dentry); + wh_dentry = ERR_PTR(-ENOENT); + } + unlock_dentry(parent); + DPUT(parent); + KFREE(whname); + return wh_dentry; +} + +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; + + print_entry_location(); + + double_lock_dentry(old_dentry, new_dentry); + + fist_checkinode(old_dir, "unionfs_rename-old_dir"); + fist_checkinode(new_dir, "unionfs_rename-new_dir"); + fist_print_dentry("IN: unionfs_rename, old_dentry", old_dentry); + fist_print_dentry("IN: unionfs_rename, new_dentry", new_dentry); + + err = unionfs_partial_lookup(old_dentry); + if (err) + goto out; + err = unionfs_partial_lookup(new_dentry); + if (err) + goto out; + + /* + * if new_dentry is already hidden because of whiteout, + * simply override it even if the whiteouted dir is not empty. + */ + wh_dentry = lookup_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(old_dentry->d_inode->i_mode)) { + /* check if this unionfs directory is empty or not */ + err = check_empty(new_dentry, NULL); + if (err) + goto out; + /* Handle the case where we are overwriting directories + * that are not really empty because of whiteout or + * non-whiteout entries. + */ + } + } +#ifdef UNIONFS_DELETE_ALL + if (IS_SET(old_dir->i_sb, DELETE_ALL)) + err = unionfs_rename_all(old_dir, old_dentry, new_dir, + new_dentry); + else +#endif + err = unionfs_rename_whiteout(old_dir, old_dentry, new_dir, + new_dentry); + + out: + fist_checkinode(new_dir, "post unionfs_rename-new_dir"); + fist_print_dentry("OUT: unionfs_rename, old_dentry", old_dentry); + + if (err) { + /* clear the new_dentry stuff created */ + d_drop(new_dentry); + } else + fist_print_dentry("OUT: unionfs_rename, new_dentry", + new_dentry); + + unlock_dentry(new_dentry); + unlock_dentry(old_dentry); + print_exit_status(err); + return err; +} + +/* + * + * vim:shiftwidth=8 + * vim:tabstop=8 + * + * For Emacs: + * Local variables: + * c-basic-offset: 8 + * c-comment-only-line-offset: 0 + * c-offsets-alist: ((statement-block-intro . +) (knr-argdecl-intro . 0) + * (substatement-open . 0) (label . 0) (statement-cont . +)) + * indent-tabs-mode: t + * tab-width: 8 + * End: + */ diff -urN linux-2.6.17-rc1-mm3-orig/fs/unionfs/stale_inode.c linux-2.6.17-rc1-mm3-new/fs/unionfs/stale_inode.c --- linux-2.6.17-rc1-mm3-orig/fs/unionfs/stale_inode.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/unionfs/stale_inode.c 2006-04-11 20:24:21.000000000 +0000 @@ -0,0 +1,142 @@ +/* + * Adpated from linux/fs/bad_inode.c + * + * Copyright (C) 1997, Stephen Tweedie + * + * Provide stub functions for "stale" inodes, a bit friendlier than the + * -EIO that bad_inode.c does. + */ +/* + * $Id: stale_inode.c,v 1.12 2005/11/20 23:03:34 arunmk Exp $ + */ + +#include +#include + +#include +#include +#include + +static struct address_space_operations unionfs_stale_aops; + +/* declarations for "sparse */ +extern struct inode_operations stale_inode_ops; + +/* + * The follow_link operation is special: it must behave as a no-op + * so that a stale root inode can at least be unmounted. To do this + * we must dput() the base and return the dentry with a dget(). + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) +static int stale_follow_link(struct dentry *dent, struct nameidata *nd) +#else +static void *stale_follow_link(struct dentry *dent, struct nameidata *nd) +#endif +{ + int err = vfs_follow_link(nd, ERR_PTR(-ESTALE)); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) + return err; +#else + return ERR_PTR(err); +#endif +} + +static int return_ESTALE(void) +{ + return -ESTALE; +} + +#define ESTALE_ERROR ((void *) (return_ESTALE)) + +static struct file_operations stale_file_ops = { + .llseek = ESTALE_ERROR, + .read = ESTALE_ERROR, + .write = ESTALE_ERROR, + .readdir = ESTALE_ERROR, + .poll = ESTALE_ERROR, + .ioctl = ESTALE_ERROR, + .mmap = ESTALE_ERROR, + .open = ESTALE_ERROR, + .flush = ESTALE_ERROR, + .release = ESTALE_ERROR, + .fsync = ESTALE_ERROR, + .fasync = ESTALE_ERROR, + .lock = ESTALE_ERROR, +}; + +struct inode_operations stale_inode_ops = { + .create = ESTALE_ERROR, + .lookup = ESTALE_ERROR, + .link = ESTALE_ERROR, + .unlink = ESTALE_ERROR, + .symlink = ESTALE_ERROR, + .mkdir = ESTALE_ERROR, + .rmdir = ESTALE_ERROR, + .mknod = ESTALE_ERROR, + .rename = ESTALE_ERROR, + .readlink = ESTALE_ERROR, + .follow_link = stale_follow_link, + .truncate = ESTALE_ERROR, + .permission = ESTALE_ERROR, +}; + +/* + * When a filesystem is unable to read an inode due to an I/O error in + * its read_inode() function, it can call make_stale_inode() to return a + * set of stubs which will return ESTALE errors as required. + * + * We only need to do limited initialisation: all other fields are + * preinitialised to zero automatically. + */ + +/** + * make_stale_inode - mark an inode stale due to an I/O error + * @inode: Inode to mark stale + * + * When an inode cannot be read due to a media or remote network + * failure this function makes the inode "stale" and causes I/O operations + * on it to fail from this point on. + */ + +void make_stale_inode(struct inode *inode) +{ + inode->i_mode = S_IFREG; + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + inode->i_op = &stale_inode_ops; + inode->i_fop = &stale_file_ops; + inode->i_mapping->a_ops = &unionfs_stale_aops; +} + +/* + * This tests whether an inode has been flagged as stale. The test uses + * &stale_inode_ops to cover the case of invalidated inodes as well as + * those created by make_stale_inode() above. + */ + +/** + * is_stale_inode - is an inode errored + * @inode: inode to test + * + * Returns true if the inode in question has been marked as stale. + */ + +int is_stale_inode(struct inode *inode) +{ + return (inode->i_op == &stale_inode_ops); +} + +/* + * + * vim:shiftwidth=8 + * vim:tabstop=8 + * + * For Emacs: + * Local variables: + * c-basic-offset: 8 + * c-comment-only-line-offset: 0 + * c-offsets-alist: ((statement-block-intro . +) (knr-argdecl-intro . 0) + * (substatement-open . 0) (label . 0) (statement-cont . +)) + * indent-tabs-mode: t + * tab-width: 8 + * End: + */ diff -urN linux-2.6.17-rc1-mm3-orig/fs/unionfs/subr.c linux-2.6.17-rc1-mm3-new/fs/unionfs/subr.c --- linux-2.6.17-rc1-mm3-orig/fs/unionfs/subr.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/unionfs/subr.c 2006-04-11 20:24:21.000000000 +0000 @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2003-2006 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2006 Josef Sipek + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2005-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2006 Stony Brook University + * Copyright (c) 2003-2006 The Research Foundation of State University of New York + * + * For specific licensing information, see the COPYING file distributed with + * this package. + * + * This Copyright notice must be kept intact and distributed with all sources. + */ +/* + * $Id: subr.c,v 1.133 2006/01/24 23:57:41 jsipek Exp $ + */ + +#include "unionfs.h" + +/* 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 *hidden_dir_dentry; + struct dentry *hidden_dentry; + struct dentry *hidden_wh_dentry; + char *name = NULL; + int err = -EINVAL; + + print_entry("start = %d", start); + + verify_locked(dentry); + + fist_print_dentry("IN create_whiteout", dentry); + bstart = dbstart(dentry); + bend = dbend(dentry); + + /* create dentry's whiteout equivalent */ + name = alloc_whname(dentry->d_name.name, dentry->d_name.len); + if (IS_ERR(name)) { + err = PTR_ERR(name); + goto out; + } + + for (bindex = start; bindex >= 0; bindex--) { + hidden_dentry = dtohd_index(dentry, bindex); + + if (!hidden_dentry) { + /* if hidden dentry is not present, create the entire + * hidden 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. + */ + hidden_dentry = create_parents(dentry->d_inode, + dentry, bindex); + if (!hidden_dentry || IS_ERR(hidden_dentry)) { + fist_dprint(8, + "hidden dentry NULL for bindex = %d\n", + bindex); + continue; + } + } + hidden_wh_dentry = + LOOKUP_ONE_LEN(name, hidden_dentry->d_parent, + dentry->d_name.len + WHLEN); + if (IS_ERR(hidden_wh_dentry)) + continue; + + /* The whiteout already exists. This used to be impossible, but + * now is possible because of opaqueness. */ + if (hidden_wh_dentry->d_inode) { + DPUT(hidden_wh_dentry); + err = 0; + goto out; + } + + hidden_dir_dentry = lock_parent(hidden_wh_dentry); + if (!(err = is_robranch_super(dentry->d_sb, bindex))) { + err = + vfs_create(hidden_dir_dentry->d_inode, + hidden_wh_dentry, + ~current->fs->umask & S_IRWXUGO, NULL); + + } + unlock_dir(hidden_dir_dentry); + DPUT(hidden_wh_dentry); + + if (!err) + break; + + if (!IS_COPYUP_ERR(err)) + break; + } + + /* set dbopaque so that lookup will not proceed after this branch */ + if (!err) + set_dbopaque(dentry, bindex); + + fist_print_dentry("OUT create_whiteout", dentry); + out: + KFREE(name); + print_exit_status(err); + return err; +} + +/* Create a whiteout for filename in parent at 'start' branch */ +/* the parent dentry has to be valid with the hidden parent also being +valid */ +/* dbopaque has to be set by the caller */ +int create_whiteout_parent(struct dentry *parent_dentry, const char *filename, + int start) +{ + int bindex; + int old_bstart, old_bend; + struct dentry *hidden_dir_dentry; + struct dentry *hidden_grand_parent_dentry; + struct dentry *hidden_parent_dentry; + struct dentry *hidden_wh_dentry; + char *name = NULL; + int err = -EINVAL; + + print_entry_location(); + + verify_locked(parent_dentry); + + old_bstart = dbstart(parent_dentry); + old_bend = dbend(parent_dentry); + + fist_print_dentry("IN create_whiteout_parent", parent_dentry); + + /* create dentry's whiteout equivalent */ + name = alloc_whname(filename, strlen(filename)); + if (IS_ERR(name)) { + err = PTR_ERR(name); + goto out; + } + + for (bindex = start; bindex >= 0; bindex--) { + hidden_parent_dentry = dtohd_index(parent_dentry, bindex); + + if (!hidden_parent_dentry) { + /* create the recursive directory structure and return + * the negative dentry for the parent where we want to + * create whiteout. */ + BUG_ON(parent_dentry->d_inode == NULL); + hidden_parent_dentry = + create_parents(parent_dentry->d_parent->d_inode, + parent_dentry, bindex); + if (!hidden_parent_dentry + || IS_ERR(hidden_parent_dentry)) { + fist_dprint(8, + "hidden dentry NULL for bindex = %d\n", + bindex); + continue; + } + + /* create directory of the hidden parent, + * if it is negative. + * This is where whiteout is created + */ + hidden_grand_parent_dentry = + lock_parent(hidden_parent_dentry); + + /* We shouldn't create things in a read-only branch. */ + if (! + (err = + is_robranch_super(parent_dentry->d_sb, bindex))) + err = + vfs_mkdir(hidden_grand_parent_dentry-> + d_inode, hidden_parent_dentry, + S_IRWXU); + + unlock_dir(hidden_grand_parent_dentry); + if (err || !hidden_parent_dentry->d_inode) { + DPUT(hidden_parent_dentry); + if (!IS_COPYUP_ERR(err)) + break; + else + continue; + } + set_itohi_index(parent_dentry->d_inode, bindex, + IGRAB(hidden_parent_dentry->d_inode)); + if (bindex < ibstart(parent_dentry->d_inode)) + ibstart(parent_dentry->d_inode) = bindex; + else if (bindex > ibend(parent_dentry->d_inode)) + ibend(parent_dentry->d_inode) = bindex; + } + + /* lookup for the whiteout dentry that we want to create */ + hidden_wh_dentry = + LOOKUP_ONE_LEN(name, hidden_parent_dentry, + strlen(filename) + WHLEN); + if (!hidden_wh_dentry || IS_ERR(hidden_wh_dentry)) + continue; + BUG_ON(hidden_wh_dentry->d_inode); + + /* hidden_dir_dentry and hidden_parent_dentry + * are going to be the same */ + hidden_dir_dentry = lock_parent(hidden_wh_dentry); + + /* We shouldn't create things in a read-only branch. */ + if (!(err = is_robranch_super(parent_dentry->d_sb, bindex))) { + err = + vfs_create(hidden_dir_dentry->d_inode, + hidden_wh_dentry, + ~current->fs->umask & S_IRWXUGO, NULL); + } + + unlock_dir(hidden_dir_dentry); + DPUT(hidden_wh_dentry); + + if (!err || !IS_COPYUP_ERR(err)) + break; + } + + out: + fist_print_dentry("OUT create_whiteout_parent", parent_dentry); + KFREE(name); + print_exit_status(err); + return err; +} + +/* This is a helper function for rename, which ends up with hosed over dentries + * when it needs to revert. */ +int unionfs_refresh_hidden_dentry(struct dentry *dentry, int bindex) +{ + struct dentry *hidden_dentry; + struct dentry *hidden_parent; + int err = 0; + + print_entry(" bindex = %d", bindex); + + verify_locked(dentry); + lock_dentry(dentry->d_parent); + hidden_parent = dtohd_index(dentry->d_parent, bindex); + unlock_dentry(dentry->d_parent); + + BUG_ON(!S_ISDIR(hidden_parent->d_inode->i_mode)); + + hidden_dentry = + LOOKUP_ONE_LEN(dentry->d_name.name, hidden_parent, + dentry->d_name.len); + if (IS_ERR(hidden_dentry)) { + err = PTR_ERR(hidden_dentry); + goto out; + } + + if (dtohd_index(dentry, bindex)) + DPUT(dtohd_index(dentry, bindex)); + if (itohi_index(dentry->d_inode, bindex)) { + IPUT(itohi_index(dentry->d_inode, bindex)); + set_itohi_index(dentry->d_inode, bindex, NULL); + } + if (!hidden_dentry->d_inode) { + DPUT(hidden_dentry); + set_dtohd_index(dentry, bindex, NULL); + } else { + set_dtohd_index(dentry, bindex, hidden_dentry); + set_itohi_index(dentry->d_inode, bindex, + IGRAB(hidden_dentry->d_inode)); + } + + out: + print_exit_status(err); + return err; +} + +/* + * + * vim:shiftwidth=8 + * vim:tabstop=8 + * + * For Emacs: + * Local variables: + * c-basic-offset: 8 + * c-comment-only-line-offset: 0 + * c-offsets-alist: ((statement-block-intro . +) (knr-argdecl-intro . 0) + * (substatement-open . 0) (label . 0) (statement-cont . +)) + * indent-tabs-mode: t + * tab-width: 8 + * End: + */ diff -urN linux-2.6.17-rc1-mm3-orig/fs/unionfs/super.c linux-2.6.17-rc1-mm3-new/fs/unionfs/super.c --- linux-2.6.17-rc1-mm3-orig/fs/unionfs/super.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/unionfs/super.c 2006-04-11 20:24:21.000000000 +0000 @@ -0,0 +1,526 @@ +/* + * Copyright (c) 2003-2005 Erez Zadok + * Copyright (c) 2003-2006 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2006 Josef Sipek + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2005-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2006 Stony Brook University + * Copyright (c) 2003-2006 The Research Foundation of State University of New York + * + * For specific licensing information, see the COPYING file distributed with + * this package. + * + * This Copyright notice must be kept intact and distributed with all sources. + */ +/* + * $Id: super.c,v 1.89 2006/02/20 22:23:01 dquigley Exp $ + */ + +#include "unionfs.h" + +/* The inode cache is used with alloc_inode for both our inode info and the + * vfs inode. */ +static kmem_cache_t *unionfs_inode_cachep; + +static void unionfs_read_inode(struct inode *inode) +{ + static struct address_space_operations unionfs_empty_aops; + int size; + + print_entry_location(); + + if (!itopd(inode)) { + printk(KERN_ERR + "No kernel memory when allocating inode private data!\n"); + BUG(); + } + + memset(itopd(inode), 0, sizeof(struct unionfs_inode_info)); + itopd(inode)->b_start = -1; + itopd(inode)->b_end = -1; + atomic_set(&itopd(inode)->uii_generation, + atomic_read(&stopd(inode->i_sb)->usi_generation)); + itopd(inode)->uii_rdlock = SPIN_LOCK_UNLOCKED; + itopd(inode)->uii_rdcount = 1; + itopd(inode)->uii_hashsize = -1; + INIT_LIST_HEAD(&itopd(inode)->uii_readdircache); + + size = sbmax(inode->i_sb) * sizeof(struct inode *); + itohi_ptr(inode) = KZALLOC(size, GFP_KERNEL); + if (!itohi_ptr(inode)) { + printk(KERN_ERR + "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; + /* I don't think ->a_ops is ever allowed to be NULL */ + inode->i_mapping->a_ops = &unionfs_empty_aops; + fist_dprint(7, "setting inode 0x%p a_ops to empty (0x%p)\n", + inode, inode->i_mapping->a_ops); + + print_exit_location(); +} + +static void unionfs_put_inode(struct inode *inode) +{ + print_entry_location(); + fist_dprint(8, "%s i_count = %d, i_nlink = %d\n", __FUNCTION__, + atomic_read(&inode->i_count), inode->i_nlink); + /* + * This is really funky stuff: + * Basically, if i_count == 1, iput will then decrement it and this inode will be destroyed. + * It is currently holding a reference to the hidden inode. + * Therefore, it needs to release that reference by calling iput on the hidden inode. + * iput() _will_ do it for us (by calling our clear_inode), but _only_ if i_nlink == 0. + * The problem is, NFS keeps i_nlink == 1 for silly_rename'd files. + * So we must for our i_nlink to 0 here to trick iput() into calling our clear_inode. + */ + if (atomic_read(&inode->i_count) == 1) + inode->i_nlink = 0; + print_exit_location(); +} + +/* + * 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. + */ +static void unionfs_delete_inode(struct inode *inode) +{ + print_entry_location(); + + fist_checkinode(inode, "unionfs_delete_inode IN"); + inode->i_size = 0; /* every f/s seems to do that */ + clear_inode(inode); + + print_exit_location(); +} + +/* final actions when unmounting a file system */ +static void unionfs_put_super(struct super_block *sb) +{ + int bindex, bstart, bend; + struct unionfs_sb_info *spd; + + print_entry_location(); + + if ((spd = stopd(sb))) { + /* XXX: Free persistent inode stuff. */ +#ifdef UNIONFS_IMAP + cleanup_imap_data(sb); +#endif + bstart = sbstart(sb); + bend = sbend(sb); + for (bindex = bstart; bindex <= bend; bindex++) + mntput(stohiddenmnt_index(sb, bindex)); + + /* Make sure we have no leaks of branchget/branchput. */ + for (bindex = bstart; bindex <= bend; bindex++) + BUG_ON(branch_count(sb, bindex) != 0); + + KFREE(spd->usi_data); + KFREE(spd); + stopd_lhs(sb) = NULL; + } + fist_dprint(6, "unionfs: released super\n"); + + print_exit_location(); +} + +static int unionfs_statfs(struct super_block *sb, struct kstatfs *buf) +{ + int err = 0; + struct super_block *hidden_sb; + struct kstatfs rsb; + int bindex, bindex1, bstart, bend; + + print_entry_location(); + memset(buf, 0, sizeof(struct kstatfs)); + buf->f_type = UNIONFS_SUPER_MAGIC; + + buf->f_frsize = 0; + buf->f_namelen = 0; + + bstart = sbstart(sb); + bend = sbend(sb); + + for (bindex = bstart; bindex <= bend; bindex++) { + int dup = 0; + + hidden_sb = stohs_index(sb, bindex); + /* Ignore duplicate super blocks. */ + for (bindex1 = bstart; bindex1 < bindex; bindex1++) { + if (hidden_sb == stohs_index(sb, bindex1)) { + dup = 1; + break; + } + } + if (dup) { + continue; + } + + err = vfs_statfs(hidden_sb, &rsb); + fist_dprint(8, + "adding values for bindex:%d bsize:%d blocks:%d bfree:%d bavail:%d\n", + bindex, (int)rsb.f_bsize, (int)rsb.f_blocks, + (int)rsb.f_bfree, (int)rsb.f_bavail); + + if (!buf->f_frsize) + buf->f_frsize = rsb.f_frsize; + if (!buf->f_namelen) { + buf->f_namelen = rsb.f_namelen; + } else { + if (buf->f_namelen > rsb.f_namelen) + buf->f_namelen = rsb.f_namelen; + } + if (!buf->f_bsize) { + buf->f_bsize = rsb.f_bsize; + } else { + if (buf->f_bsize < rsb.f_bsize) { + int shifter = 0; + while (buf->f_bsize < rsb.f_bsize) { + shifter++; + rsb.f_bsize >>= 1; + } + rsb.f_blocks <<= shifter; + rsb.f_bfree <<= shifter; + rsb.f_bavail <<= shifter; + } else { + int shifter = 0; + while (buf->f_bsize > rsb.f_bsize) { + shifter++; + rsb.f_bsize <<= 1; + } + rsb.f_blocks >>= shifter; + rsb.f_bfree >>= shifter; + rsb.f_bavail >>= shifter; + } + } + buf->f_blocks += rsb.f_blocks; + buf->f_bfree += rsb.f_bfree; + buf->f_bavail += rsb.f_bavail; + buf->f_files += rsb.f_files; + buf->f_ffree += rsb.f_ffree; + } + buf->f_namelen -= WHLEN; + + memset(&buf->f_fsid, 0, sizeof(__kernel_fsid_t)); + memset(&buf->f_spare, 0, sizeof(buf->f_spare)); + print_exit_status(err); + return err; +} + +static int do_binary_remount(struct super_block *sb, int *flags, char *data) +{ + unsigned long *uldata = (unsigned long *)data; + int err; + + uldata++; + + switch (*uldata) { + case UNIONFS_IOCTL_DELBRANCH: + err = unionfs_ioctl_delbranch(sb, *(uldata + 1)); + break; + default: + err = -ENOTTY; + } + + return err; +} + +/* We don't support a standard text remount, but we do have a magic remount + * for unionctl. The idea is that you can remove a branch without opening + * the union. Eventually it would be nice to support a full-on remount, so + * that you can have all of the directories change at once, but that would + * require some pretty complicated matching code. */ +static int unionfs_remount_fs(struct super_block *sb, int *flags, char *data) +{ + if (data && *((unsigned long *)data) == UNIONFS_REMOUNT_MAGIC) + return do_binary_remount(sb, flags, data); + return -ENOSYS; +} + +/* + * Called by iput() when the inode reference count reached zero + * and the inode is not hashed anywhere. Used to clear anything + * that needs to be, before the inode is completely destroyed and put + * on the inode free list. + */ +static void unionfs_clear_inode(struct inode *inode) +{ + int bindex, bstart, bend; + struct inode *hidden_inode; + struct list_head *pos, *n; + struct unionfs_dir_state *rdstate; + + print_entry_location(); + + fist_checkinode(inode, "unionfs_clear_inode IN"); + + list_for_each_safe(pos, n, &itopd(inode)->uii_readdircache) { + rdstate = list_entry(pos, struct unionfs_dir_state, uds_cache); + list_del(&rdstate->uds_cache); + free_rdstate(rdstate); + } + + /* Decrement a reference to a hidden_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++) { + hidden_inode = itohi_index(inode, bindex); + if (!hidden_inode) + continue; + IPUT(hidden_inode); + } + } + // XXX: why this assertion fails? + // because it doesn't like us + // BUG_ON((inode->i_state & I_DIRTY) != 0); + KFREE(itohi_ptr(inode)); + itohi_ptr(inode) = NULL; + + print_exit_location(); +} + +static struct inode *unionfs_alloc_inode(struct super_block *sb) +{ + struct unionfs_inode_container *c; + + print_entry_location(); + + c = (struct unionfs_inode_container *) + kmem_cache_alloc(unionfs_inode_cachep, SLAB_KERNEL); + if (!c) { + print_exit_pointer(NULL); + return NULL; + } + + memset(&c->info, 0, sizeof(c->info)); + + c->vfs_inode.i_version = 1; + print_exit_pointer(&c->vfs_inode); + return &c->vfs_inode; +} + +static void unionfs_destroy_inode(struct inode *inode) +{ + print_entry("inode = %p", inode); + kmem_cache_free(unionfs_inode_cachep, itopd(inode)); + print_exit_location(); +} + +static void init_once(void *v, kmem_cache_t * cachep, unsigned long flags) +{ + struct unionfs_inode_container *c = (struct unionfs_inode_container *)v; + + print_entry_location(); + + if ((flags & (SLAB_CTOR_VERIFY | SLAB_CTOR_CONSTRUCTOR)) == + SLAB_CTOR_CONSTRUCTOR) + inode_init_once(&c->vfs_inode); + + print_exit_location(); +} + +int init_inode_cache(void) +{ + int err = 0; + + print_entry_location(); + + unionfs_inode_cachep = + kmem_cache_create("unionfs_inode_cache", + sizeof(struct unionfs_inode_container), 0, + SLAB_RECLAIM_ACCOUNT, init_once, NULL); + if (!unionfs_inode_cachep) + err = -ENOMEM; + print_exit_status(err); + return err; +} + +void destroy_inode_cache(void) +{ + print_entry_location(); + if (!unionfs_inode_cachep) + goto out; + if (kmem_cache_destroy(unionfs_inode_cachep)) + printk(KERN_ERR + "unionfs_inode_cache: not all structures were freed\n"); + out: + print_exit_location(); + return; +} + +/* Called when we have a dirty inode, right here we only throw out + * parts of our readdir list that are too old. + */ +static int unionfs_write_inode(struct inode *inode, int sync) +{ + struct list_head *pos, *n; + struct unionfs_dir_state *rdstate; + + print_entry_location(); + + spin_lock(&itopd(inode)->uii_rdlock); + list_for_each_safe(pos, n, &itopd(inode)->uii_readdircache) { + rdstate = list_entry(pos, struct unionfs_dir_state, uds_cache); + /* We keep this list in LRU order. */ + if ((rdstate->uds_access + RDCACHE_JIFFIES) > jiffies) + break; + itopd(inode)->uii_rdcount--; + list_del(&rdstate->uds_cache); + free_rdstate(rdstate); + } + spin_unlock(&itopd(inode)->uii_rdlock); + + print_exit_location(); + return 0; +} + +/* + * Called in do_umount() if the MNT_FORCE flag was used and this + * function is defined. See comment in linux/fs/super.c:do_umount(). + * Used only in nfs, to kill any pending RPC tasks, so that subsequent + * code can actually succeed and won't leave tasks that need handling. + * + * PS. I wonder if this is somehow useful to undo damage that was + * left in the kernel after a user level file server (such as amd) + * dies. + */ +static void unionfs_umount_begin(struct super_block *sb) +{ + struct super_block *hidden_sb; + int bindex, bstart, bend; + + print_entry_location(); + + bstart = sbstart(sb); + bend = sbend(sb); + for (bindex = bstart; bindex <= bend; bindex++) { + hidden_sb = stohs_index(sb, bindex); + if (hidden_sb && hidden_sb->s_op && + hidden_sb->s_op->umount_begin) + hidden_sb->s_op->umount_begin(hidden_sb); + } + print_exit_location(); +} + +static int unionfs_show_options(struct seq_file *m, struct vfsmount *mnt) +{ + struct super_block *sb = mnt->mnt_sb; + int ret = 0; + unsigned long tmp = 0; + char *hidden_path; + int bindex, bstart, bend; + int perms; + + lock_dentry(sb->s_root); + + tmp = __get_free_page(GFP_KERNEL); + if (!tmp) { + ret = -ENOMEM; + goto out; + } + + bindex = bstart = sbstart(sb); + bend = sbend(sb); + + seq_printf(m, ",dirs="); + for (bindex = bstart; bindex <= bend; bindex++) { + hidden_path = + d_path(dtohd_index(sb->s_root, bindex), + stohiddenmnt_index(sb, bindex), (char *)tmp, + PAGE_SIZE); + perms = branchperms(sb, bindex); + seq_printf(m, "%s=%s", hidden_path, + perms & MAY_WRITE ? "rw" : + perms & MAY_NFSRO ? "nfsro" : "ro"); + if (bindex != bend) { + seq_printf(m, ":"); + } + } + + seq_printf(m, ",debug=%d", fist_get_debug_value()); + +#ifdef UNIONFS_DELETE_ALL + if (IS_SET(sb, DELETE_ALL)) + seq_printf(m, ",delete=all"); + else +#endif + seq_printf(m, ",delete=whiteout"); + if (IS_SET(sb, COPYUP_CURRENT_USER)) + seq_printf(m, ",copyup=currentuser"); + else + seq_printf(m, ",copyup=preserve"); + + out: + if (tmp) + free_page(tmp); + unlock_dentry(sb->s_root); + return ret; +} + +/** + * The entry given by dentry here is always a directory. + * We can't just do dentry->d_parent, because it may + * not be there, since this dentry could have been + * created by calling d_alloc_anon. + */ +static struct dentry *unionfs_get_parent(struct dentry *dentry) +{ + + struct dentry *ret; + struct inode *childinode; + childinode = dentry->d_inode; + ret = d_find_alias(childinode); + return ret; +} + +struct export_operations unionfs_export_ops = { + .get_parent = unionfs_get_parent +}; + +struct super_operations unionfs_sops = { + .read_inode = unionfs_read_inode, + .put_inode = unionfs_put_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, +#ifdef SPLIT_VIEW_CACHES + .select_super = unionfs_select_super, +#endif +}; + +/* + * + * vim:shiftwidth=8 + * vim:tabstop=8 + * + * For Emacs: + * Local variables: + * c-basic-offset: 8 + * c-comment-only-line-offset: 0 + * c-offsets-alist: ((statement-block-intro . +) (knr-argdecl-intro . 0) + * (substatement-open . 0) (label . 0) (statement-cont . +)) + * indent-tabs-mode: t + * tab-width: 8 + * End: + */ diff -urN linux-2.6.17-rc1-mm3-orig/fs/unionfs/unionctl.c linux-2.6.17-rc1-mm3-new/fs/unionfs/unionctl.c --- linux-2.6.17-rc1-mm3-orig/fs/unionfs/unionctl.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/unionfs/unionctl.c 2006-04-11 20:24:21.000000000 +0000 @@ -0,0 +1,522 @@ +/* + * Copyright (c) 2003-2006 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2006 Josef Sipek + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2005-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2006 Stony Brook University + * Copyright (c) 2003-2006 The Research Foundation of State University of New York + * + * For specific licensing information, see the COPYING file distributed with + * this package. + * + * This Copyright notice must be kept intact and distributed with all sources. + */ +/* + * $Id: unionctl.c,v 1.40 2006/01/21 02:58:11 jsipek Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "unionfs.h" + +#define MAY_READ 4 +#define MAY_WRITE 2 + +extern int find_union(const char *path, char **options, char **actual_path, + int uniononly); +static char **branches; +static int *branchperms; +static const char *progname; + +void __attribute__ ((__noreturn__)) __usage(int line); + +#define usage() __usage(__LINE__) + +void __attribute__ ((__noreturn__)) __usage(int line) +{ +#ifdef DEBUG + fprintf(stderr, "Line: %d\n", line); +#endif + fprintf(stderr, + "unionctl version: $Id: unionctl.c,v 1.40 2006/01/21 02:58:11 jsipek Exp $\n"); + fprintf(stderr, "Distributed with Unionfs " UNIONFS_VERSION "\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "unionctl UNION ACTION [arguments]\n"); + fprintf(stderr, + "ACTION can be --add, --remove, --mode, --list, or --query.\nFurther arguments depend on ACTION.\n"); + fprintf(stderr, + "\tunionctl UNION --add [ --before BRANCH | --after BRANCH ] [ --mode (rw|ro|nfsro) ] DIRECTORY\n"); + fprintf(stderr, "\tunionctl UNION --remove BRANCH\n"); + fprintf(stderr, "\tunionctl UNION --mode BRANCH (rw|ro|nfsro)\n"); + fprintf(stderr, "\tunionctl UNION --list\n"); + fprintf(stderr, "\tunionctl FILENAME --query\n"); + fprintf(stderr, + "The unionctl man page has a more detailed description of its usage.\n"); + exit(EXIT_FAILURE); +} + +static inline int parse_rw(char *p) +{ + if (strcmp(p, "ro") == 0) + return MAY_READ; + else if (strcmp(p, "nfsro") == 0) + return MAY_READ | MAY_NFSRO; + else if (strcmp(p, "rw") == 0) + return MAY_READ | MAY_WRITE; + else + return 0; +} + +static char **parse_options(char *options) +{ + char **ret = NULL; + int i = 0; + + char *p; + char *q; + char *r; + char *s, *t, *u; + + p = options; + do { + q = strchr(p, ','); + if (q) { + *q++ = '\0'; + } + if (!strncmp(p, "dirs=", strlen("dirs="))) { + r = p + strlen("dirs="); + do { + s = strchr(r, ':'); + if (s) { + *s++ = '\0'; + } + + i++; + ret = realloc(ret, sizeof(char *) * (i + 1)); + if (!ret) { + perror("realloc()"); + exit(EXIT_FAILURE); + } + branchperms = + realloc(branchperms, sizeof(int) * i); + if (!branchperms) { + perror("realloc()"); + exit(EXIT_FAILURE); + } + + t = strchr(r, '='); + u = t + 1; + if (!t || !u || !*u) + goto err; + *t = 0; + branchperms[i - 1] = parse_rw(u); + if (!branchperms[i - 1]) { + err: + fprintf(stderr, "cannot parse '%s'\n", + r); + exit(EXIT_FAILURE); + } + ret[i - 1] = strdup(r); + ret[i] = NULL; + + r = s; + } + while (r); + } + p = q; + } + while (p); + + branches = ret; + return ret; +} + +static int get_branch(char *path) +{ + char *end; + int ret; + + ret = strtol(path, &end, 0); + if (!*end) { + return ret; + } + + ret = strlen(path); + if ((ret > 1) && (path[ret - 1] == '/')) { + path[ret - 1] = '\0'; + } + + ret = 0; + while (branches[ret]) { + if (!strcmp(branches[ret], path)) { + return ret; + } + ret++; + } + + return -1; +} + +static void dump_branches(const char *prefix) +{ + int i = 0; + + while (branches[i]) { + char r, w, n; + r = (branchperms[i] & MAY_READ) ? 'r' : '-'; + w = (branchperms[i] & MAY_WRITE) ? 'w' : '-'; + n = (branchperms[i] & MAY_NFSRO) ? 'n' : '-'; + printf("%s%s (%c%c%c)\n", prefix, branches[i], r, w, n); + i++; + } +} + +#define ADD 1 +#define REMOVE 2 +#define MODE 3 +#define LIST 4 +#define QUERY 5 + +int main(int argc, char *argv[]) +{ + struct unionfs_addbranch_args addargs; + struct unionfs_rdwrbranch_args rdwrargs; + unsigned long remountdata[3]; + fd_set branchlist; + struct stat st; + + int fd = -1; + int ret, i; + + char *path, resolv_path[PATH_MAX], resolv_bp[PATH_MAX]; + char *options = NULL, *actual_path = NULL; + int action; + + char *branchpath; + int branchnum; + int unionpos = 1; + int modepos = 2; + + progname = argv[0]; + + /* check that minimum number of args were specified */ + if (argc < 3) + usage(); + + if (argv[1][0] == '-' && argv[1][1] == '-') { + modepos = 1; + unionpos = 2; + } else { + modepos = 2; + unionpos = 1; + } + + if (realpath(argv[unionpos], resolv_path) == NULL) { + perror("realpath()"); + exit(EXIT_FAILURE); + } + path = resolv_path; + if (strcmp(path, "/") && (path[strlen(path) - 1] == '/')) { + path[strlen(path) - 1] = '\0'; + } + + if (!strcmp(argv[modepos], "--add")) { + action = ADD; + } else if (!strcmp(argv[modepos], "--remove")) { + action = REMOVE; + } else if (!strcmp(argv[modepos], "--mode")) { + action = MODE; + } else if (!strcmp(argv[modepos], "--list")) { + action = LIST; + } else if (!strcmp(argv[modepos], "--query")) { + action = QUERY; + } else { + usage(); + } + + if (stat(path, &st) == -1) { + fprintf(stderr, "stat(%s): %s\n", path, strerror(errno)); + exit(EXIT_FAILURE); + } + + if (find_union(path, &options, &actual_path, 1) < 0) { + fprintf(stderr, "%s is not a valid union.\n", path); + exit(EXIT_FAILURE); + } + branches = parse_options(options); + if (!branches) { + fprintf(stderr, "Could not parse options from /proc/mounts!\n"); + exit(EXIT_FAILURE); + } + + /* open file on which ioctl will operate (that is actually any file in the union) */ + if (action != REMOVE) { + fd = open(path, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "open(%s): %s\n", path, + strerror(errno)); + exit(EXIT_FAILURE); + } + } + + /* Parse the action's options into something usable, and do it. */ + switch (action) { + case ADD: + if (argc < 4) + usage(); + + /* Default values if the user leaves them unspecified. */ + branchnum = -1; + addargs.ab_perms = MAY_READ | MAY_WRITE; + branchpath = NULL; + for (i = 3; i < argc; i++) { + if (argv[i][0] == '-' && argv[i][1] == '-') { + if (!strcmp(argv[i], "--before")) { + i++; + if (i == argc) { + fprintf(stderr, + "%s requires an argument!\n", + argv[i - 1]); + usage(); + } + + branchnum = get_branch(argv[i]); + if (branchnum == -1) { + fprintf(stderr, + "%s is not a valid branch.\nThe current branch configuration is:\n", + argv[i]); + dump_branches("\t"); + } + } else if (!strcmp(argv[i], "--after")) { + i++; + if (i == argc) { + fprintf(stderr, + "%s requires an argument!\n", + argv[i - 1]); + usage(); + } + + branchnum = get_branch(argv[i]); + if (branchnum == -1) { + fprintf(stderr, + "%s is not a valid branch.\nThe current branch configuration is:\n", + argv[i]); + dump_branches("\t"); + } + branchnum++; + } else if (!strcmp(argv[i], "--mode")) { + i++; + if (i == argc) { + fprintf(stderr, + "%s requires an argument!\n", + argv[i - 1]); + usage(); + } + + addargs.ab_perms = parse_rw(argv[i]); + if (!addargs.ab_perms) { + fprintf(stderr, + "Valid modes are ro, nfsro and rw you specified: \"%s\"\n", + argv[i]); + usage(); + } + } else { + fprintf(stderr, "Unknown option: %s\n", + argv[i]); + usage(); + } + } else { + int branchchk = -1; + /* The options must go before the path */ + if ((i + 1) != argc) { + fprintf(stderr, + "The path of the branch to add must go after all options.\n"); + usage(); + } + branchchk = get_branch(argv[i]); + if (branchchk != -1) { + fprintf(stderr, + "%s already exists in the Union.\n", + argv[i]); + usage(); + } + if (branchnum == -1) + branchnum = 0; + branchpath = argv[i]; + } + } + if (!branchpath) { + fprintf(stderr, + "You must specify the path to add into the union!\n"); + usage(); + } + + if (realpath(branchpath, resolv_bp) == NULL) { + perror("realpath()"); + exit(EXIT_FAILURE); + } + + addargs.ab_branch = branchnum; + addargs.ab_path = resolv_bp; + + errno = 0; + ret = ioctl(fd, UNIONFS_IOCTL_ADDBRANCH, &addargs); + if (ret < 0) { + switch (errno) { + case E2BIG: + fprintf(stderr, + "Unionfs supports only %d branches.\n", + FD_SETSIZE); + break; + } + fprintf(stderr, "Failed to add %s into %s: %s", + branchpath, path, strerror(errno)); + exit(EXIT_FAILURE); + } + break; + case MODE: + if (argc != 5) { + usage(); + } + + branchnum = 3; + rdwrargs.rwb_perms = parse_rw(argv[4]); + if (!rdwrargs.rwb_perms) { + branchnum = 4; + rdwrargs.rwb_perms = parse_rw(argv[3]); + if (!rdwrargs.rwb_perms) { + usage(); + exit(EXIT_FAILURE); + } + } + + if (realpath(argv[branchnum], resolv_bp) == NULL) { + perror("realpath()"); + exit(EXIT_FAILURE); + } + branchpath = resolv_bp; + + /* Set a branches writeable status. */ + rdwrargs.rwb_branch = get_branch(branchpath); + if (rdwrargs.rwb_branch == -1) { + fprintf(stderr, + "%s is not a valid branch.\nThe current branch configuration is:\n", + branchpath); + dump_branches("\t"); + exit(EXIT_FAILURE); + } + + ret = ioctl(fd, UNIONFS_IOCTL_RDWRBRANCH, &rdwrargs); + if (ret < 0) { + fprintf(stderr, + "Failed to set permissions for %s in %s: %s", + branchpath, path, strerror(errno)); + exit(EXIT_FAILURE); + } + + goto out; + break; + case REMOVE: + if (argc != 4) + usage(); + + if (realpath(argv[3], resolv_bp) == NULL) { + perror("realpath()"); + exit(EXIT_FAILURE); + } + branchpath = resolv_bp; + + branchnum = get_branch(branchpath); + if (branchnum == -1) { + fprintf(stderr, + "%s is not a valid branch.\nThe current branch configuration is:\n", + branchpath); + dump_branches("\t"); + exit(EXIT_FAILURE); + } + + errno = 0; + remountdata[0] = UNIONFS_REMOUNT_MAGIC; + remountdata[1] = UNIONFS_IOCTL_DELBRANCH; + remountdata[2] = branchnum; + ret = + mount("unionfs", actual_path, "unionfs", + MS_REMOUNT | MS_MGC_VAL, remountdata); + if (ret < 0) { + fprintf(stderr, "Failed to remove %s from %s: %s", + branchpath, path, strerror(errno)); + exit(EXIT_FAILURE); + } + break; + case LIST: + dump_branches("\t"); + break; + + case QUERY: + if (argc != 3) { + usage(); + } + + if ((fd = open(argv[unionpos], O_RDONLY)) < 0) { + fprintf(stderr, + "Unable to open file %s : %s", + argv[unionpos], strerror(errno)); + exit(EXIT_FAILURE); + } + + ret = ioctl(fd, UNIONFS_IOCTL_QUERYFILE, &branchlist); + if (ret < 0) { + fprintf(stderr, + "Unable to retrieve list of branches for file %s : %s\n", + argv[unionpos], strerror(errno)); + exit(EXIT_FAILURE); + } + + for (i = 0; i <= ret; i++) { + char r, w, n; + r = (branchperms[i] & MAY_READ) ? 'r' : '-'; + w = (branchperms[i] & MAY_WRITE) ? 'w' : '-'; + n = (branchperms[i] & MAY_NFSRO) ? 'n' : '-'; + + if (FD_ISSET(i, &branchlist)) + printf("%s\t%s (%c%c%c)\n", argv[unionpos], + branches[i], r, w, n); + } + break; + } + + out: + if (fd != -1) + close(fd); + exit(EXIT_SUCCESS); +} + +/* + * + * vim:shiftwidth=8 + * vim:tabstop=8 + * + * For Emacs: + * Local variables: + * c-basic-offset: 8 + * c-comment-only-line-offset: 0 + * c-offsets-alist: ((statement-block-intro . +) (knr-argdecl-intro . 0) + * (substatement-open . 0) (label . 0) (statement-cont . +)) + * indent-tabs-mode: t + * tab-width: 8 + * End: + */ diff -urN linux-2.6.17-rc1-mm3-orig/fs/unionfs/uniondbg.c linux-2.6.17-rc1-mm3-new/fs/unionfs/uniondbg.c --- linux-2.6.17-rc1-mm3-orig/fs/unionfs/uniondbg.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/unionfs/uniondbg.c 2006-04-11 20:24:21.000000000 +0000 @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2003-2006 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2006 Josef Sipek + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2005-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2006 Stony Brook University + * Copyright (c) 2003-2006 The Research Foundation of State University of New York + * + * For specific licensing information, see the COPYING file distributed with + * this package. + * + * This Copyright notice must be kept intact and distributed with all sources. + */ +/* + * $Id: uniondbg.c,v 1.16 2006/01/13 03:00:24 jsipek Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "unionfs.h" + +#define MAY_READ 4 +#define MAY_WRITE 2 + +static int opt_d = 0; +static int opt_c = 0; +static int opt_g = 0; +static int optcount; +static const char *progname; + +void usage(void) +{ + fprintf(stderr, "Usage:\n" + "%s -d file [val]\n\tto set/get debugging values\n" + "%s -c file\n\tto print out branch reference counts (in kernel debug output)\n" + "%s -g file\n\tto increment the super block generation count\n" + "%s -s file\n\tto duplicate the super block\n", progname, + progname, progname, progname); + exit(1); +} + +int main(int argc, char *argv[]) +{ + int fd, ret, val = 0; + int i; + + progname = argv[0]; + + /* check that minimum number of args were specified */ + if (argc < 3) + usage(); + + if (strcmp(argv[1], "-d") == 0) { + if (argc > 4) + usage(); + opt_d++; + optcount++; + } + if (strcmp(argv[1], "-c") == 0) { + if (argc > 3) + usage(); + opt_c++; + optcount++; + } + if (strcmp(argv[1], "-g") == 0) { + if (argc > 3) + usage(); + opt_g++; + optcount++; + } + + /* check that at least one option was used */ + if (!optcount) + usage(); + + /* open file on which ioctl will operate */ + fd = open(argv[2], O_RDONLY); + if (fd < 0) { + perror(argv[2]); + exit(1); + } + + /* if specified 3rd arg, want to set debug level */ + if (opt_d) { + if (argc == 4) { + val = atoi(argv[3]); + ret = ioctl(fd, FIST_IOCTL_SET_DEBUG_VALUE, &val); + if (ret < 0) { + perror("ioctl set"); + exit(1); + } + } else { + ret = ioctl(fd, FIST_IOCTL_GET_DEBUG_VALUE, &val); + if (ret < 0) { + perror("ioctl get"); + exit(1); + } + printf("debug ioctl returned value %d\n", val); + } + goto out; + } + + /* branch refcounts */ + if (opt_c) { + int *counts; + ret = ioctl(fd, UNIONFS_IOCTL_BRANCH_COUNT, NULL); + if (ret < 0) { + perror("ioctl branchcount (a)"); + exit(1); + } + counts = malloc(ret * sizeof(int)); + if (!counts) { + perror("malloc"); + exit(1); + } + ret = ioctl(fd, UNIONFS_IOCTL_BRANCH_COUNT, counts); + if (ret < 0) { + perror("ioctl branchcount (b)"); + exit(1); + } + printf("%d total branches.\n", ret); + for (i = 0; i < ret; i++) { + printf("%d: %d\n", i, counts[i]); + } + free(counts); + goto out; + } + + /* Update generation number. */ + if (opt_g) { + ret = ioctl(fd, UNIONFS_IOCTL_INCGEN, NULL); + if (ret < 0) { + perror("ioctl incgen"); + exit(1); + } + printf("New generation %d\n", ret); + goto out; + } + + out: + close(fd); + exit(0); +} + +/* + * + * vim:shiftwidth=8 + * vim:tabstop=8 + * + * For Emacs: + * Local variables: + * c-basic-offset: 8 + * c-comment-only-line-offset: 0 + * c-offsets-alist: ((statement-block-intro . +) (knr-argdecl-intro . 0) + * (substatement-open . 0) (label . 0) (statement-cont . +)) + * indent-tabs-mode: t + * tab-width: 8 + * End: + */ diff -urN linux-2.6.17-rc1-mm3-orig/fs/unionfs/unionfs.h linux-2.6.17-rc1-mm3-new/fs/unionfs/unionfs.h --- linux-2.6.17-rc1-mm3-orig/fs/unionfs/unionfs.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/unionfs/unionfs.h 2006-04-11 20:24:21.000000000 +0000 @@ -0,0 +1,837 @@ +#ifndef __UNIONFS_H_ +#define __UNIONFS_H_ + +#ifdef __KERNEL__ + +#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 +#include +#include + +#include + +#include +#include +#include +#include +#include + +#ifndef UNIONFS_UNSUPPORTED +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,9)) || (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15)) +#warning You are compiling Unionfs on an unsupported kernel version. +#warning To compile Unionfs, you will need to define UNIONFS_UNSUPPORTED. +#warning Try adding: EXTRACFLAGS=-DUNIONFS_UNSUPPORTED to fistdev.mk +#endif +#endif + +#include +#define wq_write_lock_irqsave(lock,flags) spin_lock_irqsave(lock,flags) +#define wq_write_unlock(lock) spin_unlock(lock) +#define wq_write_lock_irq(lock) spin_lock_irq(lock) +#define wq_write_unlock_irqrestore(lock,flags) spin_unlock_irqrestore(lock,flags) + +/* number of characters while generating unique temporary file names */ +#define UNIONFS_TMPNAM_LEN 12 + +/* fist file systems superblock magic */ +#define UNIONFS_SUPER_MAGIC 0xf15f083d + +/* unionfs root inode number */ +#define UNIONFS_ROOT_INO 1 + +/* Mount time flags */ +#define MOUNT_FLAG(sb) (stopd(sb)->usi_mount_flag) + +/*UUID typedef needed later*/ +typedef uint8_t uuid_t[16]; + +/* 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 export_operations unionfs_export_ops; + +/* How long should an entry be allowed to persist */ +#define RDCACHE_JIFFIES 5*HZ +/* unionfs inode data in memory */ +struct unionfs_inode_info { + int b_start; + int b_end; + atomic_t uii_generation; + int uii_stale; + /* Stuff for readdir over NFS. */ + spinlock_t uii_rdlock; + struct list_head uii_readdircache; + int uii_rdcount; + int uii_hashsize; + int uii_cookie; + /* The hidden inodes */ + struct inode **uii_inode; + /* to keep track of reads/writes for unlinks before closes */ + atomic_t uii_totalopens; +}; + +struct unionfs_inode_container { + struct unionfs_inode_info info; + 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 semaphore udi_sem; + int udi_bstart; + int udi_bend; + int udi_bopaque; + int udi_bcount; + atomic_t udi_generation; + struct dentry **udi_dentry; +}; + +/* A putmap is used so that older files can still do branchput correctly. */ +struct putmap { + atomic_t count; + int bend; + int map[0]; +}; + +/* These are the pointers to our various objects. */ +struct unionfs_usi_data { + struct super_block *sb; + struct vfsmount *hidden_mnt; + atomic_t sbcount; + int branchperms; +}; + +/* unionfs super-block data in memory */ +struct unionfs_sb_info { + int b_end; + + atomic_t usi_generation; + unsigned long usi_mount_flag; + struct rw_semaphore usi_rwsem; + + struct unionfs_usi_data *usi_data; + + /* These map branch numbers for old generation numbers to the new bindex, + * so that branchput will behave properly. */ + int usi_firstputmap; + int usi_lastputmap; + struct putmap **usi_putmaps; + +#ifdef UNIONFS_IMAP + int usi_persistent; + /* These will need a lock. */ + uint64_t usi_next_avail; + uint8_t usi_num_bmapents; + struct bmapent *usi_bmap; + struct file *usi_forwardmap; + struct file **usi_reversemaps; + struct file **usi_map_table; + int *usi_bnum_table; //This is a table of branches to fsnums. +#endif +}; + +/* + * 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 + int bindex; // we can check for duplicate whiteouts and files in the same branch in order to return -EIO. + int whiteout; // is this a whiteout entry? + char iname[DNAME_INLINE_LEN_MIN]; // Inline name, so we don't need to separately kmalloc small ones +}; + +/* Cache creation/deletion routines. */ +void destroy_filldir_cache(void); +int init_filldir_cache(void); +int init_inode_cache(void); +void destroy_inode_cache(void); +int init_dentry_cache(void); +void destroy_dentry_cache(void); + +/* Initialize and free readdir-specific state. */ +int init_rdstate(struct file *file); +struct unionfs_dir_state *alloc_rdstate(struct inode *inode, int bindex); +struct unionfs_dir_state *find_rdstate(struct inode *inode, loff_t fpos); +void free_rdstate(struct unionfs_dir_state *state); +int add_filldir_node(struct unionfs_dir_state *rdstate, const char *name, + int namelen, int bindex, int whiteout); +struct filldir_node *find_filldir_node(struct unionfs_dir_state *rdstate, + const char *name, int namelen); + +struct dentry **alloc_new_dentries(int objs); +struct unionfs_usi_data *alloc_new_data(int objs); + +/* Directory hash table. */ +struct unionfs_dir_state { + unsigned int uds_cookie; /* The cookie, which is based off of uii_rdversion */ + unsigned int uds_offset; /* The entry we have returned. */ + int uds_bindex; + loff_t uds_dirpos; /* The offset within the lower level directory. */ + int uds_size; /* How big is the hash table? */ + int uds_hashentries; /* How many entries have been inserted? */ + unsigned long uds_access; + /* This cache list is used when the inode keeps us around. */ + struct list_head uds_cache; + struct list_head uds_list[0]; +}; + +/* Compat stuff.. */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14) + +# ifdef __CHECKER__ +# define __bitwise__ __attribute__((bitwise)) +# else +# define __bitwise__ +# endif + +typedef unsigned __bitwise__ gfp_t; +void *kzalloc(size_t size, gfp_t flags); +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14) */ + +#ifdef FIST_MALLOC_DEBUG + +extern void *unionfs_kzalloc(size_t size, gfp_t flags, int line, + const char *file); +extern void *unionfs_kmalloc(size_t size, gfp_t flags, int line, + const char *file); +extern void unionfs_kfree(void *ptr, int line, const char *file); + +extern struct dentry *unionfs_dget_parent(struct dentry *child, int line, + const char *file); +extern struct dentry *unionfs_dget(struct dentry *ptr, int line, + const char *file); +extern void unionfs_dput(struct dentry *ptr, int line, const char *file); +extern struct inode *unionfs_igrab(struct inode *inode, int line, char *file); +extern void unionfs_iput(struct inode *inode, int line, char *file); +extern struct inode *unionfs_iget(struct super_block *sb, unsigned long ino, + int line, char *file); +extern struct dentry *unionfs_lookup_one_len(const char *name, + struct dentry *parent, int len, + int line, const char *file); +void record_path_lookup(struct nameidata *nd, int line, const char *file); +void record_path_release(struct nameidata *nd, int line, const char *file); +struct file *unionfs_dentry_open(struct dentry *ptr, struct vfsmount *mnt, + int flags, int line, const char *file); +void record_set(struct dentry *upper, int index, struct dentry *ptr, + struct dentry *old, int line, const char *file); + +#define KZALLOC(size,flags) unionfs_kzalloc((size),(flags),__LINE__,__FILE__) +#define KMALLOC(size,flags) unionfs_kmalloc((size),(flags),__LINE__,__FILE__) +#define KFREE(ptr) unionfs_kfree((ptr),__LINE__,__FILE__) +#define DGET(d) unionfs_dget((d),__LINE__,__FILE__) +#define DPUT(d) unionfs_dput((d),__LINE__,__FILE__) +# define IPUT(a) unionfs_iput((a),__LINE__,__FILE__) +# define IGET(a,b) unionfs_iget((a),(b),__LINE__,__FILE__) +# define IGRAB(a) unionfs_igrab((a),__LINE__,__FILE__) +#define LOOKUP_ONE_LEN(name,parent,len) unionfs_lookup_one_len((name),(parent),(len),__LINE__,__FILE__) +# define RECORD_PATH_LOOKUP(nd) record_path_lookup((nd),__LINE__,__FILE__) +# define RECORD_PATH_RELEASE(nd) record_path_release((nd),__LINE__,__FILE__) +/* This has the effect of reducing the reference count sooner or later, + * if the file is closed. If it isn't then the mount will be busy and + * you can't unmount. + */ +# define DENTRY_OPEN(d,m,f) unionfs_dentry_open((d),(m),(f),__LINE__,__FILE__) +# define GET_PARENT(dentry) unionfs_dget_parent((dentry),__LINE__,__FILE__) +#else /* not FIST_MALLOC_DEBUG */ +# define KZALLOC(a,b) kzalloc((a),(b)) +# define KMALLOC(a,b) kmalloc((a),(b)) +# define KFREE(a) kfree((a)) +# define DPUT(a) dput((a)) +# define DGET(a) dget((a)) +# define IPUT(a) iput((a)) +# define IGET(a,b) iget((a),(b)) +# define IGRAB(a) igrab((a)) +# define LOOKUP_ONE_LEN(a,b,c) lookup_one_len((a),(b),(c)) +# define RECORD_PATH_LOOKUP(a) +# define RECORD_PATH_RELEASE(a) +# define DENTRY_OPEN(d,m,f) dentry_open((d),(m),(f)) +# define GET_PARENT(d) dget_parent(d) +#endif /* not FIST_MALLOC_DEBUG */ + +/* 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->uds_cookie & MAXRDCOOKIE) << RDOFFBITS) | (buf-> + uds_offset & + DIREOF); + return tmp; +} + +/* file private data. */ +struct unionfs_file_info { + int b_start; + int b_end; + atomic_t ufi_generation; + + struct unionfs_dir_state *rdstate; + struct file **ufi_file; +}; + +/* File to private Data */ +#define ftopd(file) ((struct unionfs_file_info *)((file)->private_data)) +#define ftopd_lhs(file) ((file)->private_data) +#define ftohf_ptr(file) (ftopd(file)->ufi_file) +#define fbstart(file) (ftopd(file)->b_start) +#define fbend(file) (ftopd(file)->b_end) + +/* Inode to private data */ +static inline struct unionfs_inode_info *itopd(const struct inode *inode) +{ + return + &(container_of(inode, struct unionfs_inode_container, vfs_inode)-> + info); +} + +#define itohi_ptr(ino) (itopd(ino)->uii_inode) +#define ibstart(ino) (itopd(ino)->b_start) +#define ibend(ino) (itopd(ino)->b_end) + +/* Superblock to private data */ +#define stopd(super) ((struct unionfs_sb_info *)(super)->s_fs_info) +#define stopd_lhs(super) ((super)->s_fs_info) +#define sbstart(sb) 0 +#define sbend(sb) stopd(sb)->b_end +#define sbmax(sb) (stopd(sb)->b_end + 1) + +#define unionfs_read_lock(sb) down_read(&stopd(sb)->usi_rwsem) +#define unionfs_read_unlock(sb) up_read(&stopd(sb)->usi_rwsem) +#define unionfs_write_lock(sb) down_write(&stopd(sb)->usi_rwsem) +#define unionfs_write_unlock(sb) up_write(&stopd(sb)->usi_rwsem) + +/* The UNIONFS_NDEBUG versions are defines, the debug versions are inline + * functions with extra checks. */ +#ifdef UNIONFS_NDEBUG +#include "unionfs_macros.h" +#else +#include "unionfs_debugmacros.h" +#endif + +/* The double lock function needs to go after the debugmacros, so that + * dtopd is defined. */ +static inline void double_lock_dentry(struct dentry *d1, struct dentry *d2) +{ + if (d2 < d1) { + struct dentry *tmp = d1; + d1 = d2; + d2 = tmp; + } + lock_dentry(d1); + lock_dentry(d2); +} + +extern int new_dentry_private_data(struct dentry *dentry); +void free_dentry_private_data(struct unionfs_dentry_info *udi); +void update_bstart(struct dentry *dentry); +#define sbt(sb) ((sb)->s_type->name) + +/* + * EXTERNALS: + */ +/* replicates the directory structure upto given dentry in given branch */ +extern struct dentry *create_parents(struct inode *dir, struct dentry *dentry, + int bindex); +struct dentry *create_parents_named(struct inode *dir, struct dentry *dentry, + const char *name, int bindex); + +/* check if two branches overlap */ +extern int is_branch_overlap(struct dentry *dent1, struct dentry *dent2); + +/* partial lookup */ +extern int unionfs_partial_lookup(struct dentry *dentry); + +/* Pass an unionfs dentry and an index and it will try to create a whiteout in branch 'index'. + On error, it will proceed to a branch to the left */ +extern int create_whiteout(struct dentry *dentry, int start); +extern int create_whiteout_parent(struct dentry *parent_dentry, + const char *filename, int start); +/* 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, struct file **copyup_file, loff_t len); +extern int copyup_named_dentry(struct inode *dir, struct dentry *dentry, + int bstart, int new_bindex, const char *name, + int namelen, struct file **copyup_file, + loff_t len); + +extern int remove_whiteouts(struct dentry *dentry, struct dentry *hidden_dentry, + int bindex); + +/* Is this directory empty: 0 if it is empty, -ENOTEMPTY if not. */ +extern int check_empty(struct dentry *dentry, + struct unionfs_dir_state **namelist); +/* Delete whiteouts from this directory in branch bindex. */ +extern int delete_whiteouts(struct dentry *dentry, int bindex, + struct unionfs_dir_state *namelist); + +/* Re-lookup a hidden dentry. */ +extern int unionfs_refresh_hidden_dentry(struct dentry *dentry, int bindex); + +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, int 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); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11) +extern long unionfs_ioctl(struct file *file, unsigned int cmd, + unsigned long arg); +#else +extern int unionfs_ioctl(struct inode *unused, struct file *file, + unsigned int cmd, unsigned long arg); +#endif + +/* Inode operations */ +extern int unionfs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry); +int unionfs_unlink(struct inode *dir, struct dentry *dentry); +int unionfs_rmdir(struct inode *dir, struct dentry *dentry); + +int unionfs_d_revalidate(struct dentry *dentry, struct nameidata *nd); + +/* 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 int unionfs_interpose(struct dentry *this_dentry, struct super_block *sb, + int flag); + +/* Branch management ioctls. */ +int unionfs_ioctl_branchcount(struct file *file, unsigned int cmd, + unsigned long arg); +int unionfs_ioctl_incgen(struct file *file, unsigned int cmd, + unsigned long arg); +int unionfs_ioctl_addbranch(struct inode *inode, unsigned int cmd, + unsigned long arg); +int unionfs_ioctl_delbranch(struct super_block *sb, unsigned long arg); +int unionfs_ioctl_rdwrbranch(struct inode *inode, unsigned int cmd, + unsigned long arg); +int unionfs_ioctl_queryfile(struct file *file, unsigned int cmd, + unsigned long arg); + +/* Verify that a branch is valid. */ +int check_branch(struct nameidata *nd); + +/* Extended attribute functions. */ +extern void *xattr_alloc(size_t size, size_t limit); +extern void xattr_free(void *ptr, size_t size); + +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); + +int unionfs_setxattr(struct dentry *dentry, const char *name, const void *value, + size_t size, int flags); + +#define copy_inode_size(dst, src) \ + dst->i_size = src->i_size; \ + dst->i_blocks = src->i_blocks; + +/* 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); +} + +/* returns the sum of the n_link values of all the underlying inodes of the passed inode */ +static inline int get_nlinks(struct inode *inode) +{ + int sum_nlinks = 0; + int dirs = 0; + int bindex; + struct inode *hidden_inode; + + if (!S_ISDIR(inode->i_mode)) + return itohi(inode)->i_nlink; + + for (bindex = ibstart(inode); bindex <= ibend(inode); bindex++) { + hidden_inode = itohi_index(inode, bindex); + if (!hidden_inode || !S_ISDIR(hidden_inode->i_mode)) + continue; + BUG_ON(hidden_inode->i_nlink < 0); + + /* A deleted directory. */ + if (hidden_inode->i_nlink == 0) + continue; + dirs++; + /* A broken directory (e.g., squashfs). */ + if (hidden_inode->i_nlink == 1) + sum_nlinks += 2; + else + sum_nlinks += (hidden_inode->i_nlink - 2); + } + + if (!dirs) + return 0; + return sum_nlinks + 2; +} + +static inline void fist_copy_attr_atime(struct inode *dest, + const struct inode *src) +{ + dest->i_atime = src->i_atime; +} +static inline void fist_copy_attr_times(struct inode *dest, + const struct inode *src) +{ + dest->i_atime = src->i_atime; + dest->i_mtime = src->i_mtime; + dest->i_ctime = src->i_ctime; +} +static inline void fist_copy_attr_timesizes(struct inode *dest, + const struct inode *src) +{ + dest->i_atime = src->i_atime; + dest->i_mtime = src->i_mtime; + dest->i_ctime = src->i_ctime; + copy_inode_size(dest, src); +} +static inline void fist_copy_attr_all(struct inode *dest, + const struct inode *src) +{ + + print_entry_location(); + dest->i_mode = src->i_mode; + /* we do not need to copy if the file is a deleted file */ + if (dest->i_nlink > 0) + dest->i_nlink = get_nlinks(dest); + dest->i_uid = src->i_uid; + dest->i_gid = src->i_gid; + dest->i_rdev = src->i_rdev; + dest->i_atime = src->i_atime; + dest->i_mtime = src->i_mtime; + dest->i_ctime = src->i_ctime; + dest->i_blksize = src->i_blksize; + dest->i_blkbits = src->i_blkbits; + copy_inode_size(dest, src); + + //DQ: This was a change I noticed in the templates. In 2.6 they removedi_attr_flags. + //Which makes me think they rolled it into flags. + dest->i_flags = src->i_flags; + + print_exit_location(); +} + +struct dentry *unionfs_lookup_backend(struct dentry *dentry, int lookupmode); +int is_stale_inode(struct inode *inode); +void make_stale_inode(struct inode *inode); + +#define IS_SET(sb, check_flag) (check_flag & MOUNT_FLAG(sb)) + +/* 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(struct super_block *sb, int index) +{ + BUG_ON(index < 0); + + return stopd(sb)->usi_data[index].branchperms; +} +static inline int set_branchperms(struct super_block *sb, int index, int perms) +{ + BUG_ON(index < 0); + + stopd(sb)->usi_data[index].branchperms = perms; + + return perms; +} + +/* Is this file on a read-only branch? */ +static inline int __is_robranch_super(struct super_block *sb, int index, + char *file, const char *function, + int line) +{ + int err = 0; + + print_util_entry_location(); + + if (!(branchperms(sb, index) & MAY_WRITE)) + err = -EROFS; + + print_util_exit_status(err); + return err; +} + +/* Is this file on a read-only branch? */ +static inline int __is_robranch_index(struct dentry *dentry, int index, + char *file, const char *function, + int line) +{ + int err = 0; + int perms; + + print_util_entry_location(); + + BUG_ON(index < 0); + + perms = stopd(dentry->d_sb)->usi_data[index].branchperms; + + if ((!(perms & MAY_WRITE)) + || (IS_RDONLY(dtohd_index(dentry, index)->d_inode))) + err = -EROFS; + + print_util_exit_status(err); + + return err; +} +static inline int __is_robranch(struct dentry *dentry, char *file, + const char *function, int line) +{ + int index; + int err; + + print_util_entry_location(); + + index = dtopd(dentry)->udi_bstart; + BUG_ON(index < 0); + + err = __is_robranch_index(dentry, index, file, function, line); + + print_util_exit_status(err); + + return err; +} + +#define is_robranch(d) __is_robranch(d, __FILE__, __FUNCTION__, __LINE__) +#define is_robranch_super(s, n) __is_robranch_super(s, n, __FILE__, __FUNCTION__, __LINE__) + +/* What do we use for whiteouts. */ +#define WHPFX ".wh." +#define WHLEN 4 +/* If a directory contains this file, then it is opaque. We start with the + * .wh. flag so that it is blocked by loomkup. + */ +#define UNIONFS_DIR_OPAQUE_NAME "__dir_opaque" +#define UNIONFS_DIR_OPAQUE WHPFX UNIONFS_DIR_OPAQUE_NAME + +/* construct whiteout filename */ +static inline char *alloc_whname(const char *name, int len) +{ + char *buf; + + buf = KMALLOC(len + WHLEN + 1, GFP_KERNEL); + if (!buf) + return ERR_PTR(-ENOMEM); + + strcpy(buf, WHPFX); + strlcat(buf, name, len + WHLEN + 1); + + return buf; +} + +#ifdef UNIONFS_IMAP +/* +* Defines,structs,and functions for persistent used by kernel and user +*/ +#define MAX_MAPS 256 +#define UUID_LEN 16 +#define FORWARDMAP_MAGIC 0x4b1cb38f +#define REVERSEMAP_MAGIC 0Xfcafad71 +#define FORWARDMAP_VERSION 0x02 +#define REVERSEMAP_VERSION 0x01 +#define FIRST_VALID_INODE 3 +struct fmaphdr { + uint32_t magic; + uint32_t version; + uint8_t usedbranches; + uint8_t uuid[UUID_LEN]; +}; + +struct rmaphdr { + uint32_t magic; + uint32_t version; + uint8_t fwduuid[UUID_LEN]; + uint8_t revuuid[UUID_LEN]; + fsid_t fsid; +}; +struct bmapent { + fsid_t fsid; + uint8_t uuid[UUID_LEN]; +}; +struct fmapent { + uint8_t fsnum; + uint64_t inode; +}; + +/* Persistant Inode functions */ +extern int read_uin(struct super_block *sb, uint8_t branchnum, + ino_t inode_number, int flag, ino_t * uino); +extern int write_uin(struct super_block *sb, ino_t ino, int bindex, + ino_t hidden_ino); +extern int get_lin(struct super_block *sb, ino_t inode_number, + struct fmapent *entry); +extern int parse_imap_option(struct super_block *sb, + struct unionfs_dentry_info *hidden_root_info, + char *options); +extern void cleanup_imap_data(struct super_block *sb); +#endif /*#ifdef UNIONFS_IMAP */ + +/* Definitions for various ways to handle errors. + Each flag's value is its bit position */ + +/* 1 = DELETE_ALL, 0 = check for DELETE_WHITEOUT */ +#ifdef UNIONFS_DELETE_ALL +#define DELETE_ALL 4 +#endif + +/* 1 = use current user's permissions, 0 = use original owner's permissions */ +#define COPYUP_CURRENT_USER 8 + +#define VALID_MOUNT_FLAGS (DELETE_ALL | COPYUP_OWNER) + +/* + * MACROS: + */ + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif /* not SEEK_SET */ + +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif /* not SEEK_CUR */ + +#ifndef SEEK_END +#define SEEK_END 2 +#endif /* not SEEK_END */ + +#ifndef DEFAULT_POLLMASK +#define DEFAULT_POLLMASK (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM) +#endif + +/* + * EXTERNALS: + */ + +/* JS: These two functions are here because it is kind of daft to copy and paste the + * contents of the two functions to 32+ places in unionfs + */ +static inline struct dentry *lock_parent(struct dentry *dentry) +{ + struct dentry *dir = DGET(dentry->d_parent); + + down(&dir->d_inode->i_sem); + return dir; +} + +static inline void unlock_dir(struct dentry *dir) +{ + up(&dir->d_inode->i_sem); + DPUT(dir); +} + +#endif /* __KERNEL__ */ + +/* + * DEFINITIONS FOR USER AND KERNEL CODE: + * (Note: ioctl numbers 1--9 are reserved for fistgen, the rest + * are auto-generated automatically based on the user's .fist file.) + */ +# define FIST_IOCTL_GET_DEBUG_VALUE _IOR(0x15, 1, int) +# define FIST_IOCTL_SET_DEBUG_VALUE _IOW(0x15, 2, int) +# define UNIONFS_IOCTL_BRANCH_COUNT _IOR(0x15, 10, int) +# define UNIONFS_IOCTL_INCGEN _IOR(0x15, 11, int) +# define UNIONFS_IOCTL_ADDBRANCH _IOW(0x15, 12, int) +# define UNIONFS_IOCTL_DELBRANCH _IOW(0x15, 13, int) +# define UNIONFS_IOCTL_RDWRBRANCH _IOW(0x15, 14, int) +# define UNIONFS_IOCTL_QUERYFILE _IOR(0x15, 15, int) + +/* We don't support normal remount, but unionctl uses it. */ +# define UNIONFS_REMOUNT_MAGIC 0x4a5a4380 + +/* should be at least LAST_USED_UNIONFS_PERMISSION<<1 */ +#define MAY_NFSRO 16 + +struct unionfs_addbranch_args { + unsigned int ab_branch; + char *ab_path; + unsigned int ab_perms; +}; + +struct unionfs_rdwrbranch_args { + unsigned int rwb_branch; + unsigned int rwb_perms; +}; + +#endif /* not __UNIONFS_H_ */ +/* + * + * vim:shiftwidth=8 + * vim:tabstop=8 + * + * For Emacs: + * Local variables: + * c-basic-offset: 8 + * c-comment-only-line-offset: 0 + * c-offsets-alist: ((statement-block-intro . +) (knr-argdecl-intro . 0) + * (substatement-open . 0) (label . 0) (statement-cont . +)) + * indent-tabs-mode: t + * tab-width: 8 + * End: + */ diff -urN linux-2.6.17-rc1-mm3-orig/fs/unionfs/unionfs_debugmacros.h linux-2.6.17-rc1-mm3-new/fs/unionfs/unionfs_debugmacros.h --- linux-2.6.17-rc1-mm3-orig/fs/unionfs/unionfs_debugmacros.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/unionfs/unionfs_debugmacros.h 2006-04-11 20:24:21.000000000 +0000 @@ -0,0 +1,409 @@ +/* + * Copyright (c) 2003-2006 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2006 Josef Sipek + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2005-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2006 Stony Brook University + * Copyright (c) 2003-2006 The Research Foundation of State University of New York + * + * For specific licensing information, see the COPYING file distributed with + * this package. + * + * This Copyright notice must be kept intact and distributed with all sources. + */ +/* + * $Id: unionfs_debugmacros.h,v 1.18 2006/02/15 09:07:28 jsipek Exp $ + */ + +#ifndef __UNIONFS_H_ +#error This file should only be included from unionfs.h! +#endif + +/* File to hidden file */ + +static inline struct file *ftohf_index(const struct file *f, int index) +{ + struct file *hidden_file; + hidden_file = ftopd(f)->ufi_file[index]; + + BUG_ON(hidden_file && (atomic_read(&hidden_file->f_count) <= 0)); + + return hidden_file; +} + +static inline struct file *ftohf(const struct file *f) +{ + BUG_ON(ftopd(f)->b_start < 0); + return ftohf_index(f, fbstart(f)); +} + +static inline struct file *set_ftohf_index(struct file *f, int index, + struct file *val) +{ + BUG_ON(index < 0); + BUG_ON(val && (atomic_read(&val->f_count) <= 0)); + + ftopd(f)->ufi_file[index] = val; + return val; +} + +static inline struct file *set_ftohf(struct file *f, struct file *val) +{ + BUG_ON(ftopd(f)->b_start < 0); + return set_ftohf_index(f, fbstart(f), val); +} + +/* Inode to hidden inode. */ + +static inline struct inode *itohi_index(const struct inode *ino, int index) +{ + struct inode *hidden_inode; + BUG_ON(index < 0); + hidden_inode = itopd(ino)->uii_inode[index]; + if (hidden_inode) + BUG_ON((atomic_read(&hidden_inode->i_count)) <= 0); + return hidden_inode; +} + +static inline struct inode *itohi(const struct inode *ino) +{ + BUG_ON(itopd(ino)->b_start < 0); + return itohi_index(ino, itopd(ino)->b_start); +} + +static inline struct inode *set_itohi_index(struct inode *ino, int index, + struct inode *val) +{ + BUG_ON(index < 0); + BUG_ON(val && (atomic_read(&val->i_count) <= 0)); + + itopd(ino)->uii_inode[index] = val; + return val; +} + +/* Superblock to hidden superblock */ +static inline struct super_block *stohs_index(const struct super_block *f, + int index) +{ + return stopd(f)->usi_data[index].sb; +} + +static inline struct super_block *stohs(const struct super_block *sb) +{ + BUG_ON(sbstart(sb) != 0); + return stohs_index(sb, sbstart(sb)); +} + +static inline struct super_block *set_stohs_index(struct super_block *sb, + int index, + struct super_block *val) +{ + stopd(sb)->usi_data[index].sb = val; + return val; +} + +/* Superblock to hidden vfsmount */ +static inline struct vfsmount *stohiddenmnt_index(struct super_block *sb, + int index) +{ + BUG_ON(index < 0); + return stopd(sb)->usi_data[index].hidden_mnt; +} + +static inline void set_stohiddenmnt_index(struct super_block *sb, int index, + struct vfsmount *mnt) +{ + BUG_ON(index < 0); + stopd(sb)->usi_data[index].hidden_mnt = mnt; +} + +/* Get and put branches on the superblock. */ +static inline void set_branch_count(struct super_block *sb, int index, + int count) +{ + BUG_ON(index < 0); + atomic_set(&stopd(sb)->usi_data[index].sbcount, count); +} + +static inline int branch_count(struct super_block *sb, int index) +{ + BUG_ON(index < 0); + return atomic_read(&stopd(sb)->usi_data[index].sbcount); +} + +static inline void branchget(struct super_block *sb, int index) +{ + BUG_ON(index < 0); + atomic_inc(&stopd(sb)->usi_data[index].sbcount); + BUG_ON(atomic_read(&stopd(sb)->usi_data[index].sbcount) < 0); +} + +static inline void branchput(struct super_block *sb, int index) +{ + BUG_ON(index < 0); + atomic_dec(&stopd(sb)->usi_data[index].sbcount); + BUG_ON(atomic_read(&stopd(sb)->usi_data[index].sbcount) < 0); +} + +/* Dentry to Hidden Dentry */ +static inline struct unionfs_dentry_info *__dtopd(const struct dentry *dent, + int check) +{ + struct unionfs_dentry_info *ret; + + ret = (struct unionfs_dentry_info *)(dent)->d_fsdata; + /* We are really only interested in catching poison here. */ + if (ret && check) { + if ((ret->udi_bend > ret->udi_bcount) + || (ret->udi_bend > sbmax(dent->d_sb))) { + printk(KERN_EMERG + "udi_bend = %d, udi_count = %d, sbmax = %d\n", + ret->udi_bend, ret->udi_bcount, + sbmax(dent->d_sb)); + } + BUG_ON(ret->udi_bend > ret->udi_bcount); + BUG_ON(ret->udi_bend > sbmax(dent->d_sb)); + } + + return ret; +} + +#define dtopd(dent) __dtopd(dent, 1) +#define dtopd_nocheck(dent) __dtopd(dent, 0) +#define dtopd_lhs(dent) ((dent)->d_fsdata) + +/* Macros for locking a dentry. */ +static inline void lock_dentry(struct dentry *d) +{ +#ifdef TRACKLOCK + printk("LOCK:%p\n", d); +#endif + down(&dtopd(d)->udi_sem); +} + +static inline void unlock_dentry(struct dentry *d) +{ +#ifdef TRACKLOCK + printk("UNLOCK:%p\n", d); +#endif + up(&dtopd(d)->udi_sem); +} + +static inline void verify_locked(const struct dentry *d) +{ +#ifdef TRACKLOCK + printk("MUST BE LOCKED:%p\n", d); +#endif + BUG_ON(down_trylock(&dtopd(d)->udi_sem) == 0); +} + +static inline int dbend(const struct dentry *dentry) +{ + verify_locked(dentry); + return dtopd(dentry)->udi_bend; +} + +static inline int set_dbend(const struct dentry *dentry, int val) +{ + verify_locked(dentry); + + BUG_ON((val < 0) && (val != -1)); + BUG_ON(val > dtopd_nocheck(dentry)->udi_bcount); + BUG_ON(val > sbmax(dentry->d_sb)); + + dtopd(dentry)->udi_bend = val; + return dtopd(dentry)->udi_bend; +} + +static inline int dbstart(const struct dentry *dentry) +{ + verify_locked(dentry); + return dtopd(dentry)->udi_bstart; +} + +static inline int set_dbstart(const struct dentry *dentry, int val) +{ + verify_locked(dentry); + + BUG_ON((val < 0) && (val != -1)); + BUG_ON(val > dtopd_nocheck(dentry)->udi_bcount); + BUG_ON(val > sbmax(dentry->d_sb)); + + dtopd_nocheck(dentry)->udi_bstart = val; + return dtopd(dentry)->udi_bstart; +} + +static inline int dbopaque(const struct dentry *dentry) +{ + verify_locked(dentry); + return dtopd(dentry)->udi_bopaque; +} + +static inline int set_dbopaque(const struct dentry *dentry, int val) +{ + verify_locked(dentry); + + BUG_ON((val < 0) && (val != -1)); + BUG_ON(val > dtopd_nocheck(dentry)->udi_bcount); + BUG_ON(val > sbmax(dentry->d_sb)); + BUG_ON(val > dbend(dentry)); + + dtopd_nocheck(dentry)->udi_bopaque = val; + return dtopd(dentry)->udi_bopaque; +} + +/* Dentry to hidden dentry functions */ +#define dtohd_index(dent, index) __dtohd_index(dent, index, 1) +#define dtohd_index_nocheck(dent, index) __dtohd_index(dent, index, 0) +/* This pointer should not be generally used except for maintainence functions. */ +#define dtohd_ptr(dent) (dtopd_nocheck(dent)->udi_dentry) +static inline struct dentry *__dtohd_index(const struct dentry *dent, int index, + int docheck) +{ + struct dentry *d; + + verify_locked(dent); + BUG_ON(index < 0); + if (docheck) { + if (index > sbend(dent->d_sb)) { + printk + ("Dentry index out of super bounds: index=%d, sbend=%d\n", + index, sbend(dent->d_sb)); + BUG_ON(index > sbend(dent->d_sb)); + } + if (index > dtopd(dent)->udi_bcount) { + printk + ("Dentry index out of array bounds: index=%d, count=%d\n", + index, dtopd(dent)->udi_bcount); + printk("Generation of dentry: %d\n", + atomic_read(&dtopd(dent)->udi_generation)); + printk("Generation of sb: %d\n", + atomic_read(&stopd(dent->d_sb)->usi_generation)); + BUG_ON(index > dtopd(dent)->udi_bcount); + } + } + d = dtopd(dent)->udi_dentry[index]; + + BUG_ON(d && (atomic_read(&d->d_count) <= 0)); + + return d; +} + +static inline struct dentry *dtohd(const struct dentry *dent) +{ + struct dentry *d; + int index; + + verify_locked(dent); + BUG_ON(dbstart(dent) < 0); + index = dbstart(dent); + d = dtohd_index(dent, index); + + return d; +} + +// Dentry to Hidden Dentry based on index +#define set_dtohd_index(dent, index, val) __set_dtohd_index(dent, index, val, 1) +#define set_dtohd_index_nocheck(dent, index, val) __set_dtohd_index(dent, index, val, 0) +static inline struct dentry *__set_dtohd_index(struct dentry *dent, int index, + struct dentry *val, int docheck) +{ +#ifdef FIST_MALLOC_DEBUG + struct dentry *old; +#endif + + verify_locked(dent); + BUG_ON(index < 0); + if (docheck) { + if (index > sbend(dent->d_sb)) { + printk + ("Dentry index out of super bounds: index=%d, sbend=%d\n", + index, sbend(dent->d_sb)); + BUG_ON(index > sbend(dent->d_sb)); + } + if (index > dtopd(dent)->udi_bcount) { + printk + ("Dentry index out of array bounds: index=%d, count=%d\n", + index, dtopd(dent)->udi_bcount); + printk("Generation of dentry: %d\n", + atomic_read(&dtopd(dent)->udi_generation)); + printk("Generation of sb: %d\n", + atomic_read(&stopd(dent->d_sb)->usi_generation)); + BUG_ON(index > dtopd(dent)->udi_bcount); + } + } +#ifdef FIST_MALLOC_DEBUG + old = dtopd(dent)->udi_dentry[index]; + record_set(dent, index, val, old, line, file); +#endif + dtopd(dent)->udi_dentry[index] = val; + + return val; +} + +extern int fist_get_debug_value(void); +extern int fist_set_debug_value(int val); +extern void fist_dprint_internal(const char *file, const char *function, + int line, int level, char *str, ...) + __attribute__ ((format(__printf__, 5, 6))); + +extern void fist_print_dentry(const char *, const struct dentry *); +extern void __fist_print_dentry(const char *, const struct dentry *, int); +extern void fist_print_generic_dentry(const char *, const struct dentry *); +extern void fist_print_generic_dentry3(const char *, const char *, + const struct dentry *); +extern void __fist_print_generic_dentry(const char *, const char *, const + struct dentry *, int); +extern void fist_print_inode(const char *, const struct inode *); +extern void fist_print_generic_inode(const char *, const struct inode *); +extern void fist_print_file(const char *, const struct file *); +extern void fist_checkinode(const struct inode *, const char *); +extern void fist_print_sb(const char *str, const struct super_block *); + +extern char *add_indent(void); +extern char *del_indent(void); + +#define fist_dprint(level, str, args...) fist_dprint_internal(__FILE__, __FUNCTION__, __LINE__, level, KERN_DEBUG str, ## args) +#define print_entry(format, args...) fist_dprint(4, "%sIN: %s %s:%d " format "\n", add_indent(), __FUNCTION__, __FILE__, __LINE__, ##args) +#define print_entry_location() fist_dprint(4, "%sIN: %s %s:%d\n", add_indent(), __FUNCTION__, __FILE__, __LINE__) +#define print_exit_location() fist_dprint(5, "%s OUT: %s %s:%d\n", del_indent(), __FUNCTION__, __FILE__, __LINE__) +#define print_exit_status(status) fist_dprint(5, "%s OUT: %s %s:%d, STATUS: %d\n", del_indent(), __FUNCTION__, __FILE__, __LINE__, status) +#define print_exit_pointer(status) \ +do { \ + if (IS_ERR(status)) \ + fist_dprint(5, "%s OUT: %s %s:%d, RESULT: %ld\n", del_indent(), __FUNCTION__, __FILE__, __LINE__, PTR_ERR(status)); \ + else \ + fist_dprint(5, "%s OUT: %s %s:%d, RESULT: 0x%p\n", del_indent(), __FUNCTION__, __FILE__, __LINE__, status); \ +} while (0) + +#define print_util_entry(format, args...) fist_dprint(6, "%sIN: %s %s:%d" format "\n", add_indent(), __FUNCTION__, __FILE__, __LINE__, ##args) +#define print_util_entry_location() fist_dprint(6, "%sIN: %s %s:%d\n", add_indent(), __FUNCTION__, __FILE__, __LINE__) +#define print_util_exit_location() fist_dprint(7, "%s OUT: %s %s:%d\n", del_indent(), __FUNCTION__, __FILE__, __LINE__) +#define print_util_exit_status(status) fist_dprint(7, "%s OUT: %s %s:%d, STATUS: %d\n", del_indent(), __FUNCTION__, __FILE__, __LINE__, status) +#define print_util_exit_pointer(status) \ +do { \ + if (IS_ERR(status)) \ + fist_dprint(7, "%s OUT: %s %s:%d, RESULT: %ld\n", del_indent(), __FUNCTION__, __FILE__, __LINE__, PTR_ERR(status)); \ + else \ + fist_dprint(5, "%s OUT: %s %s:%d, RESULT: 0x%x\n", del_indent(), __FUNCTION__, __FILE__, __LINE__, PTR_ERR(status)); \ +} while (0) + +/* + * + * vim:shiftwidth=8 + * vim:tabstop=8 + * + * For Emacs: + * Local variables: + * c-basic-offset: 8 + * c-comment-only-line-offset: 0 + * c-offsets-alist: ((statement-block-intro . +) (knr-argdecl-intro . 0) + * (substatement-open . 0) (label . 0) (statement-cont . +)) + * indent-tabs-mode: t + * tab-width: 8 + * End: + */ diff -urN linux-2.6.17-rc1-mm3-orig/fs/unionfs/unionfs_macros.h linux-2.6.17-rc1-mm3-new/fs/unionfs/unionfs_macros.h --- linux-2.6.17-rc1-mm3-orig/fs/unionfs/unionfs_macros.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/unionfs/unionfs_macros.h 2006-04-11 20:24:21.000000000 +0000 @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2003-2006 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2006 Josef Sipek + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2005-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2006 Stony Brook University + * Copyright (c) 2003-2006 The Research Foundation of State University of New York + * + * For specific licensing information, see the COPYING file distributed with + * this package. + * + * This Copyright notice must be kept intact and distributed with all sources. + */ +/* + * $Id: unionfs_macros.h,v 1.10 2006/02/13 00:18:40 jsipek Exp $ + */ + +#ifndef __UNIONFS_H_ +#error This file should only be included from unionfs.h! +#endif + +/* File to hidden file. */ +static inline struct file *ftohf(struct file *f) +{ + return ftopd(f)->ufi_file[fbstart(f)]; +} + +static inline struct file *ftohf_index(struct file *f, int index) +{ + return ftopd(f)->ufi_file[index]; +} + +static inline void set_ftohf_index(struct file *f, int index, struct file *val) +{ + ftopd(f)->ufi_file[index] = val; +} + +static inline void set_ftohf(struct file *f, struct file *val) +{ + ftopd(f)->ufi_file[fbstart(f)] = val; +} + +/* Inode to hidden inode. */ +static inline struct inode *itohi(struct inode *i) +{ + return itopd(i)->uii_inode[ibstart(i)]; +} + +static inline struct inode *itohi_index(struct inode *i, int index) +{ + return itopd(i)->uii_inode[index]; +} + +static inline void set_itohi_index(struct inode *i, int index, + struct inode *val) +{ + itopd(i)->uii_inode[index] = val; +} + +static inline void set_itohi(struct inode *i, struct inode *val) +{ + itopd(i)->uii_inode[ibstart(i)] = val; +} + +/* Superblock to hidden superblock. */ +static inline struct super_block *stohs(struct super_block *o) +{ + return stopd(o)->usi_data[sbstart(o)].sb; +} + +static inline struct super_block *stohs_index(struct super_block *o, int index) +{ + return stopd(o)->usi_data[index].sb; +} + +static inline void set_stohs_index(struct super_block *o, int index, + struct super_block *val) +{ + stopd(o)->usi_data[index].sb = val; +} + +static inline void set_stohs(struct super_block *o, struct super_block *val) +{ + stopd(o)->usi_data[sbstart(o)].sb = val; +} + +/* Super to hidden mount. */ +static inline struct vfsmount *stohiddenmnt_index(struct super_block *o, + int index) +{ + return stopd(o)->usi_data[index].hidden_mnt; +} + +static inline void set_stohiddenmnt_index(struct super_block *o, int index, + struct vfsmount *val) +{ + stopd(o)->usi_data[index].hidden_mnt = val; +} + +/* Branch count macros. */ +static inline int branch_count(struct super_block *o, int index) +{ + return atomic_read(&stopd(o)->usi_data[index].sbcount); +} + +static inline void set_branch_count(struct super_block *o, int index, int val) +{ + atomic_set(&stopd(o)->usi_data[index].sbcount, val); +} + +static inline void branchget(struct super_block *o, int index) +{ + atomic_inc(&stopd(o)->usi_data[index].sbcount); +} + +static inline void branchput(struct super_block *o, int index) +{ + atomic_dec(&stopd(o)->usi_data[index].sbcount); +} + +/* Dentry macros */ +static inline struct unionfs_dentry_info *dtopd(const struct dentry *dent) +{ + return (struct unionfs_dentry_info *)dent->d_fsdata; +} + +#define dtopd_lhs(dent) ((dent)->d_fsdata) +#define dtopd_nocheck(dent) dtopd(dent) +#define dbstart(dent) (dtopd(dent)->udi_bstart) +#define set_dbstart(dent, val) do { dtopd(dent)->udi_bstart = val; } while(0) +#define dbend(dent) (dtopd(dent)->udi_bend) +#define set_dbend(dent, val) do { dtopd(dent)->udi_bend = val; } while(0) +#define dbopaque(dent) (dtopd(dent)->udi_bopaque) +#define set_dbopaque(dent, val) do { dtopd(dent)->udi_bopaque = val; } while (0) + +static inline void set_dtohd_index(struct dentry *dent, int index, + struct dentry *val) +{ + dtopd(dent)->udi_dentry[index] = val; +} + +static inline struct dentry *dtohd_index(const struct dentry *dent, int index) +{ + return dtopd(dent)->udi_dentry[index]; +} + +static inline struct dentry *dtohd(const struct dentry *dent) +{ + return dtopd(dent)->udi_dentry[dbstart(dent)]; +} + +#define set_dtohd_index_nocheck(dent, index, val) set_dtohd_index(dent, index, val) +#define dtohd_index_nocheck(dent, index) dtohd_index(dent, index) + +#define dtohd_ptr(dent) (dtopd_nocheck(dent)->udi_dentry) + +/* Macros for locking a dentry. */ +#define lock_dentry(d) down(&dtopd(d)->udi_sem) +#define unlock_dentry(d) up(&dtopd(d)->udi_sem) +#define verify_locked(d) + +/* All of these should be noops. */ +static inline int fist_get_debug_value(void) +{ + return 0; +} +static inline int fist_set_debug_value(int val) +{ + return -ENOTSUPP; +} + +#define fist_print_dentry(msg, o) +#define __fist_print_dentry(msg, o, i) +#define fist_print_generic_dentry(msg, o) +#define fist_print_generic_dentry3(msg, o) +#define __fist_print_generic_dentry(msg, o, i) +#define fist_print_inode(msg, o) +#define fist_print_generic_inode(msg, o) +#define fist_print_file(msg, o) +#define fist_checkinode(o, msg) +#define fist_print_sb(msg, o) + +#define fist_dprint(args...) +#define print_entry(args...) +#define print_entry_location() +#define print_exit_location() +#define print_exit_status(status) +#define print_exit_pointer(status) +#define print_util_entry(args...) +#define print_util_entry_location() +#define print_util_exit_location() +#define print_util_exit_status(status) +#define print_util_exit_pointer(status) + +/* + * + * vim:shiftwidth=8 + * vim:tabstop=8 + * + * For Emacs: + * Local variables: + * c-basic-offset: 8 + * c-comment-only-line-offset: 0 + * c-offsets-alist: ((statement-block-intro . +) (knr-argdecl-intro . 0) + * (substatement-open . 0) (label . 0) (statement-cont . +)) + * indent-tabs-mode: t + * tab-width: 8 + * End: + */ diff -urN linux-2.6.17-rc1-mm3-orig/fs/unionfs/unionimap.c linux-2.6.17-rc1-mm3-new/fs/unionfs/unionimap.c --- linux-2.6.17-rc1-mm3-orig/fs/unionfs/unionimap.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/unionfs/unionimap.c 2006-04-11 20:24:21.000000000 +0000 @@ -0,0 +1,516 @@ +/* + * Copyright (c) 2003-2006 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2006 Josef Sipek + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2005-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2006 Stony Brook University + * Copyright (c) 2003-2006 The Research Foundation of State University of New York + * + * For specific licensing information, see the COPYING file distributed with + * this package. + * + * This Copyright notice must be kept intact and distributed with all sources. + */ +/* + * $Id: unionimap.c,v 1.25 2006/02/09 04:41:00 jsipek Exp $ + */ + +#include "unionimap.h" + +/** + * print_usage() + * Function used to print out usage information + * when an error is encountered + */ +void print_usage(void) +{ + fprintf(stderr, + "unionimap version: $Id: unionimap.c,v 1.25 2006/02/09 04:41:00 jsipek Exp $\n"); + fprintf(stderr, "Distributed with Unionfs " UNIONFS_VERSION "\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Usage: unionimap [-c] [-a ARG:] filename path\n"); + fprintf(stderr, " flag -c: create forward map with name filename\n"); + fprintf(stderr, + " flag -a ARG: create reverse map with name filename and path and add it to forwardmap ARG\n"); + fprintf(stderr, + " flag -d: if set once will dump the header of the map if twice it will also dump the contents\n"); + fprintf(stderr, " flag -h: prints this message then quits\n"); + fprintf(stderr, + " please note that -c -a and -d are mutually exclusive\n"); +} + +/** + * create_forwardmap(char *filename) + * verifies that the forward map is valid. + * + */ +int create_forwardmap(char *filename) +{ + int byteswritten = 0, err = 0; + int fwrdmap = 0; + struct fmaphdr header; + void *table = NULL; + uuid_t uuid; + + fwrdmap = creat(filename, S_IRUSR | S_IWUSR); + if (fwrdmap < 0) { + err = -EINVAL; + goto out; + } + memset(&header, 0, sizeof(struct fmaphdr)); + header.version = FORWARDMAP_VERSION; + header.magic = FORWARDMAP_MAGIC; + header.usedbranches = 0; + uuid_generate(uuid); + memcpy(&header.uuid, &uuid, UUID_LEN); + byteswritten = write(fwrdmap, (void *)&header, sizeof(struct fmaphdr)); + if (byteswritten < sizeof(struct fmaphdr)) { + perror("Failed Writting header: "); + err = -EIO; + goto out; + } + table = malloc(sizeof(struct bmapent[256])); + if (!table) { + err = -ENOMEM; + goto out; + } + memset(table, 0, sizeof(struct bmapent[256])); + byteswritten = write(fwrdmap, table, sizeof(struct bmapent[256])); + if (byteswritten < sizeof(struct bmapent[256])) { + perror("Failed writting table: "); + err = -EIO; + goto out; + } + out: + if (fwrdmap) + close(fwrdmap); + if (table) + free(table); + + return err; +} +int open_forwardmap(struct fmaphdr *header, char *forwardmap) +{ + int fwrdmap = 0, bytesread = 0, err = 0; + fwrdmap = open(forwardmap, O_RDWR); + if (fwrdmap < 0) { + perror("Open on Forwardmap Failed: "); + err = errno; + goto out_error; + } + bytesread = read(fwrdmap, (void *)header, sizeof(struct fmaphdr)); + if (bytesread < 0 || bytesread < sizeof(struct fmaphdr)) { + err = -EINVAL; + goto out_error; + } + if (header->magic != FORWARDMAP_MAGIC + || header->version != FORWARDMAP_VERSION) { + err = -EINVAL; + goto out_error; + } + if (header->usedbranches == 255) { + fprintf(stderr, + "Forwardmap already contains maximum number of reverse maps"); + err = -EINVAL; + goto out_error; + } + err = fwrdmap; + goto out; + out_error: + if (fwrdmap) + close(fwrdmap); + out: + return err; +} +int check_if_entry_exists(int fwrdmap, struct statfs stat, char *path) +{ + int err = 0, bytesread = 0, i; + struct fmaphdr header; + struct bmapent btable[256]; + fsid_t fsid; + + if (fwrdmap < 0) { + err = -EINVAL; + goto out; + } + err = lseek(fwrdmap, 0L, SEEK_SET); + if (err) { + goto out; + } + bytesread = read(fwrdmap, (void *)&header, sizeof(struct fmaphdr)); + if (bytesread != sizeof(struct fmaphdr)) { + err = -EIO; + goto out; + } + bytesread = read(fwrdmap, (void *)&btable, sizeof(struct bmapent[256])); + if (bytesread != sizeof(struct bmapent[256])) { + err = -EIO; + goto out; + } + if (((unsigned int *)&stat.f_fsid)[0] + || ((unsigned int *)&stat.f_fsid)[1]) { + fsid = stat.f_fsid; + } else { + err = mkfsid(path, &fsid); + if (err) { + goto out; + } + } + for (i = 0; i < header.usedbranches; i++) { + if (!memcmp(&fsid, &btable[i].fsid, sizeof(fsid_t))) { + err = 1; + break; + } + } + out: + return err; +} +int create_reversemap(char *filename, char *path, char *forwardmap) +{ + int byteswritten = 0, err = 0, seekres = 0; + off_t offset = 0; + int fwrdmap = 0, revmap = 0; + struct fmaphdr fwdheader; + struct rmaphdr revheader; + struct statfs stat; + struct bmapent ent; + uuid_t uuid; + + fwrdmap = open_forwardmap(&fwdheader, forwardmap); + if (fwrdmap < 0) { + err = fwrdmap; + goto out; + } + memset(&stat, 0, sizeof(struct statfs)); + err = statfs(path, &stat); + if (err) { + perror("statfs failed on path: "); + goto out; + } + err = check_if_entry_exists(fwrdmap, stat, path); + if (err) { + if (err > 0) { + err = -EINVAL; + fprintf(stderr, + "Specified fs already exists in the forward map\n"); + } + goto out; + } + revmap = open(filename, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); + if (!revmap) { + err = -EINVAL; + perror("Open on Reversemap failed: "); + goto out; + } + revheader.version = REVERSEMAP_VERSION; + revheader.magic = REVERSEMAP_MAGIC; + uuid_generate(uuid); + memcpy(&revheader.revuuid, &uuid, UUID_LEN); + memcpy(&revheader.fwduuid, &fwdheader.uuid, UUID_LEN); + + if (((unsigned int *)&stat.f_fsid)[0] + || ((unsigned int *)&stat.f_fsid)[1]) { + revheader.fsid = stat.f_fsid; + } else { + err = mkfsid(path, &revheader.fsid); + if (err) { + goto out; + } + + } + byteswritten = write(revmap, &revheader, sizeof(struct rmaphdr)); + if (byteswritten < sizeof(struct rmaphdr)) { + err = -EIO; + perror("Reversemap Write failed: "); + goto out; + } + + offset = + sizeof(struct fmaphdr) + + (fwdheader.usedbranches * sizeof(struct bmapent)); + seekres = lseek(fwrdmap, offset, SEEK_SET); + if (!(seekres == offset)) { + err = -EIO; + perror("Couldent seek to offset in uuid table: "); + } + errno = 0; + memcpy(&ent.uuid, &uuid, UUID_LEN); + memcpy(&ent.fsid, &revheader.fsid, sizeof(fsid_t)); + byteswritten = write(fwrdmap, &ent, sizeof(struct bmapent)); + if (byteswritten < sizeof(struct bmapent)) { + perror("Forwardmap Write failed to write uuid to table: "); + err = -EIO; + goto out; + } + fwdheader.usedbranches++; + offset = (int)&(((struct fmaphdr *)(0))->usedbranches); + seekres = lseek(fwrdmap, offset, SEEK_SET); + if (!(seekres == offset)) { + perror("Couldent seek to usedbranch offset: "); + err = -EIO; + goto out; + } + byteswritten = write(fwrdmap, &fwdheader.usedbranches, sizeof(uint8_t)); + if (byteswritten < sizeof(uint8_t)) { + perror("Forwardmap Write failed to update usedbranches: "); + err = -EIO; + goto out; + } + + out: + if (fwrdmap) { + close(fwrdmap); + } + if (revmap) { + close(revmap); + } + return err; +} +int print_forwardmap(int file, int debug_level) +{ + int err = 0, bytesread = 0, i; + struct fmaphdr header; + struct bmapent btable[256]; + char *uuid_unparsed; + struct fmapent entry; + + if (file < 0 || debug_level <= 0) { + err = -EINVAL; + goto out; + } + err = lseek(file, 0L, SEEK_SET); + if (err) { + goto out; + } + bytesread = read(file, (void *)&header, sizeof(struct fmaphdr)); + if (bytesread != sizeof(struct fmaphdr)) { + err = -EIO; + goto out; + } + uuid_unparsed = (char *)malloc(37); + if (!uuid_unparsed) { + err = -EIO; + goto out; + } + memset(uuid_unparsed, 0, 37); + fprintf(stdout, "Unionfs Forwardmap:\n"); + fprintf(stdout, "Magic: %x\n", header.magic); + fprintf(stdout, "Version: %d\n", header.version); + fprintf(stdout, "Used Branches: %d\n", header.usedbranches); + uuid_unparse(header.uuid, uuid_unparsed); + fprintf(stdout, "UUID: %s\n", uuid_unparsed); + bytesread = read(file, (void *)&btable, sizeof(struct bmapent[256])); + if (bytesread != sizeof(struct bmapent[256])) { + err = -EIO; + goto out; + } + for (i = 0; i < header.usedbranches; i++) { + fprintf(stdout, "Branch at index : %d\n", i); + fprintf(stdout, "fsid: %04x%04x\n", + ((unsigned int *)&btable[i].fsid)[0], + ((unsigned int *)&btable[i].fsid)[1]); + uuid_unparse(btable[i].uuid, uuid_unparsed); + fprintf(stdout, "uuid: %s\n", uuid_unparsed); + } + if (debug_level > 1) { + unsigned long inode = 1; + fprintf(stdout, "%-11s %-8s %-22s\n", "Unionfs", "FS Num", + "Lower-Level"); + while (read(file, (void *)&entry, sizeof(struct fmapent))) { + fprintf(stdout, "%-11lu %-8d %-22llu\n", inode++, + entry.fsnum, + (long long unsigned int)entry.inode); + } + } + out: + return err; + +} +int print_reversemap(int file, int debug_level) +{ + + int err = 0, bytesread = 0; + struct rmaphdr header; + char *uuid_unparsed = NULL; + uint64_t inode; + + if (file < 0 || debug_level <= 0) { + err = -EINVAL; + goto out; + } + err = lseek(file, 0L, SEEK_SET); + if (err) { + goto out; + } + bytesread = read(file, (void *)&header, sizeof(struct rmaphdr)); + if (bytesread != sizeof(struct rmaphdr)) { + err = -EIO; + goto out; + } + uuid_unparsed = (char *)malloc(37); + if (!uuid_unparsed) { + err = -ENOMEM; + goto out; + } + memset(uuid_unparsed, 0, 37); + fprintf(stdout, "Unionfs Reversemap:\n"); + fprintf(stdout, "Magic: %x\n", header.magic); + fprintf(stdout, "Version: %d\n", header.version); + uuid_unparse(header.fwduuid, uuid_unparsed); + fprintf(stdout, "Forward UUID: %s\n", uuid_unparsed); + uuid_unparse(header.revuuid, uuid_unparsed); + fprintf(stdout, "Reverse UUID: %s\n", uuid_unparsed); + fprintf(stdout, "fsid: %04x%04x\n", ((unsigned int *)&header.fsid)[0], + ((unsigned int *)&header.fsid)[1]); + if (debug_level > 1) { + fprintf(stdout, "%-11s %-22s\n", "Lower", "Unionfs"); + unsigned long lowerlevel = 0; + while (read(file, (void *)&inode, sizeof(uint64_t))) { + if (inode) { + fprintf(stdout, "%-11lu %-22llu\n", + lowerlevel++, + (long long unsigned int)inode); + } + } + } + out: + if (uuid_unparsed) { + free(uuid_unparsed); + } + return err; +} +int dump_map(char *filename, int debug_level) +{ + int err = 0, bytesread = 0, file = 0; + uint32_t magic; + + file = open(filename, O_RDONLY); + if (file < 0) { + err = -EINVAL; + goto out; + } + bytesread = read(file, (void *)&magic, sizeof(uint32_t)); + if (bytesread < sizeof(uint32_t)) { + err = -EINVAL; + goto out; + } + if (magic == FORWARDMAP_MAGIC) { + err = print_forwardmap(file, debug_level); + } else if (magic == REVERSEMAP_MAGIC) { + err = print_reversemap(file, debug_level); + } else { + err = -EINVAL; + } + out: + if (file) { + close(file); + } + + return err; +} + +int main(int argc, char **argv) +{ + int c = 0; //Temp varliable to hold our character for the switch + int cflag = 0, aflag = 0, dflag = 0; + int err = 0; + char *avalue = NULL; + if (argc < 2) { + print_usage(); + err = -EINVAL; + goto out; + } + while ((c = getopt(argc, argv, "a:cd")) != -1) { + switch (c) { + case 'c': + cflag = c; + break; + case 'a': + aflag = 1; + avalue = strdup(optarg); + if (!avalue) { + perror("strdup"); + exit(1); + } + break; + case 'd': + dflag++; + break; + case '?': + if (isprint(optopt)) + fprintf(stderr, "Unknown option `-%c'.\n", + optopt); + else + fprintf(stderr, + "Unknown option character `\\x%x'.\n", + optopt); + print_usage(); + err = -EINVAL; + goto out; + break; + default: + print_usage(); + err = -EINVAL; + goto out; + break; + } + } + if (optind == argc) { + print_usage(); + err = -EINVAL; + goto out; + } + if (!(cflag ^ aflag ^ dflag)) { + print_usage(); + err = -EINVAL; + goto out; + } + if (cflag) { + err = create_forwardmap(argv[optind]); + if (err) { + unlink(argv[optind]); + } + goto out; + } else if (aflag) { + if (optind + 1 > argc) { + print_usage(); + err = -EINVAL; + goto out; + } + + err = create_reversemap(argv[optind], argv[optind + 1], avalue); + if (err) { + unlink(argv[optind]); + goto out; + } + + } else if (dflag) { + err = dump_map(argv[optind], dflag); + if (err) { + goto out; + } + } + out: + free(avalue); + return err; +} + +/* + * + * vim:shiftwidth=8 + * vim:tabstop=8 + * + * For Emacs: + * Local variables: + * c-basic-offset: 8 + * c-comment-only-line-offset: 0 + * c-offsets-alist: ((statement-block-intro . +) (knr-argdecl-intro . 0) + * (substatement-open . 0) (label . 0) (statement-cont . +)) + * indent-tabs-mode: t + * tab-width: 8 + * End: + */ diff -urN linux-2.6.17-rc1-mm3-orig/fs/unionfs/unionimap.h linux-2.6.17-rc1-mm3-new/fs/unionfs/unionimap.h --- linux-2.6.17-rc1-mm3-orig/fs/unionfs/unionimap.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/unionfs/unionimap.h 2006-04-11 20:24:21.000000000 +0000 @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2003-2006 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2006 Josef Sipek + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2005-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2006 Stony Brook University + * Copyright (c) 2003-2006 The Research Foundation of State University of New York + * + * For specific licensing information, see the COPYING file distributed with + * this package. + * + * This Copyright notice must be kept intact and distributed with all sources. + */ +/* + * $Id: unionimap.h,v 1.11 2006/01/03 01:15:56 jsipek Exp $ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* + * Defines,structs,and functions for persistent inodes used by kernel and user + */ +#define MAX_MAPS 256 +#define UUID_LEN 16 +#define FORWARDMAP_MAGIC 0x4b1cb38f +#define REVERSEMAP_MAGIC 0Xfcafad71 +#define FORWARDMAP_VERSION 0x02 +#define REVERSEMAP_VERSION 0x01 + +typedef u_int8_t uint8_t; +typedef u_int32_t uint32_t; +typedef u_int64_t uint64_t; + +struct fmaphdr { + uint32_t magic; + uint32_t version; + uint8_t usedbranches; + uint8_t uuid[UUID_LEN]; +}; +struct bmapent { + fsid_t fsid; + uint8_t uuid[UUID_LEN]; +}; + +struct rmaphdr { + uint32_t magic; + uint32_t version; + uint8_t fwduuid[UUID_LEN]; + uint8_t revuuid[UUID_LEN]; + fsid_t fsid; +}; + +struct bpair { + fsid_t fsid; + uint8_t fsnum; +}; + +struct fmapent { + uint8_t fsnum; + uint64_t inode; +}; + +extern int mkfsid(char *path, fsid_t * fsid); +/* + * + * vim:shiftwidth=8 + * vim:tabstop=8 + * + * For Emacs: + * Local variables: + * c-basic-offset: 8 + * c-comment-only-line-offset: 0 + * c-offsets-alist: ((statement-block-intro . +) (knr-argdecl-intro . 0) + * (substatement-open . 0) (label . 0) (statement-cont . +)) + * indent-tabs-mode: t + * tab-width: 8 + * End: + */ diff -urN linux-2.6.17-rc1-mm3-orig/fs/unionfs/unlink.c linux-2.6.17-rc1-mm3-new/fs/unionfs/unlink.c --- linux-2.6.17-rc1-mm3-orig/fs/unionfs/unlink.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/unionfs/unlink.c 2006-04-11 20:24:21.000000000 +0000 @@ -0,0 +1,379 @@ +/* + * Copyright (c) 2003-2006 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2006 Josef Sipek + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2005-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2006 Stony Brook University + * Copyright (c) 2003-2006 The Research Foundation of State University of New York + * + * For specific licensing information, see the COPYING file distributed with + * this package. + * + * This Copyright notice must be kept intact and distributed with all sources. + */ +/* + * $Id: unlink.c,v 1.38 2006/02/20 22:23:01 dquigley Exp $ + */ + +#include "unionfs.h" + +#ifdef UNIONFS_DELETE_ALL +static int unionfs_unlink_all(struct inode *dir, struct dentry *dentry) +{ + struct dentry *hidden_dentry; + struct dentry *hidden_dir_dentry; + int bstart, bend, bindex; + int err = 0; + int global_err = 0; + + print_entry_location(); + + if ((err = unionfs_partial_lookup(dentry))) + goto out; + + bstart = dbstart(dentry); + bend = dbend(dentry); + + for (bindex = bend; bindex >= bstart; bindex--) { + hidden_dentry = dtohd_index(dentry, bindex); + if (!hidden_dentry) + continue; + + hidden_dir_dentry = lock_parent(hidden_dentry); + + /* avoid destroying the hidden inode if the file is in use */ + DGET(hidden_dentry); + if (!(err = is_robranch_super(dentry->d_sb, bindex))) + err = vfs_unlink(hidden_dir_dentry->d_inode, + hidden_dentry); + DPUT(hidden_dentry); + fist_copy_attr_times(dir, hidden_dir_dentry->d_inode); + unlock_dir(hidden_dir_dentry); + + if (err) { + /* passup the last error we got */ + if (!IS_COPYUP_ERR(err)) + goto out; + global_err = err; + } + } + + /* check if encountered error in the above loop */ + if (global_err) { + /* If we failed in the leftmost branch, then err will be set + * and we should move one over to create the whiteout. + * Otherwise, we should try in the leftmost branch. */ + if (err) { + if (dbstart(dentry) == 0) { + goto out; + } + err = create_whiteout(dentry, dbstart(dentry) - 1); + } else { + err = create_whiteout(dentry, dbstart(dentry)); + } + } else if (dbopaque(dentry) != -1) { + /* There is a hidden lower-priority file with the same name. */ + err = create_whiteout(dentry, dbopaque(dentry)); + } + out: + /* propagate number of hard-links */ + if (dentry->d_inode->i_nlink != 0) { + dentry->d_inode->i_nlink = get_nlinks(dentry->d_inode); + if (!err && global_err) + dentry->d_inode->i_nlink--; + } + /* We don't want to leave negative leftover dentries for revalidate. */ + if (!err && (global_err || dbopaque(dentry) != -1)) + update_bstart(dentry); + + print_exit_status(err); + return err; +} +#endif +static int unionfs_unlink_whiteout(struct inode *dir, struct dentry *dentry) +{ + struct dentry *hidden_dentry; + struct dentry *hidden_dir_dentry; + int bindex; + int err = 0; + + print_entry_location(); + + if ((err = unionfs_partial_lookup(dentry))) + goto out; + + bindex = dbstart(dentry); + + hidden_dentry = dtohd_index(dentry, bindex); + if (!hidden_dentry) + goto out; + + hidden_dir_dentry = lock_parent(hidden_dentry); + + /* avoid destroying the hidden inode if the file is in use */ + DGET(hidden_dentry); + if (!(err = is_robranch_super(dentry->d_sb, bindex))) + err = vfs_unlink(hidden_dir_dentry->d_inode, hidden_dentry); + DPUT(hidden_dentry); + fist_copy_attr_times(dir, hidden_dir_dentry->d_inode); + unlock_dir(hidden_dir_dentry); + + if (err) { + if (!IS_COPYUP_ERR(err)) + goto out; + } + + if (err) { + if (dbstart(dentry) == 0) { + goto out; + } + err = create_whiteout(dentry, dbstart(dentry) - 1); + } else if (dbopaque(dentry) != -1) { + /* There is a hidden lower-priority file with the same name. */ + err = create_whiteout(dentry, dbopaque(dentry)); + } else { + err = create_whiteout(dentry, dbstart(dentry)); + } + out: + /* propagate number of hard-links */ + if (dentry->d_inode->i_nlink != 0) { + dentry->d_inode->i_nlink = get_nlinks(dentry->d_inode); + if (!err) + dentry->d_inode->i_nlink--; + } + /* We don't want to leave negative leftover dentries for revalidate. */ + if (!err && (dbopaque(dentry) != -1)) + update_bstart(dentry); + + print_exit_status(err); + return err; + +} + +int unionfs_unlink(struct inode *dir, struct dentry *dentry) +{ + int err = 0; + + print_entry_location(); + lock_dentry(dentry); + fist_print_dentry("IN unionfs_unlink", dentry); + +#ifdef UNIONFS_DELETE_ALL + if (IS_SET(dir->i_sb, DELETE_ALL)) + err = unionfs_unlink_all(dir, dentry); + else +#endif + err = unionfs_unlink_whiteout(dir, dentry); + /* call d_drop so the system "forgets" about us */ + if (!err) + d_drop(dentry); + + unlock_dentry(dentry); + print_exit_status(err); + return err; +} + +static int unionfs_rmdir_first(struct inode *dir, struct dentry *dentry, + struct unionfs_dir_state *namelist) +{ + int err; + struct dentry *hidden_dentry; + struct dentry *hidden_dir_dentry = NULL; + + print_entry_location(); + fist_print_dentry("IN unionfs_rmdir_first: ", dentry); + + /* Here we need to remove whiteout entries. */ + err = delete_whiteouts(dentry, dbstart(dentry), namelist); + if (err) { + goto out; + } + + hidden_dentry = dtohd(dentry); + + hidden_dir_dentry = lock_parent(hidden_dentry); + + /* avoid destroying the hidden inode if the file is in use */ + DGET(hidden_dentry); + if (!(err = is_robranch(dentry))) { + err = vfs_rmdir(hidden_dir_dentry->d_inode, hidden_dentry); + } + DPUT(hidden_dentry); + + fist_copy_attr_times(dir, hidden_dir_dentry->d_inode); + /* propagate number of hard-links */ + dentry->d_inode->i_nlink = get_nlinks(dentry->d_inode); + + out: + if (hidden_dir_dentry) { + unlock_dir(hidden_dir_dentry); + } + fist_print_dentry("OUT unionfs_rmdir_first: ", dentry); + print_exit_status(err); + return err; +} + +#ifdef UNIONFS_DELETE_ALL +static int unionfs_rmdir_all(struct inode *dir, struct dentry *dentry, + struct unionfs_dir_state *namelist) +{ + struct dentry *hidden_dentry; + struct dentry *hidden_dir_dentry; + int bstart, bend, bindex; + int err = 0; + int global_err = 0; + + print_entry_location(); + fist_print_dentry("IN unionfs_rmdir_all: ", dentry); + + bstart = dbstart(dentry); + bend = dbend(dentry); + + for (bindex = bend; bindex >= bstart; bindex--) { + hidden_dentry = dtohd_index(dentry, bindex); + if (!hidden_dentry) + continue; + + hidden_dir_dentry = lock_parent(hidden_dentry); + if (S_ISDIR(hidden_dentry->d_inode->i_mode)) { + delete_whiteouts(dentry, bindex, namelist); + if (!(err = is_robranch_super(dentry->d_sb, bindex))) { + err = + vfs_rmdir(hidden_dir_dentry->d_inode, + hidden_dentry); + } + } else { + err = -EISDIR; + } + + fist_copy_attr_times(dir, hidden_dir_dentry->d_inode); + unlock_dir(hidden_dir_dentry); + if (err) { + int local_err = + unionfs_refresh_hidden_dentry(dentry, bindex); + if (local_err) { + err = local_err; + goto out; + } + + if (!IS_COPYUP_ERR(err) && err != -ENOTEMPTY + && err != -EISDIR) + goto out; + + global_err = err; + } + } + + /* check if encountered error in the above loop */ + if (global_err) { + /* If we failed in the leftmost branch, then err will be set and we should + * move one over to create the whiteout. Otherwise, we should try in the + * leftmost branch. + */ + if (err) { + if (dbstart(dentry) == 0) { + goto out; + } + err = create_whiteout(dentry, dbstart(dentry) - 1); + } else { + err = create_whiteout(dentry, dbstart(dentry)); + } + } else { + err = create_whiteout(dentry, dbstart(dentry)); + } + + out: + /* propagate number of hard-links */ + dentry->d_inode->i_nlink = get_nlinks(dentry->d_inode); + + fist_print_dentry("OUT unionfs_rmdir_all: ", dentry); + print_exit_status(err); + return err; +} +#endif +int unionfs_rmdir(struct inode *dir, struct dentry *dentry) +{ + int err = 0; + struct unionfs_dir_state *namelist = NULL; + + print_entry_location(); + lock_dentry(dentry); + fist_print_dentry("IN unionfs_rmdir: ", dentry); + + /* check if this unionfs directory is empty or not */ + err = check_empty(dentry, &namelist); + if (err) { +#if 0 + /* vfs_rmdir(our caller) unhashed the dentry. This will recover + * the Unionfs inode number for the directory itself, but the + * children are already lost. It seems that tmpfs manages its + * way around this by upping the refcount on everything. + * + * Even if we do this, we still lose the inode numbers of the + * children. The best way to fix this is to fix the VFS (or + * use persistent inode maps). */ + if (d_unhashed(dentry)) + d_rehash(dentry); +#endif + goto out; + } +#ifdef UNIONFS_DELETE_ALL + if (IS_SET(dir->i_sb, DELETE_ALL)) { + /* delete all. */ + err = unionfs_rmdir_all(dir, dentry, namelist); + } else { /* Delete the first directory. */ +#endif + err = unionfs_rmdir_first(dir, dentry, namelist); + /* create whiteout */ + if (!err) { + err = create_whiteout(dentry, dbstart(dentry)); + } else { + int new_err; + + if (dbstart(dentry) == 0) + goto out; + + /* exit if the error returned was NOT -EROFS */ + if (!IS_COPYUP_ERR(err)) + goto out; + + new_err = create_whiteout(dentry, dbstart(dentry) - 1); + if (new_err != -EEXIST) + err = new_err; + } + +#ifdef UNIONFS_DELETE_ALL + } +#endif + out: + /* call d_drop so the system "forgets" about us */ + if (!err) + d_drop(dentry); + + if (namelist) + free_rdstate(namelist); + + unlock_dentry(dentry); + print_exit_status(err); + return err; +} + +/* + * + * vim:shiftwidth=8 + * vim:tabstop=8 + * + * For Emacs: + * Local variables: + * c-basic-offset: 8 + * c-comment-only-line-offset: 0 + * c-offsets-alist: ((statement-block-intro . +) (knr-argdecl-intro . 0) + * (substatement-open . 0) (label . 0) (statement-cont . +)) + * indent-tabs-mode: t + * tab-width: 8 + * End: + */ diff -urN linux-2.6.17-rc1-mm3-orig/fs/unionfs/usercommon.c linux-2.6.17-rc1-mm3-new/fs/unionfs/usercommon.c --- linux-2.6.17-rc1-mm3-orig/fs/unionfs/usercommon.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/unionfs/usercommon.c 2006-04-11 20:24:21.000000000 +0000 @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2003-2006 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2006 Josef Sipek + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2005-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2006 Stony Brook University + * Copyright (c) 2003-2006 The Research Foundation of State University of New York + * + * For specific licensing information, see the COPYING file distributed with + * this package. + * + * This Copyright notice must be kept intact and distributed with all sources. + */ +/* + * $Id: usercommon.c,v 1.8 2006/01/03 01:15:56 jsipek Exp $ + */ +/* + * Copyright (c) 2003-2005 Erez Zadok + * Copyright (c) 2003-2005 Charles P. Wright + * Copyright (c) 2003-2005 Mohammad Nayyer Zubair + * Copyright (c) 2003-2005 Puja Gupta + * Copyright (c) 2003-2005 Harikesavan Krishnan + * Copyright (c) 2003-2005 Stony Brook University + * Copyright (c) 2003-2005 The Research Foundation of State University of New York + * + * For specific licensing information, see the COPYING file distributed with + * this package. + * + * This Copyright notice must be kept intact and distributed with all sources. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* + * This function will take a patch and check it against /proc/mounts to + * find its mount point. If uniononly is set then it will make sure its + * a unionf mount point. This function assumes the both options and actual_path + * are valid and not null; + */ +int find_union(const char *path, char **options, char **actual_path, + int uniononly) +{ + FILE *f = NULL; + char *s = NULL; + char *s2 = NULL; + char *p; + char *q; + int candidate = 0; + int mallocsize = 1024; /* Just a reasonable starting value. */ + + retry: + if (*options) { + free(*options); + *options = NULL; + } + + if (*actual_path) { + free(*actual_path); + *actual_path = NULL; + } + if (f) { + fclose(f); + f = NULL; + } + s2 = realloc(s, mallocsize); + if (!s2) { + fprintf(stderr, "realloc(%d): %s\n", mallocsize, + strerror(errno)); + goto out; + } + s = s2; + + f = fopen("/proc/mounts", "r"); + if (!f) { + fprintf(stderr, "fopen(/proc/mounts): %s\n", strerror(errno)); + goto out; + } + while (fgets(s, mallocsize, f)) { + int testcan; + + /* If we don't have enough information, we should remalloc it. */ + if (strlen(s) == (mallocsize - 1)) { + mallocsize *= 2; + goto retry; + } + + p = strchr(s, ' '); + if (!p) + continue; + p++; + + q = strchr(p, ' '); + if (!q) + continue; + *q++ = '\0'; + + testcan = strlen(p); + if (testcan <= candidate) { + continue; + } + + if (!strncmp(path, p, testcan)) { + if (*actual_path) { + free(*actual_path); + } + *actual_path = strdup(p); + if (!*actual_path) { + fprintf(stderr, "strdup: %s\n", + strerror(errno)); + goto out; + } + p = strchr(q, ' '); + if (!p) + continue; + *p++ = '\0'; + if (uniononly) { + if (strcmp(q, "unionfs")) { + candidate = 0; + continue; + } + } + candidate = testcan; + + q = strrchr(p, ' '); + if (!q) + continue; + *q = '\0'; + q = strrchr(p, ' '); + if (!q) + continue; + *q = '\0'; + + if (*options) { + free(*options); + } + *options = strdup(p); + if (!*options) { + fprintf(stderr, "strdup: %s\n", + strerror(errno)); + goto out; + } + } + } + + out: + if (s) + free(s); + if (f) + fclose(f); + + if (*options) { + return 0; + } + + errno = -ENOENT; + return -1; +} + +/** + * Takes the device and creates an fsid from it by placing major in the first + * int and minor in the second. + */ +void fillfsid(dev_t dev, fsid_t * fsid) +{ + ((unsigned int *)fsid)[0] = major(dev); + ((unsigned int *)fsid)[1] = minor(dev); +} + +int mkfsid(char *path, fsid_t * fsid) +{ + int err = 0; + char *actual_path = NULL; + char *options = NULL; + struct stat stat_struct; + + memset(&stat_struct, 0, sizeof(struct stat)); + + err = find_union(path, &options, &actual_path, 0); + if (err) { + printf("find_union failed:\n"); + goto out; + } + err = stat(actual_path, &stat_struct); + if (err) { + perror("Couldent stat path: "); + goto out; + } + + fillfsid(stat_struct.st_dev, fsid); + + out: + return err; +} + +/* + * + * vim:shiftwidth=8 + * vim:tabstop=8 + * + * For Emacs: + * Local variables: + * c-basic-offset: 8 + * c-comment-only-line-offset: 0 + * c-offsets-alist: ((statement-block-intro . +) (knr-argdecl-intro . 0) + * (substatement-open . 0) (label . 0) (statement-cont . +)) + * indent-tabs-mode: t + * tab-width: 8 + * End: + */ diff -urN linux-2.6.17-rc1-mm3-orig/fs/unionfs/xattr.c linux-2.6.17-rc1-mm3-new/fs/unionfs/xattr.c --- linux-2.6.17-rc1-mm3-orig/fs/unionfs/xattr.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17-rc1-mm3-new/fs/unionfs/xattr.c 2006-04-11 20:24:21.000000000 +0000 @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2003-2006 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2006 Josef Sipek + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2005-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2006 Stony Brook University + * Copyright (c) 2003-2006 The Research Foundation of State University of New York + * + * For specific licensing information, see the COPYING file distributed with + * this package. + * + * This Copyright notice must be kept intact and distributed with all sources. + */ +/* + * $Id: xattr.c,v 1.29 2006/01/13 03:00:24 jsipek Exp $ + */ + +#include "unionfs.h" + +/* This is lifted from fs/xattr.c */ +void *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; + else if (size <= PAGE_SIZE) + ptr = KMALLOC((unsigned long)size, GFP_KERNEL); + else + ptr = vmalloc((unsigned long)size); + if (!ptr) + return ERR_PTR(-ENOMEM); + return ptr; +} + +void xattr_free(void *ptr, size_t size) +{ + if (!size) /* size request, no buffer was needed */ + return; + else if (size <= PAGE_SIZE) + KFREE(ptr); + else + vfree(ptr); +} + +/* BKL held by caller. + * dentry->d_inode->i_sem down + * ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t); + */ +ssize_t unionfs_getxattr(struct dentry *dentry, const char *name, void *value, + size_t size) +{ + struct dentry *hidden_dentry = NULL; + int err = -EOPNOTSUPP; + /* Define these anyway so we don't need as much ifdef'ed code. */ + char *encoded_name = NULL; + char *encoded_value = NULL; + + print_entry_location(); + + lock_dentry(dentry); + + hidden_dentry = dtohd(dentry); + + fist_dprint(8, "getxattr: name=\"%s\", value %lu bytes\n", name, + (unsigned long)size); + + if (hidden_dentry->d_inode->i_op->getxattr) { + encoded_name = (char *)name; + + encoded_value = (char *)value; + + down(&hidden_dentry->d_inode->i_sem); + /* lock_kernel() already done by caller. */ + err = + hidden_dentry->d_inode->i_op->getxattr(hidden_dentry, + encoded_name, + encoded_value, size); + /* unlock_kernel() will be done by caller. */ + up(&hidden_dentry->d_inode->i_sem); + + } + + unlock_dentry(dentry); + print_exit_status(err); + return err; +} + +/* BKL held by caller. + * dentry->d_inode->i_sem down + */ +int +unionfs_setxattr(struct dentry *dentry, const char *name, const void *value, + size_t size, int flags) +{ + struct dentry *hidden_dentry = NULL; + int err = -EOPNOTSUPP; + + print_entry_location(); + + lock_dentry(dentry); + hidden_dentry = dtohd(dentry); + + fist_dprint(8, "setxattr: name=\"%s\", value %lu bytes, flags=%x\n", + name, (unsigned long)size, flags); + + if (hidden_dentry->d_inode->i_op->setxattr) { + down(&hidden_dentry->d_inode->i_sem); + /* lock_kernel() already done by caller. */ + err = hidden_dentry->d_inode->i_op-> + setxattr(hidden_dentry, name, value, size, flags); + /* unlock_kernel() will be done by caller. */ + up(&hidden_dentry->d_inode->i_sem); + } + + unlock_dentry(dentry); + print_exit_status(err); + return err; +} + +/* BKL held by caller. + * dentry->d_inode->i_sem down + */ +int unionfs_removexattr(struct dentry *dentry, const char *name) +{ + struct dentry *hidden_dentry = NULL; + int err = -EOPNOTSUPP; + char *encoded_name; + print_entry_location(); + + lock_dentry(dentry); + hidden_dentry = dtohd(dentry); + + fist_dprint(8, "removexattr: name=\"%s\"\n", name); + + if (hidden_dentry->d_inode->i_op->removexattr) { + encoded_name = (char *)name; + + down(&hidden_dentry->d_inode->i_sem); + /* lock_kernel() already done by caller. */ + err = + hidden_dentry->d_inode->i_op->removexattr(hidden_dentry, + encoded_name); + /* unlock_kernel() will be done by caller. */ + up(&hidden_dentry->d_inode->i_sem); + } + + unlock_dentry(dentry); + print_exit_status(err); + return err; +} + +/* BKL held by caller. + * dentry->d_inode->i_sem down + */ +ssize_t unionfs_listxattr(struct dentry * dentry, char *list, size_t size) +{ + struct dentry *hidden_dentry = NULL; + int err = -EOPNOTSUPP; + char *encoded_list = NULL; + + print_entry_location(); + lock_dentry(dentry); + + hidden_dentry = dtohd(dentry); + + if (hidden_dentry->d_inode->i_op->listxattr) { + encoded_list = list; + down(&hidden_dentry->d_inode->i_sem); + /* lock_kernel() already done by caller. */ + err = + hidden_dentry->d_inode->i_op->listxattr(hidden_dentry, + encoded_list, size); + /* unlock_kernel() will be done by caller. */ + up(&hidden_dentry->d_inode->i_sem); + } + + unlock_dentry(dentry); + print_exit_status(err); + return err; +} + +/* + * + * vim:shiftwidth=8 + * vim:tabstop=8 + * + * For Emacs: + * Local variables: + * c-basic-offset: 8 + * c-comment-only-line-offset: 0 + * c-offsets-alist: ((statement-block-intro . +) (knr-argdecl-intro . 0) + * (substatement-open . 0) (label . 0) (statement-cont . +)) + * indent-tabs-mode: t + * tab-width: 8 + * End: + */