summaryrefslogtreecommitdiff
path: root/toolchain/kernel-headers/lzma
diff options
context:
space:
mode:
authorUlf Samuelsson <ulf.samuelsson@atmel.com>2007-08-16 06:26:25 +0000
committerUlf Samuelsson <ulf.samuelsson@atmel.com>2007-08-16 06:26:25 +0000
commit18199aa7b69102c99c5b6c2382f039823c781e3b (patch)
treeb33c03f416d0bb7448a1da5a27241ef088c19384 /toolchain/kernel-headers/lzma
parentc6771dfb847e53f8f5c184fad407cce599857ec9 (diff)
Move lzma patches to toolchain/kernel-headers/lzma
They will be applied ONLY if BR2_KERNEL_HEADERS_LZMA is set. BR2_KERNEL_HEADERS_LZMA defaults to NO, so the user has to actively set this config flag if lzma is needed. This means that the default behaviour of buildroot will work for most users. Comment from author: [Brad House] I find myself having to remove the lzma patches because my build system doesn't natively have lzma. I tried to compensate for this by installing the latest lzma from gentoo's package tree, and apparently, it accepts different flags than the one these patches expect, so my kernel build still fails. Finally, I used the lzma from this buildroot and compiled it by hand, and it _mostly_ worked. Regardless, it seems silly to make this a requirement. Personally, I gzip my initramfs, and that's enough compression for me...
Diffstat (limited to 'toolchain/kernel-headers/lzma')
-rw-r--r--toolchain/kernel-headers/lzma/linux-2.6.21.5-001-lzma-vmlinuz.00.patch27017
-rw-r--r--toolchain/kernel-headers/lzma/linux-2.6.21.5-002-lzma-vmlinuz.01.patch54
-rw-r--r--toolchain/kernel-headers/lzma/linux-2.6.22.1-001-lzma-vmlinuz.00.patch26856
-rw-r--r--toolchain/kernel-headers/lzma/linux-2.6.22.1-002-lzma-vmlinuz.01.patch54
4 files changed, 53981 insertions, 0 deletions
diff --git a/toolchain/kernel-headers/lzma/linux-2.6.21.5-001-lzma-vmlinuz.00.patch b/toolchain/kernel-headers/lzma/linux-2.6.21.5-001-lzma-vmlinuz.00.patch
new file mode 100644
index 000000000..87e50f8be
--- /dev/null
+++ b/toolchain/kernel-headers/lzma/linux-2.6.21.5-001-lzma-vmlinuz.00.patch
@@ -0,0 +1,27017 @@
+diff --git a/.miniconfig b/.miniconfig
+new file mode 100644
+index 0000000..5686e53
+--- /dev/null
++++ b/.miniconfig
+@@ -0,0 +1,89 @@
++#make allnoconfig KCONFIG_ALLCONFIG=miniconfig
++CONFIG_X86_32=y
++CONFIG_CLOCKSOURCE_WATCHDOG=y
++CONFIG_LOCKDEP_SUPPORT=y
++CONFIG_SEMAPHORE_SLEEPERS=y
++CONFIG_MMU=y
++CONFIG_GENERIC_ISA_DMA=y
++CONFIG_GENERIC_HWEIGHT=y
++CONFIG_DMI=y
++CONFIG_INIT_ENV_ARG_LIMIT=32
++CONFIG_IKCONFIG=y
++CONFIG_IKCONFIG_PROC=y
++CONFIG_SYSFS_DEPRECATED=y
++CONFIG_BLK_DEV_INITRD=y
++CONFIG_SYSCTL=y
++CONFIG_EMBEDDED=y
++CONFIG_PRINTK=y
++CONFIG_BASE_SMALL=1
++CONFIG_BLOCK=y
++CONFIG_IOSCHED_NOOP=y
++CONFIG_DEFAULT_IOSCHED="noop"
++CONFIG_X86_GENERIC=y
++CONFIG_X86_L1_CACHE_SHIFT=7
++CONFIG_GENERIC_CALIBRATE_DELAY=y
++CONFIG_X86_WP_WORKS_OK=y
++CONFIG_X86_BSWAP=y
++CONFIG_X86_CMPXCHG64=y
++CONFIG_X86_INTEL_USERCOPY=y
++CONFIG_X86_TSC=y
++CONFIG_PREEMPT_NONE=y
++CONFIG_VM86=y
++CONFIG_HIGHMEM=y
++CONFIG_FLATMEM=y
++CONFIG_MTRR=y
++CONFIG_HZ_250=y
++CONFIG_PHYSICAL_ALIGN=0x100000
++CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y
++CONFIG_PM=y
++CONFIG_ACPI=y
++CONFIG_ACPI_SLEEP=y
++CONFIG_ACPI_BLACKLIST_YEAR=0
++CONFIG_ACPI_EC=y
++CONFIG_ACPI_SYSTEM=y
++CONFIG_PCI=y
++CONFIG_PCI_GOANY=y
++CONFIG_PCI_DIRECT=y
++CONFIG_BINFMT_ELF=y
++CONFIG_STANDALONE=y
++CONFIG_BLK_DEV_LOOP=y
++CONFIG_IDE=y
++CONFIG_IDE_MAX_HWIFS=2
++CONFIG_BLK_DEV_IDE=y
++CONFIG_BLK_DEV_IDEDISK=y
++CONFIG_IDEDISK_MULTI_MODE=y
++CONFIG_BLK_DEV_IDECD=y
++CONFIG_IDE_GENERIC=y
++CONFIG_INPUT_MOUSEDEV=y
++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
++CONFIG_INPUT_KEYBOARD=y
++CONFIG_KEYBOARD_ATKBD=y
++CONFIG_SERIO=y
++CONFIG_VT=y
++CONFIG_VT_CONSOLE=y
++CONFIG_UNIX98_PTYS=y
++CONFIG_VGA_CONSOLE=y
++CONFIG_USB_ARCH_HAS_HCD=y
++CONFIG_USB_ARCH_HAS_EHCI=y
++CONFIG_EXT2_FS=y
++CONFIG_DNOTIFY=y
++CONFIG_ISO9660_FS=y
++CONFIG_FAT_FS=y
++CONFIG_VFAT_FS=y
++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
++CONFIG_PROC_FS=y
++CONFIG_PROC_SYSCTL=y
++CONFIG_SYSFS=y
++CONFIG_RAMFS=y
++CONFIG_SQUASHFS=y
++CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3
++CONFIG_MSDOS_PARTITION=y
++CONFIG_NLS_DEFAULT="iso8859-1"
++CONFIG_AUFS=y
++CONFIG_AUFS_FAKE_DM=y
++CONFIG_EARLY_PRINTK=y
++CONFIG_DOUBLEFAULT=y
++CONFIG_ZLIB_INFLATE=y
++CONFIG_HAS_IOPORT=y
++CONFIG_GENERIC_IRQ_PROBE=y
++CONFIG_KTIME_SCALAR=y
+diff --git a/Makefile b/Makefile
+index d970cb1..a369204 100644
+--- a/Makefile
++++ b/Makefile
+@@ -188,7 +188,7 @@ CROSS_COMPILE ?=
+ # Architecture as present in compile.h
+ UTS_MACHINE := $(ARCH)
+
+-KCONFIG_CONFIG ?= .config
++KCONFIG_CONFIG ?= .miniconfig
+
+ # SHELL used by kbuild
+ CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
+diff --git a/arch/i386/boot/compressed/LzmaDecode.c b/arch/i386/boot/compressed/LzmaDecode.c
+new file mode 100644
+index 0000000..21bf40b
+--- /dev/null
++++ b/arch/i386/boot/compressed/LzmaDecode.c
+@@ -0,0 +1,588 @@
++/*
++ LzmaDecode.c
++ LZMA Decoder (optimized for Speed version)
++
++ LZMA SDK 4.22 Copyright (c) 1999-2005 Igor Pavlov (2005-06-10)
++ http://www.7-zip.org/
++
++ LZMA SDK is licensed under two licenses:
++ 1) GNU Lesser General Public License (GNU LGPL)
++ 2) Common Public License (CPL)
++ It means that you can select one of these two licenses and
++ follow rules of that license.
++
++ SPECIAL EXCEPTION:
++ Igor Pavlov, as the author of this Code, expressly permits you to
++ statically or dynamically link your Code (or bind by name) to the
++ interfaces of this file without subjecting your linked Code to the
++ terms of the CPL or GNU LGPL. Any modifications or additions
++ to this file, however, are subject to the LGPL or CPL terms.
++*/
++
++#include "LzmaDecode.h"
++
++#ifndef Byte
++#define Byte unsigned char
++#endif
++
++#define kNumTopBits 24
++#define kTopValue ((UInt32)1 << kNumTopBits)
++
++#define kNumBitModelTotalBits 11
++#define kBitModelTotal (1 << kNumBitModelTotalBits)
++#define kNumMoveBits 5
++
++#define RC_READ_BYTE (*Buffer++)
++
++#define RC_INIT2 Code = 0; Range = 0xFFFFFFFF; \
++ { int i; for(i = 0; i < 5; i++) { RC_TEST; Code = (Code << 8) | RC_READ_BYTE; }}
++
++#ifdef _LZMA_IN_CB
++
++#define RC_TEST { if (Buffer == BufferLim) \
++ { SizeT size; int result = InCallback->Read(InCallback, &Buffer, &size); if (result != LZMA_RESULT_OK) return result; \
++ BufferLim = Buffer + size; if (size == 0) return LZMA_RESULT_DATA_ERROR; }}
++
++#define RC_INIT Buffer = BufferLim = 0; RC_INIT2
++
++#else
++
++#define RC_TEST { if (Buffer == BufferLim) return LZMA_RESULT_DATA_ERROR; }
++
++#define RC_INIT(buffer, bufferSize) Buffer = buffer; BufferLim = buffer + bufferSize; RC_INIT2
++
++#endif
++
++#define RC_NORMALIZE if (Range < kTopValue) { RC_TEST; Range <<= 8; Code = (Code << 8) | RC_READ_BYTE; }
++
++#define IfBit0(p) RC_NORMALIZE; bound = (Range >> kNumBitModelTotalBits) * *(p); if (Code < bound)
++#define UpdateBit0(p) Range = bound; *(p) += (kBitModelTotal - *(p)) >> kNumMoveBits;
++#define UpdateBit1(p) Range -= bound; Code -= bound; *(p) -= (*(p)) >> kNumMoveBits;
++
++#define RC_GET_BIT2(p, mi, A0, A1) IfBit0(p) \
++ { UpdateBit0(p); mi <<= 1; A0; } else \
++ { UpdateBit1(p); mi = (mi + mi) + 1; A1; }
++
++#define RC_GET_BIT(p, mi) RC_GET_BIT2(p, mi, ; , ;)
++
++#define RangeDecoderBitTreeDecode(probs, numLevels, res) \
++ { int i = numLevels; res = 1; \
++ do { CProb *p = probs + res; RC_GET_BIT(p, res) } while(--i != 0); \
++ res -= (1 << numLevels); }
++
++
++#define kNumPosBitsMax 4
++#define kNumPosStatesMax (1 << kNumPosBitsMax)
++
++#define kLenNumLowBits 3
++#define kLenNumLowSymbols (1 << kLenNumLowBits)
++#define kLenNumMidBits 3
++#define kLenNumMidSymbols (1 << kLenNumMidBits)
++#define kLenNumHighBits 8
++#define kLenNumHighSymbols (1 << kLenNumHighBits)
++
++#define LenChoice 0
++#define LenChoice2 (LenChoice + 1)
++#define LenLow (LenChoice2 + 1)
++#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits))
++#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits))
++#define kNumLenProbs (LenHigh + kLenNumHighSymbols)
++
++
++#define kNumStates 12
++#define kNumLitStates 7
++
++#define kStartPosModelIndex 4
++#define kEndPosModelIndex 14
++#define kNumFullDistances (1 << (kEndPosModelIndex >> 1))
++
++#define kNumPosSlotBits 6
++#define kNumLenToPosStates 4
++
++#define kNumAlignBits 4
++#define kAlignTableSize (1 << kNumAlignBits)
++
++#define kMatchMinLen 2
++
++#define IsMatch 0
++#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax))
++#define IsRepG0 (IsRep + kNumStates)
++#define IsRepG1 (IsRepG0 + kNumStates)
++#define IsRepG2 (IsRepG1 + kNumStates)
++#define IsRep0Long (IsRepG2 + kNumStates)
++#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax))
++#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits))
++#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex)
++#define LenCoder (Align + kAlignTableSize)
++#define RepLenCoder (LenCoder + kNumLenProbs)
++#define Literal (RepLenCoder + kNumLenProbs)
++
++#if Literal != LZMA_BASE_SIZE
++StopCompilingDueBUG
++#endif
++
++int LzmaDecodeProperties(CLzmaProperties *propsRes, const unsigned char *propsData, int size)
++{
++ unsigned char prop0;
++ if (size < LZMA_PROPERTIES_SIZE)
++ return LZMA_RESULT_DATA_ERROR;
++ prop0 = propsData[0];
++ if (prop0 >= (9 * 5 * 5))
++ return LZMA_RESULT_DATA_ERROR;
++ {
++ for (propsRes->pb = 0; prop0 >= (9 * 5); propsRes->pb++, prop0 -= (9 * 5));
++ for (propsRes->lp = 0; prop0 >= 9; propsRes->lp++, prop0 -= 9);
++ propsRes->lc = prop0;
++ /*
++ unsigned char remainder = (unsigned char)(prop0 / 9);
++ propsRes->lc = prop0 % 9;
++ propsRes->pb = remainder / 5;
++ propsRes->lp = remainder % 5;
++ */
++ }
++
++ #ifdef _LZMA_OUT_READ
++ {
++ int i;
++ propsRes->DictionarySize = 0;
++ for (i = 0; i < 4; i++)
++ propsRes->DictionarySize += (UInt32)(propsData[1 + i]) << (i * 8);
++ if (propsRes->DictionarySize == 0)
++ propsRes->DictionarySize = 1;
++ }
++ #endif
++ return LZMA_RESULT_OK;
++}
++
++#define kLzmaStreamWasFinishedId (-1)
++
++int LzmaDecode(CLzmaDecoderState *vs,
++ #ifdef _LZMA_IN_CB
++ ILzmaInCallback *InCallback,
++ #else
++ const unsigned char *inStream, SizeT inSize, SizeT *inSizeProcessed,
++ #endif
++ unsigned char *outStream, SizeT outSize, SizeT *outSizeProcessed)
++{
++ CProb *p = vs->Probs;
++ SizeT nowPos = 0;
++ Byte previousByte = 0;
++ UInt32 posStateMask = (1 << (vs->Properties.pb)) - 1;
++ UInt32 literalPosMask = (1 << (vs->Properties.lp)) - 1;
++ int lc = vs->Properties.lc;
++
++ #ifdef _LZMA_OUT_READ
++
++ UInt32 Range = vs->Range;
++ UInt32 Code = vs->Code;
++ #ifdef _LZMA_IN_CB
++ const Byte *Buffer = vs->Buffer;
++ const Byte *BufferLim = vs->BufferLim;
++ #else
++ const Byte *Buffer = inStream;
++ const Byte *BufferLim = inStream + inSize;
++ #endif
++ int state = vs->State;
++ UInt32 rep0 = vs->Reps[0], rep1 = vs->Reps[1], rep2 = vs->Reps[2], rep3 = vs->Reps[3];
++ int len = vs->RemainLen;
++ UInt32 globalPos = vs->GlobalPos;
++ UInt32 distanceLimit = vs->DistanceLimit;
++
++ Byte *dictionary = vs->Dictionary;
++ UInt32 dictionarySize = vs->Properties.DictionarySize;
++ UInt32 dictionaryPos = vs->DictionaryPos;
++
++ Byte tempDictionary[4];
++
++ #ifndef _LZMA_IN_CB
++ *inSizeProcessed = 0;
++ #endif
++ *outSizeProcessed = 0;
++ if (len == kLzmaStreamWasFinishedId)
++ return LZMA_RESULT_OK;
++
++ if (dictionarySize == 0)
++ {
++ dictionary = tempDictionary;
++ dictionarySize = 1;
++ tempDictionary[0] = vs->TempDictionary[0];
++ }
++
++ if (len == kLzmaNeedInitId)
++ {
++ {
++ UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + vs->Properties.lp));
++ UInt32 i;
++ for (i = 0; i < numProbs; i++)
++ p[i] = kBitModelTotal >> 1;
++ rep0 = rep1 = rep2 = rep3 = 1;
++ state = 0;
++ globalPos = 0;
++ distanceLimit = 0;
++ dictionaryPos = 0;
++ dictionary[dictionarySize - 1] = 0;
++ #ifdef _LZMA_IN_CB
++ RC_INIT;
++ #else
++ RC_INIT(inStream, inSize);
++ #endif
++ }
++ len = 0;
++ }
++ while(len != 0 && nowPos < outSize)
++ {
++ UInt32 pos = dictionaryPos - rep0;
++ if (pos >= dictionarySize)
++ pos += dictionarySize;
++ outStream[nowPos++] = dictionary[dictionaryPos] = dictionary[pos];
++ if (++dictionaryPos == dictionarySize)
++ dictionaryPos = 0;
++ len--;
++ }
++ if (dictionaryPos == 0)
++ previousByte = dictionary[dictionarySize - 1];
++ else
++ previousByte = dictionary[dictionaryPos - 1];
++
++ #else /* if !_LZMA_OUT_READ */
++
++ int state = 0;
++ UInt32 rep0 = 1, rep1 = 1, rep2 = 1, rep3 = 1;
++ int len = 0;
++ const Byte *Buffer;
++ const Byte *BufferLim;
++ UInt32 Range;
++ UInt32 Code;
++
++ #ifndef _LZMA_IN_CB
++ *inSizeProcessed = 0;
++ #endif
++ *outSizeProcessed = 0;
++
++ {
++ UInt32 i;
++ UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + vs->Properties.lp));
++ for (i = 0; i < numProbs; i++)
++ p[i] = kBitModelTotal >> 1;
++ }
++
++ #ifdef _LZMA_IN_CB
++ RC_INIT;
++ #else
++ RC_INIT(inStream, inSize);
++ #endif
++
++ #endif /* _LZMA_OUT_READ */
++
++ while(nowPos < outSize)
++ {
++ CProb *prob;
++ UInt32 bound;
++ int posState = (int)(
++ (nowPos
++ #ifdef _LZMA_OUT_READ
++ + globalPos
++ #endif
++ )
++ & posStateMask);
++
++ prob = p + IsMatch + (state << kNumPosBitsMax) + posState;
++ IfBit0(prob)
++ {
++ int symbol = 1;
++ UpdateBit0(prob)
++ prob = p + Literal + (LZMA_LIT_SIZE *
++ (((
++ (nowPos
++ #ifdef _LZMA_OUT_READ
++ + globalPos
++ #endif
++ )
++ & literalPosMask) << lc) + (previousByte >> (8 - lc))));
++
++ if (state >= kNumLitStates)
++ {
++ int matchByte;
++ #ifdef _LZMA_OUT_READ
++ UInt32 pos = dictionaryPos - rep0;
++ if (pos >= dictionarySize)
++ pos += dictionarySize;
++ matchByte = dictionary[pos];
++ #else
++ matchByte = outStream[nowPos - rep0];
++ #endif
++ do
++ {
++ int bit;
++ CProb *probLit;
++ matchByte <<= 1;
++ bit = (matchByte & 0x100);
++ probLit = prob + 0x100 + bit + symbol;
++ RC_GET_BIT2(probLit, symbol, if (bit != 0) break, if (bit == 0) break)
++ }
++ while (symbol < 0x100);
++ }
++ while (symbol < 0x100)
++ {
++ CProb *probLit = prob + symbol;
++ RC_GET_BIT(probLit, symbol)
++ }
++ previousByte = (Byte)symbol;
++
++ outStream[nowPos++] = previousByte;
++ #ifdef _LZMA_OUT_READ
++ if (distanceLimit < dictionarySize)
++ distanceLimit++;
++
++ dictionary[dictionaryPos] = previousByte;
++ if (++dictionaryPos == dictionarySize)
++ dictionaryPos = 0;
++ #endif
++ if (state < 4) state = 0;
++ else if (state < 10) state -= 3;
++ else state -= 6;
++ }
++ else
++ {
++ UpdateBit1(prob);
++ prob = p + IsRep + state;
++ IfBit0(prob)
++ {
++ UpdateBit0(prob);
++ rep3 = rep2;
++ rep2 = rep1;
++ rep1 = rep0;
++ state = state < kNumLitStates ? 0 : 3;
++ prob = p + LenCoder;
++ }
++ else
++ {
++ UpdateBit1(prob);
++ prob = p + IsRepG0 + state;
++ IfBit0(prob)
++ {
++ UpdateBit0(prob);
++ prob = p + IsRep0Long + (state << kNumPosBitsMax) + posState;
++ IfBit0(prob)
++ {
++ #ifdef _LZMA_OUT_READ
++ UInt32 pos;
++ #endif
++ UpdateBit0(prob);
++
++ #ifdef _LZMA_OUT_READ
++ if (distanceLimit == 0)
++ #else
++ if (nowPos == 0)
++ #endif
++ return LZMA_RESULT_DATA_ERROR;
++
++ state = state < kNumLitStates ? 9 : 11;
++ #ifdef _LZMA_OUT_READ
++ pos = dictionaryPos - rep0;
++ if (pos >= dictionarySize)
++ pos += dictionarySize;
++ previousByte = dictionary[pos];
++ dictionary[dictionaryPos] = previousByte;
++ if (++dictionaryPos == dictionarySize)
++ dictionaryPos = 0;
++ #else
++ previousByte = outStream[nowPos - rep0];
++ #endif
++ outStream[nowPos++] = previousByte;
++ #ifdef _LZMA_OUT_READ
++ if (distanceLimit < dictionarySize)
++ distanceLimit++;
++ #endif
++
++ continue;
++ }
++ else
++ {
++ UpdateBit1(prob);
++ }
++ }
++ else
++ {
++ UInt32 distance;
++ UpdateBit1(prob);
++ prob = p + IsRepG1 + state;
++ IfBit0(prob)
++ {
++ UpdateBit0(prob);
++ distance = rep1;
++ }
++ else
++ {
++ UpdateBit1(prob);
++ prob = p + IsRepG2 + state;
++ IfBit0(prob)
++ {
++ UpdateBit0(prob);
++ distance = rep2;
++ }
++ else
++ {
++ UpdateBit1(prob);
++ distance = rep3;
++ rep3 = rep2;
++ }
++ rep2 = rep1;
++ }
++ rep1 = rep0;
++ rep0 = distance;
++ }
++ state = state < kNumLitStates ? 8 : 11;
++ prob = p + RepLenCoder;
++ }
++ {
++ int numBits, offset;
++ CProb *probLen = prob + LenChoice;
++ IfBit0(probLen)
++ {
++ UpdateBit0(probLen);
++ probLen = prob + LenLow + (posState << kLenNumLowBits);
++ offset = 0;
++ numBits = kLenNumLowBits;
++ }
++ else
++ {
++ UpdateBit1(probLen);
++ probLen = prob + LenChoice2;
++ IfBit0(probLen)
++ {
++ UpdateBit0(probLen);
++ probLen = prob + LenMid + (posState << kLenNumMidBits);
++ offset = kLenNumLowSymbols;
++ numBits = kLenNumMidBits;
++ }
++ else
++ {
++ UpdateBit1(probLen);
++ probLen = prob + LenHigh;
++ offset = kLenNumLowSymbols + kLenNumMidSymbols;
++ numBits = kLenNumHighBits;
++ }
++ }
++ RangeDecoderBitTreeDecode(probLen, numBits, len);
++ len += offset;
++ }
++
++ if (state < 4)
++ {
++ int posSlot;
++ state += kNumLitStates;
++ prob = p + PosSlot +
++ ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) <<
++ kNumPosSlotBits);
++ RangeDecoderBitTreeDecode(prob, kNumPosSlotBits, posSlot);
++ if (posSlot >= kStartPosModelIndex)
++ {
++ int numDirectBits = ((posSlot >> 1) - 1);
++ rep0 = (2 | ((UInt32)posSlot & 1));
++ if (posSlot < kEndPosModelIndex)
++ {
++ rep0 <<= numDirectBits;
++ prob = p + SpecPos + rep0 - posSlot - 1;
++ }
++ else
++ {
++ numDirectBits -= kNumAlignBits;
++ do
++ {
++ RC_NORMALIZE
++ Range >>= 1;
++ rep0 <<= 1;
++ if (Code >= Range)
++ {
++ Code -= Range;
++ rep0 |= 1;
++ }
++ }
++ while (--numDirectBits != 0);
++ prob = p + Align;
++ rep0 <<= kNumAlignBits;
++ numDirectBits = kNumAlignBits;
++ }
++ {
++ int i = 1;
++ int mi = 1;
++ do
++ {
++ CProb *prob3 = prob + mi;
++ RC_GET_BIT2(prob3, mi, ; , rep0 |= i);
++ i <<= 1;
++ }
++ while(--numDirectBits != 0);
++ }
++ }
++ else
++ rep0 = posSlot;
++ if (++rep0 == (UInt32)(0))
++ {
++ /* it's for stream version */
++ len = kLzmaStreamWasFinishedId;
++ break;
++ }
++ }
++
++ len += kMatchMinLen;
++ #ifdef _LZMA_OUT_READ
++ if (rep0 > distanceLimit)
++ #else
++ if (rep0 > nowPos)
++ #endif
++ return LZMA_RESULT_DATA_ERROR;
++
++ #ifdef _LZMA_OUT_READ
++ if (dictionarySize - distanceLimit > (UInt32)len)
++ distanceLimit += len;
++ else
++ distanceLimit = dictionarySize;
++ #endif
++
++ do
++ {
++ #ifdef _LZMA_OUT_READ
++ UInt32 pos = dictionaryPos - rep0;
++ if (pos >= dictionarySize)
++ pos += dictionarySize;
++ previousByte = dictionary[pos];
++ dictionary[dictionaryPos] = previousByte;
++ if (++dictionaryPos == dictionarySize)
++ dictionaryPos = 0;
++ #else
++ previousByte = outStream[nowPos - rep0];
++ #endif
++ len--;
++ outStream[nowPos++] = previousByte;
++ }
++ while(len != 0 && nowPos < outSize);
++ }
++ }
++ RC_NORMALIZE;
++
++ #ifdef _LZMA_OUT_READ
++ vs->Range = Range;
++ vs->Code = Code;
++ vs->DictionaryPos = dictionaryPos;
++ vs->GlobalPos = globalPos + (UInt32)nowPos;
++ vs->DistanceLimit = distanceLimit;
++ vs->Reps[0] = rep0;
++ vs->Reps[1] = rep1;
++ vs->Reps[2] = rep2;
++ vs->Reps[3] = rep3;
++ vs->State = state;
++ vs->RemainLen = len;
++ vs->TempDictionary[0] = tempDictionary[0];
++ #endif
++
++ #ifdef _LZMA_IN_CB
++ vs->Buffer = Buffer;
++ vs->BufferLim = BufferLim;
++ #else
++ *inSizeProcessed = (SizeT)(Buffer - inStream);
++ #endif
++ *outSizeProcessed = nowPos;
++ return LZMA_RESULT_OK;
++}
+diff --git a/arch/i386/boot/compressed/LzmaDecode.h b/arch/i386/boot/compressed/LzmaDecode.h
+new file mode 100644
+index 0000000..213062a
+--- /dev/null
++++ b/arch/i386/boot/compressed/LzmaDecode.h
+@@ -0,0 +1,131 @@
++/*
++ LzmaDecode.h
++ LZMA Decoder interface
++
++ LZMA SDK 4.21 Copyright (c) 1999-2005 Igor Pavlov (2005-06-08)
++ http://www.7-zip.org/
++
++ LZMA SDK is licensed under two licenses:
++ 1) GNU Lesser General Public License (GNU LGPL)
++ 2) Common Public License (CPL)
++ It means that you can select one of these two licenses and
++ follow rules of that license.
++
++ SPECIAL EXCEPTION:
++ Igor Pavlov, as the author of this code, expressly permits you to
++ statically or dynamically link your code (or bind by name) to the
++ interfaces of this file without subjecting your linked code to the
++ terms of the CPL or GNU LGPL. Any modifications or additions
++ to this file, however, are subject to the LGPL or CPL terms.
++*/
++
++#ifndef __LZMADECODE_H
++#define __LZMADECODE_H
++
++/* #define _LZMA_IN_CB */
++/* Use callback for input data */
++
++/* #define _LZMA_OUT_READ */
++/* Use read function for output data */
++
++/* #define _LZMA_PROB32 */
++/* It can increase speed on some 32-bit CPUs,
++ but memory usage will be doubled in that case */
++
++/* #define _LZMA_LOC_OPT */
++/* Enable local speed optimizations inside code */
++
++/* #define _LZMA_SYSTEM_SIZE_T */
++/* Use system's size_t. You can use it to enable 64-bit sizes supporting*/
++
++#ifndef UInt32
++#ifdef _LZMA_UINT32_IS_ULONG
++#define UInt32 unsigned long
++#else
++#define UInt32 unsigned int
++#endif
++#endif
++
++#ifndef SizeT
++#ifdef _LZMA_SYSTEM_SIZE_T
++#include <stddef.h>
++#define SizeT size_t
++#else
++#define SizeT UInt32
++#endif
++#endif
++
++#ifdef _LZMA_PROB32
++#define CProb UInt32
++#else
++#define CProb unsigned short
++#endif
++
++#define LZMA_RESULT_OK 0
++#define LZMA_RESULT_DATA_ERROR 1
++
++#ifdef _LZMA_IN_CB
++typedef struct _ILzmaInCallback
++{
++ int (*Read)(void *object, const unsigned char **buffer, SizeT *bufferSize);
++} ILzmaInCallback;
++#endif
++
++#define LZMA_BASE_SIZE 1846
++#define LZMA_LIT_SIZE 768
++
++#define LZMA_PROPERTIES_SIZE 5
++
++typedef struct _CLzmaProperties
++{
++ int lc;
++ int lp;
++ int pb;
++ #ifdef _LZMA_OUT_READ
++ UInt32 DictionarySize;
++ #endif
++}CLzmaProperties;
++
++int LzmaDecodeProperties(CLzmaProperties *propsRes, const unsigned char *propsData, int size);
++
++#define LzmaGetNumProbs(Properties) (LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((Properties)->lc + (Properties)->lp)))
++
++#define kLzmaNeedInitId (-2)
++
++typedef struct _CLzmaDecoderState
++{
++ CLzmaProperties Properties;
++ CProb *Probs;
++
++ #ifdef _LZMA_IN_CB
++ const unsigned char *Buffer;
++ const unsigned char *BufferLim;
++ #endif
++
++ #ifdef _LZMA_OUT_READ
++ unsigned char *Dictionary;
++ UInt32 Range;
++ UInt32 Code;
++ UInt32 DictionaryPos;
++ UInt32 GlobalPos;
++ UInt32 DistanceLimit;
++ UInt32 Reps[4];
++ int State;
++ int RemainLen;
++ unsigned char TempDictionary[4];
++ #endif
++} CLzmaDecoderState;
++
++#ifdef _LZMA_OUT_READ
++#define LzmaDecoderInit(vs) { (vs)->RemainLen = kLzmaNeedInitId; }
++#endif
++
++int LzmaDecode(CLzmaDecoderState *vs,
++ #ifdef _LZMA_IN_CB
++ ILzmaInCallback *inCallback,
++ #else
++ const unsigned char *inStream, SizeT inSize, SizeT *inSizeProcessed,
++ #endif
++ unsigned char *outStream, SizeT outSize, SizeT *outSizeProcessed);
++
++#endif
+diff --git a/arch/i386/boot/compressed/Makefile b/arch/i386/boot/compressed/Makefile
+index a661217..fb40869 100644
+--- a/arch/i386/boot/compressed/Makefile
++++ b/arch/i386/boot/compressed/Makefile
+@@ -4,15 +4,16 @@
+ # create a compressed vmlinux image from the original vmlinux
+ #
+
+-targets := vmlinux vmlinux.bin vmlinux.bin.gz head.o misc.o piggy.o \
+- vmlinux.bin.all vmlinux.relocs
++tragets := head.o lzma_misc.o piggy.o \
++ vmlinux.bin.all vmlinux.relocs \
++ vmlinux vmlinux.bin vmlinux.bin.gz
+ EXTRA_AFLAGS := -traditional
+
+ LDFLAGS_vmlinux := -T
+-CFLAGS_misc.o += -fPIC
++CFLAGS_lzma_misc.o += -fPIC
+ hostprogs-y := relocs
+
+-$(obj)/vmlinux: $(src)/vmlinux.lds $(obj)/head.o $(obj)/misc.o $(obj)/piggy.o FORCE
++$(obj)/vmlinux: $(src)/vmlinux.lds $(obj)/head.o $(obj)/lzma_misc.o $(obj)/piggy.o FORCE
+ $(call if_changed,ld)
+ @:
+
+@@ -33,10 +34,10 @@ $(obj)/vmlinux.bin.all: $(vmlinux.bin.all-y) FORCE
+
+ ifdef CONFIG_RELOCATABLE
+ $(obj)/vmlinux.bin.gz: $(obj)/vmlinux.bin.all FORCE
+- $(call if_changed,gzip)
++ $(call if_changed,lzma)
+ else
+ $(obj)/vmlinux.bin.gz: $(obj)/vmlinux.bin FORCE
+- $(call if_changed,gzip)
++ $(call if_changed,lzma)
+ endif
+
+ LDFLAGS_piggy.o := -r --format binary --oformat elf32-i386 -T
+diff --git a/arch/i386/boot/compressed/lzma_misc.c b/arch/i386/boot/compressed/lzma_misc.c
+new file mode 100644
+index 0000000..4f5f7f9
+--- /dev/null
++++ b/arch/i386/boot/compressed/lzma_misc.c
+@@ -0,0 +1,290 @@
++/*
++ * lzma_misc.c
++ *
++ * Decompress LZMA compressed vmlinuz
++ * Version 0.9 Copyright (c) Ming-Ching Tiew mctiew@yahoo.com
++ * Program adapted from misc.c for 2.6.20.1 kernel
++ * Please refer to misc.c for authorship and copyright.
++ * Date: 25 March 2007
++ * Source released under GPL
++ */
++
++#undef CONFIG_PARAVIRT
++#include <linux/linkage.h>
++#include <linux/vmalloc.h>
++#include <linux/screen_info.h>
++#include <asm/io.h>
++#include <asm/page.h>
++#include <asm/boot.h>
++
++/* WARNING!!
++ * This code is compiled with -fPIC and it is relocated dynamically
++ * at run time, but no relocation processing is performed.
++ * This means that it is not safe to place pointers in static structures.
++ */
++
++#define OF(args) args
++#define STATIC static
++
++#undef memset
++#undef memcpy
++
++typedef unsigned char uch;
++typedef unsigned short ush;
++typedef unsigned long ulg;
++
++#define WSIZE 0x80000000 /* Window size must be at least 32k,
++ * and a power of two
++ * We don't actually have a window just
++ * a huge output buffer so I report
++ * a 2G windows size, as that should
++ * always be larger than our output buffer.
++ */
++
++static uch *inbuf; /* input buffer */
++static uch *window; /* Sliding window buffer, (and final output buffer) */
++
++static unsigned insize; /* valid bytes in inbuf */
++static unsigned inptr; /* index of next byte to be processed in inbuf */
++
++/* gzip flag byte */
++#define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */
++#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
++#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
++#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
++#define COMMENT 0x10 /* bit 4 set: file comment present */
++#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */
++#define RESERVED 0xC0 /* bit 6,7: reserved */
++
++#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf())
++
++/* Diagnostic functions */
++#ifdef DEBUG
++# define Assert(cond,msg) {if(!(cond)) error(msg);}
++# define Trace(x) fprintf x
++# define Tracev(x) {if (verbose) fprintf x ;}
++# define Tracevv(x) {if (verbose>1) fprintf x ;}
++# define Tracec(c,x) {if (verbose && (c)) fprintf x ;}
++# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;}
++#else
++# define Assert(cond,msg)
++# define Trace(x)
++# define Tracev(x)
++# define Tracevv(x)
++# define Tracec(c,x)
++# define Tracecv(c,x)
++#endif
++
++static int fill_inbuf(void);
++static void error(char *m);
++
++/*
++ * This is set up by the setup-routine at boot-time
++ */
++static unsigned char *real_mode; /* Pointer to real-mode data */
++
++#define RM_EXT_MEM_K (*(unsigned short *)(real_mode + 0x2))
++#ifndef STANDARD_MEMORY_BIOS_CALL
++#define RM_ALT_MEM_K (*(unsigned long *)(real_mode + 0x1e0))
++#endif
++#define RM_SCREEN_INFO (*(struct screen_info *)(real_mode+0))
++
++extern unsigned char input_data[];
++extern int input_len;
++
++static long bytes_out = 0;
++
++static void *memcpy(void *dest, const void *src, unsigned n);
++
++static void putstr(const char *);
++
++static unsigned long free_mem_ptr;
++static unsigned long free_mem_end_ptr;
++
++#define HEAP_SIZE 0x3000
++
++static char *vidmem = (char *)0xb8000;
++static int vidport;
++static int lines, cols;
++
++#ifdef CONFIG_X86_NUMAQ
++void *xquad_portio;
++#endif
++
++static void scroll(void)
++{
++ int i;
++
++ memcpy ( vidmem, vidmem + cols * 2, ( lines - 1 ) * cols * 2 );
++ for ( i = ( lines - 1 ) * cols * 2; i < lines * cols * 2; i += 2 )
++ vidmem[i] = ' ';
++}
++
++static void putstr(const char *s)
++{
++ int x,y,pos;
++ char c;
++
++ x = RM_SCREEN_INFO.orig_x;
++ y = RM_SCREEN_INFO.orig_y;
++
++ while ( ( c = *s++ ) != '\0' ) {
++ if ( c == '\n' ) {
++ x = 0;
++ if ( ++y >= lines ) {
++ scroll();
++ y--;
++ }
++ } else {
++ vidmem [ ( x + cols * y ) * 2 ] = c;
++ if ( ++x >= cols ) {
++ x = 0;
++ if ( ++y >= lines ) {
++ scroll();
++ y--;
++ }
++ }
++ }
++ }
++
++ RM_SCREEN_INFO.orig_x = x;
++ RM_SCREEN_INFO.orig_y = y;
++
++ pos = (x + cols * y) * 2; /* Update cursor position */
++ outb_p(14, vidport);
++ outb_p(0xff & (pos >> 9), vidport+1);
++ outb_p(15, vidport);
++ outb_p(0xff & (pos >> 1), vidport+1);
++}
++
++static void* memcpy(void* dest, const void* src, unsigned n)
++{
++ int i;
++ char *d = (char *)dest, *s = (char *)src;
++
++ for (i=0;i<n;i++) d[i] = s[i];
++ return dest;
++}
++
++/* ===========================================================================
++ * Fill the input buffer. This is called only when the buffer is empty
++ * and at least one byte is really needed.
++ */
++static int fill_inbuf(void)
++{
++ error("ran out of input data");
++ return 0;
++}
++
++/* ===========================================================================
++ */
++static void error(char *x)
++{
++ putstr("\n\n");
++ putstr(x);
++ putstr("\n\n -- System halted");
++
++ while(1); /* Halt */
++}
++
++#define _LZMA_IN_CB
++#include "LzmaDecode.h"
++#include "LzmaDecode.c"
++
++static int read_byte(void *object, const unsigned char **buffer, SizeT *bufferSize);
++
++/*
++ * Do the lzma decompression
++ */
++static int lzma_unzip(uch* output)
++{
++
++ unsigned int i;
++ CLzmaDecoderState state;
++ unsigned int uncompressedSize = 0;
++ unsigned char* p;
++
++ ILzmaInCallback callback;
++ callback.Read = read_byte;
++
++ // lzma args
++ i = get_byte();
++ state.Properties.lc = i % 9, i = i / 9;
++ state.Properties.lp = i % 5, state.Properties.pb = i / 5;
++
++ // skip dictionary size
++ for (i = 0; i < 4; i++)
++ get_byte();
++ // get uncompressed size
++ p= (char*)&uncompressedSize;
++ for (i = 0; i < 4; i++)
++ *p++ = get_byte();
++
++ // skip high order bytes
++ for (i = 0; i < 4; i++)
++ get_byte();
++
++ // Just point it beyond
++ state.Probs = (CProb*) ( free_mem_ptr );
++ // decompress kernel
++ if (LzmaDecode( &state, &callback,
++ (unsigned char*)output, uncompressedSize, &i) == LZMA_RESULT_OK)
++ {
++ if ( i != uncompressedSize )
++ error( "kernel corrupted!\n");
++ bytes_out = i;
++ return 0;
++ }
++ return 1;
++}
++
++
++static int read_byte(void *object, const unsigned char **buffer, SizeT *bufferSize)
++{
++ static unsigned int i = 0;
++ static unsigned char val;
++ *bufferSize = 1;
++ val = get_byte();
++ *buffer = &val;
++ return LZMA_RESULT_OK;
++}
++
++asmlinkage void decompress_kernel(void *rmode, unsigned long end,
++ uch *input_data, unsigned long input_len, uch *output)
++{
++ real_mode = rmode;
++
++ if (RM_SCREEN_INFO.orig_video_mode == 7) {
++ vidmem = (char *) 0xb0000;
++ vidport = 0x3b4;
++ } else {
++ vidmem = (char *) 0xb8000;
++ vidport = 0x3d4;
++ }
++
++ lines = RM_SCREEN_INFO.orig_video_lines;
++ cols = RM_SCREEN_INFO.orig_video_cols;
++
++ window = output; /* Output buffer (Normally at 1M) */
++ free_mem_ptr = end; /* Heap */
++ free_mem_end_ptr = end + HEAP_SIZE;
++ inbuf = input_data; /* Input buffer */
++ insize = input_len;
++ inptr = 0;
++
++ if ((u32)output & (CONFIG_PHYSICAL_ALIGN -1))
++ error("Destination address not CONFIG_PHYSICAL_ALIGN aligned");
++ if (end > ((-__PAGE_OFFSET-(512 <<20)-1) & 0x7fffffff))
++ error("Destination address too large");
++#ifndef CONFIG_RELOCATABLE
++ if ((u32)output != LOAD_PHYSICAL_ADDR)
++ error("Wrong destination address");
++#endif
++ if( lzma_unzip(output) != 0 )
++ {
++ error("inflate error\n");
++ }
++ putstr("Ok, booting the kernel.\n");
++
++ return;
++}
+diff --git a/arch/i386/boot/compressed/vmlinux.scr b/arch/i386/boot/compressed/vmlinux.scr
+index 707a88f..9d67263 100644
+--- a/arch/i386/boot/compressed/vmlinux.scr
++++ b/arch/i386/boot/compressed/vmlinux.scr
+@@ -3,8 +3,8 @@ SECTIONS
+ .data.compressed : {
+ input_len = .;
+ LONG(input_data_end - input_data) input_data = .;
++ output_len = . + 5;
+ *(.data)
+- output_len = . - 4;
+ input_data_end = .;
+ }
+ }
+diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig
+index 17ee97f..64b7bda 100644
+--- a/drivers/block/Kconfig
++++ b/drivers/block/Kconfig
+@@ -406,6 +406,47 @@ config BLK_DEV_RAM_BLOCKSIZE
+ setups function - apparently needed by the rd_load_image routine
+ that supposes the filesystem in the image uses a 1024 blocksize.
+
++config LZMA_INITRD
++ boolean "Allow LZMA compression on initrd"
++ depends on BLK_DEV_INITRD=y
++ default "y"
++ help
++ Use lzma compression on initrd, example 'lzma e initrd initrd.7z -d16'.
++ If you have sufficient memory, you could compress using bigger dictionary size,
++ 'lzma e initrd initrd.7z'.
++
++config LZMA_INITRD_KMALLOC_ONLY
++ boolean "Use only kmalloc, do not use vmalloc on lzma initrd"
++ depends on LZMA_INITRD=y
++ default "n"
++ help
++ Set to y if you do not want to use vmalloc, ie use only kmalloc.
++
++config LZMA_INITRAM_FS
++ boolean "Allow LZMA compression on initramfs"
++ depends on BLK_DEV_RAM=y
++ default "y"
++ help
++ Use lzma compression on initramfs, example 'lzma e initramfs.cpio initramfs.cpio.lzma'.
++
++config LZMA_INITRAM_FS_SMALLMEM
++ boolean "Use lzma compression with small dictonary size."
++ depends on LZMA_INITRAM_FS=y
++ default "y"
++ help
++ Use lzma compression on initramfs with small dictionary size, example
++ 'lzma e initramfs.cpio initramfs.cpio.lzma -d16'.
++ Affects only the initramfs.cpio in the ~usr directory, which is compiled into
++ the kernel. If you prepared initramfs.cpio for use with bootloader, you would
++ need to specify the commandline options (-d16) yourself.
++
++config LZMA_INITRAM_FS_KMALLOC_ONLY
++ boolean "Use only kmalloc, do not use vmalloc on lzma initramfs"
++ depends on LZMA_INITRAM_FS=y
++ default "n"
++ help
++ Set to y if you do not want to use vmalloc, ie use only kmalloc.
++
+ config CDROM_PKTCDVD
+ tristate "Packet writing on CD/DVD media"
+ depends on !UML
+diff --git a/fs/Kconfig b/fs/Kconfig
+index 3c4886b..bdcc6fb 100644
+--- a/fs/Kconfig
++++ b/fs/Kconfig
+@@ -1371,6 +1371,71 @@ config CRAMFS
+
+ If unsure, say N.
+
++config SQUASHFS
++ tristate "SquashFS 3.2 - Squashed file system support"
++ select ZLIB_INFLATE
++ help
++ Saying Y here includes support for SquashFS 3.2 (a Compressed Read-Only File
++ System). Squashfs is a highly compressed read-only filesystem for Linux.
++ It uses zlib compression to compress both files, inodes and directories.
++ Inodes in the system are very small and all blocks are packed to minimise
++ data overhead. Block sizes greater than 4K are supported up to a maximum of 64K.
++ SquashFS 3.1 supports 64 bit filesystems and files (larger than 4GB), full
++ uid/gid information, hard links and timestamps.
++
++ Squashfs is intended for general read-only filesystem use, for archival
++ use (i.e. in cases where a .tar.gz file may be used), and in embedded
++ systems where low overhead is needed. Further information and filesystem tools
++ are available from http://squashfs.sourceforge.net.
++
++ If you want to compile this as a module ( = code which can be
++ inserted in and removed from the running kernel whenever you want),
++ say M here and read <file:Documentation/modules.txt>. The module
++ will be called squashfs. Note that the root file system (the one
++ containing the directory /) cannot be compiled as a module.
++
++ If unsure, say N.
++
++config SQUASHFS_EMBEDDED
++
++ bool "Additional options for memory-constrained systems"
++ depends on SQUASHFS
++ default n
++ help
++ Saying Y here allows you to specify cache sizes and how Squashfs
++ allocates memory. This is only intended for memory constrained
++ systems.
++
++ If unsure, say N.
++
++config SQUASHFS_FRAGMENT_CACHE_SIZE
++ int "Number of fragments cached" if SQUASHFS_EMBEDDED
++ depends on SQUASHFS
++ default "3"
++ help
++ By default SquashFS caches the last 3 fragments read from
++ the filesystem. Increasing this amount may mean SquashFS
++ has to re-read fragments less often from disk, at the expense
++ of extra system memory. Decreasing this amount will mean
++ SquashFS uses less memory at the expense of extra reads from disk.
++
++ Note there must be at least one cached fragment. Anything
++ much more than three will probably not make much difference.
++
++config SQUASHFS_VMALLOC
++ bool "Use Vmalloc rather than Kmalloc" if SQUASHFS_EMBEDDED
++ depends on SQUASHFS
++ default n
++ help
++ By default SquashFS uses kmalloc to obtain fragment cache memory.
++ Kmalloc memory is the standard kernel allocator, but it can fail
++ on memory constrained systems. Because of the way Vmalloc works,
++ Vmalloc can succeed when kmalloc fails. Specifying this option
++ will make SquashFS always use Vmalloc to allocate the
++ fragment cache memory.
++
++ If unsure, say N.
++
+ config VXFS_FS
+ tristate "FreeVxFS file system support (VERITAS VxFS(TM) compatible)"
+ depends on BLOCK
+@@ -2057,3 +2122,4 @@ source "fs/dlm/Kconfig"
+
+ endmenu
+
++source "fs/aufs/Kconfig"
+diff --git a/fs/Makefile b/fs/Makefile
+index 9edf411..557766f 100644
+--- a/fs/Makefile
++++ b/fs/Makefile
+@@ -68,6 +68,7 @@ obj-$(CONFIG_JBD) += jbd/
+ obj-$(CONFIG_JBD2) += jbd2/
+ obj-$(CONFIG_EXT2_FS) += ext2/
+ obj-$(CONFIG_CRAMFS) += cramfs/
++obj-$(CONFIG_SQUASHFS) += squashfs/
+ obj-$(CONFIG_RAMFS) += ramfs/
+ obj-$(CONFIG_HUGETLBFS) += hugetlbfs/
+ obj-$(CONFIG_CODA_FS) += coda/
+@@ -114,3 +115,4 @@ obj-$(CONFIG_HPPFS) += hppfs/
+ obj-$(CONFIG_DEBUG_FS) += debugfs/
+ obj-$(CONFIG_OCFS2_FS) += ocfs2/
+ obj-$(CONFIG_GFS2_FS) += gfs2/
++obj-$(CONFIG_AUFS) += aufs/
+diff --git a/fs/aufs/Kconfig b/fs/aufs/Kconfig
+new file mode 100644
+index 0000000..3a2121c
+--- /dev/null
++++ b/fs/aufs/Kconfig
+@@ -0,0 +1,73 @@
++config AUFS
++ tristate "Another unionfs"
++ 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
++comment "These options are generated automatically for "#UTS_RELEASE
++
++config AUFS_FAKE_DM
++ bool "Use simplified (fake) nameidata"
++ depends on AUFS
++ default y
++ help
++ Faking nameidata (VFS internal data), you can get better performance
++ in some cases.
++
++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_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_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
+diff --git a/fs/aufs/Makefile b/fs/aufs/Makefile
+new file mode 100755
+index 0000000..0ee3cd0
+--- /dev/null
++++ b/fs/aufs/Makefile
+@@ -0,0 +1,18 @@
++# AUFS Makefile for the Linux 2.6.16 and later
++# $Id: Makefile,v 1.29 2007/04/23 00:59:50 sfjro Exp $
++
++obj-$(CONFIG_AUFS) += aufs.o
++aufs-y := module.o super.o sbinfo.o xino.o \
++ branch.o cpup.o whout.o plink.o wkq.o dcsub.o vfsub.o \
++ opts.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_AUFS_SYSAUFS) += sysaufs.o
++aufs-$(CONFIG_AUFS_HINOTIFY) += hinotify.o
++aufs-$(CONFIG_AUFS_EXPORT) += export.o
++#aufs-$(CONFIG_DEBUGFS) += dbgfs.o
++aufs-$(CONFIG_AUFS_DEBUG) += debug.o
+diff --git a/fs/aufs/aufs.h b/fs/aufs/aufs.h
+new file mode 100755
+index 0000000..79b3b87
+--- /dev/null
++++ b/fs/aufs/aufs.h
+@@ -0,0 +1,64 @@
++/*
++ * 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: aufs.h,v 1.24 2007/05/14 03:41:51 sfjro Exp $ */
++
++#ifndef __AUFS_H__
++#define __AUFS_H__
++
++#ifdef __KERNEL__
++
++#include <linux/version.h>
++
++/* limited support before 2.6.16, curretly 2.6.15 only. */
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
++#define atomic_long_t atomic_t
++#define atomic_long_set atomic_set
++#define timespec_to_ns(ts) ({(long long)(ts)->tv_sec;})
++#define D_CHILD d_child
++#else
++#define D_CHILD d_u.d_child
++#endif
++
++/* ---------------------------------------------------------------------- */
++
++#include "debug.h"
++
++#include "branch.h"
++#include "cpup.h"
++#include "dcsub.h"
++#include "dentry.h"
++#include "dir.h"
++#include "file.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"
++
++#if defined(CONFIG_AUFS_MODULE) && !defined(CONFIG_AUFS_KSIZE_PATCH)
++#define ksize(p) (-1U)
++#endif
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_H__ */
+diff --git a/fs/aufs/branch.c b/fs/aufs/branch.c
+new file mode 100755
+index 0000000..f1ce008
+--- /dev/null
++++ b/fs/aufs/branch.c
+@@ -0,0 +1,818 @@
++/*
++ * 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: branch.c,v 1.49 2007/05/14 03:38:23 sfjro Exp $ */
++
++//#include <linux/fs.h>
++//#include <linux/namei.h>
++#include "aufs.h"
++
++static void free_branch(struct aufs_branch *br)
++{
++ TraceEnter();
++
++ if (br->br_xino)
++ fput(br->br_xino);
++ dput(br->br_wh);
++ dput(br->br_plink);
++ mntput(br->br_mnt);
++ DEBUG_ON(br_count(br) || atomic_read(&br->br_wh_running));
++ kfree(br);
++}
++
++/*
++ * frees all branches
++ */
++void free_branches(struct aufs_sbinfo *sbinfo)
++{
++ aufs_bindex_t bmax;
++ struct aufs_branch **br;
++
++ TraceEnter();
++ 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 find_brindex(struct super_block *sb, aufs_bindex_t br_id)
++{
++ aufs_bindex_t bindex, bend;
++
++ TraceEnter();
++
++ bend = sbend(sb);
++ for (bindex = 0; bindex <= bend; bindex++)
++ if (sbr_id(sb, bindex) == br_id)
++ return bindex;
++ return -1;
++}
++
++/*
++ * test if the @br is readonly or not.
++ */
++int br_rdonly(struct aufs_branch *br)
++{
++ return ((br->br_mnt->mnt_sb->s_flags & MS_RDONLY)
++ || !br_writable(br->br_perm))
++ ? -EROFS : 0;
++}
++
++/*
++ * returns writable branch index, otherwise an error.
++ * todo: customizable writable-branch-policy
++ */
++static int find_rw_parent(struct dentry *dentry, aufs_bindex_t bend)
++{
++ int err;
++ aufs_bindex_t bindex, candidate;
++ struct super_block *sb;
++ struct dentry *parent, *hidden_parent;
++
++ err = bend;
++ sb = dentry->d_sb;
++ parent = dget_parent(dentry);
++#if 1 // branch policy
++ hidden_parent = au_h_dptr_i(parent, bend);
++ if (hidden_parent && !br_rdonly(stobr(sb, bend)))
++ goto out; /* success */
++#endif
++
++ candidate = -1;
++ for (bindex = dbstart(parent); bindex <= bend; bindex++) {
++ hidden_parent = au_h_dptr_i(parent, bindex);
++ if (hidden_parent && !br_rdonly(stobr(sb, bindex))) {
++#if 0 // branch policy
++ if (candidate == -1)
++ candidate = bindex;
++ if (!au_test_perm(hidden_parent->d_inode, MAY_WRITE))
++ return bindex;
++#endif
++ err = bindex;
++ goto out; /* success */
++ }
++ }
++#if 0 // branch policy
++ err = candidate;
++ if (candidate != -1)
++ goto out; /* success */
++#endif
++ err = -EROFS;
++
++ out:
++ dput(parent);
++ return err;
++}
++
++int find_rw_br(struct super_block *sb, aufs_bindex_t bend)
++{
++ aufs_bindex_t bindex;
++
++ for (bindex = bend; bindex >= 0; bindex--)
++ if (!br_rdonly(stobr(sb, bindex)))
++ return bindex;
++ return -EROFS;
++}
++
++int find_rw_parent_br(struct dentry *dentry, aufs_bindex_t bend)
++{
++ int err;
++
++ err = find_rw_parent(dentry, bend);
++ if (err >= 0)
++ return err;
++ return find_rw_br(dentry->d_sb, bend);
++}
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * test if two hidden_dentries have overlapping branches.
++ */
++//todo: try is_subdir()
++static int do_is_overlap(struct super_block *sb, struct dentry *hidden_d1,
++ struct dentry *hidden_d2)
++{
++ struct dentry *d;
++
++ d = hidden_d1;
++ do {
++ if (unlikely(d == hidden_d2))
++ return 1;
++ d = d->d_parent; // dget_parent()
++ } while (!IS_ROOT(d));
++
++ return (d == hidden_d2);
++}
++
++#if defined(CONFIG_BLK_DEV_LOOP) || defined(CONFIG_BLK_DEV_LOOP_MODULE)
++#include <linux/loop.h>
++static int is_overlap_loopback(struct super_block *sb, struct dentry *hidden_d1,
++ struct dentry *hidden_d2)
++{
++ struct inode *hidden_inode;
++ struct loop_device *l;
++
++ hidden_inode = hidden_d1->d_inode;
++ if (MAJOR(hidden_inode->i_sb->s_dev) != LOOP_MAJOR)
++ return 0;
++
++ l = hidden_inode->i_sb->s_bdev->bd_disk->private_data;
++ hidden_d1 = l->lo_backing_file->f_dentry;
++ if (unlikely(hidden_d1->d_sb == sb))
++ return 1;
++ return do_is_overlap(sb, hidden_d1, hidden_d2);
++}
++#else
++#define is_overlap_loopback(sb, hidden_d1, hidden_d2) 0
++#endif
++
++static int is_overlap(struct super_block *sb, struct dentry *hidden_d1,
++ struct dentry *hidden_d2)
++{
++ LKTRTrace("d1 %.*s, d2 %.*s\n", DLNPair(hidden_d1), DLNPair(hidden_d2));
++ if (unlikely(hidden_d1 == hidden_d2))
++ return 1;
++ return do_is_overlap(sb, hidden_d1, hidden_d2)
++ || do_is_overlap(sb, hidden_d2, hidden_d1)
++ || is_overlap_loopback(sb, hidden_d1, hidden_d2)
++ || is_overlap_loopback(sb, hidden_d2, hidden_d1);
++}
++
++/* ---------------------------------------------------------------------- */
++
++static int init_br_wh(struct super_block *sb, aufs_bindex_t bindex,
++ struct aufs_branch *br, int new_perm,
++ struct dentry *h_root, struct vfsmount *h_mnt)
++{
++ int err, old_perm;
++ struct inode *dir = sb->s_root->d_inode,
++ *h_dir = h_root->d_inode;
++ const int new = (bindex < 0);
++
++ LKTRTrace("b%d, new_perm %d\n", bindex, new_perm);
++
++ if (new)
++ hi_lock_parent(h_dir);
++ else
++ hdir_lock(h_dir, dir, bindex);
++
++ br_wh_write_lock(br);
++ old_perm = br->br_perm;
++ br->br_perm = new_perm;
++ err = init_wh(h_root, br, au_do_nfsmnt(h_mnt), sb);
++ br->br_perm = old_perm;
++ br_wh_write_unlock(br);
++
++ if (new)
++ i_unlock(h_dir);
++ else
++ hdir_unlock(h_dir, dir, bindex);
++
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * returns a newly allocated branch. @new_nbranch is a number of branches
++ * after adding a branch.
++ */
++static struct aufs_branch *alloc_addbr(struct super_block *sb, int new_nbranch)
++{
++ struct aufs_branch **branchp, *add_branch;
++ int sz;
++ void *p;
++ struct dentry *root;
++ struct inode *inode;
++ struct aufs_hinode *hinodep;
++ struct aufs_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 = stosi(sb)->si_branch;
++ branchp = au_kzrealloc(p, sz, sizeof(*branchp) * new_nbranch,
++ GFP_KERNEL);
++ //if (LktrCond) branchp = NULL;
++ if (unlikely(!branchp))
++ goto out;
++ stosi(sb)->si_branch = branchp;
++
++ sz = sizeof(*hdentryp) * (new_nbranch - 1);
++ if (unlikely(!sz))
++ sz = sizeof(*hdentryp);
++ p = dtodi(root)->di_hdentry;
++ hdentryp = au_kzrealloc(p, sz, sizeof(*hdentryp) * new_nbranch,
++ GFP_KERNEL);
++ //if (LktrCond) hdentryp = NULL;
++ if (unlikely(!hdentryp))
++ goto out;
++ dtodi(root)->di_hdentry = hdentryp;
++
++ sz = sizeof(*hinodep) * (new_nbranch - 1);
++ if (unlikely(!sz))
++ sz = sizeof(*hinodep);
++ p = itoii(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;
++ itoii(inode)->ii_hinode = hinodep;
++ return add_branch; /* success */
++
++ out:
++ kfree(add_branch);
++ TraceErr(-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(br_writable(brperm) && IS_RDONLY(inode))) {
++ Err("write permission for readonly fs or inode, %s\n", path);
++ err = -EINVAL;
++ }
++
++ TraceErr(err);
++ return err;
++}
++
++/*
++ * retunrs,,,
++ * 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 opt_add *add, int remount)
++{
++ int err;
++ struct dentry *root;
++ struct inode *inode, *hidden_inode;
++ aufs_bindex_t bend, bindex;
++
++ LKTRTrace("%s, remo%d\n", add->path, remount);
++
++ root = sb->s_root;
++ if (unlikely(au_find_dbindex(root, add->nd.dentry) != -1)) {
++ err = 1;
++ if (!remount) {
++ err = -EINVAL;
++ Err("%s duplicated\n", add->path);
++ }
++ goto out;
++ }
++
++ err = -ENOSPC; //-E2BIG;
++ bend = sbend(sb);
++ //if (LktrCond) bend = AUFS_BRANCH_MAX;
++ if (unlikely(AUFS_BRANCH_MAX <= add->bindex
++ || AUFS_BRANCH_MAX - 1 <= bend)) {
++ Err("number of branches exceeded %s\n", add->path);
++ goto out;
++ }
++
++ err = -EDOM;
++ if (unlikely(add->bindex < 0 || bend + 1 < add->bindex)) {
++ Err("bad index %d\n", add->bindex);
++ goto out;
++ }
++
++ inode = add->nd.dentry->d_inode;
++ DEBUG_ON(!inode || !S_ISDIR(inode->i_mode));
++ err = -ENOENT;
++ if (unlikely(!inode->i_nlink)) {
++ Err("no existence %s\n", add->path);
++ goto out;
++ }
++
++ err = -EINVAL;
++ if (unlikely(inode->i_sb == sb)) {
++ Err("%s must be outside\n", add->path);
++ goto out;
++ }
++
++#if 1 //ndef CONFIG_AUFS_ROBR
++ if (unlikely(au_is_aufs(inode->i_sb)
++ || !strcmp(au_sbtype(inode->i_sb), "unionfs"))) {
++ Err("nested " AUFS_NAME " %s\n", add->path);
++ goto out;
++ }
++#endif
++
++#ifdef AuNoNfsBranch
++ if (unlikely(au_is_nfs(inode->i_sb))) {
++ Err(AuNoNfsBranchMsg ". %s\n", add->path);
++ goto out;
++ }
++#endif
++
++ err = test_br(sb, add->nd.dentry->d_inode, add->perm, add->path);
++ if (unlikely(err))
++ goto out;
++
++ if (unlikely(bend == -1))
++ return 0; /* success */
++
++ hidden_inode = au_h_dptr(root)->d_inode;
++ if (unlikely(au_flag_test(sb, AuFlag_WARN_PERM)
++ && ((hidden_inode->i_mode & S_IALLUGO)
++ != (inode->i_mode & S_IALLUGO)
++ || hidden_inode->i_uid != inode->i_uid
++ || hidden_inode->i_gid != inode->i_gid)))
++ Warn("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),
++ hidden_inode->i_uid, hidden_inode->i_gid,
++ (hidden_inode->i_mode & S_IALLUGO));
++
++ err = -EINVAL;
++ for (bindex = 0; bindex <= bend; bindex++)
++ if (unlikely(is_overlap(sb, add->nd.dentry,
++ au_h_dptr_i(root, bindex)))) {
++ Err("%s is overlapped\n", add->path);
++ goto out;
++ }
++ err = 0;
++
++ out:
++ TraceErr(err);
++ return err;
++}
++
++int br_add(struct super_block *sb, struct opt_add *add, int remount)
++{
++ int err, sz;
++ aufs_bindex_t bend, add_bindex;
++ struct dentry *root;
++ struct aufs_iinfo *iinfo;
++ struct aufs_sbinfo *sbinfo;
++ struct aufs_dinfo *dinfo;
++ struct inode *root_inode;
++ unsigned long long maxb;
++ struct aufs_branch **branchp, *add_branch;
++ struct aufs_hdentry *hdentryp;
++ struct aufs_hinode *hinodep;
++
++ LKTRTrace("b%d, %s, 0x%x, %.*s\n", add->bindex, add->path,
++ add->perm, DLNPair(add->nd.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 (unlikely(err))
++ return 0; /* success */
++
++ bend = sbend(sb);
++ add_branch = alloc_addbr(sb, bend + 2);
++ err = PTR_ERR(add_branch);
++ if (IS_ERR(add_branch))
++ goto out;
++
++ err = 0;
++ rw_init_nolock(&add_branch->br_wh_rwsem);
++ add_branch->br_wh = add_branch->br_plink = NULL;
++ if (unlikely(br_writable(add->perm))) {
++ err = init_br_wh(sb, /*bindex*/-1, add_branch, add->perm,
++ add->nd.dentry, add->nd.mnt);
++ if (unlikely(err)) {
++ kfree(add_branch);
++ goto out;
++ }
++ }
++ add_branch->br_xino = NULL;
++ add_branch->br_mnt = mntget(add->nd.mnt);
++ atomic_set(&add_branch->br_wh_running, 0);
++ add_branch->br_id = new_br_id(sb);
++ add_branch->br_perm = add->perm;
++ atomic_set(&add_branch->br_count, 0);
++
++ sbinfo = stosi(sb);
++ dinfo = dtodi(root);
++ iinfo = itoii(root_inode);
++
++ add_bindex = add->bindex;
++ sz = sizeof(*(sbinfo->si_branch)) * (bend + 1 - add_bindex);
++ branchp = sbinfo->si_branch + add_bindex;
++ memmove(branchp + 1, branchp, sz);
++ *branchp = add_branch;
++ sz = sizeof(*hdentryp) * (bend + 1 - add_bindex);
++ hdentryp = dinfo->di_hdentry + add_bindex;
++ memmove(hdentryp + 1, hdentryp, sz);
++ hdentryp->hd_dentry = NULL;
++ sz = sizeof(*hinodep) * (bend + 1 - add_bindex);
++ hinodep = iinfo->ii_hinode + add_bindex;
++ memmove(hinodep + 1, hinodep, sz);
++ hinodep->hi_inode = NULL;
++ hinodep->hi_notify = NULL;
++
++ sbinfo->si_bend++;
++ dinfo->di_bend++;
++ iinfo->ii_bend++;
++ if (unlikely(bend == -1)) {
++ dinfo->di_bstart = 0;
++ iinfo->ii_bstart = 0;
++ }
++ set_h_dptr(root, add_bindex, dget(add->nd.dentry));
++ set_h_iptr(root_inode, add_bindex, igrab(add->nd.dentry->d_inode), 0);
++ if (!add_bindex)
++ au_cpup_attr_all(root_inode);
++ else
++ au_add_nlink(root_inode, add->nd.dentry->d_inode);
++ maxb = add->nd.dentry->d_sb->s_maxbytes;
++ if (sb->s_maxbytes < maxb)
++ sb->s_maxbytes = maxb;
++
++ if (au_flag_test(sb, AuFlag_XINO)) {
++ struct file *base_file = stobr(sb, 0)->br_xino;
++ if (!add_bindex)
++ base_file = stobr(sb, 1)->br_xino;
++ err = xino_init(sb, add_bindex, base_file, /*do_test*/1);
++ if (unlikely(err)) {
++ DEBUG_ON(add_branch->br_xino);
++ Err("ignored xino err %d, force noxino\n", err);
++ err = 0;
++ au_flag_clr(sb, AuFlag_XINO);
++ }
++ }
++
++ out:
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * test if the branch is deletable or not.
++ */
++static int test_children_busy(struct dentry *root, aufs_bindex_t bindex)
++{
++ int err, i, j, sigen;
++ struct au_dcsub_pages dpages;
++
++ LKTRTrace("b%d\n", bindex);
++ SiMustWriteLock(root->d_sb);
++ DiMustWriteLock(root);
++
++ err = au_dpages_init(&dpages, GFP_KERNEL);
++ if (unlikely(err))
++ goto out;
++ err = au_dcsub_pages(&dpages, root, NULL, NULL);
++ if (unlikely(err))
++ goto out_dpages;
++
++ sigen = au_sigen(root->d_sb);
++ DiMustNoWaiters(root);
++ IiMustNoWaiters(root->d_inode);
++ di_write_unlock(root);
++ for (i = 0; !err && i < dpages.ndpage; i++) {
++ struct au_dpage *dpage;
++ dpage = dpages.dpages + i;
++ for (j = 0; !err && j < dpage->ndentry; j++) {
++ struct dentry *d;
++
++ d = dpage->dentries[j];
++ if (au_digen(d) == sigen)
++ di_read_lock_child(d, AUFS_I_RLOCK);
++ else {
++ di_write_lock_child(d);
++ err = au_reval_dpath(d, sigen);
++ if (!err)
++ di_downgrade_lock(d, AUFS_I_RLOCK);
++ else {
++ di_write_unlock(d);
++ break;
++ }
++ }
++
++ if (au_h_dptr_i(d, bindex)
++ && (!S_ISDIR(d->d_inode->i_mode)
++ || dbstart(d) == dbend(d)))
++ err = -EBUSY;
++ di_read_unlock(d, AUFS_I_RLOCK);
++ if (err)
++ LKTRTrace("%.*s\n", DLNPair(d));
++ }
++ }
++ di_write_lock_child(root); /* aufs_write_lock() calls ..._child() */
++
++ out_dpages:
++ au_dpages_free(&dpages);
++ out:
++ TraceErr(err);
++ return err;
++}
++
++int br_del(struct super_block *sb, struct opt_del *del, int remount)
++{
++ int err, do_wh, rerr;
++ struct dentry *root;
++ struct inode *inode, *hidden_dir;
++ aufs_bindex_t bindex, bend, br_id;
++ struct aufs_sbinfo *sbinfo;
++ struct aufs_dinfo *dinfo;
++ struct aufs_iinfo *iinfo;
++ struct aufs_branch *br;
++
++ LKTRTrace("%s, %.*s\n", del->path, DLNPair(del->h_root));
++ SiMustWriteLock(sb);
++ root = sb->s_root;
++ DiMustWriteLock(root);
++ inode = root->d_inode;
++ IiMustWriteLock(inode);
++
++ bindex = au_find_dbindex(root, del->h_root);
++ if (unlikely(bindex < 0)) {
++ if (remount)
++ return 0; /* success */
++ err = -ENOENT;
++ Err("%s no such branch\n", del->path);
++ goto out;
++ }
++ LKTRTrace("bindex b%d\n", bindex);
++
++ err = -EBUSY;
++ bend = sbend(sb);
++ br = stobr(sb, bindex);
++ if (unlikely(!bend || br_count(br))) {
++ LKTRTrace("bend %d, br_count %d\n", bend, br_count(br));
++ goto out;
++ }
++
++ do_wh = 0;
++ hidden_dir = del->h_root->d_inode;
++ if (unlikely(br->br_wh || br->br_plink)) {
++#if 0
++ /* 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_wh = br->br_plink = 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;
++ sbinfo = stosi(sb);
++ dinfo = dtodi(root);
++ iinfo = itoii(inode);
++
++ dput(au_h_dptr_i(root, bindex));
++ aufs_hiput(iinfo->ii_hinode + bindex);
++ br_id = br->br_id;
++ free_branch(br);
++
++ //todo: realloc and shrink memeory
++ if (bindex < bend) {
++ const aufs_bindex_t n = bend - bindex;
++ struct aufs_branch **brp;
++ struct aufs_hdentry *hdp;
++ struct aufs_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;
++ iinfo->ii_hinode[0 + bend].hi_notify = NULL;
++
++ sbinfo->si_bend--;
++ dinfo->di_bend--;
++ iinfo->ii_bend--;
++ if (!bindex)
++ au_cpup_attr_all(inode);
++ else
++ au_sub_nlink(inode, del->h_root->d_inode);
++ if (au_flag_test(sb, AuFlag_PLINK))
++ half_refresh_plink(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 = 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)
++ Warn("failed re-creating base whiteout, %s. (%d)\n",
++ del->path, rerr);
++ out:
++ TraceErr(err);
++ return err;
++}
++
++static int do_need_sigen_inc(int a, int b)
++{
++ return (br_whable(a) && !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 br_mod(struct super_block *sb, struct opt_mod *mod, int remount,
++ int *do_update)
++{
++ int err;
++ struct dentry *root;
++ aufs_bindex_t bindex;
++ struct aufs_branch *br;
++ struct inode *hidden_dir;
++
++ LKTRTrace("%s, %.*s, 0x%x\n",
++ mod->path, DLNPair(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 (unlikely(bindex < 0)) {
++ if (remount)
++ return 0; /* success */
++ err = -ENOENT;
++ Err("%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 = stobr(sb, bindex);
++ if (unlikely(br->br_perm == mod->perm))
++ return 0; /* success */
++
++ if (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_wh = br->br_plink = NULL;
++#endif
++
++ if (!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 sbinfo is locked
++ //file_list_lock();
++ list_for_each_entry(file, &sb->s_files, f_u.fu_list) {
++ LKTRTrace("%.*s\n", DLNPair(file->f_dentry));
++ fi_read_lock(file);
++ if (!S_ISREG(file->f_dentry->d_inode->i_mode)
++ || !(file->f_mode & FMODE_WRITE)
++ || fbstart(file) != bindex) {
++ FiMustNoWaiters(file);
++ fi_read_unlock(file);
++ continue;
++ }
++
++ // todo: already flushed?
++ hf = au_h_fptr(file);
++ hf->f_flags = au_file_roflags(hf->f_flags);
++ hf->f_mode &= ~FMODE_WRITE;
++ FiMustNoWaiters(file);
++ fi_read_unlock(file);
++ }
++ //file_list_unlock();
++
++ /* aufs_write_lock() calls ..._child() */
++ di_write_lock_child(root);
++#endif
++ }
++ }
++
++ *do_update |= need_sigen_inc(br->br_perm, mod->perm);
++ br->br_perm = mod->perm;
++ return err; /* success */
++
++ out:
++ TraceErr(err);
++ return err;
++}
+diff --git a/fs/aufs/branch.h b/fs/aufs/branch.h
+new file mode 100755
+index 0000000..2557836
+--- /dev/null
++++ b/fs/aufs/branch.h
+@@ -0,0 +1,235 @@
++/*
++ * 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: branch.h,v 1.30 2007/05/14 03:41:51 sfjro Exp $ */
++
++#ifndef __AUFS_BRANCH_H__
++#define __AUFS_BRANCH_H__
++
++#ifdef __KERNEL__
++
++#include <linux/fs.h>
++#include <linux/mount.h>
++#include <linux/version.h>
++#include <linux/aufs_type.h>
++#include "misc.h"
++#include "super.h"
++
++/* protected by superblock rwsem */
++struct aufs_branch {
++ struct file *br_xino;
++ readf_t br_xino_read;
++ writef_t br_xino_write;
++
++ aufs_bindex_t br_id;
++
++ int br_perm;
++ struct vfsmount *br_mnt;
++ atomic_t br_count;
++
++ /* whiteout base */
++ struct aufs_rwsem br_wh_rwsem;
++ struct dentry *br_wh;
++ atomic_t br_wh_running;
++
++ /* pseudo-link dir */
++ struct dentry *br_plink;
++};
++
++/* ---------------------------------------------------------------------- */
++
++/* 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 br_writable(int brperm)
++{
++ return (brperm == AuBr_RW
++ || brperm == AuBr_RWNoLinkWH);
++}
++
++static inline int br_whable(int brperm)
++{
++ return (brperm == AuBr_RW
++ || brperm == AuBr_ROWH
++ || brperm == AuBr_RRWH);
++}
++
++static inline int br_linkable_wh(int brperm)
++{
++ return (brperm == AuBr_RW);
++}
++
++/* ---------------------------------------------------------------------- */
++
++#define _AuNoNfsBranchMsg "NFS branch is not supported"
++#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,15)
++#define AuNoNfsBranch
++#define AuNoNfsBranchMsg _AuNoNfsBranchMsg
++#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19) \
++ && !defined(CONFIG_AUFS_LHASH_PATCH)
++#define AuNoNfsBranch
++#define AuNoNfsBranchMsg _AuNoNfsBranchMsg \
++ ", try lhash.patch and CONFIG_AUFS_LHASH_PATCH"
++#endif
++
++/* ---------------------------------------------------------------------- */
++
++struct aufs_sbinfo;
++void free_branches(struct aufs_sbinfo *sinfo);
++int br_rdonly(struct aufs_branch *br);
++int find_brindex(struct super_block *sb, aufs_bindex_t br_id);
++int find_rw_br(struct super_block *sb, aufs_bindex_t bend);
++int find_rw_parent_br(struct dentry *dentry, aufs_bindex_t bend);
++struct opt_add;
++int br_add(struct super_block *sb, struct opt_add *add, int remount);
++struct opt_del;
++int br_del(struct super_block *sb, struct opt_del *del, int remount);
++struct opt_mod;
++int br_mod(struct super_block *sb, struct opt_mod *mod, int remount,
++ int *do_update);
++
++/* ---------------------------------------------------------------------- */
++
++static inline int br_count(struct aufs_branch *br)
++{
++ return atomic_read(&br->br_count);
++}
++
++static inline void br_get(struct aufs_branch *br)
++{
++ atomic_inc(&br->br_count);
++}
++
++static inline void br_put(struct aufs_branch *br)
++{
++ atomic_dec(&br->br_count);
++}
++
++/* ---------------------------------------------------------------------- */
++
++/* Superblock to branch */
++static inline aufs_bindex_t sbr_id(struct super_block *sb, aufs_bindex_t bindex)
++{
++ return stobr(sb, bindex)->br_id;
++}
++
++static inline
++struct vfsmount *sbr_mnt(struct super_block *sb, aufs_bindex_t bindex)
++{
++ return stobr(sb, bindex)->br_mnt;
++}
++
++static inline
++struct super_block *sbr_sb(struct super_block *sb, aufs_bindex_t bindex)
++{
++ return sbr_mnt(sb, bindex)->mnt_sb;
++}
++
++#if 0
++static inline int sbr_count(struct super_block *sb, aufs_bindex_t bindex)
++{
++ return br_count(stobr(sb, bindex));
++}
++
++static inline void sbr_get(struct super_block *sb, aufs_bindex_t bindex)
++{
++ br_get(stobr(sb, bindex));
++}
++#endif
++
++static inline void sbr_put(struct super_block *sb, aufs_bindex_t bindex)
++{
++ br_put(stobr(sb, bindex));
++}
++
++static inline int sbr_perm(struct super_block *sb, aufs_bindex_t bindex)
++{
++ return stobr(sb, bindex)->br_perm;
++}
++
++static inline int sbr_is_whable(struct super_block *sb, aufs_bindex_t bindex)
++{
++ return br_whable(sbr_perm(sb, bindex));
++}
++
++/* ---------------------------------------------------------------------- */
++
++#ifdef CONFIG_AUFS_LHASH_PATCH
++static inline struct vfsmount *au_do_nfsmnt(struct vfsmount *h_mnt)
++{
++ if (!au_is_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(sbr_mnt(sb, bindex));
++}
++#else
++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;
++}
++#endif /* CONFIG_AUFS_LHASH_PATCH */
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * br_wh_read_lock, br_wh_write_lock
++ * br_wh_read_unlock, br_wh_write_unlock, br_wh_downgrade_lock
++ */
++SimpleRwsemFuncs(br_wh, struct aufs_branch *br, br->br_wh_rwsem);
++
++/* to debug easier, do not make them inlined functions */
++#define BrWhMustReadLock(br) do { \
++ /* SiMustAnyLock(sb); */ \
++ RwMustReadLock(&(br)->br_wh_rwsem); \
++} while (0)
++
++#define BrWhMustWriteLock(br) do { \
++ /* SiMustAnyLock(sb); */ \
++ RwMustWriteLock(&(br)->br_wh_rwsem); \
++} while (0)
++
++#define BrWhMustAnyLock(br) do { \
++ /* SiMustAnyLock(sb); */ \
++ RwMustAnyLock(&(br)->br_wh_rwsem); \
++} while (0)
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_BRANCH_H__ */
+diff --git a/fs/aufs/cpup.c b/fs/aufs/cpup.c
+new file mode 100755
+index 0000000..6636f40
+--- /dev/null
++++ b/fs/aufs/cpup.c
+@@ -0,0 +1,773 @@
++/*
++ * 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: cpup.c,v 1.37 2007/05/14 03:41:52 sfjro Exp $ */
++
++#include <asm/uaccess.h>
++#include "aufs.h"
++
++/* violent cpup_attr_*() functions don't care inode lock */
++void au_cpup_attr_timesizes(struct inode *inode)
++{
++ struct inode *hidden_inode;
++
++ LKTRTrace("i%lu\n", inode->i_ino);
++ //IMustLock(inode);
++ hidden_inode = au_h_iptr(inode);
++ DEBUG_ON(!hidden_inode);
++ //IMustLock(!hidden_inode);
++
++ inode->i_atime = hidden_inode->i_atime;
++ inode->i_mtime = hidden_inode->i_mtime;
++ inode->i_ctime = hidden_inode->i_ctime;
++ spin_lock(&inode->i_lock);
++ i_size_write(inode, i_size_read(hidden_inode));
++ inode->i_blocks = hidden_inode->i_blocks;
++ 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);
++ DEBUG_ON(!inode->i_mode);
++
++ h_inode = au_h_iptr(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 = ibend(inode);
++ for (bindex = ibstart(inode) + 1; bindex <= bend; bindex++) {
++ h_inode = au_h_iptr_i(inode, bindex);
++ if (h_inode)
++ au_add_nlink(inode, h_inode);
++ }
++ }
++}
++
++void au_cpup_attr_changable(struct inode *inode)
++{
++ struct inode *hidden_inode;
++
++ LKTRTrace("i%lu\n", inode->i_ino);
++ //IMustLock(inode);
++ hidden_inode = au_h_iptr(inode);
++ DEBUG_ON(!hidden_inode);
++
++ inode->i_mode = hidden_inode->i_mode;
++ inode->i_uid = hidden_inode->i_uid;
++ inode->i_gid = hidden_inode->i_gid;
++ au_cpup_attr_timesizes(inode);
++
++ //??
++ inode->i_flags = hidden_inode->i_flags;
++}
++
++void au_cpup_igen(struct inode *inode, struct inode *h_inode)
++{
++ inode->i_generation = h_inode->i_generation;
++ itoii(inode)->ii_hsb1 = h_inode->i_sb;
++}
++
++void au_cpup_attr_all(struct inode *inode)
++{
++ struct inode *hidden_inode;
++
++ LKTRTrace("i%lu\n", inode->i_ino);
++ //IMustLock(inode);
++ hidden_inode = au_h_iptr(inode);
++ DEBUG_ON(!hidden_inode);
++
++ au_cpup_attr_changable(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 = hidden_inode->i_rdev;
++ }
++ inode->i_blkbits = hidden_inode->i_blkbits;
++ au_cpup_attr_blksize(inode, hidden_inode);
++ au_cpup_igen(inode, hidden_inode);
++}
++
++/* ---------------------------------------------------------------------- */
++
++/* Note: dt_dentry and dt_hidden_dentry are not dget/dput-ed */
++
++/* keep the timestamps of the parent dir when cpup */
++void dtime_store(struct dtime *dt, struct dentry *dentry,
++ struct dentry *hidden_dentry)
++{
++ struct inode *inode;
++
++ TraceEnter();
++ DEBUG_ON(!dentry || !hidden_dentry || !hidden_dentry->d_inode);
++
++ dt->dt_dentry = dentry;
++ dt->dt_h_dentry = hidden_dentry;
++ inode = hidden_dentry->d_inode;
++ dt->dt_atime = inode->i_atime;
++ dt->dt_mtime = inode->i_mtime;
++ //smp_mb();
++}
++
++// todo: remove extra parameter
++void dtime_revert(struct dtime *dt, int h_parent_is_locked)
++{
++ struct iattr attr;
++ int err;
++ struct dentry *dentry;
++
++ LKTRTrace("h_parent locked %d\n", h_parent_is_locked);
++
++ 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;
++ //smp_mb();
++ dentry = NULL;
++ if (!h_parent_is_locked /* && !IS_ROOT(dt->dt_dentry) */)
++ dentry = dt->dt_dentry;
++ err = vfsub_notify_change(dt->dt_h_dentry, &attr,
++ need_dlgt(dt->dt_dentry->d_sb));
++ if (unlikely(err))
++ Warn("restoring timestamps failed(%d). ignored\n", err);
++}
++
++/* ---------------------------------------------------------------------- */
++
++static int cpup_iattr(struct dentry *hidden_dst, struct dentry *hidden_src,
++ int dlgt)
++{
++ int err;
++ struct iattr ia;
++ struct inode *hidden_isrc, *hidden_idst;
++
++ LKTRTrace("%.*s\n", DLNPair(hidden_dst));
++ hidden_idst = hidden_dst->d_inode;
++ //IMustLock(hidden_idst);
++ hidden_isrc = hidden_src->d_inode;
++ //IMustLock(hidden_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 = hidden_isrc->i_mode;
++ ia.ia_uid = hidden_isrc->i_uid;
++ ia.ia_gid = hidden_isrc->i_gid;
++ ia.ia_atime = hidden_isrc->i_atime;
++ ia.ia_mtime = hidden_isrc->i_mtime;
++ err = vfsub_notify_change(hidden_dst, &ia, dlgt);
++ //if (LktrCond) err = -1;
++ if (!err)
++ hidden_idst->i_flags = hidden_isrc->i_flags; //??
++
++ TraceErr(err);
++ return err;
++}
++
++/*
++ * to support a sparse file which is opened with O_APPEND,
++ * we need to close the file.
++ */
++static int cpup_regular(struct dentry *dentry, aufs_bindex_t bdst,
++ aufs_bindex_t bsrc, loff_t len)
++{
++ int err, i, sparse;
++ struct super_block *sb;
++ struct inode *hidden_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",
++ DLNPair(dentry), bdst, bsrc, len);
++ DEBUG_ON(bsrc <= bdst);
++ DEBUG_ON(!len);
++ sb = dentry->d_sb;
++ DEBUG_ON(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_i(dentry, h->bindex);
++ DEBUG_ON(!h->dentry);
++ hidden_inode = h->dentry->d_inode;
++ DEBUG_ON(!hidden_inode || !S_ISREG(hidden_inode->i_mode));
++ h->file = hidden_open(dentry, h->bindex, h->flags);
++ //if (LktrCond)
++ //{fput(h->file); 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);
++ sparse = 0;
++ err = au_copy_file(hidden[DST].file, hidden[SRC].file, len, sb,
++ &sparse);
++
++ /* sparse file: update i_blocks next time */
++ if (unlikely(!err && sparse))
++ d_drop(dentry);
++
++ out_dst_file:
++ fput(hidden[DST].file);
++ sbr_put(sb, hidden[DST].bindex);
++ out_src_file:
++ fput(hidden[SRC].file);
++ sbr_put(sb, hidden[SRC].bindex);
++ out:
++ TraceErr(err);
++ return err;
++}
++
++// unnecessary?
++unsigned int au_flags_cpup(unsigned int init, struct dentry *parent)
++{
++ if (unlikely(parent && IS_ROOT(parent)))
++ init |= CPUP_LOCKED_GHDIR;
++ return init;
++}
++
++/* return with hidden dst inode is locked */
++static 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, symlen;
++ struct dentry *hidden_src, *hidden_dst, *hidden_parent, *parent;
++ struct inode *hidden_inode, *hidden_dir, *dir;
++ struct dtime dt;
++ umode_t mode;
++ char *sym;
++ mm_segment_t old_fs;
++ const int do_dt = flags & CPUP_DTIME;
++ struct super_block *sb;
++
++ LKTRTrace("%.*s, i%lu, bdst %d, bsrc %d, len %Ld, flags 0x%x\n",
++ DLNPair(dentry), dentry->d_inode->i_ino, bdst, bsrc, len,
++ flags);
++ sb = dentry->d_sb;
++ DEBUG_ON(bdst >= bsrc || test_ro(sb, bdst, NULL));
++ // bsrc branch can be ro/rw.
++
++ hidden_src = au_h_dptr_i(dentry, bsrc);
++ DEBUG_ON(!hidden_src);
++ hidden_inode = hidden_src->d_inode;
++ DEBUG_ON(!hidden_inode);
++
++ /* stop refrencing while we are creating */
++ //parent = dget_parent(dentry);
++ parent = dentry->d_parent;
++ dir = parent->d_inode;
++ hidden_dst = au_h_dptr_i(dentry, bdst);
++ DEBUG_ON(hidden_dst && hidden_dst->d_inode);
++ //hidden_parent = dget_parent(hidden_dst);
++ hidden_parent = hidden_dst->d_parent;
++ hidden_dir = hidden_parent->d_inode;
++ IMustLock(hidden_dir);
++
++ if (do_dt)
++ dtime_store(&dt, parent, hidden_parent);
++
++ isdir = 0;
++ mode = hidden_inode->i_mode;
++ switch (mode & S_IFMT) {
++ case S_IFREG:
++ /* stop updating while we are referencing */
++ IMustLock(hidden_inode);
++ err = vfsub_create(hidden_dir, hidden_dst, mode | S_IWUSR, NULL,
++ dlgt);
++ //if (LktrCond) {vfs_unlink(hidden_dir, hidden_dst); err = -1;}
++ if (!err) {
++ loff_t l = i_size_read(hidden_inode);
++ if (len == -1 || l < len)
++ len = l;
++ if (len) {
++ err = cpup_regular(dentry, bdst, bsrc, len);
++ //if (LktrCond) err = -1;
++ }
++ if (unlikely(err)) {
++ int rerr;
++ rerr = vfsub_unlink(hidden_dir, hidden_dst,
++ dlgt);
++ if (rerr) {
++ IOErr("failed unlinking cpup-ed %.*s"
++ "(%d, %d)\n",
++ DLNPair(hidden_dst), err, rerr);
++ err = -EIO;
++ }
++ }
++ }
++ break;
++ case S_IFDIR:
++ isdir = 1;
++ err = vfsub_mkdir(hidden_dir, hidden_dst, mode, dlgt);
++ //if (LktrCond) {vfs_rmdir(hidden_dir, hidden_dst); err = -1;}
++ if (!err) {
++ /* setattr case: dir is not locked */
++ if (0 && ibstart(dir) == bdst)
++ au_cpup_attr_nlink(dir);
++ au_cpup_attr_nlink(dentry->d_inode);
++ }
++ break;
++ case S_IFLNK:
++ err = -ENOMEM;
++ sym = __getname();
++ //if (LktrCond) {__putname(sym); sym = NULL;}
++ if (unlikely(!sym))
++ break;
++ old_fs = get_fs();
++ set_fs(KERNEL_DS);
++ err = symlen = hidden_inode->i_op->readlink
++ (hidden_src, (char __user*)sym, PATH_MAX);
++ //if (LktrCond) err = symlen = -1;
++ set_fs(old_fs);
++ if (symlen > 0) {
++ sym[symlen] = 0;
++ err = vfsub_symlink(hidden_dir, hidden_dst, sym, mode,
++ dlgt);
++ //if (LktrCond)
++ //{vfs_unlink(hidden_dir, hidden_dst); err = -1;}
++ }
++ __putname(sym);
++ break;
++ case S_IFCHR:
++ case S_IFBLK:
++ DEBUG_ON(!capable(CAP_MKNOD));
++ /*FALLTHROUGH*/
++ case S_IFIFO:
++ case S_IFSOCK:
++ err = vfsub_mknod(hidden_dir, hidden_dst, mode,
++ hidden_inode->i_rdev, dlgt);
++ //if (LktrCond) {vfs_unlink(hidden_dir, hidden_dst); err = -1;}
++ break;
++ default:
++ IOErr("Unknown inode type 0%o\n", mode);
++ err = -EIO;
++ }
++
++ if (do_dt)
++ dtime_revert(&dt, flags & CPUP_LOCKED_GHDIR);
++ //dput(parent);
++ //dput(hidden_parent);
++ TraceErr(err);
++ return err;
++}
++
++/*
++ * copyup the @dentry from @bsrc to @bdst.
++ * the caller must set the both of hidden dentries.
++ * @len is for trucating when it is -1 copyup the entire file.
++ */
++int cpup_single(struct dentry *dentry, aufs_bindex_t bdst, aufs_bindex_t bsrc,
++ loff_t len, unsigned int flags)
++{
++ int err, rerr, isdir, dlgt;
++ struct dentry *hidden_src, *hidden_dst, *parent;//, *h_parent;
++ struct inode *dst_inode, *hidden_dir, *inode, *src_inode;
++ struct super_block *sb;
++ aufs_bindex_t old_ibstart;
++ struct dtime dt;
++
++ LKTRTrace("%.*s, i%lu, bdst %d, bsrc %d, len %Ld, flags 0x%x\n",
++ DLNPair(dentry), dentry->d_inode->i_ino, bdst, bsrc, len,
++ flags);
++ sb = dentry->d_sb;
++ DEBUG_ON(bsrc <= bdst);
++ hidden_dst = au_h_dptr_i(dentry, bdst);
++ DEBUG_ON(!hidden_dst || hidden_dst->d_inode);
++ //h_parent = dget_parent(hidden_dst);
++ //hidden_dir = h_parent->d_inode;
++ hidden_dir = hidden_dst->d_parent->d_inode;
++ IMustLock(hidden_dir);
++ hidden_src = au_h_dptr_i(dentry, bsrc);
++ DEBUG_ON(!hidden_src || !hidden_src->d_inode);
++ inode = dentry->d_inode;
++ IiMustWriteLock(inode);
++
++ dlgt = need_dlgt(sb);
++ dst_inode = au_h_iptr_i(inode, bdst);
++ if (unlikely(dst_inode)) {
++ if (unlikely(!au_flag_test(sb, AuFlag_PLINK))) {
++ err = -EIO;
++ IOErr("i%lu exists on a upper branch "
++ "but plink is disabled\n", inode->i_ino);
++ goto out;
++ }
++
++ if (dst_inode->i_nlink) {
++ hidden_src = lkup_plink(sb, bdst, inode);
++ err = PTR_ERR(hidden_src);
++ if (IS_ERR(hidden_src))
++ goto out;
++ DEBUG_ON(!hidden_src->d_inode);
++ // vfs_link() does lock the inode
++ err = vfsub_link(hidden_src, hidden_dir, hidden_dst, dlgt);
++ dput(hidden_src);
++ goto out;
++ } else
++ /* udba work */
++ au_update_brange(inode, 1);
++ }
++
++ old_ibstart = ibstart(inode);
++ err = cpup_entry(dentry, bdst, bsrc, len, flags, dlgt);
++ if (unlikely(err))
++ goto out;
++ dst_inode = hidden_dst->d_inode;
++ hi_lock_child2(dst_inode);
++
++ //todo: test dlgt
++ err = cpup_iattr(hidden_dst, hidden_src, dlgt);
++ //if (LktrCond) err = -1;
++#if 0 // xattr
++ if (0 && !err)
++ err = cpup_xattrs(hidden_src, hidden_dst);
++#endif
++ isdir = S_ISDIR(dst_inode->i_mode);
++ if (!err) {
++ if (bdst < old_ibstart)
++ set_ibstart(inode, bdst);
++ set_h_iptr(inode, bdst, igrab(dst_inode),
++ au_hi_flags(inode, isdir));
++ i_unlock(dst_inode);
++ src_inode = hidden_src->d_inode;
++ if (!isdir) {
++ if (src_inode->i_nlink > 1
++ && au_flag_test(sb, AuFlag_PLINK))
++ append_plink(sb, inode, hidden_dst, bdst);
++ else {
++ /* braces are added to stop a warning */
++ ;//xino_write0(sb, bsrc, src_inode->i_ino);
++ /* ignore this error */
++ }
++ }
++ //goto out; /* success */
++ return 0; /* success */
++ }
++
++ /* revert */
++ i_unlock(dst_inode);
++ parent = dget_parent(dentry);
++ //dtime_store(&dt, parent, h_parent);
++ dtime_store(&dt, parent, hidden_dst->d_parent);
++ dput(parent);
++ if (!isdir)
++ rerr = vfsub_unlink(hidden_dir, hidden_dst, dlgt);
++ else
++ rerr = vfsub_rmdir(hidden_dir, hidden_dst, dlgt);
++ //rerr = -1;
++ dtime_revert(&dt, flags & CPUP_LOCKED_GHDIR);
++ if (rerr) {
++ IOErr("failed removing broken entry(%d, %d)\n", err, rerr);
++ err = -EIO;
++ }
++
++ out:
++ //dput(h_parent);
++ TraceErr(err);
++ return err;
++}
++
++struct cpup_single_args {
++ int *errp;
++ struct dentry *dentry;
++ aufs_bindex_t bdst, bsrc;
++ loff_t len;
++ unsigned int flags;
++};
++
++static void call_cpup_single(void *args)
++{
++ struct cpup_single_args *a = args;
++ *a->errp = cpup_single(a->dentry, a->bdst, a->bsrc, a->len, a->flags);
++}
++
++int sio_cpup_single(struct dentry *dentry, aufs_bindex_t bdst,
++ aufs_bindex_t bsrc, loff_t len, unsigned int flags)
++{
++ int err;
++ struct dentry *hidden_dentry;
++ umode_t mode;
++
++ LKTRTrace("%.*s, i%lu, bdst %d, bsrc %d, len %Ld, flags 0x%x\n",
++ DLNPair(dentry), dentry->d_inode->i_ino, bdst, bsrc, len,
++ flags);
++
++ hidden_dentry = au_h_dptr_i(dentry, bsrc);
++ mode = hidden_dentry->d_inode->i_mode & S_IFMT;
++ if ((mode != S_IFCHR && mode != S_IFBLK)
++ || capable(CAP_MKNOD))
++ err = cpup_single(dentry, bdst, bsrc, len, flags);
++ else {
++ struct cpup_single_args args = {
++ .errp = &err,
++ .dentry = dentry,
++ .bdst = bdst,
++ .bsrc = bsrc,
++ .len = len,
++ .flags = flags
++ };
++ au_wkq_wait(call_cpup_single, &args, /*dlgt*/0);
++ }
++
++ TraceErr(err);
++ return err;
++}
++
++/*
++ * copyup the @dentry from the first active hidden branch to @bdst,
++ * using cpup_single().
++ */
++int 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",
++ DLNPair(dentry), bdst, len, flags);
++ inode = dentry->d_inode;
++ DEBUG_ON(!S_ISDIR(inode->i_mode) && dbstart(dentry) < bdst);
++
++ bend = dbend(dentry);
++ for (bsrc = bdst + 1; bsrc <= bend; bsrc++)
++ if (au_h_dptr_i(dentry, bsrc))
++ break;
++ DEBUG_ON(!au_h_dptr_i(dentry, bsrc));
++
++ err = lkup_neg(dentry, bdst);
++ //err = -1;
++ if (!err) {
++ err = cpup_single(dentry, bdst, bsrc, len, flags);
++ if (!err)
++ return 0; /* success */
++
++ /* revert */
++ set_h_dptr(dentry, bdst, NULL);
++ set_dbstart(dentry, bsrc);
++ }
++
++ TraceErr(err);
++ return err;
++}
++
++struct cpup_simple_args {
++ int *errp;
++ struct dentry *dentry;
++ aufs_bindex_t bdst;
++ loff_t len;
++ unsigned int flags;
++};
++
++static void call_cpup_simple(void *args)
++{
++ struct cpup_simple_args *a = args;
++ *a->errp = cpup_simple(a->dentry, a->bdst, a->len, a->flags);
++}
++
++int sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
++ unsigned int flags)
++{
++ int err, do_sio, dlgt;
++ //struct dentry *parent;
++ struct inode *hidden_dir, *dir;
++
++ LKTRTrace("%.*s, b%d, len %Ld, flags 0x%x\n",
++ DLNPair(dentry), bdst, len, flags);
++
++ //parent = dget_parent(dentry);
++ //dir = parent->d_inode;
++ dir = dentry->d_parent->d_inode;
++ hidden_dir = au_h_iptr_i(dir, bdst);
++ dlgt = need_dlgt(dir->i_sb);
++ do_sio = au_test_perm(hidden_dir, MAY_EXEC | MAY_WRITE, dlgt);
++ if (!do_sio) {
++ umode_t mode = dentry->d_inode->i_mode & S_IFMT;
++ do_sio = ((mode == S_IFCHR || mode == S_IFBLK)
++ && !capable(CAP_MKNOD));
++ }
++ if (!do_sio)
++ err = cpup_simple(dentry, bdst, len, flags);
++ else {
++ struct cpup_simple_args args = {
++ .errp = &err,
++ .dentry = dentry,
++ .bdst = bdst,
++ .len = len,
++ .flags = flags
++ };
++ au_wkq_wait(call_cpup_simple, &args, /*dlgt*/0);
++ }
++
++ //dput(parent);
++ TraceErr(err);
++ return err;
++}
++
++//todo: dcsub
++/* cf. revalidate function in file.c */
++int cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst, struct dentry *locked)
++{
++ int err;
++ struct super_block *sb;
++ struct dentry *d, *parent, *hidden_parent;
++ unsigned int udba;
++
++ LKTRTrace("%.*s, b%d, parent i%lu, locked %p\n",
++ DLNPair(dentry), bdst, parent_ino(dentry), locked);
++ sb = dentry->d_sb;
++ DEBUG_ON(test_ro(sb, bdst, NULL));
++ parent = dentry->d_parent;
++ IiMustWriteLock(parent->d_inode);
++ if (unlikely(IS_ROOT(parent)))
++ return 0;
++ if (locked) {
++ DiMustAnyLock(locked);
++ IiMustAnyLock(locked->d_inode);
++ }
++
++ /* slow loop, keep it simple and stupid */
++ err = 0;
++ udba = au_flag_test(sb, AuFlag_UDBA_INOTIFY);
++ while (1) {
++ parent = dentry->d_parent; // dget_parent()
++ hidden_parent = au_h_dptr_i(parent, bdst);
++ if (hidden_parent)
++ return 0; /* success */
++
++ /* find top dir which is needed to cpup */
++ do {
++ d = parent;
++ parent = d->d_parent; // dget_parent()
++ if (parent != locked)
++ di_read_lock_parent3(parent, !AUFS_I_RLOCK);
++ hidden_parent = au_h_dptr_i(parent, bdst);
++ if (parent != locked)
++ di_read_unlock(parent, !AUFS_I_RLOCK);
++ } while (!hidden_parent);
++
++ if (d != dentry->d_parent)
++ di_write_lock_child3(d);
++
++ /* somebody else might create while we were sleeping */
++ if (!au_h_dptr_i(d, bdst) || !au_h_dptr_i(d, bdst)->d_inode) {
++ struct inode *h_dir = hidden_parent->d_inode,
++ *dir = parent->d_inode,
++ *h_gdir, *gdir;
++
++ if (au_h_dptr_i(d, bdst))
++ au_update_dbstart(d);
++ //DEBUG_ON(dbstart(d) <= bdst);
++ if (parent != locked)
++ di_read_lock_parent3(parent, AUFS_I_RLOCK);
++ h_gdir = gdir = NULL;
++ if (unlikely(udba && !IS_ROOT(parent))) {
++ gdir = parent->d_parent->d_inode;
++ h_gdir = hidden_parent->d_parent->d_inode;
++ hgdir_lock(h_gdir, gdir, bdst);
++ }
++ hdir_lock(h_dir, dir, bdst);
++ err = sio_cpup_simple(d, bdst, -1,
++ au_flags_cpup(CPUP_DTIME,
++ parent));
++ //if (LktrCond) err = -1;
++ hdir_unlock(h_dir, dir, bdst);
++ if (unlikely(gdir))
++ hdir_unlock(h_gdir, gdir, bdst);
++ if (parent != locked)
++ di_read_unlock(parent, AUFS_I_RLOCK);
++ }
++
++ if (d != dentry->d_parent)
++ di_write_unlock(d);
++ if (unlikely(err))
++ break;
++ }
++
++// out:
++ TraceErr(err);
++ return err;
++}
++
++int test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst,
++ struct dentry *locked)
++{
++ int err;
++ struct dentry *parent;
++ struct inode *dir;
++
++ parent = dentry->d_parent;
++ dir = parent->d_inode;
++ LKTRTrace("%.*s, b%d, parent i%lu, locked %p\n",
++ DLNPair(dentry), bdst, dir->i_ino, locked);
++ DiMustReadLock(parent);
++ IiMustReadLock(dir);
++
++ if (au_h_iptr_i(dir, bdst))
++ return 0;
++
++ err = 0;
++ di_read_unlock(parent, AUFS_I_RLOCK);
++ di_write_lock_parent(parent);
++ if (au_h_iptr_i(dir, bdst))
++ goto out;
++
++ err = cpup_dirs(dentry, bdst, locked);
++
++ out:
++ di_downgrade_lock(parent, AUFS_I_RLOCK);
++ TraceErr(err);
++ return err;
++}
+diff --git a/fs/aufs/cpup.h b/fs/aufs/cpup.h
+new file mode 100755
+index 0000000..86557aa
+--- /dev/null
++++ b/fs/aufs/cpup.h
+@@ -0,0 +1,72 @@
++/*
++ * 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: cpup.h,v 1.15 2007/05/14 03:41:52 sfjro Exp $ */
++
++#ifndef __AUFS_CPUP_H__
++#define __AUFS_CPUP_H__
++
++#ifdef __KERNEL__
++
++#include <linux/fs.h>
++#include <linux/version.h>
++#include <linux/aufs_type.h>
++
++static inline
++void au_cpup_attr_blksize(struct inode *inode, struct inode *h_inode)
++{
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
++ inode->i_blksize = h_inode->i_blksize;
++#endif
++}
++
++void au_cpup_attr_timesizes(struct inode *inode);
++void au_cpup_attr_nlink(struct inode *inode);
++void au_cpup_attr_changable(struct inode *inode);
++void au_cpup_igen(struct inode *inode, struct inode *h_inode);
++void au_cpup_attr_all(struct inode *inode);
++
++#define CPUP_DTIME 1 // do dtime_store/revert
++// todo: remove this
++#define CPUP_LOCKED_GHDIR 2 // grand parent hidden dir is locked
++unsigned int au_flags_cpup(unsigned int init, struct dentry *parent);
++
++int cpup_single(struct dentry *dentry, aufs_bindex_t bdst, aufs_bindex_t bsrc,
++ loff_t len, unsigned int flags);
++int sio_cpup_single(struct dentry *dentry, aufs_bindex_t bdst,
++ aufs_bindex_t bsrc, loff_t len, unsigned int flags);
++int cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
++ unsigned int flags);
++int sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
++ unsigned int flags);
++
++int cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst, struct dentry *locked);
++int test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst,
++ struct dentry *locked);
++
++/* keep timestamps when copyup */
++struct dtime {
++ struct dentry *dt_dentry, *dt_h_dentry;
++ struct timespec dt_atime, dt_mtime;
++};
++void dtime_store(struct dtime *dt, struct dentry *dentry,
++ struct dentry *h_dentry);
++void dtime_revert(struct dtime *dt, int h_parent_is_locked);
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_CPUP_H__ */
+diff --git a/fs/aufs/dcsub.c b/fs/aufs/dcsub.c
+new file mode 100755
+index 0000000..6ec29d3
+--- /dev/null
++++ b/fs/aufs/dcsub.c
+@@ -0,0 +1,175 @@
++/*
++ * 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: dcsub.c,v 1.3 2007/05/14 03:41:52 sfjro Exp $ */
++
++#include "aufs.h"
++
++static void au_dpage_free(struct au_dpage *dpage)
++{
++ int i;
++
++ TraceEnter();
++ DEBUG_ON(!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;
++
++ TraceEnter();
++
++ 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:
++ TraceErr(err);
++ return err;
++}
++
++void au_dpages_free(struct au_dcsub_pages *dpages)
++{
++ int i;
++
++ TraceEnter();
++
++ 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;
++
++ //TraceEnter();
++
++ dpage = dpages->dpages + dpages->ndpage - 1;
++ DEBUG_ON(!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:
++ //TraceErr(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;
++
++ TraceEnter();
++
++ 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_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_CHILD.next;
++ this_parent = this_parent->d_parent;
++ goto resume;
++ }
++ out:
++ spin_unlock(&dcache_lock);
++#if 0
++ if (!err) {
++ int i, j;
++ j = 0;
++ for (i = 0; i < dpages->ndpage; i++) {
++ if ((dpages->dpages + i)->ndentry)
++ Dbg("%d: %d\n", i, (dpages->dpages + i)->ndentry);
++ j += (dpages->dpages + i)->ndentry;
++ }
++ if (j)
++ Dbg("ndpage %d, %d\n", dpages->ndpage, j);
++ }
++#endif
++ TraceErr(err);
++ return err;
++}
+diff --git a/fs/aufs/dcsub.h b/fs/aufs/dcsub.h
+new file mode 100755
+index 0000000..0ba034b
+--- /dev/null
++++ b/fs/aufs/dcsub.h
+@@ -0,0 +1,47 @@
++/*
++ * 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: dcsub.h,v 1.2 2007/05/14 03:41:52 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);
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_DCSUB_H__ */
+diff --git a/fs/aufs/debug.c b/fs/aufs/debug.c
+new file mode 100755
+index 0000000..99d158b
+--- /dev/null
++++ b/fs/aufs/debug.c
+@@ -0,0 +1,262 @@
++/*
++ * 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: debug.c,v 1.27 2007/04/30 05:48:23 sfjro Exp $ */
++
++#include "aufs.h"
++
++atomic_t aufs_cond = ATOMIC_INIT(0);
++
++#if defined(CONFIG_LKTR) || defined(CONFIG_LKTR_MODULE)
++#define dpri(fmt, arg...) \
++ do {if (LktrCond) printk(KERN_DEBUG fmt, ##arg);} while (0)
++#else
++#define dpri(fmt, arg...) printk(KERN_DEBUG fmt, ##arg)
++#endif
++
++/* ---------------------------------------------------------------------- */
++
++void au_dpri_whlist(struct aufs_nhash *whlist)
++{
++ int i;
++ struct hlist_head *head;
++ struct aufs_wh *tpos;
++ struct hlist_node *pos;
++
++ for (i = 0; i < AUFS_NHASH_SIZE; 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 aufs_vdir *vdir)
++{
++ int i;
++ union aufs_deblk_p p;
++ unsigned char *o;
++
++ if (!vdir || IS_ERR(vdir)) {
++ dpri("err %ld\n", PTR_ERR(vdir));
++ return;
++ }
++
++ dpri("nblk %d, deblk %p %d, last{%d, %p}, ver %lu\n",
++ vdir->vd_nblk, vdir->vd_deblk, ksize(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 %d\n", i, o, ksize(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)
++{
++ 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));
++ dpri("i%d: i%lu, %s, cnt %d, nl %u, 0%o, sz %Lu, blk %Lu,"
++ " ct %Ld, np %lu, st 0x%lx, g %x\n",
++ bindex,
++ inode->i_ino, inode->i_sb ? au_sbtype(inode->i_sb) : "??",
++ atomic_read(&inode->i_count), inode->i_nlink, inode->i_mode,
++ 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_generation);
++ return 0;
++}
++
++void au_dpri_inode(struct inode *inode)
++{
++ struct aufs_iinfo *iinfo;
++ aufs_bindex_t bindex;
++ int err;
++
++ err = do_pri_inode(-1, inode);
++ if (err || !au_is_aufs(inode->i_sb))
++ return;
++
++ iinfo = itoii(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);
++}
++
++static int do_pri_dentry(aufs_bindex_t bindex, struct dentry *dentry)
++{
++ if (!dentry || IS_ERR(dentry)) {
++ dpri("d%d: err %ld\n", bindex, PTR_ERR(dentry));
++ return -1;
++ }
++ dpri("d%d: %.*s/%.*s, %s, cnt %d, flags 0x%x\n",
++ bindex,
++ DLNPair(dentry->d_parent), DLNPair(dentry),
++ dentry->d_sb ? au_sbtype(dentry->d_sb) : "??",
++ atomic_read(&dentry->d_count), dentry->d_flags);
++ do_pri_inode(bindex, dentry->d_inode);
++ return 0;
++}
++
++void au_dpri_dentry(struct dentry *dentry)
++{
++ struct aufs_dinfo *dinfo;
++ aufs_bindex_t bindex;
++ int err;
++
++ err = do_pri_dentry(-1, dentry);
++ if (err || !au_is_aufs(dentry->d_sb))
++ return;
++
++ dinfo = dtodi(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);
++}
++
++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 == -1 && ftofi(file))
++ snprintf(a, sizeof(a), ", mmapped %d", au_is_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);
++ do_pri_dentry(bindex, file->f_dentry);
++ return 0;
++}
++
++void au_dpri_file(struct file *file)
++{
++ struct aufs_finfo *finfo;
++ aufs_bindex_t bindex;
++ int err;
++
++ err = do_pri_file(-1, file);
++ if (err || !file->f_dentry || !au_is_aufs(file->f_dentry->d_sb))
++ return;
++
++ finfo = ftofi(file);
++ if (!finfo)
++ return;
++ if (finfo->fi_bstart < 0)
++ return;
++ for (bindex = finfo->fi_bstart; bindex <= finfo->fi_bend; bindex++) {
++ struct aufs_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 aufs_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 %p %p\n",
++ bindex, br->br_perm, br_count(br),
++ au_sbtype(sb), sb->s_flags, sb->s_count - S_BIAS,
++ atomic_read(&sb->s_active), br->br_xino,
++ br->br_xino ? br->br_xino->f_dentry : NULL);
++ return 0;
++}
++
++void au_dpri_sb(struct super_block *sb)
++{
++ struct aufs_sbinfo *sbinfo;
++ aufs_bindex_t bindex;
++ int err;
++ struct vfsmount mnt = {.mnt_sb = sb};
++ struct aufs_branch fake = {
++ .br_perm = 0,
++ .br_mnt = &mnt,
++ .br_count = ATOMIC_INIT(0),
++ .br_xino = NULL
++ };
++
++ atomic_set(&fake.br_count, 0);
++ err = do_pri_br(-1, &fake);
++ dpri("dev 0x%x\n", sb->s_dev);
++ if (err || !au_is_aufs(sb))
++ return;
++
++ sbinfo = stosi(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 DbgSleep(int sec)
++{
++ static DECLARE_WAIT_QUEUE_HEAD(wq);
++ Dbg("sleep %d sec\n", sec);
++ wait_event_timeout(wq, 0, sec * HZ);
++}
+diff --git a/fs/aufs/debug.h b/fs/aufs/debug.h
+new file mode 100755
+index 0000000..53f5f6a
+--- /dev/null
++++ b/fs/aufs/debug.h
+@@ -0,0 +1,129 @@
++/*
++ * 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: debug.h,v 1.31 2007/05/14 03:41:52 sfjro Exp $ */
++
++#ifndef __AUFS_DEBUG_H__
++#define __AUFS_DEBUG_H__
++
++#ifdef __KERNEL__
++
++#include <linux/fs.h>
++
++#ifdef CONFIG_AUFS_DEBUG
++#define DEBUG_ON(a) BUG_ON(a)
++extern atomic_t aufs_cond;
++#define au_debug_on() atomic_inc(&aufs_cond)
++#define au_debug_off() atomic_dec(&aufs_cond)
++#define au_is_debug() atomic_read(&aufs_cond)
++#else
++#define DEBUG_ON(a) /* */
++#define au_debug_on() /* */
++#define au_debug_off() /* */
++#define au_is_debug() 0
++#endif
++
++#define MtxMustLock(mtx) DEBUG_ON(!mutex_is_locked(mtx))
++
++/* ---------------------------------------------------------------------- */
++
++/* debug print */
++#if defined(CONFIG_LKTR) || defined(CONFIG_LKTR_MODULE)
++#include <linux/lktr.h>
++#ifdef CONFIG_AUFS_DEBUG
++#undef LktrCond
++#define LktrCond unlikely((lktr_cond && lktr_cond()) || au_is_debug())
++#endif
++#else
++#define LktrCond au_is_debug()
++#define LKTRDumpVma(pre, vma, suf) /* */
++#define LKTRDumpStack() /* */
++#define LKTRTrace(fmt, args...) do { \
++ if (LktrCond) \
++ Dbg(fmt, ##args); \
++} while (0)
++#define LKTRLabel(label) LKTRTrace("%s\n", #label)
++#endif /* CONFIG_LKTR */
++
++#define TraceErr(e) do { \
++ if (unlikely((e) < 0)) \
++ LKTRTrace("err %d\n", (int)(e)); \
++} while (0)
++#define TraceErrPtr(p) do { \
++ if (IS_ERR(p)) \
++ LKTRTrace("err %ld\n", PTR_ERR(p)); \
++} while (0)
++#define TraceEnter() LKTRLabel(enter)
++
++/* dirty macros for debug print, use with "%.*s" and caution */
++#define LNPair(qstr) (qstr)->len,(qstr)->name
++#define DLNPair(d) LNPair(&(d)->d_name)
++
++/* ---------------------------------------------------------------------- */
++
++#define Dpri(lvl, fmt, arg...) \
++ printk(lvl AUFS_NAME " %s:%d:%s[%d]: " fmt, \
++ __func__, __LINE__, current->comm, current->pid, ##arg)
++#define Dbg(fmt, arg...) Dpri(KERN_DEBUG, fmt, ##arg)
++#define Warn(fmt, arg...) Dpri(KERN_WARNING, fmt, ##arg)
++#define Warn1(fmt, arg...) do { \
++ static unsigned char c; \
++ if (!c++) Warn(fmt, ##arg); \
++ } while (0)
++#define Err(fmt, arg...) Dpri(KERN_ERR, fmt, ##arg)
++#define Err1(fmt, arg...) do { \
++ static unsigned char c; \
++ if (!c++) Err(fmt, ##arg); \
++ } while (0)
++#define IOErr(fmt, arg...) Err("I/O Error, " fmt, ##arg)
++#define IOErr1(fmt, arg...) do { \
++ static unsigned char c; \
++ if (!c++) IOErr(fmt, ##arg); \
++ } while (0)
++#define IOErrWhck(fmt, arg...) Err("I/O Error, try whck. " fmt, ##arg)
++
++/* ---------------------------------------------------------------------- */
++
++#ifdef CONFIG_AUFS_DEBUG
++struct aufs_nhash;
++void au_dpri_whlist(struct aufs_nhash *whlist);
++struct aufs_vdir;
++void au_dpri_vdir(struct aufs_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);
++#define DbgWhlist(w) do{LKTRTrace(#w "\n"); au_dpri_whlist(w);}while(0)
++#define DbgVdir(v) do{LKTRTrace(#v "\n"); au_dpri_vdir(v);}while(0)
++#define DbgInode(i) do{LKTRTrace(#i "\n"); au_dpri_inode(i);}while(0)
++#define DbgDentry(d) do{LKTRTrace(#d "\n"); au_dpri_dentry(d);}while(0)
++#define DbgFile(f) do{LKTRTrace(#f "\n"); au_dpri_file(f);}while(0)
++#define DbgSb(sb) do{LKTRTrace(#sb "\n"); au_dpri_sb(sb);}while(0)
++void DbgSleep(int sec);
++#else
++#define DbgWhlist(w) /* */
++#define DbgVdir(v) /* */
++#define DbgInode(i) /* */
++#define DbgDentry(d) /* */
++#define DbgFile(f) /* */
++#define DbgSb(sb) /* */
++#define DbgSleep(sec) /* */
++#endif
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_DEBUG_H__ */
+diff --git a/fs/aufs/dentry.c b/fs/aufs/dentry.c
+new file mode 100755
+index 0000000..2acb89b
+--- /dev/null
++++ b/fs/aufs/dentry.c
+@@ -0,0 +1,946 @@
++/*
++ * 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: dentry.c,v 1.41 2007/05/14 03:38:38 sfjro Exp $ */
++
++//#include <linux/fs.h>
++//#include <linux/namei.h>
++#include "aufs.h"
++
++#ifdef CONFIG_AUFS_LHASH_PATCH
++
++#ifdef CONFIG_AUFS_DLGT
++struct lookup_hash_args {
++ struct dentry **errp;
++ struct qstr *name;
++ struct dentry *base;
++ struct nameidata *nd;
++};
++
++static void call_lookup_hash(void *args)
++{
++ struct lookup_hash_args *a = args;
++ *a->errp = __lookup_hash(a->name, a->base, a->nd);
++}
++#endif /* CONFIG_AUFS_DLGT */
++
++static struct dentry *lkup_hash(const char *name, struct dentry *parent,
++ int len, struct lkup_args *lkup)
++{
++ struct dentry *dentry;
++ char *p;
++ unsigned long hash;
++ struct qstr this;
++ unsigned int c;
++ struct nameidata tmp_nd;
++
++ 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);
++
++ memset(&tmp_nd, 0, sizeof(tmp_nd));
++ tmp_nd.dentry = dget(parent);
++ tmp_nd.mnt = mntget(lkup->nfsmnt);
++#ifndef CONFIG_AUFS_DLGT
++ dentry = __lookup_hash(&this, parent, &tmp_nd);
++#else
++ if (!lkup->dlgt)
++ dentry = __lookup_hash(&this, parent, &tmp_nd);
++ else {
++ struct lookup_hash_args args = {
++ .errp = &dentry,
++ .name = &this,
++ .base = parent,
++ .nd = &tmp_nd
++ };
++ au_wkq_wait(call_lookup_hash, &args, /*dlgt*/1);
++ }
++#endif
++ path_release(&tmp_nd);
++
++ out:
++ TraceErrPtr(dentry);
++ return dentry;
++}
++#elif defined(CONFIG_AUFS_DLGT)
++static struct dentry *lkup_hash(const char *name, struct dentry *parent,
++ int len, struct lkup_args *lkup)
++{
++ return ERR_PTR(-ENOSYS);
++}
++#endif
++
++#ifdef CONFIG_AUFS_DLGT
++struct lookup_one_len_args {
++ struct dentry **errp;
++ const char *name;
++ struct dentry *parent;
++ int len;
++};
++
++static void call_lookup_one_len(void *args)
++{
++ struct lookup_one_len_args *a = args;
++ *a->errp = lookup_one_len(a->name, a->parent, a->len);
++}
++#endif /* CONFIG_AUFS_DLGT */
++
++#if defined(CONFIG_AUFS_LHASH_PATCH) || defined(CONFIG_AUFS_DLGT)
++/* cf. lookup_one_len() in linux/fs/namei.c */
++struct dentry *lkup_one(const char *name, struct dentry *parent, int len,
++ struct lkup_args *lkup)
++{
++ struct dentry *dentry;
++
++ LKTRTrace("%.*s/%.*s, lkup{%p, %d}\n",
++ DLNPair(parent), len, name, lkup->nfsmnt, lkup->dlgt);
++
++ if (!lkup->nfsmnt) {
++#ifndef CONFIG_AUFS_DLGT
++ dentry = lookup_one_len(name, parent, len);
++#else
++ if (!lkup->dlgt)
++ dentry = lookup_one_len(name, parent, len);
++ else {
++ struct lookup_one_len_args args = {
++ .errp = &dentry,
++ .name = name,
++ .parent = parent,
++ .len = len
++ };
++ au_wkq_wait(call_lookup_one_len, &args, /*dlgt*/1);
++ }
++#endif
++ } else
++ dentry = lkup_hash(name, parent, len, lkup);
++
++ TraceErrPtr(dentry);
++ return dentry;
++}
++#endif
++
++struct lkup_one_args {
++ struct dentry **errp;
++ const char *name;
++ struct dentry *parent;
++ int len;
++ struct lkup_args *lkup;
++};
++
++static void call_lkup_one(void *args)
++{
++ struct lkup_one_args *a = args;
++ *a->errp = lkup_one(a->name, a->parent, a->len, a->lkup);
++}
++
++/*
++ * returns positive/negative dentry, NULL or an error.
++ * NULL means whiteout-ed or not-found.
++ */
++static struct dentry *do_lookup(struct dentry *hidden_parent,
++ struct dentry *dentry, aufs_bindex_t bindex,
++ struct qstr *wh_name, int allow_neg,
++ mode_t type, int dlgt)
++{
++ struct dentry *hidden_dentry;
++ int wh_found, wh_able, opq;
++ struct inode *hidden_dir, *hidden_inode;
++ struct qstr *name;
++ struct super_block *sb;
++ struct lkup_args lkup = {.dlgt = dlgt};
++
++ LKTRTrace("%.*s/%.*s, b%d, allow_neg %d, type 0%o, dlgt %d\n",
++ DLNPair(hidden_parent), DLNPair(dentry), bindex, allow_neg,
++ type, dlgt);
++ DEBUG_ON(IS_ROOT(dentry));
++ hidden_dir = hidden_parent->d_inode;
++ IMustLock(hidden_dir);
++
++ wh_found = 0;
++ sb = dentry->d_sb;
++ wh_able = sbr_is_whable(sb, bindex);
++ lkup.nfsmnt = au_nfsmnt(sb, bindex);
++ name = &dentry->d_name;
++ if (unlikely(wh_able)) {
++#if 0 //def CONFIG_AUFS_ROBR
++ if (strncmp(name->name, AUFS_WH_PFX, AUFS_WH_PFX_LEN))
++ wh_found = is_wh(hidden_parent, wh_name, /*try_sio*/0,
++ &lkup);
++ else
++ wh_found = -EPERM;
++#else
++ wh_found = is_wh(hidden_parent, wh_name, /*try_sio*/0, &lkup);
++#endif
++ }
++ //if (LktrCond) wh_found = -1;
++ hidden_dentry = ERR_PTR(wh_found);
++ if (!wh_found)
++ goto real_lookup;
++ if (unlikely(wh_found < 0))
++ goto out;
++
++ /* We found a whiteout */
++ //set_dbend(dentry, bindex);
++ set_dbwh(dentry, bindex);
++ if (!allow_neg)
++ return NULL; /* success */
++
++ real_lookup:
++ // do not superio.
++ hidden_dentry = lkup_one(name->name, hidden_parent, name->len, &lkup);
++ //if (LktrCond) {dput(hidden_dentry); hidden_dentry = ERR_PTR(-1);}
++ if (IS_ERR(hidden_dentry))
++ goto out;
++ DEBUG_ON(d_unhashed(hidden_dentry));
++ hidden_inode = hidden_dentry->d_inode;
++ if (!hidden_inode) {
++ if (!allow_neg)
++ goto out_neg;
++ } else if (wh_found
++ || (type && type != (hidden_inode->i_mode & S_IFMT)))
++ goto out_neg;
++
++ if (dbend(dentry) <= bindex)
++ set_dbend(dentry, bindex);
++ if (dbstart(dentry) == -1 || bindex < dbstart(dentry))
++ set_dbstart(dentry, bindex);
++ set_h_dptr(dentry, bindex, hidden_dentry);
++
++ if (!hidden_inode || !S_ISDIR(hidden_inode->i_mode) || !wh_able)
++ return hidden_dentry; /* success */
++
++ hi_lock_child(hidden_inode);
++ opq = is_diropq(hidden_dentry, &lkup);
++ //if (LktrCond) opq = -1;
++ i_unlock(hidden_inode);
++ if (opq > 0)
++ set_dbdiropq(dentry, bindex);
++ else if (unlikely(opq < 0)) {
++ set_h_dptr(dentry, bindex, NULL);
++ hidden_dentry = ERR_PTR(opq);
++ }
++ goto out;
++
++ out_neg:
++ dput(hidden_dentry);
++ hidden_dentry = NULL;
++ out:
++ TraceErrPtr(hidden_dentry);
++ return hidden_dentry;
++}
++
++/*
++ * returns the number of hidden positive dentries,
++ * otherwise an error.
++ */
++int lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type)
++{
++ int npositive, err, allow_neg, dlgt;
++ struct dentry *parent;
++ aufs_bindex_t bindex, btail;
++ const struct qstr *name = &dentry->d_name;
++ struct qstr whname;
++ struct super_block *sb;
++
++ LKTRTrace("%.*s, b%d, type 0%o\n", LNPair(name), bstart, type);
++ DEBUG_ON(bstart < 0 || IS_ROOT(dentry));
++ parent = dget_parent(dentry);
++
++#if 1 //ndef CONFIG_AUFS_ROBR
++ err = -EPERM;
++ if (unlikely(!strncmp(name->name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)))
++ goto out;
++#endif
++
++ err = au_alloc_whname(name->name, name->len, &whname);
++ //if (LktrCond) {au_free_whname(&whname); err = -1;}
++ if (unlikely(err))
++ goto out;
++
++ sb = dentry->d_sb;
++ dlgt = need_dlgt(sb);
++ allow_neg = !type;
++ npositive = 0;
++ btail = dbtaildir(parent);
++ for (bindex = bstart; bindex <= btail; bindex++) {
++ struct dentry *hidden_parent, *hidden_dentry;
++ struct inode *hidden_inode;
++ struct inode *hidden_dir;
++
++ hidden_dentry = au_h_dptr_i(dentry, bindex);
++ if (hidden_dentry) {
++ if (hidden_dentry->d_inode)
++ npositive++;
++ if (type != S_IFDIR)
++ break;
++ continue;
++ }
++ hidden_parent = au_h_dptr_i(parent, bindex);
++ if (!hidden_parent)
++ continue;
++ hidden_dir = hidden_parent->d_inode;
++ if (!hidden_dir || !S_ISDIR(hidden_dir->i_mode))
++ continue;
++
++ hi_lock_parent(hidden_dir);
++ hidden_dentry = do_lookup(hidden_parent, dentry, bindex,
++ &whname, allow_neg, type, dlgt);
++ // do not dput for testing
++ //if (LktrCond) {hidden_dentry = ERR_PTR(-1);}
++ i_unlock(hidden_dir);
++ err = PTR_ERR(hidden_dentry);
++ if (IS_ERR(hidden_dentry))
++ goto out_wh;
++ allow_neg = 0;
++
++ if (dbwh(dentry) != -1)
++ break;
++ if (!hidden_dentry)
++ continue;
++ hidden_inode = hidden_dentry->d_inode;
++ if (!hidden_inode)
++ continue;
++ npositive++;
++ if (!type)
++ type = hidden_inode->i_mode & S_IFMT;
++ if (type != S_IFDIR)
++ break;
++ else if (dbdiropq(dentry) != -1)
++ break;
++ }
++
++ if (npositive) {
++ LKTRLabel(positive);
++ au_update_dbstart(dentry);
++ }
++ err = npositive;
++
++ out_wh:
++ au_free_whname(&whname);
++ out:
++ dput(parent);
++ TraceErr(err);
++ return err;
++}
++
++struct dentry *sio_lkup_one(const char *name, struct dentry *parent, int len,
++ struct lkup_args *lkup)
++{
++ struct dentry *dentry;
++
++ LKTRTrace("%.*s/%.*s\n", DLNPair(parent), len, name);
++ IMustLock(parent->d_inode);
++
++ if (!au_test_perm(parent->d_inode, MAY_EXEC, lkup->dlgt))
++ dentry = lkup_one(name, parent, len, lkup);
++ else {
++ // ugly
++ int dlgt = lkup->dlgt;
++ struct lkup_one_args args = {
++ .errp = &dentry,
++ .name = name,
++ .parent = parent,
++ .len = len,
++ .lkup = lkup
++ };
++
++ lkup->dlgt = 0;
++ au_wkq_wait(call_lkup_one, &args, /*dlgt*/0);
++ lkup->dlgt = dlgt;
++ }
++
++ TraceErrPtr(dentry);
++ return dentry;
++}
++
++/*
++ * lookup @dentry on @bindex which should be negative.
++ */
++int lkup_neg(struct dentry *dentry, aufs_bindex_t bindex)
++{
++ int err;
++ struct dentry *parent, *hidden_parent, *hidden_dentry;
++ struct inode *hidden_dir;
++ struct lkup_args lkup;
++
++ LKTRTrace("%.*s, b%d\n", DLNPair(dentry), bindex);
++ parent = dget_parent(dentry);
++ DEBUG_ON(!parent || !parent->d_inode
++ || !S_ISDIR(parent->d_inode->i_mode));
++ hidden_parent = au_h_dptr_i(parent, bindex);
++ DEBUG_ON(!hidden_parent);
++ hidden_dir = hidden_parent->d_inode;
++ DEBUG_ON(!hidden_dir || !S_ISDIR(hidden_dir->i_mode));
++ IMustLock(hidden_dir);
++
++ lkup.nfsmnt = au_nfsmnt(dentry->d_sb, bindex);
++ lkup.dlgt = need_dlgt(dentry->d_sb);
++ hidden_dentry = sio_lkup_one(dentry->d_name.name, hidden_parent,
++ dentry->d_name.len, &lkup);
++ //if (LktrCond) {dput(hidden_dentry); hidden_dentry = ERR_PTR(-1);}
++ err = PTR_ERR(hidden_dentry);
++ if (IS_ERR(hidden_dentry))
++ goto out;
++ if (unlikely(hidden_dentry->d_inode)) {
++ err = -EIO;
++ IOErr("b%d %.*s should be negative.%s\n",
++ bindex, DLNPair(hidden_dentry),
++ au_flag_test(dentry->d_sb, AuFlag_UDBA_INOTIFY) ? "" :
++ " Try udba=inotify.");
++ dput(hidden_dentry);
++ goto out;
++ }
++
++ if (bindex < dbstart(dentry))
++ set_dbstart(dentry, bindex);
++ if (dbend(dentry) < bindex)
++ set_dbend(dentry, bindex);
++ set_h_dptr(dentry, bindex, hidden_dentry);
++ err = 0;
++
++ out:
++ dput(parent);
++ TraceErr(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, pgen, new_sz, sgen, dgen;
++ struct aufs_dinfo *dinfo;
++ struct super_block *sb;
++ struct dentry *parent;
++ aufs_bindex_t bindex, parent_bend, parent_bstart, bwh, bdiropq, bend;
++ struct aufs_hdentry *p;
++ //struct nameidata nd;
++
++ LKTRTrace("%.*s, type 0%o\n", DLNPair(dentry), type);
++ DiMustWriteLock(dentry);
++ sb = dentry->d_sb;
++ DEBUG_ON(IS_ROOT(dentry));
++ parent = dget_parent(dentry);
++ pgen = au_digen(parent);
++ sgen = au_sigen(sb);
++ dgen = au_digen(dentry);
++ DEBUG_ON(pgen != sgen);
++
++ npositive = -ENOMEM;
++ new_sz = sizeof(*dinfo->di_hdentry) * (sbend(sb) + 1);
++ dinfo = dtodi(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 aufs_hdentry tmp, *q;
++ aufs_bindex_t new_bindex;
++
++ hd = p->hd_dentry;
++ if (!hd)
++ continue;
++ hdp = dget_parent(hd);
++ if (hdp == au_h_dptr_i(parent, bindex)) {
++ dput(hdp);
++ continue;
++ }
++
++ new_bindex = au_find_dbindex(parent, hdp);
++ dput(hdp);
++ DEBUG_ON(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
++ hdput(p);
++ 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 != -1 && bwh <= sbend(sb) && sbr_is_whable(sb, bwh)))
++ dinfo->di_bwh = bwh;
++ dinfo->di_bdiropq = -1;
++ if (unlikely(bdiropq != -1 && bdiropq <= sbend(sb)
++ && sbr_is_whable(sb, bdiropq)))
++ dinfo->di_bdiropq = bdiropq;
++ parent_bend = 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 = dbstart(parent);
++ if (type != S_IFDIR && dinfo->di_bstart == parent_bstart)
++ goto out_dgen; /* success */
++
++#if 0
++ nd.last_type = LAST_ROOT;
++ nd.flags = LOOKUP_FOLLOW;
++ nd.depth = 0;
++ nd.mnt = mntget(??);
++ nd.dentry = dget(parent);
++#endif
++ npositive = lkup_dentry(dentry, parent_bstart, type);
++ //if (LktrCond) npositive = -1;
++ if (npositive < 0)
++ goto out;
++
++ out_dgen:
++ au_update_digen(dentry);
++ out:
++ dput(parent);
++ TraceErr(npositive);
++ return npositive;
++}
++
++static int h_d_revalidate(struct dentry *dentry, struct nameidata *nd,
++ int do_udba)
++{
++ int err, plus, locked, unhashed, is_root, h_plus, is_nfs;
++ struct nameidata fake_nd, *p;
++ aufs_bindex_t bindex, btail, bstart, ibs, ibe;
++ struct super_block *sb;
++ struct inode *inode, *first, *h_inode, *h_cached_inode;
++ umode_t mode, h_mode;
++ struct dentry *h_dentry;
++ int (*reval)(struct dentry *, struct nameidata *);
++ struct qstr *name;
++
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++ inode = dentry->d_inode;
++ DEBUG_ON(inode && au_digen(dentry) != au_iigen(inode));
++ //DbgDentry(dentry);
++ //DbgInode(inode);
++
++ err = 0;
++ sb = dentry->d_sb;
++ plus = 0;
++ mode = 0;
++ first = NULL;
++ ibs = 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);
++ ibs = ibstart(inode);
++ ibe = ibend(inode);
++ }
++
++ btail = bstart = dbstart(dentry);
++ if (inode && S_ISDIR(inode->i_mode))
++ btail = dbtaildir(dentry);
++ locked = 0;
++ if (nd) {
++ fake_nd = *nd;
++#ifndef CONFIG_AUFS_FAKE_DM
++ if (dentry != nd->dentry) {
++ di_read_lock_parent(nd->dentry, 0);
++ locked = 1;
++ }
++#endif
++ }
++ for (bindex = bstart; bindex <= btail; bindex++) {
++ h_dentry = au_h_dptr_i(dentry, bindex);
++ if (unlikely(!h_dentry))
++ continue;
++ 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),
++ DLNPair(dentry), DLNPair(h_dentry));
++ goto err;
++ }
++
++ reval = NULL;
++ if (h_dentry->d_op)
++ reval = h_dentry->d_op->d_revalidate;
++ if (unlikely(reval)) {
++ //LKTRLabel(hidden reval);
++ p = fake_dm(&fake_nd, nd, sb, bindex);
++ DEBUG_ON(IS_ERR(p));
++ err = !reval(h_dentry, p);
++ fake_dm_release(p);
++ if (unlikely(err)) {
++ //Dbg("here\n");
++ goto err;
++ }
++ }
++
++ if (unlikely(!do_udba))
++ continue;
++
++ /* UDBA tests */
++ h_inode = h_dentry->d_inode;
++ if (unlikely(!!inode != !!h_inode)) {
++ //Dbg("here\n");
++ goto err;
++ }
++
++ h_plus = plus;
++ h_mode = mode;
++ h_cached_inode = h_inode;
++ is_nfs = 0;
++ if (h_inode) {
++ h_mode = (h_inode->i_mode & S_IFMT);
++ h_plus = (h_inode->i_nlink > 0);
++ }
++ if (inode && ibs <= bindex && bindex <= ibe) {
++ h_cached_inode = au_h_iptr_i(inode, bindex);
++ //is_nfs = au_is_nfs(h_cached_inode->i_sb);
++ }
++
++ LKTRTrace("{%d, 0%o, %p}, h{%d, 0%o, %p}\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 /* && !is_nfs */))) {
++ //Dbg("here\n");
++ goto err;
++ }
++ continue;
++
++ err:
++ err = -EINVAL;
++ break;
++ }
++#ifndef CONFIG_AUFS_FAKE_DM
++ if (unlikely(locked))
++ di_read_unlock(nd->dentry, 0);
++#endif
++
++#if 0
++ // some filesystem uses CURRENT_TIME_SEC instead of CURRENT_TIME.
++ // NFS may stop IN_DELETE because of DCACHE_NFSFS_RENAMED.
++#if 0
++ && (!timespec_equal(&inode->i_ctime, &first->i_ctime)
++ || !timespec_equal(&inode->i_atime, &first->i_atime))
++#endif
++ if (unlikely(!err && udba && first))
++ au_cpup_attr_all(inode);
++#endif
++
++ TraceErr(err);
++ return err;
++}
++
++static int simple_reval_dpath(struct dentry *dentry, int sgen)
++{
++ int err;
++ mode_t type;
++ struct dentry *parent;
++ struct inode *inode;
++
++ LKTRTrace("%.*s, sgen %d\n", DLNPair(dentry), sgen);
++ SiMustAnyLock(dentry->d_sb);
++ DiMustWriteLock(dentry);
++ inode = dentry->d_inode;
++ DEBUG_ON(!inode);
++
++ if (au_digen(dentry) == sgen)
++ return 0;
++
++ parent = dget_parent(dentry);
++ di_read_lock_parent(parent, AUFS_I_RLOCK);
++ DEBUG_ON(au_digen(parent) != sgen);
++#ifdef CONFIG_AUFS_DEBUG
++ {
++ struct dentry *d = parent;
++ while (!IS_ROOT(d)) {
++ DEBUG_ON(au_digen(d) != sgen);
++ d = d->d_parent;
++ }
++ }
++#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, AUFS_I_RLOCK);
++ dput(parent);
++ TraceErr(err);
++ return err;
++}
++
++int au_reval_dpath(struct dentry *dentry, int sgen)
++{
++ int err;
++ struct dentry *d, *parent;
++ struct inode *inode;
++
++ LKTRTrace("%.*s, sgen %d\n", DLNPair(dentry), sgen);
++ DEBUG_ON(!dentry->d_inode);
++ DiMustWriteLock(dentry);
++
++ if (!stosi(dentry->d_sb)->si_failed_refresh_dirs)
++ return simple_reval_dpath(dentry, sgen);
++
++ /* slow loop, keep it simple and stupid */
++ /* cf: cpup_dirs() */
++ err = 0;
++ while (au_digen(dentry) != sgen) {
++ d = dentry;
++ while (1) {
++ parent = d->d_parent; // dget_parent()
++ if (au_digen(parent) == sgen)
++ break;
++ d = parent;
++ }
++
++ inode = d->d_inode;
++ if (d != dentry) {
++ //i_lock(inode);
++ di_write_lock_child(d);
++ }
++
++ /* someone might update our dentry while we were sleeping */
++ if (au_digen(d) != sgen) {
++ di_read_lock_parent(parent, AUFS_I_RLOCK);
++ /* 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, AUFS_I_RLOCK);
++ }
++
++ if (d != dentry) {
++ di_write_unlock(d);
++ //i_unlock(inode);
++ }
++ if (unlikely(err))
++ break;
++ }
++
++ TraceErr(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, sgen, err, do_udba;
++ struct super_block *sb;
++ struct inode *inode;
++
++ LKTRTrace("dentry %.*s\n", DLNPair(dentry));
++ if (nd && nd->dentry)
++ LKTRTrace("nd %.*s\n", DLNPair(nd->dentry));
++ //dir case: DEBUG_ON(dentry->d_parent != nd->dentry);
++ //remove failure case: DEBUG_ON(!IS_ROOT(dentry) && d_unhashed(dentry));
++ DEBUG_ON(!dentry->d_fsdata);
++ //DbgDentry(dentry);
++
++ err = -EINVAL;
++ inode = dentry->d_inode;
++ //DbgInode(inode);
++ sb = dentry->d_sb;
++ si_read_lock(sb);
++ sgen = au_sigen(sb);
++ if (au_digen(dentry) == sgen)
++ di_read_lock_child(dentry, !AUFS_I_RLOCK);
++ else {
++ DEBUG_ON(IS_ROOT(dentry));
++#ifdef ForceInotify
++ Dbg("UDBA or digen, %.*s\n", DLNPair(dentry));
++#endif
++ //i_lock(inode);
++ di_write_lock_child(dentry);
++ if (inode)
++ err = au_reval_dpath(dentry, sgen);
++ //err = -1;
++ di_downgrade_lock(dentry, AUFS_I_RLOCK);
++ //i_unlock(inode);
++ if (unlikely(err))
++ goto out;
++ ii_read_unlock(inode);
++ DEBUG_ON(au_iigen(inode) != sgen);
++ }
++
++ if (inode) {
++ if (au_iigen(inode) == sgen)
++ ii_read_lock_child(inode);
++ else {
++ DEBUG_ON(IS_ROOT(dentry));
++#ifdef ForceInotify
++ Dbg("UDBA or survived, %.*s\n", DLNPair(dentry));
++#endif
++ ii_write_lock_child(inode);
++ err = au_refresh_hinode(inode, dentry);
++ ii_downgrade_lock(inode);
++ if (unlikely(err))
++ goto out;
++ DEBUG_ON(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)) {
++ i_lock(inode);
++ ii_write_lock(inode);
++ au_cpup_attr_nlink(inode);
++ ii_write_unlock(inode);
++ i_unlock(inode);
++ }
++#endif
++
++ err = -EINVAL;
++ do_udba = !au_flag_test(sb, AuFlag_UDBA_NONE);
++ if (do_udba && inode && ibstart(inode) >= 0
++ && au_test_higen(inode, au_h_iptr(inode)))
++ goto out;
++ err = h_d_revalidate(dentry, nd, do_udba);
++ //err = -1;
++
++ out:
++ aufs_read_unlock(dentry, AUFS_I_RLOCK);
++ TraceErr(err);
++ valid = !err;
++ //au_debug_on();
++ if (!valid)
++ LKTRTrace("%.*s invalid\n", DLNPair(dentry));
++ //au_debug_off();
++ return valid;
++}
++
++static void aufs_d_release(struct dentry *dentry)
++{
++ struct aufs_dinfo *dinfo;
++ aufs_bindex_t bend, bindex;
++
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++ DEBUG_ON(!d_unhashed(dentry));
++
++ dinfo = dentry->d_fsdata;
++ if (unlikely(!dinfo))
++ return;
++
++ /* dentry may not be revalidated */
++ bindex = dinfo->di_bstart;
++ if (bindex >= 0) {
++ struct aufs_hdentry *p;
++ bend = dinfo->di_bend;
++ DEBUG_ON(bend < bindex);
++ p = dinfo->di_hdentry + bindex;
++ while (bindex++ <= bend) {
++ if (p->hd_dentry)
++ hdput(p);
++ p++;
++ }
++ }
++ kfree(dinfo->di_hdentry);
++ cache_free_dinfo(dinfo);
++}
++
++#if 0
++/* it may be called at remount time, too */
++static void aufs_d_iput(struct dentry *dentry, struct inode *inode)
++{
++ struct super_block *sb;
++
++ LKTRTrace("%.*s, i%lu\n", DLNPair(dentry), inode->i_ino);
++
++ sb = dentry->d_sb;
++#if 0
++ si_read_lock(sb);
++ if (unlikely(au_flag_test(sb, AuFlag_PLINK)
++ && au_is_plinked(sb, inode))) {
++ ii_write_lock(inode);
++ au_update_brange(inode, 1);
++ ii_write_unlock(inode);
++ }
++ si_read_unlock(sb);
++#endif
++ iput(inode);
++}
++#endif
++
++struct dentry_operations aufs_dop = {
++ .d_revalidate = aufs_d_revalidate,
++ .d_release = aufs_d_release
++ //.d_iput = aufs_d_iput
++};
+diff --git a/fs/aufs/dentry.h b/fs/aufs/dentry.h
+new file mode 100755
+index 0000000..78049e3
+--- /dev/null
++++ b/fs/aufs/dentry.h
+@@ -0,0 +1,183 @@
++/*
++ * 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: dentry.h,v 1.25 2007/05/14 03:41:52 sfjro Exp $ */
++
++#ifndef __AUFS_DENTRY_H__
++#define __AUFS_DENTRY_H__
++
++#ifdef __KERNEL__
++
++#include <linux/fs.h>
++#include <linux/aufs_type.h>
++#include "misc.h"
++
++struct aufs_hdentry {
++ struct dentry *hd_dentry;
++};
++
++struct aufs_dinfo {
++ atomic_t di_generation;
++
++ struct aufs_rwsem di_rwsem;
++ aufs_bindex_t di_bstart, di_bend, di_bwh, di_bdiropq;
++ struct aufs_hdentry *di_hdentry;
++};
++
++struct lkup_args {
++ struct vfsmount *nfsmnt;
++ int dlgt;
++ //struct super_block *sb;
++};
++
++/* ---------------------------------------------------------------------- */
++
++/* dentry.c */
++#if defined(CONFIG_AUFS_LHASH_PATCH) || defined(CONFIG_AUFS_DLGT)
++struct dentry *lkup_one(const char *name, struct dentry *parent, int len,
++ struct lkup_args *lkup);
++#else
++static inline
++struct dentry *lkup_one(const char *name, struct dentry *parent, int len,
++ struct lkup_args *lkup)
++{
++ return lookup_one_len(name, parent, len);
++}
++#endif
++
++extern struct dentry_operations aufs_dop;
++struct dentry *sio_lkup_one(const char *name, struct dentry *parent, int len,
++ struct lkup_args *lkup);
++int lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type);
++int 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, int sgen);
++
++/* dinfo.c */
++int au_alloc_dinfo(struct dentry *dentry);
++struct aufs_dinfo *dtodi(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);
++
++aufs_bindex_t dbstart(struct dentry *dentry);
++aufs_bindex_t dbend(struct dentry *dentry);
++aufs_bindex_t dbwh(struct dentry *dentry);
++aufs_bindex_t dbdiropq(struct dentry *dentry);
++struct dentry *au_h_dptr_i(struct dentry *dentry, aufs_bindex_t bindex);
++struct dentry *au_h_dptr(struct dentry *dentry);
++
++aufs_bindex_t dbtail(struct dentry *dentry);
++aufs_bindex_t dbtaildir(struct dentry *dentry);
++aufs_bindex_t dbtail_generic(struct dentry *dentry);
++
++void set_dbstart(struct dentry *dentry, aufs_bindex_t bindex);
++void set_dbend(struct dentry *dentry, aufs_bindex_t bindex);
++void set_dbwh(struct dentry *dentry, aufs_bindex_t bindex);
++void set_dbdiropq(struct dentry *dentry, aufs_bindex_t bindex);
++void hdput(struct aufs_hdentry *hdentry);
++void set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex,
++ struct dentry *h_dentry);
++
++void au_update_digen(struct dentry *dentry);
++void au_update_dbstart(struct dentry *dentry);
++int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry);
++
++/* ---------------------------------------------------------------------- */
++
++static inline int au_digen(struct dentry *d)
++{
++ return atomic_read(&dtodi(d)->di_generation);
++}
++
++#ifdef CONFIG_AUFS_HINOTIFY
++static inline void au_digen_dec(struct dentry *d)
++{
++ atomic_dec(&dtodi(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 ReadLockFunc(name, lsc) \
++static inline void di_read_lock_##name(struct dentry *d, int flags) \
++{di_read_lock(d, flags, AuLsc_DI_##lsc);}
++
++#define WriteLockFunc(name, lsc) \
++static inline void di_write_lock_##name(struct dentry *d) \
++{di_write_lock(d, AuLsc_DI_##lsc);}
++
++#define RWLockFuncs(name, lsc) \
++ ReadLockFunc(name, lsc); \
++ WriteLockFunc(name, lsc)
++
++RWLockFuncs(child, CHILD);
++RWLockFuncs(child2, CHILD2);
++RWLockFuncs(child3, CHILD3);
++RWLockFuncs(parent, PARENT);
++RWLockFuncs(parent2, PARENT2);
++RWLockFuncs(parent3, PARENT3);
++
++#undef ReadLockFunc
++#undef WriteLockFunc
++#undef RWLockFunc
++
++/* to debug easier, do not make them inlined functions */
++#define DiMustReadLock(d) do { \
++ SiMustAnyLock((d)->d_sb); \
++ RwMustReadLock(&dtodi(d)->di_rwsem); \
++} while (0)
++
++#define DiMustWriteLock(d) do { \
++ SiMustAnyLock((d)->d_sb); \
++ RwMustWriteLock(&dtodi(d)->di_rwsem); \
++} while (0)
++
++#define DiMustAnyLock(d) do { \
++ SiMustAnyLock((d)->d_sb); \
++ RwMustAnyLock(&dtodi(d)->di_rwsem); \
++} while (0)
++
++#define DiMustNoWaiters(d) RwMustNoWaiters(&dtodi(d)->di_rwsem)
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_DENTRY_H__ */
+diff --git a/fs/aufs/dinfo.c b/fs/aufs/dinfo.c
+new file mode 100755
+index 0000000..6082149
+--- /dev/null
++++ b/fs/aufs/dinfo.c
+@@ -0,0 +1,419 @@
++/*
++ * 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: dinfo.c,v 1.23 2007/05/07 03:43:36 sfjro Exp $ */
++
++#include "aufs.h"
++
++int au_alloc_dinfo(struct dentry *dentry)
++{
++ struct aufs_dinfo *dinfo;
++ struct super_block *sb;
++ int nbr;
++
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++ DEBUG_ON(dentry->d_fsdata);
++
++ dinfo = cache_alloc_dinfo();
++ //if (LktrCond) {cache_free_dinfo(dinfo); dinfo = NULL;}
++ if (dinfo) {
++ sb = dentry->d_sb;
++ nbr = sbend(sb) + 1;
++ if (unlikely(!nbr))
++ nbr++;
++ 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) {
++ rw_init_wlock_nested(&dinfo->di_rwsem, AuLsc_DI_PARENT);
++ dinfo->di_bstart = dinfo->di_bend = -1;
++ dinfo->di_bwh = dinfo->di_bdiropq = -1;
++ atomic_set(&dinfo->di_generation, au_sigen(sb));
++
++ dentry->d_fsdata = dinfo;
++ dentry->d_op = &aufs_dop;
++ return 0; /* success */
++ }
++ cache_free_dinfo(dinfo);
++ }
++ TraceErr(-ENOMEM);
++ return -ENOMEM;
++}
++
++struct aufs_dinfo *dtodi(struct dentry *dentry)
++{
++ struct aufs_dinfo *dinfo = dentry->d_fsdata;
++ DEBUG_ON(!dinfo
++ || !dinfo->di_hdentry
++ /* || stosi(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:
++ ii_write_lock_parent(inode);
++ break;
++ case AuLsc_DI_PARENT2:
++ ii_write_lock_parent2(inode);
++ break;
++ case AuLsc_DI_PARENT3:
++ 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:
++ ii_read_lock_parent(inode);
++ break;
++ case AuLsc_DI_PARENT2:
++ ii_read_lock_parent2(inode);
++ break;
++ case AuLsc_DI_PARENT3:
++ 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?
++ rw_read_lock_nested(&dtodi(d)->di_rwsem, lsc);
++ if (d->d_inode) {
++ if (flags & AUFS_I_WLOCK)
++ do_ii_write_lock(d->d_inode, lsc);
++ else if (flags & AUFS_I_RLOCK)
++ 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 (flags & AUFS_I_WLOCK)
++ ii_write_unlock(d->d_inode);
++ else if (flags & AUFS_I_RLOCK)
++ ii_read_unlock(d->d_inode);
++ }
++ rw_read_unlock(&dtodi(d)->di_rwsem);
++}
++
++void di_downgrade_lock(struct dentry *d, int flags)
++{
++ SiMustAnyLock(d->d_sb);
++ rw_dgrade_lock(&dtodi(d)->di_rwsem);
++ if (d->d_inode && (flags & AUFS_I_RLOCK))
++ ii_downgrade_lock(d->d_inode);
++}
++
++void di_write_lock(struct dentry *d, unsigned int lsc)
++{
++ SiMustAnyLock(d->d_sb);
++ // todo: always nested?
++ rw_write_lock_nested(&dtodi(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);
++ rw_write_unlock(&dtodi(d)->di_rwsem);
++}
++
++void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir)
++{
++ struct dentry *d;
++
++ TraceEnter();
++ DEBUG_ON(d1 == d2
++ || d1->d_inode == d2->d_inode
++ || d1->d_sb != d2->d_sb);
++
++ if (isdir)
++ for (d = d1; d->d_parent != d; d = d->d_parent) // dget_parent()
++ if (d->d_parent == d2) {
++ di_write_lock_child(d1);
++ di_write_lock_child2(d2);
++ return;
++ }
++
++ di_write_lock_child(d2);
++ di_write_lock_child2(d1);
++}
++
++void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir)
++{
++ struct dentry *d;
++
++ TraceEnter();
++ DEBUG_ON(d1 == d2
++ || d1->d_inode == d2->d_inode
++ || d1->d_sb != d2->d_sb);
++
++ if (isdir)
++ for (d = d1; d->d_parent != d; d = d->d_parent) // dget_parent()
++ if (d->d_parent == d2) {
++ di_write_lock_parent(d1);
++ di_write_lock_parent2(d2);
++ return;
++ }
++
++ 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)
++ rw_write_unlock(&dtodi(d2)->di_rwsem);
++ else
++ di_write_unlock(d2);
++}
++
++/* ---------------------------------------------------------------------- */
++
++aufs_bindex_t dbstart(struct dentry *dentry)
++{
++ DiMustAnyLock(dentry);
++ return dtodi(dentry)->di_bstart;
++}
++
++aufs_bindex_t dbend(struct dentry *dentry)
++{
++ DiMustAnyLock(dentry);
++ return dtodi(dentry)->di_bend;
++}
++
++aufs_bindex_t dbwh(struct dentry *dentry)
++{
++ DiMustAnyLock(dentry);
++ return dtodi(dentry)->di_bwh;
++}
++
++aufs_bindex_t dbdiropq(struct dentry *dentry)
++{
++ DiMustAnyLock(dentry);
++ DEBUG_ON(dentry->d_inode
++ && dentry->d_inode->i_mode
++ && !S_ISDIR(dentry->d_inode->i_mode));
++ return dtodi(dentry)->di_bdiropq;
++}
++
++struct dentry *au_h_dptr_i(struct dentry *dentry, aufs_bindex_t bindex)
++{
++ struct dentry *d;
++
++ DiMustAnyLock(dentry);
++ if (dbstart(dentry) < 0 || bindex < dbstart(dentry))
++ return NULL;
++ DEBUG_ON(bindex < 0
++ /* || bindex > sbend(dentry->d_sb) */);
++ d = dtodi(dentry)->di_hdentry[0 + bindex].hd_dentry;
++ DEBUG_ON(d && (atomic_read(&d->d_count) <= 0));
++ return d;
++}
++
++struct dentry *au_h_dptr(struct dentry *dentry)
++{
++ return au_h_dptr_i(dentry, dbstart(dentry));
++}
++
++aufs_bindex_t dbtail(struct dentry *dentry)
++{
++ aufs_bindex_t bend, bwh;
++
++ bend = dbend(dentry);
++ if (0 <= bend) {
++ bwh = dbwh(dentry);
++ //DEBUG_ON(bend < bwh);
++ if (!bwh)
++ return bwh;
++ if (0 < bwh && bwh < bend)
++ return bwh - 1;
++ }
++ return bend;
++}
++
++aufs_bindex_t dbtaildir(struct dentry *dentry)
++{
++ aufs_bindex_t bend, bopq;
++
++ DEBUG_ON(dentry->d_inode
++ && dentry->d_inode->i_mode
++ && !S_ISDIR(dentry->d_inode->i_mode));
++
++ bend = dbtail(dentry);
++ if (0 <= bend) {
++ bopq = dbdiropq(dentry);
++ DEBUG_ON(bend < bopq);
++ if (0 <= bopq && bopq < bend)
++ bend = bopq;
++ }
++ return bend;
++}
++
++aufs_bindex_t dbtail_generic(struct dentry *dentry)
++{
++ struct inode *inode;
++
++ inode = dentry->d_inode;
++ if (inode && S_ISDIR(inode->i_mode))
++ return dbtaildir(dentry);
++ else
++ return dbtail(dentry);
++}
++
++/* ---------------------------------------------------------------------- */
++
++// hard/soft set
++void set_dbstart(struct dentry *dentry, aufs_bindex_t bindex)
++{
++ DiMustWriteLock(dentry);
++ DEBUG_ON(sbend(dentry->d_sb) < bindex);
++ /* */
++ dtodi(dentry)->di_bstart = bindex;
++}
++
++void set_dbend(struct dentry *dentry, aufs_bindex_t bindex)
++{
++ DiMustWriteLock(dentry);
++ DEBUG_ON(sbend(dentry->d_sb) < bindex
++ || bindex < dbstart(dentry));
++ dtodi(dentry)->di_bend = bindex;
++}
++
++void set_dbwh(struct dentry *dentry, aufs_bindex_t bindex)
++{
++ DiMustWriteLock(dentry);
++ DEBUG_ON(sbend(dentry->d_sb) < bindex);
++ /* dbwh can be outside of bstart - bend range */
++ dtodi(dentry)->di_bwh = bindex;
++}
++
++void set_dbdiropq(struct dentry *dentry, aufs_bindex_t bindex)
++{
++ DiMustWriteLock(dentry);
++ DEBUG_ON(sbend(dentry->d_sb) < bindex);
++ DEBUG_ON((bindex != -1
++ && (bindex < dbstart(dentry) || dbend(dentry) < bindex))
++ || (dentry->d_inode
++ && dentry->d_inode->i_mode
++ && !S_ISDIR(dentry->d_inode->i_mode)));
++ dtodi(dentry)->di_bdiropq = bindex;
++}
++
++void hdput(struct aufs_hdentry *hd)
++{
++ dput(hd->hd_dentry);
++}
++
++void set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex,
++ struct dentry *h_dentry)
++{
++ struct aufs_hdentry *hd = dtodi(dentry)->di_hdentry + bindex;
++ DiMustWriteLock(dentry);
++ DEBUG_ON(bindex < dtodi(dentry)->di_bstart
++ || bindex > dtodi(dentry)->di_bend
++ || (h_dentry && atomic_read(&h_dentry->d_count) <= 0)
++ || (h_dentry && hd->hd_dentry)
++ );
++ if (hd->hd_dentry)
++ hdput(hd);
++ hd->hd_dentry = h_dentry;
++}
++
++/* ---------------------------------------------------------------------- */
++
++void au_update_digen(struct dentry *dentry)
++{
++ //DiMustWriteLock(dentry);
++ DEBUG_ON(!dentry->d_sb);
++ atomic_set(&dtodi(dentry)->di_generation, au_sigen(dentry->d_sb));
++}
++
++void au_update_dbstart(struct dentry *dentry)
++{
++ aufs_bindex_t bindex, bstart = dbstart(dentry), bend = dbend(dentry);
++ struct dentry *hidden_dentry;
++
++ DiMustWriteLock(dentry);
++ for (bindex = bstart; bindex <= bend; bindex++) {
++ hidden_dentry = au_h_dptr_i(dentry, bindex);
++ if (!hidden_dentry)
++ continue;
++ if (hidden_dentry->d_inode) {
++ set_dbstart(dentry, bindex);
++ return;
++ }
++ set_h_dptr(dentry, bindex, NULL);
++ }
++ //set_dbstart(dentry, -1);
++ //set_dbend(dentry, -1);
++}
++
++int au_find_dbindex(struct dentry *dentry, struct dentry *hidden_dentry)
++{
++ aufs_bindex_t bindex, bend;
++
++ bend = dbend(dentry);
++ for (bindex = dbstart(dentry); bindex <= bend; bindex++)
++ if (au_h_dptr_i(dentry, bindex) == hidden_dentry)
++ return bindex;
++ return -1;
++}
+diff --git a/fs/aufs/dir.c b/fs/aufs/dir.c
+new file mode 100755
+index 0000000..9afb1a9
+--- /dev/null
++++ b/fs/aufs/dir.c
+@@ -0,0 +1,564 @@
++/*
++ * 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: dir.c,v 1.36 2007/05/14 03:38:52 sfjro Exp $ */
++
++#include "aufs.h"
++
++static int reopen_dir(struct file *file)
++{
++ int err;
++ struct dentry *dentry, *hidden_dentry;
++ aufs_bindex_t bindex, btail, bstart;
++ struct file *hidden_file;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++ DEBUG_ON(!S_ISDIR(dentry->d_inode->i_mode));
++
++ /* open all hidden dirs */
++ bstart = dbstart(dentry);
++#if 1
++ for (bindex = fbstart(file); bindex < bstart; bindex++)
++ set_h_fptr(file, bindex, NULL);
++#endif
++ set_fbstart(file, bstart);
++ btail = dbtaildir(dentry);
++#if 1
++ for (bindex = fbend(file); btail < bindex; bindex--)
++ set_h_fptr(file, bindex, NULL);
++#endif
++ set_fbend(file, btail);
++ for (bindex = bstart; bindex <= btail; bindex++) {
++ hidden_dentry = au_h_dptr_i(dentry, bindex);
++ if (!hidden_dentry)
++ continue;
++ hidden_file = au_h_fptr_i(file, bindex);
++ if (hidden_file) {
++ DEBUG_ON(hidden_file->f_dentry != hidden_dentry);
++ continue;
++ }
++
++ hidden_file = hidden_open(dentry, bindex, file->f_flags);
++ // unavailable
++ //if (LktrCond) {fput(hidden_file);
++ //br_put(stobr(dentry->d_sb, bindex));hidden_file=ERR_PTR(-1);}
++ err = PTR_ERR(hidden_file);
++ if (IS_ERR(hidden_file))
++ goto out; // close all?
++ //cpup_file_flags(hidden_file, file);
++ set_h_fptr(file, bindex, hidden_file);
++ }
++ err = 0;
++
++ out:
++ TraceErr(err);
++ return err;
++}
++
++static int do_open_dir(struct file *file, int flags)
++{
++ int err;
++ aufs_bindex_t bindex, btail;
++ struct dentry *dentry, *hidden_dentry;
++ struct file *hidden_file;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s, 0x%x\n", DLNPair(dentry), flags);
++ DEBUG_ON(!dentry->d_inode || !S_ISDIR(dentry->d_inode->i_mode));
++
++ err = 0;
++ set_fvdir_cache(file, NULL);
++ file->f_version = dentry->d_inode->i_version;
++ bindex = dbstart(dentry);
++ set_fbstart(file, bindex);
++ btail = dbtaildir(dentry);
++ set_fbend(file, btail);
++ for (; !err && bindex <= btail; bindex++) {
++ hidden_dentry = au_h_dptr_i(dentry, bindex);
++ if (!hidden_dentry)
++ continue;
++
++ hidden_file = hidden_open(dentry, bindex, flags);
++ //if (LktrCond) {fput(hidden_file);
++ //br_put(stobr(dentry->d_sb, bindex));hidden_file=ERR_PTR(-1);}
++ if (!IS_ERR(hidden_file)) {
++ set_h_fptr(file, bindex, hidden_file);
++ continue;
++ }
++ err = PTR_ERR(hidden_file);
++ }
++ if (!err)
++ return 0; /* success */
++
++ /* close all */
++ for (bindex = fbstart(file); !err && bindex <= btail; bindex++)
++ set_h_fptr(file, bindex, NULL);
++ set_fbstart(file, -1);
++ 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 aufs_vdir *vdir_cache;
++ struct super_block *sb;
++
++ LKTRTrace("i%lu, %.*s\n", inode->i_ino, DLNPair(file->f_dentry));
++
++ sb = file->f_dentry->d_sb;
++ si_read_lock(sb);
++ fi_write_lock(file);
++ vdir_cache = fvdir_cache(file);
++ if (vdir_cache)
++ free_vdir(vdir_cache);
++ fi_write_unlock(file);
++ au_fin_finfo(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", DLNPair(dentry), datasync);
++ DiMustAnyLock(dentry);
++ sb = dentry->d_sb;
++ SiMustAnyLock(sb);
++ inode = dentry->d_inode;
++ IMustLock(inode);
++ IiMustAnyLock(inode);
++
++ err = 0;
++ bend = dbend(dentry);
++ for (bindex = dbstart(dentry); !err && bindex <= bend; bindex++) {
++ struct dentry *h_dentry;
++ struct inode *h_inode;
++ struct file_operations *fop;
++
++ if (test_ro(sb, bindex, inode))
++ continue;
++ h_dentry = au_h_dptr_i(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 */
++ //hdir_lock(h_inode, inode, bindex);
++ i_lock(h_inode);
++ 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);
++ //hdir_unlock(h_inode, inode, bindex);
++ i_unlock(h_inode);
++ }
++
++ TraceErr(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 *hidden_file;
++ struct super_block *sb;
++ aufs_bindex_t bend, bindex;
++
++ LKTRTrace("%.*s, %d\n", DLNPair(dentry), datasync);
++ inode = dentry->d_inode;
++ IMustLock(inode);
++
++ err = 0;
++ sb = dentry->d_sb;
++ si_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, !AUFS_I_WLOCK);
++
++ ii_write_lock_child(inode);
++ if (file) {
++ bend = fbend(file);
++ for (bindex = fbstart(file); !err && bindex <= bend; bindex++) {
++ hidden_file = au_h_fptr_i(file, bindex);
++ if (!hidden_file || test_ro(sb, bindex, inode))
++ continue;
++
++ err = -EINVAL;
++ if (hidden_file->f_op && hidden_file->f_op->fsync) {
++ // todo: try do_fsync() in fs/sync.c
++#if 0
++ DEBUG_ON(hidden_file->f_dentry->d_inode
++ != au_h_iptr_i(inode, bindex));
++ hdir_lock(hidden_file->f_dentry->d_inode, inode,
++ bindex);
++#else
++ i_lock(hidden_file->f_dentry->d_inode);
++#endif
++ err = hidden_file->f_op->fsync
++ (hidden_file, hidden_file->f_dentry,
++ datasync);
++ //err = -1;
++#if 0
++ hdir_unlock(hidden_file->f_dentry->d_inode,
++ inode, bindex);
++#else
++ i_unlock(hidden_file->f_dentry->d_inode);
++#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, !AUFS_I_WLOCK);
++
++ out:
++ si_read_unlock(sb);
++ TraceErr(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", DLNPair(dentry), file->f_pos);
++ inode = dentry->d_inode;
++ IMustLock(inode);
++
++ au_nfsd_lockdep_off();
++ sb = dentry->d_sb;
++ si_read_lock(sb);
++ 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_init_vdir(file);
++ if (unlikely(err)) {
++ ii_write_unlock(inode);
++ goto out_unlock;
++ }
++ //DbgVdir(fvdir_cache(file));// goto out_unlock;
++
++ /* nfsd filldir calls lookup_one_len(). */
++ ii_downgrade_lock(inode);
++ err = au_fill_de(file, dirent, filldir);
++ //DbgVdir(fvdir_cache(file));// goto out_unlock;
++
++ inode->i_atime = au_h_iptr(inode)->i_atime;
++ 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
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++struct test_empty_arg {
++ struct aufs_nhash *whlist;
++ int whonly;
++ aufs_bindex_t bindex;
++ int err, called;
++};
++
++static int test_empty_cb(void *__arg, const char *__name, int namelen,
++ loff_t offset, filldir_ino_t ino, unsigned int d_type)
++{
++ struct test_empty_arg *arg = __arg;
++ char *name = (void*)__name;
++
++ LKTRTrace("%.*s\n", namelen, name);
++
++ arg->err = 0;
++ arg->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 (arg->whonly && !test_known_wh(arg->whlist, name, namelen))
++ arg->err = -ENOTEMPTY;
++ goto out;
++ }
++
++ name += AUFS_WH_PFX_LEN;
++ namelen -= AUFS_WH_PFX_LEN;
++ if (!test_known_wh(arg->whlist, name, namelen))
++ arg->err = append_wh(arg->whlist, name, namelen, arg->bindex);
++
++ out:
++ //smp_mb();
++ TraceErr(arg->err);
++ return arg->err;
++}
++
++static int do_test_empty(struct dentry *dentry, struct test_empty_arg *arg)
++{
++ int err, dlgt;
++ struct file *hidden_file;
++
++ LKTRTrace("%.*s, {%p, %d, %d}\n",
++ DLNPair(dentry), arg->whlist, arg->whonly, arg->bindex);
++
++ hidden_file = hidden_open(dentry, arg->bindex,
++ O_RDONLY | O_NONBLOCK | O_DIRECTORY
++ | O_LARGEFILE);
++ err = PTR_ERR(hidden_file);
++ if (IS_ERR(hidden_file))
++ goto out;
++
++ dlgt = need_dlgt(dentry->d_sb);
++ //hidden_file->f_pos = 0;
++ do {
++ arg->err = 0;
++ arg->called = 0;
++ //smp_mb();
++ err = vfsub_readdir(hidden_file, test_empty_cb, arg, dlgt);
++ if (err >= 0)
++ err = arg->err;
++ } while (!err && arg->called);
++ fput(hidden_file);
++ sbr_put(dentry->d_sb, arg->bindex);
++
++ out:
++ TraceErr(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;
++ struct dentry *hidden_dentry;
++ struct inode *hidden_inode;
++
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++ hidden_dentry = au_h_dptr_i(dentry, arg->bindex);
++ DEBUG_ON(!hidden_dentry);
++ hidden_inode = hidden_dentry->d_inode;
++ DEBUG_ON(!hidden_inode || !S_ISDIR(hidden_inode->i_mode));
++
++ hi_lock_child(hidden_inode);
++ err = au_test_perm(hidden_inode, MAY_EXEC | MAY_READ,
++ need_dlgt(dentry->d_sb));
++ i_unlock(hidden_inode);
++ if (!err)
++ err = do_test_empty(dentry, arg);
++ else {
++ struct do_test_empty_args args = {
++ .errp = &err,
++ .dentry = dentry,
++ .arg = arg
++ };
++ au_wkq_wait(call_do_test_empty, &args, /*dlgt*/0);
++ }
++
++ TraceErr(err);
++ return err;
++}
++
++int au_test_empty_lower(struct dentry *dentry)
++{
++ int err;
++ struct inode *inode;
++ struct test_empty_arg arg;
++ struct aufs_nhash *whlist;
++ aufs_bindex_t bindex, bstart, btail;
++
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++ inode = dentry->d_inode;
++ DEBUG_ON(!inode || !S_ISDIR(inode->i_mode));
++
++ whlist = nhash_new(GFP_KERNEL);
++ err = PTR_ERR(whlist);
++ if (IS_ERR(whlist))
++ goto out;
++
++ bstart = dbstart(dentry);
++ arg.whlist = whlist;
++ arg.whonly = 0;
++ arg.bindex = bstart;
++ err = do_test_empty(dentry, &arg);
++ if (unlikely(err))
++ goto out_whlist;
++
++ arg.whonly = 1;
++ btail = dbtaildir(dentry);
++ for (bindex = bstart + 1; !err && bindex <= btail; bindex++) {
++ struct dentry *hidden_dentry;
++ hidden_dentry = au_h_dptr_i(dentry, bindex);
++ if (hidden_dentry && hidden_dentry->d_inode) {
++ DEBUG_ON(!S_ISDIR(hidden_dentry->d_inode->i_mode));
++ arg.bindex = bindex;
++ err = do_test_empty(dentry, &arg);
++ }
++ }
++
++ out_whlist:
++ nhash_del(whlist);
++ out:
++ TraceErr(err);
++ return err;
++}
++
++int test_empty(struct dentry *dentry, struct aufs_nhash *whlist)
++{
++ int err;
++ struct inode *inode;
++ struct test_empty_arg arg;
++ aufs_bindex_t bindex, btail;
++
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++ inode = dentry->d_inode;
++ DEBUG_ON(!inode || !S_ISDIR(inode->i_mode));
++
++ err = 0;
++ arg.whlist = whlist;
++ arg.whonly = 1;
++ btail = dbtaildir(dentry);
++ for (bindex = dbstart(dentry); !err && bindex <= btail; bindex++) {
++ struct dentry *hidden_dentry;
++ hidden_dentry = au_h_dptr_i(dentry, bindex);
++ if (hidden_dentry && hidden_dentry->d_inode) {
++ DEBUG_ON(!S_ISDIR(hidden_dentry->d_inode->i_mode));
++ arg.bindex = bindex;
++ err = sio_test_empty(dentry, &arg);
++ }
++ }
++
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++void au_add_nlink(struct inode *dir, struct inode *h_dir)
++{
++ DEBUG_ON(!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;
++}
++
++void au_sub_nlink(struct inode *dir, struct inode *h_dir)
++{
++ DEBUG_ON(!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;
++}
++
++/* ---------------------------------------------------------------------- */
++
++#if 0 // comment
++struct file_operations {
++ struct module *owner;
++ loff_t (*llseek) (struct file *, loff_t, int);
++ ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
++ ssize_t (*aio_read) (struct kiocb *, char __user *, size_t, loff_t);
++ ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
++ ssize_t (*aio_write) (struct kiocb *, const char __user *, size_t, loff_t);
++ int (*readdir) (struct file *, void *, filldir_t);
++ unsigned int (*poll) (struct file *, struct poll_table_struct *);
++ int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
++ long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
++ long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
++ int (*mmap) (struct file *, struct vm_area_struct *);
++ int (*open) (struct inode *, struct file *);
++ int (*flush) (struct file *);
++ int (*release) (struct inode *, struct file *);
++ int (*fsync) (struct file *, struct dentry *, int datasync);
++ int (*aio_fsync) (struct kiocb *, int datasync);
++ int (*fasync) (int, struct file *, int);
++ int (*lock) (struct file *, int, struct file_lock *);
++ ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
++ ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
++ ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);
++ ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
++ unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
++ int (*check_flags)(int);
++ int (*dir_notify)(struct file *file, unsigned long arg);
++ int (*flock) (struct file *, int, struct file_lock *);
++};
++#endif
++
++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,
++};
+diff --git a/fs/aufs/dir.h b/fs/aufs/dir.h
+new file mode 100755
+index 0000000..3ddf309
+--- /dev/null
++++ b/fs/aufs/dir.h
+@@ -0,0 +1,125 @@
++/*
++ * 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: dir.h,v 1.18 2007/05/14 03:41:52 sfjro Exp $ */
++
++#ifndef __AUFS_DIR_H__
++#define __AUFS_DIR_H__
++
++#ifdef __KERNEL__
++
++#include <linux/fs.h>
++#include <linux/version.h>
++#include <linux/aufs_type.h>
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
++#define filldir_ino_t u64
++#else
++#define filldir_ino_t ino_t
++#endif
++
++/* ---------------------------------------------------------------------- */
++
++/* need to be faster and smaller */
++
++#define AUFS_DEBLK_SIZE 512 // todo: changable
++#define AUFS_NHASH_SIZE 32 // todo: changable
++#if AUFS_DEBLK_SIZE < NAME_MAX || PAGE_SIZE < AUFS_DEBLK_SIZE
++#error invalid size AUFS_DEBLK_SIZE
++#endif
++
++typedef char aufs_deblk_t[AUFS_DEBLK_SIZE];
++
++struct aufs_nhash {
++ struct hlist_head heads[AUFS_NHASH_SIZE];
++};
++
++struct aufs_destr {
++ unsigned char len;
++ char name[0];
++} __attribute__ ((packed));
++
++struct aufs_dehstr {
++ struct hlist_node hash;
++ struct aufs_destr *str;
++};
++
++struct aufs_de {
++ ino_t de_ino;
++ unsigned char de_type;
++ //caution: packed
++ struct aufs_destr de_str;
++} __attribute__ ((packed));
++
++struct aufs_wh {
++ struct hlist_node wh_hash;
++ aufs_bindex_t wh_bindex;
++ struct aufs_destr wh_str;
++} __attribute__ ((packed));
++
++union aufs_deblk_p {
++ unsigned char *p;
++ aufs_deblk_t *deblk;
++ struct aufs_de *de;
++};
++
++struct aufs_vdir {
++ aufs_deblk_t **vd_deblk;
++ int vd_nblk;
++ struct {
++ int i;
++ union aufs_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 test_empty(struct dentry *dentry, struct aufs_nhash *whlist);
++void au_add_nlink(struct inode *dir, struct inode *h_dir);
++void au_sub_nlink(struct inode *dir, struct inode *h_dir);
++
++/* vdir.c */
++struct aufs_nhash *nhash_new(gfp_t gfp);
++void nhash_del(struct aufs_nhash *nhash);
++void nhash_init(struct aufs_nhash *nhash);
++void nhash_move(struct aufs_nhash *dst, struct aufs_nhash *src);
++void nhash_fin(struct aufs_nhash *nhash);
++int is_longer_wh(struct aufs_nhash *whlist, aufs_bindex_t btgt, int limit);
++int test_known_wh(struct aufs_nhash *whlist, char *name, int namelen);
++int append_wh(struct aufs_nhash *whlist, char *name, int namelen,
++ aufs_bindex_t bindex);
++void free_vdir(struct aufs_vdir *vdir);
++int au_init_vdir(struct file *file);
++int au_fill_de(struct file *file, void *dirent, filldir_t filldir);
++
++/* ---------------------------------------------------------------------- */
++
++static inline
++unsigned int au_name_hash(const unsigned char *name, unsigned int len)
++{
++ return (full_name_hash(name, len) % AUFS_NHASH_SIZE);
++}
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_DIR_H__ */
+diff --git a/fs/aufs/export.c b/fs/aufs/export.c
+new file mode 100755
+index 0000000..7b1c6ac
+--- /dev/null
++++ b/fs/aufs/export.c
+@@ -0,0 +1,585 @@
++/*
++ * 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: export.c,v 1.7 2007/05/14 03:38:24 sfjro Exp $ */
++
++#include "aufs.h"
++
++extern struct export_operations export_op_default;
++#define CALL(ops, func) (((ops)->func) ? ((ops)->func) : export_op_default.func)
++#define is_anon(d) ((d)->d_flags & DCACHE_DISCONNECTED)
++
++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
++}
++
++static void decode_br_id_sigen(__u32 a, aufs_bindex_t *br_id,
++ aufs_bindex_t *sigen)
++{
++ BUILD_BUG_ON((sizeof(*br_id) + sizeof(*sigen)) > sizeof(a));
++ *br_id = a >> 16;
++ DEBUG_ON(*br_id < 0);
++ *sigen = a;
++ DEBUG_ON(*sigen < 0);
++}
++
++static __u32 encode_br_id_sigen(aufs_bindex_t br_id, aufs_bindex_t sigen)
++{
++ DEBUG_ON(br_id < 0 || sigen < 0);
++ return (br_id << 16) | sigen;
++}
++
++/* NFS file handle */
++enum {
++ /* support 64bit inode number */
++ /* but untested */
++ Fh_br_id_sigen,
++ Fh_ino1,
++#if BITS_PER_LONG == 64
++ Fh_ino2,
++#endif
++ Fh_dir_ino1,
++#if BITS_PER_LONG == 64
++ Fh_dir_ino2,
++#endif
++ Fh_h_ino1,
++#if BITS_PER_LONG == 64
++ Fh_h_ino2,
++#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 struct dentry *decode_by_ino(struct super_block *sb, ino_t ino,
++ ino_t dir_ino)
++{
++ struct dentry *dentry;
++ 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 (!is_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
++ && !is_anon(dentry)
++ && dentry->d_parent->d_inode->i_ino == dir_ino)
++ goto out_iput; /* success */
++
++ dput(dentry);
++ dentry = NULL;
++ }
++
++ out_iput:
++ iput(inode);
++ out:
++ TraceErrPtr(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,
++ filldir_ino_t 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(is_anon(parent))) {
++ dput(parent);
++ goto out_iput;
++ }
++ } else
++ goto out_iput;
++
++ file = dentry_open(parent, NULL, 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 lkup_one(), nor dlgt */
++ i_lock(dir);
++ dentry = lookup_one_len(arg.name, parent, arg.namelen);
++ i_unlock(dir);
++ TraceErrPtr(dentry);
++ }
++
++ //out_putname:
++ __putname(arg.name);
++ out_fput:
++ fput(file);
++ out_iput:
++ iput(dir);
++ out:
++ TraceErrPtr(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,
++ filldir_ino_t ino, unsigned int d_type)
++{
++ struct append_name *a = arg;
++ char *p;
++
++ a->called++;
++ if (ino != a->h_ino)
++ return 0;
++
++ DEBUG_ON(len == 1 && *name == '.');
++ DEBUG_ON(len == 2 && name[0] == '.' && name[1] == '.');
++ a->len = strlen(a->h_path);
++ memmove(a->h_path - a->len - 1, a->h_path, a->len);
++ a->h_path -= a->len + 1;
++ p = a->h_path + a->len;
++ *p++ = '/';
++ memcpy(p, name, a->len);
++ a->len += 1 + len;
++ a->found++;
++ return 1;
++}
++
++static int h_acceptable(void *expv, struct dentry *dentry)
++{
++ return 1;
++}
++
++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;
++ struct append_name arg;
++ int len, err;
++ struct file *h_file;
++ struct nameidata nd;
++ struct aufs_branch *br;
++
++ LKTRTrace("b%d\n", bindex);
++ SiMustAnyLock(sb);
++
++ br = stobr(sb, bindex);
++ //br_get(br);
++ h_mnt = br->br_mnt;
++ h_sb = h_mnt->mnt_sb;
++ LKTRTrace("%s, h_decode_fh\n", au_sbtype(h_sb));
++ h_parent = CALL(h_sb->s_export_op, decode_fh)
++ (h_sb, 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))) {
++ Warn1("%s decode_fh failed\n", au_sbtype(h_sb));
++ goto out;
++ }
++ dentry = NULL;
++ if (unlikely(is_anon(h_parent))) {
++ Warn1("%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, !AUFS_I_RLOCK);
++ h_root = au_h_dptr_i(root, bindex);
++ di_read_unlock(root, !AUFS_I_RLOCK);
++ arg.h_path = d_path(h_root, h_mnt, path, PATH_MAX);
++ dentry = (void*)arg.h_path;
++ if (unlikely(!arg.h_path || IS_ERR(arg.h_path)))
++ goto out_putname;
++ len = strlen(arg.h_path);
++ arg.h_path = d_path(h_parent, h_mnt, path, PATH_MAX);
++ dentry = (void*)arg.h_path;
++ if (unlikely(!arg.h_path || IS_ERR(arg.h_path)))
++ goto out_putname;
++ LKTRTrace("%s\n", arg.h_path);
++ if (len != 1)
++ arg.h_path += len;
++ LKTRTrace("%s\n", arg.h_path);
++
++ /* cf. fs/exportfs/expfs.c */
++ h_file = dentry_open(h_parent, NULL, au_dir_roflags);
++ dentry = (void*)h_file;
++ if (IS_ERR(h_file))
++ goto out_putname;
++
++ 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("%s, %d\n", arg.h_path, arg.len);
++
++ p = d_path(root, stosi(sb)->si_mnt, path, PATH_MAX - arg.len - 2);
++ dentry = (void*)p;
++ if (unlikely(!p || IS_ERR(p)))
++ goto out_fput;
++ p[strlen(p)] = '/';
++ LKTRTrace("%s\n", p);
++
++ err = path_lookup(p, LOOKUP_FOLLOW, &nd);
++ dentry = ERR_PTR(err);
++ if (!err) {
++ dentry = dget(nd.dentry);
++ if (unlikely(is_anon(dentry))) {
++ dput(dentry);
++ dentry = ERR_PTR(-ESTALE);
++ }
++ path_release(&nd);
++ }
++
++ out_fput:
++ fput(h_file);
++ out_putname:
++ __putname(path);
++ out:
++ //br_put(br);
++ TraceErrPtr(dentry);
++ return dentry;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static 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, sigen_v;
++ struct inode *inode, *h_inode;
++
++ //au_debug_on();
++ LKTRTrace("%d, fh{i%u, br_id_sigen 0x%x, hi%u}\n",
++ fh_type, fh[Fh_ino], fh[Fh_br_id_sigen], fh[Fh_h_ino]);
++ DEBUG_ON(fh_len < Fh_tail);
++
++ si_read_lock(sb);
++ lockdep_off();
++
++ /* branch id may be wrapped around */
++ dentry = ERR_PTR(-ESTALE);
++ decode_br_id_sigen(fh[Fh_br_id_sigen], &br_id, &sigen_v);
++ bindex = find_brindex(sb, br_id);
++ if (unlikely(bindex < 0 || au_sigen(sb) < sigen_v))
++ 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 (ibstart(inode) <= bindex && bindex <= ibend(inode))
++ h_inode = au_h_iptr_i(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);
++ TraceErrPtr(dentry);
++ //au_debug_off();
++ return dentry;
++}
++
++/* ---------------------------------------------------------------------- */
++
++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",
++ DLNPair(dentry), *max_len, connectable);
++ DEBUG_ON(is_anon(dentry));
++ inode = dentry->d_inode;
++ DEBUG_ON(!inode);
++ parent = dentry->d_parent;
++ DEBUG_ON(is_anon(parent));
++
++ err = -ENOSPC;
++ if (unlikely(*max_len <= Fh_tail)) {
++ Warn1("NFSv2 client (max_len %d)?\n", *max_len);
++ goto out;
++ }
++
++ sb = dentry->d_sb;
++ si_read_lock(sb);
++ di_read_lock_child(dentry, AUFS_I_RLOCK);
++ di_read_lock_parent(parent, AUFS_I_RLOCK);
++#ifdef CONFIG_AUFS_DEBUG
++ if (unlikely(!au_flag_test(sb, AuFlag_XINO)))
++ Warn1("NFS-exporting requires xino\n");
++#if 0
++ if (unlikely(au_flag_test(sb, AuFlag_UDBA_INOTIFY)))
++ Warn1("udba=inotify is not recommended when exporting\n");
++#endif
++#endif
++
++ err = -EPERM;
++ bindex = ibstart(inode);
++ h_sb = sbr_sb(sb, bindex);
++ if (unlikely(!h_sb->s_export_op)) {
++ Err1("%s branch is not exportable\n", au_sbtype(h_sb));
++ goto out_unlock;
++ }
++
++#if 0 //def CONFIG_AUFS_ROBR
++ if (unlikely(SB_AUFS(h_sb))) {
++ Err1("aufs branch is not supported\n");
++ goto out_unlock;
++ }
++#endif
++
++ /* doesn't support pseudo-link */
++ if (unlikely(bindex < dbstart(dentry)
++ || dbend(dentry) < bindex
++ || !au_h_dptr_i(dentry, bindex))) {
++ Err("%.*s/%.*s, b%d, pseudo-link?\n",
++ DLNPair(dentry->d_parent), DLNPair(dentry), bindex);
++ goto out_unlock;
++ }
++
++ fh[Fh_br_id_sigen] = encode_br_id_sigen(sbr_id(sb, bindex),
++ 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_iptr(inode);
++ encode_ino(fh + Fh_h_ino, h_inode->i_ino);
++ fh[Fh_h_igen] = h_inode->i_generation;
++
++ /* it should be set at exporting time */
++ if (unlikely(!h_sb->s_export_op->find_exported_dentry)) {
++ Warn("set default find_exported_dentry for %s\n",
++ au_sbtype(h_sb));
++ h_sb->s_export_op->find_exported_dentry = find_exported_dentry;
++ }
++
++ *max_len -= Fh_tail;
++ //LKTRTrace("Fh_tail %d, max_len %d\n", Fh_tail, *max_len);
++ h_parent = au_h_dptr_i(parent, bindex);
++ DEBUG_ON(is_anon(h_parent));
++ err = fh[Fh_h_type] = CALL(h_sb->s_export_op, encode_fh)
++ (h_parent, fh + Fh_tail, max_len, connectable);
++ *max_len += Fh_tail;
++ if (err != 255)
++ err = 2; //??
++ else
++ Warn1("%s encode_fh failed\n", au_sbtype(h_sb));
++
++ out_unlock:
++ di_read_unlock(parent, AUFS_I_RLOCK);
++ aufs_read_unlock(dentry, AUFS_I_RLOCK);
++ out:
++ TraceErr(err);
++ //au_debug_off();
++ if (unlikely(err < 0))
++ err = 255;
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++#if 0
++struct export_operations {
++ struct dentry *(*decode_fh)(struct super_block *sb, __u32 *fh, int fh_len, int fh_type,
++ int (*acceptable)(void *context, struct dentry *de),
++ void *context);
++ int (*encode_fh)(struct dentry *de, __u32 *fh, int *max_len,
++ int connectable);
++
++ /* the following are only called from the filesystem itself */
++ int (*get_name)(struct dentry *parent, char *name,
++ struct dentry *child);
++ struct dentry * (*get_parent)(struct dentry *child);
++ struct dentry * (*get_dentry)(struct super_block *sb, void *inump);
++
++ /* This is set by the exporting module to a standard helper */
++ struct dentry * (*find_exported_dentry)(
++ struct super_block *sb, void *obj, void *parent,
++ int (*acceptable)(void *context, struct dentry *de),
++ void *context);
++};
++#endif
++
++struct export_operations aufs_export_op = {
++ .decode_fh = aufs_decode_fh,
++ .encode_fh = aufs_encode_fh
++};
+diff --git a/fs/aufs/f_op.c b/fs/aufs/f_op.c
+new file mode 100755
+index 0000000..3cd1081
+--- /dev/null
++++ b/fs/aufs/f_op.c
+@@ -0,0 +1,684 @@
++/*
++ * 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: f_op.c,v 1.27 2007/05/14 03:38:24 sfjro Exp $ */
++
++#include <linux/fsnotify.h>
++#include <linux/pagemap.h>
++#include <linux/poll.h>
++#include <linux/security.h>
++#include <linux/version.h>
++#include "aufs.h"
++
++/* common function to regular file and dir */
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
++#define FlushArgs hidden_file, id
++int aufs_flush(struct file *file, fl_owner_t id)
++#else
++#define FlushArgs hidden_file
++int aufs_flush(struct file *file)
++#endif
++{
++ int err;
++ struct dentry *dentry;
++ aufs_bindex_t bindex, bend;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++
++ // aufs_read_lock_file()
++ si_read_lock(dentry->d_sb);
++ fi_read_lock(file);
++ di_read_lock_child(dentry, !AUFS_I_RLOCK);
++
++ err = 0;
++ bend = fbend(file);
++ for (bindex = fbstart(file); !err && bindex <= bend; bindex++) {
++ struct file *hidden_file;
++ hidden_file = au_h_fptr_i(file, bindex);
++ if (hidden_file && hidden_file->f_op
++ && hidden_file->f_op->flush)
++ err = hidden_file->f_op->flush(FlushArgs);
++ }
++
++ di_read_unlock(dentry, !AUFS_I_RLOCK);
++ fi_read_unlock(file);
++ si_read_unlock(dentry->d_sb);
++ TraceErr(err);
++ return err;
++}
++#undef FlushArgs
++
++/* ---------------------------------------------------------------------- */
++
++static int do_open_nondir(struct file *file, int flags)
++{
++ int err;
++ aufs_bindex_t bindex;
++ struct super_block *sb;
++ struct file *hidden_file;
++ struct dentry *dentry;
++ struct inode *inode;
++ struct aufs_finfo *finfo;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s, flags 0%o\n", DLNPair(dentry), flags);
++ FiMustWriteLock(file);
++ inode = dentry->d_inode;
++ DEBUG_ON(!inode || S_ISDIR(inode->i_mode));
++
++ err = 0;
++ finfo = ftofi(file);
++ finfo->fi_h_vm_ops = NULL;
++ sb = dentry->d_sb;
++ bindex = dbstart(dentry);
++ DEBUG_ON(!au_h_dptr(dentry)->d_inode);
++ /* O_TRUNC is processed already */
++ BUG_ON(test_ro(sb, bindex, inode) && (flags & O_TRUNC));
++
++ hidden_file = hidden_open(dentry, bindex, flags);
++ //if (LktrCond) {fput(hidden_file); br_put(stobr(dentry->d_sb, bindex));
++ //hidden_file = ERR_PTR(-1);}
++ if (!IS_ERR(hidden_file)) {
++ set_fbstart(file, bindex);
++ set_fbend(file, bindex);
++ set_h_fptr(file, bindex, hidden_file);
++ return 0; /* success */
++ }
++ err = PTR_ERR(hidden_file);
++ TraceErr(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, DLNPair(file->f_dentry));
++
++ si_read_lock(sb);
++ au_fin_finfo(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 *hidden_file;
++ struct super_block *sb;
++ struct inode *h_inode;
++ int dlgt;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s, cnt %lu, pos %Ld\n",
++ DLNPair(dentry), (unsigned long)count, *ppos);
++
++ sb = dentry->d_sb;
++ si_read_lock(sb);
++ 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 */
++ dlgt = need_dlgt(sb);
++ hidden_file = au_h_fptr(file);
++ h_inode = hidden_file->f_dentry->d_inode;
++ if (!au_flag_test(sb, AuFlag_UDBA_INOTIFY))
++ err = vfsub_read_u(hidden_file, buf, count, ppos, dlgt);
++ else {
++ struct inode *dir = dentry->d_parent->d_inode,
++ *h_dir = hidden_file->f_dentry->d_parent->d_inode;
++ aufs_bindex_t bstart = fbstart(file);
++ hdir_lock(h_dir, dir, bstart);
++ err = vfsub_read_u(hidden_file, buf, count, ppos, dlgt);
++ hdir_unlock(h_dir, dir, bstart);
++ }
++ memcpy(&file->f_ra, &hidden_file->f_ra, sizeof(file->f_ra)); //??
++ dentry->d_inode->i_atime = hidden_file->f_dentry->d_inode->i_atime;
++
++ fi_read_unlock(file);
++ out:
++ si_read_unlock(sb);
++ TraceErr(err);
++ return err;
++}
++
++static ssize_t aufs_write(struct file *file, const char __user *__buf,
++ size_t count, loff_t *ppos)
++{
++ ssize_t err;
++ struct dentry *dentry;
++ struct inode *inode;
++ struct super_block *sb;
++ struct file *hidden_file;
++ char __user *buf = (char __user*)__buf;
++ struct inode *h_inode;
++ int dlgt;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s, cnt %lu, pos %Ld\n",
++ DLNPair(dentry), (unsigned long)count, *ppos);
++
++ inode = dentry->d_inode;
++ i_lock(inode);
++ sb = dentry->d_sb;
++ si_read_lock(sb);
++ 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 */
++ dlgt = need_dlgt(sb);
++ hidden_file = au_h_fptr(file);
++ h_inode = hidden_file->f_dentry->d_inode;
++ if (!au_flag_test(sb, AuFlag_UDBA_INOTIFY))
++ err = vfsub_write_u(hidden_file, buf, count, ppos, dlgt);
++ else {
++ struct inode *dir = dentry->d_parent->d_inode,
++ *h_dir = hidden_file->f_dentry->d_parent->d_inode;
++ aufs_bindex_t bstart = fbstart(file);
++ hdir_lock(h_dir, dir, bstart);
++ err = vfsub_write_u(hidden_file, buf, count, ppos, dlgt);
++ hdir_unlock(h_dir, dir, bstart);
++ }
++ 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);
++ i_unlock(inode);
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++#if 0 //def CONFIG_AUFS_ROBR
++struct lvma {
++ struct list_head list;
++ struct vm_area_struct *vma;
++};
++
++static struct file *safe_file(struct vm_area_struct *vma)
++{
++ struct file *file = vma->vm_file;
++ struct super_block *sb = file->f_dentry->d_sb;
++ struct lvma *lvma, *entry;
++ struct aufs_sbinfo *sbinfo;
++ int found, warn;
++
++ TraceEnter();
++ DEBUG_ON(!SB_AUFS(sb));
++
++ warn = 0;
++ found = 0;
++ sbinfo = stosi(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))
++ Warn1("no memory for lvma\n");
++ return file;
++}
++
++static void reset_file(struct vm_area_struct *vma, struct file *file)
++{
++ struct super_block *sb = file->f_dentry->d_sb;
++ struct lvma *entry, *found;
++ struct aufs_sbinfo *sbinfo;
++
++ TraceEnter();
++ DEBUG_ON(!SB_AUFS(sb));
++
++ vma->vm_file = file;
++
++ found = NULL;
++ sbinfo = stosi(sb);
++ spin_lock(&sbinfo->si_lvma_lock);
++ list_for_each_entry(entry, &sbinfo->si_lvma, list)
++ if (entry->vma == vma){
++ found = entry;
++ break;
++ }
++ DEBUG_ON(!found);
++ list_del(&found->list);
++ spin_unlock(&sbinfo->si_lvma_lock);
++ kfree(found);
++}
++
++#else
++
++static struct file *safe_file(struct vm_area_struct *vma)
++{
++ struct file *file;
++
++ file = vma->vm_file;
++ if (file->private_data && au_is_aufs(file->f_dentry->d_sb))
++ return file;
++ return NULL;
++}
++
++static void reset_file(struct vm_area_struct *vma, struct file *file)
++{
++ vma->vm_file = file;
++ smp_mb();
++}
++#endif /* CONFIG_AUFS_ROBR */
++
++static struct page *aufs_nopage(struct vm_area_struct *vma, unsigned long addr,
++ int *type)
++{
++ struct page *page;
++ struct dentry *dentry;
++ struct file *file, *hidden_file;
++ struct inode *inode;
++ static DECLARE_WAIT_QUEUE_HEAD(wq);
++ struct aufs_finfo *finfo;
++
++ TraceEnter();
++ DEBUG_ON(!vma || !vma->vm_file);
++ wait_event(wq, (file = safe_file(vma)));
++ DEBUG_ON(!au_is_aufs(file->f_dentry->d_sb));
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s, addr %lx\n", DLNPair(dentry), addr);
++ inode = dentry->d_inode;
++ DEBUG_ON(!S_ISREG(inode->i_mode));
++
++ // do not revalidate, nor lock
++ finfo = ftofi(file);
++ hidden_file = finfo->fi_hfile[0 + finfo->fi_bstart].hf_file;
++ DEBUG_ON(!hidden_file || !au_is_mmapped(file));
++ vma->vm_file = hidden_file;
++ //smp_mb();
++ page = finfo->fi_h_vm_ops->nopage(vma, addr, type);
++ reset_file(vma, file);
++#if 0 //def CONFIG_SMP
++ //wake_up_nr(&wq, online_cpu - 1);
++ wake_up_all(&wq);
++#else
++ wake_up(&wq);
++#endif
++ if (!IS_ERR(page)) {
++ //page->mapping = file->f_mapping;
++ //get_page(page);
++ //file->f_mapping = hidden_file->f_mapping;
++ //touch_atime(NULL, dentry);
++ //inode->i_atime = hidden_file->f_dentry->d_inode->i_atime;
++ }
++ TraceErrPtr(page);
++ return page;
++}
++
++static int aufs_populate(struct vm_area_struct *vma, unsigned long addr,
++ unsigned long len, pgprot_t prot, unsigned long pgoff,
++ int nonblock)
++{
++ Err("please report me this application\n");
++ BUG();
++ return ftofi(vma->vm_file)->fi_h_vm_ops->populate
++ (vma, addr, len, prot, pgoff, nonblock);
++}
++
++static struct vm_operations_struct aufs_vm_ops = {
++ //.open = aufs_vmaopen,
++ //.close = aufs_vmaclose,
++ .nopage = aufs_nopage,
++ .populate = aufs_populate,
++ //page_mkwrite(struct vm_area_struct *vma, struct page *page)
++};
++
++/* ---------------------------------------------------------------------- */
++
++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;
++ unsigned long flags;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s, %lx, len %lu\n",
++ DLNPair(dentry), vma->vm_start, vma->vm_end - vma->vm_start);
++ DEBUG_ON(!S_ISREG(dentry->d_inode->i_mode));
++ DEBUG_ON(down_write_trylock(&vma->vm_mm->mmap_sem));
++
++ mmapped = au_is_mmapped(file);
++ wlock = 0;
++ if (file->f_mode & FMODE_WRITE) {
++ flags = VM_SHARED | VM_WRITE;
++ wlock = ((flags & vma->vm_flags) == flags);
++ }
++
++ sb = dentry->d_sb;
++ si_read_lock(sb);
++ 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);
++ vm_ops = ftofi(file)->fi_h_vm_ops;
++ if (unlikely(!mmapped)) {
++ // nfs uses some locks
++ lockdep_off();
++ err = h_file->f_op->mmap(h_file, vma);
++ lockdep_on();
++ if (unlikely(err))
++ goto out_unlock;
++ vm_ops = vma->vm_ops;
++ DEBUG_ON(!vm_ops);
++ err = do_munmap(current->mm, vma->vm_start,
++ vma->vm_end - vma->vm_start);
++ if (unlikely(err)) {
++ IOErr("failed internal unmapping %.*s, %d\n",
++ DLNPair(h_file->f_dentry), err);
++ err = -EIO;
++ goto out_unlock;
++ }
++ }
++ DEBUG_ON(!vm_ops);
++
++ err = generic_file_mmap(file, vma);
++ if (!err) {
++ file_accessed(h_file);
++ dentry->d_inode->i_atime = h_file->f_dentry->d_inode->i_atime;
++ vma->vm_ops = &aufs_vm_ops;
++ if (unlikely(!mmapped))
++ ftofi(file)->fi_h_vm_ops = vm_ops;
++ }
++
++ out_unlock:
++ if (!wlock && mmapped)
++ fi_read_unlock(file);
++ else
++ fi_write_unlock(file);
++ out:
++ si_read_unlock(sb);
++ TraceErr(err);
++ return err;
++}
++
++// todo: try do_sendfile() in fs/read_write.c
++static ssize_t aufs_sendfile(struct file *file, loff_t *ppos,
++ size_t count, read_actor_t actor, void *target)
++{
++ ssize_t err;
++ struct file *h_file;
++ 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);
++ struct dentry *dentry;
++ struct super_block *sb;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s, pos %Ld, cnt %lu, loopback %d\n",
++ DLNPair(dentry), *ppos, (unsigned long)count, loopback);
++
++ sb = dentry->d_sb;
++ si_read_lock(sb);
++ err = au_reval_and_lock_finfo(file, au_reopen_nondir, /*wlock*/0,
++ /*locked*/0);
++ if (unlikely(err))
++ goto out;
++
++ err = -EINVAL;
++ h_file = au_h_fptr(file);
++ if (h_file->f_op && h_file->f_op->sendfile) {
++ if (/* unlikely */(loopback)) {
++ file->f_mapping = h_file->f_mapping;
++ smp_mb(); //??
++ }
++ // nfs uses some locks
++ lockdep_off();
++ err = h_file->f_op->sendfile
++ (h_file, ppos, count, actor, target);
++ lockdep_on();
++ dentry->d_inode->i_atime = h_file->f_dentry->d_inode->i_atime;
++ }
++ fi_read_unlock(file);
++
++ out:
++ si_read_unlock(sb);
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++/* copied from linux/fs/select.h, must match */
++#define DEFAULT_POLLMASK (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)
++
++static unsigned int aufs_poll(struct file *file, poll_table *wait)
++{
++ unsigned int mask;
++ struct file *hidden_file;
++ int err;
++ struct dentry *dentry;
++ struct super_block *sb;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s, wait %p\n", DLNPair(dentry), wait);
++ DEBUG_ON(S_ISDIR(dentry->d_inode->i_mode));
++
++ /* We should pretend an error happend. */
++ mask = POLLERR /* | POLLIN | POLLOUT */;
++ sb = dentry->d_sb;
++ si_read_lock(sb);
++ 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;
++ hidden_file = au_h_fptr(file);
++ if (hidden_file->f_op && hidden_file->f_op->poll)
++ mask = hidden_file->f_op->poll(hidden_file, wait);
++ fi_read_unlock(file);
++
++ out:
++ si_read_unlock(sb);
++ TraceErr((int)mask);
++ return mask;
++}
++
++static int aufs_fsync_nondir(struct file *file, struct dentry *dentry,
++ int datasync)
++{
++ int err, my_lock;
++ struct inode *inode;
++ struct file *hidden_file;
++ struct super_block *sb;
++
++ LKTRTrace("%.*s, %d\n", DLNPair(dentry), datasync);
++ inode = dentry->d_inode;
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
++ IMustLock(inode);
++ my_lock = 0;
++#else
++ /* before 2.6.17,
++ * msync(2) calls me without locking i_sem/i_mutex, but fsync(2).
++ */
++ my_lock = !i_trylock(inode);
++#endif
++
++ sb = dentry->d_sb;
++ si_read_lock(sb);
++ 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;
++ hidden_file = au_h_fptr(file);
++ if (hidden_file->f_op && hidden_file->f_op->fsync) {
++ // todo: apparmor thread?
++ //file->f_mapping->host->i_mutex
++ ii_write_lock_child(inode);
++ hi_lock_child(hidden_file->f_dentry->d_inode);
++ err = hidden_file->f_op->fsync
++ (hidden_file, hidden_file->f_dentry, datasync);
++ //err = -1;
++ au_cpup_attr_timesizes(inode);
++ i_unlock(hidden_file->f_dentry->d_inode);
++ ii_write_unlock(inode);
++ }
++
++ out_unlock:
++ fi_write_unlock(file);
++ out:
++ if (unlikely(my_lock))
++ i_unlock(inode);
++ si_read_unlock(sb);
++ TraceErr(err);
++ return err;
++}
++
++static int aufs_fasync(int fd, struct file *file, int flag)
++{
++ int err;
++ struct file *hidden_file;
++ struct dentry *dentry;
++ struct super_block *sb;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s, %d\n", DLNPair(dentry), flag);
++
++ sb = dentry->d_sb;
++ si_read_lock(sb);
++ err = au_reval_and_lock_finfo(file, au_reopen_nondir, /*wlock*/0,
++ /*locked*/0);
++ //err = -1;
++ if (unlikely(err))
++ goto out;
++
++ hidden_file = au_h_fptr(file);
++ if (hidden_file->f_op && hidden_file->f_op->fasync)
++ err = hidden_file->f_op->fasync(fd, hidden_file, flag);
++ fi_read_unlock(file);
++
++ out:
++ si_read_unlock(sb);
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++#if 0 // comment
++struct file_operations {
++ struct module *owner;
++ loff_t (*llseek) (struct file *, loff_t, int);
++ ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
++ ssize_t (*aio_read) (struct kiocb *, char __user *, size_t, loff_t);
++ ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
++ ssize_t (*aio_write) (struct kiocb *, const char __user *, size_t, loff_t);
++ int (*readdir) (struct file *, void *, filldir_t);
++ unsigned int (*poll) (struct file *, struct poll_table_struct *);
++ int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
++ long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
++ long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
++ int (*mmap) (struct file *, struct vm_area_struct *);
++ int (*open) (struct inode *, struct file *);
++ int (*flush) (struct file *);
++ int (*release) (struct inode *, struct file *);
++ int (*fsync) (struct file *, struct dentry *, int datasync);
++ int (*aio_fsync) (struct kiocb *, int datasync);
++ int (*fasync) (int, struct file *, int);
++ int (*lock) (struct file *, int, struct file_lock *);
++ ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
++ ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
++ ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);
++ ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
++ unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
++ int (*check_flags)(int);
++ int (*dir_notify)(struct file *file, unsigned long arg);
++ int (*flock) (struct file *, int, struct file_lock *);
++};
++#endif
++
++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,
++ .sendfile = aufs_sendfile,
++};
+diff --git a/fs/aufs/file.c b/fs/aufs/file.c
+new file mode 100755
+index 0000000..857a4e8
+--- /dev/null
++++ b/fs/aufs/file.c
+@@ -0,0 +1,832 @@
++/*
++ * 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: file.c,v 1.42 2007/05/14 03:39:09 sfjro Exp $ */
++
++//#include <linux/fsnotify.h>
++#include <linux/pagemap.h>
++//#include <linux/poll.h>
++//#include <linux/security.h>
++#include "aufs.h"
++
++/* 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 *hidden_open(struct dentry *dentry, aufs_bindex_t bindex, int flags)
++{
++ struct dentry *hidden_dentry;
++ struct inode *hidden_inode;
++ struct super_block *sb;
++ struct vfsmount *hidden_mnt;
++ struct file *hidden_file;
++ struct aufs_branch *br;
++ loff_t old_size;
++ int udba;
++
++ LKTRTrace("%.*s, b%d, flags 0%o\n", DLNPair(dentry), bindex, flags);
++ DEBUG_ON(!dentry);
++ hidden_dentry = au_h_dptr_i(dentry, bindex);
++ DEBUG_ON(!hidden_dentry);
++ hidden_inode = hidden_dentry->d_inode;
++ DEBUG_ON(!hidden_inode);
++
++ sb = dentry->d_sb;
++ udba = au_flag_test(sb, AuFlag_UDBA_INOTIFY);
++ if (unlikely(udba)) {
++ // test here?
++ }
++
++ br = stobr(sb, bindex);
++ br_get(br);
++ /* drop flags for writing */
++ if (test_ro(sb, bindex, dentry->d_inode))
++ flags = au_file_roflags(flags);
++ flags &= ~O_CREAT;
++ spin_lock(&hidden_inode->i_lock);
++ old_size = i_size_read(hidden_inode);
++ spin_unlock(&hidden_inode->i_lock);
++
++ //DbgSleep(3);
++
++ dget(hidden_dentry);
++ hidden_mnt = mntget(br->br_mnt);
++ hidden_file = dentry_open(hidden_dentry, hidden_mnt, flags);
++ //if (LktrCond) {fput(hidden_file); hidden_file = ERR_PTR(-1);}
++
++ if (!IS_ERR(hidden_file)) {
++#if 0 // remove this
++ if (/* old_size && */ (flags & O_TRUNC)) {
++ au_direval_dec(dentry);
++ if (!IS_ROOT(dentry))
++ au_direval_dec(dentry->d_parent);
++ }
++#endif
++ return hidden_file;
++ }
++
++ br_put(br);
++ TraceErrPtr(hidden_file);
++ return hidden_file;
++}
++
++static int do_coo(struct dentry *dentry, aufs_bindex_t bstart)
++{
++ int err;
++ struct dentry *parent, *h_parent, *h_dentry;
++ aufs_bindex_t bcpup;
++ struct inode *h_dir, *h_inode, *dir;
++
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++ DEBUG_ON(IS_ROOT(dentry));
++ DiMustWriteLock(dentry);
++
++ parent = dentry->d_parent; // dget_parent()
++ di_write_lock_parent(parent);
++ bcpup = err = find_rw_parent_br(dentry, bstart);
++ //bcpup = err = find_rw_br(sb, bstart);
++ if (unlikely(err < 0)) {
++ err = 0; // stop copyup, it is not an error
++ goto out;
++ }
++ err = 0;
++
++ h_parent = au_h_dptr_i(parent, bcpup);
++ if (!h_parent) {
++ err = cpup_dirs(dentry, bcpup, NULL);
++ if (unlikely(err))
++ goto out;
++ h_parent = au_h_dptr_i(parent, bcpup);
++ }
++
++ h_dir = h_parent->d_inode;
++ h_dentry = au_h_dptr_i(dentry, bstart);
++ h_inode = h_dentry->d_inode;
++ dir = parent->d_inode;
++ hdir_lock(h_dir, dir, bcpup);
++ hi_lock_child(h_inode);
++ DEBUG_ON(au_h_dptr_i(dentry, bcpup));
++ err = sio_cpup_simple(dentry, bcpup, -1,
++ au_flags_cpup(CPUP_DTIME, parent));
++ TraceErr(err);
++ i_unlock(h_inode);
++ hdir_unlock(h_dir, dir, bcpup);
++
++ out:
++ di_write_unlock(parent);
++ TraceErr(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, DLNPair(dentry));
++
++ sb = dentry->d_sb;
++ si_read_lock(sb);
++ coo = 0;
++#if 0
++ switch (au_flag_test_coo(sb)) {
++ case AuFlag_COO_LEAF:
++ coo = !S_ISDIR(inode->i_mode);
++ break;
++ case AuFlag_COO_ALL:
++ coo = 1;
++ break;
++ }
++#endif
++ err = au_init_finfo(file);
++ //if (LktrCond) {fi_write_unlock(file); fin_finfo(file); err = -1;}
++ if (unlikely(err))
++ goto out;
++
++ if (!coo) {
++ di_read_lock_child(dentry, AUFS_I_RLOCK);
++ bstart = dbstart(dentry);
++ } else {
++ di_write_lock_child(dentry);
++ bstart = dbstart(dentry);
++ if (test_ro(sb, bstart, dentry->d_inode)) {
++ err = do_coo(dentry, bstart);
++ if (err) {
++ di_write_unlock(dentry);
++ goto out_finfo;
++ }
++ bstart = dbstart(dentry);
++ }
++ di_downgrade_lock(dentry, AUFS_I_RLOCK);
++ }
++
++ // todo: remove this extra locks
++ dir = dentry->d_parent->d_inode;
++ if (!IS_ROOT(dentry))
++ ii_read_lock_parent(dir);
++ h_dir = au_h_iptr_i(dir, bstart);
++ hdir_lock(h_dir, dir, bstart);
++ err = open(file, file->f_flags);
++ //if (LktrCond) err = -1;
++ hdir_unlock(h_dir, dir, bstart);
++ if (!IS_ROOT(dentry))
++ ii_read_unlock(dir);
++ di_read_unlock(dentry, AUFS_I_RLOCK);
++
++ out_finfo:
++ fi_write_unlock(file);
++ if (unlikely(err))
++ au_fin_finfo(file);
++ //DbgFile(file);
++ out:
++ si_read_unlock(sb);
++ TraceErr(err);
++ return err;
++}
++
++int au_reopen_nondir(struct file *file)
++{
++ int err;
++ struct dentry *dentry;
++ aufs_bindex_t bstart, bindex, bend;
++ struct file *hidden_file, *h_file_tmp;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++ DEBUG_ON(S_ISDIR(dentry->d_inode->i_mode)
++ || !au_h_dptr(dentry)->d_inode);
++ bstart = dbstart(dentry);
++
++ h_file_tmp = NULL;
++ if (fbstart(file) == bstart) {
++ hidden_file = au_h_fptr(file);
++ if (file->f_mode == hidden_file->f_mode)
++ return 0; /* success */
++ h_file_tmp = hidden_file;
++ get_file(h_file_tmp);
++ set_h_fptr(file, bstart, NULL);
++ }
++ DEBUG_ON(fbstart(file) < bstart
++ || ftofi(file)->fi_hfile[0 + bstart].hf_file);
++
++ hidden_file = hidden_open(dentry, bstart, file->f_flags & ~O_TRUNC);
++ //if (LktrCond) {fput(hidden_file); br_put(stobr(dentry->d_sb, bstart));
++ //hidden_file = ERR_PTR(-1);}
++ err = PTR_ERR(hidden_file);
++ if (IS_ERR(hidden_file))
++ goto out; // close all?
++ err = 0;
++ //cpup_file_flags(hidden_file, file);
++ set_fbstart(file, bstart);
++ set_h_fptr(file, bstart, hidden_file);
++ memcpy(&hidden_file->f_ra, &file->f_ra, sizeof(file->f_ra)); //??
++
++ /* close lower files */
++ bend = fbend(file);
++ for (bindex = bstart + 1; bindex <= bend; bindex++)
++ set_h_fptr(file, bindex, NULL);
++ set_fbend(file, bstart);
++
++ out:
++ if (h_file_tmp)
++ fput(h_file_tmp);
++ TraceErr(err);
++ return err;
++}
++
++/*
++ * copyup the deleted file for writing.
++ */
++static int cpup_wh_file(struct file *file, aufs_bindex_t bdst, loff_t len)
++{
++ int err;
++ struct dentry *dentry, *parent, *hidden_parent, *tmp_dentry;
++ struct dentry *hidden_dentry_bstart, *hidden_dentry_bdst;
++ struct inode *hidden_dir;
++ aufs_bindex_t bstart;
++ struct aufs_dinfo *dinfo;
++ struct dtime dt;
++ struct lkup_args lkup;
++ struct super_block *sb;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s, bdst %d, len %Lu\n", DLNPair(dentry), bdst, len);
++ DEBUG_ON(S_ISDIR(dentry->d_inode->i_mode)
++ || !(file->f_mode & FMODE_WRITE));
++ DiMustWriteLock(dentry);
++ parent = dentry->d_parent;
++ IiMustAnyLock(parent->d_inode);
++ hidden_parent = au_h_dptr_i(parent, bdst);
++ DEBUG_ON(!hidden_parent);
++ hidden_dir = hidden_parent->d_inode;
++ DEBUG_ON(!hidden_dir);
++ IMustLock(hidden_dir);
++
++ sb = parent->d_sb;
++ lkup.nfsmnt = au_nfsmnt(sb, bdst);
++ lkup.dlgt = need_dlgt(sb);
++ tmp_dentry = lkup_whtmp(hidden_parent, &dentry->d_name, &lkup);
++ //if (LktrCond) {dput(tmp_dentry); tmp_dentry = ERR_PTR(-1);}
++ err = PTR_ERR(tmp_dentry);
++ if (IS_ERR(tmp_dentry))
++ goto out;
++
++ dtime_store(&dt, parent, hidden_parent);
++ dinfo = dtodi(dentry);
++ bstart = dinfo->di_bstart;
++ hidden_dentry_bdst = dinfo->di_hdentry[0 + bdst].hd_dentry;
++ hidden_dentry_bstart = dinfo->di_hdentry[0 + bstart].hd_dentry;
++ dinfo->di_bstart = bdst;
++ dinfo->di_hdentry[0 + bdst].hd_dentry = tmp_dentry;
++ dinfo->di_hdentry[0 + bstart].hd_dentry = au_h_fptr(file)->f_dentry;
++ err = cpup_single(dentry, bdst, bstart, len,
++ au_flags_cpup(!CPUP_DTIME, parent));
++ //if (LktrCond) err = -1;
++ if (!err)
++ err = au_reopen_nondir(file);
++ //err = -1;
++ dinfo->di_hdentry[0 + bstart].hd_dentry = hidden_dentry_bstart;
++ dinfo->di_hdentry[0 + bdst].hd_dentry = hidden_dentry_bdst;
++ dinfo->di_bstart = bstart;
++ if (unlikely(err))
++ goto out_tmp;
++
++ DEBUG_ON(!d_unhashed(dentry));
++ err = vfsub_unlink(hidden_dir, tmp_dentry, lkup.dlgt);
++ //if (LktrCond) err = -1;
++ if (unlikely(err)) {
++ IOErr("failed remove copied-up tmp file %.*s(%d)\n",
++ DLNPair(tmp_dentry), err);
++ err = -EIO;
++ }
++ dtime_revert(&dt, !CPUP_LOCKED_GHDIR);
++
++ out_tmp:
++ dput(tmp_dentry);
++ out:
++ TraceErr(err);
++ return err;
++}
++
++struct cpup_wh_file_args {
++ int *errp;
++ struct file *file;
++ aufs_bindex_t bdst;
++ loff_t len;
++};
++
++static void call_cpup_wh_file(void *args)
++{
++ struct cpup_wh_file_args *a = args;
++ *a->errp = cpup_wh_file(a->file, a->bdst, a->len);
++}
++
++/*
++ * prepare the @file for writing.
++ */
++int au_ready_to_write(struct file *file, loff_t len)
++{
++ int err;
++ struct dentry *dentry, *parent, *hidden_dentry, *hidden_parent;
++ struct inode *hidden_inode, *hidden_dir, *inode, *dir;
++ struct super_block *sb;
++ aufs_bindex_t bstart, bcpup;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s, len %Ld\n", DLNPair(dentry), len);
++ FiMustWriteLock(file);
++
++ sb = dentry->d_sb;
++ bstart = fbstart(file);
++ DEBUG_ON(ftobr(file, bstart) != stobr(sb, bstart));
++
++ inode = dentry->d_inode;
++ ii_read_lock_child(inode);
++ LKTRTrace("rdonly %d, bstart %d\n", test_ro(sb, bstart, inode), bstart);
++ err = test_ro(sb, bstart, inode);
++ ii_read_unlock(inode);
++ if (!err && (au_h_fptr(file)->f_mode & FMODE_WRITE))
++ return 0;
++
++ /* need to cpup */
++ parent = dentry->d_parent; // dget_parent()
++ di_write_lock_child(dentry);
++ di_write_lock_parent(parent);
++ bcpup = err = find_rw_parent_br(dentry, bstart);
++ //bcpup = err = find_rw_br(sb, bstart);
++ if (unlikely(err < 0))
++ goto out_unlock;
++ err = 0;
++
++ hidden_parent = au_h_dptr_i(parent, bcpup);
++ if (!hidden_parent) {
++ err = cpup_dirs(dentry, bcpup, NULL);
++ //if (LktrCond) err = -1;
++ if (unlikely(err))
++ goto out_unlock;
++ hidden_parent = au_h_dptr_i(parent, bcpup);
++ }
++
++ hidden_dir = hidden_parent->d_inode;
++ hidden_dentry = au_h_fptr(file)->f_dentry;
++ hidden_inode = hidden_dentry->d_inode;
++ dir = parent->d_inode;
++ hdir_lock(hidden_dir, dir, bcpup);
++ hi_lock_child(hidden_inode);
++ if (d_unhashed(dentry) || d_unhashed(hidden_dentry)
++ /* || !hidden_inode->i_nlink */) {
++ if (!au_test_perm(hidden_dir, MAY_EXEC | MAY_WRITE,
++ need_dlgt(sb)))
++ err = cpup_wh_file(file, bcpup, len);
++ else {
++ struct cpup_wh_file_args args = {
++ .errp = &err,
++ .file = file,
++ .bdst = bcpup,
++ .len = len
++ };
++ au_wkq_wait(call_cpup_wh_file, &args, /*dlgt*/0);
++ }
++ //if (LktrCond) err = -1;
++ TraceErr(err);
++ } else {
++ if (!au_h_dptr_i(dentry, bcpup))
++ err = sio_cpup_simple(dentry, bcpup, len,
++ au_flags_cpup(CPUP_DTIME,
++ parent));
++ //if (LktrCond) err = -1;
++ TraceErr(err);
++ if (!err)
++ err = au_reopen_nondir(file);
++ //if (LktrCond) err = -1;
++ TraceErr(err);
++ }
++ i_unlock(hidden_inode);
++ hdir_unlock(hidden_dir, dir, bcpup);
++
++ out_unlock:
++ di_write_unlock(parent);
++ di_write_unlock(dentry);
++// out:
++ TraceErr(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;
++ struct dentry *dentry;
++ aufs_bindex_t bend, bindex, bstart, brid;
++ struct aufs_hfile *p;
++ struct aufs_finfo *finfo;
++ struct super_block *sb;
++ struct inode *inode;
++ struct file *hidden_file;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++ FiMustWriteLock(file);
++ DiMustReadLock(dentry);
++ inode = dentry->d_inode;
++ IiMustReadLock(inode);
++ //au_debug_on();
++ //DbgDentry(dentry);
++ //DbgFile(file);
++ //au_debug_off();
++
++ err = -ENOMEM;
++ sb = dentry->d_sb;
++ finfo = ftofi(file);
++ bstart = finfo->fi_bstart;
++ bend = finfo->fi_bstart;
++ new_sz = sizeof(*finfo->fi_hfile) * (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 aufs_hfile tmp, *q;
++ aufs_bindex_t new_bindex;
++
++ if (!p->hf_file)
++ continue;
++ new_bindex = find_bindex(sb, p->hf_br);
++ if (new_bindex == bindex)
++ continue;
++ if (new_bindex < 0) { // test here
++ 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 = sbend(sb);
++ //au_debug_on();
++ //DbgFile(file);
++ //au_debug_off();
++ finfo->fi_bstart = s;
++ finfo->fi_bend = e;
++ }
++
++ p = finfo->fi_hfile;
++ if (!au_is_mmapped(file) && !d_unhashed(dentry)) {
++ bend = 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 = find_brindex(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 = 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);
++ }
++ //Dbg("%d, %d\n", finfo->fi_bstart, finfo->fi_bend);
++ DEBUG_ON(finfo->fi_bend < finfo->fi_bstart);
++ //DbgFile(file);
++ //DbgDentry(file->f_dentry);
++
++ err = 0;
++#if 0 // todo:
++ if (!au_h_dptr(dentry)->d_inode) {
++ au_update_figen(file);
++ goto out; /* success */
++ }
++#endif
++
++ if (unlikely(au_is_mmapped(file) || d_unhashed(dentry)))
++ goto out_update; /* success */
++
++ again:
++ bstart = ibstart(inode);
++ if (bstart < finfo->fi_bstart
++ && au_flag_test(sb, AuFlag_PLINK)
++ && au_is_plinked(sb, inode)) {
++ struct dentry *parent = dentry->d_parent; // dget_parent()
++ struct inode *dir = parent->d_inode, *h_dir;
++
++ if (test_ro(sb, bstart, inode)) {
++ di_read_lock_parent(parent, !AUFS_I_RLOCK);
++ bstart = err = find_rw_parent_br(dentry, bstart);
++ //bstart = err = find_rw_br(sb, bstart);
++ di_read_unlock(parent, !AUFS_I_RLOCK);
++ //todo: err = -1;
++ if (unlikely(err < 0))
++ goto out;
++ }
++ di_read_unlock(dentry, AUFS_I_RLOCK);
++ di_write_lock_child(dentry);
++ if (bstart != ibstart(inode)) { // todo
++ /* someone changed our inode while we were sleeping */
++ di_downgrade_lock(dentry, AUFS_I_RLOCK);
++ goto again;
++ }
++
++ di_read_lock_parent(parent, AUFS_I_RLOCK);
++ err = test_and_cpup_dirs(dentry, bstart, NULL);
++
++ // always superio.
++#if 1
++ h_dir = au_h_dptr_i(parent, bstart)->d_inode;
++ hdir_lock(h_dir, dir, bstart);
++ err = sio_cpup_simple(dentry, bstart, -1,
++ au_flags_cpup(CPUP_DTIME, parent));
++ hdir_unlock(h_dir, dir, bstart);
++ di_read_unlock(parent, AUFS_I_RLOCK);
++#else
++ if (!is_au_wkq(current)) {
++ struct cpup_pseudo_link_args args = {
++ .errp = &err,
++ .dentry = dentry,
++ .bdst = bstart,
++ .do_lock = 1
++ };
++ au_wkq_wait(call_cpup_pseudo_link, &args);
++ } else
++ err = cpup_pseudo_link(dentry, bstart, /*do_lock*/1);
++#endif
++ di_downgrade_lock(dentry, AUFS_I_RLOCK);
++ if (unlikely(err))
++ goto out;
++ }
++
++ err = reopen(file);
++ //err = -1;
++ out_update:
++ if (!err) {
++ au_update_figen(file);
++ //DbgFile(file);
++ return 0; /* success */
++ }
++
++ /* error, close all hidden files */
++ bend = fbend(file);
++ for (bindex = fbstart(file); bindex <= bend; bindex++)
++ set_h_fptr(file, bindex, NULL);
++
++ out:
++ TraceErr(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, sgen, fgen, pseudo_link;
++ struct dentry *dentry;
++ struct super_block *sb;
++ aufs_bindex_t bstart;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s, w %d, l %d\n", DLNPair(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, AUFS_I_RLOCK);
++ bstart = dbstart(dentry);
++ pseudo_link = (bstart != ibstart(dentry->d_inode));
++ di_read_unlock(dentry, AUFS_I_RLOCK);
++ if (sgen == fgen && !pseudo_link && fbstart(file) == bstart) {
++ if (!wlock)
++ fi_downgrade_lock(file);
++ return 0; /* success */
++ }
++
++ LKTRTrace("sgen %d, fgen %d\n", sgen, fgen);
++ if (sgen != au_digen(dentry)) {
++ /*
++ * 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;
++ DEBUG_ON(au_digen(dentry) != sgen);
++ }
++
++ di_read_lock_child(dentry, AUFS_I_RLOCK);
++ err = refresh_file(file, reopen);
++ //if (LktrCond) err = -1;
++ di_read_unlock(dentry, AUFS_I_RLOCK);
++ if (!err) {
++ if (!wlock)
++ fi_downgrade_lock(file);
++ } else
++ fi_write_unlock(file);
++
++ out:
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++// cf. aufs_nopage()
++// for madvise(2)
++static int aufs_readpage(struct file *file, struct page *page)
++{
++ TraceEnter();
++ 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)
++{BUG();return 0;}
++static int aufs_commit_write(struct file *file, struct page *page,
++ unsigned from, unsigned to)
++{BUG();return 0;}
++static int aufs_writepage(struct page *page, struct writeback_control *wbc)
++{BUG();return 0;}
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
++static void aufs_sync_page(struct page *page)
++{BUG();}
++#else
++static int aufs_sync_page(struct page *page)
++{BUG(); return 0;}
++#endif
++
++#if 0 // comment
++static int aufs_writepages(struct address_space *mapping,
++ struct writeback_control *wbc)
++{BUG();return 0;}
++static int aufs_readpages(struct file *filp, struct address_space *mapping,
++ struct list_head *pages, unsigned nr_pages)
++{BUG();return 0;}
++static sector_t aufs_bmap(struct address_space *mapping, sector_t block)
++{BUG();return 0;}
++#endif
++
++static int aufs_set_page_dirty(struct page *page)
++{BUG();return 0;}
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
++static void aufs_invalidatepage (struct page *page, unsigned long offset)
++{BUG();}
++#else
++static int aufs_invalidatepage (struct page *page, unsigned long offset)
++{BUG(); return 0;}
++#endif
++static int aufs_releasepage (struct page *page, gfp_t gfp)
++{BUG();return 0;}
++static ssize_t aufs_direct_IO(int rw, struct kiocb *iocb,
++ const struct iovec *iov, loff_t offset,
++ unsigned long nr_segs)
++{BUG();return 0;}
++static struct page* aufs_get_xip_page(struct address_space *mapping,
++ sector_t offset, int create)
++{BUG();return NULL;}
++//static int aufs_migratepage (struct page *newpage, struct page *page)
++//{BUG();return 0;}
++#endif
++
++#if 0 // comment
++struct address_space {
++ struct inode *host; /* owner: inode, block_device */
++ struct radix_tree_root page_tree; /* radix tree of all pages */
++ rwlock_t tree_lock; /* and rwlock protecting it */
++ unsigned int i_mmap_writable;/* count VM_SHARED mappings */
++ struct prio_tree_root i_mmap; /* tree of private and shared mappings */
++ struct list_head i_mmap_nonlinear;/*list VM_NONLINEAR mappings */
++ spinlock_t i_mmap_lock; /* protect tree, count, list */
++ unsigned int truncate_count; /* Cover race condition with truncate */
++ unsigned long nrpages; /* number of total pages */
++ pgoff_t writeback_index;/* writeback starts here */
++ struct address_space_operations *a_ops; /* methods */
++ unsigned long flags; /* error bits/gfp mask */
++ struct backing_dev_info *backing_dev_info; /* device readahead, etc */
++ spinlock_t private_lock; /* for use by the address_space */
++ struct list_head private_list; /* ditto */
++ struct address_space *assoc_mapping; /* ditto */
++} __attribute__((aligned(sizeof(long))));
++
++struct address_space_operations {
++ int (*writepage)(struct page *page, struct writeback_control *wbc);
++ int (*readpage)(struct file *, struct page *);
++ void (*sync_page)(struct page *);
++
++ /* Write back some dirty pages from this mapping. */
++ int (*writepages)(struct address_space *, struct writeback_control *);
++
++ /* Set a page dirty. Return true if this dirtied it */
++ int (*set_page_dirty)(struct page *page);
++
++ int (*readpages)(struct file *filp, struct address_space *mapping,
++ struct list_head *pages, unsigned nr_pages);
++
++ /*
++ * ext3 requires that a successful prepare_write() call be followed
++ * by a commit_write() call - they must be balanced
++ */
++ int (*prepare_write)(struct file *, struct page *, unsigned, unsigned);
++ int (*commit_write)(struct file *, struct page *, unsigned, unsigned);
++ /* Unfortunately this kludge is needed for FIBMAP. Don't use it */
++ sector_t (*bmap)(struct address_space *, sector_t);
++ void (*invalidatepage) (struct page *, unsigned long);
++ int (*releasepage) (struct page *, gfp_t);
++ ssize_t (*direct_IO)(int, struct kiocb *, const struct iovec *iov,
++ loff_t offset, unsigned long nr_segs);
++ struct page* (*get_xip_page)(struct address_space *, sector_t,
++ int);
++ /* migrate the contents of a page to the specified target */
++ int (*migratepage) (struct page *, struct page *);
++};
++#endif
++
++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,
++ //.bmap = aufs_bmap,
++ .invalidatepage = aufs_invalidatepage,
++ .releasepage = aufs_releasepage,
++ .direct_IO = aufs_direct_IO,
++ .get_xip_page = aufs_get_xip_page,
++ //.migratepage = aufs_migratepage
++#endif
++};
+diff --git a/fs/aufs/file.h b/fs/aufs/file.h
+new file mode 100755
+index 0000000..f0fa448
+--- /dev/null
++++ b/fs/aufs/file.h
+@@ -0,0 +1,140 @@
++/*
++ * 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: file.h,v 1.25 2007/05/14 03:41:52 sfjro Exp $ */
++
++#ifndef __AUFS_FILE_H__
++#define __AUFS_FILE_H__
++
++#ifdef __KERNEL__
++
++#include <linux/file.h>
++#include <linux/fs.h>
++#include <linux/version.h>
++#include <linux/aufs_type.h>
++#include "misc.h"
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
++// SEEK_xxx are defined in linux/fs.h
++#else
++enum {SEEK_SET, SEEK_CUR, SEEK_END};
++#endif
++
++/* ---------------------------------------------------------------------- */
++
++struct aufs_branch;
++struct aufs_hfile {
++ struct file *hf_file;
++ struct aufs_branch *hf_br;
++};
++
++struct aufs_vdir;
++struct aufs_finfo {
++ atomic_t fi_generation;
++
++ struct aufs_rwsem fi_rwsem;
++ struct aufs_hfile *fi_hfile;
++ aufs_bindex_t fi_bstart, fi_bend;
++
++ union {
++ struct vm_operations_struct *fi_h_vm_ops;
++ struct aufs_vdir *fi_vdir_cache;
++ };
++};
++
++/* ---------------------------------------------------------------------- */
++
++/* file.c */
++extern struct address_space_operations aufs_aop;
++unsigned int au_file_roflags(unsigned int flags);
++struct file *hidden_open(struct dentry *dentry, aufs_bindex_t bindex,
++ int flags);
++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;
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
++int aufs_flush(struct file *file, fl_owner_t id);
++#else
++int aufs_flush(struct file *file);
++#endif
++
++/* finfo.c */
++struct aufs_finfo *ftofi(struct file *file);
++aufs_bindex_t fbstart(struct file *file);
++aufs_bindex_t fbend(struct file *file);
++struct aufs_vdir *fvdir_cache(struct file *file);
++struct aufs_branch *ftobr(struct file *file, aufs_bindex_t bindex);
++struct file *au_h_fptr_i(struct file *file, aufs_bindex_t bindex);
++struct file *au_h_fptr(struct file *file);
++
++void set_fbstart(struct file *file, aufs_bindex_t bindex);
++void set_fbend(struct file *file, aufs_bindex_t bindex);
++void set_fvdir_cache(struct file *file, struct aufs_vdir *vdir_cache);
++void au_hfput(struct aufs_hfile *hf);
++void set_h_fptr(struct file *file, aufs_bindex_t bindex, struct file *h_file);
++void au_update_figen(struct file *file);
++
++void au_fin_finfo(struct file *file);
++int au_init_finfo(struct file *file);
++
++/* ---------------------------------------------------------------------- */
++
++static inline int au_figen(struct file *f)
++{
++ return atomic_read(&ftofi(f)->fi_generation);
++}
++
++static inline int au_is_mmapped(struct file *f)
++{
++ return !!(ftofi(f)->fi_h_vm_ops);
++}
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * fi_read_lock, fi_write_lock,
++ * fi_read_unlock, fi_write_unlock, fi_downgrade_lock
++ */
++SimpleRwsemFuncs(fi, struct file *f, ftofi(f)->fi_rwsem);
++
++/* to debug easier, do not make them inlined functions */
++#define FiMustReadLock(f) do {\
++ SiMustAnyLock((f)->f_dentry->d_sb); \
++ RwMustReadLock(&ftofi(f)->fi_rwsem); \
++} while (0)
++
++#define FiMustWriteLock(f) do { \
++ SiMustAnyLock((f)->f_dentry->d_sb); \
++ RwMustWriteLock(&ftofi(f)->fi_rwsem); \
++} while (0)
++
++#define FiMustAnyLock(f) do { \
++ SiMustAnyLock((f)->f_dentry->d_sb); \
++ RwMustAnyLock(&ftofi(f)->fi_rwsem); \
++} while (0)
++
++#define FiMustNoWaiters(f) RwMustNoWaiters(&ftofi(f)->fi_rwsem)
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_FILE_H__ */
+diff --git a/fs/aufs/finfo.c b/fs/aufs/finfo.c
+new file mode 100755
+index 0000000..1e09da8
+--- /dev/null
++++ b/fs/aufs/finfo.c
+@@ -0,0 +1,211 @@
++/*
++ * 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: finfo.c,v 1.23 2007/04/30 05:45:21 sfjro Exp $ */
++
++#include "aufs.h"
++
++struct aufs_finfo *ftofi(struct file *file)
++{
++ struct aufs_finfo *finfo = file->private_data;
++ DEBUG_ON(!finfo
++ || !finfo->fi_hfile
++ || (0 < finfo->fi_bend
++ && (/* stosi(file->f_dentry->d_sb)->si_bend
++ < finfo->fi_bend
++ || */ finfo->fi_bend < finfo->fi_bstart)));
++ return finfo;
++}
++
++// hard/soft set
++aufs_bindex_t fbstart(struct file *file)
++{
++ FiMustAnyLock(file);
++ return ftofi(file)->fi_bstart;
++}
++
++aufs_bindex_t fbend(struct file *file)
++{
++ FiMustAnyLock(file);
++ return ftofi(file)->fi_bend;
++}
++
++struct aufs_vdir *fvdir_cache(struct file *file)
++{
++ FiMustAnyLock(file);
++ return ftofi(file)->fi_vdir_cache;
++}
++
++struct aufs_branch *ftobr(struct file *file, aufs_bindex_t bindex)
++{
++ struct aufs_finfo *finfo = ftofi(file);
++ struct aufs_hfile *hf;
++
++ FiMustAnyLock(file);
++ DEBUG_ON(!finfo
++ || finfo->fi_bstart < 0
++ || bindex < finfo->fi_bstart
++ || finfo->fi_bend < bindex);
++ hf = finfo->fi_hfile + bindex;
++ DEBUG_ON(hf->hf_br && br_count(hf->hf_br) <= 0);
++ return hf->hf_br;
++}
++
++struct file *au_h_fptr_i(struct file *file, aufs_bindex_t bindex)
++{
++ struct aufs_finfo *finfo = ftofi(file);
++ struct aufs_hfile *hf;
++
++ FiMustAnyLock(file);
++ DEBUG_ON(!finfo
++ || finfo->fi_bstart < 0
++ || bindex < finfo->fi_bstart
++ || finfo->fi_bend < bindex);
++ hf = finfo->fi_hfile + bindex;
++ DEBUG_ON(hf->hf_file
++ && file_count(hf->hf_file) <= 0
++ && br_count(hf->hf_br) <= 0);
++ return hf->hf_file;
++}
++
++struct file *au_h_fptr(struct file *file)
++{
++ return au_h_fptr_i(file, fbstart(file));
++}
++
++void set_fbstart(struct file *file, aufs_bindex_t bindex)
++{
++ FiMustWriteLock(file);
++ DEBUG_ON(sbend(file->f_dentry->d_sb) < bindex);
++ ftofi(file)->fi_bstart = bindex;
++}
++
++void set_fbend(struct file *file, aufs_bindex_t bindex)
++{
++ FiMustWriteLock(file);
++ DEBUG_ON(sbend(file->f_dentry->d_sb) < bindex
++ || bindex < fbstart(file));
++ ftofi(file)->fi_bend = bindex;
++}
++
++void set_fvdir_cache(struct file *file, struct aufs_vdir *vdir_cache)
++{
++ FiMustWriteLock(file);
++ DEBUG_ON(!S_ISDIR(file->f_dentry->d_inode->i_mode)
++ || (ftofi(file)->fi_vdir_cache && vdir_cache));
++ ftofi(file)->fi_vdir_cache = vdir_cache;
++}
++
++void au_hfput(struct aufs_hfile *hf)
++{
++ fput(hf->hf_file);
++ hf->hf_file = NULL;
++ DEBUG_ON(!hf->hf_br);
++ br_put(hf->hf_br);
++ hf->hf_br = NULL;
++}
++
++void set_h_fptr(struct file *file, aufs_bindex_t bindex, struct file *val)
++{
++ struct aufs_finfo *finfo = ftofi(file);
++ struct aufs_hfile *hf;
++
++ FiMustWriteLock(file);
++ DEBUG_ON(!finfo
++ || finfo->fi_bstart < 0
++ || bindex < finfo->fi_bstart
++ || finfo->fi_bend < bindex);
++ DEBUG_ON(val && file_count(val) <= 0);
++ hf = finfo->fi_hfile + bindex;
++ DEBUG_ON(val && hf->hf_file);
++ if (hf->hf_file)
++ au_hfput(hf);
++ if (val) {
++ hf->hf_file = val;
++ hf->hf_br = stobr(file->f_dentry->d_sb, bindex);
++ }
++}
++
++void au_update_figen(struct file *file)
++{
++ atomic_set(&ftofi(file)->fi_generation, au_digen(file->f_dentry));
++}
++
++void au_fin_finfo(struct file *file)
++{
++ struct aufs_finfo *finfo;
++ struct dentry *dentry;
++ aufs_bindex_t bindex, bend;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++ SiMustAnyLock(dentry->d_sb);
++
++ fi_write_lock(file);
++ bend = fbend(file);
++ bindex = fbstart(file);
++ if (bindex >= 0)
++ for (; bindex <= bend; bindex++)
++ set_h_fptr(file, bindex, NULL);
++
++ finfo = ftofi(file);
++#ifdef CONFIG_AUFS_DEBUG
++ if (finfo->fi_bstart >= 0) {
++ bend = fbend(file);
++ for (bindex = finfo->fi_bstart; bindex <= bend; bindex++) {
++ struct aufs_hfile *hf;
++ hf = finfo->fi_hfile + bindex;
++ DEBUG_ON(hf->hf_file || hf->hf_br);
++ }
++ }
++#endif
++
++ kfree(finfo->fi_hfile);
++ fi_write_unlock(file);
++ cache_free_finfo(finfo);
++ //file->private_data = NULL;
++}
++
++int au_init_finfo(struct file *file)
++{
++ struct aufs_finfo *finfo;
++ struct dentry *dentry;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++ DEBUG_ON(!dentry->d_inode);
++
++ finfo = cache_alloc_finfo();
++ if (finfo) {
++ finfo->fi_hfile = kcalloc(sbend(dentry->d_sb) + 1,
++ sizeof(*finfo->fi_hfile), GFP_KERNEL);
++ if (finfo->fi_hfile) {
++ rw_init_wlock(&finfo->fi_rwsem);
++ finfo->fi_bstart = -1;
++ finfo->fi_bend = -1;
++ atomic_set(&finfo->fi_generation, au_digen(dentry));
++
++ file->private_data = finfo;
++ return 0; /* success */
++ }
++ cache_free_finfo(finfo);
++ }
++
++ TraceErr(-ENOMEM);
++ return -ENOMEM;
++}
+diff --git a/fs/aufs/hinotify.c b/fs/aufs/hinotify.c
+new file mode 100755
+index 0000000..3bad3f7
+--- /dev/null
++++ b/fs/aufs/hinotify.c
+@@ -0,0 +1,536 @@
++/*
++ * 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: hinotify.c,v 1.19 2007/05/14 03:39:21 sfjro Exp $ */
++
++#include "aufs.h"
++
++static struct inotify_handle *in_handle;
++static const __u32 in_mask = (IN_MOVE | IN_DELETE | IN_CREATE /* | IN_ACCESS */
++ | IN_MODIFY | IN_ATTRIB
++ | IN_DELETE_SELF | IN_MOVE_SELF);
++
++int alloc_hinotify(struct aufs_hinode *hinode, struct inode *inode,
++ struct inode *hidden_inode)
++{
++ int err;
++ struct aufs_hinotify *hin;
++ s32 wd;
++
++ LKTRTrace("i%lu, hi%lu\n", inode->i_ino, hidden_inode->i_ino);
++
++ err = -ENOMEM;
++ hin = cache_alloc_hinotify();
++ if (hin) {
++ DEBUG_ON(hinode->hi_notify);
++ hinode->hi_notify = hin;
++ hin->hin_aufs_inode = inode;
++ inotify_init_watch(&hin->hin_watch);
++ wd = inotify_add_watch(in_handle, &hin->hin_watch, hidden_inode,
++ in_mask);
++ if (wd >= 0)
++ return 0; /* success */
++
++ err = wd;
++ put_inotify_watch(&hin->hin_watch);
++ cache_free_hinotify(hin);
++ hinode->hi_notify = NULL;
++ }
++
++ TraceErr(err);
++ return err;
++}
++
++void do_free_hinotify(struct aufs_hinode *hinode)
++{
++ int err;
++ struct aufs_hinotify *hin;
++
++ TraceEnter();
++
++ hin = hinode->hi_notify;
++ if (hin) {
++ err = 0;
++ if (atomic_read(&hin->hin_watch.count))
++ err = inotify_rm_watch(in_handle, &hin->hin_watch);
++
++ if (!err) {
++ cache_free_hinotify(hin);
++ hinode->hi_notify = NULL;
++ } else
++ IOErr1("failed inotify_rm_watch() %d\n", err);
++ }
++}
++
++/* ---------------------------------------------------------------------- */
++
++static void ctl_hinotify(struct aufs_hinode *hinode, const __u32 mask)
++{
++ struct inode *hi;
++ struct inotify_watch *watch;
++
++ hi = hinode->hi_inode;
++ LKTRTrace("hi%lu, sb %p, 0x%x\n", hi->i_ino, hi->i_sb, mask);
++ if (0 && !strcmp(current->comm, "link"))
++ dump_stack();
++ IMustLock(hi);
++ if (!hinode->hi_notify)
++ return;
++
++ watch = &hinode->hi_notify->hin_watch;
++#if 0
++ {
++ u32 wd;
++ wd = inotify_find_update_watch(in_handle, hi, mask);
++ TraceErr(wd);
++ // ignore an err;
++ }
++#else
++ watch->mask = mask;
++ smp_mb();
++#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, in_mask)
++
++void do_hdir_lock(struct inode *h_dir, struct inode *dir, aufs_bindex_t bindex,
++ unsigned int lsc)
++{
++ struct aufs_hinode *hinode;
++
++ LKTRTrace("i%lu, b%d, lsc %d\n", dir->i_ino, bindex, lsc);
++ DEBUG_ON(!S_ISDIR(dir->i_mode));
++ hinode = itoii(dir)->ii_hinode + bindex;
++ DEBUG_ON(h_dir != hinode->hi_inode);
++
++ hi_lock(h_dir, lsc);
++ if (1 /* unlikely(au_flag_test(dir->i_sb, AuFlag_UDBA_HINOTIFY) */)
++ suspend_hinotify(hinode);
++}
++
++void hdir_unlock(struct inode *h_dir, struct inode *dir, aufs_bindex_t bindex)
++{
++ struct aufs_hinode *hinode;
++
++ LKTRTrace("i%lu, b%d\n", dir->i_ino, bindex);
++ DEBUG_ON(!S_ISDIR(dir->i_mode));
++ hinode = itoii(dir)->ii_hinode + bindex;
++ DEBUG_ON(h_dir != hinode->hi_inode);
++
++ if (1 /* unlikely(au_flag_test(dir->i_sb, AuFlag_UDBA_HINOTIFY) */)
++ resume_hinotify(hinode);
++ i_unlock(h_dir);
++}
++
++void hdir_lock_rename(struct dentry **h_parents, struct inode **dirs,
++ aufs_bindex_t bindex, int issamedir)
++{
++ struct aufs_hinode *hinode;
++
++ LKTRTrace("%.*s, %.*s\n", DLNPair(h_parents[0]), DLNPair(h_parents[1]));
++
++ vfsub_lock_rename(h_parents[0], h_parents[1]);
++ hinode = itoii(dirs[0])->ii_hinode + bindex;
++ DEBUG_ON(h_parents[0]->d_inode != hinode->hi_inode);
++ suspend_hinotify(hinode);
++ if (issamedir)
++ return;
++ hinode = itoii(dirs[1])->ii_hinode + bindex;
++ DEBUG_ON(h_parents[1]->d_inode != hinode->hi_inode);
++ suspend_hinotify(hinode);
++}
++
++void hdir_unlock_rename(struct dentry **h_parents, struct inode **dirs,
++ aufs_bindex_t bindex, int issamedir)
++{
++ struct aufs_hinode *hinode;
++
++ LKTRTrace("%.*s, %.*s\n", DLNPair(h_parents[0]), DLNPair(h_parents[1]));
++
++ hinode = itoii(dirs[0])->ii_hinode + bindex;
++ DEBUG_ON(h_parents[0]->d_inode != hinode->hi_inode);
++ resume_hinotify(hinode);
++ if (!issamedir) {
++ hinode = itoii(dirs[1])->ii_hinode + bindex;
++ DEBUG_ON(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;
++
++ LKTRTrace("i%lu, 0x%x\n", inode->i_ino, flags);
++
++ bend = ibend(inode);
++ for (bindex = ibstart(inode); bindex <= bend; bindex++) {
++ hi = au_h_iptr_i(inode, bindex);
++ if (hi) {
++ //hi_lock(hi, AUFS_LSC_H_CHILD);
++ igrab(hi);
++ set_h_iptr(inode, bindex, NULL, 0);
++ set_h_iptr(inode, bindex, igrab(hi), flags);
++ iput(hi);
++ //i_unlock(hi);
++ }
++ }
++}
++
++/* ---------------------------------------------------------------------- */
++
++#ifdef CONFIG_AUFS_DEBUG
++static char *in_name(u32 mask)
++{
++#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
++#define in_name(m) "??"
++#endif
++
++static int dec_gen_by_name(struct inode *dir, const char *_name, u32 mask)
++{
++ int err;
++ struct dentry *parent, *child;
++ struct inode *inode;
++ struct qstr *dname;
++ char *name = (void*)_name;
++ unsigned int len;
++
++ LKTRTrace("i%lu, %s, 0x%x %s\n",
++ dir->i_ino, name, mask, in_name(mask));
++
++ err = -1;
++ parent = d_find_alias(dir);
++ if (unlikely(!parent))
++ goto out;
++
++#if 0
++ if (unlikely(!memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)))
++ name += AUFS_WH_PFX_LEN;
++#endif
++ len = strlen(name);
++ spin_lock(&dcache_lock);
++ list_for_each_entry(child, &parent->d_subdirs, d_u.d_child) {
++ dname = &child->d_name;
++ if (len == dname->len && !memcmp(dname->name, name, len)) {
++ au_digen_dec(child);
++#if 1
++ //todo: why both are needed
++ if (mask & IN_MOVE) {
++ spin_lock(&child->d_lock);
++ __d_drop(child);
++ spin_unlock(&child->d_lock);
++ }
++#endif
++
++ inode = child->d_inode;
++ if (inode)
++ au_iigen_dec(inode);
++ err = !!inode;
++
++ // todo: the i_nlink of newly created name by link(2)
++ // should be updated
++ // todo: some nfs dentry doesn't notified at deleteing
++ break;
++ }
++ }
++ spin_unlock(&dcache_lock);
++ dput(parent);
++
++ out:
++ TraceErr(err);
++ return err;
++}
++
++struct postproc_args {
++ struct inode *h_dir, *dir, *h_child_inode;
++ char *h_child_name;
++ u32 mask;
++};
++
++static void dec_gen_by_ino(struct postproc_args *a)
++{
++ struct super_block *sb;
++ aufs_bindex_t bindex, bend, bfound;
++ struct xino xino;
++ struct inode *cinode;
++
++ TraceEnter();
++
++ sb = a->dir->i_sb;
++ DEBUG_ON(!au_flag_test(sb, AuFlag_XINO));
++
++ bfound = -1;
++ bend = ibend(a->dir);
++ for (bindex = ibstart(a->dir); bfound == -1 && bindex <= bend; bindex++)
++ if (au_h_iptr_i(a->dir, bindex) == a->h_dir)
++ bfound = bindex;
++ if (bfound < 0)
++ return;
++
++ bindex = find_brindex(sb, itoii(a->dir)->ii_hinode[bfound + 0].hi_id);
++ if (bindex < 0)
++ return;
++ if (unlikely(xino_read(sb, bindex, a->h_child_inode->i_ino, &xino)))
++ return;
++ cinode = NULL;
++ if (xino.ino)
++ cinode = ilookup(sb, xino.ino);
++ if (cinode) {
++#if 1
++ if (1 || a->mask & IN_MOVE) {
++ struct dentry *child;
++ spin_lock(&dcache_lock);
++ list_for_each_entry(child, &cinode->i_dentry, d_alias)
++ au_digen_dec(child);
++ spin_unlock(&dcache_lock);
++ }
++#endif
++ au_iigen_dec(cinode);
++ iput(cinode);
++ }
++}
++
++static void reset_ino(struct postproc_args *a)
++{
++ aufs_bindex_t bindex, bend;
++ struct super_block *sb;
++ struct inode *h_dir;
++
++ sb = a->dir->i_sb;
++ bend = ibend(a->dir);
++ for (bindex = ibstart(a->dir); bindex <= bend; bindex++) {
++ h_dir = au_h_iptr_i(a->dir, bindex);
++ if (h_dir && h_dir != a->h_dir)
++ xino_write0(sb, bindex, h_dir->i_ino);
++ /* ignore this error */
++ }
++}
++
++static void postproc(void *args)
++{
++ struct postproc_args *a = args;
++ struct super_block *sb;
++ struct aufs_vdir *vdir;
++
++ //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);
++ DEBUG_ON(!a->dir);
++#if 0//def ForceInotify
++ Dbg("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);
++#endif
++
++ i_lock(a->dir);
++ sb = a->dir->i_sb;
++ si_read_lock(sb); // consider write_lock
++ ii_write_lock_parent(a->dir);
++
++ /* make dir entries obsolete */
++ vdir = ivdir(a->dir);
++ if (vdir)
++ vdir->vd_jiffy = 0;
++ a->dir->i_version++;
++
++ /*
++ * special handling root directory,
++ * sine d_revalidate may not be called later.
++ * main purpose is maintaining i_nlink.
++ */
++ if (unlikely(a->dir->i_ino == AUFS_ROOT_INO))
++ au_cpup_attr_all(a->dir);
++
++ if (a->h_child_inode && au_flag_test(sb, AuFlag_XINO))
++ dec_gen_by_ino(a);
++ else if (a->mask & (IN_MOVE_SELF | IN_DELETE_SELF))
++ reset_ino(a);
++
++ ii_write_unlock(a->dir);
++ si_read_unlock(sb);
++ i_unlock(a->dir);
++
++ au_mntput(a->dir->i_sb);
++ iput(a->h_child_inode);
++ iput(a->h_dir);
++ iput(a->dir);
++#if 0
++ if (atomic_dec_and_test(&stosi(sb)->si_hinotify))
++ wake_up_all(&stosi(sb)->si_hinotify_wq);
++#endif
++ kfree(a);
++ //au_debug_off();
++}
++
++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 aufs_hinotify *hinotify;
++ struct postproc_args *args;
++ int len;
++ char *p;
++ struct inode *dir;
++ //static DECLARE_WAIT_QUEUE_HEAD(wq);
++
++ //au_debug_on();
++ 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);
++ //au_debug_off();
++ //IMustLock(h_dir);
++#if 0 //defined(ForceInotify) || defined(DbgInotify)
++ Dbg("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)) {
++ put_inotify_watch(watch);
++ return;
++ }
++
++ switch (mask & IN_ALL_EVENTS) {
++ case IN_MODIFY:
++ case IN_ATTRIB:
++ if (h_child_name)
++ return;
++ break;
++
++ case IN_MOVED_FROM:
++ case IN_MOVED_TO:
++ case IN_CREATE:
++ DEBUG_ON(!h_child_name || !h_child_inode);
++ break;
++ case IN_DELETE:
++ /*
++ * aufs never be able to get this child inode.
++ * revalidation should be in d_revalide()
++ * by checking i_nlink, i_generation or d_unhashed().
++ */
++ DEBUG_ON(!h_child_name);
++ break;
++
++ case IN_DELETE_SELF:
++ case IN_MOVE_SELF:
++ DEBUG_ON(h_child_name || h_child_inode);
++ break;
++
++ case IN_ACCESS:
++ default:
++ DEBUG_ON(1);
++ }
++
++#ifdef DbgInotify
++ WARN_ON(1);
++#endif
++
++ /* iput() will be called in postproc() */
++ hinotify = container_of(watch, struct aufs_hinotify, hin_watch);
++ DEBUG_ON(!hinotify || !hinotify->hin_aufs_inode);
++ dir = hinotify->hin_aufs_inode;
++
++ /* force re-lookup in next d_revalidate() */
++ if (dir->i_ino != AUFS_ROOT_INO)
++ au_iigen_dec(dir);
++ len = 0;
++ if (h_child_name && dec_gen_by_name(dir, h_child_name, mask))
++ len = strlen(h_child_name);
++
++ //wait_event(wq, (args = kmalloc(sizeof(*args), GFP_KERNEL)));
++ args = kmalloc(sizeof(*args) + len + 1, GFP_KERNEL);
++ if (unlikely(!args)) {
++ Err("no memory\n");
++ return;
++ }
++ args->mask = mask;
++ args->dir = igrab(dir);
++ args->h_dir = igrab(watch->inode);
++ args->h_child_inode = NULL;
++ if (len) {
++ if (h_child_inode)
++ args->h_child_inode = igrab(h_child_inode);
++ p = (void*)args;
++ args->h_child_name = p + sizeof(*args);
++ memcpy(args->h_child_name, h_child_name, len + 1);
++ }
++ //atomic_inc(&stosi(args->dir->i_sb)->si_hinotify);
++ /* prohibit umount */
++ au_mntget(args->dir->i_sb);
++ au_wkq_nowait(postproc, args, /*dlgt*/0);
++}
++
++#if 0
++void hinotify_flush(struct super_block *sb)
++{
++ atomic_t *p = &stosi(sb)->si_hinotify;
++ wait_event(stosi(sb)->si_hinotify_wq, !atomic_read(p));
++}
++#endif
++
++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
++};
++
++/* ---------------------------------------------------------------------- */
++
++int __init au_inotify_init(void)
++{
++ in_handle = inotify_init(&aufs_inotify_ops);
++ if (!IS_ERR(in_handle))
++ return 0;
++ TraceErrPtr(in_handle);
++ return PTR_ERR(in_handle);
++}
++
++void au_inotify_fin(void)
++{
++ inotify_destroy(in_handle);
++}
+diff --git a/fs/aufs/i_op.c b/fs/aufs/i_op.c
+new file mode 100755
+index 0000000..1cd0453
+--- /dev/null
++++ b/fs/aufs/i_op.c
+@@ -0,0 +1,641 @@
++/*
++ * 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: i_op.c,v 1.30 2007/04/23 00:55:05 sfjro Exp $ */
++
++//#include <linux/fs.h>
++//#include <linux/namei.h>
++#include <linux/security.h>
++#include <asm/uaccess.h>
++#include "aufs.h"
++
++#ifdef CONFIG_AUFS_DLGT
++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);
++}
++#endif
++
++static int hidden_permission(struct inode *hidden_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",
++ hidden_inode->i_ino, mask, brperm);
++
++ err = -EACCES;
++ if (unlikely(write_mask && IS_IMMUTABLE(hidden_inode)))
++ goto out;
++
++ /* skip hidden fs test in the case of write to ro branch */
++ submask = mask & ~MAY_APPEND;
++ if (unlikely((write_mask && !br_writable(brperm))
++ || !hidden_inode->i_op
++ || !hidden_inode->i_op->permission)) {
++ //LKTRLabel(generic_permission);
++ err = generic_permission(hidden_inode, submask, NULL);
++ } else {
++ //LKTRLabel(h_inode->permission);
++ err = hidden_inode->i_op->permission(hidden_inode, submask,
++ fake_nd);
++ TraceErr(err);
++ }
++
++#if 1
++ if (!err) {
++#ifndef CONFIG_AUFS_DLGT
++ err = security_inode_permission(hidden_inode, mask, fake_nd);
++#else
++ if (!dlgt)
++ err = security_inode_permission(hidden_inode, mask,
++ fake_nd);
++ else {
++ struct security_inode_permission_args args = {
++ .errp = &err,
++ .h_inode = hidden_inode,
++ .mask = mask,
++ .fake_nd = fake_nd
++ };
++ au_wkq_wait(call_security_inode_permission, &args,
++ /*dlgt*/1);
++ }
++#endif
++ }
++#endif
++
++ out:
++ TraceErr(err);
++ return err;
++}
++
++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);
++
++#ifdef CONFIG_AUFS_FAKE_DM
++ si_read_lock(sb);
++ ii_read_lock_child(inode);
++#else
++ if (!nd || !nd->dentry) {
++ si_read_lock(sb);
++ ii_read_lock_child(inode);
++ } else if (nd->dentry->d_inode != inode) {
++ locked = 1;
++ /* lock child first, then parent */
++ si_read_lock(sb);
++ ii_read_lock_child(inode);
++ di_read_lock_parent(nd->dentry, 0);
++ } else {
++ locked = 2;
++ aufs_read_lock(nd->dentry, AUFS_I_RLOCK);
++ }
++#endif
++ 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);
++
++#ifdef CONFIG_AUFS_FAKE_DM
++ ii_read_unlock(inode);
++ si_read_unlock(sb);
++#else
++ switch (locked) {
++ case 0:
++ ii_read_unlock(inode);
++ si_read_unlock(sb);
++ break;
++ case 1:
++ di_read_unlock(nd->dentry, 0);
++ ii_read_unlock(inode);
++ si_read_unlock(sb);
++ break;
++ case 2:
++ aufs_read_unlock(nd->dentry, AUFS_I_RLOCK);
++ break;
++ default:
++ BUG();
++ }
++#endif
++}
++
++static int aufs_permission(struct inode *inode, int mask, struct nameidata *nd)
++{
++ int err, locked, dlgt;
++ aufs_bindex_t bindex, bend;
++ struct inode *hidden_inode;
++ struct super_block *sb;
++ 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 %p{%p, %p}\n",
++ inode->i_ino, mask, nondir, write_mask,
++ nd, nd ? nd->dentry : NULL, nd ? nd->mnt : NULL);
++
++ sb = inode->i_sb;
++ locked = silly_lock(inode, nd);
++ dlgt = need_dlgt(sb);
++
++ if (nd)
++ fake_nd = *nd;
++ if (/* unlikely */(nondir || write_mask)) {
++ hidden_inode = au_h_iptr(inode);
++ DEBUG_ON(!hidden_inode
++ || ((hidden_inode->i_mode & S_IFMT)
++ != (inode->i_mode & S_IFMT)));
++ err = 0;
++ bindex = ibstart(inode);
++ p = fake_dm(&fake_nd, nd, sb, bindex);
++ /* actual test will be delegated to LSM */
++ if (IS_ERR(p))
++ DEBUG_ON(PTR_ERR(p) != -ENOENT);
++ else {
++ err = hidden_permission(hidden_inode, mask, p,
++ sbr_perm(sb, bindex), dlgt);
++ fake_dm_release(p);
++ }
++ if (write_mask && !err) {
++ err = find_rw_br(sb, bindex);
++ if (err >= 0)
++ err = 0;
++ }
++ goto out;
++ }
++
++ /* non-write to dir */
++ err = 0;
++ bend = ibend(inode);
++ for (bindex = ibstart(inode); !err && bindex <= bend; bindex++) {
++ hidden_inode = au_h_iptr_i(inode, bindex);
++ if (!hidden_inode)
++ continue;
++ DEBUG_ON(!S_ISDIR(hidden_inode->i_mode));
++
++ p = fake_dm(&fake_nd, nd, sb, bindex);
++ /* actual test will be delegated to LSM */
++ if (IS_ERR(p))
++ DEBUG_ON(PTR_ERR(p) != -ENOENT);
++ else {
++ err = hidden_permission(hidden_inode, mask, p,
++ sbr_perm(sb, bindex), dlgt);
++ fake_dm_release(p);
++ }
++ }
++
++ out:
++ silly_unlock(locked, inode, nd);
++ TraceErr(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;
++
++ LKTRTrace("dir %lu, %.*s\n", dir->i_ino, DLNPair(dentry));
++ DEBUG_ON(IS_ROOT(dentry));
++ IMustLock(dir);
++
++ parent = dentry->d_parent; // dget_parent()
++ aufs_read_lock(parent, !AUFS_I_RLOCK);
++ err = au_alloc_dinfo(dentry);
++ //if (LktrCond) err = -1;
++ ret = ERR_PTR(err);
++ if (unlikely(err))
++ goto out;
++
++ err = npositive = lkup_dentry(dentry, dbstart(parent), /*type*/0);
++ //err = -1;
++ ret = ERR_PTR(err);
++ if (unlikely(err < 0))
++ goto out_unlock;
++ inode = NULL;
++ if (npositive) {
++ inode = au_new_inode(dentry);
++ 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
++ }
++
++ out_unlock:
++ di_write_unlock(dentry);
++ out:
++ aufs_read_unlock(parent, !AUFS_I_RLOCK);
++ TraceErrPtr(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 wr_dir(struct dentry *dentry, int add_entry, struct dentry *src_dentry,
++ aufs_bindex_t force_btgt, int do_lock_srcdir)
++{
++ int err;
++ aufs_bindex_t bcpup, bstart, src_bstart;
++ struct dentry *hidden_parent;
++ struct super_block *sb;
++ struct dentry *parent, *src_parent = NULL;
++ struct inode *dir, *src_dir = NULL;
++
++ LKTRTrace("%.*s, add %d, src %p, force %d, lock_srcdir %d\n",
++ DLNPair(dentry), add_entry, src_dentry, force_btgt,
++ do_lock_srcdir);
++
++ sb = dentry->d_sb;
++ parent = dentry->d_parent; // dget_parent()
++ bcpup = bstart = dbstart(dentry);
++ if (force_btgt < 0) {
++ if (src_dentry) {
++ src_bstart = dbstart(src_dentry);
++ if (src_bstart < bstart)
++ bcpup = src_bstart;
++ }
++ if (test_ro(sb, bcpup, dentry->d_inode)) {
++ if (!add_entry)
++ di_read_lock_parent(parent, !AUFS_I_RLOCK);
++ bcpup = err = find_rw_parent_br(dentry, bcpup);
++ //bcpup = err = find_rw_br(sb, bcpup);
++ if (!add_entry)
++ di_read_unlock(parent, !AUFS_I_RLOCK);
++ //err = -1;
++ if (unlikely(err < 0))
++ goto out;
++ }
++ } else {
++ DEBUG_ON(bstart <= force_btgt
++ || test_ro(sb, force_btgt, dentry->d_inode));
++ bcpup = force_btgt;
++ }
++ LKTRTrace("bstart %d, bcpup %d\n", bstart, bcpup);
++
++ err = bcpup;
++ if (bcpup == bstart)
++ goto out; /* success */
++
++ /* copyup the new parent into the branch we process */
++ hidden_parent = au_h_dptr(dentry)->d_parent; // dget_parent()
++ if (src_dentry) {
++ src_parent = src_dentry->d_parent; // dget_parent()
++ src_dir = src_parent->d_inode;
++ if (do_lock_srcdir)
++ di_write_lock_parent2(src_parent);
++ }
++
++ dir = parent->d_inode;
++ if (add_entry) {
++ au_update_dbstart(dentry);
++ IMustLock(dir);
++ DiMustWriteLock(parent);
++ IiMustWriteLock(dir);
++ } else
++ di_write_lock_parent(parent);
++
++ err = 0;
++ if (!au_h_dptr_i(parent, bcpup))
++ err = cpup_dirs(dentry, bcpup, src_parent);
++ //err = -1;
++ if (!err && add_entry) {
++ hidden_parent = au_h_dptr_i(parent, bcpup);
++ DEBUG_ON(!hidden_parent || !hidden_parent->d_inode);
++ hi_lock_parent(hidden_parent->d_inode);
++ err = lkup_neg(dentry, bcpup);
++ //err = -1;
++ i_unlock(hidden_parent->d_inode);
++ }
++
++ if (!add_entry)
++ di_write_unlock(parent);
++ if (do_lock_srcdir)
++ di_write_unlock(src_parent);
++ if (!err)
++ err = bcpup; /* success */
++ //err = -EPERM;
++ out:
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static int aufs_setattr(struct dentry *dentry, struct iattr *ia)
++{
++ int err, isdir;
++ aufs_bindex_t bstart, bcpup;
++ struct inode *hidden_inode, *inode, *dir, *h_dir, *gh_dir, *gdir;
++ struct dentry *hidden_dentry, *parent;
++ unsigned int udba;
++
++ LKTRTrace("%.*s, ia_valid 0x%x\n", DLNPair(dentry), ia->ia_valid);
++ inode = dentry->d_inode;
++ IMustLock(inode);
++
++ aufs_read_lock(dentry, AUFS_D_WLOCK);
++ bstart = dbstart(dentry);
++ bcpup = err = wr_dir(dentry, /*add*/0, /*src_dentry*/NULL,
++ /*force_btgt*/-1, /*do_lock_srcdir*/0);
++ //err = -1;
++ if (unlikely(err < 0))
++ goto out;
++
++ /* crazy udba locks */
++ udba = au_flag_test(dentry->d_sb, AuFlag_UDBA_INOTIFY);
++ parent = NULL;
++ gdir = gh_dir = dir = h_dir = NULL;
++ if ((udba || bstart != bcpup) && !IS_ROOT(dentry)) {
++ parent = dentry->d_parent; // dget_parent()
++ dir = parent->d_inode;
++ di_read_lock_parent(parent, AUFS_I_RLOCK);
++ h_dir = au_h_iptr_i(dir, bcpup);
++ }
++ if (parent) {
++ if (unlikely(udba && !IS_ROOT(parent))) {
++ gdir = parent->d_parent->d_inode; // dget_parent()
++ ii_read_lock_parent2(gdir);
++ gh_dir = au_h_iptr_i(gdir, bcpup);
++ hgdir_lock(gh_dir, gdir, bcpup);
++ }
++ hdir_lock(h_dir, dir, bcpup);
++ }
++
++ isdir = S_ISDIR(inode->i_mode);
++ hidden_dentry = au_h_dptr(dentry);
++ hidden_inode = hidden_dentry->d_inode;
++ DEBUG_ON(!hidden_inode);
++
++#define HiLock(bindex) do {\
++ if (!isdir) \
++ hi_lock_child(hidden_inode); \
++ else \
++ hdir2_lock(hidden_inode, inode, bindex); \
++ } while (0)
++#define HiUnlock(bindex) do {\
++ if (!isdir) \
++ i_unlock(hidden_inode); \
++ else \
++ hdir_unlock(hidden_inode, inode, bindex); \
++ } while (0)
++
++ if (bstart != bcpup) {
++ loff_t size = -1;
++
++ if ((ia->ia_valid & ATTR_SIZE)
++ && ia->ia_size < i_size_read(inode)) {
++ size = ia->ia_size;
++ ia->ia_valid &= ~ATTR_SIZE;
++ }
++ HiLock(bstart);
++ err = sio_cpup_simple(dentry, bcpup, size,
++ au_flags_cpup(CPUP_DTIME, parent));
++ //err = -1;
++ HiUnlock(bstart);
++ if (unlikely(err || !ia->ia_valid))
++ goto out_unlock;
++
++ hidden_dentry = au_h_dptr(dentry);
++ hidden_inode = hidden_dentry->d_inode;
++ DEBUG_ON(!hidden_inode);
++ }
++
++ HiLock(bcpup);
++ err = vfsub_notify_change(hidden_dentry, ia, need_dlgt(dentry->d_sb));
++ //err = -1;
++ if (!err)
++ au_cpup_attr_changable(inode);
++ HiUnlock(bcpup);
++#undef HiLock
++#undef HiUnlock
++
++ out_unlock:
++ if (parent) {
++ hdir_unlock(h_dir, dir, bcpup);
++ di_read_unlock(parent, AUFS_I_RLOCK);
++ }
++ if (unlikely(gdir)) {
++ hdir_unlock(gh_dir, gdir, bcpup);
++ ii_read_unlock(gdir);
++ }
++ out:
++ aufs_read_unlock(dentry, AUFS_D_WLOCK);
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static int hidden_readlink(struct dentry *dentry, int bindex,
++ char __user * buf, int bufsiz)
++{
++ struct super_block *sb;
++ struct dentry *hidden_dentry;
++
++ hidden_dentry = au_h_dptr_i(dentry, bindex);
++ if (unlikely(!hidden_dentry->d_inode->i_op
++ || !hidden_dentry->d_inode->i_op->readlink))
++ return -EINVAL;
++
++ sb = dentry->d_sb;
++ if (!test_ro(sb, bindex, dentry->d_inode)) {
++ touch_atime(sbr_mnt(sb, bindex), hidden_dentry);
++ dentry->d_inode->i_atime = hidden_dentry->d_inode->i_atime;
++ }
++ return hidden_dentry->d_inode->i_op->readlink
++ (hidden_dentry, buf, bufsiz);
++}
++
++static int aufs_readlink(struct dentry *dentry, char __user * buf, int bufsiz)
++{
++ int err;
++
++ LKTRTrace("%.*s, %d\n", DLNPair(dentry), bufsiz);
++
++ aufs_read_lock(dentry, AUFS_I_RLOCK);
++ err = hidden_readlink(dentry, dbstart(dentry), buf, bufsiz);
++ //err = -1;
++ aufs_read_unlock(dentry, AUFS_I_RLOCK);
++ TraceErr(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", DLNPair(dentry), DLNPair(nd->dentry));
++
++ err = -ENOMEM;
++ buf = __getname();
++ //buf = NULL;
++ if (unlikely(!buf))
++ goto out;
++
++ aufs_read_lock(dentry, AUFS_I_RLOCK);
++ old_fs = get_fs();
++ set_fs(KERNEL_DS);
++ err = hidden_readlink(dentry, dbstart(dentry), (char __user *)buf,
++ PATH_MAX);
++ //err = -1;
++ set_fs(old_fs);
++ aufs_read_unlock(dentry, AUFS_I_RLOCK);
++
++ if (err >= 0) {
++ buf[err] = 0;
++ /* will be freed by put_link */
++ nd_set_link(nd, buf);
++ return NULL; /* success */
++ }
++ __putname(buf);
++
++ out:
++ path_release(nd);
++ TraceErr(err);
++ return ERR_PTR(err);
++}
++
++static void aufs_put_link(struct dentry *dentry, struct nameidata *nd,
++ void *cookie)
++{
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++ __putname(nd_get_link(nd));
++}
++
++/* ---------------------------------------------------------------------- */
++#if 0 // comment
++struct inode_operations {
++ int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
++ struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *);
++ int (*link) (struct dentry *,struct inode *,struct dentry *);
++ int (*unlink) (struct inode *,struct dentry *);
++ int (*symlink) (struct inode *,struct dentry *,const char *);
++ int (*mkdir) (struct inode *,struct dentry *,int);
++ int (*rmdir) (struct inode *,struct dentry *);
++ int (*mknod) (struct inode *,struct dentry *,int,dev_t);
++ int (*rename) (struct inode *, struct dentry *,
++ struct inode *, struct dentry *);
++ int (*readlink) (struct dentry *, char __user *,int);
++ void * (*follow_link) (struct dentry *, struct nameidata *);
++ void (*put_link) (struct dentry *, struct nameidata *, void *);
++ void (*truncate) (struct inode *);
++ int (*permission) (struct inode *, int, struct nameidata *);
++ int (*setattr) (struct dentry *, struct iattr *);
++ int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
++ int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
++ ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
++ ssize_t (*listxattr) (struct dentry *, char *, size_t);
++ int (*removexattr) (struct dentry *, const char *);
++ void (*truncate_range)(struct inode *, loff_t, loff_t);
++};
++#endif
++
++struct inode_operations aufs_symlink_iop = {
++ .permission = aufs_permission,
++ .setattr = aufs_setattr,
++
++ .readlink = aufs_readlink,
++ .follow_link = aufs_follow_link,
++ .put_link = aufs_put_link
++};
++
++//i_op_add.c
++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 aufs_unlink(struct inode *dir, struct dentry *dentry);
++int aufs_rmdir(struct inode *dir, struct dentry *dentry);
++
++// i_op_ren.c
++int aufs_rename(struct inode *src_dir, struct dentry *src_dentry,
++ struct inode *dir, struct dentry *dentry);
++
++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,
++
++#if 0 // xattr
++ .setxattr = aufs_setxattr,
++ .getxattr = aufs_getxattr,
++ .listxattr = aufs_listxattr,
++ .removexattr = aufs_removexattr
++#endif
++};
++
++struct inode_operations aufs_iop = {
++ .permission = aufs_permission,
++ .setattr = aufs_setattr,
++
++#if 0 // xattr
++ .setxattr = aufs_setxattr,
++ .getxattr = aufs_getxattr,
++ .listxattr = aufs_listxattr,
++ .removexattr = aufs_removexattr
++#endif
++};
+diff --git a/fs/aufs/i_op_add.c b/fs/aufs/i_op_add.c
+new file mode 100755
+index 0000000..977d773
+--- /dev/null
++++ b/fs/aufs/i_op_add.c
+@@ -0,0 +1,621 @@
++/*
++ * 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: i_op_add.c,v 1.37 2007/05/07 03:46:08 sfjro Exp $ */
++
++//#include <linux/fs.h>
++//#include <linux/namei.h>
++#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 dentry *wh_dentry, struct dentry *dentry)
++{
++ int err, rerr;
++ aufs_bindex_t bwh;
++ struct inode *inode, *dir;
++ struct dentry *wh;
++ struct lkup_args lkup;
++
++ LKTRTrace("wh %p, %.*s\n", wh_dentry, DLNPair(dentry));
++
++ lkup.dlgt = need_dlgt(dentry->d_sb);
++ bwh = -1;
++ if (wh_dentry) {
++ bwh = dbwh(dentry);
++ err = au_unlink_wh_dentry(wh_dentry->d_parent->d_inode,
++ wh_dentry, dentry, lkup.dlgt);
++ //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;
++ /* or always cpup dir mtime? */
++ if (ibstart(dir) == dbstart(dentry))
++ au_cpup_attr_timesizes(dir);
++ dir->i_version++;
++ return 0; /* success */
++ }
++
++ err = PTR_ERR(inode);
++ if (!wh_dentry)
++ goto out;
++
++ /* revert */
++ lkup.nfsmnt = au_nfsmnt(dentry->d_sb, bwh);
++ wh = simple_create_wh(dentry, bwh, wh_dentry->d_parent, &lkup);
++ //wh = ERR_PTR(-1);
++ rerr = PTR_ERR(wh);
++ if (!IS_ERR(wh)) {
++ dput(wh);
++ goto out;
++ }
++ IOErr("%.*s reverting whiteout failed(%d, %d)\n",
++ DLNPair(dentry), err, rerr);
++ err = -EIO;
++
++ out:
++ TraceErr(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 dtime *dt,
++ struct dentry *src_dentry, int do_lock_srcdir)
++{
++ struct dentry *wh_dentry, *parent, *hidden_parent;
++ int err;
++ aufs_bindex_t bstart, bcpup;
++ struct inode *dir, *h_dir;
++ struct lkup_args lkup;
++
++ LKTRTrace("%.*s, src %p\n", DLNPair(dentry), src_dentry);
++
++ parent = dentry->d_parent;
++ bstart = dbstart(dentry);
++ bcpup = err = wr_dir(dentry, 1, src_dentry, -1, do_lock_srcdir);
++ //err = -1;
++ wh_dentry = ERR_PTR(err);
++ if (unlikely(err < 0))
++ goto out;
++
++ dir = parent->d_inode;
++ hidden_parent = au_h_dptr_i(parent, bcpup);
++ h_dir = hidden_parent->d_inode;
++ hdir_lock(h_dir, dir, bcpup);
++ if (dt)
++ dtime_store(dt, parent, hidden_parent);
++ if (/* bcpup != bstart || */ bcpup != dbwh(dentry))
++ return NULL; /* success */
++
++ lkup.nfsmnt = au_nfsmnt(parent->d_sb, bcpup);
++ lkup.dlgt = need_dlgt(parent->d_sb);
++ wh_dentry = lkup_wh(hidden_parent, &dentry->d_name, &lkup);
++ //wh_dentry = ERR_PTR(-1);
++ if (IS_ERR(wh_dentry))
++ hdir_unlock(h_dir, dir, bcpup);
++
++ out:
++ TraceErrPtr(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;
++ struct dentry *hidden_dentry, *hidden_parent, *wh_dentry, *parent;
++ struct inode *hidden_dir;
++ struct dtime dt;
++
++ LKTRTrace("type %d, %.*s\n", arg->type, DLNPair(dentry));
++ IMustLock(dir);
++
++ aufs_read_lock(dentry, AUFS_D_WLOCK);
++ parent = dentry->d_parent;
++ di_write_lock_parent(parent);
++ wh_dentry = lock_hdir_lkup_wh(dentry, &dt, /*src_dentry*/NULL,
++ /*do_lock_srcdir*/0);
++ //wh_dentry = ERR_PTR(-1);
++ err = PTR_ERR(wh_dentry);
++ if (IS_ERR(wh_dentry))
++ goto out;
++
++ hidden_dentry = au_h_dptr(dentry);
++ hidden_parent = hidden_dentry->d_parent;
++ hidden_dir = hidden_parent->d_inode;
++ IMustLock(hidden_dir);
++ dlgt = need_dlgt(dir->i_sb);
++
++#if 1 // partial testing
++ switch (arg->type) {
++ case Creat:
++#if 0
++ if (arg->u.c.nd) {
++ struct nameidata fake_nd;
++ fake_nd = *arg->u.c.nd;
++ fake_nd.dentry = dget(hidden_parent);
++ fake_nd.mnt = sbr_mnt(dentry->d_sb, dbstart(dentry));
++ mntget(fake_nd.mnt);
++ err = vfsub_create(hidden_dir, hidden_dentry,
++ arg->u.c.mode, &fake_nd, dlgt);
++ path_release(&fake_nd);
++ } else
++#endif
++ err = vfsub_create(hidden_dir, hidden_dentry,
++ arg->u.c.mode, NULL, dlgt);
++ break;
++ case Symlink:
++ err = vfsub_symlink(hidden_dir, hidden_dentry,
++ arg->u.s.symname, S_IALLUGO, dlgt);
++ break;
++ case Mknod:
++ err = vfsub_mknod(hidden_dir, hidden_dentry,
++ arg->u.m.mode, arg->u.m.dev, dlgt);
++ break;
++ default:
++ BUG();
++ }
++#else
++ err = -1;
++#endif
++ if (!err)
++ err = epilog(wh_dentry, dentry);
++ //err = -1;
++
++ /* revert */
++ if (unlikely(err && hidden_dentry->d_inode)) {
++ int rerr;
++ rerr = vfsub_unlink(hidden_dir, hidden_dentry, dlgt);
++ //rerr = -1;
++ if (rerr) {
++ IOErr("%.*s revert failure(%d, %d)\n",
++ DLNPair(dentry), err, rerr);
++ err = -EIO;
++ }
++ dtime_revert(&dt, !CPUP_LOCKED_GHDIR);
++ d_drop(dentry);
++ }
++
++ hdir_unlock(hidden_dir, dir, dbstart(dentry));
++ dput(wh_dentry);
++
++ out:
++ if (unlikely(err)) {
++ au_update_dbstart(dentry);
++ d_drop(dentry);
++ }
++ di_write_unlock(parent);
++ aufs_read_unlock(dentry, AUFS_D_WLOCK);
++ TraceErr(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, *hidden_dentry;
++ struct inode *hidden_dir, *inode;
++};
++
++static int cpup_before_link(struct dentry *src_dentry, struct inode *dir,
++ struct link_arg *a)
++{
++ int err;
++ unsigned int flags;
++ struct inode *hi, *hdir = NULL, *src_dir;
++
++ TraceEnter();
++
++ err = 0;
++ flags = au_flags_cpup(CPUP_DTIME, a->parent);
++ src_dir = a->src_parent->d_inode;
++ if (!a->issamedir) {
++ // todo: dead lock?
++ di_read_lock_parent2(a->src_parent, AUFS_I_RLOCK);
++ // this temporary unlock/lock is safe
++ hdir_unlock(a->hidden_dir, dir, a->bdst);
++ err = test_and_cpup_dirs(src_dentry, a->bdst, a->parent);
++ //err = -1;
++ if (!err) {
++ hdir = au_h_iptr_i(src_dir, a->bdst);
++ hdir_lock(hdir, src_dir, a->bdst);
++ flags = au_flags_cpup(CPUP_DTIME, a->src_parent);
++ }
++ }
++
++ if (!err) {
++ hi = au_h_dptr(src_dentry)->d_inode;
++ hi_lock_child(hi);
++ err = sio_cpup_simple(src_dentry, a->bdst, -1, flags);
++ //err = -1;
++ i_unlock(hi);
++ }
++
++ if (!a->issamedir) {
++ if (hdir)
++ hdir_unlock(hdir, src_dir, a->bdst);
++ hdir_lock(a->hidden_dir, dir, a->bdst);
++ di_read_unlock(a->src_parent, AUFS_I_RLOCK);
++ }
++
++ TraceErr(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;
++
++ TraceEnter();
++
++ sb = src_dentry->d_sb;
++ inode = src_dentry->d_inode;
++ h_dentry = au_h_dptr(src_dentry);
++ h_inode = h_dentry->d_inode;
++ bstart = ibstart(inode);
++ h_dst_inode = NULL;
++ if (bstart <= a->bdst)
++ h_dst_inode = au_h_iptr_i(inode, a->bdst);
++
++ if (!h_dst_inode) {
++ /* copyup src_dentry as the name of dentry. */
++ set_dbstart(src_dentry, a->bdst);
++ set_h_dptr(src_dentry, a->bdst, dget(a->hidden_dentry));
++ hi_lock_child(h_inode);
++ err = sio_cpup_single(src_dentry, a->bdst, a->bsrc, -1,
++ au_flags_cpup(!CPUP_DTIME, a->parent));
++ //err = -1;
++ i_unlock(h_inode);
++ set_h_dptr(src_dentry, a->bdst, NULL);
++ 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->hidden_dir,
++ a->hidden_dentry, a->dlgt);
++ dput(h_dentry);
++ } else {
++ IOErr("no dentry found for i%lu on b%d\n",
++ h_dst_inode->i_ino, a->bdst);
++ err = -EIO;
++ }
++ }
++
++ if (!err)
++ append_plink(sb, a->inode, a->hidden_dentry, a->bdst);
++
++ TraceErr(err);
++ return err;
++}
++
++int aufs_link(struct dentry *src_dentry, struct inode *dir,
++ struct dentry *dentry)
++{
++ int err, rerr;
++ struct dentry *hidden_parent, *wh_dentry, *hidden_src_dentry;
++ struct dtime dt;
++ struct link_arg a;
++ struct super_block *sb;
++
++ LKTRTrace("src %.*s, i%lu, dst %.*s\n",
++ DLNPair(src_dentry), dir->i_ino, DLNPair(dentry));
++ IMustLock(dir);
++ IMustLock(src_dentry->d_inode);
++
++ aufs_read_and_write_lock2(dentry, src_dentry, /*isdir*/0);
++ a.src_parent = src_dentry->d_parent;
++ a.parent = dentry->d_parent;
++ a.issamedir = (a.src_parent == a.parent);
++ di_write_lock_parent(a.parent);
++ wh_dentry = lock_hdir_lkup_wh(dentry, &dt, src_dentry, !a.issamedir);
++ //wh_dentry = ERR_PTR(-1);
++ err = PTR_ERR(wh_dentry);
++ if (IS_ERR(wh_dentry))
++ goto out;
++
++ a.inode = src_dentry->d_inode;
++ a.hidden_dentry = au_h_dptr(dentry);
++ hidden_parent = a.hidden_dentry->d_parent;
++ a.hidden_dir = hidden_parent->d_inode;
++ IMustLock(a.hidden_dir);
++
++ err = 0;
++ sb = dentry->d_sb;
++ a.dlgt = need_dlgt(sb);
++
++ //todo: minor optimize, their sb may be same while their bindex differs.
++ a.bsrc = dbstart(src_dentry);
++ a.bdst = dbstart(dentry);
++ hidden_src_dentry = au_h_dptr(src_dentry);
++ if (unlikely(!au_flag_test(sb, AuFlag_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
++ /* && hidden_src_dentry->d_sb != a.hidden_dentry->d_sb */)
++ err = cpup_before_link(src_dentry, dir, &a);
++ if (!err) {
++ hidden_src_dentry = au_h_dptr(src_dentry);
++ err = vfsub_link(hidden_src_dentry, a.hidden_dir,
++ a.hidden_dentry, a.dlgt);
++ //err = -1;
++ }
++ } else {
++ if (a.bdst < a.bsrc
++ /* && hidden_src_dentry->d_sb != a.hidden_dentry->d_sb */)
++ err = cpup_or_link(src_dentry, &a);
++ else {
++ hidden_src_dentry = au_h_dptr(src_dentry);
++ err = vfsub_link(hidden_src_dentry, a.hidden_dir,
++ a.hidden_dentry, a.dlgt);
++ //err = -1;
++ }
++ }
++ if (unlikely(err))
++ goto out_unlock;
++ if (wh_dentry) {
++ err = au_unlink_wh_dentry(a.hidden_dir, wh_dentry, dentry,
++ a.dlgt);
++ //err = -1;
++ if (unlikely(err))
++ goto out_revert;
++ }
++
++ dir->i_version++;
++ if (ibstart(dir) == dbstart(dentry))
++ au_cpup_attr_timesizes(dir);
++ if (!d_unhashed(a.hidden_dentry)
++ /* || hidden_old_inode->i_nlink <= nlink */
++ /* || SB_NFS(hidden_src_dentry->d_sb) */) {
++ dentry->d_inode = igrab(a.inode);
++ d_instantiate(dentry, a.inode);
++ a.inode->i_nlink++;
++ a.inode->i_ctime = dir->i_ctime;
++ } else
++ /* nfs case (< 2.6.15) */
++ d_drop(dentry);
++#if 0
++ au_debug_on();
++ DbgInode(a.inode);
++ au_debug_off();
++ {
++ aufs_bindex_t i;
++ for (i = ibstart(a.inode); i <= ibend(a.inode); i++) {
++ struct xino xino;
++ struct inode *hi;
++ hi = au_h_iptr_i(a.inode, i);
++ if (hi) {
++ xino_read(sb, i, hi->i_ino, &xino);
++ Dbg("hi%lu, i%lu\n", hi->i_ino, xino.ino);
++ }
++ }
++ }
++#endif
++ goto out_unlock; /* success */
++
++ out_revert:
++#if 0 // remove
++ if (d_unhashed(a.hidden_dentry)) {
++ /* hardlink on nfs (< 2.6.15) */
++ struct dentry *d;
++ const struct qstr *name = &a.hidden_dentry->d_name;
++ DEBUG_ON(a.hidden_dentry->d_parent->d_inode != a.hidden_dir);
++ // do not superio.
++ d = lkup_one(name->name, a.hidden_dentry->d_parent, name->len,
++ au_nfsmnt(sb, a.bdst)??, need_dlgt(sb));
++ rerr = PTR_ERR(d);
++ if (IS_ERR(d))
++ goto out_rerr;
++ dput(a.hidden_dentry);
++ a.hidden_dentry = d;
++ DEBUG_ON(!d->d_inode);
++ }
++#endif
++ rerr = vfsub_unlink(a.hidden_dir, a.hidden_dentry, a.dlgt);
++ //rerr = -1;
++ if (!rerr)
++ goto out_dt;
++// out_rerr:
++ IOErr("%.*s reverting failed(%d, %d)\n", DLNPair(dentry), err, rerr);
++ err = -EIO;
++ out_dt:
++ d_drop(dentry);
++ dtime_revert(&dt, !CPUP_LOCKED_GHDIR);
++ out_unlock:
++ hdir_unlock(a.hidden_dir, dir, a.bdst);
++ dput(wh_dentry);
++ out:
++ if (unlikely(err)) {
++ au_update_dbstart(dentry);
++ d_drop(dentry);
++ }
++ di_write_unlock(a.parent);
++ aufs_read_and_write_unlock2(dentry, src_dentry);
++ TraceErr(err);
++ return err;
++}
++
++int aufs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
++{
++ int err, rerr, diropq, dlgt;
++ struct dentry *hidden_dentry, *hidden_parent, *wh_dentry, *parent,
++ *opq_dentry;
++ struct inode *hidden_dir, *hidden_inode;
++ struct dtime dt;
++ aufs_bindex_t bindex;
++ struct super_block *sb;
++
++ LKTRTrace("i%lu, %.*s, mode 0%o\n", dir->i_ino, DLNPair(dentry), mode);
++ IMustLock(dir);
++
++ aufs_read_lock(dentry, AUFS_D_WLOCK);
++ parent = dentry->d_parent;
++ di_write_lock_parent(parent);
++ wh_dentry = lock_hdir_lkup_wh(dentry, &dt, /*src_dentry*/NULL,
++ /*do_lock_srcdir*/0);
++ //wh_dentry = ERR_PTR(-1);
++ err = PTR_ERR(wh_dentry);
++ if (IS_ERR(wh_dentry))
++ goto out;
++
++ sb = dentry->d_sb;
++ bindex = dbstart(dentry);
++ hidden_dentry = au_h_dptr(dentry);
++ hidden_parent = hidden_dentry->d_parent;
++ hidden_dir = hidden_parent->d_inode;
++ IMustLock(hidden_dir);
++ dlgt = need_dlgt(sb);
++
++ err = vfsub_mkdir(hidden_dir, hidden_dentry, mode, dlgt);
++ //err = -1;
++ if (unlikely(err))
++ goto out_unlock;
++ hidden_inode = hidden_dentry->d_inode;
++
++ /* make the dir opaque */
++ diropq = 0;
++ if (unlikely(wh_dentry || au_flag_test(sb, AuFlag_ALWAYS_DIROPQ))) {
++ hi_lock_child(hidden_inode);
++ opq_dentry = create_diropq(dentry, bindex, dlgt);
++ //opq_dentry = ERR_PTR(-1);
++ i_unlock(hidden_inode);
++ err = PTR_ERR(opq_dentry);
++ if (IS_ERR(opq_dentry))
++ goto out_dir;
++ dput(opq_dentry);
++ diropq = 1;
++ }
++
++ err = epilog(wh_dentry, dentry);
++ //err = -1;
++ if (!err) {
++ dir->i_nlink++;
++ goto out_unlock; /* success */
++ }
++
++ /* revert */
++ if (unlikely(diropq)) {
++ LKTRLabel(revert opq);
++ hi_lock_child(hidden_inode);
++ rerr = remove_diropq(dentry, bindex, dlgt);
++ //rerr = -1;
++ i_unlock(hidden_inode);
++ if (rerr) {
++ IOErr("%.*s reverting diropq failed(%d, %d)\n",
++ DLNPair(dentry), err, rerr);
++ err = -EIO;
++ }
++ }
++
++ out_dir:
++ LKTRLabel(revert dir);
++ rerr = vfsub_rmdir(hidden_dir, hidden_dentry, dlgt);
++ //rerr = -1;
++ if (rerr) {
++ IOErr("%.*s reverting dir failed(%d, %d)\n",
++ DLNPair(dentry), err, rerr);
++ err = -EIO;
++ }
++ d_drop(dentry);
++ dtime_revert(&dt, /*fake flag*/CPUP_LOCKED_GHDIR);
++ out_unlock:
++ hdir_unlock(hidden_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, AUFS_D_WLOCK);
++ TraceErr(err);
++ return err;
++}
+diff --git a/fs/aufs/i_op_del.c b/fs/aufs/i_op_del.c
+new file mode 100755
+index 0000000..f29b204
+--- /dev/null
++++ b/fs/aufs/i_op_del.c
+@@ -0,0 +1,414 @@
++/*
++ * 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: i_op_del.c,v 1.35 2007/05/14 03:41:52 sfjro Exp $ */
++
++#include "aufs.h"
++
++/* returns,
++ * 0: wh is unnecessary
++ * plus: wh is necessary
++ * minus: error
++ */
++int 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 *hidden_dentry;
++ struct super_block *sb;
++
++ LKTRTrace("%.*s, isdir %d, *bcpup %d, locked %p\n",
++ DLNPair(dentry), isdir, *bcpup, locked);
++ sb = dentry->d_sb;
++
++ bstart = dbstart(dentry);
++ LKTRTrace("bcpup %d, bstart %d\n", *bcpup, bstart);
++ hidden_dentry = au_h_dptr(dentry);
++ if (*bcpup < 0) {
++ *bcpup = bstart;
++ if (test_ro(sb, bstart, dentry->d_inode)) {
++ *bcpup = err = find_rw_parent_br(dentry, bstart);
++ //*bcpup = err = find_rw_br(sb, bstart);
++ //err = -1;
++ if (unlikely(err < 0))
++ goto out;
++ }
++ } else {
++ /* braces are added to stop a warning */
++ DEBUG_ON(bstart < *bcpup
++ || test_ro(sb, *bcpup, dentry->d_inode));
++ }
++ LKTRTrace("bcpup %d, bstart %d\n", *bcpup, bstart);
++
++ if (*bcpup != bstart) {
++ err = 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 = dbend(dentry);
++ if (isdir) {
++ bdiropq = dbdiropq(dentry);
++ set_dbdiropq(dentry, -1);
++ }
++ err = need_wh = lkup_dentry(dentry, bstart + 1, /*type*/0);
++ //err = -1;
++ if (isdir)
++ set_dbdiropq(dentry, bdiropq);
++ if (unlikely(err < 0))
++ goto out;
++ new_bend = dbend(dentry);
++ if (!need_wh && old_bend != new_bend) {
++ set_h_dptr(dentry, new_bend, NULL);
++ set_dbend(dentry, old_bend);
++#if 0
++ } else if (!au_h_dptr_i(dentry, new_bend)->d_inode) {
++ LKTRTrace("negative\n");
++ set_h_dptr(dentry, new_bend, NULL);
++ set_dbend(dentry, old_bend);
++ need_wh = 0;
++#endif
++ }
++ }
++ LKTRTrace("need_wh %d\n", need_wh);
++ err = need_wh;
++
++ out:
++ TraceErr(err);
++ return err;
++}
++
++static struct dentry *
++lock_hdir_create_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup,
++ struct dtime *dt)
++{
++ struct dentry *wh_dentry;
++ int err, need_wh;
++ struct dentry *hidden_parent, *parent;
++ struct inode *dir, *h_dir;
++ struct lkup_args lkup;
++
++ LKTRTrace("%.*s, isdir %d\n", DLNPair(dentry), isdir);
++
++ err = need_wh = wr_dir_need_wh(dentry, isdir, bcpup, NULL);
++ //err = -1;
++ wh_dentry = ERR_PTR(err);
++ if (unlikely(err < 0))
++ goto out;
++
++ parent = dentry->d_parent;
++ dir = parent->d_inode;
++ hidden_parent = au_h_dptr_i(parent, *bcpup);
++ h_dir = hidden_parent->d_inode;
++ hdir_lock(h_dir, dir, *bcpup);
++ dtime_store(dt, parent, hidden_parent);
++ if (!need_wh)
++ return NULL; /* success, no need to create whiteout */
++
++ lkup.nfsmnt = au_nfsmnt(dentry->d_sb, *bcpup);
++ lkup.dlgt = need_dlgt(dentry->d_sb);
++ wh_dentry = simple_create_wh(dentry, *bcpup, hidden_parent, &lkup);
++ //wh_dentry = ERR_PTR(-1);
++ if (!IS_ERR(wh_dentry))
++ goto out; /* success */
++ /* returns with the parent is locked and wh_dentry is DGETed */
++
++ hdir_unlock(h_dir, dir, *bcpup);
++
++ out:
++ TraceErrPtr(wh_dentry);
++ return wh_dentry;
++}
++
++static int renwh_and_rmdir(struct dentry *dentry, aufs_bindex_t bindex,
++ struct aufs_nhash *whlist, struct inode *dir)
++{
++ int rmdir_later, err;
++ struct dentry *hidden_dentry;
++
++ LKTRTrace("%.*s, b%d\n", DLNPair(dentry), bindex);
++
++ err = rename_whtmp(dentry, bindex);
++ //err = -1;
++#if 0
++ //todo: bug
++ if (unlikely(err)) {
++ au_direval_inc(dentry->d_parent);
++ return err;
++ }
++#endif
++
++ hidden_dentry = au_h_dptr_i(dentry, bindex);
++ if (!au_is_nfs(hidden_dentry->d_sb)) {
++ const int dirwh = stosi(dentry->d_sb)->si_dirwh;
++ rmdir_later = (dirwh <= 1);
++ if (!rmdir_later)
++ rmdir_later = is_longer_wh(whlist, bindex, dirwh);
++ if (rmdir_later)
++ return rmdir_later;
++ }
++
++ err = rmdir_whtmp(hidden_dentry, whlist, bindex, dir, dentry->d_inode);
++ //err = -1;
++ if (unlikely(err)) {
++ IOErr("rmdir %.*s, b%d failed, %d. ignored\n",
++ DLNPair(hidden_dentry), bindex, err);
++ err = 0;
++ }
++ TraceErr(err);
++ return err;
++}
++
++static void epilog(struct inode *dir, struct dentry *dentry,
++ aufs_bindex_t bindex)
++{
++ d_drop(dentry);
++ dentry->d_inode->i_ctime = dir->i_ctime;
++ if (atomic_read(&dentry->d_count) == 1) {
++ set_h_dptr(dentry, dbstart(dentry), NULL);
++ au_update_dbstart(dentry);
++ }
++ if (ibstart(dir) == bindex)
++ au_cpup_attr_timesizes(dir);
++ dir->i_version++;
++}
++
++static int do_revert(int err, struct dentry *wh_dentry, struct dentry *dentry,
++ aufs_bindex_t bwh, struct dtime *dt, int dlgt)
++{
++ int rerr;
++
++ rerr = au_unlink_wh_dentry(wh_dentry->d_parent->d_inode, wh_dentry,
++ dentry, dlgt);
++ //rerr = -1;
++ if (!rerr) {
++ set_dbwh(dentry, bwh);
++ dtime_revert(dt, !CPUP_LOCKED_GHDIR);
++ return 0;
++ }
++
++ IOErr("%.*s reverting whiteout failed(%d, %d)\n",
++ DLNPair(dentry), err, rerr);
++ return -EIO;
++}
++
++/* ---------------------------------------------------------------------- */
++
++int aufs_unlink(struct inode *dir, struct dentry *dentry)
++{
++ int err, dlgt;
++ struct inode *inode, *hidden_dir;
++ struct dentry *parent, *wh_dentry, *hidden_dentry, *hidden_parent;
++ struct dtime dt;
++ aufs_bindex_t bwh, bindex, bstart;
++ struct super_block *sb;
++
++ LKTRTrace("i%lu, %.*s\n", dir->i_ino, DLNPair(dentry));
++ IMustLock(dir);
++ inode = dentry->d_inode;
++ if (unlikely(!inode))
++ return -ENOENT; // possible?
++ IMustLock(inode);
++
++ aufs_read_lock(dentry, AUFS_D_WLOCK);
++ parent = dentry->d_parent;
++ di_write_lock_parent(parent);
++
++ bstart = dbstart(dentry);
++ bwh = 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 = need_dlgt(sb);
++ hidden_dentry = au_h_dptr(dentry);
++ dget(hidden_dentry);
++ hidden_parent = hidden_dentry->d_parent;
++ hidden_dir = hidden_parent->d_inode;
++
++ if (bindex == bstart) {
++ err = vfsub_unlink(hidden_dir, hidden_dentry, dlgt);
++ //err = -1;
++ } else {
++ DEBUG_ON(!wh_dentry);
++ hidden_parent = wh_dentry->d_parent;
++ DEBUG_ON(hidden_parent != au_h_dptr_i(parent, bindex));
++ hidden_dir = hidden_parent->d_inode;
++ IMustLock(hidden_dir);
++ err = 0;
++ }
++
++ if (!err) {
++ inode->i_nlink--;
++ epilog(dir, dentry, bindex);
++#if 0
++ xino_write0(sb, bstart, hidden_dentry->d_inode->i_ino);
++ /* ignore this error */
++#endif
++ goto out_unlock; /* success */
++ }
++
++ /* revert */
++ if (wh_dentry) {
++ int rerr;
++ rerr = do_revert(err, wh_dentry, dentry, bwh, &dt, dlgt);
++ if (rerr)
++ err = rerr;
++ }
++
++ out_unlock:
++ hdir_unlock(hidden_dir, dir, bindex);
++ dput(wh_dentry);
++ dput(hidden_dentry);
++ out:
++ di_write_unlock(parent);
++ aufs_read_unlock(dentry, AUFS_D_WLOCK);
++ TraceErr(err);
++ return err;
++}
++
++int aufs_rmdir(struct inode *dir, struct dentry *dentry)
++{
++ int err, rmdir_later;
++ struct inode *inode, *hidden_dir;
++ struct dentry *parent, *wh_dentry, *hidden_dentry, *hidden_parent;
++ struct dtime dt;
++ aufs_bindex_t bwh, bindex, bstart;
++ struct rmdir_whtmp_arg *arg;
++ struct aufs_nhash *whlist;
++ struct super_block *sb;
++
++ LKTRTrace("i%lu, %.*s\n", dir->i_ino, DLNPair(dentry));
++ IMustLock(dir);
++ inode = dentry->d_inode;
++ if (unlikely(!inode))
++ return -ENOENT; // possible?
++ IMustLock(inode);
++
++ whlist = nhash_new(GFP_KERNEL);
++ err = PTR_ERR(whlist);
++ if (IS_ERR(whlist))
++ goto out;
++
++ err = -ENOMEM;
++ arg = kmalloc(sizeof(*arg), GFP_KERNEL);
++ //arg = NULL;
++ if (unlikely(!arg))
++ goto out_whlist;
++
++ aufs_read_lock(dentry, AUFS_D_WLOCK);
++ parent = dentry->d_parent;
++ di_write_lock_parent(parent);
++ err = test_empty(dentry, whlist);
++ //err = -1;
++ if (unlikely(err))
++ goto out_arg;
++
++ bstart = dbstart(dentry);
++ bwh = 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_arg;
++
++ hidden_dentry = au_h_dptr(dentry);
++ dget(hidden_dentry);
++ hidden_parent = hidden_dentry->d_parent;
++ hidden_dir = hidden_parent->d_inode;
++
++ rmdir_later = 0;
++ if (bindex == bstart) {
++ IMustLock(hidden_dir);
++ err = renwh_and_rmdir(dentry, bstart, whlist, dir);
++ //err = -1;
++ if (err > 0) {
++ rmdir_later = err;
++ err = 0;
++ }
++ } else {
++ DEBUG_ON(!wh_dentry);
++ hidden_parent = wh_dentry->d_parent;
++ DEBUG_ON(hidden_parent != au_h_dptr_i(parent, bindex));
++ hidden_dir = hidden_parent->d_inode;
++ IMustLock(hidden_dir);
++ err = 0;
++ }
++
++ sb = dentry->d_sb;
++ if (!err) {
++ //aufs_bindex_t bi, bend;
++
++ au_reset_hinotify(inode, /*flags*/0);
++ inode->i_nlink = 0;
++ set_dbdiropq(dentry, -1);
++ epilog(dir, dentry, bindex);
++
++ if (rmdir_later) {
++ kick_rmdir_whtmp(hidden_dentry, whlist, bstart, dir,
++ inode, arg);
++ arg = NULL;
++ }
++
++#if 0
++ bend = dbend(dentry);
++ for (bi = bstart; bi <= bend; bi++) {
++ struct dentry *hd;
++ hd = au_h_dptr_i(dentry, bi);
++ if (hd && hd->d_inode)
++ xino_write0(sb, bi, hd->d_inode->i_ino);
++ /* ignore this error */
++ }
++#endif
++
++ goto out_unlock; /* success */
++ }
++
++ /* revert */
++ LKTRLabel(revert);
++ if (wh_dentry) {
++ int rerr;
++ rerr = do_revert(err, wh_dentry, dentry, bwh, &dt,
++ need_dlgt(sb));
++ if (rerr)
++ err = rerr;
++ }
++
++ out_unlock:
++ hdir_unlock(hidden_dir, dir, bindex);
++ dput(wh_dentry);
++ dput(hidden_dentry);
++ out_arg:
++ di_write_unlock(parent);
++ aufs_read_unlock(dentry, AUFS_D_WLOCK);
++ kfree(arg);
++ out_whlist:
++ nhash_del(whlist);
++ out:
++ TraceErr(err);
++ return err;
++}
+diff --git a/fs/aufs/i_op_ren.c b/fs/aufs/i_op_ren.c
+new file mode 100755
+index 0000000..08137f9
+--- /dev/null
++++ b/fs/aufs/i_op_ren.c
+@@ -0,0 +1,637 @@
++/*
++ * 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: i_op_ren.c,v 1.39 2007/05/14 03:41:52 sfjro Exp $ */
++
++//#include <linux/fs.h>
++//#include <linux/namei.h>
++#include "aufs.h"
++
++enum {SRC, DST};
++struct rename_args {
++ struct dentry *hidden_dentry[2], *parent[2], *hidden_parent[2];
++ struct aufs_nhash whlist;
++ aufs_bindex_t btgt, bstart[2];
++ struct super_block *sb;
++
++ unsigned int isdir:1;
++ unsigned int issamedir:1;
++ unsigned int whsrc:1;
++ unsigned int whdst:1;
++ unsigned int dlgt:1;
++} __attribute__((aligned(sizeof(long))));
++
++static 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 rmdir_whtmp_arg *tharg;
++ struct dentry *wh_dentry[2], *hidden_dst, *hg_parent;
++ struct inode *hidden_dir[2];
++ aufs_bindex_t bindex, bend;
++ unsigned int flags;
++ struct lkup_args lkup = {.dlgt = a->dlgt};
++
++ LKTRTrace("%.*s/%.*s, %.*s/%.*s, "
++ "hd{%p, %p}, hp{%p, %p}, wh %p, btgt %d, bstart{%d, %d}, "
++ "flags{%d, %d, %d, %d}\n",
++ DLNPair(a->parent[SRC]), DLNPair(src_dentry),
++ DLNPair(a->parent[DST]), DLNPair(dentry),
++ a->hidden_dentry[SRC], a->hidden_dentry[DST],
++ a->hidden_parent[SRC], a->hidden_parent[DST],
++ &a->whlist, a->btgt,
++ a->bstart[SRC], a->bstart[DST],
++ a->isdir, a->issamedir, a->whsrc, a->whdst);
++ hidden_dir[SRC] = a->hidden_parent[SRC]->d_inode;
++ hidden_dir[DST] = a->hidden_parent[DST]->d_inode;
++ IMustLock(hidden_dir[SRC]);
++ IMustLock(hidden_dir[DST]);
++
++ /* prepare workqueue arg */
++ hidden_dst = NULL;
++ tharg = NULL;
++ if (a->isdir && a->hidden_dentry[DST]->d_inode) {
++ err = -ENOMEM;
++ tharg = kmalloc(sizeof(*tharg), GFP_KERNEL);
++ //tharg = NULL;
++ if (unlikely(!tharg))
++ goto out;
++ hidden_dst = dget(a->hidden_dentry[DST]);
++ }
++
++ wh_dentry[SRC] = wh_dentry[DST] = NULL;
++ lkup.nfsmnt = au_nfsmnt(a->sb, a->btgt);
++ /* create whiteout for src_dentry */
++ if (a->whsrc) {
++ wh_dentry[SRC] = simple_create_wh(src_dentry, a->btgt,
++ a->hidden_parent[SRC], &lkup);
++ //wh_dentry[SRC] = ERR_PTR(-1);
++ err = PTR_ERR(wh_dentry[SRC]);
++ if (IS_ERR(wh_dentry[SRC]))
++ goto out_tharg;
++ }
++
++ /* lookup whiteout for dentry */
++ if (a->whdst) {
++ struct dentry *d;
++ d = lkup_wh(a->hidden_parent[DST], &dentry->d_name, &lkup);
++ //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 (tharg) {
++ err = rename_whtmp(dentry, a->btgt);
++ //err = -1;
++ if (unlikely(err))
++ goto out_whdst;
++ set_h_dptr(dentry, a->btgt, NULL);
++ err = lkup_neg(dentry, a->btgt);
++ //err = -1;
++ if (unlikely(err))
++ goto out_whtmp;
++ a->hidden_dentry[DST] = au_h_dptr_i(dentry, a->btgt);
++ }
++
++ /* cpup src */
++ if (a->hidden_dentry[DST]->d_inode && a->bstart[SRC] != a->btgt) {
++ flags = au_flags_cpup(!CPUP_DTIME, a->parent[SRC]);
++ hg_parent = a->hidden_parent[SRC]->d_parent;
++ if (!(flags & CPUP_LOCKED_GHDIR)
++ && hg_parent == a->hidden_parent[DST])
++ flags |= CPUP_LOCKED_GHDIR;
++
++ hi_lock_child(a->hidden_dentry[SRC]->d_inode);
++ err = sio_cpup_simple(src_dentry, a->btgt, -1, flags);
++ //err = -1; // untested dir
++ i_unlock(a->hidden_dentry[SRC]->d_inode);
++ if (unlikely(err))
++ goto out_whtmp;
++ }
++
++#if 0
++ /* clear the target ino in xino */
++ LKTRTrace("dir %d, dst inode %p\n", a->isdir, a->hidden_dentry[DST]->d_inode);
++ if (a->isdir && a->hidden_dentry[DST]->d_inode) {
++ Dbg("here\n");
++ err = xino_write(a->sb, a->btgt,
++ a->hidden_dentry[DST]->d_inode->i_ino, 0);
++ if (unlikely(err))
++ goto out_whtmp;
++ }
++#endif
++
++ /* rename by vfs_rename or cpup */
++ need_diropq = a->isdir
++ && (wh_dentry[DST]
++ || dbdiropq(dentry) == a->btgt
++ || au_flag_test(a->sb, AuFlag_ALWAYS_DIROPQ));
++ bycpup = 0;
++ if (dbstart(src_dentry) == a->btgt) {
++ if (need_diropq && dbdiropq(src_dentry) == a->btgt)
++ need_diropq = 0;
++ err = vfsub_rename(hidden_dir[SRC], au_h_dptr(src_dentry),
++ hidden_dir[DST], a->hidden_dentry[DST],
++ a->dlgt);
++ //err = -1;
++ } else {
++ bycpup = 1;
++ flags = au_flags_cpup(!CPUP_DTIME, a->parent[DST]);
++ hg_parent = a->hidden_parent[DST]->d_parent;
++ if (!(flags & CPUP_LOCKED_GHDIR)
++ && hg_parent == a->hidden_parent[SRC])
++ flags |= CPUP_LOCKED_GHDIR;
++
++ hi_lock_child(a->hidden_dentry[SRC]->d_inode);
++ set_dbstart(src_dentry, a->btgt);
++ set_h_dptr(src_dentry, a->btgt, dget(a->hidden_dentry[DST]));
++ //DbgDentry(src_dentry);
++ //DbgInode(src_dentry->d_inode);
++ err = sio_cpup_single(src_dentry, a->btgt, a->bstart[SRC], -1,
++ flags);
++ //err = -1; // untested dir
++ if (unlikely(err)) {
++ set_h_dptr(src_dentry, a->btgt, NULL);
++ set_dbstart(src_dentry, a->bstart[SRC]);
++ }
++ i_unlock(a->hidden_dentry[SRC]->d_inode);
++ }
++ if (unlikely(err))
++ goto out_whtmp;
++
++ /* make dir opaque */
++ if (need_diropq) {
++ struct dentry *diropq;
++ struct inode *h_inode;
++
++ h_inode = au_h_dptr_i(src_dentry, a->btgt)->d_inode;
++ hdir_lock(h_inode, src_dentry->d_inode, a->btgt);
++ diropq = create_diropq(src_dentry, a->btgt, a->dlgt);
++ //diropq = ERR_PTR(-1);
++ hdir_unlock(h_inode, src_dentry->d_inode, a->btgt);
++ err = PTR_ERR(diropq);
++ if (IS_ERR(diropq))
++ goto out_rename;
++ dput(diropq);
++ }
++
++ /* remove whiteout for dentry */
++ if (wh_dentry[DST]) {
++ err = au_unlink_wh_dentry(hidden_dir[DST], wh_dentry[DST],
++ dentry, a->dlgt);
++ //err = -1;
++ if (unlikely(err))
++ goto out_diropq;
++ }
++
++ /* remove whtmp */
++ if (tharg) {
++ if (au_is_nfs(hidden_dst->d_sb)
++ || !is_longer_wh(&a->whlist, a->btgt,
++ stosi(a->sb)->si_dirwh)) {
++ err = rmdir_whtmp(hidden_dst, &a->whlist, a->btgt, dir,
++ dentry->d_inode);
++ if (unlikely(err))
++ Warn("failed removing whtmp dir %.*s (%d), "
++ "ignored.\n", DLNPair(hidden_dst), err);
++ } else {
++ kick_rmdir_whtmp(hidden_dst, &a->whlist, a->btgt, dir,
++ dentry->d_inode, tharg);
++ dput(hidden_dst);
++ tharg = NULL;
++ }
++ }
++ err = 0;
++ goto out_success;
++
++#define RevertFailure(fmt, args...) do { \
++ IOErrWhck("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_i(src_dentry, a->btgt)->d_inode;
++ // i_lock simplly since inotify is not set to h_inode.
++ hi_lock_parent(h_inode);
++ //hdir_lock(h_inode, src_dentry->d_inode, a->btgt);
++ rerr = remove_diropq(src_dentry, a->btgt, a->dlgt);
++ //rerr = -1;
++ //hdir_unlock(h_inode, src_dentry->d_inode, a->btgt);
++ i_unlock(h_inode);
++ if (rerr)
++ RevertFailure("remove diropq %.*s",
++ DLNPair(src_dentry));
++ }
++ out_rename:
++ if (!bycpup) {
++ struct dentry *d;
++ struct qstr *name = &src_dentry->d_name;
++ d = lkup_one(name->name, a->hidden_parent[SRC], name->len,
++ &lkup);
++ //d = ERR_PTR(-1);
++ rerr = PTR_ERR(d);
++ if (IS_ERR(d)) {
++ RevertFailure("lkup_one %.*s", DLNPair(src_dentry));
++ goto out_whtmp;
++ }
++ DEBUG_ON(d->d_inode);
++ rerr = vfsub_rename
++ (hidden_dir[DST], au_h_dptr_i(src_dentry, a->btgt),
++ hidden_dir[SRC], d, a->dlgt);
++ //rerr = -1;
++ d_drop(d);
++ dput(d);
++ //set_h_dptr(src_dentry, a->btgt, NULL);
++ if (rerr)
++ RevertFailure("rename %.*s", DLNPair(src_dentry));
++ } else {
++ rerr = vfsub_unlink(hidden_dir[DST], a->hidden_dentry[DST],
++ a->dlgt);
++ //rerr = -1;
++ set_h_dptr(src_dentry, a->btgt, NULL);
++ set_dbstart(src_dentry, a->bstart[SRC]);
++ if (rerr)
++ RevertFailure("unlink %.*s",
++ DLNPair(a->hidden_dentry[DST]));
++ }
++ out_whtmp:
++ if (tharg) {
++ struct dentry *d;
++ struct qstr *name = &dentry->d_name;
++ LKTRLabel(here);
++ d = lkup_one(name->name, a->hidden_parent[DST], name->len,
++ &lkup);
++ //d = ERR_PTR(-1);
++ rerr = PTR_ERR(d);
++ if (IS_ERR(d)) {
++ RevertFailure("lookup %.*s", LNPair(name));
++ goto out_whdst;
++ }
++ if (d->d_inode) {
++ d_drop(d);
++ dput(d);
++ goto out_whdst;
++ }
++ DEBUG_ON(d->d_inode);
++ rerr = vfsub_rename(hidden_dir[DST], hidden_dst,
++ hidden_dir[DST], d, a->dlgt);
++ //rerr = -1;
++ d_drop(d);
++ dput(d);
++ if (rerr) {
++ RevertFailure("rename %.*s", DLNPair(hidden_dst));
++ goto out_whdst;
++ }
++ set_h_dptr(dentry, a->btgt, NULL);
++ set_h_dptr(dentry, a->btgt, dget(hidden_dst));
++ }
++ out_whdst:
++ dput(wh_dentry[DST]);
++ wh_dentry[DST] = NULL;
++ out_whsrc:
++ if (wh_dentry[SRC]) {
++ LKTRLabel(here);
++ rerr = au_unlink_wh_dentry(hidden_dir[SRC], wh_dentry[SRC],
++ src_dentry, a->dlgt);
++ //rerr = -1;
++ if (rerr)
++ RevertFailure("unlink %.*s", DLNPair(wh_dentry[SRC]));
++ }
++#undef RevertFailure
++ d_drop(src_dentry);
++ bend = dbend(src_dentry);
++ for (bindex = dbstart(src_dentry); bindex <= bend; bindex++) {
++ struct dentry *hd;
++ hd = au_h_dptr_i(src_dentry, bindex);
++ if (hd)
++ d_drop(hd);
++ }
++ d_drop(dentry);
++ bend = dbend(dentry);
++ for (bindex = dbstart(dentry); bindex <= bend; bindex++) {
++ struct dentry *hd;
++ hd = au_h_dptr_i(dentry, bindex);
++ if (hd)
++ d_drop(hd);
++ }
++ au_update_dbstart(dentry);
++ if (tharg)
++ d_drop(hidden_dst);
++ out_success:
++ dput(wh_dentry[SRC]);
++ dput(wh_dentry[DST]);
++ out_tharg:
++ if (tharg) {
++ dput(hidden_dst);
++ kfree(tharg);
++ }
++ out:
++ TraceErr(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 aufs_nhash *whlist)
++{
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++
++ return 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", DLNPair(dentry));
++
++ bstart = dbstart(dentry);
++ if (bstart != btgt) {
++ struct aufs_nhash *whlist;
++
++ whlist = nhash_new(GFP_KERNEL);
++ err = PTR_ERR(whlist);
++ if (IS_ERR(whlist))
++ goto out;
++ err = test_empty(dentry, whlist);
++ nhash_del(whlist);
++ goto out;
++ }
++
++ if (bstart == dbtaildir(dentry))
++ return 0; /* success */
++
++ err = au_test_empty_lower(dentry);
++
++ out:
++ if (/* unlikely */(err == -ENOTEMPTY))
++ err = -EXDEV;
++ TraceErr(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;
++ aufs_bindex_t bend, bindex;
++ struct inode *inode, *dirs[2];
++ enum {PARENT, CHILD};
++ /* reduce stack space */
++ struct {
++ struct rename_args a;
++ struct dtime dt[2][2];
++ } *p;
++
++ LKTRTrace("i%lu, %.*s, i%lu, %.*s\n",
++ src_dir->i_ino, DLNPair(src_dentry),
++ dir->i_ino, DLNPair(dentry));
++ IMustLock(src_dir);
++ IMustLock(dir);
++ /* braces are added to stop a warning */
++ if (dentry->d_inode) {
++ IMustLock(dentry->d_inode);
++ }
++
++ err = -ENOMEM;
++ BUILD_BUG_ON(sizeof(*p) > PAGE_SIZE);
++ p = kmalloc(sizeof(*p), GFP_KERNEL);
++ if (unlikely(!p))
++ goto out;
++
++ err = -ENOTDIR;
++ p->a.sb = src_dentry->d_sb;
++ inode = src_dentry->d_inode;
++ p->a.isdir = !!S_ISDIR(inode->i_mode);
++ if (unlikely(p->a.isdir && dentry->d_inode
++ && !S_ISDIR(dentry->d_inode->i_mode)))
++ goto out_free;
++
++ aufs_read_and_write_lock2(dentry, src_dentry, p->a.isdir);
++ p->a.dlgt = !!need_dlgt(p->a.sb);
++ p->a.parent[SRC] = p->a.parent[DST] = dentry->d_parent;
++ p->a.issamedir = (src_dir == dir);
++ if (p->a.issamedir)
++ di_write_lock_parent(p->a.parent[DST]);
++ else {
++ p->a.parent[SRC] = src_dentry->d_parent;
++ di_write_lock2_parent(p->a.parent[SRC], p->a.parent[DST],
++ /*isdir*/1);
++ }
++
++ /* which branch we process */
++ p->a.bstart[DST] = dbstart(dentry);
++ p->a.btgt = err = wr_dir(dentry, 1, src_dentry, /*force_btgt*/-1,
++ /*do_lock_srcdir*/0);
++ if (unlikely(err < 0))
++ goto out_unlock;
++
++ /* are they available to be renamed */
++ err = 0;
++ nhash_init(&p->a.whlist);
++ if (p->a.isdir && dentry->d_inode) {
++ set_dbstart(dentry, p->a.bstart[DST]);
++ err = may_rename_dstdir(dentry, p->a.btgt, &p->a.whlist);
++ set_dbstart(dentry, p->a.btgt);
++ }
++ p->a.hidden_dentry[DST] = au_h_dptr(dentry);
++ if (unlikely(err))
++ goto out_unlock;
++ //todo: minor optimize, their sb may be same while their bindex differs.
++ p->a.bstart[SRC] = dbstart(src_dentry);
++ p->a.hidden_dentry[SRC] = au_h_dptr(src_dentry);
++ if (p->a.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 = wr_dir_need_wh(src_dentry, p->a.isdir, &p->a.btgt,
++ p->a.issamedir ? NULL : p->a.parent[DST]);
++ if (unlikely(err < 0))
++ goto out_children;
++ p->a.whsrc = !!err;
++ p->a.whdst = (p->a.bstart[DST] == p->a.btgt);
++ if (!p->a.whdst) {
++ err = cpup_dirs(dentry, p->a.btgt,
++ p->a.issamedir ? NULL : p->a.parent[SRC]);
++ if (unlikely(err))
++ goto out_children;
++ }
++
++ p->a.hidden_parent[SRC] = au_h_dptr_i(p->a.parent[SRC], p->a.btgt);
++ p->a.hidden_parent[DST] = au_h_dptr_i(p->a.parent[DST], p->a.btgt);
++ dirs[0] = src_dir;
++ dirs[1] = dir;
++ hdir_lock_rename(p->a.hidden_parent, dirs, p->a.btgt, p->a.issamedir);
++
++ /* store timestamps to be revertible */
++ dtime_store(p->dt[PARENT] + SRC, p->a.parent[SRC],
++ p->a.hidden_parent[SRC]);
++ if (!p->a.issamedir)
++ dtime_store(p->dt[PARENT] + DST, p->a.parent[DST],
++ p->a.hidden_parent[DST]);
++ do_dt_dstdir = 0;
++ if (p->a.isdir) {
++ dtime_store(p->dt[CHILD] + SRC, src_dentry,
++ p->a.hidden_dentry[SRC]);
++ if (p->a.hidden_dentry[DST]->d_inode) {
++ do_dt_dstdir = 1;
++ dtime_store(p->dt[CHILD] + DST, dentry,
++ p->a.hidden_dentry[DST]);
++ }
++ }
++
++ err = do_rename(src_dir, src_dentry, dir, dentry, &p->a);
++ if (unlikely(err))
++ goto out_dt;
++ hdir_unlock_rename(p->a.hidden_parent, dirs, p->a.btgt, p->a.issamedir);
++
++ /* update dir attributes */
++ dir->i_version++;
++ if (p->a.isdir)
++ au_cpup_attr_nlink(dir);
++ if (ibstart(dir) == p->a.btgt)
++ au_cpup_attr_timesizes(dir);
++
++ if (!p->a.issamedir) {
++ src_dir->i_version++;
++ if (p->a.isdir)
++ au_cpup_attr_nlink(src_dir);
++ if (ibstart(src_dir) == p->a.btgt)
++ au_cpup_attr_timesizes(src_dir);
++ }
++
++ // is this updating defined in POSIX?
++ if (unlikely(p->a.isdir)) {
++ //i_lock(inode);
++ au_cpup_attr_timesizes(inode);
++ //i_unlock(inode);
++ }
++
++#if 0
++ d_drop(src_dentry);
++#else
++ /* dput/iput all lower dentries */
++ set_dbwh(src_dentry, -1);
++ bend = dbend(src_dentry);
++ for (bindex = p->a.btgt + 1; bindex <= bend; bindex++) {
++ struct dentry *hd;
++ hd = au_h_dptr_i(src_dentry, bindex);
++ if (hd)
++ set_h_dptr(src_dentry, bindex, NULL);
++ }
++ set_dbend(src_dentry, p->a.btgt);
++
++ bend = ibend(inode);
++ for (bindex = p->a.btgt + 1; bindex <= bend; bindex++) {
++ struct inode *hi;
++ hi = au_h_iptr_i(inode, bindex);
++ if (hi)
++ set_h_iptr(inode, bindex, NULL, 0);
++ }
++ set_ibend(inode, p->a.btgt);
++#endif
++
++#if 0
++ //au_debug_on();
++ //DbgDentry(dentry);
++ //DbgInode(dentry->d_inode);
++ //au_debug_off();
++ inode = dentry->d_inode;
++ if (inode) {
++ aufs_bindex_t bindex, bend;
++ struct dentry *hd;
++ bend = dbend(dentry);
++ for (bindex = dbstart(dentry); bindex <= bend; bindex++) {
++ hd = au_h_dptr_i(dentry, bindex);
++ if (hd && hd->d_inode)
++ xino_write0(p->a.sb, bindex, hd->d_inode->i_ino);
++ /* ignore this error */
++ }
++ }
++#endif
++
++ goto out_children; /* success */
++
++ out_dt:
++ dtime_revert(p->dt[PARENT] + SRC,
++ p->a.hidden_parent[SRC]->d_parent
++ == p->a.hidden_parent[DST]);
++ if (!p->a.issamedir)
++ dtime_revert(p->dt[PARENT] + DST,
++ p->a.hidden_parent[DST]->d_parent
++ == p->a.hidden_parent[SRC]);
++ if (p->a.isdir && err != -EIO) {
++ struct dentry *hd;
++
++ hd = p->dt[CHILD][SRC].dt_h_dentry;
++ hi_lock_child(hd->d_inode);
++ dtime_revert(p->dt[CHILD] + SRC, 1);
++ i_unlock(hd->d_inode);
++ if (do_dt_dstdir) {
++ hd = p->dt[CHILD][DST].dt_h_dentry;
++ hi_lock_child(hd->d_inode);
++ dtime_revert(p->dt[CHILD] + DST, 1);
++ i_unlock(hd->d_inode);
++ }
++ }
++ hdir_unlock_rename(p->a.hidden_parent, dirs, p->a.btgt, p->a.issamedir);
++ out_children:
++ nhash_fin(&p->a.whlist);
++ out_unlock:
++ //if (unlikely(err /* && p->a.isdir */)) {
++ if (unlikely(err && p->a.isdir)) {
++ au_update_dbstart(dentry);
++ d_drop(dentry);
++ }
++ if (p->a.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:
++ TraceErr(err);
++ return err;
++}
+diff --git a/fs/aufs/iinfo.c b/fs/aufs/iinfo.c
+new file mode 100755
+index 0000000..9efbd38
+--- /dev/null
++++ b/fs/aufs/iinfo.c
+@@ -0,0 +1,286 @@
++/*
++ * 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: iinfo.c,v 1.31 2007/05/14 03:41:52 sfjro Exp $ */
++
++//#include <linux/mm.h>
++#include "aufs.h"
++
++struct aufs_iinfo *itoii(struct inode *inode)
++{
++ struct aufs_iinfo *iinfo;
++
++ iinfo = &(container_of(inode, struct aufs_icntnr, vfs_inode)->iinfo);
++ /* bad_inode case */
++ if (unlikely(!iinfo->ii_hinode))
++ return NULL;
++ DEBUG_ON(!iinfo->ii_hinode
++ /* || stosi(inode->i_sb)->si_bend < iinfo->ii_bend */
++ || iinfo->ii_bend < iinfo->ii_bstart);
++ return iinfo;
++}
++
++aufs_bindex_t ibstart(struct inode *inode)
++{
++ IiMustAnyLock(inode);
++ return itoii(inode)->ii_bstart;
++}
++
++aufs_bindex_t ibend(struct inode *inode)
++{
++ IiMustAnyLock(inode);
++ return itoii(inode)->ii_bend;
++}
++
++struct aufs_vdir *ivdir(struct inode *inode)
++{
++ IiMustAnyLock(inode);
++ DEBUG_ON(!S_ISDIR(inode->i_mode));
++ return itoii(inode)->ii_vdir;
++}
++
++struct inode *au_h_iptr_i(struct inode *inode, aufs_bindex_t bindex)
++{
++ struct inode *hidden_inode;
++
++ IiMustAnyLock(inode);
++ DEBUG_ON(bindex < 0 || ibend(inode) < bindex);
++ hidden_inode = itoii(inode)->ii_hinode[0 + bindex].hi_inode;
++ DEBUG_ON(hidden_inode && atomic_read(&hidden_inode->i_count) <= 0);
++ return hidden_inode;
++}
++
++struct inode *au_h_iptr(struct inode *inode)
++{
++ return au_h_iptr_i(inode, ibstart(inode));
++}
++
++aufs_bindex_t itoid_index(struct inode *inode, aufs_bindex_t bindex)
++{
++ IiMustAnyLock(inode);
++ DEBUG_ON(bindex < 0
++ || ibend(inode) < bindex
++ || !itoii(inode)->ii_hinode[0 + bindex].hi_inode);
++ return itoii(inode)->ii_hinode[0 + bindex].hi_id;
++}
++
++// hard/soft set
++void set_ibstart(struct inode *inode, aufs_bindex_t bindex)
++{
++ struct aufs_iinfo *iinfo = itoii(inode);
++ struct inode *h_inode;
++
++ IiMustWriteLock(inode);
++ DEBUG_ON(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);
++}
++
++void set_ibend(struct inode *inode, aufs_bindex_t bindex)
++{
++ IiMustWriteLock(inode);
++ DEBUG_ON(sbend(inode->i_sb) < bindex
++ || bindex < ibstart(inode));
++ itoii(inode)->ii_bend = bindex;
++}
++
++void set_ivdir(struct inode *inode, struct aufs_vdir *vdir)
++{
++ IiMustWriteLock(inode);
++ DEBUG_ON(!S_ISDIR(inode->i_mode)
++ || (itoii(inode)->ii_vdir && vdir));
++ itoii(inode)->ii_vdir = vdir;
++}
++
++void aufs_hiput(struct aufs_hinode *hinode)
++{
++ if (unlikely(hinode->hi_notify))
++ do_free_hinotify(hinode);
++ if (hinode->hi_inode)
++ iput(hinode->hi_inode);
++}
++
++unsigned int au_hi_flags(struct inode *inode, int isdir)
++{
++ unsigned int flags;
++ struct super_block *sb = inode->i_sb;
++
++ flags = 0;
++ if (au_flag_test(sb, AuFlag_XINO))
++ flags = AUFS_HI_XINO;
++ if (unlikely(isdir && au_flag_test(sb, AuFlag_UDBA_INOTIFY)))
++ flags |= AUFS_HI_NOTIFY;
++ return flags;
++}
++
++void set_h_iptr(struct inode *inode, aufs_bindex_t bindex,
++ struct inode *h_inode, unsigned int flags)
++{
++ struct aufs_hinode *hinode;
++ struct inode *hi;
++ struct aufs_iinfo *iinfo = itoii(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;
++ DEBUG_ON(bindex < ibstart(inode) || ibend(inode) < bindex
++ || (h_inode && atomic_read(&h_inode->i_count) <= 0)
++ || (h_inode && hi));
++
++ if (hi)
++ aufs_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 = sbr_id(sb, bindex);
++ if (flags & AUFS_HI_XINO) {
++ struct xino xino = {
++ .ino = inode->i_ino,
++ //.h_gen = h_inode->i_generation
++ };
++ //WARN_ON(xino.h_gen == AuXino_INVALID_HGEN);
++ err = xino_write(sb, bindex, h_inode->i_ino, &xino);
++ if (unlikely(err)) {
++ IOErr1("failed xino_write() %d, force noxino\n",
++ err);
++ au_flag_clr(sb, AuFlag_XINO);
++ }
++ }
++ if (flags & AUFS_HI_NOTIFY) {
++ err = alloc_hinotify(hinode, inode, h_inode);
++ if (unlikely(err))
++ IOErr1("alloc_hinotify() %d\n", err);
++ else {
++ /* braces are added to stop a warning */
++ DEBUG_ON(!hinode->hi_notify);
++ }
++ }
++ }
++}
++
++void au_update_iigen(struct inode *inode)
++{
++ //IiMustWriteLock(inode);
++ DEBUG_ON(!inode->i_sb);
++ atomic_set(&itoii(inode)->ii_generation, au_sigen(inode->i_sb));
++}
++
++/* it may be called at remount time, too */
++void au_update_brange(struct inode *inode, int do_put_zero)
++{
++ struct aufs_iinfo *iinfo;
++
++ LKTRTrace("i%lu, %d\n", inode->i_ino, do_put_zero);
++ IiMustWriteLock(inode);
++
++ iinfo = itoii(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)
++ 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_bend = iinfo->ii_bstart = -1;
++ return;
++ }
++
++ iinfo->ii_bend++;
++ while (0 <= --iinfo->ii_bend)
++ if (iinfo->ii_hinode[0 + iinfo->ii_bend].hi_inode)
++ break;
++}
++
++/* ---------------------------------------------------------------------- */
++
++int au_iinfo_init(struct inode *inode)
++{
++ struct aufs_iinfo *iinfo;
++ struct super_block *sb;
++ int nbr, i;
++
++ sb = inode->i_sb;
++ DEBUG_ON(!sb);
++ iinfo = &(container_of(inode, struct aufs_icntnr, vfs_inode)->iinfo);
++ DEBUG_ON(iinfo->ii_hinode);
++ nbr = sbend(sb) + 1;
++ if (unlikely(!nbr))
++ nbr++;
++ 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));
++ rw_init_nolock(&iinfo->ii_rwsem);
++ iinfo->ii_bstart = -1;
++ iinfo->ii_bend = -1;
++ iinfo->ii_vdir = NULL;
++ return 0;
++ }
++ return -ENOMEM;
++}
++
++void au_iinfo_fin(struct inode *inode)
++{
++ struct aufs_iinfo *iinfo;
++
++ iinfo = itoii(inode);
++ /* bad_inode case */
++ if (unlikely(!iinfo))
++ return;
++
++ if (unlikely(iinfo->ii_vdir))
++ free_vdir(iinfo->ii_vdir);
++
++ if (iinfo->ii_bstart >= 0) {
++ aufs_bindex_t bend;
++ struct aufs_hinode *hi;
++ hi = iinfo->ii_hinode + iinfo->ii_bstart;
++ bend = iinfo->ii_bend;
++ while (iinfo->ii_bstart++ <= bend) {
++ if (hi->hi_inode)
++ aufs_hiput(hi);
++ hi++;
++ }
++ //iinfo->ii_bstart = iinfo->ii_bend = -1;
++ }
++
++ kfree(iinfo->ii_hinode);
++ //iinfo->ii_hinode = NULL;
++}
+diff --git a/fs/aufs/inode.c b/fs/aufs/inode.c
+new file mode 100755
+index 0000000..f18b5d8
+--- /dev/null
++++ b/fs/aufs/inode.c
+@@ -0,0 +1,339 @@
++/*
++ * 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: inode.c,v 1.22 2007/05/07 03:44:35 sfjro Exp $ */
++
++#include "aufs.h"
++
++int au_refresh_hinode(struct inode *inode, struct dentry *dentry)
++{
++ int err, new_sz, update, isdir;
++ struct inode *first;
++ struct aufs_hinode *p, *q, tmp;
++ struct super_block *sb;
++ struct aufs_iinfo *iinfo;
++ aufs_bindex_t bindex, bend, new_bindex;
++ unsigned int flags;
++
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++ IiMustWriteLock(inode);
++
++ err = -ENOMEM;
++ sb = dentry->d_sb;
++ bend = sbend(sb);
++ new_sz = sizeof(*iinfo->ii_hinode) * (bend + 1);
++ iinfo = itoii(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;
++ err = 0;
++ update = 0;
++ p = iinfo->ii_hinode + iinfo->ii_bstart;
++ first = p->hi_inode;
++ for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend;
++ bindex++, p++) {
++ if (unlikely(!p->hi_inode))
++ continue;
++
++ new_bindex = find_brindex(sb, p->hi_id);
++ if (new_bindex == bindex)
++ continue;
++ if (new_bindex < 0) {
++ update++;
++ aufs_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--;
++ }
++ }
++
++ isdir = S_ISDIR(inode->i_mode);
++ flags = au_hi_flags(inode, isdir);
++ bend = dbend(dentry);
++ for (bindex = dbstart(dentry); bindex <= bend; bindex++) {
++ struct inode *hi;
++ struct dentry *hd;
++
++ hd = au_h_dptr_i(dentry, bindex);
++ if (!hd || !hd->d_inode)
++ continue;
++
++ if (iinfo->ii_bstart <= bindex && bindex <= iinfo->ii_bend) {
++ hi = au_h_iptr_i(inode, bindex);
++ if (hi) {
++ if (hi == hd->d_inode)
++ continue;
++ //Dbg("here\n");
++ err = -ESTALE;
++ break;
++ }
++ }
++ if (bindex < iinfo->ii_bstart)
++ iinfo->ii_bstart = bindex;
++ if (iinfo->ii_bend < bindex)
++ iinfo->ii_bend = bindex;
++ set_h_iptr(inode, bindex, igrab(hd->d_inode), flags);
++ update++;
++ }
++
++ bend = iinfo->ii_bend;
++ p = iinfo->ii_hinode;
++ for (bindex = 0; bindex <= bend; bindex++, p++)
++ if (p->hi_inode) {
++ iinfo->ii_bstart = bindex;
++ break;
++ }
++ p = iinfo->ii_hinode + bend;
++ for (bindex = bend; bindex > iinfo->ii_bstart; bindex--, p--)
++ if (p->hi_inode) {
++ iinfo->ii_bend = bindex;
++ break;
++ }
++ DEBUG_ON(iinfo->ii_bstart > bend || iinfo->ii_bend < 0);
++
++ if (unlikely(err))
++ goto out;
++
++ if (1 || first != au_h_iptr(inode))
++ au_cpup_attr_all(inode);
++ if (update && isdir)
++ inode->i_version++;
++ au_update_iigen(inode);
++
++ out:
++ //au_debug_on();
++ TraceErr(err);
++ //au_debug_off();
++ return err;
++}
++
++static int set_inode(struct inode *inode, struct dentry *dentry)
++{
++ int err, isdir;
++ struct dentry *hidden_dentry;
++ struct inode *hidden_inode;
++ umode_t mode;
++ aufs_bindex_t bindex, bstart, btail;
++ struct aufs_iinfo *iinfo;
++ unsigned int flags;
++
++ LKTRTrace("i%lu, %.*s\n", inode->i_ino, DLNPair(dentry));
++ DEBUG_ON(!(inode->i_state & I_NEW));
++ IiMustWriteLock(inode);
++ hidden_dentry = au_h_dptr(dentry);
++ DEBUG_ON(!hidden_dentry);
++ hidden_inode = hidden_dentry->d_inode;
++ DEBUG_ON(!hidden_inode);
++
++ err = 0;
++ isdir = 0;
++ bstart = dbstart(dentry);
++ mode = hidden_inode->i_mode;
++ switch (mode & S_IFMT) {
++ case S_IFREG:
++ btail = dbtail(dentry);
++ break;
++ case S_IFDIR:
++ isdir = 1;
++ btail = dbtaildir(dentry);
++ inode->i_op = &aufs_dir_iop;
++ inode->i_fop = &aufs_dir_fop;
++ break;
++ case S_IFLNK:
++ btail = dbtail(dentry);
++ inode->i_op = &aufs_symlink_iop;
++ break;
++ case S_IFBLK:
++ case S_IFCHR:
++ case S_IFIFO:
++ case S_IFSOCK:
++ btail = dbtail(dentry);
++ init_special_inode(inode, mode, hidden_inode->i_rdev);
++ break;
++ default:
++ IOErr("Unknown file type 0%o\n", mode);
++ err = -EIO;
++ goto out;
++ }
++
++ flags = au_hi_flags(inode, isdir);
++ iinfo = itoii(inode);
++ iinfo->ii_bstart = bstart;
++ iinfo->ii_bend = btail;
++ for (bindex = bstart; bindex <= btail; bindex++) {
++ hidden_dentry = au_h_dptr_i(dentry, bindex);
++ if (!hidden_dentry)
++ continue;
++ DEBUG_ON(!hidden_dentry->d_inode);
++ set_h_iptr(inode, bindex, igrab(hidden_dentry->d_inode), flags);
++ }
++ au_cpup_attr_all(inode);
++
++ out:
++ TraceErr(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;
++ //const int udba = !au_flag_test(inode->i_sb, AuFlag_UDBA_NONE);
++
++ LKTRTrace("i%lu, %.*s\n", inode->i_ino, DLNPair(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;
++
++ h_dinode = au_h_dptr(dentry)->d_inode;
++ hi_lock_child(inode); // bad name, this is not a hidden inode.
++ ii_write_lock_new(inode);
++ bend = ibend(inode);
++ for (bindex = ibstart(inode); bindex <= bend; bindex++) {
++ h_inode = au_h_iptr_i(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;
++ }
++ }
++ i_unlock(inode);
++ if (unlikely(err))
++ ii_write_unlock(inode);
++
++ out:
++ TraceErr(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 xino xino;
++
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++ sb = dentry->d_sb;
++ h_dentry = au_h_dptr(dentry);
++ DEBUG_ON(!h_dentry);
++ h_inode = h_dentry->d_inode;
++ DEBUG_ON(!h_inode);
++
++ bstart = dbstart(dentry);
++ h_ino = h_inode->i_ino;
++ err = xino_read(sb, bstart, h_ino, &xino);
++ //err = -1;
++ inode = ERR_PTR(err);
++ if (unlikely(err))
++ goto out;
++ new_ino:
++ if (!xino.ino) {
++ xino.ino = xino_new_ino(sb);
++ if (!xino.ino) {
++ inode = ERR_PTR(-EIO);
++ goto out;
++ }
++ }
++
++ LKTRTrace("i%lu\n", xino.ino);
++ err = -ENOMEM;
++ inode = iget_locked(sb, xino.ino);
++ if (unlikely(!inode))
++ goto out;
++ err = PTR_ERR(inode);
++ if (IS_ERR(inode))
++ goto out;
++ err = -ENOMEM;
++ if (unlikely(is_bad_inode(inode)))
++ goto out_iput;
++
++ LKTRTrace("%lx, new %d\n", inode->i_state, !!(inode->i_state & I_NEW));
++ if (inode->i_state & I_NEW) {
++ sb->s_op->read_inode(inode);
++ if (!is_bad_inode(inode)) {
++ ii_write_lock_new(inode);
++ err = set_inode(inode, dentry);
++ //err = -1;
++ }
++ unlock_new_inode(inode);
++ if (!err)
++ goto out; /* success */
++ ii_write_unlock(inode);
++ goto out_iput;
++ } else {
++ err = reval_inode(inode, dentry, &match);
++ if (!err)
++ goto out; /* success */
++ else if (match)
++ goto out_iput;
++ }
++
++ Warn1("broken ino, b%d, %.*s/%.*s, hi%lu, i%lu. Try udba=inotify.\n",
++ bstart, DLNPair(dentry->d_parent), DLNPair(dentry), h_ino,
++ xino.ino);
++ xino.ino = 0;
++ err = xino_write0(sb, bstart, h_ino);
++ if (!err) {
++ iput(inode);
++ goto new_ino;
++ }
++
++ out_iput:
++ iput(inode);
++ inode = ERR_PTR(err);
++ out:
++ TraceErrPtr(inode);
++ return inode;
++}
+diff --git a/fs/aufs/inode.h b/fs/aufs/inode.h
+new file mode 100755
+index 0000000..b001ac3
+--- /dev/null
++++ b/fs/aufs/inode.h
+@@ -0,0 +1,377 @@
++/*
++ * 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: inode.h,v 1.32 2007/05/14 03:41:52 sfjro Exp $ */
++
++#ifndef __AUFS_INODE_H__
++#define __AUFS_INODE_H__
++
++#ifdef __KERNEL__
++
++#include <linux/fs.h>
++#include <linux/inotify.h>
++#include <linux/version.h>
++#include <linux/aufs_type.h>
++#include "misc.h"
++#include "vfsub.h"
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
++#else
++struct inotify_watch {};
++#endif
++
++/* ---------------------------------------------------------------------- */
++
++struct aufs_hinotify {
++ struct inotify_watch hin_watch;
++ struct inode *hin_aufs_inode; /* no get/put */
++};
++
++struct aufs_hinode {
++ struct inode *hi_inode;
++ aufs_bindex_t hi_id;
++ struct aufs_hinotify *hi_notify;
++};
++
++struct aufs_vdir;
++struct aufs_iinfo {
++ atomic_t ii_generation;
++ struct super_block *ii_hsb1; /* no get/put */
++
++ struct aufs_rwsem ii_rwsem;
++ aufs_bindex_t ii_bstart, ii_bend;
++ struct aufs_hinode *ii_hinode;
++ struct aufs_vdir *ii_vdir;
++};
++
++struct aufs_icntnr {
++ struct aufs_iinfo iinfo;
++ struct inode vfs_inode;
++};
++
++/* ---------------------------------------------------------------------- */
++
++/* inode.c */
++int au_refresh_hinode(struct inode *inode, struct dentry *dentry);
++struct inode *au_new_inode(struct dentry *dentry);
++
++/* i_op.c */
++extern struct inode_operations aufs_iop, aufs_symlink_iop, aufs_dir_iop;
++int wr_dir(struct dentry *dentry, int negative, struct dentry *src_dentry,
++ aufs_bindex_t force_btgt, int do_lock_srcdir);
++
++/* i_op_del.c */
++int wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup,
++ struct dentry *locked);
++
++/* iinfo.c */
++struct aufs_iinfo *itoii(struct inode *inode);
++aufs_bindex_t ibstart(struct inode *inode);
++aufs_bindex_t ibend(struct inode *inode);
++struct aufs_vdir *ivdir(struct inode *inode);
++struct inode *au_h_iptr_i(struct inode *inode, aufs_bindex_t bindex);
++struct inode *au_h_iptr(struct inode *inode);
++aufs_bindex_t itoid_index(struct inode *inode, aufs_bindex_t bindex);
++
++void set_ibstart(struct inode *inode, aufs_bindex_t bindex);
++void set_ibend(struct inode *inode, aufs_bindex_t bindex);
++void set_ivdir(struct inode *inode, struct aufs_vdir *vdir);
++void aufs_hiput(struct aufs_hinode *hinode);
++#define AUFS_HI_XINO 1
++#define AUFS_HI_NOTIFY 2
++unsigned int au_hi_flags(struct inode *inode, int isdir);
++void 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_list_plink(struct super_block *sb);
++#else
++static inline void au_list_plink(struct super_block *sb)
++{
++ /* nothing */
++}
++#endif
++int au_is_plinked(struct super_block *sb, struct inode *inode);
++struct dentry *lkup_plink(struct super_block *sb, aufs_bindex_t bindex,
++ struct inode *inode);
++void append_plink(struct super_block *sb, struct inode *inode,
++ struct dentry *h_dentry, aufs_bindex_t bindex);
++void au_put_plink(struct super_block *sb);
++void half_refresh_plink(struct super_block *sb, aufs_bindex_t br_id);
++
++/* ---------------------------------------------------------------------- */
++
++/* lock subclass for hidden inode */
++/* default MAX_LOCKDEP_SUBCLASSES(8) is not enough */
++// todo: reduce it by dcsub.
++enum {
++ AuLsc_Begin = I_MUTEX_QUOTA,
++ AuLsc_HI_GPARENT, /* setattr with inotify */
++ AuLsc_HI_PARENT, /* hidden inode, parent first */
++ AuLsc_HI_CHILD,
++ AuLsc_HI_PARENT2, /* copyup dirs */
++ AuLsc_HI_CHILD2,
++ AuLsc_End
++};
++
++/* simple abstraction */
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
++static inline void i_lock(struct inode *i)
++{
++ down(&i->i_sem);
++}
++
++static inline void i_unlock(struct inode *i)
++{
++ up(&i->i_sem);
++}
++
++static inline int i_trylock(struct inode *i)
++{
++ return down_trylock(&i->i_sem);
++}
++
++static inline void hi_lock(struct inode *i, unsigned int lsc)
++{
++ i_lock(i);
++}
++
++#define IMustLock(i) DEBUG_ON(!down_trylock(&(i)->i_sem))
++#else
++static inline void i_lock(struct inode *i)
++{
++ mutex_lock(&i->i_mutex);
++}
++
++static inline void i_unlock(struct inode *i)
++{
++ mutex_unlock(&i->i_mutex);
++}
++
++static inline int i_trylock(struct inode *i)
++{
++ return mutex_trylock(&i->i_mutex);
++}
++
++static inline void hi_lock(struct inode *i, unsigned int lsc)
++{
++ mutex_lock_nested(&i->i_mutex, lsc);
++}
++
++#define IMustLock(i) MtxMustLock(&(i)->i_mutex)
++#endif
++
++/*
++ * hi_lock_gparent, hi_lock_parent, hi_lock_parent2, hi_lock_child,
++ * hi_lock_child2, hi_lock_whplink
++ */
++#define LockFunc(name, lsc) \
++static inline void hi_lock_##name(struct inode *h_i) \
++{hi_lock(h_i, AuLsc_HI_##lsc);}
++
++LockFunc(gparent, GPARENT);
++LockFunc(parent, PARENT);
++LockFunc(parent2, PARENT2);
++LockFunc(child, CHILD);
++LockFunc(child2, CHILD2);
++LockFunc(whplink, CHILD2); /* sharing lock-subclass */
++
++#undef LockFunc
++
++/* ---------------------------------------------------------------------- */
++
++/* 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 !(itoii(inode)->ii_hsb1 == h_inode->i_sb
++ && inode->i_generation == h_inode->i_generation);
++}
++
++static inline int au_iigen(struct inode *inode)
++{
++ return atomic_read(&itoii(inode)->ii_generation);
++}
++
++#ifdef CONFIG_AUFS_HINOTIFY
++static inline void au_iigen_dec(struct inode *inode)
++{
++ //Dbg("i%lu\n", inode->i_ino);
++ atomic_dec(&itoii(inode)->ii_generation);
++}
++
++/* hinotify.c */
++int alloc_hinotify(struct aufs_hinode *hinode, struct inode *inode,
++ struct inode *h_inode);
++void do_free_hinotify(struct aufs_hinode *hinode);
++void do_hdir_lock(struct inode *h_dir, struct inode *dir, aufs_bindex_t bindex,
++ unsigned int lsc);
++void hdir_unlock(struct inode *h_dir, struct inode *dir, aufs_bindex_t bindex);
++void hdir_lock_rename(struct dentry **h_parents, struct inode **dirs,
++ aufs_bindex_t bindex, int issamedir);
++void 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);
++int __init au_inotify_init(void);
++void au_inotify_fin(void);
++#else
++static inline
++int alloc_hinotify(struct aufs_hinode *hinode, struct inode *inode,
++ struct inode *h_inode)
++{
++ return -EOPNOTSUPP;
++}
++
++static inline void do_free_hinotify(struct aufs_hinode *hinode)
++{
++ /* nothing */
++}
++
++static inline
++void do_hdir_lock(struct inode *h_dir, struct inode *dir, aufs_bindex_t bindex,
++ unsigned int lsc)
++{
++ hi_lock(h_dir, lsc);
++}
++
++static inline
++void hdir_unlock(struct inode *h_dir, struct inode *dir, aufs_bindex_t bindex)
++{
++ i_unlock(h_dir);
++}
++
++static inline
++void hdir_lock_rename(struct dentry **h_parents, struct inode **dirs,
++ aufs_bindex_t bindex, int issamedir)
++{
++ vfsub_lock_rename(h_parents[0], h_parents[1]);
++}
++
++static inline
++void 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 */
++}
++
++#define au_inotify_init() 0
++#define au_inotify_fin() /* */
++#endif /* CONFIG_AUFS_HINOTIFY */
++
++static inline void free_hinotify(struct inode *inode, aufs_bindex_t bindex)
++{
++ do_free_hinotify(itoii(inode)->ii_hinode + bindex);
++}
++
++/*
++ * hgdir_lock, hdir_lock, hdir2_lock
++ */
++#define LockFunc(name, lsc) \
++static inline \
++void name##_lock(struct inode *h_dir, struct inode *dir, aufs_bindex_t bindex) \
++{do_hdir_lock(h_dir, dir, bindex, AuLsc_HI_##lsc);}
++
++LockFunc(hgdir, GPARENT);
++LockFunc(hdir, PARENT);
++LockFunc(hdir2, PARENT2);
++
++#undef LockFunc
++
++/* ---------------------------------------------------------------------- */
++
++/* 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_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_new, ii_write_lock_new
++ */
++#define ReadLockFunc(name, lsc) \
++static inline void ii_read_lock_##name(struct inode *i) \
++{rw_read_lock_nested(&itoii(i)->ii_rwsem, AuLsc_II_##lsc);}
++
++#define WriteLockFunc(name, lsc) \
++static inline void ii_write_lock_##name(struct inode *i) \
++{rw_write_lock_nested(&itoii(i)->ii_rwsem, AuLsc_II_##lsc);}
++
++#define RWLockFuncs(name, lsc) \
++ ReadLockFunc(name, lsc); \
++ WriteLockFunc(name, lsc)
++
++RWLockFuncs(child, CHILD);
++RWLockFuncs(child2, CHILD2);
++RWLockFuncs(child3, CHILD3);
++RWLockFuncs(parent, PARENT);
++RWLockFuncs(parent2, PARENT2);
++RWLockFuncs(parent3, PARENT3);
++RWLockFuncs(new, NEW);
++
++#undef ReadLockFunc
++#undef WriteLockFunc
++#undef RWLockFunc
++
++/*
++ * ii_read_unlock, ii_write_unlock, ii_downgrade_lock
++ */
++SimpleUnlockRwsemFuncs(ii, struct inode *i, itoii(i)->ii_rwsem);
++
++/* to debug easier, do not make them inlined functions */
++#define IiMustReadLock(i) do { \
++ SiMustAnyLock((i)->i_sb); \
++ RwMustReadLock(&itoii(i)->ii_rwsem); \
++} while (0)
++
++#define IiMustWriteLock(i) do { \
++ SiMustAnyLock((i)->i_sb); \
++ RwMustWriteLock(&itoii(i)->ii_rwsem); \
++} while (0)
++
++#define IiMustAnyLock(i) do { \
++ SiMustAnyLock((i)->i_sb); \
++ RwMustAnyLock(&itoii(i)->ii_rwsem); \
++} while (0)
++
++#define IiMustNoWaiters(i) RwMustNoWaiters(&itoii(i)->ii_rwsem)
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_INODE_H__ */
+diff --git a/fs/aufs/misc.c b/fs/aufs/misc.c
+new file mode 100755
+index 0000000..32e0549
+--- /dev/null
++++ b/fs/aufs/misc.c
+@@ -0,0 +1,228 @@
++/*
++ * 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: misc.c,v 1.31 2007/05/14 03:41:52 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, ksize %d\n",
++ p, nused, new_sz, ksize(p));
++ DEBUG_ON(new_sz <= 0);
++ if (new_sz <= nused)
++ return p;
++ if (new_sz <= ksize(p)) {
++ memset(p + nused, 0, new_sz - nused);
++ return p;
++ }
++
++ q = kmalloc(new_sz, gfp);
++ //q = NULL;
++ if (unlikely(!q))
++ return NULL;
++ memcpy(q, p, nused);
++ memset(q + nused, 0, new_sz - nused);
++ //smp_mb();
++ kfree(p);
++ return q;
++}
++
++/* ---------------------------------------------------------------------- */
++
++// todo: make it inline
++struct nameidata *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->dentry = NULL;
++ fake_nd->mnt = NULL;
++
++#ifndef CONFIG_AUFS_FAKE_DM
++ DiMustAnyLock(nd->dentry);
++
++ if (bindex <= dbend(nd->dentry))
++ fake_nd->dentry = au_h_dptr_i(nd->dentry, bindex);
++ if (fake_nd->dentry) {
++ dget(fake_nd->dentry);
++ fake_nd->mnt = sbr_mnt(sb, bindex);
++ DEBUG_ON(!fake_nd->mnt);
++ mntget(fake_nd->mnt);
++ } else
++ fake_nd = ERR_PTR(-ENOENT);
++#endif
++
++ TraceErrPtr(fake_nd);
++ return fake_nd;
++}
++
++void fake_dm_release(struct nameidata *fake_nd)
++{
++#ifndef CONFIG_AUFS_FAKE_DM
++ if (fake_nd) {
++ mntput(fake_nd->mnt);
++ dput(fake_nd->dentry);
++ }
++#endif
++}
++
++/* ---------------------------------------------------------------------- */
++
++int au_copy_file(struct file *dst, struct file *src, loff_t len,
++ struct super_block *sb, int *sparse)
++{
++ int err, all_zero, dlgt;
++ unsigned long blksize;
++ char *buf;
++ /* reduce stack space */
++ struct iattr *ia;
++
++ LKTRTrace("%.*s, %.*s\n",
++ DLNPair(dst->f_dentry), DLNPair(src->f_dentry));
++ DEBUG_ON(!(dst->f_mode & FMODE_WRITE));
++ IMustLock(dst->f_dentry->d_parent->d_inode);
++
++ 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_KERNEL);
++ //buf = NULL;
++ if (unlikely(!buf))
++ goto out;
++ ia = kmalloc(sizeof(*ia), GFP_KERNEL);
++ if (unlikely(!ia))
++ goto out_buf;
++
++ dlgt = need_dlgt(sb);
++ err = all_zero = 0;
++ dst->f_pos = src->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;
++ while (!rbytes || err == -EAGAIN || err == -EINTR)
++ err = rbytes = vfsub_read_k(src, buf, sz, &src->f_pos,
++ dlgt);
++ if (unlikely(err < 0))
++ break;
++
++ all_zero = 0;
++ if (len >= rbytes && rbytes == blksize) {
++ all_zero = 1;
++ p = buf;
++ for (i = 0; all_zero && i < rbytes; i++)
++ all_zero = !*p++;
++ }
++ if (!all_zero) {
++ wbytes = rbytes;
++ p = buf;
++ while (wbytes) {
++ size_t b;
++ /* support LSM and notify */
++ err = b = vfsub_write_k(dst, p, wbytes,
++ &dst->f_pos, dlgt);
++ if (unlikely(err == -EAGAIN || err == -EINTR))
++ continue;
++ if (unlikely(err < 0))
++ break;
++ wbytes -= b;
++ p += b;
++ }
++ } else {
++ loff_t res;
++ LKTRLabel(hole);
++ *sparse = 1;
++ err = res = vfsub_llseek(dst, rbytes, SEEK_CUR);
++ 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 {
++ err = vfsub_write_k(dst, "\0", 1, &dst->f_pos, dlgt);
++ } 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;
++ hi_lock_child2(h_i);
++ err = vfsub_notify_change(h_d, ia, dlgt);
++ i_unlock(h_i);
++ }
++ }
++
++ kfree(ia);
++ out_buf:
++ kfree(buf);
++ out:
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++int test_ro(struct super_block *sb, aufs_bindex_t bindex, struct inode *inode)
++{
++ int err;
++
++ err = br_rdonly(stobr(sb, bindex));
++ if (!err && inode) {
++ struct inode *hi = au_h_iptr_i(inode, bindex);
++ if (hi)
++ err = IS_IMMUTABLE(hi) ? -EROFS : 0;
++ }
++ return err;
++}
++
++int au_test_perm(struct inode *hidden_inode, int mask, int dlgt)
++{
++ if (!current->fsuid)
++ return 0;
++ if (unlikely(au_is_nfs(hidden_inode->i_sb)
++ && (mask & MAY_WRITE)
++ && S_ISDIR(hidden_inode->i_mode)))
++ mask |= MAY_READ; /* force permission check */
++ return vfsub_permission(hidden_inode, mask, NULL, dlgt);
++}
+diff --git a/fs/aufs/misc.h b/fs/aufs/misc.h
+new file mode 100755
+index 0000000..fea4a2c
+--- /dev/null
++++ b/fs/aufs/misc.h
+@@ -0,0 +1,187 @@
++/*
++ * 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: misc.h,v 1.25 2007/05/14 03:41:52 sfjro Exp $ */
++
++#ifndef __AUFS_MISC_H__
++#define __AUFS_MISC_H__
++
++#ifdef __KERNEL__
++
++#include <linux/fs.h>
++#include <linux/namei.h>
++#include <linux/sched.h>
++#include <linux/version.h>
++#include <linux/aufs_type.h>
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
++#define I_MUTEX_QUOTA 0
++#define lockdep_off() /* */
++#define lockdep_on() /* */
++#define mutex_lock_nested(mtx, lsc) mutex_lock(mtx)
++#define down_write_nested(rw, lsc) down_write(rw)
++#define down_read_nested(rw, lsc) down_read(rw)
++#endif
++
++/* ---------------------------------------------------------------------- */
++
++struct aufs_rwsem {
++ struct rw_semaphore rwsem;
++#ifdef CONFIG_AUFS_DEBUG
++ atomic_t rcnt;
++#endif
++};
++
++#ifdef CONFIG_AUFS_DEBUG
++#define DbgRcntInit(rw) atomic_set(&(rw)->rcnt, 0)
++#define DbgRcntInc(rw) atomic_inc(&(rw)->rcnt)
++#define DbgRcntDec(rw) WARN_ON(atomic_dec_return(&(rw)->rcnt) < 0)
++#else
++#define DbgRcntInit(rw) /* */
++#define DbgRcntInc(rw) /* */
++#define DbgRcntDec(rw) /* */
++#endif
++
++static inline void rw_init_nolock(struct aufs_rwsem *rw)
++{
++ DbgRcntInit(rw);
++ init_rwsem(&rw->rwsem);
++}
++
++static inline void rw_init_wlock(struct aufs_rwsem *rw)
++{
++ rw_init_nolock(rw);
++ down_write(&rw->rwsem);
++}
++
++static inline void rw_init_wlock_nested(struct aufs_rwsem *rw, unsigned int lsc)
++{
++ rw_init_nolock(rw);
++ down_write_nested(&rw->rwsem, lsc);
++}
++
++static inline void rw_read_lock(struct aufs_rwsem *rw)
++{
++ down_read(&rw->rwsem);
++ DbgRcntInc(rw);
++}
++
++static inline void rw_read_lock_nested(struct aufs_rwsem *rw, unsigned int lsc)
++{
++ down_read_nested(&rw->rwsem, lsc);
++ DbgRcntInc(rw);
++}
++
++static inline void rw_read_unlock(struct aufs_rwsem *rw)
++{
++ DbgRcntDec(rw);
++ up_read(&rw->rwsem);
++}
++
++static inline void rw_dgrade_lock(struct aufs_rwsem *rw)
++{
++ DbgRcntInc(rw);
++ downgrade_write(&rw->rwsem);
++}
++
++static inline void rw_write_lock(struct aufs_rwsem *rw)
++{
++ down_write(&rw->rwsem);
++}
++
++static inline void rw_write_lock_nested(struct aufs_rwsem *rw, unsigned int lsc)
++{
++ down_write_nested(&rw->rwsem, lsc);
++}
++
++static inline void rw_write_unlock(struct aufs_rwsem *rw)
++{
++ up_write(&rw->rwsem);
++}
++
++#if 0 // why is not _nested version defined
++static inline int rw_read_trylock(struct aufs_rwsem *rw)
++{
++ int ret = down_read_trylock(&rw->rwsem);
++ if (ret)
++ DbgRcntInc(rw);
++ return ret;
++}
++
++static inline int rw_write_trylock(struct aufs_rwsem *rw)
++{
++ return down_write_trylock(&rw->rwsem);
++}
++#endif
++
++#undef DbgRcntInit
++#undef DbgRcntInc
++#undef DbgRcntDec
++
++/* to debug easier, do not make them inlined functions */
++#define RwMustNoWaiters(rw) DEBUG_ON(!list_empty(&(rw)->rwsem.wait_list))
++#define RwMustAnyLock(rw) DEBUG_ON(down_write_trylock(&(rw)->rwsem))
++#ifdef CONFIG_AUFS_DEBUG
++#define RwMustReadLock(rw) do { \
++ RwMustAnyLock(rw); \
++ DEBUG_ON(!atomic_read(&(rw)->rcnt)); \
++} while (0)
++#define RwMustWriteLock(rw) do { \
++ RwMustAnyLock(rw); \
++ DEBUG_ON(atomic_read(&(rw)->rcnt)); \
++} while (0)
++#else
++#define RwMustReadLock(rw) RwMustAnyLock(rw)
++#define RwMustWriteLock(rw) RwMustAnyLock(rw)
++#endif
++
++#define SimpleLockRwsemFuncs(prefix, param, rwsem) \
++static inline void prefix##_read_lock(param) {rw_read_lock(&(rwsem));} \
++static inline void prefix##_write_lock(param) {rw_write_lock(&(rwsem));}
++//static inline void prefix##_read_trylock(param) {rw_read_trylock(&(rwsem));}
++//static inline void prefix##_write_trylock(param) {rw_write_trylock(&(rwsem));}
++//static inline void prefix##_read_trylock_nested(param, lsc)
++//{rw_read_trylock_nested(&(rwsem, lsc));}
++//static inline void prefix##_write_trylock_nestd(param, lsc)
++//{rw_write_trylock_nested(&(rwsem), nested);}
++
++#define SimpleUnlockRwsemFuncs(prefix, param, rwsem) \
++static inline void prefix##_read_unlock(param) {rw_read_unlock(&(rwsem));} \
++static inline void prefix##_write_unlock(param) {rw_write_unlock(&(rwsem));} \
++static inline void prefix##_downgrade_lock(param) {rw_dgrade_lock(&(rwsem));}
++
++#define SimpleRwsemFuncs(prefix, param, rwsem) \
++ SimpleLockRwsemFuncs(prefix, param, rwsem); \
++ SimpleUnlockRwsemFuncs(prefix, param, rwsem)
++
++/* ---------------------------------------------------------------------- */
++
++typedef ssize_t (*readf_t)(struct file*, char __user*, size_t, loff_t*);
++typedef ssize_t (*writef_t)(struct file*, const char __user*, size_t, loff_t*);
++
++void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp);
++struct nameidata *fake_dm(struct nameidata *fake_nd, struct nameidata *nd,
++ struct super_block *sb, aufs_bindex_t bindex);
++void fake_dm_release(struct nameidata *fake_nd);
++int au_copy_file(struct file *dst, struct file *src, loff_t len,
++ struct super_block *sb, int *sparse);
++int test_ro(struct super_block *sb, aufs_bindex_t bindex, struct inode *inode);
++int au_test_perm(struct inode *h_inode, int mask, int dlgt);
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_MISC_H__ */
+diff --git a/fs/aufs/module.c b/fs/aufs/module.c
+new file mode 100755
+index 0000000..06c563e
+--- /dev/null
++++ b/fs/aufs/module.c
+@@ -0,0 +1,334 @@
++/*
++ * 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: module.c,v 1.9 2007/04/30 05:46:32 sfjro Exp $ */
++
++//#include <linux/init.h>
++//#include <linux/kobject.h>
++#include <linux/module.h>
++//#include <linux/seq_file.h>
++//#include <linux/sysfs.h>
++#include "aufs.h"
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * aufs caches
++ */
++struct kmem_cache *aufs_cachep[AuCache_Last];
++static int __init create_cache(void)
++{
++#define Cache(type) kmem_cache_create(#type, sizeof(struct type), 0, \
++ SLAB_RECLAIM_ACCOUNT, NULL, NULL)
++
++ if ((aufs_cachep[AuCache_DINFO] = Cache(aufs_dinfo))
++ && (aufs_cachep[AuCache_ICNTNR] = Cache(aufs_icntnr))
++ && (aufs_cachep[AuCache_FINFO] = Cache(aufs_finfo))
++ //&& (aufs_cachep[AuCache_FINFO] = NULL)
++ && (aufs_cachep[AuCache_VDIR] = Cache(aufs_vdir))
++ && (aufs_cachep[AuCache_DEHSTR] = Cache(aufs_dehstr))
++ && (aufs_cachep[AuCache_HINOTIFY] = Cache(aufs_hinotify)))
++ return 0;
++ return -ENOMEM;
++
++#undef Cache
++}
++
++static void destroy_cache(void)
++{
++ int i;
++ for (i = 0; i < AuCache_Last; i++)
++ if (aufs_cachep[i])
++ kmem_cache_destroy(aufs_cachep[i]);
++}
++
++/* ---------------------------------------------------------------------- */
++
++char au_esc_chars[0x20 + 3]; /* 0x01-0x20, backslash, del, and NULL */
++int au_dir_roflags;
++extern struct file_system_type aufs_fs_type;
++
++#ifdef DbgDlgt
++#include <linux/security.h>
++#include "dbg_dlgt.c"
++#else
++#define dbg_dlgt_init() 0
++#define dbg_dlgt_fin() /* */
++#endif
++
++/*
++ * 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 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, 0444);
++
++int sysaufs_brs = 0;
++MODULE_PARM_DESC(brs, "use <sysfs>/fs/aufs/brs");
++module_param_named(brs, sysaufs_brs, int, 0444);
++
++static int __init aufs_init(void)
++{
++ int err, i;
++ char *p;
++
++ //sbinfo->si_xino is atomic_long_t
++ BUILD_BUG_ON(sizeof(ino_t) != sizeof(long));
++
++#ifdef CONFIG_AUFS_DEBUG
++ {
++ struct aufs_destr destr;
++ destr.len = -1;
++ DEBUG_ON(destr.len < NAME_MAX);
++ }
++
++#ifdef CONFIG_4KSTACKS
++ printk("CONFIG_4KSTACKS is defined.\n");
++#endif
++#if 0 // verbose debug
++ {
++ union {
++ struct aufs_branch *br;
++ struct aufs_dinfo *di;
++ struct aufs_finfo *fi;
++ struct aufs_iinfo *ii;
++ struct aufs_hinode *hi;
++ struct aufs_sbinfo *si;
++ struct aufs_destr *destr;
++ struct aufs_de *de;
++ struct aufs_wh *wh;
++ struct aufs_vdir *vd;
++ } u;
++
++ printk("br{"
++ "xino %d, readf %d, writef %d, "
++ "id %d, perm %d, mnt %d, count %d, "
++ "wh_sem %d, wh %d, run %d} %d\n",
++ offsetof(typeof(*u.br), br_xino),
++ offsetof(typeof(*u.br), br_xino_read),
++ offsetof(typeof(*u.br), br_xino_write),
++ 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),
++ sizeof(*u.br));
++ printk("di{gen %d, rwsem %d, bstart %d, bend %d, bwh %d, "
++ "bdiropq %d, hdentry %d, reval %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),
++ offsetof(typeof(*u.di), di_reval),
++ sizeof(*u.di));
++ printk("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));
++ printk("ii{rwsem %d, bstart %d, bend %d, hinode %d, vdir %d} "
++ "%d\n",
++ 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));
++ printk("hi{inode %d, id %d, notify %d} %d\n",
++ offsetof(typeof(*u.hi), hi_inode),
++ offsetof(typeof(*u.hi), hi_id),
++ offsetof(typeof(*u.hi), hi_notify),
++ sizeof(*u.hi));
++ printk("si{rwsem %d, gen %d, "
++ "failed_refresh %d, "
++ "bend %d, last id %d, br %d, "
++ "flags %d, "
++ "xino %d, "
++ "rdcache %d, "
++ "dirwh %d, "
++ "pl_lock %d, pl %d, "
++ "kobj %d} %d\n",
++ offsetof(typeof(*u.si), si_rwsem),
++ offsetof(typeof(*u.si), si_generation),
++ -1,//offsetof(typeof(*u.si), si_failed_refresh_dirs),
++ 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_flags),
++ offsetof(typeof(*u.si), si_xino),
++ 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_kobj),
++ sizeof(*u.si));
++ printk("destr{len %d, name %d} %d\n",
++ offsetof(typeof(*u.destr), len),
++ offsetof(typeof(*u.destr), name),
++ sizeof(*u.destr));
++ printk("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));
++ printk("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));
++ printk("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
++#endif
++
++ 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);
++#ifndef CONFIG_AUFS_SYSAUFS
++ sysaufs_brs = 0;
++#endif
++
++ err = -EINVAL;
++ if (unlikely(aufs_nwkq <= 0))
++ goto out;
++ err = create_cache();
++ if (unlikely(err))
++ goto out;
++ err = sysaufs_init();
++ if (unlikely(err))
++ goto out_cache;
++ err = au_wkq_init();
++ if (unlikely(err))
++ goto out_kobj;
++ err = au_inotify_init();
++ if (unlikely(err))
++ goto out_wkq;
++ err = dbg_dlgt_init();
++ if (unlikely(err))
++ goto out_inotify;
++ err = register_filesystem(&aufs_fs_type);
++ if (unlikely(err))
++ goto out_dlgt;
++ printk(AUFS_NAME " " AUFS_VERSION "\n");
++ return 0; /* success */
++
++ out_dlgt:
++ dbg_dlgt_fin();
++ out_inotify:
++ au_inotify_fin();
++ out_wkq:
++ au_wkq_fin();
++ out_kobj:
++ sysaufs_fin();
++ out_cache:
++ destroy_cache();
++ out:
++ TraceErr(err);
++ return err;
++}
++
++static void __exit aufs_exit(void)
++{
++ unregister_filesystem(&aufs_fs_type);
++ dbg_dlgt_fin();
++ au_inotify_fin();
++ au_wkq_fin();
++ sysaufs_fin();
++ destroy_cache();
++}
++
++module_init(aufs_init);
++module_exit(aufs_exit);
++
++/* ---------------------------------------------------------------------- */
++
++// fake Kconfig
++#if 1
++#ifdef CONFIG_AUFS_HINOTIFY
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
++#error CONFIG_AUFS_HINOTIFY is supported in linux-2.6.18 and later.
++#endif
++#ifndef CONFIG_INOTIFY
++#error enable CONFIG_INOTIFY to use CONFIG_AUFS_HINOTIFY.
++#endif
++#endif
++
++#if AUFS_BRANCH_MAX > 511 && BITS_PER_LONG == 64 && PAGE_SIZE == 4096
++#warning For 4k pagesize and 64bit environment, \
++ CONFIG_AUFS_BRANCH_MAX_511 or smaller is recommended.
++#endif
++
++#ifdef CONFIG_AUFS_SYSAUFS
++#ifndef CONFIG_SYSFS
++#error CONFIG_AUFS_SYSAUFS requires CONFIG_SYSFS.
++#endif
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
++#error CONFIG_AUFS_SYSAUFS requires linux-2.6.18 and later.
++#endif
++#endif
++
++#ifdef CONFIG_AUFS_EXPORT
++#if !defined(CONFIG_EXPORTFS) && !defined(CONFIG_EXPORTFS_MODULE)
++#error CONFIG_AUFS_EXPORT requires CONFIG_EXPORTFS
++#endif
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
++#error CONFIG_AUFS_EXPORT requires linux-2.6.18 and later.
++#endif
++#if defined(CONFIG_EXPORTFS_MODULE) && defined(CONFIG_AUFS)
++#error need CONFIG_EXPORTFS=y to link aufs statically with CONFIG_AUFS_EXPORT
++#endif
++#endif
++
++#ifdef CONFIG_DEBUG_PROVE_LOCKING
++#if MAX_LOCKDEP_SUBCLASSES < AuLsc_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
+diff --git a/fs/aufs/module.h b/fs/aufs/module.h
+new file mode 100755
+index 0000000..3769861
+--- /dev/null
++++ b/fs/aufs/module.h
+@@ -0,0 +1,60 @@
++/*
++ * 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: module.h,v 1.8 2007/05/14 03:41:52 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, AuCache_HINOTIFY, AuCache_Last};
++extern struct kmem_cache *aufs_cachep[];
++
++#define CacheFuncs(name, index) \
++static inline void *cache_alloc_##name(void) \
++{return kmem_cache_alloc(aufs_cachep[index], GFP_KERNEL);} \
++static inline void cache_free_##name(void *p) \
++{kmem_cache_free(aufs_cachep[index], p);}
++
++CacheFuncs(dinfo, AuCache_DINFO);
++CacheFuncs(icntnr, AuCache_ICNTNR);
++CacheFuncs(finfo, AuCache_FINFO);
++CacheFuncs(vdir, AuCache_VDIR);
++CacheFuncs(dehstr, AuCache_DEHSTR);
++CacheFuncs(hinotify, AuCache_HINOTIFY);
++
++#undef CacheFuncs
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_MODULE_H__ */
+diff --git a/fs/aufs/opts.c b/fs/aufs/opts.c
+new file mode 100755
+index 0000000..c1a9445
+--- /dev/null
++++ b/fs/aufs/opts.c
+@@ -0,0 +1,1043 @@
++/*
++ * 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: opts.c,v 1.34 2007/05/14 03:40:27 sfjro Exp $ */
++
++#include <asm/types.h> // a distribution requires
++#include <linux/parser.h>
++#include "aufs.h"
++
++enum {
++ Opt_br,
++ Opt_add, Opt_del, Opt_mod, Opt_append, Opt_prepend,
++ Opt_idel, Opt_imod,
++ Opt_dirwh, Opt_rdcache, Opt_deblk, Opt_nhash,
++ Opt_xino, Opt_zxino, Opt_noxino,
++ Opt_plink, Opt_noplink, Opt_list_plink, Opt_clean_plink,
++ Opt_udba,
++ Opt_diropq_a, Opt_diropq_w,
++ Opt_warn_perm, Opt_nowarn_perm,
++ Opt_findrw_dir, Opt_findrw_br,
++ Opt_coo,
++ Opt_dlgt, Opt_nodlgt,
++ Opt_tail, Opt_ignore, 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_noxino, "noxino"},
++ //{Opt_zxino, "zxino=%s"},
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
++ {Opt_plink, "plink"},
++ {Opt_noplink, "noplink"},
++#ifdef CONFIG_AUFS_DEBUG
++ {Opt_list_plink, "list_plink"},
++#endif
++ {Opt_clean_plink, "clean_plink"},
++#endif
++
++ {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"},
++#endif
++
++ {Opt_rdcache, "rdcache=%d"},
++ {Opt_rdcache, "rdcache:%d"},
++#if 0
++ {Opt_findrw_dir, "findrw=dir"},
++ {Opt_findrw_br, "findrw=br"},
++
++ {Opt_coo, "coo=%s"},
++
++ {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}
++};
++
++/* ---------------------------------------------------------------------- */
++
++#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 int br_perm_val(char *perm)
++{
++ int val;
++ substring_t args[MAX_OPT_ARGS];
++
++ DEBUG_ON(!perm || !*perm);
++ LKTRTrace("perm %s\n", perm);
++ val = match_token(perm, brperms, args);
++ TraceErr(val);
++ return val;
++}
++
++int br_perm_str(char *p, unsigned int len, int brperm)
++{
++ struct match_token *bp = brperms;
++
++ LKTRTrace("len %d, 0x%x\n", len, brperm);
++
++ while (bp->pattern) {
++ if (bp->token == brperm) {
++ if (strlen(bp->pattern) < len) {
++ strcpy(p, bp->pattern);
++ return 0;
++ } else
++ return -E2BIG;
++ }
++ bp++;
++ }
++
++ return -EIO;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static match_table_t udbalevel = {
++ {AuFlag_UDBA_REVAL, "reval"},
++#ifdef CONFIG_AUFS_HINOTIFY
++ {AuFlag_UDBA_INOTIFY, "inotify"},
++#endif
++ {AuFlag_UDBA_NONE, "none"},
++ {-1, NULL}
++};
++
++static int udba_val(char *str)
++{
++ substring_t args[MAX_OPT_ARGS];
++ return match_token(str, udbalevel, args);
++}
++
++au_parser_pattern_t udba_str(int udba)
++{
++ struct match_token *p = udbalevel;
++ while (p->pattern) {
++ if (p->token == udba)
++ return p->pattern;
++ p++;
++ }
++ BUG();
++ return "??";
++}
++
++void udba_set(struct super_block *sb, unsigned int flg)
++{
++ au_flag_clr(sb, AuMask_UDBA);
++ au_flag_set(sb, flg);
++}
++
++/* ---------------------------------------------------------------------- */
++
++static match_table_t coolevel = {
++ {AuFlag_COO_LEAF, "leaf"},
++ {AuFlag_COO_ALL, "all"},
++ {AuFlag_COO_NONE, "none"},
++ {-1, NULL}
++};
++
++#if 0
++static int coo_val(char *str)
++{
++ substring_t args[MAX_OPT_ARGS];
++ return match_token(str, coolevel, args);
++}
++#endif
++
++au_parser_pattern_t coo_str(int coo)
++{
++ struct match_token *p = coolevel;
++ while (p->pattern) {
++ if (p->token == coo)
++ return p->pattern;
++ p++;
++ }
++ BUG();
++ return "??";
++}
++static void coo_set(struct super_block *sb, unsigned int flg)
++{
++ au_flag_clr(sb, AuMask_COO);
++ au_flag_set(sb, flg);
++}
++
++/* ---------------------------------------------------------------------- */
++
++static const int lkup_dirflags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
++
++#ifdef CONFIG_AUFS_DEBUG
++static void dump_opts(struct opts *opts)
++{
++ /* reduce stack space */
++ union {
++ struct opt_add *add;
++ struct opt_del *del;
++ struct opt_mod *mod;
++ struct opt_xino *xino;
++ } u;
++ struct opt *opt;
++
++ TraceEnter();
++
++ 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.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.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.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, DLNPair(u.xino->file->f_dentry));
++ break;
++ case Opt_noxino:
++ LKTRLabel(noxino);
++ 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, udba_str(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_coo:
++ LKTRTrace("coo %d, %s\n", opt->coo, coo_str(opt->coo));
++ break;
++ default:
++ BUG();
++ }
++ opt++;
++ }
++}
++#else
++#define dump_opts(opts) /* */
++#endif
++
++void au_free_opts(struct opts *opts)
++{
++ struct opt *opt;
++
++ TraceEnter();
++
++ opt = opts->opt;
++ while (opt->type != Opt_tail) {
++ switch (opt->type) {
++ case Opt_add:
++ case Opt_append:
++ case Opt_prepend:
++ path_release(&opt->add.nd);
++ 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 opt *opt, char *opt_str, struct super_block *sb,
++ aufs_bindex_t bindex)
++{
++ int err;
++ struct opt_add *add = &opt->add;
++ char *p;
++
++ LKTRTrace("%s, b%d\n", opt_str, bindex);
++
++ add->bindex = bindex;
++ add->perm = AuBr_RO;
++ if (!bindex && !(sb->s_flags & MS_RDONLY))
++ add->perm = AuBr_RW;
++#ifdef CONFIG_AUFS_COMPAT
++ add->perm = AuBr_RW;
++#endif
++ 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 = path_lookup(add->path, lkup_dirflags, &add->nd);
++ //err = -1;
++ if (!err) {
++ opt->type = Opt_add;
++ goto out;
++ }
++ Err("lookup failed %s (%d)\n", add->path, err);
++ err = -EINVAL;
++
++ out:
++ TraceErr(err);
++ return err;
++}
++
++/* called without aufs lock */
++int au_parse_opts(struct super_block *sb, char *str, struct opts *opts)
++{
++ int err, n;
++ struct dentry *root;
++ struct opt *opt, *opt_tail;
++ char *opt_str;
++ substring_t args[MAX_OPT_ARGS];
++ aufs_bindex_t bindex;
++ struct nameidata nd;
++ /* reduce stack space */
++ union {
++ struct opt_del *del;
++ struct opt_mod *mod;
++ struct opt_xino *xino;
++ } 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) {
++ int token, skipped;
++ char *p;
++ 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))) {
++ Err("bad integer in %s\n", opt_str);
++ break;
++ }
++ bindex = n;
++ err = opt_add(opt, args[1].from, sb, bindex);
++ break;
++ case Opt_append:
++ case Opt_prepend:
++ err = opt_add(opt, args[0].from, sb, /*dummy bindex*/1);
++ 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 = path_lookup(u.del->path, lkup_dirflags, &nd);
++ if (unlikely(err)) {
++ Err("lookup failed %s (%d)\n", u.del->path, err);
++ break;
++ }
++ u.del->h_root = dget(nd.dentry);
++ path_release(&nd);
++ opt->type = token;
++ break;
++#if 0
++ case Opt_idel:
++ u.del = &opt->del;
++ u.del->path = "(indexed)";
++ if (unlikely(match_int(&args[0], &n))) {
++ Err("bad integer in %s\n", opt_str);
++ break;
++ }
++ bindex = n;
++ aufs_read_lock(root, !AUFS_I_RLOCK);
++ if (bindex < 0 || sbend(sb) < bindex) {
++ Err("out of bounds, %d\n", bindex);
++ aufs_read_unlock(root, !AUFS_I_RLOCK);
++ break;
++ }
++ err = 0;
++ u.del->h_root = dget(au_h_dptr_i(root, bindex));
++ opt->type = token;
++ aufs_read_unlock(root, !AUFS_I_RLOCK);
++ break;
++#endif
++
++ case Opt_mod:
++ u.mod = &opt->mod;
++ u.mod->path = args[0].from;
++ p = strchr(u.mod->path, '=');
++ if (unlikely(!p)) {
++ Err("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 = path_lookup(u.mod->path, lkup_dirflags, &nd);
++ if (unlikely(err)) {
++ Err("lookup failed %s (%d)\n", u.mod->path, err);
++ break;
++ }
++ u.mod->h_root = dget(nd.dentry);
++ path_release(&nd);
++ opt->type = token;
++ break;
++#if 0
++ case Opt_imod:
++ u.mod = &opt->mod;
++ u.mod->path = "(indexed)";
++ if (unlikely(match_int(&args[0], &n))) {
++ Err("bad integer in %s\n", opt_str);
++ break;
++ }
++ bindex = n;
++ aufs_read_lock(root, !AUFS_I_RLOCK);
++ if (bindex < 0 || sbend(sb) < bindex) {
++ Err("out of bounds, %d\n", bindex);
++ aufs_read_unlock(root, !AUFS_I_RLOCK);
++ 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_i(root, bindex));
++ opt->type = token;
++ aufs_read_unlock(root, !AUFS_I_RLOCK);
++ break;
++#endif
++ case Opt_xino:
++ u.xino = &opt->xino;
++ file = 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);
++ Err("%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_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_noxino:
++ 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:
++ 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;
++
++#if 0
++ case Opt_coo:
++ opt->coo = coo_val(args[0].from);
++ if (opt->coo >= 0) {
++ err = 0;
++ opt->type = token;
++ }
++ break;
++#endif
++
++ case Opt_ignore:
++#ifndef CONFIG_AUFS_COMPAT
++ Warn("ignored %s\n", opt_str);
++#endif
++ skipped = 1;
++ err = 0;
++ break;
++ case Opt_err:
++ Err("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_free_opts(opts);
++ TraceErr(err);
++ return err;
++}
++
++/*
++ * returns,
++ * plus: processed without an error
++ * zero: unprocessed
++ */
++static int au_do_opt_simple(struct super_block *sb, struct opt *opt,
++ int remount, unsigned int *given)
++{
++ int err;
++ struct aufs_sbinfo *sbinfo = stosi(sb);
++
++ TraceEnter();
++
++ err = 1; /* handled */
++ switch (opt->type) {
++ case Opt_udba:
++ udba_set(sb, opt->udba);
++ *given |= opt->udba;
++ break;
++
++ case Opt_plink:
++ au_flag_set(sb, AuFlag_PLINK);
++ *given |= AuFlag_PLINK;
++ break;
++ case Opt_noplink:
++ if (au_flag_test(sb, AuFlag_PLINK))
++ au_put_plink(sb);
++ au_flag_clr(sb, AuFlag_PLINK);
++ *given |= AuFlag_PLINK;
++ break;
++ case Opt_list_plink:
++ if (au_flag_test(sb, AuFlag_PLINK))
++ au_list_plink(sb);
++ break;
++ case Opt_clean_plink:
++ if (au_flag_test(sb, AuFlag_PLINK))
++ au_put_plink(sb);
++ break;
++
++ case Opt_diropq_a:
++ au_flag_set(sb, AuFlag_ALWAYS_DIROPQ);
++ *given |= AuFlag_ALWAYS_DIROPQ;
++ break;
++ case Opt_diropq_w:
++ au_flag_clr(sb, AuFlag_ALWAYS_DIROPQ);
++ *given |= AuFlag_ALWAYS_DIROPQ;
++ break;
++
++ case Opt_dlgt:
++ au_flag_set(sb, AuFlag_DLGT);
++ *given |= AuFlag_DLGT;
++ break;
++ case Opt_nodlgt:
++ au_flag_clr(sb, AuFlag_DLGT);
++ *given |= AuFlag_DLGT;
++ break;
++
++ case Opt_warn_perm:
++ au_flag_set(sb, AuFlag_WARN_PERM);
++ *given |= AuFlag_WARN_PERM;
++ break;
++ case Opt_nowarn_perm:
++ au_flag_clr(sb, AuFlag_WARN_PERM);
++ *given |= AuFlag_WARN_PERM;
++ break;
++
++ case Opt_coo:
++ coo_set(sb, opt->coo);
++ *given |= opt->coo;
++ break;
++
++ case Opt_dirwh:
++ sbinfo->si_dirwh = opt->dirwh;
++ break;
++
++ case Opt_rdcache:
++ sbinfo->si_rdcache = opt->rdcache * HZ;
++ break;
++
++ default:
++ err = 0;
++ break;
++ }
++
++ TraceErr(err);
++ return err;
++}
++
++/*
++ * returns tri-state.
++ * plus: processed without an error
++ * zero: unprocessed
++ * minus: error
++ */
++static int au_do_opt_br(struct super_block *sb, struct opt *opt, int remount,
++ int *do_refresh)
++{
++ int err;
++
++ TraceEnter();
++
++ err = 0;
++ switch (opt->type) {
++ case Opt_append:
++ opt->add.bindex = sbend(sb) + 1;
++ goto add;
++ case Opt_prepend:
++ opt->add.bindex = 0;
++ add:
++ case Opt_add:
++ err = br_add(sb, &opt->add, remount);
++ if (!err)
++ *do_refresh = err = 1;
++ break;
++
++ case Opt_del:
++ case Opt_idel:
++ err = br_del(sb, &opt->del, remount);
++ if (!err)
++ *do_refresh = err = 1;
++ break;
++
++ case Opt_mod:
++ case Opt_imod:
++ err = br_mod(sb, &opt->mod, remount, do_refresh);
++ if (!err)
++ err = 1;
++ break;
++ }
++
++ TraceErr(err);
++ return err;
++}
++
++static int au_do_opt_xino(struct super_block *sb, struct opt *opt, int remount,
++ struct opt_xino **opt_xino)
++{
++ int err;
++
++ TraceEnter();
++
++ err = 0;
++ switch (opt->type) {
++ case Opt_xino:
++ err = xino_set(sb, &opt->xino, remount);
++ if (!err)
++ *opt_xino = &opt->xino;
++ break;
++ case Opt_noxino:
++ err = xino_clr(sb);
++ break;
++ }
++
++ TraceErr(err);
++ return err;
++}
++
++static int verify_opts(struct super_block *sb, int remount)
++{
++ int err;
++ aufs_bindex_t bindex, bend;
++ struct aufs_branch *br;
++ struct dentry *root;
++ struct inode *dir;
++ unsigned int do_plink;
++
++ TraceEnter();
++
++ if (unlikely(!(sb->s_flags & MS_RDONLY)
++ && !br_writable(sbr_perm(sb, 0))))
++ Warn("first branch should be rw\n");
++
++ err = 0;
++ root = sb->s_root;
++ dir = sb->s_root->d_inode;
++ do_plink = au_flag_test(sb, AuFlag_PLINK);
++ bend = sbend(sb);
++ for (bindex = 0; !err && bindex <= bend; bindex++) {
++ struct inode *h_dir;
++ int skip;
++
++ skip = 0;
++ h_dir = au_h_iptr_i(dir, bindex);
++ br = stobr(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;
++
++ hdir_lock(h_dir, dir, bindex);
++ br_wh_write_lock(br);
++ err = init_wh(au_h_dptr_i(root, bindex), br,
++ au_nfsmnt(sb, bindex), sb);
++ br_wh_write_unlock(br);
++ hdir_unlock(h_dir, dir, bindex);
++ }
++
++ TraceErr(err);
++ return err;
++}
++
++int au_do_opts_mount(struct super_block *sb, struct opts *opts)
++{
++ int err, do_refresh;
++ struct inode *dir;
++ struct opt *opt;
++ unsigned int flags, given;
++ struct opt_xino *opt_xino;
++ aufs_bindex_t bend, bindex;
++
++ TraceEnter();
++ SiMustWriteLock(sb);
++ DiMustWriteLock(sb->s_root);
++ dir = sb->s_root->d_inode;
++ IiMustWriteLock(dir);
++
++ err = 0;
++ given = 0;
++ opt_xino = NULL;
++ opt = opts->opt;
++ while (err >= 0 && opt->type != Opt_tail)
++ err = au_do_opt_simple(sb, opt++, /*remount*/0, &given);
++ if (err > 0)
++ err = 0;
++ else if (unlikely(err < 0))
++ goto out;
++
++ /* disable them temporary */
++ flags = au_flag_test(sb, AuFlag_XINO | AuMask_UDBA | AuFlag_DLGT);
++ au_flag_clr(sb, AuFlag_XINO | AuFlag_DLGT);
++ udba_set(sb, AuFlag_UDBA_REVAL);
++
++ do_refresh = 0;
++ opt = opts->opt;
++ while (err >= 0 && opt->type != Opt_tail)
++ err = au_do_opt_br(sb, opt++, /*remount*/0, &do_refresh);
++ if (err > 0)
++ err = 0;
++ else if (unlikely(err < 0))
++ goto out;
++
++ bend = sbend(sb);
++ if (unlikely(bend < 0)) {
++ err = -EINVAL;
++ Err("no branches\n");
++ goto out;
++ }
++
++ if (flags & AuFlag_XINO)
++ au_flag_set(sb, AuFlag_XINO);
++ opt = opts->opt;
++ while (!err && opt->type != Opt_tail)
++ err = au_do_opt_xino(sb, opt++, /*remount*/0, &opt_xino);
++ if (unlikely(err))
++ goto out;
++
++ //todo: test this error case.
++ err = verify_opts(sb, /*remount*/0);
++ DEBUG_ON(err);
++ if (unlikely(err))
++ goto out;
++
++ /* enable xino */
++ if (au_flag_test(sb, AuFlag_XINO) && !opt_xino) {
++ struct file *xino_file = xino_def(sb);
++ err = PTR_ERR(xino_file);
++ if (IS_ERR(xino_file))
++ goto out;
++
++ err = 0;
++ for (bindex = 0; !err && bindex <= bend; bindex++)
++ err = xino_init(sb, bindex, xino_file,
++ /*do_test*/bindex);
++ fput(xino_file);
++ if (unlikely(err))
++ goto out;
++ }
++
++ /* restore hinotify */
++ udba_set(sb, flags & AuMask_UDBA);
++ if (flags & AuFlag_UDBA_INOTIFY)
++ au_reset_hinotify(dir, au_hi_flags(dir, 1) & ~AUFS_HI_XINO);
++
++ /* restore dlgt */
++ if (flags & AuFlag_DLGT)
++ au_flag_set(sb, AuFlag_DLGT);
++
++ out:
++ TraceErr(err);
++ return err;
++}
++
++int au_do_opts_remount(struct super_block *sb, struct opts *opts,
++ int *do_refresh, unsigned int *given)
++{
++ int err, rerr;
++ struct inode *dir;
++ struct opt_xino *opt_xino;
++ struct opt *opt;
++ unsigned int dlgt;
++
++ TraceEnter();
++ SiMustWriteLock(sb);
++ DiMustWriteLock(sb->s_root);
++ dir = sb->s_root->d_inode;
++ IiMustWriteLock(dir);
++ //DEBUG_ON(au_flag_test(sb, AuFlag_UDBA_INOTIFY));
++
++ err = 0;
++ *do_refresh = 0;
++ *given = 0;
++ dlgt = au_flag_test(sb, AuFlag_DLGT);
++ opt_xino = NULL;
++ opt = opts->opt;
++ while (err >= 0 && opt->type != Opt_tail) {
++ err = au_do_opt_simple(sb, opt, /*remount*/1, given);
++
++ /* disable it temporary */
++ dlgt = au_flag_test(sb, AuFlag_DLGT);
++ au_flag_clr(sb, AuFlag_DLGT);
++
++ if (!err)
++ err = au_do_opt_br(sb, opt, /*remount*/1, do_refresh);
++ if (!err)
++ err = au_do_opt_xino(sb, opt, /*remount*/1, &opt_xino);
++
++ /* restore it */
++ au_flag_set(sb, dlgt);
++ opt++;
++ }
++ if (err > 0)
++ err = 0;
++ TraceErr(err);
++
++ /* go on if err */
++
++ //todo: test this error case.
++ au_flag_clr(sb, AuFlag_DLGT);
++ rerr = verify_opts(sb, /*remount*/1);
++ au_flag_set(sb, dlgt);
++
++ /* they are handled by the caller */
++ if (!*do_refresh)
++ *do_refresh = !!((*given & AuMask_UDBA)
++ || au_flag_test(sb, AuFlag_XINO));
++
++ TraceErr(err);
++ return err;
++}
+diff --git a/fs/aufs/opts.h b/fs/aufs/opts.h
+new file mode 100755
+index 0000000..16c1a6a
+--- /dev/null
++++ b/fs/aufs/opts.h
+@@ -0,0 +1,96 @@
++/*
++ * 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: opts.h,v 1.13 2007/05/14 06:27:18 sfjro Exp $ */
++
++#ifndef __AUFS_OPTS_H__
++#define __AUFS_OPTS_H__
++
++#ifdef __KERNEL__
++
++#include <linux/fs.h>
++#include <linux/namei.h>
++#include <linux/version.h>
++#include <linux/aufs_type.h>
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
++typedef const char* au_parser_pattern_t;
++#else
++typedef char* au_parser_pattern_t;
++#endif
++
++/* ---------------------------------------------------------------------- */
++
++struct opt_add {
++ aufs_bindex_t bindex;
++ char *path;
++ int perm;
++ struct nameidata nd;
++};
++
++struct opt_del {
++ char *path;
++ struct dentry *h_root;
++};
++
++struct opt_mod {
++ char *path;
++ int perm;
++ struct dentry *h_root;
++};
++
++struct opt_xino {
++ char *path;
++ struct file *file;
++};
++
++struct opt {
++ int type;
++ union {
++ struct opt_xino xino;
++ struct opt_add add;
++ struct opt_del del;
++ struct opt_mod mod;
++ int dirwh;
++ int rdcache;
++ int deblk;
++ int nhash;
++ int udba;
++ int coo;
++ };
++};
++
++struct opts {
++ struct opt *opt;
++ int max_opt;
++};
++
++/* ---------------------------------------------------------------------- */
++
++int br_perm_str(char *p, unsigned int len, int brperm);
++au_parser_pattern_t udba_str(int udba);
++void udba_set(struct super_block *sb, unsigned int flg);
++//au_parser_pattern_t coo_str(int coo);
++void au_free_opts(struct opts *opts);
++int au_parse_opts(struct super_block *sb, char *str, struct opts *opts);
++int au_do_opts_mount(struct super_block *sb, struct opts *opts);
++int au_do_opts_remount(struct super_block *sb, struct opts *opts,
++ int *do_refresh, unsigned int *given);
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_OPTS_H__ */
+diff --git a/fs/aufs/plink.c b/fs/aufs/plink.c
+new file mode 100755
+index 0000000..0e520af
+--- /dev/null
++++ b/fs/aufs/plink.c
+@@ -0,0 +1,331 @@
++/*
++ * 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: plink.c,v 1.4 2007/05/14 03:39:10 sfjro Exp $ */
++
++#include "aufs.h"
++
++struct pseudo_link {
++ struct list_head list;
++ struct inode *inode;
++};
++
++#ifdef CONFIG_AUFS_DEBUG
++void au_list_plink(struct super_block *sb)
++{
++ struct aufs_sbinfo *sbinfo;
++ struct list_head *plink_list;
++ struct pseudo_link *plink;
++
++ TraceEnter();
++ SiMustAnyLock(sb);
++ DEBUG_ON(!au_flag_test(sb, AuFlag_PLINK));
++
++ sbinfo = stosi(sb);
++ plink_list = &sbinfo->si_plink;
++ spin_lock(&sbinfo->si_plink_lock);
++ list_for_each_entry(plink, plink_list, list)
++ Dbg("%lu\n", plink->inode->i_ino);
++ spin_unlock(&sbinfo->si_plink_lock);
++}
++#endif
++
++int au_is_plinked(struct super_block *sb, struct inode *inode)
++{
++ int found;
++ struct aufs_sbinfo *sbinfo;
++ struct list_head *plink_list;
++ struct pseudo_link *plink;
++
++ LKTRTrace("i%lu\n", inode->i_ino);
++ SiMustAnyLock(sb);
++ DEBUG_ON(!au_flag_test(sb, AuFlag_PLINK));
++
++ found = 0;
++ sbinfo = stosi(sb);
++ 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);
++ DEBUG_ON(len != PLINK_NAME_LEN);
++ h_inode = au_h_iptr_i(inode, bindex);
++ DEBUG_ON(!h_inode);
++ rlen = snprintf(name, len, "%lu.%lu", inode->i_ino, h_inode->i_ino);
++ DEBUG_ON(rlen >= len);
++ return rlen;
++}
++
++struct dentry *lkup_plink(struct super_block *sb, aufs_bindex_t bindex,
++ struct inode *inode)
++{
++ struct dentry *h_dentry, *h_parent;
++ struct aufs_branch *br;
++ struct inode *h_dir;
++ char tgtname[PLINK_NAME_LEN];
++ int len;
++ struct lkup_args lkup;
++
++ LKTRTrace("b%d, i%lu\n", bindex, inode->i_ino);
++ br = stobr(sb, bindex);
++ h_parent = br->br_plink;
++ DEBUG_ON(!h_parent);
++ h_dir = h_parent->d_inode;
++ DEBUG_ON(!h_dir);
++
++ len = plink_name(tgtname, sizeof(tgtname), inode, bindex);
++
++ // always superio.
++ lkup.nfsmnt = au_do_nfsmnt(br->br_mnt);
++ lkup.dlgt = need_dlgt(sb);
++ hi_lock_whplink(h_dir);
++ h_dentry = sio_lkup_one(tgtname, h_parent, len, &lkup);
++ i_unlock(h_dir);
++ 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;
++ struct dentry *h_tgt;
++ struct inode *h_dir;
++ struct lkup_args lkup = {
++ .nfsmnt = nfsmnt,
++ .dlgt = need_dlgt(sb)
++ };
++
++ h_tgt = lkup_one(tgt, h_parent, len, &lkup);
++ err = PTR_ERR(h_tgt);
++ if (IS_ERR(h_tgt))
++ goto out;
++
++ err = 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, lkup.dlgt);
++ if (!err && !h_tgt->d_inode) {
++ err = vfsub_link(h_dentry, h_dir, h_tgt, lkup.dlgt);
++ //inode->i_nlink++;
++ }
++ dput(h_tgt);
++
++ out:
++ TraceErr(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;
++ struct aufs_branch *br;
++ struct dentry *h_parent;
++ struct inode *h_dir;
++ char tgtname[PLINK_NAME_LEN];
++
++ LKTRTrace("%.*s\n", DLNPair(h_dentry));
++ br = stobr(inode->i_sb, bindex);
++ h_parent = br->br_plink;
++ DEBUG_ON(!h_parent);
++ h_dir = h_parent->d_inode;
++ DEBUG_ON(!h_dir);
++
++ len = plink_name(tgtname, sizeof(tgtname), inode, bindex);
++
++ // always superio.
++ hi_lock_whplink(h_dir);
++ if (!is_au_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
++ };
++ au_wkq_wait(call_do_whplink, &args, /*dlgt*/0);
++ } else
++ err = do_whplink(tgtname, len, h_parent, h_dentry,
++ au_do_nfsmnt(br->br_mnt), sb);
++ i_unlock(h_dir);
++
++ TraceErr(err);
++ return err;
++}
++
++void append_plink(struct super_block *sb, struct inode *inode,
++ struct dentry *h_dentry, aufs_bindex_t bindex)
++{
++ struct aufs_sbinfo *sbinfo;
++ struct list_head *plink_list;
++ struct pseudo_link *plink;
++ int found, err, cnt;
++
++ LKTRTrace("i%lu\n", inode->i_ino);
++ SiMustAnyLock(sb);
++ DEBUG_ON(!au_flag_test(sb, AuFlag_PLINK));
++
++ cnt = 0;
++ found = 0;
++ sbinfo = stosi(sb);
++ 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) {
++ struct pseudo_link *plink;
++
++ 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 > 100))
++ Warn1("unexpectedly many pseudo links, %d\n", cnt);
++ if (unlikely(err))
++ Warn("err %d, damaged pseudo link. ignored.\n", err);
++}
++
++static void do_put_plink(struct pseudo_link *plink, int do_del)
++{
++ TraceEnter();
++
++ iput(plink->inode);
++ if (do_del)
++ list_del(&plink->list);
++ kfree(plink);
++}
++
++void au_put_plink(struct super_block *sb)
++{
++ struct aufs_sbinfo *sbinfo;
++ struct list_head *plink_list;
++ struct pseudo_link *plink, *tmp;
++
++ TraceEnter();
++ SiMustWriteLock(sb);
++ DEBUG_ON(!au_flag_test(sb, AuFlag_PLINK));
++
++ sbinfo = stosi(sb);
++ 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 half_refresh_plink(struct super_block *sb, aufs_bindex_t br_id)
++{
++ struct aufs_sbinfo *sbinfo;
++ struct list_head *plink_list;
++ struct pseudo_link *plink, *tmp;
++ struct inode *inode;
++ aufs_bindex_t bstart, bend, bindex;
++ int do_put;
++
++ TraceEnter();
++ SiMustWriteLock(sb);
++ DEBUG_ON(!au_flag_test(sb, AuFlag_PLINK));
++
++ sbinfo = stosi(sb);
++ 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 = ibstart(inode);
++ bend = ibend(inode);
++ if (bstart >= 0) {
++ for (bindex = bstart; bindex <= bend; bindex++) {
++ if (!au_h_iptr_i(inode, bindex)
++ || itoid_index(inode, bindex) != br_id)
++ continue;
++ 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_i(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);
++}
+diff --git a/fs/aufs/sbinfo.c b/fs/aufs/sbinfo.c
+new file mode 100755
+index 0000000..55cb64c
+--- /dev/null
++++ b/fs/aufs/sbinfo.c
+@@ -0,0 +1,173 @@
++/*
++ * 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: sbinfo.c,v 1.30 2007/05/14 03:39:31 sfjro Exp $ */
++
++#include "aufs.h"
++
++struct aufs_sbinfo *stosi(struct super_block *sb)
++{
++ struct aufs_sbinfo *sbinfo;
++ sbinfo = sb->s_fs_info;
++ //DEBUG_ON(sbinfo->si_bend < 0);
++ return sbinfo;
++}
++
++aufs_bindex_t sbend(struct super_block *sb)
++{
++ SiMustAnyLock(sb);
++ return stosi(sb)->si_bend;
++}
++
++struct aufs_branch *stobr(struct super_block *sb, aufs_bindex_t bindex)
++{
++ SiMustAnyLock(sb);
++ DEBUG_ON(bindex < 0 || sbend(sb) < bindex
++ || !stosi(sb)->si_branch[0 + bindex]);
++ return stosi(sb)->si_branch[0 + bindex];
++}
++
++int au_sigen(struct super_block *sb)
++{
++ SiMustAnyLock(sb);
++ return stosi(sb)->si_generation;
++}
++
++int au_sigen_inc(struct super_block *sb)
++{
++ int gen;
++
++ SiMustWriteLock(sb);
++ gen = ++stosi(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 find_bindex(struct super_block *sb, struct aufs_branch *br)
++{
++ aufs_bindex_t bindex, bend;
++
++ bend = sbend(sb);
++ for (bindex = 0; bindex <= bend; bindex++)
++ if (stobr(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);
++ if (flags & AUFS_D_WLOCK)
++ di_write_lock_child(dentry);
++ else
++ di_read_lock_child(dentry, flags);
++}
++
++void aufs_read_unlock(struct dentry *dentry, int flags)
++{
++ if (flags & AUFS_D_WLOCK)
++ di_write_unlock(dentry);
++ else
++ di_read_unlock(dentry, flags);
++ si_read_unlock(dentry->d_sb);
++}
++
++void aufs_write_lock(struct dentry *dentry)
++{
++ //au_wkq_wait_nwtask();
++ 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);
++ //au_wkq_wait_nwtask();
++}
++
++void aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int isdir)
++{
++ DEBUG_ON(d1 == d2 || d1->d_sb != d2->d_sb);
++ si_read_lock(d1->d_sb);
++ di_write_lock2_child(d1, d2, isdir);
++}
++
++void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2)
++{
++ DEBUG_ON(d1 == d2 || d1->d_sb != d2->d_sb);
++ di_write_unlock2(d1, d2);
++ si_read_unlock(d1->d_sb);
++}
++
++/* ---------------------------------------------------------------------- */
++
++aufs_bindex_t new_br_id(struct super_block *sb)
++{
++ aufs_bindex_t br_id;
++
++ TraceEnter();
++ SiMustWriteLock(sb);
++
++ while (1) {
++ br_id = ++stosi(sb)->si_last_br_id;
++ if (br_id && find_brindex(sb, br_id) < 0)
++ return br_id;
++ }
++}
++
++/* ---------------------------------------------------------------------- */
++
++#ifdef CONFIG_AUFS_SYSAUFS
++static int make_xino(struct seq_file *seq, struct sysaufs_args *args,
++ int *do_size)
++{
++ int err;
++ struct super_block *sb = args->sb;
++ aufs_bindex_t bindex, bend;
++ struct file *xf;
++ struct inode *xi;
++
++ TraceEnter();
++ DEBUG_ON(args->index != SysaufsSb_XINO);
++ SiMustReadLock(sb);
++
++ *do_size = 0;
++ err = seq_printf(seq, "%d %lu\n", sizeof(struct xino),
++ atomic_long_read(&stosi(sb)->si_xino));
++ bend = sbend(sb);
++ for (bindex = 0; !err && bindex <= bend; bindex++) {
++ xf = stobr(sb, bindex)->br_xino;
++ xi = xf->f_dentry->d_inode;
++ err = seq_printf(seq, "%d: %d, %Lux%d %Ld\n",
++ bindex, file_count(xf),
++ (u64)xi->i_blocks, 1 << xi->i_blkbits,
++ i_size_read(xi));
++ }
++ return err;
++}
++
++sysaufs_op au_si_ops[] = {
++ [SysaufsSb_XINO] = make_xino
++};
++#endif
+diff --git a/fs/aufs/super.c b/fs/aufs/super.c
+new file mode 100755
+index 0000000..c1123f8
+--- /dev/null
++++ b/fs/aufs/super.c
+@@ -0,0 +1,716 @@
++/*
++ * 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: super.c,v 1.50 2007/05/14 03:39:42 sfjro Exp $ */
++
++#include <linux/module.h>
++#include <linux/seq_file.h>
++#include <linux/statfs.h>
++#include "aufs.h"
++
++/*
++ * super_operations
++ */
++static struct inode *aufs_alloc_inode(struct super_block *sb)
++{
++ struct aufs_icntnr *c;
++
++ TraceEnter();
++
++ c = cache_alloc_icntnr();
++ //if (LktrCond) {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);
++ cache_free_icntnr(container_of(inode, struct aufs_icntnr, vfs_inode));
++}
++
++//todo: how about merge with alloc_inode()?
++static void aufs_read_inode(struct inode *inode)
++{
++ int err;
++
++ LKTRTrace("i%lu\n", inode->i_ino);
++
++ 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;
++ return; /* success */
++ }
++
++ LKTRTrace("intializing inode info failed(%d)\n", err);
++ make_bad_inode(inode);
++}
++
++int au_show_brs(struct seq_file *seq, struct super_block *sb)
++{
++ int err;
++ aufs_bindex_t bindex, bend;
++ char a[16];
++ struct dentry *root;
++
++ TraceEnter();
++ SiMustAnyLock(sb);
++ root = sb->s_root;
++ DiMustAnyLock(root);
++
++ err = 0;
++ bend = sbend(sb);
++ for (bindex = 0; !err && bindex <= bend; bindex++) {
++ err = br_perm_str(a, sizeof(a), sbr_perm(sb, bindex));
++ if (!err)
++ err = seq_path(seq, sbr_mnt(sb, bindex),
++ au_h_dptr_i(root, bindex), au_esc_chars);
++ if (err > 0)
++ err = seq_printf(seq, "=%s", a);
++ if (!err && bindex != bend)
++ err = seq_putc(seq, ':');
++ }
++
++ TraceErr(err);
++ return err;
++}
++
++static int aufs_show_options(struct seq_file *m, struct vfsmount *mnt)
++{
++ int err, n;
++ struct super_block *sb;
++ struct aufs_sbinfo *sbinfo;
++ struct dentry *root;
++ struct file *xino;
++
++ TraceEnter();
++
++ sb = mnt->mnt_sb;
++ root = sb->s_root;
++ aufs_read_lock(root, !AUFS_I_RLOCK);
++ if (au_flag_test(sb, AuFlag_XINO)) {
++ err = seq_puts(m, ",xino=");
++ if (unlikely(err))
++ goto out;
++ xino = stobr(sb, 0)->br_xino;
++ err = seq_path(m, xino->f_vfsmnt, xino->f_dentry, au_esc_chars);
++ if (unlikely(err <= 0))
++ goto out;
++ err = 0;
++
++#define Deleted "\\040(deleted)"
++ m->count -= sizeof(Deleted) - 1;
++ DEBUG_ON(memcmp(m->buf + m->count, Deleted,
++ sizeof(Deleted) - 1));
++#undef Deleted
++ } else
++ err = seq_puts(m, ",noxino");
++
++ n = au_flag_test(sb, AuFlag_PLINK);
++ if (unlikely(!err && (AuDefFlags & AuFlag_PLINK) != n))
++ err = seq_printf(m, ",%splink", n ? "" : "no");
++ n = au_flag_test_udba(sb);
++ if (unlikely(!err && (AuDefFlags & AuMask_UDBA) != n))
++ err = seq_printf(m, ",udba=%s", udba_str(n));
++ n = au_flag_test(sb, AuFlag_ALWAYS_DIROPQ);
++ if (unlikely(!err && (AuDefFlags & AuFlag_ALWAYS_DIROPQ) != n))
++ err = seq_printf(m, ",diropq=%c", n ? 'a' : 'w');
++ n = au_flag_test(sb, AuFlag_DLGT);
++ if (unlikely(!err && (AuDefFlags & AuFlag_DLGT) != n))
++ err = seq_printf(m, ",%sdlgt", n ? "" : "no");
++ n = au_flag_test(sb, AuFlag_WARN_PERM);
++ if (unlikely(!err && (AuDefFlags & AuFlag_WARN_PERM) != n))
++ err = seq_printf(m, ",%swarn_perm", n ? "" : "no");
++
++ sbinfo = stosi(sb);
++ n = sbinfo->si_dirwh;
++ if (unlikely(!err && n != AUFS_DIRWH_DEF))
++ err = seq_printf(m, ",dirwh=%d", n);
++ n = sbinfo->si_rdcache / HZ;
++ if (unlikely(!err && n != AUFS_RDCACHE_DEF))
++ err = seq_printf(m, ",rdcache=%d", n);
++#if 0
++ n = au_flag_test_coo(sb);
++ if (unlikely(!err && (AuDefFlags & AuMask_COO) != n))
++ err = seq_printf(m, ",coo=%s", coo_str(n));
++#endif
++
++ if (!err && !sysaufs_brs) {
++#ifdef CONFIG_AUFS_COMPAT
++ err = seq_puts(m, ",dirs=");
++#else
++ err = seq_puts(m, ",br:");
++#endif
++ if (!err)
++ err = au_show_brs(m, sb);
++ }
++
++ out:
++ aufs_read_unlock(root, !AUFS_I_RLOCK);
++ TraceErr(err);
++ if (err)
++ err = -E2BIG;
++ TraceErr(err);
++ return err;
++}
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
++#define StatfsLock(d) aufs_read_lock((d)->d_sb->s_root, 0)
++#define StatfsUnlock(d) aufs_read_unlock((d)->d_sb->s_root, 0)
++#define StatfsArg(d) au_h_dptr((d)->d_sb->s_root)
++#define StatfsHInode(d) (StatfsArg(d)->d_inode)
++#define StatfsSb(d) ((d)->d_sb)
++static int aufs_statfs(struct dentry *arg, struct kstatfs *buf)
++#else
++#define StatfsLock(s) si_read_lock(s)
++#define StatfsUnlock(s) si_read_unlock(s)
++#define StatfsArg(s) sbr_sb(s, 0)
++#define StatfsHInode(s) (StatfsArg(s)->s_root->d_inode)
++#define StatfsSb(s) (s)
++static int aufs_statfs(struct super_block *arg, struct kstatfs *buf)
++#endif
++{
++ int err;
++
++ TraceEnter();
++
++ StatfsLock(arg);
++ err = vfsub_statfs(StatfsArg(arg), buf, need_dlgt(StatfsSb(arg)));
++ //if (LktrCond) err = -1;
++ StatfsUnlock(arg);
++ if (!err) {
++ //buf->f_type = AUFS_SUPER_MAGIC;
++ buf->f_type = 0;
++ buf->f_namelen -= AUFS_WH_PFX_LEN;
++ memset(&buf->f_fsid, 0, sizeof(buf->f_fsid));
++ }
++ //buf->f_bsize = buf->f_blocks = buf->f_bfree = buf->f_bavail = -1;
++
++ TraceErr(err);
++ return err;
++}
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) || defined(UbuntuEdgy17Umount18)
++#define UmountBeginSb(mnt) (mnt)->mnt_sb
++static void aufs_umount_begin(struct vfsmount *arg, int flags)
++#else
++#define UmountBeginSb(sb) sb
++static void aufs_umount_begin(struct super_block *arg)
++#endif
++{
++ struct super_block *sb = UmountBeginSb(arg);
++
++ if (unlikely(!stosi(sb)))
++ return;
++
++ //au_wkq_wait_nwtask();
++ si_write_lock(sb);
++ if (au_flag_test(sb, AuFlag_PLINK)) {
++ au_put_plink(sb);
++ //kobj_umount(stosi(sb));
++ }
++#if 0
++ if (unlikely(au_flag_test(sb, AuFlag_UDBA_INOTIFY)))
++ shrink_dcache_sb(sb);
++#endif
++ si_write_unlock(sb);
++}
++
++static void free_sbinfo(struct aufs_sbinfo *sbinfo)
++{
++ TraceEnter();
++ DEBUG_ON(!sbinfo
++ || !list_empty(&sbinfo->si_plink));
++
++ free_branches(sbinfo);
++ kfree(sbinfo->si_branch);
++ kfree(sbinfo);
++}
++
++/* final actions when unmounting a file system */
++static void aufs_put_super(struct super_block *sb)
++{
++ struct aufs_sbinfo *sbinfo;
++
++ TraceEnter();
++
++ sbinfo = stosi(sb);
++ if (unlikely(!sbinfo))
++ return;
++
++ sysaufs_del(sbinfo);
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) && !defined(UbuntuEdgy17Umount18)
++ // umount_begin() may not be called.
++ aufs_umount_begin(sb);
++#endif
++ free_sbinfo(sbinfo);
++}
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * refresh directories at remount time.
++ */
++static int do_refresh_dir(struct dentry *dentry, unsigned int flags)
++{
++ int err;
++ struct dentry *parent;
++ struct inode *inode;
++
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++ inode = dentry->d_inode;
++ DEBUG_ON(!inode || !S_ISDIR(inode->i_mode));
++
++ di_write_lock_child(dentry);
++ parent = dget_parent(dentry);
++ di_read_lock_parent(parent, AUFS_I_RLOCK);
++ err = au_refresh_hdentry(dentry, S_IFDIR);
++ if (err >= 0) {
++ err = au_refresh_hinode(inode, dentry);
++ if (!err)
++ au_reset_hinotify(inode, flags);
++ }
++ if (unlikely(err))
++ Err("unrecoverable error %d\n", err);
++ di_read_unlock(parent, AUFS_I_RLOCK);
++ dput(parent);
++ di_write_unlock(dentry);
++
++ TraceErr(err);
++ return err;
++}
++
++static int test_dir(struct dentry *dentry, void *arg)
++{
++ return S_ISDIR(dentry->d_inode->i_mode);
++}
++
++static int refresh_dir(struct dentry *root, int sgen)
++{
++ int err, i, j, ndentry;
++ const unsigned int flags = au_hi_flags(root->d_inode, /*isdir*/1);
++ struct au_dcsub_pages dpages;
++ struct au_dpage *dpage;
++ struct dentry **dentries;
++
++ LKTRTrace("sgen %d\n", sgen);
++ SiMustWriteLock(root->d_sb);
++ DEBUG_ON(au_digen(root) != sgen);
++ DiMustWriteLock(root);
++
++ err = au_dpages_init(&dpages, GFP_KERNEL);
++ if (unlikely(err))
++ goto out;
++ err = au_dcsub_pages(&dpages, root, test_dir, NULL);
++ if (unlikely(err))
++ goto out_dpages;
++
++ DiMustNoWaiters(root);
++ IiMustNoWaiters(root->d_inode);
++ di_write_unlock(root);
++ for (i = 0; !err && i < dpages.ndpage; i++) {
++ dpage = dpages.dpages + i;
++ dentries = dpage->dentries;
++ ndentry = dpage->ndentry;
++ for (j = 0; !err && j < ndentry; j++) {
++ struct dentry *d;
++ d = dentries[j];
++ DEBUG_ON(!S_ISDIR(d->d_inode->i_mode)
++ || IS_ROOT(d)
++ || au_digen(d->d_parent) != sgen);
++ if (au_digen(d) != sgen)
++ err = do_refresh_dir(d, flags);
++ }
++ }
++ di_write_lock_child(root); /* aufs_write_lock() calls ..._child() */
++
++ out_dpages:
++ au_dpages_free(&dpages);
++ out:
++ TraceErr(err);
++ return err;
++}
++
++/* stop extra interpretation of errno in mount(8), and strange error messages */
++static int cvt_err(int err)
++{
++ TraceErr(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, do_refresh;
++ struct dentry *root;
++ struct inode *inode;
++ struct opts opts;
++ unsigned int given, dlgt;
++
++ //au_debug_on();
++ LKTRTrace("flags 0x%x, data %s, len %d\n",
++ *flags, data ? data : "NULL", data ? strlen(data) : 0);
++
++ err = 0;
++ if (unlikely(!data || !*data))
++ goto out; /* success */
++
++ err = -ENOMEM;
++ memset(&opts, 0, sizeof(opts));
++ opts.opt = (void*)__get_free_page(GFP_KERNEL);
++ //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);
++
++ /* parse it before aufs lock */
++ err = au_parse_opts(sb, data, &opts);
++ //if (LktrCond) {au_free_opts(&opts); err = -1;}
++ if (unlikely(err))
++ goto out_opts;
++
++ root = sb->s_root;
++ inode = root->d_inode;
++ i_lock(inode);
++ aufs_write_lock(root);
++
++ //DbgSleep(3);
++
++ /* au_do_opts() may return an error */
++ do_refresh = 0;
++ given = 0;
++ err = au_do_opts_remount(sb, &opts, &do_refresh, &given);
++ //if (LktrCond) err = -1;
++ au_free_opts(&opts);
++
++ if (do_refresh) {
++ int rerr;
++ struct aufs_sbinfo *sbinfo;
++
++ dlgt = au_flag_test(sb, AuFlag_DLGT);
++ au_flag_clr(sb, AuFlag_DLGT);
++ au_sigen_inc(sb);
++ au_reset_hinotify(inode, au_hi_flags(inode, /*isdir*/1));
++ sbinfo = stosi(sb);
++ sbinfo->si_failed_refresh_dirs = 0;
++ rerr = refresh_dir(root, au_sigen(sb));
++ if (unlikely(rerr)) {
++ sbinfo->si_failed_refresh_dirs = 1;
++ Warn("Refreshing directories failed, ignores (%d)\n",
++ rerr);
++ }
++ au_cpup_attr_all(inode);
++ au_flag_set(sb, dlgt);
++ }
++
++ aufs_write_unlock(root);
++ i_unlock(inode);
++ /* braces are added to stop a warning */
++ if (do_refresh) {
++ sysaufs_notify_remount();
++ }
++
++ out_opts:
++ free_page((unsigned long)opts.opt);
++ out:
++ err = cvt_err(err);
++ TraceErr(err);
++ //au_debug_off();
++ return err;
++}
++
++static struct super_operations aufs_sop = {
++ .alloc_inode = aufs_alloc_inode,
++ .destroy_inode = aufs_destroy_inode,
++ .read_inode = aufs_read_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
++};
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * at first mount time.
++ */
++
++static int alloc_sbinfo(struct super_block *sb)
++{
++ struct aufs_sbinfo *sbinfo;
++
++ TraceEnter();
++
++ 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)) {
++ kfree(sbinfo);
++ goto out;
++ }
++ rw_init_wlock(&sbinfo->si_rwsem);
++ sbinfo->si_bend = -1;
++ atomic_long_set(&sbinfo->si_xino, AUFS_FIRST_INO);
++ spin_lock_init(&sbinfo->si_plink_lock);
++ INIT_LIST_HEAD(&sbinfo->si_plink);
++ init_lvma(sbinfo);
++ sbinfo->si_generation = 0;
++ sbinfo->si_last_br_id = 0;
++ sbinfo->si_failed_refresh_dirs = 0;
++ sbinfo->si_flags = 0;
++ sbinfo->si_dirwh = AUFS_DIRWH_DEF;
++ sbinfo->si_rdcache = AUFS_RDCACHE_DEF * HZ;
++ //atomic_set(&sbinfo->si_hinotify, 0);
++ //init_waitqueue_head(&sbinfo->si_hinotify_wq);
++
++ sb->s_fs_info = sbinfo;
++ au_flag_set(sb, AuDefFlags);
++#ifdef ForceInotify
++ udba_set(sb, AuFlag_UDBA_INOTIFY);
++#endif
++#ifdef ForceDlgt
++ au_flag_set(sb, AuFlag_DLGT);
++#endif
++#ifdef ForceNoPlink
++ au_flag_clr(sb, AuFlag_PLINK);
++#endif
++ return 0; /* success */
++
++ out:
++ TraceErr(-ENOMEM);
++ return -ENOMEM;
++}
++
++static int alloc_root(struct super_block *sb)
++{
++ int err;
++ struct inode *inode;
++ struct dentry *root;
++
++ TraceEnter();
++
++ err = -ENOMEM;
++ inode = iget(sb, AUFS_ROOT_INO);
++ //if (LktrCond) {iput(inode); inode = NULL;}
++ if (unlikely(!inode))
++ goto out;
++ err = PTR_ERR(inode);
++ if (IS_ERR(inode))
++ goto out;
++ err = -ENOMEM;
++ if (unlikely(is_bad_inode(inode)))
++ goto out_iput;
++
++ 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){rw_write_unlock(&dtodi(root)->di_rwsem);err=-1;}
++ if (!err) {
++ sb->s_root = root;
++ return 0; /* success */
++ }
++ dput(root);
++ goto out; /* do not iput */
++
++ out_iput:
++ iput(inode);
++ out:
++ TraceErr(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 opts opts;
++ char *arg = raw_data;
++
++ //au_debug_on();
++ if (unlikely(!arg || !*arg)) {
++ err = -EINVAL;
++ Err("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_KERNEL);
++ //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 = alloc_sbinfo(sb);
++ //if (LktrCond) {si_write_unlock(sb);free_sbinfo(stosi(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;
++ au_init_export_op(sb);
++ //err = kobj_mount(stosi(sb));
++ //if (err)
++ //goto out_info;
++
++ err = alloc_root(sb);
++ //if (LktrCond) {rw_write_unlock(&dtodi(sb->s_root)->di_rwsem);
++ //dput(sb->s_root);sb->s_root=NULL;err=-1;}
++ if (unlikely(err)) {
++ DEBUG_ON(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_parse_opts(sb, arg, &opts);
++ //if (LktrCond) {au_free_opts(&opts); err = -1;}
++ if (unlikely(err))
++ goto out_root;
++
++ /* lock vfs_inode first, then aufs. */
++ i_lock(inode);
++ inode->i_op = &aufs_dir_iop;
++ inode->i_fop = &aufs_dir_fop;
++ aufs_write_lock(root);
++
++ sb->s_maxbytes = 0;
++ err = au_do_opts_mount(sb, &opts);
++ //if (LktrCond) err = -1;
++ au_free_opts(&opts);
++ if (unlikely(err))
++ goto out_unlock;
++ DEBUG_ON(!sb->s_maxbytes);
++
++ //DbgDentry(root);
++ aufs_write_unlock(root);
++ i_unlock(inode);
++ //DbgSb(sb);
++ goto out_opts; /* success */
++
++ out_unlock:
++ aufs_write_unlock(root);
++ i_unlock(inode);
++ out_root:
++ dput(root);
++ sb->s_root = NULL;
++ out_info:
++ free_sbinfo(stosi(sb));
++ sb->s_fs_info = NULL;
++ out_opts:
++ free_page((unsigned long)opts.opt);
++ out:
++ TraceErr(err);
++ err = cvt_err(err);
++ TraceErr(err);
++ //au_debug_off();
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
++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 aufs_sbinfo *sbinfo = stosi(mnt->mnt_sb);
++ sbinfo->si_mnt = mnt;
++ sysaufs_add(sbinfo);
++ }
++ return err;
++}
++#else
++static struct super_block *aufs_get_sb(struct file_system_type *fs_type,
++ int flags, const char *dev_name,
++ void *raw_data)
++{
++ return get_sb_nodev(fs_type, flags, raw_data, aufs_fill_super);
++}
++#endif
++
++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,
++ //no need to __module_get() and module_put().
++ .owner = THIS_MODULE,
++};
+diff --git a/fs/aufs/super.h b/fs/aufs/super.h
+new file mode 100755
+index 0000000..56ddee1
+--- /dev/null
++++ b/fs/aufs/super.h
+@@ -0,0 +1,339 @@
++/*
++ * 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: super.h,v 1.44 2007/05/14 03:39:54 sfjro Exp $ */
++
++#ifndef __AUFS_SUPER_H__
++#define __AUFS_SUPER_H__
++
++#ifdef __KERNEL__
++
++#include <linux/fs.h>
++#include <linux/version.h>
++#include <linux/aufs_type.h>
++#include "misc.h"
++#include "sysaufs.h"
++
++#ifdef CONFIG_AUFS_SYSAUFS
++/* entries under sysfs per mount-point */
++enum {SysaufsSb_XINO, /* SysaufsSb_PLINK, */ SysaufsSb_Last};
++struct sysaufs_sbinfo {
++ au_subsys_t subsys;
++ struct sysaufs_entry array[SysaufsSb_Last];
++};
++extern sysaufs_op au_si_ops[];
++#else
++struct sysaufs_sbinfo {};
++#endif
++
++struct aufs_sbinfo {
++ struct aufs_rwsem si_rwsem;
++
++ /* branch management */
++ /* wrap around attack by superuser? No. */
++ int si_generation;
++
++ /*
++ * set true when refresh_dirs() at remount time failed.
++ * then try refreshing dirs at access time again.
++ * if it is false, refreshing dirs at access time is unnecesary
++ */
++ unsigned int si_failed_refresh_dirs:1;
++
++ aufs_bindex_t si_bend;
++ aufs_bindex_t si_last_br_id;
++ struct aufs_branch **si_branch;
++
++ /* mount flags */
++ unsigned int si_flags;
++
++ /* external inode number table */
++ atomic_long_t si_xino; // time bomb
++ //struct file *si_xino_bmap;
++
++ /* 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 rename_whtmp 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;
++
++ /* pseudo_link list */ // dirty
++ spinlock_t si_plink_lock;
++ struct list_head si_plink;
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
++ /* super_blocks list is not exported */
++ struct list_head si_list;
++ struct vfsmount *si_mnt; /* no get/put */
++#endif
++
++ /* sysfs */
++ struct sysaufs_sbinfo si_sysaufs;
++
++#ifdef CONFIG_AUFS_HINOTIFY
++ /* hinotify */
++ //atomic_t si_hinotify;
++ //wait_queue_head_t si_hinotify_wq;
++#endif
++
++#ifdef CONFIG_AUFS_ROBR
++ /* locked vma list for mmap() */ // very dirty
++ spinlock_t si_lvma_lock;
++ struct list_head si_lvma;
++#endif
++};
++
++/* an entry in a xino file */
++struct xino {
++ ino_t ino;
++ //__u32 h_gen;
++} __attribute__ ((packed));
++
++//#define AuXino_INVALID_HGEN (-1)
++
++/* ---------------------------------------------------------------------- */
++
++/* Mount flags */
++#define AuFlag_XINO 1
++#define AuFlag_ZXINO (1 << 1)
++#define AuFlag_PLINK (1 << 2)
++#define AuFlag_UDBA_NONE (1 << 3)
++#define AuFlag_UDBA_REVAL (1 << 4)
++#define AuFlag_UDBA_INOTIFY (1 << 5)
++#define AuFlag_WARN_PERM (1 << 6)
++#define AuFlag_COO_NONE (1 << 7)
++#define AuFlag_COO_LEAF (1 << 8)
++#define AuFlag_COO_ALL (1 << 9)
++#define AuFlag_ALWAYS_DIROPQ (1 << 10)
++#define AuFlag_DLGT (1 << 11)
++
++#define AuMask_UDBA (AuFlag_UDBA_NONE | AuFlag_UDBA_REVAL \
++ | AuFlag_UDBA_INOTIFY)
++#define AuMask_COO (AuFlag_COO_NONE | AuFlag_COO_LEAF \
++ | AuFlag_COO_ALL)
++
++#ifdef CONFIG_AUFS_COMPAT
++#define AuDefFlag_DIROPQ AuFlag_ALWAYS_DIROPQ
++#else
++#define AuDefFlag_DIROPQ 0
++#endif
++
++#define AuDefFlags_COMM (AuFlag_XINO | AuFlag_UDBA_REVAL | AuFlag_WARN_PERM \
++ | AuFlag_COO_NONE | AuDefFlag_DIROPQ)
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
++#define AuDefFlags (AuDefFlags_COMM | AuFlag_PLINK)
++#else
++#define AuDefFlags AuDefFlags_COMM
++#endif
++
++/* ---------------------------------------------------------------------- */
++
++/* flags for aufs_read_lock()/di_read_lock() */
++#define AUFS_D_WLOCK 1
++#define AUFS_I_RLOCK 2
++#define AUFS_I_WLOCK 4
++
++/* ---------------------------------------------------------------------- */
++
++/* super.c */
++int au_show_brs(struct seq_file *seq, struct super_block *sb);
++
++/* xino.c */
++struct file *xino_create(struct super_block *sb, char *fname, int silent,
++ struct dentry *parent);
++ino_t xino_new_ino(struct super_block *sb);
++int xino_write0(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino);
++int xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
++ struct xino *xino);
++int xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
++ struct xino *xino);
++int xino_init(struct super_block *sb, aufs_bindex_t bindex,
++ struct file *base_file, int do_test);
++struct opt_xino;
++int xino_set(struct super_block *sb, struct opt_xino *xino, int remount);
++int xino_clr(struct super_block *sb);
++struct file *xino_def(struct super_block *sb);
++
++/* sbinfo.c */
++struct aufs_sbinfo *stosi(struct super_block *sb);
++aufs_bindex_t sbend(struct super_block *sb);
++struct aufs_branch *stobr(struct super_block *sb, aufs_bindex_t bindex);
++int au_sigen(struct super_block *sb);
++int au_sigen_inc(struct super_block *sb);
++int find_bindex(struct super_block *sb, struct aufs_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 new_br_id(struct super_block *sb);
++
++/* ---------------------------------------------------------------------- */
++
++static inline const char *au_sbtype(struct super_block *sb)
++{
++ return sb->s_type->name;
++}
++
++static inline int au_is_aufs(struct super_block *sb)
++{
++ return !strcmp(au_sbtype(sb), AUFS_FSTYPE);
++}
++
++static inline int au_is_nfs(struct super_block *sb)
++{
++#if defined(CONFIG_NFS_FS) || defined(CONFIG_NFS_FS_MODULE)
++ return !strcmp(au_sbtype(sb), "nfs");
++#else
++ return 0;
++#endif
++}
++
++static inline int au_is_remote(struct super_block *sb)
++{
++ return au_is_nfs(sb);
++}
++
++#ifdef CONFIG_AUFS_EXPORT
++static inline void au_init_export_op(struct super_block *sb)
++{
++ extern struct export_operations aufs_export_op;
++ sb->s_export_op = &aufs_export_op;
++}
++
++static inline int au_is_nfsd(struct task_struct *tsk)
++{
++ return (!tsk->mm && !strcmp(tsk->comm, "nfsd"));
++}
++
++static inline void au_nfsd_lockdep_off(void)
++{
++ /* braces are added to stop a warning */
++ if (au_is_nfsd(current)) {
++ lockdep_off();
++ }
++}
++
++static inline void au_nfsd_lockdep_on(void)
++{
++ /* braces are added to stop a warning */
++ if (au_is_nfsd(current)) {
++ lockdep_on();
++ }
++}
++#else
++static inline int au_is_nfsd(struct task_struct *tsk)
++{
++ return 0;
++}
++static inline void au_init_export_op(struct super_block *sb)
++{
++ /* nothing */
++}
++#define au_nfsd_lockdep_off() /* */
++#define au_nfsd_lockdep_on() /* */
++#endif /* CONFIG_AUFS_EXPORT */
++
++static inline void init_lvma(struct aufs_sbinfo *sbinfo)
++{
++#ifdef CONFIG_AUFS_ROBR
++ spin_lock_init(&sbinfo->si_lvma_lock);
++ INIT_LIST_HEAD(&sbinfo->si_lvma);
++#else
++ /* nothing */
++#endif
++}
++
++/* limited support before 2.6.18 */
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
++static inline void au_mntget(struct super_block *sb)
++{
++ mntget(stosi(sb)->si_mnt);
++}
++
++static inline void au_mntput(struct super_block *sb)
++{
++ mntput(stosi(sb)->si_mnt);
++}
++#else
++static inline void au_mntget(struct super_block *sb)
++{
++ /* empty */
++}
++
++static inline void au_mntput(struct super_block *sb)
++{
++ /* empty */
++}
++#endif
++
++/* ---------------------------------------------------------------------- */
++
++static inline void au_flag_set(struct super_block *sb, unsigned int flag)
++{
++ //SiMustWriteLock(sb);
++ stosi(sb)->si_flags |= flag;
++}
++
++static inline void au_flag_clr(struct super_block *sb, unsigned int flag)
++{
++ //SiMustWriteLock(sb);
++ stosi(sb)->si_flags &= ~flag;
++}
++
++static inline
++unsigned int au_flag_test(struct super_block *sb, unsigned int flag)
++{
++ //SiMustAnyLock(sb);
++ return stosi(sb)->si_flags & flag;
++}
++
++static inline unsigned int au_flag_test_udba(struct super_block *sb)
++{
++ return au_flag_test(sb, AuMask_UDBA);
++}
++
++static inline unsigned int au_flag_test_coo(struct super_block *sb)
++{
++ return au_flag_test(sb, AuMask_COO);
++}
++
++/* ---------------------------------------------------------------------- */
++
++/* lock superblock. mainly for entry point functions */
++/*
++ * si_read_lock, si_write_lock,
++ * si_read_unlock, si_write_unlock, si_downgrade_lock
++ */
++SimpleRwsemFuncs(si, struct super_block *sb, stosi(sb)->si_rwsem);
++
++/* to debug easier, do not make them inlined functions */
++#define SiMustReadLock(sb) RwMustReadLock(&stosi(sb)->si_rwsem)
++#define SiMustWriteLock(sb) RwMustWriteLock(&stosi(sb)->si_rwsem)
++#define SiMustAnyLock(sb) RwMustAnyLock(&stosi(sb)->si_rwsem)
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_SUPER_H__ */
+diff --git a/fs/aufs/sysaufs.c b/fs/aufs/sysaufs.c
+new file mode 100755
+index 0000000..d686862
+--- /dev/null
++++ b/fs/aufs/sysaufs.c
+@@ -0,0 +1,620 @@
++/*
++ * 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: sysaufs.c,v 1.6 2007/05/14 03:40:10 sfjro Exp $ */
++
++#include <linux/module.h>
++#include <linux/seq_file.h>
++#include <linux/sysfs.h>
++#include "aufs.h"
++
++/* ---------------------------------------------------------------------- */
++
++/* super_blocks list is not exported */
++static DEFINE_MUTEX(aufs_sbilist_mtx);
++static LIST_HEAD(aufs_sbilist);
++
++/* ---------------------------------------------------------------------- */
++
++typedef ssize_t (*rwfunc_t)(struct kobject *kobj, char *buf, loff_t offset,
++ size_t sz, struct sysaufs_args *args);
++static ssize_t sysaufs_read(struct kobject *kobj, char *buf, loff_t offset,
++ size_t sz, struct sysaufs_args *args);
++static ssize_t sysaufs_free_write(struct kobject *kobj, char *buf, loff_t
++ offset, size_t sz, struct sysaufs_args *args);
++
++#define GFunc(name, _index, func) \
++static ssize_t name(struct kobject *kobj, char *buf, loff_t offset, size_t sz) \
++{ \
++ struct sysaufs_args args = { \
++ .index = (_index), \
++ .mtx = &aufs_sbilist_mtx, \
++ .sb = NULL \
++ }; \
++ return func(kobj, buf, offset, sz, &args); \
++}
++
++#define GFuncs(name, _index) \
++ GFunc(read_##name, _index, sysaufs_read); \
++ GFunc(write_##name, _index, sysaufs_free_write);
++
++static struct super_block *find_sb_locked(struct kobject *kobj)
++{
++ struct super_block *sb;
++ struct aufs_sbinfo *sbinfo;
++
++ TraceEnter();
++ MtxMustLock(&aufs_sbilist_mtx);
++
++ sb = NULL;
++ list_for_each_entry(sbinfo, &aufs_sbilist, si_list) {
++ if (&au_subsys_to_kset(sbinfo->si_sysaufs.subsys).kobj != kobj)
++ continue;
++ sb = sbinfo->si_mnt->mnt_sb;
++ si_read_lock(sb);
++ break;
++ }
++ return sb;
++}
++
++static ssize_t sb_func(struct kobject *kobj, char *buf, loff_t offset,
++ size_t sz, struct sysaufs_args *args, rwfunc_t func)
++{
++ ssize_t err;
++
++ err = -ENOENT;
++ mutex_lock(&aufs_sbilist_mtx);
++ args->sb = find_sb_locked(kobj);
++ if (args->sb) {
++ err = func(kobj, buf, offset, sz, args);
++ si_read_unlock(args->sb);
++ }
++ mutex_unlock(&aufs_sbilist_mtx);
++ return err;
++}
++
++#define SbFunc(name, _index, func) \
++static ssize_t name(struct kobject *kobj, char *buf, loff_t offset, size_t sz) \
++{ \
++ struct sysaufs_args args = { \
++ .index = (_index), \
++ .mtx = NULL \
++ }; \
++ return sb_func(kobj, buf, offset, sz, &args, func); \
++}
++
++#define SbFuncs(name, index) \
++ SbFunc(read_##name, index, sysaufs_read); \
++ SbFunc(write_##name, index, sysaufs_free_write)
++
++static decl_subsys(aufs, NULL, NULL);
++enum {Brs, Stat, Config, _Last};
++static struct sysaufs_entry g_array[_Last];
++GFuncs(brs, Brs);
++GFuncs(stat, Stat);
++GFuncs(config, Config);
++
++SbFuncs(xino, SysaufsSb_XINO);
++
++#define SetEntry(e, _name, init_size, _ops) \
++ do { \
++ (e)->attr.attr.name = #_name; \
++ (e)->attr.attr.owner = THIS_MODULE; \
++ (e)->attr.attr.mode = S_IRUGO | S_IWUSR; \
++ (e)->attr.read = read_##_name; \
++ (e)->attr.write = write_##_name; \
++ (e)->allocated = init_size; \
++ (e)->err = -1; \
++ (e)->ops = _ops; \
++ } while (0)
++
++#define Priv(e) (e)->attr.private
++#define Allocated(e) (e)->allocated
++#define Len(e) (e)->attr.size
++#define Name(e) attr_name((e)->attr)
++
++/* ---------------------------------------------------------------------- */
++
++static void free_entry(struct sysaufs_entry *e)
++{
++ MtxMustLock(&aufs_sbilist_mtx);
++ DEBUG_ON(!Priv(e));
++
++ if (Allocated(e) > 0)
++ kfree(Priv(e));
++ else
++ free_pages((unsigned long)Priv(e), -Allocated(e));
++ Priv(e) = NULL;
++ Len(e) = 0;
++}
++
++static void free_entries(void)
++{
++ static int a[] = {Brs, -1};
++ int *p = a;
++
++ MtxMustLock(&aufs_sbilist_mtx);
++
++ while (*p >= 0) {
++ if (Priv(g_array + *p))
++ free_entry(g_array + *p);
++ p++;
++ }
++}
++
++static int alloc_entry(struct sysaufs_entry *e)
++{
++ MtxMustLock(&aufs_sbilist_mtx);
++ DEBUG_ON(Priv(e));
++ //Dbg("%d\n", Allocated(e));
++
++ if (Allocated(e) > 0)
++ Priv(e) = kmalloc(Allocated(e), GFP_KERNEL);
++ else
++ Priv(e) = (void*)__get_free_pages(GFP_KERNEL, -Allocated(e));
++ if (Priv(e))
++ return 0;
++ return -ENOMEM;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static void unreg(au_subsys_t *subsys, struct sysaufs_entry *a, int n,
++ au_subsys_t *parent)
++{
++ int i;
++
++ TraceEnter();
++
++ for (i = 0; i < n; i++, a++)
++ if (!a->err) {
++ sysfs_remove_bin_file
++ (&au_subsys_to_kset(*subsys).kobj, &a->attr);
++ if (Priv(a))
++ free_entry(a);
++ }
++
++ subsystem_unregister(subsys);
++ subsys_put(parent);
++}
++
++static int reg(au_subsys_t *subsys, struct sysaufs_entry *a, int n,
++ au_subsys_t *parent)
++{
++ int err, i;
++
++ TraceEnter();
++
++ subsys_get(parent);
++ kobj_set_kset_s(&au_subsys_to_kset(*subsys), *parent);
++ err = subsystem_register(subsys);
++ if (unlikely(err))
++ goto out;
++
++ for (i = 0; !err && i < n; i++)
++ err = a[i].err = sysfs_create_bin_file
++ (&au_subsys_to_kset(*subsys).kobj, &a[i].attr);
++ if (unlikely(err))
++ unreg(subsys, a, n, parent);
++
++ out:
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++#define SbSetEntry(index, name, init_size) \
++ SetEntry(sa->array + index, name, init_size, au_si_ops);
++
++void sysaufs_add(struct aufs_sbinfo *sbinfo)
++{
++ int err;
++ struct sysaufs_sbinfo *sa = &sbinfo->si_sysaufs;
++
++ TraceEnter();
++
++ mutex_lock(&aufs_sbilist_mtx);
++ list_add_tail(&sbinfo->si_list, &aufs_sbilist);
++ free_entries();
++
++ memset(sa, 0, sizeof(*sa));
++ SbSetEntry(SysaufsSb_XINO, xino, 128);
++ err = kobject_set_name(&au_subsys_to_kset(sa->subsys).kobj, "%p",
++ sbinfo->si_mnt->mnt_sb);
++ if (!err)
++ err = reg(&sa->subsys, sa->array, ARRAY_SIZE(sa->array),
++ &aufs_subsys);
++ if (unlikely(err))
++ Warn("failed adding sysfs (%d)\n", err);
++
++ mutex_unlock(&aufs_sbilist_mtx);
++}
++
++void sysaufs_del(struct aufs_sbinfo *sbinfo)
++{
++ struct sysaufs_sbinfo *sa = &sbinfo->si_sysaufs;
++
++ TraceEnter();
++
++ mutex_lock(&aufs_sbilist_mtx);
++ unreg(&sa->subsys, sa->array, ARRAY_SIZE(sa->array), &aufs_subsys);
++ list_del(&sbinfo->si_list);
++ free_entries();
++ mutex_unlock(&aufs_sbilist_mtx);
++}
++
++void sysaufs_notify_remount(void)
++{
++ mutex_lock(&aufs_sbilist_mtx);
++ free_entries();
++ mutex_unlock(&aufs_sbilist_mtx);
++}
++
++/* ---------------------------------------------------------------------- */
++
++static int make_brs(struct seq_file *seq, struct sysaufs_args *args,
++ int *do_size)
++{
++ int err;
++ struct aufs_sbinfo *sbinfo;
++
++ TraceEnter();
++ MtxMustLock(&aufs_sbilist_mtx);
++ DEBUG_ON(args->index != Brs);
++
++ err = 0;
++ list_for_each_entry(sbinfo, &aufs_sbilist, si_list) {
++ struct super_block *sb;
++ struct dentry *root;
++ struct vfsmount *mnt;
++
++ sb = sbinfo->si_mnt->mnt_sb;
++ root = sb->s_root;
++ aufs_read_lock(root, !AUFS_I_RLOCK);
++ mnt = sbinfo->si_mnt;
++ err = seq_escape
++ (seq, mnt->mnt_devname ? mnt->mnt_devname : "none",
++ au_esc_chars);
++ if (!err)
++ err = seq_putc(seq, ' ');
++ if (!err)
++ err = seq_path(seq, mnt, root, au_esc_chars);
++ if (err > 0)
++ err = seq_printf(seq, " %p br:", sb);
++ if (!err)
++ err = au_show_brs(seq, sb);
++ aufs_read_unlock(root, !AUFS_I_RLOCK);
++ if (!err)
++ err = seq_putc(seq, '\n');
++ else
++ break;
++ }
++
++ TraceErr(err);
++ return err;
++}
++
++static int make_config(struct seq_file *seq, struct sysaufs_args *args,
++ int *do_size)
++{
++ int err;
++
++ TraceEnter();
++ DEBUG_ON(args->index != Config);
++
++#ifdef CONFIG_AUFS
++ err = seq_puts(seq, "CONFIG_AUFS=y\n");
++#else
++ err = seq_puts(seq, "CONFIG_AUFS=m\n");
++#endif
++
++#define puts(m, v) \
++ if (!err) err = seq_puts(seq, "CONFIG_AUFS_" #m "=" #v "\n")
++#define puts_bool(m) puts(m, y)
++#define puts_mod(m) puts(m, m)
++
++#ifdef CONFIG_AUFS_FAKE_DM
++ puts_bool(FAKE_DM);
++#endif
++#ifdef CONFIG_AUFS_BRANCH_MAX_127
++ puts_bool(BRANCH_MAX_127);
++#elif defined(CONFIG_AUFS_BRANCH_MAX_511)
++ puts_bool(BRANCH_MAX_511);
++#elif defined(CONFIG_AUFS_BRANCH_MAX_1023)
++ puts_bool(BRANCH_MAX_1023);
++#elif defined(CONFIG_AUFS_BRANCH_MAX_32767)
++ puts_bool(BRANCH_MAX_32767);
++#endif
++ puts_bool(SYSAUFS);
++#ifdef CONFIG_AUFS_HINOTIFY
++ puts_bool(HINOTIFY);
++#endif
++#ifdef CONFIG_AUFS_EXPORT
++ puts_bool(EXPORT);
++#endif
++#ifdef CONFIG_AUFS_ROBR
++ puts_bool(ROBR);
++#endif
++#ifdef CONFIG_AUFS_DLGT
++ puts_bool(DLGT);
++#endif
++#ifdef CONFIG_AUFS_LHASH_PATCH
++ puts_bool(LHASH_PATCH);
++#endif
++#ifdef CONFIG_AUFS_KSIZE_PATCH
++ puts_bool(KSIZE_PATCH);
++#endif
++#ifdef CONFIG_AUFS_DEBUG
++ puts_bool(DEBUG);
++#endif
++#ifdef CONFIG_AUFS_COMPAT
++ puts_bool(COMPAT);
++#endif
++
++#undef puts_bool
++#undef puts
++
++ TraceErr(err);
++ return err;
++}
++
++static int make_stat(struct seq_file *seq, struct sysaufs_args *args,
++ int *do_size)
++{
++ int err, i;
++
++ TraceEnter();
++ DEBUG_ON(args->index != Stat);
++
++ *do_size = 0;
++ err = seq_puts(seq, "wkq max_busy:");
++ for (i = 0; !err && i < aufs_nwkq; i++)
++ err = seq_printf(seq, " %u", au_wkq[i].max_busy);
++ if (!err)
++ err = seq_printf(seq, ", %u(generic)\n",
++ au_wkq[aufs_nwkq].max_busy);
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static int make(struct sysaufs_entry *e, struct sysaufs_args *args,
++ int *do_size)
++
++{
++ int err;
++ struct seq_file *seq;
++
++ TraceEnter();
++ DEBUG_ON(Priv(e));
++ MtxMustLock(&aufs_sbilist_mtx);
++
++ err = -ENOMEM;
++ seq = kzalloc(sizeof(*seq), GFP_KERNEL);
++ if (unlikely(!seq))
++ goto out;
++
++ Len(e) = 0;
++ while (1) {
++ err = alloc_entry(e);
++ if (unlikely(err))
++ break;
++
++ //mutex_init(&seq.lock);
++ seq->buf = Priv(e);
++ seq->count = 0;
++ seq->size = Allocated(e);
++ if (unlikely(Allocated(e) <= 0))
++ seq->size = PAGE_SIZE << -Allocated(e);
++
++ err = e->ops[args->index](seq, args, do_size);
++ if (!err) {
++ Len(e) = seq->count;
++ break; /* success */
++ }
++
++ free_entry(e);
++ if (Allocated(e) > 0) {
++ Allocated(e) <<= 1;
++ if (unlikely(Allocated(e) >= (int)PAGE_SIZE))
++ Allocated(e) = 0;
++ } else
++ Allocated(e)--;
++ //Dbg("%d\n", Allocated(e));
++ }
++ kfree(seq);
++
++ out:
++ TraceErr(err);
++ return err;
++}
++
++/* why does sysfs pass my parent kobject? */
++static struct dentry *find_me(struct dentry *parent, struct sysaufs_entry *e)
++{
++#if 1
++ struct dentry *dentry;
++ const char *name = Name(e);
++ const unsigned int len = strlen(name);
++
++ //Dbg("%.*s\n", DLNPair(parent));
++ spin_lock(&dcache_lock);
++ list_for_each_entry(dentry, &parent->d_subdirs, D_CHILD) {
++ //Dbg("%.*s\n", DLNPair(dentry));
++ if (len == dentry->d_name.len
++ && !strcmp(dentry->d_name.name, name)) {
++ spin_unlock(&dcache_lock);
++ return dentry;
++ }
++ }
++ spin_unlock(&dcache_lock);
++#endif
++ return NULL;
++}
++
++static ssize_t sysaufs_read(struct kobject *kobj, char *buf, loff_t offset,
++ size_t sz, struct sysaufs_args *args)
++{
++ ssize_t err;
++ loff_t len;
++ struct dentry *d;
++ struct sysaufs_entry *e;
++ int do_size;
++
++ LKTRTrace("{%d, %p}, offset %Ld, sz %lu\n",
++ args->index, args->sb, offset, (unsigned long)sz);
++
++ if (unlikely(!sz))
++ return 0;
++
++ err = 0;
++ d = NULL;
++ e = g_array + args->index;
++ if (args->sb)
++ e = stosi(args->sb)->si_sysaufs.array + args->index;
++
++ do_size = 1;
++ if (args->mtx)
++ mutex_lock(args->mtx);
++ if (unlikely(!Priv(e))) {
++ err = make(e, args, &do_size);
++ DEBUG_ON(Len(e) > INT_MAX);
++ if (do_size) {
++ d = find_me(kobj->dentry, e);
++ if (d)
++ i_size_write(d->d_inode, Len(e));
++ }
++ }
++
++ if (!err) {
++ err = len = Len(e) - offset;
++ LKTRTrace("%Ld\n", len);
++ if (len > 0) {
++ if (len > sz)
++ err = sz;
++ memcpy(buf, Priv(e) + offset, err);
++ }
++
++ if (!do_size)
++ free_entry(e);
++ }
++ if (args->mtx)
++ mutex_unlock(args->mtx);
++
++ TraceErr(err);
++ return err;
++}
++
++static ssize_t sysaufs_free_write(struct kobject *kobj, char *buf,
++ loff_t offset, size_t sz,
++ struct sysaufs_args *args)
++{
++ struct dentry *d;
++ int allocated, len;
++ struct sysaufs_entry *e;
++
++ LKTRTrace("{%d, %p}\n", args->index, args->sb);
++
++ e = g_array + args->index;
++ if (args->sb)
++ e = stosi(args->sb)->si_sysaufs.array + args->index;
++
++ if (args->mtx)
++ mutex_lock(args->mtx);
++ if (Priv(e)) {
++ allocated = Allocated(e);
++ if (unlikely(allocated <= 0))
++ allocated = PAGE_SIZE << -allocated;
++ allocated >>= 1;
++ len = Len(e);
++
++ free_entry(e);
++ if (unlikely(len <= allocated)) {
++ if (Allocated(e) >= 0)
++ Allocated(e) = allocated;
++ else
++ Allocated(e)++;
++ }
++
++ d = find_me(kobj->dentry, e);
++ if (d && i_size_read(d->d_inode))
++ i_size_write(d->d_inode, 0);
++ }
++ if (args->mtx)
++ mutex_unlock(args->mtx);
++
++ return sz;
++}
++
++static sysaufs_op g_ops[] = {
++ [Brs] = make_brs,
++ [Stat] = make_stat,
++ [Config] = make_config
++};
++
++/* ---------------------------------------------------------------------- */
++
++#define GSetEntry(index, name, init_size) \
++ SetEntry(g_array + index, name, init_size, g_ops)
++
++int __init sysaufs_init(void)
++{
++ int err;
++
++ GSetEntry(Brs, brs, 128);
++ GSetEntry(Stat, stat, 32);
++ GSetEntry(Config, config, 256);
++ err = reg(&aufs_subsys, g_array, ARRAY_SIZE(g_array), &fs_subsys);
++ TraceErr(err);
++ return err;
++}
++
++void __exit sysaufs_fin(void)
++{
++ mutex_lock(&aufs_sbilist_mtx);
++ unreg(&aufs_subsys, g_array, ARRAY_SIZE(g_array), &fs_subsys);
++ mutex_unlock(&aufs_sbilist_mtx);
++}
++
++/* ---------------------------------------------------------------------- */
++
++#ifdef DbgDlgt
++int is_branch(struct super_block *h_sb)
++{
++ int found = 0;
++ struct aufs_sbinfo *sbinfo;
++
++ //Dbg("here\n");
++ mutex_lock(&aufs_sbilist_mtx);
++ list_for_each_entry(sbinfo, &aufs_sbilist, si_list) {
++ aufs_bindex_t bindex, bend;
++ struct super_block *sb;
++
++ sb = sbinfo->si_mnt->mnt_sb;
++ si_read_lock(sb);
++ bend = sbend(sb);
++ for (bindex = 0; !found && bindex <= bend; bindex++)
++ found = (h_sb == sbr_sb(sb, bindex));
++ si_read_unlock(sb);
++ }
++ mutex_unlock(&aufs_sbilist_mtx);
++ return found;
++}
++#endif
+diff --git a/fs/aufs/sysaufs.h b/fs/aufs/sysaufs.h
+new file mode 100755
+index 0000000..cf0247f
+--- /dev/null
++++ b/fs/aufs/sysaufs.h
+@@ -0,0 +1,83 @@
++/*
++ * 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: sysaufs.h,v 1.3 2007/05/14 06:27:18 sfjro Exp $ */
++
++#ifndef __SYSAUFS_H__
++#define __SYSAUFS_H__
++
++#ifdef __KERNEL__
++
++#include <linux/seq_file.h>
++#include <linux/sysfs.h>
++#include <linux/version.h>
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
++typedef struct kset au_subsys_t;
++#define au_subsys_to_kset(subsys) (subsys)
++#else
++typedef struct subsystem au_subsys_t;
++#define au_subsys_to_kset(subsys) ((subsys).kset)
++#endif
++
++/* ---------------------------------------------------------------------- */
++
++/* arguments for an entry under sysfs */
++struct sysaufs_args {
++ int index;
++ struct mutex *mtx;
++ struct super_block *sb;
++};
++
++typedef int (*sysaufs_op)(struct seq_file *seq, struct sysaufs_args *args,
++ int *do_size);
++
++/* an entry under sysfs */
++struct sysaufs_entry {
++ struct bin_attribute attr;
++ int allocated; /* zero minus means pages */
++ int err;
++ sysaufs_op *ops;
++};
++
++/* ---------------------------------------------------------------------- */
++
++struct aufs_sbinfo;
++#ifdef CONFIG_AUFS_SYSAUFS
++void sysaufs_add(struct aufs_sbinfo *sbinfo);
++void sysaufs_del(struct aufs_sbinfo *sbinfo);
++int __init sysaufs_init(void);
++void sysaufs_fin(void);
++void sysaufs_notify_remount(void);
++#else
++static inline void sysaufs_add(struct aufs_sbinfo *sbinfo)
++{
++ /* nothing */
++}
++
++static inline void sysaufs_del(struct aufs_sbinfo *sbinfo)
++{
++ /* nothing */
++}
++#define sysaufs_init() 0
++#define sysaufs_fin() /* */
++#define sysaufs_notify_remount() /* */
++#endif /* CONFIG_AUFS_SYSAUFS */
++
++#endif /* __KERNEL__ */
++#endif /* __SYSAUFS_H__ */
+diff --git a/fs/aufs/vdir.c b/fs/aufs/vdir.c
+new file mode 100755
+index 0000000..8e99b7d
+--- /dev/null
++++ b/fs/aufs/vdir.c
+@@ -0,0 +1,802 @@
++/*
++ * 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: vdir.c,v 1.22 2007/05/14 03:38:52 sfjro Exp $ */
++
++#include "aufs.h"
++
++static int calc_size(int namelen)
++{
++ int sz;
++
++ sz = sizeof(struct aufs_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
++ }
++
++ DEBUG_ON(sz % sizeof(ino_t));
++ return sz;
++}
++
++static int set_deblk_end(union aufs_deblk_p *p, union aufs_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 aufs_deblk_p *p, union aufs_deblk_p *deblk_end)
++{
++ if (calc_size(0) <= deblk_end->p - p->p)
++ return !p->de->de_str.len;
++ return 1;
++}
++
++static aufs_deblk_t *last_deblk(struct aufs_vdir *vdir)
++{
++ return vdir->vd_deblk[vdir->vd_nblk - 1];
++}
++
++void nhash_init(struct aufs_nhash *nhash)
++{
++ int i;
++ for (i = 0; i < AUFS_NHASH_SIZE; i++)
++ INIT_HLIST_HEAD(nhash->heads + i);
++}
++
++struct aufs_nhash *nhash_new(gfp_t gfp)
++{
++ struct aufs_nhash *nhash;
++
++ nhash = kmalloc(sizeof(*nhash), gfp);
++ if (nhash) {
++ nhash_init(nhash);
++ return nhash;
++ }
++ return ERR_PTR(-ENOMEM);
++}
++
++void nhash_del(struct aufs_nhash *nhash)
++{
++ nhash_fin(nhash);
++ kfree(nhash);
++}
++
++void nhash_move(struct aufs_nhash *dst, struct aufs_nhash *src)
++{
++ int i;
++
++ TraceEnter();
++
++ //DbgWhlist(src);
++ *dst = *src;
++ for (i = 0; i < AUFS_NHASH_SIZE; 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 nhash_fin(struct aufs_nhash *whlist)
++{
++ int i;
++ struct hlist_head *head;
++ struct aufs_wh *tpos;
++ struct hlist_node *pos, *n;
++
++ TraceEnter();
++
++ for (i = 0; i < AUFS_NHASH_SIZE; i++) {
++ head = whlist->heads + i;
++ hlist_for_each_entry_safe(tpos, pos, n, head, wh_hash) {
++ //hlist_del(pos);
++ kfree(tpos);
++ }
++ }
++}
++
++int is_longer_wh(struct aufs_nhash *whlist, aufs_bindex_t btgt, int limit)
++{
++ int n, i;
++ struct hlist_head *head;
++ struct aufs_wh *tpos;
++ struct hlist_node *pos;
++
++ LKTRTrace("limit %d\n", limit);
++ //return 1;
++
++ n = 0;
++ for (i = 0; i < AUFS_NHASH_SIZE; 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;
++}
++
++/* returns found(true) or not */
++int test_known_wh(struct aufs_nhash *whlist, char *name, int namelen)
++{
++ struct hlist_head *head;
++ struct aufs_wh *tpos;
++ struct hlist_node *pos;
++ struct aufs_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 append_wh(struct aufs_nhash *whlist, char *name, int namelen,
++ aufs_bindex_t bindex)
++{
++ int err;
++ struct aufs_destr *str;
++ struct aufs_wh *wh;
++
++ LKTRTrace("%.*s\n", namelen, name);
++
++ err = -ENOMEM;
++ wh = kmalloc(sizeof(*wh) + namelen, GFP_KERNEL);
++ if (unlikely(!wh))
++ goto out;
++ err = 0;
++ wh->wh_bindex = bindex;
++ 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:
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++void free_vdir(struct aufs_vdir *vdir)
++{
++ aufs_deblk_t **deblk;
++
++ TraceEnter();
++
++ deblk = vdir->vd_deblk;
++ while (vdir->vd_nblk--) {
++ kfree(*deblk);
++ deblk++;
++ }
++ kfree(vdir->vd_deblk);
++ cache_free_vdir(vdir);
++}
++
++static int append_deblk(struct aufs_vdir *vdir)
++{
++ int err, sz, i;
++ aufs_deblk_t **o;
++ union aufs_deblk_p p, deblk_end;
++
++ TraceEnter();
++
++ 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);
++ DEBUG_ON(err);
++ }
++
++ out:
++ TraceErr(err);
++ return err;
++}
++
++static struct aufs_vdir *alloc_vdir(void)
++{
++ struct aufs_vdir *vdir;
++ int err;
++
++ TraceEnter();
++
++ err = -ENOMEM;
++ vdir = 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:
++ cache_free_vdir(vdir);
++ out:
++ vdir = ERR_PTR(err);
++ TraceErrPtr(vdir);
++ return vdir;
++}
++
++static int reinit_vdir(struct aufs_vdir *vdir)
++{
++ int err;
++ union aufs_deblk_p p, deblk_end;
++
++ TraceEnter();
++
++ 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);
++ DEBUG_ON(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 aufs_nhash *dehlist)
++{
++ int i;
++ struct hlist_head *head;
++ struct aufs_dehstr *tpos;
++ struct hlist_node *pos, *n;
++
++ TraceEnter();
++
++ for (i = 0; i < AUFS_NHASH_SIZE; i++) {
++ head = dehlist->heads + i;
++ hlist_for_each_entry_safe(tpos, pos, n, head, hash) {
++ //hlist_del(pos);
++ cache_free_dehstr(tpos);
++ }
++ }
++}
++
++/* returns found(true) or not */
++static int test_known(struct aufs_nhash *delist, char *name, int namelen)
++{
++ struct hlist_head *head;
++ struct aufs_dehstr *tpos;
++ struct hlist_node *pos;
++ struct aufs_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 aufs_vdir *vdir, char *name, int namelen, ino_t ino,
++ unsigned int d_type, struct aufs_nhash *delist)
++{
++ int err, sz;
++ union aufs_deblk_p p, *room, deblk_end;
++ struct aufs_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;
++ DEBUG_ON(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();
++ DEBUG_ON(room->p != p.p);
++ }
++
++ err = -ENOMEM;
++ dehstr = 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:
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++struct fillvdir_arg {
++ struct file *file;
++ struct aufs_vdir *vdir;
++ struct aufs_nhash *delist;
++ struct aufs_nhash *whlist;
++ aufs_bindex_t bindex;
++ int err;
++ int called;
++};
++
++static int fillvdir(void *__arg, const char *__name, int namelen, loff_t offset,
++ filldir_ino_t h_ino, unsigned int d_type)
++{
++ struct fillvdir_arg *arg = __arg;
++ char *name = (void*)__name;
++ aufs_bindex_t bindex, bend;
++ struct xino xino;
++ struct super_block *sb;
++
++ 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;
++ arg->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)
++ || test_known_wh(arg->whlist + bindex, name,
++ namelen))
++ goto out; /* already exists or whiteouted */
++
++ arg->err = xino_read(sb, bend, h_ino, &xino);
++ if (!arg->err && !xino.ino) {
++ //struct inode *h_inode;
++ xino.ino = xino_new_ino(sb);
++ if (unlikely(!xino.ino))
++ arg->err = -EIO;
++#if 0
++ //xino.h_gen = AuXino_INVALID_HGEN;
++ h_inode = ilookup(sbr_sb(sb, bend), h_ino);
++ if (h_inode) {
++ if (!is_bad_inode(h_inode)) {
++ xino.h_gen = h_inode->i_generation;
++ WARN_ON(xino.h_gen == AuXino_INVALID_HGEN);
++ }
++ iput(h_inode);
++ }
++#endif
++ arg->err = xino_write(sb, bend, h_ino, &xino);
++ }
++ if (!arg->err)
++ arg->err = append_de(arg->vdir, name, namelen, xino.ino,
++ d_type, arg->delist + bend);
++ } else {
++ name += AUFS_WH_PFX_LEN;
++ namelen -= AUFS_WH_PFX_LEN;
++ for (bindex = 0; bindex < bend; bindex++)
++ if (test_known_wh(arg->whlist + bend, name, namelen))
++ goto out; /* already whiteouted */
++ arg->err = append_wh(arg->whlist + bend, name, namelen, bend);
++ }
++
++ out:
++ if (!arg->err)
++ arg->vdir->vd_jiffy = jiffies;
++ //smp_mb();
++ TraceErr(arg->err);
++ return arg->err;
++}
++
++static int read_vdir(struct file *file, int may_read)
++{
++ int err, do_read, dlgt;
++ struct dentry *dentry;
++ struct inode *inode;
++ struct aufs_vdir *vdir, *allocated;
++ unsigned long expire;
++ struct fillvdir_arg arg;
++ aufs_bindex_t bindex, bend, bstart;
++ struct super_block *sb;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s, may %d\n", DLNPair(dentry), may_read);
++ FiMustWriteLock(file);
++ inode = dentry->d_inode;
++ IMustLock(inode);
++ IiMustWriteLock(inode);
++ DEBUG_ON(!S_ISDIR(inode->i_mode));
++
++ err = 0;
++ allocated = NULL;
++ do_read = 0;
++ sb = inode->i_sb;
++ expire = stosi(sb)->si_rdcache;
++ vdir = ivdir(inode);
++ if (!vdir) {
++ DEBUG_ON(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;
++ }
++ //DbgVdir(vdir); goto out;
++
++ if (!do_read)
++ return 0; /* success */
++
++ err = -ENOMEM;
++ bend = fbend(file);
++ arg.delist = kmalloc(sizeof(*arg.delist) * (bend + 1), GFP_KERNEL);
++ if (unlikely(!arg.delist))
++ goto out_vdir;
++ arg.whlist = kmalloc(sizeof(*arg.whlist) * (bend + 1), GFP_KERNEL);
++ if (unlikely(!arg.whlist))
++ goto out_delist;
++ err = 0;
++ for (bindex = 0; bindex <= bend; bindex++) {
++ nhash_init(arg.delist + bindex);
++ nhash_init(arg.whlist + bindex);
++ }
++
++ dlgt = need_dlgt(sb);
++ arg.file = file;
++ arg.vdir = vdir;
++ bstart = fbstart(file);
++ for (bindex = bstart; !err && bindex <= bend; bindex++) {
++ struct file *hf;
++ struct inode *h_inode;
++
++ hf = au_h_fptr_i(file, bindex);
++ if (!hf)
++ continue;
++
++ h_inode = hf->f_dentry->d_inode;
++ //hf->f_pos = 0;
++ arg.bindex = bindex;
++ do {
++ arg.err = 0;
++ arg.called = 0;
++ //smp_mb();
++ err = vfsub_readdir(hf, fillvdir, &arg, dlgt);
++ if (err >= 0)
++ err = arg.err;
++ } while (!err && arg.called);
++ }
++
++ for (bindex = bstart; bindex <= bend; bindex++) {
++ free_dehlist(arg.delist + bindex);
++ nhash_fin(arg.whlist + bindex);
++ }
++ kfree(arg.whlist);
++
++ out_delist:
++ kfree(arg.delist);
++ out_vdir:
++ 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)
++ set_ivdir(inode, allocated);
++ } else if (allocated)
++ free_vdir(allocated);
++ //DbgVdir(vdir); goto out;
++
++ out:
++ TraceErr(err);
++ return err;
++}
++
++static int copy_vdir(struct aufs_vdir *tgt, struct aufs_vdir *src)
++{
++ int err, i, rerr, n;
++
++ TraceEnter();
++ DEBUG_ON(tgt->vd_nblk != 1);
++ //DbgVdir(tgt);
++
++ err = -ENOMEM;
++ if (tgt->vd_nblk < src->vd_nblk) {
++ aufs_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;
++ }
++
++ n = tgt->vd_nblk = src->vd_nblk;
++ memcpy(tgt->vd_deblk[0], src->vd_deblk[0], AUFS_DEBLK_SIZE);
++ //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(AUFS_DEBLK_SIZE, GFP_KERNEL);
++ if (tgt->vd_deblk[i])
++ memcpy(tgt->vd_deblk[i], src->vd_deblk[i],
++ AUFS_DEBLK_SIZE);
++ else
++ goto out;
++ }
++ //smp_mb();
++ //DbgVdir(tgt);
++ return 0; /* success */
++
++ out:
++ rerr = reinit_vdir(tgt);
++ BUG_ON(rerr);
++ TraceErr(err);
++ return err;
++}
++
++int au_init_vdir(struct file *file)
++{
++ int err;
++ struct dentry *dentry;
++ struct inode *inode;
++ struct aufs_vdir *vdir_cache, *allocated;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s, pos %Ld\n", DLNPair(dentry), file->f_pos);
++ FiMustWriteLock(file);
++ inode = dentry->d_inode;
++ IiMustWriteLock(inode);
++ DEBUG_ON(!S_ISDIR(inode->i_mode));
++
++ err = read_vdir(file, !file->f_pos);
++ if (unlikely(err))
++ goto out;
++ //DbgVdir(ivdir(inode)); goto out;
++
++ allocated = NULL;
++ vdir_cache = 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; DbgVdir(vdir_cache); goto out;
++
++ err = copy_vdir(vdir_cache, ivdir(inode));
++ if (!err) {
++ file->f_version = inode->i_version;
++ if (allocated)
++ set_fvdir_cache(file, allocated);
++ } else if (allocated)
++ free_vdir(allocated);
++
++ out:
++ TraceErr(err);
++ return err;
++}
++
++static loff_t calc_offset(struct aufs_vdir *vdir)
++{
++ loff_t offset;
++ union aufs_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 aufs_vdir *vdir_cache;
++ loff_t offset;
++ union aufs_deblk_p p, deblk_end;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s, pos %Ld\n", DLNPair(dentry), file->f_pos);
++ vdir_cache = fvdir_cache(file);
++ DEBUG_ON(!vdir_cache);
++ //DbgVdir(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 / AUFS_DEBLK_SIZE;
++ LKTRTrace("i %d\n", i);
++ if (i >= vdir_cache->vd_nblk)
++ goto out;
++
++ n = vdir_cache->vd_nblk;
++ //DbgVdir(vdir_cache);
++ for (; i < n; i++) {
++ p.deblk = vdir_cache->vd_deblk[i];
++ deblk_end.deblk = p.deblk + 1;
++ offset = i * AUFS_DEBLK_SIZE;
++ 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();
++ //DbgVdir(vdir_cache);
++ TraceErr(!valid);
++ return valid;
++}
++
++int au_fill_de(struct file *file, void *dirent, filldir_t filldir)
++{
++ int err, l;
++ struct dentry *dentry;
++ struct aufs_vdir *vdir_cache;
++ struct aufs_de *de;
++ union aufs_deblk_p deblk_end;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s, pos %Ld\n", DLNPair(dentry), file->f_pos);
++ vdir_cache = fvdir_cache(file);
++ DEBUG_ON(!vdir_cache);
++ //DbgVdir(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)) {
++ TraceErr(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;
++}
+diff --git a/fs/aufs/vfsub.c b/fs/aufs/vfsub.c
+new file mode 100755
+index 0000000..8571d21
+--- /dev/null
++++ b/fs/aufs/vfsub.c
+@@ -0,0 +1,665 @@
++/*
++ * 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: vfsub.c,v 1.5 2007/04/23 00:55:06 sfjro Exp $ */
++// I'm going to slightly mad
++
++#include "aufs.h"
++
++/* ---------------------------------------------------------------------- */
++
++#ifdef CONFIG_AUFS_DLGT
++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;
++ struct permission_args args = {
++ .errp = &err,
++ .inode = inode,
++ .mask = mask,
++ .nd = nd
++ };
++ au_wkq_wait(call_permission, &args, /*dlgt*/1);
++ 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;
++ struct create_args args = {
++ .errp = &err,
++ .dir = dir,
++ .dentry = dentry,
++ .mode = mode,
++ .nd = nd
++ };
++ au_wkq_wait(call_create, &args, /*dlgt*/1);
++ 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;
++ struct symlink_args args = {
++ .errp = &err,
++ .dir = dir,
++ .dentry = dentry,
++ .symname = symname,
++ .mode = mode
++ };
++ au_wkq_wait(call_symlink, &args, /*dlgt*/1);
++ 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;
++ struct mknod_args args = {
++ .errp = &err,
++ .dir = dir,
++ .dentry = dentry,
++ .mode = mode,
++ .dev = dev
++ };
++ au_wkq_wait(call_mknod, &args, /*dlgt*/1);
++ 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;
++ struct mkdir_args args = {
++ .errp = &err,
++ .dir = dir,
++ .dentry = dentry,
++ .mode = mode
++ };
++ au_wkq_wait(call_mkdir, &args, /*dlgt*/1);
++ 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;
++ struct link_args args = {
++ .errp = &err,
++ .src_dentry = src_dentry,
++ .dir = dir,
++ .dentry = dentry
++ };
++ au_wkq_wait(call_link, &args, /*dlgt*/1);
++ return err;
++ }
++}
++
++struct rename_args {
++ int *errp;
++ struct inode *src_dir, *dir;
++ struct dentry *src_dentry, *dentry;
++};
++
++static void call_rename(void *args)
++{
++ struct rename_args *a = args;
++ *a->errp = do_vfsub_rename(a->src_dir, a->src_dentry, a->dir,
++ a->dentry);
++}
++
++int vfsub_rename(struct inode *src_dir, struct dentry *src_dentry,
++ struct inode *dir, struct dentry *dentry, int dlgt)
++{
++ if (!dlgt)
++ return do_vfsub_rename(src_dir, src_dentry, dir, dentry);
++ else {
++ int err;
++ struct rename_args args = {
++ .errp = &err,
++ .src_dir = src_dir,
++ .src_dentry = src_dentry,
++ .dir = dir,
++ .dentry = dentry
++ };
++ au_wkq_wait(call_rename, &args, /*dlgt*/1);
++ return err;
++ }
++}
++
++struct rmdir_args {
++ int *errp;
++ struct inode *dir;
++ struct dentry *dentry;
++};
++
++static void call_rmdir(void *args)
++{
++ struct rmdir_args *a = args;
++ *a->errp = do_vfsub_rmdir(a->dir, a->dentry);
++}
++
++int vfsub_rmdir(struct inode *dir, struct dentry *dentry, int dlgt)
++{
++ if (!dlgt)
++ return do_vfsub_rmdir(dir, dentry);
++ else {
++ int err;
++ struct rmdir_args args = {
++ .errp = &err,
++ .dir = dir,
++ .dentry = dentry
++ };
++ au_wkq_wait(call_rmdir, &args, /*dlgt*/1);
++ 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",
++ DLNPair(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 {
++ 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_KERNEL);
++ if (unlikely(!args.kbuf))
++ goto out;
++
++ read = 0;
++ do {
++ au_wkq_wait(call_read_k, &args, /*dlgt*/1);
++ 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 (unlikely(count < args.count))
++ args.count = count;
++ ubuf += err;
++ read += err;
++ } while (count);
++ smp_mb();
++ 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;
++ struct read_args args = {
++ .errp = &err,
++ .file = file,
++ .count = count,
++ .ppos = ppos
++ };
++ args.kbuf = kbuf;
++ au_wkq_wait(call_read_k, &args, /*dlgt*/1);
++ 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;
++};
++
++static void call_write_k(void *args)
++{
++ struct write_args *a = args;
++ LKTRTrace("%.*s, cnt %lu, pos %Ld\n",
++ DLNPair(a->file->f_dentry), (unsigned long)a->count,
++ *a->ppos);
++ *a->errp = do_vfsub_write_k(a->file, a->kbuf, a->count, a->ppos);
++}
++
++ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count,
++ loff_t *ppos, int dlgt)
++{
++ if (!dlgt)
++ return do_vfsub_write_u(file, ubuf, count, ppos);
++ else {
++ ssize_t err, written;
++ struct write_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_KERNEL);
++ if (unlikely(!args.kbuf))
++ goto out;
++
++ written = 0;
++ do {
++ if (unlikely(copy_from_user(args.kbuf, ubuf, args.count))) {
++ err = -EFAULT;
++ goto out_free;
++ }
++
++ au_wkq_wait(call_write_k, &args, /*dlgt*/1);
++ 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,
++ int dlgt)
++{
++ if (!dlgt)
++ return do_vfsub_write_k(file, kbuf, count, ppos);
++ else {
++ ssize_t err;
++ struct write_args args = {
++ .errp = &err,
++ .file = file,
++ .count = count,
++ .ppos = ppos
++ };
++ args.kbuf = kbuf;
++ au_wkq_wait(call_write_k, &args, /*dlgt*/1);
++ 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;
++ struct readdir_args args = {
++ .errp = &err,
++ .file = file,
++ .filldir = filldir,
++ .arg = arg
++ };
++ au_wkq_wait(call_readdir, &args, /*dlgt*/1);
++ return err;
++ }
++}
++#endif /* CONFIG_AUFS_DLGT */
++
++/* ---------------------------------------------------------------------- */
++
++struct notify_change_args {
++ int *errp;
++ struct dentry *h_dentry;
++ struct iattr *ia;
++};
++
++static void call_notify_change(void *args)
++{
++ struct notify_change_args *a = args;
++ struct inode *h_inode;
++
++ LKTRTrace("%.*s, ia_valid 0x%x\n",
++ DLNPair(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)) {
++ lockdep_off();
++ *a->errp = notify_change(a->h_dentry, a->ia);
++ lockdep_on();
++ }
++ TraceErr(*a->errp);
++}
++
++int vfsub_notify_change(struct dentry *dentry, struct iattr *ia, int dlgt)
++{
++ int err;
++ struct notify_change_args args = {
++ .errp = &err,
++ .h_dentry = dentry,
++ .ia = ia
++ };
++
++#ifndef CONFIG_AUFS_DLGT
++ call_notify_change(&args);
++#else
++ if (!dlgt)
++ call_notify_change(&args);
++ else
++ au_wkq_wait(call_notify_change, &args, /*dlgt*/1);
++#endif
++
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++struct unlink_args {
++ int *errp;
++ struct inode *dir;
++ struct dentry *dentry;
++};
++
++static void call_unlink(void *args)
++{
++ struct unlink_args *a = args;
++ struct inode *h_inode;
++ const int stop_sillyrename = (au_is_nfs(a->dentry->d_sb)
++ && atomic_read(&a->dentry->d_count) == 1);
++
++ LKTRTrace("%.*s, stop_silly %d, cnt %d\n",
++ DLNPair(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(&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)) {
++ lockdep_off();
++ *a->errp = vfs_unlink(a->dir, a->dentry);
++ lockdep_on();
++ } else
++ err = -1;
++ }
++#else
++ // vfs_unlink() locks inode
++ lockdep_off();
++ *a->errp = vfs_unlink(a->dir, a->dentry);
++ lockdep_on();
++#endif
++
++ if (!stop_sillyrename)
++ dput(a->dentry);
++ if (h_inode)
++ iput(h_inode);
++
++ TraceErr(*a->errp);
++}
++
++/*
++ * @dir: must be locked.
++ * @dentry: target dentry.
++ */
++int vfsub_unlink(struct inode *dir, struct dentry *dentry, int dlgt)
++{
++ int err;
++ struct unlink_args args = {
++ .errp = &err,
++ .dir = dir,
++ .dentry = dentry
++ };
++
++#ifndef CONFIG_AUFS_DLGT
++ call_unlink(&args);
++#else
++ if (!dlgt)
++ call_unlink(&args);
++ else
++ au_wkq_wait(call_unlink, &args, /*dlgt*/1);
++#endif
++ 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);
++}
++
++int vfsub_statfs(void *arg, struct kstatfs *buf, int dlgt)
++{
++ int err;
++ struct statfs_args args = {
++ .errp = &err,
++ .arg = arg,
++ .buf = buf
++ };
++
++#ifndef CONFIG_AUFS_DLGT
++ call_statfs(&args);
++#else
++ if (!dlgt)
++ call_statfs(&args);
++ else
++ au_wkq_wait(call_statfs, &args, /*dlgt*/1);
++#endif
++ return err;
++}
+diff --git a/fs/aufs/vfsub.h b/fs/aufs/vfsub.h
+new file mode 100755
+index 0000000..52f15cc
+--- /dev/null
++++ b/fs/aufs/vfsub.h
+@@ -0,0 +1,427 @@
++/*
++ * 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: vfsub.h,v 1.8 2007/05/14 03:39:10 sfjro Exp $ */
++
++#ifndef __AUFS_VFSUB_H__
++#define __AUFS_VFSUB_H__
++
++#ifdef __KERNEL__
++
++#include <linux/fs.h>
++#include <asm/uaccess.h>
++#include "wkq.h"
++
++/* ---------------------------------------------------------------------- */
++
++/* 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 %p\n", inode->i_ino, mask, nd);
++#if 0
++#else
++ return permission(inode, mask, nd);
++#endif
++}
++
++static inline
++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();
++ return err;
++}
++
++static inline
++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();
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static inline
++int do_vfsub_create(struct inode *dir, struct dentry *dentry, int mode,
++ struct nameidata *nd)
++{
++ LKTRTrace("i%lu, %.*s, 0x%x\n", dir->i_ino, DLNPair(dentry), mode);
++#if 0
++#else
++ return vfs_create(dir, dentry, mode, nd);
++#endif
++}
++
++static inline
++int do_vfsub_symlink(struct inode *dir, struct dentry *dentry,
++ const char *symname, int mode)
++{
++ LKTRTrace("i%lu, %.*s, %s, 0x%x\n",
++ dir->i_ino, DLNPair(dentry), symname, mode);
++#if 0
++#else
++ return vfs_symlink(dir, dentry, symname, mode);
++#endif
++}
++
++static inline
++int do_vfsub_mknod(struct inode *dir, struct dentry *dentry, int mode,
++ dev_t dev)
++{
++ LKTRTrace("i%lu, %.*s, 0x%x\n", dir->i_ino, DLNPair(dentry), mode);
++#if 0
++#else
++ return vfs_mknod(dir, dentry, mode, dev);
++#endif
++}
++
++static inline
++int do_vfsub_link(struct dentry *src_dentry, struct inode *dir,
++ struct dentry *dentry)
++{
++ int err;
++
++ LKTRTrace("%.*s, i%lu, %.*s\n",
++ DLNPair(src_dentry), dir->i_ino, DLNPair(dentry));
++
++ lockdep_off();
++#if 0
++#else
++ err = vfs_link(src_dentry, dir, dentry);
++#endif
++ lockdep_on();
++ return err;
++}
++
++static inline
++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, DLNPair(src_dentry),
++ dir->i_ino, DLNPair(dentry));
++
++ lockdep_off();
++#if 0
++#else
++ err = vfs_rename(src_dir, src_dentry, dir, dentry);
++#endif
++ lockdep_on();
++ return err;
++}
++
++static inline
++int do_vfsub_mkdir(struct inode *dir, struct dentry *dentry, int mode)
++{
++ LKTRTrace("i%lu, %.*s, 0x%x\n", dir->i_ino, DLNPair(dentry), mode);
++#if 0
++#else
++ return vfs_mkdir(dir, dentry, mode);
++#endif
++}
++
++static inline int do_vfsub_rmdir(struct inode *dir, struct dentry *dentry)
++{
++ int err;
++
++ LKTRTrace("i%lu, %.*s\n", dir->i_ino, DLNPair(dentry));
++
++ lockdep_off();
++#if 0
++#else
++ err = vfs_rmdir(dir, dentry);
++#endif
++ lockdep_on();
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static inline
++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",
++ DLNPair(file->f_dentry), (unsigned long)count, *ppos);
++
++ /* nfs uses some locks */
++ lockdep_off();
++#if 0
++#else
++ err = vfs_read(file, ubuf, count, ppos);
++#endif
++ lockdep_on();
++ return err;
++}
++
++// kernel_read() ??
++static inline
++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;
++}
++
++static inline
++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",
++ DLNPair(file->f_dentry), (unsigned long)count, *ppos);
++
++ lockdep_off();
++#if 0
++#else
++ err = vfs_write(file, ubuf, count, ppos);
++#endif
++ lockdep_on();
++ return err;
++}
++
++static inline
++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;
++}
++
++static inline
++int do_vfsub_readdir(struct file *file, filldir_t filldir, void *arg)
++{
++ int err;
++
++ LKTRTrace("%.*s\n", DLNPair(file->f_dentry));
++
++ lockdep_off();
++#if 0
++#else
++ err = vfs_readdir(file, filldir, arg);
++#endif
++ lockdep_on();
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static inline loff_t vfsub_llseek(struct file *file, loff_t offset, int origin)
++{
++ loff_t err;
++
++ LKTRTrace("%.*s\n", DLNPair(file->f_dentry));
++
++ lockdep_off();
++#if 0
++#else
++ err = vfs_llseek(file, offset, origin);
++#endif
++ lockdep_on();
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++#ifdef CONFIG_AUFS_DLGT
++static inline int need_dlgt(struct super_block *sb)
++{
++ return (au_flag_test(sb, AuFlag_DLGT) && !is_au_wkq(current));
++}
++
++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, int dlgt);
++int vfsub_mkdir(struct inode *dir, struct dentry *dentry, int mode, int dlgt);
++int vfsub_rmdir(struct inode *dir, struct dentry *dentry, int dlgt);
++
++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, int dlgt);
++ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, loff_t *ppos,
++ int dlgt);
++int vfsub_readdir(struct file *file, filldir_t filldir, void *arg, int dlgt);
++
++#else
++
++static inline int need_dlgt(struct super_block *sb)
++{
++ return 0;
++}
++
++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, int dlgt)
++{
++ return do_vfsub_rename(src_dir, src_dentry, dir, dentry);
++}
++
++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, int dlgt)
++{
++ return do_vfsub_rmdir(dir, dentry);
++}
++
++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, int dlgt)
++{
++ return do_vfsub_write_u(file, ubuf, count, ppos);
++}
++
++static inline
++ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, loff_t *ppos,
++ int dlgt)
++{
++ return do_vfsub_write_k(file, kbuf, count, ppos);
++}
++
++static inline
++int vfsub_readdir(struct file *file, filldir_t filldir, void *arg, int dlgt)
++{
++ return do_vfsub_readdir(file, filldir, arg);
++}
++#endif /* CONFIG_AUFS_DLGT */
++
++/* ---------------------------------------------------------------------- */
++
++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();
++}
++
++/* ---------------------------------------------------------------------- */
++
++int vfsub_notify_change(struct dentry *dentry, struct iattr *ia, int dlgt);
++int vfsub_unlink(struct inode *dir, struct dentry *dentry, int dlgt);
++int vfsub_statfs(void *arg, struct kstatfs *buf, int dlgt);
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_VFSUB_H__ */
+diff --git a/fs/aufs/whout.c b/fs/aufs/whout.c
+new file mode 100755
+index 0000000..b7f874c
+--- /dev/null
++++ b/fs/aufs/whout.c
+@@ -0,0 +1,933 @@
++/*
++ * 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: whout.c,v 1.14 2007/05/14 03:40:40 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_free_whname().
++ */
++int au_alloc_whname(const char *name, int len, struct qstr *wh)
++{
++ char *p;
++
++ DEBUG_ON(!name || !len || !wh);
++
++ if (unlikely(len > PATH_MAX - AUFS_WH_PFX_LEN))
++ return -ENAMETOOLONG;
++
++ wh->len = len + AUFS_WH_PFX_LEN;
++ wh->name = p = kmalloc(wh->len, GFP_KERNEL);
++ //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_free_whname(struct qstr *wh)
++{
++ DEBUG_ON(!wh || !wh->name);
++ kfree(wh->name);
++#ifdef CONFIG_AUFS_DEBUG
++ wh->name = NULL;
++#endif
++}
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * test if the @wh_name exists under @hidden_parent.
++ * @try_sio specifies the necessary of super-io.
++ */
++int is_wh(struct dentry *hidden_parent, struct qstr *wh_name, int try_sio,
++ struct lkup_args *lkup)
++{
++ int err;
++ struct dentry *wh_dentry;
++ struct inode *hidden_dir;
++
++ LKTRTrace("%.*s/%.*s, lkup{%p, %d}\n", DLNPair(hidden_parent),
++ wh_name->len, wh_name->name, lkup->nfsmnt, lkup->dlgt);
++ hidden_dir = hidden_parent->d_inode;
++ DEBUG_ON(!S_ISDIR(hidden_dir->i_mode));
++ IMustLock(hidden_dir);
++
++ if (!try_sio)
++ wh_dentry = lkup_one(wh_name->name, hidden_parent,
++ wh_name->len, lkup);
++ else
++ wh_dentry = sio_lkup_one(wh_name->name, hidden_parent,
++ wh_name->len, lkup);
++ //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;
++ IOErr("%.*s Invalid whiteout entry type 0%o.\n",
++ DLNPair(wh_dentry), wh_dentry->d_inode->i_mode);
++
++ out_wh:
++ dput(wh_dentry);
++ out:
++ TraceErr(err);
++ return err;
++}
++
++/*
++ * test if the @hidden_dentry sets opaque or not.
++ */
++int is_diropq(struct dentry *hidden_dentry, struct lkup_args *lkup)
++{
++ int err;
++ struct inode *hidden_dir;
++
++ LKTRTrace("dentry %.*s\n", DLNPair(hidden_dentry));
++ hidden_dir = hidden_dentry->d_inode;
++ DEBUG_ON(!S_ISDIR(hidden_dir->i_mode));
++ IMustLock(hidden_dir);
++
++ err = is_wh(hidden_dentry, &diropq_name, /*try_sio*/1, lkup);
++ TraceErr(err);
++ return err;
++}
++
++/*
++ * returns a negative dentry whose name is unique and temporary.
++ */
++struct dentry *lkup_whtmp(struct dentry *hidden_parent, struct qstr *prefix,
++ struct lkup_args *lkup)
++{
++#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",
++ DLNPair(hidden_parent), prefix->len, prefix->name);
++ DEBUG_ON(!hidden_parent->d_inode);
++ IMustLock(hidden_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++ = '.';
++ DEBUG_ON(name + len + 1 - p <= HEX_LEN);
++
++ for (i = 0; i < 3; i++) {
++ sprintf(p, "%.*d", HEX_LEN, cnt++);
++ dentry = sio_lkup_one(name, hidden_parent, len, lkup);
++ //if (LktrCond) {dput(dentry); dentry = ERR_PTR(-1);}
++ if (unlikely(IS_ERR(dentry) || !dentry->d_inode))
++ goto out_name;
++ dput(dentry);
++ }
++ //Warn("could not get random name\n");
++ dentry = ERR_PTR(-EEXIST);
++ Dbg("%.*s\n", len, name);
++ BUG();
++
++ out_name:
++ if (unlikely(name != defname))
++ kfree(name);
++ out:
++ TraceErrPtr(dentry);
++ return dentry;
++#undef HEX_LEN
++}
++
++/*
++ * rename the @dentry of @bindex to the whiteouted temporary name.
++ */
++int rename_whtmp(struct dentry *dentry, aufs_bindex_t bindex)
++{
++ int err;
++ struct inode *hidden_dir;
++ struct dentry *hidden_dentry, *hidden_parent, *tmp_dentry;
++ struct super_block *sb;
++ struct lkup_args lkup;
++
++ LKTRTrace("%.*s, b%d\n", DLNPair(dentry), bindex);
++ hidden_dentry = au_h_dptr_i(dentry, bindex);
++ DEBUG_ON(!hidden_dentry || !hidden_dentry->d_inode);
++ hidden_parent = hidden_dentry->d_parent;
++ hidden_dir = hidden_parent->d_inode;
++ IMustLock(hidden_dir);
++
++ sb = dentry->d_sb;
++ lkup.nfsmnt = au_nfsmnt(sb, bindex);
++ lkup.dlgt = need_dlgt(sb);
++ tmp_dentry = lkup_whtmp(hidden_parent, &hidden_dentry->d_name, &lkup);
++ //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() */
++ err = vfsub_rename(hidden_dir, hidden_dentry,
++ hidden_dir, tmp_dentry, lkup.dlgt);
++ //if (LktrCond) err = -1; //unavailable
++ TraceErr(err);
++ dput(tmp_dentry);
++ }
++
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++int au_unlink_wh_dentry(struct inode *hidden_dir, struct dentry *wh_dentry,
++ struct dentry *dentry, int dlgt)
++{
++ int err;
++
++ LKTRTrace("hi%lu, wh %.*s, d %p\n", hidden_dir->i_ino,
++ DLNPair(wh_dentry), dentry);
++ DEBUG_ON((dentry && dbwh(dentry) == -1)
++ || !wh_dentry->d_inode
++ || !S_ISREG(wh_dentry->d_inode->i_mode));
++ IMustLock(hidden_dir);
++
++ err = vfsub_unlink(hidden_dir, wh_dentry, dlgt);
++ //if (LktrCond) err = -1; // unavailable
++ if (!err && dentry)
++ set_dbwh(dentry, -1);
++
++ TraceErr(err);
++ return err;
++}
++
++static int unlink_wh_name(struct dentry *hidden_parent, struct qstr *wh,
++ struct lkup_args *lkup)
++{
++ int err;
++ struct inode *hidden_dir;
++ struct dentry *hidden_dentry;
++
++ LKTRTrace("%.*s/%.*s\n", DLNPair(hidden_parent), LNPair(wh));
++ hidden_dir = hidden_parent->d_inode;
++ IMustLock(hidden_dir);
++
++ // au_test_perm() is already done
++ hidden_dentry = lkup_one(wh->name, hidden_parent, wh->len, lkup);
++ //if (LktrCond) {dput(hidden_dentry); hidden_dentry = ERR_PTR(-1);}
++ if (!IS_ERR(hidden_dentry)) {
++ err = 0;
++ if (hidden_dentry->d_inode)
++ err = vfsub_unlink(hidden_dir, hidden_dentry,
++ lkup->dlgt);
++ dput(hidden_dentry);
++ } else
++ err = PTR_ERR(hidden_dentry);
++
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static void clean_wh(struct inode *h_dir, struct dentry *wh)
++{
++ TraceEnter();
++ if (wh->d_inode) {
++ int err = vfsub_unlink(h_dir, wh, /*dlgt*/0);
++ if (unlikely(err))
++ Warn("failed unlink %.*s (%d), ignored.\n",
++ DLNPair(wh), err);
++ }
++}
++
++static void clean_plink(struct inode *h_dir, struct dentry *plink)
++{
++ TraceEnter();
++ if (plink->d_inode) {
++ int err = vfsub_rmdir(h_dir, plink, /*dlgt*/0);
++ if (unlikely(err))
++ Warn("failed rmdir %.*s (%d), ignored.\n",
++ DLNPair(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_is_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
++ Err("unknown %.*s exists\n", DLNPair(plink));
++
++ return err;
++}
++
++/*
++ * initialize the whiteout base file/dir for @br.
++ */
++int init_wh(struct dentry *h_root, struct aufs_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 lkup_args lkup = {
++ .nfsmnt = nfsmnt,
++ .dlgt = 0 // always no dlgt
++ };
++ const int do_plink = au_flag_test(sb, AuFlag_PLINK);
++
++ LKTRTrace("nfsmnt %p\n", nfsmnt);
++ BrWhMustWriteLock(br);
++ SiMustWriteLock(sb);
++ h_dir = h_root->d_inode;
++ IMustLock(h_dir);
++
++ // doubly whiteouted
++ wh = lkup_wh(h_root, base_name + 0, &lkup);
++ //if (LktrCond) {dput(wh); wh = ERR_PTR(-1);}
++ err = PTR_ERR(wh);
++ if (IS_ERR(wh))
++ goto out;
++ DEBUG_ON(br->br_wh && br->br_wh != wh);
++
++ plink = lkup_wh(h_root, base_name + 1, &lkup);
++ err = PTR_ERR(plink);
++ if (IS_ERR(plink))
++ goto out_dput_wh;
++ DEBUG_ON(br->br_plink && br->br_plink != plink);
++
++ dput(br->br_wh);
++ dput(br->br_plink);
++ br->br_wh = 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 = vfsub_create(h_dir, wh, WH_MASK, NULL, /*dlgt*/0);
++ else if (S_ISREG(wh->d_inode->i_mode))
++ err = 0;
++ else
++ Err("unknown %.*s/%.*s exists\n",
++ DLNPair(h_root), DLNPair(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:
++ TraceErr(err);
++ return err;
++ out_nolink:
++ Err("%.*s doesn't support link(2), use noplink and rw+nolwh\n",
++ DLNPair(h_root));
++ goto out_dput;
++ out_err:
++ Err("an error(%d) on the writable branch %.*s(%s)\n",
++ err, DLNPair(h_root), au_sbtype(h_root->d_sb));
++ goto out_dput;
++}
++
++struct reinit_br_wh {
++ struct super_block *sb;
++ struct aufs_branch *br;
++};
++
++static void reinit_br_wh(void *arg)
++{
++ int err;
++ struct reinit_br_wh *a = arg;
++ struct inode *hidden_dir, *dir;
++ struct dentry *hidden_root;
++ aufs_bindex_t bindex;
++
++ TraceEnter();
++ DEBUG_ON(!a->br->br_wh || !a->br->br_wh->d_inode || current->fsuid);
++
++ err = 0;
++ /* big lock */
++ si_write_lock(a->sb);
++ if (unlikely(!br_writable(a->br->br_perm)))
++ goto out;
++ bindex = find_brindex(a->sb, a->br->br_id);
++ if (unlikely(bindex < 0))
++ goto out;
++
++ dir = a->sb->s_root->d_inode;
++ hidden_root = a->br->br_wh->d_parent;
++ hidden_dir = hidden_root->d_inode;
++ DEBUG_ON(!hidden_dir->i_op || !hidden_dir->i_op->link);
++ hdir_lock(hidden_dir, dir, bindex);
++ br_wh_write_lock(a->br);
++ err = vfsub_unlink(hidden_dir, a->br->br_wh, /*dlgt*/0);
++ //if (LktrCond) err = -1;
++ dput(a->br->br_wh);
++ a->br->br_wh = NULL;
++ if (!err)
++ err = init_wh(hidden_root, a->br, au_do_nfsmnt(a->br->br_mnt),
++ a->sb);
++ br_wh_write_unlock(a->br);
++ hdir_unlock(hidden_dir, dir, bindex);
++
++ out:
++ atomic_dec(&a->br->br_wh_running);
++ br_put(a->br);
++ si_write_unlock(a->sb);
++ au_mntput(a->sb);
++ kfree(arg);
++ if (unlikely(err))
++ IOErr("err %d\n", err);
++}
++
++static void kick_reinit_br_wh(struct super_block *sb, struct aufs_branch *br)
++{
++ int do_dec;
++ 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_KERNEL);
++ if (arg) {
++ // dec(wh_running), kfree(arg) and br_put() in reinit function
++ arg->sb = sb;
++ arg->br = br;
++ br_get(br);
++ /* prohibit umount */
++ au_mntget(sb);
++ au_wkq_nowait(reinit_br_wh, arg, /*dlgt*/0);
++ do_dec = 0;
++ }
++
++ out:
++ if (do_dec)
++ atomic_dec(&br->br_wh_running);
++}
++
++/*
++ * create the whiteoute @wh.
++ */
++static int link_or_create_wh(struct dentry *wh, struct super_block *sb,
++ aufs_bindex_t bindex)
++{
++ int err, dlgt;
++ struct aufs_branch *br;
++ struct dentry *hidden_parent;
++ struct inode *hidden_dir;
++
++ LKTRTrace("%.*s\n", DLNPair(wh));
++ SiMustReadLock(sb);
++ hidden_parent = wh->d_parent;
++ hidden_dir = hidden_parent->d_inode;
++ IMustLock(hidden_dir);
++
++ dlgt = need_dlgt(sb);
++ br = stobr(sb, bindex);
++ br_wh_read_lock(br);
++ if (br->br_wh) {
++ err = vfsub_link(br->br_wh, hidden_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 = vfsub_create(hidden_dir, wh, WH_MASK, NULL, dlgt);
++
++ out:
++ br_wh_read_unlock(br);
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * create or remove the diropq.
++ */
++static struct dentry *do_diropq(struct dentry *dentry, aufs_bindex_t bindex,
++ int do_create, int dlgt)
++{
++ struct dentry *opq_dentry, *hidden_dentry;
++ struct inode *hidden_dir;
++ int err;
++ struct super_block *sb;
++ struct lkup_args lkup;
++
++ LKTRTrace("%.*s, bindex %d, do_create %d\n", DLNPair(dentry),
++ bindex, do_create);
++ hidden_dentry = au_h_dptr_i(dentry, bindex);
++ DEBUG_ON(!hidden_dentry);
++ hidden_dir = hidden_dentry->d_inode;
++ DEBUG_ON(!hidden_dir || !S_ISDIR(hidden_dir->i_mode));
++ IMustLock(hidden_dir);
++
++ // already checked by au_test_perm().
++ sb = dentry->d_sb;
++ lkup.nfsmnt = au_nfsmnt(sb, bindex);
++ lkup.dlgt = dlgt;
++ opq_dentry = lkup_one(diropq_name.name, hidden_dentry, diropq_name.len,
++ &lkup);
++ //if (LktrCond) {dput(opq_dentry); opq_dentry = ERR_PTR(-1);}
++ if (IS_ERR(opq_dentry))
++ goto out;
++
++ if (do_create) {
++ DEBUG_ON(opq_dentry->d_inode);
++ err = link_or_create_wh(opq_dentry, sb, bindex);
++ //if (LktrCond) {vfs_unlink(hidden_dir, opq_dentry); err = -1;}
++ if (!err) {
++ set_dbdiropq(dentry, bindex);
++ goto out; /* success */
++ }
++ } else {
++ DEBUG_ON(/* !S_ISDIR(dentry->d_inode->i_mode)
++ * || */!opq_dentry->d_inode);
++ err = vfsub_unlink(hidden_dir, opq_dentry, lkup.dlgt);
++ //if (LktrCond) err = -1;
++ if (!err)
++ set_dbdiropq(dentry, -1);
++ }
++ dput(opq_dentry);
++ opq_dentry = ERR_PTR(err);
++
++ out:
++ TraceErrPtr(opq_dentry);
++ return opq_dentry;
++}
++
++struct do_diropq_args {
++ struct dentry **errp;
++ struct dentry *dentry;
++ aufs_bindex_t bindex;
++ int do_create, dlgt;
++};
++
++static void call_do_diropq(void *args)
++{
++ struct do_diropq_args *a = args;
++ *a->errp = do_diropq(a->dentry, a->bindex, a->do_create, a->dlgt);
++}
++
++struct dentry *sio_diropq(struct dentry *dentry, aufs_bindex_t bindex,
++ int do_create, int dlgt)
++{
++ struct dentry *diropq, *hidden_dentry;
++
++ LKTRTrace("%.*s, bindex %d, do_create %d\n",
++ DLNPair(dentry), bindex, do_create);
++
++ hidden_dentry = au_h_dptr_i(dentry, bindex);
++ if (!au_test_perm(hidden_dentry->d_inode, MAY_EXEC | MAY_WRITE, dlgt))
++ diropq = do_diropq(dentry, bindex, do_create, dlgt);
++ else {
++ struct do_diropq_args args = {
++ .errp = &diropq,
++ .dentry = dentry,
++ .bindex = bindex,
++ .do_create = do_create,
++ .dlgt = dlgt
++ };
++ au_wkq_wait(call_do_diropq, &args, /*dlgt*/0);
++ }
++
++ TraceErrPtr(diropq);
++ return diropq;
++}
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * lookup whiteout dentry.
++ * @hidden_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 *lkup_wh(struct dentry *hidden_parent, struct qstr *base_name,
++ struct lkup_args *lkup)
++{
++ int err;
++ struct qstr wh_name;
++ struct dentry *wh_dentry;
++
++ LKTRTrace("%.*s/%.*s\n", DLNPair(hidden_parent), LNPair(base_name));
++ IMustLock(hidden_parent->d_inode);
++
++ err = au_alloc_whname(base_name->name, base_name->len, &wh_name);
++ //if (LktrCond) {au_free_whname(&wh_name); err = -1;}
++ wh_dentry = ERR_PTR(err);
++ if (!err) {
++ // do not superio.
++ wh_dentry = lkup_one(wh_name.name, hidden_parent, wh_name.len,
++ lkup);
++ au_free_whname(&wh_name);
++ }
++ TraceErrPtr(wh_dentry);
++ return wh_dentry;
++}
++
++/*
++ * link/create a whiteout for @dentry on @bindex.
++ */
++struct dentry *simple_create_wh(struct dentry *dentry, aufs_bindex_t bindex,
++ struct dentry *hidden_parent,
++ struct lkup_args *lkup)
++{
++ struct dentry *wh_dentry;
++ int err;
++ struct super_block *sb;
++
++ LKTRTrace("%.*s/%.*s on b%d\n", DLNPair(hidden_parent),
++ DLNPair(dentry), bindex);
++
++ sb = dentry->d_sb;
++ wh_dentry = lkup_wh(hidden_parent, &dentry->d_name, lkup);
++ //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) {
++ IMustLock(hidden_parent->d_inode);
++ err = link_or_create_wh(wh_dentry, sb, bindex);
++ if (!err)
++ set_dbwh(dentry, bindex);
++ else {
++ dput(wh_dentry);
++ wh_dentry = ERR_PTR(err);
++ }
++ }
++
++ TraceErrPtr(wh_dentry);
++ return wh_dentry;
++}
++
++/* ---------------------------------------------------------------------- */
++
++/* Delete all whiteouts in this directory in branch bindex. */
++static int del_wh_children(struct aufs_nhash *whlist,
++ struct dentry *hidden_parent, aufs_bindex_t bindex,
++ struct lkup_args *lkup)
++{
++ int err, i;
++ struct qstr wh_name;
++ char *p;
++ struct inode *hidden_dir;
++ struct hlist_head *head;
++ struct aufs_wh *tpos;
++ struct hlist_node *pos;
++ struct aufs_destr *str;
++
++ LKTRTrace("%.*s\n", DLNPair(hidden_parent));
++ hidden_dir = hidden_parent->d_inode;
++ IMustLock(hidden_dir);
++ DEBUG_ON(IS_RDONLY(hidden_dir));
++ //SiMustReadLock(??);
++
++ err = -ENOMEM;
++ wh_name.name = p = __getname();
++ //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_perm().
++ err = 0;
++ for (i = 0; !err && i < AUFS_NHASH_SIZE; 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(hidden_parent, &wh_name,
++ lkup);
++ //if (LktrCond) err = -1;
++ if (!err)
++ continue;
++ break;
++ }
++ IOErr("whiteout name too long %.*s\n",
++ str->len, str->name);
++ err = -EIO;
++ break;
++ }
++ }
++ __putname(wh_name.name);
++
++ out:
++ TraceErr(err);
++ return err;
++}
++
++struct del_wh_children_args {
++ int *errp;
++ struct aufs_nhash *whlist;
++ struct dentry *hidden_parent;
++ aufs_bindex_t bindex;
++ struct lkup_args *lkup;
++};
++
++static void call_del_wh_children(void *args)
++{
++ struct del_wh_children_args *a = args;
++ *a->errp = del_wh_children(a->whlist, a->hidden_parent, a->bindex,
++ a->lkup);
++}
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * rmdir the whiteouted temporary named dir @hidden_dentry.
++ * @whlist: whiteouted children.
++ */
++int rmdir_whtmp(struct dentry *hidden_dentry, struct aufs_nhash *whlist,
++ aufs_bindex_t bindex, struct inode *dir, struct inode *inode)
++{
++ int err;
++ struct inode *hidden_inode, *hidden_dir;
++ struct lkup_args lkup;
++ struct super_block *sb;
++
++ LKTRTrace("hd %.*s, b%d, i%lu\n",
++ DLNPair(hidden_dentry), bindex, dir->i_ino);
++ IMustLock(dir);
++ IiMustAnyLock(dir);
++ hidden_dir = hidden_dentry->d_parent->d_inode;
++ IMustLock(hidden_dir);
++
++ sb = inode->i_sb;
++ lkup.nfsmnt = au_nfsmnt(sb, bindex);
++ lkup.dlgt = need_dlgt(sb);
++ hidden_inode = hidden_dentry->d_inode;
++ DEBUG_ON(hidden_inode != au_h_iptr_i(inode, bindex));
++ hdir2_lock(hidden_inode, inode, bindex);
++ if (!au_test_perm(hidden_inode, MAY_EXEC | MAY_WRITE, lkup.dlgt))
++ err = del_wh_children(whlist, hidden_dentry, bindex, &lkup);
++ else {
++ // ugly
++ int dlgt = lkup.dlgt;
++ struct del_wh_children_args args = {
++ .errp = &err,
++ .whlist = whlist,
++ .hidden_parent = hidden_dentry,
++ .bindex = bindex,
++ .lkup = &lkup
++ };
++
++ lkup.dlgt = 0;
++ au_wkq_wait(call_del_wh_children, &args, /*dlgt*/0);
++ lkup.dlgt = dlgt;
++ }
++ hdir_unlock(hidden_inode, inode, bindex);
++
++ if (!err) {
++ err = vfsub_rmdir(hidden_dir, hidden_dentry, lkup.dlgt);
++ //d_drop(hidden_dentry);
++ //if (LktrCond) err = -1;
++ }
++
++ if (!err) {
++ if (ibstart(dir) == bindex) {
++ au_cpup_attr_timesizes(dir);
++ //au_cpup_attr_nlink(dir);
++ dir->i_nlink--;
++ }
++ return 0; /* success */
++ }
++
++ Warn("failed removing %.*s(%d), ignored\n",
++ DLNPair(hidden_dentry), err);
++ return err;
++}
++
++static void do_rmdir_whtmp(void *arg)
++{
++ int err;
++ struct rmdir_whtmp_arg *a = arg;
++ struct super_block *sb;
++
++ LKTRTrace("%.*s, b%d, dir i%lu\n",
++ DLNPair(a->h_dentry), a->bindex, a->dir->i_ino);
++
++ i_lock(a->dir);
++ sb = a->dir->i_sb;
++ si_read_lock(sb);
++ err = test_ro(sb, a->bindex, NULL);
++ if (!err) {
++ struct inode *hidden_dir = a->h_dentry->d_parent->d_inode;
++
++ ii_write_lock_child(a->inode);
++ ii_write_lock_parent(a->dir);
++ hdir_lock(hidden_dir, a->dir, a->bindex);
++ err = rmdir_whtmp(a->h_dentry, &a->whlist, a->bindex,
++ a->dir, a->inode);
++ hdir_unlock(hidden_dir, a->dir, a->bindex);
++ ii_write_unlock(a->dir);
++ ii_write_unlock(a->inode);
++ }
++ dput(a->h_dentry);
++ nhash_fin(&a->whlist);
++ iput(a->inode);
++ si_read_unlock(sb);
++ au_mntput(sb);
++ i_unlock(a->dir);
++ iput(a->dir);
++ kfree(arg);
++ if (unlikely(err))
++ IOErr("err %d\n", err);
++}
++
++void kick_rmdir_whtmp(struct dentry *hidden_dentry, struct aufs_nhash *whlist,
++ aufs_bindex_t bindex, struct inode *dir,
++ struct inode *inode, struct rmdir_whtmp_arg *arg)
++{
++ LKTRTrace("%.*s\n", DLNPair(hidden_dentry));
++ IMustLock(dir);
++
++ // all post-process will be done in do_rmdir_whtmp().
++ arg->h_dentry = dget(hidden_dentry);
++ nhash_init(&arg->whlist);
++ nhash_move(&arg->whlist, whlist);
++ arg->bindex = bindex;
++ arg->dir = igrab(dir);
++ arg->inode = igrab(inode);
++ /* prohibit umount */
++ au_mntget(dir->i_sb);
++
++ au_wkq_nowait(do_rmdir_whtmp, arg, /*dlgt*/0);
++}
+diff --git a/fs/aufs/whout.h b/fs/aufs/whout.h
+new file mode 100755
+index 0000000..d44c3cd
+--- /dev/null
++++ b/fs/aufs/whout.h
+@@ -0,0 +1,87 @@
++/*
++ * 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: whout.h,v 1.8 2007/05/14 03:41:52 sfjro Exp $ */
++
++#ifndef __AUFS_WHOUT_H__
++#define __AUFS_WHOUT_H__
++
++#ifdef __KERNEL__
++
++#include <linux/fs.h>
++#include <linux/aufs_type.h>
++
++int au_alloc_whname(const char *name, int len, struct qstr *wh);
++void au_free_whname(struct qstr *wh);
++
++struct lkup_args;
++int is_wh(struct dentry *h_parent, struct qstr *wh_name, int try_sio,
++ struct lkup_args *lkup);
++int is_diropq(struct dentry *h_dentry, struct lkup_args *lkup);
++
++struct dentry *lkup_whtmp(struct dentry *h_parent, struct qstr *prefix,
++ struct lkup_args *lkup);
++int rename_whtmp(struct dentry *dentry, aufs_bindex_t bindex);
++int au_unlink_wh_dentry(struct inode *h_dir, struct dentry *wh_dentry,
++ struct dentry *dentry, int dlgt);
++
++struct aufs_branch;
++int init_wh(struct dentry *h_parent, struct aufs_branch *br,
++ struct vfsmount *nfsmnt, struct super_block *sb);
++
++struct dentry *sio_diropq(struct dentry *dentry, aufs_bindex_t bindex,
++ int do_create, int dlgt);
++
++struct dentry *lkup_wh(struct dentry *h_parent, struct qstr *base_name,
++ struct lkup_args *lkup);
++struct dentry *simple_create_wh(struct dentry *dentry, aufs_bindex_t bindex,
++ struct dentry *h_parent,
++ struct lkup_args *lkup);
++
++/* real rmdir the whiteout-ed dir */
++struct rmdir_whtmp_arg {
++ struct dentry *h_dentry;
++ struct aufs_nhash whlist;
++ aufs_bindex_t bindex;
++ struct inode *dir, *inode;
++};
++
++struct aufs_nhash;
++int rmdir_whtmp(struct dentry *h_dentry, struct aufs_nhash *whlist,
++ aufs_bindex_t bindex, struct inode *dir, struct inode *inode);
++void kick_rmdir_whtmp(struct dentry *h_dentry, struct aufs_nhash *whlist,
++ aufs_bindex_t bindex, struct inode *dir,
++ struct inode *inode, struct rmdir_whtmp_arg *arg);
++
++/* ---------------------------------------------------------------------- */
++
++static inline
++struct dentry *create_diropq(struct dentry *dentry, aufs_bindex_t bindex,
++ int dlgt)
++{
++ return sio_diropq(dentry, bindex, 1, dlgt);
++}
++
++static inline
++int remove_diropq(struct dentry *dentry, aufs_bindex_t bindex, int dlgt)
++{
++ return PTR_ERR(sio_diropq(dentry, bindex, 0, dlgt));
++}
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_WHOUT_H__ */
+diff --git a/fs/aufs/wkq.c b/fs/aufs/wkq.c
+new file mode 100755
+index 0000000..b5ab023
+--- /dev/null
++++ b/fs/aufs/wkq.c
+@@ -0,0 +1,283 @@
++/*
++ * 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: wkq.c,v 1.14 2007/05/14 03:39:10 sfjro Exp $ */
++
++#include <linux/module.h>
++#include "aufs.h"
++
++struct au_wkq *au_wkq;
++
++struct au_cred {
++#ifdef CONFIG_AUFS_DLGT
++ 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;
++
++ unsigned int wait:1;
++ unsigned int dlgt:1;
++ 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->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)
++{
++ DEBUG_ON(!is_au_wkq(current));
++ 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);
++}
++#endif
++
++/* ---------------------------------------------------------------------- */
++
++static void update_busy(struct au_wkq *wkq, struct au_wkinfo *wkinfo)
++{
++#ifdef CONFIG_AUFS_SYSAUFS
++ 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)
++{
++ wkinfo->busyp = &wkq->busy;
++ update_busy(wkq, wkinfo);
++ if (wkinfo->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;
++
++ TraceEnter();
++
++ while (1) {
++ if (wkinfo->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(&au_wkq[i].busy);
++ }
++ } else
++ idle_idx = aufs_nwkq;
++
++ atomic_inc(&au_wkq[idle_idx].busy);
++ if (!enqueue(au_wkq + idle_idx, wkinfo))
++ return; /* success */
++
++ /* impossible? */
++ Warn1("failed to queue_work()\n");
++ yield();
++ }
++}
++
++static AuWkqFunc(wkq_func, wk)
++{
++ struct au_wkinfo *wkinfo = container_of(wk, struct au_wkinfo, wk);
++
++ LKTRTrace("wkinfo{%u, %u, %p, %p, %p}\n",
++ wkinfo->wait, wkinfo->dlgt, wkinfo->func, wkinfo->busyp,
++ wkinfo->comp);
++#ifdef CONFIG_AUFS_DLGT
++ if (!wkinfo->dlgt)
++ wkinfo->func(wkinfo->args);
++ else {
++ struct au_cred cred;
++ cred_switch(&cred, &wkinfo->cred);
++ wkinfo->func(wkinfo->args);
++ cred_revert(&cred);
++ }
++#else
++ wkinfo->func(wkinfo->args);
++#endif
++ atomic_dec(wkinfo->busyp);
++ if (wkinfo->wait)
++ complete(wkinfo->comp);
++ else {
++ kfree(wkinfo);
++ module_put(THIS_MODULE);
++ }
++}
++
++void au_wkq_run(au_wkq_func_t func, void *args, int dlgt, int do_wait)
++{
++ DECLARE_COMPLETION_ONSTACK(comp);
++ struct au_wkinfo _wkinfo = {
++ .wait = 1,
++ .dlgt = !!dlgt,
++ .func = func,
++ .args = args,
++ .comp = &comp
++ }, *wkinfo = &_wkinfo;
++
++ LKTRTrace("dlgt %d, do_wait %d\n", dlgt, do_wait);
++ DEBUG_ON(is_au_wkq(current));
++
++ if (unlikely(!do_wait)) {
++ static DECLARE_WAIT_QUEUE_HEAD(wq);
++ /*
++ * never fail.
++ * wkq_func() must free this wkinfo.
++ * it highly depends upon the implementation of workqueue.
++ */
++ wait_event(wq, (wkinfo = kmalloc(sizeof(*wkinfo), GFP_KERNEL)));
++ wkinfo->wait = 0;
++ wkinfo->dlgt = !!dlgt;
++ wkinfo->func = func;
++ wkinfo->args = args;
++ wkinfo->comp = NULL;
++ __module_get(THIS_MODULE);
++ }
++
++ AuInitWkq(&wkinfo->wk, wkq_func);
++#ifdef CONFIG_AUFS_DLGT
++ if (dlgt)
++ cred_store(&wkinfo->cred);
++#endif
++ do_wkq(wkinfo);
++ if (do_wait)
++ wait_for_completion(wkinfo->comp);
++}
++
++#if 0
++void au_wkq_wait_nwtask(void)
++{
++ static DECLARE_WAIT_QUEUE_HEAD(wq);
++ wait_event(wq, !atomic_read(&au_wkq[aufs_nwkq].busy));
++}
++#endif
++
++void au_wkq_fin(void)
++{
++ int i;
++
++ TraceEnter();
++
++ 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;
++
++#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:
++ TraceErr(err);
++ return err;
++}
+diff --git a/fs/aufs/wkq.h b/fs/aufs/wkq.h
+new file mode 100755
+index 0000000..cc1bb25
+--- /dev/null
++++ b/fs/aufs/wkq.h
+@@ -0,0 +1,81 @@
++/*
++ * 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: wkq.h,v 1.9 2007/05/14 03:39:10 sfjro Exp $ */
++
++#ifndef __AUFS_WKQ_H__
++#define __AUFS_WKQ_H__
++
++#ifdef __KERNEL__
++
++#include <linux/sched.h>
++#include <linux/version.h>
++#include <linux/workqueue.h>
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
++#define DECLARE_COMPLETION_ONSTACK(work) DECLARE_COMPLETION(work)
++#endif
++
++/* ---------------------------------------------------------------------- */
++
++/* internal workqueue named AUFS_WKQ_NAME */
++struct au_wkq {
++ struct workqueue_struct *q;
++
++ /* accounting */
++ atomic_t busy;
++ unsigned int max_busy;
++} ;//__attribute__ ((aligned));
++
++typedef void (*au_wkq_func_t)(void *args);
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
++#define AuInitWkq(wk, func) INIT_WORK(wk, func)
++#define AuWkqFunc(name, arg) void name(struct work_struct *arg)
++#else
++typedef void (*work_func_t)(void *arg);
++#define AuInitWkq(wk, func) INIT_WORK(wk, func, wk)
++#define AuWkqFunc(name, arg) void name(void *arg)
++#endif
++
++extern struct au_wkq *au_wkq;
++
++void au_wkq_run(au_wkq_func_t func, void *args, int dlgt, int do_wait);
++//void au_wkq_wait_nwtask(void);
++int __init au_wkq_init(void);
++void au_wkq_fin(void);
++
++/* ---------------------------------------------------------------------- */
++
++static inline int is_au_wkq(struct task_struct *tsk)
++{
++ return (!tsk->mm && !strcmp(current->comm, AUFS_WKQ_NAME));
++}
++
++static inline void au_wkq_wait(au_wkq_func_t func, void *args, int dlgt)
++{
++ au_wkq_run(func, args, dlgt, /*do_wait*/1);
++}
++
++static inline void au_wkq_nowait(au_wkq_func_t func, void *args, int dlgt)
++{
++ au_wkq_run(func, args, dlgt, /*do_wait*/0);
++}
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_WKQ_H__ */
+diff --git a/fs/aufs/xino.c b/fs/aufs/xino.c
+new file mode 100755
+index 0000000..145491e
+--- /dev/null
++++ b/fs/aufs/xino.c
+@@ -0,0 +1,644 @@
++/*
++ * 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: xino.c,v 1.27 2007/05/14 03:39:10 sfjro Exp $ */
++
++//#include <linux/fs.h>
++#include <linux/fsnotify.h>
++#include <asm/uaccess.h>
++#include "aufs.h"
++
++static 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 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);
++}
++
++/* ---------------------------------------------------------------------- */
++
++static ssize_t xino_fread(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",
++ DLNPair(file->f_dentry), (unsigned long)size, *pos);
++
++ oldfs = get_fs();
++ set_fs(KERNEL_DS);
++ do {
++ err = func(file, (char __user*)buf, size, pos);
++ } while (err == -EAGAIN || err == -EINTR);
++ set_fs(oldfs);
++
++#if 0
++ if (err > 0)
++ fsnotify_access(file->f_dentry);
++#endif
++
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static ssize_t do_xino_fwrite(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 {
++ err = func(file, (const char __user*)buf, size, pos);
++ } while (err == -EAGAIN || err == -EINTR);
++ set_fs(oldfs);
++ lockdep_on();
++
++#if 0
++ if (err > 0)
++ fsnotify_modify(file->f_dentry);
++#endif
++
++ TraceErr(err);
++ return err;
++}
++
++struct do_xino_fwrite_args {
++ ssize_t *errp;
++ 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(writef_t func, struct file *file, void *buf,
++ size_t size, loff_t *pos)
++{
++ ssize_t err;
++
++ LKTRTrace("%.*s, sz %lu, *pos %Ld\n",
++ DLNPair(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 (!is_au_wkq(current)) {
++ struct do_xino_fwrite_args args = {
++ .errp = &err,
++ .func = func,
++ .file = file,
++ .buf = buf,
++ .size = size,
++ .pos = pos
++ };
++ au_wkq_wait(call_do_xino_fwrite, &args, /*dlgt*/0);
++ } else
++ err = do_xino_fwrite(func, file, buf, size, pos);
++
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * 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 xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
++ struct xino *xino)
++{
++ struct aufs_branch *br;
++ loff_t pos;
++ ssize_t sz;
++
++ LKTRTrace("b%d, hi%lu, i%lu\n", bindex, h_ino, xino->ino);
++ //DEBUG_ON(!xino->ino /* || !xino->h_gen */);
++ //WARN_ON(bindex == 0 && h_ino == 31);
++
++ if (unlikely(!au_flag_test(sb, AuFlag_XINO)))
++ return 0;
++
++ br = stobr(sb, bindex);
++ DEBUG_ON(!br || !br->br_xino);
++ pos = h_ino * sizeof(*xino);
++ sz = xino_fwrite(br->br_xino_write, br->br_xino, xino, sizeof(*xino),
++ &pos);
++ //if (LktrCond) sz = 1;
++ if (sz == sizeof(*xino))
++ return 0; /* success */
++
++ IOErr("write failed (%ld)\n", (long)sz);
++ return -EIO;
++}
++
++int xino_write0(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino)
++{
++ struct xino xino = {
++ .ino = 0
++ };
++ return xino_write(sb, bindex, h_ino, &xino);
++}
++
++// why is not atomic_long_inc_return defined?
++static DEFINE_SPINLOCK(alir_lock);
++static long atomic_long_inc_return(atomic_long_t *a)
++{
++ long l;
++
++ spin_lock(&alir_lock);
++ atomic_long_inc(a);
++ l = atomic_long_read(a);
++ spin_unlock(&alir_lock);
++ return l;
++}
++
++ino_t xino_new_ino(struct super_block *sb)
++{
++ ino_t ino;
++
++ TraceEnter();
++ ino = atomic_long_inc_return(&stosi(sb)->si_xino);
++ BUILD_BUG_ON(AUFS_FIRST_INO < AUFS_ROOT_INO);
++ if (ino >= AUFS_ROOT_INO)
++ return ino;
++ else {
++ atomic_long_dec(&stosi(sb)->si_xino);
++ IOErr1("inode number overflow\n");
++ 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 xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
++ struct xino *xino)
++{
++ int err;
++ struct aufs_branch *br;
++ struct file *file;
++ loff_t pos;
++ ssize_t sz;
++
++ LKTRTrace("b%d, hi%lu\n", bindex, h_ino);
++
++ err = 0;
++ xino->ino = 0;
++ if (unlikely(!au_flag_test(sb, AuFlag_XINO)))
++ return 0; /* no ino */
++
++ br = stobr(sb, bindex);
++ file = br->br_xino;
++ DEBUG_ON(!file);
++ pos = h_ino * sizeof(*xino);
++ if (i_size_read(file->f_dentry->d_inode) < pos + sizeof(*xino))
++ return 0; /* no ino */
++
++ sz = xino_fread(br->br_xino_read, file, xino, sizeof(*xino), &pos);
++ if (sz == sizeof(*xino))
++ return 0; /* success */
++
++ err = sz;
++ if (unlikely(sz >= 0)) {
++ err = -EIO;
++ IOErr("xino read error (%ld)\n", (long)sz);
++ }
++
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++struct file *xino_create(struct super_block *sb, char *fname, int silent,
++ struct dentry *parent)
++{
++ struct file *file;
++ int err;
++ struct dentry *hidden_parent;
++ struct inode *hidden_dir;
++ //const int udba = au_flag_test(sb, AuFlag_UDBA_INOTIFY);
++
++ LKTRTrace("%s\n", fname);
++ //DEBUG_ON(!au_flag_test(sb, AuFlag_XINO));
++
++ // LSM may detect it
++ // use sio?
++ 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)
++ Err("open %s(%ld)\n", fname, PTR_ERR(file));
++ return file;
++ }
++#if 0
++ if (unlikely(udba && parent))
++ au_direval_dec(parent);
++#endif
++
++ /* keep file count */
++ hidden_parent = dget_parent(file->f_dentry);
++ hidden_dir = hidden_parent->d_inode;
++ hi_lock_parent(hidden_dir);
++ err = vfsub_unlink(hidden_dir, file->f_dentry, /*dlgt*/0);
++#if 0
++ if (unlikely(!err && udba && parent))
++ au_direval_dec(parent);
++#endif
++ i_unlock(hidden_dir);
++ dput(hidden_parent);
++ if (unlikely(err)) {
++ if (!silent)
++ Err("unlink %s(%d)\n", fname, err);
++ goto out;
++ }
++ if (sb != file->f_dentry->d_sb)
++ return file; /* success */
++
++ if (!silent)
++ Err("%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 = sbr_sb(sb, btgt);
++
++ for (bindex = 0; bindex <= bend; bindex++)
++ if (unlikely(btgt != bindex && tgt_sb == sbr_sb(sb, bindex)))
++ return bindex;
++ return -1;
++}
++
++/*
++ * create a new xinofile at the same place/path as @base_file.
++ */
++static struct file *xino_create2(struct file *base_file)
++{
++ struct file *file;
++ int err;
++ struct dentry *base, *dentry, *parent;
++ struct inode *dir;
++ struct qstr *name;
++ struct lkup_args lkup = {
++ .nfsmnt = NULL,
++ .dlgt = 0
++ };
++
++ base = base_file->f_dentry;
++ LKTRTrace("%.*s\n", DLNPair(base));
++ parent = dget_parent(base);
++ dir = parent->d_inode;
++ IMustLock(dir);
++
++ file = ERR_PTR(-EINVAL);
++ if (unlikely(au_is_nfs(parent->d_sb)))
++ goto out;
++
++ // do not superio, nor NFS.
++ name = &base->d_name;
++ dentry = lkup_one(name->name, parent, name->len, &lkup);
++ //if (LktrCond) {dput(dentry); dentry = ERR_PTR(-1);}
++ if (IS_ERR(dentry)) {
++ file = (void*)dentry;
++ Err("%.*s lookup err %ld\n", LNPair(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);
++ Err("%.*s create err %d\n", LNPair(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)) {
++ Err("%.*s open err %ld\n", LNPair(name), PTR_ERR(file));
++ goto out_dput;
++ }
++ err = vfsub_unlink(dir, dentry, /*dlgt*/0);
++ //if (LktrCond) err = -1;
++ if (!err)
++ goto out_dput; /* success */
++
++ Err("%.*s unlink err %d\n", LNPair(name), err);
++ fput(file);
++ file = ERR_PTR(err);
++
++ out_dput:
++ dput(dentry);
++ out:
++ dput(parent);
++ TraceErrPtr(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 xino_init(struct super_block *sb, aufs_bindex_t bindex,
++ struct file *base_file, int do_test)
++{
++ int err;
++ struct aufs_branch *br;
++ aufs_bindex_t bshared, bend;
++ struct file *file;
++ struct inode *inode, *hidden_inode;
++ struct xino xino;
++
++ LKTRTrace("b%d, base_file %p, do_test %d\n",
++ bindex, base_file, do_test);
++ SiMustWriteLock(sb);
++ DEBUG_ON(!au_flag_test(sb, AuFlag_XINO));
++ br = stobr(sb, bindex);
++ DEBUG_ON(br->br_xino);
++
++ file = NULL;
++ bshared = -1;
++ bend = sbend(sb);
++ if (do_test)
++ bshared = is_sb_shared(sb, bindex, bend);
++ if (unlikely(bshared >= 0)) {
++ struct aufs_branch *shared_br = stobr(sb, bshared);
++ if (shared_br->br_xino) {
++ file = shared_br->br_xino;
++ get_file(file);
++ }
++ }
++
++ if (!file) {
++ struct dentry *parent = dget_parent(base_file->f_dentry);
++ struct inode *dir = parent->d_inode;
++
++ hi_lock_parent(dir);
++ file = xino_create2(base_file);
++ //if (LktrCond) {fput(file); file = ERR_PTR(-1);}
++ i_unlock(dir);
++ dput(parent);
++ err = PTR_ERR(file);
++ if (IS_ERR(file))
++ goto out;
++ }
++ br->br_xino_read = find_readf(file);
++ err = PTR_ERR(br->br_xino_read);
++ if (IS_ERR(br->br_xino_read))
++ goto out_put;
++ br->br_xino_write = find_writef(file);
++ err = PTR_ERR(br->br_xino_write);
++ if (IS_ERR(br->br_xino_write))
++ goto out_put;
++ br->br_xino = file;
++
++ inode = sb->s_root->d_inode;
++ hidden_inode = au_h_iptr_i(inode, bindex);
++ xino.ino = inode->i_ino;
++ //xino.h_gen = hidden_inode->i_generation;
++ //WARN_ON(xino.h_gen == AuXino_INVALID_HGEN);
++ err = xino_write(sb, bindex, hidden_inode->i_ino, &xino);
++ //if (LktrCond) err = -1;
++ if (!err)
++ return 0; /* success */
++
++ br->br_xino = NULL;
++
++ out_put:
++ fput(file);
++ out:
++ TraceErr(err);
++ return err;
++}
++
++/*
++ * set xino mount option.
++ */
++int xino_set(struct super_block *sb, struct opt_xino *xino, int remount)
++{
++ int err, sparse;
++ aufs_bindex_t bindex, bend;
++ struct aufs_branch *br;
++ struct dentry *parent;
++ struct qstr *name;
++ struct file *cur_xino;
++ struct inode *dir;
++
++ LKTRTrace("%s\n", xino->path);
++
++ err = 0;
++ name = &xino->file->f_dentry->d_name;
++ parent = dget_parent(xino->file->f_dentry);
++ dir = parent->d_inode;
++ cur_xino = stobr(sb, 0)->br_xino;
++ if (remount
++ && cur_xino
++ && cur_xino->f_dentry->d_parent == parent
++ && name->len == cur_xino->f_dentry->d_name.len
++ && !memcmp(name->name, cur_xino->f_dentry->d_name.name, name->len))
++ goto out;
++
++ au_flag_set(sb, AuFlag_XINO);
++ bend = sbend(sb);
++ for (bindex = bend; bindex >= 0; bindex--) {
++ br = stobr(sb, bindex);
++ if (unlikely(br->br_xino && file_count(br->br_xino) > 1)) {
++ fput(br->br_xino);
++ br->br_xino = NULL;
++ }
++ }
++
++ for (bindex = 0; bindex <= bend; bindex++) {
++ struct file *file;
++ struct inode *inode;
++
++ br = stobr(sb, bindex);
++ if (unlikely(!br->br_xino))
++ continue;
++
++ DEBUG_ON(file_count(br->br_xino) != 1);
++ hi_lock_parent(dir);
++ file = xino_create2(xino->file);
++ //if (LktrCond) {fput(file); file = ERR_PTR(-1);}
++ err = PTR_ERR(file);
++ if (IS_ERR(file)) {
++ i_unlock(dir);
++ break;
++ }
++ inode = br->br_xino->f_dentry->d_inode;
++ err = au_copy_file(file, br->br_xino, i_size_read(inode), sb,
++ &sparse);
++ //if (LktrCond) err = -1;
++ i_unlock(dir);
++ if (unlikely(err)) {
++ fput(file);
++ break;
++ }
++ fput(br->br_xino);
++ br->br_xino = file;
++ br->br_xino_read = find_readf(file);
++ DEBUG_ON(IS_ERR(br->br_xino_read));
++ br->br_xino_write = find_writef(file);
++ DEBUG_ON(IS_ERR(br->br_xino_write));
++ }
++
++ for (bindex = 0; bindex <= bend; bindex++)
++ if (unlikely(!stobr(sb, bindex)->br_xino)) {
++ err = xino_init(sb, bindex, xino->file, /*do_test*/1);
++ //if (LktrCond) {fput(stobr(sb, bindex)->br_xino);
++ //stobr(sb, bindex)->br_xino = NULL; err = -1;}
++ if (!err)
++ continue;
++ IOErr("creating xino for branch %d(%d), "
++ "forcing noxino\n", bindex, err);
++ err = -EIO;
++ break;
++ }
++ out:
++ dput(parent);
++ if (!err)
++ au_flag_set(sb, AuFlag_XINO);
++ else
++ au_flag_clr(sb, AuFlag_XINO);
++ TraceErr(err);
++ return err;
++}
++
++/*
++ * clear xino mount option
++ */
++int xino_clr(struct super_block *sb)
++{
++ aufs_bindex_t bindex, bend;
++
++ TraceEnter();
++ SiMustWriteLock(sb);
++
++ bend = sbend(sb);
++ for (bindex = 0; bindex <= bend; bindex++) {
++ struct aufs_branch *br;
++ br = stobr(sb, bindex);
++ if (br->br_xino) {
++ fput(br->br_xino);
++ br->br_xino = NULL;
++ }
++ }
++
++ //todo: need to make iunique() to return the larger inode number
++
++ au_flag_clr(sb, AuFlag_XINO);
++ return 0;
++}
++
++/*
++ * create a xinofile at the default place/path.
++ */
++struct file *xino_def(struct super_block *sb)
++{
++ struct file *file;
++ aufs_bindex_t bend, bindex, bwr;
++ char *page, *p;
++
++ bend = sbend(sb);
++ bwr = -1;
++ for (bindex = 0; bindex <= bend; bindex++)
++ if (br_writable(sbr_perm(sb, bindex))
++ && !au_is_nfs(au_h_dptr_i(sb->s_root, bindex)->d_sb)) {
++ bwr = bindex;
++ break;
++ }
++
++ if (bwr != -1) {
++ // todo: rewrite with lkup_one()
++ file = ERR_PTR(-ENOMEM);
++ page = __getname();
++ //if (LktrCond) {__putname(page); page = NULL;}
++ if (unlikely(!page))
++ goto out;
++ p = d_path(au_h_dptr_i(sb->s_root, bwr), sbr_mnt(sb, bwr), 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 = xino_create(sb, p, /*silent*/0, sb->s_root);
++ //if (LktrCond) {fput(file); file = ERR_PTR(-1);}
++ }
++ __putname(page);
++ } else {
++ file = xino_create(sb, AUFS_XINO_DEFPATH, /*silent*/0,
++ /*parent*/NULL);
++ //if (LktrCond) {fput(file); file = ERR_PTR(-1);}
++ }
++
++ out:
++ TraceErrPtr(file);
++ return file;
++}
+diff --git a/fs/squashfs/Makefile b/fs/squashfs/Makefile
+new file mode 100644
+index 0000000..1bc7b06
+--- /dev/null
++++ b/fs/squashfs/Makefile
+@@ -0,0 +1,7 @@
++#
++# Makefile for the linux squashfs routines.
++#
++
++obj-$(CONFIG_SQUASHFS) += squashfs.o
++squashfs-y += inode.o
++squashfs-y += squashfs2_0.o
+diff --git a/fs/squashfs/inode.c b/fs/squashfs/inode.c
+new file mode 100644
+index 0000000..895b699
+--- /dev/null
++++ b/fs/squashfs/inode.c
+@@ -0,0 +1,2329 @@
++/*
++ * Squashfs - a compressed read only filesystem for Linux
++ *
++ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
++ * Phillip Lougher <phillip@lougher.org.uk>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2,
++ * 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ * inode.c
++ */
++
++#include <linux/squashfs_fs.h>
++#include <linux/module.h>
++#include <linux/zlib.h>
++#include <linux/fs.h>
++#include <linux/squashfs_fs_sb.h>
++#include <linux/squashfs_fs_i.h>
++#include <linux/buffer_head.h>
++#include <linux/vfs.h>
++#include <linux/vmalloc.h>
++#include <linux/smp_lock.h>
++
++#include "squashfs.h"
++
++static void vfs_read_inode(struct inode *i);
++static struct dentry *squashfs_get_parent(struct dentry *child);
++static int squashfs_read_inode(struct inode *i, squashfs_inode_t inode);
++static int squashfs_statfs(struct dentry *, struct kstatfs *);
++static int squashfs_symlink_readpage(struct file *file, struct page *page);
++static long long read_blocklist(struct inode *inode, int index,
++ int readahead_blks, char *block_list,
++ unsigned short **block_p, unsigned int *bsize);
++static int squashfs_readpage(struct file *file, struct page *page);
++static int squashfs_readpage4K(struct file *file, struct page *page);
++static int squashfs_readdir(struct file *, void *, filldir_t);
++static struct dentry *squashfs_lookup(struct inode *, struct dentry *,
++ struct nameidata *);
++static int squashfs_remount(struct super_block *s, int *flags, char *data);
++static void squashfs_put_super(struct super_block *);
++static int squashfs_get_sb(struct file_system_type *,int, const char *, void *,
++ struct vfsmount *);
++static struct inode *squashfs_alloc_inode(struct super_block *sb);
++static void squashfs_destroy_inode(struct inode *inode);
++static int init_inodecache(void);
++static void destroy_inodecache(void);
++
++static struct file_system_type squashfs_fs_type = {
++ .owner = THIS_MODULE,
++ .name = "squashfs",
++ .get_sb = squashfs_get_sb,
++ .kill_sb = kill_block_super,
++ .fs_flags = FS_REQUIRES_DEV
++};
++
++static const unsigned char squashfs_filetype_table[] = {
++ DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK
++};
++
++static struct super_operations squashfs_super_ops = {
++ .alloc_inode = squashfs_alloc_inode,
++ .destroy_inode = squashfs_destroy_inode,
++ .statfs = squashfs_statfs,
++ .put_super = squashfs_put_super,
++ .remount_fs = squashfs_remount
++};
++
++static struct super_operations squashfs_export_super_ops = {
++ .alloc_inode = squashfs_alloc_inode,
++ .destroy_inode = squashfs_destroy_inode,
++ .statfs = squashfs_statfs,
++ .put_super = squashfs_put_super,
++ .read_inode = vfs_read_inode
++};
++
++static struct export_operations squashfs_export_ops = {
++ .get_parent = squashfs_get_parent
++};
++
++SQSH_EXTERN const struct address_space_operations squashfs_symlink_aops = {
++ .readpage = squashfs_symlink_readpage
++};
++
++SQSH_EXTERN const struct address_space_operations squashfs_aops = {
++ .readpage = squashfs_readpage
++};
++
++SQSH_EXTERN const struct address_space_operations squashfs_aops_4K = {
++ .readpage = squashfs_readpage4K
++};
++
++static const struct file_operations squashfs_dir_ops = {
++ .read = generic_read_dir,
++ .readdir = squashfs_readdir
++};
++
++SQSH_EXTERN struct inode_operations squashfs_dir_inode_ops = {
++ .lookup = squashfs_lookup
++};
++
++
++static struct buffer_head *get_block_length(struct super_block *s,
++ int *cur_index, int *offset, int *c_byte)
++{
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ unsigned short temp;
++ struct buffer_head *bh;
++
++ if (!(bh = sb_bread(s, *cur_index)))
++ goto out;
++
++ if (msblk->devblksize - *offset == 1) {
++ if (msblk->swap)
++ ((unsigned char *) &temp)[1] = *((unsigned char *)
++ (bh->b_data + *offset));
++ else
++ ((unsigned char *) &temp)[0] = *((unsigned char *)
++ (bh->b_data + *offset));
++ brelse(bh);
++ if (!(bh = sb_bread(s, ++(*cur_index))))
++ goto out;
++ if (msblk->swap)
++ ((unsigned char *) &temp)[0] = *((unsigned char *)
++ bh->b_data);
++ else
++ ((unsigned char *) &temp)[1] = *((unsigned char *)
++ bh->b_data);
++ *c_byte = temp;
++ *offset = 1;
++ } else {
++ if (msblk->swap) {
++ ((unsigned char *) &temp)[1] = *((unsigned char *)
++ (bh->b_data + *offset));
++ ((unsigned char *) &temp)[0] = *((unsigned char *)
++ (bh->b_data + *offset + 1));
++ } else {
++ ((unsigned char *) &temp)[0] = *((unsigned char *)
++ (bh->b_data + *offset));
++ ((unsigned char *) &temp)[1] = *((unsigned char *)
++ (bh->b_data + *offset + 1));
++ }
++ *c_byte = temp;
++ *offset += 2;
++ }
++
++ if (SQUASHFS_CHECK_DATA(msblk->sblk.flags)) {
++ if (*offset == msblk->devblksize) {
++ brelse(bh);
++ if (!(bh = sb_bread(s, ++(*cur_index))))
++ goto out;
++ *offset = 0;
++ }
++ if (*((unsigned char *) (bh->b_data + *offset)) !=
++ SQUASHFS_MARKER_BYTE) {
++ ERROR("Metadata block marker corrupt @ %x\n",
++ *cur_index);
++ brelse(bh);
++ goto out;
++ }
++ (*offset)++;
++ }
++ return bh;
++
++out:
++ return NULL;
++}
++
++
++SQSH_EXTERN unsigned int squashfs_read_data(struct super_block *s, char *buffer,
++ long long index, unsigned int length,
++ long long *next_index, int srclength)
++{
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++ struct buffer_head *bh[((SQUASHFS_FILE_MAX_SIZE - 1) >>
++ msblk->devblksize_log2) + 2];
++ unsigned int offset = index & ((1 << msblk->devblksize_log2) - 1);
++ unsigned int cur_index = index >> msblk->devblksize_log2;
++ int bytes, avail_bytes, b = 0, k = 0;
++ unsigned int compressed;
++ unsigned int c_byte = length;
++
++ if (c_byte) {
++ bytes = msblk->devblksize - offset;
++ compressed = SQUASHFS_COMPRESSED_BLOCK(c_byte);
++ c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte);
++
++ TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n", index, compressed
++ ? "" : "un", (unsigned int) c_byte, srclength);
++
++ if (c_byte > srclength || index < 0 || (index + c_byte) > sblk->bytes_used)
++ goto read_failure;
++
++ if (!(bh[0] = sb_getblk(s, cur_index)))
++ goto block_release;
++
++ for (b = 1; bytes < c_byte; b++) {
++ if (!(bh[b] = sb_getblk(s, ++cur_index)))
++ goto block_release;
++ bytes += msblk->devblksize;
++ }
++ ll_rw_block(READ, b, bh);
++ } else {
++ if (index < 0 || (index + 2) > sblk->bytes_used)
++ goto read_failure;
++
++ if (!(bh[0] = get_block_length(s, &cur_index, &offset,
++ &c_byte)))
++ goto read_failure;
++
++ bytes = msblk->devblksize - offset;
++ compressed = SQUASHFS_COMPRESSED(c_byte);
++ c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte);
++
++ TRACE("Block @ 0x%llx, %scompressed size %d\n", index, compressed
++ ? "" : "un", (unsigned int) c_byte);
++
++ if (c_byte > srclength || (index + c_byte) > sblk->bytes_used)
++ goto read_failure;
++
++ for (b = 1; bytes < c_byte; b++) {
++ if (!(bh[b] = sb_getblk(s, ++cur_index)))
++ goto block_release;
++ bytes += msblk->devblksize;
++ }
++ ll_rw_block(READ, b - 1, bh + 1);
++ }
++
++ if (compressed) {
++ int zlib_err = 0;
++
++ /*
++ * uncompress block
++ */
++
++ mutex_lock(&msblk->read_data_mutex);
++
++ msblk->stream.next_out = buffer;
++ msblk->stream.avail_out = srclength;
++
++ for (bytes = 0; k < b; k++) {
++ avail_bytes = (c_byte - bytes) > (msblk->devblksize - offset) ?
++ msblk->devblksize - offset :
++ c_byte - bytes;
++ wait_on_buffer(bh[k]);
++ if (!buffer_uptodate(bh[k]))
++ goto release_mutex;
++
++ msblk->stream.next_in = bh[k]->b_data + offset;
++ msblk->stream.avail_in = avail_bytes;
++
++ if (k == 0) {
++ zlib_err = zlib_inflateInit(&msblk->stream);
++ if (zlib_err != Z_OK) {
++ ERROR("zlib_inflateInit returned unexpected result 0x%x, srclength %d\n",
++ zlib_err, srclength);
++ goto release_mutex;
++ }
++
++ if (avail_bytes == 0) {
++ offset = 0;
++ brelse(bh[k]);
++ continue;
++ }
++ }
++
++ zlib_err = zlib_inflate(&msblk->stream, Z_NO_FLUSH);
++ if (zlib_err != Z_OK && zlib_err != Z_STREAM_END) {
++ ERROR("zlib_inflate returned unexpected result 0x%x, srclength %d, avail_in %d, avail_out %d\n",
++ zlib_err, srclength, msblk->stream.avail_in, msblk->stream.avail_out);
++ goto release_mutex;
++ }
++
++ bytes += avail_bytes;
++ offset = 0;
++ brelse(bh[k]);
++ }
++
++ if (zlib_err != Z_STREAM_END)
++ goto release_mutex;
++
++ zlib_err = zlib_inflateEnd(&msblk->stream);
++ if (zlib_err != Z_OK) {
++ ERROR("zlib_inflateEnd returned unexpected result 0x%x, srclength %d\n",
++ zlib_err, srclength);
++ goto release_mutex;
++ }
++ bytes = msblk->stream.total_out;
++ mutex_unlock(&msblk->read_data_mutex);
++ } else {
++ int i;
++
++ for(i = 0; i < b; i++) {
++ wait_on_buffer(bh[i]);
++ if(!buffer_uptodate(bh[i]))
++ goto block_release;
++ }
++
++ for (bytes = 0; k < b; k++) {
++ avail_bytes = (c_byte - bytes) > (msblk->devblksize - offset) ?
++ msblk->devblksize - offset :
++ c_byte - bytes;
++ memcpy(buffer + bytes, bh[k]->b_data + offset, avail_bytes);
++ bytes += avail_bytes;
++ offset = 0;
++ brelse(bh[k]);
++ }
++ }
++
++ if (next_index)
++ *next_index = index + c_byte + (length ? 0 :
++ (SQUASHFS_CHECK_DATA(msblk->sblk.flags)
++ ? 3 : 2));
++ return bytes;
++
++release_mutex:
++ mutex_unlock(&msblk->read_data_mutex);
++
++block_release:
++ for (; k < b; k++)
++ brelse(bh[k]);
++
++read_failure:
++ ERROR("sb_bread failed reading block 0x%x\n", cur_index);
++ return 0;
++}
++
++
++SQSH_EXTERN int squashfs_get_cached_block(struct super_block *s, char *buffer,
++ long long block, unsigned int offset,
++ int length, long long *next_block,
++ unsigned int *next_offset)
++{
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ int n, i, bytes, return_length = length;
++ long long next_index;
++
++ TRACE("Entered squashfs_get_cached_block [%llx:%x]\n", block, offset);
++
++ while ( 1 ) {
++ for (i = 0; i < SQUASHFS_CACHED_BLKS; i++)
++ if (msblk->block_cache[i].block == block)
++ break;
++
++ mutex_lock(&msblk->block_cache_mutex);
++
++ if (i == SQUASHFS_CACHED_BLKS) {
++ /* read inode header block */
++ for (i = msblk->next_cache, n = SQUASHFS_CACHED_BLKS;
++ n ; n --, i = (i + 1) %
++ SQUASHFS_CACHED_BLKS)
++ if (msblk->block_cache[i].block !=
++ SQUASHFS_USED_BLK)
++ break;
++
++ if (n == 0) {
++ wait_queue_t wait;
++
++ init_waitqueue_entry(&wait, current);
++ add_wait_queue(&msblk->waitq, &wait);
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ mutex_unlock(&msblk->block_cache_mutex);
++ schedule();
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(&msblk->waitq, &wait);
++ continue;
++ }
++ msblk->next_cache = (i + 1) % SQUASHFS_CACHED_BLKS;
++
++ if (msblk->block_cache[i].block ==
++ SQUASHFS_INVALID_BLK) {
++ if (!(msblk->block_cache[i].data =
++ kmalloc(SQUASHFS_METADATA_SIZE,
++ GFP_KERNEL))) {
++ ERROR("Failed to allocate cache"
++ "block\n");
++ mutex_unlock(&msblk->block_cache_mutex);
++ goto out;
++ }
++ }
++
++ msblk->block_cache[i].block = SQUASHFS_USED_BLK;
++ mutex_unlock(&msblk->block_cache_mutex);
++
++ msblk->block_cache[i].length = squashfs_read_data(s,
++ msblk->block_cache[i].data, block, 0, &next_index, SQUASHFS_METADATA_SIZE);
++ if (msblk->block_cache[i].length == 0) {
++ ERROR("Unable to read cache block [%llx:%x]\n",
++ block, offset);
++ mutex_lock(&msblk->block_cache_mutex);
++ msblk->block_cache[i].block = SQUASHFS_INVALID_BLK;
++ kfree(msblk->block_cache[i].data);
++ wake_up(&msblk->waitq);
++ mutex_unlock(&msblk->block_cache_mutex);
++ goto out;
++ }
++
++ mutex_lock(&msblk->block_cache_mutex);
++ wake_up(&msblk->waitq);
++ msblk->block_cache[i].block = block;
++ msblk->block_cache[i].next_index = next_index;
++ TRACE("Read cache block [%llx:%x]\n", block, offset);
++ }
++
++ if (msblk->block_cache[i].block != block) {
++ mutex_unlock(&msblk->block_cache_mutex);
++ continue;
++ }
++
++ bytes = msblk->block_cache[i].length - offset;
++
++ if (bytes < 1) {
++ mutex_unlock(&msblk->block_cache_mutex);
++ goto out;
++ } else if (bytes >= length) {
++ if (buffer)
++ memcpy(buffer, msblk->block_cache[i].data +
++ offset, length);
++ if (msblk->block_cache[i].length - offset == length) {
++ *next_block = msblk->block_cache[i].next_index;
++ *next_offset = 0;
++ } else {
++ *next_block = block;
++ *next_offset = offset + length;
++ }
++ mutex_unlock(&msblk->block_cache_mutex);
++ goto finish;
++ } else {
++ if (buffer) {
++ memcpy(buffer, msblk->block_cache[i].data +
++ offset, bytes);
++ buffer += bytes;
++ }
++ block = msblk->block_cache[i].next_index;
++ mutex_unlock(&msblk->block_cache_mutex);
++ length -= bytes;
++ offset = 0;
++ }
++ }
++
++finish:
++ return return_length;
++out:
++ return 0;
++}
++
++
++static int get_fragment_location(struct super_block *s, unsigned int fragment,
++ long long *fragment_start_block,
++ unsigned int *fragment_size)
++{
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ long long start_block =
++ msblk->fragment_index[SQUASHFS_FRAGMENT_INDEX(fragment)];
++ int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET(fragment);
++ struct squashfs_fragment_entry fragment_entry;
++
++ if (msblk->swap) {
++ struct squashfs_fragment_entry sfragment_entry;
++
++ if (!squashfs_get_cached_block(s, (char *) &sfragment_entry,
++ start_block, offset,
++ sizeof(sfragment_entry), &start_block,
++ &offset))
++ goto out;
++ SQUASHFS_SWAP_FRAGMENT_ENTRY(&fragment_entry, &sfragment_entry);
++ } else
++ if (!squashfs_get_cached_block(s, (char *) &fragment_entry,
++ start_block, offset,
++ sizeof(fragment_entry), &start_block,
++ &offset))
++ goto out;
++
++ *fragment_start_block = fragment_entry.start_block;
++ *fragment_size = fragment_entry.size;
++
++ return 1;
++
++out:
++ return 0;
++}
++
++
++SQSH_EXTERN void release_cached_fragment(struct squashfs_sb_info *msblk, struct
++ squashfs_fragment_cache *fragment)
++{
++ mutex_lock(&msblk->fragment_mutex);
++ fragment->locked --;
++ wake_up(&msblk->fragment_wait_queue);
++ mutex_unlock(&msblk->fragment_mutex);
++}
++
++
++SQSH_EXTERN struct squashfs_fragment_cache *get_cached_fragment(struct super_block
++ *s, long long start_block,
++ int length)
++{
++ int i, n;
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++
++ while ( 1 ) {
++ mutex_lock(&msblk->fragment_mutex);
++
++ for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS &&
++ msblk->fragment[i].block != start_block; i++);
++
++ if (i == SQUASHFS_CACHED_FRAGMENTS) {
++ for (i = msblk->next_fragment, n =
++ SQUASHFS_CACHED_FRAGMENTS; n &&
++ msblk->fragment[i].locked; n--, i = (i + 1) %
++ SQUASHFS_CACHED_FRAGMENTS);
++
++ if (n == 0) {
++ wait_queue_t wait;
++
++ init_waitqueue_entry(&wait, current);
++ add_wait_queue(&msblk->fragment_wait_queue,
++ &wait);
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ mutex_unlock(&msblk->fragment_mutex);
++ schedule();
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(&msblk->fragment_wait_queue,
++ &wait);
++ continue;
++ }
++ msblk->next_fragment = (msblk->next_fragment + 1) %
++ SQUASHFS_CACHED_FRAGMENTS;
++
++ if (msblk->fragment[i].data == NULL)
++ if (!(msblk->fragment[i].data = SQUASHFS_ALLOC
++ (SQUASHFS_FILE_MAX_SIZE))) {
++ ERROR("Failed to allocate fragment "
++ "cache block\n");
++ mutex_unlock(&msblk->fragment_mutex);
++ goto out;
++ }
++
++ msblk->fragment[i].block = SQUASHFS_INVALID_BLK;
++ msblk->fragment[i].locked = 1;
++ mutex_unlock(&msblk->fragment_mutex);
++
++ if (!(msblk->fragment[i].length = squashfs_read_data(s,
++ msblk->fragment[i].data,
++ start_block, length, NULL, sblk->block_size))) {
++ ERROR("Unable to read fragment cache block "
++ "[%llx]\n", start_block);
++ msblk->fragment[i].locked = 0;
++ smp_mb();
++ goto out;
++ }
++
++ mutex_lock(&msblk->fragment_mutex);
++ msblk->fragment[i].block = start_block;
++ TRACE("New fragment %d, start block %lld, locked %d\n",
++ i, msblk->fragment[i].block,
++ msblk->fragment[i].locked);
++ mutex_unlock(&msblk->fragment_mutex);
++ break;
++ }
++
++ msblk->fragment[i].locked++;
++ mutex_unlock(&msblk->fragment_mutex);
++ TRACE("Got fragment %d, start block %lld, locked %d\n", i,
++ msblk->fragment[i].block,
++ msblk->fragment[i].locked);
++ break;
++ }
++
++ return &msblk->fragment[i];
++
++out:
++ return NULL;
++}
++
++
++static void squashfs_new_inode(struct squashfs_sb_info *msblk, struct inode *i,
++ struct squashfs_base_inode_header *inodeb)
++{
++ i->i_ino = inodeb->inode_number;
++ i->i_mtime.tv_sec = inodeb->mtime;
++ i->i_atime.tv_sec = inodeb->mtime;
++ i->i_ctime.tv_sec = inodeb->mtime;
++ i->i_uid = msblk->uid[inodeb->uid];
++ i->i_mode = inodeb->mode;
++ i->i_size = 0;
++ if (inodeb->guid == SQUASHFS_GUIDS)
++ i->i_gid = i->i_uid;
++ else
++ i->i_gid = msblk->guid[inodeb->guid];
++}
++
++
++static squashfs_inode_t squashfs_inode_lookup(struct super_block *s, int ino)
++{
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ long long start = msblk->inode_lookup_table[SQUASHFS_LOOKUP_BLOCK(ino - 1)];
++ int offset = SQUASHFS_LOOKUP_BLOCK_OFFSET(ino - 1);
++ squashfs_inode_t inode;
++
++ TRACE("Entered squashfs_inode_lookup, inode_number = %d\n", ino);
++
++ if (msblk->swap) {
++ squashfs_inode_t sinode;
++
++ if (!squashfs_get_cached_block(s, (char *) &sinode, start, offset,
++ sizeof(sinode), &start, &offset))
++ goto out;
++ SQUASHFS_SWAP_INODE_T((&inode), &sinode);
++ } else if (!squashfs_get_cached_block(s, (char *) &inode, start, offset,
++ sizeof(inode), &start, &offset))
++ goto out;
++
++ TRACE("squashfs_inode_lookup, inode = 0x%llx\n", inode);
++
++ return inode;
++
++out:
++ return SQUASHFS_INVALID_BLK;
++}
++
++
++static void vfs_read_inode(struct inode *i)
++{
++ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
++ squashfs_inode_t inode = squashfs_inode_lookup(i->i_sb, i->i_ino);
++
++ TRACE("Entered vfs_read_inode\n");
++
++ if(inode != SQUASHFS_INVALID_BLK)
++ (msblk->read_inode)(i, inode);
++}
++
++
++static struct dentry *squashfs_get_parent(struct dentry *child)
++{
++ struct inode *i = child->d_inode;
++ struct inode *parent = iget(i->i_sb, SQUASHFS_I(i)->u.s2.parent_inode);
++ struct dentry *rv;
++
++ TRACE("Entered squashfs_get_parent\n");
++
++ if(parent == NULL) {
++ rv = ERR_PTR(-EACCES);
++ goto out;
++ }
++
++ rv = d_alloc_anon(parent);
++ if(rv == NULL)
++ rv = ERR_PTR(-ENOMEM);
++
++out:
++ return rv;
++}
++
++
++SQSH_EXTERN struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode, unsigned int inode_number)
++{
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ struct inode *i = iget_locked(s, inode_number);
++
++ TRACE("Entered squashfs_iget\n");
++
++ if(i && (i->i_state & I_NEW)) {
++ (msblk->read_inode)(i, inode);
++ unlock_new_inode(i);
++ }
++
++ return i;
++}
++
++
++static int squashfs_read_inode(struct inode *i, squashfs_inode_t inode)
++{
++ struct super_block *s = i->i_sb;
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++ long long block = SQUASHFS_INODE_BLK(inode) +
++ sblk->inode_table_start;
++ unsigned int offset = SQUASHFS_INODE_OFFSET(inode);
++ long long next_block;
++ unsigned int next_offset;
++ union squashfs_inode_header id, sid;
++ struct squashfs_base_inode_header *inodeb = &id.base,
++ *sinodeb = &sid.base;
++
++ TRACE("Entered squashfs_read_inode\n");
++
++ if (msblk->swap) {
++ if (!squashfs_get_cached_block(s, (char *) sinodeb, block,
++ offset, sizeof(*sinodeb), &next_block,
++ &next_offset))
++ goto failed_read;
++ SQUASHFS_SWAP_BASE_INODE_HEADER(inodeb, sinodeb,
++ sizeof(*sinodeb));
++ } else
++ if (!squashfs_get_cached_block(s, (char *) inodeb, block,
++ offset, sizeof(*inodeb), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ squashfs_new_inode(msblk, i, inodeb);
++
++ switch(inodeb->inode_type) {
++ case SQUASHFS_FILE_TYPE: {
++ unsigned int frag_size;
++ long long frag_blk;
++ struct squashfs_reg_inode_header *inodep = &id.reg;
++ struct squashfs_reg_inode_header *sinodep = &sid.reg;
++
++ if (msblk->swap) {
++ if (!squashfs_get_cached_block(s, (char *)
++ sinodep, block, offset,
++ sizeof(*sinodep), &next_block,
++ &next_offset))
++ goto failed_read;
++ SQUASHFS_SWAP_REG_INODE_HEADER(inodep, sinodep);
++ } else
++ if (!squashfs_get_cached_block(s, (char *)
++ inodep, block, offset,
++ sizeof(*inodep), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ frag_blk = SQUASHFS_INVALID_BLK;
++ if (inodep->fragment != SQUASHFS_INVALID_FRAG &&
++ !get_fragment_location(s,
++ inodep->fragment, &frag_blk, &frag_size))
++ goto failed_read;
++
++ i->i_nlink = 1;
++ i->i_size = inodep->file_size;
++ i->i_fop = &generic_ro_fops;
++ i->i_mode |= S_IFREG;
++ i->i_blocks = ((i->i_size - 1) >> 9) + 1;
++ SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk;
++ SQUASHFS_I(i)->u.s1.fragment_size = frag_size;
++ SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset;
++ SQUASHFS_I(i)->start_block = inodep->start_block;
++ SQUASHFS_I(i)->u.s1.block_list_start = next_block;
++ SQUASHFS_I(i)->offset = next_offset;
++ if (sblk->block_size > 4096)
++ i->i_data.a_ops = &squashfs_aops;
++ else
++ i->i_data.a_ops = &squashfs_aops_4K;
++
++ TRACE("File inode %x:%x, start_block %llx, "
++ "block_list_start %llx, offset %x\n",
++ SQUASHFS_INODE_BLK(inode), offset,
++ inodep->start_block, next_block,
++ next_offset);
++ break;
++ }
++ case SQUASHFS_LREG_TYPE: {
++ unsigned int frag_size;
++ long long frag_blk;
++ struct squashfs_lreg_inode_header *inodep = &id.lreg;
++ struct squashfs_lreg_inode_header *sinodep = &sid.lreg;
++
++ if (msblk->swap) {
++ if (!squashfs_get_cached_block(s, (char *)
++ sinodep, block, offset,
++ sizeof(*sinodep), &next_block,
++ &next_offset))
++ goto failed_read;
++ SQUASHFS_SWAP_LREG_INODE_HEADER(inodep, sinodep);
++ } else
++ if (!squashfs_get_cached_block(s, (char *)
++ inodep, block, offset,
++ sizeof(*inodep), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ frag_blk = SQUASHFS_INVALID_BLK;
++ if (inodep->fragment != SQUASHFS_INVALID_FRAG &&
++ !get_fragment_location(s,
++ inodep->fragment, &frag_blk, &frag_size))
++ goto failed_read;
++
++ i->i_nlink = inodep->nlink;
++ i->i_size = inodep->file_size;
++ i->i_fop = &generic_ro_fops;
++ i->i_mode |= S_IFREG;
++ i->i_blocks = ((i->i_size - 1) >> 9) + 1;
++ SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk;
++ SQUASHFS_I(i)->u.s1.fragment_size = frag_size;
++ SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset;
++ SQUASHFS_I(i)->start_block = inodep->start_block;
++ SQUASHFS_I(i)->u.s1.block_list_start = next_block;
++ SQUASHFS_I(i)->offset = next_offset;
++ if (sblk->block_size > 4096)
++ i->i_data.a_ops = &squashfs_aops;
++ else
++ i->i_data.a_ops = &squashfs_aops_4K;
++
++ TRACE("File inode %x:%x, start_block %llx, "
++ "block_list_start %llx, offset %x\n",
++ SQUASHFS_INODE_BLK(inode), offset,
++ inodep->start_block, next_block,
++ next_offset);
++ break;
++ }
++ case SQUASHFS_DIR_TYPE: {
++ struct squashfs_dir_inode_header *inodep = &id.dir;
++ struct squashfs_dir_inode_header *sinodep = &sid.dir;
++
++ if (msblk->swap) {
++ if (!squashfs_get_cached_block(s, (char *)
++ sinodep, block, offset,
++ sizeof(*sinodep), &next_block,
++ &next_offset))
++ goto failed_read;
++ SQUASHFS_SWAP_DIR_INODE_HEADER(inodep, sinodep);
++ } else
++ if (!squashfs_get_cached_block(s, (char *)
++ inodep, block, offset,
++ sizeof(*inodep), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ i->i_nlink = inodep->nlink;
++ i->i_size = inodep->file_size;
++ i->i_op = &squashfs_dir_inode_ops;
++ i->i_fop = &squashfs_dir_ops;
++ i->i_mode |= S_IFDIR;
++ SQUASHFS_I(i)->start_block = inodep->start_block;
++ SQUASHFS_I(i)->offset = inodep->offset;
++ SQUASHFS_I(i)->u.s2.directory_index_count = 0;
++ SQUASHFS_I(i)->u.s2.parent_inode = inodep->parent_inode;
++
++ TRACE("Directory inode %x:%x, start_block %x, offset "
++ "%x\n", SQUASHFS_INODE_BLK(inode),
++ offset, inodep->start_block,
++ inodep->offset);
++ break;
++ }
++ case SQUASHFS_LDIR_TYPE: {
++ struct squashfs_ldir_inode_header *inodep = &id.ldir;
++ struct squashfs_ldir_inode_header *sinodep = &sid.ldir;
++
++ if (msblk->swap) {
++ if (!squashfs_get_cached_block(s, (char *)
++ sinodep, block, offset,
++ sizeof(*sinodep), &next_block,
++ &next_offset))
++ goto failed_read;
++ SQUASHFS_SWAP_LDIR_INODE_HEADER(inodep,
++ sinodep);
++ } else
++ if (!squashfs_get_cached_block(s, (char *)
++ inodep, block, offset,
++ sizeof(*inodep), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ i->i_nlink = inodep->nlink;
++ i->i_size = inodep->file_size;
++ i->i_op = &squashfs_dir_inode_ops;
++ i->i_fop = &squashfs_dir_ops;
++ i->i_mode |= S_IFDIR;
++ SQUASHFS_I(i)->start_block = inodep->start_block;
++ SQUASHFS_I(i)->offset = inodep->offset;
++ SQUASHFS_I(i)->u.s2.directory_index_start = next_block;
++ SQUASHFS_I(i)->u.s2.directory_index_offset =
++ next_offset;
++ SQUASHFS_I(i)->u.s2.directory_index_count =
++ inodep->i_count;
++ SQUASHFS_I(i)->u.s2.parent_inode = inodep->parent_inode;
++
++ TRACE("Long directory inode %x:%x, start_block %x, "
++ "offset %x\n",
++ SQUASHFS_INODE_BLK(inode), offset,
++ inodep->start_block, inodep->offset);
++ break;
++ }
++ case SQUASHFS_SYMLINK_TYPE: {
++ struct squashfs_symlink_inode_header *inodep =
++ &id.symlink;
++ struct squashfs_symlink_inode_header *sinodep =
++ &sid.symlink;
++
++ if (msblk->swap) {
++ if (!squashfs_get_cached_block(s, (char *)
++ sinodep, block, offset,
++ sizeof(*sinodep), &next_block,
++ &next_offset))
++ goto failed_read;
++ SQUASHFS_SWAP_SYMLINK_INODE_HEADER(inodep,
++ sinodep);
++ } else
++ if (!squashfs_get_cached_block(s, (char *)
++ inodep, block, offset,
++ sizeof(*inodep), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ i->i_nlink = inodep->nlink;
++ i->i_size = inodep->symlink_size;
++ i->i_op = &page_symlink_inode_operations;
++ i->i_data.a_ops = &squashfs_symlink_aops;
++ i->i_mode |= S_IFLNK;
++ SQUASHFS_I(i)->start_block = next_block;
++ SQUASHFS_I(i)->offset = next_offset;
++
++ TRACE("Symbolic link inode %x:%x, start_block %llx, "
++ "offset %x\n",
++ SQUASHFS_INODE_BLK(inode), offset,
++ next_block, next_offset);
++ break;
++ }
++ case SQUASHFS_BLKDEV_TYPE:
++ case SQUASHFS_CHRDEV_TYPE: {
++ struct squashfs_dev_inode_header *inodep = &id.dev;
++ struct squashfs_dev_inode_header *sinodep = &sid.dev;
++
++ if (msblk->swap) {
++ if (!squashfs_get_cached_block(s, (char *)
++ sinodep, block, offset,
++ sizeof(*sinodep), &next_block,
++ &next_offset))
++ goto failed_read;
++ SQUASHFS_SWAP_DEV_INODE_HEADER(inodep, sinodep);
++ } else
++ if (!squashfs_get_cached_block(s, (char *)
++ inodep, block, offset,
++ sizeof(*inodep), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ i->i_nlink = inodep->nlink;
++ i->i_mode |= (inodeb->inode_type ==
++ SQUASHFS_CHRDEV_TYPE) ? S_IFCHR :
++ S_IFBLK;
++ init_special_inode(i, i->i_mode,
++ old_decode_dev(inodep->rdev));
++
++ TRACE("Device inode %x:%x, rdev %x\n",
++ SQUASHFS_INODE_BLK(inode), offset,
++ inodep->rdev);
++ break;
++ }
++ case SQUASHFS_FIFO_TYPE:
++ case SQUASHFS_SOCKET_TYPE: {
++ struct squashfs_ipc_inode_header *inodep = &id.ipc;
++ struct squashfs_ipc_inode_header *sinodep = &sid.ipc;
++
++ if (msblk->swap) {
++ if (!squashfs_get_cached_block(s, (char *)
++ sinodep, block, offset,
++ sizeof(*sinodep), &next_block,
++ &next_offset))
++ goto failed_read;
++ SQUASHFS_SWAP_IPC_INODE_HEADER(inodep, sinodep);
++ } else
++ if (!squashfs_get_cached_block(s, (char *)
++ inodep, block, offset,
++ sizeof(*inodep), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ i->i_nlink = inodep->nlink;
++ i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE)
++ ? S_IFIFO : S_IFSOCK;
++ init_special_inode(i, i->i_mode, 0);
++ break;
++ }
++ default:
++ ERROR("Unknown inode type %d in squashfs_iget!\n",
++ inodeb->inode_type);
++ goto failed_read1;
++ }
++
++ return 1;
++
++failed_read:
++ ERROR("Unable to read inode [%llx:%x]\n", block, offset);
++
++failed_read1:
++ make_bad_inode(i);
++ return 0;
++}
++
++
++static int read_inode_lookup_table(struct super_block *s)
++{
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++ unsigned int length = SQUASHFS_LOOKUP_BLOCK_BYTES(sblk->inodes);
++
++ TRACE("In read_inode_lookup_table, length %d\n", length);
++
++ /* Allocate inode lookup table */
++ if (!(msblk->inode_lookup_table = kmalloc(length, GFP_KERNEL))) {
++ ERROR("Failed to allocate inode lookup table\n");
++ return 0;
++ }
++
++ if (!squashfs_read_data(s, (char *) msblk->inode_lookup_table,
++ sblk->lookup_table_start, length |
++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length)) {
++ ERROR("unable to read inode lookup table\n");
++ return 0;
++ }
++
++ if (msblk->swap) {
++ int i;
++ long long block;
++
++ for (i = 0; i < SQUASHFS_LOOKUP_BLOCKS(sblk->inodes); i++) {
++ SQUASHFS_SWAP_LOOKUP_BLOCKS((&block),
++ &msblk->inode_lookup_table[i], 1);
++ msblk->inode_lookup_table[i] = block;
++ }
++ }
++
++ return 1;
++}
++
++
++static int read_fragment_index_table(struct super_block *s)
++{
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++ unsigned int length = SQUASHFS_FRAGMENT_INDEX_BYTES(sblk->fragments);
++
++ if(length == 0)
++ return 1;
++
++ /* Allocate fragment index table */
++ if (!(msblk->fragment_index = kmalloc(length, GFP_KERNEL))) {
++ ERROR("Failed to allocate fragment index table\n");
++ return 0;
++ }
++
++ if (!squashfs_read_data(s, (char *) msblk->fragment_index,
++ sblk->fragment_table_start, length |
++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length)) {
++ ERROR("unable to read fragment index table\n");
++ return 0;
++ }
++
++ if (msblk->swap) {
++ int i;
++ long long fragment;
++
++ for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES(sblk->fragments); i++) {
++ SQUASHFS_SWAP_FRAGMENT_INDEXES((&fragment),
++ &msblk->fragment_index[i], 1);
++ msblk->fragment_index[i] = fragment;
++ }
++ }
++
++ return 1;
++}
++
++
++static int supported_squashfs_filesystem(struct squashfs_sb_info *msblk, int silent)
++{
++ struct squashfs_super_block *sblk = &msblk->sblk;
++
++ msblk->read_inode = squashfs_read_inode;
++ msblk->read_blocklist = read_blocklist;
++ msblk->read_fragment_index_table = read_fragment_index_table;
++
++ if (sblk->s_major == 1) {
++ if (!squashfs_1_0_supported(msblk)) {
++ SERROR("Major/Minor mismatch, Squashfs 1.0 filesystems "
++ "are unsupported\n");
++ SERROR("Please recompile with "
++ "Squashfs 1.0 support enabled\n");
++ return 0;
++ }
++ } else if (sblk->s_major == 2) {
++ if (!squashfs_2_0_supported(msblk)) {
++ SERROR("Major/Minor mismatch, Squashfs 2.0 filesystems "
++ "are unsupported\n");
++ SERROR("Please recompile with "
++ "Squashfs 2.0 support enabled\n");
++ return 0;
++ }
++ } else if(sblk->s_major != SQUASHFS_MAJOR || sblk->s_minor >
++ SQUASHFS_MINOR) {
++ SERROR("Major/Minor mismatch, trying to mount newer %d.%d "
++ "filesystem\n", sblk->s_major, sblk->s_minor);
++ SERROR("Please update your kernel\n");
++ return 0;
++ }
++
++ return 1;
++}
++
++
++static int squashfs_fill_super(struct super_block *s, void *data, int silent)
++{
++ struct squashfs_sb_info *msblk;
++ struct squashfs_super_block *sblk;
++ int i;
++ char b[BDEVNAME_SIZE];
++ struct inode *root;
++
++ TRACE("Entered squashfs_read_superblock\n");
++
++ if (!(s->s_fs_info = kmalloc(sizeof(struct squashfs_sb_info),
++ GFP_KERNEL))) {
++ ERROR("Failed to allocate superblock\n");
++ goto failure;
++ }
++ memset(s->s_fs_info, 0, sizeof(struct squashfs_sb_info));
++ msblk = s->s_fs_info;
++ if (!(msblk->stream.workspace = vmalloc(zlib_inflate_workspacesize()))) {
++ ERROR("Failed to allocate zlib workspace\n");
++ goto failure;
++ }
++ sblk = &msblk->sblk;
++
++ msblk->devblksize = sb_min_blocksize(s, BLOCK_SIZE);
++ msblk->devblksize_log2 = ffz(~msblk->devblksize);
++
++ mutex_init(&msblk->read_data_mutex);
++ mutex_init(&msblk->read_page_mutex);
++ mutex_init(&msblk->block_cache_mutex);
++ mutex_init(&msblk->fragment_mutex);
++ mutex_init(&msblk->meta_index_mutex);
++
++ init_waitqueue_head(&msblk->waitq);
++ init_waitqueue_head(&msblk->fragment_wait_queue);
++
++ sblk->bytes_used = sizeof(struct squashfs_super_block);
++ if (!squashfs_read_data(s, (char *) sblk, SQUASHFS_START,
++ sizeof(struct squashfs_super_block) |
++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, sizeof(struct squashfs_super_block))) {
++ SERROR("unable to read superblock\n");
++ goto failed_mount;
++ }
++
++ /* Check it is a SQUASHFS superblock */
++ msblk->swap = 0;
++ if ((s->s_magic = sblk->s_magic) != SQUASHFS_MAGIC) {
++ if (sblk->s_magic == SQUASHFS_MAGIC_SWAP) {
++ struct squashfs_super_block ssblk;
++
++ WARNING("Mounting a different endian SQUASHFS "
++ "filesystem on %s\n", bdevname(s->s_bdev, b));
++
++ SQUASHFS_SWAP_SUPER_BLOCK(&ssblk, sblk);
++ memcpy(sblk, &ssblk, sizeof(struct squashfs_super_block));
++ msblk->swap = 1;
++ } else {
++ SERROR("Can't find a SQUASHFS superblock on %s\n",
++ bdevname(s->s_bdev, b));
++ goto failed_mount;
++ }
++ }
++
++ /* Check the MAJOR & MINOR versions */
++ if(!supported_squashfs_filesystem(msblk, silent))
++ goto failed_mount;
++
++ /* Check the filesystem does not extend beyond the end of the
++ block device */
++ if(sblk->bytes_used < 0 || sblk->bytes_used > i_size_read(s->s_bdev->bd_inode))
++ goto failed_mount;
++
++ /* Check the root inode for sanity */
++ if (SQUASHFS_INODE_OFFSET(sblk->root_inode) > SQUASHFS_METADATA_SIZE)
++ goto failed_mount;
++
++ TRACE("Found valid superblock on %s\n", bdevname(s->s_bdev, b));
++ TRACE("Inodes are %scompressed\n",
++ SQUASHFS_UNCOMPRESSED_INODES
++ (sblk->flags) ? "un" : "");
++ TRACE("Data is %scompressed\n",
++ SQUASHFS_UNCOMPRESSED_DATA(sblk->flags)
++ ? "un" : "");
++ TRACE("Check data is %s present in the filesystem\n",
++ SQUASHFS_CHECK_DATA(sblk->flags) ?
++ "" : "not");
++ TRACE("Filesystem size %lld bytes\n", sblk->bytes_used);
++ TRACE("Block size %d\n", sblk->block_size);
++ TRACE("Number of inodes %d\n", sblk->inodes);
++ if (sblk->s_major > 1)
++ TRACE("Number of fragments %d\n", sblk->fragments);
++ TRACE("Number of uids %d\n", sblk->no_uids);
++ TRACE("Number of gids %d\n", sblk->no_guids);
++ TRACE("sblk->inode_table_start %llx\n", sblk->inode_table_start);
++ TRACE("sblk->directory_table_start %llx\n", sblk->directory_table_start);
++ if (sblk->s_major > 1)
++ TRACE("sblk->fragment_table_start %llx\n",
++ sblk->fragment_table_start);
++ TRACE("sblk->uid_start %llx\n", sblk->uid_start);
++
++ s->s_flags |= MS_RDONLY;
++ s->s_op = &squashfs_super_ops;
++
++ /* Init inode_table block pointer array */
++ if (!(msblk->block_cache = kmalloc(sizeof(struct squashfs_cache) *
++ SQUASHFS_CACHED_BLKS, GFP_KERNEL))) {
++ ERROR("Failed to allocate block cache\n");
++ goto failed_mount;
++ }
++
++ for (i = 0; i < SQUASHFS_CACHED_BLKS; i++)
++ msblk->block_cache[i].block = SQUASHFS_INVALID_BLK;
++
++ msblk->next_cache = 0;
++
++ /* Allocate read_page block */
++ if (!(msblk->read_page = kmalloc(sblk->block_size, GFP_KERNEL))) {
++ ERROR("Failed to allocate read_page block\n");
++ goto failed_mount;
++ }
++
++ /* Allocate uid and gid tables */
++ if (!(msblk->uid = kmalloc((sblk->no_uids + sblk->no_guids) *
++ sizeof(unsigned int), GFP_KERNEL))) {
++ ERROR("Failed to allocate uid/gid table\n");
++ goto failed_mount;
++ }
++ msblk->guid = msblk->uid + sblk->no_uids;
++
++ if (msblk->swap) {
++ unsigned int suid[sblk->no_uids + sblk->no_guids];
++
++ if (!squashfs_read_data(s, (char *) &suid, sblk->uid_start,
++ ((sblk->no_uids + sblk->no_guids) *
++ sizeof(unsigned int)) |
++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, (sblk->no_uids + sblk->no_guids) * sizeof(unsigned int))) {
++ ERROR("unable to read uid/gid table\n");
++ goto failed_mount;
++ }
++
++ SQUASHFS_SWAP_DATA(msblk->uid, suid, (sblk->no_uids +
++ sblk->no_guids), (sizeof(unsigned int) * 8));
++ } else
++ if (!squashfs_read_data(s, (char *) msblk->uid, sblk->uid_start,
++ ((sblk->no_uids + sblk->no_guids) *
++ sizeof(unsigned int)) |
++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, (sblk->no_uids + sblk->no_guids) * sizeof(unsigned int))) {
++ ERROR("unable to read uid/gid table\n");
++ goto failed_mount;
++ }
++
++
++ if (sblk->s_major == 1 && squashfs_1_0_supported(msblk))
++ goto allocate_root;
++
++ if (!(msblk->fragment = kmalloc(sizeof(struct squashfs_fragment_cache) *
++ SQUASHFS_CACHED_FRAGMENTS, GFP_KERNEL))) {
++ ERROR("Failed to allocate fragment block cache\n");
++ goto failed_mount;
++ }
++
++ for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++) {
++ msblk->fragment[i].locked = 0;
++ msblk->fragment[i].block = SQUASHFS_INVALID_BLK;
++ msblk->fragment[i].data = NULL;
++ }
++
++ msblk->next_fragment = 0;
++
++ /* Allocate and read fragment index table */
++ if (msblk->read_fragment_index_table(s) == 0)
++ goto failed_mount;
++
++ if(sblk->s_major < 3 || sblk->lookup_table_start == SQUASHFS_INVALID_BLK)
++ goto allocate_root;
++
++ /* Allocate and read inode lookup table */
++ if (read_inode_lookup_table(s) == 0)
++ goto failed_mount;
++
++ s->s_op = &squashfs_export_super_ops;
++ s->s_export_op = &squashfs_export_ops;
++
++allocate_root:
++ root = new_inode(s);
++ if ((msblk->read_inode)(root, sblk->root_inode) == 0)
++ goto failed_mount;
++ insert_inode_hash(root);
++
++ if ((s->s_root = d_alloc_root(root)) == NULL) {
++ ERROR("Root inode create failed\n");
++ iput(root);
++ goto failed_mount;
++ }
++
++ TRACE("Leaving squashfs_read_super\n");
++ return 0;
++
++failed_mount:
++ kfree(msblk->inode_lookup_table);
++ kfree(msblk->fragment_index);
++ kfree(msblk->fragment);
++ kfree(msblk->uid);
++ kfree(msblk->read_page);
++ kfree(msblk->block_cache);
++ kfree(msblk->fragment_index_2);
++ vfree(msblk->stream.workspace);
++ kfree(s->s_fs_info);
++ s->s_fs_info = NULL;
++ return -EINVAL;
++
++failure:
++ return -ENOMEM;
++}
++
++
++static int squashfs_statfs(struct dentry *dentry, struct kstatfs *buf)
++{
++ struct squashfs_sb_info *msblk = dentry->d_sb->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++
++ TRACE("Entered squashfs_statfs\n");
++
++ buf->f_type = SQUASHFS_MAGIC;
++ buf->f_bsize = sblk->block_size;
++ buf->f_blocks = ((sblk->bytes_used - 1) >> sblk->block_log) + 1;
++ buf->f_bfree = buf->f_bavail = 0;
++ buf->f_files = sblk->inodes;
++ buf->f_ffree = 0;
++ buf->f_namelen = SQUASHFS_NAME_LEN;
++
++ return 0;
++}
++
++
++static int squashfs_symlink_readpage(struct file *file, struct page *page)
++{
++ struct inode *inode = page->mapping->host;
++ int index = page->index << PAGE_CACHE_SHIFT, length, bytes;
++ long long block = SQUASHFS_I(inode)->start_block;
++ int offset = SQUASHFS_I(inode)->offset;
++ void *pageaddr = kmap(page);
++
++ TRACE("Entered squashfs_symlink_readpage, page index %ld, start block "
++ "%llx, offset %x\n", page->index,
++ SQUASHFS_I(inode)->start_block,
++ SQUASHFS_I(inode)->offset);
++
++ for (length = 0; length < index; length += bytes) {
++ if (!(bytes = squashfs_get_cached_block(inode->i_sb, NULL,
++ block, offset, PAGE_CACHE_SIZE, &block,
++ &offset))) {
++ ERROR("Unable to read symbolic link [%llx:%x]\n", block,
++ offset);
++ goto skip_read;
++ }
++ }
++
++ if (length != index) {
++ ERROR("(squashfs_symlink_readpage) length != index\n");
++ bytes = 0;
++ goto skip_read;
++ }
++
++ bytes = (i_size_read(inode) - length) > PAGE_CACHE_SIZE ? PAGE_CACHE_SIZE :
++ i_size_read(inode) - length;
++
++ if (!(bytes = squashfs_get_cached_block(inode->i_sb, pageaddr, block,
++ offset, bytes, &block, &offset)))
++ ERROR("Unable to read symbolic link [%llx:%x]\n", block, offset);
++
++skip_read:
++ memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
++ kunmap(page);
++ flush_dcache_page(page);
++ SetPageUptodate(page);
++ unlock_page(page);
++
++ return 0;
++}
++
++
++struct meta_index *locate_meta_index(struct inode *inode, int index, int offset)
++{
++ struct meta_index *meta = NULL;
++ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
++ int i;
++
++ mutex_lock(&msblk->meta_index_mutex);
++
++ TRACE("locate_meta_index: index %d, offset %d\n", index, offset);
++
++ if(msblk->meta_index == NULL)
++ goto not_allocated;
++
++ for (i = 0; i < SQUASHFS_META_NUMBER; i ++)
++ if (msblk->meta_index[i].inode_number == inode->i_ino &&
++ msblk->meta_index[i].offset >= offset &&
++ msblk->meta_index[i].offset <= index &&
++ msblk->meta_index[i].locked == 0) {
++ TRACE("locate_meta_index: entry %d, offset %d\n", i,
++ msblk->meta_index[i].offset);
++ meta = &msblk->meta_index[i];
++ offset = meta->offset;
++ }
++
++ if (meta)
++ meta->locked = 1;
++
++not_allocated:
++ mutex_unlock(&msblk->meta_index_mutex);
++
++ return meta;
++}
++
++
++struct meta_index *empty_meta_index(struct inode *inode, int offset, int skip)
++{
++ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
++ struct meta_index *meta = NULL;
++ int i;
++
++ mutex_lock(&msblk->meta_index_mutex);
++
++ TRACE("empty_meta_index: offset %d, skip %d\n", offset, skip);
++
++ if(msblk->meta_index == NULL) {
++ if (!(msblk->meta_index = kmalloc(sizeof(struct meta_index) *
++ SQUASHFS_META_NUMBER, GFP_KERNEL))) {
++ ERROR("Failed to allocate meta_index\n");
++ goto failed;
++ }
++ for(i = 0; i < SQUASHFS_META_NUMBER; i++) {
++ msblk->meta_index[i].inode_number = 0;
++ msblk->meta_index[i].locked = 0;
++ }
++ msblk->next_meta_index = 0;
++ }
++
++ for(i = SQUASHFS_META_NUMBER; i &&
++ msblk->meta_index[msblk->next_meta_index].locked; i --)
++ msblk->next_meta_index = (msblk->next_meta_index + 1) %
++ SQUASHFS_META_NUMBER;
++
++ if(i == 0) {
++ TRACE("empty_meta_index: failed!\n");
++ goto failed;
++ }
++
++ TRACE("empty_meta_index: returned meta entry %d, %p\n",
++ msblk->next_meta_index,
++ &msblk->meta_index[msblk->next_meta_index]);
++
++ meta = &msblk->meta_index[msblk->next_meta_index];
++ msblk->next_meta_index = (msblk->next_meta_index + 1) %
++ SQUASHFS_META_NUMBER;
++
++ meta->inode_number = inode->i_ino;
++ meta->offset = offset;
++ meta->skip = skip;
++ meta->entries = 0;
++ meta->locked = 1;
++
++failed:
++ mutex_unlock(&msblk->meta_index_mutex);
++ return meta;
++}
++
++
++void release_meta_index(struct inode *inode, struct meta_index *meta)
++{
++ meta->locked = 0;
++ smp_mb();
++}
++
++
++static int read_block_index(struct super_block *s, int blocks, char *block_list,
++ long long *start_block, int *offset)
++{
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ unsigned int *block_listp;
++ int block = 0;
++
++ if (msblk->swap) {
++ char sblock_list[blocks << 2];
++
++ if (!squashfs_get_cached_block(s, sblock_list, *start_block,
++ *offset, blocks << 2, start_block, offset)) {
++ ERROR("Unable to read block list [%llx:%x]\n",
++ *start_block, *offset);
++ goto failure;
++ }
++ SQUASHFS_SWAP_INTS(((unsigned int *)block_list),
++ ((unsigned int *)sblock_list), blocks);
++ } else
++ if (!squashfs_get_cached_block(s, block_list, *start_block,
++ *offset, blocks << 2, start_block, offset)) {
++ ERROR("Unable to read block list [%llx:%x]\n",
++ *start_block, *offset);
++ goto failure;
++ }
++
++ for (block_listp = (unsigned int *) block_list; blocks;
++ block_listp++, blocks --)
++ block += SQUASHFS_COMPRESSED_SIZE_BLOCK(*block_listp);
++
++ return block;
++
++failure:
++ return -1;
++}
++
++
++#define SIZE 256
++
++static inline int calculate_skip(int blocks) {
++ int skip = (blocks - 1) / ((SQUASHFS_SLOTS * SQUASHFS_META_ENTRIES + 1) * SQUASHFS_META_INDEXES);
++ return skip >= 7 ? 7 : skip + 1;
++}
++
++
++static int get_meta_index(struct inode *inode, int index,
++ long long *index_block, int *index_offset,
++ long long *data_block, char *block_list)
++{
++ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++ int skip = calculate_skip(i_size_read(inode) >> sblk->block_log);
++ int offset = 0;
++ struct meta_index *meta;
++ struct meta_entry *meta_entry;
++ long long cur_index_block = SQUASHFS_I(inode)->u.s1.block_list_start;
++ int cur_offset = SQUASHFS_I(inode)->offset;
++ long long cur_data_block = SQUASHFS_I(inode)->start_block;
++ int i;
++
++ index /= SQUASHFS_META_INDEXES * skip;
++
++ while ( offset < index ) {
++ meta = locate_meta_index(inode, index, offset + 1);
++
++ if (meta == NULL) {
++ if ((meta = empty_meta_index(inode, offset + 1,
++ skip)) == NULL)
++ goto all_done;
++ } else {
++ if(meta->entries == 0)
++ goto failed;
++ offset = index < meta->offset + meta->entries ? index :
++ meta->offset + meta->entries - 1;
++ meta_entry = &meta->meta_entry[offset - meta->offset];
++ cur_index_block = meta_entry->index_block + sblk->inode_table_start;
++ cur_offset = meta_entry->offset;
++ cur_data_block = meta_entry->data_block;
++ TRACE("get_meta_index: offset %d, meta->offset %d, "
++ "meta->entries %d\n", offset, meta->offset,
++ meta->entries);
++ TRACE("get_meta_index: index_block 0x%llx, offset 0x%x"
++ " data_block 0x%llx\n", cur_index_block,
++ cur_offset, cur_data_block);
++ }
++
++ for (i = meta->offset + meta->entries; i <= index &&
++ i < meta->offset + SQUASHFS_META_ENTRIES; i++) {
++ int blocks = skip * SQUASHFS_META_INDEXES;
++
++ while (blocks) {
++ int block = blocks > (SIZE >> 2) ? (SIZE >> 2) :
++ blocks;
++ int res = read_block_index(inode->i_sb, block,
++ block_list, &cur_index_block,
++ &cur_offset);
++
++ if (res == -1)
++ goto failed;
++
++ cur_data_block += res;
++ blocks -= block;
++ }
++
++ meta_entry = &meta->meta_entry[i - meta->offset];
++ meta_entry->index_block = cur_index_block - sblk->inode_table_start;
++ meta_entry->offset = cur_offset;
++ meta_entry->data_block = cur_data_block;
++ meta->entries ++;
++ offset ++;
++ }
++
++ TRACE("get_meta_index: meta->offset %d, meta->entries %d\n",
++ meta->offset, meta->entries);
++
++ release_meta_index(inode, meta);
++ }
++
++all_done:
++ *index_block = cur_index_block;
++ *index_offset = cur_offset;
++ *data_block = cur_data_block;
++
++ return offset * SQUASHFS_META_INDEXES * skip;
++
++failed:
++ release_meta_index(inode, meta);
++ return -1;
++}
++
++
++static long long read_blocklist(struct inode *inode, int index,
++ int readahead_blks, char *block_list,
++ unsigned short **block_p, unsigned int *bsize)
++{
++ long long block_ptr;
++ int offset;
++ long long block;
++ int res = get_meta_index(inode, index, &block_ptr, &offset, &block,
++ block_list);
++
++ TRACE("read_blocklist: res %d, index %d, block_ptr 0x%llx, offset"
++ " 0x%x, block 0x%llx\n", res, index, block_ptr, offset,
++ block);
++
++ if(res == -1)
++ goto failure;
++
++ index -= res;
++
++ while ( index ) {
++ int blocks = index > (SIZE >> 2) ? (SIZE >> 2) : index;
++ int res = read_block_index(inode->i_sb, blocks, block_list,
++ &block_ptr, &offset);
++ if (res == -1)
++ goto failure;
++ block += res;
++ index -= blocks;
++ }
++
++ if (read_block_index(inode->i_sb, 1, block_list,
++ &block_ptr, &offset) == -1)
++ goto failure;
++ *bsize = *((unsigned int *) block_list);
++
++ return block;
++
++failure:
++ return 0;
++}
++
++
++static int squashfs_readpage(struct file *file, struct page *page)
++{
++ struct inode *inode = page->mapping->host;
++ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++ unsigned char *block_list;
++ long long block;
++ unsigned int bsize, i = 0, bytes = 0, byte_offset = 0;
++ int index = page->index >> (sblk->block_log - PAGE_CACHE_SHIFT);
++ void *pageaddr;
++ struct squashfs_fragment_cache *fragment = NULL;
++ char *data_ptr = msblk->read_page;
++
++ int mask = (1 << (sblk->block_log - PAGE_CACHE_SHIFT)) - 1;
++ int start_index = page->index & ~mask;
++ int end_index = start_index | mask;
++
++ TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n",
++ page->index,
++ SQUASHFS_I(inode)->start_block);
++
++ if (!(block_list = kmalloc(SIZE, GFP_KERNEL))) {
++ ERROR("Failed to allocate block_list\n");
++ goto skip_read;
++ }
++
++ if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
++ PAGE_CACHE_SHIFT))
++ goto skip_read;
++
++ if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK
++ || index < (i_size_read(inode) >>
++ sblk->block_log)) {
++ if ((block = (msblk->read_blocklist)(inode, index, 1,
++ block_list, NULL, &bsize)) == 0)
++ goto skip_read;
++
++ mutex_lock(&msblk->read_page_mutex);
++
++ if (!(bytes = squashfs_read_data(inode->i_sb, msblk->read_page,
++ block, bsize, NULL, sblk->block_size))) {
++ ERROR("Unable to read page, block %llx, size %x\n", block,
++ bsize);
++ mutex_unlock(&msblk->read_page_mutex);
++ goto skip_read;
++ }
++ } else {
++ if ((fragment = get_cached_fragment(inode->i_sb,
++ SQUASHFS_I(inode)->
++ u.s1.fragment_start_block,
++ SQUASHFS_I(inode)->u.s1.fragment_size))
++ == NULL) {
++ ERROR("Unable to read page, block %llx, size %x\n",
++ SQUASHFS_I(inode)->
++ u.s1.fragment_start_block,
++ (int) SQUASHFS_I(inode)->
++ u.s1.fragment_size);
++ goto skip_read;
++ }
++ bytes = SQUASHFS_I(inode)->u.s1.fragment_offset +
++ (i_size_read(inode) & (sblk->block_size
++ - 1));
++ byte_offset = SQUASHFS_I(inode)->u.s1.fragment_offset;
++ data_ptr = fragment->data;
++ }
++
++ for (i = start_index; i <= end_index && byte_offset < bytes;
++ i++, byte_offset += PAGE_CACHE_SIZE) {
++ struct page *push_page;
++ int avail = (bytes - byte_offset) > PAGE_CACHE_SIZE ?
++ PAGE_CACHE_SIZE : bytes - byte_offset;
++
++ TRACE("bytes %d, i %d, byte_offset %d, available_bytes %d\n",
++ bytes, i, byte_offset, avail);
++
++ push_page = (i == page->index) ? page :
++ grab_cache_page_nowait(page->mapping, i);
++
++ if (!push_page)
++ continue;
++
++ if (PageUptodate(push_page))
++ goto skip_page;
++
++ pageaddr = kmap_atomic(push_page, KM_USER0);
++ memcpy(pageaddr, data_ptr + byte_offset, avail);
++ memset(pageaddr + avail, 0, PAGE_CACHE_SIZE - avail);
++ kunmap_atomic(pageaddr, KM_USER0);
++ flush_dcache_page(push_page);
++ SetPageUptodate(push_page);
++skip_page:
++ unlock_page(push_page);
++ if(i != page->index)
++ page_cache_release(push_page);
++ }
++
++ if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK
++ || index < (i_size_read(inode) >>
++ sblk->block_log))
++ mutex_unlock(&msblk->read_page_mutex);
++ else
++ release_cached_fragment(msblk, fragment);
++
++ kfree(block_list);
++ return 0;
++
++skip_read:
++ pageaddr = kmap_atomic(page, KM_USER0);
++ memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
++ kunmap_atomic(pageaddr, KM_USER0);
++ flush_dcache_page(page);
++ SetPageUptodate(page);
++ unlock_page(page);
++
++ kfree(block_list);
++ return 0;
++}
++
++
++static int squashfs_readpage4K(struct file *file, struct page *page)
++{
++ struct inode *inode = page->mapping->host;
++ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++ unsigned char *block_list;
++ long long block;
++ unsigned int bsize, bytes = 0;
++ void *pageaddr;
++
++ TRACE("Entered squashfs_readpage4K, page index %lx, start block %llx\n",
++ page->index,
++ SQUASHFS_I(inode)->start_block);
++
++ if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
++ PAGE_CACHE_SHIFT)) {
++ block_list = NULL;
++ goto skip_read;
++ }
++
++ if (!(block_list = kmalloc(SIZE, GFP_KERNEL))) {
++ ERROR("Failed to allocate block_list\n");
++ goto skip_read;
++ }
++
++ if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK
++ || page->index < (i_size_read(inode) >>
++ sblk->block_log)) {
++ block = (msblk->read_blocklist)(inode, page->index, 1,
++ block_list, NULL, &bsize);
++ if(block == 0)
++ goto skip_read;
++
++ mutex_lock(&msblk->read_page_mutex);
++ bytes = squashfs_read_data(inode->i_sb, msblk->read_page, block,
++ bsize, NULL, sblk->block_size);
++ if (bytes) {
++ pageaddr = kmap_atomic(page, KM_USER0);
++ memcpy(pageaddr, msblk->read_page, bytes);
++ kunmap_atomic(pageaddr, KM_USER0);
++ } else
++ ERROR("Unable to read page, block %llx, size %x\n",
++ block, bsize);
++ mutex_unlock(&msblk->read_page_mutex);
++ } else {
++ struct squashfs_fragment_cache *fragment =
++ get_cached_fragment(inode->i_sb,
++ SQUASHFS_I(inode)->
++ u.s1.fragment_start_block,
++ SQUASHFS_I(inode)-> u.s1.fragment_size);
++ if (fragment) {
++ bytes = i_size_read(inode) & (sblk->block_size - 1);
++ pageaddr = kmap_atomic(page, KM_USER0);
++ memcpy(pageaddr, fragment->data + SQUASHFS_I(inode)->
++ u.s1.fragment_offset, bytes);
++ kunmap_atomic(pageaddr, KM_USER0);
++ release_cached_fragment(msblk, fragment);
++ } else
++ ERROR("Unable to read page, block %llx, size %x\n",
++ SQUASHFS_I(inode)->
++ u.s1.fragment_start_block, (int)
++ SQUASHFS_I(inode)-> u.s1.fragment_size);
++ }
++
++skip_read:
++ pageaddr = kmap_atomic(page, KM_USER0);
++ memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
++ kunmap_atomic(pageaddr, KM_USER0);
++ flush_dcache_page(page);
++ SetPageUptodate(page);
++ unlock_page(page);
++
++ kfree(block_list);
++ return 0;
++}
++
++
++static int get_dir_index_using_offset(struct super_block *s, long long
++ *next_block, unsigned int *next_offset,
++ long long index_start,
++ unsigned int index_offset, int i_count,
++ long long f_pos)
++{
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++ int i, length = 0;
++ struct squashfs_dir_index index;
++
++ TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n",
++ i_count, (unsigned int) f_pos);
++
++ f_pos =- 3;
++ if (f_pos == 0)
++ goto finish;
++
++ for (i = 0; i < i_count; i++) {
++ if (msblk->swap) {
++ struct squashfs_dir_index sindex;
++ squashfs_get_cached_block(s, (char *) &sindex,
++ index_start, index_offset,
++ sizeof(sindex), &index_start,
++ &index_offset);
++ SQUASHFS_SWAP_DIR_INDEX(&index, &sindex);
++ } else
++ squashfs_get_cached_block(s, (char *) &index,
++ index_start, index_offset,
++ sizeof(index), &index_start,
++ &index_offset);
++
++ if (index.index > f_pos)
++ break;
++
++ squashfs_get_cached_block(s, NULL, index_start, index_offset,
++ index.size + 1, &index_start,
++ &index_offset);
++
++ length = index.index;
++ *next_block = index.start_block + sblk->directory_table_start;
++ }
++
++ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
++
++finish:
++ return length + 3;
++}
++
++
++static int get_dir_index_using_name(struct super_block *s, long long
++ *next_block, unsigned int *next_offset,
++ long long index_start,
++ unsigned int index_offset, int i_count,
++ const char *name, int size)
++{
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++ int i, length = 0;
++ struct squashfs_dir_index *index;
++ char *str;
++
++ TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count);
++
++ if (!(str = kmalloc(sizeof(struct squashfs_dir_index) +
++ (SQUASHFS_NAME_LEN + 1) * 2, GFP_KERNEL))) {
++ ERROR("Failed to allocate squashfs_dir_index\n");
++ goto failure;
++ }
++
++ index = (struct squashfs_dir_index *) (str + SQUASHFS_NAME_LEN + 1);
++ strncpy(str, name, size);
++ str[size] = '\0';
++
++ for (i = 0; i < i_count; i++) {
++ if (msblk->swap) {
++ struct squashfs_dir_index sindex;
++ squashfs_get_cached_block(s, (char *) &sindex,
++ index_start, index_offset,
++ sizeof(sindex), &index_start,
++ &index_offset);
++ SQUASHFS_SWAP_DIR_INDEX(index, &sindex);
++ } else
++ squashfs_get_cached_block(s, (char *) index,
++ index_start, index_offset,
++ sizeof(struct squashfs_dir_index),
++ &index_start, &index_offset);
++
++ squashfs_get_cached_block(s, index->name, index_start,
++ index_offset, index->size + 1,
++ &index_start, &index_offset);
++
++ index->name[index->size + 1] = '\0';
++
++ if (strcmp(index->name, str) > 0)
++ break;
++
++ length = index->index;
++ *next_block = index->start_block + sblk->directory_table_start;
++ }
++
++ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
++ kfree(str);
++failure:
++ return length + 3;
++}
++
++
++static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir)
++{
++ struct inode *i = file->f_dentry->d_inode;
++ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++ long long next_block = SQUASHFS_I(i)->start_block +
++ sblk->directory_table_start;
++ int next_offset = SQUASHFS_I(i)->offset, length = 0,
++ dir_count;
++ struct squashfs_dir_header dirh;
++ struct squashfs_dir_entry *dire;
++
++ TRACE("Entered squashfs_readdir [%llx:%x]\n", next_block, next_offset);
++
++ if (!(dire = kmalloc(sizeof(struct squashfs_dir_entry) +
++ SQUASHFS_NAME_LEN + 1, GFP_KERNEL))) {
++ ERROR("Failed to allocate squashfs_dir_entry\n");
++ goto finish;
++ }
++
++ while(file->f_pos < 3) {
++ char *name;
++ int size, i_ino;
++
++ if(file->f_pos == 0) {
++ name = ".";
++ size = 1;
++ i_ino = i->i_ino;
++ } else {
++ name = "..";
++ size = 2;
++ i_ino = SQUASHFS_I(i)->u.s2.parent_inode;
++ }
++ TRACE("Calling filldir(%x, %s, %d, %d, %d, %d)\n",
++ (unsigned int) dirent, name, size, (int)
++ file->f_pos, i_ino,
++ squashfs_filetype_table[1]);
++
++ if (filldir(dirent, name, size,
++ file->f_pos, i_ino,
++ squashfs_filetype_table[1]) < 0) {
++ TRACE("Filldir returned less than 0\n");
++ goto finish;
++ }
++ file->f_pos += size;
++ }
++
++ length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset,
++ SQUASHFS_I(i)->u.s2.directory_index_start,
++ SQUASHFS_I(i)->u.s2.directory_index_offset,
++ SQUASHFS_I(i)->u.s2.directory_index_count,
++ file->f_pos);
++
++ while (length < i_size_read(i)) {
++ /* read directory header */
++ if (msblk->swap) {
++ struct squashfs_dir_header sdirh;
++
++ if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh,
++ next_block, next_offset, sizeof(sdirh),
++ &next_block, &next_offset))
++ goto failed_read;
++
++ length += sizeof(sdirh);
++ SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh);
++ } else {
++ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
++ next_block, next_offset, sizeof(dirh),
++ &next_block, &next_offset))
++ goto failed_read;
++
++ length += sizeof(dirh);
++ }
++
++ dir_count = dirh.count + 1;
++ while (dir_count--) {
++ if (msblk->swap) {
++ struct squashfs_dir_entry sdire;
++ if (!squashfs_get_cached_block(i->i_sb, (char *)
++ &sdire, next_block, next_offset,
++ sizeof(sdire), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ length += sizeof(sdire);
++ SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire);
++ } else {
++ if (!squashfs_get_cached_block(i->i_sb, (char *)
++ dire, next_block, next_offset,
++ sizeof(*dire), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ length += sizeof(*dire);
++ }
++
++ if (!squashfs_get_cached_block(i->i_sb, dire->name,
++ next_block, next_offset,
++ dire->size + 1, &next_block,
++ &next_offset))
++ goto failed_read;
++
++ length += dire->size + 1;
++
++ if (file->f_pos >= length)
++ continue;
++
++ dire->name[dire->size + 1] = '\0';
++
++ TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d, %d)\n",
++ (unsigned int) dirent, dire->name,
++ dire->size + 1, (int) file->f_pos,
++ dirh.start_block, dire->offset,
++ dirh.inode_number + dire->inode_number,
++ squashfs_filetype_table[dire->type]);
++
++ if (filldir(dirent, dire->name, dire->size + 1,
++ file->f_pos,
++ dirh.inode_number + dire->inode_number,
++ squashfs_filetype_table[dire->type])
++ < 0) {
++ TRACE("Filldir returned less than 0\n");
++ goto finish;
++ }
++ file->f_pos = length;
++ }
++ }
++
++finish:
++ kfree(dire);
++ return 0;
++
++failed_read:
++ ERROR("Unable to read directory block [%llx:%x]\n", next_block,
++ next_offset);
++ kfree(dire);
++ return 0;
++}
++
++
++static struct dentry *squashfs_lookup(struct inode *i, struct dentry *dentry,
++ struct nameidata *nd)
++{
++ const unsigned char *name = dentry->d_name.name;
++ int len = dentry->d_name.len;
++ struct inode *inode = NULL;
++ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++ long long next_block = SQUASHFS_I(i)->start_block +
++ sblk->directory_table_start;
++ int next_offset = SQUASHFS_I(i)->offset, length = 0,
++ dir_count;
++ struct squashfs_dir_header dirh;
++ struct squashfs_dir_entry *dire;
++
++ TRACE("Entered squashfs_lookup [%llx:%x]\n", next_block, next_offset);
++
++ if (!(dire = kmalloc(sizeof(struct squashfs_dir_entry) +
++ SQUASHFS_NAME_LEN + 1, GFP_KERNEL))) {
++ ERROR("Failed to allocate squashfs_dir_entry\n");
++ goto exit_lookup;
++ }
++
++ if (len > SQUASHFS_NAME_LEN)
++ goto exit_lookup;
++
++ length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset,
++ SQUASHFS_I(i)->u.s2.directory_index_start,
++ SQUASHFS_I(i)->u.s2.directory_index_offset,
++ SQUASHFS_I(i)->u.s2.directory_index_count, name,
++ len);
++
++ while (length < i_size_read(i)) {
++ /* read directory header */
++ if (msblk->swap) {
++ struct squashfs_dir_header sdirh;
++ if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh,
++ next_block, next_offset, sizeof(sdirh),
++ &next_block, &next_offset))
++ goto failed_read;
++
++ length += sizeof(sdirh);
++ SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh);
++ } else {
++ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
++ next_block, next_offset, sizeof(dirh),
++ &next_block, &next_offset))
++ goto failed_read;
++
++ length += sizeof(dirh);
++ }
++
++ dir_count = dirh.count + 1;
++ while (dir_count--) {
++ if (msblk->swap) {
++ struct squashfs_dir_entry sdire;
++ if (!squashfs_get_cached_block(i->i_sb, (char *)
++ &sdire, next_block,next_offset,
++ sizeof(sdire), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ length += sizeof(sdire);
++ SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire);
++ } else {
++ if (!squashfs_get_cached_block(i->i_sb, (char *)
++ dire, next_block,next_offset,
++ sizeof(*dire), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ length += sizeof(*dire);
++ }
++
++ if (!squashfs_get_cached_block(i->i_sb, dire->name,
++ next_block, next_offset, dire->size + 1,
++ &next_block, &next_offset))
++ goto failed_read;
++
++ length += dire->size + 1;
++
++ if (name[0] < dire->name[0])
++ goto exit_lookup;
++
++ if ((len == dire->size + 1) && !strncmp(name, dire->name, len)) {
++ squashfs_inode_t ino = SQUASHFS_MKINODE(dirh.start_block,
++ dire->offset);
++
++ TRACE("calling squashfs_iget for directory "
++ "entry %s, inode %x:%x, %d\n", name,
++ dirh.start_block, dire->offset,
++ dirh.inode_number + dire->inode_number);
++
++ inode = squashfs_iget(i->i_sb, ino, dirh.inode_number + dire->inode_number);
++
++ goto exit_lookup;
++ }
++ }
++ }
++
++exit_lookup:
++ kfree(dire);
++ if (inode)
++ return d_splice_alias(inode, dentry);
++ d_add(dentry, inode);
++ return ERR_PTR(0);
++
++failed_read:
++ ERROR("Unable to read directory block [%llx:%x]\n", next_block,
++ next_offset);
++ goto exit_lookup;
++}
++
++
++static int squashfs_remount(struct super_block *s, int *flags, char *data)
++{
++ *flags |= MS_RDONLY;
++ return 0;
++}
++
++
++static void squashfs_put_super(struct super_block *s)
++{
++ int i;
++
++ if (s->s_fs_info) {
++ struct squashfs_sb_info *sbi = s->s_fs_info;
++ if (sbi->block_cache)
++ for (i = 0; i < SQUASHFS_CACHED_BLKS; i++)
++ if (sbi->block_cache[i].block !=
++ SQUASHFS_INVALID_BLK)
++ kfree(sbi->block_cache[i].data);
++ if (sbi->fragment)
++ for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++)
++ SQUASHFS_FREE(sbi->fragment[i].data);
++ kfree(sbi->fragment);
++ kfree(sbi->block_cache);
++ kfree(sbi->read_page);
++ kfree(sbi->uid);
++ kfree(sbi->fragment_index);
++ kfree(sbi->fragment_index_2);
++ kfree(sbi->meta_index);
++ vfree(sbi->stream.workspace);
++ kfree(s->s_fs_info);
++ s->s_fs_info = NULL;
++ }
++}
++
++
++static int squashfs_get_sb(struct file_system_type *fs_type, int flags,
++ const char *dev_name, void *data,
++ struct vfsmount *mnt)
++{
++ return get_sb_bdev(fs_type, flags, dev_name, data, squashfs_fill_super,
++ mnt);
++}
++
++
++static int __init init_squashfs_fs(void)
++{
++ int err = init_inodecache();
++ if (err)
++ goto out;
++
++ printk(KERN_INFO "squashfs: version 3.2-r2 (2007/01/15) "
++ "Phillip Lougher\n");
++
++ if ((err = register_filesystem(&squashfs_fs_type)))
++ destroy_inodecache();
++
++out:
++ return err;
++}
++
++
++static void __exit exit_squashfs_fs(void)
++{
++ unregister_filesystem(&squashfs_fs_type);
++ destroy_inodecache();
++}
++
++
++static struct kmem_cache * squashfs_inode_cachep;
++
++
++static struct inode *squashfs_alloc_inode(struct super_block *sb)
++{
++ struct squashfs_inode_info *ei;
++ ei = kmem_cache_alloc(squashfs_inode_cachep, GFP_KERNEL);
++ if (!ei)
++ return NULL;
++ return &ei->vfs_inode;
++}
++
++
++static void squashfs_destroy_inode(struct inode *inode)
++{
++ kmem_cache_free(squashfs_inode_cachep, SQUASHFS_I(inode));
++}
++
++
++static void init_once(void * foo, struct kmem_cache * cachep, unsigned long flags)
++{
++ struct squashfs_inode_info *ei = foo;
++
++ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
++ SLAB_CTOR_CONSTRUCTOR)
++ inode_init_once(&ei->vfs_inode);
++}
++
++
++static int __init init_inodecache(void)
++{
++ squashfs_inode_cachep = kmem_cache_create("squashfs_inode_cache",
++ sizeof(struct squashfs_inode_info),
++ 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT,
++ init_once, NULL);
++ if (squashfs_inode_cachep == NULL)
++ return -ENOMEM;
++ return 0;
++}
++
++
++static void destroy_inodecache(void)
++{
++ kmem_cache_destroy(squashfs_inode_cachep);
++}
++
++
++module_init(init_squashfs_fs);
++module_exit(exit_squashfs_fs);
++MODULE_DESCRIPTION("squashfs 3.2-r2, a compressed read-only filesystem");
++MODULE_AUTHOR("Phillip Lougher <phillip@lougher.org.uk>");
++MODULE_LICENSE("GPL");
+diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h
+new file mode 100644
+index 0000000..6f863f0
+--- /dev/null
++++ b/fs/squashfs/squashfs.h
+@@ -0,0 +1,87 @@
++/*
++ * Squashfs - a compressed read only filesystem for Linux
++ *
++ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
++ * Phillip Lougher <phillip@lougher.org.uk>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2,
++ * 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ * squashfs.h
++ */
++
++#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY
++#undef CONFIG_SQUASHFS_1_0_COMPATIBILITY
++#endif
++
++#ifdef SQUASHFS_TRACE
++#define TRACE(s, args...) printk(KERN_NOTICE "SQUASHFS: "s, ## args)
++#else
++#define TRACE(s, args...) {}
++#endif
++
++#define ERROR(s, args...) printk(KERN_ERR "SQUASHFS error: "s, ## args)
++
++#define SERROR(s, args...) do { \
++ if (!silent) \
++ printk(KERN_ERR "SQUASHFS error: "s, ## args);\
++ } while(0)
++
++#define WARNING(s, args...) printk(KERN_WARNING "SQUASHFS: "s, ## args)
++
++static inline struct squashfs_inode_info *SQUASHFS_I(struct inode *inode)
++{
++ return list_entry(inode, struct squashfs_inode_info, vfs_inode);
++}
++
++#if defined(CONFIG_SQUASHFS_1_0_COMPATIBILITY ) || defined(CONFIG_SQUASHFS_2_0_COMPATIBILITY)
++#define SQSH_EXTERN
++extern unsigned int squashfs_read_data(struct super_block *s, char *buffer,
++ long long index, unsigned int length,
++ long long *next_index, int srclength);
++extern int squashfs_get_cached_block(struct super_block *s, char *buffer,
++ long long block, unsigned int offset,
++ int length, long long *next_block,
++ unsigned int *next_offset);
++extern void release_cached_fragment(struct squashfs_sb_info *msblk, struct
++ squashfs_fragment_cache *fragment);
++extern struct squashfs_fragment_cache *get_cached_fragment(struct super_block
++ *s, long long start_block,
++ int length);
++extern struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode, unsigned int inode_number);
++extern const struct address_space_operations squashfs_symlink_aops;
++extern const struct address_space_operations squashfs_aops;
++extern const struct address_space_operations squashfs_aops_4K;
++extern struct inode_operations squashfs_dir_inode_ops;
++#else
++#define SQSH_EXTERN static
++#endif
++
++#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY
++extern int squashfs_1_0_supported(struct squashfs_sb_info *msblk);
++#else
++static inline int squashfs_1_0_supported(struct squashfs_sb_info *msblk)
++{
++ return 0;
++}
++#endif
++
++#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY
++extern int squashfs_2_0_supported(struct squashfs_sb_info *msblk);
++#else
++static inline int squashfs_2_0_supported(struct squashfs_sb_info *msblk)
++{
++ return 0;
++}
++#endif
+diff --git a/fs/squashfs/squashfs2_0.c b/fs/squashfs/squashfs2_0.c
+new file mode 100644
+index 0000000..d8d9d55
+--- /dev/null
++++ b/fs/squashfs/squashfs2_0.c
+@@ -0,0 +1,742 @@
++/*
++ * Squashfs - a compressed read only filesystem for Linux
++ *
++ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
++ * Phillip Lougher <phillip@lougher.org.uk>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2,
++ * 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ * squashfs2_0.c
++ */
++
++#include <linux/squashfs_fs.h>
++#include <linux/module.h>
++#include <linux/zlib.h>
++#include <linux/fs.h>
++#include <linux/squashfs_fs_sb.h>
++#include <linux/squashfs_fs_i.h>
++
++#include "squashfs.h"
++static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir);
++static struct dentry *squashfs_lookup_2(struct inode *, struct dentry *,
++ struct nameidata *);
++
++static struct file_operations squashfs_dir_ops_2 = {
++ .read = generic_read_dir,
++ .readdir = squashfs_readdir_2
++};
++
++static struct inode_operations squashfs_dir_inode_ops_2 = {
++ .lookup = squashfs_lookup_2
++};
++
++static unsigned char squashfs_filetype_table[] = {
++ DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK
++};
++
++static int read_fragment_index_table_2(struct super_block *s)
++{
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++
++ if (!(msblk->fragment_index_2 = kmalloc(SQUASHFS_FRAGMENT_INDEX_BYTES_2
++ (sblk->fragments), GFP_KERNEL))) {
++ ERROR("Failed to allocate uid/gid table\n");
++ return 0;
++ }
++
++ if (SQUASHFS_FRAGMENT_INDEX_BYTES_2(sblk->fragments) &&
++ !squashfs_read_data(s, (char *)
++ msblk->fragment_index_2,
++ sblk->fragment_table_start,
++ SQUASHFS_FRAGMENT_INDEX_BYTES_2
++ (sblk->fragments) |
++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, SQUASHFS_FRAGMENT_INDEX_BYTES_2(sblk->fragments))) {
++ ERROR("unable to read fragment index table\n");
++ return 0;
++ }
++
++ if (msblk->swap) {
++ int i;
++ unsigned int fragment;
++
++ for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES_2(sblk->fragments);
++ i++) {
++ SQUASHFS_SWAP_FRAGMENT_INDEXES_2((&fragment),
++ &msblk->fragment_index_2[i], 1);
++ msblk->fragment_index_2[i] = fragment;
++ }
++ }
++
++ return 1;
++}
++
++
++static int get_fragment_location_2(struct super_block *s, unsigned int fragment,
++ long long *fragment_start_block,
++ unsigned int *fragment_size)
++{
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ long long start_block =
++ msblk->fragment_index_2[SQUASHFS_FRAGMENT_INDEX_2(fragment)];
++ int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET_2(fragment);
++ struct squashfs_fragment_entry_2 fragment_entry;
++
++ if (msblk->swap) {
++ struct squashfs_fragment_entry_2 sfragment_entry;
++
++ if (!squashfs_get_cached_block(s, (char *) &sfragment_entry,
++ start_block, offset,
++ sizeof(sfragment_entry), &start_block,
++ &offset))
++ goto out;
++ SQUASHFS_SWAP_FRAGMENT_ENTRY_2(&fragment_entry, &sfragment_entry);
++ } else
++ if (!squashfs_get_cached_block(s, (char *) &fragment_entry,
++ start_block, offset,
++ sizeof(fragment_entry), &start_block,
++ &offset))
++ goto out;
++
++ *fragment_start_block = fragment_entry.start_block;
++ *fragment_size = fragment_entry.size;
++
++ return 1;
++
++out:
++ return 0;
++}
++
++
++static void squashfs_new_inode(struct squashfs_sb_info *msblk, struct inode *i,
++ struct squashfs_base_inode_header_2 *inodeb, unsigned int ino)
++{
++ struct squashfs_super_block *sblk = &msblk->sblk;
++
++ i->i_ino = ino;
++ i->i_mtime.tv_sec = sblk->mkfs_time;
++ i->i_atime.tv_sec = sblk->mkfs_time;
++ i->i_ctime.tv_sec = sblk->mkfs_time;
++ i->i_uid = msblk->uid[inodeb->uid];
++ i->i_mode = inodeb->mode;
++ i->i_nlink = 1;
++ i->i_size = 0;
++ if (inodeb->guid == SQUASHFS_GUIDS)
++ i->i_gid = i->i_uid;
++ else
++ i->i_gid = msblk->guid[inodeb->guid];
++}
++
++
++static int squashfs_read_inode_2(struct inode *i, squashfs_inode_t inode)
++{
++ struct super_block *s = i->i_sb;
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++ unsigned int block = SQUASHFS_INODE_BLK(inode) +
++ sblk->inode_table_start;
++ unsigned int offset = SQUASHFS_INODE_OFFSET(inode);
++ unsigned int ino = i->i_ino;
++ long long next_block;
++ unsigned int next_offset;
++ union squashfs_inode_header_2 id, sid;
++ struct squashfs_base_inode_header_2 *inodeb = &id.base,
++ *sinodeb = &sid.base;
++
++ TRACE("Entered squashfs_iget\n");
++
++ if (msblk->swap) {
++ if (!squashfs_get_cached_block(s, (char *) sinodeb, block,
++ offset, sizeof(*sinodeb), &next_block,
++ &next_offset))
++ goto failed_read;
++ SQUASHFS_SWAP_BASE_INODE_HEADER_2(inodeb, sinodeb,
++ sizeof(*sinodeb));
++ } else
++ if (!squashfs_get_cached_block(s, (char *) inodeb, block,
++ offset, sizeof(*inodeb), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ squashfs_new_inode(msblk, i, inodeb, ino);
++
++ switch(inodeb->inode_type) {
++ case SQUASHFS_FILE_TYPE: {
++ struct squashfs_reg_inode_header_2 *inodep = &id.reg;
++ struct squashfs_reg_inode_header_2 *sinodep = &sid.reg;
++ long long frag_blk;
++ unsigned int frag_size = 0;
++
++ if (msblk->swap) {
++ if (!squashfs_get_cached_block(s, (char *)
++ sinodep, block, offset,
++ sizeof(*sinodep), &next_block,
++ &next_offset))
++ goto failed_read;
++ SQUASHFS_SWAP_REG_INODE_HEADER_2(inodep, sinodep);
++ } else
++ if (!squashfs_get_cached_block(s, (char *)
++ inodep, block, offset,
++ sizeof(*inodep), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ frag_blk = SQUASHFS_INVALID_BLK;
++ if (inodep->fragment != SQUASHFS_INVALID_FRAG &&
++ !get_fragment_location_2(s,
++ inodep->fragment, &frag_blk, &frag_size))
++ goto failed_read;
++
++ i->i_size = inodep->file_size;
++ i->i_fop = &generic_ro_fops;
++ i->i_mode |= S_IFREG;
++ i->i_mtime.tv_sec = inodep->mtime;
++ i->i_atime.tv_sec = inodep->mtime;
++ i->i_ctime.tv_sec = inodep->mtime;
++ i->i_blocks = ((i->i_size - 1) >> 9) + 1;
++ SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk;
++ SQUASHFS_I(i)->u.s1.fragment_size = frag_size;
++ SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset;
++ SQUASHFS_I(i)->start_block = inodep->start_block;
++ SQUASHFS_I(i)->u.s1.block_list_start = next_block;
++ SQUASHFS_I(i)->offset = next_offset;
++ if (sblk->block_size > 4096)
++ i->i_data.a_ops = &squashfs_aops;
++ else
++ i->i_data.a_ops = &squashfs_aops_4K;
++
++ TRACE("File inode %x:%x, start_block %x, "
++ "block_list_start %llx, offset %x\n",
++ SQUASHFS_INODE_BLK(inode), offset,
++ inodep->start_block, next_block,
++ next_offset);
++ break;
++ }
++ case SQUASHFS_DIR_TYPE: {
++ struct squashfs_dir_inode_header_2 *inodep = &id.dir;
++ struct squashfs_dir_inode_header_2 *sinodep = &sid.dir;
++
++ if (msblk->swap) {
++ if (!squashfs_get_cached_block(s, (char *)
++ sinodep, block, offset,
++ sizeof(*sinodep), &next_block,
++ &next_offset))
++ goto failed_read;
++ SQUASHFS_SWAP_DIR_INODE_HEADER_2(inodep, sinodep);
++ } else
++ if (!squashfs_get_cached_block(s, (char *)
++ inodep, block, offset,
++ sizeof(*inodep), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ i->i_size = inodep->file_size;
++ i->i_op = &squashfs_dir_inode_ops_2;
++ i->i_fop = &squashfs_dir_ops_2;
++ i->i_mode |= S_IFDIR;
++ i->i_mtime.tv_sec = inodep->mtime;
++ i->i_atime.tv_sec = inodep->mtime;
++ i->i_ctime.tv_sec = inodep->mtime;
++ SQUASHFS_I(i)->start_block = inodep->start_block;
++ SQUASHFS_I(i)->offset = inodep->offset;
++ SQUASHFS_I(i)->u.s2.directory_index_count = 0;
++ SQUASHFS_I(i)->u.s2.parent_inode = 0;
++
++ TRACE("Directory inode %x:%x, start_block %x, offset "
++ "%x\n", SQUASHFS_INODE_BLK(inode),
++ offset, inodep->start_block,
++ inodep->offset);
++ break;
++ }
++ case SQUASHFS_LDIR_TYPE: {
++ struct squashfs_ldir_inode_header_2 *inodep = &id.ldir;
++ struct squashfs_ldir_inode_header_2 *sinodep = &sid.ldir;
++
++ if (msblk->swap) {
++ if (!squashfs_get_cached_block(s, (char *)
++ sinodep, block, offset,
++ sizeof(*sinodep), &next_block,
++ &next_offset))
++ goto failed_read;
++ SQUASHFS_SWAP_LDIR_INODE_HEADER_2(inodep,
++ sinodep);
++ } else
++ if (!squashfs_get_cached_block(s, (char *)
++ inodep, block, offset,
++ sizeof(*inodep), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ i->i_size = inodep->file_size;
++ i->i_op = &squashfs_dir_inode_ops_2;
++ i->i_fop = &squashfs_dir_ops_2;
++ i->i_mode |= S_IFDIR;
++ i->i_mtime.tv_sec = inodep->mtime;
++ i->i_atime.tv_sec = inodep->mtime;
++ i->i_ctime.tv_sec = inodep->mtime;
++ SQUASHFS_I(i)->start_block = inodep->start_block;
++ SQUASHFS_I(i)->offset = inodep->offset;
++ SQUASHFS_I(i)->u.s2.directory_index_start = next_block;
++ SQUASHFS_I(i)->u.s2.directory_index_offset =
++ next_offset;
++ SQUASHFS_I(i)->u.s2.directory_index_count =
++ inodep->i_count;
++ SQUASHFS_I(i)->u.s2.parent_inode = 0;
++
++ TRACE("Long directory inode %x:%x, start_block %x, "
++ "offset %x\n",
++ SQUASHFS_INODE_BLK(inode), offset,
++ inodep->start_block, inodep->offset);
++ break;
++ }
++ case SQUASHFS_SYMLINK_TYPE: {
++ struct squashfs_symlink_inode_header_2 *inodep =
++ &id.symlink;
++ struct squashfs_symlink_inode_header_2 *sinodep =
++ &sid.symlink;
++
++ if (msblk->swap) {
++ if (!squashfs_get_cached_block(s, (char *)
++ sinodep, block, offset,
++ sizeof(*sinodep), &next_block,
++ &next_offset))
++ goto failed_read;
++ SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(inodep,
++ sinodep);
++ } else
++ if (!squashfs_get_cached_block(s, (char *)
++ inodep, block, offset,
++ sizeof(*inodep), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ i->i_size = inodep->symlink_size;
++ i->i_op = &page_symlink_inode_operations;
++ i->i_data.a_ops = &squashfs_symlink_aops;
++ i->i_mode |= S_IFLNK;
++ SQUASHFS_I(i)->start_block = next_block;
++ SQUASHFS_I(i)->offset = next_offset;
++
++ TRACE("Symbolic link inode %x:%x, start_block %llx, "
++ "offset %x\n",
++ SQUASHFS_INODE_BLK(inode), offset,
++ next_block, next_offset);
++ break;
++ }
++ case SQUASHFS_BLKDEV_TYPE:
++ case SQUASHFS_CHRDEV_TYPE: {
++ struct squashfs_dev_inode_header_2 *inodep = &id.dev;
++ struct squashfs_dev_inode_header_2 *sinodep = &sid.dev;
++
++ if (msblk->swap) {
++ if (!squashfs_get_cached_block(s, (char *)
++ sinodep, block, offset,
++ sizeof(*sinodep), &next_block,
++ &next_offset))
++ goto failed_read;
++ SQUASHFS_SWAP_DEV_INODE_HEADER_2(inodep, sinodep);
++ } else
++ if (!squashfs_get_cached_block(s, (char *)
++ inodep, block, offset,
++ sizeof(*inodep), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ i->i_mode |= (inodeb->inode_type ==
++ SQUASHFS_CHRDEV_TYPE) ? S_IFCHR :
++ S_IFBLK;
++ init_special_inode(i, i->i_mode,
++ old_decode_dev(inodep->rdev));
++
++ TRACE("Device inode %x:%x, rdev %x\n",
++ SQUASHFS_INODE_BLK(inode), offset,
++ inodep->rdev);
++ break;
++ }
++ case SQUASHFS_FIFO_TYPE:
++ case SQUASHFS_SOCKET_TYPE: {
++
++ i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE)
++ ? S_IFIFO : S_IFSOCK;
++ init_special_inode(i, i->i_mode, 0);
++ break;
++ }
++ default:
++ ERROR("Unknown inode type %d in squashfs_iget!\n",
++ inodeb->inode_type);
++ goto failed_read1;
++ }
++
++ return 1;
++
++failed_read:
++ ERROR("Unable to read inode [%x:%x]\n", block, offset);
++
++failed_read1:
++ return 0;
++}
++
++
++static int get_dir_index_using_offset(struct super_block *s, long long
++ *next_block, unsigned int *next_offset,
++ long long index_start,
++ unsigned int index_offset, int i_count,
++ long long f_pos)
++{
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++ int i, length = 0;
++ struct squashfs_dir_index_2 index;
++
++ TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n",
++ i_count, (unsigned int) f_pos);
++
++ if (f_pos == 0)
++ goto finish;
++
++ for (i = 0; i < i_count; i++) {
++ if (msblk->swap) {
++ struct squashfs_dir_index_2 sindex;
++ squashfs_get_cached_block(s, (char *) &sindex,
++ index_start, index_offset,
++ sizeof(sindex), &index_start,
++ &index_offset);
++ SQUASHFS_SWAP_DIR_INDEX_2(&index, &sindex);
++ } else
++ squashfs_get_cached_block(s, (char *) &index,
++ index_start, index_offset,
++ sizeof(index), &index_start,
++ &index_offset);
++
++ if (index.index > f_pos)
++ break;
++
++ squashfs_get_cached_block(s, NULL, index_start, index_offset,
++ index.size + 1, &index_start,
++ &index_offset);
++
++ length = index.index;
++ *next_block = index.start_block + sblk->directory_table_start;
++ }
++
++ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
++
++finish:
++ return length;
++}
++
++
++static int get_dir_index_using_name(struct super_block *s, long long
++ *next_block, unsigned int *next_offset,
++ long long index_start,
++ unsigned int index_offset, int i_count,
++ const char *name, int size)
++{
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++ int i, length = 0;
++ struct squashfs_dir_index_2 *index;
++ char *str;
++
++ TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count);
++
++ if (!(str = kmalloc(sizeof(struct squashfs_dir_index) +
++ (SQUASHFS_NAME_LEN + 1) * 2, GFP_KERNEL))) {
++ ERROR("Failed to allocate squashfs_dir_index\n");
++ goto failure;
++ }
++
++ index = (struct squashfs_dir_index_2 *) (str + SQUASHFS_NAME_LEN + 1);
++ strncpy(str, name, size);
++ str[size] = '\0';
++
++ for (i = 0; i < i_count; i++) {
++ if (msblk->swap) {
++ struct squashfs_dir_index_2 sindex;
++ squashfs_get_cached_block(s, (char *) &sindex,
++ index_start, index_offset,
++ sizeof(sindex), &index_start,
++ &index_offset);
++ SQUASHFS_SWAP_DIR_INDEX_2(index, &sindex);
++ } else
++ squashfs_get_cached_block(s, (char *) index,
++ index_start, index_offset,
++ sizeof(struct squashfs_dir_index_2),
++ &index_start, &index_offset);
++
++ squashfs_get_cached_block(s, index->name, index_start,
++ index_offset, index->size + 1,
++ &index_start, &index_offset);
++
++ index->name[index->size + 1] = '\0';
++
++ if (strcmp(index->name, str) > 0)
++ break;
++
++ length = index->index;
++ *next_block = index->start_block + sblk->directory_table_start;
++ }
++
++ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
++ kfree(str);
++failure:
++ return length;
++}
++
++
++static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir)
++{
++ struct inode *i = file->f_dentry->d_inode;
++ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++ long long next_block = SQUASHFS_I(i)->start_block +
++ sblk->directory_table_start;
++ int next_offset = SQUASHFS_I(i)->offset, length = 0,
++ dir_count;
++ struct squashfs_dir_header_2 dirh;
++ struct squashfs_dir_entry_2 *dire;
++
++ TRACE("Entered squashfs_readdir_2 [%llx:%x]\n", next_block, next_offset);
++
++ if (!(dire = kmalloc(sizeof(struct squashfs_dir_entry) +
++ SQUASHFS_NAME_LEN + 1, GFP_KERNEL))) {
++ ERROR("Failed to allocate squashfs_dir_entry\n");
++ goto finish;
++ }
++
++ length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset,
++ SQUASHFS_I(i)->u.s2.directory_index_start,
++ SQUASHFS_I(i)->u.s2.directory_index_offset,
++ SQUASHFS_I(i)->u.s2.directory_index_count,
++ file->f_pos);
++
++ while (length < i_size_read(i)) {
++ /* read directory header */
++ if (msblk->swap) {
++ struct squashfs_dir_header_2 sdirh;
++
++ if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh,
++ next_block, next_offset, sizeof(sdirh),
++ &next_block, &next_offset))
++ goto failed_read;
++
++ length += sizeof(sdirh);
++ SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh);
++ } else {
++ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
++ next_block, next_offset, sizeof(dirh),
++ &next_block, &next_offset))
++ goto failed_read;
++
++ length += sizeof(dirh);
++ }
++
++ dir_count = dirh.count + 1;
++ while (dir_count--) {
++ if (msblk->swap) {
++ struct squashfs_dir_entry_2 sdire;
++ if (!squashfs_get_cached_block(i->i_sb, (char *)
++ &sdire, next_block, next_offset,
++ sizeof(sdire), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ length += sizeof(sdire);
++ SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire);
++ } else {
++ if (!squashfs_get_cached_block(i->i_sb, (char *)
++ dire, next_block, next_offset,
++ sizeof(*dire), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ length += sizeof(*dire);
++ }
++
++ if (!squashfs_get_cached_block(i->i_sb, dire->name,
++ next_block, next_offset,
++ dire->size + 1, &next_block,
++ &next_offset))
++ goto failed_read;
++
++ length += dire->size + 1;
++
++ if (file->f_pos >= length)
++ continue;
++
++ dire->name[dire->size + 1] = '\0';
++
++ TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d)\n",
++ (unsigned int) dirent, dire->name,
++ dire->size + 1, (int) file->f_pos,
++ dirh.start_block, dire->offset,
++ squashfs_filetype_table[dire->type]);
++
++ if (filldir(dirent, dire->name, dire->size + 1,
++ file->f_pos, SQUASHFS_MK_VFS_INODE(
++ dirh.start_block, dire->offset),
++ squashfs_filetype_table[dire->type])
++ < 0) {
++ TRACE("Filldir returned less than 0\n");
++ goto finish;
++ }
++ file->f_pos = length;
++ }
++ }
++
++finish:
++ kfree(dire);
++ return 0;
++
++failed_read:
++ ERROR("Unable to read directory block [%llx:%x]\n", next_block,
++ next_offset);
++ kfree(dire);
++ return 0;
++}
++
++
++static struct dentry *squashfs_lookup_2(struct inode *i, struct dentry *dentry,
++ struct nameidata *nd)
++{
++ const unsigned char *name = dentry->d_name.name;
++ int len = dentry->d_name.len;
++ struct inode *inode = NULL;
++ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++ long long next_block = SQUASHFS_I(i)->start_block +
++ sblk->directory_table_start;
++ int next_offset = SQUASHFS_I(i)->offset, length = 0,
++ dir_count;
++ struct squashfs_dir_header_2 dirh;
++ struct squashfs_dir_entry_2 *dire;
++ int sorted = sblk->s_major == 2 && sblk->s_minor >= 1;
++
++ TRACE("Entered squashfs_lookup_2 [%llx:%x]\n", next_block, next_offset);
++
++ if (!(dire = kmalloc(sizeof(struct squashfs_dir_entry) +
++ SQUASHFS_NAME_LEN + 1, GFP_KERNEL))) {
++ ERROR("Failed to allocate squashfs_dir_entry\n");
++ goto exit_loop;
++ }
++
++ if (len > SQUASHFS_NAME_LEN)
++ goto exit_loop;
++
++ length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset,
++ SQUASHFS_I(i)->u.s2.directory_index_start,
++ SQUASHFS_I(i)->u.s2.directory_index_offset,
++ SQUASHFS_I(i)->u.s2.directory_index_count, name,
++ len);
++
++ while (length < i_size_read(i)) {
++ /* read directory header */
++ if (msblk->swap) {
++ struct squashfs_dir_header_2 sdirh;
++ if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh,
++ next_block, next_offset, sizeof(sdirh),
++ &next_block, &next_offset))
++ goto failed_read;
++
++ length += sizeof(sdirh);
++ SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh);
++ } else {
++ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
++ next_block, next_offset, sizeof(dirh),
++ &next_block, &next_offset))
++ goto failed_read;
++
++ length += sizeof(dirh);
++ }
++
++ dir_count = dirh.count + 1;
++ while (dir_count--) {
++ if (msblk->swap) {
++ struct squashfs_dir_entry_2 sdire;
++ if (!squashfs_get_cached_block(i->i_sb, (char *)
++ &sdire, next_block,next_offset,
++ sizeof(sdire), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ length += sizeof(sdire);
++ SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire);
++ } else {
++ if (!squashfs_get_cached_block(i->i_sb, (char *)
++ dire, next_block,next_offset,
++ sizeof(*dire), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ length += sizeof(*dire);
++ }
++
++ if (!squashfs_get_cached_block(i->i_sb, dire->name,
++ next_block, next_offset, dire->size + 1,
++ &next_block, &next_offset))
++ goto failed_read;
++
++ length += dire->size + 1;
++
++ if (sorted && name[0] < dire->name[0])
++ goto exit_loop;
++
++ if ((len == dire->size + 1) && !strncmp(name,
++ dire->name, len)) {
++ squashfs_inode_t ino =
++ SQUASHFS_MKINODE(dirh.start_block,
++ dire->offset);
++ unsigned int inode_number = SQUASHFS_MK_VFS_INODE(dirh.start_block,
++ dire->offset);
++
++ TRACE("calling squashfs_iget for directory "
++ "entry %s, inode %x:%x, %lld\n", name,
++ dirh.start_block, dire->offset, ino);
++
++ inode = squashfs_iget(i->i_sb, ino, inode_number);
++
++ goto exit_loop;
++ }
++ }
++ }
++
++exit_loop:
++ kfree(dire);
++ d_add(dentry, inode);
++ return ERR_PTR(0);
++
++failed_read:
++ ERROR("Unable to read directory block [%llx:%x]\n", next_block,
++ next_offset);
++ goto exit_loop;
++}
++
++
++int squashfs_2_0_supported(struct squashfs_sb_info *msblk)
++{
++ struct squashfs_super_block *sblk = &msblk->sblk;
++
++ msblk->read_inode = squashfs_read_inode_2;
++ msblk->read_fragment_index_table = read_fragment_index_table_2;
++
++ sblk->bytes_used = sblk->bytes_used_2;
++ sblk->uid_start = sblk->uid_start_2;
++ sblk->guid_start = sblk->guid_start_2;
++ sblk->inode_table_start = sblk->inode_table_start_2;
++ sblk->directory_table_start = sblk->directory_table_start_2;
++ sblk->fragment_table_start = sblk->fragment_table_start_2;
++
++ return 1;
++}
+diff --git a/include/linux/aufs_type.h b/include/linux/aufs_type.h
+new file mode 100755
+index 0000000..8b4629e
+--- /dev/null
++++ b/include/linux/aufs_type.h
+@@ -0,0 +1,97 @@
++/*
++ * 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: aufs_type.h,v 1.55 2007/05/14 03:40:57 sfjro Exp $ */
++
++#include <linux/ioctl.h>
++
++#ifndef __AUFS_TYPE_H__
++#define __AUFS_TYPE_H__
++
++#define AUFS_VERSION "20070514"
++
++/* ---------------------------------------------------------------------- */
++
++#ifdef CONFIG_AUFS_BRANCH_MAX_127
++typedef 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_DIRWH_DEF 3
++#define AUFS_RDCACHE_DEF 10 /* seconds */
++#define AUFS_WKQ_NAME AUFS_NAME "d"
++#define AUFS_NWKQ_DEF 4
++
++#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
++};
++
++struct aufs_ctl_cp {
++ int bsrc, bdst;
++ int err;
++};
++
++#define Type 'A'
++#define AUFS_CTL_REFRESH _IO(Type, AuCtl_REFRESH)
++//#define AUFS_CTL_REFRESHV _IO(Type, AuCtl_REFRESHV)
++//#define AUFS_CTL_FLUSH_PLINK _IOR(Type, AuCtl_FLUSH_PLINK)
++//#define AUFS_CTL_CPUP _IOWR(Type, AuCtl_CPUP, struct aufs_ctl_cp)
++#define AUFS_CTL_CPDOWN _IOWR(Type, AuCtl_CPDOWN, struct aufs_ctl_cp)
++#define AUFS_CTL_MVDOWN _IOWR(Type, AuCtl_MVDOWN, struct aufs_ctl_cp)
++#undef Type
++
++#endif /* __AUFS_TYPE_H__ */
+diff --git a/include/linux/squashfs_fs.h b/include/linux/squashfs_fs.h
+new file mode 100644
+index 0000000..a9380ad
+--- /dev/null
++++ b/include/linux/squashfs_fs.h
+@@ -0,0 +1,934 @@
++#ifndef SQUASHFS_FS
++#define SQUASHFS_FS
++
++/*
++ * Squashfs
++ *
++ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
++ * Phillip Lougher <phillip@lougher.org.uk>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2,
++ * 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ * squashfs_fs.h
++ */
++
++#ifndef CONFIG_SQUASHFS_2_0_COMPATIBILITY
++#define CONFIG_SQUASHFS_2_0_COMPATIBILITY
++#endif
++
++#ifdef CONFIG_SQUASHFS_VMALLOC
++#define SQUASHFS_ALLOC(a) vmalloc(a)
++#define SQUASHFS_FREE(a) vfree(a)
++#else
++#define SQUASHFS_ALLOC(a) kmalloc(a, GFP_KERNEL)
++#define SQUASHFS_FREE(a) kfree(a)
++#endif
++#define SQUASHFS_CACHED_FRAGMENTS CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE
++#define SQUASHFS_MAJOR 3
++#define SQUASHFS_MINOR 0
++#define SQUASHFS_MAGIC 0x73717368
++#define SQUASHFS_MAGIC_SWAP 0x68737173
++#define SQUASHFS_START 0
++
++/* size of metadata (inode and directory) blocks */
++#define SQUASHFS_METADATA_SIZE 8192
++#define SQUASHFS_METADATA_LOG 13
++
++/* default size of data blocks */
++#define SQUASHFS_FILE_SIZE 65536
++#define SQUASHFS_FILE_LOG 16
++
++#define SQUASHFS_FILE_MAX_SIZE 65536
++
++/* Max number of uids and gids */
++#define SQUASHFS_UIDS 256
++#define SQUASHFS_GUIDS 255
++
++/* Max length of filename (not 255) */
++#define SQUASHFS_NAME_LEN 256
++
++#define SQUASHFS_INVALID ((long long) 0xffffffffffff)
++#define SQUASHFS_INVALID_FRAG ((unsigned int) 0xffffffff)
++#define SQUASHFS_INVALID_BLK ((long long) -1)
++#define SQUASHFS_USED_BLK ((long long) -2)
++
++/* Filesystem flags */
++#define SQUASHFS_NOI 0
++#define SQUASHFS_NOD 1
++#define SQUASHFS_CHECK 2
++#define SQUASHFS_NOF 3
++#define SQUASHFS_NO_FRAG 4
++#define SQUASHFS_ALWAYS_FRAG 5
++#define SQUASHFS_DUPLICATE 6
++#define SQUASHFS_EXPORT 7
++
++#define SQUASHFS_BIT(flag, bit) ((flag >> bit) & 1)
++
++#define SQUASHFS_UNCOMPRESSED_INODES(flags) SQUASHFS_BIT(flags, \
++ SQUASHFS_NOI)
++
++#define SQUASHFS_UNCOMPRESSED_DATA(flags) SQUASHFS_BIT(flags, \
++ SQUASHFS_NOD)
++
++#define SQUASHFS_UNCOMPRESSED_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
++ SQUASHFS_NOF)
++
++#define SQUASHFS_NO_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
++ SQUASHFS_NO_FRAG)
++
++#define SQUASHFS_ALWAYS_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
++ SQUASHFS_ALWAYS_FRAG)
++
++#define SQUASHFS_DUPLICATES(flags) SQUASHFS_BIT(flags, \
++ SQUASHFS_DUPLICATE)
++
++#define SQUASHFS_EXPORTABLE(flags) SQUASHFS_BIT(flags, \
++ SQUASHFS_EXPORT)
++
++#define SQUASHFS_CHECK_DATA(flags) SQUASHFS_BIT(flags, \
++ SQUASHFS_CHECK)
++
++#define SQUASHFS_MKFLAGS(noi, nod, check_data, nof, no_frag, always_frag, \
++ duplicate_checking, exortable) (noi | (nod << 1) | (check_data << 2) \
++ | (nof << 3) | (no_frag << 4) | (always_frag << 5) | \
++ (duplicate_checking << 6) | (exportable << 7))
++
++/* Max number of types and file types */
++#define SQUASHFS_DIR_TYPE 1
++#define SQUASHFS_FILE_TYPE 2
++#define SQUASHFS_SYMLINK_TYPE 3
++#define SQUASHFS_BLKDEV_TYPE 4
++#define SQUASHFS_CHRDEV_TYPE 5
++#define SQUASHFS_FIFO_TYPE 6
++#define SQUASHFS_SOCKET_TYPE 7
++#define SQUASHFS_LDIR_TYPE 8
++#define SQUASHFS_LREG_TYPE 9
++
++/* 1.0 filesystem type definitions */
++#define SQUASHFS_TYPES 5
++#define SQUASHFS_IPC_TYPE 0
++
++/* Flag whether block is compressed or uncompressed, bit is set if block is
++ * uncompressed */
++#define SQUASHFS_COMPRESSED_BIT (1 << 15)
++
++#define SQUASHFS_COMPRESSED_SIZE(B) (((B) & ~SQUASHFS_COMPRESSED_BIT) ? \
++ (B) & ~SQUASHFS_COMPRESSED_BIT : SQUASHFS_COMPRESSED_BIT)
++
++#define SQUASHFS_COMPRESSED(B) (!((B) & SQUASHFS_COMPRESSED_BIT))
++
++#define SQUASHFS_COMPRESSED_BIT_BLOCK (1 << 24)
++
++#define SQUASHFS_COMPRESSED_SIZE_BLOCK(B) (((B) & \
++ ~SQUASHFS_COMPRESSED_BIT_BLOCK) ? (B) & \
++ ~SQUASHFS_COMPRESSED_BIT_BLOCK : SQUASHFS_COMPRESSED_BIT_BLOCK)
++
++#define SQUASHFS_COMPRESSED_BLOCK(B) (!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK))
++
++/*
++ * Inode number ops. Inodes consist of a compressed block number, and an
++ * uncompressed offset within that block
++ */
++#define SQUASHFS_INODE_BLK(a) ((unsigned int) ((a) >> 16))
++
++#define SQUASHFS_INODE_OFFSET(a) ((unsigned int) ((a) & 0xffff))
++
++#define SQUASHFS_MKINODE(A, B) ((squashfs_inode_t)(((squashfs_inode_t) (A)\
++ << 16) + (B)))
++
++/* Compute 32 bit VFS inode number from squashfs inode number */
++#define SQUASHFS_MK_VFS_INODE(a, b) ((unsigned int) (((a) << 8) + \
++ ((b) >> 2) + 1))
++/* XXX */
++
++/* Translate between VFS mode and squashfs mode */
++#define SQUASHFS_MODE(a) ((a) & 0xfff)
++
++/* fragment and fragment table defines */
++#define SQUASHFS_FRAGMENT_BYTES(A) ((A) * sizeof(struct squashfs_fragment_entry))
++
++#define SQUASHFS_FRAGMENT_INDEX(A) (SQUASHFS_FRAGMENT_BYTES(A) / \
++ SQUASHFS_METADATA_SIZE)
++
++#define SQUASHFS_FRAGMENT_INDEX_OFFSET(A) (SQUASHFS_FRAGMENT_BYTES(A) % \
++ SQUASHFS_METADATA_SIZE)
++
++#define SQUASHFS_FRAGMENT_INDEXES(A) ((SQUASHFS_FRAGMENT_BYTES(A) + \
++ SQUASHFS_METADATA_SIZE - 1) / \
++ SQUASHFS_METADATA_SIZE)
++
++#define SQUASHFS_FRAGMENT_INDEX_BYTES(A) (SQUASHFS_FRAGMENT_INDEXES(A) *\
++ sizeof(long long))
++
++/* inode lookup table defines */
++#define SQUASHFS_LOOKUP_BYTES(A) ((A) * sizeof(squashfs_inode_t))
++
++#define SQUASHFS_LOOKUP_BLOCK(A) (SQUASHFS_LOOKUP_BYTES(A) / \
++ SQUASHFS_METADATA_SIZE)
++
++#define SQUASHFS_LOOKUP_BLOCK_OFFSET(A) (SQUASHFS_LOOKUP_BYTES(A) % \
++ SQUASHFS_METADATA_SIZE)
++
++#define SQUASHFS_LOOKUP_BLOCKS(A) ((SQUASHFS_LOOKUP_BYTES(A) + \
++ SQUASHFS_METADATA_SIZE - 1) / \
++ SQUASHFS_METADATA_SIZE)
++
++#define SQUASHFS_LOOKUP_BLOCK_BYTES(A) (SQUASHFS_LOOKUP_BLOCKS(A) *\
++ sizeof(long long))
++
++/* cached data constants for filesystem */
++#define SQUASHFS_CACHED_BLKS 8
++
++#define SQUASHFS_MAX_FILE_SIZE_LOG 64
++
++#define SQUASHFS_MAX_FILE_SIZE ((long long) 1 << \
++ (SQUASHFS_MAX_FILE_SIZE_LOG - 2))
++
++#define SQUASHFS_MARKER_BYTE 0xff
++
++/* meta index cache */
++#define SQUASHFS_META_INDEXES (SQUASHFS_METADATA_SIZE / sizeof(unsigned int))
++#define SQUASHFS_META_ENTRIES 31
++#define SQUASHFS_META_NUMBER 8
++#define SQUASHFS_SLOTS 4
++
++struct meta_entry {
++ long long data_block;
++ unsigned int index_block;
++ unsigned short offset;
++ unsigned short pad;
++};
++
++struct meta_index {
++ unsigned int inode_number;
++ unsigned int offset;
++ unsigned short entries;
++ unsigned short skip;
++ unsigned short locked;
++ unsigned short pad;
++ struct meta_entry meta_entry[SQUASHFS_META_ENTRIES];
++};
++
++
++/*
++ * definitions for structures on disk
++ */
++
++typedef long long squashfs_block_t;
++typedef long long squashfs_inode_t;
++
++struct squashfs_super_block {
++ unsigned int s_magic;
++ unsigned int inodes;
++ unsigned int bytes_used_2;
++ unsigned int uid_start_2;
++ unsigned int guid_start_2;
++ unsigned int inode_table_start_2;
++ unsigned int directory_table_start_2;
++ unsigned int s_major:16;
++ unsigned int s_minor:16;
++ unsigned int block_size_1:16;
++ unsigned int block_log:16;
++ unsigned int flags:8;
++ unsigned int no_uids:8;
++ unsigned int no_guids:8;
++ unsigned int mkfs_time /* time of filesystem creation */;
++ squashfs_inode_t root_inode;
++ unsigned int block_size;
++ unsigned int fragments;
++ unsigned int fragment_table_start_2;
++ long long bytes_used;
++ long long uid_start;
++ long long guid_start;
++ long long inode_table_start;
++ long long directory_table_start;
++ long long fragment_table_start;
++ long long lookup_table_start;
++} __attribute__ ((packed));
++
++struct squashfs_dir_index {
++ unsigned int index;
++ unsigned int start_block;
++ unsigned char size;
++ unsigned char name[0];
++} __attribute__ ((packed));
++
++#define SQUASHFS_BASE_INODE_HEADER \
++ unsigned int inode_type:4; \
++ unsigned int mode:12; \
++ unsigned int uid:8; \
++ unsigned int guid:8; \
++ unsigned int mtime; \
++ unsigned int inode_number;
++
++struct squashfs_base_inode_header {
++ SQUASHFS_BASE_INODE_HEADER;
++} __attribute__ ((packed));
++
++struct squashfs_ipc_inode_header {
++ SQUASHFS_BASE_INODE_HEADER;
++ unsigned int nlink;
++} __attribute__ ((packed));
++
++struct squashfs_dev_inode_header {
++ SQUASHFS_BASE_INODE_HEADER;
++ unsigned int nlink;
++ unsigned short rdev;
++} __attribute__ ((packed));
++
++struct squashfs_symlink_inode_header {
++ SQUASHFS_BASE_INODE_HEADER;
++ unsigned int nlink;
++ unsigned short symlink_size;
++ char symlink[0];
++} __attribute__ ((packed));
++
++struct squashfs_reg_inode_header {
++ SQUASHFS_BASE_INODE_HEADER;
++ squashfs_block_t start_block;
++ unsigned int fragment;
++ unsigned int offset;
++ unsigned int file_size;
++ unsigned short block_list[0];
++} __attribute__ ((packed));
++
++struct squashfs_lreg_inode_header {
++ SQUASHFS_BASE_INODE_HEADER;
++ unsigned int nlink;
++ squashfs_block_t start_block;
++ unsigned int fragment;
++ unsigned int offset;
++ long long file_size;
++ unsigned short block_list[0];
++} __attribute__ ((packed));
++
++struct squashfs_dir_inode_header {
++ SQUASHFS_BASE_INODE_HEADER;
++ unsigned int nlink;
++ unsigned int file_size:19;
++ unsigned int offset:13;
++ unsigned int start_block;
++ unsigned int parent_inode;
++} __attribute__ ((packed));
++
++struct squashfs_ldir_inode_header {
++ SQUASHFS_BASE_INODE_HEADER;
++ unsigned int nlink;
++ unsigned int file_size:27;
++ unsigned int offset:13;
++ unsigned int start_block;
++ unsigned int i_count:16;
++ unsigned int parent_inode;
++ struct squashfs_dir_index index[0];
++} __attribute__ ((packed));
++
++union squashfs_inode_header {
++ struct squashfs_base_inode_header base;
++ struct squashfs_dev_inode_header dev;
++ struct squashfs_symlink_inode_header symlink;
++ struct squashfs_reg_inode_header reg;
++ struct squashfs_lreg_inode_header lreg;
++ struct squashfs_dir_inode_header dir;
++ struct squashfs_ldir_inode_header ldir;
++ struct squashfs_ipc_inode_header ipc;
++};
++
++struct squashfs_dir_entry {
++ unsigned int offset:13;
++ unsigned int type:3;
++ unsigned int size:8;
++ int inode_number:16;
++ char name[0];
++} __attribute__ ((packed));
++
++struct squashfs_dir_header {
++ unsigned int count:8;
++ unsigned int start_block;
++ unsigned int inode_number;
++} __attribute__ ((packed));
++
++struct squashfs_fragment_entry {
++ long long start_block;
++ unsigned int size;
++ unsigned int pending;
++} __attribute__ ((packed));
++
++extern int squashfs_uncompress_block(void *d, int dstlen, void *s, int srclen);
++extern int squashfs_uncompress_init(void);
++extern int squashfs_uncompress_exit(void);
++
++/*
++ * macros to convert each packed bitfield structure from little endian to big
++ * endian and vice versa. These are needed when creating or using a filesystem
++ * on a machine with different byte ordering to the target architecture.
++ *
++ */
++
++#define SQUASHFS_SWAP_START \
++ int bits;\
++ int b_pos;\
++ unsigned long long val;\
++ unsigned char *s;\
++ unsigned char *d;
++
++#define SQUASHFS_SWAP_SUPER_BLOCK(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_super_block));\
++ SQUASHFS_SWAP((s)->s_magic, d, 0, 32);\
++ SQUASHFS_SWAP((s)->inodes, d, 32, 32);\
++ SQUASHFS_SWAP((s)->bytes_used_2, d, 64, 32);\
++ SQUASHFS_SWAP((s)->uid_start_2, d, 96, 32);\
++ SQUASHFS_SWAP((s)->guid_start_2, d, 128, 32);\
++ SQUASHFS_SWAP((s)->inode_table_start_2, d, 160, 32);\
++ SQUASHFS_SWAP((s)->directory_table_start_2, d, 192, 32);\
++ SQUASHFS_SWAP((s)->s_major, d, 224, 16);\
++ SQUASHFS_SWAP((s)->s_minor, d, 240, 16);\
++ SQUASHFS_SWAP((s)->block_size_1, d, 256, 16);\
++ SQUASHFS_SWAP((s)->block_log, d, 272, 16);\
++ SQUASHFS_SWAP((s)->flags, d, 288, 8);\
++ SQUASHFS_SWAP((s)->no_uids, d, 296, 8);\
++ SQUASHFS_SWAP((s)->no_guids, d, 304, 8);\
++ SQUASHFS_SWAP((s)->mkfs_time, d, 312, 32);\
++ SQUASHFS_SWAP((s)->root_inode, d, 344, 64);\
++ SQUASHFS_SWAP((s)->block_size, d, 408, 32);\
++ SQUASHFS_SWAP((s)->fragments, d, 440, 32);\
++ SQUASHFS_SWAP((s)->fragment_table_start_2, d, 472, 32);\
++ SQUASHFS_SWAP((s)->bytes_used, d, 504, 64);\
++ SQUASHFS_SWAP((s)->uid_start, d, 568, 64);\
++ SQUASHFS_SWAP((s)->guid_start, d, 632, 64);\
++ SQUASHFS_SWAP((s)->inode_table_start, d, 696, 64);\
++ SQUASHFS_SWAP((s)->directory_table_start, d, 760, 64);\
++ SQUASHFS_SWAP((s)->fragment_table_start, d, 824, 64);\
++ SQUASHFS_SWAP((s)->lookup_table_start, d, 888, 64);\
++}
++
++#define SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\
++ SQUASHFS_MEMSET(s, d, n);\
++ SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
++ SQUASHFS_SWAP((s)->mode, d, 4, 12);\
++ SQUASHFS_SWAP((s)->uid, d, 16, 8);\
++ SQUASHFS_SWAP((s)->guid, d, 24, 8);\
++ SQUASHFS_SWAP((s)->mtime, d, 32, 32);\
++ SQUASHFS_SWAP((s)->inode_number, d, 64, 32);
++
++#define SQUASHFS_SWAP_BASE_INODE_HEADER(s, d, n) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\
++}
++
++#define SQUASHFS_SWAP_IPC_INODE_HEADER(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
++ sizeof(struct squashfs_ipc_inode_header))\
++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
++}
++
++#define SQUASHFS_SWAP_DEV_INODE_HEADER(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
++ sizeof(struct squashfs_dev_inode_header)); \
++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
++ SQUASHFS_SWAP((s)->rdev, d, 128, 16);\
++}
++
++#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
++ sizeof(struct squashfs_symlink_inode_header));\
++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
++ SQUASHFS_SWAP((s)->symlink_size, d, 128, 16);\
++}
++
++#define SQUASHFS_SWAP_REG_INODE_HEADER(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
++ sizeof(struct squashfs_reg_inode_header));\
++ SQUASHFS_SWAP((s)->start_block, d, 96, 64);\
++ SQUASHFS_SWAP((s)->fragment, d, 160, 32);\
++ SQUASHFS_SWAP((s)->offset, d, 192, 32);\
++ SQUASHFS_SWAP((s)->file_size, d, 224, 32);\
++}
++
++#define SQUASHFS_SWAP_LREG_INODE_HEADER(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
++ sizeof(struct squashfs_lreg_inode_header));\
++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
++ SQUASHFS_SWAP((s)->start_block, d, 128, 64);\
++ SQUASHFS_SWAP((s)->fragment, d, 192, 32);\
++ SQUASHFS_SWAP((s)->offset, d, 224, 32);\
++ SQUASHFS_SWAP((s)->file_size, d, 256, 64);\
++}
++
++#define SQUASHFS_SWAP_DIR_INODE_HEADER(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
++ sizeof(struct squashfs_dir_inode_header));\
++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
++ SQUASHFS_SWAP((s)->file_size, d, 128, 19);\
++ SQUASHFS_SWAP((s)->offset, d, 147, 13);\
++ SQUASHFS_SWAP((s)->start_block, d, 160, 32);\
++ SQUASHFS_SWAP((s)->parent_inode, d, 192, 32);\
++}
++
++#define SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
++ sizeof(struct squashfs_ldir_inode_header));\
++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
++ SQUASHFS_SWAP((s)->file_size, d, 128, 27);\
++ SQUASHFS_SWAP((s)->offset, d, 155, 13);\
++ SQUASHFS_SWAP((s)->start_block, d, 168, 32);\
++ SQUASHFS_SWAP((s)->i_count, d, 200, 16);\
++ SQUASHFS_SWAP((s)->parent_inode, d, 216, 32);\
++}
++
++#define SQUASHFS_SWAP_DIR_INDEX(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index));\
++ SQUASHFS_SWAP((s)->index, d, 0, 32);\
++ SQUASHFS_SWAP((s)->start_block, d, 32, 32);\
++ SQUASHFS_SWAP((s)->size, d, 64, 8);\
++}
++
++#define SQUASHFS_SWAP_DIR_HEADER(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header));\
++ SQUASHFS_SWAP((s)->count, d, 0, 8);\
++ SQUASHFS_SWAP((s)->start_block, d, 8, 32);\
++ SQUASHFS_SWAP((s)->inode_number, d, 40, 32);\
++}
++
++#define SQUASHFS_SWAP_DIR_ENTRY(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry));\
++ SQUASHFS_SWAP((s)->offset, d, 0, 13);\
++ SQUASHFS_SWAP((s)->type, d, 13, 3);\
++ SQUASHFS_SWAP((s)->size, d, 16, 8);\
++ SQUASHFS_SWAP((s)->inode_number, d, 24, 16);\
++}
++
++#define SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry));\
++ SQUASHFS_SWAP((s)->start_block, d, 0, 64);\
++ SQUASHFS_SWAP((s)->size, d, 64, 32);\
++}
++
++#define SQUASHFS_SWAP_INODE_T(s, d) SQUASHFS_SWAP_LONG_LONGS(s, d, 1)
++
++#define SQUASHFS_SWAP_SHORTS(s, d, n) {\
++ int entry;\
++ int bit_position;\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_MEMSET(s, d, n * 2);\
++ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
++ 16)\
++ SQUASHFS_SWAP(s[entry], d, bit_position, 16);\
++}
++
++#define SQUASHFS_SWAP_INTS(s, d, n) {\
++ int entry;\
++ int bit_position;\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_MEMSET(s, d, n * 4);\
++ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
++ 32)\
++ SQUASHFS_SWAP(s[entry], d, bit_position, 32);\
++}
++
++#define SQUASHFS_SWAP_LONG_LONGS(s, d, n) {\
++ int entry;\
++ int bit_position;\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_MEMSET(s, d, n * 8);\
++ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
++ 64)\
++ SQUASHFS_SWAP(s[entry], d, bit_position, 64);\
++}
++
++#define SQUASHFS_SWAP_DATA(s, d, n, bits) {\
++ int entry;\
++ int bit_position;\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_MEMSET(s, d, n * bits / 8);\
++ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
++ bits)\
++ SQUASHFS_SWAP(s[entry], d, bit_position, bits);\
++}
++
++#define SQUASHFS_SWAP_FRAGMENT_INDEXES(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n)
++#define SQUASHFS_SWAP_LOOKUP_BLOCKS(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n)
++
++#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY
++
++struct squashfs_base_inode_header_1 {
++ unsigned int inode_type:4;
++ unsigned int mode:12; /* protection */
++ unsigned int uid:4; /* index into uid table */
++ unsigned int guid:4; /* index into guid table */
++} __attribute__ ((packed));
++
++struct squashfs_ipc_inode_header_1 {
++ unsigned int inode_type:4;
++ unsigned int mode:12; /* protection */
++ unsigned int uid:4; /* index into uid table */
++ unsigned int guid:4; /* index into guid table */
++ unsigned int type:4;
++ unsigned int offset:4;
++} __attribute__ ((packed));
++
++struct squashfs_dev_inode_header_1 {
++ unsigned int inode_type:4;
++ unsigned int mode:12; /* protection */
++ unsigned int uid:4; /* index into uid table */
++ unsigned int guid:4; /* index into guid table */
++ unsigned short rdev;
++} __attribute__ ((packed));
++
++struct squashfs_symlink_inode_header_1 {
++ unsigned int inode_type:4;
++ unsigned int mode:12; /* protection */
++ unsigned int uid:4; /* index into uid table */
++ unsigned int guid:4; /* index into guid table */
++ unsigned short symlink_size;
++ char symlink[0];
++} __attribute__ ((packed));
++
++struct squashfs_reg_inode_header_1 {
++ unsigned int inode_type:4;
++ unsigned int mode:12; /* protection */
++ unsigned int uid:4; /* index into uid table */
++ unsigned int guid:4; /* index into guid table */
++ unsigned int mtime;
++ unsigned int start_block;
++ unsigned int file_size:32;
++ unsigned short block_list[0];
++} __attribute__ ((packed));
++
++struct squashfs_dir_inode_header_1 {
++ unsigned int inode_type:4;
++ unsigned int mode:12; /* protection */
++ unsigned int uid:4; /* index into uid table */
++ unsigned int guid:4; /* index into guid table */
++ unsigned int file_size:19;
++ unsigned int offset:13;
++ unsigned int mtime;
++ unsigned int start_block:24;
++} __attribute__ ((packed));
++
++#define SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n) \
++ SQUASHFS_MEMSET(s, d, n);\
++ SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
++ SQUASHFS_SWAP((s)->mode, d, 4, 12);\
++ SQUASHFS_SWAP((s)->uid, d, 16, 4);\
++ SQUASHFS_SWAP((s)->guid, d, 20, 4);
++
++#define SQUASHFS_SWAP_BASE_INODE_HEADER_1(s, d, n) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n)\
++}
++
++#define SQUASHFS_SWAP_IPC_INODE_HEADER_1(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
++ sizeof(struct squashfs_ipc_inode_header_1));\
++ SQUASHFS_SWAP((s)->type, d, 24, 4);\
++ SQUASHFS_SWAP((s)->offset, d, 28, 4);\
++}
++
++#define SQUASHFS_SWAP_DEV_INODE_HEADER_1(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
++ sizeof(struct squashfs_dev_inode_header_1));\
++ SQUASHFS_SWAP((s)->rdev, d, 24, 16);\
++}
++
++#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_1(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
++ sizeof(struct squashfs_symlink_inode_header_1));\
++ SQUASHFS_SWAP((s)->symlink_size, d, 24, 16);\
++}
++
++#define SQUASHFS_SWAP_REG_INODE_HEADER_1(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
++ sizeof(struct squashfs_reg_inode_header_1));\
++ SQUASHFS_SWAP((s)->mtime, d, 24, 32);\
++ SQUASHFS_SWAP((s)->start_block, d, 56, 32);\
++ SQUASHFS_SWAP((s)->file_size, d, 88, 32);\
++}
++
++#define SQUASHFS_SWAP_DIR_INODE_HEADER_1(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
++ sizeof(struct squashfs_dir_inode_header_1));\
++ SQUASHFS_SWAP((s)->file_size, d, 24, 19);\
++ SQUASHFS_SWAP((s)->offset, d, 43, 13);\
++ SQUASHFS_SWAP((s)->mtime, d, 56, 32);\
++ SQUASHFS_SWAP((s)->start_block, d, 88, 24);\
++}
++
++#endif
++
++#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY
++
++struct squashfs_dir_index_2 {
++ unsigned int index:27;
++ unsigned int start_block:29;
++ unsigned char size;
++ unsigned char name[0];
++} __attribute__ ((packed));
++
++struct squashfs_base_inode_header_2 {
++ unsigned int inode_type:4;
++ unsigned int mode:12; /* protection */
++ unsigned int uid:8; /* index into uid table */
++ unsigned int guid:8; /* index into guid table */
++} __attribute__ ((packed));
++
++struct squashfs_ipc_inode_header_2 {
++ unsigned int inode_type:4;
++ unsigned int mode:12; /* protection */
++ unsigned int uid:8; /* index into uid table */
++ unsigned int guid:8; /* index into guid table */
++} __attribute__ ((packed));
++
++struct squashfs_dev_inode_header_2 {
++ unsigned int inode_type:4;
++ unsigned int mode:12; /* protection */
++ unsigned int uid:8; /* index into uid table */
++ unsigned int guid:8; /* index into guid table */
++ unsigned short rdev;
++} __attribute__ ((packed));
++
++struct squashfs_symlink_inode_header_2 {
++ unsigned int inode_type:4;
++ unsigned int mode:12; /* protection */
++ unsigned int uid:8; /* index into uid table */
++ unsigned int guid:8; /* index into guid table */
++ unsigned short symlink_size;
++ char symlink[0];
++} __attribute__ ((packed));
++
++struct squashfs_reg_inode_header_2 {
++ unsigned int inode_type:4;
++ unsigned int mode:12; /* protection */
++ unsigned int uid:8; /* index into uid table */
++ unsigned int guid:8; /* index into guid table */
++ unsigned int mtime;
++ unsigned int start_block;
++ unsigned int fragment;
++ unsigned int offset;
++ unsigned int file_size:32;
++ unsigned short block_list[0];
++} __attribute__ ((packed));
++
++struct squashfs_dir_inode_header_2 {
++ unsigned int inode_type:4;
++ unsigned int mode:12; /* protection */
++ unsigned int uid:8; /* index into uid table */
++ unsigned int guid:8; /* index into guid table */
++ unsigned int file_size:19;
++ unsigned int offset:13;
++ unsigned int mtime;
++ unsigned int start_block:24;
++} __attribute__ ((packed));
++
++struct squashfs_ldir_inode_header_2 {
++ unsigned int inode_type:4;
++ unsigned int mode:12; /* protection */
++ unsigned int uid:8; /* index into uid table */
++ unsigned int guid:8; /* index into guid table */
++ unsigned int file_size:27;
++ unsigned int offset:13;
++ unsigned int mtime;
++ unsigned int start_block:24;
++ unsigned int i_count:16;
++ struct squashfs_dir_index_2 index[0];
++} __attribute__ ((packed));
++
++union squashfs_inode_header_2 {
++ struct squashfs_base_inode_header_2 base;
++ struct squashfs_dev_inode_header_2 dev;
++ struct squashfs_symlink_inode_header_2 symlink;
++ struct squashfs_reg_inode_header_2 reg;
++ struct squashfs_dir_inode_header_2 dir;
++ struct squashfs_ldir_inode_header_2 ldir;
++ struct squashfs_ipc_inode_header_2 ipc;
++};
++
++struct squashfs_dir_header_2 {
++ unsigned int count:8;
++ unsigned int start_block:24;
++} __attribute__ ((packed));
++
++struct squashfs_dir_entry_2 {
++ unsigned int offset:13;
++ unsigned int type:3;
++ unsigned int size:8;
++ char name[0];
++} __attribute__ ((packed));
++
++struct squashfs_fragment_entry_2 {
++ unsigned int start_block;
++ unsigned int size;
++} __attribute__ ((packed));
++
++#define SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\
++ SQUASHFS_MEMSET(s, d, n);\
++ SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
++ SQUASHFS_SWAP((s)->mode, d, 4, 12);\
++ SQUASHFS_SWAP((s)->uid, d, 16, 8);\
++ SQUASHFS_SWAP((s)->guid, d, 24, 8);\
++
++#define SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, n) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\
++}
++
++#define SQUASHFS_SWAP_IPC_INODE_HEADER_2(s, d) \
++ SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, sizeof(struct squashfs_ipc_inode_header_2))
++
++#define SQUASHFS_SWAP_DEV_INODE_HEADER_2(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
++ sizeof(struct squashfs_dev_inode_header_2)); \
++ SQUASHFS_SWAP((s)->rdev, d, 32, 16);\
++}
++
++#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
++ sizeof(struct squashfs_symlink_inode_header_2));\
++ SQUASHFS_SWAP((s)->symlink_size, d, 32, 16);\
++}
++
++#define SQUASHFS_SWAP_REG_INODE_HEADER_2(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
++ sizeof(struct squashfs_reg_inode_header_2));\
++ SQUASHFS_SWAP((s)->mtime, d, 32, 32);\
++ SQUASHFS_SWAP((s)->start_block, d, 64, 32);\
++ SQUASHFS_SWAP((s)->fragment, d, 96, 32);\
++ SQUASHFS_SWAP((s)->offset, d, 128, 32);\
++ SQUASHFS_SWAP((s)->file_size, d, 160, 32);\
++}
++
++#define SQUASHFS_SWAP_DIR_INODE_HEADER_2(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
++ sizeof(struct squashfs_dir_inode_header_2));\
++ SQUASHFS_SWAP((s)->file_size, d, 32, 19);\
++ SQUASHFS_SWAP((s)->offset, d, 51, 13);\
++ SQUASHFS_SWAP((s)->mtime, d, 64, 32);\
++ SQUASHFS_SWAP((s)->start_block, d, 96, 24);\
++}
++
++#define SQUASHFS_SWAP_LDIR_INODE_HEADER_2(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
++ sizeof(struct squashfs_ldir_inode_header_2));\
++ SQUASHFS_SWAP((s)->file_size, d, 32, 27);\
++ SQUASHFS_SWAP((s)->offset, d, 59, 13);\
++ SQUASHFS_SWAP((s)->mtime, d, 72, 32);\
++ SQUASHFS_SWAP((s)->start_block, d, 104, 24);\
++ SQUASHFS_SWAP((s)->i_count, d, 128, 16);\
++}
++
++#define SQUASHFS_SWAP_DIR_INDEX_2(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index_2));\
++ SQUASHFS_SWAP((s)->index, d, 0, 27);\
++ SQUASHFS_SWAP((s)->start_block, d, 27, 29);\
++ SQUASHFS_SWAP((s)->size, d, 56, 8);\
++}
++#define SQUASHFS_SWAP_DIR_HEADER_2(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header_2));\
++ SQUASHFS_SWAP((s)->count, d, 0, 8);\
++ SQUASHFS_SWAP((s)->start_block, d, 8, 24);\
++}
++
++#define SQUASHFS_SWAP_DIR_ENTRY_2(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry_2));\
++ SQUASHFS_SWAP((s)->offset, d, 0, 13);\
++ SQUASHFS_SWAP((s)->type, d, 13, 3);\
++ SQUASHFS_SWAP((s)->size, d, 16, 8);\
++}
++
++#define SQUASHFS_SWAP_FRAGMENT_ENTRY_2(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry_2));\
++ SQUASHFS_SWAP((s)->start_block, d, 0, 32);\
++ SQUASHFS_SWAP((s)->size, d, 32, 32);\
++}
++
++#define SQUASHFS_SWAP_FRAGMENT_INDEXES_2(s, d, n) SQUASHFS_SWAP_INTS(s, d, n)
++
++/* fragment and fragment table defines */
++#define SQUASHFS_FRAGMENT_BYTES_2(A) (A * sizeof(struct squashfs_fragment_entry_2))
++
++#define SQUASHFS_FRAGMENT_INDEX_2(A) (SQUASHFS_FRAGMENT_BYTES_2(A) / \
++ SQUASHFS_METADATA_SIZE)
++
++#define SQUASHFS_FRAGMENT_INDEX_OFFSET_2(A) (SQUASHFS_FRAGMENT_BYTES_2(A) % \
++ SQUASHFS_METADATA_SIZE)
++
++#define SQUASHFS_FRAGMENT_INDEXES_2(A) ((SQUASHFS_FRAGMENT_BYTES_2(A) + \
++ SQUASHFS_METADATA_SIZE - 1) / \
++ SQUASHFS_METADATA_SIZE)
++
++#define SQUASHFS_FRAGMENT_INDEX_BYTES_2(A) (SQUASHFS_FRAGMENT_INDEXES_2(A) *\
++ sizeof(int))
++
++#endif
++
++#ifdef __KERNEL__
++
++/*
++ * macros used to swap each structure entry, taking into account
++ * bitfields and different bitfield placing conventions on differing
++ * architectures
++ */
++
++#include <asm/byteorder.h>
++
++#ifdef __BIG_ENDIAN
++ /* convert from little endian to big endian */
++#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \
++ tbits, b_pos)
++#else
++ /* convert from big endian to little endian */
++#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \
++ tbits, 64 - tbits - b_pos)
++#endif
++
++#define _SQUASHFS_SWAP(value, p, pos, tbits, SHIFT) {\
++ b_pos = pos % 8;\
++ val = 0;\
++ s = (unsigned char *)p + (pos / 8);\
++ d = ((unsigned char *) &val) + 7;\
++ for(bits = 0; bits < (tbits + b_pos); bits += 8) \
++ *d-- = *s++;\
++ value = (val >> (SHIFT))/* & ((1 << tbits) - 1)*/;\
++}
++
++#define SQUASHFS_MEMSET(s, d, n) memset(s, 0, n);
++
++#endif
++#endif
+diff --git a/include/linux/squashfs_fs_i.h b/include/linux/squashfs_fs_i.h
+new file mode 100644
+index 0000000..798891a
+--- /dev/null
++++ b/include/linux/squashfs_fs_i.h
+@@ -0,0 +1,45 @@
++#ifndef SQUASHFS_FS_I
++#define SQUASHFS_FS_I
++/*
++ * Squashfs
++ *
++ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
++ * Phillip Lougher <phillip@lougher.org.uk>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2,
++ * 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ * squashfs_fs_i.h
++ */
++
++struct squashfs_inode_info {
++ long long start_block;
++ unsigned int offset;
++ union {
++ struct {
++ long long fragment_start_block;
++ unsigned int fragment_size;
++ unsigned int fragment_offset;
++ long long block_list_start;
++ } s1;
++ struct {
++ long long directory_index_start;
++ unsigned int directory_index_offset;
++ unsigned int directory_index_count;
++ unsigned int parent_inode;
++ } s2;
++ } u;
++ struct inode vfs_inode;
++};
++#endif
+diff --git a/include/linux/squashfs_fs_sb.h b/include/linux/squashfs_fs_sb.h
+new file mode 100644
+index 0000000..8f3bf99
+--- /dev/null
++++ b/include/linux/squashfs_fs_sb.h
+@@ -0,0 +1,74 @@
++#ifndef SQUASHFS_FS_SB
++#define SQUASHFS_FS_SB
++/*
++ * Squashfs
++ *
++ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
++ * Phillip Lougher <phillip@lougher.org.uk>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2,
++ * 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ * squashfs_fs_sb.h
++ */
++
++#include <linux/squashfs_fs.h>
++
++struct squashfs_cache {
++ long long block;
++ int length;
++ long long next_index;
++ char *data;
++};
++
++struct squashfs_fragment_cache {
++ long long block;
++ int length;
++ unsigned int locked;
++ char *data;
++};
++
++struct squashfs_sb_info {
++ struct squashfs_super_block sblk;
++ int devblksize;
++ int devblksize_log2;
++ int swap;
++ struct squashfs_cache *block_cache;
++ struct squashfs_fragment_cache *fragment;
++ int next_cache;
++ int next_fragment;
++ int next_meta_index;
++ unsigned int *uid;
++ unsigned int *guid;
++ long long *fragment_index;
++ unsigned int *fragment_index_2;
++ char *read_page;
++ struct mutex read_data_mutex;
++ struct mutex read_page_mutex;
++ struct mutex block_cache_mutex;
++ struct mutex fragment_mutex;
++ struct mutex meta_index_mutex;
++ wait_queue_head_t waitq;
++ wait_queue_head_t fragment_wait_queue;
++ struct meta_index *meta_index;
++ z_stream stream;
++ long long *inode_lookup_table;
++ int (*read_inode)(struct inode *i, squashfs_inode_t \
++ inode);
++ long long (*read_blocklist)(struct inode *inode, int \
++ index, int readahead_blks, char *block_list, \
++ unsigned short **block_p, unsigned int *bsize);
++ int (*read_fragment_index_table)(struct super_block *s);
++};
++#endif
+diff --git a/init/Kconfig b/init/Kconfig
+index b170aa1..bcfc3b4 100644
+--- a/init/Kconfig
++++ b/init/Kconfig
+@@ -244,23 +244,21 @@ config AUDITSYSCALL
+ ensure that INOTIFY is configured.
+
+ config IKCONFIG
+- tristate "Kernel .config support"
++ tristate "Kernel .miniconfig support"
+ ---help---
+- This option enables the complete Linux kernel ".config" file
++ This option enables the mini Linux kernel ".miniconfig" file
+ contents to be saved in the kernel. It provides documentation
+ of which kernel options are used in a running kernel or in an
+- on-disk kernel. This information can be extracted from the kernel
+- image file with the script scripts/extract-ikconfig and used as
+- input to rebuild the current kernel or to build another kernel.
+- It can also be extracted from a running kernel by reading
+- /proc/config.gz if enabled (below).
++ on-disk kernel.
++ It can be extracted from a running kernel by reading
++ /proc/miniconfig.gz if enabled (below).
+
+ config IKCONFIG_PROC
+- bool "Enable access to .config through /proc/config.gz"
++ bool "Enable access to .miniconfig through /proc/miniconfig.gz"
+ depends on IKCONFIG && PROC_FS
+ ---help---
+ This option enables access to the kernel configuration file
+- through /proc/config.gz.
++ through /proc/miniconfig.gz.
+
+ config CPUSETS
+ bool "Cpuset support"
+diff --git a/init/LzmaDecode.c b/init/LzmaDecode.c
+new file mode 100644
+index 0000000..21bf40b
+--- /dev/null
++++ b/init/LzmaDecode.c
+@@ -0,0 +1,588 @@
++/*
++ LzmaDecode.c
++ LZMA Decoder (optimized for Speed version)
++
++ LZMA SDK 4.22 Copyright (c) 1999-2005 Igor Pavlov (2005-06-10)
++ http://www.7-zip.org/
++
++ LZMA SDK is licensed under two licenses:
++ 1) GNU Lesser General Public License (GNU LGPL)
++ 2) Common Public License (CPL)
++ It means that you can select one of these two licenses and
++ follow rules of that license.
++
++ SPECIAL EXCEPTION:
++ Igor Pavlov, as the author of this Code, expressly permits you to
++ statically or dynamically link your Code (or bind by name) to the
++ interfaces of this file without subjecting your linked Code to the
++ terms of the CPL or GNU LGPL. Any modifications or additions
++ to this file, however, are subject to the LGPL or CPL terms.
++*/
++
++#include "LzmaDecode.h"
++
++#ifndef Byte
++#define Byte unsigned char
++#endif
++
++#define kNumTopBits 24
++#define kTopValue ((UInt32)1 << kNumTopBits)
++
++#define kNumBitModelTotalBits 11
++#define kBitModelTotal (1 << kNumBitModelTotalBits)
++#define kNumMoveBits 5
++
++#define RC_READ_BYTE (*Buffer++)
++
++#define RC_INIT2 Code = 0; Range = 0xFFFFFFFF; \
++ { int i; for(i = 0; i < 5; i++) { RC_TEST; Code = (Code << 8) | RC_READ_BYTE; }}
++
++#ifdef _LZMA_IN_CB
++
++#define RC_TEST { if (Buffer == BufferLim) \
++ { SizeT size; int result = InCallback->Read(InCallback, &Buffer, &size); if (result != LZMA_RESULT_OK) return result; \
++ BufferLim = Buffer + size; if (size == 0) return LZMA_RESULT_DATA_ERROR; }}
++
++#define RC_INIT Buffer = BufferLim = 0; RC_INIT2
++
++#else
++
++#define RC_TEST { if (Buffer == BufferLim) return LZMA_RESULT_DATA_ERROR; }
++
++#define RC_INIT(buffer, bufferSize) Buffer = buffer; BufferLim = buffer + bufferSize; RC_INIT2
++
++#endif
++
++#define RC_NORMALIZE if (Range < kTopValue) { RC_TEST; Range <<= 8; Code = (Code << 8) | RC_READ_BYTE; }
++
++#define IfBit0(p) RC_NORMALIZE; bound = (Range >> kNumBitModelTotalBits) * *(p); if (Code < bound)
++#define UpdateBit0(p) Range = bound; *(p) += (kBitModelTotal - *(p)) >> kNumMoveBits;
++#define UpdateBit1(p) Range -= bound; Code -= bound; *(p) -= (*(p)) >> kNumMoveBits;
++
++#define RC_GET_BIT2(p, mi, A0, A1) IfBit0(p) \
++ { UpdateBit0(p); mi <<= 1; A0; } else \
++ { UpdateBit1(p); mi = (mi + mi) + 1; A1; }
++
++#define RC_GET_BIT(p, mi) RC_GET_BIT2(p, mi, ; , ;)
++
++#define RangeDecoderBitTreeDecode(probs, numLevels, res) \
++ { int i = numLevels; res = 1; \
++ do { CProb *p = probs + res; RC_GET_BIT(p, res) } while(--i != 0); \
++ res -= (1 << numLevels); }
++
++
++#define kNumPosBitsMax 4
++#define kNumPosStatesMax (1 << kNumPosBitsMax)
++
++#define kLenNumLowBits 3
++#define kLenNumLowSymbols (1 << kLenNumLowBits)
++#define kLenNumMidBits 3
++#define kLenNumMidSymbols (1 << kLenNumMidBits)
++#define kLenNumHighBits 8
++#define kLenNumHighSymbols (1 << kLenNumHighBits)
++
++#define LenChoice 0
++#define LenChoice2 (LenChoice + 1)
++#define LenLow (LenChoice2 + 1)
++#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits))
++#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits))
++#define kNumLenProbs (LenHigh + kLenNumHighSymbols)
++
++
++#define kNumStates 12
++#define kNumLitStates 7
++
++#define kStartPosModelIndex 4
++#define kEndPosModelIndex 14
++#define kNumFullDistances (1 << (kEndPosModelIndex >> 1))
++
++#define kNumPosSlotBits 6
++#define kNumLenToPosStates 4
++
++#define kNumAlignBits 4
++#define kAlignTableSize (1 << kNumAlignBits)
++
++#define kMatchMinLen 2
++
++#define IsMatch 0
++#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax))
++#define IsRepG0 (IsRep + kNumStates)
++#define IsRepG1 (IsRepG0 + kNumStates)
++#define IsRepG2 (IsRepG1 + kNumStates)
++#define IsRep0Long (IsRepG2 + kNumStates)
++#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax))
++#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits))
++#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex)
++#define LenCoder (Align + kAlignTableSize)
++#define RepLenCoder (LenCoder + kNumLenProbs)
++#define Literal (RepLenCoder + kNumLenProbs)
++
++#if Literal != LZMA_BASE_SIZE
++StopCompilingDueBUG
++#endif
++
++int LzmaDecodeProperties(CLzmaProperties *propsRes, const unsigned char *propsData, int size)
++{
++ unsigned char prop0;
++ if (size < LZMA_PROPERTIES_SIZE)
++ return LZMA_RESULT_DATA_ERROR;
++ prop0 = propsData[0];
++ if (prop0 >= (9 * 5 * 5))
++ return LZMA_RESULT_DATA_ERROR;
++ {
++ for (propsRes->pb = 0; prop0 >= (9 * 5); propsRes->pb++, prop0 -= (9 * 5));
++ for (propsRes->lp = 0; prop0 >= 9; propsRes->lp++, prop0 -= 9);
++ propsRes->lc = prop0;
++ /*
++ unsigned char remainder = (unsigned char)(prop0 / 9);
++ propsRes->lc = prop0 % 9;
++ propsRes->pb = remainder / 5;
++ propsRes->lp = remainder % 5;
++ */
++ }
++
++ #ifdef _LZMA_OUT_READ
++ {
++ int i;
++ propsRes->DictionarySize = 0;
++ for (i = 0; i < 4; i++)
++ propsRes->DictionarySize += (UInt32)(propsData[1 + i]) << (i * 8);
++ if (propsRes->DictionarySize == 0)
++ propsRes->DictionarySize = 1;
++ }
++ #endif
++ return LZMA_RESULT_OK;
++}
++
++#define kLzmaStreamWasFinishedId (-1)
++
++int LzmaDecode(CLzmaDecoderState *vs,
++ #ifdef _LZMA_IN_CB
++ ILzmaInCallback *InCallback,
++ #else
++ const unsigned char *inStream, SizeT inSize, SizeT *inSizeProcessed,
++ #endif
++ unsigned char *outStream, SizeT outSize, SizeT *outSizeProcessed)
++{
++ CProb *p = vs->Probs;
++ SizeT nowPos = 0;
++ Byte previousByte = 0;
++ UInt32 posStateMask = (1 << (vs->Properties.pb)) - 1;
++ UInt32 literalPosMask = (1 << (vs->Properties.lp)) - 1;
++ int lc = vs->Properties.lc;
++
++ #ifdef _LZMA_OUT_READ
++
++ UInt32 Range = vs->Range;
++ UInt32 Code = vs->Code;
++ #ifdef _LZMA_IN_CB
++ const Byte *Buffer = vs->Buffer;
++ const Byte *BufferLim = vs->BufferLim;
++ #else
++ const Byte *Buffer = inStream;
++ const Byte *BufferLim = inStream + inSize;
++ #endif
++ int state = vs->State;
++ UInt32 rep0 = vs->Reps[0], rep1 = vs->Reps[1], rep2 = vs->Reps[2], rep3 = vs->Reps[3];
++ int len = vs->RemainLen;
++ UInt32 globalPos = vs->GlobalPos;
++ UInt32 distanceLimit = vs->DistanceLimit;
++
++ Byte *dictionary = vs->Dictionary;
++ UInt32 dictionarySize = vs->Properties.DictionarySize;
++ UInt32 dictionaryPos = vs->DictionaryPos;
++
++ Byte tempDictionary[4];
++
++ #ifndef _LZMA_IN_CB
++ *inSizeProcessed = 0;
++ #endif
++ *outSizeProcessed = 0;
++ if (len == kLzmaStreamWasFinishedId)
++ return LZMA_RESULT_OK;
++
++ if (dictionarySize == 0)
++ {
++ dictionary = tempDictionary;
++ dictionarySize = 1;
++ tempDictionary[0] = vs->TempDictionary[0];
++ }
++
++ if (len == kLzmaNeedInitId)
++ {
++ {
++ UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + vs->Properties.lp));
++ UInt32 i;
++ for (i = 0; i < numProbs; i++)
++ p[i] = kBitModelTotal >> 1;
++ rep0 = rep1 = rep2 = rep3 = 1;
++ state = 0;
++ globalPos = 0;
++ distanceLimit = 0;
++ dictionaryPos = 0;
++ dictionary[dictionarySize - 1] = 0;
++ #ifdef _LZMA_IN_CB
++ RC_INIT;
++ #else
++ RC_INIT(inStream, inSize);
++ #endif
++ }
++ len = 0;
++ }
++ while(len != 0 && nowPos < outSize)
++ {
++ UInt32 pos = dictionaryPos - rep0;
++ if (pos >= dictionarySize)
++ pos += dictionarySize;
++ outStream[nowPos++] = dictionary[dictionaryPos] = dictionary[pos];
++ if (++dictionaryPos == dictionarySize)
++ dictionaryPos = 0;
++ len--;
++ }
++ if (dictionaryPos == 0)
++ previousByte = dictionary[dictionarySize - 1];
++ else
++ previousByte = dictionary[dictionaryPos - 1];
++
++ #else /* if !_LZMA_OUT_READ */
++
++ int state = 0;
++ UInt32 rep0 = 1, rep1 = 1, rep2 = 1, rep3 = 1;
++ int len = 0;
++ const Byte *Buffer;
++ const Byte *BufferLim;
++ UInt32 Range;
++ UInt32 Code;
++
++ #ifndef _LZMA_IN_CB
++ *inSizeProcessed = 0;
++ #endif
++ *outSizeProcessed = 0;
++
++ {
++ UInt32 i;
++ UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + vs->Properties.lp));
++ for (i = 0; i < numProbs; i++)
++ p[i] = kBitModelTotal >> 1;
++ }
++
++ #ifdef _LZMA_IN_CB
++ RC_INIT;
++ #else
++ RC_INIT(inStream, inSize);
++ #endif
++
++ #endif /* _LZMA_OUT_READ */
++
++ while(nowPos < outSize)
++ {
++ CProb *prob;
++ UInt32 bound;
++ int posState = (int)(
++ (nowPos
++ #ifdef _LZMA_OUT_READ
++ + globalPos
++ #endif
++ )
++ & posStateMask);
++
++ prob = p + IsMatch + (state << kNumPosBitsMax) + posState;
++ IfBit0(prob)
++ {
++ int symbol = 1;
++ UpdateBit0(prob)
++ prob = p + Literal + (LZMA_LIT_SIZE *
++ (((
++ (nowPos
++ #ifdef _LZMA_OUT_READ
++ + globalPos
++ #endif
++ )
++ & literalPosMask) << lc) + (previousByte >> (8 - lc))));
++
++ if (state >= kNumLitStates)
++ {
++ int matchByte;
++ #ifdef _LZMA_OUT_READ
++ UInt32 pos = dictionaryPos - rep0;
++ if (pos >= dictionarySize)
++ pos += dictionarySize;
++ matchByte = dictionary[pos];
++ #else
++ matchByte = outStream[nowPos - rep0];
++ #endif
++ do
++ {
++ int bit;
++ CProb *probLit;
++ matchByte <<= 1;
++ bit = (matchByte & 0x100);
++ probLit = prob + 0x100 + bit + symbol;
++ RC_GET_BIT2(probLit, symbol, if (bit != 0) break, if (bit == 0) break)
++ }
++ while (symbol < 0x100);
++ }
++ while (symbol < 0x100)
++ {
++ CProb *probLit = prob + symbol;
++ RC_GET_BIT(probLit, symbol)
++ }
++ previousByte = (Byte)symbol;
++
++ outStream[nowPos++] = previousByte;
++ #ifdef _LZMA_OUT_READ
++ if (distanceLimit < dictionarySize)
++ distanceLimit++;
++
++ dictionary[dictionaryPos] = previousByte;
++ if (++dictionaryPos == dictionarySize)
++ dictionaryPos = 0;
++ #endif
++ if (state < 4) state = 0;
++ else if (state < 10) state -= 3;
++ else state -= 6;
++ }
++ else
++ {
++ UpdateBit1(prob);
++ prob = p + IsRep + state;
++ IfBit0(prob)
++ {
++ UpdateBit0(prob);
++ rep3 = rep2;
++ rep2 = rep1;
++ rep1 = rep0;
++ state = state < kNumLitStates ? 0 : 3;
++ prob = p + LenCoder;
++ }
++ else
++ {
++ UpdateBit1(prob);
++ prob = p + IsRepG0 + state;
++ IfBit0(prob)
++ {
++ UpdateBit0(prob);
++ prob = p + IsRep0Long + (state << kNumPosBitsMax) + posState;
++ IfBit0(prob)
++ {
++ #ifdef _LZMA_OUT_READ
++ UInt32 pos;
++ #endif
++ UpdateBit0(prob);
++
++ #ifdef _LZMA_OUT_READ
++ if (distanceLimit == 0)
++ #else
++ if (nowPos == 0)
++ #endif
++ return LZMA_RESULT_DATA_ERROR;
++
++ state = state < kNumLitStates ? 9 : 11;
++ #ifdef _LZMA_OUT_READ
++ pos = dictionaryPos - rep0;
++ if (pos >= dictionarySize)
++ pos += dictionarySize;
++ previousByte = dictionary[pos];
++ dictionary[dictionaryPos] = previousByte;
++ if (++dictionaryPos == dictionarySize)
++ dictionaryPos = 0;
++ #else
++ previousByte = outStream[nowPos - rep0];
++ #endif
++ outStream[nowPos++] = previousByte;
++ #ifdef _LZMA_OUT_READ
++ if (distanceLimit < dictionarySize)
++ distanceLimit++;
++ #endif
++
++ continue;
++ }
++ else
++ {
++ UpdateBit1(prob);
++ }
++ }
++ else
++ {
++ UInt32 distance;
++ UpdateBit1(prob);
++ prob = p + IsRepG1 + state;
++ IfBit0(prob)
++ {
++ UpdateBit0(prob);
++ distance = rep1;
++ }
++ else
++ {
++ UpdateBit1(prob);
++ prob = p + IsRepG2 + state;
++ IfBit0(prob)
++ {
++ UpdateBit0(prob);
++ distance = rep2;
++ }
++ else
++ {
++ UpdateBit1(prob);
++ distance = rep3;
++ rep3 = rep2;
++ }
++ rep2 = rep1;
++ }
++ rep1 = rep0;
++ rep0 = distance;
++ }
++ state = state < kNumLitStates ? 8 : 11;
++ prob = p + RepLenCoder;
++ }
++ {
++ int numBits, offset;
++ CProb *probLen = prob + LenChoice;
++ IfBit0(probLen)
++ {
++ UpdateBit0(probLen);
++ probLen = prob + LenLow + (posState << kLenNumLowBits);
++ offset = 0;
++ numBits = kLenNumLowBits;
++ }
++ else
++ {
++ UpdateBit1(probLen);
++ probLen = prob + LenChoice2;
++ IfBit0(probLen)
++ {
++ UpdateBit0(probLen);
++ probLen = prob + LenMid + (posState << kLenNumMidBits);
++ offset = kLenNumLowSymbols;
++ numBits = kLenNumMidBits;
++ }
++ else
++ {
++ UpdateBit1(probLen);
++ probLen = prob + LenHigh;
++ offset = kLenNumLowSymbols + kLenNumMidSymbols;
++ numBits = kLenNumHighBits;
++ }
++ }
++ RangeDecoderBitTreeDecode(probLen, numBits, len);
++ len += offset;
++ }
++
++ if (state < 4)
++ {
++ int posSlot;
++ state += kNumLitStates;
++ prob = p + PosSlot +
++ ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) <<
++ kNumPosSlotBits);
++ RangeDecoderBitTreeDecode(prob, kNumPosSlotBits, posSlot);
++ if (posSlot >= kStartPosModelIndex)
++ {
++ int numDirectBits = ((posSlot >> 1) - 1);
++ rep0 = (2 | ((UInt32)posSlot & 1));
++ if (posSlot < kEndPosModelIndex)
++ {
++ rep0 <<= numDirectBits;
++ prob = p + SpecPos + rep0 - posSlot - 1;
++ }
++ else
++ {
++ numDirectBits -= kNumAlignBits;
++ do
++ {
++ RC_NORMALIZE
++ Range >>= 1;
++ rep0 <<= 1;
++ if (Code >= Range)
++ {
++ Code -= Range;
++ rep0 |= 1;
++ }
++ }
++ while (--numDirectBits != 0);
++ prob = p + Align;
++ rep0 <<= kNumAlignBits;
++ numDirectBits = kNumAlignBits;
++ }
++ {
++ int i = 1;
++ int mi = 1;
++ do
++ {
++ CProb *prob3 = prob + mi;
++ RC_GET_BIT2(prob3, mi, ; , rep0 |= i);
++ i <<= 1;
++ }
++ while(--numDirectBits != 0);
++ }
++ }
++ else
++ rep0 = posSlot;
++ if (++rep0 == (UInt32)(0))
++ {
++ /* it's for stream version */
++ len = kLzmaStreamWasFinishedId;
++ break;
++ }
++ }
++
++ len += kMatchMinLen;
++ #ifdef _LZMA_OUT_READ
++ if (rep0 > distanceLimit)
++ #else
++ if (rep0 > nowPos)
++ #endif
++ return LZMA_RESULT_DATA_ERROR;
++
++ #ifdef _LZMA_OUT_READ
++ if (dictionarySize - distanceLimit > (UInt32)len)
++ distanceLimit += len;
++ else
++ distanceLimit = dictionarySize;
++ #endif
++
++ do
++ {
++ #ifdef _LZMA_OUT_READ
++ UInt32 pos = dictionaryPos - rep0;
++ if (pos >= dictionarySize)
++ pos += dictionarySize;
++ previousByte = dictionary[pos];
++ dictionary[dictionaryPos] = previousByte;
++ if (++dictionaryPos == dictionarySize)
++ dictionaryPos = 0;
++ #else
++ previousByte = outStream[nowPos - rep0];
++ #endif
++ len--;
++ outStream[nowPos++] = previousByte;
++ }
++ while(len != 0 && nowPos < outSize);
++ }
++ }
++ RC_NORMALIZE;
++
++ #ifdef _LZMA_OUT_READ
++ vs->Range = Range;
++ vs->Code = Code;
++ vs->DictionaryPos = dictionaryPos;
++ vs->GlobalPos = globalPos + (UInt32)nowPos;
++ vs->DistanceLimit = distanceLimit;
++ vs->Reps[0] = rep0;
++ vs->Reps[1] = rep1;
++ vs->Reps[2] = rep2;
++ vs->Reps[3] = rep3;
++ vs->State = state;
++ vs->RemainLen = len;
++ vs->TempDictionary[0] = tempDictionary[0];
++ #endif
++
++ #ifdef _LZMA_IN_CB
++ vs->Buffer = Buffer;
++ vs->BufferLim = BufferLim;
++ #else
++ *inSizeProcessed = (SizeT)(Buffer - inStream);
++ #endif
++ *outSizeProcessed = nowPos;
++ return LZMA_RESULT_OK;
++}
+diff --git a/init/LzmaDecode.h b/init/LzmaDecode.h
+new file mode 100644
+index 0000000..213062a
+--- /dev/null
++++ b/init/LzmaDecode.h
+@@ -0,0 +1,131 @@
++/*
++ LzmaDecode.h
++ LZMA Decoder interface
++
++ LZMA SDK 4.21 Copyright (c) 1999-2005 Igor Pavlov (2005-06-08)
++ http://www.7-zip.org/
++
++ LZMA SDK is licensed under two licenses:
++ 1) GNU Lesser General Public License (GNU LGPL)
++ 2) Common Public License (CPL)
++ It means that you can select one of these two licenses and
++ follow rules of that license.
++
++ SPECIAL EXCEPTION:
++ Igor Pavlov, as the author of this code, expressly permits you to
++ statically or dynamically link your code (or bind by name) to the
++ interfaces of this file without subjecting your linked code to the
++ terms of the CPL or GNU LGPL. Any modifications or additions
++ to this file, however, are subject to the LGPL or CPL terms.
++*/
++
++#ifndef __LZMADECODE_H
++#define __LZMADECODE_H
++
++/* #define _LZMA_IN_CB */
++/* Use callback for input data */
++
++/* #define _LZMA_OUT_READ */
++/* Use read function for output data */
++
++/* #define _LZMA_PROB32 */
++/* It can increase speed on some 32-bit CPUs,
++ but memory usage will be doubled in that case */
++
++/* #define _LZMA_LOC_OPT */
++/* Enable local speed optimizations inside code */
++
++/* #define _LZMA_SYSTEM_SIZE_T */
++/* Use system's size_t. You can use it to enable 64-bit sizes supporting*/
++
++#ifndef UInt32
++#ifdef _LZMA_UINT32_IS_ULONG
++#define UInt32 unsigned long
++#else
++#define UInt32 unsigned int
++#endif
++#endif
++
++#ifndef SizeT
++#ifdef _LZMA_SYSTEM_SIZE_T
++#include <stddef.h>
++#define SizeT size_t
++#else
++#define SizeT UInt32
++#endif
++#endif
++
++#ifdef _LZMA_PROB32
++#define CProb UInt32
++#else
++#define CProb unsigned short
++#endif
++
++#define LZMA_RESULT_OK 0
++#define LZMA_RESULT_DATA_ERROR 1
++
++#ifdef _LZMA_IN_CB
++typedef struct _ILzmaInCallback
++{
++ int (*Read)(void *object, const unsigned char **buffer, SizeT *bufferSize);
++} ILzmaInCallback;
++#endif
++
++#define LZMA_BASE_SIZE 1846
++#define LZMA_LIT_SIZE 768
++
++#define LZMA_PROPERTIES_SIZE 5
++
++typedef struct _CLzmaProperties
++{
++ int lc;
++ int lp;
++ int pb;
++ #ifdef _LZMA_OUT_READ
++ UInt32 DictionarySize;
++ #endif
++}CLzmaProperties;
++
++int LzmaDecodeProperties(CLzmaProperties *propsRes, const unsigned char *propsData, int size);
++
++#define LzmaGetNumProbs(Properties) (LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((Properties)->lc + (Properties)->lp)))
++
++#define kLzmaNeedInitId (-2)
++
++typedef struct _CLzmaDecoderState
++{
++ CLzmaProperties Properties;
++ CProb *Probs;
++
++ #ifdef _LZMA_IN_CB
++ const unsigned char *Buffer;
++ const unsigned char *BufferLim;
++ #endif
++
++ #ifdef _LZMA_OUT_READ
++ unsigned char *Dictionary;
++ UInt32 Range;
++ UInt32 Code;
++ UInt32 DictionaryPos;
++ UInt32 GlobalPos;
++ UInt32 DistanceLimit;
++ UInt32 Reps[4];
++ int State;
++ int RemainLen;
++ unsigned char TempDictionary[4];
++ #endif
++} CLzmaDecoderState;
++
++#ifdef _LZMA_OUT_READ
++#define LzmaDecoderInit(vs) { (vs)->RemainLen = kLzmaNeedInitId; }
++#endif
++
++int LzmaDecode(CLzmaDecoderState *vs,
++ #ifdef _LZMA_IN_CB
++ ILzmaInCallback *inCallback,
++ #else
++ const unsigned char *inStream, SizeT inSize, SizeT *inSizeProcessed,
++ #endif
++ unsigned char *outStream, SizeT outSize, SizeT *outSizeProcessed);
++
++#endif
+diff --git a/init/do_mounts_rd.c b/init/do_mounts_rd.c
+index ed652f4..5fd1ec5 100644
+--- a/init/do_mounts_rd.c
++++ b/init/do_mounts_rd.c
+@@ -5,7 +5,9 @@
+ #include <linux/ext2_fs.h>
+ #include <linux/romfs_fs.h>
+ #include <linux/cramfs_fs.h>
++#include <linux/squashfs_fs.h>
+ #include <linux/initrd.h>
++#include <linux/vmalloc.h>
+ #include <linux/string.h>
+
+ #include "do_mounts.h"
+@@ -31,6 +33,9 @@ static int __init ramdisk_start_setup(char *str)
+ __setup("ramdisk_start=", ramdisk_start_setup);
+
+ static int __init crd_load(int in_fd, int out_fd);
++#ifdef CONFIG_LZMA_INITRD
++static int __init lzma_rd_load(int in_fd, int out_fd);
++#endif
+
+ /*
+ * This routine tries to find a RAM disk image to load, and returns the
+@@ -39,6 +44,7 @@ static int __init crd_load(int in_fd, int out_fd);
+ * numbers could not be found.
+ *
+ * We currently check for the following magic numbers:
++ * squashfs
+ * minix
+ * ext2
+ * romfs
+@@ -53,6 +59,7 @@ identify_ramdisk_image(int fd, int start_block)
+ struct ext2_super_block *ext2sb;
+ struct romfs_super_block *romfsb;
+ struct cramfs_super *cramfsb;
++ struct squashfs_super_block *squashfsb;
+ int nblocks = -1;
+ unsigned char *buf;
+
+@@ -64,6 +71,7 @@ identify_ramdisk_image(int fd, int start_block)
+ ext2sb = (struct ext2_super_block *) buf;
+ romfsb = (struct romfs_super_block *) buf;
+ cramfsb = (struct cramfs_super *) buf;
++ squashfsb = (struct squashfs_super_block *) buf;
+ memset(buf, 0xe5, size);
+
+ /*
+@@ -82,6 +90,17 @@ identify_ramdisk_image(int fd, int start_block)
+ nblocks = 0;
+ goto done;
+ }
++ /*
++ * handle lzma compressed initrd, returns nblocks=1 as indication
++ */
++ if( buf[0] < 9 * 5 * 5 && buf[9] == 0 && buf[10] == 0 && buf[11] == 0
++ && buf[12] == 0 )
++ {
++ printk( KERN_NOTICE "RAMDISK: LZMA image found at block %d\n",
++ start_block);
++ nblocks = 1; // just a convenient return flag
++ goto done;
++ }
+
+ /* romfs is at block zero too */
+ if (romfsb->word0 == ROMSB_WORD0 &&
+@@ -101,6 +120,18 @@ identify_ramdisk_image(int fd, int start_block)
+ goto done;
+ }
+
++ /* squashfs is at block zero too */
++ if (squashfsb->s_magic == SQUASHFS_MAGIC) {
++ printk(KERN_NOTICE
++ "RAMDISK: squashfs filesystem found at block %d\n",
++ start_block);
++ if (squashfsb->s_major < 3)
++ nblocks = (squashfsb->bytes_used_2+BLOCK_SIZE-1)>>BLOCK_SIZE_BITS;
++ else
++ nblocks = (squashfsb->bytes_used+BLOCK_SIZE-1)>>BLOCK_SIZE_BITS;
++ goto done;
++ }
++
+ /*
+ * Read block 1 to test for minix and ext2 superblock
+ */
+@@ -172,7 +203,22 @@ int __init rd_load_image(char *from)
+ #endif
+ goto done;
+ }
+-
++#ifdef CONFIG_LZMA_INITRD
++ /*
++ * handle lzma compressed image
++ */
++ if ( nblocks == 1 )
++ {
++ nblocks = 0;
++ if ( lzma_rd_load(in_fd, out_fd) == 0 )
++ {
++ printk("\nLZMA initrd loaded successfully\n");
++ goto successful_load;
++ }
++ printk(KERN_NOTICE "LZMA initrd is not in the correct format\n");
++ goto done;
++ }
++#endif
+ /*
+ * NOTE NOTE: nblocks is not actually blocks but
+ * the number of kibibytes of data to load into a ramdisk.
+@@ -393,6 +439,134 @@ static void __init error(char *x)
+ unzip_error = 1;
+ }
+
++#ifdef CONFIG_LZMA_INITRD
++#define _LZMA_IN_CB
++#define _LZMA_OUT_READ
++#include "LzmaDecode.h"
++#include "LzmaDecode.c"
++
++static int read_byte(void *object, const unsigned char **buffer, SizeT *bufferSize);
++
++/*
++ * Do the lzma decompression
++ */
++static int __init lzma_rd_load(int in_fd, int out_fd)
++{
++ unsigned int i;
++ CLzmaDecoderState state;
++ unsigned char* outputbuffer;
++ unsigned int uncompressedSize = 0;
++ unsigned char* p;
++ unsigned int kBlockSize = 0x10000;
++ unsigned int nowPos = 0;
++ unsigned int outsizeProcessed = 0;
++ int res;
++ ILzmaInCallback callback;
++
++ insize = 0; /* valid bytes in inbuf */
++ inptr = 0; /* index of next byte to be processed in inbuf */
++ exit_code = 0;
++ crd_infd = in_fd;
++ inbuf = kmalloc(INBUFSIZ, GFP_KERNEL);
++ if (inbuf == 0)
++ {
++ printk(KERN_ERR "RAMDISK: Couldn't allocate lzma input buffer\n");
++ return -1;
++ }
++
++ callback.Read = read_byte;
++
++ /* lzma args */
++ i = get_byte();
++ state.Properties.lc = i % 9, i = i / 9;
++ state.Properties.lp = i % 5, state.Properties.pb = i / 5;
++
++ /* read dictionary size */
++ p = (char*)&state.Properties.DictionarySize;
++ for (i = 0; i < 4; i++)
++ *p++ = get_byte();
++
++ /* get uncompressedSize */
++ p= (char*)&uncompressedSize;
++ for (i = 0; i < 4; i++)
++ *p++ = get_byte();
++
++ /* skip big file */
++ for (i = 0; i < 4; i++)
++ get_byte();
++
++ printk( KERN_NOTICE "RAMDISK: LZMA lc=%d,lp=%d,pb=%d,dictSize=%d,origSize=%d\n",
++ state.Properties.lc, state.Properties.lp, state.Properties.pb, state.Properties.DictionarySize, uncompressedSize);
++ outputbuffer = kmalloc(kBlockSize, GFP_KERNEL);
++ if (outputbuffer == 0) {
++ printk(KERN_ERR "RAMDISK: Couldn't allocate lzma output buffer\n");
++ return -1;
++ }
++
++ state.Probs = (CProb*)kmalloc( LzmaGetNumProbs(&state.Properties)*sizeof(CProb), GFP_KERNEL);
++ if ( state.Probs == 0) {
++ printk(KERN_ERR "RAMDISK: Couldn't allocate lzma workspace\n");
++ return -1;
++ }
++
++#ifdef CONFIG_LZMA_INITRD_KMALLOC_ONLY
++ state.Dictionary = kmalloc( state.Properties.DictionarySize, GFP_KERNEL);
++#else
++ state.Dictionary = vmalloc( state.Properties.DictionarySize);
++#endif
++ if ( state.Dictionary == 0) {
++ printk(KERN_ERR "RAMDISK: Couldn't allocate lzma dictionary\n");
++ return -1;
++ }
++
++ printk( KERN_NOTICE "LZMA initrd by Ming-Ching Tiew <mctiew@yahoo.com> " );
++
++ LzmaDecoderInit( &state );
++
++ for( nowPos =0; nowPos < uncompressedSize ; )
++ {
++ UInt32 blockSize = uncompressedSize - nowPos;
++ if( blockSize > kBlockSize)
++ blockSize = kBlockSize;
++ res = LzmaDecode( &state, &callback, outputbuffer, blockSize, &outsizeProcessed);
++ if( res != 0 ) {
++ printk( KERN_ERR "RAMDISK: Lzma decode failure\n");
++ return -1;
++ }
++ if( outsizeProcessed == 0 )
++ {
++ uncompressedSize = nowPos;
++ printk( KERN_NOTICE "RAMDISK nowPos=%d, uncompressedSize=%d\n",
++ nowPos, uncompressedSize );
++ break;
++ }
++ sys_write(out_fd, outputbuffer, outsizeProcessed );
++ nowPos += outsizeProcessed;
++ printk( ".");
++ }
++
++#ifdef CONFIG_LZMA_INITRD_KMALLOC_ONLY
++ kfree(state.Dictionary);
++#else
++ vfree(state.Dictionary);
++#endif
++ kfree(inbuf);
++ kfree(outputbuffer);
++ kfree(state.Probs);
++ return 0;
++}
++
++static int read_byte(void *object, const unsigned char **buffer, SizeT *bufferSize)
++{
++ static unsigned char val;
++ *bufferSize = 1;
++ val = get_byte();
++ *buffer = &val;
++ return LZMA_RESULT_OK;
++}
++
++#endif /*CONFIG_LZMA_INITRD*/
++
+ static int __init crd_load(int in_fd, int out_fd)
+ {
+ int result;
+diff --git a/init/initramfs.c b/init/initramfs.c
+index 00eff7a..30d32a2 100644
+--- a/init/initramfs.c
++++ b/init/initramfs.c
+@@ -6,6 +6,7 @@
+ #include <linux/delay.h>
+ #include <linux/string.h>
+ #include <linux/syscalls.h>
++#include <linux/vmalloc.h>
+
+ static __initdata char *message;
+ static void __init error(char *x)
+@@ -441,6 +442,118 @@ static void __init flush_window(void)
+ outcnt = 0;
+ }
+
++#ifdef CONFIG_LZMA_INITRAM_FS
++#define _LZMA_IN_CB
++#define _LZMA_OUT_READ
++#include "LzmaDecode.h"
++#ifndef CONFIG_LZMA_INITRD
++ #include "LzmaDecode.c"
++#endif
++static int read_byte(void *object, const unsigned char **buffer, SizeT *bufferSize)
++{
++ static unsigned char val;
++ *bufferSize = 1;
++ val = get_byte();
++ *buffer = &val;
++ return LZMA_RESULT_OK;
++}
++
++static int __init lzma_unzip(void)
++{
++ unsigned int i;
++ CLzmaDecoderState state;
++ unsigned char* outputbuffer;
++ unsigned int uncompressedSize = 0;
++ unsigned char* p;
++ unsigned int kBlockSize = 0x10000;
++ unsigned int nowPos = 0;
++ unsigned int outsizeProcessed = 0;
++ int res;
++ ILzmaInCallback callback;
++
++ callback.Read = read_byte;
++
++ // lzma args
++ i = get_byte();
++ state.Properties.lc = i % 9, i = i / 9;
++ state.Properties.lp = i % 5, state.Properties.pb = i / 5;
++
++ // read dictionary size
++ p = (char*)&state.Properties.DictionarySize;
++ for (i = 0; i < 4; i++)
++ *p++ = get_byte();
++
++ // get uncompressedSize
++ p= (char*)&uncompressedSize;
++ for (i = 0; i < 4; i++)
++ *p++ = get_byte();
++
++ // skip big file
++ for (i = 0; i < 4; i++)
++ get_byte();
++
++ printk( KERN_NOTICE "initramfs: LZMA lc=%d,lp=%d,pb=%d,dictSize=%d,origSize=%d\n",
++ state.Properties.lc,state.Properties.lp,state.Properties.pb,state.Properties.DictionarySize, uncompressedSize);
++ outputbuffer = kmalloc(kBlockSize, GFP_KERNEL);
++ if (outputbuffer == 0) {
++ printk(KERN_ERR "initramfs: Couldn't allocate lzma output buffer\n");
++ return -1;
++ }
++
++ state.Probs = (CProb*) kmalloc( LzmaGetNumProbs(&state.Properties)*sizeof(CProb), GFP_KERNEL);
++ if ( state.Probs == 0) {
++ printk(KERN_ERR "initramfs: Couldn't allocate lzma workspace\n");
++ return -1;
++ }
++
++#ifdef CONFIG_LZMA_INITRAM_FS_KMALLOC_ONLY
++ state.Dictionary = kmalloc( state.Properties.DictionarySize, GFP_KERNEL);
++#else
++ state.Dictionary = vmalloc( state.Properties.DictionarySize);
++#endif
++ if ( state.Dictionary == 0) {
++ printk(KERN_ERR "initramfs: Couldn't allocate lzma dictionary\n");
++ return -1;
++ }
++
++ printk( KERN_NOTICE "LZMA initramfs by Ming-Ching Tiew <mctiew@yahoo.com> " );
++
++ LzmaDecoderInit( &state );
++
++ for( nowPos =0; nowPos < uncompressedSize ; )
++ {
++ UInt32 blockSize = uncompressedSize - nowPos;
++ if( blockSize > kBlockSize)
++ blockSize = kBlockSize;
++ res = LzmaDecode( &state, &callback, outputbuffer, blockSize, &outsizeProcessed);
++ if( res != 0 ) {
++ panic( KERN_ERR "initramfs: Lzma decode failure\n");
++ return -1;
++ }
++ if( outsizeProcessed == 0 )
++ {
++ uncompressedSize = nowPos;
++ printk( KERN_NOTICE "initramfs: nowPos=%d, uncompressedSize=%d\n",
++ nowPos, uncompressedSize );
++ break;
++ }
++ flush_buffer(outputbuffer, outsizeProcessed);
++ nowPos += outsizeProcessed;
++ printk( ".");
++ }
++
++#ifdef CONFIG_LZMA_INITRAM_FS_KMALLOC_ONLY
++ kfree(state.Dictionary);
++#else
++ vfree(state.Dictionary);
++#endif
++ kfree(outputbuffer);
++ kfree(state.Probs);
++ return 0;
++}
++
++#endif /*CONFIG LZMA_INITRAM_FS*/
++
+ static char * __init unpack_to_rootfs(char *buf, unsigned len, int check_only)
+ {
+ int written;
+@@ -475,12 +588,31 @@ static char * __init unpack_to_rootfs(char *buf, unsigned len, int check_only)
+ inptr = 0;
+ outcnt = 0; /* bytes in output buffer */
+ bytes_out = 0;
+- crc = (ulg)0xffffffffL; /* shift register contents */
+- makecrc();
+- gunzip();
+- if (state != Reset)
++ if( inbuf[0] == 037 && ((inbuf[1] == 0213) || (inbuf[1] == 0236)))
++ {
++ printk( KERN_NOTICE "detected gzip initramfs\n");
++ crc = (ulg)0xffffffffL; /* shift register contents */
++ makecrc();
++ gunzip();
++ if (state != Reset)
+ error("junk in gzipped archive");
+- this_header = saved_offset + inptr;
++ }
++#ifdef CONFIG_LZMA_INITRAM_FS
++ else if( inbuf[0] < 9 * 5 * 5 && buf[9] == 0 && buf[10] == 0
++ && buf[11] == 0 && buf[12] == 0 )
++ {
++ printk( KERN_NOTICE "detected lzma initramfs\n");
++ lzma_unzip();
++ }
++#endif
++ else
++ {
++ // skip forward ?
++ crc = (ulg)0xffffffffL; /* shift register contents */
++ makecrc();
++ gunzip();
++ }
++ this_header = saved_offset + inptr;
+ buf += inptr;
+ len -= inptr;
+ }
+diff --git a/kernel/Makefile b/kernel/Makefile
+index ac6b27a..bd498a2 100644
+--- a/kernel/Makefile
++++ b/kernel/Makefile
+@@ -66,7 +66,7 @@ $(obj)/configs.o: $(obj)/config_data.h
+ # config_data.h contains the same information as ikconfig.h but gzipped.
+ # Info from config_data can be extracted from /proc/config*
+ targets += config_data.gz
+-$(obj)/config_data.gz: .config FORCE
++$(obj)/config_data.gz: .miniconfig FORCE
+ $(call if_changed,gzip)
+
+ quiet_cmd_ikconfiggz = IKCFG $@
+diff --git a/kernel/configs.c b/kernel/configs.c
+index 8fa1fb2..c8407eb 100644
+--- a/kernel/configs.c
++++ b/kernel/configs.c
+@@ -88,7 +88,7 @@ static int __init ikconfig_init(void)
+ struct proc_dir_entry *entry;
+
+ /* create the current config file */
+- entry = create_proc_entry("config.gz", S_IFREG | S_IRUGO,
++ entry = create_proc_entry("miniconfig.gz", S_IFREG | S_IRUGO,
+ &proc_root);
+ if (!entry)
+ return -ENOMEM;
+@@ -104,7 +104,7 @@ static int __init ikconfig_init(void)
+
+ static void __exit ikconfig_cleanup(void)
+ {
+- remove_proc_entry("config.gz", &proc_root);
++ remove_proc_entry("miniconfig.gz", &proc_root);
+ }
+
+ module_init(ikconfig_init);
+diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c
+index fe5c7db..a5150e6 100644
+--- a/kernel/time/clocksource.c
++++ b/kernel/time/clocksource.c
+@@ -85,8 +85,8 @@ static void clocksource_ratewd(struct clocksource *cs, int64_t delta)
+ if (delta > -WATCHDOG_TRESHOLD && delta < WATCHDOG_TRESHOLD)
+ return;
+
+- printk(KERN_WARNING "Clocksource %s unstable (delta = %Ld ns)\n",
+- cs->name, delta);
++/* printk(KERN_WARNING "Clocksource %s unstable (delta = %Ld ns)\n",
++ cs->name, delta); */
+ cs->flags &= ~(CLOCK_SOURCE_VALID_FOR_HRES | CLOCK_SOURCE_WATCHDOG);
+ clocksource_change_rating(cs, 0);
+ cs->flags &= ~CLOCK_SOURCE_WATCHDOG;
+diff --git a/kernel/timer.c b/kernel/timer.c
+index dd6c2c1..3a8f485 100644
+--- a/kernel/timer.c
++++ b/kernel/timer.c
+@@ -916,8 +916,8 @@ static void change_clocksource(void)
+
+ tick_clock_notify();
+
+- printk(KERN_INFO "Time: %s clocksource has been installed.\n",
+- clock->name);
++/* printk(KERN_INFO "Time: %s clocksource has been installed.\n",
++ clock->name); */
+ }
+ #else
+ static inline void change_clocksource(void) { }
+diff --git a/miniconfig.sh b/miniconfig.sh
+new file mode 100755
+index 0000000..28e7433
+--- /dev/null
++++ b/miniconfig.sh
+@@ -0,0 +1,2 @@
++#!/bin/sh -f
++make allnoconfig KCONFIG_ALLCONFIG=.miniconfig
+diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
+index fc498fe..e98172c 100644
+--- a/scripts/Makefile.lib
++++ b/scripts/Makefile.lib
+@@ -162,4 +162,9 @@ cmd_objcopy = $(OBJCOPY) $(OBJCOPYFLAGS) $(OBJCOPYFLAGS_$(@F)) $< $@
+ quiet_cmd_gzip = GZIP $@
+ cmd_gzip = gzip -f -9 < $< > $@
+
++# LZMA
++#
++quiet_cmd_lzma = LZMA $@
++cmd_lzma = lzma e $< $@ -lc7 -lp0 -pb0 2>/dev/null
++
+
+diff --git a/scripts/gen_lzma_initramfs_list.sh b/scripts/gen_lzma_initramfs_list.sh
+new file mode 100644
+index 0000000..be3ed6a
+--- /dev/null
++++ b/scripts/gen_lzma_initramfs_list.sh
+@@ -0,0 +1,292 @@
++#!/bin/bash
++# Copyright (C) Martin Schlemmer <azarah@nosferatu.za.org>
++# Copyright (c) 2006 Sam Ravnborg <sam@ravnborg.org>
++#
++# Released under the terms of the GNU GPL
++#
++# Generate a cpio packed initramfs. It uses gen_init_cpio to generate
++# the cpio archive, and gzip to pack it.
++# The script may also be used to generate the inputfile used for gen_init_cpio
++# This script assumes that gen_init_cpio is located in usr/ directory
++
++# error out on errors
++set -e
++
++usage() {
++cat << EOF
++Usage:
++$0 [-o <file>] [-u <uid>] [-g <gid>] { -s | -d | <cpio_source>} ...
++ -o <file> Create lzma initramfs file named <file> using
++ gen_init_cpio and lzma
++ -u <uid> User ID to map to user ID 0 (root).
++ <uid> is only meaningful if <cpio_source>
++ is a directory.
++ -g <gid> Group ID to map to group ID 0 (root).
++ <gid> is only meaningful if <cpio_source>
++ is a directory.
++ <cpio_source> File list or directory for cpio archive.
++ If <cpio_source> is a .cpio file it will be used
++ as direct input to initramfs.
++ -s Create lzma file with small dictionary size
++ -d Output the default cpio list.
++
++All options except -o and -l may be repeated and are interpreted
++sequentially and immediately. -u and -g states are preserved across
++<cpio_source> options so an explicit "-u 0 -g 0" is required
++to reset the root/group mapping.
++EOF
++}
++
++list_default_initramfs() {
++ # echo usr/kinit/kinit
++ :
++}
++
++default_initramfs() {
++ cat <<-EOF >> ${output}
++ # This is a very simple, default initramfs
++
++ dir /dev 0755 0 0
++ nod /dev/console 0600 0 0 c 5 1
++ dir /root 0700 0 0
++ # file /kinit usr/kinit/kinit 0755 0 0
++ # slink /init kinit 0755 0 0
++ EOF
++}
++
++filetype() {
++ local argv1="$1"
++
++ # symlink test must come before file test
++ if [ -L "${argv1}" ]; then
++ echo "slink"
++ elif [ -f "${argv1}" ]; then
++ echo "file"
++ elif [ -d "${argv1}" ]; then
++ echo "dir"
++ elif [ -b "${argv1}" -o -c "${argv1}" ]; then
++ echo "nod"
++ elif [ -p "${argv1}" ]; then
++ echo "pipe"
++ elif [ -S "${argv1}" ]; then
++ echo "sock"
++ else
++ echo "invalid"
++ fi
++ return 0
++}
++
++list_print_mtime() {
++ :
++}
++
++print_mtime() {
++ local my_mtime="0"
++
++ if [ -e "$1" ]; then
++ my_mtime=$(find "$1" -printf "%T@\n" | sort -r | head -n 1)
++ fi
++
++ echo "# Last modified: ${my_mtime}" >> ${output}
++ echo "" >> ${output}
++}
++
++list_parse() {
++ echo "$1 \\"
++}
++
++# for each file print a line in following format
++# <filetype> <name> <path to file> <octal mode> <uid> <gid>
++# for links, devices etc the format differs. See gen_init_cpio for details
++parse() {
++ local location="$1"
++ local name="${location/${srcdir}//}"
++ # change '//' into '/'
++ name="${name//\/\///}"
++ local mode="$2"
++ local uid="$3"
++ local gid="$4"
++ local ftype=$(filetype "${location}")
++ # remap uid/gid to 0 if necessary
++ [ "$uid" -eq "$root_uid" ] && uid=0
++ [ "$gid" -eq "$root_gid" ] && gid=0
++ local str="${mode} ${uid} ${gid}"
++
++ [ "${ftype}" == "invalid" ] && return 0
++ [ "${location}" == "${srcdir}" ] && return 0
++
++ case "${ftype}" in
++ "file")
++ str="${ftype} ${name} ${location} ${str}"
++ ;;
++ "nod")
++ local dev_type=
++ local maj=$(LC_ALL=C ls -l "${location}" | \
++ gawk '{sub(/,/, "", $5); print $5}')
++ local min=$(LC_ALL=C ls -l "${location}" | \
++ gawk '{print $6}')
++
++ if [ -b "${location}" ]; then
++ dev_type="b"
++ else
++ dev_type="c"
++ fi
++ str="${ftype} ${name} ${str} ${dev_type} ${maj} ${min}"
++ ;;
++ "slink")
++ local target=$(LC_ALL=C ls -l "${location}" | \
++ gawk '{print $11}')
++ str="${ftype} ${name} ${target} ${str}"
++ ;;
++ *)
++ str="${ftype} ${name} ${str}"
++ ;;
++ esac
++
++ echo "${str}" >> ${output}
++
++ return 0
++}
++
++unknown_option() {
++ printf "ERROR: unknown option \"$arg\"\n" >&2
++ printf "If the filename validly begins with '-', " >&2
++ printf "then it must be prefixed\n" >&2
++ printf "by './' so that it won't be interpreted as an option." >&2
++ printf "\n" >&2
++ usage >&2
++ exit 1
++}
++
++list_header() {
++ :
++}
++
++header() {
++ printf "\n#####################\n# $1\n" >> ${output}
++}
++
++# process one directory (incl sub-directories)
++dir_filelist() {
++ ${dep_list}header "$1"
++
++ srcdir=$(echo "$1" | sed -e 's://*:/:g')
++ dirlist=$(find "${srcdir}" -printf "%p %m %U %G\n" 2>/dev/null)
++
++ # If $dirlist is only one line, then the directory is empty
++ if [ "$(echo "${dirlist}" | wc -l)" -gt 1 ]; then
++ ${dep_list}print_mtime "$1"
++
++ echo "${dirlist}" | \
++ while read x; do
++ ${dep_list}parse ${x}
++ done
++ fi
++}
++
++# if only one file is specified and it is .cpio file then use it direct as fs
++# if a directory is specified then add all files in given direcotry to fs
++# if a regular file is specified assume it is in gen_initramfs format
++input_file() {
++ source="$1"
++ if [ -f "$1" ]; then
++ ${dep_list}header "$1"
++ is_cpio="$(echo "$1" | sed 's/^.*\.cpio/cpio/')"
++ if [ $2 -eq 0 -a ${is_cpio} == "cpio" ]; then
++ cpio_file=$1
++ [ ! -z ${dep_list} ] && echo "$1"
++ return 0
++ fi
++ if [ -z ${dep_list} ]; then
++ print_mtime "$1" >> ${output}
++ cat "$1" >> ${output}
++ else
++ cat "$1" | while read type dir file perm ; do
++ if [ "$type" == "file" ]; then
++ echo "$file \\";
++ fi
++ done
++ fi
++ elif [ -d "$1" ]; then
++ dir_filelist "$1"
++ else
++ echo " ${prog}: Cannot open '$1'" >&2
++ exit 1
++ fi
++}
++
++prog=$0
++root_uid=0
++root_gid=0
++dep_list=
++cpio_file=
++cpio_list=
++output="/dev/stdout"
++output_file=""
++opt=""
++
++arg="$1"
++case "$arg" in
++ "-l") # files included in initramfs - used by kbuild
++ dep_list="list_"
++ echo "deps_initramfs := \\"
++ shift
++ ;;
++ "-o") # generate lzma-ed cpio image named $1
++ shift
++ output_file="$1"
++ cpio_list="$(mktemp ${TMPDIR:-/tmp}/cpiolist.XXXXXX)"
++ output=${cpio_list}
++ shift
++ ;;
++esac
++while [ $# -gt 0 ]; do
++ arg="$1"
++ shift
++ case "$arg" in
++ "-u") # map $1 to uid=0 (root)
++ root_uid="$1"
++ shift
++ ;;
++ "-g") # map $1 to gid=0 (root)
++ root_gid="$1"
++ shift
++ ;;
++ "-s")
++ opt="-d16"
++ ;;
++ "-d") # display default initramfs list
++ default_list="$arg"
++ ${dep_list}default_initramfs
++ ;;
++ "-h")
++ usage
++ exit 0
++ ;;
++ *)
++ case "$arg" in
++ "-"*)
++ unknown_option
++ ;;
++ *) # input file/dir - process it
++ input_file "$arg" "$#"
++ ;;
++ esac
++ ;;
++ esac
++done
++
++# If output_file is set we will generate cpio archive and lzma it
++# we are carefull to delete tmp files
++if [ ! -z ${output_file} ]; then
++ if [ -z ${cpio_file} ]; then
++ cpio_tfile="$(mktemp ${TMPDIR:-/tmp}/cpiofile.XXXXXX)"
++ usr/gen_init_cpio ${cpio_list} > ${cpio_tfile}
++ else
++ cpio_tfile=${cpio_file}
++ fi
++ rm ${cpio_list}
++ lzma e ${cpio_tfile} ${output_file} ${opt}
++ [ -z ${cpio_file} ] && rm ${cpio_tfile}
++fi
++exit 0
+diff --git a/shrinkconfig.sh b/shrinkconfig.sh
+new file mode 100755
+index 0000000..e7a3df7
+--- /dev/null
++++ b/shrinkconfig.sh
+@@ -0,0 +1,79 @@
++#! /bin/bash
++
++# shrinkconfig copyright 2006 by Rob Landley <rob@landley.net>
++# Licensed under the GNU General Public License version 2.
++
++if [ $# -ne 1 ]
++then
++ echo "Turns current .config into a miniconfig file."
++ echo "Usage: shrinkconfig mini.config"
++ exit 1
++fi
++
++if [ ! -f .config ]
++then
++ echo "Need a .config file to shrink."
++ exit 1
++fi
++LENGTH=$(wc -l < .config)
++
++OUTPUT="$1"
++cp .config "$OUTPUT"
++if [ $? -ne 0 ]
++then
++ echo "Couldn't create $OUTPUT"
++ exit 1
++fi
++
++# If we get interrupted, clean up the mess
++
++KERNELOUTPUT=""
++
++function cleanup
++{
++ echo
++ echo "Interrupted."
++ [ ! -z "$KERNELOUTPUT" ] && rm -rf "$KERNELOUTPUT"
++ rm "$OUTPUT"
++ exit 1
++}
++
++trap cleanup HUP INT QUIT TERM
++
++# Since the "O=" argument to make doesn't work recursively, we need to jump
++# through a few hoops to avoid overwriting the .config that we're shrinking.
++
++# If we're building out of tree, we'll have absolute paths to source and build
++# directories in the Makefile.
++
++KERNELSRC=$(sed -n -e 's/KERNELSRC[^/]*:=[^/]*//p' Makefile)
++[ -z "$KERNELSRC" ] && KERNELSRC=$(pwd)
++KERNELOUTPUT=`pwd`/.config.minitemp
++
++mkdir -p "$KERNELOUTPUT" || exit 1
++
++echo "Shrinking .config to $OUTPUT..."
++
++for I in $(seq 1 $LENGTH)
++do
++ echo -n -e "\r"$I/$LENGTH lines $(wc -c < "$OUTPUT") bytes
++
++ sed -n "${I}!p" "$OUTPUT" > "$KERNELOUTPUT"/.config.test
++ # Do a config with this file
++ make -C "$KERNELSRC" O="$KERNELOUTPUT" allnoconfig KCONFIG_ALLCONFIG="$KERNELOUTPUT"/.config.test > /dev/null
++
++ # Compare. The date changes, so expect a small difference each time.
++ D=$(diff "$KERNELOUTPUT"/.config .config | wc -l)
++ if [ $D -eq 4 ]
++ then
++ mv "$KERNELOUTPUT"/.config.test "$OUTPUT"
++ LENGTH=$[$LENGTH-1]
++ else
++ I=$[$I + 1]
++ fi
++done
++
++rm -rf "$KERNELOUTPUT"
++
++# One extra echo to preserve status line.
++echo
+diff --git a/usr/Makefile b/usr/Makefile
+index 201f27f..8e1f6ea 100644
+--- a/usr/Makefile
++++ b/usr/Makefile
+@@ -19,6 +19,7 @@ $(obj)/initramfs_data.o: $(obj)/initramfs_data.cpio.gz FORCE
+
+ hostprogs-y := gen_init_cpio
+ initramfs := $(CONFIG_SHELL) $(srctree)/scripts/gen_initramfs_list.sh
++lzma_initramfs := $(CONFIG_SHELL) $(srctree)/scripts/gen_lzma_initramfs_list.sh
+ ramfs-input := $(if $(filter-out "",$(CONFIG_INITRAMFS_SOURCE)), \
+ $(shell echo $(CONFIG_INITRAMFS_SOURCE)),-d)
+ ramfs-args := \
+@@ -36,6 +37,14 @@ endif
+ quiet_cmd_initfs = GEN $@
+ cmd_initfs = $(initramfs) -o $@ $(ramfs-args) $(ramfs-input)
+
++ifdef CONFIG_LZMA_INITRAM_FS_SMALLMEM
++quiet_cmd_lzma_initfs = LZRAMFS $@
++ cmd_lzma_initfs = $(lzma_initramfs) -o $@ $(ramfs-args) -s $(ramfs-input)
++else
++quiet_cmd_lzma_initfs = LZRAMFS $@
++ cmd_lzma_initfs = $(lzma_initramfs) -o $@ $(ramfs-args) $(ramfs-input)
++endif
++
+ targets := initramfs_data.cpio.gz
+ # do not try to update files included in initramfs
+ $(deps_initramfs): ;
+@@ -48,5 +57,9 @@ $(deps_initramfs): klibcdirs
+ # 4) arguments to gen_initramfs.sh changes
+ $(obj)/initramfs_data.cpio.gz: $(obj)/gen_init_cpio $(deps_initramfs) klibcdirs
+ $(Q)$(initramfs) -l $(ramfs-input) > $(obj)/.initramfs_data.cpio.gz.d
++ifdef CONFIG_LZMA_INITRAM_FS
++ $(call if_changed,lzma_initfs)
++else
+ $(call if_changed,initfs)
++endif
+
diff --git a/toolchain/kernel-headers/lzma/linux-2.6.21.5-002-lzma-vmlinuz.01.patch b/toolchain/kernel-headers/lzma/linux-2.6.21.5-002-lzma-vmlinuz.01.patch
new file mode 100644
index 000000000..05361ff9d
--- /dev/null
+++ b/toolchain/kernel-headers/lzma/linux-2.6.21.5-002-lzma-vmlinuz.01.patch
@@ -0,0 +1,54 @@
+diff -rdup linux-2.6.21.5.oorig/arch/i386/boot/compressed/Makefile linux-2.6.21.5/arch/i386/boot/compressed/Makefile
+--- linux-2.6.21.5.oorig/arch/i386/boot/compressed/Makefile 2007-07-24 13:08:51.000000000 +0200
++++ linux-2.6.21.5/arch/i386/boot/compressed/Makefile 2007-07-24 14:54:38.000000000 +0200
+@@ -4,7 +4,7 @@
+ # create a compressed vmlinux image from the original vmlinux
+ #
+
+-tragets := head.o lzma_misc.o piggy.o \
++targets := head.o lzma_misc.o piggy.o \
+ vmlinux.bin.all vmlinux.relocs \
+ vmlinux vmlinux.bin vmlinux.bin.gz
+ EXTRA_AFLAGS := -traditional
+diff -rdup linux-2.6.21.5.oorig/scripts/gen_lzma_initramfs_list.sh linux-2.6.21.5/scripts/gen_lzma_initramfs_list.sh
+--- linux-2.6.21.5.oorig/scripts/gen_lzma_initramfs_list.sh 2007-07-24 13:08:51.000000000 +0200
++++ linux-2.6.21.5/scripts/gen_lzma_initramfs_list.sh 2007-07-24 15:12:10.000000000 +0200
+@@ -253,7 +253,7 @@ while [ $# -gt 0 ]; do
+ shift
+ ;;
+ "-s")
+- opt="-d16"
++ #opt="-d16" ? what was that supposed to do?
+ ;;
+ "-d") # display default initramfs list
+ default_list="$arg"
+@@ -286,7 +286,7 @@ if [ ! -z ${output_file} ]; then
+ cpio_tfile=${cpio_file}
+ fi
+ rm ${cpio_list}
+- lzma e ${cpio_tfile} ${output_file} ${opt}
++ lzma -z ${cpio_tfile} ${opt} -c > ${output_file}
+ [ -z ${cpio_file} ] && rm ${cpio_tfile}
+ fi
+ exit 0
+--- linux-2.6.21.5.oorig/arch/i386/boot/compressed/lzma_misc.c 2007-07-24 15:24:44.000000000 +0200
++++ linux-2.6.21.5/arch/i386/boot/compressed/lzma_misc.c 2007-07-24 17:09:40.000000000 +0200
+@@ -241,7 +241,6 @@ static int lzma_unzip(uch* output)
+
+ static int read_byte(void *object, const unsigned char **buffer, SizeT *bufferSize)
+ {
+- static unsigned int i = 0;
+ static unsigned char val;
+ *bufferSize = 1;
+ val = get_byte();
+--- linux-2.6.21.5.oorig/scripts/Makefile.lib 2007-07-24 15:24:44.000000000 +0200
++++ linux-2.6.21.5/scripts/Makefile.lib 2007-07-24 18:03:57.000000000 +0200
+@@ -165,6 +165,7 @@ cmd_gzip = gzip -f -9 < $< > $@
+ # LZMA
+ #
+ quiet_cmd_lzma = LZMA $@
+-cmd_lzma = lzma e $< $@ -lc7 -lp0 -pb0 2>/dev/null
++#cmd_lzma = lzma e $< $@ -lc7 -lp0 -pb0 2>/dev/null
++cmd_lzma = lzma -z $< -c > $@
+
+
diff --git a/toolchain/kernel-headers/lzma/linux-2.6.22.1-001-lzma-vmlinuz.00.patch b/toolchain/kernel-headers/lzma/linux-2.6.22.1-001-lzma-vmlinuz.00.patch
new file mode 100644
index 000000000..16f9ef741
--- /dev/null
+++ b/toolchain/kernel-headers/lzma/linux-2.6.22.1-001-lzma-vmlinuz.00.patch
@@ -0,0 +1,26856 @@
+diff -rduNp linux-2.6.22.1.oorig/.miniconfig linux-2.6.22.1/.miniconfig
+--- linux-2.6.22.1.oorig/.miniconfig 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/.miniconfig 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,89 @@
++#make allnoconfig KCONFIG_ALLCONFIG=miniconfig
++CONFIG_X86_32=y
++CONFIG_CLOCKSOURCE_WATCHDOG=y
++CONFIG_LOCKDEP_SUPPORT=y
++CONFIG_SEMAPHORE_SLEEPERS=y
++CONFIG_MMU=y
++CONFIG_GENERIC_ISA_DMA=y
++CONFIG_GENERIC_HWEIGHT=y
++CONFIG_DMI=y
++CONFIG_INIT_ENV_ARG_LIMIT=32
++CONFIG_IKCONFIG=y
++CONFIG_IKCONFIG_PROC=y
++CONFIG_SYSFS_DEPRECATED=y
++CONFIG_BLK_DEV_INITRD=y
++CONFIG_SYSCTL=y
++CONFIG_EMBEDDED=y
++CONFIG_PRINTK=y
++CONFIG_BASE_SMALL=1
++CONFIG_BLOCK=y
++CONFIG_IOSCHED_NOOP=y
++CONFIG_DEFAULT_IOSCHED="noop"
++CONFIG_X86_GENERIC=y
++CONFIG_X86_L1_CACHE_SHIFT=7
++CONFIG_GENERIC_CALIBRATE_DELAY=y
++CONFIG_X86_WP_WORKS_OK=y
++CONFIG_X86_BSWAP=y
++CONFIG_X86_CMPXCHG64=y
++CONFIG_X86_INTEL_USERCOPY=y
++CONFIG_X86_TSC=y
++CONFIG_PREEMPT_NONE=y
++CONFIG_VM86=y
++CONFIG_HIGHMEM=y
++CONFIG_FLATMEM=y
++CONFIG_MTRR=y
++CONFIG_HZ_250=y
++CONFIG_PHYSICAL_ALIGN=0x100000
++CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y
++CONFIG_PM=y
++CONFIG_ACPI=y
++CONFIG_ACPI_SLEEP=y
++CONFIG_ACPI_BLACKLIST_YEAR=0
++CONFIG_ACPI_EC=y
++CONFIG_ACPI_SYSTEM=y
++CONFIG_PCI=y
++CONFIG_PCI_GOANY=y
++CONFIG_PCI_DIRECT=y
++CONFIG_BINFMT_ELF=y
++CONFIG_STANDALONE=y
++CONFIG_BLK_DEV_LOOP=y
++CONFIG_IDE=y
++CONFIG_IDE_MAX_HWIFS=2
++CONFIG_BLK_DEV_IDE=y
++CONFIG_BLK_DEV_IDEDISK=y
++CONFIG_IDEDISK_MULTI_MODE=y
++CONFIG_BLK_DEV_IDECD=y
++CONFIG_IDE_GENERIC=y
++CONFIG_INPUT_MOUSEDEV=y
++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
++CONFIG_INPUT_KEYBOARD=y
++CONFIG_KEYBOARD_ATKBD=y
++CONFIG_SERIO=y
++CONFIG_VT=y
++CONFIG_VT_CONSOLE=y
++CONFIG_UNIX98_PTYS=y
++CONFIG_VGA_CONSOLE=y
++CONFIG_USB_ARCH_HAS_HCD=y
++CONFIG_USB_ARCH_HAS_EHCI=y
++CONFIG_EXT2_FS=y
++CONFIG_DNOTIFY=y
++CONFIG_ISO9660_FS=y
++CONFIG_FAT_FS=y
++CONFIG_VFAT_FS=y
++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
++CONFIG_PROC_FS=y
++CONFIG_PROC_SYSCTL=y
++CONFIG_SYSFS=y
++CONFIG_RAMFS=y
++CONFIG_SQUASHFS=y
++CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3
++CONFIG_MSDOS_PARTITION=y
++CONFIG_NLS_DEFAULT="iso8859-1"
++CONFIG_AUFS=y
++CONFIG_AUFS_FAKE_DM=y
++CONFIG_EARLY_PRINTK=y
++CONFIG_DOUBLEFAULT=y
++CONFIG_ZLIB_INFLATE=y
++CONFIG_HAS_IOPORT=y
++CONFIG_GENERIC_IRQ_PROBE=y
++CONFIG_KTIME_SCALAR=y
+diff -rduNp linux-2.6.22.1.oorig/Makefile linux-2.6.22.1/Makefile
+--- linux-2.6.22.1.oorig/Makefile 2007-07-10 20:56:30.000000000 +0200
++++ linux-2.6.22.1/Makefile 2007-07-24 14:17:46.000000000 +0200
+@@ -188,7 +188,7 @@ CROSS_COMPILE ?=
+ # Architecture as present in compile.h
+ UTS_MACHINE := $(ARCH)
+
+-KCONFIG_CONFIG ?= .config
++KCONFIG_CONFIG ?= .miniconfig
+
+ # SHELL used by kbuild
+ CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
+diff -rduNp linux-2.6.22.1.oorig/arch/i386/boot/compressed/LzmaDecode.c linux-2.6.22.1/arch/i386/boot/compressed/LzmaDecode.c
+--- linux-2.6.22.1.oorig/arch/i386/boot/compressed/LzmaDecode.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/arch/i386/boot/compressed/LzmaDecode.c 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,588 @@
++/*
++ LzmaDecode.c
++ LZMA Decoder (optimized for Speed version)
++
++ LZMA SDK 4.22 Copyright (c) 1999-2005 Igor Pavlov (2005-06-10)
++ http://www.7-zip.org/
++
++ LZMA SDK is licensed under two licenses:
++ 1) GNU Lesser General Public License (GNU LGPL)
++ 2) Common Public License (CPL)
++ It means that you can select one of these two licenses and
++ follow rules of that license.
++
++ SPECIAL EXCEPTION:
++ Igor Pavlov, as the author of this Code, expressly permits you to
++ statically or dynamically link your Code (or bind by name) to the
++ interfaces of this file without subjecting your linked Code to the
++ terms of the CPL or GNU LGPL. Any modifications or additions
++ to this file, however, are subject to the LGPL or CPL terms.
++*/
++
++#include "LzmaDecode.h"
++
++#ifndef Byte
++#define Byte unsigned char
++#endif
++
++#define kNumTopBits 24
++#define kTopValue ((UInt32)1 << kNumTopBits)
++
++#define kNumBitModelTotalBits 11
++#define kBitModelTotal (1 << kNumBitModelTotalBits)
++#define kNumMoveBits 5
++
++#define RC_READ_BYTE (*Buffer++)
++
++#define RC_INIT2 Code = 0; Range = 0xFFFFFFFF; \
++ { int i; for(i = 0; i < 5; i++) { RC_TEST; Code = (Code << 8) | RC_READ_BYTE; }}
++
++#ifdef _LZMA_IN_CB
++
++#define RC_TEST { if (Buffer == BufferLim) \
++ { SizeT size; int result = InCallback->Read(InCallback, &Buffer, &size); if (result != LZMA_RESULT_OK) return result; \
++ BufferLim = Buffer + size; if (size == 0) return LZMA_RESULT_DATA_ERROR; }}
++
++#define RC_INIT Buffer = BufferLim = 0; RC_INIT2
++
++#else
++
++#define RC_TEST { if (Buffer == BufferLim) return LZMA_RESULT_DATA_ERROR; }
++
++#define RC_INIT(buffer, bufferSize) Buffer = buffer; BufferLim = buffer + bufferSize; RC_INIT2
++
++#endif
++
++#define RC_NORMALIZE if (Range < kTopValue) { RC_TEST; Range <<= 8; Code = (Code << 8) | RC_READ_BYTE; }
++
++#define IfBit0(p) RC_NORMALIZE; bound = (Range >> kNumBitModelTotalBits) * *(p); if (Code < bound)
++#define UpdateBit0(p) Range = bound; *(p) += (kBitModelTotal - *(p)) >> kNumMoveBits;
++#define UpdateBit1(p) Range -= bound; Code -= bound; *(p) -= (*(p)) >> kNumMoveBits;
++
++#define RC_GET_BIT2(p, mi, A0, A1) IfBit0(p) \
++ { UpdateBit0(p); mi <<= 1; A0; } else \
++ { UpdateBit1(p); mi = (mi + mi) + 1; A1; }
++
++#define RC_GET_BIT(p, mi) RC_GET_BIT2(p, mi, ; , ;)
++
++#define RangeDecoderBitTreeDecode(probs, numLevels, res) \
++ { int i = numLevels; res = 1; \
++ do { CProb *p = probs + res; RC_GET_BIT(p, res) } while(--i != 0); \
++ res -= (1 << numLevels); }
++
++
++#define kNumPosBitsMax 4
++#define kNumPosStatesMax (1 << kNumPosBitsMax)
++
++#define kLenNumLowBits 3
++#define kLenNumLowSymbols (1 << kLenNumLowBits)
++#define kLenNumMidBits 3
++#define kLenNumMidSymbols (1 << kLenNumMidBits)
++#define kLenNumHighBits 8
++#define kLenNumHighSymbols (1 << kLenNumHighBits)
++
++#define LenChoice 0
++#define LenChoice2 (LenChoice + 1)
++#define LenLow (LenChoice2 + 1)
++#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits))
++#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits))
++#define kNumLenProbs (LenHigh + kLenNumHighSymbols)
++
++
++#define kNumStates 12
++#define kNumLitStates 7
++
++#define kStartPosModelIndex 4
++#define kEndPosModelIndex 14
++#define kNumFullDistances (1 << (kEndPosModelIndex >> 1))
++
++#define kNumPosSlotBits 6
++#define kNumLenToPosStates 4
++
++#define kNumAlignBits 4
++#define kAlignTableSize (1 << kNumAlignBits)
++
++#define kMatchMinLen 2
++
++#define IsMatch 0
++#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax))
++#define IsRepG0 (IsRep + kNumStates)
++#define IsRepG1 (IsRepG0 + kNumStates)
++#define IsRepG2 (IsRepG1 + kNumStates)
++#define IsRep0Long (IsRepG2 + kNumStates)
++#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax))
++#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits))
++#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex)
++#define LenCoder (Align + kAlignTableSize)
++#define RepLenCoder (LenCoder + kNumLenProbs)
++#define Literal (RepLenCoder + kNumLenProbs)
++
++#if Literal != LZMA_BASE_SIZE
++StopCompilingDueBUG
++#endif
++
++int LzmaDecodeProperties(CLzmaProperties *propsRes, const unsigned char *propsData, int size)
++{
++ unsigned char prop0;
++ if (size < LZMA_PROPERTIES_SIZE)
++ return LZMA_RESULT_DATA_ERROR;
++ prop0 = propsData[0];
++ if (prop0 >= (9 * 5 * 5))
++ return LZMA_RESULT_DATA_ERROR;
++ {
++ for (propsRes->pb = 0; prop0 >= (9 * 5); propsRes->pb++, prop0 -= (9 * 5));
++ for (propsRes->lp = 0; prop0 >= 9; propsRes->lp++, prop0 -= 9);
++ propsRes->lc = prop0;
++ /*
++ unsigned char remainder = (unsigned char)(prop0 / 9);
++ propsRes->lc = prop0 % 9;
++ propsRes->pb = remainder / 5;
++ propsRes->lp = remainder % 5;
++ */
++ }
++
++ #ifdef _LZMA_OUT_READ
++ {
++ int i;
++ propsRes->DictionarySize = 0;
++ for (i = 0; i < 4; i++)
++ propsRes->DictionarySize += (UInt32)(propsData[1 + i]) << (i * 8);
++ if (propsRes->DictionarySize == 0)
++ propsRes->DictionarySize = 1;
++ }
++ #endif
++ return LZMA_RESULT_OK;
++}
++
++#define kLzmaStreamWasFinishedId (-1)
++
++int LzmaDecode(CLzmaDecoderState *vs,
++ #ifdef _LZMA_IN_CB
++ ILzmaInCallback *InCallback,
++ #else
++ const unsigned char *inStream, SizeT inSize, SizeT *inSizeProcessed,
++ #endif
++ unsigned char *outStream, SizeT outSize, SizeT *outSizeProcessed)
++{
++ CProb *p = vs->Probs;
++ SizeT nowPos = 0;
++ Byte previousByte = 0;
++ UInt32 posStateMask = (1 << (vs->Properties.pb)) - 1;
++ UInt32 literalPosMask = (1 << (vs->Properties.lp)) - 1;
++ int lc = vs->Properties.lc;
++
++ #ifdef _LZMA_OUT_READ
++
++ UInt32 Range = vs->Range;
++ UInt32 Code = vs->Code;
++ #ifdef _LZMA_IN_CB
++ const Byte *Buffer = vs->Buffer;
++ const Byte *BufferLim = vs->BufferLim;
++ #else
++ const Byte *Buffer = inStream;
++ const Byte *BufferLim = inStream + inSize;
++ #endif
++ int state = vs->State;
++ UInt32 rep0 = vs->Reps[0], rep1 = vs->Reps[1], rep2 = vs->Reps[2], rep3 = vs->Reps[3];
++ int len = vs->RemainLen;
++ UInt32 globalPos = vs->GlobalPos;
++ UInt32 distanceLimit = vs->DistanceLimit;
++
++ Byte *dictionary = vs->Dictionary;
++ UInt32 dictionarySize = vs->Properties.DictionarySize;
++ UInt32 dictionaryPos = vs->DictionaryPos;
++
++ Byte tempDictionary[4];
++
++ #ifndef _LZMA_IN_CB
++ *inSizeProcessed = 0;
++ #endif
++ *outSizeProcessed = 0;
++ if (len == kLzmaStreamWasFinishedId)
++ return LZMA_RESULT_OK;
++
++ if (dictionarySize == 0)
++ {
++ dictionary = tempDictionary;
++ dictionarySize = 1;
++ tempDictionary[0] = vs->TempDictionary[0];
++ }
++
++ if (len == kLzmaNeedInitId)
++ {
++ {
++ UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + vs->Properties.lp));
++ UInt32 i;
++ for (i = 0; i < numProbs; i++)
++ p[i] = kBitModelTotal >> 1;
++ rep0 = rep1 = rep2 = rep3 = 1;
++ state = 0;
++ globalPos = 0;
++ distanceLimit = 0;
++ dictionaryPos = 0;
++ dictionary[dictionarySize - 1] = 0;
++ #ifdef _LZMA_IN_CB
++ RC_INIT;
++ #else
++ RC_INIT(inStream, inSize);
++ #endif
++ }
++ len = 0;
++ }
++ while(len != 0 && nowPos < outSize)
++ {
++ UInt32 pos = dictionaryPos - rep0;
++ if (pos >= dictionarySize)
++ pos += dictionarySize;
++ outStream[nowPos++] = dictionary[dictionaryPos] = dictionary[pos];
++ if (++dictionaryPos == dictionarySize)
++ dictionaryPos = 0;
++ len--;
++ }
++ if (dictionaryPos == 0)
++ previousByte = dictionary[dictionarySize - 1];
++ else
++ previousByte = dictionary[dictionaryPos - 1];
++
++ #else /* if !_LZMA_OUT_READ */
++
++ int state = 0;
++ UInt32 rep0 = 1, rep1 = 1, rep2 = 1, rep3 = 1;
++ int len = 0;
++ const Byte *Buffer;
++ const Byte *BufferLim;
++ UInt32 Range;
++ UInt32 Code;
++
++ #ifndef _LZMA_IN_CB
++ *inSizeProcessed = 0;
++ #endif
++ *outSizeProcessed = 0;
++
++ {
++ UInt32 i;
++ UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + vs->Properties.lp));
++ for (i = 0; i < numProbs; i++)
++ p[i] = kBitModelTotal >> 1;
++ }
++
++ #ifdef _LZMA_IN_CB
++ RC_INIT;
++ #else
++ RC_INIT(inStream, inSize);
++ #endif
++
++ #endif /* _LZMA_OUT_READ */
++
++ while(nowPos < outSize)
++ {
++ CProb *prob;
++ UInt32 bound;
++ int posState = (int)(
++ (nowPos
++ #ifdef _LZMA_OUT_READ
++ + globalPos
++ #endif
++ )
++ & posStateMask);
++
++ prob = p + IsMatch + (state << kNumPosBitsMax) + posState;
++ IfBit0(prob)
++ {
++ int symbol = 1;
++ UpdateBit0(prob)
++ prob = p + Literal + (LZMA_LIT_SIZE *
++ (((
++ (nowPos
++ #ifdef _LZMA_OUT_READ
++ + globalPos
++ #endif
++ )
++ & literalPosMask) << lc) + (previousByte >> (8 - lc))));
++
++ if (state >= kNumLitStates)
++ {
++ int matchByte;
++ #ifdef _LZMA_OUT_READ
++ UInt32 pos = dictionaryPos - rep0;
++ if (pos >= dictionarySize)
++ pos += dictionarySize;
++ matchByte = dictionary[pos];
++ #else
++ matchByte = outStream[nowPos - rep0];
++ #endif
++ do
++ {
++ int bit;
++ CProb *probLit;
++ matchByte <<= 1;
++ bit = (matchByte & 0x100);
++ probLit = prob + 0x100 + bit + symbol;
++ RC_GET_BIT2(probLit, symbol, if (bit != 0) break, if (bit == 0) break)
++ }
++ while (symbol < 0x100);
++ }
++ while (symbol < 0x100)
++ {
++ CProb *probLit = prob + symbol;
++ RC_GET_BIT(probLit, symbol)
++ }
++ previousByte = (Byte)symbol;
++
++ outStream[nowPos++] = previousByte;
++ #ifdef _LZMA_OUT_READ
++ if (distanceLimit < dictionarySize)
++ distanceLimit++;
++
++ dictionary[dictionaryPos] = previousByte;
++ if (++dictionaryPos == dictionarySize)
++ dictionaryPos = 0;
++ #endif
++ if (state < 4) state = 0;
++ else if (state < 10) state -= 3;
++ else state -= 6;
++ }
++ else
++ {
++ UpdateBit1(prob);
++ prob = p + IsRep + state;
++ IfBit0(prob)
++ {
++ UpdateBit0(prob);
++ rep3 = rep2;
++ rep2 = rep1;
++ rep1 = rep0;
++ state = state < kNumLitStates ? 0 : 3;
++ prob = p + LenCoder;
++ }
++ else
++ {
++ UpdateBit1(prob);
++ prob = p + IsRepG0 + state;
++ IfBit0(prob)
++ {
++ UpdateBit0(prob);
++ prob = p + IsRep0Long + (state << kNumPosBitsMax) + posState;
++ IfBit0(prob)
++ {
++ #ifdef _LZMA_OUT_READ
++ UInt32 pos;
++ #endif
++ UpdateBit0(prob);
++
++ #ifdef _LZMA_OUT_READ
++ if (distanceLimit == 0)
++ #else
++ if (nowPos == 0)
++ #endif
++ return LZMA_RESULT_DATA_ERROR;
++
++ state = state < kNumLitStates ? 9 : 11;
++ #ifdef _LZMA_OUT_READ
++ pos = dictionaryPos - rep0;
++ if (pos >= dictionarySize)
++ pos += dictionarySize;
++ previousByte = dictionary[pos];
++ dictionary[dictionaryPos] = previousByte;
++ if (++dictionaryPos == dictionarySize)
++ dictionaryPos = 0;
++ #else
++ previousByte = outStream[nowPos - rep0];
++ #endif
++ outStream[nowPos++] = previousByte;
++ #ifdef _LZMA_OUT_READ
++ if (distanceLimit < dictionarySize)
++ distanceLimit++;
++ #endif
++
++ continue;
++ }
++ else
++ {
++ UpdateBit1(prob);
++ }
++ }
++ else
++ {
++ UInt32 distance;
++ UpdateBit1(prob);
++ prob = p + IsRepG1 + state;
++ IfBit0(prob)
++ {
++ UpdateBit0(prob);
++ distance = rep1;
++ }
++ else
++ {
++ UpdateBit1(prob);
++ prob = p + IsRepG2 + state;
++ IfBit0(prob)
++ {
++ UpdateBit0(prob);
++ distance = rep2;
++ }
++ else
++ {
++ UpdateBit1(prob);
++ distance = rep3;
++ rep3 = rep2;
++ }
++ rep2 = rep1;
++ }
++ rep1 = rep0;
++ rep0 = distance;
++ }
++ state = state < kNumLitStates ? 8 : 11;
++ prob = p + RepLenCoder;
++ }
++ {
++ int numBits, offset;
++ CProb *probLen = prob + LenChoice;
++ IfBit0(probLen)
++ {
++ UpdateBit0(probLen);
++ probLen = prob + LenLow + (posState << kLenNumLowBits);
++ offset = 0;
++ numBits = kLenNumLowBits;
++ }
++ else
++ {
++ UpdateBit1(probLen);
++ probLen = prob + LenChoice2;
++ IfBit0(probLen)
++ {
++ UpdateBit0(probLen);
++ probLen = prob + LenMid + (posState << kLenNumMidBits);
++ offset = kLenNumLowSymbols;
++ numBits = kLenNumMidBits;
++ }
++ else
++ {
++ UpdateBit1(probLen);
++ probLen = prob + LenHigh;
++ offset = kLenNumLowSymbols + kLenNumMidSymbols;
++ numBits = kLenNumHighBits;
++ }
++ }
++ RangeDecoderBitTreeDecode(probLen, numBits, len);
++ len += offset;
++ }
++
++ if (state < 4)
++ {
++ int posSlot;
++ state += kNumLitStates;
++ prob = p + PosSlot +
++ ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) <<
++ kNumPosSlotBits);
++ RangeDecoderBitTreeDecode(prob, kNumPosSlotBits, posSlot);
++ if (posSlot >= kStartPosModelIndex)
++ {
++ int numDirectBits = ((posSlot >> 1) - 1);
++ rep0 = (2 | ((UInt32)posSlot & 1));
++ if (posSlot < kEndPosModelIndex)
++ {
++ rep0 <<= numDirectBits;
++ prob = p + SpecPos + rep0 - posSlot - 1;
++ }
++ else
++ {
++ numDirectBits -= kNumAlignBits;
++ do
++ {
++ RC_NORMALIZE
++ Range >>= 1;
++ rep0 <<= 1;
++ if (Code >= Range)
++ {
++ Code -= Range;
++ rep0 |= 1;
++ }
++ }
++ while (--numDirectBits != 0);
++ prob = p + Align;
++ rep0 <<= kNumAlignBits;
++ numDirectBits = kNumAlignBits;
++ }
++ {
++ int i = 1;
++ int mi = 1;
++ do
++ {
++ CProb *prob3 = prob + mi;
++ RC_GET_BIT2(prob3, mi, ; , rep0 |= i);
++ i <<= 1;
++ }
++ while(--numDirectBits != 0);
++ }
++ }
++ else
++ rep0 = posSlot;
++ if (++rep0 == (UInt32)(0))
++ {
++ /* it's for stream version */
++ len = kLzmaStreamWasFinishedId;
++ break;
++ }
++ }
++
++ len += kMatchMinLen;
++ #ifdef _LZMA_OUT_READ
++ if (rep0 > distanceLimit)
++ #else
++ if (rep0 > nowPos)
++ #endif
++ return LZMA_RESULT_DATA_ERROR;
++
++ #ifdef _LZMA_OUT_READ
++ if (dictionarySize - distanceLimit > (UInt32)len)
++ distanceLimit += len;
++ else
++ distanceLimit = dictionarySize;
++ #endif
++
++ do
++ {
++ #ifdef _LZMA_OUT_READ
++ UInt32 pos = dictionaryPos - rep0;
++ if (pos >= dictionarySize)
++ pos += dictionarySize;
++ previousByte = dictionary[pos];
++ dictionary[dictionaryPos] = previousByte;
++ if (++dictionaryPos == dictionarySize)
++ dictionaryPos = 0;
++ #else
++ previousByte = outStream[nowPos - rep0];
++ #endif
++ len--;
++ outStream[nowPos++] = previousByte;
++ }
++ while(len != 0 && nowPos < outSize);
++ }
++ }
++ RC_NORMALIZE;
++
++ #ifdef _LZMA_OUT_READ
++ vs->Range = Range;
++ vs->Code = Code;
++ vs->DictionaryPos = dictionaryPos;
++ vs->GlobalPos = globalPos + (UInt32)nowPos;
++ vs->DistanceLimit = distanceLimit;
++ vs->Reps[0] = rep0;
++ vs->Reps[1] = rep1;
++ vs->Reps[2] = rep2;
++ vs->Reps[3] = rep3;
++ vs->State = state;
++ vs->RemainLen = len;
++ vs->TempDictionary[0] = tempDictionary[0];
++ #endif
++
++ #ifdef _LZMA_IN_CB
++ vs->Buffer = Buffer;
++ vs->BufferLim = BufferLim;
++ #else
++ *inSizeProcessed = (SizeT)(Buffer - inStream);
++ #endif
++ *outSizeProcessed = nowPos;
++ return LZMA_RESULT_OK;
++}
+diff -rduNp linux-2.6.22.1.oorig/arch/i386/boot/compressed/LzmaDecode.h linux-2.6.22.1/arch/i386/boot/compressed/LzmaDecode.h
+--- linux-2.6.22.1.oorig/arch/i386/boot/compressed/LzmaDecode.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/arch/i386/boot/compressed/LzmaDecode.h 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,131 @@
++/*
++ LzmaDecode.h
++ LZMA Decoder interface
++
++ LZMA SDK 4.21 Copyright (c) 1999-2005 Igor Pavlov (2005-06-08)
++ http://www.7-zip.org/
++
++ LZMA SDK is licensed under two licenses:
++ 1) GNU Lesser General Public License (GNU LGPL)
++ 2) Common Public License (CPL)
++ It means that you can select one of these two licenses and
++ follow rules of that license.
++
++ SPECIAL EXCEPTION:
++ Igor Pavlov, as the author of this code, expressly permits you to
++ statically or dynamically link your code (or bind by name) to the
++ interfaces of this file without subjecting your linked code to the
++ terms of the CPL or GNU LGPL. Any modifications or additions
++ to this file, however, are subject to the LGPL or CPL terms.
++*/
++
++#ifndef __LZMADECODE_H
++#define __LZMADECODE_H
++
++/* #define _LZMA_IN_CB */
++/* Use callback for input data */
++
++/* #define _LZMA_OUT_READ */
++/* Use read function for output data */
++
++/* #define _LZMA_PROB32 */
++/* It can increase speed on some 32-bit CPUs,
++ but memory usage will be doubled in that case */
++
++/* #define _LZMA_LOC_OPT */
++/* Enable local speed optimizations inside code */
++
++/* #define _LZMA_SYSTEM_SIZE_T */
++/* Use system's size_t. You can use it to enable 64-bit sizes supporting*/
++
++#ifndef UInt32
++#ifdef _LZMA_UINT32_IS_ULONG
++#define UInt32 unsigned long
++#else
++#define UInt32 unsigned int
++#endif
++#endif
++
++#ifndef SizeT
++#ifdef _LZMA_SYSTEM_SIZE_T
++#include <stddef.h>
++#define SizeT size_t
++#else
++#define SizeT UInt32
++#endif
++#endif
++
++#ifdef _LZMA_PROB32
++#define CProb UInt32
++#else
++#define CProb unsigned short
++#endif
++
++#define LZMA_RESULT_OK 0
++#define LZMA_RESULT_DATA_ERROR 1
++
++#ifdef _LZMA_IN_CB
++typedef struct _ILzmaInCallback
++{
++ int (*Read)(void *object, const unsigned char **buffer, SizeT *bufferSize);
++} ILzmaInCallback;
++#endif
++
++#define LZMA_BASE_SIZE 1846
++#define LZMA_LIT_SIZE 768
++
++#define LZMA_PROPERTIES_SIZE 5
++
++typedef struct _CLzmaProperties
++{
++ int lc;
++ int lp;
++ int pb;
++ #ifdef _LZMA_OUT_READ
++ UInt32 DictionarySize;
++ #endif
++}CLzmaProperties;
++
++int LzmaDecodeProperties(CLzmaProperties *propsRes, const unsigned char *propsData, int size);
++
++#define LzmaGetNumProbs(Properties) (LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((Properties)->lc + (Properties)->lp)))
++
++#define kLzmaNeedInitId (-2)
++
++typedef struct _CLzmaDecoderState
++{
++ CLzmaProperties Properties;
++ CProb *Probs;
++
++ #ifdef _LZMA_IN_CB
++ const unsigned char *Buffer;
++ const unsigned char *BufferLim;
++ #endif
++
++ #ifdef _LZMA_OUT_READ
++ unsigned char *Dictionary;
++ UInt32 Range;
++ UInt32 Code;
++ UInt32 DictionaryPos;
++ UInt32 GlobalPos;
++ UInt32 DistanceLimit;
++ UInt32 Reps[4];
++ int State;
++ int RemainLen;
++ unsigned char TempDictionary[4];
++ #endif
++} CLzmaDecoderState;
++
++#ifdef _LZMA_OUT_READ
++#define LzmaDecoderInit(vs) { (vs)->RemainLen = kLzmaNeedInitId; }
++#endif
++
++int LzmaDecode(CLzmaDecoderState *vs,
++ #ifdef _LZMA_IN_CB
++ ILzmaInCallback *inCallback,
++ #else
++ const unsigned char *inStream, SizeT inSize, SizeT *inSizeProcessed,
++ #endif
++ unsigned char *outStream, SizeT outSize, SizeT *outSizeProcessed);
++
++#endif
+diff -rduNp linux-2.6.22.1.oorig/arch/i386/boot/compressed/Makefile linux-2.6.22.1/arch/i386/boot/compressed/Makefile
+--- linux-2.6.22.1.oorig/arch/i386/boot/compressed/Makefile 2007-07-10 20:56:30.000000000 +0200
++++ linux-2.6.22.1/arch/i386/boot/compressed/Makefile 2007-07-24 14:17:46.000000000 +0200
+@@ -4,15 +4,16 @@
+ # create a compressed vmlinux image from the original vmlinux
+ #
+
+-targets := vmlinux vmlinux.bin vmlinux.bin.gz head.o misc.o piggy.o \
+- vmlinux.bin.all vmlinux.relocs
++tragets := head.o lzma_misc.o piggy.o \
++ vmlinux.bin.all vmlinux.relocs \
++ vmlinux vmlinux.bin vmlinux.bin.gz
+ EXTRA_AFLAGS := -traditional
+
+ LDFLAGS_vmlinux := -T
+-CFLAGS_misc.o += -fPIC
++CFLAGS_lzma_misc.o += -fPIC
+ hostprogs-y := relocs
+
+-$(obj)/vmlinux: $(src)/vmlinux.lds $(obj)/head.o $(obj)/misc.o $(obj)/piggy.o FORCE
++$(obj)/vmlinux: $(src)/vmlinux.lds $(obj)/head.o $(obj)/lzma_misc.o $(obj)/piggy.o FORCE
+ $(call if_changed,ld)
+ @:
+
+@@ -33,10 +34,10 @@ $(obj)/vmlinux.bin.all: $(vmlinux.bin.al
+
+ ifdef CONFIG_RELOCATABLE
+ $(obj)/vmlinux.bin.gz: $(obj)/vmlinux.bin.all FORCE
+- $(call if_changed,gzip)
++ $(call if_changed,lzma)
+ else
+ $(obj)/vmlinux.bin.gz: $(obj)/vmlinux.bin FORCE
+- $(call if_changed,gzip)
++ $(call if_changed,lzma)
+ endif
+
+ LDFLAGS_piggy.o := -r --format binary --oformat elf32-i386 -T
+diff -rduNp linux-2.6.22.1.oorig/arch/i386/boot/compressed/lzma_misc.c linux-2.6.22.1/arch/i386/boot/compressed/lzma_misc.c
+--- linux-2.6.22.1.oorig/arch/i386/boot/compressed/lzma_misc.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/arch/i386/boot/compressed/lzma_misc.c 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,290 @@
++/*
++ * lzma_misc.c
++ *
++ * Decompress LZMA compressed vmlinuz
++ * Version 0.9 Copyright (c) Ming-Ching Tiew mctiew@yahoo.com
++ * Program adapted from misc.c for 2.6.20.1 kernel
++ * Please refer to misc.c for authorship and copyright.
++ * Date: 25 March 2007
++ * Source released under GPL
++ */
++
++#undef CONFIG_PARAVIRT
++#include <linux/linkage.h>
++#include <linux/vmalloc.h>
++#include <linux/screen_info.h>
++#include <asm/io.h>
++#include <asm/page.h>
++#include <asm/boot.h>
++
++/* WARNING!!
++ * This code is compiled with -fPIC and it is relocated dynamically
++ * at run time, but no relocation processing is performed.
++ * This means that it is not safe to place pointers in static structures.
++ */
++
++#define OF(args) args
++#define STATIC static
++
++#undef memset
++#undef memcpy
++
++typedef unsigned char uch;
++typedef unsigned short ush;
++typedef unsigned long ulg;
++
++#define WSIZE 0x80000000 /* Window size must be at least 32k,
++ * and a power of two
++ * We don't actually have a window just
++ * a huge output buffer so I report
++ * a 2G windows size, as that should
++ * always be larger than our output buffer.
++ */
++
++static uch *inbuf; /* input buffer */
++static uch *window; /* Sliding window buffer, (and final output buffer) */
++
++static unsigned insize; /* valid bytes in inbuf */
++static unsigned inptr; /* index of next byte to be processed in inbuf */
++
++/* gzip flag byte */
++#define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */
++#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
++#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
++#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
++#define COMMENT 0x10 /* bit 4 set: file comment present */
++#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */
++#define RESERVED 0xC0 /* bit 6,7: reserved */
++
++#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf())
++
++/* Diagnostic functions */
++#ifdef DEBUG
++# define Assert(cond,msg) {if(!(cond)) error(msg);}
++# define Trace(x) fprintf x
++# define Tracev(x) {if (verbose) fprintf x ;}
++# define Tracevv(x) {if (verbose>1) fprintf x ;}
++# define Tracec(c,x) {if (verbose && (c)) fprintf x ;}
++# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;}
++#else
++# define Assert(cond,msg)
++# define Trace(x)
++# define Tracev(x)
++# define Tracevv(x)
++# define Tracec(c,x)
++# define Tracecv(c,x)
++#endif
++
++static int fill_inbuf(void);
++static void error(char *m);
++
++/*
++ * This is set up by the setup-routine at boot-time
++ */
++static unsigned char *real_mode; /* Pointer to real-mode data */
++
++#define RM_EXT_MEM_K (*(unsigned short *)(real_mode + 0x2))
++#ifndef STANDARD_MEMORY_BIOS_CALL
++#define RM_ALT_MEM_K (*(unsigned long *)(real_mode + 0x1e0))
++#endif
++#define RM_SCREEN_INFO (*(struct screen_info *)(real_mode+0))
++
++extern unsigned char input_data[];
++extern int input_len;
++
++static long bytes_out = 0;
++
++static void *memcpy(void *dest, const void *src, unsigned n);
++
++static void putstr(const char *);
++
++static unsigned long free_mem_ptr;
++static unsigned long free_mem_end_ptr;
++
++#define HEAP_SIZE 0x3000
++
++static char *vidmem = (char *)0xb8000;
++static int vidport;
++static int lines, cols;
++
++#ifdef CONFIG_X86_NUMAQ
++void *xquad_portio;
++#endif
++
++static void scroll(void)
++{
++ int i;
++
++ memcpy ( vidmem, vidmem + cols * 2, ( lines - 1 ) * cols * 2 );
++ for ( i = ( lines - 1 ) * cols * 2; i < lines * cols * 2; i += 2 )
++ vidmem[i] = ' ';
++}
++
++static void putstr(const char *s)
++{
++ int x,y,pos;
++ char c;
++
++ x = RM_SCREEN_INFO.orig_x;
++ y = RM_SCREEN_INFO.orig_y;
++
++ while ( ( c = *s++ ) != '\0' ) {
++ if ( c == '\n' ) {
++ x = 0;
++ if ( ++y >= lines ) {
++ scroll();
++ y--;
++ }
++ } else {
++ vidmem [ ( x + cols * y ) * 2 ] = c;
++ if ( ++x >= cols ) {
++ x = 0;
++ if ( ++y >= lines ) {
++ scroll();
++ y--;
++ }
++ }
++ }
++ }
++
++ RM_SCREEN_INFO.orig_x = x;
++ RM_SCREEN_INFO.orig_y = y;
++
++ pos = (x + cols * y) * 2; /* Update cursor position */
++ outb_p(14, vidport);
++ outb_p(0xff & (pos >> 9), vidport+1);
++ outb_p(15, vidport);
++ outb_p(0xff & (pos >> 1), vidport+1);
++}
++
++static void* memcpy(void* dest, const void* src, unsigned n)
++{
++ int i;
++ char *d = (char *)dest, *s = (char *)src;
++
++ for (i=0;i<n;i++) d[i] = s[i];
++ return dest;
++}
++
++/* ===========================================================================
++ * Fill the input buffer. This is called only when the buffer is empty
++ * and at least one byte is really needed.
++ */
++static int fill_inbuf(void)
++{
++ error("ran out of input data");
++ return 0;
++}
++
++/* ===========================================================================
++ */
++static void error(char *x)
++{
++ putstr("\n\n");
++ putstr(x);
++ putstr("\n\n -- System halted");
++
++ while(1); /* Halt */
++}
++
++#define _LZMA_IN_CB
++#include "LzmaDecode.h"
++#include "LzmaDecode.c"
++
++static int read_byte(void *object, const unsigned char **buffer, SizeT *bufferSize);
++
++/*
++ * Do the lzma decompression
++ */
++static int lzma_unzip(uch* output)
++{
++
++ unsigned int i;
++ CLzmaDecoderState state;
++ unsigned int uncompressedSize = 0;
++ unsigned char* p;
++
++ ILzmaInCallback callback;
++ callback.Read = read_byte;
++
++ // lzma args
++ i = get_byte();
++ state.Properties.lc = i % 9, i = i / 9;
++ state.Properties.lp = i % 5, state.Properties.pb = i / 5;
++
++ // skip dictionary size
++ for (i = 0; i < 4; i++)
++ get_byte();
++ // get uncompressed size
++ p= (char*)&uncompressedSize;
++ for (i = 0; i < 4; i++)
++ *p++ = get_byte();
++
++ // skip high order bytes
++ for (i = 0; i < 4; i++)
++ get_byte();
++
++ // Just point it beyond
++ state.Probs = (CProb*) ( free_mem_ptr );
++ // decompress kernel
++ if (LzmaDecode( &state, &callback,
++ (unsigned char*)output, uncompressedSize, &i) == LZMA_RESULT_OK)
++ {
++ if ( i != uncompressedSize )
++ error( "kernel corrupted!\n");
++ bytes_out = i;
++ return 0;
++ }
++ return 1;
++}
++
++
++static int read_byte(void *object, const unsigned char **buffer, SizeT *bufferSize)
++{
++ static unsigned int i = 0;
++ static unsigned char val;
++ *bufferSize = 1;
++ val = get_byte();
++ *buffer = &val;
++ return LZMA_RESULT_OK;
++}
++
++asmlinkage void decompress_kernel(void *rmode, unsigned long end,
++ uch *input_data, unsigned long input_len, uch *output)
++{
++ real_mode = rmode;
++
++ if (RM_SCREEN_INFO.orig_video_mode == 7) {
++ vidmem = (char *) 0xb0000;
++ vidport = 0x3b4;
++ } else {
++ vidmem = (char *) 0xb8000;
++ vidport = 0x3d4;
++ }
++
++ lines = RM_SCREEN_INFO.orig_video_lines;
++ cols = RM_SCREEN_INFO.orig_video_cols;
++
++ window = output; /* Output buffer (Normally at 1M) */
++ free_mem_ptr = end; /* Heap */
++ free_mem_end_ptr = end + HEAP_SIZE;
++ inbuf = input_data; /* Input buffer */
++ insize = input_len;
++ inptr = 0;
++
++ if ((u32)output & (CONFIG_PHYSICAL_ALIGN -1))
++ error("Destination address not CONFIG_PHYSICAL_ALIGN aligned");
++ if (end > ((-__PAGE_OFFSET-(512 <<20)-1) & 0x7fffffff))
++ error("Destination address too large");
++#ifndef CONFIG_RELOCATABLE
++ if ((u32)output != LOAD_PHYSICAL_ADDR)
++ error("Wrong destination address");
++#endif
++ if( lzma_unzip(output) != 0 )
++ {
++ error("inflate error\n");
++ }
++ putstr("Ok, booting the kernel.\n");
++
++ return;
++}
+diff -rduNp linux-2.6.22.1.oorig/arch/i386/boot/compressed/vmlinux.scr linux-2.6.22.1/arch/i386/boot/compressed/vmlinux.scr
+--- linux-2.6.22.1.oorig/arch/i386/boot/compressed/vmlinux.scr 2007-07-10 20:56:30.000000000 +0200
++++ linux-2.6.22.1/arch/i386/boot/compressed/vmlinux.scr 2007-07-24 14:17:46.000000000 +0200
+@@ -3,8 +3,8 @@ SECTIONS
+ .data.compressed : {
+ input_len = .;
+ LONG(input_data_end - input_data) input_data = .;
++ output_len = . + 5;
+ *(.data)
+- output_len = . - 4;
+ input_data_end = .;
+ }
+ }
+diff -rduNp linux-2.6.22.1.oorig/drivers/block/Kconfig linux-2.6.22.1/drivers/block/Kconfig
+--- linux-2.6.22.1.oorig/drivers/block/Kconfig 2007-07-10 20:56:30.000000000 +0200
++++ linux-2.6.22.1/drivers/block/Kconfig 2007-07-24 14:17:46.000000000 +0200
+@@ -406,6 +406,47 @@ config BLK_DEV_RAM_BLOCKSIZE
+ setups function - apparently needed by the rd_load_image routine
+ that supposes the filesystem in the image uses a 1024 blocksize.
+
++config LZMA_INITRD
++ boolean "Allow LZMA compression on initrd"
++ depends on BLK_DEV_INITRD=y
++ default "y"
++ help
++ Use lzma compression on initrd, example 'lzma e initrd initrd.7z -d16'.
++ If you have sufficient memory, you could compress using bigger dictionary size,
++ 'lzma e initrd initrd.7z'.
++
++config LZMA_INITRD_KMALLOC_ONLY
++ boolean "Use only kmalloc, do not use vmalloc on lzma initrd"
++ depends on LZMA_INITRD=y
++ default "n"
++ help
++ Set to y if you do not want to use vmalloc, ie use only kmalloc.
++
++config LZMA_INITRAM_FS
++ boolean "Allow LZMA compression on initramfs"
++ depends on BLK_DEV_RAM=y
++ default "y"
++ help
++ Use lzma compression on initramfs, example 'lzma e initramfs.cpio initramfs.cpio.lzma'.
++
++config LZMA_INITRAM_FS_SMALLMEM
++ boolean "Use lzma compression with small dictonary size."
++ depends on LZMA_INITRAM_FS=y
++ default "y"
++ help
++ Use lzma compression on initramfs with small dictionary size, example
++ 'lzma e initramfs.cpio initramfs.cpio.lzma -d16'.
++ Affects only the initramfs.cpio in the ~usr directory, which is compiled into
++ the kernel. If you prepared initramfs.cpio for use with bootloader, you would
++ need to specify the commandline options (-d16) yourself.
++
++config LZMA_INITRAM_FS_KMALLOC_ONLY
++ boolean "Use only kmalloc, do not use vmalloc on lzma initramfs"
++ depends on LZMA_INITRAM_FS=y
++ default "n"
++ help
++ Set to y if you do not want to use vmalloc, ie use only kmalloc.
++
+ config CDROM_PKTCDVD
+ tristate "Packet writing on CD/DVD media"
+ depends on !UML
+diff -rduNp linux-2.6.22.1.oorig/fs/Kconfig linux-2.6.22.1/fs/Kconfig
+--- linux-2.6.22.1.oorig/fs/Kconfig 2007-07-10 20:56:30.000000000 +0200
++++ linux-2.6.22.1/fs/Kconfig 2007-07-24 14:17:46.000000000 +0200
+@@ -1367,6 +1367,71 @@ config CRAMFS
+
+ If unsure, say N.
+
++config SQUASHFS
++ tristate "SquashFS 3.2 - Squashed file system support"
++ select ZLIB_INFLATE
++ help
++ Saying Y here includes support for SquashFS 3.2 (a Compressed Read-Only File
++ System). Squashfs is a highly compressed read-only filesystem for Linux.
++ It uses zlib compression to compress both files, inodes and directories.
++ Inodes in the system are very small and all blocks are packed to minimise
++ data overhead. Block sizes greater than 4K are supported up to a maximum of 64K.
++ SquashFS 3.1 supports 64 bit filesystems and files (larger than 4GB), full
++ uid/gid information, hard links and timestamps.
++
++ Squashfs is intended for general read-only filesystem use, for archival
++ use (i.e. in cases where a .tar.gz file may be used), and in embedded
++ systems where low overhead is needed. Further information and filesystem tools
++ are available from http://squashfs.sourceforge.net.
++
++ If you want to compile this as a module ( = code which can be
++ inserted in and removed from the running kernel whenever you want),
++ say M here and read <file:Documentation/modules.txt>. The module
++ will be called squashfs. Note that the root file system (the one
++ containing the directory /) cannot be compiled as a module.
++
++ If unsure, say N.
++
++config SQUASHFS_EMBEDDED
++
++ bool "Additional options for memory-constrained systems"
++ depends on SQUASHFS
++ default n
++ help
++ Saying Y here allows you to specify cache sizes and how Squashfs
++ allocates memory. This is only intended for memory constrained
++ systems.
++
++ If unsure, say N.
++
++config SQUASHFS_FRAGMENT_CACHE_SIZE
++ int "Number of fragments cached" if SQUASHFS_EMBEDDED
++ depends on SQUASHFS
++ default "3"
++ help
++ By default SquashFS caches the last 3 fragments read from
++ the filesystem. Increasing this amount may mean SquashFS
++ has to re-read fragments less often from disk, at the expense
++ of extra system memory. Decreasing this amount will mean
++ SquashFS uses less memory at the expense of extra reads from disk.
++
++ Note there must be at least one cached fragment. Anything
++ much more than three will probably not make much difference.
++
++config SQUASHFS_VMALLOC
++ bool "Use Vmalloc rather than Kmalloc" if SQUASHFS_EMBEDDED
++ depends on SQUASHFS
++ default n
++ help
++ By default SquashFS uses kmalloc to obtain fragment cache memory.
++ Kmalloc memory is the standard kernel allocator, but it can fail
++ on memory constrained systems. Because of the way Vmalloc works,
++ Vmalloc can succeed when kmalloc fails. Specifying this option
++ will make SquashFS always use Vmalloc to allocate the
++ fragment cache memory.
++
++ If unsure, say N.
++
+ config VXFS_FS
+ tristate "FreeVxFS file system support (VERITAS VxFS(TM) compatible)"
+ depends on BLOCK
+@@ -2072,3 +2137,4 @@ source "fs/dlm/Kconfig"
+
+ endmenu
+
++source "fs/aufs/Kconfig"
+diff -rduNp linux-2.6.22.1.oorig/fs/Makefile linux-2.6.22.1/fs/Makefile
+--- linux-2.6.22.1.oorig/fs/Makefile 2007-07-10 20:56:30.000000000 +0200
++++ linux-2.6.22.1/fs/Makefile 2007-07-24 14:17:46.000000000 +0200
+@@ -72,6 +72,7 @@ obj-$(CONFIG_JBD) += jbd/
+ obj-$(CONFIG_JBD2) += jbd2/
+ obj-$(CONFIG_EXT2_FS) += ext2/
+ obj-$(CONFIG_CRAMFS) += cramfs/
++obj-$(CONFIG_SQUASHFS) += squashfs/
+ obj-$(CONFIG_RAMFS) += ramfs/
+ obj-$(CONFIG_HUGETLBFS) += hugetlbfs/
+ obj-$(CONFIG_CODA_FS) += coda/
+@@ -118,3 +119,4 @@ obj-$(CONFIG_HPPFS) += hppfs/
+ obj-$(CONFIG_DEBUG_FS) += debugfs/
+ obj-$(CONFIG_OCFS2_FS) += ocfs2/
+ obj-$(CONFIG_GFS2_FS) += gfs2/
++obj-$(CONFIG_AUFS) += aufs/
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/Kconfig linux-2.6.22.1/fs/aufs/Kconfig
+--- linux-2.6.22.1.oorig/fs/aufs/Kconfig 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/Kconfig 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,73 @@
++config AUFS
++ tristate "Another unionfs"
++ 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
++comment "These options are generated automatically for "#UTS_RELEASE
++
++config AUFS_FAKE_DM
++ bool "Use simplified (fake) nameidata"
++ depends on AUFS
++ default y
++ help
++ Faking nameidata (VFS internal data), you can get better performance
++ in some cases.
++
++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_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_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
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/Makefile linux-2.6.22.1/fs/aufs/Makefile
+--- linux-2.6.22.1.oorig/fs/aufs/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/Makefile 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,18 @@
++# AUFS Makefile for the Linux 2.6.16 and later
++# $Id: Makefile,v 1.29 2007/04/23 00:59:50 sfjro Exp $
++
++obj-$(CONFIG_AUFS) += aufs.o
++aufs-y := module.o super.o sbinfo.o xino.o \
++ branch.o cpup.o whout.o plink.o wkq.o dcsub.o vfsub.o \
++ opts.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_AUFS_SYSAUFS) += sysaufs.o
++aufs-$(CONFIG_AUFS_HINOTIFY) += hinotify.o
++aufs-$(CONFIG_AUFS_EXPORT) += export.o
++#aufs-$(CONFIG_DEBUGFS) += dbgfs.o
++aufs-$(CONFIG_AUFS_DEBUG) += debug.o
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/aufs.h linux-2.6.22.1/fs/aufs/aufs.h
+--- linux-2.6.22.1.oorig/fs/aufs/aufs.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/aufs.h 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,64 @@
++/*
++ * 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: aufs.h,v 1.24 2007/05/14 03:41:51 sfjro Exp $ */
++
++#ifndef __AUFS_H__
++#define __AUFS_H__
++
++#ifdef __KERNEL__
++
++#include <linux/version.h>
++
++/* limited support before 2.6.16, curretly 2.6.15 only. */
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
++#define atomic_long_t atomic_t
++#define atomic_long_set atomic_set
++#define timespec_to_ns(ts) ({(long long)(ts)->tv_sec;})
++#define D_CHILD d_child
++#else
++#define D_CHILD d_u.d_child
++#endif
++
++/* ---------------------------------------------------------------------- */
++
++#include "debug.h"
++
++#include "branch.h"
++#include "cpup.h"
++#include "dcsub.h"
++#include "dentry.h"
++#include "dir.h"
++#include "file.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"
++
++#if defined(CONFIG_AUFS_MODULE) && !defined(CONFIG_AUFS_KSIZE_PATCH)
++#define ksize(p) (-1U)
++#endif
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_H__ */
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/branch.c linux-2.6.22.1/fs/aufs/branch.c
+--- linux-2.6.22.1.oorig/fs/aufs/branch.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/branch.c 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,818 @@
++/*
++ * 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: branch.c,v 1.49 2007/05/14 03:38:23 sfjro Exp $ */
++
++//#include <linux/fs.h>
++//#include <linux/namei.h>
++#include "aufs.h"
++
++static void free_branch(struct aufs_branch *br)
++{
++ TraceEnter();
++
++ if (br->br_xino)
++ fput(br->br_xino);
++ dput(br->br_wh);
++ dput(br->br_plink);
++ mntput(br->br_mnt);
++ DEBUG_ON(br_count(br) || atomic_read(&br->br_wh_running));
++ kfree(br);
++}
++
++/*
++ * frees all branches
++ */
++void free_branches(struct aufs_sbinfo *sbinfo)
++{
++ aufs_bindex_t bmax;
++ struct aufs_branch **br;
++
++ TraceEnter();
++ 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 find_brindex(struct super_block *sb, aufs_bindex_t br_id)
++{
++ aufs_bindex_t bindex, bend;
++
++ TraceEnter();
++
++ bend = sbend(sb);
++ for (bindex = 0; bindex <= bend; bindex++)
++ if (sbr_id(sb, bindex) == br_id)
++ return bindex;
++ return -1;
++}
++
++/*
++ * test if the @br is readonly or not.
++ */
++int br_rdonly(struct aufs_branch *br)
++{
++ return ((br->br_mnt->mnt_sb->s_flags & MS_RDONLY)
++ || !br_writable(br->br_perm))
++ ? -EROFS : 0;
++}
++
++/*
++ * returns writable branch index, otherwise an error.
++ * todo: customizable writable-branch-policy
++ */
++static int find_rw_parent(struct dentry *dentry, aufs_bindex_t bend)
++{
++ int err;
++ aufs_bindex_t bindex, candidate;
++ struct super_block *sb;
++ struct dentry *parent, *hidden_parent;
++
++ err = bend;
++ sb = dentry->d_sb;
++ parent = dget_parent(dentry);
++#if 1 // branch policy
++ hidden_parent = au_h_dptr_i(parent, bend);
++ if (hidden_parent && !br_rdonly(stobr(sb, bend)))
++ goto out; /* success */
++#endif
++
++ candidate = -1;
++ for (bindex = dbstart(parent); bindex <= bend; bindex++) {
++ hidden_parent = au_h_dptr_i(parent, bindex);
++ if (hidden_parent && !br_rdonly(stobr(sb, bindex))) {
++#if 0 // branch policy
++ if (candidate == -1)
++ candidate = bindex;
++ if (!au_test_perm(hidden_parent->d_inode, MAY_WRITE))
++ return bindex;
++#endif
++ err = bindex;
++ goto out; /* success */
++ }
++ }
++#if 0 // branch policy
++ err = candidate;
++ if (candidate != -1)
++ goto out; /* success */
++#endif
++ err = -EROFS;
++
++ out:
++ dput(parent);
++ return err;
++}
++
++int find_rw_br(struct super_block *sb, aufs_bindex_t bend)
++{
++ aufs_bindex_t bindex;
++
++ for (bindex = bend; bindex >= 0; bindex--)
++ if (!br_rdonly(stobr(sb, bindex)))
++ return bindex;
++ return -EROFS;
++}
++
++int find_rw_parent_br(struct dentry *dentry, aufs_bindex_t bend)
++{
++ int err;
++
++ err = find_rw_parent(dentry, bend);
++ if (err >= 0)
++ return err;
++ return find_rw_br(dentry->d_sb, bend);
++}
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * test if two hidden_dentries have overlapping branches.
++ */
++//todo: try is_subdir()
++static int do_is_overlap(struct super_block *sb, struct dentry *hidden_d1,
++ struct dentry *hidden_d2)
++{
++ struct dentry *d;
++
++ d = hidden_d1;
++ do {
++ if (unlikely(d == hidden_d2))
++ return 1;
++ d = d->d_parent; // dget_parent()
++ } while (!IS_ROOT(d));
++
++ return (d == hidden_d2);
++}
++
++#if defined(CONFIG_BLK_DEV_LOOP) || defined(CONFIG_BLK_DEV_LOOP_MODULE)
++#include <linux/loop.h>
++static int is_overlap_loopback(struct super_block *sb, struct dentry *hidden_d1,
++ struct dentry *hidden_d2)
++{
++ struct inode *hidden_inode;
++ struct loop_device *l;
++
++ hidden_inode = hidden_d1->d_inode;
++ if (MAJOR(hidden_inode->i_sb->s_dev) != LOOP_MAJOR)
++ return 0;
++
++ l = hidden_inode->i_sb->s_bdev->bd_disk->private_data;
++ hidden_d1 = l->lo_backing_file->f_dentry;
++ if (unlikely(hidden_d1->d_sb == sb))
++ return 1;
++ return do_is_overlap(sb, hidden_d1, hidden_d2);
++}
++#else
++#define is_overlap_loopback(sb, hidden_d1, hidden_d2) 0
++#endif
++
++static int is_overlap(struct super_block *sb, struct dentry *hidden_d1,
++ struct dentry *hidden_d2)
++{
++ LKTRTrace("d1 %.*s, d2 %.*s\n", DLNPair(hidden_d1), DLNPair(hidden_d2));
++ if (unlikely(hidden_d1 == hidden_d2))
++ return 1;
++ return do_is_overlap(sb, hidden_d1, hidden_d2)
++ || do_is_overlap(sb, hidden_d2, hidden_d1)
++ || is_overlap_loopback(sb, hidden_d1, hidden_d2)
++ || is_overlap_loopback(sb, hidden_d2, hidden_d1);
++}
++
++/* ---------------------------------------------------------------------- */
++
++static int init_br_wh(struct super_block *sb, aufs_bindex_t bindex,
++ struct aufs_branch *br, int new_perm,
++ struct dentry *h_root, struct vfsmount *h_mnt)
++{
++ int err, old_perm;
++ struct inode *dir = sb->s_root->d_inode,
++ *h_dir = h_root->d_inode;
++ const int new = (bindex < 0);
++
++ LKTRTrace("b%d, new_perm %d\n", bindex, new_perm);
++
++ if (new)
++ hi_lock_parent(h_dir);
++ else
++ hdir_lock(h_dir, dir, bindex);
++
++ br_wh_write_lock(br);
++ old_perm = br->br_perm;
++ br->br_perm = new_perm;
++ err = init_wh(h_root, br, au_do_nfsmnt(h_mnt), sb);
++ br->br_perm = old_perm;
++ br_wh_write_unlock(br);
++
++ if (new)
++ i_unlock(h_dir);
++ else
++ hdir_unlock(h_dir, dir, bindex);
++
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * returns a newly allocated branch. @new_nbranch is a number of branches
++ * after adding a branch.
++ */
++static struct aufs_branch *alloc_addbr(struct super_block *sb, int new_nbranch)
++{
++ struct aufs_branch **branchp, *add_branch;
++ int sz;
++ void *p;
++ struct dentry *root;
++ struct inode *inode;
++ struct aufs_hinode *hinodep;
++ struct aufs_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 = stosi(sb)->si_branch;
++ branchp = au_kzrealloc(p, sz, sizeof(*branchp) * new_nbranch,
++ GFP_KERNEL);
++ //if (LktrCond) branchp = NULL;
++ if (unlikely(!branchp))
++ goto out;
++ stosi(sb)->si_branch = branchp;
++
++ sz = sizeof(*hdentryp) * (new_nbranch - 1);
++ if (unlikely(!sz))
++ sz = sizeof(*hdentryp);
++ p = dtodi(root)->di_hdentry;
++ hdentryp = au_kzrealloc(p, sz, sizeof(*hdentryp) * new_nbranch,
++ GFP_KERNEL);
++ //if (LktrCond) hdentryp = NULL;
++ if (unlikely(!hdentryp))
++ goto out;
++ dtodi(root)->di_hdentry = hdentryp;
++
++ sz = sizeof(*hinodep) * (new_nbranch - 1);
++ if (unlikely(!sz))
++ sz = sizeof(*hinodep);
++ p = itoii(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;
++ itoii(inode)->ii_hinode = hinodep;
++ return add_branch; /* success */
++
++ out:
++ kfree(add_branch);
++ TraceErr(-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(br_writable(brperm) && IS_RDONLY(inode))) {
++ Err("write permission for readonly fs or inode, %s\n", path);
++ err = -EINVAL;
++ }
++
++ TraceErr(err);
++ return err;
++}
++
++/*
++ * retunrs,,,
++ * 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 opt_add *add, int remount)
++{
++ int err;
++ struct dentry *root;
++ struct inode *inode, *hidden_inode;
++ aufs_bindex_t bend, bindex;
++
++ LKTRTrace("%s, remo%d\n", add->path, remount);
++
++ root = sb->s_root;
++ if (unlikely(au_find_dbindex(root, add->nd.dentry) != -1)) {
++ err = 1;
++ if (!remount) {
++ err = -EINVAL;
++ Err("%s duplicated\n", add->path);
++ }
++ goto out;
++ }
++
++ err = -ENOSPC; //-E2BIG;
++ bend = sbend(sb);
++ //if (LktrCond) bend = AUFS_BRANCH_MAX;
++ if (unlikely(AUFS_BRANCH_MAX <= add->bindex
++ || AUFS_BRANCH_MAX - 1 <= bend)) {
++ Err("number of branches exceeded %s\n", add->path);
++ goto out;
++ }
++
++ err = -EDOM;
++ if (unlikely(add->bindex < 0 || bend + 1 < add->bindex)) {
++ Err("bad index %d\n", add->bindex);
++ goto out;
++ }
++
++ inode = add->nd.dentry->d_inode;
++ DEBUG_ON(!inode || !S_ISDIR(inode->i_mode));
++ err = -ENOENT;
++ if (unlikely(!inode->i_nlink)) {
++ Err("no existence %s\n", add->path);
++ goto out;
++ }
++
++ err = -EINVAL;
++ if (unlikely(inode->i_sb == sb)) {
++ Err("%s must be outside\n", add->path);
++ goto out;
++ }
++
++#if 1 //ndef CONFIG_AUFS_ROBR
++ if (unlikely(au_is_aufs(inode->i_sb)
++ || !strcmp(au_sbtype(inode->i_sb), "unionfs"))) {
++ Err("nested " AUFS_NAME " %s\n", add->path);
++ goto out;
++ }
++#endif
++
++#ifdef AuNoNfsBranch
++ if (unlikely(au_is_nfs(inode->i_sb))) {
++ Err(AuNoNfsBranchMsg ". %s\n", add->path);
++ goto out;
++ }
++#endif
++
++ err = test_br(sb, add->nd.dentry->d_inode, add->perm, add->path);
++ if (unlikely(err))
++ goto out;
++
++ if (unlikely(bend == -1))
++ return 0; /* success */
++
++ hidden_inode = au_h_dptr(root)->d_inode;
++ if (unlikely(au_flag_test(sb, AuFlag_WARN_PERM)
++ && ((hidden_inode->i_mode & S_IALLUGO)
++ != (inode->i_mode & S_IALLUGO)
++ || hidden_inode->i_uid != inode->i_uid
++ || hidden_inode->i_gid != inode->i_gid)))
++ Warn("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),
++ hidden_inode->i_uid, hidden_inode->i_gid,
++ (hidden_inode->i_mode & S_IALLUGO));
++
++ err = -EINVAL;
++ for (bindex = 0; bindex <= bend; bindex++)
++ if (unlikely(is_overlap(sb, add->nd.dentry,
++ au_h_dptr_i(root, bindex)))) {
++ Err("%s is overlapped\n", add->path);
++ goto out;
++ }
++ err = 0;
++
++ out:
++ TraceErr(err);
++ return err;
++}
++
++int br_add(struct super_block *sb, struct opt_add *add, int remount)
++{
++ int err, sz;
++ aufs_bindex_t bend, add_bindex;
++ struct dentry *root;
++ struct aufs_iinfo *iinfo;
++ struct aufs_sbinfo *sbinfo;
++ struct aufs_dinfo *dinfo;
++ struct inode *root_inode;
++ unsigned long long maxb;
++ struct aufs_branch **branchp, *add_branch;
++ struct aufs_hdentry *hdentryp;
++ struct aufs_hinode *hinodep;
++
++ LKTRTrace("b%d, %s, 0x%x, %.*s\n", add->bindex, add->path,
++ add->perm, DLNPair(add->nd.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 (unlikely(err))
++ return 0; /* success */
++
++ bend = sbend(sb);
++ add_branch = alloc_addbr(sb, bend + 2);
++ err = PTR_ERR(add_branch);
++ if (IS_ERR(add_branch))
++ goto out;
++
++ err = 0;
++ rw_init_nolock(&add_branch->br_wh_rwsem);
++ add_branch->br_wh = add_branch->br_plink = NULL;
++ if (unlikely(br_writable(add->perm))) {
++ err = init_br_wh(sb, /*bindex*/-1, add_branch, add->perm,
++ add->nd.dentry, add->nd.mnt);
++ if (unlikely(err)) {
++ kfree(add_branch);
++ goto out;
++ }
++ }
++ add_branch->br_xino = NULL;
++ add_branch->br_mnt = mntget(add->nd.mnt);
++ atomic_set(&add_branch->br_wh_running, 0);
++ add_branch->br_id = new_br_id(sb);
++ add_branch->br_perm = add->perm;
++ atomic_set(&add_branch->br_count, 0);
++
++ sbinfo = stosi(sb);
++ dinfo = dtodi(root);
++ iinfo = itoii(root_inode);
++
++ add_bindex = add->bindex;
++ sz = sizeof(*(sbinfo->si_branch)) * (bend + 1 - add_bindex);
++ branchp = sbinfo->si_branch + add_bindex;
++ memmove(branchp + 1, branchp, sz);
++ *branchp = add_branch;
++ sz = sizeof(*hdentryp) * (bend + 1 - add_bindex);
++ hdentryp = dinfo->di_hdentry + add_bindex;
++ memmove(hdentryp + 1, hdentryp, sz);
++ hdentryp->hd_dentry = NULL;
++ sz = sizeof(*hinodep) * (bend + 1 - add_bindex);
++ hinodep = iinfo->ii_hinode + add_bindex;
++ memmove(hinodep + 1, hinodep, sz);
++ hinodep->hi_inode = NULL;
++ hinodep->hi_notify = NULL;
++
++ sbinfo->si_bend++;
++ dinfo->di_bend++;
++ iinfo->ii_bend++;
++ if (unlikely(bend == -1)) {
++ dinfo->di_bstart = 0;
++ iinfo->ii_bstart = 0;
++ }
++ set_h_dptr(root, add_bindex, dget(add->nd.dentry));
++ set_h_iptr(root_inode, add_bindex, igrab(add->nd.dentry->d_inode), 0);
++ if (!add_bindex)
++ au_cpup_attr_all(root_inode);
++ else
++ au_add_nlink(root_inode, add->nd.dentry->d_inode);
++ maxb = add->nd.dentry->d_sb->s_maxbytes;
++ if (sb->s_maxbytes < maxb)
++ sb->s_maxbytes = maxb;
++
++ if (au_flag_test(sb, AuFlag_XINO)) {
++ struct file *base_file = stobr(sb, 0)->br_xino;
++ if (!add_bindex)
++ base_file = stobr(sb, 1)->br_xino;
++ err = xino_init(sb, add_bindex, base_file, /*do_test*/1);
++ if (unlikely(err)) {
++ DEBUG_ON(add_branch->br_xino);
++ Err("ignored xino err %d, force noxino\n", err);
++ err = 0;
++ au_flag_clr(sb, AuFlag_XINO);
++ }
++ }
++
++ out:
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * test if the branch is deletable or not.
++ */
++static int test_children_busy(struct dentry *root, aufs_bindex_t bindex)
++{
++ int err, i, j, sigen;
++ struct au_dcsub_pages dpages;
++
++ LKTRTrace("b%d\n", bindex);
++ SiMustWriteLock(root->d_sb);
++ DiMustWriteLock(root);
++
++ err = au_dpages_init(&dpages, GFP_KERNEL);
++ if (unlikely(err))
++ goto out;
++ err = au_dcsub_pages(&dpages, root, NULL, NULL);
++ if (unlikely(err))
++ goto out_dpages;
++
++ sigen = au_sigen(root->d_sb);
++ DiMustNoWaiters(root);
++ IiMustNoWaiters(root->d_inode);
++ di_write_unlock(root);
++ for (i = 0; !err && i < dpages.ndpage; i++) {
++ struct au_dpage *dpage;
++ dpage = dpages.dpages + i;
++ for (j = 0; !err && j < dpage->ndentry; j++) {
++ struct dentry *d;
++
++ d = dpage->dentries[j];
++ if (au_digen(d) == sigen)
++ di_read_lock_child(d, AUFS_I_RLOCK);
++ else {
++ di_write_lock_child(d);
++ err = au_reval_dpath(d, sigen);
++ if (!err)
++ di_downgrade_lock(d, AUFS_I_RLOCK);
++ else {
++ di_write_unlock(d);
++ break;
++ }
++ }
++
++ if (au_h_dptr_i(d, bindex)
++ && (!S_ISDIR(d->d_inode->i_mode)
++ || dbstart(d) == dbend(d)))
++ err = -EBUSY;
++ di_read_unlock(d, AUFS_I_RLOCK);
++ if (err)
++ LKTRTrace("%.*s\n", DLNPair(d));
++ }
++ }
++ di_write_lock_child(root); /* aufs_write_lock() calls ..._child() */
++
++ out_dpages:
++ au_dpages_free(&dpages);
++ out:
++ TraceErr(err);
++ return err;
++}
++
++int br_del(struct super_block *sb, struct opt_del *del, int remount)
++{
++ int err, do_wh, rerr;
++ struct dentry *root;
++ struct inode *inode, *hidden_dir;
++ aufs_bindex_t bindex, bend, br_id;
++ struct aufs_sbinfo *sbinfo;
++ struct aufs_dinfo *dinfo;
++ struct aufs_iinfo *iinfo;
++ struct aufs_branch *br;
++
++ LKTRTrace("%s, %.*s\n", del->path, DLNPair(del->h_root));
++ SiMustWriteLock(sb);
++ root = sb->s_root;
++ DiMustWriteLock(root);
++ inode = root->d_inode;
++ IiMustWriteLock(inode);
++
++ bindex = au_find_dbindex(root, del->h_root);
++ if (unlikely(bindex < 0)) {
++ if (remount)
++ return 0; /* success */
++ err = -ENOENT;
++ Err("%s no such branch\n", del->path);
++ goto out;
++ }
++ LKTRTrace("bindex b%d\n", bindex);
++
++ err = -EBUSY;
++ bend = sbend(sb);
++ br = stobr(sb, bindex);
++ if (unlikely(!bend || br_count(br))) {
++ LKTRTrace("bend %d, br_count %d\n", bend, br_count(br));
++ goto out;
++ }
++
++ do_wh = 0;
++ hidden_dir = del->h_root->d_inode;
++ if (unlikely(br->br_wh || br->br_plink)) {
++#if 0
++ /* 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_wh = br->br_plink = 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;
++ sbinfo = stosi(sb);
++ dinfo = dtodi(root);
++ iinfo = itoii(inode);
++
++ dput(au_h_dptr_i(root, bindex));
++ aufs_hiput(iinfo->ii_hinode + bindex);
++ br_id = br->br_id;
++ free_branch(br);
++
++ //todo: realloc and shrink memeory
++ if (bindex < bend) {
++ const aufs_bindex_t n = bend - bindex;
++ struct aufs_branch **brp;
++ struct aufs_hdentry *hdp;
++ struct aufs_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;
++ iinfo->ii_hinode[0 + bend].hi_notify = NULL;
++
++ sbinfo->si_bend--;
++ dinfo->di_bend--;
++ iinfo->ii_bend--;
++ if (!bindex)
++ au_cpup_attr_all(inode);
++ else
++ au_sub_nlink(inode, del->h_root->d_inode);
++ if (au_flag_test(sb, AuFlag_PLINK))
++ half_refresh_plink(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 = 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)
++ Warn("failed re-creating base whiteout, %s. (%d)\n",
++ del->path, rerr);
++ out:
++ TraceErr(err);
++ return err;
++}
++
++static int do_need_sigen_inc(int a, int b)
++{
++ return (br_whable(a) && !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 br_mod(struct super_block *sb, struct opt_mod *mod, int remount,
++ int *do_update)
++{
++ int err;
++ struct dentry *root;
++ aufs_bindex_t bindex;
++ struct aufs_branch *br;
++ struct inode *hidden_dir;
++
++ LKTRTrace("%s, %.*s, 0x%x\n",
++ mod->path, DLNPair(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 (unlikely(bindex < 0)) {
++ if (remount)
++ return 0; /* success */
++ err = -ENOENT;
++ Err("%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 = stobr(sb, bindex);
++ if (unlikely(br->br_perm == mod->perm))
++ return 0; /* success */
++
++ if (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_wh = br->br_plink = NULL;
++#endif
++
++ if (!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 sbinfo is locked
++ //file_list_lock();
++ list_for_each_entry(file, &sb->s_files, f_u.fu_list) {
++ LKTRTrace("%.*s\n", DLNPair(file->f_dentry));
++ fi_read_lock(file);
++ if (!S_ISREG(file->f_dentry->d_inode->i_mode)
++ || !(file->f_mode & FMODE_WRITE)
++ || fbstart(file) != bindex) {
++ FiMustNoWaiters(file);
++ fi_read_unlock(file);
++ continue;
++ }
++
++ // todo: already flushed?
++ hf = au_h_fptr(file);
++ hf->f_flags = au_file_roflags(hf->f_flags);
++ hf->f_mode &= ~FMODE_WRITE;
++ FiMustNoWaiters(file);
++ fi_read_unlock(file);
++ }
++ //file_list_unlock();
++
++ /* aufs_write_lock() calls ..._child() */
++ di_write_lock_child(root);
++#endif
++ }
++ }
++
++ *do_update |= need_sigen_inc(br->br_perm, mod->perm);
++ br->br_perm = mod->perm;
++ return err; /* success */
++
++ out:
++ TraceErr(err);
++ return err;
++}
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/branch.h linux-2.6.22.1/fs/aufs/branch.h
+--- linux-2.6.22.1.oorig/fs/aufs/branch.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/branch.h 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,235 @@
++/*
++ * 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: branch.h,v 1.30 2007/05/14 03:41:51 sfjro Exp $ */
++
++#ifndef __AUFS_BRANCH_H__
++#define __AUFS_BRANCH_H__
++
++#ifdef __KERNEL__
++
++#include <linux/fs.h>
++#include <linux/mount.h>
++#include <linux/version.h>
++#include <linux/aufs_type.h>
++#include "misc.h"
++#include "super.h"
++
++/* protected by superblock rwsem */
++struct aufs_branch {
++ struct file *br_xino;
++ readf_t br_xino_read;
++ writef_t br_xino_write;
++
++ aufs_bindex_t br_id;
++
++ int br_perm;
++ struct vfsmount *br_mnt;
++ atomic_t br_count;
++
++ /* whiteout base */
++ struct aufs_rwsem br_wh_rwsem;
++ struct dentry *br_wh;
++ atomic_t br_wh_running;
++
++ /* pseudo-link dir */
++ struct dentry *br_plink;
++};
++
++/* ---------------------------------------------------------------------- */
++
++/* 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 br_writable(int brperm)
++{
++ return (brperm == AuBr_RW
++ || brperm == AuBr_RWNoLinkWH);
++}
++
++static inline int br_whable(int brperm)
++{
++ return (brperm == AuBr_RW
++ || brperm == AuBr_ROWH
++ || brperm == AuBr_RRWH);
++}
++
++static inline int br_linkable_wh(int brperm)
++{
++ return (brperm == AuBr_RW);
++}
++
++/* ---------------------------------------------------------------------- */
++
++#define _AuNoNfsBranchMsg "NFS branch is not supported"
++#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,15)
++#define AuNoNfsBranch
++#define AuNoNfsBranchMsg _AuNoNfsBranchMsg
++#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19) \
++ && !defined(CONFIG_AUFS_LHASH_PATCH)
++#define AuNoNfsBranch
++#define AuNoNfsBranchMsg _AuNoNfsBranchMsg \
++ ", try lhash.patch and CONFIG_AUFS_LHASH_PATCH"
++#endif
++
++/* ---------------------------------------------------------------------- */
++
++struct aufs_sbinfo;
++void free_branches(struct aufs_sbinfo *sinfo);
++int br_rdonly(struct aufs_branch *br);
++int find_brindex(struct super_block *sb, aufs_bindex_t br_id);
++int find_rw_br(struct super_block *sb, aufs_bindex_t bend);
++int find_rw_parent_br(struct dentry *dentry, aufs_bindex_t bend);
++struct opt_add;
++int br_add(struct super_block *sb, struct opt_add *add, int remount);
++struct opt_del;
++int br_del(struct super_block *sb, struct opt_del *del, int remount);
++struct opt_mod;
++int br_mod(struct super_block *sb, struct opt_mod *mod, int remount,
++ int *do_update);
++
++/* ---------------------------------------------------------------------- */
++
++static inline int br_count(struct aufs_branch *br)
++{
++ return atomic_read(&br->br_count);
++}
++
++static inline void br_get(struct aufs_branch *br)
++{
++ atomic_inc(&br->br_count);
++}
++
++static inline void br_put(struct aufs_branch *br)
++{
++ atomic_dec(&br->br_count);
++}
++
++/* ---------------------------------------------------------------------- */
++
++/* Superblock to branch */
++static inline aufs_bindex_t sbr_id(struct super_block *sb, aufs_bindex_t bindex)
++{
++ return stobr(sb, bindex)->br_id;
++}
++
++static inline
++struct vfsmount *sbr_mnt(struct super_block *sb, aufs_bindex_t bindex)
++{
++ return stobr(sb, bindex)->br_mnt;
++}
++
++static inline
++struct super_block *sbr_sb(struct super_block *sb, aufs_bindex_t bindex)
++{
++ return sbr_mnt(sb, bindex)->mnt_sb;
++}
++
++#if 0
++static inline int sbr_count(struct super_block *sb, aufs_bindex_t bindex)
++{
++ return br_count(stobr(sb, bindex));
++}
++
++static inline void sbr_get(struct super_block *sb, aufs_bindex_t bindex)
++{
++ br_get(stobr(sb, bindex));
++}
++#endif
++
++static inline void sbr_put(struct super_block *sb, aufs_bindex_t bindex)
++{
++ br_put(stobr(sb, bindex));
++}
++
++static inline int sbr_perm(struct super_block *sb, aufs_bindex_t bindex)
++{
++ return stobr(sb, bindex)->br_perm;
++}
++
++static inline int sbr_is_whable(struct super_block *sb, aufs_bindex_t bindex)
++{
++ return br_whable(sbr_perm(sb, bindex));
++}
++
++/* ---------------------------------------------------------------------- */
++
++#ifdef CONFIG_AUFS_LHASH_PATCH
++static inline struct vfsmount *au_do_nfsmnt(struct vfsmount *h_mnt)
++{
++ if (!au_is_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(sbr_mnt(sb, bindex));
++}
++#else
++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;
++}
++#endif /* CONFIG_AUFS_LHASH_PATCH */
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * br_wh_read_lock, br_wh_write_lock
++ * br_wh_read_unlock, br_wh_write_unlock, br_wh_downgrade_lock
++ */
++SimpleRwsemFuncs(br_wh, struct aufs_branch *br, br->br_wh_rwsem);
++
++/* to debug easier, do not make them inlined functions */
++#define BrWhMustReadLock(br) do { \
++ /* SiMustAnyLock(sb); */ \
++ RwMustReadLock(&(br)->br_wh_rwsem); \
++} while (0)
++
++#define BrWhMustWriteLock(br) do { \
++ /* SiMustAnyLock(sb); */ \
++ RwMustWriteLock(&(br)->br_wh_rwsem); \
++} while (0)
++
++#define BrWhMustAnyLock(br) do { \
++ /* SiMustAnyLock(sb); */ \
++ RwMustAnyLock(&(br)->br_wh_rwsem); \
++} while (0)
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_BRANCH_H__ */
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/cpup.c linux-2.6.22.1/fs/aufs/cpup.c
+--- linux-2.6.22.1.oorig/fs/aufs/cpup.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/cpup.c 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,773 @@
++/*
++ * 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: cpup.c,v 1.37 2007/05/14 03:41:52 sfjro Exp $ */
++
++#include <asm/uaccess.h>
++#include "aufs.h"
++
++/* violent cpup_attr_*() functions don't care inode lock */
++void au_cpup_attr_timesizes(struct inode *inode)
++{
++ struct inode *hidden_inode;
++
++ LKTRTrace("i%lu\n", inode->i_ino);
++ //IMustLock(inode);
++ hidden_inode = au_h_iptr(inode);
++ DEBUG_ON(!hidden_inode);
++ //IMustLock(!hidden_inode);
++
++ inode->i_atime = hidden_inode->i_atime;
++ inode->i_mtime = hidden_inode->i_mtime;
++ inode->i_ctime = hidden_inode->i_ctime;
++ spin_lock(&inode->i_lock);
++ i_size_write(inode, i_size_read(hidden_inode));
++ inode->i_blocks = hidden_inode->i_blocks;
++ 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);
++ DEBUG_ON(!inode->i_mode);
++
++ h_inode = au_h_iptr(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 = ibend(inode);
++ for (bindex = ibstart(inode) + 1; bindex <= bend; bindex++) {
++ h_inode = au_h_iptr_i(inode, bindex);
++ if (h_inode)
++ au_add_nlink(inode, h_inode);
++ }
++ }
++}
++
++void au_cpup_attr_changable(struct inode *inode)
++{
++ struct inode *hidden_inode;
++
++ LKTRTrace("i%lu\n", inode->i_ino);
++ //IMustLock(inode);
++ hidden_inode = au_h_iptr(inode);
++ DEBUG_ON(!hidden_inode);
++
++ inode->i_mode = hidden_inode->i_mode;
++ inode->i_uid = hidden_inode->i_uid;
++ inode->i_gid = hidden_inode->i_gid;
++ au_cpup_attr_timesizes(inode);
++
++ //??
++ inode->i_flags = hidden_inode->i_flags;
++}
++
++void au_cpup_igen(struct inode *inode, struct inode *h_inode)
++{
++ inode->i_generation = h_inode->i_generation;
++ itoii(inode)->ii_hsb1 = h_inode->i_sb;
++}
++
++void au_cpup_attr_all(struct inode *inode)
++{
++ struct inode *hidden_inode;
++
++ LKTRTrace("i%lu\n", inode->i_ino);
++ //IMustLock(inode);
++ hidden_inode = au_h_iptr(inode);
++ DEBUG_ON(!hidden_inode);
++
++ au_cpup_attr_changable(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 = hidden_inode->i_rdev;
++ }
++ inode->i_blkbits = hidden_inode->i_blkbits;
++ au_cpup_attr_blksize(inode, hidden_inode);
++ au_cpup_igen(inode, hidden_inode);
++}
++
++/* ---------------------------------------------------------------------- */
++
++/* Note: dt_dentry and dt_hidden_dentry are not dget/dput-ed */
++
++/* keep the timestamps of the parent dir when cpup */
++void dtime_store(struct dtime *dt, struct dentry *dentry,
++ struct dentry *hidden_dentry)
++{
++ struct inode *inode;
++
++ TraceEnter();
++ DEBUG_ON(!dentry || !hidden_dentry || !hidden_dentry->d_inode);
++
++ dt->dt_dentry = dentry;
++ dt->dt_h_dentry = hidden_dentry;
++ inode = hidden_dentry->d_inode;
++ dt->dt_atime = inode->i_atime;
++ dt->dt_mtime = inode->i_mtime;
++ //smp_mb();
++}
++
++// todo: remove extra parameter
++void dtime_revert(struct dtime *dt, int h_parent_is_locked)
++{
++ struct iattr attr;
++ int err;
++ struct dentry *dentry;
++
++ LKTRTrace("h_parent locked %d\n", h_parent_is_locked);
++
++ 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;
++ //smp_mb();
++ dentry = NULL;
++ if (!h_parent_is_locked /* && !IS_ROOT(dt->dt_dentry) */)
++ dentry = dt->dt_dentry;
++ err = vfsub_notify_change(dt->dt_h_dentry, &attr,
++ need_dlgt(dt->dt_dentry->d_sb));
++ if (unlikely(err))
++ Warn("restoring timestamps failed(%d). ignored\n", err);
++}
++
++/* ---------------------------------------------------------------------- */
++
++static int cpup_iattr(struct dentry *hidden_dst, struct dentry *hidden_src,
++ int dlgt)
++{
++ int err;
++ struct iattr ia;
++ struct inode *hidden_isrc, *hidden_idst;
++
++ LKTRTrace("%.*s\n", DLNPair(hidden_dst));
++ hidden_idst = hidden_dst->d_inode;
++ //IMustLock(hidden_idst);
++ hidden_isrc = hidden_src->d_inode;
++ //IMustLock(hidden_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 = hidden_isrc->i_mode;
++ ia.ia_uid = hidden_isrc->i_uid;
++ ia.ia_gid = hidden_isrc->i_gid;
++ ia.ia_atime = hidden_isrc->i_atime;
++ ia.ia_mtime = hidden_isrc->i_mtime;
++ err = vfsub_notify_change(hidden_dst, &ia, dlgt);
++ //if (LktrCond) err = -1;
++ if (!err)
++ hidden_idst->i_flags = hidden_isrc->i_flags; //??
++
++ TraceErr(err);
++ return err;
++}
++
++/*
++ * to support a sparse file which is opened with O_APPEND,
++ * we need to close the file.
++ */
++static int cpup_regular(struct dentry *dentry, aufs_bindex_t bdst,
++ aufs_bindex_t bsrc, loff_t len)
++{
++ int err, i, sparse;
++ struct super_block *sb;
++ struct inode *hidden_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",
++ DLNPair(dentry), bdst, bsrc, len);
++ DEBUG_ON(bsrc <= bdst);
++ DEBUG_ON(!len);
++ sb = dentry->d_sb;
++ DEBUG_ON(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_i(dentry, h->bindex);
++ DEBUG_ON(!h->dentry);
++ hidden_inode = h->dentry->d_inode;
++ DEBUG_ON(!hidden_inode || !S_ISREG(hidden_inode->i_mode));
++ h->file = hidden_open(dentry, h->bindex, h->flags);
++ //if (LktrCond)
++ //{fput(h->file); 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);
++ sparse = 0;
++ err = au_copy_file(hidden[DST].file, hidden[SRC].file, len, sb,
++ &sparse);
++
++ /* sparse file: update i_blocks next time */
++ if (unlikely(!err && sparse))
++ d_drop(dentry);
++
++ out_dst_file:
++ fput(hidden[DST].file);
++ sbr_put(sb, hidden[DST].bindex);
++ out_src_file:
++ fput(hidden[SRC].file);
++ sbr_put(sb, hidden[SRC].bindex);
++ out:
++ TraceErr(err);
++ return err;
++}
++
++// unnecessary?
++unsigned int au_flags_cpup(unsigned int init, struct dentry *parent)
++{
++ if (unlikely(parent && IS_ROOT(parent)))
++ init |= CPUP_LOCKED_GHDIR;
++ return init;
++}
++
++/* return with hidden dst inode is locked */
++static 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, symlen;
++ struct dentry *hidden_src, *hidden_dst, *hidden_parent, *parent;
++ struct inode *hidden_inode, *hidden_dir, *dir;
++ struct dtime dt;
++ umode_t mode;
++ char *sym;
++ mm_segment_t old_fs;
++ const int do_dt = flags & CPUP_DTIME;
++ struct super_block *sb;
++
++ LKTRTrace("%.*s, i%lu, bdst %d, bsrc %d, len %Ld, flags 0x%x\n",
++ DLNPair(dentry), dentry->d_inode->i_ino, bdst, bsrc, len,
++ flags);
++ sb = dentry->d_sb;
++ DEBUG_ON(bdst >= bsrc || test_ro(sb, bdst, NULL));
++ // bsrc branch can be ro/rw.
++
++ hidden_src = au_h_dptr_i(dentry, bsrc);
++ DEBUG_ON(!hidden_src);
++ hidden_inode = hidden_src->d_inode;
++ DEBUG_ON(!hidden_inode);
++
++ /* stop refrencing while we are creating */
++ //parent = dget_parent(dentry);
++ parent = dentry->d_parent;
++ dir = parent->d_inode;
++ hidden_dst = au_h_dptr_i(dentry, bdst);
++ DEBUG_ON(hidden_dst && hidden_dst->d_inode);
++ //hidden_parent = dget_parent(hidden_dst);
++ hidden_parent = hidden_dst->d_parent;
++ hidden_dir = hidden_parent->d_inode;
++ IMustLock(hidden_dir);
++
++ if (do_dt)
++ dtime_store(&dt, parent, hidden_parent);
++
++ isdir = 0;
++ mode = hidden_inode->i_mode;
++ switch (mode & S_IFMT) {
++ case S_IFREG:
++ /* stop updating while we are referencing */
++ IMustLock(hidden_inode);
++ err = vfsub_create(hidden_dir, hidden_dst, mode | S_IWUSR, NULL,
++ dlgt);
++ //if (LktrCond) {vfs_unlink(hidden_dir, hidden_dst); err = -1;}
++ if (!err) {
++ loff_t l = i_size_read(hidden_inode);
++ if (len == -1 || l < len)
++ len = l;
++ if (len) {
++ err = cpup_regular(dentry, bdst, bsrc, len);
++ //if (LktrCond) err = -1;
++ }
++ if (unlikely(err)) {
++ int rerr;
++ rerr = vfsub_unlink(hidden_dir, hidden_dst,
++ dlgt);
++ if (rerr) {
++ IOErr("failed unlinking cpup-ed %.*s"
++ "(%d, %d)\n",
++ DLNPair(hidden_dst), err, rerr);
++ err = -EIO;
++ }
++ }
++ }
++ break;
++ case S_IFDIR:
++ isdir = 1;
++ err = vfsub_mkdir(hidden_dir, hidden_dst, mode, dlgt);
++ //if (LktrCond) {vfs_rmdir(hidden_dir, hidden_dst); err = -1;}
++ if (!err) {
++ /* setattr case: dir is not locked */
++ if (0 && ibstart(dir) == bdst)
++ au_cpup_attr_nlink(dir);
++ au_cpup_attr_nlink(dentry->d_inode);
++ }
++ break;
++ case S_IFLNK:
++ err = -ENOMEM;
++ sym = __getname();
++ //if (LktrCond) {__putname(sym); sym = NULL;}
++ if (unlikely(!sym))
++ break;
++ old_fs = get_fs();
++ set_fs(KERNEL_DS);
++ err = symlen = hidden_inode->i_op->readlink
++ (hidden_src, (char __user*)sym, PATH_MAX);
++ //if (LktrCond) err = symlen = -1;
++ set_fs(old_fs);
++ if (symlen > 0) {
++ sym[symlen] = 0;
++ err = vfsub_symlink(hidden_dir, hidden_dst, sym, mode,
++ dlgt);
++ //if (LktrCond)
++ //{vfs_unlink(hidden_dir, hidden_dst); err = -1;}
++ }
++ __putname(sym);
++ break;
++ case S_IFCHR:
++ case S_IFBLK:
++ DEBUG_ON(!capable(CAP_MKNOD));
++ /*FALLTHROUGH*/
++ case S_IFIFO:
++ case S_IFSOCK:
++ err = vfsub_mknod(hidden_dir, hidden_dst, mode,
++ hidden_inode->i_rdev, dlgt);
++ //if (LktrCond) {vfs_unlink(hidden_dir, hidden_dst); err = -1;}
++ break;
++ default:
++ IOErr("Unknown inode type 0%o\n", mode);
++ err = -EIO;
++ }
++
++ if (do_dt)
++ dtime_revert(&dt, flags & CPUP_LOCKED_GHDIR);
++ //dput(parent);
++ //dput(hidden_parent);
++ TraceErr(err);
++ return err;
++}
++
++/*
++ * copyup the @dentry from @bsrc to @bdst.
++ * the caller must set the both of hidden dentries.
++ * @len is for trucating when it is -1 copyup the entire file.
++ */
++int cpup_single(struct dentry *dentry, aufs_bindex_t bdst, aufs_bindex_t bsrc,
++ loff_t len, unsigned int flags)
++{
++ int err, rerr, isdir, dlgt;
++ struct dentry *hidden_src, *hidden_dst, *parent;//, *h_parent;
++ struct inode *dst_inode, *hidden_dir, *inode, *src_inode;
++ struct super_block *sb;
++ aufs_bindex_t old_ibstart;
++ struct dtime dt;
++
++ LKTRTrace("%.*s, i%lu, bdst %d, bsrc %d, len %Ld, flags 0x%x\n",
++ DLNPair(dentry), dentry->d_inode->i_ino, bdst, bsrc, len,
++ flags);
++ sb = dentry->d_sb;
++ DEBUG_ON(bsrc <= bdst);
++ hidden_dst = au_h_dptr_i(dentry, bdst);
++ DEBUG_ON(!hidden_dst || hidden_dst->d_inode);
++ //h_parent = dget_parent(hidden_dst);
++ //hidden_dir = h_parent->d_inode;
++ hidden_dir = hidden_dst->d_parent->d_inode;
++ IMustLock(hidden_dir);
++ hidden_src = au_h_dptr_i(dentry, bsrc);
++ DEBUG_ON(!hidden_src || !hidden_src->d_inode);
++ inode = dentry->d_inode;
++ IiMustWriteLock(inode);
++
++ dlgt = need_dlgt(sb);
++ dst_inode = au_h_iptr_i(inode, bdst);
++ if (unlikely(dst_inode)) {
++ if (unlikely(!au_flag_test(sb, AuFlag_PLINK))) {
++ err = -EIO;
++ IOErr("i%lu exists on a upper branch "
++ "but plink is disabled\n", inode->i_ino);
++ goto out;
++ }
++
++ if (dst_inode->i_nlink) {
++ hidden_src = lkup_plink(sb, bdst, inode);
++ err = PTR_ERR(hidden_src);
++ if (IS_ERR(hidden_src))
++ goto out;
++ DEBUG_ON(!hidden_src->d_inode);
++ // vfs_link() does lock the inode
++ err = vfsub_link(hidden_src, hidden_dir, hidden_dst, dlgt);
++ dput(hidden_src);
++ goto out;
++ } else
++ /* udba work */
++ au_update_brange(inode, 1);
++ }
++
++ old_ibstart = ibstart(inode);
++ err = cpup_entry(dentry, bdst, bsrc, len, flags, dlgt);
++ if (unlikely(err))
++ goto out;
++ dst_inode = hidden_dst->d_inode;
++ hi_lock_child2(dst_inode);
++
++ //todo: test dlgt
++ err = cpup_iattr(hidden_dst, hidden_src, dlgt);
++ //if (LktrCond) err = -1;
++#if 0 // xattr
++ if (0 && !err)
++ err = cpup_xattrs(hidden_src, hidden_dst);
++#endif
++ isdir = S_ISDIR(dst_inode->i_mode);
++ if (!err) {
++ if (bdst < old_ibstart)
++ set_ibstart(inode, bdst);
++ set_h_iptr(inode, bdst, igrab(dst_inode),
++ au_hi_flags(inode, isdir));
++ i_unlock(dst_inode);
++ src_inode = hidden_src->d_inode;
++ if (!isdir) {
++ if (src_inode->i_nlink > 1
++ && au_flag_test(sb, AuFlag_PLINK))
++ append_plink(sb, inode, hidden_dst, bdst);
++ else {
++ /* braces are added to stop a warning */
++ ;//xino_write0(sb, bsrc, src_inode->i_ino);
++ /* ignore this error */
++ }
++ }
++ //goto out; /* success */
++ return 0; /* success */
++ }
++
++ /* revert */
++ i_unlock(dst_inode);
++ parent = dget_parent(dentry);
++ //dtime_store(&dt, parent, h_parent);
++ dtime_store(&dt, parent, hidden_dst->d_parent);
++ dput(parent);
++ if (!isdir)
++ rerr = vfsub_unlink(hidden_dir, hidden_dst, dlgt);
++ else
++ rerr = vfsub_rmdir(hidden_dir, hidden_dst, dlgt);
++ //rerr = -1;
++ dtime_revert(&dt, flags & CPUP_LOCKED_GHDIR);
++ if (rerr) {
++ IOErr("failed removing broken entry(%d, %d)\n", err, rerr);
++ err = -EIO;
++ }
++
++ out:
++ //dput(h_parent);
++ TraceErr(err);
++ return err;
++}
++
++struct cpup_single_args {
++ int *errp;
++ struct dentry *dentry;
++ aufs_bindex_t bdst, bsrc;
++ loff_t len;
++ unsigned int flags;
++};
++
++static void call_cpup_single(void *args)
++{
++ struct cpup_single_args *a = args;
++ *a->errp = cpup_single(a->dentry, a->bdst, a->bsrc, a->len, a->flags);
++}
++
++int sio_cpup_single(struct dentry *dentry, aufs_bindex_t bdst,
++ aufs_bindex_t bsrc, loff_t len, unsigned int flags)
++{
++ int err;
++ struct dentry *hidden_dentry;
++ umode_t mode;
++
++ LKTRTrace("%.*s, i%lu, bdst %d, bsrc %d, len %Ld, flags 0x%x\n",
++ DLNPair(dentry), dentry->d_inode->i_ino, bdst, bsrc, len,
++ flags);
++
++ hidden_dentry = au_h_dptr_i(dentry, bsrc);
++ mode = hidden_dentry->d_inode->i_mode & S_IFMT;
++ if ((mode != S_IFCHR && mode != S_IFBLK)
++ || capable(CAP_MKNOD))
++ err = cpup_single(dentry, bdst, bsrc, len, flags);
++ else {
++ struct cpup_single_args args = {
++ .errp = &err,
++ .dentry = dentry,
++ .bdst = bdst,
++ .bsrc = bsrc,
++ .len = len,
++ .flags = flags
++ };
++ au_wkq_wait(call_cpup_single, &args, /*dlgt*/0);
++ }
++
++ TraceErr(err);
++ return err;
++}
++
++/*
++ * copyup the @dentry from the first active hidden branch to @bdst,
++ * using cpup_single().
++ */
++int 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",
++ DLNPair(dentry), bdst, len, flags);
++ inode = dentry->d_inode;
++ DEBUG_ON(!S_ISDIR(inode->i_mode) && dbstart(dentry) < bdst);
++
++ bend = dbend(dentry);
++ for (bsrc = bdst + 1; bsrc <= bend; bsrc++)
++ if (au_h_dptr_i(dentry, bsrc))
++ break;
++ DEBUG_ON(!au_h_dptr_i(dentry, bsrc));
++
++ err = lkup_neg(dentry, bdst);
++ //err = -1;
++ if (!err) {
++ err = cpup_single(dentry, bdst, bsrc, len, flags);
++ if (!err)
++ return 0; /* success */
++
++ /* revert */
++ set_h_dptr(dentry, bdst, NULL);
++ set_dbstart(dentry, bsrc);
++ }
++
++ TraceErr(err);
++ return err;
++}
++
++struct cpup_simple_args {
++ int *errp;
++ struct dentry *dentry;
++ aufs_bindex_t bdst;
++ loff_t len;
++ unsigned int flags;
++};
++
++static void call_cpup_simple(void *args)
++{
++ struct cpup_simple_args *a = args;
++ *a->errp = cpup_simple(a->dentry, a->bdst, a->len, a->flags);
++}
++
++int sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
++ unsigned int flags)
++{
++ int err, do_sio, dlgt;
++ //struct dentry *parent;
++ struct inode *hidden_dir, *dir;
++
++ LKTRTrace("%.*s, b%d, len %Ld, flags 0x%x\n",
++ DLNPair(dentry), bdst, len, flags);
++
++ //parent = dget_parent(dentry);
++ //dir = parent->d_inode;
++ dir = dentry->d_parent->d_inode;
++ hidden_dir = au_h_iptr_i(dir, bdst);
++ dlgt = need_dlgt(dir->i_sb);
++ do_sio = au_test_perm(hidden_dir, MAY_EXEC | MAY_WRITE, dlgt);
++ if (!do_sio) {
++ umode_t mode = dentry->d_inode->i_mode & S_IFMT;
++ do_sio = ((mode == S_IFCHR || mode == S_IFBLK)
++ && !capable(CAP_MKNOD));
++ }
++ if (!do_sio)
++ err = cpup_simple(dentry, bdst, len, flags);
++ else {
++ struct cpup_simple_args args = {
++ .errp = &err,
++ .dentry = dentry,
++ .bdst = bdst,
++ .len = len,
++ .flags = flags
++ };
++ au_wkq_wait(call_cpup_simple, &args, /*dlgt*/0);
++ }
++
++ //dput(parent);
++ TraceErr(err);
++ return err;
++}
++
++//todo: dcsub
++/* cf. revalidate function in file.c */
++int cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst, struct dentry *locked)
++{
++ int err;
++ struct super_block *sb;
++ struct dentry *d, *parent, *hidden_parent;
++ unsigned int udba;
++
++ LKTRTrace("%.*s, b%d, parent i%lu, locked %p\n",
++ DLNPair(dentry), bdst, parent_ino(dentry), locked);
++ sb = dentry->d_sb;
++ DEBUG_ON(test_ro(sb, bdst, NULL));
++ parent = dentry->d_parent;
++ IiMustWriteLock(parent->d_inode);
++ if (unlikely(IS_ROOT(parent)))
++ return 0;
++ if (locked) {
++ DiMustAnyLock(locked);
++ IiMustAnyLock(locked->d_inode);
++ }
++
++ /* slow loop, keep it simple and stupid */
++ err = 0;
++ udba = au_flag_test(sb, AuFlag_UDBA_INOTIFY);
++ while (1) {
++ parent = dentry->d_parent; // dget_parent()
++ hidden_parent = au_h_dptr_i(parent, bdst);
++ if (hidden_parent)
++ return 0; /* success */
++
++ /* find top dir which is needed to cpup */
++ do {
++ d = parent;
++ parent = d->d_parent; // dget_parent()
++ if (parent != locked)
++ di_read_lock_parent3(parent, !AUFS_I_RLOCK);
++ hidden_parent = au_h_dptr_i(parent, bdst);
++ if (parent != locked)
++ di_read_unlock(parent, !AUFS_I_RLOCK);
++ } while (!hidden_parent);
++
++ if (d != dentry->d_parent)
++ di_write_lock_child3(d);
++
++ /* somebody else might create while we were sleeping */
++ if (!au_h_dptr_i(d, bdst) || !au_h_dptr_i(d, bdst)->d_inode) {
++ struct inode *h_dir = hidden_parent->d_inode,
++ *dir = parent->d_inode,
++ *h_gdir, *gdir;
++
++ if (au_h_dptr_i(d, bdst))
++ au_update_dbstart(d);
++ //DEBUG_ON(dbstart(d) <= bdst);
++ if (parent != locked)
++ di_read_lock_parent3(parent, AUFS_I_RLOCK);
++ h_gdir = gdir = NULL;
++ if (unlikely(udba && !IS_ROOT(parent))) {
++ gdir = parent->d_parent->d_inode;
++ h_gdir = hidden_parent->d_parent->d_inode;
++ hgdir_lock(h_gdir, gdir, bdst);
++ }
++ hdir_lock(h_dir, dir, bdst);
++ err = sio_cpup_simple(d, bdst, -1,
++ au_flags_cpup(CPUP_DTIME,
++ parent));
++ //if (LktrCond) err = -1;
++ hdir_unlock(h_dir, dir, bdst);
++ if (unlikely(gdir))
++ hdir_unlock(h_gdir, gdir, bdst);
++ if (parent != locked)
++ di_read_unlock(parent, AUFS_I_RLOCK);
++ }
++
++ if (d != dentry->d_parent)
++ di_write_unlock(d);
++ if (unlikely(err))
++ break;
++ }
++
++// out:
++ TraceErr(err);
++ return err;
++}
++
++int test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst,
++ struct dentry *locked)
++{
++ int err;
++ struct dentry *parent;
++ struct inode *dir;
++
++ parent = dentry->d_parent;
++ dir = parent->d_inode;
++ LKTRTrace("%.*s, b%d, parent i%lu, locked %p\n",
++ DLNPair(dentry), bdst, dir->i_ino, locked);
++ DiMustReadLock(parent);
++ IiMustReadLock(dir);
++
++ if (au_h_iptr_i(dir, bdst))
++ return 0;
++
++ err = 0;
++ di_read_unlock(parent, AUFS_I_RLOCK);
++ di_write_lock_parent(parent);
++ if (au_h_iptr_i(dir, bdst))
++ goto out;
++
++ err = cpup_dirs(dentry, bdst, locked);
++
++ out:
++ di_downgrade_lock(parent, AUFS_I_RLOCK);
++ TraceErr(err);
++ return err;
++}
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/cpup.h linux-2.6.22.1/fs/aufs/cpup.h
+--- linux-2.6.22.1.oorig/fs/aufs/cpup.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/cpup.h 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,72 @@
++/*
++ * 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: cpup.h,v 1.15 2007/05/14 03:41:52 sfjro Exp $ */
++
++#ifndef __AUFS_CPUP_H__
++#define __AUFS_CPUP_H__
++
++#ifdef __KERNEL__
++
++#include <linux/fs.h>
++#include <linux/version.h>
++#include <linux/aufs_type.h>
++
++static inline
++void au_cpup_attr_blksize(struct inode *inode, struct inode *h_inode)
++{
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
++ inode->i_blksize = h_inode->i_blksize;
++#endif
++}
++
++void au_cpup_attr_timesizes(struct inode *inode);
++void au_cpup_attr_nlink(struct inode *inode);
++void au_cpup_attr_changable(struct inode *inode);
++void au_cpup_igen(struct inode *inode, struct inode *h_inode);
++void au_cpup_attr_all(struct inode *inode);
++
++#define CPUP_DTIME 1 // do dtime_store/revert
++// todo: remove this
++#define CPUP_LOCKED_GHDIR 2 // grand parent hidden dir is locked
++unsigned int au_flags_cpup(unsigned int init, struct dentry *parent);
++
++int cpup_single(struct dentry *dentry, aufs_bindex_t bdst, aufs_bindex_t bsrc,
++ loff_t len, unsigned int flags);
++int sio_cpup_single(struct dentry *dentry, aufs_bindex_t bdst,
++ aufs_bindex_t bsrc, loff_t len, unsigned int flags);
++int cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
++ unsigned int flags);
++int sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
++ unsigned int flags);
++
++int cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst, struct dentry *locked);
++int test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst,
++ struct dentry *locked);
++
++/* keep timestamps when copyup */
++struct dtime {
++ struct dentry *dt_dentry, *dt_h_dentry;
++ struct timespec dt_atime, dt_mtime;
++};
++void dtime_store(struct dtime *dt, struct dentry *dentry,
++ struct dentry *h_dentry);
++void dtime_revert(struct dtime *dt, int h_parent_is_locked);
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_CPUP_H__ */
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/dcsub.c linux-2.6.22.1/fs/aufs/dcsub.c
+--- linux-2.6.22.1.oorig/fs/aufs/dcsub.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/dcsub.c 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,175 @@
++/*
++ * 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: dcsub.c,v 1.3 2007/05/14 03:41:52 sfjro Exp $ */
++
++#include "aufs.h"
++
++static void au_dpage_free(struct au_dpage *dpage)
++{
++ int i;
++
++ TraceEnter();
++ DEBUG_ON(!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;
++
++ TraceEnter();
++
++ 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:
++ TraceErr(err);
++ return err;
++}
++
++void au_dpages_free(struct au_dcsub_pages *dpages)
++{
++ int i;
++
++ TraceEnter();
++
++ 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;
++
++ //TraceEnter();
++
++ dpage = dpages->dpages + dpages->ndpage - 1;
++ DEBUG_ON(!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:
++ //TraceErr(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;
++
++ TraceEnter();
++
++ 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_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_CHILD.next;
++ this_parent = this_parent->d_parent;
++ goto resume;
++ }
++ out:
++ spin_unlock(&dcache_lock);
++#if 0
++ if (!err) {
++ int i, j;
++ j = 0;
++ for (i = 0; i < dpages->ndpage; i++) {
++ if ((dpages->dpages + i)->ndentry)
++ Dbg("%d: %d\n", i, (dpages->dpages + i)->ndentry);
++ j += (dpages->dpages + i)->ndentry;
++ }
++ if (j)
++ Dbg("ndpage %d, %d\n", dpages->ndpage, j);
++ }
++#endif
++ TraceErr(err);
++ return err;
++}
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/dcsub.h linux-2.6.22.1/fs/aufs/dcsub.h
+--- linux-2.6.22.1.oorig/fs/aufs/dcsub.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/dcsub.h 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,47 @@
++/*
++ * 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: dcsub.h,v 1.2 2007/05/14 03:41:52 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);
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_DCSUB_H__ */
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/debug.c linux-2.6.22.1/fs/aufs/debug.c
+--- linux-2.6.22.1.oorig/fs/aufs/debug.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/debug.c 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,262 @@
++/*
++ * 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: debug.c,v 1.27 2007/04/30 05:48:23 sfjro Exp $ */
++
++#include "aufs.h"
++
++atomic_t aufs_cond = ATOMIC_INIT(0);
++
++#if defined(CONFIG_LKTR) || defined(CONFIG_LKTR_MODULE)
++#define dpri(fmt, arg...) \
++ do {if (LktrCond) printk(KERN_DEBUG fmt, ##arg);} while (0)
++#else
++#define dpri(fmt, arg...) printk(KERN_DEBUG fmt, ##arg)
++#endif
++
++/* ---------------------------------------------------------------------- */
++
++void au_dpri_whlist(struct aufs_nhash *whlist)
++{
++ int i;
++ struct hlist_head *head;
++ struct aufs_wh *tpos;
++ struct hlist_node *pos;
++
++ for (i = 0; i < AUFS_NHASH_SIZE; 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 aufs_vdir *vdir)
++{
++ int i;
++ union aufs_deblk_p p;
++ unsigned char *o;
++
++ if (!vdir || IS_ERR(vdir)) {
++ dpri("err %ld\n", PTR_ERR(vdir));
++ return;
++ }
++
++ dpri("nblk %d, deblk %p %d, last{%d, %p}, ver %lu\n",
++ vdir->vd_nblk, vdir->vd_deblk, ksize(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 %d\n", i, o, ksize(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)
++{
++ 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));
++ dpri("i%d: i%lu, %s, cnt %d, nl %u, 0%o, sz %Lu, blk %Lu,"
++ " ct %Ld, np %lu, st 0x%lx, g %x\n",
++ bindex,
++ inode->i_ino, inode->i_sb ? au_sbtype(inode->i_sb) : "??",
++ atomic_read(&inode->i_count), inode->i_nlink, inode->i_mode,
++ 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_generation);
++ return 0;
++}
++
++void au_dpri_inode(struct inode *inode)
++{
++ struct aufs_iinfo *iinfo;
++ aufs_bindex_t bindex;
++ int err;
++
++ err = do_pri_inode(-1, inode);
++ if (err || !au_is_aufs(inode->i_sb))
++ return;
++
++ iinfo = itoii(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);
++}
++
++static int do_pri_dentry(aufs_bindex_t bindex, struct dentry *dentry)
++{
++ if (!dentry || IS_ERR(dentry)) {
++ dpri("d%d: err %ld\n", bindex, PTR_ERR(dentry));
++ return -1;
++ }
++ dpri("d%d: %.*s/%.*s, %s, cnt %d, flags 0x%x\n",
++ bindex,
++ DLNPair(dentry->d_parent), DLNPair(dentry),
++ dentry->d_sb ? au_sbtype(dentry->d_sb) : "??",
++ atomic_read(&dentry->d_count), dentry->d_flags);
++ do_pri_inode(bindex, dentry->d_inode);
++ return 0;
++}
++
++void au_dpri_dentry(struct dentry *dentry)
++{
++ struct aufs_dinfo *dinfo;
++ aufs_bindex_t bindex;
++ int err;
++
++ err = do_pri_dentry(-1, dentry);
++ if (err || !au_is_aufs(dentry->d_sb))
++ return;
++
++ dinfo = dtodi(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);
++}
++
++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 == -1 && ftofi(file))
++ snprintf(a, sizeof(a), ", mmapped %d", au_is_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);
++ do_pri_dentry(bindex, file->f_dentry);
++ return 0;
++}
++
++void au_dpri_file(struct file *file)
++{
++ struct aufs_finfo *finfo;
++ aufs_bindex_t bindex;
++ int err;
++
++ err = do_pri_file(-1, file);
++ if (err || !file->f_dentry || !au_is_aufs(file->f_dentry->d_sb))
++ return;
++
++ finfo = ftofi(file);
++ if (!finfo)
++ return;
++ if (finfo->fi_bstart < 0)
++ return;
++ for (bindex = finfo->fi_bstart; bindex <= finfo->fi_bend; bindex++) {
++ struct aufs_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 aufs_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 %p %p\n",
++ bindex, br->br_perm, br_count(br),
++ au_sbtype(sb), sb->s_flags, sb->s_count - S_BIAS,
++ atomic_read(&sb->s_active), br->br_xino,
++ br->br_xino ? br->br_xino->f_dentry : NULL);
++ return 0;
++}
++
++void au_dpri_sb(struct super_block *sb)
++{
++ struct aufs_sbinfo *sbinfo;
++ aufs_bindex_t bindex;
++ int err;
++ struct vfsmount mnt = {.mnt_sb = sb};
++ struct aufs_branch fake = {
++ .br_perm = 0,
++ .br_mnt = &mnt,
++ .br_count = ATOMIC_INIT(0),
++ .br_xino = NULL
++ };
++
++ atomic_set(&fake.br_count, 0);
++ err = do_pri_br(-1, &fake);
++ dpri("dev 0x%x\n", sb->s_dev);
++ if (err || !au_is_aufs(sb))
++ return;
++
++ sbinfo = stosi(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 DbgSleep(int sec)
++{
++ static DECLARE_WAIT_QUEUE_HEAD(wq);
++ Dbg("sleep %d sec\n", sec);
++ wait_event_timeout(wq, 0, sec * HZ);
++}
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/debug.h linux-2.6.22.1/fs/aufs/debug.h
+--- linux-2.6.22.1.oorig/fs/aufs/debug.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/debug.h 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,129 @@
++/*
++ * 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: debug.h,v 1.31 2007/05/14 03:41:52 sfjro Exp $ */
++
++#ifndef __AUFS_DEBUG_H__
++#define __AUFS_DEBUG_H__
++
++#ifdef __KERNEL__
++
++#include <linux/fs.h>
++
++#ifdef CONFIG_AUFS_DEBUG
++#define DEBUG_ON(a) BUG_ON(a)
++extern atomic_t aufs_cond;
++#define au_debug_on() atomic_inc(&aufs_cond)
++#define au_debug_off() atomic_dec(&aufs_cond)
++#define au_is_debug() atomic_read(&aufs_cond)
++#else
++#define DEBUG_ON(a) /* */
++#define au_debug_on() /* */
++#define au_debug_off() /* */
++#define au_is_debug() 0
++#endif
++
++#define MtxMustLock(mtx) DEBUG_ON(!mutex_is_locked(mtx))
++
++/* ---------------------------------------------------------------------- */
++
++/* debug print */
++#if defined(CONFIG_LKTR) || defined(CONFIG_LKTR_MODULE)
++#include <linux/lktr.h>
++#ifdef CONFIG_AUFS_DEBUG
++#undef LktrCond
++#define LktrCond unlikely((lktr_cond && lktr_cond()) || au_is_debug())
++#endif
++#else
++#define LktrCond au_is_debug()
++#define LKTRDumpVma(pre, vma, suf) /* */
++#define LKTRDumpStack() /* */
++#define LKTRTrace(fmt, args...) do { \
++ if (LktrCond) \
++ Dbg(fmt, ##args); \
++} while (0)
++#define LKTRLabel(label) LKTRTrace("%s\n", #label)
++#endif /* CONFIG_LKTR */
++
++#define TraceErr(e) do { \
++ if (unlikely((e) < 0)) \
++ LKTRTrace("err %d\n", (int)(e)); \
++} while (0)
++#define TraceErrPtr(p) do { \
++ if (IS_ERR(p)) \
++ LKTRTrace("err %ld\n", PTR_ERR(p)); \
++} while (0)
++#define TraceEnter() LKTRLabel(enter)
++
++/* dirty macros for debug print, use with "%.*s" and caution */
++#define LNPair(qstr) (qstr)->len,(qstr)->name
++#define DLNPair(d) LNPair(&(d)->d_name)
++
++/* ---------------------------------------------------------------------- */
++
++#define Dpri(lvl, fmt, arg...) \
++ printk(lvl AUFS_NAME " %s:%d:%s[%d]: " fmt, \
++ __func__, __LINE__, current->comm, current->pid, ##arg)
++#define Dbg(fmt, arg...) Dpri(KERN_DEBUG, fmt, ##arg)
++#define Warn(fmt, arg...) Dpri(KERN_WARNING, fmt, ##arg)
++#define Warn1(fmt, arg...) do { \
++ static unsigned char c; \
++ if (!c++) Warn(fmt, ##arg); \
++ } while (0)
++#define Err(fmt, arg...) Dpri(KERN_ERR, fmt, ##arg)
++#define Err1(fmt, arg...) do { \
++ static unsigned char c; \
++ if (!c++) Err(fmt, ##arg); \
++ } while (0)
++#define IOErr(fmt, arg...) Err("I/O Error, " fmt, ##arg)
++#define IOErr1(fmt, arg...) do { \
++ static unsigned char c; \
++ if (!c++) IOErr(fmt, ##arg); \
++ } while (0)
++#define IOErrWhck(fmt, arg...) Err("I/O Error, try whck. " fmt, ##arg)
++
++/* ---------------------------------------------------------------------- */
++
++#ifdef CONFIG_AUFS_DEBUG
++struct aufs_nhash;
++void au_dpri_whlist(struct aufs_nhash *whlist);
++struct aufs_vdir;
++void au_dpri_vdir(struct aufs_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);
++#define DbgWhlist(w) do{LKTRTrace(#w "\n"); au_dpri_whlist(w);}while(0)
++#define DbgVdir(v) do{LKTRTrace(#v "\n"); au_dpri_vdir(v);}while(0)
++#define DbgInode(i) do{LKTRTrace(#i "\n"); au_dpri_inode(i);}while(0)
++#define DbgDentry(d) do{LKTRTrace(#d "\n"); au_dpri_dentry(d);}while(0)
++#define DbgFile(f) do{LKTRTrace(#f "\n"); au_dpri_file(f);}while(0)
++#define DbgSb(sb) do{LKTRTrace(#sb "\n"); au_dpri_sb(sb);}while(0)
++void DbgSleep(int sec);
++#else
++#define DbgWhlist(w) /* */
++#define DbgVdir(v) /* */
++#define DbgInode(i) /* */
++#define DbgDentry(d) /* */
++#define DbgFile(f) /* */
++#define DbgSb(sb) /* */
++#define DbgSleep(sec) /* */
++#endif
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_DEBUG_H__ */
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/dentry.c linux-2.6.22.1/fs/aufs/dentry.c
+--- linux-2.6.22.1.oorig/fs/aufs/dentry.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/dentry.c 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,946 @@
++/*
++ * 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: dentry.c,v 1.41 2007/05/14 03:38:38 sfjro Exp $ */
++
++//#include <linux/fs.h>
++//#include <linux/namei.h>
++#include "aufs.h"
++
++#ifdef CONFIG_AUFS_LHASH_PATCH
++
++#ifdef CONFIG_AUFS_DLGT
++struct lookup_hash_args {
++ struct dentry **errp;
++ struct qstr *name;
++ struct dentry *base;
++ struct nameidata *nd;
++};
++
++static void call_lookup_hash(void *args)
++{
++ struct lookup_hash_args *a = args;
++ *a->errp = __lookup_hash(a->name, a->base, a->nd);
++}
++#endif /* CONFIG_AUFS_DLGT */
++
++static struct dentry *lkup_hash(const char *name, struct dentry *parent,
++ int len, struct lkup_args *lkup)
++{
++ struct dentry *dentry;
++ char *p;
++ unsigned long hash;
++ struct qstr this;
++ unsigned int c;
++ struct nameidata tmp_nd;
++
++ 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);
++
++ memset(&tmp_nd, 0, sizeof(tmp_nd));
++ tmp_nd.dentry = dget(parent);
++ tmp_nd.mnt = mntget(lkup->nfsmnt);
++#ifndef CONFIG_AUFS_DLGT
++ dentry = __lookup_hash(&this, parent, &tmp_nd);
++#else
++ if (!lkup->dlgt)
++ dentry = __lookup_hash(&this, parent, &tmp_nd);
++ else {
++ struct lookup_hash_args args = {
++ .errp = &dentry,
++ .name = &this,
++ .base = parent,
++ .nd = &tmp_nd
++ };
++ au_wkq_wait(call_lookup_hash, &args, /*dlgt*/1);
++ }
++#endif
++ path_release(&tmp_nd);
++
++ out:
++ TraceErrPtr(dentry);
++ return dentry;
++}
++#elif defined(CONFIG_AUFS_DLGT)
++static struct dentry *lkup_hash(const char *name, struct dentry *parent,
++ int len, struct lkup_args *lkup)
++{
++ return ERR_PTR(-ENOSYS);
++}
++#endif
++
++#ifdef CONFIG_AUFS_DLGT
++struct lookup_one_len_args {
++ struct dentry **errp;
++ const char *name;
++ struct dentry *parent;
++ int len;
++};
++
++static void call_lookup_one_len(void *args)
++{
++ struct lookup_one_len_args *a = args;
++ *a->errp = lookup_one_len(a->name, a->parent, a->len);
++}
++#endif /* CONFIG_AUFS_DLGT */
++
++#if defined(CONFIG_AUFS_LHASH_PATCH) || defined(CONFIG_AUFS_DLGT)
++/* cf. lookup_one_len() in linux/fs/namei.c */
++struct dentry *lkup_one(const char *name, struct dentry *parent, int len,
++ struct lkup_args *lkup)
++{
++ struct dentry *dentry;
++
++ LKTRTrace("%.*s/%.*s, lkup{%p, %d}\n",
++ DLNPair(parent), len, name, lkup->nfsmnt, lkup->dlgt);
++
++ if (!lkup->nfsmnt) {
++#ifndef CONFIG_AUFS_DLGT
++ dentry = lookup_one_len(name, parent, len);
++#else
++ if (!lkup->dlgt)
++ dentry = lookup_one_len(name, parent, len);
++ else {
++ struct lookup_one_len_args args = {
++ .errp = &dentry,
++ .name = name,
++ .parent = parent,
++ .len = len
++ };
++ au_wkq_wait(call_lookup_one_len, &args, /*dlgt*/1);
++ }
++#endif
++ } else
++ dentry = lkup_hash(name, parent, len, lkup);
++
++ TraceErrPtr(dentry);
++ return dentry;
++}
++#endif
++
++struct lkup_one_args {
++ struct dentry **errp;
++ const char *name;
++ struct dentry *parent;
++ int len;
++ struct lkup_args *lkup;
++};
++
++static void call_lkup_one(void *args)
++{
++ struct lkup_one_args *a = args;
++ *a->errp = lkup_one(a->name, a->parent, a->len, a->lkup);
++}
++
++/*
++ * returns positive/negative dentry, NULL or an error.
++ * NULL means whiteout-ed or not-found.
++ */
++static struct dentry *do_lookup(struct dentry *hidden_parent,
++ struct dentry *dentry, aufs_bindex_t bindex,
++ struct qstr *wh_name, int allow_neg,
++ mode_t type, int dlgt)
++{
++ struct dentry *hidden_dentry;
++ int wh_found, wh_able, opq;
++ struct inode *hidden_dir, *hidden_inode;
++ struct qstr *name;
++ struct super_block *sb;
++ struct lkup_args lkup = {.dlgt = dlgt};
++
++ LKTRTrace("%.*s/%.*s, b%d, allow_neg %d, type 0%o, dlgt %d\n",
++ DLNPair(hidden_parent), DLNPair(dentry), bindex, allow_neg,
++ type, dlgt);
++ DEBUG_ON(IS_ROOT(dentry));
++ hidden_dir = hidden_parent->d_inode;
++ IMustLock(hidden_dir);
++
++ wh_found = 0;
++ sb = dentry->d_sb;
++ wh_able = sbr_is_whable(sb, bindex);
++ lkup.nfsmnt = au_nfsmnt(sb, bindex);
++ name = &dentry->d_name;
++ if (unlikely(wh_able)) {
++#if 0 //def CONFIG_AUFS_ROBR
++ if (strncmp(name->name, AUFS_WH_PFX, AUFS_WH_PFX_LEN))
++ wh_found = is_wh(hidden_parent, wh_name, /*try_sio*/0,
++ &lkup);
++ else
++ wh_found = -EPERM;
++#else
++ wh_found = is_wh(hidden_parent, wh_name, /*try_sio*/0, &lkup);
++#endif
++ }
++ //if (LktrCond) wh_found = -1;
++ hidden_dentry = ERR_PTR(wh_found);
++ if (!wh_found)
++ goto real_lookup;
++ if (unlikely(wh_found < 0))
++ goto out;
++
++ /* We found a whiteout */
++ //set_dbend(dentry, bindex);
++ set_dbwh(dentry, bindex);
++ if (!allow_neg)
++ return NULL; /* success */
++
++ real_lookup:
++ // do not superio.
++ hidden_dentry = lkup_one(name->name, hidden_parent, name->len, &lkup);
++ //if (LktrCond) {dput(hidden_dentry); hidden_dentry = ERR_PTR(-1);}
++ if (IS_ERR(hidden_dentry))
++ goto out;
++ DEBUG_ON(d_unhashed(hidden_dentry));
++ hidden_inode = hidden_dentry->d_inode;
++ if (!hidden_inode) {
++ if (!allow_neg)
++ goto out_neg;
++ } else if (wh_found
++ || (type && type != (hidden_inode->i_mode & S_IFMT)))
++ goto out_neg;
++
++ if (dbend(dentry) <= bindex)
++ set_dbend(dentry, bindex);
++ if (dbstart(dentry) == -1 || bindex < dbstart(dentry))
++ set_dbstart(dentry, bindex);
++ set_h_dptr(dentry, bindex, hidden_dentry);
++
++ if (!hidden_inode || !S_ISDIR(hidden_inode->i_mode) || !wh_able)
++ return hidden_dentry; /* success */
++
++ hi_lock_child(hidden_inode);
++ opq = is_diropq(hidden_dentry, &lkup);
++ //if (LktrCond) opq = -1;
++ i_unlock(hidden_inode);
++ if (opq > 0)
++ set_dbdiropq(dentry, bindex);
++ else if (unlikely(opq < 0)) {
++ set_h_dptr(dentry, bindex, NULL);
++ hidden_dentry = ERR_PTR(opq);
++ }
++ goto out;
++
++ out_neg:
++ dput(hidden_dentry);
++ hidden_dentry = NULL;
++ out:
++ TraceErrPtr(hidden_dentry);
++ return hidden_dentry;
++}
++
++/*
++ * returns the number of hidden positive dentries,
++ * otherwise an error.
++ */
++int lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type)
++{
++ int npositive, err, allow_neg, dlgt;
++ struct dentry *parent;
++ aufs_bindex_t bindex, btail;
++ const struct qstr *name = &dentry->d_name;
++ struct qstr whname;
++ struct super_block *sb;
++
++ LKTRTrace("%.*s, b%d, type 0%o\n", LNPair(name), bstart, type);
++ DEBUG_ON(bstart < 0 || IS_ROOT(dentry));
++ parent = dget_parent(dentry);
++
++#if 1 //ndef CONFIG_AUFS_ROBR
++ err = -EPERM;
++ if (unlikely(!strncmp(name->name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)))
++ goto out;
++#endif
++
++ err = au_alloc_whname(name->name, name->len, &whname);
++ //if (LktrCond) {au_free_whname(&whname); err = -1;}
++ if (unlikely(err))
++ goto out;
++
++ sb = dentry->d_sb;
++ dlgt = need_dlgt(sb);
++ allow_neg = !type;
++ npositive = 0;
++ btail = dbtaildir(parent);
++ for (bindex = bstart; bindex <= btail; bindex++) {
++ struct dentry *hidden_parent, *hidden_dentry;
++ struct inode *hidden_inode;
++ struct inode *hidden_dir;
++
++ hidden_dentry = au_h_dptr_i(dentry, bindex);
++ if (hidden_dentry) {
++ if (hidden_dentry->d_inode)
++ npositive++;
++ if (type != S_IFDIR)
++ break;
++ continue;
++ }
++ hidden_parent = au_h_dptr_i(parent, bindex);
++ if (!hidden_parent)
++ continue;
++ hidden_dir = hidden_parent->d_inode;
++ if (!hidden_dir || !S_ISDIR(hidden_dir->i_mode))
++ continue;
++
++ hi_lock_parent(hidden_dir);
++ hidden_dentry = do_lookup(hidden_parent, dentry, bindex,
++ &whname, allow_neg, type, dlgt);
++ // do not dput for testing
++ //if (LktrCond) {hidden_dentry = ERR_PTR(-1);}
++ i_unlock(hidden_dir);
++ err = PTR_ERR(hidden_dentry);
++ if (IS_ERR(hidden_dentry))
++ goto out_wh;
++ allow_neg = 0;
++
++ if (dbwh(dentry) != -1)
++ break;
++ if (!hidden_dentry)
++ continue;
++ hidden_inode = hidden_dentry->d_inode;
++ if (!hidden_inode)
++ continue;
++ npositive++;
++ if (!type)
++ type = hidden_inode->i_mode & S_IFMT;
++ if (type != S_IFDIR)
++ break;
++ else if (dbdiropq(dentry) != -1)
++ break;
++ }
++
++ if (npositive) {
++ LKTRLabel(positive);
++ au_update_dbstart(dentry);
++ }
++ err = npositive;
++
++ out_wh:
++ au_free_whname(&whname);
++ out:
++ dput(parent);
++ TraceErr(err);
++ return err;
++}
++
++struct dentry *sio_lkup_one(const char *name, struct dentry *parent, int len,
++ struct lkup_args *lkup)
++{
++ struct dentry *dentry;
++
++ LKTRTrace("%.*s/%.*s\n", DLNPair(parent), len, name);
++ IMustLock(parent->d_inode);
++
++ if (!au_test_perm(parent->d_inode, MAY_EXEC, lkup->dlgt))
++ dentry = lkup_one(name, parent, len, lkup);
++ else {
++ // ugly
++ int dlgt = lkup->dlgt;
++ struct lkup_one_args args = {
++ .errp = &dentry,
++ .name = name,
++ .parent = parent,
++ .len = len,
++ .lkup = lkup
++ };
++
++ lkup->dlgt = 0;
++ au_wkq_wait(call_lkup_one, &args, /*dlgt*/0);
++ lkup->dlgt = dlgt;
++ }
++
++ TraceErrPtr(dentry);
++ return dentry;
++}
++
++/*
++ * lookup @dentry on @bindex which should be negative.
++ */
++int lkup_neg(struct dentry *dentry, aufs_bindex_t bindex)
++{
++ int err;
++ struct dentry *parent, *hidden_parent, *hidden_dentry;
++ struct inode *hidden_dir;
++ struct lkup_args lkup;
++
++ LKTRTrace("%.*s, b%d\n", DLNPair(dentry), bindex);
++ parent = dget_parent(dentry);
++ DEBUG_ON(!parent || !parent->d_inode
++ || !S_ISDIR(parent->d_inode->i_mode));
++ hidden_parent = au_h_dptr_i(parent, bindex);
++ DEBUG_ON(!hidden_parent);
++ hidden_dir = hidden_parent->d_inode;
++ DEBUG_ON(!hidden_dir || !S_ISDIR(hidden_dir->i_mode));
++ IMustLock(hidden_dir);
++
++ lkup.nfsmnt = au_nfsmnt(dentry->d_sb, bindex);
++ lkup.dlgt = need_dlgt(dentry->d_sb);
++ hidden_dentry = sio_lkup_one(dentry->d_name.name, hidden_parent,
++ dentry->d_name.len, &lkup);
++ //if (LktrCond) {dput(hidden_dentry); hidden_dentry = ERR_PTR(-1);}
++ err = PTR_ERR(hidden_dentry);
++ if (IS_ERR(hidden_dentry))
++ goto out;
++ if (unlikely(hidden_dentry->d_inode)) {
++ err = -EIO;
++ IOErr("b%d %.*s should be negative.%s\n",
++ bindex, DLNPair(hidden_dentry),
++ au_flag_test(dentry->d_sb, AuFlag_UDBA_INOTIFY) ? "" :
++ " Try udba=inotify.");
++ dput(hidden_dentry);
++ goto out;
++ }
++
++ if (bindex < dbstart(dentry))
++ set_dbstart(dentry, bindex);
++ if (dbend(dentry) < bindex)
++ set_dbend(dentry, bindex);
++ set_h_dptr(dentry, bindex, hidden_dentry);
++ err = 0;
++
++ out:
++ dput(parent);
++ TraceErr(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, pgen, new_sz, sgen, dgen;
++ struct aufs_dinfo *dinfo;
++ struct super_block *sb;
++ struct dentry *parent;
++ aufs_bindex_t bindex, parent_bend, parent_bstart, bwh, bdiropq, bend;
++ struct aufs_hdentry *p;
++ //struct nameidata nd;
++
++ LKTRTrace("%.*s, type 0%o\n", DLNPair(dentry), type);
++ DiMustWriteLock(dentry);
++ sb = dentry->d_sb;
++ DEBUG_ON(IS_ROOT(dentry));
++ parent = dget_parent(dentry);
++ pgen = au_digen(parent);
++ sgen = au_sigen(sb);
++ dgen = au_digen(dentry);
++ DEBUG_ON(pgen != sgen);
++
++ npositive = -ENOMEM;
++ new_sz = sizeof(*dinfo->di_hdentry) * (sbend(sb) + 1);
++ dinfo = dtodi(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 aufs_hdentry tmp, *q;
++ aufs_bindex_t new_bindex;
++
++ hd = p->hd_dentry;
++ if (!hd)
++ continue;
++ hdp = dget_parent(hd);
++ if (hdp == au_h_dptr_i(parent, bindex)) {
++ dput(hdp);
++ continue;
++ }
++
++ new_bindex = au_find_dbindex(parent, hdp);
++ dput(hdp);
++ DEBUG_ON(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
++ hdput(p);
++ 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 != -1 && bwh <= sbend(sb) && sbr_is_whable(sb, bwh)))
++ dinfo->di_bwh = bwh;
++ dinfo->di_bdiropq = -1;
++ if (unlikely(bdiropq != -1 && bdiropq <= sbend(sb)
++ && sbr_is_whable(sb, bdiropq)))
++ dinfo->di_bdiropq = bdiropq;
++ parent_bend = 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 = dbstart(parent);
++ if (type != S_IFDIR && dinfo->di_bstart == parent_bstart)
++ goto out_dgen; /* success */
++
++#if 0
++ nd.last_type = LAST_ROOT;
++ nd.flags = LOOKUP_FOLLOW;
++ nd.depth = 0;
++ nd.mnt = mntget(??);
++ nd.dentry = dget(parent);
++#endif
++ npositive = lkup_dentry(dentry, parent_bstart, type);
++ //if (LktrCond) npositive = -1;
++ if (npositive < 0)
++ goto out;
++
++ out_dgen:
++ au_update_digen(dentry);
++ out:
++ dput(parent);
++ TraceErr(npositive);
++ return npositive;
++}
++
++static int h_d_revalidate(struct dentry *dentry, struct nameidata *nd,
++ int do_udba)
++{
++ int err, plus, locked, unhashed, is_root, h_plus, is_nfs;
++ struct nameidata fake_nd, *p;
++ aufs_bindex_t bindex, btail, bstart, ibs, ibe;
++ struct super_block *sb;
++ struct inode *inode, *first, *h_inode, *h_cached_inode;
++ umode_t mode, h_mode;
++ struct dentry *h_dentry;
++ int (*reval)(struct dentry *, struct nameidata *);
++ struct qstr *name;
++
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++ inode = dentry->d_inode;
++ DEBUG_ON(inode && au_digen(dentry) != au_iigen(inode));
++ //DbgDentry(dentry);
++ //DbgInode(inode);
++
++ err = 0;
++ sb = dentry->d_sb;
++ plus = 0;
++ mode = 0;
++ first = NULL;
++ ibs = 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);
++ ibs = ibstart(inode);
++ ibe = ibend(inode);
++ }
++
++ btail = bstart = dbstart(dentry);
++ if (inode && S_ISDIR(inode->i_mode))
++ btail = dbtaildir(dentry);
++ locked = 0;
++ if (nd) {
++ fake_nd = *nd;
++#ifndef CONFIG_AUFS_FAKE_DM
++ if (dentry != nd->dentry) {
++ di_read_lock_parent(nd->dentry, 0);
++ locked = 1;
++ }
++#endif
++ }
++ for (bindex = bstart; bindex <= btail; bindex++) {
++ h_dentry = au_h_dptr_i(dentry, bindex);
++ if (unlikely(!h_dentry))
++ continue;
++ 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),
++ DLNPair(dentry), DLNPair(h_dentry));
++ goto err;
++ }
++
++ reval = NULL;
++ if (h_dentry->d_op)
++ reval = h_dentry->d_op->d_revalidate;
++ if (unlikely(reval)) {
++ //LKTRLabel(hidden reval);
++ p = fake_dm(&fake_nd, nd, sb, bindex);
++ DEBUG_ON(IS_ERR(p));
++ err = !reval(h_dentry, p);
++ fake_dm_release(p);
++ if (unlikely(err)) {
++ //Dbg("here\n");
++ goto err;
++ }
++ }
++
++ if (unlikely(!do_udba))
++ continue;
++
++ /* UDBA tests */
++ h_inode = h_dentry->d_inode;
++ if (unlikely(!!inode != !!h_inode)) {
++ //Dbg("here\n");
++ goto err;
++ }
++
++ h_plus = plus;
++ h_mode = mode;
++ h_cached_inode = h_inode;
++ is_nfs = 0;
++ if (h_inode) {
++ h_mode = (h_inode->i_mode & S_IFMT);
++ h_plus = (h_inode->i_nlink > 0);
++ }
++ if (inode && ibs <= bindex && bindex <= ibe) {
++ h_cached_inode = au_h_iptr_i(inode, bindex);
++ //is_nfs = au_is_nfs(h_cached_inode->i_sb);
++ }
++
++ LKTRTrace("{%d, 0%o, %p}, h{%d, 0%o, %p}\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 /* && !is_nfs */))) {
++ //Dbg("here\n");
++ goto err;
++ }
++ continue;
++
++ err:
++ err = -EINVAL;
++ break;
++ }
++#ifndef CONFIG_AUFS_FAKE_DM
++ if (unlikely(locked))
++ di_read_unlock(nd->dentry, 0);
++#endif
++
++#if 0
++ // some filesystem uses CURRENT_TIME_SEC instead of CURRENT_TIME.
++ // NFS may stop IN_DELETE because of DCACHE_NFSFS_RENAMED.
++#if 0
++ && (!timespec_equal(&inode->i_ctime, &first->i_ctime)
++ || !timespec_equal(&inode->i_atime, &first->i_atime))
++#endif
++ if (unlikely(!err && udba && first))
++ au_cpup_attr_all(inode);
++#endif
++
++ TraceErr(err);
++ return err;
++}
++
++static int simple_reval_dpath(struct dentry *dentry, int sgen)
++{
++ int err;
++ mode_t type;
++ struct dentry *parent;
++ struct inode *inode;
++
++ LKTRTrace("%.*s, sgen %d\n", DLNPair(dentry), sgen);
++ SiMustAnyLock(dentry->d_sb);
++ DiMustWriteLock(dentry);
++ inode = dentry->d_inode;
++ DEBUG_ON(!inode);
++
++ if (au_digen(dentry) == sgen)
++ return 0;
++
++ parent = dget_parent(dentry);
++ di_read_lock_parent(parent, AUFS_I_RLOCK);
++ DEBUG_ON(au_digen(parent) != sgen);
++#ifdef CONFIG_AUFS_DEBUG
++ {
++ struct dentry *d = parent;
++ while (!IS_ROOT(d)) {
++ DEBUG_ON(au_digen(d) != sgen);
++ d = d->d_parent;
++ }
++ }
++#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, AUFS_I_RLOCK);
++ dput(parent);
++ TraceErr(err);
++ return err;
++}
++
++int au_reval_dpath(struct dentry *dentry, int sgen)
++{
++ int err;
++ struct dentry *d, *parent;
++ struct inode *inode;
++
++ LKTRTrace("%.*s, sgen %d\n", DLNPair(dentry), sgen);
++ DEBUG_ON(!dentry->d_inode);
++ DiMustWriteLock(dentry);
++
++ if (!stosi(dentry->d_sb)->si_failed_refresh_dirs)
++ return simple_reval_dpath(dentry, sgen);
++
++ /* slow loop, keep it simple and stupid */
++ /* cf: cpup_dirs() */
++ err = 0;
++ while (au_digen(dentry) != sgen) {
++ d = dentry;
++ while (1) {
++ parent = d->d_parent; // dget_parent()
++ if (au_digen(parent) == sgen)
++ break;
++ d = parent;
++ }
++
++ inode = d->d_inode;
++ if (d != dentry) {
++ //i_lock(inode);
++ di_write_lock_child(d);
++ }
++
++ /* someone might update our dentry while we were sleeping */
++ if (au_digen(d) != sgen) {
++ di_read_lock_parent(parent, AUFS_I_RLOCK);
++ /* 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, AUFS_I_RLOCK);
++ }
++
++ if (d != dentry) {
++ di_write_unlock(d);
++ //i_unlock(inode);
++ }
++ if (unlikely(err))
++ break;
++ }
++
++ TraceErr(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, sgen, err, do_udba;
++ struct super_block *sb;
++ struct inode *inode;
++
++ LKTRTrace("dentry %.*s\n", DLNPair(dentry));
++ if (nd && nd->dentry)
++ LKTRTrace("nd %.*s\n", DLNPair(nd->dentry));
++ //dir case: DEBUG_ON(dentry->d_parent != nd->dentry);
++ //remove failure case: DEBUG_ON(!IS_ROOT(dentry) && d_unhashed(dentry));
++ DEBUG_ON(!dentry->d_fsdata);
++ //DbgDentry(dentry);
++
++ err = -EINVAL;
++ inode = dentry->d_inode;
++ //DbgInode(inode);
++ sb = dentry->d_sb;
++ si_read_lock(sb);
++ sgen = au_sigen(sb);
++ if (au_digen(dentry) == sgen)
++ di_read_lock_child(dentry, !AUFS_I_RLOCK);
++ else {
++ DEBUG_ON(IS_ROOT(dentry));
++#ifdef ForceInotify
++ Dbg("UDBA or digen, %.*s\n", DLNPair(dentry));
++#endif
++ //i_lock(inode);
++ di_write_lock_child(dentry);
++ if (inode)
++ err = au_reval_dpath(dentry, sgen);
++ //err = -1;
++ di_downgrade_lock(dentry, AUFS_I_RLOCK);
++ //i_unlock(inode);
++ if (unlikely(err))
++ goto out;
++ ii_read_unlock(inode);
++ DEBUG_ON(au_iigen(inode) != sgen);
++ }
++
++ if (inode) {
++ if (au_iigen(inode) == sgen)
++ ii_read_lock_child(inode);
++ else {
++ DEBUG_ON(IS_ROOT(dentry));
++#ifdef ForceInotify
++ Dbg("UDBA or survived, %.*s\n", DLNPair(dentry));
++#endif
++ ii_write_lock_child(inode);
++ err = au_refresh_hinode(inode, dentry);
++ ii_downgrade_lock(inode);
++ if (unlikely(err))
++ goto out;
++ DEBUG_ON(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)) {
++ i_lock(inode);
++ ii_write_lock(inode);
++ au_cpup_attr_nlink(inode);
++ ii_write_unlock(inode);
++ i_unlock(inode);
++ }
++#endif
++
++ err = -EINVAL;
++ do_udba = !au_flag_test(sb, AuFlag_UDBA_NONE);
++ if (do_udba && inode && ibstart(inode) >= 0
++ && au_test_higen(inode, au_h_iptr(inode)))
++ goto out;
++ err = h_d_revalidate(dentry, nd, do_udba);
++ //err = -1;
++
++ out:
++ aufs_read_unlock(dentry, AUFS_I_RLOCK);
++ TraceErr(err);
++ valid = !err;
++ //au_debug_on();
++ if (!valid)
++ LKTRTrace("%.*s invalid\n", DLNPair(dentry));
++ //au_debug_off();
++ return valid;
++}
++
++static void aufs_d_release(struct dentry *dentry)
++{
++ struct aufs_dinfo *dinfo;
++ aufs_bindex_t bend, bindex;
++
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++ DEBUG_ON(!d_unhashed(dentry));
++
++ dinfo = dentry->d_fsdata;
++ if (unlikely(!dinfo))
++ return;
++
++ /* dentry may not be revalidated */
++ bindex = dinfo->di_bstart;
++ if (bindex >= 0) {
++ struct aufs_hdentry *p;
++ bend = dinfo->di_bend;
++ DEBUG_ON(bend < bindex);
++ p = dinfo->di_hdentry + bindex;
++ while (bindex++ <= bend) {
++ if (p->hd_dentry)
++ hdput(p);
++ p++;
++ }
++ }
++ kfree(dinfo->di_hdentry);
++ cache_free_dinfo(dinfo);
++}
++
++#if 0
++/* it may be called at remount time, too */
++static void aufs_d_iput(struct dentry *dentry, struct inode *inode)
++{
++ struct super_block *sb;
++
++ LKTRTrace("%.*s, i%lu\n", DLNPair(dentry), inode->i_ino);
++
++ sb = dentry->d_sb;
++#if 0
++ si_read_lock(sb);
++ if (unlikely(au_flag_test(sb, AuFlag_PLINK)
++ && au_is_plinked(sb, inode))) {
++ ii_write_lock(inode);
++ au_update_brange(inode, 1);
++ ii_write_unlock(inode);
++ }
++ si_read_unlock(sb);
++#endif
++ iput(inode);
++}
++#endif
++
++struct dentry_operations aufs_dop = {
++ .d_revalidate = aufs_d_revalidate,
++ .d_release = aufs_d_release
++ //.d_iput = aufs_d_iput
++};
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/dentry.h linux-2.6.22.1/fs/aufs/dentry.h
+--- linux-2.6.22.1.oorig/fs/aufs/dentry.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/dentry.h 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,183 @@
++/*
++ * 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: dentry.h,v 1.25 2007/05/14 03:41:52 sfjro Exp $ */
++
++#ifndef __AUFS_DENTRY_H__
++#define __AUFS_DENTRY_H__
++
++#ifdef __KERNEL__
++
++#include <linux/fs.h>
++#include <linux/aufs_type.h>
++#include "misc.h"
++
++struct aufs_hdentry {
++ struct dentry *hd_dentry;
++};
++
++struct aufs_dinfo {
++ atomic_t di_generation;
++
++ struct aufs_rwsem di_rwsem;
++ aufs_bindex_t di_bstart, di_bend, di_bwh, di_bdiropq;
++ struct aufs_hdentry *di_hdentry;
++};
++
++struct lkup_args {
++ struct vfsmount *nfsmnt;
++ int dlgt;
++ //struct super_block *sb;
++};
++
++/* ---------------------------------------------------------------------- */
++
++/* dentry.c */
++#if defined(CONFIG_AUFS_LHASH_PATCH) || defined(CONFIG_AUFS_DLGT)
++struct dentry *lkup_one(const char *name, struct dentry *parent, int len,
++ struct lkup_args *lkup);
++#else
++static inline
++struct dentry *lkup_one(const char *name, struct dentry *parent, int len,
++ struct lkup_args *lkup)
++{
++ return lookup_one_len(name, parent, len);
++}
++#endif
++
++extern struct dentry_operations aufs_dop;
++struct dentry *sio_lkup_one(const char *name, struct dentry *parent, int len,
++ struct lkup_args *lkup);
++int lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type);
++int 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, int sgen);
++
++/* dinfo.c */
++int au_alloc_dinfo(struct dentry *dentry);
++struct aufs_dinfo *dtodi(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);
++
++aufs_bindex_t dbstart(struct dentry *dentry);
++aufs_bindex_t dbend(struct dentry *dentry);
++aufs_bindex_t dbwh(struct dentry *dentry);
++aufs_bindex_t dbdiropq(struct dentry *dentry);
++struct dentry *au_h_dptr_i(struct dentry *dentry, aufs_bindex_t bindex);
++struct dentry *au_h_dptr(struct dentry *dentry);
++
++aufs_bindex_t dbtail(struct dentry *dentry);
++aufs_bindex_t dbtaildir(struct dentry *dentry);
++aufs_bindex_t dbtail_generic(struct dentry *dentry);
++
++void set_dbstart(struct dentry *dentry, aufs_bindex_t bindex);
++void set_dbend(struct dentry *dentry, aufs_bindex_t bindex);
++void set_dbwh(struct dentry *dentry, aufs_bindex_t bindex);
++void set_dbdiropq(struct dentry *dentry, aufs_bindex_t bindex);
++void hdput(struct aufs_hdentry *hdentry);
++void set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex,
++ struct dentry *h_dentry);
++
++void au_update_digen(struct dentry *dentry);
++void au_update_dbstart(struct dentry *dentry);
++int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry);
++
++/* ---------------------------------------------------------------------- */
++
++static inline int au_digen(struct dentry *d)
++{
++ return atomic_read(&dtodi(d)->di_generation);
++}
++
++#ifdef CONFIG_AUFS_HINOTIFY
++static inline void au_digen_dec(struct dentry *d)
++{
++ atomic_dec(&dtodi(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 ReadLockFunc(name, lsc) \
++static inline void di_read_lock_##name(struct dentry *d, int flags) \
++{di_read_lock(d, flags, AuLsc_DI_##lsc);}
++
++#define WriteLockFunc(name, lsc) \
++static inline void di_write_lock_##name(struct dentry *d) \
++{di_write_lock(d, AuLsc_DI_##lsc);}
++
++#define RWLockFuncs(name, lsc) \
++ ReadLockFunc(name, lsc); \
++ WriteLockFunc(name, lsc)
++
++RWLockFuncs(child, CHILD);
++RWLockFuncs(child2, CHILD2);
++RWLockFuncs(child3, CHILD3);
++RWLockFuncs(parent, PARENT);
++RWLockFuncs(parent2, PARENT2);
++RWLockFuncs(parent3, PARENT3);
++
++#undef ReadLockFunc
++#undef WriteLockFunc
++#undef RWLockFunc
++
++/* to debug easier, do not make them inlined functions */
++#define DiMustReadLock(d) do { \
++ SiMustAnyLock((d)->d_sb); \
++ RwMustReadLock(&dtodi(d)->di_rwsem); \
++} while (0)
++
++#define DiMustWriteLock(d) do { \
++ SiMustAnyLock((d)->d_sb); \
++ RwMustWriteLock(&dtodi(d)->di_rwsem); \
++} while (0)
++
++#define DiMustAnyLock(d) do { \
++ SiMustAnyLock((d)->d_sb); \
++ RwMustAnyLock(&dtodi(d)->di_rwsem); \
++} while (0)
++
++#define DiMustNoWaiters(d) RwMustNoWaiters(&dtodi(d)->di_rwsem)
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_DENTRY_H__ */
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/dinfo.c linux-2.6.22.1/fs/aufs/dinfo.c
+--- linux-2.6.22.1.oorig/fs/aufs/dinfo.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/dinfo.c 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,419 @@
++/*
++ * 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: dinfo.c,v 1.23 2007/05/07 03:43:36 sfjro Exp $ */
++
++#include "aufs.h"
++
++int au_alloc_dinfo(struct dentry *dentry)
++{
++ struct aufs_dinfo *dinfo;
++ struct super_block *sb;
++ int nbr;
++
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++ DEBUG_ON(dentry->d_fsdata);
++
++ dinfo = cache_alloc_dinfo();
++ //if (LktrCond) {cache_free_dinfo(dinfo); dinfo = NULL;}
++ if (dinfo) {
++ sb = dentry->d_sb;
++ nbr = sbend(sb) + 1;
++ if (unlikely(!nbr))
++ nbr++;
++ 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) {
++ rw_init_wlock_nested(&dinfo->di_rwsem, AuLsc_DI_PARENT);
++ dinfo->di_bstart = dinfo->di_bend = -1;
++ dinfo->di_bwh = dinfo->di_bdiropq = -1;
++ atomic_set(&dinfo->di_generation, au_sigen(sb));
++
++ dentry->d_fsdata = dinfo;
++ dentry->d_op = &aufs_dop;
++ return 0; /* success */
++ }
++ cache_free_dinfo(dinfo);
++ }
++ TraceErr(-ENOMEM);
++ return -ENOMEM;
++}
++
++struct aufs_dinfo *dtodi(struct dentry *dentry)
++{
++ struct aufs_dinfo *dinfo = dentry->d_fsdata;
++ DEBUG_ON(!dinfo
++ || !dinfo->di_hdentry
++ /* || stosi(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:
++ ii_write_lock_parent(inode);
++ break;
++ case AuLsc_DI_PARENT2:
++ ii_write_lock_parent2(inode);
++ break;
++ case AuLsc_DI_PARENT3:
++ 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:
++ ii_read_lock_parent(inode);
++ break;
++ case AuLsc_DI_PARENT2:
++ ii_read_lock_parent2(inode);
++ break;
++ case AuLsc_DI_PARENT3:
++ 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?
++ rw_read_lock_nested(&dtodi(d)->di_rwsem, lsc);
++ if (d->d_inode) {
++ if (flags & AUFS_I_WLOCK)
++ do_ii_write_lock(d->d_inode, lsc);
++ else if (flags & AUFS_I_RLOCK)
++ 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 (flags & AUFS_I_WLOCK)
++ ii_write_unlock(d->d_inode);
++ else if (flags & AUFS_I_RLOCK)
++ ii_read_unlock(d->d_inode);
++ }
++ rw_read_unlock(&dtodi(d)->di_rwsem);
++}
++
++void di_downgrade_lock(struct dentry *d, int flags)
++{
++ SiMustAnyLock(d->d_sb);
++ rw_dgrade_lock(&dtodi(d)->di_rwsem);
++ if (d->d_inode && (flags & AUFS_I_RLOCK))
++ ii_downgrade_lock(d->d_inode);
++}
++
++void di_write_lock(struct dentry *d, unsigned int lsc)
++{
++ SiMustAnyLock(d->d_sb);
++ // todo: always nested?
++ rw_write_lock_nested(&dtodi(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);
++ rw_write_unlock(&dtodi(d)->di_rwsem);
++}
++
++void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir)
++{
++ struct dentry *d;
++
++ TraceEnter();
++ DEBUG_ON(d1 == d2
++ || d1->d_inode == d2->d_inode
++ || d1->d_sb != d2->d_sb);
++
++ if (isdir)
++ for (d = d1; d->d_parent != d; d = d->d_parent) // dget_parent()
++ if (d->d_parent == d2) {
++ di_write_lock_child(d1);
++ di_write_lock_child2(d2);
++ return;
++ }
++
++ di_write_lock_child(d2);
++ di_write_lock_child2(d1);
++}
++
++void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir)
++{
++ struct dentry *d;
++
++ TraceEnter();
++ DEBUG_ON(d1 == d2
++ || d1->d_inode == d2->d_inode
++ || d1->d_sb != d2->d_sb);
++
++ if (isdir)
++ for (d = d1; d->d_parent != d; d = d->d_parent) // dget_parent()
++ if (d->d_parent == d2) {
++ di_write_lock_parent(d1);
++ di_write_lock_parent2(d2);
++ return;
++ }
++
++ 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)
++ rw_write_unlock(&dtodi(d2)->di_rwsem);
++ else
++ di_write_unlock(d2);
++}
++
++/* ---------------------------------------------------------------------- */
++
++aufs_bindex_t dbstart(struct dentry *dentry)
++{
++ DiMustAnyLock(dentry);
++ return dtodi(dentry)->di_bstart;
++}
++
++aufs_bindex_t dbend(struct dentry *dentry)
++{
++ DiMustAnyLock(dentry);
++ return dtodi(dentry)->di_bend;
++}
++
++aufs_bindex_t dbwh(struct dentry *dentry)
++{
++ DiMustAnyLock(dentry);
++ return dtodi(dentry)->di_bwh;
++}
++
++aufs_bindex_t dbdiropq(struct dentry *dentry)
++{
++ DiMustAnyLock(dentry);
++ DEBUG_ON(dentry->d_inode
++ && dentry->d_inode->i_mode
++ && !S_ISDIR(dentry->d_inode->i_mode));
++ return dtodi(dentry)->di_bdiropq;
++}
++
++struct dentry *au_h_dptr_i(struct dentry *dentry, aufs_bindex_t bindex)
++{
++ struct dentry *d;
++
++ DiMustAnyLock(dentry);
++ if (dbstart(dentry) < 0 || bindex < dbstart(dentry))
++ return NULL;
++ DEBUG_ON(bindex < 0
++ /* || bindex > sbend(dentry->d_sb) */);
++ d = dtodi(dentry)->di_hdentry[0 + bindex].hd_dentry;
++ DEBUG_ON(d && (atomic_read(&d->d_count) <= 0));
++ return d;
++}
++
++struct dentry *au_h_dptr(struct dentry *dentry)
++{
++ return au_h_dptr_i(dentry, dbstart(dentry));
++}
++
++aufs_bindex_t dbtail(struct dentry *dentry)
++{
++ aufs_bindex_t bend, bwh;
++
++ bend = dbend(dentry);
++ if (0 <= bend) {
++ bwh = dbwh(dentry);
++ //DEBUG_ON(bend < bwh);
++ if (!bwh)
++ return bwh;
++ if (0 < bwh && bwh < bend)
++ return bwh - 1;
++ }
++ return bend;
++}
++
++aufs_bindex_t dbtaildir(struct dentry *dentry)
++{
++ aufs_bindex_t bend, bopq;
++
++ DEBUG_ON(dentry->d_inode
++ && dentry->d_inode->i_mode
++ && !S_ISDIR(dentry->d_inode->i_mode));
++
++ bend = dbtail(dentry);
++ if (0 <= bend) {
++ bopq = dbdiropq(dentry);
++ DEBUG_ON(bend < bopq);
++ if (0 <= bopq && bopq < bend)
++ bend = bopq;
++ }
++ return bend;
++}
++
++aufs_bindex_t dbtail_generic(struct dentry *dentry)
++{
++ struct inode *inode;
++
++ inode = dentry->d_inode;
++ if (inode && S_ISDIR(inode->i_mode))
++ return dbtaildir(dentry);
++ else
++ return dbtail(dentry);
++}
++
++/* ---------------------------------------------------------------------- */
++
++// hard/soft set
++void set_dbstart(struct dentry *dentry, aufs_bindex_t bindex)
++{
++ DiMustWriteLock(dentry);
++ DEBUG_ON(sbend(dentry->d_sb) < bindex);
++ /* */
++ dtodi(dentry)->di_bstart = bindex;
++}
++
++void set_dbend(struct dentry *dentry, aufs_bindex_t bindex)
++{
++ DiMustWriteLock(dentry);
++ DEBUG_ON(sbend(dentry->d_sb) < bindex
++ || bindex < dbstart(dentry));
++ dtodi(dentry)->di_bend = bindex;
++}
++
++void set_dbwh(struct dentry *dentry, aufs_bindex_t bindex)
++{
++ DiMustWriteLock(dentry);
++ DEBUG_ON(sbend(dentry->d_sb) < bindex);
++ /* dbwh can be outside of bstart - bend range */
++ dtodi(dentry)->di_bwh = bindex;
++}
++
++void set_dbdiropq(struct dentry *dentry, aufs_bindex_t bindex)
++{
++ DiMustWriteLock(dentry);
++ DEBUG_ON(sbend(dentry->d_sb) < bindex);
++ DEBUG_ON((bindex != -1
++ && (bindex < dbstart(dentry) || dbend(dentry) < bindex))
++ || (dentry->d_inode
++ && dentry->d_inode->i_mode
++ && !S_ISDIR(dentry->d_inode->i_mode)));
++ dtodi(dentry)->di_bdiropq = bindex;
++}
++
++void hdput(struct aufs_hdentry *hd)
++{
++ dput(hd->hd_dentry);
++}
++
++void set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex,
++ struct dentry *h_dentry)
++{
++ struct aufs_hdentry *hd = dtodi(dentry)->di_hdentry + bindex;
++ DiMustWriteLock(dentry);
++ DEBUG_ON(bindex < dtodi(dentry)->di_bstart
++ || bindex > dtodi(dentry)->di_bend
++ || (h_dentry && atomic_read(&h_dentry->d_count) <= 0)
++ || (h_dentry && hd->hd_dentry)
++ );
++ if (hd->hd_dentry)
++ hdput(hd);
++ hd->hd_dentry = h_dentry;
++}
++
++/* ---------------------------------------------------------------------- */
++
++void au_update_digen(struct dentry *dentry)
++{
++ //DiMustWriteLock(dentry);
++ DEBUG_ON(!dentry->d_sb);
++ atomic_set(&dtodi(dentry)->di_generation, au_sigen(dentry->d_sb));
++}
++
++void au_update_dbstart(struct dentry *dentry)
++{
++ aufs_bindex_t bindex, bstart = dbstart(dentry), bend = dbend(dentry);
++ struct dentry *hidden_dentry;
++
++ DiMustWriteLock(dentry);
++ for (bindex = bstart; bindex <= bend; bindex++) {
++ hidden_dentry = au_h_dptr_i(dentry, bindex);
++ if (!hidden_dentry)
++ continue;
++ if (hidden_dentry->d_inode) {
++ set_dbstart(dentry, bindex);
++ return;
++ }
++ set_h_dptr(dentry, bindex, NULL);
++ }
++ //set_dbstart(dentry, -1);
++ //set_dbend(dentry, -1);
++}
++
++int au_find_dbindex(struct dentry *dentry, struct dentry *hidden_dentry)
++{
++ aufs_bindex_t bindex, bend;
++
++ bend = dbend(dentry);
++ for (bindex = dbstart(dentry); bindex <= bend; bindex++)
++ if (au_h_dptr_i(dentry, bindex) == hidden_dentry)
++ return bindex;
++ return -1;
++}
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/dir.c linux-2.6.22.1/fs/aufs/dir.c
+--- linux-2.6.22.1.oorig/fs/aufs/dir.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/dir.c 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,564 @@
++/*
++ * 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: dir.c,v 1.36 2007/05/14 03:38:52 sfjro Exp $ */
++
++#include "aufs.h"
++
++static int reopen_dir(struct file *file)
++{
++ int err;
++ struct dentry *dentry, *hidden_dentry;
++ aufs_bindex_t bindex, btail, bstart;
++ struct file *hidden_file;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++ DEBUG_ON(!S_ISDIR(dentry->d_inode->i_mode));
++
++ /* open all hidden dirs */
++ bstart = dbstart(dentry);
++#if 1
++ for (bindex = fbstart(file); bindex < bstart; bindex++)
++ set_h_fptr(file, bindex, NULL);
++#endif
++ set_fbstart(file, bstart);
++ btail = dbtaildir(dentry);
++#if 1
++ for (bindex = fbend(file); btail < bindex; bindex--)
++ set_h_fptr(file, bindex, NULL);
++#endif
++ set_fbend(file, btail);
++ for (bindex = bstart; bindex <= btail; bindex++) {
++ hidden_dentry = au_h_dptr_i(dentry, bindex);
++ if (!hidden_dentry)
++ continue;
++ hidden_file = au_h_fptr_i(file, bindex);
++ if (hidden_file) {
++ DEBUG_ON(hidden_file->f_dentry != hidden_dentry);
++ continue;
++ }
++
++ hidden_file = hidden_open(dentry, bindex, file->f_flags);
++ // unavailable
++ //if (LktrCond) {fput(hidden_file);
++ //br_put(stobr(dentry->d_sb, bindex));hidden_file=ERR_PTR(-1);}
++ err = PTR_ERR(hidden_file);
++ if (IS_ERR(hidden_file))
++ goto out; // close all?
++ //cpup_file_flags(hidden_file, file);
++ set_h_fptr(file, bindex, hidden_file);
++ }
++ err = 0;
++
++ out:
++ TraceErr(err);
++ return err;
++}
++
++static int do_open_dir(struct file *file, int flags)
++{
++ int err;
++ aufs_bindex_t bindex, btail;
++ struct dentry *dentry, *hidden_dentry;
++ struct file *hidden_file;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s, 0x%x\n", DLNPair(dentry), flags);
++ DEBUG_ON(!dentry->d_inode || !S_ISDIR(dentry->d_inode->i_mode));
++
++ err = 0;
++ set_fvdir_cache(file, NULL);
++ file->f_version = dentry->d_inode->i_version;
++ bindex = dbstart(dentry);
++ set_fbstart(file, bindex);
++ btail = dbtaildir(dentry);
++ set_fbend(file, btail);
++ for (; !err && bindex <= btail; bindex++) {
++ hidden_dentry = au_h_dptr_i(dentry, bindex);
++ if (!hidden_dentry)
++ continue;
++
++ hidden_file = hidden_open(dentry, bindex, flags);
++ //if (LktrCond) {fput(hidden_file);
++ //br_put(stobr(dentry->d_sb, bindex));hidden_file=ERR_PTR(-1);}
++ if (!IS_ERR(hidden_file)) {
++ set_h_fptr(file, bindex, hidden_file);
++ continue;
++ }
++ err = PTR_ERR(hidden_file);
++ }
++ if (!err)
++ return 0; /* success */
++
++ /* close all */
++ for (bindex = fbstart(file); !err && bindex <= btail; bindex++)
++ set_h_fptr(file, bindex, NULL);
++ set_fbstart(file, -1);
++ 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 aufs_vdir *vdir_cache;
++ struct super_block *sb;
++
++ LKTRTrace("i%lu, %.*s\n", inode->i_ino, DLNPair(file->f_dentry));
++
++ sb = file->f_dentry->d_sb;
++ si_read_lock(sb);
++ fi_write_lock(file);
++ vdir_cache = fvdir_cache(file);
++ if (vdir_cache)
++ free_vdir(vdir_cache);
++ fi_write_unlock(file);
++ au_fin_finfo(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", DLNPair(dentry), datasync);
++ DiMustAnyLock(dentry);
++ sb = dentry->d_sb;
++ SiMustAnyLock(sb);
++ inode = dentry->d_inode;
++ IMustLock(inode);
++ IiMustAnyLock(inode);
++
++ err = 0;
++ bend = dbend(dentry);
++ for (bindex = dbstart(dentry); !err && bindex <= bend; bindex++) {
++ struct dentry *h_dentry;
++ struct inode *h_inode;
++ struct file_operations *fop;
++
++ if (test_ro(sb, bindex, inode))
++ continue;
++ h_dentry = au_h_dptr_i(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 */
++ //hdir_lock(h_inode, inode, bindex);
++ i_lock(h_inode);
++ 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);
++ //hdir_unlock(h_inode, inode, bindex);
++ i_unlock(h_inode);
++ }
++
++ TraceErr(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 *hidden_file;
++ struct super_block *sb;
++ aufs_bindex_t bend, bindex;
++
++ LKTRTrace("%.*s, %d\n", DLNPair(dentry), datasync);
++ inode = dentry->d_inode;
++ IMustLock(inode);
++
++ err = 0;
++ sb = dentry->d_sb;
++ si_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, !AUFS_I_WLOCK);
++
++ ii_write_lock_child(inode);
++ if (file) {
++ bend = fbend(file);
++ for (bindex = fbstart(file); !err && bindex <= bend; bindex++) {
++ hidden_file = au_h_fptr_i(file, bindex);
++ if (!hidden_file || test_ro(sb, bindex, inode))
++ continue;
++
++ err = -EINVAL;
++ if (hidden_file->f_op && hidden_file->f_op->fsync) {
++ // todo: try do_fsync() in fs/sync.c
++#if 0
++ DEBUG_ON(hidden_file->f_dentry->d_inode
++ != au_h_iptr_i(inode, bindex));
++ hdir_lock(hidden_file->f_dentry->d_inode, inode,
++ bindex);
++#else
++ i_lock(hidden_file->f_dentry->d_inode);
++#endif
++ err = hidden_file->f_op->fsync
++ (hidden_file, hidden_file->f_dentry,
++ datasync);
++ //err = -1;
++#if 0
++ hdir_unlock(hidden_file->f_dentry->d_inode,
++ inode, bindex);
++#else
++ i_unlock(hidden_file->f_dentry->d_inode);
++#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, !AUFS_I_WLOCK);
++
++ out:
++ si_read_unlock(sb);
++ TraceErr(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", DLNPair(dentry), file->f_pos);
++ inode = dentry->d_inode;
++ IMustLock(inode);
++
++ au_nfsd_lockdep_off();
++ sb = dentry->d_sb;
++ si_read_lock(sb);
++ 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_init_vdir(file);
++ if (unlikely(err)) {
++ ii_write_unlock(inode);
++ goto out_unlock;
++ }
++ //DbgVdir(fvdir_cache(file));// goto out_unlock;
++
++ /* nfsd filldir calls lookup_one_len(). */
++ ii_downgrade_lock(inode);
++ err = au_fill_de(file, dirent, filldir);
++ //DbgVdir(fvdir_cache(file));// goto out_unlock;
++
++ inode->i_atime = au_h_iptr(inode)->i_atime;
++ 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
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++struct test_empty_arg {
++ struct aufs_nhash *whlist;
++ int whonly;
++ aufs_bindex_t bindex;
++ int err, called;
++};
++
++static int test_empty_cb(void *__arg, const char *__name, int namelen,
++ loff_t offset, filldir_ino_t ino, unsigned int d_type)
++{
++ struct test_empty_arg *arg = __arg;
++ char *name = (void*)__name;
++
++ LKTRTrace("%.*s\n", namelen, name);
++
++ arg->err = 0;
++ arg->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 (arg->whonly && !test_known_wh(arg->whlist, name, namelen))
++ arg->err = -ENOTEMPTY;
++ goto out;
++ }
++
++ name += AUFS_WH_PFX_LEN;
++ namelen -= AUFS_WH_PFX_LEN;
++ if (!test_known_wh(arg->whlist, name, namelen))
++ arg->err = append_wh(arg->whlist, name, namelen, arg->bindex);
++
++ out:
++ //smp_mb();
++ TraceErr(arg->err);
++ return arg->err;
++}
++
++static int do_test_empty(struct dentry *dentry, struct test_empty_arg *arg)
++{
++ int err, dlgt;
++ struct file *hidden_file;
++
++ LKTRTrace("%.*s, {%p, %d, %d}\n",
++ DLNPair(dentry), arg->whlist, arg->whonly, arg->bindex);
++
++ hidden_file = hidden_open(dentry, arg->bindex,
++ O_RDONLY | O_NONBLOCK | O_DIRECTORY
++ | O_LARGEFILE);
++ err = PTR_ERR(hidden_file);
++ if (IS_ERR(hidden_file))
++ goto out;
++
++ dlgt = need_dlgt(dentry->d_sb);
++ //hidden_file->f_pos = 0;
++ do {
++ arg->err = 0;
++ arg->called = 0;
++ //smp_mb();
++ err = vfsub_readdir(hidden_file, test_empty_cb, arg, dlgt);
++ if (err >= 0)
++ err = arg->err;
++ } while (!err && arg->called);
++ fput(hidden_file);
++ sbr_put(dentry->d_sb, arg->bindex);
++
++ out:
++ TraceErr(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;
++ struct dentry *hidden_dentry;
++ struct inode *hidden_inode;
++
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++ hidden_dentry = au_h_dptr_i(dentry, arg->bindex);
++ DEBUG_ON(!hidden_dentry);
++ hidden_inode = hidden_dentry->d_inode;
++ DEBUG_ON(!hidden_inode || !S_ISDIR(hidden_inode->i_mode));
++
++ hi_lock_child(hidden_inode);
++ err = au_test_perm(hidden_inode, MAY_EXEC | MAY_READ,
++ need_dlgt(dentry->d_sb));
++ i_unlock(hidden_inode);
++ if (!err)
++ err = do_test_empty(dentry, arg);
++ else {
++ struct do_test_empty_args args = {
++ .errp = &err,
++ .dentry = dentry,
++ .arg = arg
++ };
++ au_wkq_wait(call_do_test_empty, &args, /*dlgt*/0);
++ }
++
++ TraceErr(err);
++ return err;
++}
++
++int au_test_empty_lower(struct dentry *dentry)
++{
++ int err;
++ struct inode *inode;
++ struct test_empty_arg arg;
++ struct aufs_nhash *whlist;
++ aufs_bindex_t bindex, bstart, btail;
++
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++ inode = dentry->d_inode;
++ DEBUG_ON(!inode || !S_ISDIR(inode->i_mode));
++
++ whlist = nhash_new(GFP_KERNEL);
++ err = PTR_ERR(whlist);
++ if (IS_ERR(whlist))
++ goto out;
++
++ bstart = dbstart(dentry);
++ arg.whlist = whlist;
++ arg.whonly = 0;
++ arg.bindex = bstart;
++ err = do_test_empty(dentry, &arg);
++ if (unlikely(err))
++ goto out_whlist;
++
++ arg.whonly = 1;
++ btail = dbtaildir(dentry);
++ for (bindex = bstart + 1; !err && bindex <= btail; bindex++) {
++ struct dentry *hidden_dentry;
++ hidden_dentry = au_h_dptr_i(dentry, bindex);
++ if (hidden_dentry && hidden_dentry->d_inode) {
++ DEBUG_ON(!S_ISDIR(hidden_dentry->d_inode->i_mode));
++ arg.bindex = bindex;
++ err = do_test_empty(dentry, &arg);
++ }
++ }
++
++ out_whlist:
++ nhash_del(whlist);
++ out:
++ TraceErr(err);
++ return err;
++}
++
++int test_empty(struct dentry *dentry, struct aufs_nhash *whlist)
++{
++ int err;
++ struct inode *inode;
++ struct test_empty_arg arg;
++ aufs_bindex_t bindex, btail;
++
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++ inode = dentry->d_inode;
++ DEBUG_ON(!inode || !S_ISDIR(inode->i_mode));
++
++ err = 0;
++ arg.whlist = whlist;
++ arg.whonly = 1;
++ btail = dbtaildir(dentry);
++ for (bindex = dbstart(dentry); !err && bindex <= btail; bindex++) {
++ struct dentry *hidden_dentry;
++ hidden_dentry = au_h_dptr_i(dentry, bindex);
++ if (hidden_dentry && hidden_dentry->d_inode) {
++ DEBUG_ON(!S_ISDIR(hidden_dentry->d_inode->i_mode));
++ arg.bindex = bindex;
++ err = sio_test_empty(dentry, &arg);
++ }
++ }
++
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++void au_add_nlink(struct inode *dir, struct inode *h_dir)
++{
++ DEBUG_ON(!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;
++}
++
++void au_sub_nlink(struct inode *dir, struct inode *h_dir)
++{
++ DEBUG_ON(!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;
++}
++
++/* ---------------------------------------------------------------------- */
++
++#if 0 // comment
++struct file_operations {
++ struct module *owner;
++ loff_t (*llseek) (struct file *, loff_t, int);
++ ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
++ ssize_t (*aio_read) (struct kiocb *, char __user *, size_t, loff_t);
++ ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
++ ssize_t (*aio_write) (struct kiocb *, const char __user *, size_t, loff_t);
++ int (*readdir) (struct file *, void *, filldir_t);
++ unsigned int (*poll) (struct file *, struct poll_table_struct *);
++ int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
++ long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
++ long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
++ int (*mmap) (struct file *, struct vm_area_struct *);
++ int (*open) (struct inode *, struct file *);
++ int (*flush) (struct file *);
++ int (*release) (struct inode *, struct file *);
++ int (*fsync) (struct file *, struct dentry *, int datasync);
++ int (*aio_fsync) (struct kiocb *, int datasync);
++ int (*fasync) (int, struct file *, int);
++ int (*lock) (struct file *, int, struct file_lock *);
++ ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
++ ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
++ ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);
++ ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
++ unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
++ int (*check_flags)(int);
++ int (*dir_notify)(struct file *file, unsigned long arg);
++ int (*flock) (struct file *, int, struct file_lock *);
++};
++#endif
++
++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,
++};
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/dir.h linux-2.6.22.1/fs/aufs/dir.h
+--- linux-2.6.22.1.oorig/fs/aufs/dir.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/dir.h 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,125 @@
++/*
++ * 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: dir.h,v 1.18 2007/05/14 03:41:52 sfjro Exp $ */
++
++#ifndef __AUFS_DIR_H__
++#define __AUFS_DIR_H__
++
++#ifdef __KERNEL__
++
++#include <linux/fs.h>
++#include <linux/version.h>
++#include <linux/aufs_type.h>
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
++#define filldir_ino_t u64
++#else
++#define filldir_ino_t ino_t
++#endif
++
++/* ---------------------------------------------------------------------- */
++
++/* need to be faster and smaller */
++
++#define AUFS_DEBLK_SIZE 512 // todo: changable
++#define AUFS_NHASH_SIZE 32 // todo: changable
++#if AUFS_DEBLK_SIZE < NAME_MAX || PAGE_SIZE < AUFS_DEBLK_SIZE
++#error invalid size AUFS_DEBLK_SIZE
++#endif
++
++typedef char aufs_deblk_t[AUFS_DEBLK_SIZE];
++
++struct aufs_nhash {
++ struct hlist_head heads[AUFS_NHASH_SIZE];
++};
++
++struct aufs_destr {
++ unsigned char len;
++ char name[0];
++} __attribute__ ((packed));
++
++struct aufs_dehstr {
++ struct hlist_node hash;
++ struct aufs_destr *str;
++};
++
++struct aufs_de {
++ ino_t de_ino;
++ unsigned char de_type;
++ //caution: packed
++ struct aufs_destr de_str;
++} __attribute__ ((packed));
++
++struct aufs_wh {
++ struct hlist_node wh_hash;
++ aufs_bindex_t wh_bindex;
++ struct aufs_destr wh_str;
++} __attribute__ ((packed));
++
++union aufs_deblk_p {
++ unsigned char *p;
++ aufs_deblk_t *deblk;
++ struct aufs_de *de;
++};
++
++struct aufs_vdir {
++ aufs_deblk_t **vd_deblk;
++ int vd_nblk;
++ struct {
++ int i;
++ union aufs_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 test_empty(struct dentry *dentry, struct aufs_nhash *whlist);
++void au_add_nlink(struct inode *dir, struct inode *h_dir);
++void au_sub_nlink(struct inode *dir, struct inode *h_dir);
++
++/* vdir.c */
++struct aufs_nhash *nhash_new(gfp_t gfp);
++void nhash_del(struct aufs_nhash *nhash);
++void nhash_init(struct aufs_nhash *nhash);
++void nhash_move(struct aufs_nhash *dst, struct aufs_nhash *src);
++void nhash_fin(struct aufs_nhash *nhash);
++int is_longer_wh(struct aufs_nhash *whlist, aufs_bindex_t btgt, int limit);
++int test_known_wh(struct aufs_nhash *whlist, char *name, int namelen);
++int append_wh(struct aufs_nhash *whlist, char *name, int namelen,
++ aufs_bindex_t bindex);
++void free_vdir(struct aufs_vdir *vdir);
++int au_init_vdir(struct file *file);
++int au_fill_de(struct file *file, void *dirent, filldir_t filldir);
++
++/* ---------------------------------------------------------------------- */
++
++static inline
++unsigned int au_name_hash(const unsigned char *name, unsigned int len)
++{
++ return (full_name_hash(name, len) % AUFS_NHASH_SIZE);
++}
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_DIR_H__ */
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/export.c linux-2.6.22.1/fs/aufs/export.c
+--- linux-2.6.22.1.oorig/fs/aufs/export.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/export.c 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,585 @@
++/*
++ * 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: export.c,v 1.7 2007/05/14 03:38:24 sfjro Exp $ */
++
++#include "aufs.h"
++
++extern struct export_operations export_op_default;
++#define CALL(ops, func) (((ops)->func) ? ((ops)->func) : export_op_default.func)
++#define is_anon(d) ((d)->d_flags & DCACHE_DISCONNECTED)
++
++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
++}
++
++static void decode_br_id_sigen(__u32 a, aufs_bindex_t *br_id,
++ aufs_bindex_t *sigen)
++{
++ BUILD_BUG_ON((sizeof(*br_id) + sizeof(*sigen)) > sizeof(a));
++ *br_id = a >> 16;
++ DEBUG_ON(*br_id < 0);
++ *sigen = a;
++ DEBUG_ON(*sigen < 0);
++}
++
++static __u32 encode_br_id_sigen(aufs_bindex_t br_id, aufs_bindex_t sigen)
++{
++ DEBUG_ON(br_id < 0 || sigen < 0);
++ return (br_id << 16) | sigen;
++}
++
++/* NFS file handle */
++enum {
++ /* support 64bit inode number */
++ /* but untested */
++ Fh_br_id_sigen,
++ Fh_ino1,
++#if BITS_PER_LONG == 64
++ Fh_ino2,
++#endif
++ Fh_dir_ino1,
++#if BITS_PER_LONG == 64
++ Fh_dir_ino2,
++#endif
++ Fh_h_ino1,
++#if BITS_PER_LONG == 64
++ Fh_h_ino2,
++#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 struct dentry *decode_by_ino(struct super_block *sb, ino_t ino,
++ ino_t dir_ino)
++{
++ struct dentry *dentry;
++ 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 (!is_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
++ && !is_anon(dentry)
++ && dentry->d_parent->d_inode->i_ino == dir_ino)
++ goto out_iput; /* success */
++
++ dput(dentry);
++ dentry = NULL;
++ }
++
++ out_iput:
++ iput(inode);
++ out:
++ TraceErrPtr(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,
++ filldir_ino_t 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(is_anon(parent))) {
++ dput(parent);
++ goto out_iput;
++ }
++ } else
++ goto out_iput;
++
++ file = dentry_open(parent, NULL, 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 lkup_one(), nor dlgt */
++ i_lock(dir);
++ dentry = lookup_one_len(arg.name, parent, arg.namelen);
++ i_unlock(dir);
++ TraceErrPtr(dentry);
++ }
++
++ //out_putname:
++ __putname(arg.name);
++ out_fput:
++ fput(file);
++ out_iput:
++ iput(dir);
++ out:
++ TraceErrPtr(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,
++ filldir_ino_t ino, unsigned int d_type)
++{
++ struct append_name *a = arg;
++ char *p;
++
++ a->called++;
++ if (ino != a->h_ino)
++ return 0;
++
++ DEBUG_ON(len == 1 && *name == '.');
++ DEBUG_ON(len == 2 && name[0] == '.' && name[1] == '.');
++ a->len = strlen(a->h_path);
++ memmove(a->h_path - a->len - 1, a->h_path, a->len);
++ a->h_path -= a->len + 1;
++ p = a->h_path + a->len;
++ *p++ = '/';
++ memcpy(p, name, a->len);
++ a->len += 1 + len;
++ a->found++;
++ return 1;
++}
++
++static int h_acceptable(void *expv, struct dentry *dentry)
++{
++ return 1;
++}
++
++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;
++ struct append_name arg;
++ int len, err;
++ struct file *h_file;
++ struct nameidata nd;
++ struct aufs_branch *br;
++
++ LKTRTrace("b%d\n", bindex);
++ SiMustAnyLock(sb);
++
++ br = stobr(sb, bindex);
++ //br_get(br);
++ h_mnt = br->br_mnt;
++ h_sb = h_mnt->mnt_sb;
++ LKTRTrace("%s, h_decode_fh\n", au_sbtype(h_sb));
++ h_parent = CALL(h_sb->s_export_op, decode_fh)
++ (h_sb, 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))) {
++ Warn1("%s decode_fh failed\n", au_sbtype(h_sb));
++ goto out;
++ }
++ dentry = NULL;
++ if (unlikely(is_anon(h_parent))) {
++ Warn1("%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, !AUFS_I_RLOCK);
++ h_root = au_h_dptr_i(root, bindex);
++ di_read_unlock(root, !AUFS_I_RLOCK);
++ arg.h_path = d_path(h_root, h_mnt, path, PATH_MAX);
++ dentry = (void*)arg.h_path;
++ if (unlikely(!arg.h_path || IS_ERR(arg.h_path)))
++ goto out_putname;
++ len = strlen(arg.h_path);
++ arg.h_path = d_path(h_parent, h_mnt, path, PATH_MAX);
++ dentry = (void*)arg.h_path;
++ if (unlikely(!arg.h_path || IS_ERR(arg.h_path)))
++ goto out_putname;
++ LKTRTrace("%s\n", arg.h_path);
++ if (len != 1)
++ arg.h_path += len;
++ LKTRTrace("%s\n", arg.h_path);
++
++ /* cf. fs/exportfs/expfs.c */
++ h_file = dentry_open(h_parent, NULL, au_dir_roflags);
++ dentry = (void*)h_file;
++ if (IS_ERR(h_file))
++ goto out_putname;
++
++ 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("%s, %d\n", arg.h_path, arg.len);
++
++ p = d_path(root, stosi(sb)->si_mnt, path, PATH_MAX - arg.len - 2);
++ dentry = (void*)p;
++ if (unlikely(!p || IS_ERR(p)))
++ goto out_fput;
++ p[strlen(p)] = '/';
++ LKTRTrace("%s\n", p);
++
++ err = path_lookup(p, LOOKUP_FOLLOW, &nd);
++ dentry = ERR_PTR(err);
++ if (!err) {
++ dentry = dget(nd.dentry);
++ if (unlikely(is_anon(dentry))) {
++ dput(dentry);
++ dentry = ERR_PTR(-ESTALE);
++ }
++ path_release(&nd);
++ }
++
++ out_fput:
++ fput(h_file);
++ out_putname:
++ __putname(path);
++ out:
++ //br_put(br);
++ TraceErrPtr(dentry);
++ return dentry;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static 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, sigen_v;
++ struct inode *inode, *h_inode;
++
++ //au_debug_on();
++ LKTRTrace("%d, fh{i%u, br_id_sigen 0x%x, hi%u}\n",
++ fh_type, fh[Fh_ino], fh[Fh_br_id_sigen], fh[Fh_h_ino]);
++ DEBUG_ON(fh_len < Fh_tail);
++
++ si_read_lock(sb);
++ lockdep_off();
++
++ /* branch id may be wrapped around */
++ dentry = ERR_PTR(-ESTALE);
++ decode_br_id_sigen(fh[Fh_br_id_sigen], &br_id, &sigen_v);
++ bindex = find_brindex(sb, br_id);
++ if (unlikely(bindex < 0 || au_sigen(sb) < sigen_v))
++ 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 (ibstart(inode) <= bindex && bindex <= ibend(inode))
++ h_inode = au_h_iptr_i(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);
++ TraceErrPtr(dentry);
++ //au_debug_off();
++ return dentry;
++}
++
++/* ---------------------------------------------------------------------- */
++
++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",
++ DLNPair(dentry), *max_len, connectable);
++ DEBUG_ON(is_anon(dentry));
++ inode = dentry->d_inode;
++ DEBUG_ON(!inode);
++ parent = dentry->d_parent;
++ DEBUG_ON(is_anon(parent));
++
++ err = -ENOSPC;
++ if (unlikely(*max_len <= Fh_tail)) {
++ Warn1("NFSv2 client (max_len %d)?\n", *max_len);
++ goto out;
++ }
++
++ sb = dentry->d_sb;
++ si_read_lock(sb);
++ di_read_lock_child(dentry, AUFS_I_RLOCK);
++ di_read_lock_parent(parent, AUFS_I_RLOCK);
++#ifdef CONFIG_AUFS_DEBUG
++ if (unlikely(!au_flag_test(sb, AuFlag_XINO)))
++ Warn1("NFS-exporting requires xino\n");
++#if 0
++ if (unlikely(au_flag_test(sb, AuFlag_UDBA_INOTIFY)))
++ Warn1("udba=inotify is not recommended when exporting\n");
++#endif
++#endif
++
++ err = -EPERM;
++ bindex = ibstart(inode);
++ h_sb = sbr_sb(sb, bindex);
++ if (unlikely(!h_sb->s_export_op)) {
++ Err1("%s branch is not exportable\n", au_sbtype(h_sb));
++ goto out_unlock;
++ }
++
++#if 0 //def CONFIG_AUFS_ROBR
++ if (unlikely(SB_AUFS(h_sb))) {
++ Err1("aufs branch is not supported\n");
++ goto out_unlock;
++ }
++#endif
++
++ /* doesn't support pseudo-link */
++ if (unlikely(bindex < dbstart(dentry)
++ || dbend(dentry) < bindex
++ || !au_h_dptr_i(dentry, bindex))) {
++ Err("%.*s/%.*s, b%d, pseudo-link?\n",
++ DLNPair(dentry->d_parent), DLNPair(dentry), bindex);
++ goto out_unlock;
++ }
++
++ fh[Fh_br_id_sigen] = encode_br_id_sigen(sbr_id(sb, bindex),
++ 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_iptr(inode);
++ encode_ino(fh + Fh_h_ino, h_inode->i_ino);
++ fh[Fh_h_igen] = h_inode->i_generation;
++
++ /* it should be set at exporting time */
++ if (unlikely(!h_sb->s_export_op->find_exported_dentry)) {
++ Warn("set default find_exported_dentry for %s\n",
++ au_sbtype(h_sb));
++ h_sb->s_export_op->find_exported_dentry = find_exported_dentry;
++ }
++
++ *max_len -= Fh_tail;
++ //LKTRTrace("Fh_tail %d, max_len %d\n", Fh_tail, *max_len);
++ h_parent = au_h_dptr_i(parent, bindex);
++ DEBUG_ON(is_anon(h_parent));
++ err = fh[Fh_h_type] = CALL(h_sb->s_export_op, encode_fh)
++ (h_parent, fh + Fh_tail, max_len, connectable);
++ *max_len += Fh_tail;
++ if (err != 255)
++ err = 2; //??
++ else
++ Warn1("%s encode_fh failed\n", au_sbtype(h_sb));
++
++ out_unlock:
++ di_read_unlock(parent, AUFS_I_RLOCK);
++ aufs_read_unlock(dentry, AUFS_I_RLOCK);
++ out:
++ TraceErr(err);
++ //au_debug_off();
++ if (unlikely(err < 0))
++ err = 255;
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++#if 0
++struct export_operations {
++ struct dentry *(*decode_fh)(struct super_block *sb, __u32 *fh, int fh_len, int fh_type,
++ int (*acceptable)(void *context, struct dentry *de),
++ void *context);
++ int (*encode_fh)(struct dentry *de, __u32 *fh, int *max_len,
++ int connectable);
++
++ /* the following are only called from the filesystem itself */
++ int (*get_name)(struct dentry *parent, char *name,
++ struct dentry *child);
++ struct dentry * (*get_parent)(struct dentry *child);
++ struct dentry * (*get_dentry)(struct super_block *sb, void *inump);
++
++ /* This is set by the exporting module to a standard helper */
++ struct dentry * (*find_exported_dentry)(
++ struct super_block *sb, void *obj, void *parent,
++ int (*acceptable)(void *context, struct dentry *de),
++ void *context);
++};
++#endif
++
++struct export_operations aufs_export_op = {
++ .decode_fh = aufs_decode_fh,
++ .encode_fh = aufs_encode_fh
++};
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/f_op.c linux-2.6.22.1/fs/aufs/f_op.c
+--- linux-2.6.22.1.oorig/fs/aufs/f_op.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/f_op.c 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,684 @@
++/*
++ * 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: f_op.c,v 1.27 2007/05/14 03:38:24 sfjro Exp $ */
++
++#include <linux/fsnotify.h>
++#include <linux/pagemap.h>
++#include <linux/poll.h>
++#include <linux/security.h>
++#include <linux/version.h>
++#include "aufs.h"
++
++/* common function to regular file and dir */
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
++#define FlushArgs hidden_file, id
++int aufs_flush(struct file *file, fl_owner_t id)
++#else
++#define FlushArgs hidden_file
++int aufs_flush(struct file *file)
++#endif
++{
++ int err;
++ struct dentry *dentry;
++ aufs_bindex_t bindex, bend;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++
++ // aufs_read_lock_file()
++ si_read_lock(dentry->d_sb);
++ fi_read_lock(file);
++ di_read_lock_child(dentry, !AUFS_I_RLOCK);
++
++ err = 0;
++ bend = fbend(file);
++ for (bindex = fbstart(file); !err && bindex <= bend; bindex++) {
++ struct file *hidden_file;
++ hidden_file = au_h_fptr_i(file, bindex);
++ if (hidden_file && hidden_file->f_op
++ && hidden_file->f_op->flush)
++ err = hidden_file->f_op->flush(FlushArgs);
++ }
++
++ di_read_unlock(dentry, !AUFS_I_RLOCK);
++ fi_read_unlock(file);
++ si_read_unlock(dentry->d_sb);
++ TraceErr(err);
++ return err;
++}
++#undef FlushArgs
++
++/* ---------------------------------------------------------------------- */
++
++static int do_open_nondir(struct file *file, int flags)
++{
++ int err;
++ aufs_bindex_t bindex;
++ struct super_block *sb;
++ struct file *hidden_file;
++ struct dentry *dentry;
++ struct inode *inode;
++ struct aufs_finfo *finfo;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s, flags 0%o\n", DLNPair(dentry), flags);
++ FiMustWriteLock(file);
++ inode = dentry->d_inode;
++ DEBUG_ON(!inode || S_ISDIR(inode->i_mode));
++
++ err = 0;
++ finfo = ftofi(file);
++ finfo->fi_h_vm_ops = NULL;
++ sb = dentry->d_sb;
++ bindex = dbstart(dentry);
++ DEBUG_ON(!au_h_dptr(dentry)->d_inode);
++ /* O_TRUNC is processed already */
++ BUG_ON(test_ro(sb, bindex, inode) && (flags & O_TRUNC));
++
++ hidden_file = hidden_open(dentry, bindex, flags);
++ //if (LktrCond) {fput(hidden_file); br_put(stobr(dentry->d_sb, bindex));
++ //hidden_file = ERR_PTR(-1);}
++ if (!IS_ERR(hidden_file)) {
++ set_fbstart(file, bindex);
++ set_fbend(file, bindex);
++ set_h_fptr(file, bindex, hidden_file);
++ return 0; /* success */
++ }
++ err = PTR_ERR(hidden_file);
++ TraceErr(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, DLNPair(file->f_dentry));
++
++ si_read_lock(sb);
++ au_fin_finfo(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 *hidden_file;
++ struct super_block *sb;
++ struct inode *h_inode;
++ int dlgt;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s, cnt %lu, pos %Ld\n",
++ DLNPair(dentry), (unsigned long)count, *ppos);
++
++ sb = dentry->d_sb;
++ si_read_lock(sb);
++ 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 */
++ dlgt = need_dlgt(sb);
++ hidden_file = au_h_fptr(file);
++ h_inode = hidden_file->f_dentry->d_inode;
++ if (!au_flag_test(sb, AuFlag_UDBA_INOTIFY))
++ err = vfsub_read_u(hidden_file, buf, count, ppos, dlgt);
++ else {
++ struct inode *dir = dentry->d_parent->d_inode,
++ *h_dir = hidden_file->f_dentry->d_parent->d_inode;
++ aufs_bindex_t bstart = fbstart(file);
++ hdir_lock(h_dir, dir, bstart);
++ err = vfsub_read_u(hidden_file, buf, count, ppos, dlgt);
++ hdir_unlock(h_dir, dir, bstart);
++ }
++ memcpy(&file->f_ra, &hidden_file->f_ra, sizeof(file->f_ra)); //??
++ dentry->d_inode->i_atime = hidden_file->f_dentry->d_inode->i_atime;
++
++ fi_read_unlock(file);
++ out:
++ si_read_unlock(sb);
++ TraceErr(err);
++ return err;
++}
++
++static ssize_t aufs_write(struct file *file, const char __user *__buf,
++ size_t count, loff_t *ppos)
++{
++ ssize_t err;
++ struct dentry *dentry;
++ struct inode *inode;
++ struct super_block *sb;
++ struct file *hidden_file;
++ char __user *buf = (char __user*)__buf;
++ struct inode *h_inode;
++ int dlgt;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s, cnt %lu, pos %Ld\n",
++ DLNPair(dentry), (unsigned long)count, *ppos);
++
++ inode = dentry->d_inode;
++ i_lock(inode);
++ sb = dentry->d_sb;
++ si_read_lock(sb);
++ 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 */
++ dlgt = need_dlgt(sb);
++ hidden_file = au_h_fptr(file);
++ h_inode = hidden_file->f_dentry->d_inode;
++ if (!au_flag_test(sb, AuFlag_UDBA_INOTIFY))
++ err = vfsub_write_u(hidden_file, buf, count, ppos, dlgt);
++ else {
++ struct inode *dir = dentry->d_parent->d_inode,
++ *h_dir = hidden_file->f_dentry->d_parent->d_inode;
++ aufs_bindex_t bstart = fbstart(file);
++ hdir_lock(h_dir, dir, bstart);
++ err = vfsub_write_u(hidden_file, buf, count, ppos, dlgt);
++ hdir_unlock(h_dir, dir, bstart);
++ }
++ 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);
++ i_unlock(inode);
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++#if 0 //def CONFIG_AUFS_ROBR
++struct lvma {
++ struct list_head list;
++ struct vm_area_struct *vma;
++};
++
++static struct file *safe_file(struct vm_area_struct *vma)
++{
++ struct file *file = vma->vm_file;
++ struct super_block *sb = file->f_dentry->d_sb;
++ struct lvma *lvma, *entry;
++ struct aufs_sbinfo *sbinfo;
++ int found, warn;
++
++ TraceEnter();
++ DEBUG_ON(!SB_AUFS(sb));
++
++ warn = 0;
++ found = 0;
++ sbinfo = stosi(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))
++ Warn1("no memory for lvma\n");
++ return file;
++}
++
++static void reset_file(struct vm_area_struct *vma, struct file *file)
++{
++ struct super_block *sb = file->f_dentry->d_sb;
++ struct lvma *entry, *found;
++ struct aufs_sbinfo *sbinfo;
++
++ TraceEnter();
++ DEBUG_ON(!SB_AUFS(sb));
++
++ vma->vm_file = file;
++
++ found = NULL;
++ sbinfo = stosi(sb);
++ spin_lock(&sbinfo->si_lvma_lock);
++ list_for_each_entry(entry, &sbinfo->si_lvma, list)
++ if (entry->vma == vma){
++ found = entry;
++ break;
++ }
++ DEBUG_ON(!found);
++ list_del(&found->list);
++ spin_unlock(&sbinfo->si_lvma_lock);
++ kfree(found);
++}
++
++#else
++
++static struct file *safe_file(struct vm_area_struct *vma)
++{
++ struct file *file;
++
++ file = vma->vm_file;
++ if (file->private_data && au_is_aufs(file->f_dentry->d_sb))
++ return file;
++ return NULL;
++}
++
++static void reset_file(struct vm_area_struct *vma, struct file *file)
++{
++ vma->vm_file = file;
++ smp_mb();
++}
++#endif /* CONFIG_AUFS_ROBR */
++
++static struct page *aufs_nopage(struct vm_area_struct *vma, unsigned long addr,
++ int *type)
++{
++ struct page *page;
++ struct dentry *dentry;
++ struct file *file, *hidden_file;
++ struct inode *inode;
++ static DECLARE_WAIT_QUEUE_HEAD(wq);
++ struct aufs_finfo *finfo;
++
++ TraceEnter();
++ DEBUG_ON(!vma || !vma->vm_file);
++ wait_event(wq, (file = safe_file(vma)));
++ DEBUG_ON(!au_is_aufs(file->f_dentry->d_sb));
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s, addr %lx\n", DLNPair(dentry), addr);
++ inode = dentry->d_inode;
++ DEBUG_ON(!S_ISREG(inode->i_mode));
++
++ // do not revalidate, nor lock
++ finfo = ftofi(file);
++ hidden_file = finfo->fi_hfile[0 + finfo->fi_bstart].hf_file;
++ DEBUG_ON(!hidden_file || !au_is_mmapped(file));
++ vma->vm_file = hidden_file;
++ //smp_mb();
++ page = finfo->fi_h_vm_ops->nopage(vma, addr, type);
++ reset_file(vma, file);
++#if 0 //def CONFIG_SMP
++ //wake_up_nr(&wq, online_cpu - 1);
++ wake_up_all(&wq);
++#else
++ wake_up(&wq);
++#endif
++ if (!IS_ERR(page)) {
++ //page->mapping = file->f_mapping;
++ //get_page(page);
++ //file->f_mapping = hidden_file->f_mapping;
++ //touch_atime(NULL, dentry);
++ //inode->i_atime = hidden_file->f_dentry->d_inode->i_atime;
++ }
++ TraceErrPtr(page);
++ return page;
++}
++
++static int aufs_populate(struct vm_area_struct *vma, unsigned long addr,
++ unsigned long len, pgprot_t prot, unsigned long pgoff,
++ int nonblock)
++{
++ Err("please report me this application\n");
++ BUG();
++ return ftofi(vma->vm_file)->fi_h_vm_ops->populate
++ (vma, addr, len, prot, pgoff, nonblock);
++}
++
++static struct vm_operations_struct aufs_vm_ops = {
++ //.open = aufs_vmaopen,
++ //.close = aufs_vmaclose,
++ .nopage = aufs_nopage,
++ .populate = aufs_populate,
++ //page_mkwrite(struct vm_area_struct *vma, struct page *page)
++};
++
++/* ---------------------------------------------------------------------- */
++
++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;
++ unsigned long flags;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s, %lx, len %lu\n",
++ DLNPair(dentry), vma->vm_start, vma->vm_end - vma->vm_start);
++ DEBUG_ON(!S_ISREG(dentry->d_inode->i_mode));
++ DEBUG_ON(down_write_trylock(&vma->vm_mm->mmap_sem));
++
++ mmapped = au_is_mmapped(file);
++ wlock = 0;
++ if (file->f_mode & FMODE_WRITE) {
++ flags = VM_SHARED | VM_WRITE;
++ wlock = ((flags & vma->vm_flags) == flags);
++ }
++
++ sb = dentry->d_sb;
++ si_read_lock(sb);
++ 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);
++ vm_ops = ftofi(file)->fi_h_vm_ops;
++ if (unlikely(!mmapped)) {
++ // nfs uses some locks
++ lockdep_off();
++ err = h_file->f_op->mmap(h_file, vma);
++ lockdep_on();
++ if (unlikely(err))
++ goto out_unlock;
++ vm_ops = vma->vm_ops;
++ DEBUG_ON(!vm_ops);
++ err = do_munmap(current->mm, vma->vm_start,
++ vma->vm_end - vma->vm_start);
++ if (unlikely(err)) {
++ IOErr("failed internal unmapping %.*s, %d\n",
++ DLNPair(h_file->f_dentry), err);
++ err = -EIO;
++ goto out_unlock;
++ }
++ }
++ DEBUG_ON(!vm_ops);
++
++ err = generic_file_mmap(file, vma);
++ if (!err) {
++ file_accessed(h_file);
++ dentry->d_inode->i_atime = h_file->f_dentry->d_inode->i_atime;
++ vma->vm_ops = &aufs_vm_ops;
++ if (unlikely(!mmapped))
++ ftofi(file)->fi_h_vm_ops = vm_ops;
++ }
++
++ out_unlock:
++ if (!wlock && mmapped)
++ fi_read_unlock(file);
++ else
++ fi_write_unlock(file);
++ out:
++ si_read_unlock(sb);
++ TraceErr(err);
++ return err;
++}
++
++// todo: try do_sendfile() in fs/read_write.c
++static ssize_t aufs_sendfile(struct file *file, loff_t *ppos,
++ size_t count, read_actor_t actor, void *target)
++{
++ ssize_t err;
++ struct file *h_file;
++ 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);
++ struct dentry *dentry;
++ struct super_block *sb;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s, pos %Ld, cnt %lu, loopback %d\n",
++ DLNPair(dentry), *ppos, (unsigned long)count, loopback);
++
++ sb = dentry->d_sb;
++ si_read_lock(sb);
++ err = au_reval_and_lock_finfo(file, au_reopen_nondir, /*wlock*/0,
++ /*locked*/0);
++ if (unlikely(err))
++ goto out;
++
++ err = -EINVAL;
++ h_file = au_h_fptr(file);
++ if (h_file->f_op && h_file->f_op->sendfile) {
++ if (/* unlikely */(loopback)) {
++ file->f_mapping = h_file->f_mapping;
++ smp_mb(); //??
++ }
++ // nfs uses some locks
++ lockdep_off();
++ err = h_file->f_op->sendfile
++ (h_file, ppos, count, actor, target);
++ lockdep_on();
++ dentry->d_inode->i_atime = h_file->f_dentry->d_inode->i_atime;
++ }
++ fi_read_unlock(file);
++
++ out:
++ si_read_unlock(sb);
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++/* copied from linux/fs/select.h, must match */
++#define DEFAULT_POLLMASK (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)
++
++static unsigned int aufs_poll(struct file *file, poll_table *wait)
++{
++ unsigned int mask;
++ struct file *hidden_file;
++ int err;
++ struct dentry *dentry;
++ struct super_block *sb;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s, wait %p\n", DLNPair(dentry), wait);
++ DEBUG_ON(S_ISDIR(dentry->d_inode->i_mode));
++
++ /* We should pretend an error happend. */
++ mask = POLLERR /* | POLLIN | POLLOUT */;
++ sb = dentry->d_sb;
++ si_read_lock(sb);
++ 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;
++ hidden_file = au_h_fptr(file);
++ if (hidden_file->f_op && hidden_file->f_op->poll)
++ mask = hidden_file->f_op->poll(hidden_file, wait);
++ fi_read_unlock(file);
++
++ out:
++ si_read_unlock(sb);
++ TraceErr((int)mask);
++ return mask;
++}
++
++static int aufs_fsync_nondir(struct file *file, struct dentry *dentry,
++ int datasync)
++{
++ int err, my_lock;
++ struct inode *inode;
++ struct file *hidden_file;
++ struct super_block *sb;
++
++ LKTRTrace("%.*s, %d\n", DLNPair(dentry), datasync);
++ inode = dentry->d_inode;
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
++ IMustLock(inode);
++ my_lock = 0;
++#else
++ /* before 2.6.17,
++ * msync(2) calls me without locking i_sem/i_mutex, but fsync(2).
++ */
++ my_lock = !i_trylock(inode);
++#endif
++
++ sb = dentry->d_sb;
++ si_read_lock(sb);
++ 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;
++ hidden_file = au_h_fptr(file);
++ if (hidden_file->f_op && hidden_file->f_op->fsync) {
++ // todo: apparmor thread?
++ //file->f_mapping->host->i_mutex
++ ii_write_lock_child(inode);
++ hi_lock_child(hidden_file->f_dentry->d_inode);
++ err = hidden_file->f_op->fsync
++ (hidden_file, hidden_file->f_dentry, datasync);
++ //err = -1;
++ au_cpup_attr_timesizes(inode);
++ i_unlock(hidden_file->f_dentry->d_inode);
++ ii_write_unlock(inode);
++ }
++
++ out_unlock:
++ fi_write_unlock(file);
++ out:
++ if (unlikely(my_lock))
++ i_unlock(inode);
++ si_read_unlock(sb);
++ TraceErr(err);
++ return err;
++}
++
++static int aufs_fasync(int fd, struct file *file, int flag)
++{
++ int err;
++ struct file *hidden_file;
++ struct dentry *dentry;
++ struct super_block *sb;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s, %d\n", DLNPair(dentry), flag);
++
++ sb = dentry->d_sb;
++ si_read_lock(sb);
++ err = au_reval_and_lock_finfo(file, au_reopen_nondir, /*wlock*/0,
++ /*locked*/0);
++ //err = -1;
++ if (unlikely(err))
++ goto out;
++
++ hidden_file = au_h_fptr(file);
++ if (hidden_file->f_op && hidden_file->f_op->fasync)
++ err = hidden_file->f_op->fasync(fd, hidden_file, flag);
++ fi_read_unlock(file);
++
++ out:
++ si_read_unlock(sb);
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++#if 0 // comment
++struct file_operations {
++ struct module *owner;
++ loff_t (*llseek) (struct file *, loff_t, int);
++ ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
++ ssize_t (*aio_read) (struct kiocb *, char __user *, size_t, loff_t);
++ ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
++ ssize_t (*aio_write) (struct kiocb *, const char __user *, size_t, loff_t);
++ int (*readdir) (struct file *, void *, filldir_t);
++ unsigned int (*poll) (struct file *, struct poll_table_struct *);
++ int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
++ long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
++ long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
++ int (*mmap) (struct file *, struct vm_area_struct *);
++ int (*open) (struct inode *, struct file *);
++ int (*flush) (struct file *);
++ int (*release) (struct inode *, struct file *);
++ int (*fsync) (struct file *, struct dentry *, int datasync);
++ int (*aio_fsync) (struct kiocb *, int datasync);
++ int (*fasync) (int, struct file *, int);
++ int (*lock) (struct file *, int, struct file_lock *);
++ ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
++ ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
++ ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *);
++ ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
++ unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
++ int (*check_flags)(int);
++ int (*dir_notify)(struct file *file, unsigned long arg);
++ int (*flock) (struct file *, int, struct file_lock *);
++};
++#endif
++
++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,
++ .sendfile = aufs_sendfile,
++};
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/file.c linux-2.6.22.1/fs/aufs/file.c
+--- linux-2.6.22.1.oorig/fs/aufs/file.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/file.c 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,832 @@
++/*
++ * 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: file.c,v 1.42 2007/05/14 03:39:09 sfjro Exp $ */
++
++//#include <linux/fsnotify.h>
++#include <linux/pagemap.h>
++//#include <linux/poll.h>
++//#include <linux/security.h>
++#include "aufs.h"
++
++/* 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 *hidden_open(struct dentry *dentry, aufs_bindex_t bindex, int flags)
++{
++ struct dentry *hidden_dentry;
++ struct inode *hidden_inode;
++ struct super_block *sb;
++ struct vfsmount *hidden_mnt;
++ struct file *hidden_file;
++ struct aufs_branch *br;
++ loff_t old_size;
++ int udba;
++
++ LKTRTrace("%.*s, b%d, flags 0%o\n", DLNPair(dentry), bindex, flags);
++ DEBUG_ON(!dentry);
++ hidden_dentry = au_h_dptr_i(dentry, bindex);
++ DEBUG_ON(!hidden_dentry);
++ hidden_inode = hidden_dentry->d_inode;
++ DEBUG_ON(!hidden_inode);
++
++ sb = dentry->d_sb;
++ udba = au_flag_test(sb, AuFlag_UDBA_INOTIFY);
++ if (unlikely(udba)) {
++ // test here?
++ }
++
++ br = stobr(sb, bindex);
++ br_get(br);
++ /* drop flags for writing */
++ if (test_ro(sb, bindex, dentry->d_inode))
++ flags = au_file_roflags(flags);
++ flags &= ~O_CREAT;
++ spin_lock(&hidden_inode->i_lock);
++ old_size = i_size_read(hidden_inode);
++ spin_unlock(&hidden_inode->i_lock);
++
++ //DbgSleep(3);
++
++ dget(hidden_dentry);
++ hidden_mnt = mntget(br->br_mnt);
++ hidden_file = dentry_open(hidden_dentry, hidden_mnt, flags);
++ //if (LktrCond) {fput(hidden_file); hidden_file = ERR_PTR(-1);}
++
++ if (!IS_ERR(hidden_file)) {
++#if 0 // remove this
++ if (/* old_size && */ (flags & O_TRUNC)) {
++ au_direval_dec(dentry);
++ if (!IS_ROOT(dentry))
++ au_direval_dec(dentry->d_parent);
++ }
++#endif
++ return hidden_file;
++ }
++
++ br_put(br);
++ TraceErrPtr(hidden_file);
++ return hidden_file;
++}
++
++static int do_coo(struct dentry *dentry, aufs_bindex_t bstart)
++{
++ int err;
++ struct dentry *parent, *h_parent, *h_dentry;
++ aufs_bindex_t bcpup;
++ struct inode *h_dir, *h_inode, *dir;
++
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++ DEBUG_ON(IS_ROOT(dentry));
++ DiMustWriteLock(dentry);
++
++ parent = dentry->d_parent; // dget_parent()
++ di_write_lock_parent(parent);
++ bcpup = err = find_rw_parent_br(dentry, bstart);
++ //bcpup = err = find_rw_br(sb, bstart);
++ if (unlikely(err < 0)) {
++ err = 0; // stop copyup, it is not an error
++ goto out;
++ }
++ err = 0;
++
++ h_parent = au_h_dptr_i(parent, bcpup);
++ if (!h_parent) {
++ err = cpup_dirs(dentry, bcpup, NULL);
++ if (unlikely(err))
++ goto out;
++ h_parent = au_h_dptr_i(parent, bcpup);
++ }
++
++ h_dir = h_parent->d_inode;
++ h_dentry = au_h_dptr_i(dentry, bstart);
++ h_inode = h_dentry->d_inode;
++ dir = parent->d_inode;
++ hdir_lock(h_dir, dir, bcpup);
++ hi_lock_child(h_inode);
++ DEBUG_ON(au_h_dptr_i(dentry, bcpup));
++ err = sio_cpup_simple(dentry, bcpup, -1,
++ au_flags_cpup(CPUP_DTIME, parent));
++ TraceErr(err);
++ i_unlock(h_inode);
++ hdir_unlock(h_dir, dir, bcpup);
++
++ out:
++ di_write_unlock(parent);
++ TraceErr(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, DLNPair(dentry));
++
++ sb = dentry->d_sb;
++ si_read_lock(sb);
++ coo = 0;
++#if 0
++ switch (au_flag_test_coo(sb)) {
++ case AuFlag_COO_LEAF:
++ coo = !S_ISDIR(inode->i_mode);
++ break;
++ case AuFlag_COO_ALL:
++ coo = 1;
++ break;
++ }
++#endif
++ err = au_init_finfo(file);
++ //if (LktrCond) {fi_write_unlock(file); fin_finfo(file); err = -1;}
++ if (unlikely(err))
++ goto out;
++
++ if (!coo) {
++ di_read_lock_child(dentry, AUFS_I_RLOCK);
++ bstart = dbstart(dentry);
++ } else {
++ di_write_lock_child(dentry);
++ bstart = dbstart(dentry);
++ if (test_ro(sb, bstart, dentry->d_inode)) {
++ err = do_coo(dentry, bstart);
++ if (err) {
++ di_write_unlock(dentry);
++ goto out_finfo;
++ }
++ bstart = dbstart(dentry);
++ }
++ di_downgrade_lock(dentry, AUFS_I_RLOCK);
++ }
++
++ // todo: remove this extra locks
++ dir = dentry->d_parent->d_inode;
++ if (!IS_ROOT(dentry))
++ ii_read_lock_parent(dir);
++ h_dir = au_h_iptr_i(dir, bstart);
++ hdir_lock(h_dir, dir, bstart);
++ err = open(file, file->f_flags);
++ //if (LktrCond) err = -1;
++ hdir_unlock(h_dir, dir, bstart);
++ if (!IS_ROOT(dentry))
++ ii_read_unlock(dir);
++ di_read_unlock(dentry, AUFS_I_RLOCK);
++
++ out_finfo:
++ fi_write_unlock(file);
++ if (unlikely(err))
++ au_fin_finfo(file);
++ //DbgFile(file);
++ out:
++ si_read_unlock(sb);
++ TraceErr(err);
++ return err;
++}
++
++int au_reopen_nondir(struct file *file)
++{
++ int err;
++ struct dentry *dentry;
++ aufs_bindex_t bstart, bindex, bend;
++ struct file *hidden_file, *h_file_tmp;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++ DEBUG_ON(S_ISDIR(dentry->d_inode->i_mode)
++ || !au_h_dptr(dentry)->d_inode);
++ bstart = dbstart(dentry);
++
++ h_file_tmp = NULL;
++ if (fbstart(file) == bstart) {
++ hidden_file = au_h_fptr(file);
++ if (file->f_mode == hidden_file->f_mode)
++ return 0; /* success */
++ h_file_tmp = hidden_file;
++ get_file(h_file_tmp);
++ set_h_fptr(file, bstart, NULL);
++ }
++ DEBUG_ON(fbstart(file) < bstart
++ || ftofi(file)->fi_hfile[0 + bstart].hf_file);
++
++ hidden_file = hidden_open(dentry, bstart, file->f_flags & ~O_TRUNC);
++ //if (LktrCond) {fput(hidden_file); br_put(stobr(dentry->d_sb, bstart));
++ //hidden_file = ERR_PTR(-1);}
++ err = PTR_ERR(hidden_file);
++ if (IS_ERR(hidden_file))
++ goto out; // close all?
++ err = 0;
++ //cpup_file_flags(hidden_file, file);
++ set_fbstart(file, bstart);
++ set_h_fptr(file, bstart, hidden_file);
++ memcpy(&hidden_file->f_ra, &file->f_ra, sizeof(file->f_ra)); //??
++
++ /* close lower files */
++ bend = fbend(file);
++ for (bindex = bstart + 1; bindex <= bend; bindex++)
++ set_h_fptr(file, bindex, NULL);
++ set_fbend(file, bstart);
++
++ out:
++ if (h_file_tmp)
++ fput(h_file_tmp);
++ TraceErr(err);
++ return err;
++}
++
++/*
++ * copyup the deleted file for writing.
++ */
++static int cpup_wh_file(struct file *file, aufs_bindex_t bdst, loff_t len)
++{
++ int err;
++ struct dentry *dentry, *parent, *hidden_parent, *tmp_dentry;
++ struct dentry *hidden_dentry_bstart, *hidden_dentry_bdst;
++ struct inode *hidden_dir;
++ aufs_bindex_t bstart;
++ struct aufs_dinfo *dinfo;
++ struct dtime dt;
++ struct lkup_args lkup;
++ struct super_block *sb;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s, bdst %d, len %Lu\n", DLNPair(dentry), bdst, len);
++ DEBUG_ON(S_ISDIR(dentry->d_inode->i_mode)
++ || !(file->f_mode & FMODE_WRITE));
++ DiMustWriteLock(dentry);
++ parent = dentry->d_parent;
++ IiMustAnyLock(parent->d_inode);
++ hidden_parent = au_h_dptr_i(parent, bdst);
++ DEBUG_ON(!hidden_parent);
++ hidden_dir = hidden_parent->d_inode;
++ DEBUG_ON(!hidden_dir);
++ IMustLock(hidden_dir);
++
++ sb = parent->d_sb;
++ lkup.nfsmnt = au_nfsmnt(sb, bdst);
++ lkup.dlgt = need_dlgt(sb);
++ tmp_dentry = lkup_whtmp(hidden_parent, &dentry->d_name, &lkup);
++ //if (LktrCond) {dput(tmp_dentry); tmp_dentry = ERR_PTR(-1);}
++ err = PTR_ERR(tmp_dentry);
++ if (IS_ERR(tmp_dentry))
++ goto out;
++
++ dtime_store(&dt, parent, hidden_parent);
++ dinfo = dtodi(dentry);
++ bstart = dinfo->di_bstart;
++ hidden_dentry_bdst = dinfo->di_hdentry[0 + bdst].hd_dentry;
++ hidden_dentry_bstart = dinfo->di_hdentry[0 + bstart].hd_dentry;
++ dinfo->di_bstart = bdst;
++ dinfo->di_hdentry[0 + bdst].hd_dentry = tmp_dentry;
++ dinfo->di_hdentry[0 + bstart].hd_dentry = au_h_fptr(file)->f_dentry;
++ err = cpup_single(dentry, bdst, bstart, len,
++ au_flags_cpup(!CPUP_DTIME, parent));
++ //if (LktrCond) err = -1;
++ if (!err)
++ err = au_reopen_nondir(file);
++ //err = -1;
++ dinfo->di_hdentry[0 + bstart].hd_dentry = hidden_dentry_bstart;
++ dinfo->di_hdentry[0 + bdst].hd_dentry = hidden_dentry_bdst;
++ dinfo->di_bstart = bstart;
++ if (unlikely(err))
++ goto out_tmp;
++
++ DEBUG_ON(!d_unhashed(dentry));
++ err = vfsub_unlink(hidden_dir, tmp_dentry, lkup.dlgt);
++ //if (LktrCond) err = -1;
++ if (unlikely(err)) {
++ IOErr("failed remove copied-up tmp file %.*s(%d)\n",
++ DLNPair(tmp_dentry), err);
++ err = -EIO;
++ }
++ dtime_revert(&dt, !CPUP_LOCKED_GHDIR);
++
++ out_tmp:
++ dput(tmp_dentry);
++ out:
++ TraceErr(err);
++ return err;
++}
++
++struct cpup_wh_file_args {
++ int *errp;
++ struct file *file;
++ aufs_bindex_t bdst;
++ loff_t len;
++};
++
++static void call_cpup_wh_file(void *args)
++{
++ struct cpup_wh_file_args *a = args;
++ *a->errp = cpup_wh_file(a->file, a->bdst, a->len);
++}
++
++/*
++ * prepare the @file for writing.
++ */
++int au_ready_to_write(struct file *file, loff_t len)
++{
++ int err;
++ struct dentry *dentry, *parent, *hidden_dentry, *hidden_parent;
++ struct inode *hidden_inode, *hidden_dir, *inode, *dir;
++ struct super_block *sb;
++ aufs_bindex_t bstart, bcpup;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s, len %Ld\n", DLNPair(dentry), len);
++ FiMustWriteLock(file);
++
++ sb = dentry->d_sb;
++ bstart = fbstart(file);
++ DEBUG_ON(ftobr(file, bstart) != stobr(sb, bstart));
++
++ inode = dentry->d_inode;
++ ii_read_lock_child(inode);
++ LKTRTrace("rdonly %d, bstart %d\n", test_ro(sb, bstart, inode), bstart);
++ err = test_ro(sb, bstart, inode);
++ ii_read_unlock(inode);
++ if (!err && (au_h_fptr(file)->f_mode & FMODE_WRITE))
++ return 0;
++
++ /* need to cpup */
++ parent = dentry->d_parent; // dget_parent()
++ di_write_lock_child(dentry);
++ di_write_lock_parent(parent);
++ bcpup = err = find_rw_parent_br(dentry, bstart);
++ //bcpup = err = find_rw_br(sb, bstart);
++ if (unlikely(err < 0))
++ goto out_unlock;
++ err = 0;
++
++ hidden_parent = au_h_dptr_i(parent, bcpup);
++ if (!hidden_parent) {
++ err = cpup_dirs(dentry, bcpup, NULL);
++ //if (LktrCond) err = -1;
++ if (unlikely(err))
++ goto out_unlock;
++ hidden_parent = au_h_dptr_i(parent, bcpup);
++ }
++
++ hidden_dir = hidden_parent->d_inode;
++ hidden_dentry = au_h_fptr(file)->f_dentry;
++ hidden_inode = hidden_dentry->d_inode;
++ dir = parent->d_inode;
++ hdir_lock(hidden_dir, dir, bcpup);
++ hi_lock_child(hidden_inode);
++ if (d_unhashed(dentry) || d_unhashed(hidden_dentry)
++ /* || !hidden_inode->i_nlink */) {
++ if (!au_test_perm(hidden_dir, MAY_EXEC | MAY_WRITE,
++ need_dlgt(sb)))
++ err = cpup_wh_file(file, bcpup, len);
++ else {
++ struct cpup_wh_file_args args = {
++ .errp = &err,
++ .file = file,
++ .bdst = bcpup,
++ .len = len
++ };
++ au_wkq_wait(call_cpup_wh_file, &args, /*dlgt*/0);
++ }
++ //if (LktrCond) err = -1;
++ TraceErr(err);
++ } else {
++ if (!au_h_dptr_i(dentry, bcpup))
++ err = sio_cpup_simple(dentry, bcpup, len,
++ au_flags_cpup(CPUP_DTIME,
++ parent));
++ //if (LktrCond) err = -1;
++ TraceErr(err);
++ if (!err)
++ err = au_reopen_nondir(file);
++ //if (LktrCond) err = -1;
++ TraceErr(err);
++ }
++ i_unlock(hidden_inode);
++ hdir_unlock(hidden_dir, dir, bcpup);
++
++ out_unlock:
++ di_write_unlock(parent);
++ di_write_unlock(dentry);
++// out:
++ TraceErr(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;
++ struct dentry *dentry;
++ aufs_bindex_t bend, bindex, bstart, brid;
++ struct aufs_hfile *p;
++ struct aufs_finfo *finfo;
++ struct super_block *sb;
++ struct inode *inode;
++ struct file *hidden_file;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++ FiMustWriteLock(file);
++ DiMustReadLock(dentry);
++ inode = dentry->d_inode;
++ IiMustReadLock(inode);
++ //au_debug_on();
++ //DbgDentry(dentry);
++ //DbgFile(file);
++ //au_debug_off();
++
++ err = -ENOMEM;
++ sb = dentry->d_sb;
++ finfo = ftofi(file);
++ bstart = finfo->fi_bstart;
++ bend = finfo->fi_bstart;
++ new_sz = sizeof(*finfo->fi_hfile) * (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 aufs_hfile tmp, *q;
++ aufs_bindex_t new_bindex;
++
++ if (!p->hf_file)
++ continue;
++ new_bindex = find_bindex(sb, p->hf_br);
++ if (new_bindex == bindex)
++ continue;
++ if (new_bindex < 0) { // test here
++ 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 = sbend(sb);
++ //au_debug_on();
++ //DbgFile(file);
++ //au_debug_off();
++ finfo->fi_bstart = s;
++ finfo->fi_bend = e;
++ }
++
++ p = finfo->fi_hfile;
++ if (!au_is_mmapped(file) && !d_unhashed(dentry)) {
++ bend = 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 = find_brindex(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 = 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);
++ }
++ //Dbg("%d, %d\n", finfo->fi_bstart, finfo->fi_bend);
++ DEBUG_ON(finfo->fi_bend < finfo->fi_bstart);
++ //DbgFile(file);
++ //DbgDentry(file->f_dentry);
++
++ err = 0;
++#if 0 // todo:
++ if (!au_h_dptr(dentry)->d_inode) {
++ au_update_figen(file);
++ goto out; /* success */
++ }
++#endif
++
++ if (unlikely(au_is_mmapped(file) || d_unhashed(dentry)))
++ goto out_update; /* success */
++
++ again:
++ bstart = ibstart(inode);
++ if (bstart < finfo->fi_bstart
++ && au_flag_test(sb, AuFlag_PLINK)
++ && au_is_plinked(sb, inode)) {
++ struct dentry *parent = dentry->d_parent; // dget_parent()
++ struct inode *dir = parent->d_inode, *h_dir;
++
++ if (test_ro(sb, bstart, inode)) {
++ di_read_lock_parent(parent, !AUFS_I_RLOCK);
++ bstart = err = find_rw_parent_br(dentry, bstart);
++ //bstart = err = find_rw_br(sb, bstart);
++ di_read_unlock(parent, !AUFS_I_RLOCK);
++ //todo: err = -1;
++ if (unlikely(err < 0))
++ goto out;
++ }
++ di_read_unlock(dentry, AUFS_I_RLOCK);
++ di_write_lock_child(dentry);
++ if (bstart != ibstart(inode)) { // todo
++ /* someone changed our inode while we were sleeping */
++ di_downgrade_lock(dentry, AUFS_I_RLOCK);
++ goto again;
++ }
++
++ di_read_lock_parent(parent, AUFS_I_RLOCK);
++ err = test_and_cpup_dirs(dentry, bstart, NULL);
++
++ // always superio.
++#if 1
++ h_dir = au_h_dptr_i(parent, bstart)->d_inode;
++ hdir_lock(h_dir, dir, bstart);
++ err = sio_cpup_simple(dentry, bstart, -1,
++ au_flags_cpup(CPUP_DTIME, parent));
++ hdir_unlock(h_dir, dir, bstart);
++ di_read_unlock(parent, AUFS_I_RLOCK);
++#else
++ if (!is_au_wkq(current)) {
++ struct cpup_pseudo_link_args args = {
++ .errp = &err,
++ .dentry = dentry,
++ .bdst = bstart,
++ .do_lock = 1
++ };
++ au_wkq_wait(call_cpup_pseudo_link, &args);
++ } else
++ err = cpup_pseudo_link(dentry, bstart, /*do_lock*/1);
++#endif
++ di_downgrade_lock(dentry, AUFS_I_RLOCK);
++ if (unlikely(err))
++ goto out;
++ }
++
++ err = reopen(file);
++ //err = -1;
++ out_update:
++ if (!err) {
++ au_update_figen(file);
++ //DbgFile(file);
++ return 0; /* success */
++ }
++
++ /* error, close all hidden files */
++ bend = fbend(file);
++ for (bindex = fbstart(file); bindex <= bend; bindex++)
++ set_h_fptr(file, bindex, NULL);
++
++ out:
++ TraceErr(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, sgen, fgen, pseudo_link;
++ struct dentry *dentry;
++ struct super_block *sb;
++ aufs_bindex_t bstart;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s, w %d, l %d\n", DLNPair(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, AUFS_I_RLOCK);
++ bstart = dbstart(dentry);
++ pseudo_link = (bstart != ibstart(dentry->d_inode));
++ di_read_unlock(dentry, AUFS_I_RLOCK);
++ if (sgen == fgen && !pseudo_link && fbstart(file) == bstart) {
++ if (!wlock)
++ fi_downgrade_lock(file);
++ return 0; /* success */
++ }
++
++ LKTRTrace("sgen %d, fgen %d\n", sgen, fgen);
++ if (sgen != au_digen(dentry)) {
++ /*
++ * 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;
++ DEBUG_ON(au_digen(dentry) != sgen);
++ }
++
++ di_read_lock_child(dentry, AUFS_I_RLOCK);
++ err = refresh_file(file, reopen);
++ //if (LktrCond) err = -1;
++ di_read_unlock(dentry, AUFS_I_RLOCK);
++ if (!err) {
++ if (!wlock)
++ fi_downgrade_lock(file);
++ } else
++ fi_write_unlock(file);
++
++ out:
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++// cf. aufs_nopage()
++// for madvise(2)
++static int aufs_readpage(struct file *file, struct page *page)
++{
++ TraceEnter();
++ 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)
++{BUG();return 0;}
++static int aufs_commit_write(struct file *file, struct page *page,
++ unsigned from, unsigned to)
++{BUG();return 0;}
++static int aufs_writepage(struct page *page, struct writeback_control *wbc)
++{BUG();return 0;}
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
++static void aufs_sync_page(struct page *page)
++{BUG();}
++#else
++static int aufs_sync_page(struct page *page)
++{BUG(); return 0;}
++#endif
++
++#if 0 // comment
++static int aufs_writepages(struct address_space *mapping,
++ struct writeback_control *wbc)
++{BUG();return 0;}
++static int aufs_readpages(struct file *filp, struct address_space *mapping,
++ struct list_head *pages, unsigned nr_pages)
++{BUG();return 0;}
++static sector_t aufs_bmap(struct address_space *mapping, sector_t block)
++{BUG();return 0;}
++#endif
++
++static int aufs_set_page_dirty(struct page *page)
++{BUG();return 0;}
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
++static void aufs_invalidatepage (struct page *page, unsigned long offset)
++{BUG();}
++#else
++static int aufs_invalidatepage (struct page *page, unsigned long offset)
++{BUG(); return 0;}
++#endif
++static int aufs_releasepage (struct page *page, gfp_t gfp)
++{BUG();return 0;}
++static ssize_t aufs_direct_IO(int rw, struct kiocb *iocb,
++ const struct iovec *iov, loff_t offset,
++ unsigned long nr_segs)
++{BUG();return 0;}
++static struct page* aufs_get_xip_page(struct address_space *mapping,
++ sector_t offset, int create)
++{BUG();return NULL;}
++//static int aufs_migratepage (struct page *newpage, struct page *page)
++//{BUG();return 0;}
++#endif
++
++#if 0 // comment
++struct address_space {
++ struct inode *host; /* owner: inode, block_device */
++ struct radix_tree_root page_tree; /* radix tree of all pages */
++ rwlock_t tree_lock; /* and rwlock protecting it */
++ unsigned int i_mmap_writable;/* count VM_SHARED mappings */
++ struct prio_tree_root i_mmap; /* tree of private and shared mappings */
++ struct list_head i_mmap_nonlinear;/*list VM_NONLINEAR mappings */
++ spinlock_t i_mmap_lock; /* protect tree, count, list */
++ unsigned int truncate_count; /* Cover race condition with truncate */
++ unsigned long nrpages; /* number of total pages */
++ pgoff_t writeback_index;/* writeback starts here */
++ struct address_space_operations *a_ops; /* methods */
++ unsigned long flags; /* error bits/gfp mask */
++ struct backing_dev_info *backing_dev_info; /* device readahead, etc */
++ spinlock_t private_lock; /* for use by the address_space */
++ struct list_head private_list; /* ditto */
++ struct address_space *assoc_mapping; /* ditto */
++} __attribute__((aligned(sizeof(long))));
++
++struct address_space_operations {
++ int (*writepage)(struct page *page, struct writeback_control *wbc);
++ int (*readpage)(struct file *, struct page *);
++ void (*sync_page)(struct page *);
++
++ /* Write back some dirty pages from this mapping. */
++ int (*writepages)(struct address_space *, struct writeback_control *);
++
++ /* Set a page dirty. Return true if this dirtied it */
++ int (*set_page_dirty)(struct page *page);
++
++ int (*readpages)(struct file *filp, struct address_space *mapping,
++ struct list_head *pages, unsigned nr_pages);
++
++ /*
++ * ext3 requires that a successful prepare_write() call be followed
++ * by a commit_write() call - they must be balanced
++ */
++ int (*prepare_write)(struct file *, struct page *, unsigned, unsigned);
++ int (*commit_write)(struct file *, struct page *, unsigned, unsigned);
++ /* Unfortunately this kludge is needed for FIBMAP. Don't use it */
++ sector_t (*bmap)(struct address_space *, sector_t);
++ void (*invalidatepage) (struct page *, unsigned long);
++ int (*releasepage) (struct page *, gfp_t);
++ ssize_t (*direct_IO)(int, struct kiocb *, const struct iovec *iov,
++ loff_t offset, unsigned long nr_segs);
++ struct page* (*get_xip_page)(struct address_space *, sector_t,
++ int);
++ /* migrate the contents of a page to the specified target */
++ int (*migratepage) (struct page *, struct page *);
++};
++#endif
++
++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,
++ //.bmap = aufs_bmap,
++ .invalidatepage = aufs_invalidatepage,
++ .releasepage = aufs_releasepage,
++ .direct_IO = aufs_direct_IO,
++ .get_xip_page = aufs_get_xip_page,
++ //.migratepage = aufs_migratepage
++#endif
++};
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/file.h linux-2.6.22.1/fs/aufs/file.h
+--- linux-2.6.22.1.oorig/fs/aufs/file.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/file.h 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,140 @@
++/*
++ * 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: file.h,v 1.25 2007/05/14 03:41:52 sfjro Exp $ */
++
++#ifndef __AUFS_FILE_H__
++#define __AUFS_FILE_H__
++
++#ifdef __KERNEL__
++
++#include <linux/file.h>
++#include <linux/fs.h>
++#include <linux/version.h>
++#include <linux/aufs_type.h>
++#include "misc.h"
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
++// SEEK_xxx are defined in linux/fs.h
++#else
++enum {SEEK_SET, SEEK_CUR, SEEK_END};
++#endif
++
++/* ---------------------------------------------------------------------- */
++
++struct aufs_branch;
++struct aufs_hfile {
++ struct file *hf_file;
++ struct aufs_branch *hf_br;
++};
++
++struct aufs_vdir;
++struct aufs_finfo {
++ atomic_t fi_generation;
++
++ struct aufs_rwsem fi_rwsem;
++ struct aufs_hfile *fi_hfile;
++ aufs_bindex_t fi_bstart, fi_bend;
++
++ union {
++ struct vm_operations_struct *fi_h_vm_ops;
++ struct aufs_vdir *fi_vdir_cache;
++ };
++};
++
++/* ---------------------------------------------------------------------- */
++
++/* file.c */
++extern struct address_space_operations aufs_aop;
++unsigned int au_file_roflags(unsigned int flags);
++struct file *hidden_open(struct dentry *dentry, aufs_bindex_t bindex,
++ int flags);
++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;
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
++int aufs_flush(struct file *file, fl_owner_t id);
++#else
++int aufs_flush(struct file *file);
++#endif
++
++/* finfo.c */
++struct aufs_finfo *ftofi(struct file *file);
++aufs_bindex_t fbstart(struct file *file);
++aufs_bindex_t fbend(struct file *file);
++struct aufs_vdir *fvdir_cache(struct file *file);
++struct aufs_branch *ftobr(struct file *file, aufs_bindex_t bindex);
++struct file *au_h_fptr_i(struct file *file, aufs_bindex_t bindex);
++struct file *au_h_fptr(struct file *file);
++
++void set_fbstart(struct file *file, aufs_bindex_t bindex);
++void set_fbend(struct file *file, aufs_bindex_t bindex);
++void set_fvdir_cache(struct file *file, struct aufs_vdir *vdir_cache);
++void au_hfput(struct aufs_hfile *hf);
++void set_h_fptr(struct file *file, aufs_bindex_t bindex, struct file *h_file);
++void au_update_figen(struct file *file);
++
++void au_fin_finfo(struct file *file);
++int au_init_finfo(struct file *file);
++
++/* ---------------------------------------------------------------------- */
++
++static inline int au_figen(struct file *f)
++{
++ return atomic_read(&ftofi(f)->fi_generation);
++}
++
++static inline int au_is_mmapped(struct file *f)
++{
++ return !!(ftofi(f)->fi_h_vm_ops);
++}
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * fi_read_lock, fi_write_lock,
++ * fi_read_unlock, fi_write_unlock, fi_downgrade_lock
++ */
++SimpleRwsemFuncs(fi, struct file *f, ftofi(f)->fi_rwsem);
++
++/* to debug easier, do not make them inlined functions */
++#define FiMustReadLock(f) do {\
++ SiMustAnyLock((f)->f_dentry->d_sb); \
++ RwMustReadLock(&ftofi(f)->fi_rwsem); \
++} while (0)
++
++#define FiMustWriteLock(f) do { \
++ SiMustAnyLock((f)->f_dentry->d_sb); \
++ RwMustWriteLock(&ftofi(f)->fi_rwsem); \
++} while (0)
++
++#define FiMustAnyLock(f) do { \
++ SiMustAnyLock((f)->f_dentry->d_sb); \
++ RwMustAnyLock(&ftofi(f)->fi_rwsem); \
++} while (0)
++
++#define FiMustNoWaiters(f) RwMustNoWaiters(&ftofi(f)->fi_rwsem)
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_FILE_H__ */
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/finfo.c linux-2.6.22.1/fs/aufs/finfo.c
+--- linux-2.6.22.1.oorig/fs/aufs/finfo.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/finfo.c 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,211 @@
++/*
++ * 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: finfo.c,v 1.23 2007/04/30 05:45:21 sfjro Exp $ */
++
++#include "aufs.h"
++
++struct aufs_finfo *ftofi(struct file *file)
++{
++ struct aufs_finfo *finfo = file->private_data;
++ DEBUG_ON(!finfo
++ || !finfo->fi_hfile
++ || (0 < finfo->fi_bend
++ && (/* stosi(file->f_dentry->d_sb)->si_bend
++ < finfo->fi_bend
++ || */ finfo->fi_bend < finfo->fi_bstart)));
++ return finfo;
++}
++
++// hard/soft set
++aufs_bindex_t fbstart(struct file *file)
++{
++ FiMustAnyLock(file);
++ return ftofi(file)->fi_bstart;
++}
++
++aufs_bindex_t fbend(struct file *file)
++{
++ FiMustAnyLock(file);
++ return ftofi(file)->fi_bend;
++}
++
++struct aufs_vdir *fvdir_cache(struct file *file)
++{
++ FiMustAnyLock(file);
++ return ftofi(file)->fi_vdir_cache;
++}
++
++struct aufs_branch *ftobr(struct file *file, aufs_bindex_t bindex)
++{
++ struct aufs_finfo *finfo = ftofi(file);
++ struct aufs_hfile *hf;
++
++ FiMustAnyLock(file);
++ DEBUG_ON(!finfo
++ || finfo->fi_bstart < 0
++ || bindex < finfo->fi_bstart
++ || finfo->fi_bend < bindex);
++ hf = finfo->fi_hfile + bindex;
++ DEBUG_ON(hf->hf_br && br_count(hf->hf_br) <= 0);
++ return hf->hf_br;
++}
++
++struct file *au_h_fptr_i(struct file *file, aufs_bindex_t bindex)
++{
++ struct aufs_finfo *finfo = ftofi(file);
++ struct aufs_hfile *hf;
++
++ FiMustAnyLock(file);
++ DEBUG_ON(!finfo
++ || finfo->fi_bstart < 0
++ || bindex < finfo->fi_bstart
++ || finfo->fi_bend < bindex);
++ hf = finfo->fi_hfile + bindex;
++ DEBUG_ON(hf->hf_file
++ && file_count(hf->hf_file) <= 0
++ && br_count(hf->hf_br) <= 0);
++ return hf->hf_file;
++}
++
++struct file *au_h_fptr(struct file *file)
++{
++ return au_h_fptr_i(file, fbstart(file));
++}
++
++void set_fbstart(struct file *file, aufs_bindex_t bindex)
++{
++ FiMustWriteLock(file);
++ DEBUG_ON(sbend(file->f_dentry->d_sb) < bindex);
++ ftofi(file)->fi_bstart = bindex;
++}
++
++void set_fbend(struct file *file, aufs_bindex_t bindex)
++{
++ FiMustWriteLock(file);
++ DEBUG_ON(sbend(file->f_dentry->d_sb) < bindex
++ || bindex < fbstart(file));
++ ftofi(file)->fi_bend = bindex;
++}
++
++void set_fvdir_cache(struct file *file, struct aufs_vdir *vdir_cache)
++{
++ FiMustWriteLock(file);
++ DEBUG_ON(!S_ISDIR(file->f_dentry->d_inode->i_mode)
++ || (ftofi(file)->fi_vdir_cache && vdir_cache));
++ ftofi(file)->fi_vdir_cache = vdir_cache;
++}
++
++void au_hfput(struct aufs_hfile *hf)
++{
++ fput(hf->hf_file);
++ hf->hf_file = NULL;
++ DEBUG_ON(!hf->hf_br);
++ br_put(hf->hf_br);
++ hf->hf_br = NULL;
++}
++
++void set_h_fptr(struct file *file, aufs_bindex_t bindex, struct file *val)
++{
++ struct aufs_finfo *finfo = ftofi(file);
++ struct aufs_hfile *hf;
++
++ FiMustWriteLock(file);
++ DEBUG_ON(!finfo
++ || finfo->fi_bstart < 0
++ || bindex < finfo->fi_bstart
++ || finfo->fi_bend < bindex);
++ DEBUG_ON(val && file_count(val) <= 0);
++ hf = finfo->fi_hfile + bindex;
++ DEBUG_ON(val && hf->hf_file);
++ if (hf->hf_file)
++ au_hfput(hf);
++ if (val) {
++ hf->hf_file = val;
++ hf->hf_br = stobr(file->f_dentry->d_sb, bindex);
++ }
++}
++
++void au_update_figen(struct file *file)
++{
++ atomic_set(&ftofi(file)->fi_generation, au_digen(file->f_dentry));
++}
++
++void au_fin_finfo(struct file *file)
++{
++ struct aufs_finfo *finfo;
++ struct dentry *dentry;
++ aufs_bindex_t bindex, bend;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++ SiMustAnyLock(dentry->d_sb);
++
++ fi_write_lock(file);
++ bend = fbend(file);
++ bindex = fbstart(file);
++ if (bindex >= 0)
++ for (; bindex <= bend; bindex++)
++ set_h_fptr(file, bindex, NULL);
++
++ finfo = ftofi(file);
++#ifdef CONFIG_AUFS_DEBUG
++ if (finfo->fi_bstart >= 0) {
++ bend = fbend(file);
++ for (bindex = finfo->fi_bstart; bindex <= bend; bindex++) {
++ struct aufs_hfile *hf;
++ hf = finfo->fi_hfile + bindex;
++ DEBUG_ON(hf->hf_file || hf->hf_br);
++ }
++ }
++#endif
++
++ kfree(finfo->fi_hfile);
++ fi_write_unlock(file);
++ cache_free_finfo(finfo);
++ //file->private_data = NULL;
++}
++
++int au_init_finfo(struct file *file)
++{
++ struct aufs_finfo *finfo;
++ struct dentry *dentry;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++ DEBUG_ON(!dentry->d_inode);
++
++ finfo = cache_alloc_finfo();
++ if (finfo) {
++ finfo->fi_hfile = kcalloc(sbend(dentry->d_sb) + 1,
++ sizeof(*finfo->fi_hfile), GFP_KERNEL);
++ if (finfo->fi_hfile) {
++ rw_init_wlock(&finfo->fi_rwsem);
++ finfo->fi_bstart = -1;
++ finfo->fi_bend = -1;
++ atomic_set(&finfo->fi_generation, au_digen(dentry));
++
++ file->private_data = finfo;
++ return 0; /* success */
++ }
++ cache_free_finfo(finfo);
++ }
++
++ TraceErr(-ENOMEM);
++ return -ENOMEM;
++}
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/hinotify.c linux-2.6.22.1/fs/aufs/hinotify.c
+--- linux-2.6.22.1.oorig/fs/aufs/hinotify.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/hinotify.c 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,536 @@
++/*
++ * 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: hinotify.c,v 1.19 2007/05/14 03:39:21 sfjro Exp $ */
++
++#include "aufs.h"
++
++static struct inotify_handle *in_handle;
++static const __u32 in_mask = (IN_MOVE | IN_DELETE | IN_CREATE /* | IN_ACCESS */
++ | IN_MODIFY | IN_ATTRIB
++ | IN_DELETE_SELF | IN_MOVE_SELF);
++
++int alloc_hinotify(struct aufs_hinode *hinode, struct inode *inode,
++ struct inode *hidden_inode)
++{
++ int err;
++ struct aufs_hinotify *hin;
++ s32 wd;
++
++ LKTRTrace("i%lu, hi%lu\n", inode->i_ino, hidden_inode->i_ino);
++
++ err = -ENOMEM;
++ hin = cache_alloc_hinotify();
++ if (hin) {
++ DEBUG_ON(hinode->hi_notify);
++ hinode->hi_notify = hin;
++ hin->hin_aufs_inode = inode;
++ inotify_init_watch(&hin->hin_watch);
++ wd = inotify_add_watch(in_handle, &hin->hin_watch, hidden_inode,
++ in_mask);
++ if (wd >= 0)
++ return 0; /* success */
++
++ err = wd;
++ put_inotify_watch(&hin->hin_watch);
++ cache_free_hinotify(hin);
++ hinode->hi_notify = NULL;
++ }
++
++ TraceErr(err);
++ return err;
++}
++
++void do_free_hinotify(struct aufs_hinode *hinode)
++{
++ int err;
++ struct aufs_hinotify *hin;
++
++ TraceEnter();
++
++ hin = hinode->hi_notify;
++ if (hin) {
++ err = 0;
++ if (atomic_read(&hin->hin_watch.count))
++ err = inotify_rm_watch(in_handle, &hin->hin_watch);
++
++ if (!err) {
++ cache_free_hinotify(hin);
++ hinode->hi_notify = NULL;
++ } else
++ IOErr1("failed inotify_rm_watch() %d\n", err);
++ }
++}
++
++/* ---------------------------------------------------------------------- */
++
++static void ctl_hinotify(struct aufs_hinode *hinode, const __u32 mask)
++{
++ struct inode *hi;
++ struct inotify_watch *watch;
++
++ hi = hinode->hi_inode;
++ LKTRTrace("hi%lu, sb %p, 0x%x\n", hi->i_ino, hi->i_sb, mask);
++ if (0 && !strcmp(current->comm, "link"))
++ dump_stack();
++ IMustLock(hi);
++ if (!hinode->hi_notify)
++ return;
++
++ watch = &hinode->hi_notify->hin_watch;
++#if 0
++ {
++ u32 wd;
++ wd = inotify_find_update_watch(in_handle, hi, mask);
++ TraceErr(wd);
++ // ignore an err;
++ }
++#else
++ watch->mask = mask;
++ smp_mb();
++#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, in_mask)
++
++void do_hdir_lock(struct inode *h_dir, struct inode *dir, aufs_bindex_t bindex,
++ unsigned int lsc)
++{
++ struct aufs_hinode *hinode;
++
++ LKTRTrace("i%lu, b%d, lsc %d\n", dir->i_ino, bindex, lsc);
++ DEBUG_ON(!S_ISDIR(dir->i_mode));
++ hinode = itoii(dir)->ii_hinode + bindex;
++ DEBUG_ON(h_dir != hinode->hi_inode);
++
++ hi_lock(h_dir, lsc);
++ if (1 /* unlikely(au_flag_test(dir->i_sb, AuFlag_UDBA_HINOTIFY) */)
++ suspend_hinotify(hinode);
++}
++
++void hdir_unlock(struct inode *h_dir, struct inode *dir, aufs_bindex_t bindex)
++{
++ struct aufs_hinode *hinode;
++
++ LKTRTrace("i%lu, b%d\n", dir->i_ino, bindex);
++ DEBUG_ON(!S_ISDIR(dir->i_mode));
++ hinode = itoii(dir)->ii_hinode + bindex;
++ DEBUG_ON(h_dir != hinode->hi_inode);
++
++ if (1 /* unlikely(au_flag_test(dir->i_sb, AuFlag_UDBA_HINOTIFY) */)
++ resume_hinotify(hinode);
++ i_unlock(h_dir);
++}
++
++void hdir_lock_rename(struct dentry **h_parents, struct inode **dirs,
++ aufs_bindex_t bindex, int issamedir)
++{
++ struct aufs_hinode *hinode;
++
++ LKTRTrace("%.*s, %.*s\n", DLNPair(h_parents[0]), DLNPair(h_parents[1]));
++
++ vfsub_lock_rename(h_parents[0], h_parents[1]);
++ hinode = itoii(dirs[0])->ii_hinode + bindex;
++ DEBUG_ON(h_parents[0]->d_inode != hinode->hi_inode);
++ suspend_hinotify(hinode);
++ if (issamedir)
++ return;
++ hinode = itoii(dirs[1])->ii_hinode + bindex;
++ DEBUG_ON(h_parents[1]->d_inode != hinode->hi_inode);
++ suspend_hinotify(hinode);
++}
++
++void hdir_unlock_rename(struct dentry **h_parents, struct inode **dirs,
++ aufs_bindex_t bindex, int issamedir)
++{
++ struct aufs_hinode *hinode;
++
++ LKTRTrace("%.*s, %.*s\n", DLNPair(h_parents[0]), DLNPair(h_parents[1]));
++
++ hinode = itoii(dirs[0])->ii_hinode + bindex;
++ DEBUG_ON(h_parents[0]->d_inode != hinode->hi_inode);
++ resume_hinotify(hinode);
++ if (!issamedir) {
++ hinode = itoii(dirs[1])->ii_hinode + bindex;
++ DEBUG_ON(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;
++
++ LKTRTrace("i%lu, 0x%x\n", inode->i_ino, flags);
++
++ bend = ibend(inode);
++ for (bindex = ibstart(inode); bindex <= bend; bindex++) {
++ hi = au_h_iptr_i(inode, bindex);
++ if (hi) {
++ //hi_lock(hi, AUFS_LSC_H_CHILD);
++ igrab(hi);
++ set_h_iptr(inode, bindex, NULL, 0);
++ set_h_iptr(inode, bindex, igrab(hi), flags);
++ iput(hi);
++ //i_unlock(hi);
++ }
++ }
++}
++
++/* ---------------------------------------------------------------------- */
++
++#ifdef CONFIG_AUFS_DEBUG
++static char *in_name(u32 mask)
++{
++#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
++#define in_name(m) "??"
++#endif
++
++static int dec_gen_by_name(struct inode *dir, const char *_name, u32 mask)
++{
++ int err;
++ struct dentry *parent, *child;
++ struct inode *inode;
++ struct qstr *dname;
++ char *name = (void*)_name;
++ unsigned int len;
++
++ LKTRTrace("i%lu, %s, 0x%x %s\n",
++ dir->i_ino, name, mask, in_name(mask));
++
++ err = -1;
++ parent = d_find_alias(dir);
++ if (unlikely(!parent))
++ goto out;
++
++#if 0
++ if (unlikely(!memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)))
++ name += AUFS_WH_PFX_LEN;
++#endif
++ len = strlen(name);
++ spin_lock(&dcache_lock);
++ list_for_each_entry(child, &parent->d_subdirs, d_u.d_child) {
++ dname = &child->d_name;
++ if (len == dname->len && !memcmp(dname->name, name, len)) {
++ au_digen_dec(child);
++#if 1
++ //todo: why both are needed
++ if (mask & IN_MOVE) {
++ spin_lock(&child->d_lock);
++ __d_drop(child);
++ spin_unlock(&child->d_lock);
++ }
++#endif
++
++ inode = child->d_inode;
++ if (inode)
++ au_iigen_dec(inode);
++ err = !!inode;
++
++ // todo: the i_nlink of newly created name by link(2)
++ // should be updated
++ // todo: some nfs dentry doesn't notified at deleteing
++ break;
++ }
++ }
++ spin_unlock(&dcache_lock);
++ dput(parent);
++
++ out:
++ TraceErr(err);
++ return err;
++}
++
++struct postproc_args {
++ struct inode *h_dir, *dir, *h_child_inode;
++ char *h_child_name;
++ u32 mask;
++};
++
++static void dec_gen_by_ino(struct postproc_args *a)
++{
++ struct super_block *sb;
++ aufs_bindex_t bindex, bend, bfound;
++ struct xino xino;
++ struct inode *cinode;
++
++ TraceEnter();
++
++ sb = a->dir->i_sb;
++ DEBUG_ON(!au_flag_test(sb, AuFlag_XINO));
++
++ bfound = -1;
++ bend = ibend(a->dir);
++ for (bindex = ibstart(a->dir); bfound == -1 && bindex <= bend; bindex++)
++ if (au_h_iptr_i(a->dir, bindex) == a->h_dir)
++ bfound = bindex;
++ if (bfound < 0)
++ return;
++
++ bindex = find_brindex(sb, itoii(a->dir)->ii_hinode[bfound + 0].hi_id);
++ if (bindex < 0)
++ return;
++ if (unlikely(xino_read(sb, bindex, a->h_child_inode->i_ino, &xino)))
++ return;
++ cinode = NULL;
++ if (xino.ino)
++ cinode = ilookup(sb, xino.ino);
++ if (cinode) {
++#if 1
++ if (1 || a->mask & IN_MOVE) {
++ struct dentry *child;
++ spin_lock(&dcache_lock);
++ list_for_each_entry(child, &cinode->i_dentry, d_alias)
++ au_digen_dec(child);
++ spin_unlock(&dcache_lock);
++ }
++#endif
++ au_iigen_dec(cinode);
++ iput(cinode);
++ }
++}
++
++static void reset_ino(struct postproc_args *a)
++{
++ aufs_bindex_t bindex, bend;
++ struct super_block *sb;
++ struct inode *h_dir;
++
++ sb = a->dir->i_sb;
++ bend = ibend(a->dir);
++ for (bindex = ibstart(a->dir); bindex <= bend; bindex++) {
++ h_dir = au_h_iptr_i(a->dir, bindex);
++ if (h_dir && h_dir != a->h_dir)
++ xino_write0(sb, bindex, h_dir->i_ino);
++ /* ignore this error */
++ }
++}
++
++static void postproc(void *args)
++{
++ struct postproc_args *a = args;
++ struct super_block *sb;
++ struct aufs_vdir *vdir;
++
++ //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);
++ DEBUG_ON(!a->dir);
++#if 0//def ForceInotify
++ Dbg("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);
++#endif
++
++ i_lock(a->dir);
++ sb = a->dir->i_sb;
++ si_read_lock(sb); // consider write_lock
++ ii_write_lock_parent(a->dir);
++
++ /* make dir entries obsolete */
++ vdir = ivdir(a->dir);
++ if (vdir)
++ vdir->vd_jiffy = 0;
++ a->dir->i_version++;
++
++ /*
++ * special handling root directory,
++ * sine d_revalidate may not be called later.
++ * main purpose is maintaining i_nlink.
++ */
++ if (unlikely(a->dir->i_ino == AUFS_ROOT_INO))
++ au_cpup_attr_all(a->dir);
++
++ if (a->h_child_inode && au_flag_test(sb, AuFlag_XINO))
++ dec_gen_by_ino(a);
++ else if (a->mask & (IN_MOVE_SELF | IN_DELETE_SELF))
++ reset_ino(a);
++
++ ii_write_unlock(a->dir);
++ si_read_unlock(sb);
++ i_unlock(a->dir);
++
++ au_mntput(a->dir->i_sb);
++ iput(a->h_child_inode);
++ iput(a->h_dir);
++ iput(a->dir);
++#if 0
++ if (atomic_dec_and_test(&stosi(sb)->si_hinotify))
++ wake_up_all(&stosi(sb)->si_hinotify_wq);
++#endif
++ kfree(a);
++ //au_debug_off();
++}
++
++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 aufs_hinotify *hinotify;
++ struct postproc_args *args;
++ int len;
++ char *p;
++ struct inode *dir;
++ //static DECLARE_WAIT_QUEUE_HEAD(wq);
++
++ //au_debug_on();
++ 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);
++ //au_debug_off();
++ //IMustLock(h_dir);
++#if 0 //defined(ForceInotify) || defined(DbgInotify)
++ Dbg("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)) {
++ put_inotify_watch(watch);
++ return;
++ }
++
++ switch (mask & IN_ALL_EVENTS) {
++ case IN_MODIFY:
++ case IN_ATTRIB:
++ if (h_child_name)
++ return;
++ break;
++
++ case IN_MOVED_FROM:
++ case IN_MOVED_TO:
++ case IN_CREATE:
++ DEBUG_ON(!h_child_name || !h_child_inode);
++ break;
++ case IN_DELETE:
++ /*
++ * aufs never be able to get this child inode.
++ * revalidation should be in d_revalide()
++ * by checking i_nlink, i_generation or d_unhashed().
++ */
++ DEBUG_ON(!h_child_name);
++ break;
++
++ case IN_DELETE_SELF:
++ case IN_MOVE_SELF:
++ DEBUG_ON(h_child_name || h_child_inode);
++ break;
++
++ case IN_ACCESS:
++ default:
++ DEBUG_ON(1);
++ }
++
++#ifdef DbgInotify
++ WARN_ON(1);
++#endif
++
++ /* iput() will be called in postproc() */
++ hinotify = container_of(watch, struct aufs_hinotify, hin_watch);
++ DEBUG_ON(!hinotify || !hinotify->hin_aufs_inode);
++ dir = hinotify->hin_aufs_inode;
++
++ /* force re-lookup in next d_revalidate() */
++ if (dir->i_ino != AUFS_ROOT_INO)
++ au_iigen_dec(dir);
++ len = 0;
++ if (h_child_name && dec_gen_by_name(dir, h_child_name, mask))
++ len = strlen(h_child_name);
++
++ //wait_event(wq, (args = kmalloc(sizeof(*args), GFP_KERNEL)));
++ args = kmalloc(sizeof(*args) + len + 1, GFP_KERNEL);
++ if (unlikely(!args)) {
++ Err("no memory\n");
++ return;
++ }
++ args->mask = mask;
++ args->dir = igrab(dir);
++ args->h_dir = igrab(watch->inode);
++ args->h_child_inode = NULL;
++ if (len) {
++ if (h_child_inode)
++ args->h_child_inode = igrab(h_child_inode);
++ p = (void*)args;
++ args->h_child_name = p + sizeof(*args);
++ memcpy(args->h_child_name, h_child_name, len + 1);
++ }
++ //atomic_inc(&stosi(args->dir->i_sb)->si_hinotify);
++ /* prohibit umount */
++ au_mntget(args->dir->i_sb);
++ au_wkq_nowait(postproc, args, /*dlgt*/0);
++}
++
++#if 0
++void hinotify_flush(struct super_block *sb)
++{
++ atomic_t *p = &stosi(sb)->si_hinotify;
++ wait_event(stosi(sb)->si_hinotify_wq, !atomic_read(p));
++}
++#endif
++
++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
++};
++
++/* ---------------------------------------------------------------------- */
++
++int __init au_inotify_init(void)
++{
++ in_handle = inotify_init(&aufs_inotify_ops);
++ if (!IS_ERR(in_handle))
++ return 0;
++ TraceErrPtr(in_handle);
++ return PTR_ERR(in_handle);
++}
++
++void au_inotify_fin(void)
++{
++ inotify_destroy(in_handle);
++}
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/i_op.c linux-2.6.22.1/fs/aufs/i_op.c
+--- linux-2.6.22.1.oorig/fs/aufs/i_op.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/i_op.c 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,641 @@
++/*
++ * 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: i_op.c,v 1.30 2007/04/23 00:55:05 sfjro Exp $ */
++
++//#include <linux/fs.h>
++//#include <linux/namei.h>
++#include <linux/security.h>
++#include <asm/uaccess.h>
++#include "aufs.h"
++
++#ifdef CONFIG_AUFS_DLGT
++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);
++}
++#endif
++
++static int hidden_permission(struct inode *hidden_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",
++ hidden_inode->i_ino, mask, brperm);
++
++ err = -EACCES;
++ if (unlikely(write_mask && IS_IMMUTABLE(hidden_inode)))
++ goto out;
++
++ /* skip hidden fs test in the case of write to ro branch */
++ submask = mask & ~MAY_APPEND;
++ if (unlikely((write_mask && !br_writable(brperm))
++ || !hidden_inode->i_op
++ || !hidden_inode->i_op->permission)) {
++ //LKTRLabel(generic_permission);
++ err = generic_permission(hidden_inode, submask, NULL);
++ } else {
++ //LKTRLabel(h_inode->permission);
++ err = hidden_inode->i_op->permission(hidden_inode, submask,
++ fake_nd);
++ TraceErr(err);
++ }
++
++#if 1
++ if (!err) {
++#ifndef CONFIG_AUFS_DLGT
++ err = security_inode_permission(hidden_inode, mask, fake_nd);
++#else
++ if (!dlgt)
++ err = security_inode_permission(hidden_inode, mask,
++ fake_nd);
++ else {
++ struct security_inode_permission_args args = {
++ .errp = &err,
++ .h_inode = hidden_inode,
++ .mask = mask,
++ .fake_nd = fake_nd
++ };
++ au_wkq_wait(call_security_inode_permission, &args,
++ /*dlgt*/1);
++ }
++#endif
++ }
++#endif
++
++ out:
++ TraceErr(err);
++ return err;
++}
++
++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);
++
++#ifdef CONFIG_AUFS_FAKE_DM
++ si_read_lock(sb);
++ ii_read_lock_child(inode);
++#else
++ if (!nd || !nd->dentry) {
++ si_read_lock(sb);
++ ii_read_lock_child(inode);
++ } else if (nd->dentry->d_inode != inode) {
++ locked = 1;
++ /* lock child first, then parent */
++ si_read_lock(sb);
++ ii_read_lock_child(inode);
++ di_read_lock_parent(nd->dentry, 0);
++ } else {
++ locked = 2;
++ aufs_read_lock(nd->dentry, AUFS_I_RLOCK);
++ }
++#endif
++ 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);
++
++#ifdef CONFIG_AUFS_FAKE_DM
++ ii_read_unlock(inode);
++ si_read_unlock(sb);
++#else
++ switch (locked) {
++ case 0:
++ ii_read_unlock(inode);
++ si_read_unlock(sb);
++ break;
++ case 1:
++ di_read_unlock(nd->dentry, 0);
++ ii_read_unlock(inode);
++ si_read_unlock(sb);
++ break;
++ case 2:
++ aufs_read_unlock(nd->dentry, AUFS_I_RLOCK);
++ break;
++ default:
++ BUG();
++ }
++#endif
++}
++
++static int aufs_permission(struct inode *inode, int mask, struct nameidata *nd)
++{
++ int err, locked, dlgt;
++ aufs_bindex_t bindex, bend;
++ struct inode *hidden_inode;
++ struct super_block *sb;
++ 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 %p{%p, %p}\n",
++ inode->i_ino, mask, nondir, write_mask,
++ nd, nd ? nd->dentry : NULL, nd ? nd->mnt : NULL);
++
++ sb = inode->i_sb;
++ locked = silly_lock(inode, nd);
++ dlgt = need_dlgt(sb);
++
++ if (nd)
++ fake_nd = *nd;
++ if (/* unlikely */(nondir || write_mask)) {
++ hidden_inode = au_h_iptr(inode);
++ DEBUG_ON(!hidden_inode
++ || ((hidden_inode->i_mode & S_IFMT)
++ != (inode->i_mode & S_IFMT)));
++ err = 0;
++ bindex = ibstart(inode);
++ p = fake_dm(&fake_nd, nd, sb, bindex);
++ /* actual test will be delegated to LSM */
++ if (IS_ERR(p))
++ DEBUG_ON(PTR_ERR(p) != -ENOENT);
++ else {
++ err = hidden_permission(hidden_inode, mask, p,
++ sbr_perm(sb, bindex), dlgt);
++ fake_dm_release(p);
++ }
++ if (write_mask && !err) {
++ err = find_rw_br(sb, bindex);
++ if (err >= 0)
++ err = 0;
++ }
++ goto out;
++ }
++
++ /* non-write to dir */
++ err = 0;
++ bend = ibend(inode);
++ for (bindex = ibstart(inode); !err && bindex <= bend; bindex++) {
++ hidden_inode = au_h_iptr_i(inode, bindex);
++ if (!hidden_inode)
++ continue;
++ DEBUG_ON(!S_ISDIR(hidden_inode->i_mode));
++
++ p = fake_dm(&fake_nd, nd, sb, bindex);
++ /* actual test will be delegated to LSM */
++ if (IS_ERR(p))
++ DEBUG_ON(PTR_ERR(p) != -ENOENT);
++ else {
++ err = hidden_permission(hidden_inode, mask, p,
++ sbr_perm(sb, bindex), dlgt);
++ fake_dm_release(p);
++ }
++ }
++
++ out:
++ silly_unlock(locked, inode, nd);
++ TraceErr(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;
++
++ LKTRTrace("dir %lu, %.*s\n", dir->i_ino, DLNPair(dentry));
++ DEBUG_ON(IS_ROOT(dentry));
++ IMustLock(dir);
++
++ parent = dentry->d_parent; // dget_parent()
++ aufs_read_lock(parent, !AUFS_I_RLOCK);
++ err = au_alloc_dinfo(dentry);
++ //if (LktrCond) err = -1;
++ ret = ERR_PTR(err);
++ if (unlikely(err))
++ goto out;
++
++ err = npositive = lkup_dentry(dentry, dbstart(parent), /*type*/0);
++ //err = -1;
++ ret = ERR_PTR(err);
++ if (unlikely(err < 0))
++ goto out_unlock;
++ inode = NULL;
++ if (npositive) {
++ inode = au_new_inode(dentry);
++ 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
++ }
++
++ out_unlock:
++ di_write_unlock(dentry);
++ out:
++ aufs_read_unlock(parent, !AUFS_I_RLOCK);
++ TraceErrPtr(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 wr_dir(struct dentry *dentry, int add_entry, struct dentry *src_dentry,
++ aufs_bindex_t force_btgt, int do_lock_srcdir)
++{
++ int err;
++ aufs_bindex_t bcpup, bstart, src_bstart;
++ struct dentry *hidden_parent;
++ struct super_block *sb;
++ struct dentry *parent, *src_parent = NULL;
++ struct inode *dir, *src_dir = NULL;
++
++ LKTRTrace("%.*s, add %d, src %p, force %d, lock_srcdir %d\n",
++ DLNPair(dentry), add_entry, src_dentry, force_btgt,
++ do_lock_srcdir);
++
++ sb = dentry->d_sb;
++ parent = dentry->d_parent; // dget_parent()
++ bcpup = bstart = dbstart(dentry);
++ if (force_btgt < 0) {
++ if (src_dentry) {
++ src_bstart = dbstart(src_dentry);
++ if (src_bstart < bstart)
++ bcpup = src_bstart;
++ }
++ if (test_ro(sb, bcpup, dentry->d_inode)) {
++ if (!add_entry)
++ di_read_lock_parent(parent, !AUFS_I_RLOCK);
++ bcpup = err = find_rw_parent_br(dentry, bcpup);
++ //bcpup = err = find_rw_br(sb, bcpup);
++ if (!add_entry)
++ di_read_unlock(parent, !AUFS_I_RLOCK);
++ //err = -1;
++ if (unlikely(err < 0))
++ goto out;
++ }
++ } else {
++ DEBUG_ON(bstart <= force_btgt
++ || test_ro(sb, force_btgt, dentry->d_inode));
++ bcpup = force_btgt;
++ }
++ LKTRTrace("bstart %d, bcpup %d\n", bstart, bcpup);
++
++ err = bcpup;
++ if (bcpup == bstart)
++ goto out; /* success */
++
++ /* copyup the new parent into the branch we process */
++ hidden_parent = au_h_dptr(dentry)->d_parent; // dget_parent()
++ if (src_dentry) {
++ src_parent = src_dentry->d_parent; // dget_parent()
++ src_dir = src_parent->d_inode;
++ if (do_lock_srcdir)
++ di_write_lock_parent2(src_parent);
++ }
++
++ dir = parent->d_inode;
++ if (add_entry) {
++ au_update_dbstart(dentry);
++ IMustLock(dir);
++ DiMustWriteLock(parent);
++ IiMustWriteLock(dir);
++ } else
++ di_write_lock_parent(parent);
++
++ err = 0;
++ if (!au_h_dptr_i(parent, bcpup))
++ err = cpup_dirs(dentry, bcpup, src_parent);
++ //err = -1;
++ if (!err && add_entry) {
++ hidden_parent = au_h_dptr_i(parent, bcpup);
++ DEBUG_ON(!hidden_parent || !hidden_parent->d_inode);
++ hi_lock_parent(hidden_parent->d_inode);
++ err = lkup_neg(dentry, bcpup);
++ //err = -1;
++ i_unlock(hidden_parent->d_inode);
++ }
++
++ if (!add_entry)
++ di_write_unlock(parent);
++ if (do_lock_srcdir)
++ di_write_unlock(src_parent);
++ if (!err)
++ err = bcpup; /* success */
++ //err = -EPERM;
++ out:
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static int aufs_setattr(struct dentry *dentry, struct iattr *ia)
++{
++ int err, isdir;
++ aufs_bindex_t bstart, bcpup;
++ struct inode *hidden_inode, *inode, *dir, *h_dir, *gh_dir, *gdir;
++ struct dentry *hidden_dentry, *parent;
++ unsigned int udba;
++
++ LKTRTrace("%.*s, ia_valid 0x%x\n", DLNPair(dentry), ia->ia_valid);
++ inode = dentry->d_inode;
++ IMustLock(inode);
++
++ aufs_read_lock(dentry, AUFS_D_WLOCK);
++ bstart = dbstart(dentry);
++ bcpup = err = wr_dir(dentry, /*add*/0, /*src_dentry*/NULL,
++ /*force_btgt*/-1, /*do_lock_srcdir*/0);
++ //err = -1;
++ if (unlikely(err < 0))
++ goto out;
++
++ /* crazy udba locks */
++ udba = au_flag_test(dentry->d_sb, AuFlag_UDBA_INOTIFY);
++ parent = NULL;
++ gdir = gh_dir = dir = h_dir = NULL;
++ if ((udba || bstart != bcpup) && !IS_ROOT(dentry)) {
++ parent = dentry->d_parent; // dget_parent()
++ dir = parent->d_inode;
++ di_read_lock_parent(parent, AUFS_I_RLOCK);
++ h_dir = au_h_iptr_i(dir, bcpup);
++ }
++ if (parent) {
++ if (unlikely(udba && !IS_ROOT(parent))) {
++ gdir = parent->d_parent->d_inode; // dget_parent()
++ ii_read_lock_parent2(gdir);
++ gh_dir = au_h_iptr_i(gdir, bcpup);
++ hgdir_lock(gh_dir, gdir, bcpup);
++ }
++ hdir_lock(h_dir, dir, bcpup);
++ }
++
++ isdir = S_ISDIR(inode->i_mode);
++ hidden_dentry = au_h_dptr(dentry);
++ hidden_inode = hidden_dentry->d_inode;
++ DEBUG_ON(!hidden_inode);
++
++#define HiLock(bindex) do {\
++ if (!isdir) \
++ hi_lock_child(hidden_inode); \
++ else \
++ hdir2_lock(hidden_inode, inode, bindex); \
++ } while (0)
++#define HiUnlock(bindex) do {\
++ if (!isdir) \
++ i_unlock(hidden_inode); \
++ else \
++ hdir_unlock(hidden_inode, inode, bindex); \
++ } while (0)
++
++ if (bstart != bcpup) {
++ loff_t size = -1;
++
++ if ((ia->ia_valid & ATTR_SIZE)
++ && ia->ia_size < i_size_read(inode)) {
++ size = ia->ia_size;
++ ia->ia_valid &= ~ATTR_SIZE;
++ }
++ HiLock(bstart);
++ err = sio_cpup_simple(dentry, bcpup, size,
++ au_flags_cpup(CPUP_DTIME, parent));
++ //err = -1;
++ HiUnlock(bstart);
++ if (unlikely(err || !ia->ia_valid))
++ goto out_unlock;
++
++ hidden_dentry = au_h_dptr(dentry);
++ hidden_inode = hidden_dentry->d_inode;
++ DEBUG_ON(!hidden_inode);
++ }
++
++ HiLock(bcpup);
++ err = vfsub_notify_change(hidden_dentry, ia, need_dlgt(dentry->d_sb));
++ //err = -1;
++ if (!err)
++ au_cpup_attr_changable(inode);
++ HiUnlock(bcpup);
++#undef HiLock
++#undef HiUnlock
++
++ out_unlock:
++ if (parent) {
++ hdir_unlock(h_dir, dir, bcpup);
++ di_read_unlock(parent, AUFS_I_RLOCK);
++ }
++ if (unlikely(gdir)) {
++ hdir_unlock(gh_dir, gdir, bcpup);
++ ii_read_unlock(gdir);
++ }
++ out:
++ aufs_read_unlock(dentry, AUFS_D_WLOCK);
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static int hidden_readlink(struct dentry *dentry, int bindex,
++ char __user * buf, int bufsiz)
++{
++ struct super_block *sb;
++ struct dentry *hidden_dentry;
++
++ hidden_dentry = au_h_dptr_i(dentry, bindex);
++ if (unlikely(!hidden_dentry->d_inode->i_op
++ || !hidden_dentry->d_inode->i_op->readlink))
++ return -EINVAL;
++
++ sb = dentry->d_sb;
++ if (!test_ro(sb, bindex, dentry->d_inode)) {
++ touch_atime(sbr_mnt(sb, bindex), hidden_dentry);
++ dentry->d_inode->i_atime = hidden_dentry->d_inode->i_atime;
++ }
++ return hidden_dentry->d_inode->i_op->readlink
++ (hidden_dentry, buf, bufsiz);
++}
++
++static int aufs_readlink(struct dentry *dentry, char __user * buf, int bufsiz)
++{
++ int err;
++
++ LKTRTrace("%.*s, %d\n", DLNPair(dentry), bufsiz);
++
++ aufs_read_lock(dentry, AUFS_I_RLOCK);
++ err = hidden_readlink(dentry, dbstart(dentry), buf, bufsiz);
++ //err = -1;
++ aufs_read_unlock(dentry, AUFS_I_RLOCK);
++ TraceErr(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", DLNPair(dentry), DLNPair(nd->dentry));
++
++ err = -ENOMEM;
++ buf = __getname();
++ //buf = NULL;
++ if (unlikely(!buf))
++ goto out;
++
++ aufs_read_lock(dentry, AUFS_I_RLOCK);
++ old_fs = get_fs();
++ set_fs(KERNEL_DS);
++ err = hidden_readlink(dentry, dbstart(dentry), (char __user *)buf,
++ PATH_MAX);
++ //err = -1;
++ set_fs(old_fs);
++ aufs_read_unlock(dentry, AUFS_I_RLOCK);
++
++ if (err >= 0) {
++ buf[err] = 0;
++ /* will be freed by put_link */
++ nd_set_link(nd, buf);
++ return NULL; /* success */
++ }
++ __putname(buf);
++
++ out:
++ path_release(nd);
++ TraceErr(err);
++ return ERR_PTR(err);
++}
++
++static void aufs_put_link(struct dentry *dentry, struct nameidata *nd,
++ void *cookie)
++{
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++ __putname(nd_get_link(nd));
++}
++
++/* ---------------------------------------------------------------------- */
++#if 0 // comment
++struct inode_operations {
++ int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
++ struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *);
++ int (*link) (struct dentry *,struct inode *,struct dentry *);
++ int (*unlink) (struct inode *,struct dentry *);
++ int (*symlink) (struct inode *,struct dentry *,const char *);
++ int (*mkdir) (struct inode *,struct dentry *,int);
++ int (*rmdir) (struct inode *,struct dentry *);
++ int (*mknod) (struct inode *,struct dentry *,int,dev_t);
++ int (*rename) (struct inode *, struct dentry *,
++ struct inode *, struct dentry *);
++ int (*readlink) (struct dentry *, char __user *,int);
++ void * (*follow_link) (struct dentry *, struct nameidata *);
++ void (*put_link) (struct dentry *, struct nameidata *, void *);
++ void (*truncate) (struct inode *);
++ int (*permission) (struct inode *, int, struct nameidata *);
++ int (*setattr) (struct dentry *, struct iattr *);
++ int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
++ int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
++ ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
++ ssize_t (*listxattr) (struct dentry *, char *, size_t);
++ int (*removexattr) (struct dentry *, const char *);
++ void (*truncate_range)(struct inode *, loff_t, loff_t);
++};
++#endif
++
++struct inode_operations aufs_symlink_iop = {
++ .permission = aufs_permission,
++ .setattr = aufs_setattr,
++
++ .readlink = aufs_readlink,
++ .follow_link = aufs_follow_link,
++ .put_link = aufs_put_link
++};
++
++//i_op_add.c
++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 aufs_unlink(struct inode *dir, struct dentry *dentry);
++int aufs_rmdir(struct inode *dir, struct dentry *dentry);
++
++// i_op_ren.c
++int aufs_rename(struct inode *src_dir, struct dentry *src_dentry,
++ struct inode *dir, struct dentry *dentry);
++
++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,
++
++#if 0 // xattr
++ .setxattr = aufs_setxattr,
++ .getxattr = aufs_getxattr,
++ .listxattr = aufs_listxattr,
++ .removexattr = aufs_removexattr
++#endif
++};
++
++struct inode_operations aufs_iop = {
++ .permission = aufs_permission,
++ .setattr = aufs_setattr,
++
++#if 0 // xattr
++ .setxattr = aufs_setxattr,
++ .getxattr = aufs_getxattr,
++ .listxattr = aufs_listxattr,
++ .removexattr = aufs_removexattr
++#endif
++};
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/i_op_add.c linux-2.6.22.1/fs/aufs/i_op_add.c
+--- linux-2.6.22.1.oorig/fs/aufs/i_op_add.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/i_op_add.c 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,621 @@
++/*
++ * 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: i_op_add.c,v 1.37 2007/05/07 03:46:08 sfjro Exp $ */
++
++//#include <linux/fs.h>
++//#include <linux/namei.h>
++#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 dentry *wh_dentry, struct dentry *dentry)
++{
++ int err, rerr;
++ aufs_bindex_t bwh;
++ struct inode *inode, *dir;
++ struct dentry *wh;
++ struct lkup_args lkup;
++
++ LKTRTrace("wh %p, %.*s\n", wh_dentry, DLNPair(dentry));
++
++ lkup.dlgt = need_dlgt(dentry->d_sb);
++ bwh = -1;
++ if (wh_dentry) {
++ bwh = dbwh(dentry);
++ err = au_unlink_wh_dentry(wh_dentry->d_parent->d_inode,
++ wh_dentry, dentry, lkup.dlgt);
++ //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;
++ /* or always cpup dir mtime? */
++ if (ibstart(dir) == dbstart(dentry))
++ au_cpup_attr_timesizes(dir);
++ dir->i_version++;
++ return 0; /* success */
++ }
++
++ err = PTR_ERR(inode);
++ if (!wh_dentry)
++ goto out;
++
++ /* revert */
++ lkup.nfsmnt = au_nfsmnt(dentry->d_sb, bwh);
++ wh = simple_create_wh(dentry, bwh, wh_dentry->d_parent, &lkup);
++ //wh = ERR_PTR(-1);
++ rerr = PTR_ERR(wh);
++ if (!IS_ERR(wh)) {
++ dput(wh);
++ goto out;
++ }
++ IOErr("%.*s reverting whiteout failed(%d, %d)\n",
++ DLNPair(dentry), err, rerr);
++ err = -EIO;
++
++ out:
++ TraceErr(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 dtime *dt,
++ struct dentry *src_dentry, int do_lock_srcdir)
++{
++ struct dentry *wh_dentry, *parent, *hidden_parent;
++ int err;
++ aufs_bindex_t bstart, bcpup;
++ struct inode *dir, *h_dir;
++ struct lkup_args lkup;
++
++ LKTRTrace("%.*s, src %p\n", DLNPair(dentry), src_dentry);
++
++ parent = dentry->d_parent;
++ bstart = dbstart(dentry);
++ bcpup = err = wr_dir(dentry, 1, src_dentry, -1, do_lock_srcdir);
++ //err = -1;
++ wh_dentry = ERR_PTR(err);
++ if (unlikely(err < 0))
++ goto out;
++
++ dir = parent->d_inode;
++ hidden_parent = au_h_dptr_i(parent, bcpup);
++ h_dir = hidden_parent->d_inode;
++ hdir_lock(h_dir, dir, bcpup);
++ if (dt)
++ dtime_store(dt, parent, hidden_parent);
++ if (/* bcpup != bstart || */ bcpup != dbwh(dentry))
++ return NULL; /* success */
++
++ lkup.nfsmnt = au_nfsmnt(parent->d_sb, bcpup);
++ lkup.dlgt = need_dlgt(parent->d_sb);
++ wh_dentry = lkup_wh(hidden_parent, &dentry->d_name, &lkup);
++ //wh_dentry = ERR_PTR(-1);
++ if (IS_ERR(wh_dentry))
++ hdir_unlock(h_dir, dir, bcpup);
++
++ out:
++ TraceErrPtr(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;
++ struct dentry *hidden_dentry, *hidden_parent, *wh_dentry, *parent;
++ struct inode *hidden_dir;
++ struct dtime dt;
++
++ LKTRTrace("type %d, %.*s\n", arg->type, DLNPair(dentry));
++ IMustLock(dir);
++
++ aufs_read_lock(dentry, AUFS_D_WLOCK);
++ parent = dentry->d_parent;
++ di_write_lock_parent(parent);
++ wh_dentry = lock_hdir_lkup_wh(dentry, &dt, /*src_dentry*/NULL,
++ /*do_lock_srcdir*/0);
++ //wh_dentry = ERR_PTR(-1);
++ err = PTR_ERR(wh_dentry);
++ if (IS_ERR(wh_dentry))
++ goto out;
++
++ hidden_dentry = au_h_dptr(dentry);
++ hidden_parent = hidden_dentry->d_parent;
++ hidden_dir = hidden_parent->d_inode;
++ IMustLock(hidden_dir);
++ dlgt = need_dlgt(dir->i_sb);
++
++#if 1 // partial testing
++ switch (arg->type) {
++ case Creat:
++#if 0
++ if (arg->u.c.nd) {
++ struct nameidata fake_nd;
++ fake_nd = *arg->u.c.nd;
++ fake_nd.dentry = dget(hidden_parent);
++ fake_nd.mnt = sbr_mnt(dentry->d_sb, dbstart(dentry));
++ mntget(fake_nd.mnt);
++ err = vfsub_create(hidden_dir, hidden_dentry,
++ arg->u.c.mode, &fake_nd, dlgt);
++ path_release(&fake_nd);
++ } else
++#endif
++ err = vfsub_create(hidden_dir, hidden_dentry,
++ arg->u.c.mode, NULL, dlgt);
++ break;
++ case Symlink:
++ err = vfsub_symlink(hidden_dir, hidden_dentry,
++ arg->u.s.symname, S_IALLUGO, dlgt);
++ break;
++ case Mknod:
++ err = vfsub_mknod(hidden_dir, hidden_dentry,
++ arg->u.m.mode, arg->u.m.dev, dlgt);
++ break;
++ default:
++ BUG();
++ }
++#else
++ err = -1;
++#endif
++ if (!err)
++ err = epilog(wh_dentry, dentry);
++ //err = -1;
++
++ /* revert */
++ if (unlikely(err && hidden_dentry->d_inode)) {
++ int rerr;
++ rerr = vfsub_unlink(hidden_dir, hidden_dentry, dlgt);
++ //rerr = -1;
++ if (rerr) {
++ IOErr("%.*s revert failure(%d, %d)\n",
++ DLNPair(dentry), err, rerr);
++ err = -EIO;
++ }
++ dtime_revert(&dt, !CPUP_LOCKED_GHDIR);
++ d_drop(dentry);
++ }
++
++ hdir_unlock(hidden_dir, dir, dbstart(dentry));
++ dput(wh_dentry);
++
++ out:
++ if (unlikely(err)) {
++ au_update_dbstart(dentry);
++ d_drop(dentry);
++ }
++ di_write_unlock(parent);
++ aufs_read_unlock(dentry, AUFS_D_WLOCK);
++ TraceErr(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, *hidden_dentry;
++ struct inode *hidden_dir, *inode;
++};
++
++static int cpup_before_link(struct dentry *src_dentry, struct inode *dir,
++ struct link_arg *a)
++{
++ int err;
++ unsigned int flags;
++ struct inode *hi, *hdir = NULL, *src_dir;
++
++ TraceEnter();
++
++ err = 0;
++ flags = au_flags_cpup(CPUP_DTIME, a->parent);
++ src_dir = a->src_parent->d_inode;
++ if (!a->issamedir) {
++ // todo: dead lock?
++ di_read_lock_parent2(a->src_parent, AUFS_I_RLOCK);
++ // this temporary unlock/lock is safe
++ hdir_unlock(a->hidden_dir, dir, a->bdst);
++ err = test_and_cpup_dirs(src_dentry, a->bdst, a->parent);
++ //err = -1;
++ if (!err) {
++ hdir = au_h_iptr_i(src_dir, a->bdst);
++ hdir_lock(hdir, src_dir, a->bdst);
++ flags = au_flags_cpup(CPUP_DTIME, a->src_parent);
++ }
++ }
++
++ if (!err) {
++ hi = au_h_dptr(src_dentry)->d_inode;
++ hi_lock_child(hi);
++ err = sio_cpup_simple(src_dentry, a->bdst, -1, flags);
++ //err = -1;
++ i_unlock(hi);
++ }
++
++ if (!a->issamedir) {
++ if (hdir)
++ hdir_unlock(hdir, src_dir, a->bdst);
++ hdir_lock(a->hidden_dir, dir, a->bdst);
++ di_read_unlock(a->src_parent, AUFS_I_RLOCK);
++ }
++
++ TraceErr(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;
++
++ TraceEnter();
++
++ sb = src_dentry->d_sb;
++ inode = src_dentry->d_inode;
++ h_dentry = au_h_dptr(src_dentry);
++ h_inode = h_dentry->d_inode;
++ bstart = ibstart(inode);
++ h_dst_inode = NULL;
++ if (bstart <= a->bdst)
++ h_dst_inode = au_h_iptr_i(inode, a->bdst);
++
++ if (!h_dst_inode) {
++ /* copyup src_dentry as the name of dentry. */
++ set_dbstart(src_dentry, a->bdst);
++ set_h_dptr(src_dentry, a->bdst, dget(a->hidden_dentry));
++ hi_lock_child(h_inode);
++ err = sio_cpup_single(src_dentry, a->bdst, a->bsrc, -1,
++ au_flags_cpup(!CPUP_DTIME, a->parent));
++ //err = -1;
++ i_unlock(h_inode);
++ set_h_dptr(src_dentry, a->bdst, NULL);
++ 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->hidden_dir,
++ a->hidden_dentry, a->dlgt);
++ dput(h_dentry);
++ } else {
++ IOErr("no dentry found for i%lu on b%d\n",
++ h_dst_inode->i_ino, a->bdst);
++ err = -EIO;
++ }
++ }
++
++ if (!err)
++ append_plink(sb, a->inode, a->hidden_dentry, a->bdst);
++
++ TraceErr(err);
++ return err;
++}
++
++int aufs_link(struct dentry *src_dentry, struct inode *dir,
++ struct dentry *dentry)
++{
++ int err, rerr;
++ struct dentry *hidden_parent, *wh_dentry, *hidden_src_dentry;
++ struct dtime dt;
++ struct link_arg a;
++ struct super_block *sb;
++
++ LKTRTrace("src %.*s, i%lu, dst %.*s\n",
++ DLNPair(src_dentry), dir->i_ino, DLNPair(dentry));
++ IMustLock(dir);
++ IMustLock(src_dentry->d_inode);
++
++ aufs_read_and_write_lock2(dentry, src_dentry, /*isdir*/0);
++ a.src_parent = src_dentry->d_parent;
++ a.parent = dentry->d_parent;
++ a.issamedir = (a.src_parent == a.parent);
++ di_write_lock_parent(a.parent);
++ wh_dentry = lock_hdir_lkup_wh(dentry, &dt, src_dentry, !a.issamedir);
++ //wh_dentry = ERR_PTR(-1);
++ err = PTR_ERR(wh_dentry);
++ if (IS_ERR(wh_dentry))
++ goto out;
++
++ a.inode = src_dentry->d_inode;
++ a.hidden_dentry = au_h_dptr(dentry);
++ hidden_parent = a.hidden_dentry->d_parent;
++ a.hidden_dir = hidden_parent->d_inode;
++ IMustLock(a.hidden_dir);
++
++ err = 0;
++ sb = dentry->d_sb;
++ a.dlgt = need_dlgt(sb);
++
++ //todo: minor optimize, their sb may be same while their bindex differs.
++ a.bsrc = dbstart(src_dentry);
++ a.bdst = dbstart(dentry);
++ hidden_src_dentry = au_h_dptr(src_dentry);
++ if (unlikely(!au_flag_test(sb, AuFlag_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
++ /* && hidden_src_dentry->d_sb != a.hidden_dentry->d_sb */)
++ err = cpup_before_link(src_dentry, dir, &a);
++ if (!err) {
++ hidden_src_dentry = au_h_dptr(src_dentry);
++ err = vfsub_link(hidden_src_dentry, a.hidden_dir,
++ a.hidden_dentry, a.dlgt);
++ //err = -1;
++ }
++ } else {
++ if (a.bdst < a.bsrc
++ /* && hidden_src_dentry->d_sb != a.hidden_dentry->d_sb */)
++ err = cpup_or_link(src_dentry, &a);
++ else {
++ hidden_src_dentry = au_h_dptr(src_dentry);
++ err = vfsub_link(hidden_src_dentry, a.hidden_dir,
++ a.hidden_dentry, a.dlgt);
++ //err = -1;
++ }
++ }
++ if (unlikely(err))
++ goto out_unlock;
++ if (wh_dentry) {
++ err = au_unlink_wh_dentry(a.hidden_dir, wh_dentry, dentry,
++ a.dlgt);
++ //err = -1;
++ if (unlikely(err))
++ goto out_revert;
++ }
++
++ dir->i_version++;
++ if (ibstart(dir) == dbstart(dentry))
++ au_cpup_attr_timesizes(dir);
++ if (!d_unhashed(a.hidden_dentry)
++ /* || hidden_old_inode->i_nlink <= nlink */
++ /* || SB_NFS(hidden_src_dentry->d_sb) */) {
++ dentry->d_inode = igrab(a.inode);
++ d_instantiate(dentry, a.inode);
++ a.inode->i_nlink++;
++ a.inode->i_ctime = dir->i_ctime;
++ } else
++ /* nfs case (< 2.6.15) */
++ d_drop(dentry);
++#if 0
++ au_debug_on();
++ DbgInode(a.inode);
++ au_debug_off();
++ {
++ aufs_bindex_t i;
++ for (i = ibstart(a.inode); i <= ibend(a.inode); i++) {
++ struct xino xino;
++ struct inode *hi;
++ hi = au_h_iptr_i(a.inode, i);
++ if (hi) {
++ xino_read(sb, i, hi->i_ino, &xino);
++ Dbg("hi%lu, i%lu\n", hi->i_ino, xino.ino);
++ }
++ }
++ }
++#endif
++ goto out_unlock; /* success */
++
++ out_revert:
++#if 0 // remove
++ if (d_unhashed(a.hidden_dentry)) {
++ /* hardlink on nfs (< 2.6.15) */
++ struct dentry *d;
++ const struct qstr *name = &a.hidden_dentry->d_name;
++ DEBUG_ON(a.hidden_dentry->d_parent->d_inode != a.hidden_dir);
++ // do not superio.
++ d = lkup_one(name->name, a.hidden_dentry->d_parent, name->len,
++ au_nfsmnt(sb, a.bdst)??, need_dlgt(sb));
++ rerr = PTR_ERR(d);
++ if (IS_ERR(d))
++ goto out_rerr;
++ dput(a.hidden_dentry);
++ a.hidden_dentry = d;
++ DEBUG_ON(!d->d_inode);
++ }
++#endif
++ rerr = vfsub_unlink(a.hidden_dir, a.hidden_dentry, a.dlgt);
++ //rerr = -1;
++ if (!rerr)
++ goto out_dt;
++// out_rerr:
++ IOErr("%.*s reverting failed(%d, %d)\n", DLNPair(dentry), err, rerr);
++ err = -EIO;
++ out_dt:
++ d_drop(dentry);
++ dtime_revert(&dt, !CPUP_LOCKED_GHDIR);
++ out_unlock:
++ hdir_unlock(a.hidden_dir, dir, a.bdst);
++ dput(wh_dentry);
++ out:
++ if (unlikely(err)) {
++ au_update_dbstart(dentry);
++ d_drop(dentry);
++ }
++ di_write_unlock(a.parent);
++ aufs_read_and_write_unlock2(dentry, src_dentry);
++ TraceErr(err);
++ return err;
++}
++
++int aufs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
++{
++ int err, rerr, diropq, dlgt;
++ struct dentry *hidden_dentry, *hidden_parent, *wh_dentry, *parent,
++ *opq_dentry;
++ struct inode *hidden_dir, *hidden_inode;
++ struct dtime dt;
++ aufs_bindex_t bindex;
++ struct super_block *sb;
++
++ LKTRTrace("i%lu, %.*s, mode 0%o\n", dir->i_ino, DLNPair(dentry), mode);
++ IMustLock(dir);
++
++ aufs_read_lock(dentry, AUFS_D_WLOCK);
++ parent = dentry->d_parent;
++ di_write_lock_parent(parent);
++ wh_dentry = lock_hdir_lkup_wh(dentry, &dt, /*src_dentry*/NULL,
++ /*do_lock_srcdir*/0);
++ //wh_dentry = ERR_PTR(-1);
++ err = PTR_ERR(wh_dentry);
++ if (IS_ERR(wh_dentry))
++ goto out;
++
++ sb = dentry->d_sb;
++ bindex = dbstart(dentry);
++ hidden_dentry = au_h_dptr(dentry);
++ hidden_parent = hidden_dentry->d_parent;
++ hidden_dir = hidden_parent->d_inode;
++ IMustLock(hidden_dir);
++ dlgt = need_dlgt(sb);
++
++ err = vfsub_mkdir(hidden_dir, hidden_dentry, mode, dlgt);
++ //err = -1;
++ if (unlikely(err))
++ goto out_unlock;
++ hidden_inode = hidden_dentry->d_inode;
++
++ /* make the dir opaque */
++ diropq = 0;
++ if (unlikely(wh_dentry || au_flag_test(sb, AuFlag_ALWAYS_DIROPQ))) {
++ hi_lock_child(hidden_inode);
++ opq_dentry = create_diropq(dentry, bindex, dlgt);
++ //opq_dentry = ERR_PTR(-1);
++ i_unlock(hidden_inode);
++ err = PTR_ERR(opq_dentry);
++ if (IS_ERR(opq_dentry))
++ goto out_dir;
++ dput(opq_dentry);
++ diropq = 1;
++ }
++
++ err = epilog(wh_dentry, dentry);
++ //err = -1;
++ if (!err) {
++ dir->i_nlink++;
++ goto out_unlock; /* success */
++ }
++
++ /* revert */
++ if (unlikely(diropq)) {
++ LKTRLabel(revert opq);
++ hi_lock_child(hidden_inode);
++ rerr = remove_diropq(dentry, bindex, dlgt);
++ //rerr = -1;
++ i_unlock(hidden_inode);
++ if (rerr) {
++ IOErr("%.*s reverting diropq failed(%d, %d)\n",
++ DLNPair(dentry), err, rerr);
++ err = -EIO;
++ }
++ }
++
++ out_dir:
++ LKTRLabel(revert dir);
++ rerr = vfsub_rmdir(hidden_dir, hidden_dentry, dlgt);
++ //rerr = -1;
++ if (rerr) {
++ IOErr("%.*s reverting dir failed(%d, %d)\n",
++ DLNPair(dentry), err, rerr);
++ err = -EIO;
++ }
++ d_drop(dentry);
++ dtime_revert(&dt, /*fake flag*/CPUP_LOCKED_GHDIR);
++ out_unlock:
++ hdir_unlock(hidden_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, AUFS_D_WLOCK);
++ TraceErr(err);
++ return err;
++}
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/i_op_del.c linux-2.6.22.1/fs/aufs/i_op_del.c
+--- linux-2.6.22.1.oorig/fs/aufs/i_op_del.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/i_op_del.c 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,414 @@
++/*
++ * 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: i_op_del.c,v 1.35 2007/05/14 03:41:52 sfjro Exp $ */
++
++#include "aufs.h"
++
++/* returns,
++ * 0: wh is unnecessary
++ * plus: wh is necessary
++ * minus: error
++ */
++int 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 *hidden_dentry;
++ struct super_block *sb;
++
++ LKTRTrace("%.*s, isdir %d, *bcpup %d, locked %p\n",
++ DLNPair(dentry), isdir, *bcpup, locked);
++ sb = dentry->d_sb;
++
++ bstart = dbstart(dentry);
++ LKTRTrace("bcpup %d, bstart %d\n", *bcpup, bstart);
++ hidden_dentry = au_h_dptr(dentry);
++ if (*bcpup < 0) {
++ *bcpup = bstart;
++ if (test_ro(sb, bstart, dentry->d_inode)) {
++ *bcpup = err = find_rw_parent_br(dentry, bstart);
++ //*bcpup = err = find_rw_br(sb, bstart);
++ //err = -1;
++ if (unlikely(err < 0))
++ goto out;
++ }
++ } else {
++ /* braces are added to stop a warning */
++ DEBUG_ON(bstart < *bcpup
++ || test_ro(sb, *bcpup, dentry->d_inode));
++ }
++ LKTRTrace("bcpup %d, bstart %d\n", *bcpup, bstart);
++
++ if (*bcpup != bstart) {
++ err = 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 = dbend(dentry);
++ if (isdir) {
++ bdiropq = dbdiropq(dentry);
++ set_dbdiropq(dentry, -1);
++ }
++ err = need_wh = lkup_dentry(dentry, bstart + 1, /*type*/0);
++ //err = -1;
++ if (isdir)
++ set_dbdiropq(dentry, bdiropq);
++ if (unlikely(err < 0))
++ goto out;
++ new_bend = dbend(dentry);
++ if (!need_wh && old_bend != new_bend) {
++ set_h_dptr(dentry, new_bend, NULL);
++ set_dbend(dentry, old_bend);
++#if 0
++ } else if (!au_h_dptr_i(dentry, new_bend)->d_inode) {
++ LKTRTrace("negative\n");
++ set_h_dptr(dentry, new_bend, NULL);
++ set_dbend(dentry, old_bend);
++ need_wh = 0;
++#endif
++ }
++ }
++ LKTRTrace("need_wh %d\n", need_wh);
++ err = need_wh;
++
++ out:
++ TraceErr(err);
++ return err;
++}
++
++static struct dentry *
++lock_hdir_create_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup,
++ struct dtime *dt)
++{
++ struct dentry *wh_dentry;
++ int err, need_wh;
++ struct dentry *hidden_parent, *parent;
++ struct inode *dir, *h_dir;
++ struct lkup_args lkup;
++
++ LKTRTrace("%.*s, isdir %d\n", DLNPair(dentry), isdir);
++
++ err = need_wh = wr_dir_need_wh(dentry, isdir, bcpup, NULL);
++ //err = -1;
++ wh_dentry = ERR_PTR(err);
++ if (unlikely(err < 0))
++ goto out;
++
++ parent = dentry->d_parent;
++ dir = parent->d_inode;
++ hidden_parent = au_h_dptr_i(parent, *bcpup);
++ h_dir = hidden_parent->d_inode;
++ hdir_lock(h_dir, dir, *bcpup);
++ dtime_store(dt, parent, hidden_parent);
++ if (!need_wh)
++ return NULL; /* success, no need to create whiteout */
++
++ lkup.nfsmnt = au_nfsmnt(dentry->d_sb, *bcpup);
++ lkup.dlgt = need_dlgt(dentry->d_sb);
++ wh_dentry = simple_create_wh(dentry, *bcpup, hidden_parent, &lkup);
++ //wh_dentry = ERR_PTR(-1);
++ if (!IS_ERR(wh_dentry))
++ goto out; /* success */
++ /* returns with the parent is locked and wh_dentry is DGETed */
++
++ hdir_unlock(h_dir, dir, *bcpup);
++
++ out:
++ TraceErrPtr(wh_dentry);
++ return wh_dentry;
++}
++
++static int renwh_and_rmdir(struct dentry *dentry, aufs_bindex_t bindex,
++ struct aufs_nhash *whlist, struct inode *dir)
++{
++ int rmdir_later, err;
++ struct dentry *hidden_dentry;
++
++ LKTRTrace("%.*s, b%d\n", DLNPair(dentry), bindex);
++
++ err = rename_whtmp(dentry, bindex);
++ //err = -1;
++#if 0
++ //todo: bug
++ if (unlikely(err)) {
++ au_direval_inc(dentry->d_parent);
++ return err;
++ }
++#endif
++
++ hidden_dentry = au_h_dptr_i(dentry, bindex);
++ if (!au_is_nfs(hidden_dentry->d_sb)) {
++ const int dirwh = stosi(dentry->d_sb)->si_dirwh;
++ rmdir_later = (dirwh <= 1);
++ if (!rmdir_later)
++ rmdir_later = is_longer_wh(whlist, bindex, dirwh);
++ if (rmdir_later)
++ return rmdir_later;
++ }
++
++ err = rmdir_whtmp(hidden_dentry, whlist, bindex, dir, dentry->d_inode);
++ //err = -1;
++ if (unlikely(err)) {
++ IOErr("rmdir %.*s, b%d failed, %d. ignored\n",
++ DLNPair(hidden_dentry), bindex, err);
++ err = 0;
++ }
++ TraceErr(err);
++ return err;
++}
++
++static void epilog(struct inode *dir, struct dentry *dentry,
++ aufs_bindex_t bindex)
++{
++ d_drop(dentry);
++ dentry->d_inode->i_ctime = dir->i_ctime;
++ if (atomic_read(&dentry->d_count) == 1) {
++ set_h_dptr(dentry, dbstart(dentry), NULL);
++ au_update_dbstart(dentry);
++ }
++ if (ibstart(dir) == bindex)
++ au_cpup_attr_timesizes(dir);
++ dir->i_version++;
++}
++
++static int do_revert(int err, struct dentry *wh_dentry, struct dentry *dentry,
++ aufs_bindex_t bwh, struct dtime *dt, int dlgt)
++{
++ int rerr;
++
++ rerr = au_unlink_wh_dentry(wh_dentry->d_parent->d_inode, wh_dentry,
++ dentry, dlgt);
++ //rerr = -1;
++ if (!rerr) {
++ set_dbwh(dentry, bwh);
++ dtime_revert(dt, !CPUP_LOCKED_GHDIR);
++ return 0;
++ }
++
++ IOErr("%.*s reverting whiteout failed(%d, %d)\n",
++ DLNPair(dentry), err, rerr);
++ return -EIO;
++}
++
++/* ---------------------------------------------------------------------- */
++
++int aufs_unlink(struct inode *dir, struct dentry *dentry)
++{
++ int err, dlgt;
++ struct inode *inode, *hidden_dir;
++ struct dentry *parent, *wh_dentry, *hidden_dentry, *hidden_parent;
++ struct dtime dt;
++ aufs_bindex_t bwh, bindex, bstart;
++ struct super_block *sb;
++
++ LKTRTrace("i%lu, %.*s\n", dir->i_ino, DLNPair(dentry));
++ IMustLock(dir);
++ inode = dentry->d_inode;
++ if (unlikely(!inode))
++ return -ENOENT; // possible?
++ IMustLock(inode);
++
++ aufs_read_lock(dentry, AUFS_D_WLOCK);
++ parent = dentry->d_parent;
++ di_write_lock_parent(parent);
++
++ bstart = dbstart(dentry);
++ bwh = 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 = need_dlgt(sb);
++ hidden_dentry = au_h_dptr(dentry);
++ dget(hidden_dentry);
++ hidden_parent = hidden_dentry->d_parent;
++ hidden_dir = hidden_parent->d_inode;
++
++ if (bindex == bstart) {
++ err = vfsub_unlink(hidden_dir, hidden_dentry, dlgt);
++ //err = -1;
++ } else {
++ DEBUG_ON(!wh_dentry);
++ hidden_parent = wh_dentry->d_parent;
++ DEBUG_ON(hidden_parent != au_h_dptr_i(parent, bindex));
++ hidden_dir = hidden_parent->d_inode;
++ IMustLock(hidden_dir);
++ err = 0;
++ }
++
++ if (!err) {
++ inode->i_nlink--;
++ epilog(dir, dentry, bindex);
++#if 0
++ xino_write0(sb, bstart, hidden_dentry->d_inode->i_ino);
++ /* ignore this error */
++#endif
++ goto out_unlock; /* success */
++ }
++
++ /* revert */
++ if (wh_dentry) {
++ int rerr;
++ rerr = do_revert(err, wh_dentry, dentry, bwh, &dt, dlgt);
++ if (rerr)
++ err = rerr;
++ }
++
++ out_unlock:
++ hdir_unlock(hidden_dir, dir, bindex);
++ dput(wh_dentry);
++ dput(hidden_dentry);
++ out:
++ di_write_unlock(parent);
++ aufs_read_unlock(dentry, AUFS_D_WLOCK);
++ TraceErr(err);
++ return err;
++}
++
++int aufs_rmdir(struct inode *dir, struct dentry *dentry)
++{
++ int err, rmdir_later;
++ struct inode *inode, *hidden_dir;
++ struct dentry *parent, *wh_dentry, *hidden_dentry, *hidden_parent;
++ struct dtime dt;
++ aufs_bindex_t bwh, bindex, bstart;
++ struct rmdir_whtmp_arg *arg;
++ struct aufs_nhash *whlist;
++ struct super_block *sb;
++
++ LKTRTrace("i%lu, %.*s\n", dir->i_ino, DLNPair(dentry));
++ IMustLock(dir);
++ inode = dentry->d_inode;
++ if (unlikely(!inode))
++ return -ENOENT; // possible?
++ IMustLock(inode);
++
++ whlist = nhash_new(GFP_KERNEL);
++ err = PTR_ERR(whlist);
++ if (IS_ERR(whlist))
++ goto out;
++
++ err = -ENOMEM;
++ arg = kmalloc(sizeof(*arg), GFP_KERNEL);
++ //arg = NULL;
++ if (unlikely(!arg))
++ goto out_whlist;
++
++ aufs_read_lock(dentry, AUFS_D_WLOCK);
++ parent = dentry->d_parent;
++ di_write_lock_parent(parent);
++ err = test_empty(dentry, whlist);
++ //err = -1;
++ if (unlikely(err))
++ goto out_arg;
++
++ bstart = dbstart(dentry);
++ bwh = 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_arg;
++
++ hidden_dentry = au_h_dptr(dentry);
++ dget(hidden_dentry);
++ hidden_parent = hidden_dentry->d_parent;
++ hidden_dir = hidden_parent->d_inode;
++
++ rmdir_later = 0;
++ if (bindex == bstart) {
++ IMustLock(hidden_dir);
++ err = renwh_and_rmdir(dentry, bstart, whlist, dir);
++ //err = -1;
++ if (err > 0) {
++ rmdir_later = err;
++ err = 0;
++ }
++ } else {
++ DEBUG_ON(!wh_dentry);
++ hidden_parent = wh_dentry->d_parent;
++ DEBUG_ON(hidden_parent != au_h_dptr_i(parent, bindex));
++ hidden_dir = hidden_parent->d_inode;
++ IMustLock(hidden_dir);
++ err = 0;
++ }
++
++ sb = dentry->d_sb;
++ if (!err) {
++ //aufs_bindex_t bi, bend;
++
++ au_reset_hinotify(inode, /*flags*/0);
++ inode->i_nlink = 0;
++ set_dbdiropq(dentry, -1);
++ epilog(dir, dentry, bindex);
++
++ if (rmdir_later) {
++ kick_rmdir_whtmp(hidden_dentry, whlist, bstart, dir,
++ inode, arg);
++ arg = NULL;
++ }
++
++#if 0
++ bend = dbend(dentry);
++ for (bi = bstart; bi <= bend; bi++) {
++ struct dentry *hd;
++ hd = au_h_dptr_i(dentry, bi);
++ if (hd && hd->d_inode)
++ xino_write0(sb, bi, hd->d_inode->i_ino);
++ /* ignore this error */
++ }
++#endif
++
++ goto out_unlock; /* success */
++ }
++
++ /* revert */
++ LKTRLabel(revert);
++ if (wh_dentry) {
++ int rerr;
++ rerr = do_revert(err, wh_dentry, dentry, bwh, &dt,
++ need_dlgt(sb));
++ if (rerr)
++ err = rerr;
++ }
++
++ out_unlock:
++ hdir_unlock(hidden_dir, dir, bindex);
++ dput(wh_dentry);
++ dput(hidden_dentry);
++ out_arg:
++ di_write_unlock(parent);
++ aufs_read_unlock(dentry, AUFS_D_WLOCK);
++ kfree(arg);
++ out_whlist:
++ nhash_del(whlist);
++ out:
++ TraceErr(err);
++ return err;
++}
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/i_op_ren.c linux-2.6.22.1/fs/aufs/i_op_ren.c
+--- linux-2.6.22.1.oorig/fs/aufs/i_op_ren.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/i_op_ren.c 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,637 @@
++/*
++ * 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: i_op_ren.c,v 1.39 2007/05/14 03:41:52 sfjro Exp $ */
++
++//#include <linux/fs.h>
++//#include <linux/namei.h>
++#include "aufs.h"
++
++enum {SRC, DST};
++struct rename_args {
++ struct dentry *hidden_dentry[2], *parent[2], *hidden_parent[2];
++ struct aufs_nhash whlist;
++ aufs_bindex_t btgt, bstart[2];
++ struct super_block *sb;
++
++ unsigned int isdir:1;
++ unsigned int issamedir:1;
++ unsigned int whsrc:1;
++ unsigned int whdst:1;
++ unsigned int dlgt:1;
++} __attribute__((aligned(sizeof(long))));
++
++static 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 rmdir_whtmp_arg *tharg;
++ struct dentry *wh_dentry[2], *hidden_dst, *hg_parent;
++ struct inode *hidden_dir[2];
++ aufs_bindex_t bindex, bend;
++ unsigned int flags;
++ struct lkup_args lkup = {.dlgt = a->dlgt};
++
++ LKTRTrace("%.*s/%.*s, %.*s/%.*s, "
++ "hd{%p, %p}, hp{%p, %p}, wh %p, btgt %d, bstart{%d, %d}, "
++ "flags{%d, %d, %d, %d}\n",
++ DLNPair(a->parent[SRC]), DLNPair(src_dentry),
++ DLNPair(a->parent[DST]), DLNPair(dentry),
++ a->hidden_dentry[SRC], a->hidden_dentry[DST],
++ a->hidden_parent[SRC], a->hidden_parent[DST],
++ &a->whlist, a->btgt,
++ a->bstart[SRC], a->bstart[DST],
++ a->isdir, a->issamedir, a->whsrc, a->whdst);
++ hidden_dir[SRC] = a->hidden_parent[SRC]->d_inode;
++ hidden_dir[DST] = a->hidden_parent[DST]->d_inode;
++ IMustLock(hidden_dir[SRC]);
++ IMustLock(hidden_dir[DST]);
++
++ /* prepare workqueue arg */
++ hidden_dst = NULL;
++ tharg = NULL;
++ if (a->isdir && a->hidden_dentry[DST]->d_inode) {
++ err = -ENOMEM;
++ tharg = kmalloc(sizeof(*tharg), GFP_KERNEL);
++ //tharg = NULL;
++ if (unlikely(!tharg))
++ goto out;
++ hidden_dst = dget(a->hidden_dentry[DST]);
++ }
++
++ wh_dentry[SRC] = wh_dentry[DST] = NULL;
++ lkup.nfsmnt = au_nfsmnt(a->sb, a->btgt);
++ /* create whiteout for src_dentry */
++ if (a->whsrc) {
++ wh_dentry[SRC] = simple_create_wh(src_dentry, a->btgt,
++ a->hidden_parent[SRC], &lkup);
++ //wh_dentry[SRC] = ERR_PTR(-1);
++ err = PTR_ERR(wh_dentry[SRC]);
++ if (IS_ERR(wh_dentry[SRC]))
++ goto out_tharg;
++ }
++
++ /* lookup whiteout for dentry */
++ if (a->whdst) {
++ struct dentry *d;
++ d = lkup_wh(a->hidden_parent[DST], &dentry->d_name, &lkup);
++ //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 (tharg) {
++ err = rename_whtmp(dentry, a->btgt);
++ //err = -1;
++ if (unlikely(err))
++ goto out_whdst;
++ set_h_dptr(dentry, a->btgt, NULL);
++ err = lkup_neg(dentry, a->btgt);
++ //err = -1;
++ if (unlikely(err))
++ goto out_whtmp;
++ a->hidden_dentry[DST] = au_h_dptr_i(dentry, a->btgt);
++ }
++
++ /* cpup src */
++ if (a->hidden_dentry[DST]->d_inode && a->bstart[SRC] != a->btgt) {
++ flags = au_flags_cpup(!CPUP_DTIME, a->parent[SRC]);
++ hg_parent = a->hidden_parent[SRC]->d_parent;
++ if (!(flags & CPUP_LOCKED_GHDIR)
++ && hg_parent == a->hidden_parent[DST])
++ flags |= CPUP_LOCKED_GHDIR;
++
++ hi_lock_child(a->hidden_dentry[SRC]->d_inode);
++ err = sio_cpup_simple(src_dentry, a->btgt, -1, flags);
++ //err = -1; // untested dir
++ i_unlock(a->hidden_dentry[SRC]->d_inode);
++ if (unlikely(err))
++ goto out_whtmp;
++ }
++
++#if 0
++ /* clear the target ino in xino */
++ LKTRTrace("dir %d, dst inode %p\n", a->isdir, a->hidden_dentry[DST]->d_inode);
++ if (a->isdir && a->hidden_dentry[DST]->d_inode) {
++ Dbg("here\n");
++ err = xino_write(a->sb, a->btgt,
++ a->hidden_dentry[DST]->d_inode->i_ino, 0);
++ if (unlikely(err))
++ goto out_whtmp;
++ }
++#endif
++
++ /* rename by vfs_rename or cpup */
++ need_diropq = a->isdir
++ && (wh_dentry[DST]
++ || dbdiropq(dentry) == a->btgt
++ || au_flag_test(a->sb, AuFlag_ALWAYS_DIROPQ));
++ bycpup = 0;
++ if (dbstart(src_dentry) == a->btgt) {
++ if (need_diropq && dbdiropq(src_dentry) == a->btgt)
++ need_diropq = 0;
++ err = vfsub_rename(hidden_dir[SRC], au_h_dptr(src_dentry),
++ hidden_dir[DST], a->hidden_dentry[DST],
++ a->dlgt);
++ //err = -1;
++ } else {
++ bycpup = 1;
++ flags = au_flags_cpup(!CPUP_DTIME, a->parent[DST]);
++ hg_parent = a->hidden_parent[DST]->d_parent;
++ if (!(flags & CPUP_LOCKED_GHDIR)
++ && hg_parent == a->hidden_parent[SRC])
++ flags |= CPUP_LOCKED_GHDIR;
++
++ hi_lock_child(a->hidden_dentry[SRC]->d_inode);
++ set_dbstart(src_dentry, a->btgt);
++ set_h_dptr(src_dentry, a->btgt, dget(a->hidden_dentry[DST]));
++ //DbgDentry(src_dentry);
++ //DbgInode(src_dentry->d_inode);
++ err = sio_cpup_single(src_dentry, a->btgt, a->bstart[SRC], -1,
++ flags);
++ //err = -1; // untested dir
++ if (unlikely(err)) {
++ set_h_dptr(src_dentry, a->btgt, NULL);
++ set_dbstart(src_dentry, a->bstart[SRC]);
++ }
++ i_unlock(a->hidden_dentry[SRC]->d_inode);
++ }
++ if (unlikely(err))
++ goto out_whtmp;
++
++ /* make dir opaque */
++ if (need_diropq) {
++ struct dentry *diropq;
++ struct inode *h_inode;
++
++ h_inode = au_h_dptr_i(src_dentry, a->btgt)->d_inode;
++ hdir_lock(h_inode, src_dentry->d_inode, a->btgt);
++ diropq = create_diropq(src_dentry, a->btgt, a->dlgt);
++ //diropq = ERR_PTR(-1);
++ hdir_unlock(h_inode, src_dentry->d_inode, a->btgt);
++ err = PTR_ERR(diropq);
++ if (IS_ERR(diropq))
++ goto out_rename;
++ dput(diropq);
++ }
++
++ /* remove whiteout for dentry */
++ if (wh_dentry[DST]) {
++ err = au_unlink_wh_dentry(hidden_dir[DST], wh_dentry[DST],
++ dentry, a->dlgt);
++ //err = -1;
++ if (unlikely(err))
++ goto out_diropq;
++ }
++
++ /* remove whtmp */
++ if (tharg) {
++ if (au_is_nfs(hidden_dst->d_sb)
++ || !is_longer_wh(&a->whlist, a->btgt,
++ stosi(a->sb)->si_dirwh)) {
++ err = rmdir_whtmp(hidden_dst, &a->whlist, a->btgt, dir,
++ dentry->d_inode);
++ if (unlikely(err))
++ Warn("failed removing whtmp dir %.*s (%d), "
++ "ignored.\n", DLNPair(hidden_dst), err);
++ } else {
++ kick_rmdir_whtmp(hidden_dst, &a->whlist, a->btgt, dir,
++ dentry->d_inode, tharg);
++ dput(hidden_dst);
++ tharg = NULL;
++ }
++ }
++ err = 0;
++ goto out_success;
++
++#define RevertFailure(fmt, args...) do { \
++ IOErrWhck("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_i(src_dentry, a->btgt)->d_inode;
++ // i_lock simplly since inotify is not set to h_inode.
++ hi_lock_parent(h_inode);
++ //hdir_lock(h_inode, src_dentry->d_inode, a->btgt);
++ rerr = remove_diropq(src_dentry, a->btgt, a->dlgt);
++ //rerr = -1;
++ //hdir_unlock(h_inode, src_dentry->d_inode, a->btgt);
++ i_unlock(h_inode);
++ if (rerr)
++ RevertFailure("remove diropq %.*s",
++ DLNPair(src_dentry));
++ }
++ out_rename:
++ if (!bycpup) {
++ struct dentry *d;
++ struct qstr *name = &src_dentry->d_name;
++ d = lkup_one(name->name, a->hidden_parent[SRC], name->len,
++ &lkup);
++ //d = ERR_PTR(-1);
++ rerr = PTR_ERR(d);
++ if (IS_ERR(d)) {
++ RevertFailure("lkup_one %.*s", DLNPair(src_dentry));
++ goto out_whtmp;
++ }
++ DEBUG_ON(d->d_inode);
++ rerr = vfsub_rename
++ (hidden_dir[DST], au_h_dptr_i(src_dentry, a->btgt),
++ hidden_dir[SRC], d, a->dlgt);
++ //rerr = -1;
++ d_drop(d);
++ dput(d);
++ //set_h_dptr(src_dentry, a->btgt, NULL);
++ if (rerr)
++ RevertFailure("rename %.*s", DLNPair(src_dentry));
++ } else {
++ rerr = vfsub_unlink(hidden_dir[DST], a->hidden_dentry[DST],
++ a->dlgt);
++ //rerr = -1;
++ set_h_dptr(src_dentry, a->btgt, NULL);
++ set_dbstart(src_dentry, a->bstart[SRC]);
++ if (rerr)
++ RevertFailure("unlink %.*s",
++ DLNPair(a->hidden_dentry[DST]));
++ }
++ out_whtmp:
++ if (tharg) {
++ struct dentry *d;
++ struct qstr *name = &dentry->d_name;
++ LKTRLabel(here);
++ d = lkup_one(name->name, a->hidden_parent[DST], name->len,
++ &lkup);
++ //d = ERR_PTR(-1);
++ rerr = PTR_ERR(d);
++ if (IS_ERR(d)) {
++ RevertFailure("lookup %.*s", LNPair(name));
++ goto out_whdst;
++ }
++ if (d->d_inode) {
++ d_drop(d);
++ dput(d);
++ goto out_whdst;
++ }
++ DEBUG_ON(d->d_inode);
++ rerr = vfsub_rename(hidden_dir[DST], hidden_dst,
++ hidden_dir[DST], d, a->dlgt);
++ //rerr = -1;
++ d_drop(d);
++ dput(d);
++ if (rerr) {
++ RevertFailure("rename %.*s", DLNPair(hidden_dst));
++ goto out_whdst;
++ }
++ set_h_dptr(dentry, a->btgt, NULL);
++ set_h_dptr(dentry, a->btgt, dget(hidden_dst));
++ }
++ out_whdst:
++ dput(wh_dentry[DST]);
++ wh_dentry[DST] = NULL;
++ out_whsrc:
++ if (wh_dentry[SRC]) {
++ LKTRLabel(here);
++ rerr = au_unlink_wh_dentry(hidden_dir[SRC], wh_dentry[SRC],
++ src_dentry, a->dlgt);
++ //rerr = -1;
++ if (rerr)
++ RevertFailure("unlink %.*s", DLNPair(wh_dentry[SRC]));
++ }
++#undef RevertFailure
++ d_drop(src_dentry);
++ bend = dbend(src_dentry);
++ for (bindex = dbstart(src_dentry); bindex <= bend; bindex++) {
++ struct dentry *hd;
++ hd = au_h_dptr_i(src_dentry, bindex);
++ if (hd)
++ d_drop(hd);
++ }
++ d_drop(dentry);
++ bend = dbend(dentry);
++ for (bindex = dbstart(dentry); bindex <= bend; bindex++) {
++ struct dentry *hd;
++ hd = au_h_dptr_i(dentry, bindex);
++ if (hd)
++ d_drop(hd);
++ }
++ au_update_dbstart(dentry);
++ if (tharg)
++ d_drop(hidden_dst);
++ out_success:
++ dput(wh_dentry[SRC]);
++ dput(wh_dentry[DST]);
++ out_tharg:
++ if (tharg) {
++ dput(hidden_dst);
++ kfree(tharg);
++ }
++ out:
++ TraceErr(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 aufs_nhash *whlist)
++{
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++
++ return 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", DLNPair(dentry));
++
++ bstart = dbstart(dentry);
++ if (bstart != btgt) {
++ struct aufs_nhash *whlist;
++
++ whlist = nhash_new(GFP_KERNEL);
++ err = PTR_ERR(whlist);
++ if (IS_ERR(whlist))
++ goto out;
++ err = test_empty(dentry, whlist);
++ nhash_del(whlist);
++ goto out;
++ }
++
++ if (bstart == dbtaildir(dentry))
++ return 0; /* success */
++
++ err = au_test_empty_lower(dentry);
++
++ out:
++ if (/* unlikely */(err == -ENOTEMPTY))
++ err = -EXDEV;
++ TraceErr(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;
++ aufs_bindex_t bend, bindex;
++ struct inode *inode, *dirs[2];
++ enum {PARENT, CHILD};
++ /* reduce stack space */
++ struct {
++ struct rename_args a;
++ struct dtime dt[2][2];
++ } *p;
++
++ LKTRTrace("i%lu, %.*s, i%lu, %.*s\n",
++ src_dir->i_ino, DLNPair(src_dentry),
++ dir->i_ino, DLNPair(dentry));
++ IMustLock(src_dir);
++ IMustLock(dir);
++ /* braces are added to stop a warning */
++ if (dentry->d_inode) {
++ IMustLock(dentry->d_inode);
++ }
++
++ err = -ENOMEM;
++ BUILD_BUG_ON(sizeof(*p) > PAGE_SIZE);
++ p = kmalloc(sizeof(*p), GFP_KERNEL);
++ if (unlikely(!p))
++ goto out;
++
++ err = -ENOTDIR;
++ p->a.sb = src_dentry->d_sb;
++ inode = src_dentry->d_inode;
++ p->a.isdir = !!S_ISDIR(inode->i_mode);
++ if (unlikely(p->a.isdir && dentry->d_inode
++ && !S_ISDIR(dentry->d_inode->i_mode)))
++ goto out_free;
++
++ aufs_read_and_write_lock2(dentry, src_dentry, p->a.isdir);
++ p->a.dlgt = !!need_dlgt(p->a.sb);
++ p->a.parent[SRC] = p->a.parent[DST] = dentry->d_parent;
++ p->a.issamedir = (src_dir == dir);
++ if (p->a.issamedir)
++ di_write_lock_parent(p->a.parent[DST]);
++ else {
++ p->a.parent[SRC] = src_dentry->d_parent;
++ di_write_lock2_parent(p->a.parent[SRC], p->a.parent[DST],
++ /*isdir*/1);
++ }
++
++ /* which branch we process */
++ p->a.bstart[DST] = dbstart(dentry);
++ p->a.btgt = err = wr_dir(dentry, 1, src_dentry, /*force_btgt*/-1,
++ /*do_lock_srcdir*/0);
++ if (unlikely(err < 0))
++ goto out_unlock;
++
++ /* are they available to be renamed */
++ err = 0;
++ nhash_init(&p->a.whlist);
++ if (p->a.isdir && dentry->d_inode) {
++ set_dbstart(dentry, p->a.bstart[DST]);
++ err = may_rename_dstdir(dentry, p->a.btgt, &p->a.whlist);
++ set_dbstart(dentry, p->a.btgt);
++ }
++ p->a.hidden_dentry[DST] = au_h_dptr(dentry);
++ if (unlikely(err))
++ goto out_unlock;
++ //todo: minor optimize, their sb may be same while their bindex differs.
++ p->a.bstart[SRC] = dbstart(src_dentry);
++ p->a.hidden_dentry[SRC] = au_h_dptr(src_dentry);
++ if (p->a.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 = wr_dir_need_wh(src_dentry, p->a.isdir, &p->a.btgt,
++ p->a.issamedir ? NULL : p->a.parent[DST]);
++ if (unlikely(err < 0))
++ goto out_children;
++ p->a.whsrc = !!err;
++ p->a.whdst = (p->a.bstart[DST] == p->a.btgt);
++ if (!p->a.whdst) {
++ err = cpup_dirs(dentry, p->a.btgt,
++ p->a.issamedir ? NULL : p->a.parent[SRC]);
++ if (unlikely(err))
++ goto out_children;
++ }
++
++ p->a.hidden_parent[SRC] = au_h_dptr_i(p->a.parent[SRC], p->a.btgt);
++ p->a.hidden_parent[DST] = au_h_dptr_i(p->a.parent[DST], p->a.btgt);
++ dirs[0] = src_dir;
++ dirs[1] = dir;
++ hdir_lock_rename(p->a.hidden_parent, dirs, p->a.btgt, p->a.issamedir);
++
++ /* store timestamps to be revertible */
++ dtime_store(p->dt[PARENT] + SRC, p->a.parent[SRC],
++ p->a.hidden_parent[SRC]);
++ if (!p->a.issamedir)
++ dtime_store(p->dt[PARENT] + DST, p->a.parent[DST],
++ p->a.hidden_parent[DST]);
++ do_dt_dstdir = 0;
++ if (p->a.isdir) {
++ dtime_store(p->dt[CHILD] + SRC, src_dentry,
++ p->a.hidden_dentry[SRC]);
++ if (p->a.hidden_dentry[DST]->d_inode) {
++ do_dt_dstdir = 1;
++ dtime_store(p->dt[CHILD] + DST, dentry,
++ p->a.hidden_dentry[DST]);
++ }
++ }
++
++ err = do_rename(src_dir, src_dentry, dir, dentry, &p->a);
++ if (unlikely(err))
++ goto out_dt;
++ hdir_unlock_rename(p->a.hidden_parent, dirs, p->a.btgt, p->a.issamedir);
++
++ /* update dir attributes */
++ dir->i_version++;
++ if (p->a.isdir)
++ au_cpup_attr_nlink(dir);
++ if (ibstart(dir) == p->a.btgt)
++ au_cpup_attr_timesizes(dir);
++
++ if (!p->a.issamedir) {
++ src_dir->i_version++;
++ if (p->a.isdir)
++ au_cpup_attr_nlink(src_dir);
++ if (ibstart(src_dir) == p->a.btgt)
++ au_cpup_attr_timesizes(src_dir);
++ }
++
++ // is this updating defined in POSIX?
++ if (unlikely(p->a.isdir)) {
++ //i_lock(inode);
++ au_cpup_attr_timesizes(inode);
++ //i_unlock(inode);
++ }
++
++#if 0
++ d_drop(src_dentry);
++#else
++ /* dput/iput all lower dentries */
++ set_dbwh(src_dentry, -1);
++ bend = dbend(src_dentry);
++ for (bindex = p->a.btgt + 1; bindex <= bend; bindex++) {
++ struct dentry *hd;
++ hd = au_h_dptr_i(src_dentry, bindex);
++ if (hd)
++ set_h_dptr(src_dentry, bindex, NULL);
++ }
++ set_dbend(src_dentry, p->a.btgt);
++
++ bend = ibend(inode);
++ for (bindex = p->a.btgt + 1; bindex <= bend; bindex++) {
++ struct inode *hi;
++ hi = au_h_iptr_i(inode, bindex);
++ if (hi)
++ set_h_iptr(inode, bindex, NULL, 0);
++ }
++ set_ibend(inode, p->a.btgt);
++#endif
++
++#if 0
++ //au_debug_on();
++ //DbgDentry(dentry);
++ //DbgInode(dentry->d_inode);
++ //au_debug_off();
++ inode = dentry->d_inode;
++ if (inode) {
++ aufs_bindex_t bindex, bend;
++ struct dentry *hd;
++ bend = dbend(dentry);
++ for (bindex = dbstart(dentry); bindex <= bend; bindex++) {
++ hd = au_h_dptr_i(dentry, bindex);
++ if (hd && hd->d_inode)
++ xino_write0(p->a.sb, bindex, hd->d_inode->i_ino);
++ /* ignore this error */
++ }
++ }
++#endif
++
++ goto out_children; /* success */
++
++ out_dt:
++ dtime_revert(p->dt[PARENT] + SRC,
++ p->a.hidden_parent[SRC]->d_parent
++ == p->a.hidden_parent[DST]);
++ if (!p->a.issamedir)
++ dtime_revert(p->dt[PARENT] + DST,
++ p->a.hidden_parent[DST]->d_parent
++ == p->a.hidden_parent[SRC]);
++ if (p->a.isdir && err != -EIO) {
++ struct dentry *hd;
++
++ hd = p->dt[CHILD][SRC].dt_h_dentry;
++ hi_lock_child(hd->d_inode);
++ dtime_revert(p->dt[CHILD] + SRC, 1);
++ i_unlock(hd->d_inode);
++ if (do_dt_dstdir) {
++ hd = p->dt[CHILD][DST].dt_h_dentry;
++ hi_lock_child(hd->d_inode);
++ dtime_revert(p->dt[CHILD] + DST, 1);
++ i_unlock(hd->d_inode);
++ }
++ }
++ hdir_unlock_rename(p->a.hidden_parent, dirs, p->a.btgt, p->a.issamedir);
++ out_children:
++ nhash_fin(&p->a.whlist);
++ out_unlock:
++ //if (unlikely(err /* && p->a.isdir */)) {
++ if (unlikely(err && p->a.isdir)) {
++ au_update_dbstart(dentry);
++ d_drop(dentry);
++ }
++ if (p->a.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:
++ TraceErr(err);
++ return err;
++}
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/iinfo.c linux-2.6.22.1/fs/aufs/iinfo.c
+--- linux-2.6.22.1.oorig/fs/aufs/iinfo.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/iinfo.c 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,286 @@
++/*
++ * 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: iinfo.c,v 1.31 2007/05/14 03:41:52 sfjro Exp $ */
++
++//#include <linux/mm.h>
++#include "aufs.h"
++
++struct aufs_iinfo *itoii(struct inode *inode)
++{
++ struct aufs_iinfo *iinfo;
++
++ iinfo = &(container_of(inode, struct aufs_icntnr, vfs_inode)->iinfo);
++ /* bad_inode case */
++ if (unlikely(!iinfo->ii_hinode))
++ return NULL;
++ DEBUG_ON(!iinfo->ii_hinode
++ /* || stosi(inode->i_sb)->si_bend < iinfo->ii_bend */
++ || iinfo->ii_bend < iinfo->ii_bstart);
++ return iinfo;
++}
++
++aufs_bindex_t ibstart(struct inode *inode)
++{
++ IiMustAnyLock(inode);
++ return itoii(inode)->ii_bstart;
++}
++
++aufs_bindex_t ibend(struct inode *inode)
++{
++ IiMustAnyLock(inode);
++ return itoii(inode)->ii_bend;
++}
++
++struct aufs_vdir *ivdir(struct inode *inode)
++{
++ IiMustAnyLock(inode);
++ DEBUG_ON(!S_ISDIR(inode->i_mode));
++ return itoii(inode)->ii_vdir;
++}
++
++struct inode *au_h_iptr_i(struct inode *inode, aufs_bindex_t bindex)
++{
++ struct inode *hidden_inode;
++
++ IiMustAnyLock(inode);
++ DEBUG_ON(bindex < 0 || ibend(inode) < bindex);
++ hidden_inode = itoii(inode)->ii_hinode[0 + bindex].hi_inode;
++ DEBUG_ON(hidden_inode && atomic_read(&hidden_inode->i_count) <= 0);
++ return hidden_inode;
++}
++
++struct inode *au_h_iptr(struct inode *inode)
++{
++ return au_h_iptr_i(inode, ibstart(inode));
++}
++
++aufs_bindex_t itoid_index(struct inode *inode, aufs_bindex_t bindex)
++{
++ IiMustAnyLock(inode);
++ DEBUG_ON(bindex < 0
++ || ibend(inode) < bindex
++ || !itoii(inode)->ii_hinode[0 + bindex].hi_inode);
++ return itoii(inode)->ii_hinode[0 + bindex].hi_id;
++}
++
++// hard/soft set
++void set_ibstart(struct inode *inode, aufs_bindex_t bindex)
++{
++ struct aufs_iinfo *iinfo = itoii(inode);
++ struct inode *h_inode;
++
++ IiMustWriteLock(inode);
++ DEBUG_ON(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);
++}
++
++void set_ibend(struct inode *inode, aufs_bindex_t bindex)
++{
++ IiMustWriteLock(inode);
++ DEBUG_ON(sbend(inode->i_sb) < bindex
++ || bindex < ibstart(inode));
++ itoii(inode)->ii_bend = bindex;
++}
++
++void set_ivdir(struct inode *inode, struct aufs_vdir *vdir)
++{
++ IiMustWriteLock(inode);
++ DEBUG_ON(!S_ISDIR(inode->i_mode)
++ || (itoii(inode)->ii_vdir && vdir));
++ itoii(inode)->ii_vdir = vdir;
++}
++
++void aufs_hiput(struct aufs_hinode *hinode)
++{
++ if (unlikely(hinode->hi_notify))
++ do_free_hinotify(hinode);
++ if (hinode->hi_inode)
++ iput(hinode->hi_inode);
++}
++
++unsigned int au_hi_flags(struct inode *inode, int isdir)
++{
++ unsigned int flags;
++ struct super_block *sb = inode->i_sb;
++
++ flags = 0;
++ if (au_flag_test(sb, AuFlag_XINO))
++ flags = AUFS_HI_XINO;
++ if (unlikely(isdir && au_flag_test(sb, AuFlag_UDBA_INOTIFY)))
++ flags |= AUFS_HI_NOTIFY;
++ return flags;
++}
++
++void set_h_iptr(struct inode *inode, aufs_bindex_t bindex,
++ struct inode *h_inode, unsigned int flags)
++{
++ struct aufs_hinode *hinode;
++ struct inode *hi;
++ struct aufs_iinfo *iinfo = itoii(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;
++ DEBUG_ON(bindex < ibstart(inode) || ibend(inode) < bindex
++ || (h_inode && atomic_read(&h_inode->i_count) <= 0)
++ || (h_inode && hi));
++
++ if (hi)
++ aufs_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 = sbr_id(sb, bindex);
++ if (flags & AUFS_HI_XINO) {
++ struct xino xino = {
++ .ino = inode->i_ino,
++ //.h_gen = h_inode->i_generation
++ };
++ //WARN_ON(xino.h_gen == AuXino_INVALID_HGEN);
++ err = xino_write(sb, bindex, h_inode->i_ino, &xino);
++ if (unlikely(err)) {
++ IOErr1("failed xino_write() %d, force noxino\n",
++ err);
++ au_flag_clr(sb, AuFlag_XINO);
++ }
++ }
++ if (flags & AUFS_HI_NOTIFY) {
++ err = alloc_hinotify(hinode, inode, h_inode);
++ if (unlikely(err))
++ IOErr1("alloc_hinotify() %d\n", err);
++ else {
++ /* braces are added to stop a warning */
++ DEBUG_ON(!hinode->hi_notify);
++ }
++ }
++ }
++}
++
++void au_update_iigen(struct inode *inode)
++{
++ //IiMustWriteLock(inode);
++ DEBUG_ON(!inode->i_sb);
++ atomic_set(&itoii(inode)->ii_generation, au_sigen(inode->i_sb));
++}
++
++/* it may be called at remount time, too */
++void au_update_brange(struct inode *inode, int do_put_zero)
++{
++ struct aufs_iinfo *iinfo;
++
++ LKTRTrace("i%lu, %d\n", inode->i_ino, do_put_zero);
++ IiMustWriteLock(inode);
++
++ iinfo = itoii(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)
++ 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_bend = iinfo->ii_bstart = -1;
++ return;
++ }
++
++ iinfo->ii_bend++;
++ while (0 <= --iinfo->ii_bend)
++ if (iinfo->ii_hinode[0 + iinfo->ii_bend].hi_inode)
++ break;
++}
++
++/* ---------------------------------------------------------------------- */
++
++int au_iinfo_init(struct inode *inode)
++{
++ struct aufs_iinfo *iinfo;
++ struct super_block *sb;
++ int nbr, i;
++
++ sb = inode->i_sb;
++ DEBUG_ON(!sb);
++ iinfo = &(container_of(inode, struct aufs_icntnr, vfs_inode)->iinfo);
++ DEBUG_ON(iinfo->ii_hinode);
++ nbr = sbend(sb) + 1;
++ if (unlikely(!nbr))
++ nbr++;
++ 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));
++ rw_init_nolock(&iinfo->ii_rwsem);
++ iinfo->ii_bstart = -1;
++ iinfo->ii_bend = -1;
++ iinfo->ii_vdir = NULL;
++ return 0;
++ }
++ return -ENOMEM;
++}
++
++void au_iinfo_fin(struct inode *inode)
++{
++ struct aufs_iinfo *iinfo;
++
++ iinfo = itoii(inode);
++ /* bad_inode case */
++ if (unlikely(!iinfo))
++ return;
++
++ if (unlikely(iinfo->ii_vdir))
++ free_vdir(iinfo->ii_vdir);
++
++ if (iinfo->ii_bstart >= 0) {
++ aufs_bindex_t bend;
++ struct aufs_hinode *hi;
++ hi = iinfo->ii_hinode + iinfo->ii_bstart;
++ bend = iinfo->ii_bend;
++ while (iinfo->ii_bstart++ <= bend) {
++ if (hi->hi_inode)
++ aufs_hiput(hi);
++ hi++;
++ }
++ //iinfo->ii_bstart = iinfo->ii_bend = -1;
++ }
++
++ kfree(iinfo->ii_hinode);
++ //iinfo->ii_hinode = NULL;
++}
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/inode.c linux-2.6.22.1/fs/aufs/inode.c
+--- linux-2.6.22.1.oorig/fs/aufs/inode.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/inode.c 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,339 @@
++/*
++ * 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: inode.c,v 1.22 2007/05/07 03:44:35 sfjro Exp $ */
++
++#include "aufs.h"
++
++int au_refresh_hinode(struct inode *inode, struct dentry *dentry)
++{
++ int err, new_sz, update, isdir;
++ struct inode *first;
++ struct aufs_hinode *p, *q, tmp;
++ struct super_block *sb;
++ struct aufs_iinfo *iinfo;
++ aufs_bindex_t bindex, bend, new_bindex;
++ unsigned int flags;
++
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++ IiMustWriteLock(inode);
++
++ err = -ENOMEM;
++ sb = dentry->d_sb;
++ bend = sbend(sb);
++ new_sz = sizeof(*iinfo->ii_hinode) * (bend + 1);
++ iinfo = itoii(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;
++ err = 0;
++ update = 0;
++ p = iinfo->ii_hinode + iinfo->ii_bstart;
++ first = p->hi_inode;
++ for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend;
++ bindex++, p++) {
++ if (unlikely(!p->hi_inode))
++ continue;
++
++ new_bindex = find_brindex(sb, p->hi_id);
++ if (new_bindex == bindex)
++ continue;
++ if (new_bindex < 0) {
++ update++;
++ aufs_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--;
++ }
++ }
++
++ isdir = S_ISDIR(inode->i_mode);
++ flags = au_hi_flags(inode, isdir);
++ bend = dbend(dentry);
++ for (bindex = dbstart(dentry); bindex <= bend; bindex++) {
++ struct inode *hi;
++ struct dentry *hd;
++
++ hd = au_h_dptr_i(dentry, bindex);
++ if (!hd || !hd->d_inode)
++ continue;
++
++ if (iinfo->ii_bstart <= bindex && bindex <= iinfo->ii_bend) {
++ hi = au_h_iptr_i(inode, bindex);
++ if (hi) {
++ if (hi == hd->d_inode)
++ continue;
++ //Dbg("here\n");
++ err = -ESTALE;
++ break;
++ }
++ }
++ if (bindex < iinfo->ii_bstart)
++ iinfo->ii_bstart = bindex;
++ if (iinfo->ii_bend < bindex)
++ iinfo->ii_bend = bindex;
++ set_h_iptr(inode, bindex, igrab(hd->d_inode), flags);
++ update++;
++ }
++
++ bend = iinfo->ii_bend;
++ p = iinfo->ii_hinode;
++ for (bindex = 0; bindex <= bend; bindex++, p++)
++ if (p->hi_inode) {
++ iinfo->ii_bstart = bindex;
++ break;
++ }
++ p = iinfo->ii_hinode + bend;
++ for (bindex = bend; bindex > iinfo->ii_bstart; bindex--, p--)
++ if (p->hi_inode) {
++ iinfo->ii_bend = bindex;
++ break;
++ }
++ DEBUG_ON(iinfo->ii_bstart > bend || iinfo->ii_bend < 0);
++
++ if (unlikely(err))
++ goto out;
++
++ if (1 || first != au_h_iptr(inode))
++ au_cpup_attr_all(inode);
++ if (update && isdir)
++ inode->i_version++;
++ au_update_iigen(inode);
++
++ out:
++ //au_debug_on();
++ TraceErr(err);
++ //au_debug_off();
++ return err;
++}
++
++static int set_inode(struct inode *inode, struct dentry *dentry)
++{
++ int err, isdir;
++ struct dentry *hidden_dentry;
++ struct inode *hidden_inode;
++ umode_t mode;
++ aufs_bindex_t bindex, bstart, btail;
++ struct aufs_iinfo *iinfo;
++ unsigned int flags;
++
++ LKTRTrace("i%lu, %.*s\n", inode->i_ino, DLNPair(dentry));
++ DEBUG_ON(!(inode->i_state & I_NEW));
++ IiMustWriteLock(inode);
++ hidden_dentry = au_h_dptr(dentry);
++ DEBUG_ON(!hidden_dentry);
++ hidden_inode = hidden_dentry->d_inode;
++ DEBUG_ON(!hidden_inode);
++
++ err = 0;
++ isdir = 0;
++ bstart = dbstart(dentry);
++ mode = hidden_inode->i_mode;
++ switch (mode & S_IFMT) {
++ case S_IFREG:
++ btail = dbtail(dentry);
++ break;
++ case S_IFDIR:
++ isdir = 1;
++ btail = dbtaildir(dentry);
++ inode->i_op = &aufs_dir_iop;
++ inode->i_fop = &aufs_dir_fop;
++ break;
++ case S_IFLNK:
++ btail = dbtail(dentry);
++ inode->i_op = &aufs_symlink_iop;
++ break;
++ case S_IFBLK:
++ case S_IFCHR:
++ case S_IFIFO:
++ case S_IFSOCK:
++ btail = dbtail(dentry);
++ init_special_inode(inode, mode, hidden_inode->i_rdev);
++ break;
++ default:
++ IOErr("Unknown file type 0%o\n", mode);
++ err = -EIO;
++ goto out;
++ }
++
++ flags = au_hi_flags(inode, isdir);
++ iinfo = itoii(inode);
++ iinfo->ii_bstart = bstart;
++ iinfo->ii_bend = btail;
++ for (bindex = bstart; bindex <= btail; bindex++) {
++ hidden_dentry = au_h_dptr_i(dentry, bindex);
++ if (!hidden_dentry)
++ continue;
++ DEBUG_ON(!hidden_dentry->d_inode);
++ set_h_iptr(inode, bindex, igrab(hidden_dentry->d_inode), flags);
++ }
++ au_cpup_attr_all(inode);
++
++ out:
++ TraceErr(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;
++ //const int udba = !au_flag_test(inode->i_sb, AuFlag_UDBA_NONE);
++
++ LKTRTrace("i%lu, %.*s\n", inode->i_ino, DLNPair(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;
++
++ h_dinode = au_h_dptr(dentry)->d_inode;
++ hi_lock_child(inode); // bad name, this is not a hidden inode.
++ ii_write_lock_new(inode);
++ bend = ibend(inode);
++ for (bindex = ibstart(inode); bindex <= bend; bindex++) {
++ h_inode = au_h_iptr_i(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;
++ }
++ }
++ i_unlock(inode);
++ if (unlikely(err))
++ ii_write_unlock(inode);
++
++ out:
++ TraceErr(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 xino xino;
++
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++ sb = dentry->d_sb;
++ h_dentry = au_h_dptr(dentry);
++ DEBUG_ON(!h_dentry);
++ h_inode = h_dentry->d_inode;
++ DEBUG_ON(!h_inode);
++
++ bstart = dbstart(dentry);
++ h_ino = h_inode->i_ino;
++ err = xino_read(sb, bstart, h_ino, &xino);
++ //err = -1;
++ inode = ERR_PTR(err);
++ if (unlikely(err))
++ goto out;
++ new_ino:
++ if (!xino.ino) {
++ xino.ino = xino_new_ino(sb);
++ if (!xino.ino) {
++ inode = ERR_PTR(-EIO);
++ goto out;
++ }
++ }
++
++ LKTRTrace("i%lu\n", xino.ino);
++ err = -ENOMEM;
++ inode = iget_locked(sb, xino.ino);
++ if (unlikely(!inode))
++ goto out;
++ err = PTR_ERR(inode);
++ if (IS_ERR(inode))
++ goto out;
++ err = -ENOMEM;
++ if (unlikely(is_bad_inode(inode)))
++ goto out_iput;
++
++ LKTRTrace("%lx, new %d\n", inode->i_state, !!(inode->i_state & I_NEW));
++ if (inode->i_state & I_NEW) {
++ sb->s_op->read_inode(inode);
++ if (!is_bad_inode(inode)) {
++ ii_write_lock_new(inode);
++ err = set_inode(inode, dentry);
++ //err = -1;
++ }
++ unlock_new_inode(inode);
++ if (!err)
++ goto out; /* success */
++ ii_write_unlock(inode);
++ goto out_iput;
++ } else {
++ err = reval_inode(inode, dentry, &match);
++ if (!err)
++ goto out; /* success */
++ else if (match)
++ goto out_iput;
++ }
++
++ Warn1("broken ino, b%d, %.*s/%.*s, hi%lu, i%lu. Try udba=inotify.\n",
++ bstart, DLNPair(dentry->d_parent), DLNPair(dentry), h_ino,
++ xino.ino);
++ xino.ino = 0;
++ err = xino_write0(sb, bstart, h_ino);
++ if (!err) {
++ iput(inode);
++ goto new_ino;
++ }
++
++ out_iput:
++ iput(inode);
++ inode = ERR_PTR(err);
++ out:
++ TraceErrPtr(inode);
++ return inode;
++}
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/inode.h linux-2.6.22.1/fs/aufs/inode.h
+--- linux-2.6.22.1.oorig/fs/aufs/inode.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/inode.h 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,377 @@
++/*
++ * 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: inode.h,v 1.32 2007/05/14 03:41:52 sfjro Exp $ */
++
++#ifndef __AUFS_INODE_H__
++#define __AUFS_INODE_H__
++
++#ifdef __KERNEL__
++
++#include <linux/fs.h>
++#include <linux/inotify.h>
++#include <linux/version.h>
++#include <linux/aufs_type.h>
++#include "misc.h"
++#include "vfsub.h"
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
++#else
++struct inotify_watch {};
++#endif
++
++/* ---------------------------------------------------------------------- */
++
++struct aufs_hinotify {
++ struct inotify_watch hin_watch;
++ struct inode *hin_aufs_inode; /* no get/put */
++};
++
++struct aufs_hinode {
++ struct inode *hi_inode;
++ aufs_bindex_t hi_id;
++ struct aufs_hinotify *hi_notify;
++};
++
++struct aufs_vdir;
++struct aufs_iinfo {
++ atomic_t ii_generation;
++ struct super_block *ii_hsb1; /* no get/put */
++
++ struct aufs_rwsem ii_rwsem;
++ aufs_bindex_t ii_bstart, ii_bend;
++ struct aufs_hinode *ii_hinode;
++ struct aufs_vdir *ii_vdir;
++};
++
++struct aufs_icntnr {
++ struct aufs_iinfo iinfo;
++ struct inode vfs_inode;
++};
++
++/* ---------------------------------------------------------------------- */
++
++/* inode.c */
++int au_refresh_hinode(struct inode *inode, struct dentry *dentry);
++struct inode *au_new_inode(struct dentry *dentry);
++
++/* i_op.c */
++extern struct inode_operations aufs_iop, aufs_symlink_iop, aufs_dir_iop;
++int wr_dir(struct dentry *dentry, int negative, struct dentry *src_dentry,
++ aufs_bindex_t force_btgt, int do_lock_srcdir);
++
++/* i_op_del.c */
++int wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup,
++ struct dentry *locked);
++
++/* iinfo.c */
++struct aufs_iinfo *itoii(struct inode *inode);
++aufs_bindex_t ibstart(struct inode *inode);
++aufs_bindex_t ibend(struct inode *inode);
++struct aufs_vdir *ivdir(struct inode *inode);
++struct inode *au_h_iptr_i(struct inode *inode, aufs_bindex_t bindex);
++struct inode *au_h_iptr(struct inode *inode);
++aufs_bindex_t itoid_index(struct inode *inode, aufs_bindex_t bindex);
++
++void set_ibstart(struct inode *inode, aufs_bindex_t bindex);
++void set_ibend(struct inode *inode, aufs_bindex_t bindex);
++void set_ivdir(struct inode *inode, struct aufs_vdir *vdir);
++void aufs_hiput(struct aufs_hinode *hinode);
++#define AUFS_HI_XINO 1
++#define AUFS_HI_NOTIFY 2
++unsigned int au_hi_flags(struct inode *inode, int isdir);
++void 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_list_plink(struct super_block *sb);
++#else
++static inline void au_list_plink(struct super_block *sb)
++{
++ /* nothing */
++}
++#endif
++int au_is_plinked(struct super_block *sb, struct inode *inode);
++struct dentry *lkup_plink(struct super_block *sb, aufs_bindex_t bindex,
++ struct inode *inode);
++void append_plink(struct super_block *sb, struct inode *inode,
++ struct dentry *h_dentry, aufs_bindex_t bindex);
++void au_put_plink(struct super_block *sb);
++void half_refresh_plink(struct super_block *sb, aufs_bindex_t br_id);
++
++/* ---------------------------------------------------------------------- */
++
++/* lock subclass for hidden inode */
++/* default MAX_LOCKDEP_SUBCLASSES(8) is not enough */
++// todo: reduce it by dcsub.
++enum {
++ AuLsc_Begin = I_MUTEX_QUOTA,
++ AuLsc_HI_GPARENT, /* setattr with inotify */
++ AuLsc_HI_PARENT, /* hidden inode, parent first */
++ AuLsc_HI_CHILD,
++ AuLsc_HI_PARENT2, /* copyup dirs */
++ AuLsc_HI_CHILD2,
++ AuLsc_End
++};
++
++/* simple abstraction */
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
++static inline void i_lock(struct inode *i)
++{
++ down(&i->i_sem);
++}
++
++static inline void i_unlock(struct inode *i)
++{
++ up(&i->i_sem);
++}
++
++static inline int i_trylock(struct inode *i)
++{
++ return down_trylock(&i->i_sem);
++}
++
++static inline void hi_lock(struct inode *i, unsigned int lsc)
++{
++ i_lock(i);
++}
++
++#define IMustLock(i) DEBUG_ON(!down_trylock(&(i)->i_sem))
++#else
++static inline void i_lock(struct inode *i)
++{
++ mutex_lock(&i->i_mutex);
++}
++
++static inline void i_unlock(struct inode *i)
++{
++ mutex_unlock(&i->i_mutex);
++}
++
++static inline int i_trylock(struct inode *i)
++{
++ return mutex_trylock(&i->i_mutex);
++}
++
++static inline void hi_lock(struct inode *i, unsigned int lsc)
++{
++ mutex_lock_nested(&i->i_mutex, lsc);
++}
++
++#define IMustLock(i) MtxMustLock(&(i)->i_mutex)
++#endif
++
++/*
++ * hi_lock_gparent, hi_lock_parent, hi_lock_parent2, hi_lock_child,
++ * hi_lock_child2, hi_lock_whplink
++ */
++#define LockFunc(name, lsc) \
++static inline void hi_lock_##name(struct inode *h_i) \
++{hi_lock(h_i, AuLsc_HI_##lsc);}
++
++LockFunc(gparent, GPARENT);
++LockFunc(parent, PARENT);
++LockFunc(parent2, PARENT2);
++LockFunc(child, CHILD);
++LockFunc(child2, CHILD2);
++LockFunc(whplink, CHILD2); /* sharing lock-subclass */
++
++#undef LockFunc
++
++/* ---------------------------------------------------------------------- */
++
++/* 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 !(itoii(inode)->ii_hsb1 == h_inode->i_sb
++ && inode->i_generation == h_inode->i_generation);
++}
++
++static inline int au_iigen(struct inode *inode)
++{
++ return atomic_read(&itoii(inode)->ii_generation);
++}
++
++#ifdef CONFIG_AUFS_HINOTIFY
++static inline void au_iigen_dec(struct inode *inode)
++{
++ //Dbg("i%lu\n", inode->i_ino);
++ atomic_dec(&itoii(inode)->ii_generation);
++}
++
++/* hinotify.c */
++int alloc_hinotify(struct aufs_hinode *hinode, struct inode *inode,
++ struct inode *h_inode);
++void do_free_hinotify(struct aufs_hinode *hinode);
++void do_hdir_lock(struct inode *h_dir, struct inode *dir, aufs_bindex_t bindex,
++ unsigned int lsc);
++void hdir_unlock(struct inode *h_dir, struct inode *dir, aufs_bindex_t bindex);
++void hdir_lock_rename(struct dentry **h_parents, struct inode **dirs,
++ aufs_bindex_t bindex, int issamedir);
++void 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);
++int __init au_inotify_init(void);
++void au_inotify_fin(void);
++#else
++static inline
++int alloc_hinotify(struct aufs_hinode *hinode, struct inode *inode,
++ struct inode *h_inode)
++{
++ return -EOPNOTSUPP;
++}
++
++static inline void do_free_hinotify(struct aufs_hinode *hinode)
++{
++ /* nothing */
++}
++
++static inline
++void do_hdir_lock(struct inode *h_dir, struct inode *dir, aufs_bindex_t bindex,
++ unsigned int lsc)
++{
++ hi_lock(h_dir, lsc);
++}
++
++static inline
++void hdir_unlock(struct inode *h_dir, struct inode *dir, aufs_bindex_t bindex)
++{
++ i_unlock(h_dir);
++}
++
++static inline
++void hdir_lock_rename(struct dentry **h_parents, struct inode **dirs,
++ aufs_bindex_t bindex, int issamedir)
++{
++ vfsub_lock_rename(h_parents[0], h_parents[1]);
++}
++
++static inline
++void 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 */
++}
++
++#define au_inotify_init() 0
++#define au_inotify_fin() /* */
++#endif /* CONFIG_AUFS_HINOTIFY */
++
++static inline void free_hinotify(struct inode *inode, aufs_bindex_t bindex)
++{
++ do_free_hinotify(itoii(inode)->ii_hinode + bindex);
++}
++
++/*
++ * hgdir_lock, hdir_lock, hdir2_lock
++ */
++#define LockFunc(name, lsc) \
++static inline \
++void name##_lock(struct inode *h_dir, struct inode *dir, aufs_bindex_t bindex) \
++{do_hdir_lock(h_dir, dir, bindex, AuLsc_HI_##lsc);}
++
++LockFunc(hgdir, GPARENT);
++LockFunc(hdir, PARENT);
++LockFunc(hdir2, PARENT2);
++
++#undef LockFunc
++
++/* ---------------------------------------------------------------------- */
++
++/* 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_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_new, ii_write_lock_new
++ */
++#define ReadLockFunc(name, lsc) \
++static inline void ii_read_lock_##name(struct inode *i) \
++{rw_read_lock_nested(&itoii(i)->ii_rwsem, AuLsc_II_##lsc);}
++
++#define WriteLockFunc(name, lsc) \
++static inline void ii_write_lock_##name(struct inode *i) \
++{rw_write_lock_nested(&itoii(i)->ii_rwsem, AuLsc_II_##lsc);}
++
++#define RWLockFuncs(name, lsc) \
++ ReadLockFunc(name, lsc); \
++ WriteLockFunc(name, lsc)
++
++RWLockFuncs(child, CHILD);
++RWLockFuncs(child2, CHILD2);
++RWLockFuncs(child3, CHILD3);
++RWLockFuncs(parent, PARENT);
++RWLockFuncs(parent2, PARENT2);
++RWLockFuncs(parent3, PARENT3);
++RWLockFuncs(new, NEW);
++
++#undef ReadLockFunc
++#undef WriteLockFunc
++#undef RWLockFunc
++
++/*
++ * ii_read_unlock, ii_write_unlock, ii_downgrade_lock
++ */
++SimpleUnlockRwsemFuncs(ii, struct inode *i, itoii(i)->ii_rwsem);
++
++/* to debug easier, do not make them inlined functions */
++#define IiMustReadLock(i) do { \
++ SiMustAnyLock((i)->i_sb); \
++ RwMustReadLock(&itoii(i)->ii_rwsem); \
++} while (0)
++
++#define IiMustWriteLock(i) do { \
++ SiMustAnyLock((i)->i_sb); \
++ RwMustWriteLock(&itoii(i)->ii_rwsem); \
++} while (0)
++
++#define IiMustAnyLock(i) do { \
++ SiMustAnyLock((i)->i_sb); \
++ RwMustAnyLock(&itoii(i)->ii_rwsem); \
++} while (0)
++
++#define IiMustNoWaiters(i) RwMustNoWaiters(&itoii(i)->ii_rwsem)
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_INODE_H__ */
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/misc.c linux-2.6.22.1/fs/aufs/misc.c
+--- linux-2.6.22.1.oorig/fs/aufs/misc.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/misc.c 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,228 @@
++/*
++ * 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: misc.c,v 1.31 2007/05/14 03:41:52 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, ksize %d\n",
++ p, nused, new_sz, ksize(p));
++ DEBUG_ON(new_sz <= 0);
++ if (new_sz <= nused)
++ return p;
++ if (new_sz <= ksize(p)) {
++ memset(p + nused, 0, new_sz - nused);
++ return p;
++ }
++
++ q = kmalloc(new_sz, gfp);
++ //q = NULL;
++ if (unlikely(!q))
++ return NULL;
++ memcpy(q, p, nused);
++ memset(q + nused, 0, new_sz - nused);
++ //smp_mb();
++ kfree(p);
++ return q;
++}
++
++/* ---------------------------------------------------------------------- */
++
++// todo: make it inline
++struct nameidata *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->dentry = NULL;
++ fake_nd->mnt = NULL;
++
++#ifndef CONFIG_AUFS_FAKE_DM
++ DiMustAnyLock(nd->dentry);
++
++ if (bindex <= dbend(nd->dentry))
++ fake_nd->dentry = au_h_dptr_i(nd->dentry, bindex);
++ if (fake_nd->dentry) {
++ dget(fake_nd->dentry);
++ fake_nd->mnt = sbr_mnt(sb, bindex);
++ DEBUG_ON(!fake_nd->mnt);
++ mntget(fake_nd->mnt);
++ } else
++ fake_nd = ERR_PTR(-ENOENT);
++#endif
++
++ TraceErrPtr(fake_nd);
++ return fake_nd;
++}
++
++void fake_dm_release(struct nameidata *fake_nd)
++{
++#ifndef CONFIG_AUFS_FAKE_DM
++ if (fake_nd) {
++ mntput(fake_nd->mnt);
++ dput(fake_nd->dentry);
++ }
++#endif
++}
++
++/* ---------------------------------------------------------------------- */
++
++int au_copy_file(struct file *dst, struct file *src, loff_t len,
++ struct super_block *sb, int *sparse)
++{
++ int err, all_zero, dlgt;
++ unsigned long blksize;
++ char *buf;
++ /* reduce stack space */
++ struct iattr *ia;
++
++ LKTRTrace("%.*s, %.*s\n",
++ DLNPair(dst->f_dentry), DLNPair(src->f_dentry));
++ DEBUG_ON(!(dst->f_mode & FMODE_WRITE));
++ IMustLock(dst->f_dentry->d_parent->d_inode);
++
++ 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_KERNEL);
++ //buf = NULL;
++ if (unlikely(!buf))
++ goto out;
++ ia = kmalloc(sizeof(*ia), GFP_KERNEL);
++ if (unlikely(!ia))
++ goto out_buf;
++
++ dlgt = need_dlgt(sb);
++ err = all_zero = 0;
++ dst->f_pos = src->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;
++ while (!rbytes || err == -EAGAIN || err == -EINTR)
++ err = rbytes = vfsub_read_k(src, buf, sz, &src->f_pos,
++ dlgt);
++ if (unlikely(err < 0))
++ break;
++
++ all_zero = 0;
++ if (len >= rbytes && rbytes == blksize) {
++ all_zero = 1;
++ p = buf;
++ for (i = 0; all_zero && i < rbytes; i++)
++ all_zero = !*p++;
++ }
++ if (!all_zero) {
++ wbytes = rbytes;
++ p = buf;
++ while (wbytes) {
++ size_t b;
++ /* support LSM and notify */
++ err = b = vfsub_write_k(dst, p, wbytes,
++ &dst->f_pos, dlgt);
++ if (unlikely(err == -EAGAIN || err == -EINTR))
++ continue;
++ if (unlikely(err < 0))
++ break;
++ wbytes -= b;
++ p += b;
++ }
++ } else {
++ loff_t res;
++ LKTRLabel(hole);
++ *sparse = 1;
++ err = res = vfsub_llseek(dst, rbytes, SEEK_CUR);
++ 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 {
++ err = vfsub_write_k(dst, "\0", 1, &dst->f_pos, dlgt);
++ } 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;
++ hi_lock_child2(h_i);
++ err = vfsub_notify_change(h_d, ia, dlgt);
++ i_unlock(h_i);
++ }
++ }
++
++ kfree(ia);
++ out_buf:
++ kfree(buf);
++ out:
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++int test_ro(struct super_block *sb, aufs_bindex_t bindex, struct inode *inode)
++{
++ int err;
++
++ err = br_rdonly(stobr(sb, bindex));
++ if (!err && inode) {
++ struct inode *hi = au_h_iptr_i(inode, bindex);
++ if (hi)
++ err = IS_IMMUTABLE(hi) ? -EROFS : 0;
++ }
++ return err;
++}
++
++int au_test_perm(struct inode *hidden_inode, int mask, int dlgt)
++{
++ if (!current->fsuid)
++ return 0;
++ if (unlikely(au_is_nfs(hidden_inode->i_sb)
++ && (mask & MAY_WRITE)
++ && S_ISDIR(hidden_inode->i_mode)))
++ mask |= MAY_READ; /* force permission check */
++ return vfsub_permission(hidden_inode, mask, NULL, dlgt);
++}
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/misc.h linux-2.6.22.1/fs/aufs/misc.h
+--- linux-2.6.22.1.oorig/fs/aufs/misc.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/misc.h 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,187 @@
++/*
++ * 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: misc.h,v 1.25 2007/05/14 03:41:52 sfjro Exp $ */
++
++#ifndef __AUFS_MISC_H__
++#define __AUFS_MISC_H__
++
++#ifdef __KERNEL__
++
++#include <linux/fs.h>
++#include <linux/namei.h>
++#include <linux/sched.h>
++#include <linux/version.h>
++#include <linux/aufs_type.h>
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
++#define I_MUTEX_QUOTA 0
++#define lockdep_off() /* */
++#define lockdep_on() /* */
++#define mutex_lock_nested(mtx, lsc) mutex_lock(mtx)
++#define down_write_nested(rw, lsc) down_write(rw)
++#define down_read_nested(rw, lsc) down_read(rw)
++#endif
++
++/* ---------------------------------------------------------------------- */
++
++struct aufs_rwsem {
++ struct rw_semaphore rwsem;
++#ifdef CONFIG_AUFS_DEBUG
++ atomic_t rcnt;
++#endif
++};
++
++#ifdef CONFIG_AUFS_DEBUG
++#define DbgRcntInit(rw) atomic_set(&(rw)->rcnt, 0)
++#define DbgRcntInc(rw) atomic_inc(&(rw)->rcnt)
++#define DbgRcntDec(rw) WARN_ON(atomic_dec_return(&(rw)->rcnt) < 0)
++#else
++#define DbgRcntInit(rw) /* */
++#define DbgRcntInc(rw) /* */
++#define DbgRcntDec(rw) /* */
++#endif
++
++static inline void rw_init_nolock(struct aufs_rwsem *rw)
++{
++ DbgRcntInit(rw);
++ init_rwsem(&rw->rwsem);
++}
++
++static inline void rw_init_wlock(struct aufs_rwsem *rw)
++{
++ rw_init_nolock(rw);
++ down_write(&rw->rwsem);
++}
++
++static inline void rw_init_wlock_nested(struct aufs_rwsem *rw, unsigned int lsc)
++{
++ rw_init_nolock(rw);
++ down_write_nested(&rw->rwsem, lsc);
++}
++
++static inline void rw_read_lock(struct aufs_rwsem *rw)
++{
++ down_read(&rw->rwsem);
++ DbgRcntInc(rw);
++}
++
++static inline void rw_read_lock_nested(struct aufs_rwsem *rw, unsigned int lsc)
++{
++ down_read_nested(&rw->rwsem, lsc);
++ DbgRcntInc(rw);
++}
++
++static inline void rw_read_unlock(struct aufs_rwsem *rw)
++{
++ DbgRcntDec(rw);
++ up_read(&rw->rwsem);
++}
++
++static inline void rw_dgrade_lock(struct aufs_rwsem *rw)
++{
++ DbgRcntInc(rw);
++ downgrade_write(&rw->rwsem);
++}
++
++static inline void rw_write_lock(struct aufs_rwsem *rw)
++{
++ down_write(&rw->rwsem);
++}
++
++static inline void rw_write_lock_nested(struct aufs_rwsem *rw, unsigned int lsc)
++{
++ down_write_nested(&rw->rwsem, lsc);
++}
++
++static inline void rw_write_unlock(struct aufs_rwsem *rw)
++{
++ up_write(&rw->rwsem);
++}
++
++#if 0 // why is not _nested version defined
++static inline int rw_read_trylock(struct aufs_rwsem *rw)
++{
++ int ret = down_read_trylock(&rw->rwsem);
++ if (ret)
++ DbgRcntInc(rw);
++ return ret;
++}
++
++static inline int rw_write_trylock(struct aufs_rwsem *rw)
++{
++ return down_write_trylock(&rw->rwsem);
++}
++#endif
++
++#undef DbgRcntInit
++#undef DbgRcntInc
++#undef DbgRcntDec
++
++/* to debug easier, do not make them inlined functions */
++#define RwMustNoWaiters(rw) DEBUG_ON(!list_empty(&(rw)->rwsem.wait_list))
++#define RwMustAnyLock(rw) DEBUG_ON(down_write_trylock(&(rw)->rwsem))
++#ifdef CONFIG_AUFS_DEBUG
++#define RwMustReadLock(rw) do { \
++ RwMustAnyLock(rw); \
++ DEBUG_ON(!atomic_read(&(rw)->rcnt)); \
++} while (0)
++#define RwMustWriteLock(rw) do { \
++ RwMustAnyLock(rw); \
++ DEBUG_ON(atomic_read(&(rw)->rcnt)); \
++} while (0)
++#else
++#define RwMustReadLock(rw) RwMustAnyLock(rw)
++#define RwMustWriteLock(rw) RwMustAnyLock(rw)
++#endif
++
++#define SimpleLockRwsemFuncs(prefix, param, rwsem) \
++static inline void prefix##_read_lock(param) {rw_read_lock(&(rwsem));} \
++static inline void prefix##_write_lock(param) {rw_write_lock(&(rwsem));}
++//static inline void prefix##_read_trylock(param) {rw_read_trylock(&(rwsem));}
++//static inline void prefix##_write_trylock(param) {rw_write_trylock(&(rwsem));}
++//static inline void prefix##_read_trylock_nested(param, lsc)
++//{rw_read_trylock_nested(&(rwsem, lsc));}
++//static inline void prefix##_write_trylock_nestd(param, lsc)
++//{rw_write_trylock_nested(&(rwsem), nested);}
++
++#define SimpleUnlockRwsemFuncs(prefix, param, rwsem) \
++static inline void prefix##_read_unlock(param) {rw_read_unlock(&(rwsem));} \
++static inline void prefix##_write_unlock(param) {rw_write_unlock(&(rwsem));} \
++static inline void prefix##_downgrade_lock(param) {rw_dgrade_lock(&(rwsem));}
++
++#define SimpleRwsemFuncs(prefix, param, rwsem) \
++ SimpleLockRwsemFuncs(prefix, param, rwsem); \
++ SimpleUnlockRwsemFuncs(prefix, param, rwsem)
++
++/* ---------------------------------------------------------------------- */
++
++typedef ssize_t (*readf_t)(struct file*, char __user*, size_t, loff_t*);
++typedef ssize_t (*writef_t)(struct file*, const char __user*, size_t, loff_t*);
++
++void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp);
++struct nameidata *fake_dm(struct nameidata *fake_nd, struct nameidata *nd,
++ struct super_block *sb, aufs_bindex_t bindex);
++void fake_dm_release(struct nameidata *fake_nd);
++int au_copy_file(struct file *dst, struct file *src, loff_t len,
++ struct super_block *sb, int *sparse);
++int test_ro(struct super_block *sb, aufs_bindex_t bindex, struct inode *inode);
++int au_test_perm(struct inode *h_inode, int mask, int dlgt);
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_MISC_H__ */
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/module.c linux-2.6.22.1/fs/aufs/module.c
+--- linux-2.6.22.1.oorig/fs/aufs/module.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/module.c 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,334 @@
++/*
++ * 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: module.c,v 1.9 2007/04/30 05:46:32 sfjro Exp $ */
++
++//#include <linux/init.h>
++//#include <linux/kobject.h>
++#include <linux/module.h>
++//#include <linux/seq_file.h>
++//#include <linux/sysfs.h>
++#include "aufs.h"
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * aufs caches
++ */
++struct kmem_cache *aufs_cachep[AuCache_Last];
++static int __init create_cache(void)
++{
++#define Cache(type) kmem_cache_create(#type, sizeof(struct type), 0, \
++ SLAB_RECLAIM_ACCOUNT, NULL, NULL)
++
++ if ((aufs_cachep[AuCache_DINFO] = Cache(aufs_dinfo))
++ && (aufs_cachep[AuCache_ICNTNR] = Cache(aufs_icntnr))
++ && (aufs_cachep[AuCache_FINFO] = Cache(aufs_finfo))
++ //&& (aufs_cachep[AuCache_FINFO] = NULL)
++ && (aufs_cachep[AuCache_VDIR] = Cache(aufs_vdir))
++ && (aufs_cachep[AuCache_DEHSTR] = Cache(aufs_dehstr))
++ && (aufs_cachep[AuCache_HINOTIFY] = Cache(aufs_hinotify)))
++ return 0;
++ return -ENOMEM;
++
++#undef Cache
++}
++
++static void destroy_cache(void)
++{
++ int i;
++ for (i = 0; i < AuCache_Last; i++)
++ if (aufs_cachep[i])
++ kmem_cache_destroy(aufs_cachep[i]);
++}
++
++/* ---------------------------------------------------------------------- */
++
++char au_esc_chars[0x20 + 3]; /* 0x01-0x20, backslash, del, and NULL */
++int au_dir_roflags;
++extern struct file_system_type aufs_fs_type;
++
++#ifdef DbgDlgt
++#include <linux/security.h>
++#include "dbg_dlgt.c"
++#else
++#define dbg_dlgt_init() 0
++#define dbg_dlgt_fin() /* */
++#endif
++
++/*
++ * 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 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, 0444);
++
++int sysaufs_brs = 0;
++MODULE_PARM_DESC(brs, "use <sysfs>/fs/aufs/brs");
++module_param_named(brs, sysaufs_brs, int, 0444);
++
++static int __init aufs_init(void)
++{
++ int err, i;
++ char *p;
++
++ //sbinfo->si_xino is atomic_long_t
++ BUILD_BUG_ON(sizeof(ino_t) != sizeof(long));
++
++#ifdef CONFIG_AUFS_DEBUG
++ {
++ struct aufs_destr destr;
++ destr.len = -1;
++ DEBUG_ON(destr.len < NAME_MAX);
++ }
++
++#ifdef CONFIG_4KSTACKS
++ printk("CONFIG_4KSTACKS is defined.\n");
++#endif
++#if 0 // verbose debug
++ {
++ union {
++ struct aufs_branch *br;
++ struct aufs_dinfo *di;
++ struct aufs_finfo *fi;
++ struct aufs_iinfo *ii;
++ struct aufs_hinode *hi;
++ struct aufs_sbinfo *si;
++ struct aufs_destr *destr;
++ struct aufs_de *de;
++ struct aufs_wh *wh;
++ struct aufs_vdir *vd;
++ } u;
++
++ printk("br{"
++ "xino %d, readf %d, writef %d, "
++ "id %d, perm %d, mnt %d, count %d, "
++ "wh_sem %d, wh %d, run %d} %d\n",
++ offsetof(typeof(*u.br), br_xino),
++ offsetof(typeof(*u.br), br_xino_read),
++ offsetof(typeof(*u.br), br_xino_write),
++ 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),
++ sizeof(*u.br));
++ printk("di{gen %d, rwsem %d, bstart %d, bend %d, bwh %d, "
++ "bdiropq %d, hdentry %d, reval %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),
++ offsetof(typeof(*u.di), di_reval),
++ sizeof(*u.di));
++ printk("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));
++ printk("ii{rwsem %d, bstart %d, bend %d, hinode %d, vdir %d} "
++ "%d\n",
++ 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));
++ printk("hi{inode %d, id %d, notify %d} %d\n",
++ offsetof(typeof(*u.hi), hi_inode),
++ offsetof(typeof(*u.hi), hi_id),
++ offsetof(typeof(*u.hi), hi_notify),
++ sizeof(*u.hi));
++ printk("si{rwsem %d, gen %d, "
++ "failed_refresh %d, "
++ "bend %d, last id %d, br %d, "
++ "flags %d, "
++ "xino %d, "
++ "rdcache %d, "
++ "dirwh %d, "
++ "pl_lock %d, pl %d, "
++ "kobj %d} %d\n",
++ offsetof(typeof(*u.si), si_rwsem),
++ offsetof(typeof(*u.si), si_generation),
++ -1,//offsetof(typeof(*u.si), si_failed_refresh_dirs),
++ 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_flags),
++ offsetof(typeof(*u.si), si_xino),
++ 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_kobj),
++ sizeof(*u.si));
++ printk("destr{len %d, name %d} %d\n",
++ offsetof(typeof(*u.destr), len),
++ offsetof(typeof(*u.destr), name),
++ sizeof(*u.destr));
++ printk("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));
++ printk("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));
++ printk("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
++#endif
++
++ 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);
++#ifndef CONFIG_AUFS_SYSAUFS
++ sysaufs_brs = 0;
++#endif
++
++ err = -EINVAL;
++ if (unlikely(aufs_nwkq <= 0))
++ goto out;
++ err = create_cache();
++ if (unlikely(err))
++ goto out;
++ err = sysaufs_init();
++ if (unlikely(err))
++ goto out_cache;
++ err = au_wkq_init();
++ if (unlikely(err))
++ goto out_kobj;
++ err = au_inotify_init();
++ if (unlikely(err))
++ goto out_wkq;
++ err = dbg_dlgt_init();
++ if (unlikely(err))
++ goto out_inotify;
++ err = register_filesystem(&aufs_fs_type);
++ if (unlikely(err))
++ goto out_dlgt;
++ printk(AUFS_NAME " " AUFS_VERSION "\n");
++ return 0; /* success */
++
++ out_dlgt:
++ dbg_dlgt_fin();
++ out_inotify:
++ au_inotify_fin();
++ out_wkq:
++ au_wkq_fin();
++ out_kobj:
++ sysaufs_fin();
++ out_cache:
++ destroy_cache();
++ out:
++ TraceErr(err);
++ return err;
++}
++
++static void __exit aufs_exit(void)
++{
++ unregister_filesystem(&aufs_fs_type);
++ dbg_dlgt_fin();
++ au_inotify_fin();
++ au_wkq_fin();
++ sysaufs_fin();
++ destroy_cache();
++}
++
++module_init(aufs_init);
++module_exit(aufs_exit);
++
++/* ---------------------------------------------------------------------- */
++
++// fake Kconfig
++#if 1
++#ifdef CONFIG_AUFS_HINOTIFY
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
++#error CONFIG_AUFS_HINOTIFY is supported in linux-2.6.18 and later.
++#endif
++#ifndef CONFIG_INOTIFY
++#error enable CONFIG_INOTIFY to use CONFIG_AUFS_HINOTIFY.
++#endif
++#endif
++
++#if AUFS_BRANCH_MAX > 511 && BITS_PER_LONG == 64 && PAGE_SIZE == 4096
++#warning For 4k pagesize and 64bit environment, \
++ CONFIG_AUFS_BRANCH_MAX_511 or smaller is recommended.
++#endif
++
++#ifdef CONFIG_AUFS_SYSAUFS
++#ifndef CONFIG_SYSFS
++#error CONFIG_AUFS_SYSAUFS requires CONFIG_SYSFS.
++#endif
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
++#error CONFIG_AUFS_SYSAUFS requires linux-2.6.18 and later.
++#endif
++#endif
++
++#ifdef CONFIG_AUFS_EXPORT
++#if !defined(CONFIG_EXPORTFS) && !defined(CONFIG_EXPORTFS_MODULE)
++#error CONFIG_AUFS_EXPORT requires CONFIG_EXPORTFS
++#endif
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
++#error CONFIG_AUFS_EXPORT requires linux-2.6.18 and later.
++#endif
++#if defined(CONFIG_EXPORTFS_MODULE) && defined(CONFIG_AUFS)
++#error need CONFIG_EXPORTFS=y to link aufs statically with CONFIG_AUFS_EXPORT
++#endif
++#endif
++
++#ifdef CONFIG_DEBUG_PROVE_LOCKING
++#if MAX_LOCKDEP_SUBCLASSES < AuLsc_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
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/module.h linux-2.6.22.1/fs/aufs/module.h
+--- linux-2.6.22.1.oorig/fs/aufs/module.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/module.h 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,60 @@
++/*
++ * 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: module.h,v 1.8 2007/05/14 03:41:52 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, AuCache_HINOTIFY, AuCache_Last};
++extern struct kmem_cache *aufs_cachep[];
++
++#define CacheFuncs(name, index) \
++static inline void *cache_alloc_##name(void) \
++{return kmem_cache_alloc(aufs_cachep[index], GFP_KERNEL);} \
++static inline void cache_free_##name(void *p) \
++{kmem_cache_free(aufs_cachep[index], p);}
++
++CacheFuncs(dinfo, AuCache_DINFO);
++CacheFuncs(icntnr, AuCache_ICNTNR);
++CacheFuncs(finfo, AuCache_FINFO);
++CacheFuncs(vdir, AuCache_VDIR);
++CacheFuncs(dehstr, AuCache_DEHSTR);
++CacheFuncs(hinotify, AuCache_HINOTIFY);
++
++#undef CacheFuncs
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_MODULE_H__ */
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/opts.c linux-2.6.22.1/fs/aufs/opts.c
+--- linux-2.6.22.1.oorig/fs/aufs/opts.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/opts.c 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,1043 @@
++/*
++ * 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: opts.c,v 1.34 2007/05/14 03:40:27 sfjro Exp $ */
++
++#include <asm/types.h> // a distribution requires
++#include <linux/parser.h>
++#include "aufs.h"
++
++enum {
++ Opt_br,
++ Opt_add, Opt_del, Opt_mod, Opt_append, Opt_prepend,
++ Opt_idel, Opt_imod,
++ Opt_dirwh, Opt_rdcache, Opt_deblk, Opt_nhash,
++ Opt_xino, Opt_zxino, Opt_noxino,
++ Opt_plink, Opt_noplink, Opt_list_plink, Opt_clean_plink,
++ Opt_udba,
++ Opt_diropq_a, Opt_diropq_w,
++ Opt_warn_perm, Opt_nowarn_perm,
++ Opt_findrw_dir, Opt_findrw_br,
++ Opt_coo,
++ Opt_dlgt, Opt_nodlgt,
++ Opt_tail, Opt_ignore, 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_noxino, "noxino"},
++ //{Opt_zxino, "zxino=%s"},
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
++ {Opt_plink, "plink"},
++ {Opt_noplink, "noplink"},
++#ifdef CONFIG_AUFS_DEBUG
++ {Opt_list_plink, "list_plink"},
++#endif
++ {Opt_clean_plink, "clean_plink"},
++#endif
++
++ {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"},
++#endif
++
++ {Opt_rdcache, "rdcache=%d"},
++ {Opt_rdcache, "rdcache:%d"},
++#if 0
++ {Opt_findrw_dir, "findrw=dir"},
++ {Opt_findrw_br, "findrw=br"},
++
++ {Opt_coo, "coo=%s"},
++
++ {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}
++};
++
++/* ---------------------------------------------------------------------- */
++
++#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 int br_perm_val(char *perm)
++{
++ int val;
++ substring_t args[MAX_OPT_ARGS];
++
++ DEBUG_ON(!perm || !*perm);
++ LKTRTrace("perm %s\n", perm);
++ val = match_token(perm, brperms, args);
++ TraceErr(val);
++ return val;
++}
++
++int br_perm_str(char *p, unsigned int len, int brperm)
++{
++ struct match_token *bp = brperms;
++
++ LKTRTrace("len %d, 0x%x\n", len, brperm);
++
++ while (bp->pattern) {
++ if (bp->token == brperm) {
++ if (strlen(bp->pattern) < len) {
++ strcpy(p, bp->pattern);
++ return 0;
++ } else
++ return -E2BIG;
++ }
++ bp++;
++ }
++
++ return -EIO;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static match_table_t udbalevel = {
++ {AuFlag_UDBA_REVAL, "reval"},
++#ifdef CONFIG_AUFS_HINOTIFY
++ {AuFlag_UDBA_INOTIFY, "inotify"},
++#endif
++ {AuFlag_UDBA_NONE, "none"},
++ {-1, NULL}
++};
++
++static int udba_val(char *str)
++{
++ substring_t args[MAX_OPT_ARGS];
++ return match_token(str, udbalevel, args);
++}
++
++au_parser_pattern_t udba_str(int udba)
++{
++ struct match_token *p = udbalevel;
++ while (p->pattern) {
++ if (p->token == udba)
++ return p->pattern;
++ p++;
++ }
++ BUG();
++ return "??";
++}
++
++void udba_set(struct super_block *sb, unsigned int flg)
++{
++ au_flag_clr(sb, AuMask_UDBA);
++ au_flag_set(sb, flg);
++}
++
++/* ---------------------------------------------------------------------- */
++
++static match_table_t coolevel = {
++ {AuFlag_COO_LEAF, "leaf"},
++ {AuFlag_COO_ALL, "all"},
++ {AuFlag_COO_NONE, "none"},
++ {-1, NULL}
++};
++
++#if 0
++static int coo_val(char *str)
++{
++ substring_t args[MAX_OPT_ARGS];
++ return match_token(str, coolevel, args);
++}
++#endif
++
++au_parser_pattern_t coo_str(int coo)
++{
++ struct match_token *p = coolevel;
++ while (p->pattern) {
++ if (p->token == coo)
++ return p->pattern;
++ p++;
++ }
++ BUG();
++ return "??";
++}
++static void coo_set(struct super_block *sb, unsigned int flg)
++{
++ au_flag_clr(sb, AuMask_COO);
++ au_flag_set(sb, flg);
++}
++
++/* ---------------------------------------------------------------------- */
++
++static const int lkup_dirflags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
++
++#ifdef CONFIG_AUFS_DEBUG
++static void dump_opts(struct opts *opts)
++{
++ /* reduce stack space */
++ union {
++ struct opt_add *add;
++ struct opt_del *del;
++ struct opt_mod *mod;
++ struct opt_xino *xino;
++ } u;
++ struct opt *opt;
++
++ TraceEnter();
++
++ 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.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.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.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, DLNPair(u.xino->file->f_dentry));
++ break;
++ case Opt_noxino:
++ LKTRLabel(noxino);
++ 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, udba_str(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_coo:
++ LKTRTrace("coo %d, %s\n", opt->coo, coo_str(opt->coo));
++ break;
++ default:
++ BUG();
++ }
++ opt++;
++ }
++}
++#else
++#define dump_opts(opts) /* */
++#endif
++
++void au_free_opts(struct opts *opts)
++{
++ struct opt *opt;
++
++ TraceEnter();
++
++ opt = opts->opt;
++ while (opt->type != Opt_tail) {
++ switch (opt->type) {
++ case Opt_add:
++ case Opt_append:
++ case Opt_prepend:
++ path_release(&opt->add.nd);
++ 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 opt *opt, char *opt_str, struct super_block *sb,
++ aufs_bindex_t bindex)
++{
++ int err;
++ struct opt_add *add = &opt->add;
++ char *p;
++
++ LKTRTrace("%s, b%d\n", opt_str, bindex);
++
++ add->bindex = bindex;
++ add->perm = AuBr_RO;
++ if (!bindex && !(sb->s_flags & MS_RDONLY))
++ add->perm = AuBr_RW;
++#ifdef CONFIG_AUFS_COMPAT
++ add->perm = AuBr_RW;
++#endif
++ 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 = path_lookup(add->path, lkup_dirflags, &add->nd);
++ //err = -1;
++ if (!err) {
++ opt->type = Opt_add;
++ goto out;
++ }
++ Err("lookup failed %s (%d)\n", add->path, err);
++ err = -EINVAL;
++
++ out:
++ TraceErr(err);
++ return err;
++}
++
++/* called without aufs lock */
++int au_parse_opts(struct super_block *sb, char *str, struct opts *opts)
++{
++ int err, n;
++ struct dentry *root;
++ struct opt *opt, *opt_tail;
++ char *opt_str;
++ substring_t args[MAX_OPT_ARGS];
++ aufs_bindex_t bindex;
++ struct nameidata nd;
++ /* reduce stack space */
++ union {
++ struct opt_del *del;
++ struct opt_mod *mod;
++ struct opt_xino *xino;
++ } 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) {
++ int token, skipped;
++ char *p;
++ 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))) {
++ Err("bad integer in %s\n", opt_str);
++ break;
++ }
++ bindex = n;
++ err = opt_add(opt, args[1].from, sb, bindex);
++ break;
++ case Opt_append:
++ case Opt_prepend:
++ err = opt_add(opt, args[0].from, sb, /*dummy bindex*/1);
++ 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 = path_lookup(u.del->path, lkup_dirflags, &nd);
++ if (unlikely(err)) {
++ Err("lookup failed %s (%d)\n", u.del->path, err);
++ break;
++ }
++ u.del->h_root = dget(nd.dentry);
++ path_release(&nd);
++ opt->type = token;
++ break;
++#if 0
++ case Opt_idel:
++ u.del = &opt->del;
++ u.del->path = "(indexed)";
++ if (unlikely(match_int(&args[0], &n))) {
++ Err("bad integer in %s\n", opt_str);
++ break;
++ }
++ bindex = n;
++ aufs_read_lock(root, !AUFS_I_RLOCK);
++ if (bindex < 0 || sbend(sb) < bindex) {
++ Err("out of bounds, %d\n", bindex);
++ aufs_read_unlock(root, !AUFS_I_RLOCK);
++ break;
++ }
++ err = 0;
++ u.del->h_root = dget(au_h_dptr_i(root, bindex));
++ opt->type = token;
++ aufs_read_unlock(root, !AUFS_I_RLOCK);
++ break;
++#endif
++
++ case Opt_mod:
++ u.mod = &opt->mod;
++ u.mod->path = args[0].from;
++ p = strchr(u.mod->path, '=');
++ if (unlikely(!p)) {
++ Err("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 = path_lookup(u.mod->path, lkup_dirflags, &nd);
++ if (unlikely(err)) {
++ Err("lookup failed %s (%d)\n", u.mod->path, err);
++ break;
++ }
++ u.mod->h_root = dget(nd.dentry);
++ path_release(&nd);
++ opt->type = token;
++ break;
++#if 0
++ case Opt_imod:
++ u.mod = &opt->mod;
++ u.mod->path = "(indexed)";
++ if (unlikely(match_int(&args[0], &n))) {
++ Err("bad integer in %s\n", opt_str);
++ break;
++ }
++ bindex = n;
++ aufs_read_lock(root, !AUFS_I_RLOCK);
++ if (bindex < 0 || sbend(sb) < bindex) {
++ Err("out of bounds, %d\n", bindex);
++ aufs_read_unlock(root, !AUFS_I_RLOCK);
++ 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_i(root, bindex));
++ opt->type = token;
++ aufs_read_unlock(root, !AUFS_I_RLOCK);
++ break;
++#endif
++ case Opt_xino:
++ u.xino = &opt->xino;
++ file = 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);
++ Err("%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_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_noxino:
++ 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:
++ 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;
++
++#if 0
++ case Opt_coo:
++ opt->coo = coo_val(args[0].from);
++ if (opt->coo >= 0) {
++ err = 0;
++ opt->type = token;
++ }
++ break;
++#endif
++
++ case Opt_ignore:
++#ifndef CONFIG_AUFS_COMPAT
++ Warn("ignored %s\n", opt_str);
++#endif
++ skipped = 1;
++ err = 0;
++ break;
++ case Opt_err:
++ Err("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_free_opts(opts);
++ TraceErr(err);
++ return err;
++}
++
++/*
++ * returns,
++ * plus: processed without an error
++ * zero: unprocessed
++ */
++static int au_do_opt_simple(struct super_block *sb, struct opt *opt,
++ int remount, unsigned int *given)
++{
++ int err;
++ struct aufs_sbinfo *sbinfo = stosi(sb);
++
++ TraceEnter();
++
++ err = 1; /* handled */
++ switch (opt->type) {
++ case Opt_udba:
++ udba_set(sb, opt->udba);
++ *given |= opt->udba;
++ break;
++
++ case Opt_plink:
++ au_flag_set(sb, AuFlag_PLINK);
++ *given |= AuFlag_PLINK;
++ break;
++ case Opt_noplink:
++ if (au_flag_test(sb, AuFlag_PLINK))
++ au_put_plink(sb);
++ au_flag_clr(sb, AuFlag_PLINK);
++ *given |= AuFlag_PLINK;
++ break;
++ case Opt_list_plink:
++ if (au_flag_test(sb, AuFlag_PLINK))
++ au_list_plink(sb);
++ break;
++ case Opt_clean_plink:
++ if (au_flag_test(sb, AuFlag_PLINK))
++ au_put_plink(sb);
++ break;
++
++ case Opt_diropq_a:
++ au_flag_set(sb, AuFlag_ALWAYS_DIROPQ);
++ *given |= AuFlag_ALWAYS_DIROPQ;
++ break;
++ case Opt_diropq_w:
++ au_flag_clr(sb, AuFlag_ALWAYS_DIROPQ);
++ *given |= AuFlag_ALWAYS_DIROPQ;
++ break;
++
++ case Opt_dlgt:
++ au_flag_set(sb, AuFlag_DLGT);
++ *given |= AuFlag_DLGT;
++ break;
++ case Opt_nodlgt:
++ au_flag_clr(sb, AuFlag_DLGT);
++ *given |= AuFlag_DLGT;
++ break;
++
++ case Opt_warn_perm:
++ au_flag_set(sb, AuFlag_WARN_PERM);
++ *given |= AuFlag_WARN_PERM;
++ break;
++ case Opt_nowarn_perm:
++ au_flag_clr(sb, AuFlag_WARN_PERM);
++ *given |= AuFlag_WARN_PERM;
++ break;
++
++ case Opt_coo:
++ coo_set(sb, opt->coo);
++ *given |= opt->coo;
++ break;
++
++ case Opt_dirwh:
++ sbinfo->si_dirwh = opt->dirwh;
++ break;
++
++ case Opt_rdcache:
++ sbinfo->si_rdcache = opt->rdcache * HZ;
++ break;
++
++ default:
++ err = 0;
++ break;
++ }
++
++ TraceErr(err);
++ return err;
++}
++
++/*
++ * returns tri-state.
++ * plus: processed without an error
++ * zero: unprocessed
++ * minus: error
++ */
++static int au_do_opt_br(struct super_block *sb, struct opt *opt, int remount,
++ int *do_refresh)
++{
++ int err;
++
++ TraceEnter();
++
++ err = 0;
++ switch (opt->type) {
++ case Opt_append:
++ opt->add.bindex = sbend(sb) + 1;
++ goto add;
++ case Opt_prepend:
++ opt->add.bindex = 0;
++ add:
++ case Opt_add:
++ err = br_add(sb, &opt->add, remount);
++ if (!err)
++ *do_refresh = err = 1;
++ break;
++
++ case Opt_del:
++ case Opt_idel:
++ err = br_del(sb, &opt->del, remount);
++ if (!err)
++ *do_refresh = err = 1;
++ break;
++
++ case Opt_mod:
++ case Opt_imod:
++ err = br_mod(sb, &opt->mod, remount, do_refresh);
++ if (!err)
++ err = 1;
++ break;
++ }
++
++ TraceErr(err);
++ return err;
++}
++
++static int au_do_opt_xino(struct super_block *sb, struct opt *opt, int remount,
++ struct opt_xino **opt_xino)
++{
++ int err;
++
++ TraceEnter();
++
++ err = 0;
++ switch (opt->type) {
++ case Opt_xino:
++ err = xino_set(sb, &opt->xino, remount);
++ if (!err)
++ *opt_xino = &opt->xino;
++ break;
++ case Opt_noxino:
++ err = xino_clr(sb);
++ break;
++ }
++
++ TraceErr(err);
++ return err;
++}
++
++static int verify_opts(struct super_block *sb, int remount)
++{
++ int err;
++ aufs_bindex_t bindex, bend;
++ struct aufs_branch *br;
++ struct dentry *root;
++ struct inode *dir;
++ unsigned int do_plink;
++
++ TraceEnter();
++
++ if (unlikely(!(sb->s_flags & MS_RDONLY)
++ && !br_writable(sbr_perm(sb, 0))))
++ Warn("first branch should be rw\n");
++
++ err = 0;
++ root = sb->s_root;
++ dir = sb->s_root->d_inode;
++ do_plink = au_flag_test(sb, AuFlag_PLINK);
++ bend = sbend(sb);
++ for (bindex = 0; !err && bindex <= bend; bindex++) {
++ struct inode *h_dir;
++ int skip;
++
++ skip = 0;
++ h_dir = au_h_iptr_i(dir, bindex);
++ br = stobr(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;
++
++ hdir_lock(h_dir, dir, bindex);
++ br_wh_write_lock(br);
++ err = init_wh(au_h_dptr_i(root, bindex), br,
++ au_nfsmnt(sb, bindex), sb);
++ br_wh_write_unlock(br);
++ hdir_unlock(h_dir, dir, bindex);
++ }
++
++ TraceErr(err);
++ return err;
++}
++
++int au_do_opts_mount(struct super_block *sb, struct opts *opts)
++{
++ int err, do_refresh;
++ struct inode *dir;
++ struct opt *opt;
++ unsigned int flags, given;
++ struct opt_xino *opt_xino;
++ aufs_bindex_t bend, bindex;
++
++ TraceEnter();
++ SiMustWriteLock(sb);
++ DiMustWriteLock(sb->s_root);
++ dir = sb->s_root->d_inode;
++ IiMustWriteLock(dir);
++
++ err = 0;
++ given = 0;
++ opt_xino = NULL;
++ opt = opts->opt;
++ while (err >= 0 && opt->type != Opt_tail)
++ err = au_do_opt_simple(sb, opt++, /*remount*/0, &given);
++ if (err > 0)
++ err = 0;
++ else if (unlikely(err < 0))
++ goto out;
++
++ /* disable them temporary */
++ flags = au_flag_test(sb, AuFlag_XINO | AuMask_UDBA | AuFlag_DLGT);
++ au_flag_clr(sb, AuFlag_XINO | AuFlag_DLGT);
++ udba_set(sb, AuFlag_UDBA_REVAL);
++
++ do_refresh = 0;
++ opt = opts->opt;
++ while (err >= 0 && opt->type != Opt_tail)
++ err = au_do_opt_br(sb, opt++, /*remount*/0, &do_refresh);
++ if (err > 0)
++ err = 0;
++ else if (unlikely(err < 0))
++ goto out;
++
++ bend = sbend(sb);
++ if (unlikely(bend < 0)) {
++ err = -EINVAL;
++ Err("no branches\n");
++ goto out;
++ }
++
++ if (flags & AuFlag_XINO)
++ au_flag_set(sb, AuFlag_XINO);
++ opt = opts->opt;
++ while (!err && opt->type != Opt_tail)
++ err = au_do_opt_xino(sb, opt++, /*remount*/0, &opt_xino);
++ if (unlikely(err))
++ goto out;
++
++ //todo: test this error case.
++ err = verify_opts(sb, /*remount*/0);
++ DEBUG_ON(err);
++ if (unlikely(err))
++ goto out;
++
++ /* enable xino */
++ if (au_flag_test(sb, AuFlag_XINO) && !opt_xino) {
++ struct file *xino_file = xino_def(sb);
++ err = PTR_ERR(xino_file);
++ if (IS_ERR(xino_file))
++ goto out;
++
++ err = 0;
++ for (bindex = 0; !err && bindex <= bend; bindex++)
++ err = xino_init(sb, bindex, xino_file,
++ /*do_test*/bindex);
++ fput(xino_file);
++ if (unlikely(err))
++ goto out;
++ }
++
++ /* restore hinotify */
++ udba_set(sb, flags & AuMask_UDBA);
++ if (flags & AuFlag_UDBA_INOTIFY)
++ au_reset_hinotify(dir, au_hi_flags(dir, 1) & ~AUFS_HI_XINO);
++
++ /* restore dlgt */
++ if (flags & AuFlag_DLGT)
++ au_flag_set(sb, AuFlag_DLGT);
++
++ out:
++ TraceErr(err);
++ return err;
++}
++
++int au_do_opts_remount(struct super_block *sb, struct opts *opts,
++ int *do_refresh, unsigned int *given)
++{
++ int err, rerr;
++ struct inode *dir;
++ struct opt_xino *opt_xino;
++ struct opt *opt;
++ unsigned int dlgt;
++
++ TraceEnter();
++ SiMustWriteLock(sb);
++ DiMustWriteLock(sb->s_root);
++ dir = sb->s_root->d_inode;
++ IiMustWriteLock(dir);
++ //DEBUG_ON(au_flag_test(sb, AuFlag_UDBA_INOTIFY));
++
++ err = 0;
++ *do_refresh = 0;
++ *given = 0;
++ dlgt = au_flag_test(sb, AuFlag_DLGT);
++ opt_xino = NULL;
++ opt = opts->opt;
++ while (err >= 0 && opt->type != Opt_tail) {
++ err = au_do_opt_simple(sb, opt, /*remount*/1, given);
++
++ /* disable it temporary */
++ dlgt = au_flag_test(sb, AuFlag_DLGT);
++ au_flag_clr(sb, AuFlag_DLGT);
++
++ if (!err)
++ err = au_do_opt_br(sb, opt, /*remount*/1, do_refresh);
++ if (!err)
++ err = au_do_opt_xino(sb, opt, /*remount*/1, &opt_xino);
++
++ /* restore it */
++ au_flag_set(sb, dlgt);
++ opt++;
++ }
++ if (err > 0)
++ err = 0;
++ TraceErr(err);
++
++ /* go on if err */
++
++ //todo: test this error case.
++ au_flag_clr(sb, AuFlag_DLGT);
++ rerr = verify_opts(sb, /*remount*/1);
++ au_flag_set(sb, dlgt);
++
++ /* they are handled by the caller */
++ if (!*do_refresh)
++ *do_refresh = !!((*given & AuMask_UDBA)
++ || au_flag_test(sb, AuFlag_XINO));
++
++ TraceErr(err);
++ return err;
++}
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/opts.h linux-2.6.22.1/fs/aufs/opts.h
+--- linux-2.6.22.1.oorig/fs/aufs/opts.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/opts.h 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,96 @@
++/*
++ * 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: opts.h,v 1.13 2007/05/14 06:27:18 sfjro Exp $ */
++
++#ifndef __AUFS_OPTS_H__
++#define __AUFS_OPTS_H__
++
++#ifdef __KERNEL__
++
++#include <linux/fs.h>
++#include <linux/namei.h>
++#include <linux/version.h>
++#include <linux/aufs_type.h>
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
++typedef const char* au_parser_pattern_t;
++#else
++typedef char* au_parser_pattern_t;
++#endif
++
++/* ---------------------------------------------------------------------- */
++
++struct opt_add {
++ aufs_bindex_t bindex;
++ char *path;
++ int perm;
++ struct nameidata nd;
++};
++
++struct opt_del {
++ char *path;
++ struct dentry *h_root;
++};
++
++struct opt_mod {
++ char *path;
++ int perm;
++ struct dentry *h_root;
++};
++
++struct opt_xino {
++ char *path;
++ struct file *file;
++};
++
++struct opt {
++ int type;
++ union {
++ struct opt_xino xino;
++ struct opt_add add;
++ struct opt_del del;
++ struct opt_mod mod;
++ int dirwh;
++ int rdcache;
++ int deblk;
++ int nhash;
++ int udba;
++ int coo;
++ };
++};
++
++struct opts {
++ struct opt *opt;
++ int max_opt;
++};
++
++/* ---------------------------------------------------------------------- */
++
++int br_perm_str(char *p, unsigned int len, int brperm);
++au_parser_pattern_t udba_str(int udba);
++void udba_set(struct super_block *sb, unsigned int flg);
++//au_parser_pattern_t coo_str(int coo);
++void au_free_opts(struct opts *opts);
++int au_parse_opts(struct super_block *sb, char *str, struct opts *opts);
++int au_do_opts_mount(struct super_block *sb, struct opts *opts);
++int au_do_opts_remount(struct super_block *sb, struct opts *opts,
++ int *do_refresh, unsigned int *given);
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_OPTS_H__ */
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/plink.c linux-2.6.22.1/fs/aufs/plink.c
+--- linux-2.6.22.1.oorig/fs/aufs/plink.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/plink.c 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,331 @@
++/*
++ * 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: plink.c,v 1.4 2007/05/14 03:39:10 sfjro Exp $ */
++
++#include "aufs.h"
++
++struct pseudo_link {
++ struct list_head list;
++ struct inode *inode;
++};
++
++#ifdef CONFIG_AUFS_DEBUG
++void au_list_plink(struct super_block *sb)
++{
++ struct aufs_sbinfo *sbinfo;
++ struct list_head *plink_list;
++ struct pseudo_link *plink;
++
++ TraceEnter();
++ SiMustAnyLock(sb);
++ DEBUG_ON(!au_flag_test(sb, AuFlag_PLINK));
++
++ sbinfo = stosi(sb);
++ plink_list = &sbinfo->si_plink;
++ spin_lock(&sbinfo->si_plink_lock);
++ list_for_each_entry(plink, plink_list, list)
++ Dbg("%lu\n", plink->inode->i_ino);
++ spin_unlock(&sbinfo->si_plink_lock);
++}
++#endif
++
++int au_is_plinked(struct super_block *sb, struct inode *inode)
++{
++ int found;
++ struct aufs_sbinfo *sbinfo;
++ struct list_head *plink_list;
++ struct pseudo_link *plink;
++
++ LKTRTrace("i%lu\n", inode->i_ino);
++ SiMustAnyLock(sb);
++ DEBUG_ON(!au_flag_test(sb, AuFlag_PLINK));
++
++ found = 0;
++ sbinfo = stosi(sb);
++ 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);
++ DEBUG_ON(len != PLINK_NAME_LEN);
++ h_inode = au_h_iptr_i(inode, bindex);
++ DEBUG_ON(!h_inode);
++ rlen = snprintf(name, len, "%lu.%lu", inode->i_ino, h_inode->i_ino);
++ DEBUG_ON(rlen >= len);
++ return rlen;
++}
++
++struct dentry *lkup_plink(struct super_block *sb, aufs_bindex_t bindex,
++ struct inode *inode)
++{
++ struct dentry *h_dentry, *h_parent;
++ struct aufs_branch *br;
++ struct inode *h_dir;
++ char tgtname[PLINK_NAME_LEN];
++ int len;
++ struct lkup_args lkup;
++
++ LKTRTrace("b%d, i%lu\n", bindex, inode->i_ino);
++ br = stobr(sb, bindex);
++ h_parent = br->br_plink;
++ DEBUG_ON(!h_parent);
++ h_dir = h_parent->d_inode;
++ DEBUG_ON(!h_dir);
++
++ len = plink_name(tgtname, sizeof(tgtname), inode, bindex);
++
++ // always superio.
++ lkup.nfsmnt = au_do_nfsmnt(br->br_mnt);
++ lkup.dlgt = need_dlgt(sb);
++ hi_lock_whplink(h_dir);
++ h_dentry = sio_lkup_one(tgtname, h_parent, len, &lkup);
++ i_unlock(h_dir);
++ 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;
++ struct dentry *h_tgt;
++ struct inode *h_dir;
++ struct lkup_args lkup = {
++ .nfsmnt = nfsmnt,
++ .dlgt = need_dlgt(sb)
++ };
++
++ h_tgt = lkup_one(tgt, h_parent, len, &lkup);
++ err = PTR_ERR(h_tgt);
++ if (IS_ERR(h_tgt))
++ goto out;
++
++ err = 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, lkup.dlgt);
++ if (!err && !h_tgt->d_inode) {
++ err = vfsub_link(h_dentry, h_dir, h_tgt, lkup.dlgt);
++ //inode->i_nlink++;
++ }
++ dput(h_tgt);
++
++ out:
++ TraceErr(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;
++ struct aufs_branch *br;
++ struct dentry *h_parent;
++ struct inode *h_dir;
++ char tgtname[PLINK_NAME_LEN];
++
++ LKTRTrace("%.*s\n", DLNPair(h_dentry));
++ br = stobr(inode->i_sb, bindex);
++ h_parent = br->br_plink;
++ DEBUG_ON(!h_parent);
++ h_dir = h_parent->d_inode;
++ DEBUG_ON(!h_dir);
++
++ len = plink_name(tgtname, sizeof(tgtname), inode, bindex);
++
++ // always superio.
++ hi_lock_whplink(h_dir);
++ if (!is_au_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
++ };
++ au_wkq_wait(call_do_whplink, &args, /*dlgt*/0);
++ } else
++ err = do_whplink(tgtname, len, h_parent, h_dentry,
++ au_do_nfsmnt(br->br_mnt), sb);
++ i_unlock(h_dir);
++
++ TraceErr(err);
++ return err;
++}
++
++void append_plink(struct super_block *sb, struct inode *inode,
++ struct dentry *h_dentry, aufs_bindex_t bindex)
++{
++ struct aufs_sbinfo *sbinfo;
++ struct list_head *plink_list;
++ struct pseudo_link *plink;
++ int found, err, cnt;
++
++ LKTRTrace("i%lu\n", inode->i_ino);
++ SiMustAnyLock(sb);
++ DEBUG_ON(!au_flag_test(sb, AuFlag_PLINK));
++
++ cnt = 0;
++ found = 0;
++ sbinfo = stosi(sb);
++ 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) {
++ struct pseudo_link *plink;
++
++ 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 > 100))
++ Warn1("unexpectedly many pseudo links, %d\n", cnt);
++ if (unlikely(err))
++ Warn("err %d, damaged pseudo link. ignored.\n", err);
++}
++
++static void do_put_plink(struct pseudo_link *plink, int do_del)
++{
++ TraceEnter();
++
++ iput(plink->inode);
++ if (do_del)
++ list_del(&plink->list);
++ kfree(plink);
++}
++
++void au_put_plink(struct super_block *sb)
++{
++ struct aufs_sbinfo *sbinfo;
++ struct list_head *plink_list;
++ struct pseudo_link *plink, *tmp;
++
++ TraceEnter();
++ SiMustWriteLock(sb);
++ DEBUG_ON(!au_flag_test(sb, AuFlag_PLINK));
++
++ sbinfo = stosi(sb);
++ 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 half_refresh_plink(struct super_block *sb, aufs_bindex_t br_id)
++{
++ struct aufs_sbinfo *sbinfo;
++ struct list_head *plink_list;
++ struct pseudo_link *plink, *tmp;
++ struct inode *inode;
++ aufs_bindex_t bstart, bend, bindex;
++ int do_put;
++
++ TraceEnter();
++ SiMustWriteLock(sb);
++ DEBUG_ON(!au_flag_test(sb, AuFlag_PLINK));
++
++ sbinfo = stosi(sb);
++ 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 = ibstart(inode);
++ bend = ibend(inode);
++ if (bstart >= 0) {
++ for (bindex = bstart; bindex <= bend; bindex++) {
++ if (!au_h_iptr_i(inode, bindex)
++ || itoid_index(inode, bindex) != br_id)
++ continue;
++ 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_i(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);
++}
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/sbinfo.c linux-2.6.22.1/fs/aufs/sbinfo.c
+--- linux-2.6.22.1.oorig/fs/aufs/sbinfo.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/sbinfo.c 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,173 @@
++/*
++ * 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: sbinfo.c,v 1.30 2007/05/14 03:39:31 sfjro Exp $ */
++
++#include "aufs.h"
++
++struct aufs_sbinfo *stosi(struct super_block *sb)
++{
++ struct aufs_sbinfo *sbinfo;
++ sbinfo = sb->s_fs_info;
++ //DEBUG_ON(sbinfo->si_bend < 0);
++ return sbinfo;
++}
++
++aufs_bindex_t sbend(struct super_block *sb)
++{
++ SiMustAnyLock(sb);
++ return stosi(sb)->si_bend;
++}
++
++struct aufs_branch *stobr(struct super_block *sb, aufs_bindex_t bindex)
++{
++ SiMustAnyLock(sb);
++ DEBUG_ON(bindex < 0 || sbend(sb) < bindex
++ || !stosi(sb)->si_branch[0 + bindex]);
++ return stosi(sb)->si_branch[0 + bindex];
++}
++
++int au_sigen(struct super_block *sb)
++{
++ SiMustAnyLock(sb);
++ return stosi(sb)->si_generation;
++}
++
++int au_sigen_inc(struct super_block *sb)
++{
++ int gen;
++
++ SiMustWriteLock(sb);
++ gen = ++stosi(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 find_bindex(struct super_block *sb, struct aufs_branch *br)
++{
++ aufs_bindex_t bindex, bend;
++
++ bend = sbend(sb);
++ for (bindex = 0; bindex <= bend; bindex++)
++ if (stobr(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);
++ if (flags & AUFS_D_WLOCK)
++ di_write_lock_child(dentry);
++ else
++ di_read_lock_child(dentry, flags);
++}
++
++void aufs_read_unlock(struct dentry *dentry, int flags)
++{
++ if (flags & AUFS_D_WLOCK)
++ di_write_unlock(dentry);
++ else
++ di_read_unlock(dentry, flags);
++ si_read_unlock(dentry->d_sb);
++}
++
++void aufs_write_lock(struct dentry *dentry)
++{
++ //au_wkq_wait_nwtask();
++ 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);
++ //au_wkq_wait_nwtask();
++}
++
++void aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int isdir)
++{
++ DEBUG_ON(d1 == d2 || d1->d_sb != d2->d_sb);
++ si_read_lock(d1->d_sb);
++ di_write_lock2_child(d1, d2, isdir);
++}
++
++void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2)
++{
++ DEBUG_ON(d1 == d2 || d1->d_sb != d2->d_sb);
++ di_write_unlock2(d1, d2);
++ si_read_unlock(d1->d_sb);
++}
++
++/* ---------------------------------------------------------------------- */
++
++aufs_bindex_t new_br_id(struct super_block *sb)
++{
++ aufs_bindex_t br_id;
++
++ TraceEnter();
++ SiMustWriteLock(sb);
++
++ while (1) {
++ br_id = ++stosi(sb)->si_last_br_id;
++ if (br_id && find_brindex(sb, br_id) < 0)
++ return br_id;
++ }
++}
++
++/* ---------------------------------------------------------------------- */
++
++#ifdef CONFIG_AUFS_SYSAUFS
++static int make_xino(struct seq_file *seq, struct sysaufs_args *args,
++ int *do_size)
++{
++ int err;
++ struct super_block *sb = args->sb;
++ aufs_bindex_t bindex, bend;
++ struct file *xf;
++ struct inode *xi;
++
++ TraceEnter();
++ DEBUG_ON(args->index != SysaufsSb_XINO);
++ SiMustReadLock(sb);
++
++ *do_size = 0;
++ err = seq_printf(seq, "%d %lu\n", sizeof(struct xino),
++ atomic_long_read(&stosi(sb)->si_xino));
++ bend = sbend(sb);
++ for (bindex = 0; !err && bindex <= bend; bindex++) {
++ xf = stobr(sb, bindex)->br_xino;
++ xi = xf->f_dentry->d_inode;
++ err = seq_printf(seq, "%d: %d, %Lux%d %Ld\n",
++ bindex, file_count(xf),
++ (u64)xi->i_blocks, 1 << xi->i_blkbits,
++ i_size_read(xi));
++ }
++ return err;
++}
++
++sysaufs_op au_si_ops[] = {
++ [SysaufsSb_XINO] = make_xino
++};
++#endif
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/super.c linux-2.6.22.1/fs/aufs/super.c
+--- linux-2.6.22.1.oorig/fs/aufs/super.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/super.c 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,716 @@
++/*
++ * 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: super.c,v 1.50 2007/05/14 03:39:42 sfjro Exp $ */
++
++#include <linux/module.h>
++#include <linux/seq_file.h>
++#include <linux/statfs.h>
++#include "aufs.h"
++
++/*
++ * super_operations
++ */
++static struct inode *aufs_alloc_inode(struct super_block *sb)
++{
++ struct aufs_icntnr *c;
++
++ TraceEnter();
++
++ c = cache_alloc_icntnr();
++ //if (LktrCond) {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);
++ cache_free_icntnr(container_of(inode, struct aufs_icntnr, vfs_inode));
++}
++
++//todo: how about merge with alloc_inode()?
++static void aufs_read_inode(struct inode *inode)
++{
++ int err;
++
++ LKTRTrace("i%lu\n", inode->i_ino);
++
++ 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;
++ return; /* success */
++ }
++
++ LKTRTrace("intializing inode info failed(%d)\n", err);
++ make_bad_inode(inode);
++}
++
++int au_show_brs(struct seq_file *seq, struct super_block *sb)
++{
++ int err;
++ aufs_bindex_t bindex, bend;
++ char a[16];
++ struct dentry *root;
++
++ TraceEnter();
++ SiMustAnyLock(sb);
++ root = sb->s_root;
++ DiMustAnyLock(root);
++
++ err = 0;
++ bend = sbend(sb);
++ for (bindex = 0; !err && bindex <= bend; bindex++) {
++ err = br_perm_str(a, sizeof(a), sbr_perm(sb, bindex));
++ if (!err)
++ err = seq_path(seq, sbr_mnt(sb, bindex),
++ au_h_dptr_i(root, bindex), au_esc_chars);
++ if (err > 0)
++ err = seq_printf(seq, "=%s", a);
++ if (!err && bindex != bend)
++ err = seq_putc(seq, ':');
++ }
++
++ TraceErr(err);
++ return err;
++}
++
++static int aufs_show_options(struct seq_file *m, struct vfsmount *mnt)
++{
++ int err, n;
++ struct super_block *sb;
++ struct aufs_sbinfo *sbinfo;
++ struct dentry *root;
++ struct file *xino;
++
++ TraceEnter();
++
++ sb = mnt->mnt_sb;
++ root = sb->s_root;
++ aufs_read_lock(root, !AUFS_I_RLOCK);
++ if (au_flag_test(sb, AuFlag_XINO)) {
++ err = seq_puts(m, ",xino=");
++ if (unlikely(err))
++ goto out;
++ xino = stobr(sb, 0)->br_xino;
++ err = seq_path(m, xino->f_vfsmnt, xino->f_dentry, au_esc_chars);
++ if (unlikely(err <= 0))
++ goto out;
++ err = 0;
++
++#define Deleted "\\040(deleted)"
++ m->count -= sizeof(Deleted) - 1;
++ DEBUG_ON(memcmp(m->buf + m->count, Deleted,
++ sizeof(Deleted) - 1));
++#undef Deleted
++ } else
++ err = seq_puts(m, ",noxino");
++
++ n = au_flag_test(sb, AuFlag_PLINK);
++ if (unlikely(!err && (AuDefFlags & AuFlag_PLINK) != n))
++ err = seq_printf(m, ",%splink", n ? "" : "no");
++ n = au_flag_test_udba(sb);
++ if (unlikely(!err && (AuDefFlags & AuMask_UDBA) != n))
++ err = seq_printf(m, ",udba=%s", udba_str(n));
++ n = au_flag_test(sb, AuFlag_ALWAYS_DIROPQ);
++ if (unlikely(!err && (AuDefFlags & AuFlag_ALWAYS_DIROPQ) != n))
++ err = seq_printf(m, ",diropq=%c", n ? 'a' : 'w');
++ n = au_flag_test(sb, AuFlag_DLGT);
++ if (unlikely(!err && (AuDefFlags & AuFlag_DLGT) != n))
++ err = seq_printf(m, ",%sdlgt", n ? "" : "no");
++ n = au_flag_test(sb, AuFlag_WARN_PERM);
++ if (unlikely(!err && (AuDefFlags & AuFlag_WARN_PERM) != n))
++ err = seq_printf(m, ",%swarn_perm", n ? "" : "no");
++
++ sbinfo = stosi(sb);
++ n = sbinfo->si_dirwh;
++ if (unlikely(!err && n != AUFS_DIRWH_DEF))
++ err = seq_printf(m, ",dirwh=%d", n);
++ n = sbinfo->si_rdcache / HZ;
++ if (unlikely(!err && n != AUFS_RDCACHE_DEF))
++ err = seq_printf(m, ",rdcache=%d", n);
++#if 0
++ n = au_flag_test_coo(sb);
++ if (unlikely(!err && (AuDefFlags & AuMask_COO) != n))
++ err = seq_printf(m, ",coo=%s", coo_str(n));
++#endif
++
++ if (!err && !sysaufs_brs) {
++#ifdef CONFIG_AUFS_COMPAT
++ err = seq_puts(m, ",dirs=");
++#else
++ err = seq_puts(m, ",br:");
++#endif
++ if (!err)
++ err = au_show_brs(m, sb);
++ }
++
++ out:
++ aufs_read_unlock(root, !AUFS_I_RLOCK);
++ TraceErr(err);
++ if (err)
++ err = -E2BIG;
++ TraceErr(err);
++ return err;
++}
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
++#define StatfsLock(d) aufs_read_lock((d)->d_sb->s_root, 0)
++#define StatfsUnlock(d) aufs_read_unlock((d)->d_sb->s_root, 0)
++#define StatfsArg(d) au_h_dptr((d)->d_sb->s_root)
++#define StatfsHInode(d) (StatfsArg(d)->d_inode)
++#define StatfsSb(d) ((d)->d_sb)
++static int aufs_statfs(struct dentry *arg, struct kstatfs *buf)
++#else
++#define StatfsLock(s) si_read_lock(s)
++#define StatfsUnlock(s) si_read_unlock(s)
++#define StatfsArg(s) sbr_sb(s, 0)
++#define StatfsHInode(s) (StatfsArg(s)->s_root->d_inode)
++#define StatfsSb(s) (s)
++static int aufs_statfs(struct super_block *arg, struct kstatfs *buf)
++#endif
++{
++ int err;
++
++ TraceEnter();
++
++ StatfsLock(arg);
++ err = vfsub_statfs(StatfsArg(arg), buf, need_dlgt(StatfsSb(arg)));
++ //if (LktrCond) err = -1;
++ StatfsUnlock(arg);
++ if (!err) {
++ //buf->f_type = AUFS_SUPER_MAGIC;
++ buf->f_type = 0;
++ buf->f_namelen -= AUFS_WH_PFX_LEN;
++ memset(&buf->f_fsid, 0, sizeof(buf->f_fsid));
++ }
++ //buf->f_bsize = buf->f_blocks = buf->f_bfree = buf->f_bavail = -1;
++
++ TraceErr(err);
++ return err;
++}
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) || defined(UbuntuEdgy17Umount18)
++#define UmountBeginSb(mnt) (mnt)->mnt_sb
++static void aufs_umount_begin(struct vfsmount *arg, int flags)
++#else
++#define UmountBeginSb(sb) sb
++static void aufs_umount_begin(struct super_block *arg)
++#endif
++{
++ struct super_block *sb = UmountBeginSb(arg);
++
++ if (unlikely(!stosi(sb)))
++ return;
++
++ //au_wkq_wait_nwtask();
++ si_write_lock(sb);
++ if (au_flag_test(sb, AuFlag_PLINK)) {
++ au_put_plink(sb);
++ //kobj_umount(stosi(sb));
++ }
++#if 0
++ if (unlikely(au_flag_test(sb, AuFlag_UDBA_INOTIFY)))
++ shrink_dcache_sb(sb);
++#endif
++ si_write_unlock(sb);
++}
++
++static void free_sbinfo(struct aufs_sbinfo *sbinfo)
++{
++ TraceEnter();
++ DEBUG_ON(!sbinfo
++ || !list_empty(&sbinfo->si_plink));
++
++ free_branches(sbinfo);
++ kfree(sbinfo->si_branch);
++ kfree(sbinfo);
++}
++
++/* final actions when unmounting a file system */
++static void aufs_put_super(struct super_block *sb)
++{
++ struct aufs_sbinfo *sbinfo;
++
++ TraceEnter();
++
++ sbinfo = stosi(sb);
++ if (unlikely(!sbinfo))
++ return;
++
++ sysaufs_del(sbinfo);
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) && !defined(UbuntuEdgy17Umount18)
++ // umount_begin() may not be called.
++ aufs_umount_begin(sb);
++#endif
++ free_sbinfo(sbinfo);
++}
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * refresh directories at remount time.
++ */
++static int do_refresh_dir(struct dentry *dentry, unsigned int flags)
++{
++ int err;
++ struct dentry *parent;
++ struct inode *inode;
++
++ LKTRTrace("%.*s\n", DLNPair(dentry));
++ inode = dentry->d_inode;
++ DEBUG_ON(!inode || !S_ISDIR(inode->i_mode));
++
++ di_write_lock_child(dentry);
++ parent = dget_parent(dentry);
++ di_read_lock_parent(parent, AUFS_I_RLOCK);
++ err = au_refresh_hdentry(dentry, S_IFDIR);
++ if (err >= 0) {
++ err = au_refresh_hinode(inode, dentry);
++ if (!err)
++ au_reset_hinotify(inode, flags);
++ }
++ if (unlikely(err))
++ Err("unrecoverable error %d\n", err);
++ di_read_unlock(parent, AUFS_I_RLOCK);
++ dput(parent);
++ di_write_unlock(dentry);
++
++ TraceErr(err);
++ return err;
++}
++
++static int test_dir(struct dentry *dentry, void *arg)
++{
++ return S_ISDIR(dentry->d_inode->i_mode);
++}
++
++static int refresh_dir(struct dentry *root, int sgen)
++{
++ int err, i, j, ndentry;
++ const unsigned int flags = au_hi_flags(root->d_inode, /*isdir*/1);
++ struct au_dcsub_pages dpages;
++ struct au_dpage *dpage;
++ struct dentry **dentries;
++
++ LKTRTrace("sgen %d\n", sgen);
++ SiMustWriteLock(root->d_sb);
++ DEBUG_ON(au_digen(root) != sgen);
++ DiMustWriteLock(root);
++
++ err = au_dpages_init(&dpages, GFP_KERNEL);
++ if (unlikely(err))
++ goto out;
++ err = au_dcsub_pages(&dpages, root, test_dir, NULL);
++ if (unlikely(err))
++ goto out_dpages;
++
++ DiMustNoWaiters(root);
++ IiMustNoWaiters(root->d_inode);
++ di_write_unlock(root);
++ for (i = 0; !err && i < dpages.ndpage; i++) {
++ dpage = dpages.dpages + i;
++ dentries = dpage->dentries;
++ ndentry = dpage->ndentry;
++ for (j = 0; !err && j < ndentry; j++) {
++ struct dentry *d;
++ d = dentries[j];
++ DEBUG_ON(!S_ISDIR(d->d_inode->i_mode)
++ || IS_ROOT(d)
++ || au_digen(d->d_parent) != sgen);
++ if (au_digen(d) != sgen)
++ err = do_refresh_dir(d, flags);
++ }
++ }
++ di_write_lock_child(root); /* aufs_write_lock() calls ..._child() */
++
++ out_dpages:
++ au_dpages_free(&dpages);
++ out:
++ TraceErr(err);
++ return err;
++}
++
++/* stop extra interpretation of errno in mount(8), and strange error messages */
++static int cvt_err(int err)
++{
++ TraceErr(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, do_refresh;
++ struct dentry *root;
++ struct inode *inode;
++ struct opts opts;
++ unsigned int given, dlgt;
++
++ //au_debug_on();
++ LKTRTrace("flags 0x%x, data %s, len %d\n",
++ *flags, data ? data : "NULL", data ? strlen(data) : 0);
++
++ err = 0;
++ if (unlikely(!data || !*data))
++ goto out; /* success */
++
++ err = -ENOMEM;
++ memset(&opts, 0, sizeof(opts));
++ opts.opt = (void*)__get_free_page(GFP_KERNEL);
++ //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);
++
++ /* parse it before aufs lock */
++ err = au_parse_opts(sb, data, &opts);
++ //if (LktrCond) {au_free_opts(&opts); err = -1;}
++ if (unlikely(err))
++ goto out_opts;
++
++ root = sb->s_root;
++ inode = root->d_inode;
++ i_lock(inode);
++ aufs_write_lock(root);
++
++ //DbgSleep(3);
++
++ /* au_do_opts() may return an error */
++ do_refresh = 0;
++ given = 0;
++ err = au_do_opts_remount(sb, &opts, &do_refresh, &given);
++ //if (LktrCond) err = -1;
++ au_free_opts(&opts);
++
++ if (do_refresh) {
++ int rerr;
++ struct aufs_sbinfo *sbinfo;
++
++ dlgt = au_flag_test(sb, AuFlag_DLGT);
++ au_flag_clr(sb, AuFlag_DLGT);
++ au_sigen_inc(sb);
++ au_reset_hinotify(inode, au_hi_flags(inode, /*isdir*/1));
++ sbinfo = stosi(sb);
++ sbinfo->si_failed_refresh_dirs = 0;
++ rerr = refresh_dir(root, au_sigen(sb));
++ if (unlikely(rerr)) {
++ sbinfo->si_failed_refresh_dirs = 1;
++ Warn("Refreshing directories failed, ignores (%d)\n",
++ rerr);
++ }
++ au_cpup_attr_all(inode);
++ au_flag_set(sb, dlgt);
++ }
++
++ aufs_write_unlock(root);
++ i_unlock(inode);
++ /* braces are added to stop a warning */
++ if (do_refresh) {
++ sysaufs_notify_remount();
++ }
++
++ out_opts:
++ free_page((unsigned long)opts.opt);
++ out:
++ err = cvt_err(err);
++ TraceErr(err);
++ //au_debug_off();
++ return err;
++}
++
++static struct super_operations aufs_sop = {
++ .alloc_inode = aufs_alloc_inode,
++ .destroy_inode = aufs_destroy_inode,
++ .read_inode = aufs_read_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
++};
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * at first mount time.
++ */
++
++static int alloc_sbinfo(struct super_block *sb)
++{
++ struct aufs_sbinfo *sbinfo;
++
++ TraceEnter();
++
++ 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)) {
++ kfree(sbinfo);
++ goto out;
++ }
++ rw_init_wlock(&sbinfo->si_rwsem);
++ sbinfo->si_bend = -1;
++ atomic_long_set(&sbinfo->si_xino, AUFS_FIRST_INO);
++ spin_lock_init(&sbinfo->si_plink_lock);
++ INIT_LIST_HEAD(&sbinfo->si_plink);
++ init_lvma(sbinfo);
++ sbinfo->si_generation = 0;
++ sbinfo->si_last_br_id = 0;
++ sbinfo->si_failed_refresh_dirs = 0;
++ sbinfo->si_flags = 0;
++ sbinfo->si_dirwh = AUFS_DIRWH_DEF;
++ sbinfo->si_rdcache = AUFS_RDCACHE_DEF * HZ;
++ //atomic_set(&sbinfo->si_hinotify, 0);
++ //init_waitqueue_head(&sbinfo->si_hinotify_wq);
++
++ sb->s_fs_info = sbinfo;
++ au_flag_set(sb, AuDefFlags);
++#ifdef ForceInotify
++ udba_set(sb, AuFlag_UDBA_INOTIFY);
++#endif
++#ifdef ForceDlgt
++ au_flag_set(sb, AuFlag_DLGT);
++#endif
++#ifdef ForceNoPlink
++ au_flag_clr(sb, AuFlag_PLINK);
++#endif
++ return 0; /* success */
++
++ out:
++ TraceErr(-ENOMEM);
++ return -ENOMEM;
++}
++
++static int alloc_root(struct super_block *sb)
++{
++ int err;
++ struct inode *inode;
++ struct dentry *root;
++
++ TraceEnter();
++
++ err = -ENOMEM;
++ inode = iget(sb, AUFS_ROOT_INO);
++ //if (LktrCond) {iput(inode); inode = NULL;}
++ if (unlikely(!inode))
++ goto out;
++ err = PTR_ERR(inode);
++ if (IS_ERR(inode))
++ goto out;
++ err = -ENOMEM;
++ if (unlikely(is_bad_inode(inode)))
++ goto out_iput;
++
++ 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){rw_write_unlock(&dtodi(root)->di_rwsem);err=-1;}
++ if (!err) {
++ sb->s_root = root;
++ return 0; /* success */
++ }
++ dput(root);
++ goto out; /* do not iput */
++
++ out_iput:
++ iput(inode);
++ out:
++ TraceErr(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 opts opts;
++ char *arg = raw_data;
++
++ //au_debug_on();
++ if (unlikely(!arg || !*arg)) {
++ err = -EINVAL;
++ Err("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_KERNEL);
++ //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 = alloc_sbinfo(sb);
++ //if (LktrCond) {si_write_unlock(sb);free_sbinfo(stosi(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;
++ au_init_export_op(sb);
++ //err = kobj_mount(stosi(sb));
++ //if (err)
++ //goto out_info;
++
++ err = alloc_root(sb);
++ //if (LktrCond) {rw_write_unlock(&dtodi(sb->s_root)->di_rwsem);
++ //dput(sb->s_root);sb->s_root=NULL;err=-1;}
++ if (unlikely(err)) {
++ DEBUG_ON(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_parse_opts(sb, arg, &opts);
++ //if (LktrCond) {au_free_opts(&opts); err = -1;}
++ if (unlikely(err))
++ goto out_root;
++
++ /* lock vfs_inode first, then aufs. */
++ i_lock(inode);
++ inode->i_op = &aufs_dir_iop;
++ inode->i_fop = &aufs_dir_fop;
++ aufs_write_lock(root);
++
++ sb->s_maxbytes = 0;
++ err = au_do_opts_mount(sb, &opts);
++ //if (LktrCond) err = -1;
++ au_free_opts(&opts);
++ if (unlikely(err))
++ goto out_unlock;
++ DEBUG_ON(!sb->s_maxbytes);
++
++ //DbgDentry(root);
++ aufs_write_unlock(root);
++ i_unlock(inode);
++ //DbgSb(sb);
++ goto out_opts; /* success */
++
++ out_unlock:
++ aufs_write_unlock(root);
++ i_unlock(inode);
++ out_root:
++ dput(root);
++ sb->s_root = NULL;
++ out_info:
++ free_sbinfo(stosi(sb));
++ sb->s_fs_info = NULL;
++ out_opts:
++ free_page((unsigned long)opts.opt);
++ out:
++ TraceErr(err);
++ err = cvt_err(err);
++ TraceErr(err);
++ //au_debug_off();
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
++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 aufs_sbinfo *sbinfo = stosi(mnt->mnt_sb);
++ sbinfo->si_mnt = mnt;
++ sysaufs_add(sbinfo);
++ }
++ return err;
++}
++#else
++static struct super_block *aufs_get_sb(struct file_system_type *fs_type,
++ int flags, const char *dev_name,
++ void *raw_data)
++{
++ return get_sb_nodev(fs_type, flags, raw_data, aufs_fill_super);
++}
++#endif
++
++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,
++ //no need to __module_get() and module_put().
++ .owner = THIS_MODULE,
++};
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/super.h linux-2.6.22.1/fs/aufs/super.h
+--- linux-2.6.22.1.oorig/fs/aufs/super.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/super.h 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,339 @@
++/*
++ * 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: super.h,v 1.44 2007/05/14 03:39:54 sfjro Exp $ */
++
++#ifndef __AUFS_SUPER_H__
++#define __AUFS_SUPER_H__
++
++#ifdef __KERNEL__
++
++#include <linux/fs.h>
++#include <linux/version.h>
++#include <linux/aufs_type.h>
++#include "misc.h"
++#include "sysaufs.h"
++
++#ifdef CONFIG_AUFS_SYSAUFS
++/* entries under sysfs per mount-point */
++enum {SysaufsSb_XINO, /* SysaufsSb_PLINK, */ SysaufsSb_Last};
++struct sysaufs_sbinfo {
++ au_subsys_t subsys;
++ struct sysaufs_entry array[SysaufsSb_Last];
++};
++extern sysaufs_op au_si_ops[];
++#else
++struct sysaufs_sbinfo {};
++#endif
++
++struct aufs_sbinfo {
++ struct aufs_rwsem si_rwsem;
++
++ /* branch management */
++ /* wrap around attack by superuser? No. */
++ int si_generation;
++
++ /*
++ * set true when refresh_dirs() at remount time failed.
++ * then try refreshing dirs at access time again.
++ * if it is false, refreshing dirs at access time is unnecesary
++ */
++ unsigned int si_failed_refresh_dirs:1;
++
++ aufs_bindex_t si_bend;
++ aufs_bindex_t si_last_br_id;
++ struct aufs_branch **si_branch;
++
++ /* mount flags */
++ unsigned int si_flags;
++
++ /* external inode number table */
++ atomic_long_t si_xino; // time bomb
++ //struct file *si_xino_bmap;
++
++ /* 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 rename_whtmp 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;
++
++ /* pseudo_link list */ // dirty
++ spinlock_t si_plink_lock;
++ struct list_head si_plink;
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
++ /* super_blocks list is not exported */
++ struct list_head si_list;
++ struct vfsmount *si_mnt; /* no get/put */
++#endif
++
++ /* sysfs */
++ struct sysaufs_sbinfo si_sysaufs;
++
++#ifdef CONFIG_AUFS_HINOTIFY
++ /* hinotify */
++ //atomic_t si_hinotify;
++ //wait_queue_head_t si_hinotify_wq;
++#endif
++
++#ifdef CONFIG_AUFS_ROBR
++ /* locked vma list for mmap() */ // very dirty
++ spinlock_t si_lvma_lock;
++ struct list_head si_lvma;
++#endif
++};
++
++/* an entry in a xino file */
++struct xino {
++ ino_t ino;
++ //__u32 h_gen;
++} __attribute__ ((packed));
++
++//#define AuXino_INVALID_HGEN (-1)
++
++/* ---------------------------------------------------------------------- */
++
++/* Mount flags */
++#define AuFlag_XINO 1
++#define AuFlag_ZXINO (1 << 1)
++#define AuFlag_PLINK (1 << 2)
++#define AuFlag_UDBA_NONE (1 << 3)
++#define AuFlag_UDBA_REVAL (1 << 4)
++#define AuFlag_UDBA_INOTIFY (1 << 5)
++#define AuFlag_WARN_PERM (1 << 6)
++#define AuFlag_COO_NONE (1 << 7)
++#define AuFlag_COO_LEAF (1 << 8)
++#define AuFlag_COO_ALL (1 << 9)
++#define AuFlag_ALWAYS_DIROPQ (1 << 10)
++#define AuFlag_DLGT (1 << 11)
++
++#define AuMask_UDBA (AuFlag_UDBA_NONE | AuFlag_UDBA_REVAL \
++ | AuFlag_UDBA_INOTIFY)
++#define AuMask_COO (AuFlag_COO_NONE | AuFlag_COO_LEAF \
++ | AuFlag_COO_ALL)
++
++#ifdef CONFIG_AUFS_COMPAT
++#define AuDefFlag_DIROPQ AuFlag_ALWAYS_DIROPQ
++#else
++#define AuDefFlag_DIROPQ 0
++#endif
++
++#define AuDefFlags_COMM (AuFlag_XINO | AuFlag_UDBA_REVAL | AuFlag_WARN_PERM \
++ | AuFlag_COO_NONE | AuDefFlag_DIROPQ)
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
++#define AuDefFlags (AuDefFlags_COMM | AuFlag_PLINK)
++#else
++#define AuDefFlags AuDefFlags_COMM
++#endif
++
++/* ---------------------------------------------------------------------- */
++
++/* flags for aufs_read_lock()/di_read_lock() */
++#define AUFS_D_WLOCK 1
++#define AUFS_I_RLOCK 2
++#define AUFS_I_WLOCK 4
++
++/* ---------------------------------------------------------------------- */
++
++/* super.c */
++int au_show_brs(struct seq_file *seq, struct super_block *sb);
++
++/* xino.c */
++struct file *xino_create(struct super_block *sb, char *fname, int silent,
++ struct dentry *parent);
++ino_t xino_new_ino(struct super_block *sb);
++int xino_write0(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino);
++int xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
++ struct xino *xino);
++int xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
++ struct xino *xino);
++int xino_init(struct super_block *sb, aufs_bindex_t bindex,
++ struct file *base_file, int do_test);
++struct opt_xino;
++int xino_set(struct super_block *sb, struct opt_xino *xino, int remount);
++int xino_clr(struct super_block *sb);
++struct file *xino_def(struct super_block *sb);
++
++/* sbinfo.c */
++struct aufs_sbinfo *stosi(struct super_block *sb);
++aufs_bindex_t sbend(struct super_block *sb);
++struct aufs_branch *stobr(struct super_block *sb, aufs_bindex_t bindex);
++int au_sigen(struct super_block *sb);
++int au_sigen_inc(struct super_block *sb);
++int find_bindex(struct super_block *sb, struct aufs_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 new_br_id(struct super_block *sb);
++
++/* ---------------------------------------------------------------------- */
++
++static inline const char *au_sbtype(struct super_block *sb)
++{
++ return sb->s_type->name;
++}
++
++static inline int au_is_aufs(struct super_block *sb)
++{
++ return !strcmp(au_sbtype(sb), AUFS_FSTYPE);
++}
++
++static inline int au_is_nfs(struct super_block *sb)
++{
++#if defined(CONFIG_NFS_FS) || defined(CONFIG_NFS_FS_MODULE)
++ return !strcmp(au_sbtype(sb), "nfs");
++#else
++ return 0;
++#endif
++}
++
++static inline int au_is_remote(struct super_block *sb)
++{
++ return au_is_nfs(sb);
++}
++
++#ifdef CONFIG_AUFS_EXPORT
++static inline void au_init_export_op(struct super_block *sb)
++{
++ extern struct export_operations aufs_export_op;
++ sb->s_export_op = &aufs_export_op;
++}
++
++static inline int au_is_nfsd(struct task_struct *tsk)
++{
++ return (!tsk->mm && !strcmp(tsk->comm, "nfsd"));
++}
++
++static inline void au_nfsd_lockdep_off(void)
++{
++ /* braces are added to stop a warning */
++ if (au_is_nfsd(current)) {
++ lockdep_off();
++ }
++}
++
++static inline void au_nfsd_lockdep_on(void)
++{
++ /* braces are added to stop a warning */
++ if (au_is_nfsd(current)) {
++ lockdep_on();
++ }
++}
++#else
++static inline int au_is_nfsd(struct task_struct *tsk)
++{
++ return 0;
++}
++static inline void au_init_export_op(struct super_block *sb)
++{
++ /* nothing */
++}
++#define au_nfsd_lockdep_off() /* */
++#define au_nfsd_lockdep_on() /* */
++#endif /* CONFIG_AUFS_EXPORT */
++
++static inline void init_lvma(struct aufs_sbinfo *sbinfo)
++{
++#ifdef CONFIG_AUFS_ROBR
++ spin_lock_init(&sbinfo->si_lvma_lock);
++ INIT_LIST_HEAD(&sbinfo->si_lvma);
++#else
++ /* nothing */
++#endif
++}
++
++/* limited support before 2.6.18 */
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
++static inline void au_mntget(struct super_block *sb)
++{
++ mntget(stosi(sb)->si_mnt);
++}
++
++static inline void au_mntput(struct super_block *sb)
++{
++ mntput(stosi(sb)->si_mnt);
++}
++#else
++static inline void au_mntget(struct super_block *sb)
++{
++ /* empty */
++}
++
++static inline void au_mntput(struct super_block *sb)
++{
++ /* empty */
++}
++#endif
++
++/* ---------------------------------------------------------------------- */
++
++static inline void au_flag_set(struct super_block *sb, unsigned int flag)
++{
++ //SiMustWriteLock(sb);
++ stosi(sb)->si_flags |= flag;
++}
++
++static inline void au_flag_clr(struct super_block *sb, unsigned int flag)
++{
++ //SiMustWriteLock(sb);
++ stosi(sb)->si_flags &= ~flag;
++}
++
++static inline
++unsigned int au_flag_test(struct super_block *sb, unsigned int flag)
++{
++ //SiMustAnyLock(sb);
++ return stosi(sb)->si_flags & flag;
++}
++
++static inline unsigned int au_flag_test_udba(struct super_block *sb)
++{
++ return au_flag_test(sb, AuMask_UDBA);
++}
++
++static inline unsigned int au_flag_test_coo(struct super_block *sb)
++{
++ return au_flag_test(sb, AuMask_COO);
++}
++
++/* ---------------------------------------------------------------------- */
++
++/* lock superblock. mainly for entry point functions */
++/*
++ * si_read_lock, si_write_lock,
++ * si_read_unlock, si_write_unlock, si_downgrade_lock
++ */
++SimpleRwsemFuncs(si, struct super_block *sb, stosi(sb)->si_rwsem);
++
++/* to debug easier, do not make them inlined functions */
++#define SiMustReadLock(sb) RwMustReadLock(&stosi(sb)->si_rwsem)
++#define SiMustWriteLock(sb) RwMustWriteLock(&stosi(sb)->si_rwsem)
++#define SiMustAnyLock(sb) RwMustAnyLock(&stosi(sb)->si_rwsem)
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_SUPER_H__ */
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/sysaufs.c linux-2.6.22.1/fs/aufs/sysaufs.c
+--- linux-2.6.22.1.oorig/fs/aufs/sysaufs.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/sysaufs.c 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,620 @@
++/*
++ * 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: sysaufs.c,v 1.6 2007/05/14 03:40:10 sfjro Exp $ */
++
++#include <linux/module.h>
++#include <linux/seq_file.h>
++#include <linux/sysfs.h>
++#include "aufs.h"
++
++/* ---------------------------------------------------------------------- */
++
++/* super_blocks list is not exported */
++static DEFINE_MUTEX(aufs_sbilist_mtx);
++static LIST_HEAD(aufs_sbilist);
++
++/* ---------------------------------------------------------------------- */
++
++typedef ssize_t (*rwfunc_t)(struct kobject *kobj, char *buf, loff_t offset,
++ size_t sz, struct sysaufs_args *args);
++static ssize_t sysaufs_read(struct kobject *kobj, char *buf, loff_t offset,
++ size_t sz, struct sysaufs_args *args);
++static ssize_t sysaufs_free_write(struct kobject *kobj, char *buf, loff_t
++ offset, size_t sz, struct sysaufs_args *args);
++
++#define GFunc(name, _index, func) \
++static ssize_t name(struct kobject *kobj, char *buf, loff_t offset, size_t sz) \
++{ \
++ struct sysaufs_args args = { \
++ .index = (_index), \
++ .mtx = &aufs_sbilist_mtx, \
++ .sb = NULL \
++ }; \
++ return func(kobj, buf, offset, sz, &args); \
++}
++
++#define GFuncs(name, _index) \
++ GFunc(read_##name, _index, sysaufs_read); \
++ GFunc(write_##name, _index, sysaufs_free_write);
++
++static struct super_block *find_sb_locked(struct kobject *kobj)
++{
++ struct super_block *sb;
++ struct aufs_sbinfo *sbinfo;
++
++ TraceEnter();
++ MtxMustLock(&aufs_sbilist_mtx);
++
++ sb = NULL;
++ list_for_each_entry(sbinfo, &aufs_sbilist, si_list) {
++ if (&au_subsys_to_kset(sbinfo->si_sysaufs.subsys).kobj != kobj)
++ continue;
++ sb = sbinfo->si_mnt->mnt_sb;
++ si_read_lock(sb);
++ break;
++ }
++ return sb;
++}
++
++static ssize_t sb_func(struct kobject *kobj, char *buf, loff_t offset,
++ size_t sz, struct sysaufs_args *args, rwfunc_t func)
++{
++ ssize_t err;
++
++ err = -ENOENT;
++ mutex_lock(&aufs_sbilist_mtx);
++ args->sb = find_sb_locked(kobj);
++ if (args->sb) {
++ err = func(kobj, buf, offset, sz, args);
++ si_read_unlock(args->sb);
++ }
++ mutex_unlock(&aufs_sbilist_mtx);
++ return err;
++}
++
++#define SbFunc(name, _index, func) \
++static ssize_t name(struct kobject *kobj, char *buf, loff_t offset, size_t sz) \
++{ \
++ struct sysaufs_args args = { \
++ .index = (_index), \
++ .mtx = NULL \
++ }; \
++ return sb_func(kobj, buf, offset, sz, &args, func); \
++}
++
++#define SbFuncs(name, index) \
++ SbFunc(read_##name, index, sysaufs_read); \
++ SbFunc(write_##name, index, sysaufs_free_write)
++
++static decl_subsys(aufs, NULL, NULL);
++enum {Brs, Stat, Config, _Last};
++static struct sysaufs_entry g_array[_Last];
++GFuncs(brs, Brs);
++GFuncs(stat, Stat);
++GFuncs(config, Config);
++
++SbFuncs(xino, SysaufsSb_XINO);
++
++#define SetEntry(e, _name, init_size, _ops) \
++ do { \
++ (e)->attr.attr.name = #_name; \
++ (e)->attr.attr.owner = THIS_MODULE; \
++ (e)->attr.attr.mode = S_IRUGO | S_IWUSR; \
++ (e)->attr.read = read_##_name; \
++ (e)->attr.write = write_##_name; \
++ (e)->allocated = init_size; \
++ (e)->err = -1; \
++ (e)->ops = _ops; \
++ } while (0)
++
++#define Priv(e) (e)->attr.private
++#define Allocated(e) (e)->allocated
++#define Len(e) (e)->attr.size
++#define Name(e) attr_name((e)->attr)
++
++/* ---------------------------------------------------------------------- */
++
++static void free_entry(struct sysaufs_entry *e)
++{
++ MtxMustLock(&aufs_sbilist_mtx);
++ DEBUG_ON(!Priv(e));
++
++ if (Allocated(e) > 0)
++ kfree(Priv(e));
++ else
++ free_pages((unsigned long)Priv(e), -Allocated(e));
++ Priv(e) = NULL;
++ Len(e) = 0;
++}
++
++static void free_entries(void)
++{
++ static int a[] = {Brs, -1};
++ int *p = a;
++
++ MtxMustLock(&aufs_sbilist_mtx);
++
++ while (*p >= 0) {
++ if (Priv(g_array + *p))
++ free_entry(g_array + *p);
++ p++;
++ }
++}
++
++static int alloc_entry(struct sysaufs_entry *e)
++{
++ MtxMustLock(&aufs_sbilist_mtx);
++ DEBUG_ON(Priv(e));
++ //Dbg("%d\n", Allocated(e));
++
++ if (Allocated(e) > 0)
++ Priv(e) = kmalloc(Allocated(e), GFP_KERNEL);
++ else
++ Priv(e) = (void*)__get_free_pages(GFP_KERNEL, -Allocated(e));
++ if (Priv(e))
++ return 0;
++ return -ENOMEM;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static void unreg(au_subsys_t *subsys, struct sysaufs_entry *a, int n,
++ au_subsys_t *parent)
++{
++ int i;
++
++ TraceEnter();
++
++ for (i = 0; i < n; i++, a++)
++ if (!a->err) {
++ sysfs_remove_bin_file
++ (&au_subsys_to_kset(*subsys).kobj, &a->attr);
++ if (Priv(a))
++ free_entry(a);
++ }
++
++ subsystem_unregister(subsys);
++ subsys_put(parent);
++}
++
++static int reg(au_subsys_t *subsys, struct sysaufs_entry *a, int n,
++ au_subsys_t *parent)
++{
++ int err, i;
++
++ TraceEnter();
++
++ subsys_get(parent);
++ kobj_set_kset_s(&au_subsys_to_kset(*subsys), *parent);
++ err = subsystem_register(subsys);
++ if (unlikely(err))
++ goto out;
++
++ for (i = 0; !err && i < n; i++)
++ err = a[i].err = sysfs_create_bin_file
++ (&au_subsys_to_kset(*subsys).kobj, &a[i].attr);
++ if (unlikely(err))
++ unreg(subsys, a, n, parent);
++
++ out:
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++#define SbSetEntry(index, name, init_size) \
++ SetEntry(sa->array + index, name, init_size, au_si_ops);
++
++void sysaufs_add(struct aufs_sbinfo *sbinfo)
++{
++ int err;
++ struct sysaufs_sbinfo *sa = &sbinfo->si_sysaufs;
++
++ TraceEnter();
++
++ mutex_lock(&aufs_sbilist_mtx);
++ list_add_tail(&sbinfo->si_list, &aufs_sbilist);
++ free_entries();
++
++ memset(sa, 0, sizeof(*sa));
++ SbSetEntry(SysaufsSb_XINO, xino, 128);
++ err = kobject_set_name(&au_subsys_to_kset(sa->subsys).kobj, "%p",
++ sbinfo->si_mnt->mnt_sb);
++ if (!err)
++ err = reg(&sa->subsys, sa->array, ARRAY_SIZE(sa->array),
++ &aufs_subsys);
++ if (unlikely(err))
++ Warn("failed adding sysfs (%d)\n", err);
++
++ mutex_unlock(&aufs_sbilist_mtx);
++}
++
++void sysaufs_del(struct aufs_sbinfo *sbinfo)
++{
++ struct sysaufs_sbinfo *sa = &sbinfo->si_sysaufs;
++
++ TraceEnter();
++
++ mutex_lock(&aufs_sbilist_mtx);
++ unreg(&sa->subsys, sa->array, ARRAY_SIZE(sa->array), &aufs_subsys);
++ list_del(&sbinfo->si_list);
++ free_entries();
++ mutex_unlock(&aufs_sbilist_mtx);
++}
++
++void sysaufs_notify_remount(void)
++{
++ mutex_lock(&aufs_sbilist_mtx);
++ free_entries();
++ mutex_unlock(&aufs_sbilist_mtx);
++}
++
++/* ---------------------------------------------------------------------- */
++
++static int make_brs(struct seq_file *seq, struct sysaufs_args *args,
++ int *do_size)
++{
++ int err;
++ struct aufs_sbinfo *sbinfo;
++
++ TraceEnter();
++ MtxMustLock(&aufs_sbilist_mtx);
++ DEBUG_ON(args->index != Brs);
++
++ err = 0;
++ list_for_each_entry(sbinfo, &aufs_sbilist, si_list) {
++ struct super_block *sb;
++ struct dentry *root;
++ struct vfsmount *mnt;
++
++ sb = sbinfo->si_mnt->mnt_sb;
++ root = sb->s_root;
++ aufs_read_lock(root, !AUFS_I_RLOCK);
++ mnt = sbinfo->si_mnt;
++ err = seq_escape
++ (seq, mnt->mnt_devname ? mnt->mnt_devname : "none",
++ au_esc_chars);
++ if (!err)
++ err = seq_putc(seq, ' ');
++ if (!err)
++ err = seq_path(seq, mnt, root, au_esc_chars);
++ if (err > 0)
++ err = seq_printf(seq, " %p br:", sb);
++ if (!err)
++ err = au_show_brs(seq, sb);
++ aufs_read_unlock(root, !AUFS_I_RLOCK);
++ if (!err)
++ err = seq_putc(seq, '\n');
++ else
++ break;
++ }
++
++ TraceErr(err);
++ return err;
++}
++
++static int make_config(struct seq_file *seq, struct sysaufs_args *args,
++ int *do_size)
++{
++ int err;
++
++ TraceEnter();
++ DEBUG_ON(args->index != Config);
++
++#ifdef CONFIG_AUFS
++ err = seq_puts(seq, "CONFIG_AUFS=y\n");
++#else
++ err = seq_puts(seq, "CONFIG_AUFS=m\n");
++#endif
++
++#define puts(m, v) \
++ if (!err) err = seq_puts(seq, "CONFIG_AUFS_" #m "=" #v "\n")
++#define puts_bool(m) puts(m, y)
++#define puts_mod(m) puts(m, m)
++
++#ifdef CONFIG_AUFS_FAKE_DM
++ puts_bool(FAKE_DM);
++#endif
++#ifdef CONFIG_AUFS_BRANCH_MAX_127
++ puts_bool(BRANCH_MAX_127);
++#elif defined(CONFIG_AUFS_BRANCH_MAX_511)
++ puts_bool(BRANCH_MAX_511);
++#elif defined(CONFIG_AUFS_BRANCH_MAX_1023)
++ puts_bool(BRANCH_MAX_1023);
++#elif defined(CONFIG_AUFS_BRANCH_MAX_32767)
++ puts_bool(BRANCH_MAX_32767);
++#endif
++ puts_bool(SYSAUFS);
++#ifdef CONFIG_AUFS_HINOTIFY
++ puts_bool(HINOTIFY);
++#endif
++#ifdef CONFIG_AUFS_EXPORT
++ puts_bool(EXPORT);
++#endif
++#ifdef CONFIG_AUFS_ROBR
++ puts_bool(ROBR);
++#endif
++#ifdef CONFIG_AUFS_DLGT
++ puts_bool(DLGT);
++#endif
++#ifdef CONFIG_AUFS_LHASH_PATCH
++ puts_bool(LHASH_PATCH);
++#endif
++#ifdef CONFIG_AUFS_KSIZE_PATCH
++ puts_bool(KSIZE_PATCH);
++#endif
++#ifdef CONFIG_AUFS_DEBUG
++ puts_bool(DEBUG);
++#endif
++#ifdef CONFIG_AUFS_COMPAT
++ puts_bool(COMPAT);
++#endif
++
++#undef puts_bool
++#undef puts
++
++ TraceErr(err);
++ return err;
++}
++
++static int make_stat(struct seq_file *seq, struct sysaufs_args *args,
++ int *do_size)
++{
++ int err, i;
++
++ TraceEnter();
++ DEBUG_ON(args->index != Stat);
++
++ *do_size = 0;
++ err = seq_puts(seq, "wkq max_busy:");
++ for (i = 0; !err && i < aufs_nwkq; i++)
++ err = seq_printf(seq, " %u", au_wkq[i].max_busy);
++ if (!err)
++ err = seq_printf(seq, ", %u(generic)\n",
++ au_wkq[aufs_nwkq].max_busy);
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static int make(struct sysaufs_entry *e, struct sysaufs_args *args,
++ int *do_size)
++
++{
++ int err;
++ struct seq_file *seq;
++
++ TraceEnter();
++ DEBUG_ON(Priv(e));
++ MtxMustLock(&aufs_sbilist_mtx);
++
++ err = -ENOMEM;
++ seq = kzalloc(sizeof(*seq), GFP_KERNEL);
++ if (unlikely(!seq))
++ goto out;
++
++ Len(e) = 0;
++ while (1) {
++ err = alloc_entry(e);
++ if (unlikely(err))
++ break;
++
++ //mutex_init(&seq.lock);
++ seq->buf = Priv(e);
++ seq->count = 0;
++ seq->size = Allocated(e);
++ if (unlikely(Allocated(e) <= 0))
++ seq->size = PAGE_SIZE << -Allocated(e);
++
++ err = e->ops[args->index](seq, args, do_size);
++ if (!err) {
++ Len(e) = seq->count;
++ break; /* success */
++ }
++
++ free_entry(e);
++ if (Allocated(e) > 0) {
++ Allocated(e) <<= 1;
++ if (unlikely(Allocated(e) >= (int)PAGE_SIZE))
++ Allocated(e) = 0;
++ } else
++ Allocated(e)--;
++ //Dbg("%d\n", Allocated(e));
++ }
++ kfree(seq);
++
++ out:
++ TraceErr(err);
++ return err;
++}
++
++/* why does sysfs pass my parent kobject? */
++static struct dentry *find_me(struct dentry *parent, struct sysaufs_entry *e)
++{
++#if 1
++ struct dentry *dentry;
++ const char *name = Name(e);
++ const unsigned int len = strlen(name);
++
++ //Dbg("%.*s\n", DLNPair(parent));
++ spin_lock(&dcache_lock);
++ list_for_each_entry(dentry, &parent->d_subdirs, D_CHILD) {
++ //Dbg("%.*s\n", DLNPair(dentry));
++ if (len == dentry->d_name.len
++ && !strcmp(dentry->d_name.name, name)) {
++ spin_unlock(&dcache_lock);
++ return dentry;
++ }
++ }
++ spin_unlock(&dcache_lock);
++#endif
++ return NULL;
++}
++
++static ssize_t sysaufs_read(struct kobject *kobj, char *buf, loff_t offset,
++ size_t sz, struct sysaufs_args *args)
++{
++ ssize_t err;
++ loff_t len;
++ struct dentry *d;
++ struct sysaufs_entry *e;
++ int do_size;
++
++ LKTRTrace("{%d, %p}, offset %Ld, sz %lu\n",
++ args->index, args->sb, offset, (unsigned long)sz);
++
++ if (unlikely(!sz))
++ return 0;
++
++ err = 0;
++ d = NULL;
++ e = g_array + args->index;
++ if (args->sb)
++ e = stosi(args->sb)->si_sysaufs.array + args->index;
++
++ do_size = 1;
++ if (args->mtx)
++ mutex_lock(args->mtx);
++ if (unlikely(!Priv(e))) {
++ err = make(e, args, &do_size);
++ DEBUG_ON(Len(e) > INT_MAX);
++ if (do_size) {
++ d = find_me(kobj->dentry, e);
++ if (d)
++ i_size_write(d->d_inode, Len(e));
++ }
++ }
++
++ if (!err) {
++ err = len = Len(e) - offset;
++ LKTRTrace("%Ld\n", len);
++ if (len > 0) {
++ if (len > sz)
++ err = sz;
++ memcpy(buf, Priv(e) + offset, err);
++ }
++
++ if (!do_size)
++ free_entry(e);
++ }
++ if (args->mtx)
++ mutex_unlock(args->mtx);
++
++ TraceErr(err);
++ return err;
++}
++
++static ssize_t sysaufs_free_write(struct kobject *kobj, char *buf,
++ loff_t offset, size_t sz,
++ struct sysaufs_args *args)
++{
++ struct dentry *d;
++ int allocated, len;
++ struct sysaufs_entry *e;
++
++ LKTRTrace("{%d, %p}\n", args->index, args->sb);
++
++ e = g_array + args->index;
++ if (args->sb)
++ e = stosi(args->sb)->si_sysaufs.array + args->index;
++
++ if (args->mtx)
++ mutex_lock(args->mtx);
++ if (Priv(e)) {
++ allocated = Allocated(e);
++ if (unlikely(allocated <= 0))
++ allocated = PAGE_SIZE << -allocated;
++ allocated >>= 1;
++ len = Len(e);
++
++ free_entry(e);
++ if (unlikely(len <= allocated)) {
++ if (Allocated(e) >= 0)
++ Allocated(e) = allocated;
++ else
++ Allocated(e)++;
++ }
++
++ d = find_me(kobj->dentry, e);
++ if (d && i_size_read(d->d_inode))
++ i_size_write(d->d_inode, 0);
++ }
++ if (args->mtx)
++ mutex_unlock(args->mtx);
++
++ return sz;
++}
++
++static sysaufs_op g_ops[] = {
++ [Brs] = make_brs,
++ [Stat] = make_stat,
++ [Config] = make_config
++};
++
++/* ---------------------------------------------------------------------- */
++
++#define GSetEntry(index, name, init_size) \
++ SetEntry(g_array + index, name, init_size, g_ops)
++
++int __init sysaufs_init(void)
++{
++ int err;
++
++ GSetEntry(Brs, brs, 128);
++ GSetEntry(Stat, stat, 32);
++ GSetEntry(Config, config, 256);
++ err = reg(&aufs_subsys, g_array, ARRAY_SIZE(g_array), &fs_subsys);
++ TraceErr(err);
++ return err;
++}
++
++void __exit sysaufs_fin(void)
++{
++ mutex_lock(&aufs_sbilist_mtx);
++ unreg(&aufs_subsys, g_array, ARRAY_SIZE(g_array), &fs_subsys);
++ mutex_unlock(&aufs_sbilist_mtx);
++}
++
++/* ---------------------------------------------------------------------- */
++
++#ifdef DbgDlgt
++int is_branch(struct super_block *h_sb)
++{
++ int found = 0;
++ struct aufs_sbinfo *sbinfo;
++
++ //Dbg("here\n");
++ mutex_lock(&aufs_sbilist_mtx);
++ list_for_each_entry(sbinfo, &aufs_sbilist, si_list) {
++ aufs_bindex_t bindex, bend;
++ struct super_block *sb;
++
++ sb = sbinfo->si_mnt->mnt_sb;
++ si_read_lock(sb);
++ bend = sbend(sb);
++ for (bindex = 0; !found && bindex <= bend; bindex++)
++ found = (h_sb == sbr_sb(sb, bindex));
++ si_read_unlock(sb);
++ }
++ mutex_unlock(&aufs_sbilist_mtx);
++ return found;
++}
++#endif
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/sysaufs.h linux-2.6.22.1/fs/aufs/sysaufs.h
+--- linux-2.6.22.1.oorig/fs/aufs/sysaufs.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/sysaufs.h 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,83 @@
++/*
++ * 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: sysaufs.h,v 1.3 2007/05/14 06:27:18 sfjro Exp $ */
++
++#ifndef __SYSAUFS_H__
++#define __SYSAUFS_H__
++
++#ifdef __KERNEL__
++
++#include <linux/seq_file.h>
++#include <linux/sysfs.h>
++#include <linux/version.h>
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
++typedef struct kset au_subsys_t;
++#define au_subsys_to_kset(subsys) (subsys)
++#else
++typedef struct subsystem au_subsys_t;
++#define au_subsys_to_kset(subsys) ((subsys).kset)
++#endif
++
++/* ---------------------------------------------------------------------- */
++
++/* arguments for an entry under sysfs */
++struct sysaufs_args {
++ int index;
++ struct mutex *mtx;
++ struct super_block *sb;
++};
++
++typedef int (*sysaufs_op)(struct seq_file *seq, struct sysaufs_args *args,
++ int *do_size);
++
++/* an entry under sysfs */
++struct sysaufs_entry {
++ struct bin_attribute attr;
++ int allocated; /* zero minus means pages */
++ int err;
++ sysaufs_op *ops;
++};
++
++/* ---------------------------------------------------------------------- */
++
++struct aufs_sbinfo;
++#ifdef CONFIG_AUFS_SYSAUFS
++void sysaufs_add(struct aufs_sbinfo *sbinfo);
++void sysaufs_del(struct aufs_sbinfo *sbinfo);
++int __init sysaufs_init(void);
++void sysaufs_fin(void);
++void sysaufs_notify_remount(void);
++#else
++static inline void sysaufs_add(struct aufs_sbinfo *sbinfo)
++{
++ /* nothing */
++}
++
++static inline void sysaufs_del(struct aufs_sbinfo *sbinfo)
++{
++ /* nothing */
++}
++#define sysaufs_init() 0
++#define sysaufs_fin() /* */
++#define sysaufs_notify_remount() /* */
++#endif /* CONFIG_AUFS_SYSAUFS */
++
++#endif /* __KERNEL__ */
++#endif /* __SYSAUFS_H__ */
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/vdir.c linux-2.6.22.1/fs/aufs/vdir.c
+--- linux-2.6.22.1.oorig/fs/aufs/vdir.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/vdir.c 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,802 @@
++/*
++ * 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: vdir.c,v 1.22 2007/05/14 03:38:52 sfjro Exp $ */
++
++#include "aufs.h"
++
++static int calc_size(int namelen)
++{
++ int sz;
++
++ sz = sizeof(struct aufs_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
++ }
++
++ DEBUG_ON(sz % sizeof(ino_t));
++ return sz;
++}
++
++static int set_deblk_end(union aufs_deblk_p *p, union aufs_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 aufs_deblk_p *p, union aufs_deblk_p *deblk_end)
++{
++ if (calc_size(0) <= deblk_end->p - p->p)
++ return !p->de->de_str.len;
++ return 1;
++}
++
++static aufs_deblk_t *last_deblk(struct aufs_vdir *vdir)
++{
++ return vdir->vd_deblk[vdir->vd_nblk - 1];
++}
++
++void nhash_init(struct aufs_nhash *nhash)
++{
++ int i;
++ for (i = 0; i < AUFS_NHASH_SIZE; i++)
++ INIT_HLIST_HEAD(nhash->heads + i);
++}
++
++struct aufs_nhash *nhash_new(gfp_t gfp)
++{
++ struct aufs_nhash *nhash;
++
++ nhash = kmalloc(sizeof(*nhash), gfp);
++ if (nhash) {
++ nhash_init(nhash);
++ return nhash;
++ }
++ return ERR_PTR(-ENOMEM);
++}
++
++void nhash_del(struct aufs_nhash *nhash)
++{
++ nhash_fin(nhash);
++ kfree(nhash);
++}
++
++void nhash_move(struct aufs_nhash *dst, struct aufs_nhash *src)
++{
++ int i;
++
++ TraceEnter();
++
++ //DbgWhlist(src);
++ *dst = *src;
++ for (i = 0; i < AUFS_NHASH_SIZE; 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 nhash_fin(struct aufs_nhash *whlist)
++{
++ int i;
++ struct hlist_head *head;
++ struct aufs_wh *tpos;
++ struct hlist_node *pos, *n;
++
++ TraceEnter();
++
++ for (i = 0; i < AUFS_NHASH_SIZE; i++) {
++ head = whlist->heads + i;
++ hlist_for_each_entry_safe(tpos, pos, n, head, wh_hash) {
++ //hlist_del(pos);
++ kfree(tpos);
++ }
++ }
++}
++
++int is_longer_wh(struct aufs_nhash *whlist, aufs_bindex_t btgt, int limit)
++{
++ int n, i;
++ struct hlist_head *head;
++ struct aufs_wh *tpos;
++ struct hlist_node *pos;
++
++ LKTRTrace("limit %d\n", limit);
++ //return 1;
++
++ n = 0;
++ for (i = 0; i < AUFS_NHASH_SIZE; 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;
++}
++
++/* returns found(true) or not */
++int test_known_wh(struct aufs_nhash *whlist, char *name, int namelen)
++{
++ struct hlist_head *head;
++ struct aufs_wh *tpos;
++ struct hlist_node *pos;
++ struct aufs_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 append_wh(struct aufs_nhash *whlist, char *name, int namelen,
++ aufs_bindex_t bindex)
++{
++ int err;
++ struct aufs_destr *str;
++ struct aufs_wh *wh;
++
++ LKTRTrace("%.*s\n", namelen, name);
++
++ err = -ENOMEM;
++ wh = kmalloc(sizeof(*wh) + namelen, GFP_KERNEL);
++ if (unlikely(!wh))
++ goto out;
++ err = 0;
++ wh->wh_bindex = bindex;
++ 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:
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++void free_vdir(struct aufs_vdir *vdir)
++{
++ aufs_deblk_t **deblk;
++
++ TraceEnter();
++
++ deblk = vdir->vd_deblk;
++ while (vdir->vd_nblk--) {
++ kfree(*deblk);
++ deblk++;
++ }
++ kfree(vdir->vd_deblk);
++ cache_free_vdir(vdir);
++}
++
++static int append_deblk(struct aufs_vdir *vdir)
++{
++ int err, sz, i;
++ aufs_deblk_t **o;
++ union aufs_deblk_p p, deblk_end;
++
++ TraceEnter();
++
++ 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);
++ DEBUG_ON(err);
++ }
++
++ out:
++ TraceErr(err);
++ return err;
++}
++
++static struct aufs_vdir *alloc_vdir(void)
++{
++ struct aufs_vdir *vdir;
++ int err;
++
++ TraceEnter();
++
++ err = -ENOMEM;
++ vdir = 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:
++ cache_free_vdir(vdir);
++ out:
++ vdir = ERR_PTR(err);
++ TraceErrPtr(vdir);
++ return vdir;
++}
++
++static int reinit_vdir(struct aufs_vdir *vdir)
++{
++ int err;
++ union aufs_deblk_p p, deblk_end;
++
++ TraceEnter();
++
++ 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);
++ DEBUG_ON(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 aufs_nhash *dehlist)
++{
++ int i;
++ struct hlist_head *head;
++ struct aufs_dehstr *tpos;
++ struct hlist_node *pos, *n;
++
++ TraceEnter();
++
++ for (i = 0; i < AUFS_NHASH_SIZE; i++) {
++ head = dehlist->heads + i;
++ hlist_for_each_entry_safe(tpos, pos, n, head, hash) {
++ //hlist_del(pos);
++ cache_free_dehstr(tpos);
++ }
++ }
++}
++
++/* returns found(true) or not */
++static int test_known(struct aufs_nhash *delist, char *name, int namelen)
++{
++ struct hlist_head *head;
++ struct aufs_dehstr *tpos;
++ struct hlist_node *pos;
++ struct aufs_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 aufs_vdir *vdir, char *name, int namelen, ino_t ino,
++ unsigned int d_type, struct aufs_nhash *delist)
++{
++ int err, sz;
++ union aufs_deblk_p p, *room, deblk_end;
++ struct aufs_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;
++ DEBUG_ON(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();
++ DEBUG_ON(room->p != p.p);
++ }
++
++ err = -ENOMEM;
++ dehstr = 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:
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++struct fillvdir_arg {
++ struct file *file;
++ struct aufs_vdir *vdir;
++ struct aufs_nhash *delist;
++ struct aufs_nhash *whlist;
++ aufs_bindex_t bindex;
++ int err;
++ int called;
++};
++
++static int fillvdir(void *__arg, const char *__name, int namelen, loff_t offset,
++ filldir_ino_t h_ino, unsigned int d_type)
++{
++ struct fillvdir_arg *arg = __arg;
++ char *name = (void*)__name;
++ aufs_bindex_t bindex, bend;
++ struct xino xino;
++ struct super_block *sb;
++
++ 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;
++ arg->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)
++ || test_known_wh(arg->whlist + bindex, name,
++ namelen))
++ goto out; /* already exists or whiteouted */
++
++ arg->err = xino_read(sb, bend, h_ino, &xino);
++ if (!arg->err && !xino.ino) {
++ //struct inode *h_inode;
++ xino.ino = xino_new_ino(sb);
++ if (unlikely(!xino.ino))
++ arg->err = -EIO;
++#if 0
++ //xino.h_gen = AuXino_INVALID_HGEN;
++ h_inode = ilookup(sbr_sb(sb, bend), h_ino);
++ if (h_inode) {
++ if (!is_bad_inode(h_inode)) {
++ xino.h_gen = h_inode->i_generation;
++ WARN_ON(xino.h_gen == AuXino_INVALID_HGEN);
++ }
++ iput(h_inode);
++ }
++#endif
++ arg->err = xino_write(sb, bend, h_ino, &xino);
++ }
++ if (!arg->err)
++ arg->err = append_de(arg->vdir, name, namelen, xino.ino,
++ d_type, arg->delist + bend);
++ } else {
++ name += AUFS_WH_PFX_LEN;
++ namelen -= AUFS_WH_PFX_LEN;
++ for (bindex = 0; bindex < bend; bindex++)
++ if (test_known_wh(arg->whlist + bend, name, namelen))
++ goto out; /* already whiteouted */
++ arg->err = append_wh(arg->whlist + bend, name, namelen, bend);
++ }
++
++ out:
++ if (!arg->err)
++ arg->vdir->vd_jiffy = jiffies;
++ //smp_mb();
++ TraceErr(arg->err);
++ return arg->err;
++}
++
++static int read_vdir(struct file *file, int may_read)
++{
++ int err, do_read, dlgt;
++ struct dentry *dentry;
++ struct inode *inode;
++ struct aufs_vdir *vdir, *allocated;
++ unsigned long expire;
++ struct fillvdir_arg arg;
++ aufs_bindex_t bindex, bend, bstart;
++ struct super_block *sb;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s, may %d\n", DLNPair(dentry), may_read);
++ FiMustWriteLock(file);
++ inode = dentry->d_inode;
++ IMustLock(inode);
++ IiMustWriteLock(inode);
++ DEBUG_ON(!S_ISDIR(inode->i_mode));
++
++ err = 0;
++ allocated = NULL;
++ do_read = 0;
++ sb = inode->i_sb;
++ expire = stosi(sb)->si_rdcache;
++ vdir = ivdir(inode);
++ if (!vdir) {
++ DEBUG_ON(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;
++ }
++ //DbgVdir(vdir); goto out;
++
++ if (!do_read)
++ return 0; /* success */
++
++ err = -ENOMEM;
++ bend = fbend(file);
++ arg.delist = kmalloc(sizeof(*arg.delist) * (bend + 1), GFP_KERNEL);
++ if (unlikely(!arg.delist))
++ goto out_vdir;
++ arg.whlist = kmalloc(sizeof(*arg.whlist) * (bend + 1), GFP_KERNEL);
++ if (unlikely(!arg.whlist))
++ goto out_delist;
++ err = 0;
++ for (bindex = 0; bindex <= bend; bindex++) {
++ nhash_init(arg.delist + bindex);
++ nhash_init(arg.whlist + bindex);
++ }
++
++ dlgt = need_dlgt(sb);
++ arg.file = file;
++ arg.vdir = vdir;
++ bstart = fbstart(file);
++ for (bindex = bstart; !err && bindex <= bend; bindex++) {
++ struct file *hf;
++ struct inode *h_inode;
++
++ hf = au_h_fptr_i(file, bindex);
++ if (!hf)
++ continue;
++
++ h_inode = hf->f_dentry->d_inode;
++ //hf->f_pos = 0;
++ arg.bindex = bindex;
++ do {
++ arg.err = 0;
++ arg.called = 0;
++ //smp_mb();
++ err = vfsub_readdir(hf, fillvdir, &arg, dlgt);
++ if (err >= 0)
++ err = arg.err;
++ } while (!err && arg.called);
++ }
++
++ for (bindex = bstart; bindex <= bend; bindex++) {
++ free_dehlist(arg.delist + bindex);
++ nhash_fin(arg.whlist + bindex);
++ }
++ kfree(arg.whlist);
++
++ out_delist:
++ kfree(arg.delist);
++ out_vdir:
++ 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)
++ set_ivdir(inode, allocated);
++ } else if (allocated)
++ free_vdir(allocated);
++ //DbgVdir(vdir); goto out;
++
++ out:
++ TraceErr(err);
++ return err;
++}
++
++static int copy_vdir(struct aufs_vdir *tgt, struct aufs_vdir *src)
++{
++ int err, i, rerr, n;
++
++ TraceEnter();
++ DEBUG_ON(tgt->vd_nblk != 1);
++ //DbgVdir(tgt);
++
++ err = -ENOMEM;
++ if (tgt->vd_nblk < src->vd_nblk) {
++ aufs_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;
++ }
++
++ n = tgt->vd_nblk = src->vd_nblk;
++ memcpy(tgt->vd_deblk[0], src->vd_deblk[0], AUFS_DEBLK_SIZE);
++ //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(AUFS_DEBLK_SIZE, GFP_KERNEL);
++ if (tgt->vd_deblk[i])
++ memcpy(tgt->vd_deblk[i], src->vd_deblk[i],
++ AUFS_DEBLK_SIZE);
++ else
++ goto out;
++ }
++ //smp_mb();
++ //DbgVdir(tgt);
++ return 0; /* success */
++
++ out:
++ rerr = reinit_vdir(tgt);
++ BUG_ON(rerr);
++ TraceErr(err);
++ return err;
++}
++
++int au_init_vdir(struct file *file)
++{
++ int err;
++ struct dentry *dentry;
++ struct inode *inode;
++ struct aufs_vdir *vdir_cache, *allocated;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s, pos %Ld\n", DLNPair(dentry), file->f_pos);
++ FiMustWriteLock(file);
++ inode = dentry->d_inode;
++ IiMustWriteLock(inode);
++ DEBUG_ON(!S_ISDIR(inode->i_mode));
++
++ err = read_vdir(file, !file->f_pos);
++ if (unlikely(err))
++ goto out;
++ //DbgVdir(ivdir(inode)); goto out;
++
++ allocated = NULL;
++ vdir_cache = 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; DbgVdir(vdir_cache); goto out;
++
++ err = copy_vdir(vdir_cache, ivdir(inode));
++ if (!err) {
++ file->f_version = inode->i_version;
++ if (allocated)
++ set_fvdir_cache(file, allocated);
++ } else if (allocated)
++ free_vdir(allocated);
++
++ out:
++ TraceErr(err);
++ return err;
++}
++
++static loff_t calc_offset(struct aufs_vdir *vdir)
++{
++ loff_t offset;
++ union aufs_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 aufs_vdir *vdir_cache;
++ loff_t offset;
++ union aufs_deblk_p p, deblk_end;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s, pos %Ld\n", DLNPair(dentry), file->f_pos);
++ vdir_cache = fvdir_cache(file);
++ DEBUG_ON(!vdir_cache);
++ //DbgVdir(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 / AUFS_DEBLK_SIZE;
++ LKTRTrace("i %d\n", i);
++ if (i >= vdir_cache->vd_nblk)
++ goto out;
++
++ n = vdir_cache->vd_nblk;
++ //DbgVdir(vdir_cache);
++ for (; i < n; i++) {
++ p.deblk = vdir_cache->vd_deblk[i];
++ deblk_end.deblk = p.deblk + 1;
++ offset = i * AUFS_DEBLK_SIZE;
++ 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();
++ //DbgVdir(vdir_cache);
++ TraceErr(!valid);
++ return valid;
++}
++
++int au_fill_de(struct file *file, void *dirent, filldir_t filldir)
++{
++ int err, l;
++ struct dentry *dentry;
++ struct aufs_vdir *vdir_cache;
++ struct aufs_de *de;
++ union aufs_deblk_p deblk_end;
++
++ dentry = file->f_dentry;
++ LKTRTrace("%.*s, pos %Ld\n", DLNPair(dentry), file->f_pos);
++ vdir_cache = fvdir_cache(file);
++ DEBUG_ON(!vdir_cache);
++ //DbgVdir(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)) {
++ TraceErr(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;
++}
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/vfsub.c linux-2.6.22.1/fs/aufs/vfsub.c
+--- linux-2.6.22.1.oorig/fs/aufs/vfsub.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/vfsub.c 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,665 @@
++/*
++ * 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: vfsub.c,v 1.5 2007/04/23 00:55:06 sfjro Exp $ */
++// I'm going to slightly mad
++
++#include "aufs.h"
++
++/* ---------------------------------------------------------------------- */
++
++#ifdef CONFIG_AUFS_DLGT
++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;
++ struct permission_args args = {
++ .errp = &err,
++ .inode = inode,
++ .mask = mask,
++ .nd = nd
++ };
++ au_wkq_wait(call_permission, &args, /*dlgt*/1);
++ 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;
++ struct create_args args = {
++ .errp = &err,
++ .dir = dir,
++ .dentry = dentry,
++ .mode = mode,
++ .nd = nd
++ };
++ au_wkq_wait(call_create, &args, /*dlgt*/1);
++ 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;
++ struct symlink_args args = {
++ .errp = &err,
++ .dir = dir,
++ .dentry = dentry,
++ .symname = symname,
++ .mode = mode
++ };
++ au_wkq_wait(call_symlink, &args, /*dlgt*/1);
++ 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;
++ struct mknod_args args = {
++ .errp = &err,
++ .dir = dir,
++ .dentry = dentry,
++ .mode = mode,
++ .dev = dev
++ };
++ au_wkq_wait(call_mknod, &args, /*dlgt*/1);
++ 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;
++ struct mkdir_args args = {
++ .errp = &err,
++ .dir = dir,
++ .dentry = dentry,
++ .mode = mode
++ };
++ au_wkq_wait(call_mkdir, &args, /*dlgt*/1);
++ 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;
++ struct link_args args = {
++ .errp = &err,
++ .src_dentry = src_dentry,
++ .dir = dir,
++ .dentry = dentry
++ };
++ au_wkq_wait(call_link, &args, /*dlgt*/1);
++ return err;
++ }
++}
++
++struct rename_args {
++ int *errp;
++ struct inode *src_dir, *dir;
++ struct dentry *src_dentry, *dentry;
++};
++
++static void call_rename(void *args)
++{
++ struct rename_args *a = args;
++ *a->errp = do_vfsub_rename(a->src_dir, a->src_dentry, a->dir,
++ a->dentry);
++}
++
++int vfsub_rename(struct inode *src_dir, struct dentry *src_dentry,
++ struct inode *dir, struct dentry *dentry, int dlgt)
++{
++ if (!dlgt)
++ return do_vfsub_rename(src_dir, src_dentry, dir, dentry);
++ else {
++ int err;
++ struct rename_args args = {
++ .errp = &err,
++ .src_dir = src_dir,
++ .src_dentry = src_dentry,
++ .dir = dir,
++ .dentry = dentry
++ };
++ au_wkq_wait(call_rename, &args, /*dlgt*/1);
++ return err;
++ }
++}
++
++struct rmdir_args {
++ int *errp;
++ struct inode *dir;
++ struct dentry *dentry;
++};
++
++static void call_rmdir(void *args)
++{
++ struct rmdir_args *a = args;
++ *a->errp = do_vfsub_rmdir(a->dir, a->dentry);
++}
++
++int vfsub_rmdir(struct inode *dir, struct dentry *dentry, int dlgt)
++{
++ if (!dlgt)
++ return do_vfsub_rmdir(dir, dentry);
++ else {
++ int err;
++ struct rmdir_args args = {
++ .errp = &err,
++ .dir = dir,
++ .dentry = dentry
++ };
++ au_wkq_wait(call_rmdir, &args, /*dlgt*/1);
++ 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",
++ DLNPair(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 {
++ 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_KERNEL);
++ if (unlikely(!args.kbuf))
++ goto out;
++
++ read = 0;
++ do {
++ au_wkq_wait(call_read_k, &args, /*dlgt*/1);
++ 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 (unlikely(count < args.count))
++ args.count = count;
++ ubuf += err;
++ read += err;
++ } while (count);
++ smp_mb();
++ 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;
++ struct read_args args = {
++ .errp = &err,
++ .file = file,
++ .count = count,
++ .ppos = ppos
++ };
++ args.kbuf = kbuf;
++ au_wkq_wait(call_read_k, &args, /*dlgt*/1);
++ 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;
++};
++
++static void call_write_k(void *args)
++{
++ struct write_args *a = args;
++ LKTRTrace("%.*s, cnt %lu, pos %Ld\n",
++ DLNPair(a->file->f_dentry), (unsigned long)a->count,
++ *a->ppos);
++ *a->errp = do_vfsub_write_k(a->file, a->kbuf, a->count, a->ppos);
++}
++
++ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count,
++ loff_t *ppos, int dlgt)
++{
++ if (!dlgt)
++ return do_vfsub_write_u(file, ubuf, count, ppos);
++ else {
++ ssize_t err, written;
++ struct write_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_KERNEL);
++ if (unlikely(!args.kbuf))
++ goto out;
++
++ written = 0;
++ do {
++ if (unlikely(copy_from_user(args.kbuf, ubuf, args.count))) {
++ err = -EFAULT;
++ goto out_free;
++ }
++
++ au_wkq_wait(call_write_k, &args, /*dlgt*/1);
++ 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,
++ int dlgt)
++{
++ if (!dlgt)
++ return do_vfsub_write_k(file, kbuf, count, ppos);
++ else {
++ ssize_t err;
++ struct write_args args = {
++ .errp = &err,
++ .file = file,
++ .count = count,
++ .ppos = ppos
++ };
++ args.kbuf = kbuf;
++ au_wkq_wait(call_write_k, &args, /*dlgt*/1);
++ 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;
++ struct readdir_args args = {
++ .errp = &err,
++ .file = file,
++ .filldir = filldir,
++ .arg = arg
++ };
++ au_wkq_wait(call_readdir, &args, /*dlgt*/1);
++ return err;
++ }
++}
++#endif /* CONFIG_AUFS_DLGT */
++
++/* ---------------------------------------------------------------------- */
++
++struct notify_change_args {
++ int *errp;
++ struct dentry *h_dentry;
++ struct iattr *ia;
++};
++
++static void call_notify_change(void *args)
++{
++ struct notify_change_args *a = args;
++ struct inode *h_inode;
++
++ LKTRTrace("%.*s, ia_valid 0x%x\n",
++ DLNPair(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)) {
++ lockdep_off();
++ *a->errp = notify_change(a->h_dentry, a->ia);
++ lockdep_on();
++ }
++ TraceErr(*a->errp);
++}
++
++int vfsub_notify_change(struct dentry *dentry, struct iattr *ia, int dlgt)
++{
++ int err;
++ struct notify_change_args args = {
++ .errp = &err,
++ .h_dentry = dentry,
++ .ia = ia
++ };
++
++#ifndef CONFIG_AUFS_DLGT
++ call_notify_change(&args);
++#else
++ if (!dlgt)
++ call_notify_change(&args);
++ else
++ au_wkq_wait(call_notify_change, &args, /*dlgt*/1);
++#endif
++
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++struct unlink_args {
++ int *errp;
++ struct inode *dir;
++ struct dentry *dentry;
++};
++
++static void call_unlink(void *args)
++{
++ struct unlink_args *a = args;
++ struct inode *h_inode;
++ const int stop_sillyrename = (au_is_nfs(a->dentry->d_sb)
++ && atomic_read(&a->dentry->d_count) == 1);
++
++ LKTRTrace("%.*s, stop_silly %d, cnt %d\n",
++ DLNPair(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(&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)) {
++ lockdep_off();
++ *a->errp = vfs_unlink(a->dir, a->dentry);
++ lockdep_on();
++ } else
++ err = -1;
++ }
++#else
++ // vfs_unlink() locks inode
++ lockdep_off();
++ *a->errp = vfs_unlink(a->dir, a->dentry);
++ lockdep_on();
++#endif
++
++ if (!stop_sillyrename)
++ dput(a->dentry);
++ if (h_inode)
++ iput(h_inode);
++
++ TraceErr(*a->errp);
++}
++
++/*
++ * @dir: must be locked.
++ * @dentry: target dentry.
++ */
++int vfsub_unlink(struct inode *dir, struct dentry *dentry, int dlgt)
++{
++ int err;
++ struct unlink_args args = {
++ .errp = &err,
++ .dir = dir,
++ .dentry = dentry
++ };
++
++#ifndef CONFIG_AUFS_DLGT
++ call_unlink(&args);
++#else
++ if (!dlgt)
++ call_unlink(&args);
++ else
++ au_wkq_wait(call_unlink, &args, /*dlgt*/1);
++#endif
++ 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);
++}
++
++int vfsub_statfs(void *arg, struct kstatfs *buf, int dlgt)
++{
++ int err;
++ struct statfs_args args = {
++ .errp = &err,
++ .arg = arg,
++ .buf = buf
++ };
++
++#ifndef CONFIG_AUFS_DLGT
++ call_statfs(&args);
++#else
++ if (!dlgt)
++ call_statfs(&args);
++ else
++ au_wkq_wait(call_statfs, &args, /*dlgt*/1);
++#endif
++ return err;
++}
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/vfsub.h linux-2.6.22.1/fs/aufs/vfsub.h
+--- linux-2.6.22.1.oorig/fs/aufs/vfsub.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/vfsub.h 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,427 @@
++/*
++ * 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: vfsub.h,v 1.8 2007/05/14 03:39:10 sfjro Exp $ */
++
++#ifndef __AUFS_VFSUB_H__
++#define __AUFS_VFSUB_H__
++
++#ifdef __KERNEL__
++
++#include <linux/fs.h>
++#include <asm/uaccess.h>
++#include "wkq.h"
++
++/* ---------------------------------------------------------------------- */
++
++/* 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 %p\n", inode->i_ino, mask, nd);
++#if 0
++#else
++ return permission(inode, mask, nd);
++#endif
++}
++
++static inline
++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();
++ return err;
++}
++
++static inline
++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();
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static inline
++int do_vfsub_create(struct inode *dir, struct dentry *dentry, int mode,
++ struct nameidata *nd)
++{
++ LKTRTrace("i%lu, %.*s, 0x%x\n", dir->i_ino, DLNPair(dentry), mode);
++#if 0
++#else
++ return vfs_create(dir, dentry, mode, nd);
++#endif
++}
++
++static inline
++int do_vfsub_symlink(struct inode *dir, struct dentry *dentry,
++ const char *symname, int mode)
++{
++ LKTRTrace("i%lu, %.*s, %s, 0x%x\n",
++ dir->i_ino, DLNPair(dentry), symname, mode);
++#if 0
++#else
++ return vfs_symlink(dir, dentry, symname, mode);
++#endif
++}
++
++static inline
++int do_vfsub_mknod(struct inode *dir, struct dentry *dentry, int mode,
++ dev_t dev)
++{
++ LKTRTrace("i%lu, %.*s, 0x%x\n", dir->i_ino, DLNPair(dentry), mode);
++#if 0
++#else
++ return vfs_mknod(dir, dentry, mode, dev);
++#endif
++}
++
++static inline
++int do_vfsub_link(struct dentry *src_dentry, struct inode *dir,
++ struct dentry *dentry)
++{
++ int err;
++
++ LKTRTrace("%.*s, i%lu, %.*s\n",
++ DLNPair(src_dentry), dir->i_ino, DLNPair(dentry));
++
++ lockdep_off();
++#if 0
++#else
++ err = vfs_link(src_dentry, dir, dentry);
++#endif
++ lockdep_on();
++ return err;
++}
++
++static inline
++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, DLNPair(src_dentry),
++ dir->i_ino, DLNPair(dentry));
++
++ lockdep_off();
++#if 0
++#else
++ err = vfs_rename(src_dir, src_dentry, dir, dentry);
++#endif
++ lockdep_on();
++ return err;
++}
++
++static inline
++int do_vfsub_mkdir(struct inode *dir, struct dentry *dentry, int mode)
++{
++ LKTRTrace("i%lu, %.*s, 0x%x\n", dir->i_ino, DLNPair(dentry), mode);
++#if 0
++#else
++ return vfs_mkdir(dir, dentry, mode);
++#endif
++}
++
++static inline int do_vfsub_rmdir(struct inode *dir, struct dentry *dentry)
++{
++ int err;
++
++ LKTRTrace("i%lu, %.*s\n", dir->i_ino, DLNPair(dentry));
++
++ lockdep_off();
++#if 0
++#else
++ err = vfs_rmdir(dir, dentry);
++#endif
++ lockdep_on();
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static inline
++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",
++ DLNPair(file->f_dentry), (unsigned long)count, *ppos);
++
++ /* nfs uses some locks */
++ lockdep_off();
++#if 0
++#else
++ err = vfs_read(file, ubuf, count, ppos);
++#endif
++ lockdep_on();
++ return err;
++}
++
++// kernel_read() ??
++static inline
++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;
++}
++
++static inline
++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",
++ DLNPair(file->f_dentry), (unsigned long)count, *ppos);
++
++ lockdep_off();
++#if 0
++#else
++ err = vfs_write(file, ubuf, count, ppos);
++#endif
++ lockdep_on();
++ return err;
++}
++
++static inline
++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;
++}
++
++static inline
++int do_vfsub_readdir(struct file *file, filldir_t filldir, void *arg)
++{
++ int err;
++
++ LKTRTrace("%.*s\n", DLNPair(file->f_dentry));
++
++ lockdep_off();
++#if 0
++#else
++ err = vfs_readdir(file, filldir, arg);
++#endif
++ lockdep_on();
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static inline loff_t vfsub_llseek(struct file *file, loff_t offset, int origin)
++{
++ loff_t err;
++
++ LKTRTrace("%.*s\n", DLNPair(file->f_dentry));
++
++ lockdep_off();
++#if 0
++#else
++ err = vfs_llseek(file, offset, origin);
++#endif
++ lockdep_on();
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++#ifdef CONFIG_AUFS_DLGT
++static inline int need_dlgt(struct super_block *sb)
++{
++ return (au_flag_test(sb, AuFlag_DLGT) && !is_au_wkq(current));
++}
++
++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, int dlgt);
++int vfsub_mkdir(struct inode *dir, struct dentry *dentry, int mode, int dlgt);
++int vfsub_rmdir(struct inode *dir, struct dentry *dentry, int dlgt);
++
++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, int dlgt);
++ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, loff_t *ppos,
++ int dlgt);
++int vfsub_readdir(struct file *file, filldir_t filldir, void *arg, int dlgt);
++
++#else
++
++static inline int need_dlgt(struct super_block *sb)
++{
++ return 0;
++}
++
++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, int dlgt)
++{
++ return do_vfsub_rename(src_dir, src_dentry, dir, dentry);
++}
++
++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, int dlgt)
++{
++ return do_vfsub_rmdir(dir, dentry);
++}
++
++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, int dlgt)
++{
++ return do_vfsub_write_u(file, ubuf, count, ppos);
++}
++
++static inline
++ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, loff_t *ppos,
++ int dlgt)
++{
++ return do_vfsub_write_k(file, kbuf, count, ppos);
++}
++
++static inline
++int vfsub_readdir(struct file *file, filldir_t filldir, void *arg, int dlgt)
++{
++ return do_vfsub_readdir(file, filldir, arg);
++}
++#endif /* CONFIG_AUFS_DLGT */
++
++/* ---------------------------------------------------------------------- */
++
++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();
++}
++
++/* ---------------------------------------------------------------------- */
++
++int vfsub_notify_change(struct dentry *dentry, struct iattr *ia, int dlgt);
++int vfsub_unlink(struct inode *dir, struct dentry *dentry, int dlgt);
++int vfsub_statfs(void *arg, struct kstatfs *buf, int dlgt);
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_VFSUB_H__ */
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/whout.c linux-2.6.22.1/fs/aufs/whout.c
+--- linux-2.6.22.1.oorig/fs/aufs/whout.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/whout.c 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,933 @@
++/*
++ * 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: whout.c,v 1.14 2007/05/14 03:40:40 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_free_whname().
++ */
++int au_alloc_whname(const char *name, int len, struct qstr *wh)
++{
++ char *p;
++
++ DEBUG_ON(!name || !len || !wh);
++
++ if (unlikely(len > PATH_MAX - AUFS_WH_PFX_LEN))
++ return -ENAMETOOLONG;
++
++ wh->len = len + AUFS_WH_PFX_LEN;
++ wh->name = p = kmalloc(wh->len, GFP_KERNEL);
++ //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_free_whname(struct qstr *wh)
++{
++ DEBUG_ON(!wh || !wh->name);
++ kfree(wh->name);
++#ifdef CONFIG_AUFS_DEBUG
++ wh->name = NULL;
++#endif
++}
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * test if the @wh_name exists under @hidden_parent.
++ * @try_sio specifies the necessary of super-io.
++ */
++int is_wh(struct dentry *hidden_parent, struct qstr *wh_name, int try_sio,
++ struct lkup_args *lkup)
++{
++ int err;
++ struct dentry *wh_dentry;
++ struct inode *hidden_dir;
++
++ LKTRTrace("%.*s/%.*s, lkup{%p, %d}\n", DLNPair(hidden_parent),
++ wh_name->len, wh_name->name, lkup->nfsmnt, lkup->dlgt);
++ hidden_dir = hidden_parent->d_inode;
++ DEBUG_ON(!S_ISDIR(hidden_dir->i_mode));
++ IMustLock(hidden_dir);
++
++ if (!try_sio)
++ wh_dentry = lkup_one(wh_name->name, hidden_parent,
++ wh_name->len, lkup);
++ else
++ wh_dentry = sio_lkup_one(wh_name->name, hidden_parent,
++ wh_name->len, lkup);
++ //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;
++ IOErr("%.*s Invalid whiteout entry type 0%o.\n",
++ DLNPair(wh_dentry), wh_dentry->d_inode->i_mode);
++
++ out_wh:
++ dput(wh_dentry);
++ out:
++ TraceErr(err);
++ return err;
++}
++
++/*
++ * test if the @hidden_dentry sets opaque or not.
++ */
++int is_diropq(struct dentry *hidden_dentry, struct lkup_args *lkup)
++{
++ int err;
++ struct inode *hidden_dir;
++
++ LKTRTrace("dentry %.*s\n", DLNPair(hidden_dentry));
++ hidden_dir = hidden_dentry->d_inode;
++ DEBUG_ON(!S_ISDIR(hidden_dir->i_mode));
++ IMustLock(hidden_dir);
++
++ err = is_wh(hidden_dentry, &diropq_name, /*try_sio*/1, lkup);
++ TraceErr(err);
++ return err;
++}
++
++/*
++ * returns a negative dentry whose name is unique and temporary.
++ */
++struct dentry *lkup_whtmp(struct dentry *hidden_parent, struct qstr *prefix,
++ struct lkup_args *lkup)
++{
++#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",
++ DLNPair(hidden_parent), prefix->len, prefix->name);
++ DEBUG_ON(!hidden_parent->d_inode);
++ IMustLock(hidden_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++ = '.';
++ DEBUG_ON(name + len + 1 - p <= HEX_LEN);
++
++ for (i = 0; i < 3; i++) {
++ sprintf(p, "%.*d", HEX_LEN, cnt++);
++ dentry = sio_lkup_one(name, hidden_parent, len, lkup);
++ //if (LktrCond) {dput(dentry); dentry = ERR_PTR(-1);}
++ if (unlikely(IS_ERR(dentry) || !dentry->d_inode))
++ goto out_name;
++ dput(dentry);
++ }
++ //Warn("could not get random name\n");
++ dentry = ERR_PTR(-EEXIST);
++ Dbg("%.*s\n", len, name);
++ BUG();
++
++ out_name:
++ if (unlikely(name != defname))
++ kfree(name);
++ out:
++ TraceErrPtr(dentry);
++ return dentry;
++#undef HEX_LEN
++}
++
++/*
++ * rename the @dentry of @bindex to the whiteouted temporary name.
++ */
++int rename_whtmp(struct dentry *dentry, aufs_bindex_t bindex)
++{
++ int err;
++ struct inode *hidden_dir;
++ struct dentry *hidden_dentry, *hidden_parent, *tmp_dentry;
++ struct super_block *sb;
++ struct lkup_args lkup;
++
++ LKTRTrace("%.*s, b%d\n", DLNPair(dentry), bindex);
++ hidden_dentry = au_h_dptr_i(dentry, bindex);
++ DEBUG_ON(!hidden_dentry || !hidden_dentry->d_inode);
++ hidden_parent = hidden_dentry->d_parent;
++ hidden_dir = hidden_parent->d_inode;
++ IMustLock(hidden_dir);
++
++ sb = dentry->d_sb;
++ lkup.nfsmnt = au_nfsmnt(sb, bindex);
++ lkup.dlgt = need_dlgt(sb);
++ tmp_dentry = lkup_whtmp(hidden_parent, &hidden_dentry->d_name, &lkup);
++ //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() */
++ err = vfsub_rename(hidden_dir, hidden_dentry,
++ hidden_dir, tmp_dentry, lkup.dlgt);
++ //if (LktrCond) err = -1; //unavailable
++ TraceErr(err);
++ dput(tmp_dentry);
++ }
++
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++int au_unlink_wh_dentry(struct inode *hidden_dir, struct dentry *wh_dentry,
++ struct dentry *dentry, int dlgt)
++{
++ int err;
++
++ LKTRTrace("hi%lu, wh %.*s, d %p\n", hidden_dir->i_ino,
++ DLNPair(wh_dentry), dentry);
++ DEBUG_ON((dentry && dbwh(dentry) == -1)
++ || !wh_dentry->d_inode
++ || !S_ISREG(wh_dentry->d_inode->i_mode));
++ IMustLock(hidden_dir);
++
++ err = vfsub_unlink(hidden_dir, wh_dentry, dlgt);
++ //if (LktrCond) err = -1; // unavailable
++ if (!err && dentry)
++ set_dbwh(dentry, -1);
++
++ TraceErr(err);
++ return err;
++}
++
++static int unlink_wh_name(struct dentry *hidden_parent, struct qstr *wh,
++ struct lkup_args *lkup)
++{
++ int err;
++ struct inode *hidden_dir;
++ struct dentry *hidden_dentry;
++
++ LKTRTrace("%.*s/%.*s\n", DLNPair(hidden_parent), LNPair(wh));
++ hidden_dir = hidden_parent->d_inode;
++ IMustLock(hidden_dir);
++
++ // au_test_perm() is already done
++ hidden_dentry = lkup_one(wh->name, hidden_parent, wh->len, lkup);
++ //if (LktrCond) {dput(hidden_dentry); hidden_dentry = ERR_PTR(-1);}
++ if (!IS_ERR(hidden_dentry)) {
++ err = 0;
++ if (hidden_dentry->d_inode)
++ err = vfsub_unlink(hidden_dir, hidden_dentry,
++ lkup->dlgt);
++ dput(hidden_dentry);
++ } else
++ err = PTR_ERR(hidden_dentry);
++
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static void clean_wh(struct inode *h_dir, struct dentry *wh)
++{
++ TraceEnter();
++ if (wh->d_inode) {
++ int err = vfsub_unlink(h_dir, wh, /*dlgt*/0);
++ if (unlikely(err))
++ Warn("failed unlink %.*s (%d), ignored.\n",
++ DLNPair(wh), err);
++ }
++}
++
++static void clean_plink(struct inode *h_dir, struct dentry *plink)
++{
++ TraceEnter();
++ if (plink->d_inode) {
++ int err = vfsub_rmdir(h_dir, plink, /*dlgt*/0);
++ if (unlikely(err))
++ Warn("failed rmdir %.*s (%d), ignored.\n",
++ DLNPair(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_is_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
++ Err("unknown %.*s exists\n", DLNPair(plink));
++
++ return err;
++}
++
++/*
++ * initialize the whiteout base file/dir for @br.
++ */
++int init_wh(struct dentry *h_root, struct aufs_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 lkup_args lkup = {
++ .nfsmnt = nfsmnt,
++ .dlgt = 0 // always no dlgt
++ };
++ const int do_plink = au_flag_test(sb, AuFlag_PLINK);
++
++ LKTRTrace("nfsmnt %p\n", nfsmnt);
++ BrWhMustWriteLock(br);
++ SiMustWriteLock(sb);
++ h_dir = h_root->d_inode;
++ IMustLock(h_dir);
++
++ // doubly whiteouted
++ wh = lkup_wh(h_root, base_name + 0, &lkup);
++ //if (LktrCond) {dput(wh); wh = ERR_PTR(-1);}
++ err = PTR_ERR(wh);
++ if (IS_ERR(wh))
++ goto out;
++ DEBUG_ON(br->br_wh && br->br_wh != wh);
++
++ plink = lkup_wh(h_root, base_name + 1, &lkup);
++ err = PTR_ERR(plink);
++ if (IS_ERR(plink))
++ goto out_dput_wh;
++ DEBUG_ON(br->br_plink && br->br_plink != plink);
++
++ dput(br->br_wh);
++ dput(br->br_plink);
++ br->br_wh = 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 = vfsub_create(h_dir, wh, WH_MASK, NULL, /*dlgt*/0);
++ else if (S_ISREG(wh->d_inode->i_mode))
++ err = 0;
++ else
++ Err("unknown %.*s/%.*s exists\n",
++ DLNPair(h_root), DLNPair(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:
++ TraceErr(err);
++ return err;
++ out_nolink:
++ Err("%.*s doesn't support link(2), use noplink and rw+nolwh\n",
++ DLNPair(h_root));
++ goto out_dput;
++ out_err:
++ Err("an error(%d) on the writable branch %.*s(%s)\n",
++ err, DLNPair(h_root), au_sbtype(h_root->d_sb));
++ goto out_dput;
++}
++
++struct reinit_br_wh {
++ struct super_block *sb;
++ struct aufs_branch *br;
++};
++
++static void reinit_br_wh(void *arg)
++{
++ int err;
++ struct reinit_br_wh *a = arg;
++ struct inode *hidden_dir, *dir;
++ struct dentry *hidden_root;
++ aufs_bindex_t bindex;
++
++ TraceEnter();
++ DEBUG_ON(!a->br->br_wh || !a->br->br_wh->d_inode || current->fsuid);
++
++ err = 0;
++ /* big lock */
++ si_write_lock(a->sb);
++ if (unlikely(!br_writable(a->br->br_perm)))
++ goto out;
++ bindex = find_brindex(a->sb, a->br->br_id);
++ if (unlikely(bindex < 0))
++ goto out;
++
++ dir = a->sb->s_root->d_inode;
++ hidden_root = a->br->br_wh->d_parent;
++ hidden_dir = hidden_root->d_inode;
++ DEBUG_ON(!hidden_dir->i_op || !hidden_dir->i_op->link);
++ hdir_lock(hidden_dir, dir, bindex);
++ br_wh_write_lock(a->br);
++ err = vfsub_unlink(hidden_dir, a->br->br_wh, /*dlgt*/0);
++ //if (LktrCond) err = -1;
++ dput(a->br->br_wh);
++ a->br->br_wh = NULL;
++ if (!err)
++ err = init_wh(hidden_root, a->br, au_do_nfsmnt(a->br->br_mnt),
++ a->sb);
++ br_wh_write_unlock(a->br);
++ hdir_unlock(hidden_dir, dir, bindex);
++
++ out:
++ atomic_dec(&a->br->br_wh_running);
++ br_put(a->br);
++ si_write_unlock(a->sb);
++ au_mntput(a->sb);
++ kfree(arg);
++ if (unlikely(err))
++ IOErr("err %d\n", err);
++}
++
++static void kick_reinit_br_wh(struct super_block *sb, struct aufs_branch *br)
++{
++ int do_dec;
++ 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_KERNEL);
++ if (arg) {
++ // dec(wh_running), kfree(arg) and br_put() in reinit function
++ arg->sb = sb;
++ arg->br = br;
++ br_get(br);
++ /* prohibit umount */
++ au_mntget(sb);
++ au_wkq_nowait(reinit_br_wh, arg, /*dlgt*/0);
++ do_dec = 0;
++ }
++
++ out:
++ if (do_dec)
++ atomic_dec(&br->br_wh_running);
++}
++
++/*
++ * create the whiteoute @wh.
++ */
++static int link_or_create_wh(struct dentry *wh, struct super_block *sb,
++ aufs_bindex_t bindex)
++{
++ int err, dlgt;
++ struct aufs_branch *br;
++ struct dentry *hidden_parent;
++ struct inode *hidden_dir;
++
++ LKTRTrace("%.*s\n", DLNPair(wh));
++ SiMustReadLock(sb);
++ hidden_parent = wh->d_parent;
++ hidden_dir = hidden_parent->d_inode;
++ IMustLock(hidden_dir);
++
++ dlgt = need_dlgt(sb);
++ br = stobr(sb, bindex);
++ br_wh_read_lock(br);
++ if (br->br_wh) {
++ err = vfsub_link(br->br_wh, hidden_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 = vfsub_create(hidden_dir, wh, WH_MASK, NULL, dlgt);
++
++ out:
++ br_wh_read_unlock(br);
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * create or remove the diropq.
++ */
++static struct dentry *do_diropq(struct dentry *dentry, aufs_bindex_t bindex,
++ int do_create, int dlgt)
++{
++ struct dentry *opq_dentry, *hidden_dentry;
++ struct inode *hidden_dir;
++ int err;
++ struct super_block *sb;
++ struct lkup_args lkup;
++
++ LKTRTrace("%.*s, bindex %d, do_create %d\n", DLNPair(dentry),
++ bindex, do_create);
++ hidden_dentry = au_h_dptr_i(dentry, bindex);
++ DEBUG_ON(!hidden_dentry);
++ hidden_dir = hidden_dentry->d_inode;
++ DEBUG_ON(!hidden_dir || !S_ISDIR(hidden_dir->i_mode));
++ IMustLock(hidden_dir);
++
++ // already checked by au_test_perm().
++ sb = dentry->d_sb;
++ lkup.nfsmnt = au_nfsmnt(sb, bindex);
++ lkup.dlgt = dlgt;
++ opq_dentry = lkup_one(diropq_name.name, hidden_dentry, diropq_name.len,
++ &lkup);
++ //if (LktrCond) {dput(opq_dentry); opq_dentry = ERR_PTR(-1);}
++ if (IS_ERR(opq_dentry))
++ goto out;
++
++ if (do_create) {
++ DEBUG_ON(opq_dentry->d_inode);
++ err = link_or_create_wh(opq_dentry, sb, bindex);
++ //if (LktrCond) {vfs_unlink(hidden_dir, opq_dentry); err = -1;}
++ if (!err) {
++ set_dbdiropq(dentry, bindex);
++ goto out; /* success */
++ }
++ } else {
++ DEBUG_ON(/* !S_ISDIR(dentry->d_inode->i_mode)
++ * || */!opq_dentry->d_inode);
++ err = vfsub_unlink(hidden_dir, opq_dentry, lkup.dlgt);
++ //if (LktrCond) err = -1;
++ if (!err)
++ set_dbdiropq(dentry, -1);
++ }
++ dput(opq_dentry);
++ opq_dentry = ERR_PTR(err);
++
++ out:
++ TraceErrPtr(opq_dentry);
++ return opq_dentry;
++}
++
++struct do_diropq_args {
++ struct dentry **errp;
++ struct dentry *dentry;
++ aufs_bindex_t bindex;
++ int do_create, dlgt;
++};
++
++static void call_do_diropq(void *args)
++{
++ struct do_diropq_args *a = args;
++ *a->errp = do_diropq(a->dentry, a->bindex, a->do_create, a->dlgt);
++}
++
++struct dentry *sio_diropq(struct dentry *dentry, aufs_bindex_t bindex,
++ int do_create, int dlgt)
++{
++ struct dentry *diropq, *hidden_dentry;
++
++ LKTRTrace("%.*s, bindex %d, do_create %d\n",
++ DLNPair(dentry), bindex, do_create);
++
++ hidden_dentry = au_h_dptr_i(dentry, bindex);
++ if (!au_test_perm(hidden_dentry->d_inode, MAY_EXEC | MAY_WRITE, dlgt))
++ diropq = do_diropq(dentry, bindex, do_create, dlgt);
++ else {
++ struct do_diropq_args args = {
++ .errp = &diropq,
++ .dentry = dentry,
++ .bindex = bindex,
++ .do_create = do_create,
++ .dlgt = dlgt
++ };
++ au_wkq_wait(call_do_diropq, &args, /*dlgt*/0);
++ }
++
++ TraceErrPtr(diropq);
++ return diropq;
++}
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * lookup whiteout dentry.
++ * @hidden_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 *lkup_wh(struct dentry *hidden_parent, struct qstr *base_name,
++ struct lkup_args *lkup)
++{
++ int err;
++ struct qstr wh_name;
++ struct dentry *wh_dentry;
++
++ LKTRTrace("%.*s/%.*s\n", DLNPair(hidden_parent), LNPair(base_name));
++ IMustLock(hidden_parent->d_inode);
++
++ err = au_alloc_whname(base_name->name, base_name->len, &wh_name);
++ //if (LktrCond) {au_free_whname(&wh_name); err = -1;}
++ wh_dentry = ERR_PTR(err);
++ if (!err) {
++ // do not superio.
++ wh_dentry = lkup_one(wh_name.name, hidden_parent, wh_name.len,
++ lkup);
++ au_free_whname(&wh_name);
++ }
++ TraceErrPtr(wh_dentry);
++ return wh_dentry;
++}
++
++/*
++ * link/create a whiteout for @dentry on @bindex.
++ */
++struct dentry *simple_create_wh(struct dentry *dentry, aufs_bindex_t bindex,
++ struct dentry *hidden_parent,
++ struct lkup_args *lkup)
++{
++ struct dentry *wh_dentry;
++ int err;
++ struct super_block *sb;
++
++ LKTRTrace("%.*s/%.*s on b%d\n", DLNPair(hidden_parent),
++ DLNPair(dentry), bindex);
++
++ sb = dentry->d_sb;
++ wh_dentry = lkup_wh(hidden_parent, &dentry->d_name, lkup);
++ //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) {
++ IMustLock(hidden_parent->d_inode);
++ err = link_or_create_wh(wh_dentry, sb, bindex);
++ if (!err)
++ set_dbwh(dentry, bindex);
++ else {
++ dput(wh_dentry);
++ wh_dentry = ERR_PTR(err);
++ }
++ }
++
++ TraceErrPtr(wh_dentry);
++ return wh_dentry;
++}
++
++/* ---------------------------------------------------------------------- */
++
++/* Delete all whiteouts in this directory in branch bindex. */
++static int del_wh_children(struct aufs_nhash *whlist,
++ struct dentry *hidden_parent, aufs_bindex_t bindex,
++ struct lkup_args *lkup)
++{
++ int err, i;
++ struct qstr wh_name;
++ char *p;
++ struct inode *hidden_dir;
++ struct hlist_head *head;
++ struct aufs_wh *tpos;
++ struct hlist_node *pos;
++ struct aufs_destr *str;
++
++ LKTRTrace("%.*s\n", DLNPair(hidden_parent));
++ hidden_dir = hidden_parent->d_inode;
++ IMustLock(hidden_dir);
++ DEBUG_ON(IS_RDONLY(hidden_dir));
++ //SiMustReadLock(??);
++
++ err = -ENOMEM;
++ wh_name.name = p = __getname();
++ //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_perm().
++ err = 0;
++ for (i = 0; !err && i < AUFS_NHASH_SIZE; 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(hidden_parent, &wh_name,
++ lkup);
++ //if (LktrCond) err = -1;
++ if (!err)
++ continue;
++ break;
++ }
++ IOErr("whiteout name too long %.*s\n",
++ str->len, str->name);
++ err = -EIO;
++ break;
++ }
++ }
++ __putname(wh_name.name);
++
++ out:
++ TraceErr(err);
++ return err;
++}
++
++struct del_wh_children_args {
++ int *errp;
++ struct aufs_nhash *whlist;
++ struct dentry *hidden_parent;
++ aufs_bindex_t bindex;
++ struct lkup_args *lkup;
++};
++
++static void call_del_wh_children(void *args)
++{
++ struct del_wh_children_args *a = args;
++ *a->errp = del_wh_children(a->whlist, a->hidden_parent, a->bindex,
++ a->lkup);
++}
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * rmdir the whiteouted temporary named dir @hidden_dentry.
++ * @whlist: whiteouted children.
++ */
++int rmdir_whtmp(struct dentry *hidden_dentry, struct aufs_nhash *whlist,
++ aufs_bindex_t bindex, struct inode *dir, struct inode *inode)
++{
++ int err;
++ struct inode *hidden_inode, *hidden_dir;
++ struct lkup_args lkup;
++ struct super_block *sb;
++
++ LKTRTrace("hd %.*s, b%d, i%lu\n",
++ DLNPair(hidden_dentry), bindex, dir->i_ino);
++ IMustLock(dir);
++ IiMustAnyLock(dir);
++ hidden_dir = hidden_dentry->d_parent->d_inode;
++ IMustLock(hidden_dir);
++
++ sb = inode->i_sb;
++ lkup.nfsmnt = au_nfsmnt(sb, bindex);
++ lkup.dlgt = need_dlgt(sb);
++ hidden_inode = hidden_dentry->d_inode;
++ DEBUG_ON(hidden_inode != au_h_iptr_i(inode, bindex));
++ hdir2_lock(hidden_inode, inode, bindex);
++ if (!au_test_perm(hidden_inode, MAY_EXEC | MAY_WRITE, lkup.dlgt))
++ err = del_wh_children(whlist, hidden_dentry, bindex, &lkup);
++ else {
++ // ugly
++ int dlgt = lkup.dlgt;
++ struct del_wh_children_args args = {
++ .errp = &err,
++ .whlist = whlist,
++ .hidden_parent = hidden_dentry,
++ .bindex = bindex,
++ .lkup = &lkup
++ };
++
++ lkup.dlgt = 0;
++ au_wkq_wait(call_del_wh_children, &args, /*dlgt*/0);
++ lkup.dlgt = dlgt;
++ }
++ hdir_unlock(hidden_inode, inode, bindex);
++
++ if (!err) {
++ err = vfsub_rmdir(hidden_dir, hidden_dentry, lkup.dlgt);
++ //d_drop(hidden_dentry);
++ //if (LktrCond) err = -1;
++ }
++
++ if (!err) {
++ if (ibstart(dir) == bindex) {
++ au_cpup_attr_timesizes(dir);
++ //au_cpup_attr_nlink(dir);
++ dir->i_nlink--;
++ }
++ return 0; /* success */
++ }
++
++ Warn("failed removing %.*s(%d), ignored\n",
++ DLNPair(hidden_dentry), err);
++ return err;
++}
++
++static void do_rmdir_whtmp(void *arg)
++{
++ int err;
++ struct rmdir_whtmp_arg *a = arg;
++ struct super_block *sb;
++
++ LKTRTrace("%.*s, b%d, dir i%lu\n",
++ DLNPair(a->h_dentry), a->bindex, a->dir->i_ino);
++
++ i_lock(a->dir);
++ sb = a->dir->i_sb;
++ si_read_lock(sb);
++ err = test_ro(sb, a->bindex, NULL);
++ if (!err) {
++ struct inode *hidden_dir = a->h_dentry->d_parent->d_inode;
++
++ ii_write_lock_child(a->inode);
++ ii_write_lock_parent(a->dir);
++ hdir_lock(hidden_dir, a->dir, a->bindex);
++ err = rmdir_whtmp(a->h_dentry, &a->whlist, a->bindex,
++ a->dir, a->inode);
++ hdir_unlock(hidden_dir, a->dir, a->bindex);
++ ii_write_unlock(a->dir);
++ ii_write_unlock(a->inode);
++ }
++ dput(a->h_dentry);
++ nhash_fin(&a->whlist);
++ iput(a->inode);
++ si_read_unlock(sb);
++ au_mntput(sb);
++ i_unlock(a->dir);
++ iput(a->dir);
++ kfree(arg);
++ if (unlikely(err))
++ IOErr("err %d\n", err);
++}
++
++void kick_rmdir_whtmp(struct dentry *hidden_dentry, struct aufs_nhash *whlist,
++ aufs_bindex_t bindex, struct inode *dir,
++ struct inode *inode, struct rmdir_whtmp_arg *arg)
++{
++ LKTRTrace("%.*s\n", DLNPair(hidden_dentry));
++ IMustLock(dir);
++
++ // all post-process will be done in do_rmdir_whtmp().
++ arg->h_dentry = dget(hidden_dentry);
++ nhash_init(&arg->whlist);
++ nhash_move(&arg->whlist, whlist);
++ arg->bindex = bindex;
++ arg->dir = igrab(dir);
++ arg->inode = igrab(inode);
++ /* prohibit umount */
++ au_mntget(dir->i_sb);
++
++ au_wkq_nowait(do_rmdir_whtmp, arg, /*dlgt*/0);
++}
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/whout.h linux-2.6.22.1/fs/aufs/whout.h
+--- linux-2.6.22.1.oorig/fs/aufs/whout.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/whout.h 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,87 @@
++/*
++ * 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: whout.h,v 1.8 2007/05/14 03:41:52 sfjro Exp $ */
++
++#ifndef __AUFS_WHOUT_H__
++#define __AUFS_WHOUT_H__
++
++#ifdef __KERNEL__
++
++#include <linux/fs.h>
++#include <linux/aufs_type.h>
++
++int au_alloc_whname(const char *name, int len, struct qstr *wh);
++void au_free_whname(struct qstr *wh);
++
++struct lkup_args;
++int is_wh(struct dentry *h_parent, struct qstr *wh_name, int try_sio,
++ struct lkup_args *lkup);
++int is_diropq(struct dentry *h_dentry, struct lkup_args *lkup);
++
++struct dentry *lkup_whtmp(struct dentry *h_parent, struct qstr *prefix,
++ struct lkup_args *lkup);
++int rename_whtmp(struct dentry *dentry, aufs_bindex_t bindex);
++int au_unlink_wh_dentry(struct inode *h_dir, struct dentry *wh_dentry,
++ struct dentry *dentry, int dlgt);
++
++struct aufs_branch;
++int init_wh(struct dentry *h_parent, struct aufs_branch *br,
++ struct vfsmount *nfsmnt, struct super_block *sb);
++
++struct dentry *sio_diropq(struct dentry *dentry, aufs_bindex_t bindex,
++ int do_create, int dlgt);
++
++struct dentry *lkup_wh(struct dentry *h_parent, struct qstr *base_name,
++ struct lkup_args *lkup);
++struct dentry *simple_create_wh(struct dentry *dentry, aufs_bindex_t bindex,
++ struct dentry *h_parent,
++ struct lkup_args *lkup);
++
++/* real rmdir the whiteout-ed dir */
++struct rmdir_whtmp_arg {
++ struct dentry *h_dentry;
++ struct aufs_nhash whlist;
++ aufs_bindex_t bindex;
++ struct inode *dir, *inode;
++};
++
++struct aufs_nhash;
++int rmdir_whtmp(struct dentry *h_dentry, struct aufs_nhash *whlist,
++ aufs_bindex_t bindex, struct inode *dir, struct inode *inode);
++void kick_rmdir_whtmp(struct dentry *h_dentry, struct aufs_nhash *whlist,
++ aufs_bindex_t bindex, struct inode *dir,
++ struct inode *inode, struct rmdir_whtmp_arg *arg);
++
++/* ---------------------------------------------------------------------- */
++
++static inline
++struct dentry *create_diropq(struct dentry *dentry, aufs_bindex_t bindex,
++ int dlgt)
++{
++ return sio_diropq(dentry, bindex, 1, dlgt);
++}
++
++static inline
++int remove_diropq(struct dentry *dentry, aufs_bindex_t bindex, int dlgt)
++{
++ return PTR_ERR(sio_diropq(dentry, bindex, 0, dlgt));
++}
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_WHOUT_H__ */
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/wkq.c linux-2.6.22.1/fs/aufs/wkq.c
+--- linux-2.6.22.1.oorig/fs/aufs/wkq.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/wkq.c 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,283 @@
++/*
++ * 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: wkq.c,v 1.14 2007/05/14 03:39:10 sfjro Exp $ */
++
++#include <linux/module.h>
++#include "aufs.h"
++
++struct au_wkq *au_wkq;
++
++struct au_cred {
++#ifdef CONFIG_AUFS_DLGT
++ 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;
++
++ unsigned int wait:1;
++ unsigned int dlgt:1;
++ 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->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)
++{
++ DEBUG_ON(!is_au_wkq(current));
++ 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);
++}
++#endif
++
++/* ---------------------------------------------------------------------- */
++
++static void update_busy(struct au_wkq *wkq, struct au_wkinfo *wkinfo)
++{
++#ifdef CONFIG_AUFS_SYSAUFS
++ 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)
++{
++ wkinfo->busyp = &wkq->busy;
++ update_busy(wkq, wkinfo);
++ if (wkinfo->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;
++
++ TraceEnter();
++
++ while (1) {
++ if (wkinfo->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(&au_wkq[i].busy);
++ }
++ } else
++ idle_idx = aufs_nwkq;
++
++ atomic_inc(&au_wkq[idle_idx].busy);
++ if (!enqueue(au_wkq + idle_idx, wkinfo))
++ return; /* success */
++
++ /* impossible? */
++ Warn1("failed to queue_work()\n");
++ yield();
++ }
++}
++
++static AuWkqFunc(wkq_func, wk)
++{
++ struct au_wkinfo *wkinfo = container_of(wk, struct au_wkinfo, wk);
++
++ LKTRTrace("wkinfo{%u, %u, %p, %p, %p}\n",
++ wkinfo->wait, wkinfo->dlgt, wkinfo->func, wkinfo->busyp,
++ wkinfo->comp);
++#ifdef CONFIG_AUFS_DLGT
++ if (!wkinfo->dlgt)
++ wkinfo->func(wkinfo->args);
++ else {
++ struct au_cred cred;
++ cred_switch(&cred, &wkinfo->cred);
++ wkinfo->func(wkinfo->args);
++ cred_revert(&cred);
++ }
++#else
++ wkinfo->func(wkinfo->args);
++#endif
++ atomic_dec(wkinfo->busyp);
++ if (wkinfo->wait)
++ complete(wkinfo->comp);
++ else {
++ kfree(wkinfo);
++ module_put(THIS_MODULE);
++ }
++}
++
++void au_wkq_run(au_wkq_func_t func, void *args, int dlgt, int do_wait)
++{
++ DECLARE_COMPLETION_ONSTACK(comp);
++ struct au_wkinfo _wkinfo = {
++ .wait = 1,
++ .dlgt = !!dlgt,
++ .func = func,
++ .args = args,
++ .comp = &comp
++ }, *wkinfo = &_wkinfo;
++
++ LKTRTrace("dlgt %d, do_wait %d\n", dlgt, do_wait);
++ DEBUG_ON(is_au_wkq(current));
++
++ if (unlikely(!do_wait)) {
++ static DECLARE_WAIT_QUEUE_HEAD(wq);
++ /*
++ * never fail.
++ * wkq_func() must free this wkinfo.
++ * it highly depends upon the implementation of workqueue.
++ */
++ wait_event(wq, (wkinfo = kmalloc(sizeof(*wkinfo), GFP_KERNEL)));
++ wkinfo->wait = 0;
++ wkinfo->dlgt = !!dlgt;
++ wkinfo->func = func;
++ wkinfo->args = args;
++ wkinfo->comp = NULL;
++ __module_get(THIS_MODULE);
++ }
++
++ AuInitWkq(&wkinfo->wk, wkq_func);
++#ifdef CONFIG_AUFS_DLGT
++ if (dlgt)
++ cred_store(&wkinfo->cred);
++#endif
++ do_wkq(wkinfo);
++ if (do_wait)
++ wait_for_completion(wkinfo->comp);
++}
++
++#if 0
++void au_wkq_wait_nwtask(void)
++{
++ static DECLARE_WAIT_QUEUE_HEAD(wq);
++ wait_event(wq, !atomic_read(&au_wkq[aufs_nwkq].busy));
++}
++#endif
++
++void au_wkq_fin(void)
++{
++ int i;
++
++ TraceEnter();
++
++ 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;
++
++#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:
++ TraceErr(err);
++ return err;
++}
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/wkq.h linux-2.6.22.1/fs/aufs/wkq.h
+--- linux-2.6.22.1.oorig/fs/aufs/wkq.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/wkq.h 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,81 @@
++/*
++ * 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: wkq.h,v 1.9 2007/05/14 03:39:10 sfjro Exp $ */
++
++#ifndef __AUFS_WKQ_H__
++#define __AUFS_WKQ_H__
++
++#ifdef __KERNEL__
++
++#include <linux/sched.h>
++#include <linux/version.h>
++#include <linux/workqueue.h>
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
++#define DECLARE_COMPLETION_ONSTACK(work) DECLARE_COMPLETION(work)
++#endif
++
++/* ---------------------------------------------------------------------- */
++
++/* internal workqueue named AUFS_WKQ_NAME */
++struct au_wkq {
++ struct workqueue_struct *q;
++
++ /* accounting */
++ atomic_t busy;
++ unsigned int max_busy;
++} ;//__attribute__ ((aligned));
++
++typedef void (*au_wkq_func_t)(void *args);
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
++#define AuInitWkq(wk, func) INIT_WORK(wk, func)
++#define AuWkqFunc(name, arg) void name(struct work_struct *arg)
++#else
++typedef void (*work_func_t)(void *arg);
++#define AuInitWkq(wk, func) INIT_WORK(wk, func, wk)
++#define AuWkqFunc(name, arg) void name(void *arg)
++#endif
++
++extern struct au_wkq *au_wkq;
++
++void au_wkq_run(au_wkq_func_t func, void *args, int dlgt, int do_wait);
++//void au_wkq_wait_nwtask(void);
++int __init au_wkq_init(void);
++void au_wkq_fin(void);
++
++/* ---------------------------------------------------------------------- */
++
++static inline int is_au_wkq(struct task_struct *tsk)
++{
++ return (!tsk->mm && !strcmp(current->comm, AUFS_WKQ_NAME));
++}
++
++static inline void au_wkq_wait(au_wkq_func_t func, void *args, int dlgt)
++{
++ au_wkq_run(func, args, dlgt, /*do_wait*/1);
++}
++
++static inline void au_wkq_nowait(au_wkq_func_t func, void *args, int dlgt)
++{
++ au_wkq_run(func, args, dlgt, /*do_wait*/0);
++}
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_WKQ_H__ */
+diff -rduNp linux-2.6.22.1.oorig/fs/aufs/xino.c linux-2.6.22.1/fs/aufs/xino.c
+--- linux-2.6.22.1.oorig/fs/aufs/xino.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/aufs/xino.c 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,644 @@
++/*
++ * 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: xino.c,v 1.27 2007/05/14 03:39:10 sfjro Exp $ */
++
++//#include <linux/fs.h>
++#include <linux/fsnotify.h>
++#include <asm/uaccess.h>
++#include "aufs.h"
++
++static 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 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);
++}
++
++/* ---------------------------------------------------------------------- */
++
++static ssize_t xino_fread(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",
++ DLNPair(file->f_dentry), (unsigned long)size, *pos);
++
++ oldfs = get_fs();
++ set_fs(KERNEL_DS);
++ do {
++ err = func(file, (char __user*)buf, size, pos);
++ } while (err == -EAGAIN || err == -EINTR);
++ set_fs(oldfs);
++
++#if 0
++ if (err > 0)
++ fsnotify_access(file->f_dentry);
++#endif
++
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static ssize_t do_xino_fwrite(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 {
++ err = func(file, (const char __user*)buf, size, pos);
++ } while (err == -EAGAIN || err == -EINTR);
++ set_fs(oldfs);
++ lockdep_on();
++
++#if 0
++ if (err > 0)
++ fsnotify_modify(file->f_dentry);
++#endif
++
++ TraceErr(err);
++ return err;
++}
++
++struct do_xino_fwrite_args {
++ ssize_t *errp;
++ 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(writef_t func, struct file *file, void *buf,
++ size_t size, loff_t *pos)
++{
++ ssize_t err;
++
++ LKTRTrace("%.*s, sz %lu, *pos %Ld\n",
++ DLNPair(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 (!is_au_wkq(current)) {
++ struct do_xino_fwrite_args args = {
++ .errp = &err,
++ .func = func,
++ .file = file,
++ .buf = buf,
++ .size = size,
++ .pos = pos
++ };
++ au_wkq_wait(call_do_xino_fwrite, &args, /*dlgt*/0);
++ } else
++ err = do_xino_fwrite(func, file, buf, size, pos);
++
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * 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 xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
++ struct xino *xino)
++{
++ struct aufs_branch *br;
++ loff_t pos;
++ ssize_t sz;
++
++ LKTRTrace("b%d, hi%lu, i%lu\n", bindex, h_ino, xino->ino);
++ //DEBUG_ON(!xino->ino /* || !xino->h_gen */);
++ //WARN_ON(bindex == 0 && h_ino == 31);
++
++ if (unlikely(!au_flag_test(sb, AuFlag_XINO)))
++ return 0;
++
++ br = stobr(sb, bindex);
++ DEBUG_ON(!br || !br->br_xino);
++ pos = h_ino * sizeof(*xino);
++ sz = xino_fwrite(br->br_xino_write, br->br_xino, xino, sizeof(*xino),
++ &pos);
++ //if (LktrCond) sz = 1;
++ if (sz == sizeof(*xino))
++ return 0; /* success */
++
++ IOErr("write failed (%ld)\n", (long)sz);
++ return -EIO;
++}
++
++int xino_write0(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino)
++{
++ struct xino xino = {
++ .ino = 0
++ };
++ return xino_write(sb, bindex, h_ino, &xino);
++}
++
++// why is not atomic_long_inc_return defined?
++static DEFINE_SPINLOCK(alir_lock);
++static long atomic_long_inc_return(atomic_long_t *a)
++{
++ long l;
++
++ spin_lock(&alir_lock);
++ atomic_long_inc(a);
++ l = atomic_long_read(a);
++ spin_unlock(&alir_lock);
++ return l;
++}
++
++ino_t xino_new_ino(struct super_block *sb)
++{
++ ino_t ino;
++
++ TraceEnter();
++ ino = atomic_long_inc_return(&stosi(sb)->si_xino);
++ BUILD_BUG_ON(AUFS_FIRST_INO < AUFS_ROOT_INO);
++ if (ino >= AUFS_ROOT_INO)
++ return ino;
++ else {
++ atomic_long_dec(&stosi(sb)->si_xino);
++ IOErr1("inode number overflow\n");
++ 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 xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
++ struct xino *xino)
++{
++ int err;
++ struct aufs_branch *br;
++ struct file *file;
++ loff_t pos;
++ ssize_t sz;
++
++ LKTRTrace("b%d, hi%lu\n", bindex, h_ino);
++
++ err = 0;
++ xino->ino = 0;
++ if (unlikely(!au_flag_test(sb, AuFlag_XINO)))
++ return 0; /* no ino */
++
++ br = stobr(sb, bindex);
++ file = br->br_xino;
++ DEBUG_ON(!file);
++ pos = h_ino * sizeof(*xino);
++ if (i_size_read(file->f_dentry->d_inode) < pos + sizeof(*xino))
++ return 0; /* no ino */
++
++ sz = xino_fread(br->br_xino_read, file, xino, sizeof(*xino), &pos);
++ if (sz == sizeof(*xino))
++ return 0; /* success */
++
++ err = sz;
++ if (unlikely(sz >= 0)) {
++ err = -EIO;
++ IOErr("xino read error (%ld)\n", (long)sz);
++ }
++
++ TraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++struct file *xino_create(struct super_block *sb, char *fname, int silent,
++ struct dentry *parent)
++{
++ struct file *file;
++ int err;
++ struct dentry *hidden_parent;
++ struct inode *hidden_dir;
++ //const int udba = au_flag_test(sb, AuFlag_UDBA_INOTIFY);
++
++ LKTRTrace("%s\n", fname);
++ //DEBUG_ON(!au_flag_test(sb, AuFlag_XINO));
++
++ // LSM may detect it
++ // use sio?
++ 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)
++ Err("open %s(%ld)\n", fname, PTR_ERR(file));
++ return file;
++ }
++#if 0
++ if (unlikely(udba && parent))
++ au_direval_dec(parent);
++#endif
++
++ /* keep file count */
++ hidden_parent = dget_parent(file->f_dentry);
++ hidden_dir = hidden_parent->d_inode;
++ hi_lock_parent(hidden_dir);
++ err = vfsub_unlink(hidden_dir, file->f_dentry, /*dlgt*/0);
++#if 0
++ if (unlikely(!err && udba && parent))
++ au_direval_dec(parent);
++#endif
++ i_unlock(hidden_dir);
++ dput(hidden_parent);
++ if (unlikely(err)) {
++ if (!silent)
++ Err("unlink %s(%d)\n", fname, err);
++ goto out;
++ }
++ if (sb != file->f_dentry->d_sb)
++ return file; /* success */
++
++ if (!silent)
++ Err("%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 = sbr_sb(sb, btgt);
++
++ for (bindex = 0; bindex <= bend; bindex++)
++ if (unlikely(btgt != bindex && tgt_sb == sbr_sb(sb, bindex)))
++ return bindex;
++ return -1;
++}
++
++/*
++ * create a new xinofile at the same place/path as @base_file.
++ */
++static struct file *xino_create2(struct file *base_file)
++{
++ struct file *file;
++ int err;
++ struct dentry *base, *dentry, *parent;
++ struct inode *dir;
++ struct qstr *name;
++ struct lkup_args lkup = {
++ .nfsmnt = NULL,
++ .dlgt = 0
++ };
++
++ base = base_file->f_dentry;
++ LKTRTrace("%.*s\n", DLNPair(base));
++ parent = dget_parent(base);
++ dir = parent->d_inode;
++ IMustLock(dir);
++
++ file = ERR_PTR(-EINVAL);
++ if (unlikely(au_is_nfs(parent->d_sb)))
++ goto out;
++
++ // do not superio, nor NFS.
++ name = &base->d_name;
++ dentry = lkup_one(name->name, parent, name->len, &lkup);
++ //if (LktrCond) {dput(dentry); dentry = ERR_PTR(-1);}
++ if (IS_ERR(dentry)) {
++ file = (void*)dentry;
++ Err("%.*s lookup err %ld\n", LNPair(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);
++ Err("%.*s create err %d\n", LNPair(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)) {
++ Err("%.*s open err %ld\n", LNPair(name), PTR_ERR(file));
++ goto out_dput;
++ }
++ err = vfsub_unlink(dir, dentry, /*dlgt*/0);
++ //if (LktrCond) err = -1;
++ if (!err)
++ goto out_dput; /* success */
++
++ Err("%.*s unlink err %d\n", LNPair(name), err);
++ fput(file);
++ file = ERR_PTR(err);
++
++ out_dput:
++ dput(dentry);
++ out:
++ dput(parent);
++ TraceErrPtr(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 xino_init(struct super_block *sb, aufs_bindex_t bindex,
++ struct file *base_file, int do_test)
++{
++ int err;
++ struct aufs_branch *br;
++ aufs_bindex_t bshared, bend;
++ struct file *file;
++ struct inode *inode, *hidden_inode;
++ struct xino xino;
++
++ LKTRTrace("b%d, base_file %p, do_test %d\n",
++ bindex, base_file, do_test);
++ SiMustWriteLock(sb);
++ DEBUG_ON(!au_flag_test(sb, AuFlag_XINO));
++ br = stobr(sb, bindex);
++ DEBUG_ON(br->br_xino);
++
++ file = NULL;
++ bshared = -1;
++ bend = sbend(sb);
++ if (do_test)
++ bshared = is_sb_shared(sb, bindex, bend);
++ if (unlikely(bshared >= 0)) {
++ struct aufs_branch *shared_br = stobr(sb, bshared);
++ if (shared_br->br_xino) {
++ file = shared_br->br_xino;
++ get_file(file);
++ }
++ }
++
++ if (!file) {
++ struct dentry *parent = dget_parent(base_file->f_dentry);
++ struct inode *dir = parent->d_inode;
++
++ hi_lock_parent(dir);
++ file = xino_create2(base_file);
++ //if (LktrCond) {fput(file); file = ERR_PTR(-1);}
++ i_unlock(dir);
++ dput(parent);
++ err = PTR_ERR(file);
++ if (IS_ERR(file))
++ goto out;
++ }
++ br->br_xino_read = find_readf(file);
++ err = PTR_ERR(br->br_xino_read);
++ if (IS_ERR(br->br_xino_read))
++ goto out_put;
++ br->br_xino_write = find_writef(file);
++ err = PTR_ERR(br->br_xino_write);
++ if (IS_ERR(br->br_xino_write))
++ goto out_put;
++ br->br_xino = file;
++
++ inode = sb->s_root->d_inode;
++ hidden_inode = au_h_iptr_i(inode, bindex);
++ xino.ino = inode->i_ino;
++ //xino.h_gen = hidden_inode->i_generation;
++ //WARN_ON(xino.h_gen == AuXino_INVALID_HGEN);
++ err = xino_write(sb, bindex, hidden_inode->i_ino, &xino);
++ //if (LktrCond) err = -1;
++ if (!err)
++ return 0; /* success */
++
++ br->br_xino = NULL;
++
++ out_put:
++ fput(file);
++ out:
++ TraceErr(err);
++ return err;
++}
++
++/*
++ * set xino mount option.
++ */
++int xino_set(struct super_block *sb, struct opt_xino *xino, int remount)
++{
++ int err, sparse;
++ aufs_bindex_t bindex, bend;
++ struct aufs_branch *br;
++ struct dentry *parent;
++ struct qstr *name;
++ struct file *cur_xino;
++ struct inode *dir;
++
++ LKTRTrace("%s\n", xino->path);
++
++ err = 0;
++ name = &xino->file->f_dentry->d_name;
++ parent = dget_parent(xino->file->f_dentry);
++ dir = parent->d_inode;
++ cur_xino = stobr(sb, 0)->br_xino;
++ if (remount
++ && cur_xino
++ && cur_xino->f_dentry->d_parent == parent
++ && name->len == cur_xino->f_dentry->d_name.len
++ && !memcmp(name->name, cur_xino->f_dentry->d_name.name, name->len))
++ goto out;
++
++ au_flag_set(sb, AuFlag_XINO);
++ bend = sbend(sb);
++ for (bindex = bend; bindex >= 0; bindex--) {
++ br = stobr(sb, bindex);
++ if (unlikely(br->br_xino && file_count(br->br_xino) > 1)) {
++ fput(br->br_xino);
++ br->br_xino = NULL;
++ }
++ }
++
++ for (bindex = 0; bindex <= bend; bindex++) {
++ struct file *file;
++ struct inode *inode;
++
++ br = stobr(sb, bindex);
++ if (unlikely(!br->br_xino))
++ continue;
++
++ DEBUG_ON(file_count(br->br_xino) != 1);
++ hi_lock_parent(dir);
++ file = xino_create2(xino->file);
++ //if (LktrCond) {fput(file); file = ERR_PTR(-1);}
++ err = PTR_ERR(file);
++ if (IS_ERR(file)) {
++ i_unlock(dir);
++ break;
++ }
++ inode = br->br_xino->f_dentry->d_inode;
++ err = au_copy_file(file, br->br_xino, i_size_read(inode), sb,
++ &sparse);
++ //if (LktrCond) err = -1;
++ i_unlock(dir);
++ if (unlikely(err)) {
++ fput(file);
++ break;
++ }
++ fput(br->br_xino);
++ br->br_xino = file;
++ br->br_xino_read = find_readf(file);
++ DEBUG_ON(IS_ERR(br->br_xino_read));
++ br->br_xino_write = find_writef(file);
++ DEBUG_ON(IS_ERR(br->br_xino_write));
++ }
++
++ for (bindex = 0; bindex <= bend; bindex++)
++ if (unlikely(!stobr(sb, bindex)->br_xino)) {
++ err = xino_init(sb, bindex, xino->file, /*do_test*/1);
++ //if (LktrCond) {fput(stobr(sb, bindex)->br_xino);
++ //stobr(sb, bindex)->br_xino = NULL; err = -1;}
++ if (!err)
++ continue;
++ IOErr("creating xino for branch %d(%d), "
++ "forcing noxino\n", bindex, err);
++ err = -EIO;
++ break;
++ }
++ out:
++ dput(parent);
++ if (!err)
++ au_flag_set(sb, AuFlag_XINO);
++ else
++ au_flag_clr(sb, AuFlag_XINO);
++ TraceErr(err);
++ return err;
++}
++
++/*
++ * clear xino mount option
++ */
++int xino_clr(struct super_block *sb)
++{
++ aufs_bindex_t bindex, bend;
++
++ TraceEnter();
++ SiMustWriteLock(sb);
++
++ bend = sbend(sb);
++ for (bindex = 0; bindex <= bend; bindex++) {
++ struct aufs_branch *br;
++ br = stobr(sb, bindex);
++ if (br->br_xino) {
++ fput(br->br_xino);
++ br->br_xino = NULL;
++ }
++ }
++
++ //todo: need to make iunique() to return the larger inode number
++
++ au_flag_clr(sb, AuFlag_XINO);
++ return 0;
++}
++
++/*
++ * create a xinofile at the default place/path.
++ */
++struct file *xino_def(struct super_block *sb)
++{
++ struct file *file;
++ aufs_bindex_t bend, bindex, bwr;
++ char *page, *p;
++
++ bend = sbend(sb);
++ bwr = -1;
++ for (bindex = 0; bindex <= bend; bindex++)
++ if (br_writable(sbr_perm(sb, bindex))
++ && !au_is_nfs(au_h_dptr_i(sb->s_root, bindex)->d_sb)) {
++ bwr = bindex;
++ break;
++ }
++
++ if (bwr != -1) {
++ // todo: rewrite with lkup_one()
++ file = ERR_PTR(-ENOMEM);
++ page = __getname();
++ //if (LktrCond) {__putname(page); page = NULL;}
++ if (unlikely(!page))
++ goto out;
++ p = d_path(au_h_dptr_i(sb->s_root, bwr), sbr_mnt(sb, bwr), 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 = xino_create(sb, p, /*silent*/0, sb->s_root);
++ //if (LktrCond) {fput(file); file = ERR_PTR(-1);}
++ }
++ __putname(page);
++ } else {
++ file = xino_create(sb, AUFS_XINO_DEFPATH, /*silent*/0,
++ /*parent*/NULL);
++ //if (LktrCond) {fput(file); file = ERR_PTR(-1);}
++ }
++
++ out:
++ TraceErrPtr(file);
++ return file;
++}
+diff -rduNp linux-2.6.22.1.oorig/fs/squashfs/Makefile linux-2.6.22.1/fs/squashfs/Makefile
+--- linux-2.6.22.1.oorig/fs/squashfs/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/squashfs/Makefile 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,7 @@
++#
++# Makefile for the linux squashfs routines.
++#
++
++obj-$(CONFIG_SQUASHFS) += squashfs.o
++squashfs-y += inode.o
++squashfs-y += squashfs2_0.o
+diff -rduNp linux-2.6.22.1.oorig/fs/squashfs/inode.c linux-2.6.22.1/fs/squashfs/inode.c
+--- linux-2.6.22.1.oorig/fs/squashfs/inode.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/squashfs/inode.c 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,2329 @@
++/*
++ * Squashfs - a compressed read only filesystem for Linux
++ *
++ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
++ * Phillip Lougher <phillip@lougher.org.uk>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2,
++ * 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ * inode.c
++ */
++
++#include <linux/squashfs_fs.h>
++#include <linux/module.h>
++#include <linux/zlib.h>
++#include <linux/fs.h>
++#include <linux/squashfs_fs_sb.h>
++#include <linux/squashfs_fs_i.h>
++#include <linux/buffer_head.h>
++#include <linux/vfs.h>
++#include <linux/vmalloc.h>
++#include <linux/smp_lock.h>
++
++#include "squashfs.h"
++
++static void vfs_read_inode(struct inode *i);
++static struct dentry *squashfs_get_parent(struct dentry *child);
++static int squashfs_read_inode(struct inode *i, squashfs_inode_t inode);
++static int squashfs_statfs(struct dentry *, struct kstatfs *);
++static int squashfs_symlink_readpage(struct file *file, struct page *page);
++static long long read_blocklist(struct inode *inode, int index,
++ int readahead_blks, char *block_list,
++ unsigned short **block_p, unsigned int *bsize);
++static int squashfs_readpage(struct file *file, struct page *page);
++static int squashfs_readpage4K(struct file *file, struct page *page);
++static int squashfs_readdir(struct file *, void *, filldir_t);
++static struct dentry *squashfs_lookup(struct inode *, struct dentry *,
++ struct nameidata *);
++static int squashfs_remount(struct super_block *s, int *flags, char *data);
++static void squashfs_put_super(struct super_block *);
++static int squashfs_get_sb(struct file_system_type *,int, const char *, void *,
++ struct vfsmount *);
++static struct inode *squashfs_alloc_inode(struct super_block *sb);
++static void squashfs_destroy_inode(struct inode *inode);
++static int init_inodecache(void);
++static void destroy_inodecache(void);
++
++static struct file_system_type squashfs_fs_type = {
++ .owner = THIS_MODULE,
++ .name = "squashfs",
++ .get_sb = squashfs_get_sb,
++ .kill_sb = kill_block_super,
++ .fs_flags = FS_REQUIRES_DEV
++};
++
++static const unsigned char squashfs_filetype_table[] = {
++ DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK
++};
++
++static struct super_operations squashfs_super_ops = {
++ .alloc_inode = squashfs_alloc_inode,
++ .destroy_inode = squashfs_destroy_inode,
++ .statfs = squashfs_statfs,
++ .put_super = squashfs_put_super,
++ .remount_fs = squashfs_remount
++};
++
++static struct super_operations squashfs_export_super_ops = {
++ .alloc_inode = squashfs_alloc_inode,
++ .destroy_inode = squashfs_destroy_inode,
++ .statfs = squashfs_statfs,
++ .put_super = squashfs_put_super,
++ .read_inode = vfs_read_inode
++};
++
++static struct export_operations squashfs_export_ops = {
++ .get_parent = squashfs_get_parent
++};
++
++SQSH_EXTERN const struct address_space_operations squashfs_symlink_aops = {
++ .readpage = squashfs_symlink_readpage
++};
++
++SQSH_EXTERN const struct address_space_operations squashfs_aops = {
++ .readpage = squashfs_readpage
++};
++
++SQSH_EXTERN const struct address_space_operations squashfs_aops_4K = {
++ .readpage = squashfs_readpage4K
++};
++
++static const struct file_operations squashfs_dir_ops = {
++ .read = generic_read_dir,
++ .readdir = squashfs_readdir
++};
++
++SQSH_EXTERN struct inode_operations squashfs_dir_inode_ops = {
++ .lookup = squashfs_lookup
++};
++
++
++static struct buffer_head *get_block_length(struct super_block *s,
++ int *cur_index, int *offset, int *c_byte)
++{
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ unsigned short temp;
++ struct buffer_head *bh;
++
++ if (!(bh = sb_bread(s, *cur_index)))
++ goto out;
++
++ if (msblk->devblksize - *offset == 1) {
++ if (msblk->swap)
++ ((unsigned char *) &temp)[1] = *((unsigned char *)
++ (bh->b_data + *offset));
++ else
++ ((unsigned char *) &temp)[0] = *((unsigned char *)
++ (bh->b_data + *offset));
++ brelse(bh);
++ if (!(bh = sb_bread(s, ++(*cur_index))))
++ goto out;
++ if (msblk->swap)
++ ((unsigned char *) &temp)[0] = *((unsigned char *)
++ bh->b_data);
++ else
++ ((unsigned char *) &temp)[1] = *((unsigned char *)
++ bh->b_data);
++ *c_byte = temp;
++ *offset = 1;
++ } else {
++ if (msblk->swap) {
++ ((unsigned char *) &temp)[1] = *((unsigned char *)
++ (bh->b_data + *offset));
++ ((unsigned char *) &temp)[0] = *((unsigned char *)
++ (bh->b_data + *offset + 1));
++ } else {
++ ((unsigned char *) &temp)[0] = *((unsigned char *)
++ (bh->b_data + *offset));
++ ((unsigned char *) &temp)[1] = *((unsigned char *)
++ (bh->b_data + *offset + 1));
++ }
++ *c_byte = temp;
++ *offset += 2;
++ }
++
++ if (SQUASHFS_CHECK_DATA(msblk->sblk.flags)) {
++ if (*offset == msblk->devblksize) {
++ brelse(bh);
++ if (!(bh = sb_bread(s, ++(*cur_index))))
++ goto out;
++ *offset = 0;
++ }
++ if (*((unsigned char *) (bh->b_data + *offset)) !=
++ SQUASHFS_MARKER_BYTE) {
++ ERROR("Metadata block marker corrupt @ %x\n",
++ *cur_index);
++ brelse(bh);
++ goto out;
++ }
++ (*offset)++;
++ }
++ return bh;
++
++out:
++ return NULL;
++}
++
++
++SQSH_EXTERN unsigned int squashfs_read_data(struct super_block *s, char *buffer,
++ long long index, unsigned int length,
++ long long *next_index, int srclength)
++{
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++ struct buffer_head *bh[((SQUASHFS_FILE_MAX_SIZE - 1) >>
++ msblk->devblksize_log2) + 2];
++ unsigned int offset = index & ((1 << msblk->devblksize_log2) - 1);
++ unsigned int cur_index = index >> msblk->devblksize_log2;
++ int bytes, avail_bytes, b = 0, k = 0;
++ unsigned int compressed;
++ unsigned int c_byte = length;
++
++ if (c_byte) {
++ bytes = msblk->devblksize - offset;
++ compressed = SQUASHFS_COMPRESSED_BLOCK(c_byte);
++ c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte);
++
++ TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n", index, compressed
++ ? "" : "un", (unsigned int) c_byte, srclength);
++
++ if (c_byte > srclength || index < 0 || (index + c_byte) > sblk->bytes_used)
++ goto read_failure;
++
++ if (!(bh[0] = sb_getblk(s, cur_index)))
++ goto block_release;
++
++ for (b = 1; bytes < c_byte; b++) {
++ if (!(bh[b] = sb_getblk(s, ++cur_index)))
++ goto block_release;
++ bytes += msblk->devblksize;
++ }
++ ll_rw_block(READ, b, bh);
++ } else {
++ if (index < 0 || (index + 2) > sblk->bytes_used)
++ goto read_failure;
++
++ if (!(bh[0] = get_block_length(s, &cur_index, &offset,
++ &c_byte)))
++ goto read_failure;
++
++ bytes = msblk->devblksize - offset;
++ compressed = SQUASHFS_COMPRESSED(c_byte);
++ c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte);
++
++ TRACE("Block @ 0x%llx, %scompressed size %d\n", index, compressed
++ ? "" : "un", (unsigned int) c_byte);
++
++ if (c_byte > srclength || (index + c_byte) > sblk->bytes_used)
++ goto read_failure;
++
++ for (b = 1; bytes < c_byte; b++) {
++ if (!(bh[b] = sb_getblk(s, ++cur_index)))
++ goto block_release;
++ bytes += msblk->devblksize;
++ }
++ ll_rw_block(READ, b - 1, bh + 1);
++ }
++
++ if (compressed) {
++ int zlib_err = 0;
++
++ /*
++ * uncompress block
++ */
++
++ mutex_lock(&msblk->read_data_mutex);
++
++ msblk->stream.next_out = buffer;
++ msblk->stream.avail_out = srclength;
++
++ for (bytes = 0; k < b; k++) {
++ avail_bytes = (c_byte - bytes) > (msblk->devblksize - offset) ?
++ msblk->devblksize - offset :
++ c_byte - bytes;
++ wait_on_buffer(bh[k]);
++ if (!buffer_uptodate(bh[k]))
++ goto release_mutex;
++
++ msblk->stream.next_in = bh[k]->b_data + offset;
++ msblk->stream.avail_in = avail_bytes;
++
++ if (k == 0) {
++ zlib_err = zlib_inflateInit(&msblk->stream);
++ if (zlib_err != Z_OK) {
++ ERROR("zlib_inflateInit returned unexpected result 0x%x, srclength %d\n",
++ zlib_err, srclength);
++ goto release_mutex;
++ }
++
++ if (avail_bytes == 0) {
++ offset = 0;
++ brelse(bh[k]);
++ continue;
++ }
++ }
++
++ zlib_err = zlib_inflate(&msblk->stream, Z_NO_FLUSH);
++ if (zlib_err != Z_OK && zlib_err != Z_STREAM_END) {
++ ERROR("zlib_inflate returned unexpected result 0x%x, srclength %d, avail_in %d, avail_out %d\n",
++ zlib_err, srclength, msblk->stream.avail_in, msblk->stream.avail_out);
++ goto release_mutex;
++ }
++
++ bytes += avail_bytes;
++ offset = 0;
++ brelse(bh[k]);
++ }
++
++ if (zlib_err != Z_STREAM_END)
++ goto release_mutex;
++
++ zlib_err = zlib_inflateEnd(&msblk->stream);
++ if (zlib_err != Z_OK) {
++ ERROR("zlib_inflateEnd returned unexpected result 0x%x, srclength %d\n",
++ zlib_err, srclength);
++ goto release_mutex;
++ }
++ bytes = msblk->stream.total_out;
++ mutex_unlock(&msblk->read_data_mutex);
++ } else {
++ int i;
++
++ for(i = 0; i < b; i++) {
++ wait_on_buffer(bh[i]);
++ if(!buffer_uptodate(bh[i]))
++ goto block_release;
++ }
++
++ for (bytes = 0; k < b; k++) {
++ avail_bytes = (c_byte - bytes) > (msblk->devblksize - offset) ?
++ msblk->devblksize - offset :
++ c_byte - bytes;
++ memcpy(buffer + bytes, bh[k]->b_data + offset, avail_bytes);
++ bytes += avail_bytes;
++ offset = 0;
++ brelse(bh[k]);
++ }
++ }
++
++ if (next_index)
++ *next_index = index + c_byte + (length ? 0 :
++ (SQUASHFS_CHECK_DATA(msblk->sblk.flags)
++ ? 3 : 2));
++ return bytes;
++
++release_mutex:
++ mutex_unlock(&msblk->read_data_mutex);
++
++block_release:
++ for (; k < b; k++)
++ brelse(bh[k]);
++
++read_failure:
++ ERROR("sb_bread failed reading block 0x%x\n", cur_index);
++ return 0;
++}
++
++
++SQSH_EXTERN int squashfs_get_cached_block(struct super_block *s, char *buffer,
++ long long block, unsigned int offset,
++ int length, long long *next_block,
++ unsigned int *next_offset)
++{
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ int n, i, bytes, return_length = length;
++ long long next_index;
++
++ TRACE("Entered squashfs_get_cached_block [%llx:%x]\n", block, offset);
++
++ while ( 1 ) {
++ for (i = 0; i < SQUASHFS_CACHED_BLKS; i++)
++ if (msblk->block_cache[i].block == block)
++ break;
++
++ mutex_lock(&msblk->block_cache_mutex);
++
++ if (i == SQUASHFS_CACHED_BLKS) {
++ /* read inode header block */
++ for (i = msblk->next_cache, n = SQUASHFS_CACHED_BLKS;
++ n ; n --, i = (i + 1) %
++ SQUASHFS_CACHED_BLKS)
++ if (msblk->block_cache[i].block !=
++ SQUASHFS_USED_BLK)
++ break;
++
++ if (n == 0) {
++ wait_queue_t wait;
++
++ init_waitqueue_entry(&wait, current);
++ add_wait_queue(&msblk->waitq, &wait);
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ mutex_unlock(&msblk->block_cache_mutex);
++ schedule();
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(&msblk->waitq, &wait);
++ continue;
++ }
++ msblk->next_cache = (i + 1) % SQUASHFS_CACHED_BLKS;
++
++ if (msblk->block_cache[i].block ==
++ SQUASHFS_INVALID_BLK) {
++ if (!(msblk->block_cache[i].data =
++ kmalloc(SQUASHFS_METADATA_SIZE,
++ GFP_KERNEL))) {
++ ERROR("Failed to allocate cache"
++ "block\n");
++ mutex_unlock(&msblk->block_cache_mutex);
++ goto out;
++ }
++ }
++
++ msblk->block_cache[i].block = SQUASHFS_USED_BLK;
++ mutex_unlock(&msblk->block_cache_mutex);
++
++ msblk->block_cache[i].length = squashfs_read_data(s,
++ msblk->block_cache[i].data, block, 0, &next_index, SQUASHFS_METADATA_SIZE);
++ if (msblk->block_cache[i].length == 0) {
++ ERROR("Unable to read cache block [%llx:%x]\n",
++ block, offset);
++ mutex_lock(&msblk->block_cache_mutex);
++ msblk->block_cache[i].block = SQUASHFS_INVALID_BLK;
++ kfree(msblk->block_cache[i].data);
++ wake_up(&msblk->waitq);
++ mutex_unlock(&msblk->block_cache_mutex);
++ goto out;
++ }
++
++ mutex_lock(&msblk->block_cache_mutex);
++ wake_up(&msblk->waitq);
++ msblk->block_cache[i].block = block;
++ msblk->block_cache[i].next_index = next_index;
++ TRACE("Read cache block [%llx:%x]\n", block, offset);
++ }
++
++ if (msblk->block_cache[i].block != block) {
++ mutex_unlock(&msblk->block_cache_mutex);
++ continue;
++ }
++
++ bytes = msblk->block_cache[i].length - offset;
++
++ if (bytes < 1) {
++ mutex_unlock(&msblk->block_cache_mutex);
++ goto out;
++ } else if (bytes >= length) {
++ if (buffer)
++ memcpy(buffer, msblk->block_cache[i].data +
++ offset, length);
++ if (msblk->block_cache[i].length - offset == length) {
++ *next_block = msblk->block_cache[i].next_index;
++ *next_offset = 0;
++ } else {
++ *next_block = block;
++ *next_offset = offset + length;
++ }
++ mutex_unlock(&msblk->block_cache_mutex);
++ goto finish;
++ } else {
++ if (buffer) {
++ memcpy(buffer, msblk->block_cache[i].data +
++ offset, bytes);
++ buffer += bytes;
++ }
++ block = msblk->block_cache[i].next_index;
++ mutex_unlock(&msblk->block_cache_mutex);
++ length -= bytes;
++ offset = 0;
++ }
++ }
++
++finish:
++ return return_length;
++out:
++ return 0;
++}
++
++
++static int get_fragment_location(struct super_block *s, unsigned int fragment,
++ long long *fragment_start_block,
++ unsigned int *fragment_size)
++{
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ long long start_block =
++ msblk->fragment_index[SQUASHFS_FRAGMENT_INDEX(fragment)];
++ int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET(fragment);
++ struct squashfs_fragment_entry fragment_entry;
++
++ if (msblk->swap) {
++ struct squashfs_fragment_entry sfragment_entry;
++
++ if (!squashfs_get_cached_block(s, (char *) &sfragment_entry,
++ start_block, offset,
++ sizeof(sfragment_entry), &start_block,
++ &offset))
++ goto out;
++ SQUASHFS_SWAP_FRAGMENT_ENTRY(&fragment_entry, &sfragment_entry);
++ } else
++ if (!squashfs_get_cached_block(s, (char *) &fragment_entry,
++ start_block, offset,
++ sizeof(fragment_entry), &start_block,
++ &offset))
++ goto out;
++
++ *fragment_start_block = fragment_entry.start_block;
++ *fragment_size = fragment_entry.size;
++
++ return 1;
++
++out:
++ return 0;
++}
++
++
++SQSH_EXTERN void release_cached_fragment(struct squashfs_sb_info *msblk, struct
++ squashfs_fragment_cache *fragment)
++{
++ mutex_lock(&msblk->fragment_mutex);
++ fragment->locked --;
++ wake_up(&msblk->fragment_wait_queue);
++ mutex_unlock(&msblk->fragment_mutex);
++}
++
++
++SQSH_EXTERN struct squashfs_fragment_cache *get_cached_fragment(struct super_block
++ *s, long long start_block,
++ int length)
++{
++ int i, n;
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++
++ while ( 1 ) {
++ mutex_lock(&msblk->fragment_mutex);
++
++ for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS &&
++ msblk->fragment[i].block != start_block; i++);
++
++ if (i == SQUASHFS_CACHED_FRAGMENTS) {
++ for (i = msblk->next_fragment, n =
++ SQUASHFS_CACHED_FRAGMENTS; n &&
++ msblk->fragment[i].locked; n--, i = (i + 1) %
++ SQUASHFS_CACHED_FRAGMENTS);
++
++ if (n == 0) {
++ wait_queue_t wait;
++
++ init_waitqueue_entry(&wait, current);
++ add_wait_queue(&msblk->fragment_wait_queue,
++ &wait);
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ mutex_unlock(&msblk->fragment_mutex);
++ schedule();
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(&msblk->fragment_wait_queue,
++ &wait);
++ continue;
++ }
++ msblk->next_fragment = (msblk->next_fragment + 1) %
++ SQUASHFS_CACHED_FRAGMENTS;
++
++ if (msblk->fragment[i].data == NULL)
++ if (!(msblk->fragment[i].data = SQUASHFS_ALLOC
++ (SQUASHFS_FILE_MAX_SIZE))) {
++ ERROR("Failed to allocate fragment "
++ "cache block\n");
++ mutex_unlock(&msblk->fragment_mutex);
++ goto out;
++ }
++
++ msblk->fragment[i].block = SQUASHFS_INVALID_BLK;
++ msblk->fragment[i].locked = 1;
++ mutex_unlock(&msblk->fragment_mutex);
++
++ if (!(msblk->fragment[i].length = squashfs_read_data(s,
++ msblk->fragment[i].data,
++ start_block, length, NULL, sblk->block_size))) {
++ ERROR("Unable to read fragment cache block "
++ "[%llx]\n", start_block);
++ msblk->fragment[i].locked = 0;
++ smp_mb();
++ goto out;
++ }
++
++ mutex_lock(&msblk->fragment_mutex);
++ msblk->fragment[i].block = start_block;
++ TRACE("New fragment %d, start block %lld, locked %d\n",
++ i, msblk->fragment[i].block,
++ msblk->fragment[i].locked);
++ mutex_unlock(&msblk->fragment_mutex);
++ break;
++ }
++
++ msblk->fragment[i].locked++;
++ mutex_unlock(&msblk->fragment_mutex);
++ TRACE("Got fragment %d, start block %lld, locked %d\n", i,
++ msblk->fragment[i].block,
++ msblk->fragment[i].locked);
++ break;
++ }
++
++ return &msblk->fragment[i];
++
++out:
++ return NULL;
++}
++
++
++static void squashfs_new_inode(struct squashfs_sb_info *msblk, struct inode *i,
++ struct squashfs_base_inode_header *inodeb)
++{
++ i->i_ino = inodeb->inode_number;
++ i->i_mtime.tv_sec = inodeb->mtime;
++ i->i_atime.tv_sec = inodeb->mtime;
++ i->i_ctime.tv_sec = inodeb->mtime;
++ i->i_uid = msblk->uid[inodeb->uid];
++ i->i_mode = inodeb->mode;
++ i->i_size = 0;
++ if (inodeb->guid == SQUASHFS_GUIDS)
++ i->i_gid = i->i_uid;
++ else
++ i->i_gid = msblk->guid[inodeb->guid];
++}
++
++
++static squashfs_inode_t squashfs_inode_lookup(struct super_block *s, int ino)
++{
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ long long start = msblk->inode_lookup_table[SQUASHFS_LOOKUP_BLOCK(ino - 1)];
++ int offset = SQUASHFS_LOOKUP_BLOCK_OFFSET(ino - 1);
++ squashfs_inode_t inode;
++
++ TRACE("Entered squashfs_inode_lookup, inode_number = %d\n", ino);
++
++ if (msblk->swap) {
++ squashfs_inode_t sinode;
++
++ if (!squashfs_get_cached_block(s, (char *) &sinode, start, offset,
++ sizeof(sinode), &start, &offset))
++ goto out;
++ SQUASHFS_SWAP_INODE_T((&inode), &sinode);
++ } else if (!squashfs_get_cached_block(s, (char *) &inode, start, offset,
++ sizeof(inode), &start, &offset))
++ goto out;
++
++ TRACE("squashfs_inode_lookup, inode = 0x%llx\n", inode);
++
++ return inode;
++
++out:
++ return SQUASHFS_INVALID_BLK;
++}
++
++
++static void vfs_read_inode(struct inode *i)
++{
++ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
++ squashfs_inode_t inode = squashfs_inode_lookup(i->i_sb, i->i_ino);
++
++ TRACE("Entered vfs_read_inode\n");
++
++ if(inode != SQUASHFS_INVALID_BLK)
++ (msblk->read_inode)(i, inode);
++}
++
++
++static struct dentry *squashfs_get_parent(struct dentry *child)
++{
++ struct inode *i = child->d_inode;
++ struct inode *parent = iget(i->i_sb, SQUASHFS_I(i)->u.s2.parent_inode);
++ struct dentry *rv;
++
++ TRACE("Entered squashfs_get_parent\n");
++
++ if(parent == NULL) {
++ rv = ERR_PTR(-EACCES);
++ goto out;
++ }
++
++ rv = d_alloc_anon(parent);
++ if(rv == NULL)
++ rv = ERR_PTR(-ENOMEM);
++
++out:
++ return rv;
++}
++
++
++SQSH_EXTERN struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode, unsigned int inode_number)
++{
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ struct inode *i = iget_locked(s, inode_number);
++
++ TRACE("Entered squashfs_iget\n");
++
++ if(i && (i->i_state & I_NEW)) {
++ (msblk->read_inode)(i, inode);
++ unlock_new_inode(i);
++ }
++
++ return i;
++}
++
++
++static int squashfs_read_inode(struct inode *i, squashfs_inode_t inode)
++{
++ struct super_block *s = i->i_sb;
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++ long long block = SQUASHFS_INODE_BLK(inode) +
++ sblk->inode_table_start;
++ unsigned int offset = SQUASHFS_INODE_OFFSET(inode);
++ long long next_block;
++ unsigned int next_offset;
++ union squashfs_inode_header id, sid;
++ struct squashfs_base_inode_header *inodeb = &id.base,
++ *sinodeb = &sid.base;
++
++ TRACE("Entered squashfs_read_inode\n");
++
++ if (msblk->swap) {
++ if (!squashfs_get_cached_block(s, (char *) sinodeb, block,
++ offset, sizeof(*sinodeb), &next_block,
++ &next_offset))
++ goto failed_read;
++ SQUASHFS_SWAP_BASE_INODE_HEADER(inodeb, sinodeb,
++ sizeof(*sinodeb));
++ } else
++ if (!squashfs_get_cached_block(s, (char *) inodeb, block,
++ offset, sizeof(*inodeb), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ squashfs_new_inode(msblk, i, inodeb);
++
++ switch(inodeb->inode_type) {
++ case SQUASHFS_FILE_TYPE: {
++ unsigned int frag_size;
++ long long frag_blk;
++ struct squashfs_reg_inode_header *inodep = &id.reg;
++ struct squashfs_reg_inode_header *sinodep = &sid.reg;
++
++ if (msblk->swap) {
++ if (!squashfs_get_cached_block(s, (char *)
++ sinodep, block, offset,
++ sizeof(*sinodep), &next_block,
++ &next_offset))
++ goto failed_read;
++ SQUASHFS_SWAP_REG_INODE_HEADER(inodep, sinodep);
++ } else
++ if (!squashfs_get_cached_block(s, (char *)
++ inodep, block, offset,
++ sizeof(*inodep), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ frag_blk = SQUASHFS_INVALID_BLK;
++ if (inodep->fragment != SQUASHFS_INVALID_FRAG &&
++ !get_fragment_location(s,
++ inodep->fragment, &frag_blk, &frag_size))
++ goto failed_read;
++
++ i->i_nlink = 1;
++ i->i_size = inodep->file_size;
++ i->i_fop = &generic_ro_fops;
++ i->i_mode |= S_IFREG;
++ i->i_blocks = ((i->i_size - 1) >> 9) + 1;
++ SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk;
++ SQUASHFS_I(i)->u.s1.fragment_size = frag_size;
++ SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset;
++ SQUASHFS_I(i)->start_block = inodep->start_block;
++ SQUASHFS_I(i)->u.s1.block_list_start = next_block;
++ SQUASHFS_I(i)->offset = next_offset;
++ if (sblk->block_size > 4096)
++ i->i_data.a_ops = &squashfs_aops;
++ else
++ i->i_data.a_ops = &squashfs_aops_4K;
++
++ TRACE("File inode %x:%x, start_block %llx, "
++ "block_list_start %llx, offset %x\n",
++ SQUASHFS_INODE_BLK(inode), offset,
++ inodep->start_block, next_block,
++ next_offset);
++ break;
++ }
++ case SQUASHFS_LREG_TYPE: {
++ unsigned int frag_size;
++ long long frag_blk;
++ struct squashfs_lreg_inode_header *inodep = &id.lreg;
++ struct squashfs_lreg_inode_header *sinodep = &sid.lreg;
++
++ if (msblk->swap) {
++ if (!squashfs_get_cached_block(s, (char *)
++ sinodep, block, offset,
++ sizeof(*sinodep), &next_block,
++ &next_offset))
++ goto failed_read;
++ SQUASHFS_SWAP_LREG_INODE_HEADER(inodep, sinodep);
++ } else
++ if (!squashfs_get_cached_block(s, (char *)
++ inodep, block, offset,
++ sizeof(*inodep), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ frag_blk = SQUASHFS_INVALID_BLK;
++ if (inodep->fragment != SQUASHFS_INVALID_FRAG &&
++ !get_fragment_location(s,
++ inodep->fragment, &frag_blk, &frag_size))
++ goto failed_read;
++
++ i->i_nlink = inodep->nlink;
++ i->i_size = inodep->file_size;
++ i->i_fop = &generic_ro_fops;
++ i->i_mode |= S_IFREG;
++ i->i_blocks = ((i->i_size - 1) >> 9) + 1;
++ SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk;
++ SQUASHFS_I(i)->u.s1.fragment_size = frag_size;
++ SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset;
++ SQUASHFS_I(i)->start_block = inodep->start_block;
++ SQUASHFS_I(i)->u.s1.block_list_start = next_block;
++ SQUASHFS_I(i)->offset = next_offset;
++ if (sblk->block_size > 4096)
++ i->i_data.a_ops = &squashfs_aops;
++ else
++ i->i_data.a_ops = &squashfs_aops_4K;
++
++ TRACE("File inode %x:%x, start_block %llx, "
++ "block_list_start %llx, offset %x\n",
++ SQUASHFS_INODE_BLK(inode), offset,
++ inodep->start_block, next_block,
++ next_offset);
++ break;
++ }
++ case SQUASHFS_DIR_TYPE: {
++ struct squashfs_dir_inode_header *inodep = &id.dir;
++ struct squashfs_dir_inode_header *sinodep = &sid.dir;
++
++ if (msblk->swap) {
++ if (!squashfs_get_cached_block(s, (char *)
++ sinodep, block, offset,
++ sizeof(*sinodep), &next_block,
++ &next_offset))
++ goto failed_read;
++ SQUASHFS_SWAP_DIR_INODE_HEADER(inodep, sinodep);
++ } else
++ if (!squashfs_get_cached_block(s, (char *)
++ inodep, block, offset,
++ sizeof(*inodep), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ i->i_nlink = inodep->nlink;
++ i->i_size = inodep->file_size;
++ i->i_op = &squashfs_dir_inode_ops;
++ i->i_fop = &squashfs_dir_ops;
++ i->i_mode |= S_IFDIR;
++ SQUASHFS_I(i)->start_block = inodep->start_block;
++ SQUASHFS_I(i)->offset = inodep->offset;
++ SQUASHFS_I(i)->u.s2.directory_index_count = 0;
++ SQUASHFS_I(i)->u.s2.parent_inode = inodep->parent_inode;
++
++ TRACE("Directory inode %x:%x, start_block %x, offset "
++ "%x\n", SQUASHFS_INODE_BLK(inode),
++ offset, inodep->start_block,
++ inodep->offset);
++ break;
++ }
++ case SQUASHFS_LDIR_TYPE: {
++ struct squashfs_ldir_inode_header *inodep = &id.ldir;
++ struct squashfs_ldir_inode_header *sinodep = &sid.ldir;
++
++ if (msblk->swap) {
++ if (!squashfs_get_cached_block(s, (char *)
++ sinodep, block, offset,
++ sizeof(*sinodep), &next_block,
++ &next_offset))
++ goto failed_read;
++ SQUASHFS_SWAP_LDIR_INODE_HEADER(inodep,
++ sinodep);
++ } else
++ if (!squashfs_get_cached_block(s, (char *)
++ inodep, block, offset,
++ sizeof(*inodep), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ i->i_nlink = inodep->nlink;
++ i->i_size = inodep->file_size;
++ i->i_op = &squashfs_dir_inode_ops;
++ i->i_fop = &squashfs_dir_ops;
++ i->i_mode |= S_IFDIR;
++ SQUASHFS_I(i)->start_block = inodep->start_block;
++ SQUASHFS_I(i)->offset = inodep->offset;
++ SQUASHFS_I(i)->u.s2.directory_index_start = next_block;
++ SQUASHFS_I(i)->u.s2.directory_index_offset =
++ next_offset;
++ SQUASHFS_I(i)->u.s2.directory_index_count =
++ inodep->i_count;
++ SQUASHFS_I(i)->u.s2.parent_inode = inodep->parent_inode;
++
++ TRACE("Long directory inode %x:%x, start_block %x, "
++ "offset %x\n",
++ SQUASHFS_INODE_BLK(inode), offset,
++ inodep->start_block, inodep->offset);
++ break;
++ }
++ case SQUASHFS_SYMLINK_TYPE: {
++ struct squashfs_symlink_inode_header *inodep =
++ &id.symlink;
++ struct squashfs_symlink_inode_header *sinodep =
++ &sid.symlink;
++
++ if (msblk->swap) {
++ if (!squashfs_get_cached_block(s, (char *)
++ sinodep, block, offset,
++ sizeof(*sinodep), &next_block,
++ &next_offset))
++ goto failed_read;
++ SQUASHFS_SWAP_SYMLINK_INODE_HEADER(inodep,
++ sinodep);
++ } else
++ if (!squashfs_get_cached_block(s, (char *)
++ inodep, block, offset,
++ sizeof(*inodep), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ i->i_nlink = inodep->nlink;
++ i->i_size = inodep->symlink_size;
++ i->i_op = &page_symlink_inode_operations;
++ i->i_data.a_ops = &squashfs_symlink_aops;
++ i->i_mode |= S_IFLNK;
++ SQUASHFS_I(i)->start_block = next_block;
++ SQUASHFS_I(i)->offset = next_offset;
++
++ TRACE("Symbolic link inode %x:%x, start_block %llx, "
++ "offset %x\n",
++ SQUASHFS_INODE_BLK(inode), offset,
++ next_block, next_offset);
++ break;
++ }
++ case SQUASHFS_BLKDEV_TYPE:
++ case SQUASHFS_CHRDEV_TYPE: {
++ struct squashfs_dev_inode_header *inodep = &id.dev;
++ struct squashfs_dev_inode_header *sinodep = &sid.dev;
++
++ if (msblk->swap) {
++ if (!squashfs_get_cached_block(s, (char *)
++ sinodep, block, offset,
++ sizeof(*sinodep), &next_block,
++ &next_offset))
++ goto failed_read;
++ SQUASHFS_SWAP_DEV_INODE_HEADER(inodep, sinodep);
++ } else
++ if (!squashfs_get_cached_block(s, (char *)
++ inodep, block, offset,
++ sizeof(*inodep), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ i->i_nlink = inodep->nlink;
++ i->i_mode |= (inodeb->inode_type ==
++ SQUASHFS_CHRDEV_TYPE) ? S_IFCHR :
++ S_IFBLK;
++ init_special_inode(i, i->i_mode,
++ old_decode_dev(inodep->rdev));
++
++ TRACE("Device inode %x:%x, rdev %x\n",
++ SQUASHFS_INODE_BLK(inode), offset,
++ inodep->rdev);
++ break;
++ }
++ case SQUASHFS_FIFO_TYPE:
++ case SQUASHFS_SOCKET_TYPE: {
++ struct squashfs_ipc_inode_header *inodep = &id.ipc;
++ struct squashfs_ipc_inode_header *sinodep = &sid.ipc;
++
++ if (msblk->swap) {
++ if (!squashfs_get_cached_block(s, (char *)
++ sinodep, block, offset,
++ sizeof(*sinodep), &next_block,
++ &next_offset))
++ goto failed_read;
++ SQUASHFS_SWAP_IPC_INODE_HEADER(inodep, sinodep);
++ } else
++ if (!squashfs_get_cached_block(s, (char *)
++ inodep, block, offset,
++ sizeof(*inodep), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ i->i_nlink = inodep->nlink;
++ i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE)
++ ? S_IFIFO : S_IFSOCK;
++ init_special_inode(i, i->i_mode, 0);
++ break;
++ }
++ default:
++ ERROR("Unknown inode type %d in squashfs_iget!\n",
++ inodeb->inode_type);
++ goto failed_read1;
++ }
++
++ return 1;
++
++failed_read:
++ ERROR("Unable to read inode [%llx:%x]\n", block, offset);
++
++failed_read1:
++ make_bad_inode(i);
++ return 0;
++}
++
++
++static int read_inode_lookup_table(struct super_block *s)
++{
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++ unsigned int length = SQUASHFS_LOOKUP_BLOCK_BYTES(sblk->inodes);
++
++ TRACE("In read_inode_lookup_table, length %d\n", length);
++
++ /* Allocate inode lookup table */
++ if (!(msblk->inode_lookup_table = kmalloc(length, GFP_KERNEL))) {
++ ERROR("Failed to allocate inode lookup table\n");
++ return 0;
++ }
++
++ if (!squashfs_read_data(s, (char *) msblk->inode_lookup_table,
++ sblk->lookup_table_start, length |
++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length)) {
++ ERROR("unable to read inode lookup table\n");
++ return 0;
++ }
++
++ if (msblk->swap) {
++ int i;
++ long long block;
++
++ for (i = 0; i < SQUASHFS_LOOKUP_BLOCKS(sblk->inodes); i++) {
++ SQUASHFS_SWAP_LOOKUP_BLOCKS((&block),
++ &msblk->inode_lookup_table[i], 1);
++ msblk->inode_lookup_table[i] = block;
++ }
++ }
++
++ return 1;
++}
++
++
++static int read_fragment_index_table(struct super_block *s)
++{
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++ unsigned int length = SQUASHFS_FRAGMENT_INDEX_BYTES(sblk->fragments);
++
++ if(length == 0)
++ return 1;
++
++ /* Allocate fragment index table */
++ if (!(msblk->fragment_index = kmalloc(length, GFP_KERNEL))) {
++ ERROR("Failed to allocate fragment index table\n");
++ return 0;
++ }
++
++ if (!squashfs_read_data(s, (char *) msblk->fragment_index,
++ sblk->fragment_table_start, length |
++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length)) {
++ ERROR("unable to read fragment index table\n");
++ return 0;
++ }
++
++ if (msblk->swap) {
++ int i;
++ long long fragment;
++
++ for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES(sblk->fragments); i++) {
++ SQUASHFS_SWAP_FRAGMENT_INDEXES((&fragment),
++ &msblk->fragment_index[i], 1);
++ msblk->fragment_index[i] = fragment;
++ }
++ }
++
++ return 1;
++}
++
++
++static int supported_squashfs_filesystem(struct squashfs_sb_info *msblk, int silent)
++{
++ struct squashfs_super_block *sblk = &msblk->sblk;
++
++ msblk->read_inode = squashfs_read_inode;
++ msblk->read_blocklist = read_blocklist;
++ msblk->read_fragment_index_table = read_fragment_index_table;
++
++ if (sblk->s_major == 1) {
++ if (!squashfs_1_0_supported(msblk)) {
++ SERROR("Major/Minor mismatch, Squashfs 1.0 filesystems "
++ "are unsupported\n");
++ SERROR("Please recompile with "
++ "Squashfs 1.0 support enabled\n");
++ return 0;
++ }
++ } else if (sblk->s_major == 2) {
++ if (!squashfs_2_0_supported(msblk)) {
++ SERROR("Major/Minor mismatch, Squashfs 2.0 filesystems "
++ "are unsupported\n");
++ SERROR("Please recompile with "
++ "Squashfs 2.0 support enabled\n");
++ return 0;
++ }
++ } else if(sblk->s_major != SQUASHFS_MAJOR || sblk->s_minor >
++ SQUASHFS_MINOR) {
++ SERROR("Major/Minor mismatch, trying to mount newer %d.%d "
++ "filesystem\n", sblk->s_major, sblk->s_minor);
++ SERROR("Please update your kernel\n");
++ return 0;
++ }
++
++ return 1;
++}
++
++
++static int squashfs_fill_super(struct super_block *s, void *data, int silent)
++{
++ struct squashfs_sb_info *msblk;
++ struct squashfs_super_block *sblk;
++ int i;
++ char b[BDEVNAME_SIZE];
++ struct inode *root;
++
++ TRACE("Entered squashfs_read_superblock\n");
++
++ if (!(s->s_fs_info = kmalloc(sizeof(struct squashfs_sb_info),
++ GFP_KERNEL))) {
++ ERROR("Failed to allocate superblock\n");
++ goto failure;
++ }
++ memset(s->s_fs_info, 0, sizeof(struct squashfs_sb_info));
++ msblk = s->s_fs_info;
++ if (!(msblk->stream.workspace = vmalloc(zlib_inflate_workspacesize()))) {
++ ERROR("Failed to allocate zlib workspace\n");
++ goto failure;
++ }
++ sblk = &msblk->sblk;
++
++ msblk->devblksize = sb_min_blocksize(s, BLOCK_SIZE);
++ msblk->devblksize_log2 = ffz(~msblk->devblksize);
++
++ mutex_init(&msblk->read_data_mutex);
++ mutex_init(&msblk->read_page_mutex);
++ mutex_init(&msblk->block_cache_mutex);
++ mutex_init(&msblk->fragment_mutex);
++ mutex_init(&msblk->meta_index_mutex);
++
++ init_waitqueue_head(&msblk->waitq);
++ init_waitqueue_head(&msblk->fragment_wait_queue);
++
++ sblk->bytes_used = sizeof(struct squashfs_super_block);
++ if (!squashfs_read_data(s, (char *) sblk, SQUASHFS_START,
++ sizeof(struct squashfs_super_block) |
++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, sizeof(struct squashfs_super_block))) {
++ SERROR("unable to read superblock\n");
++ goto failed_mount;
++ }
++
++ /* Check it is a SQUASHFS superblock */
++ msblk->swap = 0;
++ if ((s->s_magic = sblk->s_magic) != SQUASHFS_MAGIC) {
++ if (sblk->s_magic == SQUASHFS_MAGIC_SWAP) {
++ struct squashfs_super_block ssblk;
++
++ WARNING("Mounting a different endian SQUASHFS "
++ "filesystem on %s\n", bdevname(s->s_bdev, b));
++
++ SQUASHFS_SWAP_SUPER_BLOCK(&ssblk, sblk);
++ memcpy(sblk, &ssblk, sizeof(struct squashfs_super_block));
++ msblk->swap = 1;
++ } else {
++ SERROR("Can't find a SQUASHFS superblock on %s\n",
++ bdevname(s->s_bdev, b));
++ goto failed_mount;
++ }
++ }
++
++ /* Check the MAJOR & MINOR versions */
++ if(!supported_squashfs_filesystem(msblk, silent))
++ goto failed_mount;
++
++ /* Check the filesystem does not extend beyond the end of the
++ block device */
++ if(sblk->bytes_used < 0 || sblk->bytes_used > i_size_read(s->s_bdev->bd_inode))
++ goto failed_mount;
++
++ /* Check the root inode for sanity */
++ if (SQUASHFS_INODE_OFFSET(sblk->root_inode) > SQUASHFS_METADATA_SIZE)
++ goto failed_mount;
++
++ TRACE("Found valid superblock on %s\n", bdevname(s->s_bdev, b));
++ TRACE("Inodes are %scompressed\n",
++ SQUASHFS_UNCOMPRESSED_INODES
++ (sblk->flags) ? "un" : "");
++ TRACE("Data is %scompressed\n",
++ SQUASHFS_UNCOMPRESSED_DATA(sblk->flags)
++ ? "un" : "");
++ TRACE("Check data is %s present in the filesystem\n",
++ SQUASHFS_CHECK_DATA(sblk->flags) ?
++ "" : "not");
++ TRACE("Filesystem size %lld bytes\n", sblk->bytes_used);
++ TRACE("Block size %d\n", sblk->block_size);
++ TRACE("Number of inodes %d\n", sblk->inodes);
++ if (sblk->s_major > 1)
++ TRACE("Number of fragments %d\n", sblk->fragments);
++ TRACE("Number of uids %d\n", sblk->no_uids);
++ TRACE("Number of gids %d\n", sblk->no_guids);
++ TRACE("sblk->inode_table_start %llx\n", sblk->inode_table_start);
++ TRACE("sblk->directory_table_start %llx\n", sblk->directory_table_start);
++ if (sblk->s_major > 1)
++ TRACE("sblk->fragment_table_start %llx\n",
++ sblk->fragment_table_start);
++ TRACE("sblk->uid_start %llx\n", sblk->uid_start);
++
++ s->s_flags |= MS_RDONLY;
++ s->s_op = &squashfs_super_ops;
++
++ /* Init inode_table block pointer array */
++ if (!(msblk->block_cache = kmalloc(sizeof(struct squashfs_cache) *
++ SQUASHFS_CACHED_BLKS, GFP_KERNEL))) {
++ ERROR("Failed to allocate block cache\n");
++ goto failed_mount;
++ }
++
++ for (i = 0; i < SQUASHFS_CACHED_BLKS; i++)
++ msblk->block_cache[i].block = SQUASHFS_INVALID_BLK;
++
++ msblk->next_cache = 0;
++
++ /* Allocate read_page block */
++ if (!(msblk->read_page = kmalloc(sblk->block_size, GFP_KERNEL))) {
++ ERROR("Failed to allocate read_page block\n");
++ goto failed_mount;
++ }
++
++ /* Allocate uid and gid tables */
++ if (!(msblk->uid = kmalloc((sblk->no_uids + sblk->no_guids) *
++ sizeof(unsigned int), GFP_KERNEL))) {
++ ERROR("Failed to allocate uid/gid table\n");
++ goto failed_mount;
++ }
++ msblk->guid = msblk->uid + sblk->no_uids;
++
++ if (msblk->swap) {
++ unsigned int suid[sblk->no_uids + sblk->no_guids];
++
++ if (!squashfs_read_data(s, (char *) &suid, sblk->uid_start,
++ ((sblk->no_uids + sblk->no_guids) *
++ sizeof(unsigned int)) |
++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, (sblk->no_uids + sblk->no_guids) * sizeof(unsigned int))) {
++ ERROR("unable to read uid/gid table\n");
++ goto failed_mount;
++ }
++
++ SQUASHFS_SWAP_DATA(msblk->uid, suid, (sblk->no_uids +
++ sblk->no_guids), (sizeof(unsigned int) * 8));
++ } else
++ if (!squashfs_read_data(s, (char *) msblk->uid, sblk->uid_start,
++ ((sblk->no_uids + sblk->no_guids) *
++ sizeof(unsigned int)) |
++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, (sblk->no_uids + sblk->no_guids) * sizeof(unsigned int))) {
++ ERROR("unable to read uid/gid table\n");
++ goto failed_mount;
++ }
++
++
++ if (sblk->s_major == 1 && squashfs_1_0_supported(msblk))
++ goto allocate_root;
++
++ if (!(msblk->fragment = kmalloc(sizeof(struct squashfs_fragment_cache) *
++ SQUASHFS_CACHED_FRAGMENTS, GFP_KERNEL))) {
++ ERROR("Failed to allocate fragment block cache\n");
++ goto failed_mount;
++ }
++
++ for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++) {
++ msblk->fragment[i].locked = 0;
++ msblk->fragment[i].block = SQUASHFS_INVALID_BLK;
++ msblk->fragment[i].data = NULL;
++ }
++
++ msblk->next_fragment = 0;
++
++ /* Allocate and read fragment index table */
++ if (msblk->read_fragment_index_table(s) == 0)
++ goto failed_mount;
++
++ if(sblk->s_major < 3 || sblk->lookup_table_start == SQUASHFS_INVALID_BLK)
++ goto allocate_root;
++
++ /* Allocate and read inode lookup table */
++ if (read_inode_lookup_table(s) == 0)
++ goto failed_mount;
++
++ s->s_op = &squashfs_export_super_ops;
++ s->s_export_op = &squashfs_export_ops;
++
++allocate_root:
++ root = new_inode(s);
++ if ((msblk->read_inode)(root, sblk->root_inode) == 0)
++ goto failed_mount;
++ insert_inode_hash(root);
++
++ if ((s->s_root = d_alloc_root(root)) == NULL) {
++ ERROR("Root inode create failed\n");
++ iput(root);
++ goto failed_mount;
++ }
++
++ TRACE("Leaving squashfs_read_super\n");
++ return 0;
++
++failed_mount:
++ kfree(msblk->inode_lookup_table);
++ kfree(msblk->fragment_index);
++ kfree(msblk->fragment);
++ kfree(msblk->uid);
++ kfree(msblk->read_page);
++ kfree(msblk->block_cache);
++ kfree(msblk->fragment_index_2);
++ vfree(msblk->stream.workspace);
++ kfree(s->s_fs_info);
++ s->s_fs_info = NULL;
++ return -EINVAL;
++
++failure:
++ return -ENOMEM;
++}
++
++
++static int squashfs_statfs(struct dentry *dentry, struct kstatfs *buf)
++{
++ struct squashfs_sb_info *msblk = dentry->d_sb->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++
++ TRACE("Entered squashfs_statfs\n");
++
++ buf->f_type = SQUASHFS_MAGIC;
++ buf->f_bsize = sblk->block_size;
++ buf->f_blocks = ((sblk->bytes_used - 1) >> sblk->block_log) + 1;
++ buf->f_bfree = buf->f_bavail = 0;
++ buf->f_files = sblk->inodes;
++ buf->f_ffree = 0;
++ buf->f_namelen = SQUASHFS_NAME_LEN;
++
++ return 0;
++}
++
++
++static int squashfs_symlink_readpage(struct file *file, struct page *page)
++{
++ struct inode *inode = page->mapping->host;
++ int index = page->index << PAGE_CACHE_SHIFT, length, bytes;
++ long long block = SQUASHFS_I(inode)->start_block;
++ int offset = SQUASHFS_I(inode)->offset;
++ void *pageaddr = kmap(page);
++
++ TRACE("Entered squashfs_symlink_readpage, page index %ld, start block "
++ "%llx, offset %x\n", page->index,
++ SQUASHFS_I(inode)->start_block,
++ SQUASHFS_I(inode)->offset);
++
++ for (length = 0; length < index; length += bytes) {
++ if (!(bytes = squashfs_get_cached_block(inode->i_sb, NULL,
++ block, offset, PAGE_CACHE_SIZE, &block,
++ &offset))) {
++ ERROR("Unable to read symbolic link [%llx:%x]\n", block,
++ offset);
++ goto skip_read;
++ }
++ }
++
++ if (length != index) {
++ ERROR("(squashfs_symlink_readpage) length != index\n");
++ bytes = 0;
++ goto skip_read;
++ }
++
++ bytes = (i_size_read(inode) - length) > PAGE_CACHE_SIZE ? PAGE_CACHE_SIZE :
++ i_size_read(inode) - length;
++
++ if (!(bytes = squashfs_get_cached_block(inode->i_sb, pageaddr, block,
++ offset, bytes, &block, &offset)))
++ ERROR("Unable to read symbolic link [%llx:%x]\n", block, offset);
++
++skip_read:
++ memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
++ kunmap(page);
++ flush_dcache_page(page);
++ SetPageUptodate(page);
++ unlock_page(page);
++
++ return 0;
++}
++
++
++struct meta_index *locate_meta_index(struct inode *inode, int index, int offset)
++{
++ struct meta_index *meta = NULL;
++ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
++ int i;
++
++ mutex_lock(&msblk->meta_index_mutex);
++
++ TRACE("locate_meta_index: index %d, offset %d\n", index, offset);
++
++ if(msblk->meta_index == NULL)
++ goto not_allocated;
++
++ for (i = 0; i < SQUASHFS_META_NUMBER; i ++)
++ if (msblk->meta_index[i].inode_number == inode->i_ino &&
++ msblk->meta_index[i].offset >= offset &&
++ msblk->meta_index[i].offset <= index &&
++ msblk->meta_index[i].locked == 0) {
++ TRACE("locate_meta_index: entry %d, offset %d\n", i,
++ msblk->meta_index[i].offset);
++ meta = &msblk->meta_index[i];
++ offset = meta->offset;
++ }
++
++ if (meta)
++ meta->locked = 1;
++
++not_allocated:
++ mutex_unlock(&msblk->meta_index_mutex);
++
++ return meta;
++}
++
++
++struct meta_index *empty_meta_index(struct inode *inode, int offset, int skip)
++{
++ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
++ struct meta_index *meta = NULL;
++ int i;
++
++ mutex_lock(&msblk->meta_index_mutex);
++
++ TRACE("empty_meta_index: offset %d, skip %d\n", offset, skip);
++
++ if(msblk->meta_index == NULL) {
++ if (!(msblk->meta_index = kmalloc(sizeof(struct meta_index) *
++ SQUASHFS_META_NUMBER, GFP_KERNEL))) {
++ ERROR("Failed to allocate meta_index\n");
++ goto failed;
++ }
++ for(i = 0; i < SQUASHFS_META_NUMBER; i++) {
++ msblk->meta_index[i].inode_number = 0;
++ msblk->meta_index[i].locked = 0;
++ }
++ msblk->next_meta_index = 0;
++ }
++
++ for(i = SQUASHFS_META_NUMBER; i &&
++ msblk->meta_index[msblk->next_meta_index].locked; i --)
++ msblk->next_meta_index = (msblk->next_meta_index + 1) %
++ SQUASHFS_META_NUMBER;
++
++ if(i == 0) {
++ TRACE("empty_meta_index: failed!\n");
++ goto failed;
++ }
++
++ TRACE("empty_meta_index: returned meta entry %d, %p\n",
++ msblk->next_meta_index,
++ &msblk->meta_index[msblk->next_meta_index]);
++
++ meta = &msblk->meta_index[msblk->next_meta_index];
++ msblk->next_meta_index = (msblk->next_meta_index + 1) %
++ SQUASHFS_META_NUMBER;
++
++ meta->inode_number = inode->i_ino;
++ meta->offset = offset;
++ meta->skip = skip;
++ meta->entries = 0;
++ meta->locked = 1;
++
++failed:
++ mutex_unlock(&msblk->meta_index_mutex);
++ return meta;
++}
++
++
++void release_meta_index(struct inode *inode, struct meta_index *meta)
++{
++ meta->locked = 0;
++ smp_mb();
++}
++
++
++static int read_block_index(struct super_block *s, int blocks, char *block_list,
++ long long *start_block, int *offset)
++{
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ unsigned int *block_listp;
++ int block = 0;
++
++ if (msblk->swap) {
++ char sblock_list[blocks << 2];
++
++ if (!squashfs_get_cached_block(s, sblock_list, *start_block,
++ *offset, blocks << 2, start_block, offset)) {
++ ERROR("Unable to read block list [%llx:%x]\n",
++ *start_block, *offset);
++ goto failure;
++ }
++ SQUASHFS_SWAP_INTS(((unsigned int *)block_list),
++ ((unsigned int *)sblock_list), blocks);
++ } else
++ if (!squashfs_get_cached_block(s, block_list, *start_block,
++ *offset, blocks << 2, start_block, offset)) {
++ ERROR("Unable to read block list [%llx:%x]\n",
++ *start_block, *offset);
++ goto failure;
++ }
++
++ for (block_listp = (unsigned int *) block_list; blocks;
++ block_listp++, blocks --)
++ block += SQUASHFS_COMPRESSED_SIZE_BLOCK(*block_listp);
++
++ return block;
++
++failure:
++ return -1;
++}
++
++
++#define SIZE 256
++
++static inline int calculate_skip(int blocks) {
++ int skip = (blocks - 1) / ((SQUASHFS_SLOTS * SQUASHFS_META_ENTRIES + 1) * SQUASHFS_META_INDEXES);
++ return skip >= 7 ? 7 : skip + 1;
++}
++
++
++static int get_meta_index(struct inode *inode, int index,
++ long long *index_block, int *index_offset,
++ long long *data_block, char *block_list)
++{
++ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++ int skip = calculate_skip(i_size_read(inode) >> sblk->block_log);
++ int offset = 0;
++ struct meta_index *meta;
++ struct meta_entry *meta_entry;
++ long long cur_index_block = SQUASHFS_I(inode)->u.s1.block_list_start;
++ int cur_offset = SQUASHFS_I(inode)->offset;
++ long long cur_data_block = SQUASHFS_I(inode)->start_block;
++ int i;
++
++ index /= SQUASHFS_META_INDEXES * skip;
++
++ while ( offset < index ) {
++ meta = locate_meta_index(inode, index, offset + 1);
++
++ if (meta == NULL) {
++ if ((meta = empty_meta_index(inode, offset + 1,
++ skip)) == NULL)
++ goto all_done;
++ } else {
++ if(meta->entries == 0)
++ goto failed;
++ offset = index < meta->offset + meta->entries ? index :
++ meta->offset + meta->entries - 1;
++ meta_entry = &meta->meta_entry[offset - meta->offset];
++ cur_index_block = meta_entry->index_block + sblk->inode_table_start;
++ cur_offset = meta_entry->offset;
++ cur_data_block = meta_entry->data_block;
++ TRACE("get_meta_index: offset %d, meta->offset %d, "
++ "meta->entries %d\n", offset, meta->offset,
++ meta->entries);
++ TRACE("get_meta_index: index_block 0x%llx, offset 0x%x"
++ " data_block 0x%llx\n", cur_index_block,
++ cur_offset, cur_data_block);
++ }
++
++ for (i = meta->offset + meta->entries; i <= index &&
++ i < meta->offset + SQUASHFS_META_ENTRIES; i++) {
++ int blocks = skip * SQUASHFS_META_INDEXES;
++
++ while (blocks) {
++ int block = blocks > (SIZE >> 2) ? (SIZE >> 2) :
++ blocks;
++ int res = read_block_index(inode->i_sb, block,
++ block_list, &cur_index_block,
++ &cur_offset);
++
++ if (res == -1)
++ goto failed;
++
++ cur_data_block += res;
++ blocks -= block;
++ }
++
++ meta_entry = &meta->meta_entry[i - meta->offset];
++ meta_entry->index_block = cur_index_block - sblk->inode_table_start;
++ meta_entry->offset = cur_offset;
++ meta_entry->data_block = cur_data_block;
++ meta->entries ++;
++ offset ++;
++ }
++
++ TRACE("get_meta_index: meta->offset %d, meta->entries %d\n",
++ meta->offset, meta->entries);
++
++ release_meta_index(inode, meta);
++ }
++
++all_done:
++ *index_block = cur_index_block;
++ *index_offset = cur_offset;
++ *data_block = cur_data_block;
++
++ return offset * SQUASHFS_META_INDEXES * skip;
++
++failed:
++ release_meta_index(inode, meta);
++ return -1;
++}
++
++
++static long long read_blocklist(struct inode *inode, int index,
++ int readahead_blks, char *block_list,
++ unsigned short **block_p, unsigned int *bsize)
++{
++ long long block_ptr;
++ int offset;
++ long long block;
++ int res = get_meta_index(inode, index, &block_ptr, &offset, &block,
++ block_list);
++
++ TRACE("read_blocklist: res %d, index %d, block_ptr 0x%llx, offset"
++ " 0x%x, block 0x%llx\n", res, index, block_ptr, offset,
++ block);
++
++ if(res == -1)
++ goto failure;
++
++ index -= res;
++
++ while ( index ) {
++ int blocks = index > (SIZE >> 2) ? (SIZE >> 2) : index;
++ int res = read_block_index(inode->i_sb, blocks, block_list,
++ &block_ptr, &offset);
++ if (res == -1)
++ goto failure;
++ block += res;
++ index -= blocks;
++ }
++
++ if (read_block_index(inode->i_sb, 1, block_list,
++ &block_ptr, &offset) == -1)
++ goto failure;
++ *bsize = *((unsigned int *) block_list);
++
++ return block;
++
++failure:
++ return 0;
++}
++
++
++static int squashfs_readpage(struct file *file, struct page *page)
++{
++ struct inode *inode = page->mapping->host;
++ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++ unsigned char *block_list;
++ long long block;
++ unsigned int bsize, i = 0, bytes = 0, byte_offset = 0;
++ int index = page->index >> (sblk->block_log - PAGE_CACHE_SHIFT);
++ void *pageaddr;
++ struct squashfs_fragment_cache *fragment = NULL;
++ char *data_ptr = msblk->read_page;
++
++ int mask = (1 << (sblk->block_log - PAGE_CACHE_SHIFT)) - 1;
++ int start_index = page->index & ~mask;
++ int end_index = start_index | mask;
++
++ TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n",
++ page->index,
++ SQUASHFS_I(inode)->start_block);
++
++ if (!(block_list = kmalloc(SIZE, GFP_KERNEL))) {
++ ERROR("Failed to allocate block_list\n");
++ goto skip_read;
++ }
++
++ if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
++ PAGE_CACHE_SHIFT))
++ goto skip_read;
++
++ if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK
++ || index < (i_size_read(inode) >>
++ sblk->block_log)) {
++ if ((block = (msblk->read_blocklist)(inode, index, 1,
++ block_list, NULL, &bsize)) == 0)
++ goto skip_read;
++
++ mutex_lock(&msblk->read_page_mutex);
++
++ if (!(bytes = squashfs_read_data(inode->i_sb, msblk->read_page,
++ block, bsize, NULL, sblk->block_size))) {
++ ERROR("Unable to read page, block %llx, size %x\n", block,
++ bsize);
++ mutex_unlock(&msblk->read_page_mutex);
++ goto skip_read;
++ }
++ } else {
++ if ((fragment = get_cached_fragment(inode->i_sb,
++ SQUASHFS_I(inode)->
++ u.s1.fragment_start_block,
++ SQUASHFS_I(inode)->u.s1.fragment_size))
++ == NULL) {
++ ERROR("Unable to read page, block %llx, size %x\n",
++ SQUASHFS_I(inode)->
++ u.s1.fragment_start_block,
++ (int) SQUASHFS_I(inode)->
++ u.s1.fragment_size);
++ goto skip_read;
++ }
++ bytes = SQUASHFS_I(inode)->u.s1.fragment_offset +
++ (i_size_read(inode) & (sblk->block_size
++ - 1));
++ byte_offset = SQUASHFS_I(inode)->u.s1.fragment_offset;
++ data_ptr = fragment->data;
++ }
++
++ for (i = start_index; i <= end_index && byte_offset < bytes;
++ i++, byte_offset += PAGE_CACHE_SIZE) {
++ struct page *push_page;
++ int avail = (bytes - byte_offset) > PAGE_CACHE_SIZE ?
++ PAGE_CACHE_SIZE : bytes - byte_offset;
++
++ TRACE("bytes %d, i %d, byte_offset %d, available_bytes %d\n",
++ bytes, i, byte_offset, avail);
++
++ push_page = (i == page->index) ? page :
++ grab_cache_page_nowait(page->mapping, i);
++
++ if (!push_page)
++ continue;
++
++ if (PageUptodate(push_page))
++ goto skip_page;
++
++ pageaddr = kmap_atomic(push_page, KM_USER0);
++ memcpy(pageaddr, data_ptr + byte_offset, avail);
++ memset(pageaddr + avail, 0, PAGE_CACHE_SIZE - avail);
++ kunmap_atomic(pageaddr, KM_USER0);
++ flush_dcache_page(push_page);
++ SetPageUptodate(push_page);
++skip_page:
++ unlock_page(push_page);
++ if(i != page->index)
++ page_cache_release(push_page);
++ }
++
++ if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK
++ || index < (i_size_read(inode) >>
++ sblk->block_log))
++ mutex_unlock(&msblk->read_page_mutex);
++ else
++ release_cached_fragment(msblk, fragment);
++
++ kfree(block_list);
++ return 0;
++
++skip_read:
++ pageaddr = kmap_atomic(page, KM_USER0);
++ memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
++ kunmap_atomic(pageaddr, KM_USER0);
++ flush_dcache_page(page);
++ SetPageUptodate(page);
++ unlock_page(page);
++
++ kfree(block_list);
++ return 0;
++}
++
++
++static int squashfs_readpage4K(struct file *file, struct page *page)
++{
++ struct inode *inode = page->mapping->host;
++ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++ unsigned char *block_list;
++ long long block;
++ unsigned int bsize, bytes = 0;
++ void *pageaddr;
++
++ TRACE("Entered squashfs_readpage4K, page index %lx, start block %llx\n",
++ page->index,
++ SQUASHFS_I(inode)->start_block);
++
++ if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
++ PAGE_CACHE_SHIFT)) {
++ block_list = NULL;
++ goto skip_read;
++ }
++
++ if (!(block_list = kmalloc(SIZE, GFP_KERNEL))) {
++ ERROR("Failed to allocate block_list\n");
++ goto skip_read;
++ }
++
++ if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK
++ || page->index < (i_size_read(inode) >>
++ sblk->block_log)) {
++ block = (msblk->read_blocklist)(inode, page->index, 1,
++ block_list, NULL, &bsize);
++ if(block == 0)
++ goto skip_read;
++
++ mutex_lock(&msblk->read_page_mutex);
++ bytes = squashfs_read_data(inode->i_sb, msblk->read_page, block,
++ bsize, NULL, sblk->block_size);
++ if (bytes) {
++ pageaddr = kmap_atomic(page, KM_USER0);
++ memcpy(pageaddr, msblk->read_page, bytes);
++ kunmap_atomic(pageaddr, KM_USER0);
++ } else
++ ERROR("Unable to read page, block %llx, size %x\n",
++ block, bsize);
++ mutex_unlock(&msblk->read_page_mutex);
++ } else {
++ struct squashfs_fragment_cache *fragment =
++ get_cached_fragment(inode->i_sb,
++ SQUASHFS_I(inode)->
++ u.s1.fragment_start_block,
++ SQUASHFS_I(inode)-> u.s1.fragment_size);
++ if (fragment) {
++ bytes = i_size_read(inode) & (sblk->block_size - 1);
++ pageaddr = kmap_atomic(page, KM_USER0);
++ memcpy(pageaddr, fragment->data + SQUASHFS_I(inode)->
++ u.s1.fragment_offset, bytes);
++ kunmap_atomic(pageaddr, KM_USER0);
++ release_cached_fragment(msblk, fragment);
++ } else
++ ERROR("Unable to read page, block %llx, size %x\n",
++ SQUASHFS_I(inode)->
++ u.s1.fragment_start_block, (int)
++ SQUASHFS_I(inode)-> u.s1.fragment_size);
++ }
++
++skip_read:
++ pageaddr = kmap_atomic(page, KM_USER0);
++ memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
++ kunmap_atomic(pageaddr, KM_USER0);
++ flush_dcache_page(page);
++ SetPageUptodate(page);
++ unlock_page(page);
++
++ kfree(block_list);
++ return 0;
++}
++
++
++static int get_dir_index_using_offset(struct super_block *s, long long
++ *next_block, unsigned int *next_offset,
++ long long index_start,
++ unsigned int index_offset, int i_count,
++ long long f_pos)
++{
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++ int i, length = 0;
++ struct squashfs_dir_index index;
++
++ TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n",
++ i_count, (unsigned int) f_pos);
++
++ f_pos =- 3;
++ if (f_pos == 0)
++ goto finish;
++
++ for (i = 0; i < i_count; i++) {
++ if (msblk->swap) {
++ struct squashfs_dir_index sindex;
++ squashfs_get_cached_block(s, (char *) &sindex,
++ index_start, index_offset,
++ sizeof(sindex), &index_start,
++ &index_offset);
++ SQUASHFS_SWAP_DIR_INDEX(&index, &sindex);
++ } else
++ squashfs_get_cached_block(s, (char *) &index,
++ index_start, index_offset,
++ sizeof(index), &index_start,
++ &index_offset);
++
++ if (index.index > f_pos)
++ break;
++
++ squashfs_get_cached_block(s, NULL, index_start, index_offset,
++ index.size + 1, &index_start,
++ &index_offset);
++
++ length = index.index;
++ *next_block = index.start_block + sblk->directory_table_start;
++ }
++
++ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
++
++finish:
++ return length + 3;
++}
++
++
++static int get_dir_index_using_name(struct super_block *s, long long
++ *next_block, unsigned int *next_offset,
++ long long index_start,
++ unsigned int index_offset, int i_count,
++ const char *name, int size)
++{
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++ int i, length = 0;
++ struct squashfs_dir_index *index;
++ char *str;
++
++ TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count);
++
++ if (!(str = kmalloc(sizeof(struct squashfs_dir_index) +
++ (SQUASHFS_NAME_LEN + 1) * 2, GFP_KERNEL))) {
++ ERROR("Failed to allocate squashfs_dir_index\n");
++ goto failure;
++ }
++
++ index = (struct squashfs_dir_index *) (str + SQUASHFS_NAME_LEN + 1);
++ strncpy(str, name, size);
++ str[size] = '\0';
++
++ for (i = 0; i < i_count; i++) {
++ if (msblk->swap) {
++ struct squashfs_dir_index sindex;
++ squashfs_get_cached_block(s, (char *) &sindex,
++ index_start, index_offset,
++ sizeof(sindex), &index_start,
++ &index_offset);
++ SQUASHFS_SWAP_DIR_INDEX(index, &sindex);
++ } else
++ squashfs_get_cached_block(s, (char *) index,
++ index_start, index_offset,
++ sizeof(struct squashfs_dir_index),
++ &index_start, &index_offset);
++
++ squashfs_get_cached_block(s, index->name, index_start,
++ index_offset, index->size + 1,
++ &index_start, &index_offset);
++
++ index->name[index->size + 1] = '\0';
++
++ if (strcmp(index->name, str) > 0)
++ break;
++
++ length = index->index;
++ *next_block = index->start_block + sblk->directory_table_start;
++ }
++
++ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
++ kfree(str);
++failure:
++ return length + 3;
++}
++
++
++static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir)
++{
++ struct inode *i = file->f_dentry->d_inode;
++ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++ long long next_block = SQUASHFS_I(i)->start_block +
++ sblk->directory_table_start;
++ int next_offset = SQUASHFS_I(i)->offset, length = 0,
++ dir_count;
++ struct squashfs_dir_header dirh;
++ struct squashfs_dir_entry *dire;
++
++ TRACE("Entered squashfs_readdir [%llx:%x]\n", next_block, next_offset);
++
++ if (!(dire = kmalloc(sizeof(struct squashfs_dir_entry) +
++ SQUASHFS_NAME_LEN + 1, GFP_KERNEL))) {
++ ERROR("Failed to allocate squashfs_dir_entry\n");
++ goto finish;
++ }
++
++ while(file->f_pos < 3) {
++ char *name;
++ int size, i_ino;
++
++ if(file->f_pos == 0) {
++ name = ".";
++ size = 1;
++ i_ino = i->i_ino;
++ } else {
++ name = "..";
++ size = 2;
++ i_ino = SQUASHFS_I(i)->u.s2.parent_inode;
++ }
++ TRACE("Calling filldir(%x, %s, %d, %d, %d, %d)\n",
++ (unsigned int) dirent, name, size, (int)
++ file->f_pos, i_ino,
++ squashfs_filetype_table[1]);
++
++ if (filldir(dirent, name, size,
++ file->f_pos, i_ino,
++ squashfs_filetype_table[1]) < 0) {
++ TRACE("Filldir returned less than 0\n");
++ goto finish;
++ }
++ file->f_pos += size;
++ }
++
++ length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset,
++ SQUASHFS_I(i)->u.s2.directory_index_start,
++ SQUASHFS_I(i)->u.s2.directory_index_offset,
++ SQUASHFS_I(i)->u.s2.directory_index_count,
++ file->f_pos);
++
++ while (length < i_size_read(i)) {
++ /* read directory header */
++ if (msblk->swap) {
++ struct squashfs_dir_header sdirh;
++
++ if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh,
++ next_block, next_offset, sizeof(sdirh),
++ &next_block, &next_offset))
++ goto failed_read;
++
++ length += sizeof(sdirh);
++ SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh);
++ } else {
++ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
++ next_block, next_offset, sizeof(dirh),
++ &next_block, &next_offset))
++ goto failed_read;
++
++ length += sizeof(dirh);
++ }
++
++ dir_count = dirh.count + 1;
++ while (dir_count--) {
++ if (msblk->swap) {
++ struct squashfs_dir_entry sdire;
++ if (!squashfs_get_cached_block(i->i_sb, (char *)
++ &sdire, next_block, next_offset,
++ sizeof(sdire), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ length += sizeof(sdire);
++ SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire);
++ } else {
++ if (!squashfs_get_cached_block(i->i_sb, (char *)
++ dire, next_block, next_offset,
++ sizeof(*dire), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ length += sizeof(*dire);
++ }
++
++ if (!squashfs_get_cached_block(i->i_sb, dire->name,
++ next_block, next_offset,
++ dire->size + 1, &next_block,
++ &next_offset))
++ goto failed_read;
++
++ length += dire->size + 1;
++
++ if (file->f_pos >= length)
++ continue;
++
++ dire->name[dire->size + 1] = '\0';
++
++ TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d, %d)\n",
++ (unsigned int) dirent, dire->name,
++ dire->size + 1, (int) file->f_pos,
++ dirh.start_block, dire->offset,
++ dirh.inode_number + dire->inode_number,
++ squashfs_filetype_table[dire->type]);
++
++ if (filldir(dirent, dire->name, dire->size + 1,
++ file->f_pos,
++ dirh.inode_number + dire->inode_number,
++ squashfs_filetype_table[dire->type])
++ < 0) {
++ TRACE("Filldir returned less than 0\n");
++ goto finish;
++ }
++ file->f_pos = length;
++ }
++ }
++
++finish:
++ kfree(dire);
++ return 0;
++
++failed_read:
++ ERROR("Unable to read directory block [%llx:%x]\n", next_block,
++ next_offset);
++ kfree(dire);
++ return 0;
++}
++
++
++static struct dentry *squashfs_lookup(struct inode *i, struct dentry *dentry,
++ struct nameidata *nd)
++{
++ const unsigned char *name = dentry->d_name.name;
++ int len = dentry->d_name.len;
++ struct inode *inode = NULL;
++ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++ long long next_block = SQUASHFS_I(i)->start_block +
++ sblk->directory_table_start;
++ int next_offset = SQUASHFS_I(i)->offset, length = 0,
++ dir_count;
++ struct squashfs_dir_header dirh;
++ struct squashfs_dir_entry *dire;
++
++ TRACE("Entered squashfs_lookup [%llx:%x]\n", next_block, next_offset);
++
++ if (!(dire = kmalloc(sizeof(struct squashfs_dir_entry) +
++ SQUASHFS_NAME_LEN + 1, GFP_KERNEL))) {
++ ERROR("Failed to allocate squashfs_dir_entry\n");
++ goto exit_lookup;
++ }
++
++ if (len > SQUASHFS_NAME_LEN)
++ goto exit_lookup;
++
++ length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset,
++ SQUASHFS_I(i)->u.s2.directory_index_start,
++ SQUASHFS_I(i)->u.s2.directory_index_offset,
++ SQUASHFS_I(i)->u.s2.directory_index_count, name,
++ len);
++
++ while (length < i_size_read(i)) {
++ /* read directory header */
++ if (msblk->swap) {
++ struct squashfs_dir_header sdirh;
++ if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh,
++ next_block, next_offset, sizeof(sdirh),
++ &next_block, &next_offset))
++ goto failed_read;
++
++ length += sizeof(sdirh);
++ SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh);
++ } else {
++ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
++ next_block, next_offset, sizeof(dirh),
++ &next_block, &next_offset))
++ goto failed_read;
++
++ length += sizeof(dirh);
++ }
++
++ dir_count = dirh.count + 1;
++ while (dir_count--) {
++ if (msblk->swap) {
++ struct squashfs_dir_entry sdire;
++ if (!squashfs_get_cached_block(i->i_sb, (char *)
++ &sdire, next_block,next_offset,
++ sizeof(sdire), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ length += sizeof(sdire);
++ SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire);
++ } else {
++ if (!squashfs_get_cached_block(i->i_sb, (char *)
++ dire, next_block,next_offset,
++ sizeof(*dire), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ length += sizeof(*dire);
++ }
++
++ if (!squashfs_get_cached_block(i->i_sb, dire->name,
++ next_block, next_offset, dire->size + 1,
++ &next_block, &next_offset))
++ goto failed_read;
++
++ length += dire->size + 1;
++
++ if (name[0] < dire->name[0])
++ goto exit_lookup;
++
++ if ((len == dire->size + 1) && !strncmp(name, dire->name, len)) {
++ squashfs_inode_t ino = SQUASHFS_MKINODE(dirh.start_block,
++ dire->offset);
++
++ TRACE("calling squashfs_iget for directory "
++ "entry %s, inode %x:%x, %d\n", name,
++ dirh.start_block, dire->offset,
++ dirh.inode_number + dire->inode_number);
++
++ inode = squashfs_iget(i->i_sb, ino, dirh.inode_number + dire->inode_number);
++
++ goto exit_lookup;
++ }
++ }
++ }
++
++exit_lookup:
++ kfree(dire);
++ if (inode)
++ return d_splice_alias(inode, dentry);
++ d_add(dentry, inode);
++ return ERR_PTR(0);
++
++failed_read:
++ ERROR("Unable to read directory block [%llx:%x]\n", next_block,
++ next_offset);
++ goto exit_lookup;
++}
++
++
++static int squashfs_remount(struct super_block *s, int *flags, char *data)
++{
++ *flags |= MS_RDONLY;
++ return 0;
++}
++
++
++static void squashfs_put_super(struct super_block *s)
++{
++ int i;
++
++ if (s->s_fs_info) {
++ struct squashfs_sb_info *sbi = s->s_fs_info;
++ if (sbi->block_cache)
++ for (i = 0; i < SQUASHFS_CACHED_BLKS; i++)
++ if (sbi->block_cache[i].block !=
++ SQUASHFS_INVALID_BLK)
++ kfree(sbi->block_cache[i].data);
++ if (sbi->fragment)
++ for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++)
++ SQUASHFS_FREE(sbi->fragment[i].data);
++ kfree(sbi->fragment);
++ kfree(sbi->block_cache);
++ kfree(sbi->read_page);
++ kfree(sbi->uid);
++ kfree(sbi->fragment_index);
++ kfree(sbi->fragment_index_2);
++ kfree(sbi->meta_index);
++ vfree(sbi->stream.workspace);
++ kfree(s->s_fs_info);
++ s->s_fs_info = NULL;
++ }
++}
++
++
++static int squashfs_get_sb(struct file_system_type *fs_type, int flags,
++ const char *dev_name, void *data,
++ struct vfsmount *mnt)
++{
++ return get_sb_bdev(fs_type, flags, dev_name, data, squashfs_fill_super,
++ mnt);
++}
++
++
++static int __init init_squashfs_fs(void)
++{
++ int err = init_inodecache();
++ if (err)
++ goto out;
++
++ printk(KERN_INFO "squashfs: version 3.2-r2 (2007/01/15) "
++ "Phillip Lougher\n");
++
++ if ((err = register_filesystem(&squashfs_fs_type)))
++ destroy_inodecache();
++
++out:
++ return err;
++}
++
++
++static void __exit exit_squashfs_fs(void)
++{
++ unregister_filesystem(&squashfs_fs_type);
++ destroy_inodecache();
++}
++
++
++static struct kmem_cache * squashfs_inode_cachep;
++
++
++static struct inode *squashfs_alloc_inode(struct super_block *sb)
++{
++ struct squashfs_inode_info *ei;
++ ei = kmem_cache_alloc(squashfs_inode_cachep, GFP_KERNEL);
++ if (!ei)
++ return NULL;
++ return &ei->vfs_inode;
++}
++
++
++static void squashfs_destroy_inode(struct inode *inode)
++{
++ kmem_cache_free(squashfs_inode_cachep, SQUASHFS_I(inode));
++}
++
++
++static void init_once(void * foo, struct kmem_cache * cachep, unsigned long flags)
++{
++ struct squashfs_inode_info *ei = foo;
++
++ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
++ SLAB_CTOR_CONSTRUCTOR)
++ inode_init_once(&ei->vfs_inode);
++}
++
++
++static int __init init_inodecache(void)
++{
++ squashfs_inode_cachep = kmem_cache_create("squashfs_inode_cache",
++ sizeof(struct squashfs_inode_info),
++ 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT,
++ init_once, NULL);
++ if (squashfs_inode_cachep == NULL)
++ return -ENOMEM;
++ return 0;
++}
++
++
++static void destroy_inodecache(void)
++{
++ kmem_cache_destroy(squashfs_inode_cachep);
++}
++
++
++module_init(init_squashfs_fs);
++module_exit(exit_squashfs_fs);
++MODULE_DESCRIPTION("squashfs 3.2-r2, a compressed read-only filesystem");
++MODULE_AUTHOR("Phillip Lougher <phillip@lougher.org.uk>");
++MODULE_LICENSE("GPL");
+diff -rduNp linux-2.6.22.1.oorig/fs/squashfs/squashfs.h linux-2.6.22.1/fs/squashfs/squashfs.h
+--- linux-2.6.22.1.oorig/fs/squashfs/squashfs.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/squashfs/squashfs.h 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,87 @@
++/*
++ * Squashfs - a compressed read only filesystem for Linux
++ *
++ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
++ * Phillip Lougher <phillip@lougher.org.uk>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2,
++ * 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ * squashfs.h
++ */
++
++#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY
++#undef CONFIG_SQUASHFS_1_0_COMPATIBILITY
++#endif
++
++#ifdef SQUASHFS_TRACE
++#define TRACE(s, args...) printk(KERN_NOTICE "SQUASHFS: "s, ## args)
++#else
++#define TRACE(s, args...) {}
++#endif
++
++#define ERROR(s, args...) printk(KERN_ERR "SQUASHFS error: "s, ## args)
++
++#define SERROR(s, args...) do { \
++ if (!silent) \
++ printk(KERN_ERR "SQUASHFS error: "s, ## args);\
++ } while(0)
++
++#define WARNING(s, args...) printk(KERN_WARNING "SQUASHFS: "s, ## args)
++
++static inline struct squashfs_inode_info *SQUASHFS_I(struct inode *inode)
++{
++ return list_entry(inode, struct squashfs_inode_info, vfs_inode);
++}
++
++#if defined(CONFIG_SQUASHFS_1_0_COMPATIBILITY ) || defined(CONFIG_SQUASHFS_2_0_COMPATIBILITY)
++#define SQSH_EXTERN
++extern unsigned int squashfs_read_data(struct super_block *s, char *buffer,
++ long long index, unsigned int length,
++ long long *next_index, int srclength);
++extern int squashfs_get_cached_block(struct super_block *s, char *buffer,
++ long long block, unsigned int offset,
++ int length, long long *next_block,
++ unsigned int *next_offset);
++extern void release_cached_fragment(struct squashfs_sb_info *msblk, struct
++ squashfs_fragment_cache *fragment);
++extern struct squashfs_fragment_cache *get_cached_fragment(struct super_block
++ *s, long long start_block,
++ int length);
++extern struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode, unsigned int inode_number);
++extern const struct address_space_operations squashfs_symlink_aops;
++extern const struct address_space_operations squashfs_aops;
++extern const struct address_space_operations squashfs_aops_4K;
++extern struct inode_operations squashfs_dir_inode_ops;
++#else
++#define SQSH_EXTERN static
++#endif
++
++#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY
++extern int squashfs_1_0_supported(struct squashfs_sb_info *msblk);
++#else
++static inline int squashfs_1_0_supported(struct squashfs_sb_info *msblk)
++{
++ return 0;
++}
++#endif
++
++#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY
++extern int squashfs_2_0_supported(struct squashfs_sb_info *msblk);
++#else
++static inline int squashfs_2_0_supported(struct squashfs_sb_info *msblk)
++{
++ return 0;
++}
++#endif
+diff -rduNp linux-2.6.22.1.oorig/fs/squashfs/squashfs2_0.c linux-2.6.22.1/fs/squashfs/squashfs2_0.c
+--- linux-2.6.22.1.oorig/fs/squashfs/squashfs2_0.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/fs/squashfs/squashfs2_0.c 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,742 @@
++/*
++ * Squashfs - a compressed read only filesystem for Linux
++ *
++ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
++ * Phillip Lougher <phillip@lougher.org.uk>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2,
++ * 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ * squashfs2_0.c
++ */
++
++#include <linux/squashfs_fs.h>
++#include <linux/module.h>
++#include <linux/zlib.h>
++#include <linux/fs.h>
++#include <linux/squashfs_fs_sb.h>
++#include <linux/squashfs_fs_i.h>
++
++#include "squashfs.h"
++static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir);
++static struct dentry *squashfs_lookup_2(struct inode *, struct dentry *,
++ struct nameidata *);
++
++static struct file_operations squashfs_dir_ops_2 = {
++ .read = generic_read_dir,
++ .readdir = squashfs_readdir_2
++};
++
++static struct inode_operations squashfs_dir_inode_ops_2 = {
++ .lookup = squashfs_lookup_2
++};
++
++static unsigned char squashfs_filetype_table[] = {
++ DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK
++};
++
++static int read_fragment_index_table_2(struct super_block *s)
++{
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++
++ if (!(msblk->fragment_index_2 = kmalloc(SQUASHFS_FRAGMENT_INDEX_BYTES_2
++ (sblk->fragments), GFP_KERNEL))) {
++ ERROR("Failed to allocate uid/gid table\n");
++ return 0;
++ }
++
++ if (SQUASHFS_FRAGMENT_INDEX_BYTES_2(sblk->fragments) &&
++ !squashfs_read_data(s, (char *)
++ msblk->fragment_index_2,
++ sblk->fragment_table_start,
++ SQUASHFS_FRAGMENT_INDEX_BYTES_2
++ (sblk->fragments) |
++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, SQUASHFS_FRAGMENT_INDEX_BYTES_2(sblk->fragments))) {
++ ERROR("unable to read fragment index table\n");
++ return 0;
++ }
++
++ if (msblk->swap) {
++ int i;
++ unsigned int fragment;
++
++ for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES_2(sblk->fragments);
++ i++) {
++ SQUASHFS_SWAP_FRAGMENT_INDEXES_2((&fragment),
++ &msblk->fragment_index_2[i], 1);
++ msblk->fragment_index_2[i] = fragment;
++ }
++ }
++
++ return 1;
++}
++
++
++static int get_fragment_location_2(struct super_block *s, unsigned int fragment,
++ long long *fragment_start_block,
++ unsigned int *fragment_size)
++{
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ long long start_block =
++ msblk->fragment_index_2[SQUASHFS_FRAGMENT_INDEX_2(fragment)];
++ int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET_2(fragment);
++ struct squashfs_fragment_entry_2 fragment_entry;
++
++ if (msblk->swap) {
++ struct squashfs_fragment_entry_2 sfragment_entry;
++
++ if (!squashfs_get_cached_block(s, (char *) &sfragment_entry,
++ start_block, offset,
++ sizeof(sfragment_entry), &start_block,
++ &offset))
++ goto out;
++ SQUASHFS_SWAP_FRAGMENT_ENTRY_2(&fragment_entry, &sfragment_entry);
++ } else
++ if (!squashfs_get_cached_block(s, (char *) &fragment_entry,
++ start_block, offset,
++ sizeof(fragment_entry), &start_block,
++ &offset))
++ goto out;
++
++ *fragment_start_block = fragment_entry.start_block;
++ *fragment_size = fragment_entry.size;
++
++ return 1;
++
++out:
++ return 0;
++}
++
++
++static void squashfs_new_inode(struct squashfs_sb_info *msblk, struct inode *i,
++ struct squashfs_base_inode_header_2 *inodeb, unsigned int ino)
++{
++ struct squashfs_super_block *sblk = &msblk->sblk;
++
++ i->i_ino = ino;
++ i->i_mtime.tv_sec = sblk->mkfs_time;
++ i->i_atime.tv_sec = sblk->mkfs_time;
++ i->i_ctime.tv_sec = sblk->mkfs_time;
++ i->i_uid = msblk->uid[inodeb->uid];
++ i->i_mode = inodeb->mode;
++ i->i_nlink = 1;
++ i->i_size = 0;
++ if (inodeb->guid == SQUASHFS_GUIDS)
++ i->i_gid = i->i_uid;
++ else
++ i->i_gid = msblk->guid[inodeb->guid];
++}
++
++
++static int squashfs_read_inode_2(struct inode *i, squashfs_inode_t inode)
++{
++ struct super_block *s = i->i_sb;
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++ unsigned int block = SQUASHFS_INODE_BLK(inode) +
++ sblk->inode_table_start;
++ unsigned int offset = SQUASHFS_INODE_OFFSET(inode);
++ unsigned int ino = i->i_ino;
++ long long next_block;
++ unsigned int next_offset;
++ union squashfs_inode_header_2 id, sid;
++ struct squashfs_base_inode_header_2 *inodeb = &id.base,
++ *sinodeb = &sid.base;
++
++ TRACE("Entered squashfs_iget\n");
++
++ if (msblk->swap) {
++ if (!squashfs_get_cached_block(s, (char *) sinodeb, block,
++ offset, sizeof(*sinodeb), &next_block,
++ &next_offset))
++ goto failed_read;
++ SQUASHFS_SWAP_BASE_INODE_HEADER_2(inodeb, sinodeb,
++ sizeof(*sinodeb));
++ } else
++ if (!squashfs_get_cached_block(s, (char *) inodeb, block,
++ offset, sizeof(*inodeb), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ squashfs_new_inode(msblk, i, inodeb, ino);
++
++ switch(inodeb->inode_type) {
++ case SQUASHFS_FILE_TYPE: {
++ struct squashfs_reg_inode_header_2 *inodep = &id.reg;
++ struct squashfs_reg_inode_header_2 *sinodep = &sid.reg;
++ long long frag_blk;
++ unsigned int frag_size = 0;
++
++ if (msblk->swap) {
++ if (!squashfs_get_cached_block(s, (char *)
++ sinodep, block, offset,
++ sizeof(*sinodep), &next_block,
++ &next_offset))
++ goto failed_read;
++ SQUASHFS_SWAP_REG_INODE_HEADER_2(inodep, sinodep);
++ } else
++ if (!squashfs_get_cached_block(s, (char *)
++ inodep, block, offset,
++ sizeof(*inodep), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ frag_blk = SQUASHFS_INVALID_BLK;
++ if (inodep->fragment != SQUASHFS_INVALID_FRAG &&
++ !get_fragment_location_2(s,
++ inodep->fragment, &frag_blk, &frag_size))
++ goto failed_read;
++
++ i->i_size = inodep->file_size;
++ i->i_fop = &generic_ro_fops;
++ i->i_mode |= S_IFREG;
++ i->i_mtime.tv_sec = inodep->mtime;
++ i->i_atime.tv_sec = inodep->mtime;
++ i->i_ctime.tv_sec = inodep->mtime;
++ i->i_blocks = ((i->i_size - 1) >> 9) + 1;
++ SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk;
++ SQUASHFS_I(i)->u.s1.fragment_size = frag_size;
++ SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset;
++ SQUASHFS_I(i)->start_block = inodep->start_block;
++ SQUASHFS_I(i)->u.s1.block_list_start = next_block;
++ SQUASHFS_I(i)->offset = next_offset;
++ if (sblk->block_size > 4096)
++ i->i_data.a_ops = &squashfs_aops;
++ else
++ i->i_data.a_ops = &squashfs_aops_4K;
++
++ TRACE("File inode %x:%x, start_block %x, "
++ "block_list_start %llx, offset %x\n",
++ SQUASHFS_INODE_BLK(inode), offset,
++ inodep->start_block, next_block,
++ next_offset);
++ break;
++ }
++ case SQUASHFS_DIR_TYPE: {
++ struct squashfs_dir_inode_header_2 *inodep = &id.dir;
++ struct squashfs_dir_inode_header_2 *sinodep = &sid.dir;
++
++ if (msblk->swap) {
++ if (!squashfs_get_cached_block(s, (char *)
++ sinodep, block, offset,
++ sizeof(*sinodep), &next_block,
++ &next_offset))
++ goto failed_read;
++ SQUASHFS_SWAP_DIR_INODE_HEADER_2(inodep, sinodep);
++ } else
++ if (!squashfs_get_cached_block(s, (char *)
++ inodep, block, offset,
++ sizeof(*inodep), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ i->i_size = inodep->file_size;
++ i->i_op = &squashfs_dir_inode_ops_2;
++ i->i_fop = &squashfs_dir_ops_2;
++ i->i_mode |= S_IFDIR;
++ i->i_mtime.tv_sec = inodep->mtime;
++ i->i_atime.tv_sec = inodep->mtime;
++ i->i_ctime.tv_sec = inodep->mtime;
++ SQUASHFS_I(i)->start_block = inodep->start_block;
++ SQUASHFS_I(i)->offset = inodep->offset;
++ SQUASHFS_I(i)->u.s2.directory_index_count = 0;
++ SQUASHFS_I(i)->u.s2.parent_inode = 0;
++
++ TRACE("Directory inode %x:%x, start_block %x, offset "
++ "%x\n", SQUASHFS_INODE_BLK(inode),
++ offset, inodep->start_block,
++ inodep->offset);
++ break;
++ }
++ case SQUASHFS_LDIR_TYPE: {
++ struct squashfs_ldir_inode_header_2 *inodep = &id.ldir;
++ struct squashfs_ldir_inode_header_2 *sinodep = &sid.ldir;
++
++ if (msblk->swap) {
++ if (!squashfs_get_cached_block(s, (char *)
++ sinodep, block, offset,
++ sizeof(*sinodep), &next_block,
++ &next_offset))
++ goto failed_read;
++ SQUASHFS_SWAP_LDIR_INODE_HEADER_2(inodep,
++ sinodep);
++ } else
++ if (!squashfs_get_cached_block(s, (char *)
++ inodep, block, offset,
++ sizeof(*inodep), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ i->i_size = inodep->file_size;
++ i->i_op = &squashfs_dir_inode_ops_2;
++ i->i_fop = &squashfs_dir_ops_2;
++ i->i_mode |= S_IFDIR;
++ i->i_mtime.tv_sec = inodep->mtime;
++ i->i_atime.tv_sec = inodep->mtime;
++ i->i_ctime.tv_sec = inodep->mtime;
++ SQUASHFS_I(i)->start_block = inodep->start_block;
++ SQUASHFS_I(i)->offset = inodep->offset;
++ SQUASHFS_I(i)->u.s2.directory_index_start = next_block;
++ SQUASHFS_I(i)->u.s2.directory_index_offset =
++ next_offset;
++ SQUASHFS_I(i)->u.s2.directory_index_count =
++ inodep->i_count;
++ SQUASHFS_I(i)->u.s2.parent_inode = 0;
++
++ TRACE("Long directory inode %x:%x, start_block %x, "
++ "offset %x\n",
++ SQUASHFS_INODE_BLK(inode), offset,
++ inodep->start_block, inodep->offset);
++ break;
++ }
++ case SQUASHFS_SYMLINK_TYPE: {
++ struct squashfs_symlink_inode_header_2 *inodep =
++ &id.symlink;
++ struct squashfs_symlink_inode_header_2 *sinodep =
++ &sid.symlink;
++
++ if (msblk->swap) {
++ if (!squashfs_get_cached_block(s, (char *)
++ sinodep, block, offset,
++ sizeof(*sinodep), &next_block,
++ &next_offset))
++ goto failed_read;
++ SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(inodep,
++ sinodep);
++ } else
++ if (!squashfs_get_cached_block(s, (char *)
++ inodep, block, offset,
++ sizeof(*inodep), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ i->i_size = inodep->symlink_size;
++ i->i_op = &page_symlink_inode_operations;
++ i->i_data.a_ops = &squashfs_symlink_aops;
++ i->i_mode |= S_IFLNK;
++ SQUASHFS_I(i)->start_block = next_block;
++ SQUASHFS_I(i)->offset = next_offset;
++
++ TRACE("Symbolic link inode %x:%x, start_block %llx, "
++ "offset %x\n",
++ SQUASHFS_INODE_BLK(inode), offset,
++ next_block, next_offset);
++ break;
++ }
++ case SQUASHFS_BLKDEV_TYPE:
++ case SQUASHFS_CHRDEV_TYPE: {
++ struct squashfs_dev_inode_header_2 *inodep = &id.dev;
++ struct squashfs_dev_inode_header_2 *sinodep = &sid.dev;
++
++ if (msblk->swap) {
++ if (!squashfs_get_cached_block(s, (char *)
++ sinodep, block, offset,
++ sizeof(*sinodep), &next_block,
++ &next_offset))
++ goto failed_read;
++ SQUASHFS_SWAP_DEV_INODE_HEADER_2(inodep, sinodep);
++ } else
++ if (!squashfs_get_cached_block(s, (char *)
++ inodep, block, offset,
++ sizeof(*inodep), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ i->i_mode |= (inodeb->inode_type ==
++ SQUASHFS_CHRDEV_TYPE) ? S_IFCHR :
++ S_IFBLK;
++ init_special_inode(i, i->i_mode,
++ old_decode_dev(inodep->rdev));
++
++ TRACE("Device inode %x:%x, rdev %x\n",
++ SQUASHFS_INODE_BLK(inode), offset,
++ inodep->rdev);
++ break;
++ }
++ case SQUASHFS_FIFO_TYPE:
++ case SQUASHFS_SOCKET_TYPE: {
++
++ i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE)
++ ? S_IFIFO : S_IFSOCK;
++ init_special_inode(i, i->i_mode, 0);
++ break;
++ }
++ default:
++ ERROR("Unknown inode type %d in squashfs_iget!\n",
++ inodeb->inode_type);
++ goto failed_read1;
++ }
++
++ return 1;
++
++failed_read:
++ ERROR("Unable to read inode [%x:%x]\n", block, offset);
++
++failed_read1:
++ return 0;
++}
++
++
++static int get_dir_index_using_offset(struct super_block *s, long long
++ *next_block, unsigned int *next_offset,
++ long long index_start,
++ unsigned int index_offset, int i_count,
++ long long f_pos)
++{
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++ int i, length = 0;
++ struct squashfs_dir_index_2 index;
++
++ TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n",
++ i_count, (unsigned int) f_pos);
++
++ if (f_pos == 0)
++ goto finish;
++
++ for (i = 0; i < i_count; i++) {
++ if (msblk->swap) {
++ struct squashfs_dir_index_2 sindex;
++ squashfs_get_cached_block(s, (char *) &sindex,
++ index_start, index_offset,
++ sizeof(sindex), &index_start,
++ &index_offset);
++ SQUASHFS_SWAP_DIR_INDEX_2(&index, &sindex);
++ } else
++ squashfs_get_cached_block(s, (char *) &index,
++ index_start, index_offset,
++ sizeof(index), &index_start,
++ &index_offset);
++
++ if (index.index > f_pos)
++ break;
++
++ squashfs_get_cached_block(s, NULL, index_start, index_offset,
++ index.size + 1, &index_start,
++ &index_offset);
++
++ length = index.index;
++ *next_block = index.start_block + sblk->directory_table_start;
++ }
++
++ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
++
++finish:
++ return length;
++}
++
++
++static int get_dir_index_using_name(struct super_block *s, long long
++ *next_block, unsigned int *next_offset,
++ long long index_start,
++ unsigned int index_offset, int i_count,
++ const char *name, int size)
++{
++ struct squashfs_sb_info *msblk = s->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++ int i, length = 0;
++ struct squashfs_dir_index_2 *index;
++ char *str;
++
++ TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count);
++
++ if (!(str = kmalloc(sizeof(struct squashfs_dir_index) +
++ (SQUASHFS_NAME_LEN + 1) * 2, GFP_KERNEL))) {
++ ERROR("Failed to allocate squashfs_dir_index\n");
++ goto failure;
++ }
++
++ index = (struct squashfs_dir_index_2 *) (str + SQUASHFS_NAME_LEN + 1);
++ strncpy(str, name, size);
++ str[size] = '\0';
++
++ for (i = 0; i < i_count; i++) {
++ if (msblk->swap) {
++ struct squashfs_dir_index_2 sindex;
++ squashfs_get_cached_block(s, (char *) &sindex,
++ index_start, index_offset,
++ sizeof(sindex), &index_start,
++ &index_offset);
++ SQUASHFS_SWAP_DIR_INDEX_2(index, &sindex);
++ } else
++ squashfs_get_cached_block(s, (char *) index,
++ index_start, index_offset,
++ sizeof(struct squashfs_dir_index_2),
++ &index_start, &index_offset);
++
++ squashfs_get_cached_block(s, index->name, index_start,
++ index_offset, index->size + 1,
++ &index_start, &index_offset);
++
++ index->name[index->size + 1] = '\0';
++
++ if (strcmp(index->name, str) > 0)
++ break;
++
++ length = index->index;
++ *next_block = index->start_block + sblk->directory_table_start;
++ }
++
++ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
++ kfree(str);
++failure:
++ return length;
++}
++
++
++static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir)
++{
++ struct inode *i = file->f_dentry->d_inode;
++ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++ long long next_block = SQUASHFS_I(i)->start_block +
++ sblk->directory_table_start;
++ int next_offset = SQUASHFS_I(i)->offset, length = 0,
++ dir_count;
++ struct squashfs_dir_header_2 dirh;
++ struct squashfs_dir_entry_2 *dire;
++
++ TRACE("Entered squashfs_readdir_2 [%llx:%x]\n", next_block, next_offset);
++
++ if (!(dire = kmalloc(sizeof(struct squashfs_dir_entry) +
++ SQUASHFS_NAME_LEN + 1, GFP_KERNEL))) {
++ ERROR("Failed to allocate squashfs_dir_entry\n");
++ goto finish;
++ }
++
++ length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset,
++ SQUASHFS_I(i)->u.s2.directory_index_start,
++ SQUASHFS_I(i)->u.s2.directory_index_offset,
++ SQUASHFS_I(i)->u.s2.directory_index_count,
++ file->f_pos);
++
++ while (length < i_size_read(i)) {
++ /* read directory header */
++ if (msblk->swap) {
++ struct squashfs_dir_header_2 sdirh;
++
++ if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh,
++ next_block, next_offset, sizeof(sdirh),
++ &next_block, &next_offset))
++ goto failed_read;
++
++ length += sizeof(sdirh);
++ SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh);
++ } else {
++ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
++ next_block, next_offset, sizeof(dirh),
++ &next_block, &next_offset))
++ goto failed_read;
++
++ length += sizeof(dirh);
++ }
++
++ dir_count = dirh.count + 1;
++ while (dir_count--) {
++ if (msblk->swap) {
++ struct squashfs_dir_entry_2 sdire;
++ if (!squashfs_get_cached_block(i->i_sb, (char *)
++ &sdire, next_block, next_offset,
++ sizeof(sdire), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ length += sizeof(sdire);
++ SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire);
++ } else {
++ if (!squashfs_get_cached_block(i->i_sb, (char *)
++ dire, next_block, next_offset,
++ sizeof(*dire), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ length += sizeof(*dire);
++ }
++
++ if (!squashfs_get_cached_block(i->i_sb, dire->name,
++ next_block, next_offset,
++ dire->size + 1, &next_block,
++ &next_offset))
++ goto failed_read;
++
++ length += dire->size + 1;
++
++ if (file->f_pos >= length)
++ continue;
++
++ dire->name[dire->size + 1] = '\0';
++
++ TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d)\n",
++ (unsigned int) dirent, dire->name,
++ dire->size + 1, (int) file->f_pos,
++ dirh.start_block, dire->offset,
++ squashfs_filetype_table[dire->type]);
++
++ if (filldir(dirent, dire->name, dire->size + 1,
++ file->f_pos, SQUASHFS_MK_VFS_INODE(
++ dirh.start_block, dire->offset),
++ squashfs_filetype_table[dire->type])
++ < 0) {
++ TRACE("Filldir returned less than 0\n");
++ goto finish;
++ }
++ file->f_pos = length;
++ }
++ }
++
++finish:
++ kfree(dire);
++ return 0;
++
++failed_read:
++ ERROR("Unable to read directory block [%llx:%x]\n", next_block,
++ next_offset);
++ kfree(dire);
++ return 0;
++}
++
++
++static struct dentry *squashfs_lookup_2(struct inode *i, struct dentry *dentry,
++ struct nameidata *nd)
++{
++ const unsigned char *name = dentry->d_name.name;
++ int len = dentry->d_name.len;
++ struct inode *inode = NULL;
++ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info;
++ struct squashfs_super_block *sblk = &msblk->sblk;
++ long long next_block = SQUASHFS_I(i)->start_block +
++ sblk->directory_table_start;
++ int next_offset = SQUASHFS_I(i)->offset, length = 0,
++ dir_count;
++ struct squashfs_dir_header_2 dirh;
++ struct squashfs_dir_entry_2 *dire;
++ int sorted = sblk->s_major == 2 && sblk->s_minor >= 1;
++
++ TRACE("Entered squashfs_lookup_2 [%llx:%x]\n", next_block, next_offset);
++
++ if (!(dire = kmalloc(sizeof(struct squashfs_dir_entry) +
++ SQUASHFS_NAME_LEN + 1, GFP_KERNEL))) {
++ ERROR("Failed to allocate squashfs_dir_entry\n");
++ goto exit_loop;
++ }
++
++ if (len > SQUASHFS_NAME_LEN)
++ goto exit_loop;
++
++ length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset,
++ SQUASHFS_I(i)->u.s2.directory_index_start,
++ SQUASHFS_I(i)->u.s2.directory_index_offset,
++ SQUASHFS_I(i)->u.s2.directory_index_count, name,
++ len);
++
++ while (length < i_size_read(i)) {
++ /* read directory header */
++ if (msblk->swap) {
++ struct squashfs_dir_header_2 sdirh;
++ if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh,
++ next_block, next_offset, sizeof(sdirh),
++ &next_block, &next_offset))
++ goto failed_read;
++
++ length += sizeof(sdirh);
++ SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh);
++ } else {
++ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
++ next_block, next_offset, sizeof(dirh),
++ &next_block, &next_offset))
++ goto failed_read;
++
++ length += sizeof(dirh);
++ }
++
++ dir_count = dirh.count + 1;
++ while (dir_count--) {
++ if (msblk->swap) {
++ struct squashfs_dir_entry_2 sdire;
++ if (!squashfs_get_cached_block(i->i_sb, (char *)
++ &sdire, next_block,next_offset,
++ sizeof(sdire), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ length += sizeof(sdire);
++ SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire);
++ } else {
++ if (!squashfs_get_cached_block(i->i_sb, (char *)
++ dire, next_block,next_offset,
++ sizeof(*dire), &next_block,
++ &next_offset))
++ goto failed_read;
++
++ length += sizeof(*dire);
++ }
++
++ if (!squashfs_get_cached_block(i->i_sb, dire->name,
++ next_block, next_offset, dire->size + 1,
++ &next_block, &next_offset))
++ goto failed_read;
++
++ length += dire->size + 1;
++
++ if (sorted && name[0] < dire->name[0])
++ goto exit_loop;
++
++ if ((len == dire->size + 1) && !strncmp(name,
++ dire->name, len)) {
++ squashfs_inode_t ino =
++ SQUASHFS_MKINODE(dirh.start_block,
++ dire->offset);
++ unsigned int inode_number = SQUASHFS_MK_VFS_INODE(dirh.start_block,
++ dire->offset);
++
++ TRACE("calling squashfs_iget for directory "
++ "entry %s, inode %x:%x, %lld\n", name,
++ dirh.start_block, dire->offset, ino);
++
++ inode = squashfs_iget(i->i_sb, ino, inode_number);
++
++ goto exit_loop;
++ }
++ }
++ }
++
++exit_loop:
++ kfree(dire);
++ d_add(dentry, inode);
++ return ERR_PTR(0);
++
++failed_read:
++ ERROR("Unable to read directory block [%llx:%x]\n", next_block,
++ next_offset);
++ goto exit_loop;
++}
++
++
++int squashfs_2_0_supported(struct squashfs_sb_info *msblk)
++{
++ struct squashfs_super_block *sblk = &msblk->sblk;
++
++ msblk->read_inode = squashfs_read_inode_2;
++ msblk->read_fragment_index_table = read_fragment_index_table_2;
++
++ sblk->bytes_used = sblk->bytes_used_2;
++ sblk->uid_start = sblk->uid_start_2;
++ sblk->guid_start = sblk->guid_start_2;
++ sblk->inode_table_start = sblk->inode_table_start_2;
++ sblk->directory_table_start = sblk->directory_table_start_2;
++ sblk->fragment_table_start = sblk->fragment_table_start_2;
++
++ return 1;
++}
+diff -rduNp linux-2.6.22.1.oorig/include/linux/aufs_type.h linux-2.6.22.1/include/linux/aufs_type.h
+--- linux-2.6.22.1.oorig/include/linux/aufs_type.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/include/linux/aufs_type.h 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,97 @@
++/*
++ * 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: aufs_type.h,v 1.55 2007/05/14 03:40:57 sfjro Exp $ */
++
++#include <linux/ioctl.h>
++
++#ifndef __AUFS_TYPE_H__
++#define __AUFS_TYPE_H__
++
++#define AUFS_VERSION "20070514"
++
++/* ---------------------------------------------------------------------- */
++
++#ifdef CONFIG_AUFS_BRANCH_MAX_127
++typedef 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_DIRWH_DEF 3
++#define AUFS_RDCACHE_DEF 10 /* seconds */
++#define AUFS_WKQ_NAME AUFS_NAME "d"
++#define AUFS_NWKQ_DEF 4
++
++#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
++};
++
++struct aufs_ctl_cp {
++ int bsrc, bdst;
++ int err;
++};
++
++#define Type 'A'
++#define AUFS_CTL_REFRESH _IO(Type, AuCtl_REFRESH)
++//#define AUFS_CTL_REFRESHV _IO(Type, AuCtl_REFRESHV)
++//#define AUFS_CTL_FLUSH_PLINK _IOR(Type, AuCtl_FLUSH_PLINK)
++//#define AUFS_CTL_CPUP _IOWR(Type, AuCtl_CPUP, struct aufs_ctl_cp)
++#define AUFS_CTL_CPDOWN _IOWR(Type, AuCtl_CPDOWN, struct aufs_ctl_cp)
++#define AUFS_CTL_MVDOWN _IOWR(Type, AuCtl_MVDOWN, struct aufs_ctl_cp)
++#undef Type
++
++#endif /* __AUFS_TYPE_H__ */
+diff -rduNp linux-2.6.22.1.oorig/include/linux/squashfs_fs.h linux-2.6.22.1/include/linux/squashfs_fs.h
+--- linux-2.6.22.1.oorig/include/linux/squashfs_fs.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/include/linux/squashfs_fs.h 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,934 @@
++#ifndef SQUASHFS_FS
++#define SQUASHFS_FS
++
++/*
++ * Squashfs
++ *
++ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
++ * Phillip Lougher <phillip@lougher.org.uk>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2,
++ * 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ * squashfs_fs.h
++ */
++
++#ifndef CONFIG_SQUASHFS_2_0_COMPATIBILITY
++#define CONFIG_SQUASHFS_2_0_COMPATIBILITY
++#endif
++
++#ifdef CONFIG_SQUASHFS_VMALLOC
++#define SQUASHFS_ALLOC(a) vmalloc(a)
++#define SQUASHFS_FREE(a) vfree(a)
++#else
++#define SQUASHFS_ALLOC(a) kmalloc(a, GFP_KERNEL)
++#define SQUASHFS_FREE(a) kfree(a)
++#endif
++#define SQUASHFS_CACHED_FRAGMENTS CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE
++#define SQUASHFS_MAJOR 3
++#define SQUASHFS_MINOR 0
++#define SQUASHFS_MAGIC 0x73717368
++#define SQUASHFS_MAGIC_SWAP 0x68737173
++#define SQUASHFS_START 0
++
++/* size of metadata (inode and directory) blocks */
++#define SQUASHFS_METADATA_SIZE 8192
++#define SQUASHFS_METADATA_LOG 13
++
++/* default size of data blocks */
++#define SQUASHFS_FILE_SIZE 65536
++#define SQUASHFS_FILE_LOG 16
++
++#define SQUASHFS_FILE_MAX_SIZE 65536
++
++/* Max number of uids and gids */
++#define SQUASHFS_UIDS 256
++#define SQUASHFS_GUIDS 255
++
++/* Max length of filename (not 255) */
++#define SQUASHFS_NAME_LEN 256
++
++#define SQUASHFS_INVALID ((long long) 0xffffffffffff)
++#define SQUASHFS_INVALID_FRAG ((unsigned int) 0xffffffff)
++#define SQUASHFS_INVALID_BLK ((long long) -1)
++#define SQUASHFS_USED_BLK ((long long) -2)
++
++/* Filesystem flags */
++#define SQUASHFS_NOI 0
++#define SQUASHFS_NOD 1
++#define SQUASHFS_CHECK 2
++#define SQUASHFS_NOF 3
++#define SQUASHFS_NO_FRAG 4
++#define SQUASHFS_ALWAYS_FRAG 5
++#define SQUASHFS_DUPLICATE 6
++#define SQUASHFS_EXPORT 7
++
++#define SQUASHFS_BIT(flag, bit) ((flag >> bit) & 1)
++
++#define SQUASHFS_UNCOMPRESSED_INODES(flags) SQUASHFS_BIT(flags, \
++ SQUASHFS_NOI)
++
++#define SQUASHFS_UNCOMPRESSED_DATA(flags) SQUASHFS_BIT(flags, \
++ SQUASHFS_NOD)
++
++#define SQUASHFS_UNCOMPRESSED_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
++ SQUASHFS_NOF)
++
++#define SQUASHFS_NO_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
++ SQUASHFS_NO_FRAG)
++
++#define SQUASHFS_ALWAYS_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
++ SQUASHFS_ALWAYS_FRAG)
++
++#define SQUASHFS_DUPLICATES(flags) SQUASHFS_BIT(flags, \
++ SQUASHFS_DUPLICATE)
++
++#define SQUASHFS_EXPORTABLE(flags) SQUASHFS_BIT(flags, \
++ SQUASHFS_EXPORT)
++
++#define SQUASHFS_CHECK_DATA(flags) SQUASHFS_BIT(flags, \
++ SQUASHFS_CHECK)
++
++#define SQUASHFS_MKFLAGS(noi, nod, check_data, nof, no_frag, always_frag, \
++ duplicate_checking, exortable) (noi | (nod << 1) | (check_data << 2) \
++ | (nof << 3) | (no_frag << 4) | (always_frag << 5) | \
++ (duplicate_checking << 6) | (exportable << 7))
++
++/* Max number of types and file types */
++#define SQUASHFS_DIR_TYPE 1
++#define SQUASHFS_FILE_TYPE 2
++#define SQUASHFS_SYMLINK_TYPE 3
++#define SQUASHFS_BLKDEV_TYPE 4
++#define SQUASHFS_CHRDEV_TYPE 5
++#define SQUASHFS_FIFO_TYPE 6
++#define SQUASHFS_SOCKET_TYPE 7
++#define SQUASHFS_LDIR_TYPE 8
++#define SQUASHFS_LREG_TYPE 9
++
++/* 1.0 filesystem type definitions */
++#define SQUASHFS_TYPES 5
++#define SQUASHFS_IPC_TYPE 0
++
++/* Flag whether block is compressed or uncompressed, bit is set if block is
++ * uncompressed */
++#define SQUASHFS_COMPRESSED_BIT (1 << 15)
++
++#define SQUASHFS_COMPRESSED_SIZE(B) (((B) & ~SQUASHFS_COMPRESSED_BIT) ? \
++ (B) & ~SQUASHFS_COMPRESSED_BIT : SQUASHFS_COMPRESSED_BIT)
++
++#define SQUASHFS_COMPRESSED(B) (!((B) & SQUASHFS_COMPRESSED_BIT))
++
++#define SQUASHFS_COMPRESSED_BIT_BLOCK (1 << 24)
++
++#define SQUASHFS_COMPRESSED_SIZE_BLOCK(B) (((B) & \
++ ~SQUASHFS_COMPRESSED_BIT_BLOCK) ? (B) & \
++ ~SQUASHFS_COMPRESSED_BIT_BLOCK : SQUASHFS_COMPRESSED_BIT_BLOCK)
++
++#define SQUASHFS_COMPRESSED_BLOCK(B) (!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK))
++
++/*
++ * Inode number ops. Inodes consist of a compressed block number, and an
++ * uncompressed offset within that block
++ */
++#define SQUASHFS_INODE_BLK(a) ((unsigned int) ((a) >> 16))
++
++#define SQUASHFS_INODE_OFFSET(a) ((unsigned int) ((a) & 0xffff))
++
++#define SQUASHFS_MKINODE(A, B) ((squashfs_inode_t)(((squashfs_inode_t) (A)\
++ << 16) + (B)))
++
++/* Compute 32 bit VFS inode number from squashfs inode number */
++#define SQUASHFS_MK_VFS_INODE(a, b) ((unsigned int) (((a) << 8) + \
++ ((b) >> 2) + 1))
++/* XXX */
++
++/* Translate between VFS mode and squashfs mode */
++#define SQUASHFS_MODE(a) ((a) & 0xfff)
++
++/* fragment and fragment table defines */
++#define SQUASHFS_FRAGMENT_BYTES(A) ((A) * sizeof(struct squashfs_fragment_entry))
++
++#define SQUASHFS_FRAGMENT_INDEX(A) (SQUASHFS_FRAGMENT_BYTES(A) / \
++ SQUASHFS_METADATA_SIZE)
++
++#define SQUASHFS_FRAGMENT_INDEX_OFFSET(A) (SQUASHFS_FRAGMENT_BYTES(A) % \
++ SQUASHFS_METADATA_SIZE)
++
++#define SQUASHFS_FRAGMENT_INDEXES(A) ((SQUASHFS_FRAGMENT_BYTES(A) + \
++ SQUASHFS_METADATA_SIZE - 1) / \
++ SQUASHFS_METADATA_SIZE)
++
++#define SQUASHFS_FRAGMENT_INDEX_BYTES(A) (SQUASHFS_FRAGMENT_INDEXES(A) *\
++ sizeof(long long))
++
++/* inode lookup table defines */
++#define SQUASHFS_LOOKUP_BYTES(A) ((A) * sizeof(squashfs_inode_t))
++
++#define SQUASHFS_LOOKUP_BLOCK(A) (SQUASHFS_LOOKUP_BYTES(A) / \
++ SQUASHFS_METADATA_SIZE)
++
++#define SQUASHFS_LOOKUP_BLOCK_OFFSET(A) (SQUASHFS_LOOKUP_BYTES(A) % \
++ SQUASHFS_METADATA_SIZE)
++
++#define SQUASHFS_LOOKUP_BLOCKS(A) ((SQUASHFS_LOOKUP_BYTES(A) + \
++ SQUASHFS_METADATA_SIZE - 1) / \
++ SQUASHFS_METADATA_SIZE)
++
++#define SQUASHFS_LOOKUP_BLOCK_BYTES(A) (SQUASHFS_LOOKUP_BLOCKS(A) *\
++ sizeof(long long))
++
++/* cached data constants for filesystem */
++#define SQUASHFS_CACHED_BLKS 8
++
++#define SQUASHFS_MAX_FILE_SIZE_LOG 64
++
++#define SQUASHFS_MAX_FILE_SIZE ((long long) 1 << \
++ (SQUASHFS_MAX_FILE_SIZE_LOG - 2))
++
++#define SQUASHFS_MARKER_BYTE 0xff
++
++/* meta index cache */
++#define SQUASHFS_META_INDEXES (SQUASHFS_METADATA_SIZE / sizeof(unsigned int))
++#define SQUASHFS_META_ENTRIES 31
++#define SQUASHFS_META_NUMBER 8
++#define SQUASHFS_SLOTS 4
++
++struct meta_entry {
++ long long data_block;
++ unsigned int index_block;
++ unsigned short offset;
++ unsigned short pad;
++};
++
++struct meta_index {
++ unsigned int inode_number;
++ unsigned int offset;
++ unsigned short entries;
++ unsigned short skip;
++ unsigned short locked;
++ unsigned short pad;
++ struct meta_entry meta_entry[SQUASHFS_META_ENTRIES];
++};
++
++
++/*
++ * definitions for structures on disk
++ */
++
++typedef long long squashfs_block_t;
++typedef long long squashfs_inode_t;
++
++struct squashfs_super_block {
++ unsigned int s_magic;
++ unsigned int inodes;
++ unsigned int bytes_used_2;
++ unsigned int uid_start_2;
++ unsigned int guid_start_2;
++ unsigned int inode_table_start_2;
++ unsigned int directory_table_start_2;
++ unsigned int s_major:16;
++ unsigned int s_minor:16;
++ unsigned int block_size_1:16;
++ unsigned int block_log:16;
++ unsigned int flags:8;
++ unsigned int no_uids:8;
++ unsigned int no_guids:8;
++ unsigned int mkfs_time /* time of filesystem creation */;
++ squashfs_inode_t root_inode;
++ unsigned int block_size;
++ unsigned int fragments;
++ unsigned int fragment_table_start_2;
++ long long bytes_used;
++ long long uid_start;
++ long long guid_start;
++ long long inode_table_start;
++ long long directory_table_start;
++ long long fragment_table_start;
++ long long lookup_table_start;
++} __attribute__ ((packed));
++
++struct squashfs_dir_index {
++ unsigned int index;
++ unsigned int start_block;
++ unsigned char size;
++ unsigned char name[0];
++} __attribute__ ((packed));
++
++#define SQUASHFS_BASE_INODE_HEADER \
++ unsigned int inode_type:4; \
++ unsigned int mode:12; \
++ unsigned int uid:8; \
++ unsigned int guid:8; \
++ unsigned int mtime; \
++ unsigned int inode_number;
++
++struct squashfs_base_inode_header {
++ SQUASHFS_BASE_INODE_HEADER;
++} __attribute__ ((packed));
++
++struct squashfs_ipc_inode_header {
++ SQUASHFS_BASE_INODE_HEADER;
++ unsigned int nlink;
++} __attribute__ ((packed));
++
++struct squashfs_dev_inode_header {
++ SQUASHFS_BASE_INODE_HEADER;
++ unsigned int nlink;
++ unsigned short rdev;
++} __attribute__ ((packed));
++
++struct squashfs_symlink_inode_header {
++ SQUASHFS_BASE_INODE_HEADER;
++ unsigned int nlink;
++ unsigned short symlink_size;
++ char symlink[0];
++} __attribute__ ((packed));
++
++struct squashfs_reg_inode_header {
++ SQUASHFS_BASE_INODE_HEADER;
++ squashfs_block_t start_block;
++ unsigned int fragment;
++ unsigned int offset;
++ unsigned int file_size;
++ unsigned short block_list[0];
++} __attribute__ ((packed));
++
++struct squashfs_lreg_inode_header {
++ SQUASHFS_BASE_INODE_HEADER;
++ unsigned int nlink;
++ squashfs_block_t start_block;
++ unsigned int fragment;
++ unsigned int offset;
++ long long file_size;
++ unsigned short block_list[0];
++} __attribute__ ((packed));
++
++struct squashfs_dir_inode_header {
++ SQUASHFS_BASE_INODE_HEADER;
++ unsigned int nlink;
++ unsigned int file_size:19;
++ unsigned int offset:13;
++ unsigned int start_block;
++ unsigned int parent_inode;
++} __attribute__ ((packed));
++
++struct squashfs_ldir_inode_header {
++ SQUASHFS_BASE_INODE_HEADER;
++ unsigned int nlink;
++ unsigned int file_size:27;
++ unsigned int offset:13;
++ unsigned int start_block;
++ unsigned int i_count:16;
++ unsigned int parent_inode;
++ struct squashfs_dir_index index[0];
++} __attribute__ ((packed));
++
++union squashfs_inode_header {
++ struct squashfs_base_inode_header base;
++ struct squashfs_dev_inode_header dev;
++ struct squashfs_symlink_inode_header symlink;
++ struct squashfs_reg_inode_header reg;
++ struct squashfs_lreg_inode_header lreg;
++ struct squashfs_dir_inode_header dir;
++ struct squashfs_ldir_inode_header ldir;
++ struct squashfs_ipc_inode_header ipc;
++};
++
++struct squashfs_dir_entry {
++ unsigned int offset:13;
++ unsigned int type:3;
++ unsigned int size:8;
++ int inode_number:16;
++ char name[0];
++} __attribute__ ((packed));
++
++struct squashfs_dir_header {
++ unsigned int count:8;
++ unsigned int start_block;
++ unsigned int inode_number;
++} __attribute__ ((packed));
++
++struct squashfs_fragment_entry {
++ long long start_block;
++ unsigned int size;
++ unsigned int pending;
++} __attribute__ ((packed));
++
++extern int squashfs_uncompress_block(void *d, int dstlen, void *s, int srclen);
++extern int squashfs_uncompress_init(void);
++extern int squashfs_uncompress_exit(void);
++
++/*
++ * macros to convert each packed bitfield structure from little endian to big
++ * endian and vice versa. These are needed when creating or using a filesystem
++ * on a machine with different byte ordering to the target architecture.
++ *
++ */
++
++#define SQUASHFS_SWAP_START \
++ int bits;\
++ int b_pos;\
++ unsigned long long val;\
++ unsigned char *s;\
++ unsigned char *d;
++
++#define SQUASHFS_SWAP_SUPER_BLOCK(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_super_block));\
++ SQUASHFS_SWAP((s)->s_magic, d, 0, 32);\
++ SQUASHFS_SWAP((s)->inodes, d, 32, 32);\
++ SQUASHFS_SWAP((s)->bytes_used_2, d, 64, 32);\
++ SQUASHFS_SWAP((s)->uid_start_2, d, 96, 32);\
++ SQUASHFS_SWAP((s)->guid_start_2, d, 128, 32);\
++ SQUASHFS_SWAP((s)->inode_table_start_2, d, 160, 32);\
++ SQUASHFS_SWAP((s)->directory_table_start_2, d, 192, 32);\
++ SQUASHFS_SWAP((s)->s_major, d, 224, 16);\
++ SQUASHFS_SWAP((s)->s_minor, d, 240, 16);\
++ SQUASHFS_SWAP((s)->block_size_1, d, 256, 16);\
++ SQUASHFS_SWAP((s)->block_log, d, 272, 16);\
++ SQUASHFS_SWAP((s)->flags, d, 288, 8);\
++ SQUASHFS_SWAP((s)->no_uids, d, 296, 8);\
++ SQUASHFS_SWAP((s)->no_guids, d, 304, 8);\
++ SQUASHFS_SWAP((s)->mkfs_time, d, 312, 32);\
++ SQUASHFS_SWAP((s)->root_inode, d, 344, 64);\
++ SQUASHFS_SWAP((s)->block_size, d, 408, 32);\
++ SQUASHFS_SWAP((s)->fragments, d, 440, 32);\
++ SQUASHFS_SWAP((s)->fragment_table_start_2, d, 472, 32);\
++ SQUASHFS_SWAP((s)->bytes_used, d, 504, 64);\
++ SQUASHFS_SWAP((s)->uid_start, d, 568, 64);\
++ SQUASHFS_SWAP((s)->guid_start, d, 632, 64);\
++ SQUASHFS_SWAP((s)->inode_table_start, d, 696, 64);\
++ SQUASHFS_SWAP((s)->directory_table_start, d, 760, 64);\
++ SQUASHFS_SWAP((s)->fragment_table_start, d, 824, 64);\
++ SQUASHFS_SWAP((s)->lookup_table_start, d, 888, 64);\
++}
++
++#define SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\
++ SQUASHFS_MEMSET(s, d, n);\
++ SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
++ SQUASHFS_SWAP((s)->mode, d, 4, 12);\
++ SQUASHFS_SWAP((s)->uid, d, 16, 8);\
++ SQUASHFS_SWAP((s)->guid, d, 24, 8);\
++ SQUASHFS_SWAP((s)->mtime, d, 32, 32);\
++ SQUASHFS_SWAP((s)->inode_number, d, 64, 32);
++
++#define SQUASHFS_SWAP_BASE_INODE_HEADER(s, d, n) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\
++}
++
++#define SQUASHFS_SWAP_IPC_INODE_HEADER(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
++ sizeof(struct squashfs_ipc_inode_header))\
++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
++}
++
++#define SQUASHFS_SWAP_DEV_INODE_HEADER(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
++ sizeof(struct squashfs_dev_inode_header)); \
++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
++ SQUASHFS_SWAP((s)->rdev, d, 128, 16);\
++}
++
++#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
++ sizeof(struct squashfs_symlink_inode_header));\
++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
++ SQUASHFS_SWAP((s)->symlink_size, d, 128, 16);\
++}
++
++#define SQUASHFS_SWAP_REG_INODE_HEADER(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
++ sizeof(struct squashfs_reg_inode_header));\
++ SQUASHFS_SWAP((s)->start_block, d, 96, 64);\
++ SQUASHFS_SWAP((s)->fragment, d, 160, 32);\
++ SQUASHFS_SWAP((s)->offset, d, 192, 32);\
++ SQUASHFS_SWAP((s)->file_size, d, 224, 32);\
++}
++
++#define SQUASHFS_SWAP_LREG_INODE_HEADER(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
++ sizeof(struct squashfs_lreg_inode_header));\
++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
++ SQUASHFS_SWAP((s)->start_block, d, 128, 64);\
++ SQUASHFS_SWAP((s)->fragment, d, 192, 32);\
++ SQUASHFS_SWAP((s)->offset, d, 224, 32);\
++ SQUASHFS_SWAP((s)->file_size, d, 256, 64);\
++}
++
++#define SQUASHFS_SWAP_DIR_INODE_HEADER(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
++ sizeof(struct squashfs_dir_inode_header));\
++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
++ SQUASHFS_SWAP((s)->file_size, d, 128, 19);\
++ SQUASHFS_SWAP((s)->offset, d, 147, 13);\
++ SQUASHFS_SWAP((s)->start_block, d, 160, 32);\
++ SQUASHFS_SWAP((s)->parent_inode, d, 192, 32);\
++}
++
++#define SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
++ sizeof(struct squashfs_ldir_inode_header));\
++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
++ SQUASHFS_SWAP((s)->file_size, d, 128, 27);\
++ SQUASHFS_SWAP((s)->offset, d, 155, 13);\
++ SQUASHFS_SWAP((s)->start_block, d, 168, 32);\
++ SQUASHFS_SWAP((s)->i_count, d, 200, 16);\
++ SQUASHFS_SWAP((s)->parent_inode, d, 216, 32);\
++}
++
++#define SQUASHFS_SWAP_DIR_INDEX(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index));\
++ SQUASHFS_SWAP((s)->index, d, 0, 32);\
++ SQUASHFS_SWAP((s)->start_block, d, 32, 32);\
++ SQUASHFS_SWAP((s)->size, d, 64, 8);\
++}
++
++#define SQUASHFS_SWAP_DIR_HEADER(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header));\
++ SQUASHFS_SWAP((s)->count, d, 0, 8);\
++ SQUASHFS_SWAP((s)->start_block, d, 8, 32);\
++ SQUASHFS_SWAP((s)->inode_number, d, 40, 32);\
++}
++
++#define SQUASHFS_SWAP_DIR_ENTRY(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry));\
++ SQUASHFS_SWAP((s)->offset, d, 0, 13);\
++ SQUASHFS_SWAP((s)->type, d, 13, 3);\
++ SQUASHFS_SWAP((s)->size, d, 16, 8);\
++ SQUASHFS_SWAP((s)->inode_number, d, 24, 16);\
++}
++
++#define SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry));\
++ SQUASHFS_SWAP((s)->start_block, d, 0, 64);\
++ SQUASHFS_SWAP((s)->size, d, 64, 32);\
++}
++
++#define SQUASHFS_SWAP_INODE_T(s, d) SQUASHFS_SWAP_LONG_LONGS(s, d, 1)
++
++#define SQUASHFS_SWAP_SHORTS(s, d, n) {\
++ int entry;\
++ int bit_position;\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_MEMSET(s, d, n * 2);\
++ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
++ 16)\
++ SQUASHFS_SWAP(s[entry], d, bit_position, 16);\
++}
++
++#define SQUASHFS_SWAP_INTS(s, d, n) {\
++ int entry;\
++ int bit_position;\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_MEMSET(s, d, n * 4);\
++ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
++ 32)\
++ SQUASHFS_SWAP(s[entry], d, bit_position, 32);\
++}
++
++#define SQUASHFS_SWAP_LONG_LONGS(s, d, n) {\
++ int entry;\
++ int bit_position;\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_MEMSET(s, d, n * 8);\
++ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
++ 64)\
++ SQUASHFS_SWAP(s[entry], d, bit_position, 64);\
++}
++
++#define SQUASHFS_SWAP_DATA(s, d, n, bits) {\
++ int entry;\
++ int bit_position;\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_MEMSET(s, d, n * bits / 8);\
++ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
++ bits)\
++ SQUASHFS_SWAP(s[entry], d, bit_position, bits);\
++}
++
++#define SQUASHFS_SWAP_FRAGMENT_INDEXES(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n)
++#define SQUASHFS_SWAP_LOOKUP_BLOCKS(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n)
++
++#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY
++
++struct squashfs_base_inode_header_1 {
++ unsigned int inode_type:4;
++ unsigned int mode:12; /* protection */
++ unsigned int uid:4; /* index into uid table */
++ unsigned int guid:4; /* index into guid table */
++} __attribute__ ((packed));
++
++struct squashfs_ipc_inode_header_1 {
++ unsigned int inode_type:4;
++ unsigned int mode:12; /* protection */
++ unsigned int uid:4; /* index into uid table */
++ unsigned int guid:4; /* index into guid table */
++ unsigned int type:4;
++ unsigned int offset:4;
++} __attribute__ ((packed));
++
++struct squashfs_dev_inode_header_1 {
++ unsigned int inode_type:4;
++ unsigned int mode:12; /* protection */
++ unsigned int uid:4; /* index into uid table */
++ unsigned int guid:4; /* index into guid table */
++ unsigned short rdev;
++} __attribute__ ((packed));
++
++struct squashfs_symlink_inode_header_1 {
++ unsigned int inode_type:4;
++ unsigned int mode:12; /* protection */
++ unsigned int uid:4; /* index into uid table */
++ unsigned int guid:4; /* index into guid table */
++ unsigned short symlink_size;
++ char symlink[0];
++} __attribute__ ((packed));
++
++struct squashfs_reg_inode_header_1 {
++ unsigned int inode_type:4;
++ unsigned int mode:12; /* protection */
++ unsigned int uid:4; /* index into uid table */
++ unsigned int guid:4; /* index into guid table */
++ unsigned int mtime;
++ unsigned int start_block;
++ unsigned int file_size:32;
++ unsigned short block_list[0];
++} __attribute__ ((packed));
++
++struct squashfs_dir_inode_header_1 {
++ unsigned int inode_type:4;
++ unsigned int mode:12; /* protection */
++ unsigned int uid:4; /* index into uid table */
++ unsigned int guid:4; /* index into guid table */
++ unsigned int file_size:19;
++ unsigned int offset:13;
++ unsigned int mtime;
++ unsigned int start_block:24;
++} __attribute__ ((packed));
++
++#define SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n) \
++ SQUASHFS_MEMSET(s, d, n);\
++ SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
++ SQUASHFS_SWAP((s)->mode, d, 4, 12);\
++ SQUASHFS_SWAP((s)->uid, d, 16, 4);\
++ SQUASHFS_SWAP((s)->guid, d, 20, 4);
++
++#define SQUASHFS_SWAP_BASE_INODE_HEADER_1(s, d, n) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n)\
++}
++
++#define SQUASHFS_SWAP_IPC_INODE_HEADER_1(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
++ sizeof(struct squashfs_ipc_inode_header_1));\
++ SQUASHFS_SWAP((s)->type, d, 24, 4);\
++ SQUASHFS_SWAP((s)->offset, d, 28, 4);\
++}
++
++#define SQUASHFS_SWAP_DEV_INODE_HEADER_1(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
++ sizeof(struct squashfs_dev_inode_header_1));\
++ SQUASHFS_SWAP((s)->rdev, d, 24, 16);\
++}
++
++#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_1(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
++ sizeof(struct squashfs_symlink_inode_header_1));\
++ SQUASHFS_SWAP((s)->symlink_size, d, 24, 16);\
++}
++
++#define SQUASHFS_SWAP_REG_INODE_HEADER_1(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
++ sizeof(struct squashfs_reg_inode_header_1));\
++ SQUASHFS_SWAP((s)->mtime, d, 24, 32);\
++ SQUASHFS_SWAP((s)->start_block, d, 56, 32);\
++ SQUASHFS_SWAP((s)->file_size, d, 88, 32);\
++}
++
++#define SQUASHFS_SWAP_DIR_INODE_HEADER_1(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
++ sizeof(struct squashfs_dir_inode_header_1));\
++ SQUASHFS_SWAP((s)->file_size, d, 24, 19);\
++ SQUASHFS_SWAP((s)->offset, d, 43, 13);\
++ SQUASHFS_SWAP((s)->mtime, d, 56, 32);\
++ SQUASHFS_SWAP((s)->start_block, d, 88, 24);\
++}
++
++#endif
++
++#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY
++
++struct squashfs_dir_index_2 {
++ unsigned int index:27;
++ unsigned int start_block:29;
++ unsigned char size;
++ unsigned char name[0];
++} __attribute__ ((packed));
++
++struct squashfs_base_inode_header_2 {
++ unsigned int inode_type:4;
++ unsigned int mode:12; /* protection */
++ unsigned int uid:8; /* index into uid table */
++ unsigned int guid:8; /* index into guid table */
++} __attribute__ ((packed));
++
++struct squashfs_ipc_inode_header_2 {
++ unsigned int inode_type:4;
++ unsigned int mode:12; /* protection */
++ unsigned int uid:8; /* index into uid table */
++ unsigned int guid:8; /* index into guid table */
++} __attribute__ ((packed));
++
++struct squashfs_dev_inode_header_2 {
++ unsigned int inode_type:4;
++ unsigned int mode:12; /* protection */
++ unsigned int uid:8; /* index into uid table */
++ unsigned int guid:8; /* index into guid table */
++ unsigned short rdev;
++} __attribute__ ((packed));
++
++struct squashfs_symlink_inode_header_2 {
++ unsigned int inode_type:4;
++ unsigned int mode:12; /* protection */
++ unsigned int uid:8; /* index into uid table */
++ unsigned int guid:8; /* index into guid table */
++ unsigned short symlink_size;
++ char symlink[0];
++} __attribute__ ((packed));
++
++struct squashfs_reg_inode_header_2 {
++ unsigned int inode_type:4;
++ unsigned int mode:12; /* protection */
++ unsigned int uid:8; /* index into uid table */
++ unsigned int guid:8; /* index into guid table */
++ unsigned int mtime;
++ unsigned int start_block;
++ unsigned int fragment;
++ unsigned int offset;
++ unsigned int file_size:32;
++ unsigned short block_list[0];
++} __attribute__ ((packed));
++
++struct squashfs_dir_inode_header_2 {
++ unsigned int inode_type:4;
++ unsigned int mode:12; /* protection */
++ unsigned int uid:8; /* index into uid table */
++ unsigned int guid:8; /* index into guid table */
++ unsigned int file_size:19;
++ unsigned int offset:13;
++ unsigned int mtime;
++ unsigned int start_block:24;
++} __attribute__ ((packed));
++
++struct squashfs_ldir_inode_header_2 {
++ unsigned int inode_type:4;
++ unsigned int mode:12; /* protection */
++ unsigned int uid:8; /* index into uid table */
++ unsigned int guid:8; /* index into guid table */
++ unsigned int file_size:27;
++ unsigned int offset:13;
++ unsigned int mtime;
++ unsigned int start_block:24;
++ unsigned int i_count:16;
++ struct squashfs_dir_index_2 index[0];
++} __attribute__ ((packed));
++
++union squashfs_inode_header_2 {
++ struct squashfs_base_inode_header_2 base;
++ struct squashfs_dev_inode_header_2 dev;
++ struct squashfs_symlink_inode_header_2 symlink;
++ struct squashfs_reg_inode_header_2 reg;
++ struct squashfs_dir_inode_header_2 dir;
++ struct squashfs_ldir_inode_header_2 ldir;
++ struct squashfs_ipc_inode_header_2 ipc;
++};
++
++struct squashfs_dir_header_2 {
++ unsigned int count:8;
++ unsigned int start_block:24;
++} __attribute__ ((packed));
++
++struct squashfs_dir_entry_2 {
++ unsigned int offset:13;
++ unsigned int type:3;
++ unsigned int size:8;
++ char name[0];
++} __attribute__ ((packed));
++
++struct squashfs_fragment_entry_2 {
++ unsigned int start_block;
++ unsigned int size;
++} __attribute__ ((packed));
++
++#define SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\
++ SQUASHFS_MEMSET(s, d, n);\
++ SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
++ SQUASHFS_SWAP((s)->mode, d, 4, 12);\
++ SQUASHFS_SWAP((s)->uid, d, 16, 8);\
++ SQUASHFS_SWAP((s)->guid, d, 24, 8);\
++
++#define SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, n) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\
++}
++
++#define SQUASHFS_SWAP_IPC_INODE_HEADER_2(s, d) \
++ SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, sizeof(struct squashfs_ipc_inode_header_2))
++
++#define SQUASHFS_SWAP_DEV_INODE_HEADER_2(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
++ sizeof(struct squashfs_dev_inode_header_2)); \
++ SQUASHFS_SWAP((s)->rdev, d, 32, 16);\
++}
++
++#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
++ sizeof(struct squashfs_symlink_inode_header_2));\
++ SQUASHFS_SWAP((s)->symlink_size, d, 32, 16);\
++}
++
++#define SQUASHFS_SWAP_REG_INODE_HEADER_2(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
++ sizeof(struct squashfs_reg_inode_header_2));\
++ SQUASHFS_SWAP((s)->mtime, d, 32, 32);\
++ SQUASHFS_SWAP((s)->start_block, d, 64, 32);\
++ SQUASHFS_SWAP((s)->fragment, d, 96, 32);\
++ SQUASHFS_SWAP((s)->offset, d, 128, 32);\
++ SQUASHFS_SWAP((s)->file_size, d, 160, 32);\
++}
++
++#define SQUASHFS_SWAP_DIR_INODE_HEADER_2(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
++ sizeof(struct squashfs_dir_inode_header_2));\
++ SQUASHFS_SWAP((s)->file_size, d, 32, 19);\
++ SQUASHFS_SWAP((s)->offset, d, 51, 13);\
++ SQUASHFS_SWAP((s)->mtime, d, 64, 32);\
++ SQUASHFS_SWAP((s)->start_block, d, 96, 24);\
++}
++
++#define SQUASHFS_SWAP_LDIR_INODE_HEADER_2(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
++ sizeof(struct squashfs_ldir_inode_header_2));\
++ SQUASHFS_SWAP((s)->file_size, d, 32, 27);\
++ SQUASHFS_SWAP((s)->offset, d, 59, 13);\
++ SQUASHFS_SWAP((s)->mtime, d, 72, 32);\
++ SQUASHFS_SWAP((s)->start_block, d, 104, 24);\
++ SQUASHFS_SWAP((s)->i_count, d, 128, 16);\
++}
++
++#define SQUASHFS_SWAP_DIR_INDEX_2(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index_2));\
++ SQUASHFS_SWAP((s)->index, d, 0, 27);\
++ SQUASHFS_SWAP((s)->start_block, d, 27, 29);\
++ SQUASHFS_SWAP((s)->size, d, 56, 8);\
++}
++#define SQUASHFS_SWAP_DIR_HEADER_2(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header_2));\
++ SQUASHFS_SWAP((s)->count, d, 0, 8);\
++ SQUASHFS_SWAP((s)->start_block, d, 8, 24);\
++}
++
++#define SQUASHFS_SWAP_DIR_ENTRY_2(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry_2));\
++ SQUASHFS_SWAP((s)->offset, d, 0, 13);\
++ SQUASHFS_SWAP((s)->type, d, 13, 3);\
++ SQUASHFS_SWAP((s)->size, d, 16, 8);\
++}
++
++#define SQUASHFS_SWAP_FRAGMENT_ENTRY_2(s, d) {\
++ SQUASHFS_SWAP_START\
++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry_2));\
++ SQUASHFS_SWAP((s)->start_block, d, 0, 32);\
++ SQUASHFS_SWAP((s)->size, d, 32, 32);\
++}
++
++#define SQUASHFS_SWAP_FRAGMENT_INDEXES_2(s, d, n) SQUASHFS_SWAP_INTS(s, d, n)
++
++/* fragment and fragment table defines */
++#define SQUASHFS_FRAGMENT_BYTES_2(A) (A * sizeof(struct squashfs_fragment_entry_2))
++
++#define SQUASHFS_FRAGMENT_INDEX_2(A) (SQUASHFS_FRAGMENT_BYTES_2(A) / \
++ SQUASHFS_METADATA_SIZE)
++
++#define SQUASHFS_FRAGMENT_INDEX_OFFSET_2(A) (SQUASHFS_FRAGMENT_BYTES_2(A) % \
++ SQUASHFS_METADATA_SIZE)
++
++#define SQUASHFS_FRAGMENT_INDEXES_2(A) ((SQUASHFS_FRAGMENT_BYTES_2(A) + \
++ SQUASHFS_METADATA_SIZE - 1) / \
++ SQUASHFS_METADATA_SIZE)
++
++#define SQUASHFS_FRAGMENT_INDEX_BYTES_2(A) (SQUASHFS_FRAGMENT_INDEXES_2(A) *\
++ sizeof(int))
++
++#endif
++
++#ifdef __KERNEL__
++
++/*
++ * macros used to swap each structure entry, taking into account
++ * bitfields and different bitfield placing conventions on differing
++ * architectures
++ */
++
++#include <asm/byteorder.h>
++
++#ifdef __BIG_ENDIAN
++ /* convert from little endian to big endian */
++#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \
++ tbits, b_pos)
++#else
++ /* convert from big endian to little endian */
++#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \
++ tbits, 64 - tbits - b_pos)
++#endif
++
++#define _SQUASHFS_SWAP(value, p, pos, tbits, SHIFT) {\
++ b_pos = pos % 8;\
++ val = 0;\
++ s = (unsigned char *)p + (pos / 8);\
++ d = ((unsigned char *) &val) + 7;\
++ for(bits = 0; bits < (tbits + b_pos); bits += 8) \
++ *d-- = *s++;\
++ value = (val >> (SHIFT))/* & ((1 << tbits) - 1)*/;\
++}
++
++#define SQUASHFS_MEMSET(s, d, n) memset(s, 0, n);
++
++#endif
++#endif
+diff -rduNp linux-2.6.22.1.oorig/include/linux/squashfs_fs_i.h linux-2.6.22.1/include/linux/squashfs_fs_i.h
+--- linux-2.6.22.1.oorig/include/linux/squashfs_fs_i.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/include/linux/squashfs_fs_i.h 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,45 @@
++#ifndef SQUASHFS_FS_I
++#define SQUASHFS_FS_I
++/*
++ * Squashfs
++ *
++ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
++ * Phillip Lougher <phillip@lougher.org.uk>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2,
++ * 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ * squashfs_fs_i.h
++ */
++
++struct squashfs_inode_info {
++ long long start_block;
++ unsigned int offset;
++ union {
++ struct {
++ long long fragment_start_block;
++ unsigned int fragment_size;
++ unsigned int fragment_offset;
++ long long block_list_start;
++ } s1;
++ struct {
++ long long directory_index_start;
++ unsigned int directory_index_offset;
++ unsigned int directory_index_count;
++ unsigned int parent_inode;
++ } s2;
++ } u;
++ struct inode vfs_inode;
++};
++#endif
+diff -rduNp linux-2.6.22.1.oorig/include/linux/squashfs_fs_sb.h linux-2.6.22.1/include/linux/squashfs_fs_sb.h
+--- linux-2.6.22.1.oorig/include/linux/squashfs_fs_sb.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/include/linux/squashfs_fs_sb.h 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,74 @@
++#ifndef SQUASHFS_FS_SB
++#define SQUASHFS_FS_SB
++/*
++ * Squashfs
++ *
++ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007
++ * Phillip Lougher <phillip@lougher.org.uk>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2,
++ * 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ * squashfs_fs_sb.h
++ */
++
++#include <linux/squashfs_fs.h>
++
++struct squashfs_cache {
++ long long block;
++ int length;
++ long long next_index;
++ char *data;
++};
++
++struct squashfs_fragment_cache {
++ long long block;
++ int length;
++ unsigned int locked;
++ char *data;
++};
++
++struct squashfs_sb_info {
++ struct squashfs_super_block sblk;
++ int devblksize;
++ int devblksize_log2;
++ int swap;
++ struct squashfs_cache *block_cache;
++ struct squashfs_fragment_cache *fragment;
++ int next_cache;
++ int next_fragment;
++ int next_meta_index;
++ unsigned int *uid;
++ unsigned int *guid;
++ long long *fragment_index;
++ unsigned int *fragment_index_2;
++ char *read_page;
++ struct mutex read_data_mutex;
++ struct mutex read_page_mutex;
++ struct mutex block_cache_mutex;
++ struct mutex fragment_mutex;
++ struct mutex meta_index_mutex;
++ wait_queue_head_t waitq;
++ wait_queue_head_t fragment_wait_queue;
++ struct meta_index *meta_index;
++ z_stream stream;
++ long long *inode_lookup_table;
++ int (*read_inode)(struct inode *i, squashfs_inode_t \
++ inode);
++ long long (*read_blocklist)(struct inode *inode, int \
++ index, int readahead_blks, char *block_list, \
++ unsigned short **block_p, unsigned int *bsize);
++ int (*read_fragment_index_table)(struct super_block *s);
++};
++#endif
+diff -rduNp linux-2.6.22.1.oorig/init/Kconfig linux-2.6.22.1/init/Kconfig
+--- linux-2.6.22.1.oorig/init/Kconfig 2007-07-10 20:56:30.000000000 +0200
++++ linux-2.6.22.1/init/Kconfig 2007-07-24 14:17:46.000000000 +0200
+@@ -246,23 +246,21 @@ config AUDITSYSCALL
+ ensure that INOTIFY is configured.
+
+ config IKCONFIG
+- tristate "Kernel .config support"
++ tristate "Kernel .miniconfig support"
+ ---help---
+- This option enables the complete Linux kernel ".config" file
++ This option enables the mini Linux kernel ".miniconfig" file
+ contents to be saved in the kernel. It provides documentation
+ of which kernel options are used in a running kernel or in an
+- on-disk kernel. This information can be extracted from the kernel
+- image file with the script scripts/extract-ikconfig and used as
+- input to rebuild the current kernel or to build another kernel.
+- It can also be extracted from a running kernel by reading
+- /proc/config.gz if enabled (below).
++ on-disk kernel.
++ It can be extracted from a running kernel by reading
++ /proc/miniconfig.gz if enabled (below).
+
+ config IKCONFIG_PROC
+- bool "Enable access to .config through /proc/config.gz"
++ bool "Enable access to .miniconfig through /proc/miniconfig.gz"
+ depends on IKCONFIG && PROC_FS
+ ---help---
+ This option enables access to the kernel configuration file
+- through /proc/config.gz.
++ through /proc/miniconfig.gz.
+
+ config LOG_BUF_SHIFT
+ int "Kernel log buffer size (16 => 64KB, 17 => 128KB)"
+diff -rduNp linux-2.6.22.1.oorig/init/LzmaDecode.c linux-2.6.22.1/init/LzmaDecode.c
+--- linux-2.6.22.1.oorig/init/LzmaDecode.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/init/LzmaDecode.c 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,588 @@
++/*
++ LzmaDecode.c
++ LZMA Decoder (optimized for Speed version)
++
++ LZMA SDK 4.22 Copyright (c) 1999-2005 Igor Pavlov (2005-06-10)
++ http://www.7-zip.org/
++
++ LZMA SDK is licensed under two licenses:
++ 1) GNU Lesser General Public License (GNU LGPL)
++ 2) Common Public License (CPL)
++ It means that you can select one of these two licenses and
++ follow rules of that license.
++
++ SPECIAL EXCEPTION:
++ Igor Pavlov, as the author of this Code, expressly permits you to
++ statically or dynamically link your Code (or bind by name) to the
++ interfaces of this file without subjecting your linked Code to the
++ terms of the CPL or GNU LGPL. Any modifications or additions
++ to this file, however, are subject to the LGPL or CPL terms.
++*/
++
++#include "LzmaDecode.h"
++
++#ifndef Byte
++#define Byte unsigned char
++#endif
++
++#define kNumTopBits 24
++#define kTopValue ((UInt32)1 << kNumTopBits)
++
++#define kNumBitModelTotalBits 11
++#define kBitModelTotal (1 << kNumBitModelTotalBits)
++#define kNumMoveBits 5
++
++#define RC_READ_BYTE (*Buffer++)
++
++#define RC_INIT2 Code = 0; Range = 0xFFFFFFFF; \
++ { int i; for(i = 0; i < 5; i++) { RC_TEST; Code = (Code << 8) | RC_READ_BYTE; }}
++
++#ifdef _LZMA_IN_CB
++
++#define RC_TEST { if (Buffer == BufferLim) \
++ { SizeT size; int result = InCallback->Read(InCallback, &Buffer, &size); if (result != LZMA_RESULT_OK) return result; \
++ BufferLim = Buffer + size; if (size == 0) return LZMA_RESULT_DATA_ERROR; }}
++
++#define RC_INIT Buffer = BufferLim = 0; RC_INIT2
++
++#else
++
++#define RC_TEST { if (Buffer == BufferLim) return LZMA_RESULT_DATA_ERROR; }
++
++#define RC_INIT(buffer, bufferSize) Buffer = buffer; BufferLim = buffer + bufferSize; RC_INIT2
++
++#endif
++
++#define RC_NORMALIZE if (Range < kTopValue) { RC_TEST; Range <<= 8; Code = (Code << 8) | RC_READ_BYTE; }
++
++#define IfBit0(p) RC_NORMALIZE; bound = (Range >> kNumBitModelTotalBits) * *(p); if (Code < bound)
++#define UpdateBit0(p) Range = bound; *(p) += (kBitModelTotal - *(p)) >> kNumMoveBits;
++#define UpdateBit1(p) Range -= bound; Code -= bound; *(p) -= (*(p)) >> kNumMoveBits;
++
++#define RC_GET_BIT2(p, mi, A0, A1) IfBit0(p) \
++ { UpdateBit0(p); mi <<= 1; A0; } else \
++ { UpdateBit1(p); mi = (mi + mi) + 1; A1; }
++
++#define RC_GET_BIT(p, mi) RC_GET_BIT2(p, mi, ; , ;)
++
++#define RangeDecoderBitTreeDecode(probs, numLevels, res) \
++ { int i = numLevels; res = 1; \
++ do { CProb *p = probs + res; RC_GET_BIT(p, res) } while(--i != 0); \
++ res -= (1 << numLevels); }
++
++
++#define kNumPosBitsMax 4
++#define kNumPosStatesMax (1 << kNumPosBitsMax)
++
++#define kLenNumLowBits 3
++#define kLenNumLowSymbols (1 << kLenNumLowBits)
++#define kLenNumMidBits 3
++#define kLenNumMidSymbols (1 << kLenNumMidBits)
++#define kLenNumHighBits 8
++#define kLenNumHighSymbols (1 << kLenNumHighBits)
++
++#define LenChoice 0
++#define LenChoice2 (LenChoice + 1)
++#define LenLow (LenChoice2 + 1)
++#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits))
++#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits))
++#define kNumLenProbs (LenHigh + kLenNumHighSymbols)
++
++
++#define kNumStates 12
++#define kNumLitStates 7
++
++#define kStartPosModelIndex 4
++#define kEndPosModelIndex 14
++#define kNumFullDistances (1 << (kEndPosModelIndex >> 1))
++
++#define kNumPosSlotBits 6
++#define kNumLenToPosStates 4
++
++#define kNumAlignBits 4
++#define kAlignTableSize (1 << kNumAlignBits)
++
++#define kMatchMinLen 2
++
++#define IsMatch 0
++#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax))
++#define IsRepG0 (IsRep + kNumStates)
++#define IsRepG1 (IsRepG0 + kNumStates)
++#define IsRepG2 (IsRepG1 + kNumStates)
++#define IsRep0Long (IsRepG2 + kNumStates)
++#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax))
++#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits))
++#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex)
++#define LenCoder (Align + kAlignTableSize)
++#define RepLenCoder (LenCoder + kNumLenProbs)
++#define Literal (RepLenCoder + kNumLenProbs)
++
++#if Literal != LZMA_BASE_SIZE
++StopCompilingDueBUG
++#endif
++
++int LzmaDecodeProperties(CLzmaProperties *propsRes, const unsigned char *propsData, int size)
++{
++ unsigned char prop0;
++ if (size < LZMA_PROPERTIES_SIZE)
++ return LZMA_RESULT_DATA_ERROR;
++ prop0 = propsData[0];
++ if (prop0 >= (9 * 5 * 5))
++ return LZMA_RESULT_DATA_ERROR;
++ {
++ for (propsRes->pb = 0; prop0 >= (9 * 5); propsRes->pb++, prop0 -= (9 * 5));
++ for (propsRes->lp = 0; prop0 >= 9; propsRes->lp++, prop0 -= 9);
++ propsRes->lc = prop0;
++ /*
++ unsigned char remainder = (unsigned char)(prop0 / 9);
++ propsRes->lc = prop0 % 9;
++ propsRes->pb = remainder / 5;
++ propsRes->lp = remainder % 5;
++ */
++ }
++
++ #ifdef _LZMA_OUT_READ
++ {
++ int i;
++ propsRes->DictionarySize = 0;
++ for (i = 0; i < 4; i++)
++ propsRes->DictionarySize += (UInt32)(propsData[1 + i]) << (i * 8);
++ if (propsRes->DictionarySize == 0)
++ propsRes->DictionarySize = 1;
++ }
++ #endif
++ return LZMA_RESULT_OK;
++}
++
++#define kLzmaStreamWasFinishedId (-1)
++
++int LzmaDecode(CLzmaDecoderState *vs,
++ #ifdef _LZMA_IN_CB
++ ILzmaInCallback *InCallback,
++ #else
++ const unsigned char *inStream, SizeT inSize, SizeT *inSizeProcessed,
++ #endif
++ unsigned char *outStream, SizeT outSize, SizeT *outSizeProcessed)
++{
++ CProb *p = vs->Probs;
++ SizeT nowPos = 0;
++ Byte previousByte = 0;
++ UInt32 posStateMask = (1 << (vs->Properties.pb)) - 1;
++ UInt32 literalPosMask = (1 << (vs->Properties.lp)) - 1;
++ int lc = vs->Properties.lc;
++
++ #ifdef _LZMA_OUT_READ
++
++ UInt32 Range = vs->Range;
++ UInt32 Code = vs->Code;
++ #ifdef _LZMA_IN_CB
++ const Byte *Buffer = vs->Buffer;
++ const Byte *BufferLim = vs->BufferLim;
++ #else
++ const Byte *Buffer = inStream;
++ const Byte *BufferLim = inStream + inSize;
++ #endif
++ int state = vs->State;
++ UInt32 rep0 = vs->Reps[0], rep1 = vs->Reps[1], rep2 = vs->Reps[2], rep3 = vs->Reps[3];
++ int len = vs->RemainLen;
++ UInt32 globalPos = vs->GlobalPos;
++ UInt32 distanceLimit = vs->DistanceLimit;
++
++ Byte *dictionary = vs->Dictionary;
++ UInt32 dictionarySize = vs->Properties.DictionarySize;
++ UInt32 dictionaryPos = vs->DictionaryPos;
++
++ Byte tempDictionary[4];
++
++ #ifndef _LZMA_IN_CB
++ *inSizeProcessed = 0;
++ #endif
++ *outSizeProcessed = 0;
++ if (len == kLzmaStreamWasFinishedId)
++ return LZMA_RESULT_OK;
++
++ if (dictionarySize == 0)
++ {
++ dictionary = tempDictionary;
++ dictionarySize = 1;
++ tempDictionary[0] = vs->TempDictionary[0];
++ }
++
++ if (len == kLzmaNeedInitId)
++ {
++ {
++ UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + vs->Properties.lp));
++ UInt32 i;
++ for (i = 0; i < numProbs; i++)
++ p[i] = kBitModelTotal >> 1;
++ rep0 = rep1 = rep2 = rep3 = 1;
++ state = 0;
++ globalPos = 0;
++ distanceLimit = 0;
++ dictionaryPos = 0;
++ dictionary[dictionarySize - 1] = 0;
++ #ifdef _LZMA_IN_CB
++ RC_INIT;
++ #else
++ RC_INIT(inStream, inSize);
++ #endif
++ }
++ len = 0;
++ }
++ while(len != 0 && nowPos < outSize)
++ {
++ UInt32 pos = dictionaryPos - rep0;
++ if (pos >= dictionarySize)
++ pos += dictionarySize;
++ outStream[nowPos++] = dictionary[dictionaryPos] = dictionary[pos];
++ if (++dictionaryPos == dictionarySize)
++ dictionaryPos = 0;
++ len--;
++ }
++ if (dictionaryPos == 0)
++ previousByte = dictionary[dictionarySize - 1];
++ else
++ previousByte = dictionary[dictionaryPos - 1];
++
++ #else /* if !_LZMA_OUT_READ */
++
++ int state = 0;
++ UInt32 rep0 = 1, rep1 = 1, rep2 = 1, rep3 = 1;
++ int len = 0;
++ const Byte *Buffer;
++ const Byte *BufferLim;
++ UInt32 Range;
++ UInt32 Code;
++
++ #ifndef _LZMA_IN_CB
++ *inSizeProcessed = 0;
++ #endif
++ *outSizeProcessed = 0;
++
++ {
++ UInt32 i;
++ UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + vs->Properties.lp));
++ for (i = 0; i < numProbs; i++)
++ p[i] = kBitModelTotal >> 1;
++ }
++
++ #ifdef _LZMA_IN_CB
++ RC_INIT;
++ #else
++ RC_INIT(inStream, inSize);
++ #endif
++
++ #endif /* _LZMA_OUT_READ */
++
++ while(nowPos < outSize)
++ {
++ CProb *prob;
++ UInt32 bound;
++ int posState = (int)(
++ (nowPos
++ #ifdef _LZMA_OUT_READ
++ + globalPos
++ #endif
++ )
++ & posStateMask);
++
++ prob = p + IsMatch + (state << kNumPosBitsMax) + posState;
++ IfBit0(prob)
++ {
++ int symbol = 1;
++ UpdateBit0(prob)
++ prob = p + Literal + (LZMA_LIT_SIZE *
++ (((
++ (nowPos
++ #ifdef _LZMA_OUT_READ
++ + globalPos
++ #endif
++ )
++ & literalPosMask) << lc) + (previousByte >> (8 - lc))));
++
++ if (state >= kNumLitStates)
++ {
++ int matchByte;
++ #ifdef _LZMA_OUT_READ
++ UInt32 pos = dictionaryPos - rep0;
++ if (pos >= dictionarySize)
++ pos += dictionarySize;
++ matchByte = dictionary[pos];
++ #else
++ matchByte = outStream[nowPos - rep0];
++ #endif
++ do
++ {
++ int bit;
++ CProb *probLit;
++ matchByte <<= 1;
++ bit = (matchByte & 0x100);
++ probLit = prob + 0x100 + bit + symbol;
++ RC_GET_BIT2(probLit, symbol, if (bit != 0) break, if (bit == 0) break)
++ }
++ while (symbol < 0x100);
++ }
++ while (symbol < 0x100)
++ {
++ CProb *probLit = prob + symbol;
++ RC_GET_BIT(probLit, symbol)
++ }
++ previousByte = (Byte)symbol;
++
++ outStream[nowPos++] = previousByte;
++ #ifdef _LZMA_OUT_READ
++ if (distanceLimit < dictionarySize)
++ distanceLimit++;
++
++ dictionary[dictionaryPos] = previousByte;
++ if (++dictionaryPos == dictionarySize)
++ dictionaryPos = 0;
++ #endif
++ if (state < 4) state = 0;
++ else if (state < 10) state -= 3;
++ else state -= 6;
++ }
++ else
++ {
++ UpdateBit1(prob);
++ prob = p + IsRep + state;
++ IfBit0(prob)
++ {
++ UpdateBit0(prob);
++ rep3 = rep2;
++ rep2 = rep1;
++ rep1 = rep0;
++ state = state < kNumLitStates ? 0 : 3;
++ prob = p + LenCoder;
++ }
++ else
++ {
++ UpdateBit1(prob);
++ prob = p + IsRepG0 + state;
++ IfBit0(prob)
++ {
++ UpdateBit0(prob);
++ prob = p + IsRep0Long + (state << kNumPosBitsMax) + posState;
++ IfBit0(prob)
++ {
++ #ifdef _LZMA_OUT_READ
++ UInt32 pos;
++ #endif
++ UpdateBit0(prob);
++
++ #ifdef _LZMA_OUT_READ
++ if (distanceLimit == 0)
++ #else
++ if (nowPos == 0)
++ #endif
++ return LZMA_RESULT_DATA_ERROR;
++
++ state = state < kNumLitStates ? 9 : 11;
++ #ifdef _LZMA_OUT_READ
++ pos = dictionaryPos - rep0;
++ if (pos >= dictionarySize)
++ pos += dictionarySize;
++ previousByte = dictionary[pos];
++ dictionary[dictionaryPos] = previousByte;
++ if (++dictionaryPos == dictionarySize)
++ dictionaryPos = 0;
++ #else
++ previousByte = outStream[nowPos - rep0];
++ #endif
++ outStream[nowPos++] = previousByte;
++ #ifdef _LZMA_OUT_READ
++ if (distanceLimit < dictionarySize)
++ distanceLimit++;
++ #endif
++
++ continue;
++ }
++ else
++ {
++ UpdateBit1(prob);
++ }
++ }
++ else
++ {
++ UInt32 distance;
++ UpdateBit1(prob);
++ prob = p + IsRepG1 + state;
++ IfBit0(prob)
++ {
++ UpdateBit0(prob);
++ distance = rep1;
++ }
++ else
++ {
++ UpdateBit1(prob);
++ prob = p + IsRepG2 + state;
++ IfBit0(prob)
++ {
++ UpdateBit0(prob);
++ distance = rep2;
++ }
++ else
++ {
++ UpdateBit1(prob);
++ distance = rep3;
++ rep3 = rep2;
++ }
++ rep2 = rep1;
++ }
++ rep1 = rep0;
++ rep0 = distance;
++ }
++ state = state < kNumLitStates ? 8 : 11;
++ prob = p + RepLenCoder;
++ }
++ {
++ int numBits, offset;
++ CProb *probLen = prob + LenChoice;
++ IfBit0(probLen)
++ {
++ UpdateBit0(probLen);
++ probLen = prob + LenLow + (posState << kLenNumLowBits);
++ offset = 0;
++ numBits = kLenNumLowBits;
++ }
++ else
++ {
++ UpdateBit1(probLen);
++ probLen = prob + LenChoice2;
++ IfBit0(probLen)
++ {
++ UpdateBit0(probLen);
++ probLen = prob + LenMid + (posState << kLenNumMidBits);
++ offset = kLenNumLowSymbols;
++ numBits = kLenNumMidBits;
++ }
++ else
++ {
++ UpdateBit1(probLen);
++ probLen = prob + LenHigh;
++ offset = kLenNumLowSymbols + kLenNumMidSymbols;
++ numBits = kLenNumHighBits;
++ }
++ }
++ RangeDecoderBitTreeDecode(probLen, numBits, len);
++ len += offset;
++ }
++
++ if (state < 4)
++ {
++ int posSlot;
++ state += kNumLitStates;
++ prob = p + PosSlot +
++ ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) <<
++ kNumPosSlotBits);
++ RangeDecoderBitTreeDecode(prob, kNumPosSlotBits, posSlot);
++ if (posSlot >= kStartPosModelIndex)
++ {
++ int numDirectBits = ((posSlot >> 1) - 1);
++ rep0 = (2 | ((UInt32)posSlot & 1));
++ if (posSlot < kEndPosModelIndex)
++ {
++ rep0 <<= numDirectBits;
++ prob = p + SpecPos + rep0 - posSlot - 1;
++ }
++ else
++ {
++ numDirectBits -= kNumAlignBits;
++ do
++ {
++ RC_NORMALIZE
++ Range >>= 1;
++ rep0 <<= 1;
++ if (Code >= Range)
++ {
++ Code -= Range;
++ rep0 |= 1;
++ }
++ }
++ while (--numDirectBits != 0);
++ prob = p + Align;
++ rep0 <<= kNumAlignBits;
++ numDirectBits = kNumAlignBits;
++ }
++ {
++ int i = 1;
++ int mi = 1;
++ do
++ {
++ CProb *prob3 = prob + mi;
++ RC_GET_BIT2(prob3, mi, ; , rep0 |= i);
++ i <<= 1;
++ }
++ while(--numDirectBits != 0);
++ }
++ }
++ else
++ rep0 = posSlot;
++ if (++rep0 == (UInt32)(0))
++ {
++ /* it's for stream version */
++ len = kLzmaStreamWasFinishedId;
++ break;
++ }
++ }
++
++ len += kMatchMinLen;
++ #ifdef _LZMA_OUT_READ
++ if (rep0 > distanceLimit)
++ #else
++ if (rep0 > nowPos)
++ #endif
++ return LZMA_RESULT_DATA_ERROR;
++
++ #ifdef _LZMA_OUT_READ
++ if (dictionarySize - distanceLimit > (UInt32)len)
++ distanceLimit += len;
++ else
++ distanceLimit = dictionarySize;
++ #endif
++
++ do
++ {
++ #ifdef _LZMA_OUT_READ
++ UInt32 pos = dictionaryPos - rep0;
++ if (pos >= dictionarySize)
++ pos += dictionarySize;
++ previousByte = dictionary[pos];
++ dictionary[dictionaryPos] = previousByte;
++ if (++dictionaryPos == dictionarySize)
++ dictionaryPos = 0;
++ #else
++ previousByte = outStream[nowPos - rep0];
++ #endif
++ len--;
++ outStream[nowPos++] = previousByte;
++ }
++ while(len != 0 && nowPos < outSize);
++ }
++ }
++ RC_NORMALIZE;
++
++ #ifdef _LZMA_OUT_READ
++ vs->Range = Range;
++ vs->Code = Code;
++ vs->DictionaryPos = dictionaryPos;
++ vs->GlobalPos = globalPos + (UInt32)nowPos;
++ vs->DistanceLimit = distanceLimit;
++ vs->Reps[0] = rep0;
++ vs->Reps[1] = rep1;
++ vs->Reps[2] = rep2;
++ vs->Reps[3] = rep3;
++ vs->State = state;
++ vs->RemainLen = len;
++ vs->TempDictionary[0] = tempDictionary[0];
++ #endif
++
++ #ifdef _LZMA_IN_CB
++ vs->Buffer = Buffer;
++ vs->BufferLim = BufferLim;
++ #else
++ *inSizeProcessed = (SizeT)(Buffer - inStream);
++ #endif
++ *outSizeProcessed = nowPos;
++ return LZMA_RESULT_OK;
++}
+diff -rduNp linux-2.6.22.1.oorig/init/LzmaDecode.h linux-2.6.22.1/init/LzmaDecode.h
+--- linux-2.6.22.1.oorig/init/LzmaDecode.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/init/LzmaDecode.h 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,131 @@
++/*
++ LzmaDecode.h
++ LZMA Decoder interface
++
++ LZMA SDK 4.21 Copyright (c) 1999-2005 Igor Pavlov (2005-06-08)
++ http://www.7-zip.org/
++
++ LZMA SDK is licensed under two licenses:
++ 1) GNU Lesser General Public License (GNU LGPL)
++ 2) Common Public License (CPL)
++ It means that you can select one of these two licenses and
++ follow rules of that license.
++
++ SPECIAL EXCEPTION:
++ Igor Pavlov, as the author of this code, expressly permits you to
++ statically or dynamically link your code (or bind by name) to the
++ interfaces of this file without subjecting your linked code to the
++ terms of the CPL or GNU LGPL. Any modifications or additions
++ to this file, however, are subject to the LGPL or CPL terms.
++*/
++
++#ifndef __LZMADECODE_H
++#define __LZMADECODE_H
++
++/* #define _LZMA_IN_CB */
++/* Use callback for input data */
++
++/* #define _LZMA_OUT_READ */
++/* Use read function for output data */
++
++/* #define _LZMA_PROB32 */
++/* It can increase speed on some 32-bit CPUs,
++ but memory usage will be doubled in that case */
++
++/* #define _LZMA_LOC_OPT */
++/* Enable local speed optimizations inside code */
++
++/* #define _LZMA_SYSTEM_SIZE_T */
++/* Use system's size_t. You can use it to enable 64-bit sizes supporting*/
++
++#ifndef UInt32
++#ifdef _LZMA_UINT32_IS_ULONG
++#define UInt32 unsigned long
++#else
++#define UInt32 unsigned int
++#endif
++#endif
++
++#ifndef SizeT
++#ifdef _LZMA_SYSTEM_SIZE_T
++#include <stddef.h>
++#define SizeT size_t
++#else
++#define SizeT UInt32
++#endif
++#endif
++
++#ifdef _LZMA_PROB32
++#define CProb UInt32
++#else
++#define CProb unsigned short
++#endif
++
++#define LZMA_RESULT_OK 0
++#define LZMA_RESULT_DATA_ERROR 1
++
++#ifdef _LZMA_IN_CB
++typedef struct _ILzmaInCallback
++{
++ int (*Read)(void *object, const unsigned char **buffer, SizeT *bufferSize);
++} ILzmaInCallback;
++#endif
++
++#define LZMA_BASE_SIZE 1846
++#define LZMA_LIT_SIZE 768
++
++#define LZMA_PROPERTIES_SIZE 5
++
++typedef struct _CLzmaProperties
++{
++ int lc;
++ int lp;
++ int pb;
++ #ifdef _LZMA_OUT_READ
++ UInt32 DictionarySize;
++ #endif
++}CLzmaProperties;
++
++int LzmaDecodeProperties(CLzmaProperties *propsRes, const unsigned char *propsData, int size);
++
++#define LzmaGetNumProbs(Properties) (LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((Properties)->lc + (Properties)->lp)))
++
++#define kLzmaNeedInitId (-2)
++
++typedef struct _CLzmaDecoderState
++{
++ CLzmaProperties Properties;
++ CProb *Probs;
++
++ #ifdef _LZMA_IN_CB
++ const unsigned char *Buffer;
++ const unsigned char *BufferLim;
++ #endif
++
++ #ifdef _LZMA_OUT_READ
++ unsigned char *Dictionary;
++ UInt32 Range;
++ UInt32 Code;
++ UInt32 DictionaryPos;
++ UInt32 GlobalPos;
++ UInt32 DistanceLimit;
++ UInt32 Reps[4];
++ int State;
++ int RemainLen;
++ unsigned char TempDictionary[4];
++ #endif
++} CLzmaDecoderState;
++
++#ifdef _LZMA_OUT_READ
++#define LzmaDecoderInit(vs) { (vs)->RemainLen = kLzmaNeedInitId; }
++#endif
++
++int LzmaDecode(CLzmaDecoderState *vs,
++ #ifdef _LZMA_IN_CB
++ ILzmaInCallback *inCallback,
++ #else
++ const unsigned char *inStream, SizeT inSize, SizeT *inSizeProcessed,
++ #endif
++ unsigned char *outStream, SizeT outSize, SizeT *outSizeProcessed);
++
++#endif
+diff -rduNp linux-2.6.22.1.oorig/init/do_mounts_rd.c linux-2.6.22.1/init/do_mounts_rd.c
+--- linux-2.6.22.1.oorig/init/do_mounts_rd.c 2007-07-10 20:56:30.000000000 +0200
++++ linux-2.6.22.1/init/do_mounts_rd.c 2007-07-24 14:17:46.000000000 +0200
+@@ -5,7 +5,9 @@
+ #include <linux/ext2_fs.h>
+ #include <linux/romfs_fs.h>
+ #include <linux/cramfs_fs.h>
++#include <linux/squashfs_fs.h>
+ #include <linux/initrd.h>
++#include <linux/vmalloc.h>
+ #include <linux/string.h>
+
+ #include "do_mounts.h"
+@@ -31,6 +33,9 @@ static int __init ramdisk_start_setup(ch
+ __setup("ramdisk_start=", ramdisk_start_setup);
+
+ static int __init crd_load(int in_fd, int out_fd);
++#ifdef CONFIG_LZMA_INITRD
++static int __init lzma_rd_load(int in_fd, int out_fd);
++#endif
+
+ /*
+ * This routine tries to find a RAM disk image to load, and returns the
+@@ -39,6 +44,7 @@ static int __init crd_load(int in_fd, in
+ * numbers could not be found.
+ *
+ * We currently check for the following magic numbers:
++ * squashfs
+ * minix
+ * ext2
+ * romfs
+@@ -53,6 +59,7 @@ identify_ramdisk_image(int fd, int start
+ struct ext2_super_block *ext2sb;
+ struct romfs_super_block *romfsb;
+ struct cramfs_super *cramfsb;
++ struct squashfs_super_block *squashfsb;
+ int nblocks = -1;
+ unsigned char *buf;
+
+@@ -64,6 +71,7 @@ identify_ramdisk_image(int fd, int start
+ ext2sb = (struct ext2_super_block *) buf;
+ romfsb = (struct romfs_super_block *) buf;
+ cramfsb = (struct cramfs_super *) buf;
++ squashfsb = (struct squashfs_super_block *) buf;
+ memset(buf, 0xe5, size);
+
+ /*
+@@ -82,6 +90,17 @@ identify_ramdisk_image(int fd, int start
+ nblocks = 0;
+ goto done;
+ }
++ /*
++ * handle lzma compressed initrd, returns nblocks=1 as indication
++ */
++ if( buf[0] < 9 * 5 * 5 && buf[9] == 0 && buf[10] == 0 && buf[11] == 0
++ && buf[12] == 0 )
++ {
++ printk( KERN_NOTICE "RAMDISK: LZMA image found at block %d\n",
++ start_block);
++ nblocks = 1; // just a convenient return flag
++ goto done;
++ }
+
+ /* romfs is at block zero too */
+ if (romfsb->word0 == ROMSB_WORD0 &&
+@@ -101,6 +120,18 @@ identify_ramdisk_image(int fd, int start
+ goto done;
+ }
+
++ /* squashfs is at block zero too */
++ if (squashfsb->s_magic == SQUASHFS_MAGIC) {
++ printk(KERN_NOTICE
++ "RAMDISK: squashfs filesystem found at block %d\n",
++ start_block);
++ if (squashfsb->s_major < 3)
++ nblocks = (squashfsb->bytes_used_2+BLOCK_SIZE-1)>>BLOCK_SIZE_BITS;
++ else
++ nblocks = (squashfsb->bytes_used+BLOCK_SIZE-1)>>BLOCK_SIZE_BITS;
++ goto done;
++ }
++
+ /*
+ * Read block 1 to test for minix and ext2 superblock
+ */
+@@ -172,7 +203,22 @@ int __init rd_load_image(char *from)
+ #endif
+ goto done;
+ }
+-
++#ifdef CONFIG_LZMA_INITRD
++ /*
++ * handle lzma compressed image
++ */
++ if ( nblocks == 1 )
++ {
++ nblocks = 0;
++ if ( lzma_rd_load(in_fd, out_fd) == 0 )
++ {
++ printk("\nLZMA initrd loaded successfully\n");
++ goto successful_load;
++ }
++ printk(KERN_NOTICE "LZMA initrd is not in the correct format\n");
++ goto done;
++ }
++#endif
+ /*
+ * NOTE NOTE: nblocks is not actually blocks but
+ * the number of kibibytes of data to load into a ramdisk.
+@@ -393,6 +439,134 @@ static void __init error(char *x)
+ unzip_error = 1;
+ }
+
++#ifdef CONFIG_LZMA_INITRD
++#define _LZMA_IN_CB
++#define _LZMA_OUT_READ
++#include "LzmaDecode.h"
++#include "LzmaDecode.c"
++
++static int read_byte(void *object, const unsigned char **buffer, SizeT *bufferSize);
++
++/*
++ * Do the lzma decompression
++ */
++static int __init lzma_rd_load(int in_fd, int out_fd)
++{
++ unsigned int i;
++ CLzmaDecoderState state;
++ unsigned char* outputbuffer;
++ unsigned int uncompressedSize = 0;
++ unsigned char* p;
++ unsigned int kBlockSize = 0x10000;
++ unsigned int nowPos = 0;
++ unsigned int outsizeProcessed = 0;
++ int res;
++ ILzmaInCallback callback;
++
++ insize = 0; /* valid bytes in inbuf */
++ inptr = 0; /* index of next byte to be processed in inbuf */
++ exit_code = 0;
++ crd_infd = in_fd;
++ inbuf = kmalloc(INBUFSIZ, GFP_KERNEL);
++ if (inbuf == 0)
++ {
++ printk(KERN_ERR "RAMDISK: Couldn't allocate lzma input buffer\n");
++ return -1;
++ }
++
++ callback.Read = read_byte;
++
++ /* lzma args */
++ i = get_byte();
++ state.Properties.lc = i % 9, i = i / 9;
++ state.Properties.lp = i % 5, state.Properties.pb = i / 5;
++
++ /* read dictionary size */
++ p = (char*)&state.Properties.DictionarySize;
++ for (i = 0; i < 4; i++)
++ *p++ = get_byte();
++
++ /* get uncompressedSize */
++ p= (char*)&uncompressedSize;
++ for (i = 0; i < 4; i++)
++ *p++ = get_byte();
++
++ /* skip big file */
++ for (i = 0; i < 4; i++)
++ get_byte();
++
++ printk( KERN_NOTICE "RAMDISK: LZMA lc=%d,lp=%d,pb=%d,dictSize=%d,origSize=%d\n",
++ state.Properties.lc, state.Properties.lp, state.Properties.pb, state.Properties.DictionarySize, uncompressedSize);
++ outputbuffer = kmalloc(kBlockSize, GFP_KERNEL);
++ if (outputbuffer == 0) {
++ printk(KERN_ERR "RAMDISK: Couldn't allocate lzma output buffer\n");
++ return -1;
++ }
++
++ state.Probs = (CProb*)kmalloc( LzmaGetNumProbs(&state.Properties)*sizeof(CProb), GFP_KERNEL);
++ if ( state.Probs == 0) {
++ printk(KERN_ERR "RAMDISK: Couldn't allocate lzma workspace\n");
++ return -1;
++ }
++
++#ifdef CONFIG_LZMA_INITRD_KMALLOC_ONLY
++ state.Dictionary = kmalloc( state.Properties.DictionarySize, GFP_KERNEL);
++#else
++ state.Dictionary = vmalloc( state.Properties.DictionarySize);
++#endif
++ if ( state.Dictionary == 0) {
++ printk(KERN_ERR "RAMDISK: Couldn't allocate lzma dictionary\n");
++ return -1;
++ }
++
++ printk( KERN_NOTICE "LZMA initrd by Ming-Ching Tiew <mctiew@yahoo.com> " );
++
++ LzmaDecoderInit( &state );
++
++ for( nowPos =0; nowPos < uncompressedSize ; )
++ {
++ UInt32 blockSize = uncompressedSize - nowPos;
++ if( blockSize > kBlockSize)
++ blockSize = kBlockSize;
++ res = LzmaDecode( &state, &callback, outputbuffer, blockSize, &outsizeProcessed);
++ if( res != 0 ) {
++ printk( KERN_ERR "RAMDISK: Lzma decode failure\n");
++ return -1;
++ }
++ if( outsizeProcessed == 0 )
++ {
++ uncompressedSize = nowPos;
++ printk( KERN_NOTICE "RAMDISK nowPos=%d, uncompressedSize=%d\n",
++ nowPos, uncompressedSize );
++ break;
++ }
++ sys_write(out_fd, outputbuffer, outsizeProcessed );
++ nowPos += outsizeProcessed;
++ printk( ".");
++ }
++
++#ifdef CONFIG_LZMA_INITRD_KMALLOC_ONLY
++ kfree(state.Dictionary);
++#else
++ vfree(state.Dictionary);
++#endif
++ kfree(inbuf);
++ kfree(outputbuffer);
++ kfree(state.Probs);
++ return 0;
++}
++
++static int read_byte(void *object, const unsigned char **buffer, SizeT *bufferSize)
++{
++ static unsigned char val;
++ *bufferSize = 1;
++ val = get_byte();
++ *buffer = &val;
++ return LZMA_RESULT_OK;
++}
++
++#endif /*CONFIG_LZMA_INITRD*/
++
+ static int __init crd_load(int in_fd, int out_fd)
+ {
+ int result;
+diff -rduNp linux-2.6.22.1.oorig/init/initramfs.c linux-2.6.22.1/init/initramfs.c
+--- linux-2.6.22.1.oorig/init/initramfs.c 2007-07-10 20:56:30.000000000 +0200
++++ linux-2.6.22.1/init/initramfs.c 2007-07-24 14:17:46.000000000 +0200
+@@ -6,6 +6,7 @@
+ #include <linux/delay.h>
+ #include <linux/string.h>
+ #include <linux/syscalls.h>
++#include <linux/vmalloc.h>
+
+ static __initdata char *message;
+ static void __init error(char *x)
+@@ -441,6 +442,118 @@ static void __init flush_window(void)
+ outcnt = 0;
+ }
+
++#ifdef CONFIG_LZMA_INITRAM_FS
++#define _LZMA_IN_CB
++#define _LZMA_OUT_READ
++#include "LzmaDecode.h"
++#ifndef CONFIG_LZMA_INITRD
++ #include "LzmaDecode.c"
++#endif
++static int read_byte(void *object, const unsigned char **buffer, SizeT *bufferSize)
++{
++ static unsigned char val;
++ *bufferSize = 1;
++ val = get_byte();
++ *buffer = &val;
++ return LZMA_RESULT_OK;
++}
++
++static int __init lzma_unzip(void)
++{
++ unsigned int i;
++ CLzmaDecoderState state;
++ unsigned char* outputbuffer;
++ unsigned int uncompressedSize = 0;
++ unsigned char* p;
++ unsigned int kBlockSize = 0x10000;
++ unsigned int nowPos = 0;
++ unsigned int outsizeProcessed = 0;
++ int res;
++ ILzmaInCallback callback;
++
++ callback.Read = read_byte;
++
++ // lzma args
++ i = get_byte();
++ state.Properties.lc = i % 9, i = i / 9;
++ state.Properties.lp = i % 5, state.Properties.pb = i / 5;
++
++ // read dictionary size
++ p = (char*)&state.Properties.DictionarySize;
++ for (i = 0; i < 4; i++)
++ *p++ = get_byte();
++
++ // get uncompressedSize
++ p= (char*)&uncompressedSize;
++ for (i = 0; i < 4; i++)
++ *p++ = get_byte();
++
++ // skip big file
++ for (i = 0; i < 4; i++)
++ get_byte();
++
++ printk( KERN_NOTICE "initramfs: LZMA lc=%d,lp=%d,pb=%d,dictSize=%d,origSize=%d\n",
++ state.Properties.lc,state.Properties.lp,state.Properties.pb,state.Properties.DictionarySize, uncompressedSize);
++ outputbuffer = kmalloc(kBlockSize, GFP_KERNEL);
++ if (outputbuffer == 0) {
++ printk(KERN_ERR "initramfs: Couldn't allocate lzma output buffer\n");
++ return -1;
++ }
++
++ state.Probs = (CProb*) kmalloc( LzmaGetNumProbs(&state.Properties)*sizeof(CProb), GFP_KERNEL);
++ if ( state.Probs == 0) {
++ printk(KERN_ERR "initramfs: Couldn't allocate lzma workspace\n");
++ return -1;
++ }
++
++#ifdef CONFIG_LZMA_INITRAM_FS_KMALLOC_ONLY
++ state.Dictionary = kmalloc( state.Properties.DictionarySize, GFP_KERNEL);
++#else
++ state.Dictionary = vmalloc( state.Properties.DictionarySize);
++#endif
++ if ( state.Dictionary == 0) {
++ printk(KERN_ERR "initramfs: Couldn't allocate lzma dictionary\n");
++ return -1;
++ }
++
++ printk( KERN_NOTICE "LZMA initramfs by Ming-Ching Tiew <mctiew@yahoo.com> " );
++
++ LzmaDecoderInit( &state );
++
++ for( nowPos =0; nowPos < uncompressedSize ; )
++ {
++ UInt32 blockSize = uncompressedSize - nowPos;
++ if( blockSize > kBlockSize)
++ blockSize = kBlockSize;
++ res = LzmaDecode( &state, &callback, outputbuffer, blockSize, &outsizeProcessed);
++ if( res != 0 ) {
++ panic( KERN_ERR "initramfs: Lzma decode failure\n");
++ return -1;
++ }
++ if( outsizeProcessed == 0 )
++ {
++ uncompressedSize = nowPos;
++ printk( KERN_NOTICE "initramfs: nowPos=%d, uncompressedSize=%d\n",
++ nowPos, uncompressedSize );
++ break;
++ }
++ flush_buffer(outputbuffer, outsizeProcessed);
++ nowPos += outsizeProcessed;
++ printk( ".");
++ }
++
++#ifdef CONFIG_LZMA_INITRAM_FS_KMALLOC_ONLY
++ kfree(state.Dictionary);
++#else
++ vfree(state.Dictionary);
++#endif
++ kfree(outputbuffer);
++ kfree(state.Probs);
++ return 0;
++}
++
++#endif /*CONFIG LZMA_INITRAM_FS*/
++
+ static char * __init unpack_to_rootfs(char *buf, unsigned len, int check_only)
+ {
+ int written;
+@@ -475,12 +588,31 @@ static char * __init unpack_to_rootfs(ch
+ inptr = 0;
+ outcnt = 0; /* bytes in output buffer */
+ bytes_out = 0;
+- crc = (ulg)0xffffffffL; /* shift register contents */
+- makecrc();
+- gunzip();
+- if (state != Reset)
++ if( inbuf[0] == 037 && ((inbuf[1] == 0213) || (inbuf[1] == 0236)))
++ {
++ printk( KERN_NOTICE "detected gzip initramfs\n");
++ crc = (ulg)0xffffffffL; /* shift register contents */
++ makecrc();
++ gunzip();
++ if (state != Reset)
+ error("junk in gzipped archive");
+- this_header = saved_offset + inptr;
++ }
++#ifdef CONFIG_LZMA_INITRAM_FS
++ else if( inbuf[0] < 9 * 5 * 5 && buf[9] == 0 && buf[10] == 0
++ && buf[11] == 0 && buf[12] == 0 )
++ {
++ printk( KERN_NOTICE "detected lzma initramfs\n");
++ lzma_unzip();
++ }
++#endif
++ else
++ {
++ // skip forward ?
++ crc = (ulg)0xffffffffL; /* shift register contents */
++ makecrc();
++ gunzip();
++ }
++ this_header = saved_offset + inptr;
+ buf += inptr;
+ len -= inptr;
+ }
+diff -rduNp linux-2.6.22.1.oorig/kernel/Makefile linux-2.6.22.1/kernel/Makefile
+--- linux-2.6.22.1.oorig/kernel/Makefile 2007-07-10 20:56:30.000000000 +0200
++++ linux-2.6.22.1/kernel/Makefile 2007-07-24 14:17:46.000000000 +0200
+@@ -66,7 +66,7 @@ $(obj)/configs.o: $(obj)/config_data.h
+ # config_data.h contains the same information as ikconfig.h but gzipped.
+ # Info from config_data can be extracted from /proc/config*
+ targets += config_data.gz
+-$(obj)/config_data.gz: .config FORCE
++$(obj)/config_data.gz: .miniconfig FORCE
+ $(call if_changed,gzip)
+
+ quiet_cmd_ikconfiggz = IKCFG $@
+diff -rduNp linux-2.6.22.1.oorig/kernel/configs.c linux-2.6.22.1/kernel/configs.c
+--- linux-2.6.22.1.oorig/kernel/configs.c 2007-07-10 20:56:30.000000000 +0200
++++ linux-2.6.22.1/kernel/configs.c 2007-07-24 14:17:46.000000000 +0200
+@@ -79,7 +79,7 @@ static int __init ikconfig_init(void)
+ struct proc_dir_entry *entry;
+
+ /* create the current config file */
+- entry = create_proc_entry("config.gz", S_IFREG | S_IRUGO,
++ entry = create_proc_entry("miniconfig.gz", S_IFREG | S_IRUGO,
+ &proc_root);
+ if (!entry)
+ return -ENOMEM;
+@@ -95,7 +95,7 @@ static int __init ikconfig_init(void)
+
+ static void __exit ikconfig_cleanup(void)
+ {
+- remove_proc_entry("config.gz", &proc_root);
++ remove_proc_entry("miniconfig.gz", &proc_root);
+ }
+
+ module_init(ikconfig_init);
+diff -rduNp linux-2.6.22.1.oorig/kernel/time/clocksource.c linux-2.6.22.1/kernel/time/clocksource.c
+--- linux-2.6.22.1.oorig/kernel/time/clocksource.c 2007-07-10 20:56:30.000000000 +0200
++++ linux-2.6.22.1/kernel/time/clocksource.c 2007-07-24 14:17:46.000000000 +0200
+@@ -87,8 +87,8 @@ static void clocksource_ratewd(struct cl
+ if (delta > -WATCHDOG_THRESHOLD && delta < WATCHDOG_THRESHOLD)
+ return;
+
+- printk(KERN_WARNING "Clocksource %s unstable (delta = %Ld ns)\n",
+- cs->name, delta);
++/* printk(KERN_WARNING "Clocksource %s unstable (delta = %Ld ns)\n",
++ cs->name, delta); */
+ cs->flags &= ~(CLOCK_SOURCE_VALID_FOR_HRES | CLOCK_SOURCE_WATCHDOG);
+ clocksource_change_rating(cs, 0);
+ cs->flags &= ~CLOCK_SOURCE_WATCHDOG;
+diff -rduNp linux-2.6.22.1.oorig/miniconfig.sh linux-2.6.22.1/miniconfig.sh
+--- linux-2.6.22.1.oorig/miniconfig.sh 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/miniconfig.sh 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,2 @@
++#!/bin/sh -f
++make allnoconfig KCONFIG_ALLCONFIG=.miniconfig
+diff -rduNp linux-2.6.22.1.oorig/scripts/Makefile.lib linux-2.6.22.1/scripts/Makefile.lib
+--- linux-2.6.22.1.oorig/scripts/Makefile.lib 2007-07-10 20:56:30.000000000 +0200
++++ linux-2.6.22.1/scripts/Makefile.lib 2007-07-24 14:17:46.000000000 +0200
+@@ -162,4 +162,9 @@ cmd_objcopy = $(OBJCOPY) $(OBJCOPYFLAGS)
+ quiet_cmd_gzip = GZIP $@
+ cmd_gzip = gzip -f -9 < $< > $@
+
++# LZMA
++#
++quiet_cmd_lzma = LZMA $@
++cmd_lzma = lzma e $< $@ -lc7 -lp0 -pb0 2>/dev/null
++
+
+diff -rduNp linux-2.6.22.1.oorig/scripts/gen_lzma_initramfs_list.sh linux-2.6.22.1/scripts/gen_lzma_initramfs_list.sh
+--- linux-2.6.22.1.oorig/scripts/gen_lzma_initramfs_list.sh 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/scripts/gen_lzma_initramfs_list.sh 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,292 @@
++#!/bin/bash
++# Copyright (C) Martin Schlemmer <azarah@nosferatu.za.org>
++# Copyright (c) 2006 Sam Ravnborg <sam@ravnborg.org>
++#
++# Released under the terms of the GNU GPL
++#
++# Generate a cpio packed initramfs. It uses gen_init_cpio to generate
++# the cpio archive, and gzip to pack it.
++# The script may also be used to generate the inputfile used for gen_init_cpio
++# This script assumes that gen_init_cpio is located in usr/ directory
++
++# error out on errors
++set -e
++
++usage() {
++cat << EOF
++Usage:
++$0 [-o <file>] [-u <uid>] [-g <gid>] { -s | -d | <cpio_source>} ...
++ -o <file> Create lzma initramfs file named <file> using
++ gen_init_cpio and lzma
++ -u <uid> User ID to map to user ID 0 (root).
++ <uid> is only meaningful if <cpio_source>
++ is a directory.
++ -g <gid> Group ID to map to group ID 0 (root).
++ <gid> is only meaningful if <cpio_source>
++ is a directory.
++ <cpio_source> File list or directory for cpio archive.
++ If <cpio_source> is a .cpio file it will be used
++ as direct input to initramfs.
++ -s Create lzma file with small dictionary size
++ -d Output the default cpio list.
++
++All options except -o and -l may be repeated and are interpreted
++sequentially and immediately. -u and -g states are preserved across
++<cpio_source> options so an explicit "-u 0 -g 0" is required
++to reset the root/group mapping.
++EOF
++}
++
++list_default_initramfs() {
++ # echo usr/kinit/kinit
++ :
++}
++
++default_initramfs() {
++ cat <<-EOF >> ${output}
++ # This is a very simple, default initramfs
++
++ dir /dev 0755 0 0
++ nod /dev/console 0600 0 0 c 5 1
++ dir /root 0700 0 0
++ # file /kinit usr/kinit/kinit 0755 0 0
++ # slink /init kinit 0755 0 0
++ EOF
++}
++
++filetype() {
++ local argv1="$1"
++
++ # symlink test must come before file test
++ if [ -L "${argv1}" ]; then
++ echo "slink"
++ elif [ -f "${argv1}" ]; then
++ echo "file"
++ elif [ -d "${argv1}" ]; then
++ echo "dir"
++ elif [ -b "${argv1}" -o -c "${argv1}" ]; then
++ echo "nod"
++ elif [ -p "${argv1}" ]; then
++ echo "pipe"
++ elif [ -S "${argv1}" ]; then
++ echo "sock"
++ else
++ echo "invalid"
++ fi
++ return 0
++}
++
++list_print_mtime() {
++ :
++}
++
++print_mtime() {
++ local my_mtime="0"
++
++ if [ -e "$1" ]; then
++ my_mtime=$(find "$1" -printf "%T@\n" | sort -r | head -n 1)
++ fi
++
++ echo "# Last modified: ${my_mtime}" >> ${output}
++ echo "" >> ${output}
++}
++
++list_parse() {
++ echo "$1 \\"
++}
++
++# for each file print a line in following format
++# <filetype> <name> <path to file> <octal mode> <uid> <gid>
++# for links, devices etc the format differs. See gen_init_cpio for details
++parse() {
++ local location="$1"
++ local name="${location/${srcdir}//}"
++ # change '//' into '/'
++ name="${name//\/\///}"
++ local mode="$2"
++ local uid="$3"
++ local gid="$4"
++ local ftype=$(filetype "${location}")
++ # remap uid/gid to 0 if necessary
++ [ "$uid" -eq "$root_uid" ] && uid=0
++ [ "$gid" -eq "$root_gid" ] && gid=0
++ local str="${mode} ${uid} ${gid}"
++
++ [ "${ftype}" == "invalid" ] && return 0
++ [ "${location}" == "${srcdir}" ] && return 0
++
++ case "${ftype}" in
++ "file")
++ str="${ftype} ${name} ${location} ${str}"
++ ;;
++ "nod")
++ local dev_type=
++ local maj=$(LC_ALL=C ls -l "${location}" | \
++ gawk '{sub(/,/, "", $5); print $5}')
++ local min=$(LC_ALL=C ls -l "${location}" | \
++ gawk '{print $6}')
++
++ if [ -b "${location}" ]; then
++ dev_type="b"
++ else
++ dev_type="c"
++ fi
++ str="${ftype} ${name} ${str} ${dev_type} ${maj} ${min}"
++ ;;
++ "slink")
++ local target=$(LC_ALL=C ls -l "${location}" | \
++ gawk '{print $11}')
++ str="${ftype} ${name} ${target} ${str}"
++ ;;
++ *)
++ str="${ftype} ${name} ${str}"
++ ;;
++ esac
++
++ echo "${str}" >> ${output}
++
++ return 0
++}
++
++unknown_option() {
++ printf "ERROR: unknown option \"$arg\"\n" >&2
++ printf "If the filename validly begins with '-', " >&2
++ printf "then it must be prefixed\n" >&2
++ printf "by './' so that it won't be interpreted as an option." >&2
++ printf "\n" >&2
++ usage >&2
++ exit 1
++}
++
++list_header() {
++ :
++}
++
++header() {
++ printf "\n#####################\n# $1\n" >> ${output}
++}
++
++# process one directory (incl sub-directories)
++dir_filelist() {
++ ${dep_list}header "$1"
++
++ srcdir=$(echo "$1" | sed -e 's://*:/:g')
++ dirlist=$(find "${srcdir}" -printf "%p %m %U %G\n" 2>/dev/null)
++
++ # If $dirlist is only one line, then the directory is empty
++ if [ "$(echo "${dirlist}" | wc -l)" -gt 1 ]; then
++ ${dep_list}print_mtime "$1"
++
++ echo "${dirlist}" | \
++ while read x; do
++ ${dep_list}parse ${x}
++ done
++ fi
++}
++
++# if only one file is specified and it is .cpio file then use it direct as fs
++# if a directory is specified then add all files in given direcotry to fs
++# if a regular file is specified assume it is in gen_initramfs format
++input_file() {
++ source="$1"
++ if [ -f "$1" ]; then
++ ${dep_list}header "$1"
++ is_cpio="$(echo "$1" | sed 's/^.*\.cpio/cpio/')"
++ if [ $2 -eq 0 -a ${is_cpio} == "cpio" ]; then
++ cpio_file=$1
++ [ ! -z ${dep_list} ] && echo "$1"
++ return 0
++ fi
++ if [ -z ${dep_list} ]; then
++ print_mtime "$1" >> ${output}
++ cat "$1" >> ${output}
++ else
++ cat "$1" | while read type dir file perm ; do
++ if [ "$type" == "file" ]; then
++ echo "$file \\";
++ fi
++ done
++ fi
++ elif [ -d "$1" ]; then
++ dir_filelist "$1"
++ else
++ echo " ${prog}: Cannot open '$1'" >&2
++ exit 1
++ fi
++}
++
++prog=$0
++root_uid=0
++root_gid=0
++dep_list=
++cpio_file=
++cpio_list=
++output="/dev/stdout"
++output_file=""
++opt=""
++
++arg="$1"
++case "$arg" in
++ "-l") # files included in initramfs - used by kbuild
++ dep_list="list_"
++ echo "deps_initramfs := \\"
++ shift
++ ;;
++ "-o") # generate lzma-ed cpio image named $1
++ shift
++ output_file="$1"
++ cpio_list="$(mktemp ${TMPDIR:-/tmp}/cpiolist.XXXXXX)"
++ output=${cpio_list}
++ shift
++ ;;
++esac
++while [ $# -gt 0 ]; do
++ arg="$1"
++ shift
++ case "$arg" in
++ "-u") # map $1 to uid=0 (root)
++ root_uid="$1"
++ shift
++ ;;
++ "-g") # map $1 to gid=0 (root)
++ root_gid="$1"
++ shift
++ ;;
++ "-s")
++ opt="-d16"
++ ;;
++ "-d") # display default initramfs list
++ default_list="$arg"
++ ${dep_list}default_initramfs
++ ;;
++ "-h")
++ usage
++ exit 0
++ ;;
++ *)
++ case "$arg" in
++ "-"*)
++ unknown_option
++ ;;
++ *) # input file/dir - process it
++ input_file "$arg" "$#"
++ ;;
++ esac
++ ;;
++ esac
++done
++
++# If output_file is set we will generate cpio archive and lzma it
++# we are carefull to delete tmp files
++if [ ! -z ${output_file} ]; then
++ if [ -z ${cpio_file} ]; then
++ cpio_tfile="$(mktemp ${TMPDIR:-/tmp}/cpiofile.XXXXXX)"
++ usr/gen_init_cpio ${cpio_list} > ${cpio_tfile}
++ else
++ cpio_tfile=${cpio_file}
++ fi
++ rm ${cpio_list}
++ lzma e ${cpio_tfile} ${output_file} ${opt}
++ [ -z ${cpio_file} ] && rm ${cpio_tfile}
++fi
++exit 0
+diff -rduNp linux-2.6.22.1.oorig/shrinkconfig.sh linux-2.6.22.1/shrinkconfig.sh
+--- linux-2.6.22.1.oorig/shrinkconfig.sh 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.22.1/shrinkconfig.sh 2007-07-24 14:17:46.000000000 +0200
+@@ -0,0 +1,79 @@
++#! /bin/bash
++
++# shrinkconfig copyright 2006 by Rob Landley <rob@landley.net>
++# Licensed under the GNU General Public License version 2.
++
++if [ $# -ne 1 ]
++then
++ echo "Turns current .config into a miniconfig file."
++ echo "Usage: shrinkconfig mini.config"
++ exit 1
++fi
++
++if [ ! -f .config ]
++then
++ echo "Need a .config file to shrink."
++ exit 1
++fi
++LENGTH=$(wc -l < .config)
++
++OUTPUT="$1"
++cp .config "$OUTPUT"
++if [ $? -ne 0 ]
++then
++ echo "Couldn't create $OUTPUT"
++ exit 1
++fi
++
++# If we get interrupted, clean up the mess
++
++KERNELOUTPUT=""
++
++function cleanup
++{
++ echo
++ echo "Interrupted."
++ [ ! -z "$KERNELOUTPUT" ] && rm -rf "$KERNELOUTPUT"
++ rm "$OUTPUT"
++ exit 1
++}
++
++trap cleanup HUP INT QUIT TERM
++
++# Since the "O=" argument to make doesn't work recursively, we need to jump
++# through a few hoops to avoid overwriting the .config that we're shrinking.
++
++# If we're building out of tree, we'll have absolute paths to source and build
++# directories in the Makefile.
++
++KERNELSRC=$(sed -n -e 's/KERNELSRC[^/]*:=[^/]*//p' Makefile)
++[ -z "$KERNELSRC" ] && KERNELSRC=$(pwd)
++KERNELOUTPUT=`pwd`/.config.minitemp
++
++mkdir -p "$KERNELOUTPUT" || exit 1
++
++echo "Shrinking .config to $OUTPUT..."
++
++for I in $(seq 1 $LENGTH)
++do
++ echo -n -e "\r"$I/$LENGTH lines $(wc -c < "$OUTPUT") bytes
++
++ sed -n "${I}!p" "$OUTPUT" > "$KERNELOUTPUT"/.config.test
++ # Do a config with this file
++ make -C "$KERNELSRC" O="$KERNELOUTPUT" allnoconfig KCONFIG_ALLCONFIG="$KERNELOUTPUT"/.config.test > /dev/null
++
++ # Compare. The date changes, so expect a small difference each time.
++ D=$(diff "$KERNELOUTPUT"/.config .config | wc -l)
++ if [ $D -eq 4 ]
++ then
++ mv "$KERNELOUTPUT"/.config.test "$OUTPUT"
++ LENGTH=$[$LENGTH-1]
++ else
++ I=$[$I + 1]
++ fi
++done
++
++rm -rf "$KERNELOUTPUT"
++
++# One extra echo to preserve status line.
++echo
+diff -rduNp linux-2.6.22.1.oorig/usr/Makefile linux-2.6.22.1/usr/Makefile
+--- linux-2.6.22.1.oorig/usr/Makefile 2007-07-10 20:56:30.000000000 +0200
++++ linux-2.6.22.1/usr/Makefile 2007-07-24 14:17:46.000000000 +0200
+@@ -19,6 +19,7 @@ $(obj)/initramfs_data.o: $(obj)/initramf
+
+ hostprogs-y := gen_init_cpio
+ initramfs := $(CONFIG_SHELL) $(srctree)/scripts/gen_initramfs_list.sh
++lzma_initramfs := $(CONFIG_SHELL) $(srctree)/scripts/gen_lzma_initramfs_list.sh
+ ramfs-input := $(if $(filter-out "",$(CONFIG_INITRAMFS_SOURCE)), \
+ $(shell echo $(CONFIG_INITRAMFS_SOURCE)),-d)
+ ramfs-args := \
+@@ -36,6 +37,14 @@ endif
+ quiet_cmd_initfs = GEN $@
+ cmd_initfs = $(initramfs) -o $@ $(ramfs-args) $(ramfs-input)
+
++ifdef CONFIG_LZMA_INITRAM_FS_SMALLMEM
++quiet_cmd_lzma_initfs = LZRAMFS $@
++ cmd_lzma_initfs = $(lzma_initramfs) -o $@ $(ramfs-args) -s $(ramfs-input)
++else
++quiet_cmd_lzma_initfs = LZRAMFS $@
++ cmd_lzma_initfs = $(lzma_initramfs) -o $@ $(ramfs-args) $(ramfs-input)
++endif
++
+ targets := initramfs_data.cpio.gz
+ # do not try to update files included in initramfs
+ $(deps_initramfs): ;
+@@ -48,5 +57,9 @@ $(deps_initramfs): klibcdirs
+ # 4) arguments to gen_initramfs.sh changes
+ $(obj)/initramfs_data.cpio.gz: $(obj)/gen_init_cpio $(deps_initramfs) klibcdirs
+ $(Q)$(initramfs) -l $(ramfs-input) > $(obj)/.initramfs_data.cpio.gz.d
++ifdef CONFIG_LZMA_INITRAM_FS
++ $(call if_changed,lzma_initfs)
++else
+ $(call if_changed,initfs)
++endif
+
diff --git a/toolchain/kernel-headers/lzma/linux-2.6.22.1-002-lzma-vmlinuz.01.patch b/toolchain/kernel-headers/lzma/linux-2.6.22.1-002-lzma-vmlinuz.01.patch
new file mode 100644
index 000000000..05361ff9d
--- /dev/null
+++ b/toolchain/kernel-headers/lzma/linux-2.6.22.1-002-lzma-vmlinuz.01.patch
@@ -0,0 +1,54 @@
+diff -rdup linux-2.6.21.5.oorig/arch/i386/boot/compressed/Makefile linux-2.6.21.5/arch/i386/boot/compressed/Makefile
+--- linux-2.6.21.5.oorig/arch/i386/boot/compressed/Makefile 2007-07-24 13:08:51.000000000 +0200
++++ linux-2.6.21.5/arch/i386/boot/compressed/Makefile 2007-07-24 14:54:38.000000000 +0200
+@@ -4,7 +4,7 @@
+ # create a compressed vmlinux image from the original vmlinux
+ #
+
+-tragets := head.o lzma_misc.o piggy.o \
++targets := head.o lzma_misc.o piggy.o \
+ vmlinux.bin.all vmlinux.relocs \
+ vmlinux vmlinux.bin vmlinux.bin.gz
+ EXTRA_AFLAGS := -traditional
+diff -rdup linux-2.6.21.5.oorig/scripts/gen_lzma_initramfs_list.sh linux-2.6.21.5/scripts/gen_lzma_initramfs_list.sh
+--- linux-2.6.21.5.oorig/scripts/gen_lzma_initramfs_list.sh 2007-07-24 13:08:51.000000000 +0200
++++ linux-2.6.21.5/scripts/gen_lzma_initramfs_list.sh 2007-07-24 15:12:10.000000000 +0200
+@@ -253,7 +253,7 @@ while [ $# -gt 0 ]; do
+ shift
+ ;;
+ "-s")
+- opt="-d16"
++ #opt="-d16" ? what was that supposed to do?
+ ;;
+ "-d") # display default initramfs list
+ default_list="$arg"
+@@ -286,7 +286,7 @@ if [ ! -z ${output_file} ]; then
+ cpio_tfile=${cpio_file}
+ fi
+ rm ${cpio_list}
+- lzma e ${cpio_tfile} ${output_file} ${opt}
++ lzma -z ${cpio_tfile} ${opt} -c > ${output_file}
+ [ -z ${cpio_file} ] && rm ${cpio_tfile}
+ fi
+ exit 0
+--- linux-2.6.21.5.oorig/arch/i386/boot/compressed/lzma_misc.c 2007-07-24 15:24:44.000000000 +0200
++++ linux-2.6.21.5/arch/i386/boot/compressed/lzma_misc.c 2007-07-24 17:09:40.000000000 +0200
+@@ -241,7 +241,6 @@ static int lzma_unzip(uch* output)
+
+ static int read_byte(void *object, const unsigned char **buffer, SizeT *bufferSize)
+ {
+- static unsigned int i = 0;
+ static unsigned char val;
+ *bufferSize = 1;
+ val = get_byte();
+--- linux-2.6.21.5.oorig/scripts/Makefile.lib 2007-07-24 15:24:44.000000000 +0200
++++ linux-2.6.21.5/scripts/Makefile.lib 2007-07-24 18:03:57.000000000 +0200
+@@ -165,6 +165,7 @@ cmd_gzip = gzip -f -9 < $< > $@
+ # LZMA
+ #
+ quiet_cmd_lzma = LZMA $@
+-cmd_lzma = lzma e $< $@ -lc7 -lp0 -pb0 2>/dev/null
++#cmd_lzma = lzma e $< $@ -lc7 -lp0 -pb0 2>/dev/null
++cmd_lzma = lzma -z $< -c > $@
+
+