diff options
author | Peter Korsgaard <jacmet@sunsite.dk> | 2009-02-25 14:26:03 +0000 |
---|---|---|
committer | Peter Korsgaard <jacmet@sunsite.dk> | 2009-02-25 14:26:03 +0000 |
commit | 8259f1529a6190db675a1b6a27f33096ee5b0bc9 (patch) | |
tree | 4f2118526938f50fec0eba8e2bfe9d302059fdb2 /toolchain/kernel-headers/lzma/linux-2.6.21.5-001-lzma-vmlinuz.00.patch | |
parent | 895cf9e27daccf79b5af5eb473f86cca76875122 (diff) |
kernel-headers: remove 2.6.20-22 variants and outdated impi/lzma patches
Diffstat (limited to 'toolchain/kernel-headers/lzma/linux-2.6.21.5-001-lzma-vmlinuz.00.patch')
-rw-r--r-- | toolchain/kernel-headers/lzma/linux-2.6.21.5-001-lzma-vmlinuz.00.patch | 27017 |
1 files changed, 0 insertions, 27017 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 deleted file mode 100644 index 87e50f8be..000000000 --- a/toolchain/kernel-headers/lzma/linux-2.6.21.5-001-lzma-vmlinuz.00.patch +++ /dev/null @@ -1,27017 +0,0 @@ -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 - |