diff options
34 files changed, 19834 insertions, 191 deletions
diff --git a/main/abuild/APKBUILD b/main/abuild/APKBUILD index 1c8de49c5..7f61d84cf 100644 --- a/main/abuild/APKBUILD +++ b/main/abuild/APKBUILD @@ -5,6 +5,7 @@ pkgver=2.5 pkgrel=2 url=http://git.alpinelinux.org/cgit/abuild/ source="http://git.alpinelinux.org/cgit/abuild/snapshot/abuild-$pkgver.tar.bz2 + gawk.patch 0001-abuild-automatically-add-libgcc-to-depends-when-libp.patch gawk.patch " diff --git a/main/alpine-sdk/APKBUILD b/main/alpine-sdk/APKBUILD index 1b6d79cbd..632842576 100644 --- a/main/alpine-sdk/APKBUILD +++ b/main/alpine-sdk/APKBUILD @@ -1,7 +1,7 @@ # Maintainer: Natanael Copa <ncopa@alpinelinux.org> pkgname=alpine-sdk pkgver=0.3 -pkgrel=0 +pkgrel=1 url=http://dev.alpinelinux.org/cgit pkgdesc="Alpine Software Development Kit meta package" depends="abuild build-base git cramfs cdrkit acct mkinitfs mtools" diff --git a/main/eglibc/APKBUILD b/main/eglibc/APKBUILD new file mode 100644 index 000000000..7079766f8 --- /dev/null +++ b/main/eglibc/APKBUILD @@ -0,0 +1,130 @@ +# Contributor: Carlo Landmeter +# Maintainer: +pkgname=eglibc +pkgver=2.12 +pkgrel=3 +pkgdesc="Embedded GLIBC is a variant of the GNU C Library that is designed to work well on embedded systems" +url="http://www.eglibc.org" +license="GPL" +depends="" +depends_dev="linux-headers=>2.6.32" +makedepends="gawk" +options="!strip" +subpackages="$pkgname-dev $pkgname-gconv $pkgname-locales $pkgname-nscd" +source="http://alpine.nethq.org/clandmeter/src/eglibc-2.12-svn-11364.tar.bz2 + glibc-2.10-bz4781.patch + glibc-__i686.patch + nscd.initd +" + +_builddir="$srcdir"/libc + +prepare() { + cd "$_builddir" + + # http://sources.redhat.com/bugzilla/show_bug.cgi?id=4781 + patch -Np1 -i ${srcdir}/glibc-2.10-bz4781.patch + + # http://sources.redhat.com/bugzilla/show_bug.cgi?id=411 + # http://sourceware.org/ml/libc-alpha/2009-07/msg00072.html + patch -Np1 -i ${srcdir}/glibc-__i686.patch + + # Don not build timezones + sed -i 's/timezone rt/rt/' Makeconfig + + echo "slibdir=/lib" >> configparms + + # we need to build in a seperate dir + mkdir eglibc-build +} + +build() { + cd "$_builddir"/eglibc-build + + export CFLAGS="$CFLAGS -fno-stack-protector" + + ../configure --prefix=/usr \ + --sysconfdir=/etc \ + --mandir=/usr/share/man \ + --infodir=/usr/share/info \ + --mandir=/usr/share/info \ + --infodir=/usr/share/info \ + --libdir=/usr/lib \ + --libexecdir=/usr/lib \ + --with-headers=/usr/include \ + --enable-kernel=2.6.32 \ + --enable-add-ons=nptl,libidn \ + --disable-profile \ + --enable-bind-now \ + --with-tls \ + --with-__thread \ + --without-cvs \ + --without-gd + + + make || return 1 + +} + +package() { + cd "$_builddir"/eglibc-build + + install -dm755 "$pkgdir"/etc && touch "$pkgdir"/etc/ld.so.conf + + make -j1 install_root="$pkgdir" install + + # provided by kernel-headers + rm "$pkgdir"/usr/include/scsi/scsi.h + + rm "$pkgdir"/etc/ld.so.conf + + # strip all + for i in ldconfig sln gencat getconf getent iconv locale localedef \ + pcprofiledump rpcgen sprof lddlibc4 iconvconfig nscd rpcinfo; do + find "$pkgdir" -type f -name "$i" -exec strip --strip-all '{}' \; + done + strip --strip-all "$pkgdir"/usr/lib/gconv/*.so + + # strip debug + for i in ld-2.12.1.so libpthread-2.12.1.so libthread_db-1.0.so; do + strip --strip-debug "$pkgdir"/lib/"$i" + done + strip --strip-debug "$pkgdir"/usr/lib/*.a + + # strip uneeded + for i in libanl-2.12.1.so libBrokenLocale-2.12.1.so libc-2.12.1.so \ + libcidn-2.12.1.so libcrypt-2.12.1.so libnss_*-2.12.1.so \ + libdl-2.12.so libm-2.12.so libnsl-2.12.so libresolv-2.12.so \ + librt-2.12.so libutil-2.12.so libmemusage.so libpcprofile.so \ + libSegFault.so pt_chown; do + find "$pkgdir" -type f -name "$i" -exec strip --strip-unneeded '{}' \; + done + strip --strip-unneeded "$pkgdir"/usr/lib/gconv/*.so + +} + +gconv() { + pkgdesc="gconv character modules" + mkdir -p "$subpkgdir"/usr/lib/ + mv "$pkgdir"/usr/lib/gconv "$subpkgdir"/usr/lib/ +} + +locales() { + pkgdesc="Common files for locale support" + mkdir -p "$subpkgdir"/usr/share + mv "$pkgdir"/usr/share/* "$subpkgdir"/usr/share/ +} + +nscd() { + pkgdesc="eglibc name service cache daemon" + mkdir -p "$subpkgdir"/var/db/nscd "$subpkgdir"/var/run/nscd + install -Dm 755 "$srcdir"/nscd.initd "$subpkgdir"/etc/init.d/nscd + install -Dm 644 "$srcdir"/libc/nscd/nscd.conf "$subpkgdir"/etc/nscd.conf + mkdir -p "$subpkgdir"/usr/sbin + mv "$pkgdir"/usr/sbin/nscd "$subpkgdir"/usr/sbin/ +} + +md5sums="4deed2a0761d90a8e33643874fb54b41 eglibc-2.12-svn-11364.tar.bz2 +0c5540efc51c0b93996c51b57a8540ae glibc-2.10-bz4781.patch +40cd342e21f71f5e49e32622b25acc52 glibc-__i686.patch +137fe99a6bc1786da759fb99bfeddb1f nscd.initd" diff --git a/main/eglibc/glibc-2.10-bz4781.patch b/main/eglibc/glibc-2.10-bz4781.patch new file mode 100644 index 000000000..cf1a97a18 --- /dev/null +++ b/main/eglibc/glibc-2.10-bz4781.patch @@ -0,0 +1,42 @@ +diff -Naur glibc-old/sysdeps/unix/sysv/linux/i386/clone.S glibc/sysdeps/unix/sysv/linux/i386/clone.S +--- glibc-old/sysdeps/unix/sysv/linux/i386/clone.S 2009-05-09 13:35:30.000000000 +1000 ++++ glibc/sysdeps/unix/sysv/linux/i386/clone.S 2009-05-23 13:27:46.000000000 +1000 +@@ -120,9 +120,6 @@ + ret + + L(thread_start): +- cfi_startproc; +- /* Clearing frame pointer is insufficient, use CFI. */ +- cfi_undefined (eip); + /* Note: %esi is zero. */ + movl %esi,%ebp /* terminate the stack frame */ + #ifdef RESET_PID +@@ -155,7 +152,6 @@ + jmp L(haspid) + .previous + #endif +- cfi_endproc; + + cfi_startproc + PSEUDO_END (BP_SYM (__clone)) +diff -Naur glibc-old/sysdeps/unix/sysv/linux/x86_64/clone.S glibc/sysdeps/unix/sysv/linux/x86_64/clone.S +--- glibc-old/sysdeps/unix/sysv/linux/x86_64/clone.S 2009-05-09 13:35:30.000000000 +1000 ++++ glibc/sysdeps/unix/sysv/linux/x86_64/clone.S 2009-05-23 13:27:46.000000000 +1000 +@@ -89,9 +89,6 @@ + ret + + L(thread_start): +- cfi_startproc; +- /* Clearing frame pointer is insufficient, use CFI. */ +- cfi_undefined (rip); + /* Clear the frame pointer. The ABI suggests this be done, to mark + the outermost frame obviously. */ + xorl %ebp, %ebp +@@ -116,7 +113,6 @@ + /* Call exit with return value from function call. */ + movq %rax, %rdi + call HIDDEN_JUMPTARGET (_exit) +- cfi_endproc; + + cfi_startproc; + PSEUDO_END (BP_SYM (__clone)) diff --git a/main/eglibc/glibc-__i686.patch b/main/eglibc/glibc-__i686.patch new file mode 100644 index 000000000..28d5dd424 --- /dev/null +++ b/main/eglibc/glibc-__i686.patch @@ -0,0 +1,13 @@ +diff -Naur glibc-old//sysdeps/i386/Makefile glibc//sysdeps/i386/Makefile +--- glibc-old//sysdeps/i386/Makefile 2010-03-18 11:52:30.000000000 +1000 ++++ glibc//sysdeps/i386/Makefile 2010-04-16 15:05:50.000000000 +1000 +@@ -1,6 +1,7 @@ + # The mpn functions need a #define for asm syntax flavor. +-# Every i386 port in use uses gas syntax (I think). +-asm-CPPFLAGS += -DGAS_SYNTAX ++# Every i386 port in use uses gas syntax (I think). Don't replace ++# __i686 in __i686.get_pc_thunk.bx. ++asm-CPPFLAGS += -DGAS_SYNTAX -U __i686 + + # The i386 `long double' is a distinct type we support. + long-double-fcts = yes diff --git a/main/eglibc/nscd.initd b/main/eglibc/nscd.initd new file mode 100644 index 000000000..7087dcf4c --- /dev/null +++ b/main/eglibc/nscd.initd @@ -0,0 +1,19 @@ +#!/sbin/runscript + +DAEMON=/usr/sbin/nscd + +start() { + ebegin "Starting nscd" + # remove stale files + rm -f /var/db/nscd/* /var/run/nscd/* 2>/dev/null + start-stop-daemon --start --exec $DAEMON + eend $? +} + +stop () { + ebegin "Stopping nscd" + start-stop-daemon --stop --quiet \ + --pidfile=/var/run/nscd/nscd.pid + eend $? +} + diff --git a/main/gcc/APKBUILD b/main/gcc/APKBUILD index e4d64eca1..ff1dd75eb 100644 --- a/main/gcc/APKBUILD +++ b/main/gcc/APKBUILD @@ -32,7 +32,7 @@ source="ftp://gcc.gnu.org/pub/gcc/releases/gcc-$pkgver/gcc-core-$pkgver.tar.bz2 gcc-dynamic-linker.patch PR32219.patch " -# ftp://gcc.gnu.org/pub/gcc/releases/gcc-$pkgver/gcc-objc-$pkgver.tar.bz2 1 +# ftp://gcc.gnu.org/pub/gcc/releases/gcc-$pkgver/gcc-objc-$pkgver.tar.bz2 build () { diff --git a/main/linux-xbmc/APKBUILD b/main/linux-xbmc/APKBUILD new file mode 100644 index 000000000..cb67365c6 --- /dev/null +++ b/main/linux-xbmc/APKBUILD @@ -0,0 +1,140 @@ +# Maintainer: Natanael Copa <ncopa@alpinelinux.org> + +_flavor=xbmc +pkgname=linux-${_flavor} +pkgver=2.6.35.4 +_kernver=2.6.35 +pkgrel=8 +pkgdesc="Linux kernel designed for XBMC" +url=http://www.kernel.org +depends="mkinitfs" +makedepends="perl installkernel" +options="!strip" +_config=${config:-kernelconfig.${CARCH:-x86}} +install= +source="ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-$_kernver.tar.bz2 + ftp://ftp.kernel.org/pub/linux/kernel/v2.6/patch-$pkgver.bz2 + kernelconfig.x86 + linux-2.6.35-squashfs_lzma.patch + unionfs-2.5.5_for_2.6.35.4.patch + " +subpackages="$pkgname-dev" +license="GPL-2" + +_abi_release=${pkgver}-${_flavor} + +# prevent appending + to kernel version +export LOCALVERSION="" + +prepare() { + cd "$srcdir"/linux-$_kernver + if [ "$_kernver" != "$pkgver" ]; then + bunzip2 -c < ../patch-$pkgver.bz2 | patch -p1 -N || return 1 + fi + + # first apply patches in specified order + for i in $source; do + case $i in + *.patch) + msg "Applying $i..." + patch -p1 < "$srcdir"/$i || return 1 + ;; + esac + done + + mkdir -p "$srcdir"/build + cp "$srcdir"/$_config "$srcdir"/build/.config + echo "-${_flavor}" > "$srcdir"/linux-$_kernver/localversion-${_flavor} + make -C "$srcdir"/linux-$_kernver O="$srcdir"/build HOSTCC="${CC:-gcc}" \ + silentoldconfig +} + +# this is so we can do: 'abuild menuconfig' to reconfigure kernel +menuconfig() { + cd "$srcdir"/build || return 1 + make menuconfig + cp .config "$startdir"/$_config +} + +build() { + cd "$srcdir"/build + make CC="${CC:-gcc}" \ + KBUILD_BUILD_VERSION="$((pkgrel + 1 ))-Alpine" \ + || return 1 + + +} + +package() { + cd "$srcdir"/build + mkdir -p "$pkgdir"/boot "$pkgdir"/lib/modules + make modules_install install \ + INSTALL_MOD_PATH="$pkgdir" \ + INSTALL_PATH="$pkgdir"/boot + + rm -f "$pkgdir"/lib/modules/${_abi_release}/build \ + "$pkgdir"/lib/modules/${_abi_release}/source + rm -rf "$pkgdir"/lib/firmware + + install -D include/config/kernel.release \ + "$pkgdir"/usr/share/kernel/$_flavor/kernel.release +} + +dev() { + # copy the only the parts that we really need for build 3rd party + # kernel modules and install those as /usr/src/linux-headers, + # simlar to what ubuntu does + # + # this way you dont need to install the 300-400 kernel sources to + # build a tiny kernel module + # + pkgdesc="Headers and script for third party modules for $_flavor kernel" + local dir="$subpkgdir"/usr/src/linux-headers-${_abi_release} + + # first we import config, run prepare to set up for building + # external modules, and create the scripts + mkdir -p "$dir" + cp "$srcdir"/$_config "$dir"/.config + make -j1 -C "$srcdir"/linux-$_kernver O="$dir" HOSTCC="${CC:-gcc}" \ + silentoldconfig prepare scripts + + # remove the stuff that poits to real sources. we want 3rd party + # modules to believe this is the soruces + rm "$dir"/Makefile "$dir"/source + + # copy the needed stuff from real sources + # + # this is taken from ubuntu kernel build script + # http://kernel.ubuntu.com/git?p=ubuntu/ubuntu-jaunty.git;a=blob;f=debian/rules.d/3-binary-indep.mk;hb=HEAD + cd "$srcdir"/linux-$_kernver + find . -path './include/*' -prune -o -path './scripts/*' -prune \ + -o -type f \( -name 'Makefile*' -o -name 'Kconfig*' \ + -o -name 'Kbuild*' -o -name '*.sh' -o -name '*.pl' \ + -o -name '*.lds' \) | cpio -pdm "$dir" + cp -a drivers/media/dvb/dvb-core/*.h "$dir"/drivers/media/dvb/dvb-core + cp -a drivers/media/video/*.h "$dir"/drivers/media/video + cp -a drivers/media/dvb/frontends/*.h "$dir"/drivers/media/dvb/frontends + cp -a scripts include "$dir" + find $(find arch -name include -type d -print) -type f \ + | cpio -pdm "$dir" + + install -Dm644 "$srcdir"/build/Module.symvers \ + "$dir"/Module.symvers + + mkdir -p "$subpkgdir"/lib/modules/${_abi_release} + ln -sf /usr/src/linux-headers-${_abi_release} \ + "$subpkgdir"/lib/modules/${_abi_release}/build +} + +firmware() { + pkgdesc="Firmware for linux kernel" + replaces="linux-grsec linux-vserver" + mkdir -p "$subpkgdir"/lib + mv "$pkgdir"/lib/firmware "$subpkgdir"/lib/ +} + +md5sums="091abeb4684ce03d1d936851618687b6 linux-2.6.35.tar.bz2 +738f762746488345b1a8707d00895eef patch-2.6.35.4.bz2 +cb6aec662a52be24da65a8e4632a4379 kernelconfig.x86 +7410a712621d4ae3c5f9f58ef9623f48 linux-2.6.35-squashfs_lzma.patch +4ac8e47e585e9083d9c273456bcf475d unionfs-2.5.5_for_2.6.35.4.patch" diff --git a/main/linux-xbmc/kernelconfig.x86 b/main/linux-xbmc/kernelconfig.x86 new file mode 100644 index 000000000..2edb19718 --- /dev/null +++ b/main/linux-xbmc/kernelconfig.x86 @@ -0,0 +1,2396 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.35.4 +# Sat Sep 25 15:24:51 2010 +# +# CONFIG_64BIT is not set +CONFIG_X86_32=y +# CONFIG_X86_64 is not set +CONFIG_X86=y +CONFIG_INSTRUCTION_DECODER=y +CONFIG_OUTPUT_FORMAT="elf32-i386" +CONFIG_ARCH_DEFCONFIG="arch/x86/configs/i386_defconfig" +CONFIG_GENERIC_TIME=y +CONFIG_GENERIC_CMOS_UPDATE=y +CONFIG_CLOCKSOURCE_WATCHDOG=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_MMU=y +CONFIG_ZONE_DMA=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NEED_SG_DMA_LENGTH=y +CONFIG_GENERIC_ISA_DMA=y +CONFIG_GENERIC_IOMAP=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_ARCH_MAY_HAVE_PC_FDC=y +# CONFIG_RWSEM_GENERIC_SPINLOCK is not set +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +# CONFIG_GENERIC_TIME_VSYSCALL is not set +CONFIG_ARCH_HAS_CPU_RELAX=y +CONFIG_ARCH_HAS_DEFAULT_IDLE=y +CONFIG_ARCH_HAS_CACHE_LINE_SIZE=y +CONFIG_HAVE_SETUP_PER_CPU_AREA=y +CONFIG_NEED_PER_CPU_EMBED_FIRST_CHUNK=y +CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK=y +# CONFIG_HAVE_CPUMASK_OF_CPU_MAP is not set +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +# CONFIG_ZONE_DMA32 is not set +CONFIG_ARCH_POPULATES_NODE_MAP=y +# CONFIG_AUDIT_ARCH is not set +CONFIG_ARCH_SUPPORTS_OPTIMIZED_INLINING=y +CONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC=y +CONFIG_HAVE_EARLY_RES=y +CONFIG_HAVE_INTEL_TXT=y +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_GENERIC_PENDING_IRQ=y +CONFIG_USE_GENERIC_SMP_HELPERS=y +CONFIG_X86_32_SMP=y +CONFIG_X86_HT=y +CONFIG_X86_TRAMPOLINE=y +CONFIG_ARCH_HWEIGHT_CFLAGS="-fcall-saved-ecx -fcall-saved-edx" +CONFIG_KTIME_SCALAR=y +CONFIG_ARCH_CPU_PROBE_RELEASE=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_CONSTRUCTORS=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_LOCK_KERNEL=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_BZIP2=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_LZO=y +CONFIG_KERNEL_GZIP=y +# CONFIG_KERNEL_BZIP2 is not set +# CONFIG_KERNEL_LZMA is not set +# CONFIG_KERNEL_LZO is not set +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +CONFIG_POSIX_MQUEUE=y +CONFIG_POSIX_MQUEUE_SYSCTL=y +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 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_TINY_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=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=18 +CONFIG_HAVE_UNSTABLE_SCHED_CLOCK=y +CONFIG_CGROUPS=y +# CONFIG_CGROUP_DEBUG is not set +CONFIG_CGROUP_NS=y +CONFIG_CGROUP_FREEZER=y +# CONFIG_CGROUP_DEVICE is not set +CONFIG_CPUSETS=y +CONFIG_PROC_PID_CPUSET=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +# CONFIG_CGROUP_MEM_RES_CTLR is not set +# CONFIG_CGROUP_SCHED is not set +# CONFIG_BLK_CGROUP 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=y +CONFIG_RD_LZMA=y +CONFIG_RD_LZO=y +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_HOTPLUG=y +CONFIG_PRINTK=y +# CONFIG_BUG is not set +CONFIG_ELF_CORE=y +# CONFIG_PCSPKR_PLATFORM is not set +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 +CONFIG_HAVE_PERF_EVENTS=y + +# +# Kernel Performance Events And Counters +# +CONFIG_PERF_EVENTS=y +# CONFIG_PERF_COUNTERS is not set +# CONFIG_VM_EVENT_COUNTERS is not set +CONFIG_PCI_QUIRKS=y +# CONFIG_COMPAT_BRK is not set +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y +CONFIG_HAVE_IOREMAP_PROT=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_OPTPROBES=y +CONFIG_HAVE_ARCH_TRACEHOOK=y +CONFIG_HAVE_DMA_ATTRS=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_HW_BREAKPOINT=y +CONFIG_HAVE_MIXED_BREAKPOINTS_REGS=y +CONFIG_HAVE_USER_RETURN_NOTIFIER=y + +# +# GCOV-based kernel profiling +# +CONFIG_SLOW_WORK=y +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_STOP_MACHINE=y +CONFIG_BLOCK=y +CONFIG_LBDAF=y +CONFIG_BLK_DEV_BSG=y +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_IOSCHED_CFQ=y +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +CONFIG_INLINE_SPIN_UNLOCK=y +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +CONFIG_INLINE_SPIN_UNLOCK_IRQ=y +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +CONFIG_INLINE_READ_UNLOCK=y +# CONFIG_INLINE_READ_UNLOCK_BH is not set +CONFIG_INLINE_READ_UNLOCK_IRQ=y +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +CONFIG_INLINE_WRITE_UNLOCK=y +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +CONFIG_INLINE_WRITE_UNLOCK_IRQ=y +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +CONFIG_MUTEX_SPIN_ON_OWNER=y +CONFIG_FREEZER=y + +# +# Processor type and features +# +CONFIG_TICK_ONESHOT=y +# CONFIG_NO_HZ is not set +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_SMP=y +# CONFIG_SPARSE_IRQ is not set +CONFIG_X86_MPPARSE=y +# CONFIG_X86_BIGSMP is not set +# CONFIG_X86_EXTENDED_PLATFORM is not set +CONFIG_SCHED_OMIT_FRAME_POINTER=y +# CONFIG_PARAVIRT_GUEST is not set +CONFIG_NO_BOOTMEM=y +# CONFIG_MEMTEST is not set +# CONFIG_M386 is not set +# CONFIG_M486 is not set +CONFIG_M586=y +# CONFIG_M586TSC is not set +# CONFIG_M586MMX is not set +# CONFIG_M686 is not set +# CONFIG_MPENTIUMII is not set +# CONFIG_MPENTIUMIII is not set +# CONFIG_MPENTIUMM is not set +# CONFIG_MPENTIUM4 is not set +# CONFIG_MK6 is not set +# CONFIG_MK7 is not set +# CONFIG_MK8 is not set +# CONFIG_MCRUSOE is not set +# CONFIG_MEFFICEON is not set +# CONFIG_MWINCHIPC6 is not set +# CONFIG_MWINCHIP3D is not set +# CONFIG_MGEODEGX1 is not set +# CONFIG_MGEODE_LX is not set +# CONFIG_MCYRIXIII is not set +# CONFIG_MVIAC3_2 is not set +# CONFIG_MVIAC7 is not set +# CONFIG_MPSC is not set +# CONFIG_MCORE2 is not set +# CONFIG_MATOM is not set +# CONFIG_GENERIC_CPU is not set +# CONFIG_X86_GENERIC is not set +CONFIG_X86_CPU=y +CONFIG_X86_INTERNODE_CACHE_SHIFT=5 +CONFIG_X86_CMPXCHG=y +CONFIG_X86_L1_CACHE_SHIFT=5 +CONFIG_X86_XADD=y +# CONFIG_X86_PPRO_FENCE is not set +CONFIG_X86_F00F_BUG=y +CONFIG_X86_WP_WORKS_OK=y +CONFIG_X86_INVLPG=y +CONFIG_X86_BSWAP=y +CONFIG_X86_POPAD_OK=y +CONFIG_X86_ALIGNMENT_16=y +CONFIG_X86_MINIMUM_CPU_FAMILY=4 +CONFIG_PROCESSOR_SELECT=y +CONFIG_CPU_SUP_INTEL=y +# CONFIG_CPU_SUP_CYRIX_32 is not set +# CONFIG_CPU_SUP_AMD is not set +# CONFIG_CPU_SUP_CENTAUR is not set +# CONFIG_CPU_SUP_TRANSMETA_32 is not set +# CONFIG_CPU_SUP_UMC_32 is not set +CONFIG_HPET_TIMER=y +CONFIG_HPET_EMULATE_RTC=y +CONFIG_DMI=y +# CONFIG_IOMMU_HELPER is not set +CONFIG_IOMMU_API=y +CONFIG_NR_CPUS=4 +CONFIG_SCHED_SMT=y +CONFIG_SCHED_MC=y +# CONFIG_PREEMPT_NONE is not set +CONFIG_PREEMPT_VOLUNTARY=y +# CONFIG_PREEMPT is not set +CONFIG_X86_LOCAL_APIC=y +CONFIG_X86_IO_APIC=y +# CONFIG_X86_REROUTE_FOR_BROKEN_BOOT_IRQS is not set +# CONFIG_X86_MCE is not set +CONFIG_VM86=y +# CONFIG_TOSHIBA is not set +# CONFIG_I8K is not set +# CONFIG_X86_REBOOTFIXUPS is not set +# CONFIG_MICROCODE is not set +CONFIG_X86_MSR=y +CONFIG_X86_CPUID=y +# CONFIG_NOHIGHMEM is not set +CONFIG_HIGHMEM4G=y +# CONFIG_HIGHMEM64G is not set +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_3G_OPT is not set +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_2G_OPT is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_HIGHMEM=y +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set +CONFIG_ARCH_FLATMEM_ENABLE=y +CONFIG_ARCH_SPARSEMEM_ENABLE=y +CONFIG_ARCH_SELECT_MEMORY_MODEL=y +CONFIG_ILLEGAL_POINTER_VALUE=0 +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_SPARSEMEM_STATIC=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=1 +CONFIG_BOUNCE=y +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +# CONFIG_HIGHPTE is not set +# CONFIG_X86_CHECK_BIOS_CORRUPTION is not set +# CONFIG_X86_RESERVE_LOW_64K is not set +# CONFIG_MATH_EMULATION is not set +CONFIG_MTRR=y +CONFIG_MTRR_SANITIZER=y +CONFIG_MTRR_SANITIZER_ENABLE_DEFAULT=0 +CONFIG_MTRR_SANITIZER_SPARE_REG_NR_DEFAULT=1 +CONFIG_X86_PAT=y +CONFIG_ARCH_USES_PG_UNCACHED=y +# CONFIG_EFI is not set +CONFIG_SECCOMP=y +CONFIG_CC_STACKPROTECTOR=y +# CONFIG_HZ_100 is not set +# CONFIG_HZ_250 is not set +CONFIG_HZ_300=y +# CONFIG_HZ_1000 is not set +CONFIG_HZ=300 +CONFIG_SCHED_HRTICK=y +# CONFIG_KEXEC is not set +# CONFIG_CRASH_DUMP is not set +CONFIG_PHYSICAL_START=0x1000000 +# CONFIG_RELOCATABLE is not set +CONFIG_PHYSICAL_ALIGN=0x1000000 +CONFIG_HOTPLUG_CPU=y +# CONFIG_COMPAT_VDSO is not set +# CONFIG_CMDLINE_BOOL is not set +CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y + +# +# Power management and ACPI options +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +CONFIG_PM_SLEEP_SMP=y +CONFIG_PM_SLEEP=y +CONFIG_SUSPEND_NVS=y +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_PM_RUNTIME=y +CONFIG_PM_OPS=y +CONFIG_ACPI=y +CONFIG_ACPI_SLEEP=y +# CONFIG_ACPI_PROCFS is not set +# CONFIG_ACPI_PROCFS_POWER is not set +# CONFIG_ACPI_POWER_METER is not set +CONFIG_ACPI_SYSFS_POWER=y +CONFIG_ACPI_PROC_EVENT=y +CONFIG_ACPI_AC=y +CONFIG_ACPI_BATTERY=y +CONFIG_ACPI_BUTTON=y +CONFIG_ACPI_FAN=y +# CONFIG_ACPI_DOCK is not set +CONFIG_ACPI_PROCESSOR=y +CONFIG_ACPI_HOTPLUG_CPU=y +# CONFIG_ACPI_PROCESSOR_AGGREGATOR is not set +CONFIG_ACPI_THERMAL=y +# CONFIG_ACPI_CUSTOM_DSDT is not set +CONFIG_ACPI_BLACKLIST_YEAR=0 +# CONFIG_ACPI_DEBUG is not set +CONFIG_ACPI_PCI_SLOT=y +CONFIG_X86_PM_TIMER=y +CONFIG_ACPI_CONTAINER=y +# CONFIG_ACPI_SBS is not set +# CONFIG_ACPI_HED is not set +# CONFIG_ACPI_APEI is not set +# CONFIG_SFI is not set +# CONFIG_APM is not set + +# +# CPU Frequency scaling +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_TABLE=y +# CONFIG_CPU_FREQ_DEBUG is not set +# CONFIG_CPU_FREQ_STAT is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set + +# +# CPUFreq processor drivers +# +# CONFIG_X86_PCC_CPUFREQ is not set +CONFIG_X86_ACPI_CPUFREQ=y +# CONFIG_X86_POWERNOW_K6 is not set +# CONFIG_X86_POWERNOW_K7 is not set +# CONFIG_X86_POWERNOW_K8 is not set +# CONFIG_X86_GX_SUSPMOD is not set +# CONFIG_X86_SPEEDSTEP_CENTRINO is not set +# CONFIG_X86_SPEEDSTEP_ICH is not set +# CONFIG_X86_SPEEDSTEP_SMI is not set +# CONFIG_X86_P4_CLOCKMOD is not set +# CONFIG_X86_CPUFREQ_NFORCE2 is not set +# CONFIG_X86_LONGRUN is not set +# CONFIG_X86_LONGHAUL is not set +# CONFIG_X86_E_POWERSAVER is not set + +# +# shared options +# +# CONFIG_X86_SPEEDSTEP_LIB is not set +CONFIG_CPU_IDLE=y +CONFIG_CPU_IDLE_GOV_LADDER=y +CONFIG_INTEL_IDLE=y + +# +# Bus options (PCI etc.) +# +CONFIG_PCI=y +# CONFIG_PCI_GOBIOS is not set +# CONFIG_PCI_GOMMCONFIG is not set +# CONFIG_PCI_GODIRECT is not set +# CONFIG_PCI_GOOLPC is not set +CONFIG_PCI_GOANY=y +CONFIG_PCI_BIOS=y +CONFIG_PCI_DIRECT=y +CONFIG_PCI_MMCONFIG=y +CONFIG_PCI_DOMAINS=y +# CONFIG_PCI_CNB20LE_QUIRK is not set +CONFIG_DMAR=y +CONFIG_DMAR_DEFAULT_ON=y +CONFIG_DMAR_FLOPPY_WA=y +CONFIG_PCIEPORTBUS=y +CONFIG_PCIEAER=y +# CONFIG_PCIE_ECRC is not set +# CONFIG_PCIEAER_INJECT is not set +CONFIG_PCIEASPM=y +# CONFIG_PCIEASPM_DEBUG is not set +CONFIG_PCIE_PME=y +CONFIG_ARCH_SUPPORTS_MSI=y +CONFIG_PCI_MSI=y +# CONFIG_PCI_STUB is not set +CONFIG_HT_IRQ=y +# CONFIG_PCI_IOV is not set +CONFIG_PCI_IOAPIC=y +CONFIG_ISA_DMA_API=y +# CONFIG_ISA is not set +# CONFIG_MCA is not set +# CONFIG_SCx200 is not set +# CONFIG_OLPC is not set +# CONFIG_PCCARD is not set +# CONFIG_HOTPLUG_PCI is not set + +# +# Executable file formats / Emulations +# +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=y +CONFIG_HAVE_ATOMIC_IOMAP=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=m +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +CONFIG_SYN_COOKIES=y +# 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=m +CONFIG_INET_XFRM_MODE_TRANSPORT=y +CONFIG_INET_XFRM_MODE_TUNNEL=y +CONFIG_INET_XFRM_MODE_BEET=y +CONFIG_INET_LRO=y +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=m +# CONFIG_IPV6_PRIVACY is not set +# CONFIG_IPV6_ROUTER_PREF is not set +# CONFIG_IPV6_OPTIMISTIC_DAD is not set +# CONFIG_INET6_AH is not set +# CONFIG_INET6_ESP is not set +# CONFIG_INET6_IPCOMP is not set +# CONFIG_IPV6_MIP6 is not set +# CONFIG_INET6_XFRM_TUNNEL is not set +# CONFIG_INET6_TUNNEL is not set +CONFIG_INET6_XFRM_MODE_TRANSPORT=m +CONFIG_INET6_XFRM_MODE_TUNNEL=m +CONFIG_INET6_XFRM_MODE_BEET=m +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +CONFIG_IPV6_SIT=m +# CONFIG_IPV6_SIT_6RD is not set +CONFIG_IPV6_NDISC_NODETYPE=y +# CONFIG_IPV6_TUNNEL is not set +# CONFIG_IPV6_MULTIPLE_TABLES is not set +# CONFIG_IPV6_MROUTE 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_L2TP 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 +CONFIG_RPS=y + +# +# 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=m +CONFIG_BT_L2CAP=m +# CONFIG_BT_L2CAP_EXT_FEATURES is not set +CONFIG_BT_SCO=m +CONFIG_BT_RFCOMM=m +CONFIG_BT_RFCOMM_TTY=y +# CONFIG_BT_BNEP is not set +CONFIG_BT_HIDP=m + +# +# Bluetooth device drivers +# +CONFIG_BT_HCIBTUSB=m +# CONFIG_BT_HCIUART is not set +# CONFIG_BT_HCIBCM203X is not set +# CONFIG_BT_HCIBPA10X is not set +# CONFIG_BT_HCIBFUSB is not set +# CONFIG_BT_HCIVHCI is not set +# CONFIG_BT_MRVL is not set +# CONFIG_BT_ATH3K is not set +# CONFIG_AF_RXRPC is not set +CONFIG_WIRELESS=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=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_INTERNAL_REGDB is not set +CONFIG_CFG80211_WEXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +CONFIG_LIB80211=m +# CONFIG_LIB80211_DEBUG is not set +CONFIG_MAC80211=m +CONFIG_MAC80211_HAS_RC=y +# 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=y +# CONFIG_MAC80211_DEBUG_MENU is not set +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +CONFIG_RFKILL_LEDS=y +CONFIG_RFKILL_INPUT=y +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_STANDALONE=y +# CONFIG_PREVENT_FIRMWARE_BUILD is not set +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_CONNECTOR is not set +# CONFIG_MTD is not set +# CONFIG_PARPORT is not set +CONFIG_PNP=y +# CONFIG_PNP_DEBUG_MESSAGES is not set + +# +# Protocols +# +CONFIG_PNPACPI=y +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_UMEM is not set +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=m +# CONFIG_BLK_DEV_CRYPTOLOOP is not set + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_SX8 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=16384 +# 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_MISC_DEVICES is not set +CONFIG_HAVE_IDE=y +CONFIG_IDE=y + +# +# Please see Documentation/ide/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_IDE_SATA is not set +CONFIG_IDE_GD=y +CONFIG_IDE_GD_ATA=y +# CONFIG_IDE_GD_ATAPI is not set +# CONFIG_BLK_DEV_IDECD is not set +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEACPI is not set +# CONFIG_IDE_TASK_IOCTL is not set +CONFIG_IDE_PROC_FS=y + +# +# IDE chipset support/bugfixes +# +# CONFIG_IDE_GENERIC is not set +# CONFIG_BLK_DEV_PLATFORM is not set +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_BLK_DEV_IDEPNP is not set + +# +# PCI IDE chipsets support +# +# CONFIG_BLK_DEV_GENERIC is not set +# CONFIG_BLK_DEV_OPTI621 is not set +# CONFIG_BLK_DEV_RZ1000 is not set +# CONFIG_BLK_DEV_AEC62XX is not set +# CONFIG_BLK_DEV_ALI15X3 is not set +# CONFIG_BLK_DEV_AMD74XX is not set +# CONFIG_BLK_DEV_ATIIXP is not set +# CONFIG_BLK_DEV_CMD64X is not set +# CONFIG_BLK_DEV_TRIFLEX is not set +# CONFIG_BLK_DEV_CS5520 is not set +# CONFIG_BLK_DEV_CS5530 is not set +# CONFIG_BLK_DEV_CS5535 is not set +# CONFIG_BLK_DEV_CS5536 is not set +# CONFIG_BLK_DEV_HPT366 is not set +# CONFIG_BLK_DEV_JMICRON is not set +# CONFIG_BLK_DEV_SC1200 is not set +# CONFIG_BLK_DEV_PIIX is not set +# CONFIG_BLK_DEV_IT8172 is not set +# CONFIG_BLK_DEV_IT8213 is not set +# CONFIG_BLK_DEV_IT821X is not set +# CONFIG_BLK_DEV_NS87415 is not set +# CONFIG_BLK_DEV_PDC202XX_OLD is not set +# CONFIG_BLK_DEV_PDC202XX_NEW is not set +# CONFIG_BLK_DEV_SVWKS is not set +# CONFIG_BLK_DEV_SIIMAGE is not set +# CONFIG_BLK_DEV_SIS5513 is not set +# CONFIG_BLK_DEV_SLC90E66 is not set +# CONFIG_BLK_DEV_TRM290 is not set +# CONFIG_BLK_DEV_VIA82CXXX is not set +# CONFIG_BLK_DEV_TC86C001 is not set +# CONFIG_BLK_DEV_IDEDMA is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_TGT is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_SCSI_PROC_FS is not set + +# +# 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=y +# CONFIG_BLK_DEV_SR_VENDOR is not set +CONFIG_CHR_DEV_SG=y +# CONFIG_CHR_DEV_SCH is not set +# CONFIG_SCSI_MULTI_LUN is not set +# 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_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=y +# CONFIG_ATA_NONSTANDARD is not set +# CONFIG_ATA_VERBOSE_ERROR is not set +CONFIG_ATA_ACPI=y +# CONFIG_SATA_PMP is not set + +# +# Controllers with non-SFF native interface +# +CONFIG_SATA_AHCI=y +CONFIG_SATA_AHCI_PLATFORM=y +# CONFIG_SATA_INIC162X is not set +# CONFIG_SATA_SIL24 is not set +CONFIG_ATA_SFF=y + +# +# SFF controllers with custom DMA interface +# +# CONFIG_PDC_ADMA is not set +# CONFIG_SATA_QSTOR is not set +# CONFIG_SATA_SX4 is not set +CONFIG_ATA_BMDMA=y + +# +# SATA SFF controllers with BMDMA +# +CONFIG_ATA_PIIX=y +# CONFIG_SATA_MV is not set +CONFIG_SATA_NV=y +# CONFIG_SATA_PROMISE is not set +# CONFIG_SATA_SIL is not set +# CONFIG_SATA_SIS is not set +# CONFIG_SATA_SVW is not set +# CONFIG_SATA_ULI is not set +# CONFIG_SATA_VIA is not set +# CONFIG_SATA_VITESSE is not set + +# +# PATA SFF controllers with BMDMA +# +# CONFIG_PATA_ALI is not set +# CONFIG_PATA_AMD is not set +# CONFIG_PATA_ARTOP is not set +# CONFIG_PATA_ATIIXP is not set +# CONFIG_PATA_ATP867X is not set +# CONFIG_PATA_CMD64X is not set +# CONFIG_PATA_CS5520 is not set +# CONFIG_PATA_CS5530 is not set +# CONFIG_PATA_CS5535 is not set +# CONFIG_PATA_CS5536 is not set +# CONFIG_PATA_CYPRESS is not set +# CONFIG_PATA_EFAR is not set +# CONFIG_PATA_HPT366 is not set +# CONFIG_PATA_HPT37X is not set +# CONFIG_PATA_HPT3X2N is not set +# CONFIG_PATA_HPT3X3 is not set +# CONFIG_PATA_IT8213 is not set +# CONFIG_PATA_IT821X is not set +# CONFIG_PATA_JMICRON is not set +# CONFIG_PATA_MARVELL is not set +# CONFIG_PATA_NETCELL is not set +# CONFIG_PATA_NINJA32 is not set +# CONFIG_PATA_NS87415 is not set +# CONFIG_PATA_OLDPIIX is not set +# CONFIG_PATA_OPTIDMA is not set +# CONFIG_PATA_PDC2027X is not set +# CONFIG_PATA_PDC_OLD is not set +# CONFIG_PATA_RADISYS is not set +# CONFIG_PATA_RDC is not set +# CONFIG_PATA_SC1200 is not set +# CONFIG_PATA_SCH is not set +# CONFIG_PATA_SERVERWORKS is not set +# CONFIG_PATA_SIL680 is not set +# CONFIG_PATA_SIS is not set +# CONFIG_PATA_TOSHIBA is not set +# CONFIG_PATA_TRIFLEX is not set +# CONFIG_PATA_VIA is not set +# CONFIG_PATA_WINBOND is not set + +# +# PIO-only SFF controllers +# +# CONFIG_PATA_CMD640_PCI is not set +# CONFIG_PATA_MPIIX is not set +# CONFIG_PATA_NS87410 is not set +# CONFIG_PATA_OPTI is not set +# CONFIG_PATA_PLATFORM is not set +# CONFIG_PATA_RZ1000 is not set + +# +# Generic fallback / legacy drivers +# +CONFIG_PATA_ACPI=y +CONFIG_ATA_GENERIC=y +# CONFIG_PATA_LEGACY is not set +# CONFIG_MD is not set +# CONFIG_FUSION is not set + +# +# IEEE 1394 (FireWire) support +# + +# +# You can enable one or both FireWire driver stacks. +# + +# +# The newer stack is recommended. +# +CONFIG_FIREWIRE=m +CONFIG_FIREWIRE_OHCI=m +CONFIG_FIREWIRE_OHCI_DEBUG=y +CONFIG_FIREWIRE_SBP2=m +# CONFIG_FIREWIRE_NET is not set +# CONFIG_IEEE1394 is not set +# CONFIG_I2O is not set +# CONFIG_MACINTOSH_DRIVERS 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_NET_SB1000 is not set +# CONFIG_ARCNET is not set +CONFIG_PHYLIB=m + +# +# 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_MICREL_PHY is not set +# CONFIG_MDIO_BITBANG is not set +CONFIG_NET_ETHERNET=y +CONFIG_MII=m +# CONFIG_HAPPYMEAL is not set +# CONFIG_SUNGEM is not set +# CONFIG_CASSINI is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_ETHOC is not set +# CONFIG_DNET is not set +# CONFIG_NET_TULIP is not set +# CONFIG_HP100 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_NET_PCI=y +# CONFIG_PCNET32 is not set +# CONFIG_AMD8111_ETH is not set +# CONFIG_ADAPTEC_STARFIRE is not set +# CONFIG_KSZ884X_PCI is not set +# CONFIG_B44 is not set +CONFIG_FORCEDETH=m +# CONFIG_E100 is not set +# CONFIG_FEALNX is not set +# CONFIG_NATSEMI is not set +# CONFIG_NE2K_PCI is not set +# CONFIG_8139CP is not set +# CONFIG_8139TOO is not set +# CONFIG_R6040 is not set +# CONFIG_SIS900 is not set +# CONFIG_EPIC100 is not set +# CONFIG_SMSC9420 is not set +# CONFIG_SUNDANCE is not set +# CONFIG_TLAN is not set +# CONFIG_KS8842 is not set +# CONFIG_KS8851_MLL is not set +# CONFIG_VIA_RHINE is not set +# CONFIG_SC92031 is not set +# CONFIG_ATL2 is not set +CONFIG_NETDEV_1000=y +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_E1000 is not set +# CONFIG_E1000E is not set +# CONFIG_IP1000 is not set +# CONFIG_IGB is not set +# CONFIG_IGBVF is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +CONFIG_R8169=m +# CONFIG_SIS190 is not set +# CONFIG_SKGE is not set +# CONFIG_SKY2 is not set +# CONFIG_VIA_VELOCITY is not set +CONFIG_TIGON3=m +# CONFIG_BNX2 is not set +# CONFIG_CNIC is not set +# CONFIG_QLA3XXX is not set +# CONFIG_ATL1 is not set +# CONFIG_ATL1E is not set +CONFIG_ATL1C=m +# CONFIG_JME is not set +# CONFIG_NETDEV_10000 is not set +# CONFIG_TR is not set +CONFIG_WLAN=y +# CONFIG_LIBERTAS_THINFIRM is not set +# CONFIG_AIRO is not set +# CONFIG_ATMEL is not set +# CONFIG_AT76C50X_USB is not set +# CONFIG_PRISM54 is not set +# CONFIG_USB_ZD1201 is not set +# CONFIG_USB_NET_RNDIS_WLAN is not set +# CONFIG_RTL8180 is not set +# CONFIG_RTL8187 is not set +# CONFIG_ADM8211 is not set +# CONFIG_MAC80211_HWSIM is not set +# CONFIG_MWL8K is not set +CONFIG_ATH_COMMON=m +# CONFIG_ATH_DEBUG is not set +CONFIG_ATH5K=m +# CONFIG_ATH5K_DEBUG is not set +CONFIG_ATH9K_HW=m +CONFIG_ATH9K_COMMON=m +CONFIG_ATH9K=m +# CONFIG_ATH9K_HTC is not set +# CONFIG_AR9170_USB is not set +# CONFIG_B43 is not set +# CONFIG_B43LEGACY is not set +# CONFIG_HOSTAP is not set +# CONFIG_IPW2100 is not set +# CONFIG_IPW2200 is not set +# CONFIG_IWLWIFI is not set +# CONFIG_LIBERTAS is not set +# CONFIG_HERMES is not set +# CONFIG_P54_COMMON is not set +# CONFIG_RT2X00 is not set +# CONFIG_WL12XX is not set +# CONFIG_ZD1211RW 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_USB_HSO is not set +# CONFIG_USB_IPHETH is not set +# CONFIG_WAN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_NET_FC is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_VMXNET3 is not set +# CONFIG_ISDN is not set +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y +CONFIG_INPUT_FF_MEMLESS=y +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# 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_ADP5588 is not set +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_KEYBOARD_QT2160 is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_TCA6416 is not set +# CONFIG_KEYBOARD_LM8323 is not set +# CONFIG_KEYBOARD_MAX7359 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=y +CONFIG_MOUSE_PS2=y +CONFIG_MOUSE_PS2_ALPS=y +CONFIG_MOUSE_PS2_LOGIPS2PP=y +CONFIG_MOUSE_PS2_SYNAPTICS=y +CONFIG_MOUSE_PS2_LIFEBOOK=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 is not set +# CONFIG_MOUSE_APPLETOUCH is not set +# CONFIG_MOUSE_BCM5974 is not set +# CONFIG_MOUSE_VSXXXAA is not set +# CONFIG_MOUSE_SYNAPTICS_I2C is not set +CONFIG_INPUT_JOYSTICK=y +# CONFIG_JOYSTICK_ANALOG is not set +# CONFIG_JOYSTICK_A3D is not set +# CONFIG_JOYSTICK_ADI is not set +# CONFIG_JOYSTICK_COBRA is not set +# CONFIG_JOYSTICK_GF2K is not set +# CONFIG_JOYSTICK_GRIP is not set +# CONFIG_JOYSTICK_GRIP_MP is not set +# CONFIG_JOYSTICK_GUILLEMOT is not set +# CONFIG_JOYSTICK_INTERACT is not set +# CONFIG_JOYSTICK_SIDEWINDER is not set +# CONFIG_JOYSTICK_TMDC is not set +# CONFIG_JOYSTICK_IFORCE is not set +# CONFIG_JOYSTICK_WARRIOR is not set +# CONFIG_JOYSTICK_MAGELLAN is not set +# CONFIG_JOYSTICK_SPACEORB is not set +# CONFIG_JOYSTICK_SPACEBALL is not set +# CONFIG_JOYSTICK_STINGER is not set +# CONFIG_JOYSTICK_TWIDJOY is not set +# CONFIG_JOYSTICK_ZHENHUA is not set +# CONFIG_JOYSTICK_JOYDUMP is not set +CONFIG_JOYSTICK_XPAD=m +CONFIG_JOYSTICK_XPAD_FF=y +CONFIG_JOYSTICK_XPAD_LEDS=y +CONFIG_INPUT_TABLET=y +# CONFIG_TABLET_USB_ACECAD is not set +# CONFIG_TABLET_USB_AIPTEK is not set +# CONFIG_TABLET_USB_GTCO is not set +# CONFIG_TABLET_USB_KBTAB is not set +CONFIG_TABLET_USB_WACOM=m +CONFIG_INPUT_TOUCHSCREEN=y +# CONFIG_TOUCHSCREEN_AD7879_I2C is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELO is not set +CONFIG_TOUCHSCREEN_WACOM_W8001=m +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_TPS6507X is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_AD714X is not set +# CONFIG_INPUT_APANEL is not set +# CONFIG_INPUT_WISTRON_BTNS is not set +# CONFIG_INPUT_ATLAS_BTNS is not set +CONFIG_INPUT_ATI_REMOTE=m +CONFIG_INPUT_ATI_REMOTE2=m +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_CM109 is not set +# CONFIG_INPUT_UINPUT is not set +# CONFIG_INPUT_WINBOND_CIR is not set +# CONFIG_INPUT_PCF8574 is not set + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +CONFIG_SERIO_I8042=y +# CONFIG_SERIO_SERPORT is not set +# CONFIG_SERIO_CT82C710 is not set +# CONFIG_SERIO_PCIPS2 is not set +CONFIG_SERIO_LIBPS2=y +# CONFIG_SERIO_RAW is not set +# CONFIG_SERIO_ALTERA_PS2 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=y +# CONFIG_DEVKMEM is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_N_GSM is not set +# CONFIG_NOZOMI is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +# CONFIG_SERIAL_8250_CONSOLE is not set +CONFIG_FIX_EARLYCON_MEM=y +CONFIG_SERIAL_8250_PCI=y +# CONFIG_SERIAL_8250_PNP is not set +CONFIG_SERIAL_8250_NR_UARTS=4 +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +# CONFIG_SERIAL_8250_EXTENDED is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +# CONFIG_SERIAL_JSM is not set +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +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_NVRAM=y +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set +# CONFIG_SONYPI is not set +# CONFIG_MWAVE is not set +# CONFIG_PC8736x_GPIO is not set +# CONFIG_NSC_GPIO is not set +# CONFIG_CS5535_GPIO is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_HPET is not set +# CONFIG_HANGCHECK_TIMER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_TELCLOCK is not set +CONFIG_DEVPORT=y +# CONFIG_RAMOOPS is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +# CONFIG_I2C_COMPAT is not set +CONFIG_I2C_CHARDEV=m +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# + +# +# PC SMBus host controller drivers +# +# CONFIG_I2C_ALI1535 is not set +# CONFIG_I2C_ALI1563 is not set +# CONFIG_I2C_ALI15X3 is not set +# CONFIG_I2C_AMD756 is not set +# CONFIG_I2C_AMD8111 is not set +# CONFIG_I2C_I801 is not set +# CONFIG_I2C_ISCH is not set +# CONFIG_I2C_PIIX4 is not set +CONFIG_I2C_NFORCE2=y +# CONFIG_I2C_NFORCE2_S4985 is not set +# CONFIG_I2C_SIS5595 is not set +# CONFIG_I2C_SIS630 is not set +# CONFIG_I2C_SIS96X is not set +# CONFIG_I2C_VIA is not set +# CONFIG_I2C_VIAPRO is not set + +# +# ACPI drivers +# +# CONFIG_I2C_SCMI is not set + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_STUB is not set +# CONFIG_SCx200_ACB is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_SPI is not set + +# +# PPS support +# +# CONFIG_PPS is not set +CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y +# CONFIG_GPIOLIB is not set +# CONFIG_W1 is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +# CONFIG_TEST_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=y +# CONFIG_HWMON_VID is not set +# CONFIG_HWMON_DEBUG_CHIP is not set + +# +# Native drivers +# +# CONFIG_SENSORS_ABITUGURU is not set +# CONFIG_SENSORS_ABITUGURU3 is not set +# CONFIG_SENSORS_AD7414 is not set +# CONFIG_SENSORS_AD7418 is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set +# CONFIG_SENSORS_ADM1029 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ADM9240 is not set +# CONFIG_SENSORS_ADT7411 is not set +# CONFIG_SENSORS_ADT7462 is not set +# CONFIG_SENSORS_ADT7470 is not set +# CONFIG_SENSORS_ADT7475 is not set +# CONFIG_SENSORS_ASC7621 is not set +# CONFIG_SENSORS_K8TEMP is not set +# CONFIG_SENSORS_K10TEMP is not set +# CONFIG_SENSORS_ASB100 is not set +# CONFIG_SENSORS_ATXP1 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_I5K_AMB is not set +# CONFIG_SENSORS_F71805F is not set +# CONFIG_SENSORS_F71882FG is not set +# CONFIG_SENSORS_F75375S is not set +# CONFIG_SENSORS_FSCHMD is not set +# CONFIG_SENSORS_G760A is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_GL520SM is not set +CONFIG_SENSORS_CORETEMP=m +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM73 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_LM92 is not set +# CONFIG_SENSORS_LM93 is not set +# CONFIG_SENSORS_LTC4215 is not set +# CONFIG_SENSORS_LTC4245 is not set +# CONFIG_SENSORS_LM95241 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_MAX6650 is not set +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_PC87427 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_SIS5595 is not set +# CONFIG_SENSORS_DME1737 is not set +# CONFIG_SENSORS_EMC1403 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47M192 is not set +# CONFIG_SENSORS_SMSC47B397 is not set +# CONFIG_SENSORS_ADS7828 is not set +# CONFIG_SENSORS_AMC6821 is not set +# CONFIG_SENSORS_THMC50 is not set +# CONFIG_SENSORS_TMP102 is not set +# CONFIG_SENSORS_TMP401 is not set +# CONFIG_SENSORS_TMP421 is not set +# CONFIG_SENSORS_VIA_CPUTEMP is not set +# CONFIG_SENSORS_VIA686A is not set +# CONFIG_SENSORS_VT1211 is not set +# CONFIG_SENSORS_VT8231 is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83791D is not set +# CONFIG_SENSORS_W83792D is not set +# CONFIG_SENSORS_W83793 is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83L786NG is not set +# CONFIG_SENSORS_W83627HF is not set +# CONFIG_SENSORS_W83627EHF is not set +# CONFIG_SENSORS_HDAPS is not set +# CONFIG_SENSORS_LIS3_I2C is not set +# CONFIG_SENSORS_APPLESMC is not set + +# +# ACPI drivers +# +# CONFIG_SENSORS_ATK0110 is not set +# CONFIG_SENSORS_LIS3LV02D is not set +CONFIG_THERMAL=y +CONFIG_THERMAL_HWMON=y +# CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +# CONFIG_MFD_SUPPORT is not set +# CONFIG_REGULATOR is not set +CONFIG_MEDIA_SUPPORT=m + +# +# Multimedia core support +# +# CONFIG_VIDEO_DEV is not set +# CONFIG_DVB_CORE is not set +# CONFIG_VIDEO_MEDIA is not set + +# +# Multimedia drivers +# +CONFIG_IR_CORE=m +CONFIG_VIDEO_IR=m +CONFIG_RC_MAP=m +CONFIG_IR_NEC_DECODER=m +CONFIG_IR_RC5_DECODER=m +CONFIG_IR_RC6_DECODER=m +CONFIG_IR_JVC_DECODER=m +CONFIG_IR_SONY_DECODER=m +CONFIG_IR_IMON=m +# CONFIG_DAB is not set + +# +# Graphics support +# +CONFIG_AGP=y +# CONFIG_AGP_ALI is not set +# CONFIG_AGP_ATI is not set +# CONFIG_AGP_AMD is not set +CONFIG_AGP_INTEL=y +# CONFIG_AGP_NVIDIA is not set +# CONFIG_AGP_SIS is not set +# CONFIG_AGP_SWORKS is not set +# CONFIG_AGP_VIA is not set +# CONFIG_AGP_EFFICEON is not set +CONFIG_VGA_ARB=y +CONFIG_VGA_ARB_MAX_GPUS=16 +# CONFIG_VGA_SWITCHEROO is not set +# CONFIG_DRM is not set +# CONFIG_VGASTATE is not set +CONFIG_VIDEO_OUTPUT_CONTROL=y +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +CONFIG_FB_BOOT_VESA_SUPPORT=y +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# 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_CIRRUS is not set +# CONFIG_FB_PM2 is not set +# CONFIG_FB_CYBER2000 is not set +# CONFIG_FB_ARC is not set +# CONFIG_FB_ASILIANT is not set +# CONFIG_FB_IMSTT is not set +# CONFIG_FB_VGA16 is not set +CONFIG_FB_VESA=y +# CONFIG_FB_N411 is not set +# CONFIG_FB_HGA is not set +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_NVIDIA is not set +# CONFIG_FB_RIVA is not set +# CONFIG_FB_I810 is not set +# CONFIG_FB_LE80578 is not set +# CONFIG_FB_INTEL is not set +# CONFIG_FB_MATROX is not set +# CONFIG_FB_RADEON is not set +# CONFIG_FB_ATY128 is not set +# CONFIG_FB_ATY is not set +# CONFIG_FB_S3 is not set +# CONFIG_FB_SAVAGE is not set +# CONFIG_FB_SIS is not set +# CONFIG_FB_VIA is not set +# CONFIG_FB_NEOMAGIC is not set +# CONFIG_FB_KYRO is not set +# CONFIG_FB_3DFX is not set +# CONFIG_FB_VOODOO1 is not set +# CONFIG_FB_VT8623 is not set +# CONFIG_FB_TRIDENT is not set +# CONFIG_FB_ARK is not set +# CONFIG_FB_PM3 is not set +# CONFIG_FB_CARMINE is not set +# CONFIG_FB_GEODE 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 is not set + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set + +# +# Console display driver support +# +CONFIG_VGA_CONSOLE=y +CONFIG_VGACON_SOFT_SCROLLBACK=y +CONFIG_VGACON_SOFT_SCROLLBACK_SIZE=256 +CONFIG_DUMMY_CONSOLE=y +# CONFIG_FRAMEBUFFER_CONSOLE is not set +# CONFIG_LOGO is not set +CONFIG_SOUND=m +# CONFIG_SOUND_OSS_CORE is not set +CONFIG_SND=m +CONFIG_SND_TIMER=m +CONFIG_SND_PCM=m +CONFIG_SND_HWDEP=m +CONFIG_SND_RAWMIDI=m +# CONFIG_SND_SEQUENCER is not set +# CONFIG_SND_MIXER_OSS is not set +# CONFIG_SND_PCM_OSS is not set +CONFIG_SND_HRTIMER=m +# CONFIG_SND_DYNAMIC_MINORS is not set +# CONFIG_SND_SUPPORT_OLD_API is not set +# CONFIG_SND_VERBOSE_PROCFS is not set +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set +CONFIG_SND_VMASTER=y +CONFIG_SND_DMA_SGBUF=y +# CONFIG_SND_RAWMIDI_SEQ is not set +# 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_DRIVERS is not set +CONFIG_SND_PCI=y +# CONFIG_SND_AD1889 is not set +# CONFIG_SND_ALS300 is not set +# CONFIG_SND_ALS4000 is not set +# CONFIG_SND_ALI5451 is not set +# CONFIG_SND_ASIHPI is not set +# CONFIG_SND_ATIIXP is not set +# CONFIG_SND_ATIIXP_MODEM is not set +# CONFIG_SND_AU8810 is not set +# CONFIG_SND_AU8820 is not set +# CONFIG_SND_AU8830 is not set +# CONFIG_SND_AW2 is not set +# CONFIG_SND_AZT3328 is not set +# CONFIG_SND_BT87X is not set +# CONFIG_SND_CA0106 is not set +# CONFIG_SND_CMIPCI is not set +# CONFIG_SND_OXYGEN is not set +# CONFIG_SND_CS4281 is not set +# CONFIG_SND_CS46XX is not set +# CONFIG_SND_CS5530 is not set +# CONFIG_SND_CS5535AUDIO is not set +# CONFIG_SND_CTXFI is not set +# CONFIG_SND_DARLA20 is not set +# CONFIG_SND_GINA20 is not set +# CONFIG_SND_LAYLA20 is not set +# CONFIG_SND_DARLA24 is not set +# CONFIG_SND_GINA24 is not set +# CONFIG_SND_LAYLA24 is not set +# CONFIG_SND_MONA is not set +# CONFIG_SND_MIA is not set +# CONFIG_SND_ECHO3G is not set +# CONFIG_SND_INDIGO is not set +# CONFIG_SND_INDIGOIO is not set +# CONFIG_SND_INDIGODJ is not set +# CONFIG_SND_INDIGOIOX is not set +# CONFIG_SND_INDIGODJX is not set +# CONFIG_SND_EMU10K1 is not set +# CONFIG_SND_EMU10K1X is not set +# CONFIG_SND_ENS1370 is not set +# CONFIG_SND_ENS1371 is not set +# CONFIG_SND_ES1938 is not set +# CONFIG_SND_ES1968 is not set +# CONFIG_SND_FM801 is not set +CONFIG_SND_HDA_INTEL=m +CONFIG_SND_HDA_HWDEP=y +# CONFIG_SND_HDA_RECONFIG is not set +# CONFIG_SND_HDA_INPUT_BEEP is not set +# CONFIG_SND_HDA_INPUT_JACK is not set +# CONFIG_SND_HDA_PATCH_LOADER is not set +CONFIG_SND_HDA_CODEC_REALTEK=y +# CONFIG_SND_HDA_CODEC_ANALOG is not set +# CONFIG_SND_HDA_CODEC_SIGMATEL is not set +CONFIG_SND_HDA_CODEC_VIA=y +# CONFIG_SND_HDA_CODEC_ATIHDMI is not set +CONFIG_SND_HDA_CODEC_NVHDMI=y +# CONFIG_SND_HDA_CODEC_INTELHDMI is not set +CONFIG_SND_HDA_ELD=y +# CONFIG_SND_HDA_CODEC_CIRRUS is not set +CONFIG_SND_HDA_CODEC_CONEXANT=y +# CONFIG_SND_HDA_CODEC_CA0110 is not set +# CONFIG_SND_HDA_CODEC_CMEDIA is not set +# CONFIG_SND_HDA_CODEC_SI3054 is not set +CONFIG_SND_HDA_GENERIC=y +CONFIG_SND_HDA_POWER_SAVE=y +CONFIG_SND_HDA_POWER_SAVE_DEFAULT=5 +# CONFIG_SND_HDSP is not set +# CONFIG_SND_HDSPM is not set +# CONFIG_SND_HIFIER is not set +# CONFIG_SND_ICE1712 is not set +# CONFIG_SND_ICE1724 is not set +# CONFIG_SND_INTEL8X0 is not set +# CONFIG_SND_INTEL8X0M is not set +# CONFIG_SND_KORG1212 is not set +# CONFIG_SND_LX6464ES is not set +# CONFIG_SND_MAESTRO3 is not set +# CONFIG_SND_MIXART is not set +# CONFIG_SND_NM256 is not set +# CONFIG_SND_PCXHR is not set +# CONFIG_SND_RIPTIDE is not set +# CONFIG_SND_RME32 is not set +# CONFIG_SND_RME96 is not set +# CONFIG_SND_RME9652 is not set +# CONFIG_SND_SIS7019 is not set +# CONFIG_SND_SONICVIBES is not set +# CONFIG_SND_TRIDENT is not set +# CONFIG_SND_VIA82XX is not set +# CONFIG_SND_VIA82XX_MODEM is not set +# CONFIG_SND_VIRTUOSO is not set +# CONFIG_SND_VX222 is not set +# CONFIG_SND_YMFPCI is not set +CONFIG_SND_USB=y +CONFIG_SND_USB_AUDIO=m +# CONFIG_SND_USB_UA101 is not set +# CONFIG_SND_USB_USX2Y is not set +# CONFIG_SND_USB_CAIAQ is not set +# CONFIG_SND_USB_US122L is not set +# CONFIG_SND_SOC is not set +# CONFIG_SOUND_PRIME is not set +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_3M_PCT is not set +# CONFIG_HID_A4TECH is not set +# CONFIG_HID_APPLE is not set +# CONFIG_HID_BELKIN is not set +# CONFIG_HID_CANDO is not set +# CONFIG_HID_CHERRY is not set +# CONFIG_HID_CHICONY is not set +# CONFIG_HID_PRODIKEYS is not set +# CONFIG_HID_CYPRESS is not set +# CONFIG_HID_DRAGONRISE is not set +# CONFIG_HID_EGALAX 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=y +CONFIG_LOGITECH_FF=y +CONFIG_LOGIRUMBLEPAD2_FF=y +CONFIG_LOGIG940_FF=y +# CONFIG_HID_MAGICMOUSE is not set +CONFIG_HID_MICROSOFT=y +# CONFIG_HID_MOSART is not set +# CONFIG_HID_MONTEREY is not set +# CONFIG_HID_NTRIG is not set +# CONFIG_HID_ORTEK is not set +# CONFIG_HID_PANTHERLORD is not set +# CONFIG_HID_PETALYNX is not set +# CONFIG_HID_PICOLCD is not set +# CONFIG_HID_QUANTA is not set +# CONFIG_HID_ROCCAT is not set +# CONFIG_HID_ROCCAT_KONE is not set +# CONFIG_HID_SAMSUNG is not set +# CONFIG_HID_SONY is not set +# CONFIG_HID_STANTUM 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_WACOM is not set +# CONFIG_HID_ZEROPLUS is not set +# CONFIG_HID_ZYDACRON is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y +CONFIG_USB_ARCH_HAS_EHCI=y +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=y +# CONFIG_USB_DYNAMIC_MINORS is not set +CONFIG_USB_SUSPEND=y +# CONFIG_USB_OTG is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_MON is not set +# 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_XHCI_HCD is not set +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +# CONFIG_USB_EHCI_TT_NEWSCHED 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_UHCI_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_WHCI_HCD is not set +# CONFIG_USB_HWA_HCD 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=m +# CONFIG_USB_EZUSB is not set +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_AIRCABLE is not set +# CONFIG_USB_SERIAL_ARK3116 is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_CH341 is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_CP210X is not set +# CONFIG_USB_SERIAL_CYPRESS_M8 is not set +# CONFIG_USB_SERIAL_EMPEG is not set +CONFIG_USB_SERIAL_FTDI_SIO=m +# CONFIG_USB_SERIAL_FUNSOFT is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_IPAQ is not set +# CONFIG_USB_SERIAL_IR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_EDGEPORT_TI is not set +# CONFIG_USB_SERIAL_GARMIN is not set +# CONFIG_USB_SERIAL_IPW is not set +# CONFIG_USB_SERIAL_IUU is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KLSI is not set +# CONFIG_USB_SERIAL_KOBIL_SCT is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_MOS7720 is not set +# CONFIG_USB_SERIAL_MOS7840 is not set +# CONFIG_USB_SERIAL_MOTOROLA is not set +# CONFIG_USB_SERIAL_NAVMAN is not set +# CONFIG_USB_SERIAL_PL2303 is not set +# CONFIG_USB_SERIAL_OTI6858 is not set +# CONFIG_USB_SERIAL_QCAUX is not set +# CONFIG_USB_SERIAL_QUALCOMM is not set +# CONFIG_USB_SERIAL_SPCP8X5 is not set +# CONFIG_USB_SERIAL_HP4X is not set +# CONFIG_USB_SERIAL_SAFE is not set +# CONFIG_USB_SERIAL_SIEMENS_MPI is not set +# CONFIG_USB_SERIAL_SIERRAWIRELESS is not set +# CONFIG_USB_SERIAL_SYMBOL is not set +# CONFIG_USB_SERIAL_TI is not set +# CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_XIRCOM is not set +# CONFIG_USB_SERIAL_OPTION is not set +# CONFIG_USB_SERIAL_OMNINET is not set +# CONFIG_USB_SERIAL_OPTICON is not set +# CONFIG_USB_SERIAL_VIVOPAY_SERIAL is not set +# CONFIG_USB_SERIAL_ZIO is not set +# CONFIG_USB_SERIAL_DEBUG 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_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_SISUSBVGA 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_GADGET is not set + +# +# OTG and related infrastructure +# +# CONFIG_NOP_USB_XCEIV is not set +# CONFIG_UWB is not set +# CONFIG_MMC is not set +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_ALIX2 is not set +# CONFIG_LEDS_PCA9532 is not set +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_CLEVO_MAIL is not set +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_BD2802 is not set +# CONFIG_LEDS_INTEL_SS4200 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +# CONFIG_LEDS_TRIGGER_TIMER is not set +# CONFIG_LEDS_TRIGGER_IDE_DISK is not set +# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set +# CONFIG_LEDS_TRIGGER_BACKLIGHT 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_INFINIBAND is not set +# CONFIG_EDAC is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# 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_BQ32K 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 +# + +# +# 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_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set +# CONFIG_STAGING is not set +# CONFIG_X86_PLATFORM_DEVICES is not set + +# +# Firmware Drivers +# +# CONFIG_EDD is not set +CONFIG_FIRMWARE_MEMMAP=y +# CONFIG_DELL_RBU is not set +# CONFIG_DCDBAS is not set +CONFIG_DMIID=y +# CONFIG_ISCSI_IBFT_FIND 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=y +# 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 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=m +# CONFIG_FUSE_FS is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=y +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +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="ascii" +CONFIG_NTFS_FS=m +# CONFIG_NTFS_DEBUG is not set +CONFIG_NTFS_RW=y + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLBFS 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_UNION_FS=y +# CONFIG_UNION_FS_XATTR is not set +# CONFIG_UNION_FS_DEBUG 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_LOGFS is not set +CONFIG_CRAMFS=y +CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_XATTRS is not set +CONFIG_SQUASHFS_LZMA=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=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +CONFIG_NFS_V4=y +# CONFIG_NFS_V4_1 is not set +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_EXPORTFS=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +CONFIG_SUNRPC_GSS=y +CONFIG_RPCSEC_GSS_KRB5=y +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CEPH_FS is not set +CONFIG_CIFS=y +CONFIG_CIFS_STATS=y +CONFIG_CIFS_STATS2=y +# CONFIG_CIFS_WEAK_PW_HASH is not set +# CONFIG_CIFS_XATTR is not set +# CONFIG_CIFS_DEBUG2 is not set +# CONFIG_CIFS_EXPERIMENTAL 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=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +CONFIG_MAC_PARTITION=y +CONFIG_MSDOS_PARTITION=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +CONFIG_LDM_PARTITION=y +# CONFIG_LDM_DEBUG is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_KARMA_PARTITION is not set +CONFIG_EFI_PARTITION=y +# CONFIG_SYSV68_PARTITION is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="utf8" +CONFIG_NLS_CODEPAGE_437=y +# 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=y +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 is not set +# 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_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_PRINTK_TIME=y +# CONFIG_ENABLE_WARN_DEPRECATED is not set +# CONFIG_ENABLE_MUST_CHECK is not set +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_MEMORY_INIT is not set +CONFIG_ARCH_WANT_FRAME_POINTERS=y +# CONFIG_FRAME_POINTER is not set +# CONFIG_RCU_CPU_STALL_DETECTOR is not set +# CONFIG_LATENCYTOP is not set +CONFIG_SYSCTL_SYSCALL_CHECK=y +CONFIG_USER_STACKTRACE_SUPPORT=y +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_FP_TEST=y +CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_SYSCALL_TRACEPOINTS=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set +# CONFIG_PROVIDE_OHCI1394_DMA_INIT is not set +# CONFIG_FIREWIRE_OHCI_REMOTE_DMA is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +CONFIG_HAVE_ARCH_KMEMCHECK=y +CONFIG_STRICT_DEVMEM=y +# CONFIG_X86_VERBOSE_BOOTUP is not set +# CONFIG_EARLY_PRINTK is not set +# CONFIG_4KSTACKS is not set +CONFIG_DOUBLEFAULT=y +# CONFIG_IOMMU_STRESS is not set +CONFIG_HAVE_MMIOTRACE_SUPPORT=y +CONFIG_IO_DELAY_TYPE_0X80=0 +CONFIG_IO_DELAY_TYPE_0XED=1 +CONFIG_IO_DELAY_TYPE_UDELAY=2 +CONFIG_IO_DELAY_TYPE_NONE=3 +CONFIG_IO_DELAY_0X80=y +# CONFIG_IO_DELAY_0XED is not set +# CONFIG_IO_DELAY_UDELAY is not set +# CONFIG_IO_DELAY_NONE is not set +CONFIG_DEFAULT_IO_DELAY_TYPE=0 +CONFIG_OPTIMIZE_INLINING=y + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +# CONFIG_INTEL_TXT is not set +# CONFIG_DEFAULT_SECURITY_SELINUX is not set +# CONFIG_DEFAULT_SECURITY_SMACK is not set +# CONFIG_DEFAULT_SECURITY_TOMOYO is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +CONFIG_CRYPTO_MANAGER_TESTS=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_PCRYPT is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +CONFIG_CRYPTO_ECB=y +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_CRC32C_INTEL is not set +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_AES_586 is not set +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SALSA20_586 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set +# CONFIG_CRYPTO_TWOFISH_586 is not set + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_HW is not set +CONFIG_HAVE_KVM=y +# CONFIG_VIRTUALIZATION is not set +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_FIND_FIRST_BIT=y +CONFIG_GENERIC_FIND_NEXT_BIT=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_LZO_DECOMPRESS=y +CONFIG_DECOMPRESS_GZIP=y +CONFIG_DECOMPRESS_BZIP2=y +CONFIG_DECOMPRESS_LZMA=y +CONFIG_DECOMPRESS_LZO=y +CONFIG_DECOMPRESS_LZMA_NEEDED=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_NLATTR=y diff --git a/main/linux-xbmc/linux-2.6.35-squashfs_lzma.patch b/main/linux-xbmc/linux-2.6.35-squashfs_lzma.patch new file mode 100644 index 000000000..7e7d7835f --- /dev/null +++ b/main/linux-xbmc/linux-2.6.35-squashfs_lzma.patch @@ -0,0 +1,681 @@ +diff -Naur linux-2.6.35-rc6/fs/squashfs/decompressor.c linux-2.6.35-rc6.patch/fs/squashfs/decompressor.c +--- linux-2.6.35-rc6/fs/squashfs/decompressor.c 2010-07-22 21:13:38.000000000 +0200 ++++ linux-2.6.35-rc6.patch/fs/squashfs/decompressor.c 2010-07-29 06:51:11.797408264 +0200 +@@ -50,7 +50,11 @@ + + static const struct squashfs_decompressor *decompressor[] = { + &squashfs_zlib_comp_ops, ++#ifdef CONFIG_SQUASHFS_LZMA ++ &squashfs_lzma_comp_ops, ++#else + &squashfs_lzma_unsupported_comp_ops, ++#endif + &squashfs_lzo_unsupported_comp_ops, + &squashfs_unknown_comp_ops + }; +diff -Naur linux-2.6.35-rc6/fs/squashfs/Kconfig linux-2.6.35-rc6.patch/fs/squashfs/Kconfig +--- linux-2.6.35-rc6/fs/squashfs/Kconfig 2010-07-22 21:13:38.000000000 +0200 ++++ linux-2.6.35-rc6.patch/fs/squashfs/Kconfig 2010-07-29 06:51:11.798406430 +0200 +@@ -37,6 +37,12 @@ + + If unsure, say N. + ++config SQUASHFS_LZMA ++ bool "Include support for LZMA compressed file systems" ++ depends on SQUASHFS ++ select DECOMPRESS_LZMA ++ select DECOMPRESS_LZMA_NEEDED ++ + config SQUASHFS_EMBEDDED + + bool "Additional option for memory-constrained systems" +diff -Naur linux-2.6.35-rc6/fs/squashfs/lzma_wrapper.c linux-2.6.35-rc6.patch/fs/squashfs/lzma_wrapper.c +--- linux-2.6.35-rc6/fs/squashfs/lzma_wrapper.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.35-rc6.patch/fs/squashfs/lzma_wrapper.c 2010-07-29 06:51:12.257251312 +0200 +@@ -0,0 +1,152 @@ ++/* ++ * Squashfs - a compressed read only filesystem for Linux ++ * ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 ++ * Phillip Lougher <phillip@lougher.demon.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ * ++ * lzma_wrapper.c ++ */ ++ ++#include <asm/unaligned.h> ++#include <linux/slab.h> ++#include <linux/buffer_head.h> ++#include <linux/mutex.h> ++#include <linux/vmalloc.h> ++#include <linux/decompress/unlzma.h> ++ ++#include "squashfs_fs.h" ++#include "squashfs_fs_sb.h" ++#include "squashfs_fs_i.h" ++#include "squashfs.h" ++#include "decompressor.h" ++ ++struct squashfs_lzma { ++ void *input; ++ void *output; ++}; ++ ++/* decompress_unlzma.c is currently non re-entrant... */ ++DEFINE_MUTEX(lzma_mutex); ++ ++/* decompress_unlzma.c doesn't provide any context in its callbacks... */ ++static int lzma_error; ++ ++static void error(char *m) ++{ ++ ERROR("unlzma error: %s\n", m); ++ lzma_error = 1; ++} ++ ++ ++static void *lzma_init(struct squashfs_sb_info *msblk) ++{ ++ struct squashfs_lzma *stream = kzalloc(sizeof(*stream), GFP_KERNEL); ++ if (stream == NULL) ++ goto failed; ++ stream->input = vmalloc(msblk->block_size); ++ if (stream->input == NULL) ++ goto failed; ++ stream->output = vmalloc(msblk->block_size); ++ if (stream->output == NULL) ++ goto failed2; ++ ++ return stream; ++ ++failed2: ++ vfree(stream->input); ++failed: ++ ERROR("failed to allocate lzma workspace\n"); ++ kfree(stream); ++ return NULL; ++} ++ ++ ++static void lzma_free(void *strm) ++{ ++ struct squashfs_lzma *stream = strm; ++ ++ if (stream) { ++ vfree(stream->input); ++ vfree(stream->output); ++ } ++ kfree(stream); ++} ++ ++ ++static int lzma_uncompress(struct squashfs_sb_info *msblk, void **buffer, ++ struct buffer_head **bh, int b, int offset, int length, int srclength, ++ int pages) ++{ ++ struct squashfs_lzma *stream = msblk->stream; ++ void *buff = stream->input; ++ int avail, i, bytes = length, res; ++ ++ mutex_lock(&lzma_mutex); ++ ++ for (i = 0; i < b; i++) { ++ wait_on_buffer(bh[i]); ++ if (!buffer_uptodate(bh[i])) ++ goto block_release; ++ ++ avail = min(bytes, msblk->devblksize - offset); ++ memcpy(buff, bh[i]->b_data + offset, avail); ++ buff += avail; ++ bytes -= avail; ++ offset = 0; ++ put_bh(bh[i]); ++ } ++ ++ lzma_error = 0; ++ res = unlzma(stream->input, length, NULL, NULL, stream->output, NULL, ++ error); ++ if (res || lzma_error) ++ goto failed; ++ ++ /* uncompressed size is stored in the LZMA header (5 byte offset) */ ++ res = bytes = get_unaligned_le32(stream->input + 5); ++ for (i = 0, buff = stream->output; bytes && i < pages; i++) { ++ avail = min_t(int, bytes, PAGE_CACHE_SIZE); ++ memcpy(buffer[i], buff, avail); ++ buff += avail; ++ bytes -= avail; ++ } ++ if (bytes) ++ goto failed; ++ ++ mutex_unlock(&lzma_mutex); ++ return res; ++ ++block_release: ++ for (; i < b; i++) ++ put_bh(bh[i]); ++ ++failed: ++ mutex_unlock(&lzma_mutex); ++ ++ ERROR("lzma decompression failed, data probably corrupt\n"); ++ return -EIO; ++} ++ ++const struct squashfs_decompressor squashfs_lzma_comp_ops = { ++ .init = lzma_init, ++ .free = lzma_free, ++ .decompress = lzma_uncompress, ++ .id = LZMA_COMPRESSION, ++ .name = "lzma", ++ .supported = 1 ++}; ++ +diff -Naur linux-2.6.35-rc6/fs/squashfs/Makefile linux-2.6.35-rc6.patch/fs/squashfs/Makefile +--- linux-2.6.35-rc6/fs/squashfs/Makefile 2010-07-22 21:13:38.000000000 +0200 ++++ linux-2.6.35-rc6.patch/fs/squashfs/Makefile 2010-07-29 06:52:22.842250915 +0200 +@@ -5,5 +5,6 @@ + obj-$(CONFIG_SQUASHFS) += squashfs.o + squashfs-y += block.o cache.o dir.o export.o file.o fragment.o id.o inode.o + squashfs-y += namei.o super.o symlink.o zlib_wrapper.o decompressor.o ++squashfs-$(CONFIG_SQUASHFS_LZMA) += lzma_wrapper.o + squashfs-$(CONFIG_SQUASHFS_XATTRS) += xattr.o xattr_id.o + +diff -Naur linux-2.6.35-rc6/fs/squashfs/squashfs.h linux-2.6.35-rc6.patch/fs/squashfs/squashfs.h +--- linux-2.6.35-rc6/fs/squashfs/squashfs.h 2010-07-22 21:13:38.000000000 +0200 ++++ linux-2.6.35-rc6.patch/fs/squashfs/squashfs.h 2010-07-29 06:51:12.273377004 +0200 +@@ -104,3 +104,6 @@ + + /* zlib_wrapper.c */ + extern const struct squashfs_decompressor squashfs_zlib_comp_ops; ++ ++/* lzma wrapper.c */ ++extern const struct squashfs_decompressor squashfs_lzma_comp_ops; +diff -Naur linux-2.6.35-rc6/include/linux/decompress/bunzip2_mm.h linux-2.6.35-rc6.patch/include/linux/decompress/bunzip2_mm.h +--- linux-2.6.35-rc6/include/linux/decompress/bunzip2_mm.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.35-rc6.patch/include/linux/decompress/bunzip2_mm.h 2010-07-29 06:51:12.274376635 +0200 +@@ -0,0 +1,13 @@ ++#ifndef BUNZIP2_MM_H ++#define BUNZIP2_MM_H ++ ++#ifdef STATIC ++/* Code active when included from pre-boot environment: */ ++#define INIT ++#else ++/* Compile for initramfs/initrd code only */ ++#define INIT __init ++static void(*error)(char *m); ++#endif ++ ++#endif +diff -Naur linux-2.6.35-rc6/include/linux/decompress/inflate_mm.h linux-2.6.35-rc6.patch/include/linux/decompress/inflate_mm.h +--- linux-2.6.35-rc6/include/linux/decompress/inflate_mm.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.35-rc6.patch/include/linux/decompress/inflate_mm.h 2010-07-29 06:51:12.275376896 +0200 +@@ -0,0 +1,13 @@ ++#ifndef INFLATE_MM_H ++#define INFLATE_MM_H ++ ++#ifdef STATIC ++/* Code active when included from pre-boot environment: */ ++#define INIT ++#else ++/* Compile for initramfs/initrd code only */ ++#define INIT __init ++static void(*error)(char *m); ++#endif ++ ++#endif +diff -Naur linux-2.6.35-rc6/include/linux/decompress/mm.h linux-2.6.35-rc6.patch/include/linux/decompress/mm.h +--- linux-2.6.35-rc6/include/linux/decompress/mm.h 2010-07-22 21:13:38.000000000 +0200 ++++ linux-2.6.35-rc6.patch/include/linux/decompress/mm.h 2010-07-29 06:51:12.276375899 +0200 +@@ -63,8 +63,6 @@ + + #define set_error_fn(x) + +-#define INIT +- + #else /* STATIC */ + + /* Code active when compiled standalone for use when loading ramdisk: */ +@@ -84,10 +82,8 @@ + #define large_malloc(a) vmalloc(a) + #define large_free(a) vfree(a) + +-static void(*error)(char *m); + #define set_error_fn(x) error = x; + +-#define INIT __init + #define STATIC + + #include <linux/init.h> +diff -Naur linux-2.6.35-rc6/include/linux/decompress/unlzma_mm.h linux-2.6.35-rc6.patch/include/linux/decompress/unlzma_mm.h +--- linux-2.6.35-rc6/include/linux/decompress/unlzma_mm.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.35-rc6.patch/include/linux/decompress/unlzma_mm.h 2010-07-29 06:51:12.277376579 +0200 +@@ -0,0 +1,20 @@ ++#ifndef UNLZMA_MM_H ++#define UNLZMA_MM_H ++ ++#ifdef STATIC ++ ++/* Code active when included from pre-boot environment: */ ++#define INIT ++ ++#elif defined(CONFIG_DECOMPRESS_LZMA_NEEDED) ++ ++/* Make it available to non initramfs/initrd code */ ++#define INIT ++#include <linux/module.h> ++#else ++ ++/* Compile for initramfs/initrd code only */ ++#define INIT __init ++#endif ++ ++#endif +diff -Naur linux-2.6.35-rc6/include/linux/decompress/unlzo_mm.h linux-2.6.35-rc6.patch/include/linux/decompress/unlzo_mm.h +--- linux-2.6.35-rc6/include/linux/decompress/unlzo_mm.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.35-rc6.patch/include/linux/decompress/unlzo_mm.h 2010-07-29 06:51:12.278376490 +0200 +@@ -0,0 +1,13 @@ ++#ifndef UNLZO_MM_H ++#define UNLZO_MM_H ++ ++#ifdef STATIC ++/* Code active when included from pre-boot environment: */ ++#define INIT ++#else ++/* Compile for initramfs/initrd code only */ ++#define INIT __init ++static void(*error)(char *m); ++#endif ++ ++#endif +diff -Naur linux-2.6.35-rc6/lib/decompress_bunzip2.c linux-2.6.35-rc6.patch/lib/decompress_bunzip2.c +--- linux-2.6.35-rc6/lib/decompress_bunzip2.c 2010-07-22 21:13:38.000000000 +0200 ++++ linux-2.6.35-rc6.patch/lib/decompress_bunzip2.c 2010-07-29 06:51:12.315251128 +0200 +@@ -52,6 +52,7 @@ + #include <linux/slab.h> + #endif /* STATIC */ + ++#include <linux/decompress/bunzip2_mm.h> + #include <linux/decompress/mm.h> + + #ifndef INT_MAX +diff -Naur linux-2.6.35-rc6/lib/decompress_inflate.c linux-2.6.35-rc6.patch/lib/decompress_inflate.c +--- linux-2.6.35-rc6/lib/decompress_inflate.c 2010-07-22 21:13:38.000000000 +0200 ++++ linux-2.6.35-rc6.patch/lib/decompress_inflate.c 2010-07-29 06:51:12.321251434 +0200 +@@ -23,6 +23,7 @@ + + #endif /* STATIC */ + ++#include <linux/decompress/inflate_mm.h> + #include <linux/decompress/mm.h> + + #define GZIP_IOBUF_SIZE (16*1024) +diff -Naur linux-2.6.35-rc6/lib/decompress_unlzma.c linux-2.6.35-rc6.patch/lib/decompress_unlzma.c +--- linux-2.6.35-rc6/lib/decompress_unlzma.c 2010-07-22 21:13:38.000000000 +0200 ++++ linux-2.6.35-rc6.patch/lib/decompress_unlzma.c 2010-07-29 06:51:12.334286250 +0200 +@@ -36,6 +36,7 @@ + #include <linux/slab.h> + #endif /* STATIC */ + ++#include <linux/decompress/unlzma_mm.h> + #include <linux/decompress/mm.h> + + #define MIN(a, b) (((a) < (b)) ? (a) : (b)) +@@ -88,7 +89,7 @@ + } + + /* Called twice: once at startup and once in rc_normalize() */ +-static void INIT rc_read(struct rc *rc) ++static void INIT rc_read(struct rc *rc, void(*error)(char *x)) + { + rc->buffer_size = rc->fill((char *)rc->buffer, LZMA_IOBUF_SIZE); + if (rc->buffer_size <= 0) +@@ -115,13 +116,13 @@ + rc->range = 0xFFFFFFFF; + } + +-static inline void INIT rc_init_code(struct rc *rc) ++static inline void INIT rc_init_code(struct rc *rc, void(*error)(char *x)) + { + int i; + + for (i = 0; i < 5; i++) { + if (rc->ptr >= rc->buffer_end) +- rc_read(rc); ++ rc_read(rc, error); + rc->code = (rc->code << 8) | *rc->ptr++; + } + } +@@ -134,32 +135,33 @@ + } + + /* Called twice, but one callsite is in inline'd rc_is_bit_0_helper() */ +-static void INIT rc_do_normalize(struct rc *rc) ++static void INIT rc_do_normalize(struct rc *rc, void(*error)(char *x)) + { + if (rc->ptr >= rc->buffer_end) +- rc_read(rc); ++ rc_read(rc, error); + rc->range <<= 8; + rc->code = (rc->code << 8) | *rc->ptr++; + } +-static inline void INIT rc_normalize(struct rc *rc) ++static inline void INIT rc_normalize(struct rc *rc, void(*error)(char *x)) + { + if (rc->range < (1 << RC_TOP_BITS)) +- rc_do_normalize(rc); ++ rc_do_normalize(rc, error); + } + + /* Called 9 times */ + /* Why rc_is_bit_0_helper exists? + *Because we want to always expose (rc->code < rc->bound) to optimizer + */ +-static inline uint32_t INIT rc_is_bit_0_helper(struct rc *rc, uint16_t *p) ++static inline uint32_t INIT rc_is_bit_0_helper(struct rc *rc, uint16_t *p, ++ void (*error)(char *x)) + { +- rc_normalize(rc); ++ rc_normalize(rc, error); + rc->bound = *p * (rc->range >> RC_MODEL_TOTAL_BITS); + return rc->bound; + } +-static inline int INIT rc_is_bit_0(struct rc *rc, uint16_t *p) ++static inline int INIT rc_is_bit_0(struct rc *rc, uint16_t *p, void(*error)(char *x)) + { +- uint32_t t = rc_is_bit_0_helper(rc, p); ++ uint32_t t = rc_is_bit_0_helper(rc, p, error); + return rc->code < t; + } + +@@ -177,9 +179,9 @@ + } + + /* Called 4 times in unlzma loop */ +-static int INIT rc_get_bit(struct rc *rc, uint16_t *p, int *symbol) ++static int INIT rc_get_bit(struct rc *rc, uint16_t *p, int *symbol, void(*error)(char *x)) + { +- if (rc_is_bit_0(rc, p)) { ++ if (rc_is_bit_0(rc, p, error)) { + rc_update_bit_0(rc, p); + *symbol *= 2; + return 0; +@@ -191,9 +193,9 @@ + } + + /* Called once */ +-static inline int INIT rc_direct_bit(struct rc *rc) ++static inline int INIT rc_direct_bit(struct rc *rc , void(*error)(char *x)) + { +- rc_normalize(rc); ++ rc_normalize(rc, error); + rc->range >>= 1; + if (rc->code >= rc->range) { + rc->code -= rc->range; +@@ -204,13 +206,14 @@ + + /* Called twice */ + static inline void INIT +-rc_bit_tree_decode(struct rc *rc, uint16_t *p, int num_levels, int *symbol) ++rc_bit_tree_decode(struct rc *rc, uint16_t *p, int num_levels, int *symbol, ++ void(*error)(char *x)) + { + int i = num_levels; + + *symbol = 1; + while (i--) +- rc_get_bit(rc, p + *symbol, symbol); ++ rc_get_bit(rc, p + *symbol, symbol, error); + *symbol -= 1 << num_levels; + } + +@@ -347,7 +350,8 @@ + static inline void INIT process_bit0(struct writer *wr, struct rc *rc, + struct cstate *cst, uint16_t *p, + int pos_state, uint16_t *prob, +- int lc, uint32_t literal_pos_mask) { ++ int lc, uint32_t literal_pos_mask, ++ void(*error)(char *x)) { + int mi = 1; + rc_update_bit_0(rc, prob); + prob = (p + LZMA_LITERAL + +@@ -365,7 +369,7 @@ + match_byte <<= 1; + bit = match_byte & 0x100; + prob_lit = prob + 0x100 + bit + mi; +- if (rc_get_bit(rc, prob_lit, &mi)) { ++ if (rc_get_bit(rc, prob_lit, &mi, error)) { + if (!bit) + break; + } else { +@@ -376,7 +380,7 @@ + } + while (mi < 0x100) { + uint16_t *prob_lit = prob + mi; +- rc_get_bit(rc, prob_lit, &mi); ++ rc_get_bit(rc, prob_lit, &mi, error); + } + write_byte(wr, mi); + if (cst->state < 4) +@@ -389,7 +393,8 @@ + + static inline void INIT process_bit1(struct writer *wr, struct rc *rc, + struct cstate *cst, uint16_t *p, +- int pos_state, uint16_t *prob) { ++ int pos_state, uint16_t *prob, ++ void(*error)(char *x)) { + int offset; + uint16_t *prob_len; + int num_bits; +@@ -397,7 +402,7 @@ + + rc_update_bit_1(rc, prob); + prob = p + LZMA_IS_REP + cst->state; +- if (rc_is_bit_0(rc, prob)) { ++ if (rc_is_bit_0(rc, prob, error)) { + rc_update_bit_0(rc, prob); + cst->rep3 = cst->rep2; + cst->rep2 = cst->rep1; +@@ -407,13 +412,13 @@ + } else { + rc_update_bit_1(rc, prob); + prob = p + LZMA_IS_REP_G0 + cst->state; +- if (rc_is_bit_0(rc, prob)) { ++ if (rc_is_bit_0(rc, prob, error)) { + rc_update_bit_0(rc, prob); + prob = (p + LZMA_IS_REP_0_LONG + + (cst->state << + LZMA_NUM_POS_BITS_MAX) + + pos_state); +- if (rc_is_bit_0(rc, prob)) { ++ if (rc_is_bit_0(rc, prob, error)) { + rc_update_bit_0(rc, prob); + + cst->state = cst->state < LZMA_NUM_LIT_STATES ? +@@ -428,13 +433,13 @@ + + rc_update_bit_1(rc, prob); + prob = p + LZMA_IS_REP_G1 + cst->state; +- if (rc_is_bit_0(rc, prob)) { ++ if (rc_is_bit_0(rc, prob, error)) { + rc_update_bit_0(rc, prob); + distance = cst->rep1; + } else { + rc_update_bit_1(rc, prob); + prob = p + LZMA_IS_REP_G2 + cst->state; +- if (rc_is_bit_0(rc, prob)) { ++ if (rc_is_bit_0(rc, prob, error)) { + rc_update_bit_0(rc, prob); + distance = cst->rep2; + } else { +@@ -452,7 +457,7 @@ + } + + prob_len = prob + LZMA_LEN_CHOICE; +- if (rc_is_bit_0(rc, prob_len)) { ++ if (rc_is_bit_0(rc, prob_len, error)) { + rc_update_bit_0(rc, prob_len); + prob_len = (prob + LZMA_LEN_LOW + + (pos_state << +@@ -462,7 +467,7 @@ + } else { + rc_update_bit_1(rc, prob_len); + prob_len = prob + LZMA_LEN_CHOICE_2; +- if (rc_is_bit_0(rc, prob_len)) { ++ if (rc_is_bit_0(rc, prob_len, error)) { + rc_update_bit_0(rc, prob_len); + prob_len = (prob + LZMA_LEN_MID + + (pos_state << +@@ -478,7 +483,7 @@ + } + } + +- rc_bit_tree_decode(rc, prob_len, num_bits, &len); ++ rc_bit_tree_decode(rc, prob_len, num_bits, &len, error); + len += offset; + + if (cst->state < 4) { +@@ -493,7 +498,7 @@ + << LZMA_NUM_POS_SLOT_BITS); + rc_bit_tree_decode(rc, prob, + LZMA_NUM_POS_SLOT_BITS, +- &pos_slot); ++ &pos_slot, error); + if (pos_slot >= LZMA_START_POS_MODEL_INDEX) { + int i, mi; + num_bits = (pos_slot >> 1) - 1; +@@ -506,7 +511,7 @@ + num_bits -= LZMA_NUM_ALIGN_BITS; + while (num_bits--) + cst->rep0 = (cst->rep0 << 1) | +- rc_direct_bit(rc); ++ rc_direct_bit(rc, error); + prob = p + LZMA_ALIGN; + cst->rep0 <<= LZMA_NUM_ALIGN_BITS; + num_bits = LZMA_NUM_ALIGN_BITS; +@@ -514,7 +519,7 @@ + i = 1; + mi = 1; + while (num_bits--) { +- if (rc_get_bit(rc, prob + mi, &mi)) ++ if (rc_get_bit(rc, prob + mi, &mi, error)) + cst->rep0 |= i; + i <<= 1; + } +@@ -531,12 +536,12 @@ + + + +-STATIC inline int INIT unlzma(unsigned char *buf, int in_len, ++STATIC int INIT unlzma(unsigned char *buf, int in_len, + int(*fill)(void*, unsigned int), + int(*flush)(void*, unsigned int), + unsigned char *output, + int *posp, +- void(*error_fn)(char *x) ++ void(*error)(char *x) + ) + { + struct lzma_header header; +@@ -552,8 +557,6 @@ + unsigned char *inbuf; + int ret = -1; + +- set_error_fn(error_fn); +- + if (buf) + inbuf = buf; + else +@@ -576,7 +579,7 @@ + + for (i = 0; i < sizeof(header); i++) { + if (rc.ptr >= rc.buffer_end) +- rc_read(&rc); ++ rc_read(&rc, error); + ((unsigned char *)&header)[i] = *rc.ptr++; + } + +@@ -621,17 +624,17 @@ + for (i = 0; i < num_probs; i++) + p[i] = (1 << RC_MODEL_TOTAL_BITS) >> 1; + +- rc_init_code(&rc); ++ rc_init_code(&rc, error); + + while (get_pos(&wr) < header.dst_size) { + int pos_state = get_pos(&wr) & pos_state_mask; + uint16_t *prob = p + LZMA_IS_MATCH + + (cst.state << LZMA_NUM_POS_BITS_MAX) + pos_state; +- if (rc_is_bit_0(&rc, prob)) ++ if (rc_is_bit_0(&rc, prob, error)) + process_bit0(&wr, &rc, &cst, p, pos_state, prob, +- lc, literal_pos_mask); ++ lc, literal_pos_mask, error); + else { +- process_bit1(&wr, &rc, &cst, p, pos_state, prob); ++ process_bit1(&wr, &rc, &cst, p, pos_state, prob, error); + if (cst.rep0 == 0) + break; + } +@@ -652,6 +655,9 @@ + exit_0: + return ret; + } ++#if defined(CONFIG_DECOMPRESS_LZMA_NEEDED) && !defined(PREBOOT) ++EXPORT_SYMBOL(unlzma); ++#endif + + #ifdef PREBOOT + STATIC int INIT decompress(unsigned char *buf, int in_len, +diff -Naur linux-2.6.35-rc6/lib/decompress_unlzo.c linux-2.6.35-rc6.patch/lib/decompress_unlzo.c +--- linux-2.6.35-rc6/lib/decompress_unlzo.c 2010-07-22 21:13:38.000000000 +0200 ++++ linux-2.6.35-rc6.patch/lib/decompress_unlzo.c 2010-07-29 06:51:12.340249191 +0200 +@@ -39,6 +39,7 @@ + + #include <linux/types.h> + #include <linux/lzo.h> ++#include <linux/decompress/unlzo_mm.h> + #include <linux/decompress/mm.h> + + #include <linux/compiler.h> +diff -Naur linux-2.6.35-rc6/lib/Kconfig linux-2.6.35-rc6.patch/lib/Kconfig +--- linux-2.6.35-rc6/lib/Kconfig 2010-07-22 21:13:38.000000000 +0200 ++++ linux-2.6.35-rc6.patch/lib/Kconfig 2010-07-29 06:51:12.340249191 +0200 +@@ -121,6 +121,9 @@ + select LZO_DECOMPRESS + tristate + ++config DECOMPRESS_LZMA_NEEDED ++ boolean ++ + # + # Generic allocator support is selected if needed + # +diff -Naur linux-2.6.35-rc6/lib/Makefile linux-2.6.35-rc6.patch/lib/Makefile +--- linux-2.6.35-rc6/lib/Makefile 2010-07-22 21:13:38.000000000 +0200 ++++ linux-2.6.35-rc6.patch/lib/Makefile 2010-07-29 06:51:12.341249940 +0200 +@@ -72,7 +72,7 @@ + + lib-$(CONFIG_DECOMPRESS_GZIP) += decompress_inflate.o + lib-$(CONFIG_DECOMPRESS_BZIP2) += decompress_bunzip2.o +-lib-$(CONFIG_DECOMPRESS_LZMA) += decompress_unlzma.o ++obj-$(CONFIG_DECOMPRESS_LZMA) += decompress_unlzma.o + lib-$(CONFIG_DECOMPRESS_LZO) += decompress_unlzo.o + + obj-$(CONFIG_TEXTSEARCH) += textsearch.o +diff -Naur linux-2.6.35-rc6/lib/Makefile.orig linux-2.6.35-rc6.patch/lib/Makefile.orig diff --git a/main/linux-xbmc/unionfs-2.5.5_for_2.6.35.4.patch b/main/linux-xbmc/unionfs-2.5.5_for_2.6.35.4.patch new file mode 100644 index 000000000..09d2dde6d --- /dev/null +++ b/main/linux-xbmc/unionfs-2.5.5_for_2.6.35.4.patch @@ -0,0 +1,11264 @@ +diff --git a/Documentation/filesystems/00-INDEX b/Documentation/filesystems/00-INDEX +index 4303614..5ade4a8 100644 +--- a/Documentation/filesystems/00-INDEX ++++ b/Documentation/filesystems/00-INDEX +@@ -112,6 +112,8 @@ udf.txt + - info and mount options for the UDF filesystem. + ufs.txt + - info on the ufs filesystem. ++unionfs/ ++ - info on the unionfs filesystem + vfat.txt + - info on using the VFAT filesystem used in Windows NT and Windows 95 + vfs.txt +diff --git a/Documentation/filesystems/unionfs/00-INDEX b/Documentation/filesystems/unionfs/00-INDEX +new file mode 100644 +index 0000000..96fdf67 +--- /dev/null ++++ b/Documentation/filesystems/unionfs/00-INDEX +@@ -0,0 +1,10 @@ ++00-INDEX ++ - this file. ++concepts.txt ++ - A brief introduction of concepts. ++issues.txt ++ - A summary of known issues with unionfs. ++rename.txt ++ - Information regarding rename operations. ++usage.txt ++ - Usage information and examples. +diff --git a/Documentation/filesystems/unionfs/concepts.txt b/Documentation/filesystems/unionfs/concepts.txt +new file mode 100644 +index 0000000..b853788 +--- /dev/null ++++ b/Documentation/filesystems/unionfs/concepts.txt +@@ -0,0 +1,287 @@ ++Unionfs 2.x CONCEPTS: ++===================== ++ ++This file describes the concepts needed by a namespace unification file ++system. ++ ++ ++Branch Priority: ++================ ++ ++Each branch is assigned a unique priority - starting from 0 (highest ++priority). No two branches can have the same priority. ++ ++ ++Branch Mode: ++============ ++ ++Each branch is assigned a mode - read-write or read-only. This allows ++directories on media mounted read-write to be used in a read-only manner. ++ ++ ++Whiteouts: ++========== ++ ++A whiteout removes a file name from the namespace. Whiteouts are needed when ++one attempts to remove a file on a read-only branch. ++ ++Suppose we have a two-branch union, where branch 0 is read-write and branch ++1 is read-only. And a file 'foo' on branch 1: ++ ++./b0/ ++./b1/ ++./b1/foo ++ ++The unified view would simply be: ++ ++./union/ ++./union/foo ++ ++Since 'foo' is stored on a read-only branch, it cannot be removed. A ++whiteout is used to remove the name 'foo' from the unified namespace. Again, ++since branch 1 is read-only, the whiteout cannot be created there. So, we ++try on a higher priority (lower numerically) branch and create the whiteout ++there. ++ ++./b0/ ++./b0/.wh.foo ++./b1/ ++./b1/foo ++ ++Later, when Unionfs traverses branches (due to lookup or readdir), it ++eliminate 'foo' from the namespace (as well as the whiteout itself.) ++ ++ ++Opaque Directories: ++=================== ++ ++Assume we have a unionfs mount comprising of two branches. Branch 0 is ++empty; branch 1 has the directory /a and file /a/f. Let's say we mount a ++union of branch 0 as read-write and branch 1 as read-only. Now, let's say ++we try to perform the following operation in the union: ++ ++ rm -fr a ++ ++Because branch 1 is not writable, we cannot physically remove the file /a/f ++or the directory /a. So instead, we will create a whiteout in branch 0 ++named /.wh.a, masking out the name "a" from branch 1. Next, let's say we ++try to create a directory named "a" as follows: ++ ++ mkdir a ++ ++Because we have a whiteout for "a" already, Unionfs behaves as if "a" ++doesn't exist, and thus will delete the whiteout and replace it with an ++actual directory named "a". ++ ++The problem now is that if you try to "ls" in the union, Unionfs will ++perform is normal directory name unification, for *all* directories named ++"a" in all branches. This will cause the file /a/f from branch 1 to ++re-appear in the union's namespace, which violates Unix semantics. ++ ++To avoid this problem, we have a different form of whiteouts for ++directories, called "opaque directories" (same as BSD Union Mount does). ++Whenever we replace a whiteout with a directory, that directory is marked as ++opaque. In Unionfs 2.x, it means that we create a file named ++/a/.wh.__dir_opaque in branch 0, after having created directory /a there. ++When unionfs notices that a directory is opaque, it stops all namespace ++operations (including merging readdir contents) at that opaque directory. ++This prevents re-exposing names from masked out directories. ++ ++ ++Duplicate Elimination: ++====================== ++ ++It is possible for files on different branches to have the same name. ++Unionfs then has to select which instance of the file to show to the user. ++Given the fact that each branch has a priority associated with it, the ++simplest solution is to take the instance from the highest priority ++(numerically lowest value) and "hide" the others. ++ ++ ++Unlinking: ++========= ++ ++Unlink operation on non-directory instances is optimized to remove the ++maximum possible objects in case multiple underlying branches have the same ++file name. The unlink operation will first try to delete file instances ++from highest priority branch and then move further to delete from remaining ++branches in order of their decreasing priority. Consider a case (F..D..F), ++where F is a file and D is a directory of the same name; here, some ++intermediate branch could have an empty directory instance with the same ++name, so this operation also tries to delete this directory instance and ++proceed further to delete from next possible lower priority branch. The ++unionfs unlink operation will smoothly delete the files with same name from ++all possible underlying branches. In case if some error occurs, it creates ++whiteout in highest priority branch that will hide file instance in rest of ++the branches. An error could occur either if an unlink operations in any of ++the underlying branch failed or if a branch has no write permission. ++ ++This unlinking policy is known as "delete all" and it has the benefit of ++overall reducing the number of inodes used by duplicate files, and further ++reducing the total number of inodes consumed by whiteouts. The cost is of ++extra processing, but testing shows this extra processing is well worth the ++savings. ++ ++ ++Copyup: ++======= ++ ++When a change is made to the contents of a file's data or meta-data, they ++have to be stored somewhere. The best way is to create a copy of the ++original file on a branch that is writable, and then redirect the write ++though to this copy. The copy must be made on a higher priority branch so ++that lookup and readdir return this newer "version" of the file rather than ++the original (see duplicate elimination). ++ ++An entire unionfs mount can be read-only or read-write. If it's read-only, ++then none of the branches will be written to, even if some of the branches ++are physically writeable. If the unionfs mount is read-write, then the ++leftmost (highest priority) branch must be writeable (for copyup to take ++place); the remaining branches can be any mix of read-write and read-only. ++ ++In a writeable mount, unionfs will create new files/dir in the leftmost ++branch. If one tries to modify a file in a read-only branch/media, unionfs ++will copyup the file to the leftmost branch and modify it there. If you try ++to modify a file from a writeable branch which is not the leftmost branch, ++then unionfs will modify it in that branch; this is useful if you, say, ++unify differnet packages (e.g., apache, sendmail, ftpd, etc.) and you want ++changes to specific package files to remain logically in the directory where ++they came from. ++ ++Cache Coherency: ++================ ++ ++Unionfs users often want to be able to modify files and directories directly ++on the lower branches, and have those changes be visible at the Unionfs ++level. This means that data (e.g., pages) and meta-data (dentries, inodes, ++open files, etc.) have to be synchronized between the upper and lower ++layers. In other words, the newest changes from a layer below have to be ++propagated to the Unionfs layer above. If the two layers are not in sync, a ++cache incoherency ensues, which could lead to application failures and even ++oopses. The Linux kernel, however, has a rather limited set of mechanisms ++to ensure this inter-layer cache coherency---so Unionfs has to do most of ++the hard work on its own. ++ ++Maintaining Invariants: ++ ++The way Unionfs ensures cache coherency is as follows. At each entry point ++to a Unionfs file system method, we call a utility function to validate the ++primary objects of this method. Generally, we call unionfs_file_revalidate ++on open files, and __unionfs_d_revalidate_chain on dentries (which also ++validates inodes). These utility functions check to see whether the upper ++Unionfs object is in sync with any of the lower objects that it represents. ++The checks we perform include whether the Unionfs superblock has a newer ++generation number, or if any of the lower objects mtime's or ctime's are ++newer. (Note: generation numbers change when branch-management commands are ++issued, so in a way, maintaining cache coherency is also very important for ++branch-management.) If indeed we determine that any Unionfs object is no ++longer in sync with its lower counterparts, then we rebuild that object ++similarly to how we do so for branch-management. ++ ++While rebuilding Unionfs's objects, we also purge any page mappings and ++truncate inode pages (see fs/unionfs/dentry.c:purge_inode_data). This is to ++ensure that Unionfs will re-get the newer data from the lower branches. We ++perform this purging only if the Unionfs operation in question is a reading ++operation; if Unionfs is performing a data writing operation (e.g., ->write, ++->commit_write, etc.) then we do NOT flush the lower mappings/pages: this is ++because (1) a self-deadlock could occur and (2) the upper Unionfs pages are ++considered more authoritative anyway, as they are newer and will overwrite ++any lower pages. ++ ++Unionfs maintains the following important invariant regarding mtime's, ++ctime's, and atime's: the upper inode object's times are the max() of all of ++the lower ones. For non-directory objects, there's only one object below, ++so the mapping is simple; for directory objects, there could me multiple ++lower objects and we have to sync up with the newest one of all the lower ++ones. This invariant is important to maintain, especially for directories ++(besides, we need this to be POSIX compliant). A union could comprise ++multiple writable branches, each of which could change. If we don't reflect ++the newest possible mtime/ctime, some applications could fail. For example, ++NFSv2/v3 exports check for newer directory mtimes on the server to determine ++if the client-side attribute cache should be purged. ++ ++To maintain these important invariants, of course, Unionfs carefully ++synchronizes upper and lower times in various places. For example, if we ++copy-up a file to a top-level branch, the parent directory where the file ++was copied up to will now have a new mtime: so after a successful copy-up, ++we sync up with the new top-level branch's parent directory mtime. ++ ++Implementation: ++ ++This cache-coherency implementation is efficient because it defers any ++synchronizing between the upper and lower layers until absolutely needed. ++Consider the example a common situation where users perform a lot of lower ++changes, such as untarring a whole package. While these take place, ++typically the user doesn't access the files via Unionfs; only after the ++lower changes are done, does the user try to access the lower files. With ++our cache-coherency implementation, the entirety of the changes to the lower ++branches will not result in a single CPU cycle spent at the Unionfs level ++until the user invokes a system call that goes through Unionfs. ++ ++We have considered two alternate cache-coherency designs. (1) Using the ++dentry/inode notify functionality to register interest in finding out about ++any lower changes. This is a somewhat limited and also a heavy-handed ++approach which could result in many notifications to the Unionfs layer upon ++each small change at the lower layer (imagine a file being modified multiple ++times in rapid succession). (2) Rewriting the VFS to support explicit ++callbacks from lower objects to upper objects. We began exploring such an ++implementation, but found it to be very complicated--it would have resulted ++in massive VFS/MM changes which are unlikely to be accepted by the LKML ++community. We therefore believe that our current cache-coherency design and ++implementation represent the best approach at this time. ++ ++Limitations: ++ ++Our implementation works in that as long as a user process will have caused ++Unionfs to be called, directly or indirectly, even to just do ++->d_revalidate; then we will have purged the current Unionfs data and the ++process will see the new data. For example, a process that continually ++re-reads the same file's data will see the NEW data as soon as the lower ++file had changed, upon the next read(2) syscall (even if the file is still ++open!) However, this doesn't work when the process re-reads the open file's ++data via mmap(2) (unless the user unmaps/closes the file and remaps/reopens ++it). Once we respond to ->readpage(s), then the kernel maps the page into ++the process's address space and there doesn't appear to be a way to force ++the kernel to invalidate those pages/mappings, and force the process to ++re-issue ->readpage. If there's a way to invalidate active mappings and ++force a ->readpage, let us know please (invalidate_inode_pages2 doesn't do ++the trick). ++ ++Our current Unionfs code has to perform many file-revalidation calls. It ++would be really nice if the VFS would export an optional file system hook ++->file_revalidate (similarly to dentry->d_revalidate) that will be called ++before each VFS op that has a "struct file" in it. ++ ++Certain file systems have micro-second granularity (or better) for inode ++times, and asynchronous actions could cause those times to change with some ++small delay. In such cases, Unionfs may see a changed inode time that only ++differs by a tiny fraction of a second: such a change may be a false ++positive indication that the lower object has changed, whereas if unionfs ++waits a little longer, that false indication will not be seen. (These false ++positives are harmless, because they would at most cause unionfs to ++re-validate an object that may need no revalidation, and print a debugging ++message that clutters the console/logs.) Therefore, to minimize the chances ++of these situations, we delay the detection of changed times by a small ++factor of a few seconds, called UNIONFS_MIN_CC_TIME (which defaults to 3 ++seconds, as does NFS). This means that we will detect the change, only a ++couple of seconds later, if indeed the time change persists in the lower ++file object. This delayed detection has an added performance benefit: we ++reduce the number of times that unionfs has to revalidate objects, in case ++there's a lot of concurrent activity on both the upper and lower objects, ++for the same file(s). Lastly, this delayed time attribute detection is ++similar to how NFS clients operate (e.g., acregmin). ++ ++Finally, there is no way currently in Linux to prevent lower directories ++from being moved around (i.e., topology changes); there's no way to prevent ++modifications to directory sub-trees of whole file systems which are mounted ++read-write. It is therefore possible for in-flight operations in unionfs to ++take place, while a lower directory is being moved around. Therefore, if ++you try to, say, create a new file in a directory through unionfs, while the ++directory is being moved around directly, then the new file may get created ++in the new location where that directory was moved to. This is a somewhat ++similar behaviour in NFS: an NFS client could be creating a new file while ++th NFS server is moving th directory around; the file will get successfully ++created in the new location. (The one exception in unionfs is that if the ++branch is marked read-only by unionfs, then a copyup will take place.) ++ ++For more information, see <http://unionfs.filesystems.org/>. +diff --git a/Documentation/filesystems/unionfs/issues.txt b/Documentation/filesystems/unionfs/issues.txt +new file mode 100644 +index 0000000..f4b7e7e +--- /dev/null ++++ b/Documentation/filesystems/unionfs/issues.txt +@@ -0,0 +1,28 @@ ++KNOWN Unionfs 2.x ISSUES: ++========================= ++ ++1. Unionfs should not use lookup_one_len() on the underlying f/s as it ++ confuses NFSv4. Currently, unionfs_lookup() passes lookup intents to the ++ lower file-system, this eliminates part of the problem. The remaining ++ calls to lookup_one_len may need to be changed to pass an intent. We are ++ currently introducing VFS changes to fs/namei.c's do_path_lookup() to ++ allow proper file lookup and opening in stackable file systems. ++ ++2. Lockdep (a debugging feature) isn't aware of stacking, and so it ++ incorrectly complains about locking problems. The problem boils down to ++ this: Lockdep considers all objects of a certain type to be in the same ++ class, for example, all inodes. Lockdep doesn't like to see a lock held ++ on two inodes within the same task, and warns that it could lead to a ++ deadlock. However, stackable file systems do precisely that: they lock ++ an upper object, and then a lower object, in a strict order to avoid ++ locking problems; in addition, Unionfs, as a fan-out file system, may ++ have to lock several lower inodes. We are currently looking into Lockdep ++ to see how to make it aware of stackable file systems. For now, we ++ temporarily disable lockdep when calling vfs methods on lower objects, ++ but only for those places where lockdep complained. While this solution ++ may seem unclean, it is not without precedent: other places in the kernel ++ also do similar temporary disabling, of course after carefully having ++ checked that it is the right thing to do. Anyway, you get any warnings ++ from Lockdep, please report them to the Unionfs maintainers. ++ ++For more information, see <http://unionfs.filesystems.org/>. +diff --git a/Documentation/filesystems/unionfs/rename.txt b/Documentation/filesystems/unionfs/rename.txt +new file mode 100644 +index 0000000..e20bb82 +--- /dev/null ++++ b/Documentation/filesystems/unionfs/rename.txt +@@ -0,0 +1,31 @@ ++Rename is a complex beast. The following table shows which rename(2) operations ++should succeed and which should fail. ++ ++o: success ++E: error (either unionfs or vfs) ++X: EXDEV ++ ++none = file does not exist ++file = file is a file ++dir = file is a empty directory ++child= file is a non-empty directory ++wh = file is a directory containing only whiteouts; this makes it logically ++ empty ++ ++ none file dir child wh ++file o o E E E ++dir o E o E o ++child X E X E X ++wh o E o E o ++ ++ ++Renaming directories: ++===================== ++ ++Whenever a empty (either physically or logically) directory is being renamed, ++the following sequence of events should take place: ++ ++1) Remove whiteouts from both source and destination directory ++2) Rename source to destination ++3) Make destination opaque to prevent anything under it from showing up ++ +diff --git a/Documentation/filesystems/unionfs/usage.txt b/Documentation/filesystems/unionfs/usage.txt +new file mode 100644 +index 0000000..1adde69 +--- /dev/null ++++ b/Documentation/filesystems/unionfs/usage.txt +@@ -0,0 +1,134 @@ ++Unionfs is a stackable unification file system, which can appear to merge ++the contents of several directories (branches), while keeping their physical ++content separate. Unionfs is useful for unified source tree management, ++merged contents of split CD-ROM, merged separate software package ++directories, data grids, and more. Unionfs allows any mix of read-only and ++read-write branches, as well as insertion and deletion of branches anywhere ++in the fan-out. To maintain Unix semantics, Unionfs handles elimination of ++duplicates, partial-error conditions, and more. ++ ++GENERAL SYNTAX ++============== ++ ++# mount -t unionfs -o <OPTIONS>,<BRANCH-OPTIONS> none MOUNTPOINT ++ ++OPTIONS can be any legal combination of: ++ ++- ro # mount file system read-only ++- rw # mount file system read-write ++- remount # remount the file system (see Branch Management below) ++- incgen # increment generation no. (see Cache Consistency below) ++ ++BRANCH-OPTIONS can be either (1) a list of branches given to the "dirs=" ++option, or (2) a list of individual branch manipulation commands, combined ++with the "remount" option, and is further described in the "Branch ++Management" section below. ++ ++The syntax for the "dirs=" mount option is: ++ ++ dirs=branch[=ro|=rw][:...] ++ ++The "dirs=" option takes a colon-delimited list of directories to compose ++the union, with an optional branch mode for each of those directories. ++Directories that come earlier (specified first, on the left) in the list ++have a higher precedence than those which come later. Additionally, ++read-only or read-write permissions of the branch can be specified by ++appending =ro or =rw (default) to each directory. See the Copyup section in ++concepts.txt, for a description of Unionfs's behavior when mixing read-only ++and read-write branches and mounts. ++ ++Syntax: ++ ++ dirs=/branch1[=ro|=rw]:/branch2[=ro|=rw]:...:/branchN[=ro|=rw] ++ ++Example: ++ ++ dirs=/writable_branch=rw:/read-only_branch=ro ++ ++ ++BRANCH MANAGEMENT ++================= ++ ++Once you mount your union for the first time, using the "dirs=" option, you ++can then change the union's overall mode or reconfigure the branches, using ++the remount option, as follows. ++ ++To downgrade a union from read-write to read-only: ++ ++# mount -t unionfs -o remount,ro none MOUNTPOINT ++ ++To upgrade a union from read-only to read-write: ++ ++# mount -t unionfs -o remount,rw none MOUNTPOINT ++ ++To delete a branch /foo, regardless where it is in the current union: ++ ++# mount -t unionfs -o remount,del=/foo none MOUNTPOINT ++ ++To insert (add) a branch /foo before /bar: ++ ++# mount -t unionfs -o remount,add=/bar:/foo none MOUNTPOINT ++ ++To insert (add) a branch /foo (with the "rw" mode flag) before /bar: ++ ++# mount -t unionfs -o remount,add=/bar:/foo=rw none MOUNTPOINT ++ ++To insert (add) a branch /foo (in "rw" mode) at the very beginning (i.e., a ++new highest-priority branch), you can use the above syntax, or use a short ++hand version as follows: ++ ++# mount -t unionfs -o remount,add=/foo none MOUNTPOINT ++ ++To append a branch to the very end (new lowest-priority branch): ++ ++# mount -t unionfs -o remount,add=:/foo none MOUNTPOINT ++ ++To append a branch to the very end (new lowest-priority branch), in ++read-only mode: ++ ++# mount -t unionfs -o remount,add=:/foo=ro none MOUNTPOINT ++ ++Finally, to change the mode of one existing branch, say /foo, from read-only ++to read-write, and change /bar from read-write to read-only: ++ ++# mount -t unionfs -o remount,mode=/foo=rw,mode=/bar=ro none MOUNTPOINT ++ ++Note: in Unionfs 2.x, you cannot set the leftmost branch to readonly because ++then Unionfs won't have any writable place for copyups to take place. ++Moreover, the VFS can get confused when it tries to modify something in a ++file system mounted read-write, but isn't permitted to write to it. ++Instead, you should set the whole union as readonly, as described above. ++If, however, you must set the leftmost branch as readonly, perhaps so you ++can get a snapshot of it at a point in time, then you should insert a new ++writable top-level branch, and mark the one you want as readonly. This can ++be accomplished as follows, assuming that /foo is your current leftmost ++branch: ++ ++# mount -t tmpfs -o size=NNN /new ++# mount -t unionfs -o remount,add=/new,mode=/foo=ro none MOUNTPOINT ++<do what you want safely in /foo> ++# mount -t unionfs -o remount,del=/new,mode=/foo=rw none MOUNTPOINT ++<check if there's anything in /new you want to preserve> ++# umount /new ++ ++CACHE CONSISTENCY ++================= ++ ++If you modify any file on any of the lower branches directly, while there is ++a Unionfs 2.x mounted above any of those branches, you should tell Unionfs ++to purge its caches and re-get the objects. To do that, you have to ++increment the generation number of the superblock using the following ++command: ++ ++# mount -t unionfs -o remount,incgen none MOUNTPOINT ++ ++Note that the older way of incrementing the generation number using an ++ioctl, is no longer supported in Unionfs 2.0 and newer. Ioctls in general ++are not encouraged. Plus, an ioctl is per-file concept, whereas the ++generation number is a per-file-system concept. Worse, such an ioctl ++requires an open file, which then has to be invalidated by the very nature ++of the generation number increase (read: the old generation increase ioctl ++was pretty racy). ++ ++ ++For more information, see <http://unionfs.filesystems.org/>. +diff --git a/MAINTAINERS b/MAINTAINERS +index 02f75fc..8c5efe7 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -5766,6 +5766,14 @@ F: Documentation/cdrom/ + F: drivers/cdrom/cdrom.c + F: include/linux/cdrom.h + ++UNIONFS ++P: Erez Zadok ++M: ezk@cs.sunysb.edu ++L: unionfs@filesystems.org ++W: http://unionfs.filesystems.org/ ++T: git git.kernel.org/pub/scm/linux/kernel/git/ezk/unionfs.git ++S: Maintained ++ + UNSORTED BLOCK IMAGES (UBI) + M: Artem Bityutskiy <dedekind1@gmail.com> + W: http://www.linux-mtd.infradead.org/ +diff --git a/fs/Kconfig b/fs/Kconfig +index 5f85b59..7b4501b 100644 +--- a/fs/Kconfig ++++ b/fs/Kconfig +@@ -169,6 +169,7 @@ if MISC_FILESYSTEMS + source "fs/adfs/Kconfig" + source "fs/affs/Kconfig" + source "fs/ecryptfs/Kconfig" ++source "fs/unionfs/Kconfig" + source "fs/hfs/Kconfig" + source "fs/hfsplus/Kconfig" + source "fs/befs/Kconfig" +diff --git a/fs/Makefile b/fs/Makefile +index e6ec1d3..787332e 100644 +--- a/fs/Makefile ++++ b/fs/Makefile +@@ -84,6 +84,7 @@ obj-$(CONFIG_ISO9660_FS) += isofs/ + obj-$(CONFIG_HFSPLUS_FS) += hfsplus/ # Before hfs to find wrapped HFS+ + obj-$(CONFIG_HFS_FS) += hfs/ + obj-$(CONFIG_ECRYPT_FS) += ecryptfs/ ++obj-$(CONFIG_UNION_FS) += unionfs/ + obj-$(CONFIG_VXFS_FS) += freevxfs/ + obj-$(CONFIG_NFS_FS) += nfs/ + obj-$(CONFIG_EXPORTFS) += exportfs/ +diff --git a/fs/namei.c b/fs/namei.c +index 868d0cb..b5e09e1 100644 +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -386,6 +386,7 @@ void release_open_intent(struct nameidata *nd) + else + fput(nd->intent.open.file); + } ++EXPORT_SYMBOL_GPL(release_open_intent); + + static inline struct dentry * + do_revalidate(struct dentry *dentry, struct nameidata *nd) +diff --git a/fs/splice.c b/fs/splice.c +index efdbfec..1ff6bca 100644 +--- a/fs/splice.c ++++ b/fs/splice.c +@@ -1104,8 +1104,8 @@ EXPORT_SYMBOL(generic_splice_sendpage); + /* + * Attempt to initiate a splice from pipe to file. + */ +-static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, +- loff_t *ppos, size_t len, unsigned int flags) ++long vfs_splice_from(struct pipe_inode_info *pipe, struct file *out, ++ loff_t *ppos, size_t len, unsigned int flags) + { + ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, + loff_t *, size_t, unsigned int); +@@ -1128,13 +1128,14 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, + + return splice_write(pipe, out, ppos, len, flags); + } ++EXPORT_SYMBOL_GPL(vfs_splice_from); + + /* + * Attempt to initiate a splice from a file to a pipe. + */ +-static long do_splice_to(struct file *in, loff_t *ppos, +- struct pipe_inode_info *pipe, size_t len, +- unsigned int flags) ++long vfs_splice_to(struct file *in, loff_t *ppos, ++ struct pipe_inode_info *pipe, size_t len, ++ unsigned int flags) + { + ssize_t (*splice_read)(struct file *, loff_t *, + struct pipe_inode_info *, size_t, unsigned int); +@@ -1154,6 +1155,7 @@ static long do_splice_to(struct file *in, loff_t *ppos, + + return splice_read(in, ppos, pipe, len, flags); + } ++EXPORT_SYMBOL_GPL(vfs_splice_to); + + /** + * splice_direct_to_actor - splices data directly between two non-pipes +@@ -1223,7 +1225,7 @@ ssize_t splice_direct_to_actor(struct file *in, struct splice_desc *sd, + size_t read_len; + loff_t pos = sd->pos, prev_pos = pos; + +- ret = do_splice_to(in, &pos, pipe, len, flags); ++ ret = vfs_splice_to(in, &pos, pipe, len, flags); + if (unlikely(ret <= 0)) + goto out_release; + +@@ -1282,8 +1284,8 @@ static int direct_splice_actor(struct pipe_inode_info *pipe, + { + struct file *file = sd->u.file; + +- return do_splice_from(pipe, file, &file->f_pos, sd->total_len, +- sd->flags); ++ return vfs_splice_from(pipe, file, &file->f_pos, sd->total_len, ++ sd->flags); + } + + /** +@@ -1380,7 +1382,7 @@ static long do_splice(struct file *in, loff_t __user *off_in, + } else + off = &out->f_pos; + +- ret = do_splice_from(ipipe, out, off, len, flags); ++ ret = vfs_splice_from(ipipe, out, off, len, flags); + + if (off_out && copy_to_user(off_out, off, sizeof(loff_t))) + ret = -EFAULT; +@@ -1400,7 +1402,7 @@ static long do_splice(struct file *in, loff_t __user *off_in, + } else + off = &in->f_pos; + +- ret = do_splice_to(in, off, opipe, len, flags); ++ ret = vfs_splice_to(in, off, opipe, len, flags); + + if (off_in && copy_to_user(off_in, off, sizeof(loff_t))) + ret = -EFAULT; +diff --git a/fs/stack.c b/fs/stack.c +index 4a6f7f4..7eeef12 100644 +--- a/fs/stack.c ++++ b/fs/stack.c +@@ -1,8 +1,20 @@ ++/* ++ * Copyright (c) 2006-2009 Erez Zadok ++ * Copyright (c) 2006-2007 Josef 'Jeff' Sipek ++ * Copyright (c) 2006-2009 Stony Brook University ++ * Copyright (c) 2006-2009 The Research Foundation of SUNY ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ + #include <linux/module.h> + #include <linux/fs.h> + #include <linux/fs_stack.h> + +-/* does _NOT_ require i_mutex to be held. ++/* ++ * does _NOT_ require i_mutex to be held. + * + * This function cannot be inlined since i_size_{read,write} is rather + * heavy-weight on 32-bit systems +diff --git a/fs/unionfs/Kconfig b/fs/unionfs/Kconfig +new file mode 100644 +index 0000000..f3c1ac4 +--- /dev/null ++++ b/fs/unionfs/Kconfig +@@ -0,0 +1,24 @@ ++config UNION_FS ++ tristate "Union file system (EXPERIMENTAL)" ++ depends on EXPERIMENTAL ++ help ++ Unionfs is a stackable unification file system, which appears to ++ merge the contents of several directories (branches), while keeping ++ their physical content separate. ++ ++ See <http://unionfs.filesystems.org> for details ++ ++config UNION_FS_XATTR ++ bool "Unionfs extended attributes" ++ depends on UNION_FS ++ help ++ Extended attributes are name:value pairs associated with inodes by ++ the kernel or by users (see the attr(5) manual page). ++ ++ If unsure, say N. ++ ++config UNION_FS_DEBUG ++ bool "Debug Unionfs" ++ depends on UNION_FS ++ help ++ If you say Y here, you can turn on debugging output from Unionfs. +diff --git a/fs/unionfs/Makefile b/fs/unionfs/Makefile +new file mode 100644 +index 0000000..c30b01c +--- /dev/null ++++ b/fs/unionfs/Makefile +@@ -0,0 +1,17 @@ ++UNIONFS_VERSION="2.5.5 (for 2.6.35.1)" ++ ++EXTRA_CFLAGS += -DUNIONFS_VERSION=\"$(UNIONFS_VERSION)\" ++ ++obj-$(CONFIG_UNION_FS) += unionfs.o ++ ++unionfs-y := subr.o dentry.o file.o inode.o main.o super.o \ ++ rdstate.o copyup.o dirhelper.o rename.o unlink.o \ ++ lookup.o commonfops.o dirfops.o sioq.o mmap.o whiteout.o ++ ++unionfs-$(CONFIG_UNION_FS_XATTR) += xattr.o ++ ++unionfs-$(CONFIG_UNION_FS_DEBUG) += debug.o ++ ++ifeq ($(CONFIG_UNION_FS_DEBUG),y) ++EXTRA_CFLAGS += -DDEBUG ++endif +diff --git a/fs/unionfs/commonfops.c b/fs/unionfs/commonfops.c +new file mode 100644 +index 0000000..740c4ad +--- /dev/null ++++ b/fs/unionfs/commonfops.c +@@ -0,0 +1,896 @@ ++/* ++ * Copyright (c) 2003-2010 Erez Zadok ++ * Copyright (c) 2003-2006 Charles P. Wright ++ * Copyright (c) 2005-2007 Josef 'Jeff' Sipek ++ * Copyright (c) 2005-2006 Junjiro Okajima ++ * Copyright (c) 2005 Arun M. Krishnakumar ++ * Copyright (c) 2004-2006 David P. Quigley ++ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair ++ * Copyright (c) 2003 Puja Gupta ++ * Copyright (c) 2003 Harikesavan Krishnan ++ * Copyright (c) 2003-2010 Stony Brook University ++ * Copyright (c) 2003-2010 The Research Foundation of SUNY ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "union.h" ++ ++/* ++ * 1) Copyup the file ++ * 2) Rename the file to '.unionfs<original inode#><counter>' - obviously ++ * stolen from NFS's silly rename ++ */ ++static int copyup_deleted_file(struct file *file, struct dentry *dentry, ++ struct dentry *parent, int bstart, int bindex) ++{ ++ static unsigned int counter; ++ const int i_inosize = sizeof(dentry->d_inode->i_ino) * 2; ++ const int countersize = sizeof(counter) * 2; ++ const int nlen = sizeof(".unionfs") + i_inosize + countersize - 1; ++ char name[nlen + 1]; ++ int err; ++ struct dentry *tmp_dentry = NULL; ++ struct dentry *lower_dentry; ++ struct dentry *lower_dir_dentry = NULL; ++ ++ lower_dentry = unionfs_lower_dentry_idx(dentry, bstart); ++ ++ sprintf(name, ".unionfs%*.*lx", ++ i_inosize, i_inosize, lower_dentry->d_inode->i_ino); ++ ++ /* ++ * Loop, looking for an unused temp name to copyup to. ++ * ++ * It's somewhat silly that we look for a free temp tmp name in the ++ * source branch (bstart) instead of the dest branch (bindex), where ++ * the final name will be created. We _will_ catch it if somehow ++ * the name exists in the dest branch, but it'd be nice to catch it ++ * sooner than later. ++ */ ++retry: ++ tmp_dentry = NULL; ++ do { ++ char *suffix = name + nlen - countersize; ++ ++ dput(tmp_dentry); ++ counter++; ++ sprintf(suffix, "%*.*x", countersize, countersize, counter); ++ ++ pr_debug("unionfs: trying to rename %s to %s\n", ++ dentry->d_name.name, name); ++ ++ tmp_dentry = lookup_lck_len(name, lower_dentry->d_parent, ++ nlen); ++ if (IS_ERR(tmp_dentry)) { ++ err = PTR_ERR(tmp_dentry); ++ goto out; ++ } ++ } while (tmp_dentry->d_inode != NULL); /* need negative dentry */ ++ dput(tmp_dentry); ++ ++ err = copyup_named_file(parent->d_inode, file, name, bstart, bindex, ++ i_size_read(file->f_path.dentry->d_inode)); ++ if (err) { ++ if (unlikely(err == -EEXIST)) ++ goto retry; ++ goto out; ++ } ++ ++ /* bring it to the same state as an unlinked file */ ++ lower_dentry = unionfs_lower_dentry_idx(dentry, dbstart(dentry)); ++ if (!unionfs_lower_inode_idx(dentry->d_inode, bindex)) { ++ atomic_inc(&lower_dentry->d_inode->i_count); ++ unionfs_set_lower_inode_idx(dentry->d_inode, bindex, ++ lower_dentry->d_inode); ++ } ++ lower_dir_dentry = lock_parent(lower_dentry); ++ err = vfs_unlink(lower_dir_dentry->d_inode, lower_dentry); ++ unlock_dir(lower_dir_dentry); ++ ++out: ++ if (!err) ++ unionfs_check_dentry(dentry); ++ return err; ++} ++ ++/* ++ * put all references held by upper struct file and free lower file pointer ++ * array ++ */ ++static void cleanup_file(struct file *file) ++{ ++ int bindex, bstart, bend; ++ struct file **lower_files; ++ struct file *lower_file; ++ struct super_block *sb = file->f_path.dentry->d_sb; ++ ++ lower_files = UNIONFS_F(file)->lower_files; ++ bstart = fbstart(file); ++ bend = fbend(file); ++ ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ int i; /* holds (possibly) updated branch index */ ++ int old_bid; ++ ++ lower_file = unionfs_lower_file_idx(file, bindex); ++ if (!lower_file) ++ continue; ++ ++ /* ++ * Find new index of matching branch with an open ++ * file, since branches could have been added or ++ * deleted causing the one with open files to shift. ++ */ ++ old_bid = UNIONFS_F(file)->saved_branch_ids[bindex]; ++ i = branch_id_to_idx(sb, old_bid); ++ if (unlikely(i < 0)) { ++ printk(KERN_ERR "unionfs: no superblock for " ++ "file %p\n", file); ++ continue; ++ } ++ ++ /* decrement count of open files */ ++ branchput(sb, i); ++ /* ++ * fput will perform an mntput for us on the correct branch. ++ * Although we're using the file's old branch configuration, ++ * bindex, which is the old index, correctly points to the ++ * right branch in the file's branch list. In other words, ++ * we're going to mntput the correct branch even if branches ++ * have been added/removed. ++ */ ++ fput(lower_file); ++ UNIONFS_F(file)->lower_files[bindex] = NULL; ++ UNIONFS_F(file)->saved_branch_ids[bindex] = -1; ++ } ++ ++ UNIONFS_F(file)->lower_files = NULL; ++ kfree(lower_files); ++ kfree(UNIONFS_F(file)->saved_branch_ids); ++ /* set to NULL because caller needs to know if to kfree on error */ ++ UNIONFS_F(file)->saved_branch_ids = NULL; ++} ++ ++/* open all lower files for a given file */ ++static int open_all_files(struct file *file) ++{ ++ int bindex, bstart, bend, err = 0; ++ struct file *lower_file; ++ struct dentry *lower_dentry; ++ struct dentry *dentry = file->f_path.dentry; ++ struct super_block *sb = dentry->d_sb; ++ ++ bstart = dbstart(dentry); ++ bend = dbend(dentry); ++ ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); ++ if (!lower_dentry) ++ continue; ++ ++ dget(lower_dentry); ++ unionfs_mntget(dentry, bindex); ++ branchget(sb, bindex); ++ ++ lower_file = ++ dentry_open(lower_dentry, ++ unionfs_lower_mnt_idx(dentry, bindex), ++ file->f_flags, current_cred()); ++ if (IS_ERR(lower_file)) { ++ branchput(sb, bindex); ++ err = PTR_ERR(lower_file); ++ goto out; ++ } else { ++ unionfs_set_lower_file_idx(file, bindex, lower_file); ++ } ++ } ++out: ++ return err; ++} ++ ++/* open the highest priority file for a given upper file */ ++static int open_highest_file(struct file *file, bool willwrite) ++{ ++ int bindex, bstart, bend, err = 0; ++ struct file *lower_file; ++ struct dentry *lower_dentry; ++ struct dentry *dentry = file->f_path.dentry; ++ struct dentry *parent = dget_parent(dentry); ++ struct inode *parent_inode = parent->d_inode; ++ struct super_block *sb = dentry->d_sb; ++ ++ bstart = dbstart(dentry); ++ bend = dbend(dentry); ++ ++ lower_dentry = unionfs_lower_dentry(dentry); ++ if (willwrite && IS_WRITE_FLAG(file->f_flags) && is_robranch(dentry)) { ++ for (bindex = bstart - 1; bindex >= 0; bindex--) { ++ err = copyup_file(parent_inode, file, bstart, bindex, ++ i_size_read(dentry->d_inode)); ++ if (!err) ++ break; ++ } ++ atomic_set(&UNIONFS_F(file)->generation, ++ atomic_read(&UNIONFS_I(dentry->d_inode)-> ++ generation)); ++ goto out; ++ } ++ ++ dget(lower_dentry); ++ unionfs_mntget(dentry, bstart); ++ lower_file = dentry_open(lower_dentry, ++ unionfs_lower_mnt_idx(dentry, bstart), ++ file->f_flags, current_cred()); ++ if (IS_ERR(lower_file)) { ++ err = PTR_ERR(lower_file); ++ goto out; ++ } ++ branchget(sb, bstart); ++ unionfs_set_lower_file(file, lower_file); ++ /* Fix up the position. */ ++ lower_file->f_pos = file->f_pos; ++ ++ memcpy(&lower_file->f_ra, &file->f_ra, sizeof(struct file_ra_state)); ++out: ++ dput(parent); ++ return err; ++} ++ ++/* perform a delayed copyup of a read-write file on a read-only branch */ ++static int do_delayed_copyup(struct file *file, struct dentry *parent) ++{ ++ int bindex, bstart, bend, err = 0; ++ struct dentry *dentry = file->f_path.dentry; ++ struct inode *parent_inode = parent->d_inode; ++ ++ bstart = fbstart(file); ++ bend = fbend(file); ++ ++ BUG_ON(!S_ISREG(dentry->d_inode->i_mode)); ++ ++ unionfs_check_file(file); ++ for (bindex = bstart - 1; bindex >= 0; bindex--) { ++ if (!d_deleted(dentry)) ++ err = copyup_file(parent_inode, file, bstart, ++ bindex, ++ i_size_read(dentry->d_inode)); ++ else ++ err = copyup_deleted_file(file, dentry, parent, ++ bstart, bindex); ++ /* if succeeded, set lower open-file flags and break */ ++ if (!err) { ++ struct file *lower_file; ++ lower_file = unionfs_lower_file_idx(file, bindex); ++ lower_file->f_flags = file->f_flags; ++ break; ++ } ++ } ++ if (err || (bstart <= fbstart(file))) ++ goto out; ++ bend = fbend(file); ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ if (unionfs_lower_file_idx(file, bindex)) { ++ branchput(dentry->d_sb, bindex); ++ fput(unionfs_lower_file_idx(file, bindex)); ++ unionfs_set_lower_file_idx(file, bindex, NULL); ++ } ++ } ++ path_put_lowers(dentry, bstart, bend, false); ++ iput_lowers(dentry->d_inode, bstart, bend, false); ++ /* for reg file, we only open it "once" */ ++ fbend(file) = fbstart(file); ++ dbend(dentry) = dbstart(dentry); ++ ibend(dentry->d_inode) = ibstart(dentry->d_inode); ++ ++out: ++ unionfs_check_file(file); ++ return err; ++} ++ ++/* ++ * Helper function for unionfs_file_revalidate/locked. ++ * Expects dentry/parent to be locked already, and revalidated. ++ */ ++static int __unionfs_file_revalidate(struct file *file, struct dentry *dentry, ++ struct dentry *parent, ++ struct super_block *sb, int sbgen, ++ int dgen, bool willwrite) ++{ ++ int fgen; ++ int bstart, bend, orig_brid; ++ int size; ++ int err = 0; ++ ++ fgen = atomic_read(&UNIONFS_F(file)->generation); ++ ++ /* ++ * There are two cases we are interested in. The first is if the ++ * generation is lower than the super-block. The second is if ++ * someone has copied up this file from underneath us, we also need ++ * to refresh things. ++ */ ++ if (d_deleted(dentry) || ++ (sbgen <= fgen && ++ dbstart(dentry) == fbstart(file) && ++ unionfs_lower_file(file))) ++ goto out_may_copyup; ++ ++ /* save orig branch ID */ ++ orig_brid = UNIONFS_F(file)->saved_branch_ids[fbstart(file)]; ++ ++ /* First we throw out the existing files. */ ++ cleanup_file(file); ++ ++ /* Now we reopen the file(s) as in unionfs_open. */ ++ bstart = fbstart(file) = dbstart(dentry); ++ bend = fbend(file) = dbend(dentry); ++ ++ size = sizeof(struct file *) * sbmax(sb); ++ UNIONFS_F(file)->lower_files = kzalloc(size, GFP_KERNEL); ++ if (unlikely(!UNIONFS_F(file)->lower_files)) { ++ err = -ENOMEM; ++ goto out; ++ } ++ size = sizeof(int) * sbmax(sb); ++ UNIONFS_F(file)->saved_branch_ids = kzalloc(size, GFP_KERNEL); ++ if (unlikely(!UNIONFS_F(file)->saved_branch_ids)) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ if (S_ISDIR(dentry->d_inode->i_mode)) { ++ /* We need to open all the files. */ ++ err = open_all_files(file); ++ if (err) ++ goto out; ++ } else { ++ int new_brid; ++ /* We only open the highest priority branch. */ ++ err = open_highest_file(file, willwrite); ++ if (err) ++ goto out; ++ new_brid = UNIONFS_F(file)->saved_branch_ids[fbstart(file)]; ++ if (unlikely(new_brid != orig_brid && sbgen > fgen)) { ++ /* ++ * If we re-opened the file on a different branch ++ * than the original one, and this was due to a new ++ * branch inserted, then update the mnt counts of ++ * the old and new branches accordingly. ++ */ ++ unionfs_mntget(dentry, bstart); ++ unionfs_mntput(sb->s_root, ++ branch_id_to_idx(sb, orig_brid)); ++ } ++ /* regular files have only one open lower file */ ++ fbend(file) = fbstart(file); ++ } ++ atomic_set(&UNIONFS_F(file)->generation, ++ atomic_read(&UNIONFS_I(dentry->d_inode)->generation)); ++ ++out_may_copyup: ++ /* Copyup on the first write to a file on a readonly branch. */ ++ if (willwrite && IS_WRITE_FLAG(file->f_flags) && ++ !IS_WRITE_FLAG(unionfs_lower_file(file)->f_flags) && ++ is_robranch(dentry)) { ++ pr_debug("unionfs: do delay copyup of \"%s\"\n", ++ dentry->d_name.name); ++ err = do_delayed_copyup(file, parent); ++ /* regular files have only one open lower file */ ++ if (!err && !S_ISDIR(dentry->d_inode->i_mode)) ++ fbend(file) = fbstart(file); ++ } ++ ++out: ++ if (err) { ++ kfree(UNIONFS_F(file)->lower_files); ++ kfree(UNIONFS_F(file)->saved_branch_ids); ++ } ++ return err; ++} ++ ++/* ++ * Revalidate the struct file ++ * @file: file to revalidate ++ * @parent: parent dentry (locked by caller) ++ * @willwrite: true if caller may cause changes to the file; false otherwise. ++ * Caller must lock/unlock dentry's branch configuration. ++ */ ++int unionfs_file_revalidate(struct file *file, struct dentry *parent, ++ bool willwrite) ++{ ++ struct super_block *sb; ++ struct dentry *dentry; ++ int sbgen, dgen; ++ int err = 0; ++ ++ dentry = file->f_path.dentry; ++ sb = dentry->d_sb; ++ verify_locked(dentry); ++ verify_locked(parent); ++ ++ /* ++ * First revalidate the dentry inside struct file, ++ * but not unhashed dentries. ++ */ ++ if (!d_deleted(dentry) && ++ !__unionfs_d_revalidate(dentry, parent, willwrite)) { ++ err = -ESTALE; ++ goto out; ++ } ++ ++ sbgen = atomic_read(&UNIONFS_SB(sb)->generation); ++ dgen = atomic_read(&UNIONFS_D(dentry)->generation); ++ ++ if (unlikely(sbgen > dgen)) { /* XXX: should never happen */ ++ pr_debug("unionfs: failed to revalidate dentry (%s)\n", ++ dentry->d_name.name); ++ err = -ESTALE; ++ goto out; ++ } ++ ++ err = __unionfs_file_revalidate(file, dentry, parent, sb, ++ sbgen, dgen, willwrite); ++out: ++ return err; ++} ++ ++/* unionfs_open helper function: open a directory */ ++static int __open_dir(struct inode *inode, struct file *file) ++{ ++ struct dentry *lower_dentry; ++ struct file *lower_file; ++ int bindex, bstart, bend; ++ struct vfsmount *mnt; ++ ++ bstart = fbstart(file) = dbstart(file->f_path.dentry); ++ bend = fbend(file) = dbend(file->f_path.dentry); ++ ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ lower_dentry = ++ unionfs_lower_dentry_idx(file->f_path.dentry, bindex); ++ if (!lower_dentry) ++ continue; ++ ++ dget(lower_dentry); ++ unionfs_mntget(file->f_path.dentry, bindex); ++ mnt = unionfs_lower_mnt_idx(file->f_path.dentry, bindex); ++ lower_file = dentry_open(lower_dentry, mnt, file->f_flags, ++ current_cred()); ++ if (IS_ERR(lower_file)) ++ return PTR_ERR(lower_file); ++ ++ unionfs_set_lower_file_idx(file, bindex, lower_file); ++ ++ /* ++ * The branchget goes after the open, because otherwise ++ * we would miss the reference on release. ++ */ ++ branchget(inode->i_sb, bindex); ++ } ++ ++ return 0; ++} ++ ++/* unionfs_open helper function: open a file */ ++static int __open_file(struct inode *inode, struct file *file, ++ struct dentry *parent) ++{ ++ struct dentry *lower_dentry; ++ struct file *lower_file; ++ int lower_flags; ++ int bindex, bstart, bend; ++ ++ lower_dentry = unionfs_lower_dentry(file->f_path.dentry); ++ lower_flags = file->f_flags; ++ ++ bstart = fbstart(file) = dbstart(file->f_path.dentry); ++ bend = fbend(file) = dbend(file->f_path.dentry); ++ ++ /* ++ * check for the permission for lower file. If the error is ++ * COPYUP_ERR, copyup the file. ++ */ ++ if (lower_dentry->d_inode && is_robranch(file->f_path.dentry)) { ++ /* ++ * if the open will change the file, copy it up otherwise ++ * defer it. ++ */ ++ if (lower_flags & O_TRUNC) { ++ int size = 0; ++ int err = -EROFS; ++ ++ /* copyup the file */ ++ for (bindex = bstart - 1; bindex >= 0; bindex--) { ++ err = copyup_file(parent->d_inode, file, ++ bstart, bindex, size); ++ if (!err) ++ break; ++ } ++ return err; ++ } else { ++ /* ++ * turn off writeable flags, to force delayed copyup ++ * by caller. ++ */ ++ lower_flags &= ~(OPEN_WRITE_FLAGS); ++ } ++ } ++ ++ dget(lower_dentry); ++ ++ /* ++ * dentry_open will decrement mnt refcnt if err. ++ * otherwise fput() will do an mntput() for us upon file close. ++ */ ++ unionfs_mntget(file->f_path.dentry, bstart); ++ lower_file = ++ dentry_open(lower_dentry, ++ unionfs_lower_mnt_idx(file->f_path.dentry, bstart), ++ lower_flags, current_cred()); ++ if (IS_ERR(lower_file)) ++ return PTR_ERR(lower_file); ++ ++ unionfs_set_lower_file(file, lower_file); ++ branchget(inode->i_sb, bstart); ++ ++ return 0; ++} ++ ++int unionfs_open(struct inode *inode, struct file *file) ++{ ++ int err = 0; ++ struct file *lower_file = NULL; ++ struct dentry *dentry = file->f_path.dentry; ++ struct dentry *parent; ++ int bindex = 0, bstart = 0, bend = 0; ++ int size; ++ int valid = 0; ++ ++ unionfs_read_lock(inode->i_sb, UNIONFS_SMUTEX_PARENT); ++ parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); ++ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); ++ ++ /* don't open unhashed/deleted files */ ++ if (d_deleted(dentry)) { ++ err = -ENOENT; ++ goto out_nofree; ++ } ++ ++ /* XXX: should I change 'false' below to the 'willwrite' flag? */ ++ valid = __unionfs_d_revalidate(dentry, parent, false); ++ if (unlikely(!valid)) { ++ err = -ESTALE; ++ goto out_nofree; ++ } ++ ++ file->private_data = ++ kzalloc(sizeof(struct unionfs_file_info), GFP_KERNEL); ++ if (unlikely(!UNIONFS_F(file))) { ++ err = -ENOMEM; ++ goto out_nofree; ++ } ++ fbstart(file) = -1; ++ fbend(file) = -1; ++ atomic_set(&UNIONFS_F(file)->generation, ++ atomic_read(&UNIONFS_I(inode)->generation)); ++ ++ size = sizeof(struct file *) * sbmax(inode->i_sb); ++ UNIONFS_F(file)->lower_files = kzalloc(size, GFP_KERNEL); ++ if (unlikely(!UNIONFS_F(file)->lower_files)) { ++ err = -ENOMEM; ++ goto out; ++ } ++ size = sizeof(int) * sbmax(inode->i_sb); ++ UNIONFS_F(file)->saved_branch_ids = kzalloc(size, GFP_KERNEL); ++ if (unlikely(!UNIONFS_F(file)->saved_branch_ids)) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ bstart = fbstart(file) = dbstart(dentry); ++ bend = fbend(file) = dbend(dentry); ++ ++ /* ++ * open all directories and make the unionfs file struct point to ++ * these lower file structs ++ */ ++ if (S_ISDIR(inode->i_mode)) ++ err = __open_dir(inode, file); /* open a dir */ ++ else ++ err = __open_file(inode, file, parent); /* open a file */ ++ ++ /* freeing the allocated resources, and fput the opened files */ ++ if (err) { ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ lower_file = unionfs_lower_file_idx(file, bindex); ++ if (!lower_file) ++ continue; ++ ++ branchput(dentry->d_sb, bindex); ++ /* fput calls dput for lower_dentry */ ++ fput(lower_file); ++ } ++ } ++ ++out: ++ if (err) { ++ kfree(UNIONFS_F(file)->lower_files); ++ kfree(UNIONFS_F(file)->saved_branch_ids); ++ kfree(UNIONFS_F(file)); ++ } ++out_nofree: ++ if (!err) { ++ unionfs_postcopyup_setmnt(dentry); ++ unionfs_copy_attr_times(inode); ++ unionfs_check_file(file); ++ unionfs_check_inode(inode); ++ } ++ unionfs_unlock_dentry(dentry); ++ unionfs_unlock_parent(dentry, parent); ++ unionfs_read_unlock(inode->i_sb); ++ return err; ++} ++ ++/* ++ * release all lower object references & free the file info structure ++ * ++ * No need to grab sb info's rwsem. ++ */ ++int unionfs_file_release(struct inode *inode, struct file *file) ++{ ++ struct file *lower_file = NULL; ++ struct unionfs_file_info *fileinfo; ++ struct unionfs_inode_info *inodeinfo; ++ struct super_block *sb = inode->i_sb; ++ struct dentry *dentry = file->f_path.dentry; ++ struct dentry *parent; ++ int bindex, bstart, bend; ++ int fgen, err = 0; ++ ++ /* ++ * Since mm/memory.c:might_fault() (under PROVE_LOCKING) was ++ * modified in 2.6.29-rc1 to call might_lock_read on mmap_sem, this ++ * has been causing false positives in file system stacking layers. ++ * In particular, our ->mmap is called after sys_mmap2 already holds ++ * mmap_sem, then we lock our own mutexes; but earlier, it's ++ * possible for lockdep to have locked our mutexes first, and then ++ * we call a lower ->readdir which could call might_fault. The ++ * different ordering of the locks is what lockdep complains about ++ * -- unnecessarily. Therefore, we have no choice but to tell ++ * lockdep to temporarily turn off lockdep here. Note: the comments ++ * inside might_sleep also suggest that it would have been ++ * nicer to only annotate paths that needs that might_lock_read. ++ */ ++ lockdep_off(); ++ unionfs_read_lock(sb, UNIONFS_SMUTEX_PARENT); ++ parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); ++ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); ++ ++ /* ++ * We try to revalidate, but the VFS ignores return return values ++ * from file->release, so we must always try to succeed here, ++ * including to do the kfree and dput below. So if revalidation ++ * failed, all we can do is print some message and keep going. ++ */ ++ err = unionfs_file_revalidate(file, parent, ++ UNIONFS_F(file)->wrote_to_file); ++ if (!err) ++ unionfs_check_file(file); ++ fileinfo = UNIONFS_F(file); ++ BUG_ON(file->f_path.dentry->d_inode != inode); ++ inodeinfo = UNIONFS_I(inode); ++ ++ /* fput all the lower files */ ++ fgen = atomic_read(&fileinfo->generation); ++ bstart = fbstart(file); ++ bend = fbend(file); ++ ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ lower_file = unionfs_lower_file_idx(file, bindex); ++ ++ if (lower_file) { ++ unionfs_set_lower_file_idx(file, bindex, NULL); ++ fput(lower_file); ++ branchput(sb, bindex); ++ } ++ ++ /* if there are no more refs to the dentry, dput it */ ++ if (d_deleted(dentry)) { ++ dput(unionfs_lower_dentry_idx(dentry, bindex)); ++ unionfs_set_lower_dentry_idx(dentry, bindex, NULL); ++ } ++ } ++ ++ kfree(fileinfo->lower_files); ++ kfree(fileinfo->saved_branch_ids); ++ ++ if (fileinfo->rdstate) { ++ fileinfo->rdstate->access = jiffies; ++ spin_lock(&inodeinfo->rdlock); ++ inodeinfo->rdcount++; ++ list_add_tail(&fileinfo->rdstate->cache, ++ &inodeinfo->readdircache); ++ mark_inode_dirty(inode); ++ spin_unlock(&inodeinfo->rdlock); ++ fileinfo->rdstate = NULL; ++ } ++ kfree(fileinfo); ++ ++ unionfs_unlock_dentry(dentry); ++ unionfs_unlock_parent(dentry, parent); ++ unionfs_read_unlock(sb); ++ lockdep_on(); ++ return err; ++} ++ ++/* pass the ioctl to the lower fs */ ++static long do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ struct file *lower_file; ++ int err; ++ ++ lower_file = unionfs_lower_file(file); ++ ++ err = -ENOTTY; ++ if (!lower_file || !lower_file->f_op) ++ goto out; ++ if (lower_file->f_op->unlocked_ioctl) { ++ err = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg); ++ } else if (lower_file->f_op->ioctl) { ++ lock_kernel(); ++ err = lower_file->f_op->ioctl( ++ lower_file->f_path.dentry->d_inode, ++ lower_file, cmd, arg); ++ unlock_kernel(); ++ } ++ ++out: ++ return err; ++} ++ ++/* ++ * return to user-space the branch indices containing the file in question ++ * ++ * We use fd_set and therefore we are limited to the number of the branches ++ * to FD_SETSIZE, which is currently 1024 - plenty for most people ++ */ ++static int unionfs_ioctl_queryfile(struct file *file, struct dentry *parent, ++ unsigned int cmd, unsigned long arg) ++{ ++ int err = 0; ++ fd_set branchlist; ++ int bstart = 0, bend = 0, bindex = 0; ++ int orig_bstart, orig_bend; ++ struct dentry *dentry, *lower_dentry; ++ struct vfsmount *mnt; ++ ++ dentry = file->f_path.dentry; ++ orig_bstart = dbstart(dentry); ++ orig_bend = dbend(dentry); ++ err = unionfs_partial_lookup(dentry, parent); ++ if (err) ++ goto out; ++ bstart = dbstart(dentry); ++ bend = dbend(dentry); ++ ++ FD_ZERO(&branchlist); ++ ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); ++ if (!lower_dentry) ++ continue; ++ if (likely(lower_dentry->d_inode)) ++ FD_SET(bindex, &branchlist); ++ /* purge any lower objects after partial_lookup */ ++ if (bindex < orig_bstart || bindex > orig_bend) { ++ dput(lower_dentry); ++ unionfs_set_lower_dentry_idx(dentry, bindex, NULL); ++ iput(unionfs_lower_inode_idx(dentry->d_inode, bindex)); ++ unionfs_set_lower_inode_idx(dentry->d_inode, bindex, ++ NULL); ++ mnt = unionfs_lower_mnt_idx(dentry, bindex); ++ if (!mnt) ++ continue; ++ unionfs_mntput(dentry, bindex); ++ unionfs_set_lower_mnt_idx(dentry, bindex, NULL); ++ } ++ } ++ /* restore original dentry's offsets */ ++ dbstart(dentry) = orig_bstart; ++ dbend(dentry) = orig_bend; ++ ibstart(dentry->d_inode) = orig_bstart; ++ ibend(dentry->d_inode) = orig_bend; ++ ++ err = copy_to_user((void __user *)arg, &branchlist, sizeof(fd_set)); ++ if (unlikely(err)) ++ err = -EFAULT; ++ ++out: ++ return err < 0 ? err : bend; ++} ++ ++long unionfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ long err; ++ struct dentry *dentry = file->f_path.dentry; ++ struct dentry *parent; ++ ++ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT); ++ parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); ++ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); ++ ++ err = unionfs_file_revalidate(file, parent, true); ++ if (unlikely(err)) ++ goto out; ++ ++ /* check if asked for local commands */ ++ switch (cmd) { ++ case UNIONFS_IOCTL_INCGEN: ++ /* Increment the superblock generation count */ ++ pr_info("unionfs: incgen ioctl deprecated; " ++ "use \"-o remount,incgen\"\n"); ++ err = -ENOSYS; ++ break; ++ ++ case UNIONFS_IOCTL_QUERYFILE: ++ /* Return list of branches containing the given file */ ++ err = unionfs_ioctl_queryfile(file, parent, cmd, arg); ++ break; ++ ++ default: ++ /* pass the ioctl down */ ++ err = do_ioctl(file, cmd, arg); ++ break; ++ } ++ ++out: ++ unionfs_check_file(file); ++ unionfs_unlock_dentry(dentry); ++ unionfs_unlock_parent(dentry, parent); ++ unionfs_read_unlock(dentry->d_sb); ++ return err; ++} ++ ++int unionfs_flush(struct file *file, fl_owner_t id) ++{ ++ int err = 0; ++ struct file *lower_file = NULL; ++ struct dentry *dentry = file->f_path.dentry; ++ struct dentry *parent; ++ int bindex, bstart, bend; ++ ++ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT); ++ parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); ++ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); ++ ++ err = unionfs_file_revalidate(file, parent, ++ UNIONFS_F(file)->wrote_to_file); ++ if (unlikely(err)) ++ goto out; ++ unionfs_check_file(file); ++ ++ bstart = fbstart(file); ++ bend = fbend(file); ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ lower_file = unionfs_lower_file_idx(file, bindex); ++ ++ if (lower_file && lower_file->f_op && ++ lower_file->f_op->flush) { ++ err = lower_file->f_op->flush(lower_file, id); ++ if (err) ++ goto out; ++ } ++ ++ } ++ ++out: ++ if (!err) ++ unionfs_check_file(file); ++ unionfs_unlock_dentry(dentry); ++ unionfs_unlock_parent(dentry, parent); ++ unionfs_read_unlock(dentry->d_sb); ++ return err; ++} +diff --git a/fs/unionfs/copyup.c b/fs/unionfs/copyup.c +new file mode 100644 +index 0000000..bba3a75 +--- /dev/null ++++ b/fs/unionfs/copyup.c +@@ -0,0 +1,896 @@ ++/* ++ * Copyright (c) 2003-2010 Erez Zadok ++ * Copyright (c) 2003-2006 Charles P. Wright ++ * Copyright (c) 2005-2007 Josef 'Jeff' Sipek ++ * Copyright (c) 2005-2006 Junjiro Okajima ++ * Copyright (c) 2005 Arun M. Krishnakumar ++ * Copyright (c) 2004-2006 David P. Quigley ++ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair ++ * Copyright (c) 2003 Puja Gupta ++ * Copyright (c) 2003 Harikesavan Krishnan ++ * Copyright (c) 2003-2010 Stony Brook University ++ * Copyright (c) 2003-2010 The Research Foundation of SUNY ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "union.h" ++ ++/* ++ * For detailed explanation of copyup see: ++ * Documentation/filesystems/unionfs/concepts.txt ++ */ ++ ++#ifdef CONFIG_UNION_FS_XATTR ++/* copyup all extended attrs for a given dentry */ ++static int copyup_xattrs(struct dentry *old_lower_dentry, ++ struct dentry *new_lower_dentry) ++{ ++ int err = 0; ++ ssize_t list_size = -1; ++ char *name_list = NULL; ++ char *attr_value = NULL; ++ char *name_list_buf = NULL; ++ ++ /* query the actual size of the xattr list */ ++ list_size = vfs_listxattr(old_lower_dentry, NULL, 0); ++ if (list_size <= 0) { ++ err = list_size; ++ goto out; ++ } ++ ++ /* allocate space for the actual list */ ++ name_list = unionfs_xattr_alloc(list_size + 1, XATTR_LIST_MAX); ++ if (unlikely(!name_list || IS_ERR(name_list))) { ++ err = PTR_ERR(name_list); ++ goto out; ++ } ++ ++ name_list_buf = name_list; /* save for kfree at end */ ++ ++ /* now get the actual xattr list of the source file */ ++ list_size = vfs_listxattr(old_lower_dentry, name_list, list_size); ++ if (list_size <= 0) { ++ err = list_size; ++ goto out; ++ } ++ ++ /* allocate space to hold each xattr's value */ ++ attr_value = unionfs_xattr_alloc(XATTR_SIZE_MAX, XATTR_SIZE_MAX); ++ if (unlikely(!attr_value || IS_ERR(attr_value))) { ++ err = PTR_ERR(name_list); ++ goto out; ++ } ++ ++ /* in a loop, get and set each xattr from src to dst file */ ++ while (*name_list) { ++ ssize_t size; ++ ++ /* Lock here since vfs_getxattr doesn't lock for us */ ++ mutex_lock(&old_lower_dentry->d_inode->i_mutex); ++ size = vfs_getxattr(old_lower_dentry, name_list, ++ attr_value, XATTR_SIZE_MAX); ++ mutex_unlock(&old_lower_dentry->d_inode->i_mutex); ++ if (size < 0) { ++ err = size; ++ goto out; ++ } ++ if (size > XATTR_SIZE_MAX) { ++ err = -E2BIG; ++ goto out; ++ } ++ /* Don't lock here since vfs_setxattr does it for us. */ ++ err = vfs_setxattr(new_lower_dentry, name_list, attr_value, ++ size, 0); ++ /* ++ * Selinux depends on "security.*" xattrs, so to maintain ++ * the security of copied-up files, if Selinux is active, ++ * then we must copy these xattrs as well. So we need to ++ * temporarily get FOWNER privileges. ++ * XXX: move entire copyup code to SIOQ. ++ */ ++ if (err == -EPERM && !capable(CAP_FOWNER)) { ++ const struct cred *old_creds; ++ struct cred *new_creds; ++ ++ new_creds = prepare_creds(); ++ if (unlikely(!new_creds)) { ++ err = -ENOMEM; ++ goto out; ++ } ++ cap_raise(new_creds->cap_effective, CAP_FOWNER); ++ old_creds = override_creds(new_creds); ++ err = vfs_setxattr(new_lower_dentry, name_list, ++ attr_value, size, 0); ++ revert_creds(old_creds); ++ } ++ if (err < 0) ++ goto out; ++ name_list += strlen(name_list) + 1; ++ } ++out: ++ unionfs_xattr_kfree(name_list_buf); ++ unionfs_xattr_kfree(attr_value); ++ /* Ignore if xattr isn't supported */ ++ if (err == -ENOTSUPP || err == -EOPNOTSUPP) ++ err = 0; ++ return err; ++} ++#endif /* CONFIG_UNION_FS_XATTR */ ++ ++/* ++ * Determine the mode based on the copyup flags, and the existing dentry. ++ * ++ * Handle file systems which may not support certain options. For example ++ * jffs2 doesn't allow one to chmod a symlink. So we ignore such harmless ++ * errors, rather than propagating them up, which results in copyup errors ++ * and errors returned back to users. ++ */ ++static int copyup_permissions(struct super_block *sb, ++ struct dentry *old_lower_dentry, ++ struct dentry *new_lower_dentry) ++{ ++ struct inode *i = old_lower_dentry->d_inode; ++ struct iattr newattrs; ++ int err; ++ ++ newattrs.ia_atime = i->i_atime; ++ newattrs.ia_mtime = i->i_mtime; ++ newattrs.ia_ctime = i->i_ctime; ++ newattrs.ia_gid = i->i_gid; ++ newattrs.ia_uid = i->i_uid; ++ newattrs.ia_valid = ATTR_CTIME | ATTR_ATIME | ATTR_MTIME | ++ ATTR_ATIME_SET | ATTR_MTIME_SET | ATTR_FORCE | ++ ATTR_GID | ATTR_UID; ++ mutex_lock(&new_lower_dentry->d_inode->i_mutex); ++ err = notify_change(new_lower_dentry, &newattrs); ++ if (err) ++ goto out; ++ ++ /* now try to change the mode and ignore EOPNOTSUPP on symlinks */ ++ newattrs.ia_mode = i->i_mode; ++ newattrs.ia_valid = ATTR_MODE | ATTR_FORCE; ++ err = notify_change(new_lower_dentry, &newattrs); ++ if (err == -EOPNOTSUPP && ++ S_ISLNK(new_lower_dentry->d_inode->i_mode)) { ++ printk(KERN_WARNING ++ "unionfs: changing \"%s\" symlink mode unsupported\n", ++ new_lower_dentry->d_name.name); ++ err = 0; ++ } ++ ++out: ++ mutex_unlock(&new_lower_dentry->d_inode->i_mutex); ++ return err; ++} ++ ++/* ++ * create the new device/file/directory - use copyup_permission to copyup ++ * times, and mode ++ * ++ * if the object being copied up is a regular file, the file is only created, ++ * the contents have to be copied up separately ++ */ ++static int __copyup_ndentry(struct dentry *old_lower_dentry, ++ struct dentry *new_lower_dentry, ++ struct dentry *new_lower_parent_dentry, ++ char *symbuf) ++{ ++ int err = 0; ++ umode_t old_mode = old_lower_dentry->d_inode->i_mode; ++ struct sioq_args args; ++ ++ if (S_ISDIR(old_mode)) { ++ args.mkdir.parent = new_lower_parent_dentry->d_inode; ++ args.mkdir.dentry = new_lower_dentry; ++ args.mkdir.mode = old_mode; ++ ++ run_sioq(__unionfs_mkdir, &args); ++ err = args.err; ++ } else if (S_ISLNK(old_mode)) { ++ args.symlink.parent = new_lower_parent_dentry->d_inode; ++ args.symlink.dentry = new_lower_dentry; ++ args.symlink.symbuf = symbuf; ++ ++ run_sioq(__unionfs_symlink, &args); ++ err = args.err; ++ } else if (S_ISBLK(old_mode) || S_ISCHR(old_mode) || ++ S_ISFIFO(old_mode) || S_ISSOCK(old_mode)) { ++ args.mknod.parent = new_lower_parent_dentry->d_inode; ++ args.mknod.dentry = new_lower_dentry; ++ args.mknod.mode = old_mode; ++ args.mknod.dev = old_lower_dentry->d_inode->i_rdev; ++ ++ run_sioq(__unionfs_mknod, &args); ++ err = args.err; ++ } else if (S_ISREG(old_mode)) { ++ struct nameidata nd; ++ err = init_lower_nd(&nd, LOOKUP_CREATE); ++ if (unlikely(err < 0)) ++ goto out; ++ args.create.nd = &nd; ++ args.create.parent = new_lower_parent_dentry->d_inode; ++ args.create.dentry = new_lower_dentry; ++ args.create.mode = old_mode; ++ ++ run_sioq(__unionfs_create, &args); ++ err = args.err; ++ release_lower_nd(&nd, err); ++ } else { ++ printk(KERN_CRIT "unionfs: unknown inode type %d\n", ++ old_mode); ++ BUG(); ++ } ++ ++out: ++ return err; ++} ++ ++static int __copyup_reg_data(struct dentry *dentry, ++ struct dentry *new_lower_dentry, int new_bindex, ++ struct dentry *old_lower_dentry, int old_bindex, ++ struct file **copyup_file, loff_t len) ++{ ++ struct super_block *sb = dentry->d_sb; ++ struct file *input_file; ++ struct file *output_file; ++ struct vfsmount *output_mnt; ++ mm_segment_t old_fs; ++ char *buf = NULL; ++ ssize_t read_bytes, write_bytes; ++ loff_t size; ++ int err = 0; ++ ++ /* open old file */ ++ unionfs_mntget(dentry, old_bindex); ++ branchget(sb, old_bindex); ++ /* dentry_open calls dput and mntput if it returns an error */ ++ input_file = dentry_open(old_lower_dentry, ++ unionfs_lower_mnt_idx(dentry, old_bindex), ++ O_RDONLY | O_LARGEFILE, current_cred()); ++ if (IS_ERR(input_file)) { ++ dput(old_lower_dentry); ++ err = PTR_ERR(input_file); ++ goto out; ++ } ++ if (unlikely(!input_file->f_op || !input_file->f_op->read)) { ++ err = -EINVAL; ++ goto out_close_in; ++ } ++ ++ /* open new file */ ++ dget(new_lower_dentry); ++ output_mnt = unionfs_mntget(sb->s_root, new_bindex); ++ branchget(sb, new_bindex); ++ output_file = dentry_open(new_lower_dentry, output_mnt, ++ O_RDWR | O_LARGEFILE, current_cred()); ++ if (IS_ERR(output_file)) { ++ err = PTR_ERR(output_file); ++ goto out_close_in2; ++ } ++ if (unlikely(!output_file->f_op || !output_file->f_op->write)) { ++ err = -EINVAL; ++ goto out_close_out; ++ } ++ ++ /* allocating a buffer */ ++ buf = kmalloc(PAGE_SIZE, GFP_KERNEL); ++ if (unlikely(!buf)) { ++ err = -ENOMEM; ++ goto out_close_out; ++ } ++ ++ input_file->f_pos = 0; ++ output_file->f_pos = 0; ++ ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ ++ size = len; ++ err = 0; ++ do { ++ if (len >= PAGE_SIZE) ++ size = PAGE_SIZE; ++ else if ((len < PAGE_SIZE) && (len > 0)) ++ size = len; ++ ++ len -= PAGE_SIZE; ++ ++ read_bytes = ++ input_file->f_op->read(input_file, ++ (char __user *)buf, size, ++ &input_file->f_pos); ++ if (read_bytes <= 0) { ++ err = read_bytes; ++ break; ++ } ++ ++ /* see Documentation/filesystems/unionfs/issues.txt */ ++ lockdep_off(); ++ write_bytes = ++ output_file->f_op->write(output_file, ++ (char __user *)buf, ++ read_bytes, ++ &output_file->f_pos); ++ lockdep_on(); ++ if ((write_bytes < 0) || (write_bytes < read_bytes)) { ++ err = write_bytes; ++ break; ++ } ++ } while ((read_bytes > 0) && (len > 0)); ++ ++ set_fs(old_fs); ++ ++ kfree(buf); ++ ++ if (!err) ++ err = output_file->f_op->fsync(output_file, 0); ++ ++ if (err) ++ goto out_close_out; ++ ++ if (copyup_file) { ++ *copyup_file = output_file; ++ goto out_close_in; ++ } ++ ++out_close_out: ++ fput(output_file); ++ ++out_close_in2: ++ branchput(sb, new_bindex); ++ ++out_close_in: ++ fput(input_file); ++ ++out: ++ branchput(sb, old_bindex); ++ ++ return err; ++} ++ ++/* ++ * dput the lower references for old and new dentry & clear a lower dentry ++ * pointer ++ */ ++static void __clear(struct dentry *dentry, struct dentry *old_lower_dentry, ++ int old_bstart, int old_bend, ++ struct dentry *new_lower_dentry, int new_bindex) ++{ ++ /* get rid of the lower dentry and all its traces */ ++ unionfs_set_lower_dentry_idx(dentry, new_bindex, NULL); ++ dbstart(dentry) = old_bstart; ++ dbend(dentry) = old_bend; ++ ++ dput(new_lower_dentry); ++ dput(old_lower_dentry); ++} ++ ++/* ++ * Copy up a dentry to a file of specified name. ++ * ++ * @dir: used to pull the ->i_sb to access other branches ++ * @dentry: the non-negative dentry whose lower_inode we should copy ++ * @bstart: the branch of the lower_inode to copy from ++ * @new_bindex: the branch to create the new file in ++ * @name: the name of the file to create ++ * @namelen: length of @name ++ * @copyup_file: the "struct file" to return (optional) ++ * @len: how many bytes to copy-up? ++ */ ++int copyup_dentry(struct inode *dir, struct dentry *dentry, int bstart, ++ int new_bindex, const char *name, int namelen, ++ struct file **copyup_file, loff_t len) ++{ ++ struct dentry *new_lower_dentry; ++ struct dentry *old_lower_dentry = NULL; ++ struct super_block *sb; ++ int err = 0; ++ int old_bindex; ++ int old_bstart; ++ int old_bend; ++ struct dentry *new_lower_parent_dentry = NULL; ++ mm_segment_t oldfs; ++ char *symbuf = NULL; ++ ++ verify_locked(dentry); ++ ++ old_bindex = bstart; ++ old_bstart = dbstart(dentry); ++ old_bend = dbend(dentry); ++ ++ BUG_ON(new_bindex < 0); ++ BUG_ON(new_bindex >= old_bindex); ++ ++ sb = dir->i_sb; ++ ++ err = is_robranch_super(sb, new_bindex); ++ if (err) ++ goto out; ++ ++ /* Create the directory structure above this dentry. */ ++ new_lower_dentry = create_parents(dir, dentry, name, new_bindex); ++ if (IS_ERR(new_lower_dentry)) { ++ err = PTR_ERR(new_lower_dentry); ++ goto out; ++ } ++ ++ old_lower_dentry = unionfs_lower_dentry_idx(dentry, old_bindex); ++ /* we conditionally dput this old_lower_dentry at end of function */ ++ dget(old_lower_dentry); ++ ++ /* For symlinks, we must read the link before we lock the directory. */ ++ if (S_ISLNK(old_lower_dentry->d_inode->i_mode)) { ++ ++ symbuf = kmalloc(PATH_MAX, GFP_KERNEL); ++ if (unlikely(!symbuf)) { ++ __clear(dentry, old_lower_dentry, ++ old_bstart, old_bend, ++ new_lower_dentry, new_bindex); ++ err = -ENOMEM; ++ goto out_free; ++ } ++ ++ oldfs = get_fs(); ++ set_fs(KERNEL_DS); ++ err = old_lower_dentry->d_inode->i_op->readlink( ++ old_lower_dentry, ++ (char __user *)symbuf, ++ PATH_MAX); ++ set_fs(oldfs); ++ if (err < 0) { ++ __clear(dentry, old_lower_dentry, ++ old_bstart, old_bend, ++ new_lower_dentry, new_bindex); ++ goto out_free; ++ } ++ symbuf[err] = '\0'; ++ } ++ ++ /* Now we lock the parent, and create the object in the new branch. */ ++ new_lower_parent_dentry = lock_parent(new_lower_dentry); ++ ++ /* create the new inode */ ++ err = __copyup_ndentry(old_lower_dentry, new_lower_dentry, ++ new_lower_parent_dentry, symbuf); ++ ++ if (err) { ++ __clear(dentry, old_lower_dentry, ++ old_bstart, old_bend, ++ new_lower_dentry, new_bindex); ++ goto out_unlock; ++ } ++ ++ /* We actually copyup the file here. */ ++ if (S_ISREG(old_lower_dentry->d_inode->i_mode)) ++ err = __copyup_reg_data(dentry, new_lower_dentry, new_bindex, ++ old_lower_dentry, old_bindex, ++ copyup_file, len); ++ if (err) ++ goto out_unlink; ++ ++ /* Set permissions. */ ++ err = copyup_permissions(sb, old_lower_dentry, new_lower_dentry); ++ if (err) ++ goto out_unlink; ++ ++#ifdef CONFIG_UNION_FS_XATTR ++ /* Selinux uses extended attributes for permissions. */ ++ err = copyup_xattrs(old_lower_dentry, new_lower_dentry); ++ if (err) ++ goto out_unlink; ++#endif /* CONFIG_UNION_FS_XATTR */ ++ ++ /* do not allow files getting deleted to be re-interposed */ ++ if (!d_deleted(dentry)) ++ unionfs_reinterpose(dentry); ++ ++ goto out_unlock; ++ ++out_unlink: ++ /* ++ * copyup failed, because we possibly ran out of space or ++ * quota, or something else happened so let's unlink; we don't ++ * really care about the return value of vfs_unlink ++ */ ++ vfs_unlink(new_lower_parent_dentry->d_inode, new_lower_dentry); ++ ++ if (copyup_file) { ++ /* need to close the file */ ++ ++ fput(*copyup_file); ++ branchput(sb, new_bindex); ++ } ++ ++ /* ++ * TODO: should we reset the error to something like -EIO? ++ * ++ * If we don't reset, the user may get some nonsensical errors, but ++ * on the other hand, if we reset to EIO, we guarantee that the user ++ * will get a "confusing" error message. ++ */ ++ ++out_unlock: ++ unlock_dir(new_lower_parent_dentry); ++ ++out_free: ++ /* ++ * If old_lower_dentry was not a file, then we need to dput it. If ++ * it was a file, then it was already dput indirectly by other ++ * functions we call above which operate on regular files. ++ */ ++ if (old_lower_dentry && old_lower_dentry->d_inode && ++ !S_ISREG(old_lower_dentry->d_inode->i_mode)) ++ dput(old_lower_dentry); ++ kfree(symbuf); ++ ++ if (err) { ++ /* ++ * if directory creation succeeded, but inode copyup failed, ++ * then purge new dentries. ++ */ ++ if (dbstart(dentry) < old_bstart && ++ ibstart(dentry->d_inode) > dbstart(dentry)) ++ __clear(dentry, NULL, old_bstart, old_bend, ++ unionfs_lower_dentry(dentry), dbstart(dentry)); ++ goto out; ++ } ++ if (!S_ISDIR(dentry->d_inode->i_mode)) { ++ unionfs_postcopyup_release(dentry); ++ if (!unionfs_lower_inode(dentry->d_inode)) { ++ /* ++ * If we got here, then we copied up to an ++ * unlinked-open file, whose name is .unionfsXXXXX. ++ */ ++ struct inode *inode = new_lower_dentry->d_inode; ++ atomic_inc(&inode->i_count); ++ unionfs_set_lower_inode_idx(dentry->d_inode, ++ ibstart(dentry->d_inode), ++ inode); ++ } ++ } ++ unionfs_postcopyup_setmnt(dentry); ++ /* sync inode times from copied-up inode to our inode */ ++ unionfs_copy_attr_times(dentry->d_inode); ++ unionfs_check_inode(dir); ++ unionfs_check_dentry(dentry); ++out: ++ return err; ++} ++ ++/* ++ * This function creates a copy of a file represented by 'file' which ++ * currently resides in branch 'bstart' to branch 'new_bindex.' The copy ++ * will be named "name". ++ */ ++int copyup_named_file(struct inode *dir, struct file *file, char *name, ++ int bstart, int new_bindex, loff_t len) ++{ ++ int err = 0; ++ struct file *output_file = NULL; ++ ++ err = copyup_dentry(dir, file->f_path.dentry, bstart, new_bindex, ++ name, strlen(name), &output_file, len); ++ if (!err) { ++ fbstart(file) = new_bindex; ++ unionfs_set_lower_file_idx(file, new_bindex, output_file); ++ } ++ ++ return err; ++} ++ ++/* ++ * This function creates a copy of a file represented by 'file' which ++ * currently resides in branch 'bstart' to branch 'new_bindex'. ++ */ ++int copyup_file(struct inode *dir, struct file *file, int bstart, ++ int new_bindex, loff_t len) ++{ ++ int err = 0; ++ struct file *output_file = NULL; ++ struct dentry *dentry = file->f_path.dentry; ++ ++ err = copyup_dentry(dir, dentry, bstart, new_bindex, ++ dentry->d_name.name, dentry->d_name.len, ++ &output_file, len); ++ if (!err) { ++ fbstart(file) = new_bindex; ++ unionfs_set_lower_file_idx(file, new_bindex, output_file); ++ } ++ ++ return err; ++} ++ ++/* purge a dentry's lower-branch states (dput/mntput, etc.) */ ++static void __cleanup_dentry(struct dentry *dentry, int bindex, ++ int old_bstart, int old_bend) ++{ ++ int loop_start; ++ int loop_end; ++ int new_bstart = -1; ++ int new_bend = -1; ++ int i; ++ ++ loop_start = min(old_bstart, bindex); ++ loop_end = max(old_bend, bindex); ++ ++ /* ++ * This loop sets the bstart and bend for the new dentry by ++ * traversing from left to right. It also dputs all negative ++ * dentries except bindex ++ */ ++ for (i = loop_start; i <= loop_end; i++) { ++ if (!unionfs_lower_dentry_idx(dentry, i)) ++ continue; ++ ++ if (i == bindex) { ++ new_bend = i; ++ if (new_bstart < 0) ++ new_bstart = i; ++ continue; ++ } ++ ++ if (!unionfs_lower_dentry_idx(dentry, i)->d_inode) { ++ dput(unionfs_lower_dentry_idx(dentry, i)); ++ unionfs_set_lower_dentry_idx(dentry, i, NULL); ++ ++ unionfs_mntput(dentry, i); ++ unionfs_set_lower_mnt_idx(dentry, i, NULL); ++ } else { ++ if (new_bstart < 0) ++ new_bstart = i; ++ new_bend = i; ++ } ++ } ++ ++ if (new_bstart < 0) ++ new_bstart = bindex; ++ if (new_bend < 0) ++ new_bend = bindex; ++ dbstart(dentry) = new_bstart; ++ dbend(dentry) = new_bend; ++ ++} ++ ++/* set lower inode ptr and update bstart & bend if necessary */ ++static void __set_inode(struct dentry *upper, struct dentry *lower, ++ int bindex) ++{ ++ unionfs_set_lower_inode_idx(upper->d_inode, bindex, ++ igrab(lower->d_inode)); ++ if (likely(ibstart(upper->d_inode) > bindex)) ++ ibstart(upper->d_inode) = bindex; ++ if (likely(ibend(upper->d_inode) < bindex)) ++ ibend(upper->d_inode) = bindex; ++ ++} ++ ++/* set lower dentry ptr and update bstart & bend if necessary */ ++static void __set_dentry(struct dentry *upper, struct dentry *lower, ++ int bindex) ++{ ++ unionfs_set_lower_dentry_idx(upper, bindex, lower); ++ if (likely(dbstart(upper) > bindex)) ++ dbstart(upper) = bindex; ++ if (likely(dbend(upper) < bindex)) ++ dbend(upper) = bindex; ++} ++ ++/* ++ * This function replicates the directory structure up-to given dentry ++ * in the bindex branch. ++ */ ++struct dentry *create_parents(struct inode *dir, struct dentry *dentry, ++ const char *name, int bindex) ++{ ++ int err; ++ struct dentry *child_dentry; ++ struct dentry *parent_dentry; ++ struct dentry *lower_parent_dentry = NULL; ++ struct dentry *lower_dentry = NULL; ++ const char *childname; ++ unsigned int childnamelen; ++ int nr_dentry; ++ int count = 0; ++ int old_bstart; ++ int old_bend; ++ struct dentry **path = NULL; ++ struct super_block *sb; ++ ++ verify_locked(dentry); ++ ++ err = is_robranch_super(dir->i_sb, bindex); ++ if (err) { ++ lower_dentry = ERR_PTR(err); ++ goto out; ++ } ++ ++ old_bstart = dbstart(dentry); ++ old_bend = dbend(dentry); ++ ++ lower_dentry = ERR_PTR(-ENOMEM); ++ ++ /* There is no sense allocating any less than the minimum. */ ++ nr_dentry = 1; ++ path = kmalloc(nr_dentry * sizeof(struct dentry *), GFP_KERNEL); ++ if (unlikely(!path)) ++ goto out; ++ ++ /* assume the negative dentry of unionfs as the parent dentry */ ++ parent_dentry = dentry; ++ ++ /* ++ * This loop finds the first parent that exists in the given branch. ++ * We start building the directory structure from there. At the end ++ * of the loop, the following should hold: ++ * - child_dentry is the first nonexistent child ++ * - parent_dentry is the first existent parent ++ * - path[0] is the = deepest child ++ * - path[count] is the first child to create ++ */ ++ do { ++ child_dentry = parent_dentry; ++ ++ /* find the parent directory dentry in unionfs */ ++ parent_dentry = dget_parent(child_dentry); ++ ++ /* find out the lower_parent_dentry in the given branch */ ++ lower_parent_dentry = ++ unionfs_lower_dentry_idx(parent_dentry, bindex); ++ ++ /* grow path table */ ++ if (count == nr_dentry) { ++ void *p; ++ ++ nr_dentry *= 2; ++ p = krealloc(path, nr_dentry * sizeof(struct dentry *), ++ GFP_KERNEL); ++ if (unlikely(!p)) { ++ lower_dentry = ERR_PTR(-ENOMEM); ++ goto out; ++ } ++ path = p; ++ } ++ ++ /* store the child dentry */ ++ path[count++] = child_dentry; ++ } while (!lower_parent_dentry); ++ count--; ++ ++ sb = dentry->d_sb; ++ ++ /* ++ * This code goes between the begin/end labels and basically ++ * emulates a while(child_dentry != dentry), only cleaner and ++ * shorter than what would be a much longer while loop. ++ */ ++begin: ++ /* get lower parent dir in the current branch */ ++ lower_parent_dentry = unionfs_lower_dentry_idx(parent_dentry, bindex); ++ dput(parent_dentry); ++ ++ /* init the values to lookup */ ++ childname = child_dentry->d_name.name; ++ childnamelen = child_dentry->d_name.len; ++ ++ if (child_dentry != dentry) { ++ /* lookup child in the underlying file system */ ++ lower_dentry = lookup_lck_len(childname, lower_parent_dentry, ++ childnamelen); ++ if (IS_ERR(lower_dentry)) ++ goto out; ++ } else { ++ /* ++ * Is the name a whiteout of the child name ? lookup the ++ * whiteout child in the underlying file system ++ */ ++ lower_dentry = lookup_lck_len(name, lower_parent_dentry, ++ strlen(name)); ++ if (IS_ERR(lower_dentry)) ++ goto out; ++ ++ /* Replace the current dentry (if any) with the new one */ ++ dput(unionfs_lower_dentry_idx(dentry, bindex)); ++ unionfs_set_lower_dentry_idx(dentry, bindex, ++ lower_dentry); ++ ++ __cleanup_dentry(dentry, bindex, old_bstart, old_bend); ++ goto out; ++ } ++ ++ if (lower_dentry->d_inode) { ++ /* ++ * since this already exists we dput to avoid ++ * multiple references on the same dentry ++ */ ++ dput(lower_dentry); ++ } else { ++ struct sioq_args args; ++ ++ /* it's a negative dentry, create a new dir */ ++ lower_parent_dentry = lock_parent(lower_dentry); ++ ++ args.mkdir.parent = lower_parent_dentry->d_inode; ++ args.mkdir.dentry = lower_dentry; ++ args.mkdir.mode = child_dentry->d_inode->i_mode; ++ ++ run_sioq(__unionfs_mkdir, &args); ++ err = args.err; ++ ++ if (!err) ++ err = copyup_permissions(dir->i_sb, child_dentry, ++ lower_dentry); ++ unlock_dir(lower_parent_dentry); ++ if (err) { ++ dput(lower_dentry); ++ lower_dentry = ERR_PTR(err); ++ goto out; ++ } ++ ++ } ++ ++ __set_inode(child_dentry, lower_dentry, bindex); ++ __set_dentry(child_dentry, lower_dentry, bindex); ++ /* ++ * update times of this dentry, but also the parent, because if ++ * we changed, the parent may have changed too. ++ */ ++ fsstack_copy_attr_times(parent_dentry->d_inode, ++ lower_parent_dentry->d_inode); ++ unionfs_copy_attr_times(child_dentry->d_inode); ++ ++ parent_dentry = child_dentry; ++ child_dentry = path[--count]; ++ goto begin; ++out: ++ /* cleanup any leftover locks from the do/while loop above */ ++ if (IS_ERR(lower_dentry)) ++ while (count) ++ dput(path[count--]); ++ kfree(path); ++ return lower_dentry; ++} ++ ++/* ++ * Post-copyup helper to ensure we have valid mnts: set lower mnt of ++ * dentry+parents to the first parent node that has an mnt. ++ */ ++void unionfs_postcopyup_setmnt(struct dentry *dentry) ++{ ++ struct dentry *parent, *hasone; ++ int bindex = dbstart(dentry); ++ ++ if (unionfs_lower_mnt_idx(dentry, bindex)) ++ return; ++ hasone = dentry->d_parent; ++ /* this loop should stop at root dentry */ ++ while (!unionfs_lower_mnt_idx(hasone, bindex)) ++ hasone = hasone->d_parent; ++ parent = dentry; ++ while (!unionfs_lower_mnt_idx(parent, bindex)) { ++ unionfs_set_lower_mnt_idx(parent, bindex, ++ unionfs_mntget(hasone, bindex)); ++ parent = parent->d_parent; ++ } ++} ++ ++/* ++ * Post-copyup helper to release all non-directory source objects of a ++ * copied-up file. Regular files should have only one lower object. ++ */ ++void unionfs_postcopyup_release(struct dentry *dentry) ++{ ++ int bstart, bend; ++ ++ BUG_ON(S_ISDIR(dentry->d_inode->i_mode)); ++ bstart = dbstart(dentry); ++ bend = dbend(dentry); ++ ++ path_put_lowers(dentry, bstart + 1, bend, false); ++ iput_lowers(dentry->d_inode, bstart + 1, bend, false); ++ ++ dbend(dentry) = bstart; ++ ibend(dentry->d_inode) = ibstart(dentry->d_inode) = bstart; ++} +diff --git a/fs/unionfs/debug.c b/fs/unionfs/debug.c +new file mode 100644 +index 0000000..acc44bd +--- /dev/null ++++ b/fs/unionfs/debug.c +@@ -0,0 +1,533 @@ ++/* ++ * Copyright (c) 2003-2010 Erez Zadok ++ * Copyright (c) 2005-2007 Josef 'Jeff' Sipek ++ * Copyright (c) 2003-2010 Stony Brook University ++ * Copyright (c) 2003-2010 The Research Foundation of SUNY ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "union.h" ++ ++/* ++ * Helper debugging functions for maintainers (and for users to report back ++ * useful information back to maintainers) ++ */ ++ ++/* it's always useful to know what part of the code called us */ ++#define PRINT_CALLER(fname, fxn, line) \ ++ do { \ ++ if (!printed_caller) { \ ++ pr_debug("PC:%s:%s:%d\n", (fname), (fxn), (line)); \ ++ printed_caller = 1; \ ++ } \ ++ } while (0) ++ ++/* ++ * __unionfs_check_{inode,dentry,file} perform exhaustive sanity checking on ++ * the fan-out of various Unionfs objects. We check that no lower objects ++ * exist outside the start/end branch range; that all objects within are ++ * non-NULL (with some allowed exceptions); that for every lower file ++ * there's a lower dentry+inode; that the start/end ranges match for all ++ * corresponding lower objects; that open files/symlinks have only one lower ++ * objects, but directories can have several; and more. ++ */ ++void __unionfs_check_inode(const struct inode *inode, ++ const char *fname, const char *fxn, int line) ++{ ++ int bindex; ++ int istart, iend; ++ struct inode *lower_inode; ++ struct super_block *sb; ++ int printed_caller = 0; ++ void *poison_ptr; ++ ++ /* for inodes now */ ++ BUG_ON(!inode); ++ sb = inode->i_sb; ++ istart = ibstart(inode); ++ iend = ibend(inode); ++ /* don't check inode if no lower branches */ ++ if (istart < 0 && iend < 0) ++ return; ++ if (unlikely(istart > iend)) { ++ PRINT_CALLER(fname, fxn, line); ++ pr_debug(" Ci0: inode=%p istart/end=%d:%d\n", ++ inode, istart, iend); ++ } ++ if (unlikely((istart == -1 && iend != -1) || ++ (istart != -1 && iend == -1))) { ++ PRINT_CALLER(fname, fxn, line); ++ pr_debug(" Ci1: inode=%p istart/end=%d:%d\n", ++ inode, istart, iend); ++ } ++ if (!S_ISDIR(inode->i_mode)) { ++ if (unlikely(iend != istart)) { ++ PRINT_CALLER(fname, fxn, line); ++ pr_debug(" Ci2: inode=%p istart=%d iend=%d\n", ++ inode, istart, iend); ++ } ++ } ++ ++ for (bindex = sbstart(sb); bindex < sbmax(sb); bindex++) { ++ if (unlikely(!UNIONFS_I(inode))) { ++ PRINT_CALLER(fname, fxn, line); ++ pr_debug(" Ci3: no inode_info %p\n", inode); ++ return; ++ } ++ if (unlikely(!UNIONFS_I(inode)->lower_inodes)) { ++ PRINT_CALLER(fname, fxn, line); ++ pr_debug(" Ci4: no lower_inodes %p\n", inode); ++ return; ++ } ++ lower_inode = unionfs_lower_inode_idx(inode, bindex); ++ if (lower_inode) { ++ memset(&poison_ptr, POISON_INUSE, sizeof(void *)); ++ if (unlikely(bindex < istart || bindex > iend)) { ++ PRINT_CALLER(fname, fxn, line); ++ pr_debug(" Ci5: inode/linode=%p:%p bindex=%d " ++ "istart/end=%d:%d\n", inode, ++ lower_inode, bindex, istart, iend); ++ } else if (unlikely(lower_inode == poison_ptr)) { ++ /* freed inode! */ ++ PRINT_CALLER(fname, fxn, line); ++ pr_debug(" Ci6: inode/linode=%p:%p bindex=%d " ++ "istart/end=%d:%d\n", inode, ++ lower_inode, bindex, istart, iend); ++ } ++ continue; ++ } ++ /* if we get here, then lower_inode == NULL */ ++ if (bindex < istart || bindex > iend) ++ continue; ++ /* ++ * directories can have NULL lower inodes in b/t start/end, ++ * but NOT if at the start/end range. ++ */ ++ if (unlikely(S_ISDIR(inode->i_mode) && ++ bindex > istart && bindex < iend)) ++ continue; ++ PRINT_CALLER(fname, fxn, line); ++ pr_debug(" Ci7: inode/linode=%p:%p " ++ "bindex=%d istart/end=%d:%d\n", ++ inode, lower_inode, bindex, istart, iend); ++ } ++} ++ ++void __unionfs_check_dentry(const struct dentry *dentry, ++ const char *fname, const char *fxn, int line) ++{ ++ int bindex; ++ int dstart, dend, istart, iend; ++ struct dentry *lower_dentry; ++ struct inode *inode, *lower_inode; ++ struct super_block *sb; ++ struct vfsmount *lower_mnt; ++ int printed_caller = 0; ++ void *poison_ptr; ++ ++ BUG_ON(!dentry); ++ sb = dentry->d_sb; ++ inode = dentry->d_inode; ++ dstart = dbstart(dentry); ++ dend = dbend(dentry); ++ /* don't check dentry/mnt if no lower branches */ ++ if (dstart < 0 && dend < 0) ++ goto check_inode; ++ BUG_ON(dstart > dend); ++ ++ if (unlikely((dstart == -1 && dend != -1) || ++ (dstart != -1 && dend == -1))) { ++ PRINT_CALLER(fname, fxn, line); ++ pr_debug(" CD0: dentry=%p dstart/end=%d:%d\n", ++ dentry, dstart, dend); ++ } ++ /* ++ * check for NULL dentries inside the start/end range, or ++ * non-NULL dentries outside the start/end range. ++ */ ++ for (bindex = sbstart(sb); bindex < sbmax(sb); bindex++) { ++ lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); ++ if (lower_dentry) { ++ if (unlikely(bindex < dstart || bindex > dend)) { ++ PRINT_CALLER(fname, fxn, line); ++ pr_debug(" CD1: dentry/lower=%p:%p(%p) " ++ "bindex=%d dstart/end=%d:%d\n", ++ dentry, lower_dentry, ++ (lower_dentry ? lower_dentry->d_inode : ++ (void *) -1L), ++ bindex, dstart, dend); ++ } ++ } else { /* lower_dentry == NULL */ ++ if (bindex < dstart || bindex > dend) ++ continue; ++ /* ++ * Directories can have NULL lower inodes in b/t ++ * start/end, but NOT if at the start/end range. ++ * Ignore this rule, however, if this is a NULL ++ * dentry or a deleted dentry. ++ */ ++ if (unlikely(!d_deleted((struct dentry *) dentry) && ++ inode && ++ !(inode && S_ISDIR(inode->i_mode) && ++ bindex > dstart && bindex < dend))) { ++ PRINT_CALLER(fname, fxn, line); ++ pr_debug(" CD2: dentry/lower=%p:%p(%p) " ++ "bindex=%d dstart/end=%d:%d\n", ++ dentry, lower_dentry, ++ (lower_dentry ? ++ lower_dentry->d_inode : ++ (void *) -1L), ++ bindex, dstart, dend); ++ } ++ } ++ } ++ ++ /* check for vfsmounts same as for dentries */ ++ for (bindex = sbstart(sb); bindex < sbmax(sb); bindex++) { ++ lower_mnt = unionfs_lower_mnt_idx(dentry, bindex); ++ if (lower_mnt) { ++ if (unlikely(bindex < dstart || bindex > dend)) { ++ PRINT_CALLER(fname, fxn, line); ++ pr_debug(" CM0: dentry/lmnt=%p:%p bindex=%d " ++ "dstart/end=%d:%d\n", dentry, ++ lower_mnt, bindex, dstart, dend); ++ } ++ } else { /* lower_mnt == NULL */ ++ if (bindex < dstart || bindex > dend) ++ continue; ++ /* ++ * Directories can have NULL lower inodes in b/t ++ * start/end, but NOT if at the start/end range. ++ * Ignore this rule, however, if this is a NULL ++ * dentry. ++ */ ++ if (unlikely(inode && ++ !(inode && S_ISDIR(inode->i_mode) && ++ bindex > dstart && bindex < dend))) { ++ PRINT_CALLER(fname, fxn, line); ++ pr_debug(" CM1: dentry/lmnt=%p:%p " ++ "bindex=%d dstart/end=%d:%d\n", ++ dentry, lower_mnt, bindex, ++ dstart, dend); ++ } ++ } ++ } ++ ++check_inode: ++ /* for inodes now */ ++ if (!inode) ++ return; ++ istart = ibstart(inode); ++ iend = ibend(inode); ++ /* don't check inode if no lower branches */ ++ if (istart < 0 && iend < 0) ++ return; ++ BUG_ON(istart > iend); ++ if (unlikely((istart == -1 && iend != -1) || ++ (istart != -1 && iend == -1))) { ++ PRINT_CALLER(fname, fxn, line); ++ pr_debug(" CI0: dentry/inode=%p:%p istart/end=%d:%d\n", ++ dentry, inode, istart, iend); ++ } ++ if (unlikely(istart != dstart)) { ++ PRINT_CALLER(fname, fxn, line); ++ pr_debug(" CI1: dentry/inode=%p:%p istart=%d dstart=%d\n", ++ dentry, inode, istart, dstart); ++ } ++ if (unlikely(iend != dend)) { ++ PRINT_CALLER(fname, fxn, line); ++ pr_debug(" CI2: dentry/inode=%p:%p iend=%d dend=%d\n", ++ dentry, inode, iend, dend); ++ } ++ ++ if (!S_ISDIR(inode->i_mode)) { ++ if (unlikely(dend != dstart)) { ++ PRINT_CALLER(fname, fxn, line); ++ pr_debug(" CI3: dentry/inode=%p:%p dstart=%d dend=%d\n", ++ dentry, inode, dstart, dend); ++ } ++ if (unlikely(iend != istart)) { ++ PRINT_CALLER(fname, fxn, line); ++ pr_debug(" CI4: dentry/inode=%p:%p istart=%d iend=%d\n", ++ dentry, inode, istart, iend); ++ } ++ } ++ ++ for (bindex = sbstart(sb); bindex < sbmax(sb); bindex++) { ++ lower_inode = unionfs_lower_inode_idx(inode, bindex); ++ if (lower_inode) { ++ memset(&poison_ptr, POISON_INUSE, sizeof(void *)); ++ if (unlikely(bindex < istart || bindex > iend)) { ++ PRINT_CALLER(fname, fxn, line); ++ pr_debug(" CI5: dentry/linode=%p:%p bindex=%d " ++ "istart/end=%d:%d\n", dentry, ++ lower_inode, bindex, istart, iend); ++ } else if (unlikely(lower_inode == poison_ptr)) { ++ /* freed inode! */ ++ PRINT_CALLER(fname, fxn, line); ++ pr_debug(" CI6: dentry/linode=%p:%p bindex=%d " ++ "istart/end=%d:%d\n", dentry, ++ lower_inode, bindex, istart, iend); ++ } ++ continue; ++ } ++ /* if we get here, then lower_inode == NULL */ ++ if (bindex < istart || bindex > iend) ++ continue; ++ /* ++ * directories can have NULL lower inodes in b/t start/end, ++ * but NOT if at the start/end range. ++ */ ++ if (unlikely(S_ISDIR(inode->i_mode) && ++ bindex > istart && bindex < iend)) ++ continue; ++ PRINT_CALLER(fname, fxn, line); ++ pr_debug(" CI7: dentry/linode=%p:%p " ++ "bindex=%d istart/end=%d:%d\n", ++ dentry, lower_inode, bindex, istart, iend); ++ } ++ ++ /* ++ * If it's a directory, then intermediate objects b/t start/end can ++ * be NULL. But, check that all three are NULL: lower dentry, mnt, ++ * and inode. ++ */ ++ if (dstart >= 0 && dend >= 0 && S_ISDIR(inode->i_mode)) ++ for (bindex = dstart+1; bindex < dend; bindex++) { ++ lower_inode = unionfs_lower_inode_idx(inode, bindex); ++ lower_dentry = unionfs_lower_dentry_idx(dentry, ++ bindex); ++ lower_mnt = unionfs_lower_mnt_idx(dentry, bindex); ++ if (unlikely(!((lower_inode && lower_dentry && ++ lower_mnt) || ++ (!lower_inode && ++ !lower_dentry && !lower_mnt)))) { ++ PRINT_CALLER(fname, fxn, line); ++ pr_debug(" Cx: lmnt/ldentry/linode=%p:%p:%p " ++ "bindex=%d dstart/end=%d:%d\n", ++ lower_mnt, lower_dentry, lower_inode, ++ bindex, dstart, dend); ++ } ++ } ++ /* check if lower inode is newer than upper one (it shouldn't) */ ++ if (unlikely(is_newer_lower(dentry) && !is_negative_lower(dentry))) { ++ PRINT_CALLER(fname, fxn, line); ++ for (bindex = ibstart(inode); bindex <= ibend(inode); ++ bindex++) { ++ lower_inode = unionfs_lower_inode_idx(inode, bindex); ++ if (unlikely(!lower_inode)) ++ continue; ++ pr_debug(" CI8: bindex=%d mtime/lmtime=%lu.%lu/%lu.%lu " ++ "ctime/lctime=%lu.%lu/%lu.%lu\n", ++ bindex, ++ inode->i_mtime.tv_sec, ++ inode->i_mtime.tv_nsec, ++ lower_inode->i_mtime.tv_sec, ++ lower_inode->i_mtime.tv_nsec, ++ inode->i_ctime.tv_sec, ++ inode->i_ctime.tv_nsec, ++ lower_inode->i_ctime.tv_sec, ++ lower_inode->i_ctime.tv_nsec); ++ } ++ } ++} ++ ++void __unionfs_check_file(const struct file *file, ++ const char *fname, const char *fxn, int line) ++{ ++ int bindex; ++ int dstart, dend, fstart, fend; ++ struct dentry *dentry; ++ struct file *lower_file; ++ struct inode *inode; ++ struct super_block *sb; ++ int printed_caller = 0; ++ ++ BUG_ON(!file); ++ dentry = file->f_path.dentry; ++ sb = dentry->d_sb; ++ dstart = dbstart(dentry); ++ dend = dbend(dentry); ++ BUG_ON(dstart > dend); ++ fstart = fbstart(file); ++ fend = fbend(file); ++ BUG_ON(fstart > fend); ++ ++ if (unlikely((fstart == -1 && fend != -1) || ++ (fstart != -1 && fend == -1))) { ++ PRINT_CALLER(fname, fxn, line); ++ pr_debug(" CF0: file/dentry=%p:%p fstart/end=%d:%d\n", ++ file, dentry, fstart, fend); ++ } ++ if (unlikely(fstart != dstart)) { ++ PRINT_CALLER(fname, fxn, line); ++ pr_debug(" CF1: file/dentry=%p:%p fstart=%d dstart=%d\n", ++ file, dentry, fstart, dstart); ++ } ++ if (unlikely(fend != dend)) { ++ PRINT_CALLER(fname, fxn, line); ++ pr_debug(" CF2: file/dentry=%p:%p fend=%d dend=%d\n", ++ file, dentry, fend, dend); ++ } ++ inode = dentry->d_inode; ++ if (!S_ISDIR(inode->i_mode)) { ++ if (unlikely(fend != fstart)) { ++ PRINT_CALLER(fname, fxn, line); ++ pr_debug(" CF3: file/inode=%p:%p fstart=%d fend=%d\n", ++ file, inode, fstart, fend); ++ } ++ if (unlikely(dend != dstart)) { ++ PRINT_CALLER(fname, fxn, line); ++ pr_debug(" CF4: file/dentry=%p:%p dstart=%d dend=%d\n", ++ file, dentry, dstart, dend); ++ } ++ } ++ ++ /* ++ * check for NULL dentries inside the start/end range, or ++ * non-NULL dentries outside the start/end range. ++ */ ++ for (bindex = sbstart(sb); bindex < sbmax(sb); bindex++) { ++ lower_file = unionfs_lower_file_idx(file, bindex); ++ if (lower_file) { ++ if (unlikely(bindex < fstart || bindex > fend)) { ++ PRINT_CALLER(fname, fxn, line); ++ pr_debug(" CF5: file/lower=%p:%p bindex=%d " ++ "fstart/end=%d:%d\n", file, ++ lower_file, bindex, fstart, fend); ++ } ++ } else { /* lower_file == NULL */ ++ if (bindex >= fstart && bindex <= fend) { ++ /* ++ * directories can have NULL lower inodes in ++ * b/t start/end, but NOT if at the ++ * start/end range. ++ */ ++ if (unlikely(!(S_ISDIR(inode->i_mode) && ++ bindex > fstart && ++ bindex < fend))) { ++ PRINT_CALLER(fname, fxn, line); ++ pr_debug(" CF6: file/lower=%p:%p " ++ "bindex=%d fstart/end=%d:%d\n", ++ file, lower_file, bindex, ++ fstart, fend); ++ } ++ } ++ } ++ } ++ ++ __unionfs_check_dentry(dentry, fname, fxn, line); ++} ++ ++void __unionfs_check_nd(const struct nameidata *nd, ++ const char *fname, const char *fxn, int line) ++{ ++ struct file *file; ++ int printed_caller = 0; ++ ++ if (unlikely(!nd)) ++ return; ++ if (nd->flags & LOOKUP_OPEN) { ++ file = nd->intent.open.file; ++ if (unlikely(file->f_path.dentry && ++ strcmp(file->f_path.dentry->d_sb->s_type->name, ++ UNIONFS_NAME))) { ++ PRINT_CALLER(fname, fxn, line); ++ pr_debug(" CND1: lower_file of type %s\n", ++ file->f_path.dentry->d_sb->s_type->name); ++ BUG(); ++ } ++ } ++} ++ ++/* useful to track vfsmount leaks that could cause EBUSY on unmount */ ++void __show_branch_counts(const struct super_block *sb, ++ const char *file, const char *fxn, int line) ++{ ++ int i; ++ struct vfsmount *mnt; ++ ++ pr_debug("BC:"); ++ for (i = 0; i < sbmax(sb); i++) { ++ if (likely(sb->s_root)) ++ mnt = UNIONFS_D(sb->s_root)->lower_paths[i].mnt; ++ else ++ mnt = NULL; ++ printk(KERN_CONT "%d:", ++ (mnt ? atomic_read(&mnt->mnt_count) : -99)); ++ } ++ printk(KERN_CONT "%s:%s:%d\n", file, fxn, line); ++} ++ ++void __show_inode_times(const struct inode *inode, ++ const char *file, const char *fxn, int line) ++{ ++ struct inode *lower_inode; ++ int bindex; ++ ++ for (bindex = ibstart(inode); bindex <= ibend(inode); bindex++) { ++ lower_inode = unionfs_lower_inode_idx(inode, bindex); ++ if (unlikely(!lower_inode)) ++ continue; ++ pr_debug("IT(%lu:%d): %s:%s:%d " ++ "um=%lu/%lu lm=%lu/%lu uc=%lu/%lu lc=%lu/%lu\n", ++ inode->i_ino, bindex, ++ file, fxn, line, ++ inode->i_mtime.tv_sec, inode->i_mtime.tv_nsec, ++ lower_inode->i_mtime.tv_sec, ++ lower_inode->i_mtime.tv_nsec, ++ inode->i_ctime.tv_sec, inode->i_ctime.tv_nsec, ++ lower_inode->i_ctime.tv_sec, ++ lower_inode->i_ctime.tv_nsec); ++ } ++} ++ ++void __show_dinode_times(const struct dentry *dentry, ++ const char *file, const char *fxn, int line) ++{ ++ struct inode *inode = dentry->d_inode; ++ struct inode *lower_inode; ++ int bindex; ++ ++ for (bindex = ibstart(inode); bindex <= ibend(inode); bindex++) { ++ lower_inode = unionfs_lower_inode_idx(inode, bindex); ++ if (!lower_inode) ++ continue; ++ pr_debug("DT(%s:%lu:%d): %s:%s:%d " ++ "um=%lu/%lu lm=%lu/%lu uc=%lu/%lu lc=%lu/%lu\n", ++ dentry->d_name.name, inode->i_ino, bindex, ++ file, fxn, line, ++ inode->i_mtime.tv_sec, inode->i_mtime.tv_nsec, ++ lower_inode->i_mtime.tv_sec, ++ lower_inode->i_mtime.tv_nsec, ++ inode->i_ctime.tv_sec, inode->i_ctime.tv_nsec, ++ lower_inode->i_ctime.tv_sec, ++ lower_inode->i_ctime.tv_nsec); ++ } ++} ++ ++void __show_inode_counts(const struct inode *inode, ++ const char *file, const char *fxn, int line) ++{ ++ struct inode *lower_inode; ++ int bindex; ++ ++ if (unlikely(!inode)) { ++ pr_debug("SiC: Null inode\n"); ++ return; ++ } ++ for (bindex = sbstart(inode->i_sb); bindex <= sbend(inode->i_sb); ++ bindex++) { ++ lower_inode = unionfs_lower_inode_idx(inode, bindex); ++ if (unlikely(!lower_inode)) ++ continue; ++ pr_debug("SIC(%lu:%d:%d): lc=%d %s:%s:%d\n", ++ inode->i_ino, bindex, ++ atomic_read(&(inode)->i_count), ++ atomic_read(&(lower_inode)->i_count), ++ file, fxn, line); ++ } ++} +diff --git a/fs/unionfs/dentry.c b/fs/unionfs/dentry.c +new file mode 100644 +index 0000000..a0c3bba +--- /dev/null ++++ b/fs/unionfs/dentry.c +@@ -0,0 +1,397 @@ ++/* ++ * Copyright (c) 2003-2010 Erez Zadok ++ * Copyright (c) 2003-2006 Charles P. Wright ++ * Copyright (c) 2005-2007 Josef 'Jeff' Sipek ++ * Copyright (c) 2005-2006 Junjiro Okajima ++ * Copyright (c) 2005 Arun M. Krishnakumar ++ * Copyright (c) 2004-2006 David P. Quigley ++ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair ++ * Copyright (c) 2003 Puja Gupta ++ * Copyright (c) 2003 Harikesavan Krishnan ++ * Copyright (c) 2003-2010 Stony Brook University ++ * Copyright (c) 2003-2010 The Research Foundation of SUNY ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "union.h" ++ ++bool is_negative_lower(const struct dentry *dentry) ++{ ++ int bindex; ++ struct dentry *lower_dentry; ++ ++ BUG_ON(!dentry); ++ /* cache coherency: check if file was deleted on lower branch */ ++ if (dbstart(dentry) < 0) ++ return true; ++ for (bindex = dbstart(dentry); bindex <= dbend(dentry); bindex++) { ++ lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); ++ /* unhashed (i.e., unlinked) lower dentries don't count */ ++ if (lower_dentry && lower_dentry->d_inode && ++ !d_deleted(lower_dentry) && ++ !(lower_dentry->d_flags & DCACHE_NFSFS_RENAMED)) ++ return false; ++ } ++ return true; ++} ++ ++static inline void __dput_lowers(struct dentry *dentry, int start, int end) ++{ ++ struct dentry *lower_dentry; ++ int bindex; ++ ++ if (start < 0) ++ return; ++ for (bindex = start; bindex <= end; bindex++) { ++ lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); ++ if (!lower_dentry) ++ continue; ++ unionfs_set_lower_dentry_idx(dentry, bindex, NULL); ++ dput(lower_dentry); ++ } ++} ++ ++/* ++ * Purge and invalidate as many data pages of a unionfs inode. This is ++ * called when the lower inode has changed, and we want to force processes ++ * to re-get the new data. ++ */ ++static inline void purge_inode_data(struct inode *inode) ++{ ++ /* remove all non-private mappings */ ++ unmap_mapping_range(inode->i_mapping, 0, 0, 0); ++ /* invalidate as many pages as possible */ ++ invalidate_mapping_pages(inode->i_mapping, 0, -1); ++ /* ++ * Don't try to truncate_inode_pages here, because this could lead ++ * to a deadlock between some of address_space ops and dentry ++ * revalidation: the address space op is invoked with a lock on our ++ * own page, and truncate_inode_pages will block on locked pages. ++ */ ++} ++ ++/* ++ * Revalidate a single file/symlink/special dentry. Assume that info nodes ++ * of the @dentry and its @parent are locked. Assume parent is valid, ++ * otherwise return false (and let's hope the VFS will try to re-lookup this ++ * dentry). Returns true if valid, false otherwise. ++ */ ++bool __unionfs_d_revalidate(struct dentry *dentry, struct dentry *parent, ++ bool willwrite) ++{ ++ bool valid = true; /* default is valid */ ++ struct dentry *lower_dentry; ++ struct dentry *result; ++ int bindex, bstart, bend; ++ int sbgen, dgen, pdgen; ++ int positive = 0; ++ int interpose_flag; ++ ++ verify_locked(dentry); ++ verify_locked(parent); ++ ++ /* if the dentry is unhashed, do NOT revalidate */ ++ if (d_deleted(dentry)) ++ goto out; ++ ++ dgen = atomic_read(&UNIONFS_D(dentry)->generation); ++ ++ if (is_newer_lower(dentry)) { ++ /* root dentry is always valid */ ++ if (IS_ROOT(dentry)) { ++ unionfs_copy_attr_times(dentry->d_inode); ++ } else { ++ /* ++ * reset generation number to zero, guaranteed to be ++ * "old" ++ */ ++ dgen = 0; ++ atomic_set(&UNIONFS_D(dentry)->generation, dgen); ++ } ++ if (!willwrite) ++ purge_inode_data(dentry->d_inode); ++ } ++ ++ sbgen = atomic_read(&UNIONFS_SB(dentry->d_sb)->generation); ++ ++ BUG_ON(dbstart(dentry) == -1); ++ if (dentry->d_inode) ++ positive = 1; ++ ++ /* if our dentry is valid, then validate all lower ones */ ++ if (sbgen == dgen) ++ goto validate_lowers; ++ ++ /* The root entry should always be valid */ ++ BUG_ON(IS_ROOT(dentry)); ++ ++ /* We can't work correctly if our parent isn't valid. */ ++ pdgen = atomic_read(&UNIONFS_D(parent)->generation); ++ ++ /* Free the pointers for our inodes and this dentry. */ ++ path_put_lowers_all(dentry, false); ++ ++ interpose_flag = INTERPOSE_REVAL_NEG; ++ if (positive) { ++ interpose_flag = INTERPOSE_REVAL; ++ iput_lowers_all(dentry->d_inode, true); ++ } ++ ++ if (realloc_dentry_private_data(dentry) != 0) { ++ valid = false; ++ goto out; ++ } ++ ++ result = unionfs_lookup_full(dentry, parent, interpose_flag); ++ if (result) { ++ if (IS_ERR(result)) { ++ valid = false; ++ goto out; ++ } ++ /* ++ * current unionfs_lookup_backend() doesn't return ++ * a valid dentry ++ */ ++ dput(dentry); ++ dentry = result; ++ } ++ ++ if (unlikely(positive && is_negative_lower(dentry))) { ++ /* call make_bad_inode here ? */ ++ d_drop(dentry); ++ valid = false; ++ goto out; ++ } ++ ++ /* ++ * if we got here then we have revalidated our dentry and all lower ++ * ones, so we can return safely. ++ */ ++ if (!valid) /* lower dentry revalidation failed */ ++ goto out; ++ ++ /* ++ * If the parent's gen no. matches the superblock's gen no., then ++ * we can update our denty's gen no. If they didn't match, then it ++ * was OK to revalidate this dentry with a stale parent, but we'll ++ * purposely not update our dentry's gen no. (so it can be redone); ++ * and, we'll mark our parent dentry as invalid so it'll force it ++ * (and our dentry) to be revalidated. ++ */ ++ if (pdgen == sbgen) ++ atomic_set(&UNIONFS_D(dentry)->generation, sbgen); ++ goto out; ++ ++validate_lowers: ++ ++ /* The revalidation must occur across all branches */ ++ bstart = dbstart(dentry); ++ bend = dbend(dentry); ++ BUG_ON(bstart == -1); ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); ++ if (!lower_dentry || !lower_dentry->d_op ++ || !lower_dentry->d_op->d_revalidate) ++ continue; ++ /* ++ * Don't pass nameidata to lower file system, because we ++ * don't want an arbitrary lower file being opened or ++ * returned to us: it may be useless to us because of the ++ * fanout nature of unionfs (cf. file/directory open-file ++ * invariants). We will open lower files as and when needed ++ * later on. ++ */ ++ if (!lower_dentry->d_op->d_revalidate(lower_dentry, NULL)) ++ valid = false; ++ } ++ ++ if (!dentry->d_inode || ++ ibstart(dentry->d_inode) < 0 || ++ ibend(dentry->d_inode) < 0) { ++ valid = false; ++ goto out; ++ } ++ ++ if (valid) { ++ /* ++ * If we get here, and we copy the meta-data from the lower ++ * inode to our inode, then it is vital that we have already ++ * purged all unionfs-level file data. We do that in the ++ * caller (__unionfs_d_revalidate) by calling ++ * purge_inode_data. ++ */ ++ unionfs_copy_attr_all(dentry->d_inode, ++ unionfs_lower_inode(dentry->d_inode)); ++ fsstack_copy_inode_size(dentry->d_inode, ++ unionfs_lower_inode(dentry->d_inode)); ++ } ++ ++out: ++ return valid; ++} ++ ++/* ++ * Determine if the lower inode objects have changed from below the unionfs ++ * inode. Return true if changed, false otherwise. ++ * ++ * We check if the mtime or ctime have changed. However, the inode times ++ * can be changed by anyone without much protection, including ++ * asynchronously. This can sometimes cause unionfs to find that the lower ++ * file system doesn't change its inode times quick enough, resulting in a ++ * false positive indication (which is harmless, it just makes unionfs do ++ * extra work in re-validating the objects). To minimize the chances of ++ * these situations, we still consider such small time changes valid, but we ++ * don't print debugging messages unless the time changes are greater than ++ * UNIONFS_MIN_CC_TIME (which defaults to 3 seconds, as with NFS's acregmin) ++ * because significant changes are more likely due to users manually ++ * touching lower files. ++ */ ++bool is_newer_lower(const struct dentry *dentry) ++{ ++ int bindex; ++ struct inode *inode; ++ struct inode *lower_inode; ++ ++ /* ignore if we're called on semi-initialized dentries/inodes */ ++ if (!dentry || !UNIONFS_D(dentry)) ++ return false; ++ inode = dentry->d_inode; ++ if (!inode || !UNIONFS_I(inode)->lower_inodes || ++ ibstart(inode) < 0 || ibend(inode) < 0) ++ return false; ++ ++ for (bindex = ibstart(inode); bindex <= ibend(inode); bindex++) { ++ lower_inode = unionfs_lower_inode_idx(inode, bindex); ++ if (!lower_inode) ++ continue; ++ ++ /* check if mtime/ctime have changed */ ++ if (unlikely(timespec_compare(&inode->i_mtime, ++ &lower_inode->i_mtime) < 0)) { ++ if ((lower_inode->i_mtime.tv_sec - ++ inode->i_mtime.tv_sec) > UNIONFS_MIN_CC_TIME) { ++ pr_info("unionfs: new lower inode mtime " ++ "(bindex=%d, name=%s)\n", bindex, ++ dentry->d_name.name); ++ show_dinode_times(dentry); ++ } ++ return true; ++ } ++ if (unlikely(timespec_compare(&inode->i_ctime, ++ &lower_inode->i_ctime) < 0)) { ++ if ((lower_inode->i_ctime.tv_sec - ++ inode->i_ctime.tv_sec) > UNIONFS_MIN_CC_TIME) { ++ pr_info("unionfs: new lower inode ctime " ++ "(bindex=%d, name=%s)\n", bindex, ++ dentry->d_name.name); ++ show_dinode_times(dentry); ++ } ++ return true; ++ } ++ } ++ ++ /* ++ * Last check: if this is a positive dentry, but somehow all lower ++ * dentries are negative or unhashed, then this dentry needs to be ++ * revalidated, because someone probably deleted the objects from ++ * the lower branches directly. ++ */ ++ if (is_negative_lower(dentry)) ++ return true; ++ ++ return false; /* default: lower is not newer */ ++} ++ ++static int unionfs_d_revalidate(struct dentry *dentry, ++ struct nameidata *nd_unused) ++{ ++ bool valid = true; ++ int err = 1; /* 1 means valid for the VFS */ ++ struct dentry *parent; ++ ++ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); ++ parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); ++ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); ++ ++ valid = __unionfs_d_revalidate(dentry, parent, false); ++ if (valid) { ++ unionfs_postcopyup_setmnt(dentry); ++ unionfs_check_dentry(dentry); ++ } else { ++ d_drop(dentry); ++ err = valid; ++ } ++ unionfs_unlock_dentry(dentry); ++ unionfs_unlock_parent(dentry, parent); ++ unionfs_read_unlock(dentry->d_sb); ++ ++ return err; ++} ++ ++static void unionfs_d_release(struct dentry *dentry) ++{ ++ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); ++ if (unlikely(!UNIONFS_D(dentry))) ++ goto out; /* skip if no lower branches */ ++ /* must lock our branch configuration here */ ++ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); ++ ++ unionfs_check_dentry(dentry); ++ /* this could be a negative dentry, so check first */ ++ if (dbstart(dentry) < 0) { ++ unionfs_unlock_dentry(dentry); ++ goto out; /* due to a (normal) failed lookup */ ++ } ++ ++ /* Release all the lower dentries */ ++ path_put_lowers_all(dentry, true); ++ ++ unionfs_unlock_dentry(dentry); ++ ++out: ++ free_dentry_private_data(dentry); ++ unionfs_read_unlock(dentry->d_sb); ++ return; ++} ++ ++/* ++ * Called when we're removing the last reference to our dentry. So we ++ * should drop all lower references too. ++ */ ++static void unionfs_d_iput(struct dentry *dentry, struct inode *inode) ++{ ++ int rc; ++ ++ BUG_ON(!dentry); ++ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); ++ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); ++ ++ if (!UNIONFS_D(dentry) || dbstart(dentry) < 0) ++ goto drop_lower_inodes; ++ path_put_lowers_all(dentry, false); ++ ++drop_lower_inodes: ++ rc = atomic_read(&inode->i_count); ++ if (rc == 1 && inode->i_nlink == 1 && ibstart(inode) >= 0) { ++ /* see Documentation/filesystems/unionfs/issues.txt */ ++ lockdep_off(); ++ iput(unionfs_lower_inode(inode)); ++ lockdep_on(); ++ unionfs_set_lower_inode(inode, NULL); ++ /* XXX: may need to set start/end to -1? */ ++ } ++ ++ iput(inode); ++ ++ unionfs_unlock_dentry(dentry); ++ unionfs_read_unlock(dentry->d_sb); ++} ++ ++struct dentry_operations unionfs_dops = { ++ .d_revalidate = unionfs_d_revalidate, ++ .d_release = unionfs_d_release, ++ .d_iput = unionfs_d_iput, ++}; +diff --git a/fs/unionfs/dirfops.c b/fs/unionfs/dirfops.c +new file mode 100644 +index 0000000..7da0ff0 +--- /dev/null ++++ b/fs/unionfs/dirfops.c +@@ -0,0 +1,302 @@ ++/* ++ * Copyright (c) 2003-2010 Erez Zadok ++ * Copyright (c) 2003-2006 Charles P. Wright ++ * Copyright (c) 2005-2007 Josef 'Jeff' Sipek ++ * Copyright (c) 2005-2006 Junjiro Okajima ++ * Copyright (c) 2005 Arun M. Krishnakumar ++ * Copyright (c) 2004-2006 David P. Quigley ++ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair ++ * Copyright (c) 2003 Puja Gupta ++ * Copyright (c) 2003 Harikesavan Krishnan ++ * Copyright (c) 2003-2010 Stony Brook University ++ * Copyright (c) 2003-2010 The Research Foundation of SUNY ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "union.h" ++ ++/* Make sure our rdstate is playing by the rules. */ ++static void verify_rdstate_offset(struct unionfs_dir_state *rdstate) ++{ ++ BUG_ON(rdstate->offset >= DIREOF); ++ BUG_ON(rdstate->cookie >= MAXRDCOOKIE); ++} ++ ++struct unionfs_getdents_callback { ++ struct unionfs_dir_state *rdstate; ++ void *dirent; ++ int entries_written; ++ int filldir_called; ++ int filldir_error; ++ filldir_t filldir; ++ struct super_block *sb; ++}; ++ ++/* based on generic filldir in fs/readir.c */ ++static int unionfs_filldir(void *dirent, const char *oname, int namelen, ++ loff_t offset, u64 ino, unsigned int d_type) ++{ ++ struct unionfs_getdents_callback *buf = dirent; ++ struct filldir_node *found = NULL; ++ int err = 0; ++ int is_whiteout; ++ char *name = (char *) oname; ++ ++ buf->filldir_called++; ++ ++ is_whiteout = is_whiteout_name(&name, &namelen); ++ ++ found = find_filldir_node(buf->rdstate, name, namelen, is_whiteout); ++ ++ if (found) { ++ /* ++ * If we had non-whiteout entry in dir cache, then mark it ++ * as a whiteout and but leave it in the dir cache. ++ */ ++ if (is_whiteout && !found->whiteout) ++ found->whiteout = is_whiteout; ++ goto out; ++ } ++ ++ /* if 'name' isn't a whiteout, filldir it. */ ++ if (!is_whiteout) { ++ off_t pos = rdstate2offset(buf->rdstate); ++ u64 unionfs_ino = ino; ++ ++ err = buf->filldir(buf->dirent, name, namelen, pos, ++ unionfs_ino, d_type); ++ buf->rdstate->offset++; ++ verify_rdstate_offset(buf->rdstate); ++ } ++ /* ++ * If we did fill it, stuff it in our hash, otherwise return an ++ * error. ++ */ ++ if (err) { ++ buf->filldir_error = err; ++ goto out; ++ } ++ buf->entries_written++; ++ err = add_filldir_node(buf->rdstate, name, namelen, ++ buf->rdstate->bindex, is_whiteout); ++ if (err) ++ buf->filldir_error = err; ++ ++out: ++ return err; ++} ++ ++static int unionfs_readdir(struct file *file, void *dirent, filldir_t filldir) ++{ ++ int err = 0; ++ struct file *lower_file = NULL; ++ struct dentry *dentry = file->f_path.dentry; ++ struct dentry *parent; ++ struct inode *inode = NULL; ++ struct unionfs_getdents_callback buf; ++ struct unionfs_dir_state *uds; ++ int bend; ++ loff_t offset; ++ ++ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT); ++ parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); ++ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); ++ ++ err = unionfs_file_revalidate(file, parent, false); ++ if (unlikely(err)) ++ goto out; ++ ++ inode = dentry->d_inode; ++ ++ uds = UNIONFS_F(file)->rdstate; ++ if (!uds) { ++ if (file->f_pos == DIREOF) { ++ goto out; ++ } else if (file->f_pos > 0) { ++ uds = find_rdstate(inode, file->f_pos); ++ if (unlikely(!uds)) { ++ err = -ESTALE; ++ goto out; ++ } ++ UNIONFS_F(file)->rdstate = uds; ++ } else { ++ init_rdstate(file); ++ uds = UNIONFS_F(file)->rdstate; ++ } ++ } ++ bend = fbend(file); ++ ++ while (uds->bindex <= bend) { ++ lower_file = unionfs_lower_file_idx(file, uds->bindex); ++ if (!lower_file) { ++ uds->bindex++; ++ uds->dirpos = 0; ++ continue; ++ } ++ ++ /* prepare callback buffer */ ++ buf.filldir_called = 0; ++ buf.filldir_error = 0; ++ buf.entries_written = 0; ++ buf.dirent = dirent; ++ buf.filldir = filldir; ++ buf.rdstate = uds; ++ buf.sb = inode->i_sb; ++ ++ /* Read starting from where we last left off. */ ++ offset = vfs_llseek(lower_file, uds->dirpos, SEEK_SET); ++ if (offset < 0) { ++ err = offset; ++ goto out; ++ } ++ err = vfs_readdir(lower_file, unionfs_filldir, &buf); ++ ++ /* Save the position for when we continue. */ ++ offset = vfs_llseek(lower_file, 0, SEEK_CUR); ++ if (offset < 0) { ++ err = offset; ++ goto out; ++ } ++ uds->dirpos = offset; ++ ++ /* Copy the atime. */ ++ fsstack_copy_attr_atime(inode, ++ lower_file->f_path.dentry->d_inode); ++ ++ if (err < 0) ++ goto out; ++ ++ if (buf.filldir_error) ++ break; ++ ++ if (!buf.entries_written) { ++ uds->bindex++; ++ uds->dirpos = 0; ++ } ++ } ++ ++ if (!buf.filldir_error && uds->bindex >= bend) { ++ /* Save the number of hash entries for next time. */ ++ UNIONFS_I(inode)->hashsize = uds->hashentries; ++ free_rdstate(uds); ++ UNIONFS_F(file)->rdstate = NULL; ++ file->f_pos = DIREOF; ++ } else { ++ file->f_pos = rdstate2offset(uds); ++ } ++ ++out: ++ if (!err) ++ unionfs_check_file(file); ++ unionfs_unlock_dentry(dentry); ++ unionfs_unlock_parent(dentry, parent); ++ unionfs_read_unlock(dentry->d_sb); ++ return err; ++} ++ ++/* ++ * This is not meant to be a generic repositioning function. If you do ++ * things that aren't supported, then we return EINVAL. ++ * ++ * What is allowed: ++ * (1) seeking to the same position that you are currently at ++ * This really has no effect, but returns where you are. ++ * (2) seeking to the beginning of the file ++ * This throws out all state, and lets you begin again. ++ */ ++static loff_t unionfs_dir_llseek(struct file *file, loff_t offset, int origin) ++{ ++ struct unionfs_dir_state *rdstate; ++ struct dentry *dentry = file->f_path.dentry; ++ struct dentry *parent; ++ loff_t err; ++ ++ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT); ++ parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); ++ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); ++ ++ err = unionfs_file_revalidate(file, parent, false); ++ if (unlikely(err)) ++ goto out; ++ ++ rdstate = UNIONFS_F(file)->rdstate; ++ ++ /* ++ * we let users seek to their current position, but not anywhere ++ * else. ++ */ ++ if (!offset) { ++ switch (origin) { ++ case SEEK_SET: ++ if (rdstate) { ++ free_rdstate(rdstate); ++ UNIONFS_F(file)->rdstate = NULL; ++ } ++ init_rdstate(file); ++ err = 0; ++ break; ++ case SEEK_CUR: ++ err = file->f_pos; ++ break; ++ case SEEK_END: ++ /* Unsupported, because we would break everything. */ ++ err = -EINVAL; ++ break; ++ } ++ } else { ++ switch (origin) { ++ case SEEK_SET: ++ if (rdstate) { ++ if (offset == rdstate2offset(rdstate)) ++ err = offset; ++ else if (file->f_pos == DIREOF) ++ err = DIREOF; ++ else ++ err = -EINVAL; ++ } else { ++ struct inode *inode; ++ inode = dentry->d_inode; ++ rdstate = find_rdstate(inode, offset); ++ if (rdstate) { ++ UNIONFS_F(file)->rdstate = rdstate; ++ err = rdstate->offset; ++ } else { ++ err = -EINVAL; ++ } ++ } ++ break; ++ case SEEK_CUR: ++ case SEEK_END: ++ /* Unsupported, because we would break everything. */ ++ err = -EINVAL; ++ break; ++ } ++ } ++ ++out: ++ if (!err) ++ unionfs_check_file(file); ++ unionfs_unlock_dentry(dentry); ++ unionfs_unlock_parent(dentry, parent); ++ unionfs_read_unlock(dentry->d_sb); ++ return err; ++} ++ ++/* ++ * Trimmed directory options, we shouldn't pass everything down since ++ * we don't want to operate on partial directories. ++ */ ++struct file_operations unionfs_dir_fops = { ++ .llseek = unionfs_dir_llseek, ++ .read = generic_read_dir, ++ .readdir = unionfs_readdir, ++ .unlocked_ioctl = unionfs_ioctl, ++ .open = unionfs_open, ++ .release = unionfs_file_release, ++ .flush = unionfs_flush, ++ .fsync = unionfs_fsync, ++ .fasync = unionfs_fasync, ++}; +diff --git a/fs/unionfs/dirhelper.c b/fs/unionfs/dirhelper.c +new file mode 100644 +index 0000000..033343b +--- /dev/null ++++ b/fs/unionfs/dirhelper.c +@@ -0,0 +1,158 @@ ++/* ++ * Copyright (c) 2003-2010 Erez Zadok ++ * Copyright (c) 2003-2006 Charles P. Wright ++ * Copyright (c) 2005-2007 Josef 'Jeff' Sipek ++ * Copyright (c) 2005-2006 Junjiro Okajima ++ * Copyright (c) 2005 Arun M. Krishnakumar ++ * Copyright (c) 2004-2006 David P. Quigley ++ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair ++ * Copyright (c) 2003 Puja Gupta ++ * Copyright (c) 2003 Harikesavan Krishnan ++ * Copyright (c) 2003-2010 Stony Brook University ++ * Copyright (c) 2003-2010 The Research Foundation of SUNY ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "union.h" ++ ++#define RD_NONE 0 ++#define RD_CHECK_EMPTY 1 ++/* The callback structure for check_empty. */ ++struct unionfs_rdutil_callback { ++ int err; ++ int filldir_called; ++ struct unionfs_dir_state *rdstate; ++ int mode; ++}; ++ ++/* This filldir function makes sure only whiteouts exist within a directory. */ ++static int readdir_util_callback(void *dirent, const char *oname, int namelen, ++ loff_t offset, u64 ino, unsigned int d_type) ++{ ++ int err = 0; ++ struct unionfs_rdutil_callback *buf = dirent; ++ int is_whiteout; ++ struct filldir_node *found; ++ char *name = (char *) oname; ++ ++ buf->filldir_called = 1; ++ ++ if (name[0] == '.' && (namelen == 1 || ++ (name[1] == '.' && namelen == 2))) ++ goto out; ++ ++ is_whiteout = is_whiteout_name(&name, &namelen); ++ ++ found = find_filldir_node(buf->rdstate, name, namelen, is_whiteout); ++ /* If it was found in the table there was a previous whiteout. */ ++ if (found) ++ goto out; ++ ++ /* ++ * if it wasn't found and isn't a whiteout, the directory isn't ++ * empty. ++ */ ++ err = -ENOTEMPTY; ++ if ((buf->mode == RD_CHECK_EMPTY) && !is_whiteout) ++ goto out; ++ ++ err = add_filldir_node(buf->rdstate, name, namelen, ++ buf->rdstate->bindex, is_whiteout); ++ ++out: ++ buf->err = err; ++ return err; ++} ++ ++/* Is a directory logically empty? */ ++int check_empty(struct dentry *dentry, struct dentry *parent, ++ struct unionfs_dir_state **namelist) ++{ ++ int err = 0; ++ struct dentry *lower_dentry = NULL; ++ struct vfsmount *mnt; ++ struct super_block *sb; ++ struct file *lower_file; ++ struct unionfs_rdutil_callback *buf = NULL; ++ int bindex, bstart, bend, bopaque; ++ ++ sb = dentry->d_sb; ++ ++ ++ BUG_ON(!S_ISDIR(dentry->d_inode->i_mode)); ++ ++ err = unionfs_partial_lookup(dentry, parent); ++ if (err) ++ goto out; ++ ++ bstart = dbstart(dentry); ++ bend = dbend(dentry); ++ bopaque = dbopaque(dentry); ++ if (0 <= bopaque && bopaque < bend) ++ bend = bopaque; ++ ++ buf = kmalloc(sizeof(struct unionfs_rdutil_callback), GFP_KERNEL); ++ if (unlikely(!buf)) { ++ err = -ENOMEM; ++ goto out; ++ } ++ buf->err = 0; ++ buf->mode = RD_CHECK_EMPTY; ++ buf->rdstate = alloc_rdstate(dentry->d_inode, bstart); ++ if (unlikely(!buf->rdstate)) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ /* Process the lower directories with rdutil_callback as a filldir. */ ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); ++ if (!lower_dentry) ++ continue; ++ if (!lower_dentry->d_inode) ++ continue; ++ if (!S_ISDIR(lower_dentry->d_inode->i_mode)) ++ continue; ++ ++ dget(lower_dentry); ++ mnt = unionfs_mntget(dentry, bindex); ++ branchget(sb, bindex); ++ lower_file = dentry_open(lower_dentry, mnt, O_RDONLY, current_cred()); ++ if (IS_ERR(lower_file)) { ++ err = PTR_ERR(lower_file); ++ branchput(sb, bindex); ++ goto out; ++ } ++ ++ do { ++ buf->filldir_called = 0; ++ buf->rdstate->bindex = bindex; ++ err = vfs_readdir(lower_file, ++ readdir_util_callback, buf); ++ if (buf->err) ++ err = buf->err; ++ } while ((err >= 0) && buf->filldir_called); ++ ++ /* fput calls dput for lower_dentry */ ++ fput(lower_file); ++ branchput(sb, bindex); ++ ++ if (err < 0) ++ goto out; ++ } ++ ++out: ++ if (buf) { ++ if (namelist && !err) ++ *namelist = buf->rdstate; ++ else if (buf->rdstate) ++ free_rdstate(buf->rdstate); ++ kfree(buf); ++ } ++ ++ ++ return err; ++} +diff --git a/fs/unionfs/fanout.h b/fs/unionfs/fanout.h +new file mode 100644 +index 0000000..5b77eac +--- /dev/null ++++ b/fs/unionfs/fanout.h +@@ -0,0 +1,407 @@ ++/* ++ * Copyright (c) 2003-2010 Erez Zadok ++ * Copyright (c) 2003-2006 Charles P. Wright ++ * Copyright (c) 2005-2007 Josef 'Jeff' Sipek ++ * Copyright (c) 2005 Arun M. Krishnakumar ++ * Copyright (c) 2004-2006 David P. Quigley ++ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair ++ * Copyright (c) 2003 Puja Gupta ++ * Copyright (c) 2003 Harikesavan Krishnan ++ * Copyright (c) 2003-2010 Stony Brook University ++ * Copyright (c) 2003-2010 The Research Foundation of SUNY ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef _FANOUT_H_ ++#define _FANOUT_H_ ++ ++/* ++ * Inode to private data ++ * ++ * Since we use containers and the struct inode is _inside_ the ++ * unionfs_inode_info structure, UNIONFS_I will always (given a non-NULL ++ * inode pointer), return a valid non-NULL pointer. ++ */ ++static inline struct unionfs_inode_info *UNIONFS_I(const struct inode *inode) ++{ ++ return container_of(inode, struct unionfs_inode_info, vfs_inode); ++} ++ ++#define ibstart(ino) (UNIONFS_I(ino)->bstart) ++#define ibend(ino) (UNIONFS_I(ino)->bend) ++ ++/* Dentry to private data */ ++#define UNIONFS_D(dent) ((struct unionfs_dentry_info *)(dent)->d_fsdata) ++#define dbstart(dent) (UNIONFS_D(dent)->bstart) ++#define dbend(dent) (UNIONFS_D(dent)->bend) ++#define dbopaque(dent) (UNIONFS_D(dent)->bopaque) ++ ++/* Superblock to private data */ ++#define UNIONFS_SB(super) ((struct unionfs_sb_info *)(super)->s_fs_info) ++#define sbstart(sb) 0 ++#define sbend(sb) (UNIONFS_SB(sb)->bend) ++#define sbmax(sb) (UNIONFS_SB(sb)->bend + 1) ++#define sbhbid(sb) (UNIONFS_SB(sb)->high_branch_id) ++ ++/* File to private Data */ ++#define UNIONFS_F(file) ((struct unionfs_file_info *)((file)->private_data)) ++#define fbstart(file) (UNIONFS_F(file)->bstart) ++#define fbend(file) (UNIONFS_F(file)->bend) ++ ++/* macros to manipulate branch IDs in stored in our superblock */ ++static inline int branch_id(struct super_block *sb, int index) ++{ ++ BUG_ON(!sb || index < 0); ++ return UNIONFS_SB(sb)->data[index].branch_id; ++} ++ ++static inline void set_branch_id(struct super_block *sb, int index, int val) ++{ ++ BUG_ON(!sb || index < 0); ++ UNIONFS_SB(sb)->data[index].branch_id = val; ++} ++ ++static inline void new_branch_id(struct super_block *sb, int index) ++{ ++ BUG_ON(!sb || index < 0); ++ set_branch_id(sb, index, ++UNIONFS_SB(sb)->high_branch_id); ++} ++ ++/* ++ * Find new index of matching branch with an existing superblock of a known ++ * (possibly old) id. This is needed because branches could have been ++ * added/deleted causing the branches of any open files to shift. ++ * ++ * @sb: the new superblock which may have new/different branch IDs ++ * @id: the old/existing id we're looking for ++ * Returns index of newly found branch (0 or greater), -1 otherwise. ++ */ ++static inline int branch_id_to_idx(struct super_block *sb, int id) ++{ ++ int i; ++ for (i = 0; i < sbmax(sb); i++) { ++ if (branch_id(sb, i) == id) ++ return i; ++ } ++ /* in the non-ODF code, this should really never happen */ ++ printk(KERN_WARNING "unionfs: cannot find branch with id %d\n", id); ++ return -1; ++} ++ ++/* File to lower file. */ ++static inline struct file *unionfs_lower_file(const struct file *f) ++{ ++ BUG_ON(!f); ++ return UNIONFS_F(f)->lower_files[fbstart(f)]; ++} ++ ++static inline struct file *unionfs_lower_file_idx(const struct file *f, ++ int index) ++{ ++ BUG_ON(!f || index < 0); ++ return UNIONFS_F(f)->lower_files[index]; ++} ++ ++static inline void unionfs_set_lower_file_idx(struct file *f, int index, ++ struct file *val) ++{ ++ BUG_ON(!f || index < 0); ++ UNIONFS_F(f)->lower_files[index] = val; ++ /* save branch ID (may be redundant?) */ ++ UNIONFS_F(f)->saved_branch_ids[index] = ++ branch_id((f)->f_path.dentry->d_sb, index); ++} ++ ++static inline void unionfs_set_lower_file(struct file *f, struct file *val) ++{ ++ BUG_ON(!f); ++ unionfs_set_lower_file_idx((f), fbstart(f), (val)); ++} ++ ++/* Inode to lower inode. */ ++static inline struct inode *unionfs_lower_inode(const struct inode *i) ++{ ++ BUG_ON(!i); ++ return UNIONFS_I(i)->lower_inodes[ibstart(i)]; ++} ++ ++static inline struct inode *unionfs_lower_inode_idx(const struct inode *i, ++ int index) ++{ ++ BUG_ON(!i || index < 0); ++ return UNIONFS_I(i)->lower_inodes[index]; ++} ++ ++static inline void unionfs_set_lower_inode_idx(struct inode *i, int index, ++ struct inode *val) ++{ ++ BUG_ON(!i || index < 0); ++ UNIONFS_I(i)->lower_inodes[index] = val; ++} ++ ++static inline void unionfs_set_lower_inode(struct inode *i, struct inode *val) ++{ ++ BUG_ON(!i); ++ UNIONFS_I(i)->lower_inodes[ibstart(i)] = val; ++} ++ ++/* Superblock to lower superblock. */ ++static inline struct super_block *unionfs_lower_super( ++ const struct super_block *sb) ++{ ++ BUG_ON(!sb); ++ return UNIONFS_SB(sb)->data[sbstart(sb)].sb; ++} ++ ++static inline struct super_block *unionfs_lower_super_idx( ++ const struct super_block *sb, ++ int index) ++{ ++ BUG_ON(!sb || index < 0); ++ return UNIONFS_SB(sb)->data[index].sb; ++} ++ ++static inline void unionfs_set_lower_super_idx(struct super_block *sb, ++ int index, ++ struct super_block *val) ++{ ++ BUG_ON(!sb || index < 0); ++ UNIONFS_SB(sb)->data[index].sb = val; ++} ++ ++static inline void unionfs_set_lower_super(struct super_block *sb, ++ struct super_block *val) ++{ ++ BUG_ON(!sb); ++ UNIONFS_SB(sb)->data[sbstart(sb)].sb = val; ++} ++ ++/* Branch count macros. */ ++static inline int branch_count(const struct super_block *sb, int index) ++{ ++ BUG_ON(!sb || index < 0); ++ return atomic_read(&UNIONFS_SB(sb)->data[index].open_files); ++} ++ ++static inline void set_branch_count(struct super_block *sb, int index, int val) ++{ ++ BUG_ON(!sb || index < 0); ++ atomic_set(&UNIONFS_SB(sb)->data[index].open_files, val); ++} ++ ++static inline void branchget(struct super_block *sb, int index) ++{ ++ BUG_ON(!sb || index < 0); ++ atomic_inc(&UNIONFS_SB(sb)->data[index].open_files); ++} ++ ++static inline void branchput(struct super_block *sb, int index) ++{ ++ BUG_ON(!sb || index < 0); ++ atomic_dec(&UNIONFS_SB(sb)->data[index].open_files); ++} ++ ++/* Dentry macros */ ++static inline void unionfs_set_lower_dentry_idx(struct dentry *dent, int index, ++ struct dentry *val) ++{ ++ BUG_ON(!dent || index < 0); ++ UNIONFS_D(dent)->lower_paths[index].dentry = val; ++} ++ ++static inline struct dentry *unionfs_lower_dentry_idx( ++ const struct dentry *dent, ++ int index) ++{ ++ BUG_ON(!dent || index < 0); ++ return UNIONFS_D(dent)->lower_paths[index].dentry; ++} ++ ++static inline struct dentry *unionfs_lower_dentry(const struct dentry *dent) ++{ ++ BUG_ON(!dent); ++ return unionfs_lower_dentry_idx(dent, dbstart(dent)); ++} ++ ++static inline void unionfs_set_lower_mnt_idx(struct dentry *dent, int index, ++ struct vfsmount *mnt) ++{ ++ BUG_ON(!dent || index < 0); ++ UNIONFS_D(dent)->lower_paths[index].mnt = mnt; ++} ++ ++static inline struct vfsmount *unionfs_lower_mnt_idx( ++ const struct dentry *dent, ++ int index) ++{ ++ BUG_ON(!dent || index < 0); ++ return UNIONFS_D(dent)->lower_paths[index].mnt; ++} ++ ++static inline struct vfsmount *unionfs_lower_mnt(const struct dentry *dent) ++{ ++ BUG_ON(!dent); ++ return unionfs_lower_mnt_idx(dent, dbstart(dent)); ++} ++ ++/* Macros for locking a dentry. */ ++enum unionfs_dentry_lock_class { ++ UNIONFS_DMUTEX_NORMAL, ++ UNIONFS_DMUTEX_ROOT, ++ UNIONFS_DMUTEX_PARENT, ++ UNIONFS_DMUTEX_CHILD, ++ UNIONFS_DMUTEX_WHITEOUT, ++ UNIONFS_DMUTEX_REVAL_PARENT, /* for file/dentry revalidate */ ++ UNIONFS_DMUTEX_REVAL_CHILD, /* for file/dentry revalidate */ ++}; ++ ++static inline void unionfs_lock_dentry(struct dentry *d, ++ unsigned int subclass) ++{ ++ BUG_ON(!d); ++ mutex_lock_nested(&UNIONFS_D(d)->lock, subclass); ++} ++ ++static inline void unionfs_unlock_dentry(struct dentry *d) ++{ ++ BUG_ON(!d); ++ mutex_unlock(&UNIONFS_D(d)->lock); ++} ++ ++static inline struct dentry *unionfs_lock_parent(struct dentry *d, ++ unsigned int subclass) ++{ ++ struct dentry *p; ++ ++ BUG_ON(!d); ++ p = dget_parent(d); ++ if (p != d) ++ mutex_lock_nested(&UNIONFS_D(p)->lock, subclass); ++ return p; ++} ++ ++static inline void unionfs_unlock_parent(struct dentry *d, struct dentry *p) ++{ ++ BUG_ON(!d); ++ BUG_ON(!p); ++ if (p != d) { ++ BUG_ON(!mutex_is_locked(&UNIONFS_D(p)->lock)); ++ mutex_unlock(&UNIONFS_D(p)->lock); ++ } ++ dput(p); ++} ++ ++static inline void verify_locked(struct dentry *d) ++{ ++ BUG_ON(!d); ++ BUG_ON(!mutex_is_locked(&UNIONFS_D(d)->lock)); ++} ++ ++/* macros to put lower objects */ ++ ++/* ++ * iput lower inodes of an unionfs dentry, from bstart to bend. If ++ * @free_lower is true, then also kfree the memory used to hold the lower ++ * object pointers. ++ */ ++static inline void iput_lowers(struct inode *inode, ++ int bstart, int bend, bool free_lower) ++{ ++ struct inode *lower_inode; ++ int bindex; ++ ++ BUG_ON(!inode); ++ BUG_ON(!UNIONFS_I(inode)); ++ BUG_ON(bstart < 0); ++ ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ lower_inode = unionfs_lower_inode_idx(inode, bindex); ++ if (lower_inode) { ++ unionfs_set_lower_inode_idx(inode, bindex, NULL); ++ /* see Documentation/filesystems/unionfs/issues.txt */ ++ lockdep_off(); ++ iput(lower_inode); ++ lockdep_on(); ++ } ++ } ++ ++ if (free_lower) { ++ kfree(UNIONFS_I(inode)->lower_inodes); ++ UNIONFS_I(inode)->lower_inodes = NULL; ++ } ++} ++ ++/* iput all lower inodes, and reset start/end branch indices to -1 */ ++static inline void iput_lowers_all(struct inode *inode, bool free_lower) ++{ ++ int bstart, bend; ++ ++ BUG_ON(!inode); ++ BUG_ON(!UNIONFS_I(inode)); ++ bstart = ibstart(inode); ++ bend = ibend(inode); ++ BUG_ON(bstart < 0); ++ ++ iput_lowers(inode, bstart, bend, free_lower); ++ ibstart(inode) = ibend(inode) = -1; ++} ++ ++/* ++ * dput/mntput all lower dentries and vfsmounts of an unionfs dentry, from ++ * bstart to bend. If @free_lower is true, then also kfree the memory used ++ * to hold the lower object pointers. ++ * ++ * XXX: implement using path_put VFS macros ++ */ ++static inline void path_put_lowers(struct dentry *dentry, ++ int bstart, int bend, bool free_lower) ++{ ++ struct dentry *lower_dentry; ++ struct vfsmount *lower_mnt; ++ int bindex; ++ ++ BUG_ON(!dentry); ++ BUG_ON(!UNIONFS_D(dentry)); ++ BUG_ON(bstart < 0); ++ ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); ++ if (lower_dentry) { ++ unionfs_set_lower_dentry_idx(dentry, bindex, NULL); ++ dput(lower_dentry); ++ } ++ lower_mnt = unionfs_lower_mnt_idx(dentry, bindex); ++ if (lower_mnt) { ++ unionfs_set_lower_mnt_idx(dentry, bindex, NULL); ++ mntput(lower_mnt); ++ } ++ } ++ ++ if (free_lower) { ++ kfree(UNIONFS_D(dentry)->lower_paths); ++ UNIONFS_D(dentry)->lower_paths = NULL; ++ } ++} ++ ++/* ++ * dput/mntput all lower dentries and vfsmounts, and reset start/end branch ++ * indices to -1. ++ */ ++static inline void path_put_lowers_all(struct dentry *dentry, bool free_lower) ++{ ++ int bstart, bend; ++ ++ BUG_ON(!dentry); ++ BUG_ON(!UNIONFS_D(dentry)); ++ bstart = dbstart(dentry); ++ bend = dbend(dentry); ++ BUG_ON(bstart < 0); ++ ++ path_put_lowers(dentry, bstart, bend, free_lower); ++ dbstart(dentry) = dbend(dentry) = -1; ++} ++ ++#endif /* not _FANOUT_H */ +diff --git a/fs/unionfs/file.c b/fs/unionfs/file.c +new file mode 100644 +index 0000000..5a8f4e0 +--- /dev/null ++++ b/fs/unionfs/file.c +@@ -0,0 +1,379 @@ ++/* ++ * Copyright (c) 2003-2010 Erez Zadok ++ * Copyright (c) 2003-2006 Charles P. Wright ++ * Copyright (c) 2005-2007 Josef 'Jeff' Sipek ++ * Copyright (c) 2005-2006 Junjiro Okajima ++ * Copyright (c) 2005 Arun M. Krishnakumar ++ * Copyright (c) 2004-2006 David P. Quigley ++ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair ++ * Copyright (c) 2003 Puja Gupta ++ * Copyright (c) 2003 Harikesavan Krishnan ++ * Copyright (c) 2003-2010 Stony Brook University ++ * Copyright (c) 2003-2010 The Research Foundation of SUNY ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "union.h" ++ ++static ssize_t unionfs_read(struct file *file, char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ int err; ++ struct file *lower_file; ++ struct dentry *dentry = file->f_path.dentry; ++ struct dentry *parent; ++ ++ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT); ++ parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); ++ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); ++ ++ err = unionfs_file_revalidate(file, parent, false); ++ if (unlikely(err)) ++ goto out; ++ ++ lower_file = unionfs_lower_file(file); ++ err = vfs_read(lower_file, buf, count, ppos); ++ /* update our inode atime upon a successful lower read */ ++ if (err >= 0) { ++ fsstack_copy_attr_atime(dentry->d_inode, ++ lower_file->f_path.dentry->d_inode); ++ unionfs_check_file(file); ++ } ++ ++out: ++ unionfs_unlock_dentry(dentry); ++ unionfs_unlock_parent(dentry, parent); ++ unionfs_read_unlock(dentry->d_sb); ++ return err; ++} ++ ++static ssize_t unionfs_write(struct file *file, const char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ int err = 0; ++ struct file *lower_file; ++ struct dentry *dentry = file->f_path.dentry; ++ struct dentry *parent; ++ ++ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT); ++ parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); ++ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); ++ ++ err = unionfs_file_revalidate(file, parent, true); ++ if (unlikely(err)) ++ goto out; ++ ++ lower_file = unionfs_lower_file(file); ++ err = vfs_write(lower_file, buf, count, ppos); ++ /* update our inode times+sizes upon a successful lower write */ ++ if (err >= 0) { ++ fsstack_copy_inode_size(dentry->d_inode, ++ lower_file->f_path.dentry->d_inode); ++ fsstack_copy_attr_times(dentry->d_inode, ++ lower_file->f_path.dentry->d_inode); ++ UNIONFS_F(file)->wrote_to_file = true; /* for delayed copyup */ ++ unionfs_check_file(file); ++ } ++ ++out: ++ unionfs_unlock_dentry(dentry); ++ unionfs_unlock_parent(dentry, parent); ++ unionfs_read_unlock(dentry->d_sb); ++ return err; ++} ++ ++static int unionfs_file_readdir(struct file *file, void *dirent, ++ filldir_t filldir) ++{ ++ return -ENOTDIR; ++} ++ ++static int unionfs_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ int err = 0; ++ bool willwrite; ++ struct file *lower_file; ++ struct dentry *dentry = file->f_path.dentry; ++ struct dentry *parent; ++ const struct vm_operations_struct *saved_vm_ops = NULL; ++ ++ /* ++ * Since mm/memory.c:might_fault() (under PROVE_LOCKING) was ++ * modified in 2.6.29-rc1 to call might_lock_read on mmap_sem, this ++ * has been causing false positives in file system stacking layers. ++ * In particular, our ->mmap is called after sys_mmap2 already holds ++ * mmap_sem, then we lock our own mutexes; but earlier, it's ++ * possible for lockdep to have locked our mutexes first, and then ++ * we call a lower ->readdir which could call might_fault. The ++ * different ordering of the locks is what lockdep complains about ++ * -- unnecessarily. Therefore, we have no choice but to tell ++ * lockdep to temporarily turn off lockdep here. Note: the comments ++ * inside might_sleep also suggest that it would have been ++ * nicer to only annotate paths that needs that might_lock_read. ++ */ ++ lockdep_off(); ++ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT); ++ parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); ++ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); ++ ++ /* This might be deferred to mmap's writepage */ ++ willwrite = ((vma->vm_flags | VM_SHARED | VM_WRITE) == vma->vm_flags); ++ err = unionfs_file_revalidate(file, parent, willwrite); ++ if (unlikely(err)) ++ goto out; ++ unionfs_check_file(file); ++ ++ /* ++ * File systems which do not implement ->writepage may use ++ * generic_file_readonly_mmap as their ->mmap op. If you call ++ * generic_file_readonly_mmap with VM_WRITE, you'd get an -EINVAL. ++ * But we cannot call the lower ->mmap op, so we can't tell that ++ * writeable mappings won't work. Therefore, our only choice is to ++ * check if the lower file system supports the ->writepage, and if ++ * not, return EINVAL (the same error that ++ * generic_file_readonly_mmap returns in that case). ++ */ ++ lower_file = unionfs_lower_file(file); ++ if (willwrite && !lower_file->f_mapping->a_ops->writepage) { ++ err = -EINVAL; ++ printk(KERN_ERR "unionfs: branch %d file system does not " ++ "support writeable mmap\n", fbstart(file)); ++ goto out; ++ } ++ ++ /* ++ * find and save lower vm_ops. ++ * ++ * XXX: the VFS should have a cleaner way of finding the lower vm_ops ++ */ ++ if (!UNIONFS_F(file)->lower_vm_ops) { ++ err = lower_file->f_op->mmap(lower_file, vma); ++ if (err) { ++ printk(KERN_ERR "unionfs: lower mmap failed %d\n", err); ++ goto out; ++ } ++ saved_vm_ops = vma->vm_ops; ++ err = do_munmap(current->mm, vma->vm_start, ++ vma->vm_end - vma->vm_start); ++ if (err) { ++ printk(KERN_ERR "unionfs: do_munmap failed %d\n", err); ++ goto out; ++ } ++ } ++ ++ file->f_mapping->a_ops = &unionfs_dummy_aops; ++ err = generic_file_mmap(file, vma); ++ file->f_mapping->a_ops = &unionfs_aops; ++ if (err) { ++ printk(KERN_ERR "unionfs: generic_file_mmap failed %d\n", err); ++ goto out; ++ } ++ vma->vm_ops = &unionfs_vm_ops; ++ if (!UNIONFS_F(file)->lower_vm_ops) ++ UNIONFS_F(file)->lower_vm_ops = saved_vm_ops; ++ ++out: ++ if (!err) { ++ /* copyup could cause parent dir times to change */ ++ unionfs_copy_attr_times(parent->d_inode); ++ unionfs_check_file(file); ++ } ++ unionfs_unlock_dentry(dentry); ++ unionfs_unlock_parent(dentry, parent); ++ unionfs_read_unlock(dentry->d_sb); ++ lockdep_on(); ++ return err; ++} ++ ++int unionfs_fsync(struct file *file, int datasync) ++{ ++ int bindex, bstart, bend; ++ struct file *lower_file; ++ struct dentry *dentry = file->f_path.dentry; ++ struct dentry *lower_dentry; ++ struct dentry *parent; ++ struct inode *lower_inode, *inode; ++ int err = -EINVAL; ++ ++ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT); ++ parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); ++ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); ++ ++ err = unionfs_file_revalidate(file, parent, true); ++ if (unlikely(err)) ++ goto out; ++ unionfs_check_file(file); ++ ++ bstart = fbstart(file); ++ bend = fbend(file); ++ if (bstart < 0 || bend < 0) ++ goto out; ++ ++ inode = dentry->d_inode; ++ if (unlikely(!inode)) { ++ printk(KERN_ERR ++ "unionfs: null lower inode in unionfs_fsync\n"); ++ goto out; ++ } ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ lower_inode = unionfs_lower_inode_idx(inode, bindex); ++ if (!lower_inode || !lower_inode->i_fop->fsync) ++ continue; ++ lower_file = unionfs_lower_file_idx(file, bindex); ++ lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); ++ mutex_lock(&lower_inode->i_mutex); ++ err = lower_inode->i_fop->fsync(lower_file, datasync); ++ if (!err && bindex == bstart) ++ fsstack_copy_attr_times(inode, lower_inode); ++ mutex_unlock(&lower_inode->i_mutex); ++ if (err) ++ goto out; ++ } ++ ++out: ++ if (!err) ++ unionfs_check_file(file); ++ unionfs_unlock_dentry(dentry); ++ unionfs_unlock_parent(dentry, parent); ++ unionfs_read_unlock(dentry->d_sb); ++ return err; ++} ++ ++int unionfs_fasync(int fd, struct file *file, int flag) ++{ ++ int bindex, bstart, bend; ++ struct file *lower_file; ++ struct dentry *dentry = file->f_path.dentry; ++ struct dentry *parent; ++ struct inode *lower_inode, *inode; ++ int err = 0; ++ ++ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT); ++ parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); ++ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); ++ ++ err = unionfs_file_revalidate(file, parent, true); ++ if (unlikely(err)) ++ goto out; ++ unionfs_check_file(file); ++ ++ bstart = fbstart(file); ++ bend = fbend(file); ++ if (bstart < 0 || bend < 0) ++ goto out; ++ ++ inode = dentry->d_inode; ++ if (unlikely(!inode)) { ++ printk(KERN_ERR ++ "unionfs: null lower inode in unionfs_fasync\n"); ++ goto out; ++ } ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ lower_inode = unionfs_lower_inode_idx(inode, bindex); ++ if (!lower_inode || !lower_inode->i_fop->fasync) ++ continue; ++ lower_file = unionfs_lower_file_idx(file, bindex); ++ mutex_lock(&lower_inode->i_mutex); ++ err = lower_inode->i_fop->fasync(fd, lower_file, flag); ++ if (!err && bindex == bstart) ++ fsstack_copy_attr_times(inode, lower_inode); ++ mutex_unlock(&lower_inode->i_mutex); ++ if (err) ++ goto out; ++ } ++ ++out: ++ if (!err) ++ unionfs_check_file(file); ++ unionfs_unlock_dentry(dentry); ++ unionfs_unlock_parent(dentry, parent); ++ unionfs_read_unlock(dentry->d_sb); ++ return err; ++} ++ ++static ssize_t unionfs_splice_read(struct file *file, loff_t *ppos, ++ struct pipe_inode_info *pipe, size_t len, ++ unsigned int flags) ++{ ++ ssize_t err; ++ struct file *lower_file; ++ struct dentry *dentry = file->f_path.dentry; ++ struct dentry *parent; ++ ++ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT); ++ parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); ++ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); ++ ++ err = unionfs_file_revalidate(file, parent, false); ++ if (unlikely(err)) ++ goto out; ++ ++ lower_file = unionfs_lower_file(file); ++ err = vfs_splice_to(lower_file, ppos, pipe, len, flags); ++ /* update our inode atime upon a successful lower splice-read */ ++ if (err >= 0) { ++ fsstack_copy_attr_atime(dentry->d_inode, ++ lower_file->f_path.dentry->d_inode); ++ unionfs_check_file(file); ++ } ++ ++out: ++ unionfs_unlock_dentry(dentry); ++ unionfs_unlock_parent(dentry, parent); ++ unionfs_read_unlock(dentry->d_sb); ++ return err; ++} ++ ++static ssize_t unionfs_splice_write(struct pipe_inode_info *pipe, ++ struct file *file, loff_t *ppos, ++ size_t len, unsigned int flags) ++{ ++ ssize_t err = 0; ++ struct file *lower_file; ++ struct dentry *dentry = file->f_path.dentry; ++ struct dentry *parent; ++ ++ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT); ++ parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); ++ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); ++ ++ err = unionfs_file_revalidate(file, parent, true); ++ if (unlikely(err)) ++ goto out; ++ ++ lower_file = unionfs_lower_file(file); ++ err = vfs_splice_from(pipe, lower_file, ppos, len, flags); ++ /* update our inode times+sizes upon a successful lower write */ ++ if (err >= 0) { ++ fsstack_copy_inode_size(dentry->d_inode, ++ lower_file->f_path.dentry->d_inode); ++ fsstack_copy_attr_times(dentry->d_inode, ++ lower_file->f_path.dentry->d_inode); ++ unionfs_check_file(file); ++ } ++ ++out: ++ unionfs_unlock_dentry(dentry); ++ unionfs_unlock_parent(dentry, parent); ++ unionfs_read_unlock(dentry->d_sb); ++ return err; ++} ++ ++struct file_operations unionfs_main_fops = { ++ .llseek = generic_file_llseek, ++ .read = unionfs_read, ++ .write = unionfs_write, ++ .readdir = unionfs_file_readdir, ++ .unlocked_ioctl = unionfs_ioctl, ++ .mmap = unionfs_mmap, ++ .open = unionfs_open, ++ .flush = unionfs_flush, ++ .release = unionfs_file_release, ++ .fsync = unionfs_fsync, ++ .fasync = unionfs_fasync, ++ .splice_read = unionfs_splice_read, ++ .splice_write = unionfs_splice_write, ++}; +diff --git a/fs/unionfs/inode.c b/fs/unionfs/inode.c +new file mode 100644 +index 0000000..062163a +--- /dev/null ++++ b/fs/unionfs/inode.c +@@ -0,0 +1,1055 @@ ++/* ++ * Copyright (c) 2003-2010 Erez Zadok ++ * Copyright (c) 2003-2006 Charles P. Wright ++ * Copyright (c) 2005-2007 Josef 'Jeff' Sipek ++ * Copyright (c) 2005-2006 Junjiro Okajima ++ * Copyright (c) 2005 Arun M. Krishnakumar ++ * Copyright (c) 2004-2006 David P. Quigley ++ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair ++ * Copyright (c) 2003 Puja Gupta ++ * Copyright (c) 2003 Harikesavan Krishnan ++ * Copyright (c) 2003-2010 Stony Brook University ++ * Copyright (c) 2003-2010 The Research Foundation of SUNY ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "union.h" ++ ++/* ++ * Find a writeable branch to create new object in. Checks all writeble ++ * branches of the parent inode, from istart to iend order; if none are ++ * suitable, also tries branch 0 (which may require a copyup). ++ * ++ * Return a lower_dentry we can use to create object in, or ERR_PTR. ++ */ ++static struct dentry *find_writeable_branch(struct inode *parent, ++ struct dentry *dentry) ++{ ++ int err = -EINVAL; ++ int bindex, istart, iend; ++ struct dentry *lower_dentry = NULL; ++ ++ istart = ibstart(parent); ++ iend = ibend(parent); ++ if (istart < 0) ++ goto out; ++ ++begin: ++ for (bindex = istart; bindex <= iend; bindex++) { ++ /* skip non-writeable branches */ ++ err = is_robranch_super(dentry->d_sb, bindex); ++ if (err) { ++ err = -EROFS; ++ continue; ++ } ++ lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); ++ if (!lower_dentry) ++ continue; ++ /* ++ * check for whiteouts in writeable branch, and remove them ++ * if necessary. ++ */ ++ err = check_unlink_whiteout(dentry, lower_dentry, bindex); ++ if (err > 0) /* ignore if whiteout found and removed */ ++ err = 0; ++ if (err) ++ continue; ++ /* if get here, we can write to the branch */ ++ break; ++ } ++ /* ++ * If istart wasn't already branch 0, and we got any error, then try ++ * branch 0 (which may require copyup) ++ */ ++ if (err && istart > 0) { ++ istart = iend = 0; ++ goto begin; ++ } ++ ++ /* ++ * If we tried even branch 0, and still got an error, abort. But if ++ * the error was an EROFS, then we should try to copyup. ++ */ ++ if (err && err != -EROFS) ++ goto out; ++ ++ /* ++ * If we get here, then check if copyup needed. If lower_dentry is ++ * NULL, create the entire dentry directory structure in branch 0. ++ */ ++ if (!lower_dentry) { ++ bindex = 0; ++ lower_dentry = create_parents(parent, dentry, ++ dentry->d_name.name, bindex); ++ if (IS_ERR(lower_dentry)) { ++ err = PTR_ERR(lower_dentry); ++ goto out; ++ } ++ } ++ err = 0; /* all's well */ ++out: ++ if (err) ++ return ERR_PTR(err); ++ return lower_dentry; ++} ++ ++static int unionfs_create(struct inode *dir, struct dentry *dentry, ++ int mode, struct nameidata *nd_unused) ++{ ++ int err = 0; ++ struct dentry *lower_dentry = NULL; ++ struct dentry *lower_parent_dentry = NULL; ++ struct dentry *parent; ++ int valid = 0; ++ struct nameidata lower_nd; ++ ++ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); ++ parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); ++ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); ++ ++ valid = __unionfs_d_revalidate(dentry, parent, false); ++ if (unlikely(!valid)) { ++ err = -ESTALE; /* same as what real_lookup does */ ++ goto out; ++ } ++ ++ lower_dentry = find_writeable_branch(dir, dentry); ++ if (IS_ERR(lower_dentry)) { ++ err = PTR_ERR(lower_dentry); ++ goto out; ++ } ++ ++ lower_parent_dentry = lock_parent(lower_dentry); ++ if (IS_ERR(lower_parent_dentry)) { ++ err = PTR_ERR(lower_parent_dentry); ++ goto out_unlock; ++ } ++ ++ err = init_lower_nd(&lower_nd, LOOKUP_CREATE); ++ if (unlikely(err < 0)) ++ goto out_unlock; ++ err = vfs_create(lower_parent_dentry->d_inode, lower_dentry, mode, ++ &lower_nd); ++ release_lower_nd(&lower_nd, err); ++ ++ if (!err) { ++ err = PTR_ERR(unionfs_interpose(dentry, dir->i_sb, 0)); ++ if (!err) { ++ unionfs_copy_attr_times(dir); ++ fsstack_copy_inode_size(dir, ++ lower_parent_dentry->d_inode); ++ /* update no. of links on parent directory */ ++ dir->i_nlink = unionfs_get_nlinks(dir); ++ } ++ } ++ ++out_unlock: ++ unlock_dir(lower_parent_dentry); ++out: ++ if (!err) { ++ unionfs_postcopyup_setmnt(dentry); ++ unionfs_check_inode(dir); ++ unionfs_check_dentry(dentry); ++ } ++ unionfs_unlock_dentry(dentry); ++ unionfs_unlock_parent(dentry, parent); ++ unionfs_read_unlock(dentry->d_sb); ++ return err; ++} ++ ++/* ++ * unionfs_lookup is the only special function which takes a dentry, yet we ++ * do NOT want to call __unionfs_d_revalidate_chain because by definition, ++ * we don't have a valid dentry here yet. ++ */ ++static struct dentry *unionfs_lookup(struct inode *dir, ++ struct dentry *dentry, ++ struct nameidata *nd_unused) ++{ ++ struct dentry *ret, *parent; ++ int err = 0; ++ ++ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); ++ parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); ++ ++ /* ++ * As long as we lock/dget the parent, then can skip validating the ++ * parent now; we may have to rebuild this dentry on the next ++ * ->d_revalidate, however. ++ */ ++ ++ /* allocate dentry private data. We free it in ->d_release */ ++ err = new_dentry_private_data(dentry, UNIONFS_DMUTEX_CHILD); ++ if (unlikely(err)) { ++ ret = ERR_PTR(err); ++ goto out; ++ } ++ ++ ret = unionfs_lookup_full(dentry, parent, INTERPOSE_LOOKUP); ++ ++ if (!IS_ERR(ret)) { ++ if (ret) ++ dentry = ret; ++ /* lookup_full can return multiple positive dentries */ ++ if (dentry->d_inode && !S_ISDIR(dentry->d_inode->i_mode)) { ++ BUG_ON(dbstart(dentry) < 0); ++ unionfs_postcopyup_release(dentry); ++ } ++ unionfs_copy_attr_times(dentry->d_inode); ++ } ++ ++ unionfs_check_inode(dir); ++ if (!IS_ERR(ret)) ++ unionfs_check_dentry(dentry); ++ unionfs_check_dentry(parent); ++ unionfs_unlock_dentry(dentry); /* locked in new_dentry_private data */ ++ ++out: ++ unionfs_unlock_parent(dentry, parent); ++ unionfs_read_unlock(dentry->d_sb); ++ ++ return ret; ++} ++ ++static int unionfs_link(struct dentry *old_dentry, struct inode *dir, ++ struct dentry *new_dentry) ++{ ++ int err = 0; ++ struct dentry *lower_old_dentry = NULL; ++ struct dentry *lower_new_dentry = NULL; ++ struct dentry *lower_dir_dentry = NULL; ++ struct dentry *old_parent, *new_parent; ++ char *name = NULL; ++ bool valid; ++ ++ unionfs_read_lock(old_dentry->d_sb, UNIONFS_SMUTEX_CHILD); ++ old_parent = dget_parent(old_dentry); ++ new_parent = dget_parent(new_dentry); ++ unionfs_double_lock_parents(old_parent, new_parent); ++ unionfs_double_lock_dentry(old_dentry, new_dentry); ++ ++ valid = __unionfs_d_revalidate(old_dentry, old_parent, false); ++ if (unlikely(!valid)) { ++ err = -ESTALE; ++ goto out; ++ } ++ if (new_dentry->d_inode) { ++ valid = __unionfs_d_revalidate(new_dentry, new_parent, false); ++ if (unlikely(!valid)) { ++ err = -ESTALE; ++ goto out; ++ } ++ } ++ ++ lower_new_dentry = unionfs_lower_dentry(new_dentry); ++ ++ /* check for a whiteout in new dentry branch, and delete it */ ++ err = check_unlink_whiteout(new_dentry, lower_new_dentry, ++ dbstart(new_dentry)); ++ if (err > 0) { /* whiteout found and removed successfully */ ++ lower_dir_dentry = dget_parent(lower_new_dentry); ++ fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode); ++ dput(lower_dir_dentry); ++ dir->i_nlink = unionfs_get_nlinks(dir); ++ err = 0; ++ } ++ if (err) ++ goto out; ++ ++ /* check if parent hierachy is needed, then link in same branch */ ++ if (dbstart(old_dentry) != dbstart(new_dentry)) { ++ lower_new_dentry = create_parents(dir, new_dentry, ++ new_dentry->d_name.name, ++ dbstart(old_dentry)); ++ err = PTR_ERR(lower_new_dentry); ++ if (IS_COPYUP_ERR(err)) ++ goto docopyup; ++ if (!lower_new_dentry || IS_ERR(lower_new_dentry)) ++ goto out; ++ } ++ lower_new_dentry = unionfs_lower_dentry(new_dentry); ++ lower_old_dentry = unionfs_lower_dentry(old_dentry); ++ ++ BUG_ON(dbstart(old_dentry) != dbstart(new_dentry)); ++ lower_dir_dentry = lock_parent(lower_new_dentry); ++ err = is_robranch(old_dentry); ++ if (!err) { ++ /* see Documentation/filesystems/unionfs/issues.txt */ ++ lockdep_off(); ++ err = vfs_link(lower_old_dentry, lower_dir_dentry->d_inode, ++ lower_new_dentry); ++ lockdep_on(); ++ } ++ unlock_dir(lower_dir_dentry); ++ ++docopyup: ++ if (IS_COPYUP_ERR(err)) { ++ int old_bstart = dbstart(old_dentry); ++ int bindex; ++ ++ for (bindex = old_bstart - 1; bindex >= 0; bindex--) { ++ err = copyup_dentry(old_parent->d_inode, ++ old_dentry, old_bstart, ++ bindex, old_dentry->d_name.name, ++ old_dentry->d_name.len, NULL, ++ i_size_read(old_dentry->d_inode)); ++ if (err) ++ continue; ++ lower_new_dentry = ++ create_parents(dir, new_dentry, ++ new_dentry->d_name.name, ++ bindex); ++ lower_old_dentry = unionfs_lower_dentry(old_dentry); ++ lower_dir_dentry = lock_parent(lower_new_dentry); ++ /* see Documentation/filesystems/unionfs/issues.txt */ ++ lockdep_off(); ++ /* do vfs_link */ ++ err = vfs_link(lower_old_dentry, ++ lower_dir_dentry->d_inode, ++ lower_new_dentry); ++ lockdep_on(); ++ unlock_dir(lower_dir_dentry); ++ goto check_link; ++ } ++ goto out; ++ } ++ ++check_link: ++ if (err || !lower_new_dentry->d_inode) ++ goto out; ++ ++ /* Its a hard link, so use the same inode */ ++ new_dentry->d_inode = igrab(old_dentry->d_inode); ++ d_add(new_dentry, new_dentry->d_inode); ++ unionfs_copy_attr_all(dir, lower_new_dentry->d_parent->d_inode); ++ fsstack_copy_inode_size(dir, lower_new_dentry->d_parent->d_inode); ++ ++ /* propagate number of hard-links */ ++ old_dentry->d_inode->i_nlink = unionfs_get_nlinks(old_dentry->d_inode); ++ /* new dentry's ctime may have changed due to hard-link counts */ ++ unionfs_copy_attr_times(new_dentry->d_inode); ++ ++out: ++ if (!new_dentry->d_inode) ++ d_drop(new_dentry); ++ ++ kfree(name); ++ if (!err) ++ unionfs_postcopyup_setmnt(new_dentry); ++ ++ unionfs_check_inode(dir); ++ unionfs_check_dentry(new_dentry); ++ unionfs_check_dentry(old_dentry); ++ ++ unionfs_double_unlock_dentry(old_dentry, new_dentry); ++ unionfs_double_unlock_parents(old_parent, new_parent); ++ dput(new_parent); ++ dput(old_parent); ++ unionfs_read_unlock(old_dentry->d_sb); ++ ++ return err; ++} ++ ++static int unionfs_symlink(struct inode *dir, struct dentry *dentry, ++ const char *symname) ++{ ++ int err = 0; ++ struct dentry *lower_dentry = NULL; ++ struct dentry *wh_dentry = NULL; ++ struct dentry *lower_parent_dentry = NULL; ++ struct dentry *parent; ++ char *name = NULL; ++ int valid = 0; ++ umode_t mode; ++ ++ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); ++ parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); ++ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); ++ ++ valid = __unionfs_d_revalidate(dentry, parent, false); ++ if (unlikely(!valid)) { ++ err = -ESTALE; ++ goto out; ++ } ++ ++ /* ++ * It's only a bug if this dentry was not negative and couldn't be ++ * revalidated (shouldn't happen). ++ */ ++ BUG_ON(!valid && dentry->d_inode); ++ ++ lower_dentry = find_writeable_branch(dir, dentry); ++ if (IS_ERR(lower_dentry)) { ++ err = PTR_ERR(lower_dentry); ++ goto out; ++ } ++ ++ lower_parent_dentry = lock_parent(lower_dentry); ++ if (IS_ERR(lower_parent_dentry)) { ++ err = PTR_ERR(lower_parent_dentry); ++ goto out_unlock; ++ } ++ ++ mode = S_IALLUGO; ++ err = vfs_symlink(lower_parent_dentry->d_inode, lower_dentry, symname); ++ if (!err) { ++ err = PTR_ERR(unionfs_interpose(dentry, dir->i_sb, 0)); ++ if (!err) { ++ unionfs_copy_attr_times(dir); ++ fsstack_copy_inode_size(dir, ++ lower_parent_dentry->d_inode); ++ /* update no. of links on parent directory */ ++ dir->i_nlink = unionfs_get_nlinks(dir); ++ } ++ } ++ ++out_unlock: ++ unlock_dir(lower_parent_dentry); ++out: ++ dput(wh_dentry); ++ kfree(name); ++ ++ if (!err) { ++ unionfs_postcopyup_setmnt(dentry); ++ unionfs_check_inode(dir); ++ unionfs_check_dentry(dentry); ++ } ++ unionfs_unlock_dentry(dentry); ++ unionfs_unlock_parent(dentry, parent); ++ unionfs_read_unlock(dentry->d_sb); ++ return err; ++} ++ ++static int unionfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) ++{ ++ int err = 0; ++ struct dentry *lower_dentry = NULL; ++ struct dentry *lower_parent_dentry = NULL; ++ struct dentry *parent; ++ int bindex = 0, bstart; ++ char *name = NULL; ++ int valid; ++ ++ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); ++ parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); ++ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); ++ ++ valid = __unionfs_d_revalidate(dentry, parent, false); ++ if (unlikely(!valid)) { ++ err = -ESTALE; /* same as what real_lookup does */ ++ goto out; ++ } ++ ++ bstart = dbstart(dentry); ++ ++ lower_dentry = unionfs_lower_dentry(dentry); ++ ++ /* check for a whiteout in new dentry branch, and delete it */ ++ err = check_unlink_whiteout(dentry, lower_dentry, bstart); ++ if (err > 0) /* whiteout found and removed successfully */ ++ err = 0; ++ if (err) { ++ /* exit if the error returned was NOT -EROFS */ ++ if (!IS_COPYUP_ERR(err)) ++ goto out; ++ bstart--; ++ } ++ ++ /* check if copyup's needed, and mkdir */ ++ for (bindex = bstart; bindex >= 0; bindex--) { ++ int i; ++ int bend = dbend(dentry); ++ ++ if (is_robranch_super(dentry->d_sb, bindex)) ++ continue; ++ ++ lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); ++ if (!lower_dentry) { ++ lower_dentry = create_parents(dir, dentry, ++ dentry->d_name.name, ++ bindex); ++ if (!lower_dentry || IS_ERR(lower_dentry)) { ++ printk(KERN_ERR "unionfs: lower dentry " ++ " NULL for bindex = %d\n", bindex); ++ continue; ++ } ++ } ++ ++ lower_parent_dentry = lock_parent(lower_dentry); ++ ++ if (IS_ERR(lower_parent_dentry)) { ++ err = PTR_ERR(lower_parent_dentry); ++ goto out; ++ } ++ ++ err = vfs_mkdir(lower_parent_dentry->d_inode, lower_dentry, ++ mode); ++ ++ unlock_dir(lower_parent_dentry); ++ ++ /* did the mkdir succeed? */ ++ if (err) ++ break; ++ ++ for (i = bindex + 1; i <= bend; i++) { ++ /* XXX: use path_put_lowers? */ ++ if (unionfs_lower_dentry_idx(dentry, i)) { ++ dput(unionfs_lower_dentry_idx(dentry, i)); ++ unionfs_set_lower_dentry_idx(dentry, i, NULL); ++ } ++ } ++ dbend(dentry) = bindex; ++ ++ /* ++ * Only INTERPOSE_LOOKUP can return a value other than 0 on ++ * err. ++ */ ++ err = PTR_ERR(unionfs_interpose(dentry, dir->i_sb, 0)); ++ if (!err) { ++ unionfs_copy_attr_times(dir); ++ fsstack_copy_inode_size(dir, ++ lower_parent_dentry->d_inode); ++ ++ /* update number of links on parent directory */ ++ dir->i_nlink = unionfs_get_nlinks(dir); ++ } ++ ++ err = make_dir_opaque(dentry, dbstart(dentry)); ++ if (err) { ++ printk(KERN_ERR "unionfs: mkdir: error creating " ++ ".wh.__dir_opaque: %d\n", err); ++ goto out; ++ } ++ ++ /* we are done! */ ++ break; ++ } ++ ++out: ++ if (!dentry->d_inode) ++ d_drop(dentry); ++ ++ kfree(name); ++ ++ if (!err) { ++ unionfs_copy_attr_times(dentry->d_inode); ++ unionfs_postcopyup_setmnt(dentry); ++ } ++ unionfs_check_inode(dir); ++ unionfs_check_dentry(dentry); ++ unionfs_unlock_dentry(dentry); ++ unionfs_unlock_parent(dentry, parent); ++ unionfs_read_unlock(dentry->d_sb); ++ ++ return err; ++} ++ ++static int unionfs_mknod(struct inode *dir, struct dentry *dentry, int mode, ++ dev_t dev) ++{ ++ int err = 0; ++ struct dentry *lower_dentry = NULL; ++ struct dentry *wh_dentry = NULL; ++ struct dentry *lower_parent_dentry = NULL; ++ struct dentry *parent; ++ char *name = NULL; ++ int valid = 0; ++ ++ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); ++ parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); ++ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); ++ ++ valid = __unionfs_d_revalidate(dentry, parent, false); ++ if (unlikely(!valid)) { ++ err = -ESTALE; ++ goto out; ++ } ++ ++ /* ++ * It's only a bug if this dentry was not negative and couldn't be ++ * revalidated (shouldn't happen). ++ */ ++ BUG_ON(!valid && dentry->d_inode); ++ ++ lower_dentry = find_writeable_branch(dir, dentry); ++ if (IS_ERR(lower_dentry)) { ++ err = PTR_ERR(lower_dentry); ++ goto out; ++ } ++ ++ lower_parent_dentry = lock_parent(lower_dentry); ++ if (IS_ERR(lower_parent_dentry)) { ++ err = PTR_ERR(lower_parent_dentry); ++ goto out_unlock; ++ } ++ ++ err = vfs_mknod(lower_parent_dentry->d_inode, lower_dentry, mode, dev); ++ if (!err) { ++ err = PTR_ERR(unionfs_interpose(dentry, dir->i_sb, 0)); ++ if (!err) { ++ unionfs_copy_attr_times(dir); ++ fsstack_copy_inode_size(dir, ++ lower_parent_dentry->d_inode); ++ /* update no. of links on parent directory */ ++ dir->i_nlink = unionfs_get_nlinks(dir); ++ } ++ } ++ ++out_unlock: ++ unlock_dir(lower_parent_dentry); ++out: ++ dput(wh_dentry); ++ kfree(name); ++ ++ if (!err) { ++ unionfs_postcopyup_setmnt(dentry); ++ unionfs_check_inode(dir); ++ unionfs_check_dentry(dentry); ++ } ++ unionfs_unlock_dentry(dentry); ++ unionfs_unlock_parent(dentry, parent); ++ unionfs_read_unlock(dentry->d_sb); ++ return err; ++} ++ ++/* requires sb, dentry, and parent to already be locked */ ++static int __unionfs_readlink(struct dentry *dentry, char __user *buf, ++ int bufsiz) ++{ ++ int err; ++ struct dentry *lower_dentry; ++ ++ lower_dentry = unionfs_lower_dentry(dentry); ++ ++ if (!lower_dentry->d_inode->i_op || ++ !lower_dentry->d_inode->i_op->readlink) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ err = lower_dentry->d_inode->i_op->readlink(lower_dentry, ++ buf, bufsiz); ++ if (err >= 0) ++ fsstack_copy_attr_atime(dentry->d_inode, ++ lower_dentry->d_inode); ++ ++out: ++ return err; ++} ++ ++static int unionfs_readlink(struct dentry *dentry, char __user *buf, ++ int bufsiz) ++{ ++ int err; ++ struct dentry *parent; ++ ++ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); ++ parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); ++ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); ++ ++ if (unlikely(!__unionfs_d_revalidate(dentry, parent, false))) { ++ err = -ESTALE; ++ goto out; ++ } ++ ++ err = __unionfs_readlink(dentry, buf, bufsiz); ++ ++out: ++ unionfs_check_dentry(dentry); ++ unionfs_unlock_dentry(dentry); ++ unionfs_unlock_parent(dentry, parent); ++ unionfs_read_unlock(dentry->d_sb); ++ ++ return err; ++} ++ ++static void *unionfs_follow_link(struct dentry *dentry, struct nameidata *nd) ++{ ++ char *buf; ++ int len = PAGE_SIZE, err; ++ mm_segment_t old_fs; ++ struct dentry *parent; ++ ++ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); ++ parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); ++ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); ++ ++ /* This is freed by the put_link method assuming a successful call. */ ++ buf = kmalloc(len, GFP_KERNEL); ++ if (unlikely(!buf)) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ /* read the symlink, and then we will follow it */ ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ err = __unionfs_readlink(dentry, buf, len); ++ set_fs(old_fs); ++ if (err < 0) { ++ kfree(buf); ++ buf = NULL; ++ goto out; ++ } ++ buf[err] = 0; ++ nd_set_link(nd, buf); ++ err = 0; ++ ++out: ++ if (err >= 0) { ++ unionfs_check_nd(nd); ++ unionfs_check_dentry(dentry); ++ } ++ ++ unionfs_unlock_dentry(dentry); ++ unionfs_unlock_parent(dentry, parent); ++ unionfs_read_unlock(dentry->d_sb); ++ ++ return ERR_PTR(err); ++} ++ ++/* this @nd *IS* still used */ ++static void unionfs_put_link(struct dentry *dentry, struct nameidata *nd, ++ void *cookie) ++{ ++ struct dentry *parent; ++ ++ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); ++ parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); ++ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); ++ ++ if (unlikely(!__unionfs_d_revalidate(dentry, parent, false))) ++ printk(KERN_ERR ++ "unionfs: put_link failed to revalidate dentry\n"); ++ ++ unionfs_check_dentry(dentry); ++ unionfs_check_nd(nd); ++ kfree(nd_get_link(nd)); ++ unionfs_unlock_dentry(dentry); ++ unionfs_unlock_parent(dentry, parent); ++ unionfs_read_unlock(dentry->d_sb); ++} ++ ++/* ++ * This is a variant of fs/namei.c:permission() or inode_permission() which ++ * skips over EROFS tests (because we perform copyup on EROFS). ++ */ ++static int __inode_permission(struct inode *inode, int mask) ++{ ++ int retval; ++ ++ /* nobody gets write access to an immutable file */ ++ if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode)) ++ return -EACCES; ++ ++ /* Ordinary permission routines do not understand MAY_APPEND. */ ++ if (inode->i_op && inode->i_op->permission) { ++ retval = inode->i_op->permission(inode, mask); ++ if (!retval) { ++ /* ++ * Exec permission on a regular file is denied if none ++ * of the execute bits are set. ++ * ++ * This check should be done by the ->permission() ++ * method. ++ */ ++ if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode) && ++ !(inode->i_mode & S_IXUGO)) ++ return -EACCES; ++ } ++ } else { ++ retval = generic_permission(inode, mask, NULL); ++ } ++ if (retval) ++ return retval; ++ ++ return security_inode_permission(inode, ++ mask & (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND)); ++} ++ ++/* ++ * Don't grab the superblock read-lock in unionfs_permission, which prevents ++ * a deadlock with the branch-management "add branch" code (which grabbed ++ * the write lock). It is safe to not grab the read lock here, because even ++ * with branch management taking place, there is no chance that ++ * unionfs_permission, or anything it calls, will use stale branch ++ * information. ++ */ ++static int unionfs_permission(struct inode *inode, int mask) ++{ ++ struct inode *lower_inode = NULL; ++ int err = 0; ++ int bindex, bstart, bend; ++ const int is_file = !S_ISDIR(inode->i_mode); ++ const int write_mask = (mask & MAY_WRITE) && !(mask & MAY_READ); ++ struct inode *inode_grabbed = igrab(inode); ++ struct dentry *dentry = d_find_alias(inode); ++ ++ if (dentry) ++ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); ++ ++ if (!UNIONFS_I(inode)->lower_inodes) { ++ if (is_file) /* dirs can be unlinked but chdir'ed to */ ++ err = -ESTALE; /* force revalidate */ ++ goto out; ++ } ++ bstart = ibstart(inode); ++ bend = ibend(inode); ++ if (unlikely(bstart < 0 || bend < 0)) { ++ /* ++ * With branch-management, we can get a stale inode here. ++ * If so, we return ESTALE back to link_path_walk, which ++ * would discard the dcache entry and re-lookup the ++ * dentry+inode. This should be equivalent to issuing ++ * __unionfs_d_revalidate_chain on nd.dentry here. ++ */ ++ if (is_file) /* dirs can be unlinked but chdir'ed to */ ++ err = -ESTALE; /* force revalidate */ ++ goto out; ++ } ++ ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ lower_inode = unionfs_lower_inode_idx(inode, bindex); ++ if (!lower_inode) ++ continue; ++ ++ /* ++ * check the condition for D-F-D underlying files/directories, ++ * we don't have to check for files, if we are checking for ++ * directories. ++ */ ++ if (!is_file && !S_ISDIR(lower_inode->i_mode)) ++ continue; ++ ++ /* ++ * We check basic permissions, but we ignore any conditions ++ * such as readonly file systems or branches marked as ++ * readonly, because those conditions should lead to a ++ * copyup taking place later on. However, if user never had ++ * access to the file, then no copyup could ever take place. ++ */ ++ err = __inode_permission(lower_inode, mask); ++ if (err && err != -EACCES && err != EPERM && bindex > 0) { ++ umode_t mode = lower_inode->i_mode; ++ if ((is_robranch_super(inode->i_sb, bindex) || ++ __is_rdonly(lower_inode)) && ++ (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) ++ err = 0; ++ if (IS_COPYUP_ERR(err)) ++ err = 0; ++ } ++ ++ /* ++ * NFS HACK: NFSv2/3 return EACCES on readonly-exported, ++ * locally readonly-mounted file systems, instead of EROFS ++ * like other file systems do. So we have no choice here ++ * but to intercept this and ignore it for NFS branches ++ * marked readonly. Specifically, we avoid using NFS's own ++ * "broken" ->permission method, and rely on ++ * generic_permission() to do basic checking for us. ++ */ ++ if (err && err == -EACCES && ++ is_robranch_super(inode->i_sb, bindex) && ++ lower_inode->i_sb->s_magic == NFS_SUPER_MAGIC) ++ err = generic_permission(lower_inode, mask, NULL); ++ ++ /* ++ * The permissions are an intersection of the overall directory ++ * permissions, so we fail if one fails. ++ */ ++ if (err) ++ goto out; ++ ++ /* only the leftmost file matters. */ ++ if (is_file || write_mask) { ++ if (is_file && write_mask) { ++ err = get_write_access(lower_inode); ++ if (!err) ++ put_write_access(lower_inode); ++ } ++ break; ++ } ++ } ++ /* sync times which may have changed (asynchronously) below */ ++ unionfs_copy_attr_times(inode); ++ ++out: ++ unionfs_check_inode(inode); ++ if (dentry) { ++ unionfs_unlock_dentry(dentry); ++ dput(dentry); ++ } ++ iput(inode_grabbed); ++ return err; ++} ++ ++static int unionfs_setattr(struct dentry *dentry, struct iattr *ia) ++{ ++ int err = 0; ++ struct dentry *lower_dentry; ++ struct dentry *parent; ++ struct inode *inode; ++ struct inode *lower_inode; ++ int bstart, bend, bindex; ++ loff_t size; ++ ++ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); ++ parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); ++ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); ++ ++ if (unlikely(!__unionfs_d_revalidate(dentry, parent, false))) { ++ err = -ESTALE; ++ goto out; ++ } ++ ++ bstart = dbstart(dentry); ++ bend = dbend(dentry); ++ inode = dentry->d_inode; ++ ++ /* ++ * mode change is for clearing setuid/setgid. Allow lower filesystem ++ * to reinterpret it in its own way. ++ */ ++ if (ia->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID)) ++ ia->ia_valid &= ~ATTR_MODE; ++ ++ lower_dentry = unionfs_lower_dentry(dentry); ++ if (!lower_dentry) { /* should never happen after above revalidate */ ++ err = -EINVAL; ++ goto out; ++ } ++ lower_inode = unionfs_lower_inode(inode); ++ ++ /* check if user has permission to change lower inode */ ++ err = inode_change_ok(lower_inode, ia); ++ if (err) ++ goto out; ++ ++ /* copyup if the file is on a read only branch */ ++ if (is_robranch_super(dentry->d_sb, bstart) ++ || __is_rdonly(lower_inode)) { ++ /* check if we have a branch to copy up to */ ++ if (bstart <= 0) { ++ err = -EACCES; ++ goto out; ++ } ++ ++ if (ia->ia_valid & ATTR_SIZE) ++ size = ia->ia_size; ++ else ++ size = i_size_read(inode); ++ /* copyup to next available branch */ ++ for (bindex = bstart - 1; bindex >= 0; bindex--) { ++ err = copyup_dentry(parent->d_inode, ++ dentry, bstart, bindex, ++ dentry->d_name.name, ++ dentry->d_name.len, ++ NULL, size); ++ if (!err) ++ break; ++ } ++ if (err) ++ goto out; ++ /* get updated lower_dentry/inode after copyup */ ++ lower_dentry = unionfs_lower_dentry(dentry); ++ lower_inode = unionfs_lower_inode(inode); ++ } ++ ++ /* ++ * If shrinking, first truncate upper level to cancel writing dirty ++ * pages beyond the new eof; and also if its' maxbytes is more ++ * limiting (fail with -EFBIG before making any change to the lower ++ * level). There is no need to vmtruncate the upper level ++ * afterwards in the other cases: we fsstack_copy_inode_size from ++ * the lower level. ++ */ ++ if (ia->ia_valid & ATTR_SIZE) { ++ size = i_size_read(inode); ++ if (ia->ia_size < size || (ia->ia_size > size && ++ inode->i_sb->s_maxbytes < lower_inode->i_sb->s_maxbytes)) { ++ err = vmtruncate(inode, ia->ia_size); ++ if (err) ++ goto out; ++ } ++ } ++ ++ /* notify the (possibly copied-up) lower inode */ ++ /* ++ * Note: we use lower_dentry->d_inode, because lower_inode may be ++ * unlinked (no inode->i_sb and i_ino==0. This happens if someone ++ * tries to open(), unlink(), then ftruncate() a file. ++ */ ++ mutex_lock(&lower_dentry->d_inode->i_mutex); ++ err = notify_change(lower_dentry, ia); ++ mutex_unlock(&lower_dentry->d_inode->i_mutex); ++ if (err) ++ goto out; ++ ++ /* get attributes from the first lower inode */ ++ if (ibstart(inode) >= 0) ++ unionfs_copy_attr_all(inode, lower_inode); ++ /* ++ * unionfs_copy_attr_all will copy the lower times to our inode if ++ * the lower ones are newer (useful for cache coherency). However, ++ * ->setattr is the only place in which we may have to copy the ++ * lower inode times absolutely, to support utimes(2). ++ */ ++ if (ia->ia_valid & ATTR_MTIME_SET) ++ inode->i_mtime = lower_inode->i_mtime; ++ if (ia->ia_valid & ATTR_CTIME) ++ inode->i_ctime = lower_inode->i_ctime; ++ if (ia->ia_valid & ATTR_ATIME_SET) ++ inode->i_atime = lower_inode->i_atime; ++ fsstack_copy_inode_size(inode, lower_inode); ++ ++out: ++ if (!err) ++ unionfs_check_dentry(dentry); ++ unionfs_unlock_dentry(dentry); ++ unionfs_unlock_parent(dentry, parent); ++ unionfs_read_unlock(dentry->d_sb); ++ ++ return err; ++} ++ ++struct inode_operations unionfs_symlink_iops = { ++ .readlink = unionfs_readlink, ++ .permission = unionfs_permission, ++ .follow_link = unionfs_follow_link, ++ .setattr = unionfs_setattr, ++ .put_link = unionfs_put_link, ++}; ++ ++struct inode_operations unionfs_dir_iops = { ++ .create = unionfs_create, ++ .lookup = unionfs_lookup, ++ .link = unionfs_link, ++ .unlink = unionfs_unlink, ++ .symlink = unionfs_symlink, ++ .mkdir = unionfs_mkdir, ++ .rmdir = unionfs_rmdir, ++ .mknod = unionfs_mknod, ++ .rename = unionfs_rename, ++ .permission = unionfs_permission, ++ .setattr = unionfs_setattr, ++#ifdef CONFIG_UNION_FS_XATTR ++ .setxattr = unionfs_setxattr, ++ .getxattr = unionfs_getxattr, ++ .removexattr = unionfs_removexattr, ++ .listxattr = unionfs_listxattr, ++#endif /* CONFIG_UNION_FS_XATTR */ ++}; ++ ++struct inode_operations unionfs_main_iops = { ++ .permission = unionfs_permission, ++ .setattr = unionfs_setattr, ++#ifdef CONFIG_UNION_FS_XATTR ++ .setxattr = unionfs_setxattr, ++ .getxattr = unionfs_getxattr, ++ .removexattr = unionfs_removexattr, ++ .listxattr = unionfs_listxattr, ++#endif /* CONFIG_UNION_FS_XATTR */ ++}; +diff --git a/fs/unionfs/lookup.c b/fs/unionfs/lookup.c +new file mode 100644 +index 0000000..b63c17e +--- /dev/null ++++ b/fs/unionfs/lookup.c +@@ -0,0 +1,569 @@ ++/* ++ * Copyright (c) 2003-2010 Erez Zadok ++ * Copyright (c) 2003-2006 Charles P. Wright ++ * Copyright (c) 2005-2007 Josef 'Jeff' Sipek ++ * Copyright (c) 2005-2006 Junjiro Okajima ++ * Copyright (c) 2005 Arun M. Krishnakumar ++ * Copyright (c) 2004-2006 David P. Quigley ++ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair ++ * Copyright (c) 2003 Puja Gupta ++ * Copyright (c) 2003 Harikesavan Krishnan ++ * Copyright (c) 2003-2010 Stony Brook University ++ * Copyright (c) 2003-2010 The Research Foundation of SUNY ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "union.h" ++ ++/* ++ * Lookup one path component @name relative to a <base,mnt> path pair. ++ * Behaves nearly the same as lookup_one_len (i.e., return negative dentry ++ * on ENOENT), but uses the @mnt passed, so it can cross bind mounts and ++ * other lower mounts properly. If @new_mnt is non-null, will fill in the ++ * new mnt there. Caller is responsible to dput/mntput/path_put returned ++ * @dentry and @new_mnt. ++ */ ++struct dentry *__lookup_one(struct dentry *base, struct vfsmount *mnt, ++ const char *name, struct vfsmount **new_mnt) ++{ ++ struct dentry *dentry = NULL; ++ struct nameidata lower_nd; ++ int err; ++ ++ /* we use flags=0 to get basic lookup */ ++ err = vfs_path_lookup(base, mnt, name, 0, &lower_nd); ++ ++ switch (err) { ++ case 0: /* no error */ ++ dentry = lower_nd.path.dentry; ++ if (new_mnt) ++ *new_mnt = lower_nd.path.mnt; /* rc already inc'ed */ ++ break; ++ case -ENOENT: ++ /* ++ * We don't consider ENOENT an error, and we want to return ++ * a negative dentry (ala lookup_one_len). As we know ++ * there was no inode for this name before (-ENOENT), then ++ * it's safe to call lookup_one_len (which doesn't take a ++ * vfsmount). ++ */ ++ dentry = lookup_lck_len(name, base, strlen(name)); ++ if (new_mnt) ++ *new_mnt = mntget(lower_nd.path.mnt); ++ break; ++ default: /* all other real errors */ ++ dentry = ERR_PTR(err); ++ break; ++ } ++ ++ return dentry; ++} ++ ++/* ++ * This is a utility function that fills in a unionfs dentry. ++ * Caller must lock this dentry with unionfs_lock_dentry. ++ * ++ * Returns: 0 (ok), or -ERRNO if an error occurred. ++ * XXX: get rid of _partial_lookup and make callers call _lookup_full directly ++ */ ++int unionfs_partial_lookup(struct dentry *dentry, struct dentry *parent) ++{ ++ struct dentry *tmp; ++ int err = -ENOSYS; ++ ++ tmp = unionfs_lookup_full(dentry, parent, INTERPOSE_PARTIAL); ++ ++ if (!tmp) { ++ err = 0; ++ goto out; ++ } ++ if (IS_ERR(tmp)) { ++ err = PTR_ERR(tmp); ++ goto out; ++ } ++ /* XXX: need to change the interface */ ++ BUG_ON(tmp != dentry); ++out: ++ return err; ++} ++ ++/* The dentry cache is just so we have properly sized dentries. */ ++static struct kmem_cache *unionfs_dentry_cachep; ++int unionfs_init_dentry_cache(void) ++{ ++ unionfs_dentry_cachep = ++ kmem_cache_create("unionfs_dentry", ++ sizeof(struct unionfs_dentry_info), ++ 0, SLAB_RECLAIM_ACCOUNT, NULL); ++ ++ return (unionfs_dentry_cachep ? 0 : -ENOMEM); ++} ++ ++void unionfs_destroy_dentry_cache(void) ++{ ++ if (unionfs_dentry_cachep) ++ kmem_cache_destroy(unionfs_dentry_cachep); ++} ++ ++void free_dentry_private_data(struct dentry *dentry) ++{ ++ if (!dentry || !dentry->d_fsdata) ++ return; ++ kfree(UNIONFS_D(dentry)->lower_paths); ++ UNIONFS_D(dentry)->lower_paths = NULL; ++ kmem_cache_free(unionfs_dentry_cachep, dentry->d_fsdata); ++ dentry->d_fsdata = NULL; ++} ++ ++static inline int __realloc_dentry_private_data(struct dentry *dentry) ++{ ++ struct unionfs_dentry_info *info = UNIONFS_D(dentry); ++ void *p; ++ int size; ++ ++ BUG_ON(!info); ++ ++ size = sizeof(struct path) * sbmax(dentry->d_sb); ++ p = krealloc(info->lower_paths, size, GFP_ATOMIC); ++ if (unlikely(!p)) ++ return -ENOMEM; ++ ++ info->lower_paths = p; ++ ++ info->bstart = -1; ++ info->bend = -1; ++ info->bopaque = -1; ++ info->bcount = sbmax(dentry->d_sb); ++ atomic_set(&info->generation, ++ atomic_read(&UNIONFS_SB(dentry->d_sb)->generation)); ++ ++ memset(info->lower_paths, 0, size); ++ ++ return 0; ++} ++ ++/* UNIONFS_D(dentry)->lock must be locked */ ++int realloc_dentry_private_data(struct dentry *dentry) ++{ ++ if (!__realloc_dentry_private_data(dentry)) ++ return 0; ++ ++ kfree(UNIONFS_D(dentry)->lower_paths); ++ free_dentry_private_data(dentry); ++ return -ENOMEM; ++} ++ ++/* allocate new dentry private data */ ++int new_dentry_private_data(struct dentry *dentry, int subclass) ++{ ++ struct unionfs_dentry_info *info = UNIONFS_D(dentry); ++ ++ BUG_ON(info); ++ ++ info = kmem_cache_alloc(unionfs_dentry_cachep, GFP_ATOMIC); ++ if (unlikely(!info)) ++ return -ENOMEM; ++ ++ mutex_init(&info->lock); ++ mutex_lock_nested(&info->lock, subclass); ++ ++ info->lower_paths = NULL; ++ ++ dentry->d_fsdata = info; ++ ++ if (!__realloc_dentry_private_data(dentry)) ++ return 0; ++ ++ mutex_unlock(&info->lock); ++ free_dentry_private_data(dentry); ++ return -ENOMEM; ++} ++ ++/* ++ * scan through the lower dentry objects, and set bstart to reflect the ++ * starting branch ++ */ ++void update_bstart(struct dentry *dentry) ++{ ++ int bindex; ++ int bstart = dbstart(dentry); ++ int bend = dbend(dentry); ++ struct dentry *lower_dentry; ++ ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); ++ if (!lower_dentry) ++ continue; ++ if (lower_dentry->d_inode) { ++ dbstart(dentry) = bindex; ++ break; ++ } ++ dput(lower_dentry); ++ unionfs_set_lower_dentry_idx(dentry, bindex, NULL); ++ } ++} ++ ++ ++/* ++ * Initialize a nameidata structure (the intent part) we can pass to a lower ++ * file system. Returns 0 on success or -error (only -ENOMEM possible). ++ * Inside that nd structure, this function may also return an allocated ++ * struct file (for open intents). The caller, when done with this nd, must ++ * kfree the intent file (using release_lower_nd). ++ * ++ * XXX: this code, and the callers of this code, should be redone using ++ * vfs_path_lookup() when (1) the nameidata structure is refactored into a ++ * separate intent-structure, and (2) open_namei() is broken into a VFS-only ++ * function and a method that other file systems can call. ++ */ ++int init_lower_nd(struct nameidata *nd, unsigned int flags) ++{ ++ int err = 0; ++#ifdef ALLOC_LOWER_ND_FILE ++ /* ++ * XXX: one day we may need to have the lower return an open file ++ * for us. It is not needed in 2.6.23-rc1 for nfs2/nfs3, but may ++ * very well be needed for nfs4. ++ */ ++ struct file *file; ++#endif /* ALLOC_LOWER_ND_FILE */ ++ ++ memset(nd, 0, sizeof(struct nameidata)); ++ if (!flags) ++ return err; ++ ++ switch (flags) { ++ case LOOKUP_CREATE: ++ nd->intent.open.flags |= O_CREAT; ++ /* fall through: shared code for create/open cases */ ++ case LOOKUP_OPEN: ++ nd->flags = flags; ++ nd->intent.open.flags |= (FMODE_READ | FMODE_WRITE); ++#ifdef ALLOC_LOWER_ND_FILE ++ file = kzalloc(sizeof(struct file), GFP_KERNEL); ++ if (unlikely(!file)) { ++ err = -ENOMEM; ++ break; /* exit switch statement and thus return */ ++ } ++ nd->intent.open.file = file; ++#endif /* ALLOC_LOWER_ND_FILE */ ++ break; ++ default: ++ /* ++ * We should never get here, for now. ++ * We can add new cases here later on. ++ */ ++ pr_debug("unionfs: unknown nameidata flag 0x%x\n", flags); ++ BUG(); ++ break; ++ } ++ ++ return err; ++} ++ ++void release_lower_nd(struct nameidata *nd, int err) ++{ ++ if (!nd->intent.open.file) ++ return; ++ else if (!err) ++ release_open_intent(nd); ++#ifdef ALLOC_LOWER_ND_FILE ++ kfree(nd->intent.open.file); ++#endif /* ALLOC_LOWER_ND_FILE */ ++} ++ ++/* ++ * Main (and complex) driver function for Unionfs's lookup ++ * ++ * Returns: NULL (ok), ERR_PTR if an error occurred, or a non-null non-error ++ * PTR if d_splice returned a different dentry. ++ * ++ * If lookupmode is INTERPOSE_PARTIAL/REVAL/REVAL_NEG, the passed dentry's ++ * inode info must be locked. If lookupmode is INTERPOSE_LOOKUP (i.e., a ++ * newly looked-up dentry), then unionfs_lookup_backend will return a locked ++ * dentry's info, which the caller must unlock. ++ */ ++struct dentry *unionfs_lookup_full(struct dentry *dentry, ++ struct dentry *parent, int lookupmode) ++{ ++ int err = 0; ++ struct dentry *lower_dentry = NULL; ++ struct vfsmount *lower_mnt; ++ struct vfsmount *lower_dir_mnt; ++ struct dentry *wh_lower_dentry = NULL; ++ struct dentry *lower_dir_dentry = NULL; ++ struct dentry *d_interposed = NULL; ++ int bindex, bstart, bend, bopaque; ++ int opaque, num_positive = 0; ++ const char *name; ++ int namelen; ++ int pos_start, pos_end; ++ ++ /* ++ * We should already have a lock on this dentry in the case of a ++ * partial lookup, or a revalidation. Otherwise it is returned from ++ * new_dentry_private_data already locked. ++ */ ++ verify_locked(dentry); ++ verify_locked(parent); ++ ++ /* must initialize dentry operations */ ++ dentry->d_op = &unionfs_dops; ++ ++ /* We never partial lookup the root directory. */ ++ if (IS_ROOT(dentry)) ++ goto out; ++ ++ name = dentry->d_name.name; ++ namelen = dentry->d_name.len; ++ ++ /* No dentries should get created for possible whiteout names. */ ++ if (!is_validname(name)) { ++ err = -EPERM; ++ goto out_free; ++ } ++ ++ /* Now start the actual lookup procedure. */ ++ bstart = dbstart(parent); ++ bend = dbend(parent); ++ bopaque = dbopaque(parent); ++ BUG_ON(bstart < 0); ++ ++ /* adjust bend to bopaque if needed */ ++ if ((bopaque >= 0) && (bopaque < bend)) ++ bend = bopaque; ++ ++ /* lookup all possible dentries */ ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ ++ lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); ++ lower_mnt = unionfs_lower_mnt_idx(dentry, bindex); ++ ++ /* skip if we already have a positive lower dentry */ ++ if (lower_dentry) { ++ if (dbstart(dentry) < 0) ++ dbstart(dentry) = bindex; ++ if (bindex > dbend(dentry)) ++ dbend(dentry) = bindex; ++ if (lower_dentry->d_inode) ++ num_positive++; ++ continue; ++ } ++ ++ lower_dir_dentry = ++ unionfs_lower_dentry_idx(parent, bindex); ++ /* if the lower dentry's parent does not exist, skip this */ ++ if (!lower_dir_dentry || !lower_dir_dentry->d_inode) ++ continue; ++ ++ /* also skip it if the parent isn't a directory. */ ++ if (!S_ISDIR(lower_dir_dentry->d_inode->i_mode)) ++ continue; /* XXX: should be BUG_ON */ ++ ++ /* check for whiteouts: stop lookup if found */ ++ wh_lower_dentry = lookup_whiteout(name, lower_dir_dentry); ++ if (IS_ERR(wh_lower_dentry)) { ++ err = PTR_ERR(wh_lower_dentry); ++ goto out_free; ++ } ++ if (wh_lower_dentry->d_inode) { ++ dbend(dentry) = dbopaque(dentry) = bindex; ++ if (dbstart(dentry) < 0) ++ dbstart(dentry) = bindex; ++ dput(wh_lower_dentry); ++ break; ++ } ++ dput(wh_lower_dentry); ++ ++ /* Now do regular lookup; lookup @name */ ++ lower_dir_mnt = unionfs_lower_mnt_idx(parent, bindex); ++ lower_mnt = NULL; /* XXX: needed? */ ++ ++ lower_dentry = __lookup_one(lower_dir_dentry, lower_dir_mnt, ++ name, &lower_mnt); ++ ++ if (IS_ERR(lower_dentry)) { ++ err = PTR_ERR(lower_dentry); ++ goto out_free; ++ } ++ unionfs_set_lower_dentry_idx(dentry, bindex, lower_dentry); ++ if (!lower_mnt) ++ lower_mnt = unionfs_mntget(dentry->d_sb->s_root, ++ bindex); ++ unionfs_set_lower_mnt_idx(dentry, bindex, lower_mnt); ++ ++ /* adjust dbstart/end */ ++ if (dbstart(dentry) < 0) ++ dbstart(dentry) = bindex; ++ if (bindex > dbend(dentry)) ++ dbend(dentry) = bindex; ++ /* ++ * We always store the lower dentries above, and update ++ * dbstart/dbend, even if the whole unionfs dentry is ++ * negative (i.e., no lower inodes). ++ */ ++ if (!lower_dentry->d_inode) ++ continue; ++ num_positive++; ++ ++ /* ++ * check if we just found an opaque directory, if so, stop ++ * lookups here. ++ */ ++ if (!S_ISDIR(lower_dentry->d_inode->i_mode)) ++ continue; ++ opaque = is_opaque_dir(dentry, bindex); ++ if (opaque < 0) { ++ err = opaque; ++ goto out_free; ++ } else if (opaque) { ++ dbend(dentry) = dbopaque(dentry) = bindex; ++ break; ++ } ++ dbend(dentry) = bindex; ++ ++ /* update parent directory's atime with the bindex */ ++ fsstack_copy_attr_atime(parent->d_inode, ++ lower_dir_dentry->d_inode); ++ } ++ ++ /* sanity checks, then decide if to process a negative dentry */ ++ BUG_ON(dbstart(dentry) < 0 && dbend(dentry) >= 0); ++ BUG_ON(dbstart(dentry) >= 0 && dbend(dentry) < 0); ++ ++ if (num_positive > 0) ++ goto out_positive; ++ ++ /*** handle NEGATIVE dentries ***/ ++ ++ /* ++ * If negative, keep only first lower negative dentry, to save on ++ * memory. ++ */ ++ if (dbstart(dentry) < dbend(dentry)) { ++ path_put_lowers(dentry, dbstart(dentry) + 1, ++ dbend(dentry), false); ++ dbend(dentry) = dbstart(dentry); ++ } ++ if (lookupmode == INTERPOSE_PARTIAL) ++ goto out; ++ if (lookupmode == INTERPOSE_LOOKUP) { ++ /* ++ * If all we found was a whiteout in the first available ++ * branch, then create a negative dentry for a possibly new ++ * file to be created. ++ */ ++ if (dbopaque(dentry) < 0) ++ goto out; ++ /* XXX: need to get mnt here */ ++ bindex = dbstart(dentry); ++ if (unionfs_lower_dentry_idx(dentry, bindex)) ++ goto out; ++ lower_dir_dentry = ++ unionfs_lower_dentry_idx(parent, bindex); ++ if (!lower_dir_dentry || !lower_dir_dentry->d_inode) ++ goto out; ++ if (!S_ISDIR(lower_dir_dentry->d_inode->i_mode)) ++ goto out; /* XXX: should be BUG_ON */ ++ /* XXX: do we need to cross bind mounts here? */ ++ lower_dentry = lookup_lck_len(name, lower_dir_dentry, namelen); ++ if (IS_ERR(lower_dentry)) { ++ err = PTR_ERR(lower_dentry); ++ goto out; ++ } ++ /* XXX: need to mntget/mntput as needed too! */ ++ unionfs_set_lower_dentry_idx(dentry, bindex, lower_dentry); ++ /* XXX: wrong mnt for crossing bind mounts! */ ++ lower_mnt = unionfs_mntget(dentry->d_sb->s_root, bindex); ++ unionfs_set_lower_mnt_idx(dentry, bindex, lower_mnt); ++ ++ goto out; ++ } ++ ++ /* if we're revalidating a positive dentry, don't make it negative */ ++ if (lookupmode != INTERPOSE_REVAL) ++ d_add(dentry, NULL); ++ ++ goto out; ++ ++out_positive: ++ /*** handle POSITIVE dentries ***/ ++ ++ /* ++ * This unionfs dentry is positive (at least one lower inode ++ * exists), so scan entire dentry from beginning to end, and remove ++ * any negative lower dentries, if any. Then, update dbstart/dbend ++ * to reflect the start/end of positive dentries. ++ */ ++ pos_start = pos_end = -1; ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ lower_dentry = unionfs_lower_dentry_idx(dentry, ++ bindex); ++ if (lower_dentry && lower_dentry->d_inode) { ++ if (pos_start < 0) ++ pos_start = bindex; ++ if (bindex > pos_end) ++ pos_end = bindex; ++ continue; ++ } ++ path_put_lowers(dentry, bindex, bindex, false); ++ } ++ if (pos_start >= 0) ++ dbstart(dentry) = pos_start; ++ if (pos_end >= 0) ++ dbend(dentry) = pos_end; ++ ++ /* Partial lookups need to re-interpose, or throw away older negs. */ ++ if (lookupmode == INTERPOSE_PARTIAL) { ++ if (dentry->d_inode) { ++ unionfs_reinterpose(dentry); ++ goto out; ++ } ++ ++ /* ++ * This dentry was positive, so it is as if we had a ++ * negative revalidation. ++ */ ++ lookupmode = INTERPOSE_REVAL_NEG; ++ update_bstart(dentry); ++ } ++ ++ /* ++ * Interpose can return a dentry if d_splice returned a different ++ * dentry. ++ */ ++ d_interposed = unionfs_interpose(dentry, dentry->d_sb, lookupmode); ++ if (IS_ERR(d_interposed)) ++ err = PTR_ERR(d_interposed); ++ else if (d_interposed) ++ dentry = d_interposed; ++ ++ if (!err) ++ goto out; ++ d_drop(dentry); ++ ++out_free: ++ /* should dput/mntput all the underlying dentries on error condition */ ++ if (dbstart(dentry) >= 0) ++ path_put_lowers_all(dentry, false); ++ /* free lower_paths unconditionally */ ++ kfree(UNIONFS_D(dentry)->lower_paths); ++ UNIONFS_D(dentry)->lower_paths = NULL; ++ ++out: ++ if (dentry && UNIONFS_D(dentry)) { ++ BUG_ON(dbstart(dentry) < 0 && dbend(dentry) >= 0); ++ BUG_ON(dbstart(dentry) >= 0 && dbend(dentry) < 0); ++ } ++ if (d_interposed && UNIONFS_D(d_interposed)) { ++ BUG_ON(dbstart(d_interposed) < 0 && dbend(d_interposed) >= 0); ++ BUG_ON(dbstart(d_interposed) >= 0 && dbend(d_interposed) < 0); ++ } ++ ++ if (!err && d_interposed) ++ return d_interposed; ++ return ERR_PTR(err); ++} +diff --git a/fs/unionfs/main.c b/fs/unionfs/main.c +new file mode 100644 +index 0000000..258386e +--- /dev/null ++++ b/fs/unionfs/main.c +@@ -0,0 +1,758 @@ ++/* ++ * Copyright (c) 2003-2010 Erez Zadok ++ * Copyright (c) 2003-2006 Charles P. Wright ++ * Copyright (c) 2005-2007 Josef 'Jeff' Sipek ++ * Copyright (c) 2005-2006 Junjiro Okajima ++ * Copyright (c) 2005 Arun M. Krishnakumar ++ * Copyright (c) 2004-2006 David P. Quigley ++ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair ++ * Copyright (c) 2003 Puja Gupta ++ * Copyright (c) 2003 Harikesavan Krishnan ++ * Copyright (c) 2003-2010 Stony Brook University ++ * Copyright (c) 2003-2010 The Research Foundation of SUNY ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "union.h" ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++ ++static void unionfs_fill_inode(struct dentry *dentry, ++ struct inode *inode) ++{ ++ struct inode *lower_inode; ++ struct dentry *lower_dentry; ++ int bindex, bstart, bend; ++ ++ bstart = dbstart(dentry); ++ bend = dbend(dentry); ++ ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); ++ if (!lower_dentry) { ++ unionfs_set_lower_inode_idx(inode, bindex, NULL); ++ continue; ++ } ++ ++ /* Initialize the lower inode to the new lower inode. */ ++ if (!lower_dentry->d_inode) ++ continue; ++ ++ unionfs_set_lower_inode_idx(inode, bindex, ++ igrab(lower_dentry->d_inode)); ++ } ++ ++ ibstart(inode) = dbstart(dentry); ++ ibend(inode) = dbend(dentry); ++ ++ /* Use attributes from the first branch. */ ++ lower_inode = unionfs_lower_inode(inode); ++ ++ /* Use different set of inode ops for symlinks & directories */ ++ if (S_ISLNK(lower_inode->i_mode)) ++ inode->i_op = &unionfs_symlink_iops; ++ else if (S_ISDIR(lower_inode->i_mode)) ++ inode->i_op = &unionfs_dir_iops; ++ ++ /* Use different set of file ops for directories */ ++ if (S_ISDIR(lower_inode->i_mode)) ++ inode->i_fop = &unionfs_dir_fops; ++ ++ /* properly initialize special inodes */ ++ if (S_ISBLK(lower_inode->i_mode) || S_ISCHR(lower_inode->i_mode) || ++ S_ISFIFO(lower_inode->i_mode) || S_ISSOCK(lower_inode->i_mode)) ++ init_special_inode(inode, lower_inode->i_mode, ++ lower_inode->i_rdev); ++ ++ /* all well, copy inode attributes */ ++ unionfs_copy_attr_all(inode, lower_inode); ++ fsstack_copy_inode_size(inode, lower_inode); ++} ++ ++/* ++ * Connect a unionfs inode dentry/inode with several lower ones. This is ++ * the classic stackable file system "vnode interposition" action. ++ * ++ * @sb: unionfs's super_block ++ */ ++struct dentry *unionfs_interpose(struct dentry *dentry, struct super_block *sb, ++ int flag) ++{ ++ int err = 0; ++ struct inode *inode; ++ int need_fill_inode = 1; ++ struct dentry *spliced = NULL; ++ ++ verify_locked(dentry); ++ ++ /* ++ * We allocate our new inode below by calling unionfs_iget, ++ * which will initialize some of the new inode's fields ++ */ ++ ++ /* ++ * On revalidate we've already got our own inode and just need ++ * to fix it up. ++ */ ++ if (flag == INTERPOSE_REVAL) { ++ inode = dentry->d_inode; ++ UNIONFS_I(inode)->bstart = -1; ++ UNIONFS_I(inode)->bend = -1; ++ atomic_set(&UNIONFS_I(inode)->generation, ++ atomic_read(&UNIONFS_SB(sb)->generation)); ++ ++ UNIONFS_I(inode)->lower_inodes = ++ kcalloc(sbmax(sb), sizeof(struct inode *), GFP_KERNEL); ++ if (unlikely(!UNIONFS_I(inode)->lower_inodes)) { ++ err = -ENOMEM; ++ goto out; ++ } ++ } else { ++ /* get unique inode number for unionfs */ ++ inode = unionfs_iget(sb, iunique(sb, UNIONFS_ROOT_INO)); ++ if (IS_ERR(inode)) { ++ err = PTR_ERR(inode); ++ goto out; ++ } ++ if (atomic_read(&inode->i_count) > 1) ++ goto skip; ++ } ++ ++ need_fill_inode = 0; ++ unionfs_fill_inode(dentry, inode); ++ ++skip: ++ /* only (our) lookup wants to do a d_add */ ++ switch (flag) { ++ case INTERPOSE_DEFAULT: ++ /* for operations which create new inodes */ ++ d_add(dentry, inode); ++ break; ++ case INTERPOSE_REVAL_NEG: ++ d_instantiate(dentry, inode); ++ break; ++ case INTERPOSE_LOOKUP: ++ spliced = d_splice_alias(inode, dentry); ++ if (spliced && spliced != dentry) { ++ /* ++ * d_splice can return a dentry if it was ++ * disconnected and had to be moved. We must ensure ++ * that the private data of the new dentry is ++ * correct and that the inode info was filled ++ * properly. Finally we must return this new ++ * dentry. ++ */ ++ spliced->d_op = &unionfs_dops; ++ spliced->d_fsdata = dentry->d_fsdata; ++ dentry->d_fsdata = NULL; ++ dentry = spliced; ++ if (need_fill_inode) { ++ need_fill_inode = 0; ++ unionfs_fill_inode(dentry, inode); ++ } ++ goto out_spliced; ++ } else if (!spliced) { ++ if (need_fill_inode) { ++ need_fill_inode = 0; ++ unionfs_fill_inode(dentry, inode); ++ goto out_spliced; ++ } ++ } ++ break; ++ case INTERPOSE_REVAL: ++ /* Do nothing. */ ++ break; ++ default: ++ printk(KERN_CRIT "unionfs: invalid interpose flag passed!\n"); ++ BUG(); ++ } ++ goto out; ++ ++out_spliced: ++ if (!err) ++ return spliced; ++out: ++ return ERR_PTR(err); ++} ++ ++/* like interpose above, but for an already existing dentry */ ++void unionfs_reinterpose(struct dentry *dentry) ++{ ++ struct dentry *lower_dentry; ++ struct inode *inode; ++ int bindex, bstart, bend; ++ ++ verify_locked(dentry); ++ ++ /* This is pre-allocated inode */ ++ inode = dentry->d_inode; ++ ++ bstart = dbstart(dentry); ++ bend = dbend(dentry); ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); ++ if (!lower_dentry) ++ continue; ++ ++ if (!lower_dentry->d_inode) ++ continue; ++ if (unionfs_lower_inode_idx(inode, bindex)) ++ continue; ++ unionfs_set_lower_inode_idx(inode, bindex, ++ igrab(lower_dentry->d_inode)); ++ } ++ ibstart(inode) = dbstart(dentry); ++ ibend(inode) = dbend(dentry); ++} ++ ++/* ++ * make sure the branch we just looked up (nd) makes sense: ++ * ++ * 1) we're not trying to stack unionfs on top of unionfs ++ * 2) it exists ++ * 3) is a directory ++ */ ++int check_branch(struct nameidata *nd) ++{ ++ /* XXX: remove in ODF code -- stacking unions allowed there */ ++ if (!strcmp(nd->path.dentry->d_sb->s_type->name, UNIONFS_NAME)) ++ return -EINVAL; ++ if (!nd->path.dentry->d_inode) ++ return -ENOENT; ++ if (!S_ISDIR(nd->path.dentry->d_inode->i_mode)) ++ return -ENOTDIR; ++ return 0; ++} ++ ++/* checks if two lower_dentries have overlapping branches */ ++static int is_branch_overlap(struct dentry *dent1, struct dentry *dent2) ++{ ++ struct dentry *dent = NULL; ++ ++ dent = dent1; ++ while ((dent != dent2) && (dent->d_parent != dent)) ++ dent = dent->d_parent; ++ ++ if (dent == dent2) ++ return 1; ++ ++ dent = dent2; ++ while ((dent != dent1) && (dent->d_parent != dent)) ++ dent = dent->d_parent; ++ ++ return (dent == dent1); ++} ++ ++/* ++ * Parse "ro" or "rw" options, but default to "rw" if no mode options was ++ * specified. Fill the mode bits in @perms. If encounter an unknown ++ * string, return -EINVAL. Otherwise return 0. ++ */ ++int parse_branch_mode(const char *name, int *perms) ++{ ++ if (!name || !strcmp(name, "rw")) { ++ *perms = MAY_READ | MAY_WRITE; ++ return 0; ++ } ++ if (!strcmp(name, "ro")) { ++ *perms = MAY_READ; ++ return 0; ++ } ++ return -EINVAL; ++} ++ ++/* ++ * parse the dirs= mount argument ++ * ++ * We don't need to lock the superblock private data's rwsem, as we get ++ * called only by unionfs_read_super - it is still a long time before anyone ++ * can even get a reference to us. ++ */ ++static int parse_dirs_option(struct super_block *sb, struct unionfs_dentry_info ++ *lower_root_info, char *options) ++{ ++ struct nameidata nd; ++ char *name; ++ int err = 0; ++ int branches = 1; ++ int bindex = 0; ++ int i = 0; ++ int j = 0; ++ struct dentry *dent1; ++ struct dentry *dent2; ++ ++ if (options[0] == '\0') { ++ printk(KERN_ERR "unionfs: no branches specified\n"); ++ err = -EINVAL; ++ goto out; ++ } ++ ++ /* ++ * Each colon means we have a separator, this is really just a rough ++ * guess, since strsep will handle empty fields for us. ++ */ ++ for (i = 0; options[i]; i++) ++ if (options[i] == ':') ++ branches++; ++ ++ /* allocate space for underlying pointers to lower dentry */ ++ UNIONFS_SB(sb)->data = ++ kcalloc(branches, sizeof(struct unionfs_data), GFP_KERNEL); ++ if (unlikely(!UNIONFS_SB(sb)->data)) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ lower_root_info->lower_paths = ++ kcalloc(branches, sizeof(struct path), GFP_KERNEL); ++ if (unlikely(!lower_root_info->lower_paths)) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ /* now parsing a string such as "b1:b2=rw:b3=ro:b4" */ ++ branches = 0; ++ while ((name = strsep(&options, ":")) != NULL) { ++ int perms; ++ char *mode = strchr(name, '='); ++ ++ if (!name) ++ continue; ++ if (!*name) { /* bad use of ':' (extra colons) */ ++ err = -EINVAL; ++ goto out; ++ } ++ ++ branches++; ++ ++ /* strip off '=' if any */ ++ if (mode) ++ *mode++ = '\0'; ++ ++ err = parse_branch_mode(mode, &perms); ++ if (err) { ++ printk(KERN_ERR "unionfs: invalid mode \"%s\" for " ++ "branch %d\n", mode, bindex); ++ goto out; ++ } ++ /* ensure that leftmost branch is writeable */ ++ if (!bindex && !(perms & MAY_WRITE)) { ++ printk(KERN_ERR "unionfs: leftmost branch cannot be " ++ "read-only (use \"-o ro\" to create a " ++ "read-only union)\n"); ++ err = -EINVAL; ++ goto out; ++ } ++ ++ err = path_lookup(name, LOOKUP_FOLLOW, &nd); ++ if (err) { ++ printk(KERN_ERR "unionfs: error accessing " ++ "lower directory '%s' (error %d)\n", ++ name, err); ++ goto out; ++ } ++ ++ err = check_branch(&nd); ++ if (err) { ++ printk(KERN_ERR "unionfs: lower directory " ++ "'%s' is not a valid branch\n", name); ++ path_put(&nd.path); ++ goto out; ++ } ++ ++ lower_root_info->lower_paths[bindex].dentry = nd.path.dentry; ++ lower_root_info->lower_paths[bindex].mnt = nd.path.mnt; ++ ++ set_branchperms(sb, bindex, perms); ++ set_branch_count(sb, bindex, 0); ++ new_branch_id(sb, bindex); ++ ++ if (lower_root_info->bstart < 0) ++ lower_root_info->bstart = bindex; ++ lower_root_info->bend = bindex; ++ bindex++; ++ } ++ ++ if (branches == 0) { ++ printk(KERN_ERR "unionfs: no branches specified\n"); ++ err = -EINVAL; ++ goto out; ++ } ++ ++ BUG_ON(branches != (lower_root_info->bend + 1)); ++ ++ /* ++ * Ensure that no overlaps exist in the branches. ++ * ++ * This test is required because the Linux kernel has no support ++ * currently for ensuring coherency between stackable layers and ++ * branches. If we were to allow overlapping branches, it would be ++ * possible, for example, to delete a file via one branch, which ++ * would not be reflected in another branch. Such incoherency could ++ * lead to inconsistencies and even kernel oopses. Rather than ++ * implement hacks to work around some of these cache-coherency ++ * problems, we prevent branch overlapping, for now. A complete ++ * solution will involve proper kernel/VFS support for cache ++ * coherency, at which time we could safely remove this ++ * branch-overlapping test. ++ */ ++ for (i = 0; i < branches; i++) { ++ dent1 = lower_root_info->lower_paths[i].dentry; ++ for (j = i + 1; j < branches; j++) { ++ dent2 = lower_root_info->lower_paths[j].dentry; ++ if (is_branch_overlap(dent1, dent2)) { ++ printk(KERN_ERR "unionfs: branches %d and " ++ "%d overlap\n", i, j); ++ err = -EINVAL; ++ goto out; ++ } ++ } ++ } ++ ++out: ++ if (err) { ++ for (i = 0; i < branches; i++) ++ path_put(&lower_root_info->lower_paths[i]); ++ ++ kfree(lower_root_info->lower_paths); ++ kfree(UNIONFS_SB(sb)->data); ++ ++ /* ++ * MUST clear the pointers to prevent potential double free if ++ * the caller dies later on ++ */ ++ lower_root_info->lower_paths = NULL; ++ UNIONFS_SB(sb)->data = NULL; ++ } ++ return err; ++} ++ ++/* ++ * Parse mount options. See the manual page for usage instructions. ++ * ++ * Returns the dentry object of the lower-level (lower) directory; ++ * We want to mount our stackable file system on top of that lower directory. ++ */ ++static struct unionfs_dentry_info *unionfs_parse_options( ++ struct super_block *sb, ++ char *options) ++{ ++ struct unionfs_dentry_info *lower_root_info; ++ char *optname; ++ int err = 0; ++ int bindex; ++ int dirsfound = 0; ++ ++ /* allocate private data area */ ++ err = -ENOMEM; ++ lower_root_info = ++ kzalloc(sizeof(struct unionfs_dentry_info), GFP_KERNEL); ++ if (unlikely(!lower_root_info)) ++ goto out_error; ++ lower_root_info->bstart = -1; ++ lower_root_info->bend = -1; ++ lower_root_info->bopaque = -1; ++ ++ while ((optname = strsep(&options, ",")) != NULL) { ++ char *optarg; ++ ++ if (!optname || !*optname) ++ continue; ++ ++ optarg = strchr(optname, '='); ++ if (optarg) ++ *optarg++ = '\0'; ++ ++ /* ++ * All of our options take an argument now. Insert ones that ++ * don't, above this check. ++ */ ++ if (!optarg) { ++ printk(KERN_ERR "unionfs: %s requires an argument\n", ++ optname); ++ err = -EINVAL; ++ goto out_error; ++ } ++ ++ if (!strcmp("dirs", optname)) { ++ if (++dirsfound > 1) { ++ printk(KERN_ERR ++ "unionfs: multiple dirs specified\n"); ++ err = -EINVAL; ++ goto out_error; ++ } ++ err = parse_dirs_option(sb, lower_root_info, optarg); ++ if (err) ++ goto out_error; ++ continue; ++ } ++ ++ err = -EINVAL; ++ printk(KERN_ERR ++ "unionfs: unrecognized option '%s'\n", optname); ++ goto out_error; ++ } ++ if (dirsfound != 1) { ++ printk(KERN_ERR "unionfs: dirs option required\n"); ++ err = -EINVAL; ++ goto out_error; ++ } ++ goto out; ++ ++out_error: ++ if (lower_root_info && lower_root_info->lower_paths) { ++ for (bindex = lower_root_info->bstart; ++ bindex >= 0 && bindex <= lower_root_info->bend; ++ bindex++) ++ path_put(&lower_root_info->lower_paths[bindex]); ++ } ++ ++ kfree(lower_root_info->lower_paths); ++ kfree(lower_root_info); ++ ++ kfree(UNIONFS_SB(sb)->data); ++ UNIONFS_SB(sb)->data = NULL; ++ ++ lower_root_info = ERR_PTR(err); ++out: ++ return lower_root_info; ++} ++ ++/* ++ * our custom d_alloc_root work-alike ++ * ++ * we can't use d_alloc_root if we want to use our own interpose function ++ * unchanged, so we simply call our own "fake" d_alloc_root ++ */ ++static struct dentry *unionfs_d_alloc_root(struct super_block *sb) ++{ ++ struct dentry *ret = NULL; ++ ++ if (sb) { ++ static const struct qstr name = { ++ .name = "/", ++ .len = 1 ++ }; ++ ++ ret = d_alloc(NULL, &name); ++ if (likely(ret)) { ++ ret->d_op = &unionfs_dops; ++ ret->d_sb = sb; ++ ret->d_parent = ret; ++ } ++ } ++ return ret; ++} ++ ++/* ++ * There is no need to lock the unionfs_super_info's rwsem as there is no ++ * way anyone can have a reference to the superblock at this point in time. ++ */ ++static int unionfs_read_super(struct super_block *sb, void *raw_data, ++ int silent) ++{ ++ int err = 0; ++ struct unionfs_dentry_info *lower_root_info = NULL; ++ int bindex, bstart, bend; ++ ++ if (!raw_data) { ++ printk(KERN_ERR ++ "unionfs: read_super: missing data argument\n"); ++ err = -EINVAL; ++ goto out; ++ } ++ ++ /* Allocate superblock private data */ ++ sb->s_fs_info = kzalloc(sizeof(struct unionfs_sb_info), GFP_KERNEL); ++ if (unlikely(!UNIONFS_SB(sb))) { ++ printk(KERN_CRIT "unionfs: read_super: out of memory\n"); ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ UNIONFS_SB(sb)->bend = -1; ++ atomic_set(&UNIONFS_SB(sb)->generation, 1); ++ init_rwsem(&UNIONFS_SB(sb)->rwsem); ++ UNIONFS_SB(sb)->high_branch_id = -1; /* -1 == invalid branch ID */ ++ ++ lower_root_info = unionfs_parse_options(sb, raw_data); ++ if (IS_ERR(lower_root_info)) { ++ printk(KERN_ERR ++ "unionfs: read_super: error while parsing options " ++ "(err = %ld)\n", PTR_ERR(lower_root_info)); ++ err = PTR_ERR(lower_root_info); ++ lower_root_info = NULL; ++ goto out_free; ++ } ++ if (lower_root_info->bstart == -1) { ++ err = -ENOENT; ++ goto out_free; ++ } ++ ++ /* set the lower superblock field of upper superblock */ ++ bstart = lower_root_info->bstart; ++ BUG_ON(bstart != 0); ++ sbend(sb) = bend = lower_root_info->bend; ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ struct dentry *d = lower_root_info->lower_paths[bindex].dentry; ++ atomic_inc(&d->d_sb->s_active); ++ unionfs_set_lower_super_idx(sb, bindex, d->d_sb); ++ } ++ ++ /* max Bytes is the maximum bytes from highest priority branch */ ++ sb->s_maxbytes = unionfs_lower_super_idx(sb, 0)->s_maxbytes; ++ ++ /* ++ * Our c/m/atime granularity is 1 ns because we may stack on file ++ * systems whose granularity is as good. This is important for our ++ * time-based cache coherency. ++ */ ++ sb->s_time_gran = 1; ++ ++ sb->s_op = &unionfs_sops; ++ ++ /* See comment next to the definition of unionfs_d_alloc_root */ ++ sb->s_root = unionfs_d_alloc_root(sb); ++ if (unlikely(!sb->s_root)) { ++ err = -ENOMEM; ++ goto out_dput; ++ } ++ ++ /* link the upper and lower dentries */ ++ sb->s_root->d_fsdata = NULL; ++ err = new_dentry_private_data(sb->s_root, UNIONFS_DMUTEX_ROOT); ++ if (unlikely(err)) ++ goto out_freedpd; ++ ++ /* Set the lower dentries for s_root */ ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ struct dentry *d; ++ struct vfsmount *m; ++ ++ d = lower_root_info->lower_paths[bindex].dentry; ++ m = lower_root_info->lower_paths[bindex].mnt; ++ ++ unionfs_set_lower_dentry_idx(sb->s_root, bindex, d); ++ unionfs_set_lower_mnt_idx(sb->s_root, bindex, m); ++ } ++ dbstart(sb->s_root) = bstart; ++ dbend(sb->s_root) = bend; ++ ++ /* Set the generation number to one, since this is for the mount. */ ++ atomic_set(&UNIONFS_D(sb->s_root)->generation, 1); ++ ++ /* ++ * Call interpose to create the upper level inode. Only ++ * INTERPOSE_LOOKUP can return a value other than 0 on err. ++ */ ++ err = PTR_ERR(unionfs_interpose(sb->s_root, sb, 0)); ++ unionfs_unlock_dentry(sb->s_root); ++ if (!err) ++ goto out; ++ /* else fall through */ ++ ++out_freedpd: ++ if (UNIONFS_D(sb->s_root)) { ++ kfree(UNIONFS_D(sb->s_root)->lower_paths); ++ free_dentry_private_data(sb->s_root); ++ } ++ dput(sb->s_root); ++ ++out_dput: ++ if (lower_root_info && !IS_ERR(lower_root_info)) { ++ for (bindex = lower_root_info->bstart; ++ bindex <= lower_root_info->bend; bindex++) { ++ struct dentry *d; ++ d = lower_root_info->lower_paths[bindex].dentry; ++ /* drop refs we took earlier */ ++ atomic_dec(&d->d_sb->s_active); ++ path_put(&lower_root_info->lower_paths[bindex]); ++ } ++ kfree(lower_root_info->lower_paths); ++ kfree(lower_root_info); ++ lower_root_info = NULL; ++ } ++ ++out_free: ++ kfree(UNIONFS_SB(sb)->data); ++ kfree(UNIONFS_SB(sb)); ++ sb->s_fs_info = NULL; ++ ++out: ++ if (lower_root_info && !IS_ERR(lower_root_info)) { ++ kfree(lower_root_info->lower_paths); ++ kfree(lower_root_info); ++ } ++ return err; ++} ++ ++static int unionfs_get_sb(struct file_system_type *fs_type, ++ int flags, const char *dev_name, ++ void *raw_data, struct vfsmount *mnt) ++{ ++ int err; ++ err = get_sb_nodev(fs_type, flags, raw_data, unionfs_read_super, mnt); ++ if (!err) ++ UNIONFS_SB(mnt->mnt_sb)->dev_name = ++ kstrdup(dev_name, GFP_KERNEL); ++ return err; ++} ++ ++static struct file_system_type unionfs_fs_type = { ++ .owner = THIS_MODULE, ++ .name = UNIONFS_NAME, ++ .get_sb = unionfs_get_sb, ++ .kill_sb = generic_shutdown_super, ++ .fs_flags = FS_REVAL_DOT, ++}; ++ ++static int __init init_unionfs_fs(void) ++{ ++ int err; ++ ++ pr_info("Registering unionfs " UNIONFS_VERSION "\n"); ++ ++ err = unionfs_init_filldir_cache(); ++ if (unlikely(err)) ++ goto out; ++ err = unionfs_init_inode_cache(); ++ if (unlikely(err)) ++ goto out; ++ err = unionfs_init_dentry_cache(); ++ if (unlikely(err)) ++ goto out; ++ err = init_sioq(); ++ if (unlikely(err)) ++ goto out; ++ err = register_filesystem(&unionfs_fs_type); ++out: ++ if (unlikely(err)) { ++ stop_sioq(); ++ unionfs_destroy_filldir_cache(); ++ unionfs_destroy_inode_cache(); ++ unionfs_destroy_dentry_cache(); ++ } ++ return err; ++} ++ ++static void __exit exit_unionfs_fs(void) ++{ ++ stop_sioq(); ++ unionfs_destroy_filldir_cache(); ++ unionfs_destroy_inode_cache(); ++ unionfs_destroy_dentry_cache(); ++ unregister_filesystem(&unionfs_fs_type); ++ pr_info("Completed unionfs module unload\n"); ++} ++ ++MODULE_AUTHOR("Erez Zadok, Filesystems and Storage Lab, Stony Brook University" ++ " (http://www.fsl.cs.sunysb.edu)"); ++MODULE_DESCRIPTION("Unionfs " UNIONFS_VERSION ++ " (http://unionfs.filesystems.org)"); ++MODULE_LICENSE("GPL"); ++ ++module_init(init_unionfs_fs); ++module_exit(exit_unionfs_fs); +diff --git a/fs/unionfs/mmap.c b/fs/unionfs/mmap.c +new file mode 100644 +index 0000000..1f70535 +--- /dev/null ++++ b/fs/unionfs/mmap.c +@@ -0,0 +1,89 @@ ++/* ++ * Copyright (c) 2003-2010 Erez Zadok ++ * Copyright (c) 2003-2006 Charles P. Wright ++ * Copyright (c) 2005-2007 Josef 'Jeff' Sipek ++ * Copyright (c) 2005-2006 Junjiro Okajima ++ * Copyright (c) 2006 Shaya Potter ++ * Copyright (c) 2005 Arun M. Krishnakumar ++ * Copyright (c) 2004-2006 David P. Quigley ++ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair ++ * Copyright (c) 2003 Puja Gupta ++ * Copyright (c) 2003 Harikesavan Krishnan ++ * Copyright (c) 2003-2010 Stony Brook University ++ * Copyright (c) 2003-2010 The Research Foundation of SUNY ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "union.h" ++ ++ ++/* ++ * XXX: we need a dummy readpage handler because generic_file_mmap (which we ++ * use in unionfs_mmap) checks for the existence of ++ * mapping->a_ops->readpage, else it returns -ENOEXEC. The VFS will need to ++ * be fixed to allow a file system to define vm_ops->fault without any ++ * address_space_ops whatsoever. ++ * ++ * Otherwise, we don't want to use our readpage method at all. ++ */ ++static int unionfs_readpage(struct file *file, struct page *page) ++{ ++ BUG(); ++ return -EINVAL; ++} ++ ++static int unionfs_fault(struct vm_area_struct *vma, struct vm_fault *vmf) ++{ ++ int err; ++ struct file *file, *lower_file; ++ const struct vm_operations_struct *lower_vm_ops; ++ struct vm_area_struct lower_vma; ++ ++ BUG_ON(!vma); ++ memcpy(&lower_vma, vma, sizeof(struct vm_area_struct)); ++ file = lower_vma.vm_file; ++ lower_vm_ops = UNIONFS_F(file)->lower_vm_ops; ++ BUG_ON(!lower_vm_ops); ++ ++ lower_file = unionfs_lower_file(file); ++ BUG_ON(!lower_file); ++ /* ++ * XXX: vm_ops->fault may be called in parallel. Because we have to ++ * resort to temporarily changing the vma->vm_file to point to the ++ * lower file, a concurrent invocation of unionfs_fault could see a ++ * different value. In this workaround, we keep a different copy of ++ * the vma structure in our stack, so we never expose a different ++ * value of the vma->vm_file called to us, even temporarily. A ++ * better fix would be to change the calling semantics of ->fault to ++ * take an explicit file pointer. ++ */ ++ lower_vma.vm_file = lower_file; ++ err = lower_vm_ops->fault(&lower_vma, vmf); ++ return err; ++} ++ ++/* ++ * XXX: the default address_space_ops for unionfs is empty. We cannot set ++ * our inode->i_mapping->a_ops to NULL because too many code paths expect ++ * the a_ops vector to be non-NULL. ++ */ ++struct address_space_operations unionfs_aops = { ++ /* empty on purpose */ ++}; ++ ++/* ++ * XXX: we need a second, dummy address_space_ops vector, to be used ++ * temporarily during unionfs_mmap, because the latter calls ++ * generic_file_mmap, which checks if ->readpage exists, else returns ++ * -ENOEXEC. ++ */ ++struct address_space_operations unionfs_dummy_aops = { ++ .readpage = unionfs_readpage, ++}; ++ ++struct vm_operations_struct unionfs_vm_ops = { ++ .fault = unionfs_fault, ++}; +diff --git a/fs/unionfs/rdstate.c b/fs/unionfs/rdstate.c +new file mode 100644 +index 0000000..f745fbc +--- /dev/null ++++ b/fs/unionfs/rdstate.c +@@ -0,0 +1,285 @@ ++/* ++ * Copyright (c) 2003-2010 Erez Zadok ++ * Copyright (c) 2003-2006 Charles P. Wright ++ * Copyright (c) 2005-2007 Josef 'Jeff' Sipek ++ * Copyright (c) 2005-2006 Junjiro Okajima ++ * Copyright (c) 2005 Arun M. Krishnakumar ++ * Copyright (c) 2004-2006 David P. Quigley ++ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair ++ * Copyright (c) 2003 Puja Gupta ++ * Copyright (c) 2003 Harikesavan Krishnan ++ * Copyright (c) 2003-2010 Stony Brook University ++ * Copyright (c) 2003-2010 The Research Foundation of SUNY ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "union.h" ++ ++/* This file contains the routines for maintaining readdir state. */ ++ ++/* ++ * There are two structures here, rdstate which is a hash table ++ * of the second structure which is a filldir_node. ++ */ ++ ++/* ++ * This is a struct kmem_cache for filldir nodes, because we allocate a lot ++ * of them and they shouldn't waste memory. If the node has a small name ++ * (as defined by the dentry structure), then we use an inline name to ++ * preserve kmalloc space. ++ */ ++static struct kmem_cache *unionfs_filldir_cachep; ++ ++int unionfs_init_filldir_cache(void) ++{ ++ unionfs_filldir_cachep = ++ kmem_cache_create("unionfs_filldir", ++ sizeof(struct filldir_node), 0, ++ SLAB_RECLAIM_ACCOUNT, NULL); ++ ++ return (unionfs_filldir_cachep ? 0 : -ENOMEM); ++} ++ ++void unionfs_destroy_filldir_cache(void) ++{ ++ if (unionfs_filldir_cachep) ++ kmem_cache_destroy(unionfs_filldir_cachep); ++} ++ ++/* ++ * This is a tuning parameter that tells us roughly how big to make the ++ * hash table in directory entries per page. This isn't perfect, but ++ * at least we get a hash table size that shouldn't be too overloaded. ++ * The following averages are based on my home directory. ++ * 14.44693 Overall ++ * 12.29 Single Page Directories ++ * 117.93 Multi-page directories ++ */ ++#define DENTPAGE 4096 ++#define DENTPERONEPAGE 12 ++#define DENTPERPAGE 118 ++#define MINHASHSIZE 1 ++static int guesstimate_hash_size(struct inode *inode) ++{ ++ struct inode *lower_inode; ++ int bindex; ++ int hashsize = MINHASHSIZE; ++ ++ if (UNIONFS_I(inode)->hashsize > 0) ++ return UNIONFS_I(inode)->hashsize; ++ ++ for (bindex = ibstart(inode); bindex <= ibend(inode); bindex++) { ++ lower_inode = unionfs_lower_inode_idx(inode, bindex); ++ if (!lower_inode) ++ continue; ++ ++ if (i_size_read(lower_inode) == DENTPAGE) ++ hashsize += DENTPERONEPAGE; ++ else ++ hashsize += (i_size_read(lower_inode) / DENTPAGE) * ++ DENTPERPAGE; ++ } ++ ++ return hashsize; ++} ++ ++int init_rdstate(struct file *file) ++{ ++ BUG_ON(sizeof(loff_t) != ++ (sizeof(unsigned int) + sizeof(unsigned int))); ++ BUG_ON(UNIONFS_F(file)->rdstate != NULL); ++ ++ UNIONFS_F(file)->rdstate = alloc_rdstate(file->f_path.dentry->d_inode, ++ fbstart(file)); ++ ++ return (UNIONFS_F(file)->rdstate ? 0 : -ENOMEM); ++} ++ ++struct unionfs_dir_state *find_rdstate(struct inode *inode, loff_t fpos) ++{ ++ struct unionfs_dir_state *rdstate = NULL; ++ struct list_head *pos; ++ ++ spin_lock(&UNIONFS_I(inode)->rdlock); ++ list_for_each(pos, &UNIONFS_I(inode)->readdircache) { ++ struct unionfs_dir_state *r = ++ list_entry(pos, struct unionfs_dir_state, cache); ++ if (fpos == rdstate2offset(r)) { ++ UNIONFS_I(inode)->rdcount--; ++ list_del(&r->cache); ++ rdstate = r; ++ break; ++ } ++ } ++ spin_unlock(&UNIONFS_I(inode)->rdlock); ++ return rdstate; ++} ++ ++struct unionfs_dir_state *alloc_rdstate(struct inode *inode, int bindex) ++{ ++ int i = 0; ++ int hashsize; ++ unsigned long mallocsize = sizeof(struct unionfs_dir_state); ++ struct unionfs_dir_state *rdstate; ++ ++ hashsize = guesstimate_hash_size(inode); ++ mallocsize += hashsize * sizeof(struct list_head); ++ mallocsize = __roundup_pow_of_two(mallocsize); ++ ++ /* This should give us about 500 entries anyway. */ ++ if (mallocsize > PAGE_SIZE) ++ mallocsize = PAGE_SIZE; ++ ++ hashsize = (mallocsize - sizeof(struct unionfs_dir_state)) / ++ sizeof(struct list_head); ++ ++ rdstate = kmalloc(mallocsize, GFP_KERNEL); ++ if (unlikely(!rdstate)) ++ return NULL; ++ ++ spin_lock(&UNIONFS_I(inode)->rdlock); ++ if (UNIONFS_I(inode)->cookie >= (MAXRDCOOKIE - 1)) ++ UNIONFS_I(inode)->cookie = 1; ++ else ++ UNIONFS_I(inode)->cookie++; ++ ++ rdstate->cookie = UNIONFS_I(inode)->cookie; ++ spin_unlock(&UNIONFS_I(inode)->rdlock); ++ rdstate->offset = 1; ++ rdstate->access = jiffies; ++ rdstate->bindex = bindex; ++ rdstate->dirpos = 0; ++ rdstate->hashentries = 0; ++ rdstate->size = hashsize; ++ for (i = 0; i < rdstate->size; i++) ++ INIT_LIST_HEAD(&rdstate->list[i]); ++ ++ return rdstate; ++} ++ ++static void free_filldir_node(struct filldir_node *node) ++{ ++ if (node->namelen >= DNAME_INLINE_LEN_MIN) ++ kfree(node->name); ++ kmem_cache_free(unionfs_filldir_cachep, node); ++} ++ ++void free_rdstate(struct unionfs_dir_state *state) ++{ ++ struct filldir_node *tmp; ++ int i; ++ ++ for (i = 0; i < state->size; i++) { ++ struct list_head *head = &(state->list[i]); ++ struct list_head *pos, *n; ++ ++ /* traverse the list and deallocate space */ ++ list_for_each_safe(pos, n, head) { ++ tmp = list_entry(pos, struct filldir_node, file_list); ++ list_del(&tmp->file_list); ++ free_filldir_node(tmp); ++ } ++ } ++ ++ kfree(state); ++} ++ ++struct filldir_node *find_filldir_node(struct unionfs_dir_state *rdstate, ++ const char *name, int namelen, ++ int is_whiteout) ++{ ++ int index; ++ unsigned int hash; ++ struct list_head *head; ++ struct list_head *pos; ++ struct filldir_node *cursor = NULL; ++ int found = 0; ++ ++ BUG_ON(namelen <= 0); ++ ++ hash = full_name_hash(name, namelen); ++ index = hash % rdstate->size; ++ ++ head = &(rdstate->list[index]); ++ list_for_each(pos, head) { ++ cursor = list_entry(pos, struct filldir_node, file_list); ++ ++ if (cursor->namelen == namelen && cursor->hash == hash && ++ !strncmp(cursor->name, name, namelen)) { ++ /* ++ * a duplicate exists, and hence no need to create ++ * entry to the list ++ */ ++ found = 1; ++ ++ /* ++ * if a duplicate is found in this branch, and is ++ * not due to the caller looking for an entry to ++ * whiteout, then the file system may be corrupted. ++ */ ++ if (unlikely(!is_whiteout && ++ cursor->bindex == rdstate->bindex)) ++ printk(KERN_ERR "unionfs: filldir: possible " ++ "I/O error: a file is duplicated " ++ "in the same branch %d: %s\n", ++ rdstate->bindex, cursor->name); ++ break; ++ } ++ } ++ ++ if (!found) ++ cursor = NULL; ++ ++ return cursor; ++} ++ ++int add_filldir_node(struct unionfs_dir_state *rdstate, const char *name, ++ int namelen, int bindex, int whiteout) ++{ ++ struct filldir_node *new; ++ unsigned int hash; ++ int index; ++ int err = 0; ++ struct list_head *head; ++ ++ BUG_ON(namelen <= 0); ++ ++ hash = full_name_hash(name, namelen); ++ index = hash % rdstate->size; ++ head = &(rdstate->list[index]); ++ ++ new = kmem_cache_alloc(unionfs_filldir_cachep, GFP_KERNEL); ++ if (unlikely(!new)) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ INIT_LIST_HEAD(&new->file_list); ++ new->namelen = namelen; ++ new->hash = hash; ++ new->bindex = bindex; ++ new->whiteout = whiteout; ++ ++ if (namelen < DNAME_INLINE_LEN_MIN) { ++ new->name = new->iname; ++ } else { ++ new->name = kmalloc(namelen + 1, GFP_KERNEL); ++ if (unlikely(!new->name)) { ++ kmem_cache_free(unionfs_filldir_cachep, new); ++ new = NULL; ++ goto out; ++ } ++ } ++ ++ memcpy(new->name, name, namelen); ++ new->name[namelen] = '\0'; ++ ++ rdstate->hashentries++; ++ ++ list_add(&(new->file_list), head); ++out: ++ return err; ++} +diff --git a/fs/unionfs/rename.c b/fs/unionfs/rename.c +new file mode 100644 +index 0000000..936700e +--- /dev/null ++++ b/fs/unionfs/rename.c +@@ -0,0 +1,517 @@ ++/* ++ * Copyright (c) 2003-2010 Erez Zadok ++ * Copyright (c) 2003-2006 Charles P. Wright ++ * Copyright (c) 2005-2007 Josef 'Jeff' Sipek ++ * Copyright (c) 2005-2006 Junjiro Okajima ++ * Copyright (c) 2005 Arun M. Krishnakumar ++ * Copyright (c) 2004-2006 David P. Quigley ++ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair ++ * Copyright (c) 2003 Puja Gupta ++ * Copyright (c) 2003 Harikesavan Krishnan ++ * Copyright (c) 2003-2010 Stony Brook University ++ * Copyright (c) 2003-2010 The Research Foundation of SUNY ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "union.h" ++ ++/* ++ * This is a helper function for rename, used when rename ends up with hosed ++ * over dentries and we need to revert. ++ */ ++static int unionfs_refresh_lower_dentry(struct dentry *dentry, ++ struct dentry *parent, int bindex) ++{ ++ struct dentry *lower_dentry; ++ struct dentry *lower_parent; ++ int err = 0; ++ ++ verify_locked(dentry); ++ ++ lower_parent = unionfs_lower_dentry_idx(parent, bindex); ++ ++ BUG_ON(!S_ISDIR(lower_parent->d_inode->i_mode)); ++ ++ lower_dentry = lookup_one_len(dentry->d_name.name, lower_parent, ++ dentry->d_name.len); ++ if (IS_ERR(lower_dentry)) { ++ err = PTR_ERR(lower_dentry); ++ goto out; ++ } ++ ++ dput(unionfs_lower_dentry_idx(dentry, bindex)); ++ iput(unionfs_lower_inode_idx(dentry->d_inode, bindex)); ++ unionfs_set_lower_inode_idx(dentry->d_inode, bindex, NULL); ++ ++ if (!lower_dentry->d_inode) { ++ dput(lower_dentry); ++ unionfs_set_lower_dentry_idx(dentry, bindex, NULL); ++ } else { ++ unionfs_set_lower_dentry_idx(dentry, bindex, lower_dentry); ++ unionfs_set_lower_inode_idx(dentry->d_inode, bindex, ++ igrab(lower_dentry->d_inode)); ++ } ++ ++out: ++ return err; ++} ++ ++static int __unionfs_rename(struct inode *old_dir, struct dentry *old_dentry, ++ struct dentry *old_parent, ++ struct inode *new_dir, struct dentry *new_dentry, ++ struct dentry *new_parent, ++ int bindex) ++{ ++ int err = 0; ++ struct dentry *lower_old_dentry; ++ struct dentry *lower_new_dentry; ++ struct dentry *lower_old_dir_dentry; ++ struct dentry *lower_new_dir_dentry; ++ struct dentry *trap; ++ ++ lower_new_dentry = unionfs_lower_dentry_idx(new_dentry, bindex); ++ lower_old_dentry = unionfs_lower_dentry_idx(old_dentry, bindex); ++ ++ if (!lower_new_dentry) { ++ lower_new_dentry = ++ create_parents(new_parent->d_inode, ++ new_dentry, new_dentry->d_name.name, ++ bindex); ++ if (IS_ERR(lower_new_dentry)) { ++ err = PTR_ERR(lower_new_dentry); ++ if (IS_COPYUP_ERR(err)) ++ goto out; ++ printk(KERN_ERR "unionfs: error creating directory " ++ "tree for rename, bindex=%d err=%d\n", ++ bindex, err); ++ goto out; ++ } ++ } ++ ++ /* check for and remove whiteout, if any */ ++ err = check_unlink_whiteout(new_dentry, lower_new_dentry, bindex); ++ if (err > 0) /* ignore if whiteout found and successfully removed */ ++ err = 0; ++ if (err) ++ goto out; ++ ++ /* check of old_dentry branch is writable */ ++ err = is_robranch_super(old_dentry->d_sb, bindex); ++ if (err) ++ goto out; ++ ++ dget(lower_old_dentry); ++ dget(lower_new_dentry); ++ lower_old_dir_dentry = dget_parent(lower_old_dentry); ++ lower_new_dir_dentry = dget_parent(lower_new_dentry); ++ ++ trap = lock_rename(lower_old_dir_dentry, lower_new_dir_dentry); ++ /* source should not be ancenstor of target */ ++ if (trap == lower_old_dentry) { ++ err = -EINVAL; ++ goto out_err_unlock; ++ } ++ /* target should not be ancenstor of source */ ++ if (trap == lower_new_dentry) { ++ err = -ENOTEMPTY; ++ goto out_err_unlock; ++ } ++ err = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry, ++ lower_new_dir_dentry->d_inode, lower_new_dentry); ++out_err_unlock: ++ if (!err) { ++ /* update parent dir times */ ++ fsstack_copy_attr_times(old_dir, lower_old_dir_dentry->d_inode); ++ fsstack_copy_attr_times(new_dir, lower_new_dir_dentry->d_inode); ++ } ++ unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); ++ ++ dput(lower_old_dir_dentry); ++ dput(lower_new_dir_dentry); ++ dput(lower_old_dentry); ++ dput(lower_new_dentry); ++ ++out: ++ if (!err) { ++ /* Fixup the new_dentry. */ ++ if (bindex < dbstart(new_dentry)) ++ dbstart(new_dentry) = bindex; ++ else if (bindex > dbend(new_dentry)) ++ dbend(new_dentry) = bindex; ++ } ++ ++ return err; ++} ++ ++/* ++ * Main rename code. This is sufficiently complex, that it's documented in ++ * Documentation/filesystems/unionfs/rename.txt. This routine calls ++ * __unionfs_rename() above to perform some of the work. ++ */ ++static int do_unionfs_rename(struct inode *old_dir, ++ struct dentry *old_dentry, ++ struct dentry *old_parent, ++ struct inode *new_dir, ++ struct dentry *new_dentry, ++ struct dentry *new_parent) ++{ ++ int err = 0; ++ int bindex; ++ int old_bstart, old_bend; ++ int new_bstart, new_bend; ++ int do_copyup = -1; ++ int local_err = 0; ++ int eio = 0; ++ int revert = 0; ++ ++ old_bstart = dbstart(old_dentry); ++ old_bend = dbend(old_dentry); ++ ++ new_bstart = dbstart(new_dentry); ++ new_bend = dbend(new_dentry); ++ ++ /* Rename source to destination. */ ++ err = __unionfs_rename(old_dir, old_dentry, old_parent, ++ new_dir, new_dentry, new_parent, ++ old_bstart); ++ if (err) { ++ if (!IS_COPYUP_ERR(err)) ++ goto out; ++ do_copyup = old_bstart - 1; ++ } else { ++ revert = 1; ++ } ++ ++ /* ++ * Unlink all instances of destination that exist to the left of ++ * bstart of source. On error, revert back, goto out. ++ */ ++ for (bindex = old_bstart - 1; bindex >= new_bstart; bindex--) { ++ struct dentry *unlink_dentry; ++ struct dentry *unlink_dir_dentry; ++ ++ BUG_ON(bindex < 0); ++ unlink_dentry = unionfs_lower_dentry_idx(new_dentry, bindex); ++ if (!unlink_dentry) ++ continue; ++ ++ unlink_dir_dentry = lock_parent(unlink_dentry); ++ err = is_robranch_super(old_dir->i_sb, bindex); ++ if (!err) ++ err = vfs_unlink(unlink_dir_dentry->d_inode, ++ unlink_dentry); ++ ++ fsstack_copy_attr_times(new_parent->d_inode, ++ unlink_dir_dentry->d_inode); ++ /* propagate number of hard-links */ ++ new_parent->d_inode->i_nlink = ++ unionfs_get_nlinks(new_parent->d_inode); ++ ++ unlock_dir(unlink_dir_dentry); ++ if (!err) { ++ if (bindex != new_bstart) { ++ dput(unlink_dentry); ++ unionfs_set_lower_dentry_idx(new_dentry, ++ bindex, NULL); ++ } ++ } else if (IS_COPYUP_ERR(err)) { ++ do_copyup = bindex - 1; ++ } else if (revert) { ++ goto revert; ++ } ++ } ++ ++ if (do_copyup != -1) { ++ for (bindex = do_copyup; bindex >= 0; bindex--) { ++ /* ++ * copyup the file into some left directory, so that ++ * you can rename it ++ */ ++ err = copyup_dentry(old_parent->d_inode, ++ old_dentry, old_bstart, bindex, ++ old_dentry->d_name.name, ++ old_dentry->d_name.len, NULL, ++ i_size_read(old_dentry->d_inode)); ++ /* if copyup failed, try next branch to the left */ ++ if (err) ++ continue; ++ /* ++ * create whiteout before calling __unionfs_rename ++ * because the latter will change the old_dentry's ++ * lower name and parent dir, resulting in the ++ * whiteout getting created in the wrong dir. ++ */ ++ err = create_whiteout(old_dentry, bindex); ++ if (err) { ++ printk(KERN_ERR "unionfs: can't create a " ++ "whiteout for %s in rename (err=%d)\n", ++ old_dentry->d_name.name, err); ++ continue; ++ } ++ err = __unionfs_rename(old_dir, old_dentry, old_parent, ++ new_dir, new_dentry, new_parent, ++ bindex); ++ break; ++ } ++ } ++ ++ /* make it opaque */ ++ if (S_ISDIR(old_dentry->d_inode->i_mode)) { ++ err = make_dir_opaque(old_dentry, dbstart(old_dentry)); ++ if (err) ++ goto revert; ++ } ++ ++ /* ++ * Create whiteout for source, only if: ++ * (1) There is more than one underlying instance of source. ++ * (We did a copy_up is taken care of above). ++ */ ++ if ((old_bstart != old_bend) && (do_copyup == -1)) { ++ err = create_whiteout(old_dentry, old_bstart); ++ if (err) { ++ /* can't fix anything now, so we exit with -EIO */ ++ printk(KERN_ERR "unionfs: can't create a whiteout for " ++ "%s in rename!\n", old_dentry->d_name.name); ++ err = -EIO; ++ } ++ } ++ ++out: ++ return err; ++ ++revert: ++ /* Do revert here. */ ++ local_err = unionfs_refresh_lower_dentry(new_dentry, new_parent, ++ old_bstart); ++ if (local_err) { ++ printk(KERN_ERR "unionfs: revert failed in rename: " ++ "the new refresh failed\n"); ++ eio = -EIO; ++ } ++ ++ local_err = unionfs_refresh_lower_dentry(old_dentry, old_parent, ++ old_bstart); ++ if (local_err) { ++ printk(KERN_ERR "unionfs: revert failed in rename: " ++ "the old refresh failed\n"); ++ eio = -EIO; ++ goto revert_out; ++ } ++ ++ if (!unionfs_lower_dentry_idx(new_dentry, bindex) || ++ !unionfs_lower_dentry_idx(new_dentry, bindex)->d_inode) { ++ printk(KERN_ERR "unionfs: revert failed in rename: " ++ "the object disappeared from under us!\n"); ++ eio = -EIO; ++ goto revert_out; ++ } ++ ++ if (unionfs_lower_dentry_idx(old_dentry, bindex) && ++ unionfs_lower_dentry_idx(old_dentry, bindex)->d_inode) { ++ printk(KERN_ERR "unionfs: revert failed in rename: " ++ "the object was created underneath us!\n"); ++ eio = -EIO; ++ goto revert_out; ++ } ++ ++ local_err = __unionfs_rename(new_dir, new_dentry, new_parent, ++ old_dir, old_dentry, old_parent, ++ old_bstart); ++ ++ /* If we can't fix it, then we cop-out with -EIO. */ ++ if (local_err) { ++ printk(KERN_ERR "unionfs: revert failed in rename!\n"); ++ eio = -EIO; ++ } ++ ++ local_err = unionfs_refresh_lower_dentry(new_dentry, new_parent, ++ bindex); ++ if (local_err) ++ eio = -EIO; ++ local_err = unionfs_refresh_lower_dentry(old_dentry, old_parent, ++ bindex); ++ if (local_err) ++ eio = -EIO; ++ ++revert_out: ++ if (eio) ++ err = eio; ++ return err; ++} ++ ++/* ++ * We can't copyup a directory, because it may involve huge numbers of ++ * children, etc. Doing that in the kernel would be bad, so instead we ++ * return EXDEV to the user-space utility that caused this, and let the ++ * user-space recurse and ask us to copy up each file separately. ++ */ ++static int may_rename_dir(struct dentry *dentry, struct dentry *parent) ++{ ++ int err, bstart; ++ ++ err = check_empty(dentry, parent, NULL); ++ if (err == -ENOTEMPTY) { ++ if (is_robranch(dentry)) ++ return -EXDEV; ++ } else if (err) { ++ return err; ++ } ++ ++ bstart = dbstart(dentry); ++ if (dbend(dentry) == bstart || dbopaque(dentry) == bstart) ++ return 0; ++ ++ dbstart(dentry) = bstart + 1; ++ err = check_empty(dentry, parent, NULL); ++ dbstart(dentry) = bstart; ++ if (err == -ENOTEMPTY) ++ err = -EXDEV; ++ return err; ++} ++ ++/* ++ * The locking rules in unionfs_rename are complex. We could use a simpler ++ * superblock-level name-space lock for renames and copy-ups. ++ */ ++int unionfs_rename(struct inode *old_dir, struct dentry *old_dentry, ++ struct inode *new_dir, struct dentry *new_dentry) ++{ ++ int err = 0; ++ struct dentry *wh_dentry; ++ struct dentry *old_parent, *new_parent; ++ int valid = true; ++ ++ unionfs_read_lock(old_dentry->d_sb, UNIONFS_SMUTEX_CHILD); ++ old_parent = dget_parent(old_dentry); ++ new_parent = dget_parent(new_dentry); ++ /* un/lock parent dentries only if they differ from old/new_dentry */ ++ if (old_parent != old_dentry && ++ old_parent != new_dentry) ++ unionfs_lock_dentry(old_parent, UNIONFS_DMUTEX_REVAL_PARENT); ++ if (new_parent != old_dentry && ++ new_parent != new_dentry && ++ new_parent != old_parent) ++ unionfs_lock_dentry(new_parent, UNIONFS_DMUTEX_REVAL_CHILD); ++ unionfs_double_lock_dentry(old_dentry, new_dentry); ++ ++ valid = __unionfs_d_revalidate(old_dentry, old_parent, false); ++ if (!valid) { ++ err = -ESTALE; ++ goto out; ++ } ++ if (!d_deleted(new_dentry) && new_dentry->d_inode) { ++ valid = __unionfs_d_revalidate(new_dentry, new_parent, false); ++ if (!valid) { ++ err = -ESTALE; ++ goto out; ++ } ++ } ++ ++ if (!S_ISDIR(old_dentry->d_inode->i_mode)) ++ err = unionfs_partial_lookup(old_dentry, old_parent); ++ else ++ err = may_rename_dir(old_dentry, old_parent); ++ ++ if (err) ++ goto out; ++ ++ err = unionfs_partial_lookup(new_dentry, new_parent); ++ if (err) ++ goto out; ++ ++ /* ++ * if new_dentry is already lower because of whiteout, ++ * simply override it even if the whited-out dir is not empty. ++ */ ++ wh_dentry = find_first_whiteout(new_dentry); ++ if (!IS_ERR(wh_dentry)) { ++ dput(wh_dentry); ++ } else if (new_dentry->d_inode) { ++ if (S_ISDIR(old_dentry->d_inode->i_mode) != ++ S_ISDIR(new_dentry->d_inode->i_mode)) { ++ err = S_ISDIR(old_dentry->d_inode->i_mode) ? ++ -ENOTDIR : -EISDIR; ++ goto out; ++ } ++ ++ if (S_ISDIR(new_dentry->d_inode->i_mode)) { ++ struct unionfs_dir_state *namelist = NULL; ++ /* check if this unionfs directory is empty or not */ ++ err = check_empty(new_dentry, new_parent, &namelist); ++ if (err) ++ goto out; ++ ++ if (!is_robranch(new_dentry)) ++ err = delete_whiteouts(new_dentry, ++ dbstart(new_dentry), ++ namelist); ++ ++ free_rdstate(namelist); ++ ++ if (err) ++ goto out; ++ } ++ } ++ ++ err = do_unionfs_rename(old_dir, old_dentry, old_parent, ++ new_dir, new_dentry, new_parent); ++ if (err) ++ goto out; ++ ++ /* ++ * force re-lookup since the dir on ro branch is not renamed, and ++ * lower dentries still indicate the un-renamed ones. ++ */ ++ if (S_ISDIR(old_dentry->d_inode->i_mode)) ++ atomic_dec(&UNIONFS_D(old_dentry)->generation); ++ else ++ unionfs_postcopyup_release(old_dentry); ++ if (new_dentry->d_inode && !S_ISDIR(new_dentry->d_inode->i_mode)) { ++ unionfs_postcopyup_release(new_dentry); ++ unionfs_postcopyup_setmnt(new_dentry); ++ if (!unionfs_lower_inode(new_dentry->d_inode)) { ++ /* ++ * If we get here, it means that no copyup was ++ * needed, and that a file by the old name already ++ * existing on the destination branch; that file got ++ * renamed earlier in this function, so all we need ++ * to do here is set the lower inode. ++ */ ++ struct inode *inode; ++ inode = unionfs_lower_inode(old_dentry->d_inode); ++ igrab(inode); ++ unionfs_set_lower_inode_idx(new_dentry->d_inode, ++ dbstart(new_dentry), ++ inode); ++ } ++ } ++ /* if all of this renaming succeeded, update our times */ ++ unionfs_copy_attr_times(old_dentry->d_inode); ++ unionfs_copy_attr_times(new_dentry->d_inode); ++ unionfs_check_inode(old_dir); ++ unionfs_check_inode(new_dir); ++ unionfs_check_dentry(old_dentry); ++ unionfs_check_dentry(new_dentry); ++ ++out: ++ if (err) /* clear the new_dentry stuff created */ ++ d_drop(new_dentry); ++ ++ unionfs_double_unlock_dentry(old_dentry, new_dentry); ++ if (new_parent != old_dentry && ++ new_parent != new_dentry && ++ new_parent != old_parent) ++ unionfs_unlock_dentry(new_parent); ++ if (old_parent != old_dentry && ++ old_parent != new_dentry) ++ unionfs_unlock_dentry(old_parent); ++ dput(new_parent); ++ dput(old_parent); ++ unionfs_read_unlock(old_dentry->d_sb); ++ ++ return err; ++} +diff --git a/fs/unionfs/sioq.c b/fs/unionfs/sioq.c +new file mode 100644 +index 0000000..760c580 +--- /dev/null ++++ b/fs/unionfs/sioq.c +@@ -0,0 +1,101 @@ ++/* ++ * Copyright (c) 2006-2010 Erez Zadok ++ * Copyright (c) 2006 Charles P. Wright ++ * Copyright (c) 2006-2007 Josef 'Jeff' Sipek ++ * Copyright (c) 2006 Junjiro Okajima ++ * Copyright (c) 2006 David P. Quigley ++ * Copyright (c) 2006-2010 Stony Brook University ++ * Copyright (c) 2006-2010 The Research Foundation of SUNY ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "union.h" ++ ++/* ++ * Super-user IO work Queue - sometimes we need to perform actions which ++ * would fail due to the unix permissions on the parent directory (e.g., ++ * rmdir a directory which appears empty, but in reality contains ++ * whiteouts). ++ */ ++ ++static struct workqueue_struct *superio_workqueue; ++ ++int __init init_sioq(void) ++{ ++ int err; ++ ++ superio_workqueue = create_workqueue("unionfs_siod"); ++ if (!IS_ERR(superio_workqueue)) ++ return 0; ++ ++ err = PTR_ERR(superio_workqueue); ++ printk(KERN_ERR "unionfs: create_workqueue failed %d\n", err); ++ superio_workqueue = NULL; ++ return err; ++} ++ ++void stop_sioq(void) ++{ ++ if (superio_workqueue) ++ destroy_workqueue(superio_workqueue); ++} ++ ++void run_sioq(work_func_t func, struct sioq_args *args) ++{ ++ INIT_WORK(&args->work, func); ++ ++ init_completion(&args->comp); ++ while (!queue_work(superio_workqueue, &args->work)) { ++ /* TODO: do accounting if needed */ ++ schedule(); ++ } ++ wait_for_completion(&args->comp); ++} ++ ++void __unionfs_create(struct work_struct *work) ++{ ++ struct sioq_args *args = container_of(work, struct sioq_args, work); ++ struct create_args *c = &args->create; ++ ++ args->err = vfs_create(c->parent, c->dentry, c->mode, c->nd); ++ complete(&args->comp); ++} ++ ++void __unionfs_mkdir(struct work_struct *work) ++{ ++ struct sioq_args *args = container_of(work, struct sioq_args, work); ++ struct mkdir_args *m = &args->mkdir; ++ ++ args->err = vfs_mkdir(m->parent, m->dentry, m->mode); ++ complete(&args->comp); ++} ++ ++void __unionfs_mknod(struct work_struct *work) ++{ ++ struct sioq_args *args = container_of(work, struct sioq_args, work); ++ struct mknod_args *m = &args->mknod; ++ ++ args->err = vfs_mknod(m->parent, m->dentry, m->mode, m->dev); ++ complete(&args->comp); ++} ++ ++void __unionfs_symlink(struct work_struct *work) ++{ ++ struct sioq_args *args = container_of(work, struct sioq_args, work); ++ struct symlink_args *s = &args->symlink; ++ ++ args->err = vfs_symlink(s->parent, s->dentry, s->symbuf); ++ complete(&args->comp); ++} ++ ++void __unionfs_unlink(struct work_struct *work) ++{ ++ struct sioq_args *args = container_of(work, struct sioq_args, work); ++ struct unlink_args *u = &args->unlink; ++ ++ args->err = vfs_unlink(u->parent, u->dentry); ++ complete(&args->comp); ++} +diff --git a/fs/unionfs/sioq.h b/fs/unionfs/sioq.h +new file mode 100644 +index 0000000..b26d248 +--- /dev/null ++++ b/fs/unionfs/sioq.h +@@ -0,0 +1,91 @@ ++/* ++ * Copyright (c) 2006-2010 Erez Zadok ++ * Copyright (c) 2006 Charles P. Wright ++ * Copyright (c) 2006-2007 Josef 'Jeff' Sipek ++ * Copyright (c) 2006 Junjiro Okajima ++ * Copyright (c) 2006 David P. Quigley ++ * Copyright (c) 2006-2010 Stony Brook University ++ * Copyright (c) 2006-2010 The Research Foundation of SUNY ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef _SIOQ_H ++#define _SIOQ_H ++ ++struct deletewh_args { ++ struct unionfs_dir_state *namelist; ++ struct dentry *dentry; ++ int bindex; ++}; ++ ++struct is_opaque_args { ++ struct dentry *dentry; ++}; ++ ++struct create_args { ++ struct inode *parent; ++ struct dentry *dentry; ++ umode_t mode; ++ struct nameidata *nd; ++}; ++ ++struct mkdir_args { ++ struct inode *parent; ++ struct dentry *dentry; ++ umode_t mode; ++}; ++ ++struct mknod_args { ++ struct inode *parent; ++ struct dentry *dentry; ++ umode_t mode; ++ dev_t dev; ++}; ++ ++struct symlink_args { ++ struct inode *parent; ++ struct dentry *dentry; ++ char *symbuf; ++}; ++ ++struct unlink_args { ++ struct inode *parent; ++ struct dentry *dentry; ++}; ++ ++ ++struct sioq_args { ++ struct completion comp; ++ struct work_struct work; ++ int err; ++ void *ret; ++ ++ union { ++ struct deletewh_args deletewh; ++ struct is_opaque_args is_opaque; ++ struct create_args create; ++ struct mkdir_args mkdir; ++ struct mknod_args mknod; ++ struct symlink_args symlink; ++ struct unlink_args unlink; ++ }; ++}; ++ ++/* Extern definitions for SIOQ functions */ ++extern int __init init_sioq(void); ++extern void stop_sioq(void); ++extern void run_sioq(work_func_t func, struct sioq_args *args); ++ ++/* Extern definitions for our privilege escalation helpers */ ++extern void __unionfs_create(struct work_struct *work); ++extern void __unionfs_mkdir(struct work_struct *work); ++extern void __unionfs_mknod(struct work_struct *work); ++extern void __unionfs_symlink(struct work_struct *work); ++extern void __unionfs_unlink(struct work_struct *work); ++extern void __delete_whiteouts(struct work_struct *work); ++extern void __is_opaque_dir(struct work_struct *work); ++ ++#endif /* not _SIOQ_H */ +diff --git a/fs/unionfs/subr.c b/fs/unionfs/subr.c +new file mode 100644 +index 0000000..570a344 +--- /dev/null ++++ b/fs/unionfs/subr.c +@@ -0,0 +1,95 @@ ++/* ++ * Copyright (c) 2003-2010 Erez Zadok ++ * Copyright (c) 2003-2006 Charles P. Wright ++ * Copyright (c) 2005-2007 Josef 'Jeff' Sipek ++ * Copyright (c) 2005-2006 Junjiro Okajima ++ * Copyright (c) 2005 Arun M. Krishnakumar ++ * Copyright (c) 2004-2006 David P. Quigley ++ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair ++ * Copyright (c) 2003 Puja Gupta ++ * Copyright (c) 2003 Harikesavan Krishnan ++ * Copyright (c) 2003-2010 Stony Brook University ++ * Copyright (c) 2003-2010 The Research Foundation of SUNY ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "union.h" ++ ++/* ++ * returns the right n_link value based on the inode type ++ */ ++int unionfs_get_nlinks(const struct inode *inode) ++{ ++ /* don't bother to do all the work since we're unlinked */ ++ if (inode->i_nlink == 0) ++ return 0; ++ ++ if (!S_ISDIR(inode->i_mode)) ++ return unionfs_lower_inode(inode)->i_nlink; ++ ++ /* ++ * For directories, we return 1. The only place that could cares ++ * about links is readdir, and there's d_type there so even that ++ * doesn't matter. ++ */ ++ return 1; ++} ++ ++/* copy a/m/ctime from the lower branch with the newest times */ ++void unionfs_copy_attr_times(struct inode *upper) ++{ ++ int bindex; ++ struct inode *lower; ++ ++ if (!upper) ++ return; ++ if (ibstart(upper) < 0) { ++#ifdef CONFIG_UNION_FS_DEBUG ++ WARN_ON(ibstart(upper) < 0); ++#endif /* CONFIG_UNION_FS_DEBUG */ ++ return; ++ } ++ for (bindex = ibstart(upper); bindex <= ibend(upper); bindex++) { ++ lower = unionfs_lower_inode_idx(upper, bindex); ++ if (!lower) ++ continue; /* not all lower dir objects may exist */ ++ if (unlikely(timespec_compare(&upper->i_mtime, ++ &lower->i_mtime) < 0)) ++ upper->i_mtime = lower->i_mtime; ++ if (unlikely(timespec_compare(&upper->i_ctime, ++ &lower->i_ctime) < 0)) ++ upper->i_ctime = lower->i_ctime; ++ if (unlikely(timespec_compare(&upper->i_atime, ++ &lower->i_atime) < 0)) ++ upper->i_atime = lower->i_atime; ++ } ++} ++ ++/* ++ * A unionfs/fanout version of fsstack_copy_attr_all. Uses a ++ * unionfs_get_nlinks to properly calcluate the number of links to a file. ++ * Also, copies the max() of all a/m/ctimes for all lower inodes (which is ++ * important if the lower inode is a directory type) ++ */ ++void unionfs_copy_attr_all(struct inode *dest, ++ const struct inode *src) ++{ ++ dest->i_mode = src->i_mode; ++ dest->i_uid = src->i_uid; ++ dest->i_gid = src->i_gid; ++ dest->i_rdev = src->i_rdev; ++ ++ unionfs_copy_attr_times(dest); ++ ++ dest->i_blkbits = src->i_blkbits; ++ dest->i_flags = src->i_flags; ++ ++ /* ++ * Update the nlinks AFTER updating the above fields, because the ++ * get_links callback may depend on them. ++ */ ++ dest->i_nlink = unionfs_get_nlinks(dest); ++} +diff --git a/fs/unionfs/super.c b/fs/unionfs/super.c +new file mode 100644 +index 0000000..a8f5571 +--- /dev/null ++++ b/fs/unionfs/super.c +@@ -0,0 +1,1048 @@ ++/* ++ * Copyright (c) 2003-2010 Erez Zadok ++ * Copyright (c) 2003-2006 Charles P. Wright ++ * Copyright (c) 2005-2007 Josef 'Jeff' Sipek ++ * Copyright (c) 2005-2006 Junjiro Okajima ++ * Copyright (c) 2005 Arun M. Krishnakumar ++ * Copyright (c) 2004-2006 David P. Quigley ++ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair ++ * Copyright (c) 2003 Puja Gupta ++ * Copyright (c) 2003 Harikesavan Krishnan ++ * Copyright (c) 2003-2010 Stony Brook University ++ * Copyright (c) 2003-2010 The Research Foundation of SUNY ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "union.h" ++ ++/* ++ * The inode cache is used with alloc_inode for both our inode info and the ++ * vfs inode. ++ */ ++static struct kmem_cache *unionfs_inode_cachep; ++ ++struct inode *unionfs_iget(struct super_block *sb, unsigned long ino) ++{ ++ int size; ++ struct unionfs_inode_info *info; ++ struct inode *inode; ++ ++ inode = iget_locked(sb, ino); ++ if (!inode) ++ return ERR_PTR(-ENOMEM); ++ if (!(inode->i_state & I_NEW)) ++ return inode; ++ ++ info = UNIONFS_I(inode); ++ memset(info, 0, offsetof(struct unionfs_inode_info, vfs_inode)); ++ info->bstart = -1; ++ info->bend = -1; ++ atomic_set(&info->generation, ++ atomic_read(&UNIONFS_SB(inode->i_sb)->generation)); ++ spin_lock_init(&info->rdlock); ++ info->rdcount = 1; ++ info->hashsize = -1; ++ INIT_LIST_HEAD(&info->readdircache); ++ ++ size = sbmax(inode->i_sb) * sizeof(struct inode *); ++ info->lower_inodes = kzalloc(size, GFP_KERNEL); ++ if (unlikely(!info->lower_inodes)) { ++ printk(KERN_CRIT "unionfs: no kernel memory when allocating " ++ "lower-pointer array!\n"); ++ iget_failed(inode); ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ inode->i_version++; ++ inode->i_op = &unionfs_main_iops; ++ inode->i_fop = &unionfs_main_fops; ++ ++ inode->i_mapping->a_ops = &unionfs_aops; ++ ++ /* ++ * reset times so unionfs_copy_attr_all can keep out time invariants ++ * right (upper inode time being the max of all lower ones). ++ */ ++ inode->i_atime.tv_sec = inode->i_atime.tv_nsec = 0; ++ inode->i_mtime.tv_sec = inode->i_mtime.tv_nsec = 0; ++ inode->i_ctime.tv_sec = inode->i_ctime.tv_nsec = 0; ++ unlock_new_inode(inode); ++ return inode; ++} ++ ++/* ++ * we now define delete_inode, because there are two VFS paths that may ++ * destroy an inode: one of them calls clear inode before doing everything ++ * else that's needed, and the other is fine. This way we truncate the inode ++ * size (and its pages) and then clear our own inode, which will do an iput ++ * on our and the lower inode. ++ * ++ * No need to lock sb info's rwsem. ++ */ ++static void unionfs_delete_inode(struct inode *inode) ++{ ++#if BITS_PER_LONG == 32 && defined(CONFIG_SMP) ++ spin_lock(&inode->i_lock); ++#endif ++ i_size_write(inode, 0); /* every f/s seems to do that */ ++#if BITS_PER_LONG == 32 && defined(CONFIG_SMP) ++ spin_unlock(&inode->i_lock); ++#endif ++ ++ if (inode->i_data.nrpages) ++ truncate_inode_pages(&inode->i_data, 0); ++ ++ clear_inode(inode); ++} ++ ++/* ++ * final actions when unmounting a file system ++ * ++ * No need to lock rwsem. ++ */ ++static void unionfs_put_super(struct super_block *sb) ++{ ++ int bindex, bstart, bend; ++ struct unionfs_sb_info *spd; ++ int leaks = 0; ++ ++ spd = UNIONFS_SB(sb); ++ if (!spd) ++ return; ++ ++ bstart = sbstart(sb); ++ bend = sbend(sb); ++ ++ /* Make sure we have no leaks of branchget/branchput. */ ++ for (bindex = bstart; bindex <= bend; bindex++) ++ if (unlikely(branch_count(sb, bindex) != 0)) { ++ printk(KERN_CRIT ++ "unionfs: branch %d has %d references left!\n", ++ bindex, branch_count(sb, bindex)); ++ leaks = 1; ++ } ++ WARN_ON(leaks != 0); ++ ++ /* decrement lower super references */ ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ struct super_block *s; ++ s = unionfs_lower_super_idx(sb, bindex); ++ unionfs_set_lower_super_idx(sb, bindex, NULL); ++ atomic_dec(&s->s_active); ++ } ++ ++ kfree(spd->dev_name); ++ kfree(spd->data); ++ kfree(spd); ++ sb->s_fs_info = NULL; ++} ++ ++/* ++ * Since people use this to answer the "How big of a file can I write?" ++ * question, we report the size of the highest priority branch as the size of ++ * the union. ++ */ ++static int unionfs_statfs(struct dentry *dentry, struct kstatfs *buf) ++{ ++ int err = 0; ++ struct super_block *sb; ++ struct dentry *lower_dentry; ++ struct dentry *parent; ++ bool valid; ++ ++ sb = dentry->d_sb; ++ ++ unionfs_read_lock(sb, UNIONFS_SMUTEX_CHILD); ++ parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); ++ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); ++ ++ valid = __unionfs_d_revalidate(dentry, parent, false); ++ if (unlikely(!valid)) { ++ err = -ESTALE; ++ goto out; ++ } ++ unionfs_check_dentry(dentry); ++ ++ lower_dentry = unionfs_lower_dentry(sb->s_root); ++ err = vfs_statfs(lower_dentry, buf); ++ ++ /* set return buf to our f/s to avoid confusing user-level utils */ ++ buf->f_type = UNIONFS_SUPER_MAGIC; ++ /* ++ * Our maximum file name can is shorter by a few bytes because every ++ * file name could potentially be whited-out. ++ * ++ * XXX: this restriction goes away with ODF. ++ */ ++ unionfs_set_max_namelen(&buf->f_namelen); ++ ++ /* ++ * reset two fields to avoid confusing user-land. ++ * XXX: is this still necessary? ++ */ ++ memset(&buf->f_fsid, 0, sizeof(__kernel_fsid_t)); ++ memset(&buf->f_spare, 0, sizeof(buf->f_spare)); ++ ++out: ++ unionfs_check_dentry(dentry); ++ unionfs_unlock_dentry(dentry); ++ unionfs_unlock_parent(dentry, parent); ++ unionfs_read_unlock(sb); ++ return err; ++} ++ ++/* handle mode changing during remount */ ++static noinline_for_stack int do_remount_mode_option( ++ char *optarg, ++ int cur_branches, ++ struct unionfs_data *new_data, ++ struct path *new_lower_paths) ++{ ++ int err = -EINVAL; ++ int perms, idx; ++ char *modename = strchr(optarg, '='); ++ struct nameidata nd; ++ ++ /* by now, optarg contains the branch name */ ++ if (!*optarg) { ++ printk(KERN_ERR ++ "unionfs: no branch specified for mode change\n"); ++ goto out; ++ } ++ if (!modename) { ++ printk(KERN_ERR "unionfs: branch \"%s\" requires a mode\n", ++ optarg); ++ goto out; ++ } ++ *modename++ = '\0'; ++ err = parse_branch_mode(modename, &perms); ++ if (err) { ++ printk(KERN_ERR "unionfs: invalid mode \"%s\" for \"%s\"\n", ++ modename, optarg); ++ goto out; ++ } ++ ++ /* ++ * Find matching branch index. For now, this assumes that nothing ++ * has been mounted on top of this Unionfs stack. Once we have /odf ++ * and cache-coherency resolved, we'll address the branch-path ++ * uniqueness. ++ */ ++ err = path_lookup(optarg, LOOKUP_FOLLOW, &nd); ++ if (err) { ++ printk(KERN_ERR "unionfs: error accessing " ++ "lower directory \"%s\" (error %d)\n", ++ optarg, err); ++ goto out; ++ } ++ for (idx = 0; idx < cur_branches; idx++) ++ if (nd.path.mnt == new_lower_paths[idx].mnt && ++ nd.path.dentry == new_lower_paths[idx].dentry) ++ break; ++ path_put(&nd.path); /* no longer needed */ ++ if (idx == cur_branches) { ++ err = -ENOENT; /* err may have been reset above */ ++ printk(KERN_ERR "unionfs: branch \"%s\" " ++ "not found\n", optarg); ++ goto out; ++ } ++ /* check/change mode for existing branch */ ++ /* we don't warn if perms==branchperms */ ++ new_data[idx].branchperms = perms; ++ err = 0; ++out: ++ return err; ++} ++ ++/* handle branch deletion during remount */ ++static noinline_for_stack int do_remount_del_option( ++ char *optarg, int cur_branches, ++ struct unionfs_data *new_data, ++ struct path *new_lower_paths) ++{ ++ int err = -EINVAL; ++ int idx; ++ struct nameidata nd; ++ ++ /* optarg contains the branch name to delete */ ++ ++ /* ++ * Find matching branch index. For now, this assumes that nothing ++ * has been mounted on top of this Unionfs stack. Once we have /odf ++ * and cache-coherency resolved, we'll address the branch-path ++ * uniqueness. ++ */ ++ err = path_lookup(optarg, LOOKUP_FOLLOW, &nd); ++ if (err) { ++ printk(KERN_ERR "unionfs: error accessing " ++ "lower directory \"%s\" (error %d)\n", ++ optarg, err); ++ goto out; ++ } ++ for (idx = 0; idx < cur_branches; idx++) ++ if (nd.path.mnt == new_lower_paths[idx].mnt && ++ nd.path.dentry == new_lower_paths[idx].dentry) ++ break; ++ path_put(&nd.path); /* no longer needed */ ++ if (idx == cur_branches) { ++ printk(KERN_ERR "unionfs: branch \"%s\" " ++ "not found\n", optarg); ++ err = -ENOENT; ++ goto out; ++ } ++ /* check if there are any open files on the branch to be deleted */ ++ if (atomic_read(&new_data[idx].open_files) > 0) { ++ err = -EBUSY; ++ goto out; ++ } ++ ++ /* ++ * Now we have to delete the branch. First, release any handles it ++ * has. Then, move the remaining array indexes past "idx" in ++ * new_data and new_lower_paths one to the left. Finally, adjust ++ * cur_branches. ++ */ ++ path_put(&new_lower_paths[idx]); ++ ++ if (idx < cur_branches - 1) { ++ /* if idx==cur_branches-1, we delete last branch: easy */ ++ memmove(&new_data[idx], &new_data[idx+1], ++ (cur_branches - 1 - idx) * ++ sizeof(struct unionfs_data)); ++ memmove(&new_lower_paths[idx], &new_lower_paths[idx+1], ++ (cur_branches - 1 - idx) * sizeof(struct path)); ++ } ++ ++ err = 0; ++out: ++ return err; ++} ++ ++/* handle branch insertion during remount */ ++static noinline_for_stack int do_remount_add_option( ++ char *optarg, int cur_branches, ++ struct unionfs_data *new_data, ++ struct path *new_lower_paths, ++ int *high_branch_id) ++{ ++ int err = -EINVAL; ++ int perms; ++ int idx = 0; /* default: insert at beginning */ ++ char *new_branch , *modename = NULL; ++ struct nameidata nd; ++ ++ /* ++ * optarg can be of several forms: ++ * ++ * /bar:/foo insert /foo before /bar ++ * /bar:/foo=ro insert /foo in ro mode before /bar ++ * /foo insert /foo in the beginning (prepend) ++ * :/foo insert /foo at the end (append) ++ */ ++ if (*optarg == ':') { /* append? */ ++ new_branch = optarg + 1; /* skip ':' */ ++ idx = cur_branches; ++ goto found_insertion_point; ++ } ++ new_branch = strchr(optarg, ':'); ++ if (!new_branch) { /* prepend? */ ++ new_branch = optarg; ++ goto found_insertion_point; ++ } ++ *new_branch++ = '\0'; /* holds path+mode of new branch */ ++ ++ /* ++ * Find matching branch index. For now, this assumes that nothing ++ * has been mounted on top of this Unionfs stack. Once we have /odf ++ * and cache-coherency resolved, we'll address the branch-path ++ * uniqueness. ++ */ ++ err = path_lookup(optarg, LOOKUP_FOLLOW, &nd); ++ if (err) { ++ printk(KERN_ERR "unionfs: error accessing " ++ "lower directory \"%s\" (error %d)\n", ++ optarg, err); ++ goto out; ++ } ++ for (idx = 0; idx < cur_branches; idx++) ++ if (nd.path.mnt == new_lower_paths[idx].mnt && ++ nd.path.dentry == new_lower_paths[idx].dentry) ++ break; ++ path_put(&nd.path); /* no longer needed */ ++ if (idx == cur_branches) { ++ printk(KERN_ERR "unionfs: branch \"%s\" " ++ "not found\n", optarg); ++ err = -ENOENT; ++ goto out; ++ } ++ ++ /* ++ * At this point idx will hold the index where the new branch should ++ * be inserted before. ++ */ ++found_insertion_point: ++ /* find the mode for the new branch */ ++ if (new_branch) ++ modename = strchr(new_branch, '='); ++ if (modename) ++ *modename++ = '\0'; ++ if (!new_branch || !*new_branch) { ++ printk(KERN_ERR "unionfs: null new branch\n"); ++ err = -EINVAL; ++ goto out; ++ } ++ err = parse_branch_mode(modename, &perms); ++ if (err) { ++ printk(KERN_ERR "unionfs: invalid mode \"%s\" for " ++ "branch \"%s\"\n", modename, new_branch); ++ goto out; ++ } ++ err = path_lookup(new_branch, LOOKUP_FOLLOW, &nd); ++ if (err) { ++ printk(KERN_ERR "unionfs: error accessing " ++ "lower directory \"%s\" (error %d)\n", ++ new_branch, err); ++ goto out; ++ } ++ /* ++ * It's probably safe to check_mode the new branch to insert. Note: ++ * we don't allow inserting branches which are unionfs's by ++ * themselves (check_branch returns EINVAL in that case). This is ++ * because this code base doesn't support stacking unionfs: the ODF ++ * code base supports that correctly. ++ */ ++ err = check_branch(&nd); ++ if (err) { ++ printk(KERN_ERR "unionfs: lower directory " ++ "\"%s\" is not a valid branch\n", optarg); ++ path_put(&nd.path); ++ goto out; ++ } ++ ++ /* ++ * Now we have to insert the new branch. But first, move the bits ++ * to make space for the new branch, if needed. Finally, adjust ++ * cur_branches. ++ * We don't release nd here; it's kept until umount/remount. ++ */ ++ if (idx < cur_branches) { ++ /* if idx==cur_branches, we append: easy */ ++ memmove(&new_data[idx+1], &new_data[idx], ++ (cur_branches - idx) * sizeof(struct unionfs_data)); ++ memmove(&new_lower_paths[idx+1], &new_lower_paths[idx], ++ (cur_branches - idx) * sizeof(struct path)); ++ } ++ new_lower_paths[idx].dentry = nd.path.dentry; ++ new_lower_paths[idx].mnt = nd.path.mnt; ++ ++ new_data[idx].sb = nd.path.dentry->d_sb; ++ atomic_set(&new_data[idx].open_files, 0); ++ new_data[idx].branchperms = perms; ++ new_data[idx].branch_id = ++*high_branch_id; /* assign new branch ID */ ++ ++ err = 0; ++out: ++ return err; ++} ++ ++ ++/* ++ * Support branch management options on remount. ++ * ++ * See Documentation/filesystems/unionfs/ for details. ++ * ++ * @flags: numeric mount options ++ * @options: mount options string ++ * ++ * This function can rearrange a mounted union dynamically, adding and ++ * removing branches, including changing branch modes. Clearly this has to ++ * be done safely and atomically. Luckily, the VFS already calls this ++ * function with lock_super(sb) and lock_kernel() held, preventing ++ * concurrent mixing of new mounts, remounts, and unmounts. Moreover, ++ * do_remount_sb(), our caller function, already called shrink_dcache_sb(sb) ++ * to purge dentries/inodes from our superblock, and also called ++ * fsync_super(sb) to purge any dirty pages. So we're good. ++ * ++ * XXX: however, our remount code may also need to invalidate mapped pages ++ * so as to force them to be re-gotten from the (newly reconfigured) lower ++ * branches. This has to wait for proper mmap and cache coherency support ++ * in the VFS. ++ * ++ */ ++static int unionfs_remount_fs(struct super_block *sb, int *flags, ++ char *options) ++{ ++ int err = 0; ++ int i; ++ char *optionstmp, *tmp_to_free; /* kstrdup'ed of "options" */ ++ char *optname; ++ int cur_branches = 0; /* no. of current branches */ ++ int new_branches = 0; /* no. of branches actually left in the end */ ++ int add_branches; /* est. no. of branches to add */ ++ int del_branches; /* est. no. of branches to del */ ++ int max_branches; /* max possible no. of branches */ ++ struct unionfs_data *new_data = NULL, *tmp_data = NULL; ++ struct path *new_lower_paths = NULL, *tmp_lower_paths = NULL; ++ struct inode **new_lower_inodes = NULL; ++ int new_high_branch_id; /* new high branch ID */ ++ int size; /* memory allocation size, temp var */ ++ int old_ibstart, old_ibend; ++ ++ unionfs_write_lock(sb); ++ ++ /* ++ * The VFS will take care of "ro" and "rw" flags, and we can safely ++ * ignore MS_SILENT, but anything else left over is an error. So we ++ * need to check if any other flags may have been passed (none are ++ * allowed/supported as of now). ++ */ ++ if ((*flags & ~(MS_RDONLY | MS_SILENT)) != 0) { ++ printk(KERN_ERR ++ "unionfs: remount flags 0x%x unsupported\n", *flags); ++ err = -EINVAL; ++ goto out_error; ++ } ++ ++ /* ++ * If 'options' is NULL, it's probably because the user just changed ++ * the union to a "ro" or "rw" and the VFS took care of it. So ++ * nothing to do and we're done. ++ */ ++ if (!options || options[0] == '\0') ++ goto out_error; ++ ++ /* ++ * Find out how many branches we will have in the end, counting ++ * "add" and "del" commands. Copy the "options" string because ++ * strsep modifies the string and we need it later. ++ */ ++ tmp_to_free = kstrdup(options, GFP_KERNEL); ++ optionstmp = tmp_to_free; ++ if (unlikely(!optionstmp)) { ++ err = -ENOMEM; ++ goto out_free; ++ } ++ cur_branches = sbmax(sb); /* current no. branches */ ++ new_branches = sbmax(sb); ++ del_branches = 0; ++ add_branches = 0; ++ new_high_branch_id = sbhbid(sb); /* save current high_branch_id */ ++ while ((optname = strsep(&optionstmp, ",")) != NULL) { ++ char *optarg; ++ ++ if (!optname || !*optname) ++ continue; ++ ++ optarg = strchr(optname, '='); ++ if (optarg) ++ *optarg++ = '\0'; ++ ++ if (!strcmp("add", optname)) ++ add_branches++; ++ else if (!strcmp("del", optname)) ++ del_branches++; ++ } ++ kfree(tmp_to_free); ++ /* after all changes, will we have at least one branch left? */ ++ if ((new_branches + add_branches - del_branches) < 1) { ++ printk(KERN_ERR ++ "unionfs: no branches left after remount\n"); ++ err = -EINVAL; ++ goto out_free; ++ } ++ ++ /* ++ * Since we haven't actually parsed all the add/del options, nor ++ * have we checked them for errors, we don't know for sure how many ++ * branches we will have after all changes have taken place. In ++ * fact, the total number of branches left could be less than what ++ * we have now. So we need to allocate space for a temporary ++ * placeholder that is at least as large as the maximum number of ++ * branches we *could* have, which is the current number plus all ++ * the additions. Once we're done with these temp placeholders, we ++ * may have to re-allocate the final size, copy over from the temp, ++ * and then free the temps (done near the end of this function). ++ */ ++ max_branches = cur_branches + add_branches; ++ /* allocate space for new pointers to lower dentry */ ++ tmp_data = kcalloc(max_branches, ++ sizeof(struct unionfs_data), GFP_KERNEL); ++ if (unlikely(!tmp_data)) { ++ err = -ENOMEM; ++ goto out_free; ++ } ++ /* allocate space for new pointers to lower paths */ ++ tmp_lower_paths = kcalloc(max_branches, ++ sizeof(struct path), GFP_KERNEL); ++ if (unlikely(!tmp_lower_paths)) { ++ err = -ENOMEM; ++ goto out_free; ++ } ++ /* copy current info into new placeholders, incrementing refcnts */ ++ memcpy(tmp_data, UNIONFS_SB(sb)->data, ++ cur_branches * sizeof(struct unionfs_data)); ++ memcpy(tmp_lower_paths, UNIONFS_D(sb->s_root)->lower_paths, ++ cur_branches * sizeof(struct path)); ++ for (i = 0; i < cur_branches; i++) ++ path_get(&tmp_lower_paths[i]); /* drop refs at end of fxn */ ++ ++ /******************************************************************* ++ * For each branch command, do path_lookup on the requested branch, ++ * and apply the change to a temp branch list. To handle errors, we ++ * already dup'ed the old arrays (above), and increased the refcnts ++ * on various f/s objects. So now we can do all the path_lookups ++ * and branch-management commands on the new arrays. If it fail mid ++ * way, we free the tmp arrays and *put all objects. If we succeed, ++ * then we free old arrays and *put its objects, and then replace ++ * the arrays with the new tmp list (we may have to re-allocate the ++ * memory because the temp lists could have been larger than what we ++ * actually needed). ++ *******************************************************************/ ++ ++ while ((optname = strsep(&options, ",")) != NULL) { ++ char *optarg; ++ ++ if (!optname || !*optname) ++ continue; ++ /* ++ * At this stage optname holds a comma-delimited option, but ++ * without the commas. Next, we need to break the string on ++ * the '=' symbol to separate CMD=ARG, where ARG itself can ++ * be KEY=VAL. For example, in mode=/foo=rw, CMD is "mode", ++ * KEY is "/foo", and VAL is "rw". ++ */ ++ optarg = strchr(optname, '='); ++ if (optarg) ++ *optarg++ = '\0'; ++ /* incgen remount option (instead of old ioctl) */ ++ if (!strcmp("incgen", optname)) { ++ err = 0; ++ goto out_no_change; ++ } ++ ++ /* ++ * All of our options take an argument now. (Insert ones ++ * that don't above this check.) So at this stage optname ++ * contains the CMD part and optarg contains the ARG part. ++ */ ++ if (!optarg || !*optarg) { ++ printk(KERN_ERR "unionfs: all remount options require " ++ "an argument (%s)\n", optname); ++ err = -EINVAL; ++ goto out_release; ++ } ++ ++ if (!strcmp("add", optname)) { ++ err = do_remount_add_option(optarg, new_branches, ++ tmp_data, ++ tmp_lower_paths, ++ &new_high_branch_id); ++ if (err) ++ goto out_release; ++ new_branches++; ++ if (new_branches > UNIONFS_MAX_BRANCHES) { ++ printk(KERN_ERR "unionfs: command exceeds " ++ "%d branches\n", UNIONFS_MAX_BRANCHES); ++ err = -E2BIG; ++ goto out_release; ++ } ++ continue; ++ } ++ if (!strcmp("del", optname)) { ++ err = do_remount_del_option(optarg, new_branches, ++ tmp_data, ++ tmp_lower_paths); ++ if (err) ++ goto out_release; ++ new_branches--; ++ continue; ++ } ++ if (!strcmp("mode", optname)) { ++ err = do_remount_mode_option(optarg, new_branches, ++ tmp_data, ++ tmp_lower_paths); ++ if (err) ++ goto out_release; ++ continue; ++ } ++ ++ /* ++ * When you use "mount -o remount,ro", mount(8) will ++ * reportedly pass the original dirs= string from ++ * /proc/mounts. So for now, we have to ignore dirs= and ++ * not consider it an error, unless we want to allow users ++ * to pass dirs= in remount. Note that to allow the VFS to ++ * actually process the ro/rw remount options, we have to ++ * return 0 from this function. ++ */ ++ if (!strcmp("dirs", optname)) { ++ printk(KERN_WARNING ++ "unionfs: remount ignoring option \"%s\"\n", ++ optname); ++ continue; ++ } ++ ++ err = -EINVAL; ++ printk(KERN_ERR ++ "unionfs: unrecognized option \"%s\"\n", optname); ++ goto out_release; ++ } ++ ++out_no_change: ++ ++ /****************************************************************** ++ * WE'RE ALMOST DONE: check if leftmost branch might be read-only, ++ * see if we need to allocate a small-sized new vector, copy the ++ * vectors to their correct place, release the refcnt of the older ++ * ones, and return. Also handle invalidating any pages that will ++ * have to be re-read. ++ *******************************************************************/ ++ ++ if (!(tmp_data[0].branchperms & MAY_WRITE)) { ++ printk(KERN_ERR "unionfs: leftmost branch cannot be read-only " ++ "(use \"remount,ro\" to create a read-only union)\n"); ++ err = -EINVAL; ++ goto out_release; ++ } ++ ++ /* (re)allocate space for new pointers to lower dentry */ ++ size = new_branches * sizeof(struct unionfs_data); ++ new_data = krealloc(tmp_data, size, GFP_KERNEL); ++ if (unlikely(!new_data)) { ++ err = -ENOMEM; ++ goto out_release; ++ } ++ ++ /* allocate space for new pointers to lower paths */ ++ size = new_branches * sizeof(struct path); ++ new_lower_paths = krealloc(tmp_lower_paths, size, GFP_KERNEL); ++ if (unlikely(!new_lower_paths)) { ++ err = -ENOMEM; ++ goto out_release; ++ } ++ ++ /* allocate space for new pointers to lower inodes */ ++ new_lower_inodes = kcalloc(new_branches, ++ sizeof(struct inode *), GFP_KERNEL); ++ if (unlikely(!new_lower_inodes)) { ++ err = -ENOMEM; ++ goto out_release; ++ } ++ ++ /* ++ * OK, just before we actually put the new set of branches in place, ++ * we need to ensure that our own f/s has no dirty objects left. ++ * Luckily, do_remount_sb() already calls shrink_dcache_sb(sb) and ++ * fsync_super(sb), taking care of dentries, inodes, and dirty ++ * pages. So all that's left is for us to invalidate any leftover ++ * (non-dirty) pages to ensure that they will be re-read from the ++ * new lower branches (and to support mmap). ++ */ ++ ++ /* ++ * Once we finish the remounting successfully, our superblock ++ * generation number will have increased. This will be detected by ++ * our dentry-revalidation code upon subsequent f/s operations ++ * through unionfs. The revalidation code will rebuild the union of ++ * lower inodes for a given unionfs inode and invalidate any pages ++ * of such "stale" inodes (by calling our purge_inode_data ++ * function). This revalidation will happen lazily and ++ * incrementally, as users perform operations on cached inodes. We ++ * would like to encourage this revalidation to happen sooner if ++ * possible, so we like to try to invalidate as many other pages in ++ * our superblock as we can. We used to call drop_pagecache_sb() or ++ * a variant thereof, but either method was racy (drop_caches alone ++ * is known to be racy). So now we let the revalidation happen on a ++ * per file basis in ->d_revalidate. ++ */ ++ ++ /* grab new lower super references; release old ones */ ++ for (i = 0; i < new_branches; i++) ++ atomic_inc(&new_data[i].sb->s_active); ++ for (i = 0; i < sbmax(sb); i++) ++ atomic_dec(&UNIONFS_SB(sb)->data[i].sb->s_active); ++ ++ /* copy new vectors into their correct place */ ++ tmp_data = UNIONFS_SB(sb)->data; ++ UNIONFS_SB(sb)->data = new_data; ++ new_data = NULL; /* so don't free good pointers below */ ++ tmp_lower_paths = UNIONFS_D(sb->s_root)->lower_paths; ++ UNIONFS_D(sb->s_root)->lower_paths = new_lower_paths; ++ new_lower_paths = NULL; /* so don't free good pointers below */ ++ ++ /* update our unionfs_sb_info and root dentry index of last branch */ ++ i = sbmax(sb); /* save no. of branches to release at end */ ++ sbend(sb) = new_branches - 1; ++ dbend(sb->s_root) = new_branches - 1; ++ old_ibstart = ibstart(sb->s_root->d_inode); ++ old_ibend = ibend(sb->s_root->d_inode); ++ ibend(sb->s_root->d_inode) = new_branches - 1; ++ UNIONFS_D(sb->s_root)->bcount = new_branches; ++ new_branches = i; /* no. of branches to release below */ ++ ++ /* ++ * Update lower inodes: 3 steps ++ * 1. grab ref on all new lower inodes ++ */ ++ for (i = dbstart(sb->s_root); i <= dbend(sb->s_root); i++) { ++ struct dentry *lower_dentry = ++ unionfs_lower_dentry_idx(sb->s_root, i); ++ igrab(lower_dentry->d_inode); ++ new_lower_inodes[i] = lower_dentry->d_inode; ++ } ++ /* 2. release reference on all older lower inodes */ ++ iput_lowers(sb->s_root->d_inode, old_ibstart, old_ibend, true); ++ /* 3. update root dentry's inode to new lower_inodes array */ ++ UNIONFS_I(sb->s_root->d_inode)->lower_inodes = new_lower_inodes; ++ new_lower_inodes = NULL; ++ ++ /* maxbytes may have changed */ ++ sb->s_maxbytes = unionfs_lower_super_idx(sb, 0)->s_maxbytes; ++ /* update high branch ID */ ++ sbhbid(sb) = new_high_branch_id; ++ ++ /* update our sb->generation for revalidating objects */ ++ i = atomic_inc_return(&UNIONFS_SB(sb)->generation); ++ atomic_set(&UNIONFS_D(sb->s_root)->generation, i); ++ atomic_set(&UNIONFS_I(sb->s_root->d_inode)->generation, i); ++ if (!(*flags & MS_SILENT)) ++ pr_info("unionfs: %s: new generation number %d\n", ++ UNIONFS_SB(sb)->dev_name, i); ++ /* finally, update the root dentry's times */ ++ unionfs_copy_attr_times(sb->s_root->d_inode); ++ err = 0; /* reset to success */ ++ ++ /* ++ * The code above falls through to the next label, and releases the ++ * refcnts of the older ones (stored in tmp_*): if we fell through ++ * here, it means success. However, if we jump directly to this ++ * label from any error above, then an error occurred after we ++ * grabbed various refcnts, and so we have to release the ++ * temporarily constructed structures. ++ */ ++out_release: ++ /* no need to cleanup/release anything in tmp_data */ ++ if (tmp_lower_paths) ++ for (i = 0; i < new_branches; i++) ++ path_put(&tmp_lower_paths[i]); ++out_free: ++ kfree(tmp_lower_paths); ++ kfree(tmp_data); ++ kfree(new_lower_paths); ++ kfree(new_data); ++ kfree(new_lower_inodes); ++out_error: ++ unionfs_check_dentry(sb->s_root); ++ unionfs_write_unlock(sb); ++ return err; ++} ++ ++/* ++ * Called by iput() when the inode reference count reached zero ++ * and the inode is not hashed anywhere. Used to clear anything ++ * that needs to be, before the inode is completely destroyed and put ++ * on the inode free list. ++ * ++ * No need to lock sb info's rwsem. ++ */ ++static void unionfs_clear_inode(struct inode *inode) ++{ ++ int bindex, bstart, bend; ++ struct inode *lower_inode; ++ struct list_head *pos, *n; ++ struct unionfs_dir_state *rdstate; ++ ++ list_for_each_safe(pos, n, &UNIONFS_I(inode)->readdircache) { ++ rdstate = list_entry(pos, struct unionfs_dir_state, cache); ++ list_del(&rdstate->cache); ++ free_rdstate(rdstate); ++ } ++ ++ /* ++ * Decrement a reference to a lower_inode, which was incremented ++ * by our read_inode when it was created initially. ++ */ ++ bstart = ibstart(inode); ++ bend = ibend(inode); ++ if (bstart >= 0) { ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ lower_inode = unionfs_lower_inode_idx(inode, bindex); ++ if (!lower_inode) ++ continue; ++ unionfs_set_lower_inode_idx(inode, bindex, NULL); ++ /* see Documentation/filesystems/unionfs/issues.txt */ ++ lockdep_off(); ++ iput(lower_inode); ++ lockdep_on(); ++ } ++ } ++ ++ kfree(UNIONFS_I(inode)->lower_inodes); ++ UNIONFS_I(inode)->lower_inodes = NULL; ++} ++ ++static struct inode *unionfs_alloc_inode(struct super_block *sb) ++{ ++ struct unionfs_inode_info *i; ++ ++ i = kmem_cache_alloc(unionfs_inode_cachep, GFP_KERNEL); ++ if (unlikely(!i)) ++ return NULL; ++ ++ /* memset everything up to the inode to 0 */ ++ memset(i, 0, offsetof(struct unionfs_inode_info, vfs_inode)); ++ ++ i->vfs_inode.i_version = 1; ++ return &i->vfs_inode; ++} ++ ++static void unionfs_destroy_inode(struct inode *inode) ++{ ++ kmem_cache_free(unionfs_inode_cachep, UNIONFS_I(inode)); ++} ++ ++/* unionfs inode cache constructor */ ++static void init_once(void *obj) ++{ ++ struct unionfs_inode_info *i = obj; ++ ++ inode_init_once(&i->vfs_inode); ++} ++ ++int unionfs_init_inode_cache(void) ++{ ++ int err = 0; ++ ++ unionfs_inode_cachep = ++ kmem_cache_create("unionfs_inode_cache", ++ sizeof(struct unionfs_inode_info), 0, ++ SLAB_RECLAIM_ACCOUNT, init_once); ++ if (unlikely(!unionfs_inode_cachep)) ++ err = -ENOMEM; ++ return err; ++} ++ ++/* unionfs inode cache destructor */ ++void unionfs_destroy_inode_cache(void) ++{ ++ if (unionfs_inode_cachep) ++ kmem_cache_destroy(unionfs_inode_cachep); ++} ++ ++/* ++ * Called when we have a dirty inode, right here we only throw out ++ * parts of our readdir list that are too old. ++ * ++ * No need to grab sb info's rwsem. ++ */ ++static int unionfs_write_inode(struct inode *inode, ++ struct writeback_control *wbc) ++{ ++ struct list_head *pos, *n; ++ struct unionfs_dir_state *rdstate; ++ ++ spin_lock(&UNIONFS_I(inode)->rdlock); ++ list_for_each_safe(pos, n, &UNIONFS_I(inode)->readdircache) { ++ rdstate = list_entry(pos, struct unionfs_dir_state, cache); ++ /* We keep this list in LRU order. */ ++ if ((rdstate->access + RDCACHE_JIFFIES) > jiffies) ++ break; ++ UNIONFS_I(inode)->rdcount--; ++ list_del(&rdstate->cache); ++ free_rdstate(rdstate); ++ } ++ spin_unlock(&UNIONFS_I(inode)->rdlock); ++ ++ return 0; ++} ++ ++/* ++ * Used only in nfs, to kill any pending RPC tasks, so that subsequent ++ * code can actually succeed and won't leave tasks that need handling. ++ */ ++static void unionfs_umount_begin(struct super_block *sb) ++{ ++ struct super_block *lower_sb; ++ int bindex, bstart, bend; ++ ++ unionfs_read_lock(sb, UNIONFS_SMUTEX_CHILD); ++ ++ bstart = sbstart(sb); ++ bend = sbend(sb); ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ lower_sb = unionfs_lower_super_idx(sb, bindex); ++ ++ if (lower_sb && lower_sb->s_op && ++ lower_sb->s_op->umount_begin) ++ lower_sb->s_op->umount_begin(lower_sb); ++ } ++ ++ unionfs_read_unlock(sb); ++} ++ ++static int unionfs_show_options(struct seq_file *m, struct vfsmount *mnt) ++{ ++ struct super_block *sb = mnt->mnt_sb; ++ int ret = 0; ++ char *tmp_page; ++ char *path; ++ int bindex, bstart, bend; ++ int perms; ++ ++ unionfs_read_lock(sb, UNIONFS_SMUTEX_CHILD); ++ ++ unionfs_lock_dentry(sb->s_root, UNIONFS_DMUTEX_CHILD); ++ ++ tmp_page = (char *) __get_free_page(GFP_KERNEL); ++ if (unlikely(!tmp_page)) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ bstart = sbstart(sb); ++ bend = sbend(sb); ++ ++ seq_printf(m, ",dirs="); ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ struct path p; ++ p.dentry = unionfs_lower_dentry_idx(sb->s_root, bindex); ++ p.mnt = unionfs_lower_mnt_idx(sb->s_root, bindex); ++ path = d_path(&p, tmp_page, PAGE_SIZE); ++ if (IS_ERR(path)) { ++ ret = PTR_ERR(path); ++ goto out; ++ } ++ ++ perms = branchperms(sb, bindex); ++ ++ seq_printf(m, "%s=%s", path, ++ perms & MAY_WRITE ? "rw" : "ro"); ++ if (bindex != bend) ++ seq_printf(m, ":"); ++ } ++ ++out: ++ free_page((unsigned long) tmp_page); ++ ++ unionfs_unlock_dentry(sb->s_root); ++ ++ unionfs_read_unlock(sb); ++ ++ return ret; ++} ++ ++struct super_operations unionfs_sops = { ++ .delete_inode = unionfs_delete_inode, ++ .put_super = unionfs_put_super, ++ .statfs = unionfs_statfs, ++ .remount_fs = unionfs_remount_fs, ++ .clear_inode = unionfs_clear_inode, ++ .umount_begin = unionfs_umount_begin, ++ .show_options = unionfs_show_options, ++ .write_inode = unionfs_write_inode, ++ .alloc_inode = unionfs_alloc_inode, ++ .destroy_inode = unionfs_destroy_inode, ++}; +diff --git a/fs/unionfs/union.h b/fs/unionfs/union.h +new file mode 100644 +index 0000000..d49c834 +--- /dev/null ++++ b/fs/unionfs/union.h +@@ -0,0 +1,669 @@ ++/* ++ * Copyright (c) 2003-2010 Erez Zadok ++ * Copyright (c) 2003-2006 Charles P. Wright ++ * Copyright (c) 2005-2007 Josef 'Jeff' Sipek ++ * Copyright (c) 2005 Arun M. Krishnakumar ++ * Copyright (c) 2004-2006 David P. Quigley ++ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair ++ * Copyright (c) 2003 Puja Gupta ++ * Copyright (c) 2003 Harikesavan Krishnan ++ * Copyright (c) 2003-2010 Stony Brook University ++ * Copyright (c) 2003-2010 The Research Foundation of SUNY ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef _UNION_H_ ++#define _UNION_H_ ++ ++#include <linux/dcache.h> ++#include <linux/file.h> ++#include <linux/list.h> ++#include <linux/fs.h> ++#include <linux/mm.h> ++#include <linux/module.h> ++#include <linux/mount.h> ++#include <linux/namei.h> ++#include <linux/page-flags.h> ++#include <linux/pagemap.h> ++#include <linux/poll.h> ++#include <linux/security.h> ++#include <linux/seq_file.h> ++#include <linux/slab.h> ++#include <linux/spinlock.h> ++#include <linux/smp_lock.h> ++#include <linux/statfs.h> ++#include <linux/string.h> ++#include <linux/vmalloc.h> ++#include <linux/writeback.h> ++#include <linux/buffer_head.h> ++#include <linux/xattr.h> ++#include <linux/fs_stack.h> ++#include <linux/magic.h> ++#include <linux/log2.h> ++#include <linux/poison.h> ++#include <linux/mman.h> ++#include <linux/backing-dev.h> ++#include <linux/splice.h> ++ ++#include <asm/system.h> ++ ++#include <linux/union_fs.h> ++ ++/* the file system name */ ++#define UNIONFS_NAME "unionfs" ++ ++/* unionfs root inode number */ ++#define UNIONFS_ROOT_INO 1 ++ ++/* number of times we try to get a unique temporary file name */ ++#define GET_TMPNAM_MAX_RETRY 5 ++ ++/* maximum number of branches we support, to avoid memory blowup */ ++#define UNIONFS_MAX_BRANCHES 128 ++ ++/* minimum time (seconds) required for time-based cache-coherency */ ++#define UNIONFS_MIN_CC_TIME 3 ++ ++/* Operations vectors defined in specific files. */ ++extern struct file_operations unionfs_main_fops; ++extern struct file_operations unionfs_dir_fops; ++extern struct inode_operations unionfs_main_iops; ++extern struct inode_operations unionfs_dir_iops; ++extern struct inode_operations unionfs_symlink_iops; ++extern struct super_operations unionfs_sops; ++extern struct dentry_operations unionfs_dops; ++extern struct address_space_operations unionfs_aops, unionfs_dummy_aops; ++extern struct vm_operations_struct unionfs_vm_ops; ++ ++/* How long should an entry be allowed to persist */ ++#define RDCACHE_JIFFIES (5*HZ) ++ ++/* compatibility with Real-Time patches */ ++#ifdef CONFIG_PREEMPT_RT ++# define unionfs_rw_semaphore compat_rw_semaphore ++#else /* not CONFIG_PREEMPT_RT */ ++# define unionfs_rw_semaphore rw_semaphore ++#endif /* not CONFIG_PREEMPT_RT */ ++ ++/* file private data. */ ++struct unionfs_file_info { ++ int bstart; ++ int bend; ++ atomic_t generation; ++ ++ struct unionfs_dir_state *rdstate; ++ struct file **lower_files; ++ int *saved_branch_ids; /* IDs of branches when file was opened */ ++ const struct vm_operations_struct *lower_vm_ops; ++ bool wrote_to_file; /* for delayed copyup */ ++}; ++ ++/* unionfs inode data in memory */ ++struct unionfs_inode_info { ++ int bstart; ++ int bend; ++ atomic_t generation; ++ /* Stuff for readdir over NFS. */ ++ spinlock_t rdlock; ++ struct list_head readdircache; ++ int rdcount; ++ int hashsize; ++ int cookie; ++ ++ /* The lower inodes */ ++ struct inode **lower_inodes; ++ ++ struct inode vfs_inode; ++}; ++ ++/* unionfs dentry data in memory */ ++struct unionfs_dentry_info { ++ /* ++ * The semaphore is used to lock the dentry as soon as we get into a ++ * unionfs function from the VFS. Our lock ordering is that children ++ * go before their parents. ++ */ ++ struct mutex lock; ++ int bstart; ++ int bend; ++ int bopaque; ++ int bcount; ++ atomic_t generation; ++ struct path *lower_paths; ++}; ++ ++/* These are the pointers to our various objects. */ ++struct unionfs_data { ++ struct super_block *sb; /* lower super_block */ ++ atomic_t open_files; /* number of open files on branch */ ++ int branchperms; ++ int branch_id; /* unique branch ID at re/mount time */ ++}; ++ ++/* unionfs super-block data in memory */ ++struct unionfs_sb_info { ++ int bend; ++ ++ atomic_t generation; ++ ++ /* ++ * This rwsem is used to make sure that a branch management ++ * operation... ++ * 1) will not begin before all currently in-flight operations ++ * complete. ++ * 2) any new operations do not execute until the currently ++ * running branch management operation completes. ++ * ++ * The write_lock_owner records the PID of the task which grabbed ++ * the rw_sem for writing. If the same task also tries to grab the ++ * read lock, we allow it. This prevents a self-deadlock when ++ * branch-management is used on a pivot_root'ed union, because we ++ * have to ->lookup paths which belong to the same union. ++ */ ++ struct unionfs_rw_semaphore rwsem; ++ pid_t write_lock_owner; /* PID of rw_sem owner (write lock) */ ++ int high_branch_id; /* last unique branch ID given */ ++ char *dev_name; /* to identify different unions in pr_debug */ ++ struct unionfs_data *data; ++}; ++ ++/* ++ * structure for making the linked list of entries by readdir on left branch ++ * to compare with entries on right branch ++ */ ++struct filldir_node { ++ struct list_head file_list; /* list for directory entries */ ++ char *name; /* name entry */ ++ int hash; /* name hash */ ++ int namelen; /* name len since name is not 0 terminated */ ++ ++ /* ++ * we can check for duplicate whiteouts and files in the same branch ++ * in order to return -EIO. ++ */ ++ int bindex; ++ ++ /* is this a whiteout entry? */ ++ int whiteout; ++ ++ /* Inline name, so we don't need to separately kmalloc small ones */ ++ char iname[DNAME_INLINE_LEN_MIN]; ++}; ++ ++/* Directory hash table. */ ++struct unionfs_dir_state { ++ unsigned int cookie; /* the cookie, based off of rdversion */ ++ unsigned int offset; /* The entry we have returned. */ ++ int bindex; ++ loff_t dirpos; /* offset within the lower level directory */ ++ int size; /* How big is the hash table? */ ++ int hashentries; /* How many entries have been inserted? */ ++ unsigned long access; ++ ++ /* This cache list is used when the inode keeps us around. */ ++ struct list_head cache; ++ struct list_head list[0]; ++}; ++ ++/* externs needed for fanout.h or sioq.h */ ++extern int unionfs_get_nlinks(const struct inode *inode); ++extern void unionfs_copy_attr_times(struct inode *upper); ++extern void unionfs_copy_attr_all(struct inode *dest, const struct inode *src); ++ ++/* include miscellaneous macros */ ++#include "fanout.h" ++#include "sioq.h" ++ ++/* externs for cache creation/deletion routines */ ++extern void unionfs_destroy_filldir_cache(void); ++extern int unionfs_init_filldir_cache(void); ++extern int unionfs_init_inode_cache(void); ++extern void unionfs_destroy_inode_cache(void); ++extern int unionfs_init_dentry_cache(void); ++extern void unionfs_destroy_dentry_cache(void); ++ ++/* Initialize and free readdir-specific state. */ ++extern int init_rdstate(struct file *file); ++extern struct unionfs_dir_state *alloc_rdstate(struct inode *inode, ++ int bindex); ++extern struct unionfs_dir_state *find_rdstate(struct inode *inode, ++ loff_t fpos); ++extern void free_rdstate(struct unionfs_dir_state *state); ++extern int add_filldir_node(struct unionfs_dir_state *rdstate, ++ const char *name, int namelen, int bindex, ++ int whiteout); ++extern struct filldir_node *find_filldir_node(struct unionfs_dir_state *rdstate, ++ const char *name, int namelen, ++ int is_whiteout); ++ ++extern struct dentry **alloc_new_dentries(int objs); ++extern struct unionfs_data *alloc_new_data(int objs); ++ ++/* We can only use 32-bits of offset for rdstate --- blech! */ ++#define DIREOF (0xfffff) ++#define RDOFFBITS 20 /* This is the number of bits in DIREOF. */ ++#define MAXRDCOOKIE (0xfff) ++/* Turn an rdstate into an offset. */ ++static inline off_t rdstate2offset(struct unionfs_dir_state *buf) ++{ ++ off_t tmp; ++ ++ tmp = ((buf->cookie & MAXRDCOOKIE) << RDOFFBITS) ++ | (buf->offset & DIREOF); ++ return tmp; ++} ++ ++/* Macros for locking a super_block. */ ++enum unionfs_super_lock_class { ++ UNIONFS_SMUTEX_NORMAL, ++ UNIONFS_SMUTEX_PARENT, /* when locking on behalf of file */ ++ UNIONFS_SMUTEX_CHILD, /* when locking on behalf of dentry */ ++}; ++static inline void unionfs_read_lock(struct super_block *sb, int subclass) ++{ ++ if (UNIONFS_SB(sb)->write_lock_owner && ++ UNIONFS_SB(sb)->write_lock_owner == current->pid) ++ return; ++ down_read_nested(&UNIONFS_SB(sb)->rwsem, subclass); ++} ++static inline void unionfs_read_unlock(struct super_block *sb) ++{ ++ if (UNIONFS_SB(sb)->write_lock_owner && ++ UNIONFS_SB(sb)->write_lock_owner == current->pid) ++ return; ++ up_read(&UNIONFS_SB(sb)->rwsem); ++} ++static inline void unionfs_write_lock(struct super_block *sb) ++{ ++ down_write(&UNIONFS_SB(sb)->rwsem); ++ UNIONFS_SB(sb)->write_lock_owner = current->pid; ++} ++static inline void unionfs_write_unlock(struct super_block *sb) ++{ ++ up_write(&UNIONFS_SB(sb)->rwsem); ++ UNIONFS_SB(sb)->write_lock_owner = 0; ++} ++ ++static inline void unionfs_double_lock_dentry(struct dentry *d1, ++ struct dentry *d2) ++{ ++ BUG_ON(d1 == d2); ++ if (d1 < d2) { ++ unionfs_lock_dentry(d1, UNIONFS_DMUTEX_PARENT); ++ unionfs_lock_dentry(d2, UNIONFS_DMUTEX_CHILD); ++ } else { ++ unionfs_lock_dentry(d2, UNIONFS_DMUTEX_PARENT); ++ unionfs_lock_dentry(d1, UNIONFS_DMUTEX_CHILD); ++ } ++} ++ ++static inline void unionfs_double_unlock_dentry(struct dentry *d1, ++ struct dentry *d2) ++{ ++ BUG_ON(d1 == d2); ++ if (d1 < d2) { /* unlock in reverse order than double_lock_dentry */ ++ unionfs_unlock_dentry(d1); ++ unionfs_unlock_dentry(d2); ++ } else { ++ unionfs_unlock_dentry(d2); ++ unionfs_unlock_dentry(d1); ++ } ++} ++ ++static inline void unionfs_double_lock_parents(struct dentry *p1, ++ struct dentry *p2) ++{ ++ if (p1 == p2) { ++ unionfs_lock_dentry(p1, UNIONFS_DMUTEX_REVAL_PARENT); ++ return; ++ } ++ if (p1 < p2) { ++ unionfs_lock_dentry(p1, UNIONFS_DMUTEX_REVAL_PARENT); ++ unionfs_lock_dentry(p2, UNIONFS_DMUTEX_REVAL_CHILD); ++ } else { ++ unionfs_lock_dentry(p2, UNIONFS_DMUTEX_REVAL_PARENT); ++ unionfs_lock_dentry(p1, UNIONFS_DMUTEX_REVAL_CHILD); ++ } ++} ++ ++static inline void unionfs_double_unlock_parents(struct dentry *p1, ++ struct dentry *p2) ++{ ++ if (p1 == p2) { ++ unionfs_unlock_dentry(p1); ++ return; ++ } ++ if (p1 < p2) { /* unlock in reverse order of double_lock_parents */ ++ unionfs_unlock_dentry(p1); ++ unionfs_unlock_dentry(p2); ++ } else { ++ unionfs_unlock_dentry(p2); ++ unionfs_unlock_dentry(p1); ++ } ++} ++ ++extern int new_dentry_private_data(struct dentry *dentry, int subclass); ++extern int realloc_dentry_private_data(struct dentry *dentry); ++extern void free_dentry_private_data(struct dentry *dentry); ++extern void update_bstart(struct dentry *dentry); ++extern int init_lower_nd(struct nameidata *nd, unsigned int flags); ++extern void release_lower_nd(struct nameidata *nd, int err); ++ ++/* ++ * EXTERNALS: ++ */ ++ ++/* replicates the directory structure up to given dentry in given branch */ ++extern struct dentry *create_parents(struct inode *dir, struct dentry *dentry, ++ const char *name, int bindex); ++ ++/* partial lookup */ ++extern int unionfs_partial_lookup(struct dentry *dentry, ++ struct dentry *parent); ++extern struct dentry *unionfs_lookup_full(struct dentry *dentry, ++ struct dentry *parent, ++ int lookupmode); ++ ++/* copies a file from dbstart to newbindex branch */ ++extern int copyup_file(struct inode *dir, struct file *file, int bstart, ++ int newbindex, loff_t size); ++extern int copyup_named_file(struct inode *dir, struct file *file, ++ char *name, int bstart, int new_bindex, ++ loff_t len); ++/* copies a dentry from dbstart to newbindex branch */ ++extern int copyup_dentry(struct inode *dir, struct dentry *dentry, ++ int bstart, int new_bindex, const char *name, ++ int namelen, struct file **copyup_file, loff_t len); ++/* helper functions for post-copyup actions */ ++extern void unionfs_postcopyup_setmnt(struct dentry *dentry); ++extern void unionfs_postcopyup_release(struct dentry *dentry); ++ ++/* Is this directory empty: 0 if it is empty, -ENOTEMPTY if not. */ ++extern int check_empty(struct dentry *dentry, struct dentry *parent, ++ struct unionfs_dir_state **namelist); ++/* whiteout and opaque directory helpers */ ++extern char *alloc_whname(const char *name, int len); ++extern bool is_whiteout_name(char **namep, int *namelenp); ++extern bool is_validname(const char *name); ++extern struct dentry *lookup_whiteout(const char *name, ++ struct dentry *lower_parent); ++extern struct dentry *find_first_whiteout(struct dentry *dentry); ++extern int unlink_whiteout(struct dentry *wh_dentry); ++extern int check_unlink_whiteout(struct dentry *dentry, ++ struct dentry *lower_dentry, int bindex); ++extern int create_whiteout(struct dentry *dentry, int start); ++extern int delete_whiteouts(struct dentry *dentry, int bindex, ++ struct unionfs_dir_state *namelist); ++extern int is_opaque_dir(struct dentry *dentry, int bindex); ++extern int make_dir_opaque(struct dentry *dir, int bindex); ++extern void unionfs_set_max_namelen(long *namelen); ++ ++extern void unionfs_reinterpose(struct dentry *this_dentry); ++extern struct super_block *unionfs_duplicate_super(struct super_block *sb); ++ ++/* Locking functions. */ ++extern int unionfs_setlk(struct file *file, int cmd, struct file_lock *fl); ++extern int unionfs_getlk(struct file *file, struct file_lock *fl); ++ ++/* Common file operations. */ ++extern int unionfs_file_revalidate(struct file *file, struct dentry *parent, ++ bool willwrite); ++extern int unionfs_open(struct inode *inode, struct file *file); ++extern int unionfs_file_release(struct inode *inode, struct file *file); ++extern int unionfs_flush(struct file *file, fl_owner_t id); ++extern long unionfs_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg); ++extern int unionfs_fsync(struct file *file, int datasync); ++extern int unionfs_fasync(int fd, struct file *file, int flag); ++ ++/* Inode operations */ ++extern struct inode *unionfs_iget(struct super_block *sb, unsigned long ino); ++extern int unionfs_rename(struct inode *old_dir, struct dentry *old_dentry, ++ struct inode *new_dir, struct dentry *new_dentry); ++extern int unionfs_unlink(struct inode *dir, struct dentry *dentry); ++extern int unionfs_rmdir(struct inode *dir, struct dentry *dentry); ++ ++extern bool __unionfs_d_revalidate(struct dentry *dentry, ++ struct dentry *parent, bool willwrite); ++extern bool is_negative_lower(const struct dentry *dentry); ++extern bool is_newer_lower(const struct dentry *dentry); ++extern void purge_sb_data(struct super_block *sb); ++ ++/* The values for unionfs_interpose's flag. */ ++#define INTERPOSE_DEFAULT 0 ++#define INTERPOSE_LOOKUP 1 ++#define INTERPOSE_REVAL 2 ++#define INTERPOSE_REVAL_NEG 3 ++#define INTERPOSE_PARTIAL 4 ++ ++extern struct dentry *unionfs_interpose(struct dentry *this_dentry, ++ struct super_block *sb, int flag); ++ ++#ifdef CONFIG_UNION_FS_XATTR ++/* Extended attribute functions. */ ++extern void *unionfs_xattr_alloc(size_t size, size_t limit); ++static inline void unionfs_xattr_kfree(const void *p) ++{ ++ kfree(p); ++} ++extern ssize_t unionfs_getxattr(struct dentry *dentry, const char *name, ++ void *value, size_t size); ++extern int unionfs_removexattr(struct dentry *dentry, const char *name); ++extern ssize_t unionfs_listxattr(struct dentry *dentry, char *list, ++ size_t size); ++extern int unionfs_setxattr(struct dentry *dentry, const char *name, ++ const void *value, size_t size, int flags); ++#endif /* CONFIG_UNION_FS_XATTR */ ++ ++/* The root directory is unhashed, but isn't deleted. */ ++static inline int d_deleted(struct dentry *d) ++{ ++ return d_unhashed(d) && (d != d->d_sb->s_root); ++} ++ ++/* unionfs_permission, check if we should bypass error to facilitate copyup */ ++#define IS_COPYUP_ERR(err) ((err) == -EROFS) ++ ++/* unionfs_open, check if we need to copyup the file */ ++#define OPEN_WRITE_FLAGS (O_WRONLY | O_RDWR | O_APPEND) ++#define IS_WRITE_FLAG(flag) ((flag) & OPEN_WRITE_FLAGS) ++ ++static inline int branchperms(const struct super_block *sb, int index) ++{ ++ BUG_ON(index < 0); ++ return UNIONFS_SB(sb)->data[index].branchperms; ++} ++ ++static inline int set_branchperms(struct super_block *sb, int index, int perms) ++{ ++ BUG_ON(index < 0); ++ UNIONFS_SB(sb)->data[index].branchperms = perms; ++ return perms; ++} ++ ++/* check if readonly lower inode, but possibly unlinked (no inode->i_sb) */ ++static inline int __is_rdonly(const struct inode *inode) ++{ ++ /* if unlinked, can't be readonly (?) */ ++ if (!inode->i_sb) ++ return 0; ++ return IS_RDONLY(inode); ++ ++} ++/* Is this file on a read-only branch? */ ++static inline int is_robranch_super(const struct super_block *sb, int index) ++{ ++ int ret; ++ ++ ret = (!(branchperms(sb, index) & MAY_WRITE)) ? -EROFS : 0; ++ return ret; ++} ++ ++/* Is this file on a read-only branch? */ ++static inline int is_robranch_idx(const struct dentry *dentry, int index) ++{ ++ struct super_block *lower_sb; ++ ++ BUG_ON(index < 0); ++ ++ if (!(branchperms(dentry->d_sb, index) & MAY_WRITE)) ++ return -EROFS; ++ ++ lower_sb = unionfs_lower_super_idx(dentry->d_sb, index); ++ BUG_ON(lower_sb == NULL); ++ /* ++ * test sb flags directly, not IS_RDONLY(lower_inode) because the ++ * lower_dentry could be a negative. ++ */ ++ if (lower_sb->s_flags & MS_RDONLY) ++ return -EROFS; ++ ++ return 0; ++} ++ ++static inline int is_robranch(const struct dentry *dentry) ++{ ++ int index; ++ ++ index = UNIONFS_D(dentry)->bstart; ++ BUG_ON(index < 0); ++ ++ return is_robranch_idx(dentry, index); ++} ++ ++/* ++ * EXTERNALS: ++ */ ++extern int check_branch(struct nameidata *nd); ++extern int parse_branch_mode(const char *name, int *perms); ++ ++/* locking helpers */ ++static inline struct dentry *lock_parent(struct dentry *dentry) ++{ ++ struct dentry *dir = dget_parent(dentry); ++ mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); ++ return dir; ++} ++static inline struct dentry *lock_parent_wh(struct dentry *dentry) ++{ ++ struct dentry *dir = dget_parent(dentry); ++ ++ mutex_lock_nested(&dir->d_inode->i_mutex, UNIONFS_DMUTEX_WHITEOUT); ++ return dir; ++} ++ ++static inline void unlock_dir(struct dentry *dir) ++{ ++ mutex_unlock(&dir->d_inode->i_mutex); ++ dput(dir); ++} ++ ++/* lock base inode mutex before calling lookup_one_len */ ++static inline struct dentry *lookup_lck_len(const char *name, ++ struct dentry *base, int len) ++{ ++ struct dentry *d; ++ mutex_lock(&base->d_inode->i_mutex); ++ d = lookup_one_len(name, base, len); ++ mutex_unlock(&base->d_inode->i_mutex); ++ return d; ++} ++ ++static inline struct vfsmount *unionfs_mntget(struct dentry *dentry, ++ int bindex) ++{ ++ struct vfsmount *mnt; ++ ++ BUG_ON(!dentry || bindex < 0); ++ ++ mnt = mntget(unionfs_lower_mnt_idx(dentry, bindex)); ++#ifdef CONFIG_UNION_FS_DEBUG ++ if (!mnt) ++ pr_debug("unionfs: mntget: mnt=%p bindex=%d\n", ++ mnt, bindex); ++#endif /* CONFIG_UNION_FS_DEBUG */ ++ ++ return mnt; ++} ++ ++static inline void unionfs_mntput(struct dentry *dentry, int bindex) ++{ ++ struct vfsmount *mnt; ++ ++ if (!dentry && bindex < 0) ++ return; ++ BUG_ON(!dentry || bindex < 0); ++ ++ mnt = unionfs_lower_mnt_idx(dentry, bindex); ++#ifdef CONFIG_UNION_FS_DEBUG ++ /* ++ * Directories can have NULL lower objects in between start/end, but ++ * NOT if at the start/end range. We cannot verify that this dentry ++ * is a type=DIR, because it may already be a negative dentry. But ++ * if dbstart is greater than dbend, we know that this couldn't have ++ * been a regular file: it had to have been a directory. ++ */ ++ if (!mnt && !(bindex > dbstart(dentry) && bindex < dbend(dentry))) ++ pr_debug("unionfs: mntput: mnt=%p bindex=%d\n", mnt, bindex); ++#endif /* CONFIG_UNION_FS_DEBUG */ ++ mntput(mnt); ++} ++ ++#ifdef CONFIG_UNION_FS_DEBUG ++ ++/* useful for tracking code reachability */ ++#define UDBG pr_debug("DBG:%s:%s:%d\n", __FILE__, __func__, __LINE__) ++ ++#define unionfs_check_inode(i) __unionfs_check_inode((i), \ ++ __FILE__, __func__, __LINE__) ++#define unionfs_check_dentry(d) __unionfs_check_dentry((d), \ ++ __FILE__, __func__, __LINE__) ++#define unionfs_check_file(f) __unionfs_check_file((f), \ ++ __FILE__, __func__, __LINE__) ++#define unionfs_check_nd(n) __unionfs_check_nd((n), \ ++ __FILE__, __func__, __LINE__) ++#define show_branch_counts(sb) __show_branch_counts((sb), \ ++ __FILE__, __func__, __LINE__) ++#define show_inode_times(i) __show_inode_times((i), \ ++ __FILE__, __func__, __LINE__) ++#define show_dinode_times(d) __show_dinode_times((d), \ ++ __FILE__, __func__, __LINE__) ++#define show_inode_counts(i) __show_inode_counts((i), \ ++ __FILE__, __func__, __LINE__) ++ ++extern void __unionfs_check_inode(const struct inode *inode, const char *fname, ++ const char *fxn, int line); ++extern void __unionfs_check_dentry(const struct dentry *dentry, ++ const char *fname, const char *fxn, ++ int line); ++extern void __unionfs_check_file(const struct file *file, ++ const char *fname, const char *fxn, int line); ++extern void __unionfs_check_nd(const struct nameidata *nd, ++ const char *fname, const char *fxn, int line); ++extern void __show_branch_counts(const struct super_block *sb, ++ const char *file, const char *fxn, int line); ++extern void __show_inode_times(const struct inode *inode, ++ const char *file, const char *fxn, int line); ++extern void __show_dinode_times(const struct dentry *dentry, ++ const char *file, const char *fxn, int line); ++extern void __show_inode_counts(const struct inode *inode, ++ const char *file, const char *fxn, int line); ++ ++#else /* not CONFIG_UNION_FS_DEBUG */ ++ ++/* we leave useful hooks for these check functions throughout the code */ ++#define unionfs_check_inode(i) do { } while (0) ++#define unionfs_check_dentry(d) do { } while (0) ++#define unionfs_check_file(f) do { } while (0) ++#define unionfs_check_nd(n) do { } while (0) ++#define show_branch_counts(sb) do { } while (0) ++#define show_inode_times(i) do { } while (0) ++#define show_dinode_times(d) do { } while (0) ++#define show_inode_counts(i) do { } while (0) ++ ++#endif /* not CONFIG_UNION_FS_DEBUG */ ++ ++#endif /* not _UNION_H_ */ +diff --git a/fs/unionfs/unlink.c b/fs/unionfs/unlink.c +new file mode 100644 +index 0000000..542c513 +--- /dev/null ++++ b/fs/unionfs/unlink.c +@@ -0,0 +1,278 @@ ++/* ++ * Copyright (c) 2003-2010 Erez Zadok ++ * Copyright (c) 2003-2006 Charles P. Wright ++ * Copyright (c) 2005-2007 Josef 'Jeff' Sipek ++ * Copyright (c) 2005-2006 Junjiro Okajima ++ * Copyright (c) 2005 Arun M. Krishnakumar ++ * Copyright (c) 2004-2006 David P. Quigley ++ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair ++ * Copyright (c) 2003 Puja Gupta ++ * Copyright (c) 2003 Harikesavan Krishnan ++ * Copyright (c) 2003-2010 Stony Brook University ++ * Copyright (c) 2003-2010 The Research Foundation of SUNY ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "union.h" ++ ++/* ++ * Helper function for Unionfs's unlink operation. ++ * ++ * The main goal of this function is to optimize the unlinking of non-dir ++ * objects in unionfs by deleting all possible lower inode objects from the ++ * underlying branches having same dentry name as the non-dir dentry on ++ * which this unlink operation is called. This way we delete as many lower ++ * inodes as possible, and save space. Whiteouts need to be created in ++ * branch0 only if unlinking fails on any of the lower branch other than ++ * branch0, or if a lower branch is marked read-only. ++ * ++ * Also, while unlinking a file, if we encounter any dir type entry in any ++ * intermediate branch, then we remove the directory by calling vfs_rmdir. ++ * The following special cases are also handled: ++ ++ * (1) If an error occurs in branch0 during vfs_unlink, then we return ++ * appropriate error. ++ * ++ * (2) If we get an error during unlink in any of other lower branch other ++ * than branch0, then we create a whiteout in branch0. ++ * ++ * (3) If a whiteout already exists in any intermediate branch, we delete ++ * all possible inodes only up to that branch (this is an "opaqueness" ++ * as as per Documentation/filesystems/unionfs/concepts.txt). ++ * ++ */ ++static int unionfs_unlink_whiteout(struct inode *dir, struct dentry *dentry, ++ struct dentry *parent) ++{ ++ struct dentry *lower_dentry; ++ struct dentry *lower_dir_dentry; ++ int bindex; ++ int err = 0; ++ ++ err = unionfs_partial_lookup(dentry, parent); ++ if (err) ++ goto out; ++ ++ /* trying to unlink all possible valid instances */ ++ for (bindex = dbstart(dentry); bindex <= dbend(dentry); bindex++) { ++ lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); ++ if (!lower_dentry || !lower_dentry->d_inode) ++ continue; ++ ++ lower_dir_dentry = lock_parent(lower_dentry); ++ ++ /* avoid destroying the lower inode if the object is in use */ ++ dget(lower_dentry); ++ err = is_robranch_super(dentry->d_sb, bindex); ++ if (!err) { ++ /* see Documentation/filesystems/unionfs/issues.txt */ ++ lockdep_off(); ++ if (!S_ISDIR(lower_dentry->d_inode->i_mode)) ++ err = vfs_unlink(lower_dir_dentry->d_inode, ++ lower_dentry); ++ else ++ err = vfs_rmdir(lower_dir_dentry->d_inode, ++ lower_dentry); ++ lockdep_on(); ++ } ++ ++ /* if lower object deletion succeeds, update inode's times */ ++ if (!err) ++ unionfs_copy_attr_times(dentry->d_inode); ++ dput(lower_dentry); ++ fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode); ++ unlock_dir(lower_dir_dentry); ++ ++ if (err) ++ break; ++ } ++ ++ /* ++ * Create the whiteout in branch 0 (highest priority) only if (a) ++ * there was an error in any intermediate branch other than branch 0 ++ * due to failure of vfs_unlink/vfs_rmdir or (b) a branch marked or ++ * mounted read-only. ++ */ ++ if (err) { ++ if ((bindex == 0) || ++ ((bindex == dbstart(dentry)) && ++ (!IS_COPYUP_ERR(err)))) ++ goto out; ++ else { ++ if (!IS_COPYUP_ERR(err)) ++ pr_debug("unionfs: lower object deletion " ++ "failed in branch:%d\n", bindex); ++ err = create_whiteout(dentry, sbstart(dentry->d_sb)); ++ } ++ } ++ ++out: ++ if (!err) ++ inode_dec_link_count(dentry->d_inode); ++ ++ /* We don't want to leave negative leftover dentries for revalidate. */ ++ if (!err && (dbopaque(dentry) != -1)) ++ update_bstart(dentry); ++ ++ return err; ++} ++ ++int unionfs_unlink(struct inode *dir, struct dentry *dentry) ++{ ++ int err = 0; ++ struct inode *inode = dentry->d_inode; ++ struct dentry *parent; ++ int valid; ++ ++ BUG_ON(S_ISDIR(inode->i_mode)); ++ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); ++ parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); ++ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); ++ ++ valid = __unionfs_d_revalidate(dentry, parent, false); ++ if (unlikely(!valid)) { ++ err = -ESTALE; ++ goto out; ++ } ++ unionfs_check_dentry(dentry); ++ ++ err = unionfs_unlink_whiteout(dir, dentry, parent); ++ /* call d_drop so the system "forgets" about us */ ++ if (!err) { ++ unionfs_postcopyup_release(dentry); ++ unionfs_postcopyup_setmnt(parent); ++ if (inode->i_nlink == 0) /* drop lower inodes */ ++ iput_lowers_all(inode, false); ++ d_drop(dentry); ++ /* ++ * if unlink/whiteout succeeded, parent dir mtime has ++ * changed ++ */ ++ unionfs_copy_attr_times(dir); ++ } ++ ++out: ++ if (!err) { ++ unionfs_check_dentry(dentry); ++ unionfs_check_inode(dir); ++ } ++ unionfs_unlock_dentry(dentry); ++ unionfs_unlock_parent(dentry, parent); ++ unionfs_read_unlock(dentry->d_sb); ++ return err; ++} ++ ++static int unionfs_rmdir_first(struct inode *dir, struct dentry *dentry, ++ struct unionfs_dir_state *namelist) ++{ ++ int err; ++ struct dentry *lower_dentry; ++ struct dentry *lower_dir_dentry = NULL; ++ ++ /* Here we need to remove whiteout entries. */ ++ err = delete_whiteouts(dentry, dbstart(dentry), namelist); ++ if (err) ++ goto out; ++ ++ lower_dentry = unionfs_lower_dentry(dentry); ++ ++ lower_dir_dentry = lock_parent(lower_dentry); ++ ++ /* avoid destroying the lower inode if the file is in use */ ++ dget(lower_dentry); ++ err = is_robranch(dentry); ++ if (!err) ++ err = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry); ++ dput(lower_dentry); ++ ++ fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode); ++ /* propagate number of hard-links */ ++ dentry->d_inode->i_nlink = unionfs_get_nlinks(dentry->d_inode); ++ ++out: ++ if (lower_dir_dentry) ++ unlock_dir(lower_dir_dentry); ++ return err; ++} ++ ++int unionfs_rmdir(struct inode *dir, struct dentry *dentry) ++{ ++ int err = 0; ++ struct unionfs_dir_state *namelist = NULL; ++ struct dentry *parent; ++ int dstart, dend; ++ bool valid; ++ ++ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); ++ parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); ++ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); ++ ++ valid = __unionfs_d_revalidate(dentry, parent, false); ++ if (unlikely(!valid)) { ++ err = -ESTALE; ++ goto out; ++ } ++ unionfs_check_dentry(dentry); ++ ++ /* check if this unionfs directory is empty or not */ ++ err = check_empty(dentry, parent, &namelist); ++ if (err) ++ goto out; ++ ++ err = unionfs_rmdir_first(dir, dentry, namelist); ++ dstart = dbstart(dentry); ++ dend = dbend(dentry); ++ /* ++ * We create a whiteout for the directory if there was an error to ++ * rmdir the first directory entry in the union. Otherwise, we ++ * create a whiteout only if there is no chance that a lower ++ * priority branch might also have the same named directory. IOW, ++ * if there is not another same-named directory at a lower priority ++ * branch, then we don't need to create a whiteout for it. ++ */ ++ if (!err) { ++ if (dstart < dend) ++ err = create_whiteout(dentry, dstart); ++ } else { ++ int new_err; ++ ++ if (dstart == 0) ++ goto out; ++ ++ /* exit if the error returned was NOT -EROFS */ ++ if (!IS_COPYUP_ERR(err)) ++ goto out; ++ ++ new_err = create_whiteout(dentry, dstart - 1); ++ if (new_err != -EEXIST) ++ err = new_err; ++ } ++ ++out: ++ /* ++ * Drop references to lower dentry/inode so storage space for them ++ * can be reclaimed. Then, call d_drop so the system "forgets" ++ * about us. ++ */ ++ if (!err) { ++ iput_lowers_all(dentry->d_inode, false); ++ dput(unionfs_lower_dentry_idx(dentry, dstart)); ++ unionfs_set_lower_dentry_idx(dentry, dstart, NULL); ++ d_drop(dentry); ++ /* update our lower vfsmnts, in case a copyup took place */ ++ unionfs_postcopyup_setmnt(dentry); ++ unionfs_check_dentry(dentry); ++ unionfs_check_inode(dir); ++ } ++ ++ if (namelist) ++ free_rdstate(namelist); ++ ++ unionfs_unlock_dentry(dentry); ++ unionfs_unlock_parent(dentry, parent); ++ unionfs_read_unlock(dentry->d_sb); ++ return err; ++} +diff --git a/fs/unionfs/whiteout.c b/fs/unionfs/whiteout.c +new file mode 100644 +index 0000000..405073a +--- /dev/null ++++ b/fs/unionfs/whiteout.c +@@ -0,0 +1,584 @@ ++/* ++ * Copyright (c) 2003-2010 Erez Zadok ++ * Copyright (c) 2003-2006 Charles P. Wright ++ * Copyright (c) 2005-2007 Josef 'Jeff' Sipek ++ * Copyright (c) 2005-2006 Junjiro Okajima ++ * Copyright (c) 2005 Arun M. Krishnakumar ++ * Copyright (c) 2004-2006 David P. Quigley ++ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair ++ * Copyright (c) 2003 Puja Gupta ++ * Copyright (c) 2003 Harikesavan Krishnan ++ * Copyright (c) 2003-2010 Stony Brook University ++ * Copyright (c) 2003-2010 The Research Foundation of SUNY ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "union.h" ++ ++/* ++ * whiteout and opaque directory helpers ++ */ ++ ++/* What do we use for whiteouts. */ ++#define UNIONFS_WHPFX ".wh." ++#define UNIONFS_WHLEN 4 ++/* ++ * If a directory contains this file, then it is opaque. We start with the ++ * .wh. flag so that it is blocked by lookup. ++ */ ++#define UNIONFS_DIR_OPAQUE_NAME "__dir_opaque" ++#define UNIONFS_DIR_OPAQUE UNIONFS_WHPFX UNIONFS_DIR_OPAQUE_NAME ++ ++/* construct whiteout filename */ ++char *alloc_whname(const char *name, int len) ++{ ++ char *buf; ++ ++ buf = kmalloc(len + UNIONFS_WHLEN + 1, GFP_KERNEL); ++ if (unlikely(!buf)) ++ return ERR_PTR(-ENOMEM); ++ ++ strcpy(buf, UNIONFS_WHPFX); ++ strlcat(buf, name, len + UNIONFS_WHLEN + 1); ++ ++ return buf; ++} ++ ++/* ++ * XXX: this can be inline or CPP macro, but is here to keep all whiteout ++ * code in one place. ++ */ ++void unionfs_set_max_namelen(long *namelen) ++{ ++ *namelen -= UNIONFS_WHLEN; ++} ++ ++/* check if @namep is a whiteout, update @namep and @namelenp accordingly */ ++bool is_whiteout_name(char **namep, int *namelenp) ++{ ++ if (*namelenp > UNIONFS_WHLEN && ++ !strncmp(*namep, UNIONFS_WHPFX, UNIONFS_WHLEN)) { ++ *namep += UNIONFS_WHLEN; ++ *namelenp -= UNIONFS_WHLEN; ++ return true; ++ } ++ return false; ++} ++ ++/* is the filename valid == !(whiteout for a file or opaque dir marker) */ ++bool is_validname(const char *name) ++{ ++ if (!strncmp(name, UNIONFS_WHPFX, UNIONFS_WHLEN)) ++ return false; ++ if (!strncmp(name, UNIONFS_DIR_OPAQUE_NAME, ++ sizeof(UNIONFS_DIR_OPAQUE_NAME) - 1)) ++ return false; ++ return true; ++} ++ ++/* ++ * Look for a whiteout @name in @lower_parent directory. If error, return ++ * ERR_PTR. Caller must dput() the returned dentry if not an error. ++ * ++ * XXX: some callers can reuse the whname allocated buffer to avoid repeated ++ * free then re-malloc calls. Need to provide a different API for those ++ * callers. ++ */ ++struct dentry *lookup_whiteout(const char *name, struct dentry *lower_parent) ++{ ++ char *whname = NULL; ++ int err = 0, namelen; ++ struct dentry *wh_dentry = NULL; ++ ++ namelen = strlen(name); ++ whname = alloc_whname(name, namelen); ++ if (unlikely(IS_ERR(whname))) { ++ err = PTR_ERR(whname); ++ goto out; ++ } ++ ++ /* check if whiteout exists in this branch: lookup .wh.foo */ ++ wh_dentry = lookup_lck_len(whname, lower_parent, strlen(whname)); ++ if (IS_ERR(wh_dentry)) { ++ err = PTR_ERR(wh_dentry); ++ goto out; ++ } ++ ++ /* check if negative dentry (ENOENT) */ ++ if (!wh_dentry->d_inode) ++ goto out; ++ ++ /* whiteout found: check if valid type */ ++ if (!S_ISREG(wh_dentry->d_inode->i_mode)) { ++ printk(KERN_ERR "unionfs: invalid whiteout %s entry type %d\n", ++ whname, wh_dentry->d_inode->i_mode); ++ dput(wh_dentry); ++ err = -EIO; ++ goto out; ++ } ++ ++out: ++ kfree(whname); ++ if (err) ++ wh_dentry = ERR_PTR(err); ++ return wh_dentry; ++} ++ ++/* find and return first whiteout in parent directory, else ENOENT */ ++struct dentry *find_first_whiteout(struct dentry *dentry) ++{ ++ int bindex, bstart, bend; ++ struct dentry *parent, *lower_parent, *wh_dentry; ++ ++ parent = dget_parent(dentry); ++ ++ bstart = dbstart(parent); ++ bend = dbend(parent); ++ wh_dentry = ERR_PTR(-ENOENT); ++ ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ lower_parent = unionfs_lower_dentry_idx(parent, bindex); ++ if (!lower_parent) ++ continue; ++ wh_dentry = lookup_whiteout(dentry->d_name.name, lower_parent); ++ if (IS_ERR(wh_dentry)) ++ continue; ++ if (wh_dentry->d_inode) ++ break; ++ dput(wh_dentry); ++ wh_dentry = ERR_PTR(-ENOENT); ++ } ++ ++ dput(parent); ++ ++ return wh_dentry; ++} ++ ++/* ++ * Unlink a whiteout dentry. Returns 0 or -errno. Caller must hold and ++ * release dentry reference. ++ */ ++int unlink_whiteout(struct dentry *wh_dentry) ++{ ++ int err; ++ struct dentry *lower_dir_dentry; ++ ++ /* dget and lock parent dentry */ ++ lower_dir_dentry = lock_parent_wh(wh_dentry); ++ ++ /* see Documentation/filesystems/unionfs/issues.txt */ ++ lockdep_off(); ++ err = vfs_unlink(lower_dir_dentry->d_inode, wh_dentry); ++ lockdep_on(); ++ unlock_dir(lower_dir_dentry); ++ ++ /* ++ * Whiteouts are special files and should be deleted no matter what ++ * (as if they never existed), in order to allow this create ++ * operation to succeed. This is especially important in sticky ++ * directories: a whiteout may have been created by one user, but ++ * the newly created file may be created by another user. ++ * Therefore, in order to maintain Unix semantics, if the vfs_unlink ++ * above failed, then we have to try to directly unlink the ++ * whiteout. Note: in the ODF version of unionfs, whiteout are ++ * handled much more cleanly. ++ */ ++ if (err == -EPERM) { ++ struct inode *inode = lower_dir_dentry->d_inode; ++ err = inode->i_op->unlink(inode, wh_dentry); ++ } ++ if (err) ++ printk(KERN_ERR "unionfs: could not unlink whiteout %s, " ++ "err = %d\n", wh_dentry->d_name.name, err); ++ ++ return err; ++ ++} ++ ++/* ++ * Helper function when creating new objects (create, symlink, mknod, etc.). ++ * Checks to see if there's a whiteout in @lower_dentry's parent directory, ++ * whose name is taken from @dentry. Then tries to remove that whiteout, if ++ * found. If <dentry,bindex> is a branch marked readonly, return -EROFS. ++ * If it finds both a regular file and a whiteout, return -EIO (this should ++ * never happen). ++ * ++ * Return 0 if no whiteout was found. Return 1 if one was found and ++ * successfully removed. Therefore a value >= 0 tells the caller that ++ * @lower_dentry belongs to a good branch to create the new object in). ++ * Return -ERRNO if an error occurred during whiteout lookup or in trying to ++ * unlink the whiteout. ++ */ ++int check_unlink_whiteout(struct dentry *dentry, struct dentry *lower_dentry, ++ int bindex) ++{ ++ int err; ++ struct dentry *wh_dentry = NULL; ++ struct dentry *lower_dir_dentry = NULL; ++ ++ /* look for whiteout dentry first */ ++ lower_dir_dentry = dget_parent(lower_dentry); ++ wh_dentry = lookup_whiteout(dentry->d_name.name, lower_dir_dentry); ++ dput(lower_dir_dentry); ++ if (IS_ERR(wh_dentry)) { ++ err = PTR_ERR(wh_dentry); ++ goto out; ++ } ++ ++ if (!wh_dentry->d_inode) { /* no whiteout exists*/ ++ err = 0; ++ goto out_dput; ++ } ++ ++ /* check if regular file and whiteout were both found */ ++ if (unlikely(lower_dentry->d_inode)) { ++ err = -EIO; ++ printk(KERN_ERR "unionfs: found both whiteout and regular " ++ "file in directory %s (branch %d)\n", ++ lower_dir_dentry->d_name.name, bindex); ++ goto out_dput; ++ } ++ ++ /* check if branch is writeable */ ++ err = is_robranch_super(dentry->d_sb, bindex); ++ if (err) ++ goto out_dput; ++ ++ /* .wh.foo has been found, so let's unlink it */ ++ err = unlink_whiteout(wh_dentry); ++ if (!err) ++ err = 1; /* a whiteout was found and successfully removed */ ++out_dput: ++ dput(wh_dentry); ++out: ++ return err; ++} ++ ++/* ++ * Pass an unionfs dentry and an index. It will try to create a whiteout ++ * for the filename in dentry, and will try in branch 'index'. On error, ++ * it will proceed to a branch to the left. ++ */ ++int create_whiteout(struct dentry *dentry, int start) ++{ ++ int bstart, bend, bindex; ++ struct dentry *lower_dir_dentry; ++ struct dentry *lower_dentry; ++ struct dentry *lower_wh_dentry; ++ struct nameidata nd; ++ char *name = NULL; ++ int err = -EINVAL; ++ ++ verify_locked(dentry); ++ ++ bstart = dbstart(dentry); ++ bend = dbend(dentry); ++ ++ /* create dentry's whiteout equivalent */ ++ name = alloc_whname(dentry->d_name.name, dentry->d_name.len); ++ if (unlikely(IS_ERR(name))) { ++ err = PTR_ERR(name); ++ goto out; ++ } ++ ++ for (bindex = start; bindex >= 0; bindex--) { ++ lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); ++ ++ if (!lower_dentry) { ++ /* ++ * if lower dentry is not present, create the ++ * entire lower dentry directory structure and go ++ * ahead. Since we want to just create whiteout, we ++ * only want the parent dentry, and hence get rid of ++ * this dentry. ++ */ ++ lower_dentry = create_parents(dentry->d_inode, ++ dentry, ++ dentry->d_name.name, ++ bindex); ++ if (!lower_dentry || IS_ERR(lower_dentry)) { ++ int ret = PTR_ERR(lower_dentry); ++ if (!IS_COPYUP_ERR(ret)) ++ printk(KERN_ERR ++ "unionfs: create_parents for " ++ "whiteout failed: bindex=%d " ++ "err=%d\n", bindex, ret); ++ continue; ++ } ++ } ++ ++ lower_wh_dentry = ++ lookup_lck_len(name, lower_dentry->d_parent, ++ dentry->d_name.len + UNIONFS_WHLEN); ++ if (IS_ERR(lower_wh_dentry)) ++ continue; ++ ++ /* ++ * The whiteout already exists. This used to be impossible, ++ * but now is possible because of opaqueness. ++ */ ++ if (lower_wh_dentry->d_inode) { ++ dput(lower_wh_dentry); ++ err = 0; ++ goto out; ++ } ++ ++ err = init_lower_nd(&nd, LOOKUP_CREATE); ++ if (unlikely(err < 0)) ++ goto out; ++ lower_dir_dentry = lock_parent_wh(lower_wh_dentry); ++ err = is_robranch_super(dentry->d_sb, bindex); ++ if (!err) ++ err = vfs_create(lower_dir_dentry->d_inode, ++ lower_wh_dentry, ++ current_umask() & S_IRUGO, ++ &nd); ++ unlock_dir(lower_dir_dentry); ++ dput(lower_wh_dentry); ++ release_lower_nd(&nd, err); ++ ++ if (!err || !IS_COPYUP_ERR(err)) ++ break; ++ } ++ ++ /* set dbopaque so that lookup will not proceed after this branch */ ++ if (!err) ++ dbopaque(dentry) = bindex; ++ ++out: ++ kfree(name); ++ return err; ++} ++ ++/* ++ * Delete all of the whiteouts in a given directory for rmdir. ++ * ++ * lower directory inode should be locked ++ */ ++static int do_delete_whiteouts(struct dentry *dentry, int bindex, ++ struct unionfs_dir_state *namelist) ++{ ++ int err = 0; ++ struct dentry *lower_dir_dentry = NULL; ++ struct dentry *lower_dentry; ++ char *name = NULL, *p; ++ struct inode *lower_dir; ++ int i; ++ struct list_head *pos; ++ struct filldir_node *cursor; ++ ++ /* Find out lower parent dentry */ ++ lower_dir_dentry = unionfs_lower_dentry_idx(dentry, bindex); ++ BUG_ON(!S_ISDIR(lower_dir_dentry->d_inode->i_mode)); ++ lower_dir = lower_dir_dentry->d_inode; ++ BUG_ON(!S_ISDIR(lower_dir->i_mode)); ++ ++ err = -ENOMEM; ++ name = __getname(); ++ if (unlikely(!name)) ++ goto out; ++ strcpy(name, UNIONFS_WHPFX); ++ p = name + UNIONFS_WHLEN; ++ ++ err = 0; ++ for (i = 0; !err && i < namelist->size; i++) { ++ list_for_each(pos, &namelist->list[i]) { ++ cursor = ++ list_entry(pos, struct filldir_node, ++ file_list); ++ /* Only operate on whiteouts in this branch. */ ++ if (cursor->bindex != bindex) ++ continue; ++ if (!cursor->whiteout) ++ continue; ++ ++ strlcpy(p, cursor->name, PATH_MAX - UNIONFS_WHLEN); ++ lower_dentry = ++ lookup_lck_len(name, lower_dir_dentry, ++ cursor->namelen + ++ UNIONFS_WHLEN); ++ if (IS_ERR(lower_dentry)) { ++ err = PTR_ERR(lower_dentry); ++ break; ++ } ++ if (lower_dentry->d_inode) ++ err = vfs_unlink(lower_dir, lower_dentry); ++ dput(lower_dentry); ++ if (err) ++ break; ++ } ++ } ++ ++ __putname(name); ++ ++ /* After all of the removals, we should copy the attributes once. */ ++ fsstack_copy_attr_times(dentry->d_inode, lower_dir_dentry->d_inode); ++ ++out: ++ return err; ++} ++ ++ ++void __delete_whiteouts(struct work_struct *work) ++{ ++ struct sioq_args *args = container_of(work, struct sioq_args, work); ++ struct deletewh_args *d = &args->deletewh; ++ ++ args->err = do_delete_whiteouts(d->dentry, d->bindex, d->namelist); ++ complete(&args->comp); ++} ++ ++/* delete whiteouts in a dir (for rmdir operation) using sioq if necessary */ ++int delete_whiteouts(struct dentry *dentry, int bindex, ++ struct unionfs_dir_state *namelist) ++{ ++ int err; ++ struct super_block *sb; ++ struct dentry *lower_dir_dentry; ++ struct inode *lower_dir; ++ struct sioq_args args; ++ ++ sb = dentry->d_sb; ++ ++ BUG_ON(!S_ISDIR(dentry->d_inode->i_mode)); ++ BUG_ON(bindex < dbstart(dentry)); ++ BUG_ON(bindex > dbend(dentry)); ++ err = is_robranch_super(sb, bindex); ++ if (err) ++ goto out; ++ ++ lower_dir_dentry = unionfs_lower_dentry_idx(dentry, bindex); ++ BUG_ON(!S_ISDIR(lower_dir_dentry->d_inode->i_mode)); ++ lower_dir = lower_dir_dentry->d_inode; ++ BUG_ON(!S_ISDIR(lower_dir->i_mode)); ++ ++ if (!inode_permission(lower_dir, MAY_WRITE | MAY_EXEC)) { ++ err = do_delete_whiteouts(dentry, bindex, namelist); ++ } else { ++ args.deletewh.namelist = namelist; ++ args.deletewh.dentry = dentry; ++ args.deletewh.bindex = bindex; ++ run_sioq(__delete_whiteouts, &args); ++ err = args.err; ++ } ++ ++out: ++ return err; ++} ++ ++/**************************************************************************** ++ * Opaque directory helpers * ++ ****************************************************************************/ ++ ++/* ++ * is_opaque_dir: returns 0 if it is NOT an opaque dir, 1 if it is, and ++ * -errno if an error occurred trying to figure this out. ++ */ ++int is_opaque_dir(struct dentry *dentry, int bindex) ++{ ++ int err = 0; ++ struct dentry *lower_dentry; ++ struct dentry *wh_lower_dentry; ++ struct inode *lower_inode; ++ struct sioq_args args; ++ ++ lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); ++ lower_inode = lower_dentry->d_inode; ++ ++ BUG_ON(!S_ISDIR(lower_inode->i_mode)); ++ ++ mutex_lock(&lower_inode->i_mutex); ++ ++ if (!inode_permission(lower_inode, MAY_EXEC)) { ++ wh_lower_dentry = ++ lookup_one_len(UNIONFS_DIR_OPAQUE, lower_dentry, ++ sizeof(UNIONFS_DIR_OPAQUE) - 1); ++ } else { ++ args.is_opaque.dentry = lower_dentry; ++ run_sioq(__is_opaque_dir, &args); ++ wh_lower_dentry = args.ret; ++ } ++ ++ mutex_unlock(&lower_inode->i_mutex); ++ ++ if (IS_ERR(wh_lower_dentry)) { ++ err = PTR_ERR(wh_lower_dentry); ++ goto out; ++ } ++ ++ /* This is an opaque dir iff wh_lower_dentry is positive */ ++ err = !!wh_lower_dentry->d_inode; ++ ++ dput(wh_lower_dentry); ++out: ++ return err; ++} ++ ++void __is_opaque_dir(struct work_struct *work) ++{ ++ struct sioq_args *args = container_of(work, struct sioq_args, work); ++ ++ args->ret = lookup_one_len(UNIONFS_DIR_OPAQUE, args->is_opaque.dentry, ++ sizeof(UNIONFS_DIR_OPAQUE) - 1); ++ complete(&args->comp); ++} ++ ++int make_dir_opaque(struct dentry *dentry, int bindex) ++{ ++ int err = 0; ++ struct dentry *lower_dentry, *diropq; ++ struct inode *lower_dir; ++ struct nameidata nd; ++ const struct cred *old_creds; ++ struct cred *new_creds; ++ ++ /* ++ * Opaque directory whiteout markers are special files (like regular ++ * whiteouts), and should appear to the users as if they don't ++ * exist. They should be created/deleted regardless of directory ++ * search/create permissions, but only for the duration of this ++ * creation of the .wh.__dir_opaque: file. Note, this does not ++ * circumvent normal ->permission). ++ */ ++ new_creds = prepare_creds(); ++ if (unlikely(!new_creds)) { ++ err = -ENOMEM; ++ goto out_err; ++ } ++ cap_raise(new_creds->cap_effective, CAP_DAC_READ_SEARCH); ++ cap_raise(new_creds->cap_effective, CAP_DAC_OVERRIDE); ++ old_creds = override_creds(new_creds); ++ ++ lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); ++ lower_dir = lower_dentry->d_inode; ++ BUG_ON(!S_ISDIR(dentry->d_inode->i_mode) || ++ !S_ISDIR(lower_dir->i_mode)); ++ ++ mutex_lock(&lower_dir->i_mutex); ++ diropq = lookup_one_len(UNIONFS_DIR_OPAQUE, lower_dentry, ++ sizeof(UNIONFS_DIR_OPAQUE) - 1); ++ if (IS_ERR(diropq)) { ++ err = PTR_ERR(diropq); ++ goto out; ++ } ++ ++ err = init_lower_nd(&nd, LOOKUP_CREATE); ++ if (unlikely(err < 0)) ++ goto out; ++ if (!diropq->d_inode) ++ err = vfs_create(lower_dir, diropq, S_IRUGO, &nd); ++ if (!err) ++ dbopaque(dentry) = bindex; ++ release_lower_nd(&nd, err); ++ ++ dput(diropq); ++ ++out: ++ mutex_unlock(&lower_dir->i_mutex); ++ revert_creds(old_creds); ++out_err: ++ return err; ++} +diff --git a/fs/unionfs/xattr.c b/fs/unionfs/xattr.c +new file mode 100644 +index 0000000..9002e06 +--- /dev/null ++++ b/fs/unionfs/xattr.c +@@ -0,0 +1,173 @@ ++/* ++ * Copyright (c) 2003-2010 Erez Zadok ++ * Copyright (c) 2003-2006 Charles P. Wright ++ * Copyright (c) 2005-2007 Josef 'Jeff' Sipek ++ * Copyright (c) 2005-2006 Junjiro Okajima ++ * Copyright (c) 2005 Arun M. Krishnakumar ++ * Copyright (c) 2004-2006 David P. Quigley ++ * Copyright (c) 2003-2004 Mohammad Nayyer Zubair ++ * Copyright (c) 2003 Puja Gupta ++ * Copyright (c) 2003 Harikesavan Krishnan ++ * Copyright (c) 2003-2010 Stony Brook University ++ * Copyright (c) 2003-2010 The Research Foundation of SUNY ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "union.h" ++ ++/* This is lifted from fs/xattr.c */ ++void *unionfs_xattr_alloc(size_t size, size_t limit) ++{ ++ void *ptr; ++ ++ if (size > limit) ++ return ERR_PTR(-E2BIG); ++ ++ if (!size) /* size request, no buffer is needed */ ++ return NULL; ++ ++ ptr = kmalloc(size, GFP_KERNEL); ++ if (unlikely(!ptr)) ++ return ERR_PTR(-ENOMEM); ++ return ptr; ++} ++ ++/* ++ * BKL held by caller. ++ * dentry->d_inode->i_mutex locked ++ */ ++ssize_t unionfs_getxattr(struct dentry *dentry, const char *name, void *value, ++ size_t size) ++{ ++ struct dentry *lower_dentry = NULL; ++ struct dentry *parent; ++ int err = -EOPNOTSUPP; ++ bool valid; ++ ++ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); ++ parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); ++ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); ++ ++ valid = __unionfs_d_revalidate(dentry, parent, false); ++ if (unlikely(!valid)) { ++ err = -ESTALE; ++ goto out; ++ } ++ ++ lower_dentry = unionfs_lower_dentry(dentry); ++ ++ err = vfs_getxattr(lower_dentry, (char *) name, value, size); ++ ++out: ++ unionfs_check_dentry(dentry); ++ unionfs_unlock_dentry(dentry); ++ unionfs_unlock_parent(dentry, parent); ++ unionfs_read_unlock(dentry->d_sb); ++ return err; ++} ++ ++/* ++ * BKL held by caller. ++ * dentry->d_inode->i_mutex locked ++ */ ++int unionfs_setxattr(struct dentry *dentry, const char *name, ++ const void *value, size_t size, int flags) ++{ ++ struct dentry *lower_dentry = NULL; ++ struct dentry *parent; ++ int err = -EOPNOTSUPP; ++ bool valid; ++ ++ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); ++ parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); ++ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); ++ ++ valid = __unionfs_d_revalidate(dentry, parent, false); ++ if (unlikely(!valid)) { ++ err = -ESTALE; ++ goto out; ++ } ++ ++ lower_dentry = unionfs_lower_dentry(dentry); ++ ++ err = vfs_setxattr(lower_dentry, (char *) name, (void *) value, ++ size, flags); ++ ++out: ++ unionfs_check_dentry(dentry); ++ unionfs_unlock_dentry(dentry); ++ unionfs_unlock_parent(dentry, parent); ++ unionfs_read_unlock(dentry->d_sb); ++ return err; ++} ++ ++/* ++ * BKL held by caller. ++ * dentry->d_inode->i_mutex locked ++ */ ++int unionfs_removexattr(struct dentry *dentry, const char *name) ++{ ++ struct dentry *lower_dentry = NULL; ++ struct dentry *parent; ++ int err = -EOPNOTSUPP; ++ bool valid; ++ ++ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); ++ parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); ++ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); ++ ++ valid = __unionfs_d_revalidate(dentry, parent, false); ++ if (unlikely(!valid)) { ++ err = -ESTALE; ++ goto out; ++ } ++ ++ lower_dentry = unionfs_lower_dentry(dentry); ++ ++ err = vfs_removexattr(lower_dentry, (char *) name); ++ ++out: ++ unionfs_check_dentry(dentry); ++ unionfs_unlock_dentry(dentry); ++ unionfs_unlock_parent(dentry, parent); ++ unionfs_read_unlock(dentry->d_sb); ++ return err; ++} ++ ++/* ++ * BKL held by caller. ++ * dentry->d_inode->i_mutex locked ++ */ ++ssize_t unionfs_listxattr(struct dentry *dentry, char *list, size_t size) ++{ ++ struct dentry *lower_dentry = NULL; ++ struct dentry *parent; ++ int err = -EOPNOTSUPP; ++ char *encoded_list = NULL; ++ bool valid; ++ ++ unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); ++ parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); ++ unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); ++ ++ valid = __unionfs_d_revalidate(dentry, parent, false); ++ if (unlikely(!valid)) { ++ err = -ESTALE; ++ goto out; ++ } ++ ++ lower_dentry = unionfs_lower_dentry(dentry); ++ ++ encoded_list = list; ++ err = vfs_listxattr(lower_dentry, encoded_list, size); ++ ++out: ++ unionfs_check_dentry(dentry); ++ unionfs_unlock_dentry(dentry); ++ unionfs_unlock_parent(dentry, parent); ++ unionfs_read_unlock(dentry->d_sb); ++ return err; ++} +diff --git a/include/linux/fs_stack.h b/include/linux/fs_stack.h +index da317c7..64f1ced 100644 +--- a/include/linux/fs_stack.h ++++ b/include/linux/fs_stack.h +@@ -1,7 +1,19 @@ ++/* ++ * Copyright (c) 2006-2009 Erez Zadok ++ * Copyright (c) 2006-2007 Josef 'Jeff' Sipek ++ * Copyright (c) 2006-2009 Stony Brook University ++ * Copyright (c) 2006-2009 The Research Foundation of SUNY ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ + #ifndef _LINUX_FS_STACK_H + #define _LINUX_FS_STACK_H + +-/* This file defines generic functions used primarily by stackable ++/* ++ * This file defines generic functions used primarily by stackable + * filesystems; none of these functions require i_mutex to be held. + */ + +diff --git a/include/linux/magic.h b/include/linux/magic.h +index eb9800f..9770154 100644 +--- a/include/linux/magic.h ++++ b/include/linux/magic.h +@@ -47,6 +47,8 @@ + #define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs" + #define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs" + ++#define UNIONFS_SUPER_MAGIC 0xf15f083d ++ + #define SMB_SUPER_MAGIC 0x517B + #define USBDEVICE_SUPER_MAGIC 0x9fa2 + #define CGROUP_SUPER_MAGIC 0x27e0eb +diff --git a/include/linux/namei.h b/include/linux/namei.h +index 05b441d..dca6f9a 100644 +--- a/include/linux/namei.h ++++ b/include/linux/namei.h +@@ -72,6 +72,7 @@ extern int vfs_path_lookup(struct dentry *, struct vfsmount *, + + extern struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry *dentry, + int (*open)(struct inode *, struct file *)); ++extern void release_open_intent(struct nameidata *); + + extern struct dentry *lookup_one_len(const char *, struct dentry *, int); + +diff --git a/include/linux/splice.h b/include/linux/splice.h +index 997c3b4..54f5501 100644 +--- a/include/linux/splice.h ++++ b/include/linux/splice.h +@@ -81,6 +81,11 @@ extern ssize_t splice_to_pipe(struct pipe_inode_info *, + struct splice_pipe_desc *); + extern ssize_t splice_direct_to_actor(struct file *, struct splice_desc *, + splice_direct_actor *); ++extern long vfs_splice_from(struct pipe_inode_info *pipe, struct file *out, ++ loff_t *ppos, size_t len, unsigned int flags); ++extern long vfs_splice_to(struct file *in, loff_t *ppos, ++ struct pipe_inode_info *pipe, size_t len, ++ unsigned int flags); + + /* + * for dynamic pipe sizing +diff --git a/include/linux/union_fs.h b/include/linux/union_fs.h +new file mode 100644 +index 0000000..c84d97e +--- /dev/null ++++ b/include/linux/union_fs.h +@@ -0,0 +1,22 @@ ++/* ++ * Copyright (c) 2003-2009 Erez Zadok ++ * Copyright (c) 2005-2007 Josef 'Jeff' Sipek ++ * Copyright (c) 2003-2009 Stony Brook University ++ * Copyright (c) 2003-2009 The Research Foundation of SUNY ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef _LINUX_UNION_FS_H ++#define _LINUX_UNION_FS_H ++ ++/* ++ * DEFINITIONS FOR USER AND KERNEL CODE: ++ */ ++# define UNIONFS_IOCTL_INCGEN _IOR(0x15, 11, int) ++# define UNIONFS_IOCTL_QUERYFILE _IOR(0x15, 15, int) ++ ++#endif /* _LINUX_UNIONFS_H */ ++ +diff --git a/security/security.c b/security/security.c +index 351942a..69505f7 100644 +--- a/security/security.c ++++ b/security/security.c +@@ -529,6 +529,7 @@ int security_inode_permission(struct inode *inode, int mask) + return 0; + return security_ops->inode_permission(inode, mask); + } ++EXPORT_SYMBOL(security_inode_permission); + + int security_inode_setattr(struct dentry *dentry, struct iattr *attr) + { diff --git a/main/make/APKBUILD b/main/make/APKBUILD index e81b32920..08634217b 100644 --- a/main/make/APKBUILD +++ b/main/make/APKBUILD @@ -1,6 +1,6 @@ # Maintainer: Natanael Copa <ncopa@alpinelinux.org> pkgname=make -pkgver=3.82 +pkgver=3.81 pkgrel=0 pkgdesc="GNU make utility to maintain groups of programs" url="http://www.gnu.org/software/make" @@ -24,4 +24,4 @@ package() { make DESTDIR="$pkgdir" install } -md5sums="7f7c000e3b30c6840f2e9cf86b254fac make-3.82.tar.gz" +md5sums="a4e9494ac6dc3f6b0c5ff75c5d52abba make-3.81.tar.gz" diff --git a/main/mesa/APKBUILD b/main/mesa/APKBUILD index ebccf5c7a..4fc56ec03 100644 --- a/main/mesa/APKBUILD +++ b/main/mesa/APKBUILD @@ -1,34 +1,40 @@ # Maintainer: Natanael Copa <ncopa@alpinelinux.org> pkgname=mesa -pkgver=7.7.1 -pkgrel=2 +pkgver=7.8.2 +pkgrel=0 pkgdesc="Mesa DRI OpenGL library" url="http://www.mesa3d.org" license="LGPL" depends= +install="$pkgname.post-install $pkgname.post-deinstall" subpackages="$pkgname-dev - $pkgname-dri-ati:ati $pkgname-dri-ffb:ffb $pkgname-dri-intel:intel - $pkgname-dri-mach64:mach64 $pkgname-dri-mga:mga $pkgname-dri-r128:r128 - $pkgname-dri-savage:savage $pkgname-dri-sis:sis - $pkgname-dri-tdfx:tdfx $pkgname-dri-unichrome:unichrome + $pkgname-dri-intel:intel + $pkgname-dri-mach64:mach64 + $pkgname-dri-mga:mga + $pkgname-dri-r128:r128 + $pkgname-dri-savage:savage + $pkgname-dri-sis:sis + $pkgname-dri-tdfx:tdfx + $pkgname-dri-unichrome:unichrome + $pkgname-dri-ati:ati + $pkgname-dri-swrast:swrast " makedepends="pkgconfig libdrm-dev libxxf86vm-dev libxdamage-dev expat-dev - dri2proto xextproto libx11-dev glproto python" + dri2proto xextproto libx11-dev glproto python libxt-dev" source="ftp://ftp.freedesktop.org/pub/mesa/$pkgver/MesaLib-$pkgver.tar.bz2 - mesa-7.7-link-shared.patch - intel-revert-vbl.patch - mesa-7.4-parallel.patch - mesa-7.6.1-ldflags.patch - mesa-7.6.1-uclibc.patch" + mesa-7.6.1-uclibc.patch + mesa-7.8-git.patch" depends_dev="libdrm-dev dri2proto libx11-dev libxext-dev libxxf86vm-dev libxdamage-dev libxfixes-dev libxcb-dev" _dri_driverdir=/usr/lib/xorg/modules/dri +_builddir="$srcdir/Mesa-$pkgver" + prepare() { - cd "$srcdir"/Mesa-$pkgver + cd "$_builddir" for i in ../*.patch; do msg "Applying $i..." patch -p1 -i $i || return 1 @@ -36,7 +42,7 @@ prepare() { } build() { - cd "$srcdir"/Mesa-$pkgver + cd "$_builddir" export LDFLAGS="$LDFLAGS -Wl,-z,lazy" ./configure --prefix=/usr \ --with-dri-driverdir=$_dri_driverdir \ @@ -49,21 +55,26 @@ build() { --disable-glw || return 1 make || return 1 + # check so we dont have any bind NOW scanelf -Rb . | grep NOW && return 1 return 0 } package() { - cd "$srcdir"/Mesa-$pkgver + cd "$_builddir" make -j1 DESTDIR="$pkgdir" install || return 1 + # remove symlink and recreate it inside install + # this makes it possible to switch libgl like with nvidia + cd "$pkgdir"/usr/lib/ + mv libGL.so.1.2 libGL.mesa.so.1.2 || return 1 + rm libGL.so.1 && ln -s libGL.mesa.so.1.2 libGL.so.1 || return 1 } - _mv_dri() { pkgdesc="Mesa DRI driver for $@" install -d "$subpkgdir"/$_dri_driverdir - + while [ $# -gt 0 ]; do mv "$pkgdir"/$_dri_driverdir/${1}.so \ "$subpkgdir"/$_dri_driverdir/ || return 1 @@ -72,21 +83,16 @@ _mv_dri() { } ati() { _mv_dri radeon_dri r200_dri r300_dri r600_dri; } -ffb() { _mv_dri ffb_dri; } intel() { _mv_dri i810_dri i915_dri i965_dri; } mach64() { _mv_dri mach64_dri; } mga() { _mv_dri mga_dri; } r128() { _mv_dri r128_dri; } -s3v() { _mv_dri s3v_dri; } savage() { _mv_dri savage_dri; } sis() { _mv_dri sis_dri; } tdfx() { _mv_dri tdfx_dri; } -trident() { _mv_dri trident_dri; } unichrome() { _mv_dri unichrome_dri; } +swrast() { _mv_dri swrast_dri; } -md5sums="46664d99e03f1e3ac078a7fea02af115 MesaLib-7.7.1.tar.bz2 -a966f459b6430dbe87a57c5f28f19816 mesa-7.7-link-shared.patch -a111f4dc82e894f8801bc3fa129af7af intel-revert-vbl.patch -75e1bb69f384e9d60544fa03c15cc0ec mesa-7.4-parallel.patch -eb6bb53bb1643782aa572edc40d28629 mesa-7.6.1-ldflags.patch -8d98e15310e0f2e1520beb9e6cb6ab41 mesa-7.6.1-uclibc.patch" +md5sums="6be2d343a0089bfd395ce02aaf8adb57 MesaLib-7.8.2.tar.bz2 +8d98e15310e0f2e1520beb9e6cb6ab41 mesa-7.6.1-uclibc.patch +397c1249edcf03227697fef3f7129fe4 mesa-7.8-git.patch" diff --git a/main/mesa/fix-glx1.3-crash-xorg19.patch b/main/mesa/fix-glx1.3-crash-xorg19.patch new file mode 100644 index 000000000..bd09b9e71 --- /dev/null +++ b/main/mesa/fix-glx1.3-crash-xorg19.patch @@ -0,0 +1,73 @@ +From 8f13c69e7658df3a97e388f210dae175639d6d8d Mon Sep 17 00:00:00 2001 +From: Chris Wilson <chris@chris-wilson.co.uk> +Date: Fri, 16 Jul 2010 12:24:53 +0100 +Subject: [PATCH] intel: Fix invalidate before initialisation + +Fixes: + + Bug 29091 - 1.9RC5 server crash when starting GLX 1.3 app with mesa 7.8 + Intel dri2 driver. + https://bugs.freedesktop.org/show_bug.cgi?id=29091 + +Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> +--- + src/mesa/drivers/dri/common/dri_util.c | 2 +- + src/mesa/drivers/dri/intel/intel_screen.c | 19 +++++++++++++++---- + 2 files changed, 16 insertions(+), 5 deletions(-) + +diff --git a/src/mesa/drivers/dri/common/dri_util.c b/src/mesa/drivers/dri/common/dri_util.c +index 75c9882..9a9bfed 100644 +--- a/src/mesa/drivers/dri/common/dri_util.c ++++ b/src/mesa/drivers/dri/common/dri_util.c +@@ -432,7 +432,7 @@ driCreateNewDrawable(__DRIscreen *psp, const __DRIconfig *config, + */ + (void) attrs; + +- pdp = malloc(sizeof *pdp); ++ pdp = calloc(1, sizeof *pdp); + if (!pdp) { + return NULL; + } +diff --git a/src/mesa/drivers/dri/intel/intel_screen.c b/src/mesa/drivers/dri/intel/intel_screen.c +index 6e4bb64..083b7bb 100644 +--- a/src/mesa/drivers/dri/intel/intel_screen.c ++++ b/src/mesa/drivers/dri/intel/intel_screen.c +@@ -102,10 +102,21 @@ static const __DRItexBufferExtension intelTexBufferExtension = { + intelSetTexBuffer2, + }; + ++static inline struct intel_context * ++to_intel_context(__DRIdrawable *drawable) ++{ ++ if (drawable->driContextPriv == NULL) ++ return NULL; ++ ++ return drawable->driContextPriv->driverPrivate; ++} ++ + static void + intelDRI2Flush(__DRIdrawable *drawable) + { +- struct intel_context *intel = drawable->driContextPriv->driverPrivate; ++ struct intel_context *intel = to_intel_context(drawable); ++ if (!intel) ++ return; + + if (intel->gen < 4) + INTEL_FIREVERTICES(intel); +@@ -117,9 +128,9 @@ intelDRI2Flush(__DRIdrawable *drawable) + static void + intelDRI2Invalidate(__DRIdrawable *drawable) + { +- struct intel_context *intel = drawable->driContextPriv->driverPrivate; +- +- intel->using_dri2_swapbuffers = GL_TRUE; ++ struct intel_context *intel = to_intel_context(drawable); ++ if (intel) ++ intel->using_dri2_swapbuffers = GL_TRUE; + dri2InvalidateDrawable(drawable); + } + +-- +1.7.1 + diff --git a/main/mesa/intel-revert-vbl.patch b/main/mesa/intel-revert-vbl.patch deleted file mode 100644 index 039441473..000000000 --- a/main/mesa/intel-revert-vbl.patch +++ /dev/null @@ -1,21 +0,0 @@ -commit 532d2051245a1d8afe7ca236f1d966d555bb121a -Author: Dave Airlie <airlied@linux.ie> -Date: Fri Sep 12 17:21:25 2008 +1000 - - Revert "intel: sync to vblank by default" - - This reverts commit e9bf3e4cc9a7e4bcd4c45bd707541d26ecdf0409. - -diff --git a/src/mesa/drivers/dri/intel/intel_screen.c b/src/mesa/drivers/dri/intel/intel_screen.c -index c193830..f02192d 100644 ---- a/src/mesa/drivers/dri/intel/intel_screen.c -+++ b/src/mesa/drivers/dri/intel/intel_screen.c -@@ -55,7 +55,7 @@ PUBLIC const char __driConfigOptions[] = - DRI_CONF_BEGIN - DRI_CONF_SECTION_PERFORMANCE - DRI_CONF_FTHROTTLE_MODE(DRI_CONF_FTHROTTLE_IRQS) -- DRI_CONF_VBLANK_MODE(DRI_CONF_VBLANK_ALWAYS_SYNC) -+ DRI_CONF_VBLANK_MODE(DRI_CONF_VBLANK_DEF_INTERVAL_0) - /* Options correspond to DRI_CONF_BO_REUSE_DISABLED, - * DRI_CONF_BO_REUSE_ALL - */ diff --git a/main/mesa/mesa-7.4-parallel.patch b/main/mesa/mesa-7.4-parallel.patch deleted file mode 100644 index b4e37049d..000000000 --- a/main/mesa/mesa-7.4-parallel.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -ru Mesa-7.4.orig/src/mesa/drivers/dri/Makefile Mesa-7.4/src/mesa/drivers/dri/Makefile ---- Mesa-7.4.orig/src/mesa/drivers/dri/Makefile 2009-04-11 18:08:41.000000000 +0000 -+++ Mesa-7.4/src/mesa/drivers/dri/Makefile 2009-04-11 18:10:09.000000000 +0000 -@@ -18,7 +18,7 @@ - $(TOP)/$(LIB_DIR)/libdricore.so: $(TOP)/$(LIB_DIR) libdricore.so - $(INSTALL) libdricore.so $(TOP)/$(LIB_DIR) - --subdirs: -+subdirs: $(TOP)/$(LIB_DIR)/libdricore.so - @for dir in $(DRI_DIRS) ; do \ - if [ -d $$dir ] ; then \ - (cd $$dir && $(MAKE)) || exit 1 ; \ diff --git a/main/mesa/mesa-7.6.1-ldflags.patch b/main/mesa/mesa-7.6.1-ldflags.patch deleted file mode 100644 index 56914b711..000000000 --- a/main/mesa/mesa-7.6.1-ldflags.patch +++ /dev/null @@ -1,20 +0,0 @@ -diff --git a/src/gallium/winsys/drm/Makefile.template b/src/gallium/winsys/drm/Makefile.template -index 9635c3c..88e02c0 100644 ---- a/src/gallium/winsys/drm/Makefile.template -+++ b/src/gallium/winsys/drm/Makefile.template -@@ -83,6 +83,7 @@ default: depend symlinks $(TOP)/$(LIB_DIR)/gallium/$(LIBNAME) - - $(LIBNAME): $(OBJECTS) $(MESA_MODULES) $(PIPE_DRIVERS) $(WINOBJ) Makefile $(TOP)/src/mesa/drivers/dri/Makefile.template - $(MKLIB) -noprefix -o $@ \ -+ -ldflags "$(LDFLAGS)" \ - $(OBJECTS) $(PIPE_DRIVERS) \ - -Wl,--start-group $(MESA_MODULES) -Wl,--end-group \ - $(WINOBJ) $(DRI_LIB_DEPS) $(DRIVER_EXTRAS) -@@ -90,6 +91,7 @@ $(LIBNAME): $(OBJECTS) $(MESA_MODULES) $(PIPE_DRIVERS) $(WINOBJ) Makefile $(TOP) - $(LIBNAME_EGL): $(WINSYS_OBJECTS) $(LIBS) - $(MKLIB) -o $(LIBNAME_EGL) \ - -linker "$(CC)" \ -+ -ldflags "$(LDFLAGS)" \ - -noprefix \ - $(OBJECTS) $(MKLIB_OPTIONS) $(WINSYS_OBJECTS) $(PIPE_DRIVERS) $(WINOBJ) $(DRI_LIB_DEPS) \ - --whole-archive $(LIBS) $(GALLIUM_AUXILIARIES) --no-whole-archive $(DRIVER_EXTRAS) diff --git a/main/mesa/mesa-7.7-link-shared.patch b/main/mesa/mesa-7.7-link-shared.patch deleted file mode 100644 index 8c6b562d7..000000000 --- a/main/mesa/mesa-7.7-link-shared.patch +++ /dev/null @@ -1,101 +0,0 @@ -diff -up mesa-20091221/src/mesa/drivers/dri/Makefile.da mesa-20091221/src/mesa/drivers/dri/Makefile ---- mesa-20091221/src/mesa/drivers/dri/Makefile.da 2009-12-21 08:09:11.000000000 +1000 -+++ mesa-20091221/src/mesa/drivers/dri/Makefile 2009-12-21 08:43:03.000000000 +1000 -@@ -6,12 +6,17 @@ include $(TOP)/configs/current - - - --default: $(TOP)/$(LIB_DIR) subdirs dri.pc -+default: $(TOP)/$(LIB_DIR) $(TOP)/$(LIB_DIR)/libdricore.so subdirs dri.pc - - - $(TOP)/$(LIB_DIR): - -mkdir $(TOP)/$(LIB_DIR) - -+libdricore.so: -+ gcc -shared -o libdricore.so -Wl,--whole-archive ../../libmesa.a -Wl,--no-whole-archive $(LDFLAGS) -lm -lpthread -lc -+ -+$(TOP)/$(LIB_DIR)/libdricore.so: $(TOP)/$(LIB_DIR) libdricore.so -+ $(INSTALL) libdricore.so $(TOP)/$(LIB_DIR) - - subdirs: - @for dir in $(DRI_DIRS) ; do \ -@@ -32,12 +37,14 @@ dri.pc: dri.pc.in - $(pcedit) $< > $@ - - --install: dri.pc -+install: dri.pc $(TOP)/$(LIB_DIR)/libdricore.so - @for dir in $(DRI_DIRS) ; do \ - if [ -d $$dir ] ; then \ - (cd $$dir && $(MAKE) install) || exit 1 ; \ - fi \ - done -+ $(INSTALL) -d $(DESTDIR)$(DRI_DRIVER_INSTALL_DIR) -+ $(INSTALL) -m 755 $(TOP)/$(LIB_DIR)/libdricore.so $(DESTDIR)$(DRI_DRIVER_INSTALL_DIR) - $(INSTALL) -d $(DESTDIR)$(INSTALL_INC_DIR)/GL/internal - $(INSTALL) -m 0644 $(TOP)/include/GL/internal/dri_interface.h \ - $(DESTDIR)$(INSTALL_INC_DIR)/GL/internal -@@ -51,5 +58,6 @@ clean: - (cd $$dir && $(MAKE) clean) ; \ - fi \ - done -+ -rm -f libdricore.so $(TOP)/$(LIB_DIR)/libdricore.so - -rm -f common/*.o - -rm -f *.pc -diff -up mesa-20091221/src/mesa/drivers/dri/Makefile.template.da mesa-20091221/src/mesa/drivers/dri/Makefile.template ---- mesa-20091221/src/mesa/drivers/dri/Makefile.template.da 2009-12-21 08:09:11.000000000 +1000 -+++ mesa-20091221/src/mesa/drivers/dri/Makefile.template 2009-12-21 08:43:40.000000000 +1000 -@@ -1,6 +1,6 @@ - # -*-makefile-*- - --MESA_MODULES = $(TOP)/src/mesa/libmesa.a -+MESA_MODULES = $(TOP)/$(LIB_DIR)/libdricore.so - - COMMON_GALLIUM_SOURCES = \ - ../common/utils.c \ -@@ -70,7 +70,8 @@ lib: symlinks subdirs depend - $(LIBNAME): $(OBJECTS) $(MESA_MODULES) $(EXTRA_MODULES) $(WINOBJ) Makefile \ - $(TOP)/src/mesa/drivers/dri/Makefile.template - $(MKLIB) -o $@ -noprefix -linker '$(CC)' -ldflags '$(LDFLAGS)' \ -- $(OBJECTS) $(MESA_MODULES) $(EXTRA_MODULES) $(WINOBJ) \ -+ -L$(TOP)/$(LIB_DIR) -Wl,-R$(DRI_DRIVER_INSTALL_DIR) -ldricore \ -+ $(OBJECTS) $(EXTRA_MODULES) $(WINOBJ) \ - $(DRI_LIB_DEPS) - - -diff -up mesa-20091221/src/mesa/x86/read_rgba_span_x86.S.da mesa-20091221/src/mesa/x86/read_rgba_span_x86.S ---- mesa-20091221/src/mesa/x86/read_rgba_span_x86.S.da 2009-12-21 08:09:11.000000000 +1000 -+++ mesa-20091221/src/mesa/x86/read_rgba_span_x86.S 2009-12-21 08:43:03.000000000 +1000 -@@ -77,7 +77,6 @@ - */ - - .globl _generic_read_RGBA_span_BGRA8888_REV_MMX --.hidden _generic_read_RGBA_span_BGRA8888_REV_MMX - .type _generic_read_RGBA_span_BGRA8888_REV_MMX, @function - _generic_read_RGBA_span_BGRA8888_REV_MMX: - pushl %ebx -@@ -172,7 +171,6 @@ _generic_read_RGBA_span_BGRA8888_REV_MMX - */ - - .globl _generic_read_RGBA_span_BGRA8888_REV_SSE --.hidden _generic_read_RGBA_span_BGRA8888_REV_SSE - .type _generic_read_RGBA_span_BGRA8888_REV_SSE, @function - _generic_read_RGBA_span_BGRA8888_REV_SSE: - pushl %esi -@@ -335,7 +333,6 @@ _generic_read_RGBA_span_BGRA8888_REV_SSE - - .text - .globl _generic_read_RGBA_span_BGRA8888_REV_SSE2 --.hidden _generic_read_RGBA_span_BGRA8888_REV_SSE2 - .type _generic_read_RGBA_span_BGRA8888_REV_SSE2, @function - _generic_read_RGBA_span_BGRA8888_REV_SSE2: - pushl %esi -@@ -494,7 +491,6 @@ _generic_read_RGBA_span_BGRA8888_REV_SSE - - .text - .globl _generic_read_RGBA_span_RGB565_MMX -- .hidden _generic_read_RGBA_span_RGB565_MMX - .type _generic_read_RGBA_span_RGB565_MMX, @function - - _generic_read_RGBA_span_RGB565_MMX: diff --git a/main/mesa/mesa-7.8-git.patch b/main/mesa/mesa-7.8-git.patch new file mode 100644 index 000000000..169b38b05 --- /dev/null +++ b/main/mesa/mesa-7.8-git.patch @@ -0,0 +1,2423 @@ +diff --git a/Makefile b/Makefile +index 84d0038..63fdf87 100644 +--- a/Makefile ++++ b/Makefile +@@ -180,7 +180,7 @@ ultrix-gcc: + + # Rules for making release tarballs + +-VERSION=7.8.2 ++VERSION=7.8.3 + DIRECTORY = Mesa-$(VERSION) + LIB_NAME = MesaLib-$(VERSION) + DEMO_NAME = MesaDemos-$(VERSION) +@@ -285,9 +285,6 @@ MAIN_FILES = \ + $(DIRECTORY)/src/mesa/x86-64/*.[chS] \ + $(DIRECTORY)/src/mesa/x86-64/Makefile \ + $(DIRECTORY)/progs/Makefile \ +- $(DIRECTORY)/progs/util/README \ +- $(DIRECTORY)/progs/util/*.[ch] \ +- $(DIRECTORY)/progs/util/sampleMakefile \ + $(DIRECTORY)/windows/VC8/ + + ES_FILES = \ +@@ -438,7 +435,10 @@ DEMO_FILES = \ + $(DIRECTORY)/progs/glsl/*.c \ + $(DIRECTORY)/progs/glsl/*.frag \ + $(DIRECTORY)/progs/glsl/*.vert \ +- $(DIRECTORY)/progs/glsl/*.shtest ++ $(DIRECTORY)/progs/glsl/*.shtest \ ++ $(DIRECTORY)/progs/util/README \ ++ $(DIRECTORY)/progs/util/*.[ch] \ ++ $(DIRECTORY)/progs/util/sampleMakefile + + GLUT_FILES = \ + $(DIRECTORY)/include/GL/glut.h \ +diff --git a/configs/autoconf.in b/configs/autoconf.in +index fbd5faa..6218be9 100644 +--- a/configs/autoconf.in ++++ b/configs/autoconf.in +@@ -26,6 +26,11 @@ INTEL_LIBS = @INTEL_LIBS@ + INTEL_CFLAGS = @INTEL_CFLAGS@ + X11_LIBS = @X11_LIBS@ + X11_CFLAGS = @X11_CFLAGS@ ++GLW_CFLAGS = @GLW_CFLAGS@ ++GLUT_CFLAGS = @GLUT_CFLAGS@ ++ ++# dlopen ++DLOPEN_LIBS = @DLOPEN_LIBS@ + + # Assembler + MESA_ASM_SOURCES = @MESA_ASM_SOURCES@ +diff --git a/configs/default b/configs/default +index f12bec8..d4b45a3 100644 +--- a/configs/default ++++ b/configs/default +@@ -121,6 +121,8 @@ APP_LIB_DEPS = $(EXTRA_LIB_PATH) -L$(TOP)/$(LIB_DIR) -l$(GLUT_LIB) -l$(GLU_LI + APP_LIB_DEPS = -lm + X11_LIBS = -lX11 + ++DLOPEN_LIBS = -ldl ++ + # Installation directories (for make install) + INSTALL_DIR = /usr/local + INSTALL_LIB_DIR = $(INSTALL_DIR)/$(LIB_DIR) +diff --git a/configure.ac b/configure.ac +index e711634..e15371f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -398,6 +398,7 @@ dnl Check to see if dlopen is in default libraries (like Solaris, which + dnl has it in libc), or if libdl is needed to get it. + AC_CHECK_FUNC([dlopen], [], + [AC_CHECK_LIB([dl], [dlopen], [DLOPEN_LIBS="-ldl"])]) ++AC_SUBST([DLOPEN_LIBS]) + + dnl See if posix_memalign is available + AC_CHECK_FUNC([posix_memalign], [DEFINES="$DEFINES -DHAVE_POSIX_MEMALIGN"]) +@@ -699,7 +700,7 @@ AC_SUBST([DRI_DRIVER_SEARCH_DIR]) + dnl Direct rendering or just indirect rendering + AC_ARG_ENABLE([driglx-direct], + [AS_HELP_STRING([--disable-driglx-direct], +- [enable direct rendering in GLX for DRI @<:@default=enabled@:>@])], ++ [enable direct rendering in GLX and EGL for DRI @<:@default=enabled@:>@])], + [driglx_direct="$enableval"], + [driglx_direct="yes"]) + dnl Which drivers to build - default is chosen by platform +@@ -1102,10 +1103,6 @@ fi + + if test "x$enable_glut" = xyes; then + SRC_DIRS="$SRC_DIRS glut/glx" +- GLUT_CFLAGS="" +- if test "x$GCC" = xyes; then +- GLUT_CFLAGS="-fexceptions" +- fi + if test "$x11_pkgconfig" = yes; then + PKG_CHECK_MODULES([GLUT],[x11 xmu xi]) + GLUT_PC_REQ_PRIV="x11 xmu xi" +@@ -1116,6 +1113,9 @@ if test "x$enable_glut" = xyes; then + GLUT_PC_LIB_PRIV="$GLUT_LIB_DEPS" + GLUT_PC_CFLAGS="$X11_INCLUDES" + fi ++ if test "x$GCC" = xyes; then ++ GLUT_CFLAGS="$GLUT_CFLAGS -fexceptions" ++ fi + GLUT_LIB_DEPS="$GLUT_LIB_DEPS -lm" + GLUT_PC_LIB_PRIV="$GLUT_PC_LIB_PRIV -lm" + +@@ -1217,6 +1217,10 @@ yes) + if test "x$enable_egl" != xyes; then + AC_MSG_ERROR([cannot build egl state tracker without EGL library]) + fi ++ # define GLX_DIRECT_RENDERING even when the driver is not dri ++ if test "x$mesa_driver" != xdri -a "x$driglx_direct" = xyes; then ++ DEFINES="$DEFINES -DGLX_DIRECT_RENDERING" ++ fi + ;; + xorg) + PKG_CHECK_MODULES([LIBDRM_XORG], [libdrm >= $LIBDRM_XORG_REQUIRED]) +diff --git a/docs/news.html b/docs/news.html +index 92e2a18..01e2834 100644 +--- a/docs/news.html ++++ b/docs/news.html +@@ -11,6 +11,14 @@ + <H1>News</H1> + + ++<h2>TBD, 2010</h2> ++ ++<p> ++<a href="relnotes-7.8.3.html">Mesa 7.8.3</a> is released. This is a bug-fix ++release collecting fixes since the 7.8.2 release. ++</p> ++ ++ + <h2>June 16, 2010</h2> + + <p> +diff --git a/docs/relnotes-7.8.2.html b/docs/relnotes-7.8.2.html +index 4d7758d..1393b2e 100644 +--- a/docs/relnotes-7.8.2.html ++++ b/docs/relnotes-7.8.2.html +@@ -142,7 +142,5 @@ a471807b65e49c325808ba4551be93ed MesaGLUT-7.8.2.tar.bz2 + </ul> + + +-<h2>Changes</h2> +-<p>None.</p> + </body> + </html> +diff --git a/docs/relnotes-7.8.3.html b/docs/relnotes-7.8.3.html +new file mode 100644 +index 0000000..1e9f433 +--- /dev/null ++++ b/docs/relnotes-7.8.3.html +@@ -0,0 +1,89 @@ ++<HTML> ++ ++<TITLE>Mesa Release Notes</TITLE> ++ ++<head><link rel="stylesheet" type="text/css" href="mesa.css"></head> ++ ++<BODY> ++ ++<body bgcolor="#eeeeee"> ++ ++<H1>Mesa 7.8.3 Release Notes / (date tbd)</H1> ++ ++<p> ++Mesa 7.8.3 is a bug fix release which fixes bugs found since the 7.8.2 release. ++</p> ++<p> ++Mesa 7.8.3 implements the OpenGL 2.1 API, but the version reported by ++glGetString(GL_VERSION) depends on the particular driver being used. ++Some drivers don't support all the features required in OpenGL 2.1. ++</p> ++<p> ++See the <a href="install.html">Compiling/Installing page</a> for prerequisites ++for DRI hardware acceleration. ++</p> ++ ++ ++<h2>MD5 checksums</h2> ++<pre> ++x MesaLib-7.8.3.tar.gz ++x MesaLib-7.8.3.tar.bz2 ++x MesaLib-7.8.3.zip ++x MesaDemos-7.8.3.tar.gz ++x MesaDemos-7.8.3.tar.bz2 ++x MesaDemos-7.8.3.zip ++x MesaGLUT-7.8.3.tar.gz ++x MesaGLUT-7.8.3.tar.bz2 ++x MesaGLUT-7.8.3.zip ++</pre> ++ ++ ++<h2>New features</h2> ++<p>None.</p> ++ ++ ++<h2>Changes</h2> ++<ul> ++<li>The radeon driver should use less memory when searching for a valid mip ++image.</li> ++</ul> ++ ++ ++<h2>Bug fixes</h2> ++<ul> ++<li>Fix unsupported FB with D24S8 (bug ++ <a href="https://bugs.freedesktop.org/show_bug.cgi?id=23670">29116</a>)</li> ++<li>Fix ReadPixels crash when reading depth/stencil from an FBO</li> ++<li>Fixed a bug rendering to 16-bit buffers using swrast.</li> ++<li>Fixed a state tracker/TGSI bug that caused crashes when using Windows' ++ memory debugging features.</li> ++<li>Fixed an issue rendering to 32-bit channels with swrast (bug ++ <a href="https://bugs.freedesktop.org/show_bug.cgi?id=29487">29487</a>)</li> ++<li>GLSL: fix indirect <TT>gl_TextureMatrix</TT> addressing (bug ++ <a href="https://bugs.freedesktop.org/show_bug.cgi?id=28967">28967</a>)</li> ++<li>GLSL: fix for bug ++ <a href="https://bugs.freedesktop.org/show_bug.cgi?id=27216">27216</a></li> ++<li>GLSL: fix zw fragcoord entries in some cases (bug ++ <a href="https://bugs.freedesktop.org/show_bug.cgi?id=29183">29183</a>)</li> ++<li>Fix texture env generation in some cases (bug ++ <a href="https://bugs.freedesktop.org/show_bug.cgi?id=28169">28169</a>)</li> ++<li>osmesa: a fix for calling <TT>OSMesaMakeCurrent</TT> twice was applied (bug ++ <a href="https://bugs.freedesktop.org/show_bug.cgi?id=10966">10966</a></li> ++<li>A bug was fixed which could cause Mesa to ignore the ++ <TT>MESA_EXTENSION_OVERRIDE</TT> environment variable.</li> ++<li>A bug related to specular highlights on backfaces was fixed.</li> ++<li>A radeon-specific issue with <TT>glCopyTex(Sub)Image</TT> was ++ corrected.</li> ++<li>radeon/wine: flush command stream in more cases, fixing wine d3d9 ++ tests.</li> ++<li>r600: fix sin+cos normalization.</li> ++<li>r600: (properly) ignore <TT>GL_COORD_REPLACE</TT> when point sprites are ++ disabled.</li> ++<li>radeon: avoid flushing when the context is not current.</li> ++<li>r300c: a bug affecting unaligned BOs was fixed.</li> ++<li>r300c: a hardlock caused by ARB_half_float_vertex incorrectly advertised on some chipsets.</li> ++</ul> ++ ++ ++</body> ++</html> +diff --git a/docs/relnotes.html b/docs/relnotes.html +index 39b02b8..46f6469 100644 +--- a/docs/relnotes.html ++++ b/docs/relnotes.html +@@ -13,6 +13,7 @@ The release notes summarize what's new or changed in each Mesa release. + </p> + + <UL> ++<LI><A HREF="relnotes-7.8.3.html">7.8.3 release notes</A> + <LI><A HREF="relnotes-7.8.2.html">7.8.2 release notes</A> + <LI><A HREF="relnotes-7.8.1.html">7.8.1 release notes</A> + <LI><A HREF="relnotes-7.8.html">7.8 release notes</A> +diff --git a/include/GL/gl_mangle.h b/include/GL/gl_mangle.h +index 43d2e89..15589d8 100644 +--- a/include/GL/gl_mangle.h ++++ b/include/GL/gl_mangle.h +@@ -56,6 +56,7 @@ + #define glBeginOcclusionQueryNV MANGLE(BeginOcclusionQueryNV) + #define glBeginPerfMonitorAMD MANGLE(BeginPerfMonitorAMD) + #define glBeginQueryARB MANGLE(BeginQueryARB) ++#define glBeginQueryIndexed MANGLE(BeginQueryIndexed) + #define glBeginQuery MANGLE(BeginQuery) + #define glBeginTransformFeedbackEXT MANGLE(BeginTransformFeedbackEXT) + #define glBeginTransformFeedback MANGLE(BeginTransformFeedback) +@@ -75,6 +76,7 @@ + #define glBindBufferRange MANGLE(BindBufferRange) + #define glBindBufferRangeNV MANGLE(BindBufferRangeNV) + #define glBindFragDataLocationEXT MANGLE(BindFragDataLocationEXT) ++#define glBindFragDataLocationIndexed MANGLE(BindFragDataLocationIndexed) + #define glBindFragDataLocation MANGLE(BindFragDataLocation) + #define glBindFragmentShaderATI MANGLE(BindFragmentShaderATI) + #define glBindFramebufferEXT MANGLE(BindFramebufferEXT) +@@ -87,10 +89,12 @@ + #define glBindProgramNV MANGLE(BindProgramNV) + #define glBindRenderbufferEXT MANGLE(BindRenderbufferEXT) + #define glBindRenderbuffer MANGLE(BindRenderbuffer) ++#define glBindSampler MANGLE(BindSampler) + #define glBindTexGenParameterEXT MANGLE(BindTexGenParameterEXT) + #define glBindTextureEXT MANGLE(BindTextureEXT) + #define glBindTexture MANGLE(BindTexture) + #define glBindTextureUnitParameterEXT MANGLE(BindTextureUnitParameterEXT) ++#define glBindTransformFeedback MANGLE(BindTransformFeedback) + #define glBindTransformFeedbackNV MANGLE(BindTransformFeedbackNV) + #define glBindVertexArrayAPPLE MANGLE(BindVertexArrayAPPLE) + #define glBindVertexArray MANGLE(BindVertexArray) +@@ -215,6 +219,10 @@ + #define glColorMaskIndexedEXT MANGLE(ColorMaskIndexedEXT) + #define glColorMask MANGLE(ColorMask) + #define glColorMaterial MANGLE(ColorMaterial) ++#define glColorP3ui MANGLE(ColorP3ui) ++#define glColorP3uiv MANGLE(ColorP3uiv) ++#define glColorP4ui MANGLE(ColorP4ui) ++#define glColorP4uiv MANGLE(ColorP4uiv) + #define glColorPointerEXT MANGLE(ColorPointerEXT) + #define glColorPointerListIBM MANGLE(ColorPointerListIBM) + #define glColorPointer MANGLE(ColorPointer) +@@ -236,6 +244,7 @@ + #define glCombinerParameterivNV MANGLE(CombinerParameterivNV) + #define glCombinerStageParameterfvNV MANGLE(CombinerStageParameterfvNV) + #define glCompileShaderARB MANGLE(CompileShaderARB) ++#define glCompileShaderIncludeARB MANGLE(CompileShaderIncludeARB) + #define glCompileShader MANGLE(CompileShader) + #define glCompressedMultiTexImage1DEXT MANGLE(CompressedMultiTexImage1DEXT) + #define glCompressedMultiTexImage2DEXT MANGLE(CompressedMultiTexImage2DEXT) +@@ -326,6 +335,7 @@ + #define glDeleteFramebuffersEXT MANGLE(DeleteFramebuffersEXT) + #define glDeleteFramebuffers MANGLE(DeleteFramebuffers) + #define glDeleteLists MANGLE(DeleteLists) ++#define glDeleteNamedStringARB MANGLE(DeleteNamedStringARB) + #define glDeleteObjectARB MANGLE(DeleteObjectARB) + #define glDeleteOcclusionQueriesNV MANGLE(DeleteOcclusionQueriesNV) + #define glDeletePerfMonitorsAMD MANGLE(DeletePerfMonitorsAMD) +@@ -336,10 +346,12 @@ + #define glDeleteQueries MANGLE(DeleteQueries) + #define glDeleteRenderbuffersEXT MANGLE(DeleteRenderbuffersEXT) + #define glDeleteRenderbuffers MANGLE(DeleteRenderbuffers) ++#define glDeleteSamplers MANGLE(DeleteSamplers) + #define glDeleteShader MANGLE(DeleteShader) + #define glDeleteSync MANGLE(DeleteSync) + #define glDeleteTexturesEXT MANGLE(DeleteTexturesEXT) + #define glDeleteTextures MANGLE(DeleteTextures) ++#define glDeleteTransformFeedbacks MANGLE(DeleteTransformFeedbacks) + #define glDeleteTransformFeedbacksNV MANGLE(DeleteTransformFeedbacksNV) + #define glDeleteVertexArraysAPPLE MANGLE(DeleteVertexArraysAPPLE) + #define glDeleteVertexArrays MANGLE(DeleteVertexArrays) +@@ -363,6 +375,7 @@ + #define glDisableVertexAttribArrayARB MANGLE(DisableVertexAttribArrayARB) + #define glDisableVertexAttribArray MANGLE(DisableVertexAttribArray) + #define glDrawArraysEXT MANGLE(DrawArraysEXT) ++#define glDrawArraysIndirect MANGLE(DrawArraysIndirect) + #define glDrawArraysInstancedARB MANGLE(DrawArraysInstancedARB) + #define glDrawArraysInstancedEXT MANGLE(DrawArraysInstancedEXT) + #define glDrawArraysInstanced MANGLE(DrawArraysInstanced) +@@ -374,6 +387,7 @@ + #define glDrawElementArrayAPPLE MANGLE(DrawElementArrayAPPLE) + #define glDrawElementArrayATI MANGLE(DrawElementArrayATI) + #define glDrawElementsBaseVertex MANGLE(DrawElementsBaseVertex) ++#define glDrawElementsIndirect MANGLE(DrawElementsIndirect) + #define glDrawElementsInstancedARB MANGLE(DrawElementsInstancedARB) + #define glDrawElementsInstancedBaseVertex MANGLE(DrawElementsInstancedBaseVertex) + #define glDrawElementsInstancedEXT MANGLE(DrawElementsInstancedEXT) +@@ -386,7 +400,9 @@ + #define glDrawRangeElementsBaseVertex MANGLE(DrawRangeElementsBaseVertex) + #define glDrawRangeElementsEXT MANGLE(DrawRangeElementsEXT) + #define glDrawRangeElements MANGLE(DrawRangeElements) ++#define glDrawTransformFeedback MANGLE(DrawTransformFeedback) + #define glDrawTransformFeedbackNV MANGLE(DrawTransformFeedbackNV) ++#define glDrawTransformFeedbackStream MANGLE(DrawTransformFeedbackStream) + #define glEdgeFlagFormatNV MANGLE(EdgeFlagFormatNV) + #define glEdgeFlag MANGLE(EdgeFlag) + #define glEdgeFlagPointerEXT MANGLE(EdgeFlagPointerEXT) +@@ -414,6 +430,7 @@ + #define glEndOcclusionQueryNV MANGLE(EndOcclusionQueryNV) + #define glEndPerfMonitorAMD MANGLE(EndPerfMonitorAMD) + #define glEndQueryARB MANGLE(EndQueryARB) ++#define glEndQueryIndexed MANGLE(EndQueryIndexed) + #define glEndQuery MANGLE(EndQuery) + #define glEndTransformFeedbackEXT MANGLE(EndTransformFeedbackEXT) + #define glEndTransformFeedback MANGLE(EndTransformFeedback) +@@ -498,7 +515,6 @@ + #define glFramebufferTextureEXT MANGLE(FramebufferTextureEXT) + #define glFramebufferTextureFaceARB MANGLE(FramebufferTextureFaceARB) + #define glFramebufferTextureFaceEXT MANGLE(FramebufferTextureFaceEXT) +-#define glFramebufferTextureFace MANGLE(FramebufferTextureFace) + #define glFramebufferTextureLayerARB MANGLE(FramebufferTextureLayerARB) + #define glFramebufferTextureLayerEXT MANGLE(FramebufferTextureLayerEXT) + #define glFramebufferTextureLayer MANGLE(FramebufferTextureLayer) +@@ -529,15 +545,20 @@ + #define glGenQueries MANGLE(GenQueries) + #define glGenRenderbuffersEXT MANGLE(GenRenderbuffersEXT) + #define glGenRenderbuffers MANGLE(GenRenderbuffers) ++#define glGenSamplers MANGLE(GenSamplers) + #define glGenSymbolsEXT MANGLE(GenSymbolsEXT) + #define glGenTexturesEXT MANGLE(GenTexturesEXT) + #define glGenTextures MANGLE(GenTextures) ++#define glGenTransformFeedbacks MANGLE(GenTransformFeedbacks) + #define glGenTransformFeedbacksNV MANGLE(GenTransformFeedbacksNV) + #define glGenVertexArraysAPPLE MANGLE(GenVertexArraysAPPLE) + #define glGenVertexArrays MANGLE(GenVertexArrays) + #define glGenVertexShadersEXT MANGLE(GenVertexShadersEXT) + #define glGetActiveAttribARB MANGLE(GetActiveAttribARB) + #define glGetActiveAttrib MANGLE(GetActiveAttrib) ++#define glGetActiveSubroutineName MANGLE(GetActiveSubroutineName) ++#define glGetActiveSubroutineUniformiv MANGLE(GetActiveSubroutineUniformiv) ++#define glGetActiveSubroutineUniformName MANGLE(GetActiveSubroutineUniformName) + #define glGetActiveUniformARB MANGLE(GetActiveUniformARB) + #define glGetActiveUniformBlockiv MANGLE(GetActiveUniformBlockiv) + #define glGetActiveUniformBlockName MANGLE(GetActiveUniformBlockName) +@@ -599,6 +620,7 @@ + #define glGetFloatIndexedvEXT MANGLE(GetFloatIndexedvEXT) + #define glGetFloatv MANGLE(GetFloatv) + #define glGetFogFuncSGIS MANGLE(GetFogFuncSGIS) ++#define glGetFragDataIndex MANGLE(GetFragDataIndex) + #define glGetFragDataLocationEXT MANGLE(GetFragDataLocationEXT) + #define glGetFragDataLocation MANGLE(GetFragDataLocation) + #define glGetFragmentLightfvSGIX MANGLE(GetFragmentLightfvSGIX) +@@ -678,6 +700,8 @@ + #define glGetNamedProgramLocalParameterIuivEXT MANGLE(GetNamedProgramLocalParameterIuivEXT) + #define glGetNamedProgramStringEXT MANGLE(GetNamedProgramStringEXT) + #define glGetNamedRenderbufferParameterivEXT MANGLE(GetNamedRenderbufferParameterivEXT) ++#define glGetNamedStringARB MANGLE(GetNamedStringARB) ++#define glGetNamedStringivARB MANGLE(GetNamedStringivARB) + #define glGetObjectBufferfvATI MANGLE(GetObjectBufferfvATI) + #define glGetObjectBufferivATI MANGLE(GetObjectBufferivATI) + #define glGetObjectParameterfvARB MANGLE(GetObjectParameterfvARB) +@@ -717,18 +741,26 @@ + #define glGetProgramParameterdvNV MANGLE(GetProgramParameterdvNV) + #define glGetProgramParameterfvNV MANGLE(GetProgramParameterfvNV) + #define glGetProgramRegisterfvMESA MANGLE(GetProgramRegisterfvMESA) ++#define glGetProgramStageiv MANGLE(GetProgramStageiv) + #define glGetProgramStringARB MANGLE(GetProgramStringARB) + #define glGetProgramStringNV MANGLE(GetProgramStringNV) ++#define glGetQueryIndexediv MANGLE(GetQueryIndexediv) + #define glGetQueryivARB MANGLE(GetQueryivARB) + #define glGetQueryiv MANGLE(GetQueryiv) + #define glGetQueryObjecti64vEXT MANGLE(GetQueryObjecti64vEXT) ++#define glGetQueryObjecti64v MANGLE(GetQueryObjecti64v) + #define glGetQueryObjectivARB MANGLE(GetQueryObjectivARB) + #define glGetQueryObjectiv MANGLE(GetQueryObjectiv) + #define glGetQueryObjectui64vEXT MANGLE(GetQueryObjectui64vEXT) ++#define glGetQueryObjectui64v MANGLE(GetQueryObjectui64v) + #define glGetQueryObjectuivARB MANGLE(GetQueryObjectuivARB) + #define glGetQueryObjectuiv MANGLE(GetQueryObjectuiv) + #define glGetRenderbufferParameterivEXT MANGLE(GetRenderbufferParameterivEXT) + #define glGetRenderbufferParameteriv MANGLE(GetRenderbufferParameteriv) ++#define glGetSamplerParameterfv MANGLE(GetSamplerParameterfv) ++#define glGetSamplerParameterIfv MANGLE(GetSamplerParameterIfv) ++#define glGetSamplerParameterIiv MANGLE(GetSamplerParameterIiv) ++#define glGetSamplerParameteriv MANGLE(GetSamplerParameteriv) + #define glGetSeparableFilterEXT MANGLE(GetSeparableFilterEXT) + #define glGetSeparableFilter MANGLE(GetSeparableFilter) + #define glGetShaderInfoLog MANGLE(GetShaderInfoLog) +@@ -738,6 +770,8 @@ + #define glGetSharpenTexFuncSGIS MANGLE(GetSharpenTexFuncSGIS) + #define glGetStringi MANGLE(GetStringi) + #define glGetString MANGLE(GetString) ++#define glGetSubroutineIndex MANGLE(GetSubroutineIndex) ++#define glGetSubroutineUniformLocation MANGLE(GetSubroutineUniformLocation) + #define glGetSynciv MANGLE(GetSynciv) + #define glGetTexBumpParameterfvATI MANGLE(GetTexBumpParameterfvATI) + #define glGetTexBumpParameterivATI MANGLE(GetTexBumpParameterivATI) +@@ -770,6 +804,7 @@ + #define glGetTransformFeedbackVaryingNV MANGLE(GetTransformFeedbackVaryingNV) + #define glGetUniformBlockIndex MANGLE(GetUniformBlockIndex) + #define glGetUniformBufferSizeEXT MANGLE(GetUniformBufferSizeEXT) ++#define glGetUniformdv MANGLE(GetUniformdv) + #define glGetUniformfvARB MANGLE(GetUniformfvARB) + #define glGetUniformfv MANGLE(GetUniformfv) + #define glGetUniformIndices MANGLE(GetUniformIndices) +@@ -778,6 +813,7 @@ + #define glGetUniformLocationARB MANGLE(GetUniformLocationARB) + #define glGetUniformLocation MANGLE(GetUniformLocation) + #define glGetUniformOffsetEXT MANGLE(GetUniformOffsetEXT) ++#define glGetUniformSubroutineuiv MANGLE(GetUniformSubroutineuiv) + #define glGetUniformui64vNV MANGLE(GetUniformui64vNV) + #define glGetUniformuivEXT MANGLE(GetUniformuivEXT) + #define glGetUniformuiv MANGLE(GetUniformuiv) +@@ -865,6 +901,7 @@ + #define glIsFramebuffer MANGLE(IsFramebuffer) + #define glIsList MANGLE(IsList) + #define glIsNamedBufferResidentNV MANGLE(IsNamedBufferResidentNV) ++#define glIsNamedStringARB MANGLE(IsNamedStringARB) + #define glIsObjectBufferATI MANGLE(IsObjectBufferATI) + #define glIsOcclusionQueryNV MANGLE(IsOcclusionQueryNV) + #define glIsProgramARB MANGLE(IsProgramARB) +@@ -874,10 +911,12 @@ + #define glIsQuery MANGLE(IsQuery) + #define glIsRenderbufferEXT MANGLE(IsRenderbufferEXT) + #define glIsRenderbuffer MANGLE(IsRenderbuffer) ++#define glIsSampler MANGLE(IsSampler) + #define glIsShader MANGLE(IsShader) + #define glIsSync MANGLE(IsSync) + #define glIsTextureEXT MANGLE(IsTextureEXT) + #define glIsTexture MANGLE(IsTexture) ++#define glIsTransformFeedback MANGLE(IsTransformFeedback) + #define glIsTransformFeedbackNV MANGLE(IsTransformFeedbackNV) + #define glIsVariantEnabledEXT MANGLE(IsVariantEnabledEXT) + #define glIsVertexArrayAPPLE MANGLE(IsVertexArrayAPPLE) +@@ -915,6 +954,8 @@ + #define glLogicOp MANGLE(LogicOp) + #define glMakeBufferNonResidentNV MANGLE(MakeBufferNonResidentNV) + #define glMakeBufferResidentNV MANGLE(MakeBufferResidentNV) ++#define glMakeNamedBufferNonResidentNV MANGLE(MakeNamedBufferNonResidentNV) ++#define glMakeNamedBufferResidentNV MANGLE(MakeNamedBufferResidentNV) + #define glMap1d MANGLE(Map1d) + #define glMap1f MANGLE(Map1f) + #define glMap2d MANGLE(Map2d) +@@ -1048,6 +1089,14 @@ + #define glMultiTexCoord4s MANGLE(MultiTexCoord4s) + #define glMultiTexCoord4svARB MANGLE(MultiTexCoord4svARB) + #define glMultiTexCoord4sv MANGLE(MultiTexCoord4sv) ++#define glMultiTexCoordP1ui MANGLE(MultiTexCoordP1ui) ++#define glMultiTexCoordP1uiv MANGLE(MultiTexCoordP1uiv) ++#define glMultiTexCoordP2ui MANGLE(MultiTexCoordP2ui) ++#define glMultiTexCoordP2uiv MANGLE(MultiTexCoordP2uiv) ++#define glMultiTexCoordP3ui MANGLE(MultiTexCoordP3ui) ++#define glMultiTexCoordP3uiv MANGLE(MultiTexCoordP3uiv) ++#define glMultiTexCoordP4ui MANGLE(MultiTexCoordP4ui) ++#define glMultiTexCoordP4uiv MANGLE(MultiTexCoordP4uiv) + #define glMultiTexCoordPointerEXT MANGLE(MultiTexCoordPointerEXT) + #define glMultiTexEnvfEXT MANGLE(MultiTexEnvfEXT) + #define glMultiTexEnvfvEXT MANGLE(MultiTexEnvfvEXT) +@@ -1087,8 +1136,6 @@ + #define glNamedFramebufferTextureEXT MANGLE(NamedFramebufferTextureEXT) + #define glNamedFramebufferTextureFaceEXT MANGLE(NamedFramebufferTextureFaceEXT) + #define glNamedFramebufferTextureLayerEXT MANGLE(NamedFramebufferTextureLayerEXT) +-#define glNamedMakeBufferNonResidentNV MANGLE(NamedMakeBufferNonResidentNV) +-#define glNamedMakeBufferResidentNV MANGLE(NamedMakeBufferResidentNV) + #define glNamedProgramLocalParameter4dEXT MANGLE(NamedProgramLocalParameter4dEXT) + #define glNamedProgramLocalParameter4dvEXT MANGLE(NamedProgramLocalParameter4dvEXT) + #define glNamedProgramLocalParameter4fEXT MANGLE(NamedProgramLocalParameter4fEXT) +@@ -1104,6 +1151,7 @@ + #define glNamedRenderbufferStorageEXT MANGLE(NamedRenderbufferStorageEXT) + #define glNamedRenderbufferStorageMultisampleCoverageEXT MANGLE(NamedRenderbufferStorageMultisampleCoverageEXT) + #define glNamedRenderbufferStorageMultisampleEXT MANGLE(NamedRenderbufferStorageMultisampleEXT) ++#define glNamedStringARB MANGLE(NamedStringARB) + #define glNewList MANGLE(NewList) + #define glNewObjectBufferATI MANGLE(NewObjectBufferATI) + #define glNormal3b MANGLE(Normal3b) +@@ -1121,6 +1169,8 @@ + #define glNormal3s MANGLE(Normal3s) + #define glNormal3sv MANGLE(Normal3sv) + #define glNormalFormatNV MANGLE(NormalFormatNV) ++#define glNormalP3ui MANGLE(NormalP3ui) ++#define glNormalP3uiv MANGLE(NormalP3uiv) + #define glNormalPointerEXT MANGLE(NormalPointerEXT) + #define glNormalPointerListIBM MANGLE(NormalPointerListIBM) + #define glNormalPointer MANGLE(NormalPointer) +@@ -1140,6 +1190,9 @@ + #define glOrtho MANGLE(Ortho) + #define glPassTexCoordATI MANGLE(PassTexCoordATI) + #define glPassThrough MANGLE(PassThrough) ++#define glPatchParameterfv MANGLE(PatchParameterfv) ++#define glPatchParameteri MANGLE(PatchParameteri) ++#define glPauseTransformFeedback MANGLE(PauseTransformFeedback) + #define glPauseTransformFeedbackNV MANGLE(PauseTransformFeedbackNV) + #define glPixelDataRangeNV MANGLE(PixelDataRangeNV) + #define glPixelMapfv MANGLE(PixelMapfv) +@@ -1231,38 +1284,55 @@ + #define glProgramParameters4dvNV MANGLE(ProgramParameters4dvNV) + #define glProgramParameters4fvNV MANGLE(ProgramParameters4fvNV) + #define glProgramStringARB MANGLE(ProgramStringARB) ++#define glProgramUniform1dEXT MANGLE(ProgramUniform1dEXT) ++#define glProgramUniform1dvEXT MANGLE(ProgramUniform1dvEXT) + #define glProgramUniform1fEXT MANGLE(ProgramUniform1fEXT) + #define glProgramUniform1fvEXT MANGLE(ProgramUniform1fvEXT) + #define glProgramUniform1iEXT MANGLE(ProgramUniform1iEXT) + #define glProgramUniform1ivEXT MANGLE(ProgramUniform1ivEXT) + #define glProgramUniform1uiEXT MANGLE(ProgramUniform1uiEXT) + #define glProgramUniform1uivEXT MANGLE(ProgramUniform1uivEXT) ++#define glProgramUniform2dEXT MANGLE(ProgramUniform2dEXT) ++#define glProgramUniform2dvEXT MANGLE(ProgramUniform2dvEXT) + #define glProgramUniform2fEXT MANGLE(ProgramUniform2fEXT) + #define glProgramUniform2fvEXT MANGLE(ProgramUniform2fvEXT) + #define glProgramUniform2iEXT MANGLE(ProgramUniform2iEXT) + #define glProgramUniform2ivEXT MANGLE(ProgramUniform2ivEXT) + #define glProgramUniform2uiEXT MANGLE(ProgramUniform2uiEXT) + #define glProgramUniform2uivEXT MANGLE(ProgramUniform2uivEXT) ++#define glProgramUniform3dEXT MANGLE(ProgramUniform3dEXT) ++#define glProgramUniform3dvEXT MANGLE(ProgramUniform3dvEXT) + #define glProgramUniform3fEXT MANGLE(ProgramUniform3fEXT) + #define glProgramUniform3fvEXT MANGLE(ProgramUniform3fvEXT) + #define glProgramUniform3iEXT MANGLE(ProgramUniform3iEXT) + #define glProgramUniform3ivEXT MANGLE(ProgramUniform3ivEXT) + #define glProgramUniform3uiEXT MANGLE(ProgramUniform3uiEXT) + #define glProgramUniform3uivEXT MANGLE(ProgramUniform3uivEXT) ++#define glProgramUniform4dEXT MANGLE(ProgramUniform4dEXT) ++#define glProgramUniform4dvEXT MANGLE(ProgramUniform4dvEXT) + #define glProgramUniform4fEXT MANGLE(ProgramUniform4fEXT) + #define glProgramUniform4fvEXT MANGLE(ProgramUniform4fvEXT) + #define glProgramUniform4iEXT MANGLE(ProgramUniform4iEXT) + #define glProgramUniform4ivEXT MANGLE(ProgramUniform4ivEXT) + #define glProgramUniform4uiEXT MANGLE(ProgramUniform4uiEXT) + #define glProgramUniform4uivEXT MANGLE(ProgramUniform4uivEXT) ++#define glProgramUniformMatrix2dvEXT MANGLE(ProgramUniformMatrix2dvEXT) + #define glProgramUniformMatrix2fvEXT MANGLE(ProgramUniformMatrix2fvEXT) ++#define glProgramUniformMatrix2x3dvEXT MANGLE(ProgramUniformMatrix2x3dvEXT) + #define glProgramUniformMatrix2x3fvEXT MANGLE(ProgramUniformMatrix2x3fvEXT) ++#define glProgramUniformMatrix2x4dvEXT MANGLE(ProgramUniformMatrix2x4dvEXT) + #define glProgramUniformMatrix2x4fvEXT MANGLE(ProgramUniformMatrix2x4fvEXT) ++#define glProgramUniformMatrix3dvEXT MANGLE(ProgramUniformMatrix3dvEXT) + #define glProgramUniformMatrix3fvEXT MANGLE(ProgramUniformMatrix3fvEXT) ++#define glProgramUniformMatrix3x2dvEXT MANGLE(ProgramUniformMatrix3x2dvEXT) + #define glProgramUniformMatrix3x2fvEXT MANGLE(ProgramUniformMatrix3x2fvEXT) ++#define glProgramUniformMatrix3x4dvEXT MANGLE(ProgramUniformMatrix3x4dvEXT) + #define glProgramUniformMatrix3x4fvEXT MANGLE(ProgramUniformMatrix3x4fvEXT) ++#define glProgramUniformMatrix4dvEXT MANGLE(ProgramUniformMatrix4dvEXT) + #define glProgramUniformMatrix4fvEXT MANGLE(ProgramUniformMatrix4fvEXT) ++#define glProgramUniformMatrix4x2dvEXT MANGLE(ProgramUniformMatrix4x2dvEXT) + #define glProgramUniformMatrix4x2fvEXT MANGLE(ProgramUniformMatrix4x2fvEXT) ++#define glProgramUniformMatrix4x3dvEXT MANGLE(ProgramUniformMatrix4x3dvEXT) + #define glProgramUniformMatrix4x3fvEXT MANGLE(ProgramUniformMatrix4x3fvEXT) + #define glProgramUniformui64NV MANGLE(ProgramUniformui64NV) + #define glProgramUniformui64vNV MANGLE(ProgramUniformui64vNV) +@@ -1274,6 +1344,7 @@ + #define glPushClientAttrib MANGLE(PushClientAttrib) + #define glPushMatrix MANGLE(PushMatrix) + #define glPushName MANGLE(PushName) ++#define glQueryCounter MANGLE(QueryCounter) + #define glRasterPos2d MANGLE(RasterPos2d) + #define glRasterPos2dv MANGLE(RasterPos2dv) + #define glRasterPos2f MANGLE(RasterPos2f) +@@ -1345,6 +1416,7 @@ + #define glResetMinmaxEXT MANGLE(ResetMinmaxEXT) + #define glResetMinmax MANGLE(ResetMinmax) + #define glResizeBuffersMESA MANGLE(ResizeBuffersMESA) ++#define glResumeTransformFeedback MANGLE(ResumeTransformFeedback) + #define glResumeTransformFeedbackNV MANGLE(ResumeTransformFeedbackNV) + #define glRotated MANGLE(Rotated) + #define glRotatef MANGLE(Rotatef) +@@ -1357,6 +1429,12 @@ + #define glSampleMaskSGIS MANGLE(SampleMaskSGIS) + #define glSamplePatternEXT MANGLE(SamplePatternEXT) + #define glSamplePatternSGIS MANGLE(SamplePatternSGIS) ++#define glSamplerParameterf MANGLE(SamplerParameterf) ++#define glSamplerParameterfv MANGLE(SamplerParameterfv) ++#define glSamplerParameterIiv MANGLE(SamplerParameterIiv) ++#define glSamplerParameteri MANGLE(SamplerParameteri) ++#define glSamplerParameterIuiv MANGLE(SamplerParameterIuiv) ++#define glSamplerParameteriv MANGLE(SamplerParameteriv) + #define glScaled MANGLE(Scaled) + #define glScalef MANGLE(Scalef) + #define glScissor MANGLE(Scissor) +@@ -1395,6 +1473,8 @@ + #define glSecondaryColor3usvEXT MANGLE(SecondaryColor3usvEXT) + #define glSecondaryColor3usv MANGLE(SecondaryColor3usv) + #define glSecondaryColorFormatNV MANGLE(SecondaryColorFormatNV) ++#define glSecondaryColorP3ui MANGLE(SecondaryColorP3ui) ++#define glSecondaryColorP3uiv MANGLE(SecondaryColorP3uiv) + #define glSecondaryColorPointerEXT MANGLE(SecondaryColorPointerEXT) + #define glSecondaryColorPointerListIBM MANGLE(SecondaryColorPointerListIBM) + #define glSecondaryColorPointer MANGLE(SecondaryColorPointer) +@@ -1509,6 +1589,14 @@ + #define glTexCoord4s MANGLE(TexCoord4s) + #define glTexCoord4sv MANGLE(TexCoord4sv) + #define glTexCoordFormatNV MANGLE(TexCoordFormatNV) ++#define glTexCoordP1ui MANGLE(TexCoordP1ui) ++#define glTexCoordP1uiv MANGLE(TexCoordP1uiv) ++#define glTexCoordP2ui MANGLE(TexCoordP2ui) ++#define glTexCoordP2uiv MANGLE(TexCoordP2uiv) ++#define glTexCoordP3ui MANGLE(TexCoordP3ui) ++#define glTexCoordP3uiv MANGLE(TexCoordP3uiv) ++#define glTexCoordP4ui MANGLE(TexCoordP4ui) ++#define glTexCoordP4uiv MANGLE(TexCoordP4uiv) + #define glTexCoordPointerEXT MANGLE(TexCoordPointerEXT) + #define glTexCoordPointerListIBM MANGLE(TexCoordPointerListIBM) + #define glTexCoordPointer MANGLE(TexCoordPointer) +@@ -1574,6 +1662,8 @@ + #define glTransformFeedbackVaryingsNV MANGLE(TransformFeedbackVaryingsNV) + #define glTranslated MANGLE(Translated) + #define glTranslatef MANGLE(Translatef) ++#define glUniform1d MANGLE(Uniform1d) ++#define glUniform1dv MANGLE(Uniform1dv) + #define glUniform1fARB MANGLE(Uniform1fARB) + #define glUniform1f MANGLE(Uniform1f) + #define glUniform1fvARB MANGLE(Uniform1fvARB) +@@ -1586,6 +1676,8 @@ + #define glUniform1ui MANGLE(Uniform1ui) + #define glUniform1uivEXT MANGLE(Uniform1uivEXT) + #define glUniform1uiv MANGLE(Uniform1uiv) ++#define glUniform2d MANGLE(Uniform2d) ++#define glUniform2dv MANGLE(Uniform2dv) + #define glUniform2fARB MANGLE(Uniform2fARB) + #define glUniform2f MANGLE(Uniform2f) + #define glUniform2fvARB MANGLE(Uniform2fvARB) +@@ -1598,6 +1690,8 @@ + #define glUniform2ui MANGLE(Uniform2ui) + #define glUniform2uivEXT MANGLE(Uniform2uivEXT) + #define glUniform2uiv MANGLE(Uniform2uiv) ++#define glUniform3d MANGLE(Uniform3d) ++#define glUniform3dv MANGLE(Uniform3dv) + #define glUniform3fARB MANGLE(Uniform3fARB) + #define glUniform3f MANGLE(Uniform3f) + #define glUniform3fvARB MANGLE(Uniform3fvARB) +@@ -1610,6 +1704,8 @@ + #define glUniform3ui MANGLE(Uniform3ui) + #define glUniform3uivEXT MANGLE(Uniform3uivEXT) + #define glUniform3uiv MANGLE(Uniform3uiv) ++#define glUniform4d MANGLE(Uniform4d) ++#define glUniform4dv MANGLE(Uniform4dv) + #define glUniform4fARB MANGLE(Uniform4fARB) + #define glUniform4f MANGLE(Uniform4f) + #define glUniform4fvARB MANGLE(Uniform4fvARB) +@@ -1624,18 +1720,28 @@ + #define glUniform4uiv MANGLE(Uniform4uiv) + #define glUniformBlockBinding MANGLE(UniformBlockBinding) + #define glUniformBufferEXT MANGLE(UniformBufferEXT) ++#define glUniformMatrix2dv MANGLE(UniformMatrix2dv) + #define glUniformMatrix2fvARB MANGLE(UniformMatrix2fvARB) + #define glUniformMatrix2fv MANGLE(UniformMatrix2fv) ++#define glUniformMatrix2x3dv MANGLE(UniformMatrix2x3dv) + #define glUniformMatrix2x3fv MANGLE(UniformMatrix2x3fv) ++#define glUniformMatrix2x4dv MANGLE(UniformMatrix2x4dv) + #define glUniformMatrix2x4fv MANGLE(UniformMatrix2x4fv) ++#define glUniformMatrix3dv MANGLE(UniformMatrix3dv) + #define glUniformMatrix3fvARB MANGLE(UniformMatrix3fvARB) + #define glUniformMatrix3fv MANGLE(UniformMatrix3fv) ++#define glUniformMatrix3x2dv MANGLE(UniformMatrix3x2dv) + #define glUniformMatrix3x2fv MANGLE(UniformMatrix3x2fv) ++#define glUniformMatrix3x4dv MANGLE(UniformMatrix3x4dv) + #define glUniformMatrix3x4fv MANGLE(UniformMatrix3x4fv) ++#define glUniformMatrix4dv MANGLE(UniformMatrix4dv) + #define glUniformMatrix4fvARB MANGLE(UniformMatrix4fvARB) + #define glUniformMatrix4fv MANGLE(UniformMatrix4fv) ++#define glUniformMatrix4x2dv MANGLE(UniformMatrix4x2dv) + #define glUniformMatrix4x2fv MANGLE(UniformMatrix4x2fv) ++#define glUniformMatrix4x3dv MANGLE(UniformMatrix4x3dv) + #define glUniformMatrix4x3fv MANGLE(UniformMatrix4x3fv) ++#define glUniformSubroutinesuiv MANGLE(UniformSubroutinesuiv) + #define glUniformui64NV MANGLE(Uniformui64NV) + #define glUniformui64vNV MANGLE(Uniformui64vNV) + #define glUnlockArraysEXT MANGLE(UnlockArraysEXT) +@@ -1844,6 +1950,14 @@ + #define glVertexAttribIFormatNV MANGLE(VertexAttribIFormatNV) + #define glVertexAttribIPointerEXT MANGLE(VertexAttribIPointerEXT) + #define glVertexAttribIPointer MANGLE(VertexAttribIPointer) ++#define glVertexAttribP1ui MANGLE(VertexAttribP1ui) ++#define glVertexAttribP1uiv MANGLE(VertexAttribP1uiv) ++#define glVertexAttribP2ui MANGLE(VertexAttribP2ui) ++#define glVertexAttribP2uiv MANGLE(VertexAttribP2uiv) ++#define glVertexAttribP3ui MANGLE(VertexAttribP3ui) ++#define glVertexAttribP3uiv MANGLE(VertexAttribP3uiv) ++#define glVertexAttribP4ui MANGLE(VertexAttribP4ui) ++#define glVertexAttribP4uiv MANGLE(VertexAttribP4uiv) + #define glVertexAttribPointerARB MANGLE(VertexAttribPointerARB) + #define glVertexAttribPointer MANGLE(VertexAttribPointer) + #define glVertexAttribPointerNV MANGLE(VertexAttribPointerNV) +@@ -1868,6 +1982,12 @@ + #define glVertexBlendEnvfATI MANGLE(VertexBlendEnvfATI) + #define glVertexBlendEnviATI MANGLE(VertexBlendEnviATI) + #define glVertexFormatNV MANGLE(VertexFormatNV) ++#define glVertexP2ui MANGLE(VertexP2ui) ++#define glVertexP2uiv MANGLE(VertexP2uiv) ++#define glVertexP3ui MANGLE(VertexP3ui) ++#define glVertexP3uiv MANGLE(VertexP3uiv) ++#define glVertexP4ui MANGLE(VertexP4ui) ++#define glVertexP4uiv MANGLE(VertexP4uiv) + #define glVertexPointerEXT MANGLE(VertexPointerEXT) + #define glVertexPointerListIBM MANGLE(VertexPointerListIBM) + #define glVertexPointer MANGLE(VertexPointer) +diff --git a/src/gallium/auxiliary/tgsi/tgsi_ureg.c b/src/gallium/auxiliary/tgsi/tgsi_ureg.c +index 3d0455d..db7bf17 100644 +--- a/src/gallium/auxiliary/tgsi/tgsi_ureg.c ++++ b/src/gallium/auxiliary/tgsi/tgsi_ureg.c +@@ -1489,6 +1489,12 @@ const struct tgsi_token *ureg_get_tokens( struct ureg_program *ureg, + } + + ++void ureg_free_tokens( const struct tgsi_token *tokens ) ++{ ++ FREE((struct tgsi_token *)tokens); ++} ++ ++ + struct ureg_program *ureg_create( unsigned processor ) + { + struct ureg_program *ureg = CALLOC_STRUCT( ureg_program ); +diff --git a/src/gallium/auxiliary/tgsi/tgsi_ureg.h b/src/gallium/auxiliary/tgsi/tgsi_ureg.h +index 0130a77..8302d1b 100644 +--- a/src/gallium/auxiliary/tgsi/tgsi_ureg.h ++++ b/src/gallium/auxiliary/tgsi/tgsi_ureg.h +@@ -104,6 +104,10 @@ ureg_get_tokens( struct ureg_program *ureg, + unsigned *nr_tokens ); + + ++/* Free the tokens created by ureg_get_tokens() */ ++void ureg_free_tokens( const struct tgsi_token *tokens ); ++ ++ + void + ureg_destroy( struct ureg_program * ); + +diff --git a/src/gallium/state_trackers/egl/x11/glxinit.c b/src/gallium/state_trackers/egl/x11/glxinit.c +index 1ed2afd..dd35189 100644 +--- a/src/gallium/state_trackers/egl/x11/glxinit.c ++++ b/src/gallium/state_trackers/egl/x11/glxinit.c +@@ -16,6 +16,8 @@ + + #include "glxinit.h" + ++#ifdef GLX_DIRECT_RENDERING ++ + typedef struct GLXGenericGetString + { + CARD8 reqType; +@@ -680,3 +682,5 @@ __glXInitialize(Display * dpy) + + return dpyPriv; + } ++ ++#endif /* GLX_DIRECT_RENDERING */ +diff --git a/src/gallium/state_trackers/egl/x11/native_dri2.c b/src/gallium/state_trackers/egl/x11/native_dri2.c +index 8d2a8b1..a8bdd34 100644 +--- a/src/gallium/state_trackers/egl/x11/native_dri2.c ++++ b/src/gallium/state_trackers/egl/x11/native_dri2.c +@@ -37,6 +37,8 @@ + #include "native_x11.h" + #include "x11_screen.h" + ++#ifdef GLX_DIRECT_RENDERING ++ + enum dri2_surface_type { + DRI2_SURFACE_TYPE_WINDOW, + DRI2_SURFACE_TYPE_PIXMAP, +@@ -878,3 +880,15 @@ x11_create_dri2_display(EGLNativeDisplayType dpy, + + return &dri2dpy->base; + } ++ ++#else /* GLX_DIRECT_RENDERING */ ++ ++struct native_display * ++x11_create_dri2_display(EGLNativeDisplayType dpy, ++ struct native_event_handler *event_handler, ++ struct drm_api *api) ++{ ++ return NULL; ++} ++ ++#endif /* GLX_DIRECT_RENDERING */ +diff --git a/src/gallium/state_trackers/egl/x11/native_x11.c b/src/gallium/state_trackers/egl/x11/native_x11.c +index 7b4fe63..0c25e4d 100644 +--- a/src/gallium/state_trackers/egl/x11/native_x11.c ++++ b/src/gallium/state_trackers/egl/x11/native_x11.c +@@ -70,7 +70,9 @@ native_create_probe(EGLNativeDisplayType dpy) + xscr = x11_screen_create(xdpy, scr); + if (xscr) { + if (x11_screen_support(xscr, X11_SCREEN_EXTENSION_DRI2)) { ++#ifdef GLX_DIRECT_RENDERING + driver_name = x11_screen_probe_dri2(xscr, NULL, NULL); ++#endif + if (driver_name) + nprobe->data = strdup(driver_name); + } +diff --git a/src/gallium/state_trackers/egl/x11/x11_screen.c b/src/gallium/state_trackers/egl/x11/x11_screen.c +index f409611..1706120 100644 +--- a/src/gallium/state_trackers/egl/x11/x11_screen.c ++++ b/src/gallium/state_trackers/egl/x11/x11_screen.c +@@ -39,8 +39,10 @@ + #include "glxinit.h" + + struct x11_screen { ++#ifdef GLX_DIRECT_RENDERING + /* dummy base class */ + struct __GLXDRIdisplayRec base; ++#endif + + Display *dpy; + int number; +@@ -103,15 +105,19 @@ x11_screen_destroy(struct x11_screen *xscr) + if (xscr->dri_device) + Xfree(xscr->dri_device); + ++#ifdef GLX_DIRECT_RENDERING + /* xscr->glx_dpy will be destroyed with the X display */ + if (xscr->glx_dpy) + xscr->glx_dpy->dri2Display = NULL; ++#endif + + if (xscr->visuals) + XFree(xscr->visuals); + free(xscr); + } + ++#ifdef GLX_DIRECT_RENDERING ++ + static boolean + x11_screen_init_dri2(struct x11_screen *xscr) + { +@@ -133,6 +139,8 @@ x11_screen_init_glx(struct x11_screen *xscr) + return (xscr->glx_dpy != NULL); + } + ++#endif /* GLX_DIRECT_RENDERING */ ++ + /** + * Return true if the screen supports the extension. + */ +@@ -145,12 +153,14 @@ x11_screen_support(struct x11_screen *xscr, enum x11_screen_extension ext) + case X11_SCREEN_EXTENSION_XSHM: + supported = XShmQueryExtension(xscr->dpy); + break; ++#ifdef GLX_DIRECT_RENDERING + case X11_SCREEN_EXTENSION_GLX: + supported = x11_screen_init_glx(xscr); + break; + case X11_SCREEN_EXTENSION_DRI2: + supported = x11_screen_init_dri2(xscr); + break; ++#endif + default: + break; + } +@@ -234,6 +244,39 @@ x11_screen_convert_visual(struct x11_screen *xscr, const XVisualInfo *visual, + } + + /** ++ * Return the depth of a drawable. ++ * ++ * Unlike other drawable functions, the drawable needs not be a DRI2 drawable. ++ */ ++uint ++x11_drawable_get_depth(struct x11_screen *xscr, Drawable drawable) ++{ ++ unsigned int depth; ++ ++ if (drawable != xscr->last_drawable) { ++ Window root; ++ int x, y; ++ unsigned int w, h, border; ++ Status ok; ++ ++ ok = XGetGeometry(xscr->dpy, drawable, &root, ++ &x, &y, &w, &h, &border, &depth); ++ if (!ok) ++ depth = 0; ++ ++ xscr->last_drawable = drawable; ++ xscr->last_depth = depth; ++ } ++ else { ++ depth = xscr->last_depth; ++ } ++ ++ return depth; ++} ++ ++#ifdef GLX_DIRECT_RENDERING ++ ++/** + * Return the GLX fbconfigs. + */ + const __GLcontextModes * +@@ -392,37 +435,6 @@ x11_drawable_get_buffers(struct x11_screen *xscr, Drawable drawable, + } + + /** +- * Return the depth of a drawable. +- * +- * Unlike other drawable functions, the drawable needs not be a DRI2 drawable. +- */ +-uint +-x11_drawable_get_depth(struct x11_screen *xscr, Drawable drawable) +-{ +- unsigned int depth; +- +- if (drawable != xscr->last_drawable) { +- Window root; +- int x, y; +- unsigned int w, h, border; +- Status ok; +- +- ok = XGetGeometry(xscr->dpy, drawable, &root, +- &x, &y, &w, &h, &border, &depth); +- if (!ok) +- depth = 0; +- +- xscr->last_drawable = drawable; +- xscr->last_depth = depth; +- } +- else { +- depth = xscr->last_depth; +- } +- +- return depth; +-} +- +-/** + * Create a mode list of the given size. + */ + __GLcontextModes * +@@ -489,3 +501,5 @@ dri2InvalidateBuffers(Display *dpy, XID drawable) + + xscr->dri_invalidate_buffers(xscr, drawable, xscr->dri_user_data); + } ++ ++#endif /* GLX_DIRECT_RENDERING */ +diff --git a/src/gallium/state_trackers/egl/x11/x11_screen.h b/src/gallium/state_trackers/egl/x11/x11_screen.h +index 37e8d5a..55e1201 100644 +--- a/src/gallium/state_trackers/egl/x11/x11_screen.h ++++ b/src/gallium/state_trackers/egl/x11/x11_screen.h +@@ -68,20 +68,18 @@ void + x11_screen_convert_visual(struct x11_screen *xscr, const XVisualInfo *visual, + __GLcontextModes *mode); + ++uint ++x11_drawable_get_depth(struct x11_screen *xscr, Drawable drawable); ++ ++#ifdef GLX_DIRECT_RENDERING ++ ++/* GLX */ + const __GLcontextModes * + x11_screen_get_glx_configs(struct x11_screen *xscr); + + const __GLcontextModes * + x11_screen_get_glx_visuals(struct x11_screen *xscr); + +-const char * +-x11_screen_probe_dri2(struct x11_screen *xscr, int *major, int *minor); +- +-int +-x11_screen_enable_dri2(struct x11_screen *xscr, +- x11_drawable_invalidate_buffers invalidate_buffers, +- void *user_data); +- + __GLcontextModes * + x11_context_modes_create(unsigned count); + +@@ -91,6 +89,15 @@ x11_context_modes_destroy(__GLcontextModes *modes); + unsigned + x11_context_modes_count(const __GLcontextModes *modes); + ++/* DRI2 */ ++const char * ++x11_screen_probe_dri2(struct x11_screen *xscr, int *major, int *minor); ++ ++int ++x11_screen_enable_dri2(struct x11_screen *xscr, ++ x11_drawable_invalidate_buffers invalidate_buffers, ++ void *user_data); ++ + void + x11_drawable_enable_dri2(struct x11_screen *xscr, + Drawable drawable, boolean on); +@@ -105,7 +112,6 @@ x11_drawable_get_buffers(struct x11_screen *xscr, Drawable drawable, + int *width, int *height, unsigned int *attachments, + boolean with_format, int num_ins, int *num_outs); + +-uint +-x11_drawable_get_depth(struct x11_screen *xscr, Drawable drawable); ++#endif /* GLX_DIRECT_RENDERING */ + + #endif /* _X11_SCREEN_H_ */ +diff --git a/src/gallium/winsys/drm/Makefile.egl b/src/gallium/winsys/drm/Makefile.egl +index 8363de6..c48967f 100644 +--- a/src/gallium/winsys/drm/Makefile.egl ++++ b/src/gallium/winsys/drm/Makefile.egl +@@ -11,7 +11,7 @@ + + EGL_DRIVER_OBJECTS = $(EGL_DRIVER_SOURCES:.c=.o) + +-common_LIBS = -ldrm -lm -ldl ++common_LIBS = -ldrm -lm $(DLOPEN_LIBS) + + x11_ST = $(TOP)/src/gallium/state_trackers/egl/libeglx11.a + x11_LIBS = $(common_LIBS) -lX11 -lXext -lXfixes +diff --git a/src/glw/Makefile b/src/glw/Makefile +index 1fb3d3c..39352f0 100644 +--- a/src/glw/Makefile ++++ b/src/glw/Makefile +@@ -17,7 +17,7 @@ OBJECTS = $(GLW_SOURCES:.c=.o) + ##### RULES ##### + + .c.o: +- $(CC) -c $(INCDIRS) $(CFLAGS) $< ++ $(CC) -c $(INCDIRS) $(CFLAGS) $(GLW_CFLAGS) $< + + + +diff --git a/src/mesa/drivers/dri/r300/compiler/radeon_pair_schedule.c b/src/mesa/drivers/dri/r300/compiler/radeon_pair_schedule.c +index df67aaf..b8ee288 100644 +--- a/src/mesa/drivers/dri/r300/compiler/radeon_pair_schedule.c ++++ b/src/mesa/drivers/dri/r300/compiler/radeon_pair_schedule.c +@@ -141,12 +141,28 @@ static void add_inst_to_list(struct schedule_instruction ** list, struct schedul + *list = inst; + } + ++static void add_inst_to_list_end(struct schedule_instruction ** list, ++ struct schedule_instruction * inst) ++{ ++ if(!*list){ ++ *list = inst; ++ }else{ ++ struct schedule_instruction * temp = *list; ++ while(temp->NextReady){ ++ temp = temp->NextReady; ++ } ++ temp->NextReady = inst; ++ } ++} ++ + static void instruction_ready(struct schedule_state * s, struct schedule_instruction * sinst) + { + DBG("%i is now ready\n", sinst->Instruction->IP); + ++ /* Adding Ready TEX instructions to the end of the "Ready List" helps ++ * us emit TEX instructions in blocks without losing our place. */ + if (sinst->Instruction->Type == RC_INSTRUCTION_NORMAL) +- add_inst_to_list(&s->ReadyTEX, sinst); ++ add_inst_to_list_end(&s->ReadyTEX, sinst); + else if (sinst->Instruction->U.P.Alpha.Opcode == RC_OPCODE_NOP) + add_inst_to_list(&s->ReadyRGB, sinst); + else if (sinst->Instruction->U.P.RGB.Opcode == RC_OPCODE_NOP) +@@ -163,11 +179,14 @@ static void decrease_dependencies(struct schedule_state * s, struct schedule_ins + instruction_ready(s, sinst); + } + +-static void commit_instruction(struct schedule_state * s, struct schedule_instruction * sinst) +-{ +- DBG("%i: commit\n", sinst->Instruction->IP); +- +- for(unsigned int i = 0; i < sinst->NumReadValues; ++i) { ++/** ++ * This function decreases the dependencies of the next instruction that ++ * wants to write to each of sinst's read values. ++ */ ++static void commit_update_reads(struct schedule_state * s, ++ struct schedule_instruction * sinst){ ++ unsigned int i; ++ for(i = 0; i < sinst->NumReadValues; ++i) { + struct reg_value * v = sinst->ReadValues[i]; + assert(v->NumReaders > 0); + v->NumReaders--; +@@ -176,8 +195,12 @@ static void commit_instruction(struct schedule_state * s, struct schedule_instru + decrease_dependencies(s, v->Next->Writer); + } + } ++} + +- for(unsigned int i = 0; i < sinst->NumWriteValues; ++i) { ++static void commit_update_writes(struct schedule_state * s, ++ struct schedule_instruction * sinst){ ++ unsigned int i; ++ for(i = 0; i < sinst->NumWriteValues; ++i) { + struct reg_value * v = sinst->WriteValues[i]; + if (v->NumReaders) { + for(struct reg_value_reader * r = v->Readers; r; r = r->Next) { +@@ -196,6 +219,15 @@ static void commit_instruction(struct schedule_state * s, struct schedule_instru + } + } + ++static void commit_alu_instruction(struct schedule_state * s, struct schedule_instruction * sinst) ++{ ++ DBG("%i: commit\n", sinst->Instruction->IP); ++ ++ commit_update_reads(s, sinst); ++ ++ commit_update_writes(s, sinst); ++} ++ + /** + * Emit all ready texture instructions in a single block. + * +@@ -208,21 +240,37 @@ static void emit_all_tex(struct schedule_state * s, struct rc_instruction * befo + + assert(s->ReadyTEX); + +- /* Don't let the ready list change under us! */ +- readytex = s->ReadyTEX; +- s->ReadyTEX = 0; +- + /* Node marker for R300 */ + struct rc_instruction * inst_begin = rc_insert_new_instruction(s->C, before->Prev); + inst_begin->U.I.Opcode = RC_OPCODE_BEGIN_TEX; + + /* Link texture instructions back in */ ++ readytex = s->ReadyTEX; + while(readytex) { +- struct schedule_instruction * tex = readytex; ++ rc_insert_instruction(before->Prev, readytex->Instruction); ++ DBG("%i: commit TEX reads\n", readytex->Instruction->IP); ++ ++ /* All of the TEX instructions in the same TEX block have ++ * their source registers read from before any of the ++ * instructions in that block write to their destination ++ * registers. This means that when we commit a TEX ++ * instruction, any other TEX instruction that wants to write ++ * to one of the committed instruction's source register can be ++ * marked as ready and should be emitted in the same TEX ++ * block. This prevents the following sequence from being ++ * emitted in two different TEX blocks: ++ * 0: TEX temp[0].xyz, temp[1].xy__, 2D[0]; ++ * 1: TEX temp[1].xyz, temp[2].xy__, 2D[0]; ++ */ ++ commit_update_reads(s, readytex); ++ readytex = readytex->NextReady; ++ } ++ readytex = s->ReadyTEX; ++ s->ReadyTEX = 0; ++ while(readytex){ ++ DBG("%i: commit TEX writes\n", readytex->Instruction->IP); ++ commit_update_writes(s, readytex); + readytex = readytex->NextReady; +- +- rc_insert_instruction(before->Prev, tex->Instruction); +- commit_instruction(s, tex); + } + } + +@@ -328,7 +376,7 @@ static void emit_one_alu(struct schedule_state *s, struct rc_instruction * befor + } + + rc_insert_instruction(before->Prev, sinst->Instruction); +- commit_instruction(s, sinst); ++ commit_alu_instruction(s, sinst); + } else { + struct schedule_instruction **prgb; + struct schedule_instruction **palpha; +@@ -346,8 +394,8 @@ static void emit_one_alu(struct schedule_state *s, struct rc_instruction * befor + *prgb = (*prgb)->NextReady; + *palpha = (*palpha)->NextReady; + rc_insert_instruction(before->Prev, psirgb->Instruction); +- commit_instruction(s, psirgb); +- commit_instruction(s, psialpha); ++ commit_alu_instruction(s, psirgb); ++ commit_alu_instruction(s, psialpha); + goto success; + } + } +@@ -357,7 +405,7 @@ static void emit_one_alu(struct schedule_state *s, struct rc_instruction * befor + s->ReadyRGB = s->ReadyRGB->NextReady; + + rc_insert_instruction(before->Prev, sinst->Instruction); +- commit_instruction(s, sinst); ++ commit_alu_instruction(s, sinst); + success: ; + } + } +diff --git a/src/mesa/drivers/dri/r300/r300_context.c b/src/mesa/drivers/dri/r300/r300_context.c +index 689bb13..4cbd4f2 100644 +--- a/src/mesa/drivers/dri/r300/r300_context.c ++++ b/src/mesa/drivers/dri/r300/r300_context.c +@@ -456,7 +456,7 @@ static void r300InitGLExtensions(GLcontext *ctx) + if (!r300->radeon.radeonScreen->drmSupportsOcclusionQueries) { + _mesa_disable_extension(ctx, "GL_ARB_occlusion_query"); + } +- if (r300->radeon.radeonScreen->chip_family >= CHIP_FAMILY_RV350) ++ if (r300->radeon.radeonScreen->chip_family >= CHIP_FAMILY_R420) + _mesa_enable_extension(ctx, "GL_ARB_half_float_vertex"); + + if (r300->radeon.radeonScreen->chip_family >= CHIP_FAMILY_RV515) +diff --git a/src/mesa/drivers/dri/r300/r300_draw.c b/src/mesa/drivers/dri/r300/r300_draw.c +index 282c0e1..5ae9f49 100644 +--- a/src/mesa/drivers/dri/r300/r300_draw.c ++++ b/src/mesa/drivers/dri/r300/r300_draw.c +@@ -523,8 +523,7 @@ static void r300AllocDmaRegions(GLcontext *ctx, const struct gl_client_array *in + r300ConvertAttrib(ctx, count, input[i], &vbuf->attribs[index]); + } else { + if (input[i]->BufferObj->Name) { +- if (stride % 4 != 0) { +- assert(((intptr_t) input[i]->Ptr) % input[i]->StrideB == 0); ++ if (stride % 4 != 0 || (intptr_t)input[i]->Ptr % 4 != 0) { + r300AlignDataToDword(ctx, input[i], count, &vbuf->attribs[index]); + vbuf->attribs[index].is_named_bo = GL_FALSE; + } else { +diff --git a/src/mesa/drivers/dri/r600/r700_assembler.c b/src/mesa/drivers/dri/r600/r700_assembler.c +index 834bcc6..7b957fb 100644 +--- a/src/mesa/drivers/dri/r600/r700_assembler.c ++++ b/src/mesa/drivers/dri/r600/r700_assembler.c +@@ -293,7 +293,9 @@ GLuint GetSurfaceFormat(GLenum eType, GLuint nChannels, GLuint * pClient_size) + case 2: + format = FMT_16_16; break; + case 3: +- format = FMT_16_16_16; break; ++ /* 3 comp GL_SHORT vertex format doesnt work on r700 ++ 4 somehow works, test - sauerbraten */ ++ format = FMT_16_16_16_16; break; + case 4: + format = FMT_16_16_16_16; break; + default: +@@ -1051,6 +1053,67 @@ void checkop_init(r700_AssemblerBase* pAsm) + pAsm->aArgSubst[3] = -1; + } + ++static GLboolean next_ins(r700_AssemblerBase *pAsm) ++{ ++ struct prog_instruction *pILInst = &(pAsm->pILInst[pAsm->uiCurInst]); ++ ++ if (GL_TRUE == pAsm->is_tex) ++ { ++ if (pILInst->TexSrcTarget == TEXTURE_RECT_INDEX) ++ { ++ if (GL_FALSE == assemble_tex_instruction(pAsm, GL_FALSE)) ++ { ++ radeon_error("Error assembling TEX instruction\n"); ++ return GL_FALSE; ++ } ++ } ++ else ++ { ++ if (GL_FALSE == assemble_tex_instruction(pAsm, GL_TRUE)) ++ { ++ radeon_error("Error assembling TEX instruction\n"); ++ return GL_FALSE; ++ } ++ } ++ } ++ else ++ { //ALU ++ if (GL_FALSE == assemble_alu_instruction(pAsm)) ++ { ++ radeon_error("Error assembling ALU instruction\n"); ++ return GL_FALSE; ++ } ++ } ++ ++ if (pAsm->D.dst.rtype == DST_REG_OUT) ++ { ++ assert(pAsm->D.dst.reg >= pAsm->starting_export_register_number); ++ ++ if (pAsm->D.dst.op3) ++ { ++ // There is no mask for OP3 instructions, so all channels are written ++ pAsm->pucOutMask[pAsm->D.dst.reg - pAsm->starting_export_register_number] = 0xF; ++ } ++ else ++ { ++ pAsm->pucOutMask[pAsm->D.dst.reg - pAsm->starting_export_register_number] ++ |= (unsigned char)pAsm->pILInst[pAsm->uiCurInst].DstReg.WriteMask; ++ } ++ } ++ ++ //reset for next inst. ++ pAsm->D.bits = 0; ++ pAsm->D2.bits = 0; ++ pAsm->S[0].bits = 0; ++ pAsm->S[1].bits = 0; ++ pAsm->S[2].bits = 0; ++ pAsm->is_tex = GL_FALSE; ++ pAsm->need_tex_barrier = GL_FALSE; ++ pAsm->D2.bits = 0; ++ pAsm->C[0].bits = pAsm->C[1].bits = pAsm->C[2].bits = pAsm->C[3].bits = 0; ++ return GL_TRUE; ++} ++ + GLboolean mov_temp(r700_AssemblerBase* pAsm, int src) + { + GLuint tmp = gethelpr(pAsm); +@@ -1201,7 +1264,7 @@ GLboolean checkop3(r700_AssemblerBase* pAsm) + { + if( GL_FALSE == mov_temp(pAsm, 1) ) + { +- return 1; ++ return GL_FALSE; + } + } + +@@ -2578,62 +2641,6 @@ GLboolean assemble_alu_instruction(r700_AssemblerBase *pAsm) + return GL_TRUE; + } + +-GLboolean next_ins(r700_AssemblerBase *pAsm) +-{ +- struct prog_instruction *pILInst = &(pAsm->pILInst[pAsm->uiCurInst]); +- +- if( GL_TRUE == pAsm->is_tex ) +- { +- if (pILInst->TexSrcTarget == TEXTURE_RECT_INDEX) { +- if( GL_FALSE == assemble_tex_instruction(pAsm, GL_FALSE) ) +- { +- radeon_error("Error assembling TEX instruction\n"); +- return GL_FALSE; +- } +- } else { +- if( GL_FALSE == assemble_tex_instruction(pAsm, GL_TRUE) ) +- { +- radeon_error("Error assembling TEX instruction\n"); +- return GL_FALSE; +- } +- } +- } +- else +- { //ALU +- if( GL_FALSE == assemble_alu_instruction(pAsm) ) +- { +- radeon_error("Error assembling ALU instruction\n"); +- return GL_FALSE; +- } +- } +- +- if(pAsm->D.dst.rtype == DST_REG_OUT) +- { +- if(pAsm->D.dst.op3) +- { +- // There is no mask for OP3 instructions, so all channels are written +- pAsm->pucOutMask[pAsm->D.dst.reg - pAsm->starting_export_register_number] = 0xF; +- } +- else +- { +- pAsm->pucOutMask[pAsm->D.dst.reg - pAsm->starting_export_register_number] +- |= (unsigned char)pAsm->pILInst[pAsm->uiCurInst].DstReg.WriteMask; +- } +- } +- +- //reset for next inst. +- pAsm->D.bits = 0; +- pAsm->D2.bits = 0; +- pAsm->S[0].bits = 0; +- pAsm->S[1].bits = 0; +- pAsm->S[2].bits = 0; +- pAsm->is_tex = GL_FALSE; +- pAsm->need_tex_barrier = GL_FALSE; +- pAsm->D2.bits = 0; +- pAsm->C[0].bits = pAsm->C[1].bits = pAsm->C[2].bits = pAsm->C[3].bits = 0; +- return GL_TRUE; +-} +- + GLboolean assemble_math_function(r700_AssemblerBase* pAsm, BITS opcode) + { + BITS tmp; +@@ -2865,25 +2872,92 @@ GLboolean assemble_CMP(r700_AssemblerBase *pAsm) + + GLboolean assemble_TRIG(r700_AssemblerBase *pAsm, BITS opcode) + { ++ /* ++ * r600 - trunc to -PI..PI range ++ * r700 - normalize by dividing by 2PI ++ * see fdo bug 27901 ++ */ ++ + int tmp; + checkop1(pAsm); + + tmp = gethelpr(pAsm); + +- pAsm->D.dst.opcode = SQ_OP2_INST_MUL; ++ pAsm->D.dst.opcode = SQ_OP3_INST_MULADD; ++ pAsm->D.dst.op3 = 1; ++ + setaddrmode_PVSDST(&(pAsm->D.dst), ADDR_ABSOLUTE); + pAsm->D.dst.rtype = DST_REG_TEMPORARY; + pAsm->D.dst.reg = tmp; +- pAsm->D.dst.writex = 1; + + assemble_src(pAsm, 0, -1); + + pAsm->S[1].src.rtype = SRC_REC_LITERAL; + setswizzle_PVSSRC(&(pAsm->S[1].src), SQ_SEL_X); ++ ++ pAsm->S[2].src.rtype = SRC_REC_LITERAL; ++ setswizzle_PVSSRC(&(pAsm->S[2].src), SQ_SEL_Y); ++ + pAsm->D2.dst2.literal_slots = 1; + pAsm->C[0].f = 1/(3.1415926535 * 2); +- pAsm->C[1].f = 0.0F; +- next_ins(pAsm); ++ pAsm->C[1].f = 0.5f; ++ ++ if ( GL_FALSE == next_ins(pAsm) ) ++ { ++ return GL_FALSE; ++ } ++ ++ pAsm->D.dst.opcode = SQ_OP2_INST_FRACT; ++ ++ setaddrmode_PVSDST(&(pAsm->D.dst), ADDR_ABSOLUTE); ++ pAsm->D.dst.rtype = DST_REG_TEMPORARY; ++ pAsm->D.dst.reg = tmp; ++ pAsm->D.dst.writex = 1; ++ ++ setaddrmode_PVSSRC(&(pAsm->S[0].src), ADDR_ABSOLUTE); ++ pAsm->S[0].src.rtype = SRC_REG_TEMPORARY; ++ pAsm->S[0].src.reg = tmp; ++ setswizzle_PVSSRC(&(pAsm->S[0].src), SQ_SEL_X); ++ ++ if(( GL_FALSE == next_ins(pAsm) )) ++ { ++ return GL_FALSE; ++ } ++ pAsm->D.dst.opcode = SQ_OP3_INST_MULADD; ++ pAsm->D.dst.op3 = 1; ++ ++ setaddrmode_PVSDST(&(pAsm->D.dst), ADDR_ABSOLUTE); ++ pAsm->D.dst.rtype = DST_REG_TEMPORARY; ++ pAsm->D.dst.reg = tmp; ++ ++ setaddrmode_PVSSRC(&(pAsm->S[0].src), ADDR_ABSOLUTE); ++ pAsm->S[0].src.rtype = SRC_REG_TEMPORARY; ++ pAsm->S[0].src.reg = tmp; ++ setswizzle_PVSSRC(&(pAsm->S[0].src), SQ_SEL_X); ++ ++ pAsm->S[1].src.rtype = SRC_REC_LITERAL; ++ setswizzle_PVSSRC(&(pAsm->S[1].src), SQ_SEL_X); ++ ++ pAsm->S[2].src.rtype = SRC_REC_LITERAL; ++ setswizzle_PVSSRC(&(pAsm->S[2].src), SQ_SEL_Y); ++ ++ pAsm->D2.dst2.literal_slots = 1; ++ ++ if (pAsm->bR6xx) ++ { ++ pAsm->C[0].f = 3.1415926535897f * 2.0f; ++ pAsm->C[1].f = -3.1415926535897f; ++ } ++ else ++ { ++ pAsm->C[0].f = 1.0f; ++ pAsm->C[1].f = -0.5f; ++ } ++ ++ if(( GL_FALSE == next_ins(pAsm) )) ++ { ++ return GL_FALSE; ++ } + + pAsm->D.dst.opcode = opcode; + pAsm->D.dst.math = 1; +@@ -4023,22 +4097,79 @@ GLboolean assemble_SCS(r700_AssemblerBase *pAsm) + checkop1(pAsm); + + tmp = gethelpr(pAsm); +- /* tmp.x = src /2*PI */ +- pAsm->D.dst.opcode = SQ_OP2_INST_MUL; ++ ++ pAsm->D.dst.opcode = SQ_OP3_INST_MULADD; ++ pAsm->D.dst.op3 = 1; ++ + setaddrmode_PVSDST(&(pAsm->D.dst), ADDR_ABSOLUTE); + pAsm->D.dst.rtype = DST_REG_TEMPORARY; + pAsm->D.dst.reg = tmp; +- pAsm->D.dst.writex = 1; + + assemble_src(pAsm, 0, -1); + + pAsm->S[1].src.rtype = SRC_REC_LITERAL; + setswizzle_PVSSRC(&(pAsm->S[1].src), SQ_SEL_X); ++ ++ pAsm->S[2].src.rtype = SRC_REC_LITERAL; ++ setswizzle_PVSSRC(&(pAsm->S[2].src), SQ_SEL_Y); ++ + pAsm->D2.dst2.literal_slots = 1; + pAsm->C[0].f = 1/(3.1415926535 * 2); +- pAsm->C[1].f = 0.0F; ++ pAsm->C[1].f = 0.5F; + +- next_ins(pAsm); ++ if ( GL_FALSE == next_ins(pAsm) ) ++ { ++ return GL_FALSE; ++ } ++ ++ pAsm->D.dst.opcode = SQ_OP2_INST_FRACT; ++ ++ setaddrmode_PVSDST(&(pAsm->D.dst), ADDR_ABSOLUTE); ++ pAsm->D.dst.rtype = DST_REG_TEMPORARY; ++ pAsm->D.dst.reg = tmp; ++ pAsm->D.dst.writex = 1; ++ ++ setaddrmode_PVSSRC(&(pAsm->S[0].src), ADDR_ABSOLUTE); ++ pAsm->S[0].src.rtype = SRC_REG_TEMPORARY; ++ pAsm->S[0].src.reg = tmp; ++ setswizzle_PVSSRC(&(pAsm->S[0].src), SQ_SEL_X); ++ ++ if(( GL_FALSE == next_ins(pAsm) )) ++ { ++ return GL_FALSE; ++ } ++ pAsm->D.dst.opcode = SQ_OP3_INST_MULADD; ++ pAsm->D.dst.op3 = 1; ++ ++ setaddrmode_PVSDST(&(pAsm->D.dst), ADDR_ABSOLUTE); ++ pAsm->D.dst.rtype = DST_REG_TEMPORARY; ++ pAsm->D.dst.reg = tmp; ++ ++ setaddrmode_PVSSRC(&(pAsm->S[0].src), ADDR_ABSOLUTE); ++ pAsm->S[0].src.rtype = SRC_REG_TEMPORARY; ++ pAsm->S[0].src.reg = tmp; ++ setswizzle_PVSSRC(&(pAsm->S[0].src), SQ_SEL_X); ++ ++ pAsm->S[1].src.rtype = SRC_REC_LITERAL; ++ setswizzle_PVSSRC(&(pAsm->S[1].src), SQ_SEL_X); ++ ++ pAsm->S[2].src.rtype = SRC_REC_LITERAL; ++ setswizzle_PVSSRC(&(pAsm->S[2].src), SQ_SEL_Y); ++ ++ pAsm->D2.dst2.literal_slots = 1; ++ ++ if(pAsm->bR6xx) { ++ pAsm->C[0].f = 3.1415926535897f * 2.0f; ++ pAsm->C[1].f = -3.1415926535897f; ++ } else { ++ pAsm->C[0].f = 1.0f; ++ pAsm->C[1].f = -0.5f; ++ } ++ ++ if(( GL_FALSE == next_ins(pAsm) )) ++ { ++ return GL_FALSE; ++ } + + // COS dst.x, a.x + pAsm->D.dst.opcode = SQ_OP2_INST_COS; +@@ -6154,7 +6285,7 @@ GLboolean callPreSub(r700_AssemblerBase* pAsm, + } + if(uNumValidSrc > 0) + { +- prelude_cf_ptr = pAsm->cf_current_alu_clause_ptr; ++ prelude_cf_ptr = (R700ControlFlowGenericClause*) pAsm->cf_current_alu_clause_ptr; + pAsm->alu_x_opcode = SQ_CF_INST_ALU; + } + +@@ -6274,7 +6405,7 @@ GLboolean callPreSub(r700_AssemblerBase* pAsm, + + next_ins(pAsm); + +- pAsm->callers[pAsm->unCallerArrayPointer - 1].finale_cf_ptr = pAsm->cf_current_alu_clause_ptr; ++ pAsm->callers[pAsm->unCallerArrayPointer - 1].finale_cf_ptr = (R700ControlFlowGenericClause*) pAsm->cf_current_alu_clause_ptr; + pAsm->callers[pAsm->unCallerArrayPointer - 1].prelude_cf_ptr = prelude_cf_ptr; + pAsm->alu_x_opcode = SQ_CF_INST_ALU; + } +@@ -6347,6 +6478,8 @@ GLboolean Process_Export(r700_AssemblerBase* pAsm, + + if (export_count == 1) + { ++ assert(starting_register_number >= pAsm->starting_export_register_number); ++ + ucWriteMask = pAsm->pucOutMask[starting_register_number - pAsm->starting_export_register_number]; + /* exports Z as a float into Red channel */ + if (GL_TRUE == is_depth_export) +@@ -6437,6 +6570,7 @@ GLboolean Process_Fragment_Exports(r700_AssemblerBase *pR700AsmCode, + { + unsigned int unBit; + GLuint export_count = 0; ++ unsigned int i; + + if(pR700AsmCode->depth_export_register_number >= 0) + { +@@ -6446,39 +6580,24 @@ GLboolean Process_Fragment_Exports(r700_AssemblerBase *pR700AsmCode, + } + } + +- unBit = 1 << FRAG_RESULT_COLOR; +- if(OutputsWritten & unBit) +- { +- if( GL_FALSE == Process_Export(pR700AsmCode, +- SQ_EXPORT_PIXEL, +- 0, +- 1, +- pR700AsmCode->uiFP_OutputMap[FRAG_RESULT_COLOR], +- GL_FALSE) ) +- { +- return GL_FALSE; +- } +- export_count++; +- } +- unBit = 1 << FRAG_RESULT_DEPTH; +- if(OutputsWritten & unBit) +- { +- if( GL_FALSE == Process_Export(pR700AsmCode, +- SQ_EXPORT_PIXEL, +- 0, +- 1, +- pR700AsmCode->uiFP_OutputMap[FRAG_RESULT_DEPTH], +- GL_TRUE)) ++ for (i = 0; i < FRAG_RESULT_MAX; ++i) ++ { ++ unBit = 1 << i; ++ ++ if (OutputsWritten & unBit) + { +- return GL_FALSE; ++ GLboolean is_depth = i == FRAG_RESULT_DEPTH ? GL_TRUE : GL_FALSE; ++ if (!Process_Export(pR700AsmCode, SQ_EXPORT_PIXEL, 0, 1, pR700AsmCode->uiFP_OutputMap[i], is_depth)) ++ return GL_FALSE; ++ ++export_count; + } +- export_count++; +- } ++ } ++ + /* Need to export something, otherwise we'll hang + * results are undefined anyway */ + if(export_count == 0) + { +- Process_Export(pR700AsmCode, SQ_EXPORT_PIXEL, 0, 1, 0, GL_FALSE); ++ Process_Export(pR700AsmCode, SQ_EXPORT_PIXEL, 0, 1, pR700AsmCode->starting_export_register_number, GL_FALSE); + } + + if(pR700AsmCode->cf_last_export_ptr != NULL) +@@ -6511,13 +6630,30 @@ GLboolean Process_Vertex_Exports(r700_AssemblerBase *pR700AsmCode, + { + return GL_FALSE; + } ++ export_starting_index++; ++ export_count--; ++ } + ++ unBit = 1 << VERT_RESULT_PSIZ; ++ if(OutputsWritten & unBit) ++ { ++ if( GL_FALSE == Process_Export(pR700AsmCode, ++ SQ_EXPORT_POS, ++ export_starting_index, ++ 1, ++ pR700AsmCode->ucVP_OutputMap[VERT_RESULT_PSIZ], ++ GL_FALSE) ) ++ { ++ return GL_FALSE; ++ } + export_count--; ++ } ++ ++ pR700AsmCode->cf_last_export_ptr->m_Word1.f.cf_inst = SQ_CF_INST_EXPORT_DONE; + +- pR700AsmCode->cf_last_export_ptr->m_Word1.f.cf_inst = SQ_CF_INST_EXPORT_DONE; +- } + + pR700AsmCode->number_of_exports = export_count; ++ export_starting_index = 0; + + unBit = 1 << VERT_RESULT_COL0; + if(OutputsWritten & unBit) +diff --git a/src/mesa/drivers/dri/r600/r700_assembler.h b/src/mesa/drivers/dri/r600/r700_assembler.h +index 0064d08..2d3c324 100644 +--- a/src/mesa/drivers/dri/r600/r700_assembler.h ++++ b/src/mesa/drivers/dri/r600/r700_assembler.h +@@ -582,7 +582,6 @@ GLboolean check_scalar(r700_AssemblerBase* pAsm, + GLboolean check_vector(r700_AssemblerBase* pAsm, + R700ALUInstruction* alu_instruction_ptr); + GLboolean assemble_alu_instruction(r700_AssemblerBase *pAsm); +-GLboolean next_ins(r700_AssemblerBase *pAsm); + + GLboolean pops(r700_AssemblerBase *pAsm, GLuint pops); + GLboolean jumpToOffest(r700_AssemblerBase *pAsm, GLuint pops, GLint offset); +diff --git a/src/mesa/drivers/dri/r600/r700_fragprog.c b/src/mesa/drivers/dri/r600/r700_fragprog.c +index 84d51e6..39234af 100644 +--- a/src/mesa/drivers/dri/r600/r700_fragprog.c ++++ b/src/mesa/drivers/dri/r600/r700_fragprog.c +@@ -226,22 +226,23 @@ void Map_Fragment_Program(r700_AssemblerBase *pAsm, + pAsm->number_of_exports = 0; + pAsm->number_of_colorandz_exports = 0; /* don't include stencil and mask out. */ + pAsm->starting_export_register_number = pAsm->number_used_registers; +- unBit = 1 << FRAG_RESULT_COLOR; +- if(mesa_fp->Base.OutputsWritten & unBit) +- { +- pAsm->uiFP_OutputMap[FRAG_RESULT_COLOR] = pAsm->number_used_registers++; +- pAsm->number_of_exports++; +- pAsm->number_of_colorandz_exports++; +- } +- unBit = 1 << FRAG_RESULT_DEPTH; +- if(mesa_fp->Base.OutputsWritten & unBit) +- { +- pAsm->depth_export_register_number = pAsm->number_used_registers; +- pAsm->uiFP_OutputMap[FRAG_RESULT_DEPTH] = pAsm->number_used_registers++; +- pAsm->number_of_exports++; +- pAsm->number_of_colorandz_exports++; +- pAsm->pR700Shader->depthIsExported = 1; +- } ++ ++ for (i = 0; i < FRAG_RESULT_MAX; ++i) ++ { ++ unBit = 1 << i; ++ if (mesa_fp->Base.OutputsWritten & unBit) ++ { ++ if (i == FRAG_RESULT_DEPTH) ++ { ++ pAsm->depth_export_register_number = pAsm->number_used_registers; ++ pAsm->pR700Shader->depthIsExported = 1; ++ } ++ ++ pAsm->uiFP_OutputMap[i] = pAsm->number_used_registers++; ++ ++pAsm->number_of_exports; ++ ++pAsm->number_of_colorandz_exports; ++ } ++ } + + pAsm->pucOutMask = (unsigned char*) MALLOC(pAsm->number_of_exports); + for(ui=0; ui<pAsm->number_of_exports; ui++) +@@ -560,27 +561,34 @@ GLboolean r700SetupFragmentProgram(GLcontext * ctx) + CLEARbit(r700->SPI_PS_IN_CONTROL_1.u32All, FRONT_FACE_ENA_bit); + } + +- /* see if we need any point_sprite replacements */ +- for (i = VERT_RESULT_TEX0; i<= VERT_RESULT_TEX7; i++) ++ /* see if we need any point_sprite replacements, also increase num_interp ++ * as there's no vp output for them */ ++ if (ctx->Point.PointSprite) + { +- if(ctx->Point.CoordReplace[i - VERT_RESULT_TEX0] == GL_TRUE) +- point_sprite = GL_TRUE; ++ for (i = FRAG_ATTRIB_TEX0; i<= FRAG_ATTRIB_TEX7; i++) ++ { ++ if (ctx->Point.CoordReplace[i - FRAG_ATTRIB_TEX0] == GL_TRUE) ++ { ++ ui++; ++ point_sprite = GL_TRUE; ++ } ++ } + } + ++ if( mesa_fp->Base.InputsRead & (1 << FRAG_ATTRIB_PNTC)) ++ ui++; ++ + if ((mesa_fp->Base.InputsRead & (1 << FRAG_ATTRIB_PNTC)) || point_sprite) + { +- /* for FRAG_ATTRIB_PNTC we need to increase num_interp */ +- if(mesa_fp->Base.InputsRead & (1 << FRAG_ATTRIB_PNTC)) +- { +- ui++; +- SETfield(r700->SPI_PS_IN_CONTROL_0.u32All, ui, NUM_INTERP_shift, NUM_INTERP_mask); +- } ++ SETfield(r700->SPI_PS_IN_CONTROL_0.u32All, ui, NUM_INTERP_shift, NUM_INTERP_mask); + SETbit(r700->SPI_INTERP_CONTROL_0.u32All, PNT_SPRITE_ENA_bit); + SETfield(r700->SPI_INTERP_CONTROL_0.u32All, SPI_PNT_SPRITE_SEL_S, PNT_SPRITE_OVRD_X_shift, PNT_SPRITE_OVRD_X_mask); + SETfield(r700->SPI_INTERP_CONTROL_0.u32All, SPI_PNT_SPRITE_SEL_T, PNT_SPRITE_OVRD_Y_shift, PNT_SPRITE_OVRD_Y_mask); + SETfield(r700->SPI_INTERP_CONTROL_0.u32All, SPI_PNT_SPRITE_SEL_0, PNT_SPRITE_OVRD_Z_shift, PNT_SPRITE_OVRD_Z_mask); + SETfield(r700->SPI_INTERP_CONTROL_0.u32All, SPI_PNT_SPRITE_SEL_1, PNT_SPRITE_OVRD_W_shift, PNT_SPRITE_OVRD_W_mask); +- if(ctx->Point.SpriteOrigin == GL_LOWER_LEFT) ++ /* Like e.g. viewport and winding, point sprite coordinates are ++ * inverted when rendering to FBO. */ ++ if ((ctx->Point.SpriteOrigin == GL_LOWER_LEFT) == !ctx->DrawBuffer->Name) + SETbit(r700->SPI_INTERP_CONTROL_0.u32All, PNT_SPRITE_TOP_1_bit); + else + CLEARbit(r700->SPI_INTERP_CONTROL_0.u32All, PNT_SPRITE_TOP_1_bit); +@@ -668,8 +676,9 @@ GLboolean r700SetupFragmentProgram(GLcontext * ctx) + + for(i=0; i<8; i++) + { ++ GLboolean coord_replace = ctx->Point.PointSprite && ctx->Point.CoordReplace[i]; + unBit = 1 << (VERT_RESULT_TEX0 + i); +- if(OutputsWritten & unBit) ++ if ((OutputsWritten & unBit) || coord_replace) + { + ui = pAsm->uiFP_AttributeMap[FRAG_ATTRIB_TEX0 + i]; + SETbit(r700->SPI_PS_INPUT_CNTL[ui].u32All, SEL_CENTROID_bit); +@@ -677,7 +686,7 @@ GLboolean r700SetupFragmentProgram(GLcontext * ctx) + SEMANTIC_shift, SEMANTIC_mask); + CLEARbit(r700->SPI_PS_INPUT_CNTL[ui].u32All, FLAT_SHADE_bit); + /* ARB_point_sprite */ +- if(ctx->Point.CoordReplace[i] == GL_TRUE) ++ if (coord_replace) + { + SETbit(r700->SPI_PS_INPUT_CNTL[ui].u32All, PT_SPRITE_TEX_bit); + } +diff --git a/src/mesa/drivers/dri/r600/r700_vertprog.c b/src/mesa/drivers/dri/r600/r700_vertprog.c +index 05c6516..14dd2a5 100644 +--- a/src/mesa/drivers/dri/r600/r700_vertprog.c ++++ b/src/mesa/drivers/dri/r600/r700_vertprog.c +@@ -628,6 +628,16 @@ GLboolean r700SetupVertexProgram(GLcontext * ctx) + + R600_STATECHANGE(context, spi); + ++ if(vp->mesa_program->Base.OutputsWritten & (1 << VERT_RESULT_PSIZ)) { ++ R600_STATECHANGE(context, cl); ++ SETbit(r700->PA_CL_VS_OUT_CNTL.u32All, USE_VTX_POINT_SIZE_bit); ++ SETbit(r700->PA_CL_VS_OUT_CNTL.u32All, VS_OUT_MISC_VEC_ENA_bit); ++ } else if (r700->PA_CL_VS_OUT_CNTL.u32All != 0) { ++ R600_STATECHANGE(context, cl); ++ CLEARbit(r700->PA_CL_VS_OUT_CNTL.u32All, USE_VTX_POINT_SIZE_bit); ++ CLEARbit(r700->PA_CL_VS_OUT_CNTL.u32All, VS_OUT_MISC_VEC_ENA_bit); ++ } ++ + SETfield(r700->SPI_VS_OUT_CONFIG.u32All, + vp->r700Shader.nParamExports ? (vp->r700Shader.nParamExports - 1) : 0, + VS_EXPORT_COUNT_shift, VS_EXPORT_COUNT_mask); +diff --git a/src/mesa/drivers/dri/radeon/radeon_chipset.h b/src/mesa/drivers/dri/radeon/radeon_chipset.h +index 98732c8..6dbfdc9 100644 +--- a/src/mesa/drivers/dri/radeon/radeon_chipset.h ++++ b/src/mesa/drivers/dri/radeon/radeon_chipset.h +@@ -413,9 +413,9 @@ enum { + CHIP_FAMILY_R350, + CHIP_FAMILY_RV350, + CHIP_FAMILY_RV380, ++ CHIP_FAMILY_RS400, + CHIP_FAMILY_R420, + CHIP_FAMILY_RV410, +- CHIP_FAMILY_RS400, + CHIP_FAMILY_RS600, + CHIP_FAMILY_RS690, + CHIP_FAMILY_RS740, +diff --git a/src/mesa/drivers/dri/radeon/radeon_common_context.c b/src/mesa/drivers/dri/radeon/radeon_common_context.c +index 94f4766..5a7d52c 100644 +--- a/src/mesa/drivers/dri/radeon/radeon_common_context.c ++++ b/src/mesa/drivers/dri/radeon/radeon_common_context.c +@@ -300,10 +300,10 @@ void radeonDestroyContext(__DRIcontext *driContextPriv ) + _mesa_meta_free(radeon->glCtx); + + if (radeon == current) { +- radeon_firevertices(radeon); + _mesa_make_current(NULL, NULL, NULL); + } + ++ radeon_firevertices(radeon); + if (!is_empty_list(&radeon->dma.reserved)) { + rcommonFlushCmdBuf( radeon, __FUNCTION__ ); + } +diff --git a/src/mesa/drivers/dri/radeon/radeon_mipmap_tree.c b/src/mesa/drivers/dri/radeon/radeon_mipmap_tree.c +index 6c570a5..f3ce852 100644 +--- a/src/mesa/drivers/dri/radeon/radeon_mipmap_tree.c ++++ b/src/mesa/drivers/dri/radeon/radeon_mipmap_tree.c +@@ -588,17 +588,17 @@ int radeon_validate_texture_miptree(GLcontext * ctx, struct gl_texture_object *t + __FUNCTION__, texObj ,t->minLod, t->maxLod); + + radeon_mipmap_tree *dst_miptree; +- dst_miptree = get_biggest_matching_miptree(t, t->minLod, t->maxLod); ++ dst_miptree = get_biggest_matching_miptree(t, t->base.BaseLevel, t->base.MaxLevel); + ++ radeon_miptree_unreference(&t->mt); + if (!dst_miptree) { +- radeon_miptree_unreference(&t->mt); + radeon_try_alloc_miptree(rmesa, t); +- dst_miptree = t->mt; + radeon_print(RADEON_TEXTURE, RADEON_NORMAL, + "%s: No matching miptree found, allocated new one %p\n", + __FUNCTION__, t->mt); + + } else { ++ radeon_miptree_reference(dst_miptree, &t->mt); + radeon_print(RADEON_TEXTURE, RADEON_NORMAL, + "%s: Using miptree %p\n", __FUNCTION__, t->mt); + } +@@ -615,7 +615,7 @@ int radeon_validate_texture_miptree(GLcontext * ctx, struct gl_texture_object *t + "Checking image level %d, face %d, mt %p ... ", + level, face, img->mt); + +- if (img->mt != dst_miptree) { ++ if (img->mt != t->mt) { + radeon_print(RADEON_TEXTURE, RADEON_TRACE, + "MIGRATING\n"); + +@@ -623,7 +623,7 @@ int radeon_validate_texture_miptree(GLcontext * ctx, struct gl_texture_object *t + if (src_bo && radeon_bo_is_referenced_by_cs(src_bo, rmesa->cmdbuf.cs)) { + radeon_firevertices(rmesa); + } +- migrate_image_to_miptree(dst_miptree, img, face, level); ++ migrate_image_to_miptree(t->mt, img, face, level); + } else + radeon_print(RADEON_TEXTURE, RADEON_TRACE, "OK\n"); + } +diff --git a/src/mesa/drivers/dri/radeon/radeon_tex_copy.c b/src/mesa/drivers/dri/radeon/radeon_tex_copy.c +index 5cfad6f..fb6f2e5 100644 +--- a/src/mesa/drivers/dri/radeon/radeon_tex_copy.c ++++ b/src/mesa/drivers/dri/radeon/radeon_tex_copy.c +@@ -52,22 +52,34 @@ do_copy_texsubimage(GLcontext *ctx, + gl_format dst_mesaformat; + unsigned src_width; + unsigned dst_width; ++ unsigned flip_y; + + if (!radeon->vtbl.blit) { + return GL_FALSE; + } + + if (_mesa_get_format_bits(timg->base.TexFormat, GL_DEPTH_BITS) > 0) { +- rrb = radeon_get_depthbuffer(radeon); ++ if (ctx->ReadBuffer->_DepthBuffer && ctx->ReadBuffer->_DepthBuffer->Wrapped) { ++ rrb = radeon_renderbuffer(ctx->ReadBuffer->_DepthBuffer->Wrapped); ++ } else { ++ rrb = radeon_renderbuffer(ctx->ReadBuffer->_DepthBuffer); ++ } ++ flip_y = ctx->ReadBuffer->Attachment[BUFFER_DEPTH].Type == GL_NONE; + } else { +- rrb = radeon_get_colorbuffer(radeon); ++ rrb = radeon_renderbuffer(ctx->ReadBuffer->_ColorReadBuffer); ++ flip_y = ctx->ReadBuffer->Attachment[BUFFER_COLOR0].Type == GL_NONE; ++ } ++ ++ // This is software renderbuffer, fallback to swrast ++ if (!rrb) { ++ return GL_FALSE; + } + + if (!timg->mt) { + radeon_validate_texture_miptree(ctx, &tobj->base); + } + +- assert(rrb && rrb->bo); ++ assert(rrb->bo); + assert(timg->mt); + assert(timg->mt->bo); + assert(timg->base.Width >= dstx + width); +@@ -124,7 +136,7 @@ do_copy_texsubimage(GLcontext *ctx, + timg->mt->bo, dst_offset, dst_mesaformat, + timg->mt->levels[level].rowstride / dst_bpp, + dst_width, timg->base.Height, +- dstx, dsty, width, height, 1); ++ dstx, dsty, width, height, flip_y); + } + + void +diff --git a/src/mesa/drivers/dri/radeon/radeon_texture.c b/src/mesa/drivers/dri/radeon/radeon_texture.c +index ff37fd3..caa55d2 100644 +--- a/src/mesa/drivers/dri/radeon/radeon_texture.c ++++ b/src/mesa/drivers/dri/radeon/radeon_texture.c +@@ -1016,7 +1016,15 @@ radeon_get_tex_image(GLcontext * ctx, GLenum target, GLint level, + __func__, ctx, texObj, image, compressed); + + if (image->mt) { ++ radeonContextPtr rmesa = RADEON_CONTEXT(ctx); + /* Map the texture image read-only */ ++ if (radeon_bo_is_referenced_by_cs(image->mt->bo, rmesa->cmdbuf.cs)) { ++ radeon_print(RADEON_TEXTURE, RADEON_VERBOSE, ++ "%s: called for texture that is queued for GPU processing\n", ++ __func__); ++ radeon_firevertices(rmesa); ++ } ++ + radeon_teximage_map(image, GL_FALSE); + } else { + /* Image hasn't been uploaded to a miptree yet */ +diff --git a/src/mesa/drivers/osmesa/osmesa.c b/src/mesa/drivers/osmesa/osmesa.c +index e376f12..c5e874b 100644 +--- a/src/mesa/drivers/osmesa/osmesa.c ++++ b/src/mesa/drivers/osmesa/osmesa.c +@@ -1061,7 +1061,6 @@ OSMesaCreateContextExt( GLenum format, GLint depthBits, GLint stencilBits, + struct dd_function_table functions; + GLint rind, gind, bind, aind; + GLint redBits = 0, greenBits = 0, blueBits = 0, alphaBits =0; +- GLenum type = CHAN_TYPE; + + rind = gind = bind = aind = 0; + if (format==OSMESA_RGBA) { +@@ -1329,6 +1328,7 @@ OSMesaMakeCurrent( OSMesaContext osmesa, void *buffer, GLenum type, + * size. + */ + osmesa->rb = new_osmesa_renderbuffer(&osmesa->mesa, osmesa->format, type); ++ _mesa_remove_renderbuffer(osmesa->gl_buffer, BUFFER_FRONT_LEFT); + _mesa_add_renderbuffer(osmesa->gl_buffer, BUFFER_FRONT_LEFT, osmesa->rb); + assert(osmesa->rb->RefCount == 2); + +diff --git a/src/mesa/main/bufferobj.c b/src/mesa/main/bufferobj.c +index 71d1514..98bb7fa 100644 +--- a/src/mesa/main/bufferobj.c ++++ b/src/mesa/main/bufferobj.c +@@ -1880,7 +1880,7 @@ _mesa_BufferObjectUnpurgeable(GLcontext *ctx, GLuint name, GLenum option) + + bufObj->Purgeable = GL_FALSE; + +- retval = GL_RETAINED_APPLE; ++ retval = option; + if (ctx->Driver.BufferObjectUnpurgeable) + retval = ctx->Driver.BufferObjectUnpurgeable(ctx, bufObj, option); + +@@ -1910,11 +1910,11 @@ _mesa_RenderObjectUnpurgeable(GLcontext *ctx, GLuint name, GLenum option) + + bufObj->Purgeable = GL_FALSE; + +- retval = GL_RETAINED_APPLE; ++ retval = option; + if (ctx->Driver.RenderObjectUnpurgeable) + retval = ctx->Driver.RenderObjectUnpurgeable(ctx, bufObj, option); + +- return option; ++ return retval; + } + + +@@ -1940,7 +1940,7 @@ _mesa_TextureObjectUnpurgeable(GLcontext *ctx, GLuint name, GLenum option) + + bufObj->Purgeable = GL_FALSE; + +- retval = GL_RETAINED_APPLE; ++ retval = option; + if (ctx->Driver.TextureObjectUnpurgeable) + retval = ctx->Driver.TextureObjectUnpurgeable(ctx, bufObj, option); + +diff --git a/src/mesa/main/context.c b/src/mesa/main/context.c +index 73126b9..1dc0c9c 100644 +--- a/src/mesa/main/context.c ++++ b/src/mesa/main/context.c +@@ -1358,6 +1358,8 @@ _mesa_make_current( GLcontext *newCtx, GLframebuffer *drawBuffer, + if (newCtx->FirstTimeCurrent) { + _mesa_compute_version(newCtx); + ++ newCtx->Extensions.String = _mesa_make_extension_string(newCtx); ++ + check_context_limits(newCtx); + + /* We can use this to help debug user's problems. Tell them to set +diff --git a/src/mesa/main/getstring.c b/src/mesa/main/getstring.c +index 51dd5f7..b9daaba 100644 +--- a/src/mesa/main/getstring.c ++++ b/src/mesa/main/getstring.c +@@ -71,8 +71,6 @@ _mesa_GetString( GLenum name ) + case GL_VERSION: + return (const GLubyte *) ctx->VersionString; + case GL_EXTENSIONS: +- if (!ctx->Extensions.String) +- ctx->Extensions.String = _mesa_make_extension_string(ctx); + return (const GLubyte *) ctx->Extensions.String; + #if FEATURE_ARB_shading_language_100 + case GL_SHADING_LANGUAGE_VERSION_ARB: +diff --git a/src/mesa/main/state.c b/src/mesa/main/state.c +index b971cc9..db84de2 100644 +--- a/src/mesa/main/state.c ++++ b/src/mesa/main/state.c +@@ -537,7 +537,7 @@ _mesa_update_state_locked( GLcontext *ctx ) + + /* Determine which state flags effect vertex/fragment program state */ + if (ctx->FragmentProgram._MaintainTexEnvProgram) { +- prog_flags |= (_NEW_TEXTURE | _NEW_FOG | ++ prog_flags |= (_NEW_BUFFERS | _NEW_TEXTURE | _NEW_FOG | + _NEW_ARRAY | _NEW_LIGHT | _NEW_POINT | _NEW_RENDERMODE | + _NEW_PROGRAM); + } +diff --git a/src/mesa/main/texenvprogram.c b/src/mesa/main/texenvprogram.c +index 964ba13..df09c12 100644 +--- a/src/mesa/main/texenvprogram.c ++++ b/src/mesa/main/texenvprogram.c +@@ -98,6 +98,7 @@ struct state_key { + GLuint fog_enabled:1; + GLuint fog_mode:2; /**< FOG_x */ + GLuint inputs_available:12; ++ GLuint num_draw_buffers:4; + + /* NOTE: This array of structs must be last! (see "keySize" below) */ + struct { +@@ -485,6 +486,9 @@ static GLuint make_state_key( GLcontext *ctx, struct state_key *key ) + inputs_referenced |= FRAG_BIT_FOGC; /* maybe */ + } + ++ /* _NEW_BUFFERS */ ++ key->num_draw_buffers = ctx->DrawBuffer->_NumColorDrawBuffers; ++ + key->inputs_available = (inputs_available & inputs_referenced); + + /* compute size of state key, ignoring unused texture units */ +@@ -1199,11 +1203,14 @@ emit_texenv(struct texenv_fragment_program *p, GLuint unit) + else + alpha_saturate = GL_FALSE; + +- /* If this is the very last calculation, emit direct to output reg: ++ /* If this is the very last calculation (and various other conditions ++ * are met), emit directly to the color output register. Otherwise, ++ * emit to a temporary register. + */ + if (key->separate_specular || + unit != p->last_tex_stage || + alpha_shift || ++ key->num_draw_buffers != 1 || + rgb_shift) + dest = get_temp( p ); + else +@@ -1438,10 +1445,10 @@ create_new_program(GLcontext *ctx, struct state_key *key, + p.program->Base.Parameters = _mesa_new_parameter_list(); + p.program->Base.InputsRead = 0x0; + +- if (ctx->DrawBuffer->_NumColorDrawBuffers == 1) ++ if (key->num_draw_buffers == 1) + p.program->Base.OutputsWritten = 1 << FRAG_RESULT_COLOR; + else { +- for (i = 0; i < ctx->DrawBuffer->_NumColorDrawBuffers; i++) ++ for (i = 0; i < key->num_draw_buffers; i++) + p.program->Base.OutputsWritten |= (1 << (FRAG_RESULT_DATA0 + i)); + } + +@@ -1493,8 +1500,8 @@ create_new_program(GLcontext *ctx, struct state_key *key, + + cf = get_source( &p, SRC_PREVIOUS, 0 ); + +- for (i = 0; i < ctx->DrawBuffer->_NumColorDrawBuffers; i++) { +- if (ctx->DrawBuffer->_NumColorDrawBuffers == 1) ++ for (i = 0; i < key->num_draw_buffers; i++) { ++ if (key->num_draw_buffers == 1) + out = make_ureg( PROGRAM_OUTPUT, FRAG_RESULT_COLOR ); + else { + out = make_ureg( PROGRAM_OUTPUT, FRAG_RESULT_DATA0 + i ); +diff --git a/src/mesa/main/version.h b/src/mesa/main/version.h +index 9955be2..af4e69f 100644 +--- a/src/mesa/main/version.h ++++ b/src/mesa/main/version.h +@@ -34,8 +34,8 @@ + /* Mesa version */ + #define MESA_MAJOR 7 + #define MESA_MINOR 8 +-#define MESA_PATCH 2 +-#define MESA_VERSION_STRING "7.8.2" ++#define MESA_PATCH 3 ++#define MESA_VERSION_STRING "7.8.3" + + /* To make version comparison easy */ + #define MESA_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) +diff --git a/src/mesa/shader/slang/slang_builtin.c b/src/mesa/shader/slang/slang_builtin.c +index 791e751..f265285 100644 +--- a/src/mesa/shader/slang/slang_builtin.c ++++ b/src/mesa/shader/slang/slang_builtin.c +@@ -399,7 +399,7 @@ lookup_statevar(const char *var, GLint index1, GLint index2, const char *field, + } + + if (isMatrix) { +- /* load all four columns of matrix */ ++ /* load all four rows (or columns) of matrix */ + GLint pos[4]; + GLuint j; + for (j = 0; j < 4; j++) { +@@ -489,9 +489,26 @@ emit_statevars(const char *name, int array_len, + tokens[0] = STATE_TEXGEN; + tokens[2] = STATE_TEXGEN_OBJECT_Q; + } ++ else if (strcmp(name, "gl_TextureMatrix") == 0) { ++ tokens[0] = STATE_TEXTURE_MATRIX; ++ tokens[4] = STATE_MATRIX_TRANSPOSE; ++ } ++ else if (strcmp(name, "gl_TextureMatrixInverse") == 0) { ++ tokens[0] = STATE_TEXTURE_MATRIX; ++ tokens[4] = STATE_MATRIX_INVTRANS; ++ } ++ else if (strcmp(name, "gl_TextureMatrixTranspose") == 0) { ++ tokens[0] = STATE_TEXTURE_MATRIX; ++ tokens[4] = 0; ++ } ++ else if (strcmp(name, "gl_TextureMatrixInverseTranspose") == 0) { ++ tokens[0] = STATE_TEXTURE_MATRIX; ++ tokens[4] = STATE_MATRIX_INVERSE; ++ } + else { + return -1; /* invalid array name */ + } ++ /* emit state vars for each array element */ + for (i = 0; i < array_len; i++) { + GLint p; + tokens[1] = i; +@@ -513,6 +530,19 @@ emit_statevars(const char *name, int array_len, + } + return pos; + } ++ else if (type->type == SLANG_SPEC_MAT4) { ++ /* unroll/emit 4 array rows (or columns) */ ++ slang_type_specifier vec4; ++ GLint i, p, pos = -1; ++ vec4.type = SLANG_SPEC_VEC4; ++ for (i = 0; i < 4; i++) { ++ tokens[2] = tokens[3] = i; /* row[i] (or column[i]) of matrix */ ++ p = emit_statevars(NULL, 0, &vec4, tokens, paramList); ++ if (pos == -1) ++ pos = p; ++ } ++ return pos; ++ } + else { + GLint pos; + assert(type->type == SLANG_SPEC_VEC4 || +diff --git a/src/mesa/shader/slang/slang_emit.c b/src/mesa/shader/slang/slang_emit.c +index 7c0ea0c..4155052 100644 +--- a/src/mesa/shader/slang/slang_emit.c ++++ b/src/mesa/shader/slang/slang_emit.c +@@ -2362,7 +2362,10 @@ emit(slang_emit_info *emitInfo, slang_ir_node *n) + #if 0 + assert(!n->Store); + #endif +- n->Store = n->Children[1]->Store; ++ if (n->Children[1]->Store) ++ n->Store = n->Children[1]->Store; ++ else ++ n->Store = n->Children[0]->Store; + return inst; + + case IR_SCOPE: +@@ -2370,6 +2373,7 @@ emit(slang_emit_info *emitInfo, slang_ir_node *n) + _slang_push_var_table(emitInfo->vt); + inst = emit(emitInfo, n->Children[0]); + _slang_pop_var_table(emitInfo->vt); ++ n->Store = n->Children[0]->Store; + return inst; + + case IR_VAR_DECL: +diff --git a/src/mesa/state_tracker/st_cb_drawpixels.c b/src/mesa/state_tracker/st_cb_drawpixels.c +index 5fcfa86..a64c561 100644 +--- a/src/mesa/state_tracker/st_cb_drawpixels.c ++++ b/src/mesa/state_tracker/st_cb_drawpixels.c +@@ -1135,7 +1135,7 @@ st_destroy_drawpix(struct st_context *st) + st_reference_fragprog(st, &st->drawpix.z_shader, NULL); + st_reference_fragprog(st, &st->pixel_xfer.combined_prog, NULL); + if (st->drawpix.vert_shaders[0]) +- free(st->drawpix.vert_shaders[0]); ++ ureg_free_tokens(st->drawpix.vert_shaders[0]); + if (st->drawpix.vert_shaders[1]) +- free(st->drawpix.vert_shaders[1]); ++ ureg_free_tokens(st->drawpix.vert_shaders[1]); + } +diff --git a/src/mesa/state_tracker/st_cb_fbo.c b/src/mesa/state_tracker/st_cb_fbo.c +index 00e9d1d..df5674d 100644 +--- a/src/mesa/state_tracker/st_cb_fbo.c ++++ b/src/mesa/state_tracker/st_cb_fbo.c +@@ -460,26 +460,38 @@ static void + st_validate_framebuffer(GLcontext *ctx, struct gl_framebuffer *fb) + { + struct pipe_screen *screen = ctx->st->pipe->screen; +- const struct gl_renderbuffer *depthRb = +- fb->Attachment[BUFFER_DEPTH].Renderbuffer; +- const struct gl_renderbuffer *stencilRb = +- fb->Attachment[BUFFER_STENCIL].Renderbuffer; ++ const struct gl_renderbuffer_attachment *depth = ++ &fb->Attachment[BUFFER_DEPTH]; ++ const struct gl_renderbuffer_attachment *stencil = ++ &fb->Attachment[BUFFER_STENCIL]; + GLuint i; + +- if (stencilRb && depthRb && stencilRb != depthRb) { ++ if (depth->Type && stencil->Type && depth->Type != stencil->Type) { ++ fb->_Status = GL_FRAMEBUFFER_UNSUPPORTED_EXT; ++ return; ++ } ++ if (depth->Type == GL_RENDERBUFFER_EXT && ++ stencil->Type == GL_RENDERBUFFER_EXT && ++ depth->Renderbuffer != stencil->Renderbuffer) { ++ fb->_Status = GL_FRAMEBUFFER_UNSUPPORTED_EXT; ++ return; ++ } ++ if (depth->Type == GL_TEXTURE && ++ stencil->Type == GL_TEXTURE && ++ depth->Texture != stencil->Texture) { + fb->_Status = GL_FRAMEBUFFER_UNSUPPORTED_EXT; + return; + } + + if (!st_validate_attachment(screen, +- &fb->Attachment[BUFFER_DEPTH], +- PIPE_TEXTURE_USAGE_DEPTH_STENCIL)) { ++ depth, ++ PIPE_TEXTURE_USAGE_DEPTH_STENCIL)) { + fb->_Status = GL_FRAMEBUFFER_UNSUPPORTED_EXT; + return; + } + if (!st_validate_attachment(screen, +- &fb->Attachment[BUFFER_STENCIL], +- PIPE_TEXTURE_USAGE_DEPTH_STENCIL)) { ++ stencil, ++ PIPE_TEXTURE_USAGE_DEPTH_STENCIL)) { + fb->_Status = GL_FRAMEBUFFER_UNSUPPORTED_EXT; + return; + } +diff --git a/src/mesa/state_tracker/st_cb_readpixels.c b/src/mesa/state_tracker/st_cb_readpixels.c +index 952d9ce..7ff10d4 100644 +--- a/src/mesa/state_tracker/st_cb_readpixels.c ++++ b/src/mesa/state_tracker/st_cb_readpixels.c +@@ -69,6 +69,10 @@ st_read_stencil_pixels(GLcontext *ctx, GLint x, GLint y, + ubyte *stmap; + GLint j; + ++ if (strb->Base.Wrapped) { ++ strb = st_renderbuffer(strb->Base.Wrapped); ++ } ++ + if (st_fb_orientation(ctx->DrawBuffer) == Y_0_TOP) { + y = ctx->DrawBuffer->Height - y - height; + } +@@ -366,6 +370,9 @@ st_readpixels(GLcontext *ctx, GLint x, GLint y, GLsizei width, GLsizei height, + } + else if (format == GL_DEPTH_COMPONENT) { + strb = st_renderbuffer(ctx->ReadBuffer->_DepthBuffer); ++ if (strb->Base.Wrapped) { ++ strb = st_renderbuffer(strb->Base.Wrapped); ++ } + } + else { + /* Read color buffer */ +diff --git a/src/mesa/state_tracker/st_mesa_to_tgsi.c b/src/mesa/state_tracker/st_mesa_to_tgsi.c +index d7ef57e..d08af62 100644 +--- a/src/mesa/state_tracker/st_mesa_to_tgsi.c ++++ b/src/mesa/state_tracker/st_mesa_to_tgsi.c +@@ -683,8 +683,12 @@ emit_adjusted_wpos( struct st_translate *t, + struct ureg_dst wpos_temp = ureg_DECL_temporary(ureg); + struct ureg_src wpos_input = t->inputs[t->inputMapping[FRAG_ATTRIB_WPOS]]; + +- ureg_ADD(ureg, ureg_writemask(wpos_temp, TGSI_WRITEMASK_X | TGSI_WRITEMASK_Y), +- wpos_input, ureg_imm1f(ureg, value)); ++ /* Note that we bias X and Y and pass Z and W through unchanged. ++ * The shader might also use gl_FragCoord.w and .z. ++ */ ++ ureg_ADD(ureg, ++ ureg_writemask(wpos_temp, TGSI_WRITEMASK_XYZW), ++ wpos_input, ureg_imm4f(ureg, value, value, 0.0f, 0.0f)); + + t->inputs[t->inputMapping[FRAG_ATTRIB_WPOS]] = ureg_src(wpos_temp); + } +diff --git a/src/mesa/swrast/s_span.c b/src/mesa/swrast/s_span.c +index 687c8eb..32d4f52 100644 +--- a/src/mesa/swrast/s_span.c ++++ b/src/mesa/swrast/s_span.c +@@ -971,6 +971,10 @@ shade_texture_span(GLcontext *ctx, SWspan *span) + if (span->primitive == GL_BITMAP && span->array->ChanType != GL_FLOAT) { + convert_color_type(span, GL_FLOAT, 0); + } ++ else { ++ span->array->rgba = (void *) span->array->attribs[FRAG_ATTRIB_COL0]; ++ } ++ + if (span->primitive != GL_POINT || + (span->interpMask & SPAN_RGBA) || + ctx->Point.PointSprite) { +@@ -1222,9 +1226,22 @@ _swrast_write_rgba_span( GLcontext *ctx, SWspan *span) + GLchan rgbaSave[MAX_WIDTH][4]; + const GLuint fragOutput = multiFragOutputs ? buf : 0; + ++ /* set span->array->rgba to colors for render buffer's datatype */ + if (rb->DataType != span->array->ChanType || fragOutput > 0) { + convert_color_type(span, rb->DataType, fragOutput); + } ++ else { ++ if (rb->DataType == GL_UNSIGNED_BYTE) { ++ span->array->rgba = span->array->rgba8; ++ } ++ else if (rb->DataType == GL_UNSIGNED_SHORT) { ++ span->array->rgba = (void *) span->array->rgba16; ++ } ++ else { ++ span->array->rgba = (void *) ++ span->array->attribs[FRAG_ATTRIB_COL0]; ++ } ++ } + + if (!multiFragOutputs && numBuffers > 1) { + /* save colors for second, third renderbuffer writes */ +diff --git a/src/mesa/swrast_setup/ss_context.c b/src/mesa/swrast_setup/ss_context.c +index ebd1574..0fcb7c7 100644 +--- a/src/mesa/swrast_setup/ss_context.c ++++ b/src/mesa/swrast_setup/ss_context.c +@@ -115,7 +115,7 @@ setup_vertex_format(GLcontext *ctx) + GLboolean intColors = !ctx->FragmentProgram._Current + && !ctx->ATIFragmentShader._Enabled + && ctx->RenderMode == GL_RENDER +- && CHAN_TYPE == GL_UNSIGNED_BYTE; ++ && CHAN_TYPE != GL_FLOAT; + + if (intColors != swsetup->intColors || + !RENDERINPUTS_EQUAL(tnl->render_inputs_bitset, +diff --git a/src/mesa/swrast_setup/ss_triangle.c b/src/mesa/swrast_setup/ss_triangle.c +index bad0d81..f22bc52 100644 +--- a/src/mesa/swrast_setup/ss_triangle.c ++++ b/src/mesa/swrast_setup/ss_triangle.c +@@ -159,7 +159,7 @@ static void _swsetup_render_tri(GLcontext *ctx, + } + + #define SS_COLOR(a,b) UNCLAMPED_FLOAT_TO_RGBA_CHAN(a,b) +-#define SS_SPEC(a,b) UNCLAMPED_FLOAT_TO_RGB_CHAN(a,b) ++#define SS_SPEC(a,b) COPY_4V(a,b) + #define SS_IND(a,b) (a = b) + + #define IND (0) diff --git a/main/mesa/mesa.post-deinstall b/main/mesa/mesa.post-deinstall new file mode 100644 index 000000000..1ac348c3b --- /dev/null +++ b/main/mesa/mesa.post-deinstall @@ -0,0 +1,6 @@ +#!/bin/sh + +LIBGL="/usr/lib/libGL.so" +if [ -h "$LIBGL" ] && [ ! -e "$LIBGL" ]; then + rm -f "$LIBGL" +fi diff --git a/main/mesa/mesa.post-install b/main/mesa/mesa.post-install new file mode 100644 index 000000000..0f6a6a0c5 --- /dev/null +++ b/main/mesa/mesa.post-install @@ -0,0 +1,3 @@ +#!/bin/sh + +ln -s /usr/lib/libGL.mesa.so.1.2 /usr/lib/libGL.so diff --git a/main/nvidia-linux-xbmc/APKBUILD b/main/nvidia-linux-xbmc/APKBUILD new file mode 100644 index 000000000..67b8f0a32 --- /dev/null +++ b/main/nvidia-linux-xbmc/APKBUILD @@ -0,0 +1,44 @@ +# Contributor: Carlo Landmeter +# Maintainer: + +_flavor=xbmc + +# source the kernel version +if [ -f ../linux-${_flavor}/APKBUILD ]; then + . ../linux-${_flavor}/APKBUILD +fi + +_kernelver="$pkgver-r$pkgrel" +_abi_release=${pkgver}-${_flavor} +_kpkgrel=$pkgrel +_realname=nvidia-linux + +pkgname=${_realname}-${_flavor} +pkgver=256.53 +pkgrel=3 +pkgdesc="NVIDIA binary driver $pkgver" +url="http://www.nvidia.com/" +license="custom" +subpackages="" +depends="linux-${_flavor}=${_kernelver}" +makedepends="linux-${_flavor}-dev=${_kernelver}" +source="ftp://download.nvidia.com/XFree86/Linux-x86/$pkgver/NVIDIA-Linux-x86-$pkgver.run" + +prepare() { + cd "$srcdir" + sh NVIDIA-Linux-x86-${pkgver}.run --extract-only +} + +build() { + cd "$srcdir/NVIDIA-Linux-x86-$pkgver/kernel" + make SYSSRC="/lib/modules/$_abi_release/build" module +} + +package() { + install -D -m644 "$srcdir/NVIDIA-Linux-x86-$pkgver/kernel/nvidia.ko" \ + "$pkgdir/lib/modules/$_abi_release/kernel/drivers/video/nvidia.ko" + install -d -m755 $pkgdir/etc/modprobe.d + echo "blacklist nouveau" >> "$pkgdir"/etc/modprobe.d/nouveau_blacklist.conf +} + +md5sums="21fe3fe0afed7818b7adf383477b2155 NVIDIA-Linux-x86-256.53.run" diff --git a/main/nvidia/APKBUILD b/main/nvidia/APKBUILD new file mode 100644 index 000000000..4f8583025 --- /dev/null +++ b/main/nvidia/APKBUILD @@ -0,0 +1,94 @@ +# Contributor: Carlo Landmeter +# Maintainer: +pkgname=nvidia +pkgver=256.53 +pkgrel=0 +pkgdesc="NVIDIA tools xorg-driver libs and others" +url="http://www.nvidia.com/" +license="Custom" +depends= +install="" +makedepends="libx11 libxext atk gtk+ libxv libxvmc" +subpackages="$pkgname-tools $pkgname-opencl $pkgname-cuda $pkgname-tls \ + $pkgname-vdpau $pkgname-xvmc $pkgname-opengl $pkgname-glx $pkgname-xorg-driver:driver" +source="ftp://download.nvidia.com/XFree86/Linux-x86/$pkgver/NVIDIA-Linux-x86-$pkgver.run" + +_builddir="$srcdir/NVIDIA-Linux-x86-$pkgver" + +prepare() { + cd "$srcdir" + sh NVIDIA-Linux-x86-${pkgver}.run --extract-only +} + +package() { + # dummy package + cd "$_builddir" + mkdir -p "$pkgdir"/usr +} + +tools() { + pkgdesc="NVIDIA binary driver tools" + cd "$_builddir" + install -D -m755 nvidia-xconfig "$subpkgdir"/usr/bin/nvidia-xconfig + install -D -m755 nvidia-settings "$subpkgdir"/usr/bin/nvidia-settings + install -D -m644 nvidia-settings.desktop "$subpkgdir"/usr/share/applications/nvidia-settings.desktop + install -D -m644 nvidia-settings.png "$subpkgdir"/usr/share/pixmaps/nvidia-settings.png + install -D -m755 nvidia-bug-report.sh "$subpkgdir"/usr/bin/nvidia-bug-report.sh + install -D -m755 nvidia-smi "$subpkgdir"/usr/bin/nvidia-smi +} + +opencl() { + pkgdesc="NVIDIA OpenCP libraries" + cd "$_builddir" + install -D -m755 libnvidia-compiler.so."$pkgver" "$subpkgdir"/usr/lib/libnvidia-compiler.so."$pkgver" + install -D -m755 libOpenCL.so.1.0.0 "$subpkgdir"/usr/lib/libOpenCL.so.1.0.0 + install -D -m644 nvidia.icd "$subpkgdir"/etc/OpenCL/vendors/nvidia.icd +} + +cuda() { + pkgdesc="NVIDIA CUDA library" + cd "$_builddir" + install -D -m755 "libcuda.so.$pkgver" "$subpkgdir/usr/lib/libcuda.so.$pkgver" +} + +tls() { + pkgdesc="NVIDIA TLS library" + cd "$_builddir" + install -D -m755 "tls/libnvidia-tls.so.$pkgver" "$subpkgdir/usr/lib/libnvidia-tls.so.$pkgver" +} + +vdpau() { + pkgdesc="NVIDIA VDPAU library" + cd "$_builddir" + install -D -m755 "libvdpau_nvidia.so.$pkgver" "$subpkgdir/usr/lib/vdpau/libvdpau_nvidia.so.$pkgver" +} + +xvmc() { + pkgdesc="NVIDIA XvMC library" + cd "$_builddir" + install -D -m755 "libXvMCNVIDIA.so.$pkgver" "$subpkgdir/usr/lib/libXvMCNVIDIA.so.$pkgver" +} + +opengl() { + pkgdesc="NVIDIA OpenGL library" + install="nvidia-opengl.post-install nvidia-opengl.post-deinstall" + cd "$_builddir" + install -D -m755 "libnvidia-glcore.so.$pkgver" "$subpkgdir/usr/lib/libnvidia-glcore.so.$pkgver" + install -D -m755 "libGL.so.$pkgver" "$subpkgdir/usr/lib/libGL.nvidia.so" +} + +glx() { + pkgdesc="NVIDIA OpenGL library" + install="nvidia-glx.post-install nvidia-glx.post-deinstall" + cd "$_builddir" + install -D -m755 "libglx.so.$pkgver" "$subpkgdir/usr/lib/xorg/modules/extensions/libglx.nvidia.so" + #ln -s "libglx.so.$pkgver" "$subpkgdir"/usr/lib/xorg/modules/extensions/libglx.so +} + +driver() { + pkgdesc="NVIDIA Xorg driver" + cd "$_builddir" + install -D -m755 nvidia_drv.so "$subpkgdir"/usr/lib/xorg/modules/drivers/nvidia_drv.so +} + +md5sums="21fe3fe0afed7818b7adf383477b2155 NVIDIA-Linux-x86-256.53.run" diff --git a/main/nvidia/nvidia-glx.post-deinstall b/main/nvidia/nvidia-glx.post-deinstall new file mode 100644 index 000000000..17f14ea4a --- /dev/null +++ b/main/nvidia/nvidia-glx.post-deinstall @@ -0,0 +1,10 @@ +#!/bin/sh + +cd "/usr/lib/xorg/modules/extensions" +if [ -e "libglx_xorg.so" ]; then + echo "Restoring xorg-server libglx" + ln -fs libglx_xorg.so libglx.so +elif [ -h "libglx.so" ]; then + rm libglx.so +fi + diff --git a/main/nvidia/nvidia-glx.post-install b/main/nvidia/nvidia-glx.post-install new file mode 100644 index 000000000..1d33a73a2 --- /dev/null +++ b/main/nvidia/nvidia-glx.post-install @@ -0,0 +1,12 @@ +#!/bin/sh + +echo "Linking libglx to NVIDIA overwriting any previous linking" +cd "/usr/lib/xorg/modules/extensions" +if [ -h libglx.so ]; then + echo "Switching to NVIDIA libglx" + ln -fs libglx.nvidia.so libglx.so +elif [ -f libglx.so ]; then + echo "Warning: libglx.so is a regular file. Please fix this!!!" +else + ln -fs libglx.nvidia.so libglx.so +fi diff --git a/main/nvidia/nvidia-opengl.post-deinstall b/main/nvidia/nvidia-opengl.post-deinstall new file mode 100644 index 000000000..7da7a1bfc --- /dev/null +++ b/main/nvidia/nvidia-opengl.post-deinstall @@ -0,0 +1,9 @@ +#!/bin/sh + +cd "/usr/lib" +if [ -e "libGL.mesa.so.1.2" ]; then + echo "Restoring MESA libgl" + ln -fs libGL.mesa.so.1.2 libGL.so.1 +elif [ -h "libGL.so.1" ]; then + rm libGL.so.1 +fi diff --git a/main/nvidia/nvidia-opengl.post-install b/main/nvidia/nvidia-opengl.post-install new file mode 100644 index 000000000..e6f97e2d4 --- /dev/null +++ b/main/nvidia/nvidia-opengl.post-install @@ -0,0 +1,5 @@ +#!/bin/sh + +echo "Linking libgl to NVIDIA overwriting any previous linking" +cd "/usr/lib" +ln -fs libGL.nvidia.so libGL.so.1 diff --git a/main/samba-xbmc/APKBUILD b/main/samba-xbmc/APKBUILD new file mode 100644 index 000000000..ec7c513aa --- /dev/null +++ b/main/samba-xbmc/APKBUILD @@ -0,0 +1,81 @@ +# Maintainer: Natanael Copa <ncopa@alpinelinux.org> +pkgname=samba-xbmc +pkgver=3.5.5 +pkgrel=0 +pkgdesc="Samba for XMBC with less deps" +url="http://www.samba.org" +license="GPL3" +subpackages="$pkgname-dev $pkgname-doc $pkgname-libs" +makedepends="popt-dev" +source="http://us1.samba.org/samba/ftp/stable/samba-$pkgver.tar.gz" + +_builddir="$srcdir"/samba-$pkgver + +prepare() { + cd "$_builddir" +} + +build() { + cd "$_builddir"/source3 + ./configure --prefix=/usr \ + --sysconfdir=/etc/samba \ + --with-configdir=/etc/samba \ + --localstatedir=/var \ + --with-lockdir=/var/cache/samba \ + --with-piddir=/var/run/samba \ + --with-logfilebase=/var/log/samba \ + --with-privatedir=/var/run \ + --enable-shared \ + --disable-static \ + --enable-shared-libs \ + --enable-debug \ + --disable-swat \ + --disable-cups \ + --disable-iprint \ + --with-fhs \ + --enable-pie \ + --with-pthreads \ + --with-sqlite3 \ + --enable-relro \ + --disable-dnssd \ + $SMB_AVAHI \ + --without-pam \ + --without-ads \ + --without-libtalloc \ + --without-libtdb \ + --without-libnetapi \ + --with-cifsmount \ + --with-cifsumount \ + --without-ldap \ + --without-acl-support \ + --with-winbind \ + --without-dnsupdate \ + --without-automount \ + --with-included-popt \ + --with-libsmbclient \ + --with-libsmbsharemodes \ + --without-quotas \ + --without-sys-quotas \ + --with-syslog \ + --with-utmp \ + --with-sendfile-support + + make || return 1 +} + +package() { + cd "$_builddir"/source3 + make DESTDIR="$pkgdir" install + #mkdir -p "$pkgdir"/usr/bin "$pkgdir"/usr/lib + #for i in smbtree mount.cifs umount.cifs; do + # cp bin/"$i" "$pkgdir"/usr/bin + #done +} + +libs() { + pkgdesc="Libs for xbmc samba" + mkdir -p "$subpkgdir"/usr + mv "$pkgdir"/usr/lib "$subpkgdir"/usr/ +} + +md5sums="278728aeeef9db7e27fa6a2ce5b43509 samba-3.5.5.tar.gz" diff --git a/main/squashfs-tools-lzma/APKBUILD b/main/squashfs-tools-lzma/APKBUILD new file mode 100644 index 000000000..fc16a96af --- /dev/null +++ b/main/squashfs-tools-lzma/APKBUILD @@ -0,0 +1,48 @@ +# Contributor: Carlo Landmeter +# Maintainer: + +pkgname=squashfs-tools-lzma +_pkgname=squashfs-tools +pkgver=4.0 +pkgrel=0 +pkgdesc="Tools for squashfs, a highly compressed read-only filesystem for Linux. LZMA version." +url="http://squashfs.sourceforge.net" +license="GPL" +makedepends="zlib-dev" +source="http://downloads.sourceforge.net/sourceforge/squashfs/squashfs$pkgver.tar.gz + http://downloads.sourceforge.net/sourceforge/sevenzip/lzma465.tar.bz2 + squashfs-tools-4.0-lzma.patch +" + +_builddir="$srcdir"/squashfs"$pkgver"/squashfs-tools + +prepare() { + cd "$_builddir" || return 1 + for i in $source; do + case $i in + *.patch) patch -p1 -i "$srcdir"/$i || return 1 + esac + done + + sed -i \ + -e 's:-O2:$(CFLAGS):' \ + -e '/-lz/s:$: $(LDFLAGS):' \ + Makefile || return 1 + sed -i -e 's:get_nprocs():sysconf(_SC_NPROCESSORS_ONLN):' *.c || return 1 + +} + +build() { + cd "$_builddir" + make -C "$srcdir"/C/LzmaUtil -f makefile.gcc || return 1 + make LZMA_LIB="$srcdir"/C/LzmaUtil/liblzma.a USE_LZMA=1 || return 1 +} + +package() { + install -D -m755 "$srcdir/squashfs$pkgver/$_pkgname/mksquashfs" "$pkgdir"/sbin/mksquashfs.lzma + install -D -m755 "$srcdir/squashfs$pkgver/$_pkgname/unsquashfs" "$pkgdir"/sbin/unsquashfs.lzma +} + +md5sums="a3c23391da4ebab0ac4a75021ddabf96 squashfs4.0.tar.gz +29d5ffd03a5a3e51aef6a74e9eafb759 lzma465.tar.bz2 +124c0c82f921737655963cc6db139765 squashfs-tools-4.0-lzma.patch" diff --git a/main/squashfs-tools-lzma/squashfs-tools-4.0-lzma.patch b/main/squashfs-tools-lzma/squashfs-tools-4.0-lzma.patch new file mode 100644 index 000000000..143e5faa7 --- /dev/null +++ b/main/squashfs-tools-lzma/squashfs-tools-4.0-lzma.patch @@ -0,0 +1,2210 @@ +diff -Naurp squashfs-tools/compressor.c squashfs-tools-lzma/compressor.c +--- squashfs-tools/compressor.c 1970-01-01 00:00:00.000000000 +0000 ++++ squashfs-tools/compressor.c 2009-10-20 04:03:37.000000000 +0000 +@@ -0,0 +1,78 @@ ++/* ++ * ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 ++ * Phillip Lougher <phillip@lougher.demon.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * compressor.c ++ */ ++ ++#include <stdio.h> ++#include <string.h> ++#include "compressor.h" ++#include "squashfs_fs.h" ++ ++extern int gzip_compress(void **, char *, char *, int, int, int *); ++extern int gzip_uncompress(char *, char *, int, int, int *); ++extern int lzma_compress(void **, char *, char *, int, int, int *); ++extern int lzma_uncompress(char *, char *, int, int, int *); ++ ++struct compressor compressor[] = { ++ { gzip_compress, gzip_uncompress, ZLIB_COMPRESSION, "gzip", 1 }, ++#ifdef LZMA_SUPPORT ++ { lzma_compress, lzma_uncompress, LZMA_COMPRESSION, "lzma", 1 }, ++#else ++ { NULL, NULL, LZMA_COMPRESSION, "lzma", 0 }, ++#endif ++ { NULL, NULL , 0, "unknown", 0} ++}; ++ ++ ++struct compressor *lookup_compressor(char *name) ++{ ++ int i; ++ ++ for(i = 0; compressor[i].id; i++) ++ if(strcmp(compressor[i].name, name) == 0) ++ break; ++ ++ return &compressor[i]; ++} ++ ++ ++struct compressor *lookup_compressor_id(int id) ++{ ++ int i; ++ ++ for(i = 0; compressor[i].id; i++) ++ if(id == compressor[i].id) ++ break; ++ ++ return &compressor[i]; ++} ++ ++ ++void display_compressors(char *indent, char *def_comp) ++{ ++ int i; ++ ++ for(i = 0; compressor[i].id; i++) ++ if(compressor[i].supported) ++ fprintf(stderr, "%s\t%s%s\n", indent, ++ compressor[i].name, ++ strcmp(compressor[i].name, def_comp) == 0 ? ++ " (default)" : ""); ++} +diff -Naurp squashfs-tools/compressor.h squashfs-tools-lzma/compressor.h +--- squashfs-tools/compressor.h 1970-01-01 00:00:00.000000000 +0000 ++++ squashfs-tools/compressor.h 2009-10-20 04:03:37.000000000 +0000 +@@ -0,0 +1,33 @@ ++/* ++ * ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 ++ * Phillip Lougher <phillip@lougher.demon.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * compressor.h ++ */ ++ ++struct compressor { ++ int (*compress)(void **, char *, char *, int, int, int *); ++ int (*uncompress)(char *, char *, int, int, int *); ++ int id; ++ char *name; ++ int supported; ++}; ++ ++extern struct compressor *lookup_compressor(char *); ++extern struct compressor *lookup_compressor_id(int); ++extern void display_compressors(char *, char *); +diff -Naurp squashfs-tools/gzip_wrapper.c squashfs-tools-lzma/gzip_wrapper.c +--- squashfs-tools/gzip_wrapper.c 1970-01-01 00:00:00.000000000 +0000 ++++ squashfs-tools/gzip_wrapper.c 2009-10-20 04:03:37.000000000 +0000 +@@ -0,0 +1,80 @@ ++/* ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 ++ * Phillip Lougher <phillip@lougher.demon.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * gzip_wrapper.c ++ */ ++ ++#include <stdlib.h> ++#include <zlib.h> ++ ++int gzip_compress(void **strm, char *d, char *s, int size, int block_size, ++ int *error) ++{ ++ int res = 0; ++ z_stream *stream = *strm; ++ ++ if(stream == NULL) { ++ if((stream = *strm = malloc(sizeof(z_stream))) == NULL) ++ goto failed; ++ ++ stream->zalloc = Z_NULL; ++ stream->zfree = Z_NULL; ++ stream->opaque = 0; ++ ++ if((res = deflateInit(stream, 9)) != Z_OK) ++ goto failed; ++ } else if((res = deflateReset(stream)) != Z_OK) ++ goto failed; ++ ++ stream->next_in = (unsigned char *) s; ++ stream->avail_in = size; ++ stream->next_out = (unsigned char *) d; ++ stream->avail_out = block_size; ++ ++ res = deflate(stream, Z_FINISH); ++ if(res == Z_STREAM_END) ++ /* ++ * Success, return the compressed size. ++ */ ++ return (int) stream->total_out; ++ if(res == Z_OK) ++ /* ++ * Output buffer overflow. Return out of buffer space ++ */ ++ return 0; ++failed: ++ /* ++ * All other errors return failure, with the compressor ++ * specific error code in *error ++ */ ++ *error = res; ++ return -1; ++} ++ ++ ++int gzip_uncompress(char *d, char *s, int size, int block_size, int *error) ++{ ++ int res; ++ unsigned long bytes = block_size; ++ ++ res = uncompress((unsigned char *) d, &bytes, ++ (const unsigned char *) s, size); ++ ++ *error = res; ++ return res == Z_OK ? (int) bytes : -1; ++} +diff -Naurp squashfs-tools/lzma_wrapper.c squashfs-tools-lzma/lzma_wrapper.c +--- squashfs-tools/lzma_wrapper.c 1970-01-01 00:00:00.000000000 +0000 ++++ squashfs-tools/lzma_wrapper.c 2009-10-14 03:32:57.000000000 +0000 +@@ -0,0 +1,93 @@ ++/* ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 ++ * Phillip Lougher <phillip@lougher.demon.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * lzma_wrapper.c ++ */ ++ ++#include <LzmaLib.h> ++ ++#define LZMA_HEADER_SIZE (LZMA_PROPS_SIZE + 8) ++ ++int lzma_compress(void **strm, char *dest, char *src, int size,int block_size, ++ int *error) ++{ ++ unsigned char *d = (unsigned char *) dest, *s = (unsigned char *) src; ++ size_t props_size = LZMA_PROPS_SIZE, ++ outlen = block_size - LZMA_HEADER_SIZE; ++ int res; ++ ++ res = LzmaCompress(d + LZMA_HEADER_SIZE, &outlen, s, size, d, ++ &props_size, 5, block_size, 3, 0, 2, 32, 1); ++ ++ if(res == SZ_ERROR_OUTPUT_EOF) { ++ /* ++ * Output buffer overflow. Return out of buffer space error ++ */ ++ return 0; ++ } ++ ++ if(res != SZ_OK) { ++ /* ++ * All other errors return failure, with the compressor ++ * specific error code in *error ++ */ ++ *error = res; ++ return -1; ++ } ++ ++ /* ++ * Fill in the 8 byte little endian uncompressed size field in the ++ * LZMA header. 8 bytes is excessively large for squashfs but ++ * this is the standard LZMA header and which is expected by the kernel ++ * code ++ */ ++ d[LZMA_PROPS_SIZE] = size & 255; ++ d[LZMA_PROPS_SIZE + 1] = (size >> 8) & 255; ++ d[LZMA_PROPS_SIZE + 2] = (size >> 16) & 255; ++ d[LZMA_PROPS_SIZE + 3] = (size >> 24) & 255; ++ d[LZMA_PROPS_SIZE + 4] = 0; ++ d[LZMA_PROPS_SIZE + 5] = 0; ++ d[LZMA_PROPS_SIZE + 6] = 0; ++ d[LZMA_PROPS_SIZE + 7] = 0; ++ ++ /* ++ * Success, return the compressed size. Outlen returned by the LZMA ++ * compressor does not include the LZMA header space ++ */ ++ return outlen + LZMA_HEADER_SIZE; ++} ++ ++ ++int lzma_uncompress(char *dest, char *src, int size, int block_size, ++ int *error) ++{ ++ unsigned char *d = (unsigned char *) dest, *s = (unsigned char *) src; ++ size_t outlen, inlen = size - LZMA_HEADER_SIZE; ++ int res; ++ ++ outlen = s[LZMA_PROPS_SIZE] | ++ (s[LZMA_PROPS_SIZE + 1] << 8) | ++ (s[LZMA_PROPS_SIZE + 2] << 16) | ++ (s[LZMA_PROPS_SIZE + 3] << 24); ++ ++ res = LzmaUncompress(d, &outlen, s + LZMA_HEADER_SIZE, &inlen, ++ s, LZMA_PROPS_SIZE); ++ ++ *error = res; ++ return res == SZ_OK ? outlen : -1; ++} +diff -Naurp squashfs-tools/Makefile squashfs-tools-lzma/Makefile +--- squashfs-tools/Makefile 2009-04-05 02:03:36.000000000 +0000 ++++ squashfs-tools/Makefile 2009-10-22 04:17:12.000000000 +0000 +@@ -1,40 +1,76 @@ ++# ++# Building LZMA support ++# Download LZMA sdk (4.65 used in development, other versions may work), ++# set LZMA_DIR to unpacked source, and uncomment next line ++LZMA_SUPPORT = 1 ++LZMA_DIR = ../../ ++ ++#Compression default. ++COMP_DEFAULT = gzip ++ ++INCLUDEDIR = -I. + INSTALL_DIR = /usr/local/bin + +-INCLUDEDIR = . ++MKSQUASHFS_OBJS = mksquashfs.o read_fs.o sort.o swap.o pseudo.o compressor.o \ ++ gzip_wrapper.o ++ ++UNSQUASHFS_OBJS = unsquashfs.o unsquash-1.o unsquash-2.o unsquash-3.o \ ++ unsquash-4.o swap.o compressor.o gzip_wrapper.o + +-CFLAGS := -I$(INCLUDEDIR) -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_GNU_SOURCE -O2 ++CFLAGS := $(INCLUDEDIR) -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE \ ++ -D_GNU_SOURCE -DCOMP_DEFAULT=\"$(COMP_DEFAULT)\" -O2 -Wall + ++ifdef LZMA_SUPPORT ++LZMA_OBJS = $(LZMA_DIR)/C/Alloc.o $(LZMA_DIR)/C/LzFind.o \ ++ $(LZMA_DIR)/C/LzmaDec.o $(LZMA_DIR)/C/LzmaEnc.o $(LZMA_DIR)/C/LzmaLib.o ++INCLUDEDIR += -I$(LZMA_DIR)/C ++CFLAGS += -DLZMA_SUPPORT -I../../C ++MKSQUASHFS_OBJS += lzma_wrapper.o $(LZMA_OBJS) ++UNSQUASHFS_OBJS += lzma_wrapper.o $(LZMA_OBJS) ++endif ++ ++.PHONY: all + all: mksquashfs unsquashfs + +-mksquashfs: mksquashfs.o read_fs.o sort.o swap.o pseudo.o +- $(CC) mksquashfs.o read_fs.o sort.o swap.o pseudo.o -lz -lpthread -lm -o $@ ++mksquashfs: $(MKSQUASHFS_OBJS) ++ $(CC) $(MKSQUASHFS_OBJS) -lz -lpthread -lm -o $@ ++ ++mksquashfs.o: mksquashfs.c squashfs_fs.h mksquashfs.h global.h sort.h \ ++ squashfs_swap.h + +-mksquashfs.o: mksquashfs.c squashfs_fs.h mksquashfs.h global.h sort.h squashfs_swap.h Makefile ++read_fs.o: read_fs.c squashfs_fs.h read_fs.h global.h squashfs_swap.h + +-read_fs.o: read_fs.c squashfs_fs.h read_fs.h global.h squashfs_swap.h Makefile ++sort.o: sort.c squashfs_fs.h global.h sort.h + +-sort.o: sort.c squashfs_fs.h global.h sort.h Makefile ++swap.o: swap.c + +-swap.o: swap.c Makefile ++pseudo.o: pseudo.c pseudo.h + +-pseudo.o: pseudo.c pseudo.h Makefile ++compressor.o: compressor.c compressor.h + +-unsquashfs: unsquashfs.o unsquash-1.o unsquash-2.o unsquash-3.o unsquash-4.o swap.o +- $(CC) unsquashfs.o unsquash-1.o unsquash-2.o unsquash-3.o unsquash-4.o swap.o -lz -lpthread -lm -o $@ ++unsquashfs: $(UNSQUASHFS_OBJS) ++ $(CC) $(UNSQUASHFS_OBJS) -lz -lpthread -lm -o $@ + +-unsquashfs.o: unsquashfs.h unsquashfs.c squashfs_fs.h squashfs_swap.h squashfs_compat.h global.h Makefile ++unsquashfs.o: unsquashfs.h unsquashfs.c squashfs_fs.h squashfs_swap.h \ ++ squashfs_compat.h global.h + +-unsquash-1.o: unsquashfs.h unsquash-1.c squashfs_fs.h squashfs_compat.h global.h Makefile ++unsquash-1.o: unsquashfs.h unsquash-1.c squashfs_fs.h squashfs_compat.h \ ++ global.h + +-unsquash-2.o: unsquashfs.h unsquash-2.c unsquashfs.h squashfs_fs.h squashfs_compat.h global.h Makefile ++unsquash-2.o: unsquashfs.h unsquash-2.c unsquashfs.h squashfs_fs.h \ ++ squashfs_compat.h global.h + +-unsquash-3.o: unsquashfs.h unsquash-3.c squashfs_fs.h squashfs_compat.h global.h Makefile ++unsquash-3.o: unsquashfs.h unsquash-3.c squashfs_fs.h squashfs_compat.h \ ++ global.h + +-unsquash-4.o: unsquashfs.h unsquash-4.c squashfs_fs.h squashfs_swap.h global.h Makefile ++unsquash-4.o: unsquashfs.h unsquash-4.c squashfs_fs.h squashfs_swap.h \ ++ global.h + ++.PHONY: clean + clean: + -rm -f *.o mksquashfs unsquashfs + ++.PHONY: install + install: mksquashfs unsquashfs + mkdir -p $(INSTALL_DIR) + cp mksquashfs $(INSTALL_DIR) +diff -Naurp squashfs-tools/mksquashfs.c squashfs-tools-lzma/mksquashfs.c +--- squashfs-tools/mksquashfs.c 2009-04-05 21:22:48.000000000 +0000 ++++ squashfs-tools/mksquashfs.c 2009-10-20 04:03:38.000000000 +0000 +@@ -36,7 +36,6 @@ + #include <errno.h> + #include <dirent.h> + #include <string.h> +-#include <zlib.h> + #include <stdlib.h> + #include <signal.h> + #include <setjmp.h> +@@ -47,6 +46,7 @@ + #include <math.h> + #include <regex.h> + #include <fnmatch.h> ++#include <sys/wait.h> + + #ifndef linux + #define __BYTE_ORDER BYTE_ORDER +@@ -64,6 +64,7 @@ + #include "global.h" + #include "sort.h" + #include "pseudo.h" ++#include "compressor.h" + + #ifdef SQUASHFS_TRACE + #define TRACE(s, args...) do { \ +@@ -245,10 +246,8 @@ char **source_path; + /* list of root directory entries read from original filesystem */ + int old_root_entries = 0; + struct old_root_entry_info { +- char name[SQUASHFS_NAME_LEN + 1]; +- squashfs_inode inode; +- int type; +- int inode_number; ++ char *name; ++ struct inode_info inode; + }; + struct old_root_entry_info *old_root_entry; + +@@ -371,10 +370,15 @@ int writer_buffer_size; + int reader_buffer_size; + int fragment_buffer_size; + ++/* compression operations structure */ ++static struct compressor *comp; ++char *comp_name = COMP_DEFAULT; ++ + char *read_from_disk(long long start, unsigned int avail_bytes); + void add_old_root_entry(char *name, squashfs_inode inode, int inode_number, + int type); +-extern int read_super(int fd, squashfs_super_block *sBlk, char *source); ++extern struct compressor *read_super(int fd, squashfs_super_block *sBlk, ++ char *source); + extern long long read_filesystem(char *root_name, int fd, + squashfs_super_block *sBlk, char **cinode_table, char **data_cache, + char **cdirectory_table, char **directory_data_cache, +@@ -831,83 +835,32 @@ void sigalrm_handler() + } + + +-unsigned int mangle2(z_stream **strm, char *d, char *s, int size, ++int mangle2(void **strm, char *d, char *s, int size, + int block_size, int uncompressed, int data_block) + { +- unsigned long c_byte; +- unsigned int res; +- z_stream *stream = *strm; +- +- if(uncompressed) +- goto notcompressed; +- +- if(stream == NULL) { +- if((stream = *strm = malloc(sizeof(z_stream))) == NULL) +- BAD_ERROR("mangle::compress failed, not enough " +- "memory\n"); +- +- stream->zalloc = Z_NULL; +- stream->zfree = Z_NULL; +- stream->opaque = 0; +- +- if((res = deflateInit(stream, 9)) != Z_OK) { +- if(res == Z_MEM_ERROR) +- BAD_ERROR("zlib::compress failed, not enough " +- "memory\n"); +- else if(res == Z_STREAM_ERROR) +- BAD_ERROR("zlib::compress failed, not a valid " +- "compression level\n"); +- else if(res == Z_VERSION_ERROR) +- BAD_ERROR("zlib::compress failed, incorrect " +- "zlib version\n"); +- else +- BAD_ERROR("zlib::compress failed, unknown " +- "error %d\n", res); +- } +- } else if((res = deflateReset(stream)) != Z_OK) { +- if(res == Z_STREAM_ERROR) +- BAD_ERROR("zlib::compress failed, stream state " +- "inconsistent\n"); +- else +- BAD_ERROR("zlib::compress failed, unknown error %d\n", +- res); +- } ++ int error, c_byte = 0; + +- stream->next_in = (unsigned char *) s; +- stream->avail_in = size; +- stream->next_out = (unsigned char *) d; +- stream->avail_out = block_size; +- +- res = deflate(stream, Z_FINISH); +- if(res != Z_STREAM_END && res != Z_OK) { +- if(res == Z_STREAM_ERROR) +- BAD_ERROR("zlib::compress failed, stream state " +- "inconsistent\n"); +- else if(res == Z_BUF_ERROR) +- BAD_ERROR("zlib::compress failed, no progress possible" +- "\n"); +- else +- BAD_ERROR("zlib::compress failed, unknown error %d\n", +- res); ++ if(!uncompressed) { ++ c_byte = comp->compress(strm, d, s, size, block_size, &error); ++ if(c_byte == -1) ++ BAD_ERROR("mangle2:: %s compress failed with error " ++ "code %d\n", comp->name, error); + } + +- c_byte = stream->total_out; +- +- if(res != Z_STREAM_END || c_byte >= size) { +-notcompressed: ++ if(c_byte == 0 || c_byte >= size) { + memcpy(d, s, size); + return size | (data_block ? SQUASHFS_COMPRESSED_BIT_BLOCK : + SQUASHFS_COMPRESSED_BIT); + } + +- return (unsigned int) c_byte; ++ return c_byte; + } + + +-unsigned int mangle(char *d, char *s, int size, int block_size, ++int mangle(char *d, char *s, int size, int block_size, + int uncompressed, int data_block) + { +- static z_stream *stream = NULL; ++ static void *stream = NULL; + + return mangle2(&stream, d, s, size, block_size, uncompressed, + data_block); +@@ -1660,8 +1613,7 @@ struct file_buffer *get_fragment(struct + pthread_mutex_unlock(&fragment_mutex); + + if(SQUASHFS_COMPRESSED_BLOCK(disk_fragment->size)) { +- int res; +- unsigned long bytes = block_size; ++ int error, res; + char *data; + + if(compressed_buffer) +@@ -1669,19 +1621,11 @@ struct file_buffer *get_fragment(struct + else + data = read_from_disk(start_block, size); + +- res = uncompress((unsigned char *) buffer->data, &bytes, +- (const unsigned char *) data, size); +- if(res != Z_OK) { +- if(res == Z_MEM_ERROR) +- BAD_ERROR("zlib::uncompress failed, not enough " +- "memory\n"); +- else if(res == Z_BUF_ERROR) +- BAD_ERROR("zlib::uncompress failed, not enough " +- "room in output buffer\n"); +- else +- BAD_ERROR("zlib::uncompress failed," +- " unknown error %d\n", res); +- } ++ res = comp->uncompress(buffer->data, data, size, block_size, ++ &error); ++ if(res == -1) ++ BAD_ERROR("%s uncompress failed with error code %d\n", ++ comp->name, error); + } else if(compressed_buffer) + memcpy(buffer->data, compressed_buffer->data, size); + else +@@ -1733,9 +1677,7 @@ void unlock_fragments() + entry->buffer->block = bytes; + bytes += compressed_size; + fragments_outstanding --; +- pthread_mutex_unlock(&fragment_mutex); + queue_put(to_writer, entry->buffer); +- pthread_mutex_lock(&fragment_mutex); + TRACE("fragment_locked writing fragment %d, compressed size %d" + "\n", entry->fragment, compressed_size); + free(entry); +@@ -1758,6 +1700,8 @@ int add_pending_fragment(struct file_buf + pthread_mutex_lock(&fragment_mutex); + insert_fragment_list(&frag_locked_list, entry); + pthread_mutex_unlock(&fragment_mutex); ++ ++ return TRUE; + } + + +@@ -1824,7 +1768,9 @@ long long generic_write_table(int length + unsigned short c_byte; + char cbuffer[(SQUASHFS_METADATA_SIZE << 2) + 2]; + ++#ifdef SQUASHFS_TRACE + long long obytes = bytes; ++#endif + + for(i = 0; i < meta_blocks; i++) { + int avail_bytes = length > SQUASHFS_METADATA_SIZE ? +@@ -2170,11 +2116,85 @@ struct file_info *duplicate(long long fi + } + + ++static int seq = 0; ++void reader_read_process(struct dir_ent *dir_ent) ++{ ++ struct file_buffer *prev_buffer = NULL, *file_buffer; ++ int status, res, byte, count = 0; ++ int file = get_pseudo_file(dir_ent->inode->pseudo_id)->fd; ++ int child = get_pseudo_file(dir_ent->inode->pseudo_id)->child; ++ long long bytes = 0; ++ ++ while(1) { ++ file_buffer = cache_get(reader_buffer, 0, 0); ++ file_buffer->sequence = seq ++; ++ ++ byte = read_bytes(file, file_buffer->data, block_size); ++ if(byte == -1) ++ goto read_err; ++ ++ file_buffer->size = byte; ++ file_buffer->file_size = -1; ++ file_buffer->block = count ++; ++ file_buffer->error = FALSE; ++ file_buffer->fragment = FALSE; ++ bytes += byte; ++ ++ if(byte == 0) ++ break; ++ ++ /* ++ * Update estimated_uncompressed block count. This is done ++ * on every block rather than waiting for all blocks to be ++ * read incase write_file_process() is running in parallel ++ * with this. Otherwise cur uncompressed block count may ++ * get ahead of the total uncompressed block count. ++ */ ++ estimated_uncompressed ++; ++ ++ if(prev_buffer) ++ queue_put(from_reader, prev_buffer); ++ prev_buffer = file_buffer; ++ } ++ ++ /* ++ * Update inode file size now that the size of the dynamic pseudo file ++ * is known. This is needed for the -info option. ++ */ ++ dir_ent->inode->buf.st_size = bytes; ++ ++ res = waitpid(child, &status, 0); ++ if(res == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) ++ goto read_err; ++ ++ if(prev_buffer == NULL) ++ prev_buffer = file_buffer; ++ else { ++ cache_block_put(file_buffer); ++ seq --; ++ } ++ prev_buffer->file_size = bytes; ++ prev_buffer->fragment = !no_fragments && ++ (count == 2 || always_use_fragments) && (byte < block_size); ++ queue_put(from_reader, prev_buffer); ++ ++ return; ++ ++read_err: ++ if(prev_buffer) { ++ cache_block_put(file_buffer); ++ seq --; ++ file_buffer = prev_buffer; ++ } ++ file_buffer->error = TRUE; ++ queue_put(from_deflate, file_buffer); ++} ++ ++ + void reader_read_file(struct dir_ent *dir_ent) + { + struct stat *buf = &dir_ent->inode->buf, buf2; + struct file_buffer *file_buffer; +- static int index = 0; + int blocks, byte, count, expected, file, frag_block; + long long bytes, read_size; + +@@ -2202,7 +2222,7 @@ again: + if(file_buffer) + queue_put(from_reader, file_buffer); + file_buffer = cache_get(reader_buffer, 0, 0); +- file_buffer->sequence = index ++; ++ file_buffer->sequence = seq ++; + + byte = file_buffer->size = read_bytes(file, file_buffer->data, + block_size); +@@ -2238,7 +2258,7 @@ again: + + read_err: + file_buffer = cache_get(reader_buffer, 0, 0); +- file_buffer->sequence = index ++; ++ file_buffer->sequence = seq ++; + read_err2: + file_buffer->error = TRUE; + queue_put(from_deflate, file_buffer); +@@ -2262,9 +2282,14 @@ void reader_scan(struct dir_info *dir) { + for(i = 0; i < dir->count; i++) { + struct dir_ent *dir_ent = dir->list[i]; + struct stat *buf = &dir_ent->inode->buf; +- if(dir_ent->data) ++ if(dir_ent->inode->root_entry) + continue; + ++ if(dir_ent->inode->pseudo_file) { ++ reader_read_process(dir_ent); ++ continue; ++ } ++ + switch(buf->st_mode & S_IFMT) { + case S_IFREG: + reader_read_file(dir_ent); +@@ -2365,7 +2390,7 @@ int all_zero(struct file_buffer *file_bu + + void *deflator(void *arg) + { +- z_stream *stream = NULL; ++ void *stream = NULL; + int oldstate; + + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate); +@@ -2402,7 +2427,7 @@ void *deflator(void *arg) + + void *frag_deflator(void *arg) + { +- z_stream *stream = NULL; ++ void *stream = NULL; + int oldstate; + + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate); +@@ -2426,8 +2451,8 @@ void *frag_deflator(void *arg) + write_buffer->block = bytes; + bytes += compressed_size; + fragments_outstanding --; +- pthread_mutex_unlock(&fragment_mutex); + queue_put(to_writer, write_buffer); ++ pthread_mutex_unlock(&fragment_mutex); + TRACE("Writing fragment %lld, uncompressed size %d, " + "compressed size %d\n", file_buffer->block, + file_buffer->size, compressed_size); +@@ -2674,6 +2699,98 @@ void write_file_frag(squashfs_inode *ino + } + + ++int write_file_process(squashfs_inode *inode, struct dir_ent *dir_ent, ++ struct file_buffer *read_buffer, int *duplicate_file) ++{ ++ long long read_size, file_bytes, start; ++ struct fragment *fragment; ++ unsigned int *block_list = NULL; ++ int block = 0, status; ++ long long sparse = 0; ++ struct file_buffer *fragment_buffer = NULL; ++ ++ *duplicate_file = FALSE; ++ ++ lock_fragments(); ++ ++ file_bytes = 0; ++ start = bytes; ++ while (1) { ++ read_size = read_buffer->file_size; ++ if(read_buffer->fragment && read_buffer->c_byte) ++ fragment_buffer = read_buffer; ++ else { ++ block_list = realloc(block_list, (block + 1) * ++ sizeof(unsigned int)); ++ if(block_list == NULL) ++ BAD_ERROR("Out of memory allocating block_list" ++ "\n"); ++ block_list[block ++] = read_buffer->c_byte; ++ if(read_buffer->c_byte) { ++ read_buffer->block = bytes; ++ bytes += read_buffer->size; ++ cache_rehash(read_buffer, read_buffer->block); ++ file_bytes += read_buffer->size; ++ queue_put(to_writer, read_buffer); ++ } else { ++ sparse += read_buffer->size; ++ cache_block_put(read_buffer); ++ } ++ } ++ inc_progress_bar(); ++ ++ if(read_size != -1) ++ break; ++ ++ read_buffer = get_file_buffer(from_deflate); ++ if(read_buffer->error) ++ goto read_err; ++ } ++ ++ unlock_fragments(); ++ fragment = get_and_fill_fragment(fragment_buffer); ++ cache_block_put(fragment_buffer); ++ ++ if(duplicate_checking) ++ add_non_dup(read_size, file_bytes, block_list, start, fragment, ++ 0, 0, FALSE); ++ file_count ++; ++ total_bytes += read_size; ++ ++ if(read_size < (1LL << 32) && start < (1LL << 32) && sparse == 0) ++ create_inode(inode, dir_ent, SQUASHFS_FILE_TYPE, read_size, ++ start, block, block_list, fragment, NULL, 0); ++ else ++ create_inode(inode, dir_ent, SQUASHFS_LREG_TYPE, read_size, ++ start, block, block_list, fragment, NULL, sparse); ++ ++ if(duplicate_checking == FALSE) ++ free(block_list); ++ ++ return 0; ++ ++read_err: ++ cur_uncompressed -= block; ++ status = read_buffer->error; ++ bytes = start; ++ if(!block_device) { ++ int res; ++ ++ queue_put(to_writer, NULL); ++ if(queue_get(from_writer) != 0) ++ EXIT_MKSQUASHFS(); ++ res = ftruncate(fd, bytes); ++ if(res != 0) ++ BAD_ERROR("Failed to truncate dest file because %s\n", ++ strerror(errno)); ++ } ++ unlock_fragments(); ++ free(block_list); ++ cache_block_put(read_buffer); ++ return status; ++} ++ ++ + int write_file_blocks(squashfs_inode *inode, struct dir_ent *dir_ent, + long long read_size, struct file_buffer *read_buffer, + int *duplicate_file) +@@ -2941,7 +3058,10 @@ again: + + read_size = read_buffer->file_size; + +- if(read_size == 0) { ++ if(read_size == -1) ++ status = write_file_process(inode, dir_ent, read_buffer, ++ duplicate_file); ++ else if(read_size == 0) { + write_file_empty(inode, dir_ent, duplicate_file); + cache_block_put(read_buffer); + } else if(read_buffer->fragment && read_buffer->c_byte) +@@ -3036,6 +3156,8 @@ struct inode_info *lookup_inode(struct s + + memcpy(&inode->buf, buf, sizeof(struct stat)); + inode->read = FALSE; ++ inode->root_entry = FALSE; ++ inode->pseudo_file = FALSE; + inode->inode = SQUASHFS_INVALID_BLK; + inode->nlink = 1; + +@@ -3056,7 +3178,7 @@ struct inode_info *lookup_inode(struct s + + + inline void add_dir_entry(char *name, char *pathname, struct dir_info *sub_dir, +- struct inode_info *inode_info, void *data, struct dir_info *dir) ++ struct inode_info *inode_info, struct dir_info *dir) + { + if((dir->count % DIR_ENTRIES) == 0) { + dir->list = realloc(dir->list, (dir->count + DIR_ENTRIES) * +@@ -3075,8 +3197,7 @@ inline void add_dir_entry(char *name, ch + NULL; + dir->list[dir->count]->inode = inode_info; + dir->list[dir->count]->dir = sub_dir; +- dir->list[dir->count]->our_dir = dir; +- dir->list[dir->count++]->data = data; ++ dir->list[dir->count++]->our_dir = dir; + dir->byte_count += strlen(name) + sizeof(squashfs_dir_entry); + } + +@@ -3128,10 +3249,10 @@ int scan1_encomp_readdir(char *pathname, + + if(dir->count < old_root_entries) + for(i = 0; i < old_root_entries; i++) { +- if(old_root_entry[i].type == SQUASHFS_DIR_TYPE) ++ if(old_root_entry[i].inode.type == SQUASHFS_DIR_TYPE) + dir->directory_count ++; +- add_dir_entry(old_root_entry[i].name, "", NULL, NULL, +- &old_root_entry[i], dir); ++ add_dir_entry(old_root_entry[i].name, "", NULL, ++ &old_root_entry[i].inode, dir); + } + + while(index < source) { +@@ -3167,10 +3288,10 @@ int scan1_single_readdir(char *pathname, + + if(dir->count < old_root_entries) + for(i = 0; i < old_root_entries; i++) { +- if(old_root_entry[i].type == SQUASHFS_DIR_TYPE) ++ if(old_root_entry[i].inode.type == SQUASHFS_DIR_TYPE) + dir->directory_count ++; +- add_dir_entry(old_root_entry[i].name, "", NULL, NULL, +- &old_root_entry[i], dir); ++ add_dir_entry(old_root_entry[i].name, "", NULL, ++ &old_root_entry[i].inode, dir); + } + + if((d_name = readdir(dir->linuxdir)) != NULL) { +@@ -3215,7 +3336,7 @@ struct dir_ent *scan2_readdir(struct dir + int current_count; + + while((current_count = dir_info->current_count++) < dir_info->count) +- if(dir_info->list[current_count]->data) ++ if(dir_info->list[current_count]->inode->root_entry) + continue; + else + return dir_info->list[current_count]; +@@ -3240,11 +3361,11 @@ struct dir_ent *scan3_readdir(struct dir + int current_count; + + while((current_count = dir_info->current_count++) < dir_info->count) +- if(dir_info->list[current_count]->data) +- add_dir(dir_info->list[current_count]->data->inode, +- dir_info->list[current_count]->data->inode_number, ++ if(dir_info->list[current_count]->inode->root_entry) ++ add_dir(dir_info->list[current_count]->inode->inode, ++ dir_info->list[current_count]->inode->inode_number, + dir_info->list[current_count]->name, +- dir_info->list[current_count]->data->type, dir); ++ dir_info->list[current_count]->inode->type, dir); + else + return dir_info->list[current_count]; + return NULL; +@@ -3313,7 +3434,6 @@ void dir_scan(squashfs_inode *inode, cha + dir_ent->name = dir_ent->pathname = strdup(pathname); + dir_ent->dir = dir_info; + dir_ent->our_dir = NULL; +- dir_ent->data = NULL; + dir_info->dir_ent = dir_ent; + + if(sorted) +@@ -3383,7 +3503,7 @@ struct dir_info *dir_scan1(char *pathnam + sub_dir = NULL; + + add_dir_entry(dir_name, filename, sub_dir, lookup_inode(&buf), +- NULL, dir); ++ dir); + } + + scan1_freedir(dir); +@@ -3399,7 +3519,7 @@ struct dir_info *dir_scan2(struct dir_in + struct dir_ent *dir_ent; + struct pseudo_entry *pseudo_ent; + struct stat buf; +- static pseudo_ino = 1; ++ static int pseudo_ino = 1; + + if(dir == NULL && (dir = scan1_opendir("")) == NULL) + return NULL; +@@ -3415,6 +3535,29 @@ struct dir_info *dir_scan2(struct dir_in + + while((pseudo_ent = pseudo_readdir(pseudo)) != NULL) { + dir_ent = scan2_lookup(dir, pseudo_ent->name); ++ if(pseudo_ent->dev->type == 's') { ++ struct stat *buf; ++ if(dir_ent == NULL) { ++ ERROR("Pseudo set file \"%s\" does not exist " ++ "in source filesystem. Ignoring\n", ++ pseudo_ent->pathname); ++ continue; ++ } ++ if(dir_ent->inode->root_entry) { ++ ERROR("Pseudo set file \"%s\" is a pre-existing" ++ " file in the filesystem being appended" ++ " to. It cannot be modified. " ++ "Ignoring!\n", pseudo_ent->pathname); ++ continue; ++ } ++ buf = &dir_ent->inode->buf; ++ buf->st_mode = (buf->st_mode & S_IFMT) | ++ pseudo_ent->dev->mode; ++ buf->st_uid = pseudo_ent->dev->uid; ++ buf->st_gid = pseudo_ent->dev->gid; ++ continue; ++ } ++ + if(dir_ent) { + ERROR("Pseudo file \"%s\" exists in source filesystem " + "\"%s\"\n", pseudo_ent->pathname, +@@ -3444,8 +3587,29 @@ struct dir_info *dir_scan2(struct dir_in + buf.st_mtime = time(NULL); + buf.st_ino = pseudo_ino ++; + +- add_dir_entry(pseudo_ent->name, pseudo_ent->pathname, sub_dir, +- lookup_inode(&buf), NULL, dir); ++ if(pseudo_ent->dev->type == 'f') { ++#ifdef USE_TMP_FILE ++ struct stat buf2; ++ int res = stat(pseudo_ent->dev->filename, &buf2); ++ if(res == -1) { ++ ERROR("Stat on pseudo file \"%s\" failed, " ++ "skipping...", pseudo_ent->pathname); ++ continue; ++ } ++ buf.st_size = buf2.st_size; ++ add_dir_entry(pseudo_ent->name, ++ pseudo_ent->dev->filename, sub_dir, ++ lookup_inode(&buf), dir); ++#else ++ struct inode_info *inode = lookup_inode(&buf); ++ inode->pseudo_id = pseudo_ent->dev->pseudo_id; ++ inode->pseudo_file = TRUE; ++ add_dir_entry(pseudo_ent->name, pseudo_ent->pathname, ++ sub_dir, inode, dir); ++#endif ++ } else ++ add_dir_entry(pseudo_ent->name, pseudo_ent->pathname, ++ sub_dir, lookup_inode(&buf), dir); + } + + scan2_freedir(dir); +@@ -3482,8 +3646,9 @@ void dir_scan3(squashfs_inode *inode, st + &duplicate_file); + INFO("file %s, uncompressed size %lld " + "bytes %s\n", filename, +- buf->st_size, duplicate_file ? +- "DUPLICATE" : ""); ++ (long long) buf->st_size, ++ duplicate_file ? "DUPLICATE" : ++ ""); + break; + + case S_IFDIR: +@@ -3557,6 +3722,7 @@ void dir_scan3(squashfs_inode *inode, st + INFO("file %s, uncompressed " + "size %lld bytes LINK" + "\n", filename, ++ (long long) + buf->st_size); + break; + case SQUASHFS_SYMLINK_TYPE: +@@ -3667,10 +3833,11 @@ void add_old_root_entry(char *name, squa + BAD_ERROR("Out of memory in old root directory entries " + "reallocation\n"); + +- strcpy(old_root_entry[old_root_entries].name, name); +- old_root_entry[old_root_entries].inode = inode; +- old_root_entry[old_root_entries].inode_number = inode_number; +- old_root_entry[old_root_entries++].type = type; ++ old_root_entry[old_root_entries].name = strdup(name); ++ old_root_entry[old_root_entries].inode.inode = inode; ++ old_root_entry[old_root_entries].inode.inode_number = inode_number; ++ old_root_entry[old_root_entries].inode.type = type; ++ old_root_entry[old_root_entries++].inode.root_entry = TRUE; + } + + +@@ -4172,26 +4339,28 @@ int main(int argc, char *argv[]) + source_path = argv + 1; + source = i - 2; + for(; i < argc; i++) { +- if(strcmp(argv[i], "-pf") == 0) { ++ if(strcmp(argv[i], "-comp") == 0) { + if(++i == argc) { +- ERROR("%s: -pf missing filename\n", argv[0]); ++ ERROR("%s: -comp missing compression type\n", ++ argv[0]); + exit(1); + } +- if(read_pseudo_file(&pseudo, argv[i]) == FALSE) { +- ERROR("Failed to parse pseudo file \"%s\"\n", +- argv[i]); ++ comp_name = argv[i]; ++ } else if(strcmp(argv[i], "-pf") == 0) { ++ if(++i == argc) { ++ ERROR("%s: -pf missing filename\n", argv[0]); + exit(1); + } ++ if(read_pseudo_file(&pseudo, argv[i]) == FALSE) ++ exit(1); + } else if(strcmp(argv[i], "-p") == 0) { + if(++i == argc) { + ERROR("%s: -p missing pseudo file definition\n", + argv[0]); + exit(1); + } +- if(read_pseudo_def(&pseudo, argv[i]) == FALSE) { +- ERROR("Failed to parse pseudo definition\n"); ++ if(read_pseudo_def(&pseudo, argv[i]) == FALSE) + exit(1); +- } + } else if(strcmp(argv[i], "-recover") == 0) { + if(++i == argc) { + ERROR("%s: -recover missing recovery file\n", +@@ -4394,34 +4563,16 @@ int main(int argc, char *argv[]) + printOptions: + ERROR("SYNTAX:%s source1 source2 ... dest [options] " + "[-e list of exclude\ndirs/files]\n", argv[0]); +- ERROR("\nOptions are\n"); +- ERROR("-version\t\tprint version, licence and " +- "copyright message\n"); +- ERROR("-recover <name>\t\trecover filesystem data " +- "using recovery file <name>\n"); +- ERROR("-no-recovery\t\tdon't generate a recovery " +- "file\n"); +- ERROR("-info\t\t\tprint files written to filesystem\n"); +- ERROR("-no-exports\t\tdon't make the filesystem " +- "exportable via NFS\n"); +- ERROR("-no-progress\t\tdon't display the progress " +- "bar\n"); +- ERROR("-no-sparse\t\tdon't detect sparse files\n"); ++ ERROR("\nFilesystem build options:\n"); ++ ERROR("-comp <comp>\t\tselect <comp> compression\n"); ++ ERROR("\t\t\tCompressors available:\n"); ++ display_compressors("\t\t\t", COMP_DEFAULT); + ERROR("-b <block_size>\t\tset data block to " + "<block_size>. Default %d bytes\n", + SQUASHFS_FILE_SIZE); +- ERROR("-processors <number>\tUse <number> processors." +- " By default will use number of\n"); +- ERROR("\t\t\tprocessors available\n"); +- ERROR("-read-queue <size>\tSet input queue to <size> " +- "Mbytes. Default %d Mbytes\n", +- READER_BUFFER_DEFAULT); +- ERROR("-write-queue <size>\tSet output queue to <size> " +- "Mbytes. Default %d Mbytes\n", +- WRITER_BUFFER_DEFAULT); +- ERROR("-fragment-queue <size>\tSet fagment queue to " +- "<size> Mbytes. Default %d Mbytes\n", +- FRAGMENT_BUFFER_DEFAULT); ++ ERROR("-no-exports\t\tdon't make the filesystem " ++ "exportable via NFS\n"); ++ ERROR("-no-sparse\t\tdon't detect sparse files\n"); + ERROR("-noI\t\t\tdo not compress inode table\n"); + ERROR("-noD\t\t\tdo not compress data blocks\n"); + ERROR("-noF\t\t\tdo not compress fragment blocks\n"); +@@ -4430,13 +4581,34 @@ printOptions: + "files larger than block size\n"); + ERROR("-no-duplicates\t\tdo not perform duplicate " + "checking\n"); +- ERROR("-noappend\t\tdo not append to existing " +- "filesystem\n"); ++ ERROR("-all-root\t\tmake all files owned by root\n"); ++ ERROR("-force-uid uid\t\tset all file uids to uid\n"); ++ ERROR("-force-gid gid\t\tset all file gids to gid\n"); ++ ERROR("-nopad\t\t\tdo not pad filesystem to a multiple " ++ "of 4K\n"); + ERROR("-keep-as-directory\tif one source directory is " + "specified, create a root\n"); + ERROR("\t\t\tdirectory containing that directory, " + "rather than the\n"); + ERROR("\t\t\tcontents of the directory\n"); ++ ERROR("\nFilesystem filter options:\n"); ++ ERROR("-p <pseudo-definition>\tAdd pseudo file definition\n"); ++ ERROR("-pf <pseudo-file>\tAdd list of pseudo file definitions\n"); ++ ERROR("-sort <sort_file>\tsort files according to " ++ "priorities in <sort_file>. One\n"); ++ ERROR("\t\t\tfile or dir with priority per line. " ++ "Priority -32768 to\n"); ++ ERROR("\t\t\t32767, default priority 0\n"); ++ ERROR("-ef <exclude_file>\tlist of exclude dirs/files." ++ " One per line\n"); ++ ERROR("-wildcards\t\tAllow extended shell wildcards " ++ "(globbing) to be used in\n\t\t\texclude " ++ "dirs/files\n"); ++ ERROR("-regex\t\t\tAllow POSIX regular expressions to " ++ "be used in exclude\n\t\t\tdirs/files\n"); ++ ERROR("\nFilesystem append options:\n"); ++ ERROR("-noappend\t\tdo not append to existing " ++ "filesystem\n"); + ERROR("-root-becomes <name>\twhen appending source " + "files/directories, make the\n"); + ERROR("\t\t\toriginal root become a subdirectory in " +@@ -4444,11 +4616,29 @@ printOptions: + ERROR("\t\t\tcalled <name>, rather than adding the new " + "source items\n"); + ERROR("\t\t\tto the original root\n"); +- ERROR("-all-root\t\tmake all files owned by root\n"); +- ERROR("-force-uid uid\t\tset all file uids to uid\n"); +- ERROR("-force-gid gid\t\tset all file gids to gid\n"); +- ERROR("-nopad\t\t\tdo not pad filesystem to a multiple " +- "of 4K\n"); ++ ERROR("\nMksquashfs runtime options:\n"); ++ ERROR("-version\t\tprint version, licence and " ++ "copyright message\n"); ++ ERROR("-recover <name>\t\trecover filesystem data " ++ "using recovery file <name>\n"); ++ ERROR("-no-recovery\t\tdon't generate a recovery " ++ "file\n"); ++ ERROR("-info\t\t\tprint files written to filesystem\n"); ++ ERROR("-no-progress\t\tdon't display the progress " ++ "bar\n"); ++ ERROR("-processors <number>\tUse <number> processors." ++ " By default will use number of\n"); ++ ERROR("\t\t\tprocessors available\n"); ++ ERROR("-read-queue <size>\tSet input queue to <size> " ++ "Mbytes. Default %d Mbytes\n", ++ READER_BUFFER_DEFAULT); ++ ERROR("-write-queue <size>\tSet output queue to <size> " ++ "Mbytes. Default %d Mbytes\n", ++ WRITER_BUFFER_DEFAULT); ++ ERROR("-fragment-queue <size>\tSet fagment queue to " ++ "<size> Mbytes. Default %d Mbytes\n", ++ FRAGMENT_BUFFER_DEFAULT); ++ ERROR("\nMiscellaneous options:\n"); + ERROR("-root-owned\t\talternative name for -all-root" + "\n"); + ERROR("-noInodeCompression\talternative name for -noI" +@@ -4457,20 +4647,6 @@ printOptions: + "\n"); + ERROR("-noFragmentCompression\talternative name for " + "-noF\n"); +- ERROR("-sort <sort_file>\tsort files according to " +- "priorities in <sort_file>. One\n"); +- ERROR("\t\t\tfile or dir with priority per line. " +- "Priority -32768 to\n"); +- ERROR("\t\t\t32767, default priority 0\n"); +- ERROR("-ef <exclude_file>\tlist of exclude dirs/files." +- " One per line\n"); +- ERROR("-wildcards\t\tAllow extended shell wildcards " +- "(globbing) to be used in\n\t\t\texclude " +- "dirs/files\n"); +- ERROR("-regex\t\t\tAllow POSIX regular expressions to " +- "be used in exclude\n\t\t\tdirs/files\n"); +- ERROR("-p <pseudo-definition>\tAdd pseudo file definition\n"); +- ERROR("-pf <pseudo-file>\tAdd list of pseudo file definitions\n"); + exit(1); + } + } +@@ -4548,11 +4724,10 @@ printOptions: + fclose(fd); + } else if(strcmp(argv[i], "-e") == 0) + break; +- else if(strcmp(argv[i], "-b") == 0 || +- strcmp(argv[i], "-root-becomes") == 0 || ++ else if(strcmp(argv[i], "-root-becomes") == 0 || + strcmp(argv[i], "-sort") == 0 || + strcmp(argv[i], "-pf") == 0 || +- strcmp(argv[i], "-p") == 0) ++ strcmp(argv[i], "-comp") == 0) + i++; + + if(i != argc) { +@@ -4574,11 +4749,10 @@ printOptions: + sorted ++; + } else if(strcmp(argv[i], "-e") == 0) + break; +- else if(strcmp(argv[i], "-b") == 0 || +- strcmp(argv[i], "-root-becomes") == 0 || ++ else if(strcmp(argv[i], "-root-becomes") == 0 || + strcmp(argv[i], "-ef") == 0 || + strcmp(argv[i], "-pf") == 0 || +- strcmp(argv[i], "-p") == 0) ++ strcmp(argv[i], "-comp") == 0) + i++; + + #ifdef SQUASHFS_TRACE +@@ -4586,7 +4760,8 @@ printOptions: + #endif + + if(!delete) { +- if(read_super(fd, &sBlk, argv[source + 1]) == 0) { ++ comp = read_super(fd, &sBlk, argv[source + 1]); ++ if(comp == NULL) { + ERROR("Failed to read existing filesystem - will not " + "overwrite - ABORTING!\n"); + ERROR("To force Mksquashfs to write to this block " +@@ -4603,6 +4778,15 @@ printOptions: + always_use_fragments = SQUASHFS_ALWAYS_FRAGMENTS(sBlk.flags); + duplicate_checking = SQUASHFS_DUPLICATES(sBlk.flags); + exportable = SQUASHFS_EXPORTABLE(sBlk.flags); ++ } else { ++ comp = lookup_compressor(comp_name); ++ if(!comp->supported) { ++ ERROR("FATAL_ERROR: Compressor \"%s\" is not " ++ "supported!\n", comp_name); ++ ERROR("Compressors available:\n"); ++ display_compressors("", COMP_DEFAULT); ++ EXIT_MKSQUASHFS(); ++ } + } + + initialise_threads(); +@@ -4648,8 +4832,8 @@ printOptions: + "size %d\n", SQUASHFS_MAJOR, s_minor, argv[source + 1], + block_size); + printf("All -b, -noI, -noD, -noF, no-duplicates, no-fragments, " +- "-always-use-fragments and -exportable options ignored" +- "\n"); ++ "-always-use-fragments,\n-exportable and -comp options " ++ "ignored\n"); + printf("\nIf appending is not wanted, please re-run with " + "-noappend specified!\n\n"); + +@@ -4803,8 +4987,7 @@ restore_filesystem: + + sBlk.bytes_used = bytes; + +- /* Only compression supported */ +- sBlk.compression = ZLIB_COMPRESSION; ++ sBlk.compression = comp->id; + + /* Xattrs are not currently supported */ + sBlk.xattr_table_start = SQUASHFS_INVALID_BLK; +@@ -4820,6 +5003,8 @@ restore_filesystem: + + close(fd); + ++ delete_pseudo_files(); ++ + if(recovery_file[0] != '\0') + unlink(recovery_file); + +@@ -4827,9 +5012,9 @@ restore_filesystem: + * sizeof(unsigned short) + guid_count * sizeof(unsigned short) + + sizeof(squashfs_super_block); + +- printf("\n%sSquashfs %d.%d filesystem, data block size %d\n", +- exportable ? "Exportable " : "", SQUASHFS_MAJOR, SQUASHFS_MINOR, +- block_size); ++ printf("\n%sSquashfs %d.%d filesystem, %s compressed, data block size" ++ " %d\n", exportable ? "Exportable " : "", SQUASHFS_MAJOR, ++ SQUASHFS_MINOR, comp->name, block_size); + printf("\t%s data, %s metadata, %s fragments\n", + noD ? "uncompressed" : "compressed", noI ? "uncompressed" : + "compressed", no_fragments ? "no" : noF ? "uncompressed" : +diff -Naurp squashfs-tools/pseudo.c squashfs-tools-lzma/pseudo.c +--- squashfs-tools/pseudo.c 2009-04-05 02:01:58.000000000 +0000 ++++ squashfs-tools/pseudo.c 2009-10-20 04:03:38.000000000 +0000 +@@ -30,6 +30,7 @@ + #include <string.h> + #include <stdlib.h> + #include <sys/types.h> ++#include <sys/wait.h> + + #include "pseudo.h" + +@@ -55,6 +56,9 @@ + #define TRUE 1 + #define FALSE 0 + ++struct pseudo_dev **pseudo_file = NULL; ++int pseudo_count = 0; ++ + static void dump_pseudo(struct pseudo *pseudo, char *string) + { + int i; +@@ -99,7 +103,7 @@ struct pseudo *add_pseudo(struct pseudo + char *target, char *alltarget) + { + char targname[1024]; +- int i, error; ++ int i; + + target = get_component(target, targname); + +@@ -128,12 +132,8 @@ struct pseudo *add_pseudo(struct pseudo + if(target[0] == '\0') { + /* at leaf pathname component */ + pseudo->name[i].pseudo = NULL; +- pseudo->name[i].dev = malloc(sizeof(struct pseudo_dev)); +- if(pseudo->name[i].dev == NULL) +- BAD_ERROR("failed to allocate pseudo file\n"); + pseudo->name[i].pathname = strdup(alltarget); +- memcpy(pseudo->name[i].dev, pseudo_dev, +- sizeof(struct pseudo_dev)); ++ pseudo->name[i].dev = pseudo_dev; + } else { + /* recurse adding child components */ + pseudo->name[i].dev = NULL; +@@ -169,15 +169,9 @@ struct pseudo *add_pseudo(struct pseudo + if(target[0] == '\0') { + if(pseudo->name[i].dev == NULL && + pseudo_dev->type == 'd') { +- pseudo->name[i].dev = +- malloc(sizeof(struct pseudo_dev)); +- if(pseudo->name[i].dev == NULL) +- BAD_ERROR("failed to allocate " +- "pseudo file\n"); + pseudo->name[i].pathname = + strdup(alltarget); +- memcpy(pseudo->name[i].dev, pseudo_dev, +- sizeof(struct pseudo_dev)); ++ pseudo->name[i].dev = pseudo_dev; + } else + ERROR("%s already exists as a " + "directory. Ignoring %s!\n", +@@ -229,16 +223,113 @@ struct pseudo_entry *pseudo_readdir(stru + } + + ++int exec_file(char *command, struct pseudo_dev *dev) ++{ ++ int child, res; ++ static pid_t pid = -1; ++ int pipefd[2]; ++#ifdef USE_TMP_FILE ++ char filename[1024]; ++ int status; ++ static int number = 0; ++#endif ++ ++ if(pid == -1) ++ pid = getpid(); ++ ++#ifdef USE_TMP_FILE ++ sprintf(filename, "/tmp/squashfs_pseudo_%d_%d", pid, number ++); ++ pipefd[1] = open(filename, O_CREAT | O_TRUNC | O_RDWR, S_IRWXU); ++ if(pipefd[1] == -1) { ++ printf("open failed\n"); ++ return -1; ++ } ++#else ++ res = pipe(pipefd); ++ if(res == -1) { ++ printf("pipe failed\n"); ++ return -1; ++ } ++#endif ++ ++ child = fork(); ++ if(child == -1) { ++ printf("fork failed\n"); ++ goto failed; ++ } ++ ++ if(child == 0) { ++ close(STDOUT_FILENO); ++ res = dup(pipefd[1]); ++ if(res == -1) { ++ printf("dup failed\n"); ++ exit(EXIT_FAILURE); ++ } ++ execl("/bin/sh", "sh", "-c", command, (char *) NULL); ++ printf("execl failed\n"); ++ exit(EXIT_FAILURE); ++ } ++ ++#ifdef USE_TMP_FILE ++ res = waitpid(child, &status, 0); ++ close(pipefd[1]); ++ if(res != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0) { ++ dev->filename = strdup(filename); ++ return 0; ++ } ++failed: ++ unlink(filename); ++ return -1; ++#else ++ close(pipefd[1]); ++ dev->fd = pipefd[0]; ++ dev->child = child; ++ return 0; ++failed: ++ return -1; ++#endif ++} ++ ++ ++void add_pseudo_file(struct pseudo_dev *dev) ++{ ++ pseudo_file = realloc(pseudo_file, (pseudo_count + 1) * ++ sizeof(struct pseudo_dev *)); ++ if(pseudo_file == NULL) ++ BAD_ERROR("Failed to realloc pseudo_file\n"); ++ ++ dev->pseudo_id = pseudo_count; ++ pseudo_file[pseudo_count ++] = dev; ++} ++ ++ ++void delete_pseudo_files() ++{ ++#ifdef USE_TMP_FILE ++ int i; ++ ++ for(i = 0; i < pseudo_count; i++) ++ unlink(pseudo_file[i]->filename); ++#endif ++} ++ ++ ++struct pseudo_dev *get_pseudo_file(int pseudo_id) ++{ ++ return pseudo_file[pseudo_id]; ++} ++ ++ + int read_pseudo_def(struct pseudo **pseudo, char *def) + { +- int n; ++ int n, bytes; + unsigned int major = 0, minor = 0, mode; + char filename[2048], type, suid[100], sgid[100], *ptr; + long long uid, gid; +- struct pseudo_dev dev; ++ struct pseudo_dev *dev; + +- n = sscanf(def, "%s %c %o %s %s %u %u", filename, &type, &mode, suid, sgid, +- &major, &minor); ++ n = sscanf(def, "%s %c %o %s %s %n", filename, &type, &mode, suid, ++ sgid, &bytes); + + if(n < 5) { + ERROR("Not enough or invalid arguments in pseudo file " +@@ -249,7 +340,9 @@ int read_pseudo_def(struct pseudo **pseu + switch(type) { + case 'b': + case 'c': +- if(n < 7) { ++ n = sscanf(def + bytes, "%u %u", &major, &minor); ++ ++ if(n < 2) { + ERROR("Not enough or invalid arguments in pseudo file " + "definition\n"); + goto error; +@@ -265,47 +358,15 @@ int read_pseudo_def(struct pseudo **pseu + goto error; + } + +- /* fall through */ +- case 'd': +- if(mode > 0777) { +- ERROR("Mode %o out of range\n", mode); ++ case 'f': ++ if(def[bytes] == '\0') { ++ ERROR("Not enough arguments in pseudo file " ++ "definition\n"); + goto error; +- } +- +- uid = strtoll(suid, &ptr, 10); +- if(*ptr == '\0') { +- if(uid < 0 || uid > ((1LL << 32) - 1)) { +- ERROR("Uid %s out of range\n", suid); +- goto error; +- } +- } else { +- struct passwd *pwuid = getpwnam(suid); +- if(pwuid) +- uid = pwuid->pw_uid; +- else { +- ERROR("Uid %s invalid uid or unknown user\n", +- suid); +- goto error; +- } +- } +- +- gid = strtoll(sgid, &ptr, 10); +- if(*ptr == '\0') { +- if(gid < 0 || gid > ((1LL << 32) - 1)) { +- ERROR("Gid %s out of range\n", sgid); +- goto error; +- } +- } else { +- struct group *grgid = getgrnam(sgid); +- if(grgid) +- gid = grgid->gr_gid; +- else { +- ERROR("Gid %s invalid uid or unknown user\n", +- sgid); +- goto error; +- } +- } +- ++ } ++ break; ++ case 'd': ++ case 'm': + break; + default: + ERROR("Unsupported type %c\n", type); +@@ -313,6 +374,43 @@ int read_pseudo_def(struct pseudo **pseu + } + + ++ if(mode > 0777) { ++ ERROR("Mode %o out of range\n", mode); ++ goto error; ++ } ++ ++ uid = strtoll(suid, &ptr, 10); ++ if(*ptr == '\0') { ++ if(uid < 0 || uid > ((1LL << 32) - 1)) { ++ ERROR("Uid %s out of range\n", suid); ++ goto error; ++ } ++ } else { ++ struct passwd *pwuid = getpwnam(suid); ++ if(pwuid) ++ uid = pwuid->pw_uid; ++ else { ++ ERROR("Uid %s invalid uid or unknown user\n", suid); ++ goto error; ++ } ++ } ++ ++ gid = strtoll(sgid, &ptr, 10); ++ if(*ptr == '\0') { ++ if(gid < 0 || gid > ((1LL << 32) - 1)) { ++ ERROR("Gid %s out of range\n", sgid); ++ goto error; ++ } ++ } else { ++ struct group *grgid = getgrnam(sgid); ++ if(grgid) ++ gid = grgid->gr_gid; ++ else { ++ ERROR("Gid %s invalid uid or unknown user\n", sgid); ++ goto error; ++ } ++ } ++ + switch(type) { + case 'b': + mode |= S_IFBLK; +@@ -323,16 +421,37 @@ int read_pseudo_def(struct pseudo **pseu + case 'd': + mode |= S_IFDIR; + break; ++ case 'f': ++ mode |= S_IFREG; ++ break; + } + +- dev.type = type; +- dev.mode = mode; +- dev.uid = uid; +- dev.gid = gid; +- dev.major = major; +- dev.minor = minor; ++ dev = malloc(sizeof(struct pseudo_dev)); ++ if(dev == NULL) ++ BAD_ERROR("Failed to create pseudo_dev\n"); ++ ++ dev->type = type; ++ dev->mode = mode; ++ dev->uid = uid; ++ dev->gid = gid; ++ dev->major = major; ++ dev->minor = minor; ++ ++ if(type == 'f') { ++ int res; ++ ++ printf("Executing dynamic pseudo file\n"); ++ printf("\t\"%s\"\n", def); ++ res = exec_file(def + bytes, dev); ++ if(res == -1) { ++ ERROR("Failed to execute dynamic pseudo file definition" ++ " \"%s\"\n", def); ++ return FALSE; ++ } ++ add_pseudo_file(dev); ++ } + +- *pseudo = add_pseudo(*pseudo, &dev, filename, filename); ++ *pseudo = add_pseudo(*pseudo, dev, filename, filename); + + return TRUE; + +diff -Naurp squashfs-tools/pseudo.h squashfs-tools-lzma/pseudo.h +--- squashfs-tools/pseudo.h 2009-04-04 01:44:24.000000000 +0000 ++++ squashfs-tools/pseudo.h 2009-10-20 04:03:38.000000000 +0000 +@@ -27,6 +27,12 @@ struct pseudo_dev { + unsigned int gid; + unsigned int major; + unsigned int minor; ++ int pseudo_id; ++ int fd; ++ int child; ++#ifdef USE_TMP_FILE ++ char *filename; ++#endif + }; + + struct pseudo_entry { +@@ -46,3 +52,5 @@ extern int read_pseudo_def(struct pseudo + extern int read_pseudo_file(struct pseudo **, char *); + extern struct pseudo *pseudo_subdir(char *, struct pseudo *); + extern struct pseudo_entry *pseudo_readdir(struct pseudo *); ++extern struct pseudo_dev *get_pseudo_file(int); ++extern void delete_pseudo_files(); +diff -Naurp squashfs-tools/read_fs.c squashfs-tools-lzma/read_fs.c +--- squashfs-tools/read_fs.c 2009-03-31 04:23:14.000000000 +0000 ++++ squashfs-tools/read_fs.c 2009-10-20 04:03:38.000000000 +0000 +@@ -36,7 +36,6 @@ extern unsigned int get_guid(unsigned in + #include <fcntl.h> + #include <errno.h> + #include <string.h> +-#include <zlib.h> + #include <sys/mman.h> + + #ifndef linux +@@ -51,6 +50,7 @@ extern unsigned int get_guid(unsigned in + #include "squashfs_swap.h" + #include "read_fs.h" + #include "global.h" ++#include "compressor.h" + + #include <stdlib.h> + +@@ -66,7 +66,9 @@ extern unsigned int get_guid(unsigned in + fprintf(stderr, s, ## args); \ + } while(0) + +-int read_block(int fd, long long start, long long *next, unsigned char *block, ++static struct compressor *comp; ++ ++int read_block(int fd, long long start, long long *next, void *block, + squashfs_super_block *sBlk) + { + unsigned short c_byte; +@@ -77,32 +79,24 @@ int read_block(int fd, long long start, + + if(SQUASHFS_COMPRESSED(c_byte)) { + char buffer[SQUASHFS_METADATA_SIZE]; +- int res; +- unsigned long bytes = SQUASHFS_METADATA_SIZE; ++ int error, res; + + c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte); + read_destination(fd, start + offset, c_byte, buffer); + +- res = uncompress(block, &bytes, (const unsigned char *) buffer, +- c_byte); +- if(res != Z_OK) { +- if(res == Z_MEM_ERROR) +- ERROR("zlib::uncompress failed, not enough " +- "memory\n"); +- else if(res == Z_BUF_ERROR) +- ERROR("zlib::uncompress failed, not enough " +- "room in output buffer\n"); +- else +- ERROR("zlib::uncompress failed, unknown error " +- "%d\n", res); ++ res = comp->uncompress(block, buffer, c_byte, ++ SQUASHFS_METADATA_SIZE, &error); ++ if(res == -1) { ++ ERROR("%s uncompress failed with error code %d\n", ++ comp->name, error); + return 0; + } + if(next) + *next = start + offset + c_byte; +- return bytes; ++ return res; + } else { + c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte); +- read_destination(fd, start + offset, c_byte, (char *) block); ++ read_destination(fd, start + offset, c_byte, block); + if(next) + *next = start + offset + c_byte; + return c_byte; +@@ -356,7 +350,7 @@ failed: + } + + +-int read_super(int fd, squashfs_super_block *sBlk, char *source) ++struct compressor *read_super(int fd, squashfs_super_block *sBlk, char *source) + { + read_destination(fd, SQUASHFS_START, sizeof(squashfs_super_block), + (char *) sBlk); +@@ -388,8 +382,18 @@ int read_super(int fd, squashfs_super_bl + goto failed_mount; + } + ++ /* Check the compression type */ ++ comp = lookup_compressor_id(sBlk->compression); ++ if(!comp->supported) { ++ ERROR("Filesystem on %s uses %s compression, this is" ++ "unsupported by this version\n", source, comp->name); ++ display_compressors("", ""); ++ goto failed_mount; ++ } ++ + printf("Found a valid %sSQUASHFS superblock on %s.\n", + SQUASHFS_EXPORTABLE(sBlk->flags) ? "exportable " : "", source); ++ printf("\tCompression used %s\n", comp->name); + printf("\tInodes are %scompressed\n", + SQUASHFS_UNCOMPRESSED_INODES(sBlk->flags) ? "un" : ""); + printf("\tData is %scompressed\n", +@@ -417,10 +421,10 @@ int read_super(int fd, squashfs_super_bl + TRACE("sBlk->lookup_table_start %llx\n", sBlk->lookup_table_start); + printf("\n"); + +- return TRUE; ++ return comp; + + failed_mount: +- return FALSE; ++ return NULL; + } + + +@@ -514,12 +518,17 @@ unsigned int *read_id_table(int fd, squa + SQUASHFS_INSWAP_ID_BLOCKS(index, indexes); + + for(i = 0; i < indexes; i++) { +- int length; +- length = read_block(fd, index[i], NULL, ++ int length = read_block(fd, index[i], NULL, + ((unsigned char *) id_table) + + (i * SQUASHFS_METADATA_SIZE), sBlk); + TRACE("Read id table block %d, from 0x%llx, length %d\n", i, + index[i], length); ++ if(length == 0) { ++ ERROR("Failed to read id table block %d, from 0x%llx, " ++ "length %d\n", i, index[i], length); ++ free(id_table); ++ return NULL; ++ } + } + + SQUASHFS_INSWAP_INTS(id_table, sBlk->no_ids); +@@ -563,6 +572,13 @@ int read_fragment_table(int fd, squashfs + (i * SQUASHFS_METADATA_SIZE), sBlk); + TRACE("Read fragment table block %d, from 0x%llx, length %d\n", + i, fragment_table_index[i], length); ++ if(length == 0) { ++ ERROR("Failed to read fragment table block %d, from " ++ "0x%llx, length %d\n", i, ++ fragment_table_index[i], length); ++ free(*fragment_table); ++ return 0; ++ } + } + + for(i = 0; i < sBlk->fragments; i++) +@@ -599,6 +615,13 @@ int read_inode_lookup_table(int fd, squa + (i * SQUASHFS_METADATA_SIZE), sBlk); + TRACE("Read inode lookup table block %d, from 0x%llx, length " + "%d\n", i, index[i], length); ++ if(length == 0) { ++ ERROR("Failed to read inode lookup table block %d, " ++ "from 0x%llx, length %d\n", i, index[i], ++ length); ++ free(*inode_lookup_table); ++ return 0; ++ } + } + + SQUASHFS_INSWAP_LONG_LONGS(*inode_lookup_table, sBlk->inodes); +diff -Naurp squashfs-tools/sort.c squashfs-tools-lzma/sort.c +--- squashfs-tools/sort.c 2009-03-31 04:25:53.000000000 +0000 ++++ squashfs-tools/sort.c 2009-10-20 04:03:38.000000000 +0000 +@@ -198,7 +198,7 @@ void generate_file_priorities(struct dir + while(dir->current_count < dir->count) { + struct dir_ent *dir_ent = dir->list[dir->current_count++]; + struct stat *buf = &dir_ent->inode->buf; +- if(dir_ent->data) ++ if(dir_ent->inode->root_entry) + continue; + + switch(buf->st_mode & S_IFMT) { +@@ -254,6 +254,7 @@ void sort_files_and_write(struct dir_inf + write_file(&inode, entry->dir, &duplicate_file); + INFO("file %s, uncompressed size %lld bytes %s" + "\n", entry->dir->pathname, ++ (long long) + entry->dir->inode->buf.st_size, + duplicate_file ? "DUPLICATE" : ""); + entry->dir->inode->inode = inode; +@@ -261,6 +262,7 @@ void sort_files_and_write(struct dir_inf + } else + INFO("file %s, uncompressed size %lld bytes " + "LINK\n", entry->dir->pathname, ++ (long long) + entry->dir->inode->buf.st_size); + } + } +diff -Naurp squashfs-tools/sort.h squashfs-tools-lzma/sort.h +--- squashfs-tools/sort.h 2009-02-08 12:02:53.000000000 +0000 ++++ squashfs-tools/sort.h 2009-10-20 04:03:38.000000000 +0000 +@@ -42,17 +42,19 @@ struct dir_ent { + struct inode_info *inode; + struct dir_info *dir; + struct dir_info *our_dir; +- struct old_root_entry_info *data; + }; + + struct inode_info { +- unsigned int nlink; + struct stat buf; ++ struct inode_info *next; + squashfs_inode inode; +- unsigned int type; + unsigned int inode_number; ++ unsigned int nlink; ++ int pseudo_id; ++ char type; + char read; +- struct inode_info *next; ++ char root_entry; ++ char pseudo_file; + }; + + struct priority_entry { +diff -Naurp squashfs-tools/squashfs_compat.h squashfs-tools-lzma/squashfs_compat.h +--- squashfs-tools/squashfs_compat.h 2009-03-16 04:27:27.000000000 +0000 ++++ squashfs-tools/squashfs_compat.h 2009-10-20 04:03:38.000000000 +0000 +@@ -777,11 +777,10 @@ typedef union squashfs_inode_header_2 sq + #endif + + #define _SQUASHFS_SWAP(value, p, pos, tbits, SHIFT) {\ +- int bits;\ +- int b_pos = pos % 8;\ +- unsigned long long val = 0;\ +- unsigned char *s = (unsigned char *)p + (pos / 8);\ +- unsigned char *d = ((unsigned char *) &val) + 7;\ ++ b_pos = pos % 8;\ ++ val = 0;\ ++ s = (unsigned char *)p + (pos / 8);\ ++ d = ((unsigned char *) &val) + 7;\ + for(bits = 0; bits < (tbits + b_pos); bits += 8) \ + *d-- = *s++;\ + value = (val >> (SHIFT))/* & ((1 << tbits) - 1)*/;\ +diff -Naurp squashfs-tools/squashfs_fs.h squashfs-tools-lzma/squashfs_fs.h +--- squashfs-tools/squashfs_fs.h 2009-03-18 02:50:20.000000000 +0000 ++++ squashfs-tools/squashfs_fs.h 2009-10-20 04:03:38.000000000 +0000 +@@ -229,6 +229,7 @@ typedef long long squashfs_block_t; + typedef long long squashfs_inode_t; + + #define ZLIB_COMPRESSION 1 ++#define LZMA_COMPRESSION 2 + + struct squashfs_super_block { + unsigned int s_magic; +diff -Naurp squashfs-tools/unsquash-3.c squashfs-tools-lzma/unsquash-3.c +--- squashfs-tools/unsquash-3.c 2009-03-31 04:35:10.000000000 +0000 ++++ squashfs-tools/unsquash-3.c 2009-10-20 04:03:38.000000000 +0000 +@@ -36,7 +36,7 @@ int read_fragment_table_3() + sBlk.fragment_table_start); + + if(sBlk.fragments == 0) +- return; ++ return TRUE; + + if((fragment_table = malloc(sBlk.fragments * + sizeof(squashfs_fragment_entry_3))) == NULL) +diff -Naurp squashfs-tools/unsquash-4.c squashfs-tools-lzma/unsquash-4.c +--- squashfs-tools/unsquash-4.c 2009-03-31 04:38:31.000000000 +0000 ++++ squashfs-tools/unsquash-4.c 2009-10-20 04:03:38.000000000 +0000 +@@ -38,7 +38,7 @@ int read_fragment_table_4() + sBlk.fragment_table_start); + + if(sBlk.fragments == 0) +- return; ++ return TRUE; + + if((fragment_table = malloc(sBlk.fragments * + sizeof(squashfs_fragment_entry))) == NULL) +diff -Naurp squashfs-tools/unsquashfs.c squashfs-tools-lzma/unsquashfs.c +--- squashfs-tools/unsquashfs.c 2009-04-05 21:23:06.000000000 +0000 ++++ squashfs-tools/unsquashfs.c 2009-10-20 04:03:39.000000000 +0000 +@@ -25,6 +25,9 @@ + #include "squashfs_swap.h" + #include "squashfs_compat.h" + #include "read_fs.h" ++#include "compressor.h" ++ ++#include <sys/sysinfo.h> + + struct cache *fragment_cache, *data_cache; + struct queue *to_reader, *to_deflate, *to_writer, *from_writer; +@@ -36,6 +39,7 @@ int processors = -1; + + struct super_block sBlk; + squashfs_operations s_ops; ++struct compressor *comp; + + int bytes = 0, swap, file_count = 0, dir_count = 0, sym_count = 0, + dev_count = 0, fifo_count = 0; +@@ -590,31 +594,23 @@ int read_block(long long start, long lon + offset = 3; + if(SQUASHFS_COMPRESSED(c_byte)) { + char buffer[SQUASHFS_METADATA_SIZE]; +- int res; +- unsigned long bytes = SQUASHFS_METADATA_SIZE; ++ int error, res; + + c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte); + if(read_bytes(start + offset, c_byte, buffer) == FALSE) + goto failed; + +- res = uncompress((unsigned char *) block, &bytes, +- (const unsigned char *) buffer, c_byte); ++ res = comp->uncompress(block, buffer, c_byte, ++ SQUASHFS_METADATA_SIZE, &error); + +- if(res != Z_OK) { +- if(res == Z_MEM_ERROR) +- ERROR("zlib::uncompress failed, not enough " +- "memory\n"); +- else if(res == Z_BUF_ERROR) +- ERROR("zlib::uncompress failed, not enough " +- "room in output buffer\n"); +- else +- ERROR("zlib::uncompress failed, unknown error " +- "%d\n", res); ++ if(res == -1) { ++ ERROR("%s uncompress failed with error code %d\n", ++ comp->name, error); + goto failed; + } + if(next) + *next = start + offset + c_byte; +- return bytes; ++ return res; + } else { + c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte); + if(read_bytes(start + offset, c_byte, block) == FALSE) +@@ -632,36 +628,26 @@ failed: + + int read_data_block(long long start, unsigned int size, char *block) + { +- int res; +- unsigned long bytes = block_size; ++ int error, res; + int c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(size); + + TRACE("read_data_block: block @0x%llx, %d %s bytes\n", start, +- SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte), +- SQUASHFS_COMPRESSED_BLOCK(c_byte) ? "compressed" : ++ c_byte, SQUASHFS_COMPRESSED_BLOCK(size) ? "compressed" : + "uncompressed"); + + if(SQUASHFS_COMPRESSED_BLOCK(size)) { + if(read_bytes(start, c_byte, data) == FALSE) + goto failed; + +- res = uncompress((unsigned char *) block, &bytes, +- (const unsigned char *) data, c_byte); ++ res = comp->uncompress(block, data, c_byte, block_size, &error); + +- if(res != Z_OK) { +- if(res == Z_MEM_ERROR) +- ERROR("zlib::uncompress failed, not enough " +- "memory\n"); +- else if(res == Z_BUF_ERROR) +- ERROR("zlib::uncompress failed, not enough " +- "room in output buffer\n"); +- else +- ERROR("zlib::uncompress failed, unknown error " +- "%d\n", res); ++ if(res == -1) { ++ ERROR("%s uncompress failed with error code %d\n", ++ comp->name, error); + goto failed; + } + +- return bytes; ++ return res; + } else { + if(read_bytes(start, c_byte, block) == FALSE) + goto failed; +@@ -671,7 +657,7 @@ int read_data_block(long long start, uns + + failed: + ERROR("read_data_block: failed to read block @0x%llx, size %d\n", start, +- size); ++ c_byte); + return FALSE; + } + +@@ -1383,6 +1369,11 @@ void squashfs_stat(char *source) + #endif + printf("Creation or last append time %s", mkfs_str ? mkfs_str : + "failed to get time\n"); ++ printf("Filesystem size %.2f Kbytes (%.2f Mbytes)\n", ++ sBlk.bytes_used / 1024.0, sBlk.bytes_used / (1024.0 * 1024.0)); ++ if(sBlk.s_major == 4) ++ printf("Compression %s\n", comp->name); ++ printf("Block size %d\n", sBlk.block_size); + printf("Filesystem is %sexportable via NFS\n", + SQUASHFS_EXPORTABLE(sBlk.flags) ? "" : "not "); + +@@ -1409,9 +1400,6 @@ void squashfs_stat(char *source) + SQUASHFS_DUPLICATES(sBlk.flags) ? "" : "not "); + else + printf("Duplicates are removed\n"); +- printf("Filesystem size %.2f Kbytes (%.2f Mbytes)\n", +- sBlk.bytes_used / 1024.0, sBlk.bytes_used / (1024.0 * 1024.0)); +- printf("Block size %d\n", sBlk.block_size); + if(sBlk.s_major > 1) + printf("Number of fragments %d\n", sBlk.fragments); + printf("Number of inodes %d\n", sBlk.inodes); +@@ -1459,6 +1447,18 @@ int read_super(char *source) + s_ops.read_inode = read_inode_4; + s_ops.read_uids_guids = read_uids_guids_4; + memcpy(&sBlk, &sBlk_4, sizeof(sBlk_4)); ++ ++ /* ++ * Check the compression type ++ */ ++ comp = lookup_compressor_id(sBlk.compression); ++ if(!comp->supported) { ++ ERROR("Filesystem uses %s compression, this is " ++ "unsupported by this version\n", comp->name); ++ ERROR("Decompressors available:\n"); ++ display_compressors("", ""); ++ goto failed_mount; ++ } + return TRUE; + } + +@@ -1548,6 +1548,11 @@ int read_super(char *source) + goto failed_mount; + } + ++ /* ++ * 1.x, 2.x and 3.x filesystems use gzip compression. Gzip is always ++ * suppported. ++ */ ++ comp = lookup_compressor("gzip"); + return TRUE; + + failed_mount: +@@ -1707,32 +1712,24 @@ void *deflator(void *arg) + + while(1) { + struct cache_entry *entry = queue_get(to_deflate); +- int res; +- unsigned long bytes = block_size; ++ int error, res; + +- res = uncompress((unsigned char *) tmp, &bytes, +- (const unsigned char *) entry->data, +- SQUASHFS_COMPRESSED_SIZE_BLOCK(entry->size)); +- +- if(res != Z_OK) { +- if(res == Z_MEM_ERROR) +- ERROR("zlib::uncompress failed, not enough" +- "memory\n"); +- else if(res == Z_BUF_ERROR) +- ERROR("zlib::uncompress failed, not enough " +- "room in output buffer\n"); +- else +- ERROR("zlib::uncompress failed, unknown error " +- "%d\n", res); +- } else +- memcpy(entry->data, tmp, bytes); ++ res = comp->uncompress(tmp, entry->data, ++ SQUASHFS_COMPRESSED_SIZE_BLOCK(entry->size), block_size, ++ &error); ++ ++ if(res == -1) ++ ERROR("%s uncompress failed with error code %d\n", ++ comp->name, error); ++ else ++ memcpy(entry->data, tmp, res); + + /* + * block has been either successfully decompressed, or an error + * occurred, clear pending flag, set error appropriately and + * wake up any threads waiting on this block + */ +- cache_block_ready(entry, res != Z_OK); ++ cache_block_ready(entry, res == -1); + } + } + +@@ -1938,7 +1935,6 @@ int main(int argc, char *argv[]) + int fragment_buffer_size = FRAGMENT_BUFFER_DEFAULT; + int data_buffer_size = DATA_BUFFER_DEFAULT; + char *b; +- struct winsize winsize; + + pthread_mutex_init(&screen_mutex, NULL); + root_process = geteuid() == 0; +@@ -2087,6 +2083,8 @@ options: + "regular expressions\n"); + ERROR("\t\t\t\trather than use the default shell " + "wildcard\n\t\t\t\texpansion (globbing)\n"); ++ ERROR("\nDecompressors available:\n"); ++ display_compressors("", ""); + } + exit(1); + } +diff -Naurp squashfs-tools/unsquashfs.h squashfs-tools-lzma/unsquashfs.h +--- squashfs-tools/unsquashfs.h 2009-03-29 02:29:02.000000000 +0000 ++++ squashfs-tools/unsquashfs.h 2009-10-20 04:03:39.000000000 +0000 +@@ -31,7 +31,6 @@ + #include <fcntl.h> + #include <errno.h> + #include <string.h> +-#include <zlib.h> + #include <sys/mman.h> + #include <utime.h> + #include <pwd.h> diff --git a/main/xbmc-dharma/APKBUILD b/main/xbmc-dharma/APKBUILD new file mode 100644 index 000000000..3f3352494 --- /dev/null +++ b/main/xbmc-dharma/APKBUILD @@ -0,0 +1,70 @@ +# Contributor: Carlo Landmeter +# Maintainer: + +pkgname=xbmc-dharma +pkgver=34026 +pkgrel=1 +pkgdesc="XBMC Media Center Dharma branch" +url="http://xbmc.org" +license="LGPL" +depends="udisks xorg-server" +subpackages="$pkgname-confluence $pkgname-doc" +makedepends="boost-dev cmake gperf nasm subversion libvdpau-dev libxrandr-dev libxinerama-dev +lzo-dev libxtst-dev jasper-dev mysql-dev libsamplerate-dev wavpack-dev libssh-dev bzip2-dev +sdl_image-dev dbus-dev libmms-dev faac-dev glew-dev libmicrohttpd-dev libcdio-dev samba-xbmc-dev +libmad-dev mesa-dev fribidi-dev libmpeg2-dev faad2-dev sdl_mixer-dev libass-dev autoconf +gettext-dev automake libtool enca-dev libxt-dev libxmu-dev coreutils yasm libbluray-dev +jpeg-dev libogg-dev python-dev libvorbis-dev sqlite-dev alsa-lib-dev zlib-dev openssl-dev +freetype-dev fontconfig-dev libpng-dev pcre-dev glib-dev libmodplug-dev flac-dev tiff-dev +curl-dev sdl-dev gawk zip rtmpdump-dev libbluray-dev coreutils findutils" + +source="http://alpine.nethq.org/clandmeter/src/xbmc-dharma-33482.tar.bz2" + +_builddir="$srcdir"/xbmc-dharma + +prepare() { + cd "$_builddir" + #update to sepecified commit + svn up -r $pkgver || return 1 +} + +build() { + cd "$_builddir" + + #xbmc will detect ccache causing build issues + unset CC && export CC="gcc" + + ./bootstrap || return 1 + + ./configure --prefix=/usr \ + --sysconfdir=/etc \ + --mandir=/usr/share/man \ + --infodir=/usr/share/info \ + --exec-prefix=/usr \ + --disable-debug \ + --disable-optimizations \ + --enable-gl \ + --enable-vdpau \ + --disable-profiling \ + --enable-joystick \ + --disable-xrandr \ + --disable-pulse \ + --disable-hal \ + --enable-external-python \ + --disable-avahi + + make || return 1 +} + +package() { + cd "$_builddir" + make DESTDIR="$pkgdir" install +} + +#make package of theme +confluence() { + mkdir -p "$subpkgdir"/usr/share/xbmc/addons + mv "$pkgdir"/usr/share/xbmc/addons/skin.confluence "$subpkgdir"/usr/share/xbmc/addons/ +} + +md5sums="81430735ee5af65a429a3ed76a4da99f xbmc-dharma-33482.tar.bz2" diff --git a/main/xorg-server/APKBUILD b/main/xorg-server/APKBUILD index 8f22a6b2f..011b2617e 100644 --- a/main/xorg-server/APKBUILD +++ b/main/xorg-server/APKBUILD @@ -1,6 +1,6 @@ # Maintainer: Natanael Copa <ncopa@alpinelinux.org> pkgname=xorg-server -pkgver=1.7.7 +pkgver=1.9.0 pkgrel=1 pkgdesc="X.Org X servers" url="http://xorg.freedesktop.org" @@ -23,10 +23,10 @@ makedepends=" libxv-dev libxxf86dga-dev libxxf86misc-dev + recordproto openssl-dev pixman-dev zlib-dev - bigreqsproto compositeproto damageproto @@ -52,6 +52,8 @@ makedepends=" automake autoconf util-macros + udev-dev + xproto " # hal>=0.5.11 xcursor-themes xkeyboard-config>=1.3 # xorg-server-utils xorg-utils xorg-fonts-misc xbitmaps libdrm>=2.3.1 @@ -80,16 +82,18 @@ build() { # cannot pass over the linker flag to .so files. so we tweak the # gcc specs. export LDFLAGS="$LDFLAGS -Wl,-z,lazy" + _fontroot="/usr/share/fonts" ./configure --prefix=/usr \ --sysconfdir=/etc/X11 \ --localstatedir=/var \ - --with-default-font-path=/usr/share/fonts/misc,/usr/share/fonts/100dpi:unscaled,/usr/share/fonts/75dpi:unscaled,/usr/share/fonts/TTF,/usr/share/fonts/Type1 \ + --with-fontrootdir=$_fontroot \ + --with-default-font-path=${_fontroot}/misc,${_fontroot}/100dpi:unscaled,${_fontroot}/75dpi:unscaled,${_fontroot}/TTF,${_fontroot}/Type1 \ --with-xkb-path=/usr/share/X11/xkb \ --with-xkb-output=/var/lib/xkb \ --with-dri-driver-path=/usr/lib/xorg/modules/dri \ --enable-composite \ - --enable-config-dbus \ + --enable-config-udev \ --enable-dri \ --enable-ipv6 \ --enable-xfbdev \ @@ -97,6 +101,7 @@ build() { --enable-xorg \ --enable-xv \ --enable-xres \ + --enable-xace \ --disable-xephyr \ --disable-config-hal \ --disable-dmx \ @@ -104,10 +109,11 @@ build() { --disable-xnest \ --disable-xsdl \ --disable-aiglx \ - --disable-xace \ + --disable-config-dbus \ + --enable-config-udev \ || return 1 - make + make || return 1 } package() { @@ -141,5 +147,6 @@ xephyr() { mv "$pkgdir"/usr/bin/Xephyr "$subpkgdir"/usr/bin/ } -md5sums="8c0146330fb155c23d947ac37d431d4b xorg-server-1.7.7.tar.bz2 +md5sums="ba1173998a5a4216fd7b40eded96697e xorg-server-1.9.0.tar.bz2 222de594206d1148a90eddfda4f7a11a xorg-redhat-die-ugly-pattern-die-die-die.patch" + diff --git a/main/xorg-server/xorg-server.post-deinstall b/main/xorg-server/xorg-server.post-deinstall new file mode 100644 index 000000000..e90b4199f --- /dev/null +++ b/main/xorg-server/xorg-server.post-deinstall @@ -0,0 +1,6 @@ +#!/bin/sh + +LIBGLX="/usr/lib/xorg/modules/extensions/libglx.so" +if [ -h "$LIBGLX" ] && [ ! -e "$LIBGLX" ]; then + rm -f "$LIBGLX" +fi diff --git a/main/xorg-server/xorg-server.post-install b/main/xorg-server/xorg-server.post-install new file mode 100644 index 000000000..799b2fc8a --- /dev/null +++ b/main/xorg-server/xorg-server.post-install @@ -0,0 +1,4 @@ +#!/bin/sh + +cd "/usr/lib/xorg/modules/extensions" +ln -s libglx_xorg.so libglx.so |