diff --git a/README b/README index f56721313..6fb5d3942 100644 --- a/README +++ b/README @@ -171,8 +171,7 @@ The Genode source tree is composed of the following subdirectories: :'ports-': These platform-specific source-code repositories contain software that - capitalizes special features of the respective kernel platform. I.e., - for the OKL4 base platform, a port of OKLinux is provided in 'ports-okl4'. + capitalizes special features of the respective kernel platform. For the Fiasco.OC platform, 'ports-foc' hosts a port of the L4Linux kernel. For further information, please refer to the README file at the top level of the respective repository. diff --git a/doc/components.txt b/doc/components.txt index 5610c3ed2..aeba2ed27 100644 --- a/doc/components.txt +++ b/doc/components.txt @@ -455,10 +455,6 @@ Runtime environments that enables the use of unmodified command-line based GNU software. For using noux, refer to the run script 'ports/run/noux.run'. -:'ports-okl4/src/oklinux': OKLinux is a paravirtualized Linux kernel that - enables the use of Linux-based OSes as subsystems on the OKL4 kernel. For - using OKLinux, refer to the run script 'ports-okl4/run/lx_block.run'. - :'ports-foc/src/l4linux': L4Linux is a paravirtualized Linux kernel that enables the use of Linux-based OSes as subsystems on the Fiasco.OC kernel. For using L4Linux, refer to the run script 'ports-foc/run/l4linux.run'. diff --git a/ports-okl4/Makefile b/ports-okl4/Makefile deleted file mode 100644 index ab30ec472..000000000 --- a/ports-okl4/Makefile +++ /dev/null @@ -1,83 +0,0 @@ -# -# \brief Download, unpack and patch OKLinux source code -# \author Stefan Kalkowski -# \date 2010-01-06 - -DOWNLOAD_DIR = download -CONTRIB_DIR = contrib -PATCH_FILE = patches/oklx_genode.patch -PATCH_UNIONFS = patches/unionfs.patch - -VERBOSE ?= @ -ECHO = @echo -OKLX_URL = http://wiki.ok-labs.com/downloads/release-2.1.1-patch.9 -OKLX_VERSION = oklinux_2.6.23.24 -OKLX_ARCHIVE = $(OKLX_VERSION).tar.gz -OKLX_CONTRIB = kernel-2.6.23-v2 - -# -# Utility to check if a tool is installed -# -check_tool = $(if $(shell which $(1)),,$(error Need to have '$(1)' installed.)) - -$(call check_tool,wget) -$(call check_tool,patch) -$(call check_tool,sed) - -# -# Print help information by default -# -help: - $(ECHO) - $(ECHO) "Prepare the OKLinux repository" - $(ECHO) - $(ECHO) "--- available commands ---" - $(ECHO) "prepare - download and extract the OKLinux source code" - $(ECHO) "clean - clean everything except downloaded archives" - $(ECHO) "cleanall - clean everything including downloaded archives" - $(ECHO) "update-patch - updates patch to the original OKLinux code" - $(ECHO) - -prepare: $(CONTRIB_DIR) message - -$(CONTRIB_DIR): $(DOWNLOAD_DIR)/$(OKLX_ARCHIVE) - $(ECHO) "unpacking source code to '$(CONTRIB_DIR)/'" - $(VERBOSE)tar xzf $< - $(VERBOSE)mv $(OKLX_VERSION)/$(OKLX_CONTRIB) $@ - $(VERBOSE)rm -rf $(OKLX_VERSION) - $(ECHO) "applying patches to '$(CONTRIB_DIR)/'" - $(VERBOSE)patch -p0 < $(PATCH_FILE) - $(VERBOSE)cd $(CONTRIB_DIR); patch --no-backup-if-mismatch -p1 < ../$(PATCH_UNIONFS) - -$(DOWNLOAD_DIR)/$(OKLX_ARCHIVE): - $(ECHO) "downloading source code to '$(DOWNLOAD_DIR)/'" - $(VERBOSE)mkdir -p $(DOWNLOAD_DIR) - $(VERBOSE)wget -c $(OKLX_URL)/$(OKLX_ARCHIVE) -O $@ - -message: - $(ECHO) - $(ECHO) "Preparation completed!" - $(ECHO) "Now, go to your Genode build directory and type 'make oklinux'." - $(ECHO) "Hint: don't forget to put '$(shell pwd)' " - $(ECHO) " as a repository into your build.conf" - $(ECHO) - -update-patch: - $(ECHO) "producing a new diff and save it to '$(PATCH_FILE)'" - $(VERBOSE)cd $(CONTRIB_DIR); patch --no-backup-if-mismatch -R -p1 < ../$(PATCH_UNIONFS) - $(VERBOSE)tar xzf $(DOWNLOAD_DIR)/$(OKLX_ARCHIVE) - $(VERBOSE)mv $(OKLX_VERSION)/$(OKLX_CONTRIB) . - $(VERBOSE)find $(CONTRIB_DIR) -name "*~" -delete - $(VERBOSE)LC_COLLATE=C diff -urNpB $(OKLX_CONTRIB) $(CONTRIB_DIR) \ - | sed "s/\(^--- [^\t]*\).*/\\1/" \ - | sed "s/\(^+++ [^\t]*\).*/\\1/" \ - > $(PATCH_FILE) || true - $(VERBOSE)rm -rf $(OKLX_CONTRIB) $(OKLX_VERSION) - $(VERBOSE)cd $(CONTRIB_DIR); patch --no-backup-if-mismatch -p1 < ../$(PATCH_UNIONFS) - -clean: - $(VERBOSE)rm -rf $(CONTRIB_DIR) - -cleanall: clean - $(VERBOSE)rm -rf $(DOWNLOAD_DIR) - diff --git a/ports-okl4/README b/ports-okl4/README deleted file mode 100644 index 15829c540..000000000 --- a/ports-okl4/README +++ /dev/null @@ -1,120 +0,0 @@ -This repository contains the port of OKLinux for Genode. - -OKLinux is a para-virtualized version of Linux running on top of the -micro-kernel OKL4. The original code uses the Iguana framework - a bunch of -server components and libraries to simplify construction of applications -running on top of OKL4. This package contains a small OKLinux support library, -as well as a patch for OKLinux, that replaces Iguana by the Genode framework. -Nevertheless, OKLinux stays to be dependent on OKL4, meaning that -you can only use it in combination with Genode running on top of OKL4. - -Usage ------ - -If you haven't build Genode for OKL4 yet, please refer to the following documents: - -:[http://genode.org/community/wiki/GenodeOnOKL4 - Genode on OKL4 Wiki page]: - This Wiki page contains the information on how to build and use - Genode with OKL4. - -For building OKLinux for Genode, you first need to download and patch the -original sources. The top-level makefile of this repository automates this -task. Just issue: - -! make prepare - -Afterwards you need to include the OKLinux repository into the Genode build -process. Just add the path to this directory to the 'REPOSITORIES' declaration -of the 'etc/build.conf' file within your build directory. -Now, you can change to your build directory and simply type: - -! make oklinux - -That's all. The 'bin/' directory within your build directory should now contain -a symbolic link to the 'vmlinux' binary. -To test your Linux binary, you also need to tweak the config file for init and -for the elfweaver tool. You will find examples for this in the 'config/' -directory of this repository. - -Block, net and audio driver ---------------------------- - -OKLinux for Genode provides special drivers, that provide network-card and -block-device to Linux applications. They are based on the block- and nic-session -interfaces of Genode. - -The drivers are compiled in by default, but needs to be enabled in the -XML configuration of OKLinux, like so: - -! -! -! -! - -The block device gets available under the name 'sda', like traditionally -scsi, ata and sata disks under Linux. The nic is available as the first -ethernet device 'eth0'. The audio-card is available as a simple ALSA device. - -Kernel command line -------------------- - -You can state the Linux kernel command line by using the XML configuration of -the start entry of your OKLinux instance: - -! -! -! - -Initrd ------- - -If you need to start Linux with an initramfs, you have to provide the name of the -archive within the config area of your Linux instance: - -! -! -! - -Of course, you need to add this archive-file to the list of rom-files provided. - -Screens -------- - -Genode's version of OKlinux does include a special screen driver, which maps -framebuffer, input and/or nitpicker sessions to Linux's framebuffer device and -input event device interfaces. -A single screen is either a conglomerate of native framebuffer and input session, -or framebuffer and input sessions of a nitpicker session. To enable one or more -devices in Linux use the 'screens' section in your XML configuration: - -! -! -! -! -! -! - -The order in the 'screens' section determine the order of visible devices in Linux. - -Configure Linux ---------------- - -This OKLinux package contains only a minimal Linux configuration. Especially, -any hardware drivers are missing, as Genode/OKL4 doesn't allow direct hardware -access from Linux. If you want to enable/disable options in Linux, you can -simply do so by using the normal Linux build system. You will find the -'.config' file Linux is using within the 'oklinux/' directory of your build -directory. If you don't want to tweak '.config' directly, you can also change -to the 'oklinux/' directory of your build directory and issue: - -! ARCH=l4 SYSTEM=i386 make menuconfig - -Then you will get the well known ncurses interface. - -Troubleshooting ---------------- - -If you run into problems when building OKLinux and you want the build process -to be somehow more verbose, you can build OKLinux this way: - -! VERBOSE_LX_MK=1 make oklinux diff --git a/ports-okl4/config/elfweaver_config b/ports-okl4/config/elfweaver_config deleted file mode 100644 index e3201bf2b..000000000 --- a/ports-okl4/config/elfweaver_config +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ports-okl4/config/linux_config b/ports-okl4/config/linux_config deleted file mode 100644 index b75b30694..000000000 --- a/ports-okl4/config/linux_config +++ /dev/null @@ -1,606 +0,0 @@ -# -# Automatically generated make config: don't edit -# Linux kernel version: 2.6.23-i386 -# Mon Nov 15 17:17:20 2010 -# -CONFIG_L4=y -CONFIG_GENERIC_HARDIRQS=y -CONFIG_MMU=y -CONFIG_RWSEM_GENERIC_SPINLOCK=y - -# -# L4/Linux-specific options -# -CONFIG_L4KDB_CONSOLE=y -# CONFIG_IG_SERIAL is not set -# CONFIG_IG_INPUT is not set -# CONFIG_IG_MMC is not set -# CONFIG_IG_NET is not set -# CONFIG_IG_AUDIO is not set -# CONFIG_FB_IGVIRTUAL is not set -CONFIG_SCREEN_GENODE=y -CONFIG_BLOCK_GENODE=y -CONFIG_NET_GENODE=y -CONFIG_GENERIC_HWEIGHT=y -CONFIG_GENERIC_IOMAP=y - -# -# System selection -# -# CONFIG_ARCH_MIPS64 is not set -# CONFIG_ARCH_ARM is not set -CONFIG_ARCH_I386=y -# CONFIG_U4600 is not set -# CONFIG_OMAP1510 is not set -# CONFIG_SA1100 is not set -# CONFIG_PXA is not set -# CONFIG_KZM is not set -# CONFIG_IXP4XX is not set -CONFIG_PC99=y -# CONFIG_GENERIC_PXA25X is not set -# CONFIG_GENERIC_PXA27X is not set -# CONFIG_GUMSTIX is not set -# CONFIG_IBOX is not set -# CONFIG_PLEB2 is not set -CONFIG_NODES_SHIFT=5 -CONFIG_L4_ZONE_SIZE=24 -# CONFIG_GENERIC_CALIBRATE_DELAY is not set -CONFIG_ARCH_SELECT_MEMORY_MODEL=y -CONFIG_ARCH_DISCONTIGMEM_ENABLE=y -CONFIG_UID16=y -CONFIG_SELECT_MEMORY_MODEL=y -# CONFIG_FLATMEM_MANUAL is not set -CONFIG_DISCONTIGMEM_MANUAL=y -# CONFIG_SPARSEMEM_MANUAL is not set -CONFIG_DISCONTIGMEM=y -CONFIG_FLAT_NODE_MEM_MAP=y -CONFIG_NEED_MULTIPLE_NODES=y -# CONFIG_SPARSEMEM_STATIC is not set -CONFIG_SPLIT_PTLOCK_CPUS=4 -# CONFIG_RESOURCES_64BIT is not set -CONFIG_ZONE_DMA_FLAG=0 -CONFIG_VIRT_TO_BUS=y -CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" - -# -# General setup -# -CONFIG_EXPERIMENTAL=y -CONFIG_BROKEN_ON_SMP=y -CONFIG_INIT_ENV_ARG_LIMIT=32 -CONFIG_LOCALVERSION="" -# CONFIG_LOCALVERSION_AUTO is not set -# CONFIG_SWAP is not set -# CONFIG_SYSVIPC is not set -CONFIG_POSIX_MQUEUE=y -CONFIG_BSD_PROCESS_ACCT=y -CONFIG_BSD_PROCESS_ACCT_V3=y -# CONFIG_TASKSTATS is not set -# CONFIG_USER_NS is not set -# CONFIG_AUDIT is not set -# CONFIG_IKCONFIG is not set -CONFIG_LOG_BUF_SHIFT=17 -CONFIG_SYSFS_DEPRECATED=y -# CONFIG_RELAY is not set -CONFIG_BLK_DEV_INITRD=y -CONFIG_INITRAMFS_SOURCE="" -CONFIG_CC_OPTIMIZE_FOR_SIZE=y -CONFIG_SYSCTL=y -CONFIG_EMBEDDED=y -CONFIG_SYSCTL_SYSCALL=y -CONFIG_KALLSYMS=y -# CONFIG_KALLSYMS_EXTRA_PASS is not set -CONFIG_HOTPLUG=y -CONFIG_PRINTK=y -CONFIG_BUG=y -CONFIG_ELF_CORE=y -CONFIG_BASE_FULL=y -CONFIG_FUTEX=y -CONFIG_ANON_INODES=y -CONFIG_EPOLL=y -CONFIG_SIGNALFD=y -CONFIG_EVENTFD=y -# CONFIG_SHMEM is not set -CONFIG_VM_EVENT_COUNTERS=y -CONFIG_SLAB=y -# CONFIG_SLUB is not set -# CONFIG_SLOB is not set -CONFIG_RT_MUTEXES=y -CONFIG_TINY_SHMEM=y -CONFIG_BASE_SMALL=0 -# CONFIG_MODULES is not set -CONFIG_BLOCK=y -# CONFIG_LBD is not set -# CONFIG_BLK_DEV_IO_TRACE is not set -# CONFIG_LSF is not set -# CONFIG_BLK_DEV_BSG is not set - -# -# IO Schedulers -# -CONFIG_IOSCHED_NOOP=y -# CONFIG_IOSCHED_AS is not set -# CONFIG_IOSCHED_DEADLINE is not set -CONFIG_IOSCHED_CFQ=y -# CONFIG_DEFAULT_AS is not set -# CONFIG_DEFAULT_DEADLINE is not set -CONFIG_DEFAULT_CFQ=y -# CONFIG_DEFAULT_NOOP is not set -CONFIG_DEFAULT_IOSCHED="cfq" - -# -# Generic Driver Options -# -CONFIG_STANDALONE=y -CONFIG_PREVENT_FIRMWARE_BUILD=y -# CONFIG_FW_LOADER is not set -# CONFIG_SYS_HYPERVISOR is not set -# CONFIG_BLK_DEV is not set -# CONFIG_IDE is not set - -# -# Networking -# -CONFIG_NET=y - -# -# Networking options -# -CONFIG_PACKET=y -# CONFIG_PACKET_MMAP is not set -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_NET_KEY=y -# CONFIG_NET_KEY_MIGRATE is not set -CONFIG_INET=y -# CONFIG_IP_MULTICAST is not set -CONFIG_IP_ADVANCED_ROUTER=y -CONFIG_ASK_IP_FIB_HASH=y -# CONFIG_IP_FIB_TRIE is not set -CONFIG_IP_FIB_HASH=y -# CONFIG_IP_MULTIPLE_TABLES is not set -# CONFIG_IP_ROUTE_MULTIPATH is not set -# CONFIG_IP_ROUTE_VERBOSE is not set -# CONFIG_IP_PNP is not set -# CONFIG_NET_IPIP is not set -# CONFIG_NET_IPGRE is not set -# CONFIG_ARPD is not set -# CONFIG_SYN_COOKIES is not set -# CONFIG_INET_AH is not set -# CONFIG_INET_ESP is not set -# CONFIG_INET_IPCOMP is not set -# CONFIG_INET_XFRM_TUNNEL is not set -# CONFIG_INET_TUNNEL is not set -# CONFIG_INET_XFRM_MODE_TRANSPORT is not set -# CONFIG_INET_XFRM_MODE_TUNNEL is not set -# CONFIG_INET_XFRM_MODE_BEET is not set -CONFIG_INET_DIAG=y -CONFIG_INET_TCP_DIAG=y -# CONFIG_TCP_CONG_ADVANCED is not set -CONFIG_TCP_CONG_CUBIC=y -CONFIG_DEFAULT_TCP_CONG="cubic" -# CONFIG_TCP_MD5SIG is not set -# CONFIG_IPV6 is not set -# CONFIG_INET6_XFRM_TUNNEL is not set -# CONFIG_INET6_TUNNEL 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_TIPC is not set -# CONFIG_ATM is not set -# CONFIG_BRIDGE 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 - -# -# QoS and/or fair queueing -# -# CONFIG_NET_SCHED is not set - -# -# Network testing -# -# CONFIG_NET_PKTGEN is not set -# CONFIG_HAMRADIO is not set -# CONFIG_IRDA is not set -# CONFIG_BT is not set -# CONFIG_AF_RXRPC is not set - -# -# Wireless -# -# CONFIG_CFG80211 is not set -# CONFIG_WIRELESS_EXT is not set -# CONFIG_MAC80211 is not set -# CONFIG_IEEE80211 is not set -# CONFIG_RFKILL is not set -# CONFIG_NET_9P 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_FS_XATTR is not set -# CONFIG_EXT4DEV_FS is not set -CONFIG_JBD=y -# CONFIG_JBD_DEBUG is not set -# CONFIG_REISERFS_FS is not set -# CONFIG_JFS_FS is not set -# CONFIG_FS_POSIX_ACL is not set -# CONFIG_XFS_FS is not set -# CONFIG_GFS2_FS is not set -# CONFIG_OCFS2_FS is not set -# CONFIG_MINIX_FS is not set -# CONFIG_ROMFS_FS is not set -# CONFIG_INOTIFY is not set -# CONFIG_QUOTA is not set -CONFIG_DNOTIFY=y -# CONFIG_AUTOFS_FS is not set -# CONFIG_AUTOFS4_FS is not set -# CONFIG_FUSE_FS is not set - -# -# CD-ROM/DVD Filesystems -# -# CONFIG_ISO9660_FS is not set -# CONFIG_UDF_FS is not set - -# -# DOS/FAT/NT Filesystems -# -# CONFIG_MSDOS_FS is not set -# CONFIG_VFAT_FS is not set -# CONFIG_NTFS_FS is not set - -# -# Pseudo filesystems -# -CONFIG_PROC_FS=y -CONFIG_PROC_KCORE=y -CONFIG_PROC_SYSCTL=y -CONFIG_SYSFS=y -# CONFIG_TMPFS is not set -# CONFIG_HUGETLB_PAGE is not set -CONFIG_RAMFS=y -# CONFIG_CONFIGFS_FS is not set - -# -# Layered filesystems -# -CONFIG_UNION_FS=y -CONFIG_UNION_FS_XATTR=y -# CONFIG_UNION_FS_DEBUG is not set - -# -# Miscellaneous filesystems -# -# CONFIG_ADFS_FS is not set -# CONFIG_AFFS_FS is not set -# CONFIG_HFS_FS is not set -# CONFIG_HFSPLUS_FS is not set -# CONFIG_BEFS_FS is not set -# CONFIG_BFS_FS is not set -# CONFIG_EFS_FS is not set -# CONFIG_CRAMFS is not set -# CONFIG_VXFS_FS is not set -# CONFIG_HPFS_FS is not set -# CONFIG_QNX4FS_FS is not set -# CONFIG_SYSV_FS is not set -# CONFIG_UFS_FS is not set - -# -# Network File Systems -# -# CONFIG_NFS_FS is not set -# CONFIG_NFSD is not set -# CONFIG_SMB_FS is not set -# CONFIG_CIFS is not set -# CONFIG_NCP_FS is not set -# CONFIG_CODA_FS is not set -# CONFIG_AFS_FS is not set - -# -# Partition Types -# -# CONFIG_PARTITION_ADVANCED is not set -CONFIG_MSDOS_PARTITION=y - -# -# Native Language Support -# -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=y -CONFIG_NLS_CODEPAGE_852=y -# 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=y -# CONFIG_NLS_CODEPAGE_1251 is not set -CONFIG_NLS_ASCII=y -CONFIG_NLS_ISO8859_1=y -CONFIG_NLS_ISO8859_2=y -# 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 - -# -# Distributed Lock Manager -# -# CONFIG_DLM is not set -# CONFIG_OKL4FS is not set -# CONFIG_MMC is not set - -# -# Executable file formats -# -CONFIG_BINFMT_ELF=y -# CONFIG_BINFMT_MISC is not set - -# -# Bus options (PCI) -# -# CONFIG_PCI is not set -# CONFIG_ARCH_SUPPORTS_MSI is not set -# CONFIG_MCA is not set -# CONFIG_PARPORT is not set -# CONFIG_MISC_DEVICES is not set - -# -# SCSI device support -# -# CONFIG_RAID_ATTRS is not set -# CONFIG_SCSI is not set -# CONFIG_SCSI_DMA is not set -# CONFIG_SCSI_NETLINK is not set -# CONFIG_ATA is not set -# CONFIG_MD is not set -# CONFIG_MTD is not set - -# -# Input device support -# -CONFIG_INPUT=y -# CONFIG_INPUT_FF_MEMLESS is not set -# CONFIG_INPUT_POLLDEV is not set - -# -# Userland interfaces -# -CONFIG_INPUT_MOUSEDEV=y -# CONFIG_INPUT_MOUSEDEV_PSAUX is not set -CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 -CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 -# CONFIG_INPUT_JOYDEV is not set -# CONFIG_INPUT_TSDEV is not set -CONFIG_INPUT_EVDEV=y -# CONFIG_INPUT_EVBUG is not set - -# -# Input Device Drivers -# -# CONFIG_INPUT_KEYBOARD is not set -# CONFIG_INPUT_MOUSE is not set -# CONFIG_INPUT_JOYSTICK is not set -# CONFIG_INPUT_TABLET is not set -# CONFIG_INPUT_TOUCHSCREEN is not set -# CONFIG_INPUT_MISC is not set - -# -# Hardware I/O ports -# -# CONFIG_SERIO is not set -# CONFIG_GAMEPORT is not set - -# -# Character devices -# -CONFIG_VT=y -CONFIG_VT_CONSOLE=y -CONFIG_HW_CONSOLE=y -# CONFIG_VT_HW_CONSOLE_BINDING is not set -# CONFIG_SERIAL_NONSTANDARD is not set - -# -# Serial drivers -# -# CONFIG_SERIAL_8250 is not set - -# -# Non-8250 serial port support -# -CONFIG_UNIX98_PTYS=y -# CONFIG_LEGACY_PTYS is not set -# CONFIG_IPMI_HANDLER is not set -# CONFIG_WATCHDOG is not set -# CONFIG_HW_RANDOM is not set -# CONFIG_RTC is not set -# CONFIG_GEN_RTC is not set -# CONFIG_R3964 is not set - -# -# PCMCIA character devices -# -# CONFIG_RAW_DRIVER is not set -# CONFIG_TCG_TPM is not set - -# -# Graphics support -# -# CONFIG_BACKLIGHT_LCD_SUPPORT is not set - -# -# Display device support -# -# CONFIG_DISPLAY_SUPPORT is not set -# CONFIG_VGASTATE is not set -# CONFIG_VIDEO_OUTPUT_CONTROL is not set -CONFIG_FB=y -# CONFIG_FIRMWARE_EDID is not set -# CONFIG_FB_DDC is not set -CONFIG_FB_CFB_FILLRECT=y -CONFIG_FB_CFB_COPYAREA=y -CONFIG_FB_CFB_IMAGEBLIT=y -# CONFIG_FB_SYS_FILLRECT is not set -# CONFIG_FB_SYS_COPYAREA is not set -# CONFIG_FB_SYS_IMAGEBLIT is not set -# CONFIG_FB_SYS_FOPS is not set -CONFIG_FB_DEFERRED_IO=y -# CONFIG_FB_SVGALIB is not set -# CONFIG_FB_MACMODES is not set -# CONFIG_FB_BACKLIGHT is not set -CONFIG_FB_MODE_HELPERS=y -CONFIG_FB_TILEBLITTING=y - -# -# Frame buffer hardware drivers -# -# CONFIG_FB_S1D13XXX is not set -# CONFIG_FB_VIRTUAL is not set - -# -# Console display driver support -# -CONFIG_VGA_CONSOLE=y -# CONFIG_VGACON_SOFT_SCROLLBACK is not set -CONFIG_DUMMY_CONSOLE=y -CONFIG_FRAMEBUFFER_CONSOLE=y -# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set -CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y -# CONFIG_FONTS is not set -CONFIG_FONT_8x8=y -CONFIG_FONT_8x16=y -CONFIG_LOGO=y -CONFIG_LOGO_LINUX_MONO=y -CONFIG_LOGO_LINUX_VGA16=y -CONFIG_LOGO_LINUX_CLUT224=y -# CONFIG_NETDEVICES is not set -# CONFIG_HID_SUPPORT is not set -# CONFIG_HWMON is not set - -# -# Multimedia devices -# -# CONFIG_VIDEO_DEV is not set -# CONFIG_DVB_CORE is not set -# CONFIG_DAB is not set -# CONFIG_USB_SUPPORT is not set -# CONFIG_NEW_LEDS is not set - -# -# Sound -# -CONFIG_SOUND=y - -# -# Advanced Linux Sound Architecture -# -CONFIG_SND=y -CONFIG_SND_TIMER=y -CONFIG_SND_PCM=y -# CONFIG_SND_SEQUENCER is not set -CONFIG_SND_OSSEMUL=y -CONFIG_SND_MIXER_OSS=y -CONFIG_SND_PCM_OSS=y -# CONFIG_SND_PCM_OSS_PLUGINS is not set -# CONFIG_SND_DYNAMIC_MINORS is not set -CONFIG_SND_SUPPORT_OLD_API=y -CONFIG_SND_VERBOSE_PROCFS=y -# CONFIG_SND_VERBOSE_PRINTK is not set -# CONFIG_SND_DEBUG is not set - -# -# Generic devices -# -# CONFIG_SND_DUMMY is not set -# CONFIG_SND_MTPAV is not set -# CONFIG_SND_SERIAL_U16550 is not set -# CONFIG_SND_MPU401 is not set - -# -# System on Chip audio support -# -# CONFIG_SND_SOC is not set - -# -# SoC Audio support for SuperH -# - -# -# Open Sound System -# -# CONFIG_SOUND_PRIME is not set -# CONFIG_I2C is not set - -# -# Security options -# -# CONFIG_KEYS is not set -# CONFIG_SECURITY is not set -# CONFIG_CRYPTO is not set - -# -# Library routines -# -CONFIG_BITREVERSE=y -# CONFIG_CRC_CCITT is not set -# CONFIG_CRC16 is not set -# CONFIG_CRC_ITU_T is not set -CONFIG_CRC32=y -# CONFIG_CRC7 is not set -# CONFIG_LIBCRC32C is not set -CONFIG_PLIST=y -CONFIG_HAS_IOMEM=y -CONFIG_HAS_IOPORT=y -CONFIG_HAS_DMA=y - -# -# Kernel hacking -# -# CONFIG_PRINTK_TIME is not set -# CONFIG_ENABLE_MUST_CHECK is not set -# CONFIG_MAGIC_SYSRQ 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_EARLY_PRINTK is not set diff --git a/ports-okl4/include/oklx_kernel/oklx/ioctl.h b/ports-okl4/include/oklx_kernel/oklx/ioctl.h deleted file mode 100644 index e2ec93286..000000000 --- a/ports-okl4/include/oklx_kernel/oklx/ioctl.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * \brief Oklx's framebuffer driver for Genode ioctl interface - * \author Stefan Kalkowski - * \date 2010-04-23 - */ - -/* - * Copyright (C) 2010-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#ifndef _INCLUDE__OKLX_KERNEL__OKLX__IOCTL_H_ -#define _INCLUDE__OKLX_KERNEL__OKLX__IOCTL_H_ - -struct genode_screen_region -{ - int x; - int y; - int w; - int h; -}; - -struct genode_view_place -{ - int view; - struct genode_screen_region reg; -}; - -struct genode_view_stack -{ - int view; - int neighbor; - int behind; -}; - -enum Ioctl_nums { - NITPICKER_IOCTL_CREATE_VIEW = _IOW('F', 11, int), - NITPICKER_IOCTL_DESTROY_VIEW = _IOW('F', 12, int), - NITPICKER_IOCTL_BACK_VIEW = _IOW('F', 13, int), - NITPICKER_IOCTL_PLACE_VIEW = _IOW('F', 14, struct genode_view_place), - NITPICKER_IOCTL_STACK_VIEW = _IOW('F', 15, struct genode_view_stack), - FRAMEBUFFER_IOCTL_REFRESH = _IOW('F', 16, struct genode_screen_region) -}; - -#endif //_INCLUDE__OKLX_KERNEL__OKLX__IOCTL_H_ diff --git a/ports-okl4/include/oklx_lib/genode/audio.h b/ports-okl4/include/oklx_lib/genode/audio.h deleted file mode 100644 index ede347b5c..000000000 --- a/ports-okl4/include/oklx_lib/genode/audio.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * \brief Genode C API audio functions needed by OKLinux - * \author Stefan Kalkowski - * \date 2009-11-09 - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#ifndef _INCLUDE__OKLINUX_SUPPORT__GENODE__AUDIO_H_ -#define _INCLUDE__OKLINUX_SUPPORT__GENODE__AUDIO_H_ - -int genode_audio_ready(void); - -void genode_audio_collect_acks(void); - -void genode_audio_trigger_start(void (*func)(unsigned long), unsigned long data); - -void genode_audio_trigger_stop(void); - -void genode_audio_prepare(void); - -unsigned long genode_audio_position(void); - -void genode_audio_write(void* src, unsigned long sz); - -void genode_audio_fill_silence(unsigned long sz); - -unsigned int genode_audio_packet_size(void); - -unsigned int genode_audio_packet_count(void); - -#endif //_INCLUDE__OKLINUX_SUPPORT__GENODE__AUDIO_H_ diff --git a/ports-okl4/include/oklx_lib/genode/block.h b/ports-okl4/include/oklx_lib/genode/block.h deleted file mode 100644 index fc738f0a3..000000000 --- a/ports-okl4/include/oklx_lib/genode/block.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * \brief Genode C API block driver related functions needed by OKLinux - * \author Stefan Kalkowski - * \date 2010-07-08 - */ - -/* - * Copyright (C) 2010-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#ifndef _INCLUDE__OKLINUX_SUPPORT__GENODE__BLOCK_H_ -#define _INCLUDE__OKLINUX_SUPPORT__GENODE__BLOCK_H_ - -void genode_block_register_callback(void (*func)(void*, short, - void*, unsigned long)); - -void genode_block_geometry(unsigned long *blk_cnt, unsigned long *blk_sz, - int *writeable, unsigned long *req_queue_sz); - -void* genode_block_request(unsigned long sz, void *req, unsigned long *offset); - -void genode_block_submit(unsigned long queue_offset, unsigned long size, - unsigned long disc_offset, int write); - -void genode_block_collect_responses(void); - -#endif //_INCLUDE__OKLINUX_SUPPORT__GENODE__BLOCK_H_ diff --git a/ports-okl4/include/oklx_lib/genode/config.h b/ports-okl4/include/oklx_lib/genode/config.h deleted file mode 100644 index 012480048..000000000 --- a/ports-okl4/include/oklx_lib/genode/config.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * \brief Genode C API configuration related functions needed by OKLinux - * \author Stefan Kalkowski - * \date 2009-07-08 - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#ifndef _INCLUDE__OKLINUX_SUPPORT__GENODE__CONFIG_H_ -#define _INCLUDE__OKLINUX_SUPPORT__GENODE__CONFIG_H_ - -/** - * Get the kernel command line - */ -char* genode_config_cmdline(void); - -/** - * Get name of initrd file - */ -char* genode_config_initrd(void); - -/** - * Returns whether audio driver should be used, or not. - */ -int genode_config_audio(void); - -/** - * Returns whether nic driver should be used, or not. - */ -int genode_config_nic(void); - -/** - * Returns whether block driver should be used, or not. - */ -int genode_config_block(void); - -#endif //_INCLUDE__OKLINUX_SUPPORT__GENODE__CONFIG_H_ diff --git a/ports-okl4/include/oklx_lib/genode/exit.h b/ports-okl4/include/oklx_lib/genode/exit.h deleted file mode 100644 index cd9e613cc..000000000 --- a/ports-okl4/include/oklx_lib/genode/exit.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * \brief Genode C API exit function needed by OKLinux - * \author Stefan Kalkowski - * \date 2009-08-14 - */ - -/* - * Copyright (C) 2009 - * Genode Labs, Feske & Helmuth Systementwicklung GbR - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#ifndef _INCLUDE__OKLINUX_SUPPORT__GENODE__EXIT_H_ -#define _INCLUDE__OKLINUX_SUPPORT__GENODE__EXIT_H_ - -/** - * Tell parent to exit the program - */ -void genode_exit(int value); - -#endif //_INCLUDE__OKLINUX_SUPPORT__GENODE__EXIT_H_ diff --git a/ports-okl4/include/oklx_lib/genode/framebuffer.h b/ports-okl4/include/oklx_lib/genode/framebuffer.h deleted file mode 100644 index fec22ed6c..000000000 --- a/ports-okl4/include/oklx_lib/genode/framebuffer.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * \brief Genode C API framebuffer/input functions needed by OKLinux - * \author Stefan Kalkowski - * \date 2009-06-08 - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#ifndef _INCLUDE__OKLX_LIB__GENODE__FRAMEBUFFER_H_ -#define _INCLUDE__OKLX_LIB__GENODE__FRAMEBUFFER_H_ - -/** - * Returns the count of screens (framebuffer + input devices) - * given by config-file information - */ -int genode_screen_count(void); - -/** - * Get the size of the framebuffer - * - * \param screen the index of the framebuffer - * \return the size - */ -unsigned long genode_fb_size(unsigned int screen); - -/** - * Attach the framebuffer to our address space - * - * \param screen the index of the framebuffer - * \return the virtual address the framebuffer was attached to - */ -void *genode_fb_attach(unsigned int screen); - -/** - * Get information about the resolution of the framebuffer - * - * \param screen the index of the framebuffer - * \param out_w resulting pointer to the horizontal resolution info - * \param out_h resulting pointer to the vertical resolution info - */ -void genode_fb_info(unsigned int screen, int *out_w, int *out_h); - -/** - * Refresh a specified area of the framebuffer - * - * \param screen the index of the framebuffer - * \param x x-coordinate of the area's position - * \param y y-coordinate of the area's position - * \param w width of the area - * \param h height of the area - */ -void genode_fb_refresh(unsigned int screen, int x, int y, int w, int h); - -/** - * Close the framebuffer session - * - * \param screen the index of the framebuffer - */ -void genode_fb_close(unsigned screen); - -int genode_nit_view_create(unsigned screen, unsigned view); - -void genode_nit_view_destroy(unsigned screen, unsigned view); - -void genode_nit_view_back(unsigned screen, unsigned view); - -void genode_nit_view_place(unsigned screen, unsigned view, int x, - int y, int w, int h); - -void genode_nit_view_stack(unsigned int screen, unsigned view, - unsigned neighbor, int behind); - -/** - * Close all views of a nitpicker session - * - * \param screen the index of the framebuffer - */ -void genode_nit_close_all_views(unsigned screen); - -#endif //_INCLUDE__OKLX_LIB__GENODE__FRAMEBUFFER_H_ diff --git a/ports-okl4/include/oklx_lib/genode/input.h b/ports-okl4/include/oklx_lib/genode/input.h deleted file mode 100644 index 3617f0ae2..000000000 --- a/ports-okl4/include/oklx_lib/genode/input.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * \brief Genode C API input functions - * \author Stefan Kalkowski - * \date 2010-04-22 - */ - -/* - * Copyright (C) 2010-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#ifndef _INCLUDE__OKLX_LIB__GENODE__INPUT_H_ -#define _INCLUDE__OKLX_LIB__GENODE__INPUT_H_ - -void genode_input_register_callback(void (*genode_input_event) - (void*,unsigned int, unsigned int, int)); - -void genode_input_unregister_callback(void); - -void genode_input_register_keyb(unsigned int idx, void* dev); - -void genode_input_register_mouse(unsigned int idx, void* dev); - -void genode_input_unregister_keyb(unsigned int idx); - -void genode_input_unregister_mouse(unsigned int idx); - -void genode_input_handle_events(void); - -#endif //_INCLUDE__OKLX_LIB__GENODE__INPUT_H_ diff --git a/ports-okl4/include/oklx_lib/genode/lock.h b/ports-okl4/include/oklx_lib/genode/lock.h deleted file mode 100644 index d4a38e158..000000000 --- a/ports-okl4/include/oklx_lib/genode/lock.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * \brief Genode C API lock functions needed by OKLinux - * \author Stefan Kalkowski - * \date 2009-08-17 - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#ifndef _INCLUDE__OKLINUX_SUPPORT__GENODE__LOCK_H_ -#define _INCLUDE__OKLINUX_SUPPORT__GENODE__LOCK_H_ - -/** - * Allocate a lock - * - * \return virtual address of the lock - */ -void *genode_alloc_lock(void); - -/** - * Free a lock - * - * \param lock_addr virtual address of the lock - */ -void genode_free_lock(void* lock_addr); - -/** - * Lock the lock - * - * \param lock_addr virtual address of the lock - */ -void genode_lock(void* lock_addr); - -/** - * Unlock the lock - * - * \param lock_addr virtual address of the lock - */ -void genode_unlock(void* lock_addr); - -#endif //_INCLUDE__OKLINUX_SUPPORT__GENODE__LOCK_H_ diff --git a/ports-okl4/include/oklx_lib/genode/memory.h b/ports-okl4/include/oklx_lib/genode/memory.h deleted file mode 100644 index f17f402c6..000000000 --- a/ports-okl4/include/oklx_lib/genode/memory.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * \brief Genode C API memory functions needed by OKLinux - * \author Stefan Kalkowski - * \date 2009-05-19 - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#ifndef _INCLUDE__OKLINUX_SUPPORT__GENODE__MEMORY_H_ -#define _INCLUDE__OKLINUX_SUPPORT__GENODE__MEMORY_H_ - -/** - * Allocate memory from the heap - * - * \param sz amount of memory - * \return virtual address of the allocated memory area - */ -void *genode_malloc(unsigned long sz); - -/** - * Set the invoking thread as the pager of newly started Linux user processes - */ -void genode_set_pager(void); - -/** - * Get the absolute maximum of RAM Linux is allowed to consume - */ -unsigned long genode_quota(void); - -/** - * Get the actual used portion of RAM - */ -unsigned long genode_used_mem(void); - -#endif //_INCLUDE__OKLINUX_SUPPORT__GENODE__MEMORY_H_ diff --git a/ports-okl4/include/oklx_lib/genode/net.h b/ports-okl4/include/oklx_lib/genode/net.h deleted file mode 100644 index cc4e7bf2e..000000000 --- a/ports-okl4/include/oklx_lib/genode/net.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * \brief Genode C API network functions needed by OKLinux - * \author Stefan Kalkowski - * \date 2009-09-10 - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#ifndef _INCLUDE__OKLX_LIB__GENODE__NET_H_ -#define _INCLUDE__OKLX_LIB__GENODE__NET_H_ - -int genode_net_ready (void); -void genode_net_start (void *dev, void (*func)(void*, void*, unsigned long)); -void genode_net_stop (void); -void genode_net_mac (void* mac_addr, unsigned long size); -int genode_net_tx (void* addr, unsigned long len, void *skb); -int genode_net_tx_ack_avail(void); -void* genode_net_tx_ack (void); -void genode_net_rx_receive (void); - -#endif /* _INCLUDE__OKLX_LIB__GENODE__NET_H_ */ diff --git a/ports-okl4/include/oklx_lib/genode/open.h b/ports-okl4/include/oklx_lib/genode/open.h deleted file mode 100644 index f5b9c4e4b..000000000 --- a/ports-okl4/include/oklx_lib/genode/open.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * \brief Genode C API file functions needed by OKLinux - * \author Stefan Kalkowski - * \date 2009-05-19 - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#ifndef _INCLUDE__OKLINUX_SUPPORT__GENODE__OPEN_H_ -#define _INCLUDE__OKLINUX_SUPPORT__GENODE__OPEN_H_ - -/** - * Opens a file and attach it (read-only) to the address-space - * - * \param name name of the file - * \param sz resulting pointer to the size of the opened file - * \return virtual address of the attached file - */ -void *genode_open(const char *name, unsigned long *sz); - -#endif //_INCLUDE__OKLINUX_SUPPORT__GENODE__OPEN_H_ diff --git a/ports-okl4/include/oklx_lib/genode/printf.h b/ports-okl4/include/oklx_lib/genode/printf.h deleted file mode 100644 index d33edc8bb..000000000 --- a/ports-okl4/include/oklx_lib/genode/printf.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * \brief Genode C API print functions needed by OKLinux - * \author Stefan Kalkowski - * \date 2009-05-19 - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#ifndef _INCLUDE__OKLINUX_SUPPORT__GENODE__PRINT_H_ -#define _INCLUDE__OKLINUX_SUPPORT__GENODE__PRINT_H_ - -/** - * Prints a message via Genode's log session mechanism - */ -void genode_printf(const char *format, ...); - -#endif //_INCLUDE__OKLINUX_SUPPORT__GENODE__PRINT_H_ diff --git a/ports-okl4/include/oklx_lib/genode/sleep.h b/ports-okl4/include/oklx_lib/genode/sleep.h deleted file mode 100644 index 918b43ccb..000000000 --- a/ports-okl4/include/oklx_lib/genode/sleep.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * \brief Genode C API sleep functions needed by OKLinux - * \author Stefan Kalkowski - * \date 2009-05-19 - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#ifndef _INCLUDE__OKLINUX_SUPPORT__GENODE__SLEEP_H_ -#define _INCLUDE__OKLINUX_SUPPORT__GENODE__SLEEP_H_ - -/** - * Sleep for a given period of time - * - * \param ms milliseconds to sleep - */ -void genode_sleep(unsigned ms); - -/** - * Sleep forever, never wake up again - */ -void genode_sleep_forever(void); - -#endif //_INCLUDE__OKLINUX_SUPPORT__GENODE__SLEEP_H_ diff --git a/ports-okl4/include/oklx_lib/iguana/eas.h b/ports-okl4/include/oklx_lib/iguana/eas.h deleted file mode 100644 index 08aa93ed6..000000000 --- a/ports-okl4/include/oklx_lib/iguana/eas.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * \brief Iguana API functions needed by OKLinux - * \author Norman Feske - * \date 2009-04-12 - * - * This file contains function definitions to create/destroy address spaces, - * threads within other address spaces and to populate the address spaces - * with pages. - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#ifndef _INCLUDE__OKLINUX_SUPPORT__IGUANA__EAS_H_ -#define _INCLUDE__OKLINUX_SUPPORT__IGUANA__EAS_H_ - -#include - -typedef uintptr_t eas_ref_t; /* Address space id corresponds to OKL4 space id*/ - -/** - * Create a thread within another address space - * - * \param eas identifier of the target address space - * \param pager thread id of the new thread's pager - * \param scheduler thread id of the new thread's scheduler - * \param utcb address of utcb of the new thread - * \param handle_rv resulting pointer to the id of the new thread - * \return id of the new thread - */ -L4_ThreadId_t eas_create_thread(eas_ref_t eas, L4_ThreadId_t pager, - L4_ThreadId_t scheduler, void *utcb, - L4_ThreadId_t *handle_rv); - -/** - * Create a new address space - * - * \param utcb defines utcb region of the address space - * \param l4_id resulting pointer to the id of the new address space - * \return id of the new address space - */ -eas_ref_t eas_create(L4_Fpage_t utcb, L4_SpaceId_t *l4_id); - -/** - * Deletes an address space - * - * \param eas identifier of the target address space - */ -void eas_delete(eas_ref_t eas); - -/** - * Map a page area to an address space - * - * \param eas identifier of the target address space - * \param src_fpage flexpage describing the area to be mapped - * \param dst_addr destination address where the area will be mapped to - * \param attributes describes the caching policy (OKL4 manual) - */ -int eas_map(eas_ref_t eas, L4_Fpage_t src_fpage, - uintptr_t dst_addr, uintptr_t attributes); - -#endif //_INCLUDE__OKLINUX_SUPPORT__IGUANA__EAS_H_ diff --git a/ports-okl4/include/oklx_lib/iguana/hardware.h b/ports-okl4/include/oklx_lib/iguana/hardware.h deleted file mode 100644 index 5b1bde629..000000000 --- a/ports-okl4/include/oklx_lib/iguana/hardware.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * \brief Iguana API functions needed by OKLinux - * \author Norman Feske - * \date 2009-04-12 - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#ifndef _INCLUDE__OKLINUX_SUPPORT__IGUANA__HARDWARE_H_ -#define _INCLUDE__OKLINUX_SUPPORT__IGUANA__HARDWARE_H_ - -#include - -/** - * Register thread as interrupt handler - * - * \param thrd thread id of the interrupt handler - * \param interrupt interrupt number - * \return 0 if no error occured - */ -int hardware_register_interrupt(L4_ThreadId_t thrd, int interrupt); - -/** - * Register physical memory region - * - * \param memsection describes virtual memory area - * \param paddr physical address backing the memory area - * \param attributes caching policy - * \return 0 if no error occured - */ -int hardware_back_memsection(memsection_ref_t memsection, - uintptr_t paddr, uintptr_t attributes); - -#endif //_INCLUDE__OKLINUX_SUPPORT__IGUANA__HARDWARE_H_ diff --git a/ports-okl4/include/oklx_lib/iguana/memsection.h b/ports-okl4/include/oklx_lib/iguana/memsection.h deleted file mode 100644 index ba295bc15..000000000 --- a/ports-okl4/include/oklx_lib/iguana/memsection.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * \brief Iguana API functions needed by OKLinux - * \author Norman Feske - * \date 2009-04-12 - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#ifndef _INCLUDE__OKLINUX_SUPPORT__IGUANA__MEMSECTION_H_ -#define _INCLUDE__OKLINUX_SUPPORT__IGUANA__MEMSECTION_H_ - -#include - -/** - * Get physical address of the given virtual one - * - * \param vaddr the virtual address - * \param size resulting pointer to the size of the corresponding area - * \return the requested physical address - */ -uintptr_t memsection_virt_to_phys(uintptr_t vaddr, size_t *size); - -/** - * Establish mapping to virtual memory area - * - * \param memsect the target memory area - * \param from_page flexpage describing the source memory area - * \param to_page flexpage describing the virtual memory area - * \return 0 if no error occurred - */ -int memsection_page_map(memsection_ref_t memsect, - L4_Fpage_t from_page, - L4_Fpage_t to_page); - -/** - * Destroy mapping to virtual memory area - * - * \param memsect the target memory area - * \param to_page flexpage describing the virtual memory area - * \return 0 if no error occurred - */ -int memsection_page_unmap(memsection_ref_t memsect, - L4_Fpage_t to_page); - -/** - * Register pager of a memory area - * - * \param memsect the target memory area - * \param server the pager's thread id - * \return 0 if no error occurred - */ -int memsection_register_server(memsection_ref_t memsect, - thread_ref_t server); - -/** - * Lookup pager of an object - * - * \param object reference to object - * \param server resulting pointer to object's pager - * \return memory area id, object is part of - */ -memsection_ref_t memsection_lookup(objref_t object, thread_ref_t *server); - -/** - * Get base address of a memory area - * - * \param memsect memory area id - * \return base address of the memory area - */ -void *memsection_base(memsection_ref_t memsect); - -/** - * Get size of a memory area - * - * \param memsect memory area id - * \return size of the memory area - */ -uintptr_t memsection_size(memsection_ref_t memsect); - -/** - * Create a blank memory area - * - * \param size size of the area - * \param base base address of the area - * \return memory area id - */ -memsection_ref_t memsection_create_user(uintptr_t size, uintptr_t *base); - -/** - * Delete a memory area - * - * \param memsect memory area id - */ -void memsection_delete(memsection_ref_t); - -/** - * Create a blank memory area - * - * \param size size of the area - * \param base base address of the area - * \param memsect memory area id - */ -memsection_ref_t memsection_create(uintptr_t size, uintptr_t *base); - -#endif //_INCLUDE__OKLINUX_SUPPORT__IGUANA__MEMSECTION_H_ diff --git a/ports-okl4/include/oklx_lib/iguana/pd.h b/ports-okl4/include/oklx_lib/iguana/pd.h deleted file mode 100644 index c302bde9c..000000000 --- a/ports-okl4/include/oklx_lib/iguana/pd.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * \brief Iguana API functions needed by OKLinux - * \author Norman Feske - * \date 2009-04-12 - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#ifndef _INCLUDE__OKLINUX_SUPPORT__IGUANA__PD_H_ -#define _INCLUDE__OKLINUX_SUPPORT__IGUANA__PD_H_ - -#include - -typedef int pd_ref_t; /* protection domain id */ - -/** - * Get my protection domain - * - * \return protection domain id - */ -pd_ref_t pd_myself(void); - - -/** - * Delete protection domain - * - * \param pd protection domain id - */ -void pd_delete(pd_ref_t pd); - -/** - * Create memory area for protection domain - * - * \param pd protection domain id - * \param size size of new memory area - * \param base base address of new memory area - * \return memory area id - */ -memsection_ref_t pd_create_memsection(pd_ref_t pd, uintptr_t size, - uintptr_t *base); - -#endif //_INCLUDE__OKLINUX_SUPPORT__IGUANA__PD_H_ diff --git a/ports-okl4/include/oklx_lib/iguana/stdint.h b/ports-okl4/include/oklx_lib/iguana/stdint.h deleted file mode 100644 index 0b0c81af9..000000000 --- a/ports-okl4/include/oklx_lib/iguana/stdint.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * \brief Iguana API functions needed by OKLinux - * \author Norman Feske - * \date 2009-04-12 - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#ifndef _INCLUDE__OKLINUX_SUPPORT__IGUANA__STDINT_H_ -#define _INCLUDE__OKLINUX_SUPPORT__IGUANA__STDINT_H_ - -typedef long intptr_t; -typedef unsigned long uintptr_t; - -#endif //_INCLUDE__OKLINUX_SUPPORT__IGUANA__STDINT_H_ diff --git a/ports-okl4/include/oklx_lib/iguana/thread.h b/ports-okl4/include/oklx_lib/iguana/thread.h deleted file mode 100644 index 809e793ec..000000000 --- a/ports-okl4/include/oklx_lib/iguana/thread.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * \brief Iguana API functions needed by OKLinux - * \author Norman Feske - * \date 2009-04-12 - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#ifndef _INCLUDE__OKLINUX_SUPPORT__IGUANA__THREAD_H_ -#define _INCLUDE__OKLINUX_SUPPORT__IGUANA__THREAD_H_ - -#include - -/** - * Initializes the Iguana library - * - * \param obj_env resulting pointer to the iguana environment object - */ -void __lib_iguana_init(uintptr_t *obj_env); - -/** - * Create a new thread in this address space - * - * \param thrd resulting pointer to the thread id of the new thread - * \return Iguana thread id of the new thread - */ -thread_ref_t thread_create(L4_ThreadId_t *thrd); - -/** - * Get OKL4 thread id from the Iguana thread id - * - * \param server Iguana thread id - * \return OKL4 thread id requested - */ -L4_ThreadId_t thread_l4tid(thread_ref_t server); - -/** - * Get the Iguana thread id of the active thread - * - * \return Iguana thread id - */ -thread_ref_t thread_myself(void); - -/** - * Delete a thread within the same address space - * - * \param thrd iguana thread id of the thread to be deleted - */ -void thread_delete(L4_ThreadId_t thrd); - -#endif //_INCLUDE__OKLINUX_SUPPORT__IGUANA__THREAD_H_ diff --git a/ports-okl4/include/oklx_lib/iguana/tls.h b/ports-okl4/include/oklx_lib/iguana/tls.h deleted file mode 100644 index a8e1640da..000000000 --- a/ports-okl4/include/oklx_lib/iguana/tls.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * \brief Iguana API functions needed by OKLinux - * \author Norman Feske - * \date 2009-04-12 - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#ifndef _INCLUDE__OKLINUX_SUPPORT__IGUANA__TLS_H_ -#define _INCLUDE__OKLINUX_SUPPORT__IGUANA__TLS_H_ - -/** - * Initialize thread local storage for the actual thread - * - * \param tls_buffer buffer for the actual thread - */ -void __tls_init(void *tls_buffer); - -#endif //_INCLUDE__OKLINUX_SUPPORT__IGUANA__TLS_H_ diff --git a/ports-okl4/include/oklx_lib/iguana/types.h b/ports-okl4/include/oklx_lib/iguana/types.h deleted file mode 100644 index 859086360..000000000 --- a/ports-okl4/include/oklx_lib/iguana/types.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * \brief Iguana API functions needed by OKLinux - * \author Norman Feske - * \date 2009-04-12 - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#ifndef _INCLUDE__OKLINUX_SUPPORT__IGUANA__TYPES_H_ -#define _INCLUDE__OKLINUX_SUPPORT__IGUANA__TYPES_H_ - -#include -#include - -typedef uintptr_t memsection_ref_t; /* Iguana memory area id */ -typedef uintptr_t cap_t; /* Iguana capability */ -typedef L4_Word_t thread_ref_t; /* Iguana thread id corresponds to OKL4 id */ -typedef uintptr_t objref_t; /* Iguana object reference */ - -#endif //_INCLUDE__OKLINUX_SUPPORT__IGUANA__TYPES_H_ diff --git a/ports-okl4/lib/mk/oklx.mk b/ports-okl4/lib/mk/oklx.mk deleted file mode 100644 index c7defb131..000000000 --- a/ports-okl4/lib/mk/oklx.mk +++ /dev/null @@ -1,31 +0,0 @@ -# -# OKLinux support library -# -SRC_CC = genode_block.cc \ - genode_config.cc \ - genode_exit.cc \ - genode_framebuffer.cc \ - genode_input.cc \ - genode_lock.cc \ - genode_memory.cc \ - genode_net.cc \ - genode_threads.cc \ - genode_open.cc \ - genode_printf.cc \ - genode_sleep.cc -SRC_CC += iguana_eas.cc \ - iguana_hardware.cc \ - iguana_memsection.cc \ - iguana_pd.cc \ - iguana_thread.cc \ - iguana_tls.cc -INC_DIR += $(REP_DIR)/include/oklx_lib -INC_DIR += $(REP_DIR)/src/lib/oklx/include -LIBS = base config - -# do not produce position-independent code -CC_OPT_PIC = - -vpath %.cc $(REP_DIR)/src/lib/oklx/genode -vpath %.cc $(REP_DIR)/src/lib/oklx/iguana - diff --git a/ports-okl4/patches/oklx_genode.patch b/ports-okl4/patches/oklx_genode.patch deleted file mode 100644 index 80ebba4b4..000000000 --- a/ports-okl4/patches/oklx_genode.patch +++ /dev/null @@ -1,3079 +0,0 @@ -diff -urNpB kernel-2.6.23-v2/Kbuild contrib/Kbuild ---- kernel-2.6.23-v2/Kbuild -+++ contrib/Kbuild -@@ -57,4 +57,5 @@ quiet_cmd_syscalls = CALL $< - - PHONY += missing-syscalls - missing-syscalls: scripts/checksyscalls.sh FORCE -- $(call cmd,syscalls) -+ @echo Ignoring $@ -+# $(call cmd,syscalls) -diff -urNpB kernel-2.6.23-v2/arch/l4/Kconfig contrib/arch/l4/Kconfig ---- kernel-2.6.23-v2/arch/l4/Kconfig -+++ contrib/arch/l4/Kconfig -@@ -1,3 +1,5 @@ -+mainmenu "OKLinux Kernel Configuration" -+ - config L4 - bool - default y -@@ -23,7 +25,7 @@ config RWSEM_GENERIC_SPINLOCK - bool - default y - --mainmenu "L4/Linux Kernel Configuration" -+#mainmenu "L4/Linux Kernel Configuration" - - menu "L4/Linux-specific options" - -@@ -33,19 +35,19 @@ config L4KDB_CONSOLE - - config IG_SERIAL - bool "Compile Iguana virtual serial driver" -- default y -+ default n - - config IG_INPUT - bool "Compile Iguana virtual input driver" -- default y -+ default n - - config IG_MMC - bool "Compile Iguana virtual mmc/sd driver" -- default y -+ default n - - config IG_NET - bool "Compile Iguana virtual net driver" -- default y -+ default n - - config MTD_IGMTD - tristate "Iguana virtual MTD device" -@@ -69,13 +71,30 @@ config IG_AUDIO - config IG_TOUCH - bool "Compile Iguana virtual touch screen driver" - depends on INPUT && INPUT_TOUCHSCREEN -- default y -+ default n - - config FB_IGVIRTUAL - tristate "Iguana Virtual Frame buffer support (ONLY FOR TESTING!)" - depends on FB - default n - -+config SCREEN_GENODE -+ bool -+ select FB_CFB_FILLRECT -+ select FB_CFB_COPYAREA -+ select FB_CFB_IMAGEBLIT -+ default y -+ -+config BLOCK_GENODE -+ bool -+ select BLOCK -+ default y -+ -+config NET_GENODE -+ bool -+ select NET -+ default y -+ - endmenu - - config GENERIC_HWEIGHT -@@ -212,12 +231,12 @@ bool "Support for PLEB2 platform" - endchoice - - config NODES_SHIFT -- int -- default 4 -+ int "NODES_SHIFT" -+ default 5 - # default "7" if MPENTIUM4 || X86_GENERIC - - config L4_ZONE_SIZE -- int -+ int "L4_ZONE_SIZE" - default 22 - # default "7" if MPENTIUM4 || X86_GENERIC - -@@ -273,7 +292,7 @@ config CPU_HAS_LLDSCD - - config GENERIC_CALIBRATE_DELAY - bool -- default y -+ default n - - config ISA - bool -@@ -311,7 +330,7 @@ source "net/Kconfig" - source "fs/Kconfig" - config OKL4FS - bool -- default m -+ default n - - source "drivers/mmc/Kconfig" - -@@ -360,17 +379,17 @@ source "drivers/mca/Kconfig" - - endmenu - --source "drivers/scsi/Kconfig" -+source "drivers/parport/Kconfig" - --source "drivers/ata/Kconfig" -+source "drivers/pnp/Kconfig" - --#source "security/Kconfig" -+source "drivers/misc/Kconfig" - --#source "crypto/Kconfig" -+source "drivers/scsi/Kconfig" - --source "lib/Kconfig" -+source "drivers/ata/Kconfig" - --#source "drivers/md/Kconfig" -+source "drivers/md/Kconfig" - - source "drivers/mtd/Kconfig" - -@@ -384,12 +403,24 @@ source "drivers/net/Kconfig" - - source "drivers/hid/Kconfig" - -+source "drivers/hwmon/Kconfig" -+ -+source "drivers/media/Kconfig" -+ - source "drivers/usb/Kconfig" - -+source "drivers/leds/Kconfig" -+ - source "sound/Kconfig" - - source "drivers/i2c/Kconfig" - -+source "security/Kconfig" -+ -+source "crypto/Kconfig" -+ -+source "lib/Kconfig" -+ - menu "Kernel hacking" - - source "lib/Kconfig.debug" -@@ -397,7 +428,7 @@ source "lib/Kconfig.debug" - config EARLY_PRINTK - bool "Early printk" if EMBEDDED - depends on L4KDB_CONSOLE -- default n -+ default y - help - Write kernel log output directly to L4 KDB. - -diff -urNpB kernel-2.6.23-v2/arch/l4/Makefile contrib/arch/l4/Makefile ---- kernel-2.6.23-v2/arch/l4/Makefile -+++ contrib/arch/l4/Makefile -@@ -105,12 +105,21 @@ CFLAGS += -DCONFIG_HYBRID_MUTEXES - core-y += $(ARCH_DIR)/kernel/ \ - $(ARCH_DIR)/mm/ \ - $(ARCH_DIR)/lib/ \ -- $(ARCH_DIR)/sys-$(SYSTEM)/ \ -- $(ARCH_DIR)/kernel/fs/okl4fs/ -+ $(ARCH_DIR)/sys-$(SYSTEM)/ -+ -+# $(ARCH_DIR)/kernel/fs/okl4fs - - drivers-y += $(ARCH_DIR)/drivers/ - --libs-y += -lvtimer -lvserial -ll4e -lll -liguana -ll4 -lgcc -lmutex -lcircular_buffer -lc -latomic_ops -lfs -+#libs-y += -lvtimer -lvserial -ll4e -lll -liguana -ll4 -lgcc -lmutex -lcircular_buffer -lc -latomic_ops -lfs -+ -+GENODE_LIBS = base base-common cxx startup config \ -+ oklx -+ -+libs-y += $(addprefix $(GENODE_LIBS_DIR)/,$(foreach l,$(GENODE_LIBS),$l/$l.lib.a)) -+ -+# link libgcc -+libs-y += $(shell $(CC) $(WOMBAT_CFLAGS) -print-libgcc-file-name) - - #libs-$(CONFIG_ARCH_PXA) += -lpxa - -@@ -128,7 +137,7 @@ AFLAGS += -D__arch_l4__ -D__SYSTEM__=$(S - - CFLAGS += -Iarch/l4/include -Iinclude/asm-l4 - CFLAGS += -I../include --CFLAGS += -I../../../tools/magpie/include -+#CFLAGS += -I../../../tools/magpie/include - CFLAGS += $(WOMBAT_CFLAGS) - - AFLAGS += -Iarch/l4/include -diff -urNpB kernel-2.6.23-v2/arch/l4/drivers/Makefile contrib/arch/l4/drivers/Makefile ---- kernel-2.6.23-v2/arch/l4/drivers/Makefile -+++ contrib/arch/l4/drivers/Makefile -@@ -1,4 +1,6 @@ --obj-y := ig_ramdisk.o -+obj-$(CONFIG_SCREEN_GENODE) += genode_fb.o -+obj-$(CONFIG_BLOCK_GENODE) += genode_block.o -+obj-$(CONFIG_NET_GENODE) += genode_net.o - obj-$(CONFIG_FB_IGVIRTUAL) += ig_fb.o cfbcopyarea.o cfbfillrect.o cfbimgblt.o - obj-$(CONFIG_L4KDB_CONSOLE) += l4kdb_console.o - obj-$(CONFIG_IG_SERIAL) += ig_serial.o -diff -urNpB kernel-2.6.23-v2/arch/l4/drivers/genode_block.c contrib/arch/l4/drivers/genode_block.c ---- kernel-2.6.23-v2/arch/l4/drivers/genode_block.c -+++ contrib/arch/l4/drivers/genode_block.c -@@ -0,0 +1,273 @@ -+/* -+ * \brief Block driver to access Genode's block service -+ * \author Stefan Kalkowski -+ * \date 2010-07-08 -+ * -+ * This file is based on the sbull block driver from -+ * Linux Device Drivers, 3rd edition chapter 16 "Block Drivers". -+ */ -+ -+/* -+ * Copyright (C) 2006-2013 Genode Labs GmbH -+ * -+ * This file is part of the Genode OS framework, which is distributed -+ * under the terms of the GNU General Public License version 2. -+ */ -+ -+/* Linux includes */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* Genode support library includes */ -+#include -+#include -+ -+enum Geometry { -+ KERNEL_SECTOR_SIZE = 512, /* sector size used by kernel */ -+ GENODE_BLK_MINORS = 16 /* number of minor numbers */ -+}; -+ -+ -+/* -+ * The internal representation of our device. -+ */ -+static struct genode_blk_device { -+ unsigned blk_cnt; /* Total block count */ -+ unsigned long blk_sz; /* Single block size */ -+ spinlock_t lock; /* For mutual exclusion */ -+ struct gendisk *gd; /* Generic disk structure */ -+ struct request_queue *queue; /* The device request queue */ -+ struct semaphore queue_wait; /* Used to block, when queue is full */ -+ short stopped; /* Indicates queue availability */ -+} blk_dev; -+ -+ -+/* -+ * Handle an I/O request. -+ */ -+static void genode_blk_request(struct request_queue *q) -+{ -+ struct request *req; -+ unsigned long queue_offset; -+ -+ while ((req = elv_next_request(q)) != NULL) { -+ void *buf = 0; -+ unsigned long offset = req->sector * KERNEL_SECTOR_SIZE; -+ unsigned long nbytes = req->nr_sectors * KERNEL_SECTOR_SIZE; -+ short write = rq_data_dir(req); -+ -+ if (!blk_fs_request(req)) { -+ printk (KERN_NOTICE "Skip non-fs request\n"); -+ end_request(req, 0); -+ continue; -+ } -+ -+ blkdev_dequeue_request(req); -+ -+ while (!buf) { -+ if ((buf = genode_block_request(nbytes, req, &queue_offset))) -+ break; -+ -+ /* stop_queue needs disabled interrupts */ -+ isr_irq_disable(); -+ blk_stop_queue(q); -+ isr_irq_enable(); -+ -+ /* block until new responses are available */ -+ blk_dev.stopped = 1; -+ down(&blk_dev.queue_wait); -+ -+ /* start_queue needs disabled interrupts */ -+ isr_irq_disable(); -+ blk_start_queue(q); -+ isr_irq_enable(); -+ } -+ -+ if (write) { -+ struct bio *bio; -+ struct bio_vec *bvec; -+ int idx; -+ char *ptr = (char*) buf; -+ -+ rq_for_each_bio(bio, req) { -+ bio_for_each_segment(bvec, bio, idx) { -+ void *buffer = __bio_kmap_atomic(bio, idx, KM_USER0); -+ unsigned long sz = bio_cur_sectors(bio) * KERNEL_SECTOR_SIZE; -+ memcpy((void*)ptr, buffer, sz); -+ __bio_kunmap_atomic(buffer, KM_USER0); -+ } -+ } -+ } -+ -+ genode_block_submit(queue_offset, nbytes, offset, write); -+ } -+} -+ -+ -+static void genode_end_request(void *request, short write, -+ void *buf, unsigned long sz) { -+ struct request *req = (struct request*) request; -+ char *ptr = (char*) buf; -+ -+ if (!write) { -+ struct bio *bio; -+ struct bio_vec *bvec; -+ int idx; -+ rq_for_each_bio(bio, req) { -+ bio_for_each_segment(bvec, bio, idx) { -+ void *buffer = __bio_kmap_atomic(bio, idx, KM_USER0); -+ unsigned long nbytes = bio_cur_sectors(bio) * KERNEL_SECTOR_SIZE; -+ memcpy(buffer, (void*)ptr, nbytes); -+ ptr += nbytes; -+ __bio_kunmap_atomic(buffer, KM_USER0); -+ } -+ } -+ } -+ -+ if (!end_that_request_first(req, 1, req->nr_sectors)) -+ end_that_request_last(req, 1); -+ -+ if (blk_dev.stopped) { -+ blk_dev.stopped = 0; -+ up(&blk_dev.queue_wait); -+ } -+} -+ -+ -+/* -+ * Ioctl method. -+ */ -+int genode_blk_ioctl (struct inode *inode, struct file *filp, -+ unsigned int cmd, unsigned long arg) -+{ -+ struct hd_geometry geo; -+ struct genode_blk_device *dev = inode->i_bdev->bd_disk->private_data; -+ -+ switch(cmd) { -+ case HDIO_GETGEO: -+ { -+ /* -+ * Get geometry: since we cannot access the real geometry of the -+ * device, we have to make up something plausible. So we claim -+ * 16 sectors, four heads, and calculate the corresponding number -+ * of cylinders. We set the start of data at sector four. -+ */ -+ unsigned long size = dev->blk_cnt * dev->blk_sz * -+ (dev->blk_sz / KERNEL_SECTOR_SIZE); -+ geo.cylinders = (size & ~0x3f) >> 6; -+ geo.heads = 4; -+ geo.sectors = 16; -+ geo.start = 4; -+ if (copy_to_user((void *) arg, &geo, sizeof(geo))) -+ return -EFAULT; -+ return 0; -+ } -+ } -+ return -ENOTTY; /* unknown command */ -+} -+ -+ -+/* -+ * The device operations structure. -+ */ -+static struct block_device_operations genode_blk_ops = { -+ .owner = THIS_MODULE, -+ .ioctl = genode_blk_ioctl -+}; -+ -+static int __init genode_blk_init(void) -+{ -+ int major_num; -+ int writeable = 0; -+ unsigned long req_queue_sz = 0; -+ -+ if (!genode_config_block()) -+ return 0; -+ -+ /* Initialize device structure */ -+ memset (&blk_dev, 0, sizeof (struct genode_blk_device)); -+ spin_lock_init(&blk_dev.lock); -+ -+ genode_block_geometry((unsigned long*)&blk_dev.blk_cnt, -+ &blk_dev.blk_sz, &writeable, &req_queue_sz); -+ -+ genode_block_register_callback(genode_end_request); -+ -+ /* -+ * Get a request queue. -+ */ -+ if(!(blk_dev.queue = blk_init_queue(genode_blk_request, &blk_dev.lock))) -+ return -ENOMEM; -+ -+ /* -+ * Align queue requests to hardware sector size. -+ */ -+ blk_queue_hardsect_size(blk_dev.queue, blk_dev.blk_sz); -+ -+ /* -+ * Important, limit number of sectors per request, -+ * as Genode's block-session has a limited request-transmit-queue. -+ */ -+ blk_queue_max_sectors(blk_dev.queue, req_queue_sz / KERNEL_SECTOR_SIZE); -+ blk_dev.queue->queuedata = &blk_dev; -+ -+ sema_init(&blk_dev.queue_wait, 0); -+ blk_dev.stopped = 0; -+ -+ /* -+ * Register block device and gain major number. -+ */ -+ if((major_num = register_blkdev(0, "genode_blk")) < 0) { -+ printk(KERN_WARNING "genode_blk: unable to get major number\n"); -+ return -EBUSY; -+ } -+ -+ /* -+ * Allocate and setup generic disk structure. -+ */ -+ if(!(blk_dev.gd = alloc_disk(GENODE_BLK_MINORS))) { -+ unregister_blkdev(major_num, "genode_blk"); -+ return -ENOMEM; -+ } -+ blk_dev.gd->major = major_num; -+ blk_dev.gd->first_minor = 0; -+ blk_dev.gd->fops = &genode_blk_ops; -+ blk_dev.gd->private_data = &blk_dev; -+ blk_dev.gd->queue = blk_dev.queue; -+ strncpy(blk_dev.gd->disk_name, "sda", sizeof(blk_dev.gd->disk_name)); -+ set_capacity(blk_dev.gd, blk_dev.blk_cnt * -+ (blk_dev.blk_sz / KERNEL_SECTOR_SIZE)); -+ -+ /* Set it read-only or writeable */ -+ if(!writeable) -+ set_disk_ro(blk_dev.gd, 1); -+ -+ /* Make the block device available to the system */ -+ add_disk(blk_dev.gd); -+ printk("Genode blk-file driver initialized\n"); -+ return 0; -+} -+ -+ -+static void __exit -+genode_blk_exit(void) -+{ -+ del_gendisk(blk_dev.gd); -+ put_disk(blk_dev.gd); -+ unregister_blkdev(blk_dev.gd->major, "genode_blk"); -+ blk_cleanup_queue(blk_dev.queue); -+} -+ -+module_init(genode_blk_init); -+module_exit(genode_blk_exit); -+ -+MODULE_LICENSE("GPL"); -diff -urNpB kernel-2.6.23-v2/arch/l4/drivers/genode_fb.c contrib/arch/l4/drivers/genode_fb.c ---- kernel-2.6.23-v2/arch/l4/drivers/genode_fb.c -+++ contrib/arch/l4/drivers/genode_fb.c -@@ -0,0 +1,511 @@ -+/* -+ * \brief Genode screen driver -+ * \author Stefan Kalkowski -+ * \date 2010-04-20 -+ * -+ * This driver enables usage of any of Genode's framebuffer, input -+ * and nitpicker sessions, as defined in Linux corresponding XML config stub. -+ * The implementation is based on virtual (vfb.c) and -+ * L4 (l4fb.c) framebuffer driver of L4Linux from TU-Dresden. -+ */ -+ -+/* -+ * Copyright (C) 2010-2013 Genode Labs GmbH -+ * -+ * This file is part of the Genode OS framework, which is distributed -+ * under the terms of the GNU General Public License version 2. -+ */ -+ -+/* Linux includes */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* Platform includes */ -+#include -+ -+/* Genode support lib includes */ -+#include -+#include -+#include -+ -+ -+/********************************** -+ ** Datastructure declarations ** -+ **********************************/ -+ -+/** -+ * List of available framebuffers (used by device->driver_data) -+ */ -+struct genodefb_infolist { -+ struct fb_info *info; -+ struct genodefb_infolist *next; -+}; -+ -+ -+/********************** -+ ** Global variables ** -+ **********************/ -+ -+static const char GENODEFB_DRV_NAME[] = "genodefb"; -+ -+static struct fb_var_screeninfo genodefb_var __initdata = { -+ .activate = FB_ACTIVATE_NOW, -+ .height = -1, -+ .width = -1, -+ .right_margin = 32, -+ .upper_margin = 16, -+ .lower_margin = 4, -+ .vsync_len = 4, -+ .vmode = FB_VMODE_NONINTERLACED, -+ .bits_per_pixel = 16, // Genode only supports RGB565 by now */ -+ .red.length = 5, -+ .red.offset = 11, -+ .green.length = 6, -+ .green.offset = 5, -+ .blue.length = 5, -+ .blue.offset = 0, -+ .transp.length = 0, -+ .transp.offset = 0, -+}; -+ -+static struct fb_fix_screeninfo genodefb_fix __initdata = { -+ .id = "genode_fb", -+ .type = FB_TYPE_PACKED_PIXELS, -+ .accel = FB_ACCEL_NONE, -+ .visual = FB_VISUAL_TRUECOLOR, -+ .ypanstep = 0, -+ .ywrapstep = 0, -+}; -+ -+static u32 pseudo_palette[17]; -+ -+ -+/************************* -+ ** Device operations ** -+ *************************/ -+ -+/* -+ * Set a single color register. The values supplied are -+ * already rounded down to the hardware's capabilities -+ * (according to the entries in the `var' structure). Return -+ * != 0 for invalid regno and pixel formats. -+ */ -+static int genodefb_setcolreg(unsigned regno, unsigned red, unsigned green, -+ unsigned blue, unsigned transp, -+ struct fb_info *info) -+{ -+ if (regno >= info->cmap.len || info->var.bits_per_pixel != 16) -+ return 1; -+ -+ if (regno < 16) -+ ((u32*) (info->pseudo_palette))[regno] = -+ ((red >> (16 - info->var.red.length)) << info->var.red.offset) | -+ ((green >> (16 - info->var.green.length)) << info->var.green.offset) | -+ ((blue >> (16 - info->var.blue.length)) << info->var.blue.offset); -+ return 0; -+} -+ -+ -+/** -+ * Pan or Wrap the Display -+ * -+ * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag -+ */ -+static int genodefb_pan_display(struct fb_var_screeninfo *var, -+ struct fb_info *info) -+{ -+ if (var->vmode & FB_VMODE_YWRAP) { -+ if (var->yoffset < 0 -+ || var->yoffset >= info->var.yres_virtual -+ || var->xoffset) -+ return -EINVAL; -+ } else { -+ if (var->xoffset + var->xres > info->var.xres_virtual || -+ var->yoffset + var->yres > info->var.yres_virtual) -+ return -EINVAL; -+ } -+ info->var.xoffset = var->xoffset; -+ info->var.yoffset = var->yoffset; -+ if (var->vmode & FB_VMODE_YWRAP) -+ info->var.vmode |= FB_VMODE_YWRAP; -+ else -+ info->var.vmode &= ~FB_VMODE_YWRAP; -+ return 0; -+} -+ -+ -+static void genodefb_copyarea(struct fb_info *info, -+ const struct fb_copyarea *region) -+{ -+ cfb_copyarea(info, region); -+ genode_fb_refresh(info->node, region->dx, region->dy, -+ region->width, region->height); -+} -+ -+ -+static void genodefb_fillrect(struct fb_info *info, -+ const struct fb_fillrect *rect) -+{ -+ cfb_fillrect(info, rect); -+ genode_fb_refresh(info->node, rect->dx, rect->dy, -+ rect->width, rect->height); -+} -+ -+ -+static void genodefb_imageblit(struct fb_info *info, -+ const struct fb_image *image) -+{ -+ cfb_imageblit(info, image); -+ genode_fb_refresh(info->node, image->dx, image->dy, -+ image->width, image->height); -+} -+ -+ -+static int genodefb_open(struct fb_info *info, int user) -+{ -+ return 0; -+} -+ -+ -+static int genodefb_release(struct fb_info *info, int user) -+{ -+ genode_nit_close_all_views(info->node); -+ return 0; -+} -+ -+ -+static int genodefb_ioctl(struct fb_info *info, unsigned int cmd, -+ unsigned long arg) -+{ -+ void __user *argp = (void __user *)arg; -+ switch (cmd) { -+ case NITPICKER_IOCTL_CREATE_VIEW: -+ { -+ genode_nit_view_create(info->node, (int)arg); -+ break; -+ } -+ case NITPICKER_IOCTL_DESTROY_VIEW: -+ { -+ genode_nit_view_destroy(info->node, (int)arg); -+ break; -+ } -+ case NITPICKER_IOCTL_BACK_VIEW: -+ { -+ genode_nit_view_back(info->node, (int)arg); -+ break; -+ } -+ case NITPICKER_IOCTL_PLACE_VIEW: -+ { -+ struct genode_view_place val; -+ if (copy_from_user(&val, argp, sizeof(val))) -+ return -EFAULT; -+ genode_nit_view_place(info->node, val.view, val.reg.x, -+ val.reg.y, val.reg.w, val.reg.h); -+ break; -+ } -+ case NITPICKER_IOCTL_STACK_VIEW: -+ { -+ struct genode_view_stack val; -+ if (copy_from_user(&val, argp, sizeof(val))) -+ return -EFAULT; -+ genode_nit_view_stack(info->node, val.view, -+ val.neighbor, val.behind); -+ break; -+ } -+ case FRAMEBUFFER_IOCTL_REFRESH: -+ { -+ struct genode_screen_region val; -+ if (copy_from_user(&val, argp, sizeof(val))) -+ return -EFAULT; -+ genode_fb_refresh(info->node, val.x, val.y, val.w, val.h); -+ break; -+ } -+ default: -+ printk(KERN_INFO "Unknown ioctl command: %d\n", cmd); -+ } -+ return 0; -+} -+ -+ -+static struct fb_ops genodefb_ops = { -+ .owner = THIS_MODULE, -+ .fb_open = genodefb_open, -+ .fb_release = genodefb_release, -+ .fb_setcolreg = genodefb_setcolreg, -+ .fb_pan_display = genodefb_pan_display, -+ .fb_fillrect = genodefb_fillrect, -+ .fb_ioctl = genodefb_ioctl, -+ .fb_copyarea = genodefb_copyarea, -+ .fb_imageblit = genodefb_imageblit, -+}; -+ -+ -+/*********************** -+ ** Input callbacks ** -+ ***********************/ -+ -+void input_event_callback (void *dev, unsigned int type, -+ unsigned int code, int value) -+{ -+ struct input_dev *input_dev = (struct input_dev*) dev; -+ input_event(input_dev, type, code, value); -+ input_sync(input_dev); -+} -+ -+ -+/*************************************** -+ ** Device initialization / removal ** -+ ***************************************/ -+ -+static int __init genodefb_register_input_devices(unsigned int idx, -+ unsigned int xres, -+ unsigned int yres) -+{ -+ int i; -+ struct input_dev *mouse_dev = input_allocate_device(); -+ struct input_dev *keyb_dev = input_allocate_device(); -+ if (!keyb_dev || !mouse_dev) -+ return -ENOMEM; -+ -+ -+ /**************** -+ ** Keyboard ** -+ ****************/ -+ -+ keyb_dev->name = "Genode input key"; -+ keyb_dev->phys = "Genode fb key"; -+ keyb_dev->id.bustype = BUS_USB; -+ keyb_dev->id.vendor = 0; -+ keyb_dev->id.product = 0; -+ keyb_dev->id.version = 0; -+ -+ /* We generate key events */ -+ set_bit(EV_KEY, keyb_dev->evbit); -+ set_bit(EV_REP, keyb_dev->evbit); -+ -+ /* We can generate every key */ -+ for (i = 0; i < 0x100; i++) -+ set_bit(i, keyb_dev->keybit); -+ -+ /* Register keyboard device */ -+ input_register_device(keyb_dev); -+ genode_input_register_keyb(idx, (void*) keyb_dev); -+ -+ -+ /************* -+ ** Mouse ** -+ *************/ -+ -+ mouse_dev->name = "Genode input mouse"; -+ mouse_dev->phys = "Genode mouse"; -+ mouse_dev->id.bustype = BUS_USB; -+ mouse_dev->id.vendor = 0; -+ mouse_dev->id.product = 0; -+ mouse_dev->id.version = 0; -+ -+ /* We generate key and relative mouse events */ -+ set_bit(EV_KEY, mouse_dev->evbit); -+ set_bit(EV_REP, mouse_dev->evbit); -+ set_bit(EV_REL, mouse_dev->evbit); -+ set_bit(EV_ABS, mouse_dev->evbit); -+ -+ /* Buttons */ -+ set_bit(BTN_0, mouse_dev->keybit); -+ set_bit(BTN_1, mouse_dev->keybit); -+ set_bit(BTN_2, mouse_dev->keybit); -+ set_bit(BTN_3, mouse_dev->keybit); -+ set_bit(BTN_4, mouse_dev->keybit); -+ set_bit(BTN_LEFT, mouse_dev->keybit); -+ set_bit(BTN_RIGHT, mouse_dev->keybit); -+ set_bit(BTN_MIDDLE, mouse_dev->keybit); -+ -+ /* Movements */ -+ set_bit(REL_X, mouse_dev->relbit); -+ set_bit(REL_Y, mouse_dev->relbit); -+ set_bit(ABS_X, mouse_dev->absbit); -+ set_bit(ABS_Y, mouse_dev->absbit); -+ -+ /* Coordinates are 1:1 pixel in frame buffer */ -+ mouse_dev->absmax[ABS_X] = xres; -+ mouse_dev->absmax[ABS_Y] = yres; -+ mouse_dev->absmin[ABS_X] = 0; -+ mouse_dev->absmin[ABS_Y] = 0; -+ -+ /* We are precise */ -+ mouse_dev->absfuzz[ABS_X] = 0; -+ mouse_dev->absfuzz[ABS_Y] = 0; -+ mouse_dev->absflat[ABS_X] = 0; -+ mouse_dev->absflat[ABS_Y] = 0; -+ -+ /* Register mouse device */ -+ input_register_device(mouse_dev); -+ genode_input_register_mouse(idx, (void*) mouse_dev); -+ return 0; -+} -+ -+ -+static int __init genodefb_probe(struct platform_device *dev) -+{ -+ struct genodefb_infolist *pred=0, *succ; -+ int i, ret, cnt = genode_screen_count(); -+ -+ /* -+ * Iterate through all available framebuffers -+ */ -+ for (i=0; i < cnt; i++) { -+ -+ /* Allocate new framebuffer list entry */ -+ if(!(succ = kmalloc(sizeof(struct genodefb_infolist), GFP_KERNEL))) -+ return -ENOMEM; -+ succ->next = 0; -+ -+ /* Set first entry as driver's private data, else concatenate it */ -+ if (!i) -+ platform_set_drvdata(dev, succ); -+ else -+ pred->next = succ; -+ pred = succ; -+ -+ /* Allocate new framebuffer info struct */ -+ pred->info = framebuffer_alloc(0, &dev->dev); -+ if (!pred->info) -+ return -ENOMEM; -+ -+ /* Copy default values */ -+ pred->info->var = genodefb_var; -+ pred->info->fix = genodefb_fix; -+ -+ /* Get framebuffer dimensions from Genode's support lib */ -+ pred->info->screen_base = genode_fb_attach(i); -+ pred->info->screen_size = genode_fb_size(i); -+ pred->info->fix.smem_start = (unsigned long) pred->info->screen_base; -+ pred->info->fix.smem_len = pred->info->screen_size; -+ if (!pred->info->screen_base || !pred->info->screen_size) { -+ printk(KERN_ERR "genode_fb: abort, could not be initialized.\n"); -+ framebuffer_release(pred->info); -+ return -EIO; -+ } -+ -+ /* Get framebuffer resolution from Genode's support lib */ -+ genode_fb_info(i, &pred->info->var.xres, &pred->info->var.yres); -+ -+ /* We only support 16-Bit Pixel, so line length is xres*2 */ -+ pred->info->fix.line_length = pred->info->var.xres * 2; -+ -+ /* Set virtual resolution to visible resolution */ -+ pred->info->var.xres_virtual = pred->info->var.xres; -+ pred->info->var.yres_virtual = pred->info->screen_size -+ / pred->info->fix.line_length; -+ -+ /* Some dummy values for timing to make fbset happy */ -+ pred->info->var.pixclock = 10000000 / pred->info->var.xres -+ * 1000 / pred->info->var.yres; -+ pred->info->var.left_margin = (pred->info->var.xres / 8) & 0xf8; -+ pred->info->var.hsync_len = (pred->info->var.xres / 8) & 0xf8; -+ -+ pred->info->fbops = &genodefb_ops; -+ pred->info->pseudo_palette = pseudo_palette; -+ pred->info->flags = FBINFO_FLAG_DEFAULT; -+ -+ printk(KERN_INFO "genode_fb:framebuffer at 0x%p, size %dk\n", -+ pred->info->screen_base, (int)(pred->info->screen_size >> 10)); -+ printk(KERN_INFO "genode_fb: mode is %dx%dx%d\n", -+ pred->info->var.xres, pred->info->var.yres, -+ pred->info->var.bits_per_pixel); -+ -+ /* Allocate 16-Bit colormap */ -+ ret = fb_alloc_cmap(&pred->info->cmap, 16, 0); -+ if (ret < 0) { -+ framebuffer_release(pred->info); -+ return ret; -+ } -+ -+ /* Register framebuffer info structure */ -+ if (register_framebuffer(pred->info) < 0) { -+ fb_dealloc_cmap(&pred->info->cmap); -+ framebuffer_release(pred->info); -+ return -EINVAL; -+ } -+ -+ ret = genodefb_register_input_devices(i, pred->info->var.xres, -+ pred->info->var.yres); -+ if (ret) { -+ fb_dealloc_cmap(&pred->info->cmap); -+ framebuffer_release(pred->info); -+ return ret; -+ } -+ } -+ return 0; -+} -+ -+ -+static int genodefb_remove(struct platform_device *device) -+{ -+ struct genodefb_infolist *succ = platform_get_drvdata(device); -+ -+ while (succ && succ->info) { -+ struct genodefb_infolist *pred = succ; -+ succ = succ->next; -+ genode_fb_close(pred->info->node); -+ unregister_framebuffer(pred->info); -+ framebuffer_release(pred->info); -+ kfree(pred); -+ } -+ platform_set_drvdata(device, 0); -+ return 0; -+} -+ -+ -+/*************************************** -+ ** Module initialization / removal ** -+ ***************************************/ -+ -+static struct platform_driver genodefb_driver = { -+ .probe = genodefb_probe, -+ .remove = genodefb_remove, -+ .driver.name = GENODEFB_DRV_NAME, -+}; -+ -+static struct platform_device genodefb_device = { -+ .name = GENODEFB_DRV_NAME, -+}; -+ -+ -+static int __init genodefb_init(void) -+{ -+ int ret = platform_driver_register(&genodefb_driver); -+ if (!ret) { -+ ret = platform_device_register(&genodefb_device); -+ if (ret) -+ platform_driver_unregister(&genodefb_driver); -+ } -+ genode_input_register_callback(&input_event_callback); -+ return ret; -+} -+module_init(genodefb_init); -+ -+ -+static void __exit genodefb_exit(void) -+{ -+ platform_device_unregister(&genodefb_device); -+ platform_driver_unregister(&genodefb_driver); -+ genode_input_unregister_callback(); -+} -+module_exit(genodefb_exit); -+ -+ -+MODULE_AUTHOR("Stefan Kalkowski "); -+MODULE_DESCRIPTION("Frame buffer driver for OKLinux on Genode"); -+MODULE_LICENSE("GPL v2"); -diff -urNpB kernel-2.6.23-v2/arch/l4/drivers/genode_net.c contrib/arch/l4/drivers/genode_net.c ---- kernel-2.6.23-v2/arch/l4/drivers/genode_net.c -+++ contrib/arch/l4/drivers/genode_net.c -@@ -0,0 +1,165 @@ -+/* -+ * \brief NIC driver to access Genode's nic service -+ * \author Stefan Kalkowski -+ * \date 2010-09-09 -+ */ -+ -+/* -+ * Copyright (C) 2006-2013 Genode Labs GmbH -+ * -+ * This file is part of the Genode OS framework, which is distributed -+ * under the terms of the GNU General Public License version 2. -+ */ -+ -+/* Linux includes */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+static struct net_device *net_dev; -+ -+ -+static void genode_net_receive_packet(void* dev_addr, void *addr, -+ unsigned long size) -+{ -+ struct net_device *dev = (struct net_device *) dev_addr; -+ struct net_device_stats *stats = (struct net_device_stats*) netdev_priv(dev); -+ -+ /* allocate skb */ -+ struct sk_buff *skb = dev_alloc_skb(size + 2); -+ if (!skb) { -+ if (printk_ratelimit()) -+ printk(KERN_NOTICE "genode_net_rx: low on mem - packet dropped!\n"); -+ stats->rx_dropped++; -+ return; -+ } -+ -+ /* copy packet */ -+ memcpy(skb_put(skb, size), addr, size); -+ -+ skb->dev = dev; -+ skb->protocol = eth_type_trans(skb, dev); -+ skb->ip_summed = CHECKSUM_NONE; -+ netif_rx(skb); -+ -+ stats->rx_packets++; -+ stats->rx_bytes += size; -+} -+ -+ -+/******************************** -+ ** Network driver functions ** -+ ********************************/ -+ -+int genode_net_open(struct net_device *dev) -+{ -+ genode_net_start(dev, genode_net_receive_packet); -+ netif_start_queue(dev); -+ return 0; -+} -+ -+ -+int genode_net_close(struct net_device *dev) -+{ -+ netif_stop_queue(dev); -+ genode_net_stop(); -+ return 0; -+} -+ -+ -+int genode_net_xmit_frame(struct sk_buff *skb, struct net_device *dev) -+{ -+ struct net_device_stats *stats = (struct net_device_stats*) netdev_priv(dev); -+ int len = skb->len; -+ void* addr = skb->data; -+ -+ /* collect acknowledgements of old packets */ -+ while (genode_net_tx_ack_avail()) -+ dev_kfree_skb((struct sk_buff *)genode_net_tx_ack()); -+ -+ /* transmit to nic-session */ -+ if (genode_net_tx(addr, len, skb)) { -+ /* tx queue is full, could not enqueue packet */ -+ netif_stop_queue(dev); -+ return 1; -+ } -+ -+ /* save timestamp */ -+ dev->trans_start = jiffies; -+ -+ stats->tx_packets++; -+ stats->tx_bytes += len; -+ return 0; -+} -+ -+ -+struct net_device_stats* genode_net_get_stats(struct net_device *dev) -+{ -+ return (struct net_device_stats*) netdev_priv(dev); -+} -+ -+ -+void genode_net_tx_timeout(struct net_device *dev) -+{ -+} -+ -+ -+/************************** -+ ** De-/Initialization ** -+ **************************/ -+ -+/* Setup and register the device. */ -+static int __init genode_net_init(void) -+{ -+ int err = 0; -+ -+ if (!genode_net_ready()) -+ return 0; -+ -+ /* allocate network device */ -+ if (!(net_dev = alloc_etherdev(sizeof(struct net_device_stats)))) -+ goto out; -+ -+ net_dev->open = genode_net_open; -+ net_dev->stop = genode_net_close; -+ net_dev->hard_start_xmit = genode_net_xmit_frame; -+ net_dev->get_stats = genode_net_get_stats; -+ net_dev->tx_timeout = genode_net_tx_timeout; -+ net_dev->watchdog_timeo = 20 * HZ; -+ -+ /* set MAC address */ -+ genode_net_mac(net_dev->dev_addr, ETH_ALEN); -+ -+ /* register network device */ -+ if ((err = register_netdev(net_dev))) { -+ panic("loopback: Failed to register netdevice: %d\n", err); -+ goto out_free; -+ } -+ -+ return 0; -+ -+out_free: -+ free_netdev(net_dev); -+out: -+ return err; -+}; -+ -+ -+static void __exit genode_net_exit(void) -+{ -+ unregister_netdev(net_dev); -+ free_netdev(net_dev); -+} -+ -+ -+module_init(genode_net_init); -+module_exit(genode_net_exit); -diff -urNpB kernel-2.6.23-v2/arch/l4/drivers/l4kdb_console.c contrib/arch/l4/drivers/l4kdb_console.c ---- kernel-2.6.23-v2/arch/l4/drivers/l4kdb_console.c -+++ contrib/arch/l4/drivers/l4kdb_console.c -@@ -4,28 +4,25 @@ - - #include - -+#include -+ - static void l4kdb_cons_write(struct console *co, const char *p, unsigned count) - { -- while (count-- > 0) { -- L4_KDB_PrintChar(*p++); -- } -+ char buf[count+1]; -+ int i; -+ for(i=0; i - #include - --#define STACK_SIZE 0x2000 -+#define STACK_SIZE 0x4000 - - extern cap_t *first_cap; - extern bootmem_area_t bootmem_area[MAX_PHYSMEM_RANGES]; -diff -urNpB kernel-2.6.23-v2/arch/l4/kernel/Makefile contrib/arch/l4/kernel/Makefile ---- kernel-2.6.23-v2/arch/l4/kernel/Makefile -+++ contrib/arch/l4/kernel/Makefile -@@ -3,7 +3,11 @@ core-y += arch/l4/kernel/fs/ - obj-y := reboot.o glue.o main.o init_task.o time.o memcpy_user.o \ - syscalls.o proc.o process.o syscall_impl.o setup.o \ - signal.o sys_iguana.o cache.o exit.o syscall_loop.o \ -- mmap.o irq.o intervm_loop.o -+ mmap.o irq.o intervm_loop.o calibrate.o -+ -+# Genode specific -+obj-y := $(filter-out intervm_loop.o sys_iguana.o,$(obj-y)) -+obj-y += sys_genode.o - - obj-$(CONFIG_MODULES) += syms.o - obj-$(CONFIG_ARCH_ARM) += memcpy_user_armshared.o -diff -urNpB kernel-2.6.23-v2/arch/l4/kernel/calibrate.c contrib/arch/l4/kernel/calibrate.c ---- kernel-2.6.23-v2/arch/l4/kernel/calibrate.c -+++ contrib/arch/l4/kernel/calibrate.c -@@ -0,0 +1,179 @@ -+/* calibrate.c: default delay calibration -+ * -+ * Excised from init/main.c -+ * Copyright (C) 1991, 1992 Linus Torvalds -+ */ -+ -+#include -+#include -+#include -+ -+#include -+ -+#include -+ -+extern void* timer_main_kernel_lock; -+ -+static unsigned long preset_lpj; -+static int __init lpj_setup(char *str) -+{ -+ preset_lpj = simple_strtoul(str,NULL,0); -+ return 1; -+} -+ -+__setup("lpj=", lpj_setup); -+ -+#ifdef ARCH_HAS_READ_CURRENT_TIMER -+ -+/* This routine uses the read_current_timer() routine and gets the -+ * loops per jiffy directly, instead of guessing it using delay(). -+ * Also, this code tries to handle non-maskable asynchronous events -+ * (like SMIs) -+ */ -+#define DELAY_CALIBRATION_TICKS ((HZ < 100) ? 1 : (HZ/100)) -+#define MAX_DIRECT_CALIBRATION_RETRIES 5 -+ -+static unsigned long __devinit calibrate_delay_direct(void) -+{ -+ unsigned long pre_start, start, post_start; -+ unsigned long pre_end, end, post_end; -+ unsigned long start_jiffies; -+ unsigned long tsc_rate_min, tsc_rate_max; -+ unsigned long good_tsc_sum = 0; -+ unsigned long good_tsc_count = 0; -+ int i; -+ -+ if (read_current_timer(&pre_start) < 0 ) -+ return 0; -+ -+ /* -+ * A simple loop like -+ * while ( jiffies < start_jiffies+1) -+ * start = read_current_timer(); -+ * will not do. As we don't really know whether jiffy switch -+ * happened first or timer_value was read first. And some asynchronous -+ * event can happen between these two events introducing errors in lpj. -+ * -+ * So, we do -+ * 1. pre_start <- When we are sure that jiffy switch hasn't happened -+ * 2. check jiffy switch -+ * 3. start <- timer value before or after jiffy switch -+ * 4. post_start <- When we are sure that jiffy switch has happened -+ * -+ * Note, we don't know anything about order of 2 and 3. -+ * Now, by looking at post_start and pre_start difference, we can -+ * check whether any asynchronous event happened or not -+ */ -+ -+ for (i = 0; i < MAX_DIRECT_CALIBRATION_RETRIES; i++) { -+ pre_start = 0; -+ read_current_timer(&start); -+ start_jiffies = jiffies; -+ while (jiffies <= (start_jiffies + 1)) { -+ pre_start = start; -+ read_current_timer(&start); -+ } -+ read_current_timer(&post_start); -+ -+ pre_end = 0; -+ end = post_start; -+ while (jiffies <= -+ (start_jiffies + 1 + DELAY_CALIBRATION_TICKS)) { -+ pre_end = end; -+ read_current_timer(&end); -+ } -+ read_current_timer(&post_end); -+ -+ tsc_rate_max = (post_end - pre_start) / DELAY_CALIBRATION_TICKS; -+ tsc_rate_min = (pre_end - post_start) / DELAY_CALIBRATION_TICKS; -+ -+ /* -+ * If the upper limit and lower limit of the tsc_rate is -+ * >= 12.5% apart, redo calibration. -+ */ -+ if (pre_start != 0 && pre_end != 0 && -+ (tsc_rate_max - tsc_rate_min) < (tsc_rate_max >> 3)) { -+ good_tsc_count++; -+ good_tsc_sum += tsc_rate_max; -+ } -+ } -+ -+ if (good_tsc_count) -+ return (good_tsc_sum/good_tsc_count); -+ -+ printk(KERN_WARNING "calibrate_delay_direct() failed to get a good " -+ "estimate for loops_per_jiffy.\nProbably due to long platform interrupts. Consider using \"lpj=\" boot option.\n"); -+ return 0; -+} -+#else -+static unsigned long __devinit calibrate_delay_direct(void) {return 0;} -+#endif -+ -+/* -+ * This is the number of bits of precision for the loops_per_jiffy. Each -+ * bit takes on average 1.5/HZ seconds. This (like the original) is a little -+ * better than 1% -+ */ -+#define LPS_PREC 8 -+ -+void __devinit calibrate_delay(void) -+{ -+ unsigned long ticks, loopbit; -+ int lps_precision = LPS_PREC; -+ -+ genode_unlock(timer_main_kernel_lock); -+ -+ if (preset_lpj) { -+ loops_per_jiffy = preset_lpj; -+ printk("Calibrating delay loop (skipped)... " -+ "%lu.%02lu BogoMIPS preset\n", -+ loops_per_jiffy/(500000/HZ), -+ (loops_per_jiffy/(5000/HZ)) % 100); -+ } else if ((loops_per_jiffy = calibrate_delay_direct()) != 0) { -+ printk("Calibrating delay using timer specific routine.. "); -+ printk("%lu.%02lu BogoMIPS (lpj=%lu)\n", -+ loops_per_jiffy/(500000/HZ), -+ (loops_per_jiffy/(5000/HZ)) % 100, -+ loops_per_jiffy); -+ } else { -+ loops_per_jiffy = (1<<12); -+ -+ printk(KERN_DEBUG "Calibrating delay loop... "); -+ while ((loops_per_jiffy <<= 1) != 0) { -+ /* wait for "start of" clock tick */ -+ ticks = jiffies; -+ while (ticks == jiffies) -+ /* nothing */; -+ /* Go .. */ -+ ticks = jiffies; -+ __delay(loops_per_jiffy); -+ ticks = jiffies - ticks; -+ if (ticks) -+ break; -+ } -+ -+ /* -+ * Do a binary approximation to get loops_per_jiffy set to -+ * equal one clock (up to lps_precision bits) -+ */ -+ loops_per_jiffy >>= 1; -+ loopbit = loops_per_jiffy; -+ while (lps_precision-- && (loopbit >>= 1)) { -+ loops_per_jiffy |= loopbit; -+ ticks = jiffies; -+ while (ticks == jiffies) -+ /* nothing */; -+ ticks = jiffies; -+ __delay(loops_per_jiffy); -+ if (jiffies != ticks) /* longer than 1 tick */ -+ loops_per_jiffy &= ~loopbit; -+ } -+ -+ /* Round the value and print it */ -+ printk("%lu.%02lu BogoMIPS (lpj=%lu)\n", -+ loops_per_jiffy/(500000/HZ), -+ (loops_per_jiffy/(5000/HZ)) % 100, -+ loops_per_jiffy); -+ } -+ genode_lock(timer_main_kernel_lock); -+} -diff -urNpB kernel-2.6.23-v2/arch/l4/kernel/exit.c contrib/arch/l4/kernel/exit.c ---- kernel-2.6.23-v2/arch/l4/kernel/exit.c -+++ contrib/arch/l4/kernel/exit.c -@@ -9,7 +9,7 @@ - #include - - #include --#include -+#include - - /* - * Called when Linux startup fails / exit server -@@ -18,10 +18,8 @@ - void - _Exit(int status) - { -- pd_delete(pd_myself()); -- -- assert(!"We have exitted -- sholdn't reach here"); -- while(1); -+ genode_exit(status); -+ while(1) ; - } - - void __div0(void) -diff -urNpB kernel-2.6.23-v2/arch/l4/kernel/fs/okl4fs/inode.c contrib/arch/l4/kernel/fs/okl4fs/inode.c ---- kernel-2.6.23-v2/arch/l4/kernel/fs/okl4fs/inode.c -+++ contrib/arch/l4/kernel/fs/okl4fs/inode.c -@@ -17,8 +17,8 @@ - #include - #include "internal.h" - --#include --#include -+//#include -+//#include - - #include - -diff -urNpB kernel-2.6.23-v2/arch/l4/kernel/glue.c contrib/arch/l4/kernel/glue.c ---- kernel-2.6.23-v2/arch/l4/kernel/glue.c -+++ contrib/arch/l4/kernel/glue.c -@@ -4,9 +4,12 @@ - #include - #include - -+#include /* needed by 'asm/tlb.h' */ -+ - #define __KERNEL_SYSCALLS__ - --#include /* execve */ -+#include /* execve */ -+#include /* 'struct mmu_gather' */ - - int - kernel_execve(const char *filename, char *const argv[], char *const envp[]) -@@ -40,7 +43,8 @@ const char *get_system_type(void) - return "L4 Default arch"; - } - --void per_cpu__mmu_gathers (void) { printk("per_cpu__mmu_gathers called\n"); } -+DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); -+ - void ptrace_disable (void) { printk("ptrace_disable called\n"); } - //void search_extable (void) { printk("search_extable called\n"); } - void show_stack(struct task_struct *task, unsigned long *sp) { printk("show_stack called\n"); } -diff -urNpB kernel-2.6.23-v2/arch/l4/kernel/irq.c contrib/arch/l4/kernel/irq.c ---- kernel-2.6.23-v2/arch/l4/kernel/irq.c -+++ contrib/arch/l4/kernel/irq.c -@@ -78,12 +78,12 @@ show_interrupts(struct seq_file *p, void - #endif - seq_printf(p, " %14s", irq_desc[i].chip->name); - seq_printf(p, " %c%s", -- (action->flags & SA_INTERRUPT)?'+':' ', -+ (action->flags & IRQF_DISABLED)?'+':' ', - action->name); - - for (action=action->next; action; action = action->next) { - seq_printf(p, ", %c%s", -- (action->flags & SA_INTERRUPT)?'+':' ', -+ (action->flags & IRQF_DISABLED)?'+':' ', - action->name); - } - -diff -urNpB kernel-2.6.23-v2/arch/l4/kernel/main.c contrib/arch/l4/kernel/main.c ---- kernel-2.6.23-v2/arch/l4/kernel/main.c -+++ contrib/arch/l4/kernel/main.c -@@ -24,12 +24,13 @@ - #include - - #include --#include - #include - #include --#include - --void *__callback_buffer; -+#include -+#include -+#include -+#include - - uintptr_t temp_cap_slot; - uintptr_t temp_cap_used; -@@ -39,17 +40,20 @@ uintptr_t temp_cap_addr; - uintptr_t dma_heap_base, dma_heap_phys_base; - - static uintptr_t main_stack[STACK_SIZE]; --static uintptr_t intervm_stack[STACK_SIZE]; -+static uintptr_t timer_stack[STACK_SIZE]; -+ -+void* timer_main_kernel_lock; - - extern void interrupt_loop(void); - - void *main_tls_area[KERNEL_THREADS][32]; - - extern void start_kernel(void); --extern void intervm_loop(void); - - void *console_out, *console_in; - -+extern void enable_early_printk(void); -+ - /* Our per cpu irq enable/disable. Where should this go? */ - DEFINE_PER_CPU(irq_info_t, _l4_irq_state) = { 0 }; - EXPORT_PER_CPU_SYMBOL(_l4_irq_state); -@@ -59,7 +63,6 @@ L4_Word_t start_phys_mem, end_phys_mem; - L4_ThreadId_t main_thread; - L4_ThreadId_t timer_thread; - L4_ThreadId_t timer_handle; --L4_ThreadId_t intervm_thread; - - unsigned long get_instr(unsigned long addr) - { -@@ -96,67 +99,13 @@ unsigned long set_instr(unsigned long ad - return 0; - } - --/* --Malloc --*/ --void * --malloc(size_t size) --{ -- void *ret; -- //printk("malloc: %d\n", (int)size); -- ret = kmalloc(size, GFP_ATOMIC); -- //printk("malloc returned: %p\n", ret); -- return ret; --} -- --void * --calloc(size_t nmemb, size_t size) --{ -- printk("calloc called\n"); -- return NULL; --} -- --void --free(void *ptr) --{ -- return kfree(ptr); --} -- --int --puts(const char *format) --{ -- printk("puts called\n"); -- return 0; --} -- --int --printf(const char *format, ...) --{ -- printk("printf\n"); -- return 0; --// return printk(format); --} -- - -- --void --__linux_cap_init(uintptr_t cap_slot, uintptr_t cap_used, uintptr_t cap_size, uintptr_t cap_addr) --{ --// temp_cap_slot = cap_slot; --// temp_cap_used = cap_used; --// temp_cap_size = cap_size; --// temp_cap_addr = cap_addr; --// first_cap = (cap_t*) cap_addr; --} -- -- --void --__libc_setup(void *callback, void *stdin_p, void *stdout_p, void *stderr_p, -- unsigned long heap_base, unsigned long heap_end) -+void __init -+__libc_setup(void *stdin_p, void *stdout_p, void *stderr_p, -+ unsigned long heap_base, unsigned long heap_end) - { - console_out = stdout_p; - console_in = stdin_p; -- __callback_buffer = callback; - - /* Setup the memory areas from iguana */ - assert(heap_end > heap_base); -@@ -165,106 +114,96 @@ __libc_setup(void *callback, void *stdin - bootmem_area[0].pages = (heap_end >> PAGE_SHIFT) - bootmem_area[0].page_base; - } - --void --__lib_init(uintptr_t *buf) -+void __init __lib_init(void) - { -- void* callback; -- void* stdin_p; -- void* stdout_p; -- void* stderr_p; - char* heap_base; - size_t heap_size; - uint32_t dma_size; - -- __lib_iguana_init(buf); -- -- callback = env_memsection_base(iguana_getenv("__OKL4_CALLBACK_BUFFER")); -- stdin_p = NULL; //env_memsection_base(iguana_getenv("OKL4_SERIAL_SERVER")); -- stdout_p = NULL; //env_memsection_base(iguana_getenv("OKL4_SERIAL_SERVER")); -- stderr_p = NULL; //env_memsection_base(iguana_getenv("OKL4_SERIAL_SERVER")); -- heap_base = (char*) env_const(iguana_getenv("HEAP_BASE")); -- heap_size = env_const(iguana_getenv("HEAP_SIZE")); -- -- -- __libc_setup(callback, stdin_p, stdout_p, stderr_p, -- (unsigned long) heap_base, -- (unsigned long) (heap_base + heap_size - 1)); -- __cap_init(); -- --// __linux_cap_init((uintptr_t) iguana_getenv(IGUANA_GETENV_CLIST_SLOT), --// (uintptr_t) iguana_getenv(IGUANA_GETENV_CLIST_USED), --// CLIST_MEMORY_SIZE / sizeof(cap_t), --// (uintptr_t) iguana_getenv(IGUANA_GETENV_CLIST_BASE)); --// -+ heap_size = genode_quota() - genode_used_mem() -+ - 8*1024*1024; // Leave some memory left for dynamic allocations -+ heap_base = genode_malloc(heap_size); -+ __libc_setup(0, 0, 0, (unsigned long) heap_base, -+ (unsigned long) (heap_base + heap_size - 1)); - dma_heap_base = (uint32_t)heap_base; - dma_heap_phys_base = memsection_virt_to_phys((uintptr_t)heap_base, -- &dma_size); -+ &dma_size); - } - -- --void __sys_entry(void *buf, int argc, char** argv); - void _Exit(int status); - - int main(int argc, char** argv); -+void __sys_entry(void *buf, int argc, char** argv){} - --/* This is the entry point from the C libraries crt0 */ --void --__sys_entry(void *buf, int argc, char** argv) -+void setup_tls(int thread_num) - { -- int result; -- __lib_init(buf); -- /* Start Linux */ -- result = main(argc, argv); -- /* Exit Linux Server */ -- _Exit(result); - } - --void --setup_tls(int thread_num) -+void __init start_main_thread(void) - { -- __tls_init(main_tls_area[thread_num]); -+ /* Set me as the pager for every user process started by Linux */ -+ genode_set_pager(); -+ genode_lock(timer_main_kernel_lock); -+ -+#ifdef CONFIG_EARLY_PRINTK -+ /* early console initialisation */ -+ enable_early_printk(); -+#endif -+ -+ start_kernel(); - } - --int --main(int argc, char **argv) -+void __init start_timer_thread(void) - { -- int r; -- /* Start a new thread */ -- assert(argc > 0 && argv[0] != NULL); -- -- strlcpy(boot_command_line, argv[1], COMMAND_LINE_SIZE); -- L4_KDB_SetThreadName(L4_myselfconst, "L_timer"); -- -+ /* Create main kernel thread, doing paging, syscalls etc. */ - thread_create(&main_thread); -- r = L4_Set_Priority(main_thread, 99); -- assert (r != 0); -- -- timer_thread = thread_l4tid(env_thread(iguana_getenv("MAIN"))); - L4_KDB_SetThreadName(main_thread, "L_syscall"); - - /* Setup our TLS as well */ -- setup_tls(TIMER_THREAD); -- -- thread_create(&intervm_thread); -- r = L4_Set_Priority(intervm_thread, 98); -- assert(r != 0); -- L4_KDB_SetThreadName(intervm_thread, "L_intervm"); -+ __tls_init(main_tls_area[TIMER_THREAD]); - - /* Thread info setup. */ - /* FIXME: remember for SMP startup */ -- current_tinfo(smp_processor_id()) = (unsigned long)&init_thread_union.thread_info; -+ current_tinfo(smp_processor_id()) = -+ (unsigned long)&init_thread_union.thread_info; - current_thread_info()->user_tid = L4_nilthread; - current_thread_info()->user_handle = L4_nilthread; - -+ /* Start linux main thread */ - L4_Start_SpIp(main_thread, -- (L4_Word_t) &main_stack[STACK_SIZE-1], -- (L4_Word_t) start_kernel); -- -- L4_Start_SpIp(intervm_thread, -- (L4_Word_t) &intervm_stack[STACK_SIZE-1], -- (L4_Word_t) intervm_loop); -+ (L4_Word_t) &main_stack[STACK_SIZE-1], -+ (L4_Word_t) start_main_thread); - -- /* Now we go and do the timer stuff */ -+ /* Now we go and do the timer/IRQ stuff */ - interrupt_loop(); -+} -+ -+int __init -+main(int argc, char **argv) -+{ -+ char* cmdline = genode_config_cmdline(); -+ -+ /* Do init stuff, especially get memory */ -+ __lib_init(); -+ -+ /* Copy kernel commandline from the XML config */ -+ if (cmdline) -+ memcpy(boot_command_line, cmdline, -+ min_t(size_t, COMMAND_LINE_SIZE, -+ sizeof(char)*strlen(cmdline))); -+ -+ /* Create timer thread, which provides so to say timer interrupts */ -+ thread_create(&timer_thread); -+ L4_KDB_SetThreadName(timer_thread, "L_timer"); -+ -+ timer_main_kernel_lock = genode_alloc_lock(); -+ -+ /* Start timer thread */ -+ L4_Start_SpIp(timer_thread, -+ (L4_Word_t) &timer_stack[STACK_SIZE-1], -+ (L4_Word_t) start_timer_thread); -+ -+ /* Now, work is done, lets go sleeping */ -+ genode_sleep_forever(); - return 0; - } -diff -urNpB kernel-2.6.23-v2/arch/l4/kernel/process.c contrib/arch/l4/kernel/process.c ---- kernel-2.6.23-v2/arch/l4/kernel/process.c -+++ contrib/arch/l4/kernel/process.c -@@ -23,6 +23,10 @@ - #include - #include - -+#include -+ -+extern void* timer_main_kernel_lock; -+ - extern void NORET_TYPE syscall_loop (void); - static void new_thread_handler(struct thread_info *prev); - static void new_process_handler(struct thread_info *prev); -@@ -144,7 +148,7 @@ __execve(char *file, char **argv, char * - void - cpu_idle (void) - { -- int r; -+ //int r; - - /* Idle thread has special user_tid so we can identify it */ - current_thread_info()->user_tid = L4_anythread; -@@ -152,25 +156,17 @@ cpu_idle (void) - - //printk("Starting idle loop (%lx)\n", L4_Myself().raw); XXX: L4_Myself() is no longer valid - nt - -- r = L4_Set_Priority(L4_myselfconst, 99); -- if (r == 0) { -- printk("Failed: %lx\n", L4_ErrorCode()); -- } -- assert(r != 0); -- - atomic_inc(&init_mm.mm_count); - current->mm = &init_mm; - current->active_mm = &init_mm; - - while (1) { -- while (!need_resched()) -- { -- /* Wait on timer_thread */ -- if (!need_resched()){ -- L4_Receive(timer_thread); -- } -+ while (!need_resched()){ -+ genode_unlock(timer_main_kernel_lock); -+ L4_Receive(timer_thread); -+ genode_lock(timer_main_kernel_lock); -+ - } -- //printk("!"); - schedule(); - } - } -@@ -187,7 +183,7 @@ copy_thread(int nr, unsigned long clone_ - unsigned long unused, struct task_struct * p, - struct pt_regs * regs) - { -- int r = 0; -+ //int r = 0; - //printk("%s %d %lx usp=%lx, task=%p, regs=%p\n", __func__, nr, clone_flags, usp, p, regs); - - if ((task_thread_info(p)->request.op != OP_KTHREAD) && -@@ -222,9 +218,6 @@ copy_thread(int nr, unsigned long clone_ - name); - } - -- r = L4_Set_Priority(task_thread_info(p)->user_tid, 98); -- assert(r != 0); -- - L4_Copy_regs (current_thread_info()->user_tid, - task_thread_info(p)->user_tid); - -@@ -308,7 +301,7 @@ start_thread(struct pt_regs * regs, unsi - //printk("%s current = %p pc = %lx, sp = %lx\n", __func__, current, pc, sp); - - if (current_thread_info()->user_tid.raw == L4_nilthread.raw) { -- int r; -+ //int r; - void * utcb; - - if (utcb_area.raw == L4_Nilpage.raw) -@@ -328,8 +321,7 @@ start_thread(struct pt_regs * regs, unsi - utcb, &handle); - - assert(thrd.raw != 0); -- r = L4_Set_Priority(thrd, 98); -- assert(r != 0); -+ - /* - * This will be disabled in libl4 with NDEBUG set, so it's OK. - */ -@@ -352,7 +344,8 @@ start_thread(struct pt_regs * regs, unsi - // syscall_exit(1); - - set_need_restart(current_thread_info(), pc, sp, 0); -- L4_AbortIpc_and_stop_Thread(thrd); -+ //L4_AbortIpc_and_stop_Thread(thrd); -+ L4_Stop(thrd); - set_user_ipc_cancelled(current_thread_info()); - - /* Setup which messages we will recieve */ -@@ -385,6 +378,29 @@ __switch_to(struct task_struct *prev, st - /* XXX - is the race from here to the switch important? */ - current_tinfo(smp_processor_id()) = (unsigned long)next_info; - -+ /* Suspend running thread */ -+ if(prev_info->user_tid.raw != 0) -+ { -+ L4_Word_t dummy; -+ L4_ThreadId_t dummy_id; -+ L4_ExchangeRegisters(prev_info->user_tid, -+ L4_ExReg_Halt + L4_ExReg_AbortSendIPC, -+ 0, 0, 0, -+ 0, L4_nilthread, &dummy, &dummy, &dummy, -+ &dummy, &dummy, &dummy_id); -+ } -+ -+ /* Resume scheduled thread */ -+ if(next_info->user_tid.raw != 0) -+ { -+ L4_Word_t dummy; -+ L4_ThreadId_t dummy_id; -+ L4_ExchangeRegisters(next_info->user_tid, -+ L4_ExReg_Resume, 0, 0, 0, -+ 0, L4_nilthread, &dummy, &dummy, &dummy, -+ &dummy, &dummy, &dummy_id); -+ } -+ - prev_info = arch_switch(prev_info, next_info); - mb(); - } else { -diff -urNpB kernel-2.6.23-v2/arch/l4/kernel/setup.c contrib/arch/l4/kernel/setup.c ---- kernel-2.6.23-v2/arch/l4/kernel/setup.c -+++ contrib/arch/l4/kernel/setup.c -@@ -14,8 +14,6 @@ - #include - #include - --#include --#include - #include - - #ifdef CONFIG_VT -@@ -25,7 +23,9 @@ - #include - #include - --#include -+#include -+#include -+ - unsigned long VGA_offset; - - struct screen_info screen_info = { -@@ -52,15 +52,16 @@ struct screen_info screen_info = { - .orig_video_points = 16 - }; - #define IORESOURCE_RAM (IORESOURCE_BUSY | IORESOURCE_MEM) --static struct resource video_ram_resource = { -- .name = "Video RAM area", -- .start = 0xa0000, -- .end 0xbffff, -- .flags = IORESOURCE_RAM --}; -+/* static struct resource video_ram_resource = { */ -+/* .name = "Video RAM area", */ -+/* .start = 0xa0000, */ -+/* .end = 0xbffff, */ -+/* .flags = IORESOURCE_RAM */ -+/* }; */ - - void __init __setup_vga(void) - { -+ /* - char* vga_obj; - envitem_t *ig_ref; - -@@ -72,7 +73,7 @@ void __init __setup_vga(void) - - VGA_offset = (unsigned long)vga_obj - video_ram_resource.start; - request_resource(&iomem_resource, &video_ram_resource); -- -+ */ - } - - -@@ -91,8 +92,6 @@ L4_Fpage_t utcb_area; - extern unsigned long start_phys_mem, end_phys_mem; - extern void * __rd_start, * __rd_end; - --extern void enable_early_printk(void); -- - void __init - setup_machine_name(void) - { -@@ -112,7 +111,7 @@ void __init - setup_arch (char **command_line) - { - unsigned long base, area; -- -+ char* initrd_name; - setup_tls(1); - - /* Return the command line to the rest of the kernel */ -@@ -156,19 +155,19 @@ setup_arch (char **command_line) - task_thread_info(current)->user_tid = L4_nilthread; - task_thread_info(current)->user_handle = L4_nilthread; - --#ifdef CONFIG_EARLY_PRINTK -- /* early console initialisation */ -- enable_early_printk(); --#endif -- - /* Ramdisk setup */ - #ifdef CONFIG_BLK_DEV_INITRD - /* Board specific code should have set up initrd_start and initrd_end */ - ROOT_DEV = Root_RAM0; - /* FIXME! */ -- initrd_start = 0; //naming_lookup("ramdisk"); -- initrd_end = 0; //naming_lookup("ramdisk_end"); -- printk("end: %lx\n", initrd_end); -+ initrd_name = genode_config_initrd(); -+ if(initrd_name) { -+ initrd_start = (unsigned long) genode_open(initrd_name, &initrd_end); -+ initrd_end = initrd_start + initrd_end; -+ } else { -+ initrd_start = 0; -+ initrd_end = 0; -+ } - initrd_below_start_ok = 1; - - if (initrd_start) { -diff -urNpB kernel-2.6.23-v2/arch/l4/kernel/sys_genode.c contrib/arch/l4/kernel/sys_genode.c ---- kernel-2.6.23-v2/arch/l4/kernel/sys_genode.c -+++ contrib/arch/l4/kernel/sys_genode.c -@@ -0,0 +1,427 @@ -+#define timer_t timer_t_linux -+ -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#undef timer_t -+ -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#ifdef CONFIG_SCREEN_GENODE -+#include -+#endif -+#ifdef CONFIG_NET_GENODE -+#include -+#endif -+#ifdef CONFIG_BLOCK_GENODE -+#include -+#endif -+ -+#include "assert.h" -+#include "irq_impl.h" -+ -+#include -+ -+ -+extern L4_ThreadId_t main_thread; -+extern L4_ThreadId_t timer_thread; -+extern L4_ThreadId_t timer_handle; -+ -+/* -+ * Mask is set (1) if enabled -+ */ -+L4_Word_t irq_mask = 1; // Keep bit 1 reserverd for timer as it is somewhat magic -+ -+#define NUM_CPUS 1 -+fd_set pending_irqs[NUM_CPUS]; -+#define PENDING_MASK_SIZE (NR_IRQS / 32) -+ -+/* -+ * Need SMP-safe access to interrupt CSRs -+ */ -+DEFINE_SPINLOCK(iguana_irq_lock); -+ -+static inline void -+iguana_enable_irq(unsigned int irq) -+{ -+ printk("WARN: %s does not work by now!\n",__func__); -+ spin_lock(&iguana_irq_lock); -+ if (irq < 32) { -+ irq_mask |= (1UL << 31); /* always use bit 31 for hw irqs */ -+ //L4_LoadMR(0, irq); -+ //L4_AcknowledgeInterruptOnBehalf(timer_thread, 0, 0); -+ } else { -+ irq_mask |= (1UL << irq); -+ } -+ spin_unlock(&iguana_irq_lock); -+} -+ -+static inline void -+iguana_disable_irq(unsigned int irq) -+{ -+ printk("WARN: %s does not work by now!\n",__func__); -+ spin_lock(&iguana_irq_lock); -+ if (irq < 32) { -+ //L4_LoadMR(0, irq); -+ //L4_UnregisterInterrupt(timer_thread, 0, 0); -+ } else { -+ irq_mask &= ~(1UL << irq); -+ } -+ -+ spin_unlock(&iguana_irq_lock); -+} -+ -+static unsigned int -+iguana_startup_irq(unsigned int irq) -+{ -+ if (irq < 32) -+ hardware_register_interrupt(timer_thread, irq); -+ -+ iguana_enable_irq(irq); -+ -+ return 0; -+} -+ -+static void -+iguana_ack_irq(unsigned int irq) -+{ -+} -+ -+static void -+iguana_end_irq(unsigned int irq) -+{ -+ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) -+ iguana_enable_irq(irq); -+} -+ -+static void -+iguana_cpu_set_irq_affinity(unsigned int irq, cpumask_t affinity) -+{ -+ // XXX fixme -+ printk("%s called\n", __func__); -+} -+ -+static void -+iguana_set_irq_affinity(unsigned int irq, cpumask_t affinity) -+{ -+ spin_lock(&iguana_irq_lock); -+ iguana_cpu_set_irq_affinity(irq, affinity); -+ spin_unlock(&iguana_irq_lock); -+} -+ -+static void __init -+init_iguana_irqs(struct hw_interrupt_type * ops, int imin, int imax) -+{ -+ long i; -+ for (i = imin; i <= imax; ++i) { -+ irq_desc[i].status = IRQ_DISABLED | IRQ_LEVEL; -+ irq_desc[i].chip = ops; -+ } -+} -+ -+static struct hw_interrupt_type iguana_irq_type = { -+ .typename = "iguana", -+ .startup = iguana_startup_irq, -+ .shutdown = iguana_disable_irq, -+ .enable = iguana_enable_irq, -+ .disable = iguana_disable_irq, -+ .ack = iguana_ack_irq, -+ .end = iguana_end_irq, -+ .set_affinity = iguana_set_irq_affinity, -+}; -+ -+static void __init -+iguana_init_irq(void) -+{ -+ init_iguana_irqs(&iguana_irq_type, 0, NR_IRQS-1); -+} -+ -+void __init -+init_IRQ(void) -+{ -+ iguana_init_irq(); -+} -+ -+/* -+ * Iguana Family -+ */ -+extern uintptr_t temp_cap_slot; -+extern uintptr_t temp_cap_used; -+extern uintptr_t temp_cap_size; -+extern uintptr_t temp_cap_addr; -+ -+extern void -+__cap_init(uintptr_t cap_slot, uintptr_t cap_used, uintptr_t cap_size, uintptr_t cap_addr); -+ -+memsection_ref_t vmalloc_memsect; -+uintptr_t vmalloc_start, vmalloc_end; -+ -+/* FIXME: Should be extended for SMP? */ -+struct ig_irq_rec{ -+ unsigned short pending; -+ L4_ThreadId_t tid; -+} irq_recs[NR_IRQS]; -+ -+static int __init -+iguana_arch_init(void) -+{ -+#if 0 -+ /* -+ * Enable the system error interrupts. These interrupts are -+ * all reported to the kernel as machine checks, so the handler -+ * is a nop so it can be called to count the individual events. -+ */ -+ -+ /* -+ FIXME: We need to setup capabilties library properly now! -+ Ideally we would do this really early, but this is about as early -+ as we get! -+ */ -+ __cap_init(temp_cap_slot, temp_cap_used, temp_cap_size, temp_cap_addr); -+#endif -+ -+ /* Just take physical memory end + 1MB hole as starting point */ -+ unsigned long offset = 10*1024*1024; -+ vmalloc_start = (uintptr_t) genode_malloc(10); -+ vmalloc_start += (2*offset-1) & ~(offset-1); -+ -+ /* Let the vmalloc area size be 100MB and hope it doesn't collude */ -+ vmalloc_end = vmalloc_start + 100*1024*1024; -+ genode_printf("vmalloc area: %lx - %lx\n", vmalloc_start, vmalloc_end); -+ -+ return 0; -+} -+ -+arch_initcall(iguana_arch_init); -+ -+extern void * __callback_buffer; -+ -+#define BIT(x) (1<<(x)) -+#define TIMER_IRQ 0 -+ -+/* -+ * XXX: namespace clash. :-( -+ */ -+#define device_create okl4_device_create -+ -+//#include -+//#include -+ -+//server_t timer_server; -+//device_t timer_dev; -+ -+/* -+ * XXX this needs 32/64bit cleanup + merge into irq.c -+ */ -+/* int mask_to_irq(L4_Word_t *mask) */ -+/* { */ -+/* int i; */ -+ -+/* for (i = 0 ; i < NR_IGUANA_IRQS; i++) */ -+/* { */ -+/* if (*mask & (1 << i)) */ -+/* { */ -+/* *mask &= ~(1 << i); */ -+ -+/* /\* Bit 31 is ia32 BSP's way of saying hardware interrupt *\/ */ -+/* if (i == 31) */ -+/* { */ -+/* /\* read hw irq number from utcb->platform_reserved[0] *\/ */ -+/* //printk("mask_to_irq: got hw irq %ld!\n", utcb_base_get()->platform_reserved[0]); */ -+/* return utcb_base_get()->platform_reserved[0]; */ -+/* } */ -+/* else */ -+/* { */ -+/* return IGUANA_IRQ(i); */ -+/* } */ -+/* } */ -+/* } */ -+/* return -1; */ -+/* } */ -+ -+int iguana_alloc_irq(void) -+{ -+ int i; -+ for (i = 0; i < NR_IGUANA_IRQS; i++){ -+ if (!(irq_mask & (1UL<> 8; */ -+/* else */ -+/* return 0; */ -+/* } */ -+ -+extern void* timer_main_kernel_lock; -+ -+void -+interrupt_loop(void) -+{ -+ struct thread_info * curinfo; -+ -+ /* timer interrupt */ -+ int irq = IGUANA_IRQ(0); -+ -+ /* Wait for wake up message from timer init */ -+ L4_Call(main_thread); -+ -+ printk("START CLOCK LOOP\n"); -+ -+ while(1) { -+ genode_sleep(10); -+ genode_lock(timer_main_kernel_lock); -+#ifdef CONFIG_SCREEN_GENODE -+ genode_input_handle_events(); -+#endif -+#ifdef CONFIG_NET_GENODE -+ genode_net_rx_receive(); -+#endif -+#ifdef CONFIG_BLOCK_GENODE -+ genode_block_collect_responses(); -+#endif -+ curinfo = current_thread_info(); -+ curinfo->regs.mode ++; -+ -+ if (irqs_disabled()) -+ { -+ FD_SET(irq, &pending_irqs[smp_processor_id()]); -+ -+ /* Keep time going for missed ticks */ -+ if (pending_tick) -+ jiffies_64++; -+ IRQ_pending(smp_processor_id()) ++; -+ pending_tick = 1; -+ curinfo->regs.mode --; -+ genode_unlock(timer_main_kernel_lock); -+ } -+ else -+ { -+ isr_irq_disable(); -+ // Should loop over all bits potentially set -+ handle_irq(irq, NULL); -+ isr_irq_enable(); -+ pending_tick = 0; -+ isr_irq_disable(); -+ irq_enter(); -+ do_timer(1); -+ update_process_times(user_mode(&curinfo->regs)); -+ irq_exit(); -+ isr_irq_enable(); -+ curinfo->regs.mode --; -+ -+ genode_unlock(timer_main_kernel_lock); -+ if (need_resched()) { -+ L4_MsgTag_t msgtag = L4_Niltag; -+ L4_Msg_t msg; -+ L4_Set_MsgMsgTag( &msg, msgtag ); -+ L4_MsgLoad( &msg ); -+ L4_Send_Nonblocking(main_thread); -+ } -+ } -+ } -+} -+ -+static int irq_mask_to_num(void){ -+ int i; -+ int cpu = smp_processor_id(); -+ unsigned int least_sig; -+ -+ for (i = 0; i <= PENDING_MASK_SIZE; ++i){ -+ least_sig = fls(pending_irqs[cpu].fds_bits[i]); -+ if (least_sig == 0) -+ continue; -+ else{ -+ int bit = (i * sizeof(long)*8) + least_sig; -+ FD_CLR(bit-1, &pending_irqs[smp_processor_id()]); -+ return (bit - 1); -+ } -+ } -+ return -1; -+} -+/* -+ * This is called to handle pending interrupts from the main linux thread. -+ * We thus need to send a deceiving IPC to the L4 interrupt thread to -+ * ACK the interrupt. This is called with interrupts disabled -+ */ -+void -+l4_handle_pending(void) -+{ -+ int irq_num; -+ struct thread_info * curinfo = current_thread_info(); -+ -+ curinfo->regs.mode ++; -+ -+ if (pending_tick) -+ { -+ irq_enter(); -+ do_timer(1); -+ update_process_times(user_mode(&curinfo->regs)); -+ -+ pending_tick = 0; -+ irq_exit(); -+ } -+ -+ /* -+ * XXX -+ * -+ * Concurrency problem: the FD_SET() and FD_CLR() are not -+ * atomic but can be accessed either in the timer thread -+ * context or the syscall loop thread context -+ * -+ * -gl -+ */ -+ while((irq_num = irq_mask_to_num()) != -1) -+ { -+ handle_irq(irq_num, NULL); -+ } -+ -+ IRQ_pending(smp_processor_id())--; -+ -+ curinfo->regs.mode --; -+} -+ -+void local_irq_enable(void) -+{ -+ if (unlikely(!in_interrupt() && IRQ_pending(smp_processor_id()))) -+ { -+ IRQ_state(smp_processor_id()) = 1; -+ l4_handle_pending(); -+ } -+ IRQ_state(smp_processor_id()) = 0; -+} -+ -+EXPORT_SYMBOL(local_irq_enable); -+ -diff -urNpB kernel-2.6.23-v2/arch/l4/kernel/syscall_loop.c contrib/arch/l4/kernel/syscall_loop.c ---- kernel-2.6.23-v2/arch/l4/kernel/syscall_loop.c -+++ contrib/arch/l4/kernel/syscall_loop.c -@@ -23,10 +23,8 @@ - #include - - #include --#include - #include --#include --#include -+#include - - #include - -@@ -36,6 +34,7 @@ - extern L4_ThreadId_t timer_thread; - extern L4_ThreadId_t timer_handle; - extern L4_ThreadId_t main_thread; -+extern void* timer_main_kernel_lock; - - extern unsigned long get_instr(unsigned long addr); - extern unsigned long set_instr(unsigned long addr, unsigned long value); -@@ -556,9 +555,12 @@ return_to_user: - if (likely(reply_user_ipc(curinfo))) { - curinfo->regs.mode = 0; - L4_MsgLoad(&curinfo->regs.msg); -+ genode_unlock(timer_main_kernel_lock); - /* Reply to caller and wait for next IPC */ - tag = L4_ReplyWait(curinfo->user_tid, - &from); -+ genode_lock(timer_main_kernel_lock); -+ - } else { - retry: - if (user_need_restart(curinfo)) { -@@ -570,10 +572,12 @@ retry: - } - curinfo->regs.mode = 0; - /* Open Wait */ -+ genode_unlock(timer_main_kernel_lock); - tag = L4_Wait (&from); -+ genode_lock(timer_main_kernel_lock); - } - -- if (likely(from.raw == curinfo->user_handle.raw)) -+ if (likely(from.raw == curinfo->user_tid.raw)) - { - curinfo->regs.mode = 1; - curinfo->tag = tag; -@@ -587,6 +591,7 @@ retry: - } - else - { -+#if 0 - /* XXX - * Here we have to lookup the linux thread from - * the L4 thread_id, which may be expensive. -@@ -595,9 +600,12 @@ retry: - * thread could use this to kill root processes. - */ - printk("illegal ipc from %lx, expected %lx\n", from.raw, -- curinfo->user_handle.raw); -+ curinfo->user_tid.raw); - L4_KDB_Enter("ill"); - goto retry; -+#endif -+ curinfo->regs.mode = 1; -+ curinfo->tag = tag; - } - } - -diff -urNpB kernel-2.6.23-v2/arch/l4/kernel/vmlinux.lds.S contrib/arch/l4/kernel/vmlinux.lds.S ---- kernel-2.6.23-v2/arch/l4/kernel/vmlinux.lds.S -+++ contrib/arch/l4/kernel/vmlinux.lds.S -@@ -22,6 +22,10 @@ SECTIONS - #endif - /* read-only */ - .text : { -+ /* begin of program image (link address) */ -+ _prog_img_beg = .; -+ -+ _stext = .; - _text = .; /* Text and read-only data */ - *(.text) - SCHED_TEXT -@@ -29,6 +33,18 @@ SECTIONS - *(.fixup) - *(.gnu.warning) - *(.note*) -+ -+ . = ALIGN(0x04); -+ -+ _ctors_start = .; -+ KEEP (*(.ctors)) -+ KEEP (*(SORT(.ctors.*))) -+ _ctors_end = .; -+ _dtors_start = .; -+ KEEP (*(SORT(.dtors.*))) -+ KEEP (*(.dtors)) -+ _dtors_end = .; -+ - . = ALIGN(PAGE_SIZE); - __user_exregs_page = .; - *(.exregspage) -@@ -42,11 +58,25 @@ SECTIONS - __ex_table : { *(__ex_table) } - __stop___ex_table = .; - -+ .eh_frame_hdr : { *(.eh_frame_hdr) } -+ - RODATA - -- . = ALIGN(PAGE_SIZE); -- -+ . = ALIGN(PAGE_SIZE); -+ -+ _prog_img_data = .; -+ - .data : { /* Data */ -+ /* Leave space for parent capability parameters at start of data -+ * section. The protection domain creator is reponsible for storing -+ * sane values here. -+ */ -+ _parent_cap = .; -+ LONG(0xffffffff); -+ LONG(0xffffffff); -+ LONG(0xffffffff); -+ LONG(0xffffffff); -+ - /* Align the initial ramdisk image (INITRD) on page boundaries. */ - /* - __rd_start = .; -@@ -59,7 +89,16 @@ SECTIONS - - CONSTRUCTORS - . = ALIGN(PAGE_SIZE); -- } -+ } : rw -+ -+ /* exception frames for C++ */ -+ .eh_frame : { -+ __eh_frame_start__ = .; -+ KEEP (*(.eh_frame)) -+ LONG(0) -+ } : rw -+ .gcc_except_table : { KEEP(*(.gcc_except_table)) } -+ .dynamic : { *(.dynamic) } - - . = ALIGN(32); - .data.cacheline_aligned : { *(.data.cacheline_aligned) } -@@ -122,9 +161,15 @@ SECTIONS - *(COMMON) - _end = .; - } -+ -+ /* end of program image -- must be after last section */ -+ _prog_img_end = .; -+ - } - - PHDRS - { - main_seg PT_LOAD; -+ ro PT_LOAD; -+ rw PT_LOAD; - } -diff -urNpB kernel-2.6.23-v2/arch/l4/lib/checksum.c contrib/arch/l4/lib/checksum.c ---- kernel-2.6.23-v2/arch/l4/lib/checksum.c -+++ contrib/arch/l4/lib/checksum.c -@@ -85,7 +85,7 @@ out: - /* - * computes a partial checksum, e.g. for TCP/UDP fragments - */ --__wsum csum_partial(const void *buff, int len, __wsum sum) -+asmlinkage __wsum csum_partial(const void *buff, int len, __wsum sum) - { - unsigned int result = do_csum(buff, len); - -diff -urNpB kernel-2.6.23-v2/arch/l4/mm/mmu.c contrib/arch/l4/mm/mmu.c ---- kernel-2.6.23-v2/arch/l4/mm/mmu.c -+++ contrib/arch/l4/mm/mmu.c -@@ -39,7 +39,6 @@ void activate_mm(struct mm_struct *old, - - if (old_thrd.raw != L4_nilthread.raw) - { -- int r; - thread_delete(old_thrd); - current_thread_info()->user_tid = - eas_create_thread(new->context.eas, -@@ -48,8 +47,6 @@ void activate_mm(struct mm_struct *old, - ¤t_thread_info()->user_handle); - - assert(current_thread_info()->user_tid.raw != 0); -- r = L4_Set_Priority(current_thread_info()->user_tid, 98); -- assert(r != 0); - - { - char name[16]; -diff -urNpB kernel-2.6.23-v2/arch/l4/sys-i386/Makefile contrib/arch/l4/sys-i386/Makefile ---- kernel-2.6.23-v2/arch/l4/sys-i386/Makefile -+++ contrib/arch/l4/sys-i386/Makefile -@@ -4,7 +4,7 @@ CFLAGS += -O2 - ELF_ARCH = i386 - ELF_FORMAT = elf32-i386 - --obj-y = crt0.o delay.o signal.o syscalls.o semaphore.o io.o sys_i386.o bitops.o user.o ptrace.o process.o ldt.o traps.o processor.o string.o dma.o memcpy.o strstr.o ioport.o -+obj-y = delay.o signal.o syscalls.o semaphore.o io.o sys_i386.o bitops.o user.o ptrace.o process.o ldt.o traps.o processor.o string.o dma.o memcpy.o strstr.o ioport.o - - ifdef CONFIG_IA32_VDSO_ENABLE - obj-y += sysenter.o vsyscall.o -@@ -42,7 +42,7 @@ targets += vsyscall-note.o vsyscall.lds - - # The DSO images are built using a special linker script. - quiet_cmd_syscall = SYSCALL $@ -- cmd_syscall = $(CC) -m elf_i386 -nostdlib $(SYSCFLAGS_$(@F)) \ -+ cmd_syscall = $(CC) -m32 -nostdlib $(SYSCFLAGS_$(@F)) \ - -Wl,-T,$(filter-out FORCE,$^) -o $@ - - export CPPFLAGS_vsyscall.lds += -P -C -U$(ARCH) -diff -urNpB kernel-2.6.23-v2/arch/l4/sys-i386/crt0.S contrib/arch/l4/sys-i386/crt0.S ---- kernel-2.6.23-v2/arch/l4/sys-i386/crt0.S -+++ contrib/arch/l4/sys-i386/crt0.S -@@ -31,13 +31,13 @@ - ********************************************************************/ - - .text -- .global _start -+ .global _wombat_start - _stext: - .global _stext - --_start: -+_wombat_start: - push $0xfee1dead /* dummy frame thingy */ - jmp __sys_entry /* off we go! */ - /*NOTREACHED*/ - -- .end _start -+ .end _wombat_start -diff -urNpB kernel-2.6.23-v2/arch/l4/sys-i386/dma.c contrib/arch/l4/sys-i386/dma.c ---- kernel-2.6.23-v2/arch/l4/sys-i386/dma.c -+++ contrib/arch/l4/sys-i386/dma.c -@@ -11,7 +11,7 @@ - #include - #include - #include --#include -+//#include - - /* Allocate consistent (uncached, or the appearance of it) memory */ - /* We ignore the flags at the moment */ -diff -urNpB kernel-2.6.23-v2/arch/l4/sys-i386/ioport.c contrib/arch/l4/sys-i386/ioport.c ---- kernel-2.6.23-v2/arch/l4/sys-i386/ioport.c -+++ contrib/arch/l4/sys-i386/ioport.c -@@ -13,8 +13,8 @@ - */ - asmlinkage long sys_iopl(unsigned long unused) - { -- unsigned long old = 0; /* XXX */ -- unsigned long level; /* in %ebx */ -+ //unsigned long old = 0; /* XXX */ -+ //unsigned long level; /* in %ebx */ - - printk("%s: not implemented.\n", __func__); - #if 0 -diff -urNpB kernel-2.6.23-v2/arch/l4/sys-i386/signal.c contrib/arch/l4/sys-i386/signal.c ---- kernel-2.6.23-v2/arch/l4/sys-i386/signal.c -+++ contrib/arch/l4/sys-i386/signal.c -@@ -189,8 +189,8 @@ restore_sigcontext(struct pt_regs *ptreg - - return err; - --badframe: -- return 1; -+//badframe: -+// return 1; - } - - asmlinkage int sys_sigreturn(struct pt_regs *regs) -diff -urNpB kernel-2.6.23-v2/arch/l4/sys-i386/syscalls.c contrib/arch/l4/sys-i386/syscalls.c ---- kernel-2.6.23-v2/arch/l4/sys-i386/syscalls.c -+++ contrib/arch/l4/sys-i386/syscalls.c -@@ -10,8 +10,8 @@ - - #define __NR_Linux 0 - --extern long sys_mmap2(unsigned long addr, unsigned long len, unsigned long prot, -- unsigned long flags, unsigned long fd, unsigned long pgoff); -+extern asmlinkage long sys_mmap2(unsigned long addr, unsigned long len, unsigned long prot, -+ unsigned long flags, unsigned long fd, unsigned long pgoff); - - - unsigned int l4_i386_abi_syscall_start = 0; -diff -urNpB kernel-2.6.23-v2/block/cfq-iosched.c contrib/block/cfq-iosched.c ---- kernel-2.6.23-v2/block/cfq-iosched.c -+++ contrib/block/cfq-iosched.c -@@ -1427,6 +1427,7 @@ cfq_async_queue_prio(struct cfq_data *cf - default: - BUG(); - } -+ return 0; - } - - static struct cfq_queue * -diff -urNpB kernel-2.6.23-v2/include/asm-l4/bug.h contrib/include/asm-l4/bug.h ---- kernel-2.6.23-v2/include/asm-l4/bug.h -+++ contrib/include/asm-l4/bug.h -@@ -4,12 +4,13 @@ - #ifndef __ASSEMBLY__ - - #include -+#include -+#include - - #define HAVE_ARCH_BUG - #define BUG() do { \ -- /*panic("kernel BUG at %s:%d!\n", __FILE__, __LINE__);*/ \ -- printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \ -- pd_delete(pd_myself()); \ -+ genode_printf("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \ -+ genode_exit(1); \ - } while (0) - - #define HAVE_ARCH_BUG_ON -@@ -23,6 +24,18 @@ - BUG(); \ - } while (0) - -+ -+#define HAVE_ARCH_WARN_ON -+#define WARN_ON(condition) ({ \ -+ int __ret_warn_on = !!(condition); \ -+ if (unlikely(__ret_warn_on)) { \ -+ genode_printf("WARNING: at %s:%d %s()\n", __FILE__, \ -+ __LINE__, __FUNCTION__); \ -+ } \ -+ unlikely(__ret_warn_on); \ -+}) -+ -+ - extern int foo; - - #include -diff -urNpB kernel-2.6.23-v2/include/asm-l4/i386/dma-mapping.h contrib/include/asm-l4/i386/dma-mapping.h ---- kernel-2.6.23-v2/include/asm-l4/i386/dma-mapping.h -+++ contrib/include/asm-l4/i386/dma-mapping.h -@@ -26,7 +26,7 @@ - #include - #include - #include --#include -+//#include - - - static inline int -diff -urNpB kernel-2.6.23-v2/include/asm-l4/i386/ptrace.h contrib/include/asm-l4/i386/ptrace.h ---- kernel-2.6.23-v2/include/asm-l4/i386/ptrace.h -+++ contrib/include/asm-l4/i386/ptrace.h -@@ -61,7 +61,7 @@ - #define PTRACE_GET_THREAD_AREA 25 - #define PTRACE_SET_THREAD_AREA 26 - --extern inline void -+static inline void - l4_arch_setup_restart(struct pt_regs *regs) - { - i386_put_eax(regs, i386_save(regs)); -@@ -70,7 +70,7 @@ l4_arch_setup_restart(struct pt_regs *re - - #define __NR_restart_syscall 0 - --extern inline void -+static inline void - l4_arch_setup_sys_restart(struct pt_regs *regs) - { - i386_put_eax(regs, __NR_restart_syscall); -diff -urNpB kernel-2.6.23-v2/include/asm-l4/i386/syscalls.h contrib/include/asm-l4/i386/syscalls.h ---- kernel-2.6.23-v2/include/asm-l4/i386/syscalls.h -+++ contrib/include/asm-l4/i386/syscalls.h -@@ -47,7 +47,7 @@ extern l4_i386_abi_syscalls_t l4_i386_ab - - #ifdef CONFIG_IA32_VDSO_ENABLE - --extern inline void -+static inline void - l4_i386_get_fast_syscall_args(struct pt_regs *regs) - { - /* TODO: ensure sp is valid */ -@@ -69,7 +69,7 @@ l4_i386_get_fast_syscall_args(struct pt_ - - #endif /* ifdef CONFIG_IA32_VDSO_ENABLE */ - --extern inline long -+static inline long - l4_arch_lookup_syscall (struct pt_regs *regs, L4_Word_t *abi) - { - L4_Word_t call; -@@ -98,7 +98,7 @@ typedef L4_Word_t func_four_arg_t(L4_Wor - typedef L4_Word_t func_five_arg_t(L4_Word_t, L4_Word_t, L4_Word_t, L4_Word_t, L4_Word_t); - typedef L4_Word_t func_six_arg_t(L4_Word_t, L4_Word_t, L4_Word_t, L4_Word_t, L4_Word_t, L4_Word_t); - --extern inline L4_Word_t -+static inline L4_Word_t - l4_arch_abi_call(struct pt_regs *regs, L4_Word_t sys_num, L4_Word_t abi) - { - L4_Word_t result = -1ul; -@@ -159,13 +159,13 @@ l4_arch_abi_call(struct pt_regs *regs, L - return result; - } - --extern inline long -+static inline long - l4_arch_get_error(struct pt_regs *regs) - { - return -(long)i386_eax(regs); - } - --extern inline int -+static inline int - l4_arch_restart_syscall(struct pt_regs *regs) - { - L4_Word_t temp; -diff -urNpB kernel-2.6.23-v2/include/asm-l4/signal_l4.h contrib/include/asm-l4/signal_l4.h ---- kernel-2.6.23-v2/include/asm-l4/signal_l4.h -+++ contrib/include/asm-l4/signal_l4.h -@@ -22,7 +22,7 @@ static inline int need_to_resched(struct - - extern void do_syscall_trace(int, void *); - --extern inline void -+static inline void - syscall_entry(struct thread_info * my_info) - { - if (unlikely(need_syscall_trace(my_info))) -@@ -34,7 +34,7 @@ syscall_entry(struct thread_info * my_in - } - } - --extern inline void -+static inline void - syscall_exit(struct thread_info * my_info, int syscall) - { - if (unlikely( syscall && need_syscall_trace(my_info) )) -@@ -46,7 +46,7 @@ syscall_exit(struct thread_info * my_inf - } - } - --extern inline int -+static inline int - l4_work_pending(struct thread_info * my_info, int syscall, struct pt_regs *regs) - { - if (unlikely( need_to_resched(my_info) )) { -@@ -59,7 +59,7 @@ l4_work_pending(struct thread_info * my_ - return 0; - } - --extern inline int -+static inline int - l4_work_pending_preempt(struct pt_regs *regs) - { - int restart = 0; -diff -urNpB kernel-2.6.23-v2/include/asm-l4/system.h contrib/include/asm-l4/system.h ---- kernel-2.6.23-v2/include/asm-l4/system.h -+++ contrib/include/asm-l4/system.h -@@ -8,7 +8,7 @@ - #include - - struct task_struct; --extern asmlinkage void *__switch_to(struct task_struct *prev, struct task_struct *next); -+extern void *__switch_to(struct task_struct *prev, struct task_struct *next); - - #include - #include -diff -urNpB kernel-2.6.23-v2/include/asm-l4/uaccess.h contrib/include/asm-l4/uaccess.h ---- kernel-2.6.23-v2/include/asm-l4/uaccess.h -+++ contrib/include/asm-l4/uaccess.h -@@ -101,7 +101,7 @@ static inline int verify_area(int type, - return 0; - } - --#define access_ok(type,addr,size) 1 -+#define access_ok(type,addr,size) (((unsigned long)addr != size) || 1) - - #endif - -diff -urNpB kernel-2.6.23-v2/include/asm-l4/unistd.h contrib/include/asm-l4/unistd.h ---- kernel-2.6.23-v2/include/asm-l4/unistd.h -+++ contrib/include/asm-l4/unistd.h -@@ -30,19 +30,19 @@ - - asmlinkage long sys_wait4(pid_t pid, int __user *stat_addr, - int options, struct rusage __user *ru); --extern long sys_open(const char *filename, int flags, int mode); --extern long sys_dup(unsigned int fildes); --extern long sys_close(unsigned int fd); -+extern asmlinkage long sys_open(const char *filename, int flags, int mode); -+extern asmlinkage long sys_dup(unsigned int fildes); -+extern asmlinkage long sys_close(unsigned int fd); - extern int __execve(char *file, char **argv, char **env); - extern int sys_execve(char *file, char **argv, char **env); --extern long sys_setsid(void); --extern long sys_mount(char *dev_name, char *dir_name, char *type, -+extern asmlinkage long sys_setsid(void); -+extern asmlinkage long sys_mount(char *dev_name, char *dir_name, char *type, - unsigned long flags, void *data); --extern long sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, -+extern asmlinkage long sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, - struct timeval *tvp); --extern off_t sys_lseek(unsigned int fd, off_t offset, unsigned int origin); --extern ssize_t sys_read(unsigned int fd, char __user *buf, size_t count); --extern ssize_t sys_write(unsigned int fd, const char __user *buf, size_t count); -+extern asmlinkage off_t sys_lseek(unsigned int fd, off_t offset, unsigned int origin); -+extern asmlinkage ssize_t sys_read(unsigned int fd, char __user *buf, size_t count); -+extern asmlinkage ssize_t sys_write(unsigned int fd, const char __user *buf, size_t count); - - #define KERNEL_CALL(ret_t, sys, args...) \ - mm_segment_t fs = get_fs(); \ -diff -urNpB kernel-2.6.23-v2/kernel/mutex.c contrib/kernel/mutex.c ---- kernel-2.6.23-v2/kernel/mutex.c -+++ contrib/kernel/mutex.c -@@ -57,7 +57,7 @@ EXPORT_SYMBOL(__mutex_init); - * We also put the fastpath first in the kernel image, to make sure the - * branch is predicted by the CPU as default-untaken. - */ --static void fastcall noinline __sched -+void fastcall noinline __sched - __mutex_lock_slowpath(atomic_t *lock_count); - - /*** -@@ -93,7 +93,7 @@ void inline fastcall __sched mutex_lock( - - EXPORT_SYMBOL(mutex_lock); - --static void fastcall noinline __sched -+void fastcall noinline __sched - __mutex_unlock_slowpath(atomic_t *lock_count); - - /*** -@@ -197,7 +197,7 @@ done: - return 0; - } - --static void fastcall noinline __sched -+void fastcall noinline __sched - __mutex_lock_slowpath(atomic_t *lock_count) - { - struct mutex *lock = container_of(lock_count, struct mutex, count); -@@ -265,7 +265,7 @@ __mutex_unlock_common_slowpath(atomic_t - /* - * Release the lock, slowpath: - */ --static fastcall noinline void -+fastcall noinline void - __mutex_unlock_slowpath(atomic_t *lock_count) - { - __mutex_unlock_common_slowpath(lock_count, 1); -diff -urNpB kernel-2.6.23-v2/mm/slab.c contrib/mm/slab.c ---- kernel-2.6.23-v2/mm/slab.c -+++ contrib/mm/slab.c -@@ -3758,6 +3758,7 @@ void kfree(const void *objp) - { - struct kmem_cache *c; - unsigned long flags; -+ void *ptr = (void*)objp; - - if (unlikely(ZERO_OR_NULL_PTR(objp))) - return; -@@ -3765,7 +3766,7 @@ void kfree(const void *objp) - kfree_debugcheck(objp); - c = virt_to_cache(objp); - debug_check_no_locks_freed(objp, obj_size(c)); -- __cache_free(c, (void *)objp); -+ __cache_free(c, /*(void *)objp*/ptr); - local_irq_restore(flags); - } - EXPORT_SYMBOL(kfree); -diff -urNpB kernel-2.6.23-v2/scripts/checksyscalls.sh contrib/scripts/checksyscalls.sh ---- kernel-2.6.23-v2/scripts/checksyscalls.sh -+++ contrib/scripts/checksyscalls.sh -@@ -119,5 +119,9 @@ sed -n -e '/^\#define/ { s/[^_]*__NR_\([ - \#endif/p }' $1 - } - -+echo "SRCTREE" -+echo "${srctree}" -+echo "END" -+ - (ignore_list && syscall_list ${srctree}/include/asm-i386/unistd.h) | \ - $* -E -x c - > /dev/null diff --git a/ports-okl4/patches/unionfs.patch b/ports-okl4/patches/unionfs.patch deleted file mode 100644 index eacf13549..000000000 --- a/ports-okl4/patches/unionfs.patch +++ /dev/null @@ -1,11419 +0,0 @@ -diff --git a/Documentation/filesystems/00-INDEX b/Documentation/filesystems/00-INDEX -index 59db1bc..40816d4 100644 ---- a/Documentation/filesystems/00-INDEX -+++ b/Documentation/filesystems/00-INDEX -@@ -86,6 +86,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 . -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 . -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 , 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 -+ -+# mount -t unionfs -o remount,del=/new,mode=/foo=rw none MOUNTPOINT -+ -+# 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 . -diff --git a/MAINTAINERS b/MAINTAINERS -index 9a91d9e..87e1e63 100644 ---- a/MAINTAINERS -+++ b/MAINTAINERS -@@ -3740,6 +3740,14 @@ L: linux-kernel@vger.kernel.org - W: http://www.kernel.dk - S: Maintained - -+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 -+ - USB ACM DRIVER - P: Oliver Neukum - M: oliver@neukum.name -diff --git a/fs/Kconfig b/fs/Kconfig -index f9eed6d..9afb8df 100644 ---- a/fs/Kconfig -+++ b/fs/Kconfig -@@ -1027,6 +1027,47 @@ config CONFIGFS_FS - - endmenu - -+menu "Layered filesystems" -+ -+config ECRYPT_FS -+ tristate "eCrypt filesystem layer support (EXPERIMENTAL)" -+ depends on EXPERIMENTAL && KEYS && CRYPTO && NET -+ help -+ Encrypted filesystem that operates on the VFS layer. See -+ to learn more about -+ eCryptfs. Userspace components are required and can be -+ obtained from . -+ -+ To compile this file system support as a module, choose M here: the -+ module will be called ecryptfs. -+ -+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 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. -+ -+endmenu -+ - menu "Miscellaneous filesystems" - - config ADFS_FS -@@ -1079,18 +1120,6 @@ config AFFS_FS - To compile this file system support as a module, choose M here: the - module will be called affs. If unsure, say N. - --config ECRYPT_FS -- tristate "eCrypt filesystem layer support (EXPERIMENTAL)" -- depends on EXPERIMENTAL && KEYS && CRYPTO && NET -- help -- Encrypted filesystem that operates on the VFS layer. See -- to learn more about -- eCryptfs. Userspace components are required and can be -- obtained from . -- -- To compile this file system support as a module, choose M here: the -- module will be called ecryptfs. -- - config HFS_FS - tristate "Apple Macintosh file system support (EXPERIMENTAL)" - depends on BLOCK && EXPERIMENTAL -diff --git a/fs/Makefile b/fs/Makefile -index 720c29d..f1301f3 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/ecryptfs/dentry.c b/fs/ecryptfs/dentry.c -index cb20b96..a8c1686 100644 ---- a/fs/ecryptfs/dentry.c -+++ b/fs/ecryptfs/dentry.c -@@ -62,7 +62,7 @@ static int ecryptfs_d_revalidate(struct dentry *dentry, struct nameidata *nd) - struct inode *lower_inode = - ecryptfs_inode_to_lower(dentry->d_inode); - -- fsstack_copy_attr_all(dentry->d_inode, lower_inode, NULL); -+ fsstack_copy_attr_all(dentry->d_inode, lower_inode); - } - out: - return rc; -diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c -index 131954b..fc4c6cb 100644 ---- a/fs/ecryptfs/inode.c -+++ b/fs/ecryptfs/inode.c -@@ -601,9 +601,9 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry, - lower_new_dir_dentry->d_inode, lower_new_dentry); - if (rc) - goto out_lock; -- fsstack_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode, NULL); -+ fsstack_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode); - if (new_dir != old_dir) -- fsstack_copy_attr_all(old_dir, lower_old_dir_dentry->d_inode, NULL); -+ fsstack_copy_attr_all(old_dir, lower_old_dir_dentry->d_inode); - out_lock: - unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); - dput(lower_new_dentry->d_parent); -@@ -961,7 +961,7 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia) - } - rc = notify_change(lower_dentry, ia); - out: -- fsstack_copy_attr_all(inode, lower_inode, NULL); -+ fsstack_copy_attr_all(inode, lower_inode); - return rc; - } - -diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c -index a984972..cb349a4 100644 ---- a/fs/ecryptfs/main.c -+++ b/fs/ecryptfs/main.c -@@ -151,7 +151,7 @@ int ecryptfs_interpose(struct dentry *lower_dentry, struct dentry *dentry, - d_add(dentry, inode); - else - d_instantiate(dentry, inode); -- fsstack_copy_attr_all(inode, lower_inode, NULL); -+ fsstack_copy_attr_all(inode, lower_inode); - /* This size will be overwritten for real files w/ headers and - * other metadata */ - fsstack_copy_inode_size(inode, lower_inode); -diff --git a/fs/namei.c b/fs/namei.c -index 314afe6..c15ce85 100644 ---- a/fs/namei.c -+++ b/fs/namei.c -@@ -374,6 +374,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 1a9c0e6..f931be4 100644 ---- a/fs/splice.c -+++ b/fs/splice.c -@@ -943,8 +943,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) - { - int ret; - -@@ -964,13 +964,14 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, - - return out->f_op->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) - { - int ret; - -@@ -990,6 +991,7 @@ static long do_splice_to(struct file *in, loff_t *ppos, - - return in->f_op->splice_read(in, ppos, pipe, len, flags); - } -+EXPORT_SYMBOL_GPL(vfs_splice_to); - - /** - * splice_direct_to_actor - splices data directly between two non-pipes -@@ -1059,7 +1061,7 @@ ssize_t splice_direct_to_actor(struct file *in, struct splice_desc *sd, - size_t read_len; - loff_t pos = sd->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; - -@@ -1117,7 +1119,7 @@ static int direct_splice_actor(struct pipe_inode_info *pipe, - { - struct file *file = sd->u.file; - -- return do_splice_from(pipe, file, &sd->pos, sd->total_len, sd->flags); -+ return vfs_splice_from(pipe, file, &sd->pos, sd->total_len, sd->flags); - } - - /** -@@ -1191,7 +1193,7 @@ static long do_splice(struct file *in, loff_t __user *off_in, - } else - off = &out->f_pos; - -- ret = do_splice_from(pipe, out, off, len, flags); -+ ret = vfs_splice_from(pipe, out, off, len, flags); - - if (off_out && copy_to_user(off_out, off, sizeof(loff_t))) - ret = -EFAULT; -@@ -1212,7 +1214,7 @@ static long do_splice(struct file *in, loff_t __user *off_in, - } else - off = &in->f_pos; - -- ret = do_splice_to(in, off, pipe, len, flags); -+ ret = vfs_splice_to(in, off, pipe, 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 67716f6..cc1443d 100644 ---- a/fs/stack.c -+++ b/fs/stack.c -@@ -1,24 +1,82 @@ -+/* -+ * 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 - #include - #include - --/* 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 - */ --void fsstack_copy_inode_size(struct inode *dst, const struct inode *src) -+void fsstack_copy_inode_size(struct inode *dst, struct inode *src) - { -- i_size_write(dst, i_size_read((struct inode *)src)); -- dst->i_blocks = src->i_blocks; -+ loff_t i_size; -+ blkcnt_t i_blocks; -+ -+ /* -+ * i_size_read() includes its own seqlocking and protection from -+ * preemption (see include/linux/fs.h): we need nothing extra for -+ * that here, and prefer to avoid nesting locks than attempt to -+ * keep i_size and i_blocks in synch together. -+ */ -+ i_size = i_size_read(src); -+ -+ /* -+ * But if CONFIG_LSF (on 32-bit), we ought to make an effort to keep -+ * the two halves of i_blocks in synch despite SMP or PREEMPT - though -+ * stat's generic_fillattr() doesn't bother, and we won't be applying -+ * quotas (where i_blocks does become important) at the upper level. -+ * -+ * We don't actually know what locking is used at the lower level; but -+ * if it's a filesystem that supports quotas, it will be using i_lock -+ * as in inode_add_bytes(). tmpfs uses other locking, and its 32-bit -+ * is (just) able to exceed 2TB i_size with the aid of holes; but its -+ * i_blocks cannot carry into the upper long without almost 2TB swap - -+ * let's ignore that case. -+ */ -+ if (sizeof(i_blocks) > sizeof(long)) -+ spin_lock(&src->i_lock); -+ i_blocks = src->i_blocks; -+ if (sizeof(i_blocks) > sizeof(long)) -+ spin_unlock(&src->i_lock); -+ -+ /* -+ * If CONFIG_SMP on 32-bit, it's vital for fsstack_copy_inode_size() -+ * to hold some lock around i_size_write(), otherwise i_size_read() -+ * may spin forever (see include/linux/fs.h). We don't necessarily -+ * hold i_mutex when this is called, so take i_lock for that case. -+ * -+ * And if CONFIG_LSF (on 32-bit), continue our effort to keep the -+ * two halves of i_blocks in synch despite SMP or PREEMPT: use i_lock -+ * for that case too, and do both at once by combining the tests. -+ * -+ * There is none of this locking overhead in the 64-bit case. -+ */ -+ if (sizeof(i_size) > sizeof(long) || sizeof(i_blocks) > sizeof(long)) -+ spin_lock(&dst->i_lock); -+ i_size_write(dst, i_size); -+ dst->i_blocks = i_blocks; -+ if (sizeof(i_size) > sizeof(long) || sizeof(i_blocks) > sizeof(long)) -+ spin_unlock(&dst->i_lock); - } - EXPORT_SYMBOL_GPL(fsstack_copy_inode_size); - --/* copy all attributes; get_nlinks is optional way to override the i_nlink -+/* -+ * copy all attributes; get_nlinks is optional way to override the i_nlink - * copying - */ --void fsstack_copy_attr_all(struct inode *dest, const struct inode *src, -- int (*get_nlinks)(struct inode *)) -+void fsstack_copy_attr_all(struct inode *dest, const struct inode *src) - { - dest->i_mode = src->i_mode; - dest->i_uid = src->i_uid; -@@ -29,14 +87,6 @@ void fsstack_copy_attr_all(struct inode *dest, const struct inode *src, - dest->i_ctime = src->i_ctime; - 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. -- */ -- if (!get_nlinks) -- dest->i_nlink = src->i_nlink; -- else -- dest->i_nlink = (*get_nlinks)(dest); -+ dest->i_nlink = src->i_nlink; - } - EXPORT_SYMBOL_GPL(fsstack_copy_attr_all); -diff --git a/fs/unionfs/Makefile b/fs/unionfs/Makefile -new file mode 100644 -index 0000000..bbc102d ---- /dev/null -+++ b/fs/unionfs/Makefile -@@ -0,0 +1,17 @@ -+UNIONFS_VERSION="2.5.4 (for 2.6.23.17)" -+ -+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..be2fa34 ---- /dev/null -+++ b/fs/unionfs/commonfops.c -@@ -0,0 +1,879 @@ -+/* -+ * 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' - 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_one_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); -+ 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); -+ 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); -+ 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); -+ 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; -+ -+ 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); -+ 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..297e4ad ---- /dev/null -+++ b/fs/unionfs/copyup.c -@@ -0,0 +1,889 @@ -+/* -+ * 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)) { -+ cap_raise(current->cap_effective, CAP_FOWNER); -+ err = vfs_setxattr(new_lower_dentry, name_list, -+ attr_value, size, 0); -+ cap_lower(current->cap_effective, CAP_FOWNER); -+ } -+ 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; -+ args.symlink.mode = old_mode; -+ -+ 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); -+ 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); -+ 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, -+ new_lower_dentry, 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_one_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_one_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..c8f3e62 ---- /dev/null -+++ b/fs/unionfs/debug.c -@@ -0,0 +1,537 @@ -+/* -+ * 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) -+ */ -+ -+#ifndef KERN_CONT -+# define KERN_CONT "" -+#endif /* not KERN_CONT */ -+ -+/* 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..e9343fd ---- /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); -+ 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..4ec537e ---- /dev/null -+++ b/fs/unionfs/file.c -@@ -0,0 +1,364 @@ -+/* -+ * 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; -+ struct vm_operations_struct *saved_vm_ops = NULL; -+ -+ 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); -+ return err; -+} -+ -+int unionfs_fsync(struct file *file, struct dentry *dentry, int datasync) -+{ -+ int bindex, bstart, bend; -+ struct file *lower_file; -+ 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, -+ lower_dentry, -+ 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..0ece7bc ---- /dev/null -+++ b/fs/unionfs/inode.c -@@ -0,0 +1,1050 @@ -+/* -+ * 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, mode); -+ 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, -+ struct nameidata *nd) -+{ -+ 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, nd); -+ 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), -+ nd); -+} -+ -+/* -+ * 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 nameidata *nd) -+{ -+ 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); -+ -+ if (nd) -+ unionfs_lock_dentry(nd->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, nd); -+ 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); -+ unionfs_check_nd(nd); -+ if (nd) -+ unionfs_unlock_dentry(nd->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; -+ -+ 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..020ea83 ---- /dev/null -+++ b/fs/unionfs/lookup.c -@@ -0,0 +1,572 @@ -+/* -+ * 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 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.dentry; -+ if (new_mnt) -+ *new_mnt = lower_nd.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_one_len(name, base, strlen(name)); -+ if (new_mnt) -+ *new_mnt = mntget(lower_nd.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; -+ case LOOKUP_ACCESS: -+ nd->flags = flags; -+ 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_one_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..875c56f ---- /dev/null -+++ b/fs/unionfs/main.c -@@ -0,0 +1,778 @@ -+/* -+ * 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 -+#include -+ -+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 iget. -+ * iget will call our read_inode 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 = iget(sb, iunique(sb, UNIONFS_ROOT_INO)); -+ if (!inode) { -+ err = -EACCES; -+ 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->dentry->d_sb->s_type->name, UNIONFS_NAME)) -+ return -EINVAL; -+ if (!nd->dentry->d_inode) -+ return -ENOENT; -+ if (!S_ISDIR(nd->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_release(&nd); -+ goto out; -+ } -+ -+ lower_root_info->lower_paths[bindex].dentry = nd.dentry; -+ lower_root_info->lower_paths[bindex].mnt = nd.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++) -+ if (lower_root_info->lower_paths[i].dentry) { -+ dput(lower_root_info->lower_paths[i].dentry); -+ /* initialize: can't use unionfs_mntput here */ -+ mntput(lower_root_info->lower_paths[i].mnt); -+ } -+ -+ 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++) { -+ struct dentry *d; -+ struct vfsmount *m; -+ -+ d = lower_root_info->lower_paths[bindex].dentry; -+ m = lower_root_info->lower_paths[bindex].mnt; -+ -+ dput(d); -+ /* initializing: can't use unionfs_mntput here */ -+ mntput(m); -+ } -+ } -+ -+ 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; -+ struct vfsmount *m; -+ -+ d = lower_root_info->lower_paths[bindex].dentry; -+ m = lower_root_info->lower_paths[bindex].mnt; -+ -+ dput(d); -+ /* initializing: can't use unionfs_mntput here */ -+ mntput(m); -+ /* drop refs we took earlier */ -+ atomic_dec(&d->d_sb->s_active); -+ } -+ 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..eb6b40a ---- /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; -+ 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..88d07e4 ---- /dev/null -+++ b/fs/unionfs/rename.c -@@ -0,0 +1,520 @@ -+/* -+ * 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); -+ -+ /* see Documentation/filesystems/unionfs/issues.txt */ -+ lockdep_off(); -+ 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); -+ lockdep_on(); -+ -+ 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..64d0cc0 ---- /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, s->mode); -+ 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..9cf560d ---- /dev/null -+++ b/fs/unionfs/sioq.h -@@ -0,0 +1,92 @@ -+/* -+ * 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; -+ umode_t mode; -+}; -+ -+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..1312a49 ---- /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; -+ -+static void unionfs_read_inode(struct inode *inode) -+{ -+ int size; -+ struct unionfs_inode_info *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"); -+ BUG(); -+ } -+ -+ 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; -+ -+} -+ -+/* -+ * 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.mnt == new_lower_paths[idx].mnt && -+ nd.dentry == new_lower_paths[idx].dentry) -+ break; -+ path_release(&nd); /* 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.mnt == new_lower_paths[idx].mnt && -+ nd.dentry == new_lower_paths[idx].dentry) -+ break; -+ path_release(&nd); /* 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. -+ */ -+ pathput(&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.mnt == new_lower_paths[idx].mnt && -+ nd.dentry == new_lower_paths[idx].dentry) -+ break; -+ path_release(&nd); /* 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_release(&nd); -+ 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.dentry; -+ new_lower_paths[idx].mnt = nd.mnt; -+ -+ new_data[idx].sb = nd.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++) -+ pathget(&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++) -+ pathput(&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 *v, struct kmem_cache *cachep, unsigned long flags) -+{ -+ struct unionfs_inode_info *i = v; -+ -+ 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, int sync) -+{ -+ 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 vfsmount *mnt, int flags) -+{ -+ struct super_block *sb, *lower_sb; -+ struct vfsmount *lower_mnt; -+ int bindex, bstart, bend; -+ -+ if (!(flags & MNT_FORCE)) -+ /* -+ * we are not being MNT_FORCE'd, therefore we should emulate -+ * old behavior -+ */ -+ return; -+ -+ sb = mnt->mnt_sb; -+ -+ unionfs_read_lock(sb, UNIONFS_SMUTEX_CHILD); -+ -+ bstart = sbstart(sb); -+ bend = sbend(sb); -+ for (bindex = bstart; bindex <= bend; bindex++) { -+ lower_mnt = unionfs_lower_mnt_idx(sb->s_root, bindex); -+ lower_sb = unionfs_lower_super_idx(sb, bindex); -+ -+ if (lower_mnt && lower_sb && lower_sb->s_op && -+ lower_sb->s_op->umount_begin) -+ lower_sb->s_op->umount_begin(lower_mnt, flags); -+ } -+ -+ 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++) { -+ path = d_path(unionfs_lower_dentry_idx(sb->s_root, bindex), -+ unionfs_lower_mnt_idx(sb->s_root, bindex), -+ 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 = { -+ .read_inode = unionfs_read_inode, -+ .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..f6d19ed ---- /dev/null -+++ b/fs/unionfs/union.h -@@ -0,0 +1,662 @@ -+/* -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#include -+ -+/* 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 */ -+ -+#ifndef noinline_for_stack -+# define noinline_for_stack noinline -+#endif /* not noinline_for_stack */ -+ -+/* 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 */ -+ 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, struct dentry *dentry, -+ int datasync); -+extern int unionfs_fasync(int fd, struct file *file, int flag); -+ -+/* Inode operations */ -+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); -+} -+ -+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..2edb6ca ---- /dev/null -+++ b/fs/unionfs/unlink.c -@@ -0,0 +1,282 @@ -+/* -+ * 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) { -+ /* see Documentation/filesystems/unionfs/issues.txt */ -+ lockdep_off(); -+ err = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry); -+ lockdep_on(); -+ } -+ 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..fa5ab93 ---- /dev/null -+++ b/fs/unionfs/whiteout.c -@@ -0,0 +1,577 @@ -+/* -+ * 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_one_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 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_one_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->fs->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_one_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 (!permission(lower_dir, MAY_WRITE | MAY_EXEC, NULL)) { -+ 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 (!permission(lower_inode, MAY_EXEC, NULL)) { -+ 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; -+ kernel_cap_t orig_cap; -+ -+ /* -+ * 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). -+ */ -+ orig_cap = current->cap_effective; -+ cap_raise(current->cap_effective, CAP_DAC_READ_SEARCH); -+ cap_raise(current->cap_effective, CAP_DAC_OVERRIDE); -+ -+ 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); -+ current->cap_effective = orig_cap; -+ 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 bb516ce..64f1ced 100644 ---- a/include/linux/fs_stack.h -+++ b/include/linux/fs_stack.h -@@ -1,17 +1,27 @@ -+/* -+ * 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. - */ - - #include - - /* externs for fs/stack.c */ --extern void fsstack_copy_attr_all(struct inode *dest, const struct inode *src, -- int (*get_nlinks)(struct inode *)); -- --extern void fsstack_copy_inode_size(struct inode *dst, const struct inode *src); -+extern void fsstack_copy_attr_all(struct inode *dest, const struct inode *src); -+extern void fsstack_copy_inode_size(struct inode *dst, struct inode *src); - - /* inlines */ - static inline void fsstack_copy_attr_atime(struct inode *dest, -diff --git a/include/linux/magic.h b/include/linux/magic.h -index 36cc20d..8dac50b 100644 ---- a/include/linux/magic.h -+++ b/include/linux/magic.h -@@ -35,6 +35,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 - -diff --git a/include/linux/namei.h b/include/linux/namei.h -index 6c38efb..e9477b5 100644 ---- a/include/linux/namei.h -+++ b/include/linux/namei.h -@@ -3,6 +3,7 @@ - - #include - #include -+#include - - struct vfsmount; - -@@ -100,4 +101,16 @@ static inline char *nd_get_link(struct nameidata *nd) - return nd->saved_names[nd->depth]; - } - -+static inline void pathget(struct path *path) -+{ -+ mntget(path->mnt); -+ dget(path->dentry); -+} -+ -+static inline void pathput(struct path *path) -+{ -+ dput(path->dentry); -+ mntput(path->mnt); -+} -+ - #endif /* _LINUX_NAMEI_H */ -diff --git a/include/linux/splice.h b/include/linux/splice.h -index 33e447f..616d473 100644 ---- a/include/linux/splice.h -+++ b/include/linux/splice.h -@@ -69,5 +69,10 @@ 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); - - #endif -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/ports-okl4/run/lx_block.run b/ports-okl4/run/lx_block.run deleted file mode 100644 index d256563e5..000000000 --- a/ports-okl4/run/lx_block.run +++ /dev/null @@ -1,104 +0,0 @@ -# -# Build -# - -if {[have_spec okl4] == 0} { - puts "Runs on OKL4 only" - exit 0 -} - -# generic components -set build_components { - core - init - drivers/timer - drivers/pci - drivers/framebuffer/vesa - drivers/input/ps2 - server/rom_loopdev - oklinux -} - -build $build_components -create_boot_directory - - -# -# Config -# - -set config { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -} - -install_config $config - - -# -# Boot modules -# - -set boot_modules { } - -# basic binaries -lappend boot_modules "core init" - -# drivers -lappend boot_modules "pci_drv fb_drv ps2_drv timer rom_loopdev" - -# oklinux -lappend boot_modules "vmlinux initrd.gz tinycore.img" - -build_boot_image [join $boot_modules " "] - -append qemu_args " -m 512 " - -run_genode_until forever diff --git a/ports-okl4/src/app/xev_track/bounding_box.h b/ports-okl4/src/app/xev_track/bounding_box.h deleted file mode 100644 index 5e77b3d20..000000000 --- a/ports-okl4/src/app/xev_track/bounding_box.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * \brief Bounding box collects different refresh ops and combines them. - * \author Norman Feske - * \date 2009-11-03 - * - * taken from Xvfb display application for Nitpicker - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#ifndef _APP__XEV_TRACK__BOUNDING_BOX_H_ -#define _APP__XEV_TRACK__BOUNDING_BOX_H_ - -/* Genode includes */ -#include - -class Bounding_box -{ - private: - - int _x1, _y1, _x2, _y2; - - public: - - /** - * Constructor - */ - Bounding_box() { reset(); } - - bool valid() { return _x1 <= _x2 && _y1 <= _y2; } - - void reset() { _x1 = 0, _y1 = 0, _x2 = -1, _y2 = -1; } - - void extend(int x, int y, int w, int h) - { - int nx2 = x + w - 1; - int ny2 = y + h - 1; - - if (!valid()) { - _x1 = x, _y1 = y, _x2 = x + w - 1, _y2 = y + h - 1; - } else { - _x1 = Genode::min(_x1, x); - _y1 = Genode::min(_y1, y); - _x2 = Genode::max(_x2, nx2); - _y2 = Genode::max(_y2, ny2); - } - } - - int x() { return _x1; } - int y() { return _y1; } - int w() { return _x2 - _x1 + 1; } - int h() { return _y2 - _y1 + 1; } -}; - -#endif //_APP__XEV_TRACK__BOUNDING_BOX_H_ diff --git a/ports-okl4/src/app/xev_track/main.cc b/ports-okl4/src/app/xev_track/main.cc deleted file mode 100644 index 46898efd9..000000000 --- a/ports-okl4/src/app/xev_track/main.cc +++ /dev/null @@ -1,187 +0,0 @@ -/* - * \brief X event tracker, sending X11 events via ioctl to OKlinux's screen driver - * \author Stefan Kalkowski - * \date 2010-04-27 - */ - -/* - * Copyright (C) 2010-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -/* Standard includes */ -#include -#include -#include -#include -#include -#include -#include -#include - -/* X11 includes */ -#include -#include - -/* X event-tracker includes */ -#include - -/* OKlinux kernel includes */ -#include - -/* App-specific includes */ -#include "bounding_box.h" - -int config_force_top = 1; /* evaluated by the X event-tracker library */ -int nitpicker_fd = -1; /* nitpicker screen device file-descriptor */ -static Bounding_box pending_redraw; /* collects refresh operations */ - - -/** - * C++ wrapper around C struct 'genode_screen_region' - */ -struct Genode_screen_region : public genode_screen_region -{ - Genode_screen_region() { } - Genode_screen_region(int ix, int iy, int iw, int ih) { - x = ix, y = iy, w = iw, h = ih; } - - bool operator != (Genode_screen_region &r) { - return x != r.x || y != r.y || w != r.w || h != r.h; } -}; - - -struct View_state -{ - Genode_screen_region flushed; /* last flushed view position */ - Genode_screen_region curr; /* current view position */ - - bool to_be_flushed() { return flushed != curr; } -}; - - -static View_state view_states[MAX_VIEWS]; - - -static bool view_id_is_valid(int view_id) { - return view_id > 0 && view_id < MAX_VIEWS; } - - -/******************************* - ** X event-tracker callbacks ** - *******************************/ - -void create_view(int view_id) { - if(ioctl(nitpicker_fd, NITPICKER_IOCTL_CREATE_VIEW, view_id)) - printf("An error occured! errno=%d\n", errno); -} - - -void destroy_view(int view_id) { - if(ioctl(nitpicker_fd, NITPICKER_IOCTL_DESTROY_VIEW, view_id)) - printf("An error occured! errno=%d\n", errno); -} - - -void set_background_view(int view_id) { - if(ioctl(nitpicker_fd, NITPICKER_IOCTL_BACK_VIEW, view_id)) - printf("An error occured! errno=%d\n", errno); -} - - -void place_view(int view_id, int x, int y, int w, int h) { - if (view_id_is_valid(view_id)) - view_states[view_id].curr = Genode_screen_region(x, y, w, h); -} - - -void stack_view(int view_id, int neighbor_id, bool behind) { - struct genode_view_stack st = { view_id, neighbor_id, behind }; - if(ioctl(nitpicker_fd, NITPICKER_IOCTL_STACK_VIEW, &st)) - printf("An error occured! errno=%d\n", errno); -} - - -void refresh(int x, int y, int w, int h) -{ - pending_redraw.extend(x, y, w, h); -} - - -static void flush(int x, int y, int width, int height) -{ - if (width <= 0 || height <= 0) return; - - struct genode_screen_region reg = { x, y, width, height }; - if(ioctl(nitpicker_fd, FRAMEBUFFER_IOCTL_REFRESH, ®)) - printf("An error occured! errno=%d\n", errno); - - for (int i = 0; i < MAX_VIEWS; i++) { - if (!view_states[i].to_be_flushed()) continue; - - struct genode_view_place place = { i, view_states[i].curr }; - if(ioctl(nitpicker_fd, NITPICKER_IOCTL_PLACE_VIEW, &place)) - printf("An error occured! errno=%d\n", errno); - - view_states[i].flushed = view_states[i].curr; - } -} - - -static void flush_view_placements() -{ - for (int i = 0; i < MAX_VIEWS; i++) { - if (!view_states[i].to_be_flushed()) continue; - - struct genode_view_place place = { i, view_states[i].curr }; - if(ioctl(nitpicker_fd, NITPICKER_IOCTL_PLACE_VIEW, &place)) - printf("An error occured! errno=%d\n", errno); - - view_states[i].flushed = view_states[i].curr; - } -} - - -/** - * Main program - */ -int main() -{ - /* create connection to the X server */ - Display *dpy = XOpenDisplay(":0"); - if (!dpy) { - printf("Error: Cannot open display\n"); - return -4; - } - - /* open nitpicker screen device */ - if((nitpicker_fd = open("/dev/fb1", O_WRONLY)) < 0) - return -7; - - /* init event tracker library */ - if (!xev_track_init(dpy)) - return -6; - - /* busy loop polls X events */ - for (;;) { - pending_redraw.reset(); - - XEvent ev; - while (XPending(dpy)) { - XNextEvent(dpy, &ev); - xev_track_handle_event(dpy, ev); - } - - flush_view_placements(); - xev_track_handle_cursor(dpy); - - if (pending_redraw.valid()) - flush(pending_redraw.x(), pending_redraw.y(), - pending_redraw.w(), pending_redraw.h()); - - usleep(10000); - } - return 0; -} diff --git a/ports-okl4/src/app/xev_track/target.mk b/ports-okl4/src/app/xev_track/target.mk deleted file mode 100644 index d94c66005..000000000 --- a/ports-okl4/src/app/xev_track/target.mk +++ /dev/null @@ -1,6 +0,0 @@ -TARGET = xevent-tracker -REQUIRES = host x11 xtest xdamage -SRC_CC = main.cc -LIBS = xev_track -EXT_OBJECTS += -lX11 -lXdamage /usr/lib/libXtst.so.6 -INC_DIR += $(REP_DIR)/include/oklx_kernel diff --git a/ports-okl4/src/lib/oklx/genode/genode_audio.cc b/ports-okl4/src/lib/oklx/genode/genode_audio.cc deleted file mode 100644 index 16c0b9415..000000000 --- a/ports-okl4/src/lib/oklx/genode/genode_audio.cc +++ /dev/null @@ -1,303 +0,0 @@ -/* - * \brief Genode C API audio functions needed by OKLinux - * \author Stefan Kalkowski - * \date 2009-11-12 - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -/* Genode includes */ -#include -#include -#include - -extern "C" { -#include -} - -static unsigned long stream_data = 0; -static void (*interrupt_handler)(unsigned long) = 0; - -class Audio_cache -{ - public: - - typedef Audio_out::Session::Channel::Source Stream; - - enum Channels { LEFT, RIGHT, CHANNEL }; - - enum { - FRAME_SIZE_OUT = Audio_out::FRAME_SIZE, - FRAME_SIZE_IN = sizeof(Genode::int16_t), - PACKET_SIZE_OUT = FRAME_SIZE_OUT * Audio_out::PERIOD, - PACKET_SIZE_IN = FRAME_SIZE_IN * Audio_out::PERIOD * CHANNEL, - PACKET_CNT_MAX = Audio_out::QUEUE_SIZE - 1, - BUF_SIZE = PACKET_CNT_MAX * PACKET_SIZE_OUT + 0x400 - }; - - private: - - class Entry - { - private: - - Stream *_stream; - Packet_descriptor _descriptor; /* packet stream descriptor */ - bool _ready; /* ready to submit */ - bool _submitted; /* submitted to hardware */ - - public: - - Entry(Stream *stream) - : _stream(stream), _descriptor(_stream->alloc_packet(PACKET_SIZE_OUT)), - _ready(false), _submitted(false) {} - - Entry() : _ready(false), _submitted(false) { } - - ~Entry() {} - - Packet_descriptor descriptor() { return _descriptor; } - - float* content() { - return _stream->packet_content(_descriptor); } - - void submit(bool delay=false) - { - _ready = true; - if(interrupt_handler && !_submitted && !delay) { - _submitted = true; - _stream->submit_packet(_descriptor); - } - } - - bool submitted() { return _submitted; } - - bool ready() { return _ready; } - - void acknowledge() - { - _submitted = false; - _ready = false; - } - }; - - - Genode::Allocator_avl _alloc_left; - Genode::Allocator_avl _alloc_right; - Audio_out::Connection _left; - Audio_out::Connection _right; - Entry _entries[CHANNEL][PACKET_CNT_MAX]; - unsigned _idx[CHANNEL]; - Genode::size_t _offset; - unsigned _hw_pointer[CHANNEL]; - - Stream *_stream(unsigned channel) { - return channel == LEFT ? _left.stream() : _right.stream(); } - - public: - - Audio_cache() : _alloc_left(Genode::env()->heap()), - _alloc_right(Genode::env()->heap()), - _left("front left", &_alloc_left, BUF_SIZE), - _right("front right", &_alloc_right, BUF_SIZE) - { - for (unsigned ch=LEFT; ch < CHANNEL; ch++) { - for (unsigned i = 0; i < PACKET_CNT_MAX; i++) - _entries[ch][i] = Entry(_stream(ch)); - _idx[ch] = 0; - _hw_pointer[ch] = 0; - } - _offset = 0; - _right.sync_session(_left.session_capability()); - } - - ~Audio_cache() - { - for (unsigned ch=LEFT; ch < CHANNEL; ch++) - for (unsigned i = 0; i < PACKET_CNT_MAX; i++) - _stream(ch)->release_packet(_entries[ch][i].descriptor()); - } - - void write (void *src, Genode::size_t size) - { - using namespace Genode; - - /* The actual packet should not be in use */ - for (unsigned ch=0; ch < CHANNEL; ch++) - if (_entries[ch][_idx[ch]].submitted()) { - PERR("Error: (un-)acknowledged packet chan=%d idx=%d", - ch, _idx[ch]); - } - - short *src_ptr = (short*)src; - while (size) { - /* Use left space in packet or content-size */ - size_t packet_space = - min(size / CHANNEL / FRAME_SIZE_IN * FRAME_SIZE_OUT, - PACKET_SIZE_OUT - _offset); - - for (unsigned chan=0; chan < CHANNEL; chan++) { - float *dest = _entries[chan][_idx[chan]].content() - + (_offset / FRAME_SIZE_OUT); - for (unsigned frame = 0; frame < packet_space / FRAME_SIZE_OUT; - frame++) - dest[frame] = src_ptr[(frame * CHANNEL) + chan] / 32767.0; - } - - /* If packet is full, submit it and switch to next one */ - if ((_offset + packet_space) == PACKET_SIZE_OUT) { - for (unsigned chan=0; chan < CHANNEL; chan++) { - _entries[chan][_idx[chan]].submit(true); - _idx[chan] = (_idx[chan] + 1) % PACKET_CNT_MAX; - } - _offset = 0; - } else - _offset += packet_space; - src_ptr += (packet_space / FRAME_SIZE_OUT) * CHANNEL; - size -= packet_space / FRAME_SIZE_OUT * FRAME_SIZE_IN * CHANNEL; - } - } - - void submit_all() - { - for (unsigned i = 0; i < PACKET_CNT_MAX; i++) { - for (unsigned ch = 0; ch < CHANNEL; ch++) { - if (_entries[ch][i].ready() && !_entries[ch][i].submitted()) - _entries[ch][i].submit(); - } - } - } - - void flush() { - _left.flush(); - _right.flush(); - - for (unsigned i = 0; i < PACKET_CNT_MAX; i++) { - for (unsigned ch = 0; ch < CHANNEL; ch++) { - if (_entries[ch][i].submitted()) - _stream(ch)->get_acked_packet(); - _entries[ch][i].acknowledge(); - } - } - - for (unsigned ch = 0; ch < CHANNEL; ch++) { - _idx[ch] = 0; - _hw_pointer[ch] = 0; - } - _offset = 0; - } - - void acknowledge() - { - bool acked = false; - for (unsigned ch = 0; ch < CHANNEL; ch++) { - if (_stream(ch)->ack_avail()) { - _stream(ch)->get_acked_packet(); - - for (unsigned i = _idx[ch]; ; - i = (i + 1) % PACKET_CNT_MAX) { - if (_entries[ch][i].submitted()) { - _entries[ch][i].acknowledge(); - break; - } - } - - _hw_pointer[ch]++; - acked = true; - } - } - if (acked) - interrupt_handler(stream_data); - } - - Genode::size_t pointer() - { - using namespace Genode; - Genode::size_t size = min(_hw_pointer[LEFT], _hw_pointer[RIGHT]); - return size * Audio_cache::PACKET_SIZE_OUT * CHANNEL - / FRAME_SIZE_OUT * FRAME_SIZE_IN ; - } -}; - - -static Audio_cache *audio_cache(void) -{ - try { - static Audio_cache _cache; - return &_cache; - } catch(...) {} - return 0; -} - -static Genode::uint8_t zeroes[Audio_cache::PACKET_SIZE_IN]; - - -extern "C" { -#include - - int genode_audio_ready() - { - return genode_config_audio() && audio_cache(); - } - - - void genode_audio_collect_acks() - { - if (!interrupt_handler) - return; - audio_cache()->submit_all(); - audio_cache()->acknowledge(); - } - - - void genode_audio_prepare() - { - audio_cache()->flush(); - } - - - void genode_audio_trigger_start(void (*func)(unsigned long), unsigned long data) - { - interrupt_handler = func; - stream_data = data; - } - - - void genode_audio_trigger_stop() - { - interrupt_handler = 0; - stream_data = 0; - audio_cache()->flush(); - } - - - unsigned long genode_audio_position() - { - unsigned long ret = audio_cache()->pointer(); - return ret; - } - - - void genode_audio_write(void* src, unsigned long sz) - { - audio_cache()->write(src, sz); - } - - - void genode_audio_fill_silence(unsigned long sz) - { - audio_cache()->write(&zeroes, sz); - } - - - unsigned int genode_audio_packet_size() { return Audio_cache::PACKET_SIZE_IN; } - - - unsigned int genode_audio_packet_count() { return Audio_cache::PACKET_CNT_MAX; } - -} // extern "C" diff --git a/ports-okl4/src/lib/oklx/genode/genode_block.cc b/ports-okl4/src/lib/oklx/genode/genode_block.cc deleted file mode 100644 index b3c167752..000000000 --- a/ports-okl4/src/lib/oklx/genode/genode_block.cc +++ /dev/null @@ -1,179 +0,0 @@ -/* - * \brief Genode C API config functions needed by OKLinux - * \author Stefan Kalkowski - * \date 2009-05-19 - * - * OKLinux includes several stub drivers, used as frontends to the Genode - * framework, e.g. framebuffer-driver or rom-file block-driver. - * Due to the strong bond of these drivers with the Genode framework, - * they should be configured using Genode's configuration format and - * not the kernel commandline. - * A valid configuration example can be found in 'config/init_config' - * within this repository. - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#include -#include -#include -#include - - -extern "C" { -#include -#include -} - -class Req_cache -{ - private: - - class Req_entry - { - public: - - void *pkt; - void *req; - - Req_entry() : pkt(0), req(0) {} - Req_entry(void *packet, void *request) - : pkt(packet), req(request) {} - }; - - - enum { MAX = 128 }; - - Req_entry _cache[MAX]; - - int _find(void *packet) - { - for (int i=0; i < MAX; i++) - if (_cache[i].pkt == packet) - return i; - return -1; - } - - public: - - void insert(void *packet, void *request) - { - int idx = _find(0); - if (idx < 0) - PERR("Req cache is full!"); - else - _cache[idx] = Req_entry(packet, request); - } - - void remove(void *packet, void **request) - { - int idx = _find(packet); - - if (idx < 0) { - *request = 0; - PERR("Req cache entry not found!"); - } - *request = _cache[idx].req; - _cache[idx].pkt = 0; - _cache[idx].req = 0; - } -}; - - -enum Dimensions { - TX_BUF_SIZE = 1024 * 1024 -}; - - -static void (*end_request)(void*, short, void*, unsigned long) = 0; -static Genode::size_t blk_size = 0; -static Genode::size_t blk_cnt = 0; -static Block::Session::Operations blk_ops; - - -static Req_cache *cache() -{ - static Req_cache _cache; - return &_cache; -} - - -static Block::Connection *session() -{ - static Genode::Allocator_avl _alloc(Genode::env()->heap()); - static Block::Connection _session(&_alloc, TX_BUF_SIZE); - return &_session; -} - - - -extern "C" { - - void genode_block_register_callback(void (*func)(void*, short, - void*, unsigned long)) - { - end_request = func; - } - - - void - genode_block_geometry(unsigned long *cnt, unsigned long *sz, - int *write, unsigned long *queue_sz) - { - if (!blk_size && !blk_cnt) - session()->info(&blk_cnt, &blk_size, &blk_ops); - *cnt = blk_cnt; - *sz = blk_size; - *queue_sz = session()->tx()->bulk_buffer_size(); - *write = blk_ops.supported(Block::Packet_descriptor::WRITE) ? 1 : 0; - } - - - void* genode_block_request(unsigned long sz, void *req, unsigned long *offset) - { - try { - Block::Packet_descriptor p = session()->tx()->alloc_packet(sz); - void *addr = session()->tx()->packet_content(p); - cache()->insert(addr, req); - *offset = p.offset(); - return addr; - } catch (Block::Session::Tx::Source::Packet_alloc_failed) { } - return 0; - } - - - void genode_block_submit(unsigned long queue_offset, unsigned long size, - unsigned long disc_offset, int write) - { - Genode::size_t sector = disc_offset / blk_size; - Genode::size_t sector_cnt = size / blk_size; - Block::Packet_descriptor p(Block::Packet_descriptor(queue_offset, size), - write ? Block::Packet_descriptor::WRITE - : Block::Packet_descriptor::READ, - sector, sector_cnt); - session()->tx()->submit_packet(p); - } - - - void genode_block_collect_responses() - { - static bool avail = genode_config_block(); - if (avail) { - void *req; - while (session()->tx()->ack_avail()) { - Block::Packet_descriptor packet = session()->tx()->get_acked_packet(); - void *addr = session()->tx()->packet_content(packet); - bool write = packet.operation() == Block::Packet_descriptor::WRITE; - cache()->remove(session()->tx()->packet_content(packet), &req); - if (req && end_request) - end_request(req, write, addr, packet.size()); - session()->tx()->release_packet(packet); - } - } - } -} // extern "C" diff --git a/ports-okl4/src/lib/oklx/genode/genode_config.cc b/ports-okl4/src/lib/oklx/genode/genode_config.cc deleted file mode 100644 index fb1bd4c46..000000000 --- a/ports-okl4/src/lib/oklx/genode/genode_config.cc +++ /dev/null @@ -1,145 +0,0 @@ -/* - * \brief Genode C API config functions needed by OKLinux - * \author Stefan Kalkowski - * \date 2009-05-19 - * - * OKLinux includes several stub drivers, used as frontends to the Genode - * framework, e.g. framebuffer-driver or rom-file block-driver. - * Due to the strong bond of these drivers with the Genode framework, - * they should be configured using Genode's configuration format and - * not the kernel commandline. - * A valid configuration example can be found in 'config/init_config' - * within this repository. - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -/* Genode includes */ -#include -#include -#include - -#include - - -static Genode::Xml_node node(const char* name) -{ - Genode::Xml_node config_node = Genode::config()->xml_node(); - - for (unsigned i = 0; i < config_node.num_sub_nodes(); i++) { - Genode::Xml_node node = config_node.sub_node(i); - if(node.has_type(name)) - return node; - } - throw Genode::Xml_node::Nonexistent_sub_node(); -} - - -static void content(const char* node_name, const char *attr_name, - char *buf, Genode::size_t sz) -{ - *buf = 0; - try { - Genode::Xml_node _node = node(node_name); - _node.attribute(attr_name).value(buf, sz); - } catch (Genode::Xml_node::Invalid_syntax) { - PWRN("Malformed entry in Linux config."); - } catch (...) { } -} - - -Screen_array::Screen_array() -{ - try { - Genode::Xml_node scr_node = node("screens"); - for (unsigned i = 0; i < SIZE; i++) { - if (i >= scr_node.num_sub_nodes()) - _screens[i] = (Screen*) 0; - else { - Genode::Xml_node n = scr_node.sub_node(i); - if(n.has_type("framebuffer")) - _screens[i] = new (Genode::env()->heap()) Simple_screen(); - else if(n.has_type("nitpicker")) - _screens[i] = new (Genode::env()->heap()) Nitpicker_screen(); - else - PWRN("Ignoring unknown tag in screen section"); - } - } - } catch (Genode::Xml_node::Invalid_syntax) { - PWRN("Malformed entry in Linux config."); - } catch (Genode::Xml_node::Nonexistent_sub_node) { - PWRN("No screen section in config"); - } -} - - -extern "C" { -#include - - char* genode_config_cmdline() - { - static char cmd_line[512]; - static bool initialized = false; - if (!initialized) { - content("commandline", "args", cmd_line, sizeof(cmd_line)); - initialized = true; - } - return Genode::strlen(cmd_line) ? cmd_line : 0; - } - - - char* genode_config_initrd() - { - static char initrd[64]; - static bool initialized = false; - if (!initialized) { - content("initrd", "name", initrd, sizeof(initrd)); - initialized = true; - } - return Genode::strlen(initrd) ? initrd : 0; - } - - - int genode_config_audio() - { - try { - node("audio"); - return 1; - } catch (Genode::Xml_node::Invalid_syntax) { - PWRN("Malformed entry in Linux config."); - } catch (Genode::Xml_node::Nonexistent_sub_node) { - } - return 0; - } - - - int genode_config_nic() - { - try { - node("nic"); - return 1; - } catch (Genode::Xml_node::Invalid_syntax) { - PWRN("Malformed entry in Linux config."); - } catch (Genode::Xml_node::Nonexistent_sub_node) { - } - return 0; - } - - int genode_config_block() - { - try { - node("block"); - return 1; - } catch (Genode::Xml_node::Invalid_syntax) { - PWRN("Malformed entry in Linux config."); - } catch (Genode::Xml_node::Nonexistent_sub_node) { - } - return 0; - } - -} // extern "C" diff --git a/ports-okl4/src/lib/oklx/genode/genode_exit.cc b/ports-okl4/src/lib/oklx/genode/genode_exit.cc deleted file mode 100644 index 0be9a9836..000000000 --- a/ports-okl4/src/lib/oklx/genode/genode_exit.cc +++ /dev/null @@ -1,29 +0,0 @@ -/* - * \brief Genode C API exit function needed by OKLinux - * \author Stefan Kalkowski - * \date 2009-08-14 - */ - -/* - * Copyright (C) 2009 - * Genode Labs, Feske & Helmuth Systementwicklung GbR - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -/* Genode includes */ -#include - -extern "C" { - - namespace Okl4 { -#include - } - - void genode_exit(int value) - { - Genode::env()->parent()->exit(1); - } - -} // extern "C" diff --git a/ports-okl4/src/lib/oklx/genode/genode_framebuffer.cc b/ports-okl4/src/lib/oklx/genode/genode_framebuffer.cc deleted file mode 100644 index d709dc79b..000000000 --- a/ports-okl4/src/lib/oklx/genode/genode_framebuffer.cc +++ /dev/null @@ -1,174 +0,0 @@ -/* - * \brief Genode C API framebuffer functions of the OKLinux support library - * \author Stefan Kalkowski - * \date 2009-06-08 - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -/* Genode includes */ -#include -#include -#include -#include -#include -#include - -/* Oklx library includes */ -#include -#include - - -Screen_array* Screen_array::screens() -{ - static Screen_array _screens; - return &_screens; -} - - -extern "C" { - - namespace Okl4 { -#include - } - - using namespace Okl4; - - - int genode_screen_count() - { - return Screen_array::screens()->count(); - } - - - unsigned long genode_fb_size(unsigned screen) - { - Screen *s = Screen_array::screens()->get(screen); - if (s && s->framebuffer()) { - Genode::Dataspace_client dsc(s->framebuffer()->dataspace()); - return dsc.size(); - } - return 0; - } - - - void *genode_fb_attach(unsigned screen) - { - using namespace Genode; - - Screen *s = Screen_array::screens()->get(screen); - if (!s || !s->framebuffer()) - return (void*) 0; - - /* Attach the framebuffer */ - Dataspace_capability cap = s->framebuffer()->dataspace(); - Dataspace_client dsc(cap); - void* base = Genode::env()->rm_session()->attach(cap); - size_t size = dsc.size(); - - /* Put the framebuffer area in our database*/ - Memory_area::memory_map()->insert(new (env()->heap()) - Memory_area((addr_t)base, size, cap)); - return (void*)base; - } - - - void genode_fb_info(unsigned screen, int *out_w, int *out_h) - { - Screen *s = Screen_array::screens()->get(screen); - if (s && s->framebuffer()) { - Framebuffer::Mode const mode = s->framebuffer()->mode(); - *out_w = mode.width(); - *out_h = mode.height(); - } - } - - - void genode_fb_refresh(unsigned screen, int x, int y, int w, int h) - { - Screen *s = Screen_array::screens()->get(screen); - if (s && s->framebuffer()) - s->framebuffer()->refresh(x,y,w,h); - } - - - void genode_fb_close(unsigned screen) - { - Screen *s = Screen_array::screens()->get(screen); - if (s) - destroy(Genode::env()->heap(), s); - } - - - void genode_nit_view_create(unsigned screen, unsigned view) - { - Nitpicker_screen *s = - dynamic_cast(Screen_array::screens()->get(screen)); - if (s) - s->put_view(view, s->nitpicker()->create_view()); - } - - - void genode_nit_view_destroy(unsigned screen, unsigned view) - { - Nitpicker_screen *s = - dynamic_cast(Screen_array::screens()->get(screen)); - if (s) { - s->nitpicker()->destroy_view(s->get_view(view)); - s->put_view(view, Nitpicker::View_capability()); - } - } - - - void genode_nit_view_back(unsigned screen, unsigned view) - { - Nitpicker_screen *s = - dynamic_cast(Screen_array::screens()->get(screen)); - if (s) - s->nitpicker()->background(s->get_view(view)); - } - - void genode_nit_view_place(unsigned screen, unsigned view, - int x, int y, int w, int h) - { - Nitpicker_screen *s = - dynamic_cast(Screen_array::screens()->get(screen)); - if (s) { - Nitpicker::View_client v(s->get_view(view)); - v.viewport(x, y, w, h, -x, -y, false); - } - } - - void genode_nit_view_stack(unsigned screen, unsigned view, - unsigned neighbor, int behind) - { - Nitpicker_screen *s = - dynamic_cast(Screen_array::screens()->get(screen)); - if (s) { - Nitpicker::View_client v(s->get_view(view)); - v.stack(s->get_view(neighbor), behind, false); - } - } - - - void genode_nit_close_all_views(unsigned screen) - { - Nitpicker_screen *s = - dynamic_cast(Screen_array::screens()->get(screen)); - if(s) { - for (unsigned i = 0; i < Nitpicker_screen::VIEW_CNT; i++) { - Nitpicker::View_capability n = s->get_view(i); - if (n.valid()) { - s->nitpicker()->destroy_view(n); - s->put_view(i, Nitpicker::View_capability()); - } - } - } - } - -} // extern "C" diff --git a/ports-okl4/src/lib/oklx/genode/genode_input.cc b/ports-okl4/src/lib/oklx/genode/genode_input.cc deleted file mode 100644 index 323522847..000000000 --- a/ports-okl4/src/lib/oklx/genode/genode_input.cc +++ /dev/null @@ -1,153 +0,0 @@ -/* - * \brief Genode C API input functions for the OKLinux support libary - * \author Stefan Kalkowski - * \date 2010-04-21 - */ - -/* - * Copyright (C) 2010-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -/* Genode includes */ -#include -#include -#include -#include - -/* OKLx library includes */ -#include -#include - -enum Event_types {EV_SYN=0x00, EV_KEY=0x01, EV_REL=0x02, EV_ABS=0x03 }; -enum Relative_axes {REL_X=0x00, REL_Y=0x01, REL_WHEEL=0x08 }; -enum Absolute_axes {ABS_X=0x00, ABS_Y=0x01, ABS_WHEEL=0x08 }; - -extern "C" { - namespace Okl4 { -#include - } - - using namespace Okl4; - - static void (*genode_input_event) (void*,unsigned int, unsigned int, int) = 0; - - - void genode_input_register_callback(void (*func) - (void*,unsigned int, unsigned int, int)) - { - genode_input_event = func; - } - - - void genode_input_unregister_callback(void) - { - genode_input_event = 0; - } - - - void genode_input_register_keyb(unsigned int idx, void* dev) - { - Screen *s = Screen_array::screens()->get(idx); - if(s) - s->keyb_device(dev); - } - - - void genode_input_unregister_keyb(unsigned int idx) - { - Screen *s = Screen_array::screens()->get(idx); - if(s) - s->keyb_device(0); - } - - - void genode_input_register_mouse(unsigned int idx, void* dev) - { - Screen *s = Screen_array::screens()->get(idx); - if(s) - s->mouse_device(dev); - } - - - void genode_input_unregister_mouse(unsigned int idx) - { - Screen *s = Screen_array::screens()->get(idx); - if(s) - s->mouse_device(0); - } - - - static void handle_event(void *mouse, void *keyb, Input::Event *ev) - { - using namespace Input; - - switch(ev->type()) { - case Event::MOTION: - { - if(ev->rx()) - genode_input_event(mouse, EV_REL, REL_X, ev->rx()); - if(ev->ry()) - genode_input_event(mouse, EV_REL, REL_Y, ev->ry()); - if(ev->ax()) - genode_input_event(mouse, EV_ABS, ABS_X, ev->ax()); - if(ev->ay()) - genode_input_event(mouse, EV_ABS, ABS_Y, ev->ay()); - return; - } - case Event::PRESS: - { - if (ev->code() < BTN_MISC) - genode_input_event(keyb, EV_KEY, ev->code(), 1); - else - genode_input_event(mouse, EV_KEY, ev->code(), 1); - return; - } - case Event::RELEASE: - { - if (ev->code() < BTN_MISC) - genode_input_event(keyb, EV_KEY, ev->code(), 0); - else - genode_input_event(mouse, EV_KEY, ev->code(), 0); - return; - } - case Event::WHEEL: - { - if(ev->rx()) - genode_input_event(mouse, EV_REL, REL_WHEEL, ev->rx()); - else - genode_input_event(mouse, EV_ABS, ABS_WHEEL, ev->ax()); - return; - } - case Event::INVALID: - default: - ; - } - } - - - void genode_input_handle_events(void) - { - if (!genode_input_event) - return; - - for (unsigned i = 0; i < Screen_array::SIZE; i++) { - Screen *s = Screen_array::screens()->get(i); - if (!s) - break; - - Input::Session *inp = s->input(); - if (inp && s->mouse_device() && s->keyb_device() ) { - int num = inp->flush(); - for (int i = 0; i < num; i++) { - Input::Event *ev = &s->buffer()[i]; - handle_event(s->mouse_device(), - s->keyb_device(), ev); - } - } - s = s->next(); - } - } -} //extern "C" diff --git a/ports-okl4/src/lib/oklx/genode/genode_lock.cc b/ports-okl4/src/lib/oklx/genode/genode_lock.cc deleted file mode 100644 index 4ab187122..000000000 --- a/ports-okl4/src/lib/oklx/genode/genode_lock.cc +++ /dev/null @@ -1,53 +0,0 @@ -/* - * \brief Genode C API memory functions needed by OKLinux - * \author Stefan Kalkowski - * \date 2009-05-07 - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -/* Genode includes */ -#include -#include -#include - - -extern "C" { - - namespace Okl4 { -#include - } - - void *genode_alloc_lock() - { - return new (Genode::env()->heap()) Genode::Lock(); - } - - - void genode_free_lock(void* lock) - { - if(lock) - Genode::destroy(Genode::env()->heap(), - static_cast(lock)); - } - - - void genode_lock(void* lock) - { - if(lock) - static_cast(lock)->lock(); - } - - - void genode_unlock(void* lock) - { - if(lock) - static_cast(lock)->unlock(); - } - -} // extern "C" diff --git a/ports-okl4/src/lib/oklx/genode/genode_memory.cc b/ports-okl4/src/lib/oklx/genode/genode_memory.cc deleted file mode 100644 index 9e9648e5a..000000000 --- a/ports-okl4/src/lib/oklx/genode/genode_memory.cc +++ /dev/null @@ -1,71 +0,0 @@ -/* - * \brief Genode C API memory functions needed by OKLinux - * \author Stefan Kalkowski - * \date 2009-05-07 - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -/* Genode includes */ -#include -#include - -/* local includes */ -#include -#include - -extern "C" { - - namespace Okl4 { -#include -#include - } - - - void *genode_malloc(unsigned long sz) - { - using namespace Genode; - - try { - - /* Get a new dataspace and attach it to our address space */ - Dataspace_capability ds = env()->ram_session()->alloc(sz); - void *base = env()->rm_session()->attach(ds); - - /* Put the dataspace area in our database */ - Memory_area::memory_map()->insert(new (env()->heap()) - Memory_area((addr_t)base,sz,ds)); - return base; - } - catch(...) - { - PWRN("Could not open dataspace!"); - return 0; - } - } - - - void genode_set_pager() - { - Genode::Oklx_process::set_pager(); - } - - - unsigned long genode_quota() - { - return Genode::env()->ram_session()->quota(); - } - - - unsigned long genode_used_mem() - { - return Genode::env()->ram_session()->used(); - } - - -} // extern "C" diff --git a/ports-okl4/src/lib/oklx/genode/genode_net.cc b/ports-okl4/src/lib/oklx/genode/genode_net.cc deleted file mode 100644 index a8a0f7152..000000000 --- a/ports-okl4/src/lib/oklx/genode/genode_net.cc +++ /dev/null @@ -1,176 +0,0 @@ -/* - * \brief Genode C API framebuffer functions of the OKLinux support library - * \author Stefan Kalkowski - * \date 2009-06-08 - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -/* Genode includes */ -#include -#include -#include -#include -#include -#include -#include - -extern "C" { -#include -} - -static bool avail = false; - -static Nic::Connection *nic() { - try { - static Genode::Allocator_avl tx_block_alloc(Genode::env()->heap()); - static Nic::Connection nic(&tx_block_alloc); - avail = true; - return &nic; - } catch(...) { } - return 0; -} - - -class Packet_pool -{ - private: - - class Entry - { - public: - - Packet_descriptor packet; - void *addr; - - Entry() : addr(0) {} - }; - - - enum { MAX_ENTRIES = 100 }; - - Entry _entries[MAX_ENTRIES]; - - public: - - class Pool_full : Genode::Exception {}; - - - void add(Packet_descriptor p, void* addr) - { - for (unsigned i=0; i < MAX_ENTRIES; i++) { - if (!_entries[i].addr) { - _entries[i].addr = addr; - _entries[i].packet = p; - return; - } - } - throw Pool_full(); - } - - void* get(Packet_descriptor _packet) - { - for (unsigned i=0; i < MAX_ENTRIES; i++) - if (nic()->tx()->packet_content(_packet) - == nic()->tx()->packet_content(_entries[i].packet)) { - void *ret = _entries[i].addr; - _entries[i].addr = 0; - return ret; - } - return 0; - } -}; - - -static Packet_pool *packet_pool() -{ - static Packet_pool pool; - return &pool; -} - - -extern "C" { - -#include - - static void (*receive_packet)(void*, void*, unsigned long) = 0; - static void *net_device = 0; - - - void genode_net_start(void *dev, void (*func)(void*, void*, unsigned long)) - { - receive_packet = func; - net_device = dev; - } - - - void genode_net_stop() - { - net_device = 0; - receive_packet = 0; - } - - - void genode_net_mac(void* mac, unsigned long size) - { - using namespace Genode; - - Nic::Mac_address m = nic()->mac_address(); - memcpy(mac, &m.addr, min(sizeof(m.addr), (size_t)size)); - } - - - int genode_net_tx(void* addr, unsigned long len, void *skb) - { - try { - Packet_descriptor packet = nic()->tx()->alloc_packet(len); - void* content = nic()->tx()->packet_content(packet); - packet_pool()->add(packet, skb); - Genode::memcpy(content, addr, len); - nic()->tx()->submit_packet(packet); - return 0; - } catch(Packet_pool::Pool_full) { - PWRN("skb_buff/packet pool full!"); - } catch(...) { - PWRN("Send failed!"); - } - return 1; - } - - - int genode_net_tx_ack_avail() { - return nic()->tx()->ack_avail(); } - - - void* genode_net_tx_ack() - { - Packet_descriptor packet = nic()->tx()->get_acked_packet(); - void *skb = packet_pool()->get(packet); - nic()->tx()->release_packet(packet); - return skb; - } - - - void genode_net_rx_receive() - { - if (avail) { - while(nic()->rx()->packet_avail()) { - Packet_descriptor p = nic()->rx()->get_packet(); - if (receive_packet && net_device) - receive_packet(net_device, nic()->rx()->packet_content(p), p.size()); - nic()->rx()->acknowledge_packet(p); - } - } - } - - - int genode_net_ready() - { - return genode_config_nic() && nic(); - } -} diff --git a/ports-okl4/src/lib/oklx/genode/genode_open.cc b/ports-okl4/src/lib/oklx/genode/genode_open.cc deleted file mode 100644 index 14033069c..000000000 --- a/ports-okl4/src/lib/oklx/genode/genode_open.cc +++ /dev/null @@ -1,56 +0,0 @@ -/* - * \brief Genode C API file functions needed by OKLinux - * \author Stefan Kalkowski - * \date 2009-05-19 - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -/* Genode includes */ -#include -#include -#include -#include - -/* OKLinux support includes */ -#include - -extern "C" { - - namespace Okl4 { -#include - } - - - void* genode_open(const char *name, unsigned long *sz) - { - using namespace Genode; - try { - - /* Open the file dataspace and attach it */ - Rom_connection rc(name); - rc.on_destruction(Rom_connection::KEEP_OPEN); - Rom_dataspace_capability cap = rc.dataspace(); - Dataspace_client dsc(cap); - *sz = dsc.size(); - void *base = env()->rm_session()->attach(cap); - - /* Put it in our database */ - Memory_area::memory_map()->insert(new (env()->heap()) - Memory_area((addr_t)base, *sz, - cap)); - return base; - } - catch(...) - { - PWRN("Could not open rom dataspace %s!", name); - return 0; - } - } - -} // extern "C" diff --git a/ports-okl4/src/lib/oklx/genode/genode_printf.cc b/ports-okl4/src/lib/oklx/genode/genode_printf.cc deleted file mode 100644 index 19b6cfd0f..000000000 --- a/ports-okl4/src/lib/oklx/genode/genode_printf.cc +++ /dev/null @@ -1,29 +0,0 @@ -/* - * \brief Genode C API print functions needed by OKLinux - * \author Stefan Kalkowski - * \date 2009-05-19 - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -/* Genode includes */ -#include -#include - -extern "C" { -#include - - - void genode_printf(const char *format, ...) - { - va_list list; - va_start(list, format); - Genode::vprintf(format, list); - } - -} // extern "C" diff --git a/ports-okl4/src/lib/oklx/genode/genode_sleep.cc b/ports-okl4/src/lib/oklx/genode/genode_sleep.cc deleted file mode 100644 index 98488550a..000000000 --- a/ports-okl4/src/lib/oklx/genode/genode_sleep.cc +++ /dev/null @@ -1,34 +0,0 @@ -/* - * \brief Timer functions needed by Genode/OKLinux - * \author Stefan Kalkowski - * \date 2009-05-07 - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -/* Genode includes */ -#include -#include - -/* - * We only have one timer connection by now, as only the timer thread - * in OKLinux uses this functionality - */ -static Timer::Connection timer; - -extern "C" -{ -#include - - - void genode_sleep(unsigned ms) { timer.msleep(ms); } - - - void genode_sleep_forever() { Genode::sleep_forever(); } - -} // extern "C" diff --git a/ports-okl4/src/lib/oklx/genode/genode_threads.cc b/ports-okl4/src/lib/oklx/genode/genode_threads.cc deleted file mode 100644 index e70086705..000000000 --- a/ports-okl4/src/lib/oklx/genode/genode_threads.cc +++ /dev/null @@ -1,179 +0,0 @@ -/* - * \brief Genode C API thread functions needed by OKLinux - * \author Stefan Kalkowski - * \date 2009-05-19 - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -/* Genode includes */ -#include - -/* OKLinux support library */ -#include - -namespace Okl4 { -extern "C" { -#include -#include -} -} - -using namespace Genode; -using namespace Okl4; - -static Thread_capability oklx_pager_cap; /* cap to the Linux main thread */ - - -/** - * Get the thread capability of the active kernel thread - */ -static Thread_capability my_cap() -{ - Okl4::L4_Word_t tid = thread_myself(); - - Oklx_kernel_thread * thread = Genode::Oklx_thread_list::thread_list()->first(); - while (thread) { - if (thread->tid().raw == tid) - return thread->cap(); - thread = thread->next(); - } - - /* invalid cap */ - return Thread_capability(); -} - - -void Oklx_kernel_thread::entry() -{ - L4_ThreadId_t tid; - - /* Save our thread id to the first entry in the UTCB */ - __L4_TCR_Set_ThreadWord(0, L4_UserDefinedHandle()); - - /* Synchronize with the thread, that created us and sleep afterwards */ - L4_Wait(&tid); - sleep_forever(); -} - - -L4_ThreadId_t Oklx_thread_list::add() -{ - try - { - /* Create the thread an start it immediately */ - Thread_capability cap = _cpu.create_thread("Lx_kernel_thread"); - Oklx_kernel_thread *thd = new (env()->heap()) Oklx_kernel_thread(cap); - _threads.insert(thd); - env()->pd_session()->bind_thread(cap); - Pager_capability pager = env()->rm_session()->add_client(cap); - _cpu.set_pager(cap, pager); - _cpu.start(cap, (addr_t)&Oklx_kernel_thread::entry, - (addr_t)thd->stack_addr()); - - /* Get the OKL4 thread id of the new thread */ - Thread_state state = _cpu.state(cap); - thd->set_tid(state.tid); - - /* Acknowledge startup and return */ - L4_Send(state.tid); - return state.tid; - } - catch(...) - { - PWRN("Creation of new thread failed!"); - return L4_nilthread; - } -} - - -Oklx_thread_list* Oklx_thread_list::thread_list() -{ - static Oklx_thread_list _list; - return &_list; -} - - -Oklx_process::~Oklx_process() -{ - /* When the process dies, kill all of its threads */ - while(_threads.first()) - { - Oklx_user_thread *th = _threads.first(); - _threads.remove(th); - destroy(env()->heap(), th); - } -} - - -L4_ThreadId_t -Oklx_process::add_thread() -{ - Oklx_user_thread *th = 0; - try { - Thread_state dst_state; - th = new (env()->heap()) Oklx_user_thread(); - _pd.bind_thread(th->cap()); - - /* - * Initialize eip and esp with max. value to signal - * core, that it doesn't really need to start this thread, - * but will create the OKL4 thread inactive - */ - _cpu.start(th->cap(), 0xffffffff, 0xffffffff); - dst_state = _cpu.state(th->cap()); - th->_tid = dst_state.tid; - _threads.insert(th); - return th->_tid; - } - catch(...) - { - PWRN("Couldn't create a new Thread for space %lx", _pd.space_id().raw); - if(th) - destroy(env()->heap(), th); - return L4_nilthread; - } -} - - -bool -Oklx_process::kill_thread(Okl4::L4_ThreadId_t tid) -{ - Oklx_user_thread *th = _threads.first(); - while (th) - { - if(th->tid().raw == tid.raw) - { - _threads.remove(th); - destroy(env()->heap(), th); - return true; - } - th = th->next(); - } - return false; -} - - -List* -Oklx_process::processes() -{ - static List _list; - return &_list; -} - - -void Oklx_process::set_pager() -{ - oklx_pager_cap = my_cap(); -} - - -Thread_capability Oklx_process::pager_cap() -{ - return oklx_pager_cap; -} diff --git a/ports-okl4/src/lib/oklx/iguana/iguana_eas.cc b/ports-okl4/src/lib/oklx/iguana/iguana_eas.cc deleted file mode 100644 index 27acc2d48..000000000 --- a/ports-okl4/src/lib/oklx/iguana/iguana_eas.cc +++ /dev/null @@ -1,159 +0,0 @@ -/* - * \brief Iguana API functions needed by OKLinux - * \author Stefan Kalkowski - * \date 2009-05-07 - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -/* Genode includes */ -#include -#include - -/* OKLinux support includes */ -#include -#include - -namespace Okl4 { - -/* OKL4 includes */ -#include -#include -} - -using namespace Okl4; - - -/** - * Get OKLinux exregs page, a special page used to synchronize - * OKLinux kernel main thread and the user processes - */ -static Genode::addr_t get_exregs_page() -{ - using namespace Genode; - - static bool initialized = false; - static addr_t phys_addr = 0; - - if(!initialized) - { - /* Order one page and copy the src content to it */ - extern char __user_exregs_page[]; - Dataspace_capability ds = env()->ram_session()->alloc(1 << 12); - Dataspace_client dsc(ds); - void* page = env()->rm_session()->attach(ds); - memcpy(page, (const void*)&__user_exregs_page, 1 << 12); - phys_addr = dsc.phys_addr(); - } - return phys_addr; -} - - -extern "C" { - - - L4_ThreadId_t eas_create_thread(eas_ref_t eas, L4_ThreadId_t pager, - L4_ThreadId_t scheduler, - void *utcb, L4_ThreadId_t *handle_rv) - { - using namespace Genode; - - /* Find the right user process and add a thread to it */ - Oklx_process *p = Oklx_process::processes()->first(); - while(p) - { - if(p->pd()->space_id().raw == eas) - return p->add_thread(); - p = p->next(); - } - PWRN("Oklinux user process %lx not found!", eas); - return L4_nilthread; - } - - - eas_ref_t eas_create(L4_Fpage_t utcb, L4_SpaceId_t *l4_id) - { - using namespace Genode; - - /* Create a new OKLinux user process and return its space id */ - Oklx_process *p = 0; - try { - p = new (env()->heap()) Oklx_process(); - Oklx_process::processes()->insert(p); - *l4_id = p->pd()->space_id(); - return l4_id->raw; - } - catch(...) { - PWRN("Could not create a new protection domain!\n"); - if(p) - destroy(env()->heap(),p); - return 0; - } - } - - - void eas_delete(eas_ref_t eas) - { - using namespace Genode; - - /* Find the process and remove it from the global list */ - Oklx_process *p = Oklx_process::processes()->first(); - while(p) - { - if(p->pd()->space_id().raw == eas) - { - Oklx_process::processes()->remove(p); - destroy(env()->heap(), p); - return; - } - p = p->next(); - } - } - - - int eas_map(eas_ref_t eas, L4_Fpage_t src_fpage, uintptr_t dst_addr, - uintptr_t attributes) - { - using namespace Genode; - using namespace Okl4; - - unsigned long dest_addr = dst_addr & 0xfffff000; - unsigned long phys_addr = 0; - unsigned long src_addr = L4_Address(src_fpage); - - /* Dirty hack for the evil oklx exregs page */ - if(dest_addr == 0x98765000UL) - phys_addr = get_exregs_page(); - else - { - Memory_area *ma = Memory_area::memory_map()->first(); - while(ma) - { - if(src_addr >= ma->vaddr() && - src_addr < ma->vaddr()+ma->size()) - { - phys_addr = ma->paddr() + src_addr - ma->vaddr(); - break; - } - ma = ma->next(); - } - } - if(phys_addr == 0) - PERR("wants to map from=0x%08lx to=0x%08lx", src_addr, dest_addr); - L4_Fpage_t vpage = L4_Fpage(dest_addr, 1 << 12); - L4_PhysDesc_t pdesc = L4_PhysDesc(phys_addr, attributes); - int rwx = L4_Rights(src_fpage); - L4_SpaceId_t id; - id.raw = eas; - L4_FpageAddRightsTo(&vpage, rwx); - if(!L4_MapFpage(id, vpage, pdesc)) - PERR("Mapping failed"); - return 0; - } - -} // extern "C" diff --git a/ports-okl4/src/lib/oklx/iguana/iguana_hardware.cc b/ports-okl4/src/lib/oklx/iguana/iguana_hardware.cc deleted file mode 100644 index 129ef1738..000000000 --- a/ports-okl4/src/lib/oklx/iguana/iguana_hardware.cc +++ /dev/null @@ -1,35 +0,0 @@ -/* - * \brief Iguana API functions needed by OKLinux - * \author Stefan Kalkowski - * \date 2009-05-07 - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#include - -extern "C" { -#include - - - int hardware_register_interrupt(L4_ThreadId_t thrd, int interrupt) - { - PWRN("Not yet implemented!"); - return 0; - } - - - int hardware_back_memsection(memsection_ref_t memsection, - uintptr_t paddr, uintptr_t attributes) - { - PWRN("Not yet implemented!"); - return 0; - } - -} // extern "C" - diff --git a/ports-okl4/src/lib/oklx/iguana/iguana_memsection.cc b/ports-okl4/src/lib/oklx/iguana/iguana_memsection.cc deleted file mode 100644 index 19383e5b4..000000000 --- a/ports-okl4/src/lib/oklx/iguana/iguana_memsection.cc +++ /dev/null @@ -1,132 +0,0 @@ -/* - * \brief Iguana API functions needed by OKLinux - * \author Stefan Kalkowski - * \date 2009-05-07 - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#include -#include - -extern "C" { - - namespace Okl4 { -#include -#include -#include -#include - } - - using namespace Okl4; - - - uintptr_t memsection_virt_to_phys(uintptr_t vaddr, size_t *size) - { - using namespace Genode; - Memory_area *area = Memory_area::memory_map()->first(); - while(area) - { - if(area->vaddr() == vaddr) - { - uintptr_t paddr = area->paddr(); - *size = area->size(); - return paddr; - } - area = area->next(); - } - PWRN("Memory area beginning @vaddr=0x%08lx doesn't exist!", - (unsigned long)vaddr); - return 0; - } - - - int memsection_page_map(memsection_ref_t memsect, L4_Fpage_t from_page, - L4_Fpage_t to_page) - { - PWRN("Not yet implemented!"); - return 0; - } - - - int memsection_page_unmap(memsection_ref_t memsect, L4_Fpage_t to_page) - { - PWRN("Not yet implemented!"); - return 0; - } - - - int memsection_register_server(memsection_ref_t memsect, - thread_ref_t server) - { - PWRN("Not yet implemented!"); - return 0; - } - - - memsection_ref_t memsection_lookup(objref_t object, thread_ref_t *server) - { - return object; - } - - - void *memsection_base(memsection_ref_t memsect) - { - using namespace Genode; - Memory_area *area = Memory_area::memory_map()->first(); - while(area) - { - if((area->vaddr() <= memsect) && - ((area->vaddr()+area->size()) >= memsect)) - return (void*) area->vaddr(); - area = area->next(); - } - PWRN("Memory area with @vaddr=0x%08lx doesn't exist!", - (unsigned long)memsect); - return 0; - } - - - uintptr_t memsection_size(memsection_ref_t memsect) - { - using namespace Genode; - Memory_area *area = Memory_area::memory_map()->first(); - while(area) - { - if((area->vaddr() <= memsect) && - ((area->vaddr()+area->size()) >= memsect)) - return area->size(); - area = area->next(); - } - PWRN("Memory area with @vaddr=0x%08lx doesn't exist!", - (unsigned long)memsect); - return 0; - } - - - memsection_ref_t memsection_create_user(uintptr_t size, uintptr_t *base) - { - PWRN("Not yet implemented!"); - return 0; - } - - - void memsection_delete(memsection_ref_t) - { - PWRN("Not yet implemented!"); - } - - - memsection_ref_t memsection_create(uintptr_t size, uintptr_t *base) - { - PWRN("Not yet implemented!"); - return 0; - } - -} // extern "C" - diff --git a/ports-okl4/src/lib/oklx/iguana/iguana_pd.cc b/ports-okl4/src/lib/oklx/iguana/iguana_pd.cc deleted file mode 100644 index 6f4c022f0..000000000 --- a/ports-okl4/src/lib/oklx/iguana/iguana_pd.cc +++ /dev/null @@ -1,41 +0,0 @@ -/* - * \brief Iguana API functions needed by OKLinux - * \author Stefan Kalkowski - * \date 2009-05-07 - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -/* Genode includes */ -#include - -extern "C" { -#include - - - pd_ref_t pd_myself(void) - { - PWRN("Not yet implemented!"); - return 0; - } - - - void pd_delete(pd_ref_t pd) - { - PWRN("Not yet implemented!"); - } - - - memsection_ref_t pd_create_memsection(pd_ref_t pd, uintptr_t size, - uintptr_t *base) - { - PWRN("Not yet implemented!"); - return 0; - } - -} // extern "C" diff --git a/ports-okl4/src/lib/oklx/iguana/iguana_thread.cc b/ports-okl4/src/lib/oklx/iguana/iguana_thread.cc deleted file mode 100644 index 56550aeff..000000000 --- a/ports-okl4/src/lib/oklx/iguana/iguana_thread.cc +++ /dev/null @@ -1,54 +0,0 @@ -/* - * \brief Iguana API functions needed by OKLinux - * \author Stefan Kalkowski - * \date 2009-05-07 - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#include -#include - -using namespace Okl4; - -extern "C" { - - thread_ref_t thread_create(L4_ThreadId_t *thrd) - { - thrd->raw = Genode::Oklx_thread_list::thread_list()->add().raw; - return thrd->raw; - } - - - L4_ThreadId_t thread_l4tid(thread_ref_t server) - { - L4_ThreadId_t tid; - tid.raw = server; - return tid; - } - - - thread_ref_t thread_myself(void) - { - return __L4_TCR_ThreadWord(Genode::UTCB_TCR_THREAD_WORD_MYSELF); - } - - - void thread_delete(L4_ThreadId_t thrd) - { - using namespace Genode; - Oklx_process *p = Oklx_process::processes()->first(); - while(p) - { - if(p->kill_thread(thrd)) - return; - p = p->next(); - } - } - -} // extern "C" diff --git a/ports-okl4/src/lib/oklx/iguana/iguana_tls.cc b/ports-okl4/src/lib/oklx/iguana/iguana_tls.cc deleted file mode 100644 index 87fa16ee8..000000000 --- a/ports-okl4/src/lib/oklx/iguana/iguana_tls.cc +++ /dev/null @@ -1,34 +0,0 @@ -/* - * \brief Iguana API functions needed by OKLinux - * \author Stefan Kalkowski - * \date 2009-05-07 - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#include -#include - -enum {TLS_ERRNO_KEY, TLS_TIMER_KEY, TLS_NAMING_KEY, - TLS_SYNCH_BITS_KEY, TLS_UNUSED1, TLS_UNUSED2, TLS_THREAD_ID}; - -extern "C" { -#include -#include -#include - - - void __tls_init(void *tls_buffer) - { - Genode::memset(tls_buffer, 0, 32 * sizeof(Genode::addr_t)); - __L4_TCR_Set_ThreadLocalStorage((L4_Word_t)tls_buffer); - ((L4_Word_t *)tls_buffer)[TLS_THREAD_ID] = - thread_l4tid(thread_myself()).raw; - } - -} // extern "C" diff --git a/ports-okl4/src/lib/oklx/include/oklx_memory_maps.h b/ports-okl4/src/lib/oklx/include/oklx_memory_maps.h deleted file mode 100644 index 097725a39..000000000 --- a/ports-okl4/src/lib/oklx/include/oklx_memory_maps.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * \brief OKLinux library specific memory data - * \author Stefan Kalkowski - * \date 2009-05-28 - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#ifndef _OKLINUX_SUPPORT__INCLUDE__OKLX_MEMORY_MAPS_H_ -#define _OKLINUX_SUPPORT__INCLUDE__OKLX_MEMORY_MAPS_H_ - -/* Genode includes */ -#include -#include - -namespace Genode -{ - - /** - * This class represents a memory area within the OKLinux kernel - * address space. - */ - class Memory_area : public List::Element - { - private: - - addr_t _vaddr; /* virtual address */ - size_t _size; /* memory area size */ - Dataspace_capability _cap; /* dataspace mapped */ - - public: - - Memory_area(addr_t vaddr, size_t size, Dataspace_capability cap) - : _vaddr(vaddr), _size(size), _cap(cap) {} - - /* - * Get the virtual address of the memory area - */ - addr_t vaddr() { return _vaddr; } - - /* - * Get the size of the memory area - */ - size_t size() { return _size; } - - /* - * Get the capability of the dataspace backing this memory area - */ - Dataspace_capability dataspace() { return _cap; } - - /* - * Get the physical address of this memory area - */ - addr_t paddr() - { - Dataspace_client dsc(_cap); - return dsc.phys_addr(); - } - - /* - * List of all memory areas of the OKLinux kernel - */ - static List *memory_map() - { - static List _maps; - return &_maps; - } - }; -} - -#endif //_OKLINUX_SUPPORT__INCLUDE__OKLX_MEMORY_MAPS_H_ diff --git a/ports-okl4/src/lib/oklx/include/oklx_screens.h b/ports-okl4/src/lib/oklx/include/oklx_screens.h deleted file mode 100644 index f5792a215..000000000 --- a/ports-okl4/src/lib/oklx/include/oklx_screens.h +++ /dev/null @@ -1,146 +0,0 @@ -/* - * \brief Oklx library specific screen data - * \author Stefan Kalkowski - * \date 2010-04-22 - */ - -/* - * Copyright (C) 2010-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#ifndef _LIB__OKLX__INCLUDE__OKLX_SCREENS_H_ -#define _LIB__OKLX__INCLUDE__OKLX_SCREENS_H_ - -#include -#include -#include -#include -#include - - -class Screen : public Genode::List::Element -{ - private: - - void *_mouse_dev; - void *_keyb_dev; - - protected: - - Input::Event *_ev_buf; - - public: - - Screen() : _mouse_dev(0), _keyb_dev(0), _ev_buf(0) {} - - virtual Framebuffer::Session *framebuffer() = 0; - virtual Input::Session *input() = 0; - - void *keyb_device (void) { return _keyb_dev; } - void *mouse_device (void) { return _mouse_dev; } - void keyb_device (void *dev) { _keyb_dev = dev; } - void mouse_device (void *dev) { _mouse_dev = dev; } - Input::Event *buffer (void) { return _ev_buf; } -}; - - -class Simple_screen : public Screen -{ - private: - - Framebuffer::Connection _fb_con; - Input::Connection _input_con; - - public: - - Simple_screen() - { - _ev_buf = (Input::Event*) - Genode::env()->rm_session()->attach(_input_con.dataspace()); - } - - Framebuffer::Session *framebuffer() { return &_fb_con; } - Input::Session *input() { return &_input_con; } -}; - -class Nitpicker_screen : public Screen -{ - public: - - enum { VIEW_CNT=256 }; - - private: - - Nitpicker::Connection _nit_con; - Nitpicker::View_capability _views[VIEW_CNT]; - - public: - - Nitpicker_screen() - { - _ev_buf = (Input::Event*) - Genode::env()->rm_session()->attach(_nit_con.input()->dataspace()); - for (unsigned i = 0; i < VIEW_CNT; i++) - _views[i] = Nitpicker::View_capability(); - } - - Framebuffer::Session *framebuffer() { return _nit_con.framebuffer(); } - - Input::Session *input() { return _nit_con.input(); } - - Nitpicker::Connection *nitpicker() { return &_nit_con;} - - Nitpicker::View_capability get_view (unsigned idx) - { - if (idx >= VIEW_CNT) - return Nitpicker::View_capability(); - return _views[idx]; - } - - void put_view (unsigned idx, Nitpicker::View_capability view) - { - if (idx < VIEW_CNT) - _views[idx] = view; - } -}; - - -class Screen_array -{ - public: - - enum { SIZE=10 }; - - private: - - Screen *_screens[SIZE]; - - public: - - Screen_array(); - - Screen *get (unsigned int idx) - { - if (idx >= SIZE) { - PWRN("Invalid index %d", idx); - return (Screen*) 0; - } - return _screens[idx]; - } - - unsigned count () - { - for (unsigned i = 0; i < SIZE; i++) { - if (!_screens[i]) - return i; - } - return SIZE; - } - - static Screen_array *screens(void); -}; - -#endif //_LIB__OKLX__INCLUDE__OKLX_SCREENS_H_ diff --git a/ports-okl4/src/lib/oklx/include/oklx_threads.h b/ports-okl4/src/lib/oklx/include/oklx_threads.h deleted file mode 100644 index 6f8e54d93..000000000 --- a/ports-okl4/src/lib/oklx/include/oklx_threads.h +++ /dev/null @@ -1,178 +0,0 @@ -/* - * \brief OKLinux library specific thread data - * \author Stefan Kalkowski - * \date 2009-05-28 - */ - -/* - * Copyright (C) 2009-2013 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU General Public License version 2. - */ - -#ifndef _OKLINUX_SUPPORT__INCLUDE__OKLX_THREADS_H_ -#define _OKLINUX_SUPPORT__INCLUDE__OKLX_THREADS_H_ - -/* Genode includes */ -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Okl4 { -extern "C" { - -/* Iguana includes */ -#include -#include - -/* OKL4 includes */ -#include -#include -} -} - -namespace Genode { - - /** - * This class represents a OKLinux kernel thread - */ - class Oklx_kernel_thread : public List::Element - { - private: - - /* - * We only need a small stack for startup code, - * afterwards OKLinux will rearrange the stack pointer - * to another memory area - */ - enum { STACK_SIZE=1024 }; - - Thread_capability _cap; /* Genodes thread capability */ - Okl4::L4_ThreadId_t _tid; - - char _stack[STACK_SIZE]; /* stack for startup code */ - - public: - - Oklx_kernel_thread(Thread_capability cap) : _cap(cap) {} - - Thread_capability cap() { return _cap; } - - void* stack_addr() { return (void*)&_stack[STACK_SIZE-1]; }; - - static void entry(); - - void set_tid(Okl4::L4_ThreadId_t tid) { _tid = tid; } - - Okl4::L4_ThreadId_t tid() { return _tid; } - - }; - - - /** - * An object of this class contains OKLinux kernel threads and - * an own cpu_session to create them. - */ - class Oklx_thread_list - { - private: - - List _threads; - Cpu_connection _cpu; - - public: - - Okl4::L4_ThreadId_t add(); - Cpu_connection* cpu() { return &_cpu; } - - /** - * Get the global list of OKLinux kernel threads - */ - static Oklx_thread_list *thread_list(); - - Oklx_kernel_thread * first() { return _threads.first(); } - }; - - - /** - * This class represents an OKLinux process, and its threads. - */ - class Oklx_process : public List::Element - { - private: - - /** - * A thread within an OKLinux process - */ - class Oklx_user_thread : public List::Element - { - private: - - friend class Oklx_process; - - Okl4::L4_ThreadId_t _tid; - Thread_capability _cap; - - public: - - Oklx_user_thread() - : _cap(env()->cpu_session()->create_thread("Oklx user thread")) {} - - ~Oklx_user_thread() { - env()->cpu_session()->kill_thread(_cap); } - - Okl4::L4_ThreadId_t tid() { return _tid; } - - Thread_capability cap() { return _cap; } - }; - - - Pd_connection _pd; /* protection domain of the process */ - Cpu_connection _cpu; /* cpu session to construct threads */ - List _threads; /* list of all threads */ - Rm_connection _rm; /* rm session to manage addr. space */ - - public: - - Oklx_process() { _pd.space_pager(pager_cap()); } - - ~Oklx_process(); - - Pd_connection* pd() { return &_pd; } - - Cpu_connection* cpu() { return &_cpu; } - - Rm_connection* rm() { return &_rm; } - - Okl4::L4_ThreadId_t add_thread(); - - bool kill_thread(Okl4::L4_ThreadId_t tid); - - bool empty() { return !_threads.first(); } - - /** - * Get the global list of all OKLinux processes - */ - static List *processes(); - - /** - * Get the capability of the pager thread, - * that pages OKLinux user processes (kernel main thread) - */ - static Thread_capability pager_cap(); - - /** - * Set the capability of the pager thread, - * that pages OKLinux user processes (kernel main thread) - */ - static void set_pager(); - }; -} - -#endif //_OKLINUX_SUPPORT__INCLUDE__OKLX_THREADS_H_ diff --git a/ports-okl4/src/oklinux/main.cc b/ports-okl4/src/oklinux/main.cc deleted file mode 100644 index 76e819701..000000000 --- a/ports-okl4/src/oklinux/main.cc +++ /dev/null @@ -1 +0,0 @@ -int main() { return 0; } diff --git a/ports-okl4/src/oklinux/target.mk b/ports-okl4/src/oklinux/target.mk deleted file mode 100644 index 8d4f5dfcd..000000000 --- a/ports-okl4/src/oklinux/target.mk +++ /dev/null @@ -1,53 +0,0 @@ -TARGET = dummy -LIBS = oklx -REQUIRES = okl4 -SRC_CC = main.cc -VERBOSE_LX_MK ?= 0 -VMLINUX = $(BUILD_BASE_DIR)/oklinux/vmlinux - -$(TARGET): $(VMLINUX) $(BUILD_BASE_DIR)/bin/vmlinux - -$(BUILD_BASE_DIR)/bin/vmlinux: - $(VERBOSE)ln -s $(VMLINUX) $(BUILD_BASE_DIR)/bin/. - -WOMBAT_CFLAGS = -I$(BUILD_BASE_DIR)/include -I$(REP_DIR)/include/oklx_lib \ - -I$(REP_DIR)/include/oklx_kernel -DMAX_ELF_SEGMENTS=1000 \ - -DCONFIG_MAX_THREAD_BITS=10 -D__i386__ $(CC_MARCH) - -# -# The Linux kernel comes with its own declarations of typically builtin -# functions. Unfortunately, gcc-4.6.1 complains that the size type used in -# these declarations does not correspond to the compiler's __SIZE_TYPE__. This -# causes a flood of compile warnings. By marking said functions as non-builtin, -# those warnings are suppressed. -# -WOMBAT_CFLAGS += $(addprefix -fno-builtin-,strncpy strncat strncmp strlen memmove \ - memchr snprintf vsnprintf strncasecmp \ - strspn strcspn memcmp) - -# disable noise caused by uncritical warnings -WOMBAT_CFLAGS += -Wno-unused-but-set-variable - -$(VMLINUX): $(BUILD_BASE_DIR)/oklinux/.config - $(VERBOSE_MK)$(MAKE) $(VERBOSE_DIR) \ - -C $(REP_DIR)/contrib vmlinux \ - ARCH=l4 O=$(BUILD_BASE_DIR)/oklinux \ - CROSS_COMPILE="$(CROSS_DEV_PREFIX)" \ - WOMBAT_CFLAGS='$(WOMBAT_CFLAGS)' \ - LDFLAGS='$(LD_MARCH)' \ - LINK_ADDRESS=b000000 V=1 SYSTEM=i386 \ - GENODE_LIBS_DIR=$(BUILD_BASE_DIR)/var/libcache \ - GENODE_SRC_DIR=$(GENODE_DIR) \ - KBUILD_VERBOSE=$(VERBOSE_LX_MK) \ - V=$(VERBOSE_LX_MK) - -$(BUILD_BASE_DIR)/oklinux/.config: - $(VERBOSE)cp $(REP_DIR)/config/linux_config $@ - -clean: clean_oklinux - -clean_oklinux: - $(VERBOSE)rm -rf .t* ..t* .v* .missing-syscalls.d * .config - -.PHONY: $(VMLINUX) - diff --git a/tool/builddir/etc/build.conf.ports-okl4 b/tool/builddir/etc/build.conf.ports-okl4 deleted file mode 100644 index e3e584c78..000000000 --- a/tool/builddir/etc/build.conf.ports-okl4 +++ /dev/null @@ -1,5 +0,0 @@ - -# -# Software ported specifically for OKL4, i.e., OKLinux -# -#REPOSITORIES += $(GENODE_DIR)/ports-okl4 diff --git a/tool/create_builddir b/tool/create_builddir index 568724ca4..bb9011ba9 100755 --- a/tool/create_builddir +++ b/tool/create_builddir @@ -108,14 +108,6 @@ $(BUILD_DIR)/etc/build.conf:: @cat $(BUILD_CONF).optional >> $@ endif -# -# Add 'ports-okl4' repository to OKL4 build directory -# -ifeq ($(PLATFORM),okl4_x86) -$(BUILD_DIR)/etc/build.conf:: - @cat $(BUILD_CONF).ports-okl4 >> $@ -endif - $(BUILD_DIR)/Makefile: @ln -sf $(GENODE_ABS_DIR)/tool/builddir/build.mk $@