summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWaldemar Brodkorb <wbx@openadk.org>2010-02-17 00:25:00 +0100
committerWaldemar Brodkorb <wbx@openadk.org>2010-02-17 00:25:00 +0100
commit2e96c40f5529285d113f15275ac2e7214aebd3fe (patch)
tree903b15a9e4b3c2078d81fd3c32a6024d82917690
parent0d0d6116a6a733a35dd2e8651988bf5ae2b172b6 (diff)
boot via nfs works and I get a shell
-rw-r--r--target/Config.in4
-rw-r--r--target/foxg20/Makefile2
-rw-r--r--target/foxg20/kernel.config984
-rw-r--r--target/foxg20/patches/foxg20.patch4730
-rw-r--r--target/linux/patches/2.6.32.2/aufs2.patch24455
-rw-r--r--target/linux/patches/2.6.32.2/bsd-compatibility.patch2513
-rw-r--r--target/linux/patches/2.6.32.2/cc-abstract.patch14
-rw-r--r--target/linux/patches/2.6.32.2/cris-initrd.patch14
-rw-r--r--target/linux/patches/2.6.32.2/cygwin-compat.patch14
-rw-r--r--target/linux/patches/2.6.32.2/exmap.patch11
-rw-r--r--target/linux/patches/2.6.32.2/freebsd-compat.patch11
-rw-r--r--target/linux/patches/2.6.32.2/mtd-root.patch62
-rw-r--r--target/linux/patches/2.6.32.2/ocf.patch23653
-rw-r--r--target/linux/patches/2.6.32.2/scsi-header.patch41
-rw-r--r--target/linux/patches/2.6.32.2/startup.patch20
-rw-r--r--target/linux/patches/2.6.32.2/swconfig.patch1075
-rw-r--r--target/linux/patches/2.6.32.2/yaffs2.patch15066
17 files changed, 72422 insertions, 247 deletions
diff --git a/target/Config.in b/target/Config.in
index 61d602e4b..8bf1378ec 100644
--- a/target/Config.in
+++ b/target/Config.in
@@ -228,6 +228,10 @@ config ADK_LINUX_ARM_FOXBOARD
select ADK_EABI
select ADK_KERNEL_NLS
select ADK_PACKAGE_KMOD_USB_CONTROLLER
+ select ADK_KERNEL_NETDEVICES
+ select ADK_KERNEL_NET_PCI
+ select ADK_KERNEL_NET_ETHERNET
+ select ADK_KERNEL_MII
select ADK_TARGET_WITH_USB
select ADK_TARGET_WITH_WATCHDOG
help
diff --git a/target/foxg20/Makefile b/target/foxg20/Makefile
index 8d302a15a..b59d668d1 100644
--- a/target/foxg20/Makefile
+++ b/target/foxg20/Makefile
@@ -8,6 +8,8 @@ include $(TOPDIR)/mk/kernel-build.mk
include $(TOPDIR)/mk/image.mk
kernel-install:
+ @cp $(LINUX_DIR)/uImage \
+ $(BIN_DIR)/${ADK_TARGET}-${FS}-kernel
ifeq ($(FS),nfsroot)
imageinstall: ${BIN_DIR}/${ROOTFSUSERTARBALL}
diff --git a/target/foxg20/kernel.config b/target/foxg20/kernel.config
index b99fb3403..646d1342c 100644
--- a/target/foxg20/kernel.config
+++ b/target/foxg20/kernel.config
@@ -1,21 +1,25 @@
#
# Automatically generated make config: don't edit
-# Linux kernel version: 2.6.32
-# Mon Feb 1 19:21:16 2010
-#
-CONFIG_MMU=y
-CONFIG_ZONE_DMA=y
+# Linux kernel version: 2.6.32.2
+# Wed Feb 17 00:21:32 2010
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_GENERIC_GPIO=y
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_PROBE=y
CONFIG_RWSEM_GENERIC_SPINLOCK=y
-CONFIG_GENERIC_IOMAP=y
-# CONFIG_ARCH_HAS_ILOG2_U32 is not set
-# CONFIG_ARCH_HAS_ILOG2_U64 is not set
-CONFIG_GENERIC_FIND_NEXT_BIT=y
CONFIG_GENERIC_HWEIGHT=y
CONFIG_GENERIC_CALIBRATE_DELAY=y
-CONFIG_NO_IOPORT=y
-CONFIG_FORCE_MAX_ZONEORDER=6
-CONFIG_CRIS=y
-CONFIG_HZ=100
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
+CONFIG_VECTORS_BASE=0xffff0000
CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
CONFIG_CONSTRUCTORS=y
@@ -27,6 +31,12 @@ CONFIG_BROKEN_ON_SMP=y
CONFIG_INIT_ENV_ARG_LIMIT=32
CONFIG_LOCALVERSION=""
# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_HAVE_KERNEL_GZIP=y
+CONFIG_HAVE_KERNEL_BZIP2=y
+CONFIG_HAVE_KERNEL_LZMA=y
+CONFIG_KERNEL_GZIP=y
+# CONFIG_KERNEL_BZIP2 is not set
+# CONFIG_KERNEL_LZMA is not set
# CONFIG_SWAP is not set
CONFIG_SYSVIPC=y
CONFIG_SYSVIPC_SYSCTL=y
@@ -51,18 +61,23 @@ CONFIG_LOG_BUF_SHIFT=14
# CONFIG_SYSFS_DEPRECATED_V2 is not set
# CONFIG_RELAY is not set
# CONFIG_NAMESPACES is not set
-# CONFIG_BLK_DEV_INITRD is not set
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_RD_GZIP=y
+# CONFIG_RD_BZIP2 is not set
+# CONFIG_RD_LZMA is not set
CONFIG_CC_OPTIMIZE_FOR_SIZE=y
CONFIG_SYSCTL=y
CONFIG_ANON_INODES=y
CONFIG_EMBEDDED=y
CONFIG_UID16=y
CONFIG_SYSCTL_SYSCALL=y
-# CONFIG_KALLSYMS is not set
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
CONFIG_HOTPLUG=y
CONFIG_PRINTK=y
CONFIG_BUG=y
-# CONFIG_ELF_CORE is not set
+CONFIG_ELF_CORE=y
CONFIG_BASE_FULL=y
CONFIG_FUTEX=y
CONFIG_EPOLL=y
@@ -70,7 +85,7 @@ CONFIG_SIGNALFD=y
CONFIG_TIMERFD=y
CONFIG_EVENTFD=y
CONFIG_SHMEM=y
-# CONFIG_AIO is not set
+CONFIG_AIO=y
#
# Kernel Performance Events And Counters
@@ -81,12 +96,17 @@ CONFIG_SLAB=y
# CONFIG_SLUB is not set
# CONFIG_SLOB is not set
# CONFIG_PROFILING is not set
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_HAVE_CLK=y
#
# GCOV-based kernel profiling
#
# CONFIG_SLOW_WORK is not set
-# CONFIG_HAVE_GENERIC_DMA_COHERENT is not set
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
CONFIG_SLABINFO=y
CONFIG_RT_MUTEXES=y
CONFIG_BASE_SMALL=0
@@ -116,21 +136,147 @@ CONFIG_DEFAULT_IOSCHED="noop"
# CONFIG_FREEZER is not set
#
-# General setup
+# System Type
#
-CONFIG_BINFMT_ELF=y
-# CONFIG_HAVE_AOUT is not set
-# CONFIG_BINFMT_MISC is not set
-CONFIG_GENERIC_HARDIRQS=y
-CONFIG_ETRAX_CMDLINE="console=ttyS0,115200"
-CONFIG_ETRAX_WATCHDOG=y
-# CONFIG_ETRAX_WATCHDOG_NICE_DOGGY is not set
-CONFIG_ETRAX_FAST_TIMER=y
-# CONFIG_ETRAX_KMALLOCED_MODULES is not set
-# CONFIG_OOM_REBOOT is not set
+CONFIG_MMU=y
+# CONFIG_ARCH_AAEC2000 is not set
+# CONFIG_ARCH_INTEGRATOR is not set
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_VERSATILE is not set
+CONFIG_ARCH_AT91=y
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_GEMINI is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_STMP3XXX is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_H720X is not set
+# CONFIG_ARCH_NOMADIK is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP23XX is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_L7200 is not set
+# CONFIG_ARCH_KIRKWOOD is not set
+# CONFIG_ARCH_LOKI is not set
+# CONFIG_ARCH_MV78XX0 is not set
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_MMP is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_NS9XXX is not set
+# CONFIG_ARCH_W90X900 is not set
+# CONFIG_ARCH_PNX4008 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_MSM is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_S3C64XX is not set
+# CONFIG_ARCH_S5PC1XX is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_U300 is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+# CONFIG_ARCH_BCMRING is not set
+
+#
+# Atmel AT91 System-on-Chip
+#
+# CONFIG_ARCH_AT91RM9200 is not set
+# CONFIG_ARCH_AT91SAM9260 is not set
+# CONFIG_ARCH_AT91SAM9261 is not set
+# CONFIG_ARCH_AT91SAM9G10 is not set
+# CONFIG_ARCH_AT91SAM9263 is not set
+# CONFIG_ARCH_AT91SAM9RL is not set
+CONFIG_ARCH_AT91SAM9G20=y
+# CONFIG_ARCH_AT91SAM9G45 is not set
+# CONFIG_ARCH_AT91CAP9 is not set
+# CONFIG_ARCH_AT91X40 is not set
+CONFIG_AT91_PMC_UNIT=y
+
+#
+# AT91SAM9G20 Board Type
+#
+# CONFIG_MACH_AT91SAM9G20EK is not set
+# CONFIG_MACH_AT91SAM9G20EK_2MMC is not set
+# CONFIG_MACH_CPU9G20 is not set
+CONFIG_MACH_FOXG20=y
+
+#
+# AT91 Board Options
+#
+# CONFIG_MTD_AT91_DATAFLASH_CARD is not set
+# CONFIG_MTD_NAND_ATMEL_BUSWIDTH_16 is not set
+
+#
+# AT91 Feature Selections
+#
+CONFIG_AT91_PROGRAMMABLE_CLOCKS=y
+CONFIG_AT91_TIMER_HZ=100
+# CONFIG_AT91_EARLY_DBGU is not set
+CONFIG_AT91_EARLY_USART0=y
+# CONFIG_AT91_EARLY_USART1 is not set
+# CONFIG_AT91_EARLY_USART2 is not set
+# CONFIG_AT91_EARLY_USART3 is not set
+# CONFIG_AT91_EARLY_USART4 is not set
+# CONFIG_AT91_EARLY_USART5 is not set
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+CONFIG_CPU_ARM926T=y
+CONFIG_CPU_32v5=y
+CONFIG_CPU_ABRT_EV5TJ=y
+CONFIG_CPU_PABRT_LEGACY=y
+CONFIG_CPU_CACHE_VIVT=y
+CONFIG_CPU_COPY_V4WB=y
+CONFIG_CPU_TLB_V4WBI=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+
+#
+# Processor Features
+#
+CONFIG_ARM_THUMB=y
+# CONFIG_CPU_ICACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_WRITETHROUGH is not set
+# CONFIG_CPU_CACHE_ROUND_ROBIN is not set
+CONFIG_ARM_L1_CACHE_SHIFT=5
+
+#
+# Bus support
+#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+# CONFIG_PCCARD is not set
+
+#
+# Kernel Features
+#
+CONFIG_TICK_ONESHOT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_VMSPLIT_3G=y
+# CONFIG_VMSPLIT_2G is not set
+# CONFIG_VMSPLIT_1G is not set
+CONFIG_PAGE_OFFSET=0xC0000000
CONFIG_PREEMPT_NONE=y
# CONFIG_PREEMPT_VOLUNTARY is not set
# CONFIG_PREEMPT is not set
+CONFIG_HZ=100
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
+# CONFIG_HIGHMEM is not set
CONFIG_SELECT_MEMORY_MODEL=y
CONFIG_FLATMEM_MANUAL=y
# CONFIG_DISCONTIGMEM_MANUAL is not set
@@ -138,67 +284,58 @@ CONFIG_FLATMEM_MANUAL=y
CONFIG_FLATMEM=y
CONFIG_FLAT_NODE_MEM_MAP=y
CONFIG_PAGEFLAGS_EXTENDED=y
-CONFIG_SPLIT_PTLOCK_CPUS=4
+CONFIG_SPLIT_PTLOCK_CPUS=4096
# CONFIG_PHYS_ADDR_T_64BIT is not set
-CONFIG_ZONE_DMA_FLAG=1
-CONFIG_BOUNCE=y
+CONFIG_ZONE_DMA_FLAG=0
CONFIG_VIRT_TO_BUS=y
CONFIG_HAVE_MLOCK=y
CONFIG_HAVE_MLOCKED_PAGE_BIT=y
# CONFIG_KSM is not set
CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
+CONFIG_LEDS=y
+CONFIG_LEDS_CPU=y
+CONFIG_ALIGNMENT_TRAP=y
+# CONFIG_UACCESS_WITH_MEMCPY is not set
#
-# Hardware setup
-#
-# CONFIG_ETRAX100LX is not set
-CONFIG_ETRAX100LX_V2=y
-# CONFIG_SVINTO_SIM is not set
-# CONFIG_ETRAXFS is not set
-# CONFIG_CRIS_MACH_ARTPEC3 is not set
-# CONFIG_ETRAX_VCS_SIM is not set
-CONFIG_ETRAX_ARCH_V10=y
-# CONFIG_ETRAX_ARCH_V32 is not set
-CONFIG_ETRAX_DRAM_SIZE=16
-CONFIG_ETRAX_MTD_SIZE=0x00800000
-CONFIG_ETRAX_FLASH_BUSWIDTH=2
-CONFIG_ETRAX_NANDFLASH_BUSWIDTH=1
-CONFIG_ETRAX_FLASH1_SIZE=0
-# CONFIG_ETRAX_DEBUG_PORT0 is not set
-# CONFIG_ETRAX_DEBUG_PORT1 is not set
-# CONFIG_ETRAX_DEBUG_PORT2 is not set
-# CONFIG_ETRAX_DEBUG_PORT3 is not set
-CONFIG_ETRAX_DEBUG_PORT_NULL=y
-
-#
-# CRIS v10 options
-#
-CONFIG_ETRAX_DRAM_VIRTUAL_BASE=c0000000
-CONFIG_ETRAX_PA_LEDS=y
-# CONFIG_ETRAX_PB_LEDS is not set
-# CONFIG_ETRAX_CSP0_LEDS is not set
-# CONFIG_ETRAX_NO_LEDS is not set
-CONFIG_ETRAX_LED1G=2
-CONFIG_ETRAX_LED1R=2
-CONFIG_ETRAX_LED2G=3
-CONFIG_ETRAX_LED2R=3
-CONFIG_ETRAX_LED3G=2
-CONFIG_ETRAX_LED3R=2
-CONFIG_ETRAX_RESCUE_SER0=y
-# CONFIG_ETRAX_RESCUE_SER1 is not set
-# CONFIG_ETRAX_RESCUE_SER2 is not set
-# CONFIG_ETRAX_RESCUE_SER3 is not set
-CONFIG_ETRAX_DEF_R_WAITSTATES=0x95f8
-CONFIG_ETRAX_DEF_R_BUS_CONFIG=0x4
-CONFIG_ETRAX_SDRAM=y
-CONFIG_ETRAX_DEF_R_SDRAM_CONFIG=0x09603737
-CONFIG_ETRAX_DEF_R_SDRAM_TIMING=0x80608002
-CONFIG_ETRAX_DEF_R_PORT_PA_DIR=0x1c
-CONFIG_ETRAX_DEF_R_PORT_PA_DATA=0xf0
-CONFIG_ETRAX_DEF_R_PORT_PB_CONFIG=0x00
-CONFIG_ETRAX_DEF_R_PORT_PB_DIR=0xce
-CONFIG_ETRAX_DEF_R_PORT_PB_DATA=0x03
-# CONFIG_ETRAX_SOFT_SHUTDOWN is not set
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE=""
+# CONFIG_XIP_KERNEL is not set
+# CONFIG_KEXEC is not set
+
+#
+# CPU Power Management
+#
+CONFIG_CPU_IDLE=y
+CONFIG_CPU_IDLE_GOV_LADDER=y
+CONFIG_CPU_IDLE_GOV_MENU=y
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+# CONFIG_VFP is not set
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_HAVE_AOUT=y
+# CONFIG_BINFMT_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Power management options
+#
+# CONFIG_PM is not set
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
CONFIG_NET=y
#
@@ -226,7 +363,8 @@ CONFIG_IP_FIB_HASH=y
# CONFIG_INET_XFRM_MODE_TUNNEL is not set
# CONFIG_INET_XFRM_MODE_BEET is not set
# CONFIG_INET_LRO is not set
-# CONFIG_INET_DIAG is not set
+CONFIG_INET_DIAG=y
+CONFIG_INET_TCP_DIAG=y
# CONFIG_TCP_CONG_ADVANCED is not set
CONFIG_TCP_CONG_CUBIC=y
CONFIG_DEFAULT_TCP_CONG="cubic"
@@ -270,56 +408,8 @@ CONFIG_DEFAULT_TCP_CONG="cubic"
# CONFIG_NET_9P is not set
#
-# Drivers for built-in interfaces
-#
-CONFIG_ETRAX_ETHERNET=y
-CONFIG_ETRAX_SERIAL=y
-# CONFIG_ETRAX_SERIAL_FAST_TIMER is not set
-# CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST is not set
-CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS=1
-CONFIG_ETRAX_SERIAL_PORT0=y
-CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_NONE=y
-# CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_PA is not set
-# CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_PB is not set
-# CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED is not set
-CONFIG_ETRAX_SER0_DTR_ON_PA_BIT=-1
-CONFIG_ETRAX_SER0_RI_ON_PA_BIT=-1
-CONFIG_ETRAX_SER0_DSR_ON_PA_BIT=-1
-CONFIG_ETRAX_SER0_CD_ON_PA_BIT=-1
-CONFIG_ETRAX_SER0_DTR_ON_PB_BIT=-1
-CONFIG_ETRAX_SER0_RI_ON_PB_BIT=-1
-CONFIG_ETRAX_SER0_DSR_ON_PB_BIT=-1
-CONFIG_ETRAX_SER0_CD_ON_PB_BIT=-1
-# CONFIG_ETRAX_SERIAL_PORT1 is not set
-# CONFIG_ETRAX_SERIAL_PORT2 is not set
-# CONFIG_ETRAX_SERIAL_PORT3 is not set
-# CONFIG_ETRAX_RS485 is not set
-CONFIG_ETRAX_USB_HOST=y
-CONFIG_ETRAX_USB_HOST_PORT1=y
-CONFIG_ETRAX_USB_HOST_PORT2=y
-CONFIG_ETRAX_PTABLE_SECTOR=0
-CONFIG_ETRAX_I2C=y
-CONFIG_ETRAX_I2C_USES_PB_NOT_PB_I2C=y
-CONFIG_ETRAX_I2C_DATA_PORT=0
-CONFIG_ETRAX_I2C_CLK_PORT=1
-# CONFIG_ETRAX_I2C_EEPROM is not set
-CONFIG_ETRAX_GPIO=y
-CONFIG_ETRAX_PA_BUTTON_BITMASK=02
-CONFIG_ETRAX_PA_CHANGEABLE_DIR=0xFF
-CONFIG_ETRAX_PA_CHANGEABLE_BITS=0xFF
-CONFIG_ETRAX_PB_CHANGEABLE_DIR=0xFF
-CONFIG_ETRAX_PB_CHANGEABLE_BITS=0xFF
-# CONFIG_ETRAX_SYNCHRONOUS_SERIAL is not set
-CONFIG_ETRAX_AXISFLASHMAP=y
-# CONFIG_ETRAX_RTC is not set
-# CONFIG_ETRAX_NETWORK_LED_ON_WHEN_LINK is not set
-CONFIG_ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY=y
-CONFIG_ETRAX_SERIAL_PORT0_NO_DMA_OUT=y
-# CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT is not set
-# CONFIG_ETRAX_SERIAL_PORT0_DMA0_OUT is not set
-CONFIG_ETRAX_SERIAL_PORT0_NO_DMA_IN=y
-# CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN is not set
-# CONFIG_ETRAX_SERIAL_PORT0_DMA1_IN is not set
+# Device Drivers
+#
#
# Generic Driver Options
@@ -327,10 +417,11 @@ CONFIG_ETRAX_SERIAL_PORT0_NO_DMA_IN=y
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
-# CONFIG_STANDALONE is not set
+CONFIG_STANDALONE=y
# CONFIG_PREVENT_FIRMWARE_BUILD is not set
# CONFIG_FW_LOADER is not set
# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_CONNECTOR is not set
CONFIG_MTD=y
# CONFIG_MTD_DEBUG is not set
# CONFIG_MTD_TESTS is not set
@@ -338,7 +429,8 @@ CONFIG_MTD_CONCAT=y
CONFIG_MTD_PARTITIONS=y
CONFIG_MTD_ROOTFS_ROOT_DEV=y
# CONFIG_MTD_REDBOOT_PARTS is not set
-# CONFIG_MTD_CMDLINE_PARTS is not set
+CONFIG_MTD_CMDLINE_PARTS=y
+# CONFIG_MTD_AFS_PARTS is not set
# CONFIG_MTD_AR7_PARTS is not set
#
@@ -346,8 +438,7 @@ CONFIG_MTD_ROOTFS_ROOT_DEV=y
#
CONFIG_MTD_CHAR=y
CONFIG_MTD_BLKDEVS=y
-# CONFIG_MTD_BLOCK is not set
-CONFIG_MTD_BLOCK_RO=y
+CONFIG_MTD_BLOCK=y
# CONFIG_FTL is not set
# CONFIG_NFTL is not set
# CONFIG_INFTL is not set
@@ -358,14 +449,8 @@ CONFIG_MTD_BLOCK_RO=y
#
# RAM/ROM/Flash chip drivers
#
-CONFIG_MTD_CFI=y
-CONFIG_MTD_JEDECPROBE=y
-CONFIG_MTD_GEN_PROBE=y
-CONFIG_MTD_CFI_ADV_OPTIONS=y
-CONFIG_MTD_CFI_NOSWAP=y
-# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set
-# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set
-# CONFIG_MTD_CFI_GEOMETRY is not set
+# CONFIG_MTD_CFI is not set
+# CONFIG_MTD_JEDECPROBE is not set
CONFIG_MTD_MAP_BANK_WIDTH_1=y
CONFIG_MTD_MAP_BANK_WIDTH_2=y
CONFIG_MTD_MAP_BANK_WIDTH_4=y
@@ -376,25 +461,24 @@ CONFIG_MTD_CFI_I1=y
CONFIG_MTD_CFI_I2=y
# CONFIG_MTD_CFI_I4 is not set
# CONFIG_MTD_CFI_I8 is not set
-# CONFIG_MTD_OTP is not set
-# CONFIG_MTD_CFI_INTELEXT is not set
-CONFIG_MTD_CFI_AMDSTD=y
-# CONFIG_MTD_CFI_STAA is not set
-CONFIG_MTD_CFI_UTIL=y
-CONFIG_MTD_RAM=y
-CONFIG_MTD_ROM=y
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
# CONFIG_MTD_ABSENT is not set
#
# Mapping drivers for chip access
#
-CONFIG_MTD_COMPLEX_MAPPINGS=y
-# CONFIG_MTD_PHYSMAP is not set
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
# CONFIG_MTD_PLATRAM is not set
#
# Self-contained MTD device drivers
#
+CONFIG_MTD_DATAFLASH=y
+# CONFIG_MTD_DATAFLASH_WRITE_VERIFY is not set
+# CONFIG_MTD_DATAFLASH_OTP is not set
+# CONFIG_MTD_M25P80 is not set
+# CONFIG_MTD_SST25L is not set
# CONFIG_MTD_SLRAM is not set
# CONFIG_MTD_PHRAM is not set
# CONFIG_MTD_MTDRAM is not set
@@ -406,7 +490,20 @@ CONFIG_MTD_COMPLEX_MAPPINGS=y
# CONFIG_MTD_DOC2000 is not set
# CONFIG_MTD_DOC2001 is not set
# CONFIG_MTD_DOC2001PLUS is not set
-# CONFIG_MTD_NAND is not set
+CONFIG_MTD_NAND=y
+# CONFIG_MTD_NAND_VERIFY_WRITE is not set
+# CONFIG_MTD_NAND_ECC_SMC is not set
+# CONFIG_MTD_NAND_MUSEUM_IDS is not set
+# CONFIG_MTD_NAND_GPIO is not set
+CONFIG_MTD_NAND_IDS=y
+# CONFIG_MTD_NAND_DISKONCHIP is not set
+CONFIG_MTD_NAND_ATMEL=y
+# CONFIG_MTD_NAND_ATMEL_ECC_HW is not set
+CONFIG_MTD_NAND_ATMEL_ECC_SOFT=y
+# CONFIG_MTD_NAND_ATMEL_ECC_NONE is not set
+# CONFIG_MTD_NAND_NANDSIM is not set
+# CONFIG_MTD_NAND_PLATFORM is not set
+# CONFIG_MTD_ALAUDA is not set
# CONFIG_MTD_ONENAND is not set
#
@@ -421,13 +518,30 @@ CONFIG_MTD_COMPLEX_MAPPINGS=y
# CONFIG_PARPORT is not set
CONFIG_BLK_DEV=y
# CONFIG_BLK_DEV_COW_COMMON is not set
-# CONFIG_BLK_DEV_LOOP is not set
+CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
# CONFIG_BLK_DEV_NBD is not set
# CONFIG_BLK_DEV_UB is not set
-# CONFIG_BLK_DEV_RAM is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=8192
+# CONFIG_BLK_DEV_XIP is not set
# CONFIG_CDROM_PKTCDVD is not set
# CONFIG_ATA_OVER_ETH is not set
-# CONFIG_BLK_DEV_HD is not set
+# CONFIG_MG_DISK is not set
+CONFIG_MISC_DEVICES=y
+CONFIG_ATMEL_TCLIB=y
+CONFIG_ATMEL_TCB_CLKSRC=y
+CONFIG_ATMEL_TCB_CLKSRC_BLOCK=0
+CONFIG_ATMEL_SSC=y
+# CONFIG_ENCLOSURE_SERVICES is not set
+# CONFIG_C2PORT is not set
+
+#
+# EEPROM support
+#
+# CONFIG_EEPROM_AT25 is not set
+# CONFIG_EEPROM_93CX6 is not set
CONFIG_HAVE_IDE=y
# CONFIG_IDE is not set
@@ -435,10 +549,40 @@ CONFIG_HAVE_IDE=y
# SCSI device support
#
# CONFIG_RAID_ATTRS is not set
-# CONFIG_SCSI is not set
-# CONFIG_SCSI_DMA is not set
+CONFIG_SCSI=y
+CONFIG_SCSI_DMA=y
+CONFIG_SCSI_TGT=y
# CONFIG_SCSI_NETLINK is not set
-# CONFIG_MEDIA_SUPPORT is not set
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+# CONFIG_CHR_DEV_ST is not set
+# CONFIG_CHR_DEV_OSST is not set
+# CONFIG_BLK_DEV_SR is not set
+# CONFIG_CHR_DEV_SG is not set
+# CONFIG_CHR_DEV_SCH is not set
+CONFIG_SCSI_MULTI_LUN=y
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+# CONFIG_SCSI_SCAN_ASYNC is not set
+CONFIG_SCSI_WAIT_SCAN=m
+
+#
+# SCSI Transports
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+# CONFIG_SCSI_ISCSI_ATTRS is not set
+# CONFIG_SCSI_SAS_LIBSAS is not set
+# CONFIG_SCSI_SRP_ATTRS is not set
+# CONFIG_SCSI_LOWLEVEL is not set
+# CONFIG_SCSI_DH is not set
+# CONFIG_SCSI_OSD_INITIATOR is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
CONFIG_NETDEVICES=y
# CONFIG_DUMMY is not set
# CONFIG_BONDING is not set
@@ -446,10 +590,37 @@ CONFIG_NETDEVICES=y
# CONFIG_EQUALIZER is not set
# CONFIG_TUN is not set
# CONFIG_VETH is not set
-# CONFIG_PHYLIB is not set
+CONFIG_PHYLIB=y
+# CONFIG_SWCONFIG is not set
+
+#
+# MII PHY device drivers
+#
+# CONFIG_MARVELL_PHY is not set
+# CONFIG_DAVICOM_PHY is not set
+# CONFIG_QSEMI_PHY is not set
+# CONFIG_LXT_PHY is not set
+# CONFIG_CICADA_PHY is not set
+# CONFIG_VITESSE_PHY is not set
+# CONFIG_SMSC_PHY is not set
+# CONFIG_BROADCOM_PHY is not set
+# CONFIG_ICPLUS_PHY is not set
+# CONFIG_REALTEK_PHY is not set
+# CONFIG_NATIONAL_PHY is not set
+# CONFIG_STE10XP is not set
+# CONFIG_LSI_ET1011C_PHY is not set
+# CONFIG_FIXED_PHY is not set
+# CONFIG_MDIO_BITBANG is not set
CONFIG_NET_ETHERNET=y
CONFIG_MII=y
+CONFIG_MACB=y
+# CONFIG_AX88796 is not set
+# CONFIG_SMC91X is not set
+# CONFIG_DM9000 is not set
+# CONFIG_ENC28J60 is not set
# CONFIG_ETHOC is not set
+# CONFIG_SMC911X is not set
+# CONFIG_SMSC911X is not set
# CONFIG_DNET is not set
# CONFIG_IBM_NEW_EMAC_ZMII is not set
# CONFIG_IBM_NEW_EMAC_RGMII is not set
@@ -458,7 +629,9 @@ CONFIG_MII=y
# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set
# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
+# CONFIG_B44 is not set
# CONFIG_KS8842 is not set
+# CONFIG_KS8851 is not set
# CONFIG_KS8851_MLL is not set
# CONFIG_NETDEV_1000 is not set
# CONFIG_NETDEV_10000 is not set
@@ -473,7 +646,7 @@ CONFIG_WLAN=y
#
# USB Network Adapters
#
-CONFIG_USB_CATC=m
+# CONFIG_USB_CATC is not set
# CONFIG_USB_KAWETH is not set
# CONFIG_USB_PEGASUS is not set
# CONFIG_USB_RTL8150 is not set
@@ -484,13 +657,42 @@ CONFIG_USB_CATC=m
# CONFIG_NETCONSOLE is not set
# CONFIG_NETPOLL is not set
# CONFIG_NET_POLL_CONTROLLER is not set
-# CONFIG_I2C is not set
-# CONFIG_RTC_CLASS is not set
+# CONFIG_ISDN is not set
+# CONFIG_PHONE is not set
#
# Input device support
#
-# CONFIG_INPUT is not set
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+
+#
+# Userland interfaces
+#
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ATKBD is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+CONFIG_KEYBOARD_GPIO=y
+# CONFIG_KEYBOARD_MATRIX is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_OPENCORES is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
#
# Hardware I/O ports
@@ -513,108 +715,141 @@ CONFIG_USB_CATC=m
#
# Non-8250 serial port support
#
+CONFIG_SERIAL_ATMEL=y
+CONFIG_SERIAL_ATMEL_CONSOLE=y
+CONFIG_SERIAL_ATMEL_PDC=y
+# CONFIG_SERIAL_ATMEL_TTYAT is not set
+# CONFIG_SERIAL_MAX3100 is not set
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
CONFIG_UNIX98_PTYS=y
# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
# CONFIG_LEGACY_PTYS is not set
# CONFIG_IPMI_HANDLER is not set
-# CONFIG_HW_RANDOM is not set
-# CONFIG_RTC is not set
-# CONFIG_GEN_RTC is not set
+CONFIG_HW_RANDOM=y
+# CONFIG_HW_RANDOM_TIMERIOMEM is not set
+# CONFIG_NVRAM is not set
# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+# CONFIG_I2C is not set
+CONFIG_SPI=y
+CONFIG_SPI_MASTER=y
#
-# PCMCIA character devices
+# SPI Master Controller Drivers
#
-# CONFIG_RAW_DRIVER is not set
-# CONFIG_TCG_TPM is not set
+CONFIG_SPI_ATMEL=y
+# CONFIG_SPI_BITBANG is not set
+# CONFIG_SPI_GPIO is not set
#
-# File systems
+# SPI Protocol Masters
#
-# CONFIG_EXT2_FS is not set
-# CONFIG_EXT3_FS is not set
-# CONFIG_EXT4_FS is not set
-# CONFIG_REISERFS_FS is not set
-# CONFIG_JFS_FS is not set
-# CONFIG_FS_POSIX_ACL is not set
-# CONFIG_XFS_FS is not set
-# CONFIG_OCFS2_FS is not set
-# CONFIG_BTRFS_FS is not set
-# CONFIG_NILFS2_FS is not set
-# CONFIG_AUFS_FS is not set
-CONFIG_FILE_LOCKING=y
-# CONFIG_FSNOTIFY is not set
-# CONFIG_DNOTIFY is not set
-# CONFIG_INOTIFY is not set
-# CONFIG_INOTIFY_USER is not set
-# CONFIG_QUOTA is not set
-# CONFIG_AUTOFS_FS is not set
-# CONFIG_AUTOFS4_FS is not set
-# CONFIG_FUSE_FS is not set
+# CONFIG_SPI_SPIDEV is not set
+# CONFIG_SPI_TLE62X0 is not set
#
-# Caches
+# PPS support
#
-# CONFIG_FSCACHE is not set
+# CONFIG_PPS is not set
+CONFIG_ARCH_REQUIRE_GPIOLIB=y
+CONFIG_GPIOLIB=y
+CONFIG_GPIO_SYSFS=y
#
-# CD-ROM/DVD Filesystems
+# Memory mapped GPIO expanders:
#
-# CONFIG_ISO9660_FS is not set
-# CONFIG_UDF_FS is not set
#
-# DOS/FAT/NT Filesystems
+# I2C GPIO expanders:
#
-# CONFIG_MSDOS_FS is not set
-# CONFIG_VFAT_FS is not set
-# CONFIG_NTFS_FS is not set
#
-# Pseudo filesystems
+# PCI GPIO expanders:
#
-CONFIG_PROC_FS=y
-CONFIG_PROC_KCORE=y
-CONFIG_PROC_SYSCTL=y
-# CONFIG_PROC_PAGE_MONITOR is not set
-CONFIG_SYSFS=y
-CONFIG_TMPFS=y
-# CONFIG_TMPFS_POSIX_ACL is not set
-# CONFIG_HUGETLB_PAGE is not set
-CONFIG_CONFIGFS_FS=y
-CONFIG_MISC_FILESYSTEMS=y
-# CONFIG_ADFS_FS is not set
-# CONFIG_AFFS_FS is not set
-# CONFIG_HFS_FS is not set
-# CONFIG_HFSPLUS_FS is not set
-# CONFIG_BEFS_FS is not set
-# CONFIG_BFS_FS is not set
-# CONFIG_EFS_FS is not set
-# CONFIG_JFFS2_FS is not set
-# CONFIG_CRAMFS is not set
-CONFIG_SQUASHFS=y
-# CONFIG_SQUASHFS_EMBEDDED is not set
-CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3
-# CONFIG_VXFS_FS is not set
-# CONFIG_MINIX_FS is not set
-# CONFIG_OMFS_FS is not set
-# CONFIG_HPFS_FS is not set
-# CONFIG_QNX4FS_FS is not set
-# CONFIG_ROMFS_FS is not set
-# CONFIG_SYSV_FS is not set
-# CONFIG_UFS_FS is not set
-# CONFIG_NETWORK_FILESYSTEMS is not set
#
-# Partition Types
+# SPI GPIO expanders:
#
-# CONFIG_PARTITION_ADVANCED is not set
-CONFIG_MSDOS_PARTITION=y
-# CONFIG_NLS is not set
-# CONFIG_DLM is not set
+# CONFIG_GPIO_MAX7301 is not set
+# CONFIG_GPIO_MCP23S08 is not set
+# CONFIG_GPIO_MC33880 is not set
+
+#
+# AC97 GPIO expanders:
+#
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_THERMAL is not set
+CONFIG_WATCHDOG=y
+# CONFIG_WATCHDOG_NOWAYOUT is not set
+
+#
+# Watchdog Device Drivers
+#
+# CONFIG_SOFT_WATCHDOG is not set
+CONFIG_AT91SAM9X_WATCHDOG=y
+
+#
+# USB-based Watchdog Cards
+#
+# CONFIG_USBPCWATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
+
+#
+# Sonics Silicon Backplane
+#
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_MFD_ASIC3 is not set
+# CONFIG_HTC_EGPIO is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_MFD_T7L66XB is not set
+# CONFIG_MFD_TC6387XB is not set
+# CONFIG_MFD_TC6393XB is not set
+# CONFIG_MFD_MC13783 is not set
+# CONFIG_EZX_PCAP is not set
+# CONFIG_REGULATOR is not set
+# CONFIG_MEDIA_SUPPORT is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+# CONFIG_FB is not set
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+# CONFIG_SOUND is not set
+CONFIG_HID_SUPPORT=y
+# CONFIG_HID is not set
+
+#
+# USB Input Devices
+#
+# CONFIG_USB_HID is not set
+# CONFIG_HID_PID is not set
+
+#
+# USB HID Boot Protocol drivers
+#
+# CONFIG_USB_KBD is not set
+# CONFIG_USB_MOUSE is not set
CONFIG_USB_SUPPORT=y
-# CONFIG_USB_ARCH_HAS_HCD is not set
-# CONFIG_USB_ARCH_HAS_OHCI is not set
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
# CONFIG_USB_ARCH_HAS_EHCI is not set
CONFIG_USB=y
# CONFIG_USB_DEBUG is not set
@@ -641,9 +876,14 @@ CONFIG_USB=y
# CONFIG_USB_ISP116X_HCD is not set
# CONFIG_USB_ISP1760_HCD is not set
# CONFIG_USB_ISP1362_HCD is not set
+CONFIG_USB_OHCI_HCD=y
+# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set
+# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set
+CONFIG_USB_OHCI_LITTLE_ENDIAN=y
# CONFIG_USB_SL811_HCD is not set
# CONFIG_USB_R8A66597_HCD is not set
# CONFIG_USB_HWA_HCD is not set
+# CONFIG_USB_MUSB_HDRC is not set
#
# USB Device Class drivers
@@ -660,12 +900,26 @@ CONFIG_USB=y
#
# also be needed; see USB_STORAGE Help for more info
#
+CONFIG_USB_STORAGE=y
+# CONFIG_USB_STORAGE_DEBUG is not set
+# CONFIG_USB_STORAGE_DATAFAB is not set
+# CONFIG_USB_STORAGE_FREECOM is not set
+# CONFIG_USB_STORAGE_ISD200 is not set
+# CONFIG_USB_STORAGE_USBAT is not set
+# CONFIG_USB_STORAGE_SDDR09 is not set
+# CONFIG_USB_STORAGE_SDDR55 is not set
+# CONFIG_USB_STORAGE_JUMPSHOT is not set
+# CONFIG_USB_STORAGE_ALAUDA is not set
+# CONFIG_USB_STORAGE_ONETOUCH is not set
+# CONFIG_USB_STORAGE_KARMA is not set
+# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set
# CONFIG_USB_LIBUSUAL is not set
#
# USB Imaging devices
#
# CONFIG_USB_MDC800 is not set
+# CONFIG_USB_MICROTEK is not set
#
# USB port drivers
@@ -700,13 +954,234 @@ CONFIG_USB=y
#
# OTG and related infrastructure
#
+# CONFIG_USB_GPIO_VBUS is not set
# CONFIG_NOP_USB_XCEIV is not set
+CONFIG_MMC=y
+# CONFIG_MMC_DEBUG is not set
+# CONFIG_MMC_UNSAFE_RESUME is not set
+
+#
+# MMC/SD/SDIO Card Drivers
+#
+CONFIG_MMC_BLOCK=y
+CONFIG_MMC_BLOCK_BOUNCE=y
+# CONFIG_SDIO_UART is not set
+# CONFIG_MMC_TEST is not set
+
+#
+# MMC/SD/SDIO Host Controller Drivers
+#
+# CONFIG_MMC_SDHCI is not set
+CONFIG_MMC_AT91=y
+# CONFIG_MMC_ATMELMCI is not set
+# CONFIG_MMC_SPI is not set
+# CONFIG_MEMSTICK is not set
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+
+#
+# LED drivers
+#
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_GPIO_PLATFORM=y
+# CONFIG_LEDS_DAC124S085 is not set
+
+#
+# LED Triggers
+#
+# CONFIG_LEDS_TRIGGERS is not set
+# CONFIG_ACCESSIBILITY is not set
+CONFIG_RTC_LIB=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+CONFIG_RTC_DEBUG=y
+
+#
+# RTC interfaces
+#
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
+CONFIG_RTC_INTF_DEV_UIE_EMUL=y
+# CONFIG_RTC_DRV_TEST is not set
+
+#
+# SPI RTC drivers
+#
+# CONFIG_RTC_DRV_M41T94 is not set
+# CONFIG_RTC_DRV_DS1305 is not set
+# CONFIG_RTC_DRV_DS1390 is not set
+# CONFIG_RTC_DRV_MAX6902 is not set
+# CONFIG_RTC_DRV_R9701 is not set
+# CONFIG_RTC_DRV_RS5C348 is not set
+# CONFIG_RTC_DRV_DS3234 is not set
+# CONFIG_RTC_DRV_PCF2123 is not set
+
+#
+# Platform RTC drivers
+#
+CONFIG_RTC_DRV_CMOS=y
+# CONFIG_RTC_DRV_DS1286 is not set
+# CONFIG_RTC_DRV_DS1511 is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T35 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_BQ4802 is not set
+# CONFIG_RTC_DRV_V3020 is not set
+
+#
+# on-CPU RTC drivers
+#
+CONFIG_RTC_DRV_AT91SAM9=y
+CONFIG_RTC_DRV_AT91SAM9_RTT=0
+CONFIG_RTC_DRV_AT91SAM9_GPBR=0
+# CONFIG_DMADEVICES is not set
+# CONFIG_AUXDISPLAY is not set
+# CONFIG_UIO is not set
+
+#
+# TI VLYNQ
+#
# CONFIG_STAGING is not set
#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_EXT4_FS is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_FS_POSIX_ACL is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_BTRFS_FS is not set
+# CONFIG_NILFS2_FS is not set
+# CONFIG_AUFS_FS is not set
+CONFIG_FILE_LOCKING=y
+# CONFIG_FSNOTIFY is not set
+# CONFIG_DNOTIFY is not set
+# CONFIG_INOTIFY is not set
+# CONFIG_INOTIFY_USER is not set
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# Caches
+#
+# CONFIG_FSCACHE is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+# CONFIG_MSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+# CONFIG_PROC_PAGE_MONITOR is not set
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+CONFIG_MISC_FILESYSTEMS=y
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_YAFFS_FS is not set
+# CONFIG_JFFS2_FS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_SQUASHFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_OMFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+# CONFIG_NFS_FS is not set
+# CONFIG_NFSD is not set
+# CONFIG_SMB_FS is not set
+# CONFIG_CIFS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+# CONFIG_NLS_CODEPAGE_437 is not set
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_ASCII is not set
+# CONFIG_NLS_ISO8859_1 is not set
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+# CONFIG_NLS_UTF8 is not set
+# CONFIG_DLM is not set
+
+#
# Kernel hacking
#
-# CONFIG_SYSTEM_PROFILER is not set
CONFIG_PRINTK_TIME=y
# CONFIG_ENABLE_WARN_DEPRECATED is not set
# CONFIG_ENABLE_MUST_CHECK is not set
@@ -717,10 +1192,19 @@ CONFIG_MAGIC_SYSRQ=y
# CONFIG_DEBUG_FS is not set
# CONFIG_HEADERS_CHECK is not set
# CONFIG_DEBUG_KERNEL is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
# CONFIG_DEBUG_MEMORY_INIT is not set
+CONFIG_FRAME_POINTER=y
# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+# CONFIG_LATENCYTOP is not set
# CONFIG_SYSCTL_SYSCALL_CHECK is not set
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_TRACING_SUPPORT=y
+# CONFIG_FTRACE is not set
# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_ARM_UNWIND is not set
+# CONFIG_DEBUG_USER is not set
#
# Security options
@@ -734,6 +1218,11 @@ CONFIG_CRYPTO=y
#
# Crypto core or helper
#
+# CONFIG_CRYPTO_FIPS is not set
+CONFIG_CRYPTO_ALGAPI=m
+CONFIG_CRYPTO_ALGAPI2=m
+CONFIG_CRYPTO_RNG=m
+CONFIG_CRYPTO_RNG2=m
# CONFIG_CRYPTO_MANAGER is not set
# CONFIG_CRYPTO_MANAGER2 is not set
# CONFIG_CRYPTO_GF128MUL is not set
@@ -788,7 +1277,7 @@ CONFIG_CRYPTO=y
#
# Ciphers
#
-# CONFIG_CRYPTO_AES is not set
+CONFIG_CRYPTO_AES=m
# CONFIG_CRYPTO_ANUBIS is not set
# CONFIG_CRYPTO_ARC4 is not set
# CONFIG_CRYPTO_BLOWFISH is not set
@@ -814,8 +1303,8 @@ CONFIG_CRYPTO=y
#
# Random Number Generation
#
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
-# CONFIG_CRYPTO_HW is not set
+CONFIG_CRYPTO_ANSI_CPRNG=m
+CONFIG_CRYPTO_HW=y
#
# OCF Configuration
@@ -826,16 +1315,17 @@ CONFIG_CRYPTO=y
#
# Library routines
#
-CONFIG_BITREVERSE=y
CONFIG_GENERIC_FIND_LAST_BIT=y
# CONFIG_CRC_CCITT is not set
# CONFIG_CRC16 is not set
# CONFIG_CRC_T10DIF is not set
# CONFIG_CRC_ITU_T is not set
-CONFIG_CRC32=y
+# CONFIG_CRC32 is not set
# CONFIG_CRC7 is not set
# CONFIG_LIBCRC32C is not set
CONFIG_ZLIB_INFLATE=y
+CONFIG_DECOMPRESS_GZIP=y
CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
CONFIG_HAS_DMA=y
CONFIG_NLATTR=y
diff --git a/target/foxg20/patches/foxg20.patch b/target/foxg20/patches/foxg20.patch
new file mode 100644
index 000000000..92031067f
--- /dev/null
+++ b/target/foxg20/patches/foxg20.patch
@@ -0,0 +1,4730 @@
+diff -Nur linux-2.6.32.2.orig/arch/arm/Kconfig linux-2.6.32.2/arch/arm/Kconfig
+--- linux-2.6.32.2.orig/arch/arm/Kconfig 2009-12-18 23:27:07.000000000 +0100
++++ linux-2.6.32.2/arch/arm/Kconfig 2010-02-16 21:29:07.000000000 +0100
+@@ -18,6 +18,9 @@
+ select HAVE_KRETPROBES if (HAVE_KPROBES)
+ select HAVE_FUNCTION_TRACER if (!XIP_KERNEL)
+ select HAVE_GENERIC_DMA_COHERENT
++ select HAVE_KERNEL_GZIP
++ select HAVE_KERNEL_BZIP2
++ select HAVE_KERNEL_LZMA
+ help
+ The ARM series is a line of low-power-consumption RISC chip designs
+ licensed by ARM Ltd and targeted at embedded applications and
+diff -Nur linux-2.6.32.2.orig/arch/arm/mach-at91/at91sam9260_devices.c linux-2.6.32.2/arch/arm/mach-at91/at91sam9260_devices.c
+--- linux-2.6.32.2.orig/arch/arm/mach-at91/at91sam9260_devices.c 2009-12-18 23:27:07.000000000 +0100
++++ linux-2.6.32.2/arch/arm/mach-at91/at91sam9260_devices.c 2010-02-16 21:29:07.000000000 +0100
+@@ -454,7 +454,15 @@
+ .sda_is_open_drain = 1,
+ .scl_pin = AT91_PIN_PA24,
+ .scl_is_open_drain = 1,
++#if defined(CONFIG_MACH_FOXG20)
++ /* Some I2C devices are limited to 100 kHz and i2c-gpio.h
++ * says "frequency is (500 / udelay) kHz" so 5 is best (and is
++ * used in i2c-gpio.c)
++ */
++ .udelay = 5, /* ~100 kHz */
++#else
+ .udelay = 2, /* ~100 kHz */
++#endif
+ };
+
+ static struct platform_device at91sam9260_twi_device = {
+diff -Nur linux-2.6.32.2.orig/arch/arm/mach-at91/board-foxg20.c linux-2.6.32.2/arch/arm/mach-at91/board-foxg20.c
+--- linux-2.6.32.2.orig/arch/arm/mach-at91/board-foxg20.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.32.2/arch/arm/mach-at91/board-foxg20.c 2010-02-16 21:29:07.000000000 +0100
+@@ -0,0 +1,371 @@
++/*
++ * Copyright (C) 2005 SAN People
++ * Copyright (C) 2008 Atmel
++ * Copyright (C) 2010 Lee McLoughlin - lee@lmmrtech.com
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++#include <linux/types.h>
++#include <linux/init.h>
++#include <linux/mm.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/spi/spi.h>
++#include <linux/spi/at73c213.h>
++#include <linux/gpio_keys.h>
++#include <linux/input.h>
++#include <linux/clk.h>
++#include <linux/w1-gpio.h>
++
++#include <mach/hardware.h>
++#include <asm/setup.h>
++#include <asm/mach-types.h>
++#include <asm/irq.h>
++
++#include <asm/mach/arch.h>
++#include <asm/mach/map.h>
++#include <asm/mach/irq.h>
++
++#include <mach/board.h>
++#include <mach/gpio.h>
++#include <mach/at91sam9_smc.h>
++
++#include "sam9_smc.h"
++#include "generic.h"
++
++/*
++ * The FOX hardware comes as the "Netus" board with just the cpu, ram,
++ * dataflash and two header connectors. This is plugged into the Fox board
++ * which provides the ethernet, usb, rtc, leds, switch, ...
++ * Other version of the Fox board are planned which could contain
++ * both NAND and sound (WM8731).
++ *
++ * By default USART4 and USART5 are not enabled (freeing up those pins
++ * for general use)
++ *
++ * Note: Enabling the NAND without a NAND device present doesn't cause
++ * any issues as on boot the probe will fail.
++ */
++/* #define FOXG20_NAND */
++/* #define FOXG20_WM8731 */
++/* #define FOX_USART4 */
++/* #define FOX_USART5 */
++
++static void __init foxg20_map_io(void)
++{
++ /* Initialize processor: 18.432 MHz crystal */
++ at91sam9260_initialize(18432000);
++
++ /* DBGU on ttyS0. (Rx & Tx only) */
++ at91_register_uart(0, 0, 0);
++
++ /* USART0 on ttyS1. (Rx, Tx, CTS, RTS, DTR, DSR, DCD, RI) */
++ at91_register_uart(AT91SAM9260_ID_US0, 1, ATMEL_UART_CTS | ATMEL_UART_RTS
++ | ATMEL_UART_DTR | ATMEL_UART_DSR | ATMEL_UART_DCD
++ | ATMEL_UART_RI);
++
++ /* USART1 on ttyS2. (Rx, Tx, RTS, CTS) */
++ at91_register_uart(AT91SAM9260_ID_US1, 2, ATMEL_UART_CTS | ATMEL_UART_RTS);
++
++ /* USART2 on ttyS3. (Rx & Tx only) */
++ at91_register_uart(AT91SAM9260_ID_US2, 3, 0);
++
++ /* USART3 on ttyS4. (Rx, Tx, RTS, CTS) */
++ at91_register_uart(AT91SAM9260_ID_US3, 4, ATMEL_UART_CTS | ATMEL_UART_RTS);
++
++#if defined(FOX_USART4)
++ /* USART4 on ttyS5. (Rx & Tx only) */
++ at91_register_uart(AT91SAM9260_ID_US4, 5, 0);
++#endif
++
++#if defined(FOX_USART5)
++ /* USART5 on ttyS6. (Rx & Tx only) */
++ at91_register_uart(AT91SAM9260_ID_US5, 6, 0);
++#endif
++
++ /* set serial console to ttyS0 (ie, DBGU) */
++ at91_set_serial_console(0);
++}
++
++static void __init foxg20_init_irq(void)
++{
++ at91sam9260_init_interrupts(NULL);
++}
++
++
++/*
++ * USB Host port
++ */
++static struct at91_usbh_data __initdata foxg20_usbh_data = {
++ .ports = 2,
++};
++
++/*
++ * USB Device port
++ */
++static struct at91_udc_data __initdata foxg20_udc_data = {
++ .vbus_pin = AT91_PIN_PC6,
++ .pullup_pin = 0, /* pull-up driven by UDC */
++};
++
++
++/*
++ * SPI devices.
++ */
++static struct spi_board_info foxg20_spi_devices[] = {
++#if !defined(CONFIG_MMC_AT91)
++ {
++ .modalias = "mtd_dataflash",
++ .chip_select = 1,
++ .max_speed_hz = 15 * 1000 * 1000,
++ .bus_num = 0,
++ },
++#endif
++};
++
++
++/*
++ * MACB Ethernet device
++ */
++static struct at91_eth_data __initdata foxg20_macb_data = {
++ .phy_irq_pin = AT91_PIN_PA7,
++ .is_rmii = 1,
++};
++
++
++#ifdef FOXG20_NAND
++/* The Fox doesn't have NAND memory */
++/*
++ * NAND flash
++ */
++static struct mtd_partition __initdata foxg20_nand_partition[] = {
++ {
++ .name = "Bootstrap",
++ .offset = 0,
++ .size = 4 * SZ_1M,
++ },
++ {
++ .name = "Partition 1",
++ .offset = MTDPART_OFS_NXTBLK,
++ .size = 60 * SZ_1M,
++ },
++ {
++ .name = "Partition 2",
++ .offset = MTDPART_OFS_NXTBLK,
++ .size = MTDPART_SIZ_FULL,
++ },
++};
++
++static struct mtd_partition * __init nand_partitions(int size, int *num_partitions)
++{
++ *num_partitions = ARRAY_SIZE(foxg20_nand_partition);
++ return foxg20_nand_partition;
++}
++
++/* det_pin is not connected */
++static struct atmel_nand_data __initdata foxg20_nand_data = {
++ .ale = 21,
++ .cle = 22,
++ .rdy_pin = AT91_PIN_PC13,
++ .enable_pin = AT91_PIN_PC14,
++ .partition_info = nand_partitions,
++#if defined(CONFIG_MTD_NAND_ATMEL_BUSWIDTH_16)
++ .bus_width_16 = 1,
++#else
++ .bus_width_16 = 0,
++#endif
++};
++
++static struct sam9_smc_config __initdata foxg20_nand_smc_config = {
++ .ncs_read_setup = 0,
++ .nrd_setup = 2,
++ .ncs_write_setup = 0,
++ .nwe_setup = 2,
++
++ .ncs_read_pulse = 4,
++ .nrd_pulse = 4,
++ .ncs_write_pulse = 4,
++ .nwe_pulse = 4,
++
++ .read_cycle = 7,
++ .write_cycle = 7,
++
++ .mode = AT91_SMC_READMODE | AT91_SMC_WRITEMODE | AT91_SMC_EXNWMODE_DISABLE,
++ .tdf_cycles = 3,
++};
++
++static void __init foxg20_add_device_nand(void)
++{
++ /* setup bus-width (8 or 16) */
++ if (foxg20_nand_data.bus_width_16)
++ foxg20_nand_smc_config.mode |= AT91_SMC_DBW_16;
++ else
++ foxg20_nand_smc_config.mode |= AT91_SMC_DBW_8;
++
++ /* configure chip-select 3 (NAND) */
++ sam9_smc_configure(3, &foxg20_nand_smc_config);
++
++ at91_add_device_nand(&foxg20_nand_data);
++}
++#endif
++
++
++/*
++ * MCI (SD/MMC)
++ * det_pin, wp_pin and vcc_pin are not connected
++ */
++static struct at91_mmc_data __initdata foxg20_mmc_data = {
++ .slot_b = 1,
++ .wire4 = 1,
++};
++
++
++/*
++ * LEDs
++ */
++static struct gpio_led foxg20_leds[] = {
++ { /* user led, red */
++ .name = "user_led",
++ .gpio = AT91_PIN_PC7,
++ .active_low = 0,
++ .default_trigger = "heartbeat",
++ },
++};
++
++
++/*
++ * GPIO Buttons
++ */
++#if defined(CONFIG_KEYBOARD_GPIO) || defined(CONFIG_KEYBOARD_GPIO_MODULE)
++static struct gpio_keys_button foxg20_buttons[] = {
++ {
++ .gpio = AT91_PIN_PC4,
++ .code = BTN_1,
++ .desc = "Button 1",
++ .active_low = 1,
++ .wakeup = 1,
++ },
++};
++
++static struct gpio_keys_platform_data foxg20_button_data = {
++ .buttons = foxg20_buttons,
++ .nbuttons = ARRAY_SIZE(foxg20_buttons),
++};
++
++static struct platform_device foxg20_button_device = {
++ .name = "gpio-keys",
++ .id = -1,
++ .num_resources = 0,
++ .dev = {
++ .platform_data = &foxg20_button_data,
++ }
++};
++
++static void __init foxg20_add_device_buttons(void)
++{
++ at91_set_gpio_input(AT91_PIN_PC4, 1); /* btn1 */
++ at91_set_deglitch(AT91_PIN_PC4, 1);
++
++ platform_device_register(&foxg20_button_device);
++}
++#else
++static void __init foxg20_add_device_buttons(void) {}
++#endif
++
++
++#if !defined(FOXG20_WM8731)
++#if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE)
++static struct w1_gpio_platform_data w1_gpio_pdata = {
++ /* If you choose to use a pin other than PB16 it needs to be 3.3V */
++ .pin = AT91_PIN_PB16,
++ .is_open_drain = 1,
++};
++
++static struct platform_device w1_device = {
++ .name = "w1-gpio",
++ .id = -1,
++ .dev.platform_data = &w1_gpio_pdata,
++};
++
++static void __init at91_add_device_w1(void)
++{
++ at91_set_GPIO_periph(w1_gpio_pdata.pin, 1);
++ at91_set_multi_drive(w1_gpio_pdata.pin, 1);
++ platform_device_register(&w1_device);
++}
++
++#endif
++#endif
++
++
++static struct i2c_board_info __initdata foxg20_i2c_devices[] = {
++ {
++ I2C_BOARD_INFO("24c512", 0x50),
++#ifdef FOXG20_WM8731
++ I2C_BOARD_INFO("wm8731", 0x1b),
++#endif
++ },
++};
++
++
++static void __init foxg20_board_init(void)
++{
++ /* Serial */
++ at91_add_device_serial();
++ /* USB Host */
++ at91_add_device_usbh(&foxg20_usbh_data);
++ /* USB Device */
++ at91_add_device_udc(&foxg20_udc_data);
++ /* SPI */
++ at91_add_device_spi(foxg20_spi_devices, ARRAY_SIZE(foxg20_spi_devices));
++#ifdef FOXG20_NAND
++ /* The Fox doesn't have NAND memory */
++ /* NAND */
++ foxg20_add_device_nand();
++#endif
++ /* Ethernet */
++ at91_add_device_eth(&foxg20_macb_data);
++ /* MMC */
++ at91_add_device_mmc(0, &foxg20_mmc_data);
++ /* I2C */
++ at91_add_device_i2c(foxg20_i2c_devices, ARRAY_SIZE(foxg20_i2c_devices));
++ /* LEDs */
++ at91_gpio_leds(foxg20_leds, ARRAY_SIZE(foxg20_leds));
++ /* Push Buttons */
++ foxg20_add_device_buttons();
++#ifdef FOXG20_WM8731
++ /* The Fox doesn't have this sound chip */
++ /* PCK0 provides MCLK to the WM8731 */
++ at91_set_B_periph(AT91_PIN_PC1, 0);
++ /* SSC (for WM8731) */
++ at91_add_device_ssc(AT91SAM9260_ID_SSC, ATMEL_SSC_TX);
++#else
++#if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE)
++ at91_add_device_w1();
++#endif
++#endif
++}
++
++MACHINE_START(AT91SAM9G20EK, "Acme Systems FOXG20")
++ /* Maintainer: Lee McLoughlin */
++ .phys_io = AT91_BASE_SYS,
++ .io_pg_offst = (AT91_VA_BASE_SYS >> 18) & 0xfffc,
++ .boot_params = AT91_SDRAM_BASE + 0x100,
++ .timer = &at91sam926x_timer,
++ .map_io = foxg20_map_io,
++ .init_irq = foxg20_init_irq,
++ .init_machine = foxg20_board_init,
++MACHINE_END
+diff -Nur linux-2.6.32.2.orig/arch/arm/mach-at91/Kconfig linux-2.6.32.2/arch/arm/mach-at91/Kconfig
+--- linux-2.6.32.2.orig/arch/arm/mach-at91/Kconfig 2009-12-18 23:27:07.000000000 +0100
++++ linux-2.6.32.2/arch/arm/mach-at91/Kconfig 2010-02-16 21:29:07.000000000 +0100
+@@ -333,6 +333,13 @@
+ Select this if you are using a Eukrea Electromatique's
+ CPU9G20 Board <http://www.eukrea.com/>
+
++config MACH_FOXG20
++ bool "Acme Systems FOX G20"
++ depends on ARCH_AT91SAM9G20
++ help
++ Select this if you are using Acme Systems
++ FOX Board G20 <http://netus.acmesystems.it>
++
+ endif
+
+ # ----------------------------------------------------------
+@@ -388,13 +395,13 @@
+
+ config MTD_AT91_DATAFLASH_CARD
+ bool "Enable DataFlash Card support"
+- depends on (ARCH_AT91RM9200DK || MACH_AT91RM9200EK || MACH_AT91SAM9260EK || MACH_AT91SAM9261EK || MACH_AT91SAM9G10EK || MACH_AT91SAM9263EK || MACH_AT91SAM9G20EK || MACH_ECBAT91 || MACH_SAM9_L9260 || MACH_AT91CAP9ADK || MACH_NEOCORE926)
++ depends on (ARCH_AT91RM9200DK || MACH_AT91RM9200EK || MACH_AT91SAM9260EK || MACH_AT91SAM9261EK || MACH_AT91SAM9G10EK || MACH_AT91SAM9263EK || MACH_AT91SAM9G20EK || MACH_ECBAT91 || MACH_SAM9_L9260 || MACH_AT91CAP9ADK || MACH_NEOCORE926 || MACH_FOXG20)
+ help
+ Enable support for the DataFlash card.
+
+ config MTD_NAND_ATMEL_BUSWIDTH_16
+ bool "Enable 16-bit data bus interface to NAND flash"
+- depends on (MACH_AT91SAM9260EK || MACH_AT91SAM9261EK || MACH_AT91SAM9G10EK || MACH_AT91SAM9263EK || MACH_AT91SAM9G20EK || MACH_AT91SAM9G20EK_2MMC || MACH_AT91SAM9G45EKES || MACH_AT91CAP9ADK)
++ depends on (MACH_AT91SAM9260EK || MACH_AT91SAM9261EK || MACH_AT91SAM9G10EK || MACH_AT91SAM9263EK || MACH_AT91SAM9G20EK || MACH_AT91SAM9G20EK_2MMC || MACH_AT91SAM9G45EKES || MACH_AT91CAP9ADK || MACH_FOXG20)
+ help
+ On AT91SAM926x boards both types of NAND flash can be present
+ (8 and 16 bit data bus width).
+diff -Nur linux-2.6.32.2.orig/arch/arm/mach-at91/Makefile linux-2.6.32.2/arch/arm/mach-at91/Makefile
+--- linux-2.6.32.2.orig/arch/arm/mach-at91/Makefile 2009-12-18 23:27:07.000000000 +0100
++++ linux-2.6.32.2/arch/arm/mach-at91/Makefile 2010-02-16 21:29:07.000000000 +0100
+@@ -65,6 +65,9 @@
+ # AT91SAM9G45 board-specific support
+ obj-$(CONFIG_MACH_AT91SAM9G45EKES) += board-sam9m10g45ek.o
+
++# FOXG20 board-specific support
++obj-$(CONFIG_MACH_FOXG20) += board-foxg20.o
++
+ # AT91CAP9 board-specific support
+ obj-$(CONFIG_MACH_AT91CAP9ADK) += board-cap9adk.o
+
+diff -Nur linux-2.6.32.2.orig/drivers/mmc/host/at91_mci.c linux-2.6.32.2/drivers/mmc/host/at91_mci.c
+--- linux-2.6.32.2.orig/drivers/mmc/host/at91_mci.c 2009-12-18 23:27:07.000000000 +0100
++++ linux-2.6.32.2/drivers/mmc/host/at91_mci.c 2010-02-16 21:29:07.000000000 +0100
+@@ -461,7 +461,7 @@
+ at91_mci_write(host, AT91_MCI_DTOR, AT91_MCI_DTOMUL_1M | AT91_MCI_DTOCYC);
+ mr = AT91_MCI_PDCMODE | 0x34a;
+
+- if (cpu_is_at91sam9260() || cpu_is_at91sam9263())
++ if (cpu_is_at91sam9260() || cpu_is_at91sam9263() || cpu_is_at91sam9g20())
+ mr |= AT91_MCI_RDPROOF | AT91_MCI_WRPROOF;
+
+ at91_mci_write(host, AT91_MCI_MR, mr);
+@@ -1017,7 +1017,7 @@
+ host->bus_mode = 0;
+ host->board = pdev->dev.platform_data;
+ if (host->board->wire4) {
+- if (cpu_is_at91sam9260() || cpu_is_at91sam9263())
++ if (cpu_is_at91sam9260() || cpu_is_at91sam9263() || cpu_is_at91sam9g20())
+ mmc->caps |= MMC_CAP_4_BIT_DATA;
+ else
+ dev_warn(&pdev->dev, "4 wire bus mode not supported"
+diff -Nur linux-2.6.32.2.orig/foxg20.config-2.6.32.2-COMPLEX-v3 linux-2.6.32.2/foxg20.config-2.6.32.2-COMPLEX-v3
+--- linux-2.6.32.2.orig/foxg20.config-2.6.32.2-COMPLEX-v3 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.32.2/foxg20.config-2.6.32.2-COMPLEX-v3 2010-02-16 21:29:07.000000000 +0100
+@@ -0,0 +1,2789 @@
++#
++# Automatically generated make config: don't edit
++# Linux kernel version: 2.6.32.2
++# Fri Jan 29 20:04:28 2010
++#
++CONFIG_ARM=y
++CONFIG_SYS_SUPPORTS_APM_EMULATION=y
++CONFIG_GENERIC_GPIO=y
++CONFIG_GENERIC_TIME=y
++CONFIG_GENERIC_CLOCKEVENTS=y
++CONFIG_GENERIC_HARDIRQS=y
++CONFIG_STACKTRACE_SUPPORT=y
++CONFIG_HAVE_LATENCYTOP_SUPPORT=y
++CONFIG_LOCKDEP_SUPPORT=y
++CONFIG_TRACE_IRQFLAGS_SUPPORT=y
++CONFIG_HARDIRQS_SW_RESEND=y
++CONFIG_GENERIC_IRQ_PROBE=y
++CONFIG_RWSEM_GENERIC_SPINLOCK=y
++CONFIG_GENERIC_HWEIGHT=y
++CONFIG_GENERIC_CALIBRATE_DELAY=y
++CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
++CONFIG_VECTORS_BASE=0xffff0000
++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
++CONFIG_CONSTRUCTORS=y
++
++#
++# General setup
++#
++CONFIG_EXPERIMENTAL=y
++CONFIG_BROKEN_ON_SMP=y
++CONFIG_INIT_ENV_ARG_LIMIT=32
++CONFIG_LOCALVERSION=""
++# CONFIG_LOCALVERSION_AUTO is not set
++CONFIG_HAVE_KERNEL_GZIP=y
++CONFIG_HAVE_KERNEL_BZIP2=y
++CONFIG_HAVE_KERNEL_LZMA=y
++CONFIG_KERNEL_GZIP=y
++# CONFIG_KERNEL_BZIP2 is not set
++# CONFIG_KERNEL_LZMA is not set
++CONFIG_SWAP=y
++CONFIG_SYSVIPC=y
++CONFIG_SYSVIPC_SYSCTL=y
++# CONFIG_POSIX_MQUEUE is not set
++# CONFIG_BSD_PROCESS_ACCT is not set
++# CONFIG_TASKSTATS is not set
++# CONFIG_AUDIT is not set
++
++#
++# RCU Subsystem
++#
++CONFIG_TREE_RCU=y
++# CONFIG_TREE_PREEMPT_RCU is not set
++# CONFIG_RCU_TRACE is not set
++CONFIG_RCU_FANOUT=32
++# CONFIG_RCU_FANOUT_EXACT is not set
++# CONFIG_TREE_RCU_TRACE is not set
++CONFIG_IKCONFIG=m
++CONFIG_IKCONFIG_PROC=y
++CONFIG_LOG_BUF_SHIFT=14
++# CONFIG_GROUP_SCHED is not set
++# CONFIG_CGROUPS is not set
++# CONFIG_SYSFS_DEPRECATED_V2 is not set
++# CONFIG_RELAY is not set
++# CONFIG_NAMESPACES is not set
++CONFIG_BLK_DEV_INITRD=y
++CONFIG_INITRAMFS_SOURCE=""
++CONFIG_RD_GZIP=y
++# CONFIG_RD_BZIP2 is not set
++# CONFIG_RD_LZMA is not set
++CONFIG_CC_OPTIMIZE_FOR_SIZE=y
++CONFIG_SYSCTL=y
++CONFIG_ANON_INODES=y
++CONFIG_EMBEDDED=y
++CONFIG_UID16=y
++CONFIG_SYSCTL_SYSCALL=y
++CONFIG_KALLSYMS=y
++# CONFIG_KALLSYMS_EXTRA_PASS is not set
++CONFIG_HOTPLUG=y
++CONFIG_PRINTK=y
++CONFIG_BUG=y
++CONFIG_ELF_CORE=y
++CONFIG_BASE_FULL=y
++CONFIG_FUTEX=y
++CONFIG_EPOLL=y
++CONFIG_SIGNALFD=y
++CONFIG_TIMERFD=y
++CONFIG_EVENTFD=y
++CONFIG_SHMEM=y
++CONFIG_AIO=y
++
++#
++# Kernel Performance Events And Counters
++#
++CONFIG_VM_EVENT_COUNTERS=y
++CONFIG_COMPAT_BRK=y
++CONFIG_SLAB=y
++# CONFIG_SLUB is not set
++# CONFIG_SLOB is not set
++# CONFIG_PROFILING is not set
++CONFIG_HAVE_OPROFILE=y
++# CONFIG_KPROBES is not set
++CONFIG_HAVE_KPROBES=y
++CONFIG_HAVE_KRETPROBES=y
++CONFIG_HAVE_CLK=y
++
++#
++# GCOV-based kernel profiling
++#
++# CONFIG_GCOV_KERNEL is not set
++CONFIG_SLOW_WORK=y
++# CONFIG_SLOW_WORK_DEBUG is not set
++CONFIG_HAVE_GENERIC_DMA_COHERENT=y
++CONFIG_SLABINFO=y
++CONFIG_RT_MUTEXES=y
++CONFIG_BASE_SMALL=0
++CONFIG_MODULES=y
++# CONFIG_MODULE_FORCE_LOAD is not set
++CONFIG_MODULE_UNLOAD=y
++# CONFIG_MODULE_FORCE_UNLOAD is not set
++# CONFIG_MODVERSIONS is not set
++# CONFIG_MODULE_SRCVERSION_ALL is not set
++CONFIG_BLOCK=y
++CONFIG_LBDAF=y
++# CONFIG_BLK_DEV_BSG is not set
++# CONFIG_BLK_DEV_INTEGRITY is not set
++
++#
++# IO Schedulers
++#
++CONFIG_IOSCHED_NOOP=y
++CONFIG_IOSCHED_AS=y
++# CONFIG_IOSCHED_DEADLINE is not set
++# CONFIG_IOSCHED_CFQ is not set
++CONFIG_DEFAULT_AS=y
++# CONFIG_DEFAULT_DEADLINE is not set
++# CONFIG_DEFAULT_CFQ is not set
++# CONFIG_DEFAULT_NOOP is not set
++CONFIG_DEFAULT_IOSCHED="anticipatory"
++CONFIG_FREEZER=y
++
++#
++# System Type
++#
++CONFIG_MMU=y
++# CONFIG_ARCH_AAEC2000 is not set
++# CONFIG_ARCH_INTEGRATOR is not set
++# CONFIG_ARCH_REALVIEW is not set
++# CONFIG_ARCH_VERSATILE is not set
++CONFIG_ARCH_AT91=y
++# CONFIG_ARCH_CLPS711X is not set
++# CONFIG_ARCH_GEMINI is not set
++# CONFIG_ARCH_EBSA110 is not set
++# CONFIG_ARCH_EP93XX is not set
++# CONFIG_ARCH_FOOTBRIDGE is not set
++# CONFIG_ARCH_MXC is not set
++# CONFIG_ARCH_STMP3XXX is not set
++# CONFIG_ARCH_NETX is not set
++# CONFIG_ARCH_H720X is not set
++# CONFIG_ARCH_NOMADIK is not set
++# CONFIG_ARCH_IOP13XX is not set
++# CONFIG_ARCH_IOP32X is not set
++# CONFIG_ARCH_IOP33X is not set
++# CONFIG_ARCH_IXP23XX is not set
++# CONFIG_ARCH_IXP2000 is not set
++# CONFIG_ARCH_IXP4XX is not set
++# CONFIG_ARCH_L7200 is not set
++# CONFIG_ARCH_KIRKWOOD is not set
++# CONFIG_ARCH_LOKI is not set
++# CONFIG_ARCH_MV78XX0 is not set
++# CONFIG_ARCH_ORION5X is not set
++# CONFIG_ARCH_MMP is not set
++# CONFIG_ARCH_KS8695 is not set
++# CONFIG_ARCH_NS9XXX is not set
++# CONFIG_ARCH_W90X900 is not set
++# CONFIG_ARCH_PNX4008 is not set
++# CONFIG_ARCH_PXA is not set
++# CONFIG_ARCH_MSM is not set
++# CONFIG_ARCH_RPC is not set
++# CONFIG_ARCH_SA1100 is not set
++# CONFIG_ARCH_S3C2410 is not set
++# CONFIG_ARCH_S3C64XX is not set
++# CONFIG_ARCH_S5PC1XX is not set
++# CONFIG_ARCH_SHARK is not set
++# CONFIG_ARCH_LH7A40X is not set
++# CONFIG_ARCH_U300 is not set
++# CONFIG_ARCH_DAVINCI is not set
++# CONFIG_ARCH_OMAP is not set
++# CONFIG_ARCH_BCMRING is not set
++
++#
++# Atmel AT91 System-on-Chip
++#
++# CONFIG_ARCH_AT91RM9200 is not set
++# CONFIG_ARCH_AT91SAM9260 is not set
++# CONFIG_ARCH_AT91SAM9261 is not set
++# CONFIG_ARCH_AT91SAM9G10 is not set
++# CONFIG_ARCH_AT91SAM9263 is not set
++# CONFIG_ARCH_AT91SAM9RL is not set
++CONFIG_ARCH_AT91SAM9G20=y
++# CONFIG_ARCH_AT91SAM9G45 is not set
++# CONFIG_ARCH_AT91CAP9 is not set
++# CONFIG_ARCH_AT91X40 is not set
++CONFIG_AT91_PMC_UNIT=y
++
++#
++# AT91SAM9G20 Board Type
++#
++# CONFIG_MACH_AT91SAM9G20EK is not set
++# CONFIG_MACH_AT91SAM9G20EK_2MMC is not set
++# CONFIG_MACH_CPU9G20 is not set
++CONFIG_MACH_FOXG20=y
++
++#
++# AT91 Board Options
++#
++# CONFIG_MTD_AT91_DATAFLASH_CARD is not set
++# CONFIG_MTD_NAND_ATMEL_BUSWIDTH_16 is not set
++
++#
++# AT91 Feature Selections
++#
++CONFIG_AT91_PROGRAMMABLE_CLOCKS=y
++CONFIG_AT91_SLOW_CLOCK=y
++CONFIG_AT91_TIMER_HZ=100
++# CONFIG_AT91_EARLY_DBGU is not set
++CONFIG_AT91_EARLY_USART0=y
++# CONFIG_AT91_EARLY_USART1 is not set
++# CONFIG_AT91_EARLY_USART2 is not set
++# CONFIG_AT91_EARLY_USART3 is not set
++# CONFIG_AT91_EARLY_USART4 is not set
++# CONFIG_AT91_EARLY_USART5 is not set
++
++#
++# Processor Type
++#
++CONFIG_CPU_32=y
++CONFIG_CPU_ARM926T=y
++CONFIG_CPU_32v5=y
++CONFIG_CPU_ABRT_EV5TJ=y
++CONFIG_CPU_PABRT_LEGACY=y
++CONFIG_CPU_CACHE_VIVT=y
++CONFIG_CPU_COPY_V4WB=y
++CONFIG_CPU_TLB_V4WBI=y
++CONFIG_CPU_CP15=y
++CONFIG_CPU_CP15_MMU=y
++
++#
++# Processor Features
++#
++CONFIG_ARM_THUMB=y
++# CONFIG_CPU_ICACHE_DISABLE is not set
++# CONFIG_CPU_DCACHE_DISABLE is not set
++# CONFIG_CPU_DCACHE_WRITETHROUGH is not set
++# CONFIG_CPU_CACHE_ROUND_ROBIN is not set
++CONFIG_ARM_L1_CACHE_SHIFT=5
++
++#
++# Bus support
++#
++# CONFIG_PCI_SYSCALL is not set
++# CONFIG_ARCH_SUPPORTS_MSI is not set
++# CONFIG_PCCARD is not set
++
++#
++# Kernel Features
++#
++CONFIG_TICK_ONESHOT=y
++CONFIG_NO_HZ=y
++CONFIG_HIGH_RES_TIMERS=y
++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
++CONFIG_VMSPLIT_3G=y
++# CONFIG_VMSPLIT_2G is not set
++# CONFIG_VMSPLIT_1G is not set
++CONFIG_PAGE_OFFSET=0xC0000000
++CONFIG_PREEMPT_NONE=y
++# CONFIG_PREEMPT_VOLUNTARY is not set
++# CONFIG_PREEMPT is not set
++CONFIG_HZ=100
++CONFIG_AEABI=y
++CONFIG_OABI_COMPAT=y
++# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
++# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
++# CONFIG_HIGHMEM is not set
++CONFIG_SELECT_MEMORY_MODEL=y
++CONFIG_FLATMEM_MANUAL=y
++# CONFIG_DISCONTIGMEM_MANUAL is not set
++# CONFIG_SPARSEMEM_MANUAL is not set
++CONFIG_FLATMEM=y
++CONFIG_FLAT_NODE_MEM_MAP=y
++CONFIG_PAGEFLAGS_EXTENDED=y
++CONFIG_SPLIT_PTLOCK_CPUS=4096
++# CONFIG_PHYS_ADDR_T_64BIT is not set
++CONFIG_ZONE_DMA_FLAG=0
++CONFIG_VIRT_TO_BUS=y
++CONFIG_HAVE_MLOCK=y
++CONFIG_HAVE_MLOCKED_PAGE_BIT=y
++# CONFIG_KSM is not set
++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
++CONFIG_LEDS=y
++CONFIG_LEDS_CPU=y
++CONFIG_ALIGNMENT_TRAP=y
++# CONFIG_UACCESS_WITH_MEMCPY is not set
++
++#
++# Boot options
++#
++CONFIG_ZBOOT_ROM_TEXT=0x0
++CONFIG_ZBOOT_ROM_BSS=0x0
++CONFIG_CMDLINE=""
++# CONFIG_XIP_KERNEL is not set
++# CONFIG_KEXEC is not set
++
++#
++# CPU Power Management
++#
++CONFIG_CPU_IDLE=y
++CONFIG_CPU_IDLE_GOV_LADDER=y
++CONFIG_CPU_IDLE_GOV_MENU=y
++
++#
++# Floating point emulation
++#
++
++#
++# At least one emulation must be selected
++#
++CONFIG_FPE_NWFPE=y
++# CONFIG_FPE_NWFPE_XP is not set
++# CONFIG_FPE_FASTFPE is not set
++# CONFIG_VFP is not set
++
++#
++# Userspace binary formats
++#
++CONFIG_BINFMT_ELF=y
++# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
++CONFIG_HAVE_AOUT=y
++CONFIG_BINFMT_AOUT=m
++CONFIG_BINFMT_MISC=m
++
++#
++# Power management options
++#
++CONFIG_PM=y
++# CONFIG_PM_DEBUG is not set
++CONFIG_PM_SLEEP=y
++CONFIG_SUSPEND=y
++CONFIG_SUSPEND_FREEZER=y
++CONFIG_APM_EMULATION=m
++# CONFIG_PM_RUNTIME is not set
++CONFIG_ARCH_SUSPEND_POSSIBLE=y
++CONFIG_NET=y
++
++#
++# Networking options
++#
++CONFIG_PACKET=y
++# CONFIG_PACKET_MMAP is not set
++CONFIG_UNIX=y
++CONFIG_XFRM=y
++CONFIG_XFRM_USER=m
++# CONFIG_XFRM_SUB_POLICY is not set
++# CONFIG_XFRM_MIGRATE is not set
++# CONFIG_XFRM_STATISTICS is not set
++CONFIG_XFRM_IPCOMP=m
++CONFIG_NET_KEY=m
++# CONFIG_NET_KEY_MIGRATE is not set
++CONFIG_INET=y
++# CONFIG_IP_MULTICAST is not set
++CONFIG_IP_ADVANCED_ROUTER=y
++CONFIG_ASK_IP_FIB_HASH=y
++# CONFIG_IP_FIB_TRIE is not set
++CONFIG_IP_FIB_HASH=y
++# CONFIG_IP_MULTIPLE_TABLES is not set
++# CONFIG_IP_ROUTE_MULTIPATH is not set
++# CONFIG_IP_ROUTE_VERBOSE is not set
++CONFIG_IP_PNP=y
++# CONFIG_IP_PNP_DHCP is not set
++CONFIG_IP_PNP_BOOTP=y
++# CONFIG_IP_PNP_RARP is not set
++CONFIG_NET_IPIP=m
++CONFIG_NET_IPGRE=m
++# CONFIG_ARPD is not set
++# CONFIG_SYN_COOKIES is not set
++CONFIG_INET_AH=m
++CONFIG_INET_ESP=m
++CONFIG_INET_IPCOMP=m
++CONFIG_INET_XFRM_TUNNEL=m
++CONFIG_INET_TUNNEL=m
++CONFIG_INET_XFRM_MODE_TRANSPORT=m
++CONFIG_INET_XFRM_MODE_TUNNEL=m
++CONFIG_INET_XFRM_MODE_BEET=m
++# CONFIG_INET_LRO is not set
++CONFIG_INET_DIAG=y
++CONFIG_INET_TCP_DIAG=y
++CONFIG_TCP_CONG_ADVANCED=y
++CONFIG_TCP_CONG_BIC=m
++CONFIG_TCP_CONG_CUBIC=y
++CONFIG_TCP_CONG_WESTWOOD=m
++CONFIG_TCP_CONG_HTCP=m
++CONFIG_TCP_CONG_HSTCP=m
++CONFIG_TCP_CONG_HYBLA=m
++CONFIG_TCP_CONG_VEGAS=m
++CONFIG_TCP_CONG_SCALABLE=m
++CONFIG_TCP_CONG_LP=m
++CONFIG_TCP_CONG_VENO=m
++CONFIG_TCP_CONG_YEAH=m
++CONFIG_TCP_CONG_ILLINOIS=m
++# CONFIG_DEFAULT_BIC is not set
++CONFIG_DEFAULT_CUBIC=y
++# CONFIG_DEFAULT_HTCP is not set
++# CONFIG_DEFAULT_VEGAS is not set
++# CONFIG_DEFAULT_WESTWOOD is not set
++# CONFIG_DEFAULT_RENO is not set
++CONFIG_DEFAULT_TCP_CONG="cubic"
++# CONFIG_TCP_MD5SIG is not set
++CONFIG_IPV6=m
++# CONFIG_IPV6_PRIVACY is not set
++# CONFIG_IPV6_ROUTER_PREF is not set
++# CONFIG_IPV6_OPTIMISTIC_DAD is not set
++CONFIG_INET6_AH=m
++CONFIG_INET6_ESP=m
++CONFIG_INET6_IPCOMP=m
++CONFIG_IPV6_MIP6=m
++CONFIG_INET6_XFRM_TUNNEL=m
++CONFIG_INET6_TUNNEL=m
++CONFIG_INET6_XFRM_MODE_TRANSPORT=m
++CONFIG_INET6_XFRM_MODE_TUNNEL=m
++CONFIG_INET6_XFRM_MODE_BEET=m
++CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION=m
++CONFIG_IPV6_SIT=m
++CONFIG_IPV6_NDISC_NODETYPE=y
++CONFIG_IPV6_TUNNEL=m
++# CONFIG_IPV6_MULTIPLE_TABLES is not set
++# CONFIG_IPV6_MROUTE is not set
++# CONFIG_NETWORK_SECMARK is not set
++CONFIG_NETFILTER=y
++# CONFIG_NETFILTER_DEBUG is not set
++CONFIG_NETFILTER_ADVANCED=y
++CONFIG_BRIDGE_NETFILTER=y
++
++#
++# Core Netfilter Configuration
++#
++CONFIG_NETFILTER_NETLINK=m
++CONFIG_NETFILTER_NETLINK_QUEUE=m
++CONFIG_NETFILTER_NETLINK_LOG=m
++CONFIG_NF_CONNTRACK=m
++CONFIG_NF_CT_ACCT=y
++CONFIG_NF_CONNTRACK_MARK=y
++# CONFIG_NF_CONNTRACK_EVENTS is not set
++CONFIG_NF_CT_PROTO_DCCP=m
++CONFIG_NF_CT_PROTO_GRE=m
++CONFIG_NF_CT_PROTO_SCTP=m
++CONFIG_NF_CT_PROTO_UDPLITE=m
++CONFIG_NF_CONNTRACK_AMANDA=m
++CONFIG_NF_CONNTRACK_FTP=m
++CONFIG_NF_CONNTRACK_H323=m
++CONFIG_NF_CONNTRACK_IRC=m
++CONFIG_NF_CONNTRACK_NETBIOS_NS=m
++CONFIG_NF_CONNTRACK_PPTP=m
++CONFIG_NF_CONNTRACK_SANE=m
++CONFIG_NF_CONNTRACK_SIP=m
++CONFIG_NF_CONNTRACK_TFTP=m
++CONFIG_NF_CT_NETLINK=m
++# CONFIG_NETFILTER_TPROXY is not set
++CONFIG_NETFILTER_XTABLES=m
++CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m
++CONFIG_NETFILTER_XT_TARGET_CONNMARK=m
++# CONFIG_NETFILTER_XT_TARGET_DSCP is not set
++CONFIG_NETFILTER_XT_TARGET_HL=m
++CONFIG_NETFILTER_XT_TARGET_LED=m
++CONFIG_NETFILTER_XT_TARGET_MARK=m
++CONFIG_NETFILTER_XT_TARGET_NFLOG=m
++CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m
++# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set
++CONFIG_NETFILTER_XT_TARGET_RATEEST=m
++# CONFIG_NETFILTER_XT_TARGET_TRACE is not set
++CONFIG_NETFILTER_XT_TARGET_TCPMSS=m
++# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set
++CONFIG_NETFILTER_XT_MATCH_CLUSTER=m
++CONFIG_NETFILTER_XT_MATCH_COMMENT=m
++CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m
++CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m
++CONFIG_NETFILTER_XT_MATCH_CONNMARK=m
++CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m
++CONFIG_NETFILTER_XT_MATCH_DCCP=m
++CONFIG_NETFILTER_XT_MATCH_DSCP=m
++CONFIG_NETFILTER_XT_MATCH_ESP=m
++CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m
++CONFIG_NETFILTER_XT_MATCH_HELPER=m
++CONFIG_NETFILTER_XT_MATCH_HL=m
++CONFIG_NETFILTER_XT_MATCH_IPRANGE=m
++CONFIG_NETFILTER_XT_MATCH_LENGTH=m
++CONFIG_NETFILTER_XT_MATCH_LIMIT=m
++CONFIG_NETFILTER_XT_MATCH_MAC=m
++CONFIG_NETFILTER_XT_MATCH_MARK=m
++CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m
++CONFIG_NETFILTER_XT_MATCH_OWNER=m
++CONFIG_NETFILTER_XT_MATCH_POLICY=m
++# CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set
++CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m
++CONFIG_NETFILTER_XT_MATCH_QUOTA=m
++CONFIG_NETFILTER_XT_MATCH_RATEEST=m
++CONFIG_NETFILTER_XT_MATCH_REALM=m
++CONFIG_NETFILTER_XT_MATCH_RECENT=m
++# CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT is not set
++# CONFIG_NETFILTER_XT_MATCH_SCTP is not set
++CONFIG_NETFILTER_XT_MATCH_STATE=m
++CONFIG_NETFILTER_XT_MATCH_STATISTIC=m
++CONFIG_NETFILTER_XT_MATCH_STRING=m
++CONFIG_NETFILTER_XT_MATCH_TCPMSS=m
++CONFIG_NETFILTER_XT_MATCH_TIME=m
++CONFIG_NETFILTER_XT_MATCH_U32=m
++CONFIG_NETFILTER_XT_MATCH_OSF=m
++CONFIG_IP_VS=m
++# CONFIG_IP_VS_IPV6 is not set
++# CONFIG_IP_VS_DEBUG is not set
++CONFIG_IP_VS_TAB_BITS=12
++
++#
++# IPVS transport protocol load balancing support
++#
++CONFIG_IP_VS_PROTO_TCP=y
++CONFIG_IP_VS_PROTO_UDP=y
++CONFIG_IP_VS_PROTO_AH_ESP=y
++CONFIG_IP_VS_PROTO_ESP=y
++CONFIG_IP_VS_PROTO_AH=y
++
++#
++# IPVS scheduler
++#
++CONFIG_IP_VS_RR=m
++CONFIG_IP_VS_WRR=m
++CONFIG_IP_VS_LC=m
++CONFIG_IP_VS_WLC=m
++CONFIG_IP_VS_LBLC=m
++CONFIG_IP_VS_LBLCR=m
++CONFIG_IP_VS_DH=m
++CONFIG_IP_VS_SH=m
++CONFIG_IP_VS_SED=m
++CONFIG_IP_VS_NQ=m
++
++#
++# IPVS application helper
++#
++CONFIG_IP_VS_FTP=m
++
++#
++# IP: Netfilter Configuration
++#
++CONFIG_NF_DEFRAG_IPV4=m
++CONFIG_NF_CONNTRACK_IPV4=m
++CONFIG_NF_CONNTRACK_PROC_COMPAT=y
++CONFIG_IP_NF_QUEUE=m
++CONFIG_IP_NF_IPTABLES=m
++CONFIG_IP_NF_MATCH_ADDRTYPE=m
++CONFIG_IP_NF_MATCH_AH=m
++CONFIG_IP_NF_MATCH_ECN=m
++CONFIG_IP_NF_MATCH_TTL=m
++CONFIG_IP_NF_FILTER=m
++CONFIG_IP_NF_TARGET_REJECT=m
++CONFIG_IP_NF_TARGET_LOG=m
++CONFIG_IP_NF_TARGET_ULOG=m
++CONFIG_NF_NAT=m
++CONFIG_NF_NAT_NEEDED=y
++CONFIG_IP_NF_TARGET_MASQUERADE=m
++CONFIG_IP_NF_TARGET_NETMAP=m
++CONFIG_IP_NF_TARGET_REDIRECT=m
++CONFIG_NF_NAT_SNMP_BASIC=m
++CONFIG_NF_NAT_PROTO_DCCP=m
++CONFIG_NF_NAT_PROTO_GRE=m
++CONFIG_NF_NAT_PROTO_UDPLITE=m
++CONFIG_NF_NAT_PROTO_SCTP=m
++CONFIG_NF_NAT_FTP=m
++CONFIG_NF_NAT_IRC=m
++CONFIG_NF_NAT_TFTP=m
++CONFIG_NF_NAT_AMANDA=m
++CONFIG_NF_NAT_PPTP=m
++CONFIG_NF_NAT_H323=m
++CONFIG_NF_NAT_SIP=m
++CONFIG_IP_NF_MANGLE=m
++CONFIG_IP_NF_TARGET_CLUSTERIP=m
++CONFIG_IP_NF_TARGET_ECN=m
++CONFIG_IP_NF_TARGET_TTL=m
++CONFIG_IP_NF_RAW=m
++CONFIG_IP_NF_ARPTABLES=m
++CONFIG_IP_NF_ARPFILTER=m
++CONFIG_IP_NF_ARP_MANGLE=m
++
++#
++# IPv6: Netfilter Configuration
++#
++CONFIG_NF_CONNTRACK_IPV6=m
++CONFIG_IP6_NF_QUEUE=m
++CONFIG_IP6_NF_IPTABLES=m
++CONFIG_IP6_NF_MATCH_AH=m
++CONFIG_IP6_NF_MATCH_EUI64=m
++CONFIG_IP6_NF_MATCH_FRAG=m
++CONFIG_IP6_NF_MATCH_OPTS=m
++CONFIG_IP6_NF_MATCH_HL=m
++CONFIG_IP6_NF_MATCH_IPV6HEADER=m
++CONFIG_IP6_NF_MATCH_MH=m
++CONFIG_IP6_NF_MATCH_RT=m
++CONFIG_IP6_NF_TARGET_HL=m
++CONFIG_IP6_NF_TARGET_LOG=m
++CONFIG_IP6_NF_FILTER=m
++CONFIG_IP6_NF_TARGET_REJECT=m
++CONFIG_IP6_NF_MANGLE=m
++CONFIG_IP6_NF_RAW=m
++# CONFIG_BRIDGE_NF_EBTABLES is not set
++# CONFIG_IP_DCCP is not set
++CONFIG_IP_SCTP=m
++# CONFIG_SCTP_DBG_MSG is not set
++# CONFIG_SCTP_DBG_OBJCNT is not set
++# CONFIG_SCTP_HMAC_NONE is not set
++# CONFIG_SCTP_HMAC_SHA1 is not set
++CONFIG_SCTP_HMAC_MD5=y
++# CONFIG_RDS is not set
++# CONFIG_TIPC is not set
++CONFIG_ATM=m
++CONFIG_ATM_CLIP=m
++# CONFIG_ATM_CLIP_NO_ICMP is not set
++CONFIG_ATM_LANE=m
++CONFIG_ATM_MPOA=m
++CONFIG_ATM_BR2684=m
++# CONFIG_ATM_BR2684_IPFILTER is not set
++CONFIG_STP=m
++CONFIG_BRIDGE=m
++# CONFIG_NET_DSA is not set
++CONFIG_VLAN_8021Q=m
++# CONFIG_VLAN_8021Q_GVRP is not set
++# CONFIG_DECNET is not set
++CONFIG_LLC=m
++CONFIG_LLC2=m
++CONFIG_IPX=m
++# CONFIG_IPX_INTERN is not set
++CONFIG_ATALK=m
++CONFIG_DEV_APPLETALK=m
++CONFIG_IPDDP=m
++# CONFIG_IPDDP_ENCAP is not set
++# CONFIG_IPDDP_DECAP is not set
++# CONFIG_X25 is not set
++# CONFIG_LAPB is not set
++# CONFIG_ECONET is not set
++CONFIG_WAN_ROUTER=m
++CONFIG_PHONET=m
++CONFIG_IEEE802154=m
++CONFIG_NET_SCHED=y
++
++#
++# Queueing/Scheduling
++#
++CONFIG_NET_SCH_CBQ=m
++CONFIG_NET_SCH_HTB=m
++CONFIG_NET_SCH_HFSC=m
++CONFIG_NET_SCH_ATM=m
++CONFIG_NET_SCH_PRIO=m
++CONFIG_NET_SCH_MULTIQ=m
++CONFIG_NET_SCH_RED=m
++CONFIG_NET_SCH_SFQ=m
++CONFIG_NET_SCH_TEQL=m
++CONFIG_NET_SCH_TBF=m
++CONFIG_NET_SCH_GRED=m
++CONFIG_NET_SCH_DSMARK=m
++CONFIG_NET_SCH_NETEM=m
++CONFIG_NET_SCH_DRR=m
++
++#
++# Classification
++#
++CONFIG_NET_CLS=y
++CONFIG_NET_CLS_BASIC=m
++CONFIG_NET_CLS_TCINDEX=m
++CONFIG_NET_CLS_ROUTE4=m
++CONFIG_NET_CLS_ROUTE=y
++CONFIG_NET_CLS_FW=m
++CONFIG_NET_CLS_U32=m
++# CONFIG_CLS_U32_PERF is not set
++# CONFIG_CLS_U32_MARK is not set
++CONFIG_NET_CLS_RSVP=m
++CONFIG_NET_CLS_RSVP6=m
++CONFIG_NET_CLS_FLOW=m
++# CONFIG_NET_EMATCH is not set
++# CONFIG_NET_CLS_ACT is not set
++# CONFIG_NET_CLS_IND is not set
++CONFIG_NET_SCH_FIFO=y
++# CONFIG_DCB is not set
++
++#
++# Network testing
++#
++# CONFIG_NET_PKTGEN is not set
++CONFIG_HAMRADIO=y
++
++#
++# Packet Radio protocols
++#
++CONFIG_AX25=m
++CONFIG_AX25_DAMA_SLAVE=y
++CONFIG_NETROM=m
++CONFIG_ROSE=m
++
++#
++# AX.25 network device drivers
++#
++# CONFIG_MKISS is not set
++# CONFIG_6PACK is not set
++# CONFIG_BPQETHER is not set
++# CONFIG_BAYCOM_SER_FDX is not set
++# CONFIG_BAYCOM_SER_HDX is not set
++# CONFIG_BAYCOM_PAR is not set
++# CONFIG_BAYCOM_EPP is not set
++# CONFIG_YAM is not set
++CONFIG_CAN=m
++CONFIG_CAN_RAW=m
++CONFIG_CAN_BCM=m
++
++#
++# CAN Device Drivers
++#
++# CONFIG_CAN_VCAN is not set
++# CONFIG_CAN_DEV is not set
++# CONFIG_CAN_DEBUG_DEVICES is not set
++CONFIG_IRDA=m
++
++#
++# IrDA protocols
++#
++CONFIG_IRLAN=m
++CONFIG_IRCOMM=m
++CONFIG_IRDA_ULTRA=y
++
++#
++# IrDA options
++#
++CONFIG_IRDA_CACHE_LAST_LSAP=y
++CONFIG_IRDA_FAST_RR=y
++# CONFIG_IRDA_DEBUG is not set
++
++#
++# Infrared-port device drivers
++#
++
++#
++# SIR device drivers
++#
++CONFIG_IRTTY_SIR=m
++
++#
++# Dongle support
++#
++CONFIG_DONGLE=y
++CONFIG_ESI_DONGLE=m
++CONFIG_ACTISYS_DONGLE=m
++CONFIG_TEKRAM_DONGLE=m
++CONFIG_TOIM3232_DONGLE=m
++CONFIG_LITELINK_DONGLE=m
++CONFIG_MA600_DONGLE=m
++CONFIG_GIRBIL_DONGLE=m
++CONFIG_MCP2120_DONGLE=m
++CONFIG_OLD_BELKIN_DONGLE=m
++CONFIG_ACT200L_DONGLE=m
++CONFIG_KINGSUN_DONGLE=m
++CONFIG_KSDAZZLE_DONGLE=m
++CONFIG_KS959_DONGLE=m
++
++#
++# FIR device drivers
++#
++CONFIG_USB_IRDA=m
++CONFIG_SIGMATEL_FIR=m
++CONFIG_MCS_FIR=m
++CONFIG_BT=m
++CONFIG_BT_L2CAP=m
++CONFIG_BT_SCO=m
++CONFIG_BT_RFCOMM=m
++CONFIG_BT_RFCOMM_TTY=y
++CONFIG_BT_BNEP=m
++# CONFIG_BT_BNEP_MC_FILTER is not set
++# CONFIG_BT_BNEP_PROTO_FILTER is not set
++CONFIG_BT_HIDP=m
++
++#
++# Bluetooth device drivers
++#
++CONFIG_BT_HCIBTUSB=m
++CONFIG_BT_HCIBTSDIO=m
++CONFIG_BT_HCIUART=m
++CONFIG_BT_HCIUART_H4=y
++CONFIG_BT_HCIUART_BCSP=y
++CONFIG_BT_HCIUART_LL=y
++CONFIG_BT_HCIBCM203X=m
++CONFIG_BT_HCIBPA10X=m
++CONFIG_BT_HCIBFUSB=m
++CONFIG_BT_HCIVHCI=m
++CONFIG_BT_MRVL=m
++CONFIG_BT_MRVL_SDIO=m
++CONFIG_AF_RXRPC=m
++# CONFIG_AF_RXRPC_DEBUG is not set
++# CONFIG_RXKAD is not set
++CONFIG_WIRELESS=y
++CONFIG_CFG80211=m
++# CONFIG_NL80211_TESTMODE is not set
++# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set
++# CONFIG_CFG80211_REG_DEBUG is not set
++CONFIG_CFG80211_DEFAULT_PS=y
++CONFIG_CFG80211_DEFAULT_PS_VALUE=1
++# CONFIG_CFG80211_DEBUGFS is not set
++# CONFIG_WIRELESS_OLD_REGULATORY is not set
++CONFIG_WIRELESS_EXT=y
++CONFIG_WIRELESS_EXT_SYSFS=y
++CONFIG_LIB80211=m
++CONFIG_LIB80211_CRYPT_WEP=m
++CONFIG_LIB80211_CRYPT_CCMP=m
++CONFIG_LIB80211_CRYPT_TKIP=m
++# CONFIG_LIB80211_DEBUG is not set
++CONFIG_MAC80211=m
++# CONFIG_MAC80211_RC_PID is not set
++CONFIG_MAC80211_RC_MINSTREL=y
++# CONFIG_MAC80211_RC_DEFAULT_PID is not set
++CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y
++CONFIG_MAC80211_RC_DEFAULT="minstrel"
++# CONFIG_MAC80211_MESH is not set
++# CONFIG_MAC80211_LEDS is not set
++# CONFIG_MAC80211_DEBUGFS is not set
++# CONFIG_MAC80211_DEBUG_MENU is not set
++CONFIG_WIMAX=m
++CONFIG_WIMAX_DEBUG_LEVEL=8
++CONFIG_RFKILL=m
++CONFIG_RFKILL_LEDS=y
++# CONFIG_RFKILL_INPUT is not set
++# CONFIG_NET_9P is not set
++
++#
++# Device Drivers
++#
++
++#
++# Generic Driver Options
++#
++CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
++# CONFIG_DEVTMPFS is not set
++CONFIG_STANDALONE=y
++# CONFIG_PREVENT_FIRMWARE_BUILD is not set
++CONFIG_FW_LOADER=m
++CONFIG_FIRMWARE_IN_KERNEL=y
++CONFIG_EXTRA_FIRMWARE=""
++# CONFIG_SYS_HYPERVISOR is not set
++CONFIG_CONNECTOR=m
++CONFIG_MTD=y
++# CONFIG_MTD_DEBUG is not set
++# CONFIG_MTD_TESTS is not set
++CONFIG_MTD_CONCAT=y
++CONFIG_MTD_PARTITIONS=y
++# CONFIG_MTD_REDBOOT_PARTS is not set
++CONFIG_MTD_CMDLINE_PARTS=y
++# CONFIG_MTD_AFS_PARTS is not set
++# CONFIG_MTD_AR7_PARTS is not set
++
++#
++# User Modules And Translation Layers
++#
++CONFIG_MTD_CHAR=y
++CONFIG_MTD_BLKDEVS=y
++CONFIG_MTD_BLOCK=y
++# CONFIG_FTL is not set
++# CONFIG_NFTL is not set
++# CONFIG_INFTL is not set
++# CONFIG_RFD_FTL is not set
++# CONFIG_SSFDC is not set
++# CONFIG_MTD_OOPS is not set
++
++#
++# RAM/ROM/Flash chip drivers
++#
++# CONFIG_MTD_CFI is not set
++# CONFIG_MTD_JEDECPROBE is not set
++CONFIG_MTD_MAP_BANK_WIDTH_1=y
++CONFIG_MTD_MAP_BANK_WIDTH_2=y
++CONFIG_MTD_MAP_BANK_WIDTH_4=y
++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
++CONFIG_MTD_CFI_I1=y
++CONFIG_MTD_CFI_I2=y
++# CONFIG_MTD_CFI_I4 is not set
++# CONFIG_MTD_CFI_I8 is not set
++# CONFIG_MTD_RAM is not set
++# CONFIG_MTD_ROM is not set
++# CONFIG_MTD_ABSENT is not set
++
++#
++# Mapping drivers for chip access
++#
++# CONFIG_MTD_COMPLEX_MAPPINGS is not set
++# CONFIG_MTD_PLATRAM is not set
++
++#
++# Self-contained MTD device drivers
++#
++CONFIG_MTD_DATAFLASH=y
++# CONFIG_MTD_DATAFLASH_WRITE_VERIFY is not set
++# CONFIG_MTD_DATAFLASH_OTP is not set
++# CONFIG_MTD_M25P80 is not set
++# CONFIG_MTD_SST25L is not set
++# CONFIG_MTD_SLRAM is not set
++# CONFIG_MTD_PHRAM is not set
++# CONFIG_MTD_MTDRAM is not set
++# CONFIG_MTD_BLOCK2MTD is not set
++
++#
++# Disk-On-Chip Device Drivers
++#
++# CONFIG_MTD_DOC2000 is not set
++# CONFIG_MTD_DOC2001 is not set
++# CONFIG_MTD_DOC2001PLUS is not set
++CONFIG_MTD_NAND=y
++# CONFIG_MTD_NAND_VERIFY_WRITE is not set
++# CONFIG_MTD_NAND_ECC_SMC is not set
++# CONFIG_MTD_NAND_MUSEUM_IDS is not set
++# CONFIG_MTD_NAND_GPIO is not set
++CONFIG_MTD_NAND_IDS=y
++# CONFIG_MTD_NAND_DISKONCHIP is not set
++CONFIG_MTD_NAND_ATMEL=y
++# CONFIG_MTD_NAND_ATMEL_ECC_HW is not set
++CONFIG_MTD_NAND_ATMEL_ECC_SOFT=y
++# CONFIG_MTD_NAND_ATMEL_ECC_NONE is not set
++# CONFIG_MTD_NAND_NANDSIM is not set
++# CONFIG_MTD_NAND_PLATFORM is not set
++# CONFIG_MTD_ALAUDA is not set
++# CONFIG_MTD_ONENAND is not set
++
++#
++# LPDDR flash memory drivers
++#
++# CONFIG_MTD_LPDDR is not set
++
++#
++# UBI - Unsorted block images
++#
++# CONFIG_MTD_UBI is not set
++CONFIG_PARPORT=m
++CONFIG_PARPORT_PC=m
++# CONFIG_PARPORT_PC_FIFO is not set
++# CONFIG_PARPORT_PC_SUPERIO is not set
++# CONFIG_PARPORT_GSC is not set
++CONFIG_PARPORT_AX88796=m
++# CONFIG_PARPORT_1284 is not set
++CONFIG_PARPORT_NOT_PC=y
++CONFIG_BLK_DEV=y
++# CONFIG_PARIDE is not set
++# CONFIG_BLK_DEV_COW_COMMON is not set
++CONFIG_BLK_DEV_LOOP=y
++# CONFIG_BLK_DEV_CRYPTOLOOP is not set
++# CONFIG_BLK_DEV_NBD is not set
++# CONFIG_BLK_DEV_OSD is not set
++# CONFIG_BLK_DEV_UB is not set
++CONFIG_BLK_DEV_RAM=y
++CONFIG_BLK_DEV_RAM_COUNT=16
++CONFIG_BLK_DEV_RAM_SIZE=8192
++# CONFIG_BLK_DEV_XIP is not set
++# CONFIG_CDROM_PKTCDVD is not set
++# CONFIG_ATA_OVER_ETH is not set
++# CONFIG_MG_DISK is not set
++CONFIG_MISC_DEVICES=y
++CONFIG_ATMEL_TCLIB=y
++CONFIG_ATMEL_TCB_CLKSRC=y
++CONFIG_ATMEL_TCB_CLKSRC_BLOCK=0
++# CONFIG_ICS932S401 is not set
++CONFIG_ATMEL_SSC=y
++# CONFIG_ENCLOSURE_SERVICES is not set
++# CONFIG_ISL29003 is not set
++# CONFIG_C2PORT is not set
++
++#
++# EEPROM support
++#
++# CONFIG_EEPROM_AT24 is not set
++# CONFIG_EEPROM_AT25 is not set
++# CONFIG_EEPROM_LEGACY is not set
++# CONFIG_EEPROM_MAX6875 is not set
++CONFIG_EEPROM_93CX6=m
++CONFIG_HAVE_IDE=y
++# CONFIG_IDE is not set
++
++#
++# SCSI device support
++#
++# CONFIG_RAID_ATTRS is not set
++CONFIG_SCSI=y
++CONFIG_SCSI_DMA=y
++CONFIG_SCSI_TGT=y
++CONFIG_SCSI_NETLINK=y
++CONFIG_SCSI_PROC_FS=y
++
++#
++# SCSI support type (disk, tape, CD-ROM)
++#
++CONFIG_BLK_DEV_SD=y
++CONFIG_CHR_DEV_ST=m
++CONFIG_CHR_DEV_OSST=m
++CONFIG_BLK_DEV_SR=m
++# CONFIG_BLK_DEV_SR_VENDOR is not set
++CONFIG_CHR_DEV_SG=m
++CONFIG_CHR_DEV_SCH=m
++CONFIG_SCSI_MULTI_LUN=y
++# CONFIG_SCSI_CONSTANTS is not set
++# CONFIG_SCSI_LOGGING is not set
++# CONFIG_SCSI_SCAN_ASYNC is not set
++CONFIG_SCSI_WAIT_SCAN=m
++
++#
++# SCSI Transports
++#
++CONFIG_SCSI_SPI_ATTRS=m
++CONFIG_SCSI_FC_ATTRS=m
++# CONFIG_SCSI_FC_TGT_ATTRS is not set
++CONFIG_SCSI_ISCSI_ATTRS=m
++CONFIG_SCSI_SAS_ATTRS=m
++CONFIG_SCSI_SAS_LIBSAS=m
++# CONFIG_SCSI_SAS_ATA is not set
++CONFIG_SCSI_SAS_HOST_SMP=y
++CONFIG_SCSI_SAS_LIBSAS_DEBUG=y
++CONFIG_SCSI_SRP_ATTRS=m
++# CONFIG_SCSI_SRP_TGT_ATTRS is not set
++CONFIG_SCSI_LOWLEVEL=y
++CONFIG_ISCSI_TCP=m
++CONFIG_LIBFC=m
++CONFIG_LIBFCOE=m
++# CONFIG_SCSI_PPA is not set
++# CONFIG_SCSI_IMM is not set
++CONFIG_SCSI_DEBUG=m
++CONFIG_SCSI_DH=m
++CONFIG_SCSI_DH_RDAC=m
++CONFIG_SCSI_DH_HP_SW=m
++CONFIG_SCSI_DH_EMC=m
++CONFIG_SCSI_DH_ALUA=m
++CONFIG_SCSI_OSD_INITIATOR=m
++CONFIG_SCSI_OSD_ULD=m
++CONFIG_SCSI_OSD_DPRINT_SENSE=1
++# CONFIG_SCSI_OSD_DEBUG is not set
++CONFIG_ATA=m
++# CONFIG_ATA_NONSTANDARD is not set
++CONFIG_ATA_VERBOSE_ERROR=y
++CONFIG_SATA_PMP=y
++CONFIG_ATA_SFF=y
++CONFIG_SATA_MV=m
++CONFIG_PATA_PLATFORM=m
++CONFIG_PATA_AT91=m
++CONFIG_MD=y
++CONFIG_BLK_DEV_MD=m
++CONFIG_MD_LINEAR=m
++CONFIG_MD_RAID0=m
++CONFIG_MD_RAID1=m
++CONFIG_MD_RAID10=m
++CONFIG_MD_RAID456=m
++CONFIG_MD_RAID6_PQ=m
++CONFIG_ASYNC_RAID6_TEST=m
++CONFIG_MD_MULTIPATH=m
++CONFIG_MD_FAULTY=m
++CONFIG_BLK_DEV_DM=m
++# CONFIG_DM_DEBUG is not set
++CONFIG_DM_CRYPT=m
++CONFIG_DM_SNAPSHOT=m
++CONFIG_DM_MIRROR=m
++CONFIG_DM_LOG_USERSPACE=m
++CONFIG_DM_ZERO=m
++CONFIG_DM_MULTIPATH=m
++CONFIG_DM_MULTIPATH_QL=m
++CONFIG_DM_MULTIPATH_ST=m
++CONFIG_DM_DELAY=m
++# CONFIG_DM_UEVENT is not set
++CONFIG_NETDEVICES=y
++# CONFIG_DUMMY is not set
++# CONFIG_BONDING is not set
++# CONFIG_MACVLAN is not set
++# CONFIG_EQUALIZER is not set
++# CONFIG_TUN is not set
++# CONFIG_VETH is not set
++CONFIG_PHYLIB=y
++
++#
++# MII PHY device drivers
++#
++# CONFIG_MARVELL_PHY is not set
++# CONFIG_DAVICOM_PHY is not set
++# CONFIG_QSEMI_PHY is not set
++# CONFIG_LXT_PHY is not set
++# CONFIG_CICADA_PHY is not set
++# CONFIG_VITESSE_PHY is not set
++# CONFIG_SMSC_PHY is not set
++# CONFIG_BROADCOM_PHY is not set
++# CONFIG_ICPLUS_PHY is not set
++# CONFIG_REALTEK_PHY is not set
++# CONFIG_NATIONAL_PHY is not set
++# CONFIG_STE10XP is not set
++# CONFIG_LSI_ET1011C_PHY is not set
++# CONFIG_FIXED_PHY is not set
++# CONFIG_MDIO_BITBANG is not set
++CONFIG_NET_ETHERNET=y
++CONFIG_MII=y
++CONFIG_MACB=y
++# CONFIG_AX88796 is not set
++# CONFIG_SMC91X is not set
++# CONFIG_DM9000 is not set
++# CONFIG_ENC28J60 is not set
++# CONFIG_ETHOC is not set
++# CONFIG_SMC911X is not set
++# CONFIG_SMSC911X is not set
++# CONFIG_DNET is not set
++# CONFIG_IBM_NEW_EMAC_ZMII is not set
++# CONFIG_IBM_NEW_EMAC_RGMII is not set
++# CONFIG_IBM_NEW_EMAC_TAH is not set
++# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
++# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set
++# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
++# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
++# CONFIG_B44 is not set
++# CONFIG_KS8842 is not set
++# CONFIG_KS8851 is not set
++# CONFIG_KS8851_MLL is not set
++# CONFIG_NET_POCKET is not set
++# CONFIG_NETDEV_1000 is not set
++# CONFIG_NETDEV_10000 is not set
++CONFIG_WLAN=y
++# CONFIG_WLAN_PRE80211 is not set
++CONFIG_WLAN_80211=y
++CONFIG_LIBERTAS=m
++CONFIG_LIBERTAS_USB=m
++CONFIG_LIBERTAS_SDIO=m
++CONFIG_LIBERTAS_SPI=m
++# CONFIG_LIBERTAS_DEBUG is not set
++CONFIG_LIBERTAS_THINFIRM=m
++CONFIG_LIBERTAS_THINFIRM_USB=m
++CONFIG_AT76C50X_USB=m
++CONFIG_USB_ZD1201=m
++CONFIG_USB_NET_RNDIS_WLAN=m
++CONFIG_RTL8187=m
++CONFIG_MAC80211_HWSIM=m
++CONFIG_P54_COMMON=m
++CONFIG_P54_USB=m
++CONFIG_P54_SPI=m
++CONFIG_ATH_COMMON=m
++CONFIG_AR9170_USB=m
++CONFIG_HOSTAP=m
++CONFIG_HOSTAP_FIRMWARE=y
++CONFIG_HOSTAP_FIRMWARE_NVRAM=y
++CONFIG_B43=m
++# CONFIG_B43_SDIO is not set
++CONFIG_B43_PHY_LP=y
++CONFIG_B43_HWRNG=y
++# CONFIG_B43_DEBUG is not set
++CONFIG_B43LEGACY=m
++CONFIG_B43LEGACY_HWRNG=y
++CONFIG_B43LEGACY_DEBUG=y
++CONFIG_B43LEGACY_DMA=y
++CONFIG_B43LEGACY_PIO=y
++CONFIG_B43LEGACY_DMA_AND_PIO_MODE=y
++# CONFIG_B43LEGACY_DMA_MODE is not set
++# CONFIG_B43LEGACY_PIO_MODE is not set
++CONFIG_ZD1211RW=m
++# CONFIG_ZD1211RW_DEBUG is not set
++CONFIG_RT2X00=m
++CONFIG_RT2500USB=m
++CONFIG_RT73USB=m
++CONFIG_RT2800USB=m
++CONFIG_RT2X00_LIB_USB=m
++CONFIG_RT2X00_LIB=m
++CONFIG_RT2X00_LIB_HT=y
++CONFIG_RT2X00_LIB_FIRMWARE=y
++CONFIG_RT2X00_LIB_CRYPTO=y
++CONFIG_RT2X00_LIB_LEDS=y
++# CONFIG_RT2X00_DEBUG is not set
++# CONFIG_WL12XX is not set
++CONFIG_IWM=m
++# CONFIG_IWM_DEBUG is not set
++
++#
++# WiMAX Wireless Broadband devices
++#
++# CONFIG_WIMAX_I2400M_USB is not set
++# CONFIG_WIMAX_I2400M_SDIO is not set
++
++#
++# USB Network Adapters
++#
++# CONFIG_USB_CATC is not set
++# CONFIG_USB_KAWETH is not set
++# CONFIG_USB_PEGASUS is not set
++# CONFIG_USB_RTL8150 is not set
++CONFIG_USB_USBNET=m
++CONFIG_USB_NET_AX8817X=m
++CONFIG_USB_NET_CDCETHER=m
++# CONFIG_USB_NET_CDC_EEM is not set
++# CONFIG_USB_NET_DM9601 is not set
++# CONFIG_USB_NET_SMSC95XX is not set
++# CONFIG_USB_NET_GL620A is not set
++CONFIG_USB_NET_NET1080=m
++# CONFIG_USB_NET_PLUSB is not set
++# CONFIG_USB_NET_MCS7830 is not set
++CONFIG_USB_NET_RNDIS_HOST=m
++CONFIG_USB_NET_CDC_SUBSET=m
++# CONFIG_USB_ALI_M5632 is not set
++# CONFIG_USB_AN2720 is not set
++CONFIG_USB_BELKIN=y
++CONFIG_USB_ARMLINUX=y
++# CONFIG_USB_EPSON2888 is not set
++# CONFIG_USB_KC2190 is not set
++CONFIG_USB_NET_ZAURUS=m
++# CONFIG_USB_HSO is not set
++# CONFIG_USB_NET_INT51X1 is not set
++# CONFIG_USB_CDC_PHONET is not set
++# CONFIG_WAN is not set
++CONFIG_ATM_DRIVERS=y
++# CONFIG_ATM_DUMMY is not set
++# CONFIG_ATM_TCP is not set
++CONFIG_IEEE802154_DRIVERS=m
++# CONFIG_IEEE802154_FAKEHARD is not set
++# CONFIG_PLIP is not set
++# CONFIG_PPP is not set
++# CONFIG_SLIP is not set
++# CONFIG_NETCONSOLE is not set
++# CONFIG_NETPOLL is not set
++# CONFIG_NET_POLL_CONTROLLER is not set
++# CONFIG_ISDN is not set
++CONFIG_PHONE=m
++
++#
++# Input device support
++#
++CONFIG_INPUT=y
++CONFIG_INPUT_FF_MEMLESS=m
++CONFIG_INPUT_POLLDEV=m
++
++#
++# Userland interfaces
++#
++CONFIG_INPUT_MOUSEDEV=y
++# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
++CONFIG_INPUT_MOUSEDEV_SCREEN_X=320
++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=240
++CONFIG_INPUT_JOYDEV=m
++CONFIG_INPUT_EVDEV=y
++CONFIG_INPUT_EVBUG=m
++CONFIG_INPUT_APMPOWER=m
++
++#
++# Input Device Drivers
++#
++CONFIG_INPUT_KEYBOARD=y
++CONFIG_KEYBOARD_ADP5588=m
++CONFIG_KEYBOARD_ATKBD=m
++CONFIG_QT2160=m
++CONFIG_KEYBOARD_LKKBD=m
++CONFIG_KEYBOARD_GPIO=y
++CONFIG_KEYBOARD_MATRIX=m
++CONFIG_KEYBOARD_LM8323=m
++CONFIG_KEYBOARD_MAX7359=m
++CONFIG_KEYBOARD_NEWTON=m
++CONFIG_KEYBOARD_OPENCORES=m
++CONFIG_KEYBOARD_STOWAWAY=m
++CONFIG_KEYBOARD_SUNKBD=m
++CONFIG_KEYBOARD_XTKBD=m
++CONFIG_INPUT_MOUSE=y
++CONFIG_MOUSE_PS2=y
++CONFIG_MOUSE_PS2_ALPS=y
++CONFIG_MOUSE_PS2_LOGIPS2PP=y
++CONFIG_MOUSE_PS2_SYNAPTICS=y
++CONFIG_MOUSE_PS2_TRACKPOINT=y
++# CONFIG_MOUSE_PS2_ELANTECH is not set
++# CONFIG_MOUSE_PS2_SENTELIC is not set
++# CONFIG_MOUSE_PS2_TOUCHKIT is not set
++CONFIG_MOUSE_SERIAL=m
++CONFIG_MOUSE_APPLETOUCH=m
++CONFIG_MOUSE_BCM5974=m
++CONFIG_MOUSE_VSXXXAA=m
++CONFIG_MOUSE_GPIO=m
++CONFIG_MOUSE_SYNAPTICS_I2C=m
++CONFIG_INPUT_JOYSTICK=y
++CONFIG_JOYSTICK_ANALOG=m
++CONFIG_JOYSTICK_A3D=m
++CONFIG_JOYSTICK_ADI=m
++CONFIG_JOYSTICK_COBRA=m
++CONFIG_JOYSTICK_GF2K=m
++CONFIG_JOYSTICK_GRIP=m
++CONFIG_JOYSTICK_GRIP_MP=m
++CONFIG_JOYSTICK_GUILLEMOT=m
++CONFIG_JOYSTICK_INTERACT=m
++CONFIG_JOYSTICK_SIDEWINDER=m
++CONFIG_JOYSTICK_TMDC=m
++CONFIG_JOYSTICK_IFORCE=m
++# CONFIG_JOYSTICK_IFORCE_USB is not set
++# CONFIG_JOYSTICK_IFORCE_232 is not set
++CONFIG_JOYSTICK_WARRIOR=m
++CONFIG_JOYSTICK_MAGELLAN=m
++CONFIG_JOYSTICK_SPACEORB=m
++CONFIG_JOYSTICK_SPACEBALL=m
++CONFIG_JOYSTICK_STINGER=m
++CONFIG_JOYSTICK_TWIDJOY=m
++CONFIG_JOYSTICK_ZHENHUA=m
++CONFIG_JOYSTICK_DB9=m
++CONFIG_JOYSTICK_GAMECON=m
++CONFIG_JOYSTICK_TURBOGRAFX=m
++CONFIG_JOYSTICK_JOYDUMP=m
++CONFIG_JOYSTICK_XPAD=m
++# CONFIG_JOYSTICK_XPAD_FF is not set
++# CONFIG_JOYSTICK_XPAD_LEDS is not set
++CONFIG_JOYSTICK_WALKERA0701=m
++CONFIG_INPUT_TABLET=y
++CONFIG_TABLET_USB_ACECAD=m
++CONFIG_TABLET_USB_AIPTEK=m
++CONFIG_TABLET_USB_GTCO=m
++CONFIG_TABLET_USB_KBTAB=m
++CONFIG_TABLET_USB_WACOM=m
++CONFIG_INPUT_TOUCHSCREEN=y
++CONFIG_TOUCHSCREEN_ADS7846=m
++CONFIG_TOUCHSCREEN_AD7877=m
++CONFIG_TOUCHSCREEN_AD7879_I2C=m
++CONFIG_TOUCHSCREEN_AD7879=m
++CONFIG_TOUCHSCREEN_EETI=m
++CONFIG_TOUCHSCREEN_FUJITSU=m
++CONFIG_TOUCHSCREEN_GUNZE=m
++CONFIG_TOUCHSCREEN_ELO=m
++CONFIG_TOUCHSCREEN_WACOM_W8001=m
++CONFIG_TOUCHSCREEN_MCS5000=m
++CONFIG_TOUCHSCREEN_MTOUCH=m
++CONFIG_TOUCHSCREEN_INEXIO=m
++CONFIG_TOUCHSCREEN_MK712=m
++CONFIG_TOUCHSCREEN_PENMOUNT=m
++CONFIG_TOUCHSCREEN_TOUCHRIGHT=m
++CONFIG_TOUCHSCREEN_TOUCHWIN=m
++CONFIG_TOUCHSCREEN_USB_COMPOSITE=m
++CONFIG_TOUCHSCREEN_USB_EGALAX=y
++CONFIG_TOUCHSCREEN_USB_PANJIT=y
++CONFIG_TOUCHSCREEN_USB_3M=y
++CONFIG_TOUCHSCREEN_USB_ITM=y
++CONFIG_TOUCHSCREEN_USB_ETURBO=y
++CONFIG_TOUCHSCREEN_USB_GUNZE=y
++CONFIG_TOUCHSCREEN_USB_DMC_TSC10=y
++CONFIG_TOUCHSCREEN_USB_IRTOUCH=y
++CONFIG_TOUCHSCREEN_USB_IDEALTEK=y
++CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH=y
++CONFIG_TOUCHSCREEN_USB_GOTOP=y
++CONFIG_TOUCHSCREEN_USB_JASTEC=y
++CONFIG_TOUCHSCREEN_USB_E2I=y
++CONFIG_TOUCHSCREEN_TOUCHIT213=m
++CONFIG_TOUCHSCREEN_TSC2007=m
++CONFIG_TOUCHSCREEN_W90X900=m
++CONFIG_INPUT_MISC=y
++CONFIG_INPUT_ATI_REMOTE=m
++CONFIG_INPUT_ATI_REMOTE2=m
++CONFIG_INPUT_KEYSPAN_REMOTE=m
++CONFIG_INPUT_POWERMATE=m
++CONFIG_INPUT_YEALINK=m
++CONFIG_INPUT_CM109=m
++CONFIG_INPUT_UINPUT=m
++CONFIG_INPUT_GPIO_ROTARY_ENCODER=m
++
++#
++# Hardware I/O ports
++#
++CONFIG_SERIO=y
++CONFIG_SERIO_SERPORT=y
++# CONFIG_SERIO_PARKBD is not set
++CONFIG_SERIO_LIBPS2=y
++# CONFIG_SERIO_RAW is not set
++CONFIG_GAMEPORT=m
++# CONFIG_GAMEPORT_NS558 is not set
++# CONFIG_GAMEPORT_L4 is not set
++
++#
++# Character devices
++#
++CONFIG_VT=y
++CONFIG_CONSOLE_TRANSLATIONS=y
++CONFIG_VT_CONSOLE=y
++CONFIG_HW_CONSOLE=y
++# CONFIG_VT_HW_CONSOLE_BINDING is not set
++CONFIG_DEVKMEM=y
++# CONFIG_SERIAL_NONSTANDARD is not set
++
++#
++# Serial drivers
++#
++# CONFIG_SERIAL_8250 is not set
++
++#
++# Non-8250 serial port support
++#
++CONFIG_SERIAL_ATMEL=y
++CONFIG_SERIAL_ATMEL_CONSOLE=y
++CONFIG_SERIAL_ATMEL_PDC=y
++# CONFIG_SERIAL_ATMEL_TTYAT is not set
++# CONFIG_SERIAL_MAX3100 is not set
++CONFIG_SERIAL_CORE=y
++CONFIG_SERIAL_CORE_CONSOLE=y
++CONFIG_UNIX98_PTYS=y
++# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
++CONFIG_LEGACY_PTYS=y
++CONFIG_LEGACY_PTY_COUNT=16
++# CONFIG_PRINTER is not set
++# CONFIG_PPDEV is not set
++CONFIG_IPMI_HANDLER=m
++# CONFIG_IPMI_PANIC_EVENT is not set
++# CONFIG_IPMI_DEVICE_INTERFACE is not set
++# CONFIG_IPMI_SI is not set
++# CONFIG_IPMI_WATCHDOG is not set
++# CONFIG_IPMI_POWEROFF is not set
++CONFIG_HW_RANDOM=y
++# CONFIG_HW_RANDOM_TIMERIOMEM is not set
++CONFIG_NVRAM=m
++CONFIG_R3964=m
++CONFIG_RAW_DRIVER=m
++CONFIG_MAX_RAW_DEVS=256
++# CONFIG_TCG_TPM is not set
++CONFIG_I2C=m
++CONFIG_I2C_BOARDINFO=y
++CONFIG_I2C_COMPAT=y
++CONFIG_I2C_CHARDEV=m
++CONFIG_I2C_HELPER_AUTO=y
++CONFIG_I2C_ALGOBIT=m
++
++#
++# I2C Hardware Bus support
++#
++
++#
++# I2C system bus drivers (mostly embedded / system-on-chip)
++#
++# CONFIG_I2C_DESIGNWARE is not set
++CONFIG_I2C_GPIO=m
++# CONFIG_I2C_OCORES is not set
++# CONFIG_I2C_SIMTEC is not set
++
++#
++# External I2C/SMBus adapter drivers
++#
++# CONFIG_I2C_PARPORT is not set
++# CONFIG_I2C_PARPORT_LIGHT is not set
++# CONFIG_I2C_TAOS_EVM is not set
++CONFIG_I2C_TINY_USB=m
++
++#
++# Other I2C/SMBus bus drivers
++#
++# CONFIG_I2C_PCA_PLATFORM is not set
++# CONFIG_I2C_STUB is not set
++
++#
++# Miscellaneous I2C Chip support
++#
++CONFIG_DS1682=m
++CONFIG_SENSORS_TSL2550=m
++# CONFIG_I2C_DEBUG_CORE is not set
++# CONFIG_I2C_DEBUG_ALGO is not set
++# CONFIG_I2C_DEBUG_BUS is not set
++# CONFIG_I2C_DEBUG_CHIP is not set
++CONFIG_SPI=y
++CONFIG_SPI_MASTER=y
++
++#
++# SPI Master Controller Drivers
++#
++CONFIG_SPI_ATMEL=y
++CONFIG_SPI_BITBANG=m
++# CONFIG_SPI_BUTTERFLY is not set
++CONFIG_SPI_GPIO=m
++# CONFIG_SPI_LM70_LLP is not set
++
++#
++# SPI Protocol Masters
++#
++CONFIG_SPI_SPIDEV=y
++CONFIG_SPI_TLE62X0=m
++
++#
++# PPS support
++#
++CONFIG_PPS=m
++# CONFIG_PPS_DEBUG is not set
++CONFIG_ARCH_REQUIRE_GPIOLIB=y
++CONFIG_GPIOLIB=y
++CONFIG_GPIO_SYSFS=y
++
++#
++# Memory mapped GPIO expanders:
++#
++
++#
++# I2C GPIO expanders:
++#
++# CONFIG_GPIO_MAX732X is not set
++# CONFIG_GPIO_PCA953X is not set
++# CONFIG_GPIO_PCF857X is not set
++
++#
++# PCI GPIO expanders:
++#
++
++#
++# SPI GPIO expanders:
++#
++# CONFIG_GPIO_MAX7301 is not set
++# CONFIG_GPIO_MCP23S08 is not set
++# CONFIG_GPIO_MC33880 is not set
++
++#
++# AC97 GPIO expanders:
++#
++CONFIG_W1=m
++CONFIG_W1_CON=y
++
++#
++# 1-wire Bus Masters
++#
++# CONFIG_W1_MASTER_DS2490 is not set
++# CONFIG_W1_MASTER_DS2482 is not set
++# CONFIG_W1_MASTER_DS1WM is not set
++CONFIG_W1_MASTER_GPIO=m
++
++#
++# 1-wire Slaves
++#
++CONFIG_W1_SLAVE_THERM=m
++CONFIG_W1_SLAVE_SMEM=m
++CONFIG_W1_SLAVE_DS2431=m
++CONFIG_W1_SLAVE_DS2433=m
++# CONFIG_W1_SLAVE_DS2433_CRC is not set
++CONFIG_W1_SLAVE_DS2760=m
++CONFIG_W1_SLAVE_BQ27000=m
++CONFIG_POWER_SUPPLY=m
++# CONFIG_POWER_SUPPLY_DEBUG is not set
++# CONFIG_PDA_POWER is not set
++# CONFIG_APM_POWER is not set
++# CONFIG_BATTERY_DS2760 is not set
++# CONFIG_BATTERY_DS2782 is not set
++# CONFIG_BATTERY_BQ27x00 is not set
++# CONFIG_BATTERY_MAX17040 is not set
++# CONFIG_HWMON is not set
++# CONFIG_THERMAL is not set
++CONFIG_WATCHDOG=y
++# CONFIG_WATCHDOG_NOWAYOUT is not set
++
++#
++# Watchdog Device Drivers
++#
++# CONFIG_SOFT_WATCHDOG is not set
++CONFIG_AT91SAM9X_WATCHDOG=y
++
++#
++# USB-based Watchdog Cards
++#
++# CONFIG_USBPCWATCHDOG is not set
++CONFIG_SSB_POSSIBLE=y
++
++#
++# Sonics Silicon Backplane
++#
++CONFIG_SSB=m
++CONFIG_SSB_SDIOHOST_POSSIBLE=y
++# CONFIG_SSB_SDIOHOST is not set
++# CONFIG_SSB_SILENT is not set
++# CONFIG_SSB_DEBUG is not set
++
++#
++# Multifunction device drivers
++#
++# CONFIG_MFD_CORE is not set
++# CONFIG_MFD_SM501 is not set
++# CONFIG_MFD_ASIC3 is not set
++# CONFIG_HTC_EGPIO is not set
++# CONFIG_HTC_PASIC3 is not set
++# CONFIG_TPS65010 is not set
++# CONFIG_MFD_TMIO is not set
++# CONFIG_MFD_T7L66XB is not set
++# CONFIG_MFD_TC6387XB is not set
++# CONFIG_MFD_TC6393XB is not set
++# CONFIG_MFD_WM8400 is not set
++# CONFIG_MFD_WM831X is not set
++# CONFIG_MFD_WM8350_I2C is not set
++# CONFIG_MFD_PCF50633 is not set
++# CONFIG_MFD_MC13783 is not set
++# CONFIG_AB3100_CORE is not set
++# CONFIG_EZX_PCAP is not set
++# CONFIG_REGULATOR is not set
++CONFIG_MEDIA_SUPPORT=m
++
++#
++# Multimedia core support
++#
++CONFIG_VIDEO_DEV=m
++CONFIG_VIDEO_V4L2_COMMON=m
++CONFIG_VIDEO_ALLOW_V4L1=y
++CONFIG_VIDEO_V4L1_COMPAT=y
++CONFIG_DVB_CORE=m
++CONFIG_VIDEO_MEDIA=m
++
++#
++# Multimedia drivers
++#
++# CONFIG_MEDIA_ATTACH is not set
++CONFIG_MEDIA_TUNER=m
++# CONFIG_MEDIA_TUNER_CUSTOMISE is not set
++CONFIG_MEDIA_TUNER_SIMPLE=m
++CONFIG_MEDIA_TUNER_TDA8290=m
++CONFIG_MEDIA_TUNER_TDA827X=m
++CONFIG_MEDIA_TUNER_TDA18271=m
++CONFIG_MEDIA_TUNER_TDA9887=m
++CONFIG_MEDIA_TUNER_TEA5761=m
++CONFIG_MEDIA_TUNER_TEA5767=m
++CONFIG_MEDIA_TUNER_MT20XX=m
++CONFIG_MEDIA_TUNER_MT2060=m
++CONFIG_MEDIA_TUNER_MT2266=m
++CONFIG_MEDIA_TUNER_QT1010=m
++CONFIG_MEDIA_TUNER_XC2028=m
++CONFIG_MEDIA_TUNER_XC5000=m
++CONFIG_MEDIA_TUNER_MXL5005S=m
++CONFIG_MEDIA_TUNER_MXL5007T=m
++CONFIG_MEDIA_TUNER_MC44S803=m
++CONFIG_VIDEO_V4L2=m
++CONFIG_VIDEO_V4L1=m
++CONFIG_VIDEOBUF_GEN=m
++CONFIG_VIDEOBUF_VMALLOC=m
++CONFIG_VIDEOBUF_DMA_CONTIG=m
++CONFIG_VIDEOBUF_DVB=m
++CONFIG_VIDEO_IR=m
++CONFIG_VIDEO_TVEEPROM=m
++CONFIG_VIDEO_TUNER=m
++CONFIG_VIDEO_CAPTURE_DRIVERS=y
++# CONFIG_VIDEO_ADV_DEBUG is not set
++# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set
++CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
++CONFIG_VIDEO_IR_I2C=m
++CONFIG_VIDEO_MSP3400=m
++CONFIG_VIDEO_CS53L32A=m
++CONFIG_VIDEO_WM8775=m
++CONFIG_VIDEO_MT9V011=m
++CONFIG_VIDEO_SAA711X=m
++CONFIG_VIDEO_TVP5150=m
++CONFIG_VIDEO_CX25840=m
++CONFIG_VIDEO_CX2341X=m
++CONFIG_VIDEO_VIVI=m
++CONFIG_VIDEO_BWQCAM=m
++CONFIG_VIDEO_CQCAM=m
++CONFIG_VIDEO_CPIA=m
++CONFIG_VIDEO_CPIA_USB=m
++CONFIG_VIDEO_CPIA2=m
++CONFIG_VIDEO_SAA5246A=m
++CONFIG_VIDEO_SAA5249=m
++CONFIG_VIDEO_AU0828=m
++CONFIG_SOC_CAMERA=m
++CONFIG_SOC_CAMERA_MT9M001=m
++CONFIG_SOC_CAMERA_MT9M111=m
++CONFIG_SOC_CAMERA_MT9T031=m
++CONFIG_SOC_CAMERA_MT9V022=m
++CONFIG_SOC_CAMERA_TW9910=m
++CONFIG_SOC_CAMERA_PLATFORM=m
++CONFIG_SOC_CAMERA_OV772X=m
++CONFIG_VIDEO_SH_MOBILE_CEU=m
++CONFIG_V4L_USB_DRIVERS=y
++CONFIG_USB_VIDEO_CLASS=m
++CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y
++CONFIG_USB_GSPCA=m
++CONFIG_USB_M5602=m
++CONFIG_USB_STV06XX=m
++CONFIG_USB_GL860=m
++CONFIG_USB_GSPCA_CONEX=m
++CONFIG_USB_GSPCA_ETOMS=m
++CONFIG_USB_GSPCA_FINEPIX=m
++CONFIG_USB_GSPCA_JEILINJ=m
++CONFIG_USB_GSPCA_MARS=m
++CONFIG_USB_GSPCA_MR97310A=m
++CONFIG_USB_GSPCA_OV519=m
++CONFIG_USB_GSPCA_OV534=m
++CONFIG_USB_GSPCA_PAC207=m
++CONFIG_USB_GSPCA_PAC7311=m
++CONFIG_USB_GSPCA_SN9C20X=m
++# CONFIG_USB_GSPCA_SN9C20X_EVDEV is not set
++CONFIG_USB_GSPCA_SONIXB=m
++CONFIG_USB_GSPCA_SONIXJ=m
++CONFIG_USB_GSPCA_SPCA500=m
++CONFIG_USB_GSPCA_SPCA501=m
++CONFIG_USB_GSPCA_SPCA505=m
++CONFIG_USB_GSPCA_SPCA506=m
++CONFIG_USB_GSPCA_SPCA508=m
++CONFIG_USB_GSPCA_SPCA561=m
++CONFIG_USB_GSPCA_SQ905=m
++CONFIG_USB_GSPCA_SQ905C=m
++CONFIG_USB_GSPCA_STK014=m
++CONFIG_USB_GSPCA_SUNPLUS=m
++CONFIG_USB_GSPCA_T613=m
++CONFIG_USB_GSPCA_TV8532=m
++CONFIG_USB_GSPCA_VC032X=m
++CONFIG_USB_GSPCA_ZC3XX=m
++CONFIG_VIDEO_PVRUSB2=m
++CONFIG_VIDEO_PVRUSB2_SYSFS=y
++CONFIG_VIDEO_PVRUSB2_DVB=y
++# CONFIG_VIDEO_PVRUSB2_DEBUGIFC is not set
++CONFIG_VIDEO_HDPVR=m
++CONFIG_VIDEO_EM28XX=m
++CONFIG_VIDEO_EM28XX_ALSA=m
++CONFIG_VIDEO_EM28XX_DVB=m
++CONFIG_VIDEO_CX231XX=m
++CONFIG_VIDEO_CX231XX_ALSA=m
++CONFIG_VIDEO_CX231XX_DVB=m
++CONFIG_VIDEO_USBVISION=m
++CONFIG_VIDEO_USBVIDEO=m
++CONFIG_USB_VICAM=m
++CONFIG_USB_IBMCAM=m
++CONFIG_USB_KONICAWC=m
++# CONFIG_USB_QUICKCAM_MESSENGER is not set
++CONFIG_USB_ET61X251=m
++CONFIG_VIDEO_OVCAMCHIP=m
++CONFIG_USB_W9968CF=m
++# CONFIG_USB_OV511 is not set
++CONFIG_USB_SE401=m
++CONFIG_USB_SN9C102=m
++CONFIG_USB_STV680=m
++CONFIG_USB_ZC0301=m
++CONFIG_USB_PWC=m
++# CONFIG_USB_PWC_DEBUG is not set
++CONFIG_USB_PWC_INPUT_EVDEV=y
++CONFIG_USB_ZR364XX=m
++CONFIG_USB_STKWEBCAM=m
++CONFIG_USB_S2255=m
++CONFIG_RADIO_ADAPTERS=y
++CONFIG_I2C_SI4713=m
++CONFIG_RADIO_SI4713=m
++CONFIG_USB_DSBR=m
++# CONFIG_RADIO_SI470X is not set
++CONFIG_USB_MR800=m
++CONFIG_RADIO_TEA5764=m
++CONFIG_DVB_MAX_ADAPTERS=8
++# CONFIG_DVB_DYNAMIC_MINORS is not set
++CONFIG_DVB_CAPTURE_DRIVERS=y
++# CONFIG_TTPCI_EEPROM is not set
++
++#
++# Supported USB Adapters
++#
++CONFIG_DVB_USB=m
++# CONFIG_DVB_USB_DEBUG is not set
++CONFIG_DVB_USB_A800=m
++CONFIG_DVB_USB_DIBUSB_MB=m
++# CONFIG_DVB_USB_DIBUSB_MB_FAULTY is not set
++CONFIG_DVB_USB_DIBUSB_MC=m
++CONFIG_DVB_USB_DIB0700=m
++CONFIG_DVB_USB_UMT_010=m
++CONFIG_DVB_USB_CXUSB=m
++CONFIG_DVB_USB_M920X=m
++CONFIG_DVB_USB_GL861=m
++CONFIG_DVB_USB_AU6610=m
++CONFIG_DVB_USB_DIGITV=m
++CONFIG_DVB_USB_VP7045=m
++CONFIG_DVB_USB_VP702X=m
++CONFIG_DVB_USB_GP8PSK=m
++CONFIG_DVB_USB_NOVA_T_USB2=m
++CONFIG_DVB_USB_TTUSB2=m
++CONFIG_DVB_USB_DTT200U=m
++CONFIG_DVB_USB_OPERA1=m
++CONFIG_DVB_USB_AF9005=m
++CONFIG_DVB_USB_AF9005_REMOTE=m
++CONFIG_DVB_USB_DW2102=m
++CONFIG_DVB_USB_CINERGY_T2=m
++CONFIG_DVB_USB_ANYSEE=m
++CONFIG_DVB_USB_DTV5100=m
++CONFIG_DVB_USB_AF9015=m
++CONFIG_DVB_USB_CE6230=m
++CONFIG_DVB_USB_FRIIO=m
++CONFIG_SMS_SIANO_MDTV=m
++
++#
++# Siano module components
++#
++CONFIG_SMS_USB_DRV=m
++CONFIG_SMS_SDIO_DRV=m
++
++#
++# Supported FlexCopII (B2C2) Adapters
++#
++CONFIG_DVB_B2C2_FLEXCOP=m
++CONFIG_DVB_B2C2_FLEXCOP_USB=m
++# CONFIG_DVB_B2C2_FLEXCOP_DEBUG is not set
++
++#
++# Supported DVB Frontends
++#
++# CONFIG_DVB_FE_CUSTOMISE is not set
++CONFIG_DVB_CX24123=m
++CONFIG_DVB_MT312=m
++CONFIG_DVB_ZL10039=m
++CONFIG_DVB_S5H1420=m
++CONFIG_DVB_STV0288=m
++CONFIG_DVB_STB6000=m
++CONFIG_DVB_STV0299=m
++CONFIG_DVB_TDA10086=m
++CONFIG_DVB_TUNER_ITD1000=m
++CONFIG_DVB_TUNER_CX24113=m
++CONFIG_DVB_TDA826X=m
++CONFIG_DVB_CX24116=m
++CONFIG_DVB_SI21XX=m
++CONFIG_DVB_CX22702=m
++CONFIG_DVB_TDA1004X=m
++CONFIG_DVB_NXT6000=m
++CONFIG_DVB_MT352=m
++CONFIG_DVB_ZL10353=m
++CONFIG_DVB_DIB3000MB=m
++CONFIG_DVB_DIB3000MC=m
++CONFIG_DVB_DIB7000M=m
++CONFIG_DVB_DIB7000P=m
++CONFIG_DVB_TDA10048=m
++CONFIG_DVB_AF9013=m
++CONFIG_DVB_TDA10021=m
++CONFIG_DVB_TDA10023=m
++CONFIG_DVB_STV0297=m
++CONFIG_DVB_NXT200X=m
++CONFIG_DVB_BCM3510=m
++CONFIG_DVB_LGDT330X=m
++CONFIG_DVB_LGDT3305=m
++CONFIG_DVB_S5H1409=m
++CONFIG_DVB_AU8522=m
++CONFIG_DVB_S5H1411=m
++CONFIG_DVB_DIB8000=m
++CONFIG_DVB_PLL=m
++CONFIG_DVB_TUNER_DIB0070=m
++CONFIG_DVB_LNBP21=m
++CONFIG_DVB_ISL6421=m
++CONFIG_DVB_LGS8GL5=m
++# CONFIG_DAB is not set
++
++#
++# Graphics support
++#
++# CONFIG_VGASTATE is not set
++CONFIG_VIDEO_OUTPUT_CONTROL=m
++CONFIG_FB=m
++# CONFIG_FIRMWARE_EDID is not set
++# CONFIG_FB_DDC is not set
++# CONFIG_FB_BOOT_VESA_SUPPORT is not set
++# CONFIG_FB_CFB_FILLRECT is not set
++# CONFIG_FB_CFB_COPYAREA is not set
++# CONFIG_FB_CFB_IMAGEBLIT is not set
++# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
++# CONFIG_FB_SYS_FILLRECT is not set
++# CONFIG_FB_SYS_COPYAREA is not set
++# CONFIG_FB_SYS_IMAGEBLIT is not set
++# CONFIG_FB_FOREIGN_ENDIAN is not set
++# CONFIG_FB_SYS_FOPS is not set
++# CONFIG_FB_SVGALIB is not set
++# CONFIG_FB_MACMODES is not set
++# CONFIG_FB_BACKLIGHT is not set
++# CONFIG_FB_MODE_HELPERS is not set
++# CONFIG_FB_TILEBLITTING is not set
++
++#
++# Frame buffer hardware drivers
++#
++# CONFIG_FB_UVESA is not set
++# CONFIG_FB_S1D13XXX is not set
++# CONFIG_FB_VIRTUAL is not set
++# CONFIG_FB_METRONOME is not set
++# CONFIG_FB_MB862XX is not set
++# CONFIG_FB_BROADSHEET is not set
++CONFIG_BACKLIGHT_LCD_SUPPORT=y
++CONFIG_LCD_CLASS_DEVICE=m
++CONFIG_LCD_LMS283GF05=m
++CONFIG_LCD_LTV350QV=m
++CONFIG_LCD_ILI9320=m
++CONFIG_LCD_TDO24M=m
++CONFIG_LCD_VGG2432A4=m
++CONFIG_LCD_PLATFORM=m
++CONFIG_BACKLIGHT_CLASS_DEVICE=m
++CONFIG_BACKLIGHT_GENERIC=m
++
++#
++# Display device support
++#
++CONFIG_DISPLAY_SUPPORT=m
++
++#
++# Display hardware drivers
++#
++
++#
++# Console display driver support
++#
++# CONFIG_VGA_CONSOLE is not set
++CONFIG_DUMMY_CONSOLE=y
++CONFIG_FRAMEBUFFER_CONSOLE=m
++# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
++# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
++# CONFIG_FONTS is not set
++CONFIG_FONT_8x8=y
++CONFIG_FONT_8x16=y
++# CONFIG_LOGO is not set
++CONFIG_SOUND=m
++CONFIG_SOUND_OSS_CORE=y
++CONFIG_SOUND_OSS_CORE_PRECLAIM=y
++CONFIG_SND=m
++CONFIG_SND_TIMER=m
++CONFIG_SND_PCM=m
++CONFIG_SND_HWDEP=m
++CONFIG_SND_RAWMIDI=m
++CONFIG_SND_JACK=y
++CONFIG_SND_SEQUENCER=m
++CONFIG_SND_SEQ_DUMMY=m
++CONFIG_SND_OSSEMUL=y
++CONFIG_SND_MIXER_OSS=m
++CONFIG_SND_PCM_OSS=m
++CONFIG_SND_PCM_OSS_PLUGINS=y
++# CONFIG_SND_SEQUENCER_OSS is not set
++CONFIG_SND_HRTIMER=m
++CONFIG_SND_SEQ_HRTIMER_DEFAULT=y
++# CONFIG_SND_DYNAMIC_MINORS is not set
++CONFIG_SND_SUPPORT_OLD_API=y
++CONFIG_SND_VERBOSE_PROCFS=y
++# CONFIG_SND_VERBOSE_PRINTK is not set
++# CONFIG_SND_DEBUG is not set
++CONFIG_SND_RAWMIDI_SEQ=m
++# CONFIG_SND_OPL3_LIB_SEQ is not set
++# CONFIG_SND_OPL4_LIB_SEQ is not set
++# CONFIG_SND_SBAWE_SEQ is not set
++# CONFIG_SND_EMU10K1_SEQ is not set
++CONFIG_SND_MPU401_UART=m
++CONFIG_SND_DRIVERS=y
++CONFIG_SND_DUMMY=m
++CONFIG_SND_VIRMIDI=m
++CONFIG_SND_MTPAV=m
++CONFIG_SND_MTS64=m
++CONFIG_SND_SERIAL_U16550=m
++CONFIG_SND_MPU401=m
++CONFIG_SND_PORTMAN2X4=m
++CONFIG_SND_ARM=y
++
++#
++# Atmel devices (AVR32 and AT91)
++#
++CONFIG_SND_SPI=y
++CONFIG_SND_AT73C213=m
++CONFIG_SND_AT73C213_TARGET_BITRATE=48000
++CONFIG_SND_USB=y
++CONFIG_SND_USB_AUDIO=m
++CONFIG_SND_USB_CAIAQ=m
++# CONFIG_SND_USB_CAIAQ_INPUT is not set
++CONFIG_SND_SOC=m
++CONFIG_SND_ATMEL_SOC=m
++CONFIG_SND_ATMEL_SOC_SSC=m
++CONFIG_SND_AT91_SOC_SAM9G20_WM8731=m
++CONFIG_SND_SOC_I2C_AND_SPI=m
++CONFIG_SND_SOC_ALL_CODECS=m
++CONFIG_SND_SOC_WM_HUBS=m
++CONFIG_SND_SOC_AD1836=m
++CONFIG_SND_SOC_AD1938=m
++CONFIG_SND_SOC_AD73311=m
++CONFIG_SND_SOC_AK4104=m
++CONFIG_SND_SOC_AK4535=m
++CONFIG_SND_SOC_AK4642=m
++CONFIG_SND_SOC_CS4270=m
++CONFIG_SND_SOC_L3=m
++CONFIG_SND_SOC_PCM3008=m
++CONFIG_SND_SOC_SPDIF=m
++CONFIG_SND_SOC_SSM2602=m
++CONFIG_SND_SOC_TLV320AIC23=m
++CONFIG_SND_SOC_TLV320AIC26=m
++CONFIG_SND_SOC_TLV320AIC3X=m
++CONFIG_SND_SOC_UDA134X=m
++CONFIG_SND_SOC_UDA1380=m
++CONFIG_SND_SOC_WM8510=m
++CONFIG_SND_SOC_WM8523=m
++CONFIG_SND_SOC_WM8580=m
++CONFIG_SND_SOC_WM8728=m
++CONFIG_SND_SOC_WM8731=m
++CONFIG_SND_SOC_WM8750=m
++CONFIG_SND_SOC_WM8753=m
++CONFIG_SND_SOC_WM8776=m
++CONFIG_SND_SOC_WM8900=m
++CONFIG_SND_SOC_WM8903=m
++CONFIG_SND_SOC_WM8940=m
++CONFIG_SND_SOC_WM8960=m
++CONFIG_SND_SOC_WM8961=m
++CONFIG_SND_SOC_WM8971=m
++CONFIG_SND_SOC_WM8974=m
++CONFIG_SND_SOC_WM8988=m
++CONFIG_SND_SOC_WM8990=m
++CONFIG_SND_SOC_WM8993=m
++CONFIG_SND_SOC_WM9081=m
++CONFIG_SND_SOC_MAX9877=m
++CONFIG_SOUND_PRIME=m
++CONFIG_HID_SUPPORT=y
++CONFIG_HID=y
++CONFIG_HIDRAW=y
++
++#
++# USB Input Devices
++#
++CONFIG_USB_HID=y
++# CONFIG_HID_PID is not set
++CONFIG_USB_HIDDEV=y
++
++#
++# Special HID drivers
++#
++CONFIG_HID_A4TECH=m
++CONFIG_HID_APPLE=m
++CONFIG_HID_BELKIN=m
++CONFIG_HID_CHERRY=m
++CONFIG_HID_CHICONY=m
++CONFIG_HID_CYPRESS=m
++CONFIG_HID_DRAGONRISE=m
++# CONFIG_DRAGONRISE_FF is not set
++CONFIG_HID_EZKEY=m
++CONFIG_HID_KYE=m
++CONFIG_HID_GYRATION=m
++CONFIG_HID_TWINHAN=m
++CONFIG_HID_KENSINGTON=m
++CONFIG_HID_LOGITECH=m
++# CONFIG_LOGITECH_FF is not set
++# CONFIG_LOGIRUMBLEPAD2_FF is not set
++CONFIG_HID_MICROSOFT=m
++CONFIG_HID_MONTEREY=m
++CONFIG_HID_NTRIG=m
++CONFIG_HID_PANTHERLORD=m
++# CONFIG_PANTHERLORD_FF is not set
++CONFIG_HID_PETALYNX=m
++CONFIG_HID_SAMSUNG=m
++CONFIG_HID_SONY=m
++CONFIG_HID_SUNPLUS=m
++CONFIG_HID_GREENASIA=m
++# CONFIG_GREENASIA_FF is not set
++CONFIG_HID_SMARTJOYPLUS=m
++# CONFIG_SMARTJOYPLUS_FF is not set
++CONFIG_HID_TOPSEED=m
++CONFIG_HID_THRUSTMASTER=m
++# CONFIG_THRUSTMASTER_FF is not set
++CONFIG_HID_WACOM=m
++CONFIG_HID_ZEROPLUS=m
++# CONFIG_ZEROPLUS_FF is not set
++CONFIG_USB_SUPPORT=y
++CONFIG_USB_ARCH_HAS_HCD=y
++CONFIG_USB_ARCH_HAS_OHCI=y
++# CONFIG_USB_ARCH_HAS_EHCI is not set
++CONFIG_USB=y
++# CONFIG_USB_DEBUG is not set
++# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set
++
++#
++# Miscellaneous USB options
++#
++CONFIG_USB_DEVICEFS=y
++# CONFIG_USB_DEVICE_CLASS is not set
++# CONFIG_USB_DYNAMIC_MINORS is not set
++# CONFIG_USB_SUSPEND is not set
++# CONFIG_USB_OTG is not set
++# CONFIG_USB_OTG_WHITELIST is not set
++# CONFIG_USB_OTG_BLACKLIST_HUB is not set
++CONFIG_USB_MON=y
++# CONFIG_USB_WUSB is not set
++CONFIG_USB_WUSB_CBAF=m
++# CONFIG_USB_WUSB_CBAF_DEBUG is not set
++
++#
++# USB Host Controller Drivers
++#
++# CONFIG_USB_C67X00_HCD is not set
++# CONFIG_USB_OXU210HP_HCD is not set
++# CONFIG_USB_ISP116X_HCD is not set
++# CONFIG_USB_ISP1760_HCD is not set
++# CONFIG_USB_ISP1362_HCD is not set
++CONFIG_USB_OHCI_HCD=y
++# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set
++# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set
++CONFIG_USB_OHCI_LITTLE_ENDIAN=y
++# CONFIG_USB_U132_HCD is not set
++# CONFIG_USB_SL811_HCD is not set
++# CONFIG_USB_R8A66597_HCD is not set
++# CONFIG_USB_HWA_HCD is not set
++# CONFIG_USB_MUSB_HDRC is not set
++# CONFIG_USB_GADGET_MUSB_HDRC is not set
++
++#
++# USB Device Class drivers
++#
++CONFIG_USB_ACM=m
++CONFIG_USB_PRINTER=m
++CONFIG_USB_WDM=m
++CONFIG_USB_TMC=m
++
++#
++# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may
++#
++
++#
++# also be needed; see USB_STORAGE Help for more info
++#
++CONFIG_USB_STORAGE=y
++# CONFIG_USB_STORAGE_DEBUG is not set
++CONFIG_USB_STORAGE_DATAFAB=m
++CONFIG_USB_STORAGE_FREECOM=m
++CONFIG_USB_STORAGE_ISD200=m
++CONFIG_USB_STORAGE_USBAT=m
++CONFIG_USB_STORAGE_SDDR09=m
++CONFIG_USB_STORAGE_SDDR55=m
++CONFIG_USB_STORAGE_JUMPSHOT=m
++CONFIG_USB_STORAGE_ALAUDA=m
++CONFIG_USB_STORAGE_ONETOUCH=m
++CONFIG_USB_STORAGE_KARMA=m
++CONFIG_USB_STORAGE_CYPRESS_ATACB=m
++# CONFIG_USB_LIBUSUAL is not set
++
++#
++# USB Imaging devices
++#
++CONFIG_USB_MDC800=m
++CONFIG_USB_MICROTEK=m
++
++#
++# USB port drivers
++#
++CONFIG_USB_USS720=m
++CONFIG_USB_SERIAL=m
++CONFIG_USB_EZUSB=y
++CONFIG_USB_SERIAL_GENERIC=y
++CONFIG_USB_SERIAL_AIRCABLE=m
++CONFIG_USB_SERIAL_ARK3116=m
++CONFIG_USB_SERIAL_BELKIN=m
++CONFIG_USB_SERIAL_CH341=m
++CONFIG_USB_SERIAL_WHITEHEAT=m
++CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m
++CONFIG_USB_SERIAL_CP210X=m
++CONFIG_USB_SERIAL_CYPRESS_M8=m
++CONFIG_USB_SERIAL_EMPEG=m
++CONFIG_USB_SERIAL_FTDI_SIO=m
++CONFIG_USB_SERIAL_FUNSOFT=m
++CONFIG_USB_SERIAL_VISOR=m
++CONFIG_USB_SERIAL_IPAQ=m
++CONFIG_USB_SERIAL_IR=m
++CONFIG_USB_SERIAL_EDGEPORT=m
++CONFIG_USB_SERIAL_EDGEPORT_TI=m
++CONFIG_USB_SERIAL_GARMIN=m
++CONFIG_USB_SERIAL_IPW=m
++CONFIG_USB_SERIAL_IUU=m
++CONFIG_USB_SERIAL_KEYSPAN_PDA=m
++CONFIG_USB_SERIAL_KEYSPAN=m
++CONFIG_USB_SERIAL_KEYSPAN_MPR=y
++CONFIG_USB_SERIAL_KEYSPAN_USA28=y
++CONFIG_USB_SERIAL_KEYSPAN_USA28X=y
++CONFIG_USB_SERIAL_KEYSPAN_USA28XA=y
++CONFIG_USB_SERIAL_KEYSPAN_USA28XB=y
++CONFIG_USB_SERIAL_KEYSPAN_USA19=y
++CONFIG_USB_SERIAL_KEYSPAN_USA18X=y
++CONFIG_USB_SERIAL_KEYSPAN_USA19W=y
++CONFIG_USB_SERIAL_KEYSPAN_USA19QW=y
++CONFIG_USB_SERIAL_KEYSPAN_USA19QI=y
++CONFIG_USB_SERIAL_KEYSPAN_USA49W=y
++CONFIG_USB_SERIAL_KEYSPAN_USA49WLC=y
++CONFIG_USB_SERIAL_KLSI=m
++CONFIG_USB_SERIAL_KOBIL_SCT=m
++CONFIG_USB_SERIAL_MCT_U232=m
++CONFIG_USB_SERIAL_MOS7720=m
++CONFIG_USB_SERIAL_MOS7840=m
++CONFIG_USB_SERIAL_MOTOROLA=m
++CONFIG_USB_SERIAL_NAVMAN=m
++CONFIG_USB_SERIAL_PL2303=m
++CONFIG_USB_SERIAL_OTI6858=m
++CONFIG_USB_SERIAL_QUALCOMM=m
++CONFIG_USB_SERIAL_SPCP8X5=m
++CONFIG_USB_SERIAL_HP4X=m
++CONFIG_USB_SERIAL_SAFE=m
++# CONFIG_USB_SERIAL_SAFE_PADDED is not set
++CONFIG_USB_SERIAL_SIEMENS_MPI=m
++CONFIG_USB_SERIAL_SIERRAWIRELESS=m
++CONFIG_USB_SERIAL_SYMBOL=m
++CONFIG_USB_SERIAL_TI=m
++CONFIG_USB_SERIAL_CYBERJACK=m
++CONFIG_USB_SERIAL_XIRCOM=m
++CONFIG_USB_SERIAL_OPTION=m
++CONFIG_USB_SERIAL_OMNINET=m
++CONFIG_USB_SERIAL_OPTICON=m
++CONFIG_USB_SERIAL_DEBUG=m
++
++#
++# USB Miscellaneous drivers
++#
++CONFIG_USB_EMI62=m
++CONFIG_USB_EMI26=m
++CONFIG_USB_ADUTUX=m
++CONFIG_USB_SEVSEG=m
++CONFIG_USB_RIO500=m
++CONFIG_USB_LEGOTOWER=m
++CONFIG_USB_LCD=m
++CONFIG_USB_BERRY_CHARGE=m
++CONFIG_USB_LED=m
++CONFIG_USB_CYPRESS_CY7C63=m
++CONFIG_USB_CYTHERM=m
++CONFIG_USB_IDMOUSE=m
++CONFIG_USB_FTDI_ELAN=m
++CONFIG_USB_APPLEDISPLAY=m
++CONFIG_USB_LD=m
++CONFIG_USB_TRANCEVIBRATOR=m
++CONFIG_USB_IOWARRIOR=m
++CONFIG_USB_TEST=m
++CONFIG_USB_ISIGHTFW=m
++CONFIG_USB_VST=m
++CONFIG_USB_ATM=m
++CONFIG_USB_SPEEDTOUCH=m
++CONFIG_USB_CXACRU=m
++CONFIG_USB_UEAGLEATM=m
++CONFIG_USB_XUSBATM=m
++CONFIG_USB_GADGET=y
++# CONFIG_USB_GADGET_DEBUG_FILES is not set
++# CONFIG_USB_GADGET_DEBUG_FS is not set
++CONFIG_USB_GADGET_VBUS_DRAW=2
++CONFIG_USB_GADGET_SELECTED=y
++CONFIG_USB_GADGET_AT91=y
++CONFIG_USB_AT91=y
++# CONFIG_USB_GADGET_ATMEL_USBA is not set
++# CONFIG_USB_GADGET_FSL_USB2 is not set
++# CONFIG_USB_GADGET_LH7A40X is not set
++# CONFIG_USB_GADGET_OMAP is not set
++# CONFIG_USB_GADGET_PXA25X is not set
++# CONFIG_USB_GADGET_R8A66597 is not set
++# CONFIG_USB_GADGET_PXA27X is not set
++# CONFIG_USB_GADGET_S3C_HSOTG is not set
++# CONFIG_USB_GADGET_IMX is not set
++# CONFIG_USB_GADGET_S3C2410 is not set
++# CONFIG_USB_GADGET_M66592 is not set
++# CONFIG_USB_GADGET_AMD5536UDC is not set
++# CONFIG_USB_GADGET_FSL_QE is not set
++# CONFIG_USB_GADGET_CI13XXX is not set
++# CONFIG_USB_GADGET_NET2280 is not set
++# CONFIG_USB_GADGET_GOKU is not set
++# CONFIG_USB_GADGET_LANGWELL is not set
++# CONFIG_USB_GADGET_DUMMY_HCD is not set
++# CONFIG_USB_GADGET_DUALSPEED is not set
++CONFIG_USB_ZERO=m
++# CONFIG_USB_AUDIO is not set
++CONFIG_USB_ETH=m
++CONFIG_USB_ETH_RNDIS=y
++# CONFIG_USB_ETH_EEM is not set
++CONFIG_USB_GADGETFS=m
++CONFIG_USB_FILE_STORAGE=m
++# CONFIG_USB_FILE_STORAGE_TEST is not set
++CONFIG_USB_G_SERIAL=m
++# CONFIG_USB_MIDI_GADGET is not set
++CONFIG_USB_G_PRINTER=m
++CONFIG_USB_CDC_COMPOSITE=m
++
++#
++# OTG and related infrastructure
++#
++CONFIG_USB_OTG_UTILS=y
++CONFIG_USB_GPIO_VBUS=m
++CONFIG_NOP_USB_XCEIV=m
++CONFIG_MMC=y
++# CONFIG_MMC_DEBUG is not set
++# CONFIG_MMC_UNSAFE_RESUME is not set
++
++#
++# MMC/SD/SDIO Card Drivers
++#
++CONFIG_MMC_BLOCK=y
++CONFIG_MMC_BLOCK_BOUNCE=y
++CONFIG_SDIO_UART=m
++# CONFIG_MMC_TEST is not set
++
++#
++# MMC/SD/SDIO Host Controller Drivers
++#
++# CONFIG_MMC_SDHCI is not set
++CONFIG_MMC_AT91=y
++# CONFIG_MMC_ATMELMCI is not set
++# CONFIG_MMC_SPI is not set
++# CONFIG_MEMSTICK is not set
++CONFIG_NEW_LEDS=y
++CONFIG_LEDS_CLASS=y
++
++#
++# LED drivers
++#
++# CONFIG_LEDS_PCA9532 is not set
++CONFIG_LEDS_GPIO=y
++CONFIG_LEDS_GPIO_PLATFORM=y
++CONFIG_LEDS_LP3944=m
++CONFIG_LEDS_PCA955X=m
++CONFIG_LEDS_DAC124S085=m
++CONFIG_LEDS_BD2802=m
++
++#
++# LED Triggers
++#
++CONFIG_LEDS_TRIGGERS=y
++CONFIG_LEDS_TRIGGER_TIMER=y
++CONFIG_LEDS_TRIGGER_HEARTBEAT=y
++# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set
++# CONFIG_LEDS_TRIGGER_GPIO is not set
++# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set
++
++#
++# iptables trigger is under Netfilter config (LED target)
++#
++# CONFIG_ACCESSIBILITY is not set
++CONFIG_RTC_LIB=y
++CONFIG_RTC_CLASS=y
++CONFIG_RTC_HCTOSYS=y
++CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
++CONFIG_RTC_DEBUG=y
++
++#
++# RTC interfaces
++#
++CONFIG_RTC_INTF_SYSFS=y
++CONFIG_RTC_INTF_PROC=y
++CONFIG_RTC_INTF_DEV=y
++CONFIG_RTC_INTF_DEV_UIE_EMUL=y
++# CONFIG_RTC_DRV_TEST is not set
++
++#
++# I2C RTC drivers
++#
++# CONFIG_RTC_DRV_DS1307 is not set
++# CONFIG_RTC_DRV_DS1374 is not set
++# CONFIG_RTC_DRV_DS1672 is not set
++# CONFIG_RTC_DRV_MAX6900 is not set
++# CONFIG_RTC_DRV_RS5C372 is not set
++# CONFIG_RTC_DRV_ISL1208 is not set
++# CONFIG_RTC_DRV_X1205 is not set
++# CONFIG_RTC_DRV_PCF8563 is not set
++# CONFIG_RTC_DRV_PCF8583 is not set
++# CONFIG_RTC_DRV_M41T80 is not set
++# CONFIG_RTC_DRV_S35390A is not set
++# CONFIG_RTC_DRV_FM3130 is not set
++# CONFIG_RTC_DRV_RX8581 is not set
++# CONFIG_RTC_DRV_RX8025 is not set
++
++#
++# SPI RTC drivers
++#
++# CONFIG_RTC_DRV_M41T94 is not set
++# CONFIG_RTC_DRV_DS1305 is not set
++# CONFIG_RTC_DRV_DS1390 is not set
++# CONFIG_RTC_DRV_MAX6902 is not set
++# CONFIG_RTC_DRV_R9701 is not set
++# CONFIG_RTC_DRV_RS5C348 is not set
++# CONFIG_RTC_DRV_DS3234 is not set
++# CONFIG_RTC_DRV_PCF2123 is not set
++
++#
++# Platform RTC drivers
++#
++CONFIG_RTC_DRV_CMOS=y
++# CONFIG_RTC_DRV_DS1286 is not set
++# CONFIG_RTC_DRV_DS1511 is not set
++# CONFIG_RTC_DRV_DS1553 is not set
++# CONFIG_RTC_DRV_DS1742 is not set
++# CONFIG_RTC_DRV_STK17TA8 is not set
++# CONFIG_RTC_DRV_M48T86 is not set
++# CONFIG_RTC_DRV_M48T35 is not set
++# CONFIG_RTC_DRV_M48T59 is not set
++# CONFIG_RTC_DRV_BQ4802 is not set
++# CONFIG_RTC_DRV_V3020 is not set
++
++#
++# on-CPU RTC drivers
++#
++CONFIG_RTC_DRV_AT91SAM9=y
++CONFIG_RTC_DRV_AT91SAM9_RTT=0
++CONFIG_RTC_DRV_AT91SAM9_GPBR=0
++# CONFIG_DMADEVICES is not set
++CONFIG_AUXDISPLAY=y
++CONFIG_KS0108=m
++CONFIG_KS0108_PORT=0x378
++CONFIG_KS0108_DELAY=2
++CONFIG_UIO=m
++CONFIG_UIO_PDRV=m
++CONFIG_UIO_PDRV_GENIRQ=m
++CONFIG_UIO_SMX=m
++CONFIG_UIO_SERCOS3=m
++
++#
++# TI VLYNQ
++#
++# CONFIG_STAGING is not set
++
++#
++# File systems
++#
++CONFIG_EXT2_FS=y
++# CONFIG_EXT2_FS_XATTR is not set
++# CONFIG_EXT2_FS_XIP is not set
++CONFIG_EXT3_FS=y
++# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
++CONFIG_EXT3_FS_XATTR=y
++# CONFIG_EXT3_FS_POSIX_ACL is not set
++# CONFIG_EXT3_FS_SECURITY is not set
++CONFIG_EXT4_FS=y
++CONFIG_EXT4_FS_XATTR=y
++# CONFIG_EXT4_FS_POSIX_ACL is not set
++# CONFIG_EXT4_FS_SECURITY is not set
++# CONFIG_EXT4_DEBUG is not set
++CONFIG_JBD=y
++# CONFIG_JBD_DEBUG is not set
++CONFIG_JBD2=y
++# CONFIG_JBD2_DEBUG is not set
++CONFIG_FS_MBCACHE=y
++CONFIG_REISERFS_FS=y
++# CONFIG_REISERFS_CHECK is not set
++# CONFIG_REISERFS_PROC_INFO is not set
++# CONFIG_REISERFS_FS_XATTR is not set
++CONFIG_JFS_FS=m
++# CONFIG_JFS_POSIX_ACL is not set
++# CONFIG_JFS_SECURITY is not set
++# CONFIG_JFS_DEBUG is not set
++# CONFIG_JFS_STATISTICS is not set
++CONFIG_FS_POSIX_ACL=y
++CONFIG_XFS_FS=m
++# CONFIG_XFS_QUOTA is not set
++# CONFIG_XFS_POSIX_ACL is not set
++# CONFIG_XFS_RT is not set
++# CONFIG_XFS_DEBUG is not set
++CONFIG_GFS2_FS=m
++# CONFIG_GFS2_FS_LOCKING_DLM is not set
++CONFIG_OCFS2_FS=m
++CONFIG_OCFS2_FS_O2CB=m
++CONFIG_OCFS2_FS_USERSPACE_CLUSTER=m
++CONFIG_OCFS2_FS_STATS=y
++CONFIG_OCFS2_DEBUG_MASKLOG=y
++# CONFIG_OCFS2_DEBUG_FS is not set
++# CONFIG_OCFS2_FS_POSIX_ACL is not set
++# CONFIG_BTRFS_FS is not set
++CONFIG_NILFS2_FS=m
++CONFIG_FILE_LOCKING=y
++CONFIG_FSNOTIFY=y
++CONFIG_DNOTIFY=y
++CONFIG_INOTIFY=y
++CONFIG_INOTIFY_USER=y
++CONFIG_QUOTA=y
++# CONFIG_QUOTA_NETLINK_INTERFACE is not set
++CONFIG_PRINT_QUOTA_WARNING=y
++CONFIG_QUOTA_TREE=m
++# CONFIG_QFMT_V1 is not set
++CONFIG_QFMT_V2=m
++CONFIG_QUOTACTL=y
++CONFIG_AUTOFS_FS=m
++CONFIG_AUTOFS4_FS=m
++CONFIG_FUSE_FS=m
++CONFIG_CUSE=m
++
++#
++# Caches
++#
++CONFIG_FSCACHE=m
++# CONFIG_FSCACHE_STATS is not set
++# CONFIG_FSCACHE_HISTOGRAM is not set
++# CONFIG_FSCACHE_DEBUG is not set
++# CONFIG_FSCACHE_OBJECT_LIST is not set
++CONFIG_CACHEFILES=m
++# CONFIG_CACHEFILES_DEBUG is not set
++# CONFIG_CACHEFILES_HISTOGRAM is not set
++
++#
++# CD-ROM/DVD Filesystems
++#
++CONFIG_ISO9660_FS=m
++# CONFIG_JOLIET is not set
++# CONFIG_ZISOFS is not set
++CONFIG_UDF_FS=m
++CONFIG_UDF_NLS=y
++
++#
++# DOS/FAT/NT Filesystems
++#
++CONFIG_FAT_FS=y
++CONFIG_MSDOS_FS=y
++CONFIG_VFAT_FS=y
++CONFIG_FAT_DEFAULT_CODEPAGE=437
++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
++CONFIG_NTFS_FS=m
++# CONFIG_NTFS_DEBUG is not set
++# CONFIG_NTFS_RW is not set
++
++#
++# Pseudo filesystems
++#
++CONFIG_PROC_FS=y
++CONFIG_PROC_SYSCTL=y
++CONFIG_PROC_PAGE_MONITOR=y
++CONFIG_SYSFS=y
++CONFIG_TMPFS=y
++# CONFIG_TMPFS_POSIX_ACL is not set
++# CONFIG_HUGETLB_PAGE is not set
++CONFIG_CONFIGFS_FS=m
++CONFIG_MISC_FILESYSTEMS=y
++# CONFIG_ADFS_FS is not set
++# CONFIG_AFFS_FS is not set
++# CONFIG_ECRYPT_FS is not set
++# CONFIG_HFS_FS is not set
++CONFIG_HFSPLUS_FS=m
++# CONFIG_BEFS_FS is not set
++# CONFIG_BFS_FS is not set
++# CONFIG_EFS_FS is not set
++CONFIG_JFFS2_FS=y
++CONFIG_JFFS2_FS_DEBUG=0
++CONFIG_JFFS2_FS_WRITEBUFFER=y
++# CONFIG_JFFS2_FS_WBUF_VERIFY is not set
++CONFIG_JFFS2_SUMMARY=y
++# CONFIG_JFFS2_FS_XATTR is not set
++# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
++CONFIG_JFFS2_ZLIB=y
++# CONFIG_JFFS2_LZO is not set
++CONFIG_JFFS2_RTIME=y
++# CONFIG_JFFS2_RUBIN is not set
++CONFIG_CRAMFS=y
++CONFIG_SQUASHFS=y
++# CONFIG_SQUASHFS_EMBEDDED is not set
++CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3
++CONFIG_VXFS_FS=m
++CONFIG_MINIX_FS=m
++CONFIG_OMFS_FS=m
++CONFIG_HPFS_FS=m
++CONFIG_QNX4FS_FS=m
++CONFIG_ROMFS_FS=m
++CONFIG_ROMFS_BACKED_BY_BLOCK=y
++# CONFIG_ROMFS_BACKED_BY_MTD is not set
++# CONFIG_ROMFS_BACKED_BY_BOTH is not set
++CONFIG_ROMFS_ON_BLOCK=y
++CONFIG_SYSV_FS=m
++CONFIG_UFS_FS=m
++# CONFIG_UFS_FS_WRITE is not set
++# CONFIG_UFS_DEBUG is not set
++CONFIG_EXOFS_FS=m
++# CONFIG_EXOFS_DEBUG is not set
++CONFIG_NETWORK_FILESYSTEMS=y
++CONFIG_NFS_FS=m
++CONFIG_NFS_V3=y
++# CONFIG_NFS_V3_ACL is not set
++# CONFIG_NFS_V4 is not set
++# CONFIG_NFS_FSCACHE is not set
++CONFIG_NFSD=m
++# CONFIG_NFSD_V3 is not set
++# CONFIG_NFSD_V4 is not set
++CONFIG_LOCKD=m
++CONFIG_LOCKD_V4=y
++CONFIG_EXPORTFS=m
++CONFIG_NFS_COMMON=y
++CONFIG_SUNRPC=m
++# CONFIG_RPCSEC_GSS_KRB5 is not set
++# CONFIG_RPCSEC_GSS_SPKM3 is not set
++# CONFIG_SMB_FS is not set
++CONFIG_CIFS=m
++# CONFIG_CIFS_STATS is not set
++# CONFIG_CIFS_WEAK_PW_HASH is not set
++# CONFIG_CIFS_UPCALL is not set
++# CONFIG_CIFS_XATTR is not set
++# CONFIG_CIFS_DEBUG2 is not set
++# CONFIG_CIFS_DFS_UPCALL is not set
++# CONFIG_CIFS_EXPERIMENTAL is not set
++CONFIG_NCP_FS=m
++# CONFIG_NCPFS_PACKET_SIGNING is not set
++# CONFIG_NCPFS_IOCTL_LOCKING is not set
++# CONFIG_NCPFS_STRONG is not set
++# CONFIG_NCPFS_NFS_NS is not set
++# CONFIG_NCPFS_OS2_NS is not set
++# CONFIG_NCPFS_SMALLDOS is not set
++# CONFIG_NCPFS_NLS is not set
++# CONFIG_NCPFS_EXTRAS is not set
++CONFIG_CODA_FS=m
++CONFIG_AFS_FS=m
++# CONFIG_AFS_DEBUG is not set
++# CONFIG_AFS_FSCACHE is not set
++
++#
++# Partition Types
++#
++# CONFIG_PARTITION_ADVANCED is not set
++CONFIG_MSDOS_PARTITION=y
++CONFIG_NLS=y
++CONFIG_NLS_DEFAULT="iso8859-1"
++CONFIG_NLS_CODEPAGE_437=y
++# CONFIG_NLS_CODEPAGE_737 is not set
++# CONFIG_NLS_CODEPAGE_775 is not set
++CONFIG_NLS_CODEPAGE_850=y
++# CONFIG_NLS_CODEPAGE_852 is not set
++# CONFIG_NLS_CODEPAGE_855 is not set
++# CONFIG_NLS_CODEPAGE_857 is not set
++# CONFIG_NLS_CODEPAGE_860 is not set
++# CONFIG_NLS_CODEPAGE_861 is not set
++# CONFIG_NLS_CODEPAGE_862 is not set
++# CONFIG_NLS_CODEPAGE_863 is not set
++# CONFIG_NLS_CODEPAGE_864 is not set
++# CONFIG_NLS_CODEPAGE_865 is not set
++# CONFIG_NLS_CODEPAGE_866 is not set
++# CONFIG_NLS_CODEPAGE_869 is not set
++# CONFIG_NLS_CODEPAGE_936 is not set
++# CONFIG_NLS_CODEPAGE_950 is not set
++# CONFIG_NLS_CODEPAGE_932 is not set
++# CONFIG_NLS_CODEPAGE_949 is not set
++# CONFIG_NLS_CODEPAGE_874 is not set
++# CONFIG_NLS_ISO8859_8 is not set
++# CONFIG_NLS_CODEPAGE_1250 is not set
++# CONFIG_NLS_CODEPAGE_1251 is not set
++# CONFIG_NLS_ASCII is not set
++CONFIG_NLS_ISO8859_1=y
++# CONFIG_NLS_ISO8859_2 is not set
++# CONFIG_NLS_ISO8859_3 is not set
++# CONFIG_NLS_ISO8859_4 is not set
++# CONFIG_NLS_ISO8859_5 is not set
++# CONFIG_NLS_ISO8859_6 is not set
++# CONFIG_NLS_ISO8859_7 is not set
++# CONFIG_NLS_ISO8859_9 is not set
++# CONFIG_NLS_ISO8859_13 is not set
++# CONFIG_NLS_ISO8859_14 is not set
++CONFIG_NLS_ISO8859_15=y
++# CONFIG_NLS_KOI8_R is not set
++# CONFIG_NLS_KOI8_U is not set
++CONFIG_NLS_UTF8=y
++CONFIG_DLM=m
++# CONFIG_DLM_DEBUG is not set
++
++#
++# Kernel hacking
++#
++# CONFIG_PRINTK_TIME is not set
++# CONFIG_ENABLE_WARN_DEPRECATED is not set
++CONFIG_ENABLE_MUST_CHECK=y
++CONFIG_FRAME_WARN=1024
++# CONFIG_MAGIC_SYSRQ is not set
++# CONFIG_STRIP_ASM_SYMS is not set
++# CONFIG_UNUSED_SYMBOLS is not set
++CONFIG_DEBUG_FS=y
++# CONFIG_HEADERS_CHECK is not set
++# CONFIG_DEBUG_KERNEL is not set
++# CONFIG_DEBUG_BUGVERBOSE is not set
++# CONFIG_DEBUG_MEMORY_INIT is not set
++CONFIG_FRAME_POINTER=y
++# CONFIG_RCU_CPU_STALL_DETECTOR is not set
++# CONFIG_LATENCYTOP is not set
++# CONFIG_SYSCTL_SYSCALL_CHECK is not set
++CONFIG_HAVE_FUNCTION_TRACER=y
++CONFIG_TRACING_SUPPORT=y
++# CONFIG_FTRACE is not set
++# CONFIG_DYNAMIC_DEBUG is not set
++# CONFIG_SAMPLES is not set
++CONFIG_HAVE_ARCH_KGDB=y
++# CONFIG_ARM_UNWIND is not set
++# CONFIG_DEBUG_USER is not set
++
++#
++# Security options
++#
++CONFIG_KEYS=y
++# CONFIG_KEYS_DEBUG_PROC_KEYS is not set
++# CONFIG_SECURITY is not set
++# CONFIG_SECURITYFS is not set
++# CONFIG_SECURITY_FILE_CAPABILITIES is not set
++CONFIG_XOR_BLOCKS=m
++CONFIG_ASYNC_CORE=m
++CONFIG_ASYNC_MEMCPY=m
++CONFIG_ASYNC_XOR=m
++CONFIG_ASYNC_PQ=m
++CONFIG_ASYNC_RAID6_RECOV=m
++CONFIG_CRYPTO=y
++
++#
++# Crypto core or helper
++#
++# CONFIG_CRYPTO_FIPS is not set
++CONFIG_CRYPTO_ALGAPI=m
++CONFIG_CRYPTO_ALGAPI2=m
++CONFIG_CRYPTO_AEAD=m
++CONFIG_CRYPTO_AEAD2=m
++CONFIG_CRYPTO_BLKCIPHER=m
++CONFIG_CRYPTO_BLKCIPHER2=m
++CONFIG_CRYPTO_HASH=m
++CONFIG_CRYPTO_HASH2=m
++CONFIG_CRYPTO_RNG=m
++CONFIG_CRYPTO_RNG2=m
++CONFIG_CRYPTO_PCOMP=m
++CONFIG_CRYPTO_MANAGER=m
++CONFIG_CRYPTO_MANAGER2=m
++CONFIG_CRYPTO_GF128MUL=m
++CONFIG_CRYPTO_NULL=m
++CONFIG_CRYPTO_WORKQUEUE=m
++CONFIG_CRYPTO_CRYPTD=m
++CONFIG_CRYPTO_AUTHENC=m
++CONFIG_CRYPTO_TEST=m
++
++#
++# Authenticated Encryption with Associated Data
++#
++CONFIG_CRYPTO_CCM=m
++CONFIG_CRYPTO_GCM=m
++CONFIG_CRYPTO_SEQIV=m
++
++#
++# Block modes
++#
++CONFIG_CRYPTO_CBC=m
++CONFIG_CRYPTO_CTR=m
++# CONFIG_CRYPTO_CTS is not set
++CONFIG_CRYPTO_ECB=m
++CONFIG_CRYPTO_LRW=m
++CONFIG_CRYPTO_PCBC=m
++CONFIG_CRYPTO_XTS=m
++
++#
++# Hash modes
++#
++CONFIG_CRYPTO_HMAC=m
++CONFIG_CRYPTO_XCBC=m
++CONFIG_CRYPTO_VMAC=m
++
++#
++# Digest
++#
++CONFIG_CRYPTO_CRC32C=m
++CONFIG_CRYPTO_GHASH=m
++CONFIG_CRYPTO_MD4=m
++CONFIG_CRYPTO_MD5=m
++CONFIG_CRYPTO_MICHAEL_MIC=m
++CONFIG_CRYPTO_RMD128=m
++CONFIG_CRYPTO_RMD160=m
++CONFIG_CRYPTO_RMD256=m
++CONFIG_CRYPTO_RMD320=m
++CONFIG_CRYPTO_SHA1=m
++CONFIG_CRYPTO_SHA256=m
++CONFIG_CRYPTO_SHA512=m
++CONFIG_CRYPTO_TGR192=m
++CONFIG_CRYPTO_WP512=m
++
++#
++# Ciphers
++#
++CONFIG_CRYPTO_AES=m
++CONFIG_CRYPTO_ANUBIS=m
++CONFIG_CRYPTO_ARC4=m
++CONFIG_CRYPTO_BLOWFISH=m
++CONFIG_CRYPTO_CAMELLIA=m
++CONFIG_CRYPTO_CAST5=m
++CONFIG_CRYPTO_CAST6=m
++CONFIG_CRYPTO_DES=m
++CONFIG_CRYPTO_FCRYPT=m
++CONFIG_CRYPTO_KHAZAD=m
++# CONFIG_CRYPTO_SALSA20 is not set
++CONFIG_CRYPTO_SEED=m
++CONFIG_CRYPTO_SERPENT=m
++CONFIG_CRYPTO_TEA=m
++CONFIG_CRYPTO_TWOFISH=m
++CONFIG_CRYPTO_TWOFISH_COMMON=m
++
++#
++# Compression
++#
++CONFIG_CRYPTO_DEFLATE=m
++CONFIG_CRYPTO_ZLIB=m
++CONFIG_CRYPTO_LZO=m
++
++#
++# Random Number Generation
++#
++CONFIG_CRYPTO_ANSI_CPRNG=m
++CONFIG_CRYPTO_HW=y
++# CONFIG_BINARY_PRINTF is not set
++
++#
++# Library routines
++#
++CONFIG_BITREVERSE=y
++CONFIG_GENERIC_FIND_LAST_BIT=y
++CONFIG_CRC_CCITT=m
++CONFIG_CRC16=y
++CONFIG_CRC_T10DIF=m
++CONFIG_CRC_ITU_T=y
++CONFIG_CRC32=y
++CONFIG_CRC7=m
++CONFIG_LIBCRC32C=m
++CONFIG_ZLIB_INFLATE=y
++CONFIG_ZLIB_DEFLATE=y
++CONFIG_LZO_COMPRESS=m
++CONFIG_LZO_DECOMPRESS=m
++CONFIG_DECOMPRESS_GZIP=y
++CONFIG_TEXTSEARCH=y
++CONFIG_TEXTSEARCH_KMP=m
++CONFIG_TEXTSEARCH_BM=m
++CONFIG_TEXTSEARCH_FSM=m
++CONFIG_HAS_IOMEM=y
++CONFIG_HAS_IOPORT=y
++CONFIG_HAS_DMA=y
++CONFIG_NLATTR=y
+diff -Nur linux-2.6.32.2.orig/foxg20.config-2.6.32.2-SIMPLE linux-2.6.32.2/foxg20.config-2.6.32.2-SIMPLE
+--- linux-2.6.32.2.orig/foxg20.config-2.6.32.2-SIMPLE 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.32.2/foxg20.config-2.6.32.2-SIMPLE 2010-02-16 21:29:07.000000000 +0100
+@@ -0,0 +1,1339 @@
++#
++# Automatically generated make config: don't edit
++# Linux kernel version: 2.6.32.2
++# Thu Jan 7 10:45:15 2010
++#
++CONFIG_ARM=y
++CONFIG_SYS_SUPPORTS_APM_EMULATION=y
++CONFIG_GENERIC_GPIO=y
++CONFIG_GENERIC_TIME=y
++CONFIG_GENERIC_CLOCKEVENTS=y
++CONFIG_GENERIC_HARDIRQS=y
++CONFIG_STACKTRACE_SUPPORT=y
++CONFIG_HAVE_LATENCYTOP_SUPPORT=y
++CONFIG_LOCKDEP_SUPPORT=y
++CONFIG_TRACE_IRQFLAGS_SUPPORT=y
++CONFIG_HARDIRQS_SW_RESEND=y
++CONFIG_GENERIC_IRQ_PROBE=y
++CONFIG_RWSEM_GENERIC_SPINLOCK=y
++CONFIG_GENERIC_HWEIGHT=y
++CONFIG_GENERIC_CALIBRATE_DELAY=y
++CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
++CONFIG_VECTORS_BASE=0xffff0000
++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
++CONFIG_CONSTRUCTORS=y
++
++#
++# General setup
++#
++CONFIG_EXPERIMENTAL=y
++CONFIG_BROKEN_ON_SMP=y
++CONFIG_INIT_ENV_ARG_LIMIT=32
++CONFIG_LOCALVERSION=""
++# CONFIG_LOCALVERSION_AUTO is not set
++CONFIG_SWAP=y
++CONFIG_SYSVIPC=y
++CONFIG_SYSVIPC_SYSCTL=y
++# CONFIG_POSIX_MQUEUE is not set
++# CONFIG_BSD_PROCESS_ACCT is not set
++# CONFIG_TASKSTATS is not set
++# CONFIG_AUDIT is not set
++
++#
++# RCU Subsystem
++#
++CONFIG_TREE_RCU=y
++# CONFIG_TREE_PREEMPT_RCU is not set
++# CONFIG_RCU_TRACE is not set
++CONFIG_RCU_FANOUT=32
++# CONFIG_RCU_FANOUT_EXACT is not set
++# CONFIG_TREE_RCU_TRACE is not set
++# CONFIG_IKCONFIG is not set
++CONFIG_LOG_BUF_SHIFT=14
++# CONFIG_GROUP_SCHED is not set
++# CONFIG_CGROUPS is not set
++# CONFIG_SYSFS_DEPRECATED_V2 is not set
++# CONFIG_RELAY is not set
++# CONFIG_NAMESPACES is not set
++CONFIG_BLK_DEV_INITRD=y
++CONFIG_INITRAMFS_SOURCE=""
++CONFIG_RD_GZIP=y
++# CONFIG_RD_BZIP2 is not set
++# CONFIG_RD_LZMA is not set
++CONFIG_CC_OPTIMIZE_FOR_SIZE=y
++CONFIG_SYSCTL=y
++CONFIG_ANON_INODES=y
++CONFIG_EMBEDDED=y
++CONFIG_UID16=y
++CONFIG_SYSCTL_SYSCALL=y
++CONFIG_KALLSYMS=y
++# CONFIG_KALLSYMS_EXTRA_PASS is not set
++CONFIG_HOTPLUG=y
++CONFIG_PRINTK=y
++CONFIG_BUG=y
++CONFIG_ELF_CORE=y
++CONFIG_BASE_FULL=y
++CONFIG_FUTEX=y
++CONFIG_EPOLL=y
++CONFIG_SIGNALFD=y
++CONFIG_TIMERFD=y
++CONFIG_EVENTFD=y
++CONFIG_SHMEM=y
++CONFIG_AIO=y
++
++#
++# Kernel Performance Events And Counters
++#
++CONFIG_VM_EVENT_COUNTERS=y
++CONFIG_COMPAT_BRK=y
++CONFIG_SLAB=y
++# CONFIG_SLUB is not set
++# CONFIG_SLOB is not set
++# CONFIG_PROFILING is not set
++CONFIG_HAVE_OPROFILE=y
++# CONFIG_KPROBES is not set
++CONFIG_HAVE_KPROBES=y
++CONFIG_HAVE_KRETPROBES=y
++CONFIG_HAVE_CLK=y
++
++#
++# GCOV-based kernel profiling
++#
++# CONFIG_SLOW_WORK is not set
++CONFIG_HAVE_GENERIC_DMA_COHERENT=y
++CONFIG_SLABINFO=y
++CONFIG_RT_MUTEXES=y
++CONFIG_BASE_SMALL=0
++CONFIG_MODULES=y
++# CONFIG_MODULE_FORCE_LOAD is not set
++CONFIG_MODULE_UNLOAD=y
++# CONFIG_MODULE_FORCE_UNLOAD is not set
++# CONFIG_MODVERSIONS is not set
++# CONFIG_MODULE_SRCVERSION_ALL is not set
++CONFIG_BLOCK=y
++CONFIG_LBDAF=y
++# CONFIG_BLK_DEV_BSG is not set
++# CONFIG_BLK_DEV_INTEGRITY is not set
++
++#
++# IO Schedulers
++#
++CONFIG_IOSCHED_NOOP=y
++CONFIG_IOSCHED_AS=y
++# CONFIG_IOSCHED_DEADLINE is not set
++# CONFIG_IOSCHED_CFQ is not set
++CONFIG_DEFAULT_AS=y
++# CONFIG_DEFAULT_DEADLINE is not set
++# CONFIG_DEFAULT_CFQ is not set
++# CONFIG_DEFAULT_NOOP is not set
++CONFIG_DEFAULT_IOSCHED="anticipatory"
++CONFIG_FREEZER=y
++
++#
++# System Type
++#
++CONFIG_MMU=y
++# CONFIG_ARCH_AAEC2000 is not set
++# CONFIG_ARCH_INTEGRATOR is not set
++# CONFIG_ARCH_REALVIEW is not set
++# CONFIG_ARCH_VERSATILE is not set
++CONFIG_ARCH_AT91=y
++# CONFIG_ARCH_CLPS711X is not set
++# CONFIG_ARCH_GEMINI is not set
++# CONFIG_ARCH_EBSA110 is not set
++# CONFIG_ARCH_EP93XX is not set
++# CONFIG_ARCH_FOOTBRIDGE is not set
++# CONFIG_ARCH_MXC is not set
++# CONFIG_ARCH_STMP3XXX is not set
++# CONFIG_ARCH_NETX is not set
++# CONFIG_ARCH_H720X is not set
++# CONFIG_ARCH_NOMADIK is not set
++# CONFIG_ARCH_IOP13XX is not set
++# CONFIG_ARCH_IOP32X is not set
++# CONFIG_ARCH_IOP33X is not set
++# CONFIG_ARCH_IXP23XX is not set
++# CONFIG_ARCH_IXP2000 is not set
++# CONFIG_ARCH_IXP4XX is not set
++# CONFIG_ARCH_L7200 is not set
++# CONFIG_ARCH_KIRKWOOD is not set
++# CONFIG_ARCH_LOKI is not set
++# CONFIG_ARCH_MV78XX0 is not set
++# CONFIG_ARCH_ORION5X is not set
++# CONFIG_ARCH_MMP is not set
++# CONFIG_ARCH_KS8695 is not set
++# CONFIG_ARCH_NS9XXX is not set
++# CONFIG_ARCH_W90X900 is not set
++# CONFIG_ARCH_PNX4008 is not set
++# CONFIG_ARCH_PXA is not set
++# CONFIG_ARCH_MSM is not set
++# CONFIG_ARCH_RPC is not set
++# CONFIG_ARCH_SA1100 is not set
++# CONFIG_ARCH_S3C2410 is not set
++# CONFIG_ARCH_S3C64XX is not set
++# CONFIG_ARCH_S5PC1XX is not set
++# CONFIG_ARCH_SHARK is not set
++# CONFIG_ARCH_LH7A40X is not set
++# CONFIG_ARCH_U300 is not set
++# CONFIG_ARCH_DAVINCI is not set
++# CONFIG_ARCH_OMAP is not set
++# CONFIG_ARCH_BCMRING is not set
++
++#
++# Atmel AT91 System-on-Chip
++#
++# CONFIG_ARCH_AT91RM9200 is not set
++# CONFIG_ARCH_AT91SAM9260 is not set
++# CONFIG_ARCH_AT91SAM9261 is not set
++# CONFIG_ARCH_AT91SAM9G10 is not set
++# CONFIG_ARCH_AT91SAM9263 is not set
++# CONFIG_ARCH_AT91SAM9RL is not set
++CONFIG_ARCH_AT91SAM9G20=y
++# CONFIG_ARCH_AT91SAM9G45 is not set
++# CONFIG_ARCH_AT91CAP9 is not set
++# CONFIG_ARCH_AT91X40 is not set
++CONFIG_AT91_PMC_UNIT=y
++
++#
++# AT91SAM9G20 Board Type
++#
++# CONFIG_MACH_AT91SAM9G20EK is not set
++# CONFIG_MACH_AT91SAM9G20EK_2MMC is not set
++# CONFIG_MACH_CPU9G20 is not set
++CONFIG_MACH_FOXG20=y
++
++#
++# AT91 Board Options
++#
++# CONFIG_MTD_AT91_DATAFLASH_CARD is not set
++# CONFIG_MTD_NAND_ATMEL_BUSWIDTH_16 is not set
++
++#
++# AT91 Feature Selections
++#
++CONFIG_AT91_PROGRAMMABLE_CLOCKS=y
++CONFIG_AT91_SLOW_CLOCK=y
++CONFIG_AT91_TIMER_HZ=100
++# CONFIG_AT91_EARLY_DBGU is not set
++CONFIG_AT91_EARLY_USART0=y
++# CONFIG_AT91_EARLY_USART1 is not set
++# CONFIG_AT91_EARLY_USART2 is not set
++# CONFIG_AT91_EARLY_USART3 is not set
++# CONFIG_AT91_EARLY_USART4 is not set
++# CONFIG_AT91_EARLY_USART5 is not set
++
++#
++# Processor Type
++#
++CONFIG_CPU_32=y
++CONFIG_CPU_ARM926T=y
++CONFIG_CPU_32v5=y
++CONFIG_CPU_ABRT_EV5TJ=y
++CONFIG_CPU_PABRT_LEGACY=y
++CONFIG_CPU_CACHE_VIVT=y
++CONFIG_CPU_COPY_V4WB=y
++CONFIG_CPU_TLB_V4WBI=y
++CONFIG_CPU_CP15=y
++CONFIG_CPU_CP15_MMU=y
++
++#
++# Processor Features
++#
++CONFIG_ARM_THUMB=y
++# CONFIG_CPU_ICACHE_DISABLE is not set
++# CONFIG_CPU_DCACHE_DISABLE is not set
++# CONFIG_CPU_DCACHE_WRITETHROUGH is not set
++# CONFIG_CPU_CACHE_ROUND_ROBIN is not set
++CONFIG_ARM_L1_CACHE_SHIFT=5
++
++#
++# Bus support
++#
++# CONFIG_PCI_SYSCALL is not set
++# CONFIG_ARCH_SUPPORTS_MSI is not set
++# CONFIG_PCCARD is not set
++
++#
++# Kernel Features
++#
++CONFIG_TICK_ONESHOT=y
++CONFIG_NO_HZ=y
++CONFIG_HIGH_RES_TIMERS=y
++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
++CONFIG_VMSPLIT_3G=y
++# CONFIG_VMSPLIT_2G is not set
++# CONFIG_VMSPLIT_1G is not set
++CONFIG_PAGE_OFFSET=0xC0000000
++CONFIG_PREEMPT_NONE=y
++# CONFIG_PREEMPT_VOLUNTARY is not set
++# CONFIG_PREEMPT is not set
++CONFIG_HZ=100
++CONFIG_AEABI=y
++CONFIG_OABI_COMPAT=y
++# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
++# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
++# CONFIG_HIGHMEM is not set
++CONFIG_SELECT_MEMORY_MODEL=y
++CONFIG_FLATMEM_MANUAL=y
++# CONFIG_DISCONTIGMEM_MANUAL is not set
++# CONFIG_SPARSEMEM_MANUAL is not set
++CONFIG_FLATMEM=y
++CONFIG_FLAT_NODE_MEM_MAP=y
++CONFIG_PAGEFLAGS_EXTENDED=y
++CONFIG_SPLIT_PTLOCK_CPUS=4096
++# CONFIG_PHYS_ADDR_T_64BIT is not set
++CONFIG_ZONE_DMA_FLAG=0
++CONFIG_VIRT_TO_BUS=y
++CONFIG_HAVE_MLOCK=y
++CONFIG_HAVE_MLOCKED_PAGE_BIT=y
++# CONFIG_KSM is not set
++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
++CONFIG_LEDS=y
++CONFIG_LEDS_CPU=y
++CONFIG_ALIGNMENT_TRAP=y
++# CONFIG_UACCESS_WITH_MEMCPY is not set
++
++#
++# Boot options
++#
++CONFIG_ZBOOT_ROM_TEXT=0x0
++CONFIG_ZBOOT_ROM_BSS=0x0
++CONFIG_CMDLINE=""
++# CONFIG_XIP_KERNEL is not set
++# CONFIG_KEXEC is not set
++
++#
++# CPU Power Management
++#
++CONFIG_CPU_IDLE=y
++CONFIG_CPU_IDLE_GOV_LADDER=y
++CONFIG_CPU_IDLE_GOV_MENU=y
++
++#
++# Floating point emulation
++#
++
++#
++# At least one emulation must be selected
++#
++CONFIG_FPE_NWFPE=y
++# CONFIG_FPE_NWFPE_XP is not set
++# CONFIG_FPE_FASTFPE is not set
++# CONFIG_VFP is not set
++
++#
++# Userspace binary formats
++#
++CONFIG_BINFMT_ELF=y
++# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
++CONFIG_HAVE_AOUT=y
++# CONFIG_BINFMT_AOUT is not set
++# CONFIG_BINFMT_MISC is not set
++
++#
++# Power management options
++#
++CONFIG_PM=y
++# CONFIG_PM_DEBUG is not set
++CONFIG_PM_SLEEP=y
++CONFIG_SUSPEND=y
++CONFIG_SUSPEND_FREEZER=y
++# CONFIG_APM_EMULATION is not set
++# CONFIG_PM_RUNTIME is not set
++CONFIG_ARCH_SUSPEND_POSSIBLE=y
++CONFIG_NET=y
++
++#
++# Networking options
++#
++CONFIG_PACKET=y
++# CONFIG_PACKET_MMAP is not set
++CONFIG_UNIX=y
++# CONFIG_NET_KEY is not set
++CONFIG_INET=y
++# CONFIG_IP_MULTICAST is not set
++# CONFIG_IP_ADVANCED_ROUTER is not set
++CONFIG_IP_FIB_HASH=y
++CONFIG_IP_PNP=y
++# CONFIG_IP_PNP_DHCP is not set
++CONFIG_IP_PNP_BOOTP=y
++# CONFIG_IP_PNP_RARP is not set
++# CONFIG_NET_IPIP is not set
++# CONFIG_NET_IPGRE is not set
++# CONFIG_ARPD is not set
++# CONFIG_SYN_COOKIES is not set
++# CONFIG_INET_AH is not set
++# CONFIG_INET_ESP is not set
++# CONFIG_INET_IPCOMP is not set
++# CONFIG_INET_XFRM_TUNNEL is not set
++# CONFIG_INET_TUNNEL is not set
++# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
++# CONFIG_INET_XFRM_MODE_TUNNEL is not set
++# CONFIG_INET_XFRM_MODE_BEET is not set
++# CONFIG_INET_LRO is not set
++CONFIG_INET_DIAG=y
++CONFIG_INET_TCP_DIAG=y
++# CONFIG_TCP_CONG_ADVANCED is not set
++CONFIG_TCP_CONG_CUBIC=y
++CONFIG_DEFAULT_TCP_CONG="cubic"
++# CONFIG_TCP_MD5SIG is not set
++# CONFIG_IPV6 is not set
++# CONFIG_NETWORK_SECMARK is not set
++# CONFIG_NETFILTER is not set
++# CONFIG_IP_DCCP is not set
++# CONFIG_IP_SCTP is not set
++# CONFIG_RDS is not set
++# CONFIG_TIPC is not set
++# CONFIG_ATM is not set
++# CONFIG_BRIDGE is not set
++# CONFIG_NET_DSA is not set
++# CONFIG_VLAN_8021Q is not set
++# CONFIG_DECNET is not set
++# CONFIG_LLC2 is not set
++# CONFIG_IPX is not set
++# CONFIG_ATALK is not set
++# CONFIG_X25 is not set
++# CONFIG_LAPB is not set
++# CONFIG_ECONET is not set
++# CONFIG_WAN_ROUTER is not set
++# CONFIG_PHONET is not set
++# CONFIG_IEEE802154 is not set
++# CONFIG_NET_SCHED is not set
++# CONFIG_DCB is not set
++
++#
++# Network testing
++#
++# CONFIG_NET_PKTGEN is not set
++# CONFIG_HAMRADIO is not set
++# CONFIG_CAN is not set
++# CONFIG_IRDA is not set
++# CONFIG_BT is not set
++# CONFIG_AF_RXRPC is not set
++# CONFIG_WIRELESS is not set
++# CONFIG_WIMAX is not set
++# CONFIG_RFKILL is not set
++# CONFIG_NET_9P is not set
++
++#
++# Device Drivers
++#
++
++#
++# Generic Driver Options
++#
++CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
++# CONFIG_DEVTMPFS is not set
++CONFIG_STANDALONE=y
++# CONFIG_PREVENT_FIRMWARE_BUILD is not set
++# CONFIG_FW_LOADER is not set
++# CONFIG_SYS_HYPERVISOR is not set
++# CONFIG_CONNECTOR is not set
++CONFIG_MTD=y
++# CONFIG_MTD_DEBUG is not set
++# CONFIG_MTD_TESTS is not set
++CONFIG_MTD_CONCAT=y
++CONFIG_MTD_PARTITIONS=y
++# CONFIG_MTD_REDBOOT_PARTS is not set
++CONFIG_MTD_CMDLINE_PARTS=y
++# CONFIG_MTD_AFS_PARTS is not set
++# CONFIG_MTD_AR7_PARTS is not set
++
++#
++# User Modules And Translation Layers
++#
++CONFIG_MTD_CHAR=y
++CONFIG_MTD_BLKDEVS=y
++CONFIG_MTD_BLOCK=y
++# CONFIG_FTL is not set
++# CONFIG_NFTL is not set
++# CONFIG_INFTL is not set
++# CONFIG_RFD_FTL is not set
++# CONFIG_SSFDC is not set
++# CONFIG_MTD_OOPS is not set
++
++#
++# RAM/ROM/Flash chip drivers
++#
++# CONFIG_MTD_CFI is not set
++# CONFIG_MTD_JEDECPROBE is not set
++CONFIG_MTD_MAP_BANK_WIDTH_1=y
++CONFIG_MTD_MAP_BANK_WIDTH_2=y
++CONFIG_MTD_MAP_BANK_WIDTH_4=y
++# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
++# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
++# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
++CONFIG_MTD_CFI_I1=y
++CONFIG_MTD_CFI_I2=y
++# CONFIG_MTD_CFI_I4 is not set
++# CONFIG_MTD_CFI_I8 is not set
++# CONFIG_MTD_RAM is not set
++# CONFIG_MTD_ROM is not set
++# CONFIG_MTD_ABSENT is not set
++
++#
++# Mapping drivers for chip access
++#
++# CONFIG_MTD_COMPLEX_MAPPINGS is not set
++# CONFIG_MTD_PLATRAM is not set
++
++#
++# Self-contained MTD device drivers
++#
++CONFIG_MTD_DATAFLASH=y
++# CONFIG_MTD_DATAFLASH_WRITE_VERIFY is not set
++# CONFIG_MTD_DATAFLASH_OTP is not set
++# CONFIG_MTD_M25P80 is not set
++# CONFIG_MTD_SST25L is not set
++# CONFIG_MTD_SLRAM is not set
++# CONFIG_MTD_PHRAM is not set
++# CONFIG_MTD_MTDRAM is not set
++# CONFIG_MTD_BLOCK2MTD is not set
++
++#
++# Disk-On-Chip Device Drivers
++#
++# CONFIG_MTD_DOC2000 is not set
++# CONFIG_MTD_DOC2001 is not set
++# CONFIG_MTD_DOC2001PLUS is not set
++CONFIG_MTD_NAND=y
++# CONFIG_MTD_NAND_VERIFY_WRITE is not set
++# CONFIG_MTD_NAND_ECC_SMC is not set
++# CONFIG_MTD_NAND_MUSEUM_IDS is not set
++# CONFIG_MTD_NAND_GPIO is not set
++CONFIG_MTD_NAND_IDS=y
++# CONFIG_MTD_NAND_DISKONCHIP is not set
++CONFIG_MTD_NAND_ATMEL=y
++# CONFIG_MTD_NAND_ATMEL_ECC_HW is not set
++CONFIG_MTD_NAND_ATMEL_ECC_SOFT=y
++# CONFIG_MTD_NAND_ATMEL_ECC_NONE is not set
++# CONFIG_MTD_NAND_NANDSIM is not set
++# CONFIG_MTD_NAND_PLATFORM is not set
++# CONFIG_MTD_ALAUDA is not set
++# CONFIG_MTD_ONENAND is not set
++
++#
++# LPDDR flash memory drivers
++#
++# CONFIG_MTD_LPDDR is not set
++
++#
++# UBI - Unsorted block images
++#
++# CONFIG_MTD_UBI is not set
++# CONFIG_PARPORT is not set
++CONFIG_BLK_DEV=y
++# CONFIG_BLK_DEV_COW_COMMON is not set
++CONFIG_BLK_DEV_LOOP=y
++# CONFIG_BLK_DEV_CRYPTOLOOP is not set
++# CONFIG_BLK_DEV_NBD is not set
++# CONFIG_BLK_DEV_UB is not set
++CONFIG_BLK_DEV_RAM=y
++CONFIG_BLK_DEV_RAM_COUNT=16
++CONFIG_BLK_DEV_RAM_SIZE=8192
++# CONFIG_BLK_DEV_XIP is not set
++# CONFIG_CDROM_PKTCDVD is not set
++# CONFIG_ATA_OVER_ETH is not set
++# CONFIG_MG_DISK is not set
++CONFIG_MISC_DEVICES=y
++CONFIG_ATMEL_TCLIB=y
++CONFIG_ATMEL_TCB_CLKSRC=y
++CONFIG_ATMEL_TCB_CLKSRC_BLOCK=0
++CONFIG_ATMEL_SSC=y
++# CONFIG_ENCLOSURE_SERVICES is not set
++# CONFIG_C2PORT is not set
++
++#
++# EEPROM support
++#
++# CONFIG_EEPROM_AT25 is not set
++# CONFIG_EEPROM_93CX6 is not set
++CONFIG_HAVE_IDE=y
++# CONFIG_IDE is not set
++
++#
++# SCSI device support
++#
++# CONFIG_RAID_ATTRS is not set
++CONFIG_SCSI=y
++CONFIG_SCSI_DMA=y
++CONFIG_SCSI_TGT=y
++# CONFIG_SCSI_NETLINK is not set
++CONFIG_SCSI_PROC_FS=y
++
++#
++# SCSI support type (disk, tape, CD-ROM)
++#
++CONFIG_BLK_DEV_SD=y
++# CONFIG_CHR_DEV_ST is not set
++# CONFIG_CHR_DEV_OSST is not set
++# CONFIG_BLK_DEV_SR is not set
++# CONFIG_CHR_DEV_SG is not set
++# CONFIG_CHR_DEV_SCH is not set
++CONFIG_SCSI_MULTI_LUN=y
++# CONFIG_SCSI_CONSTANTS is not set
++# CONFIG_SCSI_LOGGING is not set
++# CONFIG_SCSI_SCAN_ASYNC is not set
++CONFIG_SCSI_WAIT_SCAN=m
++
++#
++# SCSI Transports
++#
++# CONFIG_SCSI_SPI_ATTRS is not set
++# CONFIG_SCSI_FC_ATTRS is not set
++# CONFIG_SCSI_ISCSI_ATTRS is not set
++# CONFIG_SCSI_SAS_LIBSAS is not set
++# CONFIG_SCSI_SRP_ATTRS is not set
++# CONFIG_SCSI_LOWLEVEL is not set
++# CONFIG_SCSI_DH is not set
++# CONFIG_SCSI_OSD_INITIATOR is not set
++# CONFIG_ATA is not set
++# CONFIG_MD is not set
++CONFIG_NETDEVICES=y
++# CONFIG_DUMMY is not set
++# CONFIG_BONDING is not set
++# CONFIG_MACVLAN is not set
++# CONFIG_EQUALIZER is not set
++# CONFIG_TUN is not set
++# CONFIG_VETH is not set
++CONFIG_PHYLIB=y
++
++#
++# MII PHY device drivers
++#
++# CONFIG_MARVELL_PHY is not set
++# CONFIG_DAVICOM_PHY is not set
++# CONFIG_QSEMI_PHY is not set
++# CONFIG_LXT_PHY is not set
++# CONFIG_CICADA_PHY is not set
++# CONFIG_VITESSE_PHY is not set
++# CONFIG_SMSC_PHY is not set
++# CONFIG_BROADCOM_PHY is not set
++# CONFIG_ICPLUS_PHY is not set
++# CONFIG_REALTEK_PHY is not set
++# CONFIG_NATIONAL_PHY is not set
++# CONFIG_STE10XP is not set
++# CONFIG_LSI_ET1011C_PHY is not set
++# CONFIG_FIXED_PHY is not set
++# CONFIG_MDIO_BITBANG is not set
++CONFIG_NET_ETHERNET=y
++CONFIG_MII=y
++CONFIG_MACB=y
++# CONFIG_AX88796 is not set
++# CONFIG_SMC91X is not set
++# CONFIG_DM9000 is not set
++# CONFIG_ENC28J60 is not set
++# CONFIG_ETHOC is not set
++# CONFIG_SMC911X is not set
++# CONFIG_SMSC911X is not set
++# CONFIG_DNET is not set
++# CONFIG_IBM_NEW_EMAC_ZMII is not set
++# CONFIG_IBM_NEW_EMAC_RGMII is not set
++# CONFIG_IBM_NEW_EMAC_TAH is not set
++# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
++# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set
++# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
++# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
++# CONFIG_B44 is not set
++# CONFIG_KS8842 is not set
++# CONFIG_KS8851 is not set
++# CONFIG_KS8851_MLL is not set
++# CONFIG_NETDEV_1000 is not set
++# CONFIG_NETDEV_10000 is not set
++CONFIG_WLAN=y
++# CONFIG_WLAN_PRE80211 is not set
++# CONFIG_WLAN_80211 is not set
++
++#
++# Enable WiMAX (Networking options) to see the WiMAX drivers
++#
++
++#
++# USB Network Adapters
++#
++# CONFIG_USB_CATC is not set
++# CONFIG_USB_KAWETH is not set
++# CONFIG_USB_PEGASUS is not set
++# CONFIG_USB_RTL8150 is not set
++# CONFIG_USB_USBNET is not set
++# CONFIG_WAN is not set
++# CONFIG_PPP is not set
++# CONFIG_SLIP is not set
++# CONFIG_NETCONSOLE is not set
++# CONFIG_NETPOLL is not set
++# CONFIG_NET_POLL_CONTROLLER is not set
++# CONFIG_ISDN is not set
++# CONFIG_PHONE is not set
++
++#
++# Input device support
++#
++CONFIG_INPUT=y
++# CONFIG_INPUT_FF_MEMLESS is not set
++# CONFIG_INPUT_POLLDEV is not set
++
++#
++# Userland interfaces
++#
++CONFIG_INPUT_MOUSEDEV=y
++# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
++CONFIG_INPUT_MOUSEDEV_SCREEN_X=320
++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=240
++# CONFIG_INPUT_JOYDEV is not set
++CONFIG_INPUT_EVDEV=y
++# CONFIG_INPUT_EVBUG is not set
++
++#
++# Input Device Drivers
++#
++CONFIG_INPUT_KEYBOARD=y
++# CONFIG_KEYBOARD_ATKBD is not set
++# CONFIG_KEYBOARD_LKKBD is not set
++CONFIG_KEYBOARD_GPIO=y
++# CONFIG_KEYBOARD_MATRIX is not set
++# CONFIG_KEYBOARD_NEWTON is not set
++# CONFIG_KEYBOARD_OPENCORES is not set
++# CONFIG_KEYBOARD_STOWAWAY is not set
++# CONFIG_KEYBOARD_SUNKBD is not set
++# CONFIG_KEYBOARD_XTKBD is not set
++# CONFIG_INPUT_MOUSE is not set
++# CONFIG_INPUT_JOYSTICK is not set
++# CONFIG_INPUT_TABLET is not set
++# CONFIG_INPUT_TOUCHSCREEN is not set
++# CONFIG_INPUT_MISC is not set
++
++#
++# Hardware I/O ports
++#
++CONFIG_SERIO=y
++CONFIG_SERIO_SERPORT=y
++# CONFIG_SERIO_LIBPS2 is not set
++# CONFIG_SERIO_RAW is not set
++# CONFIG_GAMEPORT is not set
++
++#
++# Character devices
++#
++CONFIG_VT=y
++CONFIG_CONSOLE_TRANSLATIONS=y
++CONFIG_VT_CONSOLE=y
++CONFIG_HW_CONSOLE=y
++# CONFIG_VT_HW_CONSOLE_BINDING is not set
++CONFIG_DEVKMEM=y
++# CONFIG_SERIAL_NONSTANDARD is not set
++
++#
++# Serial drivers
++#
++# CONFIG_SERIAL_8250 is not set
++
++#
++# Non-8250 serial port support
++#
++CONFIG_SERIAL_ATMEL=y
++CONFIG_SERIAL_ATMEL_CONSOLE=y
++CONFIG_SERIAL_ATMEL_PDC=y
++# CONFIG_SERIAL_ATMEL_TTYAT is not set
++# CONFIG_SERIAL_MAX3100 is not set
++CONFIG_SERIAL_CORE=y
++CONFIG_SERIAL_CORE_CONSOLE=y
++CONFIG_UNIX98_PTYS=y
++# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
++CONFIG_LEGACY_PTYS=y
++CONFIG_LEGACY_PTY_COUNT=16
++# CONFIG_IPMI_HANDLER is not set
++CONFIG_HW_RANDOM=y
++# CONFIG_HW_RANDOM_TIMERIOMEM is not set
++# CONFIG_NVRAM is not set
++# CONFIG_R3964 is not set
++# CONFIG_RAW_DRIVER is not set
++# CONFIG_TCG_TPM is not set
++# CONFIG_I2C is not set
++CONFIG_SPI=y
++CONFIG_SPI_MASTER=y
++
++#
++# SPI Master Controller Drivers
++#
++CONFIG_SPI_ATMEL=y
++# CONFIG_SPI_BITBANG is not set
++# CONFIG_SPI_GPIO is not set
++
++#
++# SPI Protocol Masters
++#
++# CONFIG_SPI_SPIDEV is not set
++# CONFIG_SPI_TLE62X0 is not set
++
++#
++# PPS support
++#
++# CONFIG_PPS is not set
++CONFIG_ARCH_REQUIRE_GPIOLIB=y
++CONFIG_GPIOLIB=y
++CONFIG_GPIO_SYSFS=y
++
++#
++# Memory mapped GPIO expanders:
++#
++
++#
++# I2C GPIO expanders:
++#
++
++#
++# PCI GPIO expanders:
++#
++
++#
++# SPI GPIO expanders:
++#
++# CONFIG_GPIO_MAX7301 is not set
++# CONFIG_GPIO_MCP23S08 is not set
++# CONFIG_GPIO_MC33880 is not set
++
++#
++# AC97 GPIO expanders:
++#
++# CONFIG_W1 is not set
++# CONFIG_POWER_SUPPLY is not set
++# CONFIG_HWMON is not set
++# CONFIG_THERMAL is not set
++CONFIG_WATCHDOG=y
++# CONFIG_WATCHDOG_NOWAYOUT is not set
++
++#
++# Watchdog Device Drivers
++#
++# CONFIG_SOFT_WATCHDOG is not set
++CONFIG_AT91SAM9X_WATCHDOG=y
++
++#
++# USB-based Watchdog Cards
++#
++# CONFIG_USBPCWATCHDOG is not set
++CONFIG_SSB_POSSIBLE=y
++
++#
++# Sonics Silicon Backplane
++#
++# CONFIG_SSB is not set
++
++#
++# Multifunction device drivers
++#
++# CONFIG_MFD_CORE is not set
++# CONFIG_MFD_SM501 is not set
++# CONFIG_MFD_ASIC3 is not set
++# CONFIG_HTC_EGPIO is not set
++# CONFIG_HTC_PASIC3 is not set
++# CONFIG_MFD_TMIO is not set
++# CONFIG_MFD_T7L66XB is not set
++# CONFIG_MFD_TC6387XB is not set
++# CONFIG_MFD_TC6393XB is not set
++# CONFIG_MFD_MC13783 is not set
++# CONFIG_EZX_PCAP is not set
++# CONFIG_REGULATOR is not set
++# CONFIG_MEDIA_SUPPORT is not set
++
++#
++# Graphics support
++#
++# CONFIG_VGASTATE is not set
++# CONFIG_VIDEO_OUTPUT_CONTROL is not set
++# CONFIG_FB is not set
++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
++
++#
++# Display device support
++#
++# CONFIG_DISPLAY_SUPPORT is not set
++
++#
++# Console display driver support
++#
++# CONFIG_VGA_CONSOLE is not set
++CONFIG_DUMMY_CONSOLE=y
++# CONFIG_SOUND is not set
++CONFIG_HID_SUPPORT=y
++CONFIG_HID=y
++# CONFIG_HIDRAW is not set
++
++#
++# USB Input Devices
++#
++CONFIG_USB_HID=y
++# CONFIG_HID_PID is not set
++# CONFIG_USB_HIDDEV is not set
++
++#
++# Special HID drivers
++#
++# CONFIG_HID_A4TECH is not set
++# CONFIG_HID_APPLE is not set
++# CONFIG_HID_BELKIN is not set
++# CONFIG_HID_CHERRY is not set
++# CONFIG_HID_CHICONY is not set
++# CONFIG_HID_CYPRESS is not set
++# CONFIG_HID_DRAGONRISE is not set
++# CONFIG_HID_EZKEY is not set
++# CONFIG_HID_KYE is not set
++# CONFIG_HID_GYRATION is not set
++# CONFIG_HID_TWINHAN is not set
++# CONFIG_HID_KENSINGTON is not set
++# CONFIG_HID_LOGITECH is not set
++# CONFIG_HID_MICROSOFT is not set
++# CONFIG_HID_MONTEREY is not set
++# CONFIG_HID_NTRIG is not set
++# CONFIG_HID_PANTHERLORD is not set
++# CONFIG_HID_PETALYNX is not set
++# CONFIG_HID_SAMSUNG is not set
++# CONFIG_HID_SONY is not set
++# CONFIG_HID_SUNPLUS is not set
++# CONFIG_HID_GREENASIA is not set
++# CONFIG_HID_SMARTJOYPLUS is not set
++# CONFIG_HID_TOPSEED is not set
++# CONFIG_HID_THRUSTMASTER is not set
++# CONFIG_HID_ZEROPLUS is not set
++CONFIG_USB_SUPPORT=y
++CONFIG_USB_ARCH_HAS_HCD=y
++CONFIG_USB_ARCH_HAS_OHCI=y
++# CONFIG_USB_ARCH_HAS_EHCI is not set
++CONFIG_USB=y
++# CONFIG_USB_DEBUG is not set
++# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set
++
++#
++# Miscellaneous USB options
++#
++CONFIG_USB_DEVICEFS=y
++# CONFIG_USB_DEVICE_CLASS is not set
++# CONFIG_USB_DYNAMIC_MINORS is not set
++# CONFIG_USB_SUSPEND is not set
++# CONFIG_USB_OTG is not set
++# CONFIG_USB_OTG_WHITELIST is not set
++# CONFIG_USB_OTG_BLACKLIST_HUB is not set
++CONFIG_USB_MON=y
++# CONFIG_USB_WUSB is not set
++# CONFIG_USB_WUSB_CBAF is not set
++
++#
++# USB Host Controller Drivers
++#
++# CONFIG_USB_C67X00_HCD is not set
++# CONFIG_USB_OXU210HP_HCD is not set
++# CONFIG_USB_ISP116X_HCD is not set
++# CONFIG_USB_ISP1760_HCD is not set
++# CONFIG_USB_ISP1362_HCD is not set
++CONFIG_USB_OHCI_HCD=y
++# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set
++# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set
++CONFIG_USB_OHCI_LITTLE_ENDIAN=y
++# CONFIG_USB_SL811_HCD is not set
++# CONFIG_USB_R8A66597_HCD is not set
++# CONFIG_USB_HWA_HCD is not set
++# CONFIG_USB_MUSB_HDRC is not set
++
++#
++# USB Device Class drivers
++#
++# CONFIG_USB_ACM is not set
++# CONFIG_USB_PRINTER is not set
++# CONFIG_USB_WDM is not set
++# CONFIG_USB_TMC is not set
++
++#
++# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may
++#
++
++#
++# also be needed; see USB_STORAGE Help for more info
++#
++CONFIG_USB_STORAGE=y
++# CONFIG_USB_STORAGE_DEBUG is not set
++# CONFIG_USB_STORAGE_DATAFAB is not set
++# CONFIG_USB_STORAGE_FREECOM is not set
++# CONFIG_USB_STORAGE_ISD200 is not set
++# CONFIG_USB_STORAGE_USBAT is not set
++# CONFIG_USB_STORAGE_SDDR09 is not set
++# CONFIG_USB_STORAGE_SDDR55 is not set
++# CONFIG_USB_STORAGE_JUMPSHOT is not set
++# CONFIG_USB_STORAGE_ALAUDA is not set
++# CONFIG_USB_STORAGE_ONETOUCH is not set
++# CONFIG_USB_STORAGE_KARMA is not set
++# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set
++# CONFIG_USB_LIBUSUAL is not set
++
++#
++# USB Imaging devices
++#
++# CONFIG_USB_MDC800 is not set
++# CONFIG_USB_MICROTEK is not set
++
++#
++# USB port drivers
++#
++# CONFIG_USB_SERIAL is not set
++
++#
++# USB Miscellaneous drivers
++#
++# CONFIG_USB_EMI62 is not set
++# CONFIG_USB_EMI26 is not set
++# CONFIG_USB_ADUTUX is not set
++# CONFIG_USB_SEVSEG is not set
++# CONFIG_USB_RIO500 is not set
++# CONFIG_USB_LEGOTOWER is not set
++# CONFIG_USB_LCD is not set
++# CONFIG_USB_BERRY_CHARGE is not set
++# CONFIG_USB_LED is not set
++# CONFIG_USB_CYPRESS_CY7C63 is not set
++# CONFIG_USB_CYTHERM is not set
++# CONFIG_USB_IDMOUSE is not set
++# CONFIG_USB_FTDI_ELAN is not set
++# CONFIG_USB_APPLEDISPLAY is not set
++# CONFIG_USB_LD is not set
++# CONFIG_USB_TRANCEVIBRATOR is not set
++# CONFIG_USB_IOWARRIOR is not set
++# CONFIG_USB_TEST is not set
++# CONFIG_USB_ISIGHTFW is not set
++# CONFIG_USB_VST is not set
++# CONFIG_USB_GADGET is not set
++
++#
++# OTG and related infrastructure
++#
++# CONFIG_USB_GPIO_VBUS is not set
++# CONFIG_NOP_USB_XCEIV is not set
++CONFIG_MMC=y
++# CONFIG_MMC_DEBUG is not set
++# CONFIG_MMC_UNSAFE_RESUME is not set
++
++#
++# MMC/SD/SDIO Card Drivers
++#
++CONFIG_MMC_BLOCK=y
++CONFIG_MMC_BLOCK_BOUNCE=y
++# CONFIG_SDIO_UART is not set
++# CONFIG_MMC_TEST is not set
++
++#
++# MMC/SD/SDIO Host Controller Drivers
++#
++# CONFIG_MMC_SDHCI is not set
++CONFIG_MMC_AT91=y
++# CONFIG_MMC_ATMELMCI is not set
++# CONFIG_MMC_SPI is not set
++# CONFIG_MEMSTICK is not set
++CONFIG_NEW_LEDS=y
++CONFIG_LEDS_CLASS=y
++
++#
++# LED drivers
++#
++CONFIG_LEDS_GPIO=y
++CONFIG_LEDS_GPIO_PLATFORM=y
++# CONFIG_LEDS_DAC124S085 is not set
++
++#
++# LED Triggers
++#
++CONFIG_LEDS_TRIGGERS=y
++CONFIG_LEDS_TRIGGER_TIMER=y
++CONFIG_LEDS_TRIGGER_HEARTBEAT=y
++# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set
++# CONFIG_LEDS_TRIGGER_GPIO is not set
++# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set
++
++#
++# iptables trigger is under Netfilter config (LED target)
++#
++# CONFIG_ACCESSIBILITY is not set
++CONFIG_RTC_LIB=y
++CONFIG_RTC_CLASS=y
++CONFIG_RTC_HCTOSYS=y
++CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
++CONFIG_RTC_DEBUG=y
++
++#
++# RTC interfaces
++#
++CONFIG_RTC_INTF_SYSFS=y
++CONFIG_RTC_INTF_PROC=y
++CONFIG_RTC_INTF_DEV=y
++CONFIG_RTC_INTF_DEV_UIE_EMUL=y
++# CONFIG_RTC_DRV_TEST is not set
++
++#
++# SPI RTC drivers
++#
++# CONFIG_RTC_DRV_M41T94 is not set
++# CONFIG_RTC_DRV_DS1305 is not set
++# CONFIG_RTC_DRV_DS1390 is not set
++# CONFIG_RTC_DRV_MAX6902 is not set
++# CONFIG_RTC_DRV_R9701 is not set
++# CONFIG_RTC_DRV_RS5C348 is not set
++# CONFIG_RTC_DRV_DS3234 is not set
++# CONFIG_RTC_DRV_PCF2123 is not set
++
++#
++# Platform RTC drivers
++#
++CONFIG_RTC_DRV_CMOS=y
++# CONFIG_RTC_DRV_DS1286 is not set
++# CONFIG_RTC_DRV_DS1511 is not set
++# CONFIG_RTC_DRV_DS1553 is not set
++# CONFIG_RTC_DRV_DS1742 is not set
++# CONFIG_RTC_DRV_STK17TA8 is not set
++# CONFIG_RTC_DRV_M48T86 is not set
++# CONFIG_RTC_DRV_M48T35 is not set
++# CONFIG_RTC_DRV_M48T59 is not set
++# CONFIG_RTC_DRV_BQ4802 is not set
++# CONFIG_RTC_DRV_V3020 is not set
++
++#
++# on-CPU RTC drivers
++#
++CONFIG_RTC_DRV_AT91SAM9=y
++CONFIG_RTC_DRV_AT91SAM9_RTT=0
++CONFIG_RTC_DRV_AT91SAM9_GPBR=0
++# CONFIG_DMADEVICES is not set
++# CONFIG_AUXDISPLAY is not set
++# CONFIG_UIO is not set
++
++#
++# TI VLYNQ
++#
++# CONFIG_STAGING is not set
++
++#
++# File systems
++#
++CONFIG_EXT2_FS=y
++# CONFIG_EXT2_FS_XATTR is not set
++# CONFIG_EXT2_FS_XIP is not set
++CONFIG_EXT3_FS=y
++# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
++CONFIG_EXT3_FS_XATTR=y
++# CONFIG_EXT3_FS_POSIX_ACL is not set
++# CONFIG_EXT3_FS_SECURITY is not set
++CONFIG_EXT4_FS=y
++CONFIG_EXT4_FS_XATTR=y
++# CONFIG_EXT4_FS_POSIX_ACL is not set
++# CONFIG_EXT4_FS_SECURITY is not set
++# CONFIG_EXT4_DEBUG is not set
++CONFIG_JBD=y
++CONFIG_JBD2=y
++CONFIG_FS_MBCACHE=y
++CONFIG_REISERFS_FS=y
++# CONFIG_REISERFS_CHECK is not set
++# CONFIG_REISERFS_PROC_INFO is not set
++# CONFIG_REISERFS_FS_XATTR is not set
++# CONFIG_JFS_FS is not set
++# CONFIG_FS_POSIX_ACL is not set
++# CONFIG_XFS_FS is not set
++# CONFIG_GFS2_FS is not set
++# CONFIG_OCFS2_FS is not set
++# CONFIG_BTRFS_FS is not set
++# CONFIG_NILFS2_FS is not set
++CONFIG_FILE_LOCKING=y
++CONFIG_FSNOTIFY=y
++CONFIG_DNOTIFY=y
++CONFIG_INOTIFY=y
++CONFIG_INOTIFY_USER=y
++# CONFIG_QUOTA is not set
++# CONFIG_AUTOFS_FS is not set
++# CONFIG_AUTOFS4_FS is not set
++# CONFIG_FUSE_FS is not set
++
++#
++# Caches
++#
++# CONFIG_FSCACHE is not set
++
++#
++# CD-ROM/DVD Filesystems
++#
++CONFIG_ISO9660_FS=y
++# CONFIG_JOLIET is not set
++# CONFIG_ZISOFS is not set
++CONFIG_UDF_FS=y
++CONFIG_UDF_NLS=y
++
++#
++# DOS/FAT/NT Filesystems
++#
++CONFIG_FAT_FS=y
++CONFIG_MSDOS_FS=y
++CONFIG_VFAT_FS=y
++CONFIG_FAT_DEFAULT_CODEPAGE=437
++CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
++# CONFIG_NTFS_FS is not set
++
++#
++# Pseudo filesystems
++#
++CONFIG_PROC_FS=y
++CONFIG_PROC_SYSCTL=y
++CONFIG_PROC_PAGE_MONITOR=y
++CONFIG_SYSFS=y
++CONFIG_TMPFS=y
++# CONFIG_TMPFS_POSIX_ACL is not set
++# CONFIG_HUGETLB_PAGE is not set
++# CONFIG_CONFIGFS_FS is not set
++CONFIG_MISC_FILESYSTEMS=y
++# CONFIG_ADFS_FS is not set
++# CONFIG_AFFS_FS is not set
++# CONFIG_HFS_FS is not set
++# CONFIG_HFSPLUS_FS is not set
++# CONFIG_BEFS_FS is not set
++# CONFIG_BFS_FS is not set
++# CONFIG_EFS_FS is not set
++CONFIG_JFFS2_FS=y
++CONFIG_JFFS2_FS_DEBUG=0
++CONFIG_JFFS2_FS_WRITEBUFFER=y
++# CONFIG_JFFS2_FS_WBUF_VERIFY is not set
++CONFIG_JFFS2_SUMMARY=y
++# CONFIG_JFFS2_FS_XATTR is not set
++# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
++CONFIG_JFFS2_ZLIB=y
++# CONFIG_JFFS2_LZO is not set
++CONFIG_JFFS2_RTIME=y
++# CONFIG_JFFS2_RUBIN is not set
++CONFIG_CRAMFS=y
++CONFIG_SQUASHFS=y
++# CONFIG_SQUASHFS_EMBEDDED is not set
++CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3
++# CONFIG_VXFS_FS is not set
++# CONFIG_MINIX_FS is not set
++# CONFIG_OMFS_FS is not set
++# CONFIG_HPFS_FS is not set
++# CONFIG_QNX4FS_FS is not set
++CONFIG_ROMFS_FS=y
++CONFIG_ROMFS_BACKED_BY_BLOCK=y
++# CONFIG_ROMFS_BACKED_BY_MTD is not set
++# CONFIG_ROMFS_BACKED_BY_BOTH is not set
++CONFIG_ROMFS_ON_BLOCK=y
++# CONFIG_SYSV_FS is not set
++# CONFIG_UFS_FS is not set
++CONFIG_NETWORK_FILESYSTEMS=y
++CONFIG_NFS_FS=y
++CONFIG_NFS_V3=y
++# CONFIG_NFS_V3_ACL is not set
++# CONFIG_NFS_V4 is not set
++CONFIG_ROOT_NFS=y
++# CONFIG_NFSD is not set
++CONFIG_LOCKD=y
++CONFIG_LOCKD_V4=y
++CONFIG_NFS_COMMON=y
++CONFIG_SUNRPC=y
++# CONFIG_RPCSEC_GSS_KRB5 is not set
++# CONFIG_RPCSEC_GSS_SPKM3 is not set
++# CONFIG_SMB_FS is not set
++# CONFIG_CIFS is not set
++# CONFIG_NCP_FS is not set
++# CONFIG_CODA_FS is not set
++# CONFIG_AFS_FS is not set
++
++#
++# Partition Types
++#
++# CONFIG_PARTITION_ADVANCED is not set
++CONFIG_MSDOS_PARTITION=y
++CONFIG_NLS=y
++CONFIG_NLS_DEFAULT="iso8859-1"
++CONFIG_NLS_CODEPAGE_437=y
++# CONFIG_NLS_CODEPAGE_737 is not set
++# CONFIG_NLS_CODEPAGE_775 is not set
++CONFIG_NLS_CODEPAGE_850=y
++# CONFIG_NLS_CODEPAGE_852 is not set
++# CONFIG_NLS_CODEPAGE_855 is not set
++# CONFIG_NLS_CODEPAGE_857 is not set
++# CONFIG_NLS_CODEPAGE_860 is not set
++# CONFIG_NLS_CODEPAGE_861 is not set
++# CONFIG_NLS_CODEPAGE_862 is not set
++# CONFIG_NLS_CODEPAGE_863 is not set
++# CONFIG_NLS_CODEPAGE_864 is not set
++# CONFIG_NLS_CODEPAGE_865 is not set
++# CONFIG_NLS_CODEPAGE_866 is not set
++# CONFIG_NLS_CODEPAGE_869 is not set
++# CONFIG_NLS_CODEPAGE_936 is not set
++# CONFIG_NLS_CODEPAGE_950 is not set
++# CONFIG_NLS_CODEPAGE_932 is not set
++# CONFIG_NLS_CODEPAGE_949 is not set
++# CONFIG_NLS_CODEPAGE_874 is not set
++# CONFIG_NLS_ISO8859_8 is not set
++# CONFIG_NLS_CODEPAGE_1250 is not set
++# CONFIG_NLS_CODEPAGE_1251 is not set
++# CONFIG_NLS_ASCII is not set
++CONFIG_NLS_ISO8859_1=y
++# CONFIG_NLS_ISO8859_2 is not set
++# CONFIG_NLS_ISO8859_3 is not set
++# CONFIG_NLS_ISO8859_4 is not set
++# CONFIG_NLS_ISO8859_5 is not set
++# CONFIG_NLS_ISO8859_6 is not set
++# CONFIG_NLS_ISO8859_7 is not set
++# CONFIG_NLS_ISO8859_9 is not set
++# CONFIG_NLS_ISO8859_13 is not set
++# CONFIG_NLS_ISO8859_14 is not set
++CONFIG_NLS_ISO8859_15=y
++# CONFIG_NLS_KOI8_R is not set
++# CONFIG_NLS_KOI8_U is not set
++CONFIG_NLS_UTF8=y
++# CONFIG_DLM is not set
++
++#
++# Kernel hacking
++#
++# CONFIG_PRINTK_TIME is not set
++# CONFIG_ENABLE_WARN_DEPRECATED is not set
++CONFIG_ENABLE_MUST_CHECK=y
++CONFIG_FRAME_WARN=1024
++# CONFIG_MAGIC_SYSRQ is not set
++# CONFIG_STRIP_ASM_SYMS is not set
++# CONFIG_UNUSED_SYMBOLS is not set
++# CONFIG_DEBUG_FS is not set
++# CONFIG_HEADERS_CHECK is not set
++# CONFIG_DEBUG_KERNEL is not set
++# CONFIG_DEBUG_BUGVERBOSE is not set
++# CONFIG_DEBUG_MEMORY_INIT is not set
++CONFIG_FRAME_POINTER=y
++# CONFIG_RCU_CPU_STALL_DETECTOR is not set
++# CONFIG_LATENCYTOP is not set
++# CONFIG_SYSCTL_SYSCALL_CHECK is not set
++CONFIG_HAVE_FUNCTION_TRACER=y
++CONFIG_TRACING_SUPPORT=y
++# CONFIG_FTRACE is not set
++# CONFIG_SAMPLES is not set
++CONFIG_HAVE_ARCH_KGDB=y
++# CONFIG_ARM_UNWIND is not set
++# CONFIG_DEBUG_USER is not set
++
++#
++# Security options
++#
++# CONFIG_KEYS is not set
++# CONFIG_SECURITY is not set
++# CONFIG_SECURITYFS is not set
++# CONFIG_SECURITY_FILE_CAPABILITIES is not set
++# CONFIG_CRYPTO is not set
++# CONFIG_BINARY_PRINTF is not set
++
++#
++# Library routines
++#
++CONFIG_BITREVERSE=y
++CONFIG_GENERIC_FIND_LAST_BIT=y
++# CONFIG_CRC_CCITT is not set
++CONFIG_CRC16=y
++# CONFIG_CRC_T10DIF is not set
++CONFIG_CRC_ITU_T=y
++CONFIG_CRC32=y
++# CONFIG_CRC7 is not set
++# CONFIG_LIBCRC32C is not set
++CONFIG_ZLIB_INFLATE=y
++CONFIG_ZLIB_DEFLATE=y
++CONFIG_DECOMPRESS_GZIP=y
++CONFIG_HAS_IOMEM=y
++CONFIG_HAS_IOPORT=y
++CONFIG_HAS_DMA=y
++CONFIG_NLATTR=y
+diff -Nur linux-2.6.32.2.orig/foxg20-script linux-2.6.32.2/foxg20-script
+--- linux-2.6.32.2.orig/foxg20-script 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.32.2/foxg20-script 2010-02-16 21:29:07.000000000 +0100
+@@ -0,0 +1,3 @@
++set bootargs 'bootargs=mem=64M console=ttyS0,115200 noinitrd root=/dev/mmcblk0p2 rw rootwait init=/sbin/init'
++fatload mmc 0 0x20400000 uimage
++bootm 0x20400000
+diff -Nur linux-2.6.32.2.orig/makefile linux-2.6.32.2/makefile
+--- linux-2.6.32.2.orig/makefile 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.32.2/makefile 2010-02-16 23:52:08.000000000 +0100
+@@ -0,0 +1,80 @@
++# makefile for the FOX Board G20/Netus
++# Lee McLoughlin <lee@lmmrtech.com>
++
++# By default make will use this file (makefile) before Makefile
++# I use it to setup variables for building the FOX kernel and modules
++# however if you are doing anything complex with your linux builds
++# then you should consider deleting this file!
++
++ARCH=arm
++CROSS_COMPILE=arm-linux-gnueabi-
++#INSTALL_MOD_PATH=./FoxModules
++
++# arch/arm/boot/uImage
++UIMAGE=./uImage
++
++# In normal use:
++# First time do:
++# make distclean
++# To complete build from then on:
++# make menuconfig
++# make
++# make modules
++# make modules_install
++# To remove old modules do 'rm -rf FoxModules' before make modules_install
++# To install modules copy the entire FoxModules/lib to /lib on your Fox
++
++uimage uImage: foxg20-script.bin
++ @echo Generating a NON compressed uimage
++ $(MAKE) $(MFLAGS) -fmakefile Image
++ /bin/bash scripts/mkuboot.sh \
++ -A arm \
++ -O linux \
++ -T kernel \
++ -C none \
++ -a 0x20008000 \
++ -e 0x20008000 \
++ -n $$(pwd | sed 's,.*/,,;s/^/FOX-/;') \
++ -d arch/arm/boot/Image $(UIMAGE)
++ @echo " "
++ @echo Your new kernel is in: $(UIMAGE)
++ @echo Copy $(UIMAGE) and ./foxg20-script.bin to the kernel partition on your SD card
++ @echo " "
++
++uimageGzip uImageGzip: foxg20-script.bin
++ @echo Generating a compressed uimage
++ $(MAKE) $(MFLAGS) -fmakefile Image
++ gzip -v9 < arch/arm/boot/Image > Image.gz
++ /bin/bash scripts/mkuboot.sh \
++ -A arm \
++ -O linux \
++ -T kernel \
++ -C gzip \
++ -a 0x20008000 \
++ -e 0x20008000 \
++ -n $$(pwd | sed 's,.*/,,;s/^/FOX-/;') \
++ -d Image.gz $(UIMAGE)
++ @echo " "
++ @echo Your new kernel is in: $(UIMAGE)
++ @echo " "
++
++foxg20-script.bin: foxg20-script
++ @echo Generating updated $@
++ mkimage \
++ -T script \
++ -n 'FOXG20 Boot Script' \
++ -d $< $@
++ @echo " "
++ @echo Copy $@ to the kernel partition on your SD card
++ @echo " "
++
++
++# This is a pattern rule to cover all make targets and pass them to the
++# "real" Makefile but with ARCH, CROSS_COMPILE and INSTALL_MOD_PATH set
++%:
++ $(MAKE) $(MFLAGS) \
++ -fMakefile \
++ ARCH=$(ARCH) \
++ CROSS_COMPILE=$(CROSS_COMPILE) \
++ INSTALL_MOD_PATH=$(INSTALL_MOD_PATH) \
++ $@
+diff -Nur linux-2.6.32.2.orig/README.FOX linux-2.6.32.2/README.FOX
+--- linux-2.6.32.2.orig/README.FOX 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.32.2/README.FOX 2010-02-16 21:29:07.000000000 +0100
+@@ -0,0 +1,25 @@
++Build Linux for the FOX Board G20/Netus
++Lee McLoughlin <lee@lmmrtech.com>
++
++The FOX specific makefile overrides Makefile and I use this to hide all
++required cross compilation changes.
++
++Normally all you need to do is run:
++ make menuconfig
++ make
++ make modules
++ make modules_install
++
++By default makefile builds a uimage which is an NON gzipped kernel uimage.
++It also builds a new foxg20-script.bin. Once built both must be copied to
++the kernel partition on your micro SD card.
++
++
++Personally I prefer a gzipped uimage - its not any faster but it
++saves dataflash space.
++
++If you want to switch to gzipped uimages use:
++ make uimageGzip
++instead of just
++ make
++in the above instructions.
diff --git a/target/linux/patches/2.6.32.2/aufs2.patch b/target/linux/patches/2.6.32.2/aufs2.patch
new file mode 100644
index 000000000..6c40cfb81
--- /dev/null
+++ b/target/linux/patches/2.6.32.2/aufs2.patch
@@ -0,0 +1,24455 @@
+diff -Nur linux-2.6.31.5.orig/Documentation/ABI/testing/debugfs-aufs linux-2.6.31.5/Documentation/ABI/testing/debugfs-aufs
+--- linux-2.6.31.5.orig/Documentation/ABI/testing/debugfs-aufs 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/Documentation/ABI/testing/debugfs-aufs 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,40 @@
++What: /debug/aufs/si_<id>/
++Date: March 2009
++Contact: J. R. Okajima <hooanon05@yahoo.co.jp>
++Description:
++ Under /debug/aufs, a directory named si_<id> is created
++ per aufs mount, where <id> is a unique id generated
++ internally.
++
++What: /debug/aufs/si_<id>/xib
++Date: March 2009
++Contact: J. R. Okajima <hooanon05@yahoo.co.jp>
++Description:
++ It shows the consumed blocks by xib (External Inode Number
++ Bitmap), its block size and file size.
++ When the aufs mount option 'noxino' is specified, it
++ will be empty. About XINO files, see
++ Documentation/filesystems/aufs/aufs.5 in detail.
++
++What: /debug/aufs/si_<id>/xino0, xino1 ... xinoN
++Date: March 2009
++Contact: J. R. Okajima <hooanon05@yahoo.co.jp>
++Description:
++ It shows the consumed blocks by xino (External Inode Number
++ Translation Table), its link count, block size and file
++ size.
++ When the aufs mount option 'noxino' is specified, it
++ will be empty. About XINO files, see
++ Documentation/filesystems/aufs/aufs.5 in detail.
++
++What: /debug/aufs/si_<id>/xigen
++Date: March 2009
++Contact: J. R. Okajima <hooanon05@yahoo.co.jp>
++Description:
++ It shows the consumed blocks by xigen (External Inode
++ Generation Table), its block size and file size.
++ If CONFIG_AUFS_EXPORT is disabled, this entry will not
++ be created.
++ When the aufs mount option 'noxino' is specified, it
++ will be empty. About XINO files, see
++ Documentation/filesystems/aufs/aufs.5 in detail.
+diff -Nur linux-2.6.31.5.orig/Documentation/ABI/testing/sysfs-aufs linux-2.6.31.5/Documentation/ABI/testing/sysfs-aufs
+--- linux-2.6.31.5.orig/Documentation/ABI/testing/sysfs-aufs 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/Documentation/ABI/testing/sysfs-aufs 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,25 @@
++What: /sys/fs/aufs/si_<id>/
++Date: March 2009
++Contact: J. R. Okajima <hooanon05@yahoo.co.jp>
++Description:
++ Under /sys/fs/aufs, a directory named si_<id> is created
++ per aufs mount, where <id> is a unique id generated
++ internally.
++
++What: /sys/fs/aufs/si_<id>/br0, br1 ... brN
++Date: March 2009
++Contact: J. R. Okajima <hooanon05@yahoo.co.jp>
++Description:
++ It shows the abolute path of a member directory (which
++ is called branch) in aufs, and its permission.
++
++What: /sys/fs/aufs/si_<id>/xi_path
++Date: March 2009
++Contact: J. R. Okajima <hooanon05@yahoo.co.jp>
++Description:
++ It shows the abolute path of XINO (External Inode Number
++ Bitmap, Translation Table and Generation Table) file
++ even if it is the default path.
++ When the aufs mount option 'noxino' is specified, it
++ will be empty. About XINO files, see
++ Documentation/filesystems/aufs/aufs.5 in detail.
+diff -Nur linux-2.6.31.5.orig/fs/aufs/aufs.h linux-2.6.31.5/fs/aufs/aufs.h
+--- linux-2.6.31.5.orig/fs/aufs/aufs.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/aufs.h 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,51 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. 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
++ */
++
++/*
++ * all header files
++ */
++
++#ifndef __AUFS_H__
++#define __AUFS_H__
++
++#ifdef __KERNEL__
++
++#include "debug.h"
++
++#include "branch.h"
++#include "cpup.h"
++#include "dcsub.h"
++#include "dbgaufs.h"
++#include "dentry.h"
++#include "dir.h"
++#include "file.h"
++#include "fstype.h"
++#include "inode.h"
++#include "loop.h"
++#include "module.h"
++#include "opts.h"
++#include "rwsem.h"
++#include "spl.h"
++#include "super.h"
++#include "sysaufs.h"
++#include "vfsub.h"
++#include "whout.h"
++#include "wkq.h"
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_H__ */
+diff -Nur linux-2.6.31.5.orig/fs/aufs/branch.c linux-2.6.31.5/fs/aufs/branch.c
+--- linux-2.6.31.5.orig/fs/aufs/branch.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/branch.c 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,974 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. Okajima
++ *
++ * This program, aufs is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/*
++ * branch management
++ */
++
++#include <linux/file.h>
++#include "aufs.h"
++
++/*
++ * free a single branch
++ */
++static void au_br_do_free(struct au_branch *br)
++{
++ int i;
++ struct au_wbr *wbr;
++
++ if (br->br_xino.xi_file)
++ fput(br->br_xino.xi_file);
++ mutex_destroy(&br->br_xino.xi_nondir_mtx);
++
++ AuDebugOn(atomic_read(&br->br_count));
++
++ wbr = br->br_wbr;
++ if (wbr) {
++ for (i = 0; i < AuBrWh_Last; i++)
++ dput(wbr->wbr_wh[i]);
++ AuDebugOn(atomic_read(&wbr->wbr_wh_running));
++ AuRwDestroy(&wbr->wbr_wh_rwsem);
++ }
++
++ /* some filesystems acquire extra lock */
++ lockdep_off();
++ mntput(br->br_mnt);
++ lockdep_on();
++
++ kfree(wbr);
++ kfree(br);
++}
++
++/*
++ * frees all branches
++ */
++void au_br_free(struct au_sbinfo *sbinfo)
++{
++ aufs_bindex_t bmax;
++ struct au_branch **br;
++
++ AuRwMustWriteLock(&sbinfo->si_rwsem);
++
++ bmax = sbinfo->si_bend + 1;
++ br = sbinfo->si_branch;
++ while (bmax--)
++ au_br_do_free(*br++);
++}
++
++/*
++ * find the index of a branch which is specified by @br_id.
++ */
++int au_br_index(struct super_block *sb, aufs_bindex_t br_id)
++{
++ aufs_bindex_t bindex, bend;
++
++ bend = au_sbend(sb);
++ for (bindex = 0; bindex <= bend; bindex++)
++ if (au_sbr_id(sb, bindex) == br_id)
++ return bindex;
++ return -1;
++}
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * add a branch
++ */
++
++static int test_overlap(struct super_block *sb, struct dentry *h_d1,
++ struct dentry *h_d2)
++{
++ if (unlikely(h_d1 == h_d2))
++ return 1;
++ return !!au_test_subdir(h_d1, h_d2)
++ || !!au_test_subdir(h_d2, h_d1)
++ || au_test_loopback_overlap(sb, h_d1, h_d2)
++ || au_test_loopback_overlap(sb, h_d2, h_d1);
++}
++
++/*
++ * returns a newly allocated branch. @new_nbranch is a number of branches
++ * after adding a branch.
++ */
++static struct au_branch *au_br_alloc(struct super_block *sb, int new_nbranch,
++ int perm)
++{
++ struct au_branch *add_branch;
++ struct dentry *root;
++
++ root = sb->s_root;
++ add_branch = kmalloc(sizeof(*add_branch), GFP_NOFS);
++ if (unlikely(!add_branch))
++ goto out;
++
++ add_branch->br_wbr = NULL;
++ if (au_br_writable(perm)) {
++ /* may be freed separately at changing the branch permission */
++ add_branch->br_wbr = kmalloc(sizeof(*add_branch->br_wbr),
++ GFP_NOFS);
++ if (unlikely(!add_branch->br_wbr))
++ goto out_br;
++ }
++
++ if (unlikely(au_sbr_realloc(au_sbi(sb), new_nbranch)
++ || au_di_realloc(au_di(root), new_nbranch)
++ || au_ii_realloc(au_ii(root->d_inode), new_nbranch)))
++ goto out_wbr;
++ return add_branch; /* success */
++
++ out_wbr:
++ kfree(add_branch->br_wbr);
++ out_br:
++ kfree(add_branch);
++ out:
++ return ERR_PTR(-ENOMEM);
++}
++
++/*
++ * test if the branch permission is legal or not.
++ */
++static int test_br(struct inode *inode, int brperm, char *path)
++{
++ int err;
++
++ err = 0;
++ if (unlikely(au_br_writable(brperm) && IS_RDONLY(inode))) {
++ AuErr("write permission for readonly mount or inode, %s\n",
++ path);
++ err = -EINVAL;
++ }
++
++ return err;
++}
++
++/*
++ * returns:
++ * 0: success, the caller will add it
++ * plus: success, it is already unified, the caller should ignore it
++ * minus: error
++ */
++static int test_add(struct super_block *sb, struct au_opt_add *add, int remount)
++{
++ int err;
++ aufs_bindex_t bend, bindex;
++ struct dentry *root;
++ struct inode *inode, *h_inode;
++
++ root = sb->s_root;
++ bend = au_sbend(sb);
++ if (unlikely(bend >= 0
++ && au_find_dbindex(root, add->path.dentry) >= 0)) {
++ err = 1;
++ if (!remount) {
++ err = -EINVAL;
++ AuErr("%s duplicated\n", add->pathname);
++ }
++ goto out;
++ }
++
++ err = -ENOSPC; /* -E2BIG; */
++ if (unlikely(AUFS_BRANCH_MAX <= add->bindex
++ || AUFS_BRANCH_MAX - 1 <= bend)) {
++ AuErr("number of branches exceeded %s\n", add->pathname);
++ goto out;
++ }
++
++ err = -EDOM;
++ if (unlikely(add->bindex < 0 || bend + 1 < add->bindex)) {
++ AuErr("bad index %d\n", add->bindex);
++ goto out;
++ }
++
++ inode = add->path.dentry->d_inode;
++ err = -ENOENT;
++ if (unlikely(!inode->i_nlink)) {
++ AuErr("no existence %s\n", add->pathname);
++ goto out;
++ }
++
++ err = -EINVAL;
++ if (unlikely(inode->i_sb == sb)) {
++ AuErr("%s must be outside\n", add->pathname);
++ goto out;
++ }
++
++ if (unlikely(au_test_fs_unsuppoted(inode->i_sb))) {
++ AuErr("unsupported filesystem, %s (%s)\n",
++ add->pathname, au_sbtype(inode->i_sb));
++ goto out;
++ }
++
++ err = test_br(add->path.dentry->d_inode, add->perm, add->pathname);
++ if (unlikely(err))
++ goto out;
++
++ if (bend < 0)
++ return 0; /* success */
++
++ err = -EINVAL;
++ for (bindex = 0; bindex <= bend; bindex++)
++ if (unlikely(test_overlap(sb, add->path.dentry,
++ au_h_dptr(root, bindex)))) {
++ AuErr("%s is overlapped\n", add->pathname);
++ goto out;
++ }
++
++ err = 0;
++ if (au_opt_test(au_mntflags(sb), WARN_PERM)) {
++ h_inode = au_h_dptr(root, 0)->d_inode;
++ if ((h_inode->i_mode & S_IALLUGO) != (inode->i_mode & S_IALLUGO)
++ || h_inode->i_uid != inode->i_uid
++ || h_inode->i_gid != inode->i_gid)
++ AuWarn("uid/gid/perm %s %u/%u/0%o, %u/%u/0%o\n",
++ add->pathname,
++ inode->i_uid, inode->i_gid,
++ (inode->i_mode & S_IALLUGO),
++ h_inode->i_uid, h_inode->i_gid,
++ (h_inode->i_mode & S_IALLUGO));
++ }
++
++ out:
++ return err;
++}
++
++/*
++ * initialize or clean the whiteouts for an adding branch
++ */
++static int au_br_init_wh(struct super_block *sb, struct au_branch *br,
++ int new_perm, struct dentry *h_root)
++{
++ int err, old_perm;
++ aufs_bindex_t bindex;
++ struct mutex *h_mtx;
++ struct au_wbr *wbr;
++ struct au_hinode *hdir;
++
++ wbr = br->br_wbr;
++ old_perm = br->br_perm;
++ br->br_perm = new_perm;
++ hdir = NULL;
++ h_mtx = NULL;
++ bindex = au_br_index(sb, br->br_id);
++ if (0 <= bindex) {
++ hdir = au_hi(sb->s_root->d_inode, bindex);
++ au_hin_imtx_lock_nested(hdir, AuLsc_I_PARENT);
++ } else {
++ h_mtx = &h_root->d_inode->i_mutex;
++ mutex_lock_nested(h_mtx, AuLsc_I_PARENT);
++ }
++ if (!wbr)
++ err = au_wh_init(h_root, br, sb);
++ else {
++ wbr_wh_write_lock(wbr);
++ err = au_wh_init(h_root, br, sb);
++ wbr_wh_write_unlock(wbr);
++ }
++ if (hdir)
++ au_hin_imtx_unlock(hdir);
++ else
++ mutex_unlock(h_mtx);
++ br->br_perm = old_perm;
++
++ if (!err && wbr && !au_br_writable(new_perm)) {
++ kfree(wbr);
++ br->br_wbr = NULL;
++ }
++
++ return err;
++}
++
++static int au_wbr_init(struct au_branch *br, struct super_block *sb,
++ int perm, struct path *path)
++{
++ int err;
++ struct au_wbr *wbr;
++
++ wbr = br->br_wbr;
++ au_rw_init(&wbr->wbr_wh_rwsem);
++ memset(wbr->wbr_wh, 0, sizeof(wbr->wbr_wh));
++ atomic_set(&wbr->wbr_wh_running, 0);
++ wbr->wbr_bytes = 0;
++
++ err = au_br_init_wh(sb, br, perm, path->dentry);
++
++ return err;
++}
++
++/* intialize a new branch */
++static int au_br_init(struct au_branch *br, struct super_block *sb,
++ struct au_opt_add *add)
++{
++ int err;
++
++ err = 0;
++ memset(&br->br_xino, 0, sizeof(br->br_xino));
++ mutex_init(&br->br_xino.xi_nondir_mtx);
++ br->br_perm = add->perm;
++ br->br_mnt = add->path.mnt; /* set first, mntget() later */
++ atomic_set(&br->br_count, 0);
++ br->br_xino_upper = AUFS_XINO_TRUNC_INIT;
++ atomic_set(&br->br_xino_running, 0);
++ br->br_id = au_new_br_id(sb);
++
++ if (au_br_writable(add->perm)) {
++ err = au_wbr_init(br, sb, add->perm, &add->path);
++ if (unlikely(err))
++ goto out;
++ }
++
++ if (au_opt_test(au_mntflags(sb), XINO)) {
++ err = au_xino_br(sb, br, add->path.dentry->d_inode->i_ino,
++ au_sbr(sb, 0)->br_xino.xi_file, /*do_test*/1);
++ if (unlikely(err)) {
++ AuDebugOn(br->br_xino.xi_file);
++ goto out;
++ }
++ }
++
++ sysaufs_br_init(br);
++ mntget(add->path.mnt);
++
++ out:
++ return err;
++}
++
++static void au_br_do_add_brp(struct au_sbinfo *sbinfo, aufs_bindex_t bindex,
++ struct au_branch *br, aufs_bindex_t bend,
++ aufs_bindex_t amount)
++{
++ struct au_branch **brp;
++
++ AuRwMustWriteLock(&sbinfo->si_rwsem);
++
++ brp = sbinfo->si_branch + bindex;
++ memmove(brp + 1, brp, sizeof(*brp) * amount);
++ *brp = br;
++ sbinfo->si_bend++;
++ if (unlikely(bend < 0))
++ sbinfo->si_bend = 0;
++}
++
++static void au_br_do_add_hdp(struct au_dinfo *dinfo, aufs_bindex_t bindex,
++ aufs_bindex_t bend, aufs_bindex_t amount)
++{
++ struct au_hdentry *hdp;
++
++ AuRwMustWriteLock(&dinfo->di_rwsem);
++
++ hdp = dinfo->di_hdentry + bindex;
++ memmove(hdp + 1, hdp, sizeof(*hdp) * amount);
++ au_h_dentry_init(hdp);
++ dinfo->di_bend++;
++ if (unlikely(bend < 0))
++ dinfo->di_bstart = 0;
++}
++
++static void au_br_do_add_hip(struct au_iinfo *iinfo, aufs_bindex_t bindex,
++ aufs_bindex_t bend, aufs_bindex_t amount)
++{
++ struct au_hinode *hip;
++
++ AuRwMustWriteLock(&iinfo->ii_rwsem);
++
++ hip = iinfo->ii_hinode + bindex;
++ memmove(hip + 1, hip, sizeof(*hip) * amount);
++ hip->hi_inode = NULL;
++ au_hin_init(hip, NULL);
++ iinfo->ii_bend++;
++ if (unlikely(bend < 0))
++ iinfo->ii_bstart = 0;
++}
++
++static void au_br_do_add(struct super_block *sb, struct dentry *h_dentry,
++ struct au_branch *br, aufs_bindex_t bindex)
++{
++ struct dentry *root;
++ struct inode *root_inode;
++ aufs_bindex_t bend, amount;
++
++ root = sb->s_root;
++ root_inode = root->d_inode;
++ au_plink_block_maintain(sb);
++ bend = au_sbend(sb);
++ amount = bend + 1 - bindex;
++ au_br_do_add_brp(au_sbi(sb), bindex, br, bend, amount);
++ au_br_do_add_hdp(au_di(root), bindex, bend, amount);
++ au_br_do_add_hip(au_ii(root_inode), bindex, bend, amount);
++ au_set_h_dptr(root, bindex, dget(h_dentry));
++ au_set_h_iptr(root_inode, bindex, au_igrab(h_dentry->d_inode),
++ /*flags*/0);
++}
++
++int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount)
++{
++ int err;
++ unsigned long long maxb;
++ aufs_bindex_t bend, add_bindex;
++ struct dentry *root, *h_dentry;
++ struct inode *root_inode;
++ struct au_branch *add_branch;
++
++ root = sb->s_root;
++ root_inode = root->d_inode;
++ IMustLock(root_inode);
++ err = test_add(sb, add, remount);
++ if (unlikely(err < 0))
++ goto out;
++ if (err) {
++ err = 0;
++ goto out; /* success */
++ }
++
++ bend = au_sbend(sb);
++ add_branch = au_br_alloc(sb, bend + 2, add->perm);
++ err = PTR_ERR(add_branch);
++ if (IS_ERR(add_branch))
++ goto out;
++
++ err = au_br_init(add_branch, sb, add);
++ if (unlikely(err)) {
++ au_br_do_free(add_branch);
++ goto out;
++ }
++
++ add_bindex = add->bindex;
++ h_dentry = add->path.dentry;
++ if (!remount)
++ au_br_do_add(sb, h_dentry, add_branch, add_bindex);
++ else {
++ sysaufs_brs_del(sb, add_bindex);
++ au_br_do_add(sb, h_dentry, add_branch, add_bindex);
++ sysaufs_brs_add(sb, add_bindex);
++ }
++
++ if (!add_bindex)
++ au_cpup_attr_all(root_inode, /*force*/1);
++ else
++ au_add_nlink(root_inode, h_dentry->d_inode);
++ maxb = h_dentry->d_sb->s_maxbytes;
++ if (sb->s_maxbytes < maxb)
++ sb->s_maxbytes = maxb;
++
++ /*
++ * this test/set prevents aufs from handling unnecesary inotify events
++ * of xino files, in a case of re-adding a writable branch which was
++ * once detached from aufs.
++ */
++ if (au_xino_brid(sb) < 0
++ && au_br_writable(add_branch->br_perm)
++ && !au_test_fs_bad_xino(h_dentry->d_sb)
++ && add_branch->br_xino.xi_file
++ && add_branch->br_xino.xi_file->f_dentry->d_parent == h_dentry)
++ au_xino_brid_set(sb, add_branch->br_id);
++
++ out:
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * delete a branch
++ */
++
++/* to show the line number, do not make it inlined function */
++#define AuVerbose(do_info, fmt, args...) do { \
++ if (do_info) \
++ AuInfo(fmt, ##args); \
++} while (0)
++
++/*
++ * test if the branch is deletable or not.
++ */
++static int test_dentry_busy(struct dentry *root, aufs_bindex_t bindex,
++ unsigned int sigen)
++{
++ int err, i, j, ndentry;
++ aufs_bindex_t bstart, bend;
++ unsigned char verbose;
++ struct au_dcsub_pages dpages;
++ struct au_dpage *dpage;
++ struct dentry *d;
++ struct inode *inode;
++
++ err = au_dpages_init(&dpages, GFP_NOFS);
++ if (unlikely(err))
++ goto out;
++ err = au_dcsub_pages(&dpages, root, NULL, NULL);
++ if (unlikely(err))
++ goto out_dpages;
++
++ verbose = !!au_opt_test(au_mntflags(root->d_sb), VERBOSE);
++ for (i = 0; !err && i < dpages.ndpage; i++) {
++ dpage = dpages.dpages + i;
++ ndentry = dpage->ndentry;
++ for (j = 0; !err && j < ndentry; j++) {
++ d = dpage->dentries[j];
++ AuDebugOn(!atomic_read(&d->d_count));
++ inode = d->d_inode;
++ if (au_digen(d) == sigen && au_iigen(inode) == sigen)
++ di_read_lock_child(d, AuLock_IR);
++ else {
++ di_write_lock_child(d);
++ err = au_reval_dpath(d, sigen);
++ if (!err)
++ di_downgrade_lock(d, AuLock_IR);
++ else {
++ di_write_unlock(d);
++ break;
++ }
++ }
++
++ bstart = au_dbstart(d);
++ bend = au_dbend(d);
++ if (bstart <= bindex
++ && bindex <= bend
++ && au_h_dptr(d, bindex)
++ && (!S_ISDIR(inode->i_mode) || bstart == bend)) {
++ err = -EBUSY;
++ AuVerbose(verbose, "busy %.*s\n", AuDLNPair(d));
++ }
++ di_read_unlock(d, AuLock_IR);
++ }
++ }
++
++ out_dpages:
++ au_dpages_free(&dpages);
++ out:
++ return err;
++}
++
++static int test_inode_busy(struct super_block *sb, aufs_bindex_t bindex,
++ unsigned int sigen)
++{
++ int err;
++ struct inode *i;
++ aufs_bindex_t bstart, bend;
++ unsigned char verbose;
++
++ err = 0;
++ verbose = !!au_opt_test(au_mntflags(sb), VERBOSE);
++ list_for_each_entry(i, &sb->s_inodes, i_sb_list) {
++ AuDebugOn(!atomic_read(&i->i_count));
++ if (!list_empty(&i->i_dentry))
++ continue;
++
++ if (au_iigen(i) == sigen)
++ ii_read_lock_child(i);
++ else {
++ ii_write_lock_child(i);
++ err = au_refresh_hinode_self(i, /*do_attr*/1);
++ if (!err)
++ ii_downgrade_lock(i);
++ else {
++ ii_write_unlock(i);
++ break;
++ }
++ }
++
++ bstart = au_ibstart(i);
++ bend = au_ibend(i);
++ if (bstart <= bindex
++ && bindex <= bend
++ && au_h_iptr(i, bindex)
++ && (!S_ISDIR(i->i_mode) || bstart == bend)) {
++ err = -EBUSY;
++ AuVerbose(verbose, "busy i%lu\n", i->i_ino);
++ ii_read_unlock(i);
++ break;
++ }
++ ii_read_unlock(i);
++ }
++
++ return err;
++}
++
++static int test_children_busy(struct dentry *root, aufs_bindex_t bindex)
++{
++ int err;
++ unsigned int sigen;
++
++ sigen = au_sigen(root->d_sb);
++ DiMustNoWaiters(root);
++ IiMustNoWaiters(root->d_inode);
++ di_write_unlock(root);
++ err = test_dentry_busy(root, bindex, sigen);
++ if (!err)
++ err = test_inode_busy(root->d_sb, bindex, sigen);
++ di_write_lock_child(root); /* aufs_write_lock() calls ..._child() */
++
++ return err;
++}
++
++static void au_br_do_del_brp(struct au_sbinfo *sbinfo,
++ const aufs_bindex_t bindex,
++ const aufs_bindex_t bend)
++{
++ struct au_branch **brp, **p;
++
++ AuRwMustWriteLock(&sbinfo->si_rwsem);
++
++ brp = sbinfo->si_branch + bindex;
++ if (bindex < bend)
++ memmove(brp, brp + 1, sizeof(*brp) * (bend - bindex));
++ sbinfo->si_branch[0 + bend] = NULL;
++ sbinfo->si_bend--;
++
++ p = krealloc(sbinfo->si_branch, sizeof(*p) * bend, GFP_NOFS);
++ if (p)
++ sbinfo->si_branch = p;
++}
++
++static void au_br_do_del_hdp(struct au_dinfo *dinfo, const aufs_bindex_t bindex,
++ const aufs_bindex_t bend)
++{
++ struct au_hdentry *hdp, *p;
++
++ AuRwMustWriteLock(&dinfo->di_rwsem);
++
++ hdp = dinfo->di_hdentry + bindex;
++ if (bindex < bend)
++ memmove(hdp, hdp + 1, sizeof(*hdp) * (bend - bindex));
++ dinfo->di_hdentry[0 + bend].hd_dentry = NULL;
++ dinfo->di_bend--;
++
++ p = krealloc(dinfo->di_hdentry, sizeof(*p) * bend, GFP_NOFS);
++ if (p)
++ dinfo->di_hdentry = p;
++}
++
++static void au_br_do_del_hip(struct au_iinfo *iinfo, const aufs_bindex_t bindex,
++ const aufs_bindex_t bend)
++{
++ struct au_hinode *hip, *p;
++
++ AuRwMustWriteLock(&iinfo->ii_rwsem);
++
++ hip = iinfo->ii_hinode + bindex;
++ if (bindex < bend)
++ memmove(hip, hip + 1, sizeof(*hip) * (bend - bindex));
++ iinfo->ii_hinode[0 + bend].hi_inode = NULL;
++ au_hin_init(iinfo->ii_hinode + bend, NULL);
++ iinfo->ii_bend--;
++
++ p = krealloc(iinfo->ii_hinode, sizeof(*p) * bend, GFP_NOFS);
++ if (p)
++ iinfo->ii_hinode = p;
++}
++
++static void au_br_do_del(struct super_block *sb, aufs_bindex_t bindex,
++ struct au_branch *br)
++{
++ aufs_bindex_t bend;
++ struct au_sbinfo *sbinfo;
++ struct dentry *root;
++ struct inode *inode;
++
++ SiMustWriteLock(sb);
++
++ root = sb->s_root;
++ inode = root->d_inode;
++ au_plink_block_maintain(sb);
++ sbinfo = au_sbi(sb);
++ bend = sbinfo->si_bend;
++
++ dput(au_h_dptr(root, bindex));
++ au_hiput(au_hi(inode, bindex));
++ au_br_do_free(br);
++
++ au_br_do_del_brp(sbinfo, bindex, bend);
++ au_br_do_del_hdp(au_di(root), bindex, bend);
++ au_br_do_del_hip(au_ii(inode), bindex, bend);
++}
++
++int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount)
++{
++ int err, rerr, i;
++ unsigned int mnt_flags;
++ aufs_bindex_t bindex, bend, br_id;
++ unsigned char do_wh, verbose;
++ struct au_branch *br;
++ struct au_wbr *wbr;
++
++ err = 0;
++ bindex = au_find_dbindex(sb->s_root, del->h_path.dentry);
++ if (bindex < 0) {
++ if (remount)
++ goto out; /* success */
++ err = -ENOENT;
++ AuErr("%s no such branch\n", del->pathname);
++ goto out;
++ }
++ AuDbg("bindex b%d\n", bindex);
++
++ err = -EBUSY;
++ mnt_flags = au_mntflags(sb);
++ verbose = !!au_opt_test(mnt_flags, VERBOSE);
++ bend = au_sbend(sb);
++ if (unlikely(!bend)) {
++ AuVerbose(verbose, "no more branches left\n");
++ goto out;
++ }
++ br = au_sbr(sb, bindex);
++ i = atomic_read(&br->br_count);
++ if (unlikely(i)) {
++ AuVerbose(verbose, "%d file(s) opened\n", i);
++ goto out;
++ }
++
++ wbr = br->br_wbr;
++ do_wh = wbr && (wbr->wbr_whbase || wbr->wbr_plink || wbr->wbr_orph);
++ if (do_wh) {
++ /* instead of WbrWhMustWriteLock(wbr) */
++ SiMustWriteLock(sb);
++ for (i = 0; i < AuBrWh_Last; i++) {
++ dput(wbr->wbr_wh[i]);
++ wbr->wbr_wh[i] = NULL;
++ }
++ }
++
++ err = test_children_busy(sb->s_root, bindex);
++ if (unlikely(err)) {
++ if (do_wh)
++ goto out_wh;
++ goto out;
++ }
++
++ err = 0;
++ br_id = br->br_id;
++ if (!remount)
++ au_br_do_del(sb, bindex, br);
++ else {
++ sysaufs_brs_del(sb, bindex);
++ au_br_do_del(sb, bindex, br);
++ sysaufs_brs_add(sb, bindex);
++ }
++
++ if (!bindex)
++ au_cpup_attr_all(sb->s_root->d_inode, /*force*/1);
++ else
++ au_sub_nlink(sb->s_root->d_inode, del->h_path.dentry->d_inode);
++ if (au_opt_test(mnt_flags, PLINK))
++ au_plink_half_refresh(sb, br_id);
++
++ if (sb->s_maxbytes == del->h_path.dentry->d_sb->s_maxbytes) {
++ bend--;
++ sb->s_maxbytes = 0;
++ for (bindex = 0; bindex <= bend; bindex++) {
++ unsigned long long maxb;
++
++ maxb = au_sbr_sb(sb, bindex)->s_maxbytes;
++ if (sb->s_maxbytes < maxb)
++ sb->s_maxbytes = maxb;
++ }
++ }
++
++ if (au_xino_brid(sb) == br->br_id)
++ au_xino_brid_set(sb, -1);
++ goto out; /* success */
++
++ out_wh:
++ /* revert */
++ rerr = au_br_init_wh(sb, br, br->br_perm, del->h_path.dentry);
++ if (rerr)
++ AuWarn("failed re-creating base whiteout, %s. (%d)\n",
++ del->pathname, rerr);
++ out:
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * change a branch permission
++ */
++
++static int do_need_sigen_inc(int a, int b)
++{
++ return au_br_whable(a) && !au_br_whable(b);
++}
++
++static int need_sigen_inc(int old, int new)
++{
++ return do_need_sigen_inc(old, new)
++ || do_need_sigen_inc(new, old);
++}
++
++static int au_br_mod_files_ro(struct super_block *sb, aufs_bindex_t bindex)
++{
++ int err;
++ unsigned long n, ul, bytes, files;
++ aufs_bindex_t bstart;
++ struct file *file, *hf, **a;
++ const int step_bytes = 1024, /* memory allocation unit */
++ step_files = step_bytes / sizeof(*a);
++
++ err = -ENOMEM;
++ n = 0;
++ bytes = step_bytes;
++ files = step_files;
++ a = kmalloc(bytes, GFP_NOFS);
++ if (unlikely(!a))
++ goto out;
++
++ /* no need file_list_lock() since sbinfo is locked? defered? */
++ list_for_each_entry(file, &sb->s_files, f_u.fu_list) {
++ if (special_file(file->f_dentry->d_inode->i_mode))
++ continue;
++
++ AuDbg("%.*s\n", AuDLNPair(file->f_dentry));
++ fi_read_lock(file);
++ if (unlikely(au_test_mmapped(file))) {
++ err = -EBUSY;
++ FiMustNoWaiters(file);
++ fi_read_unlock(file);
++ goto out_free;
++ }
++
++ bstart = au_fbstart(file);
++ if (!S_ISREG(file->f_dentry->d_inode->i_mode)
++ || !(file->f_mode & FMODE_WRITE)
++ || bstart != bindex) {
++ FiMustNoWaiters(file);
++ fi_read_unlock(file);
++ continue;
++ }
++
++ hf = au_h_fptr(file, bstart);
++ FiMustNoWaiters(file);
++ fi_read_unlock(file);
++
++ if (n < files)
++ a[n++] = hf;
++ else {
++ void *p;
++
++ err = -ENOMEM;
++ bytes += step_bytes;
++ files += step_files;
++ p = krealloc(a, bytes, GFP_NOFS);
++ if (p) {
++ a = p;
++ a[n++] = hf;
++ } else
++ goto out_free;
++ }
++ }
++
++ err = 0;
++ for (ul = 0; ul < n; ul++) {
++ /* todo: already flushed? */
++ /* cf. fs/super.c:mark_files_ro() */
++ hf = a[ul];
++ hf->f_mode &= ~FMODE_WRITE;
++ if (!file_check_writeable(hf)) {
++ file_release_write(hf);
++ mnt_drop_write(hf->f_vfsmnt);
++ }
++ }
++
++ out_free:
++ kfree(a);
++ out:
++ return err;
++}
++
++int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount,
++ int *do_update)
++{
++ int err, rerr;
++ aufs_bindex_t bindex;
++ struct dentry *root;
++ struct au_branch *br;
++
++ root = sb->s_root;
++ au_plink_block_maintain(sb);
++ bindex = au_find_dbindex(root, mod->h_root);
++ if (bindex < 0) {
++ if (remount)
++ return 0; /* success */
++ err = -ENOENT;
++ AuErr("%s no such branch\n", mod->path);
++ goto out;
++ }
++ AuDbg("bindex b%d\n", bindex);
++
++ err = test_br(mod->h_root->d_inode, mod->perm, mod->path);
++ if (unlikely(err))
++ goto out;
++
++ br = au_sbr(sb, bindex);
++ if (br->br_perm == mod->perm)
++ return 0; /* success */
++
++ if (au_br_writable(br->br_perm)) {
++ /* remove whiteout base */
++ err = au_br_init_wh(sb, br, mod->perm, mod->h_root);
++ if (unlikely(err))
++ goto out;
++
++ if (!au_br_writable(mod->perm)) {
++ /* rw --> ro, file might be mmapped */
++ DiMustNoWaiters(root);
++ IiMustNoWaiters(root->d_inode);
++ di_write_unlock(root);
++ err = au_br_mod_files_ro(sb, bindex);
++ /* aufs_write_lock() calls ..._child() */
++ di_write_lock_child(root);
++
++ if (unlikely(err)) {
++ rerr = -ENOMEM;
++ br->br_wbr = kmalloc(sizeof(*br->br_wbr),
++ GFP_NOFS);
++ if (br->br_wbr)
++ rerr = au_br_init_wh
++ (sb, br, br->br_perm,
++ mod->h_root);
++ if (unlikely(rerr)) {
++ AuIOErr("nested error %d (%d)\n",
++ rerr, err);
++ br->br_perm = mod->perm;
++ }
++ }
++ }
++ } else if (au_br_writable(mod->perm)) {
++ /* ro --> rw */
++ err = -ENOMEM;
++ br->br_wbr = kmalloc(sizeof(*br->br_wbr), GFP_NOFS);
++ if (br->br_wbr) {
++ struct path path = {
++ .mnt = br->br_mnt,
++ .dentry = mod->h_root
++ };
++
++ err = au_wbr_init(br, sb, mod->perm, &path);
++ if (unlikely(err)) {
++ kfree(br->br_wbr);
++ br->br_wbr = NULL;
++ }
++ }
++ }
++
++ if (!err) {
++ *do_update |= need_sigen_inc(br->br_perm, mod->perm);
++ br->br_perm = mod->perm;
++ }
++
++ out:
++ return err;
++}
+diff -Nur linux-2.6.31.5.orig/fs/aufs/branch.h linux-2.6.31.5/fs/aufs/branch.h
+--- linux-2.6.31.5.orig/fs/aufs/branch.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/branch.h 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,219 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. Okajima
++ *
++ * This program, aufs is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/*
++ * branch filesystems and xino for them
++ */
++
++#ifndef __AUFS_BRANCH_H__
++#define __AUFS_BRANCH_H__
++
++#ifdef __KERNEL__
++
++#include <linux/fs.h>
++#include <linux/mount.h>
++#include <linux/aufs_type.h>
++#include "rwsem.h"
++#include "super.h"
++
++/* ---------------------------------------------------------------------- */
++
++/* a xino file */
++struct au_xino_file {
++ struct file *xi_file;
++ struct mutex xi_nondir_mtx;
++
++ /* todo: make xino files an array to support huge inode number */
++
++#ifdef CONFIG_DEBUG_FS
++ struct dentry *xi_dbgaufs;
++#endif
++};
++
++/* members for writable branch only */
++enum {AuBrWh_BASE, AuBrWh_PLINK, AuBrWh_ORPH, AuBrWh_Last};
++struct au_wbr {
++ struct au_rwsem wbr_wh_rwsem;
++ struct dentry *wbr_wh[AuBrWh_Last];
++ atomic_t wbr_wh_running;
++#define wbr_whbase wbr_wh[AuBrWh_BASE] /* whiteout base */
++#define wbr_plink wbr_wh[AuBrWh_PLINK] /* pseudo-link dir */
++#define wbr_orph wbr_wh[AuBrWh_ORPH] /* dir for orphans */
++
++ /* mfs mode */
++ unsigned long long wbr_bytes;
++};
++
++/* protected by superblock rwsem */
++struct au_branch {
++ struct au_xino_file br_xino;
++
++ aufs_bindex_t br_id;
++
++ int br_perm;
++ struct vfsmount *br_mnt;
++ atomic_t br_count;
++
++ struct au_wbr *br_wbr;
++
++ /* xino truncation */
++ blkcnt_t br_xino_upper; /* watermark in blocks */
++ atomic_t br_xino_running;
++
++#ifdef CONFIG_SYSFS
++ /* an entry under sysfs per mount-point */
++ char br_name[8];
++ struct attribute br_attr;
++#endif
++};
++
++/* ---------------------------------------------------------------------- */
++
++/* branch permission and attribute */
++enum {
++ AuBrPerm_RW, /* writable, linkable wh */
++ AuBrPerm_RO, /* readonly, no wh */
++ AuBrPerm_RR, /* natively readonly, no wh */
++
++ AuBrPerm_RWNoLinkWH, /* un-linkable whiteouts */
++
++ AuBrPerm_ROWH, /* whiteout-able */
++ AuBrPerm_RRWH, /* whiteout-able */
++
++ AuBrPerm_Last
++};
++
++static inline int au_br_writable(int brperm)
++{
++ return brperm == AuBrPerm_RW || brperm == AuBrPerm_RWNoLinkWH;
++}
++
++static inline int au_br_whable(int brperm)
++{
++ return brperm == AuBrPerm_RW
++ || brperm == AuBrPerm_ROWH
++ || brperm == AuBrPerm_RRWH;
++}
++
++static inline int au_br_rdonly(struct au_branch *br)
++{
++ return ((br->br_mnt->mnt_sb->s_flags & MS_RDONLY)
++ || !au_br_writable(br->br_perm))
++ ? -EROFS : 0;
++}
++
++static inline int au_br_hinotifyable(int brperm __maybe_unused)
++{
++#ifdef CONFIG_AUFS_HINOTIFY
++ return brperm != AuBrPerm_RR && brperm != AuBrPerm_RRWH;
++#else
++ return 0;
++#endif
++}
++
++/* ---------------------------------------------------------------------- */
++
++/* branch.c */
++struct au_sbinfo;
++void au_br_free(struct au_sbinfo *sinfo);
++int au_br_index(struct super_block *sb, aufs_bindex_t br_id);
++struct au_opt_add;
++int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount);
++struct au_opt_del;
++int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount);
++struct au_opt_mod;
++int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount,
++ int *do_update);
++
++/* xino.c */
++static const loff_t au_loff_max = LLONG_MAX;
++
++int au_xib_trunc(struct super_block *sb);
++ssize_t xino_fread(au_readf_t func, struct file *file, void *buf, size_t size,
++ loff_t *pos);
++ssize_t xino_fwrite(au_writef_t func, struct file *file, void *buf, size_t size,
++ loff_t *pos);
++struct file *au_xino_create2(struct file *base_file, struct file *copy_src);
++struct file *au_xino_create(struct super_block *sb, char *fname, int silent);
++ino_t au_xino_new_ino(struct super_block *sb);
++int au_xino_write0(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
++ ino_t ino);
++int au_xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
++ ino_t ino);
++int au_xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
++ ino_t *ino);
++int au_xino_br(struct super_block *sb, struct au_branch *br, ino_t hino,
++ struct file *base_file, int do_test);
++int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex);
++
++struct au_opt_xino;
++int au_xino_set(struct super_block *sb, struct au_opt_xino *xino, int remount);
++void au_xino_clr(struct super_block *sb);
++struct file *au_xino_def(struct super_block *sb);
++int au_xino_path(struct seq_file *seq, struct file *file);
++
++/* ---------------------------------------------------------------------- */
++
++/* Superblock to branch */
++static inline
++aufs_bindex_t au_sbr_id(struct super_block *sb, aufs_bindex_t bindex)
++{
++ return au_sbr(sb, bindex)->br_id;
++}
++
++static inline
++struct vfsmount *au_sbr_mnt(struct super_block *sb, aufs_bindex_t bindex)
++{
++ return au_sbr(sb, bindex)->br_mnt;
++}
++
++static inline
++struct super_block *au_sbr_sb(struct super_block *sb, aufs_bindex_t bindex)
++{
++ return au_sbr_mnt(sb, bindex)->mnt_sb;
++}
++
++static inline void au_sbr_put(struct super_block *sb, aufs_bindex_t bindex)
++{
++ atomic_dec_return(&au_sbr(sb, bindex)->br_count);
++}
++
++static inline int au_sbr_perm(struct super_block *sb, aufs_bindex_t bindex)
++{
++ return au_sbr(sb, bindex)->br_perm;
++}
++
++static inline int au_sbr_whable(struct super_block *sb, aufs_bindex_t bindex)
++{
++ return au_br_whable(au_sbr_perm(sb, bindex));
++}
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * wbr_wh_read_lock, wbr_wh_write_lock
++ * wbr_wh_read_unlock, wbr_wh_write_unlock, wbr_wh_downgrade_lock
++ */
++AuSimpleRwsemFuncs(wbr_wh, struct au_wbr *wbr, &wbr->wbr_wh_rwsem);
++
++#define WbrWhMustNoWaiters(wbr) AuRwMustNoWaiters(&wbr->wbr_wh_rwsem)
++#define WbrWhMustAnyLock(wbr) AuRwMustAnyLock(&wbr->wbr_wh_rwsem)
++#define WbrWhMustWriteLock(wbr) AuRwMustWriteLock(&wbr->wbr_wh_rwsem)
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_BRANCH_H__ */
+diff -Nur linux-2.6.31.5.orig/fs/aufs/cpup.c linux-2.6.31.5/fs/aufs/cpup.c
+--- linux-2.6.31.5.orig/fs/aufs/cpup.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/cpup.c 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,1048 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. Okajima
++ *
++ * This program, aufs is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/*
++ * copy-up functions, see wbr_policy.c for copy-down
++ */
++
++#include <linux/file.h>
++#include <linux/fs_stack.h>
++#include <linux/mm.h>
++#include <linux/uaccess.h>
++#include "aufs.h"
++
++void au_cpup_attr_flags(struct inode *dst, struct inode *src)
++{
++ const unsigned int mask = S_DEAD | S_SWAPFILE | S_PRIVATE
++ | S_NOATIME | S_NOCMTIME;
++
++ dst->i_flags |= src->i_flags & ~mask;
++ if (au_test_fs_notime(dst->i_sb))
++ dst->i_flags |= S_NOATIME | S_NOCMTIME;
++}
++
++void au_cpup_attr_timesizes(struct inode *inode)
++{
++ struct inode *h_inode;
++
++ h_inode = au_h_iptr(inode, au_ibstart(inode));
++ fsstack_copy_attr_times(inode, h_inode);
++ vfsub_copy_inode_size(inode, h_inode);
++}
++
++void au_cpup_attr_nlink(struct inode *inode, int force)
++{
++ struct inode *h_inode;
++ struct super_block *sb;
++ aufs_bindex_t bindex, bend;
++
++ sb = inode->i_sb;
++ bindex = au_ibstart(inode);
++ h_inode = au_h_iptr(inode, bindex);
++ if (!force
++ && !S_ISDIR(h_inode->i_mode)
++ && au_opt_test(au_mntflags(sb), PLINK)
++ && au_plink_test(inode))
++ return;
++
++ inode->i_nlink = h_inode->i_nlink;
++
++ /*
++ * fewer nlink makes find(1) noisy, but larger nlink doesn't.
++ * it may includes whplink directory.
++ */
++ if (S_ISDIR(h_inode->i_mode)) {
++ bend = au_ibend(inode);
++ for (bindex++; bindex <= bend; bindex++) {
++ h_inode = au_h_iptr(inode, bindex);
++ if (h_inode)
++ au_add_nlink(inode, h_inode);
++ }
++ }
++}
++
++void au_cpup_attr_changeable(struct inode *inode)
++{
++ struct inode *h_inode;
++
++ h_inode = au_h_iptr(inode, au_ibstart(inode));
++ inode->i_mode = h_inode->i_mode;
++ inode->i_uid = h_inode->i_uid;
++ inode->i_gid = h_inode->i_gid;
++ au_cpup_attr_timesizes(inode);
++ au_cpup_attr_flags(inode, h_inode);
++}
++
++void au_cpup_igen(struct inode *inode, struct inode *h_inode)
++{
++ struct au_iinfo *iinfo = au_ii(inode);
++
++ IiMustWriteLock(inode);
++
++ iinfo->ii_higen = h_inode->i_generation;
++ iinfo->ii_hsb1 = h_inode->i_sb;
++}
++
++void au_cpup_attr_all(struct inode *inode, int force)
++{
++ struct inode *h_inode;
++
++ h_inode = au_h_iptr(inode, au_ibstart(inode));
++ au_cpup_attr_changeable(inode);
++ if (inode->i_nlink > 0)
++ au_cpup_attr_nlink(inode, force);
++ inode->i_rdev = h_inode->i_rdev;
++ inode->i_blkbits = h_inode->i_blkbits;
++ au_cpup_igen(inode, h_inode);
++}
++
++/* ---------------------------------------------------------------------- */
++
++/* Note: dt_dentry and dt_h_dentry are not dget/dput-ed */
++
++/* keep the timestamps of the parent dir when cpup */
++void au_dtime_store(struct au_dtime *dt, struct dentry *dentry,
++ struct path *h_path)
++{
++ struct inode *h_inode;
++
++ dt->dt_dentry = dentry;
++ dt->dt_h_path = *h_path;
++ h_inode = h_path->dentry->d_inode;
++ dt->dt_atime = h_inode->i_atime;
++ dt->dt_mtime = h_inode->i_mtime;
++ /* smp_mb(); */
++}
++
++void au_dtime_revert(struct au_dtime *dt)
++{
++ struct iattr attr;
++ int err;
++
++ 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;
++
++ err = vfsub_notify_change(&dt->dt_h_path, &attr);
++ if (unlikely(err))
++ AuWarn("restoring timestamps failed(%d). ignored\n", err);
++}
++
++/* ---------------------------------------------------------------------- */
++
++static noinline_for_stack
++int cpup_iattr(struct dentry *dst, aufs_bindex_t bindex, struct dentry *h_src)
++{
++ int err, sbits;
++ struct iattr ia;
++ struct path h_path;
++ struct inode *h_isrc, *h_idst;
++
++ h_path.dentry = au_h_dptr(dst, bindex);
++ h_idst = h_path.dentry->d_inode;
++ h_path.mnt = au_sbr_mnt(dst->d_sb, bindex);
++ h_isrc = h_src->d_inode;
++ ia.ia_valid = ATTR_FORCE | ATTR_UID | ATTR_GID
++ | ATTR_ATIME | ATTR_MTIME
++ | ATTR_ATIME_SET | ATTR_MTIME_SET;
++ ia.ia_uid = h_isrc->i_uid;
++ ia.ia_gid = h_isrc->i_gid;
++ ia.ia_atime = h_isrc->i_atime;
++ ia.ia_mtime = h_isrc->i_mtime;
++ if (h_idst->i_mode != h_isrc->i_mode
++ && !S_ISLNK(h_idst->i_mode)) {
++ ia.ia_valid |= ATTR_MODE;
++ ia.ia_mode = h_isrc->i_mode;
++ }
++ sbits = !!(h_isrc->i_mode & (S_ISUID | S_ISGID));
++ au_cpup_attr_flags(h_idst, h_isrc);
++ err = vfsub_notify_change(&h_path, &ia);
++
++ /* is this nfs only? */
++ if (!err && sbits && au_test_nfs(h_path.dentry->d_sb)) {
++ ia.ia_valid = ATTR_FORCE | ATTR_MODE;
++ ia.ia_mode = h_isrc->i_mode;
++ err = vfsub_notify_change(&h_path, &ia);
++ }
++
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static int au_do_copy_file(struct file *dst, struct file *src, loff_t len,
++ char *buf, unsigned long blksize)
++{
++ int err;
++ size_t sz, rbytes, wbytes;
++ unsigned char all_zero;
++ char *p, *zp;
++ struct mutex *h_mtx;
++ /* reduce stack usage */
++ struct iattr *ia;
++
++ zp = page_address(ZERO_PAGE(0));
++ if (unlikely(!zp))
++ return -ENOMEM; /* possible? */
++
++ err = 0;
++ all_zero = 0;
++ while (len) {
++ AuDbg("len %lld\n", len);
++ sz = blksize;
++ if (len < blksize)
++ sz = len;
++
++ rbytes = 0;
++ /* todo: signal_pending? */
++ while (!rbytes || err == -EAGAIN || err == -EINTR) {
++ rbytes = vfsub_read_k(src, buf, sz, &src->f_pos);
++ err = rbytes;
++ }
++ if (unlikely(err < 0))
++ break;
++
++ all_zero = 0;
++ if (len >= rbytes && rbytes == blksize)
++ all_zero = !memcmp(buf, zp, rbytes);
++ if (!all_zero) {
++ wbytes = rbytes;
++ p = buf;
++ while (wbytes) {
++ size_t b;
++
++ b = vfsub_write_k(dst, p, wbytes, &dst->f_pos);
++ err = b;
++ /* todo: signal_pending? */
++ if (unlikely(err == -EAGAIN || err == -EINTR))
++ continue;
++ if (unlikely(err < 0))
++ break;
++ wbytes -= b;
++ p += b;
++ }
++ } else {
++ loff_t res;
++
++ AuLabel(hole);
++ res = vfsub_llseek(dst, rbytes, SEEK_CUR);
++ err = res;
++ if (unlikely(res < 0))
++ break;
++ }
++ len -= rbytes;
++ err = 0;
++ }
++
++ /* the last block may be a hole */
++ if (!err && all_zero) {
++ AuLabel(last hole);
++
++ err = 1;
++ if (au_test_nfs(dst->f_dentry->d_sb)) {
++ /* nfs requires this step to make last hole */
++ /* is this only nfs? */
++ do {
++ /* todo: signal_pending? */
++ err = vfsub_write_k(dst, "\0", 1, &dst->f_pos);
++ } while (err == -EAGAIN || err == -EINTR);
++ if (err == 1)
++ dst->f_pos--;
++ }
++
++ if (err == 1) {
++ ia = (void *)buf;
++ ia->ia_size = dst->f_pos;
++ ia->ia_valid = ATTR_SIZE | ATTR_FILE;
++ ia->ia_file = dst;
++ h_mtx = &dst->f_dentry->d_inode->i_mutex;
++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD2);
++ err = vfsub_notify_change(&dst->f_path, ia);
++ mutex_unlock(h_mtx);
++ }
++ }
++
++ return err;
++}
++
++int au_copy_file(struct file *dst, struct file *src, loff_t len)
++{
++ int err;
++ unsigned long blksize;
++ unsigned char do_kfree;
++ char *buf;
++
++ err = -ENOMEM;
++ blksize = dst->f_dentry->d_sb->s_blocksize;
++ if (!blksize || PAGE_SIZE < blksize)
++ blksize = PAGE_SIZE;
++ AuDbg("blksize %lu\n", blksize);
++ do_kfree = (blksize != PAGE_SIZE && blksize >= sizeof(struct iattr *));
++ if (do_kfree)
++ buf = kmalloc(blksize, GFP_NOFS);
++ else
++ buf = (void *)__get_free_page(GFP_NOFS);
++ if (unlikely(!buf))
++ goto out;
++
++ if (len > (1 << 22))
++ AuDbg("copying a large file %lld\n", (long long)len);
++
++ src->f_pos = 0;
++ dst->f_pos = 0;
++ err = au_do_copy_file(dst, src, len, buf, blksize);
++ if (do_kfree)
++ kfree(buf);
++ else
++ free_page((unsigned long)buf);
++
++ out:
++ return err;
++}
++
++/*
++ * to support a sparse file which is opened with O_APPEND,
++ * we need to close the file.
++ */
++static int au_cp_regular(struct dentry *dentry, aufs_bindex_t bdst,
++ aufs_bindex_t bsrc, loff_t len)
++{
++ int err, i;
++ enum { SRC, DST };
++ struct {
++ aufs_bindex_t bindex;
++ unsigned int flags;
++ struct dentry *dentry;
++ struct file *file;
++ void *label, *label_file;
++ } *f, file[] = {
++ {
++ .bindex = bsrc,
++ .flags = O_RDONLY | O_NOATIME | O_LARGEFILE,
++ .file = NULL,
++ .label = &&out,
++ .label_file = &&out_src
++ },
++ {
++ .bindex = bdst,
++ .flags = O_WRONLY | O_NOATIME | O_LARGEFILE,
++ .file = NULL,
++ .label = &&out_src,
++ .label_file = &&out_dst
++ }
++ };
++ struct super_block *sb;
++
++ /* bsrc branch can be ro/rw. */
++ sb = dentry->d_sb;
++ f = file;
++ for (i = 0; i < 2; i++, f++) {
++ f->dentry = au_h_dptr(dentry, f->bindex);
++ f->file = au_h_open(dentry, f->bindex, f->flags, /*file*/NULL);
++ err = PTR_ERR(f->file);
++ if (IS_ERR(f->file))
++ goto *f->label;
++ err = -EINVAL;
++ if (unlikely(!f->file->f_op))
++ goto *f->label_file;
++ }
++
++ /* try stopping to update while we copyup */
++ IMustLock(file[SRC].dentry->d_inode);
++ err = au_copy_file(file[DST].file, file[SRC].file, len);
++
++ out_dst:
++ fput(file[DST].file);
++ au_sbr_put(sb, file[DST].bindex);
++ out_src:
++ fput(file[SRC].file);
++ au_sbr_put(sb, file[SRC].bindex);
++ out:
++ return err;
++}
++
++static int au_do_cpup_regular(struct dentry *dentry, aufs_bindex_t bdst,
++ aufs_bindex_t bsrc, loff_t len,
++ struct inode *h_dir, struct path *h_path)
++{
++ int err, rerr;
++ loff_t l;
++
++ err = 0;
++ l = i_size_read(au_h_iptr(dentry->d_inode, bsrc));
++ if (len == -1 || l < len)
++ len = l;
++ if (len)
++ err = au_cp_regular(dentry, bdst, bsrc, len);
++ if (!err)
++ goto out; /* success */
++
++ rerr = vfsub_unlink(h_dir, h_path, /*force*/0);
++ if (rerr) {
++ AuIOErr("failed unlinking cpup-ed %.*s(%d, %d)\n",
++ AuDLNPair(h_path->dentry), err, rerr);
++ err = -EIO;
++ }
++
++ out:
++ return err;
++}
++
++static int au_do_cpup_symlink(struct path *h_path, struct dentry *h_src,
++ struct inode *h_dir)
++{
++ int err, symlen;
++ mm_segment_t old_fs;
++ char *sym;
++
++ err = -ENOSYS;
++ if (unlikely(!h_src->d_inode->i_op->readlink))
++ goto out;
++
++ err = -ENOMEM;
++ sym = __getname();
++ if (unlikely(!sym))
++ goto out;
++
++ old_fs = get_fs();
++ set_fs(KERNEL_DS);
++ symlen = h_src->d_inode->i_op->readlink(h_src, (char __user *)sym,
++ PATH_MAX);
++ err = symlen;
++ set_fs(old_fs);
++
++ if (symlen > 0) {
++ sym[symlen] = 0;
++ err = vfsub_symlink(h_dir, h_path, sym);
++ }
++ __putname(sym);
++
++ out:
++ return err;
++}
++
++/* return with the lower dst inode is locked */
++static noinline_for_stack
++int cpup_entry(struct dentry *dentry, aufs_bindex_t bdst,
++ aufs_bindex_t bsrc, loff_t len, unsigned int flags,
++ struct dentry *dst_parent)
++{
++ int err;
++ umode_t mode;
++ unsigned int mnt_flags;
++ unsigned char isdir;
++ const unsigned char do_dt = !!au_ftest_cpup(flags, DTIME);
++ struct au_dtime dt;
++ struct path h_path;
++ struct dentry *h_src, *h_dst, *h_parent;
++ struct inode *h_inode, *h_dir;
++ struct super_block *sb;
++
++ /* bsrc branch can be ro/rw. */
++ h_src = au_h_dptr(dentry, bsrc);
++ h_inode = h_src->d_inode;
++ AuDebugOn(h_inode != au_h_iptr(dentry->d_inode, bsrc));
++
++ /* try stopping to be referenced while we are creating */
++ h_dst = au_h_dptr(dentry, bdst);
++ h_parent = h_dst->d_parent; /* dir inode is locked */
++ h_dir = h_parent->d_inode;
++ IMustLock(h_dir);
++ AuDebugOn(h_parent != h_dst->d_parent);
++
++ sb = dentry->d_sb;
++ h_path.mnt = au_sbr_mnt(sb, bdst);
++ if (do_dt) {
++ h_path.dentry = h_parent;
++ au_dtime_store(&dt, dst_parent, &h_path);
++ }
++ h_path.dentry = h_dst;
++
++ isdir = 0;
++ mode = h_inode->i_mode;
++ switch (mode & S_IFMT) {
++ case S_IFREG:
++ /* try stopping to update while we are referencing */
++ IMustLock(h_inode);
++ err = vfsub_create(h_dir, &h_path, mode | S_IWUSR);
++ if (!err)
++ err = au_do_cpup_regular
++ (dentry, bdst, bsrc, len,
++ au_h_iptr(dst_parent->d_inode, bdst), &h_path);
++ break;
++ case S_IFDIR:
++ isdir = 1;
++ err = vfsub_mkdir(h_dir, &h_path, mode);
++ if (!err) {
++ /*
++ * strange behaviour from the users view,
++ * particularry setattr case
++ */
++ if (au_ibstart(dst_parent->d_inode) == bdst)
++ au_cpup_attr_nlink(dst_parent->d_inode,
++ /*force*/1);
++ au_cpup_attr_nlink(dentry->d_inode, /*force*/1);
++ }
++ break;
++ case S_IFLNK:
++ err = au_do_cpup_symlink(&h_path, h_src, h_dir);
++ break;
++ case S_IFCHR:
++ case S_IFBLK:
++ AuDebugOn(!capable(CAP_MKNOD));
++ /*FALLTHROUGH*/
++ case S_IFIFO:
++ case S_IFSOCK:
++ err = vfsub_mknod(h_dir, &h_path, mode, h_inode->i_rdev);
++ break;
++ default:
++ AuIOErr("Unknown inode type 0%o\n", mode);
++ err = -EIO;
++ }
++
++ mnt_flags = au_mntflags(sb);
++ if (!au_opt_test(mnt_flags, UDBA_NONE)
++ && !isdir
++ && au_opt_test(mnt_flags, XINO)
++ && h_inode->i_nlink == 1
++ /* todo: unnecessary? */
++ /* && dentry->d_inode->i_nlink == 1 */
++ && bdst < bsrc
++ && !au_ftest_cpup(flags, KEEPLINO))
++ au_xino_write(sb, bsrc, h_inode->i_ino, /*ino*/0);
++ /* ignore this error */
++
++ if (do_dt)
++ au_dtime_revert(&dt);
++ return err;
++}
++
++/*
++ * copyup the @dentry from @bsrc to @bdst.
++ * the caller must set the both of lower dentries.
++ * @len is for truncating when it is -1 copyup the entire file.
++ * in link/rename cases, @dst_parent may be different from the real one.
++ */
++static int au_cpup_single(struct dentry *dentry, aufs_bindex_t bdst,
++ aufs_bindex_t bsrc, loff_t len, unsigned int flags,
++ struct dentry *dst_parent)
++{
++ int err, rerr;
++ aufs_bindex_t old_ibstart;
++ unsigned char isdir, plink;
++ struct au_dtime dt;
++ struct path h_path;
++ struct dentry *h_src, *h_dst, *h_parent;
++ struct inode *dst_inode, *h_dir, *inode;
++ struct super_block *sb;
++
++ AuDebugOn(bsrc <= bdst);
++
++ sb = dentry->d_sb;
++ h_path.mnt = au_sbr_mnt(sb, bdst);
++ h_dst = au_h_dptr(dentry, bdst);
++ h_parent = h_dst->d_parent; /* dir inode is locked */
++ h_dir = h_parent->d_inode;
++ IMustLock(h_dir);
++
++ h_src = au_h_dptr(dentry, bsrc);
++ inode = dentry->d_inode;
++
++ if (!dst_parent)
++ dst_parent = dget_parent(dentry);
++ else
++ dget(dst_parent);
++
++ plink = !!au_opt_test(au_mntflags(sb), PLINK);
++ dst_inode = au_h_iptr(inode, bdst);
++ if (dst_inode) {
++ if (unlikely(!plink)) {
++ err = -EIO;
++ AuIOErr("i%lu exists on a upper branch "
++ "but plink is disabled\n", inode->i_ino);
++ goto out;
++ }
++
++ if (dst_inode->i_nlink) {
++ const int do_dt = au_ftest_cpup(flags, DTIME);
++
++ h_src = au_plink_lkup(inode, bdst);
++ err = PTR_ERR(h_src);
++ if (IS_ERR(h_src))
++ goto out;
++ if (unlikely(!h_src->d_inode)) {
++ err = -EIO;
++ AuIOErr("i%lu exists on a upper branch "
++ "but plink is broken\n", inode->i_ino);
++ dput(h_src);
++ goto out;
++ }
++
++ if (do_dt) {
++ h_path.dentry = h_parent;
++ au_dtime_store(&dt, dst_parent, &h_path);
++ }
++ h_path.dentry = h_dst;
++ err = vfsub_link(h_src, h_dir, &h_path);
++ if (do_dt)
++ au_dtime_revert(&dt);
++ dput(h_src);
++ goto out;
++ } else
++ /* todo: cpup_wh_file? */
++ /* udba work */
++ au_update_brange(inode, 1);
++ }
++
++ old_ibstart = au_ibstart(inode);
++ err = cpup_entry(dentry, bdst, bsrc, len, flags, dst_parent);
++ if (unlikely(err))
++ goto out;
++ dst_inode = h_dst->d_inode;
++ mutex_lock_nested(&dst_inode->i_mutex, AuLsc_I_CHILD2);
++
++ err = cpup_iattr(dentry, bdst, h_src);
++ isdir = S_ISDIR(dst_inode->i_mode);
++ if (!err) {
++ if (bdst < old_ibstart)
++ au_set_ibstart(inode, bdst);
++ au_set_h_iptr(inode, bdst, au_igrab(dst_inode),
++ au_hi_flags(inode, isdir));
++ mutex_unlock(&dst_inode->i_mutex);
++ if (!isdir
++ && h_src->d_inode->i_nlink > 1
++ && plink)
++ au_plink_append(inode, bdst, h_dst);
++ goto out; /* success */
++ }
++
++ /* revert */
++ h_path.dentry = h_parent;
++ mutex_unlock(&dst_inode->i_mutex);
++ au_dtime_store(&dt, dst_parent, &h_path);
++ h_path.dentry = h_dst;
++ if (!isdir)
++ rerr = vfsub_unlink(h_dir, &h_path, /*force*/0);
++ else
++ rerr = vfsub_rmdir(h_dir, &h_path);
++ au_dtime_revert(&dt);
++ if (rerr) {
++ AuIOErr("failed removing broken entry(%d, %d)\n", err, rerr);
++ err = -EIO;
++ }
++
++ out:
++ dput(dst_parent);
++ return err;
++}
++
++struct au_cpup_single_args {
++ int *errp;
++ struct dentry *dentry;
++ aufs_bindex_t bdst, bsrc;
++ loff_t len;
++ unsigned int flags;
++ struct dentry *dst_parent;
++};
++
++static void au_call_cpup_single(void *args)
++{
++ struct au_cpup_single_args *a = args;
++ *a->errp = au_cpup_single(a->dentry, a->bdst, a->bsrc, a->len,
++ a->flags, a->dst_parent);
++}
++
++int au_sio_cpup_single(struct dentry *dentry, aufs_bindex_t bdst,
++ aufs_bindex_t bsrc, loff_t len, unsigned int flags,
++ struct dentry *dst_parent)
++{
++ int err, wkq_err;
++ umode_t mode;
++ struct dentry *h_dentry;
++
++ h_dentry = au_h_dptr(dentry, bsrc);
++ mode = h_dentry->d_inode->i_mode & S_IFMT;
++ if ((mode != S_IFCHR && mode != S_IFBLK)
++ || capable(CAP_MKNOD))
++ err = au_cpup_single(dentry, bdst, bsrc, len, flags,
++ dst_parent);
++ else {
++ struct au_cpup_single_args args = {
++ .errp = &err,
++ .dentry = dentry,
++ .bdst = bdst,
++ .bsrc = bsrc,
++ .len = len,
++ .flags = flags,
++ .dst_parent = dst_parent
++ };
++ wkq_err = au_wkq_wait(au_call_cpup_single, &args);
++ if (unlikely(wkq_err))
++ err = wkq_err;
++ }
++
++ return err;
++}
++
++/*
++ * copyup the @dentry from the first active lower branch to @bdst,
++ * using au_cpup_single().
++ */
++static int au_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
++ unsigned int flags)
++{
++ int err;
++ aufs_bindex_t bsrc, bend;
++
++ bend = au_dbend(dentry);
++ for (bsrc = bdst + 1; bsrc <= bend; bsrc++)
++ if (au_h_dptr(dentry, bsrc))
++ break;
++
++ err = au_lkup_neg(dentry, bdst);
++ if (!err) {
++ err = au_cpup_single(dentry, bdst, bsrc, len, flags, NULL);
++ if (!err)
++ return 0; /* success */
++
++ /* revert */
++ au_set_h_dptr(dentry, bdst, NULL);
++ au_set_dbstart(dentry, bsrc);
++ }
++
++ return err;
++}
++
++struct au_cpup_simple_args {
++ int *errp;
++ struct dentry *dentry;
++ aufs_bindex_t bdst;
++ loff_t len;
++ unsigned int flags;
++};
++
++static void au_call_cpup_simple(void *args)
++{
++ struct au_cpup_simple_args *a = args;
++ *a->errp = au_cpup_simple(a->dentry, a->bdst, a->len, a->flags);
++}
++
++int au_sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
++ unsigned int flags)
++{
++ int err, wkq_err;
++ unsigned char do_sio;
++ struct dentry *parent;
++ struct inode *h_dir;
++
++ parent = dget_parent(dentry);
++ h_dir = au_h_iptr(parent->d_inode, bdst);
++ do_sio = !!au_test_h_perm_sio(h_dir, MAY_EXEC | MAY_WRITE);
++ if (!do_sio) {
++ /*
++ * testing CAP_MKNOD is for generic fs,
++ * but CAP_FSETID is for xfs only, currently.
++ */
++ umode_t mode = dentry->d_inode->i_mode;
++ do_sio = (((mode & (S_IFCHR | S_IFBLK))
++ && !capable(CAP_MKNOD))
++ || ((mode & (S_ISUID | S_ISGID))
++ && !capable(CAP_FSETID)));
++ }
++ if (!do_sio)
++ err = au_cpup_simple(dentry, bdst, len, flags);
++ else {
++ struct au_cpup_simple_args args = {
++ .errp = &err,
++ .dentry = dentry,
++ .bdst = bdst,
++ .len = len,
++ .flags = flags
++ };
++ wkq_err = au_wkq_wait(au_call_cpup_simple, &args);
++ if (unlikely(wkq_err))
++ err = wkq_err;
++ }
++
++ dput(parent);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * copyup the deleted file for writing.
++ */
++static int au_do_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst,
++ struct dentry *wh_dentry, struct file *file,
++ loff_t len)
++{
++ int err;
++ aufs_bindex_t bstart;
++ struct au_dinfo *dinfo;
++ struct dentry *h_d_dst, *h_d_start;
++
++ dinfo = au_di(dentry);
++ AuRwMustWriteLock(&dinfo->di_rwsem);
++
++ bstart = dinfo->di_bstart;
++ h_d_dst = dinfo->di_hdentry[0 + bdst].hd_dentry;
++ dinfo->di_bstart = bdst;
++ dinfo->di_hdentry[0 + bdst].hd_dentry = wh_dentry;
++ h_d_start = dinfo->di_hdentry[0 + bstart].hd_dentry;
++ if (file)
++ dinfo->di_hdentry[0 + bstart].hd_dentry
++ = au_h_fptr(file, au_fbstart(file))->f_dentry;
++ err = au_cpup_single(dentry, bdst, bstart, len, !AuCpup_DTIME,
++ /*h_parent*/NULL);
++ if (!err && file) {
++ err = au_reopen_nondir(file);
++ dinfo->di_hdentry[0 + bstart].hd_dentry = h_d_start;
++ }
++ dinfo->di_hdentry[0 + bdst].hd_dentry = h_d_dst;
++ dinfo->di_bstart = bstart;
++
++ return err;
++}
++
++static int au_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
++ struct file *file)
++{
++ int err;
++ struct au_dtime dt;
++ struct dentry *parent, *h_parent, *wh_dentry;
++ struct au_branch *br;
++ struct path h_path;
++
++ br = au_sbr(dentry->d_sb, bdst);
++ parent = dget_parent(dentry);
++ h_parent = au_h_dptr(parent, bdst);
++ wh_dentry = au_whtmp_lkup(h_parent, br, &dentry->d_name);
++ err = PTR_ERR(wh_dentry);
++ if (IS_ERR(wh_dentry))
++ goto out;
++
++ h_path.dentry = h_parent;
++ h_path.mnt = br->br_mnt;
++ au_dtime_store(&dt, parent, &h_path);
++ err = au_do_cpup_wh(dentry, bdst, wh_dentry, file, len);
++ if (unlikely(err))
++ goto out_wh;
++
++ dget(wh_dentry);
++ h_path.dentry = wh_dentry;
++ err = vfsub_unlink(h_parent->d_inode, &h_path, /*force*/0);
++ if (unlikely(err)) {
++ AuIOErr("failed remove copied-up tmp file %.*s(%d)\n",
++ AuDLNPair(wh_dentry), err);
++ err = -EIO;
++ }
++ au_dtime_revert(&dt);
++ au_set_hi_wh(dentry->d_inode, bdst, wh_dentry);
++
++ out_wh:
++ dput(wh_dentry);
++ out:
++ dput(parent);
++ return err;
++}
++
++struct au_cpup_wh_args {
++ int *errp;
++ struct dentry *dentry;
++ aufs_bindex_t bdst;
++ loff_t len;
++ struct file *file;
++};
++
++static void au_call_cpup_wh(void *args)
++{
++ struct au_cpup_wh_args *a = args;
++ *a->errp = au_cpup_wh(a->dentry, a->bdst, a->len, a->file);
++}
++
++int au_sio_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
++ struct file *file)
++{
++ int err, wkq_err;
++ struct dentry *parent, *h_orph, *h_parent, *h_dentry;
++ struct inode *dir, *h_dir, *h_tmpdir, *h_inode;
++ struct au_wbr *wbr;
++
++ parent = dget_parent(dentry);
++ dir = parent->d_inode;
++ h_orph = NULL;
++ h_parent = NULL;
++ h_dir = au_igrab(au_h_iptr(dir, bdst));
++ h_tmpdir = h_dir;
++ if (!h_dir->i_nlink) {
++ wbr = au_sbr(dentry->d_sb, bdst)->br_wbr;
++ h_orph = wbr->wbr_orph;
++
++ h_parent = dget(au_h_dptr(parent, bdst));
++ au_set_h_dptr(parent, bdst, NULL);
++ au_set_h_dptr(parent, bdst, dget(h_orph));
++ h_tmpdir = h_orph->d_inode;
++ au_set_h_iptr(dir, bdst, NULL, 0);
++ au_set_h_iptr(dir, bdst, au_igrab(h_tmpdir), /*flags*/0);
++
++ /* this temporary unlock is safe */
++ if (file)
++ h_dentry = au_h_fptr(file, au_fbstart(file))->f_dentry;
++ else
++ h_dentry = au_h_dptr(dentry, au_dbstart(dentry));
++ h_inode = h_dentry->d_inode;
++ IMustLock(h_inode);
++ mutex_unlock(&h_inode->i_mutex);
++ mutex_lock_nested(&h_tmpdir->i_mutex, AuLsc_I_PARENT3);
++ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);
++ }
++
++ if (!au_test_h_perm_sio(h_tmpdir, MAY_EXEC | MAY_WRITE))
++ err = au_cpup_wh(dentry, bdst, len, file);
++ else {
++ struct au_cpup_wh_args args = {
++ .errp = &err,
++ .dentry = dentry,
++ .bdst = bdst,
++ .len = len,
++ .file = file
++ };
++ wkq_err = au_wkq_wait(au_call_cpup_wh, &args);
++ if (unlikely(wkq_err))
++ err = wkq_err;
++ }
++
++ if (h_orph) {
++ mutex_unlock(&h_tmpdir->i_mutex);
++ au_set_h_iptr(dir, bdst, NULL, 0);
++ au_set_h_iptr(dir, bdst, au_igrab(h_dir), /*flags*/0);
++ au_set_h_dptr(parent, bdst, NULL);
++ au_set_h_dptr(parent, bdst, h_parent);
++ }
++ iput(h_dir);
++ dput(parent);
++
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * generic routine for both of copy-up and copy-down.
++ */
++/* cf. revalidate function in file.c */
++int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst,
++ int (*cp)(struct dentry *dentry, aufs_bindex_t bdst,
++ struct dentry *h_parent, void *arg),
++ void *arg)
++{
++ int err;
++ struct au_pin pin;
++ struct dentry *d, *parent, *h_parent, *real_parent;
++
++ err = 0;
++ parent = dget_parent(dentry);
++ if (IS_ROOT(parent))
++ goto out;
++
++ au_pin_init(&pin, dentry, bdst, AuLsc_DI_PARENT2, AuLsc_I_PARENT2,
++ au_opt_udba(dentry->d_sb), AuPin_MNT_WRITE);
++
++ /* do not use au_dpage */
++ real_parent = parent;
++ while (1) {
++ dput(parent);
++ parent = dget_parent(dentry);
++ h_parent = au_h_dptr(parent, bdst);
++ if (h_parent)
++ goto out; /* success */
++
++ /* find top dir which is necessary to cpup */
++ do {
++ d = parent;
++ dput(parent);
++ parent = dget_parent(d);
++ di_read_lock_parent3(parent, !AuLock_IR);
++ h_parent = au_h_dptr(parent, bdst);
++ di_read_unlock(parent, !AuLock_IR);
++ } while (!h_parent);
++
++ if (d != real_parent)
++ di_write_lock_child3(d);
++
++ /* somebody else might create while we were sleeping */
++ if (!au_h_dptr(d, bdst) || !au_h_dptr(d, bdst)->d_inode) {
++ if (au_h_dptr(d, bdst))
++ au_update_dbstart(d);
++
++ au_pin_set_dentry(&pin, d);
++ err = au_do_pin(&pin);
++ if (!err) {
++ err = cp(d, bdst, h_parent, arg);
++ au_unpin(&pin);
++ }
++ }
++
++ if (d != real_parent)
++ di_write_unlock(d);
++ if (unlikely(err))
++ break;
++ }
++
++ out:
++ dput(parent);
++ return err;
++}
++
++static int au_cpup_dir(struct dentry *dentry, aufs_bindex_t bdst,
++ struct dentry *h_parent __maybe_unused ,
++ void *arg __maybe_unused)
++{
++ return au_sio_cpup_simple(dentry, bdst, -1, AuCpup_DTIME);
++}
++
++int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst)
++{
++ return au_cp_dirs(dentry, bdst, au_cpup_dir, NULL);
++}
++
++int au_test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst)
++{
++ int err;
++ struct dentry *parent;
++ struct inode *dir;
++
++ parent = dget_parent(dentry);
++ dir = parent->d_inode;
++ err = 0;
++ if (au_h_iptr(dir, bdst))
++ goto out;
++
++ di_read_unlock(parent, AuLock_IR);
++ di_write_lock_parent(parent);
++ /* someone else might change our inode while we were sleeping */
++ if (!au_h_iptr(dir, bdst))
++ err = au_cpup_dirs(dentry, bdst);
++ di_downgrade_lock(parent, AuLock_IR);
++
++ out:
++ dput(parent);
++ return err;
++}
+diff -Nur linux-2.6.31.5.orig/fs/aufs/cpup.h linux-2.6.31.5/fs/aufs/cpup.h
+--- linux-2.6.31.5.orig/fs/aufs/cpup.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/cpup.h 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,81 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. Okajima
++ *
++ * This program, aufs is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/*
++ * copy-up/down functions
++ */
++
++#ifndef __AUFS_CPUP_H__
++#define __AUFS_CPUP_H__
++
++#ifdef __KERNEL__
++
++#include <linux/path.h>
++#include <linux/time.h>
++#include <linux/aufs_type.h>
++
++struct inode;
++struct file;
++
++void au_cpup_attr_flags(struct inode *dst, struct inode *src);
++void au_cpup_attr_timesizes(struct inode *inode);
++void au_cpup_attr_nlink(struct inode *inode, int force);
++void au_cpup_attr_changeable(struct inode *inode);
++void au_cpup_igen(struct inode *inode, struct inode *h_inode);
++void au_cpup_attr_all(struct inode *inode, int force);
++
++/* ---------------------------------------------------------------------- */
++
++/* cpup flags */
++#define AuCpup_DTIME 1 /* do dtime_store/revert */
++#define AuCpup_KEEPLINO (1 << 1) /* do not clear the lower xino,
++ for link(2) */
++#define au_ftest_cpup(flags, name) ((flags) & AuCpup_##name)
++#define au_fset_cpup(flags, name) { (flags) |= AuCpup_##name; }
++#define au_fclr_cpup(flags, name) { (flags) &= ~AuCpup_##name; }
++
++int au_copy_file(struct file *dst, struct file *src, loff_t len);
++int au_sio_cpup_single(struct dentry *dentry, aufs_bindex_t bdst,
++ aufs_bindex_t bsrc, loff_t len, unsigned int flags,
++ struct dentry *dst_parent);
++int au_sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
++ unsigned int flags);
++int au_sio_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
++ struct file *file);
++
++int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst,
++ int (*cp)(struct dentry *dentry, aufs_bindex_t bdst,
++ struct dentry *h_parent, void *arg),
++ void *arg);
++int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst);
++int au_test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst);
++
++/* ---------------------------------------------------------------------- */
++
++/* keep timestamps when copyup */
++struct au_dtime {
++ struct dentry *dt_dentry;
++ struct path dt_h_path;
++ struct timespec dt_atime, dt_mtime;
++};
++void au_dtime_store(struct au_dtime *dt, struct dentry *dentry,
++ struct path *h_path);
++void au_dtime_revert(struct au_dtime *dt);
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_CPUP_H__ */
+diff -Nur linux-2.6.31.5.orig/fs/aufs/dbgaufs.c linux-2.6.31.5/fs/aufs/dbgaufs.c
+--- linux-2.6.31.5.orig/fs/aufs/dbgaufs.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/dbgaufs.c 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,331 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. 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
++ */
++
++/*
++ * debugfs interface
++ */
++
++#include <linux/debugfs.h>
++#include "aufs.h"
++
++#ifndef CONFIG_SYSFS
++#error DEBUG_FS depends upon SYSFS
++#endif
++
++static struct dentry *dbgaufs;
++static const mode_t dbgaufs_mode = S_IRUSR | S_IRGRP | S_IROTH;
++
++/* 20 is max digits length of ulong 64 */
++struct dbgaufs_arg {
++ int n;
++ char a[20 * 4];
++};
++
++/*
++ * common function for all XINO files
++ */
++static int dbgaufs_xi_release(struct inode *inode __maybe_unused,
++ struct file *file)
++{
++ kfree(file->private_data);
++ return 0;
++}
++
++static int dbgaufs_xi_open(struct file *xf, struct file *file, int do_fcnt)
++{
++ int err;
++ struct kstat st;
++ struct dbgaufs_arg *p;
++
++ err = -ENOMEM;
++ p = kmalloc(sizeof(*p), GFP_NOFS);
++ if (unlikely(!p))
++ goto out;
++
++ err = 0;
++ p->n = 0;
++ file->private_data = p;
++ if (!xf)
++ goto out;
++
++ err = vfs_getattr(xf->f_vfsmnt, xf->f_dentry, &st);
++ if (!err) {
++ if (do_fcnt)
++ p->n = snprintf
++ (p->a, sizeof(p->a), "%ld, %llux%lu %lld\n",
++ (long)file_count(xf), st.blocks, st.blksize,
++ (long long)st.size);
++ else
++ p->n = snprintf(p->a, sizeof(p->a), "%llux%lu %lld\n",
++ st.blocks, st.blksize,
++ (long long)st.size);
++ AuDebugOn(p->n >= sizeof(p->a));
++ } else {
++ p->n = snprintf(p->a, sizeof(p->a), "err %d\n", err);
++ err = 0;
++ }
++
++ out:
++ return err;
++
++}
++
++static ssize_t dbgaufs_xi_read(struct file *file, char __user *buf,
++ size_t count, loff_t *ppos)
++{
++ struct dbgaufs_arg *p;
++
++ p = file->private_data;
++ return simple_read_from_buffer(buf, count, ppos, p->a, p->n);
++}
++
++/* ---------------------------------------------------------------------- */
++
++static int dbgaufs_xib_open(struct inode *inode, struct file *file)
++{
++ int err;
++ struct au_sbinfo *sbinfo;
++ struct super_block *sb;
++
++ sbinfo = inode->i_private;
++ sb = sbinfo->si_sb;
++ si_noflush_read_lock(sb);
++ err = dbgaufs_xi_open(sbinfo->si_xib, file, /*do_fcnt*/0);
++ si_read_unlock(sb);
++ return err;
++}
++
++static const struct file_operations dbgaufs_xib_fop = {
++ .open = dbgaufs_xib_open,
++ .release = dbgaufs_xi_release,
++ .read = dbgaufs_xi_read
++};
++
++/* ---------------------------------------------------------------------- */
++
++#define DbgaufsXi_PREFIX "xi"
++
++static int dbgaufs_xino_open(struct inode *inode, struct file *file)
++{
++ int err;
++ long l;
++ struct au_sbinfo *sbinfo;
++ struct super_block *sb;
++ struct file *xf;
++ struct qstr *name;
++
++ err = -ENOENT;
++ xf = NULL;
++ name = &file->f_dentry->d_name;
++ if (unlikely(name->len < sizeof(DbgaufsXi_PREFIX)
++ || memcmp(name->name, DbgaufsXi_PREFIX,
++ sizeof(DbgaufsXi_PREFIX) - 1)))
++ goto out;
++ err = strict_strtol(name->name + sizeof(DbgaufsXi_PREFIX) - 1, 10, &l);
++ if (unlikely(err))
++ goto out;
++
++ sbinfo = inode->i_private;
++ sb = sbinfo->si_sb;
++ si_noflush_read_lock(sb);
++ if (l <= au_sbend(sb)) {
++ xf = au_sbr(sb, (aufs_bindex_t)l)->br_xino.xi_file;
++ err = dbgaufs_xi_open(xf, file, /*do_fcnt*/1);
++ } else
++ err = -ENOENT;
++ si_read_unlock(sb);
++
++ out:
++ return err;
++}
++
++static const struct file_operations dbgaufs_xino_fop = {
++ .open = dbgaufs_xino_open,
++ .release = dbgaufs_xi_release,
++ .read = dbgaufs_xi_read
++};
++
++void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex)
++{
++ aufs_bindex_t bend;
++ struct au_branch *br;
++ struct au_xino_file *xi;
++
++ if (!au_sbi(sb)->si_dbgaufs)
++ return;
++
++ bend = au_sbend(sb);
++ for (; bindex <= bend; bindex++) {
++ br = au_sbr(sb, bindex);
++ xi = &br->br_xino;
++ if (xi->xi_dbgaufs) {
++ debugfs_remove(xi->xi_dbgaufs);
++ xi->xi_dbgaufs = NULL;
++ }
++ }
++}
++
++void dbgaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex)
++{
++ struct au_sbinfo *sbinfo;
++ struct dentry *parent;
++ struct au_branch *br;
++ struct au_xino_file *xi;
++ aufs_bindex_t bend;
++ char name[sizeof(DbgaufsXi_PREFIX) + 5]; /* "xi" bindex NULL */
++
++ sbinfo = au_sbi(sb);
++ parent = sbinfo->si_dbgaufs;
++ if (!parent)
++ return;
++
++ bend = au_sbend(sb);
++ for (; bindex <= bend; bindex++) {
++ snprintf(name, sizeof(name), DbgaufsXi_PREFIX "%d", bindex);
++ br = au_sbr(sb, bindex);
++ xi = &br->br_xino;
++ AuDebugOn(xi->xi_dbgaufs);
++ xi->xi_dbgaufs = debugfs_create_file(name, dbgaufs_mode, parent,
++ sbinfo, &dbgaufs_xino_fop);
++ /* ignore an error */
++ if (unlikely(!xi->xi_dbgaufs))
++ AuWarn1("failed %s under debugfs\n", name);
++ }
++}
++
++/* ---------------------------------------------------------------------- */
++
++#ifdef CONFIG_AUFS_EXPORT
++static int dbgaufs_xigen_open(struct inode *inode, struct file *file)
++{
++ int err;
++ struct au_sbinfo *sbinfo;
++ struct super_block *sb;
++
++ sbinfo = inode->i_private;
++ sb = sbinfo->si_sb;
++ si_noflush_read_lock(sb);
++ err = dbgaufs_xi_open(sbinfo->si_xigen, file, /*do_fcnt*/0);
++ si_read_unlock(sb);
++ return err;
++}
++
++static const struct file_operations dbgaufs_xigen_fop = {
++ .open = dbgaufs_xigen_open,
++ .release = dbgaufs_xi_release,
++ .read = dbgaufs_xi_read
++};
++
++static int dbgaufs_xigen_init(struct au_sbinfo *sbinfo)
++{
++ int err;
++
++ /*
++ * This function is a dynamic '__init' fucntion actually,
++ * so the tiny check for si_rwsem is unnecessary.
++ */
++ /* AuRwMustWriteLock(&sbinfo->si_rwsem); */
++
++ err = -EIO;
++ sbinfo->si_dbgaufs_xigen = debugfs_create_file
++ ("xigen", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo,
++ &dbgaufs_xigen_fop);
++ if (sbinfo->si_dbgaufs_xigen)
++ err = 0;
++
++ return err;
++}
++#else
++static int dbgaufs_xigen_init(struct au_sbinfo *sbinfo)
++{
++ return 0;
++}
++#endif /* CONFIG_AUFS_EXPORT */
++
++/* ---------------------------------------------------------------------- */
++
++void dbgaufs_si_fin(struct au_sbinfo *sbinfo)
++{
++ /*
++ * This function is a dynamic '__init' fucntion actually,
++ * so the tiny check for si_rwsem is unnecessary.
++ */
++ /* AuRwMustWriteLock(&sbinfo->si_rwsem); */
++
++ debugfs_remove_recursive(sbinfo->si_dbgaufs);
++ sbinfo->si_dbgaufs = NULL;
++ kobject_put(&sbinfo->si_kobj);
++}
++
++int dbgaufs_si_init(struct au_sbinfo *sbinfo)
++{
++ int err;
++ char name[SysaufsSiNameLen];
++
++ /*
++ * This function is a dynamic '__init' fucntion actually,
++ * so the tiny check for si_rwsem is unnecessary.
++ */
++ /* AuRwMustWriteLock(&sbinfo->si_rwsem); */
++
++ err = -ENOENT;
++ if (!dbgaufs) {
++ AuErr1("/debug/aufs is uninitialized\n");
++ goto out;
++ }
++
++ err = -EIO;
++ sysaufs_name(sbinfo, name);
++ sbinfo->si_dbgaufs = debugfs_create_dir(name, dbgaufs);
++ if (unlikely(!sbinfo->si_dbgaufs))
++ goto out;
++ kobject_get(&sbinfo->si_kobj);
++
++ sbinfo->si_dbgaufs_xib = debugfs_create_file
++ ("xib", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo,
++ &dbgaufs_xib_fop);
++ if (unlikely(!sbinfo->si_dbgaufs_xib))
++ goto out_dir;
++
++ err = dbgaufs_xigen_init(sbinfo);
++ if (!err)
++ goto out; /* success */
++
++ out_dir:
++ dbgaufs_si_fin(sbinfo);
++ out:
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++void dbgaufs_fin(void)
++{
++ debugfs_remove(dbgaufs);
++}
++
++int __init dbgaufs_init(void)
++{
++ int err;
++
++ err = -EIO;
++ dbgaufs = debugfs_create_dir(AUFS_NAME, NULL);
++ if (dbgaufs)
++ err = 0;
++ return err;
++}
+diff -Nur linux-2.6.31.5.orig/fs/aufs/dbgaufs.h linux-2.6.31.5/fs/aufs/dbgaufs.h
+--- linux-2.6.31.5.orig/fs/aufs/dbgaufs.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/dbgaufs.h 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,79 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. 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
++ */
++
++/*
++ * debugfs interface
++ */
++
++#ifndef __DBGAUFS_H__
++#define __DBGAUFS_H__
++
++#ifdef __KERNEL__
++
++#include <linux/init.h>
++#include <linux/aufs_type.h>
++
++struct super_block;
++struct au_sbinfo;
++
++#ifdef CONFIG_DEBUG_FS
++/* dbgaufs.c */
++void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex);
++void dbgaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex);
++void dbgaufs_si_fin(struct au_sbinfo *sbinfo);
++int dbgaufs_si_init(struct au_sbinfo *sbinfo);
++void dbgaufs_fin(void);
++int __init dbgaufs_init(void);
++
++#else
++
++static inline
++void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex)
++{
++ /* empty */
++}
++
++static inline
++void dbgaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex)
++{
++ /* empty */
++}
++
++static inline
++void dbgaufs_si_fin(struct au_sbinfo *sbinfo)
++{
++ /* empty */
++}
++
++static inline
++int dbgaufs_si_init(struct au_sbinfo *sbinfo)
++{
++ return 0;
++}
++
++#define dbgaufs_fin() do {} while (0)
++
++static inline
++int __init dbgaufs_init(void)
++{
++ return 0;
++}
++#endif /* CONFIG_DEBUG_FS */
++
++#endif /* __KERNEL__ */
++#endif /* __DBGAUFS_H__ */
+diff -Nur linux-2.6.31.5.orig/fs/aufs/dcsub.c linux-2.6.31.5/fs/aufs/dcsub.c
+--- linux-2.6.31.5.orig/fs/aufs/dcsub.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/dcsub.c 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,223 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. Okajima
++ *
++ * This program, aufs is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/*
++ * sub-routines for dentry cache
++ */
++
++#include "aufs.h"
++
++static void au_dpage_free(struct au_dpage *dpage)
++{
++ int i;
++ struct dentry **p;
++
++ p = dpage->dentries;
++ for (i = 0; i < dpage->ndentry; i++)
++ dput(*p++);
++ free_page((unsigned long)dpage->dentries);
++}
++
++int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp)
++{
++ int err;
++ void *p;
++
++ 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:
++ return err;
++}
++
++void au_dpages_free(struct au_dcsub_pages *dpages)
++{
++ int i;
++ struct au_dpage *p;
++
++ p = dpages->dpages;
++ for (i = 0; i < dpages->ndpage; i++)
++ au_dpage_free(p++);
++ 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;
++
++ dpage = dpages->dpages + dpages->ndpage - 1;
++ sz = PAGE_SIZE / sizeof(dentry);
++ if (unlikely(dpage->ndentry >= sz)) {
++ AuLabel(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;
++
++ dpages->dpages = p;
++ 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:
++ 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;
++
++ err = 0;
++ spin_lock(&dcache_lock);
++ repeat:
++ next = this_parent->d_subdirs.next;
++ resume:
++ if (this_parent->d_sb == sb
++ && !IS_ROOT(this_parent)
++ && atomic_read(&this_parent->d_count)
++ && this_parent->d_inode
++ && (!test || test(this_parent, arg))) {
++ err = au_dpages_append(dpages, this_parent, GFP_ATOMIC);
++ if (unlikely(err))
++ goto out;
++ }
++
++ while (next != &this_parent->d_subdirs) {
++ struct list_head *tmp = next;
++ struct dentry *dentry = list_entry(tmp, struct dentry,
++ d_u.d_child);
++ next = tmp->next;
++ if (/*d_unhashed(dentry) || */!dentry->d_inode)
++ continue;
++ if (!list_empty(&dentry->d_subdirs)) {
++ this_parent = dentry;
++ goto repeat;
++ }
++ if (dentry->d_sb == sb
++ && atomic_read(&dentry->d_count)
++ && (!test || test(dentry, arg))) {
++ err = au_dpages_append(dpages, dentry, GFP_ATOMIC);
++ if (unlikely(err))
++ goto out;
++ }
++ }
++
++ if (this_parent != root) {
++ next = this_parent->d_u.d_child.next;
++ this_parent = this_parent->d_parent; /* dcache_lock is locked */
++ goto resume;
++ }
++ out:
++ spin_unlock(&dcache_lock);
++ return err;
++}
++
++int au_dcsub_pages_rev(struct au_dcsub_pages *dpages, struct dentry *dentry,
++ int do_include, au_dpages_test test, void *arg)
++{
++ int err;
++
++ err = 0;
++ spin_lock(&dcache_lock);
++ if (do_include && (!test || test(dentry, arg))) {
++ err = au_dpages_append(dpages, dentry, GFP_ATOMIC);
++ if (unlikely(err))
++ goto out;
++ }
++ while (!IS_ROOT(dentry)) {
++ dentry = dentry->d_parent; /* dcache_lock is locked */
++ if (!test || test(dentry, arg)) {
++ err = au_dpages_append(dpages, dentry, GFP_ATOMIC);
++ if (unlikely(err))
++ break;
++ }
++ }
++
++ out:
++ spin_unlock(&dcache_lock);
++
++ return err;
++}
++
++struct dentry *au_test_subdir(struct dentry *d1, struct dentry *d2)
++{
++ struct dentry *trap, **dentries;
++ int err, i, j;
++ struct au_dcsub_pages dpages;
++ struct au_dpage *dpage;
++
++ trap = ERR_PTR(-ENOMEM);
++ err = au_dpages_init(&dpages, GFP_NOFS);
++ if (unlikely(err))
++ goto out;
++ err = au_dcsub_pages_rev(&dpages, d1, /*do_include*/1, NULL, NULL);
++ if (unlikely(err))
++ goto out_dpages;
++
++ trap = d1;
++ for (i = 0; !err && i < dpages.ndpage; i++) {
++ dpage = dpages.dpages + i;
++ dentries = dpage->dentries;
++ for (j = 0; !err && j < dpage->ndentry; j++) {
++ struct dentry *d;
++
++ d = dentries[j];
++ err = (d == d2);
++ if (!err)
++ trap = d;
++ }
++ }
++ if (!err)
++ trap = NULL;
++
++ out_dpages:
++ au_dpages_free(&dpages);
++ out:
++ return trap;
++}
+diff -Nur linux-2.6.31.5.orig/fs/aufs/dcsub.h linux-2.6.31.5/fs/aufs/dcsub.h
+--- linux-2.6.31.5.orig/fs/aufs/dcsub.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/dcsub.h 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,54 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. Okajima
++ *
++ * This program, aufs is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/*
++ * sub-routines for dentry cache
++ */
++
++#ifndef __AUFS_DCSUB_H__
++#define __AUFS_DCSUB_H__
++
++#ifdef __KERNEL__
++
++#include <linux/types.h>
++
++struct dentry;
++
++struct au_dpage {
++ int ndentry;
++ struct dentry **dentries;
++};
++
++struct au_dcsub_pages {
++ int ndpage;
++ struct au_dpage *dpages;
++};
++
++/* ---------------------------------------------------------------------- */
++
++int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp);
++void au_dpages_free(struct au_dcsub_pages *dpages);
++typedef int (*au_dpages_test)(struct dentry *dentry, void *arg);
++int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root,
++ au_dpages_test test, void *arg);
++int au_dcsub_pages_rev(struct au_dcsub_pages *dpages, struct dentry *dentry,
++ int do_include, au_dpages_test test, void *arg);
++struct dentry *au_test_subdir(struct dentry *d1, struct dentry *d2);
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_DCSUB_H__ */
+diff -Nur linux-2.6.31.5.orig/fs/aufs/debug.c linux-2.6.31.5/fs/aufs/debug.c
+--- linux-2.6.31.5.orig/fs/aufs/debug.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/debug.c 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,427 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. Okajima
++ *
++ * This program, aufs is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/*
++ * debug print functions
++ */
++
++#include <linux/module.h>
++#include <linux/vt_kern.h>
++#include "aufs.h"
++
++int aufs_debug;
++MODULE_PARM_DESC(debug, "debug print");
++module_param_named(debug, aufs_debug, int, S_IRUGO | S_IWUSR | S_IWGRP);
++
++char *au_plevel = KERN_DEBUG;
++#define dpri(fmt, arg...) do { \
++ if (au_debug_test()) \
++ printk("%s" fmt, au_plevel, ##arg); \
++} while (0)
++
++/* ---------------------------------------------------------------------- */
++
++void au_dpri_whlist(struct au_nhash *whlist)
++{
++ unsigned long ul, n;
++ struct hlist_head *head;
++ struct au_vdir_wh *tpos;
++ struct hlist_node *pos;
++
++ n = whlist->nh_num;
++ head = whlist->nh_head;
++ for (ul = 0; ul < n; ul++) {
++ 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);
++ head++;
++ }
++}
++
++void au_dpri_vdir(struct au_vdir *vdir)
++{
++ unsigned long ul;
++ union au_vdir_deblk_p p;
++ unsigned char *o;
++
++ if (!vdir || IS_ERR(vdir)) {
++ dpri("err %ld\n", PTR_ERR(vdir));
++ return;
++ }
++
++ dpri("deblk %u, nblk %lu, deblk %p, last{%lu, %p}, ver %lu\n",
++ vdir->vd_deblk_sz, vdir->vd_nblk, vdir->vd_deblk,
++ vdir->vd_last.ul, vdir->vd_last.p.deblk, vdir->vd_version);
++ for (ul = 0; ul < vdir->vd_nblk; ul++) {
++ p.deblk = vdir->vd_deblk[ul];
++ o = p.deblk;
++ dpri("[%lu]: %p\n", ul, o);
++ }
++}
++
++static int do_pri_inode(aufs_bindex_t bindex, struct inode *inode,
++ struct dentry *wh)
++{
++ char *n = NULL;
++ int l = 0;
++
++ if (!inode || IS_ERR(inode)) {
++ dpri("i%d: err %ld\n", bindex, PTR_ERR(inode));
++ return -1;
++ }
++
++ /* the type of i_blocks depends upon CONFIG_LSF */
++ BUILD_BUG_ON(sizeof(inode->i_blocks) != sizeof(unsigned long)
++ && sizeof(inode->i_blocks) != sizeof(u64));
++ if (wh) {
++ n = (void *)wh->d_name.name;
++ l = wh->d_name.len;
++ }
++
++ dpri("i%d: i%lu, %s, cnt %d, nl %u, 0%o, sz %llu, blk %llu,"
++ " ct %lld, np %lu, st 0x%lx, f 0x%x, g %x%s%.*s\n",
++ bindex,
++ inode->i_ino, inode->i_sb ? au_sbtype(inode->i_sb) : "??",
++ atomic_read(&inode->i_count), inode->i_nlink, inode->i_mode,
++ i_size_read(inode), (unsigned long long)inode->i_blocks,
++ (long long)timespec_to_ns(&inode->i_ctime) & 0x0ffff,
++ inode->i_mapping ? inode->i_mapping->nrpages : 0,
++ inode->i_state, inode->i_flags, inode->i_generation,
++ l ? ", wh " : "", l, n);
++ return 0;
++}
++
++void au_dpri_inode(struct inode *inode)
++{
++ struct au_iinfo *iinfo;
++ aufs_bindex_t bindex;
++ int err;
++
++ err = do_pri_inode(-1, inode, NULL);
++ if (err || !au_test_aufs(inode->i_sb))
++ return;
++
++ iinfo = au_ii(inode);
++ if (!iinfo)
++ return;
++ dpri("i-1: bstart %d, bend %d, gen %d\n",
++ iinfo->ii_bstart, iinfo->ii_bend, au_iigen(inode));
++ if (iinfo->ii_bstart < 0)
++ return;
++ for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; bindex++)
++ do_pri_inode(bindex, iinfo->ii_hinode[0 + bindex].hi_inode,
++ iinfo->ii_hinode[0 + bindex].hi_whdentry);
++}
++
++static int do_pri_dentry(aufs_bindex_t bindex, struct dentry *dentry)
++{
++ struct dentry *wh = NULL;
++
++ if (!dentry || IS_ERR(dentry)) {
++ dpri("d%d: err %ld\n", bindex, PTR_ERR(dentry));
++ return -1;
++ }
++ /* do not call dget_parent() here */
++ dpri("d%d: %.*s?/%.*s, %s, cnt %d, flags 0x%x\n",
++ bindex,
++ AuDLNPair(dentry->d_parent), AuDLNPair(dentry),
++ dentry->d_sb ? au_sbtype(dentry->d_sb) : "??",
++ atomic_read(&dentry->d_count), dentry->d_flags);
++ if (bindex >= 0 && dentry->d_inode && au_test_aufs(dentry->d_sb)) {
++ struct au_iinfo *iinfo = au_ii(dentry->d_inode);
++ if (iinfo)
++ wh = iinfo->ii_hinode[0 + bindex].hi_whdentry;
++ }
++ do_pri_inode(bindex, dentry->d_inode, wh);
++ return 0;
++}
++
++void au_dpri_dentry(struct dentry *dentry)
++{
++ struct au_dinfo *dinfo;
++ aufs_bindex_t bindex;
++ int err;
++
++ err = do_pri_dentry(-1, dentry);
++ if (err || !au_test_aufs(dentry->d_sb))
++ return;
++
++ dinfo = au_di(dentry);
++ if (!dinfo)
++ return;
++ dpri("d-1: bstart %d, bend %d, bwh %d, bdiropq %d, gen %d\n",
++ dinfo->di_bstart, dinfo->di_bend,
++ dinfo->di_bwh, dinfo->di_bdiropq, au_digen(dentry));
++ if (dinfo->di_bstart < 0)
++ return;
++ for (bindex = dinfo->di_bstart; bindex <= dinfo->di_bend; bindex++)
++ do_pri_dentry(bindex, dinfo->di_hdentry[0 + bindex].hd_dentry);
++}
++
++static int do_pri_file(aufs_bindex_t bindex, struct file *file)
++{
++ char a[32];
++
++ if (!file || IS_ERR(file)) {
++ dpri("f%d: err %ld\n", bindex, PTR_ERR(file));
++ return -1;
++ }
++ a[0] = 0;
++ if (bindex < 0
++ && file->f_dentry
++ && au_test_aufs(file->f_dentry->d_sb)
++ && au_fi(file))
++ snprintf(a, sizeof(a), ", mmapped %d", au_test_mmapped(file));
++ dpri("f%d: mode 0x%x, flags 0%o, cnt %ld, pos %llu%s\n",
++ bindex, file->f_mode, file->f_flags, (long)file_count(file),
++ file->f_pos, a);
++ if (file->f_dentry)
++ do_pri_dentry(bindex, file->f_dentry);
++ return 0;
++}
++
++void au_dpri_file(struct file *file)
++{
++ struct au_finfo *finfo;
++ aufs_bindex_t bindex;
++ int err;
++
++ err = do_pri_file(-1, file);
++ if (err || !file->f_dentry || !au_test_aufs(file->f_dentry->d_sb))
++ return;
++
++ finfo = au_fi(file);
++ if (!finfo)
++ return;
++ if (finfo->fi_bstart < 0)
++ return;
++ for (bindex = finfo->fi_bstart; bindex <= finfo->fi_bend; bindex++) {
++ struct au_hfile *hf;
++
++ hf = finfo->fi_hfile + bindex;
++ do_pri_file(bindex, hf ? hf->hf_file : NULL);
++ }
++}
++
++static int do_pri_br(aufs_bindex_t bindex, struct au_branch *br)
++{
++ struct vfsmount *mnt;
++ struct super_block *sb;
++
++ if (!br || IS_ERR(br))
++ goto out;
++ mnt = br->br_mnt;
++ if (!mnt || IS_ERR(mnt))
++ goto out;
++ sb = mnt->mnt_sb;
++ if (!sb || IS_ERR(sb))
++ goto out;
++
++ dpri("s%d: {perm 0x%x, cnt %d, wbr %p}, "
++ "%s, dev 0x%02x%02x, flags 0x%lx, cnt(BIAS) %d, active %d, "
++ "xino %d\n",
++ bindex, br->br_perm, atomic_read(&br->br_count), br->br_wbr,
++ au_sbtype(sb), MAJOR(sb->s_dev), MINOR(sb->s_dev),
++ sb->s_flags, sb->s_count - S_BIAS,
++ atomic_read(&sb->s_active), !!br->br_xino.xi_file);
++ return 0;
++
++ out:
++ dpri("s%d: err %ld\n", bindex, PTR_ERR(br));
++ return -1;
++}
++
++void au_dpri_sb(struct super_block *sb)
++{
++ struct au_sbinfo *sbinfo;
++ aufs_bindex_t bindex;
++ int err;
++ /* to reuduce stack size */
++ struct {
++ struct vfsmount mnt;
++ struct au_branch fake;
++ } *a;
++
++ /* this function can be called from magic sysrq */
++ a = kzalloc(sizeof(*a), GFP_ATOMIC);
++ if (unlikely(!a)) {
++ dpri("no memory\n");
++ return;
++ }
++
++ a->mnt.mnt_sb = sb;
++ a->fake.br_perm = 0;
++ a->fake.br_mnt = &a->mnt;
++ a->fake.br_xino.xi_file = NULL;
++ atomic_set(&a->fake.br_count, 0);
++ smp_mb(); /* atomic_set */
++ err = do_pri_br(-1, &a->fake);
++ kfree(a);
++ dpri("dev 0x%x\n", sb->s_dev);
++ if (err || !au_test_aufs(sb))
++ return;
++
++ sbinfo = au_sbi(sb);
++ if (!sbinfo)
++ return;
++ dpri("nw %d, gen %u, kobj %d\n",
++ atomic_read(&sbinfo->si_nowait.nw_len), sbinfo->si_generation,
++ atomic_read(&sbinfo->si_kobj.kref.refcount));
++ for (bindex = 0; bindex <= sbinfo->si_bend; bindex++)
++ do_pri_br(bindex, sbinfo->si_branch[0 + bindex]);
++}
++
++/* ---------------------------------------------------------------------- */
++
++void au_dbg_sleep_jiffy(int jiffy)
++{
++ while (jiffy)
++ jiffy = schedule_timeout_uninterruptible(jiffy);
++}
++
++void au_dbg_iattr(struct iattr *ia)
++{
++#define AuBit(name) if (ia->ia_valid & ATTR_ ## name) \
++ dpri(#name "\n")
++ AuBit(MODE);
++ AuBit(UID);
++ AuBit(GID);
++ AuBit(SIZE);
++ AuBit(ATIME);
++ AuBit(MTIME);
++ AuBit(CTIME);
++ AuBit(ATIME_SET);
++ AuBit(MTIME_SET);
++ AuBit(FORCE);
++ AuBit(ATTR_FLAG);
++ AuBit(KILL_SUID);
++ AuBit(KILL_SGID);
++ AuBit(FILE);
++ AuBit(KILL_PRIV);
++ AuBit(OPEN);
++ AuBit(TIMES_SET);
++#undef AuBit
++ dpri("ia_file %p\n", ia->ia_file);
++}
++
++/* ---------------------------------------------------------------------- */
++
++void au_dbg_verify_dir_parent(struct dentry *dentry, unsigned int sigen)
++{
++ struct dentry *parent;
++
++ parent = dget_parent(dentry);
++ AuDebugOn(!S_ISDIR(dentry->d_inode->i_mode)
++ || IS_ROOT(dentry)
++ || au_digen(parent) != sigen);
++ dput(parent);
++}
++
++void au_dbg_verify_nondir_parent(struct dentry *dentry, unsigned int sigen)
++{
++ struct dentry *parent;
++
++ parent = dget_parent(dentry);
++ AuDebugOn(S_ISDIR(dentry->d_inode->i_mode)
++ || au_digen(parent) != sigen);
++ dput(parent);
++}
++
++void au_dbg_verify_gen(struct dentry *parent, unsigned int sigen)
++{
++ int err, i, j;
++ struct au_dcsub_pages dpages;
++ struct au_dpage *dpage;
++ struct dentry **dentries;
++
++ err = au_dpages_init(&dpages, GFP_NOFS);
++ AuDebugOn(err);
++ err = au_dcsub_pages_rev(&dpages, parent, /*do_include*/1, NULL, NULL);
++ AuDebugOn(err);
++ for (i = dpages.ndpage - 1; !err && i >= 0; i--) {
++ dpage = dpages.dpages + i;
++ dentries = dpage->dentries;
++ for (j = dpage->ndentry - 1; !err && j >= 0; j--)
++ AuDebugOn(au_digen(dentries[j]) != sigen);
++ }
++ au_dpages_free(&dpages);
++}
++
++void au_dbg_verify_hf(struct au_finfo *finfo)
++{
++ struct au_hfile *hf;
++ aufs_bindex_t bend, bindex;
++
++ if (finfo->fi_bstart >= 0) {
++ bend = finfo->fi_bend;
++ for (bindex = finfo->fi_bstart; bindex <= bend; bindex++) {
++ hf = finfo->fi_hfile + bindex;
++ AuDebugOn(hf->hf_file || hf->hf_br);
++ }
++ }
++}
++
++void au_dbg_verify_kthread(void)
++{
++ if (au_test_wkq(current)) {
++ au_dbg_blocked();
++ BUG();
++ }
++}
++
++/* ---------------------------------------------------------------------- */
++
++void au_debug_sbinfo_init(struct au_sbinfo *sbinfo __maybe_unused)
++{
++#ifdef AuForceNoPlink
++ au_opt_clr(sbinfo->si_mntflags, PLINK);
++#endif
++#ifdef AuForceNoXino
++ au_opt_clr(sbinfo->si_mntflags, XINO);
++#endif
++#ifdef AuForceNoRefrof
++ au_opt_clr(sbinfo->si_mntflags, REFROF);
++#endif
++#ifdef AuForceHinotify
++ au_opt_set_udba(sbinfo->si_mntflags, UDBA_HINOTIFY);
++#endif
++}
++
++int __init au_debug_init(void)
++{
++ aufs_bindex_t bindex;
++ struct au_vdir_destr destr;
++
++ bindex = -1;
++ AuDebugOn(bindex >= 0);
++
++ destr.len = -1;
++ AuDebugOn(destr.len < NAME_MAX);
++
++#ifdef CONFIG_4KSTACKS
++ AuWarn("CONFIG_4KSTACKS is defined.\n");
++#endif
++
++#ifdef AuForceNoBrs
++ sysaufs_brs = 0;
++#endif
++
++ return 0;
++}
+diff -Nur linux-2.6.31.5.orig/fs/aufs/debug.h linux-2.6.31.5/fs/aufs/debug.h
+--- linux-2.6.31.5.orig/fs/aufs/debug.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/debug.h 2009-11-15 22:16:14.000000000 +0100
+@@ -0,0 +1,261 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. Okajima
++ *
++ * This program, aufs is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/*
++ * debug print functions
++ */
++
++#ifndef __AUFS_DEBUG_H__
++#define __AUFS_DEBUG_H__
++
++#ifdef __KERNEL__
++
++#include <asm/system.h>
++#include <linux/bug.h>
++/* #include <linux/err.h> */
++#include <linux/init.h>
++/* #include <linux/kernel.h> */
++#include <linux/delay.h>
++/* #include <linux/kd.h> */
++/* #include <linux/vt_kern.h> */
++#include <linux/sysrq.h>
++#include <linux/aufs_type.h>
++
++#ifdef CONFIG_AUFS_DEBUG
++#define AuDebugOn(a) BUG_ON(a)
++
++/* module parameter */
++extern int aufs_debug;
++static inline void au_debug(int n)
++{
++ aufs_debug = n;
++ smp_mb();
++}
++
++static inline int au_debug_test(void)
++{
++ return aufs_debug;
++}
++#else
++#define AuDebugOn(a) do {} while (0)
++#define au_debug() do {} while (0)
++static inline int au_debug_test(void)
++{
++ return 0;
++}
++#endif /* CONFIG_AUFS_DEBUG */
++
++/* ---------------------------------------------------------------------- */
++
++/* debug print */
++
++#define AuDpri(lvl, fmt, arg...) \
++ printk(lvl AUFS_NAME " %s:%d:%s[%d]: " fmt, \
++ __func__, __LINE__, current->comm, current->pid, ##arg)
++#define AuDbg(fmt, arg...) do { \
++ if (au_debug_test()) \
++ AuDpri(KERN_DEBUG, "DEBUG: " fmt, ##arg); \
++} while (0)
++#define AuLabel(l) AuDbg(#l "\n")
++#define AuInfo(fmt, arg...) AuDpri(KERN_INFO, fmt, ##arg)
++#define AuWarn(fmt, arg...) AuDpri(KERN_WARNING, fmt, ##arg)
++#define AuErr(fmt, arg...) AuDpri(KERN_ERR, fmt, ##arg)
++#define AuIOErr(fmt, arg...) AuErr("I/O Error, " fmt, ##arg)
++#define AuWarn1(fmt, arg...) do { \
++ static unsigned char _c; \
++ if (!_c++) \
++ AuWarn(fmt, ##arg); \
++} while (0)
++
++#define AuErr1(fmt, arg...) do { \
++ static unsigned char _c; \
++ if (!_c++) \
++ AuErr(fmt, ##arg); \
++} while (0)
++
++#define AuIOErr1(fmt, arg...) do { \
++ static unsigned char _c; \
++ if (!_c++) \
++ AuIOErr(fmt, ##arg); \
++} while (0)
++
++#define AuUnsupportMsg "This operation is not supported." \
++ " Please report this application to aufs-users ML."
++#define AuUnsupport(fmt, args...) do { \
++ AuErr(AuUnsupportMsg "\n" fmt, ##args); \
++ dump_stack(); \
++} while (0)
++
++#define AuTraceErr(e) do { \
++ if (unlikely((e) < 0)) \
++ AuDbg("err %d\n", (int)(e)); \
++} while (0)
++
++#define AuTraceErrPtr(p) do { \
++ if (IS_ERR(p)) \
++ AuDbg("err %ld\n", PTR_ERR(p)); \
++} while (0)
++
++/* dirty macros for debug print, use with "%.*s" and caution */
++#define AuLNPair(qstr) (qstr)->len, (qstr)->name
++#define AuDLNPair(d) AuLNPair(&(d)->d_name)
++
++/* ---------------------------------------------------------------------- */
++
++struct au_sbinfo;
++struct au_finfo;
++struct dentry;
++#ifdef CONFIG_AUFS_DEBUG
++extern char *au_plevel;
++struct au_nhash;
++void au_dpri_whlist(struct au_nhash *whlist);
++struct au_vdir;
++void au_dpri_vdir(struct au_vdir *vdir);
++struct inode;
++void au_dpri_inode(struct inode *inode);
++void au_dpri_dentry(struct dentry *dentry);
++struct file;
++void au_dpri_file(struct file *filp);
++struct super_block;
++void au_dpri_sb(struct super_block *sb);
++
++void au_dbg_sleep_jiffy(int jiffy);
++struct iattr;
++void au_dbg_iattr(struct iattr *ia);
++
++void au_dbg_verify_dir_parent(struct dentry *dentry, unsigned int sigen);
++void au_dbg_verify_nondir_parent(struct dentry *dentry, unsigned int sigen);
++void au_dbg_verify_gen(struct dentry *parent, unsigned int sigen);
++void au_dbg_verify_hf(struct au_finfo *finfo);
++void au_dbg_verify_kthread(void);
++
++int __init au_debug_init(void);
++void au_debug_sbinfo_init(struct au_sbinfo *sbinfo);
++#define AuDbgWhlist(w) do { \
++ AuDbg(#w "\n"); \
++ au_dpri_whlist(w); \
++} while (0)
++
++#define AuDbgVdir(v) do { \
++ AuDbg(#v "\n"); \
++ au_dpri_vdir(v); \
++} while (0)
++
++#define AuDbgInode(i) do { \
++ AuDbg(#i "\n"); \
++ au_dpri_inode(i); \
++} while (0)
++
++#define AuDbgDentry(d) do { \
++ AuDbg(#d "\n"); \
++ au_dpri_dentry(d); \
++} while (0)
++
++#define AuDbgFile(f) do { \
++ AuDbg(#f "\n"); \
++ au_dpri_file(f); \
++} while (0)
++
++#define AuDbgSb(sb) do { \
++ AuDbg(#sb "\n"); \
++ au_dpri_sb(sb); \
++} while (0)
++
++#define AuDbgSleep(sec) do { \
++ AuDbg("sleep %d sec\n", sec); \
++ ssleep(sec); \
++} while (0)
++
++#define AuDbgSleepJiffy(jiffy) do { \
++ AuDbg("sleep %d jiffies\n", jiffy); \
++ au_dbg_sleep_jiffy(jiffy); \
++} while (0)
++
++#define AuDbgIAttr(ia) do { \
++ AuDbg("ia_valid 0x%x\n", (ia)->ia_valid); \
++ au_dbg_iattr(ia); \
++} while (0)
++#else
++static inline void au_dbg_verify_dir_parent(struct dentry *dentry,
++ unsigned int sigen)
++{
++ /* empty */
++}
++static inline void au_dbg_verify_nondir_parent(struct dentry *dentry,
++ unsigned int sigen)
++{
++ /* empty */
++}
++static inline void au_dbg_verify_gen(struct dentry *parent, unsigned int sigen)
++{
++ /* empty */
++}
++static inline void au_dbg_verify_hf(struct au_finfo *finfo)
++{
++ /* empty */
++}
++static inline void au_dbg_verify_kthread(void)
++{
++ /* empty */
++}
++
++static inline int au_debug_init(void)
++{
++ return 0;
++}
++static inline void au_debug_sbinfo_init(struct au_sbinfo *sbinfo)
++{
++ /* empty */
++}
++#define AuDbgWhlist(w) do {} while (0)
++#define AuDbgVdir(v) do {} while (0)
++#define AuDbgInode(i) do {} while (0)
++#define AuDbgDentry(d) do {} while (0)
++#define AuDbgFile(f) do {} while (0)
++#define AuDbgSb(sb) do {} while (0)
++#define AuDbgSleep(sec) do {} while (0)
++#define AuDbgSleepJiffy(jiffy) do {} while (0)
++#define AuDbgIAttr(ia) do {} while (0)
++#endif /* CONFIG_AUFS_DEBUG */
++
++/* ---------------------------------------------------------------------- */
++
++#ifdef CONFIG_AUFS_MAGIC_SYSRQ
++int __init au_sysrq_init(void);
++void au_sysrq_fin(void);
++
++#ifdef CONFIG_HW_CONSOLE
++#define au_dbg_blocked() do { \
++ WARN_ON(1); \
++ handle_sysrq('w', vc_cons[fg_console].d->vc_tty); \
++} while (0)
++#else
++#define au_dbg_blocked() do {} while (0)
++#endif
++
++#else
++static inline int au_sysrq_init(void)
++{
++ return 0;
++}
++#define au_sysrq_fin() do {} while (0)
++#define au_dbg_blocked() do {} while (0)
++#endif /* CONFIG_AUFS_MAGIC_SYSRQ */
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_DEBUG_H__ */
+diff -Nur linux-2.6.31.5.orig/fs/aufs/dentry.c linux-2.6.31.5/fs/aufs/dentry.c
+--- linux-2.6.31.5.orig/fs/aufs/dentry.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/dentry.c 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,880 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. Okajima
++ *
++ * This program, aufs is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/*
++ * lookup and dentry operations
++ */
++
++#include <linux/namei.h>
++#include "aufs.h"
++
++static void au_h_nd(struct nameidata *h_nd, struct nameidata *nd)
++{
++ if (nd) {
++ *h_nd = *nd;
++
++ /*
++ * gave up supporting LOOKUP_CREATE/OPEN for lower fs,
++ * due to whiteout and branch permission.
++ */
++ h_nd->flags &= ~(/*LOOKUP_PARENT |*/ LOOKUP_OPEN | LOOKUP_CREATE
++ | LOOKUP_FOLLOW);
++ /* unnecessary? */
++ h_nd->intent.open.file = NULL;
++ } else
++ memset(h_nd, 0, sizeof(*h_nd));
++}
++
++struct au_lkup_one_args {
++ struct dentry **errp;
++ struct qstr *name;
++ struct dentry *h_parent;
++ struct au_branch *br;
++ struct nameidata *nd;
++};
++
++struct dentry *au_lkup_one(struct qstr *name, struct dentry *h_parent,
++ struct au_branch *br, struct nameidata *nd)
++{
++ struct dentry *h_dentry;
++ int err;
++ struct nameidata h_nd;
++
++ if (au_test_fs_null_nd(h_parent->d_sb))
++ return vfsub_lookup_one_len(name->name, h_parent, name->len);
++
++ au_h_nd(&h_nd, nd);
++ h_nd.path.dentry = h_parent;
++ h_nd.path.mnt = br->br_mnt;
++
++ err = __lookup_one_len(name->name, &h_nd.last, NULL, name->len);
++ h_dentry = ERR_PTR(err);
++ if (!err) {
++ path_get(&h_nd.path);
++ h_dentry = vfsub_lookup_hash(&h_nd);
++ path_put(&h_nd.path);
++ }
++
++ return h_dentry;
++}
++
++static void au_call_lkup_one(void *args)
++{
++ struct au_lkup_one_args *a = args;
++ *a->errp = au_lkup_one(a->name, a->h_parent, a->br, a->nd);
++}
++
++#define AuLkup_ALLOW_NEG 1
++#define au_ftest_lkup(flags, name) ((flags) & AuLkup_##name)
++#define au_fset_lkup(flags, name) { (flags) |= AuLkup_##name; }
++#define au_fclr_lkup(flags, name) { (flags) &= ~AuLkup_##name; }
++
++struct au_do_lookup_args {
++ unsigned int flags;
++ mode_t type;
++ struct nameidata *nd;
++};
++
++/*
++ * returns positive/negative dentry, NULL or an error.
++ * NULL means whiteout-ed or not-found.
++ */
++static struct dentry*
++au_do_lookup(struct dentry *h_parent, struct dentry *dentry,
++ aufs_bindex_t bindex, struct qstr *wh_name,
++ struct au_do_lookup_args *args)
++{
++ struct dentry *h_dentry;
++ struct inode *h_inode, *inode;
++ struct qstr *name;
++ struct au_branch *br;
++ int wh_found, opq;
++ unsigned char wh_able;
++ const unsigned char allow_neg = !!au_ftest_lkup(args->flags, ALLOW_NEG);
++
++ name = &dentry->d_name;
++ wh_found = 0;
++ br = au_sbr(dentry->d_sb, bindex);
++ wh_able = !!au_br_whable(br->br_perm);
++ if (wh_able)
++ wh_found = au_wh_test(h_parent, wh_name, br, /*try_sio*/0);
++ h_dentry = ERR_PTR(wh_found);
++ if (!wh_found)
++ goto real_lookup;
++ if (unlikely(wh_found < 0))
++ goto out;
++
++ /* We found a whiteout */
++ /* au_set_dbend(dentry, bindex); */
++ au_set_dbwh(dentry, bindex);
++ if (!allow_neg)
++ return NULL; /* success */
++
++ real_lookup:
++ h_dentry = au_lkup_one(name, h_parent, br, args->nd);
++ if (IS_ERR(h_dentry))
++ goto out;
++
++ h_inode = h_dentry->d_inode;
++ if (!h_inode) {
++ if (!allow_neg)
++ goto out_neg;
++ } else if (wh_found
++ || (args->type && args->type != (h_inode->i_mode & S_IFMT)))
++ goto out_neg;
++
++ if (au_dbend(dentry) <= bindex)
++ au_set_dbend(dentry, bindex);
++ if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry))
++ au_set_dbstart(dentry, bindex);
++ au_set_h_dptr(dentry, bindex, h_dentry);
++
++ inode = dentry->d_inode;
++ if (!h_inode || !S_ISDIR(h_inode->i_mode) || !wh_able
++ || (inode && !S_ISDIR(inode->i_mode)))
++ goto out; /* success */
++
++ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);
++ opq = au_diropq_test(h_dentry, br);
++ mutex_unlock(&h_inode->i_mutex);
++ if (opq > 0)
++ au_set_dbdiropq(dentry, bindex);
++ else if (unlikely(opq < 0)) {
++ au_set_h_dptr(dentry, bindex, NULL);
++ h_dentry = ERR_PTR(opq);
++ }
++ goto out;
++
++ out_neg:
++ dput(h_dentry);
++ h_dentry = NULL;
++ out:
++ return h_dentry;
++}
++
++static int au_test_shwh(struct super_block *sb, const struct qstr *name)
++{
++ if (unlikely(!au_opt_test(au_mntflags(sb), SHWH)
++ && !strncmp(name->name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)))
++ return -EPERM;
++ return 0;
++}
++
++/*
++ * returns the number of lower positive dentries,
++ * otherwise an error.
++ * can be called at unlinking with @type is zero.
++ */
++int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type,
++ struct nameidata *nd)
++{
++ int npositive, err;
++ aufs_bindex_t bindex, btail, bdiropq;
++ unsigned char isdir;
++ struct qstr whname;
++ struct au_do_lookup_args args = {
++ .flags = 0,
++ .type = type,
++ .nd = nd
++ };
++ const struct qstr *name = &dentry->d_name;
++ struct dentry *parent;
++ struct inode *inode;
++
++ parent = dget_parent(dentry);
++ err = au_test_shwh(dentry->d_sb, name);
++ if (unlikely(err))
++ goto out;
++
++ err = au_wh_name_alloc(&whname, name);
++ if (unlikely(err))
++ goto out;
++
++ inode = dentry->d_inode;
++ isdir = !!(inode && S_ISDIR(inode->i_mode));
++ if (!type)
++ au_fset_lkup(args.flags, ALLOW_NEG);
++
++ npositive = 0;
++ btail = au_dbtaildir(parent);
++ for (bindex = bstart; bindex <= btail; bindex++) {
++ struct dentry *h_parent, *h_dentry;
++ struct inode *h_inode, *h_dir;
++
++ h_dentry = au_h_dptr(dentry, bindex);
++ if (h_dentry) {
++ if (h_dentry->d_inode)
++ npositive++;
++ if (type != S_IFDIR)
++ break;
++ continue;
++ }
++ h_parent = au_h_dptr(parent, bindex);
++ if (!h_parent)
++ continue;
++ h_dir = h_parent->d_inode;
++ if (!h_dir || !S_ISDIR(h_dir->i_mode))
++ continue;
++
++ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT);
++ h_dentry = au_do_lookup(h_parent, dentry, bindex, &whname,
++ &args);
++ mutex_unlock(&h_dir->i_mutex);
++ err = PTR_ERR(h_dentry);
++ if (IS_ERR(h_dentry))
++ goto out_wh;
++ au_fclr_lkup(args.flags, ALLOW_NEG);
++
++ if (au_dbwh(dentry) >= 0)
++ break;
++ if (!h_dentry)
++ continue;
++ h_inode = h_dentry->d_inode;
++ if (!h_inode)
++ continue;
++ npositive++;
++ if (!args.type)
++ args.type = h_inode->i_mode & S_IFMT;
++ if (args.type != S_IFDIR)
++ break;
++ else if (isdir) {
++ /* the type of lower may be different */
++ bdiropq = au_dbdiropq(dentry);
++ if (bdiropq >= 0 && bdiropq <= bindex)
++ break;
++ }
++ }
++
++ if (npositive) {
++ AuLabel(positive);
++ au_update_dbstart(dentry);
++ }
++ err = npositive;
++ if (unlikely(!au_opt_test(au_mntflags(dentry->d_sb), UDBA_NONE)
++ && au_dbstart(dentry) < 0))
++ /* both of real entry and whiteout found */
++ err = -EIO;
++
++ out_wh:
++ kfree(whname.name);
++ out:
++ dput(parent);
++ return err;
++}
++
++struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent,
++ struct au_branch *br)
++{
++ struct dentry *dentry;
++ int wkq_err;
++
++ if (!au_test_h_perm_sio(parent->d_inode, MAY_EXEC))
++ dentry = au_lkup_one(name, parent, br, /*nd*/NULL);
++ else {
++ struct au_lkup_one_args args = {
++ .errp = &dentry,
++ .name = name,
++ .h_parent = parent,
++ .br = br,
++ .nd = NULL
++ };
++
++ wkq_err = au_wkq_wait(au_call_lkup_one, &args);
++ if (unlikely(wkq_err))
++ dentry = ERR_PTR(wkq_err);
++ }
++
++ return dentry;
++}
++
++/*
++ * lookup @dentry on @bindex which should be negative.
++ */
++int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex)
++{
++ int err;
++ struct dentry *parent, *h_parent, *h_dentry;
++ struct qstr *name;
++
++ name = &dentry->d_name;
++ parent = dget_parent(dentry);
++ h_parent = au_h_dptr(parent, bindex);
++ h_dentry = au_sio_lkup_one(name, h_parent,
++ au_sbr(dentry->d_sb, bindex));
++ err = PTR_ERR(h_dentry);
++ if (IS_ERR(h_dentry))
++ goto out;
++ if (unlikely(h_dentry->d_inode)) {
++ err = -EIO;
++ AuIOErr("b%d %.*s should be negative.\n",
++ bindex, AuDLNPair(h_dentry));
++ dput(h_dentry);
++ goto out;
++ }
++
++ if (bindex < au_dbstart(dentry))
++ au_set_dbstart(dentry, bindex);
++ if (au_dbend(dentry) < bindex)
++ au_set_dbend(dentry, bindex);
++ au_set_h_dptr(dentry, bindex, h_dentry);
++ err = 0;
++
++ out:
++ dput(parent);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++/* subset of struct inode */
++struct au_iattr {
++ unsigned long i_ino;
++ /* unsigned int i_nlink; */
++ uid_t i_uid;
++ gid_t i_gid;
++ u64 i_version;
++/*
++ loff_t i_size;
++ blkcnt_t i_blocks;
++*/
++ umode_t i_mode;
++};
++
++static void au_iattr_save(struct au_iattr *ia, struct inode *h_inode)
++{
++ ia->i_ino = h_inode->i_ino;
++ /* ia->i_nlink = h_inode->i_nlink; */
++ ia->i_uid = h_inode->i_uid;
++ ia->i_gid = h_inode->i_gid;
++ ia->i_version = h_inode->i_version;
++/*
++ ia->i_size = h_inode->i_size;
++ ia->i_blocks = h_inode->i_blocks;
++*/
++ ia->i_mode = (h_inode->i_mode & S_IFMT);
++}
++
++static int au_iattr_test(struct au_iattr *ia, struct inode *h_inode)
++{
++ return ia->i_ino != h_inode->i_ino
++ /* || ia->i_nlink != h_inode->i_nlink */
++ || ia->i_uid != h_inode->i_uid
++ || ia->i_gid != h_inode->i_gid
++ || ia->i_version != h_inode->i_version
++/*
++ || ia->i_size != h_inode->i_size
++ || ia->i_blocks != h_inode->i_blocks
++*/
++ || ia->i_mode != (h_inode->i_mode & S_IFMT);
++}
++
++static int au_h_verify_dentry(struct dentry *h_dentry, struct dentry *h_parent,
++ struct au_branch *br)
++{
++ int err;
++ struct au_iattr ia;
++ struct inode *h_inode;
++ struct dentry *h_d;
++ struct super_block *h_sb;
++
++ err = 0;
++ memset(&ia, -1, sizeof(ia));
++ h_sb = h_dentry->d_sb;
++ h_inode = h_dentry->d_inode;
++ if (h_inode)
++ au_iattr_save(&ia, h_inode);
++ else if (au_test_nfs(h_sb) || au_test_fuse(h_sb))
++ /* nfs d_revalidate may return 0 for negative dentry */
++ /* fuse d_revalidate always return 0 for negative dentry */
++ goto out;
++
++ /* main purpose is namei.c:cached_lookup() and d_revalidate */
++ h_d = au_lkup_one(&h_dentry->d_name, h_parent, br, /*nd*/NULL);
++ err = PTR_ERR(h_d);
++ if (IS_ERR(h_d))
++ goto out;
++
++ err = 0;
++ if (unlikely(h_d != h_dentry
++ || h_d->d_inode != h_inode
++ || (h_inode && au_iattr_test(&ia, h_inode))))
++ err = au_busy_or_stale();
++ dput(h_d);
++
++ out:
++ AuTraceErr(err);
++ return err;
++}
++
++int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir,
++ struct dentry *h_parent, struct au_branch *br)
++{
++ int err;
++
++ err = 0;
++ if (udba == AuOpt_UDBA_REVAL) {
++ IMustLock(h_dir);
++ err = (h_dentry->d_parent->d_inode != h_dir);
++ } else if (udba == AuOpt_UDBA_HINOTIFY)
++ err = au_h_verify_dentry(h_dentry, h_parent, br);
++
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static void au_do_refresh_hdentry(struct au_hdentry *p, struct au_dinfo *dinfo,
++ struct dentry *parent)
++{
++ struct dentry *h_d, *h_dp;
++ struct au_hdentry tmp, *q;
++ struct super_block *sb;
++ aufs_bindex_t new_bindex, bindex, bend, bwh, bdiropq;
++
++ AuRwMustWriteLock(&dinfo->di_rwsem);
++
++ bend = dinfo->di_bend;
++ bwh = dinfo->di_bwh;
++ bdiropq = dinfo->di_bdiropq;
++ for (bindex = dinfo->di_bstart; bindex <= bend; bindex++, p++) {
++ h_d = p->hd_dentry;
++ if (!h_d)
++ continue;
++
++ h_dp = dget_parent(h_d);
++ if (h_dp == au_h_dptr(parent, bindex)) {
++ dput(h_dp);
++ continue;
++ }
++
++ new_bindex = au_find_dbindex(parent, h_dp);
++ dput(h_dp);
++ if (dinfo->di_bwh == bindex)
++ bwh = new_bindex;
++ if (dinfo->di_bdiropq == bindex)
++ bdiropq = new_bindex;
++ if (new_bindex < 0) {
++ au_hdput(p);
++ p->hd_dentry = NULL;
++ continue;
++ }
++
++ /* swap two lower dentries, and loop again */
++ q = dinfo->di_hdentry + new_bindex;
++ tmp = *q;
++ *q = *p;
++ *p = tmp;
++ if (tmp.hd_dentry) {
++ bindex--;
++ p--;
++ }
++ }
++
++ sb = parent->d_sb;
++ dinfo->di_bwh = -1;
++ if (bwh >= 0 && bwh <= au_sbend(sb) && au_sbr_whable(sb, bwh))
++ dinfo->di_bwh = bwh;
++
++ dinfo->di_bdiropq = -1;
++ if (bdiropq >= 0
++ && bdiropq <= au_sbend(sb)
++ && au_sbr_whable(sb, bdiropq))
++ dinfo->di_bdiropq = bdiropq;
++
++ bend = au_dbend(parent);
++ p = dinfo->di_hdentry;
++ for (bindex = 0; bindex <= bend; bindex++, p++)
++ if (p->hd_dentry) {
++ dinfo->di_bstart = bindex;
++ break;
++ }
++
++ p = dinfo->di_hdentry + bend;
++ for (bindex = bend; bindex >= 0; bindex--, p--)
++ if (p->hd_dentry) {
++ dinfo->di_bend = bindex;
++ break;
++ }
++}
++
++/*
++ * returns the number of found lower positive dentries,
++ * otherwise an error.
++ */
++int au_refresh_hdentry(struct dentry *dentry, mode_t type)
++{
++ int npositive, err;
++ unsigned int sigen;
++ aufs_bindex_t bstart;
++ struct au_dinfo *dinfo;
++ struct super_block *sb;
++ struct dentry *parent;
++
++ DiMustWriteLock(dentry);
++
++ sb = dentry->d_sb;
++ AuDebugOn(IS_ROOT(dentry));
++ sigen = au_sigen(sb);
++ parent = dget_parent(dentry);
++ AuDebugOn(au_digen(parent) != sigen
++ || au_iigen(parent->d_inode) != sigen);
++
++ dinfo = au_di(dentry);
++ err = au_di_realloc(dinfo, au_sbend(sb) + 1);
++ npositive = err;
++ if (unlikely(err))
++ goto out;
++ au_do_refresh_hdentry(dinfo->di_hdentry + dinfo->di_bstart, dinfo,
++ parent);
++
++ npositive = 0;
++ bstart = au_dbstart(parent);
++ if (type != S_IFDIR && dinfo->di_bstart == bstart)
++ goto out_dgen; /* success */
++
++ npositive = au_lkup_dentry(dentry, bstart, type, /*nd*/NULL);
++ if (npositive < 0)
++ goto out;
++ if (dinfo->di_bwh >= 0 && dinfo->di_bwh <= dinfo->di_bstart)
++ d_drop(dentry);
++
++ out_dgen:
++ au_update_digen(dentry);
++ out:
++ dput(parent);
++ AuTraceErr(npositive);
++ return npositive;
++}
++
++static noinline_for_stack
++int au_do_h_d_reval(struct dentry *h_dentry, struct nameidata *nd,
++ struct dentry *dentry, aufs_bindex_t bindex)
++{
++ int err, valid;
++ int (*reval)(struct dentry *, struct nameidata *);
++
++ err = 0;
++ reval = NULL;
++ if (h_dentry->d_op)
++ reval = h_dentry->d_op->d_revalidate;
++ if (!reval)
++ goto out;
++
++ AuDbg("b%d\n", bindex);
++ if (au_test_fs_null_nd(h_dentry->d_sb))
++ /* it may return tri-state */
++ valid = reval(h_dentry, NULL);
++ else {
++ struct nameidata h_nd;
++ int locked;
++ struct dentry *parent;
++
++ au_h_nd(&h_nd, nd);
++ parent = nd->path.dentry;
++ locked = (nd && nd->path.dentry != dentry);
++ if (locked)
++ di_read_lock_parent(parent, AuLock_IR);
++ BUG_ON(bindex > au_dbend(parent));
++ h_nd.path.dentry = au_h_dptr(parent, bindex);
++ BUG_ON(!h_nd.path.dentry);
++ h_nd.path.mnt = au_sbr(parent->d_sb, bindex)->br_mnt;
++ path_get(&h_nd.path);
++ valid = reval(h_dentry, &h_nd);
++ path_put(&h_nd.path);
++ if (locked)
++ di_read_unlock(parent, AuLock_IR);
++ }
++
++ if (unlikely(valid < 0))
++ err = valid;
++ else if (!valid)
++ err = -EINVAL;
++
++ out:
++ AuTraceErr(err);
++ return err;
++}
++
++/* todo: remove this */
++static int h_d_revalidate(struct dentry *dentry, struct inode *inode,
++ struct nameidata *nd, int do_udba)
++{
++ int err;
++ umode_t mode, h_mode;
++ aufs_bindex_t bindex, btail, bstart, ibs, ibe;
++ unsigned char plus, unhashed, is_root, h_plus;
++ struct inode *first, *h_inode, *h_cached_inode;
++ struct dentry *h_dentry;
++ struct qstr *name, *h_name;
++
++ err = 0;
++ plus = 0;
++ mode = 0;
++ first = NULL;
++ ibs = -1;
++ ibe = -1;
++ unhashed = !!d_unhashed(dentry);
++ is_root = !!IS_ROOT(dentry);
++ name = &dentry->d_name;
++
++ /*
++ * Theoretically, REVAL test should be unnecessary in case of INOTIFY.
++ * But inotify doesn't fire some necessary events,
++ * IN_ATTRIB for atime/nlink/pageio
++ * IN_DELETE for NFS dentry
++ * Let's do REVAL test too.
++ */
++ if (do_udba && inode) {
++ mode = (inode->i_mode & S_IFMT);
++ plus = (inode->i_nlink > 0);
++ first = au_h_iptr(inode, au_ibstart(inode));
++ ibs = au_ibstart(inode);
++ ibe = au_ibend(inode);
++ }
++
++ bstart = au_dbstart(dentry);
++ btail = bstart;
++ if (inode && S_ISDIR(inode->i_mode))
++ btail = au_dbtaildir(dentry);
++ for (bindex = bstart; bindex <= btail; bindex++) {
++ h_dentry = au_h_dptr(dentry, bindex);
++ if (!h_dentry)
++ continue;
++
++ AuDbg("b%d, %.*s\n", bindex, AuDLNPair(h_dentry));
++ h_name = &h_dentry->d_name;
++ if (unlikely(do_udba
++ && !is_root
++ && (unhashed != !!d_unhashed(h_dentry)
++ || name->len != h_name->len
++ || memcmp(name->name, h_name->name, name->len))
++ )) {
++ AuDbg("unhash 0x%x 0x%x, %.*s %.*s\n",
++ unhashed, d_unhashed(h_dentry),
++ AuDLNPair(dentry), AuDLNPair(h_dentry));
++ goto err;
++ }
++
++ err = au_do_h_d_reval(h_dentry, nd, dentry, bindex);
++ if (unlikely(err))
++ /* do not goto err, to keep the errno */
++ break;
++
++ /* todo: plink too? */
++ if (!do_udba)
++ continue;
++
++ /* UDBA tests */
++ h_inode = h_dentry->d_inode;
++ if (unlikely(!!inode != !!h_inode))
++ goto err;
++
++ h_plus = plus;
++ h_mode = mode;
++ h_cached_inode = h_inode;
++ if (h_inode) {
++ h_mode = (h_inode->i_mode & S_IFMT);
++ h_plus = (h_inode->i_nlink > 0);
++ }
++ if (inode && ibs <= bindex && bindex <= ibe)
++ h_cached_inode = au_h_iptr(inode, bindex);
++
++ if (unlikely(plus != h_plus
++ || mode != h_mode
++ || h_cached_inode != h_inode))
++ goto err;
++ continue;
++
++ err:
++ err = -EINVAL;
++ break;
++ }
++
++ return err;
++}
++
++static int simple_reval_dpath(struct dentry *dentry, unsigned int sigen)
++{
++ int err;
++ struct dentry *parent;
++ struct inode *inode;
++
++ inode = dentry->d_inode;
++ if (au_digen(dentry) == sigen && au_iigen(inode) == sigen)
++ return 0;
++
++ parent = dget_parent(dentry);
++ di_read_lock_parent(parent, AuLock_IR);
++ AuDebugOn(au_digen(parent) != sigen
++ || au_iigen(parent->d_inode) != sigen);
++ au_dbg_verify_gen(parent, sigen);
++
++ /* returns a number of positive dentries */
++ err = au_refresh_hdentry(dentry, inode->i_mode & S_IFMT);
++ if (err >= 0)
++ err = au_refresh_hinode(inode, dentry);
++
++ di_read_unlock(parent, AuLock_IR);
++ dput(parent);
++ return err;
++}
++
++int au_reval_dpath(struct dentry *dentry, unsigned int sigen)
++{
++ int err;
++ struct dentry *d, *parent;
++ struct inode *inode;
++
++ if (!au_ftest_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIRS))
++ return simple_reval_dpath(dentry, sigen);
++
++ /* slow loop, keep it simple and stupid */
++ /* cf: au_cpup_dirs() */
++ err = 0;
++ parent = NULL;
++ while (au_digen(dentry) != sigen
++ || au_iigen(dentry->d_inode) != sigen) {
++ d = dentry;
++ while (1) {
++ dput(parent);
++ parent = dget_parent(d);
++ if (au_digen(parent) == sigen
++ && au_iigen(parent->d_inode) == sigen)
++ break;
++ d = parent;
++ }
++
++ inode = d->d_inode;
++ if (d != dentry)
++ di_write_lock_child(d);
++
++ /* someone might update our dentry while we were sleeping */
++ if (au_digen(d) != sigen || au_iigen(d->d_inode) != sigen) {
++ di_read_lock_parent(parent, AuLock_IR);
++ /* returns a number of positive dentries */
++ err = au_refresh_hdentry(d, inode->i_mode & S_IFMT);
++ if (err >= 0)
++ err = au_refresh_hinode(inode, d);
++ di_read_unlock(parent, AuLock_IR);
++ }
++
++ if (d != dentry)
++ di_write_unlock(d);
++ dput(parent);
++ if (unlikely(err))
++ break;
++ }
++
++ return err;
++}
++
++/*
++ * if valid returns 1, otherwise 0.
++ */
++static int aufs_d_revalidate(struct dentry *dentry, struct nameidata *nd)
++{
++ int valid, err;
++ unsigned int sigen;
++ unsigned char do_udba;
++ struct super_block *sb;
++ struct inode *inode;
++
++ err = -EINVAL;
++ sb = dentry->d_sb;
++ inode = dentry->d_inode;
++ aufs_read_lock(dentry, AuLock_FLUSH | AuLock_DW);
++ sigen = au_sigen(sb);
++ if (au_digen(dentry) != sigen) {
++ AuDebugOn(IS_ROOT(dentry));
++ if (inode)
++ err = au_reval_dpath(dentry, sigen);
++ if (unlikely(err))
++ goto out_dgrade;
++ AuDebugOn(au_digen(dentry) != sigen);
++ }
++ if (inode && au_iigen(inode) != sigen) {
++ AuDebugOn(IS_ROOT(dentry));
++ err = au_refresh_hinode(inode, dentry);
++ if (unlikely(err))
++ goto out_dgrade;
++ AuDebugOn(au_iigen(inode) != sigen);
++ }
++ di_downgrade_lock(dentry, AuLock_IR);
++
++ AuDebugOn(au_digen(dentry) != sigen);
++ AuDebugOn(inode && au_iigen(inode) != sigen);
++ err = -EINVAL;
++ do_udba = !au_opt_test(au_mntflags(sb), UDBA_NONE);
++ if (do_udba && inode) {
++ aufs_bindex_t bstart = au_ibstart(inode);
++
++ if (bstart >= 0
++ && au_test_higen(inode, au_h_iptr(inode, bstart)))
++ goto out;
++ }
++
++ err = h_d_revalidate(dentry, inode, nd, do_udba);
++ if (unlikely(!err && do_udba && au_dbstart(dentry) < 0))
++ /* both of real entry and whiteout found */
++ err = -EIO;
++ goto out;
++
++ out_dgrade:
++ di_downgrade_lock(dentry, AuLock_IR);
++ out:
++ au_store_oflag(nd, inode);
++ aufs_read_unlock(dentry, AuLock_IR);
++ AuTraceErr(err);
++ valid = !err;
++ if (!valid)
++ AuDbg("%.*s invalid\n", AuDLNPair(dentry));
++ return valid;
++}
++
++static void aufs_d_release(struct dentry *dentry)
++{
++ struct au_dinfo *dinfo;
++ aufs_bindex_t bend, bindex;
++
++ dinfo = dentry->d_fsdata;
++ if (!dinfo)
++ return;
++
++ /* dentry may not be revalidated */
++ bindex = dinfo->di_bstart;
++ if (bindex >= 0) {
++ struct au_hdentry *p;
++
++ bend = dinfo->di_bend;
++ p = dinfo->di_hdentry + bindex;
++ while (bindex++ <= bend) {
++ if (p->hd_dentry)
++ au_hdput(p);
++ p++;
++ }
++ }
++ kfree(dinfo->di_hdentry);
++ AuRwDestroy(&dinfo->di_rwsem);
++ au_cache_free_dinfo(dinfo);
++ au_hin_di_reinit(dentry);
++}
++
++struct dentry_operations aufs_dop = {
++ .d_revalidate = aufs_d_revalidate,
++ .d_release = aufs_d_release
++};
+diff -Nur linux-2.6.31.5.orig/fs/aufs/dentry.h linux-2.6.31.5/fs/aufs/dentry.h
+--- linux-2.6.31.5.orig/fs/aufs/dentry.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/dentry.h 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,231 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. Okajima
++ *
++ * This program, aufs is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/*
++ * lookup and dentry operations
++ */
++
++#ifndef __AUFS_DENTRY_H__
++#define __AUFS_DENTRY_H__
++
++#ifdef __KERNEL__
++
++#include <linux/dcache.h>
++#include <linux/aufs_type.h>
++#include "rwsem.h"
++
++/* make a single member structure for future use */
++/* todo: remove this structure */
++struct au_hdentry {
++ struct dentry *hd_dentry;
++};
++
++struct au_dinfo {
++ atomic_t di_generation;
++
++ struct au_rwsem di_rwsem;
++ aufs_bindex_t di_bstart, di_bend, di_bwh, di_bdiropq;
++ struct au_hdentry *di_hdentry;
++};
++
++/* ---------------------------------------------------------------------- */
++
++/* dentry.c */
++extern struct dentry_operations aufs_dop;
++struct au_branch;
++struct dentry *au_lkup_one(struct qstr *name, struct dentry *h_parent,
++ struct au_branch *br, struct nameidata *nd);
++struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent,
++ struct au_branch *br);
++int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir,
++ struct dentry *h_parent, struct au_branch *br);
++
++int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type,
++ struct nameidata *nd);
++int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex);
++int au_refresh_hdentry(struct dentry *dentry, mode_t type);
++int au_reval_dpath(struct dentry *dentry, unsigned int sigen);
++
++/* dinfo.c */
++int au_alloc_dinfo(struct dentry *dentry);
++int au_di_realloc(struct au_dinfo *dinfo, int nbr);
++
++void di_read_lock(struct dentry *d, int flags, unsigned int lsc);
++void di_read_unlock(struct dentry *d, int flags);
++void di_downgrade_lock(struct dentry *d, int flags);
++void di_write_lock(struct dentry *d, unsigned int lsc);
++void di_write_unlock(struct dentry *d);
++void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir);
++void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir);
++void di_write_unlock2(struct dentry *d1, struct dentry *d2);
++
++struct dentry *au_h_dptr(struct dentry *dentry, aufs_bindex_t bindex);
++aufs_bindex_t au_dbtail(struct dentry *dentry);
++aufs_bindex_t au_dbtaildir(struct dentry *dentry);
++
++void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex,
++ struct dentry *h_dentry);
++void au_update_digen(struct dentry *dentry);
++void au_update_dbrange(struct dentry *dentry, int do_put_zero);
++void au_update_dbstart(struct dentry *dentry);
++void au_update_dbend(struct dentry *dentry);
++int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry);
++
++/* ---------------------------------------------------------------------- */
++
++static inline struct au_dinfo *au_di(struct dentry *dentry)
++{
++ return dentry->d_fsdata;
++}
++
++/* ---------------------------------------------------------------------- */
++
++/* lock subclass for dinfo */
++enum {
++ AuLsc_DI_CHILD, /* child first */
++ AuLsc_DI_CHILD2, /* rename(2), link(2), and cpup at hinotify */
++ AuLsc_DI_CHILD3, /* copyup dirs */
++ AuLsc_DI_PARENT,
++ AuLsc_DI_PARENT2,
++ AuLsc_DI_PARENT3
++};
++
++/*
++ * di_read_lock_child, di_write_lock_child,
++ * di_read_lock_child2, di_write_lock_child2,
++ * di_read_lock_child3, di_write_lock_child3,
++ * di_read_lock_parent, di_write_lock_parent,
++ * di_read_lock_parent2, di_write_lock_parent2,
++ * di_read_lock_parent3, di_write_lock_parent3,
++ */
++#define AuReadLockFunc(name, lsc) \
++static inline void di_read_lock_##name(struct dentry *d, int flags) \
++{ di_read_lock(d, flags, AuLsc_DI_##lsc); }
++
++#define AuWriteLockFunc(name, lsc) \
++static inline void di_write_lock_##name(struct dentry *d) \
++{ di_write_lock(d, AuLsc_DI_##lsc); }
++
++#define AuRWLockFuncs(name, lsc) \
++ AuReadLockFunc(name, lsc) \
++ AuWriteLockFunc(name, lsc)
++
++AuRWLockFuncs(child, CHILD);
++AuRWLockFuncs(child2, CHILD2);
++AuRWLockFuncs(child3, CHILD3);
++AuRWLockFuncs(parent, PARENT);
++AuRWLockFuncs(parent2, PARENT2);
++AuRWLockFuncs(parent3, PARENT3);
++
++#undef AuReadLockFunc
++#undef AuWriteLockFunc
++#undef AuRWLockFuncs
++
++#define DiMustNoWaiters(d) AuRwMustNoWaiters(&au_di(d)->di_rwsem)
++#define DiMustAnyLock(d) AuRwMustAnyLock(&au_di(d)->di_rwsem)
++#define DiMustWriteLock(d) AuRwMustWriteLock(&au_di(d)->di_rwsem)
++
++/* ---------------------------------------------------------------------- */
++
++/* todo: memory barrier? */
++static inline unsigned int au_digen(struct dentry *d)
++{
++ return atomic_read(&au_di(d)->di_generation);
++}
++
++static inline void au_h_dentry_init(struct au_hdentry *hdentry)
++{
++ hdentry->hd_dentry = NULL;
++}
++
++static inline void au_hdput(struct au_hdentry *hd)
++{
++ dput(hd->hd_dentry);
++}
++
++static inline aufs_bindex_t au_dbstart(struct dentry *dentry)
++{
++ DiMustAnyLock(dentry);
++ return au_di(dentry)->di_bstart;
++}
++
++static inline aufs_bindex_t au_dbend(struct dentry *dentry)
++{
++ DiMustAnyLock(dentry);
++ return au_di(dentry)->di_bend;
++}
++
++static inline aufs_bindex_t au_dbwh(struct dentry *dentry)
++{
++ DiMustAnyLock(dentry);
++ return au_di(dentry)->di_bwh;
++}
++
++static inline aufs_bindex_t au_dbdiropq(struct dentry *dentry)
++{
++ DiMustAnyLock(dentry);
++ return au_di(dentry)->di_bdiropq;
++}
++
++/* todo: hard/soft set? */
++static inline void au_set_dbstart(struct dentry *dentry, aufs_bindex_t bindex)
++{
++ DiMustWriteLock(dentry);
++ au_di(dentry)->di_bstart = bindex;
++}
++
++static inline void au_set_dbend(struct dentry *dentry, aufs_bindex_t bindex)
++{
++ DiMustWriteLock(dentry);
++ au_di(dentry)->di_bend = bindex;
++}
++
++static inline void au_set_dbwh(struct dentry *dentry, aufs_bindex_t bindex)
++{
++ DiMustWriteLock(dentry);
++ /* dbwh can be outside of bstart - bend range */
++ au_di(dentry)->di_bwh = bindex;
++}
++
++static inline void au_set_dbdiropq(struct dentry *dentry, aufs_bindex_t bindex)
++{
++ DiMustWriteLock(dentry);
++ au_di(dentry)->di_bdiropq = bindex;
++}
++
++/* ---------------------------------------------------------------------- */
++
++#ifdef CONFIG_AUFS_HINOTIFY
++static inline void au_digen_dec(struct dentry *d)
++{
++ atomic_dec_return(&au_di(d)->di_generation);
++}
++
++static inline void au_hin_di_reinit(struct dentry *dentry)
++{
++ dentry->d_fsdata = NULL;
++}
++#else
++static inline void au_hin_di_reinit(struct dentry *dentry __maybe_unused)
++{
++ /* empty */
++}
++#endif /* CONFIG_AUFS_HINOTIFY */
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_DENTRY_H__ */
+diff -Nur linux-2.6.31.5.orig/fs/aufs/dinfo.c linux-2.6.31.5/fs/aufs/dinfo.c
+--- linux-2.6.31.5.orig/fs/aufs/dinfo.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/dinfo.c 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,367 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. Okajima
++ *
++ * This program, aufs is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/*
++ * dentry private data
++ */
++
++#include "aufs.h"
++
++int au_alloc_dinfo(struct dentry *dentry)
++{
++ struct au_dinfo *dinfo;
++ struct super_block *sb;
++ int nbr;
++
++ dinfo = au_cache_alloc_dinfo();
++ if (unlikely(!dinfo))
++ goto out;
++
++ sb = dentry->d_sb;
++ nbr = au_sbend(sb) + 1;
++ if (nbr <= 0)
++ nbr = 1;
++ dinfo->di_hdentry = kcalloc(nbr, sizeof(*dinfo->di_hdentry), GFP_NOFS);
++ if (unlikely(!dinfo->di_hdentry))
++ goto out_dinfo;
++
++ atomic_set(&dinfo->di_generation, au_sigen(sb));
++ /* smp_mb(); */ /* atomic_set */
++ au_rw_init_wlock_nested(&dinfo->di_rwsem, AuLsc_DI_CHILD);
++ dinfo->di_bstart = -1;
++ dinfo->di_bend = -1;
++ dinfo->di_bwh = -1;
++ dinfo->di_bdiropq = -1;
++
++ dentry->d_fsdata = dinfo;
++ dentry->d_op = &aufs_dop;
++ return 0; /* success */
++
++ out_dinfo:
++ au_cache_free_dinfo(dinfo);
++ out:
++ return -ENOMEM;
++}
++
++int au_di_realloc(struct au_dinfo *dinfo, int nbr)
++{
++ int err, sz;
++ struct au_hdentry *hdp;
++
++ AuRwMustWriteLock(&dinfo->di_rwsem);
++
++ err = -ENOMEM;
++ sz = sizeof(*hdp) * (dinfo->di_bend + 1);
++ if (!sz)
++ sz = sizeof(*hdp);
++ hdp = au_kzrealloc(dinfo->di_hdentry, sz, sizeof(*hdp) * nbr, GFP_NOFS);
++ if (hdp) {
++ dinfo->di_hdentry = hdp;
++ err = 0;
++ }
++
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++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)
++{
++ au_rw_read_lock_nested(&au_di(d)->di_rwsem, lsc);
++ if (d->d_inode) {
++ if (au_ftest_lock(flags, IW))
++ do_ii_write_lock(d->d_inode, lsc);
++ else if (au_ftest_lock(flags, IR))
++ do_ii_read_lock(d->d_inode, lsc);
++ }
++}
++
++void di_read_unlock(struct dentry *d, int flags)
++{
++ if (d->d_inode) {
++ if (au_ftest_lock(flags, IW))
++ ii_write_unlock(d->d_inode);
++ else if (au_ftest_lock(flags, IR))
++ ii_read_unlock(d->d_inode);
++ }
++ au_rw_read_unlock(&au_di(d)->di_rwsem);
++}
++
++void di_downgrade_lock(struct dentry *d, int flags)
++{
++ if (d->d_inode && au_ftest_lock(flags, IR))
++ ii_downgrade_lock(d->d_inode);
++ au_rw_dgrade_lock(&au_di(d)->di_rwsem);
++}
++
++void di_write_lock(struct dentry *d, unsigned int lsc)
++{
++ au_rw_write_lock_nested(&au_di(d)->di_rwsem, lsc);
++ if (d->d_inode)
++ do_ii_write_lock(d->d_inode, lsc);
++}
++
++void di_write_unlock(struct dentry *d)
++{
++ if (d->d_inode)
++ ii_write_unlock(d->d_inode);
++ au_rw_write_unlock(&au_di(d)->di_rwsem);
++}
++
++void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir)
++{
++ AuDebugOn(d1 == d2
++ || d1->d_inode == d2->d_inode
++ || d1->d_sb != d2->d_sb);
++
++ if (isdir && au_test_subdir(d1, d2)) {
++ di_write_lock_child(d1);
++ di_write_lock_child2(d2);
++ } else {
++ /* there should be no races */
++ di_write_lock_child(d2);
++ di_write_lock_child2(d1);
++ }
++}
++
++void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir)
++{
++ AuDebugOn(d1 == d2
++ || d1->d_inode == d2->d_inode
++ || d1->d_sb != d2->d_sb);
++
++ if (isdir && au_test_subdir(d1, d2)) {
++ di_write_lock_parent(d1);
++ di_write_lock_parent2(d2);
++ } else {
++ /* there should be no races */
++ di_write_lock_parent(d2);
++ di_write_lock_parent2(d1);
++ }
++}
++
++void di_write_unlock2(struct dentry *d1, struct dentry *d2)
++{
++ di_write_unlock(d1);
++ if (d1->d_inode == d2->d_inode)
++ au_rw_write_unlock(&au_di(d2)->di_rwsem);
++ else
++ di_write_unlock(d2);
++}
++
++/* ---------------------------------------------------------------------- */
++
++struct dentry *au_h_dptr(struct dentry *dentry, aufs_bindex_t bindex)
++{
++ struct dentry *d;
++
++ DiMustAnyLock(dentry);
++
++ if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry))
++ return NULL;
++ AuDebugOn(bindex < 0);
++ d = au_di(dentry)->di_hdentry[0 + bindex].hd_dentry;
++ AuDebugOn(d && (atomic_read(&d->d_count) <= 0));
++ return d;
++}
++
++aufs_bindex_t au_dbtail(struct dentry *dentry)
++{
++ aufs_bindex_t bend, bwh;
++
++ bend = au_dbend(dentry);
++ if (0 <= bend) {
++ bwh = au_dbwh(dentry);
++ if (!bwh)
++ return bwh;
++ if (0 < bwh && bwh < bend)
++ return bwh - 1;
++ }
++ return bend;
++}
++
++aufs_bindex_t au_dbtaildir(struct dentry *dentry)
++{
++ aufs_bindex_t bend, bopq;
++
++ bend = au_dbtail(dentry);
++ if (0 <= bend) {
++ bopq = au_dbdiropq(dentry);
++ if (0 <= bopq && bopq < bend)
++ bend = bopq;
++ }
++ return bend;
++}
++
++/* ---------------------------------------------------------------------- */
++
++void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex,
++ struct dentry *h_dentry)
++{
++ struct au_hdentry *hd = au_di(dentry)->di_hdentry + bindex;
++
++ DiMustWriteLock(dentry);
++
++ if (hd->hd_dentry)
++ au_hdput(hd);
++ hd->hd_dentry = h_dentry;
++}
++
++void au_update_digen(struct dentry *dentry)
++{
++ atomic_set(&au_di(dentry)->di_generation, au_sigen(dentry->d_sb));
++ /* smp_mb(); */ /* atomic_set */
++}
++
++void au_update_dbrange(struct dentry *dentry, int do_put_zero)
++{
++ struct au_dinfo *dinfo;
++ struct dentry *h_d;
++
++ DiMustWriteLock(dentry);
++
++ dinfo = au_di(dentry);
++ if (!dinfo || dinfo->di_bstart < 0)
++ return;
++
++ if (do_put_zero) {
++ aufs_bindex_t bindex, bend;
++
++ bend = dinfo->di_bend;
++ for (bindex = dinfo->di_bstart; bindex <= bend; bindex++) {
++ h_d = dinfo->di_hdentry[0 + bindex].hd_dentry;
++ if (h_d && !h_d->d_inode)
++ au_set_h_dptr(dentry, bindex, NULL);
++ }
++ }
++
++ dinfo->di_bstart = -1;
++ while (++dinfo->di_bstart <= dinfo->di_bend)
++ if (dinfo->di_hdentry[0 + dinfo->di_bstart].hd_dentry)
++ break;
++ if (dinfo->di_bstart > dinfo->di_bend) {
++ dinfo->di_bstart = -1;
++ dinfo->di_bend = -1;
++ return;
++ }
++
++ dinfo->di_bend++;
++ while (0 <= --dinfo->di_bend)
++ if (dinfo->di_hdentry[0 + dinfo->di_bend].hd_dentry)
++ break;
++ AuDebugOn(dinfo->di_bstart > dinfo->di_bend || dinfo->di_bend < 0);
++}
++
++void au_update_dbstart(struct dentry *dentry)
++{
++ aufs_bindex_t bindex, bend;
++ struct dentry *h_dentry;
++
++ bend = au_dbend(dentry);
++ for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) {
++ h_dentry = au_h_dptr(dentry, bindex);
++ if (!h_dentry)
++ continue;
++ if (h_dentry->d_inode) {
++ au_set_dbstart(dentry, bindex);
++ return;
++ }
++ au_set_h_dptr(dentry, bindex, NULL);
++ }
++}
++
++void au_update_dbend(struct dentry *dentry)
++{
++ aufs_bindex_t bindex, bstart;
++ struct dentry *h_dentry;
++
++ bstart = au_dbstart(dentry);
++ for (bindex = au_dbend(dentry); bindex <= bstart; bindex--) {
++ h_dentry = au_h_dptr(dentry, bindex);
++ if (!h_dentry)
++ continue;
++ if (h_dentry->d_inode) {
++ au_set_dbend(dentry, bindex);
++ return;
++ }
++ au_set_h_dptr(dentry, bindex, NULL);
++ }
++}
++
++int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry)
++{
++ aufs_bindex_t bindex, bend;
++
++ bend = au_dbend(dentry);
++ for (bindex = au_dbstart(dentry); bindex <= bend; bindex++)
++ if (au_h_dptr(dentry, bindex) == h_dentry)
++ return bindex;
++ return -1;
++}
+diff -Nur linux-2.6.31.5.orig/fs/aufs/dir.c linux-2.6.31.5/fs/aufs/dir.c
+--- linux-2.6.31.5.orig/fs/aufs/dir.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/dir.c 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,538 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. Okajima
++ *
++ * This program, aufs is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/*
++ * directory operations
++ */
++
++#include <linux/file.h>
++#include <linux/fs_stack.h>
++#include "aufs.h"
++
++void au_add_nlink(struct inode *dir, struct inode *h_dir)
++{
++ AuDebugOn(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode));
++
++ dir->i_nlink += h_dir->i_nlink - 2;
++ if (h_dir->i_nlink < 2)
++ dir->i_nlink += 2;
++}
++
++void au_sub_nlink(struct inode *dir, struct inode *h_dir)
++{
++ AuDebugOn(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode));
++
++ dir->i_nlink -= h_dir->i_nlink - 2;
++ if (h_dir->i_nlink < 2)
++ dir->i_nlink -= 2;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static int reopen_dir(struct file *file)
++{
++ int err;
++ unsigned int flags;
++ aufs_bindex_t bindex, btail, bstart;
++ struct dentry *dentry, *h_dentry;
++ struct file *h_file;
++
++ /* open all lower dirs */
++ dentry = file->f_dentry;
++ bstart = au_dbstart(dentry);
++ for (bindex = au_fbstart(file); bindex < bstart; bindex++)
++ au_set_h_fptr(file, bindex, NULL);
++ au_set_fbstart(file, bstart);
++
++ btail = au_dbtaildir(dentry);
++ for (bindex = au_fbend(file); btail < bindex; bindex--)
++ au_set_h_fptr(file, bindex, NULL);
++ au_set_fbend(file, btail);
++
++ flags = file->f_flags;
++ for (bindex = bstart; bindex <= btail; bindex++) {
++ h_dentry = au_h_dptr(dentry, bindex);
++ if (!h_dentry)
++ continue;
++ h_file = au_h_fptr(file, bindex);
++ if (h_file)
++ continue;
++
++ h_file = au_h_open(dentry, bindex, flags, file);
++ err = PTR_ERR(h_file);
++ if (IS_ERR(h_file))
++ goto out; /* close all? */
++ au_set_h_fptr(file, bindex, h_file);
++ }
++ au_update_figen(file);
++ /* todo: necessary? */
++ /* file->f_ra = h_file->f_ra; */
++ err = 0;
++
++ out:
++ return err;
++}
++
++static int do_open_dir(struct file *file, int flags)
++{
++ int err;
++ aufs_bindex_t bindex, btail;
++ struct dentry *dentry, *h_dentry;
++ struct file *h_file;
++
++ FiMustWriteLock(file);
++
++ err = 0;
++ dentry = file->f_dentry;
++ au_set_fvdir_cache(file, NULL);
++ au_fi(file)->fi_maintain_plink = 0;
++ file->f_version = dentry->d_inode->i_version;
++ bindex = au_dbstart(dentry);
++ au_set_fbstart(file, bindex);
++ btail = au_dbtaildir(dentry);
++ au_set_fbend(file, btail);
++ for (; !err && bindex <= btail; bindex++) {
++ h_dentry = au_h_dptr(dentry, bindex);
++ if (!h_dentry)
++ continue;
++
++ h_file = au_h_open(dentry, bindex, flags, file);
++ if (IS_ERR(h_file)) {
++ err = PTR_ERR(h_file);
++ break;
++ }
++ au_set_h_fptr(file, bindex, h_file);
++ }
++ au_update_figen(file);
++ /* todo: necessary? */
++ /* file->f_ra = h_file->f_ra; */
++ if (!err)
++ return 0; /* success */
++
++ /* close all */
++ for (bindex = au_fbstart(file); bindex <= btail; bindex++)
++ au_set_h_fptr(file, bindex, NULL);
++ au_set_fbstart(file, -1);
++ au_set_fbend(file, -1);
++ return err;
++}
++
++static int aufs_open_dir(struct inode *inode __maybe_unused,
++ struct file *file)
++{
++ return au_do_open(file, do_open_dir);
++}
++
++static int aufs_release_dir(struct inode *inode __maybe_unused,
++ struct file *file)
++{
++ struct au_vdir *vdir_cache;
++ struct super_block *sb;
++ struct au_sbinfo *sbinfo;
++
++ sb = file->f_dentry->d_sb;
++ si_noflush_read_lock(sb);
++ fi_write_lock(file);
++ vdir_cache = au_fvdir_cache(file);
++ if (vdir_cache)
++ au_vdir_free(vdir_cache);
++ if (au_fi(file)->fi_maintain_plink) {
++ sbinfo = au_sbi(sb);
++ /* clear the flag without write-lock */
++ sbinfo->au_si_status &= ~AuSi_MAINTAIN_PLINK;
++ smp_mb();
++ wake_up_all(&sbinfo->si_plink_wq);
++ }
++ fi_write_unlock(file);
++ au_finfo_fin(file);
++ si_read_unlock(sb);
++ return 0;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static int au_do_fsync_dir_no_file(struct dentry *dentry, int datasync)
++{
++ int err;
++ aufs_bindex_t bend, bindex;
++ struct inode *inode;
++ struct super_block *sb;
++
++ err = 0;
++ sb = dentry->d_sb;
++ inode = dentry->d_inode;
++ IMustLock(inode);
++ bend = au_dbend(dentry);
++ for (bindex = au_dbstart(dentry); !err && bindex <= bend; bindex++) {
++ struct path h_path;
++ struct inode *h_inode;
++
++ if (au_test_ro(sb, bindex, inode))
++ continue;
++ h_path.dentry = au_h_dptr(dentry, bindex);
++ if (!h_path.dentry)
++ continue;
++ h_inode = h_path.dentry->d_inode;
++ if (!h_inode)
++ continue;
++
++ /* no mnt_want_write() */
++ /* cf. fs/nsfd/vfs.c and fs/nfsd/nfs4recover.c */
++ /* todo: inotiry fired? */
++ h_path.mnt = au_sbr_mnt(sb, bindex);
++ mutex_lock(&h_inode->i_mutex);
++ err = filemap_fdatawrite(h_inode->i_mapping);
++ AuDebugOn(!h_inode->i_fop);
++ if (!err && h_inode->i_fop->fsync)
++ err = h_inode->i_fop->fsync(NULL, h_path.dentry,
++ datasync);
++ if (!err)
++ err = filemap_fdatawrite(h_inode->i_mapping);
++ if (!err)
++ vfsub_update_h_iattr(&h_path, /*did*/NULL); /*ignore*/
++ mutex_unlock(&h_inode->i_mutex);
++ }
++
++ return err;
++}
++
++static int au_do_fsync_dir(struct file *file, int datasync)
++{
++ int err;
++ aufs_bindex_t bend, bindex;
++ struct file *h_file;
++ struct super_block *sb;
++ struct inode *inode;
++ struct mutex *h_mtx;
++
++ err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1);
++ if (unlikely(err))
++ goto out;
++
++ sb = file->f_dentry->d_sb;
++ inode = file->f_dentry->d_inode;
++ bend = au_fbend(file);
++ for (bindex = au_fbstart(file); !err && bindex <= bend; bindex++) {
++ h_file = au_h_fptr(file, bindex);
++ if (!h_file || au_test_ro(sb, bindex, inode))
++ continue;
++
++ err = vfs_fsync(h_file, h_file->f_dentry, datasync);
++ if (!err) {
++ h_mtx = &h_file->f_dentry->d_inode->i_mutex;
++ mutex_lock(h_mtx);
++ vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL);
++ /*ignore*/
++ mutex_unlock(h_mtx);
++ }
++ }
++
++ out:
++ return err;
++}
++
++/*
++ * @file may be NULL
++ */
++static int aufs_fsync_dir(struct file *file, struct dentry *dentry,
++ int datasync)
++{
++ int err;
++ struct super_block *sb;
++
++ IMustLock(dentry->d_inode);
++
++ err = 0;
++ sb = dentry->d_sb;
++ si_noflush_read_lock(sb);
++ if (file)
++ err = au_do_fsync_dir(file, datasync);
++ else {
++ di_write_lock_child(dentry);
++ err = au_do_fsync_dir_no_file(dentry, datasync);
++ }
++ au_cpup_attr_timesizes(dentry->d_inode);
++ di_write_unlock(dentry);
++ if (file)
++ fi_write_unlock(file);
++
++ si_read_unlock(sb);
++ 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;
++ inode = dentry->d_inode;
++ IMustLock(inode);
++
++ sb = dentry->d_sb;
++ si_read_lock(sb, AuLock_FLUSH);
++ err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1);
++ if (unlikely(err))
++ goto out;
++ err = au_vdir_init(file);
++ di_downgrade_lock(dentry, AuLock_IR);
++ if (unlikely(err))
++ goto out_unlock;
++
++ if (!au_test_nfsd(current)) {
++ err = au_vdir_fill_de(file, dirent, filldir);
++ fsstack_copy_attr_atime(inode,
++ au_h_iptr(inode, au_ibstart(inode)));
++ } else {
++ /*
++ * nfsd filldir may call lookup_one_len(), vfs_getattr(),
++ * encode_fh() and others.
++ */
++ struct inode *h_inode = au_h_iptr(inode, au_ibstart(inode));
++
++ di_read_unlock(dentry, AuLock_IR);
++ si_read_unlock(sb);
++ lockdep_off();
++ err = au_vdir_fill_de(file, dirent, filldir);
++ lockdep_on();
++ fsstack_copy_attr_atime(inode, h_inode);
++ fi_write_unlock(file);
++
++ AuTraceErr(err);
++ return err;
++ }
++
++ out_unlock:
++ di_read_unlock(dentry, AuLock_IR);
++ fi_write_unlock(file);
++ out:
++ si_read_unlock(sb);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++#define AuTestEmpty_WHONLY 1
++#define AuTestEmpty_CALLED (1 << 1)
++#define AuTestEmpty_SHWH (1 << 2)
++#define au_ftest_testempty(flags, name) ((flags) & AuTestEmpty_##name)
++#define au_fset_testempty(flags, name) { (flags) |= AuTestEmpty_##name; }
++#define au_fclr_testempty(flags, name) { (flags) &= ~AuTestEmpty_##name; }
++
++#ifndef CONFIG_AUFS_SHWH
++#undef AuTestEmpty_SHWH
++#define AuTestEmpty_SHWH 0
++#endif
++
++struct test_empty_arg {
++ struct au_nhash whlist;
++ unsigned int flags;
++ int err;
++ aufs_bindex_t bindex;
++};
++
++static int test_empty_cb(void *__arg, const char *__name, int namelen,
++ loff_t offset __maybe_unused, u64 ino,
++ unsigned int d_type)
++{
++ struct test_empty_arg *arg = __arg;
++ char *name = (void *)__name;
++
++ arg->err = 0;
++ au_fset_testempty(arg->flags, CALLED);
++ /* smp_mb(); */
++ if (name[0] == '.'
++ && (namelen == 1 || (name[1] == '.' && namelen == 2)))
++ goto out; /* success */
++
++ if (namelen <= AUFS_WH_PFX_LEN
++ || memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) {
++ if (au_ftest_testempty(arg->flags, WHONLY)
++ && !au_nhash_test_known_wh(&arg->whlist, name, namelen))
++ arg->err = -ENOTEMPTY;
++ goto out;
++ }
++
++ name += AUFS_WH_PFX_LEN;
++ namelen -= AUFS_WH_PFX_LEN;
++ if (!au_nhash_test_known_wh(&arg->whlist, name, namelen))
++ arg->err = au_nhash_append_wh
++ (&arg->whlist, name, namelen, ino, d_type, arg->bindex,
++ au_ftest_testempty(arg->flags, SHWH));
++
++ out:
++ /* smp_mb(); */
++ AuTraceErr(arg->err);
++ return arg->err;
++}
++
++static int do_test_empty(struct dentry *dentry, struct test_empty_arg *arg)
++{
++ int err;
++ struct file *h_file;
++
++ h_file = au_h_open(dentry, arg->bindex,
++ O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_LARGEFILE,
++ /*file*/NULL);
++ err = PTR_ERR(h_file);
++ if (IS_ERR(h_file))
++ goto out;
++
++ err = 0;
++ if (!au_opt_test(au_mntflags(dentry->d_sb), UDBA_NONE)
++ && !h_file->f_dentry->d_inode->i_nlink)
++ goto out_put;
++
++ do {
++ arg->err = 0;
++ au_fclr_testempty(arg->flags, CALLED);
++ /* smp_mb(); */
++ err = vfsub_readdir(h_file, test_empty_cb, arg);
++ if (err >= 0)
++ err = arg->err;
++ } while (!err && au_ftest_testempty(arg->flags, CALLED));
++
++ out_put:
++ fput(h_file);
++ au_sbr_put(dentry->d_sb, arg->bindex);
++ out:
++ return err;
++}
++
++struct do_test_empty_args {
++ int *errp;
++ struct dentry *dentry;
++ struct test_empty_arg *arg;
++};
++
++static void call_do_test_empty(void *args)
++{
++ struct do_test_empty_args *a = args;
++ *a->errp = do_test_empty(a->dentry, a->arg);
++}
++
++static int sio_test_empty(struct dentry *dentry, struct test_empty_arg *arg)
++{
++ int err, wkq_err;
++ struct dentry *h_dentry;
++ struct inode *h_inode;
++
++ h_dentry = au_h_dptr(dentry, arg->bindex);
++ h_inode = h_dentry->d_inode;
++ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);
++ err = au_test_h_perm_sio(h_inode, MAY_EXEC | MAY_READ);
++ mutex_unlock(&h_inode->i_mutex);
++ if (!err)
++ err = do_test_empty(dentry, arg);
++ else {
++ struct do_test_empty_args args = {
++ .errp = &err,
++ .dentry = dentry,
++ .arg = arg
++ };
++ unsigned int flags = arg->flags;
++
++ wkq_err = au_wkq_wait(call_do_test_empty, &args);
++ if (unlikely(wkq_err))
++ err = wkq_err;
++ arg->flags = flags;
++ }
++
++ return err;
++}
++
++int au_test_empty_lower(struct dentry *dentry)
++{
++ int err;
++ aufs_bindex_t bindex, bstart, btail;
++ struct test_empty_arg arg;
++
++ SiMustAnyLock(dentry->d_sb);
++
++ err = au_nhash_alloc(&arg.whlist, au_sbi(dentry->d_sb)->si_rdhash,
++ GFP_NOFS);
++ if (unlikely(err))
++ goto out;
++
++ bstart = au_dbstart(dentry);
++ arg.flags = 0;
++ if (au_opt_test(au_mntflags(dentry->d_sb), SHWH))
++ au_fset_testempty(arg.flags, SHWH);
++ arg.bindex = bstart;
++ err = do_test_empty(dentry, &arg);
++ if (unlikely(err))
++ goto out_whlist;
++
++ au_fset_testempty(arg.flags, WHONLY);
++ btail = au_dbtaildir(dentry);
++ for (bindex = bstart + 1; !err && bindex <= btail; bindex++) {
++ struct dentry *h_dentry;
++
++ h_dentry = au_h_dptr(dentry, bindex);
++ if (h_dentry && h_dentry->d_inode) {
++ arg.bindex = bindex;
++ err = do_test_empty(dentry, &arg);
++ }
++ }
++
++ out_whlist:
++ au_nhash_wh_free(&arg.whlist);
++ out:
++ return err;
++}
++
++int au_test_empty(struct dentry *dentry, struct au_nhash *whlist)
++{
++ int err;
++ struct test_empty_arg arg;
++ aufs_bindex_t bindex, btail;
++
++ err = 0;
++ arg.whlist = *whlist;
++ arg.flags = AuTestEmpty_WHONLY;
++ if (au_opt_test(au_mntflags(dentry->d_sb), SHWH))
++ au_fset_testempty(arg.flags, SHWH);
++ btail = au_dbtaildir(dentry);
++ for (bindex = au_dbstart(dentry); !err && bindex <= btail; bindex++) {
++ struct dentry *h_dentry;
++
++ h_dentry = au_h_dptr(dentry, bindex);
++ if (h_dentry && h_dentry->d_inode) {
++ arg.bindex = bindex;
++ err = sio_test_empty(dentry, &arg);
++ }
++ }
++
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++const struct file_operations aufs_dir_fop = {
++ .read = generic_read_dir,
++ .readdir = aufs_readdir,
++ .unlocked_ioctl = aufs_ioctl_dir,
++ .open = aufs_open_dir,
++ .release = aufs_release_dir,
++ .flush = aufs_flush,
++ .fsync = aufs_fsync_dir
++};
+diff -Nur linux-2.6.31.5.orig/fs/aufs/dir.h linux-2.6.31.5/fs/aufs/dir.h
+--- linux-2.6.31.5.orig/fs/aufs/dir.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/dir.h 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,114 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. Okajima
++ *
++ * This program, aufs is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/*
++ * directory operations
++ */
++
++#ifndef __AUFS_DIR_H__
++#define __AUFS_DIR_H__
++
++#ifdef __KERNEL__
++
++#include <linux/fs.h>
++#include <linux/aufs_type.h>
++
++/* ---------------------------------------------------------------------- */
++
++/* need to be faster and smaller */
++
++struct au_nhash {
++ unsigned int nh_num;
++ struct hlist_head *nh_head;
++};
++
++struct au_vdir_destr {
++ unsigned char len;
++ unsigned char name[0];
++} __packed;
++
++struct au_vdir_dehstr {
++ struct hlist_node hash;
++ struct au_vdir_destr *str;
++};
++
++struct au_vdir_de {
++ ino_t de_ino;
++ unsigned char de_type;
++ /* caution: packed */
++ struct au_vdir_destr de_str;
++} __packed;
++
++struct au_vdir_wh {
++ struct hlist_node wh_hash;
++#ifdef CONFIG_AUFS_SHWH
++ ino_t wh_ino;
++ aufs_bindex_t wh_bindex;
++ unsigned char wh_type;
++#else
++ aufs_bindex_t wh_bindex;
++#endif
++ /* caution: packed */
++ struct au_vdir_destr wh_str;
++} __packed;
++
++union au_vdir_deblk_p {
++ unsigned char *deblk;
++ struct au_vdir_de *de;
++};
++
++struct au_vdir {
++ unsigned char **vd_deblk;
++ unsigned long vd_nblk;
++ struct {
++ unsigned long ul;
++ union au_vdir_deblk_p p;
++ } vd_last;
++
++ unsigned long vd_version;
++ unsigned int vd_deblk_sz;
++ unsigned long vd_jiffy;
++};
++
++/* ---------------------------------------------------------------------- */
++
++/* dir.c */
++extern const struct file_operations aufs_dir_fop;
++void au_add_nlink(struct inode *dir, struct inode *h_dir);
++void au_sub_nlink(struct inode *dir, struct inode *h_dir);
++int au_test_empty_lower(struct dentry *dentry);
++int au_test_empty(struct dentry *dentry, struct au_nhash *whlist);
++
++/* vdir.c */
++int au_nhash_alloc(struct au_nhash *nhash, unsigned int num_hash, gfp_t gfp);
++void au_nhash_wh_free(struct au_nhash *whlist);
++int au_nhash_test_longer_wh(struct au_nhash *whlist, aufs_bindex_t btgt,
++ int limit);
++int au_nhash_test_known_wh(struct au_nhash *whlist, char *name, int nlen);
++int au_nhash_append_wh(struct au_nhash *whlist, char *name, int nlen, ino_t ino,
++ unsigned int d_type, aufs_bindex_t bindex,
++ unsigned char shwh);
++void au_vdir_free(struct au_vdir *vdir);
++int au_vdir_init(struct file *file);
++int au_vdir_fill_de(struct file *file, void *dirent, filldir_t filldir);
++
++/* ioctl.c */
++long aufs_ioctl_dir(struct file *file, unsigned int cmd, unsigned long arg);
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_DIR_H__ */
+diff -Nur linux-2.6.31.5.orig/fs/aufs/export.c linux-2.6.31.5/fs/aufs/export.c
+--- linux-2.6.31.5.orig/fs/aufs/export.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/export.c 2009-11-15 22:27:33.000000000 +0100
+@@ -0,0 +1,746 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. Okajima
++ *
++ * This program, aufs is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/*
++ * export via nfs
++ */
++
++#include <linux/exportfs.h>
++#include <linux/file.h>
++#include <linux/mnt_namespace.h>
++#include <linux/nsproxy.h>
++#include <linux/namei.h>
++#include <linux/random.h>
++#include "aufs.h"
++
++union conv {
++#ifdef CONFIG_AUFS_INO_T_64
++ __u32 a[2];
++#else
++ __u32 a[1];
++#endif
++ ino_t ino;
++};
++
++static ino_t decode_ino(__u32 *a)
++{
++ union conv u;
++
++ BUILD_BUG_ON(sizeof(u.ino) != sizeof(u.a));
++ u.a[0] = a[0];
++#ifdef CONFIG_AUFS_INO_T_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];
++#ifdef CONFIG_AUFS_INO_T_64
++ a[1] = u.a[1];
++#endif
++}
++
++/* NFS file handle */
++enum {
++ Fh_br_id,
++ Fh_sigen,
++#ifdef CONFIG_AUFS_INO_T_64
++ /* support 64bit inode number */
++ Fh_ino1,
++ Fh_ino2,
++ Fh_dir_ino1,
++ Fh_dir_ino2,
++#else
++ Fh_ino1,
++ Fh_dir_ino1,
++#endif
++ Fh_igen,
++ Fh_h_type,
++ Fh_tail,
++
++ Fh_ino = Fh_ino1,
++ Fh_dir_ino = Fh_dir_ino1
++};
++
++static int au_test_anon(struct dentry *dentry)
++{
++ return !!(dentry->d_flags & DCACHE_DISCONNECTED);
++}
++
++/* ---------------------------------------------------------------------- */
++/* inode generation external table */
++
++int au_xigen_inc(struct inode *inode)
++{
++ int err;
++ loff_t pos;
++ ssize_t sz;
++ __u32 igen;
++ struct super_block *sb;
++ struct au_sbinfo *sbinfo;
++
++ err = 0;
++ sb = inode->i_sb;
++ sbinfo = au_sbi(sb);
++ /*
++ * temporary workaround for escaping from SiMustAnyLock() in
++ * au_mntflags(), since this function is called from au_iinfo_fin().
++ */
++ if (unlikely(!au_opt_test(sbinfo->si_mntflags, XINO)))
++ goto out;
++
++ pos = inode->i_ino;
++ pos *= sizeof(igen);
++ igen = inode->i_generation + 1;
++ sz = xino_fwrite(sbinfo->si_xwrite, sbinfo->si_xigen, &igen,
++ sizeof(igen), &pos);
++ if (sz == sizeof(igen))
++ goto out; /* success */
++
++ err = sz;
++ if (unlikely(sz >= 0)) {
++ err = -EIO;
++ AuIOErr("xigen error (%zd)\n", sz);
++ }
++
++ out:
++ return err;
++}
++
++int au_xigen_new(struct inode *inode)
++{
++ int err;
++ loff_t pos;
++ ssize_t sz;
++ struct super_block *sb;
++ struct au_sbinfo *sbinfo;
++ struct file *file;
++
++ err = 0;
++ /* todo: dirty, at mount time */
++ if (inode->i_ino == AUFS_ROOT_INO)
++ goto out;
++ sb = inode->i_sb;
++ SiMustAnyLock(sb);
++ if (unlikely(!au_opt_test(au_mntflags(sb), XINO)))
++ goto out;
++
++ err = -EFBIG;
++ pos = inode->i_ino;
++ if (unlikely(au_loff_max / sizeof(inode->i_generation) - 1 < pos)) {
++ AuIOErr1("too large i%lld\n", pos);
++ goto out;
++ }
++ pos *= sizeof(inode->i_generation);
++
++ err = 0;
++ sbinfo = au_sbi(sb);
++ file = sbinfo->si_xigen;
++ BUG_ON(!file);
++
++ if (i_size_read(file->f_dentry->d_inode)
++ < pos + sizeof(inode->i_generation)) {
++ inode->i_generation = atomic_inc_return(&sbinfo->si_xigen_next);
++ sz = xino_fwrite(sbinfo->si_xwrite, file, &inode->i_generation,
++ sizeof(inode->i_generation), &pos);
++ } else
++ sz = xino_fread(sbinfo->si_xread, file, &inode->i_generation,
++ sizeof(inode->i_generation), &pos);
++ if (sz == sizeof(inode->i_generation))
++ goto out; /* success */
++
++ err = sz;
++ if (unlikely(sz >= 0)) {
++ err = -EIO;
++ AuIOErr("xigen error (%zd)\n", sz);
++ }
++
++ out:
++ return err;
++}
++
++int au_xigen_set(struct super_block *sb, struct file *base)
++{
++ int err;
++ struct au_sbinfo *sbinfo;
++ struct file *file;
++
++ SiMustWriteLock(sb);
++
++ sbinfo = au_sbi(sb);
++ file = au_xino_create2(base, sbinfo->si_xigen);
++ err = PTR_ERR(file);
++ if (IS_ERR(file))
++ goto out;
++ err = 0;
++ if (sbinfo->si_xigen)
++ fput(sbinfo->si_xigen);
++ sbinfo->si_xigen = file;
++
++ out:
++ return err;
++}
++
++void au_xigen_clr(struct super_block *sb)
++{
++ struct au_sbinfo *sbinfo;
++
++ SiMustWriteLock(sb);
++
++ sbinfo = au_sbi(sb);
++ if (sbinfo->si_xigen) {
++ fput(sbinfo->si_xigen);
++ sbinfo->si_xigen = NULL;
++ }
++}
++
++/* ---------------------------------------------------------------------- */
++
++static struct dentry *decode_by_ino(struct super_block *sb, ino_t ino,
++ ino_t dir_ino)
++{
++ struct dentry *dentry, *d;
++ struct inode *inode;
++ unsigned int sigen;
++
++ dentry = NULL;
++ inode = ilookup(sb, ino);
++ if (!inode)
++ goto out;
++
++ dentry = ERR_PTR(-ESTALE);
++ sigen = au_sigen(sb);
++ if (unlikely(is_bad_inode(inode)
++ || IS_DEADDIR(inode)
++ || sigen != au_iigen(inode)))
++ goto out_iput;
++
++ dentry = NULL;
++ if (!dir_ino || S_ISDIR(inode->i_mode))
++ dentry = d_find_alias(inode);
++ else {
++ spin_lock(&dcache_lock);
++ list_for_each_entry(d, &inode->i_dentry, d_alias)
++ if (!au_test_anon(d)
++ && d->d_parent->d_inode->i_ino == dir_ino) {
++ dentry = dget_locked(d);
++ break;
++ }
++ spin_unlock(&dcache_lock);
++ }
++ if (unlikely(dentry && sigen != au_digen(dentry))) {
++ dput(dentry);
++ dentry = ERR_PTR(-ESTALE);
++ }
++
++ out_iput:
++ iput(inode);
++ out:
++ return dentry;
++}
++
++/* ---------------------------------------------------------------------- */
++
++/* todo: dirty? */
++/* if exportfs_decode_fh() passed vfsmount*, we could be happy */
++static struct vfsmount *au_mnt_get(struct super_block *sb)
++{
++ struct mnt_namespace *ns;
++ struct vfsmount *pos, *mnt;
++
++ spin_lock(&vfsmount_lock);
++ /* no get/put ?? */
++ AuDebugOn(!current->nsproxy);
++ ns = current->nsproxy->mnt_ns;
++ AuDebugOn(!ns);
++ mnt = NULL;
++ /* the order (reverse) will not be a problem */
++ list_for_each_entry(pos, &ns->list, mnt_list)
++ if (pos->mnt_sb == sb) {
++ mnt = mntget(pos);
++ break;
++ }
++ spin_unlock(&vfsmount_lock);
++ AuDebugOn(!mnt);
++
++ return mnt;
++}
++
++struct au_nfsd_si_lock {
++ const unsigned int sigen;
++ const aufs_bindex_t br_id;
++ unsigned char force_lock;
++};
++
++static aufs_bindex_t si_nfsd_read_lock(struct super_block *sb,
++ struct au_nfsd_si_lock *nsi_lock)
++{
++ aufs_bindex_t bindex;
++
++ si_read_lock(sb, AuLock_FLUSH);
++
++ /* branch id may be wrapped around */
++ bindex = au_br_index(sb, nsi_lock->br_id);
++ if (bindex >= 0 && nsi_lock->sigen + AUFS_BRANCH_MAX > au_sigen(sb))
++ goto out; /* success */
++
++ if (!nsi_lock->force_lock)
++ si_read_unlock(sb);
++ bindex = -1;
++
++ out:
++ return bindex;
++}
++
++struct find_name_by_ino {
++ int called, found;
++ ino_t ino;
++ char *name;
++ int namelen;
++};
++
++static int
++find_name_by_ino(void *arg, const char *name, int namelen, loff_t offset,
++ u64 ino, unsigned int d_type)
++{
++ struct find_name_by_ino *a = arg;
++
++ a->called++;
++ if (a->ino != ino)
++ return 0;
++
++ memcpy(a->name, name, namelen);
++ a->namelen = namelen;
++ a->found = 1;
++ return 1;
++}
++
++static struct dentry *au_lkup_by_ino(struct path *path, ino_t ino,
++ struct au_nfsd_si_lock *nsi_lock)
++{
++ struct dentry *dentry, *parent;
++ struct file *file;
++ struct inode *dir;
++ struct find_name_by_ino arg;
++ int err;
++
++ parent = path->dentry;
++ if (nsi_lock)
++ si_read_unlock(parent->d_sb);
++ path_get(path);
++ file = dentry_open(parent, path->mnt, au_dir_roflags, current_cred());
++ dentry = (void *)file;
++ if (IS_ERR(file))
++ goto out;
++
++ dentry = ERR_PTR(-ENOMEM);
++ arg.name = __getname();
++ if (unlikely(!arg.name))
++ goto out_file;
++ arg.ino = ino;
++ arg.found = 0;
++ do {
++ arg.called = 0;
++ /* smp_mb(); */
++ err = vfsub_readdir(file, find_name_by_ino, &arg);
++ } while (!err && !arg.found && arg.called);
++ dentry = ERR_PTR(err);
++ if (unlikely(err))
++ goto out_name;
++ dentry = ERR_PTR(-ENOENT);
++ if (!arg.found)
++ goto out_name;
++
++ /* do not call au_lkup_one() */
++ dir = parent->d_inode;
++ mutex_lock(&dir->i_mutex);
++ dentry = vfsub_lookup_one_len(arg.name, parent, arg.namelen);
++ mutex_unlock(&dir->i_mutex);
++ AuTraceErrPtr(dentry);
++ if (IS_ERR(dentry))
++ goto out_name;
++ AuDebugOn(au_test_anon(dentry));
++ if (unlikely(!dentry->d_inode)) {
++ dput(dentry);
++ dentry = ERR_PTR(-ENOENT);
++ }
++
++ out_name:
++ __putname(arg.name);
++ out_file:
++ fput(file);
++ out:
++ if (unlikely(nsi_lock
++ && si_nfsd_read_lock(parent->d_sb, nsi_lock) < 0))
++ if (!IS_ERR(dentry)) {
++ dput(dentry);
++ dentry = ERR_PTR(-ESTALE);
++ }
++ AuTraceErrPtr(dentry);
++ return dentry;
++}
++
++static struct dentry *decode_by_dir_ino(struct super_block *sb, ino_t ino,
++ ino_t dir_ino,
++ struct au_nfsd_si_lock *nsi_lock)
++{
++ struct dentry *dentry;
++ struct path path;
++
++ if (dir_ino != AUFS_ROOT_INO) {
++ path.dentry = decode_by_ino(sb, dir_ino, 0);
++ dentry = path.dentry;
++ if (!path.dentry || IS_ERR(path.dentry))
++ goto out;
++ AuDebugOn(au_test_anon(path.dentry));
++ } else
++ path.dentry = dget(sb->s_root);
++
++ path.mnt = au_mnt_get(sb);
++ dentry = au_lkup_by_ino(&path, ino, nsi_lock);
++ path_put(&path);
++
++ out:
++ AuTraceErrPtr(dentry);
++ return dentry;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static int h_acceptable(void *expv, struct dentry *dentry)
++{
++ return 1;
++}
++
++static char *au_build_path(struct dentry *h_parent, struct path *h_rootpath,
++ char *buf, int len, struct super_block *sb)
++{
++ char *p;
++ int n;
++ struct path path;
++
++ p = d_path(h_rootpath, buf, len);
++ if (IS_ERR(p))
++ goto out;
++ n = strlen(p);
++
++ path.mnt = h_rootpath->mnt;
++ path.dentry = h_parent;
++ p = d_path(&path, buf, len);
++ if (IS_ERR(p))
++ goto out;
++ if (n != 1)
++ p += n;
++
++ path.mnt = au_mnt_get(sb);
++ path.dentry = sb->s_root;
++ p = d_path(&path, buf, len - strlen(p));
++ mntput(path.mnt);
++ if (IS_ERR(p))
++ goto out;
++ if (n != 1)
++ p[strlen(p)] = '/';
++
++ out:
++ AuTraceErrPtr(p);
++ return p;
++}
++
++static
++struct dentry *decode_by_path(struct super_block *sb, aufs_bindex_t bindex,
++ ino_t ino, __u32 *fh, int fh_len,
++ struct au_nfsd_si_lock *nsi_lock)
++{
++ struct dentry *dentry, *h_parent, *root;
++ struct super_block *h_sb;
++ char *pathname, *p;
++ struct vfsmount *h_mnt;
++ struct au_branch *br;
++ int err;
++ struct path path;
++
++ br = au_sbr(sb, bindex);
++ /* au_br_get(br); */
++ h_mnt = br->br_mnt;
++ h_sb = h_mnt->mnt_sb;
++ /* todo: call lower fh_to_dentry()? fh_to_parent()? */
++ h_parent = exportfs_decode_fh(h_mnt, (void *)(fh + Fh_tail),
++ fh_len - Fh_tail, fh[Fh_h_type],
++ h_acceptable, /*context*/NULL);
++ dentry = h_parent;
++ if (unlikely(!h_parent || IS_ERR(h_parent))) {
++ AuWarn1("%s decode_fh failed, %ld\n",
++ au_sbtype(h_sb), PTR_ERR(h_parent));
++ goto out;
++ }
++ dentry = NULL;
++ if (unlikely(au_test_anon(h_parent))) {
++ AuWarn1("%s decode_fh returned a disconnected dentry\n",
++ au_sbtype(h_sb));
++ goto out_h_parent;
++ }
++
++ dentry = ERR_PTR(-ENOMEM);
++ pathname = (void *)__get_free_page(GFP_NOFS);
++ if (unlikely(!pathname))
++ goto out_h_parent;
++
++ root = sb->s_root;
++ path.mnt = h_mnt;
++ di_read_lock_parent(root, !AuLock_IR);
++ path.dentry = au_h_dptr(root, bindex);
++ di_read_unlock(root, !AuLock_IR);
++ p = au_build_path(h_parent, &path, pathname, PAGE_SIZE, sb);
++ dentry = (void *)p;
++ if (IS_ERR(p))
++ goto out_pathname;
++
++ si_read_unlock(sb);
++ err = vfsub_kern_path(p, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path);
++ dentry = ERR_PTR(err);
++ if (unlikely(err))
++ goto out_relock;
++
++ dentry = ERR_PTR(-ENOENT);
++ AuDebugOn(au_test_anon(path.dentry));
++ if (unlikely(!path.dentry->d_inode))
++ goto out_path;
++
++ if (ino != path.dentry->d_inode->i_ino)
++ dentry = au_lkup_by_ino(&path, ino, /*nsi_lock*/NULL);
++ else
++ dentry = dget(path.dentry);
++
++ out_path:
++ path_put(&path);
++ out_relock:
++ if (unlikely(si_nfsd_read_lock(sb, nsi_lock) < 0))
++ if (!IS_ERR(dentry)) {
++ dput(dentry);
++ dentry = ERR_PTR(-ESTALE);
++ }
++ out_pathname:
++ free_page((unsigned long)pathname);
++ out_h_parent:
++ dput(h_parent);
++ out:
++ /* au_br_put(br); */
++ AuTraceErrPtr(dentry);
++ return dentry;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static struct dentry *
++aufs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len,
++ int fh_type)
++{
++ struct dentry *dentry;
++ __u32 *fh = fid->raw;
++ ino_t ino, dir_ino;
++ aufs_bindex_t bindex;
++ struct au_nfsd_si_lock nsi_lock = {
++ .sigen = fh[Fh_sigen],
++ .br_id = fh[Fh_br_id],
++ .force_lock = 0
++ };
++
++ AuDebugOn(fh_len < Fh_tail);
++
++ dentry = ERR_PTR(-ESTALE);
++ /* branch id may be wrapped around */
++ bindex = si_nfsd_read_lock(sb, &nsi_lock);
++ if (unlikely(bindex < 0))
++ goto out;
++ nsi_lock.force_lock = 1;
++
++ /* is this inode still cached? */
++ ino = decode_ino(fh + Fh_ino);
++ AuDebugOn(ino == AUFS_ROOT_INO);
++ dir_ino = decode_ino(fh + Fh_dir_ino);
++ dentry = decode_by_ino(sb, ino, dir_ino);
++ if (IS_ERR(dentry))
++ goto out_unlock;
++ if (dentry)
++ goto accept;
++
++ /* is the parent dir cached? */
++ dentry = decode_by_dir_ino(sb, ino, dir_ino, &nsi_lock);
++ if (IS_ERR(dentry))
++ goto out_unlock;
++ if (dentry)
++ goto accept;
++
++ /* lookup path */
++ dentry = decode_by_path(sb, bindex, ino, fh, fh_len, &nsi_lock);
++ if (IS_ERR(dentry))
++ goto out_unlock;
++ if (unlikely(!dentry))
++ /* todo?: make it ESTALE */
++ goto out_unlock;
++
++ accept:
++ if (dentry->d_inode->i_generation == fh[Fh_igen])
++ goto out_unlock; /* success */
++
++ dput(dentry);
++ dentry = ERR_PTR(-ESTALE);
++ out_unlock:
++ si_read_unlock(sb);
++ out:
++ AuTraceErrPtr(dentry);
++ return dentry;
++}
++
++#if 0 /* reserved for future use */
++/* support subtreecheck option */
++static struct dentry *aufs_fh_to_parent(struct super_block *sb, struct fid *fid,
++ int fh_len, int fh_type)
++{
++ struct dentry *parent;
++ __u32 *fh = fid->raw;
++ ino_t dir_ino;
++
++ dir_ino = decode_ino(fh + Fh_dir_ino);
++ parent = decode_by_ino(sb, dir_ino, 0);
++ if (IS_ERR(parent))
++ goto out;
++ if (!parent)
++ parent = decode_by_path(sb, au_br_index(sb, fh[Fh_br_id]),
++ dir_ino, fh, fh_len);
++
++ out:
++ AuTraceErrPtr(parent);
++ return parent;
++}
++#endif
++
++/* ---------------------------------------------------------------------- */
++
++static int aufs_encode_fh(struct dentry *dentry, __u32 *fh, int *max_len,
++ int connectable)
++{
++ int err;
++ aufs_bindex_t bindex, bend;
++ struct super_block *sb, *h_sb;
++ struct inode *inode;
++ struct dentry *parent, *h_parent;
++ struct au_branch *br;
++
++ AuDebugOn(au_test_anon(dentry));
++
++ parent = NULL;
++ err = -ENOSPC;
++ if (unlikely(*max_len <= Fh_tail)) {
++ AuWarn1("NFSv2 client (max_len %d)?\n", *max_len);
++ goto out;
++ }
++
++ err = FILEID_ROOT;
++ if (IS_ROOT(dentry)) {
++ AuDebugOn(dentry->d_inode->i_ino != AUFS_ROOT_INO);
++ goto out;
++ }
++
++ err = -EIO;
++ h_parent = NULL;
++ sb = dentry->d_sb;
++ aufs_read_lock(dentry, AuLock_FLUSH | AuLock_IR);
++ parent = dget_parent(dentry);
++ di_read_lock_parent(parent, !AuLock_IR);
++ inode = dentry->d_inode;
++ AuDebugOn(!inode);
++#ifdef CONFIG_AUFS_DEBUG
++ if (unlikely(!au_opt_test(au_mntflags(sb), XINO)))
++ AuWarn1("NFS-exporting requires xino\n");
++#endif
++
++ bend = au_dbtaildir(parent);
++ for (bindex = au_dbstart(parent); bindex <= bend; bindex++) {
++ h_parent = au_h_dptr(parent, bindex);
++ if (h_parent) {
++ dget(h_parent);
++ break;
++ }
++ }
++ if (unlikely(!h_parent))
++ goto out_unlock;
++
++ err = -EPERM;
++ br = au_sbr(sb, bindex);
++ h_sb = br->br_mnt->mnt_sb;
++ if (unlikely(!h_sb->s_export_op)) {
++ AuErr1("%s branch is not exportable\n", au_sbtype(h_sb));
++ goto out_dput;
++ }
++
++ fh[Fh_br_id] = br->br_id;
++ fh[Fh_sigen] = au_sigen(sb);
++ encode_ino(fh + Fh_ino, inode->i_ino);
++ encode_ino(fh + Fh_dir_ino, parent->d_inode->i_ino);
++ fh[Fh_igen] = inode->i_generation;
++
++ *max_len -= Fh_tail;
++ fh[Fh_h_type] = exportfs_encode_fh(h_parent, (void *)(fh + Fh_tail),
++ max_len,
++ /*connectable or subtreecheck*/0);
++ err = fh[Fh_h_type];
++ *max_len += Fh_tail;
++ /* todo: macros? */
++ if (err != 255)
++ err = 99;
++ else
++ AuWarn1("%s encode_fh failed\n", au_sbtype(h_sb));
++
++ out_dput:
++ dput(h_parent);
++ out_unlock:
++ di_read_unlock(parent, !AuLock_IR);
++ dput(parent);
++ aufs_read_unlock(dentry, AuLock_IR);
++ out:
++ if (unlikely(err < 0))
++ err = 255;
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static struct export_operations aufs_export_op = {
++ .fh_to_dentry = aufs_fh_to_dentry,
++ /* .fh_to_parent = aufs_fh_to_parent, */
++ .encode_fh = aufs_encode_fh
++};
++
++void au_export_init(struct super_block *sb)
++{
++ struct au_sbinfo *sbinfo;
++ __u32 u;
++
++ sb->s_export_op = &aufs_export_op;
++ sbinfo = au_sbi(sb);
++ sbinfo->si_xigen = NULL;
++ get_random_bytes(&u, sizeof(u));
++ BUILD_BUG_ON(sizeof(u) != sizeof(int));
++ atomic_set(&sbinfo->si_xigen_next, u);
++}
+diff -Nur linux-2.6.31.5.orig/fs/aufs/file.c linux-2.6.31.5/fs/aufs/file.c
+--- linux-2.6.31.5.orig/fs/aufs/file.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/file.c 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,578 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. Okajima
++ *
++ * This program, aufs is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/*
++ * handling file/dir, and address_space operation
++ */
++
++#include <linux/file.h>
++#include <linux/fsnotify.h>
++#include <linux/namei.h>
++#include <linux/pagemap.h>
++#include "aufs.h"
++
++/*
++ * a dirty trick for handling deny_write_access().
++ * because FMODE_EXEC flag is not passed to f_op->open(),
++ * set it to file->private_data temporary.
++ */
++void au_store_oflag(struct nameidata *nd, struct inode *inode)
++{
++ if (nd
++ /* && !(nd->flags & LOOKUP_CONTINUE) */
++ && (nd->flags & LOOKUP_OPEN)
++ && (nd->intent.open.flags & vfsub_fmode_to_uint(FMODE_EXEC))
++ && inode
++ && S_ISREG(inode->i_mode)) {
++ /* suppress a warning in lp64 */
++ unsigned long flags = nd->intent.open.flags;
++ nd->intent.open.file->private_data = (void *)flags;
++ /* smp_mb(); */
++ }
++}
++
++/* drop flags for writing */
++unsigned int au_file_roflags(unsigned int flags)
++{
++ flags &= ~(O_WRONLY | O_RDWR | O_APPEND | O_CREAT | O_TRUNC);
++ flags |= O_RDONLY | O_NOATIME;
++ return flags;
++}
++
++/* common functions to regular file and dir */
++struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags,
++ struct file *file)
++{
++ struct file *h_file;
++ struct dentry *h_dentry;
++ struct inode *h_inode;
++ struct super_block *sb;
++ struct au_branch *br;
++ int err;
++
++ /* a race condition can happen between open and unlink/rmdir */
++ h_file = ERR_PTR(-ENOENT);
++ h_dentry = au_h_dptr(dentry, bindex);
++ if (au_test_nfsd(current) && !h_dentry)
++ goto out;
++ h_inode = h_dentry->d_inode;
++ if (au_test_nfsd(current) && !h_inode)
++ goto out;
++ if (unlikely((!d_unhashed(dentry) && d_unhashed(h_dentry))
++ || !h_inode))
++ goto out;
++
++ sb = dentry->d_sb;
++ br = au_sbr(sb, bindex);
++ h_file = ERR_PTR(-EACCES);
++ if (file && (file->f_mode & FMODE_EXEC)
++ && (br->br_mnt->mnt_flags & MNT_NOEXEC))
++ goto out;
++
++ /* drop flags for writing */
++ if (au_test_ro(sb, bindex, dentry->d_inode))
++ flags = au_file_roflags(flags);
++ flags &= ~O_CREAT;
++ atomic_inc(&br->br_count);
++ h_file = dentry_open(dget(h_dentry), mntget(br->br_mnt), flags,
++ current_cred());
++ if (IS_ERR(h_file))
++ goto out_br;
++
++ if (file && (file->f_mode & FMODE_EXEC)) {
++ h_file->f_mode |= FMODE_EXEC;
++ err = deny_write_access(h_file);
++ if (unlikely(err)) {
++ fput(h_file);
++ h_file = ERR_PTR(err);
++ goto out_br;
++ }
++ }
++ fsnotify_open(h_dentry);
++ goto out; /* success */
++
++ out_br:
++ atomic_dec(&br->br_count);
++ out:
++ return h_file;
++}
++
++int au_do_open(struct file *file, int (*open)(struct file *file, int flags))
++{
++ int err;
++ struct dentry *dentry;
++ struct super_block *sb;
++
++ dentry = file->f_dentry;
++ sb = dentry->d_sb;
++ si_read_lock(sb, AuLock_FLUSH);
++ err = au_finfo_init(file);
++ if (unlikely(err))
++ goto out;
++
++ di_read_lock_child(dentry, AuLock_IR);
++ err = open(file, file->f_flags);
++ di_read_unlock(dentry, AuLock_IR);
++
++ fi_write_unlock(file);
++ if (unlikely(err))
++ au_finfo_fin(file);
++ out:
++ si_read_unlock(sb);
++ return err;
++}
++
++int au_reopen_nondir(struct file *file)
++{
++ int err;
++ aufs_bindex_t bstart, bindex, bend;
++ struct dentry *dentry;
++ struct file *h_file, *h_file_tmp;
++
++ dentry = file->f_dentry;
++ bstart = au_dbstart(dentry);
++ h_file_tmp = NULL;
++ if (au_fbstart(file) == bstart) {
++ h_file = au_h_fptr(file, bstart);
++ if (file->f_mode == h_file->f_mode)
++ return 0; /* success */
++ h_file_tmp = h_file;
++ get_file(h_file_tmp);
++ au_set_h_fptr(file, bstart, NULL);
++ }
++ AuDebugOn(au_fbstart(file) < bstart
++ || au_fi(file)->fi_hfile[0 + bstart].hf_file);
++
++ h_file = au_h_open(dentry, bstart, file->f_flags & ~O_TRUNC, file);
++ err = PTR_ERR(h_file);
++ if (IS_ERR(h_file))
++ goto out; /* todo: close all? */
++
++ err = 0;
++ au_set_fbstart(file, bstart);
++ au_set_h_fptr(file, bstart, h_file);
++ au_update_figen(file);
++ /* todo: necessary? */
++ /* file->f_ra = h_file->f_ra; */
++
++ /* close lower files */
++ bend = au_fbend(file);
++ for (bindex = bstart + 1; bindex <= bend; bindex++)
++ au_set_h_fptr(file, bindex, NULL);
++ au_set_fbend(file, bstart);
++
++ out:
++ if (h_file_tmp)
++ fput(h_file_tmp);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static int au_reopen_wh(struct file *file, aufs_bindex_t btgt,
++ struct dentry *hi_wh)
++{
++ int err;
++ aufs_bindex_t bstart;
++ struct au_dinfo *dinfo;
++ struct dentry *h_dentry;
++
++ dinfo = au_di(file->f_dentry);
++ AuRwMustWriteLock(&dinfo->di_rwsem);
++
++ bstart = dinfo->di_bstart;
++ dinfo->di_bstart = btgt;
++ h_dentry = dinfo->di_hdentry[0 + btgt].hd_dentry;
++ dinfo->di_hdentry[0 + btgt].hd_dentry = hi_wh;
++ err = au_reopen_nondir(file);
++ dinfo->di_hdentry[0 + btgt].hd_dentry = h_dentry;
++ dinfo->di_bstart = bstart;
++
++ return err;
++}
++
++static int au_ready_to_write_wh(struct file *file, loff_t len,
++ aufs_bindex_t bcpup)
++{
++ int err;
++ struct inode *inode;
++ struct dentry *dentry, *hi_wh;
++ struct super_block *sb;
++
++ dentry = file->f_dentry;
++ inode = dentry->d_inode;
++ hi_wh = au_hi_wh(inode, bcpup);
++ if (!hi_wh)
++ err = au_sio_cpup_wh(dentry, bcpup, len, file);
++ else
++ /* already copied-up after unlink */
++ err = au_reopen_wh(file, bcpup, hi_wh);
++
++ sb = dentry->d_sb;
++ if (!err && inode->i_nlink > 1 && au_opt_test(au_mntflags(sb), PLINK))
++ au_plink_append(inode, bcpup, au_h_dptr(dentry, bcpup));
++
++ return err;
++}
++
++/*
++ * prepare the @file for writing.
++ */
++int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin)
++{
++ int err;
++ aufs_bindex_t bstart, bcpup;
++ struct dentry *dentry, *parent, *h_dentry;
++ struct inode *h_inode, *inode;
++ struct super_block *sb;
++
++ dentry = file->f_dentry;
++ sb = dentry->d_sb;
++ bstart = au_fbstart(file);
++ inode = dentry->d_inode;
++ err = au_test_ro(sb, bstart, inode);
++ if (!err && (au_h_fptr(file, bstart)->f_mode & FMODE_WRITE)) {
++ err = au_pin(pin, dentry, bstart, AuOpt_UDBA_NONE, /*flags*/0);
++ goto out;
++ }
++
++ /* need to cpup */
++ parent = dget_parent(dentry);
++ di_write_lock_parent(parent);
++ err = AuWbrCopyup(au_sbi(sb), dentry);
++ bcpup = err;
++ if (unlikely(err < 0))
++ goto out_dgrade;
++ err = 0;
++
++ if (!au_h_dptr(parent, bcpup)) {
++ err = au_cpup_dirs(dentry, bcpup);
++ if (unlikely(err))
++ goto out_dgrade;
++ }
++
++ err = au_pin(pin, dentry, bcpup, AuOpt_UDBA_NONE,
++ AuPin_DI_LOCKED | AuPin_MNT_WRITE);
++ if (unlikely(err))
++ goto out_dgrade;
++
++ h_dentry = au_h_fptr(file, bstart)->f_dentry;
++ h_inode = h_dentry->d_inode;
++ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);
++ if (d_unhashed(dentry) /* || d_unhashed(h_dentry) */
++ /* || !h_inode->i_nlink */) {
++ err = au_ready_to_write_wh(file, len, bcpup);
++ di_downgrade_lock(parent, AuLock_IR);
++ } else {
++ di_downgrade_lock(parent, AuLock_IR);
++ if (!au_h_dptr(dentry, bcpup))
++ err = au_sio_cpup_simple(dentry, bcpup, len,
++ AuCpup_DTIME);
++ if (!err)
++ err = au_reopen_nondir(file);
++ }
++ mutex_unlock(&h_inode->i_mutex);
++
++ if (!err) {
++ au_pin_set_parent_lflag(pin, /*lflag*/0);
++ goto out_dput; /* success */
++ }
++ au_unpin(pin);
++ goto out_unlock;
++
++ out_dgrade:
++ di_downgrade_lock(parent, AuLock_IR);
++ out_unlock:
++ di_read_unlock(parent, AuLock_IR);
++ out_dput:
++ dput(parent);
++ out:
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static int au_file_refresh_by_inode(struct file *file, int *need_reopen)
++{
++ int err;
++ aufs_bindex_t bstart;
++ struct au_pin pin;
++ struct au_finfo *finfo;
++ struct dentry *dentry, *parent, *hi_wh;
++ struct inode *inode;
++ struct super_block *sb;
++
++ FiMustWriteLock(file);
++
++ err = 0;
++ finfo = au_fi(file);
++ dentry = file->f_dentry;
++ sb = dentry->d_sb;
++ inode = dentry->d_inode;
++ bstart = au_ibstart(inode);
++ if (bstart == finfo->fi_bstart)
++ goto out;
++
++ parent = dget_parent(dentry);
++ if (au_test_ro(sb, bstart, inode)) {
++ di_read_lock_parent(parent, !AuLock_IR);
++ err = AuWbrCopyup(au_sbi(sb), dentry);
++ bstart = err;
++ di_read_unlock(parent, !AuLock_IR);
++ if (unlikely(err < 0))
++ goto out_parent;
++ err = 0;
++ }
++
++ di_read_lock_parent(parent, AuLock_IR);
++ hi_wh = au_hi_wh(inode, bstart);
++ if (au_opt_test(au_mntflags(sb), PLINK)
++ && au_plink_test(inode)
++ && !d_unhashed(dentry)) {
++ err = au_test_and_cpup_dirs(dentry, bstart);
++ if (unlikely(err))
++ goto out_unlock;
++
++ /* always superio. */
++ err = au_pin(&pin, dentry, bstart, AuOpt_UDBA_NONE,
++ AuPin_DI_LOCKED | AuPin_MNT_WRITE);
++ if (!err)
++ err = au_sio_cpup_simple(dentry, bstart, -1,
++ AuCpup_DTIME);
++ au_unpin(&pin);
++ } else if (hi_wh) {
++ /* already copied-up after unlink */
++ err = au_reopen_wh(file, bstart, hi_wh);
++ *need_reopen = 0;
++ }
++
++ out_unlock:
++ di_read_unlock(parent, AuLock_IR);
++ out_parent:
++ dput(parent);
++ out:
++ return err;
++}
++
++static void au_do_refresh_file(struct file *file)
++{
++ aufs_bindex_t bindex, bend, new_bindex, brid;
++ struct au_hfile *p, tmp, *q;
++ struct au_finfo *finfo;
++ struct super_block *sb;
++
++ FiMustWriteLock(file);
++
++ sb = file->f_dentry->d_sb;
++ finfo = au_fi(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++) {
++ if (!p->hf_file)
++ continue;
++
++ new_bindex = au_br_index(sb, p->hf_br->br_id);
++ if (new_bindex == bindex)
++ continue;
++ if (new_bindex < 0) {
++ au_set_h_fptr(file, bindex, NULL);
++ continue;
++ }
++
++ /* swap two lower inode, and loop again */
++ q = finfo->fi_hfile + new_bindex;
++ tmp = *q;
++ *q = *p;
++ *p = tmp;
++ if (tmp.hf_file) {
++ bindex--;
++ p--;
++ }
++ }
++
++ p = finfo->fi_hfile;
++ if (!au_test_mmapped(file) && !d_unhashed(file->f_dentry)) {
++ bend = au_sbend(sb);
++ for (finfo->fi_bstart = 0; finfo->fi_bstart <= bend;
++ finfo->fi_bstart++, p++)
++ if (p->hf_file) {
++ if (p->hf_file->f_dentry
++ && p->hf_file->f_dentry->d_inode)
++ break;
++ else
++ au_hfput(p, file);
++ }
++ } else {
++ bend = au_br_index(sb, brid);
++ for (finfo->fi_bstart = 0; finfo->fi_bstart < bend;
++ finfo->fi_bstart++, p++)
++ if (p->hf_file)
++ au_hfput(p, file);
++ bend = au_sbend(sb);
++ }
++
++ p = finfo->fi_hfile + bend;
++ for (finfo->fi_bend = bend; finfo->fi_bend >= finfo->fi_bstart;
++ finfo->fi_bend--, p--)
++ if (p->hf_file) {
++ if (p->hf_file->f_dentry
++ && p->hf_file->f_dentry->d_inode)
++ break;
++ else
++ au_hfput(p, file);
++ }
++ AuDebugOn(finfo->fi_bend < finfo->fi_bstart);
++}
++
++/*
++ * after branch manipulating, refresh the file.
++ */
++static int refresh_file(struct file *file, int (*reopen)(struct file *file))
++{
++ int err, need_reopen;
++ struct dentry *dentry;
++ aufs_bindex_t bend, bindex;
++
++ dentry = file->f_dentry;
++ err = au_fi_realloc(au_fi(file), au_sbend(dentry->d_sb) + 1);
++ if (unlikely(err))
++ goto out;
++ au_do_refresh_file(file);
++
++ err = 0;
++ need_reopen = 1;
++ if (!au_test_mmapped(file))
++ err = au_file_refresh_by_inode(file, &need_reopen);
++ if (!err && need_reopen && !d_unhashed(dentry))
++ err = reopen(file);
++ if (!err) {
++ au_update_figen(file);
++ return 0; /* success */
++ }
++
++ /* error, close all lower files */
++ bend = au_fbend(file);
++ for (bindex = au_fbstart(file); bindex <= bend; bindex++)
++ au_set_h_fptr(file, bindex, NULL);
++
++ out:
++ return err;
++}
++
++/* common function to regular file and dir */
++int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file),
++ int wlock)
++{
++ int err;
++ unsigned int sigen, figen;
++ aufs_bindex_t bstart;
++ unsigned char pseudo_link;
++ struct dentry *dentry;
++
++ err = 0;
++ dentry = file->f_dentry;
++ sigen = au_sigen(dentry->d_sb);
++ fi_write_lock(file);
++ figen = au_figen(file);
++ di_write_lock_child(dentry);
++ bstart = au_dbstart(dentry);
++ pseudo_link = (bstart != au_ibstart(dentry->d_inode));
++ if (sigen == figen && !pseudo_link && au_fbstart(file) == bstart) {
++ if (!wlock) {
++ di_downgrade_lock(dentry, AuLock_IR);
++ fi_downgrade_lock(file);
++ }
++ goto out; /* success */
++ }
++
++ AuDbg("sigen %d, figen %d\n", sigen, figen);
++ if (sigen != au_digen(dentry)
++ || sigen != au_iigen(dentry->d_inode)) {
++ err = au_reval_dpath(dentry, sigen);
++ if (unlikely(err < 0))
++ goto out;
++ AuDebugOn(au_digen(dentry) != sigen
++ || au_iigen(dentry->d_inode) != sigen);
++ }
++
++ err = refresh_file(file, reopen);
++ if (!err) {
++ if (!wlock) {
++ di_downgrade_lock(dentry, AuLock_IR);
++ fi_downgrade_lock(file);
++ }
++ } else {
++ di_write_unlock(dentry);
++ fi_write_unlock(file);
++ }
++
++ out:
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++/* cf. aufs_nopage() */
++/* for madvise(2) */
++static int aufs_readpage(struct file *file __maybe_unused, struct page *page)
++{
++ unlock_page(page);
++ return 0;
++}
++
++/* they will never be called. */
++#ifdef CONFIG_AUFS_DEBUG
++static int aufs_write_begin(struct file *file, struct address_space *mapping,
++ loff_t pos, unsigned len, unsigned flags,
++ struct page **pagep, void **fsdata)
++{ AuUnsupport(); return 0; }
++static int aufs_write_end(struct file *file, struct address_space *mapping,
++ loff_t pos, unsigned len, unsigned copied,
++ struct page *page, void *fsdata)
++{ AuUnsupport(); return 0; }
++static int aufs_writepage(struct page *page, struct writeback_control *wbc)
++{ AuUnsupport(); return 0; }
++static void aufs_sync_page(struct page *page)
++{ AuUnsupport(); }
++
++static int aufs_set_page_dirty(struct page *page)
++{ AuUnsupport(); return 0; }
++static void aufs_invalidatepage(struct page *page, unsigned long offset)
++{ AuUnsupport(); }
++static int aufs_releasepage(struct page *page, gfp_t gfp)
++{ AuUnsupport(); return 0; }
++static ssize_t aufs_direct_IO(int rw, struct kiocb *iocb,
++ const struct iovec *iov, loff_t offset,
++ unsigned long nr_segs)
++{ AuUnsupport(); return 0; }
++#endif /* CONFIG_AUFS_DEBUG */
++
++struct address_space_operations aufs_aop = {
++ .readpage = aufs_readpage,
++#ifdef CONFIG_AUFS_DEBUG
++ .writepage = aufs_writepage,
++ .sync_page = aufs_sync_page,
++ .set_page_dirty = aufs_set_page_dirty,
++ .write_begin = aufs_write_begin,
++ .write_end = aufs_write_end,
++ .invalidatepage = aufs_invalidatepage,
++ .releasepage = aufs_releasepage,
++ .direct_IO = aufs_direct_IO,
++#endif /* CONFIG_AUFS_DEBUG */
++};
+diff -Nur linux-2.6.31.5.orig/fs/aufs/file.h linux-2.6.31.5/fs/aufs/file.h
+--- linux-2.6.31.5.orig/fs/aufs/file.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/file.h 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,175 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. Okajima
++ *
++ * This program, aufs is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/*
++ * file operations
++ */
++
++#ifndef __AUFS_FILE_H__
++#define __AUFS_FILE_H__
++
++#ifdef __KERNEL__
++
++#include <linux/fs.h>
++#include <linux/poll.h>
++#include <linux/aufs_type.h>
++#include "rwsem.h"
++
++struct au_branch;
++struct au_hfile {
++ struct file *hf_file;
++ struct au_branch *hf_br;
++};
++
++struct au_vdir;
++struct au_finfo {
++ atomic_t fi_generation;
++
++ struct au_rwsem fi_rwsem;
++ struct au_hfile *fi_hfile;
++ aufs_bindex_t fi_bstart, fi_bend;
++
++ union {
++ /* non-dir only */
++ struct {
++ struct vm_operations_struct *fi_h_vm_ops;
++ struct vm_operations_struct *fi_vm_ops;
++ };
++
++ /* dir only */
++ struct {
++ struct au_vdir *fi_vdir_cache;
++ int fi_maintain_plink;
++ };
++ };
++};
++
++/* ---------------------------------------------------------------------- */
++
++/* file.c */
++extern struct address_space_operations aufs_aop;
++void au_store_oflag(struct nameidata *nd, struct inode *inode);
++unsigned int au_file_roflags(unsigned int flags);
++struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags,
++ struct file *file);
++int au_do_open(struct file *file, int (*open)(struct file *file, int flags));
++int au_reopen_nondir(struct file *file);
++struct au_pin;
++int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin);
++int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file),
++ int wlock);
++
++/* poll.c */
++#ifdef CONFIG_AUFS_POLL
++unsigned int aufs_poll(struct file *file, poll_table *wait);
++#endif
++
++/* f_op.c */
++extern const struct file_operations aufs_file_fop;
++int aufs_flush(struct file *file, fl_owner_t id);
++
++/* finfo.c */
++void au_hfput(struct au_hfile *hf, struct file *file);
++void au_set_h_fptr(struct file *file, aufs_bindex_t bindex,
++ struct file *h_file);
++
++void au_update_figen(struct file *file);
++
++void au_finfo_fin(struct file *file);
++int au_finfo_init(struct file *file);
++int au_fi_realloc(struct au_finfo *finfo, int nbr);
++
++/* ---------------------------------------------------------------------- */
++
++static inline struct au_finfo *au_fi(struct file *file)
++{
++ return file->private_data;
++}
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * fi_read_lock, fi_write_lock,
++ * fi_read_unlock, fi_write_unlock, fi_downgrade_lock
++ */
++AuSimpleRwsemFuncs(fi, struct file *f, &au_fi(f)->fi_rwsem);
++
++#define FiMustNoWaiters(f) AuRwMustNoWaiters(&au_fi(f)->fi_rwsem)
++#define FiMustAnyLock(f) AuRwMustAnyLock(&au_fi(f)->fi_rwsem)
++#define FiMustWriteLock(f) AuRwMustWriteLock(&au_fi(f)->fi_rwsem)
++
++/* ---------------------------------------------------------------------- */
++
++/* todo: hard/soft set? */
++static inline aufs_bindex_t au_fbstart(struct file *file)
++{
++ FiMustAnyLock(file);
++ return au_fi(file)->fi_bstart;
++}
++
++static inline aufs_bindex_t au_fbend(struct file *file)
++{
++ FiMustAnyLock(file);
++ return au_fi(file)->fi_bend;
++}
++
++static inline struct au_vdir *au_fvdir_cache(struct file *file)
++{
++ FiMustAnyLock(file);
++ return au_fi(file)->fi_vdir_cache;
++}
++
++static inline void au_set_fbstart(struct file *file, aufs_bindex_t bindex)
++{
++ FiMustWriteLock(file);
++ au_fi(file)->fi_bstart = bindex;
++}
++
++static inline void au_set_fbend(struct file *file, aufs_bindex_t bindex)
++{
++ FiMustWriteLock(file);
++ au_fi(file)->fi_bend = bindex;
++}
++
++static inline void au_set_fvdir_cache(struct file *file,
++ struct au_vdir *vdir_cache)
++{
++ FiMustWriteLock(file);
++ au_fi(file)->fi_vdir_cache = vdir_cache;
++}
++
++static inline struct file *au_h_fptr(struct file *file, aufs_bindex_t bindex)
++{
++ FiMustAnyLock(file);
++ return au_fi(file)->fi_hfile[0 + bindex].hf_file;
++}
++
++/* todo: memory barrier? */
++static inline unsigned int au_figen(struct file *f)
++{
++ return atomic_read(&au_fi(f)->fi_generation);
++}
++
++static inline int au_test_mmapped(struct file *f)
++{
++ FiMustAnyLock(f);
++ return !!(au_fi(f)->fi_h_vm_ops);
++}
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_FILE_H__ */
+diff -Nur linux-2.6.31.5.orig/fs/aufs/finfo.c linux-2.6.31.5/fs/aufs/finfo.c
+--- linux-2.6.31.5.orig/fs/aufs/finfo.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/finfo.c 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,133 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. Okajima
++ *
++ * This program, aufs is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/*
++ * file private data
++ */
++
++#include <linux/file.h>
++#include "aufs.h"
++
++void au_hfput(struct au_hfile *hf, struct file *file)
++{
++ if (file->f_mode & FMODE_EXEC)
++ allow_write_access(hf->hf_file);
++ fput(hf->hf_file);
++ hf->hf_file = NULL;
++ atomic_dec_return(&hf->hf_br->br_count);
++ hf->hf_br = NULL;
++}
++
++void au_set_h_fptr(struct file *file, aufs_bindex_t bindex, struct file *val)
++{
++ struct au_finfo *finfo = au_fi(file);
++ struct au_hfile *hf;
++
++ hf = finfo->fi_hfile + bindex;
++ if (hf->hf_file)
++ au_hfput(hf, file);
++ if (val) {
++ hf->hf_file = val;
++ hf->hf_br = au_sbr(file->f_dentry->d_sb, bindex);
++ }
++}
++
++void au_update_figen(struct file *file)
++{
++ atomic_set(&au_fi(file)->fi_generation, au_digen(file->f_dentry));
++ /* smp_mb(); */ /* atomic_set */
++}
++
++/* ---------------------------------------------------------------------- */
++
++void au_finfo_fin(struct file *file)
++{
++ struct au_finfo *finfo;
++ aufs_bindex_t bindex, bend;
++
++ fi_write_lock(file);
++ bend = au_fbend(file);
++ bindex = au_fbstart(file);
++ if (bindex >= 0)
++ /*
++ * calls fput() instead of filp_close(),
++ * since no dnotify or lock for the lower file.
++ */
++ for (; bindex <= bend; bindex++)
++ au_set_h_fptr(file, bindex, NULL);
++
++ finfo = au_fi(file);
++ au_dbg_verify_hf(finfo);
++ kfree(finfo->fi_hfile);
++ fi_write_unlock(file);
++ AuRwDestroy(&finfo->fi_rwsem);
++ au_cache_free_finfo(finfo);
++}
++
++int au_finfo_init(struct file *file)
++{
++ struct au_finfo *finfo;
++ struct dentry *dentry;
++ unsigned long ul;
++
++ dentry = file->f_dentry;
++ finfo = au_cache_alloc_finfo();
++ if (unlikely(!finfo))
++ goto out;
++
++ finfo->fi_hfile = kcalloc(au_sbend(dentry->d_sb) + 1,
++ sizeof(*finfo->fi_hfile), GFP_NOFS);
++ if (unlikely(!finfo->fi_hfile))
++ goto out_finfo;
++
++ au_rw_init_wlock(&finfo->fi_rwsem);
++ finfo->fi_bstart = -1;
++ finfo->fi_bend = -1;
++ atomic_set(&finfo->fi_generation, au_digen(dentry));
++ /* smp_mb(); */ /* atomic_set */
++
++ /* cf. au_store_oflag() */
++ /* suppress a warning in lp64 */
++ ul = (unsigned long)file->private_data;
++ file->f_mode |= (vfsub_uint_to_fmode(ul) & FMODE_EXEC);
++ file->private_data = finfo;
++ return 0; /* success */
++
++ out_finfo:
++ au_cache_free_finfo(finfo);
++ out:
++ return -ENOMEM;
++}
++
++int au_fi_realloc(struct au_finfo *finfo, int nbr)
++{
++ int err, sz;
++ struct au_hfile *hfp;
++
++ err = -ENOMEM;
++ sz = sizeof(*hfp) * (finfo->fi_bend + 1);
++ if (!sz)
++ sz = sizeof(*hfp);
++ hfp = au_kzrealloc(finfo->fi_hfile, sz, sizeof(*hfp) * nbr, GFP_NOFS);
++ if (hfp) {
++ finfo->fi_hfile = hfp;
++ err = 0;
++ }
++
++ return err;
++}
+diff -Nur linux-2.6.31.5.orig/fs/aufs/f_op.c linux-2.6.31.5/fs/aufs/f_op.c
+--- linux-2.6.31.5.orig/fs/aufs/f_op.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/f_op.c 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,802 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. Okajima
++ *
++ * This program, aufs is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/*
++ * file and vm operations
++ */
++
++#include <linux/file.h>
++#include <linux/fs_stack.h>
++#include <linux/mm.h>
++#include <linux/security.h>
++#include "aufs.h"
++
++/* common function to regular file and dir */
++int aufs_flush(struct file *file, fl_owner_t id)
++{
++ int err;
++ aufs_bindex_t bindex, bend;
++ struct dentry *dentry;
++ struct file *h_file;
++
++ dentry = file->f_dentry;
++ si_noflush_read_lock(dentry->d_sb);
++ fi_read_lock(file);
++ di_read_lock_child(dentry, AuLock_IW);
++
++ err = 0;
++ bend = au_fbend(file);
++ for (bindex = au_fbstart(file); !err && bindex <= bend; bindex++) {
++ h_file = au_h_fptr(file, bindex);
++ if (!h_file || !h_file->f_op || !h_file->f_op->flush)
++ continue;
++
++ err = h_file->f_op->flush(h_file, id);
++ if (!err)
++ vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL);
++ /*ignore*/
++ }
++ au_cpup_attr_timesizes(dentry->d_inode);
++
++ di_read_unlock(dentry, AuLock_IW);
++ fi_read_unlock(file);
++ si_read_unlock(dentry->d_sb);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static int do_open_nondir(struct file *file, int flags)
++{
++ int err;
++ aufs_bindex_t bindex;
++ struct file *h_file;
++ struct dentry *dentry;
++ struct au_finfo *finfo;
++
++ FiMustWriteLock(file);
++
++ err = 0;
++ dentry = file->f_dentry;
++ finfo = au_fi(file);
++ finfo->fi_h_vm_ops = NULL;
++ finfo->fi_vm_ops = NULL;
++ bindex = au_dbstart(dentry);
++ /* O_TRUNC is processed already */
++ BUG_ON(au_test_ro(dentry->d_sb, bindex, dentry->d_inode)
++ && (flags & O_TRUNC));
++
++ h_file = au_h_open(dentry, bindex, flags, file);
++ if (IS_ERR(h_file))
++ err = PTR_ERR(h_file);
++ else {
++ au_set_fbstart(file, bindex);
++ au_set_fbend(file, bindex);
++ au_set_h_fptr(file, bindex, h_file);
++ au_update_figen(file);
++ /* todo: necessary? */
++ /* file->f_ra = h_file->f_ra; */
++ }
++ return err;
++}
++
++static int aufs_open_nondir(struct inode *inode __maybe_unused,
++ struct file *file)
++{
++ return au_do_open(file, do_open_nondir);
++}
++
++static int aufs_release_nondir(struct inode *inode __maybe_unused,
++ struct file *file)
++{
++ struct super_block *sb = file->f_dentry->d_sb;
++
++ si_noflush_read_lock(sb);
++ kfree(au_fi(file)->fi_vm_ops);
++ au_finfo_fin(file);
++ si_read_unlock(sb);
++ return 0;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static ssize_t aufs_read(struct file *file, char __user *buf, size_t count,
++ loff_t *ppos)
++{
++ ssize_t err;
++ struct dentry *dentry;
++ struct file *h_file;
++ struct super_block *sb;
++
++ dentry = file->f_dentry;
++ sb = dentry->d_sb;
++ si_read_lock(sb, AuLock_FLUSH);
++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0);
++ if (unlikely(err))
++ goto out;
++
++ h_file = au_h_fptr(file, au_fbstart(file));
++ err = vfsub_read_u(h_file, buf, count, ppos);
++ /* todo: necessary? */
++ /* file->f_ra = h_file->f_ra; */
++ fsstack_copy_attr_atime(dentry->d_inode, h_file->f_dentry->d_inode);
++
++ di_read_unlock(dentry, AuLock_IR);
++ fi_read_unlock(file);
++ out:
++ si_read_unlock(sb);
++ return err;
++}
++
++static ssize_t aufs_write(struct file *file, const char __user *ubuf,
++ size_t count, loff_t *ppos)
++{
++ ssize_t err;
++ aufs_bindex_t bstart;
++ struct au_pin pin;
++ struct dentry *dentry;
++ struct inode *inode;
++ struct super_block *sb;
++ struct file *h_file;
++ char __user *buf = (char __user *)ubuf;
++
++ dentry = file->f_dentry;
++ sb = dentry->d_sb;
++ inode = dentry->d_inode;
++ mutex_lock(&inode->i_mutex);
++ si_read_lock(sb, AuLock_FLUSH);
++
++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1);
++ if (unlikely(err))
++ goto out;
++
++ err = au_ready_to_write(file, -1, &pin);
++ di_downgrade_lock(dentry, AuLock_IR);
++ if (unlikely(err))
++ goto out_unlock;
++
++ bstart = au_fbstart(file);
++ h_file = au_h_fptr(file, bstart);
++ au_unpin(&pin);
++ err = vfsub_write_u(h_file, buf, count, ppos);
++ au_cpup_attr_timesizes(inode);
++ inode->i_mode = h_file->f_dentry->d_inode->i_mode;
++
++ out_unlock:
++ di_read_unlock(dentry, AuLock_IR);
++ fi_write_unlock(file);
++ out:
++ si_read_unlock(sb);
++ mutex_unlock(&inode->i_mutex);
++ return err;
++}
++
++static ssize_t aufs_aio_read(struct kiocb *kio, const struct iovec *iov,
++ unsigned long nv, loff_t pos)
++{
++ ssize_t err;
++ struct file *file, *h_file;
++ struct dentry *dentry;
++ struct super_block *sb;
++
++ file = kio->ki_filp;
++ dentry = file->f_dentry;
++ sb = dentry->d_sb;
++ si_read_lock(sb, AuLock_FLUSH);
++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0);
++ if (unlikely(err))
++ goto out;
++
++ err = -ENOSYS;
++ h_file = au_h_fptr(file, au_fbstart(file));
++ if (h_file->f_op && h_file->f_op->aio_read) {
++ err = security_file_permission(h_file, MAY_READ);
++ if (unlikely(err))
++ goto out_unlock;
++ if (!is_sync_kiocb(kio)) {
++ get_file(h_file);
++ fput(file);
++ }
++ kio->ki_filp = h_file;
++ err = h_file->f_op->aio_read(kio, iov, nv, pos);
++ /* todo: necessary? */
++ /* file->f_ra = h_file->f_ra; */
++ fsstack_copy_attr_atime(dentry->d_inode,
++ h_file->f_dentry->d_inode);
++ } else
++ /* currently there is no such fs */
++ WARN_ON_ONCE(h_file->f_op && h_file->f_op->read);
++
++ out_unlock:
++ di_read_unlock(dentry, AuLock_IR);
++ fi_read_unlock(file);
++ out:
++ si_read_unlock(sb);
++ return err;
++}
++
++static ssize_t aufs_aio_write(struct kiocb *kio, const struct iovec *iov,
++ unsigned long nv, loff_t pos)
++{
++ ssize_t err;
++ aufs_bindex_t bstart;
++ struct au_pin pin;
++ struct dentry *dentry;
++ struct inode *inode;
++ struct super_block *sb;
++ struct file *file, *h_file;
++
++ file = kio->ki_filp;
++ dentry = file->f_dentry;
++ sb = dentry->d_sb;
++ inode = dentry->d_inode;
++ mutex_lock(&inode->i_mutex);
++ si_read_lock(sb, AuLock_FLUSH);
++
++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1);
++ if (unlikely(err))
++ goto out;
++
++ err = au_ready_to_write(file, -1, &pin);
++ di_downgrade_lock(dentry, AuLock_IR);
++ if (unlikely(err))
++ goto out_unlock;
++
++ err = -ENOSYS;
++ bstart = au_fbstart(file);
++ h_file = au_h_fptr(file, bstart);
++ au_unpin(&pin);
++ if (h_file->f_op && h_file->f_op->aio_write) {
++ err = security_file_permission(h_file, MAY_WRITE);
++ if (unlikely(err))
++ goto out_unlock;
++ if (!is_sync_kiocb(kio)) {
++ get_file(h_file);
++ fput(file);
++ }
++ kio->ki_filp = h_file;
++ err = h_file->f_op->aio_write(kio, iov, nv, pos);
++ au_cpup_attr_timesizes(inode);
++ inode->i_mode = h_file->f_dentry->d_inode->i_mode;
++ } else
++ /* currently there is no such fs */
++ WARN_ON_ONCE(h_file->f_op && h_file->f_op->write);
++
++ out_unlock:
++ di_read_unlock(dentry, AuLock_IR);
++ fi_write_unlock(file);
++ out:
++ si_read_unlock(sb);
++ mutex_unlock(&inode->i_mutex);
++ return err;
++}
++
++static ssize_t aufs_splice_read(struct file *file, loff_t *ppos,
++ struct pipe_inode_info *pipe, size_t len,
++ unsigned int flags)
++{
++ ssize_t err;
++ struct file *h_file;
++ struct dentry *dentry;
++ struct super_block *sb;
++
++ dentry = file->f_dentry;
++ sb = dentry->d_sb;
++ si_read_lock(sb, AuLock_FLUSH);
++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0);
++ if (unlikely(err))
++ goto out;
++
++ err = -EINVAL;
++ h_file = au_h_fptr(file, au_fbstart(file));
++ if (au_test_loopback_kthread()) {
++ file->f_mapping = h_file->f_mapping;
++ smp_mb(); /* unnecessary? */
++ }
++ err = vfsub_splice_to(h_file, ppos, pipe, len, flags);
++ /* todo: necessasry? */
++ /* file->f_ra = h_file->f_ra; */
++ fsstack_copy_attr_atime(dentry->d_inode, h_file->f_dentry->d_inode);
++
++ di_read_unlock(dentry, AuLock_IR);
++ fi_read_unlock(file);
++
++ out:
++ si_read_unlock(sb);
++ return err;
++}
++
++static ssize_t
++aufs_splice_write(struct pipe_inode_info *pipe, struct file *file, loff_t *ppos,
++ size_t len, unsigned int flags)
++{
++ ssize_t err;
++ struct au_pin pin;
++ struct dentry *dentry;
++ struct inode *inode;
++ struct super_block *sb;
++ struct file *h_file;
++
++ dentry = file->f_dentry;
++ inode = dentry->d_inode;
++ mutex_lock(&inode->i_mutex);
++ sb = dentry->d_sb;
++ si_read_lock(sb, AuLock_FLUSH);
++
++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1);
++ if (unlikely(err))
++ goto out;
++
++ err = au_ready_to_write(file, -1, &pin);
++ di_downgrade_lock(dentry, AuLock_IR);
++ if (unlikely(err))
++ goto out_unlock;
++
++ h_file = au_h_fptr(file, au_fbstart(file));
++ au_unpin(&pin);
++ err = vfsub_splice_from(pipe, h_file, ppos, len, flags);
++ au_cpup_attr_timesizes(inode);
++ inode->i_mode = h_file->f_dentry->d_inode->i_mode;
++
++ out_unlock:
++ di_read_unlock(dentry, AuLock_IR);
++ fi_write_unlock(file);
++ out:
++ si_read_unlock(sb);
++ mutex_unlock(&inode->i_mutex);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static struct file *au_safe_file(struct vm_area_struct *vma)
++{
++ struct file *file;
++
++ file = vma->vm_file;
++ if (file->private_data && au_test_aufs(file->f_dentry->d_sb))
++ return file;
++ return NULL;
++}
++
++static void au_reset_file(struct vm_area_struct *vma, struct file *file)
++{
++ vma->vm_file = file;
++ /* smp_mb(); */ /* flush vm_file */
++}
++
++static int aufs_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
++{
++ int err;
++ static DECLARE_WAIT_QUEUE_HEAD(wq);
++ struct file *file, *h_file;
++ struct au_finfo *finfo;
++
++ /* todo: non-robr mode, user vm_file as it is? */
++ wait_event(wq, (file = au_safe_file(vma)));
++
++ /* do not revalidate, no si lock */
++ finfo = au_fi(file);
++ h_file = finfo->fi_hfile[0 + finfo->fi_bstart].hf_file;
++ AuDebugOn(!h_file || !finfo->fi_h_vm_ops);
++
++ fi_write_lock(file);
++ vma->vm_file = h_file;
++ err = finfo->fi_h_vm_ops->fault(vma, vmf);
++ /* todo: necessary? */
++ /* file->f_ra = h_file->f_ra; */
++ au_reset_file(vma, file);
++ fi_write_unlock(file);
++#if 0 /* def CONFIG_SMP */
++ /* wake_up_nr(&wq, online_cpu - 1); */
++ wake_up_all(&wq);
++#else
++ wake_up(&wq);
++#endif
++
++ return err;
++}
++
++static int aufs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
++{
++ int err;
++ static DECLARE_WAIT_QUEUE_HEAD(wq);
++ struct file *file, *h_file;
++ struct au_finfo *finfo;
++
++ wait_event(wq, (file = au_safe_file(vma)));
++
++ finfo = au_fi(file);
++ h_file = finfo->fi_hfile[0 + finfo->fi_bstart].hf_file;
++ AuDebugOn(!h_file || !finfo->fi_h_vm_ops);
++
++ fi_write_lock(file);
++ vma->vm_file = h_file;
++ err = finfo->fi_h_vm_ops->page_mkwrite(vma, vmf);
++ au_reset_file(vma, file);
++ fi_write_unlock(file);
++ wake_up(&wq);
++
++ return err;
++}
++
++static void aufs_vm_close(struct vm_area_struct *vma)
++{
++ static DECLARE_WAIT_QUEUE_HEAD(wq);
++ struct file *file, *h_file;
++ struct au_finfo *finfo;
++
++ wait_event(wq, (file = au_safe_file(vma)));
++
++ finfo = au_fi(file);
++ h_file = finfo->fi_hfile[0 + finfo->fi_bstart].hf_file;
++ AuDebugOn(!h_file || !finfo->fi_h_vm_ops);
++
++ fi_write_lock(file);
++ vma->vm_file = h_file;
++ finfo->fi_h_vm_ops->close(vma);
++ au_reset_file(vma, file);
++ fi_write_unlock(file);
++ wake_up(&wq);
++}
++
++static struct vm_operations_struct aufs_vm_ops = {
++ /* .close and .page_mkwrite are not set by default */
++ .fault = aufs_fault,
++};
++
++/* ---------------------------------------------------------------------- */
++
++static struct vm_operations_struct *au_vm_ops(struct file *h_file,
++ struct vm_area_struct *vma)
++{
++ struct vm_operations_struct *vm_ops;
++ int err;
++
++ vm_ops = ERR_PTR(-ENODEV);
++ if (!h_file->f_op || !h_file->f_op->mmap)
++ goto out;
++
++ err = h_file->f_op->mmap(h_file, vma);
++ vm_ops = ERR_PTR(err);
++ if (unlikely(err))
++ goto out;
++
++ vm_ops = vma->vm_ops;
++ err = do_munmap(current->mm, vma->vm_start,
++ vma->vm_end - vma->vm_start);
++ if (unlikely(err)) {
++ AuIOErr("failed internal unmapping %.*s, %d\n",
++ AuDLNPair(h_file->f_dentry), err);
++ vm_ops = ERR_PTR(-EIO);
++ }
++
++ out:
++ return vm_ops;
++}
++
++static int au_custom_vm_ops(struct au_finfo *finfo, struct vm_area_struct *vma)
++{
++ int err;
++ struct vm_operations_struct *h_ops;
++
++ AuRwMustAnyLock(&finfo->fi_rwsem);
++
++ err = 0;
++ h_ops = finfo->fi_h_vm_ops;
++ AuDebugOn(!h_ops);
++ if ((!h_ops->page_mkwrite && !h_ops->close)
++ || finfo->fi_vm_ops)
++ goto out;
++
++ err = -ENOMEM;
++ finfo->fi_vm_ops = kmemdup(&aufs_vm_ops, sizeof(aufs_vm_ops), GFP_NOFS);
++ if (unlikely(!finfo->fi_vm_ops))
++ goto out;
++
++ err = 0;
++ if (h_ops->page_mkwrite)
++ finfo->fi_vm_ops->page_mkwrite = aufs_page_mkwrite;
++ if (h_ops->close)
++ finfo->fi_vm_ops->close = aufs_vm_close;
++
++ vma->vm_ops = finfo->fi_vm_ops;
++
++ out:
++ return err;
++}
++
++static int aufs_mmap(struct file *file, struct vm_area_struct *vma)
++{
++ int err;
++ unsigned char wlock, mmapped;
++ struct dentry *dentry;
++ struct super_block *sb;
++ struct file *h_file;
++ struct vm_operations_struct *vm_ops;
++
++ dentry = file->f_dentry;
++ wlock = !!(file->f_mode & FMODE_WRITE) && (vma->vm_flags & VM_SHARED);
++ sb = dentry->d_sb;
++ si_read_lock(sb, AuLock_FLUSH);
++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1);
++ if (unlikely(err))
++ goto out;
++
++ mmapped = !!au_test_mmapped(file);
++ if (wlock) {
++ struct au_pin pin;
++
++ err = au_ready_to_write(file, -1, &pin);
++ di_downgrade_lock(dentry, AuLock_IR);
++ if (unlikely(err))
++ goto out_unlock;
++ au_unpin(&pin);
++ } else
++ di_downgrade_lock(dentry, AuLock_IR);
++
++ h_file = au_h_fptr(file, au_fbstart(file));
++ if (!mmapped && au_test_fs_bad_mapping(h_file->f_dentry->d_sb)) {
++ /*
++ * by this assignment, f_mapping will differs from aufs inode
++ * i_mapping.
++ * if someone else mixes the use of f_dentry->d_inode and
++ * f_mapping->host, then a problem may arise.
++ */
++ file->f_mapping = h_file->f_mapping;
++ }
++
++ vm_ops = NULL;
++ if (!mmapped) {
++ vm_ops = au_vm_ops(h_file, vma);
++ err = PTR_ERR(vm_ops);
++ if (IS_ERR(vm_ops))
++ goto out_unlock;
++ }
++
++ /*
++ * unnecessary to handle MAP_DENYWRITE and deny_write_access()?
++ * currently MAP_DENYWRITE from userspace is ignored, but elf loader
++ * sets it. when FMODE_EXEC is set (by open_exec() or sys_uselib()),
++ * both of the aufs file and the lower file is deny_write_access()-ed.
++ * finally I hope we can skip handlling MAP_DENYWRITE here.
++ */
++ err = generic_file_mmap(file, vma);
++ if (unlikely(err))
++ goto out_unlock;
++
++ vma->vm_ops = &aufs_vm_ops;
++ /* test again */
++ if (!au_test_mmapped(file))
++ au_fi(file)->fi_h_vm_ops = vm_ops;
++
++ err = au_custom_vm_ops(au_fi(file), vma);
++ if (unlikely(err))
++ goto out_unlock;
++
++ vfsub_file_accessed(h_file);
++ fsstack_copy_attr_atime(dentry->d_inode, h_file->f_dentry->d_inode);
++
++ out_unlock:
++ di_read_unlock(dentry, AuLock_IR);
++ fi_write_unlock(file);
++ out:
++ si_read_unlock(sb);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static int aufs_fsync_nondir(struct file *file, struct dentry *dentry,
++ int datasync)
++{
++ int err;
++ struct au_pin pin;
++ struct inode *inode;
++ struct file *h_file;
++ struct super_block *sb;
++
++ inode = dentry->d_inode;
++ IMustLock(file->f_mapping->host);
++ if (inode != file->f_mapping->host) {
++ mutex_unlock(&file->f_mapping->host->i_mutex);
++ mutex_lock(&inode->i_mutex);
++ }
++ IMustLock(inode);
++
++ sb = dentry->d_sb;
++ si_read_lock(sb, AuLock_FLUSH);
++
++ err = 0; /* -EBADF; */ /* posix? */
++ if (unlikely(!(file->f_mode & FMODE_WRITE)))
++ goto out;
++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1);
++ if (unlikely(err))
++ goto out;
++
++ err = au_ready_to_write(file, -1, &pin);
++ di_downgrade_lock(dentry, AuLock_IR);
++ if (unlikely(err))
++ goto out_unlock;
++ au_unpin(&pin);
++
++ err = -EINVAL;
++ h_file = au_h_fptr(file, au_fbstart(file));
++ if (h_file->f_op && h_file->f_op->fsync) {
++ struct dentry *h_d;
++ struct mutex *h_mtx;
++
++ /*
++ * no filemap_fdatawrite() since aufs file has no its own
++ * mapping, but dir.
++ */
++ h_d = h_file->f_dentry;
++ h_mtx = &h_d->d_inode->i_mutex;
++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD);
++ err = h_file->f_op->fsync(h_file, h_d, datasync);
++ if (!err)
++ vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL);
++ /*ignore*/
++ au_cpup_attr_timesizes(inode);
++ mutex_unlock(h_mtx);
++ }
++
++ out_unlock:
++ di_read_unlock(dentry, AuLock_IR);
++ fi_write_unlock(file);
++ out:
++ si_read_unlock(sb);
++ if (inode != file->f_mapping->host) {
++ mutex_unlock(&inode->i_mutex);
++ mutex_lock(&file->f_mapping->host->i_mutex);
++ }
++ return err;
++}
++
++/* no one supports this operation, currently */
++#if 0
++static int aufs_aio_fsync_nondir(struct kiocb *kio, int datasync)
++{
++ int err;
++ struct au_pin pin;
++ struct dentry *dentry;
++ struct inode *inode;
++ struct file *file, *h_file;
++ struct super_block *sb;
++
++ file = kio->ki_filp;
++ dentry = file->f_dentry;
++ inode = dentry->d_inode;
++ mutex_lock(&inode->i_mutex);
++
++ sb = dentry->d_sb;
++ si_read_lock(sb, AuLock_FLUSH);
++
++ err = 0; /* -EBADF; */ /* posix? */
++ if (unlikely(!(file->f_mode & FMODE_WRITE)))
++ goto out;
++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1);
++ if (unlikely(err))
++ goto out;
++
++ err = au_ready_to_write(file, -1, &pin);
++ di_downgrade_lock(dentry, AuLock_IR);
++ if (unlikely(err))
++ goto out_unlock;
++ au_unpin(&pin);
++
++ err = -ENOSYS;
++ h_file = au_h_fptr(file, au_fbstart(file));
++ if (h_file->f_op && h_file->f_op->aio_fsync) {
++ struct dentry *h_d;
++ struct mutex *h_mtx;
++
++ h_d = h_file->f_dentry;
++ h_mtx = &h_d->d_inode->i_mutex;
++ if (!is_sync_kiocb(kio)) {
++ get_file(h_file);
++ fput(file);
++ }
++ kio->ki_filp = h_file;
++ err = h_file->f_op->aio_fsync(kio, datasync);
++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD);
++ if (!err)
++ vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL);
++ /*ignore*/
++ au_cpup_attr_timesizes(inode);
++ mutex_unlock(h_mtx);
++ }
++
++ out_unlock:
++ di_read_unlock(dentry, AuLock_IR);
++ fi_write_unlock(file);
++ out:
++ si_read_unlock(sb);
++ mutex_unlock(&inode->i_mutex);
++ return err;
++}
++#endif
++
++static int aufs_fasync(int fd, struct file *file, int flag)
++{
++ int err;
++ struct file *h_file;
++ struct dentry *dentry;
++ struct super_block *sb;
++
++ dentry = file->f_dentry;
++ sb = dentry->d_sb;
++ si_read_lock(sb, AuLock_FLUSH);
++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0);
++ if (unlikely(err))
++ goto out;
++
++ h_file = au_h_fptr(file, au_fbstart(file));
++ if (h_file->f_op && h_file->f_op->fasync)
++ err = h_file->f_op->fasync(fd, h_file, flag);
++
++ di_read_unlock(dentry, AuLock_IR);
++ fi_read_unlock(file);
++
++ out:
++ si_read_unlock(sb);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++/* no one supports this operation, currently */
++#if 0
++static ssize_t aufs_sendpage(struct file *file, struct page *page, int offset,
++ size_t len, loff_t *pos , int more)
++{
++}
++#endif
++
++/* ---------------------------------------------------------------------- */
++
++const struct file_operations aufs_file_fop = {
++ /*
++ * while generic_file_llseek/_unlocked() don't use BKL,
++ * don't use it since it operates file->f_mapping->host.
++ * in aufs, it may be a real file and may confuse users by UDBA.
++ */
++ /* .llseek = generic_file_llseek, */
++
++ .read = aufs_read,
++ .write = aufs_write,
++ .aio_read = aufs_aio_read,
++ .aio_write = aufs_aio_write,
++#ifdef CONFIG_AUFS_POLL
++ .poll = aufs_poll,
++#endif
++ .mmap = aufs_mmap,
++ .open = aufs_open_nondir,
++ .flush = aufs_flush,
++ .release = aufs_release_nondir,
++ .fsync = aufs_fsync_nondir,
++ /* .aio_fsync = aufs_aio_fsync_nondir, */
++ .fasync = aufs_fasync,
++ /* .sendpage = aufs_sendpage, */
++ .splice_write = aufs_splice_write,
++ .splice_read = aufs_splice_read,
++#if 0
++ .aio_splice_write = aufs_aio_splice_write,
++ .aio_splice_read = aufs_aio_splice_read
++#endif
++};
+diff -Nur linux-2.6.31.5.orig/fs/aufs/fstype.h linux-2.6.31.5/fs/aufs/fstype.h
+--- linux-2.6.31.5.orig/fs/aufs/fstype.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/fstype.h 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,474 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. 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
++ */
++
++/*
++ * judging filesystem type
++ */
++
++#ifndef __AUFS_FSTYPE_H__
++#define __AUFS_FSTYPE_H__
++
++#ifdef __KERNEL__
++
++#include <linux/cramfs_fs.h>
++#include <linux/fs.h>
++#include <linux/magic.h>
++#include <linux/romfs_fs.h>
++#include <linux/aufs_type.h>
++
++static inline int au_test_aufs(struct super_block *sb)
++{
++ return sb->s_magic == AUFS_SUPER_MAGIC;
++}
++
++static inline const char *au_sbtype(struct super_block *sb)
++{
++ return sb->s_type->name;
++}
++
++static inline int au_test_iso9660(struct super_block *sb __maybe_unused)
++{
++#if defined(CONFIG_ROMFS_FS) || defined(CONFIG_ROMFS_FS_MODULE)
++ return sb->s_magic == ROMFS_MAGIC;
++#else
++ return 0;
++#endif
++}
++
++static inline int au_test_romfs(struct super_block *sb __maybe_unused)
++{
++#if defined(CONFIG_ISO9660_FS) || defined(CONFIG_ISO9660_FS_MODULE)
++ return sb->s_magic == ISOFS_SUPER_MAGIC;
++#else
++ return 0;
++#endif
++}
++
++static inline int au_test_cramfs(struct super_block *sb __maybe_unused)
++{
++#if defined(CONFIG_CRAMFS) || defined(CONFIG_CRAMFS_MODULE)
++ return sb->s_magic == CRAMFS_MAGIC;
++#endif
++ return 0;
++}
++
++static inline int au_test_nfs(struct super_block *sb __maybe_unused)
++{
++#if defined(CONFIG_NFS_FS) || defined(CONFIG_NFS_FS_MODULE)
++ return sb->s_magic == NFS_SUPER_MAGIC;
++#else
++ return 0;
++#endif
++}
++
++static inline int au_test_fuse(struct super_block *sb __maybe_unused)
++{
++#if defined(CONFIG_FUSE_FS) || defined(CONFIG_FUSE_FS_MODULE)
++ return sb->s_magic == FUSE_SUPER_MAGIC;
++#else
++ return 0;
++#endif
++}
++
++static inline int au_test_xfs(struct super_block *sb __maybe_unused)
++{
++#if defined(CONFIG_XFS_FS) || defined(CONFIG_XFS_FS_MODULE)
++ return sb->s_magic == XFS_SB_MAGIC;
++#else
++ return 0;
++#endif
++}
++
++static inline int au_test_tmpfs(struct super_block *sb __maybe_unused)
++{
++#ifdef CONFIG_TMPFS
++ return sb->s_magic == TMPFS_MAGIC;
++#else
++ return 0;
++#endif
++}
++
++static inline int au_test_ecryptfs(struct super_block *sb __maybe_unused)
++{
++#if defined(CONFIG_ECRYPT_FS) || defined(CONFIG_ECRYPT_FS_MODULE)
++ return !strcmp(au_sbtype(sb), "ecryptfs");
++#else
++ return 0;
++#endif
++}
++
++static inline int au_test_smbfs(struct super_block *sb __maybe_unused)
++{
++#if defined(CONFIG_SMB_FS) || defined(CONFIG_SMB_FS_MODULE)
++ return sb->s_magic == SMB_SUPER_MAGIC;
++#else
++ return 0;
++#endif
++}
++
++static inline int au_test_ocfs2(struct super_block *sb __maybe_unused)
++{
++#if defined(CONFIG_OCFS2_FS) || defined(CONFIG_OCFS2_FS_MODULE)
++ return sb->s_magic == OCFS2_SUPER_MAGIC;
++#else
++ return 0;
++#endif
++}
++
++static inline int au_test_ocfs2_dlmfs(struct super_block *sb __maybe_unused)
++{
++#if defined(CONFIG_OCFS2_FS_O2CB) || defined(CONFIG_OCFS2_FS_O2CB_MODULE)
++ return sb->s_magic == DLMFS_MAGIC;
++#else
++ return 0;
++#endif
++}
++
++static inline int au_test_coda(struct super_block *sb __maybe_unused)
++{
++#if defined(CONFIG_CODA_FS) || defined(CONFIG_CODA_FS_MODULE)
++ return sb->s_magic == CODA_SUPER_MAGIC;
++#else
++ return 0;
++#endif
++}
++
++static inline int au_test_v9fs(struct super_block *sb __maybe_unused)
++{
++#if defined(CONFIG_9P_FS) || defined(CONFIG_9P_FS_MODULE)
++ return sb->s_magic == V9FS_MAGIC;
++#else
++ return 0;
++#endif
++}
++
++static inline int au_test_ext4(struct super_block *sb __maybe_unused)
++{
++#if defined(CONFIG_EXT4DEV_FS) || defined(CONFIG_EXT4DEV_FS_MODULE)
++ return sb->s_magic == EXT4_SUPER_MAGIC;
++#else
++ return 0;
++#endif
++}
++
++static inline int au_test_sysv(struct super_block *sb __maybe_unused)
++{
++#if defined(CONFIG_SYSV_FS) || defined(CONFIG_SYSV_FS_MODULE)
++ return !strcmp(au_sbtype(sb), "sysv");
++#else
++ return 0;
++#endif
++}
++
++static inline int au_test_ramfs(struct super_block *sb)
++{
++ return sb->s_magic == RAMFS_MAGIC;
++}
++
++static inline int au_test_ubifs(struct super_block *sb __maybe_unused)
++{
++#if defined(CONFIG_UBIFS_FS) || defined(CONFIG_UBIFS_FS_MODULE)
++ return sb->s_magic == UBIFS_SUPER_MAGIC;
++#else
++ return 0;
++#endif
++}
++
++static inline int au_test_procfs(struct super_block *sb __maybe_unused)
++{
++#ifdef CONFIG_PROC_FS
++ return sb->s_magic == PROC_SUPER_MAGIC;
++#else
++ return 0;
++#endif
++}
++
++static inline int au_test_sysfs(struct super_block *sb __maybe_unused)
++{
++#ifdef CONFIG_SYSFS
++ return sb->s_magic == SYSFS_MAGIC;
++#else
++ return 0;
++#endif
++}
++
++static inline int au_test_configfs(struct super_block *sb __maybe_unused)
++{
++#if defined(CONFIG_CONFIGFS_FS) || defined(CONFIG_CONFIGFS_FS_MODULE)
++ return sb->s_magic == CONFIGFS_MAGIC;
++#else
++ return 0;
++#endif
++}
++
++static inline int au_test_minix(struct super_block *sb __maybe_unused)
++{
++#if defined(CONFIG_MINIX_FS) || defined(CONFIG_MINIX_FS_MODULE)
++ return sb->s_magic == MINIX3_SUPER_MAGIC
++ || sb->s_magic == MINIX2_SUPER_MAGIC
++ || sb->s_magic == MINIX2_SUPER_MAGIC2
++ || sb->s_magic == MINIX_SUPER_MAGIC
++ || sb->s_magic == MINIX_SUPER_MAGIC2;
++#else
++ return 0;
++#endif
++}
++
++static inline int au_test_cifs(struct super_block *sb __maybe_unused)
++{
++#if defined(CONFIG_CIFS_FS) || defined(CONFIGCIFS_FS_MODULE)
++ return sb->s_magic == CIFS_MAGIC_NUMBER;
++#else
++ return 0;
++#endif
++}
++
++static inline int au_test_fat(struct super_block *sb __maybe_unused)
++{
++#if defined(CONFIG_FAT_FS) || defined(CONFIG_FAT_FS_MODULE)
++ return sb->s_magic == MSDOS_SUPER_MAGIC;
++#else
++ return 0;
++#endif
++}
++
++static inline int au_test_msdos(struct super_block *sb)
++{
++ return au_test_fat(sb);
++}
++
++static inline int au_test_vfat(struct super_block *sb)
++{
++ return au_test_fat(sb);
++}
++
++static inline int au_test_securityfs(struct super_block *sb __maybe_unused)
++{
++#ifdef CONFIG_SECURITYFS
++ return sb->s_magic == SECURITYFS_MAGIC;
++#else
++ return 0;
++#endif
++}
++
++static inline int au_test_squashfs(struct super_block *sb __maybe_unused)
++{
++#if defined(CONFIG_SQUASHFS) || defined(CONFIG_SQUASHFS_MODULE)
++ return sb->s_magic == SQUASHFS_MAGIC;
++#else
++ return 0;
++#endif
++}
++
++static inline int au_test_btrfs(struct super_block *sb __maybe_unused)
++{
++#if defined(CONFIG_BTRFS_FS) || defined(CONFIG_BTRFS_FS_MODULE)
++ return sb->s_magic == BTRFS_SUPER_MAGIC;
++#else
++ return 0;
++#endif
++}
++
++static inline int au_test_xenfs(struct super_block *sb __maybe_unused)
++{
++#if defined(CONFIG_XENFS) || defined(CONFIG_XENFS_MODULE)
++ return sb->s_magic == XENFS_SUPER_MAGIC;
++#else
++ return 0;
++#endif
++}
++
++static inline int au_test_debugfs(struct super_block *sb __maybe_unused)
++{
++#ifdef CONFIG_DEBUG_FS
++ return sb->s_magic == DEBUGFS_MAGIC;
++#else
++ return 0;
++#endif
++}
++
++/* ---------------------------------------------------------------------- */
++/*
++ * they can't be an aufs branch.
++ */
++static inline int au_test_fs_unsuppoted(struct super_block *sb)
++{
++ return
++#ifndef CONFIG_AUFS_BR_RAMFS
++ au_test_ramfs(sb) ||
++#endif
++ au_test_procfs(sb)
++ || au_test_sysfs(sb)
++ || au_test_configfs(sb)
++ || au_test_debugfs(sb)
++ || au_test_securityfs(sb)
++ || au_test_xenfs(sb)
++ /* || !strcmp(au_sbtype(sb), "unionfs") */
++ || au_test_aufs(sb); /* will be supported in next version */
++}
++
++/*
++ * If the filesystem supports NFS-export, then it has to support NULL as
++ * a nameidata parameter for ->create(), ->lookup() and ->d_revalidate().
++ * We can apply this principle when we handle a lower filesystem.
++ */
++static inline int au_test_fs_null_nd(struct super_block *sb)
++{
++ return !!sb->s_export_op;
++}
++
++static inline int au_test_fs_remote(struct super_block *sb)
++{
++ return !au_test_tmpfs(sb)
++#ifdef CONFIG_AUFS_BR_RAMFS
++ && !au_test_ramfs(sb)
++#endif
++ && !(sb->s_type->fs_flags & FS_REQUIRES_DEV);
++}
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * Note: these functions (below) are created after reading ->getattr() in all
++ * filesystems under linux/fs. it means we have to do so in every update...
++ */
++
++/*
++ * some filesystems require getattr to refresh the inode attributes before
++ * referencing.
++ * in most cases, we can rely on the inode attribute in NFS (or every remote fs)
++ * and leave the work for d_revalidate()
++ */
++static inline int au_test_fs_refresh_iattr(struct super_block *sb)
++{
++ return au_test_nfs(sb)
++ || au_test_fuse(sb)
++ /* || au_test_smbfs(sb) */ /* untested */
++ /* || au_test_ocfs2(sb) */ /* untested */
++ /* || au_test_btrfs(sb) */ /* untested */
++ /* || au_test_coda(sb) */ /* untested */
++ /* || au_test_v9fs(sb) */ /* untested */
++ ;
++}
++
++/*
++ * filesystems which don't maintain i_size or i_blocks.
++ */
++static inline int au_test_fs_bad_iattr_size(struct super_block *sb)
++{
++ return au_test_xfs(sb)
++ /* || au_test_ext4(sb) */ /* untested */
++ /* || au_test_ocfs2(sb) */ /* untested */
++ /* || au_test_ocfs2_dlmfs(sb) */ /* untested */
++ /* || au_test_sysv(sb) */ /* untested */
++ /* || au_test_ubifs(sb) */ /* untested */
++ /* || au_test_minix(sb) */ /* untested */
++ ;
++}
++
++/*
++ * filesystems which don't store the correct value in some of their inode
++ * attributes.
++ */
++static inline int au_test_fs_bad_iattr(struct super_block *sb)
++{
++ return au_test_fs_bad_iattr_size(sb)
++ /* || au_test_cifs(sb) */ /* untested */
++ || au_test_fat(sb)
++ || au_test_msdos(sb)
++ || au_test_vfat(sb);
++}
++
++/* they don't check i_nlink in link(2) */
++static inline int au_test_fs_no_limit_nlink(struct super_block *sb)
++{
++ return au_test_tmpfs(sb)
++#ifdef CONFIG_AUFS_BR_RAMFS
++ || au_test_ramfs(sb)
++#endif
++ || au_test_ubifs(sb);
++}
++
++/*
++ * filesystems which sets S_NOATIME and S_NOCMTIME.
++ */
++static inline int au_test_fs_notime(struct super_block *sb)
++{
++ return au_test_nfs(sb)
++ || au_test_fuse(sb)
++ || au_test_ubifs(sb)
++ /* || au_test_cifs(sb) */ /* untested */
++ ;
++}
++
++/*
++ * filesystems which requires replacing i_mapping.
++ */
++static inline int au_test_fs_bad_mapping(struct super_block *sb)
++{
++ return au_test_fuse(sb)
++ || au_test_ubifs(sb);
++}
++
++/* temporary support for i#1 in cramfs */
++static inline int au_test_fs_unique_ino(struct inode *inode)
++{
++ if (au_test_cramfs(inode->i_sb))
++ return inode->i_ino != 1;
++ return 1;
++}
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * the filesystem where the xino files placed must support i/o after unlink and
++ * maintain i_size and i_blocks.
++ */
++static inline int au_test_fs_bad_xino(struct super_block *sb)
++{
++ return au_test_fs_remote(sb)
++ || au_test_fs_bad_iattr_size(sb)
++#ifdef CONFIG_AUFS_BR_RAMFS
++ || !(au_test_ramfs(sb) || au_test_fs_null_nd(sb))
++#else
++ || !au_test_fs_null_nd(sb) /* to keep xino code simple */
++#endif
++ /* don't want unnecessary work for xino */
++ || au_test_aufs(sb)
++ || au_test_ecryptfs(sb);
++}
++
++static inline int au_test_fs_trunc_xino(struct super_block *sb)
++{
++ return au_test_tmpfs(sb)
++ || au_test_ramfs(sb);
++}
++
++/*
++ * test if the @sb is real-readonly.
++ */
++static inline int au_test_fs_rr(struct super_block *sb)
++{
++ return au_test_squashfs(sb)
++ || au_test_iso9660(sb)
++ || au_test_cramfs(sb)
++ || au_test_romfs(sb);
++}
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_FSTYPE_H__ */
+diff -Nur linux-2.6.31.5.orig/fs/aufs/hinotify.c linux-2.6.31.5/fs/aufs/hinotify.c
+--- linux-2.6.31.5.orig/fs/aufs/hinotify.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/hinotify.c 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,755 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. Okajima
++ *
++ * This program, aufs is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/*
++ * inotify for the lower directories
++ */
++
++#include "aufs.h"
++
++static const __u32 AuHinMask = (IN_MOVE | IN_DELETE | IN_CREATE);
++static struct inotify_handle *au_hin_handle;
++
++AuCacheFuncs(hinotify, HINOTIFY);
++
++int au_hin_alloc(struct au_hinode *hinode, struct inode *inode,
++ struct inode *h_inode)
++{
++ int err;
++ struct au_hinotify *hin;
++ s32 wd;
++
++ err = -ENOMEM;
++ hin = au_cache_alloc_hinotify();
++ if (hin) {
++ AuDebugOn(hinode->hi_notify);
++ hinode->hi_notify = hin;
++ hin->hin_aufs_inode = inode;
++
++ inotify_init_watch(&hin->hin_watch);
++ wd = inotify_add_watch(au_hin_handle, &hin->hin_watch, h_inode,
++ AuHinMask);
++ if (wd >= 0)
++ return 0; /* success */
++
++ err = wd;
++ put_inotify_watch(&hin->hin_watch);
++ au_cache_free_hinotify(hin);
++ hinode->hi_notify = NULL;
++ }
++
++ return err;
++}
++
++void au_hin_free(struct au_hinode *hinode)
++{
++ int err;
++ struct au_hinotify *hin;
++
++ hin = hinode->hi_notify;
++ if (hin) {
++ err = 0;
++ if (atomic_read(&hin->hin_watch.count))
++ err = inotify_rm_watch(au_hin_handle, &hin->hin_watch);
++ if (unlikely(err))
++ /* it means the watch is already removed */
++ AuWarn("failed inotify_rm_watch() %d\n", err);
++ au_cache_free_hinotify(hin);
++ hinode->hi_notify = NULL;
++ }
++}
++
++/* ---------------------------------------------------------------------- */
++
++void au_hin_ctl(struct au_hinode *hinode, int do_set)
++{
++ struct inode *h_inode;
++ struct inotify_watch *watch;
++
++ if (!hinode->hi_notify)
++ return;
++
++ h_inode = hinode->hi_inode;
++ IMustLock(h_inode);
++
++ /* todo: try inotify_find_update_watch()? */
++ watch = &hinode->hi_notify->hin_watch;
++ mutex_lock(&h_inode->inotify_mutex);
++ /* mutex_lock(&watch->ih->mutex); */
++ if (do_set) {
++ AuDebugOn(watch->mask & AuHinMask);
++ watch->mask |= AuHinMask;
++ } else {
++ AuDebugOn(!(watch->mask & AuHinMask));
++ watch->mask &= ~AuHinMask;
++ }
++ /* mutex_unlock(&watch->ih->mutex); */
++ mutex_unlock(&h_inode->inotify_mutex);
++}
++
++void au_reset_hinotify(struct inode *inode, unsigned int flags)
++{
++ aufs_bindex_t bindex, bend;
++ struct inode *hi;
++ struct dentry *iwhdentry;
++
++ bend = au_ibend(inode);
++ for (bindex = au_ibstart(inode); bindex <= bend; bindex++) {
++ hi = au_h_iptr(inode, bindex);
++ if (!hi)
++ continue;
++
++ /* mutex_lock_nested(&hi->i_mutex, AuLsc_I_CHILD); */
++ iwhdentry = au_hi_wh(inode, bindex);
++ if (iwhdentry)
++ dget(iwhdentry);
++ au_igrab(hi);
++ au_set_h_iptr(inode, bindex, NULL, 0);
++ au_set_h_iptr(inode, bindex, au_igrab(hi),
++ flags & ~AuHi_XINO);
++ iput(hi);
++ dput(iwhdentry);
++ /* mutex_unlock(&hi->i_mutex); */
++ }
++}
++
++/* ---------------------------------------------------------------------- */
++
++static int hin_xino(struct inode *inode, struct inode *h_inode)
++{
++ int err;
++ aufs_bindex_t bindex, bend, bfound, bstart;
++ struct inode *h_i;
++
++ err = 0;
++ if (unlikely(inode->i_ino == AUFS_ROOT_INO)) {
++ AuWarn("branch root dir was changed\n");
++ goto out;
++ }
++
++ bfound = -1;
++ bend = au_ibend(inode);
++ bstart = au_ibstart(inode);
++#if 0 /* reserved for future use */
++ if (bindex == bend) {
++ /* keep this ino in rename case */
++ goto out;
++ }
++#endif
++ for (bindex = bstart; bindex <= bend; bindex++) {
++ if (au_h_iptr(inode, bindex) == h_inode) {
++ bfound = bindex;
++ break;
++ }
++ }
++ if (bfound < 0)
++ goto out;
++
++ for (bindex = bstart; bindex <= bend; bindex++) {
++ h_i = au_h_iptr(inode, bindex);
++ if (!h_i)
++ continue;
++
++ err = au_xino_write(inode->i_sb, bindex, h_i->i_ino, /*ino*/0);
++ /* ignore this error */
++ /* bad action? */
++ }
++
++ /* children inode number will be broken */
++
++ out:
++ AuTraceErr(err);
++ return err;
++}
++
++static int hin_gen_tree(struct dentry *dentry)
++{
++ int err, i, j, ndentry;
++ struct au_dcsub_pages dpages;
++ struct au_dpage *dpage;
++ struct dentry **dentries;
++
++ err = au_dpages_init(&dpages, GFP_NOFS);
++ if (unlikely(err))
++ goto out;
++ err = au_dcsub_pages(&dpages, dentry, NULL, NULL);
++ if (unlikely(err))
++ goto out_dpages;
++
++ for (i = 0; i < dpages.ndpage; i++) {
++ dpage = dpages.dpages + i;
++ dentries = dpage->dentries;
++ ndentry = dpage->ndentry;
++ for (j = 0; j < ndentry; j++) {
++ struct dentry *d;
++
++ d = dentries[j];
++ if (IS_ROOT(d))
++ continue;
++
++ d_drop(d);
++ au_digen_dec(d);
++ if (d->d_inode)
++ /* todo: reset children xino?
++ cached children only? */
++ au_iigen_dec(d->d_inode);
++ }
++ }
++
++ out_dpages:
++ au_dpages_free(&dpages);
++
++ /* discard children */
++ dentry_unhash(dentry);
++ dput(dentry);
++ out:
++ return err;
++}
++
++/*
++ * return 0 if processed.
++ */
++static int hin_gen_by_inode(char *name, unsigned int nlen, struct inode *inode,
++ const unsigned int isdir)
++{
++ int err;
++ struct dentry *d;
++ struct qstr *dname;
++
++ err = 1;
++ if (unlikely(inode->i_ino == AUFS_ROOT_INO)) {
++ AuWarn("branch root dir was changed\n");
++ err = 0;
++ goto out;
++ }
++
++ if (!isdir) {
++ AuDebugOn(!name);
++ au_iigen_dec(inode);
++ spin_lock(&dcache_lock);
++ list_for_each_entry(d, &inode->i_dentry, d_alias) {
++ dname = &d->d_name;
++ if (dname->len != nlen
++ && memcmp(dname->name, name, nlen))
++ continue;
++ err = 0;
++ spin_lock(&d->d_lock);
++ __d_drop(d);
++ au_digen_dec(d);
++ spin_unlock(&d->d_lock);
++ break;
++ }
++ spin_unlock(&dcache_lock);
++ } else {
++ au_fset_si(au_sbi(inode->i_sb), FAILED_REFRESH_DIRS);
++ d = d_find_alias(inode);
++ if (!d) {
++ au_iigen_dec(inode);
++ goto out;
++ }
++
++ dname = &d->d_name;
++ if (dname->len == nlen && !memcmp(dname->name, name, nlen))
++ err = hin_gen_tree(d);
++ dput(d);
++ }
++
++ out:
++ AuTraceErr(err);
++ return err;
++}
++
++static int hin_gen_by_name(struct dentry *dentry, const unsigned int isdir)
++{
++ int err;
++ struct inode *inode;
++
++ inode = dentry->d_inode;
++ if (IS_ROOT(dentry)
++ /* || (inode && inode->i_ino == AUFS_ROOT_INO) */
++ ) {
++ AuWarn("branch root dir was changed\n");
++ return 0;
++ }
++
++ err = 0;
++ if (!isdir) {
++ d_drop(dentry);
++ au_digen_dec(dentry);
++ if (inode)
++ au_iigen_dec(inode);
++ } else {
++ au_fset_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIRS);
++ if (inode)
++ err = hin_gen_tree(dentry);
++ }
++
++ AuTraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++/* hinotify job flags */
++#define AuHinJob_XINO0 1
++#define AuHinJob_GEN (1 << 1)
++#define AuHinJob_DIRENT (1 << 2)
++#define AuHinJob_ISDIR (1 << 3)
++#define AuHinJob_TRYXINO0 (1 << 4)
++#define AuHinJob_MNTPNT (1 << 5)
++#define au_ftest_hinjob(flags, name) ((flags) & AuHinJob_##name)
++#define au_fset_hinjob(flags, name) { (flags) |= AuHinJob_##name; }
++#define au_fclr_hinjob(flags, name) { (flags) &= ~AuHinJob_##name; }
++
++struct hin_job_args {
++ unsigned int flags;
++ struct inode *inode, *h_inode, *dir, *h_dir;
++ struct dentry *dentry;
++ char *h_name;
++ int h_nlen;
++};
++
++static int hin_job(struct hin_job_args *a)
++{
++ const unsigned int isdir = au_ftest_hinjob(a->flags, ISDIR);
++
++ /* reset xino */
++ if (au_ftest_hinjob(a->flags, XINO0) && a->inode)
++ hin_xino(a->inode, a->h_inode); /* ignore this error */
++
++ if (au_ftest_hinjob(a->flags, TRYXINO0)
++ && a->inode
++ && a->h_inode) {
++ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD);
++ if (!a->h_inode->i_nlink)
++ hin_xino(a->inode, a->h_inode); /* ignore this error */
++ mutex_unlock(&a->h_inode->i_mutex);
++ }
++
++ /* make the generation obsolete */
++ if (au_ftest_hinjob(a->flags, GEN)) {
++ int err = -1;
++ if (a->inode)
++ err = hin_gen_by_inode(a->h_name, a->h_nlen, a->inode,
++ isdir);
++ if (err && a->dentry)
++ hin_gen_by_name(a->dentry, isdir);
++ /* ignore this error */
++ }
++
++ /* make dir entries obsolete */
++ if (au_ftest_hinjob(a->flags, DIRENT) && a->inode) {
++ struct au_vdir *vdir;
++
++ vdir = au_ivdir(a->inode);
++ if (vdir)
++ vdir->vd_jiffy = 0;
++ /* IMustLock(a->inode); */
++ /* a->inode->i_version++; */
++ }
++
++ /* can do nothing but warn */
++ if (au_ftest_hinjob(a->flags, MNTPNT)
++ && a->dentry
++ && d_mountpoint(a->dentry))
++ AuWarn("mount-point %.*s is removed or renamed\n",
++ AuDLNPair(a->dentry));
++
++ return 0;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static char *in_name(u32 mask)
++{
++#ifdef CONFIG_AUFS_DEBUG
++#define test_ret(flag) if (mask & flag) \
++ return #flag;
++ test_ret(IN_ACCESS);
++ test_ret(IN_MODIFY);
++ test_ret(IN_ATTRIB);
++ test_ret(IN_CLOSE_WRITE);
++ test_ret(IN_CLOSE_NOWRITE);
++ test_ret(IN_OPEN);
++ test_ret(IN_MOVED_FROM);
++ test_ret(IN_MOVED_TO);
++ test_ret(IN_CREATE);
++ test_ret(IN_DELETE);
++ test_ret(IN_DELETE_SELF);
++ test_ret(IN_MOVE_SELF);
++ test_ret(IN_UNMOUNT);
++ test_ret(IN_Q_OVERFLOW);
++ test_ret(IN_IGNORED);
++ return "";
++#undef test_ret
++#else
++ return "??";
++#endif
++}
++
++static struct dentry *lookup_wlock_by_name(char *name, unsigned int nlen,
++ struct inode *dir)
++{
++ struct dentry *dentry, *d, *parent;
++ struct qstr *dname;
++
++ parent = d_find_alias(dir);
++ if (!parent)
++ return NULL;
++
++ dentry = NULL;
++ spin_lock(&dcache_lock);
++ list_for_each_entry(d, &parent->d_subdirs, d_u.d_child) {
++ /* AuDbg("%.*s\n", AuDLNPair(d)); */
++ dname = &d->d_name;
++ if (dname->len != nlen || memcmp(dname->name, name, nlen))
++ continue;
++ if (!atomic_read(&d->d_count) || !d->d_fsdata) {
++ spin_lock(&d->d_lock);
++ __d_drop(d);
++ spin_unlock(&d->d_lock);
++ continue;
++ }
++
++ dentry = dget(d);
++ break;
++ }
++ spin_unlock(&dcache_lock);
++ dput(parent);
++
++ if (dentry)
++ di_write_lock_child(dentry);
++
++ return dentry;
++}
++
++static struct inode *lookup_wlock_by_ino(struct super_block *sb,
++ aufs_bindex_t bindex, ino_t h_ino)
++{
++ struct inode *inode;
++ ino_t ino;
++ int err;
++
++ inode = NULL;
++ err = au_xino_read(sb, bindex, h_ino, &ino);
++ if (!err && ino)
++ inode = ilookup(sb, ino);
++ if (!inode)
++ goto out;
++
++ if (unlikely(inode->i_ino == AUFS_ROOT_INO)) {
++ AuWarn("wrong root branch\n");
++ iput(inode);
++ inode = NULL;
++ goto out;
++ }
++
++ ii_write_lock_child(inode);
++
++ out:
++ return inode;
++}
++
++enum { CHILD, PARENT };
++struct postproc_args {
++ struct inode *h_dir, *dir, *h_child_inode;
++ u32 mask;
++ unsigned int flags[2];
++ unsigned int h_child_nlen;
++ char h_child_name[];
++};
++
++static void postproc(void *_args)
++{
++ struct postproc_args *a = _args;
++ struct super_block *sb;
++ aufs_bindex_t bindex, bend, bfound;
++ unsigned char xino, try_iput;
++ int err;
++ struct inode *inode;
++ ino_t h_ino;
++ struct hin_job_args args;
++ struct dentry *dentry;
++ struct au_sbinfo *sbinfo;
++
++ AuDebugOn(!_args);
++ AuDebugOn(!a->h_dir);
++ AuDebugOn(!a->dir);
++ AuDebugOn(!a->mask);
++ AuDbg("mask 0x%x %s, i%lu, hi%lu, hci%lu\n",
++ a->mask, in_name(a->mask), a->dir->i_ino, a->h_dir->i_ino,
++ a->h_child_inode ? a->h_child_inode->i_ino : 0);
++
++ inode = NULL;
++ dentry = NULL;
++ /*
++ * do not lock a->dir->i_mutex here
++ * because of d_revalidate() may cause a deadlock.
++ */
++ sb = a->dir->i_sb;
++ AuDebugOn(!sb);
++ sbinfo = au_sbi(sb);
++ AuDebugOn(!sbinfo);
++ /* big aufs lock */
++ si_noflush_write_lock(sb);
++
++ ii_read_lock_parent(a->dir);
++ bfound = -1;
++ bend = au_ibend(a->dir);
++ for (bindex = au_ibstart(a->dir); bindex <= bend; bindex++)
++ if (au_h_iptr(a->dir, bindex) == a->h_dir) {
++ bfound = bindex;
++ break;
++ }
++ ii_read_unlock(a->dir);
++ if (unlikely(bfound < 0))
++ goto out;
++
++ xino = !!au_opt_test(au_mntflags(sb), XINO);
++ h_ino = 0;
++ if (a->h_child_inode)
++ h_ino = a->h_child_inode->i_ino;
++
++ if (a->h_child_nlen
++ && (au_ftest_hinjob(a->flags[CHILD], GEN)
++ || au_ftest_hinjob(a->flags[CHILD], MNTPNT)))
++ dentry = lookup_wlock_by_name(a->h_child_name, a->h_child_nlen,
++ a->dir);
++ try_iput = 0;
++ if (dentry)
++ inode = dentry->d_inode;
++ if (xino && !inode && h_ino
++ && (au_ftest_hinjob(a->flags[CHILD], XINO0)
++ || au_ftest_hinjob(a->flags[CHILD], TRYXINO0)
++ || au_ftest_hinjob(a->flags[CHILD], GEN))) {
++ inode = lookup_wlock_by_ino(sb, bfound, h_ino);
++ try_iput = 1;
++ }
++
++ args.flags = a->flags[CHILD];
++ args.dentry = dentry;
++ args.inode = inode;
++ args.h_inode = a->h_child_inode;
++ args.dir = a->dir;
++ args.h_dir = a->h_dir;
++ args.h_name = a->h_child_name;
++ args.h_nlen = a->h_child_nlen;
++ err = hin_job(&args);
++ if (dentry) {
++ if (dentry->d_fsdata)
++ di_write_unlock(dentry);
++ dput(dentry);
++ }
++ if (inode && try_iput) {
++ ii_write_unlock(inode);
++ iput(inode);
++ }
++
++ ii_write_lock_parent(a->dir);
++ args.flags = a->flags[PARENT];
++ args.dentry = NULL;
++ args.inode = a->dir;
++ args.h_inode = a->h_dir;
++ args.dir = NULL;
++ args.h_dir = NULL;
++ args.h_name = NULL;
++ args.h_nlen = 0;
++ err = hin_job(&args);
++ ii_write_unlock(a->dir);
++
++ out:
++ au_nwt_done(&sbinfo->si_nowait);
++ si_write_unlock(sb);
++
++ iput(a->h_child_inode);
++ iput(a->h_dir);
++ iput(a->dir);
++ kfree(a);
++}
++
++/* ---------------------------------------------------------------------- */
++
++static void aufs_inotify(struct inotify_watch *watch, u32 wd __maybe_unused,
++ u32 mask, u32 cookie __maybe_unused,
++ const char *h_child_name, struct inode *h_child_inode)
++{
++ struct au_hinotify *hinotify;
++ struct postproc_args *args;
++ int len, wkq_err;
++ unsigned char isdir, isroot, wh;
++ char *p;
++ struct inode *dir;
++ unsigned int flags[2];
++
++ /* if IN_UNMOUNT happens, there must be another bug */
++ AuDebugOn(mask & IN_UNMOUNT);
++ if (mask & (IN_IGNORED | IN_UNMOUNT)) {
++ put_inotify_watch(watch);
++ return;
++ }
++#ifdef AuDbgHinotify
++ au_debug(1);
++ if (1 || !h_child_name || strcmp(h_child_name, AUFS_XINO_FNAME)) {
++ AuDbg("i%lu, wd %d, mask 0x%x %s, cookie 0x%x, hcname %s,"
++ " hi%lu\n",
++ watch->inode->i_ino, wd, mask, in_name(mask), cookie,
++ h_child_name ? h_child_name : "",
++ h_child_inode ? h_child_inode->i_ino : 0);
++ WARN_ON(1);
++ }
++ au_debug(0);
++#endif
++
++ hinotify = container_of(watch, struct au_hinotify, hin_watch);
++ AuDebugOn(!hinotify || !hinotify->hin_aufs_inode);
++ dir = igrab(hinotify->hin_aufs_inode);
++ if (!dir)
++ return;
++
++ isroot = (dir->i_ino == AUFS_ROOT_INO);
++ len = 0;
++ wh = 0;
++ if (h_child_name) {
++ len = strlen(h_child_name);
++ if (!memcmp(h_child_name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) {
++ h_child_name += AUFS_WH_PFX_LEN;
++ len -= AUFS_WH_PFX_LEN;
++ wh = 1;
++ }
++ }
++
++ isdir = 0;
++ if (h_child_inode)
++ isdir = !!S_ISDIR(h_child_inode->i_mode);
++ flags[PARENT] = AuHinJob_ISDIR;
++ flags[CHILD] = 0;
++ if (isdir)
++ flags[CHILD] = AuHinJob_ISDIR;
++ switch (mask & IN_ALL_EVENTS) {
++ case IN_MOVED_FROM:
++ case IN_MOVED_TO:
++ AuDebugOn(!h_child_name || !h_child_inode);
++ au_fset_hinjob(flags[CHILD], GEN);
++ au_fset_hinjob(flags[CHILD], XINO0);
++ au_fset_hinjob(flags[CHILD], MNTPNT);
++ au_fset_hinjob(flags[PARENT], DIRENT);
++ break;
++
++ case IN_CREATE:
++ AuDebugOn(!h_child_name || !h_child_inode);
++ au_fset_hinjob(flags[PARENT], DIRENT);
++ au_fset_hinjob(flags[CHILD], GEN);
++ break;
++
++ case IN_DELETE:
++ /*
++ * aufs never be able to get this child inode.
++ * revalidation should be in d_revalidate()
++ * by checking i_nlink, i_generation or d_unhashed().
++ */
++ AuDebugOn(!h_child_name);
++ au_fset_hinjob(flags[PARENT], DIRENT);
++ au_fset_hinjob(flags[CHILD], GEN);
++ au_fset_hinjob(flags[CHILD], TRYXINO0);
++ au_fset_hinjob(flags[CHILD], MNTPNT);
++ break;
++
++ default:
++ AuDebugOn(1);
++ }
++
++ if (wh)
++ h_child_inode = NULL;
++
++ /* iput() and kfree() will be called in postproc() */
++ /*
++ * inotify_mutex is already acquired and kmalloc/prune_icache may lock
++ * iprune_mutex. strange.
++ */
++ lockdep_off();
++ args = kmalloc(sizeof(*args) + len + 1, GFP_NOFS);
++ lockdep_on();
++ if (unlikely(!args)) {
++ AuErr1("no memory\n");
++ iput(dir);
++ return;
++ }
++ args->flags[PARENT] = flags[PARENT];
++ args->flags[CHILD] = flags[CHILD];
++ args->mask = mask;
++ args->dir = dir;
++ args->h_dir = igrab(watch->inode);
++ if (h_child_inode)
++ h_child_inode = igrab(h_child_inode); /* can be NULL */
++ args->h_child_inode = h_child_inode;
++ args->h_child_nlen = len;
++ if (len) {
++ p = (void *)args;
++ p += sizeof(*args);
++ memcpy(p, h_child_name, len + 1);
++ }
++
++ lockdep_off();
++ wkq_err = au_wkq_nowait(postproc, args, dir->i_sb);
++ lockdep_on();
++ if (unlikely(wkq_err))
++ AuErr("wkq %d\n", wkq_err);
++}
++
++static void aufs_inotify_destroy(struct inotify_watch *watch __maybe_unused)
++{
++ return;
++}
++
++static struct inotify_operations aufs_inotify_ops = {
++ .handle_event = aufs_inotify,
++ .destroy_watch = aufs_inotify_destroy
++};
++
++/* ---------------------------------------------------------------------- */
++
++static void au_hin_destroy_cache(void)
++{
++ kmem_cache_destroy(au_cachep[AuCache_HINOTIFY]);
++ au_cachep[AuCache_HINOTIFY] = NULL;
++}
++
++int __init au_hinotify_init(void)
++{
++ int err;
++
++ err = -ENOMEM;
++ au_cachep[AuCache_HINOTIFY] = AuCache(au_hinotify);
++ if (au_cachep[AuCache_HINOTIFY]) {
++ err = 0;
++ au_hin_handle = inotify_init(&aufs_inotify_ops);
++ if (IS_ERR(au_hin_handle)) {
++ err = PTR_ERR(au_hin_handle);
++ au_hin_destroy_cache();
++ }
++ }
++ AuTraceErr(err);
++ return err;
++}
++
++void au_hinotify_fin(void)
++{
++ inotify_destroy(au_hin_handle);
++ if (au_cachep[AuCache_HINOTIFY])
++ au_hin_destroy_cache();
++}
+diff -Nur linux-2.6.31.5.orig/fs/aufs/iinfo.c linux-2.6.31.5/fs/aufs/iinfo.c
+--- linux-2.6.31.5.orig/fs/aufs/iinfo.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/iinfo.c 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,283 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. Okajima
++ *
++ * This program, aufs is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/*
++ * inode private data
++ */
++
++#include "aufs.h"
++
++struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex)
++{
++ struct inode *h_inode;
++
++ IiMustAnyLock(inode);
++
++ h_inode = au_ii(inode)->ii_hinode[0 + bindex].hi_inode;
++ AuDebugOn(h_inode && atomic_read(&h_inode->i_count) <= 0);
++ return h_inode;
++}
++
++/* todo: hard/soft set? */
++void au_set_ibstart(struct inode *inode, aufs_bindex_t bindex)
++{
++ struct au_iinfo *iinfo = au_ii(inode);
++ struct inode *h_inode;
++
++ IiMustWriteLock(inode);
++
++ iinfo->ii_bstart = bindex;
++ h_inode = iinfo->ii_hinode[bindex + 0].hi_inode;
++ if (h_inode)
++ au_cpup_igen(inode, h_inode);
++}
++
++void au_hiput(struct au_hinode *hinode)
++{
++ au_hin_free(hinode);
++ dput(hinode->hi_whdentry);
++ iput(hinode->hi_inode);
++}
++
++unsigned int au_hi_flags(struct inode *inode, int isdir)
++{
++ unsigned int flags;
++ const unsigned int mnt_flags = au_mntflags(inode->i_sb);
++
++ flags = 0;
++ if (au_opt_test(mnt_flags, XINO))
++ au_fset_hi(flags, XINO);
++ if (isdir && au_opt_test(mnt_flags, UDBA_HINOTIFY))
++ au_fset_hi(flags, HINOTIFY);
++ return flags;
++}
++
++void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex,
++ struct inode *h_inode, unsigned int flags)
++{
++ struct au_hinode *hinode;
++ struct inode *hi;
++ struct au_iinfo *iinfo = au_ii(inode);
++
++ IiMustWriteLock(inode);
++
++ hinode = iinfo->ii_hinode + bindex;
++ hi = hinode->hi_inode;
++ AuDebugOn(h_inode && atomic_read(&h_inode->i_count) <= 0);
++ AuDebugOn(h_inode && hi);
++
++ if (hi)
++ au_hiput(hinode);
++ hinode->hi_inode = h_inode;
++ if (h_inode) {
++ int err;
++ struct super_block *sb = inode->i_sb;
++ struct au_branch *br;
++
++ if (bindex == iinfo->ii_bstart)
++ au_cpup_igen(inode, h_inode);
++ br = au_sbr(sb, bindex);
++ hinode->hi_id = br->br_id;
++ if (au_ftest_hi(flags, XINO)) {
++ err = au_xino_write(sb, bindex, h_inode->i_ino,
++ inode->i_ino);
++ if (unlikely(err))
++ AuIOErr1("failed au_xino_write() %d\n", err);
++ }
++
++ if (au_ftest_hi(flags, HINOTIFY)
++ && au_br_hinotifyable(br->br_perm)) {
++ err = au_hin_alloc(hinode, inode, h_inode);
++ if (unlikely(err))
++ AuIOErr1("au_hin_alloc() %d\n", err);
++ }
++ }
++}
++
++void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex,
++ struct dentry *h_wh)
++{
++ struct au_hinode *hinode;
++
++ IiMustWriteLock(inode);
++
++ hinode = au_ii(inode)->ii_hinode + bindex;
++ AuDebugOn(hinode->hi_whdentry);
++ hinode->hi_whdentry = h_wh;
++}
++
++void au_update_iigen(struct inode *inode)
++{
++ atomic_set(&au_ii(inode)->ii_generation, au_sigen(inode->i_sb));
++ /* smp_mb(); */ /* atomic_set */
++}
++
++/* it may be called at remount time, too */
++void au_update_brange(struct inode *inode, int do_put_zero)
++{
++ struct au_iinfo *iinfo;
++
++ iinfo = au_ii(inode);
++ if (!iinfo || iinfo->ii_bstart < 0)
++ return;
++
++ IiMustWriteLock(inode);
++
++ if (do_put_zero) {
++ aufs_bindex_t bindex;
++
++ for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend;
++ bindex++) {
++ struct inode *h_i;
++
++ h_i = iinfo->ii_hinode[0 + bindex].hi_inode;
++ if (h_i && !h_i->i_nlink)
++ au_set_h_iptr(inode, bindex, NULL, 0);
++ }
++ }
++
++ iinfo->ii_bstart = -1;
++ while (++iinfo->ii_bstart <= iinfo->ii_bend)
++ if (iinfo->ii_hinode[0 + iinfo->ii_bstart].hi_inode)
++ break;
++ if (iinfo->ii_bstart > iinfo->ii_bend) {
++ iinfo->ii_bstart = -1;
++ iinfo->ii_bend = -1;
++ return;
++ }
++
++ iinfo->ii_bend++;
++ while (0 <= --iinfo->ii_bend)
++ if (iinfo->ii_hinode[0 + iinfo->ii_bend].hi_inode)
++ break;
++ AuDebugOn(iinfo->ii_bstart > iinfo->ii_bend || iinfo->ii_bend < 0);
++}
++
++/* ---------------------------------------------------------------------- */
++
++int au_iinfo_init(struct inode *inode)
++{
++ struct au_iinfo *iinfo;
++ struct super_block *sb;
++ int nbr, i;
++
++ sb = inode->i_sb;
++ iinfo = &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo);
++ nbr = au_sbend(sb) + 1;
++ if (unlikely(nbr <= 0))
++ nbr = 1;
++ iinfo->ii_hinode = kcalloc(nbr, sizeof(*iinfo->ii_hinode), GFP_NOFS);
++ if (iinfo->ii_hinode) {
++ for (i = 0; i < nbr; i++)
++ iinfo->ii_hinode[i].hi_id = -1;
++
++ atomic_set(&iinfo->ii_generation, au_sigen(sb));
++ /* smp_mb(); */ /* atomic_set */
++ au_rw_init(&iinfo->ii_rwsem);
++ iinfo->ii_bstart = -1;
++ iinfo->ii_bend = -1;
++ iinfo->ii_vdir = NULL;
++ return 0;
++ }
++ return -ENOMEM;
++}
++
++int au_ii_realloc(struct au_iinfo *iinfo, int nbr)
++{
++ int err, sz;
++ struct au_hinode *hip;
++
++ AuRwMustWriteLock(&iinfo->ii_rwsem);
++
++ err = -ENOMEM;
++ sz = sizeof(*hip) * (iinfo->ii_bend + 1);
++ if (!sz)
++ sz = sizeof(*hip);
++ hip = au_kzrealloc(iinfo->ii_hinode, sz, sizeof(*hip) * nbr, GFP_NOFS);
++ if (hip) {
++ iinfo->ii_hinode = hip;
++ err = 0;
++ }
++
++ return err;
++}
++
++static int au_iinfo_write0(struct super_block *sb, struct au_hinode *hinode,
++ ino_t ino)
++{
++ int err;
++ aufs_bindex_t bindex;
++ unsigned char locked;
++
++ err = 0;
++ locked = !!si_noflush_read_trylock(sb);
++ bindex = au_br_index(sb, hinode->hi_id);
++ if (bindex >= 0)
++ err = au_xino_write0(sb, bindex, hinode->hi_inode->i_ino, ino);
++ /* error action? */
++ if (locked)
++ si_read_unlock(sb);
++ return err;
++}
++
++void au_iinfo_fin(struct inode *inode)
++{
++ ino_t ino;
++ aufs_bindex_t bend;
++ unsigned char unlinked = !inode->i_nlink;
++ struct au_iinfo *iinfo;
++ struct au_hinode *hi;
++ struct super_block *sb;
++
++ if (unlinked) {
++ int err = au_xigen_inc(inode);
++ if (unlikely(err))
++ AuWarn1("failed resetting i_generation, %d\n", err);
++ }
++
++ iinfo = au_ii(inode);
++ /* bad_inode case */
++ if (!iinfo)
++ return;
++
++ if (iinfo->ii_vdir)
++ au_vdir_free(iinfo->ii_vdir);
++
++ if (iinfo->ii_bstart >= 0) {
++ sb = inode->i_sb;
++ ino = 0;
++ if (unlinked)
++ ino = inode->i_ino;
++ hi = iinfo->ii_hinode + iinfo->ii_bstart;
++ bend = iinfo->ii_bend;
++ while (iinfo->ii_bstart++ <= bend) {
++ if (hi->hi_inode) {
++ if (unlinked || !hi->hi_inode->i_nlink) {
++ au_iinfo_write0(sb, hi, ino);
++ /* ignore this error */
++ ino = 0;
++ }
++ au_hiput(hi);
++ }
++ hi++;
++ }
++ }
++
++ kfree(iinfo->ii_hinode);
++ AuRwDestroy(&iinfo->ii_rwsem);
++}
+diff -Nur linux-2.6.31.5.orig/fs/aufs/inode.c linux-2.6.31.5/fs/aufs/inode.c
+--- linux-2.6.31.5.orig/fs/aufs/inode.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/inode.c 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,380 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. Okajima
++ *
++ * This program, aufs is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/*
++ * inode functions
++ */
++
++#include "aufs.h"
++
++struct inode *au_igrab(struct inode *inode)
++{
++ if (inode) {
++ AuDebugOn(!atomic_read(&inode->i_count));
++ atomic_inc_return(&inode->i_count);
++ }
++ return inode;
++}
++
++static void au_refresh_hinode_attr(struct inode *inode, int do_version)
++{
++ au_cpup_attr_all(inode, /*force*/0);
++ au_update_iigen(inode);
++ if (do_version)
++ inode->i_version++;
++}
++
++int au_refresh_hinode_self(struct inode *inode, int do_attr)
++{
++ int err;
++ aufs_bindex_t bindex, new_bindex;
++ unsigned char update;
++ struct inode *first;
++ struct au_hinode *p, *q, tmp;
++ struct super_block *sb;
++ struct au_iinfo *iinfo;
++
++ IiMustWriteLock(inode);
++
++ update = 0;
++ sb = inode->i_sb;
++ iinfo = au_ii(inode);
++ err = au_ii_realloc(iinfo, au_sbend(sb) + 1);
++ if (unlikely(err))
++ goto out;
++
++ p = iinfo->ii_hinode + iinfo->ii_bstart;
++ first = p->hi_inode;
++ err = 0;
++ for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend;
++ bindex++, p++) {
++ if (!p->hi_inode)
++ continue;
++
++ new_bindex = au_br_index(sb, p->hi_id);
++ if (new_bindex == bindex)
++ continue;
++
++ if (new_bindex < 0) {
++ update++;
++ au_hiput(p);
++ p->hi_inode = NULL;
++ continue;
++ }
++
++ if (new_bindex < iinfo->ii_bstart)
++ iinfo->ii_bstart = new_bindex;
++ if (iinfo->ii_bend < new_bindex)
++ iinfo->ii_bend = new_bindex;
++ /* swap two lower inode, and loop again */
++ q = iinfo->ii_hinode + new_bindex;
++ tmp = *q;
++ *q = *p;
++ *p = tmp;
++ if (tmp.hi_inode) {
++ bindex--;
++ p--;
++ }
++ }
++ au_update_brange(inode, /*do_put_zero*/0);
++ if (do_attr)
++ au_refresh_hinode_attr(inode, update && S_ISDIR(inode->i_mode));
++
++ out:
++ return err;
++}
++
++int au_refresh_hinode(struct inode *inode, struct dentry *dentry)
++{
++ int err, update;
++ unsigned int flags;
++ aufs_bindex_t bindex, bend;
++ unsigned char isdir;
++ struct inode *first;
++ struct au_hinode *p;
++ struct au_iinfo *iinfo;
++
++ err = au_refresh_hinode_self(inode, /*do_attr*/0);
++ if (unlikely(err))
++ goto out;
++
++ update = 0;
++ iinfo = au_ii(inode);
++ p = iinfo->ii_hinode + iinfo->ii_bstart;
++ first = p->hi_inode;
++ isdir = S_ISDIR(inode->i_mode);
++ flags = au_hi_flags(inode, isdir);
++ bend = au_dbend(dentry);
++ for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) {
++ struct inode *h_i;
++ struct dentry *h_d;
++
++ h_d = au_h_dptr(dentry, bindex);
++ if (!h_d || !h_d->d_inode)
++ continue;
++
++ if (iinfo->ii_bstart <= bindex && bindex <= iinfo->ii_bend) {
++ h_i = au_h_iptr(inode, bindex);
++ if (h_i) {
++ if (h_i == h_d->d_inode)
++ continue;
++ err = -EIO;
++ break;
++ }
++ }
++ if (bindex < iinfo->ii_bstart)
++ iinfo->ii_bstart = bindex;
++ if (iinfo->ii_bend < bindex)
++ iinfo->ii_bend = bindex;
++ au_set_h_iptr(inode, bindex, au_igrab(h_d->d_inode), flags);
++ update = 1;
++ }
++ au_update_brange(inode, /*do_put_zero*/0);
++
++ if (unlikely(err))
++ goto out;
++
++ au_refresh_hinode_attr(inode, update && isdir);
++
++ out:
++ return err;
++}
++
++static int set_inode(struct inode *inode, struct dentry *dentry)
++{
++ int err;
++ unsigned int flags;
++ umode_t mode;
++ aufs_bindex_t bindex, bstart, btail;
++ unsigned char isdir;
++ struct dentry *h_dentry;
++ struct inode *h_inode;
++ struct au_iinfo *iinfo;
++
++ IiMustWriteLock(inode);
++
++ err = 0;
++ isdir = 0;
++ bstart = au_dbstart(dentry);
++ h_inode = au_h_dptr(dentry, bstart)->d_inode;
++ mode = h_inode->i_mode;
++ switch (mode & S_IFMT) {
++ case S_IFREG:
++ btail = au_dbtail(dentry);
++ inode->i_op = &aufs_iop;
++ inode->i_fop = &aufs_file_fop;
++ inode->i_mapping->a_ops = &aufs_aop;
++ break;
++ case S_IFDIR:
++ isdir = 1;
++ btail = au_dbtaildir(dentry);
++ inode->i_op = &aufs_dir_iop;
++ inode->i_fop = &aufs_dir_fop;
++ break;
++ case S_IFLNK:
++ btail = au_dbtail(dentry);
++ inode->i_op = &aufs_symlink_iop;
++ break;
++ case S_IFBLK:
++ case S_IFCHR:
++ case S_IFIFO:
++ case S_IFSOCK:
++ btail = au_dbtail(dentry);
++ inode->i_op = &aufs_iop;
++ init_special_inode(inode, mode, h_inode->i_rdev);
++ break;
++ default:
++ AuIOErr("Unknown file type 0%o\n", mode);
++ err = -EIO;
++ goto out;
++ }
++
++ /* do not set inotify for whiteouted dirs (SHWH mode) */
++ flags = au_hi_flags(inode, isdir);
++ if (au_opt_test(au_mntflags(dentry->d_sb), SHWH)
++ && au_ftest_hi(flags, HINOTIFY)
++ && dentry->d_name.len > AUFS_WH_PFX_LEN
++ && !memcmp(dentry->d_name.name, AUFS_WH_PFX, AUFS_WH_PFX_LEN))
++ au_fclr_hi(flags, HINOTIFY);
++ iinfo = au_ii(inode);
++ iinfo->ii_bstart = bstart;
++ iinfo->ii_bend = btail;
++ for (bindex = bstart; bindex <= btail; bindex++) {
++ h_dentry = au_h_dptr(dentry, bindex);
++ if (h_dentry)
++ au_set_h_iptr(inode, bindex,
++ au_igrab(h_dentry->d_inode), flags);
++ }
++ au_cpup_attr_all(inode, /*force*/1);
++
++ out:
++ return err;
++}
++
++/* successful returns with iinfo write_locked */
++static int reval_inode(struct inode *inode, struct dentry *dentry, int *matched)
++{
++ int err;
++ aufs_bindex_t bindex, bend;
++ struct inode *h_inode, *h_dinode;
++
++ *matched = 0;
++
++ /*
++ * before this function, if aufs got any iinfo lock, it must be only
++ * one, the parent dir.
++ * it can happen by UDBA and the obsoleted inode number.
++ */
++ err = -EIO;
++ if (unlikely(inode->i_ino == parent_ino(dentry)))
++ goto out;
++
++ err = 0;
++ ii_write_lock_new_child(inode);
++ h_dinode = au_h_dptr(dentry, au_dbstart(dentry))->d_inode;
++ bend = au_ibend(inode);
++ for (bindex = au_ibstart(inode); bindex <= bend; bindex++) {
++ h_inode = au_h_iptr(inode, bindex);
++ if (h_inode && h_inode == h_dinode) {
++ *matched = 1;
++ err = 0;
++ if (au_iigen(inode) != au_digen(dentry))
++ err = au_refresh_hinode(inode, dentry);
++ break;
++ }
++ }
++
++ if (unlikely(err))
++ ii_write_unlock(inode);
++ out:
++ return err;
++}
++
++/* successful returns with iinfo write_locked */
++/* todo: return with unlocked? */
++struct inode *au_new_inode(struct dentry *dentry, int must_new)
++{
++ struct inode *inode;
++ struct dentry *h_dentry;
++ struct super_block *sb;
++ ino_t h_ino, ino;
++ int err, match;
++ aufs_bindex_t bstart;
++
++ sb = dentry->d_sb;
++ bstart = au_dbstart(dentry);
++ h_dentry = au_h_dptr(dentry, bstart);
++ h_ino = h_dentry->d_inode->i_ino;
++ err = au_xino_read(sb, bstart, h_ino, &ino);
++ inode = ERR_PTR(err);
++ if (unlikely(err))
++ goto out;
++ new_ino:
++ if (!ino) {
++ ino = au_xino_new_ino(sb);
++ if (unlikely(!ino)) {
++ inode = ERR_PTR(-EIO);
++ goto out;
++ }
++ }
++
++ AuDbg("i%lu\n", (unsigned long)ino);
++ inode = au_iget_locked(sb, ino);
++ err = PTR_ERR(inode);
++ if (IS_ERR(inode))
++ goto out;
++
++ AuDbg("%lx, new %d\n", inode->i_state, !!(inode->i_state & I_NEW));
++ if (inode->i_state & I_NEW) {
++ ii_write_lock_new_child(inode);
++ err = set_inode(inode, dentry);
++ unlock_new_inode(inode);
++ if (!err)
++ goto out; /* success */
++
++ iget_failed(inode);
++ ii_write_unlock(inode);
++ goto out_iput;
++ } else if (!must_new) {
++ err = reval_inode(inode, dentry, &match);
++ if (!err)
++ goto out; /* success */
++ else if (match)
++ goto out_iput;
++ }
++
++ if (unlikely(au_test_fs_unique_ino(h_dentry->d_inode)))
++ AuWarn1("Warning: Un-notified UDBA or repeatedly renamed dir,"
++ " b%d, %s, %.*s, hi%lu, i%lu.\n",
++ bstart, au_sbtype(h_dentry->d_sb), AuDLNPair(dentry),
++ (unsigned long)h_ino, (unsigned long)ino);
++ ino = 0;
++ err = au_xino_write(sb, bstart, h_ino, /*ino*/0);
++ if (!err) {
++ iput(inode);
++ goto new_ino;
++ }
++
++ out_iput:
++ iput(inode);
++ inode = ERR_PTR(err);
++ out:
++ return inode;
++}
++
++/* ---------------------------------------------------------------------- */
++
++int au_test_ro(struct super_block *sb, aufs_bindex_t bindex,
++ struct inode *inode)
++{
++ int err;
++
++ err = au_br_rdonly(au_sbr(sb, bindex));
++
++ /* pseudo-link after flushed may happen out of bounds */
++ if (!err
++ && inode
++ && au_ibstart(inode) <= bindex
++ && bindex <= au_ibend(inode)) {
++ /*
++ * permission check is unnecessary since vfsub routine
++ * will be called later
++ */
++ struct inode *hi = au_h_iptr(inode, bindex);
++ if (hi)
++ err = IS_IMMUTABLE(hi) ? -EROFS : 0;
++ }
++
++ return err;
++}
++
++int au_test_h_perm(struct inode *h_inode, int mask)
++{
++ if (!current_fsuid())
++ return 0;
++ return inode_permission(h_inode, mask);
++}
++
++int au_test_h_perm_sio(struct inode *h_inode, int mask)
++{
++ if (au_test_nfs(h_inode->i_sb)
++ && (mask & MAY_WRITE)
++ && S_ISDIR(h_inode->i_mode))
++ mask |= MAY_READ; /* force permission check */
++ return au_test_h_perm(h_inode, mask);
++}
+diff -Nur linux-2.6.31.5.orig/fs/aufs/inode.h linux-2.6.31.5/fs/aufs/inode.h
+--- linux-2.6.31.5.orig/fs/aufs/inode.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/inode.h 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,484 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. Okajima
++ *
++ * This program, aufs is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/*
++ * inode operations
++ */
++
++#ifndef __AUFS_INODE_H__
++#define __AUFS_INODE_H__
++
++#ifdef __KERNEL__
++
++#include <linux/fs.h>
++#include <linux/inotify.h>
++#include <linux/aufs_type.h>
++#include "rwsem.h"
++
++struct vfsmount;
++
++struct au_hinotify {
++#ifdef CONFIG_AUFS_HINOTIFY
++ struct inotify_watch hin_watch;
++ struct inode *hin_aufs_inode; /* no get/put */
++#endif
++};
++
++struct au_hinode {
++ struct inode *hi_inode;
++ aufs_bindex_t hi_id;
++#ifdef CONFIG_AUFS_HINOTIFY
++ struct au_hinotify *hi_notify;
++#endif
++
++ /* reference to the copied-up whiteout with get/put */
++ struct dentry *hi_whdentry;
++};
++
++struct au_vdir;
++struct au_iinfo {
++ atomic_t ii_generation;
++ struct super_block *ii_hsb1; /* no get/put */
++
++ struct au_rwsem ii_rwsem;
++ aufs_bindex_t ii_bstart, ii_bend;
++ __u32 ii_higen;
++ struct au_hinode *ii_hinode;
++ struct au_vdir *ii_vdir;
++};
++
++struct au_icntnr {
++ struct au_iinfo iinfo;
++ struct inode vfs_inode;
++};
++
++/* au_pin flags */
++#define AuPin_DI_LOCKED 1
++#define AuPin_MNT_WRITE (1 << 1)
++#define au_ftest_pin(flags, name) ((flags) & AuPin_##name)
++#define au_fset_pin(flags, name) { (flags) |= AuPin_##name; }
++#define au_fclr_pin(flags, name) { (flags) &= ~AuPin_##name; }
++
++struct au_pin {
++ /* input */
++ struct dentry *dentry;
++ unsigned int udba;
++ unsigned char lsc_di, lsc_hi, flags;
++ aufs_bindex_t bindex;
++
++ /* output */
++ struct dentry *parent;
++ struct au_hinode *hdir;
++ struct vfsmount *h_mnt;
++};
++
++/* ---------------------------------------------------------------------- */
++
++static inline struct au_iinfo *au_ii(struct inode *inode)
++{
++ struct au_iinfo *iinfo;
++
++ iinfo = &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo);
++ if (iinfo->ii_hinode)
++ return iinfo;
++ return NULL; /* debugging bad_inode case */
++}
++
++/* ---------------------------------------------------------------------- */
++
++/* inode.c */
++struct inode *au_igrab(struct inode *inode);
++int au_refresh_hinode_self(struct inode *inode, int do_attr);
++int au_refresh_hinode(struct inode *inode, struct dentry *dentry);
++struct inode *au_new_inode(struct dentry *dentry, int must_new);
++int au_test_ro(struct super_block *sb, aufs_bindex_t bindex,
++ struct inode *inode);
++int au_test_h_perm(struct inode *h_inode, int mask);
++int au_test_h_perm_sio(struct inode *h_inode, int mask);
++
++/* i_op.c */
++extern struct inode_operations aufs_iop, aufs_symlink_iop, aufs_dir_iop;
++
++/* au_wr_dir flags */
++#define AuWrDir_ADD_ENTRY 1
++#define AuWrDir_ISDIR (1 << 1)
++#define au_ftest_wrdir(flags, name) ((flags) & AuWrDir_##name)
++#define au_fset_wrdir(flags, name) { (flags) |= AuWrDir_##name; }
++#define au_fclr_wrdir(flags, name) { (flags) &= ~AuWrDir_##name; }
++
++struct au_wr_dir_args {
++ aufs_bindex_t force_btgt;
++ unsigned char flags;
++};
++int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry,
++ struct au_wr_dir_args *args);
++
++struct dentry *au_pinned_h_parent(struct au_pin *pin);
++void au_pin_init(struct au_pin *pin, struct dentry *dentry,
++ aufs_bindex_t bindex, int lsc_di, int lsc_hi,
++ unsigned int udba, unsigned char flags);
++int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex,
++ unsigned int udba, unsigned char flags) __must_check;
++int au_do_pin(struct au_pin *pin) __must_check;
++void au_unpin(struct au_pin *pin);
++
++/* i_op_add.c */
++int au_may_add(struct dentry *dentry, aufs_bindex_t bindex,
++ struct dentry *h_parent, int isdir);
++int aufs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev);
++int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname);
++int aufs_create(struct inode *dir, struct dentry *dentry, int mode,
++ struct nameidata *nd);
++int aufs_link(struct dentry *src_dentry, struct inode *dir,
++ struct dentry *dentry);
++int aufs_mkdir(struct inode *dir, struct dentry *dentry, int mode);
++
++/* i_op_del.c */
++int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup);
++int au_may_del(struct dentry *dentry, aufs_bindex_t bindex,
++ struct dentry *h_parent, int isdir);
++int aufs_unlink(struct inode *dir, struct dentry *dentry);
++int aufs_rmdir(struct inode *dir, struct dentry *dentry);
++
++/* i_op_ren.c */
++int au_wbr(struct dentry *dentry, aufs_bindex_t btgt);
++int aufs_rename(struct inode *src_dir, struct dentry *src_dentry,
++ struct inode *dir, struct dentry *dentry);
++
++/* iinfo.c */
++struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex);
++void au_hiput(struct au_hinode *hinode);
++void au_set_ibstart(struct inode *inode, aufs_bindex_t bindex);
++void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex,
++ struct dentry *h_wh);
++unsigned int au_hi_flags(struct inode *inode, int isdir);
++
++/* hinode flags */
++#define AuHi_XINO 1
++#define AuHi_HINOTIFY (1 << 1)
++#define au_ftest_hi(flags, name) ((flags) & AuHi_##name)
++#define au_fset_hi(flags, name) { (flags) |= AuHi_##name; }
++#define au_fclr_hi(flags, name) { (flags) &= ~AuHi_##name; }
++
++#ifndef CONFIG_AUFS_HINOTIFY
++#undef AuHi_HINOTIFY
++#define AuHi_HINOTIFY 0
++#endif
++
++void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex,
++ struct inode *h_inode, unsigned int flags);
++
++void au_update_iigen(struct inode *inode);
++void au_update_brange(struct inode *inode, int do_put_zero);
++
++int au_iinfo_init(struct inode *inode);
++void au_iinfo_fin(struct inode *inode);
++int au_ii_realloc(struct au_iinfo *iinfo, int nbr);
++
++/* plink.c */
++void au_plink_block_maintain(struct super_block *sb);
++#ifdef CONFIG_AUFS_DEBUG
++void au_plink_list(struct super_block *sb);
++#else
++static inline void au_plink_list(struct super_block *sb)
++{
++ /* nothing */
++}
++#endif
++int au_plink_test(struct inode *inode);
++struct dentry *au_plink_lkup(struct inode *inode, aufs_bindex_t bindex);
++void au_plink_append(struct inode *inode, aufs_bindex_t bindex,
++ struct dentry *h_dentry);
++void au_plink_put(struct super_block *sb);
++void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id);
++
++/* ---------------------------------------------------------------------- */
++
++/* lock subclass for iinfo */
++enum {
++ AuLsc_II_CHILD, /* child first */
++ AuLsc_II_CHILD2, /* rename(2), link(2), and cpup at hinotify */
++ AuLsc_II_CHILD3, /* copyup dirs */
++ AuLsc_II_PARENT, /* see AuLsc_I_PARENT in vfsub.h */
++ AuLsc_II_PARENT2,
++ AuLsc_II_PARENT3, /* copyup dirs */
++ AuLsc_II_NEW_CHILD
++};
++
++/*
++ * 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_child, ii_write_lock_new_child,
++ */
++#define AuReadLockFunc(name, lsc) \
++static inline void ii_read_lock_##name(struct inode *i) \
++{ \
++ au_rw_read_lock_nested(&au_ii(i)->ii_rwsem, AuLsc_II_##lsc); \
++}
++
++#define AuWriteLockFunc(name, lsc) \
++static inline void ii_write_lock_##name(struct inode *i) \
++{ \
++ au_rw_write_lock_nested(&au_ii(i)->ii_rwsem, AuLsc_II_##lsc); \
++}
++
++#define AuRWLockFuncs(name, lsc) \
++ AuReadLockFunc(name, lsc) \
++ AuWriteLockFunc(name, lsc)
++
++AuRWLockFuncs(child, CHILD);
++AuRWLockFuncs(child2, CHILD2);
++AuRWLockFuncs(child3, CHILD3);
++AuRWLockFuncs(parent, PARENT);
++AuRWLockFuncs(parent2, PARENT2);
++AuRWLockFuncs(parent3, PARENT3);
++AuRWLockFuncs(new_child, NEW_CHILD);
++
++#undef AuReadLockFunc
++#undef AuWriteLockFunc
++#undef AuRWLockFuncs
++
++/*
++ * ii_read_unlock, ii_write_unlock, ii_downgrade_lock
++ */
++AuSimpleUnlockRwsemFuncs(ii, struct inode *i, &au_ii(i)->ii_rwsem);
++
++#define IiMustNoWaiters(i) AuRwMustNoWaiters(&au_ii(i)->ii_rwsem)
++#define IiMustAnyLock(i) AuRwMustAnyLock(&au_ii(i)->ii_rwsem)
++#define IiMustWriteLock(i) AuRwMustWriteLock(&au_ii(i)->ii_rwsem)
++
++/* ---------------------------------------------------------------------- */
++
++static inline unsigned int au_iigen(struct inode *inode)
++{
++ return atomic_read(&au_ii(inode)->ii_generation);
++}
++
++/* tiny test for inode number */
++/* tmpfs generation is too rough */
++static inline int au_test_higen(struct inode *inode, struct inode *h_inode)
++{
++ struct au_iinfo *iinfo;
++
++ iinfo = au_ii(inode);
++ AuRwMustAnyLock(&iinfo->ii_rwsem);
++ return !(iinfo->ii_hsb1 == h_inode->i_sb
++ && iinfo->ii_higen == h_inode->i_generation);
++}
++
++/* ---------------------------------------------------------------------- */
++
++static inline aufs_bindex_t au_ii_br_id(struct inode *inode,
++ aufs_bindex_t bindex)
++{
++ IiMustAnyLock(inode);
++ return au_ii(inode)->ii_hinode[0 + bindex].hi_id;
++}
++
++static inline aufs_bindex_t au_ibstart(struct inode *inode)
++{
++ IiMustAnyLock(inode);
++ return au_ii(inode)->ii_bstart;
++}
++
++static inline aufs_bindex_t au_ibend(struct inode *inode)
++{
++ IiMustAnyLock(inode);
++ return au_ii(inode)->ii_bend;
++}
++
++static inline struct au_vdir *au_ivdir(struct inode *inode)
++{
++ IiMustAnyLock(inode);
++ return au_ii(inode)->ii_vdir;
++}
++
++static inline struct dentry *au_hi_wh(struct inode *inode, aufs_bindex_t bindex)
++{
++ IiMustAnyLock(inode);
++ return au_ii(inode)->ii_hinode[0 + bindex].hi_whdentry;
++}
++
++static inline void au_set_ibend(struct inode *inode, aufs_bindex_t bindex)
++{
++ IiMustWriteLock(inode);
++ au_ii(inode)->ii_bend = bindex;
++}
++
++static inline void au_set_ivdir(struct inode *inode, struct au_vdir *vdir)
++{
++ IiMustWriteLock(inode);
++ au_ii(inode)->ii_vdir = vdir;
++}
++
++static inline struct au_hinode *au_hi(struct inode *inode, aufs_bindex_t bindex)
++{
++ IiMustAnyLock(inode);
++ return au_ii(inode)->ii_hinode + bindex;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static inline struct dentry *au_pinned_parent(struct au_pin *pin)
++{
++ if (pin)
++ return pin->parent;
++ return NULL;
++}
++
++static inline struct inode *au_pinned_h_dir(struct au_pin *pin)
++{
++ if (pin && pin->hdir)
++ return pin->hdir->hi_inode;
++ return NULL;
++}
++
++static inline struct au_hinode *au_pinned_hdir(struct au_pin *pin)
++{
++ if (pin)
++ return pin->hdir;
++ return NULL;
++}
++
++static inline void au_pin_set_dentry(struct au_pin *pin, struct dentry *dentry)
++{
++ if (pin)
++ pin->dentry = dentry;
++}
++
++static inline void au_pin_set_parent_lflag(struct au_pin *pin,
++ unsigned char lflag)
++{
++ if (pin) {
++ /* dirty macros require brackets */
++ if (lflag) {
++ au_fset_pin(pin->flags, DI_LOCKED);
++ } else {
++ au_fclr_pin(pin->flags, DI_LOCKED);
++ }
++ }
++}
++
++static inline void au_pin_set_parent(struct au_pin *pin, struct dentry *parent)
++{
++ if (pin) {
++ dput(pin->parent);
++ pin->parent = dget(parent);
++ }
++}
++
++/* ---------------------------------------------------------------------- */
++
++#ifdef CONFIG_AUFS_HINOTIFY
++/* hinotify.c */
++int au_hin_alloc(struct au_hinode *hinode, struct inode *inode,
++ struct inode *h_inode);
++void au_hin_free(struct au_hinode *hinode);
++void au_hin_ctl(struct au_hinode *hinode, int do_set);
++void au_reset_hinotify(struct inode *inode, unsigned int flags);
++
++int __init au_hinotify_init(void);
++void au_hinotify_fin(void);
++
++static inline
++void au_hin_init(struct au_hinode *hinode, struct au_hinotify *val)
++{
++ hinode->hi_notify = val;
++}
++
++static inline void au_iigen_dec(struct inode *inode)
++{
++ atomic_dec_return(&au_ii(inode)->ii_generation);
++}
++
++#else
++static inline
++int au_hin_alloc(struct au_hinode *hinode __maybe_unused,
++ struct inode *inode __maybe_unused,
++ struct inode *h_inode __maybe_unused)
++{
++ return -EOPNOTSUPP;
++}
++
++static inline void au_hin_free(struct au_hinode *hinode __maybe_unused)
++{
++ /* nothing */
++}
++
++static inline void au_hin_ctl(struct au_hinode *hinode __maybe_unused,
++ int do_set __maybe_unused)
++{
++ /* nothing */
++}
++
++static inline void au_reset_hinotify(struct inode *inode __maybe_unused,
++ unsigned int flags __maybe_unused)
++{
++ /* nothing */
++}
++
++static inline int au_hinotify_init(void)
++{
++ return 0;
++}
++
++#define au_hinotify_fin() do {} while (0)
++
++static inline
++void au_hin_init(struct au_hinode *hinode __maybe_unused,
++ struct au_hinotify *val __maybe_unused)
++{
++ /* empty */
++}
++#endif /* CONFIG_AUFS_HINOTIFY */
++
++static inline void au_hin_suspend(struct au_hinode *hdir)
++{
++ au_hin_ctl(hdir, /*do_set*/0);
++}
++
++static inline void au_hin_resume(struct au_hinode *hdir)
++{
++ au_hin_ctl(hdir, /*do_set*/1);
++}
++
++static inline void au_hin_imtx_lock(struct au_hinode *hdir)
++{
++ mutex_lock(&hdir->hi_inode->i_mutex);
++ au_hin_suspend(hdir);
++}
++
++static inline void au_hin_imtx_lock_nested(struct au_hinode *hdir,
++ unsigned int sc __maybe_unused)
++{
++ mutex_lock_nested(&hdir->hi_inode->i_mutex, sc);
++ au_hin_suspend(hdir);
++}
++
++static inline void au_hin_imtx_unlock(struct au_hinode *hdir)
++{
++ au_hin_resume(hdir);
++ mutex_unlock(&hdir->hi_inode->i_mutex);
++}
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_INODE_H__ */
+diff -Nur linux-2.6.31.5.orig/fs/aufs/ioctl.c linux-2.6.31.5/fs/aufs/ioctl.c
+--- linux-2.6.31.5.orig/fs/aufs/ioctl.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/ioctl.c 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,67 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. 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
++ */
++
++/*
++ * ioctl
++ * currently plink-management only.
++ */
++
++#include <linux/uaccess.h>
++#include "aufs.h"
++
++long aufs_ioctl_dir(struct file *file, unsigned int cmd,
++ unsigned long arg __maybe_unused)
++{
++ long err;
++ struct super_block *sb;
++ struct au_sbinfo *sbinfo;
++
++ err = -EACCES;
++ if (!capable(CAP_SYS_ADMIN))
++ goto out;
++
++ err = 0;
++ sb = file->f_dentry->d_sb;
++ sbinfo = au_sbi(sb);
++ switch (cmd) {
++ case AUFS_CTL_PLINK_MAINT:
++ /*
++ * pseudo-link maintenance mode,
++ * cleared by aufs_release_dir()
++ */
++ si_write_lock(sb);
++ if (!au_ftest_si(sbinfo, MAINTAIN_PLINK)) {
++ au_fset_si(sbinfo, MAINTAIN_PLINK);
++ au_fi(file)->fi_maintain_plink = 1;
++ } else
++ err = -EBUSY;
++ si_write_unlock(sb);
++ break;
++ case AUFS_CTL_PLINK_CLEAN:
++ aufs_write_lock(sb->s_root);
++ if (au_opt_test(sbinfo->si_mntflags, PLINK))
++ au_plink_put(sb);
++ aufs_write_unlock(sb->s_root);
++ break;
++ default:
++ err = -EINVAL;
++ }
++
++ out:
++ return err;
++}
+diff -Nur linux-2.6.31.5.orig/fs/aufs/i_op_add.c linux-2.6.31.5/fs/aufs/i_op_add.c
+--- linux-2.6.31.5.orig/fs/aufs/i_op_add.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/i_op_add.c 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,649 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. Okajima
++ *
++ * This program, aufs is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/*
++ * inode operations (add entry)
++ */
++
++#include "aufs.h"
++
++/*
++ * final procedure of adding a new entry, except link(2).
++ * remove whiteout, instantiate, copyup the parent dir's times and size
++ * and update version.
++ * if it failed, re-create the removed whiteout.
++ */
++static int epilog(struct inode *dir, aufs_bindex_t bindex,
++ struct dentry *wh_dentry, struct dentry *dentry)
++{
++ int err, rerr;
++ aufs_bindex_t bwh;
++ struct path h_path;
++ struct inode *inode, *h_dir;
++ struct dentry *wh;
++
++ bwh = -1;
++ if (wh_dentry) {
++ h_dir = wh_dentry->d_parent->d_inode; /* dir inode is locked */
++ IMustLock(h_dir);
++ AuDebugOn(au_h_iptr(dir, bindex) != h_dir);
++ bwh = au_dbwh(dentry);
++ h_path.dentry = wh_dentry;
++ h_path.mnt = au_sbr_mnt(dir->i_sb, bindex);
++ err = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path,
++ dentry);
++ if (unlikely(err))
++ goto out;
++ }
++
++ inode = au_new_inode(dentry, /*must_new*/1);
++ if (!IS_ERR(inode)) {
++ d_instantiate(dentry, inode);
++ dir = dentry->d_parent->d_inode; /* dir inode is locked */
++ IMustLock(dir);
++ if (au_ibstart(dir) == au_dbstart(dentry))
++ au_cpup_attr_timesizes(dir);
++ dir->i_version++;
++ return 0; /* success */
++ }
++
++ err = PTR_ERR(inode);
++ if (!wh_dentry)
++ goto out;
++
++ /* revert */
++ /* dir inode is locked */
++ wh = au_wh_create(dentry, bwh, wh_dentry->d_parent);
++ rerr = PTR_ERR(wh);
++ if (IS_ERR(wh)) {
++ AuIOErr("%.*s reverting whiteout failed(%d, %d)\n",
++ AuDLNPair(dentry), err, rerr);
++ err = -EIO;
++ } else
++ dput(wh);
++
++ out:
++ return err;
++}
++
++/*
++ * simple tests for the adding inode operations.
++ * following the checks in vfs, plus the parent-child relationship.
++ */
++int au_may_add(struct dentry *dentry, aufs_bindex_t bindex,
++ struct dentry *h_parent, int isdir)
++{
++ int err;
++ umode_t h_mode;
++ struct dentry *h_dentry;
++ struct inode *h_inode;
++
++ h_dentry = au_h_dptr(dentry, bindex);
++ h_inode = h_dentry->d_inode;
++ if (!dentry->d_inode) {
++ err = -EEXIST;
++ if (unlikely(h_inode))
++ goto out;
++ } else {
++ /* rename(2) case */
++ err = -EIO;
++ if (unlikely(!h_inode || !h_inode->i_nlink))
++ goto out;
++
++ h_mode = h_inode->i_mode;
++ if (!isdir) {
++ err = -EISDIR;
++ if (unlikely(S_ISDIR(h_mode)))
++ goto out;
++ } else if (unlikely(!S_ISDIR(h_mode))) {
++ err = -ENOTDIR;
++ goto out;
++ }
++ }
++
++ err = -EIO;
++ /* expected parent dir is locked */
++ if (unlikely(h_parent != h_dentry->d_parent))
++ goto out;
++ err = 0;
++
++ out:
++ return err;
++}
++
++/*
++ * initial procedure of adding a new entry.
++ * prepare writable branch and the parent dir, lock it,
++ * and lookup whiteout for the new entry.
++ */
++static struct dentry*
++lock_hdir_lkup_wh(struct dentry *dentry, struct au_dtime *dt,
++ struct dentry *src_dentry, struct au_pin *pin,
++ struct au_wr_dir_args *wr_dir_args)
++{
++ struct dentry *wh_dentry, *h_parent;
++ struct super_block *sb;
++ struct au_branch *br;
++ int err;
++ unsigned int udba;
++ aufs_bindex_t bcpup;
++
++ err = au_wr_dir(dentry, src_dentry, wr_dir_args);
++ bcpup = err;
++ wh_dentry = ERR_PTR(err);
++ if (unlikely(err < 0))
++ goto out;
++
++ sb = dentry->d_sb;
++ udba = au_opt_udba(sb);
++ err = au_pin(pin, dentry, bcpup, udba,
++ AuPin_DI_LOCKED | AuPin_MNT_WRITE);
++ wh_dentry = ERR_PTR(err);
++ if (unlikely(err))
++ goto out;
++
++ h_parent = au_pinned_h_parent(pin);
++ if (udba != AuOpt_UDBA_NONE
++ && au_dbstart(dentry) == bcpup) {
++ err = au_may_add(dentry, bcpup, h_parent,
++ au_ftest_wrdir(wr_dir_args->flags, ISDIR));
++ wh_dentry = ERR_PTR(err);
++ if (unlikely(err))
++ goto out_unpin;
++ }
++
++ br = au_sbr(sb, bcpup);
++ if (dt) {
++ struct path tmp = {
++ .dentry = h_parent,
++ .mnt = br->br_mnt
++ };
++ au_dtime_store(dt, au_pinned_parent(pin), &tmp);
++ }
++
++ wh_dentry = NULL;
++ if (bcpup != au_dbwh(dentry))
++ goto out; /* success */
++
++ wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, br);
++
++ out_unpin:
++ if (IS_ERR(wh_dentry))
++ au_unpin(pin);
++ out:
++ 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;
++ aufs_bindex_t bstart;
++ unsigned char created;
++ struct au_dtime dt;
++ struct au_pin pin;
++ struct path h_path;
++ struct dentry *wh_dentry, *parent;
++ struct inode *h_dir;
++ struct au_wr_dir_args wr_dir_args = {
++ .force_btgt = -1,
++ .flags = AuWrDir_ADD_ENTRY
++ };
++
++ IMustLock(dir);
++
++ parent = dentry->d_parent; /* dir inode is locked */
++ aufs_read_lock(dentry, AuLock_DW);
++ di_write_lock_parent(parent);
++ wh_dentry = lock_hdir_lkup_wh(dentry, &dt, /*src_dentry*/NULL, &pin,
++ &wr_dir_args);
++ err = PTR_ERR(wh_dentry);
++ if (IS_ERR(wh_dentry))
++ goto out;
++
++ bstart = au_dbstart(dentry);
++ h_path.dentry = au_h_dptr(dentry, bstart);
++ h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart);
++ h_dir = au_pinned_h_dir(&pin);
++ switch (arg->type) {
++ case Creat:
++ err = vfsub_create(h_dir, &h_path, arg->u.c.mode);
++ break;
++ case Symlink:
++ err = vfsub_symlink(h_dir, &h_path, arg->u.s.symname);
++ break;
++ case Mknod:
++ err = vfsub_mknod(h_dir, &h_path, arg->u.m.mode, arg->u.m.dev);
++ break;
++ default:
++ BUG();
++ }
++ created = !err;
++ if (!err)
++ err = epilog(dir, bstart, wh_dentry, dentry);
++
++ /* revert */
++ if (unlikely(created && err && h_path.dentry->d_inode)) {
++ int rerr;
++ rerr = vfsub_unlink(h_dir, &h_path, /*force*/0);
++ if (rerr) {
++ AuIOErr("%.*s revert failure(%d, %d)\n",
++ AuDLNPair(dentry), err, rerr);
++ err = -EIO;
++ }
++ au_dtime_revert(&dt);
++ d_drop(dentry);
++ }
++
++ au_unpin(&pin);
++ dput(wh_dentry);
++
++ out:
++ if (unlikely(err)) {
++ au_update_dbstart(dentry);
++ d_drop(dentry);
++ }
++ di_write_unlock(parent);
++ aufs_read_unlock(dentry, AuLock_DW);
++ 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 au_link_args {
++ aufs_bindex_t bdst, bsrc;
++ struct au_pin pin;
++ struct path h_path;
++ struct dentry *src_parent, *parent;
++};
++
++static int au_cpup_before_link(struct dentry *src_dentry,
++ struct au_link_args *a)
++{
++ int err;
++ struct dentry *h_src_dentry;
++ struct mutex *h_mtx;
++
++ di_read_lock_parent(a->src_parent, AuLock_IR);
++ err = au_test_and_cpup_dirs(src_dentry, a->bdst);
++ if (unlikely(err))
++ goto out;
++
++ h_src_dentry = au_h_dptr(src_dentry, a->bsrc);
++ h_mtx = &h_src_dentry->d_inode->i_mutex;
++ err = au_pin(&a->pin, src_dentry, a->bdst,
++ au_opt_udba(src_dentry->d_sb),
++ AuPin_DI_LOCKED | AuPin_MNT_WRITE);
++ if (unlikely(err))
++ goto out;
++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD);
++ err = au_sio_cpup_simple(src_dentry, a->bdst, -1,
++ AuCpup_DTIME /* | AuCpup_KEEPLINO */);
++ mutex_unlock(h_mtx);
++ au_unpin(&a->pin);
++
++ out:
++ di_read_unlock(a->src_parent, AuLock_IR);
++ return err;
++}
++
++static int au_cpup_or_link(struct dentry *src_dentry, struct au_link_args *a)
++{
++ int err;
++ unsigned char plink;
++ struct inode *h_inode, *inode;
++ struct dentry *h_src_dentry;
++ struct super_block *sb;
++
++ plink = 0;
++ h_inode = NULL;
++ sb = src_dentry->d_sb;
++ inode = src_dentry->d_inode;
++ if (au_ibstart(inode) <= a->bdst)
++ h_inode = au_h_iptr(inode, a->bdst);
++ if (!h_inode || !h_inode->i_nlink) {
++ /* copyup src_dentry as the name of dentry. */
++ au_set_dbstart(src_dentry, a->bdst);
++ au_set_h_dptr(src_dentry, a->bdst, dget(a->h_path.dentry));
++ h_inode = au_h_dptr(src_dentry, a->bsrc)->d_inode;
++ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);
++ err = au_sio_cpup_single(src_dentry, a->bdst, a->bsrc, -1,
++ AuCpup_KEEPLINO, a->parent);
++ mutex_unlock(&h_inode->i_mutex);
++ au_set_h_dptr(src_dentry, a->bdst, NULL);
++ au_set_dbstart(src_dentry, a->bsrc);
++ } else {
++ /* the inode of src_dentry already exists on a.bdst branch */
++ h_src_dentry = d_find_alias(h_inode);
++ if (!h_src_dentry && au_plink_test(inode)) {
++ plink = 1;
++ h_src_dentry = au_plink_lkup(inode, a->bdst);
++ err = PTR_ERR(h_src_dentry);
++ if (IS_ERR(h_src_dentry))
++ goto out;
++
++ if (unlikely(!h_src_dentry->d_inode)) {
++ dput(h_src_dentry);
++ h_src_dentry = NULL;
++ }
++
++ }
++ if (h_src_dentry) {
++ err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin),
++ &a->h_path);
++ dput(h_src_dentry);
++ } else {
++ AuIOErr("no dentry found for hi%lu on b%d\n",
++ h_inode->i_ino, a->bdst);
++ err = -EIO;
++ }
++ }
++
++ if (!err && !plink)
++ au_plink_append(inode, a->bdst, a->h_path.dentry);
++
++out:
++ return err;
++}
++
++int aufs_link(struct dentry *src_dentry, struct inode *dir,
++ struct dentry *dentry)
++{
++ int err, rerr;
++ struct au_dtime dt;
++ struct au_link_args *a;
++ struct dentry *wh_dentry, *h_src_dentry;
++ struct inode *inode;
++ struct super_block *sb;
++ struct au_wr_dir_args wr_dir_args = {
++ /* .force_btgt = -1, */
++ .flags = AuWrDir_ADD_ENTRY
++ };
++
++ IMustLock(dir);
++ inode = src_dentry->d_inode;
++ IMustLock(inode);
++
++ err = -ENOENT;
++ if (unlikely(!inode->i_nlink))
++ goto out;
++
++ err = -ENOMEM;
++ a = kzalloc(sizeof(*a), GFP_NOFS);
++ if (unlikely(!a))
++ goto out;
++
++ a->parent = dentry->d_parent; /* dir inode is locked */
++ aufs_read_and_write_lock2(dentry, src_dentry, /*AuLock_FLUSH*/0);
++ a->src_parent = dget_parent(src_dentry);
++ wr_dir_args.force_btgt = au_dbstart(src_dentry);
++
++ di_write_lock_parent(a->parent);
++ wr_dir_args.force_btgt = au_wbr(dentry, wr_dir_args.force_btgt);
++ wh_dentry = lock_hdir_lkup_wh(dentry, &dt, src_dentry, &a->pin,
++ &wr_dir_args);
++ err = PTR_ERR(wh_dentry);
++ if (IS_ERR(wh_dentry))
++ goto out_unlock;
++
++ err = 0;
++ sb = dentry->d_sb;
++ a->bdst = au_dbstart(dentry);
++ a->h_path.dentry = au_h_dptr(dentry, a->bdst);
++ a->h_path.mnt = au_sbr_mnt(sb, a->bdst);
++ a->bsrc = au_dbstart(src_dentry);
++ if (au_opt_test(au_mntflags(sb), PLINK)) {
++ if (a->bdst < a->bsrc
++ /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */)
++ err = au_cpup_or_link(src_dentry, a);
++ else {
++ h_src_dentry = au_h_dptr(src_dentry, a->bdst);
++ err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin),
++ &a->h_path);
++ }
++ } else {
++ /*
++ * copyup src_dentry to the branch we process,
++ * and then link(2) to it.
++ */
++ if (a->bdst < a->bsrc
++ /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */) {
++ au_unpin(&a->pin);
++ di_write_unlock(a->parent);
++ err = au_cpup_before_link(src_dentry, a);
++ di_write_lock_parent(a->parent);
++ if (!err)
++ err = au_pin(&a->pin, dentry, a->bdst,
++ au_opt_udba(sb),
++ AuPin_DI_LOCKED | AuPin_MNT_WRITE);
++ if (unlikely(err))
++ goto out_wh;
++ }
++ if (!err) {
++ h_src_dentry = au_h_dptr(src_dentry, a->bdst);
++ err = -ENOENT;
++ if (h_src_dentry && h_src_dentry->d_inode)
++ err = vfsub_link(h_src_dentry,
++ au_pinned_h_dir(&a->pin),
++ &a->h_path);
++ }
++ }
++ if (unlikely(err))
++ goto out_unpin;
++
++ if (wh_dentry) {
++ a->h_path.dentry = wh_dentry;
++ err = au_wh_unlink_dentry(au_pinned_h_dir(&a->pin), &a->h_path,
++ dentry);
++ if (unlikely(err))
++ goto out_revert;
++ }
++
++ dir->i_version++;
++ if (au_ibstart(dir) == au_dbstart(dentry))
++ au_cpup_attr_timesizes(dir);
++ inc_nlink(inode);
++ inode->i_ctime = dir->i_ctime;
++ if (!d_unhashed(a->h_path.dentry))
++ d_instantiate(dentry, au_igrab(inode));
++ else
++ /* some filesystem calls d_drop() */
++ d_drop(dentry);
++ goto out_unpin; /* success */
++
++ out_revert:
++ rerr = vfsub_unlink(au_pinned_h_dir(&a->pin), &a->h_path, /*force*/0);
++ if (!rerr)
++ goto out_dt;
++ AuIOErr("%.*s reverting failed(%d, %d)\n",
++ AuDLNPair(dentry), err, rerr);
++ err = -EIO;
++ out_dt:
++ d_drop(dentry);
++ au_dtime_revert(&dt);
++ out_unpin:
++ au_unpin(&a->pin);
++ out_wh:
++ dput(wh_dentry);
++ out_unlock:
++ if (unlikely(err)) {
++ au_update_dbstart(dentry);
++ d_drop(dentry);
++ }
++ di_write_unlock(a->parent);
++ dput(a->src_parent);
++ aufs_read_and_write_unlock2(dentry, src_dentry);
++ kfree(a);
++ out:
++ return err;
++}
++
++int aufs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
++{
++ int err, rerr;
++ aufs_bindex_t bindex;
++ unsigned char diropq;
++ struct path h_path;
++ struct dentry *wh_dentry, *parent, *opq_dentry;
++ struct mutex *h_mtx;
++ struct super_block *sb;
++ struct {
++ struct au_pin pin;
++ struct au_dtime dt;
++ } *a; /* reduce the stack usage */
++ struct au_wr_dir_args wr_dir_args = {
++ .force_btgt = -1,
++ .flags = AuWrDir_ADD_ENTRY | AuWrDir_ISDIR
++ };
++
++ IMustLock(dir);
++
++ err = -ENOMEM;
++ a = kmalloc(sizeof(*a), GFP_NOFS);
++ if (unlikely(!a))
++ goto out;
++
++ aufs_read_lock(dentry, AuLock_DW);
++ parent = dentry->d_parent; /* dir inode is locked */
++ di_write_lock_parent(parent);
++ wh_dentry = lock_hdir_lkup_wh(dentry, &a->dt, /*src_dentry*/NULL,
++ &a->pin, &wr_dir_args);
++ err = PTR_ERR(wh_dentry);
++ if (IS_ERR(wh_dentry))
++ goto out_free;
++
++ sb = dentry->d_sb;
++ bindex = au_dbstart(dentry);
++ h_path.dentry = au_h_dptr(dentry, bindex);
++ h_path.mnt = au_sbr_mnt(sb, bindex);
++ err = vfsub_mkdir(au_pinned_h_dir(&a->pin), &h_path, mode);
++ if (unlikely(err))
++ goto out_unlock;
++
++ /* make the dir opaque */
++ diropq = 0;
++ h_mtx = &h_path.dentry->d_inode->i_mutex;
++ if (wh_dentry
++ || au_opt_test(au_mntflags(sb), ALWAYS_DIROPQ)) {
++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD);
++ opq_dentry = au_diropq_create(dentry, bindex);
++ mutex_unlock(h_mtx);
++ err = PTR_ERR(opq_dentry);
++ if (IS_ERR(opq_dentry))
++ goto out_dir;
++ dput(opq_dentry);
++ diropq = 1;
++ }
++
++ err = epilog(dir, bindex, wh_dentry, dentry);
++ if (!err) {
++ inc_nlink(dir);
++ goto out_unlock; /* success */
++ }
++
++ /* revert */
++ if (diropq) {
++ AuLabel(revert opq);
++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD);
++ rerr = au_diropq_remove(dentry, bindex);
++ mutex_unlock(h_mtx);
++ if (rerr) {
++ AuIOErr("%.*s reverting diropq failed(%d, %d)\n",
++ AuDLNPair(dentry), err, rerr);
++ err = -EIO;
++ }
++ }
++
++ out_dir:
++ AuLabel(revert dir);
++ rerr = vfsub_rmdir(au_pinned_h_dir(&a->pin), &h_path);
++ if (rerr) {
++ AuIOErr("%.*s reverting dir failed(%d, %d)\n",
++ AuDLNPair(dentry), err, rerr);
++ err = -EIO;
++ }
++ d_drop(dentry);
++ au_dtime_revert(&a->dt);
++ out_unlock:
++ au_unpin(&a->pin);
++ dput(wh_dentry);
++ out_free:
++ if (unlikely(err)) {
++ au_update_dbstart(dentry);
++ d_drop(dentry);
++ }
++ di_write_unlock(parent);
++ aufs_read_unlock(dentry, AuLock_DW);
++ kfree(a);
++ out:
++ return err;
++}
+diff -Nur linux-2.6.31.5.orig/fs/aufs/i_op.c linux-2.6.31.5/fs/aufs/i_op.c
+--- linux-2.6.31.5.orig/fs/aufs/i_op.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/i_op.c 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,872 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. Okajima
++ *
++ * This program, aufs is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/*
++ * inode operations (except add/del/rename)
++ */
++
++#include <linux/device_cgroup.h>
++#include <linux/fs_stack.h>
++#include <linux/mm.h>
++#include <linux/namei.h>
++#include <linux/security.h>
++#include <linux/uaccess.h>
++#include "aufs.h"
++
++static int h_permission(struct inode *h_inode, int mask,
++ struct vfsmount *h_mnt, int brperm)
++{
++ int err;
++ const unsigned char write_mask = !!(mask & (MAY_WRITE | MAY_APPEND));
++
++ err = -EACCES;
++ if ((write_mask && IS_IMMUTABLE(h_inode))
++ || ((mask & MAY_EXEC)
++ && S_ISREG(h_inode->i_mode)
++ && ((h_mnt->mnt_flags & MNT_NOEXEC)
++ || !(h_inode->i_mode & S_IXUGO))))
++ goto out;
++
++ /*
++ * - skip the lower fs test in the case of write to ro branch.
++ * - nfs dir permission write check is optimized, but a policy for
++ * link/rename requires a real check.
++ */
++ if ((write_mask && !au_br_writable(brperm))
++ || (au_test_nfs(h_inode->i_sb) && S_ISDIR(h_inode->i_mode)
++ && write_mask && !(mask & MAY_READ))
++ || !h_inode->i_op->permission) {
++ /* AuLabel(generic_permission); */
++ err = generic_permission(h_inode, mask, NULL);
++ } else {
++ /* AuLabel(h_inode->permission); */
++ err = h_inode->i_op->permission(h_inode, mask);
++ AuTraceErr(err);
++ }
++
++ if (!err)
++ err = devcgroup_inode_permission(h_inode, mask);
++ if (!err)
++ err = security_inode_permission
++ (h_inode, mask & (MAY_READ | MAY_WRITE | MAY_EXEC
++ | MAY_APPEND));
++
++ out:
++ return err;
++}
++
++static int aufs_permission(struct inode *inode, int mask)
++{
++ int err;
++ aufs_bindex_t bindex, bend;
++ const unsigned char isdir = !!S_ISDIR(inode->i_mode);
++ const unsigned char write_mask = !!(mask & (MAY_WRITE | MAY_APPEND));
++ struct inode *h_inode;
++ struct super_block *sb;
++ struct au_branch *br;
++
++ sb = inode->i_sb;
++ si_read_lock(sb, AuLock_FLUSH);
++ ii_read_lock_child(inode);
++
++ if (!isdir || write_mask) {
++ h_inode = au_h_iptr(inode, au_ibstart(inode));
++ AuDebugOn(!h_inode
++ || ((h_inode->i_mode & S_IFMT)
++ != (inode->i_mode & S_IFMT)));
++ err = 0;
++ bindex = au_ibstart(inode);
++ br = au_sbr(sb, bindex);
++ err = h_permission(h_inode, mask, br->br_mnt, br->br_perm);
++
++ if (write_mask && !err) {
++ /* test whether the upper writable branch exists */
++ err = -EROFS;
++ for (; bindex >= 0; bindex--)
++ if (!au_br_rdonly(au_sbr(sb, bindex))) {
++ err = 0;
++ break;
++ }
++ }
++ goto out;
++ }
++
++ /* non-write to dir */
++ err = 0;
++ bend = au_ibend(inode);
++ for (bindex = au_ibstart(inode); !err && bindex <= bend; bindex++) {
++ h_inode = au_h_iptr(inode, bindex);
++ if (h_inode) {
++ AuDebugOn(!S_ISDIR(h_inode->i_mode));
++ br = au_sbr(sb, bindex);
++ err = h_permission(h_inode, mask, br->br_mnt,
++ br->br_perm);
++ }
++ }
++
++ out:
++ ii_read_unlock(inode);
++ si_read_unlock(sb);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry,
++ struct nameidata *nd)
++{
++ struct dentry *ret, *parent;
++ struct inode *inode, *h_inode;
++ struct mutex *mtx;
++ struct super_block *sb;
++ int err, npositive;
++ aufs_bindex_t bstart;
++
++ /* temporary workaround for a bug in NFSD readdir */
++ if (!au_test_nfsd(current))
++ IMustLock(dir);
++ else
++ WARN_ONCE(!mutex_is_locked(&dir->i_mutex),
++ "a known problem of NFSD readdir since 2.6.28\n");
++
++ sb = dir->i_sb;
++ si_read_lock(sb, AuLock_FLUSH);
++ err = au_alloc_dinfo(dentry);
++ ret = ERR_PTR(err);
++ if (unlikely(err))
++ goto out;
++
++ parent = dentry->d_parent; /* dir inode is locked */
++ di_read_lock_parent(parent, AuLock_IR);
++ npositive = au_lkup_dentry(dentry, au_dbstart(parent), /*type*/0, nd);
++ di_read_unlock(parent, AuLock_IR);
++ err = npositive;
++ ret = ERR_PTR(err);
++ if (unlikely(err < 0))
++ goto out_unlock;
++
++ inode = NULL;
++ if (npositive) {
++ bstart = au_dbstart(dentry);
++ h_inode = au_h_dptr(dentry, bstart)->d_inode;
++ if (!S_ISDIR(h_inode->i_mode)) {
++ /*
++ * stop 'race'-ing between hardlinks under different
++ * parents.
++ */
++ mtx = &au_sbr(sb, bstart)->br_xino.xi_nondir_mtx;
++ mutex_lock(mtx);
++ inode = au_new_inode(dentry, /*must_new*/0);
++ mutex_unlock(mtx);
++ } else
++ inode = au_new_inode(dentry, /*must_new*/0);
++ ret = (void *)inode;
++ }
++ if (IS_ERR(inode))
++ goto out_unlock;
++
++ ret = d_splice_alias(inode, dentry);
++ if (unlikely(IS_ERR(ret) && inode))
++ ii_write_unlock(inode);
++ au_store_oflag(nd, inode);
++
++ out_unlock:
++ di_write_unlock(dentry);
++ out:
++ si_read_unlock(sb);
++ return ret;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static int au_wr_dir_cpup(struct dentry *dentry, struct dentry *parent,
++ const unsigned char add_entry, aufs_bindex_t bcpup,
++ aufs_bindex_t bstart)
++{
++ int err;
++ struct dentry *h_parent;
++ struct inode *h_dir;
++
++ if (add_entry) {
++ au_update_dbstart(dentry);
++ IMustLock(parent->d_inode);
++ } else
++ di_write_lock_parent(parent);
++
++ err = 0;
++ if (!au_h_dptr(parent, bcpup)) {
++ if (bstart < bcpup)
++ err = au_cpdown_dirs(dentry, bcpup);
++ else
++ err = au_cpup_dirs(dentry, bcpup);
++ }
++ if (!err && add_entry) {
++ h_parent = au_h_dptr(parent, bcpup);
++ h_dir = h_parent->d_inode;
++ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT);
++ err = au_lkup_neg(dentry, bcpup);
++ /* todo: no unlock here */
++ mutex_unlock(&h_dir->i_mutex);
++ if (bstart < bcpup && au_dbstart(dentry) < 0) {
++ au_set_dbstart(dentry, 0);
++ au_update_dbrange(dentry, /*do_put_zero*/0);
++ }
++ }
++
++ if (!add_entry)
++ di_write_unlock(parent);
++ if (!err)
++ err = bcpup; /* success */
++
++ return err;
++}
++
++/*
++ * decide the branch and the parent dir where we will create a new entry.
++ * returns new bindex or an error.
++ * copyup the parent dir if needed.
++ */
++int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry,
++ struct au_wr_dir_args *args)
++{
++ int err;
++ aufs_bindex_t bcpup, bstart, src_bstart;
++ const unsigned char add_entry = !!au_ftest_wrdir(args->flags,
++ ADD_ENTRY);
++ struct super_block *sb;
++ struct dentry *parent;
++ struct au_sbinfo *sbinfo;
++
++ sb = dentry->d_sb;
++ sbinfo = au_sbi(sb);
++ parent = dget_parent(dentry);
++ bstart = au_dbstart(dentry);
++ bcpup = bstart;
++ if (args->force_btgt < 0) {
++ if (src_dentry) {
++ src_bstart = au_dbstart(src_dentry);
++ if (src_bstart < bstart)
++ bcpup = src_bstart;
++ } else if (add_entry) {
++ err = AuWbrCreate(sbinfo, dentry,
++ au_ftest_wrdir(args->flags, ISDIR));
++ bcpup = err;
++ }
++
++ if (bcpup < 0 || au_test_ro(sb, bcpup, dentry->d_inode)) {
++ if (add_entry)
++ err = AuWbrCopyup(sbinfo, dentry);
++ else {
++ if (!IS_ROOT(dentry)) {
++ di_read_lock_parent(parent, !AuLock_IR);
++ err = AuWbrCopyup(sbinfo, dentry);
++ di_read_unlock(parent, !AuLock_IR);
++ } else
++ err = AuWbrCopyup(sbinfo, dentry);
++ }
++ bcpup = err;
++ if (unlikely(err < 0))
++ goto out;
++ }
++ } else {
++ bcpup = args->force_btgt;
++ AuDebugOn(au_test_ro(sb, bcpup, dentry->d_inode));
++ }
++ AuDbg("bstart %d, bcpup %d\n", bstart, bcpup);
++ if (bstart < bcpup)
++ au_update_dbrange(dentry, /*do_put_zero*/1);
++
++ err = bcpup;
++ if (bcpup == bstart)
++ goto out; /* success */
++
++ /* copyup the new parent into the branch we process */
++ err = au_wr_dir_cpup(dentry, parent, add_entry, bcpup, bstart);
++
++ out:
++ dput(parent);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++struct dentry *au_pinned_h_parent(struct au_pin *pin)
++{
++ if (pin && pin->parent)
++ return au_h_dptr(pin->parent, pin->bindex);
++ return NULL;
++}
++
++void au_unpin(struct au_pin *p)
++{
++ if (au_ftest_pin(p->flags, MNT_WRITE))
++ mnt_drop_write(p->h_mnt);
++ if (!p->hdir)
++ return;
++
++ au_hin_imtx_unlock(p->hdir);
++ if (!au_ftest_pin(p->flags, DI_LOCKED))
++ di_read_unlock(p->parent, AuLock_IR);
++ iput(p->hdir->hi_inode);
++ dput(p->parent);
++ p->parent = NULL;
++ p->hdir = NULL;
++ p->h_mnt = NULL;
++}
++
++int au_do_pin(struct au_pin *p)
++{
++ int err;
++ struct super_block *sb;
++ struct dentry *h_dentry, *h_parent;
++ struct au_branch *br;
++ struct inode *h_dir;
++
++ err = 0;
++ sb = p->dentry->d_sb;
++ br = au_sbr(sb, p->bindex);
++ if (IS_ROOT(p->dentry)) {
++ if (au_ftest_pin(p->flags, MNT_WRITE)) {
++ p->h_mnt = br->br_mnt;
++ err = mnt_want_write(p->h_mnt);
++ if (unlikely(err)) {
++ au_fclr_pin(p->flags, MNT_WRITE);
++ goto out_err;
++ }
++ }
++ goto out;
++ }
++
++ h_dentry = NULL;
++ if (p->bindex <= au_dbend(p->dentry))
++ h_dentry = au_h_dptr(p->dentry, p->bindex);
++
++ p->parent = dget_parent(p->dentry);
++ if (!au_ftest_pin(p->flags, DI_LOCKED))
++ di_read_lock(p->parent, AuLock_IR, p->lsc_di);
++
++ h_dir = NULL;
++ h_parent = au_h_dptr(p->parent, p->bindex);
++ p->hdir = au_hi(p->parent->d_inode, p->bindex);
++ if (p->hdir)
++ h_dir = p->hdir->hi_inode;
++
++ /* udba case */
++ if (unlikely(!p->hdir || !h_dir)) {
++ if (!au_ftest_pin(p->flags, DI_LOCKED))
++ di_read_unlock(p->parent, AuLock_IR);
++ dput(p->parent);
++ p->parent = NULL;
++ goto out_err;
++ }
++
++ au_igrab(h_dir);
++ au_hin_imtx_lock_nested(p->hdir, p->lsc_hi);
++
++ if (unlikely(p->hdir->hi_inode != h_parent->d_inode)) {
++ err = -EBUSY;
++ goto out_unpin;
++ }
++ if (h_dentry) {
++ err = au_h_verify(h_dentry, p->udba, h_dir, h_parent, br);
++ if (unlikely(err)) {
++ au_fclr_pin(p->flags, MNT_WRITE);
++ goto out_unpin;
++ }
++ }
++
++ if (au_ftest_pin(p->flags, MNT_WRITE)) {
++ p->h_mnt = br->br_mnt;
++ err = mnt_want_write(p->h_mnt);
++ if (unlikely(err)) {
++ au_fclr_pin(p->flags, MNT_WRITE);
++ goto out_unpin;
++ }
++ }
++ goto out; /* success */
++
++ out_unpin:
++ au_unpin(p);
++ out_err:
++ AuErr("err %d\n", err);
++ err = au_busy_or_stale();
++ out:
++ return err;
++}
++
++void au_pin_init(struct au_pin *p, struct dentry *dentry,
++ aufs_bindex_t bindex, int lsc_di, int lsc_hi,
++ unsigned int udba, unsigned char flags)
++{
++ p->dentry = dentry;
++ p->udba = udba;
++ p->lsc_di = lsc_di;
++ p->lsc_hi = lsc_hi;
++ p->flags = flags;
++ p->bindex = bindex;
++
++ p->parent = NULL;
++ p->hdir = NULL;
++ p->h_mnt = NULL;
++}
++
++int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex,
++ unsigned int udba, unsigned char flags)
++{
++ au_pin_init(pin, dentry, bindex, AuLsc_DI_PARENT, AuLsc_I_PARENT2,
++ udba, flags);
++ return au_do_pin(pin);
++}
++
++/* ---------------------------------------------------------------------- */
++
++#define AuIcpup_DID_CPUP 1
++#define au_ftest_icpup(flags, name) ((flags) & AuIcpup_##name)
++#define au_fset_icpup(flags, name) { (flags) |= AuIcpup_##name; }
++#define au_fclr_icpup(flags, name) { (flags) &= ~AuIcpup_##name; }
++
++struct au_icpup_args {
++ unsigned char flags;
++ unsigned char pin_flags;
++ aufs_bindex_t btgt;
++ struct au_pin pin;
++ struct path h_path;
++ struct inode *h_inode;
++};
++
++static int au_lock_and_icpup(struct dentry *dentry, struct iattr *ia,
++ struct au_icpup_args *a)
++{
++ int err;
++ unsigned int udba;
++ loff_t sz;
++ aufs_bindex_t bstart;
++ struct dentry *hi_wh, *parent;
++ struct inode *inode;
++ struct au_wr_dir_args wr_dir_args = {
++ .force_btgt = -1,
++ .flags = 0
++ };
++
++ di_write_lock_child(dentry);
++ bstart = au_dbstart(dentry);
++ inode = dentry->d_inode;
++ if (S_ISDIR(inode->i_mode))
++ au_fset_wrdir(wr_dir_args.flags, ISDIR);
++ /* plink or hi_wh() case */
++ if (bstart != au_ibstart(inode))
++ wr_dir_args.force_btgt = au_ibstart(inode);
++ err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args);
++ if (unlikely(err < 0))
++ goto out_dentry;
++ a->btgt = err;
++ if (err != bstart)
++ au_fset_icpup(a->flags, DID_CPUP);
++
++ err = 0;
++ a->pin_flags = AuPin_MNT_WRITE;
++ parent = NULL;
++ if (!IS_ROOT(dentry)) {
++ au_fset_pin(a->pin_flags, DI_LOCKED);
++ parent = dget_parent(dentry);
++ di_write_lock_parent(parent);
++ }
++
++ udba = au_opt_udba(dentry->d_sb);
++ if (d_unhashed(dentry) || (ia->ia_valid & ATTR_FILE))
++ udba = AuOpt_UDBA_NONE;
++ err = au_pin(&a->pin, dentry, a->btgt, udba, a->pin_flags);
++ if (unlikely(err)) {
++ if (parent) {
++ di_write_unlock(parent);
++ dput(parent);
++ }
++ goto out_dentry;
++ }
++ a->h_path.dentry = au_h_dptr(dentry, bstart);
++ a->h_inode = a->h_path.dentry->d_inode;
++ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD);
++ sz = -1;
++ if ((ia->ia_valid & ATTR_SIZE) && ia->ia_size < i_size_read(a->h_inode))
++ sz = ia->ia_size;
++
++ hi_wh = NULL;
++ if (au_ftest_icpup(a->flags, DID_CPUP) && d_unhashed(dentry)) {
++ hi_wh = au_hi_wh(inode, a->btgt);
++ if (!hi_wh) {
++ err = au_sio_cpup_wh(dentry, a->btgt, sz, /*file*/NULL);
++ if (unlikely(err))
++ goto out_unlock;
++ hi_wh = au_hi_wh(inode, a->btgt);
++ /* todo: revalidate hi_wh? */
++ }
++ }
++
++ if (parent) {
++ au_pin_set_parent_lflag(&a->pin, /*lflag*/0);
++ di_downgrade_lock(parent, AuLock_IR);
++ dput(parent);
++ }
++ if (!au_ftest_icpup(a->flags, DID_CPUP))
++ goto out; /* success */
++
++ if (!d_unhashed(dentry)) {
++ err = au_sio_cpup_simple(dentry, a->btgt, sz, AuCpup_DTIME);
++ if (!err)
++ a->h_path.dentry = au_h_dptr(dentry, a->btgt);
++ } else if (!hi_wh)
++ a->h_path.dentry = au_h_dptr(dentry, a->btgt);
++ else
++ a->h_path.dentry = hi_wh; /* do not dget here */
++
++ out_unlock:
++ mutex_unlock(&a->h_inode->i_mutex);
++ a->h_inode = a->h_path.dentry->d_inode;
++ if (!err) {
++ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD);
++ goto out; /* success */
++ }
++
++ au_unpin(&a->pin);
++
++ out_dentry:
++ di_write_unlock(dentry);
++ out:
++ return err;
++}
++
++static int aufs_setattr(struct dentry *dentry, struct iattr *ia)
++{
++ int err;
++ struct inode *inode;
++ struct super_block *sb;
++ struct file *file;
++ struct au_icpup_args *a;
++
++ err = -ENOMEM;
++ a = kzalloc(sizeof(*a), GFP_NOFS);
++ if (unlikely(!a))
++ goto out;
++
++ inode = dentry->d_inode;
++ IMustLock(inode);
++ sb = dentry->d_sb;
++ si_read_lock(sb, AuLock_FLUSH);
++
++ file = NULL;
++ if (ia->ia_valid & ATTR_FILE) {
++ /* currently ftruncate(2) only */
++ file = ia->ia_file;
++ fi_write_lock(file);
++ ia->ia_file = au_h_fptr(file, au_fbstart(file));
++ }
++
++ if (ia->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID))
++ ia->ia_valid &= ~ATTR_MODE;
++
++ err = au_lock_and_icpup(dentry, ia, a);
++ if (unlikely(err < 0))
++ goto out_si;
++ if (au_ftest_icpup(a->flags, DID_CPUP)) {
++ ia->ia_file = NULL;
++ ia->ia_valid &= ~ATTR_FILE;
++ }
++
++ a->h_path.mnt = au_sbr_mnt(sb, a->btgt);
++ if (ia->ia_valid & ATTR_SIZE) {
++ struct file *f;
++
++ if (ia->ia_size < i_size_read(inode)) {
++ /* unmap only */
++ err = vmtruncate(inode, ia->ia_size);
++ if (unlikely(err))
++ goto out_unlock;
++ }
++
++ f = NULL;
++ if (ia->ia_valid & ATTR_FILE)
++ f = ia->ia_file;
++ mutex_unlock(&a->h_inode->i_mutex);
++ err = vfsub_trunc(&a->h_path, ia->ia_size, ia->ia_valid, f);
++ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD);
++ } else
++ err = vfsub_notify_change(&a->h_path, ia);
++ if (!err)
++ au_cpup_attr_changeable(inode);
++
++ out_unlock:
++ mutex_unlock(&a->h_inode->i_mutex);
++ au_unpin(&a->pin);
++ di_write_unlock(dentry);
++ out_si:
++ if (file) {
++ fi_write_unlock(file);
++ ia->ia_file = file;
++ ia->ia_valid |= ATTR_FILE;
++ }
++ si_read_unlock(sb);
++ kfree(a);
++ out:
++ return err;
++}
++
++static int au_getattr_lock_reval(struct dentry *dentry, unsigned int sigen)
++{
++ int err;
++ struct inode *inode;
++ struct dentry *parent;
++
++ err = 0;
++ inode = dentry->d_inode;
++ di_write_lock_child(dentry);
++ if (au_digen(dentry) != sigen || au_iigen(inode) != sigen) {
++ parent = dget_parent(dentry);
++ di_read_lock_parent(parent, AuLock_IR);
++ /* returns a number of positive dentries */
++ err = au_refresh_hdentry(dentry, inode->i_mode & S_IFMT);
++ if (err > 0)
++ err = au_refresh_hinode(inode, dentry);
++ di_read_unlock(parent, AuLock_IR);
++ dput(parent);
++ if (unlikely(!err))
++ err = -EIO;
++ }
++ di_downgrade_lock(dentry, AuLock_IR);
++ if (unlikely(err))
++ di_read_unlock(dentry, AuLock_IR);
++
++ return err;
++}
++
++static void au_refresh_iattr(struct inode *inode, struct kstat *st,
++ unsigned int nlink)
++{
++ inode->i_mode = st->mode;
++ inode->i_uid = st->uid;
++ inode->i_gid = st->gid;
++ inode->i_atime = st->atime;
++ inode->i_mtime = st->mtime;
++ inode->i_ctime = st->ctime;
++
++ au_cpup_attr_nlink(inode, /*force*/0);
++ if (S_ISDIR(inode->i_mode)) {
++ inode->i_nlink -= nlink;
++ inode->i_nlink += st->nlink;
++ }
++
++ spin_lock(&inode->i_lock);
++ inode->i_blocks = st->blocks;
++ i_size_write(inode, st->size);
++ spin_unlock(&inode->i_lock);
++}
++
++static int aufs_getattr(struct vfsmount *mnt __maybe_unused,
++ struct dentry *dentry, struct kstat *st)
++{
++ int err;
++ unsigned int mnt_flags;
++ aufs_bindex_t bindex;
++ unsigned char udba_none, positive;
++ struct super_block *sb, *h_sb;
++ struct inode *inode;
++ struct vfsmount *h_mnt;
++ struct dentry *h_dentry;
++
++ err = 0;
++ sb = dentry->d_sb;
++ inode = dentry->d_inode;
++ si_read_lock(sb, AuLock_FLUSH);
++ mnt_flags = au_mntflags(sb);
++ udba_none = !!au_opt_test(mnt_flags, UDBA_NONE);
++
++ /* support fstat(2) */
++ if (!d_unhashed(dentry) && !udba_none) {
++ unsigned int sigen = au_sigen(sb);
++ if (au_digen(dentry) == sigen && au_iigen(inode) == sigen)
++ di_read_lock_child(dentry, AuLock_IR);
++ else {
++ AuDebugOn(!IS_ROOT(dentry));
++ err = au_getattr_lock_reval(dentry, sigen);
++ if (unlikely(err))
++ goto out;
++ }
++ } else
++ di_read_lock_child(dentry, AuLock_IR);
++
++ bindex = au_ibstart(inode);
++ h_mnt = au_sbr_mnt(sb, bindex);
++ h_sb = h_mnt->mnt_sb;
++ if (!au_test_fs_bad_iattr(h_sb) && udba_none)
++ goto out_fill; /* success */
++
++ h_dentry = NULL;
++ if (au_dbstart(dentry) == bindex)
++ h_dentry = dget(au_h_dptr(dentry, bindex));
++ else if (au_opt_test(mnt_flags, PLINK) && au_plink_test(inode)) {
++ h_dentry = au_plink_lkup(inode, bindex);
++ if (IS_ERR(h_dentry))
++ goto out_fill; /* pretending success */
++ }
++ /* illegally overlapped or something */
++ if (unlikely(!h_dentry))
++ goto out_fill; /* pretending success */
++
++ positive = !!h_dentry->d_inode;
++ if (positive)
++ err = vfs_getattr(h_mnt, h_dentry, st);
++ dput(h_dentry);
++ if (!err) {
++ if (positive)
++ au_refresh_iattr(inode, st, h_dentry->d_inode->i_nlink);
++ goto out_fill; /* success */
++ }
++ goto out_unlock;
++
++ out_fill:
++ generic_fillattr(inode, st);
++ out_unlock:
++ di_read_unlock(dentry, AuLock_IR);
++ out:
++ si_read_unlock(sb);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static int h_readlink(struct dentry *dentry, int bindex, char __user *buf,
++ int bufsiz)
++{
++ int err;
++ struct super_block *sb;
++ struct dentry *h_dentry;
++
++ err = -EINVAL;
++ h_dentry = au_h_dptr(dentry, bindex);
++ if (unlikely(/* !h_dentry
++ || !h_dentry->d_inode
++ || !h_dentry->d_inode->i_op
++ || */ !h_dentry->d_inode->i_op->readlink))
++ goto out;
++
++ err = security_inode_readlink(h_dentry);
++ if (unlikely(err))
++ goto out;
++
++ sb = dentry->d_sb;
++ if (!au_test_ro(sb, bindex, dentry->d_inode)) {
++ vfsub_touch_atime(au_sbr_mnt(sb, bindex), h_dentry);
++ fsstack_copy_attr_atime(dentry->d_inode, h_dentry->d_inode);
++ }
++ err = h_dentry->d_inode->i_op->readlink(h_dentry, buf, bufsiz);
++
++ out:
++ return err;
++}
++
++static int aufs_readlink(struct dentry *dentry, char __user *buf, int bufsiz)
++{
++ int err;
++
++ aufs_read_lock(dentry, AuLock_IR);
++ err = h_readlink(dentry, au_dbstart(dentry), buf, bufsiz);
++ aufs_read_unlock(dentry, AuLock_IR);
++
++ return err;
++}
++
++static void *aufs_follow_link(struct dentry *dentry, struct nameidata *nd)
++{
++ int err;
++ char *buf;
++ mm_segment_t old_fs;
++
++ err = -ENOMEM;
++ buf = __getname();
++ if (unlikely(!buf))
++ goto out;
++
++ aufs_read_lock(dentry, AuLock_IR);
++ old_fs = get_fs();
++ set_fs(KERNEL_DS);
++ err = h_readlink(dentry, au_dbstart(dentry), (char __user *)buf,
++ PATH_MAX);
++ set_fs(old_fs);
++ aufs_read_unlock(dentry, AuLock_IR);
++
++ if (err >= 0) {
++ buf[err] = 0;
++ /* will be freed by put_link */
++ nd_set_link(nd, buf);
++ return NULL; /* success */
++ }
++ __putname(buf);
++
++ out:
++ path_put(&nd->path);
++ AuTraceErr(err);
++ return ERR_PTR(err);
++}
++
++static void aufs_put_link(struct dentry *dentry __maybe_unused,
++ struct nameidata *nd, void *cookie __maybe_unused)
++{
++ __putname(nd_get_link(nd));
++}
++
++/* ---------------------------------------------------------------------- */
++
++static void aufs_truncate_range(struct inode *inode __maybe_unused,
++ loff_t start __maybe_unused,
++ loff_t end __maybe_unused)
++{
++ AuUnsupport();
++}
++
++/* ---------------------------------------------------------------------- */
++
++struct inode_operations aufs_symlink_iop = {
++ .permission = aufs_permission,
++ .setattr = aufs_setattr,
++ .getattr = aufs_getattr,
++ .readlink = aufs_readlink,
++ .follow_link = aufs_follow_link,
++ .put_link = aufs_put_link
++};
++
++struct inode_operations aufs_dir_iop = {
++ .create = aufs_create,
++ .lookup = aufs_lookup,
++ .link = aufs_link,
++ .unlink = aufs_unlink,
++ .symlink = aufs_symlink,
++ .mkdir = aufs_mkdir,
++ .rmdir = aufs_rmdir,
++ .mknod = aufs_mknod,
++ .rename = aufs_rename,
++
++ .permission = aufs_permission,
++ .setattr = aufs_setattr,
++ .getattr = aufs_getattr
++};
++
++struct inode_operations aufs_iop = {
++ .permission = aufs_permission,
++ .setattr = aufs_setattr,
++ .getattr = aufs_getattr,
++ .truncate_range = aufs_truncate_range
++};
+diff -Nur linux-2.6.31.5.orig/fs/aufs/i_op_del.c linux-2.6.31.5/fs/aufs/i_op_del.c
+--- linux-2.6.31.5.orig/fs/aufs/i_op_del.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/i_op_del.c 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,468 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. Okajima
++ *
++ * This program, aufs is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/*
++ * inode operations (del entry)
++ */
++
++#include "aufs.h"
++
++/*
++ * decide if a new whiteout for @dentry is necessary or not.
++ * when it is necessary, prepare the parent dir for the upper branch whose
++ * branch index is @bcpup for creation. the actual creation of the whiteout will
++ * be done by caller.
++ * return value:
++ * 0: wh is unnecessary
++ * plus: wh is necessary
++ * minus: error
++ */
++int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup)
++{
++ int need_wh, err;
++ aufs_bindex_t bstart;
++ struct super_block *sb;
++
++ sb = dentry->d_sb;
++ bstart = au_dbstart(dentry);
++ if (*bcpup < 0) {
++ *bcpup = bstart;
++ if (au_test_ro(sb, bstart, dentry->d_inode)) {
++ err = AuWbrCopyup(au_sbi(sb), dentry);
++ *bcpup = err;
++ if (unlikely(err < 0))
++ goto out;
++ }
++ } else
++ AuDebugOn(bstart < *bcpup
++ || au_test_ro(sb, *bcpup, dentry->d_inode));
++ AuDbg("bcpup %d, bstart %d\n", *bcpup, bstart);
++
++ if (*bcpup != bstart) {
++ err = au_cpup_dirs(dentry, *bcpup);
++ if (unlikely(err))
++ goto out;
++ need_wh = 1;
++ } else {
++ aufs_bindex_t old_bend, new_bend, bdiropq = -1;
++
++ old_bend = au_dbend(dentry);
++ if (isdir) {
++ bdiropq = au_dbdiropq(dentry);
++ au_set_dbdiropq(dentry, -1);
++ }
++ need_wh = au_lkup_dentry(dentry, bstart + 1, /*type*/0,
++ /*nd*/NULL);
++ err = need_wh;
++ if (isdir)
++ au_set_dbdiropq(dentry, bdiropq);
++ if (unlikely(err < 0))
++ goto out;
++ new_bend = au_dbend(dentry);
++ if (!need_wh && old_bend != new_bend) {
++ au_set_h_dptr(dentry, new_bend, NULL);
++ au_set_dbend(dentry, old_bend);
++ }
++ }
++ AuDbg("need_wh %d\n", need_wh);
++ err = need_wh;
++
++ out:
++ return err;
++}
++
++/*
++ * simple tests for the del-entry operations.
++ * following the checks in vfs, plus the parent-child relationship.
++ */
++int au_may_del(struct dentry *dentry, aufs_bindex_t bindex,
++ struct dentry *h_parent, int isdir)
++{
++ int err;
++ umode_t h_mode;
++ struct dentry *h_dentry, *h_latest;
++ struct inode *h_inode;
++
++ h_dentry = au_h_dptr(dentry, bindex);
++ h_inode = h_dentry->d_inode;
++ if (dentry->d_inode) {
++ err = -ENOENT;
++ if (unlikely(!h_inode || !h_inode->i_nlink))
++ goto out;
++
++ h_mode = h_inode->i_mode;
++ if (!isdir) {
++ err = -EISDIR;
++ if (unlikely(S_ISDIR(h_mode)))
++ goto out;
++ } else if (unlikely(!S_ISDIR(h_mode))) {
++ err = -ENOTDIR;
++ goto out;
++ }
++ } else {
++ /* rename(2) case */
++ err = -EIO;
++ if (unlikely(h_inode))
++ goto out;
++ }
++
++ err = -ENOENT;
++ /* expected parent dir is locked */
++ if (unlikely(h_parent != h_dentry->d_parent))
++ goto out;
++ err = 0;
++
++ /*
++ * rmdir a dir may break the consistency on some filesystem.
++ * let's try heavy test.
++ */
++ err = -EACCES;
++ if (unlikely(au_test_h_perm(h_parent->d_inode, MAY_EXEC | MAY_WRITE)))
++ goto out;
++
++ h_latest = au_sio_lkup_one(&dentry->d_name, h_parent,
++ au_sbr(dentry->d_sb, bindex));
++ err = -EIO;
++ if (IS_ERR(h_latest))
++ goto out;
++ if (h_latest == h_dentry)
++ err = 0;
++ dput(h_latest);
++
++ out:
++ return err;
++}
++
++/*
++ * decide the branch where we operate for @dentry. the branch index will be set
++ * @rbcpup. after diciding it, 'pin' it and store the timestamps of the parent
++ * dir for reverting.
++ * when a new whiteout is necessary, create it.
++ */
++static struct dentry*
++lock_hdir_create_wh(struct dentry *dentry, int isdir, aufs_bindex_t *rbcpup,
++ struct au_dtime *dt, struct au_pin *pin)
++{
++ struct dentry *wh_dentry;
++ struct super_block *sb;
++ struct path h_path;
++ int err, need_wh;
++ unsigned int udba;
++ aufs_bindex_t bcpup;
++
++ need_wh = au_wr_dir_need_wh(dentry, isdir, rbcpup);
++ wh_dentry = ERR_PTR(need_wh);
++ if (unlikely(need_wh < 0))
++ goto out;
++
++ sb = dentry->d_sb;
++ udba = au_opt_udba(sb);
++ bcpup = *rbcpup;
++ err = au_pin(pin, dentry, bcpup, udba,
++ AuPin_DI_LOCKED | AuPin_MNT_WRITE);
++ wh_dentry = ERR_PTR(err);
++ if (unlikely(err))
++ goto out;
++
++ h_path.dentry = au_pinned_h_parent(pin);
++ if (udba != AuOpt_UDBA_NONE
++ && au_dbstart(dentry) == bcpup) {
++ err = au_may_del(dentry, bcpup, h_path.dentry, isdir);
++ wh_dentry = ERR_PTR(err);
++ if (unlikely(err))
++ goto out_unpin;
++ }
++
++ h_path.mnt = au_sbr_mnt(sb, bcpup);
++ au_dtime_store(dt, au_pinned_parent(pin), &h_path);
++ wh_dentry = NULL;
++ if (!need_wh)
++ goto out; /* success, no need to create whiteout */
++
++ wh_dentry = au_wh_create(dentry, bcpup, h_path.dentry);
++ if (!IS_ERR(wh_dentry))
++ goto out; /* success */
++ /* returns with the parent is locked and wh_dentry is dget-ed */
++
++ out_unpin:
++ au_unpin(pin);
++ out:
++ return wh_dentry;
++}
++
++/*
++ * when removing a dir, rename it to a unique temporary whiteout-ed name first
++ * in order to be revertible and save time for removing many child whiteouts
++ * under the dir.
++ * returns 1 when there are too many child whiteout and caller should remove
++ * them asynchronously. returns 0 when the number of children is enough small to
++ * remove now or the branch fs is a remote fs.
++ * otherwise return an error.
++ */
++static int renwh_and_rmdir(struct dentry *dentry, aufs_bindex_t bindex,
++ struct au_nhash *whlist, struct inode *dir)
++{
++ int rmdir_later, err, dirwh;
++ struct dentry *h_dentry;
++ struct super_block *sb;
++
++ sb = dentry->d_sb;
++ SiMustAnyLock(sb);
++ h_dentry = au_h_dptr(dentry, bindex);
++ err = au_whtmp_ren(h_dentry, au_sbr(sb, bindex));
++ if (unlikely(err))
++ goto out;
++
++ /* stop monitoring */
++ au_hin_free(au_hi(dentry->d_inode, bindex));
++
++ if (!au_test_fs_remote(h_dentry->d_sb)) {
++ dirwh = au_sbi(sb)->si_dirwh;
++ rmdir_later = (dirwh <= 1);
++ if (!rmdir_later)
++ rmdir_later = au_nhash_test_longer_wh(whlist, bindex,
++ dirwh);
++ if (rmdir_later)
++ return rmdir_later;
++ }
++
++ err = au_whtmp_rmdir(dir, bindex, h_dentry, whlist);
++ if (unlikely(err)) {
++ AuIOErr("rmdir %.*s, b%d failed, %d. ignored\n",
++ AuDLNPair(h_dentry), bindex, err);
++ err = 0;
++ }
++
++ out:
++ return err;
++}
++
++/*
++ * final procedure for deleting a entry.
++ * maintain dentry and iattr.
++ */
++static void epilog(struct inode *dir, struct dentry *dentry,
++ aufs_bindex_t bindex)
++{
++ struct inode *inode;
++
++ inode = dentry->d_inode;
++ d_drop(dentry);
++ inode->i_ctime = dir->i_ctime;
++
++ if (atomic_read(&dentry->d_count) == 1) {
++ au_set_h_dptr(dentry, au_dbstart(dentry), NULL);
++ au_update_dbstart(dentry);
++ }
++ if (au_ibstart(dir) == bindex)
++ au_cpup_attr_timesizes(dir);
++ dir->i_version++;
++}
++
++/*
++ * when an error happened, remove the created whiteout and revert everything.
++ */
++static int do_revert(int err, struct inode *dir, aufs_bindex_t bwh,
++ struct dentry *wh_dentry, struct dentry *dentry,
++ struct au_dtime *dt)
++{
++ int rerr;
++ struct path h_path = {
++ .dentry = wh_dentry,
++ .mnt = au_sbr_mnt(dir->i_sb, bwh)
++ };
++
++ rerr = au_wh_unlink_dentry(au_h_iptr(dir, bwh), &h_path, dentry);
++ if (!rerr) {
++ au_set_dbwh(dentry, bwh);
++ au_dtime_revert(dt);
++ return 0;
++ }
++
++ AuIOErr("%.*s reverting whiteout failed(%d, %d)\n",
++ AuDLNPair(dentry), err, rerr);
++ return -EIO;
++}
++
++/* ---------------------------------------------------------------------- */
++
++int aufs_unlink(struct inode *dir, struct dentry *dentry)
++{
++ int err;
++ aufs_bindex_t bwh, bindex, bstart;
++ struct au_dtime dt;
++ struct au_pin pin;
++ struct path h_path;
++ struct inode *inode, *h_dir;
++ struct dentry *parent, *wh_dentry;
++
++ IMustLock(dir);
++ inode = dentry->d_inode;
++ if (unlikely(!inode))
++ return -ENOENT; /* possible? */
++ IMustLock(inode);
++
++ aufs_read_lock(dentry, AuLock_DW);
++ parent = dentry->d_parent; /* dir inode is locked */
++ di_write_lock_parent(parent);
++
++ bstart = au_dbstart(dentry);
++ bwh = au_dbwh(dentry);
++ bindex = -1;
++ wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/0, &bindex, &dt, &pin);
++ err = PTR_ERR(wh_dentry);
++ if (IS_ERR(wh_dentry))
++ goto out;
++
++ h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart);
++ h_path.dentry = au_h_dptr(dentry, bstart);
++ dget(h_path.dentry);
++ if (bindex == bstart) {
++ h_dir = au_pinned_h_dir(&pin);
++ err = vfsub_unlink(h_dir, &h_path, /*force*/0);
++ } else {
++ /* dir inode is locked */
++ h_dir = wh_dentry->d_parent->d_inode;
++ IMustLock(h_dir);
++ err = 0;
++ }
++
++ if (!err) {
++ drop_nlink(inode);
++ epilog(dir, dentry, bindex);
++
++ /* update target timestamps */
++ if (bindex == bstart) {
++ vfsub_update_h_iattr(&h_path, /*did*/NULL); /*ignore*/
++ inode->i_ctime = h_path.dentry->d_inode->i_ctime;
++ } else
++ /* todo: this timestamp may be reverted later */
++ inode->i_ctime = h_dir->i_ctime;
++ goto out_unlock; /* success */
++ }
++
++ /* revert */
++ if (wh_dentry) {
++ int rerr;
++
++ rerr = do_revert(err, dir, bwh, wh_dentry, dentry, &dt);
++ if (rerr)
++ err = rerr;
++ }
++
++ out_unlock:
++ au_unpin(&pin);
++ dput(wh_dentry);
++ dput(h_path.dentry);
++ out:
++ di_write_unlock(parent);
++ aufs_read_unlock(dentry, AuLock_DW);
++ return err;
++}
++
++int aufs_rmdir(struct inode *dir, struct dentry *dentry)
++{
++ int err, rmdir_later;
++ aufs_bindex_t bwh, bindex, bstart;
++ struct au_dtime dt;
++ struct au_pin pin;
++ struct inode *inode;
++ struct dentry *parent, *wh_dentry, *h_dentry;
++ struct au_whtmp_rmdir *args;
++
++ IMustLock(dir);
++ inode = dentry->d_inode;
++ err = -ENOENT; /* possible? */
++ if (unlikely(!inode))
++ goto out;
++ IMustLock(inode);
++
++ aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH);
++ err = -ENOMEM;
++ args = au_whtmp_rmdir_alloc(dir->i_sb, GFP_NOFS);
++ if (unlikely(!args))
++ goto out_unlock;
++
++ parent = dentry->d_parent; /* dir inode is locked */
++ di_write_lock_parent(parent);
++ err = au_test_empty(dentry, &args->whlist);
++ if (unlikely(err))
++ goto out_args;
++
++ bstart = au_dbstart(dentry);
++ bwh = au_dbwh(dentry);
++ bindex = -1;
++ wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/1, &bindex, &dt, &pin);
++ err = PTR_ERR(wh_dentry);
++ if (IS_ERR(wh_dentry))
++ goto out_args;
++
++ h_dentry = au_h_dptr(dentry, bstart);
++ dget(h_dentry);
++ rmdir_later = 0;
++ if (bindex == bstart) {
++ err = renwh_and_rmdir(dentry, bstart, &args->whlist, dir);
++ if (err > 0) {
++ rmdir_later = err;
++ err = 0;
++ }
++ } else {
++ /* stop monitoring */
++ au_hin_free(au_hi(inode, bstart));
++
++ /* dir inode is locked */
++ IMustLock(wh_dentry->d_parent->d_inode);
++ err = 0;
++ }
++
++ if (!err) {
++ clear_nlink(inode);
++ au_set_dbdiropq(dentry, -1);
++ epilog(dir, dentry, bindex);
++
++ if (rmdir_later) {
++ au_whtmp_kick_rmdir(dir, bstart, h_dentry, args);
++ args = NULL;
++ }
++
++ goto out_unpin; /* success */
++ }
++
++ /* revert */
++ AuLabel(revert);
++ if (wh_dentry) {
++ int rerr;
++
++ rerr = do_revert(err, dir, bwh, wh_dentry, dentry, &dt);
++ if (rerr)
++ err = rerr;
++ }
++
++ out_unpin:
++ au_unpin(&pin);
++ dput(wh_dentry);
++ dput(h_dentry);
++ out_args:
++ di_write_unlock(parent);
++ if (args)
++ au_whtmp_rmdir_free(args);
++ out_unlock:
++ aufs_read_unlock(dentry, AuLock_DW);
++ out:
++ return err;
++}
+diff -Nur linux-2.6.31.5.orig/fs/aufs/i_op_ren.c linux-2.6.31.5/fs/aufs/i_op_ren.c
+--- linux-2.6.31.5.orig/fs/aufs/i_op_ren.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/i_op_ren.c 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,948 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. Okajima
++ *
++ * This program, aufs is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/*
++ * inode operation (rename entry)
++ * todo: this is crazy monster
++ */
++
++#include "aufs.h"
++
++enum { AuSRC, AuDST, AuSrcDst };
++enum { AuPARENT, AuCHILD, AuParentChild };
++
++#define AuRen_ISDIR 1
++#define AuRen_ISSAMEDIR (1 << 1)
++#define AuRen_WHSRC (1 << 2)
++#define AuRen_WHDST (1 << 3)
++#define AuRen_MNT_WRITE (1 << 4)
++#define AuRen_DT_DSTDIR (1 << 5)
++#define AuRen_DIROPQ (1 << 6)
++#define AuRen_CPUP (1 << 7)
++#define au_ftest_ren(flags, name) ((flags) & AuRen_##name)
++#define au_fset_ren(flags, name) { (flags) |= AuRen_##name; }
++#define au_fclr_ren(flags, name) { (flags) &= ~AuRen_##name; }
++
++struct au_ren_args {
++ struct {
++ struct dentry *dentry, *h_dentry, *parent, *h_parent,
++ *wh_dentry;
++ struct inode *dir, *inode;
++ struct au_hinode *hdir;
++ struct au_dtime dt[AuParentChild];
++ aufs_bindex_t bstart;
++ } sd[AuSrcDst];
++
++#define src_dentry sd[AuSRC].dentry
++#define src_dir sd[AuSRC].dir
++#define src_inode sd[AuSRC].inode
++#define src_h_dentry sd[AuSRC].h_dentry
++#define src_parent sd[AuSRC].parent
++#define src_h_parent sd[AuSRC].h_parent
++#define src_wh_dentry sd[AuSRC].wh_dentry
++#define src_hdir sd[AuSRC].hdir
++#define src_h_dir sd[AuSRC].hdir->hi_inode
++#define src_dt sd[AuSRC].dt
++#define src_bstart sd[AuSRC].bstart
++
++#define dst_dentry sd[AuDST].dentry
++#define dst_dir sd[AuDST].dir
++#define dst_inode sd[AuDST].inode
++#define dst_h_dentry sd[AuDST].h_dentry
++#define dst_parent sd[AuDST].parent
++#define dst_h_parent sd[AuDST].h_parent
++#define dst_wh_dentry sd[AuDST].wh_dentry
++#define dst_hdir sd[AuDST].hdir
++#define dst_h_dir sd[AuDST].hdir->hi_inode
++#define dst_dt sd[AuDST].dt
++#define dst_bstart sd[AuDST].bstart
++
++ struct dentry *h_trap;
++ struct au_branch *br;
++ struct au_hinode *src_hinode;
++ struct path h_path;
++ struct au_nhash whlist;
++ aufs_bindex_t btgt;
++
++ unsigned int flags;
++
++ struct au_whtmp_rmdir *thargs;
++ struct dentry *h_dst;
++};
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * functions for reverting.
++ * when an error happened in a single rename systemcall, we should revert
++ * everything as if nothing happend.
++ * we don't need to revert the copied-up/down the parent dir since they are
++ * harmless.
++ */
++
++#define RevertFailure(fmt, args...) do { \
++ AuIOErr("revert failure: " fmt " (%d, %d)\n", \
++ ##args, err, rerr); \
++ err = -EIO; \
++} while (0)
++
++static void au_ren_rev_diropq(int err, struct au_ren_args *a)
++{
++ int rerr;
++
++ au_hin_imtx_lock_nested(a->src_hinode, AuLsc_I_CHILD);
++ rerr = au_diropq_remove(a->src_dentry, a->btgt);
++ au_hin_imtx_unlock(a->src_hinode);
++ if (rerr)
++ RevertFailure("remove diropq %.*s", AuDLNPair(a->src_dentry));
++}
++
++
++static void au_ren_rev_rename(int err, struct au_ren_args *a)
++{
++ int rerr;
++
++ a->h_path.dentry = au_lkup_one(&a->src_dentry->d_name, a->src_h_parent,
++ a->br, /*nd*/NULL);
++ rerr = PTR_ERR(a->h_path.dentry);
++ if (IS_ERR(a->h_path.dentry)) {
++ RevertFailure("au_lkup_one %.*s", AuDLNPair(a->src_dentry));
++ return;
++ }
++
++ rerr = vfsub_rename(a->dst_h_dir,
++ au_h_dptr(a->src_dentry, a->btgt),
++ a->src_h_dir, &a->h_path);
++ d_drop(a->h_path.dentry);
++ dput(a->h_path.dentry);
++ /* au_set_h_dptr(a->src_dentry, a->btgt, NULL); */
++ if (rerr)
++ RevertFailure("rename %.*s", AuDLNPair(a->src_dentry));
++}
++
++static void au_ren_rev_cpup(int err, struct au_ren_args *a)
++{
++ int rerr;
++
++ a->h_path.dentry = a->dst_h_dentry;
++ rerr = vfsub_unlink(a->dst_h_dir, &a->h_path, /*force*/0);
++ au_set_h_dptr(a->src_dentry, a->btgt, NULL);
++ au_set_dbstart(a->src_dentry, a->src_bstart);
++ if (rerr)
++ RevertFailure("unlink %.*s", AuDLNPair(a->dst_h_dentry));
++}
++
++
++static void au_ren_rev_whtmp(int err, struct au_ren_args *a)
++{
++ int rerr;
++
++ a->h_path.dentry = au_lkup_one(&a->dst_dentry->d_name, a->dst_h_parent,
++ a->br, /*nd*/NULL);
++ rerr = PTR_ERR(a->h_path.dentry);
++ if (IS_ERR(a->h_path.dentry)) {
++ RevertFailure("lookup %.*s", AuDLNPair(a->dst_dentry));
++ return;
++ }
++ if (a->h_path.dentry->d_inode) {
++ d_drop(a->h_path.dentry);
++ dput(a->h_path.dentry);
++ return;
++ }
++
++ rerr = vfsub_rename(a->dst_h_dir, a->h_dst, a->dst_h_dir, &a->h_path);
++ d_drop(a->h_path.dentry);
++ dput(a->h_path.dentry);
++ if (!rerr) {
++ au_set_h_dptr(a->dst_dentry, a->btgt, NULL);
++ au_set_h_dptr(a->dst_dentry, a->btgt, dget(a->h_dst));
++ } else
++ RevertFailure("rename %.*s", AuDLNPair(a->h_dst));
++}
++
++static void au_ren_rev_whsrc(int err, struct au_ren_args *a)
++{
++ int rerr;
++
++ a->h_path.dentry = a->src_wh_dentry;
++ rerr = au_wh_unlink_dentry(a->src_h_dir, &a->h_path, a->src_dentry);
++ if (rerr)
++ RevertFailure("unlink %.*s", AuDLNPair(a->src_wh_dentry));
++}
++
++static void au_ren_rev_drop(struct au_ren_args *a)
++{
++ struct dentry *d, *h_d;
++ int i;
++ aufs_bindex_t bend, bindex;
++
++ for (i = 0; i < AuSrcDst; i++) {
++ d = a->sd[i].dentry;
++ d_drop(d);
++ bend = au_dbend(d);
++ for (bindex = au_dbstart(d); bindex <= bend; bindex++) {
++ h_d = au_h_dptr(d, bindex);
++ if (h_d)
++ d_drop(h_d);
++ }
++ }
++
++ au_update_dbstart(a->dst_dentry);
++ if (a->thargs)
++ d_drop(a->h_dst);
++}
++#undef RevertFailure
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * when we have to copyup the renaming entry, do it with the rename-target name
++ * in order to minimize the cost (the later actual rename is unnecessary).
++ * otherwise rename it on the target branch.
++ */
++static int au_ren_or_cpup(struct au_ren_args *a)
++{
++ int err;
++ struct dentry *d;
++
++ d = a->src_dentry;
++ if (au_dbstart(d) == a->btgt) {
++ a->h_path.dentry = a->dst_h_dentry;
++ if (au_ftest_ren(a->flags, DIROPQ)
++ && au_dbdiropq(d) == a->btgt)
++ au_fclr_ren(a->flags, DIROPQ);
++ AuDebugOn(au_dbstart(d) != a->btgt);
++ err = vfsub_rename(a->src_h_dir, au_h_dptr(d, a->btgt),
++ a->dst_h_dir, &a->h_path);
++ } else {
++ struct mutex *h_mtx = &a->src_h_dentry->d_inode->i_mutex;
++
++ au_fset_ren(a->flags, CPUP);
++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD);
++ au_set_dbstart(d, a->btgt);
++ au_set_h_dptr(d, a->btgt, dget(a->dst_h_dentry));
++ err = au_sio_cpup_single(d, a->btgt, a->src_bstart, -1,
++ !AuCpup_DTIME, a->dst_parent);
++ if (unlikely(err)) {
++ au_set_h_dptr(d, a->btgt, NULL);
++ au_set_dbstart(d, a->src_bstart);
++ }
++ mutex_unlock(h_mtx);
++ }
++
++ return err;
++}
++
++/* cf. aufs_rmdir() */
++static int au_ren_del_whtmp(struct au_ren_args *a)
++{
++ int err;
++ struct inode *dir;
++
++ dir = a->dst_dir;
++ SiMustAnyLock(dir->i_sb);
++ if (!au_nhash_test_longer_wh(&a->whlist, a->btgt,
++ au_sbi(dir->i_sb)->si_dirwh)
++ || au_test_fs_remote(a->h_dst->d_sb)) {
++ err = au_whtmp_rmdir(dir, a->btgt, a->h_dst, &a->whlist);
++ if (unlikely(err))
++ AuWarn("failed removing whtmp dir %.*s (%d), "
++ "ignored.\n", AuDLNPair(a->h_dst), err);
++ } else {
++ au_nhash_wh_free(&a->thargs->whlist);
++ a->thargs->whlist = a->whlist;
++ a->whlist.nh_num = 0;
++ au_whtmp_kick_rmdir(dir, a->btgt, a->h_dst, a->thargs);
++ dput(a->h_dst);
++ a->thargs = NULL;
++ }
++
++ return 0;
++}
++
++/* make it 'opaque' dir. */
++static int au_ren_diropq(struct au_ren_args *a)
++{
++ int err;
++ struct dentry *diropq;
++
++ err = 0;
++ a->src_hinode = au_hi(a->src_inode, a->btgt);
++ au_hin_imtx_lock_nested(a->src_hinode, AuLsc_I_CHILD);
++ diropq = au_diropq_create(a->src_dentry, a->btgt);
++ au_hin_imtx_unlock(a->src_hinode);
++ if (IS_ERR(diropq))
++ err = PTR_ERR(diropq);
++ dput(diropq);
++
++ return err;
++}
++
++static int do_rename(struct au_ren_args *a)
++{
++ int err;
++ struct dentry *d, *h_d;
++
++ /* prepare workqueue args for asynchronous rmdir */
++ h_d = a->dst_h_dentry;
++ if (au_ftest_ren(a->flags, ISDIR) && h_d->d_inode) {
++ err = -ENOMEM;
++ a->thargs = au_whtmp_rmdir_alloc(a->src_dentry->d_sb, GFP_NOFS);
++ if (unlikely(!a->thargs))
++ goto out;
++ a->h_dst = dget(h_d);
++ }
++
++ /* create whiteout for src_dentry */
++ if (au_ftest_ren(a->flags, WHSRC)) {
++ a->src_wh_dentry
++ = au_wh_create(a->src_dentry, a->btgt, a->src_h_parent);
++ err = PTR_ERR(a->src_wh_dentry);
++ if (IS_ERR(a->src_wh_dentry))
++ goto out_thargs;
++ }
++
++ /* lookup whiteout for dentry */
++ if (au_ftest_ren(a->flags, WHDST)) {
++ h_d = au_wh_lkup(a->dst_h_parent, &a->dst_dentry->d_name,
++ a->br);
++ err = PTR_ERR(h_d);
++ if (IS_ERR(h_d))
++ goto out_whsrc;
++ if (!h_d->d_inode)
++ dput(h_d);
++ else
++ a->dst_wh_dentry = h_d;
++ }
++
++ /* rename dentry to tmpwh */
++ if (a->thargs) {
++ err = au_whtmp_ren(a->dst_h_dentry, a->br);
++ if (unlikely(err))
++ goto out_whdst;
++
++ d = a->dst_dentry;
++ au_set_h_dptr(d, a->btgt, NULL);
++ err = au_lkup_neg(d, a->btgt);
++ if (unlikely(err))
++ goto out_whtmp;
++ a->dst_h_dentry = au_h_dptr(d, a->btgt);
++ }
++
++ /* cpup src */
++ if (a->dst_h_dentry->d_inode && a->src_bstart != a->btgt) {
++ struct mutex *h_mtx = &a->src_h_dentry->d_inode->i_mutex;
++
++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD);
++ err = au_sio_cpup_simple(a->src_dentry, a->btgt, -1,
++ !AuCpup_DTIME);
++ mutex_unlock(h_mtx);
++ if (unlikely(err))
++ goto out_whtmp;
++ }
++
++ /* rename by vfs_rename or cpup */
++ d = a->dst_dentry;
++ if (au_ftest_ren(a->flags, ISDIR)
++ && (a->dst_wh_dentry
++ || au_dbdiropq(d) == a->btgt
++ /* hide the lower to keep xino */
++ || a->btgt < au_dbend(d)
++ || au_opt_test(au_mntflags(d->d_sb), ALWAYS_DIROPQ)))
++ au_fset_ren(a->flags, DIROPQ);
++ err = au_ren_or_cpup(a);
++ if (unlikely(err))
++ /* leave the copied-up one */
++ goto out_whtmp;
++
++ /* make dir opaque */
++ if (au_ftest_ren(a->flags, DIROPQ)) {
++ err = au_ren_diropq(a);
++ if (unlikely(err))
++ goto out_rename;
++ }
++
++ /* update target timestamps */
++ AuDebugOn(au_dbstart(a->src_dentry) != a->btgt);
++ a->h_path.dentry = au_h_dptr(a->src_dentry, a->btgt);
++ vfsub_update_h_iattr(&a->h_path, /*did*/NULL); /*ignore*/
++ a->src_inode->i_ctime = a->h_path.dentry->d_inode->i_ctime;
++
++ /* remove whiteout for dentry */
++ if (a->dst_wh_dentry) {
++ a->h_path.dentry = a->dst_wh_dentry;
++ err = au_wh_unlink_dentry(a->dst_h_dir, &a->h_path,
++ a->dst_dentry);
++ if (unlikely(err))
++ goto out_diropq;
++ }
++
++ /* remove whtmp */
++ if (a->thargs)
++ au_ren_del_whtmp(a); /* ignore this error */
++
++ err = 0;
++ goto out_success;
++
++ out_diropq:
++ if (au_ftest_ren(a->flags, DIROPQ))
++ au_ren_rev_diropq(err, a);
++ out_rename:
++ if (!au_ftest_ren(a->flags, CPUP))
++ au_ren_rev_rename(err, a);
++ else
++ au_ren_rev_cpup(err, a);
++ out_whtmp:
++ if (a->thargs)
++ au_ren_rev_whtmp(err, a);
++ out_whdst:
++ dput(a->dst_wh_dentry);
++ a->dst_wh_dentry = NULL;
++ out_whsrc:
++ if (a->src_wh_dentry)
++ au_ren_rev_whsrc(err, a);
++ au_ren_rev_drop(a);
++ out_success:
++ dput(a->src_wh_dentry);
++ dput(a->dst_wh_dentry);
++ out_thargs:
++ if (a->thargs) {
++ dput(a->h_dst);
++ au_whtmp_rmdir_free(a->thargs);
++ a->thargs = NULL;
++ }
++ out:
++ 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, struct au_nhash *whlist)
++{
++ return au_test_empty(dentry, whlist);
++}
++
++/*
++ * test if @dentry dir can be rename source or not.
++ * if it can, return 0 and @children is filled.
++ * success means,
++ * - 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;
++
++ bstart = au_dbstart(dentry);
++ if (bstart != btgt) {
++ struct au_nhash whlist;
++
++ SiMustAnyLock(dentry->d_sb);
++ err = au_nhash_alloc(&whlist, au_sbi(dentry->d_sb)->si_rdhash,
++ GFP_NOFS);
++ if (unlikely(err))
++ goto out;
++ err = au_test_empty(dentry, &whlist);
++ au_nhash_wh_free(&whlist);
++ goto out;
++ }
++
++ if (bstart == au_dbtaildir(dentry))
++ return 0; /* success */
++
++ err = au_test_empty_lower(dentry);
++
++ out:
++ if (err == -ENOTEMPTY) {
++ AuWarn1("renaming dir who has child(ren) on multiple branches,"
++ " is not supported\n");
++ err = -EXDEV;
++ }
++ return err;
++}
++
++/* side effect: sets whlist and h_dentry */
++static int au_ren_may_dir(struct au_ren_args *a)
++{
++ int err;
++ struct dentry *d;
++
++ d = a->dst_dentry;
++ SiMustAnyLock(d->d_sb);
++ err = au_nhash_alloc(&a->whlist, au_sbi(d->d_sb)->si_rdhash, GFP_NOFS);
++ if (unlikely(err))
++ goto out;
++
++ err = 0;
++ if (au_ftest_ren(a->flags, ISDIR) && a->dst_inode) {
++ au_set_dbstart(d, a->dst_bstart);
++ err = may_rename_dstdir(d, &a->whlist);
++ au_set_dbstart(d, a->btgt);
++ }
++ a->dst_h_dentry = au_h_dptr(d, au_dbstart(d));
++ if (unlikely(err))
++ goto out;
++
++ d = a->src_dentry;
++ a->src_h_dentry = au_h_dptr(d, au_dbstart(d));
++ if (au_ftest_ren(a->flags, ISDIR)) {
++ err = may_rename_srcdir(d, a->btgt);
++ if (unlikely(err)) {
++ au_nhash_wh_free(&a->whlist);
++ a->whlist.nh_num = 0;
++ }
++ }
++ out:
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * simple tests for rename.
++ * following the checks in vfs, plus the parent-child relationship.
++ */
++static int au_may_ren(struct au_ren_args *a)
++{
++ int err, isdir;
++ struct inode *h_inode;
++
++ if (a->src_bstart == a->btgt) {
++ err = au_may_del(a->src_dentry, a->btgt, a->src_h_parent,
++ au_ftest_ren(a->flags, ISDIR));
++ if (unlikely(err))
++ goto out;
++ err = -EINVAL;
++ if (unlikely(a->src_h_dentry == a->h_trap))
++ goto out;
++ }
++
++ err = 0;
++ if (a->dst_bstart != a->btgt)
++ goto out;
++
++ err = -EIO;
++ h_inode = a->dst_h_dentry->d_inode;
++ isdir = !!au_ftest_ren(a->flags, ISDIR);
++ if (!a->dst_dentry->d_inode) {
++ if (unlikely(h_inode))
++ goto out;
++ err = au_may_add(a->dst_dentry, a->btgt, a->dst_h_parent,
++ isdir);
++ } else {
++ if (unlikely(!h_inode || !h_inode->i_nlink))
++ goto out;
++ err = au_may_del(a->dst_dentry, a->btgt, a->dst_h_parent,
++ isdir);
++ if (unlikely(err))
++ goto out;
++ err = -ENOTEMPTY;
++ if (unlikely(a->dst_h_dentry == a->h_trap))
++ goto out;
++ err = 0;
++ }
++
++ out:
++ if (unlikely(err == -ENOENT || err == -EEXIST))
++ err = -EIO;
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * locking order
++ * (VFS)
++ * - src_dir and dir by lock_rename()
++ * - inode if exitsts
++ * (aufs)
++ * - lock all
++ * + src_dentry and dentry by aufs_read_and_write_lock2() which calls,
++ * + si_read_lock
++ * + di_write_lock2_child()
++ * + di_write_lock_child()
++ * + ii_write_lock_child()
++ * + di_write_lock_child2()
++ * + ii_write_lock_child2()
++ * + src_parent and parent
++ * + di_write_lock_parent()
++ * + ii_write_lock_parent()
++ * + di_write_lock_parent2()
++ * + ii_write_lock_parent2()
++ * + lower src_dir and dir by vfsub_lock_rename()
++ * + verify the every relationships between child and parent. if any
++ * of them failed, unlock all and return -EBUSY.
++ */
++static void au_ren_unlock(struct au_ren_args *a)
++{
++ struct super_block *sb;
++
++ sb = a->dst_dentry->d_sb;
++ if (au_ftest_ren(a->flags, MNT_WRITE))
++ mnt_drop_write(a->br->br_mnt);
++ vfsub_unlock_rename(a->src_h_parent, a->src_hdir,
++ a->dst_h_parent, a->dst_hdir);
++}
++
++static int au_ren_lock(struct au_ren_args *a)
++{
++ int err;
++ unsigned int udba;
++
++ err = 0;
++ a->src_h_parent = au_h_dptr(a->src_parent, a->btgt);
++ a->src_hdir = au_hi(a->src_dir, a->btgt);
++ a->dst_h_parent = au_h_dptr(a->dst_parent, a->btgt);
++ a->dst_hdir = au_hi(a->dst_dir, a->btgt);
++ a->h_trap = vfsub_lock_rename(a->src_h_parent, a->src_hdir,
++ a->dst_h_parent, a->dst_hdir);
++ udba = au_opt_udba(a->src_dentry->d_sb);
++ if (unlikely(a->src_hdir->hi_inode != a->src_h_parent->d_inode
++ || a->dst_hdir->hi_inode != a->dst_h_parent->d_inode))
++ err = au_busy_or_stale();
++ if (!err && au_dbstart(a->src_dentry) == a->btgt)
++ err = au_h_verify(a->src_h_dentry, udba,
++ a->src_h_parent->d_inode, a->src_h_parent,
++ a->br);
++ if (!err && au_dbstart(a->dst_dentry) == a->btgt)
++ err = au_h_verify(a->dst_h_dentry, udba,
++ a->dst_h_parent->d_inode, a->dst_h_parent,
++ a->br);
++ if (!err) {
++ err = mnt_want_write(a->br->br_mnt);
++ if (unlikely(err))
++ goto out_unlock;
++ au_fset_ren(a->flags, MNT_WRITE);
++ goto out; /* success */
++ }
++
++ err = au_busy_or_stale();
++
++ out_unlock:
++ au_ren_unlock(a);
++ out:
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static void au_ren_refresh_dir(struct au_ren_args *a)
++{
++ struct inode *dir;
++
++ dir = a->dst_dir;
++ dir->i_version++;
++ if (au_ftest_ren(a->flags, ISDIR)) {
++ /* is this updating defined in POSIX? */
++ au_cpup_attr_timesizes(a->src_inode);
++ au_cpup_attr_nlink(dir, /*force*/1);
++ if (a->dst_inode) {
++ clear_nlink(a->dst_inode);
++ au_cpup_attr_timesizes(a->dst_inode);
++ }
++ }
++ if (au_ibstart(dir) == a->btgt)
++ au_cpup_attr_timesizes(dir);
++
++ if (au_ftest_ren(a->flags, ISSAMEDIR))
++ return;
++
++ dir = a->src_dir;
++ dir->i_version++;
++ if (au_ftest_ren(a->flags, ISDIR))
++ au_cpup_attr_nlink(dir, /*force*/1);
++ if (au_ibstart(dir) == a->btgt)
++ au_cpup_attr_timesizes(dir);
++}
++
++static void au_ren_refresh(struct au_ren_args *a)
++{
++ aufs_bindex_t bend, bindex;
++ struct dentry *d, *h_d;
++ struct inode *i, *h_i;
++ struct super_block *sb;
++
++ d = a->src_dentry;
++ au_set_dbwh(d, -1);
++ bend = au_dbend(d);
++ for (bindex = a->btgt + 1; bindex <= bend; bindex++) {
++ h_d = au_h_dptr(d, bindex);
++ if (h_d)
++ au_set_h_dptr(d, bindex, NULL);
++ }
++ au_set_dbend(d, a->btgt);
++
++ sb = d->d_sb;
++ i = a->src_inode;
++ if (au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(i))
++ return; /* success */
++
++ bend = au_ibend(i);
++ for (bindex = a->btgt + 1; bindex <= bend; bindex++) {
++ h_i = au_h_iptr(i, bindex);
++ if (h_i) {
++ au_xino_write(sb, bindex, h_i->i_ino, /*ino*/0);
++ /* ignore this error */
++ au_set_h_iptr(i, bindex, NULL, 0);
++ }
++ }
++ au_set_ibend(i, a->btgt);
++}
++
++/* ---------------------------------------------------------------------- */
++
++/* mainly for link(2) and rename(2) */
++int au_wbr(struct dentry *dentry, aufs_bindex_t btgt)
++{
++ aufs_bindex_t bdiropq, bwh;
++ struct dentry *parent;
++ struct au_branch *br;
++
++ parent = dentry->d_parent;
++ IMustLock(parent->d_inode); /* dir is locked */
++
++ bdiropq = au_dbdiropq(parent);
++ bwh = au_dbwh(dentry);
++ br = au_sbr(dentry->d_sb, btgt);
++ if (au_br_rdonly(br)
++ || (0 <= bdiropq && bdiropq < btgt)
++ || (0 <= bwh && bwh < btgt))
++ btgt = -1;
++
++ AuDbg("btgt %d\n", btgt);
++ return btgt;
++}
++
++/* sets src_bstart, dst_bstart and btgt */
++static int au_ren_wbr(struct au_ren_args *a)
++{
++ int err;
++ struct au_wr_dir_args wr_dir_args = {
++ /* .force_btgt = -1, */
++ .flags = AuWrDir_ADD_ENTRY
++ };
++
++ a->src_bstart = au_dbstart(a->src_dentry);
++ a->dst_bstart = au_dbstart(a->dst_dentry);
++ if (au_ftest_ren(a->flags, ISDIR))
++ au_fset_wrdir(wr_dir_args.flags, ISDIR);
++ wr_dir_args.force_btgt = a->src_bstart;
++ if (a->dst_inode && a->dst_bstart < a->src_bstart)
++ wr_dir_args.force_btgt = a->dst_bstart;
++ wr_dir_args.force_btgt = au_wbr(a->dst_dentry, wr_dir_args.force_btgt);
++ err = au_wr_dir(a->dst_dentry, a->src_dentry, &wr_dir_args);
++ a->btgt = err;
++
++ return err;
++}
++
++static void au_ren_dt(struct au_ren_args *a)
++{
++ a->h_path.dentry = a->src_h_parent;
++ au_dtime_store(a->src_dt + AuPARENT, a->src_parent, &a->h_path);
++ if (!au_ftest_ren(a->flags, ISSAMEDIR)) {
++ a->h_path.dentry = a->dst_h_parent;
++ au_dtime_store(a->dst_dt + AuPARENT, a->dst_parent, &a->h_path);
++ }
++
++ au_fclr_ren(a->flags, DT_DSTDIR);
++ if (!au_ftest_ren(a->flags, ISDIR))
++ return;
++
++ a->h_path.dentry = a->src_h_dentry;
++ au_dtime_store(a->src_dt + AuCHILD, a->src_dentry, &a->h_path);
++ if (a->dst_h_dentry->d_inode) {
++ au_fset_ren(a->flags, DT_DSTDIR);
++ a->h_path.dentry = a->dst_h_dentry;
++ au_dtime_store(a->dst_dt + AuCHILD, a->dst_dentry, &a->h_path);
++ }
++}
++
++static void au_ren_rev_dt(int err, struct au_ren_args *a)
++{
++ struct dentry *h_d;
++ struct mutex *h_mtx;
++
++ au_dtime_revert(a->src_dt + AuPARENT);
++ if (!au_ftest_ren(a->flags, ISSAMEDIR))
++ au_dtime_revert(a->dst_dt + AuPARENT);
++
++ if (au_ftest_ren(a->flags, ISDIR) && err != -EIO) {
++ h_d = a->src_dt[AuCHILD].dt_h_path.dentry;
++ h_mtx = &h_d->d_inode->i_mutex;
++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD);
++ au_dtime_revert(a->src_dt + AuCHILD);
++ mutex_unlock(h_mtx);
++
++ if (au_ftest_ren(a->flags, DT_DSTDIR)) {
++ h_d = a->dst_dt[AuCHILD].dt_h_path.dentry;
++ h_mtx = &h_d->d_inode->i_mutex;
++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD);
++ au_dtime_revert(a->dst_dt + AuCHILD);
++ mutex_unlock(h_mtx);
++ }
++ }
++}
++
++/* ---------------------------------------------------------------------- */
++
++int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry,
++ struct inode *_dst_dir, struct dentry *_dst_dentry)
++{
++ int err;
++ /* reduce stack space */
++ struct au_ren_args *a;
++
++ IMustLock(_src_dir);
++ IMustLock(_dst_dir);
++
++ err = -ENOMEM;
++ BUILD_BUG_ON(sizeof(*a) > PAGE_SIZE);
++ a = kzalloc(sizeof(*a), GFP_NOFS);
++ if (unlikely(!a))
++ goto out;
++
++ a->src_dir = _src_dir;
++ a->src_dentry = _src_dentry;
++ a->src_inode = a->src_dentry->d_inode;
++ a->src_parent = a->src_dentry->d_parent; /* dir inode is locked */
++ a->dst_dir = _dst_dir;
++ a->dst_dentry = _dst_dentry;
++ a->dst_inode = a->dst_dentry->d_inode;
++ a->dst_parent = a->dst_dentry->d_parent; /* dir inode is locked */
++ if (a->dst_inode) {
++ IMustLock(a->dst_inode);
++ au_igrab(a->dst_inode);
++ }
++
++ err = -ENOTDIR;
++ if (S_ISDIR(a->src_inode->i_mode)) {
++ au_fset_ren(a->flags, ISDIR);
++ if (unlikely(a->dst_inode && !S_ISDIR(a->dst_inode->i_mode)))
++ goto out_free;
++ aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry,
++ AuLock_DIR | AuLock_FLUSH);
++ } else
++ aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry,
++ AuLock_FLUSH);
++
++ au_fset_ren(a->flags, ISSAMEDIR); /* temporary */
++ di_write_lock_parent(a->dst_parent);
++
++ /* which branch we process */
++ err = au_ren_wbr(a);
++ if (unlikely(err < 0))
++ goto out_unlock;
++ a->br = au_sbr(a->dst_dentry->d_sb, a->btgt);
++ a->h_path.mnt = a->br->br_mnt;
++
++ /* are they available to be renamed */
++ err = au_ren_may_dir(a);
++ if (unlikely(err))
++ goto out_unlock;
++
++ /* prepare the writable parent dir on the same branch */
++ if (a->dst_bstart == a->btgt) {
++ au_fset_ren(a->flags, WHDST);
++ } else {
++ err = au_cpup_dirs(a->dst_dentry, a->btgt);
++ if (unlikely(err))
++ goto out_children;
++ }
++
++ if (a->src_dir != a->dst_dir) {
++ /*
++ * this temporary unlock is safe,
++ * because both dir->i_mutex are locked.
++ */
++ di_write_unlock(a->dst_parent);
++ di_write_lock_parent(a->src_parent);
++ err = au_wr_dir_need_wh(a->src_dentry,
++ au_ftest_ren(a->flags, ISDIR),
++ &a->btgt);
++ di_write_unlock(a->src_parent);
++ di_write_lock2_parent(a->src_parent, a->dst_parent, /*isdir*/1);
++ au_fclr_ren(a->flags, ISSAMEDIR);
++ } else
++ err = au_wr_dir_need_wh(a->src_dentry,
++ au_ftest_ren(a->flags, ISDIR),
++ &a->btgt);
++ if (unlikely(err < 0))
++ goto out_children;
++ if (err)
++ au_fset_ren(a->flags, WHSRC);
++
++ /* lock them all */
++ err = au_ren_lock(a);
++ if (unlikely(err))
++ goto out_children;
++
++ if (!au_opt_test(au_mntflags(a->dst_dir->i_sb), UDBA_NONE)) {
++ err = au_may_ren(a);
++ if (unlikely(err))
++ goto out_hdir;
++ }
++
++ /* store timestamps to be revertible */
++ au_ren_dt(a);
++
++ /* here we go */
++ err = do_rename(a);
++ if (unlikely(err))
++ goto out_dt;
++
++ /* update dir attributes */
++ au_ren_refresh_dir(a);
++
++ /* dput/iput all lower dentries */
++ au_ren_refresh(a);
++
++ goto out_hdir; /* success */
++
++ out_dt:
++ au_ren_rev_dt(err, a);
++ out_hdir:
++ au_ren_unlock(a);
++ out_children:
++ au_nhash_wh_free(&a->whlist);
++ out_unlock:
++ if (unlikely(err && au_ftest_ren(a->flags, ISDIR))) {
++ au_update_dbstart(a->dst_dentry);
++ d_drop(a->dst_dentry);
++ }
++ if (!err)
++ d_move(a->src_dentry, a->dst_dentry);
++ if (au_ftest_ren(a->flags, ISSAMEDIR))
++ di_write_unlock(a->dst_parent);
++ else
++ di_write_unlock2(a->src_parent, a->dst_parent);
++ aufs_read_and_write_unlock2(a->dst_dentry, a->src_dentry);
++ out_free:
++ iput(a->dst_inode);
++ if (a->thargs)
++ au_whtmp_rmdir_free(a->thargs);
++ kfree(a);
++ out:
++ return err;
++}
+diff -Nur linux-2.6.31.5.orig/fs/aufs/Kconfig linux-2.6.31.5/fs/aufs/Kconfig
+--- linux-2.6.31.5.orig/fs/aufs/Kconfig 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/Kconfig 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,132 @@
++config AUFS_FS
++ tristate "Aufs (Advanced multi layered unification filesystem) support"
++ depends on EXPERIMENTAL
++ help
++ Aufs is a stackable unification filesystem such as Unionfs,
++ which unifies several directories and provides a merged single
++ directory.
++ In the early days, aufs was entirely re-designed and
++ re-implemented Unionfs Version 1.x series. Introducing many
++ original ideas, approaches and improvements, it becomes totally
++ different from Unionfs while keeping the basic features.
++
++if AUFS_FS
++choice
++ prompt "Maximum number of branches"
++ 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 a minor 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 a minor 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 a minor 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 a minor 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 a minor impact to performance.
++endchoice
++
++config AUFS_HINOTIFY
++ bool "Use inotify to detect actions on a branch"
++ depends on INOTIFY
++ help
++ If you want to modify files on branches directly, eg. bypassing aufs,
++ and want aufs to detect the changes of them fully, then enable this
++ option and use 'udba=inotify' mount option.
++ It will have a negative impact to the performance.
++ See detail in aufs.5.
++
++config AUFS_EXPORT
++ bool "NFS-exportable aufs"
++ depends on (AUFS_FS = y && EXPORTFS = y) || (AUFS_FS = m && EXPORTFS)
++ help
++ If you want to export your mounted aufs via NFS, then enable this
++ option. There are several requirements for this configuration.
++ See detail in aufs.5.
++
++config AUFS_SHWH
++ bool "Show whiteouts"
++ help
++ If you want to make the whiteouts in aufs visible, then enable
++ this option and specify 'shwh' mount option. Although it may
++ sounds like philosophy or something, but in technically it
++ simply shows the name of whiteout with keeping its behaviour.
++
++config AUFS_BR_RAMFS
++ bool "Ramfs (initramfs/rootfs) as an aufs branch"
++ help
++ If you want to use ramfs as an aufs branch fs, then enable this
++ option. Generally tmpfs is recommended.
++ Aufs prohibited them to be a branch fs by default, because
++ initramfs becomes unusable after switch_root or something
++ generally. If you sets initramfs as an aufs branch and boot your
++ system by switch_root, you will meet a problem easily since the
++ files in initramfs may be inaccessible.
++ Unless you are going to use ramfs as an aufs branch fs without
++ switch_root or something, leave it N.
++
++config AUFS_BR_FUSE
++ bool "Fuse fs as an aufs branch"
++ depends on FUSE_FS
++ select AUFS_POLL
++ help
++ If you want to use fuse-based userspace filesystem as an aufs
++ branch fs, then enable this option.
++ It implements the internal poll(2) operation which is
++ implemented by fuse only (curretnly).
++
++config AUFS_DEBUG
++ bool "Debug aufs"
++ help
++ Enable this to compile aufs internal debug code.
++ It will have a negative impact to the performance.
++
++config AUFS_MAGIC_SYSRQ
++ bool
++ depends on AUFS_DEBUG && MAGIC_SYSRQ
++ default y
++ help
++ Automatic configuration for internal use.
++ When aufs supports Magic SysRq, enabled automatically.
++
++config AUFS_BDEV_LOOP
++ bool
++ depends on BLK_DEV_LOOP
++ default y
++ help
++ Automatic configuration for internal use.
++ Convert =[ym] into =y.
++
++config AUFS_INO_T_64
++ bool
++ depends on AUFS_EXPORT
++ depends on 64BIT && !(ALPHA || S390)
++ default y
++ help
++ Automatic configuration for internal use.
++ /* typedef unsigned long/int __kernel_ino_t */
++ /* alpha and s390x are int */
++
++config AUFS_POLL
++ bool
++ help
++ Automatic configuration for internal use.
++endif
+diff -Nur linux-2.6.31.5.orig/fs/aufs/loop.c linux-2.6.31.5/fs/aufs/loop.c
+--- linux-2.6.31.5.orig/fs/aufs/loop.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/loop.c 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,55 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. 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
++ */
++
++/*
++ * support for loopback block device as a branch
++ */
++
++#include <linux/loop.h>
++#include "aufs.h"
++
++/*
++ * test if two lower dentries have overlapping branches.
++ */
++int au_test_loopback_overlap(struct super_block *sb, struct dentry *h_d1,
++ struct dentry *h_d2)
++{
++ struct inode *h_inode;
++ struct loop_device *l;
++
++ h_inode = h_d1->d_inode;
++ if (MAJOR(h_inode->i_sb->s_dev) != LOOP_MAJOR)
++ return 0;
++
++ l = h_inode->i_sb->s_bdev->bd_disk->private_data;
++ h_d1 = l->lo_backing_file->f_dentry;
++ /* h_d1 can be local NFS. in this case aufs cannot detect the loop */
++ if (unlikely(h_d1->d_sb == sb))
++ return 1;
++ return !!au_test_subdir(h_d1, h_d2);
++}
++
++/* true if a kernel thread named 'loop[0-9].*' accesses a file */
++int au_test_loopback_kthread(void)
++{
++ const char c = current->comm[4];
++
++ return current->mm == NULL
++ && '0' <= c && c <= '9'
++ && strncmp(current->comm, "loop", 4) == 0;
++}
+diff -Nur linux-2.6.31.5.orig/fs/aufs/loop.h linux-2.6.31.5/fs/aufs/loop.h
+--- linux-2.6.31.5.orig/fs/aufs/loop.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/loop.h 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,51 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. 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
++ */
++
++/*
++ * support for loopback mount as a branch
++ */
++
++#ifndef __AUFS_LOOP_H__
++#define __AUFS_LOOP_H__
++
++#ifdef __KERNEL__
++
++struct dentry;
++struct super_block;
++
++#ifdef CONFIG_AUFS_BDEV_LOOP
++/* loop.c */
++int au_test_loopback_overlap(struct super_block *sb, struct dentry *h_d1,
++ struct dentry *h_d2);
++int au_test_loopback_kthread(void);
++#else
++static inline
++int au_test_loopback_overlap(struct super_block *sb, struct dentry *h_d1,
++ struct dentry *h_d2)
++{
++ return 0;
++}
++
++static inline int au_test_loopback_kthread(void)
++{
++ return 0;
++}
++#endif /* BLK_DEV_LOOP */
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_LOOP_H__ */
+diff -Nur linux-2.6.31.5.orig/fs/aufs/magic.mk linux-2.6.31.5/fs/aufs/magic.mk
+--- linux-2.6.31.5.orig/fs/aufs/magic.mk 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/magic.mk 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,52 @@
++
++# defined in ${srctree}/fs/fuse/inode.c
++# tristate
++ifdef CONFIG_FUSE_FS
++ccflags-y += -DFUSE_SUPER_MAGIC=0x65735546
++endif
++
++# defined in ${srctree}/fs/ocfs2/ocfs2_fs.h
++# tristate
++ifdef CONFIG_OCFS2_FS
++ccflags-y += -DOCFS2_SUPER_MAGIC=0x7461636f
++endif
++
++# defined in ${srctree}/fs/ocfs2/dlm/userdlm.h
++# tristate
++ifdef CONFIG_OCFS2_FS_O2CB
++ccflags-y += -DDLMFS_MAGIC=0x76a9f425
++endif
++
++# defined in ${srctree}/fs/ramfs/inode.c
++# always true
++ccflags-y += -DRAMFS_MAGIC=0x858458f6
++
++# defined in ${srctree}/fs/cifs/cifsfs.c
++# tristate
++ifdef CONFIG_CIFS_FS
++ccflags-y += -DCIFS_MAGIC_NUMBER=0xFF534D42
++endif
++
++# defined in ${srctree}/fs/xfs/xfs_sb.h
++# tristate
++ifdef CONFIG_XFS_FS
++ccflags-y += -DXFS_SB_MAGIC=0x58465342
++endif
++
++# defined in ${srctree}/fs/configfs/mount.c
++# tristate
++ifdef CONFIG_CONFIGFS_FS
++ccflags-y += -DCONFIGFS_MAGIC=0x62656570
++endif
++
++# defined in ${srctree}/fs/9p/v9fs.h
++# tristate
++ifdef CONFIG_9P_FS
++ccflags-y += -DV9FS_MAGIC=0x01021997
++endif
++
++# defined in ${srctree}/fs/ubifs/ubifs.h
++# tristate
++ifdef CONFIG_UBIFS_FS
++ccflags-y += -DUBIFS_SUPER_MAGIC=0x24051905
++endif
+diff -Nur linux-2.6.31.5.orig/fs/aufs/Makefile linux-2.6.31.5/fs/aufs/Makefile
+--- linux-2.6.31.5.orig/fs/aufs/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/Makefile 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,23 @@
++
++include ${src}/magic.mk
++-include ${src}/priv_def.mk
++
++obj-$(CONFIG_AUFS_FS) += aufs.o
++aufs-y := module.o sbinfo.o super.o branch.o xino.o sysaufs.o opts.o \
++ wkq.o vfsub.o dcsub.o \
++ cpup.o whout.o plink.o wbr_policy.o \
++ dinfo.o dentry.o \
++ finfo.o file.o f_op.o \
++ dir.o vdir.o \
++ iinfo.o inode.o i_op.o i_op_add.o i_op_del.o i_op_ren.o \
++ ioctl.o
++
++# all are boolean
++aufs-$(CONFIG_SYSFS) += sysfs.o
++aufs-$(CONFIG_DEBUG_FS) += dbgaufs.o
++aufs-$(CONFIG_AUFS_BDEV_LOOP) += loop.o
++aufs-$(CONFIG_AUFS_HINOTIFY) += hinotify.o
++aufs-$(CONFIG_AUFS_EXPORT) += export.o
++aufs-$(CONFIG_AUFS_POLL) += poll.o
++aufs-$(CONFIG_AUFS_DEBUG) += debug.o
++aufs-$(CONFIG_AUFS_MAGIC_SYSRQ) += sysrq.o
+diff -Nur linux-2.6.31.5.orig/fs/aufs/module.c linux-2.6.31.5/fs/aufs/module.c
+--- linux-2.6.31.5.orig/fs/aufs/module.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/module.c 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,173 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. Okajima
++ *
++ * This program, aufs is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/*
++ * module global variables and operations
++ */
++
++#include <linux/module.h>
++#include <linux/seq_file.h>
++#include "aufs.h"
++
++void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp)
++{
++ if (new_sz <= nused)
++ return p;
++
++ p = krealloc(p, new_sz, gfp);
++ if (p)
++ memset(p + nused, 0, new_sz - nused);
++ return p;
++}
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * aufs caches
++ */
++struct kmem_cache *au_cachep[AuCache_Last];
++static int __init au_cache_init(void)
++{
++ au_cachep[AuCache_DINFO] = AuCache(au_dinfo);
++ if (au_cachep[AuCache_DINFO])
++ au_cachep[AuCache_ICNTNR] = AuCache(au_icntnr);
++ if (au_cachep[AuCache_ICNTNR])
++ au_cachep[AuCache_FINFO] = AuCache(au_finfo);
++ if (au_cachep[AuCache_FINFO])
++ au_cachep[AuCache_VDIR] = AuCache(au_vdir);
++ if (au_cachep[AuCache_VDIR])
++ au_cachep[AuCache_DEHSTR] = AuCache(au_vdir_dehstr);
++ if (au_cachep[AuCache_DEHSTR])
++ return 0;
++
++ return -ENOMEM;
++}
++
++static void au_cache_fin(void)
++{
++ int i;
++ for (i = 0; i < AuCache_Last; i++)
++ if (au_cachep[i]) {
++ kmem_cache_destroy(au_cachep[i]);
++ au_cachep[i] = NULL;
++ }
++}
++
++/* ---------------------------------------------------------------------- */
++
++int au_dir_roflags;
++
++/*
++ * functions for module interface.
++ */
++MODULE_LICENSE("GPL");
++/* MODULE_LICENSE("GPL v2"); */
++MODULE_AUTHOR("Junjiro R. Okajima <aufs-users@lists.sourceforge.net>");
++MODULE_DESCRIPTION(AUFS_NAME
++ " -- Advanced multi layered unification filesystem");
++MODULE_VERSION(AUFS_VERSION);
++
++/* it should be 'byte', but param_set_byte() prints it by "%c" */
++short aufs_nwkq = AUFS_NWKQ_DEF;
++MODULE_PARM_DESC(nwkq, "the number of workqueue thread, " AUFS_WKQ_NAME);
++module_param_named(nwkq, aufs_nwkq, short, S_IRUGO);
++
++/* this module parameter has no meaning when SYSFS is disabled */
++int sysaufs_brs = 1;
++MODULE_PARM_DESC(brs, "use <sysfs>/fs/aufs/si_*/brN");
++module_param_named(brs, sysaufs_brs, int, S_IRUGO);
++
++/* ---------------------------------------------------------------------- */
++
++static char au_esc_chars[0x20 + 3]; /* 0x01-0x20, backslash, del, and NULL */
++
++int au_seq_path(struct seq_file *seq, struct path *path)
++{
++ return seq_path(seq, path, au_esc_chars);
++}
++
++/* ---------------------------------------------------------------------- */
++
++static int __init aufs_init(void)
++{
++ int err, i;
++ char *p;
++
++ 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);
++
++ sysaufs_brs_init();
++ au_debug_init();
++
++ err = -EINVAL;
++ if (unlikely(aufs_nwkq <= 0))
++ goto out;
++
++ err = sysaufs_init();
++ if (unlikely(err))
++ goto out;
++ err = au_wkq_init();
++ if (unlikely(err))
++ goto out_sysaufs;
++ err = au_hinotify_init();
++ if (unlikely(err))
++ goto out_wkq;
++ err = au_sysrq_init();
++ if (unlikely(err))
++ goto out_hin;
++ err = au_cache_init();
++ if (unlikely(err))
++ goto out_sysrq;
++ err = register_filesystem(&aufs_fs_type);
++ if (unlikely(err))
++ goto out_cache;
++ pr_info(AUFS_NAME " " AUFS_VERSION "\n");
++ goto out; /* success */
++
++ out_cache:
++ au_cache_fin();
++ out_sysrq:
++ au_sysrq_fin();
++ out_hin:
++ au_hinotify_fin();
++ out_wkq:
++ au_wkq_fin();
++ out_sysaufs:
++ sysaufs_fin();
++ out:
++ return err;
++}
++
++static void __exit aufs_exit(void)
++{
++ unregister_filesystem(&aufs_fs_type);
++ au_cache_fin();
++ au_sysrq_fin();
++ au_hinotify_fin();
++ au_wkq_fin();
++ sysaufs_fin();
++}
++
++module_init(aufs_init);
++module_exit(aufs_exit);
+diff -Nur linux-2.6.31.5.orig/fs/aufs/module.h linux-2.6.31.5/fs/aufs/module.h
+--- linux-2.6.31.5.orig/fs/aufs/module.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/module.h 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,78 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. Okajima
++ *
++ * This program, aufs is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/*
++ * module initialization and module-global
++ */
++
++#ifndef __AUFS_MODULE_H__
++#define __AUFS_MODULE_H__
++
++#ifdef __KERNEL__
++
++#include <linux/slab.h>
++
++struct path;
++struct seq_file;
++
++/* module parameters */
++extern short aufs_nwkq;
++extern int sysaufs_brs;
++
++/* ---------------------------------------------------------------------- */
++
++extern int au_dir_roflags;
++
++void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp);
++int au_seq_path(struct seq_file *seq, struct path *path);
++
++/* ---------------------------------------------------------------------- */
++
++/* kmem cache */
++enum {
++ AuCache_DINFO,
++ AuCache_ICNTNR,
++ AuCache_FINFO,
++ AuCache_VDIR,
++ AuCache_DEHSTR,
++#ifdef CONFIG_AUFS_HINOTIFY
++ AuCache_HINOTIFY,
++#endif
++ AuCache_Last
++};
++
++#define AuCache(type) KMEM_CACHE(type, SLAB_RECLAIM_ACCOUNT)
++
++extern struct kmem_cache *au_cachep[];
++
++#define AuCacheFuncs(name, index) \
++static inline void *au_cache_alloc_##name(void) \
++{ return kmem_cache_alloc(au_cachep[AuCache_##index], GFP_NOFS); } \
++static inline void au_cache_free_##name(void *p) \
++{ kmem_cache_free(au_cachep[AuCache_##index], p); }
++
++AuCacheFuncs(dinfo, DINFO);
++AuCacheFuncs(icntnr, ICNTNR);
++AuCacheFuncs(finfo, FINFO);
++AuCacheFuncs(vdir, VDIR);
++AuCacheFuncs(dehstr, DEHSTR);
++
++/* ---------------------------------------------------------------------- */
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_MODULE_H__ */
+diff -Nur linux-2.6.31.5.orig/fs/aufs/opts.c linux-2.6.31.5/fs/aufs/opts.c
+--- linux-2.6.31.5.orig/fs/aufs/opts.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.31.5/fs/aufs/opts.c 2009-11-15 22:02:37.000000000 +0100
+@@ -0,0 +1,1543 @@
++/*
++ * Copyright (C) 2005-2009 Junjiro R. Okajima
++ *
++ * This program, aufs is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++/*
++ * mount options/flags
++ */
++
++#include <linux/file.h>
++#include <linux/namei.h>
++#include <linux/types.h> /* a distribution requires */
++#include <linux/parser.h>
++#include "aufs.h"
++
++/* ---------------------------------------------------------------------- */
++
++enum {
++ Opt_br,
++ Opt_add, Opt_del, Opt_mod, Opt_reorder, Opt_append, Opt_prepend,
++ Opt_idel, Opt_imod, Opt_ireorder,
++ Opt_dirwh, Opt_rdcache, Opt_rdblk, Opt_rdhash, Opt_rendir,
++ Opt_rdblk_def, Opt_rdhash_def,
++ Opt_xino, Opt_zxino, Opt_noxino,
++ Opt_trunc_xino, Opt_trunc_xino_v, Opt_notrunc_xino,
++ Opt_trunc_xino_path, Opt_itrunc_xino,
++ Opt_trunc_xib, Opt_notrunc_xib,
++ Opt_shwh, Opt_noshwh,
++ Opt_plink, Opt_noplink, Opt_list_plink,
++ Opt_udba,
++ /* Opt_lock, Opt_unlock, */
++ Opt_cmd, Opt_cmd_args,
++ Opt_diropq_a, Opt_diropq_w,
++ Opt_warn_perm, Opt_nowarn_perm,
++ Opt_wbr_copyup, Opt_wbr_create,
++ Opt_refrof, Opt_norefrof,
++ Opt_verbose, Opt_noverbose,
++ Opt_sum, Opt_nosum, Opt_wsum,
++ Opt_tail, Opt_ignore, Opt_ignore_silent, Opt_err
++};
++
++static match_table_t options = {
++ {Opt_br, "br=%s"},
++ {Opt_br, "br:%s"},
++
++ {Opt_add, "add=%d:%s"},
++ {Opt_add, "add:%d:%s"},
++ {Opt_add, "ins=%d:%s"},
++ {Opt_add, "ins:%d:%s"},
++ {Opt_append, "append=%s"},
++ {Opt_append, "append:%s"},
++ {Opt_prepend, "prepend=%s"},
++ {Opt_prepend, "prepend:%s"},
++
++ {Opt_del, "del=%s"},
++ {Opt_del, "del:%s"},
++ /* {Opt_idel, "idel:%d"}, */
++ {Opt_mod, "mod=%s"},
++ {Opt_mod, "mod:%s"},
++ /* {Opt_imod, "imod:%d:%s"}, */
++
++ {Opt_dirwh, "dirwh=%d"},
++
++ {Opt_xino, "xino=%s"},
++ {Opt_noxino, "noxino"},
++ {Opt_trunc_xino, "trunc_xino"},
++ {Opt_trunc_xino_v, "trunc_xino_v=%d:%d"},
++ {Opt_notrunc_xino, "notrunc_xino"},
++ {Opt_trunc_xino_path, "trunc_xino=%s"},
++ {Opt_itrunc_xino, "itrunc_xino=%d"},
++ /* {Opt_zxino, "zxino=%s"}, */
++ {Opt_trunc_xib, "trunc_xib"},
++ {Opt_notrunc_xib, "notrunc_xib"},
++
++ {Opt_plink, "plink"},
++ {Opt_noplink, "noplink"},
++#ifdef CONFIG_AUFS_DEBUG
++ {Opt_list_plink, "list_plink"},
++#endif
++
++ {Opt_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"},
++
++ /* keep them temporary */
++ {Opt_ignore_silent, "coo=%s"},
++ {Opt_ignore_silent, "nodlgt"},
++ {Opt_ignore_silent, "nodirperm1"},
++ {Opt_ignore_silent, "clean_plink"},
++
++#ifdef CONFIG_AUFS_SHWH
++ {Opt_shwh, "shwh"},
++#endif
++ {Opt_noshwh, "noshwh"},
++
++ {Opt_rendir, "rendir=%d"},
++
++ {Opt_refrof, "refrof"},
++ {Opt_norefrof, "norefrof"},
++
++ {Opt_verbose, "verbose"},
++ {Opt_verbose, "v"},
++ {Opt_noverbose, "noverbose"},
++ {Opt_noverbose, "quiet"},
++ {Opt_noverbose, "q"},
++ {Opt_noverbose, "silent"},
++
++ {Opt_sum, "sum"},
++ {Opt_nosum, "nosum"},
++ {Opt_wsum, "wsum"},
++
++ {Opt_rdcache, "rdcache=%d"},
++ {Opt_rdblk, "rdblk=%d"},
++ {Opt_rdblk_def, "rdblk=def"},
++ {Opt_rdhash, "rdhash=%d"},
++ {Opt_rdhash_def, "rdhash=def"},
++
++ {Opt_wbr_create, "create=%s"},
++ {Opt_wbr_create, "create_policy=%s"},
++ {Opt_wbr_copyup, "cpup=%s"},
++ {Opt_wbr_copyup, "copyup=%s"},
++ {Opt_wbr_copyup, "copyup_policy=%s"},
++
++ /* internal use for the scripts */
++ {Opt_ignore_silent, "si=%s"},
++
++ {Opt_br, "dirs=%s"},
++ {Opt_ignore, "debug=%d"},
++ {Opt_ignore, "delete=whiteout"},
++ {Opt_ignore, "delete=all"},
++ {Opt_ignore, "imap=%s"},
++
++ {Opt_err, NULL}
++};
++
++/* ---------------------------------------------------------------------- */
++
++static const char *au_parser_pattern(int val, struct match_token *token)
++{
++ while (token->pattern) {
++ if (token->token == val)
++ return token->pattern;
++ token++;
++ }
++ BUG();
++ return "??";
++}
++
++/* ---------------------------------------------------------------------- */
++
++static match_table_t brperms = {
++ {AuBrPerm_RO, AUFS_BRPERM_RO},
++ {AuBrPerm_RR, AUFS_BRPERM_RR},
++ {AuBrPerm_RW, AUFS_BRPERM_RW},
++
++ {AuBrPerm_ROWH, AUFS_BRPERM_ROWH},
++ {AuBrPerm_RRWH, AUFS_BRPERM_RRWH},
++ {AuBrPerm_RWNoLinkWH, AUFS_BRPERM_RWNLWH},
++
++ {AuBrPerm_ROWH, "nfsro"},
++ {AuBrPerm_RO, NULL}
++};
++
++static int br_perm_val(char *perm)
++{
++ int val;
++ substring_t args[MAX_OPT_ARGS];
++
++ val = match_token(perm, brperms, args);
++ return val;
++}
++
++const char *au_optstr_br_perm(int brperm)
++{
++ return au_parser_pattern(brperm, (void *)brperms);
++}
++
++/* ---------------------------------------------------------------------- */
++
++static match_table_t udbalevel = {
++ {AuOpt_UDBA_REVAL, "reval"},
++ {AuOpt_UDBA_NONE, "none"},
++#ifdef CONFIG_AUFS_HINOTIFY
++ {AuOpt_UDBA_HINOTIFY, "inotify"},
++#endif
++ {-1, NULL}
++};
++
++static int udba_val(char *str)
++{
++ substring_t args[MAX_OPT_ARGS];
++
++ return match_token(str, udbalevel, args);
++}
++
++const char *au_optstr_udba(int udba)
++{
++ return au_parser_pattern(udba, (void *)udbalevel);
++}
++
++/* ---------------------------------------------------------------------- */
++
++static match_table_t au_wbr_create_policy = {
++ {AuWbrCreate_TDP, "tdp"},
++ {AuWbrCreate_TDP, "top-down-parent"},
++ {AuWbrCreate_RR, "rr"},
++ {AuWbrCreate_RR, "round-robin"},
++ {AuWbrCreate_MFS, "mfs"},
++ {AuWbrCreate_MFS, "most-free-space"},
++ {AuWbrCreate_MFSV, "mfs:%d"},
++ {AuWbrCreate_MFSV, "most-free-space:%d"},
++
++ {AuWbrCreate_MFSRR, "mfsrr:%d"},
++ {AuWbrCreate_MFSRRV, "mfsrr:%d:%d"},
++ {AuWbrCreate_PMFS, "pmfs"},
++ {AuWbrCreate_PMFSV, "pmfs:%d"},
++
++ {-1, NULL}
++};
++
++/*
++ * cf. linux/lib/parser.c and cmdline.c
++ * gave up calling memparse() since it uses simple_strtoull() instead of
++ * strict_...().
++ */
++static int au_match_ull(substring_t *s, unsigned long long *result)
++{
++ int err;
++ unsigned int len;
++ char a[32];
++
++ err = -ERANGE;
++ len = s->to - s->from;
++ if (len + 1 <= sizeof(a)) {
++ memcpy(a, s->from, len);
++ a[len] = '\0';
++ err = strict_strtoull(a, 0, result);
++ }
++ return err;
++}
++
++static int au_wbr_mfs_wmark(substring_t *arg, char *str,
++ struct au_opt_wbr_create *create)
++{
++ int err;
++ unsigned long long ull;
++
++ err = 0;
++ if (!au_match_ull(arg, &ull))
++ create->mfsrr_watermark = ull;
++ else {
++ AuErr("bad integer in %s\n", str);
++ err = -EINVAL;
++ }
++
++ return err;
++}
++
++static int au_wbr_mfs_sec(substring_t *arg, char *str,
++ struct au_opt_wbr_create *create)
++{
++ int n, err;
++
++ err = 0;
++ if (!match_int(arg, &n) && 0 <= n)
++ create->mfs_second = n;
++ else {
++ AuErr("bad integer in %s\n", str);
++ err = -EINVAL;
++ }
++
++ return err;
++}
++
++static int au_wbr_create_val(char *str, struct au_opt_wbr_create *create)
++{
++ int err, e;
++ substring_t args[MAX_OPT_ARGS];
++
++ err = match_token(str, au_wbr_create_policy, args);
++ create->wbr_create = err;
++ switch (err) {
++ case AuWbrCreate_MFSRRV:
++ e = au_wbr_mfs_wmark(&args[0], str, create);
++ if (!e)
++ e = au_wbr_mfs_sec(&args[1], str, create);
++ if (unlikely(e))
++ err = e;
++ break;
++ case AuWbrCreate_MFSRR:
++ e = au_wbr_mfs_wmark(&args[0], str, create);
++ if (unlikely(e)) {
++ err = e;
++ break;
++ }
++ /*FALLTHROUGH*/
++ case AuWbrCreate_MFS:
++ case AuWbrCreate_PMFS:
++ create->mfs_second = AUFS_MFS_SECOND_DEF;
++ break;
++ case AuWbrCreate_MFSV:
++ case AuWbrCreate_PMFSV:
++ e = au_wbr_mfs_sec(&args[0], str, create);
++ if (unlikely(e))
++ err = e;
++ break;
++ }
++
++ return err;
++}
++
++const char *au_optstr_wbr_create(int wbr_create)
++{
++ return au_parser_pattern(wbr_create, (void *)au_wbr_create_policy);
++}
++
++static match_table_t au_wbr_copyup_policy = {
++ {AuWbrCopyup_TDP, "tdp"},
++ {AuWbrCopyup_TDP, "top-down-parent"},
++ {AuWbrCopyup_BUP, "bup"},
++ {AuWbrCopyup_BUP, "bottom-up-parent"},
++ {AuWbrCopyup_BU, "bu"},
++ {AuWbrCopyup_BU, "bottom-up"},
++ {-1, NULL}
++};
++
++static int au_wbr_copyup_val(char *str)
++{
++ substring_t args[MAX_OPT_ARGS];
++
++ return match_token(str, au_wbr_copyup_policy, args);
++}
++
++const char *au_optstr_wbr_copyup(int wbr_copyup)
++{
++ return au_parser_pattern(wbr_copyup, (void *)au_wbr_copyup_policy);
++}
++
++/* ---------------------------------------------------------------------- */
++
++static const int lkup_dirflags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
++
++static void dump_opts(struct au_opts *opts)
++{
++#ifdef CONFIG_AUFS_DEBUG
++ /* reduce stack space */
++ union {
++ struct au_opt_add *add;
++ struct au_opt_del *del;
++ struct au_opt_mod *mod;
++ struct au_opt_xino *xino;
++ struct au_opt_xino_itrunc *xino_itrunc;
++ struct au_opt_wbr_create *create;
++ } u;
++ struct au_opt *opt;
++
++ opt = opts->opt;
++ while (opt->type != Opt_tail) {
++ switch (opt->type) {
++ case Opt_add:
++ u.add = &opt->add;
++ AuDbg("add {b%d, %s, 0x%x, %p}\n",
++ u.add->bindex, u.add->pathname, u.add->perm,
++ u.add->path.dentry);
++ break;
++ case Opt_del:
++ case Opt_idel:
++ u.del = &opt->del;
++ AuDbg("del {%s, %p}\n",
++ u.del->pathname, u.del->h_path.dentry);
++ break;
++ case Opt_mod:
++ case Opt_imod:
++ u.mod = &opt->mod;
++ AuDbg("mod {%s, 0x%x, %p}\n",
++ u.mod->path, u.mod->perm, u.mod->h_root);
++ break;
++ case Opt_append:
++ u.add = &opt->add;
++ AuDbg("append {b%d, %s, 0x%x, %p}\n",
++ u.add->bindex, u.add->pathname, u.add->perm,
++ u.add->path.dentry);
++ break;
++ case Opt_prepend:
++ u.add = &opt->add;
++ AuDbg("prepend {b%d, %s, 0x%x, %p}\n",
++ u.add->bindex, u.add->pathname, u.add->perm,
++ u.add->path.dentry);
++ break;
++ case Opt_dirwh:
++ AuDbg("dirwh %d\n", opt->dirwh);
++ break;
++ case Opt_rdcache:
++ AuDbg("rdcache %d\n", opt->rdcache);
++ break;
++ case Opt_rdblk:
++ AuDbg("rdblk %u\n", opt->rdblk);
++ break;
++ case Opt_rdblk_def:
++ AuDbg("rdblk_def\n");
++ break;
++ case Opt_rdhash:
++ AuDbg("rdhash %u\n", opt->rdhash);
++ break;
++ case Opt_rdhash_def:
++ AuDbg("rdhash_def\n");
++ break;
++ case Opt_xino:
++ u.xino = &opt->xino;
++ AuDbg("xino {%s %.*s}\n",