| 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 |
| |