* [PATCH 1/67] aufs document
@ 2008-05-16 14:32 hooanon05
2008-05-16 14:32 ` [PATCH 2/67] aufs manual hooanon05
2008-05-16 14:59 ` [PATCH 1/67] aufs document Dave Quigley
0 siblings, 2 replies; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs document
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
Documentation/filesystems/aufs/README | 374 +++++++++++++++++++++++++++++++++
1 files changed, 374 insertions(+), 0 deletions(-)
diff --git a/Documentation/filesystems/aufs/README b/Documentation/filesystems/aufs/README
new file mode 100644
index 0000000..ebe19db
--- /dev/null
+++ b/Documentation/filesystems/aufs/README
@@ -0,0 +1,374 @@
+
+Aufs -- Another Unionfs
+Junjiro Okajima
+
+# $Id: README,v 1.79 2008/05/04 23:55:14 sfjro Exp $
+
+
+0. Introduction
+----------------------------------------
+In the early days, aufs was entirely re-designed and re-implemented
+Unionfs Version 1.x series. After many original ideas, approaches,
+improvements and implementations, it becomes totally different from
+Unionfs while keeping the basic features.
+Recently, Unionfs Version 2.x series begin taking some of same
+approaches to aufs's.
+Unionfs is being developed by Professor Erez Zadok at Stony Brook
+University and his team.
+If you don't know Unionfs, I recommend you becoming familiar with it
+before using aufs. Some terminology in aufs follows Unionfs's.
+
+Bug reports (including my broken English), suggestions, comments
+and donations are always welcome. Your bug report may help other users,
+including future users. Especially the bug report which doesn't follow
+unix/linux filesystem's semantics is important.
+
+
+1. Features
+----------------------------------------
+- unite several directories into a single virtual filesystem. The member
+ directory is called as a branch.
+- you can specify the permission flags to the branch, which are 'readonly',
+ 'readwrite' and 'whiteout-able.'
+- by upper writable branch, internal copyup and whiteout, files/dirs on
+ readonly branch are modifiable logically.
+- dynamic branch manipulation, add, del.
+- etc... see Unionfs in detail.
+
+Also there are many enhancements in aufs, such as:
+- safer and faster
+- keep inode number by external inode number table
+- keep the timestamps of file/dir in internal copyup operation
+- seekable directory, supporting NFS readdir.
+- support mmap(2) including /proc/PID/exe symlink, without page-copy
+- whiteout is hardlinked in order to reduce the consumption of inodes
+ on branch
+- do not copyup, nor create a whiteout when it is unnecessary
+- revert a single systemcall when an error occurs in aufs
+- remount interface instead of ioctl
+- maintain /etc/mtab by an external shell script, /sbin/mount.aufs.
+- loopback mounted filesystem as a branch
+- kernel thread for removing the dir who has a plenty of whiteouts
+- support copyup sparse file (a file which has a 'hole' in it)
+- default permission flags for branches
+- selectable permission flags for ro branch, whether whiteout can
+ exist or not
+- export via NFS.
+- support <sysfs>/fs/aufs.
+- support multiple writable branches, some policies to select one
+ among multiple writable branches.
+- a new semantics for link(2) and rename(2) to support multiple
+ writable branches.
+- a delegation of the internal branch access to support task I/O
+ accounting, which also supports Linux Security Modules (LSM) mainly
+ for Suse AppArmor.
+- nested mount, i.e. aufs as readonly no-whiteout branch of another aufs.
+- copyup-on-open or copyup-on-write
+- show-whiteout mode
+- show configuration even out of kernel tree
+- no glibc changes are required.
+- and more... see aufs manual in detail
+
+Aufs is in still development stage, especially:
+- pseudo hardlink (hardlink over branches)
+- allow a direct access manually to a file on branch, e.g. bypassing aufs.
+ including NFS or remote filesystem branch.
+- refine xino and revalidate
+- pseudo-link in NFS-exporting
+
+(current work)
+- reorder the branch index without del/re-add.
+- permanent xino files
+
+(next work)
+- an option for refreshing the opened files after add/del branches
+- 'move' policy for copy-up between two writable branches, after
+ checking free space.
+- ioctl to manipulate file between branches.
+- and documentation
+
+(just an idea)
+- remount option copy/move between two branches. (unnecessary?)
+- O_DIRECT (unnecessary?)
+- light version, without branch manipulation. (unnecessary?)
+- SMP, because I don't have such machine. But several users reported
+ aufs is working fine on SMP machines.
+- copyup in userspace
+- inotify in userspace
+- xattr, acl
+
+
+2. Download
+----------------------------------------
+CVS tree is in aufs project of SourceForge.
+Here is simple instructions to get aufs source files. It is recommended to
+refer to the document about CVS on SourceForge.
+ $ mkdir aufs.wcvs
+ $ cd aufs.wcvs
+ $ cvs -d:pserver:anonymous@aufs.cvs.sourceforge.net:/cvsroot/aufs login
+ (CVS password is empty)
+ $ cvs -z3 -d:pserver:anonymous@aufs.cvs.sourceforge.net:/cvsroot/aufs co aufs
+
+In order to update files after the first checkout,
+ $ cd aufs.wcvs/aufs
+ $ cvs update
+Do not forget -A option for 'cvs update' if you have ever 'cvs update' with
+specifying a file version.
+
+In order to see what the difference between two versions (two dates) is,
+ $ cd aufs.wcvs/aufs
+ $ cvs diff -D20061212 -D20061219
+
+Usually I am updating CVS tree on every Monday.
+I always try putting the stable version in CVS, so you can try CVS
+instead of SourceForge File Release. And every changes are summarized
+and reported to aufs-users at lists.sourceforge.net ML. I'd like to
+recommend you to join this ML.
+
+
+3. Configuration and Compilation
+----------------------------------------
+Aufs is being developed and tested on linux-2.6.16 and later.
+You need the correct environment to build a kernel module. Generally
+it is an environment which your kernel was built.
+The aufs source files are under ./fs/aufs or ./fs/aufs25, which are
+for linux-2.6.16 - 2.6.24, and linux-2.6.25 and later respectively.
+Aufs makefile refers the files which is generated by linux build
+system.
+If you are unsure that you got the correct environment, then I
+recommend you to built your kernel by yourself. If it succeeds, you've
+got the correct environment.
+
+Currently aufs configuration is written in ./Kconfig.in. You can
+generate the real ./fs/aufs/Kconfig file by make(1). And follow the
+instructions which will be produced by make(1). The built
+./fs/aufs/Kconfig depends upon the kernel version.
+Before you build ./fs/aufs/Kconfig, you need to configure your kernel,
+since this build refers to linux/include/version.h.
+ $ make -f local.mk kconfig
+
+The local.mk searches your kernel build path by
+ KDIR = /lib/modules/$(shell uname -r)/build
+If you are cross-compiling the aufs module, try
+ $ make KDIR=/your/kernel/build/path -f local.mk kconfig
+If you link aufs statically, the generated Kconfig will help you.
+
+Also you can use ./local.mk to compile aufs as a module by simply,
+ $ make -f local.mk
+or
+ $ make KDIR=/your/kernel/build/path -f local.mk
+
+The default configuration is written in ./local.mk too, they will work
+for you in most cases. You can edit this file and change the
+configuration for your aufs module.
+If you don't want to change the ./local.mk, then create a new file
+./priv_def.mk and write the definitions your aufs configuration. The
+./local.mk includes ./priv_def.mk if it exists.
+When you configure aufs by modifying ./local.mk (or ./priv_dev.mk),
+leave it blank/unset when you disable an aufs configuration, instead
+of setting 'n.'
+
+The aufs build system may refer some kernel source files for some
+macros or internal functions which are not declared in their header
+files. It depends upon the configuration whether these files will be
+referred or not.
+Even if you don't have these source files in your build environment,
+aufs can be built safely, but works less efficiently in some cases.
+
+There is a contributed Makefile for aufs users. You might want to try
+it. Check
+http://sourceforge.net/mailarchive/forum.php?thread_name=9c4865a10707241831r189f9646n5f2c4a9c303797d1%40mail.gmail.com&forum_name=aufs-users
+and see the attachment.
+
+Currently, CONFIG_DEBUG_PROVE_LOCKING (in linux kernel) is not
+supported since MAX_LOCKDEP_SUBCLASSES is too small for a stackable
+filesystem. I am trying reducing the depth of lock subclasses.
+Until then, you need to increase the value of MAX_LOCKDEP_SUBCLASSES
+macro in include/linux/lockdep.h, for example, 16UL, when you enable
+CONFIG_DEBUG_PROVE_LOCKING.
+
+When you test the performance, it is recommended to disable
+CONFIG_AUFS_DEBUG. It is enabled by default for the feature test.
+
+Since aufs supports several versions of linux kernel, it uses the
+condition like this,
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
+On the other hand, some distributions modify their kernel by their own
+patches. For example, just a piece of code is brought from v2.6.18 and
+merged into a distribution kernel which calls itself as v2.6.17.
+Finally, the conditions in aufs will not work correctly.
+You may or may not find them when you compile aufs.
+You need to modify aufs source code for 'custom-version' kernel
+(cf. patch/ubuntu-edgy.patch).
+
+
+o Patches
+There are several patches for linux kernel, in order to use aufs.
+All of them are not necessary essentially, but in some cases you need
+them. It is up to your environment and aufs usage.
+All these patches are just for exporting a kernel internal function to
+modules. If the function was already declared as extern and you link
+aufs statically to your kernel, then you don't need such patch. If the
+function was not extern and you want the feature, you need to apply
+the patch.
+When you apply a patch, you need to enable the corresponding aufs
+configuration.
+All these patches are under CVS_TREE/aufs/patch.
+
+- sec_perm-2.6.24.patch
+ For linux-2.6.24 and later.
+ When you compile aufs as a module and enable CONFIG_SECURITY, this
+ patch is required.
+- splice-2.6.23.patch
+ For linux-2.6.23 and later.
+ When you use splice(2) (sendfile(2) in linux-2.6.23 and later), or
+ loopback-mount an fs-image file in aufs, this patch is
+ required. Aufs doesn't support splice(2) in linux-2.6.22 and
+ earlier.
+- put_filp.patch
+ For linux-2.6.19 and later.
+ When you compile aufs as a module and use NFS as an aufs branch
+ filesystem, this patch is required.
+- lhash.patch
+ For linux-2.6.19 and later till linux-2.6.21.
+ When you use NFS as an aufs branch filesystem, this patch is
+ required.
+- lhash-2.6.22.patch
+ Same above, but this patch is for linux-2.6.22 only.
+- fsync_super-2.6.16.patch
+ For linux-2.6.16 and later.
+ When you compile aufs as a module, apply this patch and enable a
+ configuration, aufs tries flushing everything for branch filesystems
+ which are not marked as 'rr' or 'rr+wh' at umount or remount time.
+- fsync_super-2.6.19.patch
+ Same above, but this patch is for linux-2.6.19 and later.
+- deny_write_access.patch
+ For linux-2.6.17 and later.
+ When you compile aufs as a module, applied this patch and enabled a
+ configuration, a minor security enhancement will be available at
+ execve(2).
+ You can omit this if you don't care the writing to a running
+ executable on a lower branch filesystem which was invoked through
+ aufs.
+- ksize.patch
+ For linux-2.6.22 and earlier.
+ When you compile aufs as a module and applied this patch, an
+ optimization inside aufs will be available at adding or deleting a
+ branch filesystem. You can omit this if you don't care the aufs
+ performance.
+
+Additionally, there are patches for aufs which will be necessary when
+you use non-standard kernel modules or patches. Some of them have been
+tested by several people, but not all.
+See also the comments in the patches.
+- ubuntu-2.6.22-14.46.patch
+ For Ubuntu kernel
+ (http://archive.ubuntu.com/ubuntu/pool/main/l/linux-source-2.6.22/
+ linux-source-2.6.22_2.6.22-14.46_all.deb) which is modified a lot by
+ ubuntu people.
+- ubuntu-2.6.24-5.8.patch
+ For http://archive.ubuntu.com/ubuntu/pool/main/l/linux/
+ linux-source-2.6.24_2.6.24-5.8_all.deb.
+- ubuntu-edgy.patch
+ For Ubuntu Edgy kernel which calls itself as 2.6.17, while its
+ umount_begin() interface has arguments of 2.6.18.
+- rt-compat.patch
+ For realtime patch (http://people.redhat.com/mingo/realtime-preempt).
+- vserver.patch
+ For linux-vserver module (http://linux-vserver.org).
+- openvz_028stab039.1.patch
+ For openvz module (http://openvz.org).
+
+
+4. Usage
+----------------------------------------
+After 'make',
+ $ man -l ./aufs.5
+ # install -m 500 -p mount.aufs umount.aufs auplink aulchown /sbin (recommended)
+ # echo FLUSH=ALL > /etc/default/auplink (recommended)
+ # insmod ./aufs.ko
+ $ mkdir /tmp/rw /tmp/aufs
+ # mount -t aufs -o dirs=/tmp/rw:${HOME}=ro none /tmp/aufs
+
+If you are familiar with Unionfs Version 1.x series and want to use
+unionctl(8), you can try the sample unionctl script under sample/
+directory too.
+
+Here is another example.
+
+ # mount -t aufs -o br:/tmp/rw:${HOME}=ro none /tmp/aufs
+ or
+ # mount -t aufs -o br:/tmp/rw none /tmp/aufs
+ # mount -o remount,append:${HOME}=ro /tmp/aufs
+
+If you disable CONFIG_AUFS_COMPAT in your configuration, you can remove the
+default branch permission '=ro' since '=rw' is set to the first branch
+only by default.
+
+ # mount -t aufs -o br:/tmp/rw:${HOME} none /tmp/aufs
+
+Then, you can see whole tree of your home dir through /tmp/aufs. If
+you modify a file under /tmp/aufs, the one on your home directory is
+not affected, instead the same named file will be newly created under
+/tmp/rw. And all of your modification to the file will be applied to
+the one under /tmp/rw. This is called the file based Copy on Write
+(COW) method.
+Aufs mount options are described in the generated ./aufs.5.
+
+Additionally, there are some sample usages of aufs which are a
+diskless system with network booting, and LiveCD over NFS.
+See ./sample/diskless in detail.
+
+
+5. Contact
+----------------------------------------
+When you have any problems or strange behaviour in aufs, please let me
+know with:
+- /proc/mounts (instead of the output of mount(8))
+- /sys/fs/aufs/* (if you have them)
+- /sys/module/aufs/*
+- linux kernel version
+ if your kernel is not plain, for example modified by distributor,
+ the url where i can download its source is necessary too.
+- aufs version which was printed at loading the module or booting the
+ system, instead of the date you downloaded.
+- configuration (define/undefine CONFIG_AUFS_xxx, or plain local.mk is
+ used or not)
+- kernel configuration or /proc/config.gz (if you have it)
+- behaviour which you think to be incorrect
+- actual operation, reproducible one is better
+- mailto: aufs-users at lists.sourceforge.net
+
+Usually, I don't watch the Public Areas(Bugs, Support Requests, Patches,
+and Feature Requests) on SourceForge. Please join and write to
+aufs-users ML.
+
+
+6. Acknowledgements
+----------------------------------------
+Thanks to everyone who have tried and are using aufs, especially who
+have reported a bug or any feedback.
+
+Tomas Matejicek(slax.org) made a donation (much more than once).
+Dai Itasaka made a donation (2007/8).
+Chuck Smith made a donation (2008/4).
+
+Thank you very much.
+Donations are always, including future donations, very important and
+helpful for me to keep on developing aufs.
+If you have a plan to develop or customize linux, feel free to ask me
+a job with some donations or payments.
+
+
+7.
+----------------------------------------
+If you are an experienced user, no explanation is needed. Aufs is
+just a linux filesystem module. take a glance at ./local.mk,
+aufs.5, and Unionfs.
+
+
+Enjoy!
+
+# Local variables: ;
+# mode: text;
+# End: ;
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 2/67] aufs manual
2008-05-16 14:32 [PATCH 1/67] aufs document hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 3/67] aufs global header file hooanon05
2008-05-16 14:59 ` [PATCH 1/67] aufs document Dave Quigley
1 sibling, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs manual
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
Documentation/filesystems/aufs/aufs.5 | 1608 +++++++++++++++++++++++++++++++++
1 files changed, 1608 insertions(+), 0 deletions(-)
diff --git a/Documentation/filesystems/aufs/aufs.5 b/Documentation/filesystems/aufs/aufs.5
new file mode 100644
index 0000000..7335e14
--- /dev/null
+++ b/Documentation/filesystems/aufs/aufs.5
@@ -0,0 +1,1608 @@
+.ds AUFS_VERSION 20080516-mm
+.ds AUFS_XINO_FNAME .aufs.xino
+.ds AUFS_XINO_DEFPATH /tmp/.aufs.xino
+.ds AUFS_DIRWH_DEF 3
+.ds AUFS_WH_PFX .wh.
+.ds AUFS_WH_PFX_LEN 4
+.ds AUFS_WKQ_NAME aufsd
+.ds AUFS_NWKQ_DEF 4
+.ds AUFS_WH_DIROPQ .wh..wh..opq
+.ds AUFS_WH_BASENAME .wh.aufs
+.ds AUFS_WH_PLINKDIR .wh.plink
+.ds AUFS_BRANCH_MAX 127
+.ds AUFS_MFS_SECOND_DEF 30
+.\".so aufs.tmac
+.
+.eo
+.de TQ
+.br
+.ns
+.TP \$1
+..
+.de Bu
+.IP \(bu 4
+..
+.ec
+.\" end of macro definitions
+.
+.\" ----------------------------------------------------------------------
+.TH aufs 5 \*[AUFS_VERSION] Linux "Linux Aufs User\[aq]s Manual"
+.SH NAME
+aufs \- another unionfs. version \*[AUFS_VERSION]
+
+.\" ----------------------------------------------------------------------
+.SH DESCRIPTION
+Aufs is a stackable unification filesystem such as Unionfs, which unifies
+several directories and provides a merged single directory.
+In the early days, aufs was entirely re-designed and re-implemented
+Unionfs Version 1.x series. After
+many original ideas, approaches and improvements, it
+becomes totally different from Unionfs while keeping the basic features.
+See Unionfs Version 1.x series for the basic features.
+Recently, Unionfs Version 2.x series begin taking some of same
+approaches to aufs\[aq]s.
+
+.\" ----------------------------------------------------------------------
+.SH MOUNT OPTIONS
+At mount-time, the order of interpreting options is,
+.RS
+.Bu
+simple flags, except xino/noxino, udba=inotify and dlgt
+.Bu
+branches
+.Bu
+xino/noxino
+.Bu
+udba=inotify
+.Bu
+dlgt
+.RE
+
+At remount-time,
+the options are interpreted in the given order,
+e.g. left to right, except dlgt. The \[oq]dlgt\[cq] option is
+disabled in interpreting.
+.RS
+.Bu
+create or remove
+whiteout-base(\*[AUFS_WH_PFX]\*[AUFS_WH_BASENAME]) and
+whplink-dir(\*[AUFS_WH_PFX]\*[AUFS_WH_PLINKDIR]) if necessary
+.Bu
+re-enable dlgt if necessary
+.RE
+.
+.TP
+.B br:BRANCH[:BRANCH ...] (dirs=BRANCH[:BRANCH ...])
+Adds new branches.
+(cf. Branch Syntax).
+
+Aufs rejects the branch which is an ancestor or a descendant of anther
+branch. It is called overlapped. When the branch is loopback-mounted
+directory, aufs also checks the source fs-image file of loopback
+device. If the source file is a descendant of another branch, it will
+be rejected too.
+
+After mounting aufs or adding a branch, if you move a branch under
+another branch and make it descendant of anther branch, aufs will not
+work correctly.
+.
+.TP
+.B [ add | ins ]:index:BRANCH
+Adds a new branch.
+The index begins with 0.
+Aufs creates
+whiteout-base(\*[AUFS_WH_PFX]\*[AUFS_WH_BASENAME]) and
+whplink-dir(\*[AUFS_WH_PFX]\*[AUFS_WH_PLINKDIR]) if necessary.
+
+If there is the same named file on the lower branch (larger index),
+aufs will hide the lower file.
+You can only see the highest file.
+You will be confused if the added branch has whiteouts (including
+diropq), they may or may not hide the lower entries.
+.\" It is recommended to make sure that the added branch has no whiteout.
+
+If a process have once mapped a file by mmap(2) with MAP_SHARED
+and the same named file exists on the lower branch,
+the process still refers the file on the lower(hidden)
+branch after adding the branch.
+If you want to update the contents of a process address space after
+adding, you need to restart your process or open/mmap the file again.
+.\" Usually, such files are executables or shared libraries.
+(cf. Branch Syntax).
+.
+.TP
+.B del:dir
+Removes a branch.
+Aufs does not remove
+whiteout-base(\*[AUFS_WH_PFX]\*[AUFS_WH_BASENAME]) and
+whplink-dir(\*[AUFS_WH_PFX]\*[AUFS_WH_PLINKDIR]) automatically.
+For example, when you add a RO branch which was unified as RW, you
+will see whiteout-base or whplink-dir on the added RO branch.
+
+If a process is referencing the file/directory on the deleting branch
+(by open, mmap, current working directory, etc.), aufs will return an
+error EBUSY.
+.
+.TP
+.B mod:BRANCH
+Modifies the permission flags of the branch.
+Aufs creates or removes
+whiteout-base(\*[AUFS_WH_PFX]\*[AUFS_WH_BASENAME]) and/or
+whplink-dir(\*[AUFS_WH_PFX]\*[AUFS_WH_PLINKDIR]) if necessary.
+
+If the branch permission is been changing \[oq]rw\[cq] to \[oq]ro\[cq], and a process
+is mapping a file by mmap(2)
+.\" with MAP_SHARED
+on the branch, the process may or may not
+be able to modify its mapped memory region after modifying branch
+permission flags.
+(cf. Branch Syntax).
+.
+.TP
+.B append:BRANCH
+equivalent to \[oq]add:(last index + 1):BRANCH\[cq].
+(cf. Branch Syntax).
+.
+.TP
+.B prepend:BRANCH
+equivalent to \[oq]add:0:BRANCH.\[cq]
+(cf. Branch Syntax).
+.
+.TP
+.B xino=filename
+Use external inode number bitmap and translation table. It is set to
+<FirstWritableBranch>/\*[AUFS_XINO_FNAME] by default, or
+\*[AUFS_XINO_DEFPATH].
+Comma character in filename is not allowed.
+
+The files are created per an aufs and per a branch filesystem, and
+unlinked. So you
+cannot find this file, but it exists and is read/written frequently by
+aufs.
+(cf. External Inode Number Bitmap and Translation Table).
+.
+.TP
+.B noxino
+Stop using external inode number bitmap and translation table.
+
+If you use this option,
+Some applications will not work correctly.
+.\" And pseudo link feature will not work after the inode cache is
+.\" shrunk.
+(cf. External Inode Number Bitmap and Translation Table).
+.
+.TP
+.B trunc_xib
+Truncate the external inode number bitmap file. The truncation is done
+automatically when you delete a branch unless you do not specify
+\[oq]notrunc_xib\[cq] option.
+(cf. External Inode Number Bitmap and Translation Table).
+.
+.TP
+.B notrunc_xib
+Stop truncating the external inode number bitmap file when you delete
+a branch.
+(cf. External Inode Number Bitmap and Translation Table).
+.
+.TP
+.B create_policy | create=CREATE_POLICY
+.TQ
+.B copyup_policy | copyup | cpup=COPYUP_POLICY
+Policies to select one among multiple writable branches. The default
+values are \[oq]create=tdp\[cq] and \[oq]cpup=tdp\[cq].
+link(2) and rename(2) systemcalls have an exception. In aufs, they
+try keeping their operations in the branch where the source exists.
+(cf. Policies to Select One among Multiple Writable Branches).
+.
+.TP
+.B verbose | v
+Print some information.
+Currently, it is only busy file (or inode) at deleting a branch.
+.
+.TP
+.B noverbose | quiet | q | silent
+Disable \[oq]verbose\[cq] option.
+This is default value.
+.
+.TP
+.B dirwh=N
+Watermark to remove a dir actually at rmdir(2) and rename(2).
+
+If the target dir which is being removed or renamed (destination dir)
+has a huge number of whiteouts, i.e. the dir is empty logically but
+physically, the cost to remove/rename the single
+dir may be very high.
+It is
+required to unlink all of whiteouts internally before issuing
+rmdir/rename to the branch.
+To reduce the cost of single systemcall,
+aufs renames the target dir to a whiteout-ed temporary name and
+invokes a pre-created
+kernel thread to remove whiteout-ed children and the target dir.
+The rmdir/rename systemcall returns just after kicking the thread.
+
+When the number of whiteout-ed children is less than the value of
+dirwh, aufs remove them in a single systemcall instead of passing
+another thread.
+This value is ignored when the branch is NFS.
+The default value is \*[AUFS_DIRWH_DEF].
+.
+.TP
+.B plink
+.TQ
+.B noplink
+Specifies to use \[oq]pseudo link\[cq] feature or not.
+The default is \[oq]plink\[cq] which means use this feature.
+(cf. Pseudo Link)
+.
+.TP
+.B clean_plink
+Removes all pseudo-links in memory.
+In order to make pseudo-link permanent, use
+\[oq]auplink\[cq] script just before one of these operations,
+unmounting aufs,
+using \[oq]ro\[cq] or \[oq]noplink\[cq] mount option,
+deleting a branch from aufs,
+adding a branch into aufs,
+or changing your writable branch as readonly.
+If you installed both of /sbin/mount.aufs and /sbin/umount.aufs, and your
+mount(8) and umount(8) support them, and /etc/default/auplink is configured,
+\[oq]auplink\[cq] script will be executed automatically and flush pseudo-links.
+(cf. Pseudo Link)
+.
+.TP
+.B udba=none | reval | inotify
+Specifies the level of UDBA (User\[aq]s Direct Branch Access) test.
+(cf. User\[aq]s Direct Branch Access and Inotify Limitation).
+.
+.TP
+.B diropq=whiteouted | w | always | a
+Specifies whether mkdir(2) and rename(2) dir case make the created directory
+\[oq]opaque\[cq] or not.
+In other words, to create \[oq]\*[AUFS_WH_DIROPQ]\[cq] under the created or renamed
+directory, or not to create.
+When you specify diropq=w or diropq=whiteouted, aufs will not create
+it if the
+directory was not whiteouted or opaqued. If the directory was whiteouted
+or opaqued, the created or renamed directory will be opaque.
+When you specify diropq=a or diropq==always, aufs will always create
+it regardless
+the directory was whiteouted/opaqued or not.
+The default value is diropq=w, it means not to create when it is unnecessary.
+If you define CONFIG_AUFS_COMPAT at aufs compiling time, the default will be
+diropq=a.
+You need to consider this option if you are planning to add a branch later
+since \[oq]diropq\[cq] affects the same named directory on the added branch.
+.
+.TP
+.B warn_perm
+.TQ
+.B nowarn_perm
+Adding a branch, aufs will issue a warning about uid/gid/permission of
+the adding branch directory,
+when they differ from the existing branch\[aq]s. This difference may or
+may not impose a security risk.
+If you are sure that there is no problem and want to stop the warning,
+use \[oq]nowarn_perm\[cq] option.
+The default is \[oq]warn_perm\[cq] (cf. DIAGNOSTICS).
+.
+.TP
+.B coo=none | leaf | all
+Specifies copyup-on-open level.
+When you open a file which is on readonly branch, aufs opens the file after
+copying-up it to the writable branch following this level.
+When the keyword \[oq]all\[cq] is specified, aufs copies-up the opening object even if
+it is a directory. In this case, simple \[oq]ls\[cq] or \[oq]find\[cq] cause the copyup and
+your writable branch will have a lot of empty directories.
+When the keyword \[oq]leaf\[cq] is specified, aufs copies-up the opening object except
+directory.
+The keyword \[oq]none\[cq] disables copyup-on-open.
+The default is \[oq]coo=none\[cq].
+.
+.TP
+.B dlgt
+.TQ
+.B nodlgt
+If you do not want your application to access branches though aufs or
+to be traced strictly by task I/O accounting, you can
+use the kernel threads in aufs. If you enable CONFIG_AUFS_DLGT and
+specify \[oq]dlgt\[cq] mount option, then
+aufs delegates its internal
+access to the branches to the kernel threads.
+
+When you define CONFIG_SECURITY and use any type of Linux Security Module
+(LSM), for example SUSE AppArmor, you may meet some errors or
+warnings from your security module. Because aufs access its branches
+internally, your security module may detect, report, or prohibit it.
+The behaviour is highly depending upon your security module and its
+configuration.
+In this case, you can use \[oq]dlgt\[cq] mount option, too.
+Your LSM will see the
+aufs kernel threads access to the branch, instead of your
+application.
+
+The delegation may damage the performance since it includes
+task-switch (scheduling) and waits for the thread to complete the
+delegated access. You should consider increasing the number of the
+kernel thread specifying the aufs module parameter \[oq]nwkq.\[cq]
+
+Currently, aufs does NOT delegate it at mount and remount time.
+The default is nodlgt which means aufs does not delegate the internal
+access.
+.\" .
+.\" .TP
+.\" .B dirperm1
+.\" .TQ
+.\" .B nodirperm1
+.\" By default (nodirperm1), aufs checks the permission bits of target
+.\" directory on all branches. If any of them refused the requested
+.\" access, then aufs returns negative even if the topmost permission bits
+.\" of the directory allowed the access.
+.\" If you enable CONFIG_AUFS_DLGT and specify \[oq]dirperm1\[cq] option, aufs
+.\" doesn\[aq]t check the directories on all lower branches but the topmost
+.\" one.
+.
+.TP
+.B shwh
+.TQ
+.B noshwh
+By default (noshwh), aufs doesn\[aq]t show the whiteouts and
+they just hide the same named entries in the lower branches. The
+whiteout itself also never be appeared.
+If you enable CONFIG_AUFS_SHWH and specify \[oq]shwh\[cq] option, aufs
+will show you the name of whiteouts
+with keeping its feature to hide the lowers.
+Honestly speaking, I am rather confused with this \[oq]visible whiteouts.\[cq]
+But a user who originally requested this feature wrote a nice how-to
+document about this feature. See Tips file in the aufs CVS tree.
+
+.\" ----------------------------------------------------------------------
+.SH Module Parameters
+.TP
+.B nwkq=N
+The number of kernel thread named \*[AUFS_WKQ_NAME].
+
+Those threads stay in the system while the aufs module is loaded,
+and handle the special I/O requests from aufs.
+The default value is \*[AUFS_NWKQ_DEF].
+
+The special I/O requests from aufs include a part of copy-up, lookup,
+directory handling, pseudo-link, xino file operations and the
+delegated access to branches.
+For example, Unix filesystems allow you to rmdir(2) which has no write
+permission bit, if its parent directory has write permission bit. In aufs, the
+removing directory may or may not have whiteout or \[oq]dir opaque\[cq] mark as its
+child. And aufs needs to unlink(2) them before rmdir(2).
+Therefore aufs delegates the actual unlink(2) and rmdir(2) to another kernel
+thread which has been created already and has a superuser privilege.
+
+If you enable CONFIG_SYSFS, you can check this value through
+<sysfs>/module/aufs/parameters/nwkq.
+
+So how many threads is enough? You can check it by
+<sysfs>/fs/aufs/stat, if you enable CONFIG_AUFS_SYSAUFS (for
+linux\-2.6.24 and earlier) or CONFIG_AUFS_STAT (for linux\-2.6.25 and
+later) too.
+It shows the maximum number of the enqueued work
+at a time per a thread. Usually they are all small numbers or
+0. If your workload is heavy
+and you feel the response is low, then check these values. If there
+are no zero and any of them is larger than 2 or 3, you should set \[oq]nwkq\[cq]
+module parameter greater then the default value.
+But the reason of the bad response is in your branch filesystem, to
+increase the number of aufs thread will not help you.
+
+The last number in <sysfs>/fs/aufs/stat after comma is the maximum
+number of the \[oq]no-wait\[cq] enqueued work at a time. Aufs enqueues such
+work to the system global workqueue called \[oq]events\[cq], but does not wait
+for its completion. Usually they does no harm the time-performance of
+aufs.
+.
+.TP
+.B brs=1 | 0
+Specifies to use the branch path data file under sysfs or not.
+
+If the number of your branches is large or their path is long
+and you meet the limitation of mount(8) ro /etc/mtab, you need to
+enable CONFIG_SYSFS and set aufs module parameter brs=1.
+If your linux version is linux\-2.6.24 and earlier, you need to enable
+CONFIG_AUFS_SYSAUFS too.
+
+When this parameter is set as 1, aufs does not show \[oq]br:\[cq] (or dirs=)
+mount option through /proc/mounts, and /sbin/mount.aufs does not put it
+to /etc/mtab. So you can keep yourself from the page limitation of
+mount(8) or /etc/mtab.
+Aufs shows branch paths through <sysfs>/fs/aufs/si_XXX/brNNN.
+Actually the file under sysfs has also a size limitation, but I don\[aq]t
+think it is harmful.
+
+The default is brs=0, which means <sysfs>/fs/aufs/si_XXX/brNNN does not exist
+and \[oq]br:\[cq] option will appear in /proc/mounts, and /etc/mtab if you
+install /sbin/mount.aufs.
+If you did not enable CONFIG_AUFS_SYSAUFS (for
+linux\-2.6.24 and earlier), this parameter will be
+ignored.
+
+There is one more side effect in setting 1 to this parameter.
+If you rename your branch, the branch path written in /etc/mtab will be
+obsoleted and the future remount will meet some error due to the
+unmatched parameters (Remember that mount(8) may take the options from
+/etc/mtab and pass them to the systemcall).
+If you set 1, /etc/mtab will not hold the branch path and you will not
+meet such trouble. On the other hand, /proc/mounts which holds the
+branch path is updated dynamically. So it must not be obsoleted.
+.
+.TP
+.B sysrq=key
+Specifies MagicSysRq key for debugging aufs.
+You need to enable both of CONFIG_MAGIC_SYSRQ and CONFIG_AUFS_DEBUG.
+If your linux version is linux\-2.6.24 and earlier, you need to enable
+CONFIG_AUFS_SYSAUFS too.
+Currently this is for developers only.
+The default is \[oq]a\[cq].
+
+.\" ----------------------------------------------------------------------
+.SH Branch Syntax
+.TP
+.B dir_path[ =permission [ + attribute ] ]
+.TQ
+.B permission := rw | ro | rr
+.TQ
+.B attribute := wh | nolwh
+dir_path is a directory path.
+The keyword after \[oq]dir_path=\[cq] is a
+permission flags for that branch.
+Comma, colon and the permission flags string (including \[oq]=\[cq])in the path
+are not allowed.
+Any filesystem can be a branch, but aufs and unionfs.
+If you specify aufs or unionfs as a branch, aufs will return an error
+saying it is overlapped or nested.
+If you enable CONFIG_AUFS_ROBR, you can use aufs as a non-writable
+branch of another aufs.
+
+Cramfs in linux stable release has strange inodes and it makes aufs
+confused. For example,
+.nf
+$ mkdir -p w/d1 w/d2
+$ > w/z1
+$ > w/z2
+$ mkcramfs w cramfs
+$ sudo mount -t cramfs -o ro,loop cramfs /mnt
+$ find /mnt -ls
+ 76 1 drwxr-xr-x 1 jro 232 64 Jan 1 1970 /mnt
+ 1 1 drwxr-xr-x 1 jro 232 0 Jan 1 1970 /mnt/d1
+ 1 1 drwxr-xr-x 1 jro 232 0 Jan 1 1970 /mnt/d2
+ 1 1 -rw-r--r-- 1 jro 232 0 Jan 1 1970 /mnt/z1
+ 1 1 -rw-r--r-- 1 jro 232 0 Jan 1 1970 /mnt/z2
+.fi
+
+All these two directories and two files have the same inode with one
+as their link count. Aufs cannot handle such inode correctly.
+Currently, aufs involves a tiny workaround for such inodes. But some
+applications may not work correctly since aufs inode number for such
+inode will change silently.
+If you do not have any empty files, empty directories or special files,
+inodes on cramfs will be all fine.
+
+A branch should not be shared as the writable branch between multiple
+aufs. A readonly branch can be shared.
+
+The maximum number of branches is configurable at compile time.
+The current value is \*[AUFS_BRANCH_MAX] which depends upon
+configuration.
+
+When an unknown permission or attribute is given, aufs sets ro to that
+branch silently.
+
+.SS Permission
+.
+.TP
+.B rw
+Readable and writable branch. Set as default for the first branch.
+If the branch filesystem is mounted as readonly, you cannot set it \[oq]rw.\[cq]
+.\" A filesystem which does not support link(2) and i_op\->setattr(), for
+.\" example FAT, will not be used as the writable branch.
+.
+.TP
+.B ro
+Readonly branch and it has no whiteouts on it.
+Set as default for all branches except the first one. Aufs never issue
+both of write operation and lookup operation for whiteout to this branch.
+.
+.TP
+.B rr
+Real readonly branch, special case of \[oq]ro\[cq], for natively readonly
+branch. Assuming the branch is natively readonly, aufs can optimize
+some internal operation. For example, if you specify \[oq]udba=inotify\[cq]
+option, aufs does not set inotify for the things on rr branch.
+Set by default for a branch whose fs-type is either \[oq]iso9660\[cq],
+\[oq]cramfs\[cq], \[oq]romfs\[cq] or \[oq]squashfs.\[cq]
+
+.SS Attribute
+.
+.TP
+.B wh
+Readonly branch and it has/might have whiteouts on it.
+Aufs never issue write operation to this branch, but lookup for whiteout.
+Use this as \[oq]<branch_dir>=ro+wh\[cq].
+.
+.TP
+.B nolwh
+Usually, aufs creates a whiteout as a hardlink on a writable
+branch. This attributes prohibits aufs to create the hardlinked
+whiteout, including the source file of all hardlinked whiteout
+(\*[AUFS_WH_PFX]\*[AUFS_WH_BASENAME].)
+If you do not like a hardlink, or your writable branch does not support
+link(2), then use this attribute.
+But I am afraid a filesystem which does not support link(2) natively
+will fail in other place such as copy-up.
+Use this as \[oq]<branch_dir>=rw+nolwh\[cq].
+Also you may want to try \[oq]noplink\[cq] mount option, while it is not recommended.
+
+.\" ----------------------------------------------------------------------
+.SH External Inode Number Bitmap and Translation Table (xino)
+Aufs uses one external bitmap file and one external inode number
+translation table files per an aufs and per a branch
+filesystem by
+default. The bitmap is for recycling aufs inode number and the others
+are a table for converting an inode number on a branch to
+an aufs inode number. The default path
+is \[oq]first writable branch\[cq]/\*[AUFS_XINO_FNAME].
+If there is no writable branch, the
+default path
+will be \*[AUFS_XINO_DEFPATH].
+.\" A user who executes mount(8) needs the privilege to create xino
+.\" file.
+
+Those files are always opened and read/write by aufs frequently.
+If your writable branch is on flash memory device, it is recommended
+to put xino files on other than flash memory by specifying \[oq]xino=\[cq]
+mount option.
+
+The
+maximum file size of the bitmap is, basically, the amount of the
+number of all the files on all branches divided by 8 (the number of
+bits in a byte).
+For example, on a 4KB page size system, if you have 32,768 (or
+2,599,968) files in aufs world,
+then the maximum file size of the bitmap is 4KB (or 320KB).
+
+The
+maximum file size of the table will
+be \[oq]max inode number on the branch x size of an inode number\[cq].
+For example in 32bit environment,
+
+.nf
+$ df -i /branch_fs
+/dev/hda14 2599968 203127 2396841 8% /branch_fs
+.fi
+
+and /branch_fs is an branch of the aufs. When the inode number is
+assigned contiguously (without \[oq]hole\[cq]), the maximum xino file size for
+/branch_fs will be 2,599,968 x 4 bytes = about 10 MB. But it might not be
+allocated all of disk blocks.
+When the inode number is assigned discontinuously, the maximum size of
+xino file will be the largest inode number on a branch x 4 bytes.
+Additionally, the file size is limited to LLONG_MAX or the s_maxbytes
+in filesystem\[aq]s superblock (s_maxbytes may be smaller than
+LLONG_MAX). So the
+support-able largest inode number on a branch is less than
+2305843009213693950 (LLONG_MAX/4\-1).
+This is the current limitation of aufs.
+On 64bit environment, this limitation becomes more strict and the
+supported largest inode number is less than LLONG_MAX/8\-1.
+
+The xino files are always hidden, i.e. removed. So you cannot
+do \[oq]ls \-l xino_file\[cq].
+If you enable CONFIG_SYSFS, you can check these information through
+<sysfs>/fs/aufs/<si_id>/xino (for linux\-2.6.24 and earlier, you
+need to enable CONFIG_AUFS_SYSAUFS too).
+The first line in <sysfs>/fs/aufs/<si_id>/xino shows the information
+of the bitmap file, in the format of,
+
+.nf
+<blocks>x<block size> <file size>
+.fi
+
+Note that a filesystem usually has a
+feature called pre-allocation, which means a number of
+blocks are allocated automatically, and then deallocated
+silently when the filesystem thinks they are unnecessary.
+You do not have to be surprised the sudden changes of the number of
+blocks, when your filesystem which xino files are placed supports the
+pre-allocation feature.
+
+The rests are hidden xino file information in the format of,
+
+.nf
+<branch index>: <file count>, <blocks>x<block size> <file size>
+.fi
+
+If the file count is larger than 1, it means some of your branches are
+on the same filesystem and the xino file is shared by them.
+Note that the file size may not be equal to the actual consuming blocks
+since xino file is a sparse file, i.e. a hole in a file which does not
+consume any disk blocks.
+
+Once you unmount aufs, the xino files for that aufs are totally gone.
+It means that the inode number is not permanent.
+
+The xino files should be created on the filesystem except NFS.
+If your first writable branch is NFS, you will need to specify xino
+file path other than NFS.
+Also if you are going to remove the branch where xino files exist or
+change the branch permission to readonly, you need to use xino option
+before del/mod the branch.
+
+The bitmap file can be truncated.
+For example, if you delete a branch which has huge number of files,
+many inode numbers will be recycled and the bitmap will be truncated
+to smaller size. Aufs does this automatically when a branch is
+deleted.
+You can truncate it anytime you like if you specify \[oq]trunc_xib\[cq] mount
+option. But when the accessed inode number was not deleted, nothing
+will be truncated.
+If you do not want to truncate it (it may be slow) when you delete a
+branch, specify \[oq]notrunc_xib\[cq] after \[oq]del\[cq] mount option.
+
+If you do not want to use xino, use noxino mount option. Use this
+option with care, since the inode number may be changed silently and
+unexpectedly anytime.
+For example,
+rmdir failure, recursive chmod/chown/etc to a large and deep directory
+or anything else.
+And some applications will not work correctly.
+.\" When the inode number has been changed, your system
+.\" can be crazy.
+If you want to change the xino default path, use xino mount option.
+
+After you add branches, the persistence of inode number may not be
+guaranteed.
+At remount time, cached but unused inodes are discarded.
+And the newly appeared inode may have different inode number at the
+next access time. The inodes in use have the persistent inode number.
+
+When aufs assigned an inode number to a file, and if you create the
+same named file on the upper branch directly, then the next time you
+access the file, aufs may assign another inode number to the file even
+if you use xino option.
+Some applications may treat the file whose inode number has been
+changed as totally different file.
+
+.\" ----------------------------------------------------------------------
+.SH Pseudo Link (hardlink over branches)
+Aufs supports \[oq]pseudo link\[cq] which is a logical hard-link over
+branches (cf. ln(1) and link(2)).
+In other words, a copied-up file by link(2) and a copied-up file which was
+hard-linked on a readonly branch filesystem.
+
+When you have files named fileA and fileB which are
+hardlinked on a readonly branch, if you write something into fileA,
+aufs copies-up fileA to a writable branch, and write(2) the originally
+requested thing to the copied-up fileA. On the writable branch,
+fileA is not hardlinked.
+But aufs remembers it was hardlinked, and handles fileB as if it existed
+on the writable branch, by referencing fileA\[aq]s inode on the writable
+branch as fileB\[aq]s inode.
+
+Once you unmount aufs, the plink info for that aufs kept in memory are totally
+gone.
+It means that the pseudo-link is not permanent.
+If you want to make plink permanent, try \[oq]auplink\[cq] script just before
+one of these operations,
+unmounting your aufs,
+using \[oq]ro\[cq] or \[oq]noplink\[cq] mount option,
+deleting a branch from aufs,
+adding a branch into aufs,
+or changing your writable branch to readonly.
+
+This script will reproduces all real hardlinks on a writable branch by linking
+them, and removes pseudo-link info in memory and temporary link on the
+writable branch.
+Since this script access your branches directly, you cannot hide them by
+\[oq]mount \-\-bind /tmp /branch\[cq] or something.
+
+If you are willing to rebuild your aufs with the same branches later, you
+should use auplink script before you umount your aufs.
+If you installed both of /sbin/mount.aufs and /sbin/umount.aufs, and your
+mount(8) and umount(8) support them, and /etc/default/auplink is configured,
+\[oq]auplink\[cq] script will be executed automatically and flush pseudo-links.
+
+The /etc/default/auplink is a simple shell script which does nothing but defines
+$FLUSH. If your aufs mount point is set in $FLUSH, \[oq]auplink\[cq] flushes
+the pseudo-links on that mount point.
+If $FLUSH is set to \[oq]ALL\[cq], \[oq]auplink\[cq] will be executed for every aufs.
+
+The \[oq]auplink\[cq] script uses \[oq]aulchown\[cq] binary, you need to install it too.
+The \[oq]auplink\[cq] script executes \[oq]find\[cq] and \[oq]mount \-o remount\[cq], they may take a
+long time and impact the later system performance.
+If you did not install /sbin/mount.aufs, /sbin/umount.aufs or /sbin/auplink,
+but you want to flush pseudo-links, then you need to execute \[oq]auplink\[cq] manually.
+If you installed and configured them, but do not want to execute \[oq]auplink\[cq] at
+umount time, then use \[oq]\-i\[cq] option for umount(8).
+
+.nf
+# auplink /your/aufs/root flush
+# umount /your/aufs/root
+or
+# auplink /your/aufs/root flush
+# mount -o remount,mod:/your/writable/branch=ro /your/aufs/root
+or
+# auplink /your/aufs/root flush
+# mount -o remount,noplink /your/aufs/root
+or
+# auplink /your/aufs/root flush
+# mount -o remount,del:/your/aufs/branch /your/aufs/root
+or
+# auplink /your/aufs/root flush
+# mount -o remount,append:/your/aufs/branch /your/aufs/root
+.fi
+
+The plinks are kept both in memory and on disk. When they consumes too much
+resources on your system, you can use the \[oq]auplink\[cq] script at anytime and
+throw away the unnecessary pseudo-links in safe.
+
+Additionally, the \[oq]auplink\[cq] script is very useful for some security reasons.
+For example, when you have a directory whose permission flags
+are 0700, and a file who is 0644 under the 0700 directory. Usually,
+all files under the 0700 directory are private and no one else can see
+the file. But when the directory is 0711 and someone else knows the 0644
+filename, he can read the file.
+
+Basically, aufs pseudo-link feature creates a temporary link under the
+directory whose owner is root and the permission flags are 0700.
+But when the writable branch is NFS, aufs sets 0711 to the directory.
+When the 0644 file is pseudo-linked, the temporary link, of course the
+contents of the file is totally equivalent, will be created under the
+0711 directory. The filename will be generated by its inode number.
+While it is hard to know the generated filename, someone else may try peeping
+the temporary pseudo-linked file by his software tool which may try the name
+from one to MAX_INT or something.
+In this case, the 0644 file will be read unexpectedly.
+I am afraid that leaving the temporary pseudo-links can be a security hole.
+It makes sense to execute \[oq]auplink /your/aufs/root flush\[cq]
+periodically, when your writable branch is NFS.
+
+When your writable branch is not NFS, or all users are careful enough to set 0600
+to their private files, you do not have to worry about this issue.
+
+If you do not want this feature, use \[oq]noplink\[cq] mount option and you do
+not need
+to install \[oq]auplink\[cq] script and \[oq]aulchown\[cq] binary.
+
+.SS The behaviours of plink and noplink
+This sample shows that the \[oq]f_src_linked2\[cq] with \[oq]noplink\[cq] option cannot follow
+the link.
+
+.nf
+none on /dev/shm/u type aufs (rw,xino=/dev/shm/rw/.aufs.xino,br:/dev/shm/rw=rw:/dev/shm/ro=ro)
+$ ls -li ../r?/f_src_linked* ./f_src_linked* ./copied
+ls: ./copied: No such file or directory
+15 -rw-r--r-- 2 jro jro 2 Dec 22 11:03 ../ro/f_src_linked
+15 -rw-r--r-- 2 jro jro 2 Dec 22 11:03 ../ro/f_src_linked2
+22 -rw-r--r-- 2 jro jro 2 Dec 22 11:03 ./f_src_linked
+22 -rw-r--r-- 2 jro jro 2 Dec 22 11:03 ./f_src_linked2
+$ echo abc >> f_src_linked
+$ cp f_src_linked copied
+$ ls -li ../r?/f_src_linked* ./f_src_linked* ./copied
+15 -rw-r--r-- 2 jro jro 2 Dec 22 11:03 ../ro/f_src_linked
+15 -rw-r--r-- 2 jro jro 2 Dec 22 11:03 ../ro/f_src_linked2
+36 -rw-r--r-- 2 jro jro 6 Dec 22 11:03 ../rw/f_src_linked
+53 -rw-r--r-- 1 jro jro 6 Dec 22 11:03 ./copied
+22 -rw-r--r-- 2 jro jro 6 Dec 22 11:03 ./f_src_linked
+22 -rw-r--r-- 2 jro jro 6 Dec 22 11:03 ./f_src_linked2
+$ cmp copied f_src_linked2
+$
+
+none on /dev/shm/u type aufs (rw,xino=/dev/shm/rw/.aufs.xino,noplink,br:/dev/shm/rw=rw:/dev/shm/ro=ro)
+$ ls -li ../r?/f_src_linked* ./f_src_linked* ./copied
+ls: ./copied: No such file or directory
+17 -rw-r--r-- 2 jro jro 2 Dec 22 11:03 ../ro/f_src_linked
+17 -rw-r--r-- 2 jro jro 2 Dec 22 11:03 ../ro/f_src_linked2
+23 -rw-r--r-- 2 jro jro 2 Dec 22 11:03 ./f_src_linked
+23 -rw-r--r-- 2 jro jro 2 Dec 22 11:03 ./f_src_linked2
+$ echo abc >> f_src_linked
+$ cp f_src_linked copied
+$ ls -li ../r?/f_src_linked* ./f_src_linked* ./copied
+17 -rw-r--r-- 2 jro jro 2 Dec 22 11:03 ../ro/f_src_linked
+17 -rw-r--r-- 2 jro jro 2 Dec 22 11:03 ../ro/f_src_linked2
+36 -rw-r--r-- 1 jro jro 6 Dec 22 11:03 ../rw/f_src_linked
+53 -rw-r--r-- 1 jro jro 6 Dec 22 11:03 ./copied
+23 -rw-r--r-- 2 jro jro 6 Dec 22 11:03 ./f_src_linked
+23 -rw-r--r-- 2 jro jro 6 Dec 22 11:03 ./f_src_linked2
+$ cmp copied f_src_linked2
+cmp: EOF on f_src_linked2
+$
+.fi
+
+.\"
+.\" If you add/del a branch, or link/unlink the pseudo-linked
+.\" file on a branch
+.\" directly, aufs cannot keep the correct link count, but the status of
+.\" \[oq]pseudo-linked.\[cq]
+.\" Those files may or may not keep the file data after you unlink the
+.\" file on the branch directly, especially the case of your branch is
+.\" NFS.
+
+If you add a branch which has fileA or fileB, aufs does not follow the
+pseudo link. The file on the added branch has no relation to the same
+named file(s) on the lower branch(es).
+If you use noxino mount option, pseudo link will not work after the
+kernel shrinks the inode cache.
+
+This feature will not work for squashfs before version 3.2 since its
+inode is tricky.
+When the inode is hardlinked, squashfs inodes has the same inode
+number and correct link count, but the inode memory object is
+different. Squashfs inodes (before v3.2) are generated for each, even
+they are hardlinked.
+
+.\" ----------------------------------------------------------------------
+.SH User\[aq]s Direct Branch Access (UDBA)
+UDBA means a modification to a branch filesystem manually or directly,
+e.g. bypassing aufs.
+While aufs is designed and implemented to be safe after UDBA,
+it can make yourself and your aufs confused. And some information like
+aufs inode will be incorrect.
+For example, if you rename a file on a branch directly, the file on
+aufs may
+or may not be accessible through both of old and new name.
+Because aufs caches various information about the files on
+branches. And the cache still remains after UDBA.
+
+Aufs has a mount option named \[oq]udba\[cq] which specifies the test level at
+access time whether UDBA was happened or not.
+.
+.TP
+.B udba=none
+Aufs trusts the dentry and the inode cache on the system, and never
+test about UDBA. With this option, aufs runs fastest, but it may show
+you incorrect data.
+Additionally, if you often modify a branch
+directly, aufs will not be able to trace the changes of inodes on the
+branch. It can be a cause of wrong behaviour, deadlock or anything else.
+
+It is recommended to use this option only when you are sure that
+nobody access a file on a branch.
+It might be difficult for you to achieve real \[oq]no UDBA\[cq] world when you
+cannot stop your users doing \[oq]find / \-ls\[cq] or something.
+If you really want to forbid all of your users to UDBA, here is a trick
+for it.
+With this trick, users cannot see the
+branches directly and aufs runs with no problem, except \[oq]auplink\[cq] script.
+But if you are not familiar with aufs, this trick may make
+yourself confused.
+
+.nf
+# d=/tmp/.aufs.hide
+# mkdir $d
+# for i in $branches_you_want_to_hide
+> do
+> mount -n --bind $d $i
+> done
+.fi
+
+When you unmount the aufs, delete/modify the branch by remount, or you
+want to show the hidden branches again, unmount the bound
+/tmp/.aufs.hide.
+
+.nf
+# umount -n $branches_you_want_to_unbound
+.fi
+
+If you use FUSE filesystem as an aufs branch which supports hardlink,
+you should not set this option, since FUSE makes inode objects for
+each hardlinks (at least in linux\-2.6.23). When your FUSE filesystem
+maintains them at link/unlinking, it is equivalent
+to \[oq]direct branch access\[cq] for aufs.
+
+.
+.TP
+.B udba=reval
+Aufs tests only the existence of the file which existed. If
+the existed file was removed on the branch directly, aufs
+discard the cache about the file and
+re-lookup it. So the data will be updated.
+This test is at minimum level to keep the performance and ensure the
+existence of a file.
+This is default and aufs runs still fast.
+
+This rule leads to some unexpected situation, but I hope it is
+harmless. Those are totally depends upon cache. Here are just a few
+examples.
+.
+.RS
+.Bu
+If the file is cached as negative or
+not-existed, aufs does not test it. And the file is still handled as
+negative after a user created the file on a branch directly. If the
+file is not cached, aufs will lookup normally and find the file.
+.
+.Bu
+When the file is cached as positive or existed, and a user created the
+same named file directly on the upper branch. Aufs detects the cached
+inode of the file is still existing and will show you the old (cached)
+file which is on the lower branch.
+.
+.Bu
+When the file is cached as positive or existed, and a user renamed the
+file by rename(2) directly. Aufs detects the inode of the file is
+still existing. You may or may not see both of the old and new files.
+Todo: If aufs also tests the name, we can detect this case.
+.RE
+
+If your outer modification (UDBA) is rare and you can ignore the
+temporary and minor differences between virtual aufs world and real
+branch filesystem, then try this mount option.
+.\" And when you modify a branch directly, set udba=inotify temporary
+.\" before the modification and set udba=reval again after that.
+.
+.TP
+.B udba=inotify
+Aufs sets \[oq]inotify\[cq] to all the accessed directories on its branches
+and receives the event about the dir and its children. It consumes
+resources, cpu and memory. And I am afraid that the performance will be
+damaged, but it is most strict test level.
+There are some limitations of linux inotify, see also Inotify
+Limitation.
+So it is recommended to leave udba default option usually, and set it
+to inotify by remount when you need it.
+
+When a user accesses the file which was notified UDBA before, the cached data
+about the file will be discarded and aufs re-lookup it. So the data will
+be updated.
+When an error condition occurs between UDBA and aufs operation, aufs
+will return an error, including EIO.
+To use this option, you need linux\-2.6.18 and later, and need to
+enable CONFIG_INOTIFY and CONFIG_AUFS_UDBA_INOTIFY.
+
+To rename/rmdir a directory on a branch directory may reveal the same named
+directory on the lower branch. Aufs tries re-lookuping the renamed
+directory and the revealed directory and assigning different inode
+number to them. But the inode number including their children can be a
+problem. The inode numbers will be changed silently, and
+aufs may produce a warning. If you rename a directory repeatedly and
+reveal/hide the lower directory, then aufs may confuse their inode
+numbers too. It depends upon the system cache.
+
+When you make a directory in aufs and mount other filesystem on it,
+the directory in aufs cannot be removed expectedly because it is a
+mount point. But the same named directory on the writable branch can
+be removed, if someone wants. It is just an empty directory, instead
+of a mount point.
+Aufs cannot stop such direct rmdir, but produces a warning about it.
+
+
+.\" ----------------------------------------------------------------------
+.SH Linux Inotify Limitation
+Unfortunately, current inotify (linux\-2.6.18) has some limitations,
+and aufs must derive it. I am going to address some harmful cases.
+
+.SS IN_ATTRIB, updating atime
+When a file/dir on a branch is accessed directly, the inode atime (access
+time, cf. stat(2)) may or may not be updated. In some cases, inotify
+does not fire this event. So the aufs inode atime may remain old.
+
+.SS IN_ATTRIB, updating nlink
+When the link count of a file on a branch is incremented by link(2)
+directly,
+inotify fires IN_CREATE to the parent
+directory, but IN_ATTRIB to the file. So the aufs inode nlink may
+remain old.
+
+.SS IN_DELETE, removing file on NFS
+When a file on a NFS branch is deleted directly, inotify may or may
+not fire
+IN_DELETE event. It depends upon the status of dentry
+(DCACHE_NFSFS_RENAMED flag).
+In this case, the file on aufs seems still exists. Aufs and any user can see
+the file.
+
+.SS IN_IGNORED, deleted rename target
+When a file/dir on a branch is unlinked by rename(2) directly, inotify
+fires IN_IGNORED which means the inode is deleted. Actually, in some
+cases, the inode survives. For example, the rename target is linked or
+opened. In this case, inotify watch set by aufs is removed by VFS and
+inotify.
+And aufs cannot receive the events anymore. So aufs may show you
+incorrect data about the file/dir.
+
+.\" ----------------------------------------------------------------------
+.SH Policies to Select One among Multiple Writable Branches
+Aufs has some policies to select one among multiple writable branches
+when you are going to write/modify something. There are two kinds of
+policies, one is for newly create something and the other is for
+internal copy-up.
+You can select them by specifying mount option \[oq]create=CREATE_POLICY\[cq]
+or \[oq]cpup=COPYUP_POLICY.\[cq]
+These policies have no meaning when you have only one writable
+branch. If there is some meaning, it must be damaging the performance.
+
+.SS Exceptions for Policies
+In every cases below, even if the policy says that the branch where a
+new file should be created is /rw2, the file will be created on /rw1.
+.
+.Bu
+If there is a readonly branch with \[oq]wh\[cq] attribute above the
+policy-selected branch and the parent dir is marked as opaque,
+or the target (creating) file is whiteouted on the ro+wh branch, then
+the policy will be ignored and the target file will be created on the
+nearest upper writable branch than the ro+wh branch.
+.RS
+.nf
+/aufs = /rw1 + /ro+wh/diropq + /rw2
+/aufs = /rw1 + /ro+wh/wh.tgt + /rw2
+.fi
+.RE
+.
+.Bu
+If there is a writable branch above the policy-selected branch and the
+parent dir is marked as opaque or the target file is whiteouted on the
+branch, then the policy will be ignored and the target file will be
+created on the highest one among the upper writable branches who has
+diropq or whiteout. In case of whiteout, aufs removes it as usual.
+.RS
+.nf
+/aufs = /rw1/diropq + /rw2
+/aufs = /rw1/wh.tgt + /rw2
+.fi
+.RE
+.
+.Bu
+link(2) and rename(2) systemcalls are exceptions in every policy.
+They try selecting the branch where the source exists as possible since
+copyup a large file will take long time. If it can\[aq]t be, ie. the
+branch where the source exists is readonly, then they will follow the
+copyup policy.
+.
+.Bu
+There is an exception for rename(2) when the target exists.
+If the rename target exists, aufs compares the index of the branches
+where the source and the target are existing and selects the higher
+one. If the selected branch is readonly, then aufs follows the copyup
+policy.
+
+.SS Policies for Creating
+.
+.TP
+.B create=tdp | top\-down\-parent
+Selects the highest writable branch where the parent dir exists. If
+the parent dir does not exist on a writable branch, then the internal
+copyup will happen. The policy for this copyup is always \[oq]bottom-up.\[cq]
+This is the default policy.
+.
+.TP
+.B create=rr | round\-robin
+Selects a writable branch in round robin. When you have two writable
+branches and creates 10 new files, 5 files will be created for each
+branch.
+mkdir(2) systemcall is an exception. When you create 10 new directories,
+all are created on the same branch.
+.
+.TP
+.B create=mfs[:second] | most\-free\-space[:second]
+Selects a writable branch which has most free space. In order to keep
+the performance, you can specify the duration (\[oq]second\[cq]) which makes
+aufs hold the index of last selected writable branch until the
+specified seconds expires. The first time you create something in aufs
+after the specified seconds expired, aufs checks the amount of free
+space of all writable branches by internal statfs call
+and the held branch index will be updated.
+The default value is \*[AUFS_MFS_SECOND_DEF] seconds.
+
+In this mode, a FUSE branch needs special attention.
+The struct fuse_operations has a statfs operation. It is OK, but the
+parameter is struct statvfs* instead of struct statfs*. So almost
+all user\-space implementaion will call statvfs(3)/fstatvfs(3) instead of
+statfs(2)/fstatfs(2).
+In glibc, [f]statvfs(3) issues [f]statfs(2), open(2)/read(2) for
+/proc/mounts,
+and stat(2) for the mountpoint. With this situation, a FUSE branch will
+cause a deadlock in creating something in aufs. Here is a sample
+scenario,
+.\" .RS
+.\" .IN -10
+.Bu
+create a file just under the aufs root dir.
+.Bu
+aufs will aquire a write-lock for the parent directory.
+.Bu
+aufs may call statfs internally for each writable branches to decide the
+branch which has most free space.
+.Bu
+FUSE in kernel\-space converts and redirects the statfs request to the
+user\-space.
+.Bu
+the user-space statfs handler will call [f]statvfs(3).
+.Bu
+the [f]statvfs(3) in glibc will access /proc/mounts and issue
+stat(2) for the mountpoint. But those require a read-lock for the aufs
+root directory.
+.Bu
+Then a deadlock occurs.
+.\" .RE 1
+.\" .IN
+
+In order to avoid this deadlock, I would suggest not to call
+[f]statvfs(3). Here is a sample code to do this.
+.nf
+struct statvfs stvfs;
+
+main()
+{
+ [f]statvfs(..., &stvfs)
+}
+
+statfs_handler(const char *path, struct statvfs *arg)
+{
+ struct statfs stfs;
+ [f]statfs(..., &stfs);
+ memcpy(arg, &stvfs, sizeof(stvfs));
+ arg->f_bfree = stfs.f_bfree;
+ arg->f_bavail = stfs.f_bavail;
+ arg->f_ffree = stfs.f_ffree;
+ arg->f_favail = /* any value */;
+}
+.fi
+.
+.TP
+.B create=mfsrr:low[:second]
+Selects a writable branch in most-free-space mode first, and then
+round-robin mode. If the selected branch has less free space than the
+specified value \[oq]low\[cq] in bytes, then aufs re-tries in round-robin mode.
+Try an arithmetic expansion of shell which is defined by POSIX.
+For example, $((10 * 1024 * 1024)) for 10M.
+You can also specify the duration (\[oq]second\[cq]) which is equivalent to
+the \[oq]mfs\[cq] mode.
+.
+.TP
+.B create=pmfs[:second]
+Selects a writable branch where the parent dir exists, such as tdp
+mode. When the parent dir exists on multiple writable branches, aufs
+selects the one which has most free space, such as mfs mode.
+
+.SS Policies for Copy-Up
+.
+.TP
+.B cpup=tdp | top\-down\-parent
+Equivalent to the same named policy for create.
+This is the default policy.
+.
+.TP
+.B cpup=bup | bottom\-up\-parent
+Selects the writable branch where the parent dir exists and the branch
+is nearest upper one from the copyup-source.
+.
+.TP
+.B cpup=bu | bottom\-up
+Selects the nearest upper writable branch from the copyup-source,
+regardless the existence of the parent dir.
+
+.\" ----------------------------------------------------------------------
+.SH Exporting Aufs via NFS
+Aufs is supporting NFS-exporting in linux\-2.6.18 and later.
+Since aufs has no actual block device, you need to add NFS \[oq]fsid\[cq] option at
+exporting. Refer to the manual of NFS about the detail of this option.
+
+In linux\-2.6.23 and earlier,
+it is recommended to export your branch filesystems once before
+exporting aufs. By exporting once, the branch filesystem internal
+pointer named find_exported_dentry is initialized. After this
+initialization, you may unexport them.
+Additionally, this initialization should be done per the
+filesystem type. If your branches are all the same filesystem
+type, you need to export just one of them once.
+If you have never export a filesystem which is used in your
+branches, aufs will initialize the internal pointer by the default
+value, and produce a
+warning. While it will work correctly, I am afraid it will be unsafe
+in the future.
+In linux\-2.6.24 and later, this exporting is unnecessary.
+
+Additionally, there are several limitations or requirements.
+.RS
+.Bu
+The version of linux kernel must be linux\-2.6.18 or later.
+.Bu
+You need to enable CONFIG_AUFS_EXPORT.
+.Bu
+The branch filesystem must support NFS-exporting. For example, tmpfs in
+linux\-2.6.18 (or earlier) does not support it.
+.Bu
+NFSv2 is not supported. When you mount the exported aufs from your NFS
+client, you will need to some NFS options like v3 or nfsvers=v3,
+especially if it is nfsroot.
+.Bu
+If the size of the NFS file handle on your branch filesystem is large,
+aufs will
+not be able to handle it. The maximum size of NFSv3 file
+handle for a filesystem is 64 bytes. Aufs uses 24 bytes for 32bit
+system, plus 12 bytes for 64bit system. The rest is a room for a file
+handle of a branch filesystem.
+.Bu
+The External Inode Number Bitmap and Translation Table (xino) is
+required since NFS file
+handle is based upon inode number. The mount option \[oq]xino\[cq] is enabled
+by default.
+.Bu
+The branch filesystems must be accessible, which means \[oq]not hidden.\[cq]
+It means you need to \[oq]mount \-\-move\[cq] when you use initramfs and
+switch_root(8), or chroot(8).
+.\" .Bu
+.\" The \[oq]noplink\[cq] option is recommended, currently.
+.\" .Bu
+.\" If you add/del branches many times between the accesses to the same file
+.\" from the same NFS client,
+.\" and the number of the add/del operation is greater than the maximum
+.\" number of branches, then aufs may not handle the request from the NFS
+.\" client correctly.
+.RE
+
+.\" ----------------------------------------------------------------------
+.SH Dentry and Inode Caches
+If you want to clear caches on your system, there are several tricks
+for that. If your system ram is low,
+try \[oq]find /large/dir \-ls > /dev/null\[cq].
+It will read many inodes and dentries and cache them. Then old caches will be
+discarded.
+But when you have large ram or you do not have such large
+directory, it is not effective.
+
+If you want to discard cache within a certain filesystem,
+try \[oq]mount \-o remount /your/mntpnt\[cq]. Some filesystem may return an error of
+EINVAL or something, but VFS discards the unused dentry/inode caches on the
+specified filesystem.
+
+.\" ----------------------------------------------------------------------
+.SH Compatible/Incompatible with Unionfs Version 1.x Series
+If you compile aufs with \-DCONFIG_AUFS_COMPAT, dirs= option and =nfsro
+branch permission flag are available. They are interpreted as
+br: option and =ro flags respectively.
+ \[oq]debug\[cq], \[oq]delete\[cq], \[oq]imap\[cq] options are ignored silently. When you
+compile aufs without \-DCONFIG_AUFS_COMPAT, these three options are
+also ignored, but a warning message is issued.
+
+Ignoring \[oq]delete\[cq] option, and to keep filesystem consistency, aufs tries
+writing something to only one branch in a single systemcall. It means
+aufs may copyup even if the copyup-src branch is specified as writable.
+For example, you have two writable branches and a large regular file
+on the lower writable branch. When you issue rename(2) to the file on aufs,
+aufs may copyup it to the upper writable branch.
+If this behaviour is not what you want, then you should rename(2) it
+on the lower branch directly.
+
+And there is a simple shell
+script \[oq]unionctl\[cq] under sample subdirectory, which is compatible with
+unionctl(8) in
+Unionfs Version 1.x series, except \-\-query action.
+This script executes mount(8) with \[oq]remount\[cq] option and uses
+add/del/mod aufs mount options.
+If you are familiar with Unionfs Version 1.x series and want to use unionctl(8), you can
+try this script instead of using mount \-o remount,... directly.
+Aufs does not support ioctl(2) interface.
+This script is highly depending upon mount(8) in
+util\-linux\-2.12p package, and you need to mount /proc to use this script.
+If your mount(8) version differs, you can try modifying this
+script. It is very easy.
+The unionctl script is just for a sample usage of aufs remount
+interface.
+
+Aufs uses the external inode number bitmap and translation table by
+default.
+
+The default branch permission for the first branch is \[oq]rw\[cq], and the
+rest is \[oq]ro.\[cq]
+
+The whiteout is for hiding files on lower branches. Also it is applied
+to stop readdir going lower branches.
+The latter case is called \[oq]opaque directory.\[cq] Any
+whiteout is an empty file, it means whiteout is just an mark.
+In the case of hiding lower files, the name of whiteout is
+\[oq]\*[AUFS_WH_PFX]<filename>.\[cq]
+And in the case of stopping readdir, the name is
+\[oq]\*[AUFS_WH_PFX]\*[AUFS_WH_PFX].opq\[cq] or
+\[oq]\*[AUFS_WH_PFX]__dir_opaque.\[cq] The name depends upon your compile
+configuration
+CONFIG_AUFS_COMPAT.
+.\" All of newly created or renamed directory will be opaque.
+All whiteouts are hardlinked,
+including \[oq]<writable branch top dir>/\*[AUFS_WH_PFX]\*[AUFS_WH_BASENAME].\[cq]
+
+The hardlink on an ordinary (disk based) filesystem does not
+consume inode resource newly. But in linux tmpfs, the number of free
+inodes will be decremented by link(2). It is recommended to specify
+nr_inodes option to your tmpfs if you meet ENOSPC. Use this option
+after checking by \[oq]df \-i.\[cq]
+
+When you rmdir or rename-to the dir who has a number of whiteouts,
+aufs rename the dir to the temporary whiteouted-name like
+\[oq]\*[AUFS_WH_PFX]<dir>.<random hex>.\[cq] Then remove it after actual operation.
+cf. mount option \[oq]dirwh.\[cq]
+
+.\" ----------------------------------------------------------------------
+.SH Incompatible with an Ordinary Filesystem
+stat(2) returns the inode info from the first existence inode among
+the branches, except the directory link count.
+Aufs computes the directory link count larger than the exact value usually, in
+order to keep UNIX filesystem semantics, or in order to shut find(1) mouth up.
+The size of a directory may be wrong too, but it has to do no harm.
+The timestamp of a directory will not be updated when a file is
+created or removed under it, and it was done on a lower branch.
+
+The test for permission bits has two cases. One is for a directory,
+and the other is for a non-directory. In the case of a directory, aufs
+checks the permission bits of all existing directories. It means you
+need the correct privilege for the directories including the lower
+branches.
+.\" You can change this behaviour with \[oq]dirperm1\[cq] mount option.
+The test for a non-directory is more simple. It checks only the
+topmost inode.
+
+statfs(2) returns the first branch info except namelen. The namelen is
+decreased by the whiteout prefix length.
+
+Remember, seekdir(3) and telldir(3) are not defined in POSIX. They may
+not work as you expect. Try rewinddir(3) or re-open the dir.
+
+The whiteout prefix (\*[AUFS_WH_PFX]) is reserved on all branches. Users should
+not handle the filename begins with this prefix.
+In order to future whiteout, the maxmum filename length is limited by
+the longest value \- \*[AUFS_WH_PFX_LEN]. It may be a violation of POSIX.
+
+If you dislike the difference between the aufs entries in /etc/mtab
+and /proc/mounts, and if you are using mount(8) in util\-linux package,
+then try ./mount.aufs script. Copy the script to /sbin/mount.aufs.
+This simple script tries updating
+/etc/mtab. If you do not care about /etc/mtab, you can ignore this
+script.
+Remember this script is highly depending upon mount(8) in
+util\-linux\-2.12p package, and you need to mount /proc.
+
+Since aufs uses its own inode and dentry, your system may cache huge
+number of inodes and dentries. It can be as twice as all of the files
+in your union.
+It means that unmounting or remounting readonly at shutdown time may
+take a long time, since mount(2) in VFS tries freeing all of the cache
+on the target filesystem.
+.\" In this case, you had better try \[oq]echo 2 > /proc/sys/vm/drop_caches\[cq]
+.\" just before unmounting in shutdown procedure.
+.\" It frees unused inodes and dentries quickly.
+.\" If your system cache is not so large, you do not need this trick.
+
+When you open a directory, aufs will open several directories
+internally.
+It means you may reach the limit of the number of file descriptor.
+And when the lower directory cannot be opened, aufs will close all the
+opened upper directories and return an error.
+
+The sub-mount under the branch
+of local filesystem
+is ignored.
+For example, if you have mount another filesystem on
+/branch/another/mntpnt, the files under \[oq]mntpnt\[cq] will be ignored by aufs.
+It is recommended to mount the sub-mount under the mounted aufs.
+For example,
+
+.nf
+# sudo mount /dev/sdaXX /ro_branch
+# d=another/mntpnt
+# sudo mount /dev/sdbXX /ro_branch/$d
+# mkdir -p /rw_branch/$d
+# sudo mount -t aufs -o br:/rw_branch:/ro_branch none /aufs
+# sudo mount -t aufs -o br:/rw_branch/${d}:/ro_branch/${d} none /aufs/another/$d
+.fi
+
+There are several characters which are not allowed to use in a branch
+directory path and xino filename. See detail in Branch Syntax and Mount
+Option.
+
+The file-lock which means fcntl(2) with F_SETLK, F_SETLKW or F_GETLK, flock(2)
+and lockf(3), is applied to virtual aufs file only, not to the file on a
+branch. It means you can break the lock by accessing a branch directly.
+TODO: check \[oq]security\[cq] to hook locks, as inotify does.
+
+The fsync(2) and fdatasync(2) systemcalls return 0 which means success, even
+if the given file descriptor is not opened for writing.
+I am afraid this behaviour may violate some standards. Checking the
+behaviour of fsync(2) on ext2, aufs decided to return success.
+
+If you want to use disk-quota, you should set it up to your writable
+branch since aufs does not have its own block device.
+
+When your aufs is the root directory of your system, and your system
+tells you some of the filesystem were not unmounted cleanly, try these
+procedure when you shutdown your system.
+.nf
+# mount -no remount,ro /
+# for i in $writable_branches
+# do mount -no remount,ro $i
+# done
+.fi
+If your xino file is on a hard drive, you also need to specify
+\[oq]noxino\[cq] option or \[oq]xino=/your/tmpfs/xino\[cq] at remounting root
+directory.
+
+To rename(2) directory may return EXDEV even if both of src and tgt
+are on the same aufs. When the rename-src dir exists on multiple
+branches and the lower dir has child(ren), aufs has to copyup all his
+children. It can be recursive copyup. Current aufs does not support
+such huge copyup operation at one time in kernel space, instead
+produces a warning and returns EXDEV.
+Generally, mv(1) detects this error and tries mkdir(2) and
+rename(2) or copy/unlink recursively. So the result is harmless.
+If your application which issues rename(2) for a directory does not
+support EXDEV, it will not work on aufs.
+Also this specification is applied to the case when the src directroy
+exists on the lower readonly branch and it has child(ren).
+
+.\" ----------------------------------------------------------------------
+.SH EXAMPLES
+The mount options are interpreted from left to right at remount-time.
+These examples
+shows how the options are handled. (assuming /sbin/mount.aufs was
+installed)
+
+.nf
+# mount -v -t aufs br:/day0:/base none /u
+none on /u type aufs (rw,xino=/day0/.aufs.xino,br:/day0=rw:/base=ro)
+# mount -v -o remount,\\
+ prepend:/day1,\\
+ xino=/day1/xino,\\
+ mod:/day0=ro,\\
+ del:/day0 \\
+ /u
+none on /u type aufs (rw,xino=/day1/xino,br:/day1=rw:/base=ro)
+.fi
+
+.nf
+# mount -t aufs br:/rw none /u
+# mount -o remount,append:/ro /u
+different uid/gid/permission, /ro
+# mount -o remount,del:/ro /u
+# mount -o remount,nowarn_perm,append:/ro /u
+#
+(there is no warning)
+.fi
+
+.\" If you want to expand your filesystem size, aufs may help you by
+.\" adding an writable branch. Since aufs supports multiple writable
+.\" branches, the old writable branch can be being writable, if you want.
+.\" In this example, any modifications to the files under /ro branch will
+.\" be copied-up to /new, but modifications to the files under /rw branch
+.\" will not.
+.\" And the next example shows the modifications to the files under /rw branch
+.\" will be copied-up to /new/a.
+.\"
+.\" Todo: test multiple writable branches policy. cpup=nearest, cpup=exist_parent.
+.\"
+.\" .nf
+.\" # mount -v -t aufs br:/rw:/ro none /u
+.\" none on /u type aufs (rw,xino=/rw/.aufs.xino,br:/rw=rw:/ro=ro)
+.\" # mkfs /new
+.\" # mount -v -o remount,add:1:/new=rw /u
+.\" none on /u type aufs (rw,xino=/rw/.aufs.xino,br:/rw=rw:/new=rw:/ro=ro)
+.\" .fi
+.\"
+.\" .nf
+.\" # mount -v -t aufs br:/rw:/ro none /u
+.\" none on /u type aufs (rw,xino=/rw/.aufs.xino,br:/rw=rw:/ro=ro)
+.\" # mkfs /new
+.\" # mkdir /new/a new/b
+.\" # mount -v -o remount,add:1:/new/b=rw,prepend:/new/a,mod:/rw=ro /u
+.\" none on /u type aufs (rw,xino=/rw/.aufs.xino,br:/new/a=rw:/rw=ro:/new/b=rw:/ro=ro)
+.\" .fi
+
+When you use aufs as root filesystem, it is recommended to consider to
+exclude some directories. For example, /tmp and /var/log are not need
+to stack in many cases. They do not usually need to copyup or to whiteout.
+Also the swapfile on aufs (a regular file, not a block device) is not
+supported.
+
+And there is a good sample which is for network booted diskless machines. See
+sample/ in detail.
+
+.\" ----------------------------------------------------------------------
+.SH DIAGNOSTICS
+When you add an branch to your union, aufs may warn you about the
+privilege or security of the branch, which is the permission bits,
+owner and group of the top directory of the branch.
+For example, when your upper writable branch has a world writable top
+directory,
+a malicious user can create any files on the writable branch directly,
+like copyup and modify manually. I am afraid it can be a security
+issue.
+
+When you mount or remount your union without \-o ro common mount option
+and without writable branch, aufs will warn you that the first branch
+should be writable.
+
+.\" It is discouraged to set both of \[oq]udba\[cq] and \[oq]noxino\[cq] mount options. In
+.\" this case the inode number under aufs will always be changed and may
+.\" reach the end of inode number which is a maximum of unsigned long. If
+.\" the inode number reaches the end, aufs will return EIO repeatedly.
+
+When you set udba other than inotify and change something on your
+branch filesystem directly, later aufs may detect some mismatches to
+its cache. If it is a critical mismatch, aufs returns EIO and issues a
+warning saying \[oq]try udba=inotify.\[cq]
+
+When an error occurs in aufs, aufs prints the kernel message with
+\[oq]errno.\[cq] The priority of the message (log level) is ERR or WARNING which
+depends upon the message itself.
+You can convert the \[oq]errno\[cq] into the error message by perror(3),
+strerror(3) or something.
+For example, the \[oq]errno\[cq] in the message \[oq]I/O Error, write failed (\-28)\[cq]
+is 28 which means ENOSPC or \[oq]No space left on device.\[cq]
+
+.\" .SH Current Limitation
+.
+.\" ----------------------------------------------------------------------
+.\" SYNOPSIS
+.\" briefly describes the command or function\[aq]s interface. For commands, this
+.\" shows the syntax of the command and its arguments (including options); bold-
+.\" face is used for as-is text and italics are used to indicate replaceable
+.\" arguments. Brackets ([]) surround optional arguments, vertical bars (|) sep-
+.\" arate choices, and ellipses (...) can be repeated. For functions, it shows
+.\" any required data declarations or #include directives, followed by the func-
+.\" tion declaration.
+.
+.\" DESCRIPTION
+.\" gives an explanation of what the command, function, or format does. Discuss
+.\" how it interacts with files and standard input, and what it produces on
+.\" standard output or standard error. Omit internals and implementation
+.\" details unless they\[aq]re critical for understanding the interface. Describe
+.\" the usual case; for information on options use the OPTIONS section. If
+.\" there is some kind of input grammar or complex set of subcommands, consider
+.\" describing them in a separate USAGE section (and just place an overview in
+.\" the DESCRIPTION section).
+.
+.\" RETURN VALUE
+.\" gives a list of the values the library routine will return to the caller and
+.\" the conditions that cause these values to be returned.
+.
+.\" EXIT STATUS
+.\" lists the possible exit status values or a program and the conditions that
+.\" cause these values to be returned.
+.
+.\" USAGE
+.\" describes the grammar of any sublanguage this implements.
+.
+.\" FILES
+.\" lists the files the program or function uses, such as configuration files,
+.\" startup files, and files the program directly operates on. Give the full
+.\" pathname of these files, and use the installation process to modify the
+.\" directory part to match user preferences. For many programs, the default
+.\" installation location is in /usr/local, so your base manual page should use
+.\" /usr/local as the base.
+.
+.\" ENVIRONMENT
+.\" lists all environment variables that affect your program or function and how
+.\" they affect it.
+.
+.\" SECURITY
+.\" discusses security issues and implications. Warn about configurations or
+.\" environments that should be avoided, commands that may have security impli-
+.\" cations, and so on, especially if they aren\[aq]t obvious. Discussing security
+.\" in a separate section isn\[aq]t necessary; if it\[aq]s easier to understand, place
+.\" security information in the other sections (such as the DESCRIPTION or USAGE
+.\" section). However, please include security information somewhere!
+.
+.\" CONFORMING TO
+.\" describes any standards or conventions this implements.
+.
+.\" NOTES
+.\" provides miscellaneous notes.
+.
+.\" BUGS
+.\" lists limitations, known defects or inconveniences, and other questionable
+.\" activities.
+
+.SH COPYRIGHT
+Copyright \(co 2005, 2006, 2007, 2008 Junjiro Okajima
+
+.SH AUTHOR
+Junjiro Okajima
+
+.\" SEE ALSO
+.\" lists related man pages in alphabetical order, possibly followed by other
+.\" related pages or documents. Conventionally this is the last section.
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 3/67] aufs global header file
2008-05-16 14:32 ` [PATCH 2/67] aufs manual hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 4/67] aufs configuration hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs global header file
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
include/linux/aufs_type.h | 111 +++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 111 insertions(+), 0 deletions(-)
diff --git a/include/linux/aufs_type.h b/include/linux/aufs_type.h
new file mode 100644
index 0000000..1f56ec9
--- /dev/null
+++ b/include/linux/aufs_type.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2005, 2006, 2007, 2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* $Id: aufs_type.h,v 1.106 2008/05/12 00:29:11 sfjro Exp $ */
+
+#include <linux/ioctl.h>
+
+#ifndef __AUFS_TYPE_H__
+#define __AUFS_TYPE_H__
+
+#define AUFS_VERSION "20080516-mm"
+
+/* move this to linux-2.6.19/include/magic.h */
+#define AUFS_SUPER_MAGIC ('a' << 24 | 'u' << 16 | 'f' << 8 | 's')
+
+/* ---------------------------------------------------------------------- */
+
+#ifdef CONFIG_AUFS_BRANCH_MAX_127
+/* some environments treat 'char' as 'unsigned char' by default */
+typedef signed char aufs_bindex_t;
+#define AUFS_BRANCH_MAX 127
+#else
+typedef short aufs_bindex_t;
+#ifdef CONFIG_AUFS_BRANCH_MAX_511
+#define AUFS_BRANCH_MAX 511
+#elif defined(CONFIG_AUFS_BRANCH_MAX_1023)
+#define AUFS_BRANCH_MAX 1023
+#elif defined(CONFIG_AUFS_BRANCH_MAX_32767)
+#define AUFS_BRANCH_MAX 32767
+#else
+#error unknown CONFIG_AUFS_BRANCH_MAX value
+#endif
+#endif
+
+#define AUFS_NAME "aufs"
+#define AUFS_FSTYPE AUFS_NAME
+
+#define AUFS_ROOT_INO 2
+#define AUFS_FIRST_INO 11
+
+#define AUFS_WH_PFX ".wh."
+#define AUFS_WH_PFX_LEN ((int)sizeof(AUFS_WH_PFX) - 1)
+#define AUFS_XINO_FNAME "." AUFS_NAME ".xino"
+#define AUFS_XINO_DEFPATH "/tmp/" AUFS_XINO_FNAME
+#define AUFS_XINO_TRUNC_INIT 64 /* blocks */
+#define AUFS_XINO_TRUNC_STEP 4 /* blocks */
+#define AUFS_DIRWH_DEF 3
+#define AUFS_RDCACHE_DEF 10 /* seconds */
+#define AUFS_WKQ_NAME AUFS_NAME "d"
+#define AUFS_NWKQ_DEF 4
+#define AUFS_MFS_SECOND_DEF 30 /* seconds */
+#define AUFS_PLINK_WARN 100 /* number of plinks */
+
+#ifdef CONFIG_AUFS_COMPAT
+#define AUFS_DIROPQ_NAME "__dir_opaque"
+#else
+#define AUFS_DIROPQ_NAME AUFS_WH_PFX ".opq" /* whiteouted doubly */
+#endif
+#define AUFS_WH_DIROPQ AUFS_WH_PFX AUFS_DIROPQ_NAME
+
+/* will be whiteouted doubly */
+#define AUFS_WH_BASENAME AUFS_WH_PFX AUFS_NAME
+#define AUFS_WH_PLINKDIR AUFS_WH_PFX "plink"
+
+/* ---------------------------------------------------------------------- */
+
+/* ioctl */
+enum {
+ AuCtlErr,
+ AuCtlErr_Last
+};
+enum {
+ AuCtl_REFRESH, //AuCtl_REFRESHV,
+ //AuCtl_FLUSH_PLINK,
+ //AuCtl_CPUP,
+ AuCtl_CPDOWN, AuCtl_MVDOWN,
+ //AuCtl_DIROPQ
+};
+
+struct aufs_ctl_cp {
+ int bsrc, bdst;
+ int err;
+};
+
+#define AuCtlType 'A'
+#define AUFS_CTL_REFRESH _IO(AuCtlType, AuCtl_REFRESH)
+//#define AUFS_CTL_REFRESHV _IO(AuCtlType, AuCtl_REFRESHV)
+//#define AUFS_CTL_FLUSH_PLINK _IOR(AuCtlType, AuCtl_FLUSH_PLINK)
+//#define AUFS_CTL_CPUP _IOWR(AuCtlType, AuCtl_CPUP, struct aufs_ctl_cp)
+#define AUFS_CTL_CPDOWN \
+ _IOWR(AuCtlType, AuCtl_CPDOWN, struct aufs_ctl_cp)
+#define AUFS_CTL_MVDOWN \
+ _IOWR(AuCtlType, AuCtl_MVDOWN, struct aufs_ctl_cp)
+//#define AUFS_CTL_DIROPQ _IO(AuCtlType, AuCtl_DIROPQ)
+
+#endif /* __AUFS_TYPE_H__ */
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 4/67] aufs configuration
2008-05-16 14:32 ` [PATCH 3/67] aufs global header file hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 5/67] aufs Makefile hooanon05
2008-05-16 15:28 ` [PATCH 4/67] aufs configuration Jan Engelhardt
0 siblings, 2 replies; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs configuration
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/Kconfig | 198 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 198 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/Kconfig b/fs/aufs/Kconfig
new file mode 100644
index 0000000..aa99e50
--- /dev/null
+++ b/fs/aufs/Kconfig
@@ -0,0 +1,198 @@
+config AUFS
+ bool "Another unionfs (EXPERIMENTAL)"
+ depends on EXPERIMENTAL
+ help
+ Aufs is a stackable unification filesystem such as Unionfs,
+ which unifies several directories and provides a merged single
+ directory.
+ In the early days, aufs was entirely re-designed and
+ re-implemented Unionfs Version 1.x series. After many original
+ ideas, approaches and improvements, it becomes totally
+ different from Unionfs while keeping the basic features.
+ See Unionfs for the basic features.
+
+if AUFS
+config AUFS_FAKE_DM
+ bool
+ depends on AUFS
+ default n
+
+choice
+ prompt "Maximum number of branches"
+ depends on AUFS
+ default AUFS_BRANCH_MAX_127
+ help
+ Specifies the maximum number of branches (or member directories)
+ in a single aufs. The larger value consumes more system
+ resources and has an impact to performance.
+config AUFS_BRANCH_MAX_127
+ bool "127"
+ help
+ Specifies the maximum number of branches (or member directories)
+ in a single aufs. The larger value consumes more system
+ resources and has an impact to performance.
+config AUFS_BRANCH_MAX_511
+ bool "511"
+ help
+ Specifies the maximum number of branches (or member directories)
+ in a single aufs. The larger value consumes more system
+ resources and has an impact to performance.
+config AUFS_BRANCH_MAX_1023
+ bool "1023"
+ help
+ Specifies the maximum number of branches (or member directories)
+ in a single aufs. The larger value consumes more system
+ resources and has an impact to performance.
+# config AUFS_BRANCH_MAX_32767
+# bool "32767"
+# help
+# Specifies the maximum number of branches (or member directories)
+# in a single aufs. The larger value consumes more system
+# resources and has an impact to performance.
+endchoice
+
+config AUFS_STAT
+ bool "Use <sysfs>/fs/aufs/stat"
+ depends on AUFS
+ depends on SYSFS
+ default n
+ help
+ Shows some statistic data via <sysfs>/fs/aufs/stat.
+ See detail in aufs.5.
+
+config AUFS_HINOTIFY
+ bool "Use inotify to detect actions on a branch"
+ depends on AUFS
+ depends on INOTIFY
+ default n
+ help
+ If you want to modify files on branches directly, eg. bypassing aufs,
+ and want aufs to detect the changes of them fully, then enable this
+ option and use 'udba=inotify' mount option.
+ It will damage the performance.
+ See detail in aufs.5.
+
+config AUFS_EXPORT
+ bool "NFS-exportable aufs"
+ depends on AUFS
+ select EXPORTFS
+ default n
+ help
+ If you want to export your mounted aufs, then enable this
+ option. There are several requirements to export aufs.
+ See detail in aufs.5.
+
+config AUFS_ROBR
+ bool "Aufs as an readonly branch of another aufs"
+ depends on AUFS
+ default n
+ help
+ If you want make your aufs to be a part of another aufs, then
+ enable this option. In other words, you can specify your aufs
+ path in 'br:' mount option for another aufs, but cannot
+ specify 'rw' as the branch permission.
+ It will damage the performance.
+ See detail in aufs.5.
+
+config AUFS_DLGT
+ bool "Delegate the internal branch access the kernel thread"
+ depends on AUFS
+ default n
+ help
+ If you want aufs to delegate
+ the internal access to the branches which is made by aufs, to
+ the kernel thread, in order to hide the access issued by your
+ application from your LSM or something or make your
+ application to be traced strictly by the task I/O accounting,
+ then enable this option and use 'dlgt' mount option.
+ It will damage the performance.
+ See detail in aufs.5.
+
+config AUFS_SHWH
+ bool "show whiteouts"
+ depends on AUFS
+ default n
+ help
+ If you want to make the whiteouts in aufs visible, then enable
+ this option and specify 'shwh' mount option. Although it may
+ sounds like philosophy or something, but in technically it
+ simply shows the name of whiteout with keeping its behaviour.
+
+config AUFS_SPLICE_PATCH
+ bool
+ depends on AUFS
+ default y
+
+config AUFS_LHASH_PATCH
+ bool
+ depends on AUFS
+ depends on NFS_FS
+ depends on AUFS_FAKE_DM = n
+ default y
+
+config AUFS_BR_NFS
+ bool
+ depends on AUFS
+ depends on NFS_FS
+ depends on !AUFS_FAKE_DM
+ default n if (!AUFS_LHASH_PATCH || AUFS_FAKE_DM)
+ default y
+
+config AUFS_BR_XFS
+ bool
+ depends on AUFS
+ depends on XFS_FS
+ default y
+
+config AUFS_WORKAROUND_FUSE
+ bool "Special handling for FUSE-based filesystem"
+ depends on AUFS && FUSE_FS
+ default n
+ help
+ A FUSE-based filesystem may not initialize its inode
+ attributes and the FUSE developer thinks the inode attributes
+ in a positive dentry which is returned by VFS lookup operation
+ are not reliable.
+ If you use a FUSE-based filesystem as an aufs branch, and it
+ customizes the inode attribute on it without overriding
+ fuse_lowlevel_ops.lookup, probably you need to enable this
+ configuration.
+ If you enable this configuration, aufs calls getattr operation
+ in every lookup and revalidate operation for the FUSE-based
+ filesystem branch.
+ It will damage the performance even if you don't use a
+ FUSE-based filesystem branch.
+
+config AUFS_DEBUG
+ bool "Debug aufs"
+ depends on AUFS
+ default y
+ help
+ Enable this to compile aufs internal debug code.
+ The performance will be damaged.
+
+config AUFS_MAGIC_SYSRQ
+ bool
+ depends on AUFS
+ depends on AUFS_DEBUG
+ depends on MAGIC_SYSRQ
+ default y
+
+config AUFS_COMPAT
+ bool "Compatibility with Unionfs (obsolete)"
+ depends on AUFS
+ default n
+ help
+ This makes aufs compatible with unionfs-style mount options and some
+ behaviours.
+ The dirs= mount option and =nfsro branch permission flag are always
+ interpreted as br: mount option and =ro flag respectively. The
+ 'debug', 'delete' and 'imap' mount options are ignored.
+ If you disable this option, you will get,
+ - aufs issues a warning about the ignored mount options
+ - the default branch permission flag is set. RW for the first branch,
+ and RO for the rests.
+ - the name of a internal file which represents the directory is
+ 'opaque', becomes '.wh..wh..opq'
+ - the 'diropq=w' mount option is set by default
+endif
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 5/67] aufs Makefile
2008-05-16 14:32 ` [PATCH 4/67] aufs configuration hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 6/67] aufs main header file hooanon05
2008-05-16 17:25 ` [PATCH 5/67] aufs Makefile Sam Ravnborg
2008-05-16 15:28 ` [PATCH 4/67] aufs configuration Jan Engelhardt
1 sibling, 2 replies; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs Makefile
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/Makefile | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 57 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/Makefile b/fs/aufs/Makefile
new file mode 100644
index 0000000..05e0864
--- /dev/null
+++ b/fs/aufs/Makefile
@@ -0,0 +1,57 @@
+# AUFS Makefile for the Linux 2.6.25 and later
+# $Id: Makefile,v 1.3 2008/04/28 03:03:10 sfjro Exp $
+
+# the environment variables are not inherited since 2.6.23
+ifdef AUFS_EXTRA_CFLAGS
+EXTRA_CFLAGS += ${AUFS_EXTRA_CFLAGS}
+endif
+
+########################################
+
+ifdef CONFIG_AUFS_WORKAROUND_FUSE
+# defined in ${srctree}/fs/fuse/inode.c
+EXTRA_CFLAGS += -DFUSE_SUPER_MAGIC=0x65735546
+endif
+
+ifdef CONFIG_XFS_FS
+# defined in ${srctree}/fs/xfs/xfs_sb.h
+EXTRA_CFLAGS += -DXFS_SB_MAGIC=0x58465342
+endif
+
+ifdef CONFIG_TMPFS
+# defined in ${srctree}/mm/shmem.c
+EXTRA_CFLAGS += -DTMPFS_MAGIC=0x01021994
+endif
+
+-include $(dir $(lastword ${MAKEFILE_LIST}))priv.mk
+#$(warning ${EXTRA_CFLAGS})
+
+########################################
+
+obj-$(CONFIG_AUFS) += aufs.o
+aufs-y := module.o super.o sbinfo.o branch.o xino.o sysaufs.o opts.o \
+ wkq.o vfsub.o dcsub.o \
+ cpup.o whout.o plink.o wbr_policy.o \
+ dentry.o dinfo.o \
+ file.o f_op.o finfo.o \
+ dir.o vdir.o \
+ inode.o i_op.o i_op_add.o i_op_del.o i_op_ren.o iinfo.o \
+ misc.o
+
+#xattr.o
+aufs-$(CONFIG_SYSFS) += sysfs.o
+aufs-$(CONFIG_AUFS_BR_NFS) += br_nfs.o
+aufs-$(CONFIG_AUFS_BR_XFS) += br_xfs.o
+aufs-$(CONFIG_AUFS_WORKAROUND_FUSE) += br_fuse.o
+
+aufs-$(CONFIG_AUFS_DLGT) += dlgt.o
+aufs-$(CONFIG_AUFS_HINOTIFY) += hinotify.o hin_or_dlgt.o
+# dirty
+ifndef CONFIG_AUFS_HINOTIFY
+aufs-$(CONFIG_AUFS_DLGT) += hin_or_dlgt.o
+endif
+
+aufs-$(CONFIG_AUFS_EXPORT) += export.o
+aufs-$(CONFIG_AUFS_ROBR) += robr.o
+aufs-$(CONFIG_AUFS_DEBUG) += debug.o
+aufs-$(CONFIG_AUFS_MAGIC_SYSRQ) += sysrq.o
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 6/67] aufs main header file
2008-05-16 14:32 ` [PATCH 5/67] aufs Makefile hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 7/67] aufs module initialization and module-global hooanon05
2008-05-16 15:33 ` [PATCH 6/67] aufs main header file Jan Engelhardt
2008-05-16 17:25 ` [PATCH 5/67] aufs Makefile Sam Ravnborg
1 sibling, 2 replies; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs main header file
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/aufs.h | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 56 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/aufs.h b/fs/aufs/aufs.h
new file mode 100644
index 0000000..915bde0
--- /dev/null
+++ b/fs/aufs/aufs.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * main header files
+ *
+ * $Id: aufs.h,v 1.2 2008/04/21 01:31:49 sfjro Exp $
+ */
+
+#ifndef __AUFS_H__
+#define __AUFS_H__
+
+#ifdef __KERNEL__
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
+#error you got wrong version
+#endif
+
+#include "debug.h"
+
+#include "branch.h"
+#include "cpup.h"
+#include "dcsub.h"
+#include "dentry.h"
+#include "dir.h"
+#include "file.h"
+#include "hinode.h"
+#include "inode.h"
+#include "misc.h"
+#include "module.h"
+#include "opts.h"
+#include "super.h"
+#include "sysaufs.h"
+#include "vfsub.h"
+#include "whout.h"
+#include "wkq.h"
+//#include "xattr.h"
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_H__ */
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 7/67] aufs module initialization and module-global
2008-05-16 14:32 ` [PATCH 6/67] aufs main header file hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 8/67] aufs module global variables and operations hooanon05
2008-05-16 15:33 ` [PATCH 6/67] aufs main header file Jan Engelhardt
1 sibling, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs module initialization and module-global
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/module.h | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 78 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/module.h b/fs/aufs/module.h
new file mode 100644
index 0000000..cccee38
--- /dev/null
+++ b/fs/aufs/module.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2007-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * module initialization and module-global
+ *
+ * $Id: module.h,v 1.2 2008/04/21 01:33:00 sfjro Exp $
+ */
+
+#ifndef __AUFS_MODULE_H__
+#define __AUFS_MODULE_H__
+
+#ifdef __KERNEL__
+
+//#include <linux/slab.h>
+
+/* ---------------------------------------------------------------------- */
+
+/* module parameters */
+extern short aufs_nwkq;
+extern int sysaufs_brs;
+
+/* ---------------------------------------------------------------------- */
+
+extern char au_esc_chars[];
+extern int au_dir_roflags;
+
+/* kmem cache */
+enum {
+ AuCache_DINFO,
+ AuCache_ICNTNR,
+ AuCache_FINFO,
+ AuCache_VDIR,
+ AuCache_DEHSTR,
+#ifdef CONFIG_AUFS_HINOTIFY
+ AuCache_HINOTIFY,
+#endif
+ AuCache_Last
+};
+
+extern struct kmem_cache *au_cachep[];
+
+#define AuCacheArgs(type, sz) (type), (sz), 0, SLAB_RECLAIM_ACCOUNT, NULL
+#define AuCacheX(type, extra) \
+ kmem_cache_create(AuCacheArgs(#type, sizeof(struct type) + extra))
+#define AuCache(type) AuCacheX(type, 0)
+
+/* ---------------------------------------------------------------------- */
+
+#define AuCacheFuncs(name, index) \
+static inline void *au_cache_alloc_##name(void) \
+{ return kmem_cache_alloc(au_cachep[index], GFP_KERNEL); } \
+static inline void au_cache_free_##name(void *p) \
+{ kmem_cache_free(au_cachep[index], p); }
+
+AuCacheFuncs(dinfo, AuCache_DINFO);
+AuCacheFuncs(icntnr, AuCache_ICNTNR);
+AuCacheFuncs(finfo, AuCache_FINFO);
+AuCacheFuncs(vdir, AuCache_VDIR);
+AuCacheFuncs(dehstr, AuCache_DEHSTR);
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_MODULE_H__ */
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 8/67] aufs module global variables and operations
2008-05-16 14:32 ` [PATCH 7/67] aufs module initialization and module-global hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 9/67] aufs super_block operations hooanon05
2008-05-16 15:36 ` [PATCH 8/67] aufs module global variables and operations Jan Engelhardt
0 siblings, 2 replies; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs module global variables and operations
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/module.c | 243 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 243 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/module.c b/fs/aufs/module.c
new file mode 100644
index 0000000..c5577c7
--- /dev/null
+++ b/fs/aufs/module.c
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2007 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * module global variables and operations
+ *
+ * $Id: module.c,v 1.3 2008/04/28 03:02:06 sfjro Exp $
+ */
+
+#include <linux/module.h>
+#include "aufs.h"
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * aufs caches
+ */
+struct kmem_cache *au_cachep[AuCache_Last];
+static int __init create_cache(void)
+{
+ au_cachep[AuCache_DINFO] = AuCache(au_dinfo);
+ if (au_cachep[AuCache_DINFO])
+ au_cachep[AuCache_ICNTNR] = AuCache(aufs_icntnr);
+ if (au_cachep[AuCache_ICNTNR])
+ au_cachep[AuCache_FINFO] = AuCache(au_finfo);
+ //au_cachep[AuCache_FINFO] = NULL;
+ if (au_cachep[AuCache_FINFO])
+ au_cachep[AuCache_VDIR] = AuCache(au_vdir);
+ if (au_cachep[AuCache_VDIR])
+ au_cachep[AuCache_DEHSTR] = AuCache(au_vdir_dehstr);
+ if (au_cachep[AuCache_DEHSTR])
+ return 0;
+
+ return -ENOMEM;
+}
+
+static void destroy_cache(void)
+{
+ int i;
+ for (i = 0; i < AuCache_Last; i++)
+ if (au_cachep[i]) {
+ kmem_cache_destroy(au_cachep[i]);
+ au_cachep[i] = NULL;
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+
+char au_esc_chars[0x20 + 3]; /* 0x01-0x20, backslash, del, and NULL */
+int au_dir_roflags;
+
+/*
+ * functions for module interface.
+ */
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Junjiro Okajima");
+MODULE_DESCRIPTION(AUFS_NAME " -- Another unionfs");
+MODULE_VERSION(AUFS_VERSION);
+
+/* it should be 'byte', but param_set_byte() prints it by "%c" */
+short aufs_nwkq = AUFS_NWKQ_DEF;
+MODULE_PARM_DESC(nwkq, "the number of workqueue thread, " AUFS_WKQ_NAME);
+module_param_named(nwkq, aufs_nwkq, short, S_IRUGO);
+
+int sysaufs_brs;
+MODULE_PARM_DESC(brs, "use <sysfs>/fs/aufs/si_*/brN");
+module_param_named(brs, sysaufs_brs, int, S_IRUGO);
+
+/* ---------------------------------------------------------------------- */
+
+static int __init aufs_init(void)
+{
+ int err, i;
+ char *p;
+
+ au_debug_init();
+
+ p = au_esc_chars;
+ for (i = 1; i <= ' '; i++)
+ *p++ = i;
+ *p++ = '\\';
+ *p++ = '\x7f';
+ *p = 0;
+
+ au_dir_roflags = au_file_roflags(O_DIRECTORY | O_LARGEFILE);
+
+ err = -EINVAL;
+ if (unlikely(aufs_nwkq <= 0))
+ goto out;
+
+ err = sysaufs_init();
+ if (unlikely(err))
+ goto out;
+ err = au_wkq_init();
+ if (unlikely(err))
+ goto out_sysaufs;
+ err = au_inotify_init();
+ if (unlikely(err))
+ goto out_wkq;
+ err = au_sysrq_init();
+ if (unlikely(err))
+ goto out_inotify;
+
+ err = create_cache();
+ if (unlikely(err))
+ goto out_sysrq;
+
+ err = register_filesystem(&aufs_fs_type);
+ if (unlikely(err))
+ goto out_cache;
+ pr_info(AUFS_NAME " " AUFS_VERSION "\n");
+ return 0; /* success */
+
+ out_cache:
+ destroy_cache();
+ out_sysrq:
+ au_sysrq_fin();
+ out_inotify:
+ au_inotify_fin();
+ out_wkq:
+ au_wkq_fin();
+ out_sysaufs:
+ sysaufs_fin();
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+static void __exit aufs_exit(void)
+{
+ unregister_filesystem(&aufs_fs_type);
+ destroy_cache();
+
+ au_sysrq_fin();
+ au_inotify_fin();
+ au_wkq_fin();
+ sysaufs_fin();
+}
+
+module_init(aufs_init);
+module_exit(aufs_exit);
+
+/* ---------------------------------------------------------------------- */
+
+/* fake Kconfig */
+#if 1
+
+#ifdef CONFIG_AUFS_HINOTIFY
+#ifndef CONFIG_INOTIFY
+#error enable CONFIG_INOTIFY to use CONFIG_AUFS_HINOTIFY.
+#endif
+#endif /* CONFIG_AUFS_HINOTIFY */
+
+#if AUFS_BRANCH_MAX > 511 && PAGE_SIZE > 4096
+#warning pagesize is larger than 4kb, \
+ CONFIG_AUFS_BRANCH_MAX_511 or smaller is recommended.
+#endif
+
+#ifdef CONFIG_AUFS_STAT
+#ifndef CONFIG_SYSFS
+#error CONFIG_AUFS_STAT requires CONFIG_SYSFS.
+#endif
+#endif /* CONFIG_AUFS_STAT */
+
+#ifdef CONFIG_AUFS_SYSAUFS
+#warning CONFIG_AUFS_SYSAUFS is unnecessary for linux-2.6.25 and later.
+#endif
+
+#ifdef CONFIG_AUFS_EXPORT
+#if !defined(CONFIG_EXPORTFS) && !defined(CONFIG_EXPORTFS_MODULE)
+#error CONFIG_AUFS_EXPORT requires CONFIG_EXPORTFS
+#endif
+#if defined(CONFIG_EXPORTFS_MODULE) && defined(CONFIG_AUFS)
+#error need CONFIG_EXPORTFS = y to link aufs statically with CONFIG_AUFS_EXPORT
+#endif
+#endif /* CONFIG_AUFS_EXPORT */
+
+#ifdef CONFIG_AUFS_SEC_PERM_PATCH
+#ifndef CONFIG_SECURITY
+#warning CONFIG_AUFS_SEC_PERM_PATCH is unnecessary since CONFIG_SECURITY is disabled.
+#endif
+#ifdef CONFIG_AUFS
+#warning CONFIG_AUFS_SEC_PERM_PATCH is unnecessary since CONFIG_AUFS is not a module.
+#endif
+#endif
+
+#ifdef CONFIG_AUFS_PUT_FILP_PATCH
+#if !defined(CONFIG_NFS_FS) && !defined(CONFIG_NFS_FS_MODULE)
+#warning CONFIG_AUFS_PUT_FILP_PATCH is unnecessary since CONFIG_NFS_FS is disabled.
+#endif
+#ifdef CONFIG_AUFS
+#warning CONFIG_AUFS_PUT_FILP_PATCH is unnecessary since CONFIG_AUFS is not a module.
+#endif
+#ifdef CONFIG_AUFS_FAKE_DM
+#error CONFIG_AUFS_FAKE_DM must be disabled for CONFIG_AUFS_PUT_FILP_PATCH.
+#endif
+#endif /* CONFIG_AUFS_PUT_FILP_PATCH */
+
+#ifdef CONFIG_AUFS_LHASH_PATCH
+#if !defined(CONFIG_NFS_FS) && !defined(CONFIG_NFS_FS_MODULE)
+#warning CONFIG_AUFS_LHASH_PATCH is unnecessary since CONFIG_NFS_FS is disabled.
+#endif
+#ifdef CONFIG_AUFS_FAKE_DM
+#error CONFIG_AUFS_FAKE_DM must be disabled for CONFIG_AUFS_LHASH_PATCH.
+#endif
+#endif
+
+#ifdef CONFIG_AUFS_KSIZE_PATCH
+#warning CONFIG_AUFS_KSIZE_PATCH is unnecessary for linux-2.6.22 and later.
+#endif
+
+#ifdef CONFIG_AUFS_WORKAROUND_FUSE
+#if !defined(CONFIG_FUSE_FS) && !defined(CONFIG_FUSE_FS_MODULE)
+#warning CONFIG_AUFS_WORKAROUND_FUSE is enabled while FUSE is disabled.
+#endif
+#endif
+
+#ifdef CONFIG_DEBUG_PROVE_LOCKING
+#if MAX_LOCKDEP_SUBCLASSES < AuLsc_I_End
+#warning lockdep will not work since aufs uses deeper locks.
+#endif
+#endif
+
+#ifdef CONFIG_AUFS_COMPAT
+#warning CONFIG_AUFS_COMPAT will be removed in the near future.
+#endif
+
+#endif
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 9/67] aufs super_block operations
2008-05-16 14:32 ` [PATCH 8/67] aufs module global variables and operations hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 10/67] aufs mount and " hooanon05
2008-05-16 15:36 ` [PATCH 8/67] aufs module global variables and operations Jan Engelhardt
1 sibling, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs super_block operations
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/super.h | 410 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 410 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/super.h b/fs/aufs/super.h
new file mode 100644
index 0000000..f6e6d8b
--- /dev/null
+++ b/fs/aufs/super.h
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * super_block operations
+ *
+ * $Id: super.h,v 1.4 2008/05/04 23:52:41 sfjro Exp $
+ */
+
+#ifndef __AUFS_SUPER_H__
+#define __AUFS_SUPER_H__
+
+#ifdef __KERNEL__
+
+#include <linux/fs.h>
+#include <linux/cramfs_fs.h>
+#include <linux/kobject.h>
+#include <linux/magic.h>
+#include <linux/aufs_type.h>
+//#include "hinode.h"
+#include "misc.h"
+//#include "opts.h"
+#include "wkq.h"
+
+typedef ssize_t (*au_readf_t)(struct file *, char __user *, size_t, loff_t *);
+typedef ssize_t (*au_writef_t)(struct file *, const char __user *, size_t,
+ loff_t *);
+
+struct au_wbr_copyup_operations {
+ int (*copyup)(struct dentry *dentry);
+};
+
+struct au_wbr_create_operations {
+ int (*create)(struct dentry *dentry, int isdir);
+ int (*init)(struct super_block *sb);
+ int (*fin)(struct super_block *sb);
+};
+
+struct au_wbr_mfs {
+ struct mutex mfs_lock; /* protect this structure */
+ unsigned long mfs_jiffy;
+ unsigned long mfs_expire;
+ aufs_bindex_t mfs_bindex;
+
+ u64 mfsrr_bytes;
+ u64 mfsrr_watermark;
+};
+
+/* sbinfo status flags */
+/*
+ * set true when refresh_dirs() failed at remount time.
+ * then try refreshing dirs at access time again.
+ * if it is false, refreshing dirs at access time is unnecesary
+ */
+#define AuSi_FAILED_REFRESH_DIRS 1
+#define AuSi_FAILED_INIT (1 << 1)
+#define au_ftest_si(sbinfo, name) ((sbinfo)->au_si_status & AuSi_##name)
+#define au_fset_si(sbinfo, name) \
+ { (sbinfo)->au_si_status |= AuSi_##name; }
+#define au_fclr_si(sbinfo, name) \
+ { (sbinfo)->au_si_status &= ~AuSi_##name; }
+
+struct au_branch;
+struct au_sbinfo {
+ /* nowait tasks in the system-wide workqueue */
+ struct au_nowait_tasks si_nowait;
+
+ struct au_rwsem si_rwsem;
+
+ /* branch management */
+ au_gen_t si_generation;
+
+ /* see above flags */
+ unsigned char au_si_status;
+
+ aufs_bindex_t si_bend;
+ aufs_bindex_t si_last_br_id;
+ struct au_branch **si_branch;
+
+ /* policy to select a writable branch */
+ unsigned char si_wbr_copyup;
+ unsigned char si_wbr_create;
+ struct au_wbr_copyup_operations *si_wbr_copyup_ops;
+ struct au_wbr_create_operations *si_wbr_create_ops;
+
+ /* round robin */
+ atomic_t si_wbr_rr_next;
+
+ /* most free space */
+ struct au_wbr_mfs si_wbr_mfs;
+
+ /* mount flags */
+ /* include/asm-ia64/siginfo.h defines a macro named si_flags */
+ unsigned int si_mntflags;
+
+ /* external inode number (bitmap and translation table) */
+ au_readf_t si_xread;
+ au_writef_t si_xwrite;
+ struct file *si_xib;
+ struct mutex si_xib_mtx; /* protect xib members */
+ unsigned long *si_xib_buf;
+ unsigned long si_xib_last_pindex;
+ int si_xib_next_bit;
+ //unsigned long long si_xib_limit; /* Max xib file size */
+
+ /* readdir cache time, max, in HZ */
+ unsigned long si_rdcache;
+
+ /*
+ * If the number of whiteouts are larger than si_dirwh, leave all of
+ * them after au_whtmp_ren to reduce the cost of rmdir(2).
+ * future fsck.aufs or kernel thread will remove them later.
+ * Otherwise, remove all whiteouts and the dir in rmdir(2).
+ */
+ unsigned int si_dirwh;
+
+ /*
+ * rename(2) a directory with all children.
+ */
+ //int si_rendir;
+
+ /* pseudo_link list */ // dirty
+ spinlock_t si_plink_lock;
+ struct list_head si_plink;
+
+ /* dirty, for export, async ops, and sysfs */
+ spinlock_t si_mntcache_lock;
+ struct vfsmount *si_mntcache; /* no get/put */
+
+ /*
+ * sysfs and lifetime management.
+ * this is not a small structure and it may be a waste of memory in case
+ * of sysfs is disabled, particulary when many aufs-es are mounted.
+ */
+ struct kobject si_kobj;
+
+ //todo: remove this list.
+ /* super_blocks list is not exported */
+ struct list_head si_list;
+
+#ifdef CONFIG_AUFS_ROBR
+ /* locked vma list for mmap() */ // dirty
+ spinlock_t si_lvma_lock;
+ struct list_head si_lvma;
+#endif
+
+ /* dirty, necessary for unmounting, sysfs and sysrq */
+ struct super_block *si_sb;
+};
+
+/* ---------------------------------------------------------------------- */
+
+/* policy to select one among writable branches */
+#define AuWbrCopyup(sbinfo, args... ) \
+ (sbinfo)->si_wbr_copyup_ops->copyup(args)
+#define AuWbrCreate(sbinfo, args... ) \
+ (sbinfo)->si_wbr_create_ops->create(args)
+
+/* flags for si_read_lock()/aufs_read_lock()/di_read_lock() */
+#define AuLock_DW 1 /* write-lock dentry */
+#define AuLock_IR (1 << 1) /* read-lock inode */
+#define AuLock_IW (1 << 2) /* write-lock inode */
+#define AuLock_FLUSH (1 << 3) /* wait for 'nowait' tasks */
+#define AuLock_DIR (1 << 4) /* target is a dir */
+#define au_ftest_lock(flags, name) ((flags) & AuLock_##name)
+#define au_fset_lock(flags, name) { (flags) |= AuLock_##name; }
+#define au_fclr_lock(flags, name) { (flags) &= ~AuLock_##name; }
+
+/* ---------------------------------------------------------------------- */
+
+/* super.c */
+extern struct file_system_type aufs_fs_type;
+struct inode *au_iget_locked(struct super_block *sb, ino_t ino);
+
+/* sbinfo.c */
+void au_si_free(struct kobject *kobj);
+int au_si_alloc(struct super_block *sb);
+struct au_branch *au_sbr(struct super_block *sb, aufs_bindex_t bindex);
+au_gen_t au_sigen_inc(struct super_block *sb);
+int au_find_bindex(struct super_block *sb, struct au_branch *br);
+
+void aufs_read_lock(struct dentry *dentry, int flags);
+void aufs_read_unlock(struct dentry *dentry, int flags);
+void aufs_write_lock(struct dentry *dentry);
+void aufs_write_unlock(struct dentry *dentry);
+void aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int isdir);
+void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2);
+
+aufs_bindex_t au_new_br_id(struct super_block *sb);
+struct vfsmount *au_mntcache_get(struct super_block *sb);
+
+/* wbr_policy.c */
+extern struct au_wbr_copyup_operations au_wbr_copyup_ops[];
+extern struct au_wbr_create_operations au_wbr_create_ops[];
+int au_cpdown_dirs(struct dentry *dentry, aufs_bindex_t bdst,
+ struct dentry *locked);
+
+/* ---------------------------------------------------------------------- */
+
+static inline struct au_sbinfo *au_sbi(struct super_block *sb)
+{
+ return sb->s_fs_info;
+}
+
+static inline const char *au_sbtype(struct super_block *sb)
+{
+ return sb->s_type->name;
+}
+
+static inline int au_test_aufs(struct super_block *sb)
+{
+ return (sb->s_magic == AUFS_SUPER_MAGIC);
+}
+
+static inline int au_test_nfs(struct super_block *sb)
+{
+#ifdef CONFIG_AUFS_BR_NFS
+ return (sb->s_magic == NFS_SUPER_MAGIC);
+#else
+ return 0;
+#endif
+}
+
+static inline int au_test_fuse(struct super_block *sb)
+{
+#ifdef CONFIG_AUFS_WORKAROUND_FUSE
+#ifdef FUSE_SUPER_MAGIC
+ BUILD_BUG_ON(FUSE_SUPER_MAGIC != 0x65735546);
+ return (sb->s_magic == FUSE_SUPER_MAGIC);
+#else
+ return !strcmp(au_sbtype(sb), "fuse");
+#endif
+#endif
+ return 0;
+}
+
+static inline int au_test_xfs(struct super_block *sb)
+{
+#ifdef CONFIG_AUFS_BR_XFS
+#ifdef XFS_SB_MAGIC
+ BUILD_BUG_ON(XFS_SB_MAGIC != 0x58465342);
+ return (sb->s_magic == XFS_SB_MAGIC);
+#else
+ return !strcmp(au_sbtype(sb), "xfs");
+#endif
+#endif
+ return 0;
+}
+
+static inline int au_test_tmpfs(struct super_block *sb)
+{
+#ifdef CONFIG_TMPFS
+#ifdef TMPFS_MAGIC
+ BUILD_BUG_ON(TMPFS_MAGIC != 0x01021994);
+ return (sb->s_magic == TMPFS_MAGIC);
+#else
+ return !strcmp(au_sbtype(sb), "tmpfs");
+#endif
+#endif
+ return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+#ifdef CONFIG_AUFS_EXPORT
+extern struct export_operations aufs_export_op;
+static inline void au_init_export_op(struct super_block *sb)
+{
+ sb->s_export_op = &aufs_export_op;
+}
+
+static inline int au_test_nfsd(struct task_struct *tsk)
+{
+ return (!tsk->mm && !strcmp(tsk->comm, "nfsd"));
+}
+
+static inline void au_nfsd_lockdep_off(void)
+{
+ if (au_test_nfsd(current))
+ lockdep_off();
+}
+
+static inline void au_nfsd_lockdep_on(void)
+{
+ if (au_test_nfsd(current))
+ lockdep_on();
+}
+#else
+static inline int au_test_nfsd(struct task_struct *tsk)
+{
+ return 0;
+}
+
+static inline void au_init_export_op(struct super_block *sb)
+{
+ /* nothing */
+}
+
+#define au_nfsd_lockdep_off() do {} while (0)
+#define au_nfsd_lockdep_on() do {} while (0)
+#endif /* CONFIG_AUFS_EXPORT */
+
+#ifdef CONFIG_AUFS_ROBR
+static inline int au_test_nested(struct super_block *h_sb)
+{
+ return 0;
+}
+
+static inline void au_robr_lvma_init(struct au_sbinfo *sbinfo)
+{
+ spin_lock_init(&sbinfo->si_lvma_lock);
+ INIT_LIST_HEAD(&sbinfo->si_lvma);
+}
+#else
+static inline int au_test_nested(struct super_block *h_sb)
+{
+ int err = 0;
+ if (unlikely(au_test_aufs(h_sb))) {
+ err = -EINVAL;
+ AuTraceErr(err);
+ }
+ return err;
+}
+
+static inline void au_robr_lvma_init(struct au_sbinfo *sbinfo)
+{
+ /* empty */
+}
+#endif /* CONFIG_AUFS_ROBR */
+
+/* ---------------------------------------------------------------------- */
+
+/* lock superblock. mainly for entry point functions */
+/*
+ * si_noflush_read_lock, si_noflush_write_lock,
+ * si_read_unlock, si_write_unlock, si_downgrade_lock
+ */
+AuSimpleLockRwsemFuncs(si_noflush, struct super_block *sb,
+ au_sbi(sb)->si_rwsem);
+AuSimpleUnlockRwsemFuncs(si, struct super_block *sb, au_sbi(sb)->si_rwsem);
+
+static inline void si_read_lock(struct super_block *sb, int flags)
+{
+ if (au_ftest_lock(flags, FLUSH))
+ au_nwt_flush(&au_sbi(sb)->si_nowait);
+ si_noflush_read_lock(sb);
+}
+
+static inline void si_write_lock(struct super_block *sb)
+{
+ au_nwt_flush(&au_sbi(sb)->si_nowait);
+ si_noflush_write_lock(sb);
+}
+
+static inline int si_read_trylock(struct super_block *sb, int flags)
+{
+ if (au_ftest_lock(flags, FLUSH))
+ au_nwt_flush(&au_sbi(sb)->si_nowait);
+ return si_noflush_read_trylock(sb);
+}
+
+static inline int si_write_trylock(struct super_block *sb, int flags)
+{
+ if (au_ftest_lock(flags, FLUSH))
+ au_nwt_flush(&au_sbi(sb)->si_nowait);
+ return si_noflush_write_trylock(sb);
+}
+
+/* to debug easier, do not make them inlined functions */
+#define SiMustReadLock(sb) AuRwMustReadLock(&au_sbi(sb)->si_rwsem)
+#define SiMustWriteLock(sb) AuRwMustWriteLock(&au_sbi(sb)->si_rwsem)
+#define SiMustAnyLock(sb) AuRwMustAnyLock(&au_sbi(sb)->si_rwsem)
+
+/* ---------------------------------------------------------------------- */
+
+static inline aufs_bindex_t au_sbend(struct super_block *sb)
+{
+ SiMustAnyLock(sb);
+ return au_sbi(sb)->si_bend;
+}
+
+static inline unsigned int au_mntflags(struct super_block *sb)
+{
+ SiMustAnyLock(sb);
+ return au_sbi(sb)->si_mntflags;
+}
+
+static inline au_gen_t au_sigen(struct super_block *sb)
+{
+ SiMustAnyLock(sb);
+ return au_sbi(sb)->si_generation;
+}
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_SUPER_H__ */
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 10/67] aufs mount and super_block operations
2008-05-16 14:32 ` [PATCH 9/67] aufs super_block operations hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 11/67] aufs superblock private data hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs mount and super_block operations
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/super.c | 891 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 891 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/super.c b/fs/aufs/super.c
new file mode 100644
index 0000000..491f75f
--- /dev/null
+++ b/fs/aufs/super.c
@@ -0,0 +1,891 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * mount and super_block operations
+ *
+ * $Id: super.c,v 1.5 2008/05/12 00:28:35 sfjro Exp $
+ */
+
+#include <linux/module.h>
+#include <linux/buffer_head.h>
+#include <linux/seq_file.h>
+#include <linux/smp_lock.h>
+#include <linux/statfs.h>
+
+#include "aufs.h"
+
+/*
+ * super_operations
+ */
+static struct inode *aufs_alloc_inode(struct super_block *sb)
+{
+ struct aufs_icntnr *c;
+
+ AuTraceEnter();
+
+ c = au_cache_alloc_icntnr();
+ //if (LktrCond) {au_cache_free_icntnr(c); c = NULL;}
+ if (c) {
+ inode_init_once(&c->vfs_inode);
+ c->vfs_inode.i_version = 1; //sigen(sb);
+ c->iinfo.ii_hinode = NULL;
+ return &c->vfs_inode;
+ }
+ return NULL;
+}
+
+static void aufs_destroy_inode(struct inode *inode)
+{
+ LKTRTrace("i%lu\n", inode->i_ino);
+ au_iinfo_fin(inode);
+ au_cache_free_icntnr(container_of(inode, struct aufs_icntnr,
+ vfs_inode));
+}
+
+struct inode *au_iget_locked(struct super_block *sb, ino_t ino)
+{
+ struct inode *inode;
+ int err;
+#if 0
+ static struct backing_dev_info bdi = {
+ .ra_pages = 0, /* No readahead */
+ .capabilities = BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK
+ };
+#endif
+
+ LKTRTrace("i%lu\n", ino);
+
+ inode = iget_locked(sb, ino);
+ if (unlikely(!inode)) {
+ inode = ERR_PTR(-ENOMEM);
+ goto out;
+ }
+ AuDebugOn(IS_ERR(inode));
+ if (unlikely(!(inode->i_state & I_NEW)))
+ goto out;
+
+ err = au_iinfo_init(inode);
+ //if (LktrCond) err = -1;
+ if (!err) {
+ inode->i_version++;
+ inode->i_op = &aufs_iop;
+ inode->i_fop = &aufs_file_fop;
+ inode->i_mapping->a_ops = &aufs_aop;
+ //inode->i_mapping->backing_dev_info = &bdi;
+ } else {
+ iget_failed(inode);
+ inode = ERR_PTR(err);
+ }
+
+ out:
+ /* never return NULL */
+ AuDebugOn(!inode);
+ AuTraceErrPtr(inode);
+ return inode;
+}
+
+static int au_show_brs(struct seq_file *seq, struct super_block *sb)
+{
+ int err;
+ aufs_bindex_t bindex, bend;
+ struct dentry *root;
+ struct path path;
+
+ AuTraceEnter();
+
+ err = 0;
+ root = sb->s_root;
+ bend = au_sbend(sb);
+ for (bindex = 0; !err && bindex <= bend; bindex++) {
+ path.mnt = au_sbr_mnt(sb, bindex);
+ path.dentry = au_h_dptr(root, bindex);
+ err = seq_path(seq, &path, au_esc_chars);
+ if (err > 0)
+ err = seq_printf
+ (seq, "=%s",
+ au_optstr_br_perm(au_sbr_perm(sb, bindex)));
+ if (!err && bindex != bend)
+ err = seq_putc(seq, ':');
+ }
+
+ AuTraceErr(err);
+ return err;
+}
+
+static void au_show_wbr_create(struct seq_file *m, int v,
+ struct au_sbinfo *sbinfo)
+{
+ const char *pat;
+
+ AuDebugOn(v == AuWbrCreate_Def);
+
+ seq_printf(m, ",create=");
+ pat = au_optstr_wbr_create(v);
+ switch (v) {
+ case AuWbrCreate_TDP:
+ case AuWbrCreate_RR:
+ case AuWbrCreate_MFS:
+ case AuWbrCreate_PMFS:
+ seq_printf(m, pat);
+ break;
+ case AuWbrCreate_MFSV:
+ seq_printf(m, /*pat*/"mfs:%lu",
+ sbinfo->si_wbr_mfs.mfs_expire / HZ);
+ break;
+ case AuWbrCreate_PMFSV:
+ seq_printf(m, /*pat*/"pmfs:%lu",
+ sbinfo->si_wbr_mfs.mfs_expire / HZ);
+ break;
+ case AuWbrCreate_MFSRR:
+ seq_printf(m, /*pat*/"mfsrr:%Lu",
+ sbinfo->si_wbr_mfs.mfsrr_watermark);
+ break;
+ case AuWbrCreate_MFSRRV:
+ seq_printf(m, /*pat*/"mfsrr:%Lu:%lu",
+ sbinfo->si_wbr_mfs.mfsrr_watermark,
+ sbinfo->si_wbr_mfs.mfs_expire / HZ);
+ break;
+ }
+}
+
+/* seq_file will re-call me in case of too long string */
+static int aufs_show_options(struct seq_file *m, struct vfsmount *mnt)
+{
+ int err, n;
+ struct super_block *sb;
+ struct au_sbinfo *sbinfo;
+ struct dentry *root;
+ struct file *xino;
+ unsigned int mnt_flags, v;
+ struct path path;
+
+ //au_debug_on();
+ AuTraceEnter();
+
+ sb = mnt->mnt_sb;
+ root = sb->s_root;
+ if (!sysaufs_brs)
+ aufs_read_lock(root, !AuLock_IR);
+ else
+ si_noflush_read_lock(sb);
+ sbinfo = au_sbi(sb);
+ seq_printf(m, ",si=%p", sbinfo);
+ mnt_flags = au_mntflags(sb);
+ if (au_opt_test(mnt_flags, XINO)) {
+ seq_puts(m, ",xino=");
+ xino = sbinfo->si_xib;
+ path.mnt = xino->f_vfsmnt;
+ path.dentry = xino->f_dentry;
+ err = seq_path(m, &path, au_esc_chars);
+ if (unlikely(err <= 0))
+ goto out;
+ err = 0;
+#define Deleted "\\040(deleted)"
+ m->count -= sizeof(Deleted) - 1;
+ AuDebugOn(memcmp(m->buf + m->count, Deleted,
+ sizeof(Deleted) - 1));
+#undef Deleted
+ } else
+ seq_puts(m, ",noxino");
+
+#define AuBool(name, str) do { \
+ v = au_opt_test(mnt_flags, name); \
+ if (v != au_opt_test(AuOpt_Def, name)) \
+ seq_printf(m, ",%s" #str, v ? "" : "no"); \
+} while (0)
+
+#define AuStr(name, str) do { \
+ v = mnt_flags & AuOptMask_##name; \
+ if (v != (AuOpt_Def & AuOptMask_##name)) \
+ seq_printf(m, "," #str "=%s", au_optstr_##str(v)); \
+} while (0)
+
+#ifdef CONFIG_AUFS_COMPAT
+#define AuStr_BrOpt "dirs="
+#else
+#define AuStr_BrOpt "br:"
+#endif
+
+ AuBool(TRUNC_XINO, trunc_xino);
+ AuBool(DIRPERM1, dirperm1);
+ AuBool(SHWH, shwh);
+ AuBool(PLINK, plink);
+ AuStr(UDBA, udba);
+
+ v = sbinfo->si_wbr_create;
+ if (v != AuWbrCreate_Def)
+ au_show_wbr_create(m, v, sbinfo);
+
+ v = sbinfo->si_wbr_copyup;
+ if (v != AuWbrCopyup_Def)
+ seq_printf(m, ",cpup=%s", au_optstr_wbr_copyup(v));
+
+ v = au_opt_test(mnt_flags, ALWAYS_DIROPQ);
+ if (v != au_opt_test(AuOpt_Def, ALWAYS_DIROPQ))
+ seq_printf(m, ",diropq=%c", v ? 'a' : 'w');
+ AuBool(REFROF, refrof);
+ AuBool(DLGT, dlgt);
+ AuBool(WARN_PERM, warn_perm);
+ AuBool(VERBOSE, verbose);
+
+ n = sbinfo->si_dirwh;
+ if (n != AUFS_DIRWH_DEF)
+ seq_printf(m, ",dirwh=%d", n);
+ n = sbinfo->si_rdcache / HZ;
+ if (n != AUFS_RDCACHE_DEF)
+ seq_printf(m, ",rdcache=%d", n);
+
+ AuStr(COO, coo);
+
+ out:
+ if (!sysaufs_brs) {
+ seq_puts(m, "," AuStr_BrOpt);
+ au_show_brs(m, sb);
+ aufs_read_unlock(root, !AuLock_IR);
+ } else
+ si_read_unlock(sb);
+ //au_debug_off();
+ return 0;
+
+#undef AuBool
+#undef AuStr
+#undef AuStr_BrOpt
+}
+
+static int aufs_statfs(struct dentry *dentry, struct kstatfs *buf)
+{
+ int err;
+
+ AuTraceEnter();
+
+ aufs_read_lock(dentry->d_sb->s_root, 0);
+ err = vfsub_statfs(au_h_dptr(dentry->d_sb->s_root, 0), buf,
+ !!au_opt_test_dlgt(au_mntflags(dentry->d_sb)));
+ //if (LktrCond) err = -1;
+ aufs_read_unlock(dentry->d_sb->s_root, 0);
+ if (!err) {
+ buf->f_type = AUFS_SUPER_MAGIC;
+ buf->f_namelen -= AUFS_WH_PFX_LEN;
+ //todo: support uuid?
+ memset(&buf->f_fsid, 0, sizeof(buf->f_fsid));
+ }
+ /* buf->f_bsize = buf->f_blocks = buf->f_bfree = buf->f_bavail = -1; */
+
+ AuTraceErr(err);
+ return err;
+}
+
+static void au_fsync_br(struct super_block *sb)
+{
+#ifdef CONFIG_AUFS_FSYNC_SUPER_PATCH
+ aufs_bindex_t bend, bindex;
+ int brperm;
+ struct super_block *h_sb;
+
+ AuTraceEnter();
+
+ si_write_lock(sb);
+ bend = au_sbend(sb);
+ for (bindex = 0; bindex < bend; bindex++) {
+ brperm = au_sbr_perm(sb, bindex);
+ if (brperm == AuBr_RR || brperm == AuBr_RRWH)
+ continue;
+ h_sb = au_sbr_sb(sb, bindex);
+ if (bdev_read_only(h_sb->s_bdev))
+ continue;
+
+ lockdep_off();
+ down_write(&h_sb->s_umount);
+ shrink_dcache_sb(h_sb);
+ fsync_super(h_sb);
+ up_write(&h_sb->s_umount);
+ lockdep_on();
+ }
+ si_write_unlock(sb);
+#endif
+}
+
+static void aufs_umount_begin(struct vfsmount *mnt, int flags)
+{
+ struct super_block *sb = mnt->mnt_sb;
+ struct au_sbinfo *sbinfo;
+
+ AuTraceEnter();
+ //todo: dont trust BKL.
+ AuDebugOn(!kernel_locked());
+
+ sbinfo = au_sbi(sb);
+ if (unlikely(!sbinfo))
+ return;
+
+ au_fsync_br(sb);
+
+ si_write_lock(sb);
+ if (au_opt_test(au_mntflags(sb), PLINK))
+ au_plink_put(sb);
+#if 0 // remove
+ if (unlikely(au_opt_test(au_mntflags(sb), UDBA_INOTIFY)))
+ shrink_dcache_sb(sb);
+#endif
+ spin_lock(&sbinfo->si_mntcache_lock);
+ sbinfo->si_mntcache = NULL;
+ spin_unlock(&sbinfo->si_mntcache_lock);
+#if 0
+ if (sbinfo->si_wbr_create_ops->fin)
+ sbinfo->si_wbr_create_ops->fin(sb);
+#endif
+ si_write_unlock(sb);
+}
+
+/* final actions when unmounting a file system */
+static void aufs_put_super(struct super_block *sb)
+{
+ struct au_sbinfo *sbinfo;
+
+ AuTraceEnter();
+
+ sbinfo = au_sbi(sb);
+ if (unlikely(!sbinfo))
+ return;
+ kobject_put(&sbinfo->si_kobj);
+}
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * refresh dentry and inode at remount time.
+ */
+static int do_refresh(struct dentry *dentry, mode_t type,
+ unsigned int dir_flags)
+{
+ int err;
+ struct dentry *parent;
+ struct inode *inode;
+
+ LKTRTrace("%.*s, 0%o\n", AuDLNPair(dentry), type);
+ inode = dentry->d_inode;
+ AuDebugOn(!inode);
+
+ di_write_lock_child(dentry);
+ parent = dget_parent(dentry);
+ di_read_lock_parent(parent, AuLock_IR);
+ /* returns a number of positive dentries */
+ err = au_refresh_hdentry(dentry, type);
+ //err = -1;
+ if (err >= 0) {
+ err = au_refresh_hinode(inode, dentry);
+ //err = -1;
+ if (!err && type == S_IFDIR)
+ au_reset_hinotify(inode, dir_flags);
+ }
+ if (unlikely(err))
+ AuErr("unrecoverable error %d, %.*s\n", err, AuDLNPair(dentry));
+ di_read_unlock(parent, AuLock_IR);
+ dput(parent);
+ di_write_unlock(dentry);
+
+ AuTraceErr(err);
+ return err;
+}
+
+static int test_dir(struct dentry *dentry, void *arg)
+{
+ return S_ISDIR(dentry->d_inode->i_mode);
+}
+
+//todo: merge with refresh_nondir()
+static int refresh_dir(struct dentry *root, au_gen_t sgen)
+{
+ int err, i, j, ndentry, e;
+ const unsigned int flags = au_hi_flags(root->d_inode, /*isdir*/1);
+ struct au_dcsub_pages dpages;
+ struct au_dpage *dpage;
+ struct dentry **dentries;
+ struct inode *inode;
+
+ LKTRTrace("sgen %d\n", sgen);
+ SiMustWriteLock(root->d_sb);
+ //todo: dont trust BKL.
+ AuDebugOn(au_digen(root) != sgen || !kernel_locked());
+
+ err = 0;
+ list_for_each_entry(inode, &root->d_sb->s_inodes, i_sb_list)
+ if (S_ISDIR(inode->i_mode) && au_iigen(inode) != sgen) {
+ ii_write_lock_child(inode);
+ e = au_refresh_hinode_self(inode);
+ //e = -1;
+ ii_write_unlock(inode);
+ if (unlikely(e)) {
+ LKTRTrace("e %d, i%lu\n", e, inode->i_ino);
+ if (!err)
+ err = e;
+ /* go on even if err */
+ }
+ }
+
+ e = au_dpages_init(&dpages, GFP_TEMPORARY);
+ if (unlikely(e)) {
+ if (!err)
+ err = e;
+ goto out;
+ }
+ e = au_dcsub_pages(&dpages, root, test_dir, NULL);
+ if (unlikely(e)) {
+ if (!err)
+ err = e;
+ goto out_dpages;
+ }
+
+ for (i = 0; !e && i < dpages.ndpage; i++) {
+ dpage = dpages.dpages + i;
+ dentries = dpage->dentries;
+ ndentry = dpage->ndentry;
+ for (j = 0; !e && j < ndentry; j++) {
+ struct dentry *d;
+ d = dentries[j];
+#ifdef CONFIG_AUFS_DEBUG
+ {
+ struct dentry *parent;
+ parent = dget_parent(d);
+ AuDebugOn(!S_ISDIR(d->d_inode->i_mode)
+ || IS_ROOT(d)
+ || au_digen(parent) != sgen);
+ dput(parent);
+ }
+#endif
+ if (au_digen(d) != sgen) {
+ e = do_refresh(d, S_IFDIR, flags);
+ //e = -1;
+ if (unlikely(e && !err))
+ err = e;
+ /* break on err */
+ }
+ }
+ }
+
+ out_dpages:
+ au_dpages_free(&dpages);
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+static int test_nondir(struct dentry *dentry, void *arg)
+{
+ return !S_ISDIR(dentry->d_inode->i_mode);
+}
+
+static int refresh_nondir(struct dentry *root, au_gen_t sgen, int do_dentry)
+{
+ int err, i, j, ndentry, e;
+ struct au_dcsub_pages dpages;
+ struct au_dpage *dpage;
+ struct dentry **dentries;
+ struct inode *inode;
+
+ LKTRTrace("sgen %d\n", sgen);
+ SiMustWriteLock(root->d_sb);
+ //todo: dont trust BKL.
+ AuDebugOn(au_digen(root) != sgen || !kernel_locked());
+
+ err = 0;
+ list_for_each_entry(inode, &root->d_sb->s_inodes, i_sb_list)
+ if (!S_ISDIR(inode->i_mode) && au_iigen(inode) != sgen) {
+ ii_write_lock_child(inode);
+ e = au_refresh_hinode_self(inode);
+ //e = -1;
+ ii_write_unlock(inode);
+ if (unlikely(e)) {
+ LKTRTrace("e %d, i%lu\n", e, inode->i_ino);
+ if (!err)
+ err = e;
+ /* go on even if err */
+ }
+ }
+
+ if (!do_dentry)
+ goto out;
+
+ e = au_dpages_init(&dpages, GFP_TEMPORARY);
+ if (unlikely(e)) {
+ if (!err)
+ err = e;
+ goto out;
+ }
+ e = au_dcsub_pages(&dpages, root, test_nondir, NULL);
+ if (unlikely(e)) {
+ if (!err)
+ err = e;
+ goto out_dpages;
+ }
+
+ for (i = 0; i < dpages.ndpage; i++) {
+ dpage = dpages.dpages + i;
+ dentries = dpage->dentries;
+ ndentry = dpage->ndentry;
+ for (j = 0; j < ndentry; j++) {
+ struct dentry *d;
+ d = dentries[j];
+#ifdef CONFIG_AUFS_DEBUG
+ {
+ struct dentry *parent;
+ parent = dget_parent(d);
+ AuDebugOn(S_ISDIR(d->d_inode->i_mode)
+ || au_digen(parent) != sgen);
+ dput(parent);
+ }
+#endif
+ inode = d->d_inode;
+ if (inode && au_digen(d) != sgen) {
+ e = do_refresh(d, inode->i_mode & S_IFMT, 0);
+ //e = -1;
+ if (unlikely(e && !err))
+ err = e;
+ /* go on even err */
+ }
+ }
+ }
+
+ out_dpages:
+ au_dpages_free(&dpages);
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+/* stop extra interpretation of errno in mount(8), and strange error messages */
+static int cvt_err(int err)
+{
+ AuTraceErr(err);
+
+ switch (err) {
+ case -ENOENT:
+ case -ENOTDIR:
+ case -EEXIST:
+ case -EIO:
+ err = -EINVAL;
+ }
+ return err;
+}
+
+/* protected by s_umount */
+static int aufs_remount_fs(struct super_block *sb, int *flags, char *data)
+{
+ int err;
+ struct dentry *root;
+ struct inode *inode;
+ struct au_opts opts;
+ unsigned int dlgt;
+ struct au_sbinfo *sbinfo;
+
+ //au_debug_on();
+ LKTRTrace("flags 0x%x, data %s, len %lu\n",
+ *flags, data ? data : "NULL",
+ (unsigned long)(data ? strlen(data) : 0));
+
+ au_fsync_br(sb);
+ err = 0;
+ if (!data || !*data)
+ goto out; /* success */
+
+ err = -ENOMEM;
+ memset(&opts, 0, sizeof(opts));
+ opts.opt = (void *)__get_free_page(GFP_TEMPORARY);
+ //if (LktrCond) {free_page((unsigned long)opts.opt); opts.opt = NULL;}
+ if (unlikely(!opts.opt))
+ goto out;
+ opts.max_opt = PAGE_SIZE / sizeof(*opts.opt);
+ opts.flags = AuOpts_REMOUNT;
+
+ /* parse it before aufs lock */
+ err = au_opts_parse(sb, *flags, data, &opts);
+ //if (LktrCond) {au_free_opts(&opts); err = -1;}
+ if (unlikely(err))
+ goto out_opts;
+
+ sbinfo = au_sbi(sb);
+ root = sb->s_root;
+ inode = root->d_inode;
+ mutex_lock(&inode->i_mutex);
+ aufs_write_lock(root);
+
+ //DbgSleep(3);
+
+ /* au_do_opts() may return an error */
+ err = au_opts_remount(sb, &opts);
+ //if (LktrCond) err = -1;
+ au_opts_free(&opts);
+
+ if (au_ftest_opts(opts.flags, REFRESH_DIR)
+ || au_ftest_opts(opts.flags, REFRESH_NONDIR)) {
+ int rerr;
+ au_gen_t sigen;
+
+ dlgt = !!au_opt_test_dlgt(sbinfo->si_mntflags);
+ au_opt_clr(sbinfo->si_mntflags, DLGT);
+ au_sigen_inc(sb);
+ au_reset_hinotify(inode, au_hi_flags(inode, /*isdir*/1));
+ sigen = au_sigen(sb);
+ au_fclr_si(sbinfo, FAILED_REFRESH_DIRS);
+
+ DiMustNoWaiters(root);
+ IiMustNoWaiters(root->d_inode);
+ di_write_unlock(root);
+
+ rerr = refresh_dir(root, sigen);
+ if (unlikely(rerr)) {
+ au_fset_si(sbinfo, FAILED_REFRESH_DIRS);
+ AuWarn("Refreshing directories failed, ignores (%d)\n",
+ rerr);
+ }
+
+ if (unlikely(au_ftest_opts(opts.flags, REFRESH_NONDIR))) {
+ //au_debug_on();
+ rerr = refresh_nondir(root, sigen, !rerr);
+ if (unlikely(rerr))
+ AuWarn("Refreshing non-directories failed,"
+ " ignores (%d)\n", rerr);
+ //au_debug_off();
+ }
+
+ /* aufs_write_lock() calls ..._child() */
+ di_write_lock_child(root);
+
+ au_cpup_attr_all(inode);
+ if (unlikely(dlgt))
+ au_opt_set(sbinfo->si_mntflags, DLGT);
+ }
+
+ aufs_write_unlock(root);
+ mutex_unlock(&inode->i_mutex);
+
+ out_opts:
+ free_page((unsigned long)opts.opt);
+ out:
+ err = cvt_err(err);
+ AuTraceErr(err);
+ //au_debug_off();
+ return err;
+}
+
+static struct super_operations aufs_sop = {
+ .alloc_inode = aufs_alloc_inode,
+ .destroy_inode = aufs_destroy_inode,
+ //.dirty_inode = aufs_dirty_inode,
+ //.write_inode = aufs_write_inode,
+ //void (*put_inode) (struct inode *);
+ .drop_inode = generic_delete_inode,
+ //.delete_inode = aufs_delete_inode,
+ //.clear_inode = aufs_clear_inode,
+
+ .show_options = aufs_show_options,
+ .statfs = aufs_statfs,
+
+ .put_super = aufs_put_super,
+ //void (*write_super) (struct super_block *);
+ //int (*sync_fs)(struct super_block *sb, int wait);
+ //void (*write_super_lockfs) (struct super_block *);
+ //void (*unlockfs) (struct super_block *);
+ .remount_fs = aufs_remount_fs,
+ /* depends upon umount flags. also use put_super() (< 2.6.18) */
+ .umount_begin = aufs_umount_begin
+};
+
+/* ---------------------------------------------------------------------- */
+
+static int alloc_root(struct super_block *sb)
+{
+ int err;
+ struct inode *inode;
+ struct dentry *root;
+
+ AuTraceEnter();
+
+ err = -ENOMEM;
+ inode = au_iget_locked(sb, AUFS_ROOT_INO);
+ //if (LktrCond) {iput(inode); inode = NULL;}
+ err = PTR_ERR(inode);
+ if (IS_ERR(inode))
+ goto out;
+ unlock_new_inode(inode);
+ inode->i_mode = S_IFDIR;
+ root = d_alloc_root(inode);
+ //if (LktrCond) {igrab(inode); dput(root); root = NULL;}
+ if (unlikely(!root))
+ goto out_iput;
+ err = PTR_ERR(root);
+ if (IS_ERR(root))
+ goto out_iput;
+
+ err = au_alloc_dinfo(root);
+ //if (LktrCond){au_rw_write_unlock(&au_di(root)->di_rwsem);err=-1;}
+ if (!err) {
+ sb->s_root = root;
+ return 0; /* success */
+ }
+ dput(root);
+ goto out; /* do not iput */
+
+ out_iput:
+ iget_failed(inode);
+ iput(inode);
+ out:
+ AuTraceErr(err);
+ return err;
+
+}
+
+static int aufs_fill_super(struct super_block *sb, void *raw_data, int silent)
+{
+ int err;
+ struct dentry *root;
+ struct inode *inode;
+ struct au_opts opts;
+ char *arg = raw_data;
+
+ //au_debug_on();
+ if (unlikely(!arg || !*arg)) {
+ err = -EINVAL;
+ AuErr("no arg\n");
+ goto out;
+ }
+ LKTRTrace("%s, silent %d\n", arg, silent);
+
+ err = -ENOMEM;
+ memset(&opts, 0, sizeof(opts));
+ opts.opt = (void *)__get_free_page(GFP_TEMPORARY);
+ //if (LktrCond) {free_page((unsigned long)opts.opt); opts.opt = NULL;}
+ if (unlikely(!opts.opt))
+ goto out;
+ opts.max_opt = PAGE_SIZE / sizeof(*opts.opt);
+
+ err = au_si_alloc(sb);
+ //if (LktrCond) {si_write_unlock(sb);free_sbinfo(sb);err=-1;}
+ if (unlikely(err))
+ goto out_opts;
+ SiMustWriteLock(sb);
+ /* all timestamps always follow the ones on the branch */
+ sb->s_flags |= MS_NOATIME | MS_NODIRATIME;
+ sb->s_op = &aufs_sop;
+ sb->s_magic = AUFS_SUPER_MAGIC;
+ au_init_export_op(sb);
+
+ err = alloc_root(sb);
+ //if (LktrCond) {au_rw_write_unlock(&au_di(sb->s_root)->di_rwsem);
+ //dput(sb->s_root);sb->s_root=NULL;err=-1;}
+ if (unlikely(err)) {
+ AuDebugOn(sb->s_root);
+ si_write_unlock(sb);
+ goto out_info;
+ }
+ root = sb->s_root;
+ DiMustWriteLock(root);
+ inode = root->d_inode;
+ inode->i_nlink = 2;
+
+ /*
+ * actually we can parse options regardless aufs lock here.
+ * but at remount time, parsing must be done before aufs lock.
+ * so we follow the same rule.
+ */
+ ii_write_lock_parent(inode);
+ aufs_write_unlock(root);
+ err = au_opts_parse(sb, sb->s_flags, arg, &opts);
+ //if (LktrCond) {au_opts_free(&opts); err = -1;}
+ if (unlikely(err))
+ goto out_root;
+
+ /* lock vfs_inode first, then aufs. */
+ mutex_lock(&inode->i_mutex);
+ inode->i_op = &aufs_dir_iop;
+ inode->i_fop = &aufs_dir_fop;
+ aufs_write_lock(root);
+
+ sb->s_maxbytes = 0;
+ err = au_opts_mount(sb, &opts);
+ //if (LktrCond) err = -1;
+ au_opts_free(&opts);
+ if (unlikely(err))
+ goto out_unlock;
+ AuDebugOn(!sb->s_maxbytes);
+
+ //AuDbgDentry(root);
+ aufs_write_unlock(root);
+ mutex_unlock(&inode->i_mutex);
+ //AuDbgSb(sb);
+ goto out_opts; /* success */
+
+ out_unlock:
+ aufs_write_unlock(root);
+ mutex_unlock(&inode->i_mutex);
+ out_root:
+ dput(root);
+ sb->s_root = NULL;
+ out_info:
+ au_fset_si(au_sbi(sb), FAILED_INIT);
+ kobject_put(&au_sbi(sb)->si_kobj);
+ sb->s_fs_info = NULL;
+ out_opts:
+ free_page((unsigned long)opts.opt);
+ out:
+ AuTraceErr(err);
+ err = cvt_err(err);
+ AuTraceErr(err);
+ //au_debug_off();
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int aufs_get_sb(struct file_system_type *fs_type, int flags,
+ const char *dev_name, void *raw_data,
+ struct vfsmount *mnt)
+{
+ int err;
+
+ /* all timestamps always follow the ones on the branch */
+ /* mnt->mnt_flags |= MNT_NOATIME | MNT_NODIRATIME; */
+ err = get_sb_nodev(fs_type, flags, raw_data, aufs_fill_super, mnt);
+ if (!err) {
+ struct super_block *sb = mnt->mnt_sb;
+ struct au_sbinfo *sbinfo = au_sbi(sb);
+
+ /* no get/put */
+ spin_lock_init(&sbinfo->si_mntcache_lock);
+ sbinfo->si_mntcache = mnt;
+
+ au_sbilist_lock();
+ au_sbilist_add(sbinfo);
+ si_write_lock(sb);
+ sysaufs_brs_add(sb, 0);
+ si_write_unlock(sb);
+ au_sbilist_unlock();
+ }
+ return err;
+}
+
+struct file_system_type aufs_fs_type = {
+ .name = AUFS_FSTYPE,
+ .fs_flags = FS_REVAL_DOT, /* for UDBA and NFS branch */
+ .get_sb = aufs_get_sb,
+ .kill_sb = generic_shutdown_super,
+ //.kill_sb = kill_anon_super,
+ /* no need to __module_get() and module_put(). */
+ .owner = THIS_MODULE,
+};
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 11/67] aufs superblock private data
2008-05-16 14:32 ` [PATCH 10/67] aufs mount and " hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 12/67] aufs branch filesystems and xino for them hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs superblock private data
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/sbinfo.c | 268 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 268 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/sbinfo.c b/fs/aufs/sbinfo.c
new file mode 100644
index 0000000..a412811
--- /dev/null
+++ b/fs/aufs/sbinfo.c
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * superblock private data
+ *
+ * $Id: sbinfo.c,v 1.4 2008/05/12 00:28:22 sfjro Exp $
+ */
+
+#include <linux/mnt_namespace.h>
+#include <linux/smp_lock.h>
+#include "aufs.h"
+
+/*
+ * they are necessary regardless sysfs is disabled.
+ */
+void au_si_free(struct kobject *kobj)
+{
+ struct au_sbinfo *sbinfo;
+ struct super_block *sb;
+
+ LKTRTrace("kobj %p\n", kobj);
+ sbinfo = container_of(kobj, struct au_sbinfo, si_kobj);
+ LKTRTrace("sbinfo %p\n", sbinfo);
+ AuDebugOn(!list_empty(&sbinfo->si_plink));
+
+ sb = sbinfo->si_sb;
+ if (unlikely(!au_ftest_si(sbinfo, FAILED_INIT))) {
+ au_sbilist_lock();
+ au_sbilist_del(sbinfo);
+ au_sbilist_unlock();
+ }
+
+ si_write_lock(sb);
+ au_xino_clr(sb);
+ au_br_free(sbinfo);
+ kfree(sbinfo->si_branch);
+ si_write_unlock(sb);
+
+ //AuDbg("free\n");
+ kfree(sbinfo);
+}
+
+int au_si_alloc(struct super_block *sb)
+{
+ int err;
+ struct au_sbinfo *sbinfo;
+
+ AuTraceEnter();
+
+ err = -ENOMEM;
+ sbinfo = kmalloc(sizeof(*sbinfo), GFP_KERNEL);
+ //if (LktrCond) {kfree(sbinfo); sbinfo = NULL;}
+ if (unlikely(!sbinfo))
+ goto out;
+ sbinfo->si_branch = kzalloc(sizeof(*sbinfo->si_branch), GFP_KERNEL);
+ //if (LktrCond) {kfree(sbinfo->si_branch); sbinfo->si_branch = NULL;}
+ if (unlikely(!sbinfo->si_branch))
+ goto out_sbinfo;
+
+ memset(&sbinfo->si_kobj, 0, sizeof(sbinfo->si_kobj));
+ err = sysaufs_si_init(sbinfo);
+ if (unlikely(err))
+ goto out_br;
+
+ au_rw_init_wlock(&sbinfo->si_rwsem);
+ sbinfo->si_generation = 0;
+ //sbinfo->si_generation = INT_MAX - 2;
+ sbinfo->au_si_status = 0;
+ sbinfo->si_bend = -1;
+ sbinfo->si_last_br_id = 0;
+
+ sbinfo->si_wbr_copyup = AuWbrCopyup_Def;
+ sbinfo->si_wbr_create = AuWbrCreate_Def;
+ sbinfo->si_wbr_copyup_ops = au_wbr_copyup_ops + AuWbrCopyup_Def;
+ sbinfo->si_wbr_create_ops = au_wbr_create_ops + AuWbrCreate_Def;
+
+ sbinfo->si_mntflags = AuOpt_Def;
+
+ sbinfo->si_xread = NULL;
+ sbinfo->si_xwrite = NULL;
+ sbinfo->si_xib = NULL;
+ mutex_init(&sbinfo->si_xib_mtx);
+ sbinfo->si_xib_buf = NULL;
+ /* leave si_xib_last_pindex and si_xib_next_bit */
+
+ au_nwt_init(&sbinfo->si_nowait);
+
+ sbinfo->si_rdcache = AUFS_RDCACHE_DEF * HZ;
+ sbinfo->si_dirwh = AUFS_DIRWH_DEF;
+
+ spin_lock_init(&sbinfo->si_plink_lock);
+ INIT_LIST_HEAD(&sbinfo->si_plink);
+
+ au_robr_lvma_init(sbinfo);
+
+ /* leave other members for sysaufs and si_mnt. */
+ sbinfo->si_sb = sb;
+
+ sb->s_fs_info = sbinfo;
+ //sysaufs_si_get(sb);
+
+ au_debug_sbinfo_init(sbinfo);
+ return 0; /* success */
+
+ out_br:
+ kfree(sbinfo->si_branch);
+ out_sbinfo:
+ kfree(sbinfo);
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+struct au_branch *au_sbr(struct super_block *sb, aufs_bindex_t bindex)
+{
+ struct au_branch *br;
+
+ SiMustAnyLock(sb);
+ AuDebugOn(bindex < 0 || au_sbend(sb) < bindex);
+ br = au_sbi(sb)->si_branch[0 + bindex];
+ AuDebugOn(!br);
+ return br;
+}
+
+au_gen_t au_sigen_inc(struct super_block *sb)
+{
+ au_gen_t gen;
+
+ SiMustWriteLock(sb);
+ gen = ++au_sbi(sb)->si_generation;
+ au_update_digen(sb->s_root);
+ au_update_iigen(sb->s_root->d_inode);
+ sb->s_root->d_inode->i_version++;
+ return gen;
+}
+
+int au_find_bindex(struct super_block *sb, struct au_branch *br)
+{
+ aufs_bindex_t bindex, bend;
+
+ bend = au_sbend(sb);
+ for (bindex = 0; bindex <= bend; bindex++)
+ if (au_sbr(sb, bindex) == br)
+ return bindex;
+ return -1;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/* dentry and super_block lock. call at entry point */
+void aufs_read_lock(struct dentry *dentry, int flags)
+{
+ si_read_lock(dentry->d_sb, flags);
+ if (au_ftest_lock(flags, DW))
+ di_write_lock_child(dentry);
+ else
+ di_read_lock_child(dentry, flags);
+}
+
+void aufs_read_unlock(struct dentry *dentry, int flags)
+{
+ if (au_ftest_lock(flags, DW))
+ di_write_unlock(dentry);
+ else
+ di_read_unlock(dentry, flags);
+ si_read_unlock(dentry->d_sb);
+}
+
+void aufs_write_lock(struct dentry *dentry)
+{
+ si_write_lock(dentry->d_sb);
+ di_write_lock_child(dentry);
+}
+
+void aufs_write_unlock(struct dentry *dentry)
+{
+ di_write_unlock(dentry);
+ si_write_unlock(dentry->d_sb);
+}
+
+void aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int flags)
+{
+ AuDebugOn(d1 == d2 || d1->d_sb != d2->d_sb);
+ si_read_lock(d1->d_sb, flags);
+ di_write_lock2_child(d1, d2, au_ftest_lock(flags, DIR));
+}
+
+void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2)
+{
+ AuDebugOn(d1 == d2 || d1->d_sb != d2->d_sb);
+ di_write_unlock2(d1, d2);
+ si_read_unlock(d1->d_sb);
+}
+
+/* ---------------------------------------------------------------------- */
+
+aufs_bindex_t au_new_br_id(struct super_block *sb)
+{
+ aufs_bindex_t br_id;
+ struct au_sbinfo *sbinfo;
+
+ AuTraceEnter();
+ SiMustWriteLock(sb);
+
+ sbinfo = au_sbi(sb);
+ while (1) {
+ br_id = ++sbinfo->si_last_br_id;
+ if (br_id && au_br_index(sb, br_id) < 0)
+ return br_id;
+ }
+}
+
+//todo: dirty
+/*
+ * when you mntput() for the return value of this function,
+ * you have to store it to your local var.
+ * ie. never mntput si_mntcache directly.
+ */
+struct vfsmount *au_mntcache_get(struct super_block *sb)
+{
+ struct au_sbinfo *sbinfo;
+ struct mnt_namespace *ns;
+ struct vfsmount *pos, *mnt;
+
+ AuTraceEnter();
+
+ sbinfo = au_sbi(sb);
+
+ spin_lock(&sbinfo->si_mntcache_lock);
+ if (sbinfo->si_mntcache)
+ goto out;
+
+ /* vfsmount_lock is not exported */
+ //root_mnt = current->fs->root.mnt;
+ /* no get/put */
+ AuDebugOn(!current->nsproxy);
+ ns = current->nsproxy->mnt_ns;
+ AuDebugOn(!ns);
+ list_for_each_entry(pos, &ns->list, mnt_list)
+ if (pos->mnt_sb == sb) {
+ sbinfo->si_mntcache = pos;
+ break;
+ }
+ AuDebugOn(!sbinfo->si_mntcache);
+
+ out:
+ mnt = mntget(sbinfo->si_mntcache);
+ spin_unlock(&sbinfo->si_mntcache_lock);
+ return mnt;
+}
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 12/67] aufs branch filesystems and xino for them
2008-05-16 14:32 ` [PATCH 11/67] aufs superblock private data hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 13/67] aufs branch management hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs branch filesystems and xino for them
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/branch.h | 345 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 345 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/branch.h b/fs/aufs/branch.h
new file mode 100644
index 0000000..a9146eb
--- /dev/null
+++ b/fs/aufs/branch.h
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * branch filesystems and xino for them
+ *
+ * $Id: branch.h,v 1.3 2008/04/28 03:03:32 sfjro Exp $
+ */
+
+#ifndef __AUFS_BRANCH_H__
+#define __AUFS_BRANCH_H__
+
+#ifdef __KERNEL__
+
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/sysfs.h>
+#include <linux/aufs_type.h>
+#include "misc.h"
+#include "super.h"
+
+/* ---------------------------------------------------------------------- */
+
+/* an entry in a xino file */
+struct au_xino_entry {
+ ino_t ino;
+ //__u32 h_gen;
+} __packed;
+
+//#define AuXino_INVALID_HGEN (-1)
+
+/* a xino file */
+struct au_xino_file {
+ //struct file **xi_file;
+ struct file *xi_file;
+
+ /* array management */
+ //unsigned long long xi_limit; /* Max xino file size */
+ //unsigned long long xi_size; /* s_maxbytes */
+
+ /* truncation */
+ blkcnt_t xi_upper; /* watermark in blocks */
+ //u64 xi_upper; /* watermark in bytes */
+ //u64 xi_step; /* to next watermark in bytes */
+ atomic_t xi_running;
+};
+
+/* protected by superblock rwsem */
+struct au_branch {
+ struct file *br_xino;
+ //struct au_xino_file *br_xino;
+
+ aufs_bindex_t br_id;
+
+ int br_perm;
+ struct vfsmount *br_mnt;
+ atomic_t br_count;
+
+ /* whiteout base */
+ struct au_rwsem br_wh_rwsem;
+ struct dentry *br_wh;
+ atomic_t br_wh_running;
+
+ /* pseudo-link dir */
+ struct dentry *br_plink;
+
+#if 1
+ /* xino truncation */
+ blkcnt_t br_xino_upper; /* watermark in blocks */
+ atomic_t br_xino_running;
+#endif
+
+ /* mfs mode */
+ u64 br_bytes;
+
+#ifdef CONFIG_SYSFS
+ /* an entry under sysfs per mount-point */
+ char br_name[8];
+ struct attribute br_attr;
+#endif
+
+ au_gen_t br_generation;
+};
+
+/* ---------------------------------------------------------------------- */
+
+/* branch permission and attribute */
+enum {
+ AuBr_RW, /* writable, linkable wh */
+ AuBr_RO, /* readonly, no wh */
+ AuBr_RR, /* natively readonly, no wh */
+
+ AuBr_RWNoLinkWH, /* un-linkable whiteouts */
+
+ AuBr_ROWH,
+ AuBr_RRWH, /* whiteout-able */
+
+ AuBr_Last
+};
+
+static inline int au_br_writable(int brperm)
+{
+ return (brperm == AuBr_RW || brperm == AuBr_RWNoLinkWH);
+}
+
+static inline int au_br_whable(int brperm)
+{
+ return (brperm == AuBr_RW
+ || brperm == AuBr_ROWH
+ || brperm == AuBr_RRWH);
+}
+
+#if 0 // rfu
+static inline int au_br_linkable_wh(int brperm)
+{
+ return (brperm == AuBr_RW);
+}
+#endif
+
+static inline int au_br_hinotifyable(int brperm)
+{
+#ifdef CONFIG_AUFS_HINOTIFY
+ return (brperm != AuBr_RR && brperm != AuBr_RRWH);
+#else
+ return 0;
+#endif
+}
+
+/* ---------------------------------------------------------------------- */
+
+/* branch.c */
+struct au_sbinfo;
+void au_br_free(struct au_sbinfo *sinfo);
+int au_test_def_rr(struct super_block *h_sb);
+int au_br_index(struct super_block *sb, aufs_bindex_t br_id);
+struct au_opt_add;
+int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount);
+struct au_opt_del;
+int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount);
+struct au_opt_mod;
+int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount,
+ int *do_update);
+
+/* xino.c */
+int au_xib_trunc(struct super_block *sb);
+
+struct file *au_xino_create(struct super_block *sb, char *fname, int silent,
+ struct dentry *parent);
+ino_t au_xino_new_ino(struct super_block *sb);
+int au_xino_write0(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
+ ino_t ino);
+int au_xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
+ struct au_xino_entry *xinoe);
+int au_xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
+ struct au_xino_entry *xinoe);
+int au_xino_br(struct super_block *sb, struct au_branch *br, ino_t hino,
+ struct file *base_file, int do_test);
+int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex);
+
+struct au_opt_xino;
+int au_xino_set(struct super_block *sb, struct au_opt_xino *xino, int remount);
+void au_xino_clr(struct super_block *sb);
+struct file *au_xino_def(struct super_block *sb);
+
+/* ---------------------------------------------------------------------- */
+
+//todo: memory barrier?
+static inline int au_br_count(struct au_branch *br)
+{
+ return atomic_read(&br->br_count);
+}
+
+static inline int au_br_get(struct au_branch *br)
+{
+ return atomic_inc_return(&br->br_count);
+}
+
+static inline int au_br_put(struct au_branch *br)
+{
+ return atomic_dec_return(&br->br_count);
+}
+
+static inline au_gen_t au_br_gen(struct au_branch *br)
+{
+ return br->br_generation;
+}
+
+/*
+ * test if the @br is readonly or not.
+ */
+static inline int au_br_rdonly(struct au_branch *br)
+{
+ return ((br->br_mnt->mnt_sb->s_flags & MS_RDONLY)
+ || !au_br_writable(br->br_perm))
+ ? -EROFS : 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/* Superblock to branch */
+static inline
+aufs_bindex_t au_sbr_id(struct super_block *sb, aufs_bindex_t bindex)
+{
+ return au_sbr(sb, bindex)->br_id;
+}
+
+static inline
+struct vfsmount *au_sbr_mnt(struct super_block *sb, aufs_bindex_t bindex)
+{
+ return au_sbr(sb, bindex)->br_mnt;
+}
+
+static inline
+struct super_block *au_sbr_sb(struct super_block *sb, aufs_bindex_t bindex)
+{
+ return au_sbr_mnt(sb, bindex)->mnt_sb;
+}
+
+#if 0 // rfu
+static inline int au_sbr_count(struct super_block *sb, aufs_bindex_t bindex)
+{
+ return au_br_count(au_sbr(sb, bindex));
+}
+
+static inline void au_sbr_get(struct super_block *sb, aufs_bindex_t bindex)
+{
+ au_br_get(au_sbr(sb, bindex));
+}
+#endif
+
+static inline void au_sbr_put(struct super_block *sb, aufs_bindex_t bindex)
+{
+ au_br_put(au_sbr(sb, bindex));
+}
+
+static inline int au_sbr_perm(struct super_block *sb, aufs_bindex_t bindex)
+{
+ return au_sbr(sb, bindex)->br_perm;
+}
+
+static inline int au_sbr_whable(struct super_block *sb, aufs_bindex_t bindex)
+{
+ return au_br_whable(au_sbr_perm(sb, bindex));
+}
+
+static inline int au_test_trunc_xino(struct super_block *sb)
+{
+ return au_test_tmpfs(sb);
+}
+
+/* temporary support for i#1 in cramfs */
+static inline int au_test_unique_ino(struct dentry *h_dentry, ino_t h_ino)
+{
+#if defined(CONFIG_CRAMFS) || defined(CONFIG_CRAMFS_MODULE)
+ if (unlikely(h_dentry->d_sb->s_magic == CRAMFS_MAGIC))
+ return (h_ino != 1);
+#endif
+ return 1;
+}
+
+#ifdef CONFIG_AUFS_BR_NFS
+static inline int au_test_unsupported_nfs(struct super_block *h_sb)
+{
+ return 0;
+}
+
+static inline struct vfsmount *au_do_nfsmnt(struct vfsmount *h_mnt)
+{
+ if (!au_test_nfs(h_mnt->mnt_sb))
+ return NULL;
+ return h_mnt;
+}
+
+/* it doesn't mntget() */
+static inline
+struct vfsmount *au_nfsmnt(struct super_block *sb, aufs_bindex_t bindex)
+{
+ return au_do_nfsmnt(au_sbr_mnt(sb, bindex));
+}
+
+#define AuNoNfsBranchMsg "dummy"
+
+#else
+static inline int au_test_unsupported_nfs(struct super_block *h_sb)
+{
+ return au_test_nfs(h_sb);
+}
+
+static inline struct vfsmount *au_do_nfsmnt(struct vfsmount *h_mnt)
+{
+ return NULL;
+}
+
+static inline
+struct vfsmount *au_nfsmnt(struct super_block *sb, aufs_bindex_t bindex)
+{
+ return NULL;
+}
+
+#define AuNoNfsBranchMsg "NFS branch is not supported" \
+ ", try some configurations and patches included in aufs source CVS."
+
+#endif /* CONFIG_AUFS_BR_NFS */
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * br_wh_read_lock, br_wh_write_lock
+ * br_wh_read_unlock, br_wh_write_unlock, br_wh_downgrade_lock
+ */
+AuSimpleRwsemFuncs(br_wh, struct au_branch *br, br->br_wh_rwsem);
+
+/* to debug easier, do not make them inlined functions */
+#define BrWhMustReadLock(br) do { \
+ /* SiMustAnyLock(sb); */ \
+ AuRwMustReadLock(&(br)->br_wh_rwsem); \
+} while (0)
+
+#define BrWhMustWriteLock(br) do { \
+ /* SiMustAnyLock(sb); */ \
+ AuRwMustWriteLock(&(br)->br_wh_rwsem); \
+} while (0)
+
+#define BrWhMustAnyLock(br) do { \
+ /* SiMustAnyLock(sb); */ \
+ AuRwMustAnyLock(&(br)->br_wh_rwsem); \
+} while (0)
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_BRANCH_H__ */
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 13/67] aufs branch management
2008-05-16 14:32 ` [PATCH 12/67] aufs branch filesystems and xino for them hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 14/67] aufs external inode number translation table and bitmap hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs branch management
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/branch.c | 933 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 933 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/branch.c b/fs/aufs/branch.c
new file mode 100644
index 0000000..ae5d4b3
--- /dev/null
+++ b/fs/aufs/branch.c
@@ -0,0 +1,933 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * branch management
+ *
+ * $Id: branch.c,v 1.4 2008/05/04 23:51:26 sfjro Exp $
+ */
+
+#include <linux/iso_fs.h>
+#include <linux/loop.h>
+#include <linux/romfs_fs.h>
+#include <linux/smp_lock.h>
+#include "aufs.h"
+
+static void free_branch(struct au_branch *br)
+{
+ AuTraceEnter();
+
+ if (br->br_xino)
+ fput(br->br_xino);
+ dput(br->br_wh);
+ dput(br->br_plink);
+ if (!au_test_nfs(br->br_mnt->mnt_sb))
+ mntput(br->br_mnt);
+ else {
+ lockdep_off();
+ mntput(br->br_mnt);
+ lockdep_on();
+ }
+ AuDebugOn(au_br_count(br) || atomic_read(&br->br_wh_running));
+ //AuDbg("free\n");
+ kfree(br);
+}
+
+/*
+ * frees all branches
+ */
+void au_br_free(struct au_sbinfo *sbinfo)
+{
+ aufs_bindex_t bmax;
+ struct au_branch **br;
+
+ AuTraceEnter();
+ bmax = sbinfo->si_bend + 1;
+ br = sbinfo->si_branch;
+ while (bmax--)
+ free_branch(*br++);
+}
+
+/*
+ * find the index of a branch which is specified by @br_id.
+ */
+int au_br_index(struct super_block *sb, aufs_bindex_t br_id)
+{
+ aufs_bindex_t bindex, bend;
+
+ AuTraceEnter();
+
+ bend = au_sbend(sb);
+ for (bindex = 0; bindex <= bend; bindex++)
+ if (au_sbr_id(sb, bindex) == br_id)
+ return bindex;
+ return -1;
+}
+
+/*
+ * test if the @h_sb is real-readonly.
+ */
+int au_test_def_rr(struct super_block *h_sb)
+{
+ switch (h_sb->s_magic) {
+#ifdef CONFIG_AUFS_RR_SQUASHFS
+ case SQUASHFS_MAGIC_LZMA:
+ case SQUASHFS_MAGIC:
+ case SQUASHFS_MAGIC_LZMA_SWAP:
+ case SQUASHFS_MAGIC_SWAP:
+ return 1; /* real readonly */
+#endif
+
+#if defined(CONFIG_ISO9660_FS) || defined(CONFIG_ISO9660_FS_MODULE)
+ case ISOFS_SUPER_MAGIC:
+ return 1;
+#endif
+
+#if defined(CONFIG_CRAMFS) || defined(CONFIG_CRAMFS_MODULE)
+ case CRAMFS_MAGIC:
+ return 1;
+#endif
+
+#if defined(CONFIG_ROMFS_FS) || defined(CONFIG_ROMFS_FS_MODULE)
+ case ROMFS_MAGIC:
+ return 1;
+#endif
+
+ default:
+ return 0;
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * test if two hidden_dentries have overlapping branches.
+ */
+static int do_test_overlap(struct super_block *sb, struct dentry *h_d1,
+ struct dentry *h_d2)
+{
+ int err;
+
+ LKTRTrace("%.*s, %.*s\n", AuDLNPair(h_d1), AuDLNPair(h_d2));
+
+ err = au_test_subdir(h_d1, h_d2);
+ AuTraceErr(err);
+ return err;
+}
+
+static int test_overlap_loopback(struct super_block *sb, struct dentry *h_d1,
+ struct dentry *h_d2)
+{
+#if defined(CONFIG_BLK_DEV_LOOP) || defined(CONFIG_BLK_DEV_LOOP_MODULE)
+ struct inode *h_inode;
+ struct loop_device *l;
+
+ h_inode = h_d1->d_inode;
+ if (MAJOR(h_inode->i_sb->s_dev) != LOOP_MAJOR)
+ return 0;
+
+ l = h_inode->i_sb->s_bdev->bd_disk->private_data;
+ h_d1 = l->lo_backing_file->f_dentry;
+ if (unlikely(h_d1->d_sb == sb))
+ return 1;
+ return do_test_overlap(sb, h_d1, h_d2);
+#else
+ return 0;
+#endif
+}
+
+static int test_overlap(struct super_block *sb, struct dentry *h_d1,
+ struct dentry *h_d2)
+{
+ LKTRTrace("d1 %.*s, d2 %.*s\n", AuDLNPair(h_d1), AuDLNPair(h_d2));
+
+ if (unlikely(h_d1 == h_d2))
+ return 1;
+ return do_test_overlap(sb, h_d1, h_d2)
+ || do_test_overlap(sb, h_d2, h_d1)
+ || test_overlap_loopback(sb, h_d1, h_d2)
+ || test_overlap_loopback(sb, h_d2, h_d1);
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int init_br_wh(struct super_block *sb, aufs_bindex_t bindex,
+ struct au_branch *br, int new_perm,
+ struct dentry *h_root, struct vfsmount *h_mnt)
+{
+ int err, old_perm;
+ struct inode *dir, *h_dir;
+ const int new = (bindex < 0);
+
+ LKTRTrace("b%d, new_perm %d\n", bindex, new_perm);
+
+ dir = sb->s_root->d_inode;
+ h_dir = h_root->d_inode;
+ if (new)
+ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT);
+ else
+ au_hdir_lock(h_dir, dir, bindex);
+
+ br_wh_write_lock(br);
+ old_perm = br->br_perm;
+ br->br_perm = new_perm;
+ err = au_wh_init(h_root, br, au_do_nfsmnt(h_mnt), sb);
+ br->br_perm = old_perm;
+ br_wh_write_unlock(br);
+
+ if (new)
+ mutex_unlock(&h_dir->i_mutex);
+ else
+ au_hdir_unlock(h_dir, dir, bindex);
+
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * returns a newly allocated branch. @new_nbranch is a number of branches
+ * after adding a branch.
+ */
+static struct au_branch *alloc_addbr(struct super_block *sb, int new_nbranch)
+{
+ struct au_branch **branchp, *add_branch;
+ int sz;
+ void *p;
+ struct dentry *root;
+ struct inode *inode;
+ struct au_hinode *hinodep;
+ struct au_hdentry *hdentryp;
+
+ LKTRTrace("new_nbranch %d\n", new_nbranch);
+ SiMustWriteLock(sb);
+ root = sb->s_root;
+ DiMustWriteLock(root);
+ inode = root->d_inode;
+ IiMustWriteLock(inode);
+
+ add_branch = kmalloc(sizeof(*add_branch), GFP_KERNEL);
+ //if (LktrCond) {kfree(add_branch); add_branch = NULL;}
+ if (unlikely(!add_branch))
+ goto out;
+
+ sz = sizeof(*branchp) * (new_nbranch - 1);
+ if (unlikely(!sz))
+ sz = sizeof(*branchp);
+ p = au_sbi(sb)->si_branch;
+ branchp = au_kzrealloc(p, sz, sizeof(*branchp) * new_nbranch,
+ GFP_KERNEL);
+ //if (LktrCond) branchp = NULL;
+ if (unlikely(!branchp))
+ goto out_br;
+ au_sbi(sb)->si_branch = branchp;
+
+ sz = sizeof(*hdentryp) * (new_nbranch - 1);
+ if (unlikely(!sz))
+ sz = sizeof(*hdentryp);
+ p = au_di(root)->di_hdentry;
+ hdentryp = au_kzrealloc(p, sz, sizeof(*hdentryp) * new_nbranch,
+ GFP_KERNEL);
+ //if (LktrCond) hdentryp = NULL;
+ if (unlikely(!hdentryp))
+ goto out_br;
+ au_di(root)->di_hdentry = hdentryp;
+
+ sz = sizeof(*hinodep) * (new_nbranch - 1);
+ if (unlikely(!sz))
+ sz = sizeof(*hinodep);
+ p = au_ii(inode)->ii_hinode;
+ hinodep = au_kzrealloc(p, sz, sizeof(*hinodep) * new_nbranch,
+ GFP_KERNEL);
+ //if (LktrCond) hinodep = NULL; // unavailable test
+ if (unlikely(!hinodep))
+ goto out_br;
+ au_ii(inode)->ii_hinode = hinodep;
+ return add_branch; /* success */
+
+ out_br:
+ kfree(add_branch);
+ out:
+ AuTraceErr(-ENOMEM);
+ return ERR_PTR(-ENOMEM);
+}
+
+/*
+ * test if the branch permission is legal or not.
+ */
+static int test_br(struct super_block *sb, struct inode *inode, int brperm,
+ char *path)
+{
+ int err;
+
+ err = 0;
+ if (unlikely(au_br_writable(brperm) && IS_RDONLY(inode))) {
+ AuErr("write permission for readonly fs or inode, %s\n", path);
+ err = -EINVAL;
+ }
+
+ AuTraceErr(err);
+ return err;
+}
+
+/*
+ * returns:
+ * 0: success, the caller will add it
+ * plus: success, it is already unified, the caller should ignore it
+ * minus: error
+ */
+static int test_add(struct super_block *sb, struct au_opt_add *add, int remount)
+{
+ int err;
+ struct dentry *root;
+ struct inode *inode, *h_inode;
+ aufs_bindex_t bend, bindex;
+
+ LKTRTrace("%s, remo%d\n", add->path, remount);
+
+ root = sb->s_root;
+ bend = au_sbend(sb);
+ if (unlikely(bend >= 0
+ && au_find_dbindex(root, add->nd.path.dentry) >= 0)) {
+ err = 1;
+ if (!remount) {
+ err = -EINVAL;
+ AuErr("%s duplicated\n", add->path);
+ }
+ goto out;
+ }
+
+ err = -ENOSPC; //-E2BIG;
+ //if (LktrCond) bend = AUFS_BRANCH_MAX;
+ if (unlikely(AUFS_BRANCH_MAX <= add->bindex
+ || AUFS_BRANCH_MAX - 1 <= bend)) {
+ AuErr("number of branches exceeded %s\n", add->path);
+ goto out;
+ }
+
+ err = -EDOM;
+ if (unlikely(add->bindex < 0 || bend + 1 < add->bindex)) {
+ AuErr("bad index %d\n", add->bindex);
+ goto out;
+ }
+
+ inode = add->nd.path.dentry->d_inode;
+ AuDebugOn(!inode || !S_ISDIR(inode->i_mode));
+ err = -ENOENT;
+ if (unlikely(!inode->i_nlink)) {
+ AuErr("no existence %s\n", add->path);
+ goto out;
+ }
+
+ err = -EINVAL;
+ if (unlikely(inode->i_sb == sb)) {
+ AuErr("%s must be outside\n", add->path);
+ goto out;
+ }
+
+ if (unlikely(au_test_nested(inode->i_sb))) {
+ AuErr("nested " AUFS_NAME " %s\n", add->path);
+ goto out;
+ }
+
+ if (unlikely(!strcmp(au_sbtype(inode->i_sb), "unionfs"))) {
+ AuErr("unsupported filesystem, %s\n", add->path);
+ goto out;
+ }
+
+ if (unlikely(au_test_unsupported_nfs(inode->i_sb))) {
+ AuErr(AuNoNfsBranchMsg " %s\n", add->path);
+ goto out;
+ }
+
+ err = test_br(sb, add->nd.path.dentry->d_inode, add->perm, add->path);
+ if (unlikely(err))
+ goto out;
+
+ if (bend < 0)
+ return 0; /* success */
+
+ h_inode = au_h_dptr(root, 0)->d_inode;
+ if (unlikely(au_opt_test(au_mntflags(sb), WARN_PERM)
+ && ((h_inode->i_mode & S_IALLUGO)
+ != (inode->i_mode & S_IALLUGO)
+ || h_inode->i_uid != inode->i_uid
+ || h_inode->i_gid != inode->i_gid)))
+ AuWarn("uid/gid/perm %s %u/%u/0%o, %u/%u/0%o\n",
+ add->path,
+ inode->i_uid, inode->i_gid, (inode->i_mode & S_IALLUGO),
+ h_inode->i_uid, h_inode->i_gid,
+ (h_inode->i_mode & S_IALLUGO));
+
+ err = -EINVAL;
+ for (bindex = 0; bindex <= bend; bindex++)
+ if (unlikely(test_overlap(sb, add->nd.path.dentry,
+ au_h_dptr(root, bindex)))) {
+ AuErr("%s is overlapped\n", add->path);
+ goto out;
+ }
+ err = 0;
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+static int au_br_init(struct au_branch *br, struct super_block *sb,
+ struct au_opt_add *add)
+{
+ int err;
+
+ AuTraceEnter();
+
+ err = 0;
+ au_rw_init_nolock(&br->br_wh_rwsem);
+ br->br_plink = NULL;
+ br->br_wh = NULL;
+ if (unlikely(au_br_writable(add->perm))) {
+ err = init_br_wh(sb, /*bindex*/-1, br, add->perm,
+ add->nd.path.dentry, add->nd.path.mnt);
+ if (unlikely(err))
+ goto out;
+ }
+
+ br->br_xino = NULL;
+ br->br_mnt = mntget(add->nd.path.mnt);
+ if (au_opt_test(au_mntflags(sb), XINO)) {
+ err = au_xino_br(sb, br, add->nd.path.dentry->d_inode->i_ino,
+ au_sbr(sb, 0)->br_xino, /*do_test*/1);
+ if (unlikely(err)) {
+ AuDebugOn(br->br_xino);
+ goto out;
+ }
+ }
+
+ atomic_set(&br->br_wh_running, 0);
+ br->br_id = au_new_br_id(sb);
+ br->br_perm = add->perm;
+ atomic_set(&br->br_count, 0);
+ br->br_bytes = 0;
+ br->br_xino_upper = AUFS_XINO_TRUNC_INIT;
+ atomic_set(&br->br_xino_running, 0);
+ sysaufs_br_init(br);
+ br->br_generation = au_sigen(sb);
+ //smp_mb(); /* atomic_set */
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount)
+{
+ int err, amount;
+ aufs_bindex_t bend, add_bindex;
+ struct dentry *root, *dentry;
+ struct au_iinfo *iinfo;
+ struct au_sbinfo *sbinfo;
+ struct au_dinfo *dinfo;
+ struct inode *root_inode, *inode;
+ unsigned long long maxb;
+ struct au_branch **branchp, *add_branch;
+ struct au_hdentry *hdentryp;
+ struct au_hinode *hinodep;
+
+ dentry = add->nd.path.dentry;
+ LKTRTrace("b%d, %s, 0x%x, %.*s\n",
+ add->bindex, add->path, add->perm, AuDLNPair(dentry));
+ SiMustWriteLock(sb);
+ root = sb->s_root;
+ DiMustWriteLock(root);
+ root_inode = root->d_inode;
+ IMustLock(root_inode);
+ IiMustWriteLock(root_inode);
+
+ err = test_add(sb, add, remount);
+ if (unlikely(err < 0))
+ goto out;
+ if (err)
+ return 0; /* success */
+
+ bend = au_sbend(sb);
+ add_branch = alloc_addbr(sb, bend + 2);
+ err = PTR_ERR(add_branch);
+ if (IS_ERR(add_branch))
+ goto out;
+ err = au_br_init(add_branch, sb, add);
+ if (unlikely(err)) {
+ kfree(add_branch);
+ goto out;
+ }
+
+ add_bindex = add->bindex;
+ if (remount)
+ sysaufs_brs_del(sb, add_bindex);
+
+ sbinfo = au_sbi(sb);
+ dinfo = au_di(root);
+ iinfo = au_ii(root_inode);
+
+ amount = bend + 1 - add_bindex;
+ branchp = sbinfo->si_branch + add_bindex;
+ memmove(branchp + 1, branchp, sizeof(*branchp) * amount);
+ *branchp = add_branch;
+ hdentryp = dinfo->di_hdentry + add_bindex;
+ memmove(hdentryp + 1, hdentryp, sizeof(*hdentryp) * amount);
+ au_h_dentry_init(hdentryp);
+ hinodep = iinfo->ii_hinode + add_bindex;
+ memmove(hinodep + 1, hinodep, sizeof(*hinodep) * amount);
+ hinodep->hi_inode = NULL;
+ au_hin_init(hinodep, NULL);
+
+ sbinfo->si_bend++;
+ dinfo->di_bend++;
+ iinfo->ii_bend++;
+ if (unlikely(bend < 0)) {
+ sbinfo->si_bend = 0;
+ dinfo->di_bstart = 0;
+ iinfo->ii_bstart = 0;
+ }
+ inode = dentry->d_inode;
+ au_set_h_dptr(root, add_bindex, dget(dentry));
+ au_set_h_iptr(root_inode, add_bindex, igrab(inode), 0);
+ if (remount)
+ sysaufs_brs_add(sb, add_bindex);
+
+ if (!add_bindex)
+ au_cpup_attr_all(root_inode);
+ else
+ au_add_nlink(root_inode, inode);
+ maxb = dentry->d_sb->s_maxbytes;
+ if (sb->s_maxbytes < maxb)
+ sb->s_maxbytes = maxb;
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+#define AuVerbose(do_info, fmt, args...) do { \
+ if (!do_info) \
+ LKTRTrace(fmt, ##args); \
+ else \
+ AuInfo(fmt, ##args); \
+} while (0)
+
+/*
+ * test if the branch is deletable or not.
+ */
+static int test_dentry_busy(struct dentry *root, aufs_bindex_t bindex,
+ au_gen_t sigen)
+{
+ int err, i, j, ndentry, verbose;
+ struct au_dcsub_pages dpages;
+ struct au_dpage *dpage;
+ struct dentry *d;
+ aufs_bindex_t bstart, bend;
+ struct inode *inode;
+
+ LKTRTrace("b%d, gen%d\n", bindex, sigen);
+ SiMustWriteLock(root->d_sb);
+
+ err = au_dpages_init(&dpages, GFP_TEMPORARY);
+ if (unlikely(err))
+ goto out;
+ err = au_dcsub_pages(&dpages, root, NULL, NULL);
+ if (unlikely(err))
+ goto out_dpages;
+
+ verbose = !!au_opt_test(au_mntflags(root->d_sb), VERBOSE);
+ for (i = 0; !err && i < dpages.ndpage; i++) {
+ dpage = dpages.dpages + i;
+ ndentry = dpage->ndentry;
+ for (j = 0; !err && j < ndentry; j++) {
+ d = dpage->dentries[j];
+ AuDebugOn(!atomic_read(&d->d_count));
+ inode = d->d_inode;
+ AuDebugOn(!inode);
+ if (au_digen(d) == sigen
+ && au_iigen(inode) == sigen)
+ di_read_lock_child(d, AuLock_IR);
+ else {
+ di_write_lock_child(d);
+ err = au_reval_dpath(d, sigen);
+ if (!err)
+ di_downgrade_lock(d, AuLock_IR);
+ else {
+ di_write_unlock(d);
+ break;
+ }
+ }
+
+ bstart = au_dbstart(d);
+ bend = au_dbend(d);
+ if (bstart <= bindex
+ && bindex <= bend
+ && au_h_dptr(d, bindex)
+ && (!S_ISDIR(d->d_inode->i_mode)
+ || bstart == bend)) {
+ err = -EBUSY;
+ AuVerbose(verbose, "busy %.*s\n", AuDLNPair(d));
+ }
+ di_read_unlock(d, AuLock_IR);
+ }
+ }
+
+ out_dpages:
+ au_dpages_free(&dpages);
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+static int test_inode_busy(struct super_block *sb, aufs_bindex_t bindex,
+ au_gen_t sigen)
+{
+ int err, verbose;
+ struct inode *i;
+ aufs_bindex_t bstart, bend;
+
+ LKTRTrace("b%d, gen%d\n", bindex, sigen);
+ SiMustWriteLock(sb);
+
+ err = 0;
+ verbose = !!au_opt_test(au_mntflags(sb), VERBOSE);
+ list_for_each_entry(i, &sb->s_inodes, i_sb_list) {
+ AuDebugOn(!atomic_read(&i->i_count));
+ if (!list_empty(&i->i_dentry))
+ continue;
+
+ if (au_iigen(i) == sigen)
+ ii_read_lock_child(i);
+ else {
+ ii_write_lock_child(i);
+ err = au_refresh_hinode_self(i);
+ if (!err)
+ ii_downgrade_lock(i);
+ else {
+ ii_write_unlock(i);
+ break;
+ }
+ }
+
+ bstart = au_ibstart(i);
+ bend = au_ibend(i);
+ if (bstart <= bindex
+ && bindex <= bend
+ && au_h_iptr(i, bindex)
+ && (!S_ISDIR(i->i_mode) || bstart == bend)) {
+ err = -EBUSY;
+ AuVerbose(verbose, "busy i%lu\n", i->i_ino);
+ //au_debug_on();
+ //DbgInode(i);
+ //au_debug_off();
+ ii_read_unlock(i);
+ break;
+ }
+ ii_read_unlock(i);
+ }
+
+ AuTraceErr(err);
+ return err;
+}
+
+static int test_children_busy(struct dentry *root, aufs_bindex_t bindex)
+{
+ int err;
+ au_gen_t sigen;
+
+ LKTRTrace("b%d\n", bindex);
+ SiMustWriteLock(root->d_sb);
+ DiMustWriteLock(root);
+ //todo: dont trust BKL.
+ AuDebugOn(!kernel_locked());
+
+ sigen = au_sigen(root->d_sb);
+ DiMustNoWaiters(root);
+ IiMustNoWaiters(root->d_inode);
+ di_write_unlock(root);
+ err = test_dentry_busy(root, bindex, sigen);
+ if (!err)
+ err = test_inode_busy(root->d_sb, bindex, sigen);
+ di_write_lock_child(root); /* aufs_write_lock() calls ..._child() */
+
+ AuTraceErr(err);
+ return err;
+}
+
+int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount)
+{
+ int err, do_wh, rerr, verbose;
+ struct dentry *root;
+ struct inode *inode, *hidden_dir;
+ aufs_bindex_t bindex, bend, br_id;
+ struct au_sbinfo *sbinfo;
+ struct au_dinfo *dinfo;
+ struct au_iinfo *iinfo;
+ struct au_branch *br;
+ unsigned int mnt_flags;
+
+ //au_debug_on();
+ LKTRTrace("%s, %.*s\n", del->path, AuDLNPair(del->h_root));
+ SiMustWriteLock(sb);
+ root = sb->s_root;
+ DiMustWriteLock(root);
+ inode = root->d_inode;
+ IiMustWriteLock(inode);
+
+ err = 0;
+ bindex = au_find_dbindex(root, del->h_root);
+ if (bindex < 0) {
+ if (remount)
+ goto out; /* success */
+ err = -ENOENT;
+ AuErr("%s no such branch\n", del->path);
+ goto out;
+ }
+ LKTRTrace("bindex b%d\n", bindex);
+
+ err = -EBUSY;
+ mnt_flags = au_mntflags(sb);
+ verbose = au_opt_test(mnt_flags, VERBOSE);
+ bend = au_sbend(sb);
+ if (unlikely(!bend)) {
+ AuVerbose(verbose, "no more branches left\n");
+ goto out;
+ }
+ br = au_sbr(sb, bindex);
+ if (unlikely(au_br_count(br))) {
+ AuVerbose(verbose, "%d file(s) opened\n", au_br_count(br));
+ goto out;
+ }
+
+ do_wh = 0;
+ hidden_dir = del->h_root->d_inode;
+ if (br->br_wh || br->br_plink) {
+#if 0 // rfu
+ /* remove whiteout base */
+ err = init_br_wh(sb, bindex, br, AuBr_RO, del->h_root,
+ br->br_mnt);
+ if (unlikely(err))
+ goto out;
+#else
+ dput(br->br_wh);
+ dput(br->br_plink);
+ br->br_plink = NULL;
+ br->br_wh = NULL;
+#endif
+ do_wh = 1;
+ }
+
+ err = test_children_busy(root, bindex);
+ if (unlikely(err)) {
+ if (unlikely(do_wh))
+ goto out_wh;
+ goto out;
+ }
+
+ err = 0;
+ if (remount)
+ sysaufs_brs_del(sb, bindex);
+ sbinfo = au_sbi(sb);
+ dinfo = au_di(root);
+ iinfo = au_ii(inode);
+
+ dput(au_h_dptr(root, bindex));
+ au_hiput(iinfo->ii_hinode + bindex);
+ br_id = br->br_id;
+ free_branch(br);
+
+ //todo: realloc and shrink memory
+ if (bindex < bend) {
+ const aufs_bindex_t n = bend - bindex;
+ struct au_branch **brp;
+ struct au_hdentry *hdp;
+ struct au_hinode *hip;
+
+ brp = sbinfo->si_branch + bindex;
+ memmove(brp, brp + 1, sizeof(*brp) * n);
+ hdp = dinfo->di_hdentry + bindex;
+ memmove(hdp, hdp + 1, sizeof(*hdp) * n);
+ hip = iinfo->ii_hinode + bindex;
+ memmove(hip, hip + 1, sizeof(*hip) * n);
+ }
+ sbinfo->si_branch[0 + bend] = NULL;
+ dinfo->di_hdentry[0 + bend].hd_dentry = NULL;
+ iinfo->ii_hinode[0 + bend].hi_inode = NULL;
+ au_hin_init(iinfo->ii_hinode + bend, NULL);
+
+ sbinfo->si_bend--;
+ dinfo->di_bend--;
+ iinfo->ii_bend--;
+ if (remount)
+ sysaufs_brs_add(sb, bindex);
+
+ if (!bindex)
+ au_cpup_attr_all(inode);
+ else
+ au_sub_nlink(inode, del->h_root->d_inode);
+ if (au_opt_test(mnt_flags, PLINK))
+ au_plink_half_refresh(sb, br_id);
+
+ if (sb->s_maxbytes == del->h_root->d_sb->s_maxbytes) {
+ bend--;
+ sb->s_maxbytes = 0;
+ for (bindex = 0; bindex <= bend; bindex++) {
+ unsigned long long maxb;
+ maxb = au_sbr_sb(sb, bindex)->s_maxbytes;
+ if (sb->s_maxbytes < maxb)
+ sb->s_maxbytes = maxb;
+ }
+ }
+ goto out; /* success */
+
+ out_wh:
+ /* revert */
+ rerr = init_br_wh(sb, bindex, br, br->br_perm, del->h_root, br->br_mnt);
+ if (rerr)
+ AuWarn("failed re-creating base whiteout, %s. (%d)\n",
+ del->path, rerr);
+ out:
+ AuTraceErr(err);
+ //au_debug_off();
+ return err;
+}
+
+static int do_need_sigen_inc(int a, int b)
+{
+ return (au_br_whable(a) && !au_br_whable(b));
+}
+
+static int need_sigen_inc(int old, int new)
+{
+ return (do_need_sigen_inc(old, new)
+ || do_need_sigen_inc(new, old));
+}
+
+int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount,
+ int *do_update)
+{
+ int err;
+ struct dentry *root;
+ aufs_bindex_t bindex;
+ struct au_branch *br;
+ struct inode *hidden_dir;
+
+ LKTRTrace("%s, %.*s, 0x%x\n",
+ mod->path, AuDLNPair(mod->h_root), mod->perm);
+ SiMustWriteLock(sb);
+ root = sb->s_root;
+ DiMustWriteLock(root);
+ IiMustWriteLock(root->d_inode);
+
+ bindex = au_find_dbindex(root, mod->h_root);
+ if (bindex < 0) {
+ if (remount)
+ return 0; /* success */
+ err = -ENOENT;
+ AuErr("%s no such branch\n", mod->path);
+ goto out;
+ }
+ LKTRTrace("bindex b%d\n", bindex);
+
+ hidden_dir = mod->h_root->d_inode;
+ err = test_br(sb, hidden_dir, mod->perm, mod->path);
+ if (unlikely(err))
+ goto out;
+
+ br = au_sbr(sb, bindex);
+ if (br->br_perm == mod->perm)
+ return 0; /* success */
+
+ if (au_br_writable(br->br_perm)) {
+#if 1
+ /* remove whiteout base */
+ //todo: mod->perm?
+ err = init_br_wh(sb, bindex, br, AuBr_RO, mod->h_root,
+ br->br_mnt);
+ if (unlikely(err))
+ goto out;
+#else
+ dput(br->br_wh);
+ dput(br->br_plink);
+ br->br_plink = NULL;
+ br->br_wh = NULL;
+#endif
+
+ if (!au_br_writable(mod->perm)) {
+ /* rw --> ro, file might be mmapped */
+ struct file *file, *hf;
+
+#if 1 // test here
+ DiMustNoWaiters(root);
+ IiMustNoWaiters(root->d_inode);
+ di_write_unlock(root);
+
+ /*
+ * no need file_list_lock()
+ * since BKL (and sbinfo) is locked
+ */
+ AuDebugOn(!kernel_locked());
+ list_for_each_entry(file, &sb->s_files, f_u.fu_list) {
+ LKTRTrace("%.*s\n", AuDLNPair(file->f_dentry));
+ if (!au_test_aufs_file(file))
+ continue;
+
+ fi_read_lock(file);
+ if (!S_ISREG(file->f_dentry->d_inode->i_mode)
+ || !(file->f_mode & FMODE_WRITE)
+ || au_fbstart(file) != bindex) {
+ FiMustNoWaiters(file);
+ fi_read_unlock(file);
+ continue;
+ }
+
+ if (unlikely(au_test_mmapped(file))) {
+ err = -EBUSY;
+ FiMustNoWaiters(file);
+ fi_read_unlock(file);
+ break;
+ }
+
+ // todo: already flushed?
+ hf = au_h_fptr(file, au_fbstart(file));
+ hf->f_flags = au_file_roflags(hf->f_flags);
+ hf->f_mode &= ~FMODE_WRITE;
+ put_write_access(hf->f_dentry->d_inode);
+ FiMustNoWaiters(file);
+ fi_read_unlock(file);
+ }
+
+ /* aufs_write_lock() calls ..._child() */
+ di_write_lock_child(root);
+#endif
+ }
+ }
+
+ if (!err) {
+ *do_update |= need_sigen_inc(br->br_perm, mod->perm);
+ br->br_perm = mod->perm;
+ }
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 14/67] aufs external inode number translation table and bitmap
2008-05-16 14:32 ` [PATCH 13/67] aufs branch management hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 15/67] aufs special handling for inode attributes on FUSE branch hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs external inode number translation table and bitmap
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/xino.c | 1249 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 1249 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/xino.c b/fs/aufs/xino.c
new file mode 100644
index 0000000..58fbafd
--- /dev/null
+++ b/fs/aufs/xino.c
@@ -0,0 +1,1249 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * external inode number translation table and bitmap
+ *
+ * $Id: xino.c,v 1.5 2008/05/12 00:29:11 sfjro Exp $
+ */
+
+//#include <linux/fsnotify.h>
+//#include <linux/smp_lock.h>
+#include <linux/uaccess.h>
+#include "aufs.h"
+
+/* ---------------------------------------------------------------------- */
+
+static ssize_t xino_fread(au_readf_t func, struct file *file, void *buf,
+ size_t size, loff_t *pos)
+{
+ ssize_t err;
+ mm_segment_t oldfs;
+
+ LKTRTrace("%.*s, sz %lu, *pos %Ld\n",
+ AuDLNPair(file->f_dentry), (unsigned long)size, *pos);
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ do {
+ // signal_pending
+ err = func(file, (char __user *)buf, size, pos);
+ } while (err == -EAGAIN || err == -EINTR);
+ set_fs(oldfs);
+
+#if 0 // rfu
+ if (err > 0)
+ fsnotify_access(file->f_dentry);
+#endif
+
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static ssize_t do_xino_fwrite(au_writef_t func, struct file *file, void *buf,
+ size_t size, loff_t *pos)
+{
+ ssize_t err;
+ mm_segment_t oldfs;
+
+ lockdep_off();
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ do {
+ // signal_pending
+ err = func(file, (const char __user *)buf, size, pos);
+ } while (err == -EAGAIN || err == -EINTR);
+ set_fs(oldfs);
+ lockdep_on();
+
+ if (err >= 0)
+ au_update_fuse_h_inode(file->f_vfsmnt, file->f_dentry);
+ /*ignore*/
+
+#if 0 // rfu
+ if (err > 0)
+ fsnotify_modify(file->f_dentry);
+#endif
+
+ AuTraceErr(err);
+ return err;
+}
+
+struct do_xino_fwrite_args {
+ ssize_t *errp;
+ au_writef_t func;
+ struct file *file;
+ void *buf;
+ size_t size;
+ loff_t *pos;
+};
+
+static void call_do_xino_fwrite(void *args)
+{
+ struct do_xino_fwrite_args *a = args;
+ *a->errp = do_xino_fwrite(a->func, a->file, a->buf, a->size, a->pos);
+}
+
+static ssize_t xino_fwrite(au_writef_t func, struct file *file, void *buf,
+ size_t size, loff_t *pos)
+{
+ ssize_t err;
+
+ LKTRTrace("%.*s, sz %lu, *pos %Ld\n",
+ AuDLNPair(file->f_dentry), (unsigned long)size, *pos);
+
+ // signal block and no wkq?
+ /*
+ * it breaks RLIMIT_FSIZE and normal user's limit,
+ * users should care about quota and real 'filesystem full.'
+ */
+ if (!au_test_wkq(current)) {
+ int wkq_err;
+ struct do_xino_fwrite_args args = {
+ .errp = &err,
+ .func = func,
+ .file = file,
+ .buf = buf,
+ .size = size,
+ .pos = pos
+ };
+ wkq_err = au_wkq_wait(call_do_xino_fwrite, &args, /*dlgt*/0);
+ if (unlikely(wkq_err))
+ err = wkq_err;
+ } else
+ err = do_xino_fwrite(func, file, buf, size, pos);
+
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+struct xino_do_trunc_args {
+ struct vfsmount *mnt;
+ struct au_branch *br;
+};
+
+static void xino_do_trunc(void *_args)
+{
+ struct xino_do_trunc_args *args = _args;
+ struct super_block *sb;
+ aufs_bindex_t bindex;
+ int err;
+ struct file *file;
+
+ err = 0;
+ sb = args->mnt->mnt_sb;
+ si_noflush_write_lock(sb);
+ bindex = au_br_index(sb, args->br->br_id);
+ AuDebugOn(bindex < 0);
+ err = au_xino_trunc(sb, bindex);
+ if (unlikely(err))
+ goto out;
+
+ file = args->br->br_xino;
+ au_update_fuse_h_inode(args->br->br_mnt, file->f_dentry); /*ignore*/
+ if (file->f_dentry->d_inode->i_blocks >= args->br->br_xino_upper)
+ args->br->br_xino_upper += AUFS_XINO_TRUNC_STEP;
+ //AuDbg("%Lu\n", (unsigned long long)args->br->br_xino_upper);
+
+ out:
+ si_write_unlock(sb);
+ if (unlikely(err))
+ AuWarn("err b%d, (%d)\n", bindex, err);
+ au_nwt_dec(&au_sbi(sb)->si_nowait);
+ atomic_dec_return(&args->br->br_xino_running);
+ au_br_put(args->br);
+ mntput(args->mnt);
+ kfree(args);
+}
+
+static void xino_try_trunc(struct super_block *sb, struct au_branch *br)
+{
+ struct xino_do_trunc_args *args;
+ struct au_sbinfo *sbinfo;
+ struct file *file = br->br_xino;
+ int wkq_err;
+
+ au_update_fuse_h_inode(br->br_mnt, file->f_dentry); /*ignore*/
+ if (file->f_dentry->d_inode->i_blocks < br->br_xino_upper)
+ return;
+ if (atomic_inc_return(&br->br_xino_running) > 1)
+ goto out;
+
+ /* lock and kfree() will be called in trunc_xino() */
+ args = kmalloc(sizeof(*args), GFP_TEMPORARY);
+ if (args) {
+ args->mnt = au_mntcache_get(sb);
+ args->br = br;
+ au_br_get(br);
+ sbinfo = au_sbi(sb);
+ au_nwt_inc(&sbinfo->si_nowait);
+ wkq_err = au_wkq_nowait(xino_do_trunc, args, sb, /*dlgt*/0);
+ if (!wkq_err)
+ return; /* success */
+
+ AuErr("wkq %d\n", wkq_err);
+ au_nwt_dec(&sbinfo->si_nowait);
+ mntput(args->mnt);
+ au_br_put(br);
+ kfree(args);
+ } else
+ AuErr1("no memory\n");
+ out:
+ atomic_dec_return(&br->br_xino_running);
+}
+
+/* ---------------------------------------------------------------------- */
+
+#define Au_LOFF_MAX ((loff_t)LLONG_MAX)
+
+static int au_xino_do_write(au_writef_t write, struct file *file,
+ ino_t h_ino, struct au_xino_entry *xinoe)
+{
+ loff_t pos;
+ ssize_t sz;
+
+ AuTraceEnter();
+
+ pos = h_ino;
+ if (unlikely(Au_LOFF_MAX / sizeof(*xinoe) - 1 < pos)) {
+ AuIOErr1("too large hi%lu\n", h_ino);
+ return -EFBIG;
+ }
+ pos *= sizeof(*xinoe);
+ sz = xino_fwrite(write, file, xinoe, sizeof(*xinoe), &pos);
+ if (sz == sizeof(*xinoe))
+ return 0; /* success */
+
+ AuIOErr("write failed (%ld)\n", (long)sz);
+ return -EIO;
+}
+
+/*
+ * write @ino to the xinofile for the specified branch{@sb, @bindex}
+ * at the position of @_ino.
+ * when @ino is zero, it is written to the xinofile and means no entry.
+ */
+int au_xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
+ struct au_xino_entry *xinoe)
+{
+ int err;
+ struct file *file;
+ struct au_branch *br;
+ unsigned int mnt_flags;
+
+ LKTRTrace("b%d, hi%lu, i%lu\n", bindex, h_ino, xinoe->ino);
+ BUILD_BUG_ON(sizeof(long long) != sizeof(Au_LOFF_MAX)
+ || ((loff_t) - 1) > 0);
+
+ mnt_flags = au_mntflags(sb);
+ if (unlikely(!au_opt_test(mnt_flags, XINO)))
+ return 0;
+
+ br = au_sbr(sb, bindex);
+ file = br->br_xino;
+ AuDebugOn(!file);
+
+ err = au_xino_do_write(au_sbi(sb)->si_xwrite, file, h_ino, xinoe);
+ if (!err) {
+ if (unlikely(au_opt_test(mnt_flags, TRUNC_XINO)
+ && au_test_trunc_xino(br->br_mnt->mnt_sb)))
+ xino_try_trunc(sb, br);
+ return 0; /* success */
+ }
+
+ AuIOErr("write failed (%d)\n", err);
+ return -EIO;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static const int page_bits = (int)PAGE_SIZE * BITS_PER_BYTE;
+//static const int page_bits = 4;
+static ino_t xib_calc_ino(unsigned long pindex, int bit)
+{
+ ino_t ino;
+ AuDebugOn(bit < 0 || page_bits <= bit);
+ ino = AUFS_FIRST_INO + pindex * page_bits + bit;
+ return ino;
+}
+
+static void xib_calc_bit(ino_t ino, unsigned long *pindex, int *bit)
+{
+ AuDebugOn(ino < AUFS_FIRST_INO);
+ ino -= AUFS_FIRST_INO;
+ *pindex = ino / page_bits;
+ *bit = ino % page_bits;
+}
+
+static int xib_pindex(struct super_block *sb, unsigned long pindex)
+{
+ int err;
+ struct au_sbinfo *sbinfo;
+ loff_t pos;
+ ssize_t sz;
+ struct file *xib;
+ unsigned long *p;
+
+ LKTRTrace("pindex %lu\n", pindex);
+ sbinfo = au_sbi(sb);
+ MtxMustLock(&sbinfo->si_xib_mtx);
+ AuDebugOn(pindex > ULONG_MAX / PAGE_SIZE
+ || !au_opt_test(sbinfo->si_mntflags, XINO));
+
+ if (pindex == sbinfo->si_xib_last_pindex)
+ return 0;
+
+ xib = sbinfo->si_xib;
+ p = sbinfo->si_xib_buf;
+ pos = sbinfo->si_xib_last_pindex;
+ pos *= PAGE_SIZE;
+ sz = xino_fwrite(sbinfo->si_xwrite, xib, p, PAGE_SIZE, &pos);
+ if (unlikely(sz != PAGE_SIZE))
+ goto out;
+
+ pos = pindex;
+ pos *= PAGE_SIZE;
+ if (i_size_read(xib->f_dentry->d_inode) >= pos + PAGE_SIZE)
+ sz = xino_fread(sbinfo->si_xread, xib, p, PAGE_SIZE, &pos);
+ else {
+ memset(p, 0, PAGE_SIZE);
+ sz = xino_fwrite(sbinfo->si_xwrite, xib, p, PAGE_SIZE, &pos);
+ }
+ if (sz == PAGE_SIZE) {
+ sbinfo->si_xib_last_pindex = pindex;
+ return 0; /* success */
+ }
+
+ out:
+ AuIOErr1("write failed (%ld)\n", (long)sz);
+ err = sz;
+ if (sz >= 0)
+ err = -EIO;
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+int au_xino_write0(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
+ ino_t ino)
+{
+ int err, bit;
+ unsigned long pindex;
+ struct au_sbinfo *sbinfo;
+ struct au_xino_entry xinoe = {
+ .ino = 0
+ };
+
+ LKTRTrace("b%d, hi%lu, i%lu\n", bindex, h_ino, ino);
+
+ sbinfo = au_sbi(sb);
+ if (unlikely(!au_opt_test(au_mntflags(sb), XINO)))
+ return 0;
+
+ err = 0;
+ if (unlikely(ino)) {
+ AuDebugOn(ino < AUFS_FIRST_INO);
+ xib_calc_bit(ino, &pindex, &bit);
+ AuDebugOn(page_bits <= bit);
+ mutex_lock(&sbinfo->si_xib_mtx);
+ err = xib_pindex(sb, pindex);
+ if (!err) {
+ clear_bit(bit, sbinfo->si_xib_buf);
+ sbinfo->si_xib_next_bit = bit;
+ }
+ mutex_unlock(&sbinfo->si_xib_mtx);
+ }
+
+ if (!err)
+ err = au_xino_write(sb, bindex, h_ino, &xinoe);
+ return err;
+}
+
+ino_t au_xino_new_ino(struct super_block *sb)
+{
+ ino_t ino;
+ struct au_sbinfo *sbinfo;
+ int free_bit, err;
+ unsigned long *p, pindex, ul, pend;
+ struct file *file;
+
+ //au_debug_on();
+ AuTraceEnter();
+
+ sbinfo = au_sbi(sb);
+ if (unlikely(!au_opt_test(au_mntflags(sb), XINO)))
+ return iunique(sb, AUFS_FIRST_INO);
+
+ mutex_lock(&sbinfo->si_xib_mtx);
+ p = sbinfo->si_xib_buf;
+ free_bit = sbinfo->si_xib_next_bit;
+ //AuDbg("bit %d, pindex %d\n", free_bit, sbinfo->si_xib_last_pindex);
+ //AuDbg("bit %d, %d\n", free_bit, test_bit(free_bit, p));
+ if (free_bit < page_bits && !test_bit(free_bit, p))
+ goto out; /* success */
+ free_bit = find_first_zero_bit(p, page_bits);
+ if (free_bit < page_bits)
+ goto out; /* success */
+
+ pindex = sbinfo->si_xib_last_pindex;
+ for (ul = pindex - 1; ul < ULONG_MAX; ul--) {
+ err = xib_pindex(sb, ul);
+ if (unlikely(err))
+ goto out_err;
+ free_bit = find_first_zero_bit(p, page_bits);
+ if (free_bit < page_bits)
+ goto out; /* success */
+ }
+
+ file = sbinfo->si_xib;
+ pend = i_size_read(file->f_dentry->d_inode) / PAGE_SIZE;
+ for (ul = pindex + 1; ul <= pend; ul++) {
+ err = xib_pindex(sb, ul);
+ if (unlikely(err))
+ goto out_err;
+ free_bit = find_first_zero_bit(p, page_bits);
+ if (free_bit < page_bits)
+ goto out; /* success */
+ }
+ BUG();
+
+ out:
+ set_bit(free_bit, p);
+ sbinfo->si_xib_next_bit++;
+ pindex = sbinfo->si_xib_last_pindex;
+ mutex_unlock(&sbinfo->si_xib_mtx);
+ ino = xib_calc_ino(pindex, free_bit);
+ //AuDbg("i%lu\n", ino);
+ LKTRTrace("i%lu\n", ino);
+ //au_debug_off();
+ return ino;
+ out_err:
+ mutex_unlock(&sbinfo->si_xib_mtx);
+ LKTRTrace("i0\n");
+ //au_debug_off();
+ return 0;
+}
+
+/*
+ * read @ino from xinofile for the specified branch{@sb, @bindex}
+ * at the position of @h_ino.
+ * if @ino does not exist and @do_new is true, get new one.
+ */
+int au_xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
+ struct au_xino_entry *xinoe)
+{
+ int err;
+ struct file *file;
+ loff_t pos;
+ ssize_t sz;
+ struct au_sbinfo *sbinfo;
+
+ LKTRTrace("b%d, hi%lu\n", bindex, h_ino);
+
+ err = 0;
+ xinoe->ino = 0;
+ sbinfo = au_sbi(sb);
+ if (unlikely(!au_opt_test(au_mntflags(sb), XINO)))
+ return 0; /* no ino */
+
+ pos = h_ino;
+ if (unlikely(Au_LOFF_MAX / sizeof(*xinoe) - 1 < pos)) {
+ AuIOErr1("too large hi%lu\n", h_ino);
+ return -EFBIG;
+ }
+ pos *= sizeof(*xinoe);
+
+ file = au_sbr(sb, bindex)->br_xino;
+ AuDebugOn(!file);
+ if (i_size_read(file->f_dentry->d_inode) < pos + sizeof(*xinoe))
+ return 0; /* no ino */
+
+ sz = xino_fread(sbinfo->si_xread, file, xinoe, sizeof(*xinoe), &pos);
+ if (sz == sizeof(*xinoe))
+ return 0; /* success */
+
+ err = sz;
+ if (unlikely(sz >= 0)) {
+ err = -EIO;
+ AuIOErr("xino read error (%ld)\n", (long)sz);
+ }
+
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+struct file *au_xino_create(struct super_block *sb, char *fname, int silent,
+ struct dentry *parent)
+{
+ struct file *file;
+ int err;
+ struct dentry *h_parent;
+ struct inode *h_dir;
+ struct vfsub_args vargs;
+ //const int hinotify = au_opt_test(au_mntflags(sb), UDBA_INOTIFY);
+
+ LKTRTrace("%s\n", fname);
+ //AuDebugOn(!au_opt_test(au_mntflags(sb), XINO));
+
+ /* LSM may detect it */
+ file = vfsub_filp_open(fname, O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE,
+ S_IRUGO | S_IWUGO);
+ //file = ERR_PTR(-1);
+ if (IS_ERR(file)) {
+ if (!silent)
+ AuErr("open %s(%ld)\n", fname, PTR_ERR(file));
+ return file;
+ }
+
+ /* keep file count */
+ h_parent = dget_parent(file->f_dentry);
+ h_dir = h_parent->d_inode;
+ vfsub_args_init(&vargs, NULL, 0, 0);
+ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT);
+ err = vfsub_unlink(h_dir, file->f_dentry, &vargs);
+ mutex_unlock(&h_dir->i_mutex);
+ dput(h_parent);
+ if (unlikely(err)) {
+ if (!silent)
+ AuErr("unlink %s(%d)\n", fname, err);
+ goto out;
+ }
+
+ if (sb != file->f_dentry->d_sb)
+ return file; /* success */
+
+ if (!silent)
+ AuErr("%s must be outside\n", fname);
+ err = -EINVAL;
+
+ out:
+ fput(file);
+ file = ERR_PTR(err);
+ return file;
+}
+
+/*
+ * find another branch who is on the same filesystem of the specified
+ * branch{@btgt}. search until @bend.
+ */
+static int is_sb_shared(struct super_block *sb, aufs_bindex_t btgt,
+ aufs_bindex_t bend)
+{
+ aufs_bindex_t bindex;
+ struct super_block *tgt_sb = au_sbr_sb(sb, btgt);
+
+ for (bindex = 0; bindex <= bend; bindex++)
+ if (unlikely(btgt != bindex && tgt_sb == au_sbr_sb(sb, bindex)))
+ return bindex;
+ return -1;
+}
+
+/*
+ * create a new xinofile at the same place/path as @base_file.
+ */
+static
+struct file *au_xino_create2(struct super_block *sb, struct file *base_file,
+ struct file *copy_src)
+{
+ struct file *file;
+ int err;
+ struct dentry *base, *dentry, *parent;
+ struct inode *dir, *inode;
+ struct qstr *name;
+ struct vfsub_args vargs;
+ struct au_ndx ndx = {
+ .nfsmnt = NULL,
+ .flags = 0,
+ .nd = NULL,
+ //.br = NULL
+ };
+
+ base = base_file->f_dentry;
+ LKTRTrace("%.*s\n", AuDLNPair(base));
+ parent = base->d_parent; /* dir inode is locked */
+ dir = parent->d_inode;
+ IMustLock(dir);
+
+ file = ERR_PTR(-EINVAL);
+ if (unlikely(au_test_nfs(parent->d_sb)))
+ goto out;
+
+ /* do not superio, nor NFS. */
+ name = &base->d_name;
+ dentry = au_lkup_one(name->name, parent, name->len, &ndx);
+ //if (LktrCond) {dput(dentry); dentry = ERR_PTR(-1);}
+ if (IS_ERR(dentry)) {
+ file = (void *)dentry;
+ AuErr("%.*s lookup err %ld\n", AuLNPair(name), PTR_ERR(dentry));
+ goto out;
+ }
+ err = vfsub_create(dir, dentry, S_IRUGO | S_IWUGO, NULL, /*dlgt*/0);
+ //if (LktrCond) {vfs_unlink(dir, dentry); err = -1;}
+ if (unlikely(err)) {
+ file = ERR_PTR(err);
+ AuErr("%.*s create err %d\n", AuLNPair(name), err);
+ goto out_dput;
+ }
+ file = dentry_open(dget(dentry), mntget(base_file->f_vfsmnt),
+ O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE);
+ //if (LktrCond) {fput(file); file = ERR_PTR(-1);}
+ if (IS_ERR(file)) {
+ AuErr("%.*s open err %ld\n", AuLNPair(name), PTR_ERR(file));
+ goto out_dput;
+ }
+ vfsub_args_init(&vargs, NULL, 0, 0);
+ err = vfsub_unlink(dir, dentry, &vargs);
+ //if (LktrCond) err = -1;
+ if (unlikely(err)) {
+ AuErr("%.*s unlink err %d\n", AuLNPair(name), err);
+ goto out_fput;
+ }
+
+ if (copy_src) {
+ inode = copy_src->f_dentry->d_inode;
+ err = au_copy_file(file, copy_src, i_size_read(inode), sb);
+ if (unlikely(err)) {
+ AuErr("%.*s copy err %d\n", AuLNPair(name), err);
+ goto out_fput;
+ }
+ }
+ goto out_dput; /* success */
+
+ out_fput:
+ fput(file);
+ file = ERR_PTR(err);
+ out_dput:
+ dput(dentry);
+ out:
+ AuTraceErrPtr(file);
+ return file;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * initialize the xinofile for the specified branch{@sb, @bindex}
+ * at the place/path where @base_file indicates.
+ * test whether another branch is on the same filesystem or not,
+ * if @do_test is true.
+ */
+int au_xino_br(struct super_block *sb, struct au_branch *br, ino_t h_ino,
+ struct file *base_file, int do_test)
+{
+ int err, do_create;
+ struct au_branch *shared_br;
+ aufs_bindex_t bshared, bend;
+ struct inode *dir;
+ struct au_xino_entry xinoe;
+ struct dentry *parent;
+ struct file *file;
+
+ LKTRTrace("base_file %p, do_test %d\n", base_file, do_test);
+ SiMustWriteLock(sb);
+ AuDebugOn(!au_opt_test(au_mntflags(sb), XINO));
+ AuDebugOn(br->br_xino);
+
+ do_create = 1;
+ bshared = -1;
+ shared_br = NULL;
+ bend = au_sbend(sb);
+ if (do_test) {
+ aufs_bindex_t bindex;
+
+ struct super_block *tgt_sb = br->br_mnt->mnt_sb;
+ for (bindex = 0; bindex <= bend; bindex++)
+ if (unlikely(tgt_sb == au_sbr_sb(sb, bindex))) {
+ bshared = bindex;
+ break;
+ }
+ }
+ if (unlikely(bshared >= 0)) {
+ shared_br = au_sbr(sb, bshared);
+ do_create = !shared_br->br_xino;
+ }
+
+ if (do_create) {
+ parent = dget_parent(base_file->f_dentry);
+ dir = parent->d_inode;
+
+ mutex_lock_nested(&dir->i_mutex, AuLsc_I_PARENT);
+ file = au_xino_create2(sb, base_file, NULL);
+ err = PTR_ERR(file);
+ mutex_unlock(&dir->i_mutex);
+ dput(parent);
+ if (IS_ERR(file))
+ goto out;
+ br->br_xino = file;
+ } else {
+ br->br_xino = shared_br->br_xino;
+ get_file(br->br_xino);
+ }
+
+ xinoe.ino = AUFS_ROOT_INO;
+ //xinoe.h_gen = h_inode->i_generation;
+ //WARN_ON(xinoe.h_gen == AuXino_INVALID_HGEN);
+ err = au_xino_do_write(au_sbi(sb)->si_xwrite, br->br_xino, h_ino,
+ &xinoe);
+ //if (LktrCond) err = -1;
+ if (!err)
+ return 0; /* success */
+
+ //fput(br->br_xino);
+ //br->br_xino = NULL;
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+/* too slow */
+static int do_xib_restore(struct super_block *sb, struct file *file, void *page)
+{
+ int err, bit;
+ struct au_sbinfo *sbinfo;
+ au_readf_t func;
+ loff_t pos, pend;
+ ssize_t sz;
+ struct au_xino_entry *xinoe;
+ unsigned long pindex;
+
+ AuTraceEnter();
+ SiMustWriteLock(sb);
+
+ err = 0;
+ sbinfo = au_sbi(sb);
+ func = sbinfo->si_xread;
+ pend = i_size_read(file->f_dentry->d_inode);
+#ifdef CONFIG_AUFS_DEBUG
+ if (unlikely(pend > (1 << 22)))
+ AuWarn("testing a large xino file %Ld\n", (long long)pend);
+#endif
+ pos = 0;
+ while (pos < pend) {
+ sz = xino_fread(func, file, page, PAGE_SIZE, &pos);
+ err = sz;
+ if (unlikely(sz <= 0))
+ goto out;
+
+ err = 0;
+ for (xinoe = page; sz > 0; xinoe++, sz -= sizeof(xinoe)) {
+ //AuDbg("i%lu\n", xinoe->ino);
+ if (unlikely(xinoe->ino < AUFS_FIRST_INO))
+ continue;
+
+ xib_calc_bit(xinoe->ino, &pindex, &bit);
+ AuDebugOn(page_bits <= bit);
+ err = xib_pindex(sb, pindex);
+ if (!err)
+ set_bit(bit, sbinfo->si_xib_buf);
+ else
+ goto out;
+ //AuDbg("i%lu, bit %d\n", xinoe->ino, bit);
+ }
+ }
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+static int xib_restore(struct super_block *sb)
+{
+ int err;
+ aufs_bindex_t bindex, bend;
+ void *page;
+
+ //au_debug_on();
+ AuTraceEnter();
+
+ err = -ENOMEM;
+ page = (void *)__get_free_page(GFP_TEMPORARY);
+ if (unlikely(!page))
+ goto out;
+
+ err = 0;
+ bend = au_sbend(sb);
+ for (bindex = 0; !err && bindex <= bend; bindex++)
+ if (!bindex || is_sb_shared(sb, bindex, bindex - 1) < 0)
+ err = do_xib_restore
+ (sb, au_sbr(sb, bindex)->br_xino, page);
+ else
+ LKTRTrace("b%d\n", bindex);
+ free_page((unsigned long)page);
+
+ out:
+ AuTraceErr(err);
+ //au_debug_off();
+ return err;
+}
+
+int au_xib_trunc(struct super_block *sb)
+{
+ int err;
+ struct au_sbinfo *sbinfo;
+ unsigned long *p;
+ //ino_t ino;
+ loff_t pos;
+ ssize_t sz;
+ struct dentry *parent;
+ struct inode *dir;
+ struct file *file;
+ unsigned int mnt_flags;
+
+ AuTraceEnter();
+ SiMustWriteLock(sb);
+
+ mnt_flags = au_mntflags(sb);
+ if (unlikely(!au_opt_test(mnt_flags, XINO)))
+ return 0;
+
+ //aufs_debug_on();
+ sbinfo = au_sbi(sb);
+ parent = dget_parent(sbinfo->si_xib->f_dentry);
+ dir = parent->d_inode;
+ mutex_lock_nested(&dir->i_mutex, AuLsc_I_PARENT);
+ file = au_xino_create2(sb, sbinfo->si_xib, NULL);
+ mutex_unlock(&dir->i_mutex);
+ dput(parent);
+ err = PTR_ERR(file);
+ if (IS_ERR(file))
+ goto out;
+ fput(sbinfo->si_xib);
+ sbinfo->si_xib = file;
+
+ p = sbinfo->si_xib_buf;
+ memset(p, 0, PAGE_SIZE);
+ pos = 0;
+ sz = xino_fwrite(sbinfo->si_xwrite, sbinfo->si_xib, p, PAGE_SIZE, &pos);
+ if (unlikely(sz != PAGE_SIZE)) {
+ err = sz;
+ AuIOErr("err %d\n", err);
+ if (sz >= 0)
+ err = -EIO;
+ goto out;
+ }
+
+ if (au_opt_test(mnt_flags, XINO)) {
+ mutex_lock(&sbinfo->si_xib_mtx);
+ err = xib_restore(sb);
+ mutex_unlock(&sbinfo->si_xib_mtx);
+#if 0 // rfu
+ } else {
+ /* is it really safe? */
+ //todo: dont trust BKL.
+ AuDebugOn(!kernel_locked());
+ ino = AUFS_FIRST_INO;
+ list_for_each_entry(inode, &sb->s_inodes, i_sb_list)
+ if (ino < inode->i_ino)
+ ino = inode->i_ino;
+
+ /* make iunique to return larger than active max inode number */
+ iunique(sb, ino);
+ err = 0;
+#endif
+ }
+
+out:
+ //aufs_debug_off();
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * xino mount option handlers
+ */
+static au_readf_t find_readf(struct file *h_file)
+{
+ const struct file_operations *fop = h_file->f_op;
+
+ if (fop) {
+ if (fop->read)
+ return fop->read;
+ if (fop->aio_read)
+ return do_sync_read;
+ }
+ return ERR_PTR(-ENOSYS);
+}
+
+static au_writef_t find_writef(struct file *h_file)
+{
+ const struct file_operations *fop = h_file->f_op;
+
+ if (fop) {
+ if (fop->write)
+ return fop->write;
+ if (fop->aio_write)
+ return do_sync_write;
+ }
+ return ERR_PTR(-ENOSYS);
+}
+
+/* xino bitmap */
+static void xino_clear_xib(struct super_block *sb)
+{
+ struct au_sbinfo *sbinfo;
+
+ AuTraceEnter();
+ SiMustWriteLock(sb);
+
+ sbinfo = au_sbi(sb);
+ sbinfo->si_xread = NULL;
+ sbinfo->si_xwrite = NULL;
+ if (sbinfo->si_xib)
+ fput(sbinfo->si_xib);
+ sbinfo->si_xib = NULL;
+ free_page((unsigned long)sbinfo->si_xib_buf);
+ sbinfo->si_xib_buf = NULL;
+}
+
+static int au_xino_set_xib(struct super_block *sb, struct file *base)
+{
+ int err;
+ struct au_sbinfo *sbinfo;
+ struct file *file;
+ loff_t pos;
+
+ LKTRTrace("%.*s\n", AuDLNPair(base->f_dentry));
+ SiMustWriteLock(sb);
+
+ sbinfo = au_sbi(sb);
+ file = au_xino_create2(sb, base, sbinfo->si_xib);
+ err = PTR_ERR(file);
+ if (IS_ERR(file))
+ goto out;
+ if (sbinfo->si_xib)
+ fput(sbinfo->si_xib);
+ sbinfo->si_xib = file;
+ sbinfo->si_xread = find_readf(file);
+ AuDebugOn(IS_ERR(sbinfo->si_xread));
+ sbinfo->si_xwrite = find_writef(file);
+ AuDebugOn(IS_ERR(sbinfo->si_xwrite));
+
+ err = -ENOMEM;
+ if (!sbinfo->si_xib_buf)
+ sbinfo->si_xib_buf = (void *)get_zeroed_page(GFP_KERNEL);
+ if (unlikely(!sbinfo->si_xib_buf))
+ goto out_unset;
+
+ sbinfo->si_xib_last_pindex = 0;
+ sbinfo->si_xib_next_bit = 0;
+
+ /* no need to lock for i_size_read() */
+ if (i_size_read(file->f_dentry->d_inode) < PAGE_SIZE) {
+ pos = 0;
+ err = xino_fwrite(sbinfo->si_xwrite, file, sbinfo->si_xib_buf,
+ PAGE_SIZE, &pos);
+ if (unlikely(err != PAGE_SIZE))
+ goto out_free;
+ }
+ err = 0;
+ goto out; /* success */
+
+ out_free:
+ free_page((unsigned long)sbinfo->si_xib_buf);
+ sbinfo->si_xib_buf = NULL;
+ if (err >= 0)
+ err = -EIO;
+ out_unset:
+ fput(sbinfo->si_xib);
+ sbinfo->si_xib = NULL;
+ sbinfo->si_xread = NULL;
+ sbinfo->si_xwrite = NULL;
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+/* xino for each branch */
+static void xino_clear_br(struct super_block *sb)
+{
+ aufs_bindex_t bindex, bend;
+ struct au_branch *br;
+
+ AuTraceEnter();
+ SiMustWriteLock(sb);
+
+ bend = au_sbend(sb);
+ for (bindex = 0; bindex <= bend; bindex++) {
+ br = au_sbr(sb, bindex);
+ if (unlikely(!br || !br->br_xino))
+ continue;
+
+ fput(br->br_xino);
+ br->br_xino = NULL;
+ }
+}
+
+static int au_xino_set_br(struct super_block *sb, struct file *base)
+{
+ int err;
+ aufs_bindex_t bindex, bend, bshared;
+ struct {
+ struct file *old, *new;
+ } *fpair, *p;
+ struct au_branch *br;
+ struct au_xino_entry xinoe;
+ struct inode *inode;
+ au_writef_t writef;
+
+ //au_debug_on();
+ LKTRTrace("%.*s\n", AuDLNPair(base->f_dentry));
+ SiMustWriteLock(sb);
+
+ err = -ENOMEM;
+ bend = au_sbend(sb);
+ fpair = kcalloc(bend + 1, sizeof(*fpair), GFP_TEMPORARY);
+ if (unlikely(!fpair))
+ goto out;
+
+ inode = sb->s_root->d_inode;
+ xinoe.ino = AUFS_ROOT_INO;
+ writef = au_sbi(sb)->si_xwrite;
+ for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) {
+ br = au_sbr(sb, bindex);
+ bshared = is_sb_shared(sb, bindex, bindex - 1);
+ if (bshared >= 0) {
+ /* shared xino */
+ *p = fpair[bshared];
+ get_file(p->new);
+ }
+
+ if (!p->new) {
+ /* new xino */
+ p->old = br->br_xino;
+ p->new = au_xino_create2(sb, base, br->br_xino);
+ err = PTR_ERR(p->new);
+ if (IS_ERR(p->new)) {
+ p->new = NULL;
+ goto out_pair;
+ }
+ }
+
+ err = au_xino_do_write(writef, p->new,
+ au_h_iptr(inode, bindex)->i_ino, &xinoe);
+ if (unlikely(err))
+ goto out_pair;
+ }
+
+ for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) {
+ br = au_sbr(sb, bindex);
+ AuDebugOn(p->old != br->br_xino);
+ if (br->br_xino)
+ fput(br->br_xino);
+ get_file(p->new);
+ br->br_xino = p->new;
+ }
+
+ out_pair:
+ for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++)
+ if (p->new)
+ fput(p->new);
+ else
+ break;
+ kfree(fpair);
+ out:
+ AuTraceErr(err);
+ //au_debug_off();
+ return err;
+}
+
+void au_xino_clr(struct super_block *sb)
+{
+ AuTraceEnter();
+ SiMustWriteLock(sb);
+
+ xino_clear_xib(sb);
+ xino_clear_br(sb);
+ au_opt_clr(au_sbi(sb)->si_mntflags, XINO);
+}
+
+int au_xino_set(struct super_block *sb, struct au_opt_xino *xino, int remount)
+{
+ int err, skip;
+ struct dentry *parent, *cur_parent;
+ struct qstr *dname, *cur_name;
+ struct file *cur_xino;
+ struct inode *dir;
+ struct au_sbinfo *sbinfo;
+
+ LKTRTrace("remount %d\n", remount);
+ SiMustWriteLock(sb);
+
+ err = 0;
+ sbinfo = au_sbi(sb);
+ parent = dget_parent(xino->file->f_dentry);
+ if (remount) {
+ skip = 0;
+ dname = &xino->file->f_dentry->d_name;
+ cur_xino = sbinfo->si_xib;
+ if (cur_xino) {
+ cur_parent = dget_parent(cur_xino->f_dentry);
+ cur_name = &cur_xino->f_dentry->d_name;
+ skip = (cur_parent == parent
+ && dname->len == cur_name->len
+ && !memcmp(dname->name, cur_name->name,
+ dname->len));
+ dput(cur_parent);
+ }
+ if (skip)
+ goto out;
+ }
+
+ au_opt_set(sbinfo->si_mntflags, XINO);
+ dir = parent->d_inode;
+ mutex_lock_nested(&dir->i_mutex, AuLsc_I_PARENT);
+ err = au_xino_set_xib(sb, xino->file);
+ if (!err)
+ err = au_xino_set_br(sb, xino->file);
+ mutex_unlock(&dir->i_mutex);
+ if (!err)
+ goto out; /* success */
+
+ /* reset all */
+ AuIOErr("failed creating xino, forcing noxino (%d).\n", err);
+ err = -EIO;
+ au_xino_clr(sb);
+
+ out:
+ dput(parent);
+ AuTraceErr(err);
+ return err;
+}
+
+int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex)
+{
+ int err;
+ struct au_branch *br;
+ struct file *new_xino;
+ struct super_block *h_sb;
+ aufs_bindex_t bi, bend;
+ struct dentry *parent;
+ struct inode *dir;
+
+ //au_debug_on();
+ LKTRTrace("b%d\n", bindex);
+ SiMustWriteLock(sb);
+
+ err = -EINVAL;
+ bend = au_sbend(sb);
+ if (unlikely(bindex < 0 || bend < bindex))
+ goto out;
+ br = au_sbr(sb, bindex);
+ if (unlikely(!br->br_xino))
+ goto out;
+
+ parent = dget_parent(br->br_xino->f_dentry);
+ dir = parent->d_inode;
+ //AuDbgFile(br->br_xino);
+ mutex_lock_nested(&dir->i_mutex, AuLsc_I_PARENT);
+ new_xino = au_xino_create2(sb, br->br_xino, br->br_xino);
+ mutex_unlock(&dir->i_mutex);
+ //AuDbgFile(new_xino);
+ dput(parent);
+ err = PTR_ERR(new_xino);
+ if (IS_ERR(new_xino))
+ goto out;
+ err = 0;
+ fput(br->br_xino);
+ br->br_xino = new_xino;
+
+ h_sb = br->br_mnt->mnt_sb;
+ for (bi = 0; bi <= bend; bi++) {
+ if (unlikely(bi == bindex))
+ continue;
+ br = au_sbr(sb, bi);
+ if (br->br_mnt->mnt_sb != h_sb)
+ continue;
+
+ fput(br->br_xino);
+ br->br_xino = new_xino;
+ get_file(new_xino);
+ }
+
+ out:
+ AuTraceErr(err);
+ //au_debug_off();
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * create a xinofile at the default place/path.
+ */
+struct file *au_xino_def(struct super_block *sb)
+{
+ struct file *file;
+ aufs_bindex_t bend, bindex, bwr;
+ char *page, *p;
+ struct path path;
+
+ AuTraceEnter();
+
+ bend = au_sbend(sb);
+ bwr = -1;
+ for (bindex = 0; bindex <= bend; bindex++)
+ if (au_br_writable(au_sbr_perm(sb, bindex))
+ && !au_test_nfs(au_h_dptr(sb->s_root, bindex)->d_sb)) {
+ bwr = bindex;
+ break;
+ }
+
+ if (bwr >= 0) {
+ file = ERR_PTR(-ENOMEM);
+ page = __getname();
+ //if (LktrCond) {__putname(page); page = NULL;}
+ if (unlikely(!page))
+ goto out;
+ path.mnt = au_sbr_mnt(sb, bwr);
+ path.dentry = au_h_dptr(sb->s_root, bwr);
+ p = d_path(&path, page, PATH_MAX - sizeof(AUFS_XINO_FNAME));
+ //if (LktrCond) p = ERR_PTR(-1);
+ file = (void *)p;
+ if (p && !IS_ERR(p)) {
+ strcat(p, "/" AUFS_XINO_FNAME);
+ LKTRTrace("%s\n", p);
+ file = au_xino_create(sb, p, /*silent*/0, sb->s_root);
+ //if (LktrCond) {fput(file); file = ERR_PTR(-1);}
+ }
+ __putname(page);
+ } else {
+ file = au_xino_create(sb, AUFS_XINO_DEFPATH, /*silent*/0,
+ /*parent*/NULL);
+ //if (LktrCond) {fput(file); file = ERR_PTR(-1);}
+ if (unlikely(au_test_nfs(file->f_dentry->d_sb))) {
+ AuErr("xino or noxino option is required "
+ "since %s is NFS\n", AUFS_XINO_DEFPATH);
+ fput(file);
+ file = ERR_PTR(-EINVAL);
+ }
+ }
+
+ out:
+ AuTraceErrPtr(file);
+ return file;
+}
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 15/67] aufs special handling for inode attributes on FUSE branch
2008-05-16 14:32 ` [PATCH 14/67] aufs external inode number translation table and bitmap hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 16/67] aufs lookup functions for NFS branch in linux-2.6.19 and later hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs special handling for inode attributes on FUSE branch
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/br_fuse.c | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 109 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/br_fuse.c b/fs/aufs/br_fuse.c
new file mode 100644
index 0000000..295139b
--- /dev/null
+++ b/fs/aufs/br_fuse.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * special handling for inode attributes on FUSE branch
+ *
+ * $Id: br_fuse.c,v 1.2 2008/04/21 01:32:05 sfjro Exp $
+ */
+
+#include "aufs.h"
+
+/* h_mnt can be NULL, is it safe? */
+int au_update_fuse_h_inode(struct vfsmount *h_mnt, struct dentry *h_dentry)
+{
+ int err;
+ struct kstat st;
+
+ LKTRTrace("%.*s\n", AuDLNPair(h_dentry));
+
+ err = 0;
+ if (unlikely(h_dentry->d_inode
+ //&& atomic_read(&h_dentry->d_inode->i_count)
+ && au_test_fuse(h_dentry->d_sb))) {
+ err = vfsub_getattr(h_mnt, h_dentry, &st, /*dlgt*/0);
+ if (unlikely(err)) {
+ AuDbg("err %d\n", err);
+ au_debug_on();
+ AuDbgDentry(h_dentry);
+ au_debug_off();
+ WARN_ON(err);
+ }
+ }
+ return err;
+}
+
+/* currently, for fuse only */
+int aufs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *st)
+{
+ int err;
+ struct inode *inode, *h_inode;
+ struct dentry *h_dentry;
+
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+
+ err = 0;
+ aufs_read_lock(dentry, AuLock_IR);
+ inode = dentry->d_inode;
+ h_inode = au_h_iptr(inode, au_ibstart(inode));
+ if (unlikely(au_test_fuse(h_inode->i_sb))) {
+ h_dentry = d_find_alias(h_inode);
+ /* simply gave up updating fuse inode */
+ if (h_dentry) {
+ /*ignore*/
+ if (!au_update_fuse_h_inode(NULL, h_dentry))
+ au_cpup_attr_all(inode);
+ dput(h_dentry);
+ }
+ }
+ generic_fillattr(inode, st);
+
+ aufs_read_unlock(dentry, AuLock_IR);
+ return err;
+}
+
+#if 0 // temp
+/*
+ * This function was born after a discussion with the FUSE developer.
+ * The inode attributes on a filesystem who defines i_op->getattr()
+ * is unreliable since such fs may not maintain the attributes at lookup.
+ * This function doesn't want the result of stat, instead wants the side-effect
+ * which refreshes the attributes.
+ * Hmm, there seems to be no such filesystem except fuse.
+ */
+int vfsub_i_attr(struct vfsmount *mnt, struct dentry *dentry, int dlgt)
+{
+ int err;
+ struct inode *inode;
+ struct inode_operations *op;
+ struct kstat st;
+
+ inode = dentry->d_inode;
+ AuDebugOn(!inode);
+
+ err = 0;
+ op = inode->i_op;
+ if (unlikely(op && op->getattr && !au_test_aufs(dentry->d_sb))) {
+ err = security_inode_getattr(mnt, dentry);
+ if (!err)
+ err = op->getattr(mnt, dentry, &st);
+ }
+ AuTraceErr(err);
+ return err;
+}
+#endif
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 16/67] aufs lookup functions for NFS branch in linux-2.6.19 and later
2008-05-16 14:32 ` [PATCH 15/67] aufs special handling for inode attributes on FUSE branch hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 17/67] aufs special handling inode attributes on XFS branch in linux-2.6.24 " hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs lookup functions for NFS branch in linux-2.6.19 and later
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/br_nfs.c | 391 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 391 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/br_nfs.c b/fs/aufs/br_nfs.c
new file mode 100644
index 0000000..ab5c8c2
--- /dev/null
+++ b/fs/aufs/br_nfs.c
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * lookup functions for NFS branch in linux-2.6.19 and later
+ *
+ * $Id: br_nfs.c,v 1.3 2008/05/04 23:51:14 sfjro Exp $
+ */
+
+#include "aufs.h"
+
+static struct file *au_find_h_intent(struct au_hdentry *hd, struct file *file)
+{
+ struct file *h_file, *hf;
+ struct au_hdintent *hdi, *tmp, *do_free;
+
+ LKTRTrace("%.*s\n", AuDLNPair(hd->hd_dentry));
+
+ h_file = NULL;
+ do_free = NULL;
+ spin_lock(&hd->hd_lock);
+ list_for_each_entry_safe(hdi, tmp, hd->hd_intent_list, hdi_list) {
+ hf = hdi->hdi_file[AuIntent_BRANCH];
+ if (hdi->hdi_file[AuIntent_AUFS] == file
+ && hf->f_dentry == hd->hd_dentry) {
+ h_file = hf;
+ do_free = hdi;
+ list_del(&hdi->hdi_list);
+ break;
+ }
+ }
+ spin_unlock(&hd->hd_lock);
+ kfree(do_free);
+
+ return h_file;
+}
+
+struct file *au_h_intent(struct dentry *dentry, aufs_bindex_t bindex,
+ struct file *file)
+{
+ struct file *h_file;
+ struct au_hdentry *hd = au_di(dentry)->di_hdentry + bindex;
+
+ LKTRTrace("%.*s, b%d, f %p\n", AuDLNPair(dentry), bindex, file);
+ DiMustAnyLock(dentry);
+ AuDebugOn(bindex < au_di(dentry)->di_bstart
+ || bindex > au_di(dentry)->di_bend);
+
+ h_file = NULL;
+ if (!hd->hd_intent_list || !file)
+ return h_file; /* success */
+
+ //AuDebugOn(au_test_wkq(current));
+ h_file = au_find_h_intent(hd, file);
+ //AuDbgFile(h_file);
+ return h_file;
+}
+
+static int au_set_h_intent(struct dentry *dentry, aufs_bindex_t bindex,
+ struct file *file, struct file *h_file)
+{
+ int err;
+ struct au_hdentry *hd = au_di(dentry)->di_hdentry + bindex;
+ struct au_hdintent *hdi;
+ struct file *hf;
+
+ LKTRTrace("%.*s, b%d, f %p\n", AuDLNPair(dentry), bindex, file);
+ /* d_revalidate() holds read_lock */
+ //DiMustWriteLock(dentry);
+ AuDebugOn(bindex < au_di(dentry)->di_bstart
+ || bindex > au_di(dentry)->di_bend
+ || !file
+ || !h_file
+ /* || au_test_wkq(current) */);
+
+ err = -ENOMEM;
+ if (hd->hd_intent_list) {
+ while (1) {
+ hf = au_find_h_intent(hd, file);
+ if (!hf)
+ break;
+#if 0 //def CONFIG_AUFS_DEBUG
+ au_debug_on();
+ DbgDentry(dentry);
+ DbgFile(hf);
+ au_debug_off();
+#endif
+ fput(hf);
+ AuWarn("freed hfile %.*s b%d left\n",
+ AuDLNPair(dentry), bindex);
+ }
+ } else {
+ spin_lock(&hd->hd_lock);
+ if (!hd->hd_intent_list) {
+ hd->hd_intent_list
+ = kmalloc(sizeof(*hd->hd_intent_list),
+ GFP_ATOMIC);
+ if (unlikely(!hd->hd_intent_list)) {
+ spin_unlock(&hd->hd_lock);
+ goto out;
+ }
+ INIT_LIST_HEAD(hd->hd_intent_list);
+ }
+ spin_unlock(&hd->hd_lock);
+ }
+
+ hdi = kmalloc(sizeof(*hdi), GFP_TEMPORARY);
+ if (unlikely(!hdi))
+ goto out;
+
+ err = 0;
+ //hdi->hdi_pid = current->pid;
+ hdi->hdi_file[AuIntent_AUFS] = file;
+ hdi->hdi_file[AuIntent_BRANCH] = h_file;
+ spin_lock(&hd->hd_lock);
+ list_add(&hdi->hdi_list, hd->hd_intent_list);
+ spin_unlock(&hd->hd_lock);
+ //AuDbgDentry(dentry);
+ //AuDbgFile(h_file);
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+int au_br_nfs_h_intent(struct file *nd_file, struct dentry *dentry,
+ aufs_bindex_t bindex, struct nameidata *nd)
+{
+ int err;
+
+ AuTraceEnter();
+
+ err = 0;
+ if (!nd_file)
+ goto out;
+
+ AuDebugOn(!nd);
+ err = au_set_h_intent(dentry, bindex, nd->intent.open.file, nd_file);
+ //err = -1;
+ if (unlikely(err)) {
+ fput(nd_file);
+ au_set_h_dptr(dentry, bindex, NULL);
+ //todo: update bstart and bend?
+ }
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+void au_hintent_put(struct au_hdentry *hd, int do_free)
+{
+ struct au_hdintent *hdi, *tmp;
+ struct file *hf;
+
+ if (unlikely(hd->hd_intent_list)) {
+ // no spin lock
+ list_for_each_entry_safe(hdi, tmp, hd->hd_intent_list,
+ hdi_list) {
+ LKTRTrace("hdi %p\n", hdi);
+ hf = hdi->hdi_file[AuIntent_BRANCH];
+ if (unlikely(hf))
+ fput(hf);
+ //list_del(&hdi->hdi_list);
+ kfree(hdi);
+ }
+ if (do_free)
+ kfree(hd->hd_intent_list);
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+
+#if 0
+/* subset of nameidata */
+struct au_ndsub {
+ struct dentry *dentry;
+ struct vfsmount *mnt;
+ unsigned int flags;
+
+ union {
+ struct open_intent open;
+ } intent;
+};
+
+static void au_ndsub_restore(struct nameidata *nd, struct au_ndsub *save)
+{
+ nd->path.dentry = save->dentty;
+ nd->path.mnt = save->mnt;
+ nd->flags = save->flags;
+ nd->intent = save->intent;
+}
+#endif
+
+int au_fake_intent(/* struct au_ndsub *save, */struct nameidata *nd,
+ int perm)
+{
+ int err;
+
+ LKTRTrace("perm %d\n", perm);
+
+ err = 0;
+#if 0
+ nd->path.dentry = save->dentty;
+ nd->path.mnt = save->mnt;
+ save->flags = nd->flags;
+ save->intent = nd->intent;
+#endif
+
+ nd->intent.open.file = NULL;
+ if (nd->flags & LOOKUP_OPEN) {
+ err = -ENFILE;
+ nd->intent.open.file = get_empty_filp();
+ if (unlikely(!nd->intent.open.file)) {
+ //nd->intent.open.file = save->intent.open.file;
+ goto out;
+ }
+
+ err = 0;
+ if (!au_br_writable(perm)) {
+ nd->intent.open.flags = FMODE_READ
+ | au_file_roflags(nd->intent.open.flags);
+ nd->flags &= ~LOOKUP_CREATE;
+ }
+ }
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+int au_hin_after_reval(struct nameidata *nd, struct dentry *dentry,
+ aufs_bindex_t bindex, struct file *file)
+{
+ int err;
+
+ LKTRTrace("nd %p, %.*s, b%d, f %d\n",
+ nd, AuDLNPair(dentry), bindex, !!file);
+
+ err = 0;
+ if ((nd->flags & LOOKUP_OPEN)
+ && nd->intent.open.file
+ && !IS_ERR(nd->intent.open.file)) {
+ if (nd->intent.open.file->f_dentry) {
+ //AuDbgFile(nd->intent.open.file);
+ err = au_set_h_intent(dentry, bindex, file,
+ nd->intent.open.file);
+ if (!err)
+ nd->intent.open.file = NULL;
+ }
+ if (unlikely(nd->intent.open.file))
+ put_filp(nd->intent.open.file);
+ }
+
+ return err;
+}
+
+#ifdef CONFIG_AUFS_DLGT
+struct au_lookup_hash_args {
+ struct dentry **errp;
+ struct qstr *name;
+ struct dentry *base;
+ struct nameidata *nd;
+};
+
+static void au_call_lookup_hash(void *args)
+{
+ struct au_lookup_hash_args *a = args;
+ *a->errp = vfsub__lookup_hash(a->name, a->base, a->nd);
+}
+
+static struct dentry *
+au_lkup_hash_dlgt(struct qstr *this, struct dentry *parent,
+ struct nameidata *nd, unsigned int flags)
+{
+ struct dentry *dentry;
+ int dirperm1;
+
+ dirperm1 = au_ftest_ndx(flags, DIRPERM1);
+ if (!dirperm1 && !au_ftest_ndx(flags, DLGT))
+ dentry = vfsub__lookup_hash(this, parent, nd);
+ else {
+ int wkq_err;
+ struct au_lookup_hash_args args = {
+ .errp = &dentry,
+ .name = this,
+ .base = parent,
+ .nd = nd
+ };
+ wkq_err = au_wkq_wait(au_call_lookup_hash, &args,
+ /*dlgt*/!dirperm1);
+ if (unlikely(wkq_err))
+ dentry = ERR_PTR(wkq_err);
+ }
+
+ AuTraceErrPtr(dentry);
+ return dentry;
+}
+#else
+static struct dentry *
+au_lkup_hash_dlgt(struct qstr *this, struct dentry *parent,
+ struct nameidata *nd, unsigned int flags)
+{
+ return vfsub__lookup_hash(this, parent, nd);
+}
+#endif /* CONFIG_AUFS_DLGT */
+
+struct dentry *au_lkup_hash(const char *name, struct dentry *parent,
+ int len, struct au_ndx *ndx)
+{
+ struct dentry *dentry;
+ char *p;
+ unsigned long hash;
+ struct qstr this;
+ unsigned int c;
+ struct nameidata tmp_nd, *ndo;
+ int err;
+
+ LKTRTrace("%.*s/%.*s\n", AuDLNPair(parent), len, name);
+
+ //todo: export and call __lookup_one_len() in fs/namei.c
+ dentry = ERR_PTR(-EACCES);
+ this.name = name;
+ this.len = len;
+ if (unlikely(!len))
+ goto out;
+
+ p = (void *)name;
+ hash = init_name_hash();
+ while (len--) {
+ c = *p++;
+ if (unlikely(c == '/' || c == '\0'))
+ goto out;
+ hash = partial_name_hash(c, hash);
+ }
+ this.hash = end_name_hash(hash);
+
+ ndo = ndx->nd;
+ if (ndo) {
+ tmp_nd = *ndo;
+ err = au_fake_intent(&tmp_nd, ndx->br->br_perm);
+ dentry = ERR_PTR(err);
+ if (unlikely(err))
+ goto out_intent;
+ } else
+ memset(&tmp_nd, 0, sizeof(tmp_nd));
+
+ tmp_nd.path.dentry = parent;
+ tmp_nd.path.mnt = ndx->nfsmnt;
+ path_get(&tmp_nd.path);
+ dentry = au_lkup_hash_dlgt(&this, parent, &tmp_nd, ndx->flags);
+ if (0 && !IS_ERR(dentry))
+ AuDbgDentry(dentry);
+ if (!IS_ERR(dentry)) {
+ /* why negative dentry for a new dir was unhashed? */
+ if (unlikely(d_unhashed(dentry)))
+ d_rehash(dentry);
+ if (tmp_nd.intent.open.file
+ && tmp_nd.intent.open.file->f_dentry) {
+ //AuDbgFile(tmp_nd.intent.open.file);
+ ndx->nd_file = tmp_nd.intent.open.file;
+ tmp_nd.intent.open.file = NULL;
+ //au_br_get(ndx->br);
+ }
+ }
+ path_put(&tmp_nd.path);
+
+ out_intent:
+ if (tmp_nd.intent.open.file)
+ put_filp(tmp_nd.intent.open.file);
+ out:
+ AuTraceErrPtr(dentry);
+ return dentry;
+}
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 17/67] aufs special handling inode attributes on XFS branch in linux-2.6.24 and later
2008-05-16 14:32 ` [PATCH 16/67] aufs lookup functions for NFS branch in linux-2.6.19 and later hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 18/67] aufs sysfs interface and lifetime management, header hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs special handling inode attributes on XFS branch in linux-2.6.24 and later
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/br_xfs.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 69 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/br_xfs.c b/fs/aufs/br_xfs.c
new file mode 100644
index 0000000..360ffaa
--- /dev/null
+++ b/fs/aufs/br_xfs.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * special handling inode attributes on XFS branch in linux-2.6.24 and later
+ *
+ * $Id: br_xfs.c,v 1.2 2008/04/21 01:33:00 sfjro Exp $
+ */
+
+#include "aufs.h"
+
+/* h_mnt can be NULL, is it safe? */
+dev_t au_h_rdev(struct inode *h_inode, struct vfsmount *h_mnt,
+ struct dentry *h_dentry)
+{
+ dev_t rdev;
+ int err;
+ struct kstat st;
+
+ LKTRTrace("hi%lu\n", h_inode->i_ino);
+ if (h_dentry)
+ LKTRTrace("%.*s\n", AuDLNPair(h_dentry));
+
+ rdev = h_inode->i_rdev;
+ if (!rdev || !au_test_xfs(h_inode->i_sb))
+ goto out;
+
+ rdev = 0;
+ if (!h_dentry) {
+ err = 0;
+ h_dentry = d_find_alias(h_inode);
+ if (unlikely(!h_dentry))
+ goto failure;
+ err = PTR_ERR(h_dentry);
+ if (IS_ERR(h_dentry)) {
+ h_dentry = NULL;
+ goto failure;
+ }
+ LKTRTrace("%.*s\n", AuDLNPair(h_dentry));
+ } else
+ dget(h_dentry);
+
+ err = vfsub_getattr(h_mnt, h_dentry, &st, /*dlgt*/0);
+ dput(h_dentry);
+ if (!err) {
+ rdev = st.rdev;
+ goto out; /* success */
+ }
+
+ failure:
+ AuIOErr("failed rdev for XFS inode, hi%lu, %d\n", h_inode->i_ino, err);
+ out:
+ return rdev;
+}
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 18/67] aufs sysfs interface and lifetime management, header
2008-05-16 14:32 ` [PATCH 17/67] aufs special handling inode attributes on XFS branch in linux-2.6.24 " hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 19/67] aufs sysfs interface and lifetime management, source hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs sysfs interface and lifetime management, header
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/sysaufs.h | 150 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 150 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/sysaufs.h b/fs/aufs/sysaufs.h
new file mode 100644
index 0000000..eff08c0
--- /dev/null
+++ b/fs/aufs/sysaufs.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2007-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * sysfs interface and lifetime management
+ *
+ * $Id: sysaufs.h,v 1.5 2008/05/04 23:51:26 sfjro Exp $
+ */
+
+#ifndef __SYSAUFS_H__
+#define __SYSAUFS_H__
+
+#ifdef __KERNEL__
+
+#include <linux/fs.h>
+#include <linux/sysfs.h>
+#include "module.h"
+#include "super.h"
+
+/* entries under sysfs per super block */
+enum {
+ SysaufsSb_XINO,
+ SysaufsSb_MNTPNT1,
+#if 0
+ SysaufsSb_PLINK,
+ SysaufsSb_files,
+#endif
+ SysaufsSb_Last
+};
+
+#define SysaufsSb_PREFIX "si_" /* followed by %p */
+
+struct au_sbi_attr {
+ struct attribute attr;
+ int (*show)(struct seq_file *seq, struct super_block *sb);
+};
+
+/* ---------------------------------------------------------------------- */
+
+/* sysaufs.c */
+extern struct mutex au_sbilist_mtx;
+extern struct list_head au_sbilist;
+extern struct attribute *au_sbi_attrs[];
+int sysaufs_si_init(struct au_sbinfo *sbinfo);
+int __init sysaufs_init(void);
+void sysaufs_fin(void);
+
+/* ---------------------------------------------------------------------- */
+
+static inline void au_sbilist_lock(void)
+{
+ mutex_lock(&au_sbilist_mtx);
+}
+
+static inline void au_sbilist_unlock(void)
+{
+ mutex_unlock(&au_sbilist_mtx);
+}
+
+static inline void au_sbilist_del(struct au_sbinfo *sbinfo)
+{
+ list_del(&sbinfo->si_list);
+}
+
+static inline void au_sbilist_add(struct au_sbinfo *sbinfo)
+{
+ /* the order in this list is important */
+ list_add_tail(&sbinfo->si_list, &au_sbilist);
+}
+
+/* ---------------------------------------------------------------------- */
+
+struct au_branch;
+#ifdef CONFIG_SYSFS
+/* sysfs.c */
+extern struct attribute_group *au_attr_group;
+extern struct kobj_type *au_ktype;
+
+int sysaufs_sbi_xino(struct seq_file *seq, struct super_block *sb);
+int sysaufs_sbi_mntpnt1(struct seq_file *seq, struct super_block *sb);
+ssize_t sysaufs_sbi_show(struct kobject *kobj, struct attribute *attr,
+ char *buf);
+
+void sysaufs_br_init(struct au_branch *br);
+void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex);
+void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex);
+
+#define sysaufs_brs_init() do {} while (0)
+
+#else
+#define au_attr_group NULL
+#define au_ktype NULL
+
+static inline
+int sysaufs_sbi_xino(struct seq_file *seq, struct super_block *sb)
+{
+ return 0;
+}
+
+static inline
+int sysaufs_sbi_mntpnt1(struct seq_file *seq, struct super_block *sb)
+{
+ return 0;
+}
+
+static inline
+ssize_t sysaufs_sbi_show(struct kobject *kobj, struct attribute *attr,
+ char *buf)
+{
+ return 0;
+}
+
+static inline void sysaufs_br_init(struct au_branch *br)
+{
+ /* empty */
+}
+
+static inline void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex)
+{
+ /* nothing */
+}
+
+static inline void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex)
+{
+ /* nothing */
+}
+
+static inline void sysaufs_brs_init(void)
+{
+ sysaufs_brs = 0;
+}
+#endif /* CONFIG_SYSFS */
+
+#endif /* __KERNEL__ */
+#endif /* __SYSAUFS_H__ */
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 19/67] aufs sysfs interface and lifetime management, source
2008-05-16 14:32 ` [PATCH 18/67] aufs sysfs interface and lifetime management, header hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 20/67] aufs mount options/flags, header hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs sysfs interface and lifetime management, source
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/sysaufs.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 104 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/sysaufs.c b/fs/aufs/sysaufs.c
new file mode 100644
index 0000000..7df12fd
--- /dev/null
+++ b/fs/aufs/sysaufs.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2007-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * sysfs interface and lifetime management
+ * they are necessary regardless sysfs is disabled.
+ *
+ * $Id: sysaufs.c,v 1.4 2008/04/28 03:05:39 sfjro Exp $
+ */
+
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/sysfs.h>
+#include "aufs.h"
+
+/* ---------------------------------------------------------------------- */
+
+/* super_blocks list is not exported */
+DEFINE_MUTEX(au_sbilist_mtx);
+LIST_HEAD(au_sbilist);
+
+/* ---------------------------------------------------------------------- */
+
+static struct kset au_kset;
+
+#define AuSbiAttr(_name) { \
+ .attr = { .name = __stringify(_name), .mode = 0444 }, \
+ .show = sysaufs_sbi_##_name, \
+}
+
+static struct au_sbi_attr au_sbi_attr_xino = AuSbiAttr(xino);
+struct attribute *au_sbi_attrs[] = {
+ &au_sbi_attr_xino.attr,
+ NULL,
+};
+
+static struct sysfs_ops au_sbi_ops = {
+ .show = sysaufs_sbi_show
+};
+
+static struct kobj_type au_sbi_ktype = {
+ .release = au_si_free,
+ .sysfs_ops = &au_sbi_ops,
+ .default_attrs = au_sbi_attrs,
+};
+
+/* ---------------------------------------------------------------------- */
+
+int sysaufs_si_init(struct au_sbinfo *sbinfo)
+{
+ int err;
+
+ sbinfo->si_kobj.kset = &au_kset;
+ err = kobject_init_and_add(&sbinfo->si_kobj, &au_sbi_ktype,
+ NULL/*&au_kset.kobj*/,
+ SysaufsSb_PREFIX "%p", sbinfo);
+ AuTraceErr(err);
+ return err;
+}
+
+
+/* ---------------------------------------------------------------------- */
+
+void sysaufs_fin(void)
+{
+ AuDebugOn(!list_empty(&au_sbilist));
+ sysfs_remove_group(&au_kset.kobj, au_attr_group);
+ kset_unregister(&au_kset);
+}
+
+int __init sysaufs_init(void)
+{
+ int err;
+
+ sysaufs_brs_init();
+ au_kset.kobj.parent = fs_kobj;
+ kobject_set_name(&au_kset.kobj, AUFS_NAME);
+ au_kset.kobj.ktype = au_ktype;
+ err = kset_register(&au_kset);
+ if (unlikely(err))
+ goto out;
+ err = sysfs_create_group(&au_kset.kobj, au_attr_group);
+ if (unlikely(err))
+ kset_unregister(&au_kset);
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 20/67] aufs mount options/flags, header
2008-05-16 14:32 ` [PATCH 19/67] aufs sysfs interface and lifetime management, source hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 21/67] aufs mount options/flags, source hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs mount options/flags, header
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/opts.h | 245 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 245 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/opts.h b/fs/aufs/opts.h
new file mode 100644
index 0000000..3b3ae84
--- /dev/null
+++ b/fs/aufs/opts.h
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * mount options/flags
+ *
+ * $Id: opts.h,v 1.2 2008/04/21 01:46:26 sfjro Exp $
+ */
+
+#ifndef __AUFS_OPTS_H__
+#define __AUFS_OPTS_H__
+
+#ifdef __KERNEL__
+
+#include <linux/fs.h>
+#include <linux/namei.h>
+#include <linux/aufs_type.h>
+#include "wkq.h"
+
+/* ---------------------------------------------------------------------- */
+/* mount flags */
+
+/* external inode number bitmap and translation table */
+#define AuOpt_XINO 1
+#define AuOpt_TRUNC_XINO (1 << 1)
+#define AuOpt_LINO (1 << 2)
+#define AuOpt_UDBA_NONE (1 << 3) /* users direct branch access */
+#define AuOpt_UDBA_REVAL (1 << 4)
+#define AuOpt_UDBA_INOTIFY (1 << 5)
+#define AuOpt_SHWH (1 << 6)
+#define AuOpt_PLINK (1 << 7)
+#define AuOpt_WARN_PERM (1 << 8)
+#define AuOpt_DIRPERM1 (1 << 9)
+#define AuOpt_DLGT (1 << 10)
+#define AuOpt_COO_NONE (1 << 11) /* copyup on open */
+#define AuOpt_COO_LEAF (1 << 12)
+#define AuOpt_COO_ALL (1 << 13)
+#define AuOpt_ALWAYS_DIROPQ (1 << 14)
+#define AuOpt_REFROF (1 << 15)
+#define AuOpt_VERBOSE (1 << 16)
+#ifndef CONFIG_AUFS_HINOTIFY
+#undef AuOpt_UDBA_INOTIFY
+#define AuOpt_UDBA_INOTIFY 0
+#endif
+#ifndef CONFIG_AUFS_SHWH
+#undef AuOpt_SHWH
+#define AuOpt_SHWH 0
+#endif
+#ifndef CONFIG_AUFS_DLGT
+#undef AuOpt_DIRPERM1
+#define AuOpt_DIRPERM1 0
+#undef AuOpt_DLGT
+#define AuOpt_DLGT 0
+#endif
+
+/* policies to select one among multiple writable branches */
+enum {
+ AuWbrCreate_TDP, /* top down parent */
+ AuWbrCreate_RR, /* round robin */
+ AuWbrCreate_MFS, /* most free space */
+ AuWbrCreate_MFSV, /* mfs with seconds */
+ AuWbrCreate_MFSRR, /* mfs then rr */
+ AuWbrCreate_MFSRRV, /* mfs then rr with seconds */
+ AuWbrCreate_PMFS, /* parent and mfs */
+ AuWbrCreate_PMFSV, /* parent and mfs with seconds */
+
+ AuWbrCreate_Def = AuWbrCreate_TDP
+};
+
+enum {
+ AuWbrCopyup_TDP, /* top down parent */
+ AuWbrCopyup_BUP, /* bottom up parent */
+ AuWbrCopyup_BU, /* bottom up */
+
+ AuWbrCopyup_Def = AuWbrCopyup_TDP
+};
+
+#define AuOptMask_COO (AuOpt_COO_NONE \
+ | AuOpt_COO_LEAF \
+ | AuOpt_COO_ALL)
+#define AuOptMask_UDBA (AuOpt_UDBA_NONE \
+ | AuOpt_UDBA_REVAL \
+ | AuOpt_UDBA_INOTIFY)
+
+#ifdef CONFIG_AUFS_COMPAT
+#define AuOpt_DefExtra1 AuOpt_ALWAYS_DIROPQ
+#else
+#define AuOpt_DefExtra1 0
+#endif
+
+#define AuOpt_Def (AuOpt_XINO \
+ | AuOpt_UDBA_REVAL \
+ | AuOpt_WARN_PERM \
+ | AuOpt_COO_NONE \
+ | AuOpt_PLINK \
+ | AuOpt_DefExtra1)
+
+/* ---------------------------------------------------------------------- */
+
+struct au_opt_add {
+ aufs_bindex_t bindex;
+ char *path;
+ int perm;
+ struct nameidata nd;
+};
+
+struct au_opt_del {
+ char *path;
+ struct dentry *h_root;
+};
+
+struct au_opt_mod {
+ char *path;
+ int perm;
+ struct dentry *h_root;
+};
+
+struct au_opt_xino {
+ char *path;
+ struct file *file;
+};
+
+struct au_opt_xino_itrunc {
+ aufs_bindex_t bindex;
+};
+
+struct au_opt_xino_trunc_v {
+ u64 upper;
+ int step;
+};
+
+struct au_opt_wbr_create {
+ int wbr_create;
+ int mfs_second;
+ u64 mfsrr_watermark;
+};
+
+struct au_opt {
+ int type;
+ union {
+ struct au_opt_xino xino;
+ struct au_opt_xino_itrunc xino_itrunc;
+ struct au_opt_add add;
+ struct au_opt_del del;
+ struct au_opt_mod mod;
+ int dirwh;
+ int rdcache;
+ int deblk;
+ int nhash;
+ int udba;
+ int coo;
+ struct au_opt_wbr_create wbr_create;
+ int wbr_copyup;
+ };
+};
+
+/* opts flags */
+#define AuOpts_REMOUNT 1
+#define AuOpts_REFRESH_DIR (1 << 1)
+#define AuOpts_REFRESH_NONDIR (1 << 2)
+#define AuOpts_TRUNC_XIB (1 << 3)
+#define au_ftest_opts(flags, name) ((flags) & AuOpts_##name)
+#define au_fset_opts(flags, name) { (flags) |= AuOpts_##name; }
+#define au_fclr_opts(flags, name) { (flags) &= ~AuOpts_##name; }
+
+struct au_opts {
+ struct au_opt *opt;
+ int max_opt;
+
+ unsigned int given_udba;
+ unsigned int flags;
+};
+
+/* ---------------------------------------------------------------------- */
+
+const char *au_optstr_br_perm(int brperm);
+const char *au_optstr_udba(int udba);
+const char *au_optstr_coo(int coo);
+const char *au_optstr_wbr_copyup(int wbr_copyup);
+const char *au_optstr_wbr_create(int wbr_create);
+
+void au_opts_free(struct au_opts *opts);
+int au_opts_parse(struct super_block *sb, unsigned long flags, char *str,
+ struct au_opts *opts);
+int au_opts_mount(struct super_block *sb, struct au_opts *opts);
+int au_opts_remount(struct super_block *sb, struct au_opts *opts);
+
+/* ---------------------------------------------------------------------- */
+
+static inline unsigned int au_opt_do_test(unsigned int flags, unsigned int bit)
+{
+ AuDebugOn(bit & (AuOpt_DLGT | AuOpt_DIRPERM1));
+ return (flags & bit);
+}
+
+#define au_opt_test(flags, name) au_opt_do_test(flags, AuOpt_##name)
+
+#define au_opt_set(flags, name) do { \
+ BUILD_BUG_ON(AuOpt_##name & (AuOptMask_COO | AuOptMask_UDBA)); \
+ ((flags) |= AuOpt_##name); \
+} while (0)
+
+#define au_opt_clr(flags, name) { ((flags) &= ~AuOpt_##name); }
+
+#define au_opt_set_coo(flags, name) do { \
+ (flags) &= ~AuOptMask_COO; \
+ ((flags) |= AuOpt_##name); \
+} while(0)
+
+#define au_opt_set_udba(flags, name) do { \
+ (flags) &= ~AuOptMask_UDBA; \
+ ((flags) |= AuOpt_##name); \
+} while(0)
+
+static inline unsigned int au_opt_test_dlgt(unsigned int flags)
+{
+ if (!au_test_wkq(current))
+ return (flags & AuOpt_DLGT);
+ return 0;
+}
+
+static inline unsigned int au_opt_test_dirperm1(unsigned int flags)
+{
+ if (!au_test_wkq(current))
+ return (flags & AuOpt_DIRPERM1);
+ return 0;
+}
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_OPTS_H__ */
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 21/67] aufs mount options/flags, source
2008-05-16 14:32 ` [PATCH 20/67] aufs mount options/flags, header hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 22/67] aufs workqueue for asynchronous/super-io/delegated operations, header hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs mount options/flags, source
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/opts.c | 1493 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 1493 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/opts.c b/fs/aufs/opts.c
new file mode 100644
index 0000000..0c4b64d
--- /dev/null
+++ b/fs/aufs/opts.c
@@ -0,0 +1,1493 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * mount options/flags
+ *
+ * $Id: opts.c,v 1.4 2008/05/04 23:51:41 sfjro Exp $
+ */
+
+#include <linux/types.h> /* a distribution requires */
+#include <linux/parser.h>
+#include "aufs.h"
+
+/* ---------------------------------------------------------------------- */
+
+enum {
+ Opt_br,
+ Opt_add, Opt_del, Opt_mod, Opt_reorder, Opt_append, Opt_prepend,
+ Opt_idel, Opt_imod, Opt_ireorder,
+ Opt_dirwh, Opt_rdcache, Opt_deblk, Opt_nhash, Opt_rendir,
+ Opt_xino, Opt_zxino, Opt_noxino,
+ Opt_trunc_xino, Opt_trunc_xino_v, Opt_notrunc_xino,
+ Opt_trunc_xino_path, Opt_itrunc_xino,
+ Opt_xinodir, Opt_xinonames, Opt_ixinonames,
+ Opt_trunc_xib, Opt_notrunc_xib,
+ Opt_dirperm1, Opt_nodirperm1,
+ Opt_shwh, Opt_noshwh,
+ Opt_plink, Opt_noplink, Opt_list_plink, Opt_clean_plink,
+ Opt_udba,
+ //Opt_lock, Opt_unlock,
+ Opt_cmd, Opt_cmd_args,
+ Opt_diropq_a, Opt_diropq_w,
+ Opt_warn_perm, Opt_nowarn_perm,
+ Opt_wbr_copyup, Opt_wbr_create,
+ Opt_coo,
+ Opt_dlgt, Opt_nodlgt,
+ Opt_refrof, Opt_norefrof,
+ Opt_verbose, Opt_noverbose,
+ Opt_tail, Opt_ignore, Opt_ignore_silent, Opt_err
+};
+
+static match_table_t options = {
+ {Opt_br, "br=%s"},
+ {Opt_br, "br:%s"},
+
+ {Opt_add, "add=%d:%s"},
+ {Opt_add, "add:%d:%s"},
+ {Opt_add, "ins=%d:%s"},
+ {Opt_add, "ins:%d:%s"},
+ {Opt_append, "append=%s"},
+ {Opt_append, "append:%s"},
+ {Opt_prepend, "prepend=%s"},
+ {Opt_prepend, "prepend:%s"},
+
+ {Opt_del, "del=%s"},
+ {Opt_del, "del:%s"},
+ //{Opt_idel, "idel:%d"},
+ {Opt_mod, "mod=%s"},
+ {Opt_mod, "mod:%s"},
+ {Opt_imod, "imod:%d:%s"},
+
+ {Opt_dirwh, "dirwh=%d"},
+ {Opt_dirwh, "dirwh:%d"},
+
+ {Opt_xino, "xino=%s"},
+ {Opt_xino, "xino:%s"},
+ {Opt_xinodir, "xinodir=%s"},
+ {Opt_xinodir, "xinodir:%s"},
+ {Opt_noxino, "noxino"},
+ {Opt_trunc_xino, "trunc_xino"},
+ {Opt_trunc_xino_v, "trunc_xino_v=%d:%d"},
+ {Opt_notrunc_xino, "notrunc_xino"},
+ {Opt_trunc_xino_path, "trunc_xino=%s"},
+ {Opt_trunc_xino_path, "trunc_xino:%s"},
+ {Opt_itrunc_xino, "itrunc_xino=%d"},
+ {Opt_itrunc_xino, "itrunc_xino:%d"},
+ //{Opt_zxino, "zxino=%s"},
+ {Opt_trunc_xib, "trunc_xib"},
+ {Opt_notrunc_xib, "notrunc_xib"},
+
+ {Opt_plink, "plink"},
+ {Opt_noplink, "noplink"},
+#ifdef CONFIG_AUFS_DEBUG
+ {Opt_list_plink, "list_plink"},
+#endif
+ {Opt_clean_plink, "clean_plink"},
+
+ {Opt_udba, "udba=%s"},
+
+ {Opt_diropq_a, "diropq=always"},
+ {Opt_diropq_a, "diropq=a"},
+ {Opt_diropq_w, "diropq=whiteouted"},
+ {Opt_diropq_w, "diropq=w"},
+
+ {Opt_warn_perm, "warn_perm"},
+ {Opt_nowarn_perm, "nowarn_perm"},
+
+#ifdef CONFIG_AUFS_DLGT
+ {Opt_dlgt, "dlgt"},
+ {Opt_nodlgt, "nodlgt"},
+
+ {Opt_dirperm1, "dirperm1"},
+ {Opt_nodirperm1, "nodirperm1"},
+#endif
+
+#ifdef CONFIG_AUFS_SHWH
+ {Opt_shwh, "shwh"},
+ {Opt_noshwh, "noshwh"},
+#endif
+
+ {Opt_rendir, "rendir=%d"},
+ {Opt_rendir, "rendir:%d"},
+
+ {Opt_refrof, "refrof"},
+ {Opt_norefrof, "norefrof"},
+
+ {Opt_verbose, "verbose"},
+ {Opt_verbose, "v"},
+ {Opt_noverbose, "noverbose"},
+ {Opt_noverbose, "quiet"},
+ {Opt_noverbose, "q"},
+ {Opt_noverbose, "silent"},
+
+ {Opt_rdcache, "rdcache=%d"},
+ {Opt_rdcache, "rdcache:%d"},
+
+ {Opt_coo, "coo=%s"},
+
+ {Opt_wbr_create, "create=%s"},
+ {Opt_wbr_create, "create:%s"},
+ {Opt_wbr_create, "create_policy=%s"},
+ {Opt_wbr_create, "create_policy:%s"},
+ {Opt_wbr_copyup, "cpup=%s"},
+ {Opt_wbr_copyup, "cpup:%s"},
+ {Opt_wbr_copyup, "copyup=%s"},
+ {Opt_wbr_copyup, "copyup:%s"},
+ {Opt_wbr_copyup, "copyup_policy=%s"},
+ {Opt_wbr_copyup, "copyup_policy:%s"},
+
+ /* internal use for the scripts */
+ {Opt_ignore_silent, "si=%s"},
+
+#if 0 // rfu
+ {Opt_deblk, "deblk=%d"},
+ {Opt_deblk, "deblk:%d"},
+ {Opt_nhash, "nhash=%d"},
+ {Opt_nhash, "nhash:%d"},
+#endif
+
+ {Opt_br, "dirs=%s"},
+ {Opt_ignore, "debug=%d"},
+ {Opt_ignore, "delete=whiteout"},
+ {Opt_ignore, "delete=all"},
+ {Opt_ignore, "imap=%s"},
+
+ {Opt_err, NULL}
+};
+
+/* ---------------------------------------------------------------------- */
+
+static const char *au_parser_pattern(int val, struct match_token *token)
+{
+ while (token->pattern) {
+ if (token->token == val)
+ return token->pattern;
+ token++;
+ }
+ BUG();
+ return "??";
+}
+
+/* ---------------------------------------------------------------------- */
+
+#define RW "rw"
+#define RO "ro"
+#define WH "wh"
+#define RR "rr"
+#define NoLinkWH "nolwh"
+
+static match_table_t brperms = {
+ {AuBr_RR, RR},
+ {AuBr_RO, RO},
+ {AuBr_RW, RW},
+
+ {AuBr_RRWH, RR "+" WH},
+ {AuBr_ROWH, RO "+" WH},
+ {AuBr_RWNoLinkWH, RW "+" NoLinkWH},
+
+ {AuBr_ROWH, "nfsro"},
+ {AuBr_RO, NULL}
+};
+
+static noinline_for_stack int br_perm_val(char *perm)
+{
+ int val;
+ substring_t args[MAX_OPT_ARGS];
+
+ AuDebugOn(!perm || !*perm);
+ LKTRTrace("perm %s\n", perm);
+ val = match_token(perm, brperms, args);
+ AuTraceErr(val);
+ return val;
+}
+
+const char *au_optstr_br_perm(int brperm)
+{
+ return au_parser_pattern(brperm, brperms);
+}
+
+/* ---------------------------------------------------------------------- */
+
+static match_table_t udbalevel = {
+ {AuOpt_UDBA_REVAL, "reval"},
+#ifdef CONFIG_AUFS_HINOTIFY
+ {AuOpt_UDBA_INOTIFY, "inotify"},
+#endif
+ {AuOpt_UDBA_NONE, "none"},
+ {-1, NULL}
+};
+
+static noinline_for_stack int udba_val(char *str)
+{
+ substring_t args[MAX_OPT_ARGS];
+ return match_token(str, udbalevel, args);
+}
+
+const char *au_optstr_udba(int udba)
+{
+ return au_parser_pattern(udba, udbalevel);
+}
+
+/* ---------------------------------------------------------------------- */
+
+static match_table_t coolevel = {
+ {AuOpt_COO_NONE, "none"},
+ {AuOpt_COO_LEAF, "leaf"},
+ {AuOpt_COO_ALL, "all"},
+ {-1, NULL}
+};
+
+static noinline_for_stack int coo_val(char *str)
+{
+ substring_t args[MAX_OPT_ARGS];
+ return match_token(str, coolevel, args);
+}
+
+const char *au_optstr_coo(int coo)
+{
+ return au_parser_pattern(coo, coolevel);
+}
+
+/* ---------------------------------------------------------------------- */
+
+static match_table_t au_wbr_create_policy = {
+ {AuWbrCreate_TDP, "tdp"},
+ {AuWbrCreate_TDP, "top-down-parent"},
+ {AuWbrCreate_RR, "rr"},
+ {AuWbrCreate_RR, "round-robin"},
+ {AuWbrCreate_MFS, "mfs"},
+ {AuWbrCreate_MFS, "most-free-space"},
+ {AuWbrCreate_MFSV, "mfs:%d"},
+ {AuWbrCreate_MFSV, "most-free-space:%d"},
+
+ {AuWbrCreate_MFSRR, "mfsrr:%d"},
+ {AuWbrCreate_MFSRRV, "mfsrr:%d:%d"},
+ {AuWbrCreate_PMFS, "pmfs"},
+ {AuWbrCreate_PMFSV, "pmfs:%d"},
+
+ {-1, NULL}
+};
+
+/* cf. linux/lib/parser.c */
+static int au_match_ull(substring_t *s, unsigned long long *result, int base)
+{
+ char *endp;
+ char *buf;
+ int ret;
+
+ buf = kmalloc(s->to - s->from + 1, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ memcpy(buf, s->from, s->to - s->from);
+ buf[s->to - s->from] = '\0';
+ *result = simple_strtoull(buf, &endp, base);
+ ret = 0;
+ if (endp == buf)
+ ret = -EINVAL;
+ kfree(buf);
+ return ret;
+}
+
+static int au_wbr_mfs_wmark(substring_t *arg, char *str,
+ struct au_opt_wbr_create *create)
+{
+ int err;
+ u64 ull;
+
+ err = 0;
+ if (!au_match_ull(arg, &ull, 0))
+ create->mfsrr_watermark = ull;
+ else {
+ AuErr("bad integer in %s\n", str);
+ err = -EINVAL;
+ }
+
+ AuTraceErr(err);
+ return err;
+}
+
+static int au_wbr_mfs_sec(substring_t *arg, char *str,
+ struct au_opt_wbr_create *create)
+{
+ int n, err;
+
+ err = 0;
+ if (!match_int(arg, &n) && 0 <= n)
+ create->mfs_second = n;
+ else {
+ AuErr("bad integer in %s\n", str);
+ err = -EINVAL;
+ }
+
+ AuTraceErr(err);
+ return err;
+}
+
+static noinline_for_stack int
+au_wbr_create_val(char *str, struct au_opt_wbr_create *create)
+{
+ int err, e;
+ substring_t args[MAX_OPT_ARGS];
+
+ err = match_token(str, au_wbr_create_policy, args);
+ create->wbr_create = err;
+ switch (err) {
+ case AuWbrCreate_MFSRRV:
+ e = au_wbr_mfs_wmark(&args[0], str, create);
+ if (!e)
+ e = au_wbr_mfs_sec(&args[1], str, create);
+ if (unlikely(e))
+ err = e;
+ break;
+ case AuWbrCreate_MFSRR:
+ e = au_wbr_mfs_wmark(&args[0], str, create);
+ if (unlikely(e)) {
+ err = e;
+ break;
+ }
+ /*FALLTHROUGH*/
+ case AuWbrCreate_MFS:
+ case AuWbrCreate_PMFS:
+ create->mfs_second = AUFS_MFS_SECOND_DEF;
+ break;
+ case AuWbrCreate_MFSV:
+ case AuWbrCreate_PMFSV:
+ e = au_wbr_mfs_sec(&args[0], str, create);
+ if (unlikely(e))
+ err = e;
+ break;
+ }
+
+ return err;
+}
+
+const char *au_optstr_wbr_create(int wbr_create)
+{
+ return au_parser_pattern(wbr_create, au_wbr_create_policy);
+}
+
+static match_table_t au_wbr_copyup_policy = {
+ {AuWbrCopyup_TDP, "tdp"},
+ {AuWbrCopyup_TDP, "top-down-parent"},
+ {AuWbrCopyup_BUP, "bup"},
+ {AuWbrCopyup_BUP, "bottom-up-parent"},
+ {AuWbrCopyup_BU, "bu"},
+ {AuWbrCopyup_BU, "bottom-up"},
+ {-1, NULL}
+};
+
+static noinline_for_stack int au_wbr_copyup_val(char *str)
+{
+ substring_t args[MAX_OPT_ARGS];
+ return match_token(str, au_wbr_copyup_policy, args);
+}
+
+const char *au_optstr_wbr_copyup(int wbr_copyup)
+{
+ return au_parser_pattern(wbr_copyup, au_wbr_copyup_policy);
+}
+
+/* ---------------------------------------------------------------------- */
+
+static const int lkup_dirflags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
+
+static noinline_for_stack void dump_opts(struct au_opts *opts)
+{
+#ifdef CONFIG_AUFS_DEBUG
+ /* reduce stack space */
+ union {
+ struct au_opt_add *add;
+ struct au_opt_del *del;
+ struct au_opt_mod *mod;
+ struct au_opt_xino *xino;
+ struct au_opt_xino_itrunc *xino_itrunc;
+ struct au_opt_wbr_create *create;
+ } u;
+ struct au_opt *opt;
+
+ AuTraceEnter();
+
+ opt = opts->opt;
+ while (/* opt < opts_tail && */ opt->type != Opt_tail) {
+ switch (opt->type) {
+ case Opt_add:
+ u.add = &opt->add;
+ LKTRTrace("add {b%d, %s, 0x%x, %p}\n",
+ u.add->bindex, u.add->path, u.add->perm,
+ u.add->nd.path.dentry);
+ break;
+ case Opt_del:
+ case Opt_idel:
+ u.del = &opt->del;
+ LKTRTrace("del {%s, %p}\n", u.del->path, u.del->h_root);
+ break;
+ case Opt_mod:
+ case Opt_imod:
+ u.mod = &opt->mod;
+ LKTRTrace("mod {%s, 0x%x, %p}\n",
+ u.mod->path, u.mod->perm, u.mod->h_root);
+ break;
+ case Opt_append:
+ u.add = &opt->add;
+ LKTRTrace("append {b%d, %s, 0x%x, %p}\n",
+ u.add->bindex, u.add->path, u.add->perm,
+ u.add->nd.path.dentry);
+ break;
+ case Opt_prepend:
+ u.add = &opt->add;
+ LKTRTrace("prepend {b%d, %s, 0x%x, %p}\n",
+ u.add->bindex, u.add->path, u.add->perm,
+ u.add->nd.path.dentry);
+ break;
+ case Opt_dirwh:
+ LKTRTrace("dirwh %d\n", opt->dirwh);
+ break;
+ case Opt_rdcache:
+ LKTRTrace("rdcache %d\n", opt->rdcache);
+ break;
+ case Opt_xino:
+ u.xino = &opt->xino;
+ LKTRTrace("xino {%s %.*s}\n",
+ u.xino->path,
+ AuDLNPair(u.xino->file->f_dentry));
+ break;
+ case Opt_trunc_xino:
+ LKTRLabel(trunc_xino);
+ break;
+ case Opt_notrunc_xino:
+ LKTRLabel(notrunc_xino);
+ break;
+ case Opt_trunc_xino_path:
+ case Opt_itrunc_xino:
+ u.xino_itrunc = &opt->xino_itrunc;
+ LKTRTrace("trunc_xino %d\n", u.xino_itrunc->bindex);
+ break;
+
+ case Opt_noxino:
+ LKTRLabel(noxino);
+ break;
+ case Opt_trunc_xib:
+ LKTRLabel(trunc_xib);
+ break;
+ case Opt_notrunc_xib:
+ LKTRLabel(notrunc_xib);
+ break;
+ case Opt_dirperm1:
+ LKTRLabel(dirperm1);
+ break;
+ case Opt_nodirperm1:
+ LKTRLabel(nodirperm1);
+ break;
+ case Opt_shwh:
+ LKTRLabel(shwh);
+ break;
+ case Opt_noshwh:
+ LKTRLabel(noshwh);
+ break;
+ case Opt_plink:
+ LKTRLabel(plink);
+ break;
+ case Opt_noplink:
+ LKTRLabel(noplink);
+ break;
+ case Opt_list_plink:
+ LKTRLabel(list_plink);
+ break;
+ case Opt_clean_plink:
+ LKTRLabel(clean_plink);
+ break;
+ case Opt_udba:
+ LKTRTrace("udba %d, %s\n",
+ opt->udba, au_optstr_udba(opt->udba));
+ break;
+ case Opt_diropq_a:
+ LKTRLabel(diropq_a);
+ break;
+ case Opt_diropq_w:
+ LKTRLabel(diropq_w);
+ break;
+ case Opt_warn_perm:
+ LKTRLabel(warn_perm);
+ break;
+ case Opt_nowarn_perm:
+ LKTRLabel(nowarn_perm);
+ break;
+ case Opt_dlgt:
+ LKTRLabel(dlgt);
+ break;
+ case Opt_nodlgt:
+ LKTRLabel(nodlgt);
+ break;
+ case Opt_refrof:
+ LKTRLabel(refrof);
+ break;
+ case Opt_norefrof:
+ LKTRLabel(norefrof);
+ break;
+ case Opt_verbose:
+ LKTRLabel(verbose);
+ break;
+ case Opt_noverbose:
+ LKTRLabel(noverbose);
+ break;
+ case Opt_coo:
+ LKTRTrace("coo %d, %s\n",
+ opt->coo, au_optstr_coo(opt->coo));
+ break;
+ case Opt_wbr_create:
+ u.create = &opt->wbr_create;
+ LKTRTrace("create %d, %s\n", u.create->wbr_create,
+ au_optstr_wbr_create(u.create->wbr_create));
+ switch (u.create->wbr_create) {
+ case AuWbrCreate_MFSV:
+ case AuWbrCreate_PMFSV:
+ LKTRTrace("%d sec\n", u.create->mfs_second);
+ break;
+ case AuWbrCreate_MFSRR:
+ LKTRTrace("%Lu watermark\n",
+ u.create->mfsrr_watermark);
+ break;
+ case AuWbrCreate_MFSRRV:
+ LKTRTrace("%Lu watermark, %d sec\n",
+ u.create->mfsrr_watermark,
+ u.create->mfs_second);
+ break;
+ }
+ break;
+ case Opt_wbr_copyup:
+ LKTRTrace("copyup %d, %s\n", opt->wbr_copyup,
+ au_optstr_wbr_copyup(opt->wbr_copyup));
+ break;
+ default:
+ BUG();
+ }
+ opt++;
+ }
+#endif
+}
+
+void au_opts_free(struct au_opts *opts)
+{
+ struct au_opt *opt;
+
+ AuTraceEnter();
+
+ opt = opts->opt;
+ while (opt->type != Opt_tail) {
+ switch (opt->type) {
+ case Opt_add:
+ case Opt_append:
+ case Opt_prepend:
+ path_put(&opt->add.nd.path);
+ break;
+ case Opt_del:
+ case Opt_idel:
+ dput(opt->del.h_root);
+ break;
+ case Opt_mod:
+ case Opt_imod:
+ dput(opt->mod.h_root);
+ break;
+ case Opt_xino:
+ fput(opt->xino.file);
+ break;
+ }
+ opt++;
+ }
+}
+
+static int opt_add(struct au_opt *opt, char *opt_str, struct super_block *sb,
+ aufs_bindex_t bindex)
+{
+ int err;
+ struct au_opt_add *add = &opt->add;
+ char *p;
+
+ LKTRTrace("%s, b%d\n", opt_str, bindex);
+
+ add->bindex = bindex;
+ add->perm = AuBr_Last;
+ add->path = opt_str;
+ p = strchr(opt_str, '=');
+ if (unlikely(p)) {
+ *p++ = 0;
+ if (*p)
+ add->perm = br_perm_val(p);
+ }
+
+ /* LSM may detect it */
+ /* do not superio. */
+ err = vfsub_path_lookup(add->path, lkup_dirflags, &add->nd);
+ //err = -1;
+ if (!err) {
+ if (!p /* && add->perm == AuBr_Last */) {
+ add->perm = AuBr_RO;
+ if (au_test_def_rr(add->nd.path.dentry->d_sb))
+ add->perm = AuBr_RR;
+ if (!bindex && !(sb->s_flags & MS_RDONLY))
+ add->perm = AuBr_RW;
+#ifdef CONFIG_AUFS_COMPAT
+ add->perm = AuBr_RW;
+#endif
+ }
+ opt->type = Opt_add;
+ goto out;
+ }
+ AuErr("lookup failed %s (%d)\n", add->path, err);
+ err = -EINVAL;
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+/* called without aufs lock */
+int au_opts_parse(struct super_block *sb, unsigned long flags, char *str,
+ struct au_opts *opts)
+{
+ int err, n, token, skipped;
+ struct dentry *root;
+ struct au_opt *opt, *opt_tail;
+ char *opt_str, *p;
+ substring_t args[MAX_OPT_ARGS];
+ aufs_bindex_t bindex, bend;
+ struct nameidata nd;
+ union {
+ struct au_opt_del *del;
+ struct au_opt_mod *mod;
+ struct au_opt_xino *xino;
+ struct au_opt_xino_itrunc *xino_itrunc;
+ struct au_opt_wbr_create *create;
+ } u;
+ struct file *file;
+
+ LKTRTrace("%s, nopts %d\n", str, opts->max_opt);
+
+ root = sb->s_root;
+ err = 0;
+ bindex = 0;
+ opt = opts->opt;
+ opt_tail = opt + opts->max_opt - 1;
+ opt->type = Opt_tail;
+ while (!err && (opt_str = strsep(&str, ",")) && *opt_str) {
+ err = -EINVAL;
+ token = match_token(opt_str, options, args);
+ LKTRTrace("%s, token %d, args[0]{%p, %p}\n",
+ opt_str, token, args[0].from, args[0].to);
+
+ skipped = 0;
+ switch (token) {
+ case Opt_br:
+ err = 0;
+ while (!err && (opt_str = strsep(&args[0].from, ":"))
+ && *opt_str) {
+ err = opt_add(opt, opt_str, sb, bindex++);
+ //if (LktrCond) err = -1;
+ if (unlikely(!err && ++opt > opt_tail)) {
+ err = -E2BIG;
+ break;
+ }
+ opt->type = Opt_tail;
+ skipped = 1;
+ }
+ break;
+ case Opt_add:
+ if (unlikely(match_int(&args[0], &n))) {
+ AuErr("bad integer in %s\n", opt_str);
+ break;
+ }
+ bindex = n;
+ err = opt_add(opt, args[1].from, sb, bindex);
+ break;
+ case Opt_append:
+ err = opt_add(opt, args[0].from, sb, /*dummy bindex*/1);
+ if (!err)
+ opt->type = token;
+ break;
+ case Opt_prepend:
+ err = opt_add(opt, args[0].from, sb, /*bindex*/0);
+ if (!err)
+ opt->type = token;
+ break;
+ case Opt_del:
+ u.del = &opt->del;
+ u.del->path = args[0].from;
+ LKTRTrace("del path %s\n", u.del->path);
+ /* LSM may detect it */
+ /* do not superio. */
+ err = vfsub_path_lookup(u.del->path, lkup_dirflags,
+ &nd);
+ if (unlikely(err)) {
+ AuErr("lookup failed %s (%d)\n",
+ u.del->path, err);
+ break;
+ }
+ u.del->h_root = dget(nd.path.dentry);
+ path_put(&nd.path);
+ opt->type = token;
+ break;
+#if 0 // rfu
+ case Opt_idel:
+ u.del = &opt->del;
+ u.del->path = "(indexed)";
+ if (unlikely(match_int(&args[0], &n))) {
+ AuErr("bad integer in %s\n", opt_str);
+ break;
+ }
+ bindex = n;
+ aufs_read_lock(root, AuLock_FLUSH);
+ if (bindex < 0 || au_sbend(sb) < bindex) {
+ AuErr("out of bounds, %d\n", bindex);
+ aufs_read_unlock(root, !AuLock_IR);
+ break;
+ }
+ err = 0;
+ u.del->h_root = dget(au_h_dptr(root, bindex));
+ opt->type = token;
+ aufs_read_unlock(root, !AuLock_IR);
+ break;
+#endif
+ case Opt_mod:
+ u.mod = &opt->mod;
+ u.mod->path = args[0].from;
+ p = strchr(u.mod->path, '=');
+ if (unlikely(!p)) {
+ AuErr("no permssion %s\n", opt_str);
+ break;
+ }
+ *p++ = 0;
+ u.mod->perm = br_perm_val(p);
+ LKTRTrace("mod path %s, perm 0x%x, %s\n",
+ u.mod->path, u.mod->perm, p);
+ /* LSM may detect it */
+ /* do not superio. */
+ err = vfsub_path_lookup(u.mod->path, lkup_dirflags,
+ &nd);
+ if (unlikely(err)) {
+ AuErr("lookup failed %s (%d)\n",
+ u.mod->path, err);
+ break;
+ }
+ u.mod->h_root = dget(nd.path.dentry);
+ path_put(&nd.path);
+ opt->type = token;
+ break;
+#ifdef IMOD
+ case Opt_imod:
+ u.mod = &opt->mod;
+ u.mod->path = "(indexed)";
+ if (unlikely(match_int(&args[0], &n))) {
+ AuErr("bad integer in %s\n", opt_str);
+ break;
+ }
+ bindex = n;
+ aufs_read_lock(root, AuLock_FLUSH);
+ if (bindex < 0 || au_sbend(sb) < bindex) {
+ AuErr("out of bounds, %d\n", bindex);
+ aufs_read_unlock(root, !AuLock_IR);
+ break;
+ }
+ u.mod->perm = br_perm_val(args[1].from);
+ LKTRTrace("mod path %s, perm 0x%x, %s\n",
+ u.mod->path, u.mod->perm, args[1].from);
+ err = 0;
+ u.mod->h_root = dget(au_h_dptr(root, bindex));
+ opt->type = token;
+ aufs_read_unlock(root, !AuLock_IR);
+ break;
+#endif
+ case Opt_xino:
+ u.xino = &opt->xino;
+ file = au_xino_create(sb, args[0].from, /*silent*/0,
+ /*parent*/NULL);
+ err = PTR_ERR(file);
+ if (IS_ERR(file))
+ break;
+ err = -EINVAL;
+ if (unlikely(file->f_dentry->d_sb == sb)) {
+ fput(file);
+ AuErr("%s must be outside\n", args[0].from);
+ break;
+ }
+ err = 0;
+ u.xino->file = file;
+ u.xino->path = args[0].from;
+ opt->type = token;
+ break;
+
+ case Opt_trunc_xino_path:
+ u.xino_itrunc = &opt->xino_itrunc;
+ p = args[0].from;
+ LKTRTrace("trunc_xino path %s\n", p);
+ /* LSM may detect it */
+ /* do not superio. */
+ err = vfsub_path_lookup(p, lkup_dirflags, &nd);
+ if (unlikely(err)) {
+ AuErr("lookup failed %s (%d)\n", p , err);
+ break;
+ }
+ u.xino_itrunc->bindex = -1;
+ aufs_read_lock(root, AuLock_FLUSH);
+ bend = au_sbend(sb);
+ for (bindex = 0; bindex <= bend; bindex++) {
+ if (au_h_dptr(root, bindex) == nd.path.dentry) {
+ u.xino_itrunc->bindex = bindex;
+ break;
+ }
+ }
+ aufs_read_unlock(root, !AuLock_IR);
+ path_put(&nd.path);
+ if (unlikely(u.xino_itrunc->bindex < 0)) {
+ AuErr("no such branch %s\n", p);
+ err = -EINVAL;
+ break;
+ }
+ opt->type = token;
+ break;
+
+ case Opt_itrunc_xino:
+ u.xino_itrunc = &opt->xino_itrunc;
+ if (unlikely(match_int(&args[0], &n))) {
+ AuErr("bad integer in %s\n", opt_str);
+ break;
+ }
+ u.xino_itrunc->bindex = n;
+ aufs_read_lock(root, AuLock_FLUSH);
+ if (n < 0 || au_sbend(sb) < n) {
+ AuErr("out of bounds, %d\n", n);
+ aufs_read_unlock(root, !AuLock_IR);
+ break;
+ }
+ aufs_read_unlock(root, !AuLock_IR);
+ err = 0;
+ opt->type = token;
+ break;
+
+ case Opt_dirwh:
+ if (unlikely(match_int(&args[0], &opt->dirwh)))
+ break;
+ err = 0;
+ opt->type = token;
+ break;
+
+ case Opt_rdcache:
+ if (unlikely(match_int(&args[0], &opt->rdcache)))
+ break;
+ err = 0;
+ opt->type = token;
+ break;
+
+ case Opt_shwh:
+ if (flags & MS_RDONLY) {
+ err = 0;
+ opt->type = token;
+ } else
+ AuErr("shwh requires ro\n");
+ break;
+
+ case Opt_trunc_xino:
+ case Opt_notrunc_xino:
+ case Opt_noxino:
+ case Opt_trunc_xib:
+ case Opt_notrunc_xib:
+ case Opt_dirperm1:
+ case Opt_nodirperm1:
+ case Opt_noshwh:
+ case Opt_plink:
+ case Opt_noplink:
+ case Opt_list_plink:
+ case Opt_clean_plink:
+ case Opt_diropq_a:
+ case Opt_diropq_w:
+ case Opt_warn_perm:
+ case Opt_nowarn_perm:
+ case Opt_dlgt:
+ case Opt_nodlgt:
+ case Opt_refrof:
+ case Opt_norefrof:
+ case Opt_verbose:
+ case Opt_noverbose:
+ err = 0;
+ opt->type = token;
+ break;
+
+ case Opt_udba:
+ opt->udba = udba_val(args[0].from);
+ if (opt->udba >= 0) {
+ err = 0;
+ opt->type = token;
+ }
+ break;
+
+ case Opt_wbr_create:
+ u.create = &opt->wbr_create;
+ u.create->wbr_create
+ = au_wbr_create_val(args[0].from, u.create);
+ if (u.create->wbr_create >= 0) {
+ err = 0;
+ opt->type = token;
+ }
+ break;
+ case Opt_wbr_copyup:
+ opt->wbr_copyup = au_wbr_copyup_val(args[0].from);
+ if (opt->wbr_copyup >= 0) {
+ err = 0;
+ opt->type = token;
+ }
+ break;
+
+ case Opt_coo:
+ opt->coo = coo_val(args[0].from);
+ if (opt->coo >= 0) {
+ err = 0;
+ opt->type = token;
+ }
+ break;
+
+ case Opt_ignore:
+#ifndef CONFIG_AUFS_COMPAT
+ AuWarn("ignored %s\n", opt_str);
+#endif
+ case Opt_ignore_silent:
+ skipped = 1;
+ err = 0;
+ break;
+ case Opt_err:
+ AuErr("unknown option %s\n", opt_str);
+ break;
+ }
+
+ if (!err && !skipped) {
+ if (unlikely(++opt > opt_tail)) {
+ err = -E2BIG;
+ opt--;
+ opt->type = Opt_tail;
+ break;
+ }
+ opt->type = Opt_tail;
+ }
+ }
+
+ dump_opts(opts);
+ if (unlikely(err))
+ au_opts_free(opts);
+ AuTraceErr(err);
+ return err;
+}
+
+/*
+ * returns,
+ * plus: processed without an error
+ * zero: unprocessed
+ */
+static int au_opt_simple(struct super_block *sb, struct au_opt *opt,
+ struct au_opts *opts)
+{
+ int err;
+ struct au_sbinfo *sbinfo;
+ struct au_opt_wbr_create *create;
+
+ AuTraceEnter();
+
+ err = 1; /* handled */
+ sbinfo = au_sbi(sb);
+ switch (opt->type) {
+ case Opt_udba:
+ sbinfo->si_mntflags &= ~AuOptMask_UDBA;
+ sbinfo->si_mntflags |= opt->udba;
+ opts->given_udba |= opt->udba;
+ break;
+
+ case Opt_plink:
+ au_opt_set(sbinfo->si_mntflags, PLINK);
+ break;
+ case Opt_noplink:
+ if (au_opt_test(sbinfo->si_mntflags, PLINK))
+ au_plink_put(sb);
+ au_opt_clr(sbinfo->si_mntflags, PLINK);
+ break;
+ case Opt_list_plink:
+ if (au_opt_test(sbinfo->si_mntflags, PLINK))
+ au_plink_list(sb);
+ break;
+ case Opt_clean_plink:
+ if (au_opt_test(sbinfo->si_mntflags, PLINK))
+ au_plink_put(sb);
+ break;
+
+ case Opt_diropq_a:
+ au_opt_set(sbinfo->si_mntflags, ALWAYS_DIROPQ);
+ break;
+ case Opt_diropq_w:
+ au_opt_clr(sbinfo->si_mntflags, ALWAYS_DIROPQ);
+ break;
+
+ case Opt_dlgt:
+ au_opt_set(sbinfo->si_mntflags, DLGT);
+ break;
+ case Opt_nodlgt:
+ au_opt_clr(sbinfo->si_mntflags, DLGT);
+ break;
+
+ case Opt_warn_perm:
+ au_opt_set(sbinfo->si_mntflags, WARN_PERM);
+ break;
+ case Opt_nowarn_perm:
+ au_opt_clr(sbinfo->si_mntflags, WARN_PERM);
+ break;
+
+ case Opt_refrof:
+ au_opt_set(sbinfo->si_mntflags, REFROF);
+ break;
+ case Opt_norefrof:
+ //au_opt_set(sbinfo->si_mntflags, COO_LEAF);
+ au_opt_clr(sbinfo->si_mntflags, REFROF);
+ break;
+
+ case Opt_verbose:
+ au_opt_set(sbinfo->si_mntflags, VERBOSE);
+ break;
+ case Opt_noverbose:
+ au_opt_clr(sbinfo->si_mntflags, VERBOSE);
+ break;
+
+ case Opt_wbr_create:
+ create = &opt->wbr_create;
+ if (sbinfo->si_wbr_create_ops->fin) {
+ err = sbinfo->si_wbr_create_ops->fin(sb);
+ if (!err)
+ err = 1;
+ }
+ sbinfo->si_wbr_create = create->wbr_create;
+ sbinfo->si_wbr_create_ops
+ = au_wbr_create_ops + create->wbr_create;
+ switch (create->wbr_create) {
+ case AuWbrCreate_MFSRRV:
+ case AuWbrCreate_MFSRR:
+ sbinfo->si_wbr_mfs.mfsrr_watermark
+ = create->mfsrr_watermark;
+ /*FALLTHROUGH*/
+ case AuWbrCreate_MFS:
+ case AuWbrCreate_MFSV:
+ case AuWbrCreate_PMFS:
+ case AuWbrCreate_PMFSV:
+ sbinfo->si_wbr_mfs.mfs_expire = create->mfs_second * HZ;
+ break;
+ }
+ if (sbinfo->si_wbr_create_ops->init)
+ sbinfo->si_wbr_create_ops->init(sb); /* ignore */
+ break;
+ case Opt_wbr_copyup:
+ sbinfo->si_wbr_copyup = opt->wbr_copyup;
+ sbinfo->si_wbr_copyup_ops = au_wbr_copyup_ops + opt->wbr_copyup;
+ break;
+
+ case Opt_coo:
+ sbinfo->si_mntflags &= ~AuOptMask_COO;
+ sbinfo->si_mntflags |= opt->coo;
+ break;
+
+ case Opt_dirwh:
+ sbinfo->si_dirwh = opt->dirwh;
+ break;
+
+ case Opt_rdcache:
+ sbinfo->si_rdcache = opt->rdcache * HZ;
+ break;
+
+ case Opt_trunc_xino:
+ au_opt_set(sbinfo->si_mntflags, TRUNC_XINO);
+ break;
+ case Opt_notrunc_xino:
+ au_opt_clr(sbinfo->si_mntflags, TRUNC_XINO);
+ break;
+
+ case Opt_dirperm1:
+ au_opt_set(sbinfo->si_mntflags, DIRPERM1);
+ break;
+ case Opt_nodirperm1:
+ au_opt_clr(sbinfo->si_mntflags, DIRPERM1);
+ break;
+
+ case Opt_shwh:
+ au_opt_set(sbinfo->si_mntflags, SHWH);
+ break;
+ case Opt_noshwh:
+ au_opt_clr(sbinfo->si_mntflags, SHWH);
+ break;
+
+ case Opt_trunc_xino_path:
+ case Opt_itrunc_xino:
+ err = au_xino_trunc(sb, opt->xino_itrunc.bindex);
+ if (!err)
+ err = 1;
+ break;
+
+ case Opt_trunc_xib:
+ au_fset_opts(opts->flags, TRUNC_XIB);
+ break;
+ case Opt_notrunc_xib:
+ au_fclr_opts(opts->flags, TRUNC_XIB);
+ break;
+
+ default:
+ err = 0;
+ break;
+ }
+
+ AuTraceErr(err);
+ return err;
+}
+
+/*
+ * returns tri-state.
+ * plus: processed without an error
+ * zero: unprocessed
+ * minus: error
+ */
+static int au_opt_br(struct super_block *sb, struct au_opt *opt,
+ struct au_opts *opts)
+{
+ int err, do_refresh;
+
+ AuTraceEnter();
+
+ err = 0;
+ switch (opt->type) {
+ case Opt_append:
+ opt->add.bindex = au_sbend(sb) + 1;
+ if (unlikely(opt->add.bindex < 0))
+ opt->add.bindex = 0;
+ goto add;
+ case Opt_prepend:
+ opt->add.bindex = 0;
+ add:
+ case Opt_add:
+ err = au_br_add(sb, &opt->add,
+ au_ftest_opts(opts->flags, REMOUNT));
+ if (!err) {
+ err = 1;
+ au_fset_opts(opts->flags, REFRESH_DIR);
+ if (unlikely(au_br_whable(opt->add.perm)))
+ au_fset_opts(opts->flags, REFRESH_NONDIR);
+ }
+ break;
+
+ case Opt_del:
+ case Opt_idel:
+ err = au_br_del(sb, &opt->del,
+ au_ftest_opts(opts->flags, REMOUNT));
+ if (!err) {
+ err = 1;
+ au_fset_opts(opts->flags, TRUNC_XIB);
+ au_fset_opts(opts->flags, REFRESH_DIR);
+ au_fset_opts(opts->flags, REFRESH_NONDIR);
+ }
+ break;
+
+ case Opt_mod:
+ case Opt_imod:
+ err = au_br_mod(sb, &opt->mod,
+ au_ftest_opts(opts->flags, REMOUNT),
+ &do_refresh);
+ if (!err) {
+ err = 1;
+ if (unlikely(do_refresh)) {
+ au_fset_opts(opts->flags, REFRESH_DIR);
+ au_fset_opts(opts->flags, REFRESH_NONDIR);
+ }
+ }
+ break;
+ }
+
+ AuTraceErr(err);
+ return err;
+}
+
+static int au_opt_xino(struct super_block *sb, struct au_opt *opt,
+ struct au_opt_xino **opt_xino, struct au_opts *opts)
+{
+ int err;
+
+ AuTraceEnter();
+
+ err = 0;
+ switch (opt->type) {
+ case Opt_xino:
+ err = au_xino_set(sb, &opt->xino,
+ au_ftest_opts(opts->flags, REMOUNT));
+ if (!err)
+ *opt_xino = &opt->xino;
+ break;
+ case Opt_noxino:
+ au_xino_clr(sb);
+ *opt_xino = (void *)-1;
+ break;
+ }
+
+ AuTraceErr(err);
+ return err;
+}
+
+static noinline_for_stack int
+verify_opts(struct super_block *sb, unsigned int pending, int remount)
+{
+ int err;
+ aufs_bindex_t bindex, bend;
+ struct au_branch *br;
+ struct dentry *root;
+ struct inode *dir;
+ unsigned int do_plink;
+ unsigned int mnt_flags;
+
+ AuTraceEnter();
+ mnt_flags = au_mntflags(sb);
+ AuDebugOn(!(mnt_flags & AuOptMask_COO));
+ AuDebugOn(!(mnt_flags & AuOptMask_UDBA));
+
+ if (!(sb->s_flags & MS_RDONLY)) {
+ if (unlikely(!au_br_writable(au_sbr_perm(sb, 0))))
+ AuWarn("first branch should be rw\n");
+ if (unlikely(au_opt_test(mnt_flags, SHWH)))
+ AuWarn("shwh should be used with ro\n");
+ }
+
+ if (unlikely(au_opt_test(mnt_flags | pending, UDBA_INOTIFY)
+ && !au_opt_test(mnt_flags, XINO)))
+ AuWarn("udba=inotify requires xino\n");
+
+ err = 0;
+ root = sb->s_root;
+ dir = sb->s_root->d_inode;
+ do_plink = !!au_opt_test(mnt_flags, PLINK);
+ bend = au_sbend(sb);
+ for (bindex = 0; !err && bindex <= bend; bindex++) {
+ struct inode *h_dir;
+ int skip;
+
+ skip = 0;
+ h_dir = au_h_iptr(dir, bindex);
+ br = au_sbr(sb, bindex);
+ br_wh_read_lock(br);
+ switch (br->br_perm) {
+ case AuBr_RR:
+ case AuBr_RO:
+ case AuBr_RRWH:
+ case AuBr_ROWH:
+ skip = (!br->br_wh && !br->br_plink);
+ break;
+
+ case AuBr_RWNoLinkWH:
+ skip = !br->br_wh;
+ if (skip) {
+ if (do_plink)
+ skip = !!br->br_plink;
+ else
+ skip = !br->br_plink;
+ }
+ break;
+
+ case AuBr_RW:
+ skip = !!br->br_wh;
+ if (skip) {
+ if (do_plink)
+ skip = !!br->br_plink;
+ else
+ skip = !br->br_plink;
+ }
+ break;
+
+ default:
+ BUG();
+ }
+ br_wh_read_unlock(br);
+
+ if (skip)
+ continue;
+
+ au_hdir_lock(h_dir, dir, bindex);
+ br_wh_write_lock(br);
+ err = au_wh_init(au_h_dptr(root, bindex), br,
+ au_nfsmnt(sb, bindex), sb);
+ br_wh_write_unlock(br);
+ au_hdir_unlock(h_dir, dir, bindex);
+ }
+
+ AuTraceErr(err);
+ return err;
+}
+
+int au_opts_mount(struct super_block *sb, struct au_opts *opts)
+{
+ int err;
+ struct inode *dir;
+ struct au_opt *opt;
+ struct au_opt_xino *opt_xino;
+ aufs_bindex_t bend;
+ struct au_sbinfo *sbinfo;
+ unsigned int tmp;
+
+ AuTraceEnter();
+ SiMustWriteLock(sb);
+ DiMustWriteLock(sb->s_root);
+ dir = sb->s_root->d_inode;
+ IiMustWriteLock(dir);
+
+ err = 0;
+ opt_xino = NULL;
+ opt = opts->opt;
+ while (err >= 0 && opt->type != Opt_tail)
+ err = au_opt_simple(sb, opt++, opts);
+ if (err > 0)
+ err = 0;
+ else if (unlikely(err < 0))
+ goto out;
+
+ /* disable xino, hinotify, dlgt temporary */
+ sbinfo = au_sbi(sb);
+ tmp = sbinfo->si_mntflags;
+ au_opt_clr(sbinfo->si_mntflags, XINO);
+ au_opt_clr(sbinfo->si_mntflags, DLGT);
+ au_opt_set_udba(sbinfo->si_mntflags, UDBA_REVAL);
+
+ opt = opts->opt;
+ while (err >= 0 && opt->type != Opt_tail)
+ err = au_opt_br(sb, opt++, opts);
+ if (err > 0)
+ err = 0;
+ else if (unlikely(err < 0))
+ goto out;
+
+ bend = au_sbend(sb);
+ if (unlikely(bend < 0)) {
+ err = -EINVAL;
+ AuErr("no branches\n");
+ goto out;
+ }
+
+ if (au_opt_test(tmp, XINO))
+ au_opt_set(sbinfo->si_mntflags, XINO);
+ opt = opts->opt;
+ while (!err && opt->type != Opt_tail)
+ err = au_opt_xino(sb, opt++, &opt_xino, opts);
+ if (unlikely(err))
+ goto out;
+
+ //todo: test this error case.
+ err = verify_opts(sb, tmp, /*remount*/0);
+ if (unlikely(err))
+ goto out;
+
+ /* enable xino */
+ if (au_opt_test(tmp, XINO) && !opt_xino) {
+ struct au_opt_xino xino;
+
+ xino.file = au_xino_def(sb);
+ err = PTR_ERR(xino.file);
+ if (IS_ERR(xino.file))
+ goto out;
+
+ err = au_xino_set(sb, &xino, /*remount*/0);
+ fput(xino.file);
+ if (unlikely(err))
+ goto out;
+ }
+
+ /* restore hinotify */
+ sbinfo->si_mntflags &= ~AuOptMask_UDBA;
+ sbinfo->si_mntflags |= (tmp & AuOptMask_UDBA);
+ if (au_opt_test(tmp, UDBA_INOTIFY))
+ au_reset_hinotify(dir, au_hi_flags(dir, 1) & ~AuHi_XINO);
+
+ /* restore dlgt */
+ if (au_opt_test_dlgt(tmp))
+ au_opt_set(sbinfo->si_mntflags, DLGT);
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+int au_opts_remount(struct super_block *sb, struct au_opts *opts)
+{
+ int err, rerr;
+ struct inode *dir;
+ struct au_opt_xino *opt_xino;
+ struct au_opt *opt;
+ unsigned int dlgt;
+ struct au_sbinfo *sbinfo;
+
+ AuTraceEnter();
+ SiMustWriteLock(sb);
+ DiMustWriteLock(sb->s_root);
+ dir = sb->s_root->d_inode;
+ IiMustWriteLock(dir);
+ sbinfo = au_sbi(sb);
+ //AuDebugOn(au_opt_test(sbifno->si_mntflags, UDBA_INOTIFY));
+
+ err = 0;
+ dlgt = !!au_opt_test_dlgt(sbinfo->si_mntflags);
+ opt_xino = NULL;
+ opt = opts->opt;
+ while (err >= 0 && opt->type != Opt_tail) {
+ err = au_opt_simple(sb, opt, opts);
+
+ /* disable it temporary */
+ dlgt = !!au_opt_test_dlgt(sbinfo->si_mntflags);
+ au_opt_clr(sbinfo->si_mntflags, DLGT);
+
+ if (!err)
+ err = au_opt_br(sb, opt, opts);
+ if (!err)
+ err = au_opt_xino(sb, opt, &opt_xino, opts);
+
+ /* restore it */
+ if (unlikely(dlgt))
+ au_opt_set(sbinfo->si_mntflags, DLGT);
+ opt++;
+ }
+ if (err > 0)
+ err = 0;
+ AuTraceErr(err);
+
+ /* go on even err */
+
+ //todo: test this error case.
+ au_opt_clr(sbinfo->si_mntflags, DLGT);
+ rerr = verify_opts(sb, sbinfo->si_mntflags, /*remount*/1);
+ if (unlikely(dlgt))
+ au_opt_set(sbinfo->si_mntflags, DLGT);
+ if (unlikely(rerr && !err))
+ err = rerr;
+
+ if (unlikely(au_ftest_opts(opts->flags, TRUNC_XIB))) {
+ rerr = au_xib_trunc(sb);
+ if (unlikely(rerr && !err))
+ err = rerr;
+ }
+
+ /* they are handled by the caller */
+ if (!au_ftest_opts(opts->flags, REFRESH_DIR)
+ && (opts->given_udba || au_opt_test(sbinfo->si_mntflags, XINO)))
+ au_fset_opts(opts->flags, REFRESH_DIR);
+
+ LKTRTrace("status 0x%x\n", opts->flags);
+ AuTraceErr(err);
+ return err;
+}
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 22/67] aufs workqueue for asynchronous/super-io/delegated operations, header
2008-05-16 14:32 ` [PATCH 21/67] aufs mount options/flags, source hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 23/67] aufs workqueue for asynchronous/super-io/delegated operations, source hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs workqueue for asynchronous/super-io/delegated operations, header
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/wkq.h | 160 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 160 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/wkq.h b/fs/aufs/wkq.h
new file mode 100644
index 0000000..b9e2edd
--- /dev/null
+++ b/fs/aufs/wkq.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * workqueue for asynchronous/super-io/delegated operations
+ *
+ * $Id: wkq.h,v 1.3 2008/04/28 03:18:21 sfjro Exp $
+ */
+
+#ifndef __AUFS_WKQ_H__
+#define __AUFS_WKQ_H__
+
+#ifdef __KERNEL__
+
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/workqueue.h>
+#include <linux/aufs_type.h>
+
+/* ---------------------------------------------------------------------- */
+
+/* internal workqueue named AUFS_WKQ_NAME */
+struct au_wkq {
+ struct workqueue_struct *q;
+
+ /* accounting */
+ atomic_t busy;
+ unsigned int max_busy; // todo: STAT only
+};
+
+/*
+ * in the next operation, wait for the 'nowait' tasks in system-wide workqueue
+ */
+struct au_nowait_tasks {
+#ifdef CONFIG_AUFS_HINOTIFY
+ /*
+ * currently, the 'nowait' task which should be waited in the next
+ * operation is only hinotify.
+ */
+ atomic_t nw_len;
+ wait_queue_head_t nw_wq;
+#endif
+};
+
+/* ---------------------------------------------------------------------- */
+
+extern struct au_wkq *au_wkq;
+typedef void (*au_wkq_func_t)(void *args);
+
+/* wkq flags */
+#define AuWkq_WAIT 1
+#define AuWkq_DLGT (1 << 1)
+#define au_ftest_wkq(flags, name) ((flags) & AuWkq_##name)
+#define au_fset_wkq(flags, name) { (flags) |= AuWkq_##name; }
+#define au_fclr_wkq(flags, name) { (flags) &= ~AuWkq_##name; }
+#ifndef CONFIG_AUFS_DLGT
+#undef AuWkq_DLGT
+#define AuWkq_DLGT 0
+#endif
+
+int au_wkq_run(au_wkq_func_t func, void *args, struct super_block *sb,
+ unsigned int flags);
+int __init au_wkq_init(void);
+void au_wkq_fin(void);
+
+/* ---------------------------------------------------------------------- */
+
+static inline int au_test_wkq(struct task_struct *tsk)
+{
+ return (!tsk->mm && !strcmp(tsk->comm, AUFS_WKQ_NAME));
+#if 0 // per-cpu workqueue
+ return (!tsk->mm
+ && !memcmp(tsk->comm, AUFS_WKQ_NAME "/",
+ sizeof(AUFS_WKQ_NAME)));
+#endif
+}
+
+static inline int au_wkq_wait(au_wkq_func_t func, void *args, int dlgt)
+{
+ unsigned int flags = AuWkq_WAIT;
+ if (unlikely(dlgt))
+ au_fset_wkq(flags, DLGT);
+ return au_wkq_run(func, args, /*sb*/NULL, flags);
+}
+
+static inline int au_wkq_nowait(au_wkq_func_t func, void *args,
+ struct super_block *sb, int dlgt)
+{
+ unsigned int flags = !AuWkq_WAIT;
+ if (unlikely(dlgt))
+ au_fset_wkq(flags, DLGT);
+ return au_wkq_run(func, args, sb, flags);
+}
+
+#ifdef CONFIG_AUFS_HINOTIFY
+//todo: memory barrier?
+static inline void au_nwt_init(struct au_nowait_tasks *nwt)
+{
+ atomic_set(&nwt->nw_len, 0);
+ smp_mb(); /* atomic_set */
+ init_waitqueue_head(&nwt->nw_wq);
+}
+
+static inline int au_nwt_inc(struct au_nowait_tasks *nwt)
+{
+ return atomic_inc_return(&nwt->nw_len);
+}
+
+static inline int au_nwt_dec(struct au_nowait_tasks *nwt)
+{
+ int ret = atomic_dec_return(&nwt->nw_len);
+ if (!ret)
+ wake_up_all(&nwt->nw_wq);
+ return ret;
+}
+
+static inline int au_nwt_flush(struct au_nowait_tasks *nwt)
+{
+ wait_event(nwt->nw_wq, !atomic_read(&nwt->nw_len));
+ return 0;
+}
+#else
+static inline void au_nwt_init(struct au_nowait_tasks *nwt)
+{
+ /* nothing */
+}
+
+static inline int au_nwt_inc(struct au_nowait_tasks *nwt)
+{
+ return 0;
+}
+
+static inline int au_nwt_dec(struct au_nowait_tasks *nwt)
+{
+ return 0;
+}
+
+static inline int au_nwt_flush(struct au_nowait_tasks *nwt)
+{
+ return 0;
+}
+#endif /* CONFIG_AUFS_HINOTIFY */
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_WKQ_H__ */
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 23/67] aufs workqueue for asynchronous/super-io/delegated operations, source
2008-05-16 14:32 ` [PATCH 22/67] aufs workqueue for asynchronous/super-io/delegated operations, header hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 24/67] aufs sub-VFS, header hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs workqueue for asynchronous/super-io/delegated operations, source
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/wkq.c | 321 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 321 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/wkq.c b/fs/aufs/wkq.c
new file mode 100644
index 0000000..965d1e4
--- /dev/null
+++ b/fs/aufs/wkq.c
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * workqueue for asynchronous/super-io/delegated operations
+ *
+ * $Id: wkq.c,v 1.4 2008/05/04 23:53:38 sfjro Exp $
+ */
+
+#include <linux/module.h>
+#include "aufs.h"
+
+struct au_wkq *au_wkq;
+
+struct au_cred {
+#ifdef CONFIG_AUFS_DLGT
+ int umask;
+ uid_t fsuid;
+ gid_t fsgid;
+ kernel_cap_t cap_effective, cap_inheritable, cap_permitted;
+ //unsigned keep_capabilities:1;
+ //struct user_struct *user;
+ //struct fs_struct *fs;
+ //struct nsproxy *nsproxy;
+#endif
+};
+
+struct au_wkinfo {
+ struct work_struct wk;
+ struct vfsmount *mnt;
+
+ unsigned int flags;
+ struct au_cred cred;
+
+ au_wkq_func_t func;
+ void *args;
+
+ atomic_t *busyp;
+ struct completion *comp;
+};
+
+/* ---------------------------------------------------------------------- */
+
+#ifdef CONFIG_AUFS_DLGT
+static void cred_store(struct au_cred *cred)
+{
+ cred->umask = current->fs->umask;
+ cred->fsuid = current->fsuid;
+ cred->fsgid = current->fsgid;
+ cred->cap_effective = current->cap_effective;
+ cred->cap_inheritable = current->cap_inheritable;
+ cred->cap_permitted = current->cap_permitted;
+}
+
+static void cred_revert(struct au_cred *cred)
+{
+ AuDebugOn(!au_test_wkq(current));
+ current->fs->umask = cred->umask;
+ current->fsuid = cred->fsuid;
+ current->fsgid = cred->fsgid;
+ current->cap_effective = cred->cap_effective;
+ current->cap_inheritable = cred->cap_inheritable;
+ current->cap_permitted = cred->cap_permitted;
+}
+
+static void cred_switch(struct au_cred *old, struct au_cred *new)
+{
+ cred_store(old);
+ cred_revert(new);
+}
+
+static void dlgt_cred_store(unsigned int flags, struct au_wkinfo *wkinfo)
+{
+ if (unlikely(au_ftest_wkq(flags, DLGT)))
+ cred_store(&wkinfo->cred);
+}
+
+static void dlgt_func(struct au_wkinfo *wkinfo)
+{
+ if (!au_ftest_wkq(wkinfo->flags, DLGT))
+ wkinfo->func(wkinfo->args);
+ else {
+ struct au_cred cred;
+ cred_switch(&cred, &wkinfo->cred);
+ wkinfo->func(wkinfo->args);
+ cred_revert(&cred);
+ }
+}
+#else
+static void dlgt_cred_store(unsigned int flags, struct au_wkinfo *wkinfo)
+{
+ /* empty */
+}
+
+static void dlgt_func(struct au_wkinfo *wkinfo)
+{
+ wkinfo->func(wkinfo->args);
+}
+#endif /* CONFIG_AUFS_DLGT */
+
+/* ---------------------------------------------------------------------- */
+
+static void update_busy(struct au_wkq *wkq, struct au_wkinfo *wkinfo)
+{
+#ifdef CONFIG_AUFS_STAT
+ unsigned int new, old;
+
+ do {
+ new = atomic_read(wkinfo->busyp);
+ old = wkq->max_busy;
+ if (new <= old)
+ break;
+ } while (cmpxchg(&wkq->max_busy, old, new) == old);
+#endif
+}
+
+static int enqueue(struct au_wkq *wkq, struct au_wkinfo *wkinfo)
+{
+ AuTraceEnter();
+ wkinfo->busyp = &wkq->busy;
+ update_busy(wkq, wkinfo);
+ if (au_ftest_wkq(wkinfo->flags, WAIT))
+ return !queue_work(wkq->q, &wkinfo->wk);
+ else
+ return !schedule_work(&wkinfo->wk);
+}
+
+static void do_wkq(struct au_wkinfo *wkinfo)
+{
+ unsigned int idle, n;
+ int i, idle_idx;
+
+ AuTraceEnter();
+
+ while (1) {
+ if (au_ftest_wkq(wkinfo->flags, WAIT)) {
+ idle_idx = 0;
+ idle = UINT_MAX;
+ for (i = 0; i < aufs_nwkq; i++) {
+ n = atomic_inc_return(&au_wkq[i].busy);
+ if (n == 1 && !enqueue(au_wkq + i, wkinfo))
+ return; /* success */
+
+ if (n < idle) {
+ idle_idx = i;
+ idle = n;
+ }
+ atomic_dec_return(&au_wkq[i].busy);
+ }
+ } else
+ idle_idx = aufs_nwkq;
+
+ atomic_inc_return(&au_wkq[idle_idx].busy);
+ if (!enqueue(au_wkq + idle_idx, wkinfo))
+ return; /* success */
+
+ /* impossible? */
+ AuWarn1("failed to queue_work()\n");
+ yield();
+ }
+}
+
+static void wkq_func(struct work_struct *wk)
+{
+ struct au_wkinfo *wkinfo = container_of(wk, struct au_wkinfo, wk);
+
+ LKTRTrace("wkinfo{0x%x, %p, %p, %p}\n",
+ wkinfo->flags, wkinfo->func, wkinfo->busyp, wkinfo->comp);
+
+ dlgt_func(wkinfo);
+ atomic_dec_return(wkinfo->busyp);
+ if (au_ftest_wkq(wkinfo->flags, WAIT))
+ complete(wkinfo->comp);
+ else {
+#if 0 // rfu
+ struct au_sbinfo *sbinfo;
+ sbinfo = au_sbi(wkinfo->mnt->mnt_sb);
+ if (atomic_dec_and_test(&sbinfo->si_wkq_nowait))
+ wake_up_all(&sbinfo->si_wkq_nowait_wq);
+#endif
+ mntput(wkinfo->mnt);
+ module_put(THIS_MODULE);
+ kfree(wkinfo);
+ }
+}
+
+int au_wkq_run(au_wkq_func_t func, void *args, struct super_block *sb,
+ unsigned int flags)
+{
+ int err;
+ //int queued;
+ //struct workqueue_struct *wkq;
+ DECLARE_COMPLETION_ONSTACK(comp);
+ struct au_wkinfo _wkinfo = {
+ //.sb = sb,
+ .flags = flags,
+ .func = func,
+ .args = args,
+ .comp = &comp
+ }, *wkinfo = &_wkinfo;
+
+ LKTRTrace("0x%x\n", flags);
+ AuDebugOn(au_test_wkq(current));
+
+ err = 0;
+ if (unlikely(!au_ftest_wkq(flags, WAIT))) {
+ AuDebugOn(!sb);
+ /*
+ * wkq_func() must free this wkinfo.
+ * it highly depends upon the implementation of workqueue.
+ */
+ err = -ENOMEM;
+ wkinfo = kmalloc(sizeof(*wkinfo), GFP_TEMPORARY);
+ if (unlikely(!wkinfo))
+ goto out;
+ err = 0;
+ /* prohibit umount */
+ wkinfo->mnt = au_mntcache_get(sb);
+ wkinfo->flags = flags;
+ wkinfo->func = func;
+ wkinfo->args = args;
+ wkinfo->comp = NULL;
+ __module_get(THIS_MODULE);
+ }
+
+ INIT_WORK(&wkinfo->wk, wkq_func);
+ dlgt_cred_store(flags, wkinfo);
+ do_wkq(wkinfo);
+ if (au_ftest_wkq(flags, WAIT))
+ /* no timeout, no interrupt */
+ wait_for_completion(wkinfo->comp);
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+void au_wkq_fin(void)
+{
+ int i;
+
+ AuTraceEnter();
+
+ for (i = 0; i < aufs_nwkq; i++)
+ if (au_wkq[i].q && !IS_ERR(au_wkq[i].q))
+ destroy_workqueue(au_wkq[i].q);
+ kfree(au_wkq);
+}
+
+int __init au_wkq_init(void)
+{
+ int err, i;
+ struct au_wkq *nowaitq;
+
+ LKTRTrace("%d\n", aufs_nwkq);
+
+ /* '+1' is for accounting of nowait queue */
+ err = -ENOMEM;
+ au_wkq = kcalloc(aufs_nwkq + 1, sizeof(*au_wkq), GFP_KERNEL);
+ if (unlikely(!au_wkq))
+ goto out;
+
+ err = 0;
+ for (i = 0; i < aufs_nwkq; i++) {
+ au_wkq[i].q = create_singlethread_workqueue(AUFS_WKQ_NAME);
+ if (au_wkq[i].q && !IS_ERR(au_wkq[i].q)) {
+ atomic_set(&au_wkq[i].busy, 0);
+ au_wkq[i].max_busy = 0;
+ continue;
+ }
+
+ err = PTR_ERR(au_wkq[i].q);
+ au_wkq_fin();
+ break;
+ }
+
+ /* nowait accounting */
+ nowaitq = au_wkq + aufs_nwkq;
+ atomic_set(&nowaitq->busy, 0);
+ nowaitq->max_busy = 0;
+ nowaitq->q = NULL;
+ //smp_mb(); /* atomic_set */
+
+#if 0 // test accouting
+ if (!err) {
+ static void f(void *args)
+ {
+ DbgSleep(1);
+ }
+ int i;
+ //au_debug_on();
+ LKTRTrace("f %p\n", f);
+ for (i = 0; i < 10; i++)
+ au_wkq_nowait(f, NULL, 0);
+ for (i = 0; i < aufs_nwkq; i++)
+ au_wkq_wait(f, NULL, 0);
+ DbgSleep(11);
+ //au_debug_off();
+ }
+#endif
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 24/67] aufs sub-VFS, header
2008-05-16 14:32 ` [PATCH 23/67] aufs workqueue for asynchronous/super-io/delegated operations, source hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 25/67] aufs sub-VFS, source hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs sub-VFS, header
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/vfsub.h | 493 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 493 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/vfsub.h b/fs/aufs/vfsub.h
new file mode 100644
index 0000000..a7852bb
--- /dev/null
+++ b/fs/aufs/vfsub.h
@@ -0,0 +1,493 @@
+/*
+ * Copyright (C) 2007-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * sub-routines for VFS
+ *
+ * $Id: vfsub.h,v 1.2 2008/04/21 01:49:22 sfjro Exp $
+ */
+
+#ifndef __AUFS_VFSUB_H__
+#define __AUFS_VFSUB_H__
+
+#ifdef __KERNEL__
+
+#include <linux/fs.h>
+#include <linux/namei.h>
+#include <linux/splice.h>
+#include <linux/inotify.h>
+
+/* ---------------------------------------------------------------------- */
+
+/* vfsub flags */
+#define Vfsub_DLGT 1 /* operation with delegation */
+#define Vfsub_FORCE_UNLINK (1 << 1) /* force unlinking */
+#define vfsub_ftest(flags, name) ((flags) & Vfsub_##name)
+#define vfsub_fset(flags, name) { (flags) |= Vfsub_##name; }
+#define vfsub_fclr(flags, name) { (flags) &= ~Vfsub_##name; }
+#ifndef CONFIG_AUFS_DLGT
+#undef Vfsub_DLGT
+#define Vfsub_DLGT 0
+#endif
+
+struct au_hin_ignore;
+struct vfsub_args {
+#ifdef CONFIG_AUFS_HINOTIFY
+ /* inotify events to be ignored */
+ int nignore;
+ struct au_hin_ignore *ignore;
+#endif
+
+ unsigned int flags;
+};
+
+struct au_hinode;
+#ifdef CONFIG_AUFS_HINOTIFY
+static inline
+void do_vfsub_args_reinit(struct vfsub_args *vargs, struct au_hin_ignore *ign)
+{
+ vargs->nignore = 0;
+ vargs->ignore = ign;
+}
+
+static inline void vfsub_args_reinit(struct vfsub_args *vargs)
+{
+ vargs->nignore = 0;
+}
+
+__u32 vfsub_events_notify_change(struct iattr *ia);
+void vfsub_ign_hinode(struct vfsub_args *vargs, __u32 events,
+ struct au_hinode *hinode);
+void vfsub_ign_inode(struct vfsub_args *vargs, __u32 events,
+ struct inode *inode, struct inode *h_inode);
+
+void vfsub_ignore(struct vfsub_args *vargs);
+void vfsub_unignore(struct vfsub_args *vargs);
+#else
+static inline
+void do_vfsub_args_reinit(struct vfsub_args *vargs, struct au_hin_ignore *ign)
+{
+ /* empty */
+}
+
+static inline void vfsub_args_reinit(struct vfsub_args *vargs)
+{
+ /* empty */
+}
+
+static inline __u32 vfsub_events_notify_change(struct iattr *ia)
+{
+ return 0;
+}
+
+static inline void vfsub_ign_hinode(struct vfsub_args *vargs, __u32 events,
+ struct au_hinode *hinode)
+{
+ /* empty */
+}
+
+static inline void vfsub_ign_inode(struct vfsub_args *vargs, __u32 events,
+ struct inode *inode, struct inode *h_inode)
+{
+ /* empty */
+}
+
+static inline void vfsub_ignore(struct vfsub_args *vargs)
+{
+ /* empty */
+}
+
+static inline void vfsub_unignore(struct vfsub_args *vargs)
+{
+ /* empty */
+}
+#endif /* CONFIG_AUFS_HINOTIFY */
+
+void vfsub_args_init(struct vfsub_args *vargs, struct au_hin_ignore *ign,
+ int dlgt, int force_unlink);
+
+/* ---------------------------------------------------------------------- */
+
+/* inotify_inode_watched() is not exported */
+static inline int au_test_inotify(struct inode *inode)
+{
+#ifdef CONFIG_INOTIFY
+ return !list_empty(&inode->inotify_watches);
+#endif
+ return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/* lock subclass for hidden inode */
+/* default MAX_LOCKDEP_SUBCLASSES(8) is not enough */
+// todo: reduce it
+enum {
+ AuLsc_I_Begin = I_MUTEX_QUOTA, /* 4 */
+ AuLsc_I_PARENT, /* hidden inode, parent first */
+ AuLsc_I_CHILD,
+ AuLsc_I_PARENT2, /* copyup dirs */
+ AuLsc_I_CHILD2,
+ AuLsc_I_End
+};
+
+#define IMustLock(i) MtxMustLock(&(i)->i_mutex)
+
+static inline
+struct dentry *vfsub_lock_rename(struct dentry *d1, struct dentry *d2)
+{
+ struct dentry *d;
+
+ lockdep_off();
+ d = lock_rename(d1, d2);
+ lockdep_on();
+ return d;
+}
+
+static inline void vfsub_unlock_rename(struct dentry *d1, struct dentry *d2)
+{
+ lockdep_off();
+ unlock_rename(d1, d2);
+ lockdep_on();
+}
+
+/* ---------------------------------------------------------------------- */
+
+#ifdef CONFIG_AUFS_WORKAROUND_FUSE
+/* br_fuse.c */
+int au_update_fuse_h_inode(struct vfsmount *h_mnt, struct dentry *h_dentry);
+#else
+static inline
+int au_update_fuse_h_inode(struct vfsmount *h_mnt, struct dentry *h_dentry)
+{
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_AUFS_BR_XFS
+/* br_xfs.c */
+dev_t au_h_rdev(struct inode *h_inode, struct vfsmount *h_mnt,
+ struct dentry *h_dentry);
+#else
+static inline
+dev_t au_h_rdev(struct inode *h_inode, struct vfsmount *h_mnt,
+ struct dentry *h_dentry)
+{
+ return h_inode->i_rdev;
+}
+#endif
+
+/* simple abstractions, for future use */
+static inline
+int do_vfsub_permission(struct inode *inode, int mask, struct nameidata *nd)
+{
+ LKTRTrace("i%lu, mask 0x%x, nd %d\n", inode->i_ino, mask, !!nd);
+ IMustLock(inode);
+ return permission(inode, mask, nd);
+}
+
+/* ---------------------------------------------------------------------- */
+
+struct file *vfsub_filp_open(const char *path, int oflags, int mode);
+int vfsub_path_lookup(const char *name, unsigned int flags,
+ struct nameidata *nd);
+struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent,
+ int len);
+
+#ifdef CONFIG_AUFS_LHASH_PATCH
+struct dentry *vfsub__lookup_hash(struct qstr *name, struct dentry *parent,
+ struct nameidata *nd);
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+int do_vfsub_create(struct inode *dir, struct dentry *dentry, int mode,
+ struct nameidata *nd);
+int do_vfsub_symlink(struct inode *dir, struct dentry *dentry,
+ const char *symname, int mode);
+int do_vfsub_mknod(struct inode *dir, struct dentry *dentry, int mode,
+ dev_t dev);
+int do_vfsub_link(struct dentry *src_dentry, struct inode *dir,
+ struct dentry *dentry);
+int do_vfsub_rename(struct inode *src_dir, struct dentry *src_dentry,
+ struct inode *dir, struct dentry *dentry);
+int do_vfsub_mkdir(struct inode *dir, struct dentry *dentry, int mode);
+int do_vfsub_rmdir(struct inode *dir, struct dentry *dentry);
+int do_vfsub_unlink(struct inode *dir, struct dentry *dentry);
+
+/* ---------------------------------------------------------------------- */
+
+ssize_t do_vfsub_read_u(struct file *file, char __user *ubuf, size_t count,
+ loff_t *ppos);
+// kernel_read() ??
+ssize_t do_vfsub_read_k(struct file *file, void *kbuf, size_t count,
+ loff_t *ppos);
+ssize_t do_vfsub_write_u(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *ppos);
+ssize_t do_vfsub_write_k(struct file *file, void *kbuf, size_t count,
+ loff_t *ppos);
+int do_vfsub_readdir(struct file *file, filldir_t filldir, void *arg);
+
+/* ---------------------------------------------------------------------- */
+
+#ifdef CONFIG_AUFS_SPLICE_PATCH
+long do_vfsub_splice_to(struct file *in, loff_t *ppos,
+ struct pipe_inode_info *pipe, size_t len,
+ unsigned int flags);
+long do_vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out,
+ loff_t *ppos, size_t len, unsigned int flags);
+#else
+static inline
+long do_vfsub_splice_to(struct file *in, loff_t *ppos,
+ struct pipe_inode_info *pipe, size_t len,
+ unsigned int flags)
+{
+ return -ENOSYS;
+}
+
+static inline
+long do_vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out,
+ loff_t *ppos, size_t len, unsigned int flags)
+{
+ return -ENOSYS;
+}
+#endif /* CONFIG_AUFS_SPLICE_PATCH */
+
+/* ---------------------------------------------------------------------- */
+
+static inline loff_t vfsub_llseek(struct file *file, loff_t offset, int origin)
+{
+ loff_t err;
+
+ LKTRTrace("%.*s\n", AuDLNPair(file->f_dentry));
+
+ lockdep_off();
+ err = vfs_llseek(file, offset, origin);
+ lockdep_on();
+ return err;
+}
+
+static inline int do_vfsub_getattr(struct vfsmount *mnt, struct dentry *dentry,
+ struct kstat *st)
+{
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+ return vfs_getattr(mnt, dentry, st);
+}
+
+/* ---------------------------------------------------------------------- */
+
+#if defined(CONFIG_AUFS_HINOTIFY) || defined(CONFIG_AUFS_DLGT)
+/* hin_or_dlgt.c */
+int vfsub_permission(struct inode *inode, int mask, struct nameidata *nd,
+ int dlgt);
+
+int vfsub_create(struct inode *dir, struct dentry *dentry, int mode,
+ struct nameidata *nd, int dlgt);
+int vfsub_symlink(struct inode *dir, struct dentry *dentry, const char *symname,
+ int mode, int dlgt);
+int vfsub_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev,
+ int dlgt);
+int vfsub_link(struct dentry *src_dentry, struct inode *dir,
+ struct dentry *dentry, int dlgt);
+int vfsub_rename(struct inode *src_dir, struct dentry *src_dentry,
+ struct inode *dir, struct dentry *dentry,
+ struct vfsub_args *vargs);
+int vfsub_mkdir(struct inode *dir, struct dentry *dentry, int mode, int dlgt);
+int vfsub_rmdir(struct inode *dir, struct dentry *dentry,
+ struct vfsub_args *vargs);
+
+ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count,
+ loff_t *ppos, int dlgt);
+ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count, loff_t *ppos,
+ int dlgt);
+ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count,
+ loff_t *ppos, struct vfsub_args *vargs);
+ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, loff_t *ppos,
+ struct vfsub_args *vargs);
+int vfsub_readdir(struct file *file, filldir_t filldir, void *arg, int dlgt);
+long vfsub_splice_to(struct file *in, loff_t *ppos,
+ struct pipe_inode_info *pipe, size_t len,
+ unsigned int flags, int dlgt);
+long vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out,
+ loff_t *ppos, size_t len, unsigned int flags,
+ struct vfsub_args *vargs);
+
+int vfsub_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *st,
+ int dlgt);
+#else
+
+static inline
+int vfsub_permission(struct inode *inode, int mask, struct nameidata *nd,
+ int dlgt)
+{
+ return do_vfsub_permission(inode, mask, nd);
+}
+
+static inline
+int vfsub_create(struct inode *dir, struct dentry *dentry, int mode,
+ struct nameidata *nd, int dlgt)
+{
+ return do_vfsub_create(dir, dentry, mode, nd);
+}
+
+static inline
+int vfsub_symlink(struct inode *dir, struct dentry *dentry, const char *symname,
+ int mode, int dlgt)
+{
+ return do_vfsub_symlink(dir, dentry, symname, mode);
+}
+
+static inline
+int vfsub_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev,
+ int dlgt)
+{
+ return do_vfsub_mknod(dir, dentry, mode, dev);
+}
+
+static inline
+int vfsub_link(struct dentry *src_dentry, struct inode *dir,
+ struct dentry *dentry, int dlgt)
+{
+ return do_vfsub_link(src_dentry, dir, dentry);
+}
+
+static inline
+int vfsub_rename(struct inode *src_dir, struct dentry *src_dentry,
+ struct inode *dir, struct dentry *dentry,
+ struct vfsub_args *vargs)
+{
+ int err;
+
+ vfsub_ignore(vargs);
+ err = do_vfsub_rename(src_dir, src_dentry, dir, dentry);
+ if (unlikely(err))
+ vfsub_unignore(vargs);
+ return err;
+}
+
+static inline
+int vfsub_mkdir(struct inode *dir, struct dentry *dentry, int mode, int dlgt)
+{
+ return do_vfsub_mkdir(dir, dentry, mode);
+}
+
+static inline
+int vfsub_rmdir(struct inode *dir, struct dentry *dentry,
+ struct vfsub_args *vargs)
+{
+ int err;
+
+ vfsub_ignore(vargs);
+ err = do_vfsub_rmdir(dir, dentry);
+ if (unlikely(err))
+ vfsub_unignore(vargs);
+ return err;
+}
+
+static inline
+ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count,
+ loff_t *ppos, int dlgt)
+{
+ return do_vfsub_read_u(file, ubuf, count, ppos);
+}
+
+static inline
+ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count, loff_t *ppos,
+ int dlgt)
+{
+ return do_vfsub_read_k(file, kbuf, count, ppos);
+}
+
+static inline
+ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count,
+ loff_t *ppos, struct vfsub_args *vargs)
+{
+ int err;
+
+ vfsub_ignore(vargs);
+ err = do_vfsub_write_u(file, ubuf, count, ppos);
+ if (unlikely(err < 0))
+ vfsub_unignore(vargs);
+ return err;
+}
+
+static inline
+ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, loff_t *ppos,
+ struct vfsub_args *vargs)
+{
+ int err;
+
+ vfsub_ignore(vargs);
+ err = do_vfsub_write_k(file, kbuf, count, ppos);
+ if (unlikely(err < 0))
+ vfsub_unignore(vargs);
+ return err;
+}
+
+static inline
+int vfsub_readdir(struct file *file, filldir_t filldir, void *arg, int dlgt)
+{
+ return do_vfsub_readdir(file, filldir, arg);
+}
+
+static inline
+long vfsub_splice_to(struct file *in, loff_t *ppos,
+ struct pipe_inode_info *pipe, size_t len,
+ unsigned int flags, int dlgt)
+{
+ return do_vfsub_splice_to(in, ppos, pipe, len, flags);
+}
+
+static inline
+long vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out,
+ loff_t *ppos, size_t len, unsigned int flags,
+ struct vfsub_args *vargs)
+{
+ long err;
+
+ vfsub_ignore(vargs);
+ err = do_vfsub_splice_from(pipe, out, ppos, len, flags);
+ if (unlikely(err < 0))
+ vfsub_unignore(vargs);
+ return err;
+}
+
+static inline
+int vfsub_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *st,
+ int dlgt)
+{
+ return do_vfsub_getattr(mnt, dentry, st);
+}
+#endif /* CONFIG_AUFS_DLGT || CONFIG_AUFS_HINOTIFY */
+
+/* ---------------------------------------------------------------------- */
+
+int vfsub_sio_mkdir(struct inode *dir, struct dentry *dentry, int mode,
+ int dlgt);
+int vfsub_sio_rmdir(struct inode *dir, struct dentry *dentry, int dlgt);
+
+/* ---------------------------------------------------------------------- */
+
+int vfsub_notify_change(struct dentry *dentry, struct iattr *ia,
+ struct vfsub_args *vargs);
+int vfsub_unlink(struct inode *dir, struct dentry *dentry,
+ struct vfsub_args *vargs);
+int vfsub_statfs(void *arg, struct kstatfs *buf, int dlgt);
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_VFSUB_H__ */
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 25/67] aufs sub-VFS, source
2008-05-16 14:32 ` [PATCH 24/67] aufs sub-VFS, header hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 26/67] aufs sub-dcache, header hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs sub-VFS, source
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/vfsub.c | 653 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 653 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/vfsub.c b/fs/aufs/vfsub.c
new file mode 100644
index 0000000..88d1ca0
--- /dev/null
+++ b/fs/aufs/vfsub.c
@@ -0,0 +1,653 @@
+/*
+ * Copyright (C) 2007-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * sub-routines for VFS
+ *
+ * $Id: vfsub.c,v 1.3 2008/05/12 00:29:11 sfjro Exp $
+ */
+// I'm going to slightly mad
+
+#include <linux/uaccess.h>
+#include "aufs.h"
+
+/* ---------------------------------------------------------------------- */
+
+void vfsub_args_init(struct vfsub_args *vargs, struct au_hin_ignore *ign,
+ int dlgt, int force_unlink)
+{
+ do_vfsub_args_reinit(vargs, ign);
+ vargs->flags = 0;
+ if (unlikely(dlgt))
+ vfsub_fset(vargs->flags, DLGT);
+ if (force_unlink)
+ vfsub_fset(vargs->flags, FORCE_UNLINK);
+}
+
+/* ---------------------------------------------------------------------- */
+
+struct file *vfsub_filp_open(const char *path, int oflags, int mode)
+{
+ struct file *err;
+
+ LKTRTrace("%s\n", path);
+
+ lockdep_off();
+ err = filp_open(path, oflags, mode);
+ lockdep_on();
+ if (!IS_ERR(err))
+ au_update_fuse_h_inode(err->f_vfsmnt, err->f_dentry); /*ignore*/
+ return err;
+}
+
+int vfsub_path_lookup(const char *name, unsigned int flags,
+ struct nameidata *nd)
+{
+ int err;
+
+ LKTRTrace("%s\n", name);
+
+ /* lockdep_off(); */
+ err = path_lookup(name, flags, nd);
+ /* lockdep_on(); */
+ if (!err)
+ au_update_fuse_h_inode(nd->path.mnt, nd->path.dentry);
+ /*ignore*/
+ return err;
+}
+
+struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent,
+ int len)
+{
+ struct dentry *d;
+
+ LKTRTrace("%.*s/%.*s\n", AuDLNPair(parent), len, name);
+ IMustLock(parent->d_inode);
+
+ d = lookup_one_len(name, parent, len);
+ if (!IS_ERR(d))
+ au_update_fuse_h_inode(NULL, d); /*ignore*/
+ return d;
+}
+
+#ifdef CONFIG_AUFS_LHASH_PATCH
+struct dentry *vfsub__lookup_hash(struct qstr *name, struct dentry *parent,
+ struct nameidata *nd)
+{
+ struct dentry *d;
+
+ LKTRTrace("%.*s/%.*s, nd %d\n",
+ AuDLNPair(parent), AuLNPair(name), !!nd);
+ if (nd)
+ LKTRTrace("nd{0x%x}\n", nd->flags);
+ IMustLock(parent->d_inode);
+
+ d = __lookup_hash(name, parent, nd);
+ if (!IS_ERR(d))
+ au_update_fuse_h_inode(NULL, d); /*ignore*/
+ return d;
+}
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+int do_vfsub_create(struct inode *dir, struct dentry *dentry, int mode,
+ struct nameidata *nd)
+{
+ int err;
+ struct vfsmount *mnt;
+
+ LKTRTrace("i%lu, %.*s, 0x%x\n", dir->i_ino, AuDLNPair(dentry), mode);
+ IMustLock(dir);
+
+ err = vfs_create(dir, dentry, mode, nd);
+ if (!err) {
+ mnt = NULL;
+ if (nd)
+ mnt = nd->path.mnt;
+ /* dir inode is locked */
+ au_update_fuse_h_inode(mnt, dentry->d_parent); /*ignore*/
+ au_update_fuse_h_inode(mnt, dentry); /*ignore*/
+ }
+ return err;
+}
+
+int do_vfsub_symlink(struct inode *dir, struct dentry *dentry,
+ const char *symname, int mode)
+{
+ int err;
+
+ LKTRTrace("i%lu, %.*s, %s, 0x%x\n",
+ dir->i_ino, AuDLNPair(dentry), symname, mode);
+ IMustLock(dir);
+
+ err = vfs_symlink(dir, dentry, symname, mode);
+ if (!err) {
+ /* dir inode is locked */
+ au_update_fuse_h_inode(NULL, dentry->d_parent); /*ignore*/
+ au_update_fuse_h_inode(NULL, dentry); /*ignore*/
+ }
+ return err;
+}
+
+int do_vfsub_mknod(struct inode *dir, struct dentry *dentry, int mode,
+ dev_t dev)
+{
+ int err;
+
+ LKTRTrace("i%lu, %.*s, 0x%x\n", dir->i_ino, AuDLNPair(dentry), mode);
+ IMustLock(dir);
+
+ err = vfs_mknod(dir, dentry, mode, dev);
+ if (!err) {
+ /* dir inode is locked */
+ au_update_fuse_h_inode(NULL, dentry->d_parent); /*ignore*/
+ au_update_fuse_h_inode(NULL, dentry); /*ignore*/
+ }
+ return err;
+}
+
+int do_vfsub_link(struct dentry *src_dentry, struct inode *dir,
+ struct dentry *dentry)
+{
+ int err;
+
+ LKTRTrace("%.*s, i%lu, %.*s\n",
+ AuDLNPair(src_dentry), dir->i_ino, AuDLNPair(dentry));
+ IMustLock(dir);
+
+ lockdep_off();
+ err = vfs_link(src_dentry, dir, dentry);
+ lockdep_on();
+ if (!err) {
+ LKTRTrace("src_i %p, dst_i %p\n",
+ src_dentry->d_inode, dentry->d_inode);
+ /* fuse has different memory inode for the same inumber */
+ au_update_fuse_h_inode(NULL, src_dentry); /*ignore*/
+ /* dir inode is locked */
+ au_update_fuse_h_inode(NULL, dentry->d_parent); /*ignore*/
+ au_update_fuse_h_inode(NULL, dentry); /*ignore*/
+ }
+ return err;
+}
+
+int do_vfsub_rename(struct inode *src_dir, struct dentry *src_dentry,
+ struct inode *dir, struct dentry *dentry)
+{
+ int err;
+
+ LKTRTrace("i%lu, %.*s, i%lu, %.*s\n",
+ src_dir->i_ino, AuDLNPair(src_dentry),
+ dir->i_ino, AuDLNPair(dentry));
+ IMustLock(dir);
+ IMustLock(src_dir);
+
+ lockdep_off();
+ err = vfs_rename(src_dir, src_dentry, dir, dentry);
+ lockdep_on();
+ if (!err) {
+ /* dir inode is locked */
+ au_update_fuse_h_inode(NULL, dentry->d_parent); /*ignore*/
+ au_update_fuse_h_inode(NULL, src_dentry->d_parent); /*ignore*/
+ au_update_fuse_h_inode(NULL, src_dentry); /*ignore*/
+ }
+ return err;
+}
+
+int do_vfsub_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+ int err;
+
+ LKTRTrace("i%lu, %.*s, 0x%x\n", dir->i_ino, AuDLNPair(dentry), mode);
+ IMustLock(dir);
+
+ err = vfs_mkdir(dir, dentry, mode);
+ if (!err) {
+ /* dir inode is locked */
+ au_update_fuse_h_inode(NULL, dentry->d_parent); /*ignore*/
+ au_update_fuse_h_inode(NULL, dentry); /*ignore*/
+ }
+ return err;
+}
+
+int do_vfsub_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ int err;
+
+ LKTRTrace("i%lu, %.*s\n", dir->i_ino, AuDLNPair(dentry));
+ IMustLock(dir);
+
+ lockdep_off();
+ err = vfs_rmdir(dir, dentry);
+ lockdep_on();
+ /* dir inode is locked */
+ if (!err)
+ au_update_fuse_h_inode(NULL, dentry->d_parent); /*ignore*/
+ return err;
+}
+
+int do_vfsub_unlink(struct inode *dir, struct dentry *dentry)
+{
+ int err;
+
+ LKTRTrace("i%lu, %.*s\n", dir->i_ino, AuDLNPair(dentry));
+ IMustLock(dir);
+
+ /* vfs_unlink() locks inode */
+ lockdep_off();
+ err = vfs_unlink(dir, dentry);
+ lockdep_on();
+ /* dir inode is locked */
+ if (!err)
+ au_update_fuse_h_inode(NULL, dentry->d_parent); /*ignore*/
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+ssize_t do_vfsub_read_u(struct file *file, char __user *ubuf, size_t count,
+ loff_t *ppos)
+{
+ ssize_t err;
+
+ LKTRTrace("%.*s, cnt %lu, pos %Ld\n",
+ AuDLNPair(file->f_dentry), (unsigned long)count, *ppos);
+
+ if (0 /*!au_test_nfs(file->f_vfsmnt->mnt_sb)*/)
+ err = vfs_read(file, ubuf, count, ppos);
+ else {
+ lockdep_off();
+ err = vfs_read(file, ubuf, count, ppos);
+ lockdep_on();
+ }
+ if (err >= 0)
+ au_update_fuse_h_inode(file->f_vfsmnt, file->f_dentry);
+ /*ignore*/
+ return err;
+}
+
+// kernel_read() ??
+ssize_t do_vfsub_read_k(struct file *file, void *kbuf, size_t count,
+ loff_t *ppos)
+{
+ ssize_t err;
+ mm_segment_t oldfs;
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ err = do_vfsub_read_u(file, (char __user *)kbuf, count, ppos);
+ set_fs(oldfs);
+ return err;
+}
+
+ssize_t do_vfsub_write_u(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ ssize_t err;
+
+ LKTRTrace("%.*s, cnt %lu, pos %Ld\n",
+ AuDLNPair(file->f_dentry), (unsigned long)count, *ppos);
+
+ lockdep_off();
+ err = vfs_write(file, ubuf, count, ppos);
+ lockdep_on();
+ if (err >= 0)
+ au_update_fuse_h_inode(file->f_vfsmnt, file->f_dentry);
+ /*ignore*/
+ return err;
+}
+
+ssize_t do_vfsub_write_k(struct file *file, void *kbuf, size_t count,
+ loff_t *ppos)
+{
+ ssize_t err;
+ mm_segment_t oldfs;
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ err = do_vfsub_write_u(file, (const char __user *)kbuf, count, ppos);
+ set_fs(oldfs);
+ return err;
+}
+
+int do_vfsub_readdir(struct file *file, filldir_t filldir, void *arg)
+{
+ int err;
+
+ LKTRTrace("%.*s\n", AuDLNPair(file->f_dentry));
+
+ lockdep_off();
+ err = vfs_readdir(file, filldir, arg);
+ lockdep_on();
+ if (err >= 0)
+ au_update_fuse_h_inode(file->f_vfsmnt, file->f_dentry);
+ /*ignore*/
+ return err;
+}
+
+#ifdef CONFIG_AUFS_SPLICE_PATCH
+long do_vfsub_splice_to(struct file *in, loff_t *ppos,
+ struct pipe_inode_info *pipe, size_t len,
+ unsigned int flags)
+{
+ long err;
+
+ LKTRTrace("%.*s, pos %Ld, len %lu, 0x%x\n",
+ AuDLNPair(in->f_dentry), *ppos, (unsigned long)len, flags);
+
+ lockdep_off();
+ err = vfs_splice_to(in, ppos, pipe, len, flags);
+ lockdep_on();
+ if (err >= 0)
+ au_update_fuse_h_inode(in->f_vfsmnt, in->f_dentry); /*ignore*/
+ return err;
+}
+
+long do_vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out,
+ loff_t *ppos, size_t len, unsigned int flags)
+{
+ long err;
+
+ LKTRTrace("%.*s, pos %Ld, len %lu, 0x%x\n",
+ AuDLNPair(out->f_dentry), *ppos, (unsigned long)len, flags);
+
+ lockdep_off();
+ err = vfs_splice_from(pipe, out, ppos, len, flags);
+ lockdep_on();
+ if (err >= 0)
+ au_update_fuse_h_inode(out->f_vfsmnt, out->f_dentry); /*ignore*/
+ return err;
+}
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+struct au_vfsub_mkdir_args {
+ int *errp;
+ struct inode *dir;
+ struct dentry *dentry;
+ int mode;
+ int dlgt;
+};
+
+static void au_call_vfsub_mkdir(void *args)
+{
+ struct au_vfsub_mkdir_args *a = args;
+ *a->errp = vfsub_mkdir(a->dir, a->dentry, a->mode, a->dlgt);
+}
+
+int vfsub_sio_mkdir(struct inode *dir, struct dentry *dentry, int mode,
+ int dlgt)
+{
+ int err, do_sio, wkq_err;
+
+ LKTRTrace("i%lu, %.*s\n", dir->i_ino, AuDLNPair(dentry));
+
+ do_sio = au_test_h_perm_sio(dir, MAY_EXEC | MAY_WRITE, dlgt);
+ if (!do_sio)
+ err = vfsub_mkdir(dir, dentry, mode, dlgt);
+ else {
+ struct au_vfsub_mkdir_args args = {
+ .errp = &err,
+ .dir = dir,
+ .dentry = dentry,
+ .mode = mode,
+ .dlgt = dlgt
+ };
+ wkq_err = au_wkq_wait(au_call_vfsub_mkdir, &args, /*dlgt*/0);
+ if (unlikely(wkq_err))
+ err = wkq_err;
+ }
+
+ AuTraceErr(err);
+ return err;
+}
+
+struct au_vfsub_rmdir_args {
+ int *errp;
+ struct inode *dir;
+ struct dentry *dentry;
+ struct vfsub_args *vargs;
+};
+
+static void au_call_vfsub_rmdir(void *args)
+{
+ struct au_vfsub_rmdir_args *a = args;
+ *a->errp = vfsub_rmdir(a->dir, a->dentry, a->vargs);
+}
+
+int vfsub_sio_rmdir(struct inode *dir, struct dentry *dentry, int dlgt)
+{
+ int err, do_sio, wkq_err;
+ struct vfsub_args vargs;
+
+ LKTRTrace("i%lu, %.*s\n", dir->i_ino, AuDLNPair(dentry));
+
+ vfsub_args_init(&vargs, /*ign*/NULL, dlgt, /*force_unlink*/0);
+ do_sio = au_test_h_perm_sio(dir, MAY_EXEC | MAY_WRITE, dlgt);
+ if (!do_sio)
+ err = vfsub_rmdir(dir, dentry, &vargs);
+ else {
+ struct au_vfsub_rmdir_args args = {
+ .errp = &err,
+ .dir = dir,
+ .dentry = dentry,
+ .vargs = &vargs
+ };
+ wkq_err = au_wkq_wait(au_call_vfsub_rmdir, &args, /*dlgt*/0);
+ if (unlikely(wkq_err))
+ err = wkq_err;
+ }
+
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+struct notify_change_args {
+ int *errp;
+ struct dentry *h_dentry;
+ struct iattr *ia;
+ struct vfsub_args *vargs;
+};
+
+static void call_notify_change(void *args)
+{
+ struct notify_change_args *a = args;
+ struct inode *h_inode;
+
+ LKTRTrace("%.*s, ia_valid 0x%x\n",
+ AuDLNPair(a->h_dentry), a->ia->ia_valid);
+ h_inode = a->h_dentry->d_inode;
+ IMustLock(h_inode);
+
+ *a->errp = -EPERM;
+ if (!IS_IMMUTABLE(h_inode) && !IS_APPEND(h_inode)) {
+ vfsub_ignore(a->vargs);
+ lockdep_off();
+ *a->errp = notify_change(a->h_dentry, a->ia);
+ lockdep_on();
+ if (!*a->errp)
+ au_update_fuse_h_inode(NULL, a->h_dentry); /*ignore*/
+ else
+ vfsub_unignore(a->vargs);
+ }
+ AuTraceErr(*a->errp);
+}
+
+#ifdef CONFIG_AUFS_DLGT
+static void vfsub_notify_change_dlgt(struct notify_change_args *args,
+ unsigned int flags)
+{
+ if (!vfsub_ftest(flags, DLGT))
+ call_notify_change(args);
+ else {
+ int wkq_err;
+ wkq_err = au_wkq_wait(call_notify_change, args, /*dlgt*/1);
+ if (unlikely(wkq_err))
+ *args->errp = wkq_err;
+ }
+}
+#else
+static void vfsub_notify_change_dlgt(struct notify_change_args *args,
+ unsigned int flags)
+{
+ call_notify_change(args);
+}
+#endif
+
+int vfsub_notify_change(struct dentry *dentry, struct iattr *ia,
+ struct vfsub_args *vargs)
+{
+ int err;
+ struct notify_change_args args = {
+ .errp = &err,
+ .h_dentry = dentry,
+ .ia = ia,
+ .vargs = vargs
+ };
+
+ vfsub_notify_change_dlgt(&args, vargs->flags);
+
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+struct unlink_args {
+ int *errp;
+ struct inode *dir;
+ struct dentry *dentry;
+ struct vfsub_args *vargs;
+};
+
+static void call_unlink(void *args)
+{
+ struct unlink_args *a = args;
+ struct inode *h_inode;
+ const int stop_sillyrename = (au_test_nfs(a->dentry->d_sb)
+ && atomic_read(&a->dentry->d_count) == 1);
+
+ LKTRTrace("%.*s, stop_silly %d, cnt %d\n",
+ AuDLNPair(a->dentry), stop_sillyrename,
+ atomic_read(&a->dentry->d_count));
+ //IMustLock(a->dir);
+
+ if (!stop_sillyrename)
+ dget(a->dentry);
+ h_inode = a->dentry->d_inode;
+ if (h_inode)
+ atomic_inc_return(&h_inode->i_count);
+#if 0 // partial testing
+ {
+ struct qstr *name = &a->dentry->d_name;
+ if (name->len == sizeof(AUFS_XINO_FNAME) - 1
+ && !strncmp(name->name, AUFS_XINO_FNAME, name->len))
+ *a->errp = do_vfsub_unlink(a->dir, a->dentry);
+ else
+ err = -1;
+ }
+#else
+ *a->errp = do_vfsub_unlink(a->dir, a->dentry);
+#endif
+
+ if (!stop_sillyrename)
+ dput(a->dentry);
+ if (h_inode)
+ iput(h_inode);
+
+ AuTraceErr(*a->errp);
+}
+
+/*
+ * @dir: must be locked.
+ * @dentry: target dentry.
+ */
+int vfsub_unlink(struct inode *dir, struct dentry *dentry,
+ struct vfsub_args *vargs)
+{
+ int err;
+ struct unlink_args args = {
+ .errp = &err,
+ .dir = dir,
+ .dentry = dentry,
+ .vargs = vargs
+ };
+
+ if (!vfsub_ftest(vargs->flags, DLGT)
+ && !vfsub_ftest(vargs->flags, FORCE_UNLINK))
+ call_unlink(&args);
+ else {
+ int wkq_err;
+ wkq_err = au_wkq_wait(call_unlink, &args,
+ vfsub_ftest(vargs->flags, DLGT));
+ if (unlikely(wkq_err))
+ err = wkq_err;
+ }
+
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+struct statfs_args {
+ int *errp;
+ void *arg;
+ struct kstatfs *buf;
+};
+
+static void call_statfs(void *args)
+{
+ struct statfs_args *a = args;
+ *a->errp = vfs_statfs(a->arg, a->buf);
+}
+
+#ifdef CONFIG_AUFS_DLGT
+static void vfsub_statfs_dlgt(struct statfs_args *args, int dlgt)
+{
+ if (!dlgt)
+ call_statfs(args);
+ else {
+ int wkq_err;
+ wkq_err = au_wkq_wait(call_statfs, args, /*dlgt*/1);
+ if (unlikely(wkq_err))
+ *args->errp = wkq_err;
+ }
+}
+#else
+static void vfsub_statfs_dlgt(struct statfs_args *args, int dlgt)
+{
+ call_statfs(args);
+}
+#endif
+
+int vfsub_statfs(void *arg, struct kstatfs *buf, int dlgt)
+{
+ int err;
+ struct statfs_args args = {
+ .errp = &err,
+ .arg = arg,
+ .buf = buf
+ };
+
+ vfsub_statfs_dlgt(&args, dlgt);
+
+ return err;
+}
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 26/67] aufs sub-dcache, header
2008-05-16 14:32 ` [PATCH 25/67] aufs sub-VFS, source hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 27/67] aufs sub-dcache, source hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs sub-dcache, header
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/dcsub.h | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 54 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/dcsub.h b/fs/aufs/dcsub.h
new file mode 100644
index 0000000..e012b94
--- /dev/null
+++ b/fs/aufs/dcsub.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2007-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * sub-routines for dentry cache
+ *
+ * $Id: dcsub.h,v 1.1 2008/04/18 12:18:29 sfjro Exp $
+ */
+
+#ifndef __AUFS_DCSUB_H__
+#define __AUFS_DCSUB_H__
+
+#ifdef __KERNEL__
+
+#include <linux/dcache.h>
+
+struct au_dpage {
+ int ndentry;
+ struct dentry **dentries;
+};
+
+struct au_dcsub_pages {
+ int ndpage;
+ struct au_dpage *dpages;
+};
+
+/* ---------------------------------------------------------------------- */
+
+int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp);
+void au_dpages_free(struct au_dcsub_pages *dpages);
+typedef int (*au_dpages_test)(struct dentry *dentry, void *arg);
+int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root,
+ au_dpages_test test, void *arg);
+int au_dcsub_pages_rev(struct au_dcsub_pages *dpages, struct dentry *dentry,
+ int do_include, au_dpages_test test, void *arg);
+int au_test_subdir(struct dentry *d1, struct dentry *d2);
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_DCSUB_H__ */
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 27/67] aufs sub-dcache, source
2008-05-16 14:32 ` [PATCH 26/67] aufs sub-dcache, header hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 28/67] aufs copy-up/down functions hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs sub-dcache, source
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/dcsub.c | 246 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 246 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/dcsub.c b/fs/aufs/dcsub.c
new file mode 100644
index 0000000..69ec3f6
--- /dev/null
+++ b/fs/aufs/dcsub.c
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2007-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * sub-routines for dentry cache
+ *
+ * $Id: dcsub.c,v 1.2 2008/04/21 01:31:49 sfjro Exp $
+ */
+
+#include "aufs.h"
+
+static void au_dpage_free(struct au_dpage *dpage)
+{
+ int i;
+
+ AuTraceEnter();
+ AuDebugOn(!dpage);
+
+ for (i = 0; i < dpage->ndentry; i++)
+ dput(dpage->dentries[i]);
+ free_page((unsigned long)dpage->dentries);
+}
+
+int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp)
+{
+ int err;
+ void *p;
+
+ AuTraceEnter();
+
+ err = -ENOMEM;
+ dpages->dpages = kmalloc(sizeof(*dpages->dpages), gfp);
+ if (unlikely(!dpages->dpages))
+ goto out;
+ p = (void *)__get_free_page(gfp);
+ if (unlikely(!p))
+ goto out_dpages;
+ dpages->dpages[0].ndentry = 0;
+ dpages->dpages[0].dentries = p;
+ dpages->ndpage = 1;
+ return 0; /* success */
+
+ out_dpages:
+ kfree(dpages->dpages);
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+void au_dpages_free(struct au_dcsub_pages *dpages)
+{
+ int i;
+
+ AuTraceEnter();
+
+ for (i = 0; i < dpages->ndpage; i++)
+ au_dpage_free(dpages->dpages + i);
+ kfree(dpages->dpages);
+}
+
+static int au_dpages_append(struct au_dcsub_pages *dpages,
+ struct dentry *dentry, gfp_t gfp)
+{
+ int err, sz;
+ struct au_dpage *dpage;
+ void *p;
+
+ //AuTraceEnter();
+
+ dpage = dpages->dpages + dpages->ndpage - 1;
+ AuDebugOn(!dpage);
+ sz = PAGE_SIZE / sizeof(dentry);
+ if (unlikely(dpage->ndentry >= sz)) {
+ LKTRLabel(new dpage);
+ err = -ENOMEM;
+ sz = dpages->ndpage * sizeof(*dpages->dpages);
+ p = au_kzrealloc(dpages->dpages, sz,
+ sz + sizeof(*dpages->dpages), gfp);
+ if (unlikely(!p))
+ goto out;
+ dpage = dpages->dpages + dpages->ndpage;
+ p = (void *)__get_free_page(gfp);
+ if (unlikely(!p))
+ goto out;
+ dpage->ndentry = 0;
+ dpage->dentries = p;
+ dpages->ndpage++;
+ }
+
+ dpage->dentries[dpage->ndentry++] = dget(dentry);
+ return 0; /* success */
+
+ out:
+ //AuTraceErr(err);
+ return err;
+}
+
+int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root,
+ au_dpages_test test, void *arg)
+{
+ int err;
+ struct dentry *this_parent = root;
+ struct list_head *next;
+ struct super_block *sb = root->d_sb;
+
+ AuTraceEnter();
+
+ err = 0;
+ spin_lock(&dcache_lock);
+ repeat:
+ next = this_parent->d_subdirs.next;
+ resume:
+ if (this_parent->d_sb == sb
+ && !IS_ROOT(this_parent)
+ && atomic_read(&this_parent->d_count)
+ && this_parent->d_inode
+ && (!test || test(this_parent, arg))) {
+ err = au_dpages_append(dpages, this_parent, GFP_ATOMIC);
+ if (unlikely(err))
+ goto out;
+ }
+
+ while (next != &this_parent->d_subdirs) {
+ struct list_head *tmp = next;
+ struct dentry *dentry = list_entry(tmp, struct dentry,
+ d_u.d_child);
+ next = tmp->next;
+ if (unlikely(/*d_unhashed(dentry) || */!dentry->d_inode))
+ continue;
+ if (!list_empty(&dentry->d_subdirs)) {
+ this_parent = dentry;
+ goto repeat;
+ }
+ if (dentry->d_sb == sb
+ && atomic_read(&dentry->d_count)
+ && (!test || test(dentry, arg))) {
+ err = au_dpages_append(dpages, dentry, GFP_ATOMIC);
+ if (unlikely(err))
+ goto out;
+ }
+ }
+
+ if (this_parent != root) {
+ next = this_parent->d_u.d_child.next;
+ this_parent = this_parent->d_parent; /* dcache_lock is locked */
+ goto resume;
+ }
+ out:
+ spin_unlock(&dcache_lock);
+#if 0 // debug
+ if (!err) {
+ int i, j;
+ j = 0;
+ for (i = 0; i < dpages->ndpage; i++) {
+ if ((dpages->dpages + i)->ndentry)
+ AuDbg("%d: %d\n",
+ i, (dpages->dpages + i)->ndentry);
+ j += (dpages->dpages + i)->ndentry;
+ }
+ if (j)
+ AuDbg("ndpage %d, %d\n", dpages->ndpage, j);
+ }
+#endif
+ AuTraceErr(err);
+ return err;
+}
+
+int au_dcsub_pages_rev(struct au_dcsub_pages *dpages, struct dentry *dentry,
+ int do_include, au_dpages_test test, void *arg)
+{
+ int err;
+
+ AuTraceEnter();
+
+ err = 0;
+ spin_lock(&dcache_lock);
+ if (do_include && (!test || test(dentry, arg))) {
+ err = au_dpages_append(dpages, dentry, GFP_ATOMIC);
+ if (unlikely(err))
+ goto out;
+ }
+ while (!IS_ROOT(dentry)) {
+ dentry = dentry->d_parent; /* dcache_lock is locked */
+ if (!test || test(dentry, arg)) {
+ err = au_dpages_append(dpages, dentry, GFP_ATOMIC);
+ if (unlikely(err))
+ break;
+ }
+ }
+
+ out:
+ spin_unlock(&dcache_lock);
+
+ AuTraceErr(err);
+ return err;
+}
+
+int au_test_subdir(struct dentry *d1, struct dentry *d2)
+{
+ int err;
+ int i, j;
+ struct au_dcsub_pages dpages;
+ struct au_dpage *dpage;
+ struct dentry **dentries;
+
+ LKTRTrace("%.*s, %.*s\n", AuDLNPair(d1), AuDLNPair(d2));
+
+ err = au_dpages_init(&dpages, GFP_TEMPORARY);
+ if (unlikely(err))
+ goto out;
+ err = au_dcsub_pages_rev(&dpages, d1, /*do_include*/1, NULL, NULL);
+ if (unlikely(err))
+ goto out_dpages;
+
+ for (i = dpages.ndpage - 1; !err && i >= 0; i--) {
+ dpage = dpages.dpages + i;
+ dentries = dpage->dentries;
+ for (j = dpage->ndentry - 1; !err && j >= 0; j--) {
+ struct dentry *d;
+ d = dentries[j];
+ //AuDbg("d %.*s\n", AuDLNPair(d));
+ err = (d == d2);
+ }
+ }
+
+ out_dpages:
+ au_dpages_free(&dpages);
+ out:
+ AuTraceErr(err);
+ return err;
+}
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 28/67] aufs copy-up/down functions
2008-05-16 14:32 ` [PATCH 27/67] aufs sub-dcache, source hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 29/67] aufs copy-up functions, see wbr_policy.c for copy-down hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs copy-up/down functions
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/cpup.h | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 82 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/cpup.h b/fs/aufs/cpup.h
new file mode 100644
index 0000000..0e398f2
--- /dev/null
+++ b/fs/aufs/cpup.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * copy-up/down functions
+ *
+ * $Id: cpup.h,v 1.2 2008/04/21 01:33:43 sfjro Exp $
+ */
+
+#ifndef __AUFS_CPUP_H__
+#define __AUFS_CPUP_H__
+
+#ifdef __KERNEL__
+
+#include <linux/fs.h>
+#include <linux/aufs_type.h>
+
+void au_cpup_attr_timesizes(struct inode *inode);
+void au_cpup_attr_nlink(struct inode *inode);
+void au_cpup_attr_changeable(struct inode *inode);
+void au_cpup_igen(struct inode *inode, struct inode *h_inode);
+void au_cpup_attr_all(struct inode *inode);
+
+/* ---------------------------------------------------------------------- */
+
+/* cpup flags */
+#define AuCpup_DTIME 1 /* do dtime_store/revert */
+#define au_ftest_cpup(flags, name) ((flags) & AuCpup_##name)
+#define au_fset_cpup(flags, name) { (flags) |= AuCpup_##name; }
+#define au_fclr_cpup(flags, name) { (flags) &= ~AuCpup_##name; }
+
+int au_cpup_single(struct dentry *dentry, aufs_bindex_t bdst,
+ aufs_bindex_t bsrc, loff_t len, unsigned int flags);
+int au_sio_cpup_single(struct dentry *dentry, aufs_bindex_t bdst,
+ aufs_bindex_t bsrc, loff_t len, unsigned int flags);
+int au_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
+ unsigned int flags);
+int au_sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
+ unsigned int flags);
+int au_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
+ struct file *file);
+int au_sio_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
+ struct file *file);
+
+int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst, struct dentry *locked,
+ int (*cp)(struct dentry *dentry, aufs_bindex_t bdst,
+ struct dentry *h_parent, void *arg),
+ void *arg);
+int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst,
+ struct dentry *locked);
+int au_test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst,
+ struct dentry *locked);
+
+/* ---------------------------------------------------------------------- */
+
+/* keep timestamps when copyup */
+struct au_dtime {
+ struct dentry *dt_dentry, *dt_h_dentry;
+ struct au_hinode *dt_hdir;
+ struct timespec dt_atime, dt_mtime;
+};
+void au_dtime_store(struct au_dtime *dt, struct dentry *dentry,
+ struct dentry *h_dentry, struct au_hinode *hdir);
+void au_dtime_revert(struct au_dtime *dt);
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_CPUP_H__ */
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 29/67] aufs copy-up functions, see wbr_policy.c for copy-down
2008-05-16 14:32 ` [PATCH 28/67] aufs copy-up/down functions hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 30/67] aufs whiteout for logical deletion and opaque directory, header hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs copy-up functions, see wbr_policy.c for copy-down
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/cpup.c | 1043 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 1043 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/cpup.c b/fs/aufs/cpup.c
new file mode 100644
index 0000000..3227d19
--- /dev/null
+++ b/fs/aufs/cpup.c
@@ -0,0 +1,1043 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * copy-up functions, see wbr_policy.c for copy-down
+ *
+ * $Id: cpup.c,v 1.5 2008/05/12 00:29:11 sfjro Exp $
+ */
+
+#include <linux/fs_stack.h>
+#include <linux/uaccess.h>
+#include "aufs.h"
+
+/* violent cpup_attr_*() functions don't care inode lock */
+void au_cpup_attr_timesizes(struct inode *inode)
+{
+ struct inode *h_inode;
+
+ LKTRTrace("i%lu\n", inode->i_ino);
+ //IMustLock(inode);
+ h_inode = au_h_iptr(inode, au_ibstart(inode));
+ AuDebugOn(!h_inode);
+ //IMustLock(!h_inode);
+
+ fsstack_copy_attr_times(inode, h_inode);
+ //todo: this spin_lock conflicts the new unionfs patch in -mm tree
+ //spin_lock(&inode->i_lock);
+ fsstack_copy_inode_size(inode, h_inode);
+ //spin_unlock(&inode->i_lock);
+}
+
+void au_cpup_attr_nlink(struct inode *inode)
+{
+ struct inode *h_inode;
+
+ LKTRTrace("i%lu\n", inode->i_ino);
+ //IMustLock(inode);
+ AuDebugOn(!inode->i_mode);
+
+ h_inode = au_h_iptr(inode, au_ibstart(inode));
+ inode->i_nlink = h_inode->i_nlink;
+
+ /*
+ * fewer nlink makes find(1) noisy, but larger nlink doesn't.
+ * it may includes whplink directory.
+ */
+ if (unlikely(S_ISDIR(h_inode->i_mode))) {
+ aufs_bindex_t bindex, bend;
+ bend = au_ibend(inode);
+ for (bindex = au_ibstart(inode) + 1; bindex <= bend; bindex++) {
+ h_inode = au_h_iptr(inode, bindex);
+ if (h_inode)
+ au_add_nlink(inode, h_inode);
+ }
+ }
+}
+
+void au_cpup_attr_changeable(struct inode *inode)
+{
+ struct inode *h_inode;
+
+ LKTRTrace("i%lu\n", inode->i_ino);
+ //IMustLock(inode);
+ h_inode = au_h_iptr(inode, au_ibstart(inode));
+ AuDebugOn(!h_inode);
+
+ inode->i_mode = h_inode->i_mode;
+ inode->i_uid = h_inode->i_uid;
+ inode->i_gid = h_inode->i_gid;
+ au_cpup_attr_timesizes(inode);
+
+ //??
+ inode->i_flags = h_inode->i_flags;
+}
+
+void au_cpup_igen(struct inode *inode, struct inode *h_inode)
+{
+ inode->i_generation = h_inode->i_generation;
+ au_ii(inode)->ii_hsb1 = h_inode->i_sb;
+}
+
+void au_cpup_attr_all(struct inode *inode)
+{
+ struct inode *h_inode;
+
+ LKTRTrace("i%lu\n", inode->i_ino);
+ //IMustLock(inode);
+ h_inode = au_h_iptr(inode, au_ibstart(inode));
+ AuDebugOn(!h_inode);
+
+ au_cpup_attr_changeable(inode);
+ if (inode->i_nlink > 0)
+ au_cpup_attr_nlink(inode);
+
+ switch (inode->i_mode & S_IFMT) {
+ case S_IFBLK:
+ case S_IFCHR:
+ inode->i_rdev = au_h_rdev(h_inode, /*h_mnt*/NULL,
+ /*h_dentry*/NULL);
+ }
+ inode->i_blkbits = h_inode->i_blkbits;
+ au_cpup_igen(inode, h_inode);
+}
+
+/* ---------------------------------------------------------------------- */
+
+/* Note: dt_dentry and dt_hidden_dentry are not dget/dput-ed */
+
+/* keep the timestamps of the parent dir when cpup */
+void au_dtime_store(struct au_dtime *dt, struct dentry *dentry,
+ struct dentry *h_dentry, struct au_hinode *hdir)
+{
+ struct inode *inode;
+
+ LKTRTrace("%.*s, hdir %d\n", AuDLNPair(dentry), !!hdir);
+ AuDebugOn(!dentry || !h_dentry || !h_dentry->d_inode);
+
+ dt->dt_dentry = dentry;
+ dt->dt_h_dentry = h_dentry;
+ dt->dt_hdir = hdir;
+ inode = h_dentry->d_inode;
+ dt->dt_atime = inode->i_atime;
+ dt->dt_mtime = inode->i_mtime;
+ //smp_mb();
+}
+
+void au_dtime_revert(struct au_dtime *dt)
+{
+ struct iattr attr;
+ int err;
+ struct au_hin_ignore ign;
+ struct vfsub_args vargs;
+
+ LKTRTrace("%.*s\n", AuDLNPair(dt->dt_dentry));
+
+ attr.ia_atime = dt->dt_atime;
+ attr.ia_mtime = dt->dt_mtime;
+ attr.ia_valid = ATTR_FORCE | ATTR_MTIME | ATTR_MTIME_SET
+ | ATTR_ATIME | ATTR_ATIME_SET;
+
+ vfsub_args_init(&vargs, &ign,
+ au_opt_test_dlgt(au_mntflags(dt->dt_dentry->d_sb)), 0);
+ if (unlikely(dt->dt_hdir))
+ vfsub_ign_hinode(&vargs, IN_ATTRIB, dt->dt_hdir);
+ err = vfsub_notify_change(dt->dt_h_dentry, &attr, &vargs);
+ if (unlikely(err))
+ AuWarn("restoring timestamps failed(%d). ignored\n", err);
+}
+
+/* ---------------------------------------------------------------------- */
+
+static noinline_for_stack int
+cpup_iattr(struct dentry *h_dst, struct dentry *h_src, int dlgt)
+{
+ int err, sbits;
+ struct iattr ia;
+ struct inode *h_isrc, *h_idst;
+ struct vfsub_args vargs;
+
+ LKTRTrace("%.*s\n", AuDLNPair(h_dst));
+ h_idst = h_dst->d_inode;
+ //IMustLock(h_idst);
+ h_isrc = h_src->d_inode;
+ //IMustLock(h_isrc);
+
+ ia.ia_valid = ATTR_FORCE | ATTR_MODE | ATTR_UID | ATTR_GID
+ | ATTR_ATIME | ATTR_MTIME
+ | ATTR_ATIME_SET | ATTR_MTIME_SET;
+ ia.ia_mode = h_isrc->i_mode;
+ ia.ia_uid = h_isrc->i_uid;
+ ia.ia_gid = h_isrc->i_gid;
+ ia.ia_atime = h_isrc->i_atime;
+ ia.ia_mtime = h_isrc->i_mtime;
+ sbits = !!(ia.ia_mode & (S_ISUID | S_ISGID));
+
+ vfsub_args_init(&vargs, NULL, dlgt, /*force_unlink*/0);
+ err = vfsub_notify_change(h_dst, &ia, &vargs);
+ //if (LktrCond) err = -1;
+
+ /* is this nfs only? */
+ if (!err && sbits && au_test_nfs(h_dst->d_sb)) {
+ ia.ia_valid = ATTR_FORCE | ATTR_MODE;
+ ia.ia_mode = h_isrc->i_mode;
+ err = vfsub_notify_change(h_dst, &ia, &vargs);
+ }
+
+ if (!err)
+ h_idst->i_flags = h_isrc->i_flags; //??
+
+ AuTraceErr(err);
+ return err;
+}
+
+/*
+ * to support a sparse file which is opened with O_APPEND,
+ * we need to close the file.
+ */
+static noinline_for_stack int
+cpup_regular(struct dentry *dentry, aufs_bindex_t bdst, aufs_bindex_t bsrc,
+ loff_t len)
+{
+ int err, i;
+ struct super_block *sb;
+ struct inode *h_inode;
+ enum { SRC, DST };
+ struct {
+ aufs_bindex_t bindex;
+ unsigned int flags;
+ struct dentry *dentry;
+ struct file *file;
+ void *label, *label_file;
+ } *h, hidden[] = {
+ {
+ .bindex = bsrc,
+ .flags = O_RDONLY | O_NOATIME | O_LARGEFILE,
+ .file = NULL,
+ .label = &&out,
+ .label_file = &&out_src_file
+ },
+ {
+ .bindex = bdst,
+ .flags = O_WRONLY | O_NOATIME | O_LARGEFILE,
+ .file = NULL,
+ .label = &&out_src_file,
+ .label_file = &&out_dst_file
+ }
+ };
+
+ LKTRTrace("dentry %.*s, bdst %d, bsrc %d, len %lld\n",
+ AuDLNPair(dentry), bdst, bsrc, len);
+ AuDebugOn(bsrc <= bdst);
+ AuDebugOn(!len);
+ sb = dentry->d_sb;
+ AuDebugOn(au_test_ro(sb, bdst, dentry->d_inode));
+ /* bsrc branch can be ro/rw. */
+
+ h = hidden;
+ for (i = 0; i < 2; i++, h++) {
+ h->dentry = au_h_dptr(dentry, h->bindex);
+ AuDebugOn(!h->dentry);
+ h_inode = h->dentry->d_inode;
+ AuDebugOn(!h_inode || !S_ISREG(h_inode->i_mode));
+ h->file = au_h_open(dentry, h->bindex, h->flags, /*file*/NULL);
+ //if (LktrCond)
+ //{fput(h->file);au_sbr_put(sb, h->bindex);h->file=ERR_PTR(-1);}
+ err = PTR_ERR(h->file);
+ if (IS_ERR(h->file))
+ goto *h->label;
+ err = -EINVAL;
+ if (unlikely(!h->file->f_op))
+ goto *h->label_file;
+ }
+
+ /* stop updating while we copyup */
+ IMustLock(hidden[SRC].dentry->d_inode);
+ err = au_copy_file(hidden[DST].file, hidden[SRC].file, len, sb);
+
+ out_dst_file:
+ fput(hidden[DST].file);
+ au_sbr_put(sb, hidden[DST].bindex);
+ out_src_file:
+ fput(hidden[SRC].file);
+ au_sbr_put(sb, hidden[SRC].bindex);
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+static noinline_for_stack int
+au_do_cpup_regular(struct dentry *dentry, aufs_bindex_t bdst,
+ aufs_bindex_t bsrc, loff_t len, struct inode *h_inode,
+ struct inode *h_dir, struct dentry *h_dst, int dlgt)
+{
+ int err, rerr;
+ loff_t l;
+ struct vfsub_args vargs;
+
+ AuTraceEnter();
+
+ err = 0;
+ l = i_size_read(h_inode);
+ if (len == -1 || l < len)
+ len = l;
+ if (len)
+ err = cpup_regular(dentry, bdst, bsrc, len);
+ //if (LktrCond) err = -1;
+ if (!err)
+ goto out; /* success */
+
+ vfsub_args_init(&vargs, NULL, dlgt, 0);
+ rerr = vfsub_unlink(h_dir, h_dst, &vargs);
+ if (rerr) {
+ AuIOErr("failed unlinking cpup-ed %.*s(%d, %d)\n",
+ AuDLNPair(h_dst), err, rerr);
+ err = -EIO;
+ }
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+static noinline_for_stack int
+au_do_cpup_symlink(struct dentry *h_dst, struct dentry *h_src,
+ struct inode *h_dir, umode_t mode, int dlgt)
+{
+ int err, symlen;
+ char *sym;
+ mm_segment_t old_fs;
+
+ AuTraceEnter();
+
+ err = -ENOMEM;
+ sym = __getname();
+ //if (LktrCond) {__putname(sym); sym = NULL;}
+ if (unlikely(!sym))
+ goto out;
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ symlen = h_src->d_inode->i_op->readlink(h_src, (char __user *)sym,
+ PATH_MAX);
+ err = symlen;
+ //if (LktrCond) err = symlen = -1;
+ set_fs(old_fs);
+
+ if (symlen > 0) {
+ sym[symlen] = 0;
+ err = vfsub_symlink(h_dir, h_dst, sym, mode, dlgt);
+ //if (LktrCond)
+ //{vfs_unlink(h_dir, h_dst); err = -1;}
+ }
+ __putname(sym);
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+/* return with hidden dst inode is locked */
+static noinline_for_stack int
+cpup_entry(struct dentry *dentry, aufs_bindex_t bdst, aufs_bindex_t bsrc,
+ loff_t len, unsigned int flags, int dlgt)
+{
+ int err, isdir, hinotify;
+ struct dentry *h_src, *h_dst, *h_parent, *parent;
+ struct inode *h_inode, *h_dir;
+ struct au_dtime dt;
+ umode_t mode;
+ struct super_block *sb;
+ struct au_hinode *hgdir;
+ const int do_dt = au_ftest_cpup(flags, DTIME);
+ unsigned int mnt_flags;
+
+ LKTRTrace("%.*s, i%lu, bdst %d, bsrc %d, len %Ld, dtime %u\n",
+ AuDLNPair(dentry), dentry->d_inode->i_ino, bdst, bsrc, len,
+ do_dt);
+ sb = dentry->d_sb;
+ AuDebugOn(bdst >= bsrc || au_test_ro(sb, bdst, NULL));
+ /* bsrc branch can be ro/rw. */
+
+ h_src = au_h_dptr(dentry, bsrc);
+ AuDebugOn(!h_src);
+ h_inode = h_src->d_inode;
+ AuDebugOn(!h_inode);
+
+ /* stop referencing while we are creating */
+ parent = dget_parent(dentry);
+ h_dst = au_h_dptr(dentry, bdst);
+ AuDebugOn(h_dst && h_dst->d_inode);
+ h_parent = h_dst->d_parent; /* dir inode is locked */
+ h_dir = h_parent->d_inode;
+ IMustLock(h_dir);
+
+ mnt_flags = au_mntflags(sb);
+ hinotify = !!au_opt_test(mnt_flags, UDBA_INOTIFY);
+ if (do_dt) {
+ hgdir = NULL;
+ if (unlikely(hinotify && !IS_ROOT(parent))) {
+ struct dentry *gparent;
+ gparent = dget_parent(parent);
+ hgdir = au_hi(gparent->d_inode, bdst);
+ dput(gparent);
+ }
+ au_dtime_store(&dt, parent, h_parent, hgdir);
+ }
+
+ isdir = 0;
+ mode = h_inode->i_mode;
+ switch (mode & S_IFMT) {
+ case S_IFREG:
+ /* stop updating while we are referencing */
+ IMustLock(h_inode);
+ err = au_h_create(h_dir, h_dst, mode | S_IWUSR, dlgt, NULL,
+ au_nfsmnt(sb, bdst));
+ //if (LktrCond) {vfs_unlink(h_dir, h_dst); err = -1;}
+ if (!err)
+ err = au_do_cpup_regular(dentry, bdst, bsrc, len,
+ h_inode, h_dir, h_dst, dlgt);
+ break;
+ case S_IFDIR:
+ isdir = 1;
+ err = vfsub_mkdir(h_dir, h_dst, mode, dlgt);
+ //if (LktrCond) {vfs_rmdir(h_dir, h_dst); err = -1;}
+ if (!err) {
+ /* setattr case: dir is not locked */
+ if (0 && au_ibstart(parent->d_inode) == bdst)
+ au_cpup_attr_nlink(parent->d_inode);
+ au_cpup_attr_nlink(dentry->d_inode);
+ }
+ break;
+ case S_IFLNK:
+ err = au_do_cpup_symlink(h_dst, h_src, h_dir, mode, dlgt);
+ break;
+ case S_IFCHR:
+ case S_IFBLK:
+ AuDebugOn(!capable(CAP_MKNOD));
+ /*FALLTHROUGH*/
+ case S_IFIFO:
+ case S_IFSOCK:
+ err = vfsub_mknod(h_dir, h_dst, mode,
+ au_h_rdev(h_inode, /*h_mnt*/NULL, h_src),
+ dlgt);
+ //if (LktrCond) {vfs_unlink(h_dir, h_dst); err = -1;}
+ break;
+ default:
+ AuIOErr("Unknown inode type 0%o\n", mode);
+ err = -EIO;
+ }
+
+ //todo: should it be always?
+ if (unlikely(hinotify
+ && !isdir
+ && au_opt_test(mnt_flags, XINO)
+ && h_inode->i_nlink == 1
+ && bdst < bsrc))
+ au_xino_write0(sb, bsrc, h_inode->i_ino, /*ino*/0);
+ /* ignore this error */
+
+ if (do_dt)
+ au_dtime_revert(&dt);
+ dput(parent);
+ AuTraceErr(err);
+ return err;
+}
+
+/*
+ * copyup the @dentry from @bsrc to @bdst.
+ * the caller must set the both of hidden dentries.
+ * @len is for truncating when it is -1 copyup the entire file.
+ */
+int au_cpup_single(struct dentry *dentry, aufs_bindex_t bdst,
+ aufs_bindex_t bsrc, loff_t len, unsigned int flags)
+{
+ int err, rerr, isdir, dlgt, plink;
+ struct dentry *h_src, *h_dst, *parent, *h_parent;
+ struct inode *dst_inode, *h_dir, *inode;
+ struct super_block *sb;
+ aufs_bindex_t old_ibstart;
+ struct au_dtime dt;
+ struct vfsub_args vargs;
+ struct au_hinode *hgdir;
+ unsigned int mnt_flags;
+
+ LKTRTrace("%.*s, i%lu, bdst %d, bsrc %d, len %Ld, flags 0x%x\n",
+ AuDLNPair(dentry), dentry->d_inode->i_ino, bdst, bsrc, len,
+ flags);
+ sb = dentry->d_sb;
+ AuDebugOn(bsrc <= bdst);
+ h_dst = au_h_dptr(dentry, bdst);
+ AuDebugOn(!h_dst || h_dst->d_inode);
+ h_parent = h_dst->d_parent; /* dir inode is locked */
+ h_dir = h_parent->d_inode;
+ IMustLock(h_dir);
+ h_src = au_h_dptr(dentry, bsrc);
+ AuDebugOn(!h_src || !h_src->d_inode);
+ inode = dentry->d_inode;
+ IiMustWriteLock(inode);
+ parent = dget_parent(dentry);
+
+ mnt_flags = au_mntflags(sb);
+ dlgt = !!au_opt_test_dlgt(mnt_flags);
+ plink = !!au_opt_test(mnt_flags, PLINK);
+ dst_inode = au_h_iptr(inode, bdst);
+ if (unlikely(dst_inode)) {
+ if (unlikely(!plink)) {
+ err = -EIO;
+ AuIOErr("i%lu exists on a upper branch "
+ "but plink is disabled\n", inode->i_ino);
+ goto out;
+ }
+
+ if (dst_inode->i_nlink) {
+ h_src = au_plink_lkup(sb, bdst, inode);
+ err = PTR_ERR(h_src);
+ if (IS_ERR(h_src))
+ goto out;
+ AuDebugOn(!h_src->d_inode);
+ err = vfsub_link(h_src, h_dir, h_dst, dlgt);
+ dput(h_src);
+ goto out;
+ } else
+ //todo: cpup_wh_file
+ /* udba work */
+ au_update_brange(inode, 1);
+ }
+
+ old_ibstart = au_ibstart(inode);
+ err = cpup_entry(dentry, bdst, bsrc, len, flags, dlgt);
+ if (unlikely(err))
+ goto out;
+ dst_inode = h_dst->d_inode;
+ mutex_lock_nested(&dst_inode->i_mutex, AuLsc_I_CHILD2);
+
+ //todo: test dlgt
+ err = cpup_iattr(h_dst, h_src, dlgt);
+ //if (LktrCond) err = -1;
+#if 0 // xattr
+ if (0 && !err)
+ err = cpup_xattrs(h_src, h_dst);
+#endif
+ isdir = S_ISDIR(dst_inode->i_mode);
+ if (!err) {
+ if (bdst < old_ibstart)
+ au_set_ibstart(inode, bdst);
+ au_set_h_iptr(inode, bdst, igrab(dst_inode),
+ au_hi_flags(inode, isdir));
+ mutex_unlock(&dst_inode->i_mutex);
+ if (!isdir
+ && h_src->d_inode->i_nlink > 1
+ && plink)
+ au_plink_append(sb, inode, h_dst, bdst);
+ goto out; /* success */
+ }
+
+ /* revert */
+ mutex_unlock(&dst_inode->i_mutex);
+ hgdir = NULL;
+ if (unlikely(au_opt_test(mnt_flags, UDBA_INOTIFY)
+ && !IS_ROOT(parent))) {
+ struct dentry *gparent;
+ gparent = dget_parent(parent);
+ hgdir = au_hi(gparent->d_inode, bdst);
+ dput(gparent);
+ }
+ au_dtime_store(&dt, parent, h_parent, hgdir);
+ vfsub_args_init(&vargs, NULL, dlgt, 0);
+ if (!isdir)
+ rerr = vfsub_unlink(h_dir, h_dst, &vargs);
+ else
+ rerr = vfsub_rmdir(h_dir, h_dst, &vargs);
+ //rerr = -1;
+ au_dtime_revert(&dt);
+ if (rerr) {
+ AuIOErr("failed removing broken entry(%d, %d)\n", err, rerr);
+ err = -EIO;
+ }
+
+ out:
+ dput(parent);
+ AuTraceErr(err);
+ return err;
+}
+
+struct au_cpup_single_args {
+ int *errp;
+ struct dentry *dentry;
+ aufs_bindex_t bdst, bsrc;
+ loff_t len;
+ unsigned int flags;
+};
+
+static void au_call_cpup_single(void *args)
+{
+ struct au_cpup_single_args *a = args;
+ *a->errp = au_cpup_single(a->dentry, a->bdst, a->bsrc, a->len,
+ a->flags);
+}
+
+int au_sio_cpup_single(struct dentry *dentry, aufs_bindex_t bdst,
+ aufs_bindex_t bsrc, loff_t len, unsigned int flags)
+{
+ int err, wkq_err;
+ struct dentry *h_dentry;
+ umode_t mode;
+
+ LKTRTrace("%.*s, i%lu, bdst %d, bsrc %d, len %Ld, flags 0x%x\n",
+ AuDLNPair(dentry), dentry->d_inode->i_ino, bdst, bsrc, len,
+ flags);
+
+ h_dentry = au_h_dptr(dentry, bsrc);
+ mode = h_dentry->d_inode->i_mode & S_IFMT;
+ if ((mode != S_IFCHR && mode != S_IFBLK)
+ || capable(CAP_MKNOD))
+ err = au_cpup_single(dentry, bdst, bsrc, len, flags);
+ else {
+ struct au_cpup_single_args args = {
+ .errp = &err,
+ .dentry = dentry,
+ .bdst = bdst,
+ .bsrc = bsrc,
+ .len = len,
+ .flags = flags
+ };
+ wkq_err = au_wkq_wait(au_call_cpup_single, &args, /*dlgt*/0);
+ if (unlikely(wkq_err))
+ err = wkq_err;
+ }
+
+ AuTraceErr(err);
+ return err;
+}
+
+/*
+ * copyup the @dentry from the first active hidden branch to @bdst,
+ * using au_cpup_single().
+ */
+int au_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
+ unsigned int flags)
+{
+ int err;
+ struct inode *inode;
+ aufs_bindex_t bsrc, bend;
+
+ LKTRTrace("%.*s, bdst %d, len %Ld, flags 0x%x\n",
+ AuDLNPair(dentry), bdst, len, flags);
+ inode = dentry->d_inode;
+ AuDebugOn(!S_ISDIR(inode->i_mode) && au_dbstart(dentry) < bdst);
+
+ bend = au_dbend(dentry);
+ for (bsrc = bdst + 1; bsrc <= bend; bsrc++)
+ if (au_h_dptr(dentry, bsrc))
+ break;
+ AuDebugOn(!au_h_dptr(dentry, bsrc));
+
+ err = au_lkup_neg(dentry, bdst);
+ //err = -1;
+ if (!err) {
+ err = au_cpup_single(dentry, bdst, bsrc, len, flags);
+ if (!err)
+ return 0; /* success */
+
+ /* revert */
+ au_set_h_dptr(dentry, bdst, NULL);
+ au_set_dbstart(dentry, bsrc);
+ }
+
+ AuTraceErr(err);
+ return err;
+}
+
+struct au_cpup_simple_args {
+ int *errp;
+ struct dentry *dentry;
+ aufs_bindex_t bdst;
+ loff_t len;
+ unsigned int flags;
+};
+
+static void au_call_cpup_simple(void *args)
+{
+ struct au_cpup_simple_args *a = args;
+ *a->errp = au_cpup_simple(a->dentry, a->bdst, a->len, a->flags);
+}
+
+int au_sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
+ unsigned int flags)
+{
+ int err, do_sio, dlgt, wkq_err;
+ struct dentry *parent;
+ struct inode *h_dir, *dir;
+
+ LKTRTrace("%.*s, b%d, len %Ld, flags 0x%x\n",
+ AuDLNPair(dentry), bdst, len, flags);
+
+ parent = dget_parent(dentry);
+ dir = parent->d_inode;
+ h_dir = au_h_iptr(dir, bdst);
+ dlgt = !!au_opt_test_dlgt(au_mntflags(dir->i_sb));
+ do_sio = au_test_h_perm_sio(h_dir, MAY_EXEC | MAY_WRITE, dlgt);
+ if (!do_sio) {
+ /*
+ * testing CAP_MKNOD is for generic fs,
+ * but CAP_FSETID is for xfs only, currently.
+ */
+ umode_t mode = dentry->d_inode->i_mode;
+ do_sio = (((mode & (S_IFCHR | S_IFBLK))
+ && !capable(CAP_MKNOD))
+ || ((mode & (S_ISUID | S_ISGID))
+ && !capable(CAP_FSETID)));
+ }
+ if (!do_sio)
+ err = au_cpup_simple(dentry, bdst, len, flags);
+ else {
+ struct au_cpup_simple_args args = {
+ .errp = &err,
+ .dentry = dentry,
+ .bdst = bdst,
+ .len = len,
+ .flags = flags
+ };
+ wkq_err = au_wkq_wait(au_call_cpup_simple, &args, /*dlgt*/0);
+ if (unlikely(wkq_err))
+ err = wkq_err;
+ }
+
+ dput(parent);
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static noinline_for_stack int
+au_do_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst,
+ struct dentry *wh_dentry, struct file *file, loff_t len)
+{
+ int err;
+ struct au_dinfo *dinfo;
+ aufs_bindex_t bstart;
+ struct dentry *h_d_bdst, *h_d_bstart;
+
+ AuTraceEnter();
+
+ dinfo = au_di(dentry);
+ bstart = dinfo->di_bstart;
+ h_d_bdst = dinfo->di_hdentry[0 + bdst].hd_dentry;
+ dinfo->di_bstart = bdst;
+ dinfo->di_hdentry[0 + bdst].hd_dentry = wh_dentry;
+ h_d_bstart = dinfo->di_hdentry[0 + bstart].hd_dentry;
+ if (file)
+ dinfo->di_hdentry[0 + bstart].hd_dentry
+ = au_h_fptr(file, au_fbstart(file))->f_dentry;
+ err = au_cpup_single(dentry, bdst, bstart, len, !AuCpup_DTIME);
+ //if (LktrCond) err = -1;
+ if (!err && file) {
+ err = au_reopen_nondir(file);
+ //err = -1;
+ dinfo->di_hdentry[0 + bstart].hd_dentry = h_d_bstart;
+ }
+ dinfo->di_hdentry[0 + bdst].hd_dentry = h_d_bdst;
+ dinfo->di_bstart = bstart;
+
+ AuTraceErr(err);
+ return err;
+}
+
+/*
+ * copyup the deleted file for writing.
+ */
+int au_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
+ struct file *file)
+{
+ int err, dlgt;
+ struct dentry *parent, *h_parent, *wh_dentry;
+ struct super_block *sb;
+ unsigned int mnt_flags;
+ struct au_dtime dt;
+ struct vfsub_args vargs;
+ struct au_hinode *hgdir;
+ struct au_ndx ndx = {
+ .nd = NULL,
+ .flags = 0,
+ //.br = NULL
+ };
+
+ LKTRTrace("%.*s, bdst %d, len %Lu\n", AuDLNPair(dentry), bdst, len);
+ AuDebugOn(S_ISDIR(dentry->d_inode->i_mode)
+ || (file && !(file->f_mode & FMODE_WRITE)));
+ DiMustWriteLock(dentry);
+
+ parent = dget_parent(dentry);
+ IiMustAnyLock(parent->d_inode);
+ h_parent = au_h_dptr(parent, bdst);
+ AuDebugOn(!h_parent);
+
+ sb = parent->d_sb;
+ mnt_flags = au_mntflags(sb);
+ dlgt = 0;
+ ndx.nfsmnt = au_nfsmnt(sb, bdst);
+ if (unlikely(au_opt_test_dlgt(mnt_flags))) {
+ dlgt = 1;
+ au_fset_ndx(ndx.flags, DLGT);
+ }
+ wh_dentry = au_whtmp_lkup(h_parent, &dentry->d_name, &ndx);
+ //if (LktrCond) {dput(wh_dentry); wh_dentry = ERR_PTR(-1);}
+ err = PTR_ERR(wh_dentry);
+ if (IS_ERR(wh_dentry))
+ goto out;
+
+ hgdir = NULL;
+ if (unlikely(au_opt_test(mnt_flags, UDBA_INOTIFY)
+ && !IS_ROOT(parent))) {
+ struct dentry *gparent;
+ gparent = dget_parent(parent);
+ hgdir = au_hi(gparent->d_inode, bdst);
+ dput(gparent);
+ }
+ au_dtime_store(&dt, parent, h_parent, hgdir);
+ err = au_do_cpup_wh(dentry, bdst, wh_dentry, file, len);
+ if (unlikely(err))
+ goto out_wh;
+
+ AuDebugOn(!d_unhashed(dentry));
+ /* dget first to force sillyrename on nfs */
+ dget(wh_dentry);
+ vfsub_args_init(&vargs, NULL, dlgt, 0);
+ err = vfsub_unlink(h_parent->d_inode, wh_dentry, &vargs);
+ //if (LktrCond) err = -1;
+ if (unlikely(err)) {
+ AuIOErr("failed remove copied-up tmp file %.*s(%d)\n",
+ AuDLNPair(wh_dentry), err);
+ err = -EIO;
+ }
+ au_dtime_revert(&dt);
+ au_set_hi_wh(dentry->d_inode, bdst, wh_dentry);
+
+ out_wh:
+ dput(wh_dentry);
+ out:
+ dput(parent);
+ AuTraceErr(err);
+ //au_debug_off();
+ return err;
+}
+
+struct au_cpup_wh_args {
+ int *errp;
+ struct dentry *dentry;
+ aufs_bindex_t bdst;
+ loff_t len;
+ struct file *file;
+};
+
+static void au_call_cpup_wh(void *args)
+{
+ struct au_cpup_wh_args *a = args;
+ *a->errp = au_cpup_wh(a->dentry, a->bdst, a->len, a->file);
+}
+
+int au_sio_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
+ struct file *file)
+{
+ int err, wkq_err;
+ struct dentry *parent;
+ struct inode *dir, *h_dir;
+
+ AuTraceEnter();
+ parent = dget_parent(dentry);
+ dir = parent->d_inode;
+ IiMustAnyLock(dir);
+ h_dir = au_h_iptr(dir, bdst);
+
+ if (!au_test_h_perm_sio
+ (h_dir, MAY_EXEC | MAY_WRITE,
+ au_opt_test_dlgt(au_mntflags(dentry->d_sb))))
+ err = au_cpup_wh(dentry, bdst, len, file);
+ else {
+ struct au_cpup_wh_args args = {
+ .errp = &err,
+ .dentry = dentry,
+ .bdst = bdst,
+ .len = len,
+ .file = file
+ };
+ wkq_err = au_wkq_wait(au_call_cpup_wh, &args, /*dlgt*/0);
+ if (unlikely(wkq_err))
+ err = wkq_err;
+ }
+ dput(parent);
+
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * generic routine for both of copy-up and copy-down.
+ * Although I've tried building a path by dcsub, I gave up this approach.
+ * Since the ancestor directory may be moved/renamed during copy.
+ */
+/* cf. revalidate function in file.c */
+int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst, struct dentry *locked,
+ int (*cp)(struct dentry *dentry, aufs_bindex_t bdst,
+ struct dentry *h_parent, void *arg),
+ void *arg)
+{
+ int err, hinotify;
+ struct super_block *sb;
+ struct dentry *d, *parent, *h_parent, *gparent, *real_parent;
+
+ LKTRTrace("%.*s, b%d, parent i%lu, locked %p\n",
+ AuDLNPair(dentry), bdst, parent_ino(dentry), locked);
+ sb = dentry->d_sb;
+ AuDebugOn(au_test_ro(sb, bdst, NULL));
+ err = 0;
+ parent = dget_parent(dentry);
+ IiMustWriteLock(parent->d_inode);
+ if (unlikely(IS_ROOT(parent)))
+ goto out;
+ if (locked) {
+ DiMustAnyLock(locked);
+ IiMustAnyLock(locked->d_inode);
+ }
+
+ /* slow loop, keep it simple and stupid */
+ real_parent = parent;
+ hinotify = !!au_opt_test(au_mntflags(sb), UDBA_INOTIFY);
+ while (1) {
+ dput(parent);
+ parent = dget_parent(dentry);
+ h_parent = au_h_dptr(parent, bdst);
+ if (h_parent)
+ goto out; /* success */
+
+ /* find top dir which is needed to cpup */
+ do {
+ d = parent;
+ dput(parent);
+ parent = dget_parent(d);
+ if (parent != locked) {
+ di_read_lock_parent3(parent, !AuLock_IR);
+ h_parent = au_h_dptr(parent, bdst);
+ di_read_unlock(parent, !AuLock_IR);
+ } else
+ h_parent = au_h_dptr(parent, bdst);
+ } while (!h_parent);
+
+ if (d != real_parent)
+ di_write_lock_child3(d);
+
+ /* somebody else might create while we were sleeping */
+ if (!au_h_dptr(d, bdst) || !au_h_dptr(d, bdst)->d_inode) {
+ struct inode *h_dir = h_parent->d_inode,
+ *dir = parent->d_inode;
+
+ if (au_h_dptr(d, bdst))
+ au_update_dbstart(d);
+ //AuDebugOn(au_dbstart(d) <= bdst);
+ if (parent != locked)
+ di_read_lock_parent3(parent, AuLock_IR);
+ gparent = NULL;
+ if (unlikely(hinotify && !IS_ROOT(parent))) {
+ gparent = dget_parent(parent);
+ if (gparent != locked)
+ ii_read_lock_parent4(gparent->d_inode);
+ else {
+ dput(gparent);
+ gparent = NULL;
+ }
+ }
+ au_hdir_lock(h_dir, dir, bdst);
+ err = cp(d, bdst, h_parent, arg);
+ //if (LktrCond) err = -1;
+ au_hdir_unlock(h_dir, dir, bdst);
+ if (unlikely(gparent)) {
+ ii_read_unlock(gparent->d_inode);
+ dput(gparent);
+ }
+ if (parent != locked)
+ di_read_unlock(parent, AuLock_IR);
+ }
+
+ if (d != real_parent)
+ di_write_unlock(d);
+ if (unlikely(err))
+ break;
+ }
+
+ out:
+ dput(parent);
+ AuTraceErr(err);
+ return err;
+}
+
+static int au_cpup_dir(struct dentry *dentry, aufs_bindex_t bdst,
+ struct dentry *h_parent, void *arg)
+{
+ int err;
+
+ err = au_sio_cpup_simple(dentry, bdst, -1, AuCpup_DTIME);
+
+ AuTraceErr(err);
+ return err;
+}
+
+int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst,
+ struct dentry *locked)
+{
+ int err;
+
+ err = au_cp_dirs(dentry, bdst, locked, au_cpup_dir, NULL);
+
+ AuTraceErr(err);
+ return err;
+}
+
+int au_test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst,
+ struct dentry *locked)
+{
+ int err;
+ struct dentry *parent;
+ struct inode *dir;
+
+ parent = dget_parent(dentry);
+ dir = parent->d_inode;
+ LKTRTrace("%.*s, b%d, parent i%lu, locked %p\n",
+ AuDLNPair(dentry), bdst, dir->i_ino, locked);
+ DiMustReadLock(parent);
+ IiMustReadLock(dir);
+
+ err = 0;
+ if (au_h_iptr(dir, bdst))
+ goto out;
+
+ di_read_unlock(parent, AuLock_IR);
+ di_write_lock_parent2(parent);
+ /* someone else might change our inode while we were sleeping */
+ if (unlikely(!au_h_iptr(dir, bdst)))
+ err = au_cpup_dirs(dentry, bdst, locked);
+ di_downgrade_lock(parent, AuLock_IR);
+
+ out:
+ dput(parent);
+ AuTraceErr(err);
+ return err;
+}
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 30/67] aufs whiteout for logical deletion and opaque directory, header
2008-05-16 14:32 ` [PATCH 29/67] aufs copy-up functions, see wbr_policy.c for copy-down hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 31/67] aufs whiteout for logical deletion and opaque directory, source hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs whiteout for logical deletion and opaque directory, header
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/whout.h | 140 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 140 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/whout.h b/fs/aufs/whout.h
new file mode 100644
index 0000000..f66239f
--- /dev/null
+++ b/fs/aufs/whout.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * whiteout for logical deletion and opaque directory
+ *
+ * $Id: whout.h,v 1.2 2008/04/21 02:00:37 sfjro Exp $
+ */
+
+#ifndef __AUFS_WHOUT_H__
+#define __AUFS_WHOUT_H__
+
+#ifdef __KERNEL__
+
+#include <linux/fs.h>
+#include <linux/aufs_type.h>
+#include "dir.h"
+#include "opts.h"
+#include "super.h"
+
+int au_wh_name_alloc(const char *name, int len, struct qstr *wh);
+void au_wh_name_free(struct qstr *wh);
+
+struct au_ndx;
+int au_wh_test(struct dentry *h_parent, struct qstr *wh_name, int try_sio,
+ struct au_ndx *ndx);
+int au_diropq_test(struct dentry *h_dentry, struct au_ndx *ndx);
+
+struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct qstr *prefix,
+ struct au_ndx *ndx);
+int au_whtmp_ren(struct inode *dir, struct dentry *dentry, aufs_bindex_t bindex,
+ int noself);
+int au_wh_unlink_dentry(struct inode *h_dir, struct dentry *wh_dentry,
+ struct dentry *dentry, struct inode *dir, int dlgt);
+
+struct au_branch;
+int au_wh_init(struct dentry *h_parent, struct au_branch *br,
+ struct vfsmount *nfsmnt, struct super_block *sb);
+
+/* diropq flags */
+#define AuDiropq_CREATE 1
+#define AuDiropq_DLGT (1 << 1)
+#define au_ftest_diropq(flags, name) ((flags) & AuDiropq_##name)
+#define au_fset_diropq(flags, name) { (flags) |= AuDiropq_##name; }
+#define au_fclr_diropq(flags, name) { (flags) &= ~AuDiropq_##name; }
+#ifndef CONFIG_AUFS_DLGT
+#undef AuDiropq_DLGT
+#define AuDiropq_DLGT 0
+#endif
+
+struct dentry *au_diropq_sio(struct dentry *dentry, aufs_bindex_t bindex,
+ unsigned int flags);
+
+struct dentry *au_wh_lkup(struct dentry *h_parent, struct qstr *base_name,
+ struct au_ndx *ndx);
+struct dentry *au_wh_create(struct inode *dir, struct dentry *dentry,
+ aufs_bindex_t bindex, struct dentry *h_parent,
+ struct au_ndx *ndx);
+
+/* real rmdir the whiteout-ed dir */
+struct au_whtmp_rmdir_args {
+ struct dentry *h_dentry;
+ struct au_nhash whlist;
+ aufs_bindex_t bindex;
+ struct inode *dir, *inode;
+ int noself;
+};
+
+struct au_nhash;
+int au_whtmp_rmdir(struct dentry *h_dentry, struct au_nhash *whlist,
+ aufs_bindex_t bindex, struct inode *dir, struct inode *inode,
+ int noself);
+void au_whtmp_kick_rmdir(struct dentry *h_dentry, struct au_nhash *whlist,
+ aufs_bindex_t bindex, struct inode *dir,
+ struct inode *inode, int noself,
+ struct au_whtmp_rmdir_args *args);
+
+/* ---------------------------------------------------------------------- */
+
+static inline
+struct dentry *au_diropq_create(struct dentry *dentry, aufs_bindex_t bindex,
+ int dlgt)
+{
+ unsigned int flags = AuDiropq_CREATE;
+ if (unlikely(dlgt))
+ au_fset_diropq(flags, DLGT);
+ return au_diropq_sio(dentry, bindex, flags);
+}
+
+static inline
+int au_diropq_remove(struct dentry *dentry, aufs_bindex_t bindex, int dlgt)
+{
+ unsigned int flags = !AuDiropq_CREATE;
+ if (unlikely(dlgt))
+ au_fset_diropq(flags, DLGT);
+ return PTR_ERR(au_diropq_sio(dentry, bindex, flags));
+}
+
+/* ---------------------------------------------------------------------- */
+
+#ifdef CONFIG_AUFS_ROBR
+/* robr.c */
+int au_test_robr_wh(struct qstr *name, struct dentry *h_parent,
+ struct qstr *wh_name, int try_sio, struct au_ndx *ndx);
+int au_test_robr_shwh(struct super_block *sb, const struct qstr *name);
+#else
+static inline
+int au_test_robr_wh(struct qstr *name, struct dentry *h_parent,
+ struct qstr *wh_name, int try_sio, struct au_ndx *ndx)
+{
+ return au_wh_test(h_parent, wh_name, try_sio, ndx);
+}
+
+static inline
+int au_test_robr_shwh(struct super_block *sb, const struct qstr *name)
+{
+ if (unlikely(!au_opt_test(au_mntflags(sb), SHWH)
+ && !strncmp(name->name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)))
+ return -EPERM;
+ return 0;
+}
+#endif /* CONFIG_AUFS_ROBR */
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_WHOUT_H__ */
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 31/67] aufs whiteout for logical deletion and opaque directory, source
2008-05-16 14:32 ` [PATCH 30/67] aufs whiteout for logical deletion and opaque directory, header hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 32/67] aufs pseudo-link hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs whiteout for logical deletion and opaque directory, source
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/whout.c | 1058 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 1058 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/whout.c b/fs/aufs/whout.c
new file mode 100644
index 0000000..8d1dcd3
--- /dev/null
+++ b/fs/aufs/whout.c
@@ -0,0 +1,1058 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * whiteout for logical deletion and opaque directory
+ *
+ * $Id: whout.c,v 1.4 2008/05/12 00:27:58 sfjro Exp $
+ */
+
+#include <linux/fs.h>
+#include <linux/namei.h>
+//#include <linux/random.h>
+//#include <linux/security.h>
+#include "aufs.h"
+
+#define WH_MASK S_IRUGO
+
+/* If a directory contains this file, then it is opaque. We start with the
+ * .wh. flag so that it is blocked by lookup.
+ */
+static struct qstr diropq_name = {
+ .name = AUFS_WH_DIROPQ,
+ .len = sizeof(AUFS_WH_DIROPQ) - 1
+};
+
+/*
+ * generate whiteout name, which is NOT terminated by NULL.
+ * @name: original d_name.name
+ * @len: original d_name.len
+ * @wh: whiteout qstr
+ * returns zero when succeeds, otherwise error.
+ * succeeded value as wh->name should be freed by au_wh_name_free().
+ */
+int au_wh_name_alloc(const char *name, int len, struct qstr *wh)
+{
+ char *p;
+
+ AuDebugOn(!name || !len || !wh);
+
+ if (unlikely(len > PATH_MAX - AUFS_WH_PFX_LEN))
+ return -ENAMETOOLONG;
+
+ wh->len = len + AUFS_WH_PFX_LEN;
+ p = kmalloc(wh->len, GFP_KERNEL);
+ wh->name = p;
+ //if (LktrCond) {kfree(p); wh->name = p = NULL;}
+ if (p) {
+ memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN);
+ memcpy(p + AUFS_WH_PFX_LEN, name, len);
+ //smp_mb();
+ return 0;
+ }
+ return -ENOMEM;
+}
+
+void au_wh_name_free(struct qstr *wh)
+{
+ AuDebugOn(!wh || !wh->name);
+ kfree(wh->name);
+#ifdef CONFIG_AUFS_DEBUG
+ wh->name = NULL;
+#endif
+}
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * test if the @wh_name exists under @h_parent.
+ * @try_sio specifies the necessary of super-io.
+ */
+int au_wh_test(struct dentry *h_parent, struct qstr *wh_name, int try_sio,
+ struct au_ndx *ndx)
+{
+ int err;
+ struct dentry *wh_dentry;
+ struct inode *h_dir;
+ unsigned int flags;
+
+ LKTRTrace("%.*s/%.*s, ndx{%p, 0x%x}\n", AuDLNPair(h_parent),
+ wh_name->len, wh_name->name, ndx->nfsmnt, ndx->flags);
+ h_dir = h_parent->d_inode;
+ AuDebugOn(!S_ISDIR(h_dir->i_mode));
+
+ flags = 0;
+ if (ndx && ndx->nd) {
+ flags = ndx->nd->flags;
+ ndx->nd->flags &= ~(LOOKUP_OPEN | LOOKUP_CREATE);
+ }
+
+ if (!try_sio)
+ wh_dentry = au_lkup_one(wh_name->name, h_parent,
+ wh_name->len, ndx);
+ else
+ wh_dentry = au_sio_lkup_one(wh_name->name, h_parent,
+ wh_name->len, ndx);
+ if (flags)
+ ndx->nd->flags = flags;
+ //if (LktrCond) {dput(wh_dentry); wh_dentry = ERR_PTR(-1);}
+ err = PTR_ERR(wh_dentry);
+ if (IS_ERR(wh_dentry))
+ goto out;
+
+ err = 0;
+ if (!wh_dentry->d_inode)
+ goto out_wh; /* success */
+
+ err = 1;
+ if (S_ISREG(wh_dentry->d_inode->i_mode))
+ goto out_wh; /* success */
+
+ err = -EIO;
+ AuIOErr("%.*s Invalid whiteout entry type 0%o.\n",
+ AuDLNPair(wh_dentry), wh_dentry->d_inode->i_mode);
+
+ out_wh:
+ dput(wh_dentry);
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+/*
+ * test if the @h_dentry sets opaque or not.
+ */
+int au_diropq_test(struct dentry *h_dentry, struct au_ndx *ndx)
+{
+ int err, try_sio;
+ struct inode *h_dir;
+
+ LKTRTrace("dentry %.*s\n", AuDLNPair(h_dentry));
+ h_dir = h_dentry->d_inode;
+ AuDebugOn(!S_ISDIR(h_dir->i_mode));
+
+ try_sio = au_test_h_perm_sio(h_dir, MAY_EXEC,
+ au_ftest_ndx(ndx->flags, DLGT));
+ err = au_wh_test(h_dentry, &diropq_name, try_sio, ndx);
+ AuTraceErr(err);
+ return err;
+}
+
+/*
+ * returns a negative dentry whose name is unique and temporary.
+ */
+struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct qstr *prefix,
+ struct au_ndx *ndx)
+{
+#define HEX_LEN 4
+ struct dentry *dentry;
+ int len, i;
+ char defname[AUFS_WH_PFX_LEN * 2 + DNAME_INLINE_LEN_MIN + 1
+ + HEX_LEN + 1], *name, *p;
+ static unsigned char cnt;
+
+ LKTRTrace("hp %.*s, prefix %.*s\n",
+ AuDLNPair(h_parent), prefix->len, prefix->name);
+ AuDebugOn(!h_parent->d_inode);
+
+ name = defname;
+ len = sizeof(defname) - DNAME_INLINE_LEN_MIN + prefix->len - 1;
+ if (unlikely(prefix->len > DNAME_INLINE_LEN_MIN)) {
+ dentry = ERR_PTR(-ENAMETOOLONG);
+ if (unlikely(len >= PATH_MAX))
+ goto out;
+ dentry = ERR_PTR(-ENOMEM);
+ name = kmalloc(len + 1, GFP_KERNEL);
+ //if (LktrCond) {kfree(name); name = NULL;}
+ if (unlikely(!name))
+ goto out;
+ }
+
+ /* doubly whiteout-ed */
+ memcpy(name, AUFS_WH_PFX AUFS_WH_PFX, AUFS_WH_PFX_LEN * 2);
+ p = name + AUFS_WH_PFX_LEN * 2;
+ memcpy(p, prefix->name, prefix->len);
+ p += prefix->len;
+ *p++ = '.';
+ AuDebugOn(name + len + 1 - p <= HEX_LEN);
+
+ for (i = 0; i < 3; i++) {
+ sprintf(p, "%.*d", HEX_LEN, cnt++);
+ dentry = au_sio_lkup_one(name, h_parent, len, ndx);
+ //if (LktrCond) {dput(dentry); dentry = ERR_PTR(-1);}
+ if (IS_ERR(dentry) || !dentry->d_inode)
+ goto out_name;
+ dput(dentry);
+ }
+ /* AuWarn("could not get random name\n"); */
+ dentry = ERR_PTR(-EEXIST);
+ AuDbg("%.*s\n", len, name);
+ BUG();
+
+ out_name:
+ if (unlikely(name != defname))
+ kfree(name);
+ out:
+ AuTraceErrPtr(dentry);
+ return dentry;
+#undef HEX_LEN
+}
+
+/*
+ * rename the @dentry of @bindex to the whiteouted temporary name.
+ */
+int au_whtmp_ren(struct inode *dir, struct dentry *dentry, aufs_bindex_t bindex,
+ int noself)
+{
+ int err, dlgt;
+ struct inode *h_dir;
+ struct dentry *h_dentry, *h_parent, *tmp_dentry;
+ struct super_block *sb;
+ unsigned int mnt_flags;
+ struct au_hin_ignore ign;
+ struct vfsub_args vargs;
+ struct au_ndx ndx = {
+ .flags = 0,
+ .nd = NULL,
+ //.br = NULL
+ };
+
+ LKTRTrace("%.*s, b%d\n", AuDLNPair(dentry), bindex);
+ h_dentry = au_h_dptr(dentry, bindex);
+ AuDebugOn(!h_dentry || !h_dentry->d_inode);
+ h_parent = h_dentry->d_parent; /* dir inode is locked */
+ h_dir = h_parent->d_inode;
+ IMustLock(h_dir);
+
+ sb = dentry->d_sb;
+ mnt_flags = au_mntflags(sb);
+ dlgt = !!au_opt_test_dlgt(mnt_flags);
+ if (unlikely(dlgt))
+ au_fset_ndx(ndx.flags, DLGT);
+ ndx.nfsmnt = au_nfsmnt(sb, bindex);
+ tmp_dentry = au_whtmp_lkup(h_parent, &h_dentry->d_name, &ndx);
+ //if (LktrCond) {dput(tmp_dentry); tmp_dentry = ERR_PTR(-1);}
+ err = PTR_ERR(tmp_dentry);
+ if (!IS_ERR(tmp_dentry)) {
+ /* under the same dir, no need to lock_rename() */
+ vfsub_args_init(&vargs, &ign, dlgt, 0);
+ AuDebugOn(!S_ISDIR(dentry->d_inode->i_mode));
+ if (unlikely(au_opt_test(mnt_flags, UDBA_INOTIFY) && !noself))
+ vfsub_ign_hinode(&vargs, IN_MOVE_SELF,
+ au_hi(dentry->d_inode, bindex));
+ err = vfsub_rename(h_dir, h_dentry, h_dir, tmp_dentry, &vargs);
+ //if (LktrCond) err = -1; //unavailable
+ AuTraceErr(err);
+ dput(tmp_dentry);
+ }
+
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int do_unlink_wh(struct inode *h_dir, struct dentry *wh_dentry,
+ struct inode *dir, int dlgt)
+{
+ struct vfsub_args vargs;
+
+ LKTRTrace("hi%lu, wh %.*s\n", h_dir->i_ino, AuDLNPair(wh_dentry));
+ AuDebugOn(!wh_dentry->d_inode || !S_ISREG(wh_dentry->d_inode->i_mode));
+
+ /*
+ * forces superio when the dir has a sticky bit.
+ * this may be a violation of unix fs semantics.
+ */
+ vfsub_args_init(&vargs, NULL, dlgt,
+ (h_dir->i_mode & S_ISVTX)
+ && wh_dentry->d_inode->i_uid != current->fsuid);
+ return vfsub_unlink(h_dir, wh_dentry, &vargs);
+}
+
+int au_wh_unlink_dentry(struct inode *h_dir, struct dentry *wh_dentry,
+ struct dentry *dentry, struct inode *dir, int dlgt)
+{
+ int err;
+
+ LKTRTrace("hi%lu, wh %.*s, d %p\n", h_dir->i_ino,
+ AuDLNPair(wh_dentry), dentry);
+ AuDebugOn((dentry && au_dbwh(dentry) < 0)
+ || !wh_dentry->d_inode
+ || !S_ISREG(wh_dentry->d_inode->i_mode));
+
+ err = do_unlink_wh(h_dir, wh_dentry, dir, dlgt);
+ //if (LktrCond) err = -1; // unavailable
+ if (!err && dentry)
+ au_set_dbwh(dentry, -1);
+
+ AuTraceErr(err);
+ return err;
+}
+
+static int unlink_wh_name(struct dentry *h_parent, struct qstr *wh,
+ struct inode *dir, struct au_ndx *ndx)
+{
+ int err;
+ struct inode *h_dir;
+ struct dentry *h_dentry;
+
+ LKTRTrace("%.*s/%.*s\n", AuDLNPair(h_parent), AuLNPair(wh));
+ h_dir = h_parent->d_inode;
+
+ /* au_test_h_perm() is already done */
+ h_dentry = au_lkup_one(wh->name, h_parent, wh->len, ndx);
+ //if (LktrCond) {dput(h_dentry); h_dentry = ERR_PTR(-1);}
+ if (!IS_ERR(h_dentry)) {
+ err = 0;
+ if (h_dentry->d_inode && S_ISREG(h_dentry->d_inode->i_mode))
+ err = do_unlink_wh(h_dir, h_dentry, dir,
+ au_ftest_ndx(ndx->flags, DLGT));
+ dput(h_dentry);
+ } else
+ err = PTR_ERR(h_dentry);
+
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void clean_wh(struct inode *h_dir, struct dentry *wh)
+{
+ int err;
+ struct vfsub_args vargs;
+
+ AuTraceEnter();
+
+ if (wh->d_inode) {
+ vfsub_args_init(&vargs, NULL, 0, 0);
+ err = vfsub_unlink(h_dir, wh, &vargs);
+ if (unlikely(err))
+ AuWarn("failed unlink %.*s (%d), ignored.\n",
+ AuDLNPair(wh), err);
+ }
+}
+
+static void clean_plink(struct inode *h_dir, struct dentry *plink)
+{
+ int err;
+ struct vfsub_args vargs;
+
+ AuTraceEnter();
+
+ if (plink->d_inode) {
+ vfsub_args_init(&vargs, NULL, 0, 0);
+ err = vfsub_rmdir(h_dir, plink, &vargs);
+ if (unlikely(err))
+ AuWarn("failed rmdir %.*s (%d), ignored.\n",
+ AuDLNPair(plink), err);
+ }
+}
+
+static int test_linkable(struct inode *h_dir)
+{
+ if (h_dir->i_op && h_dir->i_op->link)
+ return 0;
+ return -ENOSYS;
+}
+
+static int plink_dir(struct inode *h_dir, struct dentry *plink)
+{
+ int err;
+
+ err = -EEXIST;
+ if (!plink->d_inode) {
+ int mode = S_IRWXU;
+ if (unlikely(au_test_nfs(plink->d_sb)))
+ mode |= S_IXUGO;
+ err = vfsub_mkdir(h_dir, plink, mode, /*dlgt*/0);
+ } else if (S_ISDIR(plink->d_inode->i_mode))
+ err = 0;
+ else
+ AuErr("unknown %.*s exists\n", AuDLNPair(plink));
+
+ return err;
+}
+
+/*
+ * initialize the whiteout base file/dir for @br.
+ */
+int au_wh_init(struct dentry *h_root, struct au_branch *br,
+ struct vfsmount *nfsmnt, struct super_block *sb)
+{
+ int err;
+ struct dentry *wh, *plink;
+ struct inode *h_dir;
+ static struct qstr base_name[] = {
+ {
+ .name = AUFS_WH_BASENAME,
+ .len = sizeof(AUFS_WH_BASENAME) - 1
+ },
+ {
+ .name = AUFS_WH_PLINKDIR,
+ .len = sizeof(AUFS_WH_PLINKDIR) - 1
+ }
+ };
+ struct au_ndx ndx = {
+ .nfsmnt = nfsmnt,
+ .flags = 0, /* always no dlgt */
+ .nd = NULL,
+ //.br = NULL
+ };
+ const int do_plink = au_opt_test(au_mntflags(sb), PLINK);
+
+ LKTRTrace("nfsmnt %p\n", nfsmnt);
+ BrWhMustWriteLock(br);
+ SiMustWriteLock(sb);
+ h_dir = h_root->d_inode;
+
+ /* doubly whiteouted */
+ wh = au_wh_lkup(h_root, base_name + 0, &ndx);
+ //if (LktrCond) {dput(wh); wh = ERR_PTR(-1);}
+ err = PTR_ERR(wh);
+ if (IS_ERR(wh))
+ goto out;
+ AuDebugOn(br->br_wh && br->br_wh != wh);
+
+ plink = au_wh_lkup(h_root, base_name + 1, &ndx);
+ err = PTR_ERR(plink);
+ if (IS_ERR(plink))
+ goto out_dput_wh;
+ AuDebugOn(br->br_plink && br->br_plink != plink);
+
+ dput(br->br_wh);
+ dput(br->br_plink);
+ br->br_wh = NULL;
+ br->br_plink = NULL;
+
+ err = 0;
+ switch (br->br_perm) {
+ case AuBr_RR:
+ case AuBr_RO:
+ case AuBr_RRWH:
+ case AuBr_ROWH:
+ clean_wh(h_dir, wh);
+ clean_plink(h_dir, plink);
+ break;
+
+ case AuBr_RWNoLinkWH:
+ clean_wh(h_dir, wh);
+ if (do_plink) {
+ err = test_linkable(h_dir);
+ if (unlikely(err))
+ goto out_nolink;
+
+ err = plink_dir(h_dir, plink);
+ if (unlikely(err))
+ goto out_err;
+ br->br_plink = dget(plink);
+ } else
+ clean_plink(h_dir, plink);
+ break;
+
+ case AuBr_RW:
+ /*
+ * for the moment, aufs supports the branch filesystem
+ * which does not support link(2).
+ * testing on FAT which does not support i_op->setattr() fully
+ * either, copyup failed.
+ * finally, such filesystem will not be used as the writable
+ * branch.
+ */
+ err = test_linkable(h_dir);
+ if (unlikely(err))
+ goto out_nolink;
+
+ err = -EEXIST;
+ if (!wh->d_inode)
+ err = au_h_create(h_dir, wh, WH_MASK, /*dlgt*/0,
+ /*nd*/NULL, nfsmnt);
+ else if (S_ISREG(wh->d_inode->i_mode))
+ err = 0;
+ else
+ AuErr("unknown %.*s/%.*s exists\n",
+ AuDLNPair(h_root), AuDLNPair(wh));
+ if (unlikely(err))
+ goto out_err;
+
+ if (do_plink) {
+ err = plink_dir(h_dir, plink);
+ if (unlikely(err))
+ goto out_err;
+ br->br_plink = dget(plink);
+ } else
+ clean_plink(h_dir, plink);
+ br->br_wh = dget(wh);
+ break;
+
+ default:
+ BUG();
+ }
+
+ out_dput:
+ dput(plink);
+ out_dput_wh:
+ dput(wh);
+ out:
+ AuTraceErr(err);
+ return err;
+ out_nolink:
+ AuErr("%.*s doesn't support link(2), use noplink and rw+nolwh\n",
+ AuDLNPair(h_root));
+ goto out_dput;
+ out_err:
+ AuErr("an error(%d) on the writable branch %.*s(%s)\n",
+ err, AuDLNPair(h_root), au_sbtype(h_root->d_sb));
+ goto out_dput;
+}
+
+struct reinit_br_wh {
+ struct super_block *sb;
+ struct au_branch *br;
+};
+
+static void reinit_br_wh(void *arg)
+{
+ int err;
+ struct reinit_br_wh *a = arg;
+ struct inode *h_dir, *dir;
+ struct dentry *h_root;
+ aufs_bindex_t bindex;
+ struct vfsub_args vargs;
+
+ AuTraceEnter();
+ AuDebugOn(!a->br->br_wh || !a->br->br_wh->d_inode || current->fsuid);
+
+ err = 0;
+ /* big aufs lock */
+ si_write_lock(a->sb);
+ if (unlikely(!au_br_writable(a->br->br_perm)))
+ goto out;
+ bindex = au_br_index(a->sb, a->br->br_id);
+ if (unlikely(bindex < 0))
+ goto out;
+
+ dir = a->sb->s_root->d_inode;
+ h_root = dget_parent(a->br->br_wh);
+ h_dir = h_root->d_inode;
+ AuDebugOn(!h_dir->i_op || !h_dir->i_op->link);
+ vfsub_args_init(&vargs, NULL, /*dlgt*/0, 0);
+ au_hdir_lock(h_dir, dir, bindex);
+#if 0
+ revalidate h_wh
+#endif
+ br_wh_write_lock(a->br);
+ err = vfsub_unlink(h_dir, a->br->br_wh, &vargs);
+ //if (LktrCond) err = -1;
+ dput(a->br->br_wh);
+ a->br->br_wh = NULL;
+ if (!err)
+ err = au_wh_init(h_root, a->br, au_do_nfsmnt(a->br->br_mnt),
+ a->sb);
+ br_wh_write_unlock(a->br);
+ au_hdir_unlock(h_dir, dir, bindex);
+ dput(h_root);
+
+ out:
+ atomic_dec_return(&a->br->br_wh_running);
+ au_br_put(a->br);
+ si_write_unlock(a->sb);
+ kfree(arg);
+ if (unlikely(err))
+ AuIOErr("err %d\n", err);
+}
+
+static void kick_reinit_br_wh(struct super_block *sb, struct au_branch *br)
+{
+ int do_dec, wkq_err;
+ struct reinit_br_wh *arg;
+
+ do_dec = 1;
+ if (atomic_inc_return(&br->br_wh_running) != 1)
+ goto out;
+
+ /* ignore ENOMEM */
+ arg = kmalloc(sizeof(*arg), GFP_TEMPORARY);
+ if (arg) {
+ /*
+ * dec(wh_running), kfree(arg) and au_br_put()
+ * in reinit function
+ */
+ arg->sb = sb;
+ arg->br = br;
+ au_br_get(br);
+ wkq_err = au_wkq_nowait(reinit_br_wh, arg, sb, /*dlgt*/0);
+ if (unlikely(wkq_err)) {
+ atomic_dec_return(&br->br_wh_running);
+ au_br_put(br);
+ kfree(arg);
+ }
+ do_dec = 0;
+ }
+
+ out:
+ if (do_dec)
+ atomic_dec_return(&br->br_wh_running);
+}
+
+/*
+ * create the whiteout @wh.
+ */
+static int link_or_create_wh(struct dentry *wh, struct super_block *sb,
+ aufs_bindex_t bindex, struct inode *dir)
+{
+ int err, dlgt;
+ struct au_branch *br;
+ struct dentry *h_parent;
+ struct inode *h_dir;
+
+ LKTRTrace("%.*s\n", AuDLNPair(wh));
+ SiMustReadLock(sb);
+ h_parent = wh->d_parent; /* dir inode is locked */
+ h_dir = h_parent->d_inode;
+ IMustLock(h_dir);
+
+ dlgt = !!au_opt_test_dlgt(au_mntflags(sb));
+ br = au_sbr(sb, bindex);
+ br_wh_read_lock(br);
+ if (br->br_wh) {
+ err = vfsub_link(br->br_wh, h_dir, wh, dlgt);
+ if (!err || err != -EMLINK)
+ goto out;
+
+ /* link count full. re-initialize br_wh. */
+ kick_reinit_br_wh(sb, br);
+ }
+
+ /* return this error in this context */
+ err = au_h_create(h_dir, wh, WH_MASK, dlgt, /*nd*/NULL,
+ au_do_nfsmnt(br->br_mnt));
+
+ out:
+ br_wh_read_unlock(br);
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * create or remove the diropq.
+ */
+static struct dentry *do_diropq(struct dentry *dentry, aufs_bindex_t bindex,
+ unsigned int flags)
+{
+ struct dentry *opq_dentry, *h_dentry;
+ struct inode *h_dir;
+ int err, dlgt;
+ struct super_block *sb;
+ struct au_ndx ndx = {
+ .flags = 0,
+ .nd = NULL,
+ //.br = NULL
+ };
+
+ LKTRTrace("%.*s, bindex %d, flags 0x%x\n",
+ AuDLNPair(dentry), bindex, flags);
+ h_dentry = au_h_dptr(dentry, bindex);
+ AuDebugOn(!h_dentry);
+ h_dir = h_dentry->d_inode;
+ AuDebugOn(!h_dir || !S_ISDIR(h_dir->i_mode));
+
+ /* already checked by au_test_h_perm(). */
+ sb = dentry->d_sb;
+ ndx.nfsmnt = au_nfsmnt(sb, bindex);
+ dlgt = 0;
+ if (unlikely(au_ftest_diropq(flags, DLGT))) {
+ dlgt = 1;
+ au_fset_ndx(ndx.flags, DLGT);
+ }
+ opq_dentry = au_lkup_one(diropq_name.name, h_dentry, diropq_name.len,
+ &ndx);
+ //if (LktrCond) {dput(opq_dentry); opq_dentry = ERR_PTR(-1);}
+ if (IS_ERR(opq_dentry))
+ goto out;
+
+ if (au_ftest_diropq(flags, CREATE)) {
+ AuDebugOn(opq_dentry->d_inode);
+ err = link_or_create_wh(opq_dentry, sb, bindex,
+ dentry->d_inode);
+ //if (LktrCond) {vfs_unlink(h_dir, opq_dentry); err = -1;}
+ if (!err) {
+ au_set_dbdiropq(dentry, bindex);
+ goto out; /* success */
+ }
+ } else {
+ AuDebugOn(/* !S_ISDIR(dentry->d_inode->i_mode)
+ * || */!opq_dentry->d_inode);
+ err = do_unlink_wh(h_dir, opq_dentry, dentry->d_inode, dlgt);
+ //if (LktrCond) err = -1;
+ if (!err)
+ au_set_dbdiropq(dentry, -1);
+ }
+ dput(opq_dentry);
+ opq_dentry = ERR_PTR(err);
+
+ out:
+ AuTraceErrPtr(opq_dentry);
+ return opq_dentry;
+}
+
+struct do_diropq_args {
+ struct dentry **errp;
+ struct dentry *dentry;
+ aufs_bindex_t bindex;
+ unsigned int flags;
+};
+
+static void call_do_diropq(void *args)
+{
+ struct do_diropq_args *a = args;
+ *a->errp = do_diropq(a->dentry, a->bindex, a->flags);
+}
+
+struct dentry *au_diropq_sio(struct dentry *dentry, aufs_bindex_t bindex,
+ unsigned int flags)
+{
+ struct dentry *diropq, *h_dentry;
+
+ LKTRTrace("%.*s, bindex %d, flags 0x%x\n",
+ AuDLNPair(dentry), bindex, flags);
+
+ h_dentry = au_h_dptr(dentry, bindex);
+ if (!au_test_h_perm_sio(h_dentry->d_inode, MAY_EXEC | MAY_WRITE,
+ au_ftest_diropq(flags, DLGT)))
+ diropq = do_diropq(dentry, bindex, flags);
+ else {
+ int wkq_err;
+ struct do_diropq_args args = {
+ .errp = &diropq,
+ .dentry = dentry,
+ .bindex = bindex,
+ .flags = flags
+ };
+ wkq_err = au_wkq_wait(call_do_diropq, &args, /*dlgt*/0);
+ if (unlikely(wkq_err))
+ diropq = ERR_PTR(wkq_err);
+ }
+
+ AuTraceErrPtr(diropq);
+ return diropq;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * lookup whiteout dentry.
+ * @h_parent: hidden parent dentry which must exist and be locked
+ * @base_name: name of dentry which will be whiteouted
+ * returns dentry for whiteout.
+ */
+struct dentry *au_wh_lkup(struct dentry *h_parent, struct qstr *base_name,
+ struct au_ndx *ndx)
+{
+ int err;
+ struct qstr wh_name;
+ struct dentry *wh_dentry;
+
+ LKTRTrace("%.*s/%.*s\n", AuDLNPair(h_parent), AuLNPair(base_name));
+
+ err = au_wh_name_alloc(base_name->name, base_name->len, &wh_name);
+ //if (LktrCond) {au_wh_name_free(&wh_name); err = -1;}
+ wh_dentry = ERR_PTR(err);
+ if (!err) {
+ /* do not superio. */
+ wh_dentry = au_lkup_one(wh_name.name, h_parent,
+ wh_name.len, ndx);
+ au_wh_name_free(&wh_name);
+ }
+ AuTraceErrPtr(wh_dentry);
+ return wh_dentry;
+}
+
+/*
+ * link/create a whiteout for @dentry on @bindex.
+ */
+struct dentry *au_wh_create(struct inode *dir, struct dentry *dentry,
+ aufs_bindex_t bindex, struct dentry *h_parent,
+ struct au_ndx *ndx)
+{
+ struct dentry *wh_dentry;
+ int err;
+ struct super_block *sb;
+
+ LKTRTrace("%.*s/%.*s on b%d\n", AuDLNPair(h_parent),
+ AuDLNPair(dentry), bindex);
+
+ sb = dentry->d_sb;
+ wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, ndx);
+ //au_nfsmnt(sb, bindex), need_dlgt(sb));
+ //if (LktrCond) {dput(wh_dentry); wh_dentry = ERR_PTR(-1);}
+ if (!IS_ERR(wh_dentry) && !wh_dentry->d_inode) {
+ err = link_or_create_wh(wh_dentry, sb, bindex, dir);
+ if (!err)
+ au_set_dbwh(dentry, bindex);
+ else {
+ dput(wh_dentry);
+ wh_dentry = ERR_PTR(err);
+ }
+ }
+
+ AuTraceErrPtr(wh_dentry);
+ return wh_dentry;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/* Delete all whiteouts in this directory on branch bindex. */
+static int del_wh_children(struct au_nhash *whlist, struct dentry *h_parent,
+ aufs_bindex_t bindex, struct inode *inode,
+ struct au_ndx *ndx)
+{
+ int err, i;
+ struct qstr wh_name;
+ char *p;
+ struct inode *h_dir;
+ struct hlist_head *head;
+ struct au_vdir_wh *tpos;
+ struct hlist_node *pos;
+ struct au_vdir_destr *str;
+
+ LKTRTrace("%.*s\n", AuDLNPair(h_parent));
+ h_dir = h_parent->d_inode;
+ AuDebugOn(IS_RDONLY(h_dir));
+ //SiMustReadLock(??);
+
+ err = -ENOMEM;
+ p = __getname();
+ wh_name.name = p;
+ //if (LktrCond) {__putname(p); wh_name.name = p = NULL;}
+ if (unlikely(!wh_name.name))
+ goto out;
+ memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN);
+ p += AUFS_WH_PFX_LEN;
+
+ /* already checked by au_test_h_perm(). */
+ err = 0;
+ for (i = 0; !err && i < AuSize_NHASH; i++) {
+ head = whlist->heads + i;
+ hlist_for_each_entry(tpos, pos, head, wh_hash) {
+ if (tpos->wh_bindex != bindex)
+ continue;
+ str = &tpos->wh_str;
+ if (str->len + AUFS_WH_PFX_LEN <= PATH_MAX) {
+ memcpy(p, str->name, str->len);
+ wh_name.len = AUFS_WH_PFX_LEN + str->len;
+ err = unlink_wh_name(h_parent, &wh_name, inode,
+ ndx);
+ //if (LktrCond) err = -1;
+ if (!err)
+ continue;
+ break;
+ }
+ AuIOErr("whiteout name too long %.*s\n",
+ str->len, str->name);
+ err = -EIO;
+ break;
+ }
+ }
+ __putname(wh_name.name);
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+struct del_wh_children_args {
+ int *errp;
+ struct au_nhash *whlist;
+ struct dentry *h_parent;
+ aufs_bindex_t bindex;
+ struct inode *inode;
+ struct au_ndx *ndx;
+};
+
+static void call_del_wh_children(void *args)
+{
+ struct del_wh_children_args *a = args;
+ *a->errp = del_wh_children(a->whlist, a->h_parent, a->bindex,
+ a->inode, a->ndx);
+}
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * rmdir the whiteouted temporary named dir @h_dentry.
+ * @whlist: whiteouted children.
+ */
+int au_whtmp_rmdir(struct dentry *h_dentry, struct au_nhash *whlist,
+ aufs_bindex_t bindex, struct inode *dir, struct inode *inode,
+ int noself)
+{
+ int err, dlgt;
+ struct inode *h_inode, *h_dir;
+ struct super_block *sb;
+ unsigned int mnt_flags;
+ struct au_hin_ignore ign;
+ struct vfsub_args vargs;
+ struct au_ndx ndx = {
+ .flags = 0,
+ .nd = NULL,
+ //.br = NULL
+ };
+
+ LKTRTrace("hd %.*s, b%d, i%lu\n",
+ AuDLNPair(h_dentry), bindex, dir->i_ino);
+ IMustLock(dir);
+ IiMustAnyLock(dir);
+ h_dir = h_dentry->d_parent->d_inode; /* dir inode is locked */
+ IMustLock(h_dir);
+
+ sb = inode->i_sb;
+ mnt_flags = au_mntflags(sb);
+ dlgt = !!au_opt_test_dlgt(mnt_flags);
+ if (unlikely(dlgt))
+ au_fset_ndx(ndx.flags, DLGT);
+ ndx.nfsmnt = au_nfsmnt(sb, bindex);
+ h_inode = h_dentry->d_inode;
+ AuDebugOn(h_inode != au_h_iptr(inode, bindex));
+ au_hdir2_lock(h_inode, inode, bindex);
+
+ /*
+ * someone else might change some whiteouts while we were sleeping.
+ * it means this whlist may have an obsoleted entry.
+ */
+ if (!au_test_h_perm_sio(h_inode, MAY_EXEC | MAY_WRITE, dlgt))
+ err = del_wh_children(whlist, h_dentry, bindex, inode, &ndx);
+ else {
+ int wkq_err;
+ /* ugly */
+ unsigned int flags = ndx.flags;
+ struct del_wh_children_args args = {
+ .errp = &err,
+ .whlist = whlist,
+ .h_parent = h_dentry,
+ .bindex = bindex,
+ .inode = inode,
+ .ndx = &ndx
+ };
+
+ ndx.flags = 0;
+ wkq_err = au_wkq_wait(call_del_wh_children, &args, /*dlgt*/0);
+ if (unlikely(wkq_err))
+ err = wkq_err;
+ ndx.flags = flags;
+ }
+ au_hdir_unlock(h_inode, inode, bindex);
+
+ if (!err) {
+ vfsub_args_init(&vargs, &ign, dlgt, 0);
+ if (unlikely(au_opt_test(mnt_flags, UDBA_INOTIFY) && !noself))
+ vfsub_ign_hinode(&vargs, IN_DELETE_SELF,
+ au_hi(inode, bindex));
+ err = vfsub_rmdir(h_dir, h_dentry, &vargs);
+ //d_drop(h_dentry);
+ //if (LktrCond) err = -1;
+ }
+
+ if (!err) {
+ if (au_ibstart(dir) == bindex) {
+ au_cpup_attr_timesizes(dir);
+ //au_cpup_attr_nlink(dir);
+ drop_nlink(dir);
+ }
+ return 0; /* success */
+ }
+
+ AuWarn("failed removing %.*s(%d), ignored\n", AuDLNPair(h_dentry), err);
+ return err;
+}
+
+static void au_whtmp_rmdir_free_args(struct au_whtmp_rmdir_args *args)
+{
+ dput(args->h_dentry);
+ au_nhash_fin(&args->whlist);
+ iput(args->inode);
+ mutex_unlock(&args->dir->i_mutex);
+ iput(args->dir);
+ kfree(args);
+}
+
+static void do_rmdir_whtmp(void *args)
+{
+ int err;
+ struct au_whtmp_rmdir_args *a = args;
+ struct super_block *sb;
+
+ LKTRTrace("%.*s, b%d, dir i%lu\n",
+ AuDLNPair(a->h_dentry), a->bindex, a->dir->i_ino);
+
+ mutex_lock(&a->dir->i_mutex);
+ sb = a->dir->i_sb;
+ //AuDbgSleep(3);
+ si_noflush_read_lock(sb);
+ err = au_test_ro(sb, a->bindex, NULL);
+ if (!err) {
+ struct dentry *h_parent = dget_parent(a->h_dentry);
+ struct inode *h_dir = h_parent->d_inode;
+
+ ii_write_lock_child(a->inode);
+ ii_write_lock_parent(a->dir);
+ au_hdir_lock(h_dir, a->dir, a->bindex);
+#if 0
+ revalidate h_dentry
+#endif
+ err = au_whtmp_rmdir(a->h_dentry, &a->whlist, a->bindex,
+ a->dir, a->inode, a->noself);
+ au_hdir_unlock(h_dir, a->dir, a->bindex);
+ ii_write_unlock(a->dir);
+ ii_write_unlock(a->inode);
+ dput(h_parent);
+ }
+ si_read_unlock(sb);
+ au_whtmp_rmdir_free_args(a);
+ if (unlikely(err))
+ AuIOErr("err %d\n", err);
+}
+
+void au_whtmp_kick_rmdir(struct dentry *h_dentry, struct au_nhash *whlist,
+ aufs_bindex_t bindex, struct inode *dir,
+ struct inode *inode, int noself,
+ struct au_whtmp_rmdir_args *args)
+{
+ int wkq_err;
+
+ LKTRTrace("%.*s\n", AuDLNPair(h_dentry));
+ IMustLock(dir);
+
+ /* all post-process will be done in do_rmdir_whtmp(). */
+ args->h_dentry = dget(h_dentry);
+ au_nhash_init(&args->whlist);
+ au_nhash_move(&args->whlist, whlist);
+ args->bindex = bindex;
+ args->dir = igrab(dir);
+ args->inode = igrab(inode);
+ args->noself = noself;
+ wkq_err = au_wkq_nowait(do_rmdir_whtmp, args, dir->i_sb, /*dlgt*/0);
+ if (unlikely(wkq_err)) {
+ AuWarn("rmdir error %.*s (%d), ignored\n",
+ AuDLNPair(h_dentry), wkq_err);
+ au_whtmp_rmdir_free_args(args);
+ }
+}
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 32/67] aufs pseudo-link
2008-05-16 14:32 ` [PATCH 31/67] aufs whiteout for logical deletion and opaque directory, source hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 33/67] aufs policies for selecting one among multiple writable branches hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs pseudo-link
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/plink.c | 349 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 349 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/plink.c b/fs/aufs/plink.c
new file mode 100644
index 0000000..f991fa6
--- /dev/null
+++ b/fs/aufs/plink.c
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2007-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * pseudo-link
+ *
+ * $Id: plink.c,v 1.2 2008/04/21 01:45:16 sfjro Exp $
+ */
+
+#include "aufs.h"
+
+struct pseudo_link {
+ struct list_head list;
+ struct inode *inode;
+};
+
+#ifdef CONFIG_AUFS_DEBUG
+void au_plink_list(struct super_block *sb)
+{
+ struct au_sbinfo *sbinfo;
+ struct list_head *plink_list;
+ struct pseudo_link *plink;
+
+ AuTraceEnter();
+ SiMustAnyLock(sb);
+ sbinfo = au_sbi(sb);
+ AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK));
+
+ plink_list = &sbinfo->si_plink;
+ spin_lock(&sbinfo->si_plink_lock);
+ list_for_each_entry(plink, plink_list, list)
+ AuDbg("%lu\n", plink->inode->i_ino);
+ spin_unlock(&sbinfo->si_plink_lock);
+}
+#endif
+
+int au_plink_test(struct super_block *sb, struct inode *inode)
+{
+ int found;
+ struct au_sbinfo *sbinfo;
+ struct list_head *plink_list;
+ struct pseudo_link *plink;
+
+ LKTRTrace("i%lu\n", inode->i_ino);
+ SiMustAnyLock(sb);
+ sbinfo = au_sbi(sb);
+ AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK));
+
+ found = 0;
+ plink_list = &sbinfo->si_plink;
+ spin_lock(&sbinfo->si_plink_lock);
+ list_for_each_entry(plink, plink_list, list)
+ if (plink->inode == inode) {
+ found = 1;
+ break;
+ }
+ spin_unlock(&sbinfo->si_plink_lock);
+ return found;
+}
+
+/* 20 is max digits length of ulong 64 */
+#define PLINK_NAME_LEN ((20 + 1) * 2)
+
+static int plink_name(char *name, int len, struct inode *inode,
+ aufs_bindex_t bindex)
+{
+ int rlen;
+ struct inode *h_inode;
+
+ LKTRTrace("i%lu, b%d\n", inode->i_ino, bindex);
+ AuDebugOn(len != PLINK_NAME_LEN);
+ h_inode = au_h_iptr(inode, bindex);
+ AuDebugOn(!h_inode);
+ rlen = snprintf(name, len, "%lu.%lu", inode->i_ino, h_inode->i_ino);
+ AuDebugOn(rlen >= len);
+ return rlen;
+}
+
+struct dentry *au_plink_lkup(struct super_block *sb, aufs_bindex_t bindex,
+ struct inode *inode)
+{
+ struct dentry *h_dentry, *h_parent;
+ struct au_branch *br;
+ struct inode *h_dir;
+ char tgtname[PLINK_NAME_LEN];
+ int len;
+ struct au_ndx ndx = {
+ .flags = 0,
+ .nd = NULL,
+ //.br = NULL
+ };
+
+ LKTRTrace("b%d, i%lu\n", bindex, inode->i_ino);
+ br = au_sbr(sb, bindex);
+ h_parent = br->br_plink;
+ AuDebugOn(!h_parent);
+ h_dir = h_parent->d_inode;
+ AuDebugOn(!h_dir);
+
+ len = plink_name(tgtname, sizeof(tgtname), inode, bindex);
+
+ /* always superio. */
+ ndx.nfsmnt = au_do_nfsmnt(br->br_mnt);
+#if 0
+ if (unlikely(au_opt_test_dlgt(au_mntflags(sb))))
+ au_fset_ndx(ndx.flags, DLGT);
+#endif
+ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_CHILD2);
+ h_dentry = au_sio_lkup_one(tgtname, h_parent, len, &ndx);
+ mutex_unlock(&h_dir->i_mutex);
+ return h_dentry;
+}
+
+static int do_whplink(char *tgt, int len, struct dentry *h_parent,
+ struct dentry *h_dentry, struct vfsmount *nfsmnt,
+ struct super_block *sb)
+{
+ int err, dlgt;
+ struct dentry *h_tgt;
+ struct inode *h_dir;
+ struct vfsub_args vargs;
+ struct au_ndx ndx = {
+ .nfsmnt = nfsmnt,
+ .flags = 0,
+ .nd = NULL,
+ //.br = NULL
+ };
+
+ dlgt = !!au_opt_test_dlgt(au_mntflags(sb));
+ if (unlikely(dlgt))
+ au_fset_ndx(ndx.flags, DLGT);
+ h_tgt = au_lkup_one(tgt, h_parent, len, &ndx);
+ err = PTR_ERR(h_tgt);
+ if (IS_ERR(h_tgt))
+ goto out;
+
+ err = 0;
+ vfsub_args_init(&vargs, NULL, dlgt, 0);
+ h_dir = h_parent->d_inode;
+ if (unlikely(h_tgt->d_inode && h_tgt->d_inode != h_dentry->d_inode))
+ err = vfsub_unlink(h_dir, h_tgt, &vargs);
+ if (!err && !h_tgt->d_inode) {
+ err = vfsub_link(h_dentry, h_dir, h_tgt, dlgt);
+ //inode->i_nlink++;
+ }
+ dput(h_tgt);
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+struct do_whplink_args {
+ int *errp;
+ char *tgt;
+ int len;
+ struct dentry *h_parent;
+ struct dentry *h_dentry;
+ struct vfsmount *nfsmnt;
+ struct super_block *sb;
+};
+
+static void call_do_whplink(void *args)
+{
+ struct do_whplink_args *a = args;
+ *a->errp = do_whplink(a->tgt, a->len, a->h_parent, a->h_dentry,
+ a->nfsmnt, a->sb);
+}
+
+static int whplink(struct dentry *h_dentry, struct inode *inode,
+ aufs_bindex_t bindex, struct super_block *sb)
+{
+ int err, len, wkq_err;
+ struct au_branch *br;
+ struct dentry *h_parent;
+ struct inode *h_dir;
+ char tgtname[PLINK_NAME_LEN];
+
+ LKTRTrace("%.*s\n", AuDLNPair(h_dentry));
+ br = au_sbr(inode->i_sb, bindex);
+ h_parent = br->br_plink;
+ AuDebugOn(!h_parent);
+ h_dir = h_parent->d_inode;
+ AuDebugOn(!h_dir);
+
+ len = plink_name(tgtname, sizeof(tgtname), inode, bindex);
+
+ /* always superio. */
+ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_CHILD2);
+ if (!au_test_wkq(current)) {
+ struct do_whplink_args args = {
+ .errp = &err,
+ .tgt = tgtname,
+ .len = len,
+ .h_parent = h_parent,
+ .h_dentry = h_dentry,
+ .nfsmnt = au_do_nfsmnt(br->br_mnt),
+ .sb = sb
+ };
+ wkq_err = au_wkq_wait(call_do_whplink, &args, /*dlgt*/0);
+ if (unlikely(wkq_err))
+ err = wkq_err;
+ } else
+ err = do_whplink(tgtname, len, h_parent, h_dentry,
+ au_do_nfsmnt(br->br_mnt), sb);
+ mutex_unlock(&h_dir->i_mutex);
+
+ AuTraceErr(err);
+ return err;
+}
+
+void au_plink_append(struct super_block *sb, struct inode *inode,
+ struct dentry *h_dentry, aufs_bindex_t bindex)
+{
+ struct au_sbinfo *sbinfo;
+ struct list_head *plink_list;
+ struct pseudo_link *plink;
+ int found, err, cnt;
+
+ LKTRTrace("i%lu\n", inode->i_ino);
+ SiMustAnyLock(sb);
+ sbinfo = au_sbi(sb);
+ AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK));
+
+ cnt = 0;
+ found = 0;
+ plink_list = &sbinfo->si_plink;
+ spin_lock(&sbinfo->si_plink_lock);
+ list_for_each_entry(plink, plink_list, list) {
+ cnt++;
+ if (plink->inode == inode) {
+ found = 1;
+ break;
+ }
+ }
+
+ err = 0;
+ if (!found) {
+ plink = kmalloc(sizeof(*plink), GFP_ATOMIC);
+ if (plink) {
+ plink->inode = igrab(inode);
+ list_add(&plink->list, plink_list);
+ cnt++;
+ } else
+ err = -ENOMEM;
+ }
+ spin_unlock(&sbinfo->si_plink_lock);
+
+ if (!err)
+ err = whplink(h_dentry, inode, bindex, sb);
+
+ if (unlikely(cnt > AUFS_PLINK_WARN))
+ AuWarn1("unexpectedly many pseudo links, %d\n", cnt);
+ if (unlikely(err))
+ AuWarn("err %d, damaged pseudo link. ignored.\n", err);
+}
+
+static void do_put_plink(struct pseudo_link *plink, int do_del)
+{
+ AuTraceEnter();
+
+ iput(plink->inode);
+ if (do_del)
+ list_del(&plink->list);
+ kfree(plink);
+}
+
+void au_plink_put(struct super_block *sb)
+{
+ struct au_sbinfo *sbinfo;
+ struct list_head *plink_list;
+ struct pseudo_link *plink, *tmp;
+
+ AuTraceEnter();
+ SiMustWriteLock(sb);
+ sbinfo = au_sbi(sb);
+ AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK));
+
+ plink_list = &sbinfo->si_plink;
+ //spin_lock(&sbinfo->si_plink_lock);
+ list_for_each_entry_safe(plink, tmp, plink_list, list)
+ do_put_plink(plink, 0);
+ INIT_LIST_HEAD(plink_list);
+ //spin_unlock(&sbinfo->si_plink_lock);
+}
+
+void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id)
+{
+ struct au_sbinfo *sbinfo;
+ struct list_head *plink_list;
+ struct pseudo_link *plink, *tmp;
+ struct inode *inode;
+ aufs_bindex_t bstart, bend, bindex;
+ int do_put;
+
+ AuTraceEnter();
+ SiMustWriteLock(sb);
+ sbinfo = au_sbi(sb);
+ AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK));
+
+ plink_list = &sbinfo->si_plink;
+ //spin_lock(&sbinfo->si_plink_lock);
+ list_for_each_entry_safe(plink, tmp, plink_list, list) {
+ do_put = 0;
+ inode = igrab(plink->inode);
+ ii_write_lock_child(inode);
+ bstart = au_ibstart(inode);
+ bend = au_ibend(inode);
+ if (bstart >= 0) {
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ if (!au_h_iptr(inode, bindex)
+ || au_ii_br_id(inode, bindex) != br_id)
+ continue;
+ au_set_h_iptr(inode, bindex, NULL, 0);
+ do_put = 1;
+ break;
+ }
+ } else
+ do_put_plink(plink, 1);
+
+ if (do_put) {
+ for (bindex = bstart; bindex <= bend; bindex++)
+ if (au_h_iptr(inode, bindex)) {
+ do_put = 0;
+ break;
+ }
+ if (do_put)
+ do_put_plink(plink, 1);
+ }
+ ii_write_unlock(inode);
+ iput(inode);
+ }
+ //spin_unlock(&sbinfo->si_plink_lock);
+}
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 33/67] aufs policies for selecting one among multiple writable branches
2008-05-16 14:32 ` [PATCH 32/67] aufs pseudo-link hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 34/67] aufs lookup and dentry operations, header hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs policies for selecting one among multiple writable branches
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/wbr_policy.c | 693 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 693 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/wbr_policy.c b/fs/aufs/wbr_policy.c
new file mode 100644
index 0000000..1e0f1b8
--- /dev/null
+++ b/fs/aufs/wbr_policy.c
@@ -0,0 +1,693 @@
+/*
+ * Copyright (C) 2007-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * policies for selecting one among multiple writable branches
+ *
+ * $Id: wbr_policy.c,v 1.3 2008/04/28 03:04:12 sfjro Exp $
+ */
+
+#include <linux/statfs.h>
+#include "aufs.h"
+
+static int au_cpdown_attr(struct dentry *h_dst, struct dentry *h_src, int dlgt)
+{
+ int err, sbits;
+ struct iattr ia;
+ struct inode *h_idst, *h_isrc;
+ struct vfsub_args vargs;
+
+ LKTRTrace("%.*s\n", AuDLNPair(h_dst));
+ h_idst = h_dst->d_inode;
+ //IMustLock(h_idst);
+ h_isrc = h_src->d_inode;
+ //IMustLock(h_isrc);
+
+ ia.ia_valid = ATTR_FORCE | ATTR_MODE | ATTR_UID | ATTR_GID;
+ ia.ia_mode = h_isrc->i_mode;
+ ia.ia_uid = h_isrc->i_uid;
+ ia.ia_gid = h_isrc->i_gid;
+ sbits = !!(ia.ia_mode & (S_ISUID | S_ISGID));
+
+ vfsub_args_init(&vargs, NULL, dlgt, /*force_unlink*/0);
+ err = vfsub_notify_change(h_dst, &ia, &vargs);
+
+ /* is this nfs only? */
+ if (!err && sbits && au_test_nfs(h_dst->d_sb)) {
+ ia.ia_valid = ATTR_FORCE | ATTR_MODE;
+ ia.ia_mode = h_isrc->i_mode;
+ err = vfsub_notify_change(h_dst, &ia, &vargs);
+ }
+
+ if (!err)
+ h_idst->i_flags = h_isrc->i_flags; //??
+
+ AuTraceErr(err);
+ return err;
+}
+
+struct au_cpdown_dir_args {
+ struct dentry *parent;
+ unsigned int parent_opq; // bit-flags
+};
+
+static int au_cpdown_dir(struct dentry *dentry, aufs_bindex_t bdst,
+ struct dentry *h_parent, void *arg)
+{
+ int err, parent_opq, whed, dlgt, do_opq, made_dir, diropq, rerr;
+ struct au_cpdown_dir_args *args = arg;
+ aufs_bindex_t bend, bopq, bstart;
+ struct dentry *h_dentry, *opq_dentry, *wh_dentry;
+ struct inode *h_dir, *h_inode, *inode;
+
+ LKTRTrace("%.*s, b%d\n", AuDLNPair(dentry), bdst);
+ bstart = au_dbstart(dentry);
+ AuDebugOn(bstart <= bdst
+ && bdst <= au_dbend(dentry)
+ && au_h_dptr(dentry, bdst));
+ AuDebugOn(!h_parent);
+ h_dir = h_parent->d_inode;
+ AuDebugOn(!h_dir);
+ IMustLock(h_dir);
+
+ err = au_lkup_neg(dentry, bdst);
+ if (unlikely(err < 0))
+ goto out;
+ h_dentry = au_h_dptr(dentry, bdst);
+ dlgt = !!au_opt_test_dlgt(au_mntflags(dentry->d_sb));
+ err = vfsub_sio_mkdir(h_dir, h_dentry, S_IRWXU | S_IRUGO | S_IXUGO,
+ dlgt);
+ if (unlikely(err))
+ goto out_put;
+
+ made_dir = 1;
+ bend = au_dbend(dentry);
+ bopq = au_dbdiropq(dentry);
+ whed = (au_dbwh(dentry) == bdst);
+ if (!args->parent_opq)
+ args->parent_opq |= (bopq <= bdst);
+ parent_opq = (args->parent_opq && args->parent == dentry);
+ do_opq = 0;
+ diropq = 0;
+ h_inode = h_dentry->d_inode;
+ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);
+ if (whed || (parent_opq && do_opq)) {
+ opq_dentry = au_diropq_create(dentry, bdst, dlgt);
+ err = PTR_ERR(opq_dentry);
+ if (IS_ERR(opq_dentry)) {
+ mutex_unlock(&h_inode->i_mutex);
+ goto out_dir;
+ }
+ dput(opq_dentry);
+ diropq = 1;
+ }
+
+ err = au_cpdown_attr(h_dentry, au_h_dptr(dentry, bstart), dlgt);
+ mutex_unlock(&h_inode->i_mutex);
+ if (unlikely(err))
+ goto out_opq;
+
+ wh_dentry = NULL;
+ if (whed) {
+ wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, /*ndx*/NULL);
+ err = PTR_ERR(wh_dentry);
+ if (IS_ERR(wh_dentry))
+ goto out_opq;
+ err = 0;
+ if (wh_dentry->d_inode)
+ err = au_wh_unlink_dentry(h_dir, wh_dentry, dentry,
+ NULL, dlgt);
+ dput(wh_dentry);
+ if (unlikely(err))
+ goto out_opq;
+ }
+
+ inode = dentry->d_inode;
+ if (au_ibend(inode) < bdst)
+ au_set_ibend(inode, bdst);
+ au_set_h_iptr(inode, bdst, igrab(h_inode), au_hi_flags(inode, 1));
+ goto out; /* success */
+
+ /* revert */
+ out_opq:
+ if (diropq) {
+ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);
+ rerr = au_diropq_remove(dentry, bdst, dlgt);
+ mutex_unlock(&h_inode->i_mutex);
+ if (unlikely(rerr)) {
+ AuIOErr("failed removing diropq for %.*s b%d (%d)\n",
+ AuDLNPair(dentry), bdst, rerr);
+ err = -EIO;
+ goto out;
+ }
+ }
+ out_dir:
+ if (made_dir) {
+ rerr = vfsub_sio_rmdir(h_dir, h_dentry, dlgt);
+ if (unlikely(rerr)) {
+ AuIOErr("failed removing %.*s b%d (%d)\n",
+ AuDLNPair(dentry), bdst, rerr);
+ err = -EIO;
+ }
+ }
+ out_put:
+ au_set_h_dptr(dentry, bdst, NULL);
+ if (au_dbend(dentry) == bdst)
+ au_update_dbend(dentry);
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+int au_cpdown_dirs(struct dentry *dentry, aufs_bindex_t bdst,
+ struct dentry *locked)
+{
+ int err;
+ struct au_cpdown_dir_args args = {
+ .parent = dget_parent(dentry),
+ .parent_opq = 0
+ };
+
+ LKTRTrace("%.*s, b%d\n", AuDLNPair(dentry), bdst);
+
+ err = au_cp_dirs(dentry, bdst, locked, au_cpdown_dir, &args);
+ dput(args.parent);
+
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/* policies for create */
+
+static int au_wbr_bu(struct super_block *sb, aufs_bindex_t bindex)
+{
+ for (; bindex >= 0; bindex--)
+ if (!au_br_rdonly(au_sbr(sb, bindex)))
+ return bindex;
+ return -EROFS;
+}
+
+/* top down parent */
+static int au_wbr_create_tdp(struct dentry *dentry, int isdir)
+{
+ int err, dirperm1;
+ struct super_block *sb;
+ aufs_bindex_t bstart, bindex;
+ struct dentry *parent, *h_parent;
+ struct inode *h_dir;
+
+ LKTRTrace("%.*s, dir %d\n", AuDLNPair(dentry), isdir);
+
+ sb = dentry->d_sb;
+ dirperm1 = !!au_opt_test_dirperm1(au_mntflags(sb));
+ bstart = au_dbstart(dentry);
+ err = bstart;
+ if (!au_br_rdonly(au_sbr(sb, bstart)))
+ goto out;
+
+ err = -EROFS;
+ parent = dget_parent(dentry);
+ for (bindex = au_dbstart(parent); bindex < bstart; bindex++) {
+ h_parent = au_h_dptr(parent, bindex);
+ if (!h_parent)
+ continue;
+ h_dir = h_parent->d_inode;
+ if (!h_dir)
+ continue;
+
+ if (!au_br_rdonly(au_sbr(sb, bindex))
+ && (!dirperm1
+ || au_test_h_perm(h_dir, MAY_WRITE | MAY_EXEC,
+ /*dlgt*/0))) {
+ err = bindex;
+ break;
+ }
+ }
+ dput(parent);
+
+ /* bottom up here */
+ if (unlikely(err < 0))
+ err = au_wbr_bu(sb, bstart - 1);
+
+ out:
+ LKTRTrace("b%d\n", err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/* an exception for the policy other than tdp */
+static int au_wbr_create_exp(struct dentry *dentry)
+{
+ int err;
+ struct dentry *parent;
+ aufs_bindex_t bwh, bdiropq;
+
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+
+ err = -1;
+ bwh = au_dbwh(dentry);
+ parent = dget_parent(dentry);
+ bdiropq = au_dbdiropq(parent);
+ if (bwh >= 0) {
+ if (bdiropq >= 0)
+ err = min(bdiropq, bwh);
+ else
+ err = bwh;
+ LKTRTrace("%d\n", err);
+ } else if (bdiropq >= 0) {
+ err = bdiropq;
+ LKTRTrace("%d\n", err);
+ }
+ dput(parent);
+
+ if (err >= 0 && au_br_rdonly(au_sbr(dentry->d_sb, err)))
+ err = -1;
+
+ LKTRTrace("%d\n", err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/* round robin */
+static int au_wbr_create_init_rr(struct super_block *sb)
+{
+ int err;
+
+ err = au_wbr_bu(sb, au_sbend(sb));
+ atomic_set(&au_sbi(sb)->si_wbr_rr_next, -err); /* less important */
+
+ LKTRTrace("b%d\n", err);
+ return err;
+}
+
+static int au_wbr_create_rr(struct dentry *dentry, int isdir)
+{
+ int err, nbr;
+ struct super_block *sb;
+ atomic_t *next;
+ unsigned int u;
+ aufs_bindex_t bindex, bend;
+
+ //au_debug_on();
+ LKTRTrace("%.*s, dir %d\n", AuDLNPair(dentry), isdir);
+
+ sb = dentry->d_sb;
+ next = NULL;
+ err = au_wbr_create_exp(dentry);
+ if (err >= 0)
+ goto out;
+
+ next = &au_sbi(sb)->si_wbr_rr_next;
+ bend = au_sbend(sb);
+ nbr = bend + 1;
+ for (bindex = 0; bindex <= bend; bindex++) {
+ if (!isdir) {
+ err = atomic_dec_return(next) + 1;
+ /* modulo for 0 is meaningless */
+ if (unlikely(!err))
+ err = atomic_dec_return(next) + 1;
+ } else
+ err = atomic_read(next);
+ LKTRTrace("%d\n", err);
+ u = err;
+ err = u % nbr;
+ LKTRTrace("%d\n", err);
+ if (!au_br_rdonly(au_sbr(sb, err)))
+ break;
+ err = -EROFS;
+ }
+
+ out:
+ LKTRTrace("%d\n", err);
+ //au_debug_off();
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/* most free space */
+static void *au_wbr_statfs_arg(struct au_branch *br, struct super_block *sb,
+ aufs_bindex_t bindex)
+{
+ struct super_block *h_sb;
+
+ h_sb = br->br_mnt->mnt_sb;
+
+ if (!au_test_nfs(h_sb))
+ return h_sb->s_root;
+
+ /* sigh,,, why nfs s_root has wrong inode? */
+ return au_di(sb->s_root)->di_hdentry[0 + bindex].hd_dentry;
+}
+
+static void au_mfs(struct dentry *dentry)
+{
+ struct super_block *sb;
+ aufs_bindex_t bindex, bend;
+ int dlgt, err;
+ struct kstatfs st;
+ u64 b, bavail;
+ void *arg;
+ struct au_branch *br;
+ struct au_wbr_mfs *mfs;
+
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+
+ bavail = 0;
+ sb = dentry->d_sb;
+ mfs = &au_sbi(sb)->si_wbr_mfs;
+ mfs->mfs_bindex = -EROFS;
+ mfs->mfsrr_bytes = 0;
+ dlgt = !!au_opt_test_dlgt(au_mntflags(sb));
+ bend = au_sbend(sb);
+ for (bindex = 0; bindex <= bend; bindex++) {
+ br = au_sbr(sb, bindex);
+ if (au_br_rdonly(br))
+ continue;
+ arg = au_wbr_statfs_arg(br, sb, bindex);
+ if (!arg)
+ continue;
+
+ err = vfsub_statfs(arg, &st, dlgt);
+ LKTRTrace("b%d, %d, %Lu\n",
+ bindex, err, (unsigned long long)st.f_bavail);
+ if (unlikely(err)) {
+ AuWarn1("failed statfs, b%d, %d\n", bindex, err);
+ continue;
+ }
+
+ /* when the available size is equal, select lower one */
+ b = st.f_bavail * st.f_bsize;
+ br->br_bytes = b;
+ if (b >= bavail) {
+ bavail = b;
+ mfs->mfs_bindex = bindex;
+ mfs->mfs_jiffy = jiffies;
+ }
+ }
+
+ mfs->mfsrr_bytes = bavail;
+ LKTRTrace("b%d\n", mfs->mfs_bindex);
+}
+
+static int au_wbr_create_mfs(struct dentry *dentry, int isdir)
+{
+ int err;
+ struct super_block *sb;
+ struct au_wbr_mfs *mfs;
+
+ //au_debug_on();
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+
+ sb = dentry->d_sb;
+ err = au_wbr_create_exp(dentry);
+ if (err >= 0)
+ goto out;
+
+ mfs = &au_sbi(sb)->si_wbr_mfs;
+ mutex_lock(&mfs->mfs_lock);
+ if (unlikely(time_after(jiffies, mfs->mfs_jiffy + mfs->mfs_expire)
+ || mfs->mfs_bindex < 0
+ || au_br_rdonly(au_sbr(sb, mfs->mfs_bindex))))
+ au_mfs(dentry);
+ mutex_unlock(&mfs->mfs_lock);
+ err = mfs->mfs_bindex;
+
+ out:
+ LKTRTrace("b%d\n", err);
+ //au_debug_off();
+ return err;
+}
+
+static int au_wbr_create_init_mfs(struct super_block *sb)
+{
+ struct au_wbr_mfs *mfs;
+
+ mfs = &au_sbi(sb)->si_wbr_mfs;
+ LKTRTrace("expire %lu\n", mfs->mfs_expire);
+
+ mutex_init(&mfs->mfs_lock);
+ mfs->mfs_jiffy = 0;
+ mfs->mfs_bindex = -EROFS;
+
+ return 0;
+}
+
+static int au_wbr_create_fin_mfs(struct super_block *sb)
+{
+ AuTraceEnter();
+ mutex_destroy(&au_sbi(sb)->si_wbr_mfs.mfs_lock);
+ return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/* most free space and then round robin */
+static int au_wbr_create_mfsrr(struct dentry *dentry, int isdir)
+{
+ int err;
+ struct au_wbr_mfs *mfs;
+
+ //au_debug_on();
+ LKTRTrace("%.*s, %d\n", AuDLNPair(dentry), isdir);
+
+ err = au_wbr_create_mfs(dentry, isdir);
+ if (err >= 0) {
+ mfs = &au_sbi(dentry->d_sb)->si_wbr_mfs;
+ LKTRTrace("%Lu bytes, %Lu wmark\n",
+ mfs->mfsrr_bytes, mfs->mfsrr_watermark);
+ if (unlikely(mfs->mfsrr_bytes < mfs->mfsrr_watermark))
+ err = au_wbr_create_rr(dentry, isdir);
+ }
+
+ LKTRTrace("b%d\n", err);
+ //au_debug_off();
+ return err;
+}
+
+static int au_wbr_create_init_mfsrr(struct super_block *sb)
+{
+ int err;
+ //au_debug_on();
+ au_wbr_create_init_mfs(sb); /* ignore */
+ err = au_wbr_create_init_rr(sb);
+ //au_debug_off();
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/* top down parent and most free space */
+static int au_wbr_create_pmfs(struct dentry *dentry, int isdir)
+{
+ int err, e2, dirperm1;
+ struct super_block *sb;
+ struct dentry *parent, *h_parent;
+ aufs_bindex_t bindex, bstart, bend;
+ struct au_branch *br;
+ u64 b;
+ struct inode *h_dir;
+
+ //au_debug_on();
+ LKTRTrace("%.*s, %d\n", AuDLNPair(dentry), isdir);
+
+ err = au_wbr_create_tdp(dentry, isdir);
+ if (unlikely(err < 0))
+ goto out;
+ parent = dget_parent(dentry);
+ bstart = au_dbstart(parent);
+ bend = au_dbtaildir(parent);
+ if (bstart == bend)
+ goto out_parent; /* success */
+
+ e2 = au_wbr_create_mfs(dentry, isdir);
+ if (e2 < 0)
+ goto out_parent; /* success */
+
+ /* when the available size is equal, select upper one */
+ sb = dentry->d_sb;
+ br = au_sbr(sb, err);
+ dirperm1 = !!au_opt_test_dirperm1(au_mntflags(sb));
+ b = br->br_bytes;
+ LKTRTrace("b%d, %Lu\n", err, b);
+
+ if (unlikely(dirperm1)) {
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ h_parent = au_h_dptr(parent, bindex);
+ if (!h_parent)
+ continue;
+ h_dir = h_parent->d_inode;
+ if (!h_dir)
+ continue;
+
+ br = au_sbr(sb, bindex);
+ if (!au_br_rdonly(br)
+ && au_test_h_perm(h_dir, MAY_WRITE | MAY_EXEC,
+ /*dlgt*/0)
+ && br->br_bytes > b) {
+ b = br->br_bytes;
+ err = bindex;
+ LKTRTrace("b%d, %Lu\n", err, b);
+ }
+ }
+ if (err >= 0)
+ goto out_parent;
+ }
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ h_parent = au_h_dptr(parent, bindex);
+ if (!h_parent || !h_parent->d_inode)
+ continue;
+
+ br = au_sbr(sb, bindex);
+ if (!au_br_rdonly(br) && br->br_bytes > b) {
+ b = br->br_bytes;
+ err = bindex;
+ LKTRTrace("b%d, %Lu\n", err, b);
+ }
+ }
+
+ out_parent:
+ dput(parent);
+ out:
+ LKTRTrace("b%d\n", err);
+ //au_debug_off();
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/* policies for copyup */
+
+/* top down parent */
+static int au_wbr_copyup_tdp(struct dentry *dentry)
+{
+ return au_wbr_create_tdp(dentry, /*isdir, anything is ok*/0);
+}
+
+/* bottom up parent */
+static int au_wbr_copyup_bup(struct dentry *dentry)
+{
+ int err, dirperm1;
+ struct dentry *parent, *h_parent;
+ aufs_bindex_t bindex, bstart;
+ struct super_block *sb;
+ struct inode *h_dir;
+
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+
+ err = -EROFS;
+ sb = dentry->d_sb;
+ dirperm1 = !!au_opt_test_dirperm1(au_mntflags(sb));
+ parent = dget_parent(dentry);
+ bstart = au_dbstart(parent);
+ for (bindex = au_dbstart(dentry); bindex >= bstart; bindex--) {
+ h_parent = au_h_dptr(parent, bindex);
+ if (!h_parent)
+ continue;
+ h_dir = h_parent->d_inode;
+ if (!h_dir)
+ continue;
+
+ if (!au_br_rdonly(au_sbr(sb, bindex))
+ && (!dirperm1
+ || au_test_h_perm(h_dir, MAY_WRITE | MAY_EXEC,
+ /*dlgt*/0))) {
+ err = bindex;
+ break;
+ }
+ }
+ dput(parent);
+
+ /* bottom up here */
+ if (unlikely(err < 0))
+ err = au_wbr_bu(sb, bstart - 1);
+
+ LKTRTrace("b%d\n", err);
+ return err;
+}
+
+/* bottom up */
+static int au_wbr_copyup_bu(struct dentry *dentry)
+{
+ int err;
+
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+
+ err = au_wbr_bu(dentry->d_sb, au_dbstart(dentry));
+
+ LKTRTrace("b%d\n", err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+struct au_wbr_copyup_operations au_wbr_copyup_ops[] = {
+ [AuWbrCopyup_TDP] = {
+ .copyup = au_wbr_copyup_tdp
+ },
+ [AuWbrCopyup_BUP] = {
+ .copyup = au_wbr_copyup_bup
+ },
+ [AuWbrCopyup_BU] = {
+ .copyup = au_wbr_copyup_bu
+ }
+};
+
+struct au_wbr_create_operations au_wbr_create_ops[] = {
+ [AuWbrCreate_TDP] = {
+ .create = au_wbr_create_tdp
+ },
+ [AuWbrCreate_RR] = {
+ .create = au_wbr_create_rr,
+ .init = au_wbr_create_init_rr
+ },
+ [AuWbrCreate_MFS] = {
+ .create = au_wbr_create_mfs,
+ .init = au_wbr_create_init_mfs,
+ .fin = au_wbr_create_fin_mfs
+ },
+ [AuWbrCreate_MFSV] = {
+ .create = au_wbr_create_mfs,
+ .init = au_wbr_create_init_mfs,
+ .fin = au_wbr_create_fin_mfs
+ },
+ [AuWbrCreate_MFSRR] = {
+ .create = au_wbr_create_mfsrr,
+ .init = au_wbr_create_init_mfsrr,
+ .fin = au_wbr_create_fin_mfs
+ },
+ [AuWbrCreate_MFSRRV] = {
+ .create = au_wbr_create_mfsrr,
+ .init = au_wbr_create_init_mfsrr,
+ .fin = au_wbr_create_fin_mfs
+ },
+ [AuWbrCreate_PMFS] = {
+ .create = au_wbr_create_pmfs,
+ .init = au_wbr_create_init_mfs,
+ .fin = au_wbr_create_fin_mfs
+ },
+ [AuWbrCreate_PMFSV] = {
+ .create = au_wbr_create_pmfs,
+ .init = au_wbr_create_init_mfs,
+ .fin = au_wbr_create_fin_mfs
+ }
+};
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 34/67] aufs lookup and dentry operations, header
2008-05-16 14:32 ` [PATCH 33/67] aufs policies for selecting one among multiple writable branches hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 35/67] aufs lookup and dentry operations, source hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs lookup and dentry operations, header
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/dentry.h | 384 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 384 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/dentry.h b/fs/aufs/dentry.h
new file mode 100644
index 0000000..3ed8ebf
--- /dev/null
+++ b/fs/aufs/dentry.h
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * lookup and dentry operations
+ *
+ * $Id: dentry.h,v 1.4 2008/05/04 23:54:53 sfjro Exp $
+ */
+
+#ifndef __AUFS_DENTRY_H__
+#define __AUFS_DENTRY_H__
+
+#ifdef __KERNEL__
+
+#include <linux/fs.h>
+#include <linux/namei.h>
+#include <linux/aufs_type.h>
+#include "misc.h"
+#include "super.h"
+#include "vfsub.h"
+
+/* nameidata open_intent */
+enum {
+ AuIntent_AUFS,
+ AuIntent_BRANCH,
+ AuIntent_Last
+};
+
+struct au_hdintent {
+ struct list_head hdi_list;
+ struct file *hdi_file[AuIntent_Last];
+};
+
+struct au_hdentry {
+ struct dentry *hd_dentry;
+
+#ifdef CONFIG_AUFS_BR_NFS
+ spinlock_t hd_lock; /* intest_list */
+ struct list_head *hd_intent_list;
+#endif
+};
+
+struct au_dinfo {
+ atomic_t di_generation;
+
+ struct au_rwsem di_rwsem;
+ aufs_bindex_t di_bstart, di_bend, di_bwh, di_bdiropq;
+ struct au_hdentry *di_hdentry;
+};
+
+/* nameidata extension flags */
+#define AuNdx_DLGT 1
+#define AuNdx_DIRPERM1 (1 << 1)
+#define au_ftest_ndx(flags, name) ((flags) & AuNdx_##name)
+#define au_fset_ndx(flags, name) { (flags) |= AuNdx_##name; }
+#define au_fclr_ndx(flags, name) { (flags) &= ~AuNdx_##name; }
+#ifndef CONFIG_AUFS_DLGT
+#undef AuNdx_DLGT
+#define AuNdx_DLGT 0
+#undef AuNdx_DIRPERM1
+#define AuNdx_DIRPERM1 0
+#endif
+
+struct au_ndx {
+ struct vfsmount *nfsmnt;
+ unsigned int flags;
+ struct nameidata *nd;
+ struct au_branch *br;
+ struct file *nd_file;
+};
+
+/* ---------------------------------------------------------------------- */
+
+static inline void au_do_h_dentry_init(struct au_hdentry *hdentry)
+{
+ hdentry->hd_dentry = NULL;
+}
+
+#ifdef CONFIG_AUFS_BR_NFS
+static inline void au_h_dentry_init(struct au_hdentry *hdentry)
+{
+ au_do_h_dentry_init(hdentry);
+ spin_lock_init(&hdentry->hd_lock);
+}
+
+static inline void au_h_dentry_init_all(struct au_hdentry *hdentry, int n)
+{
+ while (n--)
+ spin_lock_init(&hdentry[n].hd_lock);
+}
+
+/* br_nfs.c */
+struct file *au_h_intent(struct dentry *dentry, aufs_bindex_t bindex,
+ struct file *file);
+int au_br_nfs_h_intent(struct file *nd_file, struct dentry *dentry,
+ aufs_bindex_t bindex, struct nameidata *nd);
+void au_hintent_put(struct au_hdentry *hd, int do_free);
+int au_fake_intent(struct nameidata *nd, int perm);
+int au_hin_after_reval(struct nameidata *nd, struct dentry *dentry,
+ aufs_bindex_t bindex, struct file *file);
+struct dentry *au_lkup_hash(const char *name, struct dentry *parent, int len,
+ struct au_ndx *ndx);
+#else
+
+static inline void au_h_dentry_init(struct au_hdentry *hdentry)
+{
+ au_do_h_dentry_init(hdentry);
+}
+
+static inline void au_h_dentry_init_all(struct au_hdentry *hdentry, int n)
+{
+ /* nothing */
+}
+
+static inline
+struct file *au_h_intent(struct dentry *dentry, aufs_bindex_t bindex,
+ struct file *file)
+{
+ //return ERR_PTR(-ENOSYS);
+ return NULL;
+}
+
+static inline
+int au_br_nfs_h_intent(struct file *nd_file, struct dentry *dentry,
+ aufs_bindex_t bindex, struct nameidata *nd)
+{
+ return 0;
+}
+
+static inline void au_hintent_put(struct au_hdentry *hd, int do_free)
+{
+ /* empty */
+}
+
+static inline int au_fake_intent(struct nameidata *nd, int perm)
+{
+ return 0;
+}
+
+static inline
+int au_hin_after_reval(struct nameidata *nd, struct dentry *dentry,
+ aufs_bindex_t bindex, struct file *file)
+{
+ return 0;
+}
+
+#ifdef CONFIG_AUFS_DLGT
+static inline
+struct dentry *au_lkup_hash(const char *name, struct dentry *parent, int len,
+ struct au_ndx *ndx)
+{
+ //return ERR_PTR(-ENOSYS);
+ return vfsub_lookup_one_len(name, parent, len);
+}
+#endif
+#endif /* CONFIG_AUFS_BR_NFS */
+
+#ifdef CONFIG_AUFS_DLGT
+/* dlgt.c */
+struct dentry *au_lkup_one_dlgt(const char *name, struct dentry *parent,
+ int len, unsigned int flags);
+#elif defined(CONFIG_AUFS_BR_NFS)
+/* regardelss kernel version */
+static inline
+struct dentry *au_lkup_one_dlgt(const char *name, struct dentry *parent,
+ int len, unsigned int flags)
+{
+ return vfsub_lookup_one_len(name, parent, len);
+}
+#endif
+
+/* dentry.c */
+extern struct dentry_operations aufs_dop;
+#if defined(CONFIG_AUFS_BR_NFS) || defined(CONFIG_AUFS_DLGT)
+struct dentry *au_lkup_one(const char *name, struct dentry *parent, int len,
+ struct au_ndx *ndx);
+#else
+static inline
+struct dentry *au_lkup_one(const char *name, struct dentry *parent, int len,
+ struct au_ndx *ndx)
+{
+ //todo: ndx->nd_file = NULL;
+ return vfsub_lookup_one_len(name, parent, len);
+}
+#endif
+struct dentry *au_sio_lkup_one(const char *name, struct dentry *parent, int len,
+ struct au_ndx *ndx);
+int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type,
+ struct nameidata *nd);
+int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex);
+int au_refresh_hdentry(struct dentry *dentry, mode_t type);
+int au_reval_dpath(struct dentry *dentry, au_gen_t sgen);
+
+/* dinfo.c */
+int au_alloc_dinfo(struct dentry *dentry);
+struct au_dinfo *au_di(struct dentry *dentry);
+
+void di_read_lock(struct dentry *d, int flags, unsigned int lsc);
+void di_read_unlock(struct dentry *d, int flags);
+void di_downgrade_lock(struct dentry *d, int flags);
+void di_write_lock(struct dentry *d, unsigned int lsc);
+void di_write_unlock(struct dentry *d);
+void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir);
+void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir);
+void di_write_unlock2(struct dentry *d1, struct dentry *d2);
+
+struct dentry *au_h_dptr(struct dentry *dentry, aufs_bindex_t bindex);
+
+aufs_bindex_t au_dbtail(struct dentry *dentry);
+aufs_bindex_t au_dbtaildir(struct dentry *dentry);
+#if 0 // rfu
+aufs_bindex_t au_dbtail_generic(struct dentry *dentry);
+#endif
+
+void au_set_dbdiropq(struct dentry *dentry, aufs_bindex_t bindex);
+void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex,
+ struct dentry *h_dentry);
+
+void au_update_dbrange(struct dentry *dentry, int do_put_zero);
+void au_update_dbstart(struct dentry *dentry);
+void au_update_dbend(struct dentry *dentry);
+int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry);
+
+/* ---------------------------------------------------------------------- */
+
+//todo: memory barrier?
+static inline au_gen_t au_digen(struct dentry *d)
+{
+ return atomic_read(&au_di(d)->di_generation);
+}
+
+#ifdef CONFIG_AUFS_HINOTIFY
+static inline au_gen_t au_digen_dec(struct dentry *d)
+{
+ return atomic_dec_return(&au_di(d)->di_generation);
+}
+#endif /* CONFIG_AUFS_HINOTIFY */
+
+/* ---------------------------------------------------------------------- */
+
+/* lock subclass for dinfo */
+enum {
+ AuLsc_DI_CHILD, /* child first */
+ AuLsc_DI_CHILD2, /* rename(2), link(2), and cpup at hinotify */
+ AuLsc_DI_CHILD3, /* copyup dirs */
+ AuLsc_DI_PARENT,
+ AuLsc_DI_PARENT2,
+ AuLsc_DI_PARENT3
+};
+
+/*
+ * di_read_lock_child, di_write_lock_child,
+ * di_read_lock_child2, di_write_lock_child2,
+ * di_read_lock_child3, di_write_lock_child3,
+ * di_read_lock_parent, di_write_lock_parent,
+ * di_read_lock_parent2, di_write_lock_parent2,
+ * di_read_lock_parent3, di_write_lock_parent3,
+ */
+#define AuReadLockFunc(name, lsc) \
+static inline void di_read_lock_##name(struct dentry *d, int flags) \
+{ di_read_lock(d, flags, AuLsc_DI_##lsc); }
+
+#define AuWriteLockFunc(name, lsc) \
+static inline void di_write_lock_##name(struct dentry *d) \
+{ di_write_lock(d, AuLsc_DI_##lsc); }
+
+#define AuRWLockFuncs(name, lsc) \
+ AuReadLockFunc(name, lsc) \
+ AuWriteLockFunc(name, lsc)
+
+AuRWLockFuncs(child, CHILD);
+AuRWLockFuncs(child2, CHILD2);
+AuRWLockFuncs(child3, CHILD3);
+AuRWLockFuncs(parent, PARENT);
+AuRWLockFuncs(parent2, PARENT2);
+AuRWLockFuncs(parent3, PARENT3);
+
+#undef AuReadLockFunc
+#undef AuWriteLockFunc
+#undef AuRWLockFuncs
+
+/* to debug easier, do not make them inlined functions */
+#define DiMustReadLock(d) do { \
+ SiMustAnyLock((d)->d_sb); \
+ AuRwMustReadLock(&au_di(d)->di_rwsem); \
+} while (0)
+
+#define DiMustWriteLock(d) do { \
+ SiMustAnyLock((d)->d_sb); \
+ AuRwMustWriteLock(&au_di(d)->di_rwsem); \
+} while (0)
+
+#define DiMustAnyLock(d) do { \
+ SiMustAnyLock((d)->d_sb); \
+ AuRwMustAnyLock(&au_di(d)->di_rwsem); \
+} while (0)
+
+#define DiMustNoWaiters(d) AuRwMustNoWaiters(&au_di(d)->di_rwsem)
+
+/* ---------------------------------------------------------------------- */
+
+static inline aufs_bindex_t au_dbstart(struct dentry *dentry)
+{
+ DiMustAnyLock(dentry);
+ return au_di(dentry)->di_bstart;
+}
+
+static inline aufs_bindex_t au_dbend(struct dentry *dentry)
+{
+ DiMustAnyLock(dentry);
+ return au_di(dentry)->di_bend;
+}
+
+static inline aufs_bindex_t au_dbwh(struct dentry *dentry)
+{
+ DiMustAnyLock(dentry);
+ return au_di(dentry)->di_bwh;
+}
+
+static inline aufs_bindex_t au_dbdiropq(struct dentry *dentry)
+{
+ DiMustAnyLock(dentry);
+ AuDebugOn(dentry->d_inode
+ && dentry->d_inode->i_mode
+ && !S_ISDIR(dentry->d_inode->i_mode));
+ return au_di(dentry)->di_bdiropq;
+}
+
+// hard/soft set
+static inline void au_set_dbstart(struct dentry *dentry, aufs_bindex_t bindex)
+{
+ DiMustWriteLock(dentry);
+ AuDebugOn(au_sbend(dentry->d_sb) < bindex);
+ /* */
+ au_di(dentry)->di_bstart = bindex;
+}
+
+static inline void au_set_dbend(struct dentry *dentry, aufs_bindex_t bindex)
+{
+ DiMustWriteLock(dentry);
+ AuDebugOn(au_sbend(dentry->d_sb) < bindex
+ || bindex < au_dbstart(dentry));
+ au_di(dentry)->di_bend = bindex;
+}
+
+static inline void au_set_dbwh(struct dentry *dentry, aufs_bindex_t bindex)
+{
+ DiMustWriteLock(dentry);
+ AuDebugOn(au_sbend(dentry->d_sb) < bindex);
+ /* dbwh can be outside of bstart - bend range */
+ au_di(dentry)->di_bwh = bindex;
+}
+
+static inline void au_hdput(struct au_hdentry *hd, int do_free)
+{
+ au_hintent_put(hd, do_free);
+ dput(hd->hd_dentry);
+}
+
+static inline void au_update_digen(struct dentry *dentry)
+{
+ //DiMustWriteLock(dentry);
+ AuDebugOn(!dentry->d_sb);
+ atomic_set(&au_di(dentry)->di_generation, au_sigen(dentry->d_sb));
+ //smp_mb(); /* atomic_set */
+}
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_DENTRY_H__ */
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 35/67] aufs lookup and dentry operations, source
2008-05-16 14:32 ` [PATCH 34/67] aufs lookup and dentry operations, header hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 36/67] aufs dentry private data hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs lookup and dentry operations, source
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/dentry.c | 1020 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 1020 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/dentry.c b/fs/aufs/dentry.c
new file mode 100644
index 0000000..06afa17
--- /dev/null
+++ b/fs/aufs/dentry.c
@@ -0,0 +1,1020 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * lookup and dentry operations
+ *
+ * $Id: dentry.c,v 1.5 2008/05/12 00:29:11 sfjro Exp $
+ */
+
+//#include <linux/fs.h>
+//#include <linux/namei.h>
+#include "aufs.h"
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * au_lkup_one() is a generic abstract entry function which calls
+ * lookup_one_len() or __lookup_hash() finally. it is some condisions that makes
+ * lookup complicated, which are nfs branch, open-intent and dlgt mode.
+ */
+
+#if defined(CONFIG_AUFS_BR_NFS) || defined(CONFIG_AUFS_DLGT)
+/* cf. lookup_one_len() in linux/fs/namei.c */
+struct dentry *au_lkup_one(const char *name, struct dentry *parent, int len,
+ struct au_ndx *ndx)
+{
+ struct dentry *dentry;
+
+ LKTRTrace("%.*s/%.*s, ndx{%d, 0x%x}\n",
+ AuDLNPair(parent), len, name, !!ndx->nfsmnt, ndx->flags);
+
+ ndx->nd_file = NULL;
+ if (!ndx->nfsmnt)
+ dentry = au_lkup_one_dlgt(name, parent, len, ndx->flags);
+ else
+ dentry = au_lkup_hash(name, parent, len, ndx);
+
+ AuTraceErrPtr(dentry);
+ return dentry;
+}
+#endif /* CONFIG_AUFS_BR_NFS || CONFIG_AUFS_DLGT */
+
+struct au_lkup_one_args {
+ struct dentry **errp;
+ const char *name;
+ struct dentry *parent;
+ int len;
+ struct au_ndx *ndx;
+};
+
+static void au_call_lkup_one(void *args)
+{
+ struct au_lkup_one_args *a = args;
+ *a->errp = au_lkup_one(a->name, a->parent, a->len, a->ndx);
+}
+
+#define AuLkup_ALLOW_NEG 1
+#define AuLkup_DLGT (1 << 1)
+#define AuLkup_DIRPERM1 (1 << 2)
+#define au_ftest_lkup(flags, name) ((flags) & AuLkup_##name)
+#define au_fset_lkup(flags, name) { (flags) |= AuLkup_##name; }
+#define au_fclr_lkup(flags, name) { (flags) &= ~AuLkup_##name; }
+#ifndef CONFIG_AUFS_DLGT
+#undef AuLkup_DLGT
+#define AuLkup_DLGT 0
+#undef AuLkup_DIRPERM1
+#define AuLkup_DIRPERM1 0
+#endif
+
+struct au_do_lookup_args {
+ unsigned int flags;
+ mode_t type;
+ struct nameidata *nd;
+};
+
+/*
+ * returns positive/negative dentry, NULL or an error.
+ * NULL means whiteout-ed or not-found.
+ */
+static noinline_for_stack
+struct dentry *au_do_lookup(struct dentry *h_parent, struct dentry *dentry,
+ aufs_bindex_t bindex, struct qstr *wh_name,
+ struct au_do_lookup_args *args)
+{
+ struct dentry *h_dentry;
+ int wh_found, wh_able, opq, err;
+ struct inode *h_dir, *h_inode, *inode;
+ struct qstr *name;
+ struct super_block *sb;
+ unsigned int nd_flags;
+ struct au_ndx ndx = {
+ .flags = 0,
+ .nd = args->nd
+ };
+ const int allow_neg = au_ftest_lkup(args->flags, ALLOW_NEG);
+
+ LKTRTrace("%.*s/%.*s, b%d, {flags 0x%x, type 0%o, nd %d}\n",
+ AuDLNPair(h_parent), AuDLNPair(dentry), bindex,
+ args->flags, args->type, !!args->nd);
+ if (args->nd)
+ LKTRTrace("nd{0x%x}\n", args->nd->flags);
+ AuDebugOn(IS_ROOT(dentry));
+ h_dir = h_parent->d_inode;
+
+ nd_flags = 0;
+ wh_found = 0;
+ sb = dentry->d_sb;
+ ndx.nfsmnt = au_nfsmnt(sb, bindex);
+ if (unlikely(au_ftest_lkup(args->flags, DLGT)))
+ au_fset_ndx(ndx.flags, DLGT);
+ if (unlikely(au_ftest_lkup(args->flags, DIRPERM1)))
+ au_fset_ndx(ndx.flags, DIRPERM1);
+ LKTRTrace("nfsmnt %p\n", ndx.nfsmnt);
+ ndx.br = au_sbr(sb, bindex);
+ wh_able = au_br_whable(ndx.br->br_perm);
+ name = &dentry->d_name;
+ if (unlikely(wh_able))
+ wh_found = au_test_robr_wh(name, h_parent, wh_name,
+ /*try_sio*/0, &ndx);
+ //if (LktrCond) wh_found = -1;
+ h_dentry = ERR_PTR(wh_found);
+ if (!wh_found)
+ goto real_lookup;
+ if (unlikely(wh_found < 0))
+ goto out;
+
+ /* We found a whiteout */
+ //au_set_dbend(dentry, bindex);
+ au_set_dbwh(dentry, bindex);
+ if (!allow_neg)
+ return NULL; /* success */
+ if (unlikely(ndx.nd
+ && au_test_nfs(h_parent->d_sb)
+ && (ndx.nd->flags & LOOKUP_CREATE))) {
+ nd_flags = ndx.nd->flags;
+ ndx.nd->flags &= ~(LOOKUP_OPEN | LOOKUP_CREATE);
+ }
+
+ real_lookup:
+ /* do not superio. */
+ h_dentry = au_lkup_one(name->name, h_parent, name->len, &ndx);
+ //if (LktrCond) {dput(h_dentry); h_dentry = ERR_PTR(-1);}
+ if (IS_ERR(h_dentry))
+ goto out;
+ AuDebugOn(d_unhashed(h_dentry));
+ h_inode = h_dentry->d_inode;
+ if (!h_inode) {
+ if (!allow_neg)
+ goto out_neg;
+ } else if (wh_found
+ || (args->type && args->type != (h_inode->i_mode & S_IFMT)))
+ goto out_neg;
+
+ if (au_dbend(dentry) <= bindex)
+ au_set_dbend(dentry, bindex);
+ if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry))
+ au_set_dbstart(dentry, bindex);
+ au_set_h_dptr(dentry, bindex, h_dentry);
+
+ err = au_br_nfs_h_intent(ndx.nd_file, dentry, bindex, args->nd);
+ if (unlikely(err)) {
+ h_dentry = ERR_PTR(err);
+ goto out;
+ }
+
+ inode = dentry->d_inode;
+ if (!h_inode || !S_ISDIR(h_inode->i_mode) || !wh_able
+ || (inode && !S_ISDIR(inode->i_mode)))
+ goto out; /* success */
+
+ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);
+ opq = au_diropq_test(h_dentry, &ndx);
+ //if (LktrCond) opq = -1;
+ mutex_unlock(&h_inode->i_mutex);
+ if (opq > 0)
+ au_set_dbdiropq(dentry, bindex);
+ else if (unlikely(opq < 0)) {
+ au_set_h_dptr(dentry, bindex, NULL);
+ h_dentry = ERR_PTR(opq);
+ }
+ goto out;
+
+ out_neg:
+ dput(h_dentry);
+ h_dentry = NULL;
+ out:
+ if (unlikely(nd_flags))
+ ndx.nd->flags |= (nd_flags & (LOOKUP_OPEN | LOOKUP_CREATE));
+ AuTraceErrPtr(h_dentry);
+ return h_dentry;
+}
+
+/*
+ * returns the number of hidden positive dentries,
+ * otherwise an error.
+ * can be called at unlinking with @type is zero.
+ */
+int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type,
+ struct nameidata *nd)
+{
+ int npositive, err, isdir;
+ struct dentry *parent;
+ aufs_bindex_t bindex, btail, bdiropq;
+ const struct qstr *name = &dentry->d_name;
+ struct qstr whname;
+ struct super_block *sb;
+ unsigned int mnt_flags;
+ struct inode *inode;
+ struct au_do_lookup_args args = {
+ .type = type,
+ .nd = nd
+ };
+
+ LKTRTrace("%.*s, b%d, type 0%o\n", AuLNPair(name), bstart, type);
+ AuDebugOn(bstart < 0 || IS_ROOT(dentry));
+
+ /* dir may not be locked */
+ parent = dget_parent(dentry);
+
+ err = au_test_robr_shwh(dentry->d_sb, name);
+ if (unlikely(err))
+ goto out;
+
+ err = au_wh_name_alloc(name->name, name->len, &whname);
+ //if (LktrCond) {au_wh_name_free(&whname); err = -1;}
+ if (unlikely(err))
+ goto out;
+
+ sb = dentry->d_sb;
+ mnt_flags = au_mntflags(sb);
+ inode = dentry->d_inode;
+ isdir = (inode && S_ISDIR(inode->i_mode));
+ args.flags = 0;
+ if (unlikely(au_opt_test_dlgt(mnt_flags)))
+ au_fset_lkup(args.flags, DLGT);
+ if (unlikely(au_opt_test_dirperm1(mnt_flags)))
+ au_fset_lkup(args.flags, DIRPERM1);
+ if (!type)
+ au_fset_lkup(args.flags, ALLOW_NEG);
+ npositive = 0;
+ btail = au_dbtaildir(parent);
+ for (bindex = bstart; bindex <= btail; bindex++) {
+ struct dentry *h_parent, *h_dentry;
+ struct inode *h_inode, *h_dir;
+
+ h_dentry = au_h_dptr(dentry, bindex);
+ if (h_dentry) {
+ if (h_dentry->d_inode)
+ npositive++;
+ if (type != S_IFDIR)
+ break;
+ continue;
+ }
+ h_parent = au_h_dptr(parent, bindex);
+ if (!h_parent)
+ continue;
+ h_dir = h_parent->d_inode;
+ if (!h_dir || !S_ISDIR(h_dir->i_mode))
+ continue;
+
+ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT);
+ h_dentry = au_do_lookup(h_parent, dentry, bindex, &whname,
+ &args);
+ // do not dput for testing
+ //if (LktrCond) {h_dentry = ERR_PTR(-1);}
+ mutex_unlock(&h_dir->i_mutex);
+ err = PTR_ERR(h_dentry);
+ if (IS_ERR(h_dentry))
+ goto out_wh;
+ au_fclr_lkup(args.flags, ALLOW_NEG);
+
+ if (au_dbwh(dentry) >= 0)
+ break;
+ if (!h_dentry)
+ continue;
+ h_inode = h_dentry->d_inode;
+ if (!h_inode)
+ continue;
+ npositive++;
+ if (!args.type)
+ args.type = h_inode->i_mode & S_IFMT;
+ if (args.type != S_IFDIR)
+ break;
+ else if (isdir) {
+ /* the type of lowers may be different */
+ bdiropq = au_dbdiropq(dentry);
+ if (bdiropq >= 0 && bdiropq <= bindex)
+ break;
+ }
+ }
+
+ if (npositive) {
+ LKTRLabel(positive);
+ au_update_dbstart(dentry);
+ }
+ err = npositive;
+
+ out_wh:
+ au_wh_name_free(&whname);
+ out:
+ dput(parent);
+ AuTraceErr(err);
+ return err;
+}
+
+struct dentry *au_sio_lkup_one(const char *name, struct dentry *parent, int len,
+ struct au_ndx *ndx)
+{
+ struct dentry *dentry;
+ int wkq_err;
+
+ LKTRTrace("%.*s/%.*s\n", AuDLNPair(parent), len, name);
+
+ if (!au_test_h_perm_sio(parent->d_inode, MAY_EXEC,
+ au_ftest_ndx(ndx->flags, DLGT)))
+ dentry = au_lkup_one(name, parent, len, ndx);
+ else {
+ // ugly
+ unsigned int flags = ndx->flags;
+ struct au_lkup_one_args args = {
+ .errp = &dentry,
+ .name = name,
+ .parent = parent,
+ .len = len,
+ .ndx = ndx
+ };
+
+ au_fclr_ndx(ndx->flags, DLGT);
+ au_fclr_ndx(ndx->flags, DIRPERM1);
+ wkq_err = au_wkq_wait(au_call_lkup_one, &args, /*dlgt*/0);
+ if (unlikely(wkq_err))
+ dentry = ERR_PTR(wkq_err);
+ ndx->flags = flags;
+ }
+
+ AuTraceErrPtr(dentry);
+ return dentry;
+}
+
+/*
+ * lookup @dentry on @bindex which should be negative.
+ */
+int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex)
+{
+ int err;
+ struct dentry *parent, *h_parent, *h_dentry;
+ struct inode *h_dir;
+ struct au_ndx ndx = {
+ .flags = 0,
+ .nd = NULL,
+ //.br = NULL
+ };
+ struct super_block *sb;
+ unsigned int mnt_flags;
+
+ LKTRTrace("%.*s, b%d\n", AuDLNPair(dentry), bindex);
+ /* dir may not be locked */
+ parent = dget_parent(dentry);
+ AuDebugOn(!parent || !parent->d_inode
+ || !S_ISDIR(parent->d_inode->i_mode));
+ h_parent = au_h_dptr(parent, bindex);
+ AuDebugOn(!h_parent);
+ h_dir = h_parent->d_inode;
+ AuDebugOn(!h_dir || !S_ISDIR(h_dir->i_mode));
+
+ sb = dentry->d_sb;
+ mnt_flags = au_mntflags(sb);
+ ndx.nfsmnt = au_nfsmnt(sb, bindex);
+ if (unlikely(au_opt_test_dlgt(mnt_flags)))
+ au_fset_ndx(ndx.flags, DLGT);
+ if (unlikely(au_opt_test_dirperm1(mnt_flags)))
+ au_fset_ndx(ndx.flags, DIRPERM1);
+ h_dentry = au_sio_lkup_one(dentry->d_name.name, h_parent,
+ dentry->d_name.len, &ndx);
+ //if (LktrCond) {dput(h_dentry); h_dentry = ERR_PTR(-1);}
+ err = PTR_ERR(h_dentry);
+ if (IS_ERR(h_dentry))
+ goto out;
+ if (unlikely(h_dentry->d_inode)) {
+ err = -EIO;
+ AuIOErr("b%d %.*s should be negative.\n",
+ bindex, AuDLNPair(h_dentry));
+ dput(h_dentry);
+ goto out;
+ }
+
+ if (bindex < au_dbstart(dentry))
+ au_set_dbstart(dentry, bindex);
+ if (au_dbend(dentry) < bindex)
+ au_set_dbend(dentry, bindex);
+ au_set_h_dptr(dentry, bindex, h_dentry);
+ err = 0;
+
+ out:
+ dput(parent);
+ AuTraceErr(err);
+ return err;
+}
+
+/*
+ * returns the number of found hidden positive dentries,
+ * otherwise an error.
+ */
+int au_refresh_hdentry(struct dentry *dentry, mode_t type)
+{
+ int npositive, new_sz;
+ struct au_dinfo *dinfo;
+ struct super_block *sb;
+ struct dentry *parent;
+ aufs_bindex_t bindex, parent_bend, parent_bstart, bwh, bdiropq, bend;
+ struct au_hdentry *p;
+ au_gen_t sgen;
+
+ LKTRTrace("%.*s, type 0%o\n", AuDLNPair(dentry), type);
+ DiMustWriteLock(dentry);
+ sb = dentry->d_sb;
+ AuDebugOn(IS_ROOT(dentry));
+ sgen = au_sigen(sb);
+ parent = dget_parent(dentry);
+ AuDebugOn(au_digen(parent) != sgen
+ || au_iigen(parent->d_inode) != sgen);
+
+ npositive = -ENOMEM;
+ new_sz = sizeof(*dinfo->di_hdentry) * (au_sbend(sb) + 1);
+ dinfo = au_di(dentry);
+ p = au_kzrealloc(dinfo->di_hdentry, sizeof(*p) * (dinfo->di_bend + 1),
+ new_sz, GFP_KERNEL);
+ //p = NULL;
+ if (unlikely(!p))
+ goto out;
+ dinfo->di_hdentry = p;
+
+ bend = dinfo->di_bend;
+ bwh = dinfo->di_bwh;
+ bdiropq = dinfo->di_bdiropq;
+ p += dinfo->di_bstart;
+ for (bindex = dinfo->di_bstart; bindex <= bend; bindex++, p++) {
+ struct dentry *hd, *hdp;
+ struct au_hdentry tmp, *q;
+ aufs_bindex_t new_bindex;
+
+ hd = p->hd_dentry;
+ if (!hd)
+ continue;
+ hdp = dget_parent(hd);
+ if (hdp == au_h_dptr(parent, bindex)) {
+ dput(hdp);
+ continue;
+ }
+
+ new_bindex = au_find_dbindex(parent, hdp);
+ dput(hdp);
+ AuDebugOn(new_bindex == bindex);
+ if (dinfo->di_bwh == bindex)
+ bwh = new_bindex;
+ if (dinfo->di_bdiropq == bindex)
+ bdiropq = new_bindex;
+ if (new_bindex < 0) { // test here
+ au_hdput(p, /*do_free*/0);
+ p->hd_dentry = NULL;
+ continue;
+ }
+ /* swap two hidden dentries, and loop again */
+ q = dinfo->di_hdentry + new_bindex;
+ tmp = *q;
+ *q = *p;
+ *p = tmp;
+ if (tmp.hd_dentry) {
+ bindex--;
+ p--;
+ }
+ }
+
+ // test here
+ dinfo->di_bwh = -1;
+ if (unlikely(bwh >= 0 && bwh <= au_sbend(sb) && au_sbr_whable(sb, bwh)))
+ dinfo->di_bwh = bwh;
+ dinfo->di_bdiropq = -1;
+ if (unlikely(bdiropq >= 0 && bdiropq <= au_sbend(sb)
+ && au_sbr_whable(sb, bdiropq)))
+ dinfo->di_bdiropq = bdiropq;
+ parent_bend = au_dbend(parent);
+ p = dinfo->di_hdentry;
+ for (bindex = 0; bindex <= parent_bend; bindex++, p++)
+ if (p->hd_dentry) {
+ dinfo->di_bstart = bindex;
+ break;
+ }
+ p = dinfo->di_hdentry + parent_bend;
+ //for (bindex = parent_bend; bindex > dinfo->di_bstart; bindex--, p--)
+ for (bindex = parent_bend; bindex >= 0; bindex--, p--)
+ if (p->hd_dentry) {
+ dinfo->di_bend = bindex;
+ break;
+ }
+
+ npositive = 0;
+ parent_bstart = au_dbstart(parent);
+ if (type != S_IFDIR && dinfo->di_bstart == parent_bstart)
+ goto out_dgen; /* success */
+
+ npositive = au_lkup_dentry(dentry, parent_bstart, type, /*nd*/NULL);
+ //if (LktrCond) npositive = -1;
+ if (npositive < 0)
+ goto out;
+ if (unlikely(dinfo->di_bwh >= 0 && dinfo->di_bwh <= dinfo->di_bstart))
+ d_drop(dentry);
+
+ out_dgen:
+ au_update_digen(dentry);
+ out:
+ dput(parent);
+ AuTraceErr(npositive);
+ return npositive;
+}
+
+#ifdef CONFIG_AUFS_FAKE_DM
+
+static int au_lock_nd(struct dentry *dentry, struct nameidata *nd)
+{
+ return 0;
+}
+
+static void au_unlock_nd(int locked, struct nameidata *nd)
+{
+ /* empty */
+}
+
+#else
+
+static int au_lock_nd(struct dentry *dentry, struct nameidata *nd)
+{
+ int locked = 0;
+ if (nd && dentry != nd->path.dentry) {
+ di_read_lock_parent(nd->path.dentry, 0);
+ locked = 1;
+ }
+ return locked;
+}
+
+static void au_unlock_nd(int locked, struct nameidata *nd)
+{
+ if (locked)
+ di_read_unlock(nd->path.dentry, 0);
+}
+
+#endif /* CONFIG_AUFS_FAKE_DM */
+
+//#define TestingFuse
+static noinline_for_stack int
+au_do_h_d_reval(struct dentry *dentry, aufs_bindex_t bindex,
+ struct nameidata *nd, struct dentry *h_dentry)
+{
+ int err, valid, e;
+ int (*reval)(struct dentry *, struct nameidata *);
+ struct super_block *sb;
+ struct nameidata fake_nd, *p;
+
+ LKTRTrace("%.*s, b%d, nd %d\n", AuDLNPair(dentry), bindex, !!nd);
+
+ err = 0;
+ reval = NULL;
+ if (h_dentry->d_op)
+ reval = h_dentry->d_op->d_revalidate;
+ if (!reval)
+ goto out;
+
+ sb = dentry->d_sb;
+ if (nd) {
+ memcpy(&fake_nd, nd, sizeof(*nd));
+ err = au_fake_intent(&fake_nd, au_sbr_perm(sb, bindex));
+ if (unlikely(err)) {
+ err = -EINVAL;
+ goto out;
+ }
+ }
+ p = au_fake_dm(&fake_nd, nd, sb, bindex);
+ AuDebugOn(IS_ERR(p));
+ AuDebugOn(nd && p != &fake_nd);
+ LKTRTrace("b%d\n", bindex);
+
+ /* it may return tri-state */
+ valid = reval(h_dentry, p);
+ if (unlikely(valid < 0))
+ err = valid;
+ else if (!valid)
+ err = -EINVAL;
+ else
+ AuDebugOn(err);
+
+ if (p) {
+ AuDebugOn(!nd);
+ e = au_hin_after_reval(p, dentry, bindex, nd->intent.open.file);
+#ifndef TestingFuse
+ au_update_fuse_h_inode(p->path.mnt, h_dentry); /*ignore*/
+#endif
+ if (unlikely(e && !err))
+ err = e;
+ }
+#ifndef TestingFuse
+ else
+ au_update_fuse_h_inode(NULL, h_dentry); /*ignore*/
+#endif
+ au_fake_dm_release(p);
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+static noinline_for_stack int
+h_d_revalidate(struct dentry *dentry, struct inode *inode,
+ struct nameidata *nd, int do_udba)
+{
+ int err, plus, locked, unhashed, is_root, h_plus;
+ aufs_bindex_t bindex, btail, bstart, ibs, ibe;
+ struct super_block *sb;
+ struct inode *first, *h_inode, *h_cached_inode;
+ umode_t mode, h_mode;
+ struct dentry *h_dentry;
+ struct qstr *name;
+
+ LKTRTrace("%.*s, nd %d\n", AuDLNPair(dentry), !!nd);
+ AuDebugOn(inode && au_digen(dentry) != au_iigen(inode));
+
+ err = 0;
+ sb = dentry->d_sb;
+ plus = 0;
+ mode = 0;
+ first = NULL;
+ ibs = -1;
+ ibe = -1;
+ unhashed = d_unhashed(dentry);
+ is_root = IS_ROOT(dentry);
+ name = &dentry->d_name;
+
+ /*
+ * Theoretically, REVAL test should be unnecessary in case of INOTIFY.
+ * But inotify doesn't fire some necessary events,
+ * IN_ATTRIB for atime/nlink/pageio
+ * IN_DELETE for NFS dentry
+ * Let's do REVAL test too.
+ */
+ if (do_udba && inode) {
+ mode = (inode->i_mode & S_IFMT);
+ plus = (inode->i_nlink > 0);
+ first = au_h_iptr(inode, au_ibstart(inode));
+ ibs = au_ibstart(inode);
+ ibe = au_ibend(inode);
+ }
+
+ bstart = au_dbstart(dentry);
+ btail = bstart;
+ if (inode && S_ISDIR(inode->i_mode))
+ btail = au_dbtaildir(dentry);
+ locked = au_lock_nd(dentry, nd);
+ for (bindex = bstart; bindex <= btail; bindex++) {
+ h_dentry = au_h_dptr(dentry, bindex);
+ if (!h_dentry)
+ continue;
+
+ LKTRTrace("b%d, %.*s\n", bindex, AuDLNPair(h_dentry));
+#ifdef TestingFuse
+ /* force re-lookup for fuse, in order to update attributes */
+ if (unlikely(au_test_fuse(h_dentry->d_sb)))
+ goto err;
+#endif
+
+ if (unlikely(do_udba
+ && !is_root
+ && (unhashed != d_unhashed(h_dentry)
+//#if 1
+ || name->len != h_dentry->d_name.len
+ || memcmp(name->name, h_dentry->d_name.name,
+ name->len)
+//#endif
+ ))) {
+ LKTRTrace("unhash 0x%x 0x%x, %.*s %.*s\n",
+ unhashed, d_unhashed(h_dentry),
+ AuDLNPair(dentry), AuDLNPair(h_dentry));
+ goto err;
+ }
+
+ err = au_do_h_d_reval(dentry, bindex, nd, h_dentry);
+ if (unlikely(err))
+ /* do not goto err, to keep the errno */
+ break;
+
+ if (unlikely(!do_udba))
+ continue;
+
+ /* UDBA tests */
+ h_inode = h_dentry->d_inode;
+ if (unlikely(!!inode != !!h_inode)) {
+ //AuDbg("here\n");
+ goto err;
+ }
+
+ h_plus = plus;
+ h_mode = mode;
+ h_cached_inode = h_inode;
+ if (h_inode) {
+ h_mode = (h_inode->i_mode & S_IFMT);
+ h_plus = (h_inode->i_nlink > 0);
+ //AuDbgInode(inode);
+ //AuDbgInode(h_inode);
+ }
+ if (inode && ibs <= bindex && bindex <= ibe)
+ h_cached_inode = au_h_iptr(inode, bindex);
+
+ LKTRTrace("{%d, 0%o, %d}, h{%d, 0%o, %d}\n",
+ plus, mode, !!h_cached_inode,
+ h_plus, h_mode, !!h_inode);
+ if (unlikely(plus != h_plus
+ || mode != h_mode
+ || h_cached_inode != h_inode))
+ goto err;
+ continue;
+
+ err:
+ err = -EINVAL;
+ break;
+ }
+ au_unlock_nd(locked, nd);
+
+ /*
+ * judging by timestamps is meaningless since some filesystem uses
+ * CURRENT_TIME_SEC instead of CURRENT_TIME.
+ */
+ /*
+ * NFS may stop IN_DELETE because of DCACHE_NFSFS_RENAMED.
+ */
+
+#if 0 // debug
+ if (!err)
+ au_update_from_fuse(inode);
+ if (unlikely(!err && udba && first))
+ au_cpup_attr_all(inode);
+#endif
+
+ AuTraceErr(err);
+ return err;
+}
+
+static noinline_for_stack int
+simple_reval_dpath(struct dentry *dentry, au_gen_t sgen)
+{
+ int err;
+ mode_t type;
+ struct dentry *parent;
+ struct inode *inode;
+
+ LKTRTrace("%.*s, sgen %d\n", AuDLNPair(dentry), sgen);
+ SiMustAnyLock(dentry->d_sb);
+ DiMustWriteLock(dentry);
+ inode = dentry->d_inode;
+ AuDebugOn(!inode);
+
+ if (au_digen(dentry) == sgen && au_iigen(inode) == sgen)
+ return 0;
+
+ parent = dget_parent(dentry);
+ di_read_lock_parent(parent, AuLock_IR);
+ AuDebugOn(au_digen(parent) != sgen
+ || au_iigen(parent->d_inode) != sgen);
+#ifdef CONFIG_AUFS_DEBUG
+ {
+ int i, j;
+ struct au_dcsub_pages dpages;
+ struct au_dpage *dpage;
+ struct dentry **dentries;
+
+ err = au_dpages_init(&dpages, GFP_TEMPORARY);
+ AuDebugOn(err);
+ err = au_dcsub_pages_rev(&dpages, parent, /*do_include*/1, NULL,
+ NULL);
+ AuDebugOn(err);
+ for (i = dpages.ndpage - 1; !err && i >= 0; i--) {
+ dpage = dpages.dpages + i;
+ dentries = dpage->dentries;
+ for (j = dpage->ndentry - 1; !err && j >= 0; j--)
+ AuDebugOn(au_digen(dentries[j]) != sgen);
+ }
+ au_dpages_free(&dpages);
+ }
+#endif
+ type = (inode->i_mode & S_IFMT);
+ /* returns a number of positive dentries */
+ err = au_refresh_hdentry(dentry, type);
+ if (err >= 0)
+ err = au_refresh_hinode(inode, dentry);
+ di_read_unlock(parent, AuLock_IR);
+ dput(parent);
+ AuTraceErr(err);
+ return err;
+}
+
+int au_reval_dpath(struct dentry *dentry, au_gen_t sgen)
+{
+ int err;
+ struct dentry *d, *parent;
+ struct inode *inode;
+
+ LKTRTrace("%.*s, sgen %d\n", AuDLNPair(dentry), sgen);
+ AuDebugOn(!dentry->d_inode);
+ DiMustWriteLock(dentry);
+
+ if (!au_ftest_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIRS))
+ return simple_reval_dpath(dentry, sgen);
+
+ /* slow loop, keep it simple and stupid */
+ /* cf: au_cpup_dirs() */
+ err = 0;
+ parent = NULL;
+ while (au_digen(dentry) != sgen || au_iigen(dentry->d_inode) != sgen) {
+ d = dentry;
+ while (1) {
+ dput(parent);
+ parent = dget_parent(d);
+ if (au_digen(parent) == sgen
+ && au_iigen(parent->d_inode) == sgen)
+ break;
+ d = parent;
+ }
+
+ inode = d->d_inode;
+ if (d != dentry) {
+ //mutex_lock(&inode->i_mutex);
+ di_write_lock_child(d);
+ }
+
+ /* someone might update our dentry while we were sleeping */
+ if (au_digen(d) != sgen || au_iigen(d->d_inode) != sgen) {
+ di_read_lock_parent(parent, AuLock_IR);
+ /* returns a number of positive dentries */
+ err = au_refresh_hdentry(d, inode->i_mode & S_IFMT);
+ //err = -1;
+ if (err >= 0)
+ err = au_refresh_hinode(inode, d);
+ //err = -1;
+ di_read_unlock(parent, AuLock_IR);
+ }
+
+ if (d != dentry) {
+ di_write_unlock(d);
+ //mutex_unlock(&inode->i_mutex);
+ }
+ dput(parent);
+ if (unlikely(err))
+ break;
+ }
+
+ AuTraceErr(err);
+ return err;
+}
+
+/*
+ * THIS IS A BOOLEAN FUNCTION: returns 1 if valid, 0 otherwise.
+ * nfsd passes NULL as nameidata.
+ */
+static int aufs_d_revalidate(struct dentry *dentry, struct nameidata *nd)
+{
+ int valid, err, do_udba;
+ struct super_block *sb;
+ au_gen_t sgen;
+ struct inode *inode;
+ struct nameidata tmp_nd, *ndp;
+
+ LKTRTrace("dentry %.*s\n", AuDLNPair(dentry));
+ if (nd && nd->path.dentry)
+ LKTRTrace("nd{%.*s, 0x%x}\n",
+ AuDLNPair(nd->path.dentry), nd->flags);
+ //dir case: AuDebugOn(dentry->d_parent != nd->dentry);
+ //remove failure case:AuDebugOn(!IS_ROOT(dentry) && d_unhashed(dentry));
+ AuDebugOn(!dentry->d_fsdata);
+ //AuDbgDentry(dentry);
+
+ err = -EINVAL;
+ inode = dentry->d_inode;
+ sb = dentry->d_sb;
+ si_read_lock(sb, AuLock_FLUSH);
+
+ sgen = au_sigen(sb);
+ if (au_digen(dentry) == sgen)
+ di_read_lock_child(dentry, !AuLock_IR);
+ else {
+ AuDebugOn(IS_ROOT(dentry));
+#ifdef ForceInotify
+ AuDbg("UDBA or digen, %.*s\n", AuDLNPair(dentry));
+#endif
+ di_write_lock_child(dentry);
+ if (inode)
+ err = au_reval_dpath(dentry, sgen);
+ //err = -1;
+ di_downgrade_lock(dentry, AuLock_IR);
+ if (unlikely(err))
+ goto out;
+ if (inode)
+ ii_read_unlock(inode);
+ AuDebugOn(au_digen(dentry) != sgen);
+ }
+
+ if (inode) {
+ if (au_iigen(inode) == sgen)
+ ii_read_lock_child(inode);
+ else {
+ AuDebugOn(IS_ROOT(dentry));
+#ifdef ForceInotify
+ AuDbg("UDBA or survived, %.*s\n", AuDLNPair(dentry));
+ //au_debug_on();
+ //AuDbgInode(inode);
+ //au_debug_off();
+#endif
+ ii_write_lock_child(inode);
+ err = au_refresh_hinode(inode, dentry);
+ ii_downgrade_lock(inode);
+ if (unlikely(err))
+ goto out;
+ AuDebugOn(au_iigen(inode) != sgen);
+ }
+ }
+
+#if 0 // fix it
+ /* parent dir i_nlink is not updated in the case of setattr */
+ if (S_ISDIR(inode->i_mode)) {
+ mutex_lock(&inode->i_mutex);
+ ii_write_lock(inode);
+ au_cpup_attr_nlink(inode);
+ ii_write_unlock(inode);
+ mutex_unlock(&inode->i_mutex);
+ }
+#endif
+
+ AuDebugOn(au_digen(dentry) != sgen);
+ AuDebugOn(inode && au_iigen(inode) != sgen);
+ err = -EINVAL;
+ do_udba = !au_opt_test(au_mntflags(sb), UDBA_NONE);
+ if (do_udba && inode) {
+ aufs_bindex_t bstart = au_ibstart(inode);
+ if (bstart >= 0
+ && au_test_higen(inode, au_h_iptr(inode, bstart)))
+ goto out;
+ }
+ ndp = au_dup_nd(au_sbi(sb), &tmp_nd, nd);
+ err = h_d_revalidate(dentry, inode, ndp, do_udba);
+ //err = -1;
+
+ out:
+#if 0
+ AuDbgDentry(dentry);
+ if (nd && (nd->flags & LOOKUP_OPEN)) {
+ LKTRTrace("intent.open.file %p\n", nd->intent.open.file);
+ if (nd->intent.open.file && nd->intent.open.file->f_dentry)
+ AuDbgFile(nd->intent.open.file);
+ }
+#endif
+ au_store_fmode_exec(nd, inode);
+
+ if (inode)
+ ii_read_unlock(inode);
+ di_read_unlock(dentry, !AuLock_IR);
+ si_read_unlock(sb);
+ AuTraceErr(err);
+ valid = !err;
+ if (!valid)
+ LKTRTrace("%.*s invalid\n", AuDLNPair(dentry));
+ return valid;
+}
+
+static void aufs_d_release(struct dentry *dentry)
+{
+ struct au_dinfo *dinfo;
+ aufs_bindex_t bend, bindex;
+
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+ AuDebugOn(!d_unhashed(dentry));
+
+ dinfo = dentry->d_fsdata;
+ if (unlikely(!dinfo))
+ return;
+
+ /* dentry may not be revalidated */
+ bindex = dinfo->di_bstart;
+ if (bindex >= 0) {
+ struct au_hdentry *p;
+ bend = dinfo->di_bend;
+ AuDebugOn(bend < bindex);
+ p = dinfo->di_hdentry + bindex;
+ while (bindex++ <= bend) {
+ if (p->hd_dentry)
+ au_hdput(p, /*do_free*/1);
+ p++;
+ }
+ }
+ kfree(dinfo->di_hdentry);
+ au_cache_free_dinfo(dinfo);
+}
+
+struct dentry_operations aufs_dop = {
+ .d_revalidate = aufs_d_revalidate,
+ .d_release = aufs_d_release,
+ /* never use d_delete, especially in case of nfs server */
+ //.d_delete = aufs_d_delete
+ //.d_iput = aufs_d_iput
+};
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 36/67] aufs dentry private data
2008-05-16 14:32 ` [PATCH 35/67] aufs lookup and dentry operations, source hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 37/67] aufs file operations hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs dentry private data
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/dinfo.c | 425 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 425 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/dinfo.c b/fs/aufs/dinfo.c
new file mode 100644
index 0000000..2022cef
--- /dev/null
+++ b/fs/aufs/dinfo.c
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * dentry private data
+ *
+ * $Id: dinfo.c,v 1.3 2008/04/28 03:04:23 sfjro Exp $
+ */
+
+#include "aufs.h"
+
+int au_alloc_dinfo(struct dentry *dentry)
+{
+ struct au_dinfo *dinfo;
+ struct super_block *sb;
+ int nbr;
+
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+ AuDebugOn(dentry->d_fsdata);
+
+ dinfo = au_cache_alloc_dinfo();
+ //if (LktrCond) {au_cache_free_dinfo(dinfo); dinfo = NULL;}
+ if (dinfo) {
+ sb = dentry->d_sb;
+ nbr = au_sbend(sb) + 1;
+ if (unlikely(nbr <= 0))
+ nbr = 1;
+ dinfo->di_hdentry = kcalloc(nbr, sizeof(*dinfo->di_hdentry),
+ GFP_KERNEL);
+ //if (LktrCond)
+ //{kfree(dinfo->di_hdentry); dinfo->di_hdentry = NULL;}
+ if (dinfo->di_hdentry) {
+ au_h_dentry_init_all(dinfo->di_hdentry, nbr);
+ atomic_set(&dinfo->di_generation, au_sigen(sb));
+ //smp_mb(); /* atomic_set */
+ au_rw_init_wlock_nested(&dinfo->di_rwsem,
+ AuLsc_DI_PARENT);
+ dinfo->di_bstart = -1;
+ dinfo->di_bend = -1;
+ dinfo->di_bwh = -1;
+ dinfo->di_bdiropq = -1;
+
+ dentry->d_fsdata = dinfo;
+ dentry->d_op = &aufs_dop;
+ return 0; /* success */
+ }
+ au_cache_free_dinfo(dinfo);
+ }
+ AuTraceErr(-ENOMEM);
+ return -ENOMEM;
+}
+
+struct au_dinfo *au_di(struct dentry *dentry)
+{
+ struct au_dinfo *dinfo = dentry->d_fsdata;
+ AuDebugOn(!dinfo
+ || !dinfo->di_hdentry
+ /* || au_sbi(dentry->d_sb)->si_bend < dinfo->di_bend */
+ || dinfo->di_bend < dinfo->di_bstart
+ /* dbwh can be outside of this range */
+ || (0 <= dinfo->di_bdiropq
+ && (dinfo->di_bdiropq < dinfo->di_bstart
+ /* || dinfo->di_bend < dinfo->di_bdiropq */))
+ );
+ return dinfo;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void do_ii_write_lock(struct inode *inode, unsigned int lsc)
+{
+ switch (lsc) {
+ case AuLsc_DI_CHILD:
+ ii_write_lock_child(inode);
+ break;
+ case AuLsc_DI_CHILD2:
+ ii_write_lock_child2(inode);
+ break;
+ case AuLsc_DI_CHILD3:
+ ii_write_lock_child3(inode);
+ break;
+ case AuLsc_DI_PARENT:
+ //AuDebugOn(!S_ISDIR(inode->i_mode));
+ ii_write_lock_parent(inode);
+ break;
+ case AuLsc_DI_PARENT2:
+ //AuDebugOn(!S_ISDIR(inode->i_mode));
+ ii_write_lock_parent2(inode);
+ break;
+ case AuLsc_DI_PARENT3:
+ //AuDebugOn(!S_ISDIR(inode->i_mode));
+ ii_write_lock_parent3(inode);
+ break;
+ default:
+ BUG();
+ }
+}
+
+static void do_ii_read_lock(struct inode *inode, unsigned int lsc)
+{
+ switch (lsc) {
+ case AuLsc_DI_CHILD:
+ ii_read_lock_child(inode);
+ break;
+ case AuLsc_DI_CHILD2:
+ ii_read_lock_child2(inode);
+ break;
+ case AuLsc_DI_CHILD3:
+ ii_read_lock_child3(inode);
+ break;
+ case AuLsc_DI_PARENT:
+ //AuDebugOn(!S_ISDIR(inode->i_mode));
+ ii_read_lock_parent(inode);
+ break;
+ case AuLsc_DI_PARENT2:
+ //AuDebugOn(!S_ISDIR(inode->i_mode));
+ ii_read_lock_parent2(inode);
+ break;
+ case AuLsc_DI_PARENT3:
+ //AuDebugOn(!S_ISDIR(inode->i_mode));
+ ii_read_lock_parent3(inode);
+ break;
+ default:
+ BUG();
+ }
+}
+
+void di_read_lock(struct dentry *d, int flags, unsigned int lsc)
+{
+ SiMustAnyLock(d->d_sb);
+ // todo: always nested?
+ au_rw_read_lock_nested(&au_di(d)->di_rwsem, lsc);
+ if (d->d_inode) {
+ if (au_ftest_lock(flags, IW))
+ do_ii_write_lock(d->d_inode, lsc);
+ else if (au_ftest_lock(flags, IR))
+ do_ii_read_lock(d->d_inode, lsc);
+ }
+}
+
+void di_read_unlock(struct dentry *d, int flags)
+{
+ SiMustAnyLock(d->d_sb);
+ if (d->d_inode) {
+ if (au_ftest_lock(flags, IW))
+ ii_write_unlock(d->d_inode);
+ else if (au_ftest_lock(flags, IR))
+ ii_read_unlock(d->d_inode);
+ }
+ au_rw_read_unlock(&au_di(d)->di_rwsem);
+}
+
+void di_downgrade_lock(struct dentry *d, int flags)
+{
+ SiMustAnyLock(d->d_sb);
+ au_rw_dgrade_lock(&au_di(d)->di_rwsem);
+ if (d->d_inode && au_ftest_lock(flags, IR))
+ ii_downgrade_lock(d->d_inode);
+}
+
+void di_write_lock(struct dentry *d, unsigned int lsc)
+{
+ SiMustAnyLock(d->d_sb);
+ // todo: always nested?
+ au_rw_write_lock_nested(&au_di(d)->di_rwsem, lsc);
+ if (d->d_inode)
+ do_ii_write_lock(d->d_inode, lsc);
+}
+
+void di_write_unlock(struct dentry *d)
+{
+ SiMustAnyLock(d->d_sb);
+ if (d->d_inode)
+ ii_write_unlock(d->d_inode);
+ au_rw_write_unlock(&au_di(d)->di_rwsem);
+}
+
+void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir)
+{
+ AuTraceEnter();
+ AuDebugOn(d1 == d2
+ || d1->d_inode == d2->d_inode
+ || d1->d_sb != d2->d_sb);
+
+ if (isdir && au_test_subdir(d1, d2)) {
+ di_write_lock_child(d1);
+ di_write_lock_child2(d2);
+ } else {
+ /* there should be no races */
+ di_write_lock_child(d2);
+ di_write_lock_child2(d1);
+ }
+}
+
+void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir)
+{
+ AuTraceEnter();
+ AuDebugOn(d1 == d2
+ || d1->d_inode == d2->d_inode
+ || d1->d_sb != d2->d_sb);
+
+ if (isdir && au_test_subdir(d1, d2)) {
+ di_write_lock_parent(d1);
+ di_write_lock_parent2(d2);
+ } else {
+ /* there should be no races */
+ di_write_lock_parent(d2);
+ di_write_lock_parent2(d1);
+ }
+}
+
+void di_write_unlock2(struct dentry *d1, struct dentry *d2)
+{
+ di_write_unlock(d1);
+ if (d1->d_inode == d2->d_inode)
+ au_rw_write_unlock(&au_di(d2)->di_rwsem);
+ else
+ di_write_unlock(d2);
+}
+
+/* ---------------------------------------------------------------------- */
+
+struct dentry *au_h_dptr(struct dentry *dentry, aufs_bindex_t bindex)
+{
+ struct dentry *d;
+
+ DiMustAnyLock(dentry);
+ if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry))
+ return NULL;
+ AuDebugOn(bindex < 0
+ /* || bindex > au_sbend(dentry->d_sb) */);
+ d = au_di(dentry)->di_hdentry[0 + bindex].hd_dentry;
+ AuDebugOn(d && (atomic_read(&d->d_count) <= 0));
+ return d;
+}
+
+aufs_bindex_t au_dbtail(struct dentry *dentry)
+{
+ aufs_bindex_t bend, bwh;
+
+ bend = au_dbend(dentry);
+ if (0 <= bend) {
+ bwh = au_dbwh(dentry);
+ //AuDebugOn(bend < bwh);
+ if (!bwh)
+ return bwh;
+ if (0 < bwh && bwh < bend)
+ return bwh - 1;
+ }
+ return bend;
+}
+
+aufs_bindex_t au_dbtaildir(struct dentry *dentry)
+{
+ aufs_bindex_t bend, bopq;
+
+ AuDebugOn(dentry->d_inode
+ && dentry->d_inode->i_mode
+ && !S_ISDIR(dentry->d_inode->i_mode));
+
+ bend = au_dbtail(dentry);
+ if (0 <= bend) {
+ bopq = au_dbdiropq(dentry);
+ AuDebugOn(bend < bopq);
+ if (0 <= bopq && bopq < bend)
+ bend = bopq;
+ }
+ return bend;
+}
+
+#if 0 // rfu
+aufs_bindex_t au_dbtail_generic(struct dentry *dentry)
+{
+ struct inode *inode;
+
+ inode = dentry->d_inode;
+ if (inode && S_ISDIR(inode->i_mode))
+ return au_dbtaildir(dentry);
+ else
+ return au_dbtail(dentry);
+}
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+void au_set_dbdiropq(struct dentry *dentry, aufs_bindex_t bindex)
+{
+ DiMustWriteLock(dentry);
+ AuDebugOn(au_sbend(dentry->d_sb) < bindex);
+ AuDebugOn((bindex >= 0
+ && (bindex < au_dbstart(dentry)
+ || au_dbend(dentry) < bindex))
+ || (dentry->d_inode
+ && dentry->d_inode->i_mode
+ && !S_ISDIR(dentry->d_inode->i_mode)));
+ au_di(dentry)->di_bdiropq = bindex;
+}
+
+void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex,
+ struct dentry *h_dentry)
+{
+ struct au_hdentry *hd = au_di(dentry)->di_hdentry + bindex;
+ DiMustWriteLock(dentry);
+ AuDebugOn(bindex < au_di(dentry)->di_bstart
+ || bindex > au_di(dentry)->di_bend
+ || (h_dentry && atomic_read(&h_dentry->d_count) <= 0)
+ || (h_dentry && hd->hd_dentry)
+ );
+ if (hd->hd_dentry)
+ au_hdput(hd, /*do_free*/0);
+ hd->hd_dentry = h_dentry;
+}
+
+/* ---------------------------------------------------------------------- */
+
+void au_update_dbrange(struct dentry *dentry, int do_put_zero)
+{
+ struct au_dinfo *dinfo;
+ aufs_bindex_t bindex;
+ struct dentry *h_d;
+
+ LKTRTrace("%.*s, %d\n", AuDLNPair(dentry), do_put_zero);
+ DiMustWriteLock(dentry);
+
+ dinfo = au_di(dentry);
+ if (unlikely(!dinfo) || dinfo->di_bstart < 0)
+ return;
+
+ if (do_put_zero) {
+ for (bindex = dinfo->di_bstart; bindex <= dinfo->di_bend;
+ bindex++) {
+ h_d = dinfo->di_hdentry[0 + bindex].hd_dentry;
+ if (h_d && !h_d->d_inode)
+ au_set_h_dptr(dentry, bindex, NULL);
+ }
+ }
+
+ dinfo->di_bstart = -1;
+ while (++dinfo->di_bstart <= dinfo->di_bend)
+ if (dinfo->di_hdentry[0 + dinfo->di_bstart].hd_dentry)
+ break;
+ if (dinfo->di_bstart > dinfo->di_bend) {
+ dinfo->di_bstart = -1;
+ dinfo->di_bend = -1;
+ return;
+ }
+
+ dinfo->di_bend++;
+ while (0 <= --dinfo->di_bend)
+ if (dinfo->di_hdentry[0 + dinfo->di_bend].hd_dentry)
+ break;
+ AuDebugOn(dinfo->di_bstart > dinfo->di_bend || dinfo->di_bend < 0);
+}
+
+void au_update_dbstart(struct dentry *dentry)
+{
+ aufs_bindex_t bindex,
+ bstart = au_dbstart(dentry),
+ bend = au_dbend(dentry);
+ struct dentry *h_dentry;
+
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+ DiMustWriteLock(dentry);
+
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ h_dentry = au_h_dptr(dentry, bindex);
+ if (!h_dentry)
+ continue;
+ if (h_dentry->d_inode) {
+ au_set_dbstart(dentry, bindex);
+ return;
+ }
+ au_set_h_dptr(dentry, bindex, NULL);
+ }
+ //au_set_dbstart(dentry, -1);
+ //au_set_dbend(dentry, -1);
+}
+
+void au_update_dbend(struct dentry *dentry)
+{
+ aufs_bindex_t bindex,
+ bstart = au_dbstart(dentry),
+ bend = au_dbend(dentry);
+ struct dentry *h_dentry;
+
+ DiMustWriteLock(dentry);
+ for (bindex = bend; bindex <= bstart; bindex--) {
+ h_dentry = au_h_dptr(dentry, bindex);
+ if (!h_dentry)
+ continue;
+ if (h_dentry->d_inode) {
+ au_set_dbend(dentry, bindex);
+ return;
+ }
+ au_set_h_dptr(dentry, bindex, NULL);
+ }
+ //au_set_dbstart(dentry, -1);
+ //au_set_dbend(dentry, -1);
+}
+
+int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry)
+{
+ aufs_bindex_t bindex, bend;
+
+ bend = au_dbend(dentry);
+ for (bindex = au_dbstart(dentry); bindex <= bend; bindex++)
+ if (au_h_dptr(dentry, bindex) == h_dentry)
+ return bindex;
+ return -1;
+}
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 37/67] aufs file operations
2008-05-16 14:32 ` [PATCH 36/67] aufs dentry private data hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 38/67] aufs handling file/dir, and address_space operation hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs file operations
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/file.h | 246 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 246 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/file.h b/fs/aufs/file.h
new file mode 100644
index 0000000..33a1036
--- /dev/null
+++ b/fs/aufs/file.h
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * file operations
+ *
+ * $Id: file.h,v 1.2 2008/04/21 01:35:14 sfjro Exp $
+ */
+
+#ifndef __AUFS_FILE_H__
+#define __AUFS_FILE_H__
+
+#ifdef __KERNEL__
+
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/aufs_type.h>
+#include "dentry.h"
+#include "misc.h"
+#include "super.h"
+
+/* ---------------------------------------------------------------------- */
+
+struct au_branch;
+struct au_hfile {
+ struct file *hf_file;
+ struct au_branch *hf_br;
+};
+
+struct au_vdir;
+struct au_finfo {
+ atomic_t fi_generation;
+
+ struct au_rwsem fi_rwsem;
+ struct au_hfile *fi_hfile;
+ aufs_bindex_t fi_bstart, fi_bend;
+
+ union {
+ struct vm_operations_struct *fi_h_vm_ops;
+ struct au_vdir *fi_vdir_cache;
+ };
+};
+
+/* ---------------------------------------------------------------------- */
+
+/* file.c */
+extern struct address_space_operations aufs_aop;
+unsigned int au_file_roflags(unsigned int flags);
+struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags,
+ struct file *file);
+int au_do_open(struct inode *inode, struct file *file,
+ int (*open)(struct file *file, int flags));
+int au_reopen_nondir(struct file *file);
+int au_ready_to_write(struct file *file, loff_t len);
+int au_reval_and_lock_finfo(struct file *file, int (*reopen)(struct file *file),
+ int wlock, int locked);
+
+/* f_op.c */
+extern struct file_operations aufs_file_fop;
+int aufs_flush(struct file *file, fl_owner_t id);
+
+/* finfo.c */
+struct au_finfo *au_fi(struct file *file);
+struct au_branch *au_fbr(struct file *file, aufs_bindex_t bindex);
+struct file *au_h_fptr(struct file *file, aufs_bindex_t bindex);
+
+void au_hfput(struct au_hfile *hf);
+void au_set_h_fptr(struct file *file, aufs_bindex_t bindex,
+ struct file *h_file);
+
+void au_finfo_fin(struct file *file);
+int au_finfo_init(struct file *file);
+
+#ifdef CONFIG_AUFS_ROBR
+/* robr.c */
+struct file *au_robr_safe_file(struct vm_area_struct *vma);
+void au_robr_reset_file(struct vm_area_struct *vma, struct file *file);
+#else
+static inline struct file *au_robr_safe_file(struct vm_area_struct *vma)
+{
+ struct file *file;
+
+ file = vma->vm_file;
+ if (file->private_data && au_test_aufs(file->f_dentry->d_sb))
+ return file;
+ return NULL;
+}
+
+static inline
+void au_robr_reset_file(struct vm_area_struct *vma, struct file *file)
+{
+ vma->vm_file = file;
+ //smp_mb(); /* flush vm_file */
+}
+#endif /* CONFIG_AUFS_ROBR */
+
+/* ---------------------------------------------------------------------- */
+
+//todo: memory barrier?
+static inline au_gen_t au_figen(struct file *f)
+{
+ return atomic_read(&au_fi(f)->fi_generation);
+}
+
+static inline int au_test_mmapped(struct file *f)
+{
+ return !!(au_fi(f)->fi_h_vm_ops);
+}
+
+static inline int au_test_aufs_file(struct file *f)
+{
+ return !(f->f_dentry->d_inode->i_mode
+ & (S_IFCHR | S_IFBLK | S_IFIFO | S_IFSOCK));
+}
+
+/* ---------------------------------------------------------------------- */
+
+#if !defined(CONFIG_AUFS_MODULE) || defined(CONFIG_AUFS_DENY_WRITE_ACCESS_PATCH)
+int au_store_fmode_exec(struct nameidata *nd, struct inode *inode);
+
+static inline int au_deny_write_access(struct file *h_file)
+{
+ LKTRTrace("%.*s\n", AuDLNPair(h_file->f_dentry));
+ return deny_write_access(h_file);
+}
+
+static inline void au_allow_write_access(struct file *h_file)
+{
+ allow_write_access(h_file);
+}
+
+#else
+
+static inline int au_store_fmode_exec(struct nameidata *nd, struct inode *inode)
+{
+ /* nothing */
+ return 0;
+}
+
+static inline int au_deny_write_access(struct file *h_file)
+{
+ /* nothing */
+ return 0;
+}
+
+static inline void au_allow_write_access(struct file *h_file)
+{
+ /* nothing */
+}
+#endif /* CONFIG_AUFS_DENY_WRITE_ACCESS_PATCH */
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * fi_read_lock, fi_write_lock,
+ * fi_read_unlock, fi_write_unlock, fi_downgrade_lock
+ */
+AuSimpleRwsemFuncs(fi, struct file *f, au_fi(f)->fi_rwsem);
+
+/* to debug easier, do not make them inlined functions */
+#define FiMustReadLock(f) do { \
+ SiMustAnyLock((f)->f_dentry->d_sb); \
+ AuRwMustReadLock(&au_fi(f)->fi_rwsem); \
+} while (0)
+
+#define FiMustWriteLock(f) do { \
+ SiMustAnyLock((f)->f_dentry->d_sb); \
+ AuRwMustWriteLock(&au_fi(f)->fi_rwsem); \
+} while (0)
+
+#define FiMustAnyLock(f) do { \
+ SiMustAnyLock((f)->f_dentry->d_sb); \
+ AuRwMustAnyLock(&au_fi(f)->fi_rwsem); \
+} while (0)
+
+#define FiMustNoWaiters(f) AuRwMustNoWaiters(&au_fi(f)->fi_rwsem)
+
+/* ---------------------------------------------------------------------- */
+
+// hard/soft set
+static inline aufs_bindex_t au_fbstart(struct file *file)
+{
+ FiMustAnyLock(file);
+ return au_fi(file)->fi_bstart;
+}
+
+static inline aufs_bindex_t au_fbend(struct file *file)
+{
+ FiMustAnyLock(file);
+ return au_fi(file)->fi_bend;
+}
+
+static inline struct au_vdir *au_fvdir_cache(struct file *file)
+{
+ FiMustAnyLock(file);
+ return au_fi(file)->fi_vdir_cache;
+}
+
+static inline void au_set_fbstart(struct file *file, aufs_bindex_t bindex)
+{
+ FiMustWriteLock(file);
+ AuDebugOn(au_sbend(file->f_dentry->d_sb) < bindex);
+ au_fi(file)->fi_bstart = bindex;
+}
+
+static inline void au_set_fbend(struct file *file, aufs_bindex_t bindex)
+{
+ FiMustWriteLock(file);
+ AuDebugOn(au_sbend(file->f_dentry->d_sb) < bindex
+ || bindex < au_fbstart(file));
+ au_fi(file)->fi_bend = bindex;
+}
+
+static inline void au_set_fvdir_cache(struct file *file,
+ struct au_vdir *vdir_cache)
+{
+ FiMustWriteLock(file);
+ AuDebugOn(!S_ISDIR(file->f_dentry->d_inode->i_mode)
+ || (au_fi(file)->fi_vdir_cache && vdir_cache));
+ au_fi(file)->fi_vdir_cache = vdir_cache;
+}
+
+static inline void au_update_figen(struct file *file)
+{
+ atomic_set(&au_fi(file)->fi_generation, au_digen(file->f_dentry));
+ //smp_mb(); /* atomic_set */
+}
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_FILE_H__ */
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 38/67] aufs handling file/dir, and address_space operation
2008-05-16 14:32 ` [PATCH 37/67] aufs file operations hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 39/67] aufs file private data hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs handling file/dir, and address_space operation
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/file.c | 822 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 822 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/file.c b/fs/aufs/file.c
new file mode 100644
index 0000000..d4396d3
--- /dev/null
+++ b/fs/aufs/file.c
@@ -0,0 +1,822 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * handling file/dir, and address_space operation
+ *
+ * $Id: file.c,v 1.4 2008/05/04 23:54:53 sfjro Exp $
+ */
+
+//#include <linux/fsnotify.h>
+#include <linux/pagemap.h>
+//#include <linux/poll.h>
+//#include <linux/security.h>
+#include "aufs.h"
+
+/*
+ * a dirty trick for handling FMODE_EXEC and deny_write_access().
+ * because FMODE_EXEC flag is not passed to f_op->open(),
+ * set it to file->private_data temporary.
+ */
+#if !defined(CONFIG_AUFS_MODULE) || defined(CONFIG_AUFS_DENY_WRITE_ACCESS_PATCH)
+int au_store_fmode_exec(struct nameidata *nd, struct inode *inode)
+{
+ int err;
+ union {
+ void *p;
+ unsigned long ul;
+ } u;
+
+ err = 0;
+ if (nd
+ && (nd->flags & LOOKUP_OPEN)
+ && nd->intent.open.file
+ && (nd->intent.open.flags & FMODE_EXEC)
+ && inode
+ && S_ISREG(inode->i_mode)) {
+ u.ul = nd->intent.open.flags;
+ nd->intent.open.file->private_data = u.p;
+ //smp_mb();
+ err = 1;
+ }
+
+ return err;
+}
+#endif
+
+/* drop flags for writing */
+unsigned int au_file_roflags(unsigned int flags)
+{
+ flags &= ~(O_WRONLY | O_RDWR | O_APPEND | O_CREAT | O_TRUNC);
+ flags |= O_RDONLY | O_NOATIME;
+ return flags;
+}
+
+/* common functions to regular file and dir */
+struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags,
+ struct file *file)
+{
+ struct file *h_file;
+ struct dentry *h_dentry;
+ struct inode *h_inode;
+ struct super_block *sb;
+ struct au_branch *br;
+ int hinotify, err;
+
+ LKTRTrace("%.*s, b%d, flags 0%o, f %d\n",
+ AuDLNPair(dentry), bindex, flags, !!file);
+ AuDebugOn(!dentry);
+ h_dentry = au_h_dptr(dentry, bindex);
+ AuDebugOn(!h_dentry);
+ h_inode = h_dentry->d_inode;
+ AuDebugOn(!h_inode);
+
+ sb = dentry->d_sb;
+ hinotify = !!au_opt_test(au_mntflags(sb), UDBA_INOTIFY);
+#if 0
+ if (unlikely(hinotify))
+ test here?
+#endif
+
+ br = au_sbr(sb, bindex);
+ au_br_get(br);
+ /* drop flags for writing */
+ if (au_test_ro(sb, bindex, dentry->d_inode))
+ flags = au_file_roflags(flags);
+ flags &= ~O_CREAT;
+
+ h_file = NULL;
+ if (unlikely(file && au_test_nfs(h_dentry->d_sb)))
+ h_file = au_h_intent(dentry, bindex, file);
+ if (!h_file) {
+ //DbgSleep(3);
+ h_file = dentry_open(dget(h_dentry), mntget(br->br_mnt), flags);
+ //if (LktrCond) {fput(h_file); h_file = ERR_PTR(-1);}
+ }
+
+ /*
+ * a dirty trick for handling FMODE_EXEC and deny_write_access().
+ */
+ if (file && (file->f_mode & FMODE_EXEC)) {
+ h_file->f_mode |= FMODE_EXEC;
+ smp_mb(); /* flush f_mode */
+ err = au_deny_write_access(h_file);
+ if (unlikely(err)) {
+ fput(h_file);
+ h_file = ERR_PTR(err);
+ }
+ }
+ if (!IS_ERR(h_file))
+ return h_file;
+
+ au_br_put(br);
+ AuTraceErrPtr(h_file);
+ return h_file;
+}
+
+static int do_coo(struct dentry *dentry, aufs_bindex_t bstart)
+{
+ int err;
+ struct dentry *parent, *h_parent, *h_dentry, *gparent;
+ aufs_bindex_t bcpup;
+ struct inode *h_dir, *h_inode, *dir;
+ struct super_block *sb;
+
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+ AuDebugOn(IS_ROOT(dentry));
+ DiMustWriteLock(dentry);
+
+ parent = dget_parent(dentry);
+ di_write_lock_parent(parent);
+ sb = dentry->d_sb;
+ err = AuWbrCopyup(au_sbi(sb), dentry);
+ bcpup = err;
+ if (err < 0) {
+ err = 0; /* stop copyup, it is not an error */
+ goto out;
+ }
+ err = 0;
+
+ h_parent = au_h_dptr(parent, bcpup);
+ if (!h_parent) {
+ err = au_cpup_dirs(dentry, bcpup, NULL);
+ if (unlikely(err))
+ goto out;
+ h_parent = au_h_dptr(parent, bcpup);
+ }
+
+ h_dir = h_parent->d_inode;
+ h_dentry = au_h_dptr(dentry, bstart);
+ h_inode = h_dentry->d_inode;
+ dir = parent->d_inode;
+ //todo: meaningless lock if CONFIG_AUFS_DEBUG is disabled.
+ gparent = NULL;
+ if (unlikely(au_opt_test(au_mntflags(sb), UDBA_INOTIFY)
+ && !IS_ROOT(parent))) {
+ gparent = dget_parent(parent);
+ ii_read_lock_parent2(gparent->d_inode);
+ }
+ au_hdir_lock(h_dir, dir, bcpup);
+ //todo: test parent-gparent relationship
+ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);
+ AuDebugOn(au_h_dptr(dentry, bcpup));
+ err = au_sio_cpup_simple(dentry, bcpup, -1, AuCpup_DTIME);
+ AuTraceErr(err);
+ mutex_unlock(&h_inode->i_mutex);
+ au_hdir_unlock(h_dir, dir, bcpup);
+ if (unlikely(gparent)) {
+ ii_read_unlock(gparent->d_inode);
+ dput(gparent);
+ }
+
+ out:
+ di_write_unlock(parent);
+ dput(parent);
+ AuTraceErr(err);
+ return err;
+}
+
+int au_do_open(struct inode *inode, struct file *file,
+ int (*open)(struct file *file, int flags))
+{
+ int err, coo;
+ struct dentry *dentry;
+ struct super_block *sb;
+ aufs_bindex_t bstart;
+ //struct inode *h_dir, *dir;
+
+ dentry = file->f_dentry;
+ LKTRTrace("i%lu, %.*s\n", inode->i_ino, AuDLNPair(dentry));
+
+ sb = dentry->d_sb;
+ si_read_lock(sb, AuLock_FLUSH);
+ coo = 0;
+ switch (au_mntflags(sb) & AuOptMask_COO) {
+ case AuOpt_COO_LEAF:
+ coo = !S_ISDIR(inode->i_mode);
+ break;
+ case AuOpt_COO_ALL:
+ coo = 1;
+ break;
+ }
+ err = au_finfo_init(file);
+ //if (LktrCond) {fi_write_unlock(file); fin_finfo(file); err = -1;}
+ if (unlikely(err))
+ goto out;
+
+ if (!coo) {
+ di_read_lock_child(dentry, AuLock_IR);
+ bstart = au_dbstart(dentry);
+ } else {
+ di_write_lock_child(dentry);
+ bstart = au_dbstart(dentry);
+ if (au_test_ro(sb, bstart, dentry->d_inode)) {
+ err = do_coo(dentry, bstart);
+ if (err) {
+ di_write_unlock(dentry);
+ goto out_finfo;
+ }
+ bstart = au_dbstart(dentry);
+ }
+ di_downgrade_lock(dentry, AuLock_IR);
+ }
+
+ err = open(file, file->f_flags);
+ //if (LktrCond) err = -1;
+ di_read_unlock(dentry, AuLock_IR);
+
+ out_finfo:
+ fi_write_unlock(file);
+ if (unlikely(err))
+ au_finfo_fin(file);
+ //DbgFile(file);
+ out:
+ si_read_unlock(sb);
+ AuTraceErr(err);
+ return err;
+}
+
+int au_reopen_nondir(struct file *file)
+{
+ int err;
+ struct dentry *dentry;
+ aufs_bindex_t bstart, bindex, bend;
+ struct file *h_file, *h_file_tmp;
+
+ dentry = file->f_dentry;
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+ bstart = au_dbstart(dentry);
+ AuDebugOn(S_ISDIR(dentry->d_inode->i_mode)
+ || !au_h_dptr(dentry, bstart)->d_inode);
+
+ h_file_tmp = NULL;
+ if (au_fbstart(file) == bstart) {
+ h_file = au_h_fptr(file, bstart);
+ if (file->f_mode == h_file->f_mode)
+ return 0; /* success */
+ h_file_tmp = h_file;
+ get_file(h_file_tmp);
+ au_set_h_fptr(file, bstart, NULL);
+ }
+ AuDebugOn(au_fbstart(file) < bstart
+ || au_fi(file)->fi_hfile[0 + bstart].hf_file);
+
+ h_file = au_h_open(dentry, bstart, file->f_flags & ~O_TRUNC, file);
+ //if (LktrCond) {fput(h_file); au_br_put(au_sbr(dentry->d_sb, bstart));
+ //h_file = ERR_PTR(-1);}
+ err = PTR_ERR(h_file);
+ if (IS_ERR(h_file))
+ goto out; // close all?
+ err = 0;
+ //cpup_file_flags(h_file, file);
+ au_set_fbstart(file, bstart);
+ au_set_h_fptr(file, bstart, h_file);
+ au_update_figen(file);
+ //file->f_ra = h_file->f_ra; //??
+
+ /* close lower files */
+ bend = au_fbend(file);
+ for (bindex = bstart + 1; bindex <= bend; bindex++)
+ au_set_h_fptr(file, bindex, NULL);
+ au_set_fbend(file, bstart);
+
+ out:
+ if (h_file_tmp)
+ fput(h_file_tmp);
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+#if 0
+static int au_ready_to_write_wh(struct file *file, loff_t len,
+ aufs_bindex_t bcpup)
+{
+ int err;
+ struct dentry *dentry, *hi_wh, *old_h_dentry;
+ struct au_dinfo *dinfo;
+ aufs_bindex_t old_bstart;
+
+ AuTraceEnter();
+
+ dentry = file->f_dentry;
+ hi_wh = au_hi_wh(dentry->d_inode, bcpup);
+ if (!hi_wh)
+ err = au_sio_cpup_wh(dentry, bcpup, len, file);
+ else {
+ /* already copied-up after unlink */
+ dinfo = au_di(dentry);
+ old_bstart = dinfo->di_bstart;
+ dinfo->di_bstart = bcpup;
+ old_h_dentry = dinfo->di_hdentry[0 + bcpup].hd_dentry;
+ dinfo->di_hdentry[0 + bcpup].hd_dentry = hi_wh;
+ err = au_reopen_nondir(file);
+ dinfo->di_hdentry[0 + bcpup].hd_dentry = old_h_dentry;
+ dinfo->di_bstart = old_bstart;
+ }
+ //if (LktrCond) err = -1;
+
+ AuTraceErr(err);
+ return err;
+}
+#endif
+
+/*
+ * prepare the @file for writing.
+ */
+int au_ready_to_write(struct file *file, loff_t len)
+{
+ int err;
+ struct dentry *dentry, *parent, *h_dentry, *h_parent, *hi_wh,
+ *old_h_dentry, *gparent;
+ struct inode *h_inode, *h_dir, *inode, *dir;
+ struct super_block *sb;
+ aufs_bindex_t bstart, bcpup, old_bstart;
+ struct au_dinfo *dinfo;
+
+ dentry = file->f_dentry;
+ LKTRTrace("%.*s, len %Ld\n", AuDLNPair(dentry), len);
+ FiMustWriteLock(file);
+
+ sb = dentry->d_sb;
+ bstart = au_fbstart(file);
+ AuDebugOn(au_fbr(file, bstart) != au_sbr(sb, bstart));
+
+ inode = dentry->d_inode;
+ AuDebugOn(S_ISDIR(inode->i_mode));
+ ii_read_lock_child(inode);
+ LKTRTrace("rdonly %d, bstart %d\n",
+ au_test_ro(sb, bstart, inode), bstart);
+ err = au_test_ro(sb, bstart, inode);
+ ii_read_unlock(inode);
+ if (!err && (au_h_fptr(file, bstart)->f_mode & FMODE_WRITE))
+ return 0;
+
+ /* need to cpup */
+ di_write_lock_child(dentry);
+ parent = dget_parent(dentry);
+ di_write_lock_parent(parent);
+ err = AuWbrCopyup(au_sbi(sb), dentry);
+ bcpup = err;
+ if (unlikely(err < 0))
+ goto out_unlock;
+ err = 0;
+
+ h_parent = au_h_dptr(parent, bcpup);
+ if (!h_parent) {
+ err = au_cpup_dirs(dentry, bcpup, NULL);
+ //if (LktrCond) err = -1;
+ if (unlikely(err))
+ goto out_unlock;
+ h_parent = au_h_dptr(parent, bcpup);
+ }
+
+ //todo: meaningless lock if CONFIG_AUFS_DEBUG is disabled.
+ gparent = NULL;
+ if (unlikely(au_opt_test(au_mntflags(sb), UDBA_INOTIFY)
+ && !IS_ROOT(parent))) {
+ gparent = dget_parent(parent);
+ ii_read_lock_parent2(gparent->d_inode);
+ }
+ h_dir = h_parent->d_inode;
+ h_dentry = au_h_fptr(file, au_fbstart(file))->f_dentry;
+ h_inode = h_dentry->d_inode;
+ dir = parent->d_inode;
+ au_hdir_lock(h_dir, dir, bcpup);
+ //todo: test parent-gparent relationship
+ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);
+ if (d_unhashed(dentry) /* || d_unhashed(h_dentry) */
+ /* || !h_inode->i_nlink */) {
+#if 0
+ err = au_ready_to_write_wh(file, len, bcpup);
+#else
+ hi_wh = au_hi_wh(inode, bcpup);
+ if (!hi_wh)
+ err = au_sio_cpup_wh(dentry, bcpup, len, file);
+ else {
+ /* already copied-up after unlink */
+ dinfo = au_di(dentry);
+ old_bstart = dinfo->di_bstart;
+ dinfo->di_bstart = bcpup;
+ old_h_dentry = dinfo->di_hdentry[0 + bcpup].hd_dentry;
+ dinfo->di_hdentry[0 + bcpup].hd_dentry = hi_wh;
+ err = au_reopen_nondir(file);
+ dinfo->di_hdentry[0 + bcpup].hd_dentry = old_h_dentry;
+ dinfo->di_bstart = old_bstart;
+ }
+ //if (LktrCond) err = -1;
+ AuTraceErr(err);
+#endif
+ } else {
+ if (!au_h_dptr(dentry, bcpup))
+ err = au_sio_cpup_simple(dentry, bcpup, len,
+ AuCpup_DTIME);
+ //if (LktrCond) err = -1;
+ AuTraceErr(err);
+ if (!err)
+ err = au_reopen_nondir(file);
+ //if (LktrCond) err = -1;
+ AuTraceErr(err);
+ }
+ mutex_unlock(&h_inode->i_mutex);
+ au_hdir_unlock(h_dir, dir, bcpup);
+ if (unlikely(gparent)) {
+ ii_read_unlock(gparent->d_inode);
+ dput(gparent);
+ }
+
+ out_unlock:
+ di_write_unlock(parent);
+ di_write_unlock(dentry);
+ dput(parent);
+ AuTraceErr(err);
+ return err;
+
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int refresh_file_by_inode(struct file *file, int *need_reopen)
+{
+ int err;
+ struct au_finfo *finfo;
+ struct dentry *dentry, *parent, *old_h_dentry, *hi_wh;
+ struct inode *inode, *dir, *h_dir;
+ aufs_bindex_t bstart, new_bstart, old_bstart;
+ struct super_block *sb;
+ struct au_dinfo *dinfo;
+
+ dentry = file->f_dentry;
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+ FiMustWriteLock(file);
+
+ err = 0;
+ finfo = au_fi(file);
+ inode = dentry->d_inode;
+ sb = dentry->d_sb;
+ again:
+ bstart = au_ibstart(inode);
+ if (bstart == finfo->fi_bstart)
+ goto out;
+
+ new_bstart = bstart;
+ parent = dget_parent(dentry);
+ dir = parent->d_inode;
+ if (au_test_ro(sb, bstart, inode)) {
+ di_read_lock_parent(parent, !AuLock_IR);
+ err = AuWbrCopyup(au_sbi(sb), dentry);
+ new_bstart = err;
+ di_read_unlock(parent, !AuLock_IR);
+ //todo: err = -1;
+ if (unlikely(err < 0))
+ goto out_put;
+ }
+ di_read_unlock(dentry, AuLock_IR);
+ di_write_lock_child(dentry);
+ /* someone else might change our inode while we were sleeping */
+ if (bstart != au_ibstart(inode)) { // todo
+ di_downgrade_lock(dentry, AuLock_IR);
+ err = 0;
+ dput(parent);
+ goto again;
+ }
+ di_read_lock_parent(parent, AuLock_IR);
+ bstart = new_bstart;
+
+ hi_wh = au_hi_wh(inode, bstart);
+ if (au_opt_test(au_mntflags(sb), PLINK)
+ && au_plink_test(sb, inode)
+ && !d_unhashed(dentry)) {
+ err = au_test_and_cpup_dirs(dentry, bstart, NULL);
+
+ /* always superio. */
+#if 1
+ h_dir = au_h_dptr(parent, bstart)->d_inode;
+ au_hdir_lock(h_dir, dir, bstart);
+ err = au_sio_cpup_simple(dentry, bstart, -1, AuCpup_DTIME);
+ au_hdir_unlock(h_dir, dir, bstart);
+#else
+ if (!au_test_wkq(current)) {
+ int wkq_err;
+ struct cpup_pseudo_link_args args = {
+ .errp = &err,
+ .dentry = dentry,
+ .bdst = bstart,
+ .do_lock = 1
+ };
+ wkq_err = au_wkq_wait(call_cpup_pseudo_link, &args);
+ if (unlikely(wkq_err))
+ err = wkq_err;
+ } else
+ err = cpup_pseudo_link(dentry, bstart, /*do_lock*/1);
+#endif
+ } else if (hi_wh) {
+ /* already copied-up after unlink */
+ dinfo = au_di(dentry);
+ old_bstart = dinfo->di_bstart;
+ dinfo->di_bstart = bstart;
+ old_h_dentry = dinfo->di_hdentry[0 + bstart].hd_dentry;
+ dinfo->di_hdentry[0 + bstart].hd_dentry = hi_wh;
+ err = au_reopen_nondir(file);
+ dinfo->di_hdentry[0 + bstart].hd_dentry = old_h_dentry;
+ dinfo->di_bstart = old_bstart;
+ *need_reopen = 0;
+ }
+ di_read_unlock(parent, AuLock_IR);
+ di_downgrade_lock(dentry, AuLock_IR);
+
+ out_put:
+ dput(parent);
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+/*
+ * after branch manipulating, refresh the file.
+ */
+static int refresh_file(struct file *file, int (*reopen)(struct file *file))
+{
+ int err, new_sz, need_reopen;
+ struct dentry *dentry;
+ aufs_bindex_t bend, bindex, bstart, brid;
+ struct au_hfile *p;
+ struct au_finfo *finfo;
+ struct super_block *sb;
+ struct inode *inode;
+ struct file *hidden_file;
+
+ dentry = file->f_dentry;
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+ FiMustWriteLock(file);
+ DiMustReadLock(dentry);
+ inode = dentry->d_inode;
+ IiMustReadLock(inode);
+
+ err = -ENOMEM;
+ sb = dentry->d_sb;
+ finfo = au_fi(file);
+ bstart = finfo->fi_bstart;
+ bend = finfo->fi_bstart;
+ new_sz = sizeof(*finfo->fi_hfile) * (au_sbend(sb) + 1);
+ p = au_kzrealloc(finfo->fi_hfile, sizeof(*p) * (finfo->fi_bend + 1),
+ new_sz, GFP_KERNEL);
+ //p = NULL;
+ if (unlikely(!p))
+ goto out;
+ finfo->fi_hfile = p;
+ hidden_file = p[0 + bstart].hf_file;
+
+ p = finfo->fi_hfile + finfo->fi_bstart;
+ brid = p->hf_br->br_id;
+ bend = finfo->fi_bend;
+ for (bindex = finfo->fi_bstart; bindex <= bend; bindex++, p++) {
+ struct au_hfile tmp, *q;
+ aufs_bindex_t new_bindex;
+
+ if (!p->hf_file)
+ continue;
+ new_bindex = au_find_bindex(sb, p->hf_br);
+ if (new_bindex == bindex)
+ continue;
+ if (new_bindex < 0) { // test here
+ au_set_h_fptr(file, bindex, NULL);
+ continue;
+ }
+
+ /* swap two hidden inode, and loop again */
+ q = finfo->fi_hfile + new_bindex;
+ tmp = *q;
+ *q = *p;
+ *p = tmp;
+ if (tmp.hf_file) {
+ bindex--;
+ p--;
+ }
+ }
+ {
+ aufs_bindex_t s = finfo->fi_bstart, e = finfo->fi_bend;
+ finfo->fi_bstart = 0;
+ finfo->fi_bend = au_sbend(sb);
+ finfo->fi_bstart = s;
+ finfo->fi_bend = e;
+ }
+
+ p = finfo->fi_hfile;
+ if (!au_test_mmapped(file) && !d_unhashed(dentry)) {
+ bend = au_sbend(sb);
+ for (finfo->fi_bstart = 0; finfo->fi_bstart <= bend;
+ finfo->fi_bstart++, p++)
+ if (p->hf_file) {
+ if (p->hf_file->f_dentry
+ && p->hf_file->f_dentry->d_inode)
+ break;
+ else
+ au_hfput(p);
+ }
+ } else {
+ bend = au_br_index(sb, brid);
+ //LKTRTrace("%d\n", bend);
+ for (finfo->fi_bstart = 0; finfo->fi_bstart < bend;
+ finfo->fi_bstart++, p++)
+ if (p->hf_file)
+ au_hfput(p);
+ //LKTRTrace("%d\n", finfo->fi_bstart);
+ bend = au_sbend(sb);
+ }
+
+ p = finfo->fi_hfile + bend;
+ for (finfo->fi_bend = bend; finfo->fi_bend >= finfo->fi_bstart;
+ finfo->fi_bend--, p--)
+ if (p->hf_file) {
+ if (p->hf_file->f_dentry
+ && p->hf_file->f_dentry->d_inode)
+ break;
+ else
+ au_hfput(p);
+ }
+ AuDebugOn(finfo->fi_bend < finfo->fi_bstart);
+ //DbgFile(file);
+ //DbgDentry(file->f_dentry);
+
+ err = 0;
+ need_reopen = 1;
+ if (!au_test_mmapped(file))
+ err = refresh_file_by_inode(file, &need_reopen);
+ if (!err && need_reopen && !d_unhashed(dentry))
+ err = reopen(file);
+ //err = -1;
+ if (!err) {
+ au_update_figen(file);
+ //DbgFile(file);
+ return 0; /* success */
+ }
+
+ /* error, close all hidden files */
+ bend = au_fbend(file);
+ for (bindex = au_fbstart(file); bindex <= bend; bindex++)
+ au_set_h_fptr(file, bindex, NULL);
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+/* common function to regular file and dir */
+int au_reval_and_lock_finfo(struct file *file, int (*reopen)(struct file *file),
+ int wlock, int locked)
+{
+ int err, pseudo_link;
+ struct dentry *dentry;
+ struct super_block *sb;
+ aufs_bindex_t bstart;
+ au_gen_t sgen, fgen;
+
+ dentry = file->f_dentry;
+ LKTRTrace("%.*s, w %d, l %d\n", AuDLNPair(dentry), wlock, locked);
+ sb = dentry->d_sb;
+ SiMustAnyLock(sb);
+
+ err = 0;
+ sgen = au_sigen(sb);
+ fi_write_lock(file);
+ fgen = au_figen(file);
+ di_read_lock_child(dentry, AuLock_IR);
+ bstart = au_dbstart(dentry);
+ pseudo_link = (bstart != au_ibstart(dentry->d_inode));
+ di_read_unlock(dentry, AuLock_IR);
+ if (sgen == fgen && !pseudo_link && au_fbstart(file) == bstart) {
+ if (!wlock)
+ fi_downgrade_lock(file);
+ return 0; /* success */
+ }
+
+ LKTRTrace("sgen %d, fgen %d\n", sgen, fgen);
+ if (unlikely(sgen != au_digen(dentry)
+ || sgen != au_iigen(dentry->d_inode))) {
+ /*
+ * d_path() and path_lookup() is a simple and good approach
+ * to revalidate. but si_rwsem in DEBUG_RWSEM will cause a
+ * deadlock. removed the code.
+ */
+ di_write_lock_child(dentry);
+ err = au_reval_dpath(dentry, sgen);
+ //if (LktrCond) err = -1;
+ di_write_unlock(dentry);
+ if (unlikely(err < 0))
+ goto out;
+ AuDebugOn(au_digen(dentry) != sgen
+ || au_iigen(dentry->d_inode) != sgen);
+ }
+
+ di_read_lock_child(dentry, AuLock_IR);
+ err = refresh_file(file, reopen
+ /* , au_opt_test(au_mnt_flags(sb), REFROF) */);
+ //if (LktrCond) err = -1;
+ di_read_unlock(dentry, AuLock_IR);
+ if (!err) {
+ if (!wlock)
+ fi_downgrade_lock(file);
+ } else
+ fi_write_unlock(file);
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/* cf. aufs_nopage() */
+/* for madvise(2) */
+static int aufs_readpage(struct file *file, struct page *page)
+{
+ AuTraceEnter();
+ unlock_page(page);
+ return 0;
+}
+
+/* they will never be called. */
+#ifdef CONFIG_AUFS_DEBUG
+static int aufs_prepare_write(struct file *file, struct page *page,
+ unsigned from, unsigned to)
+{ AuUnsupport(); return 0; }
+static int aufs_commit_write(struct file *file, struct page *page,
+ unsigned from, unsigned to)
+{ AuUnsupport(); return 0; }
+static int aufs_write_begin(struct file *file, struct address_space *mapping,
+ loff_t pos, unsigned len, unsigned flags,
+ struct page **pagep, void **fsdata)
+{ AuUnsupport(); return 0; }
+static int aufs_write_end(struct file *file, struct address_space *mapping,
+ loff_t pos, unsigned len, unsigned copied,
+ struct page *page, void *fsdata)
+{ AuUnsupport(); return 0; }
+static int aufs_writepage(struct page *page, struct writeback_control *wbc)
+{ AuUnsupport(); return 0; }
+static void aufs_sync_page(struct page *page)
+{ AuUnsupport(); }
+
+#if 0 // comment
+static int aufs_writepages(struct address_space *mapping,
+ struct writeback_control *wbc)
+{ AuUnsupport(); return 0; }
+static int aufs_readpages(struct file *filp, struct address_space *mapping,
+ struct list_head *pages, unsigned nr_pages)
+{ AuUnsupport(); return 0; }
+static sector_t aufs_bmap(struct address_space *mapping, sector_t block)
+{ AuUnsupport(); return 0; }
+#endif
+
+static int aufs_set_page_dirty(struct page *page)
+{ AuUnsupport(); return 0; }
+static void aufs_invalidatepage(struct page *page, unsigned long offset)
+{ AuUnsupport(); }
+static int aufs_releasepage(struct page *page, gfp_t gfp)
+{ AuUnsupport(); return 0; }
+static ssize_t aufs_direct_IO(int rw, struct kiocb *iocb,
+ const struct iovec *iov, loff_t offset,
+ unsigned long nr_segs)
+{ AuUnsupport(); return 0; }
+//static int aufs_migratepage (struct page *newpage, struct page *page)
+//{ AuUnsupport(); return 0; }
+#if 0 //LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
+int (*launder_page) (struct page *);
+#endif
+#endif /* CONFIG_AUFS_DEBUG */
+
+struct address_space_operations aufs_aop = {
+ .readpage = aufs_readpage,
+#ifdef CONFIG_AUFS_DEBUG
+ .writepage = aufs_writepage,
+ .sync_page = aufs_sync_page,
+ //.writepages = aufs_writepages,
+ .set_page_dirty = aufs_set_page_dirty,
+ //.readpages = aufs_readpages,
+ .prepare_write = aufs_prepare_write,
+ .commit_write = aufs_commit_write,
+ .write_begin = aufs_write_begin,
+ .write_end = aufs_write_end,
+ //.bmap = aufs_bmap,
+ .invalidatepage = aufs_invalidatepage,
+ .releasepage = aufs_releasepage,
+ .direct_IO = aufs_direct_IO,
+ //.migratepage = aufs_migratepage
+#endif /* CONFIG_AUFS_DEBUG */
+};
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 39/67] aufs file private data
2008-05-16 14:32 ` [PATCH 38/67] aufs handling file/dir, and address_space operation hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 40/67] aufs file and vm operations hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs file private data
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/finfo.c | 185 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 185 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/finfo.c b/fs/aufs/finfo.c
new file mode 100644
index 0000000..74b2ebf
--- /dev/null
+++ b/fs/aufs/finfo.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * file private data
+ *
+ * $Id: finfo.c,v 1.2 2008/04/21 01:35:14 sfjro Exp $
+ */
+
+#include "aufs.h"
+
+struct au_finfo *au_fi(struct file *file)
+{
+ struct au_finfo *finfo = file->private_data;
+ AuDebugOn(!finfo
+ || !finfo->fi_hfile
+ || (0 < finfo->fi_bend
+ && (/* au_sbi(file->f_dentry->d_sb)->si_bend
+ < finfo->fi_bend
+ || */ finfo->fi_bend < finfo->fi_bstart)));
+ return finfo;
+}
+
+struct au_branch *au_fbr(struct file *file, aufs_bindex_t bindex)
+{
+ struct au_finfo *finfo = au_fi(file);
+ struct au_hfile *hf;
+
+ FiMustAnyLock(file);
+ AuDebugOn(!finfo
+ || finfo->fi_bstart < 0
+ || bindex < finfo->fi_bstart
+ || finfo->fi_bend < bindex);
+ hf = finfo->fi_hfile + bindex;
+ AuDebugOn(hf->hf_br && au_br_count(hf->hf_br) <= 0);
+ return hf->hf_br;
+}
+
+struct file *au_h_fptr(struct file *file, aufs_bindex_t bindex)
+{
+ struct au_finfo *finfo = au_fi(file);
+ struct au_hfile *hf;
+
+ FiMustAnyLock(file);
+ AuDebugOn(!finfo
+ || finfo->fi_bstart < 0
+ || bindex < finfo->fi_bstart
+ || finfo->fi_bend < bindex);
+ hf = finfo->fi_hfile + bindex;
+ AuDebugOn(hf->hf_file
+ && file_count(hf->hf_file) <= 0
+ && au_br_count(hf->hf_br) <= 0);
+ return hf->hf_file;
+}
+
+void au_hfput(struct au_hfile *hf)
+{
+ if (hf->hf_file->f_mode & FMODE_EXEC)
+ au_allow_write_access(hf->hf_file);
+ fput(hf->hf_file);
+ hf->hf_file = NULL;
+ AuDebugOn(!hf->hf_br);
+ au_br_put(hf->hf_br);
+ hf->hf_br = NULL;
+}
+
+void au_set_h_fptr(struct file *file, aufs_bindex_t bindex, struct file *val)
+{
+ struct au_finfo *finfo = au_fi(file);
+ struct au_hfile *hf;
+
+ FiMustWriteLock(file);
+ AuDebugOn(!finfo
+ || finfo->fi_bstart < 0
+ || bindex < finfo->fi_bstart
+ || finfo->fi_bend < bindex);
+ AuDebugOn(val && file_count(val) <= 0);
+ hf = finfo->fi_hfile + bindex;
+ AuDebugOn(val && hf->hf_file);
+ if (hf->hf_file)
+ au_hfput(hf);
+ if (val) {
+ hf->hf_file = val;
+ hf->hf_br = au_sbr(file->f_dentry->d_sb, bindex);
+ }
+}
+
+void au_finfo_fin(struct file *file)
+{
+ struct au_finfo *finfo;
+ struct dentry *dentry;
+ aufs_bindex_t bindex, bend;
+
+ dentry = file->f_dentry;
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+ SiMustAnyLock(dentry->d_sb);
+
+ fi_write_lock(file);
+ bend = au_fbend(file);
+ bindex = au_fbstart(file);
+ if (bindex >= 0)
+ for (; bindex <= bend; bindex++)
+ au_set_h_fptr(file, bindex, NULL);
+
+ finfo = au_fi(file);
+#ifdef CONFIG_AUFS_DEBUG
+ if (finfo->fi_bstart >= 0) {
+ bend = au_fbend(file);
+ for (bindex = finfo->fi_bstart; bindex <= bend; bindex++) {
+ struct au_hfile *hf;
+ hf = finfo->fi_hfile + bindex;
+ AuDebugOn(hf->hf_file || hf->hf_br);
+ }
+ }
+#endif
+
+ kfree(finfo->fi_hfile);
+ fi_write_unlock(file);
+ au_cache_free_finfo(finfo);
+ //file->private_data = NULL;
+}
+
+int au_finfo_init(struct file *file)
+{
+ struct au_finfo *finfo;
+ struct dentry *dentry;
+ union {
+ void *p;
+ unsigned long ul;
+ } u;
+
+ dentry = file->f_dentry;
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+ AuDebugOn(!dentry->d_inode);
+
+ finfo = au_cache_alloc_finfo();
+ if (finfo) {
+ finfo->fi_hfile = kcalloc(au_sbend(dentry->d_sb) + 1,
+ sizeof(*finfo->fi_hfile), GFP_KERNEL);
+ if (finfo->fi_hfile) {
+ au_rw_init_wlock(&finfo->fi_rwsem);
+ finfo->fi_bstart = -1;
+ finfo->fi_bend = -1;
+ atomic_set(&finfo->fi_generation, au_digen(dentry));
+ //smp_mb(); /* atomic_set */
+
+ /*
+ * a dirty trick for handling FMODE_EXEC and
+ * deny_write_access().
+ * because FMODE_EXEC flag is not passed to
+ * f_op->open(),
+ * aufs set it to file->private_data temporary in lookup
+ * or dentry revalidation operations.
+ * restore the flag to f_mode here.
+ */
+ u.p = file->private_data;
+ if (u.ul & FMODE_EXEC) {
+ file->f_mode |= FMODE_EXEC;
+ smp_mb(); /* flush f_mode */
+ }
+
+ file->private_data = finfo;
+ return 0; /* success */
+ }
+ au_cache_free_finfo(finfo);
+ }
+
+ AuTraceErr(-ENOMEM);
+ return -ENOMEM;
+}
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 40/67] aufs file and vm operations
2008-05-16 14:32 ` [PATCH 39/67] aufs file private data hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 41/67] aufs directory operations, header hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs file and vm operations
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/f_op.c | 665 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 665 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/f_op.c b/fs/aufs/f_op.c
new file mode 100644
index 0000000..ee8a849
--- /dev/null
+++ b/fs/aufs/f_op.c
@@ -0,0 +1,665 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * file and vm operations
+ *
+ * $Id: f_op.c,v 1.4 2008/05/12 00:28:10 sfjro Exp $
+ */
+
+//#include <linux/fsnotify.h>
+#include <linux/fs_stack.h>
+//#include <linux/pagemap.h>
+#include <linux/poll.h>
+//#include <linux/security.h>
+#include "aufs.h"
+
+/* common function to regular file and dir */
+int aufs_flush(struct file *file, fl_owner_t id)
+{
+ int err;
+ struct dentry *dentry;
+ aufs_bindex_t bindex, bend;
+
+ dentry = file->f_dentry;
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+
+ // aufs_read_lock_file()
+ si_noflush_read_lock(dentry->d_sb);
+ fi_read_lock(file);
+ di_read_lock_child(dentry, AuLock_IW);
+
+ err = 0;
+ bend = au_fbend(file);
+ for (bindex = au_fbstart(file); !err && bindex <= bend; bindex++) {
+ struct file *h_file;
+ h_file = au_h_fptr(file, bindex);
+ if (h_file && h_file->f_op && h_file->f_op->flush) {
+ err = h_file->f_op->flush(h_file, id);
+ if (!err)
+ au_update_fuse_h_inode
+ (h_file->f_vfsmnt, h_file->f_dentry);
+ /*ignore*/
+ }
+ }
+ au_cpup_attr_timesizes(dentry->d_inode);
+
+ di_read_unlock(dentry, AuLock_IW);
+ fi_read_unlock(file);
+ si_read_unlock(dentry->d_sb);
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int do_open_nondir(struct file *file, int flags)
+{
+ int err;
+ aufs_bindex_t bindex;
+ struct super_block *sb;
+ struct file *h_file;
+ struct dentry *dentry;
+ struct inode *inode;
+ struct au_finfo *finfo;
+
+ dentry = file->f_dentry;
+ LKTRTrace("%.*s, flags 0%o\n", AuDLNPair(dentry), flags);
+ FiMustWriteLock(file);
+ inode = dentry->d_inode;
+ AuDebugOn(!inode || S_ISDIR(inode->i_mode));
+
+ err = 0;
+ finfo = au_fi(file);
+ finfo->fi_h_vm_ops = NULL;
+ sb = dentry->d_sb;
+ bindex = au_dbstart(dentry);
+ AuDebugOn(!au_h_dptr(dentry, bindex)->d_inode);
+ /* O_TRUNC is processed already */
+ BUG_ON(au_test_ro(sb, bindex, inode) && (flags & O_TRUNC));
+
+ h_file = au_h_open(dentry, bindex, flags, file);
+ //if (LktrCond) {fput(h_file); au_br_put(au_sbr(dentry->d_sb, bindex));
+ //h_file = ERR_PTR(-1);}
+ if (!IS_ERR(h_file)) {
+ au_set_fbstart(file, bindex);
+ au_set_fbend(file, bindex);
+ au_set_h_fptr(file, bindex, h_file);
+ au_update_figen(file);
+ //file->f_ra = h_file->f_ra; //??
+ //AuDbgFile(file);
+ return 0; /* success */
+ }
+ err = PTR_ERR(h_file);
+ AuTraceErr(err);
+ return err;
+}
+
+static int aufs_open_nondir(struct inode *inode, struct file *file)
+{
+ return au_do_open(inode, file, do_open_nondir);
+}
+
+static int aufs_release_nondir(struct inode *inode, struct file *file)
+{
+ struct super_block *sb = file->f_dentry->d_sb;
+
+ LKTRTrace("i%lu, %.*s\n", inode->i_ino, AuDLNPair(file->f_dentry));
+
+ si_noflush_read_lock(sb);
+ au_finfo_fin(file);
+ si_read_unlock(sb);
+ return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static ssize_t aufs_read(struct file *file, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ ssize_t err;
+ struct dentry *dentry;
+ struct file *h_file;
+ struct super_block *sb;
+ struct inode *h_inode;
+
+ dentry = file->f_dentry;
+ LKTRTrace("%.*s, cnt %lu, pos %Ld\n",
+ AuDLNPair(dentry), (unsigned long)count, *ppos);
+ //AuDbgDentry(dentry);
+
+ sb = dentry->d_sb;
+ si_read_lock(sb, AuLock_FLUSH);
+ err = au_reval_and_lock_finfo(file, au_reopen_nondir, /*wlock*/0,
+ /*locked*/0);
+ //if (LktrCond) {fi_read_unlock(file); err = -1;}
+ if (unlikely(err))
+ goto out;
+
+ /* support LSM and notify */
+ h_file = au_h_fptr(file, au_fbstart(file));
+ h_inode = h_file->f_dentry->d_inode;
+ err = vfsub_read_u(h_file, buf, count, ppos,
+ au_opt_test_dlgt(au_mntflags(sb)));
+ //file->f_ra = h_file->f_ra; //??
+ fsstack_copy_attr_atime(dentry->d_inode, h_file->f_dentry->d_inode);
+
+ fi_read_unlock(file);
+ out:
+ si_read_unlock(sb);
+ AuTraceErr(err);
+ return err;
+}
+
+static ssize_t aufs_write(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ ssize_t err;
+ struct dentry *dentry, *parent;
+ struct inode *inode, *dir;
+ struct super_block *sb;
+ unsigned int mnt_flags;
+ struct file *h_file;
+ char __user *buf = (char __user *)ubuf;
+ struct au_hin_ignore ign;
+ struct vfsub_args vargs;
+
+ dentry = file->f_dentry;
+ LKTRTrace("%.*s, cnt %lu, pos %Ld\n",
+ AuDLNPair(dentry), (unsigned long)count, *ppos);
+
+ inode = dentry->d_inode;
+ mutex_lock(&inode->i_mutex);
+ sb = dentry->d_sb;
+ si_read_lock(sb, AuLock_FLUSH);
+ err = au_reval_and_lock_finfo(file, au_reopen_nondir, /*wlock*/1,
+ /*locked*/1);
+ //if (LktrCond) {fi_write_unlock(file); err = -1;}
+ if (unlikely(err))
+ goto out;
+ err = au_ready_to_write(file, -1);
+ //if (LktrCond) err = -1;
+ if (unlikely(err))
+ goto out_unlock;
+
+ /* support LSM and notify */
+ mnt_flags = au_mntflags(sb);
+ vfsub_args_init(&vargs, &ign, au_opt_test_dlgt(mnt_flags), 0);
+ h_file = au_h_fptr(file, au_fbstart(file));
+ if (!au_opt_test(mnt_flags, UDBA_INOTIFY))
+ err = vfsub_write_u(h_file, buf, count, ppos, &vargs);
+ else {
+ parent = dget_parent(dentry);
+ dir = parent->d_inode;
+ ii_read_lock_parent(dir);
+ vfsub_ign_hinode(&vargs, IN_MODIFY,
+ au_hi(dir, au_fbstart(file)));
+ err = vfsub_write_u(h_file, buf, count, ppos, &vargs);
+ ii_read_unlock(dir);
+ dput(parent);
+ }
+ ii_write_lock_child(inode);
+ au_cpup_attr_timesizes(inode);
+ ii_write_unlock(inode);
+
+ out_unlock:
+ fi_write_unlock(file);
+ out:
+ si_read_unlock(sb);
+ mutex_unlock(&inode->i_mutex);
+ AuTraceErr(err);
+ return err;
+}
+
+#ifdef CONFIG_AUFS_SPLICE_PATCH
+static int au_test_loopback(void)
+{
+ const char c = current->comm[4];
+ /* true if a kernel thread named 'loop[0-9].*' accesses a file */
+ const int loopback = (current->mm == NULL
+ && '0' <= c && c <= '9'
+ && strncmp(current->comm, "loop", 4) == 0);
+ return loopback;
+}
+
+static ssize_t aufs_splice_read(struct file *file, loff_t *ppos,
+ struct pipe_inode_info *pipe, size_t len,
+ unsigned int flags)
+{
+ ssize_t err;
+ struct file *h_file;
+ struct dentry *dentry;
+ struct super_block *sb;
+
+ dentry = file->f_dentry;
+ LKTRTrace("%.*s, pos %Ld, len %lu\n",
+ AuDLNPair(dentry), *ppos, (unsigned long)len);
+
+ sb = dentry->d_sb;
+ si_read_lock(sb, AuLock_FLUSH);
+ err = au_reval_and_lock_finfo(file, au_reopen_nondir, /*wlock*/0,
+ /*locked*/0);
+ if (unlikely(err))
+ goto out;
+
+ err = -EINVAL;
+ /* support LSM and notify */
+ h_file = au_h_fptr(file, au_fbstart(file));
+ if (/* unlikely */(au_test_loopback())) {
+ file->f_mapping = h_file->f_mapping;
+ smp_mb(); /* unnecessary? */
+ }
+ err = vfsub_splice_to(h_file, ppos, pipe, len, flags,
+ au_opt_test_dlgt(au_mntflags(sb)));
+ //file->f_ra = h_file->f_ra; //??
+ fsstack_copy_attr_atime(dentry->d_inode, h_file->f_dentry->d_inode);
+ fi_read_unlock(file);
+
+ out:
+ si_read_unlock(sb);
+ AuTraceErr(err);
+ return err;
+}
+
+static ssize_t
+aufs_splice_write(struct pipe_inode_info *pipe, struct file *file, loff_t *ppos,
+ size_t len, unsigned int flags)
+{
+ ssize_t err;
+ struct dentry *dentry;
+ struct inode *inode, *h_inode;
+ struct super_block *sb;
+ struct file *h_file;
+ struct au_hin_ignore ign;
+ struct vfsub_args vargs;
+ unsigned int mnt_flags;
+
+ dentry = file->f_dentry;
+ LKTRTrace("%.*s, len %lu, pos %Ld\n",
+ AuDLNPair(dentry), (unsigned long)len, *ppos);
+
+ inode = dentry->d_inode;
+ mutex_lock(&inode->i_mutex);
+ sb = dentry->d_sb;
+ si_read_lock(sb, AuLock_FLUSH);
+ err = au_reval_and_lock_finfo(file, au_reopen_nondir, /*wlock*/1,
+ /*locked*/1);
+ //if (LktrCond) {fi_write_unlock(file); err = -1;}
+ if (unlikely(err))
+ goto out;
+ err = au_ready_to_write(file, -1);
+ //if (LktrCond) err = -1;
+ if (unlikely(err))
+ goto out_unlock;
+
+ /* support LSM and notify */
+ mnt_flags = au_mntflags(sb);
+ vfsub_args_init(&vargs, &ign, au_opt_test_dlgt(mnt_flags), 0);
+ h_file = au_h_fptr(file, au_fbstart(file));
+ h_inode = h_file->f_dentry->d_inode;
+ /* current do_splice_from() doesn't fire up the inotify event */
+ if (1 || !au_opt_test(mnt_flags, UDBA_INOTIFY))
+ err = vfsub_splice_from(pipe, h_file, ppos, len, flags, &vargs);
+ else {
+ //struct dentry *parent = dget_parent(dentry);
+ //vfsub_ign_hinode(&vargs, IN_MODIFY,
+ //au_hi(parent->d_inode, au_fbstart(file));
+ err = vfsub_splice_from(pipe, h_file, ppos, len, flags, &vargs);
+ //dput(parent);
+ }
+ ii_write_lock_child(inode);
+ au_cpup_attr_timesizes(inode);
+ ii_write_unlock(inode);
+
+ out_unlock:
+ fi_write_unlock(file);
+ out:
+ si_read_unlock(sb);
+ mutex_unlock(&inode->i_mutex);
+ AuTraceErr(err);
+ return err;
+}
+#endif /* CONFIG_AUFS_SPLICE_PATCH */
+
+/* ---------------------------------------------------------------------- */
+
+static int aufs_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+ int err;
+ struct dentry *dentry;
+ struct file *file, *h_file;
+ struct inode *inode;
+ static DECLARE_WAIT_QUEUE_HEAD(wq);
+ struct au_finfo *finfo;
+ //struct page *page;
+
+ AuTraceEnter();
+ AuDebugOn(!vma || !vma->vm_file);
+ //todo: non-robr mode, user vm_file as it is.
+ wait_event(wq, (file = au_robr_safe_file(vma)));
+ AuDebugOn(!au_test_aufs(file->f_dentry->d_sb));
+ dentry = file->f_dentry;
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+ inode = dentry->d_inode;
+ AuDebugOn(!S_ISREG(inode->i_mode));
+
+ /* do not revalidate, no si lock */
+ finfo = au_fi(file);
+ h_file = finfo->fi_hfile[0 + finfo->fi_bstart].hf_file;
+ AuDebugOn(!h_file || !au_test_mmapped(file));
+ fi_write_lock(file);
+ vma->vm_file = h_file;
+ err = finfo->fi_h_vm_ops->fault(vma, vmf);
+ //file->f_ra = h_file->f_ra; //??
+ au_robr_reset_file(vma, file);
+ fi_write_unlock(file);
+#if 0 //def CONFIG_SMP
+ //wake_up_nr(&wq, online_cpu - 1);
+ wake_up_all(&wq);
+#else
+ wake_up(&wq);
+#endif
+
+ if (!(err & VM_FAULT_ERROR)) {
+#if 0
+ page = vmf->page;
+ AuDbg("%p, %d\n", page, page_mapcount(page));
+#endif
+ //page->mapping = file->f_mapping;
+ //get_page(page);
+ //file->f_mapping = h_file->f_mapping;
+ //touch_atime(NULL, dentry);
+ //inode->i_atime = h_file->f_dentry->d_inode->i_atime;
+ }
+ AuTraceErr(err);
+ //AuDbg("err %d\n", err);
+ return err;
+}
+
+static struct vm_operations_struct aufs_vm_ops = {
+ //.open = aufs_vmaopen,
+ //.close = aufs_vmaclose,
+ .fault = aufs_fault,
+#if 0 // rfu
+ unsigned long (*nopfn)(struct vm_area_struct *area,
+ unsigned long address);
+ //page_mkwrite(struct vm_area_struct *vma, struct page *page)
+#endif
+};
+
+/* ---------------------------------------------------------------------- */
+
+static struct vm_operations_struct *au_vm_ops(struct file *h_file,
+ struct vm_area_struct *vma)
+{
+ struct vm_operations_struct *vm_ops;
+ int err;
+
+ AuTraceEnter();
+
+ if (!au_test_nfs(h_file->f_vfsmnt->mnt_sb))
+ err = h_file->f_op->mmap(h_file, vma);
+ else {
+ lockdep_off();
+ err = h_file->f_op->mmap(h_file, vma);
+ lockdep_on();
+ }
+ vm_ops = ERR_PTR(err);
+ if (unlikely(err))
+ goto out;
+ vm_ops = vma->vm_ops;
+ err = do_munmap(current->mm, vma->vm_start,
+ vma->vm_end - vma->vm_start);
+ if (unlikely(err)) {
+ AuIOErr("failed internal unmapping %.*s, %d\n",
+ AuDLNPair(h_file->f_dentry), err);
+ vm_ops = ERR_PTR(-EIO);
+ }
+
+ out:
+ AuTraceErrPtr(vm_ops);
+ return vm_ops;
+}
+
+static int aufs_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ int err, wlock, mmapped;
+ struct dentry *dentry;
+ struct super_block *sb;
+ struct file *h_file;
+ struct vm_operations_struct *vm_ops;
+
+ dentry = file->f_dentry;
+ LKTRTrace("%.*s, %lx, len %lu\n",
+ AuDLNPair(dentry), vma->vm_start,
+ vma->vm_end - vma->vm_start);
+ AuDebugOn(!S_ISREG(dentry->d_inode->i_mode));
+ AuDebugOn(down_write_trylock(&vma->vm_mm->mmap_sem));
+
+ mmapped = au_test_mmapped(file); /* can be harmless race condition */
+ wlock = !!(file->f_mode & FMODE_WRITE);
+ sb = dentry->d_sb;
+ si_read_lock(sb, AuLock_FLUSH);
+ err = au_reval_and_lock_finfo(file, au_reopen_nondir,
+ wlock | !mmapped, /*locked*/0);
+ //err = -1;
+ if (unlikely(err))
+ goto out;
+
+ if (wlock) {
+ err = au_ready_to_write(file, -1);
+ //err = -1;
+ if (unlikely(err))
+ goto out_unlock;
+ }
+
+ h_file = au_h_fptr(file, au_fbstart(file));
+ if (unlikely(au_test_fuse(h_file->f_dentry->d_sb))) {
+ /*
+ * by this assignment, f_mapping will differs from aufs inode
+ * i_mapping.
+ * if someone else mixes the use of f_dentry->d_inode and
+ * f_mapping->host, then a problem may arise.
+ */
+ file->f_mapping = h_file->f_mapping;
+ }
+
+ if (0 && h_file->f_op->mmap == generic_file_mmap) {
+ err = generic_file_mmap(file, vma); /* instead of h_file */
+ if (unlikely(err))
+ goto out_unlock;
+ au_fi(file)->fi_h_vm_ops = vma->vm_ops;
+ } else {
+ vm_ops = NULL;
+ if (!mmapped) {
+ vm_ops = au_vm_ops(h_file, vma);
+ err = PTR_ERR(vm_ops);
+ if (IS_ERR(vm_ops))
+ goto out_unlock;
+ }
+
+ err = generic_file_mmap(file, vma);
+ if (unlikely(err))
+ goto out_unlock;
+ vma->vm_ops = &aufs_vm_ops;
+ /* test again */
+ if (!au_test_mmapped(file)) {
+ FiMustWriteLock(file);
+ au_fi(file)->fi_h_vm_ops = vm_ops;
+ }
+ }
+
+ file_accessed(h_file);
+ au_update_fuse_h_inode(h_file->f_vfsmnt, h_file->f_dentry); /*ignore*/
+ fsstack_copy_attr_atime(dentry->d_inode, h_file->f_dentry->d_inode);
+
+ out_unlock:
+ if (!wlock && mmapped)
+ fi_read_unlock(file);
+ else
+ fi_write_unlock(file);
+ out:
+ si_read_unlock(sb);
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static unsigned int aufs_poll(struct file *file, poll_table *wait)
+{
+ unsigned int mask;
+ struct file *h_file;
+ int err;
+ struct dentry *dentry;
+ struct super_block *sb;
+
+ dentry = file->f_dentry;
+ LKTRTrace("%.*s, wait %p\n", AuDLNPair(dentry), wait);
+ AuDebugOn(S_ISDIR(dentry->d_inode->i_mode));
+
+ /* We should pretend an error happened. */
+ mask = POLLERR /* | POLLIN | POLLOUT */;
+ sb = dentry->d_sb;
+ si_read_lock(sb, AuLock_FLUSH);
+ err = au_reval_and_lock_finfo(file, au_reopen_nondir, /*wlock*/0,
+ /*locked*/0);
+ //err = -1;
+ if (unlikely(err))
+ goto out;
+
+ /* it is not an error of hidden_file has no operation */
+ mask = DEFAULT_POLLMASK;
+ h_file = au_h_fptr(file, au_fbstart(file));
+ if (h_file->f_op && h_file->f_op->poll)
+ mask = h_file->f_op->poll(h_file, wait);
+ fi_read_unlock(file);
+
+ out:
+ si_read_unlock(sb);
+ AuTraceErr((int)mask);
+ return mask;
+}
+
+static int aufs_fsync_nondir(struct file *file, struct dentry *dentry,
+ int datasync)
+{
+ int err;
+ struct inode *inode;
+ struct file *h_file;
+ struct super_block *sb;
+
+ LKTRTrace("%.*s, %d\n", AuDLNPair(dentry), datasync);
+ inode = dentry->d_inode;
+
+ IMustLock(file->f_mapping->host);
+ if (unlikely(inode != file->f_mapping->host)) {
+ mutex_unlock(&file->f_mapping->host->i_mutex);
+ mutex_lock(&inode->i_mutex);
+ }
+ IMustLock(inode);
+
+ sb = dentry->d_sb;
+ si_read_lock(sb, AuLock_FLUSH);
+ err = 0; //-EBADF; // posix?
+ if (unlikely(!(file->f_mode & FMODE_WRITE)))
+ goto out;
+ err = au_reval_and_lock_finfo(file, au_reopen_nondir, /*wlock*/1,
+ /*locked*/1);
+ //err = -1;
+ if (unlikely(err))
+ goto out;
+ err = au_ready_to_write(file, -1);
+ //err = -1;
+ if (unlikely(err))
+ goto out_unlock;
+
+ err = -EINVAL;
+ h_file = au_h_fptr(file, au_fbstart(file));
+ if (h_file->f_op && h_file->f_op->fsync) {
+ ii_write_lock_child(inode);
+ mutex_lock_nested(&h_file->f_dentry->d_inode->i_mutex,
+ AuLsc_I_CHILD);
+ err = h_file->f_op->fsync(h_file, h_file->f_dentry, datasync);
+ //err = -1;
+ if (!err)
+ au_update_fuse_h_inode(h_file->f_vfsmnt,
+ h_file->f_dentry);
+ au_cpup_attr_timesizes(inode);
+ mutex_unlock(&h_file->f_dentry->d_inode->i_mutex);
+ ii_write_unlock(inode);
+ }
+
+ out_unlock:
+ fi_write_unlock(file);
+ out:
+ si_read_unlock(sb);
+ if (unlikely(inode != file->f_mapping->host)) {
+ mutex_unlock(&inode->i_mutex);
+ mutex_lock(&file->f_mapping->host->i_mutex);
+ }
+ AuTraceErr(err);
+ return err;
+}
+
+static int aufs_fasync(int fd, struct file *file, int flag)
+{
+ int err;
+ struct file *h_file;
+ struct dentry *dentry;
+ struct super_block *sb;
+
+ dentry = file->f_dentry;
+ LKTRTrace("%.*s, %d\n", AuDLNPair(dentry), flag);
+
+ sb = dentry->d_sb;
+ si_read_lock(sb, AuLock_FLUSH);
+ err = au_reval_and_lock_finfo(file, au_reopen_nondir, /*wlock*/0,
+ /*locked*/0);
+ //err = -1;
+ if (unlikely(err))
+ goto out;
+
+ h_file = au_h_fptr(file, au_fbstart(file));
+ if (h_file->f_op && h_file->f_op->fasync)
+ err = h_file->f_op->fasync(fd, h_file, flag);
+ fi_read_unlock(file);
+
+ out:
+ si_read_unlock(sb);
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+struct file_operations aufs_file_fop = {
+ .read = aufs_read,
+ .write = aufs_write,
+ .poll = aufs_poll,
+ .mmap = aufs_mmap,
+ .open = aufs_open_nondir,
+ .flush = aufs_flush,
+ .release = aufs_release_nondir,
+ .fsync = aufs_fsync_nondir,
+ .fasync = aufs_fasync,
+#ifdef CONFIG_AUFS_SPLICE_PATCH
+ .splice_write = aufs_splice_write,
+ .splice_read = aufs_splice_read,
+#endif
+};
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 41/67] aufs directory operations, header
2008-05-16 14:32 ` [PATCH 40/67] aufs file and vm operations hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 42/67] aufs directory operations, source hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs directory operations, header
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/dir.h | 146 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 146 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/dir.h b/fs/aufs/dir.h
new file mode 100644
index 0000000..94ccbd1
--- /dev/null
+++ b/fs/aufs/dir.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * directory operations
+ *
+ * $Id: dir.h,v 1.2 2008/04/21 01:34:26 sfjro Exp $
+ */
+
+#ifndef __AUFS_DIR_H__
+#define __AUFS_DIR_H__
+
+#ifdef __KERNEL__
+
+#include <linux/fs.h>
+#include <linux/aufs_type.h>
+
+/* ---------------------------------------------------------------------- */
+
+/* need to be faster and smaller */
+
+#define AuSize_DEBLK 512 // todo: changeable
+#define AuSize_NHASH 32 // todo: changeable
+#if AuSize_DEBLK < NAME_MAX || PAGE_SIZE < AuSize_DEBLK
+#error invalid size AuSize_DEBLK
+#endif
+
+typedef char au_vdir_deblk_t[AuSize_DEBLK];
+
+struct au_nhash {
+ struct hlist_head heads[AuSize_NHASH];
+};
+
+struct au_vdir_destr {
+ unsigned char len;
+ char name[0];
+} __packed;
+
+struct au_vdir_dehstr {
+ struct hlist_node hash;
+ struct au_vdir_destr *str;
+};
+
+struct au_vdir_de {
+ ino_t de_ino;
+ unsigned char de_type;
+ /* caution: packed */
+ struct au_vdir_destr de_str;
+} __packed;
+
+struct au_vdir_wh {
+ struct hlist_node wh_hash;
+ aufs_bindex_t wh_bindex;
+#ifdef CONFIG_AUFS_SHWH
+ ino_t wh_ino;
+ unsigned char wh_type;
+ /* caution: packed */
+#endif
+ struct au_vdir_destr wh_str;
+} __packed;
+
+union au_vdir_deblk_p {
+ unsigned char *p;
+ au_vdir_deblk_t *deblk;
+ struct au_vdir_de *de;
+};
+
+struct au_vdir {
+ au_vdir_deblk_t **vd_deblk;
+ int vd_nblk;
+ struct {
+ int i;
+ union au_vdir_deblk_p p;
+ } vd_last;
+
+ unsigned long vd_version;
+ unsigned long vd_jiffy;
+};
+
+/* ---------------------------------------------------------------------- */
+
+/* dir.c */
+extern struct file_operations aufs_dir_fop;
+int au_test_empty_lower(struct dentry *dentry);
+int au_test_empty(struct dentry *dentry, struct au_nhash *whlist);
+
+/* vdir.c */
+struct au_nhash *au_nhash_new(gfp_t gfp);
+void au_nhash_del(struct au_nhash *nhash);
+void au_nhash_init(struct au_nhash *nhash);
+void au_nhash_move(struct au_nhash *dst, struct au_nhash *src);
+void au_nhash_fin(struct au_nhash *nhash);
+int au_nhash_test_longer_wh(struct au_nhash *whlist, aufs_bindex_t btgt,
+ int limit);
+int au_nhash_test_known_wh(struct au_nhash *whlist, char *name, int namelen);
+int au_nhash_append_wh(struct au_nhash *whlist, char *name, int namelen,
+ ino_t ino, unsigned int d_type, aufs_bindex_t bindex,
+ unsigned char shwh);
+void au_vdir_free(struct au_vdir *vdir);
+int au_vdir_init(struct file *file);
+int au_vdir_fill_de(struct file *file, void *dirent, filldir_t filldir);
+
+/* ---------------------------------------------------------------------- */
+
+static inline
+void au_shwh_init_wh(struct au_vdir_wh *wh, ino_t ino, unsigned char d_type)
+{
+#ifdef CONFIG_AUFS_SHWH
+ wh->wh_ino = ino;
+ wh->wh_type = d_type;
+#endif
+}
+
+static inline void au_add_nlink(struct inode *dir, struct inode *h_dir)
+{
+ AuDebugOn(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode));
+ dir->i_nlink += h_dir->i_nlink - 2;
+ if (unlikely(h_dir->i_nlink < 2))
+ dir->i_nlink += 2;
+}
+
+static inline void au_sub_nlink(struct inode *dir, struct inode *h_dir)
+{
+ AuDebugOn(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode));
+ dir->i_nlink -= h_dir->i_nlink - 2;
+ if (unlikely(h_dir->i_nlink < 2))
+ dir->i_nlink -= 2;
+}
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_DIR_H__ */
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 42/67] aufs directory operations, source
2008-05-16 14:32 ` [PATCH 41/67] aufs directory operations, header hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 43/67] aufs virtual or vertical directory hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs directory operations, source
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/dir.c | 573 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 573 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/dir.c b/fs/aufs/dir.c
new file mode 100644
index 0000000..e9dac57
--- /dev/null
+++ b/fs/aufs/dir.c
@@ -0,0 +1,573 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * directory operations
+ *
+ * $Id: dir.c,v 1.4 2008/05/12 00:27:58 sfjro Exp $
+ */
+
+#include <linux/fs_stack.h>
+#include "aufs.h"
+
+static int reopen_dir(struct file *file)
+{
+ int err;
+ struct dentry *dentry, *h_dentry;
+ aufs_bindex_t bindex, btail, bstart;
+ struct file *h_file;
+
+ dentry = file->f_dentry;
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+ AuDebugOn(!S_ISDIR(dentry->d_inode->i_mode));
+
+ /* open all hidden dirs */
+ bstart = au_dbstart(dentry);
+#if 1
+ for (bindex = au_fbstart(file); bindex < bstart; bindex++)
+ au_set_h_fptr(file, bindex, NULL);
+#endif
+ au_set_fbstart(file, bstart);
+ btail = au_dbtaildir(dentry);
+#if 1
+ for (bindex = au_fbend(file); btail < bindex; bindex--)
+ au_set_h_fptr(file, bindex, NULL);
+#endif
+ au_set_fbend(file, btail);
+ for (bindex = bstart; bindex <= btail; bindex++) {
+ h_dentry = au_h_dptr(dentry, bindex);
+ if (!h_dentry)
+ continue;
+ h_file = au_h_fptr(file, bindex);
+ if (h_file) {
+ AuDebugOn(h_file->f_dentry != h_dentry);
+ continue;
+ }
+
+ h_file = au_h_open(dentry, bindex, file->f_flags, file);
+ // unavailable
+ //if (LktrCond) {fput(h_file);
+ //au_br_put(au_sbr(dentry->d_sb, bindex));h_file=ERR_PTR(-1);}
+ err = PTR_ERR(h_file);
+ if (IS_ERR(h_file))
+ goto out; // close all?
+ //cpup_file_flags(h_file, file);
+ au_set_h_fptr(file, bindex, h_file);
+ }
+ au_update_figen(file);
+ //file->f_ra = h_file->f_ra; //??
+ err = 0;
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+static int do_open_dir(struct file *file, int flags)
+{
+ int err;
+ aufs_bindex_t bindex, btail;
+ struct dentry *dentry, *h_dentry;
+ struct file *h_file;
+
+ dentry = file->f_dentry;
+ LKTRTrace("%.*s, 0x%x\n", AuDLNPair(dentry), flags);
+ AuDebugOn(!dentry->d_inode || !S_ISDIR(dentry->d_inode->i_mode));
+
+ err = 0;
+ au_set_fvdir_cache(file, NULL);
+ file->f_version = dentry->d_inode->i_version;
+ bindex = au_dbstart(dentry);
+ au_set_fbstart(file, bindex);
+ btail = au_dbtaildir(dentry);
+ au_set_fbend(file, btail);
+ for (; !err && bindex <= btail; bindex++) {
+ h_dentry = au_h_dptr(dentry, bindex);
+ if (!h_dentry)
+ continue;
+
+ h_file = au_h_open(dentry, bindex, flags, file);
+ //if (LktrCond) {fput(h_file);
+ //au_br_put(au_sbr(dentry->d_sb, bindex));h_file=ERR_PTR(-1);}
+ if (!IS_ERR(h_file)) {
+ au_set_h_fptr(file, bindex, h_file);
+ continue;
+ }
+ err = PTR_ERR(h_file);
+ }
+ au_update_figen(file);
+ //file->f_ra = h_file->f_ra; //??
+ if (!err)
+ return 0; /* success */
+
+ /* close all */
+ for (bindex = au_fbstart(file); !err && bindex <= btail; bindex++)
+ au_set_h_fptr(file, bindex, NULL);
+ au_set_fbstart(file, -1);
+ au_set_fbend(file, -1);
+ return err;
+}
+
+static int aufs_open_dir(struct inode *inode, struct file *file)
+{
+ return au_do_open(inode, file, do_open_dir);
+}
+
+static int aufs_release_dir(struct inode *inode, struct file *file)
+{
+ struct au_vdir *vdir_cache;
+ struct super_block *sb;
+
+ LKTRTrace("i%lu, %.*s\n", inode->i_ino, AuDLNPair(file->f_dentry));
+
+ sb = file->f_dentry->d_sb;
+ si_noflush_read_lock(sb);
+ fi_write_lock(file);
+ vdir_cache = au_fvdir_cache(file);
+ if (vdir_cache)
+ au_vdir_free(vdir_cache);
+ fi_write_unlock(file);
+ au_finfo_fin(file);
+ si_read_unlock(sb);
+ return 0;
+}
+
+static int fsync_dir(struct dentry *dentry, int datasync)
+{
+ int err;
+ struct inode *inode;
+ struct super_block *sb;
+ aufs_bindex_t bend, bindex;
+
+ LKTRTrace("%.*s, %d\n", AuDLNPair(dentry), datasync);
+ DiMustAnyLock(dentry);
+ sb = dentry->d_sb;
+ SiMustAnyLock(sb);
+ inode = dentry->d_inode;
+ IMustLock(inode);
+ IiMustAnyLock(inode);
+
+ err = 0;
+ bend = au_dbend(dentry);
+ for (bindex = au_dbstart(dentry); !err && bindex <= bend; bindex++) {
+ struct dentry *h_dentry;
+ struct inode *h_inode;
+ struct file_operations *fop;
+
+ if (au_test_ro(sb, bindex, inode))
+ continue;
+ h_dentry = au_h_dptr(dentry, bindex);
+ if (!h_dentry)
+ continue;
+ h_inode = h_dentry->d_inode;
+ if (!h_inode)
+ continue;
+
+ /* cf. fs/nsfd/vfs.c and fs/nfsd/nfs4recover.c */
+ //au_hdir_lock(h_inode, inode, bindex);
+ mutex_lock(&h_inode->i_mutex);
+ fop = (void *)h_inode->i_fop;
+ err = filemap_fdatawrite(h_inode->i_mapping);
+ if (!err && fop && fop->fsync)
+ err = fop->fsync(NULL, h_dentry, datasync);
+ if (!err)
+ err = filemap_fdatawrite(h_inode->i_mapping);
+ if (!err)
+ au_update_fuse_h_inode(NULL, h_dentry); /*ignore*/
+ //au_hdir_unlock(h_inode, inode, bindex);
+ mutex_unlock(&h_inode->i_mutex);
+ }
+
+ AuTraceErr(err);
+ return err;
+}
+
+/*
+ * @file may be NULL
+ */
+static int aufs_fsync_dir(struct file *file, struct dentry *dentry,
+ int datasync)
+{
+ int err;
+ struct inode *inode;
+ struct file *h_file;
+ struct super_block *sb;
+ aufs_bindex_t bend, bindex;
+
+ LKTRTrace("%.*s, %d\n", AuDLNPair(dentry), datasync);
+ inode = dentry->d_inode;
+ IMustLock(inode);
+
+ err = 0;
+ sb = dentry->d_sb;
+ si_noflush_read_lock(sb);
+ if (file) {
+ err = au_reval_and_lock_finfo(file, reopen_dir, /*wlock*/1,
+ /*locked*/1);
+ //err = -1;
+ if (unlikely(err))
+ goto out;
+ } else
+ di_read_lock_child(dentry, !AuLock_IW);
+
+ ii_write_lock_child(inode);
+ if (file) {
+ bend = au_fbend(file);
+ for (bindex = au_fbstart(file); !err && bindex <= bend;
+ bindex++) {
+ h_file = au_h_fptr(file, bindex);
+ if (!h_file || au_test_ro(sb, bindex, inode))
+ continue;
+
+ err = -EINVAL;
+ if (h_file->f_op && h_file->f_op->fsync) {
+ // todo: try do_fsync() in fs/sync.c
+#if 0 // debug
+ AuDebugOn(h_file->f_dentry->d_inode
+ != au_h_iptr(inode, bindex));
+ au_hdir_lock(h_file->f_dentry->d_inode, inode,
+ bindex);
+#else
+ mutex_lock(&h_file->f_mapping->host->i_mutex);
+#endif
+ err = h_file->f_op->fsync
+ (h_file, h_file->f_dentry, datasync);
+ //err = -1;
+ if (!err)
+ au_update_fuse_h_inode
+ (h_file->f_vfsmnt,
+ h_file->f_dentry);
+ /*ignore*/
+#if 0 // debug
+ au_hdir_unlock(h_file->f_dentry->d_inode, inode,
+ bindex);
+#else
+ mutex_unlock(&h_file->f_mapping->host->i_mutex);
+#endif
+ }
+ }
+ } else
+ err = fsync_dir(dentry, datasync);
+ au_cpup_attr_timesizes(inode);
+ ii_write_unlock(inode);
+ if (file)
+ fi_write_unlock(file);
+ else
+ di_read_unlock(dentry, !AuLock_IW);
+
+ out:
+ si_read_unlock(sb);
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int aufs_readdir(struct file *file, void *dirent, filldir_t filldir)
+{
+ int err;
+ struct dentry *dentry;
+ struct inode *inode;
+ struct super_block *sb;
+
+ dentry = file->f_dentry;
+ LKTRTrace("%.*s, pos %Ld\n", AuDLNPair(dentry), file->f_pos);
+ inode = dentry->d_inode;
+ IMustLock(inode);
+
+ au_nfsd_lockdep_off();
+ sb = dentry->d_sb;
+ si_read_lock(sb, AuLock_FLUSH);
+ err = au_reval_and_lock_finfo(file, reopen_dir, /*wlock*/1,
+ /*locked*/1);
+ if (unlikely(err))
+ goto out;
+
+ ii_write_lock_child(inode);
+ err = au_vdir_init(file);
+ if (unlikely(err)) {
+ ii_write_unlock(inode);
+ goto out_unlock;
+ }
+ //DbgVdir(au_fvdir_cache(file));// goto out_unlock;
+
+ /* nfsd filldir calls lookup_one_len(). */
+ ii_downgrade_lock(inode);
+ err = au_vdir_fill_de(file, dirent, filldir);
+ //DbgVdir(au_fvdir_cache(file));// goto out_unlock;
+
+ fsstack_copy_attr_atime(inode, au_h_iptr(inode, au_ibstart(inode)));
+ ii_read_unlock(inode);
+
+ out_unlock:
+ fi_write_unlock(file);
+ out:
+ si_read_unlock(sb);
+ au_nfsd_lockdep_on();
+#if 0 // debug
+ if (LktrCond)
+ igrab(inode);
+#endif
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+#define AuTestEmpty_WHONLY 1
+#define AuTestEmpty_DLGT (1 << 1)
+#define AuTestEmpty_DIRPERM1 (1 << 2)
+#define AuTestEmpty_CALLED (1 << 3)
+#define AuTestEmpty_SHWH (1 << 4)
+#define au_ftest_testempty(flags, name) ((flags) & AuTestEmpty_##name)
+#define au_fset_testempty(flags, name) { (flags) |= AuTestEmpty_##name; }
+#define au_fclr_testempty(flags, name) { (flags) &= ~AuTestEmpty_##name; }
+#ifndef CONFIG_AUFS_DLGT
+#undef AuTestEmpty_DLGT
+#define AuTestEmpty_DLGT 0
+#undef AuTestEmpty_DIRPERM1
+#define AuTestEmpty_DIRPERM1 0
+#endif
+#ifndef CONFIG_AUFS_SHWH
+#undef AuTestEmpty_SHWH
+#define AuTestEmpty_SHWH 0
+#endif
+
+struct test_empty_arg {
+ struct au_nhash *whlist;
+ unsigned int flags;
+ int err;
+ aufs_bindex_t bindex;
+};
+
+static int test_empty_cb(void *__arg, const char *__name, int namelen,
+ loff_t offset, u64 ino, unsigned int d_type)
+{
+ struct test_empty_arg *arg = __arg;
+ char *name = (void *)__name;
+
+ LKTRTrace("%.*s\n", namelen, name);
+
+ arg->err = 0;
+ au_fset_testempty(arg->flags, CALLED);
+ //smp_mb();
+ if (name[0] == '.'
+ && (namelen == 1 || (name[1] == '.' && namelen == 2)))
+ return 0; /* success */
+
+ if (namelen <= AUFS_WH_PFX_LEN
+ || memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) {
+ if (au_ftest_testempty(arg->flags, WHONLY)
+ && !au_nhash_test_known_wh(arg->whlist, name, namelen))
+ arg->err = -ENOTEMPTY;
+ goto out;
+ }
+
+ name += AUFS_WH_PFX_LEN;
+ namelen -= AUFS_WH_PFX_LEN;
+ if (!au_nhash_test_known_wh(arg->whlist, name, namelen))
+ arg->err = au_nhash_append_wh
+ (arg->whlist, name, namelen, ino, d_type, arg->bindex,
+ au_ftest_testempty(arg->flags, SHWH));
+
+ out:
+ //smp_mb();
+ AuTraceErr(arg->err);
+ return arg->err;
+}
+
+static int do_test_empty(struct dentry *dentry, struct test_empty_arg *arg)
+{
+ int err, dlgt;
+ struct file *h_file;
+
+ LKTRTrace("%.*s, {%p, 0x%x, %d}\n",
+ AuDLNPair(dentry), arg->whlist, arg->flags, arg->bindex);
+
+ h_file = au_h_open(dentry, arg->bindex,
+ O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_LARGEFILE,
+ /*file*/NULL);
+ err = PTR_ERR(h_file);
+ if (IS_ERR(h_file))
+ goto out;
+ err = 0;
+ if (unlikely(au_opt_test(au_mntflags(dentry->d_sb), UDBA_INOTIFY)
+ && !h_file->f_dentry->d_inode->i_nlink))
+ goto out_put;
+
+ dlgt = au_ftest_testempty(arg->flags, DLGT);
+ //h_file->f_pos = 0;
+ do {
+ arg->err = 0;
+ au_fclr_testempty(arg->flags, CALLED);
+ //smp_mb();
+ err = vfsub_readdir(h_file, test_empty_cb, arg, dlgt);
+ if (err >= 0)
+ err = arg->err;
+ } while (!err && au_ftest_testempty(arg->flags, CALLED));
+
+ out_put:
+ fput(h_file);
+ au_sbr_put(dentry->d_sb, arg->bindex);
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+struct do_test_empty_args {
+ int *errp;
+ struct dentry *dentry;
+ struct test_empty_arg *arg;
+};
+
+static void call_do_test_empty(void *args)
+{
+ struct do_test_empty_args *a = args;
+ *a->errp = do_test_empty(a->dentry, a->arg);
+}
+
+static int sio_test_empty(struct dentry *dentry, struct test_empty_arg *arg)
+{
+ int err, wkq_err;
+ struct dentry *h_dentry;
+ struct inode *h_inode;
+
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+ h_dentry = au_h_dptr(dentry, arg->bindex);
+ AuDebugOn(!h_dentry);
+ h_inode = h_dentry->d_inode;
+ AuDebugOn(!h_inode || !S_ISDIR(h_inode->i_mode));
+
+ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);
+ err = au_test_h_perm_sio(h_inode, MAY_EXEC | MAY_READ,
+ au_opt_test_dlgt(au_mntflags(dentry->d_sb)));
+ mutex_unlock(&h_inode->i_mutex);
+ if (!err)
+ err = do_test_empty(dentry, arg);
+ else {
+ struct do_test_empty_args args = {
+ .errp = &err,
+ .dentry = dentry,
+ .arg = arg
+ };
+ unsigned int flags = arg->flags;
+ au_fclr_testempty(arg->flags, DLGT);
+ au_fclr_testempty(arg->flags, DIRPERM1);
+ wkq_err = au_wkq_wait(call_do_test_empty, &args, /*dlgt*/0);
+ if (unlikely(wkq_err))
+ err = wkq_err;
+ arg->flags = flags;
+ }
+
+ AuTraceErr(err);
+ return err;
+}
+
+int au_test_empty_lower(struct dentry *dentry)
+{
+ int err;
+ struct inode *inode;
+ struct test_empty_arg arg;
+ struct au_nhash *whlist;
+ aufs_bindex_t bindex, bstart, btail;
+ unsigned int mnt_flags;
+
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+ inode = dentry->d_inode;
+ AuDebugOn(!inode || !S_ISDIR(inode->i_mode));
+
+ whlist = au_nhash_new(GFP_TEMPORARY);
+ err = PTR_ERR(whlist);
+ if (IS_ERR(whlist))
+ goto out;
+
+ bstart = au_dbstart(dentry);
+ mnt_flags = au_mntflags(dentry->d_sb);
+ arg.whlist = whlist;
+ arg.flags = 0;
+ if (unlikely(au_opt_test_dlgt(mnt_flags)))
+ au_fset_testempty(arg.flags, DLGT);
+ if (unlikely(au_opt_test(mnt_flags, SHWH)))
+ au_fset_testempty(arg.flags, SHWH);
+ arg.bindex = bstart;
+ err = do_test_empty(dentry, &arg);
+ if (unlikely(err))
+ goto out_whlist;
+
+ au_fset_testempty(arg.flags, WHONLY);
+ if (unlikely(au_opt_test_dirperm1(mnt_flags)))
+ au_fset_testempty(arg.flags, DIRPERM1);
+ btail = au_dbtaildir(dentry);
+ for (bindex = bstart + 1; !err && bindex <= btail; bindex++) {
+ struct dentry *h_dentry;
+ h_dentry = au_h_dptr(dentry, bindex);
+ if (h_dentry && h_dentry->d_inode) {
+ AuDebugOn(!S_ISDIR(h_dentry->d_inode->i_mode));
+ arg.bindex = bindex;
+ err = do_test_empty(dentry, &arg);
+ }
+ }
+
+ out_whlist:
+ au_nhash_del(whlist);
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+int au_test_empty(struct dentry *dentry, struct au_nhash *whlist)
+{
+ int err;
+ struct inode *inode;
+ struct test_empty_arg arg;
+ aufs_bindex_t bindex, btail;
+
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+ inode = dentry->d_inode;
+ AuDebugOn(!inode || !S_ISDIR(inode->i_mode));
+
+ err = 0;
+ arg.whlist = whlist;
+ arg.flags = AuTestEmpty_WHONLY;
+ if (unlikely(au_opt_test(au_mntflags(dentry->d_sb), SHWH)))
+ au_fset_testempty(arg.flags, SHWH);
+ btail = au_dbtaildir(dentry);
+ for (bindex = au_dbstart(dentry); !err && bindex <= btail; bindex++) {
+ struct dentry *h_dentry;
+ h_dentry = au_h_dptr(dentry, bindex);
+ if (h_dentry && h_dentry->d_inode) {
+ AuDebugOn(!S_ISDIR(h_dentry->d_inode->i_mode));
+ arg.bindex = bindex;
+ err = sio_test_empty(dentry, &arg);
+ }
+ }
+
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+struct file_operations aufs_dir_fop = {
+ .read = generic_read_dir,
+ .readdir = aufs_readdir,
+ .open = aufs_open_dir,
+ .release = aufs_release_dir,
+ .flush = aufs_flush,
+ .fsync = aufs_fsync_dir,
+};
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 43/67] aufs virtual or vertical directory
2008-05-16 14:32 ` [PATCH 42/67] aufs directory operations, source hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 44/67] aufs inode operations hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs virtual or vertical directory
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/vdir.c | 963 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 963 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/vdir.c b/fs/aufs/vdir.c
new file mode 100644
index 0000000..f2b67e9
--- /dev/null
+++ b/fs/aufs/vdir.c
@@ -0,0 +1,963 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * virtual or vertical directory
+ *
+ * $Id: vdir.c,v 1.3 2008/04/28 03:05:48 sfjro Exp $
+ */
+
+#include "aufs.h"
+
+static int calc_size(int namelen)
+{
+ int sz;
+
+ sz = sizeof(struct au_vdir_de) + namelen;
+ if (sizeof(ino_t) == sizeof(long)) {
+ const int mask = sizeof(ino_t) - 1;
+ if (sz & mask) {
+ sz += sizeof(ino_t);
+ sz &= ~mask;
+ }
+ } else {
+#if 0 // remove
+ BUG();
+ /* this block will be discarded by optimizer. */
+ int m;
+ m = sz % sizeof(ino_t);
+ if (m)
+ sz += sizeof(ino_t) - m;
+#endif
+ }
+
+ AuDebugOn(sz % sizeof(ino_t));
+ return sz;
+}
+
+static int set_deblk_end(union au_vdir_deblk_p *p,
+ union au_vdir_deblk_p *deblk_end)
+{
+ if (calc_size(0) <= deblk_end->p - p->p) {
+ p->de->de_str.len = 0;
+ //smp_mb();
+ return 0;
+ }
+ return -1; /* error */
+}
+
+/* returns true or false */
+static int is_deblk_end(union au_vdir_deblk_p *p,
+ union au_vdir_deblk_p *deblk_end)
+{
+ if (calc_size(0) <= deblk_end->p - p->p)
+ return !p->de->de_str.len;
+ return 1;
+}
+
+static au_vdir_deblk_t *last_deblk(struct au_vdir *vdir)
+{
+ return vdir->vd_deblk[vdir->vd_nblk - 1];
+}
+
+void au_nhash_init(struct au_nhash *nhash)
+{
+ int i;
+ for (i = 0; i < AuSize_NHASH; i++)
+ INIT_HLIST_HEAD(nhash->heads + i);
+}
+
+struct au_nhash *au_nhash_new(gfp_t gfp)
+{
+ struct au_nhash *nhash;
+
+ nhash = kmalloc(sizeof(*nhash), gfp);
+ if (nhash) {
+ au_nhash_init(nhash);
+ return nhash;
+ }
+ return ERR_PTR(-ENOMEM);
+}
+
+void au_nhash_del(struct au_nhash *nhash)
+{
+ au_nhash_fin(nhash);
+ kfree(nhash);
+}
+
+void au_nhash_move(struct au_nhash *dst, struct au_nhash *src)
+{
+ int i;
+
+ AuTraceEnter();
+
+ //DbgWhlist(src);
+ *dst = *src;
+ for (i = 0; i < AuSize_NHASH; i++) {
+ struct hlist_head *h;
+ h = dst->heads + i;
+ if (h->first)
+ h->first->pprev = &h->first;
+ INIT_HLIST_HEAD(src->heads + i);
+ }
+ //DbgWhlist(src);
+ //DbgWhlist(dst);
+ //smp_mb();
+}
+
+/* ---------------------------------------------------------------------- */
+
+void au_nhash_fin(struct au_nhash *whlist)
+{
+ int i;
+ struct hlist_head *head;
+ struct au_vdir_wh *tpos;
+ struct hlist_node *pos, *n;
+
+ AuTraceEnter();
+
+ for (i = 0; i < AuSize_NHASH; i++) {
+ head = whlist->heads + i;
+ hlist_for_each_entry_safe(tpos, pos, n, head, wh_hash) {
+ //hlist_del(pos);
+ kfree(tpos);
+ }
+ }
+}
+
+int au_nhash_test_longer_wh(struct au_nhash *whlist, aufs_bindex_t btgt,
+ int limit)
+{
+ int n, i;
+ struct hlist_head *head;
+ struct au_vdir_wh *tpos;
+ struct hlist_node *pos;
+
+ LKTRTrace("limit %d\n", limit);
+ //return 1;
+
+ n = 0;
+ for (i = 0; i < AuSize_NHASH; i++) {
+ head = whlist->heads + i;
+ hlist_for_each_entry(tpos, pos, head, wh_hash)
+ if (tpos->wh_bindex == btgt && ++n > limit)
+ return 1;
+ }
+ return 0;
+}
+
+static unsigned int au_name_hash(const unsigned char *name, unsigned int len)
+{
+ return (full_name_hash(name, len) % AuSize_NHASH);
+}
+
+/* returns found(true) or not */
+int au_nhash_test_known_wh(struct au_nhash *whlist, char *name, int namelen)
+{
+ struct hlist_head *head;
+ struct au_vdir_wh *tpos;
+ struct hlist_node *pos;
+ struct au_vdir_destr *str;
+
+ LKTRTrace("%.*s\n", namelen, name);
+
+ head = whlist->heads + au_name_hash(name, namelen);
+ hlist_for_each_entry(tpos, pos, head, wh_hash) {
+ str = &tpos->wh_str;
+ LKTRTrace("%.*s\n", str->len, str->name);
+ if (str->len == namelen && !memcmp(str->name, name, namelen))
+ return 1;
+ }
+ return 0;
+}
+
+int au_nhash_append_wh(struct au_nhash *whlist, char *name, int namelen,
+ ino_t ino, unsigned int d_type, aufs_bindex_t bindex,
+ unsigned char shwh)
+{
+ int err;
+ struct au_vdir_destr *str;
+ struct au_vdir_wh *wh;
+
+ LKTRTrace("%.*s\n", namelen, name);
+
+ err = -ENOMEM;
+ wh = kmalloc(sizeof(*wh) + namelen, GFP_TEMPORARY);
+ if (unlikely(!wh))
+ goto out;
+ err = 0;
+ wh->wh_bindex = bindex;
+ if (unlikely(shwh))
+ au_shwh_init_wh(wh, ino, d_type);
+ str = &wh->wh_str;
+ str->len = namelen;
+ memcpy(str->name, name, namelen);
+ hlist_add_head(&wh->wh_hash,
+ whlist->heads + au_name_hash(name, namelen));
+ //smp_mb();
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+void au_vdir_free(struct au_vdir *vdir)
+{
+ au_vdir_deblk_t **deblk;
+
+ AuTraceEnter();
+
+ deblk = vdir->vd_deblk;
+ while (vdir->vd_nblk--) {
+ kfree(*deblk);
+ deblk++;
+ }
+ kfree(vdir->vd_deblk);
+ au_cache_free_vdir(vdir);
+}
+
+static int append_deblk(struct au_vdir *vdir)
+{
+ int err, sz, i;
+ au_vdir_deblk_t **o;
+ union au_vdir_deblk_p p, deblk_end;
+
+ AuTraceEnter();
+
+ err = -ENOMEM;
+ sz = sizeof(*o) * vdir->vd_nblk;
+ o = au_kzrealloc(vdir->vd_deblk, sz, sz + sizeof(*o), GFP_KERNEL);
+ if (unlikely(!o))
+ goto out;
+ vdir->vd_deblk = o;
+ p.deblk = kmalloc(sizeof(*p.deblk), GFP_KERNEL);
+ if (p.deblk) {
+ i = vdir->vd_nblk++;
+ vdir->vd_deblk[i] = p.deblk;
+ vdir->vd_last.i = i;
+ vdir->vd_last.p.p = p.p;
+ deblk_end.deblk = p.deblk + 1;
+ err = set_deblk_end(&p, &deblk_end);
+ AuDebugOn(err);
+ }
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+static struct au_vdir *alloc_vdir(void)
+{
+ struct au_vdir *vdir;
+ int err;
+
+ AuTraceEnter();
+
+ err = -ENOMEM;
+ vdir = au_cache_alloc_vdir();
+ if (unlikely(!vdir))
+ goto out;
+ vdir->vd_deblk = kzalloc(sizeof(*vdir->vd_deblk), GFP_KERNEL);
+ if (unlikely(!vdir->vd_deblk))
+ goto out_free;
+
+ vdir->vd_nblk = 0;
+ vdir->vd_version = 0;
+ vdir->vd_jiffy = 0;
+ err = append_deblk(vdir);
+ if (!err)
+ return vdir; /* success */
+
+ kfree(vdir->vd_deblk);
+
+ out_free:
+ au_cache_free_vdir(vdir);
+ out:
+ vdir = ERR_PTR(err);
+ AuTraceErrPtr(vdir);
+ return vdir;
+}
+
+static int reinit_vdir(struct au_vdir *vdir)
+{
+ int err;
+ union au_vdir_deblk_p p, deblk_end;
+
+ AuTraceEnter();
+
+ while (vdir->vd_nblk > 1) {
+ kfree(vdir->vd_deblk[vdir->vd_nblk - 1]);
+ vdir->vd_deblk[vdir->vd_nblk - 1] = NULL;
+ vdir->vd_nblk--;
+ }
+ p.deblk = vdir->vd_deblk[0];
+ deblk_end.deblk = p.deblk + 1;
+ err = set_deblk_end(&p, &deblk_end);
+ AuDebugOn(err);
+ vdir->vd_version = 0;
+ vdir->vd_jiffy = 0;
+ vdir->vd_last.i = 0;
+ vdir->vd_last.p.deblk = vdir->vd_deblk[0];
+ //smp_mb();
+ //DbgVdir(vdir);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void free_dehlist(struct au_nhash *dehlist)
+{
+ int i;
+ struct hlist_head *head;
+ struct au_vdir_dehstr *tpos;
+ struct hlist_node *pos, *n;
+
+ AuTraceEnter();
+
+ for (i = 0; i < AuSize_NHASH; i++) {
+ head = dehlist->heads + i;
+ hlist_for_each_entry_safe(tpos, pos, n, head, hash) {
+ //hlist_del(pos);
+ au_cache_free_dehstr(tpos);
+ }
+ }
+}
+
+/* returns found(true) or not */
+static int test_known(struct au_nhash *delist, char *name, int namelen)
+{
+ struct hlist_head *head;
+ struct au_vdir_dehstr *tpos;
+ struct hlist_node *pos;
+ struct au_vdir_destr *str;
+
+ LKTRTrace("%.*s\n", namelen, name);
+
+ head = delist->heads + au_name_hash(name, namelen);
+ hlist_for_each_entry(tpos, pos, head, hash) {
+ str = tpos->str;
+ LKTRTrace("%.*s\n", str->len, str->name);
+ if (str->len == namelen && !memcmp(str->name, name, namelen))
+ return 1;
+ }
+ return 0;
+
+}
+
+static int append_de(struct au_vdir *vdir, char *name, int namelen, ino_t ino,
+ unsigned int d_type, struct au_nhash *delist)
+{
+ int err, sz;
+ union au_vdir_deblk_p p, *room, deblk_end;
+ struct au_vdir_dehstr *dehstr;
+
+ LKTRTrace("%.*s %d, i%lu, dt%u\n", namelen, name, namelen, ino, d_type);
+
+ p.deblk = last_deblk(vdir);
+ deblk_end.deblk = p.deblk + 1;
+ room = &vdir->vd_last.p;
+ AuDebugOn(room->p < p.p || deblk_end.p <= room->p
+ || !is_deblk_end(room, &deblk_end));
+
+ sz = calc_size(namelen);
+ if (unlikely(sz > deblk_end.p - room->p)) {
+ err = append_deblk(vdir);
+ if (unlikely(err))
+ goto out;
+ p.deblk = last_deblk(vdir);
+ deblk_end.deblk = p.deblk + 1;
+ //smp_mb();
+ AuDebugOn(room->p != p.p);
+ }
+
+ err = -ENOMEM;
+ dehstr = au_cache_alloc_dehstr();
+ if (unlikely(!dehstr))
+ goto out;
+ dehstr->str = &room->de->de_str;
+ hlist_add_head(&dehstr->hash,
+ delist->heads + au_name_hash(name, namelen));
+
+ room->de->de_ino = ino;
+ room->de->de_type = d_type;
+ room->de->de_str.len = namelen;
+ memcpy(room->de->de_str.name, name, namelen);
+
+ err = 0;
+ room->p += sz;
+ if (unlikely(set_deblk_end(room, &deblk_end)))
+ err = append_deblk(vdir);
+ //smp_mb();
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int au_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
+ ino_t *ino)
+{
+ int err;
+ struct au_xino_entry xinoe;
+ static DEFINE_MUTEX(mtx);
+
+ /* a race condition for hardlinks */
+ mutex_lock(&mtx);
+ err = au_xino_read(sb, bindex, h_ino, &xinoe);
+ if (unlikely(err))
+ goto out;
+
+ if (!xinoe.ino) {
+ //struct inode *h_inode;
+ err = -EIO;
+ xinoe.ino = au_xino_new_ino(sb);
+ if (unlikely(!xinoe.ino))
+ goto out;
+#if 0 // rfu
+ //xinoe.h_gen = AuXino_INVALID_HGEN;
+ h_inode = ilookup(au_sbr_sb(sb, bindex), h_ino);
+ if (h_inode) {
+ if (!is_bad_inode(h_inode)) {
+ xinoe.h_gen = h_inode->i_generation;
+ WARN_ON(xinoe.h_gen == AuXino_INVALID_HGEN);
+ }
+ iput(h_inode);
+ }
+#endif
+ err = au_xino_write(sb, bindex, h_ino, &xinoe);
+ if (unlikely(err))
+ goto out;
+ }
+
+ *ino = xinoe.ino;
+
+ out:
+ mutex_unlock(&mtx);
+ AuTraceErr(err);
+ return err;
+}
+
+static int au_wh_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
+ ino_t *ino)
+{
+#ifdef CONFIG_AUFS_SHWH
+ return au_ino(sb, bindex, h_ino, ino);
+#else
+ return 0;
+#endif
+}
+
+#define AuFillVdir_CALLED 1
+#define AuFillVdir_SHWH (1 << 1)
+#define au_ftest_fillvdir(flags, name) ((flags) & AuFillVdir_##name)
+#define au_fset_fillvdir(flags, name) { (flags) |= AuFillVdir_##name; }
+#define au_fclr_fillvdir(flags, name) { (flags) &= ~AuFillVdir_##name; }
+#ifndef CONFIG_AUFS_SHWH
+#undef AuFillVdir_SHWH
+#define AuFillVdir_SHWH 0
+#endif
+
+struct fillvdir_arg {
+ struct file *file;
+ struct au_vdir *vdir;
+ struct au_nhash *delist;
+ struct au_nhash *whlist;
+ aufs_bindex_t bindex;
+ unsigned int flags;
+ int err;
+};
+
+static int fillvdir(void *__arg, const char *__name, int namelen, loff_t offset,
+ u64 h_ino, unsigned int d_type)
+{
+ struct fillvdir_arg *arg = __arg;
+ char *name = (void *)__name;
+ aufs_bindex_t bindex, bend;
+ struct super_block *sb;
+ ino_t ino;
+
+ LKTRTrace("%.*s, namelen %d, i%Lu, dt%u\n",
+ namelen, name, namelen, (u64)h_ino, d_type);
+
+ sb = arg->file->f_dentry->d_sb;
+ bend = arg->bindex;
+ arg->err = 0;
+ au_fset_fillvdir(arg->flags, CALLED);
+ //smp_mb();
+ if (namelen <= AUFS_WH_PFX_LEN
+ || memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) {
+ for (bindex = 0; bindex < bend; bindex++)
+ if (test_known(arg->delist + bindex, name, namelen)
+ || au_nhash_test_known_wh(arg->whlist + bindex,
+ name, namelen))
+ goto out; /* already exists or whiteouted */
+
+ ino = 1; /* why does gcc warns? */
+ arg->err = au_ino(sb, bend, h_ino, &ino);
+ if (!arg->err)
+ arg->err = append_de(arg->vdir, name, namelen, ino,
+ d_type, arg->delist + bend);
+ } else {
+ name += AUFS_WH_PFX_LEN;
+ namelen -= AUFS_WH_PFX_LEN;
+ for (bindex = 0; bindex < bend; bindex++)
+ if (au_nhash_test_known_wh(arg->whlist + bend, name,
+ namelen))
+ goto out; /* already whiteouted */
+
+ ino = 1; /* dummy */
+ if (unlikely(au_ftest_fillvdir(arg->flags, SHWH)))
+ arg->err = au_wh_ino(sb, bend, h_ino, &ino);
+ if (!arg->err)
+ arg->err = au_nhash_append_wh
+ (arg->whlist + bend, name, namelen, ino, d_type,
+ bend, au_ftest_fillvdir(arg->flags, SHWH));
+ }
+
+ out:
+ if (!arg->err)
+ arg->vdir->vd_jiffy = jiffies;
+ //smp_mb();
+ AuTraceErr(arg->err);
+ return arg->err;
+}
+
+static int au_handle_shwh(struct super_block *sb, struct au_vdir *vdir,
+ aufs_bindex_t bstart, aufs_bindex_t bend,
+ struct au_nhash *_whlist, struct au_nhash *_delist)
+{
+#ifdef CONFIG_AUFS_SHWH
+ int err, i;
+ struct hlist_head *head;
+ struct au_vdir_wh *tpos;
+ struct hlist_node *pos, *n;
+ char *p, *o;
+ struct au_nhash *whlist, *delist;
+ struct au_vdir_destr *destr;
+ aufs_bindex_t bindex;
+
+ AuTraceEnter();
+ AuDebugOn(!au_opt_test(au_mntflags(sb), SHWH));
+
+ err = -ENOMEM;
+ o = p = __getname();
+ if (unlikely(!p))
+ goto out;
+
+ err = 0;
+ memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN);
+ p += AUFS_WH_PFX_LEN;
+ for (bindex = bstart; !err && bindex <= bend; bindex++) {
+ whlist = _whlist + bindex;
+ delist = _delist + bindex;
+
+ for (i = 0; i < AuSize_NHASH; i++) {
+ head = whlist->heads + i;
+ hlist_for_each_entry_safe(tpos, pos, n, head, wh_hash) {
+ destr = &tpos->wh_str;
+ memcpy(p, destr->name, destr->len);
+ err = append_de(vdir, o,
+ destr->len + AUFS_WH_PFX_LEN,
+ tpos->wh_ino, tpos->wh_type,
+ delist);
+ if (unlikely(err))
+ break;
+ }
+ }
+ }
+
+ __putname(o);
+
+ out:
+ AuTraceErr(err);
+ return err;
+#else
+ return 0;
+#endif
+}
+
+static int au_do_read_vdir(struct fillvdir_arg *arg)
+{
+ int err, dlgt, shwh;
+ aufs_bindex_t bend, bindex, bstart;
+ struct super_block *sb;
+ unsigned int mnt_flags;
+ struct file *hf;
+ loff_t offset;
+
+ AuTraceEnter();
+
+ err = -ENOMEM;
+ bend = au_fbend(arg->file);
+ arg->delist = kmalloc(sizeof(*arg->delist) * (bend + 1), GFP_TEMPORARY);
+ if (unlikely(!arg->delist))
+ goto out;
+ arg->whlist = kmalloc(sizeof(*arg->whlist) * (bend + 1), GFP_TEMPORARY);
+ if (unlikely(!arg->whlist))
+ goto out_delist;
+ err = 0;
+ for (bindex = 0; bindex <= bend; bindex++) {
+ au_nhash_init(arg->delist + bindex);
+ au_nhash_init(arg->whlist + bindex);
+ }
+
+ sb = arg->file->f_dentry->d_sb;
+ mnt_flags = au_mntflags(sb);
+ dlgt = !!au_opt_test_dlgt(mnt_flags);
+ arg->flags = 0;
+ shwh = 0;
+ if (unlikely(au_opt_test(mnt_flags, SHWH))) {
+ shwh = 1;
+ au_fset_fillvdir(arg->flags, SHWH);
+ }
+ bstart = au_fbstart(arg->file);
+ for (bindex = bstart; !err && bindex <= bend; bindex++) {
+ hf = au_h_fptr(arg->file, bindex);
+ if (!hf)
+ continue;
+
+ offset = vfsub_llseek(hf, 0, SEEK_SET);
+ err = offset;
+ if (unlikely(offset))
+ break;
+ arg->bindex = bindex;
+ do {
+ arg->err = 0;
+ au_fclr_fillvdir(arg->flags, CALLED);
+ //smp_mb();
+ err = vfsub_readdir(hf, fillvdir, arg, dlgt);
+ if (err >= 0)
+ err = arg->err;
+ } while (!err && au_ftest_fillvdir(arg->flags, CALLED));
+ }
+
+ if (unlikely(!err && shwh))
+ err = au_handle_shwh(sb, arg->vdir, bstart, bend, arg->whlist,
+ arg->delist);
+
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ free_dehlist(arg->delist + bindex);
+ au_nhash_fin(arg->whlist + bindex);
+ }
+ kfree(arg->whlist);
+
+ out_delist:
+ kfree(arg->delist);
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+static int read_vdir(struct file *file, int may_read)
+{
+ int err, do_read;
+ struct dentry *dentry;
+ struct inode *inode;
+ struct au_vdir *vdir, *allocated;
+ unsigned long expire;
+ struct fillvdir_arg arg;
+ struct super_block *sb;
+
+ dentry = file->f_dentry;
+ LKTRTrace("%.*s, may %d\n", AuDLNPair(dentry), may_read);
+ FiMustWriteLock(file);
+ inode = dentry->d_inode;
+ IMustLock(inode);
+ IiMustWriteLock(inode);
+ AuDebugOn(!S_ISDIR(inode->i_mode));
+
+ err = 0;
+ allocated = NULL;
+ do_read = 0;
+ sb = inode->i_sb;
+ expire = au_sbi(sb)->si_rdcache;
+ vdir = au_ivdir(inode);
+#if 0 // debug
+ AuDbg("vdir %p, fvdir %p, i_ver %lu, vd_ver %lu, jiffy %lu,"
+ " vd_jiffy %lu, expire %lu\n",
+ vdir, au_fvdir_cache(file), inode->i_version,
+ vdir ? vdir->vd_version : 0LU, jiffies,
+ vdir ? vdir->vd_jiffy + expire : 0LU, expire);
+#endif
+ if (!vdir) {
+ AuDebugOn(au_fvdir_cache(file));
+ do_read = 1;
+ vdir = alloc_vdir();
+ err = PTR_ERR(vdir);
+ if (IS_ERR(vdir))
+ goto out;
+ err = 0;
+ allocated = vdir;
+ } else if (may_read
+ && (inode->i_version != vdir->vd_version
+ || time_after(jiffies, vdir->vd_jiffy + expire))) {
+ LKTRTrace("iver %Lu, vdver %lu, exp %lu\n",
+ inode->i_version, vdir->vd_version,
+ vdir->vd_jiffy + expire);
+ do_read = 1;
+ err = reinit_vdir(vdir);
+ if (unlikely(err))
+ goto out;
+ }
+ //AuDbgVdir(vdir); goto out;
+
+#if 0 // debug
+ AuDbg("do_read %d\n", do_read);
+#endif
+ if (!do_read)
+ return 0; /* success */
+
+ arg.file = file;
+ arg.vdir = vdir;
+ err = au_do_read_vdir(&arg);
+ if (!err) {
+ //file->f_pos = 0;
+ vdir->vd_version = inode->i_version;
+ vdir->vd_last.i = 0;
+ vdir->vd_last.p.deblk = vdir->vd_deblk[0];
+ if (allocated)
+ au_set_ivdir(inode, allocated);
+ //AuDbgVdir(vdir);
+ } else if (allocated)
+ au_vdir_free(allocated);
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+static int copy_vdir(struct au_vdir *tgt, struct au_vdir *src)
+{
+ int err, i, rerr, n;
+
+ AuTraceEnter();
+ AuDebugOn(tgt->vd_nblk != 1);
+ //AuDbgVdir(tgt);
+
+ err = -ENOMEM;
+ if (tgt->vd_nblk < src->vd_nblk) {
+ au_vdir_deblk_t **p;
+ p = au_kzrealloc(tgt->vd_deblk, sizeof(*p) * tgt->vd_nblk,
+ sizeof(*p) * src->vd_nblk, GFP_KERNEL);
+ if (unlikely(!p))
+ goto out;
+ tgt->vd_deblk = p;
+ }
+
+ tgt->vd_nblk = src->vd_nblk;
+ n = src->vd_nblk;
+ memcpy(tgt->vd_deblk[0], src->vd_deblk[0], AuSize_DEBLK);
+ //tgt->vd_last.i = 0;
+ //tgt->vd_last.p.deblk = tgt->vd_deblk[0];
+ tgt->vd_version = src->vd_version;
+ tgt->vd_jiffy = src->vd_jiffy;
+
+ for (i = 1; i < n; i++) {
+ tgt->vd_deblk[i] = kmalloc(AuSize_DEBLK, GFP_KERNEL);
+ if (tgt->vd_deblk[i])
+ memcpy(tgt->vd_deblk[i], src->vd_deblk[i],
+ AuSize_DEBLK);
+ else
+ goto out;
+ }
+ //smp_mb();
+ //AuDbgVdir(tgt);
+ return 0; /* success */
+
+ out:
+ rerr = reinit_vdir(tgt);
+ BUG_ON(rerr);
+ AuTraceErr(err);
+ return err;
+}
+
+int au_vdir_init(struct file *file)
+{
+ int err;
+ struct dentry *dentry;
+ struct inode *inode;
+ struct au_vdir *vdir_cache, *allocated;
+
+ dentry = file->f_dentry;
+ LKTRTrace("%.*s, pos %Ld\n", AuDLNPair(dentry), file->f_pos);
+ FiMustWriteLock(file);
+ inode = dentry->d_inode;
+ IiMustWriteLock(inode);
+ AuDebugOn(!S_ISDIR(inode->i_mode));
+
+ err = read_vdir(file, !file->f_pos);
+ if (unlikely(err))
+ goto out;
+ //AuDbgVdir(au_ivdir(inode)); goto out;
+
+ allocated = NULL;
+ vdir_cache = au_fvdir_cache(file);
+ if (!vdir_cache) {
+ vdir_cache = alloc_vdir();
+ err = PTR_ERR(vdir_cache);
+ if (IS_ERR(vdir_cache))
+ goto out;
+ allocated = vdir_cache;
+ } else if (!file->f_pos && vdir_cache->vd_version != file->f_version) {
+ err = reinit_vdir(vdir_cache);
+ if (unlikely(err))
+ goto out;
+ } else
+ return 0; /* success */
+ //err = 0; AuDbgVdir(vdir_cache); goto out;
+
+ err = copy_vdir(vdir_cache, au_ivdir(inode));
+ if (!err) {
+ file->f_version = inode->i_version;
+ if (allocated)
+ au_set_fvdir_cache(file, allocated);
+ } else if (allocated)
+ au_vdir_free(allocated);
+
+ out:
+ //au_debug_on();
+ AuTraceErr(err);
+ //au_debug_off();
+ return err;
+}
+
+static loff_t calc_offset(struct au_vdir *vdir)
+{
+ loff_t offset;
+ union au_vdir_deblk_p p;
+
+ p.deblk = vdir->vd_deblk[vdir->vd_last.i];
+ offset = vdir->vd_last.p.p - p.p;
+ offset += sizeof(*p.deblk) * vdir->vd_last.i;
+ return offset;
+}
+
+/* returns true or false */
+static int seek_vdir(struct file *file)
+{
+ int valid, i, n;
+ struct dentry *dentry;
+ struct au_vdir *vdir_cache;
+ loff_t offset;
+ union au_vdir_deblk_p p, deblk_end;
+
+ dentry = file->f_dentry;
+ LKTRTrace("%.*s, pos %Ld\n", AuDLNPair(dentry), file->f_pos);
+ vdir_cache = au_fvdir_cache(file);
+ AuDebugOn(!vdir_cache);
+ //AuDbgVdir(vdir_cache);
+
+ valid = 1;
+ offset = calc_offset(vdir_cache);
+ LKTRTrace("offset %Ld\n", offset);
+ if (file->f_pos == offset)
+ goto out;
+
+ vdir_cache->vd_last.i = 0;
+ vdir_cache->vd_last.p.deblk = vdir_cache->vd_deblk[0];
+ if (!file->f_pos)
+ goto out;
+
+ valid = 0;
+ i = file->f_pos / AuSize_DEBLK;
+ LKTRTrace("i %d\n", i);
+ if (i >= vdir_cache->vd_nblk)
+ goto out;
+
+ n = vdir_cache->vd_nblk;
+ for (; i < n; i++) {
+ p.deblk = vdir_cache->vd_deblk[i];
+ deblk_end.deblk = p.deblk + 1;
+ offset = i;
+ offset *= AuSize_DEBLK;
+ while (!is_deblk_end(&p, &deblk_end) && offset < file->f_pos) {
+ int l;
+ l = calc_size(p.de->de_str.len);
+ offset += l;
+ p.p += l;
+ }
+ if (!is_deblk_end(&p, &deblk_end)) {
+ valid = 1;
+ vdir_cache->vd_last.i = i;
+ vdir_cache->vd_last.p = p;
+ break;
+ }
+ }
+
+ out:
+ //smp_mb();
+ AuTraceErr(!valid);
+ return valid;
+}
+
+int au_vdir_fill_de(struct file *file, void *dirent, filldir_t filldir)
+{
+ int err, l;
+ struct dentry *dentry;
+ struct au_vdir *vdir_cache;
+ struct au_vdir_de *de;
+ union au_vdir_deblk_p deblk_end;
+
+ dentry = file->f_dentry;
+ LKTRTrace("%.*s, pos %Ld\n", AuDLNPair(dentry), file->f_pos);
+ vdir_cache = au_fvdir_cache(file);
+ AuDebugOn(!vdir_cache);
+ //AuDbgVdir(vdir_cache);
+
+ if (!seek_vdir(file))
+ return 0;
+
+ while (1) {
+ deblk_end.deblk
+ = vdir_cache->vd_deblk[vdir_cache->vd_last.i] + 1;
+ while (!is_deblk_end(&vdir_cache->vd_last.p, &deblk_end)) {
+ de = vdir_cache->vd_last.p.de;
+ LKTRTrace("%.*s, off%Ld, i%lu, dt%d\n",
+ de->de_str.len, de->de_str.name,
+ file->f_pos, de->de_ino, de->de_type);
+ err = filldir(dirent, de->de_str.name, de->de_str.len,
+ file->f_pos, de->de_ino, de->de_type);
+ if (unlikely(err)) {
+ AuTraceErr(err);
+ //return err;
+ //todo: ignore the error caused by udba.
+ return 0;
+ }
+
+ l = calc_size(de->de_str.len);
+ vdir_cache->vd_last.p.p += l;
+ file->f_pos += l;
+ }
+ if (vdir_cache->vd_last.i < vdir_cache->vd_nblk - 1) {
+ vdir_cache->vd_last.i++;
+ vdir_cache->vd_last.p.deblk
+ = vdir_cache->vd_deblk[vdir_cache->vd_last.i];
+ file->f_pos = sizeof(*vdir_cache->vd_last.p.deblk)
+ * vdir_cache->vd_last.i;
+ continue;
+ }
+ break;
+ }
+
+ //smp_mb();
+ return 0;
+}
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 44/67] aufs inode operations
2008-05-16 14:32 ` [PATCH 43/67] aufs virtual or vertical directory hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:32 ` [PATCH 45/67] aufs inode functions hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs inode operations
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/inode.h | 336 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 336 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/inode.h b/fs/aufs/inode.h
new file mode 100644
index 0000000..6b6945f
--- /dev/null
+++ b/fs/aufs/inode.h
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * inode operations
+ *
+ * $Id: inode.h,v 1.2 2008/04/21 01:32:05 sfjro Exp $
+ */
+
+#ifndef __AUFS_INODE_H__
+#define __AUFS_INODE_H__
+
+#ifdef __KERNEL__
+
+#include <linux/fs.h>
+#include <linux/namei.h>
+#include <linux/security.h>
+#include <linux/aufs_type.h>
+#include "hinode.h"
+#include "misc.h"
+#include "super.h"
+
+struct au_hinode;
+struct au_vdir;
+struct au_iinfo {
+ atomic_t ii_generation;
+ struct super_block *ii_hsb1; /* no get/put */
+
+ struct au_rwsem ii_rwsem;
+ aufs_bindex_t ii_bstart, ii_bend;
+ struct au_hinode *ii_hinode;
+ struct au_vdir *ii_vdir;
+};
+
+struct aufs_icntnr {
+ struct au_iinfo iinfo;
+ struct inode vfs_inode;
+};
+
+/* ---------------------------------------------------------------------- */
+
+/* inode.c */
+int au_refresh_hinode_self(struct inode *inode);
+int au_refresh_hinode(struct inode *inode, struct dentry *dentry);
+struct inode *au_new_inode(struct dentry *dentry);
+int au_test_ro(struct super_block *sb, aufs_bindex_t bindex,
+ struct inode *inode);
+int au_test_h_perm(struct inode *h_inode, int mask, int dlgt);
+int au_test_h_perm_sio(struct inode *h_inode, int mask, int dlgt);
+
+/* i_op.c */
+extern struct inode_operations aufs_iop, aufs_symlink_iop, aufs_dir_iop;
+
+/* au_wr_dir flags */
+#define AuWrDir_ADD_ENTRY 1
+#define AuWrDir_LOCK_SRCDIR (1 << 1)
+#define AuWrDir_ISDIR (1 << 2)
+#define au_ftest_wrdir(flags, name) ((flags) & AuWrDir_##name)
+#define au_fset_wrdir(flags, name) { (flags) |= AuWrDir_##name; }
+#define au_fclr_wrdir(flags, name) { (flags) &= ~AuWrDir_##name; }
+
+struct au_wr_dir_args {
+ aufs_bindex_t force_btgt;
+ unsigned int flags;
+};
+int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry,
+ struct au_wr_dir_args *args);
+
+/* i_op_add.c */
+struct au_ndx;
+int au_may_add(struct dentry *dentry, aufs_bindex_t bindex,
+ struct dentry *h_parent, int isdir, struct au_ndx *ndx);
+int aufs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev);
+int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname);
+int aufs_create(struct inode *dir, struct dentry *dentry, int mode,
+ struct nameidata *nd);
+int aufs_link(struct dentry *src_dentry, struct inode *dir,
+ struct dentry *dentry);
+int aufs_mkdir(struct inode *dir, struct dentry *dentry, int mode);
+
+/* i_op_del.c */
+int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup,
+ struct dentry *locked);
+int au_may_del(struct dentry *dentry, aufs_bindex_t bindex,
+ struct dentry *h_parent, int isdir, struct au_ndx *ndx);
+int aufs_unlink(struct inode *dir, struct dentry *dentry);
+int aufs_rmdir(struct inode *dir, struct dentry *dentry);
+
+/* i_op_ren.c */
+int au_wbr(struct dentry *dentry, aufs_bindex_t btgt);
+int aufs_rename(struct inode *src_dir, struct dentry *src_dentry,
+ struct inode *dir, struct dentry *dentry);
+
+#ifdef CONFIG_AUFS_DLGT
+/* dlgt.c */
+int au_security_inode_permission(struct inode *h_inode, int mask,
+ struct nameidata *fake_nd, int dlgt);
+#else
+static inline
+int au_security_inode_permission(struct inode *h_inode, int mask,
+ struct nameidata *fake_nd, int dlgt)
+{
+ return security_inode_permission(h_inode, mask, fake_nd);
+}
+#endif /* CONFIG_AUFS_DLGT */
+
+#ifdef CONFIG_AUFS_WORKAROUND_FUSE
+/* br_fuse.c */
+int aufs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *st);
+#endif
+
+#if 0 // xattr
+/* xattr.c */
+int aufs_setxattr(struct dentry *dentry, const char *name, const void *value,
+ size_t sz, int flags);
+ssize_t aufs_getxattr(struct dentry *dentry, const char *name, void *value,
+ size_t sz);
+ssize_t aufs_listxattr(struct dentry *dentry, char *list, size_t sz);
+int aufs_removexattr(struct dentry *dentry, const char *name);
+#endif
+
+/* iinfo.c */
+struct au_iinfo *au_ii(struct inode *inode);
+struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex);
+aufs_bindex_t au_ii_br_id(struct inode *inode, aufs_bindex_t bindex);
+
+void au_set_ibstart(struct inode *inode, aufs_bindex_t bindex);
+void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex,
+ struct dentry *h_wh);
+unsigned int au_hi_flags(struct inode *inode, int isdir);
+
+/* hinode flags */
+#define AuHi_XINO 1
+#define AuHi_NOTIFY (1 << 1)
+#define au_ftest_hi(flags, name) ((flags) & AuHi_##name)
+#define au_fset_hi(flags, name) { (flags) |= AuHi_##name; }
+#define au_fclr_hi(flags, name) { (flags) &= ~AuHi_##name; }
+#ifndef CONFIG_AUFS_HINOTIFY
+#undef AuHi_NOTIFY
+#define AuHi_NOTIFY 0
+#endif
+
+void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex,
+ struct inode *h_inode, unsigned int flags);
+
+void au_update_iigen(struct inode *inode);
+void au_update_brange(struct inode *inode, int do_put_zero);
+
+int au_iinfo_init(struct inode *inode);
+void au_iinfo_fin(struct inode *inode);
+
+/* plink.c */
+#ifdef CONFIG_AUFS_DEBUG
+void au_plink_list(struct super_block *sb);
+#else
+static inline void au_plink_list(struct super_block *sb)
+{
+ /* nothing */
+}
+#endif
+int au_plink_test(struct super_block *sb, struct inode *inode);
+struct dentry *au_plink_lkup(struct super_block *sb, aufs_bindex_t bindex,
+ struct inode *inode);
+void au_plink_append(struct super_block *sb, struct inode *inode,
+ struct dentry *h_dentry, aufs_bindex_t bindex);
+void au_plink_put(struct super_block *sb);
+void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id);
+
+/* ---------------------------------------------------------------------- */
+
+/* lock subclass for iinfo */
+enum {
+ AuLsc_II_CHILD, /* child first */
+ AuLsc_II_CHILD2, /* rename(2), link(2), and cpup at hinotify */
+ AuLsc_II_CHILD3, /* copyup dirs */
+ AuLsc_II_PARENT,
+ AuLsc_II_PARENT2,
+ AuLsc_II_PARENT3,
+ AuLsc_II_PARENT4,
+ AuLsc_II_NEW /* new inode */
+};
+
+/*
+ * ii_read_lock_child, ii_write_lock_child,
+ * ii_read_lock_child2, ii_write_lock_child2,
+ * ii_read_lock_child3, ii_write_lock_child3,
+ * ii_read_lock_parent, ii_write_lock_parent,
+ * ii_read_lock_parent2, ii_write_lock_parent2,
+ * ii_read_lock_parent3, ii_write_lock_parent3,
+ * ii_read_lock_parent4, ii_write_lock_parent4,
+ * ii_read_lock_new, ii_write_lock_new
+ */
+#define AuReadLockFunc(name, lsc) \
+static inline void ii_read_lock_##name(struct inode *i) \
+{ au_rw_read_lock_nested(&au_ii(i)->ii_rwsem, AuLsc_II_##lsc); }
+
+#define AuWriteLockFunc(name, lsc) \
+static inline void ii_write_lock_##name(struct inode *i) \
+{ au_rw_write_lock_nested(&au_ii(i)->ii_rwsem, AuLsc_II_##lsc); }
+
+#define AuRWLockFuncs(name, lsc) \
+ AuReadLockFunc(name, lsc) \
+ AuWriteLockFunc(name, lsc)
+
+AuRWLockFuncs(child, CHILD);
+AuRWLockFuncs(child2, CHILD2);
+AuRWLockFuncs(child3, CHILD3);
+AuRWLockFuncs(parent, PARENT);
+AuRWLockFuncs(parent2, PARENT2);
+AuRWLockFuncs(parent3, PARENT3);
+AuRWLockFuncs(parent4, PARENT4);
+AuRWLockFuncs(new, NEW);
+
+#undef AuReadLockFunc
+#undef AuWriteLockFunc
+#undef AuRWLockFuncs
+
+/*
+ * ii_read_unlock, ii_write_unlock, ii_downgrade_lock
+ */
+AuSimpleUnlockRwsemFuncs(ii, struct inode *i, au_ii(i)->ii_rwsem);
+
+/* to debug easier, do not make them inlined functions */
+#define IiMustReadLock(i) do { \
+ SiMustAnyLock((i)->i_sb); \
+ AuRwMustReadLock(&au_ii(i)->ii_rwsem); \
+} while (0)
+
+#define IiMustWriteLock(i) do { \
+ SiMustAnyLock((i)->i_sb); \
+ AuRwMustWriteLock(&au_ii(i)->ii_rwsem); \
+} while (0)
+
+#define IiMustAnyLock(i) do { \
+ SiMustAnyLock((i)->i_sb); \
+ AuRwMustAnyLock(&au_ii(i)->ii_rwsem); \
+} while (0)
+
+#define IiMustNoWaiters(i) AuRwMustNoWaiters(&au_ii(i)->ii_rwsem)
+
+/* ---------------------------------------------------------------------- */
+
+static inline aufs_bindex_t au_ibstart(struct inode *inode)
+{
+ IiMustAnyLock(inode);
+ return au_ii(inode)->ii_bstart;
+}
+
+static inline aufs_bindex_t au_ibend(struct inode *inode)
+{
+ IiMustAnyLock(inode);
+ return au_ii(inode)->ii_bend;
+}
+
+static inline struct au_vdir *au_ivdir(struct inode *inode)
+{
+ IiMustAnyLock(inode);
+ AuDebugOn(!S_ISDIR(inode->i_mode));
+ return au_ii(inode)->ii_vdir;
+}
+
+static inline struct dentry *au_hi_wh(struct inode *inode, aufs_bindex_t bindex)
+{
+ struct au_hinode *hinode;
+ IiMustAnyLock(inode);
+ hinode = au_ii(inode)->ii_hinode + bindex;
+ return hinode->hi_whdentry;
+}
+
+static inline void au_set_ibend(struct inode *inode, aufs_bindex_t bindex)
+{
+ IiMustWriteLock(inode);
+ AuDebugOn(au_sbend(inode->i_sb) < bindex || bindex < au_ibstart(inode));
+ au_ii(inode)->ii_bend = bindex;
+}
+
+static inline void au_set_ivdir(struct inode *inode, struct au_vdir *vdir)
+{
+ IiMustWriteLock(inode);
+ AuDebugOn(!S_ISDIR(inode->i_mode) || (au_ii(inode)->ii_vdir && vdir));
+ au_ii(inode)->ii_vdir = vdir;
+}
+
+static inline void au_hiput(struct au_hinode *hinode)
+{
+ au_hin_free(hinode);
+ dput(hinode->hi_whdentry);
+ iput(hinode->hi_inode);
+}
+
+static inline struct au_hinode *au_hi(struct inode *inode, aufs_bindex_t bindex)
+{
+ //todo: this lock check causes some unnecessary locks in callers.
+ IiMustAnyLock(inode);
+ return au_ii(inode)->ii_hinode + bindex;
+}
+
+/* tiny test for inode number */
+/* tmpfs generation is too rough */
+static inline int au_test_higen(struct inode *inode, struct inode *h_inode)
+{
+ IiMustAnyLock(inode);
+ return !(au_ii(inode)->ii_hsb1 == h_inode->i_sb
+ && inode->i_generation == h_inode->i_generation);
+}
+
+static inline au_gen_t au_iigen(struct inode *inode)
+{
+ return atomic_read(&au_ii(inode)->ii_generation);
+}
+
+#ifdef CONFIG_AUFS_HINOTIFY
+static inline au_gen_t au_iigen_dec(struct inode *inode)
+{
+ //AuDbg("i%lu\n", inode->i_ino);
+ return atomic_dec_return(&au_ii(inode)->ii_generation);
+}
+#endif
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_INODE_H__ */
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 45/67] aufs inode functions
2008-05-16 14:32 ` [PATCH 44/67] aufs inode operations hooanon05
@ 2008-05-16 14:32 ` hooanon05
2008-05-16 14:33 ` [PATCH 46/67] aufs inode private data hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:32 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs inode functions
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/inode.c | 425 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 425 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/inode.c b/fs/aufs/inode.c
new file mode 100644
index 0000000..7bab2cc
--- /dev/null
+++ b/fs/aufs/inode.c
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * inode functions
+ *
+ * $Id: inode.c,v 1.3 2008/04/28 03:04:12 sfjro Exp $
+ */
+
+#include "aufs.h"
+
+int au_refresh_hinode_self(struct inode *inode)
+{
+ int err, new_sz, update;
+ struct inode *first;
+ struct au_hinode *p, *q, tmp;
+ struct super_block *sb;
+ struct au_iinfo *iinfo;
+ aufs_bindex_t bindex, bend, new_bindex;
+
+ LKTRTrace("i%lu\n", inode->i_ino);
+ IiMustWriteLock(inode);
+
+ err = -ENOMEM;
+ update = 0;
+ sb = inode->i_sb;
+ bend = au_sbend(sb);
+ new_sz = sizeof(*iinfo->ii_hinode) * (bend + 1);
+ iinfo = au_ii(inode);
+ p = au_kzrealloc(iinfo->ii_hinode, sizeof(*p) * (iinfo->ii_bend + 1),
+ new_sz, GFP_KERNEL);
+ //p = NULL;
+ if (unlikely(!p))
+ goto out;
+
+ iinfo->ii_hinode = p;
+ p = iinfo->ii_hinode + iinfo->ii_bstart;
+ first = p->hi_inode;
+ err = 0;
+ for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend;
+ bindex++, p++) {
+ if (!p->hi_inode)
+ continue;
+
+ new_bindex = au_br_index(sb, p->hi_id);
+ if (new_bindex == bindex)
+ continue;
+ if (new_bindex < 0) {
+ update++;
+ au_hiput(p);
+ p->hi_inode = NULL;
+ continue;
+ }
+
+ if (new_bindex < iinfo->ii_bstart)
+ iinfo->ii_bstart = new_bindex;
+ if (iinfo->ii_bend < new_bindex)
+ iinfo->ii_bend = new_bindex;
+ /* swap two hidden inode, and loop again */
+ q = iinfo->ii_hinode + new_bindex;
+ tmp = *q;
+ *q = *p;
+ *p = tmp;
+ if (tmp.hi_inode) {
+ bindex--;
+ p--;
+ }
+ }
+ au_update_brange(inode, /*do_put_zero*/0);
+
+ if (unlikely(err))
+ goto out;
+
+ if (1 || first != au_h_iptr(inode, iinfo->ii_bstart))
+ au_cpup_attr_all(inode);
+ if (update && S_ISDIR(inode->i_mode))
+ inode->i_version++;
+ au_update_iigen(inode);
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+int au_refresh_hinode(struct inode *inode, struct dentry *dentry)
+{
+ int err, update, isdir;
+ struct inode *first;
+ struct au_hinode *p;
+ struct super_block *sb;
+ struct au_iinfo *iinfo;
+ aufs_bindex_t bindex, bend;
+ unsigned int flags;
+
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+ IiMustWriteLock(inode);
+
+ err = au_refresh_hinode_self(inode);
+ if (unlikely(err))
+ goto out;
+
+ sb = dentry->d_sb;
+ bend = au_sbend(sb);
+ iinfo = au_ii(inode);
+ update = 0;
+ p = iinfo->ii_hinode + iinfo->ii_bstart;
+ first = p->hi_inode;
+ isdir = S_ISDIR(inode->i_mode);
+ flags = au_hi_flags(inode, isdir);
+ bend = au_dbend(dentry);
+ for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) {
+ struct inode *hi;
+ struct dentry *hd;
+
+ hd = au_h_dptr(dentry, bindex);
+ if (!hd || !hd->d_inode)
+ continue;
+
+ if (iinfo->ii_bstart <= bindex && bindex <= iinfo->ii_bend) {
+ hi = au_h_iptr(inode, bindex);
+ if (hi) {
+ if (hi == hd->d_inode)
+ continue;
+ err = -ESTALE;
+ break;
+ }
+ }
+ if (bindex < iinfo->ii_bstart)
+ iinfo->ii_bstart = bindex;
+ if (iinfo->ii_bend < bindex)
+ iinfo->ii_bend = bindex;
+ au_set_h_iptr(inode, bindex, igrab(hd->d_inode), flags);
+ update++;
+ }
+ au_update_brange(inode, /*do_put_zero*/0);
+
+ if (unlikely(err))
+ goto out;
+
+ if (1 || first != au_h_iptr(inode, iinfo->ii_bstart))
+ au_cpup_attr_all(inode);
+ if (update && isdir)
+ inode->i_version++;
+ au_update_iigen(inode);
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+static int set_inode(struct inode *inode, struct dentry *dentry)
+{
+ int err, isdir;
+ struct dentry *h_dentry;
+ struct inode *h_inode;
+ umode_t mode;
+ aufs_bindex_t bindex, bstart, btail;
+ struct au_iinfo *iinfo;
+ unsigned int flags;
+
+ LKTRTrace("i%lu, %.*s\n", inode->i_ino, AuDLNPair(dentry));
+ AuDebugOn(!(inode->i_state & I_NEW));
+ IiMustWriteLock(inode);
+ bstart = au_dbstart(dentry);
+ h_dentry = au_h_dptr(dentry, bstart);
+ AuDebugOn(!h_dentry);
+ h_inode = h_dentry->d_inode;
+ AuDebugOn(!h_inode);
+
+ err = 0;
+ isdir = 0;
+ mode = h_inode->i_mode;
+ switch (mode & S_IFMT) {
+ case S_IFREG:
+ btail = au_dbtail(dentry);
+ break;
+ case S_IFDIR:
+ isdir = 1;
+ btail = au_dbtaildir(dentry);
+ inode->i_op = &aufs_dir_iop;
+ inode->i_fop = &aufs_dir_fop;
+ break;
+ case S_IFLNK:
+ btail = au_dbtail(dentry);
+ inode->i_op = &aufs_symlink_iop;
+ break;
+ case S_IFBLK:
+ case S_IFCHR:
+ case S_IFIFO:
+ case S_IFSOCK:
+ btail = au_dbtail(dentry);
+ init_special_inode(inode, mode,
+ au_h_rdev(h_inode, /*h_mnt*/NULL, h_dentry));
+ break;
+ default:
+ AuIOErr("Unknown file type 0%o\n", mode);
+ err = -EIO;
+ goto out;
+ }
+
+ flags = au_hi_flags(inode, isdir);
+ iinfo = au_ii(inode);
+ iinfo->ii_bstart = bstart;
+ iinfo->ii_bend = btail;
+ for (bindex = bstart; bindex <= btail; bindex++) {
+ h_dentry = au_h_dptr(dentry, bindex);
+ if (!h_dentry)
+ continue;
+ AuDebugOn(!h_dentry->d_inode);
+ au_set_h_iptr(inode, bindex, igrab(h_dentry->d_inode), flags);
+ }
+ au_cpup_attr_all(inode);
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+/* successful returns with iinfo write_locked */
+//todo: return with unlocked?
+static int reval_inode(struct inode *inode, struct dentry *dentry, int *matched)
+{
+ int err;
+ struct inode *h_inode, *h_dinode;
+ aufs_bindex_t bindex, bend;
+#if 0
+ const int hinotify = au_opt_test(au_mntflags(inode->i_sb),
+ UDBA_INOTIFY);
+#endif
+
+ LKTRTrace("i%lu, %.*s\n", inode->i_ino, AuDLNPair(dentry));
+
+ *matched = 0;
+
+ /*
+ * before this function, if aufs got any iinfo lock, it must be only
+ * one, the parent dir.
+ * it can happen by UDBA and the obsoleted inode number.
+ */
+ err = -EIO;
+ if (unlikely(inode->i_ino == parent_ino(dentry)))
+ goto out;
+
+ err = 0;
+ h_dinode = au_h_dptr(dentry, au_dbstart(dentry))->d_inode;
+ mutex_lock_nested(&inode->i_mutex, AuLsc_I_CHILD);
+ ii_write_lock_new(inode);
+ bend = au_ibend(inode);
+ for (bindex = au_ibstart(inode); bindex <= bend; bindex++) {
+ h_inode = au_h_iptr(inode, bindex);
+ if (h_inode && h_inode == h_dinode) {
+ //&& (ibs != bstart || !au_test_higen(inode, h_inode)));
+ *matched = 1;
+ err = 0;
+ if (unlikely(au_iigen(inode) != au_digen(dentry)))
+ err = au_refresh_hinode(inode, dentry);
+ break;
+ }
+ }
+ if (unlikely(err))
+ ii_write_unlock(inode);
+ mutex_unlock(&inode->i_mutex);
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+/* successful returns with iinfo write_locked */
+//todo: return with unlocked?
+struct inode *au_new_inode(struct dentry *dentry)
+{
+ struct inode *inode, *h_inode;
+ struct dentry *h_dentry;
+ ino_t h_ino;
+ struct super_block *sb;
+ int err, match;
+ aufs_bindex_t bstart;
+ struct au_xino_entry xinoe;
+
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+ sb = dentry->d_sb;
+ bstart = au_dbstart(dentry);
+ h_dentry = au_h_dptr(dentry, bstart);
+ AuDebugOn(!h_dentry);
+ h_inode = h_dentry->d_inode;
+ AuDebugOn(!h_inode);
+
+ h_ino = h_inode->i_ino;
+ err = au_xino_read(sb, bstart, h_ino, &xinoe);
+ //err = -1;
+ inode = ERR_PTR(err);
+ if (unlikely(err))
+ goto out;
+ new_ino:
+ if (!xinoe.ino) {
+ xinoe.ino = au_xino_new_ino(sb);
+ if (!xinoe.ino) {
+ inode = ERR_PTR(-EIO);
+ goto out;
+ }
+ }
+
+ LKTRTrace("i%lu\n", xinoe.ino);
+ inode = au_iget_locked(sb, xinoe.ino);
+ err = PTR_ERR(inode);
+ if (IS_ERR(inode))
+ goto out;
+
+ LKTRTrace("%lx, new %d\n", inode->i_state, !!(inode->i_state & I_NEW));
+ if (inode->i_state & I_NEW) {
+ ii_write_lock_new(inode);
+ err = set_inode(inode, dentry);
+ //err = -1;
+ unlock_new_inode(inode);
+ if (!err)
+ goto out; /* success */
+ iget_failed(inode);
+ ii_write_unlock(inode);
+ goto out_iput;
+ } else {
+ //todo: remove this
+ AuDebugOn(inode->i_state & I_LOCK);
+ err = reval_inode(inode, dentry, &match);
+ if (!err)
+ goto out; /* success */
+ else if (match)
+ goto out_iput;
+ }
+
+ if (unlikely(au_test_unique_ino(h_dentry, h_ino)))
+ AuWarn1("Un-notified UDBA or repeatedly renamed dir,"
+ " b%d, %s, %.*s, hi%lu, i%lu.\n",
+ bstart, au_sbtype(h_dentry->d_sb), AuDLNPair(dentry),
+ h_ino, xinoe.ino);
+#if 0 // debug
+ {
+ static unsigned char c;
+ if (!c++) {
+ struct dentry *d = d_find_alias(inode);
+ au_debug_on();
+ DbgDentry(dentry);
+ DbgInode(inode);
+ if (d) {
+ DbgDentry(d);
+ dput(d);
+ }
+ au_debug_off();
+ }
+ }
+#endif
+ xinoe.ino = 0;
+ err = au_xino_write0(sb, bstart, h_ino, 0);
+ if (!err) {
+ iput(inode);
+ goto new_ino;
+ }
+ /* force noxino? */
+
+ out_iput:
+ iput(inode);
+ inode = ERR_PTR(err);
+ out:
+ AuTraceErrPtr(inode);
+ return inode;
+}
+
+/* ---------------------------------------------------------------------- */
+
+int au_test_ro(struct super_block *sb, aufs_bindex_t bindex,
+ struct inode *inode)
+{
+ int err;
+
+ err = au_br_rdonly(au_sbr(sb, bindex));
+
+ /* pseudo-link after flushed may out of bounds */
+ if (!err
+ && inode
+ && au_ibstart(inode) <= bindex
+ && bindex <= au_ibend(inode)) {
+ /*
+ * permission check is unnecessary since vfsub routine
+ * will be called later
+ */
+ struct inode *hi = au_h_iptr(inode, bindex);
+ if (hi)
+ err = IS_IMMUTABLE(hi) ? -EROFS : 0;
+ }
+
+ AuTraceErr(err);
+ return err;
+}
+
+int au_test_h_perm(struct inode *h_inode, int mask, int dlgt)
+{
+ if (!current->fsuid)
+ return 0;
+ //todo: fake nameidata
+ return vfsub_permission(h_inode, mask, NULL, dlgt);
+}
+
+int au_test_h_perm_sio(struct inode *h_inode, int mask, int dlgt)
+{
+ if (unlikely(au_test_nfs(h_inode->i_sb)
+ && (mask & MAY_WRITE)
+ && S_ISDIR(h_inode->i_mode)))
+ mask |= MAY_READ; /* force permission check */
+ return au_test_h_perm(h_inode, mask, dlgt);
+}
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 46/67] aufs inode private data
2008-05-16 14:32 ` [PATCH 45/67] aufs inode functions hooanon05
@ 2008-05-16 14:33 ` hooanon05
2008-05-16 14:33 ` [PATCH 47/67] aufs inode operations (except add/del/rename) hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:33 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs inode private data
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/iinfo.c | 290 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 290 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/iinfo.c b/fs/aufs/iinfo.c
new file mode 100644
index 0000000..c8eb976
--- /dev/null
+++ b/fs/aufs/iinfo.c
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * inode private data
+ *
+ * $Id: iinfo.c,v 1.2 2008/04/21 01:32:05 sfjro Exp $
+ */
+
+#include "aufs.h"
+
+struct au_iinfo *au_ii(struct inode *inode)
+{
+ struct au_iinfo *iinfo;
+
+ iinfo = &(container_of(inode, struct aufs_icntnr, vfs_inode)->iinfo);
+ /* bad_inode case */
+ if (unlikely(!iinfo->ii_hinode))
+ return NULL;
+ AuDebugOn(!iinfo->ii_hinode
+ /* || au_sbi(inode->i_sb)->si_bend < iinfo->ii_bend */
+ || iinfo->ii_bend < iinfo->ii_bstart);
+ return iinfo;
+}
+
+struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex)
+{
+ struct inode *hidden_inode;
+
+ IiMustAnyLock(inode);
+ AuDebugOn(bindex < 0 || au_ibend(inode) < bindex);
+ hidden_inode = au_ii(inode)->ii_hinode[0 + bindex].hi_inode;
+ AuDebugOn(hidden_inode && atomic_read(&hidden_inode->i_count) <= 0);
+ return hidden_inode;
+}
+
+aufs_bindex_t au_ii_br_id(struct inode *inode, aufs_bindex_t bindex)
+{
+ IiMustAnyLock(inode);
+ AuDebugOn(bindex < 0
+ || au_ibend(inode) < bindex
+ || !au_ii(inode)->ii_hinode[0 + bindex].hi_inode);
+ return au_ii(inode)->ii_hinode[0 + bindex].hi_id;
+}
+
+// hard/soft set
+void au_set_ibstart(struct inode *inode, aufs_bindex_t bindex)
+{
+ struct au_iinfo *iinfo = au_ii(inode);
+ struct inode *h_inode;
+
+ IiMustWriteLock(inode);
+ AuDebugOn(au_sbend(inode->i_sb) < bindex);
+ iinfo->ii_bstart = bindex;
+ h_inode = iinfo->ii_hinode[bindex + 0].hi_inode;
+ if (h_inode)
+ au_cpup_igen(inode, h_inode);
+}
+
+unsigned int au_hi_flags(struct inode *inode, int isdir)
+{
+ unsigned int flags;
+ const unsigned int mnt_flags = au_mntflags(inode->i_sb);
+
+ flags = 0;
+ if (au_opt_test(mnt_flags, XINO))
+ au_fset_hi(flags, XINO);
+ if (unlikely(isdir && au_opt_test(mnt_flags, UDBA_INOTIFY)))
+ au_fset_hi(flags, NOTIFY);
+ return flags;
+}
+
+void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex,
+ struct inode *h_inode, unsigned int flags)
+{
+ struct au_hinode *hinode;
+ struct inode *hi;
+ struct au_iinfo *iinfo = au_ii(inode);
+
+ LKTRTrace("i%lu, b%d, hi%lu, flags 0x%x\n",
+ inode->i_ino, bindex, h_inode ? h_inode->i_ino : 0, flags);
+ IiMustWriteLock(inode);
+ hinode = iinfo->ii_hinode + bindex;
+ hi = hinode->hi_inode;
+ AuDebugOn(bindex < au_ibstart(inode) || au_ibend(inode) < bindex
+ || (h_inode && atomic_read(&h_inode->i_count) <= 0)
+ || (h_inode && hi));
+
+ if (hi) {
+#if 0 // remove this
+ if (!hi->i_nlink && au_ftest_hi(flags, XINO)) {
+ AuDebugOn(au_sbr_id(sb, bindex) != hinode->hi_id);
+ au_xino_write0(inode->i_sb, bindex, hi->i_ino, 0);
+ /* ignore this error */
+ /* bad action? */
+ }
+#endif
+ au_hiput(hinode);
+ }
+ hinode->hi_inode = h_inode;
+ if (h_inode) {
+ int err;
+ struct super_block *sb = inode->i_sb;
+
+ if (bindex == iinfo->ii_bstart)
+ au_cpup_igen(inode, h_inode);
+ hinode->hi_id = au_sbr_id(sb, bindex);
+ if (au_ftest_hi(flags, XINO)) {
+ struct au_xino_entry xinoe = {
+ .ino = inode->i_ino,
+ //.h_gen = h_inode->i_generation
+ };
+ err = au_xino_write(sb, bindex, h_inode->i_ino, &xinoe);
+ if (unlikely(err))
+ AuIOErr1("failed au_xino_write() %d\n", err);
+ }
+
+ if (unlikely(au_ftest_hi(flags, NOTIFY)
+ && au_br_hinotifyable(au_sbr_perm(sb, bindex)))) {
+ err = au_hin_alloc(hinode, inode, h_inode);
+ if (unlikely(err))
+ AuIOErr1("au_hin_alloc() %d\n", err);
+ }
+ }
+}
+
+void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex,
+ struct dentry *h_wh)
+{
+ struct au_hinode *hinode;
+
+ IiMustWriteLock(inode);
+ hinode = au_ii(inode)->ii_hinode + bindex;
+ AuDebugOn(hinode->hi_whdentry);
+ hinode->hi_whdentry = h_wh;
+}
+
+void au_update_iigen(struct inode *inode)
+{
+ //IiMustWriteLock(inode);
+ AuDebugOn(!inode->i_sb);
+ atomic_set(&au_ii(inode)->ii_generation, au_sigen(inode->i_sb));
+ //smp_mb(); /* atomic_set */
+}
+
+/* it may be called at remount time, too */
+void au_update_brange(struct inode *inode, int do_put_zero)
+{
+ struct au_iinfo *iinfo;
+
+ LKTRTrace("i%lu, %d\n", inode->i_ino, do_put_zero);
+ IiMustWriteLock(inode);
+
+ iinfo = au_ii(inode);
+ if (unlikely(!iinfo) || iinfo->ii_bstart < 0)
+ return;
+
+ if (do_put_zero) {
+ aufs_bindex_t bindex;
+ for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend;
+ bindex++) {
+ struct inode *h_i;
+ h_i = iinfo->ii_hinode[0 + bindex].hi_inode;
+ if (h_i && !h_i->i_nlink)
+ au_set_h_iptr(inode, bindex, NULL, 0);
+ }
+ }
+
+ iinfo->ii_bstart = -1;
+ while (++iinfo->ii_bstart <= iinfo->ii_bend)
+ if (iinfo->ii_hinode[0 + iinfo->ii_bstart].hi_inode)
+ break;
+ if (iinfo->ii_bstart > iinfo->ii_bend) {
+ iinfo->ii_bstart = -1;
+ iinfo->ii_bend = -1;
+ return;
+ }
+
+ iinfo->ii_bend++;
+ while (0 <= --iinfo->ii_bend)
+ if (iinfo->ii_hinode[0 + iinfo->ii_bend].hi_inode)
+ break;
+ AuDebugOn(iinfo->ii_bstart > iinfo->ii_bend || iinfo->ii_bend < 0);
+}
+
+/* ---------------------------------------------------------------------- */
+
+int au_iinfo_init(struct inode *inode)
+{
+ struct au_iinfo *iinfo;
+ struct super_block *sb;
+ int nbr, i;
+
+ sb = inode->i_sb;
+ AuDebugOn(!sb);
+ iinfo = &(container_of(inode, struct aufs_icntnr, vfs_inode)->iinfo);
+ AuDebugOn(iinfo->ii_hinode);
+ nbr = au_sbend(sb) + 1;
+ if (unlikely(nbr <= 0))
+ nbr = 1;
+ iinfo->ii_hinode = kcalloc(nbr, sizeof(*iinfo->ii_hinode), GFP_KERNEL);
+ //iinfo->ii_hinode = NULL;
+ if (iinfo->ii_hinode) {
+ for (i = 0; i < nbr; i++)
+ iinfo->ii_hinode[i].hi_id = -1;
+ atomic_set(&iinfo->ii_generation, au_sigen(sb));
+ //smp_mb(); /* atomic_set */
+ au_rw_init_nolock(&iinfo->ii_rwsem);
+ iinfo->ii_bstart = -1;
+ iinfo->ii_bend = -1;
+ iinfo->ii_vdir = NULL;
+ return 0;
+ }
+ return -ENOMEM;
+}
+
+static int au_iinfo_write0(struct super_block *sb, struct au_hinode *hinode,
+ ino_t ino)
+{
+ int err, locked;
+ aufs_bindex_t bindex;
+
+ err = 0;
+ locked = si_read_trylock(sb, !AuLock_FLUSH); // crucio!
+ bindex = au_br_index(sb, hinode->hi_id);
+ if (bindex >= 0)
+ err = au_xino_write0(sb, bindex, hinode->hi_inode->i_ino, ino);
+ /* error action? */
+ if (locked)
+ si_read_unlock(sb);
+ return err;
+}
+
+void au_iinfo_fin(struct inode *inode)
+{
+ struct au_iinfo *iinfo;
+ aufs_bindex_t bend;
+ struct au_hinode *hi;
+ struct super_block *sb;
+ int unlinked;
+ ino_t ino;
+
+ iinfo = au_ii(inode);
+ /* bad_inode case */
+ if (unlikely(!iinfo))
+ return;
+
+ if (unlikely(iinfo->ii_vdir))
+ au_vdir_free(iinfo->ii_vdir);
+
+ if (iinfo->ii_bstart >= 0) {
+ sb = inode->i_sb;
+ unlinked = !inode->i_nlink;
+ ino = 0;
+ if (unlinked)
+ ino = inode->i_ino;
+ hi = iinfo->ii_hinode + iinfo->ii_bstart;
+ bend = iinfo->ii_bend;
+ while (iinfo->ii_bstart++ <= bend) {
+ if (hi->hi_inode) {
+ if (unlinked || !hi->hi_inode->i_nlink) {
+ au_iinfo_write0(sb, hi, ino);
+ /* ignore this error */
+ ino = 0;
+ }
+ au_hiput(hi);
+ }
+ hi++;
+ }
+ //iinfo->ii_bstart = iinfo->ii_bend = -1;
+ }
+
+ kfree(iinfo->ii_hinode);
+ //iinfo->ii_hinode = NULL;
+}
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 47/67] aufs inode operations (except add/del/rename)
2008-05-16 14:33 ` [PATCH 46/67] aufs inode private data hooanon05
@ 2008-05-16 14:33 ` hooanon05
2008-05-16 14:33 ` [PATCH 48/67] aufs inode operations (add entry) hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:33 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs inode operations (except add/del/rename)
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/i_op.c | 844 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 844 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/i_op.c b/fs/aufs/i_op.c
new file mode 100644
index 0000000..2efa931
--- /dev/null
+++ b/fs/aufs/i_op.c
@@ -0,0 +1,844 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * inode operations (except add/del/rename)
+ *
+ * $Id: i_op.c,v 1.4 2008/05/04 23:54:53 sfjro Exp $
+ */
+
+//#include <linux/fs.h>
+#include <linux/fs_stack.h>
+//#include <linux/namei.h>
+//#include <linux/mm.h>
+#include <linux/uaccess.h>
+#include "aufs.h"
+
+static int h_permission(struct inode *h_inode, int mask,
+ struct nameidata *fake_nd, int brperm, int dlgt)
+{
+ int err, submask;
+ const int write_mask = (mask & (MAY_WRITE | MAY_APPEND));
+
+ LKTRTrace("ino %lu, mask 0x%x, brperm 0x%x\n",
+ h_inode->i_ino, mask, brperm);
+
+ err = -EACCES;
+ if (unlikely((write_mask && IS_IMMUTABLE(h_inode))
+ || ((mask & MAY_EXEC) && S_ISREG(h_inode->i_mode)
+ && fake_nd && fake_nd->path.mnt
+ && (fake_nd->path.mnt->mnt_flags & MNT_NOEXEC))
+ ))
+ goto out;
+
+ /* skip hidden fs test in the case of write to ro branch */
+ submask = mask & ~MAY_APPEND;
+ if (unlikely((write_mask && !au_br_writable(brperm))
+ || !h_inode->i_op
+ || !h_inode->i_op->permission)) {
+ //LKTRLabel(generic_permission);
+ err = generic_permission(h_inode, submask, NULL);
+ } else {
+ //LKTRLabel(h_inode->permission);
+ err = h_inode->i_op->permission(h_inode, submask, fake_nd);
+ AuTraceErr(err);
+ }
+
+#if 1
+ if (!err)
+ err = au_security_inode_permission(h_inode, mask, fake_nd,
+ dlgt);
+#endif
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+#ifdef CONFIG_AUFS_FAKE_DM
+
+static int silly_lock(struct inode *inode, struct nameidata *nd)
+{
+ LKTRTrace("i%lu, nd %p\n", inode->i_ino, nd);
+
+ si_read_lock(inode->i_sb, AuLock_FLUSH);
+ ii_read_lock_child(inode);
+ return 0;
+}
+
+static void silly_unlock(int locked, struct inode *inode, struct nameidata *nd)
+{
+ LKTRTrace("locked %d, i%lu, nd %p\n", locked, inode->i_ino, nd);
+
+ ii_read_unlock(inode);
+ si_read_unlock(inode->i_sb);
+}
+
+#else
+
+static int silly_lock(struct inode *inode, struct nameidata *nd)
+{
+ int locked = 0;
+ struct super_block *sb = inode->i_sb;
+
+ LKTRTrace("i%lu, nd %p\n", inode->i_ino, nd);
+
+ if (!nd || !nd->path.dentry) {
+ si_read_lock(sb, AuLock_FLUSH);
+ ii_read_lock_child(inode);
+ } else if (nd->path.dentry->d_inode != inode) {
+ locked = 1;
+ /* lock child first, then parent */
+ si_read_lock(sb, AuLock_FLUSH);
+ ii_read_lock_child(inode);
+ di_read_lock_parent(nd->path.dentry, 0);
+ } else {
+ locked = 2;
+ aufs_read_lock(nd->path.dentry, AuLock_FLUSH | AuLock_IR);
+ }
+ return locked;
+}
+
+static void silly_unlock(int locked, struct inode *inode, struct nameidata *nd)
+{
+ struct super_block *sb = inode->i_sb;
+
+ LKTRTrace("locked %d, i%lu, nd %p\n", locked, inode->i_ino, nd);
+
+ switch (locked) {
+ case 0:
+ ii_read_unlock(inode);
+ si_read_unlock(sb);
+ break;
+ case 1:
+ di_read_unlock(nd->path.dentry, 0);
+ ii_read_unlock(inode);
+ si_read_unlock(sb);
+ break;
+ case 2:
+ aufs_read_unlock(nd->path.dentry, AuLock_FLUSH | AuLock_IR);
+ break;
+ default:
+ BUG();
+ }
+}
+
+#endif /* CONFIG_AUFS_FAKE_DM */
+
+static int aufs_permission(struct inode *inode, int mask, struct nameidata *nd)
+{
+ int err, locked, dlgt;
+ aufs_bindex_t bindex, bend;
+ struct inode *h_inode;
+ struct super_block *sb;
+ unsigned int mnt_flags;
+ struct nameidata fake_nd, *p;
+ const int write_mask = (mask & (MAY_WRITE | MAY_APPEND));
+ const int nondir = !S_ISDIR(inode->i_mode);
+
+ LKTRTrace("ino %lu, mask 0x%x, nondir %d, write_mask %d, "
+ "nd %d{%d, %d}\n",
+ inode->i_ino, mask, nondir, write_mask,
+ !!nd, nd ? !!nd->path.dentry : 0, nd ? !!nd->path.mnt : 0);
+
+ sb = inode->i_sb;
+ locked = silly_lock(inode, nd);
+ mnt_flags = au_mntflags(sb);
+ dlgt = !!au_opt_test_dlgt(mnt_flags);
+
+ if (nd)
+ fake_nd = *nd;
+ if (/* unlikely */(nondir || write_mask
+ || au_opt_test_dirperm1(mnt_flags))) {
+ h_inode = au_h_iptr(inode, au_ibstart(inode));
+ AuDebugOn(!h_inode
+ || ((h_inode->i_mode & S_IFMT)
+ != (inode->i_mode & S_IFMT)));
+ err = 0;
+ bindex = au_ibstart(inode);
+ p = au_fake_dm(&fake_nd, nd, sb, bindex);
+ /* actual test will be delegated to LSM */
+ if (IS_ERR(p))
+ AuDebugOn(PTR_ERR(p) != -ENOENT);
+ else {
+ LKTRTrace("b%d\n", bindex);
+ err = h_permission(h_inode, mask, p,
+ au_sbr_perm(sb, bindex), dlgt);
+ au_fake_dm_release(p);
+ }
+ if (write_mask && !err) {
+ /* test whether the upper writable branch exists */
+ err = -EROFS;
+ for (; bindex >= 0; bindex--)
+ if (!au_br_rdonly(au_sbr(sb, bindex))) {
+ err = 0;
+ break;
+ }
+ }
+ goto out;
+ }
+
+ /* non-write to dir */
+ err = 0;
+ bend = au_ibend(inode);
+ for (bindex = au_ibstart(inode); !err && bindex <= bend; bindex++) {
+ h_inode = au_h_iptr(inode, bindex);
+ if (!h_inode)
+ continue;
+ AuDebugOn(!S_ISDIR(h_inode->i_mode));
+
+ p = au_fake_dm(&fake_nd, nd, sb, bindex);
+ /* actual test will be delegated to LSM */
+ if (IS_ERR(p))
+ AuDebugOn(PTR_ERR(p) != -ENOENT);
+ else {
+ LKTRTrace("b%d\n", bindex);
+ err = h_permission(h_inode, mask, p,
+ au_sbr_perm(sb, bindex), dlgt);
+ au_fake_dm_release(p);
+ }
+ }
+
+ out:
+ silly_unlock(locked, inode, nd);
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry,
+ struct nameidata *nd)
+{
+ struct dentry *ret, *parent;
+ int err, npositive;
+ struct inode *inode, *h_inode;
+ struct nameidata tmp_nd, *ndp;
+
+ LKTRTrace("dir %lu, %.*s, nd{0x%x}\n",
+ dir->i_ino, AuDLNPair(dentry), nd ? nd->flags : 0);
+ AuDebugOn(IS_ROOT(dentry));
+ IMustLock(dir);
+
+ /* nd can be NULL */
+ parent = dentry->d_parent; /* dir inode is locked */
+ aufs_read_lock(parent, AuLock_FLUSH);
+ err = au_alloc_dinfo(dentry);
+ //if (LktrCond) err = -1;
+ ret = ERR_PTR(err);
+ if (unlikely(err))
+ goto out;
+
+ ndp = au_dup_nd(au_sbi(dir->i_sb), &tmp_nd, nd);
+ npositive = au_lkup_dentry(dentry, au_dbstart(parent), /*type*/0, ndp);
+ err = npositive;
+ //err = -1;
+ ret = ERR_PTR(err);
+ if (unlikely(err < 0))
+ goto out_unlock;
+ inode = NULL;
+ if (npositive) {
+ /*
+ * stop 'race'-ing between hardlinks under different parents.
+ */
+ h_inode = au_h_dptr(dentry, au_dbstart(dentry))->d_inode;
+ AuDebugOn(!h_inode);
+ if (h_inode->i_nlink == 1 || S_ISDIR(h_inode->i_mode))
+ inode = au_new_inode(dentry);
+ else {
+ static DEFINE_MUTEX(mtx);
+ mutex_lock(&mtx);
+ inode = au_new_inode(dentry);
+ mutex_unlock(&mtx);
+ }
+ ret = (void *)inode;
+ }
+ if (!IS_ERR(inode)) {
+#if 1
+ /* d_splice_alias() also supports d_add() */
+ ret = d_splice_alias(inode, dentry);
+ if (unlikely(IS_ERR(ret) && inode))
+ ii_write_unlock(inode);
+#else
+ d_add(dentry, inode);
+#endif
+ //AuDbgDentry(dentry);
+ AuDebugOn(nd
+ && (nd->flags & LOOKUP_OPEN)
+ && nd->intent.open.file
+ && nd->intent.open.file->f_dentry);
+ au_store_fmode_exec(nd, inode);
+ }
+
+ out_unlock:
+ di_write_unlock(dentry);
+ out:
+ aufs_read_unlock(parent, !AuLock_IR);
+ AuTraceErrPtr(ret);
+ return ret;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * decide the branch and the parent dir where we will create a new entry.
+ * returns new bindex or an error.
+ * copyup the parent dir if needed.
+ */
+int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry,
+ struct au_wr_dir_args *args)
+{
+ int err;
+ aufs_bindex_t bcpup, bstart, src_bstart;
+ struct super_block *sb;
+ struct dentry *parent, *src_parent;
+ struct au_sbinfo *sbinfo;
+ const int add_entry = au_ftest_wrdir(args->flags, ADD_ENTRY);
+ const int lock_srcdir = au_ftest_wrdir(args->flags, LOCK_SRCDIR);
+
+ LKTRTrace("%.*s, src %p, {%d, 0x%x}\n",
+ AuDLNPair(dentry), src_dentry, args->force_btgt, args->flags);
+ //AuDbgDentry(dentry);
+
+ src_parent = NULL;
+ sb = dentry->d_sb;
+ sbinfo = au_sbi(sb);
+ parent = dget_parent(dentry);
+ bstart = au_dbstart(dentry);
+ bcpup = bstart;
+ if (args->force_btgt < 0) {
+ if (src_dentry) {
+ src_bstart = au_dbstart(src_dentry);
+ if (src_bstart < bstart)
+ bcpup = src_bstart;
+ } else if (add_entry) {
+ err = AuWbrCreate(sbinfo, dentry,
+ au_ftest_wrdir(args->flags, ISDIR));
+ bcpup = err;
+ }
+
+ if (bcpup < 0 || au_test_ro(sb, bcpup, dentry->d_inode)) {
+ if (add_entry)
+ err = AuWbrCopyup(sbinfo, dentry);
+ else {
+ di_read_lock_parent(parent, !AuLock_IR);
+ err = AuWbrCopyup(sbinfo, dentry);
+ di_read_unlock(parent, !AuLock_IR);
+ }
+ //err = -1;
+ bcpup = err;
+ if (unlikely(err < 0))
+ goto out;
+ }
+ } else {
+ bcpup = args->force_btgt;
+ AuDebugOn(au_test_ro(sb, bcpup, dentry->d_inode));
+ }
+ LKTRTrace("bstart %d, bcpup %d\n", bstart, bcpup);
+ if (bstart < bcpup)
+ au_update_dbrange(dentry, /*do_put_zero*/1);
+
+ err = bcpup;
+ if (bcpup == bstart)
+ goto out; /* success */
+
+ /* copyup the new parent into the branch we process */
+ if (src_dentry) {
+ src_parent = dget_parent(src_dentry);
+ if (lock_srcdir)
+ di_write_lock_parent2(src_parent);
+ }
+
+ if (add_entry) {
+ au_update_dbstart(dentry);
+ IMustLock(parent->d_inode);
+ DiMustWriteLock(parent);
+ IiMustWriteLock(parent->d_inode);
+ } else
+ di_write_lock_parent(parent);
+
+ err = 0;
+ if (!au_h_dptr(parent, bcpup)) {
+ if (bstart < bcpup)
+ err = au_cpdown_dirs(dentry, bcpup, src_parent);
+ else
+ err = au_cpup_dirs(dentry, bcpup, src_parent);
+ }
+ //err = -1;
+ if (!err && add_entry) {
+ struct dentry *h_parent;
+ struct inode *h_dir;
+
+ h_parent = au_h_dptr(parent, bcpup);
+ AuDebugOn(!h_parent);
+ h_dir = h_parent->d_inode;
+ AuDebugOn(!h_dir);
+ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT);
+ err = au_lkup_neg(dentry, bcpup);
+ //err = -1;
+ mutex_unlock(&h_dir->i_mutex);
+ if (bstart < bcpup && au_dbstart(dentry) < 0) {
+ au_set_dbstart(dentry, 0);
+ au_update_dbrange(dentry, /*do_put_zero*/0);
+ }
+ }
+
+ if (!add_entry)
+ di_write_unlock(parent);
+ if (lock_srcdir)
+ di_write_unlock(src_parent);
+ dput(src_parent);
+ if (!err)
+ err = bcpup; /* success */
+ //err = -EPERM;
+ out:
+ dput(parent);
+ LKTRTrace("err %d\n", err);
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void au_hi_lock(struct inode *h_inode, int isdir, struct inode *inode,
+ aufs_bindex_t bindex)
+{
+ if (!isdir)
+ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);
+ else
+ au_hdir2_lock(h_inode, inode, bindex);
+}
+
+static void au_hi_unlock(struct inode *h_inode, int isdir, struct inode *inode,
+ aufs_bindex_t bindex)
+{
+ if (!isdir)
+ mutex_unlock(&h_inode->i_mutex);
+ else
+ au_hdir_unlock(h_inode, inode, bindex);
+}
+
+struct au_icpup_args {
+ aufs_bindex_t btgt;
+ unsigned char isdir, did_cpup; /* flags */
+ unsigned char hinotify;
+ struct dentry *parent, *gparent, *h_dentry;
+ struct inode *dir, *gdir, *h_inode, *h_dir;
+};
+
+static int au_lock_and_icpup(struct dentry *dentry, loff_t sz,
+ struct au_icpup_args *rargs)
+{
+ int err;
+ aufs_bindex_t bstart;
+ struct super_block *sb;
+ struct dentry *hi_wh;
+ struct inode *inode;
+ struct au_wr_dir_args wr_dir_args = {
+ .force_btgt = -1,
+ .flags = 0
+ };
+
+ LKTRTrace("%.*s, %lld\n", AuDLNPair(dentry), sz);
+
+ di_write_lock_child(dentry);
+ bstart = au_dbstart(dentry);
+ sb = dentry->d_sb;
+ inode = dentry->d_inode;
+ rargs->isdir = S_ISDIR(inode->i_mode);
+ if (rargs->isdir)
+ au_fset_wrdir(wr_dir_args.flags, ISDIR);
+ err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args);
+ //err = -1;
+ if (unlikely(err < 0))
+ goto out_dentry;
+ rargs->btgt = err;
+ rargs->did_cpup = (err != bstart);
+ err = 0;
+
+ /* crazy udba locks */
+ rargs->hinotify = !!au_opt_test(au_mntflags(sb), UDBA_INOTIFY);
+ if (unlikely(!IS_ROOT(dentry))) {
+ rargs->parent = dget_parent(dentry);
+ rargs->dir = rargs->parent->d_inode;
+ di_read_lock_parent(rargs->parent, AuLock_IR);
+ }
+ rargs->h_dentry = au_h_dptr(dentry, au_dbstart(dentry));
+ rargs->h_inode = rargs->h_dentry->d_inode;
+ AuDebugOn(!rargs->h_inode);
+
+ if (!rargs->did_cpup) {
+ au_hi_lock(rargs->h_inode, rargs->isdir, inode, rargs->btgt);
+ //todo: revalidate the lower dentry?
+ goto out; /* success */
+ }
+
+ if (unlikely(rargs->hinotify
+ && rargs->parent
+ && !IS_ROOT(rargs->parent))) {
+ rargs->gparent = dget_parent(rargs->parent);
+ rargs->gdir = rargs->gparent->d_inode;
+ ii_read_lock_parent2(rargs->gdir);
+ }
+
+ hi_wh = NULL;
+ rargs->h_dir = au_h_iptr(rargs->dir, rargs->btgt);
+ au_hdir_lock(rargs->h_dir, rargs->dir, rargs->btgt);
+ //todo: revalidate the lower dentry?
+ au_hi_lock(rargs->h_inode, rargs->isdir, inode, bstart);
+ if (!d_unhashed(dentry)) {
+ err = au_sio_cpup_simple(dentry, rargs->btgt, sz, AuCpup_DTIME);
+ if (!err)
+ rargs->h_dentry = au_h_dptr(dentry, au_dbstart(dentry));
+ } else {
+ hi_wh = au_hi_wh(inode, rargs->btgt);
+ if (!hi_wh) {
+ err = au_sio_cpup_wh(dentry, rargs->btgt, sz,
+ /*file*/NULL);
+ if (!err)
+ hi_wh = au_hi_wh(inode, rargs->btgt);
+#if 0
+ revalidate hi_wh;
+#endif
+ }
+ if (!hi_wh)
+ rargs->h_dentry = au_h_dptr(dentry, au_dbstart(dentry));
+ else
+ rargs->h_dentry = hi_wh; /* do not dget here */
+ }
+
+ //err = -1;
+ au_hi_unlock(rargs->h_inode, rargs->isdir, inode, bstart);
+ rargs->h_inode = rargs->h_dentry->d_inode;
+ AuDebugOn(!rargs->h_inode);
+ if (!err)
+ au_hi_lock(rargs->h_inode, rargs->isdir, inode, rargs->btgt);
+ au_hdir_unlock(rargs->h_dir, rargs->dir, rargs->btgt);
+ if (!err)
+ goto out; /* success */
+
+ au_hi_unlock(rargs->h_inode, rargs->isdir, inode, rargs->btgt);
+ if (unlikely(rargs->gdir)) {
+ ii_read_unlock(rargs->gdir);
+ dput(rargs->gparent);
+ }
+ if (unlikely(rargs->dir)) {
+ di_read_unlock(rargs->parent, AuLock_IR);
+ dput(rargs->parent);
+ }
+
+ out_dentry:
+ di_write_unlock(dentry);
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+#if 0 //LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
+static long aufs_fallocate(struct inode *inode, int mode, loff_t offset,
+ loff_t len)
+{
+ long err;
+ struct super_block *sb;
+ struct dentry *dentry;
+ struct au_icpup_args rargs;
+
+ LKTRTrace("i%lu, 0x%x, %lld, %lld\n", inode->i_ino, mode, offset, len);
+
+ sb = inode->i_sb;
+ si_read_lock(sb, AuLock_FLUSH);
+#if 0
+ /* currently there are few filesystems to support fallocate(2) */
+ err = -EOPNOTSUPP;
+ goto out;
+#endif
+
+ /*
+ * if it is not the target dentry, it does no harm since fallocate(2)
+ * works on the inode.
+ */
+ dentry = d_find_alias(inode);
+ if (unlikely(!dentry)) {
+ AuIOErr("no dentry for i%lu\n", inode->i_ino);
+ err = -EIO;
+ goto out;
+ }
+
+ memset(&rargs, 0, sizeof(rargs));
+ err = au_lock_and_icpup(dentry, -1, &rargs);
+ if (unlikely(err < 0))
+ goto out;
+
+ err = -EOPNOTSUPP;
+ if (rargs.h_inode->i_op && rargs.h_inode->i_op->fallocate)
+ err = rargs.h_inode->i_op->fallocate(rargs.h_inode, mode,
+ offset, len);
+
+ au_hi_unlock(rargs.h_inode, rargs.isdir, inode, rargs.btgt);
+ if (unlikely(rargs.gdir)) {
+ ii_read_unlock(rargs.gdir);
+ dput(rargs.gparent);
+ }
+ if (unlikely(rargs.dir)) {
+ di_read_unlock(rargs.parent, AuLock_IR);
+ dput(rargs.parent);
+ }
+ di_write_unlock(dentry);
+
+ out:
+ si_read_unlock(sb);
+ AuTraceErr(err);
+ return err;
+}
+#endif
+
+static int aufs_setattr(struct dentry *dentry, struct iattr *ia)
+{
+ int err;
+ struct inode *inode;
+ struct au_hin_ignore ign;
+ struct vfsub_args vargs;
+ struct super_block *sb;
+ __u32 events;
+ struct file *file;
+ loff_t sz;
+ struct au_icpup_args rargs;
+
+ LKTRTrace("%.*s, ia_valid 0x%x\n", AuDLNPair(dentry), ia->ia_valid);
+ inode = dentry->d_inode;
+ IMustLock(inode);
+
+ sb = dentry->d_sb;
+ si_read_lock(sb, AuLock_FLUSH);
+
+ file = NULL;
+ if (ia->ia_valid & ATTR_FILE) {
+ /* currently ftruncate(2) only */
+ file = ia->ia_file;
+ fi_write_lock(file);
+ ia->ia_file = au_h_fptr(file, au_fbstart(file));
+ }
+
+ sz = -1;
+ if ((ia->ia_valid & ATTR_SIZE)
+ && ia->ia_size < i_size_read(inode))
+ sz = ia->ia_size;
+ memset(&rargs, 0, sizeof(rargs));
+ err = au_lock_and_icpup(dentry, sz, &rargs);
+ if (unlikely(err < 0))
+ goto out;
+
+ if ((ia->ia_valid & ATTR_SIZE)
+ && ia->ia_size < i_size_read(inode)) {
+ err = vmtruncate(inode, ia->ia_size);
+ if (unlikely(err))
+ goto out_unlock;
+ }
+
+ if (ia->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID))
+ ia->ia_valid &= ~ATTR_MODE;
+
+ events = 0;
+ vfsub_args_init(&vargs, &ign, au_opt_test_dlgt(au_mntflags(sb)), 0);
+ if (unlikely(rargs.hinotify && rargs.dir)) {
+ events = vfsub_events_notify_change(ia);
+ if (events)
+ vfsub_ign_hinode(&vargs, events,
+ au_hi(rargs.dir, rargs.btgt));
+ }
+ err = vfsub_notify_change(rargs.h_dentry, ia, &vargs);
+ //err = -1;
+ if (!err)
+ au_cpup_attr_changeable(inode);
+
+ out_unlock:
+ au_hi_unlock(rargs.h_inode, rargs.isdir, inode, rargs.btgt);
+ if (unlikely(rargs.gdir)) {
+ ii_read_unlock(rargs.gdir);
+ dput(rargs.gparent);
+ }
+ if (unlikely(rargs.dir)) {
+ di_read_unlock(rargs.parent, AuLock_IR);
+ dput(rargs.parent);
+ }
+ di_write_unlock(dentry);
+ out:
+ if (file) {
+ ia->ia_file = file;
+ //ia->ia_valid |= ATTR_FILE;
+ fi_write_unlock(file);
+ }
+ si_read_unlock(sb);
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int h_readlink(struct dentry *dentry, int bindex, char __user *buf,
+ int bufsiz)
+{
+ struct super_block *sb;
+ struct dentry *h_dentry;
+
+ LKTRTrace("%.*s, b%d, %d\n", AuDLNPair(dentry), bindex, bufsiz);
+
+ h_dentry = au_h_dptr(dentry, bindex);
+ if (unlikely(!h_dentry->d_inode->i_op
+ || !h_dentry->d_inode->i_op->readlink))
+ return -EINVAL;
+
+ sb = dentry->d_sb;
+ if (!au_test_ro(sb, bindex, dentry->d_inode)) {
+ touch_atime(au_sbr_mnt(sb, bindex), h_dentry);
+ au_update_fuse_h_inode(NULL, h_dentry); /*ignore*/
+ fsstack_copy_attr_atime(dentry->d_inode, h_dentry->d_inode);
+ }
+ return h_dentry->d_inode->i_op->readlink(h_dentry, buf, bufsiz);
+}
+
+static int aufs_readlink(struct dentry *dentry, char __user *buf, int bufsiz)
+{
+ int err;
+
+ LKTRTrace("%.*s, %d\n", AuDLNPair(dentry), bufsiz);
+
+ aufs_read_lock(dentry, AuLock_IR);
+ err = h_readlink(dentry, au_dbstart(dentry), buf, bufsiz);
+ //err = -1;
+ aufs_read_unlock(dentry, AuLock_IR);
+ AuTraceErr(err);
+ return err;
+}
+
+static void *aufs_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+ int err;
+ char *buf;
+ mm_segment_t old_fs;
+
+ LKTRTrace("%.*s, nd %.*s\n",
+ AuDLNPair(dentry), AuDLNPair(nd->path.dentry));
+
+ err = -ENOMEM;
+ buf = __getname();
+ //buf = NULL;
+ if (unlikely(!buf))
+ goto out;
+
+ aufs_read_lock(dentry, AuLock_IR);
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ err = h_readlink(dentry, au_dbstart(dentry), (char __user *)buf,
+ PATH_MAX);
+ //err = -1;
+ set_fs(old_fs);
+ aufs_read_unlock(dentry, AuLock_IR);
+
+ if (err >= 0) {
+ buf[err] = 0;
+ /* will be freed by put_link */
+ nd_set_link(nd, buf);
+ return NULL; /* success */
+ }
+ __putname(buf);
+
+ out:
+ path_put(&nd->path);
+ AuTraceErr(err);
+ return ERR_PTR(err);
+}
+
+static void aufs_put_link(struct dentry *dentry, struct nameidata *nd,
+ void *cookie)
+{
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+ __putname(nd_get_link(nd));
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void aufs_truncate_range(struct inode *inode, loff_t start, loff_t end)
+{
+ AuUnsupport();
+}
+
+/* ---------------------------------------------------------------------- */
+
+struct inode_operations aufs_symlink_iop = {
+ .permission = aufs_permission,
+ .setattr = aufs_setattr,
+#ifdef CONFIG_AUFS_WORKAROUND_FUSE
+ .getattr = aufs_getattr,
+#endif
+
+ .readlink = aufs_readlink,
+ .follow_link = aufs_follow_link,
+ .put_link = aufs_put_link
+};
+
+struct inode_operations aufs_dir_iop = {
+ .create = aufs_create,
+ .lookup = aufs_lookup,
+ .link = aufs_link,
+ .unlink = aufs_unlink,
+ .symlink = aufs_symlink,
+ .mkdir = aufs_mkdir,
+ .rmdir = aufs_rmdir,
+ .mknod = aufs_mknod,
+ .rename = aufs_rename,
+
+ .permission = aufs_permission,
+ .setattr = aufs_setattr,
+#ifdef CONFIG_AUFS_WORKAROUND_FUSE
+ .getattr = aufs_getattr,
+#endif
+
+#if 0 // xattr
+ .setxattr = aufs_setxattr,
+ .getxattr = aufs_getxattr,
+ .listxattr = aufs_listxattr,
+ .removexattr = aufs_removexattr,
+#endif
+
+#if 0 //LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
+ .fallocate = aufs_fallocate
+#endif
+};
+
+struct inode_operations aufs_iop = {
+ .permission = aufs_permission,
+ .setattr = aufs_setattr,
+#ifdef CONFIG_AUFS_WORKAROUND_FUSE
+ .getattr = aufs_getattr,
+#endif
+
+#if 0 // xattr
+ .setxattr = aufs_setxattr,
+ .getxattr = aufs_getxattr,
+ .listxattr = aufs_listxattr,
+ .removexattr = aufs_removexattr,
+#endif
+
+ //void (*truncate) (struct inode *);
+ .truncate_range = aufs_truncate_range,
+
+#if 0 //LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
+ .fallocate = aufs_fallocate
+#endif
+};
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 48/67] aufs inode operations (add entry)
2008-05-16 14:33 ` [PATCH 47/67] aufs inode operations (except add/del/rename) hooanon05
@ 2008-05-16 14:33 ` hooanon05
2008-05-16 14:33 ` [PATCH 49/67] aufs inode operations (del entry) hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:33 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs inode operations (add entry)
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/i_op_add.c | 828 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 828 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/i_op_add.c b/fs/aufs/i_op_add.c
new file mode 100644
index 0000000..1f1fb6c
--- /dev/null
+++ b/fs/aufs/i_op_add.c
@@ -0,0 +1,828 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * inode operations (add entry)
+ *
+ * $Id: i_op_add.c,v 1.4 2008/05/04 23:54:53 sfjro Exp $
+ */
+
+#include "aufs.h"
+
+/*
+ * final procedure of adding a new entry, except link(2).
+ * remove whiteout, instantiate, copyup the parent dir's times and size
+ * and update version.
+ * if it failed, re-create the removed whiteout.
+ */
+static int epilog(struct inode *dir, struct dentry *wh_dentry,
+ struct dentry *dentry)
+{
+ int err, rerr;
+ aufs_bindex_t bwh;
+ struct inode *inode, *h_dir;
+ struct dentry *wh;
+ struct au_ndx ndx;
+ struct super_block *sb;
+
+ LKTRTrace("wh %p, %.*s\n", wh_dentry, AuDLNPair(dentry));
+
+ bwh = -1;
+ if (wh_dentry) {
+ h_dir = wh_dentry->d_parent->d_inode; /* dir inode is locked */
+ IMustLock(h_dir);
+ bwh = au_dbwh(dentry);
+ err = au_wh_unlink_dentry(h_dir, wh_dentry, dentry, dir,
+ /*dlgt*/0);
+ //err = -1;
+ if (unlikely(err))
+ goto out;
+ }
+
+ inode = au_new_inode(dentry);
+ //inode = ERR_PTR(-1);
+ if (!IS_ERR(inode)) {
+ d_instantiate(dentry, inode);
+ dir = dentry->d_parent->d_inode; /* dir inode is locked */
+ IMustLock(dir);
+ /* or always cpup dir mtime? */
+ if (au_ibstart(dir) == au_dbstart(dentry))
+ au_cpup_attr_timesizes(dir);
+ dir->i_version++;
+ return 0; /* success */
+ }
+
+ err = PTR_ERR(inode);
+ if (!wh_dentry)
+ goto out;
+
+ /* revert */
+ sb = dentry->d_sb;
+ ndx.flags = 0;
+ if (unlikely(au_opt_test_dlgt(au_mntflags(sb))))
+ au_fset_ndx(ndx.flags, DLGT);
+ ndx.nfsmnt = au_nfsmnt(sb, bwh);
+ ndx.nd = NULL;
+ //ndx.br = NULL;
+ /* dir inode is locked */
+ wh = au_wh_create(dir, dentry, bwh, wh_dentry->d_parent, &ndx);
+ //wh = ERR_PTR(-1);
+ rerr = PTR_ERR(wh);
+ if (!IS_ERR(wh)) {
+ dput(wh);
+ goto out;
+ }
+ AuIOErr("%.*s reverting whiteout failed(%d, %d)\n",
+ AuDLNPair(dentry), err, rerr);
+ err = -EIO;
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+/*
+ * simple tests for the adding inode operations.
+ * following the checks in vfs, plus the parent-child relationship.
+ */
+int au_may_add(struct dentry *dentry, aufs_bindex_t bindex,
+ struct dentry *h_parent, int isdir, struct au_ndx *ndx)
+{
+ int err, exist;
+ struct dentry *h_dentry;
+ struct inode *h_inode;
+ umode_t h_mode;
+
+ LKTRTrace("%.*s/%.*s, b%d, dir %d\n",
+ AuDLNPair(h_parent), AuDLNPair(dentry), bindex, isdir);
+
+ exist = !!dentry->d_inode;
+ h_dentry = au_h_dptr(dentry, bindex);
+ h_inode = h_dentry->d_inode;
+ if (!exist) {
+ err = -EEXIST;
+ if (unlikely(h_inode))
+ goto out;
+ } else {
+ /* rename(2) case */
+ err = -EIO;
+ if (unlikely(!h_inode || !h_inode->i_nlink))
+ goto out;
+
+ h_mode = h_inode->i_mode;
+ if (!isdir) {
+ err = -EISDIR;
+ if (unlikely(S_ISDIR(h_mode)))
+ goto out;
+ } else if (unlikely(!S_ISDIR(h_mode))) {
+ err = -ENOTDIR;
+ goto out;
+ }
+ }
+
+ err = -EIO;
+ /* expected parent dir is locked */
+ if (unlikely(h_parent != h_dentry->d_parent))
+ goto out;
+ err = 0;
+
+ if (unlikely(au_opt_test(au_mntflags(dentry->d_sb), UDBA_INOTIFY))) {
+ struct dentry *h_latest;
+ struct qstr *qstr = &dentry->d_name;
+
+ err = -EACCES;
+ if (unlikely(au_test_h_perm
+ (h_parent->d_inode, MAY_EXEC | MAY_WRITE,
+ au_ftest_ndx(ndx->flags, DLGT))))
+ goto out;
+
+ err = -EIO;
+ h_latest = au_sio_lkup_one(qstr->name, h_parent, qstr->len,
+ ndx);
+ err = PTR_ERR(h_latest);
+ if (IS_ERR(h_latest))
+ goto out;
+ err = -EIO;
+ dput(h_latest);
+ if (h_latest == h_dentry)
+ err = 0;
+ }
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+/*
+ * initial procedure of adding a new entry.
+ * prepare writable branch and the parent dir, lock it,
+ * lookup whiteout for the new entry.
+ */
+static struct dentry *
+lock_hdir_lkup_wh(struct dentry *dentry, struct au_dtime *dt,
+ struct dentry *src_dentry, struct au_wr_dir_args *wr_dir_args)
+{
+ struct dentry *wh_dentry, *parent, *h_parent, *gparent;
+ int err;
+ aufs_bindex_t bstart, bcpup;
+ struct inode *dir, *h_dir, *gdir;
+ struct au_ndx ndx;
+ struct super_block *sb;
+ struct au_hinode *hgdir;
+ unsigned int mnt_flags;
+
+ LKTRTrace("%.*s, src %p\n", AuDLNPair(dentry), src_dentry);
+
+ parent = dentry->d_parent; /* dir inode is locked */
+ IMustLock(parent->d_inode);
+ bstart = au_dbstart(dentry);
+ err = au_wr_dir(dentry, src_dentry, wr_dir_args);
+ bcpup = err;
+ //err = -1;
+ wh_dentry = ERR_PTR(err);
+ if (unlikely(err < 0))
+ goto out;
+
+ sb = parent->d_sb;
+ mnt_flags = au_mntflags(sb);
+ //todo: meaningless lock if CONFIG_AUFS_DEBUG is disabled.
+ hgdir = NULL;
+ if (unlikely(dt && au_opt_test(mnt_flags, UDBA_INOTIFY)
+ && !IS_ROOT(parent))) {
+ gparent = dget_parent(parent);
+ gdir = gparent->d_inode;
+ ii_read_lock_parent2(gdir);
+ hgdir = au_hi(gdir, bcpup);
+ ii_read_unlock(gdir);
+ dput(gparent);
+ }
+ dir = parent->d_inode;
+ h_parent = au_h_dptr(parent, bcpup);
+ h_dir = h_parent->d_inode;
+
+ AuDbgSleep_UdbaRace();
+ au_hdir_lock(h_dir, dir, bcpup);
+ //todo: revalidate the lower dentry?
+
+ ndx.nfsmnt = au_nfsmnt(sb, bcpup);
+ ndx.flags = 0;
+ if (unlikely(au_opt_test_dlgt(mnt_flags)))
+ au_fset_ndx(ndx.flags, DLGT);
+ ndx.nd = NULL;
+ //ndx.br = NULL;
+ //ndx.nd_file = NULL;
+
+ if (!au_opt_test(mnt_flags, UDBA_NONE) && au_dbstart(dentry) == bcpup) {
+ struct nameidata nd;
+
+ if (unlikely(ndx.nfsmnt)) {
+ //todo: dirty
+ ndx.nd = &nd;
+ ndx.br = au_sbr(sb, bcpup);
+ memset(&nd, 0, sizeof(nd));
+ nd.flags = LOOKUP_CREATE;
+ nd.intent.open.flags = O_EXCL;
+ }
+ err = au_may_add(dentry, bcpup, h_parent,
+ au_ftest_wrdir(wr_dir_args->flags, ISDIR),
+ &ndx);
+ wh_dentry = ERR_PTR(err);
+ if (unlikely(err))
+ goto out_dir;
+ ndx.nd = NULL;
+ ndx.br = NULL;
+ }
+
+ if (dt)
+ au_dtime_store(dt, parent, h_parent, hgdir);
+ wh_dentry = NULL;
+ if (/* bcpup != bstart || */ bcpup != au_dbwh(dentry))
+ goto out; /* success */
+
+ wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, &ndx);
+ //wh_dentry = ERR_PTR(-1);
+
+ out_dir:
+ if (IS_ERR(wh_dentry))
+ au_hdir_unlock(h_dir, dir, bcpup);
+ out:
+ AuTraceErrPtr(wh_dentry);
+ return wh_dentry;
+}
+
+/* ---------------------------------------------------------------------- */
+
+enum { Mknod, Symlink, Creat };
+struct simple_arg {
+ int type;
+ union {
+ struct {
+ int mode;
+ struct nameidata *nd;
+ } c;
+ struct {
+ const char *symname;
+ } s;
+ struct {
+ int mode;
+ dev_t dev;
+ } m;
+ } u;
+};
+
+static int add_simple(struct inode *dir, struct dentry *dentry,
+ struct simple_arg *arg)
+{
+ int err, dlgt, created;
+ struct dentry *h_dentry, *h_parent, *wh_dentry, *parent;
+ struct inode *h_dir;
+ struct au_dtime dt;
+ struct vfsub_args vargs;
+ struct super_block *sb;
+ aufs_bindex_t bstart;
+ struct au_wr_dir_args wr_dir_args = {
+ .force_btgt = -1,
+ .flags = AuWrDir_ADD_ENTRY
+ };
+
+ LKTRTrace("type %d, %.*s\n", arg->type, AuDLNPair(dentry));
+ IMustLock(dir);
+
+ aufs_read_lock(dentry, AuLock_DW);
+ parent = dentry->d_parent; /* dir inode is locked */
+ di_write_lock_parent(parent);
+ wh_dentry = lock_hdir_lkup_wh(dentry, &dt, /*src_dentry*/NULL,
+ &wr_dir_args);
+ //wh_dentry = ERR_PTR(-1);
+ err = PTR_ERR(wh_dentry);
+ if (IS_ERR(wh_dentry))
+ goto out;
+
+ bstart = au_dbstart(dentry);
+ h_dentry = au_h_dptr(dentry, bstart);
+ h_parent = h_dentry->d_parent; /* dir inode is locked */
+ h_dir = h_parent->d_inode;
+ IMustLock(h_dir);
+ sb = dir->i_sb;
+ dlgt = !!au_opt_test_dlgt(au_mntflags(sb));
+
+#if 1 // partial testing
+ switch (arg->type) {
+ case Creat:
+ AuDebugOn(au_test_nfs(h_dir->i_sb) && !arg->u.c.nd);
+ err = au_h_create(h_dir, h_dentry, arg->u.c.mode, dlgt,
+ arg->u.c.nd, au_nfsmnt(sb, bstart));
+ break;
+ case Symlink:
+ err = vfsub_symlink(h_dir, h_dentry, arg->u.s.symname,
+ S_IALLUGO, dlgt);
+ break;
+ case Mknod:
+ err = vfsub_mknod(h_dir, h_dentry, arg->u.m.mode, arg->u.m.dev,
+ dlgt);
+ break;
+ default:
+ BUG();
+ }
+#else
+ err = -1;
+#endif
+ created = !err;
+ if (!err)
+ err = epilog(dir, wh_dentry, dentry);
+ //err = -1;
+
+ /* revert */
+ if (unlikely(created && err && h_dentry->d_inode)) {
+ int rerr;
+ vfsub_args_init(&vargs, NULL, dlgt, 0);
+ rerr = vfsub_unlink(h_dir, h_dentry, &vargs);
+ //rerr = -1;
+ if (rerr) {
+ AuIOErr("%.*s revert failure(%d, %d)\n",
+ AuDLNPair(dentry), err, rerr);
+ err = -EIO;
+ }
+ //todo: inotify will be fired to the grand parent dir
+ au_dtime_revert(&dt);
+ d_drop(dentry);
+ }
+
+ au_hdir_unlock(h_dir, dir, bstart);
+ dput(wh_dentry);
+
+ out:
+ if (unlikely(err)) {
+ au_update_dbstart(dentry);
+ d_drop(dentry);
+ }
+ di_write_unlock(parent);
+ aufs_read_unlock(dentry, AuLock_DW);
+ AuTraceErr(err);
+ return err;
+}
+
+int aufs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
+{
+ struct simple_arg arg = {
+ .type = Mknod,
+ .u.m = {
+ .mode = mode,
+ .dev = dev
+ }
+ };
+ return add_simple(dir, dentry, &arg);
+}
+
+int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
+{
+ struct simple_arg arg = {
+ .type = Symlink,
+ .u.s.symname = symname
+ };
+ return add_simple(dir, dentry, &arg);
+}
+
+int aufs_create(struct inode *dir, struct dentry *dentry, int mode,
+ struct nameidata *nd)
+{
+ struct simple_arg arg = {
+ .type = Creat,
+ .u.c = {
+ .mode = mode,
+ .nd = nd
+ }
+ };
+ return add_simple(dir, dentry, &arg);
+}
+
+/* ---------------------------------------------------------------------- */
+
+struct link_arg {
+ aufs_bindex_t bdst, bsrc;
+ int issamedir, dlgt;
+ struct dentry *src_parent, *parent, *h_dentry;
+ struct inode *h_dir, *inode, *dir;
+};
+
+static int cpup_before_link(struct dentry *src_dentry, struct inode *dir,
+ struct link_arg *a)
+{
+ int err;
+ struct inode *hi, *h_dir, *src_dir, *gdir;
+ struct dentry *gparent;
+
+ AuTraceEnter();
+
+ gparent = NULL;
+ gdir = NULL;
+ if (unlikely(au_opt_test(au_mntflags(src_dentry->d_sb), UDBA_INOTIFY)
+ && !IS_ROOT(a->src_parent))) {
+ gparent = dget_parent(a->src_parent);
+ gdir = gparent->d_inode;
+ if (gdir == dir) {
+ dput(gparent);
+ gparent = NULL;
+ }
+ }
+ src_dir = a->src_parent->d_inode;
+ h_dir = NULL;
+
+ if (!a->issamedir) {
+ /* this temporary unlock/lock is safe */
+ au_hdir_unlock(a->h_dir, dir, a->bdst);
+ di_read_lock_parent2(a->src_parent, AuLock_IR);
+ err = au_test_and_cpup_dirs(src_dentry, a->bdst, a->parent);
+ //err = -1;
+ if (unlikely(err)) {
+ au_hdir_lock(a->h_dir, dir, a->bdst);
+ goto out;
+ }
+
+ //todo: meaningless lock if CONFIG_AUFS_DEBUG is disabled.
+ if (unlikely(gparent))
+ ii_read_lock_parent3(gdir);
+ h_dir = au_h_iptr(src_dir, a->bdst);
+ au_hdir_lock(h_dir, src_dir, a->bdst);
+ } else if (unlikely(gparent)) {
+ /* this temporary unlock/lock is safe */
+ au_hdir_unlock(a->h_dir, dir, a->bdst);
+ ii_read_lock_parent3(gdir);
+ au_hdir_lock(a->h_dir, dir, a->bdst);
+ }
+ //todo: test parent-gparent relationship
+
+ AuDebugOn(au_dbstart(src_dentry) != a->bsrc);
+ hi = au_h_dptr(src_dentry, a->bsrc)->d_inode;
+ mutex_lock_nested(&hi->i_mutex, AuLsc_I_CHILD);
+ err = au_sio_cpup_simple(src_dentry, a->bdst, -1, AuCpup_DTIME);
+ //err = -1;
+ mutex_unlock(&hi->i_mutex);
+
+ if (unlikely(gparent)) {
+ ii_read_unlock(gdir);
+ dput(gparent);
+ }
+
+ out:
+ if (h_dir) {
+ au_hdir_unlock(h_dir, src_dir, a->bdst);
+ au_hdir_lock(a->h_dir, dir, a->bdst);
+ }
+ if (!a->issamedir)
+ di_read_unlock(a->src_parent, AuLock_IR);
+
+ AuTraceErr(err);
+ return err;
+}
+
+static int cpup_or_link(struct dentry *src_dentry, struct link_arg *a)
+{
+ int err;
+ struct inode *inode, *h_inode, *h_dst_inode;
+ struct dentry *h_dentry;
+ aufs_bindex_t bstart;
+ struct super_block *sb;
+
+ AuTraceEnter();
+
+ sb = src_dentry->d_sb;
+ inode = src_dentry->d_inode;
+ AuDebugOn(au_dbstart(src_dentry) != a->bsrc);
+ h_dentry = au_h_dptr(src_dentry, a->bsrc);
+ h_inode = h_dentry->d_inode;
+ bstart = au_ibstart(inode);
+ h_dst_inode = NULL;
+ if (bstart <= a->bdst)
+ h_dst_inode = au_h_iptr(inode, a->bdst);
+
+ if (!h_dst_inode || !h_dst_inode->i_nlink) {
+ /* copyup src_dentry as the name of dentry. */
+ au_set_dbstart(src_dentry, a->bdst);
+ au_set_h_dptr(src_dentry, a->bdst, dget(a->h_dentry));
+ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);
+ err = au_sio_cpup_single(src_dentry, a->bdst, a->bsrc, -1,
+ !AuCpup_DTIME);
+ //err = -1;
+ mutex_unlock(&h_inode->i_mutex);
+ au_set_h_dptr(src_dentry, a->bdst, NULL);
+ au_set_dbstart(src_dentry, a->bsrc);
+ } else {
+ /* the inode of src_dentry already exists on a.bdst branch */
+ h_dentry = d_find_alias(h_dst_inode);
+ if (h_dentry) {
+ err = vfsub_link(h_dentry, a->h_dir,
+ a->h_dentry, a->dlgt);
+ dput(h_dentry);
+ } else {
+ AuIOErr("no dentry found for i%lu on b%d\n",
+ h_dst_inode->i_ino, a->bdst);
+ err = -EIO;
+ }
+ }
+
+ if (!err)
+ au_plink_append(sb, a->inode, a->h_dentry, a->bdst);
+
+ AuTraceErr(err);
+ return err;
+}
+
+int aufs_link(struct dentry *src_dentry, struct inode *dir,
+ struct dentry *dentry)
+{
+ int err, rerr;
+ struct dentry *h_parent, *wh_dentry, *h_src_dentry;
+ struct au_dtime dt;
+ struct link_arg a;
+ struct super_block *sb;
+ unsigned int mnt_flags;
+ struct vfsub_args vargs;
+ struct au_wr_dir_args wr_dir_args = {
+ //.force_btgt = -1,
+ .flags = AuWrDir_ADD_ENTRY
+ };
+
+ LKTRTrace("src %.*s, i%lu, dst %.*s\n",
+ AuDLNPair(src_dentry), dir->i_ino, AuDLNPair(dentry));
+ IMustLock(dir);
+ IMustLock(src_dentry->d_inode);
+ AuDebugOn(S_ISDIR(src_dentry->d_inode->i_mode));
+
+ aufs_read_and_write_lock2(dentry, src_dentry, /*flags*/0);
+ sb = dentry->d_sb;
+ a.dir = dir;
+ a.src_parent = dget_parent(src_dentry);
+ a.parent = dentry->d_parent; /* dir inode is locked */
+ a.issamedir = (a.src_parent == a.parent);
+ if (!a.issamedir)
+ au_fset_wrdir(wr_dir_args.flags, LOCK_SRCDIR);
+ wr_dir_args.force_btgt = au_dbstart(src_dentry);
+ di_write_lock_parent(a.parent);
+ wr_dir_args.force_btgt = au_wbr(dentry, wr_dir_args.force_btgt);
+ wh_dentry = lock_hdir_lkup_wh(dentry, &dt, src_dentry, &wr_dir_args);
+ //wh_dentry = ERR_PTR(-1);
+ err = PTR_ERR(wh_dentry);
+ if (IS_ERR(wh_dentry))
+ goto out;
+
+ a.inode = src_dentry->d_inode;
+ a.bdst = au_dbstart(dentry);
+ a.h_dentry = au_h_dptr(dentry, a.bdst);
+ h_parent = a.h_dentry->d_parent; /* dir inode is locked */
+ a.h_dir = h_parent->d_inode;
+ IMustLock(a.h_dir);
+
+ err = 0;
+ mnt_flags = au_mntflags(sb);
+ a.dlgt = !!au_opt_test_dlgt(mnt_flags);
+
+ //todo: minor optimize, their sb may be same while their bindex differs.
+ a.bsrc = au_dbstart(src_dentry);
+ h_src_dentry = au_h_dptr(src_dentry, a.bsrc);
+ if (unlikely(!au_opt_test(mnt_flags, PLINK))) {
+ /*
+ * copyup src_dentry to the branch we process,
+ * and then link(2) to it.
+ * gave up 'pseudo link by cpup' approach,
+ * since nlink may be one and some applications will not work.
+ */
+ if (a.bdst < a.bsrc
+ /* && h_src_dentry->d_sb != a.h_dentry->d_sb */)
+ err = cpup_before_link(src_dentry, dir, &a);
+ if (!err) {
+ h_src_dentry = au_h_dptr(src_dentry, a.bdst);
+ err = vfsub_link(h_src_dentry, a.h_dir, a.h_dentry,
+ a.dlgt);
+ //err = -1;
+ }
+ } else {
+ if (a.bdst < a.bsrc
+ /* && h_src_dentry->d_sb != a.h_dentry->d_sb */)
+ err = cpup_or_link(src_dentry, &a);
+ else {
+ h_src_dentry = au_h_dptr(src_dentry, a.bdst);
+ err = vfsub_link(h_src_dentry, a.h_dir, a.h_dentry,
+ a.dlgt);
+ //err = -1;
+ }
+ }
+ if (unlikely(err))
+ goto out_unlock;
+ if (wh_dentry) {
+ err = au_wh_unlink_dentry(a.h_dir, wh_dentry, dentry,
+ dir, /*dlgt*/0);
+ //err = -1;
+ if (unlikely(err))
+ goto out_revert;
+ }
+
+#if 0 // cannot support it
+ /* fuse has different memory inode for the same inode number */
+ if (unlikely(au_test_fuse(a.h_dentry->d_sb))) {
+ LKTRLabel(here);
+ d_drop(a.h_dentry);
+ //d_drop(h_src_dentry);
+ //d_drop(src_dentry);
+ inc_nlink(a.inode);
+ a.inode->i_ctime = dir->i_ctime;
+ }
+#endif
+
+ dir->i_version++;
+ if (au_ibstart(dir) == au_dbstart(dentry))
+ au_cpup_attr_timesizes(dir);
+ if (!d_unhashed(a.h_dentry)
+ /* || h_old_inode->i_nlink <= nlink */
+ /* || SB_NFS(h_src_dentry->d_sb) */) {
+ dentry->d_inode = igrab(a.inode);
+ d_instantiate(dentry, a.inode);
+ inc_nlink(a.inode);
+ a.inode->i_ctime = dir->i_ctime;
+ } else
+ /* nfs case (< 2.6.15) */
+ d_drop(dentry);
+#if 0 // debug
+ //au_debug_on();
+ AuDbgInode(a.inode);
+ //au_debug_off();
+ {
+ aufs_bindex_t i;
+ for (i = au_ibstart(a.inode); i <= au_ibend(a.inode); i++) {
+ struct au_xino_entry xinoe;
+ struct inode *hi;
+ hi = au_h_iptr(a.inode, i);
+ if (hi) {
+ au_xino_read(sb, i, hi->i_ino, &xinoe);
+ AuDbg("hi%lu, i%lu\n", hi->i_ino, xinoe.ino);
+ }
+ }
+ }
+#endif
+ goto out_unlock; /* success */
+
+ out_revert:
+#if 0 // remove
+ if (d_unhashed(a.h_dentry)) {
+ /* hardlink on nfs (< 2.6.15) */
+ struct dentry *d;
+ const struct qstr *name = &a.h_dentry->d_name;
+ AuDebugOn(a.h_dentry->d_parent->d_inode != a.h_dir);
+ /* do not superio. */
+ d = au_lkup_one(name->name, a.h_dentry->d_parent,
+ name->len, au_nfsmnt(sb, a.bdst)??,
+ au_opt_test_dlgt(mnt_flags));
+ rerr = PTR_ERR(d);
+ if (IS_ERR(d))
+ goto out_rerr;
+ dput(a.h_dentry);
+ a.h_dentry = d;
+ AuDebugOn(!d->d_inode);
+ }
+#endif
+ vfsub_args_init(&vargs, NULL, a.dlgt, 0);
+ rerr = vfsub_unlink(a.h_dir, a.h_dentry, &vargs);
+ //rerr = -1;
+ if (!rerr)
+ goto out_dt;
+// out_rerr:
+ AuIOErr("%.*s reverting failed(%d, %d)\n",
+ AuDLNPair(dentry), err, rerr);
+ err = -EIO;
+ out_dt:
+ d_drop(dentry);
+ au_dtime_revert(&dt);
+ out_unlock:
+ au_hdir_unlock(a.h_dir, dir, a.bdst);
+ dput(wh_dentry);
+ out:
+ if (unlikely(err)) {
+ au_update_dbstart(dentry);
+ d_drop(dentry);
+ }
+ di_write_unlock(a.parent);
+ dput(a.src_parent);
+ aufs_read_and_write_unlock2(dentry, src_dentry);
+ AuTraceErr(err);
+ return err;
+}
+
+int aufs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+ int err, rerr, diropq, dlgt;
+ struct dentry *h_dentry, *h_parent, *wh_dentry, *parent, *opq_dentry;
+ struct inode *h_dir, *h_inode;
+ struct au_dtime dt;
+ aufs_bindex_t bindex;
+ struct super_block *sb;
+ unsigned int mnt_flags;
+ struct vfsub_args vargs;
+ struct au_wr_dir_args wr_dir_args = {
+ .force_btgt = -1,
+ .flags = AuWrDir_ADD_ENTRY | AuWrDir_ISDIR
+ };
+
+ LKTRTrace("i%lu, %.*s, mode 0%o\n",
+ dir->i_ino, AuDLNPair(dentry), mode);
+ IMustLock(dir);
+
+ aufs_read_lock(dentry, AuLock_DW);
+ parent = dentry->d_parent; /* dir inode is locked */
+ di_write_lock_parent(parent);
+ wh_dentry = lock_hdir_lkup_wh(dentry, &dt, /*src_dentry*/NULL,
+ &wr_dir_args);
+ //wh_dentry = ERR_PTR(-1);
+ err = PTR_ERR(wh_dentry);
+ if (IS_ERR(wh_dentry))
+ goto out;
+
+ sb = dentry->d_sb;
+ bindex = au_dbstart(dentry);
+ h_dentry = au_h_dptr(dentry, bindex);
+ h_parent = h_dentry->d_parent; /* dir inode is locked */
+ h_dir = h_parent->d_inode;
+ IMustLock(h_dir);
+ mnt_flags = au_mntflags(sb);
+ dlgt = !!au_opt_test_dlgt(mnt_flags);
+
+ err = vfsub_mkdir(h_dir, h_dentry, mode, dlgt);
+ //err = -1;
+ if (unlikely(err))
+ goto out_unlock;
+ h_inode = h_dentry->d_inode;
+
+ /* make the dir opaque */
+ diropq = 0;
+ if (wh_dentry || au_opt_test(mnt_flags, ALWAYS_DIROPQ)) {
+ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);
+ opq_dentry = au_diropq_create(dentry, bindex, /*dlgt*/0);
+ //opq_dentry = ERR_PTR(-1);
+ mutex_unlock(&h_inode->i_mutex);
+ err = PTR_ERR(opq_dentry);
+ if (IS_ERR(opq_dentry))
+ goto out_dir;
+ dput(opq_dentry);
+ diropq = 1;
+ }
+
+ err = epilog(dir, wh_dentry, dentry);
+ //err = -1;
+ if (!err) {
+ inc_nlink(dir);
+ goto out_unlock; /* success */
+ }
+
+ /* revert */
+ if (diropq) {
+ LKTRLabel(revert opq);
+ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);
+ rerr = au_diropq_remove(dentry, bindex, dlgt);
+ //rerr = -1;
+ mutex_unlock(&h_inode->i_mutex);
+ if (rerr) {
+ AuIOErr("%.*s reverting diropq failed(%d, %d)\n",
+ AuDLNPair(dentry), err, rerr);
+ err = -EIO;
+ }
+ }
+
+ out_dir:
+ LKTRLabel(revert dir);
+ vfsub_args_init(&vargs, NULL, dlgt, 0);
+ rerr = vfsub_rmdir(h_dir, h_dentry, &vargs);
+ //rerr = -1;
+ if (rerr) {
+ AuIOErr("%.*s reverting dir failed(%d, %d)\n",
+ AuDLNPair(dentry), err, rerr);
+ err = -EIO;
+ }
+ d_drop(dentry);
+ au_dtime_revert(&dt);
+ out_unlock:
+ au_hdir_unlock(h_dir, dir, bindex);
+ dput(wh_dentry);
+ out:
+ if (unlikely(err)) {
+ au_update_dbstart(dentry);
+ d_drop(dentry);
+ }
+ di_write_unlock(parent);
+ aufs_read_unlock(dentry, AuLock_DW);
+ AuTraceErr(err);
+ return err;
+}
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 49/67] aufs inode operations (del entry)
2008-05-16 14:33 ` [PATCH 48/67] aufs inode operations (add entry) hooanon05
@ 2008-05-16 14:33 ` hooanon05
2008-05-16 14:33 ` [PATCH 50/67] aufs inode operation (rename entry) hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:33 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs inode operations (del entry)
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/i_op_del.c | 582 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 582 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/i_op_del.c b/fs/aufs/i_op_del.c
new file mode 100644
index 0000000..5b84cca
--- /dev/null
+++ b/fs/aufs/i_op_del.c
@@ -0,0 +1,582 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * inode operations (del entry)
+ *
+ * $Id: i_op_del.c,v 1.4 2008/05/04 23:54:53 sfjro Exp $
+ */
+
+#include "aufs.h"
+
+/* returns,
+ * 0: wh is unnecessary
+ * plus: wh is necessary
+ * minus: error
+ */
+int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup,
+ struct dentry *locked)
+{
+ int need_wh, err;
+ aufs_bindex_t bstart;
+ struct dentry *h_dentry;
+ struct super_block *sb;
+
+ LKTRTrace("%.*s, isdir %d, *bcpup %d, locked %p\n",
+ AuDLNPair(dentry), isdir, *bcpup, locked);
+ sb = dentry->d_sb;
+
+ bstart = au_dbstart(dentry);
+ LKTRTrace("bcpup %d, bstart %d\n", *bcpup, bstart);
+ h_dentry = au_h_dptr(dentry, bstart);
+ if (*bcpup < 0) {
+ *bcpup = bstart;
+ if (au_test_ro(sb, bstart, dentry->d_inode)) {
+ err = AuWbrCopyup(au_sbi(sb), dentry);
+ *bcpup = err;
+ //err = -1;
+ if (unlikely(err < 0))
+ goto out;
+ }
+ } else
+ AuDebugOn(bstart < *bcpup
+ || au_test_ro(sb, *bcpup, dentry->d_inode));
+ LKTRTrace("bcpup %d, bstart %d\n", *bcpup, bstart);
+
+ if (*bcpup != bstart) {
+ err = au_cpup_dirs(dentry, *bcpup, locked);
+ //err = -1;
+ if (unlikely(err))
+ goto out;
+ need_wh = 1;
+ } else {
+ //struct nameidata nd;
+ aufs_bindex_t old_bend, new_bend, bdiropq = -1;
+ old_bend = au_dbend(dentry);
+ if (isdir) {
+ bdiropq = au_dbdiropq(dentry);
+ au_set_dbdiropq(dentry, -1);
+ }
+ need_wh = au_lkup_dentry(dentry, bstart + 1, /*type*/0,
+ /*nd*/NULL);
+ err = need_wh;
+ //err = -1;
+ if (isdir)
+ au_set_dbdiropq(dentry, bdiropq);
+ if (unlikely(err < 0))
+ goto out;
+ new_bend = au_dbend(dentry);
+ if (!need_wh && old_bend != new_bend) {
+ au_set_h_dptr(dentry, new_bend, NULL);
+ au_set_dbend(dentry, old_bend);
+#if 0 // remove this?
+ } else if (!au_h_dptr(dentry, new_bend)->d_inode) {
+ LKTRTrace("negative\n");
+ au_set_h_dptr(dentry, new_bend, NULL);
+ au_set_dbend(dentry, old_bend);
+ need_wh = 0;
+#endif
+ }
+ }
+ LKTRTrace("need_wh %d\n", need_wh);
+ err = need_wh;
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+/*
+ * simple tests for the removal inode operations.
+ * following the checks in vfs, plus the parent-child relationship.
+ */
+int au_may_del(struct dentry *dentry, aufs_bindex_t bindex,
+ struct dentry *h_parent, int isdir, struct au_ndx *ndx)
+{
+ int err, exist;
+ struct super_block *sb;
+ struct dentry *h_dentry;
+ struct inode *h_inode;
+ umode_t h_mode;
+
+ LKTRTrace("%.*s/%.*s, b%d, dir %d\n",
+ AuDLNPair(h_parent), AuDLNPair(dentry), bindex, isdir);
+
+ sb = dentry->d_sb;
+ exist = !!dentry->d_inode;
+ h_dentry = au_h_dptr(dentry, bindex);
+ h_inode = h_dentry->d_inode;
+ if (exist) {
+ err = -ENOENT;
+ if (unlikely(!h_inode || !h_inode->i_nlink))
+ goto out;
+
+ h_mode = h_inode->i_mode;
+ if (!isdir) {
+ err = -EISDIR;
+ if (unlikely(S_ISDIR(h_mode)))
+ goto out;
+ } else if (unlikely(!S_ISDIR(h_mode))) {
+ err = -ENOTDIR;
+ goto out;
+ }
+ } else {
+ /* rename(2) case */
+ err = -EIO;
+ if (unlikely(h_inode))
+ goto out;
+ }
+
+ err = -ENOENT;
+ /* expected parent dir is locked */
+ if (unlikely(h_parent != h_dentry->d_parent))
+ goto out;
+ err = 0;
+
+ /*
+ * some filesystem may unlink a dir and corrupt its consistency.
+ * so let's try heavy test.
+ */
+ if (1 /*unlikely(au_opt_test(au_mntflags(sb), UDBA_INOTIFY))*/) {
+ struct dentry *h_latest;
+ struct qstr *qstr = &dentry->d_name;
+
+ err = -EACCES;
+ if (unlikely(au_test_h_perm(h_parent->d_inode,
+ MAY_EXEC | MAY_WRITE,
+ au_ftest_ndx(ndx->flags, DLGT))))
+ goto out;
+
+ h_latest = au_sio_lkup_one(qstr->name, h_parent, qstr->len,
+ ndx);
+ //err = PTR_ERR(h_latest);
+ err = -EIO;
+ if (IS_ERR(h_latest))
+ goto out;
+ dput(h_latest);
+ if (h_latest == h_dentry)
+ err = 0;
+ }
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+static struct dentry *
+lock_hdir_create_wh(struct dentry *dentry, int isdir, aufs_bindex_t *rbcpup,
+ struct au_dtime *dt)
+{
+ struct dentry *wh_dentry;
+ int err, need_wh;
+ struct dentry *h_parent, *parent, *gparent;
+ struct inode *dir, *h_dir, *gdir;
+ struct au_ndx ndx;
+ struct super_block *sb;
+ struct au_hinode *hgdir;
+ aufs_bindex_t bcpup;
+ unsigned int mnt_flags;
+
+ LKTRTrace("%.*s, isdir %d\n", AuDLNPair(dentry), isdir);
+
+ need_wh = au_wr_dir_need_wh(dentry, isdir, rbcpup, NULL);
+ err = need_wh;
+ //err = -1;
+ wh_dentry = ERR_PTR(err);
+ if (unlikely(err < 0))
+ goto out;
+
+ //todo: meaningless lock if CONFIG_AUFS_DEBUG is disabled.
+ hgdir = NULL;
+ bcpup = *rbcpup;
+ sb = dentry->d_sb;
+ mnt_flags = au_mntflags(sb);
+ parent = dentry->d_parent; /* dir inode is locked */
+ if (unlikely(au_opt_test(mnt_flags, UDBA_INOTIFY)
+ && !IS_ROOT(parent))) {
+ gparent = dget_parent(parent);
+ gdir = gparent->d_inode;
+ ii_read_lock_parent2(gdir);
+ hgdir = au_hi(gdir, bcpup);
+ ii_read_unlock(gdir);
+ dput(gparent);
+ }
+ dir = parent->d_inode;
+ IMustLock(dir);
+ h_parent = au_h_dptr(parent, bcpup);
+ h_dir = h_parent->d_inode;
+
+ AuDbgSleep_UdbaRace();
+ au_hdir_lock(h_dir, dir, bcpup);
+ //todo: revalidate the lower dentry?
+
+ if (!au_opt_test(mnt_flags, UDBA_NONE) && au_dbstart(dentry) == bcpup) {
+ ndx.nfsmnt = au_nfsmnt(sb, bcpup);
+ ndx.flags = 0;
+ if (unlikely(au_opt_test_dlgt(mnt_flags)))
+ au_fset_ndx(ndx.flags, DLGT);
+ ndx.nd = NULL;
+ //ndx.br = au_sbr(sb, bcpup);
+ //ndx.nd_file = NULL;
+ err = au_may_del(dentry, bcpup, h_parent, isdir, &ndx);
+ wh_dentry = ERR_PTR(err);
+ if (unlikely(err))
+ goto out_dir;
+ }
+
+ au_dtime_store(dt, parent, h_parent, hgdir);
+ wh_dentry = NULL;
+ if (!need_wh)
+ goto out; /* success, no need to create whiteout */
+
+ ndx.nfsmnt = au_nfsmnt(sb, bcpup);
+ ndx.flags = 0;
+ if (unlikely(au_opt_test_dlgt(mnt_flags)))
+ au_fset_ndx(ndx.flags, DLGT);
+ ndx.nd = NULL;
+ //ndx.br = NULL;
+ wh_dentry = au_wh_create(dir, dentry, bcpup, h_parent, &ndx);
+ //wh_dentry = ERR_PTR(-1);
+ if (!IS_ERR(wh_dentry))
+ goto out; /* success */
+ /* returns with the parent is locked and wh_dentry is DGETed */
+
+ out_dir:
+ au_hdir_unlock(h_dir, dir, bcpup);
+ out:
+ AuTraceErrPtr(wh_dentry);
+ return wh_dentry;
+}
+
+static int renwh_and_rmdir(struct dentry *dentry, aufs_bindex_t bindex,
+ struct au_nhash *whlist, struct inode *dir)
+{
+ int rmdir_later, err;
+ struct dentry *h_dentry;
+ struct inode *inode, *h_inode;
+ struct super_block *sb;
+
+ LKTRTrace("%.*s, b%d\n", AuDLNPair(dentry), bindex);
+
+ inode = NULL;
+ h_inode = NULL;
+ sb = dentry->d_sb;
+ if (unlikely(au_opt_test(au_mntflags(sb), UDBA_INOTIFY))) {
+ inode = dentry->d_inode;
+ h_inode = au_h_iptr(inode, bindex);
+ au_hdir2_lock(h_inode, inode, bindex);
+ }
+ err = au_whtmp_ren(dir, dentry, bindex, /*noself*/1);
+ if (unlikely(inode))
+ au_hdir_unlock(h_inode, inode, bindex);
+ //err = -1;
+ if (unlikely(err))
+ goto out;
+
+ h_dentry = au_h_dptr(dentry, bindex);
+ if (!au_test_nfs(h_dentry->d_sb)) {
+ const int dirwh = au_sbi(sb)->si_dirwh;
+ rmdir_later = (dirwh <= 1);
+ if (!rmdir_later)
+ rmdir_later = au_nhash_test_longer_wh(whlist, bindex,
+ dirwh);
+ if (rmdir_later)
+ return rmdir_later;
+ }
+
+ err = au_whtmp_rmdir(h_dentry, whlist, bindex, dir, dentry->d_inode,
+ /*noself*/1);
+ //err = -1;
+ if (unlikely(err)) {
+ AuIOErr("rmdir %.*s, b%d failed, %d. ignored\n",
+ AuDLNPair(h_dentry), bindex, err);
+ err = 0;
+ }
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+static void epilog(struct inode *dir, struct dentry *dentry,
+ aufs_bindex_t bindex)
+{
+ //todo: unnecessary?
+ d_drop(dentry);
+ dentry->d_inode->i_ctime = dir->i_ctime;
+
+ if (atomic_read(&dentry->d_count) == 1) {
+ au_set_h_dptr(dentry, au_dbstart(dentry), NULL);
+ au_update_dbstart(dentry);
+ }
+ if (au_ibstart(dir) == bindex)
+ au_cpup_attr_timesizes(dir);
+ dir->i_version++;
+}
+
+/* revert flags */
+#define AuRev_DLGT 1
+#define au_ftest_rev(flags, name) ((flags) & AuRev_##name)
+#define au_fset_rev(flags, name) { (flags) |= AuRev_##name; }
+#define au_fclr_rev(flags, name) { (flags) &= ~AuRev_##name; }
+#ifndef CONFIG_AUFS_DLGT
+#undef AuRev_DLGT
+#define AuRev_DLGT 0
+#endif
+
+static int do_revert(int err, struct dentry *wh_dentry, struct dentry *dentry,
+ aufs_bindex_t bwh, struct au_dtime *dt, unsigned int flags)
+{
+ int rerr;
+ struct inode *dir;
+
+ dir = wh_dentry->d_parent->d_inode; /* dir inode is locked */
+ IMustLock(dir);
+ rerr = au_wh_unlink_dentry(dir, wh_dentry, dentry, dir,
+ au_ftest_rev(flags, DLGT));
+ //rerr = -1;
+ if (!rerr) {
+ au_set_dbwh(dentry, bwh);
+ au_dtime_revert(dt);
+ return 0;
+ }
+
+ AuIOErr("%.*s reverting whiteout failed(%d, %d)\n",
+ AuDLNPair(dentry), err, rerr);
+ return -EIO;
+}
+
+/* ---------------------------------------------------------------------- */
+
+int aufs_unlink(struct inode *dir, struct dentry *dentry)
+{
+ int err, dlgt;
+ struct inode *inode, *h_dir;
+ struct dentry *parent, *wh_dentry, *h_dentry;
+ struct au_dtime dt;
+ aufs_bindex_t bwh, bindex, bstart;
+ struct super_block *sb;
+ struct vfsub_args vargs;
+
+ LKTRTrace("i%lu, %.*s\n", dir->i_ino, AuDLNPair(dentry));
+ IMustLock(dir);
+ inode = dentry->d_inode;
+ if (unlikely(!inode))
+ return -ENOENT; /* possible? */
+ IMustLock(inode);
+
+ aufs_read_lock(dentry, AuLock_DW);
+ parent = dentry->d_parent; /* dir inode is locked */
+ di_write_lock_parent(parent);
+
+ bstart = au_dbstart(dentry);
+ bwh = au_dbwh(dentry);
+ bindex = -1;
+ wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/0, &bindex, &dt);
+ //wh_dentry = ERR_PTR(-1);
+ err = PTR_ERR(wh_dentry);
+ if (IS_ERR(wh_dentry))
+ goto out;
+
+ sb = dir->i_sb;
+ dlgt = !!au_opt_test_dlgt(au_mntflags(sb));
+ AuDebugOn(au_dbstart(dentry) != bstart);
+ h_dentry = au_h_dptr(dentry, bstart);
+ dget(h_dentry);
+
+ if (bindex == bstart) {
+ vfsub_args_init(&vargs, NULL, dlgt, 0);
+ h_dir = h_dentry->d_parent->d_inode; /* dir inode is locked */
+ IMustLock(h_dir);
+ err = vfsub_unlink(h_dir, h_dentry, &vargs);
+ //err = -1;
+ } else {
+ /* dir inode is locked */
+ AuDebugOn(!wh_dentry
+ || wh_dentry->d_parent != au_h_dptr(parent, bindex));
+ h_dir = wh_dentry->d_parent->d_inode;
+ IMustLock(h_dir);
+ err = 0;
+ }
+
+ if (!err) {
+ drop_nlink(inode);
+#if 0 //todo: update plink
+ if (unlikely(!inode->i_nlink
+ && au_plink_test(sb, inode)
+ /* && atomic_read(&inode->i_count) == 2) */)) {
+ au_debug_on();
+ DbgInode(inode);
+ au_debug_off();
+ }
+#endif
+ epilog(dir, dentry, bindex);
+
+ /* update target timestamps */
+ if (bindex == bstart) {
+ au_update_fuse_h_inode(NULL, h_dentry); /*ignore*/
+ inode->i_ctime = h_dentry->d_inode->i_ctime;
+ } else
+ //todo: this timestamp may be reverted later
+ inode->i_ctime = h_dir->i_ctime;
+ goto out_unlock; /* success */
+ }
+
+ /* revert */
+ if (wh_dentry) {
+ int rerr;
+ unsigned int rev_flags;
+
+ rev_flags = 0;
+ if (unlikely(dlgt))
+ au_fset_rev(rev_flags, DLGT);
+ rerr = do_revert(err, wh_dentry, dentry, bwh, &dt, rev_flags);
+ if (rerr)
+ err = rerr;
+ }
+
+ out_unlock:
+ au_hdir_unlock(h_dir, dir, bindex);
+ dput(wh_dentry);
+ dput(h_dentry);
+ out:
+ di_write_unlock(parent);
+ aufs_read_unlock(dentry, AuLock_DW);
+ AuTraceErr(err);
+ return err;
+}
+
+int aufs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ int err, rmdir_later;
+ struct inode *inode, *h_dir;
+ struct dentry *parent, *wh_dentry, *h_dentry;
+ struct au_dtime dt;
+ aufs_bindex_t bwh, bindex, bstart;
+ struct au_whtmp_rmdir_args *args;
+ struct au_nhash *whlist;
+ struct super_block *sb;
+ unsigned int mnt_flags;
+
+ LKTRTrace("i%lu, %.*s\n", dir->i_ino, AuDLNPair(dentry));
+ IMustLock(dir);
+ inode = dentry->d_inode;
+ if (unlikely(!inode))
+ return -ENOENT; /* possible? */
+ IMustLock(inode);
+
+ whlist = au_nhash_new(GFP_TEMPORARY);
+ err = PTR_ERR(whlist);
+ if (IS_ERR(whlist))
+ goto out;
+
+ err = -ENOMEM;
+ args = kmalloc(sizeof(*args), GFP_TEMPORARY);
+ //args = NULL;
+ if (unlikely(!args))
+ goto out_whlist;
+
+ aufs_read_lock(dentry, AuLock_DW);
+ parent = dentry->d_parent; /* dir inode is locked */
+ di_write_lock_parent(parent);
+ err = au_test_empty(dentry, whlist);
+ //err = -1;
+ if (unlikely(err))
+ goto out_args;
+
+ bstart = au_dbstart(dentry);
+ bwh = au_dbwh(dentry);
+ bindex = -1;
+ wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/1, &bindex, &dt);
+ //wh_dentry = ERR_PTR(-1);
+ err = PTR_ERR(wh_dentry);
+ if (IS_ERR(wh_dentry))
+ goto out_args;
+
+ AuDebugOn(au_dbstart(dentry) != bstart);
+ h_dentry = au_h_dptr(dentry, bstart);
+ dget(h_dentry);
+
+ rmdir_later = 0;
+ if (bindex == bstart) {
+ h_dir = h_dentry->d_parent->d_inode; /* dir inode is locked */
+ IMustLock(h_dir);
+ err = renwh_and_rmdir(dentry, bstart, whlist, dir);
+ //err = -1;
+ if (err > 0) {
+ rmdir_later = err;
+ err = 0;
+ }
+ } else {
+ /* dir inode is locked */
+ AuDebugOn(!wh_dentry
+ || wh_dentry->d_parent != au_h_dptr(parent, bindex));
+ h_dir = wh_dentry->d_parent->d_inode;
+ IMustLock(h_dir);
+ err = 0;
+ }
+
+ sb = dentry->d_sb;
+ mnt_flags = au_mntflags(sb);
+ if (!err) {
+ //aufs_bindex_t bi, bend;
+
+ if (unlikely(au_opt_test(mnt_flags, UDBA_INOTIFY)
+ && rmdir_later))
+ au_reset_hinotify(inode, /*flags*/0);
+ clear_nlink(inode);
+ au_set_dbdiropq(dentry, -1);
+ epilog(dir, dentry, bindex);
+
+ if (rmdir_later) {
+ au_whtmp_kick_rmdir(h_dentry, whlist, bstart, dir,
+ inode, /*noself*/1, args);
+ args = NULL;
+ }
+
+ goto out_unlock; /* success */
+ }
+
+ /* revert */
+ LKTRLabel(revert);
+ if (wh_dentry) {
+ int rerr;
+ unsigned int rev_flags;
+
+ rev_flags = 0;
+ if (unlikely(au_opt_test_dlgt(mnt_flags)))
+ au_fset_rev(rev_flags, DLGT);
+ rerr = do_revert(err, wh_dentry, dentry, bwh, &dt, rev_flags);
+ if (rerr)
+ err = rerr;
+ }
+
+ out_unlock:
+ au_hdir_unlock(h_dir, dir, bindex);
+ dput(wh_dentry);
+ dput(h_dentry);
+ out_args:
+ di_write_unlock(parent);
+ aufs_read_unlock(dentry, AuLock_DW);
+ kfree(args);
+ out_whlist:
+ au_nhash_del(whlist);
+ out:
+ AuTraceErr(err);
+ return err;
+}
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 50/67] aufs inode operation (rename entry)
2008-05-16 14:33 ` [PATCH 49/67] aufs inode operations (del entry) hooanon05
@ 2008-05-16 14:33 ` hooanon05
2008-05-16 14:33 ` [PATCH 51/67] aufs lower (branch filesystem) inode and setting inotify hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:33 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs inode operation (rename entry)
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/i_op_ren.c | 832 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 832 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/i_op_ren.c b/fs/aufs/i_op_ren.c
new file mode 100644
index 0000000..e1e05fb
--- /dev/null
+++ b/fs/aufs/i_op_ren.c
@@ -0,0 +1,832 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * inode operation (rename entry)
+ * todo: this is monster
+ *
+ * $Id: i_op_ren.c,v 1.4 2008/05/04 23:51:40 sfjro Exp $
+ */
+
+#include "aufs.h"
+
+enum { SRC, DST };
+
+#define AuRen_ISDIR 1
+#define AuRen_ISSAMEDIR (1 << 1)
+#define AuRen_WHSRC (1 << 2)
+#define AuRen_WHDST (1 << 3)
+#define AuRen_DLGT (1 << 4)
+#define au_ftest_ren(flags, name) ((flags) & AuRen_##name)
+#define au_fset_ren(flags, name) { (flags) |= AuRen_##name; }
+#define au_fclr_ren(flags, name) { (flags) &= ~AuRen_##name; }
+#ifndef CONFIG_AUFS_DLGT
+#undef AuRen_DLGT
+#define AuRen_DLGT 0
+#endif
+
+struct rename_args {
+ struct dentry *h_dentry[2], *parent[2], *h_parent[2], *h_trap;
+ struct au_nhash whlist;
+ aufs_bindex_t btgt, bstart[2];
+ struct super_block *sb;
+ unsigned int flags;
+ unsigned int mnt_flags;
+};
+
+static noinline_for_stack int
+do_rename(struct inode *src_dir, struct dentry *src_dentry,
+ struct inode *dir, struct dentry *dentry, struct rename_args *a)
+{
+ int err, need_diropq, bycpup, rerr;
+ struct au_whtmp_rmdir_args *thargs;
+ struct dentry *wh_dentry[2], *h_dst, *h_src;
+ struct inode *h_dir[2];
+ aufs_bindex_t bindex, bend;
+ struct au_hin_ignore ign;
+ struct vfsub_args vargs;
+ struct au_ndx ndx = {
+ .flags = 0,
+ .nd = NULL,
+ //.br = NULL
+ };
+
+ LKTRTrace("%.*s/%.*s, %.*s/%.*s, "
+ "hd{%p, %p}, hp{%p, %p}, wh %p, btgt %d, bstart{%d, %d}, "
+ "flags 0x%x\n",
+ AuDLNPair(a->parent[SRC]), AuDLNPair(src_dentry),
+ AuDLNPair(a->parent[DST]), AuDLNPair(dentry),
+ a->h_dentry[SRC], a->h_dentry[DST],
+ a->h_parent[SRC], a->h_parent[DST],
+ &a->whlist, a->btgt,
+ a->bstart[SRC], a->bstart[DST],
+ a->flags);
+
+ h_dir[SRC] = a->h_parent[SRC]->d_inode;
+ h_dir[DST] = a->h_parent[DST]->d_inode;
+
+ /* prepare workqueue args */
+ h_dst = NULL;
+ thargs = NULL;
+ if (au_ftest_ren(a->flags, ISDIR) && a->h_dentry[DST]->d_inode) {
+ err = -ENOMEM;
+ thargs = kmalloc(sizeof(*thargs), GFP_TEMPORARY);
+ //thargs = NULL;
+ if (unlikely(!thargs))
+ goto out;
+ h_dst = dget(a->h_dentry[DST]);
+ }
+
+ wh_dentry[SRC] = NULL;
+ wh_dentry[DST] = NULL;
+ ndx.nfsmnt = au_nfsmnt(a->sb, a->btgt);
+ if (unlikely(au_ftest_ren(a->flags, DLGT)))
+ au_fset_ndx(ndx.flags, DLGT);
+
+ /* create whiteout for src_dentry */
+ if (au_ftest_ren(a->flags, WHSRC)) {
+ wh_dentry[SRC] = au_wh_create(src_dir, src_dentry, a->btgt,
+ a->h_parent[SRC], &ndx);
+ //wh_dentry[SRC] = ERR_PTR(-1);
+ err = PTR_ERR(wh_dentry[SRC]);
+ if (IS_ERR(wh_dentry[SRC]))
+ goto out_thargs;
+ }
+
+ /* lookup whiteout for dentry */
+ if (au_ftest_ren(a->flags, WHDST)) {
+ struct dentry *d;
+
+ d = au_wh_lkup(a->h_parent[DST], &dentry->d_name, &ndx);
+ //d = ERR_PTR(-1);
+ err = PTR_ERR(d);
+ if (IS_ERR(d))
+ goto out_whsrc;
+ if (!d->d_inode)
+ dput(d);
+ else
+ wh_dentry[DST] = d;
+ }
+
+ /* rename dentry to tmpwh */
+ if (thargs) {
+ err = au_whtmp_ren(dir, dentry, a->btgt, /*noself*/0);
+ //err = -1;
+ if (unlikely(err))
+ goto out_whdst;
+ au_set_h_dptr(dentry, a->btgt, NULL);
+ err = au_lkup_neg(dentry, a->btgt);
+ //err = -1;
+ if (unlikely(err))
+ goto out_whtmp;
+ a->h_dentry[DST] = au_h_dptr(dentry, a->btgt);
+ }
+
+ /* cpup src */
+ if (a->h_dentry[DST]->d_inode && a->bstart[SRC] != a->btgt) {
+ mutex_lock_nested(&a->h_dentry[SRC]->d_inode->i_mutex,
+ AuLsc_I_CHILD);
+ err = au_sio_cpup_simple(src_dentry, a->btgt, -1,
+ !AuCpup_DTIME);
+ //err = -1; // untested dir
+ mutex_unlock(&a->h_dentry[SRC]->d_inode->i_mutex);
+ if (unlikely(err))
+ goto out_whtmp;
+ }
+
+ /* rename by vfs_rename or cpup */
+ need_diropq = au_ftest_ren(a->flags, ISDIR)
+ && (wh_dentry[DST]
+ || au_dbdiropq(dentry) == a->btgt
+ /* hide the lower to keep xino */
+ || a->btgt < au_dbend(dentry)
+ || au_opt_test(a->mnt_flags, ALWAYS_DIROPQ));
+ bycpup = 0;
+ if (au_dbstart(src_dentry) == a->btgt) {
+ if (need_diropq && au_dbdiropq(src_dentry) == a->btgt)
+ need_diropq = 0;
+ vfsub_args_init(&vargs, &ign, au_ftest_ren(a->flags, DLGT), 0);
+ if (unlikely(au_opt_test(a->mnt_flags, UDBA_INOTIFY)
+ && au_ftest_ren(a->flags, ISDIR)))
+ vfsub_ign_hinode(&vargs, IN_MOVE_SELF,
+ au_hi(src_dentry->d_inode, a->btgt));
+ AuDebugOn(au_dbstart(src_dentry) != a->btgt);
+ err = vfsub_rename(h_dir[SRC], au_h_dptr(src_dentry, a->btgt),
+ h_dir[DST], a->h_dentry[DST], &vargs);
+ //err = -1;
+ } else {
+ bycpup = 1;
+ mutex_lock_nested(&a->h_dentry[SRC]->d_inode->i_mutex,
+ AuLsc_I_CHILD);
+ au_set_dbstart(src_dentry, a->btgt);
+ au_set_h_dptr(src_dentry, a->btgt, dget(a->h_dentry[DST]));
+ err = au_sio_cpup_single(src_dentry, a->btgt, a->bstart[SRC],
+ -1, !AuCpup_DTIME);
+ //err = -1; // untested dir
+ if (unlikely(err)) {
+ au_set_h_dptr(src_dentry, a->btgt, NULL);
+ au_set_dbstart(src_dentry, a->bstart[SRC]);
+ }
+ mutex_unlock(&a->h_dentry[SRC]->d_inode->i_mutex);
+ }
+ if (unlikely(err))
+ goto out_whtmp;
+
+ /* make dir opaque */
+ if (need_diropq) {
+ struct dentry *diropq;
+ struct inode *h_inode;
+
+ h_inode = au_h_dptr(src_dentry, a->btgt)->d_inode;
+ au_hdir_lock(h_inode, src_dentry->d_inode, a->btgt);
+ diropq = au_diropq_create(src_dentry, a->btgt,
+ au_ftest_ren(a->flags, DLGT));
+ //diropq = ERR_PTR(-1);
+ au_hdir_unlock(h_inode, src_dentry->d_inode, a->btgt);
+ err = PTR_ERR(diropq);
+ if (IS_ERR(diropq))
+ goto out_rename;
+ dput(diropq);
+ }
+
+ /* update target timestamps */
+ AuDebugOn(au_dbstart(src_dentry) != a->btgt);
+ h_src = au_h_dptr(src_dentry, a->btgt);
+ au_update_fuse_h_inode(NULL, h_src); /*ignore*/
+ //fsstack_copy_attr_atime(src_dentry->d_inode, h_src->d_inode);
+ src_dentry->d_inode->i_ctime = h_src->d_inode->i_ctime;
+
+ /* remove whiteout for dentry */
+ if (wh_dentry[DST]) {
+ err = au_wh_unlink_dentry(h_dir[DST], wh_dentry[DST],
+ dentry, dir, /*dlgt*/0);
+ //err = -1;
+ if (unlikely(err))
+ goto out_diropq;
+ }
+
+ /* remove whtmp */
+ if (thargs) {
+ if (au_test_nfs(h_dst->d_sb)
+ || !au_nhash_test_longer_wh(&a->whlist, a->btgt,
+ au_sbi(a->sb)->si_dirwh)) {
+ err = au_whtmp_rmdir(h_dst, &a->whlist, a->btgt, dir,
+ dentry->d_inode, /*noself*/0);
+ if (unlikely(err))
+ AuWarn("failed removing whtmp dir %.*s (%d), "
+ "ignored.\n", AuDLNPair(h_dst), err);
+ } else {
+ au_whtmp_kick_rmdir(h_dst, &a->whlist, a->btgt, dir,
+ dentry->d_inode, /*noself*/0,
+ thargs);
+ dput(h_dst);
+ thargs = NULL;
+ }
+ }
+ err = 0;
+ goto out_success;
+
+#define RevertFailure(fmt, args...) do { \
+ AuIOErrWhck("revert failure: " fmt " (%d, %d)\n", \
+ ##args, err, rerr); \
+ err = -EIO; \
+ } while (0)
+
+ out_diropq:
+ if (need_diropq) {
+ struct inode *h_inode;
+
+ h_inode = au_h_dptr(src_dentry, a->btgt)->d_inode;
+ //br_wh_read_lock(au_sbr(a->sb, a->btgt));
+ /* i_lock simply since inotify is not set to h_inode. */
+ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_PARENT);
+ //au_hdir_lock(h_inode, src_dentry->d_inode, a->btgt);
+ rerr = au_diropq_remove(src_dentry, a->btgt,
+ au_ftest_ren(a->flags, DLGT));
+ //rerr = -1;
+ //au_hdir_unlock(h_inode, src_dentry->d_inode, a->btgt);
+ mutex_unlock(&h_inode->i_mutex);
+ //br_wh_read_unlock(au_sbr(a->sb, a->btgt));
+ if (rerr)
+ RevertFailure("remove diropq %.*s",
+ AuDLNPair(src_dentry));
+ }
+ out_rename:
+ if (!bycpup) {
+ struct dentry *d;
+ struct qstr *name = &src_dentry->d_name;
+
+ d = au_lkup_one(name->name, a->h_parent[SRC], name->len, &ndx);
+ //d = ERR_PTR(-1);
+ rerr = PTR_ERR(d);
+ if (IS_ERR(d)) {
+ RevertFailure("au_lkup_one %.*s",
+ AuDLNPair(src_dentry));
+ goto out_whtmp;
+ }
+ AuDebugOn(d->d_inode);
+ vfsub_args_init(&vargs, &ign, au_ftest_ren(a->flags, DLGT), 0);
+ if (unlikely(au_opt_test(a->mnt_flags, UDBA_INOTIFY)
+ && au_ftest_ren(a->flags, ISDIR)))
+ vfsub_ign_hinode(&vargs, IN_MOVE_SELF,
+ au_hi(src_dentry->d_inode, a->btgt));
+ rerr = vfsub_rename(h_dir[DST], au_h_dptr(src_dentry, a->btgt),
+ h_dir[SRC], d, &vargs);
+ //rerr = -1;
+ d_drop(d);
+ dput(d);
+ //au_set_h_dptr(src_dentry, a->btgt, NULL);
+ if (rerr)
+ RevertFailure("rename %.*s", AuDLNPair(src_dentry));
+ } else {
+ vfsub_args_init(&vargs, NULL, au_ftest_ren(a->flags, DLGT), 0);
+ rerr = vfsub_unlink(h_dir[DST], a->h_dentry[DST], &vargs);
+ //rerr = -1;
+ au_set_h_dptr(src_dentry, a->btgt, NULL);
+ au_set_dbstart(src_dentry, a->bstart[SRC]);
+ if (rerr)
+ RevertFailure("unlink %.*s",
+ AuDLNPair(a->h_dentry[DST]));
+ }
+ out_whtmp:
+ if (thargs) {
+ struct dentry *d;
+ struct qstr *name = &dentry->d_name;
+
+ d = au_lkup_one(name->name, a->h_parent[DST], name->len, &ndx);
+ //d = ERR_PTR(-1);
+ rerr = PTR_ERR(d);
+ if (IS_ERR(d)) {
+ RevertFailure("lookup %.*s", AuLNPair(name));
+ goto out_whdst;
+ }
+ if (d->d_inode) {
+ d_drop(d);
+ dput(d);
+ goto out_whdst;
+ }
+ AuDebugOn(d->d_inode);
+ vfsub_args_init(&vargs, &ign, au_ftest_ren(a->flags, DLGT), 0);
+ if (unlikely(0 && au_opt_test(a->mnt_flags, UDBA_INOTIFY)
+ && au_ftest_ren(a->flags, ISDIR)))
+ vfsub_ign_hinode(&vargs, IN_MOVE_SELF,
+ au_hi(dentry->d_inode, a->btgt));
+ rerr = vfsub_rename(h_dir[DST], h_dst, h_dir[DST], d, &vargs);
+ //rerr = -1;
+ d_drop(d);
+ dput(d);
+ if (rerr) {
+ RevertFailure("rename %.*s", AuDLNPair(h_dst));
+ goto out_whdst;
+ }
+ au_set_h_dptr(dentry, a->btgt, NULL);
+ au_set_h_dptr(dentry, a->btgt, dget(h_dst));
+ }
+ out_whdst:
+ dput(wh_dentry[DST]);
+ wh_dentry[DST] = NULL;
+ out_whsrc:
+ if (wh_dentry[SRC]) {
+ rerr = au_wh_unlink_dentry(h_dir[SRC], wh_dentry[SRC],
+ src_dentry, src_dir, /*dlgt*/0);
+ //rerr = -1;
+ if (rerr)
+ RevertFailure("unlink %.*s", AuDLNPair(wh_dentry[SRC]));
+ }
+#undef RevertFailure
+ d_drop(src_dentry);
+ bend = au_dbend(src_dentry);
+ for (bindex = au_dbstart(src_dentry); bindex <= bend; bindex++) {
+ struct dentry *hd;
+
+ hd = au_h_dptr(src_dentry, bindex);
+ if (hd)
+ d_drop(hd);
+ }
+ d_drop(dentry);
+ bend = au_dbend(dentry);
+ for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) {
+ struct dentry *hd;
+
+ hd = au_h_dptr(dentry, bindex);
+ if (hd)
+ d_drop(hd);
+ }
+ au_update_dbstart(dentry);
+ if (thargs)
+ d_drop(h_dst);
+ out_success:
+ dput(wh_dentry[SRC]);
+ dput(wh_dentry[DST]);
+ out_thargs:
+ if (thargs) {
+ dput(h_dst);
+ kfree(thargs);
+ }
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+/*
+ * test if @dentry dir can be rename destination or not.
+ * success means, it is a logically empty dir.
+ */
+static int may_rename_dstdir(struct dentry *dentry, aufs_bindex_t btgt,
+ struct au_nhash *whlist)
+{
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+
+ return au_test_empty(dentry, whlist);
+}
+
+/*
+ * test if @dentry dir can be rename source or not.
+ * if it can, return 0 and @children is filled.
+ * success means,
+ * - or, it is a logically empty dir.
+ * - or, it exists on writable branch and has no children including whiteouts
+ * on the lower branch.
+ */
+static int may_rename_srcdir(struct dentry *dentry, aufs_bindex_t btgt)
+{
+ int err;
+ aufs_bindex_t bstart;
+
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+
+ bstart = au_dbstart(dentry);
+ if (bstart != btgt) {
+ struct au_nhash *whlist;
+
+ whlist = au_nhash_new(GFP_TEMPORARY);
+ err = PTR_ERR(whlist);
+ if (IS_ERR(whlist))
+ goto out;
+ err = au_test_empty(dentry, whlist);
+ au_nhash_del(whlist);
+ goto out;
+ }
+
+ if (bstart == au_dbtaildir(dentry))
+ return 0; /* success */
+
+ err = au_test_empty_lower(dentry);
+
+ out:
+ if (/* unlikely */(err == -ENOTEMPTY)) {
+ AuWarn1("renaming dir who has child(ren) on multiple branches,"
+ " is not supported\n");
+ err = -EXDEV;
+ }
+ AuTraceErr(err);
+ return err;
+}
+
+/* mainly for link(2) and rename(2) */
+int au_wbr(struct dentry *dentry, aufs_bindex_t btgt)
+{
+ aufs_bindex_t bdiropq, bwh;
+ struct dentry *parent;
+
+ LKTRTrace("%.*s, b%d\n", AuDLNPair(dentry), btgt);
+ parent = dentry->d_parent;
+ IMustLock(parent->d_inode); /* dir is locked */
+
+ bdiropq = au_dbdiropq(parent);
+ bwh = au_dbwh(dentry);
+ if (au_br_rdonly(au_sbr(dentry->d_sb, btgt))
+ || (0 <= bdiropq && bdiropq < btgt)
+ || (0 <= bwh && bwh < btgt))
+ btgt = -1;
+
+ LKTRTrace("btgt %d\n", btgt);
+ return btgt;
+}
+
+//todo: meaningless lock if CONFIG_AUFS_DEBUG is disabled.
+static void au_hgdirs(struct au_hinode **hgdir, struct rename_args *a)
+{
+ struct dentry *gparent[2];
+ struct inode *gdir;
+
+ if (!au_opt_test(a->mnt_flags, UDBA_INOTIFY))
+ return;
+
+ gparent[SRC] = NULL;
+ if (!IS_ROOT(a->parent[SRC])) {
+ gparent[SRC] = dget_parent(a->parent[SRC]);
+ gdir = gparent[SRC]->d_inode;
+ if (gparent[SRC] != a->parent[DST]) {
+ ii_read_lock_parent3(gdir);
+ hgdir[SRC] = au_hi(gdir, a->btgt);
+ ii_read_unlock(gdir);
+ } else
+ hgdir[SRC] = au_hi(gdir, a->btgt);
+ dput(gparent[SRC]);
+ }
+
+ if (!au_ftest_ren(a->flags, ISSAMEDIR)
+ && !IS_ROOT(a->parent[DST])
+ && a->parent[DST] != gparent[SRC]) {
+ gparent[DST] = dget_parent(a->parent[DST]);
+ gdir = gparent[DST]->d_inode;
+ if (gparent[DST] != a->parent[SRC]) {
+ ii_read_lock_parent3(gdir);
+ hgdir[DST] = au_hi(gdir, a->btgt);
+ ii_read_unlock(gdir);
+ } else
+ hgdir[DST] = au_hi(gdir, a->btgt);
+ dput(gparent[DST]);
+ }
+}
+
+/*
+ * simple tests for rename.
+ * following the checks in vfs, plus the parent-child relationship.
+ */
+static int au_may_ren(struct inode *src_dir, struct dentry *src_dentry,
+ struct inode *dir, struct dentry *dentry,
+ struct rename_args *a, struct au_ndx *ndx)
+{
+ int err;
+ struct inode *h_inode;
+
+ AuTraceEnter();
+
+ if (a->bstart[SRC] == a->btgt) {
+ err = au_may_del(src_dentry, a->btgt, a->h_parent[SRC],
+ au_ftest_ren(a->flags, ISDIR), ndx);
+ if (unlikely(err))
+ goto out;
+ err = -EINVAL;
+ if (unlikely(a->h_dentry[SRC] == a->h_trap))
+ goto out;
+ }
+
+ err = 0;
+ if (a->bstart[DST] != a->btgt)
+ goto out;
+
+ err = -EIO;
+ h_inode = a->h_dentry[DST]->d_inode;
+ if (!dentry->d_inode) {
+ if (unlikely(h_inode))
+ goto out;
+ err = au_may_add(dentry, a->btgt, a->h_parent[DST],
+ au_ftest_ren(a->flags, ISDIR), ndx);
+ } else {
+ if (unlikely(!h_inode || !h_inode->i_nlink))
+ goto out;
+ err = au_may_del(dentry, a->btgt, a->h_parent[DST],
+ au_ftest_ren(a->flags, ISDIR), ndx);
+ if (unlikely(err))
+ goto out;
+ err = -ENOTEMPTY;
+ if (unlikely(a->h_dentry[DST] == a->h_trap))
+ goto out;
+ err = 0;
+ }
+
+ out:
+ if (unlikely(err == -ENOENT || err == -EEXIST))
+ err = -EIO;
+ AuTraceErr(err);
+ return err;
+}
+
+int aufs_rename(struct inode *src_dir, struct dentry *src_dentry,
+ struct inode *dir, struct dentry *dentry)
+{
+ int err, do_dt_dstdir, flags;
+ aufs_bindex_t bend, bindex;
+ struct inode *inode[2], *dirs[2];
+ struct au_hinode *hgdir[2], *hdir;
+ enum { PARENT, CHILD };
+ /* reduce stack space */
+ struct {
+ struct rename_args a;
+ struct au_dtime dt[2][2];
+ } *p;
+ struct au_wr_dir_args wr_dir_args = {
+ //.force_btgt = -1,
+ .flags = AuWrDir_ADD_ENTRY
+ };
+
+ LKTRTrace("i%lu, %.*s, i%lu, %.*s\n",
+ src_dir->i_ino, AuDLNPair(src_dentry),
+ dir->i_ino, AuDLNPair(dentry));
+ IMustLock(src_dir);
+ IMustLock(dir);
+ inode[DST] = dentry->d_inode;
+ if (inode[DST]) {
+ IMustLock(inode[DST]);
+ igrab(inode[DST]);
+ }
+
+ err = -ENOMEM;
+ BUILD_BUG_ON(sizeof(*p) > PAGE_SIZE);
+ p = kmalloc(sizeof(*p), GFP_TEMPORARY);
+ if (unlikely(!p))
+ goto out;
+
+ err = -ENOTDIR;
+ p->a.sb = src_dentry->d_sb;
+ inode[SRC] = src_dentry->d_inode;
+ flags = 0;
+ p->a.flags = 0;
+ if (S_ISDIR(inode[SRC]->i_mode)) {
+ flags = AuLock_DIR;
+ au_fset_ren(p->a.flags, ISDIR);
+ if (unlikely(inode[DST] && !S_ISDIR(inode[DST]->i_mode)))
+ goto out_free;
+ }
+
+ aufs_read_and_write_lock2(dentry, src_dentry, flags);
+ p->a.mnt_flags = au_mntflags(p->a.sb);
+ if (unlikely(au_opt_test_dlgt(p->a.mnt_flags)))
+ au_fset_ren(p->a.flags, DLGT);
+ p->a.parent[SRC] = src_dentry->d_parent; /* dir inode is locked */
+ p->a.parent[DST] = dentry->d_parent; /* dir inode is locked */
+ if (src_dir == dir) {
+ au_fset_ren(p->a.flags, ISSAMEDIR);
+ di_write_lock_parent(p->a.parent[DST]);
+ } else
+ di_write_lock2_parent(p->a.parent[SRC], p->a.parent[DST],
+ /*isdir*/1);
+
+ /* which branch we process */
+ p->a.bstart[SRC] = au_dbstart(src_dentry);
+ p->a.bstart[DST] = au_dbstart(dentry);
+ if (au_ftest_ren(p->a.flags, ISDIR))
+ au_fset_wrdir(wr_dir_args.flags, ISDIR);
+ wr_dir_args.force_btgt = p->a.bstart[SRC];
+ if (dentry->d_inode && p->a.bstart[DST] < p->a.bstart[SRC])
+ wr_dir_args.force_btgt = p->a.bstart[DST];
+ wr_dir_args.force_btgt = au_wbr(dentry, wr_dir_args.force_btgt);
+ err = au_wr_dir(dentry, src_dentry, &wr_dir_args);
+ p->a.btgt = err;
+ if (unlikely(err < 0))
+ goto out_unlock;
+
+ /* are they available to be renamed */
+ err = 0;
+ au_nhash_init(&p->a.whlist);
+ if (au_ftest_ren(p->a.flags, ISDIR) && inode[DST]) {
+ au_set_dbstart(dentry, p->a.bstart[DST]);
+ err = may_rename_dstdir(dentry, p->a.btgt, &p->a.whlist);
+ au_set_dbstart(dentry, p->a.btgt);
+ }
+ p->a.h_dentry[DST] = au_h_dptr(dentry, au_dbstart(dentry));
+ if (unlikely(err))
+ goto out_unlock;
+ //todo: minor optimize, their sb may be same while their bindex differs.
+ p->a.h_dentry[SRC] = au_h_dptr(src_dentry, au_dbstart(src_dentry));
+ if (au_ftest_ren(p->a.flags, ISDIR)) {
+ err = may_rename_srcdir(src_dentry, p->a.btgt);
+ if (unlikely(err))
+ goto out_children;
+ }
+
+ /* prepare the writable parent dir on the same branch */
+ err = au_wr_dir_need_wh(src_dentry, au_ftest_ren(p->a.flags, ISDIR),
+ &p->a.btgt,
+ au_ftest_ren(p->a.flags, ISSAMEDIR)
+ ? NULL : p->a.parent[DST]);
+ if (unlikely(err < 0))
+ goto out_children;
+ if (err)
+ au_fset_ren(p->a.flags, WHSRC);
+ if (p->a.bstart[DST] == p->a.btgt) {
+ au_fset_ren(p->a.flags, WHDST);
+ } else {
+ err = au_cpup_dirs(dentry, p->a.btgt,
+ au_ftest_ren(p->a.flags, ISSAMEDIR)
+ ? NULL : p->a.parent[SRC]);
+ if (unlikely(err))
+ goto out_children;
+ }
+
+ hgdir[SRC] = NULL;
+ hgdir[DST] = NULL;
+ au_hgdirs(hgdir, &p->a);
+ p->a.h_parent[SRC] = au_h_dptr(p->a.parent[SRC], p->a.btgt);
+ p->a.h_parent[DST] = au_h_dptr(p->a.parent[DST], p->a.btgt);
+ dirs[0] = src_dir;
+ dirs[1] = dir;
+
+ AuDbgSleep_UdbaRace();
+ p->a.h_trap = au_hdir_lock_rename(p->a.h_parent, dirs, p->a.btgt,
+ au_ftest_ren(p->a.flags, ISSAMEDIR));
+ //todo: revalidate the lower dentries?
+
+ if (!au_opt_test(p->a.mnt_flags, UDBA_NONE)) {
+ struct au_ndx ndx = {
+ .nfsmnt = au_nfsmnt(p->a.sb, p->a.btgt),
+ .flags = 0,
+ .nd = NULL,
+ //.br = NULL
+ };
+ if (unlikely(au_ftest_ren(p->a.flags, DLGT)))
+ au_fset_ndx(ndx.flags, DLGT);
+ err = au_may_ren(src_dir, src_dentry, dir, dentry, &p->a, &ndx);
+ if (unlikely(err))
+ goto out_hdir;
+ }
+
+ /* store timestamps to be revertible */
+ au_dtime_store(p->dt[PARENT] + SRC, p->a.parent[SRC],
+ p->a.h_parent[SRC], hgdir[SRC]);
+ if (!au_ftest_ren(p->a.flags, ISSAMEDIR))
+ au_dtime_store(p->dt[PARENT] + DST, p->a.parent[DST],
+ p->a.h_parent[DST], hgdir[DST]);
+ do_dt_dstdir = 0;
+ if (au_ftest_ren(p->a.flags, ISDIR)) {
+ hdir = NULL;
+ if (unlikely(au_opt_test(p->a.mnt_flags, UDBA_INOTIFY)))
+ hdir = au_hi(p->a.parent[SRC]->d_inode, p->a.btgt);
+ au_dtime_store(p->dt[CHILD] + SRC, src_dentry,
+ p->a.h_dentry[SRC], hdir);
+ if (p->a.h_dentry[DST]->d_inode) {
+ do_dt_dstdir = 1;
+ if (unlikely(au_opt_test(p->a.mnt_flags, UDBA_INOTIFY)))
+ hdir = au_hi(p->a.parent[DST]->d_inode,
+ p->a.btgt);
+ au_dtime_store(p->dt[CHILD] + DST, dentry,
+ p->a.h_dentry[DST], hdir);
+ }
+ }
+
+ err = do_rename(src_dir, src_dentry, dir, dentry, &p->a);
+ if (unlikely(err))
+ goto out_dt;
+ au_hdir_unlock_rename(p->a.h_parent, dirs, p->a.btgt,
+ au_ftest_ren(p->a.flags, ISSAMEDIR));
+
+ /* update dir attributes */
+ dir->i_version++;
+ if (au_ftest_ren(p->a.flags, ISDIR)) {
+ /* is this updating defined in POSIX? */
+ //mutex_lock(&inode[SRC]->i_mutex);
+ au_cpup_attr_timesizes(inode[SRC]);
+ //mutex_unlock(&inode[SRC]->i_mutex);
+
+ au_cpup_attr_nlink(dir);
+ if (inode[DST]) {
+ clear_nlink(inode[DST]);
+ au_cpup_attr_timesizes(inode[DST]);
+ }
+ }
+ if (au_ibstart(dir) == p->a.btgt)
+ au_cpup_attr_timesizes(dir);
+
+ if (!au_ftest_ren(p->a.flags, ISSAMEDIR)) {
+ src_dir->i_version++;
+ if (au_ftest_ren(p->a.flags, ISDIR))
+ au_cpup_attr_nlink(src_dir);
+ if (au_ibstart(src_dir) == p->a.btgt)
+ au_cpup_attr_timesizes(src_dir);
+ }
+
+#if 0 // todo: test it
+ d_drop(src_dentry);
+#else
+ /* dput/iput all lower dentries */
+ au_set_dbwh(src_dentry, -1);
+ bend = au_dbend(src_dentry);
+ for (bindex = p->a.btgt + 1; bindex <= bend; bindex++) {
+ struct dentry *hd;
+ hd = au_h_dptr(src_dentry, bindex);
+ if (hd)
+ au_set_h_dptr(src_dentry, bindex, NULL);
+ }
+ au_set_dbend(src_dentry, p->a.btgt);
+
+ bend = au_ibend(inode[SRC]);
+ for (bindex = p->a.btgt + 1; bindex <= bend; bindex++) {
+ struct inode *hi;
+ hi = au_h_iptr(inode[SRC], bindex);
+ if (hi) {
+ //AuDbg("hi%lu, i%lu\n", hi->i_ino, 0LU);
+ au_xino_write0(p->a.sb, bindex, hi->i_ino, 0);
+ /* ignore this error */
+ au_set_h_iptr(inode[SRC], bindex, NULL, 0);
+ }
+ }
+ au_set_ibend(inode[SRC], p->a.btgt);
+#endif
+
+#if 0 // remove this
+ if (inode[DST]) {
+ struct inode *h_i;
+
+ bend = au_ibend(inode[DST]);
+ for (bindex = au_ibstart(inode[DST]); bindex <= bend;
+ bindex++) {
+ h_i = au_h_iptr(inode[DST], bindex);
+ if (h_i)
+ au_xino_write0(p->a.sb, bindex, h_i->i_ino, 0);
+ /* ignore this error */
+ /* bad action? */
+ }
+ }
+#endif
+
+ goto out_children; /* success */
+
+ out_dt:
+ au_dtime_revert(p->dt[PARENT] + SRC);
+ if (!au_ftest_ren(p->a.flags, ISSAMEDIR))
+ au_dtime_revert(p->dt[PARENT] + DST);
+ if (au_ftest_ren(p->a.flags, ISDIR) && err != -EIO) {
+ struct dentry *hd;
+
+ hd = p->dt[CHILD][SRC].dt_h_dentry;
+ mutex_lock_nested(&hd->d_inode->i_mutex, AuLsc_I_CHILD);
+ au_dtime_revert(p->dt[CHILD] + SRC);
+ mutex_unlock(&hd->d_inode->i_mutex);
+ if (do_dt_dstdir) {
+ hd = p->dt[CHILD][DST].dt_h_dentry;
+ mutex_lock_nested(&hd->d_inode->i_mutex, AuLsc_I_CHILD);
+ au_dtime_revert(p->dt[CHILD] + DST);
+ mutex_unlock(&hd->d_inode->i_mutex);
+ }
+ }
+ out_hdir:
+ au_hdir_unlock_rename(p->a.h_parent, dirs, p->a.btgt,
+ au_ftest_ren(p->a.flags, ISSAMEDIR));
+ out_children:
+ au_nhash_fin(&p->a.whlist);
+ out_unlock:
+ //if (unlikely(err /* && au_ftest_ren(p->a.flags, ISDIR) */)) {
+ if (unlikely(err && au_ftest_ren(p->a.flags, ISDIR))) {
+ au_update_dbstart(dentry);
+ d_drop(dentry);
+ }
+ if (au_ftest_ren(p->a.flags, ISSAMEDIR))
+ di_write_unlock(p->a.parent[DST]);
+ else
+ di_write_unlock2(p->a.parent[SRC], p->a.parent[DST]);
+ aufs_read_and_write_unlock2(dentry, src_dentry);
+ out_free:
+ kfree(p);
+ out:
+ iput(inode[DST]);
+ AuTraceErr(err);
+ return err;
+}
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 51/67] aufs lower (branch filesystem) inode and setting inotify
2008-05-16 14:33 ` [PATCH 50/67] aufs inode operation (rename entry) hooanon05
@ 2008-05-16 14:33 ` hooanon05
2008-05-16 14:33 ` [PATCH 52/67] aufs inotify handler hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:33 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs lower (branch filesystem) inode and setting inotify
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/hinode.h | 188 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 188 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/hinode.h b/fs/aufs/hinode.h
new file mode 100644
index 0000000..59ddae7
--- /dev/null
+++ b/fs/aufs/hinode.h
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * lower (branch filesystem) inode and setting inotify
+ *
+ * $Id: hinode.h,v 1.3 2008/04/28 03:08:37 sfjro Exp $
+ */
+
+#ifndef __AUFS_HINODE_H__
+#define __AUFS_HINODE_H__
+
+#ifdef __KERNEL__
+
+#include <linux/fs.h>
+#include <linux/inotify.h>
+#include <linux/aufs_type.h>
+//#include "branch.h"
+//#include "inode.h"
+#include "super.h"
+#include "vfsub.h"
+
+/* ---------------------------------------------------------------------- */
+
+struct au_hinotify {
+#ifdef CONFIG_AUFS_HINOTIFY
+ struct inotify_watch hin_watch;
+ struct inode *hin_aufs_inode; /* no get/put */
+
+ /* an array of atomic_t X au_hin_nignore */
+ atomic_t hin_ignore[0];
+#endif
+};
+
+struct au_hinode {
+ struct inode *hi_inode;
+ aufs_bindex_t hi_id;
+#ifdef CONFIG_AUFS_HINOTIFY
+ struct au_hinotify *hi_notify;
+#endif
+
+ /* reference to the copied-up whiteout with get/put */
+ struct dentry *hi_whdentry;
+};
+
+struct au_hin_ignore {
+#ifdef CONFIG_AUFS_HINOTIFY
+ __u32 ign_events;
+ struct au_hinode *ign_hinode;
+#endif
+};
+
+/* ---------------------------------------------------------------------- */
+
+#ifdef CONFIG_AUFS_HINOTIFY
+static inline
+void au_hin_init(struct au_hinode *hinode, struct au_hinotify *val)
+{
+ hinode->hi_notify = val;
+}
+
+/* hinotify.c */
+int au_hin_alloc(struct au_hinode *hinode, struct inode *inode,
+ struct inode *h_inode);
+void au_hin_free(struct au_hinode *hinode);
+void au_do_hdir_lock(struct inode *h_dir, struct inode *dir,
+ aufs_bindex_t bindex, unsigned int lsc);
+void au_hdir_unlock(struct inode *h_dir, struct inode *dir,
+ aufs_bindex_t bindex);
+struct dentry *au_hdir_lock_rename(struct dentry **h_parents,
+ struct inode **dirs, aufs_bindex_t bindex,
+ int issamedir);
+void au_hdir_unlock_rename(struct dentry **h_parents, struct inode **dirs,
+ aufs_bindex_t bindex, int issamedir);
+void au_reset_hinotify(struct inode *inode, unsigned int flags);
+
+void au_hin_ignore(struct au_hinode *hinode, __u32 events);
+void au_hin_unignore(struct au_hinode *hinode, __u32 events);
+
+int __init au_inotify_init(void);
+void au_inotify_fin(void);
+
+#else
+
+static inline
+void au_hin_init(struct au_hinode *hinode, struct au_hinotify *val)
+{
+ /* empty */
+}
+
+static inline
+int au_hin_alloc(struct au_hinode *hinode, struct inode *inode,
+ struct inode *h_inode)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline void au_hin_free(struct au_hinode *hinode)
+{
+ /* nothing */
+}
+
+static inline
+void au_do_hdir_lock(struct inode *h_dir, struct inode *dir,
+ aufs_bindex_t bindex, unsigned int lsc)
+{
+ mutex_lock_nested(&h_dir->i_mutex, lsc);
+}
+
+static inline
+void au_hdir_unlock(struct inode *h_dir, struct inode *dir,
+ aufs_bindex_t bindex)
+{
+ mutex_unlock(&h_dir->i_mutex);
+}
+
+static inline
+struct dentry *au_hdir_lock_rename(struct dentry **h_parents,
+ struct inode **dirs, aufs_bindex_t bindex,
+ int issamedir)
+{
+ return vfsub_lock_rename(h_parents[0], h_parents[1]);
+}
+
+static inline
+void au_hdir_unlock_rename(struct dentry **h_parents, struct inode **dirs,
+ aufs_bindex_t bindex, int issamedir)
+{
+ vfsub_unlock_rename(h_parents[0], h_parents[1]);
+}
+
+static inline void au_reset_hinotify(struct inode *inode, unsigned int flags)
+{
+ /* nothing */
+}
+
+static inline void au_hin_ignore(struct au_hinotify *hinotify, __u32 events)
+{
+ /* nothing */
+}
+
+static inline void au_hin_unignore(struct au_hinotify *hinotify, __u32 events)
+{
+ /* nothing */
+}
+
+static inline int au_inotify_init(void)
+{
+ return 0;
+}
+
+#define au_inotify_fin() do {} while (0)
+#endif /* CONFIG_AUFS_HINOTIFY */
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * au_hdir_lock, au_hdir2_lock
+ */
+#define AuLockFunc(name, lsc) \
+static inline \
+void name##_lock(struct inode *h_dir, struct inode *dir, aufs_bindex_t bindex) \
+{ au_do_hdir_lock(h_dir, dir, bindex, AuLsc_I_##lsc); }
+
+AuLockFunc(au_hdir, PARENT);
+AuLockFunc(au_hdir2, PARENT2);
+
+#undef AuLockFunc
+
+/* ---------------------------------------------------------------------- */
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_HINODE_H__ */
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 52/67] aufs inotify handler
2008-05-16 14:33 ` [PATCH 51/67] aufs lower (branch filesystem) inode and setting inotify hooanon05
@ 2008-05-16 14:33 ` hooanon05
2008-05-16 14:33 ` [PATCH 53/67] aufs sub-routines for vfs in hinotify or dlgt mode hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:33 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs inotify handler
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/hinotify.c | 1114 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 1114 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/hinotify.c b/fs/aufs/hinotify.c
new file mode 100644
index 0000000..28176f6
--- /dev/null
+++ b/fs/aufs/hinotify.c
@@ -0,0 +1,1114 @@
+/*
+ * Copyright (C) 2006-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * inotify handler
+ *
+ * $Id: hinotify.c,v 1.4 2008/05/04 23:52:19 sfjro Exp $
+ */
+
+#include "aufs.h"
+
+/* inotify events */
+static const __u32 AuInMask = (IN_MOVE | IN_DELETE | IN_CREATE
+ /* | IN_ACCESS */
+ | IN_MODIFY | IN_ATTRIB
+ /* | IN_DELETE_SELF | IN_MOVE_SELF */
+ );
+static struct inotify_handle *in_handle;
+
+/* the size of an array for ignore counter */
+static int au_hin_nignore;
+
+AuCacheFuncs(hinotify, AuCache_HINOTIFY);
+
+int au_hin_alloc(struct au_hinode *hinode, struct inode *inode,
+ struct inode *hidden_inode)
+{
+ int err, i;
+ struct au_hinotify *hin;
+ s32 wd;
+
+ LKTRTrace("i%lu, hi%lu\n", inode->i_ino, hidden_inode->i_ino);
+
+ err = -ENOMEM;
+ hin = au_cache_alloc_hinotify();
+ if (hin) {
+ AuDebugOn(hinode->hi_notify);
+ hinode->hi_notify = hin;
+ hin->hin_aufs_inode = inode;
+ for (i = 0; i < au_hin_nignore; i++)
+ atomic_set(hin->hin_ignore + i, 0);
+
+ inotify_init_watch(&hin->hin_watch);
+ wd = inotify_add_watch(in_handle, &hin->hin_watch, hidden_inode,
+ AuInMask);
+ if (wd >= 0)
+ return 0; /* success */
+
+ err = wd;
+ put_inotify_watch(&hin->hin_watch);
+ au_cache_free_hinotify(hin);
+ hinode->hi_notify = NULL;
+ }
+
+ AuTraceErr(err);
+ return err;
+}
+
+void au_hin_free(struct au_hinode *hinode)
+{
+ int err;
+ struct au_hinotify *hin;
+
+ AuTraceEnter();
+
+ hin = hinode->hi_notify;
+ if (unlikely(hin)) {
+ err = 0;
+ if (atomic_read(&hin->hin_watch.count))
+ err = inotify_rm_watch(in_handle, &hin->hin_watch);
+
+ if (!err) {
+ au_cache_free_hinotify(hin);
+ hinode->hi_notify = NULL;
+ } else
+ AuIOErr1("failed inotify_rm_watch() %d\n", err);
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void ctl_hinotify(struct au_hinode *hinode, const __u32 mask)
+{
+ struct inode *h_inode;
+ struct inotify_watch *watch;
+
+ h_inode = hinode->hi_inode;
+ LKTRTrace("hi%lu, sb %p, 0x%x\n", h_inode->i_ino, h_inode->i_sb, mask);
+ IMustLock(h_inode);
+ if (!hinode->hi_notify)
+ return;
+
+ watch = &hinode->hi_notify->hin_watch;
+#if 0 // temp
+ {
+ u32 wd;
+ wd = inotify_find_update_watch(in_handle, h_inode, mask);
+ AuTraceErr(wd);
+ /* ignore an err; */
+ }
+#else
+ /* struct inotify_handle is hidden */
+ mutex_lock(&h_inode->inotify_mutex);
+ //mutex_lock(&watch->ih->mutex);
+ watch->mask = mask;
+ //mutex_unlock(&watch->ih->mutex);
+ mutex_unlock(&h_inode->inotify_mutex);
+#endif
+ LKTRTrace("watch %p, mask %u\n", watch, watch->mask);
+}
+
+#define suspend_hinotify(hi) ctl_hinotify(hi, 0)
+#define resume_hinotify(hi) ctl_hinotify(hi, AuInMask)
+
+void au_do_hdir_lock(struct inode *h_dir, struct inode *dir,
+ aufs_bindex_t bindex, unsigned int lsc)
+{
+ struct au_hinode *hinode;
+
+ LKTRTrace("i%lu, b%d, lsc %d\n", dir->i_ino, bindex, lsc);
+ AuDebugOn(!S_ISDIR(dir->i_mode));
+ hinode = au_ii(dir)->ii_hinode + bindex;
+ AuDebugOn(h_dir != hinode->hi_inode);
+
+ mutex_lock_nested(&h_dir->i_mutex, lsc);
+ suspend_hinotify(hinode);
+}
+
+void au_hdir_unlock(struct inode *h_dir, struct inode *dir,
+ aufs_bindex_t bindex)
+{
+ struct au_hinode *hinode;
+
+ LKTRTrace("i%lu, b%d\n", dir->i_ino, bindex);
+ AuDebugOn(!S_ISDIR(dir->i_mode));
+ hinode = au_ii(dir)->ii_hinode + bindex;
+ AuDebugOn(h_dir != hinode->hi_inode);
+
+ resume_hinotify(hinode);
+ mutex_unlock(&h_dir->i_mutex);
+}
+
+struct dentry *au_hdir_lock_rename(struct dentry **h_parents,
+ struct inode **dirs, aufs_bindex_t bindex,
+ int issamedir)
+{
+ struct dentry *h_trap;
+ struct au_hinode *hinode;
+
+ LKTRTrace("%.*s, %.*s\n",
+ AuDLNPair(h_parents[0]), AuDLNPair(h_parents[1]));
+
+ h_trap = vfsub_lock_rename(h_parents[0], h_parents[1]);
+ hinode = au_ii(dirs[0])->ii_hinode + bindex;
+ AuDebugOn(h_parents[0]->d_inode != hinode->hi_inode);
+ suspend_hinotify(hinode);
+ if (!issamedir) {
+ hinode = au_ii(dirs[1])->ii_hinode + bindex;
+ AuDebugOn(h_parents[1]->d_inode != hinode->hi_inode);
+ suspend_hinotify(hinode);
+ }
+
+ return h_trap;
+}
+
+void au_hdir_unlock_rename(struct dentry **h_parents, struct inode **dirs,
+ aufs_bindex_t bindex, int issamedir)
+{
+ struct au_hinode *hinode;
+
+ LKTRTrace("%.*s, %.*s\n",
+ AuDLNPair(h_parents[0]), AuDLNPair(h_parents[1]));
+
+ hinode = au_ii(dirs[0])->ii_hinode + bindex;
+ AuDebugOn(h_parents[0]->d_inode != hinode->hi_inode);
+ resume_hinotify(hinode);
+ if (!issamedir) {
+ hinode = au_ii(dirs[1])->ii_hinode + bindex;
+ AuDebugOn(h_parents[1]->d_inode != hinode->hi_inode);
+ resume_hinotify(hinode);
+ }
+ vfsub_unlock_rename(h_parents[0], h_parents[1]);
+}
+
+void au_reset_hinotify(struct inode *inode, unsigned int flags)
+{
+ aufs_bindex_t bindex, bend;
+ struct inode *hi;
+ struct dentry *iwhdentry;
+
+ LKTRTrace("i%lu, 0x%x\n", inode->i_ino, flags);
+
+ bend = au_ibend(inode);
+ for (bindex = au_ibstart(inode); bindex <= bend; bindex++) {
+ hi = au_h_iptr(inode, bindex);
+ if (hi) {
+ //mutex_lock_nested(&hi->i_mutex, AuLsc_I_CHILD);
+ iwhdentry = au_hi_wh(inode, bindex);
+ if (unlikely(iwhdentry))
+ dget(iwhdentry);
+ igrab(hi);
+ au_set_h_iptr(inode, bindex, NULL, 0);
+ au_set_h_iptr(inode, bindex, igrab(hi),
+ flags & ~AuHi_XINO);
+ iput(hi);
+ dput(iwhdentry);
+ //mutex_unlock(&hi->i_mutex);
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+
+/* cf. fsnotify_change() */
+__u32 vfsub_events_notify_change(struct iattr *ia)
+{
+ __u32 events;
+ const unsigned int amtime = (ATTR_ATIME | ATTR_MTIME);
+
+ events = 0;
+ if ((ia->ia_valid & (ATTR_UID | ATTR_GID | ATTR_MODE))
+ || (ia->ia_valid & amtime) == amtime)
+ events |= IN_ATTRIB;
+ if ((ia->ia_valid & ATTR_SIZE)
+ || (ia->ia_valid & amtime) == ATTR_MTIME)
+ events |= IN_MODIFY;
+ return events;
+}
+
+void vfsub_ign_hinode(struct vfsub_args *vargs, __u32 events,
+ struct au_hinode *hinode)
+{
+ struct au_hin_ignore *ign;
+
+ AuDebugOn(!hinode);
+
+ ign = vargs->ignore + vargs->nignore++;
+ ign->ign_events = events;
+ ign->ign_hinode = hinode;
+}
+
+void vfsub_ignore(struct vfsub_args *vargs)
+{
+ int n;
+ struct au_hin_ignore *ign;
+
+ n = vargs->nignore;
+ ign = vargs->ignore;
+ while (n-- > 0) {
+ au_hin_ignore(ign->ign_hinode, ign->ign_events);
+ ign++;
+ }
+}
+
+void vfsub_unignore(struct vfsub_args *vargs)
+{
+ int n;
+ struct au_hin_ignore *ign;
+
+ n = vargs->nignore;
+ ign = vargs->ignore;
+ while (n-- > 0) {
+ au_hin_unignore(ign->ign_hinode, ign->ign_events);
+ ign++;
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+
+void au_hin_ignore(struct au_hinode *hinode, __u32 events)
+{
+ int i;
+ atomic_t *ign;
+
+ LKTRTrace("0x%x\n", events);
+ AuDebugOn(!hinode || !events);
+ if (unlikely(!hinode->hi_inode || !hinode->hi_notify)) {
+ /*
+ * it may happen by this scenario.
+ * - a file and its parent dir exist on two branches
+ * - a file on the upper branch is opened
+ * - the parent dir and the file are removed by udba
+ * - the parent is re-accessed, and new dentry/inode in
+ * aufs is generated for it, based upon the one on the lower
+ * branch
+ * - the opened file is re-accessed, re-validated, and it may be
+ * re-connected to the new parent dentry
+ * it means the file in aufs cannot get the actual removed
+ * parent dir on the branch.
+ */
+ return;
+ }
+ LKTRTrace("hi%lu\n", hinode->hi_inode->i_ino);
+#ifdef DbgInotify
+ AuDbg("hi%lu, 0x%x\n", hinode->hi_inode->i_ino, events);
+#endif
+ AuDebugOn(!hinode->hi_notify);
+
+ ign = hinode->hi_notify->hin_ignore;
+ for (i = 0; i < au_hin_nignore; i++)
+ if (1U << i & events)
+ atomic_inc_return(ign + i);
+}
+
+void au_hin_unignore(struct au_hinode *hinode, __u32 events)
+{
+ int i;
+ atomic_t *ign;
+
+ LKTRTrace("0x%x\n", events);
+ AuDebugOn(!hinode || !events);
+ if (unlikely(!hinode->hi_inode || !hinode->hi_notify))
+ return;
+ LKTRTrace("hi%lu\n", hinode->hi_inode->i_ino);
+#ifdef DbgInotify
+ AuDbg("hi%lu, 0x%x\n", hinode->hi_inode->i_ino, events);
+#endif
+ AuDebugOn(!hinode->hi_notify);
+
+ ign = hinode->hi_notify->hin_ignore;
+ for (i = 0; i < au_hin_nignore; i++)
+ if (1U << i & events)
+ atomic_dec_return(ign + i);
+}
+
+/* ---------------------------------------------------------------------- */
+
+static char *in_name(u32 mask)
+{
+#ifdef CONFIG_AUFS_DEBUG
+#define test_ret(flag) if (mask & flag) return #flag;
+ test_ret(IN_ACCESS);
+ test_ret(IN_MODIFY);
+ test_ret(IN_ATTRIB);
+ test_ret(IN_CLOSE_WRITE);
+ test_ret(IN_CLOSE_NOWRITE);
+ test_ret(IN_OPEN);
+ test_ret(IN_MOVED_FROM);
+ test_ret(IN_MOVED_TO);
+ test_ret(IN_CREATE);
+ test_ret(IN_DELETE);
+ test_ret(IN_DELETE_SELF);
+ test_ret(IN_MOVE_SELF);
+ test_ret(IN_UNMOUNT);
+ test_ret(IN_Q_OVERFLOW);
+ test_ret(IN_IGNORED);
+ return "";
+#undef test_ret
+#else
+ return "??";
+#endif
+}
+
+/* ---------------------------------------------------------------------- */
+
+static struct dentry *lookup_wlock_by_name(char *name, unsigned int nlen,
+ struct inode *dir)
+{
+ struct dentry *dentry, *d, *parent;
+ struct qstr *dname;
+
+ LKTRTrace("%.*s, dir%lu\n", nlen, name, dir->i_ino);
+
+ parent = d_find_alias(dir);
+ if (!parent)
+ return NULL;
+
+ dentry = NULL;
+ spin_lock(&dcache_lock);
+ list_for_each_entry(d, &parent->d_subdirs, d_u.d_child) {
+ LKTRTrace("%.*s\n", AuDLNPair(d));
+ dname = &d->d_name;
+ if (dname->len != nlen || memcmp(dname->name, name, nlen))
+ continue;
+ if (!atomic_read(&d->d_count)) {
+ spin_lock(&d->d_lock);
+ __d_drop(d);
+ spin_unlock(&d->d_lock);
+ continue;
+ }
+
+ dentry = dget(d);
+ break;
+ }
+ spin_unlock(&dcache_lock);
+ dput(parent);
+
+ if (dentry)
+ di_write_lock_child(dentry);
+ return dentry;
+}
+
+static struct inode *lookup_wlock_by_ino(struct super_block *sb,
+ aufs_bindex_t bindex, ino_t h_ino)
+{
+ struct inode *inode;
+ struct au_xino_entry xinoe;
+ int err;
+
+ LKTRTrace("b%d, hi%lu\n", bindex, h_ino);
+ AuDebugOn(!au_opt_test(au_mntflags(sb), XINO));
+
+ inode = NULL;
+ err = au_xino_read(sb, bindex, h_ino, &xinoe);
+ if (!err && xinoe.ino)
+ inode = ilookup(sb, xinoe.ino);
+ if (!inode)
+ goto out;
+ if (unlikely(inode->i_ino == AUFS_ROOT_INO)) {
+ AuWarn("wrong root branch\n");
+ iput(inode);
+ inode = NULL;
+ goto out;
+ }
+
+ ii_write_lock_child(inode);
+#if 0 // debug
+ if (au_iigen(inode) == au_sigen(sb))
+ goto out; /* success */
+
+ err = au_refresh_hinode_self(inode);
+ if (!err)
+ goto out; /* success */
+
+ AuIOErr1("err %d ignored, but ino will be broken\n", err);
+ ii_write_unlock(inode);
+ iput(inode);
+ inode = NULL;
+#endif
+
+ out:
+ return inode;
+}
+
+static int hin_xino(struct inode *inode, struct inode *h_inode)
+{
+ int err;
+ aufs_bindex_t bindex, bend, bfound, bstart;
+ struct inode *h_i;
+
+ LKTRTrace("i%lu, hi%lu\n", inode->i_ino, h_inode->i_ino);
+
+ err = 0;
+ if (unlikely(inode->i_ino == AUFS_ROOT_INO)) {
+ AuWarn("branch root dir was changed\n");
+ goto out;
+ }
+
+ bfound = -1;
+ bend = au_ibend(inode);
+ bstart = au_ibstart(inode);
+#if 0
+ if (bindex == bend) {
+ /* keep this ino in rename case */
+ goto out;
+ }
+#endif
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ if (au_h_iptr(inode, bindex) == h_inode) {
+ bfound = bindex;
+ break;
+ }
+ }
+ if (bfound < 0)
+ goto out;
+
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ h_i = au_h_iptr(inode, bindex);
+ if (h_i)
+ err = au_xino_write0(inode->i_sb, bindex, h_i->i_ino,
+ 0);
+ /* ignore this error */
+ /* bad action? */
+ }
+
+ /* children inode number will be broken */
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+static int hin_gen_tree(struct dentry *dentry)
+{
+ int err, i, j, ndentry;
+ struct au_dcsub_pages dpages;
+ struct au_dpage *dpage;
+ struct dentry **dentries;
+
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+
+ err = au_dpages_init(&dpages, GFP_TEMPORARY);
+ if (unlikely(err))
+ goto out;
+ err = au_dcsub_pages(&dpages, dentry, NULL, NULL);
+ if (unlikely(err))
+ goto out_dpages;
+
+ for (i = 0; i < dpages.ndpage; i++) {
+ dpage = dpages.dpages + i;
+ dentries = dpage->dentries;
+ ndentry = dpage->ndentry;
+ for (j = 0; j < ndentry; j++) {
+ struct dentry *d;
+ d = dentries[j];
+ LKTRTrace("%.*s\n", AuDLNPair(d));
+ if (IS_ROOT(d))
+ continue;
+
+ d_drop(d);
+ au_digen_dec(d);
+ if (d->d_inode)
+ //reset children xino? cached children only?
+ au_iigen_dec(d->d_inode);
+ }
+ }
+
+ out_dpages:
+ au_dpages_free(&dpages);
+
+ /* discard children */
+ dentry_unhash(dentry);
+ dput(dentry);
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+/*
+ * return 0 if processed.
+ */
+static int hin_gen_by_inode(char *name, unsigned int nlen, struct inode *inode,
+ const unsigned int isdir)
+{
+ int err;
+ struct dentry *d;
+ struct qstr *dname;
+
+ LKTRTrace("%.*s, i%lu\n", nlen, name, inode->i_ino);
+
+ err = 1;
+ if (unlikely(inode->i_ino == AUFS_ROOT_INO)) {
+ AuWarn("branch root dir was changed\n");
+ err = 0;
+ goto out;
+ }
+
+ if (!isdir) {
+ AuDebugOn(!name);
+ au_iigen_dec(inode);
+ spin_lock(&dcache_lock);
+ list_for_each_entry(d, &inode->i_dentry, d_alias) {
+ dname = &d->d_name;
+ if (dname->len != nlen
+ && memcmp(dname->name, name, nlen))
+ continue;
+ err = 0;
+ spin_lock(&d->d_lock);
+ __d_drop(d);
+ au_digen_dec(d);
+ spin_unlock(&d->d_lock);
+ break;
+ }
+ spin_unlock(&dcache_lock);
+ } else {
+ au_fset_si(au_sbi(inode->i_sb), FAILED_REFRESH_DIRS);
+ d = d_find_alias(inode);
+ if (!d) {
+ au_iigen_dec(inode);
+ goto out;
+ }
+
+ dname = &d->d_name;
+ if (dname->len == nlen && !memcmp(dname->name, name, nlen))
+ err = hin_gen_tree(d);
+ dput(d);
+ }
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+static int hin_gen_by_name(struct dentry *dentry, const unsigned int isdir)
+{
+ int err;
+ struct inode *inode;
+
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+
+ inode = dentry->d_inode;
+ if (IS_ROOT(dentry)
+ //|| (inode && inode->i_ino == AUFS_ROOT_INO)
+ ) {
+ AuWarn("branch root dir was changed\n");
+ return 0;
+ }
+
+ err = 0;
+ if (!isdir) {
+ d_drop(dentry);
+ au_digen_dec(dentry);
+ if (inode)
+ au_iigen_dec(inode);
+ } else {
+ au_fset_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIRS);
+ if (inode)
+ err = hin_gen_tree(dentry);
+ }
+
+ AuTraceErr(err);
+ return err;
+}
+
+static void hin_attr(struct inode *inode, struct inode *h_inode)
+{
+ struct dentry *h_dentry;
+
+ LKTRTrace("i%lu, hi%lu\n", inode->i_ino, h_inode->i_ino);
+
+ if (au_h_iptr(inode, au_ibstart(inode)) != h_inode)
+ return;
+
+ h_dentry = d_find_alias(h_inode);
+ if (h_dentry) {
+ au_update_fuse_h_inode(NULL, h_dentry);
+ /* ignore an error*/
+ dput(h_dentry);
+ }
+
+ au_cpup_attr_all(inode);
+}
+
+/* ---------------------------------------------------------------------- */
+
+/* hinotify job flags */
+#define AuHinJob_XINO0 1
+#define AuHinJob_GEN (1 << 1)
+#define AuHinJob_DIRENT (1 << 2)
+#define AuHinJob_ATTR (1 << 3)
+#define AuHinJob_ISDIR (1 << 4)
+#define AuHinJob_TRYXINO0 (1 << 5)
+#define AuHinJob_MNTPNT (1 << 6)
+#define au_ftest_hinjob(flags, name) ((flags) & AuHinJob_##name)
+#define au_fset_hinjob(flags, name) { (flags) |= AuHinJob_##name; }
+#define au_fclr_hinjob(flags, name) { (flags) &= ~AuHinJob_##name; }
+
+struct hin_job_args {
+ unsigned int flags;
+ struct inode *inode, *h_inode, *dir, *h_dir;
+ struct dentry *dentry;
+ char *h_name;
+ int h_nlen;
+};
+
+static int hin_job(struct hin_job_args *a)
+{
+ const unsigned int isdir = au_ftest_hinjob(a->flags, ISDIR);
+
+ /* reset xino */
+ if (au_ftest_hinjob(a->flags, XINO0) && a->inode)
+ hin_xino(a->inode, a->h_inode);
+ /* ignore this error */
+
+ if (au_ftest_hinjob(a->flags, TRYXINO0)
+ && a->inode
+ && a->h_inode) {
+ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD);
+#if 0
+ au_debug_on();
+ AuDbgInode(a->inode);
+ AuDbgInode(a->h_inode);
+ au_debug_off();
+#endif
+ if (!a->h_inode->i_nlink)
+ hin_xino(a->inode, a->h_inode);
+ /* ignore this error */
+ mutex_unlock(&a->h_inode->i_mutex);
+ }
+
+ /* make the generation obsolete */
+ if (au_ftest_hinjob(a->flags, GEN)) {
+ int err = -1;
+ if (a->inode)
+ err = hin_gen_by_inode(a->h_name, a->h_nlen, a->inode,
+ isdir);
+ if (err && a->dentry)
+ hin_gen_by_name(a->dentry, isdir);
+ /* ignore this error */
+ }
+
+ /* make dir entries obsolete */
+ if (au_ftest_hinjob(a->flags, DIRENT) && a->inode) {
+ struct au_vdir *vdir;
+ IiMustWriteLock(a->inode);
+ vdir = au_ivdir(a->inode);
+ if (vdir)
+ vdir->vd_jiffy = 0;
+ //IMustLock(a->inode);
+ //a->inode->i_version++;
+ }
+
+ /* update the attr */
+ if (au_ftest_hinjob(a->flags, ATTR) && a->inode && a->h_inode)
+ hin_attr(a->inode, a->h_inode);
+
+ /* can do nothing but warn */
+ if (au_ftest_hinjob(a->flags, MNTPNT)
+ && a->dentry
+ && d_mountpoint(a->dentry))
+ AuWarn("mount-point %.*s is removed or renamed\n",
+ AuDLNPair(a->dentry));
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+enum { CHILD, PARENT };
+struct postproc_args {
+ struct inode *h_dir, *dir, *h_child_inode;
+ u32 mask;
+ unsigned int flags[2];
+ unsigned int h_child_nlen;
+ char h_child_name[];
+};
+
+static void postproc(void *_args)
+{
+ struct postproc_args *a = _args;
+ struct super_block *sb;
+ aufs_bindex_t bindex, bend, bfound;
+ int xino, err;
+ struct inode *inode;
+ ino_t h_ino;
+ struct hin_job_args args;
+ struct dentry *dentry;
+ struct au_sbinfo *sbinfo;
+
+ AuDebugOn(!_args);
+ AuDebugOn(!a->h_dir);
+ AuDebugOn(!a->dir);
+ AuDebugOn(!a->mask);
+ //au_debug_on();
+ LKTRTrace("mask 0x%x %s, i%lu, hi%lu, hci%lu\n",
+ a->mask, in_name(a->mask), a->dir->i_ino, a->h_dir->i_ino,
+ a->h_child_inode ? a->h_child_inode->i_ino : 0);
+
+ inode = NULL;
+ dentry = NULL;
+ // do not lock here because of d_revalidate() may cause a deadlock.
+ //mutex_lock(&a->dir->i_mutex);
+ sb = a->dir->i_sb;
+ AuDebugOn(!sb);
+ sbinfo = au_sbi(sb);
+ AuDebugOn(!sbinfo);
+ /* big aufs lock */
+ si_noflush_write_lock(sb);
+
+ ii_read_lock_parent(a->dir);
+ bfound = -1;
+ bend = au_ibend(a->dir);
+ for (bindex = au_ibstart(a->dir); bindex <= bend; bindex++)
+ if (au_h_iptr(a->dir, bindex) == a->h_dir) {
+ bfound = bindex;
+ break;
+ }
+ ii_read_unlock(a->dir);
+ if (unlikely(bfound < 0))
+ goto out;
+
+ xino = !!au_opt_test(au_mntflags(sb), XINO);
+ h_ino = 0;
+ if (a->h_child_inode)
+ h_ino = a->h_child_inode->i_ino;
+ //AuDbg("here\n");
+
+ if (a->h_child_nlen
+ && (au_ftest_hinjob(a->flags[CHILD], GEN)
+ || au_ftest_hinjob(a->flags[CHILD], MNTPNT)))
+ dentry = lookup_wlock_by_name(a->h_child_name, a->h_child_nlen,
+ a->dir);
+ //AuDbg("here\n");
+ if (dentry)
+ inode = dentry->d_inode;
+ if (xino && !inode && h_ino
+ && (au_ftest_hinjob(a->flags[CHILD], XINO0)
+ || au_ftest_hinjob(a->flags[CHILD], TRYXINO0)
+ || au_ftest_hinjob(a->flags[CHILD], GEN)
+ || au_ftest_hinjob(a->flags[CHILD], ATTR)))
+ inode = lookup_wlock_by_ino(sb, bfound, h_ino);
+ //AuDbg("here\n");
+
+ args.flags = a->flags[CHILD];
+ args.dentry = dentry;
+ args.inode = inode;
+ args.h_inode = a->h_child_inode;
+ args.dir = a->dir;
+ args.h_dir = a->h_dir;
+ args.h_name = a->h_child_name;
+ args.h_nlen = a->h_child_nlen;
+ err = hin_job(&args);
+ if (dentry) {
+ di_write_unlock(dentry);
+ dput(dentry);
+ } else if (inode) {
+ ii_write_unlock(inode);
+ iput(inode);
+ }
+ //AuDbg("here\n");
+
+ ii_write_lock_parent(a->dir);
+ args.flags = a->flags[PARENT];
+ args.dentry = NULL;
+ args.inode = a->dir;
+ args.h_inode = a->h_dir;
+ args.dir = NULL;
+ args.h_dir = NULL;
+ args.h_name = NULL;
+ args.h_nlen = 0;
+ err = hin_job(&args);
+ ii_write_unlock(a->dir);
+ //AuDbg("here\n");
+
+ out:
+ si_write_unlock(sb);
+ //mutex_unlock(&a->dir->i_mutex);
+ au_nwt_dec(&sbinfo->si_nowait);
+
+ iput(a->h_child_inode);
+ iput(a->h_dir);
+ iput(a->dir);
+ kfree(a);
+ //au_debug_off();
+}
+
+//todo: endian?
+#ifndef ilog2
+#define ilog2(n) ffz(~(n))
+#endif
+
+static void aufs_inotify(struct inotify_watch *watch, u32 wd, u32 mask,
+ u32 cookie, const char *h_child_name,
+ struct inode *h_child_inode)
+{
+ struct au_hinotify *hinotify;
+ struct postproc_args *args;
+ int len, wkq_err, isdir, isroot, wh, idx;
+ char *p;
+ struct inode *dir;
+ unsigned int flags[2];
+ struct super_block *sb;
+ atomic_t *cnt;
+
+ LKTRTrace("i%lu, wd %d, mask 0x%x %s, cookie 0x%x, hcname %s, hi%lu\n",
+ watch->inode->i_ino, wd, mask, in_name(mask), cookie,
+ h_child_name ? h_child_name : "",
+ h_child_inode ? h_child_inode->i_ino : 0);
+#if 0 //defined(ForceInotify) || defined(DbgInotify)
+ AuDbg("i%lu, wd %d, mask 0x%x %s, cookie 0x%x, hcname %s, hi%lu\n",
+ watch->inode->i_ino, wd, mask, in_name(mask), cookie,
+ h_child_name ? h_child_name : "",
+ h_child_inode ? h_child_inode->i_ino : 0);
+#endif
+ /* if IN_UNMOUNT happens, there must be another bug */
+ if (mask & (IN_IGNORED | IN_UNMOUNT)) {
+ //WARN_ON(watch->inode->i_ino == 15);
+ put_inotify_watch(watch);
+ return;
+ }
+
+#ifdef DbgInotify
+ if (!h_child_name || strcmp(h_child_name, AUFS_XINO_FNAME))
+ AuDbg("i%lu, wd %d, mask 0x%x %s, cookie 0x%x, hcname %s,"
+ " hi%lu\n",
+ watch->inode->i_ino, wd, mask, in_name(mask), cookie,
+ h_child_name ? h_child_name : "",
+ h_child_inode ? h_child_inode->i_ino : 0);
+ //WARN_ON(1);
+#endif
+
+ hinotify = container_of(watch, struct au_hinotify, hin_watch);
+ AuDebugOn(!hinotify || !hinotify->hin_aufs_inode);
+ idx = ilog2(mask & IN_ALL_EVENTS);
+ AuDebugOn(au_hin_nignore <= idx);
+ cnt = hinotify->hin_ignore + idx;
+ if (0 <= atomic_dec_return(cnt))
+ return;
+ atomic_inc_return(cnt);
+#ifdef DbgInotify
+#if 0
+ AuDbg("i%lu, wd %d, mask 0x%x %s, cookie 0x%x, hcname %s, hi%lu\n",
+ watch->inode->i_ino, wd, mask, in_name(mask), cookie,
+ h_child_name ? h_child_name : "",
+ h_child_inode ? h_child_inode->i_ino : 0);
+#endif
+#if 0
+ if (!h_child_name || strcmp(h_child_name, AUFS_XINO_FNAME))
+ WARN_ON(1);
+#endif
+#endif
+
+ dir = igrab(hinotify->hin_aufs_inode);
+ if (!dir)
+ return;
+ isroot = (dir->i_ino == AUFS_ROOT_INO);
+ len = 0;
+ wh = 0;
+ if (h_child_name) {
+ len = strlen(h_child_name);
+ if (!memcmp(h_child_name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) {
+ h_child_name += AUFS_WH_PFX_LEN;
+ len -= AUFS_WH_PFX_LEN;
+ wh = 1;
+ }
+ }
+
+ isdir = 0;
+ if (h_child_inode)
+ isdir = !!S_ISDIR(h_child_inode->i_mode);
+ flags[PARENT] = AuHinJob_ISDIR;
+ flags[CHILD] = 0;
+ if (isdir)
+ flags[CHILD] = AuHinJob_ISDIR;
+ switch (mask & IN_ALL_EVENTS) {
+ case IN_MODIFY:
+ /*FALLTHROUGH*/
+ case IN_ATTRIB:
+ if (h_child_inode) {
+ if (!wh)
+ au_fset_hinjob(flags[CHILD], ATTR);
+ } else
+ au_fset_hinjob(flags[PARENT], ATTR);
+ break;
+
+ /* IN_MOVED_FROM is the first event in rename(2) */
+ case IN_MOVED_FROM:
+ case IN_MOVED_TO:
+ AuDebugOn(!h_child_name || !h_child_inode);
+ au_fset_hinjob(flags[CHILD], GEN);
+ au_fset_hinjob(flags[CHILD], ATTR);
+ if (1 || isdir)
+ au_fset_hinjob(flags[CHILD], XINO0);
+ au_fset_hinjob(flags[CHILD], MNTPNT);
+
+ au_fset_hinjob(flags[PARENT], ATTR);
+ au_fset_hinjob(flags[PARENT], DIRENT);
+ break;
+
+ case IN_CREATE:
+ AuDebugOn(!h_child_name || !h_child_inode);
+ au_fset_hinjob(flags[PARENT], ATTR);
+ au_fset_hinjob(flags[PARENT], DIRENT);
+ au_fset_hinjob(flags[CHILD], GEN);
+ /* hard link */
+ if (!isdir && h_child_inode->i_nlink > 1)
+ au_fset_hinjob(flags[CHILD], ATTR);
+ break;
+
+ case IN_DELETE:
+ /*
+ * aufs never be able to get this child inode.
+ * revalidation should be in d_revalidate()
+ * by checking i_nlink, i_generation or d_unhashed().
+ */
+ AuDebugOn(!h_child_name);
+ au_fset_hinjob(flags[PARENT], ATTR);
+ au_fset_hinjob(flags[PARENT], DIRENT);
+ au_fset_hinjob(flags[CHILD], GEN);
+ au_fset_hinjob(flags[CHILD], TRYXINO0);
+ au_fset_hinjob(flags[CHILD], MNTPNT);
+ break;
+
+ case IN_DELETE_SELF:
+ if (!isroot)
+ au_fset_hinjob(flags[PARENT], GEN);
+ /*FALLTHROUGH*/
+
+ case IN_MOVE_SELF:
+ /*
+ * when an inotify is set to an aufs inode,
+ * such inode can be isolated and this event can be fired
+ * solely.
+ */
+ AuDebugOn(h_child_name || h_child_inode);
+ if (unlikely(isroot)) {
+ AuWarn("root branch was moved\n");
+ iput(dir);
+ return;
+ }
+#if 0
+ return;
+#else
+ au_fset_hinjob(flags[PARENT], XINO0);
+ au_fset_hinjob(flags[PARENT], GEN);
+ au_fset_hinjob(flags[PARENT], ATTR);
+ au_fset_hinjob(flags[PARENT], DIRENT);
+ //au_fset_hinjob(flags[PARENT], MNTPNT);
+ break;
+#endif
+ case IN_ACCESS:
+ default:
+ AuDebugOn(1);
+ }
+
+#if 0 //def DbgInotify
+ WARN_ON(1);
+#endif
+
+ if (wh)
+ h_child_inode = NULL;
+
+ /* iput() and kfree() will be called in postproc() */
+ /*
+ * inotify_mutex is already acquired and kmalloc/prune_icache may lock
+ * iprune_mutex. strange.
+ */
+ lockdep_off();
+ args = kmalloc(sizeof(*args) + len + 1, GFP_TEMPORARY);
+ lockdep_on();
+ if (unlikely(!args)) {
+ AuErr1("no memory\n");
+ iput(dir);
+ return;
+ }
+ args->flags[PARENT] = flags[PARENT];
+ args->flags[CHILD] = flags[CHILD];
+ args->mask = mask;
+ args->dir = dir;
+ args->h_dir = igrab(watch->inode);
+ if (h_child_inode)
+ igrab(h_child_inode);
+ args->h_child_inode = h_child_inode;
+ args->h_child_nlen = len;
+ if (len) {
+ p = (void *)args;
+ p += sizeof(*args);
+ memcpy(p, h_child_name, len + 1);
+ }
+
+ sb = dir->i_sb;
+ au_nwt_inc(&au_sbi(sb)->si_nowait);
+ lockdep_off();
+ wkq_err = au_wkq_nowait(postproc, args, sb, /*dlgt*/0);
+ lockdep_on();
+ if (unlikely(wkq_err)) {
+ AuErr("wkq %d\n", wkq_err);
+ au_nwt_dec(&au_sbi(sb)->si_nowait);
+ }
+}
+
+static void aufs_inotify_destroy(struct inotify_watch *watch)
+{
+ return;
+}
+
+static struct inotify_operations aufs_inotify_ops = {
+ .handle_event = aufs_inotify,
+ .destroy_watch = aufs_inotify_destroy
+};
+
+/* ---------------------------------------------------------------------- */
+
+static void au_hin_destroy_cache(void)
+{
+ kmem_cache_destroy(au_cachep[AuCache_HINOTIFY]);
+ au_cachep[AuCache_HINOTIFY] = NULL;
+}
+
+int __init au_inotify_init(void)
+{
+ au_hin_nignore = 6;
+ while (1U << au_hin_nignore < AuInMask)
+ au_hin_nignore++;
+ //AuDbg("au_hin_nignore %d\n", au_hin_nignore);
+ AuDebugOn(au_hin_nignore != 10);
+
+ in_handle = ERR_PTR(-ENOMEM);
+ au_cachep[AuCache_HINOTIFY]
+ = AuCacheX(au_hinotify, sizeof(atomic_t) * au_hin_nignore);
+ if (unlikely(!au_cachep[AuCache_HINOTIFY]))
+ goto out;
+
+ in_handle = inotify_init(&aufs_inotify_ops);
+ if (!IS_ERR(in_handle))
+ return 0;
+
+ au_hin_destroy_cache();
+ out:
+ AuTraceErrPtr(in_handle);
+ return PTR_ERR(in_handle);
+}
+
+void au_inotify_fin(void)
+{
+ inotify_destroy(in_handle);
+ if (au_cachep[AuCache_HINOTIFY])
+ au_hin_destroy_cache();
+}
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 53/67] aufs sub-routines for vfs in hinotify or dlgt mode
2008-05-16 14:33 ` [PATCH 52/67] aufs inotify handler hooanon05
@ 2008-05-16 14:33 ` hooanon05
2008-05-16 14:33 ` [PATCH 54/67] aufs lookup functions in 'delegate' mode hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:33 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs sub-routines for vfs in hinotify or dlgt mode
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/hin_or_dlgt.c | 708 +++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 708 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/hin_or_dlgt.c b/fs/aufs/hin_or_dlgt.c
new file mode 100644
index 0000000..ad0a2fc
--- /dev/null
+++ b/fs/aufs/hin_or_dlgt.c
@@ -0,0 +1,708 @@
+/*
+ * Copyright (C) 2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * sub-routines for vfs in hinotify or dlgt mode
+ *
+ * $Id: hin_or_dlgt.c,v 1.2 2008/04/21 02:00:37 sfjro Exp $
+ */
+// I'm going to slightly mad
+
+#include <linux/uaccess.h>
+#include "aufs.h"
+
+#if !defined(CONFIG_AUFS_HINOTIFY) && !defined(CONFIG_AUFS_DLGT)
+#error mis-configuraion or Makefile
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+struct permission_args {
+ int *errp;
+ struct inode *inode;
+ int mask;
+ struct nameidata *nd;
+};
+
+static void call_permission(void *args)
+{
+ struct permission_args *a = args;
+ *a->errp = do_vfsub_permission(a->inode, a->mask, a->nd);
+}
+
+int vfsub_permission(struct inode *inode, int mask, struct nameidata *nd,
+ int dlgt)
+{
+ if (!dlgt)
+ return do_vfsub_permission(inode, mask, nd);
+ else {
+ int err, wkq_err;
+ struct permission_args args = {
+ .errp = &err,
+ .inode = inode,
+ .mask = mask,
+ .nd = nd
+ };
+ wkq_err = au_wkq_wait(call_permission, &args, /*dlgt*/1);
+ if (unlikely(wkq_err))
+ err = wkq_err;
+ return err;
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+
+struct create_args {
+ int *errp;
+ struct inode *dir;
+ struct dentry *dentry;
+ int mode;
+ struct nameidata *nd;
+};
+
+static void call_create(void *args)
+{
+ struct create_args *a = args;
+ *a->errp = do_vfsub_create(a->dir, a->dentry, a->mode, a->nd);
+}
+
+int vfsub_create(struct inode *dir, struct dentry *dentry, int mode,
+ struct nameidata *nd, int dlgt)
+{
+ if (!dlgt)
+ return do_vfsub_create(dir, dentry, mode, nd);
+ else {
+ int err, wkq_err;
+ struct create_args args = {
+ .errp = &err,
+ .dir = dir,
+ .dentry = dentry,
+ .mode = mode,
+ .nd = nd
+ };
+ wkq_err = au_wkq_wait(call_create, &args, /*dlgt*/1);
+ if (unlikely(wkq_err))
+ err = wkq_err;
+ return err;
+ }
+}
+
+struct symlink_args {
+ int *errp;
+ struct inode *dir;
+ struct dentry *dentry;
+ const char *symname;
+ int mode;
+};
+
+static void call_symlink(void *args)
+{
+ struct symlink_args *a = args;
+ *a->errp = do_vfsub_symlink(a->dir, a->dentry, a->symname, a->mode);
+}
+
+int vfsub_symlink(struct inode *dir, struct dentry *dentry, const char *symname,
+ int mode, int dlgt)
+{
+ if (!dlgt)
+ return do_vfsub_symlink(dir, dentry, symname, mode);
+ else {
+ int err, wkq_err;
+ struct symlink_args args = {
+ .errp = &err,
+ .dir = dir,
+ .dentry = dentry,
+ .symname = symname,
+ .mode = mode
+ };
+ wkq_err = au_wkq_wait(call_symlink, &args, /*dlgt*/1);
+ if (unlikely(wkq_err))
+ err = wkq_err;
+ return err;
+ }
+}
+
+struct mknod_args {
+ int *errp;
+ struct inode *dir;
+ struct dentry *dentry;
+ int mode;
+ dev_t dev;
+};
+
+static void call_mknod(void *args)
+{
+ struct mknod_args *a = args;
+ *a->errp = do_vfsub_mknod(a->dir, a->dentry, a->mode, a->dev);
+}
+
+int vfsub_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev,
+ int dlgt)
+{
+ if (!dlgt)
+ return do_vfsub_mknod(dir, dentry, mode, dev);
+ else {
+ int err, wkq_err;
+ struct mknod_args args = {
+ .errp = &err,
+ .dir = dir,
+ .dentry = dentry,
+ .mode = mode,
+ .dev = dev
+ };
+ wkq_err = au_wkq_wait(call_mknod, &args, /*dlgt*/1);
+ if (unlikely(wkq_err))
+ err = wkq_err;
+ return err;
+ }
+}
+
+struct mkdir_args {
+ int *errp;
+ struct inode *dir;
+ struct dentry *dentry;
+ int mode;
+};
+
+static void call_mkdir(void *args)
+{
+ struct mkdir_args *a = args;
+ *a->errp = do_vfsub_mkdir(a->dir, a->dentry, a->mode);
+}
+
+int vfsub_mkdir(struct inode *dir, struct dentry *dentry, int mode, int dlgt)
+{
+ if (!dlgt)
+ return do_vfsub_mkdir(dir, dentry, mode);
+ else {
+ int err, wkq_err;
+ struct mkdir_args args = {
+ .errp = &err,
+ .dir = dir,
+ .dentry = dentry,
+ .mode = mode
+ };
+ wkq_err = au_wkq_wait(call_mkdir, &args, /*dlgt*/1);
+ if (unlikely(wkq_err))
+ err = wkq_err;
+ return err;
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+
+struct link_args {
+ int *errp;
+ struct inode *dir;
+ struct dentry *src_dentry, *dentry;
+};
+
+static void call_link(void *args)
+{
+ struct link_args *a = args;
+ *a->errp = do_vfsub_link(a->src_dentry, a->dir, a->dentry);
+}
+
+int vfsub_link(struct dentry *src_dentry, struct inode *dir,
+ struct dentry *dentry, int dlgt)
+{
+ if (!dlgt)
+ return do_vfsub_link(src_dentry, dir, dentry);
+ else {
+ int err, wkq_err;
+ struct link_args args = {
+ .errp = &err,
+ .src_dentry = src_dentry,
+ .dir = dir,
+ .dentry = dentry
+ };
+ wkq_err = au_wkq_wait(call_link, &args, /*dlgt*/1);
+ if (unlikely(wkq_err))
+ err = wkq_err;
+ return err;
+ }
+}
+
+struct rename_args {
+ int *errp;
+ struct inode *src_dir, *dir;
+ struct dentry *src_dentry, *dentry;
+ struct vfsub_args *vargs;
+};
+
+static void call_rename(void *args)
+{
+ struct rename_args *a = args;
+ vfsub_ignore(a->vargs);
+ *a->errp = do_vfsub_rename(a->src_dir, a->src_dentry, a->dir,
+ a->dentry);
+ if (unlikely(*a->errp))
+ vfsub_unignore(a->vargs);
+}
+
+int vfsub_rename(struct inode *src_dir, struct dentry *src_dentry,
+ struct inode *dir, struct dentry *dentry,
+ struct vfsub_args *vargs)
+{
+ int err;
+
+ if (!vfsub_ftest(vargs->flags, DLGT)) {
+ vfsub_ignore(vargs);
+ err = do_vfsub_rename(src_dir, src_dentry, dir, dentry);
+ if (unlikely(err))
+ vfsub_unignore(vargs);
+ } else {
+ int wkq_err;
+ struct rename_args args = {
+ .errp = &err,
+ .src_dir = src_dir,
+ .src_dentry = src_dentry,
+ .dir = dir,
+ .dentry = dentry,
+ .vargs = vargs
+ };
+ wkq_err = au_wkq_wait(call_rename, &args, /*dlgt*/1);
+ if (unlikely(wkq_err))
+ err = wkq_err;
+ }
+ return err;
+}
+
+struct rmdir_args {
+ int *errp;
+ struct inode *dir;
+ struct dentry *dentry;
+ struct vfsub_args *vargs;
+};
+
+static void call_rmdir(void *args)
+{
+ struct rmdir_args *a = args;
+ vfsub_ignore(a->vargs);
+ *a->errp = do_vfsub_rmdir(a->dir, a->dentry);
+ if (unlikely(*a->errp))
+ vfsub_unignore(a->vargs);
+}
+
+int vfsub_rmdir(struct inode *dir, struct dentry *dentry,
+ struct vfsub_args *vargs)
+{
+ int err;
+
+ if (!vfsub_ftest(vargs->flags, DLGT)) {
+ vfsub_ignore(vargs);
+ err = do_vfsub_rmdir(dir, dentry);
+ if (unlikely(err))
+ vfsub_unignore(vargs);
+ } else {
+ int wkq_err;
+ struct rmdir_args args = {
+ .errp = &err,
+ .dir = dir,
+ .dentry = dentry,
+ .vargs = vargs
+ };
+ wkq_err = au_wkq_wait(call_rmdir, &args, /*dlgt*/1);
+ if (unlikely(wkq_err))
+ err = wkq_err;
+ }
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+struct read_args {
+ ssize_t *errp;
+ struct file *file;
+ union {
+ void *kbuf;
+ char __user *ubuf;
+ };
+ size_t count;
+ loff_t *ppos;
+};
+
+static void call_read_k(void *args)
+{
+ struct read_args *a = args;
+ LKTRTrace("%.*s, cnt %lu, pos %Ld\n",
+ AuDLNPair(a->file->f_dentry), (unsigned long)a->count,
+ *a->ppos);
+ *a->errp = do_vfsub_read_k(a->file, a->kbuf, a->count, a->ppos);
+}
+
+ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count,
+ loff_t *ppos, int dlgt)
+{
+ if (!dlgt)
+ return do_vfsub_read_u(file, ubuf, count, ppos);
+ else {
+ int wkq_err;
+ ssize_t err, read;
+ struct read_args args = {
+ .errp = &err,
+ .file = file,
+ .count = count,
+ .ppos = ppos
+ };
+
+ if (unlikely(!count))
+ return 0;
+
+ /*
+ * workaround an application bug.
+ * generally, read(2) or write(2) may return the value shorter
+ * than requested. But many applications don't support it,
+ * for example bash.
+ */
+ err = -ENOMEM;
+ if (args.count > PAGE_SIZE)
+ args.count = PAGE_SIZE;
+ args.kbuf = kmalloc(args.count, GFP_TEMPORARY);
+ if (unlikely(!args.kbuf))
+ goto out;
+
+ read = 0;
+ do {
+ wkq_err = au_wkq_wait(call_read_k, &args, /*dlgt*/1);
+ if (unlikely(wkq_err))
+ err = wkq_err;
+ if (unlikely(err > 0
+ && copy_to_user(ubuf, args.kbuf, err))) {
+ err = -EFAULT;
+ goto out_free;
+ } else if (!err)
+ break;
+ else if (unlikely(err < 0))
+ goto out_free;
+ count -= err;
+ /* do not read too much because of file i/o pointer */
+ if (count < args.count)
+ args.count = count;
+ ubuf += err;
+ read += err;
+ } while (count);
+ smp_mb(); /* flush ubuf */
+ err = read;
+
+ out_free:
+ kfree(args.kbuf);
+ out:
+ return err;
+ }
+}
+
+ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count, loff_t *ppos,
+ int dlgt)
+{
+ if (!dlgt)
+ return do_vfsub_read_k(file, kbuf, count, ppos);
+ else {
+ ssize_t err;
+ int wkq_err;
+ struct read_args args = {
+ .errp = &err,
+ .file = file,
+ .count = count,
+ .ppos = ppos
+ };
+ args.kbuf = kbuf;
+ wkq_err = au_wkq_wait(call_read_k, &args, /*dlgt*/1);
+ if (unlikely(wkq_err))
+ err = wkq_err;
+ return err;
+ }
+}
+
+struct write_args {
+ ssize_t *errp;
+ struct file *file;
+ union {
+ void *kbuf;
+ const char __user *ubuf;
+ };
+ void *buf;
+ size_t count;
+ loff_t *ppos;
+ struct vfsub_args *vargs;
+};
+
+static void call_write_k(void *args)
+{
+ struct write_args *a = args;
+ LKTRTrace("%.*s, cnt %lu, pos %Ld\n",
+ AuDLNPair(a->file->f_dentry), (unsigned long)a->count,
+ *a->ppos);
+ vfsub_ignore(a->vargs);
+ *a->errp = do_vfsub_write_k(a->file, a->kbuf, a->count, a->ppos);
+ if (unlikely(*a->errp < 0))
+ vfsub_unignore(a->vargs);
+}
+
+ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count,
+ loff_t *ppos, struct vfsub_args *vargs)
+{
+ ssize_t err;
+
+ if (!vfsub_ftest(vargs->flags, DLGT)) {
+ vfsub_ignore(vargs);
+ err = do_vfsub_write_u(file, ubuf, count, ppos);
+ if (unlikely(err < 0))
+ vfsub_unignore(vargs);
+ } else {
+ ssize_t written;
+ int wkq_err;
+ struct write_args args = {
+ .errp = &err,
+ .file = file,
+ .count = count,
+ .ppos = ppos,
+ .vargs = vargs
+ };
+
+ if (unlikely(!count))
+ return 0;
+
+ /*
+ * workaround an application bug.
+ * generally, read(2) or write(2) may return the value shorter
+ * than requested. But many applications don't support it,
+ * for example bash.
+ */
+ err = -ENOMEM;
+ if (args.count > PAGE_SIZE)
+ args.count = PAGE_SIZE;
+ args.kbuf = kmalloc(args.count, GFP_TEMPORARY);
+ if (unlikely(!args.kbuf))
+ goto out;
+
+ written = 0;
+ do {
+ if (unlikely(copy_from_user(args.kbuf, ubuf,
+ args.count))) {
+ err = -EFAULT;
+ goto out_free;
+ }
+
+ wkq_err = au_wkq_wait(call_write_k, &args, /*dlgt*/1);
+ if (unlikely(wkq_err))
+ err = wkq_err;
+ if (err > 0) {
+ count -= err;
+ if (count < args.count)
+ args.count = count;
+ ubuf += err;
+ written += err;
+ } else if (!err)
+ break;
+ else if (unlikely(err < 0))
+ goto out_free;
+ } while (count);
+ err = written;
+
+ out_free:
+ kfree(args.kbuf);
+ }
+ out:
+ return err;
+}
+
+ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, loff_t *ppos,
+ struct vfsub_args *vargs)
+{
+ ssize_t err;
+
+ if (!vfsub_ftest(vargs->flags, DLGT)) {
+ vfsub_ignore(vargs);
+ err = do_vfsub_write_k(file, kbuf, count, ppos);
+ if (unlikely(err < 0))
+ vfsub_unignore(vargs);
+ } else {
+ int wkq_err;
+ struct write_args args = {
+ .errp = &err,
+ .file = file,
+ .count = count,
+ .ppos = ppos,
+ .vargs = vargs
+ };
+ args.kbuf = kbuf;
+ wkq_err = au_wkq_wait(call_write_k, &args, /*dlgt*/1);
+ if (unlikely(wkq_err))
+ err = wkq_err;
+ }
+ return err;
+}
+
+struct readdir_args {
+ int *errp;
+ struct file *file;
+ filldir_t filldir;
+ void *arg;
+};
+
+static void call_readdir(void *args)
+{
+ struct readdir_args *a = args;
+ *a->errp = do_vfsub_readdir(a->file, a->filldir, a->arg);
+}
+
+int vfsub_readdir(struct file *file, filldir_t filldir, void *arg, int dlgt)
+{
+ if (!dlgt)
+ return do_vfsub_readdir(file, filldir, arg);
+ else {
+ int err, wkq_err;
+ struct readdir_args args = {
+ .errp = &err,
+ .file = file,
+ .filldir = filldir,
+ .arg = arg
+ };
+ wkq_err = au_wkq_wait(call_readdir, &args, /*dlgt*/1);
+ if (unlikely(wkq_err))
+ err = wkq_err;
+ return err;
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+
+struct splice_to_args {
+ long *errp;
+ struct file *in;
+ loff_t *ppos;
+ struct pipe_inode_info *pipe;
+ size_t len;
+ unsigned int flags;
+};
+
+static void call_splice_to(void *args)
+{
+ struct splice_to_args *a = args;
+ *a->errp = do_vfsub_splice_to(a->in, a->ppos, a->pipe, a->len,
+ a->flags);
+}
+
+long vfsub_splice_to(struct file *in, loff_t *ppos,
+ struct pipe_inode_info *pipe, size_t len,
+ unsigned int flags, int dlgt)
+{
+ if (!dlgt)
+ return do_vfsub_splice_to(in, ppos, pipe, len, flags);
+ else {
+ long err;
+ int wkq_err;
+ struct splice_to_args args = {
+ .errp = &err,
+ .in = in,
+ .ppos = ppos,
+ .pipe = pipe,
+ .len = len,
+ .flags = flags
+ };
+ wkq_err = au_wkq_wait(call_splice_to, &args, /*dlgt*/1);
+ if (unlikely(wkq_err))
+ err = wkq_err;
+ return err;
+ }
+}
+
+struct splice_from_args {
+ long *errp;
+ struct pipe_inode_info *pipe;
+ struct file *out;
+ loff_t *ppos;
+ size_t len;
+ unsigned int flags;
+ struct vfsub_args *vargs;
+};
+
+static void call_splice_from(void *args)
+{
+ struct splice_from_args *a = args;
+ vfsub_ignore(a->vargs);
+ *a->errp = do_vfsub_splice_from(a->pipe, a->out, a->ppos, a->len,
+ a->flags);
+ if (unlikely(*a->errp < 0))
+ vfsub_unignore(a->vargs);
+}
+
+long vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out,
+ loff_t *ppos, size_t len, unsigned int flags,
+ struct vfsub_args *vargs)
+{
+ long err;
+
+ if (!vfsub_ftest(vargs->flags, DLGT)) {
+ vfsub_ignore(vargs);
+ err = do_vfsub_splice_from(pipe, out, ppos, len, flags);
+ if (unlikely(err < 0))
+ vfsub_unignore(vargs);
+ } else {
+ int wkq_err;
+ struct splice_from_args args = {
+ .errp = &err,
+ .pipe = pipe,
+ .out = out,
+ .ppos = ppos,
+ .len = len,
+ .flags = flags,
+ .vargs = vargs
+ };
+ wkq_err = au_wkq_wait(call_splice_from, &args, /*dlgt*/1);
+ if (unlikely(wkq_err))
+ err = wkq_err;
+ }
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+struct getattr_args {
+ int *errp;
+ struct vfsmount *mnt;
+ struct dentry *dentry;
+ struct kstat *st;
+};
+
+static void call_getattr(void *args)
+{
+ struct getattr_args *a = args;
+ *a->errp = do_vfsub_getattr(a->mnt, a->dentry, a->st);
+}
+
+int vfsub_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *st,
+ int dlgt)
+{
+ if (!dlgt)
+ return do_vfsub_getattr(mnt, dentry, st);
+ else {
+ int err, wkq_err;
+ struct getattr_args args = {
+ .errp = &err,
+ .mnt = mnt,
+ .dentry = dentry,
+ .st = st
+ };
+ wkq_err = au_wkq_wait(call_getattr, &args, /*dlgt*/1);
+ if (unlikely(wkq_err))
+ err = wkq_err;
+ return err;
+ }
+}
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 54/67] aufs lookup functions in 'delegate' mode
2008-05-16 14:33 ` [PATCH 53/67] aufs sub-routines for vfs in hinotify or dlgt mode hooanon05
@ 2008-05-16 14:33 ` hooanon05
2008-05-16 14:33 ` [PATCH 55/67] aufs export via nfs hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:33 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs lookup functions in 'delegate' mode
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/dlgt.c | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 113 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/dlgt.c b/fs/aufs/dlgt.c
new file mode 100644
index 0000000..0721a73
--- /dev/null
+++ b/fs/aufs/dlgt.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * lookup functions in 'delegate' mode
+ *
+ * $Id: dlgt.c,v 1.2 2008/04/21 02:00:37 sfjro Exp $
+ */
+
+//#include <linux/security.h>
+#include "aufs.h"
+
+/* ---------------------------------------------------------------------- */
+
+struct au_lookup_one_len_args {
+ struct dentry **errp;
+ const char *name;
+ struct dentry *parent;
+ int len;
+};
+
+static void au_call_lookup_one_len(void *args)
+{
+ struct au_lookup_one_len_args *a = args;
+ *a->errp = vfsub_lookup_one_len(a->name, a->parent, a->len);
+}
+
+struct dentry *au_lkup_one_dlgt(const char *name, struct dentry *parent,
+ int len, unsigned int flags)
+{
+ struct dentry *dentry;
+ int dirperm1;
+
+ LKTRTrace("%.*s/%.*s, 0x%x\n", AuDLNPair(parent), len, name, flags);
+
+ dirperm1 = au_ftest_ndx(flags, DIRPERM1);
+ if (!dirperm1 && !au_ftest_ndx(flags, DLGT))
+ dentry = vfsub_lookup_one_len(name, parent, len);
+ else {
+ int wkq_err;
+ struct au_lookup_one_len_args args = {
+ .errp = &dentry,
+ .name = name,
+ .parent = parent,
+ .len = len
+ };
+ wkq_err = au_wkq_wait(au_call_lookup_one_len, &args,
+ /*dlgt*/!dirperm1);
+ if (unlikely(wkq_err))
+ dentry = ERR_PTR(wkq_err);
+ }
+
+ AuTraceErrPtr(dentry);
+ return dentry;
+}
+
+/* ---------------------------------------------------------------------- */
+
+struct security_inode_permission_args {
+ int *errp;
+ struct inode *h_inode;
+ int mask;
+ struct nameidata *fake_nd;
+};
+
+static void call_security_inode_permission(void *args)
+{
+ struct security_inode_permission_args *a = args;
+ LKTRTrace("fsuid %d\n", current->fsuid);
+ *a->errp = security_inode_permission(a->h_inode, a->mask, a->fake_nd);
+}
+
+int au_security_inode_permission(struct inode *h_inode, int mask,
+ struct nameidata *fake_nd, int dlgt)
+{
+ int err;
+
+ AuTraceEnter();
+
+ if (!dlgt)
+ err = security_inode_permission(h_inode, mask, fake_nd);
+ else {
+ int wkq_err;
+ struct security_inode_permission_args args = {
+ .errp = &err,
+ .h_inode = h_inode,
+ .mask = mask,
+ .fake_nd = fake_nd
+ };
+ wkq_err = au_wkq_wait(call_security_inode_permission, &args,
+ /*dlgt*/1);
+ if (unlikely(wkq_err))
+ err = wkq_err;
+ }
+
+ AuTraceErr(err);
+ return err;
+}
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 55/67] aufs export via nfs
2008-05-16 14:33 ` [PATCH 54/67] aufs lookup functions in 'delegate' mode hooanon05
@ 2008-05-16 14:33 ` hooanon05
2008-05-16 14:33 ` [PATCH 56/67] aufs 'robr', aufs as readonly branch of another aufs hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:33 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs export via nfs
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/export.c | 597 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 597 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/export.c b/fs/aufs/export.c
new file mode 100644
index 0000000..edcbb6f
--- /dev/null
+++ b/fs/aufs/export.c
@@ -0,0 +1,597 @@
+/*
+ * Copyright (C) 2007-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * export via nfs
+ *
+ * $Id: export.c,v 1.4 2008/05/04 23:52:08 sfjro Exp $
+ */
+
+#include <linux/exportfs.h>
+#include "aufs.h"
+
+union conv {
+#if BITS_PER_LONG == 32
+ __u32 a[1];
+#else
+ __u32 a[2];
+#endif
+ ino_t ino;
+};
+
+static ino_t decode_ino(__u32 *a)
+{
+ union conv u;
+
+ u.a[0] = a[0];
+#if BITS_PER_LONG == 64
+ u.a[1] = a[1];
+#endif
+ return u.ino;
+}
+
+static void encode_ino(__u32 *a, ino_t ino)
+{
+ union conv u;
+
+ u.ino = ino;
+ a[0] = u.a[0];
+#if BITS_PER_LONG == 64
+ a[1] = u.a[1];
+#endif
+}
+
+/* NFS file handle */
+enum {
+ Fh_br_id,
+ Fh_sigen,
+#if BITS_PER_LONG == 64
+ /* support 64bit inode number */
+ Fh_ino1,
+ Fh_ino2,
+ Fh_dir_ino1,
+ Fh_dir_ino2,
+ Fh_h_ino1,
+ Fh_h_ino2,
+#else
+ Fh_ino1,
+ Fh_dir_ino1,
+ Fh_h_ino1,
+#endif
+ Fh_h_igen,
+ Fh_h_type,
+ Fh_tail,
+
+ Fh_ino = Fh_ino1,
+ Fh_dir_ino = Fh_dir_ino1,
+ Fh_h_ino = Fh_h_ino1,
+};
+
+static int au_test_anon(struct dentry *dentry)
+{
+ return !!(dentry->d_flags & DCACHE_DISCONNECTED);
+}
+
+/* ---------------------------------------------------------------------- */
+
+static struct dentry *decode_by_ino(struct super_block *sb, ino_t ino,
+ ino_t dir_ino)
+{
+ struct dentry *dentry, *parent;
+ struct inode *inode;
+
+ LKTRTrace("i%lu, diri%lu\n", ino, dir_ino);
+
+ dentry = NULL;
+ inode = ilookup(sb, ino);
+ if (unlikely(!inode))
+ goto out;
+
+ dentry = ERR_PTR(-ESTALE);
+ if (unlikely(is_bad_inode(inode)))
+ goto out_iput;
+
+ dentry = NULL;
+ if (!S_ISDIR(inode->i_mode)) {
+ struct dentry *d;
+ spin_lock(&dcache_lock);
+ list_for_each_entry(d, &inode->i_dentry, d_alias)
+ if (!au_test_anon(d)
+ && d->d_parent->d_inode->i_ino == dir_ino) {
+ dentry = dget_locked(d);
+ break;
+ }
+ spin_unlock(&dcache_lock);
+ } else {
+ dentry = d_find_alias(inode);
+ if (dentry && !au_test_anon(dentry)) {
+ int same_ino;
+ parent = dget_parent(dentry);
+ same_ino = (parent->d_inode->i_ino == dir_ino);
+ dput(parent);
+ if (same_ino)
+ goto out_iput; /* success */
+ }
+
+ dput(dentry);
+ dentry = NULL;
+ }
+
+ out_iput:
+ iput(inode);
+ out:
+ AuTraceErrPtr(dentry);
+ return dentry;
+}
+
+/* ---------------------------------------------------------------------- */
+
+struct find_name_by_ino {
+ int called, found;
+ ino_t ino;
+ char *name;
+ int namelen;
+};
+
+static int
+find_name_by_ino(void *arg, const char *name, int namelen, loff_t offset,
+ u64 ino, unsigned int d_type)
+{
+ struct find_name_by_ino *a = arg;
+
+ a->called++;
+ if (a->ino != ino)
+ return 0;
+
+ memcpy(a->name, name, namelen);
+ a->namelen = namelen;
+ a->found = 1;
+ return 1;
+}
+
+static struct dentry *decode_by_dir_ino(struct super_block *sb, ino_t ino,
+ ino_t dir_ino)
+{
+ struct dentry *dentry, *parent;
+ struct inode *dir;
+ struct find_name_by_ino arg;
+ struct file *file;
+ int err;
+
+ LKTRTrace("i%lu, diri%lu\n", ino, dir_ino);
+
+ dentry = NULL;
+ dir = ilookup(sb, dir_ino);
+ if (unlikely(!dir))
+ goto out;
+
+ dentry = ERR_PTR(-ESTALE);
+ if (unlikely(is_bad_inode(dir)))
+ goto out_iput;
+
+ dentry = NULL;
+ parent = d_find_alias(dir);
+ if (parent) {
+ if (unlikely(au_test_anon(parent))) {
+ dput(parent);
+ goto out_iput;
+ }
+ } else
+ goto out_iput;
+
+ file = dentry_open(parent, au_mntcache_get(sb), au_dir_roflags);
+ dentry = (void *)file;
+ if (IS_ERR(file))
+ goto out_iput;
+
+ dentry = ERR_PTR(-ENOMEM);
+ arg.name = __getname();
+ if (unlikely(!arg.name))
+ goto out_fput;
+ arg.ino = ino;
+ arg.found = 0;
+
+ do {
+ arg.called = 0;
+ //smp_mb();
+ err = vfsub_readdir(file, find_name_by_ino, &arg, /*dlgt*/0);
+ } while (!err && !arg.found && arg.called);
+ dentry = ERR_PTR(err);
+ if (arg.found) {
+ /* do not call au_lkup_one(), nor dlgt */
+ mutex_lock(&dir->i_mutex);
+ dentry = vfsub_lookup_one_len(arg.name, parent, arg.namelen);
+ mutex_unlock(&dir->i_mutex);
+ AuTraceErrPtr(dentry);
+ }
+
+ //out_putname:
+ __putname(arg.name);
+ out_fput:
+ fput(file);
+ out_iput:
+ iput(dir);
+ out:
+ AuTraceErrPtr(dentry);
+ return dentry;
+}
+
+/* ---------------------------------------------------------------------- */
+
+struct append_name {
+ int found, called, len;
+ char *h_path;
+ ino_t h_ino;
+};
+
+static int append_name(void *arg, const char *name, int len, loff_t pos,
+ u64 ino, unsigned int d_type)
+{
+ struct append_name *a = arg;
+ char *p;
+
+ a->called++;
+ if (ino != a->h_ino)
+ return 0;
+
+ AuDebugOn(len == 1 && *name == '.');
+ AuDebugOn(len == 2 && name[0] == '.' && name[1] == '.');
+ a->len = strlen(a->h_path);
+ memmove(a->h_path - len - 1, a->h_path, a->len);
+ a->h_path -= len + 1;
+ p = a->h_path + a->len;
+ *p++ = '/';
+ memcpy(p, name, len);
+ a->len += 1 + len;
+ a->found++;
+ return 1;
+}
+
+static int h_acceptable(void *expv, struct dentry *dentry)
+{
+ return 1;
+}
+
+static char *au_build_path(struct super_block *sb, __u32 *fh, char *path,
+ struct vfsmount *h_mnt, struct dentry *h_root,
+ struct dentry *h_parent)
+{
+ char *ret;
+ int err, len;
+ struct file *h_file;
+ struct append_name arg;
+ struct path dm_path = {
+ .mnt = h_mnt,
+ .dentry = h_root
+ };
+
+ AuTraceEnter();
+
+ arg.h_path = d_path(&dm_path, path, PATH_MAX);
+ ret = arg.h_path;
+ if (unlikely(!arg.h_path || IS_ERR(arg.h_path)))
+ goto out;
+
+ len = strlen(arg.h_path);
+ dm_path.dentry = h_parent;
+ arg.h_path = d_path(&dm_path, path, PATH_MAX);
+ ret = arg.h_path;
+ if (unlikely(!arg.h_path || IS_ERR(arg.h_path)))
+ goto out;
+ LKTRTrace("%s\n", arg.h_path);
+ if (len != 1)
+ arg.h_path += len;
+ LKTRTrace("%p, %s, %ld\n",
+ arg.h_path, arg.h_path, (long)(arg.h_path - path));
+
+ /* cf. fs/exportfs/expfs.c */
+ h_file = dentry_open(dget(h_parent), mntget(h_mnt), au_dir_roflags);
+ ret = (void *)h_file;
+ if (IS_ERR(h_file))
+ goto out;
+
+ arg.found = 0;
+ arg.h_ino = decode_ino(fh + Fh_h_ino);
+ do {
+ arg.called = 0;
+ err = vfsub_readdir(h_file, append_name, &arg, /*dlgt*/0);
+ } while (!err && !arg.found && arg.called);
+ LKTRTrace("%p, %s, %d\n", arg.h_path, arg.h_path, arg.len);
+ fput(h_file);
+ ret = ERR_PTR(err);
+ if (unlikely(err))
+ goto out;
+
+ dm_path.mnt = au_mntcache_get(sb);
+ dm_path.dentry = sb->s_root;
+ ret = d_path(&dm_path, path, PATH_MAX - arg.len);
+ mntput(dm_path.mnt);
+ if (unlikely(!ret || IS_ERR(ret)))
+ goto out;
+ ret[strlen(ret)] = '/';
+ LKTRTrace("%s\n", ret);
+
+ out:
+ AuTraceErrPtr(ret);
+ return ret;
+}
+
+static struct dentry *
+decode_by_path(struct super_block *sb, aufs_bindex_t bindex, __u32 *fh,
+ int fh_len, void *context)
+{
+ struct dentry *dentry, *h_parent, *root, *h_root;
+ struct super_block *h_sb;
+ char *path, *p;
+ struct vfsmount *h_mnt;
+ int err;
+ struct nameidata nd;
+ struct au_branch *br;
+
+ LKTRTrace("b%d\n", bindex);
+ SiMustAnyLock(sb);
+
+ br = au_sbr(sb, bindex);
+ /* au_br_get(br); */
+ h_mnt = br->br_mnt;
+ h_sb = h_mnt->mnt_sb;
+ LKTRTrace("%s, h_decode_fh\n", au_sbtype(h_sb));
+ /* in linux-2.6.24, it takes struct fid * as file handle */
+ //todo: call lower fh_to_dentry()? fh_to_parent()?
+ h_parent = exportfs_decode_fh(h_mnt, (void *)(fh + Fh_tail),
+ fh_len - Fh_tail, fh[Fh_h_type],
+ h_acceptable, /*context*/NULL);
+ dentry = h_parent;
+ if (unlikely(!h_parent || IS_ERR(h_parent))) {
+ AuWarn1("%s decode_fh failed\n", au_sbtype(h_sb));
+ goto out;
+ }
+ dentry = NULL;
+ if (unlikely(au_test_anon(h_parent))) {
+ AuWarn1("%s decode_fh returned a disconnected dentry\n",
+ au_sbtype(h_sb));
+ dput(h_parent);
+ goto out;
+ }
+
+ dentry = ERR_PTR(-ENOMEM);
+ path = __getname();
+ if (unlikely(!path)) {
+ dput(h_parent);
+ goto out;
+ }
+
+ root = sb->s_root;
+ di_read_lock_parent(root, !AuLock_IR);
+ h_root = au_h_dptr(root, bindex);
+ di_read_unlock(root, !AuLock_IR);
+ p = au_build_path(sb, fh, path, h_mnt, h_root, h_parent);
+ dput(h_parent);
+ if (IS_ERR(p))
+ goto out_putname;
+
+ err = vfsub_path_lookup(p, LOOKUP_FOLLOW, &nd);
+ dentry = ERR_PTR(err);
+ if (!err) {
+ dentry = dget(nd.path.dentry);
+ if (unlikely(au_test_anon(dentry))) {
+ dput(dentry);
+ dentry = ERR_PTR(-ESTALE);
+ }
+ path_put(&nd.path);
+ }
+
+ out_putname:
+ __putname(path);
+ out:
+ /* au_br_put(br); */
+ AuTraceErrPtr(dentry);
+ return dentry;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static noinline_for_stack struct dentry *
+aufs_decode_fh(struct super_block *sb, __u32 *fh, int fh_len, int fh_type,
+ int (*acceptable)(void *context, struct dentry *de),
+ void *context)
+{
+ struct dentry *dentry;
+ ino_t ino, dir_ino;
+ aufs_bindex_t bindex, br_id;
+ struct inode *inode, *h_inode;
+ au_gen_t sigen;
+
+ //au_debug_on();
+ LKTRTrace("%d, fh{i%u, br_id %u, sigen %u, hi%u}\n",
+ fh_type, fh[Fh_ino], fh[Fh_br_id], fh[Fh_sigen],
+ fh[Fh_h_ino]);
+ AuDebugOn(fh_len < Fh_tail);
+
+ si_read_lock(sb, AuLock_FLUSH);
+ lockdep_off();
+
+ /* branch id may be wrapped around */
+ dentry = ERR_PTR(-ESTALE);
+ br_id = fh[Fh_br_id];
+ sigen = fh[Fh_sigen];
+ bindex = au_br_index(sb, br_id);
+ if (unlikely(bindex < 0 || sigen + AUFS_BRANCH_MAX <= au_sigen(sb)))
+ goto out;
+
+ /* is this inode still cached? */
+ ino = decode_ino(fh + Fh_ino);
+ dir_ino = decode_ino(fh + Fh_dir_ino);
+ dentry = decode_by_ino(sb, ino, dir_ino);
+ if (IS_ERR(dentry))
+ goto out;
+ if (dentry)
+ goto accept;
+
+ /* is the parent dir cached? */
+ dentry = decode_by_dir_ino(sb, ino, dir_ino);
+ if (IS_ERR(dentry))
+ goto out;
+ if (dentry)
+ goto accept;
+
+ /* lookup path */
+ dentry = decode_by_path(sb, bindex, fh, fh_len, context);
+ if (IS_ERR(dentry))
+ goto out;
+ if (unlikely(!dentry))
+ goto out_stale;
+ if (unlikely(dentry->d_inode->i_ino != ino))
+ goto out_dput;
+
+ accept:
+ inode = dentry->d_inode;
+ h_inode = NULL;
+ ii_read_lock_child(inode);
+ if (au_ibstart(inode) <= bindex && bindex <= au_ibend(inode))
+ h_inode = au_h_iptr(inode, bindex);
+ ii_read_unlock(inode);
+ if (h_inode
+ && h_inode->i_generation == fh[Fh_h_igen]
+ && acceptable(context, dentry))
+ goto out; /* success */
+ out_dput:
+ dput(dentry);
+ out_stale:
+ dentry = ERR_PTR(-ESTALE);
+ out:
+ lockdep_on();
+ si_read_unlock(sb);
+ AuTraceErrPtr(dentry);
+ //au_debug_off();
+ return dentry;
+}
+
+static struct dentry *
+aufs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len,
+ int fh_type)
+{
+ return aufs_decode_fh(sb, fid->raw, fh_len, fh_type, h_acceptable,
+ /*context*/NULL);
+}
+
+#if 0
+/* support subtreecheck option */
+static struct dentry *au_fh_to_parent(struct super_block *sb, struct fid *fid,
+ int fh_len, int fh_type)
+{
+}
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+static int aufs_encode_fh(struct dentry *dentry, __u32 *fh, int *max_len,
+ int connectable)
+{
+ int err;
+ struct super_block *sb, *h_sb;
+ struct inode *inode, *h_inode, *dir;
+ aufs_bindex_t bindex;
+ union conv u;
+ struct dentry *parent, *h_parent;
+
+ //au_debug_on();
+ BUILD_BUG_ON(sizeof(u.ino) != sizeof(u.a));
+ LKTRTrace("%.*s, max %d, conn %d\n",
+ AuDLNPair(dentry), *max_len, connectable);
+ AuDebugOn(au_test_anon(dentry));
+ inode = dentry->d_inode;
+ AuDebugOn(!inode);
+ parent = dget_parent(dentry);
+ AuDebugOn(au_test_anon(parent));
+
+ err = -ENOSPC;
+ if (unlikely(*max_len <= Fh_tail)) {
+ AuWarn1("NFSv2 client (max_len %d)?\n", *max_len);
+ goto out;
+ }
+
+ sb = dentry->d_sb;
+ si_read_lock(sb, AuLock_FLUSH);
+ di_read_lock_child(dentry, AuLock_IR);
+ di_read_lock_parent(parent, AuLock_IR);
+#ifdef CONFIG_AUFS_DEBUG
+ if (unlikely(!au_opt_test(au_mntflags(sb), XINO)))
+ AuWarn1("NFS-exporting requires xino\n");
+#if 0 // temp
+ if (unlikely(au_opt_test(au_mntflags(sb), UDBA_INOTIFY)))
+ AuWarn1("udba=inotify is not recommended when exporting\n");
+#endif
+#endif
+
+ err = -EPERM;
+ bindex = au_dbstart(dentry);
+ h_sb = au_sbr_sb(sb, bindex);
+ if (unlikely(!h_sb->s_export_op)) {
+ AuErr1("%s branch is not exportable\n", au_sbtype(h_sb));
+ goto out_unlock;
+ }
+
+#if 0 //def CONFIG_AUFS_ROBR
+ if (unlikely(au_test_aufs(h_sb))) {
+ AuErr1("aufs branch is not supported\n");
+ goto out_unlock;
+ }
+#endif
+
+ fh[Fh_br_id] = au_sbr_id(sb, bindex);
+ fh[Fh_sigen] = au_sigen(sb);
+ encode_ino(fh + Fh_ino, inode->i_ino);
+ dir = parent->d_inode;
+ encode_ino(fh + Fh_dir_ino, dir->i_ino);
+ h_inode = au_h_dptr(dentry, bindex)->d_inode;
+ encode_ino(fh + Fh_h_ino, h_inode->i_ino);
+ fh[Fh_h_igen] = h_inode->i_generation;
+
+ *max_len -= Fh_tail;
+ //LKTRTrace("Fh_tail %d, max_len %d\n", Fh_tail, *max_len);
+ h_parent = au_h_dptr(parent, bindex);
+ AuDebugOn(au_test_anon(h_parent));
+ /* in linux-2.6.24, it takes struct fid * as file handle */
+ fh[Fh_h_type] = exportfs_encode_fh(h_parent, (void *)(fh + Fh_tail),
+ max_len, connectable);
+ err = fh[Fh_h_type];
+ *max_len += Fh_tail;
+ if (err != 255)
+ err = 2; //??
+ else
+ AuWarn1("%s encode_fh failed\n", au_sbtype(h_sb));
+
+ out_unlock:
+ di_read_unlock(parent, AuLock_IR);
+ aufs_read_unlock(dentry, AuLock_IR);
+ out:
+ dput(parent);
+ AuTraceErr(err);
+ //au_debug_off();
+ if (unlikely(err < 0))
+ err = 255;
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+struct export_operations aufs_export_op = {
+ .fh_to_dentry = aufs_fh_to_dentry,
+ .encode_fh = aufs_encode_fh
+};
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 56/67] aufs 'robr', aufs as readonly branch of another aufs
2008-05-16 14:33 ` [PATCH 55/67] aufs export via nfs hooanon05
@ 2008-05-16 14:33 ` hooanon05
2008-05-16 14:33 ` [PATCH 57/67] aufs sysfs interface hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:33 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs 'robr', aufs as readonly branch of another aufs
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/robr.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 111 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/robr.c b/fs/aufs/robr.c
new file mode 100644
index 0000000..0e508bc
--- /dev/null
+++ b/fs/aufs/robr.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * 'robr', aufs as readonly branch of another aufs
+ *
+ * $Id: robr.c,v 1.2 2008/05/12 00:26:53 sfjro Exp $
+ */
+
+#include "aufs.h"
+
+/* ---------------------------------------------------------------------- */
+
+int au_test_robr_wh(struct qstr *name, struct dentry *h_parent,
+ struct qstr *wh_name, int try_sio, struct au_ndx *ndx)
+{
+ if (strncmp(name->name, AUFS_WH_PFX, AUFS_WH_PFX_LEN))
+ return au_wh_test(h_parent, wh_name, try_sio, ndx);
+ return -EPERM;
+}
+
+int au_test_robr_shwh(struct super_block *sb, const struct qstr *name)
+{
+ return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+struct au_robr_lvma {
+ struct list_head list;
+ struct vm_area_struct *vma;
+};
+
+struct file *au_robr_safe_file(struct vm_area_struct *vma)
+{
+ struct file *file = vma->vm_file;
+ struct super_block *sb = file->f_dentry->d_sb;
+ struct au_robr_lvma *lvma, *entry;
+ struct au_sbinfo *sbinfo;
+ int found, warn;
+
+ AuTraceEnter();
+ AuDebugOn(!au_test_aufs(sb));
+
+ warn = 0;
+ found = 0;
+ sbinfo = au_sbi(sb);
+ spin_lock(&sbinfo->si_lvma_lock);
+ list_for_each_entry(entry, &sbinfo->si_lvma, list) {
+ found = (entry->vma == vma);
+ if (unlikely(found))
+ break;
+ }
+ if (!found) {
+ lvma = kmalloc(sizeof(*lvma), GFP_ATOMIC);
+ if (lvma) {
+ lvma->vma = vma;
+ list_add(&lvma->list, &sbinfo->si_lvma);
+ } else {
+ warn = 1;
+ file = NULL;
+ }
+ } else
+ file = NULL;
+ spin_unlock(&sbinfo->si_lvma_lock);
+
+ if (unlikely(warn))
+ AuWarn1("no memory for lvma\n");
+ return file;
+}
+
+void au_robr_reset_file(struct vm_area_struct *vma, struct file *file)
+{
+ struct super_block *sb = file->f_dentry->d_sb;
+ struct au_robr_lvma *entry, *found;
+ struct au_sbinfo *sbinfo;
+
+ AuTraceEnter();
+ AuDebugOn(!au_test_aufs(sb));
+
+ vma->vm_file = file;
+ //smp_mb(); /* flush vm_file */
+
+ found = NULL;
+ sbinfo = au_sbi(sb);
+ spin_lock(&sbinfo->si_lvma_lock);
+ list_for_each_entry(entry, &sbinfo->si_lvma, list)
+ if (entry->vma == vma) {
+ found = entry;
+ break;
+ }
+ AuDebugOn(!found);
+ list_del(&found->list);
+ spin_unlock(&sbinfo->si_lvma_lock);
+ kfree(found);
+}
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 57/67] aufs sysfs interface
2008-05-16 14:33 ` [PATCH 56/67] aufs 'robr', aufs as readonly branch of another aufs hooanon05
@ 2008-05-16 14:33 ` hooanon05
2008-05-16 14:33 ` [PATCH 58/67] aufs misc functions, header hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:33 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs sysfs interface
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/sysfs.c | 459 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 459 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/sysfs.c b/fs/aufs/sysfs.c
new file mode 100644
index 0000000..eb0d367
--- /dev/null
+++ b/fs/aufs/sysfs.c
@@ -0,0 +1,459 @@
+/*
+ * Copyright (C) 2007-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * sysfs interface
+ *
+ * $Id: sysfs.c,v 1.3 2008/05/12 00:27:58 sfjro Exp $
+ */
+
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <linux/sysfs.h>
+#include "aufs.h"
+
+
+#ifdef CONFIG_AUFS_LOCAL
+static ssize_t config_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf)
+{
+#define conf_bool(name) "CONFIG_AUFS_" #name "=y\n"
+ static const char opt[] =
+#ifdef CONFIG_AUFS
+ "CONFIG_AUFS=y\n"
+#else
+ "CONFIG_AUFS=m\n"
+#endif
+#ifdef CONFIG_AUFS_FAKE_DM
+ conf_bool(FAKE_DM)
+#endif
+#ifdef CONFIG_AUFS_BRANCH_MAX_127
+ conf_bool(BRANCH_MAX_127)
+#elif defined(CONFIG_AUFS_BRANCH_MAX_511)
+ conf_bool(BRANCH_MAX_511)
+#elif defined(CONFIG_AUFS_BRANCH_MAX_1023)
+ conf_bool(BRANCH_MAX_1023)
+#elif defined(CONFIG_AUFS_BRANCH_MAX_32767)
+ conf_bool(BRANCH_MAX_32767)
+#endif
+#ifdef CONFIG_AUFS_HINOTIFY
+ conf_bool(HINOTIFY)
+#endif
+#ifdef CONFIG_AUFS_EXPORT
+ conf_bool(EXPORT)
+#endif
+#ifdef CONFIG_AUFS_ROBR
+ conf_bool(ROBR)
+#endif
+#ifdef CONFIG_AUFS_SHWH
+ conf_bool(SHWH)
+#endif
+#ifdef CONFIG_AUFS_DLGT
+ conf_bool(DLGT)
+#endif
+#ifdef CONFIG_AUFS_RR_SQUASHFS
+ conf_bool(RR_SQUASHFS)
+#endif
+#ifdef CONFIG_AUFS_SEC_PERM_PATCH
+ conf_bool(SEC_PERM_PATCH)
+#endif
+#ifdef CONFIG_AUFS_SPLICE_PATCH
+ conf_bool(SPLICE_PATCH)
+#endif
+#ifdef CONFIG_AUFS_PUT_FILP_PATCH
+ conf_bool(PUT_FILP_PATCH)
+#endif
+#ifdef CONFIG_AUFS_LHASH_PATCH
+ conf_bool(LHASH_PATCH)
+#endif
+#ifdef CONFIG_AUFS_FSYNC_SUPER_PATCH
+ conf_bool(FSYNC_SUPER_PATCH)
+#endif
+#ifdef CONFIG_AUFS_DENY_WRITE_ACCESS_PATCH
+ conf_bool(DENY_WRITE_ACCESS_PATCH)
+#endif
+#ifdef CONFIG_AUFS_KSIZE_PATCH
+ conf_bool(KSIZE_PATCH)
+#endif
+#ifdef CONFIG_AUFS_WORKAROUND_FUSE
+ conf_bool(WORKAROUND_FUSE)
+#endif
+#ifdef CONFIG_AUFS_STAT
+ conf_bool(STAT)
+#endif
+#ifdef CONFIG_AUFS_DEBUG
+ conf_bool(DEBUG)
+#endif
+#ifdef CONFIG_AUFS_COMPAT
+ conf_bool(COMPAT)
+#endif
+
+/* automatic configurations */
+#ifdef CONFIG_AUFS_BR_NFS
+ conf_bool(BR_NFS)
+#endif
+#ifdef CONFIG_AUFS_MAGIC_SYSRQ
+ conf_bool(MAGIC_SYSRQ)
+#endif
+ ;
+#undef conf_bool
+
+ char *p = buf;
+ const char *end = buf + PAGE_SIZE;
+
+ p += snprintf(p, end - p, "%s", opt);
+#ifdef DbgUdbaRace
+ if (p < end)
+ p += snprintf(p, end - p, "DbgUdbaRace=%d\n", DbgUdbaRace);
+#endif
+ if (p < end)
+ return p - buf;
+ else
+ return -EFBIG;
+}
+
+static struct kobj_attribute au_config_attr = __ATTR_RO(config);
+#endif
+
+#ifdef CONFIG_AUFS_STAT
+static ssize_t stat_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf)
+{
+ char *p = buf;
+ const char *end = buf + PAGE_SIZE;
+ int i;
+
+ p += snprintf(p, end - p, "wkq max_busy:");
+ for (i = 0; p < end && i < aufs_nwkq; i++)
+ p += snprintf(p, end - p, " %u", au_wkq[i].max_busy);
+ if (p < end)
+ p += snprintf(p, end - p, ", %u(generic)\n",
+ au_wkq[aufs_nwkq].max_busy);
+
+ if (p < end)
+ return p - buf;
+ else
+ return -EFBIG;
+}
+
+static struct kobj_attribute au_stat_attr = __ATTR_RO(stat);
+#endif
+
+#ifdef CONFIG_AUFS_DEBUG
+static ssize_t debug_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%d\n", au_debug_test());
+}
+
+static ssize_t debug_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t sz)
+{
+ LKTRTrace("%.*s\n", (unsigned int)sz, buf);
+
+ if (unlikely(!sz || (*buf != '0' && *buf != '1')))
+ return -EOPNOTSUPP;
+
+ if (*buf == '0')
+ au_debug_off();
+ else if (*buf == '1')
+ au_debug_on();
+ return sz;
+}
+
+static struct kobj_attribute au_debug_attr = __ATTR(debug, S_IRUGO | S_IWUSR,
+ debug_show, debug_store);
+#endif
+
+static struct attribute *au_attr[] = {
+#ifdef CONFIG_AUFS_LOCAL
+ &au_config_attr.attr,
+#endif
+#ifdef CONFIG_AUFS_STAT
+ &au_stat_attr.attr,
+#endif
+#ifdef CONFIG_AUFS_DEBUG
+ &au_debug_attr.attr,
+#endif
+ NULL, /* need to NULL terminate the list of attributes */
+};
+
+static struct attribute_group au_attr_group_body = {
+ .attrs = au_attr
+};
+
+struct attribute_group *au_attr_group = &au_attr_group_body;
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * they are copied from linux/lib/kobject.c,
+ * and will be exported in the future.
+ */
+static ssize_t au_attr_show(struct kobject *kobj, struct attribute *attr,
+ char *buf)
+{
+ struct kobj_attribute *kattr;
+ ssize_t ret = -EIO;
+
+ kattr = container_of(attr, struct kobj_attribute, attr);
+ if (kattr->show)
+ ret = kattr->show(kobj, kattr, buf);
+ return ret;
+}
+
+#ifdef CONFIG_AUFS_DEBUG
+static ssize_t au_attr_store(struct kobject *kobj, struct attribute *attr,
+ const char *buf, size_t count)
+{
+ struct kobj_attribute *kattr;
+ ssize_t ret = -EIO;
+
+ kattr = container_of(attr, struct kobj_attribute, attr);
+ if (kattr->store)
+ ret = kattr->store(kobj, kattr, buf, count);
+ return ret;
+}
+#endif
+
+static struct sysfs_ops sysaufs_ops = {
+ .show = au_attr_show,
+#ifdef CONFIG_AUFS_DEBUG
+ .store = au_attr_store
+#endif
+};
+
+static struct kobj_type au_ktype_body = {
+ .sysfs_ops = &sysaufs_ops
+};
+struct kobj_type *au_ktype = &au_ktype_body;
+
+/* ---------------------------------------------------------------------- */
+
+int sysaufs_sbi_xino(struct seq_file *seq, struct super_block *sb)
+{
+ int err, dlgt;
+ struct au_sbinfo *sbinfo;
+ unsigned int mnt_flags;
+ aufs_bindex_t bend, bindex;
+ struct file *xf;
+ struct kstat st;
+
+ AuTraceEnter();
+
+ sbinfo = au_sbi(sb);
+ mnt_flags = au_mntflags(sb);
+ if (unlikely(!au_opt_test(mnt_flags, XINO))) {
+#ifdef CONFIG_AUFS_DEBUG
+ AuDebugOn(sbinfo->si_xib);
+ bend = au_sbend(sb);
+ for (bindex = 0; bindex <= bend; bindex++)
+ AuDebugOn(au_sbr(sb, bindex)->br_xino);
+#endif
+ err = 0;
+ goto out; /* success */
+ }
+
+ dlgt = !!au_opt_test_dlgt(mnt_flags);
+ xf = sbinfo->si_xib;
+ err = vfsub_getattr(xf->f_vfsmnt, xf->f_dentry, &st, dlgt);
+ if (!err)
+ seq_printf(seq, "%Lux%lu %Ld\n",
+ st.blocks, st.blksize, (long long)st.size);
+ else
+ seq_printf(seq, "err %d\n", err);
+
+ bend = au_sbend(sb);
+ for (bindex = 0; !err && bindex <= bend; bindex++) {
+ xf = au_sbr(sb, bindex)->br_xino;
+ if (!xf)
+ continue;
+ seq_printf(seq, "%d: ", bindex);
+ err = vfsub_getattr(xf->f_vfsmnt, xf->f_dentry, &st, dlgt);
+ if (!err)
+ seq_printf(seq, "%d, %Lux%lu %Ld\n",
+ file_count(xf), st.blocks, st.blksize,
+ (long long)st.size);
+ else
+ seq_printf(seq, "err %d\n", err);
+ }
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+/*
+ * the lifetime of branch is independent from the entry under sysfs.
+ * sysfs handles the lifetime of the entry, and never call ->show() after it is
+ * unlinked.
+ */
+#define SysaufsBr_PREFIX "br"
+static int sysaufs_sbi_br(struct seq_file *seq, struct super_block *sb,
+ aufs_bindex_t bindex)
+{
+ int err;
+ struct dentry *root;
+ struct au_branch *br;
+ struct path path;
+
+ LKTRTrace("b%d\n", bindex);
+
+ err = -ENOENT;
+ if (unlikely(au_sbend(sb) < bindex))
+ goto out;
+
+ err = 0;
+ root = sb->s_root;
+ di_read_lock_parent(root, !AuLock_IR);
+ br = au_sbr(sb, bindex);
+ path.mnt = br->br_mnt;
+ path.dentry = au_h_dptr(root, bindex);
+ seq_path(seq, &path, au_esc_chars);
+ di_read_unlock(root, !AuLock_IR);
+ seq_printf(seq, "=%s\n", au_optstr_br_perm(br->br_perm));
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static struct seq_file *au_seq(char *p, ssize_t len)
+{
+ struct seq_file *seq;
+
+ seq = kzalloc(sizeof(*seq), GFP_TEMPORARY);
+ if (seq) {
+ //mutex_init(&seq.lock);
+ seq->buf = p;
+ seq->count = 0;
+ seq->size = len;
+ return seq; /* success */
+ }
+
+ seq = ERR_PTR(-ENOMEM);
+ AuTraceErrPtr(seq);
+ return seq;
+}
+
+//todo: file size may exceed PAGE_SIZE
+ssize_t sysaufs_sbi_show(struct kobject *kobj, struct attribute *attr,
+ char *buf)
+{
+ ssize_t err;
+ struct au_sbinfo *sbinfo;
+ struct super_block *sb;
+ struct seq_file *seq;
+ char *name;
+ struct attribute **cattr;
+
+ LKTRTrace("%s/%s\n", kobject_name(kobj), attr->name);
+
+ sbinfo = container_of(kobj, struct au_sbinfo, si_kobj);
+ sb = sbinfo->si_sb;
+ si_noflush_read_lock(sb);
+
+ seq = au_seq(buf, PAGE_SIZE);
+ err = PTR_ERR(seq);
+ if (IS_ERR(seq))
+ goto out;
+
+ name = (void *)attr->name;
+ cattr = au_sbi_attrs;
+ while (*cattr) {
+ if (!strcmp(name, (*cattr)->name)) {
+ err = container_of(*cattr, struct au_sbi_attr, attr)
+ ->show(seq, sb);
+ goto out_seq;
+ }
+ cattr++;
+ }
+
+ if (!strncmp(name, SysaufsBr_PREFIX, sizeof(SysaufsBr_PREFIX) - 1)) {
+ name += sizeof(SysaufsBr_PREFIX) - 1;
+ err = sysaufs_sbi_br(seq, sb, simple_strtol(name, NULL, 10));
+ goto out_seq;
+ }
+ BUG();
+
+ out_seq:
+ if (!err) {
+ err = seq->count;
+ /* sysfs limit */
+ if (unlikely(err == PAGE_SIZE))
+ err = -EFBIG;
+ }
+ kfree(seq);
+ out:
+ si_read_unlock(sb);
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+void sysaufs_br_init(struct au_branch *br)
+{
+ br->br_attr.name = br->br_name;
+ br->br_attr.mode = S_IRUGO;
+ br->br_attr.owner = THIS_MODULE;
+}
+
+void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex)
+{
+ struct au_sbinfo *sbinfo;
+ aufs_bindex_t bend;
+
+ LKTRTrace("b%d\n", bindex);
+
+ if (!sysaufs_brs)
+ return;
+
+ sbinfo = au_sbi(sb);
+ bend = au_sbend(sb);
+ for (; bindex <= bend; bindex++)
+ sysfs_remove_file(&sbinfo->si_kobj,
+ &au_sbr(sb, bindex)->br_attr);
+}
+
+void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex)
+{
+ int err;
+ struct kobject *kobj;
+ aufs_bindex_t bend;
+ struct au_branch *br;
+
+ LKTRTrace("b%d\n", bindex);
+
+ if (!sysaufs_brs)
+ return;
+
+ kobj = &au_sbi(sb)->si_kobj;
+ bend = au_sbend(sb);
+ for (; bindex <= bend; bindex++) {
+ br = au_sbr(sb, bindex);
+ snprintf(br->br_name, sizeof(br->br_name),
+ SysaufsBr_PREFIX "%d", bindex);
+ err = sysfs_create_file(kobj, &br->br_attr);
+ if (unlikely(err))
+ AuWarn("failed %s under sysfs(%d)\n", br->br_name, err);
+ }
+}
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 58/67] aufs misc functions, header
2008-05-16 14:33 ` [PATCH 57/67] aufs sysfs interface hooanon05
@ 2008-05-16 14:33 ` hooanon05
2008-05-16 14:33 ` [PATCH 59/67] aufs misc functions, source hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:33 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs misc functions, header
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/misc.h | 201 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 201 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/misc.h b/fs/aufs/misc.h
new file mode 100644
index 0000000..e57c398
--- /dev/null
+++ b/fs/aufs/misc.h
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * $Id: misc.h,v 1.2 2008/04/21 01:33:00 sfjro Exp $
+ */
+
+#ifndef __AUFS_MISC_H__
+#define __AUFS_MISC_H__
+
+#ifdef __KERNEL__
+
+#include <linux/fs.h>
+#include <linux/namei.h>
+#include <linux/aufs_type.h>
+
+/* ---------------------------------------------------------------------- */
+
+typedef unsigned int au_gen_t;
+/* see linux/include/linux/jiffies.h */
+#define AuGenYounger(a, b) ((int)(b) - (int)(a) < 0)
+#define AuGenOlder(a, b) AufsGenYounger(b, a)
+
+/* ---------------------------------------------------------------------- */
+
+struct au_rwsem {
+ struct rw_semaphore rwsem;
+#ifdef CONFIG_AUFS_DEBUG
+ atomic_t rcnt;
+#endif
+};
+
+#ifdef CONFIG_AUFS_DEBUG
+#define AuDbgRcntInit(rw) do { \
+ atomic_set(&(rw)->rcnt, 0); \
+ smp_mb(); \
+} while (0)
+
+#define AuDbgRcntInc(rw) atomic_inc_return(&(rw)->rcnt)
+#define AuDbgRcntDec(rw) WARN_ON(atomic_dec_return(&(rw)->rcnt) < 0)
+#else
+#define AuDbgRcntInit(rw) do {} while (0)
+#define AuDbgRcntInc(rw) do {} while (0)
+#define AuDbgRcntDec(rw) do {} while (0)
+#endif /* CONFIG_AUFS_DEBUG */
+
+static inline void au_rw_init_nolock(struct au_rwsem *rw)
+{
+ AuDbgRcntInit(rw);
+ init_rwsem(&rw->rwsem);
+}
+
+static inline void au_rw_init_wlock(struct au_rwsem *rw)
+{
+ au_rw_init_nolock(rw);
+ down_write(&rw->rwsem);
+}
+
+static inline void au_rw_init_wlock_nested(struct au_rwsem *rw,
+ unsigned int lsc)
+{
+ au_rw_init_nolock(rw);
+ down_write_nested(&rw->rwsem, lsc);
+}
+
+static inline void au_rw_read_lock(struct au_rwsem *rw)
+{
+ down_read(&rw->rwsem);
+ AuDbgRcntInc(rw);
+}
+
+static inline void au_rw_read_lock_nested(struct au_rwsem *rw, unsigned int lsc)
+{
+ down_read_nested(&rw->rwsem, lsc);
+ AuDbgRcntInc(rw);
+}
+
+static inline void au_rw_read_unlock(struct au_rwsem *rw)
+{
+ AuDbgRcntDec(rw);
+ up_read(&rw->rwsem);
+}
+
+static inline void au_rw_dgrade_lock(struct au_rwsem *rw)
+{
+ AuDbgRcntInc(rw);
+ downgrade_write(&rw->rwsem);
+}
+
+static inline void au_rw_write_lock(struct au_rwsem *rw)
+{
+ down_write(&rw->rwsem);
+}
+
+static inline void au_rw_write_lock_nested(struct au_rwsem *rw,
+ unsigned int lsc)
+{
+ down_write_nested(&rw->rwsem, lsc);
+}
+
+static inline void au_rw_write_unlock(struct au_rwsem *rw)
+{
+ up_write(&rw->rwsem);
+}
+
+/* why is not _nested version defined */
+static inline int au_rw_read_trylock(struct au_rwsem *rw)
+{
+ int ret = down_read_trylock(&rw->rwsem);
+ if (ret)
+ AuDbgRcntInc(rw);
+ return ret;
+}
+
+static inline int au_rw_write_trylock(struct au_rwsem *rw)
+{
+ return down_write_trylock(&rw->rwsem);
+}
+
+#undef AuDbgRcntInit
+#undef AuDbgRcntInc
+#undef AuDbgRcntDec
+
+/* to debug easier, do not make them inlined functions */
+#define AuRwMustNoWaiters(rw) AuDebugOn(!list_empty(&(rw)->rwsem.wait_list))
+#define AuRwMustAnyLock(rw) AuDebugOn(down_write_trylock(&(rw)->rwsem))
+#ifdef CONFIG_AUFS_DEBUG
+#define AuRwMustReadLock(rw) do { \
+ AuRwMustAnyLock(rw); \
+ AuDebugOn(!atomic_read(&(rw)->rcnt)); \
+} while (0)
+
+#define AuRwMustWriteLock(rw) do { \
+ AuRwMustAnyLock(rw); \
+ AuDebugOn(atomic_read(&(rw)->rcnt)); \
+} while (0)
+#else
+#define AuRwMustReadLock(rw) AuRwMustAnyLock(rw)
+#define AuRwMustWriteLock(rw) AuRwMustAnyLock(rw)
+#endif /* CONFIG_AUFS_DEBUG */
+
+#define AuSimpleLockRwsemFuncs(prefix, param, rwsem) \
+static inline void prefix##_read_lock(param) \
+{ au_rw_read_lock(&(rwsem)); } \
+static inline void prefix##_write_lock(param) \
+{ au_rw_write_lock(&(rwsem)); } \
+static inline int prefix##_read_trylock(param) \
+{ return au_rw_read_trylock(&(rwsem)); } \
+static inline int prefix##_write_trylock(param) \
+{ return au_rw_write_trylock(&(rwsem)); }
+//static inline void prefix##_read_trylock_nested(param, lsc)
+//{au_rw_read_trylock_nested(&(rwsem, lsc));}
+//static inline void prefix##_write_trylock_nestd(param, lsc)
+//{au_rw_write_trylock_nested(&(rwsem), nested);}
+
+#define AuSimpleUnlockRwsemFuncs(prefix, param, rwsem) \
+static inline void prefix##_read_unlock(param) \
+{ au_rw_read_unlock(&(rwsem)); } \
+static inline void prefix##_write_unlock(param) \
+{ au_rw_write_unlock(&(rwsem)); } \
+static inline void prefix##_downgrade_lock(param) \
+{ au_rw_dgrade_lock(&(rwsem)); }
+
+#define AuSimpleRwsemFuncs(prefix, param, rwsem) \
+ AuSimpleLockRwsemFuncs(prefix, param, rwsem) \
+ AuSimpleUnlockRwsemFuncs(prefix, param, rwsem)
+
+/* ---------------------------------------------------------------------- */
+
+void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp);
+
+struct au_sbinfo;
+struct nameidata *au_dup_nd(struct au_sbinfo *sbinfo, struct nameidata *dst,
+ struct nameidata *src);
+
+struct nameidata *au_fake_dm(struct nameidata *fake_nd, struct nameidata *nd,
+ struct super_block *sb, aufs_bindex_t bindex);
+void au_fake_dm_release(struct nameidata *fake_nd);
+int au_h_create(struct inode *h_dir, struct dentry *h_dentry, int mode,
+ int dlgt, struct nameidata *nd, struct vfsmount *nfsmnt);
+
+int au_copy_file(struct file *dst, struct file *src, loff_t len,
+ struct super_block *sb);
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_MISC_H__ */
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 59/67] aufs misc functions, source
2008-05-16 14:33 ` [PATCH 58/67] aufs misc functions, header hooanon05
@ 2008-05-16 14:33 ` hooanon05
2008-05-16 14:33 ` [PATCH 60/67] aufs debug print functions, header hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:33 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs misc functions, source
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/misc.c | 307 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 307 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/misc.c b/fs/aufs/misc.c
new file mode 100644
index 0000000..edb5f7c
--- /dev/null
+++ b/fs/aufs/misc.c
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * $Id: misc.c,v 1.5 2008/05/12 00:27:16 sfjro Exp $
+ */
+
+//#include <linux/fs.h>
+//#include <linux/namei.h>
+//#include <linux/mm.h>
+//#include <asm/uaccess.h>
+#include "aufs.h"
+
+void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp)
+{
+ void *q;
+
+ LKTRTrace("p %p, nused %d, sz %d\n", p, nused, new_sz);
+ AuDebugOn(new_sz <= 0);
+ if (new_sz <= nused)
+ return p;
+
+ q = krealloc(p, new_sz, gfp);
+ if (q)
+ memset(q + nused, 0, new_sz - nused);
+ return q;
+}
+
+/* ---------------------------------------------------------------------- */
+
+struct nameidata *au_dup_nd(struct au_sbinfo *sbinfo, struct nameidata *dst,
+ struct nameidata *src)
+{
+ LKTRTrace("src %p\n", src);
+
+ if (src) {
+ *dst = *src;
+ dst->flags &= ~LOOKUP_PARENT;
+ if (sbinfo->si_wbr_create == AuWbrCreate_TDP) {
+ if ((dst->flags & LOOKUP_CREATE)
+ && !(dst->intent.open.flags & O_CREAT))
+ dst->flags &= ~LOOKUP_CREATE;
+ } else {
+ dst->flags &= ~LOOKUP_CREATE;
+ dst->intent.open.flags &= ~O_CREAT;
+ }
+ } else
+ dst = NULL;
+
+ return dst;
+}
+
+#ifdef CONFIG_AUFS_FAKE_DM
+struct nameidata *au_fake_dm(struct nameidata *fake_nd, struct nameidata *nd,
+ struct super_block *sb, aufs_bindex_t bindex)
+{
+ LKTRTrace("nd %p, b%d\n", nd, bindex);
+
+ if (!nd)
+ return NULL;
+
+ fake_nd->path.dentry = NULL;
+ fake_nd->path.mnt = NULL;
+ return fake_nd;
+}
+
+void au_fake_dm_release(struct nameidata *fake_nd)
+{
+ /* empty */
+}
+
+int au_h_create(struct inode *h_dir, struct dentry *h_dentry, int mode,
+ int dlgt, struct nameidata *nd, struct vfsmount *nfsmnt)
+{
+ int err;
+
+ LKTRTrace("hi%lu, %.*s, 0%o, nd %d, nfsmnt %d\n",
+ h_dir->i_ino, AuDLNPair(h_dentry), mode, !!nd, !!nfsmnt);
+
+ err = -ENOSYS;
+ if (!nfsmnt)
+ err = vfsub_create(h_dir, h_dentry, mode, /*nd*/NULL, dlgt);
+
+ AuTraceErr(err);
+ return err;
+}
+#else
+struct nameidata *au_fake_dm(struct nameidata *fake_nd, struct nameidata *nd,
+ struct super_block *sb, aufs_bindex_t bindex)
+{
+ LKTRTrace("nd %p, b%d\n", nd, bindex);
+
+ if (!nd)
+ return NULL;
+
+ DiMustAnyLock(nd->path.dentry);
+
+ fake_nd->path.dentry = NULL;
+ fake_nd->path.mnt = NULL;
+
+ if (bindex <= au_dbend(nd->path.dentry))
+ fake_nd->path.dentry = au_h_dptr(nd->path.dentry, bindex);
+ if (fake_nd->path.dentry) {
+ fake_nd->path.mnt = au_sbr_mnt(sb, bindex);
+ AuDebugOn(!fake_nd->path.mnt);
+ path_get(&fake_nd->path);
+ } else
+ fake_nd = ERR_PTR(-ENOENT);
+
+ AuTraceErrPtr(fake_nd);
+ return fake_nd;
+}
+
+void au_fake_dm_release(struct nameidata *fake_nd)
+{
+ if (fake_nd)
+ path_put(&fake_nd->path);
+}
+
+int au_h_create(struct inode *h_dir, struct dentry *h_dentry, int mode,
+ int dlgt, struct nameidata *nd, struct vfsmount *nfsmnt)
+{
+ int err;
+
+ LKTRTrace("hi%lu, %.*s, 0%o, nd %d, nfsmnt %d\n",
+ h_dir->i_ino, AuDLNPair(h_dentry), mode, !!nd, !!nfsmnt);
+
+ err = -ENOSYS;
+ if (!nfsmnt)
+ err = vfsub_create(h_dir, h_dentry, mode, /*nd*/NULL, dlgt);
+ else {
+ struct nameidata fake_nd;
+
+ if (nd)
+ fake_nd = *nd;
+ else
+ memset(&fake_nd, 0, sizeof(fake_nd));
+ fake_nd.path.dentry = h_dentry;
+ fake_nd.path.mnt = nfsmnt;
+ path_get(&fake_nd.path);
+ fake_nd.flags = LOOKUP_CREATE;
+ fake_nd.intent.open.flags = O_CREAT | FMODE_READ;
+ fake_nd.intent.open.create_mode = mode;
+
+ err = vfsub_create(h_dir, h_dentry, mode, &fake_nd, dlgt);
+ path_put(&fake_nd.path);
+ }
+
+ AuTraceErr(err);
+ return err;
+}
+#endif /* CONFIG_AUFS_FAKE_DM */
+
+/* ---------------------------------------------------------------------- */
+
+int au_copy_file(struct file *dst, struct file *src, loff_t len,
+ struct super_block *sb)
+{
+ int err, all_zero;
+ unsigned long blksize;
+ char *buf;
+ struct vfsub_args vargs;
+ /* reduce stack space */
+ struct iattr *ia;
+
+ LKTRTrace("%.*s, %.*s\n",
+ AuDLNPair(dst->f_dentry), AuDLNPair(src->f_dentry));
+ AuDebugOn(!(dst->f_mode & FMODE_WRITE));
+#ifdef CONFIG_AUFS_DEBUG
+ {
+ struct dentry *parent;
+ parent = dget_parent(dst->f_dentry);
+ IMustLock(parent->d_inode);
+ dput(parent);
+ }
+#endif
+
+ err = -ENOMEM;
+ blksize = dst->f_dentry->d_sb->s_blocksize;
+ if (!blksize || PAGE_SIZE < blksize)
+ blksize = PAGE_SIZE;
+ LKTRTrace("blksize %lu\n", blksize);
+ buf = kmalloc(blksize, GFP_TEMPORARY);
+ //buf = NULL;
+ if (unlikely(!buf))
+ goto out;
+ ia = kmalloc(sizeof(*ia), GFP_TEMPORARY);
+ if (unlikely(!ia))
+ goto out_buf;
+
+#ifdef CONFIG_AUFS_DEBUG
+ if (len > (1 << 22))
+ AuWarn("copying a large file %Ld\n", (long long)len);
+#endif
+ vfsub_args_init(&vargs, NULL, au_opt_test_dlgt(au_mntflags(sb)), 0);
+ err = 0;
+ all_zero = 0;
+ src->f_pos = 0;
+ dst->f_pos = 0;
+ while (len) {
+ size_t sz, rbytes, wbytes, i;
+ char *p;
+
+ LKTRTrace("len %lld\n", len);
+ sz = blksize;
+ if (len < blksize)
+ sz = len;
+
+ /* support LSM and notify */
+ rbytes = 0;
+ // signal_pending
+ while (!rbytes || err == -EAGAIN || err == -EINTR) {
+ rbytes = vfsub_read_k(src, buf, sz, &src->f_pos,
+ vfsub_ftest(vargs.flags, DLGT));
+ err = rbytes;
+ }
+ if (unlikely(err < 0))
+ break;
+
+ all_zero = 0;
+ if (len >= rbytes && rbytes == blksize) {
+ //todo: try bitmap or memcmp()/get_zeroed_page()
+ unsigned long *ulp;
+ size_t n;
+
+ all_zero = 1;
+ ulp = (void *)buf;
+ n = rbytes / sizeof(*ulp);
+ i = n;
+ while (n-- > 0 && all_zero)
+ all_zero = !*ulp++;
+ p = (void *)ulp;
+ i *= sizeof(*ulp);
+ for (; all_zero && i < rbytes; i++)
+ all_zero = !*p++;
+ }
+ if (!all_zero) {
+ wbytes = rbytes;
+ p = buf;
+ while (wbytes) {
+ size_t b;
+ /* support LSM and notify */
+ b = vfsub_write_k(dst, p, wbytes, &dst->f_pos,
+ &vargs);
+ err = b;
+ // signal_pending
+ if (unlikely(err == -EAGAIN || err == -EINTR))
+ continue;
+ if (unlikely(err < 0))
+ break;
+ wbytes -= b;
+ p += b;
+ }
+ } else {
+ loff_t res;
+ LKTRLabel(hole);
+ res = vfsub_llseek(dst, rbytes, SEEK_CUR);
+ err = res;
+ if (unlikely(res < 0))
+ break;
+ }
+ len -= rbytes;
+ err = 0;
+ }
+
+ /* the last block may be a hole */
+ if (unlikely(!err && all_zero)) {
+ struct dentry *h_d = dst->f_dentry;
+ struct inode *h_i = h_d->d_inode;
+
+ LKTRLabel(last hole);
+ do {
+ // signal_pending
+ err = vfsub_write_k(dst, "\0", 1, &dst->f_pos, &vargs);
+ } while (err == -EAGAIN || err == -EINTR);
+ if (err == 1) {
+ ia->ia_size = dst->f_pos;
+ ia->ia_valid = ATTR_SIZE | ATTR_FILE;
+ ia->ia_file = dst;
+ mutex_lock_nested(&h_i->i_mutex, AuLsc_I_CHILD2);
+ err = vfsub_notify_change(h_d, ia, &vargs);
+ mutex_unlock(&h_i->i_mutex);
+ }
+ }
+
+ kfree(ia);
+ out_buf:
+ kfree(buf);
+ out:
+ AuTraceErr(err);
+ return err;
+}
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 60/67] aufs debug print functions, header
2008-05-16 14:33 ` [PATCH 59/67] aufs misc functions, source hooanon05
@ 2008-05-16 14:33 ` hooanon05
2008-05-16 14:33 ` [PATCH 61/67] aufs debug print functions, source hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:33 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs debug print functions, header
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/debug.h | 210 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 210 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/debug.h b/fs/aufs/debug.h
new file mode 100644
index 0000000..5e723a0
--- /dev/null
+++ b/fs/aufs/debug.h
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * debug print functions
+ *
+ * $Id: debug.h,v 1.2 2008/04/21 02:00:37 sfjro Exp $
+ */
+
+#ifndef __AUFS_DEBUG_H__
+#define __AUFS_DEBUG_H__
+
+#ifdef __KERNEL__
+
+#include <linux/fs.h>
+
+/* to debug easier, do not make it an inlined function */
+#define MtxMustLock(mtx) AuDebugOn(!mutex_is_locked(mtx))
+
+#ifdef CONFIG_AUFS_DEBUG
+/* sparse warns about pointer */
+#define AuDebugOn(a) BUG_ON(!!(a))
+extern atomic_t au_cond;
+#define au_debug_on() atomic_inc_return(&au_cond)
+#define au_debug_off() atomic_dec_return(&au_cond)
+static inline int au_debug_test(void)
+{
+ return atomic_read(&au_cond);
+}
+#else
+#define AuDebugOn(a) do {} while (0)
+#define au_debug_on() do {} while (0)
+#define au_debug_off() do {} while (0)
+static inline int au_debug_test(void)
+{
+ return 0;
+}
+#endif /* CONFIG_AUFS_DEBUG */
+
+/* ---------------------------------------------------------------------- */
+
+/* debug print */
+#if defined(CONFIG_LKTR) || defined(CONFIG_LKTR_MODULE)
+#include <linux/lktr.h>
+#ifdef CONFIG_AUFS_DEBUG
+#undef LktrCond
+#define LktrCond unlikely(au_debug_test() || (lktr_cond && lktr_cond()))
+#endif
+#else
+#define LktrCond au_debug_test()
+#define LKTRDumpVma(pre, vma, suf) do {} while (0)
+#define LKTRDumpStack() do {} while (0)
+#define LKTRTrace(fmt, args...) do { \
+ if (LktrCond) \
+ AuDbg(fmt, ##args); \
+} while (0)
+#define LKTRLabel(label) LKTRTrace("%s\n", #label)
+#endif /* CONFIG_LKTR */
+
+#define AuTraceErr(e) do { \
+ if (unlikely((e) < 0)) \
+ LKTRTrace("err %d\n", (int)(e)); \
+} while (0)
+
+#define AuTraceErrPtr(p) do { \
+ if (IS_ERR(p)) \
+ LKTRTrace("err %ld\n", PTR_ERR(p)); \
+} while (0)
+
+#define AuTraceEnter() LKTRLabel(enter)
+
+/* dirty macros for debug print, use with "%.*s" and caution */
+#define AuLNPair(qstr) (qstr)->len, (qstr)->name
+#define AuDLNPair(d) AuLNPair(&(d)->d_name)
+
+/* ---------------------------------------------------------------------- */
+
+#define AuDpri(lvl, fmt, arg...) \
+ printk(lvl AUFS_NAME " %s:%d:%s[%d]: " fmt, \
+ __func__, __LINE__, current->comm, current->pid, ##arg)
+#define AuDbg(fmt, arg...) AuDpri(KERN_DEBUG, fmt, ##arg)
+#define AuInfo(fmt, arg...) AuDpri(KERN_INFO, fmt, ##arg)
+#define AuWarn(fmt, arg...) AuDpri(KERN_WARNING, fmt, ##arg)
+#define AuErr(fmt, arg...) AuDpri(KERN_ERR, fmt, ##arg)
+#define AuIOErr(fmt, arg...) AuErr("I/O Error, " fmt, ##arg)
+#define AuIOErrWhck(fmt, arg...) AuErr("I/O Error, try whck. " fmt, ##arg)
+#define AuWarn1(fmt, arg...) do { \
+ static unsigned char _c; \
+ if (!_c++) AuWarn(fmt, ##arg); \
+} while (0)
+
+#define AuErr1(fmt, arg...) do { \
+ static unsigned char _c; \
+ if (!_c++) AuErr(fmt, ##arg); \
+} while (0)
+
+#define AuIOErr1(fmt, arg...) do { \
+ static unsigned char _c; \
+ if (!_c++) AuIOErr(fmt, ##arg); \
+} while (0)
+
+#define AuUnsupportMsg "This operation is not supported." \
+ " Please report this application to aufs-users ML."
+#define AuUnsupport(fmt, args...) do { \
+ AuErr(AuUnsupportMsg "\n" fmt, ##args); \
+ dump_stack(); \
+} while (0)
+
+/* ---------------------------------------------------------------------- */
+
+struct au_sbinfo;
+#ifdef CONFIG_AUFS_DEBUG
+extern char *au_plevel;
+struct au_nhash;
+void au_dpri_whlist(struct au_nhash *whlist);
+struct au_vdir;
+void au_dpri_vdir(struct au_vdir *vdir);
+void au_dpri_inode(struct inode *inode);
+void au_dpri_dentry(struct dentry *dentry);
+void au_dpri_file(struct file *filp);
+void au_dpri_sb(struct super_block *sb);
+void au_dbg_sleep(int sec);
+int __init au_debug_init(void);
+void au_debug_sbinfo_init(struct au_sbinfo *sbinfo);
+#define AuDbgWhlist(w) do { \
+ LKTRTrace(#w "\n"); \
+ au_dpri_whlist(w); \
+} while (0)
+
+#define AuDbgVdir(v) do { \
+ LKTRTrace(#v "\n"); \
+ au_dpri_vdir(v); \
+} while (0)
+
+#define AuDbgInode(i) do { \
+ LKTRTrace(#i "\n"); \
+ au_dpri_inode(i); \
+} while (0)
+
+#define AuDbgDentry(d) do { \
+ LKTRTrace(#d "\n"); \
+ au_dpri_dentry(d); \
+} while (0)
+
+#define AuDbgFile(f) do { \
+ LKTRTrace(#f "\n"); \
+ au_dpri_file(f); \
+} while (0)
+
+#define AuDbgSb(sb) do { \
+ LKTRTrace(#sb "\n"); \
+ au_dpri_sb(sb); \
+} while (0)
+
+#define AuDbgSleep(sec) do { \
+ AuDbg("sleep %d sec\n", sec); \
+ au_dbg_sleep(sec); \
+} while (0)
+#else
+static inline int au_debug_init(void)
+{
+ return 0;
+}
+static inline void au_debug_sbinfo_init(struct au_sbinfo *sbinfo)
+{
+ /* empty */
+}
+#define AuDbgWhlist(w) do {} while (0)
+#define AuDbgVdir(v) do {} while (0)
+#define AuDbgInode(i) do {} while (0)
+#define AuDbgDentry(d) do {} while (0)
+#define AuDbgFile(f) do {} while (0)
+#define AuDbgSb(sb) do {} while (0)
+#define AuDbgSleep(sec) do {} while (0)
+#endif /* CONFIG_AUFS_DEBUG */
+
+#ifdef DbgUdbaRace
+#define AuDbgSleep_UdbaRace() AuDbgSleep(DbgUdbaRace)
+#else
+#define AuDbgSleep_UdbaRace() do {} while (0)
+#endif
+
+#ifdef CONFIG_AUFS_MAGIC_SYSRQ
+int __init au_sysrq_init(void);
+void au_sysrq_fin(void);
+#else
+static inline int au_sysrq_init(void)
+{
+ return 0;
+}
+#define au_sysrq_fin() do {} while (0)
+#endif
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_DEBUG_H__ */
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 61/67] aufs debug print functions, source
2008-05-16 14:33 ` [PATCH 60/67] aufs debug print functions, header hooanon05
@ 2008-05-16 14:33 ` hooanon05
2008-05-16 14:33 ` [PATCH 62/67] aufs magic sysrq handler hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:33 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs debug print functions, source
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/debug.c | 485 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 485 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/debug.c b/fs/aufs/debug.c
new file mode 100644
index 0000000..e6e178d
--- /dev/null
+++ b/fs/aufs/debug.c
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) 2005-2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * debug print functions
+ *
+ * $Id: debug.c,v 1.2 2008/04/21 01:33:00 sfjro Exp $
+ */
+
+//#include <linux/errno.h>
+#include "aufs.h"
+
+atomic_t au_cond = ATOMIC_INIT(0);
+
+char *au_plevel = KERN_DEBUG;
+#define dpri(fmt, arg...) do { \
+ if (LktrCond) \
+ printk("%s" fmt, au_plevel, ##arg); \
+} while (0)
+
+/* ---------------------------------------------------------------------- */
+
+void au_dpri_whlist(struct au_nhash *whlist)
+{
+ int i;
+ struct hlist_head *head;
+ struct au_vdir_wh *tpos;
+ struct hlist_node *pos;
+
+ for (i = 0; i < AuSize_NHASH; i++) {
+ head = whlist->heads + i;
+ hlist_for_each_entry(tpos, pos, head, wh_hash)
+ dpri("b%d, %.*s, %d\n",
+ tpos->wh_bindex,
+ tpos->wh_str.len, tpos->wh_str.name,
+ tpos->wh_str.len);
+ }
+}
+
+void au_dpri_vdir(struct au_vdir *vdir)
+{
+ int i;
+ union au_vdir_deblk_p p;
+ unsigned char *o;
+
+ if (!vdir || IS_ERR(vdir)) {
+ dpri("err %ld\n", PTR_ERR(vdir));
+ return;
+ }
+
+ dpri("nblk %d, deblk %p, last{%d, %p}, ver %lu\n",
+ vdir->vd_nblk, vdir->vd_deblk,
+ vdir->vd_last.i, vdir->vd_last.p.p, vdir->vd_version);
+ for (i = 0; i < vdir->vd_nblk; i++) {
+ p.deblk = vdir->vd_deblk[i];
+ o = p.p;
+ dpri("[%d]: %p\n", i, o);
+#if 0 // verbose
+ int j;
+ for (j = 0; j < 8; j++) {
+ dpri("%p(+%d) {%02x %02x %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x %02x %02x}\n",
+ p.p, p.p - o,
+ p.p[0], p.p[1], p.p[2], p.p[3],
+ p.p[4], p.p[5], p.p[6], p.p[7],
+ p.p[8], p.p[9], p.p[10], p.p[11],
+ p.p[12], p.p[13], p.p[14], p.p[15]);
+ p.p += 16;
+ }
+#endif
+ }
+}
+
+static int do_pri_inode(aufs_bindex_t bindex, struct inode *inode,
+ struct dentry *wh)
+{
+ char *n = NULL;
+ int l = 0, ntfy = 0;
+
+ if (!inode || IS_ERR(inode)) {
+ dpri("i%d: err %ld\n", bindex, PTR_ERR(inode));
+ return -1;
+ }
+
+ /* the type of i_blocks depends upon CONFIG_LSF */
+ BUILD_BUG_ON(sizeof(inode->i_blocks) != sizeof(unsigned long)
+ && sizeof(inode->i_blocks) != sizeof(u64));
+ if (wh) {
+ n = (void *)wh->d_name.name;
+ l = wh->d_name.len;
+ }
+
+ ntfy = au_test_inotify(inode);
+ dpri("i%d: i%lu, %s, cnt %d, nl %u, 0%o, ntfy %d, sz %Lu, blk %Lu,"
+ " ct %Ld, np %lu, st 0x%lx, f 0x%x, g %x%s%.*s\n",
+ bindex,
+ inode->i_ino, inode->i_sb ? au_sbtype(inode->i_sb) : "??",
+ atomic_read(&inode->i_count), inode->i_nlink, inode->i_mode,
+ ntfy,
+ i_size_read(inode), (u64)inode->i_blocks,
+ timespec_to_ns(&inode->i_ctime) & 0x0ffff,
+ inode->i_mapping ? inode->i_mapping->nrpages : 0,
+ inode->i_state, inode->i_flags, inode->i_generation,
+ l ? ", wh " : "", l, n);
+ return 0;
+}
+
+void au_dpri_inode(struct inode *inode)
+{
+ struct au_iinfo *iinfo;
+ aufs_bindex_t bindex;
+ int err;
+
+ err = do_pri_inode(-1, inode, NULL);
+ if (err || !au_test_aufs(inode->i_sb))
+ return;
+
+ iinfo = au_ii(inode);
+ if (!iinfo)
+ return;
+ dpri("i-1: bstart %d, bend %d, gen %d\n",
+ iinfo->ii_bstart, iinfo->ii_bend, au_iigen(inode));
+ if (iinfo->ii_bstart < 0)
+ return;
+ for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; bindex++)
+ do_pri_inode(bindex, iinfo->ii_hinode[0 + bindex].hi_inode,
+ iinfo->ii_hinode[0 + bindex].hi_whdentry);
+}
+
+static int do_pri_dentry(aufs_bindex_t bindex, struct dentry *dentry,
+ struct list_head *intent)
+{
+ struct dentry *wh = NULL;
+
+ if (!dentry || IS_ERR(dentry)) {
+ dpri("d%d: err %ld\n", bindex, PTR_ERR(dentry));
+ return -1;
+ }
+ /* do not call dget_parent() here */
+ dpri("d%d: %.*s/%.*s, %s, cnt %d, flags 0x%x, intent %d\n",
+ bindex,
+ AuDLNPair(dentry->d_parent), AuDLNPair(dentry),
+ dentry->d_sb ? au_sbtype(dentry->d_sb) : "??",
+ atomic_read(&dentry->d_count), dentry->d_flags, !!intent);
+ if (bindex >= 0 && dentry->d_inode && au_test_aufs(dentry->d_sb)) {
+ struct au_iinfo *iinfo = au_ii(dentry->d_inode);
+ if (iinfo)
+ wh = iinfo->ii_hinode[0 + bindex].hi_whdentry;
+ }
+ do_pri_inode(bindex, dentry->d_inode, wh);
+ return 0;
+}
+
+static struct list_head *au_dbg_h_intent(struct au_dinfo *dinfo,
+ aufs_bindex_t bindex)
+{
+#ifdef CONFIG_AUFS_BR_NFS
+ return dinfo->di_hdentry[0 + bindex].hd_intent_list;
+#else
+ return NULL;
+#endif
+}
+
+void au_dpri_dentry(struct dentry *dentry)
+{
+ struct au_dinfo *dinfo;
+ aufs_bindex_t bindex;
+ int err;
+
+ err = do_pri_dentry(-1, dentry, NULL);
+ if (err || !au_test_aufs(dentry->d_sb))
+ return;
+
+ dinfo = au_di(dentry);
+ if (!dinfo)
+ return;
+ dpri("d-1: bstart %d, bend %d, bwh %d, bdiropq %d, gen %d\n",
+ dinfo->di_bstart, dinfo->di_bend,
+ dinfo->di_bwh, dinfo->di_bdiropq, au_digen(dentry));
+ if (dinfo->di_bstart < 0)
+ return;
+ for (bindex = dinfo->di_bstart; bindex <= dinfo->di_bend; bindex++)
+ do_pri_dentry(bindex, dinfo->di_hdentry[0 + bindex].hd_dentry,
+ au_dbg_h_intent(dinfo, bindex));
+}
+
+static int do_pri_file(aufs_bindex_t bindex, struct file *file)
+{
+ char a[32];
+
+ if (!file || IS_ERR(file)) {
+ dpri("f%d: err %ld\n", bindex, PTR_ERR(file));
+ return -1;
+ }
+ a[0] = 0;
+ if (bindex < 0
+ && file->f_dentry
+ && au_test_aufs(file->f_dentry->d_sb)
+ && au_fi(file))
+ snprintf(a, sizeof(a), ", mmapped %d", au_test_mmapped(file));
+ dpri("f%d: mode 0x%x, flags 0%o, cnt %d, pos %Lu%s\n",
+ bindex, file->f_mode, file->f_flags, file_count(file),
+ file->f_pos, a);
+ if (file->f_dentry)
+ do_pri_dentry(bindex, file->f_dentry, NULL);
+ return 0;
+}
+
+void au_dpri_file(struct file *file)
+{
+ struct au_finfo *finfo;
+ aufs_bindex_t bindex;
+ int err;
+
+ err = do_pri_file(-1, file);
+ if (err || !file->f_dentry || !au_test_aufs(file->f_dentry->d_sb))
+ return;
+
+ finfo = au_fi(file);
+ if (!finfo)
+ return;
+ if (finfo->fi_bstart < 0)
+ return;
+ for (bindex = finfo->fi_bstart; bindex <= finfo->fi_bend; bindex++) {
+ struct au_hfile *hf;
+ //dpri("bindex %d\n", bindex);
+ hf = finfo->fi_hfile + bindex;
+ do_pri_file(bindex, hf ? hf->hf_file : NULL);
+ }
+}
+
+static int do_pri_br(aufs_bindex_t bindex, struct au_branch *br)
+{
+ struct vfsmount *mnt;
+ struct super_block *sb;
+
+ if (!br || IS_ERR(br)
+ || !(mnt = br->br_mnt) || IS_ERR(mnt)
+ || !(sb = mnt->mnt_sb) || IS_ERR(sb)) {
+ dpri("s%d: err %ld\n", bindex, PTR_ERR(br));
+ return -1;
+ }
+
+ dpri("s%d: {perm 0x%x, cnt %d}, "
+ "%s, flags 0x%lx, cnt(BIAS) %d, active %d, xino %d\n",
+ bindex, br->br_perm, au_br_count(br),
+ au_sbtype(sb), sb->s_flags, sb->s_count - S_BIAS,
+ atomic_read(&sb->s_active), !!br->br_xino);
+ return 0;
+}
+
+void au_dpri_sb(struct super_block *sb)
+{
+ struct au_sbinfo *sbinfo;
+ aufs_bindex_t bindex;
+ int err;
+ struct vfsmount mnt = { .mnt_sb = sb };
+ struct au_branch fake = {
+ .br_perm = 0,
+ .br_mnt = &mnt,
+ .br_count = ATOMIC_INIT(0),
+ .br_xino = NULL
+ };
+
+ atomic_set(&fake.br_count, 0);
+ smp_mb(); /* atomic_set */
+ err = do_pri_br(-1, &fake);
+ dpri("dev 0x%x\n", sb->s_dev);
+ if (err || !au_test_aufs(sb))
+ return;
+
+ sbinfo = au_sbi(sb);
+ if (!sbinfo)
+ return;
+ for (bindex = 0; bindex <= sbinfo->si_bend; bindex++) {
+ //dpri("bindex %d\n", bindex);
+ do_pri_br(bindex, sbinfo->si_branch[0 + bindex]);
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+
+void au_dbg_sleep(int sec)
+{
+ static DECLARE_WAIT_QUEUE_HEAD(wq);
+ wait_event_timeout(wq, 0, sec * HZ);
+}
+
+/* ---------------------------------------------------------------------- */
+
+void au_debug_sbinfo_init(struct au_sbinfo *sbinfo)
+{
+#ifdef ForceInotify
+ au_opt_set_udba(sbinfo->si_mntflags, UDBA_INOTIFY);
+#endif
+#ifdef ForceDlgt
+ au_opt_set(sbinfo->si_mntflags, DLGT);
+#endif
+#ifdef ForceNoPlink
+ au_opt_clr(sbinfo->si_mntflags, PLINK);
+#endif
+#ifdef ForceNoXino
+ au_opt_clr(sbinfo->si_mntflags, XINO);
+#endif
+#ifdef ForceNoRefrof
+ au_opt_clr(sbinfo->si_mntflags, REFROF);
+#endif
+}
+
+int __init au_debug_init(void)
+{
+ aufs_bindex_t bindex;
+ struct au_vdir_destr destr;
+
+ bindex = -1;
+ AuDebugOn(bindex >= 0);
+
+ destr.len = -1;
+ AuDebugOn(destr.len < NAME_MAX);
+
+#ifdef CONFIG_4KSTACKS
+ AuWarn("CONFIG_4KSTACKS is defined.\n");
+#endif
+
+#if 0 // verbose debug
+ {
+ union {
+ struct au_branch *br;
+ struct au_dinfo *di;
+ struct au_finfo *fi;
+ struct au_iinfo *ii;
+ struct au_hinode *hi;
+ struct au_sbinfo *si;
+ struct au_vdir_destr *destr;
+ struct au_vdir_de *de;
+ struct au_vdir_wh *wh;
+ struct au_vdir *vd;
+ } u;
+
+ pr_info("br{"
+ "xino %d, "
+ "id %d, perm %d, mnt %d, count %d, "
+ "wh_sem %d, wh %d, run %d, plink %d, "
+ "xup %d, xrun %d, "
+ "by %d, "
+ "gen %d, "
+ "sa %d} %d\n",
+ offsetof(typeof(*u.br), br_xino),
+ offsetof(typeof(*u.br), br_id),
+ offsetof(typeof(*u.br), br_perm),
+ offsetof(typeof(*u.br), br_mnt),
+ offsetof(typeof(*u.br), br_count),
+ offsetof(typeof(*u.br), br_wh_rwsem),
+ offsetof(typeof(*u.br), br_wh),
+ offsetof(typeof(*u.br), br_wh_running),
+ offsetof(typeof(*u.br), br_plink),
+ offsetof(typeof(*u.br), br_xino_upper),
+ offsetof(typeof(*u.br), br_xino_running),
+ offsetof(typeof(*u.br), br_bytes),
+ offsetof(typeof(*u.br), br_generation),
+ offsetof(typeof(*u.br), br_sabr),
+ sizeof(*u.br));
+ pr_info("di{gen %d, rwsem %d, bstart %d, bend %d, bwh %d, "
+ "bdiropq %d, hdentry %d} %d\n",
+ offsetof(typeof(*u.di), di_generation),
+ offsetof(typeof(*u.di), di_rwsem),
+ offsetof(typeof(*u.di), di_bstart),
+ offsetof(typeof(*u.di), di_bend),
+ offsetof(typeof(*u.di), di_bwh),
+ offsetof(typeof(*u.di), di_bdiropq),
+ offsetof(typeof(*u.di), di_hdentry),
+ sizeof(*u.di));
+ pr_info("fi{gen %d, rwsem %d, hfile %d, bstart %d, bend %d, "
+ "h_vm_ops %d, vdir_cach %d} %d\n",
+ offsetof(typeof(*u.fi), fi_generation),
+ offsetof(typeof(*u.fi), fi_rwsem),
+ offsetof(typeof(*u.fi), fi_hfile),
+ offsetof(typeof(*u.fi), fi_bstart),
+ offsetof(typeof(*u.fi), fi_bend),
+ offsetof(typeof(*u.fi), fi_h_vm_ops),
+ offsetof(typeof(*u.fi), fi_vdir_cache),
+ sizeof(*u.fi));
+ pr_info("ii{gen %d, hsb %d, "
+ "rwsem %d, bstart %d, bend %d, hinode %d, vdir %d} "
+ "%d\n",
+ offsetof(typeof(*u.ii), ii_generation),
+ offsetof(typeof(*u.ii), ii_hsb1),
+ offsetof(typeof(*u.ii), ii_rwsem),
+ offsetof(typeof(*u.ii), ii_bstart),
+ offsetof(typeof(*u.ii), ii_bend),
+ offsetof(typeof(*u.ii), ii_hinode),
+ offsetof(typeof(*u.ii), ii_vdir),
+ sizeof(*u.ii));
+ pr_info("hi{inode %d, id %d, notify %d, wh %d} %d\n",
+ offsetof(typeof(*u.hi), hi_inode),
+ offsetof(typeof(*u.hi), hi_id),
+ offsetof(typeof(*u.hi), hi_notify),
+ offsetof(typeof(*u.hi), hi_whdentry),
+ sizeof(*u.hi));
+ pr_info("si{nwt %d, rwsem %d, gen %d, stat %d, "
+ "bend %d, last id %d, br %d, "
+ "cpup %d, creat %d, ops %d, ops %d, "
+ "rr %d, mfs %d, "
+ "mntflags %d, "
+ "xread %d, xwrite %d, xib %d, xmtx %d, buf %d, "
+ "xlast %d, xnext %d, "
+ "rdcache %d, "
+ "dirwh %d, "
+ "pl_lock %d, pl %d, "
+ "mnt %d, "
+ "l %d, sys %d, "
+ //"lvma_l %d, lvma %d"
+ "} %d\n",
+ offsetof(typeof(*u.si), si_nowait),
+ offsetof(typeof(*u.si), si_rwsem),
+ offsetof(typeof(*u.si), si_generation),
+ offsetof(typeof(*u.si), au_si_status),
+ offsetof(typeof(*u.si), si_bend),
+ offsetof(typeof(*u.si), si_last_br_id),
+ offsetof(typeof(*u.si), si_branch),
+ offsetof(typeof(*u.si), si_wbr_copyup),
+ offsetof(typeof(*u.si), si_wbr_create),
+ offsetof(typeof(*u.si), si_wbr_copyup_ops),
+ offsetof(typeof(*u.si), si_wbr_create_ops),
+ offsetof(typeof(*u.si), si_wbr_rr_next),
+ offsetof(typeof(*u.si), si_wbr_mfs),
+ offsetof(typeof(*u.si), si_mntflags),
+ offsetof(typeof(*u.si), si_xread),
+ offsetof(typeof(*u.si), si_xwrite),
+ offsetof(typeof(*u.si), si_xib),
+ offsetof(typeof(*u.si), si_xib_mtx),
+ offsetof(typeof(*u.si), si_xib_buf),
+ offsetof(typeof(*u.si), si_xib_last_pindex),
+ offsetof(typeof(*u.si), si_xib_next_bit),
+ offsetof(typeof(*u.si), si_rdcache),
+ offsetof(typeof(*u.si), si_dirwh),
+ offsetof(typeof(*u.si), si_plink_lock),
+ offsetof(typeof(*u.si), si_plink),
+ offsetof(typeof(*u.si), si_mnt),
+ offsetof(typeof(*u.si), si_list),
+ offsetof(typeof(*u.si), si_sa),
+ //offsetof(typeof(*u.si), si_lvma_lock),
+ //offsetof(typeof(*u.si), si_lvma),
+ sizeof(*u.si));
+ pr_info("destr{len %d, name %d} %d\n",
+ offsetof(typeof(*u.destr), len),
+ offsetof(typeof(*u.destr), name),
+ sizeof(*u.destr));
+ pr_info("de{ino %d, type %d, str %d} %d\n",
+ offsetof(typeof(*u.de), de_ino),
+ offsetof(typeof(*u.de), de_type),
+ offsetof(typeof(*u.de), de_str),
+ sizeof(*u.de));
+ pr_info("wh{hash %d, bindex %d, str %d} %d\n",
+ offsetof(typeof(*u.wh), wh_hash),
+ offsetof(typeof(*u.wh), wh_bindex),
+ offsetof(typeof(*u.wh), wh_str),
+ sizeof(*u.wh));
+ pr_info("vd{deblk %d, nblk %d, last %d, ver %d, jiffy %d} %d\n",
+ offsetof(typeof(*u.vd), vd_deblk),
+ offsetof(typeof(*u.vd), vd_nblk),
+ offsetof(typeof(*u.vd), vd_last),
+ offsetof(typeof(*u.vd), vd_version),
+ offsetof(typeof(*u.vd), vd_jiffy),
+ sizeof(*u.vd));
+ }
+#endif
+
+ return 0;
+}
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 62/67] aufs magic sysrq handler
2008-05-16 14:33 ` [PATCH 61/67] aufs debug print functions, source hooanon05
@ 2008-05-16 14:33 ` hooanon05
2008-05-16 14:33 ` [PATCH 63/67] aufs mount helper hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:33 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs magic sysrq handler
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/aufs/sysrq.c | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 112 insertions(+), 0 deletions(-)
diff --git a/fs/aufs/sysrq.c b/fs/aufs/sysrq.c
new file mode 100644
index 0000000..c50c885
--- /dev/null
+++ b/fs/aufs/sysrq.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2008 Junjiro Okajima
+ *
+ * This program, aufs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * magic sysrq hanlder
+ *
+ * $Id: sysrq.c,v 1.4 2008/05/04 23:53:27 sfjro Exp $
+ */
+
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/sysrq.h>
+#include "aufs.h"
+
+static void sysrq_sb(struct super_block *sb)
+{
+ char *plevel;
+ struct inode *i;
+
+ plevel = au_plevel;
+ au_plevel = KERN_WARNING;
+ au_debug_on();
+
+ pr_warning(AUFS_NAME ": superblock\n");
+ au_dpri_sb(sb);
+ pr_warning(AUFS_NAME ": root dentry\n");
+ au_dpri_dentry(sb->s_root);
+#if 0
+ pr_warning(AUFS_NAME ": root inode\n");
+ au_dpri_inode(sb->s_root->d_inode);
+#endif
+#if 1
+ pr_warning(AUFS_NAME ": isolated inode\n");
+ list_for_each_entry(i, &sb->s_inodes, i_sb_list)
+ if (list_empty(&i->i_dentry))
+ au_dpri_inode(i);
+#endif
+
+ au_plevel = plevel;
+ au_debug_off();
+}
+
+/* ---------------------------------------------------------------------- */
+
+/* module parameter */
+static char *aufs_sysrq_key = "a";
+module_param_named(sysrq, aufs_sysrq_key, charp, S_IRUGO);
+MODULE_PARM_DESC(sysrq, "MagicSysRq key for " AUFS_NAME);
+
+static void au_sysrq(int key, struct tty_struct *tty)
+{
+ struct au_sbinfo *sbinfo;
+
+#if 0
+ if (au_debug_test())
+ au_debug_off();
+ else
+ au_debug_on();
+#endif
+
+ //mutex_lock(&au_sbilist_mtx);
+ list_for_each_entry(sbinfo, &au_sbilist, si_list)
+ sysrq_sb(sbinfo->si_sb);
+ //mutex_unlock(&au_sbilist_mtx);
+}
+
+static struct sysrq_key_op au_sysrq_op = {
+ .handler = au_sysrq,
+ .help_msg = "Aufs",
+ .action_msg = "Aufs",
+ .enable_mask = SYSRQ_ENABLE_DUMP //??
+};
+
+/* ---------------------------------------------------------------------- */
+
+int __init au_sysrq_init(void)
+{
+ int err;
+ char key;
+
+ err = -1;
+ key = *aufs_sysrq_key;
+ if ('a' <= key && key <= 'z')
+ err = register_sysrq_key(key, &au_sysrq_op);
+ if (unlikely(err))
+ AuErr("err %d, sysrq=%c\n", err, key);
+ return err;
+}
+
+void au_sysrq_fin(void)
+{
+ int err;
+ err = unregister_sysrq_key(*aufs_sysrq_key, &au_sysrq_op);
+ if (unlikely(err))
+ AuErr("err %d (ignored)\n", err);
+}
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 63/67] aufs mount helper
2008-05-16 14:33 ` [PATCH 62/67] aufs magic sysrq handler hooanon05
@ 2008-05-16 14:33 ` hooanon05
2008-05-16 14:33 ` [PATCH 64/67] aufs pseudo-link helper hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:33 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs mount helper
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
Documentation/filesystems/aufs/mount.aufs | 205 +++++++++++++++++++++++++++++
1 files changed, 205 insertions(+), 0 deletions(-)
diff --git a/Documentation/filesystems/aufs/mount.aufs b/Documentation/filesystems/aufs/mount.aufs
new file mode 100755
index 0000000..28db51e
--- /dev/null
+++ b/Documentation/filesystems/aufs/mount.aufs
@@ -0,0 +1,205 @@
+#!/bin/sh -
+
+# Copyright (C) 2005, 2006, 2007 Junjiro Okajima
+#
+# This program, aufs is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+# $Id: mount.aufs,v 1.17 2008/03/31 07:44:10 sfjro Exp $
+
+#
+# The main purpose of this script is updating /etc/mtab and calling auplilnk.
+# This script is highly depending on mount(8) in util-linux-2.12p package.
+# TODO: rewrite in C.
+# TODO: updating mtab for bind-mount is not fully supportetd.
+#
+
+PATH=/usr/bin:/usr/sbin:/bin:/sbin
+export PATH
+eecho() { echo "$@" 1>&2; }
+
+conv() # [escape]
+{
+ sed -r -e '
+ s/\\/\\134/g
+ s/$/\\012/
+ ' |
+ tr -d '\n' |
+ sed -r -e '
+ s/ /\\040/g
+ s/\t/\\011/g
+ s/\r/\\015/g
+ s/\\012$//
+ ' |
+ { test "$1" = "escape" && sed -r -e 's/\\/\\\\/g' || cat; }
+ echo
+}
+
+########################################
+
+set -e
+#set -x; echo $0 $@
+tmp=/tmp/$$
+fstype=`basename "$0" | cut -f2 -d.`
+
+########################################
+# mount(8) always passes the arguments in this order
+
+do_update=1
+verbose=0
+dev="$1"
+e_dev=`echo "$dev" | conv escape`
+cd "$2"
+mntpnt=`readlink -f "$PWD"`
+e_mntpnt=`echo "$mntpnt" | conv escape`
+cd "$OLDPWD"
+shift 2
+test "$1" = "-n" && do_update=0 && shift
+test "$1" = "-v" && verbose=1 && shift
+test "$1" = "-o" || { eecho Bad arg. "$@"; exit 1; }
+shift
+remount=0
+echo "$@" | grep -q ',remount[,$]' && remount=1
+
+f=/etc/mtab
+test ! -r $f -o \( $do_update -eq 1 -a ! -w $f \) &&
+eecho ${f}: Permission denied &&
+exit 1
+
+f=/proc/mounts
+test \( $do_update -eq 1 -o $verbose -eq 1 \) -a ! -r $f &&
+eecho ${f}: Permission denied. &&
+exit 1
+
+oldmtab=
+> $tmp.old_opt
+if [ $remount -eq 1 ]
+then
+oldmtab=`grep "^$e_dev $e_mntpnt $fstype .*,si=" $f | tail -n 1`
+echo "$oldmtab" |
+sed -e '
+s:^'"$dev $mntpnt $fstype"' ::
+s/\( [0-9]*\)\{2\}$//
+' | tr ',' '\n' > $tmp.old_opt
+#test -s $tmp.old_opt
+eval `grep '^si=' $tmp.old_opt`
+
+# append brs from sysfs
+si_dir=/sys/fs/aufs/si_$si
+if [ -r $si_dir/br0 ]
+then
+ cd $si_dir
+ {
+ ls -1 br* |
+ cut -c3- |
+ sort -n |
+ sed -e 's/^/br/' |
+ xargs -n 200 sed -e 's/^/:/' |
+ tr -d '\n' |
+ sed -e 's/^/br/'
+ echo # last newline
+ } >> $tmp.old_opt
+ cd $OLDPWD
+ #cat $tmp.old_opt; #rm -f $tmp $tmp.*; exit
+fi
+fi
+
+echo "$@" | tr ',' '\n' > $tmp.new_opt
+remount=
+fgrep -v remount $tmp.new_opt > $tmp #|| :
+diff -q $tmp.new_opt $tmp > /dev/null ||
+{
+ mv $tmp $tmp.new_opt
+ remount="remount,"
+}
+#cat $tmp.new_opt; rm -f $tmp $tmp.*; exit
+
+#more $tmp.old_opt $tmp.new_opt
+real_opt=`comm -13 $tmp.old_opt $tmp.new_opt | tee $tmp | paste -s -d, -`
+real_opt="${remount}${real_opt}"
+test "$real_opt" = "" &&
+eecho bad arg "$@" &&
+rm -f $tmp $tmp.* &&
+exit 1
+#echo ${real_opt}; exit
+
+test "$remount" &&
+egrep -q '^((add|ins|append|prepend|del)[:=]|(mod|imod)[:=][^,]*=ro|(noplink|ro)$)' $tmp &&
+auplink "$mntpnt" flush
+rm -f $tmp $tmp.*
+
+########################################
+# actual mount operation
+
+set +e
+mount -in -t $fstype -o "$real_opt" "$dev" "$mntpnt"
+err=$?
+set -e
+
+test $do_update -eq 0 -a $verbose -eq 0 && exit $err
+
+########################################
+# in order to handle '-v' option, keep going even if we failed
+
+failure()
+{
+ eecho failure "$1" $?
+ exit 1
+}
+
+newmtab=`{ grep "^$e_dev $e_mntpnt $fstype" /proc/mounts ||
+ failure "bad /proc/mounts"; } |
+ tail -n 1 | sed -e 's:^'"$e_dev $e_mntpnt $fstype"' ::'`
+digits=`echo "$newmtab" | rev | cut -f-2 -d' ' | rev`
+newmtab=`echo "$newmtab" | rev | cut -f3- -d' ' | rev | conv escape`
+newmtab="$e_dev $e_mntpnt $fstype $newmtab $digits"
+
+#if [ $do_update -eq 1 -a -w /etc -a -w /etc/mtab ]
+if [ $do_update -eq 1 ]
+then
+lock_pid=/etc/mtab~$$
+lock=/etc/mtab~
+f=/etc/mtab.aufs.tmp
+touch $lock_pid || failure "creating pid lockfile"
+
+# re-define
+failure()
+{
+ eecho failure "$1" $?
+ test $err -eq 0 && eecho failed updating /etc/mtab
+ exit 1
+}
+#trap "rm -f $lock; failure" `seq 32`
+
+ln $lock_pid $lock || failure "linking lockfile"
+rm $lock_pid || failure "removing pid lockfile"
+
+if [ "$oldmtab" = "" ]
+then
+ { cat /etc/mtab; echo "$newmtab"; } |
+ dd of=$f 2> /dev/null || failure "appending $f"
+else
+ tac /etc/mtab |
+ awk -v new="$newmtab" -v mntpnt="$e_mntpnt" -v fstype="$fstype" '
+ !done && $2 == mntpnt && $3 == fstype {print new; done = 1; next}
+ {print}
+ ' | tac > $f || failure "creating $f"
+fi
+mv $f /etc/mtab || failure "renaming from $f"
+rm $lock || failure "removing lockfile"
+fi
+
+test $verbose -eq 1 &&
+echo "$dev" on "$mntpnt" type "$fstype" \(`echo "$newmtab" | cut -f4 -d' '`\)
+exit $err
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 64/67] aufs pseudo-link helper
2008-05-16 14:33 ` [PATCH 63/67] aufs mount helper hooanon05
@ 2008-05-16 14:33 ` hooanon05
2008-05-16 14:33 ` [PATCH 65/67] aufs pseudo-link helper for symlink hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:33 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs pseudo-link helper
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
Documentation/filesystems/aufs/auplink | 170 ++++++++++++++++++++++++++++++++
1 files changed, 170 insertions(+), 0 deletions(-)
diff --git a/Documentation/filesystems/aufs/auplink b/Documentation/filesystems/aufs/auplink
new file mode 100755
index 0000000..52daf3f
--- /dev/null
+++ b/Documentation/filesystems/aufs/auplink
@@ -0,0 +1,170 @@
+#!/bin/sh
+
+# Copyright (C) 2006, 2007, 2008 Junjiro Okajima
+#
+# This program, aufs is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+# $Id: auplink,v 1.9 2008/03/31 07:44:10 sfjro Exp $
+
+#set -x; echo $0 $@
+tmp=/tmp/$$
+set -e
+
+Usage()
+{
+ cat <<- EOF 1>&2
+ usage: $0 aufs_mount_point list|cpup|flush
+ 'list' shows the pseudo-linked inode numbers and filenames.
+ 'cpup' copies-up all pseudo-link to the writeble branch.
+ 'flush' calls 'cpup', and then "mount -o remount,clean_plink=inum"
+ and remove the whiteouted plink.
+ EOF
+ rm -f $tmp $tmp.*
+ exit 1
+}
+
+eecho() { echo "$@" 1>&2; }
+
+conv() # [escape]
+{
+ sed -r -e '
+ s/\\/\\134/g
+ s/$/\\012/
+ ' |
+ tr -d '\n' |
+ sed -r -e '
+ s/ /\\040/g
+ s/\t/\\011/g
+ s/\r/\\015/g
+ s/\\012$//
+ ' |
+ { test "$1" = "escape" && sed -r -e 's/\\/\\\\/g' || cat; }
+ echo
+}
+
+Find() # dir [ find_args ]
+{
+ dir="$1"
+ shift
+ find "$dir" -xdev -noleaf $@
+}
+
+List()
+{
+ sed -e 's/^\([0-9]*\) .*$/-o -inum \1/' |
+ cut -c3- |
+ xargs -n 200 |
+ while read args
+ do Find "$mntpnt" \( $args \) $@
+ done
+}
+
+Cpup()
+{
+ List \( \( -type l -fprint $tmp.slink \) \
+ -o ! -type l -fprint $tmp.other \)
+
+ # do nothing but update something. it causes copyup.
+ xargs -r touch -ac -r $tmp.other < $tmp.other
+ xargs -r aulchown < $tmp.slink
+}
+
+test $# -eq 2 || Usage
+cd "$1"
+mntpnt=`readlink -f "$PWD"`
+e_mntpnt=`echo "$mntpnt" | conv escape`
+cd "$OLDPWD"
+
+do_flush=0
+f=${AUPLINK_CONFIG:-/etc/default/auplink}
+if [ -r $f ]
+then
+ . $f
+ for i in $FLUSH
+ do
+ test "$i" = "$mntpnt" -o "$i" = "ALL" || continue
+ do_flush=1
+ break
+ done
+fi
+case $2 in
+cpup|flush) test $do_flush -eq 0 && exit 0;;
+esac
+
+ent=`grep " $e_mntpnt aufs .*,si=" /proc/mounts | tail -n 1`
+test ! "$ent" && exit 0
+eval `echo "$ent" | tr ',' '\n' | grep '^si='`
+test ! "$si" && eecho no such mount-point $e_mntpnt && exit 1
+
+si_dir=/sys/fs/aufs/si_$si
+if [ -r $si_dir/br0 ]
+then
+ cd $si_dir
+ ls -1 br* |
+ cut -c3- |
+ sort -n |
+ sed -e 's/^/br/' |
+ xargs -rn 200 cat
+ cd $OLDPWD
+else
+ echo "$ent" |
+ tr ',' '\n' |
+ sed -e 's/\\/\\\\/g' |
+ egrep '^(dirs=|br:)' |
+ tr ':' '\n'
+fi |
+grep '=rw$' |
+sed -e 's/=rw$//' |
+while read wbr
+do
+ d=`echo $wbr/.wh..wh.plink`
+ # -printf '%n %p\n'
+ test -d "$d" && Find "$d" -maxdepth 1 ! -type d -printf '%n\t%p\n'
+done |
+while read nlink pname
+do
+ inum=`basename "$pname" | cut -f1 -d.`
+ echo $inum $nlink "$pname"
+done > $tmp
+test -s $tmp || { rm -f $tmp $tmp.*; exit 0; }
+
+# debug
+#mount -o remount,list_plink "$mntpnt"
+
+case $2 in
+list)
+ cut -f1 -d' ' $tmp | tr '\n' ' '
+ echo
+ List < $tmp
+ ;;
+cpup)
+ Cpup < $tmp
+ ;;
+flush)
+ #echo -n Flushing pseudo-links on "$mntpnt"...
+ Cpup < $tmp
+
+ # all are copied-up, and then remove all pseudo-links.
+ mount -o remount,clean_plink "$mntpnt"
+ cut -f3- -d' ' $tmp | xargs -r rm
+ # debug
+ #mount -o remount,list_plink "$mntpnt"
+ #echo done.
+ ;;
+*)
+ Usage;;
+esac
+
+rm -f $tmp $tmp.*
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 65/67] aufs pseudo-link helper for symlink
2008-05-16 14:33 ` [PATCH 64/67] aufs pseudo-link helper hooanon05
@ 2008-05-16 14:33 ` hooanon05
2008-05-16 14:33 ` [PATCH 66/67] aufs umount helper hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:33 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs pseudo-link helper for symlink
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
Documentation/filesystems/aufs/aulchown.c | 29 +++++++++++++++++++++++++++++
1 files changed, 29 insertions(+), 0 deletions(-)
diff --git a/Documentation/filesystems/aufs/aulchown.c b/Documentation/filesystems/aufs/aulchown.c
new file mode 100644
index 0000000..ca20584
--- /dev/null
+++ b/Documentation/filesystems/aufs/aulchown.c
@@ -0,0 +1,29 @@
+
+/*
+ * While this tool should be atomic, I choose loose/rough way.
+ * cf. auplink and aufs.5
+ * $Id: aulchown.c,v 1.1 2007/01/08 02:00:18 sfjro Exp $
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+
+int main(int argc, char *argv[])
+{
+ int err, nerr;
+ struct stat st;
+
+ nerr = 0;
+ while (*++argv) {
+ err = lstat(*argv, &st);
+ if (!err && S_ISLNK(st.st_mode))
+ err = lchown(*argv, st.st_uid, st.st_gid);
+ if (!err)
+ continue;
+ perror(*argv);
+ nerr++;
+ }
+ return nerr;
+}
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 66/67] aufs umount helper
2008-05-16 14:33 ` [PATCH 65/67] aufs pseudo-link helper for symlink hooanon05
@ 2008-05-16 14:33 ` hooanon05
2008-05-16 14:33 ` [PATCH 67/67] merge aufs hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:33 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
initial commit
aufs umount helper
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
Documentation/filesystems/aufs/umount.aufs | 33 ++++++++++++++++++++++++++++
1 files changed, 33 insertions(+), 0 deletions(-)
diff --git a/Documentation/filesystems/aufs/umount.aufs b/Documentation/filesystems/aufs/umount.aufs
new file mode 100755
index 0000000..f05a462
--- /dev/null
+++ b/Documentation/filesystems/aufs/umount.aufs
@@ -0,0 +1,33 @@
+#!/bin/sh -
+
+# Copyright (C) 2007 Junjiro Okajima
+#
+# This program, aufs is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+# $Id: umount.aufs,v 1.1 2007/01/15 04:58:42 sfjro Exp $
+
+#
+# The main purpose of this script is calling auplink.
+#
+
+PATH=/usr/bin:/usr/sbin:/bin:/sbin
+export PATH
+
+set -e
+#set -x; echo $0 $@
+dev="$1"
+shift
+auplink "$dev" flush
+exec umount -i $@ "$dev"
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 67/67] merge aufs
2008-05-16 14:33 ` [PATCH 66/67] aufs umount helper hooanon05
@ 2008-05-16 14:33 ` hooanon05
0 siblings, 0 replies; 80+ messages in thread
From: hooanon05 @ 2008-05-16 14:33 UTC (permalink / raw)
To: linux-fsdevel, linux-kernel; +Cc: Junjiro Okajima
From: Junjiro Okajima <hooanon05@yahoo.co.jp>
incorporate aufs, export __lookup_hash() and deeper lockdep
Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
---
fs/Kconfig | 2 ++
fs/Makefile | 1 +
fs/namei.c | 2 +-
include/linux/lockdep.h | 4 ++++
include/linux/namei.h | 1 +
5 files changed, 9 insertions(+), 1 deletions(-)
diff --git a/fs/Kconfig b/fs/Kconfig
index 379eb47..6df842f 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -1072,6 +1072,8 @@ config UNION_FS_DEBUG
help
If you say Y here, you can turn on debugging output from Unionfs.
+source "fs/aufs/Kconfig"
+
endmenu
menu "Miscellaneous filesystems"
diff --git a/fs/Makefile b/fs/Makefile
index 7996220..bc5964a 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -121,3 +121,4 @@ obj-$(CONFIG_DEBUG_FS) += debugfs/
obj-$(CONFIG_OCFS2_FS) += ocfs2/
obj-$(CONFIG_GFS2_FS) += gfs2/
obj-$(CONFIG_UNION_FS) += unionfs/
+obj-$(CONFIG_AUFS) += aufs/
diff --git a/fs/namei.c b/fs/namei.c
index 185d56c..1b9e577 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1297,7 +1297,7 @@ int __user_path_lookup_open(const char __user *name, unsigned int lookup_flags,
return err;
}
-static struct dentry *__lookup_hash(struct qstr *name,
+struct dentry *__lookup_hash(struct qstr *name,
struct dentry *base, struct nameidata *nd)
{
struct dentry *dentry;
diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h
index 4c4d236..f5d0642 100644
--- a/include/linux/lockdep.h
+++ b/include/linux/lockdep.h
@@ -58,7 +58,11 @@ enum lock_usage_bit
#define LOCKF_USED_IN_IRQ_READ \
(LOCKF_USED_IN_HARDIRQ_READ | LOCKF_USED_IN_SOFTIRQ_READ)
+#ifdef CONFIG_AUFS
+#define MAX_LOCKDEP_SUBCLASSES 16UL
+#else
#define MAX_LOCKDEP_SUBCLASSES 8UL
+#endif
/*
* Lock-classes are keyed via unique addresses, by embedding the
diff --git a/include/linux/namei.h b/include/linux/namei.h
index 24d88e9..33c7f23 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -74,6 +74,7 @@ extern struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry
extern struct file *nameidata_to_filp(struct nameidata *nd, int flags);
extern void release_open_intent(struct nameidata *);
+struct dentry * __lookup_hash(struct qstr *name, struct dentry * base, struct nameidata *nd);
extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
extern struct dentry *lookup_one_noperm(const char *, struct dentry *);
--
1.4.4.4
^ permalink raw reply related [flat|nested] 80+ messages in thread
* Re: [PATCH 1/67] aufs document
2008-05-16 14:32 [PATCH 1/67] aufs document hooanon05
2008-05-16 14:32 ` [PATCH 2/67] aufs manual hooanon05
@ 2008-05-16 14:59 ` Dave Quigley
2008-05-16 15:32 ` Jan Engelhardt
2008-05-16 15:45 ` hooanon05
1 sibling, 2 replies; 80+ messages in thread
From: Dave Quigley @ 2008-05-16 14:59 UTC (permalink / raw)
To: hooanon05; +Cc: linux-fsdevel, linux-kernel
A couple of comments about this. First off you might want to reconsider
how you are breaking up the patches. 67 patches seems a bit much to me
and while the separation between patches is clear it seems that some of
them can be folded. Second I'm not sure if I haven't gotten it yet or if
it wasn't sent but I am missing an Email 0/67. Typically this contains a
description of the patch set, an index of the patches (possibly with
descriptions), and a diffstat for the entire patch set. The last thing
is that each patch should have a worth wile description attached to it.
All of your comments are two lines and that may make sense based on how
you have it broken up but if you start breaking up the patch set
differently you will need better descriptions (possibly even now for
some of the more obscure functionality).
Dave
^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 4/67] aufs configuration
2008-05-16 14:32 ` [PATCH 4/67] aufs configuration hooanon05
2008-05-16 14:32 ` [PATCH 5/67] aufs Makefile hooanon05
@ 2008-05-16 15:28 ` Jan Engelhardt
2008-05-19 2:23 ` hooanon05
1 sibling, 1 reply; 80+ messages in thread
From: Jan Engelhardt @ 2008-05-16 15:28 UTC (permalink / raw)
To: Junjiro Okajima; +Cc: linux-fsdevel, linux-kernel
On Friday 2008-05-16 16:32, hooanon05@yahoo.co.jp wrote:
>+
>+if AUFS
>+config AUFS_FAKE_DM
>+ bool
>+ depends on AUFS
>+ default n
When you have "if AUFS" you do not need all the "depends on AUFS".
>+config AUFS_ROBR
>+ bool "Aufs as an readonly branch of another aufs"
>+ depends on AUFS
>+ default n
>+ help
>+ If you want make your aufs to be a part of another aufs, then
>+ enable this option. In other words, you can specify your aufs
>+ path in 'br:' mount option for another aufs, but cannot
>+ specify 'rw' as the branch permission.
>+ It will damage the performance.
>+ See detail in aufs.5.
IIRC, unionfs did not need such an option, so why does AUFS?
>+config AUFS_DLGT
>+ bool "Delegate the internal branch access the kernel thread"
>+ depends on AUFS
>+ default n
I do not think you need "default n" either as that should be the
implicit default.
>+config AUFS_SPLICE_PATCH
>+ bool
>+ depends on AUFS
>+ default y
This should just go away if it is always selected anyway.
>+config AUFS_BR_NFS
>+ bool
>+ depends on AUFS
>+ depends on NFS_FS
>+ depends on !AUFS_FAKE_DM
>+ default n if (!AUFS_LHASH_PATCH || AUFS_FAKE_DM)
>+ default y
One AUFS_FAKE_DM is redundant here. It should probably be just
depends on NFS_FS && !AUFS_FAKE_DM && AUFS_LHASH_PATCH
>+config AUFS_BR_XFS
>+ bool
>+ depends on AUFS
>+ depends on XFS_FS
>+ default y
Same here, just depends on XFS_FS.
>+config AUFS_WORKAROUND_FUSE
>+ bool "Special handling for FUSE-based filesystem"
>+ depends on AUFS && FUSE_FS
>+ default n
>+ help
>+ A FUSE-based filesystem may not initialize its inode
>+ attributes and the FUSE developer thinks the inode attributes
>+ in a positive dentry which is returned by VFS lookup operation
>+ are not reliable.
>+ If you use a FUSE-based filesystem as an aufs branch, and it
>+ customizes the inode attribute on it without overriding
>+ fuse_lowlevel_ops.lookup, probably you need to enable this
>+ configuration.
>+ If you enable this configuration, aufs calls getattr operation
>+ in every lookup and revalidate operation for the FUSE-based
>+ filesystem branch.
>+ It will damage the performance even if you don't use a
>+ FUSE-based filesystem branch.
Performance is not "damaged" but "hurt" :-) [or just "will negatively
impact performance"]
>+config AUFS_MAGIC_SYSRQ
>+ bool
>+ depends on AUFS
>+ depends on AUFS_DEBUG
>+ depends on MAGIC_SYSRQ
>+ default y
Since I see this for the first time, a bit of help text would not be
bad, even if it is not visible in menuconfig.
>+config AUFS_COMPAT
>+ bool "Compatibility with Unionfs (obsolete)"
If it is obsolete, it should not be merged.
^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 1/67] aufs document
2008-05-16 14:59 ` [PATCH 1/67] aufs document Dave Quigley
@ 2008-05-16 15:32 ` Jan Engelhardt
2008-05-16 21:07 ` Josef 'Jeff' Sipek
2008-05-19 2:25 ` hooanon05
2008-05-16 15:45 ` hooanon05
1 sibling, 2 replies; 80+ messages in thread
From: Jan Engelhardt @ 2008-05-16 15:32 UTC (permalink / raw)
To: Dave Quigley; +Cc: hooanon05, linux-fsdevel, linux-kernel
On Friday 2008-05-16 16:59, Dave Quigley wrote:
>A couple of comments about this. First off you might want to reconsider
>how you are breaking up the patches. 67 patches seems a bit much to me
>and while the separation between patches is clear it seems that some of
>them can be folded. Second I'm not sure if I haven't gotten it yet or if
>it wasn't sent but I am missing an Email 0/67. Typically this contains a
No 0/67 unfortunately, and --no-chain was also missing when calling
git-send-email. (Argh, git-send-email should default to no-chain!)
^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 6/67] aufs main header file
2008-05-16 14:32 ` [PATCH 6/67] aufs main header file hooanon05
2008-05-16 14:32 ` [PATCH 7/67] aufs module initialization and module-global hooanon05
@ 2008-05-16 15:33 ` Jan Engelhardt
1 sibling, 0 replies; 80+ messages in thread
From: Jan Engelhardt @ 2008-05-16 15:33 UTC (permalink / raw)
To: Junjiro Okajima; +Cc: linux-fsdevel, linux-kernel
On Friday 2008-05-16 16:32, hooanon05@yahoo.co.jp wrote:
>+
>+#include <linux/version.h>
>+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
>+#error you got wrong version
>+#endif
Kill this.
^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 8/67] aufs module global variables and operations
2008-05-16 14:32 ` [PATCH 8/67] aufs module global variables and operations hooanon05
2008-05-16 14:32 ` [PATCH 9/67] aufs super_block operations hooanon05
@ 2008-05-16 15:36 ` Jan Engelhardt
1 sibling, 0 replies; 80+ messages in thread
From: Jan Engelhardt @ 2008-05-16 15:36 UTC (permalink / raw)
To: Junjiro Okajima; +Cc: linux-fsdevel, linux-kernel
On Friday 2008-05-16 16:32, hooanon05@yahoo.co.jp wrote:
>+/* ---------------------------------------------------------------------- */
>+
>+/* fake Kconfig */
I doubt this is needed anymore:
>+#if 1
>+
>+#ifdef CONFIG_AUFS_HINOTIFY
>+#ifndef CONFIG_INOTIFY
>+#error enable CONFIG_INOTIFY to use CONFIG_AUFS_HINOTIFY.
>+#endif
>+#endif /* CONFIG_AUFS_HINOTIFY */
>+
>+#if AUFS_BRANCH_MAX > 511 && PAGE_SIZE > 4096
>+#warning pagesize is larger than 4kb, \
>+ CONFIG_AUFS_BRANCH_MAX_511 or smaller is recommended.
>+#endif
>+
>+#ifdef CONFIG_AUFS_STAT
>+#ifndef CONFIG_SYSFS
>+#error CONFIG_AUFS_STAT requires CONFIG_SYSFS.
>+#endif
>+#endif /* CONFIG_AUFS_STAT */
>+
>+#ifdef CONFIG_AUFS_SYSAUFS
>+#warning CONFIG_AUFS_SYSAUFS is unnecessary for linux-2.6.25 and later.
>+#endif
>+
>+#ifdef CONFIG_AUFS_EXPORT
>+#if !defined(CONFIG_EXPORTFS) && !defined(CONFIG_EXPORTFS_MODULE)
>+#error CONFIG_AUFS_EXPORT requires CONFIG_EXPORTFS
>+#endif
>+#if defined(CONFIG_EXPORTFS_MODULE) && defined(CONFIG_AUFS)
>+#error need CONFIG_EXPORTFS = y to link aufs statically with CONFIG_AUFS_EXPORT
>+#endif
>+#endif /* CONFIG_AUFS_EXPORT */
>+
...
^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 1/67] aufs document
2008-05-16 14:59 ` [PATCH 1/67] aufs document Dave Quigley
2008-05-16 15:32 ` Jan Engelhardt
@ 2008-05-16 15:45 ` hooanon05
2008-05-16 16:09 ` Dave Quigley
1 sibling, 1 reply; 80+ messages in thread
From: hooanon05 @ 2008-05-16 15:45 UTC (permalink / raw)
To: Dave Quigley; +Cc: linux-fsdevel, linux-kernel
Hello Dave,
Thank you for your comments.
About diffstat, actually I send it but the subject was not 0/67.
Please read the thread whose subject is
"[RFC n/2] AUFS: merging/stacking several filesystems"
The last message in the thread which I sent just before the patch series
has diffstat.
Do you want me to re-send it after changing the subject line?
About the number of patches, I have to agree it is large. I chose the
way to make a patch per a new file, and in case of modifying the
existing multiple files I made a single patch. I guess the latter is the
case you wrote "can be folded."
About the description, simple one for the new files because they are
brand new... I wrote a little more in case of modifying the existing
files (see the last patch). Of course a more detail description will be
necessary in future modification.
If you think the functionality is obscure, please read the aufs
documents included in the patch set (the first patches).
Junjiro Okajima
Dave Quigley:
> A couple of comments about this. First off you might want to reconsider
> how you are breaking up the patches. 67 patches seems a bit much to me
> and while the separation between patches is clear it seems that some of
> them can be folded. Second I'm not sure if I haven't gotten it yet or if
> it wasn't sent but I am missing an Email 0/67. Typically this contains a
> description of the patch set, an index of the patches (possibly with
> descriptions), and a diffstat for the entire patch set. The last thing
> is that each patch should have a worth wile description attached to it.
> All of your comments are two lines and that may make sense based on how
> you have it broken up but if you start breaking up the patch set
> differently you will need better descriptions (possibly even now for
> some of the more obscure functionality).
^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 1/67] aufs document
2008-05-16 15:45 ` hooanon05
@ 2008-05-16 16:09 ` Dave Quigley
2008-05-17 2:06 ` hooanon05
0 siblings, 1 reply; 80+ messages in thread
From: Dave Quigley @ 2008-05-16 16:09 UTC (permalink / raw)
To: hooanon05; +Cc: linux-fsdevel, linux-kernel
On Sat, 2008-05-17 at 00:45 +0900, hooanon05@yahoo.co.jp wrote:
> Hello Dave,
> Thank you for your comments.
>
> About diffstat, actually I send it but the subject was not 0/67.
> Please read the thread whose subject is
> "[RFC n/2] AUFS: merging/stacking several filesystems"
> The last message in the thread which I sent just before the patch series
> has diffstat.
> Do you want me to re-send it after changing the subject line?
You don't have to resend it now. Just keep it in mind for future patch
set submissions.
>
> About the number of patches, I have to agree it is large. I chose the
> way to make a patch per a new file, and in case of modifying the
> existing multiple files I made a single patch. I guess the latter is the
> case you wrote "can be folded."
Yea in some cases this is possible. You could roll all headers into one
patch. Or you could roll a header and its corresponding source into a
patch. There are many ways to do it.
>
> About the description, simple one for the new files because they are
> brand new... I wrote a little more in case of modifying the existing
> files (see the last patch). Of course a more detail description will be
> necessary in future modification.
> If you think the functionality is obscure, please read the aufs
> documents included in the patch set (the first patches).
>
It is fine to have an overall document describing your FS but there are
things you have missing. For example patch 62. Why do you have magic
sysreq handling? What does it do? What problem is it solving? This isn't
in your 01 patch and I can't tell its purpose at all from the patch.
Another example is what are your sysfs entries for? A description of
what they are for in either the main doc or as a patch comment is
necessary. Why is your sysfs functionality broken out into two patches?
There are many other things like this. I must admit our initial release
of Unionfs wasn't any better in terms of patch descriptions. Also our
patch set was smaller since we removed some functionality we knew would
be resisted by upstream and because our file layout is different from
yours. However like the sysfs patches being broken up there are a couple
of other things that represent one logical set that can be grouped
together.
The point of posting these sets to LKML is so people will review them.
If I have to read through a large document and then through each patch
individually just to figure out what the patch is trying to accomplish
before I can see how it is going about accomplishing it, then that is
extra resistance to actually looking through the set.
Dave
> Junjiro Okajima
>
>
> Dave Quigley:
> > A couple of comments about this. First off you might want to reconsider
> > how you are breaking up the patches. 67 patches seems a bit much to me
> > and while the separation between patches is clear it seems that some of
> > them can be folded. Second I'm not sure if I haven't gotten it yet or if
> > it wasn't sent but I am missing an Email 0/67. Typically this contains a
> > description of the patch set, an index of the patches (possibly with
> > descriptions), and a diffstat for the entire patch set. The last thing
> > is that each patch should have You could break up the patches by functionality likea worth wile description attached to it.
> > All of your comments are two lines and that may make sense based on how
> > you have it broken up but if you start breaking up the patch set
> > differently you will need better descriptions (possibly even now for
> > some of the more obscure functionality).
^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 5/67] aufs Makefile
2008-05-16 14:32 ` [PATCH 5/67] aufs Makefile hooanon05
2008-05-16 14:32 ` [PATCH 6/67] aufs main header file hooanon05
@ 2008-05-16 17:25 ` Sam Ravnborg
2008-05-19 2:27 ` hooanon05
1 sibling, 1 reply; 80+ messages in thread
From: Sam Ravnborg @ 2008-05-16 17:25 UTC (permalink / raw)
To: hooanon05; +Cc: linux-fsdevel, linux-kernel
Hi Junjiro.
A few comments to your Makefile.
On Fri, May 16, 2008 at 11:32:19PM +0900, hooanon05@yahoo.co.jp wrote:
> From: Junjiro Okajima <hooanon05@yahoo.co.jp>
>
> initial commit
> aufs Makefile
>
> Signed-off-by: Junjiro Okajima <hooanon05@yahoo.co.jp>
> ---
> fs/aufs/Makefile | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 files changed, 57 insertions(+), 0 deletions(-)
>
> diff --git a/fs/aufs/Makefile b/fs/aufs/Makefile
> new file mode 100644
> index 0000000..05e0864
> --- /dev/null
> +++ b/fs/aufs/Makefile
> @@ -0,0 +1,57 @@
> +# AUFS Makefile for the Linux 2.6.25 and later
> +# $Id: Makefile,v 1.3 2008/04/28 03:03:10 sfjro Exp $
Please drop these two lines
> +
> +# the environment variables are not inherited since 2.6.23
> +ifdef AUFS_EXTRA_CFLAGS
> +EXTRA_CFLAGS += ${AUFS_EXTRA_CFLAGS}
> +endif
And this as well. If this is needed do like the others do.
> +
> +########################################
> +
> +ifdef CONFIG_AUFS_WORKAROUND_FUSE
> +# defined in ${srctree}/fs/fuse/inode.c
> +EXTRA_CFLAGS += -DFUSE_SUPER_MAGIC=0x65735546
> +endif
Use:
ccflags-$(CONFIG_AUFS_WORKAROUND_FUSE) += -DFUSE_SUPER_MAGIC=0x65735546
> +
> +ifdef CONFIG_XFS_FS
> +# defined in ${srctree}/fs/xfs/xfs_sb.h
> +EXTRA_CFLAGS += -DXFS_SB_MAGIC=0x58465342
> +endif
ccflags-$(CONFIG_XFS_FS) += -DXFS_SB_MAGIC=0x58465342
> +
> +ifdef CONFIG_TMPFS
> +# defined in ${srctree}/mm/shmem.c
> +EXTRA_CFLAGS += -DTMPFS_MAGIC=0x01021994
> +endif
ccflags-$(CONFIG_TMPFS) += -DTMPFS_MAGIC=0x01021994
> +
> +-include $(dir $(lastword ${MAKEFILE_LIST}))priv.mk
Please drop this.
> +#$(warning ${EXTRA_CFLAGS})
Not used - drop it.
> +
> +########################################
> +
> +obj-$(CONFIG_AUFS) += aufs.o
> +aufs-y := module.o super.o sbinfo.o branch.o xino.o sysaufs.o opts.o \
> + wkq.o vfsub.o dcsub.o \
> + cpup.o whout.o plink.o wbr_policy.o \
> + dentry.o dinfo.o \
> + file.o f_op.o finfo.o \
> + dir.o vdir.o \
> + inode.o i_op.o i_op_add.o i_op_del.o i_op_ren.o iinfo.o \
> + misc.o
> +
> +#xattr.o
> +aufs-$(CONFIG_SYSFS) += sysfs.o
> +aufs-$(CONFIG_AUFS_BR_NFS) += br_nfs.o
> +aufs-$(CONFIG_AUFS_BR_XFS) += br_xfs.o
> +aufs-$(CONFIG_AUFS_WORKAROUND_FUSE) += br_fuse.o
> +
> +aufs-$(CONFIG_AUFS_DLGT) += dlgt.o
> +aufs-$(CONFIG_AUFS_HINOTIFY) += hinotify.o hin_or_dlgt.o
> +# dirty
> +ifndef CONFIG_AUFS_HINOTIFY
> +aufs-$(CONFIG_AUFS_DLGT) += hin_or_dlgt.o
> +endif
Can we have this dependency expressed in your Kconfig file?
> +
> +aufs-$(CONFIG_AUFS_EXPORT) += export.o
> +aufs-$(CONFIG_AUFS_ROBR) += robr.o
> +aufs-$(CONFIG_AUFS_DEBUG) += debug.o
> +aufs-$(CONFIG_AUFS_MAGIC_SYSRQ) += sysrq.o
Sam
^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 1/67] aufs document
2008-05-16 15:32 ` Jan Engelhardt
@ 2008-05-16 21:07 ` Josef 'Jeff' Sipek
2008-05-19 2:25 ` hooanon05
1 sibling, 0 replies; 80+ messages in thread
From: Josef 'Jeff' Sipek @ 2008-05-16 21:07 UTC (permalink / raw)
To: Jan Engelhardt; +Cc: Dave Quigley, hooanon05, linux-fsdevel, linux-kernel
On Fri, May 16, 2008 at 05:32:45PM +0200, Jan Engelhardt wrote:
>
> On Friday 2008-05-16 16:59, Dave Quigley wrote:
>
> >A couple of comments about this. First off you might want to reconsider
> >how you are breaking up the patches. 67 patches seems a bit much to me
> >and while the separation between patches is clear it seems that some of
> >them can be folded. Second I'm not sure if I haven't gotten it yet or if
> >it wasn't sent but I am missing an Email 0/67. Typically this contains a
>
> No 0/67 unfortunately, and --no-chain was also missing when calling
> git-send-email. (Argh, git-send-email should default to no-chain!)
If you use Guilt, it does all the brain-damage removal for you ;)
(guilt-patchbomb to be exact)
Josef 'Jeff' Sipek.
--
I'm somewhere between geek and normal.
- Linus Torvalds
^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 1/67] aufs document
2008-05-16 16:09 ` Dave Quigley
@ 2008-05-17 2:06 ` hooanon05
0 siblings, 0 replies; 80+ messages in thread
From: hooanon05 @ 2008-05-17 2:06 UTC (permalink / raw)
To: Dave Quigley; +Cc: linux-fsdevel, linux-kernel
Dave Quigley:
> It is fine to have an overall document describing your FS but there are
> things you have missing. For example patch 62. Why do you have magic
> sysreq handling? What does it do? What problem is it solving? This isn't
> in your 01 patch and I can't tell its purpose at all from the patch.
It simply dumps some aufs status to console. Because I think it is a
nature of magic sysrq, and it is not a feature to solve a specific
problem, I didn't write much, just simply
----------------------------------------------------------------------
.B sysrq=key
Specifies MagicSysRq key for debugging aufs.
You need to enable both of CONFIG_MAGIC_SYSRQ and CONFIG_AUFS_DEBUG.
If your linux version is linux\-2.6.24 and earlier, you need to enable
CONFIG_AUFS_SYSAUFS too.
Currently this is for developers only.
The default is `a'.
----------------------------------------------------------------------
in the aufs manual.
> Another example is what are your sysfs entries for? A description of
> what they are for in either the main doc or as a patch comment is
> necessary. Why is your sysfs functionality broken out into two patches?
One of them is for lifetime management as the description
said. Generally an object correspoing to an entry might be under sysfs
is managed by kref. This management is independent from CONFIG_SYSFS,
and the former patch is compiled unconditionally.
The other is for the actual entires under sysfs, and compiled when SYSFS
is enabled only.
I don't think it is a good idea to make them in a single patch.
But I will re-send the entire aufs patch series in next week, after...
- make some simple header + source pairs in a single patch and try more
description text, as you pointed out.
- remove some lines unnecessary for -mm tree, pointed out by Jan
Engelhardt and Sam Ravnborg.
- future other things if someone will point out.
> The point of posting these sets to LKML is so people will review them.
> If I have to read through a large document and then through each patch
> individually just to figure out what the patch is trying to accomplish
> before I can see how it is going about accomplishing it, then that is
> extra resistance to actually looking through the set.
I tried to do so...
I thought first document is important too, but the smaller patches in
the later is more preferable than larger ones. Addionally most of them
are totally new files. So I chose "one patch by one new file" way.
Anyway, I will re-send them in next week after trying some grouping.
Thank you
Junjiro Okajima
^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 4/67] aufs configuration
2008-05-16 15:28 ` [PATCH 4/67] aufs configuration Jan Engelhardt
@ 2008-05-19 2:23 ` hooanon05
0 siblings, 0 replies; 80+ messages in thread
From: hooanon05 @ 2008-05-19 2:23 UTC (permalink / raw)
To: Jan Engelhardt; +Cc: linux-fsdevel, linux-kernel
Hi,
Thank you for your review and comments.
I will fix as you pointed out.
Jan Engelhardt:
> >+config AUFS_ROBR
> >+ bool "Aufs as an readonly branch of another aufs"
> >+ depends on AUFS
> >+ default n
> >+ help
> >+ If you want make your aufs to be a part of another aufs, then
> >+ enable this option. In other words, you can specify your aufs
> >+ path in 'br:' mount option for another aufs, but cannot
> >+ specify 'rw' as the branch permission.
> >+ It will damage the performance.
> >+ See detail in aufs.5.
>
> IIRC, unionfs did not need such an option, so why does AUFS?
The *current* unionfs doesn't support such nested mount, ie. unionfs as
a branch of another unionfs mount.
Aufs supports it only readonly mode (if you enable this configuration).
> Performance is not "damaged" but "hurt" :-) [or just "will negatively
> impact performance"]
English language is much more difficult than C language for me...
Junjiro Okajima
^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 1/67] aufs document
2008-05-16 15:32 ` Jan Engelhardt
2008-05-16 21:07 ` Josef 'Jeff' Sipek
@ 2008-05-19 2:25 ` hooanon05
1 sibling, 0 replies; 80+ messages in thread
From: hooanon05 @ 2008-05-19 2:25 UTC (permalink / raw)
To: Jan Engelhardt; +Cc: Dave Quigley, linux-fsdevel, linux-kernel
Jan Engelhardt:
> No 0/67 unfortunately, and --no-chain was also missing when calling
> git-send-email. (Argh, git-send-email should default to no-chain!)
I have upgraded GIT on my system, and got a new option '--cover-letter.'
Next time I will send 0/n and specify --no-chain-reply-to too.
Junjiro Okajima
^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 5/67] aufs Makefile
2008-05-16 17:25 ` [PATCH 5/67] aufs Makefile Sam Ravnborg
@ 2008-05-19 2:27 ` hooanon05
0 siblings, 0 replies; 80+ messages in thread
From: hooanon05 @ 2008-05-19 2:27 UTC (permalink / raw)
To: Sam Ravnborg; +Cc: linux-fsdevel, linux-kernel
Hi Sam,
Thank you for your review and comments.
I will fix as you pointed out.
Sam Ravnborg:
> Use:
> ccflags-$(CONFIG_AUFS_WORKAROUND_FUSE) += -DFUSE_SUPER_MAGIC=0x65735546
I didn't know that. It is much better.
> > +# dirty
> > +ifndef CONFIG_AUFS_HINOTIFY
> > +aufs-$(CONFIG_AUFS_DLGT) += hin_or_dlgt.o
> > +endif
> Can we have this dependency expressed in your Kconfig file?
I will add a new automatic configuration CONFIG_AUFS_HIN_OR_DLGT.
Junjiro Okajima
^ permalink raw reply [flat|nested] 80+ messages in thread
end of thread, other threads:[~2008-05-19 2:27 UTC | newest]
Thread overview: 80+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-05-16 14:32 [PATCH 1/67] aufs document hooanon05
2008-05-16 14:32 ` [PATCH 2/67] aufs manual hooanon05
2008-05-16 14:32 ` [PATCH 3/67] aufs global header file hooanon05
2008-05-16 14:32 ` [PATCH 4/67] aufs configuration hooanon05
2008-05-16 14:32 ` [PATCH 5/67] aufs Makefile hooanon05
2008-05-16 14:32 ` [PATCH 6/67] aufs main header file hooanon05
2008-05-16 14:32 ` [PATCH 7/67] aufs module initialization and module-global hooanon05
2008-05-16 14:32 ` [PATCH 8/67] aufs module global variables and operations hooanon05
2008-05-16 14:32 ` [PATCH 9/67] aufs super_block operations hooanon05
2008-05-16 14:32 ` [PATCH 10/67] aufs mount and " hooanon05
2008-05-16 14:32 ` [PATCH 11/67] aufs superblock private data hooanon05
2008-05-16 14:32 ` [PATCH 12/67] aufs branch filesystems and xino for them hooanon05
2008-05-16 14:32 ` [PATCH 13/67] aufs branch management hooanon05
2008-05-16 14:32 ` [PATCH 14/67] aufs external inode number translation table and bitmap hooanon05
2008-05-16 14:32 ` [PATCH 15/67] aufs special handling for inode attributes on FUSE branch hooanon05
2008-05-16 14:32 ` [PATCH 16/67] aufs lookup functions for NFS branch in linux-2.6.19 and later hooanon05
2008-05-16 14:32 ` [PATCH 17/67] aufs special handling inode attributes on XFS branch in linux-2.6.24 " hooanon05
2008-05-16 14:32 ` [PATCH 18/67] aufs sysfs interface and lifetime management, header hooanon05
2008-05-16 14:32 ` [PATCH 19/67] aufs sysfs interface and lifetime management, source hooanon05
2008-05-16 14:32 ` [PATCH 20/67] aufs mount options/flags, header hooanon05
2008-05-16 14:32 ` [PATCH 21/67] aufs mount options/flags, source hooanon05
2008-05-16 14:32 ` [PATCH 22/67] aufs workqueue for asynchronous/super-io/delegated operations, header hooanon05
2008-05-16 14:32 ` [PATCH 23/67] aufs workqueue for asynchronous/super-io/delegated operations, source hooanon05
2008-05-16 14:32 ` [PATCH 24/67] aufs sub-VFS, header hooanon05
2008-05-16 14:32 ` [PATCH 25/67] aufs sub-VFS, source hooanon05
2008-05-16 14:32 ` [PATCH 26/67] aufs sub-dcache, header hooanon05
2008-05-16 14:32 ` [PATCH 27/67] aufs sub-dcache, source hooanon05
2008-05-16 14:32 ` [PATCH 28/67] aufs copy-up/down functions hooanon05
2008-05-16 14:32 ` [PATCH 29/67] aufs copy-up functions, see wbr_policy.c for copy-down hooanon05
2008-05-16 14:32 ` [PATCH 30/67] aufs whiteout for logical deletion and opaque directory, header hooanon05
2008-05-16 14:32 ` [PATCH 31/67] aufs whiteout for logical deletion and opaque directory, source hooanon05
2008-05-16 14:32 ` [PATCH 32/67] aufs pseudo-link hooanon05
2008-05-16 14:32 ` [PATCH 33/67] aufs policies for selecting one among multiple writable branches hooanon05
2008-05-16 14:32 ` [PATCH 34/67] aufs lookup and dentry operations, header hooanon05
2008-05-16 14:32 ` [PATCH 35/67] aufs lookup and dentry operations, source hooanon05
2008-05-16 14:32 ` [PATCH 36/67] aufs dentry private data hooanon05
2008-05-16 14:32 ` [PATCH 37/67] aufs file operations hooanon05
2008-05-16 14:32 ` [PATCH 38/67] aufs handling file/dir, and address_space operation hooanon05
2008-05-16 14:32 ` [PATCH 39/67] aufs file private data hooanon05
2008-05-16 14:32 ` [PATCH 40/67] aufs file and vm operations hooanon05
2008-05-16 14:32 ` [PATCH 41/67] aufs directory operations, header hooanon05
2008-05-16 14:32 ` [PATCH 42/67] aufs directory operations, source hooanon05
2008-05-16 14:32 ` [PATCH 43/67] aufs virtual or vertical directory hooanon05
2008-05-16 14:32 ` [PATCH 44/67] aufs inode operations hooanon05
2008-05-16 14:32 ` [PATCH 45/67] aufs inode functions hooanon05
2008-05-16 14:33 ` [PATCH 46/67] aufs inode private data hooanon05
2008-05-16 14:33 ` [PATCH 47/67] aufs inode operations (except add/del/rename) hooanon05
2008-05-16 14:33 ` [PATCH 48/67] aufs inode operations (add entry) hooanon05
2008-05-16 14:33 ` [PATCH 49/67] aufs inode operations (del entry) hooanon05
2008-05-16 14:33 ` [PATCH 50/67] aufs inode operation (rename entry) hooanon05
2008-05-16 14:33 ` [PATCH 51/67] aufs lower (branch filesystem) inode and setting inotify hooanon05
2008-05-16 14:33 ` [PATCH 52/67] aufs inotify handler hooanon05
2008-05-16 14:33 ` [PATCH 53/67] aufs sub-routines for vfs in hinotify or dlgt mode hooanon05
2008-05-16 14:33 ` [PATCH 54/67] aufs lookup functions in 'delegate' mode hooanon05
2008-05-16 14:33 ` [PATCH 55/67] aufs export via nfs hooanon05
2008-05-16 14:33 ` [PATCH 56/67] aufs 'robr', aufs as readonly branch of another aufs hooanon05
2008-05-16 14:33 ` [PATCH 57/67] aufs sysfs interface hooanon05
2008-05-16 14:33 ` [PATCH 58/67] aufs misc functions, header hooanon05
2008-05-16 14:33 ` [PATCH 59/67] aufs misc functions, source hooanon05
2008-05-16 14:33 ` [PATCH 60/67] aufs debug print functions, header hooanon05
2008-05-16 14:33 ` [PATCH 61/67] aufs debug print functions, source hooanon05
2008-05-16 14:33 ` [PATCH 62/67] aufs magic sysrq handler hooanon05
2008-05-16 14:33 ` [PATCH 63/67] aufs mount helper hooanon05
2008-05-16 14:33 ` [PATCH 64/67] aufs pseudo-link helper hooanon05
2008-05-16 14:33 ` [PATCH 65/67] aufs pseudo-link helper for symlink hooanon05
2008-05-16 14:33 ` [PATCH 66/67] aufs umount helper hooanon05
2008-05-16 14:33 ` [PATCH 67/67] merge aufs hooanon05
2008-05-16 15:36 ` [PATCH 8/67] aufs module global variables and operations Jan Engelhardt
2008-05-16 15:33 ` [PATCH 6/67] aufs main header file Jan Engelhardt
2008-05-16 17:25 ` [PATCH 5/67] aufs Makefile Sam Ravnborg
2008-05-19 2:27 ` hooanon05
2008-05-16 15:28 ` [PATCH 4/67] aufs configuration Jan Engelhardt
2008-05-19 2:23 ` hooanon05
2008-05-16 14:59 ` [PATCH 1/67] aufs document Dave Quigley
2008-05-16 15:32 ` Jan Engelhardt
2008-05-16 21:07 ` Josef 'Jeff' Sipek
2008-05-19 2:25 ` hooanon05
2008-05-16 15:45 ` hooanon05
2008-05-16 16:09 ` Dave Quigley
2008-05-17 2:06 ` hooanon05
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).